1.浮点数的比较:
直接判断可能会有误差 if(x==y)
一般通过计算两个数的差的绝对值和一个很小的数进行判断来比较
if(Math.abs(x - y) < 1e-8)
2.输入输出:
java中的单词命名都是驼峰命名法,两个单词的话第二个单词要大些
java的优势:生态好,便于多人协作,工具多包多,运行效率比python快
(1)输入1:
输入要利用工具Scanner,先要引入包 import java.util.Scanner;
next
遇到空白字符就会输入结束(空格、回车、制表符等等)
nextLine
可以读入一行字符串
比较慢的数据输入方式,如果输入的数在10的5次方以内可以用
(2)输入方式2:
输入数据量超过10的五次方,一般用BufferedReader
前面要加:
import java.io.BufferedReader;
import java.io.InputStreamReader;
注意抛出异常
BufferedWriter
输入整数要手动输入
输入方式2:
双引号表示字符串单引号表示字符
readLine
只能读一行字符串,想要读整数的话,需要手动操作将字符串转化成int
,如果有空格,需要手动先分割出来
String[] str = br.readLine().split(” “);
int x = Integer.parseInt(str[0]);
int y = Integer.parseInt(str[1]);
输出
(1)输出方式1:效率慢
println
:输出自带一个回车,可以输出字符串、变量、数字等,不能格式化输出
print
:输出最后不带回车,其他与println一样,不能格式化输出
printf
:格式化输出,与c++中printf一样
(java中的double和float都是%f,不是%lf)
(%04,前面不足四位会补0,在输出年份是常用,%4d占4位,不足4位在前面补空格,%-4d,不足四位在后面补空格)
(2)输出方式2:(效率快)
注意最后要需要手动刷新缓冲区,如果不刷新有些时候会获取不到输出
bw.flush(); // 需要手动刷新缓冲区
3.关于double精度
由于double精度的问题,通常需要double x += 1e-8;
来确保一些精度问题;
4.java中所有的变量必须先赋初值
5.数组
- java中数组长度可以是一个变量,C++中最好是一个常量
- java中二维数组的长度可以不一样
- 属性length,返回数组长度,这不是API ->
a.length//返回数组a的长度
6.数组中的常用API
- 使用
Arrays
需要import java.util.Arrays
- Arrays.sort(q):将数组q中的元素从小到大排序,要想实现从大到小排序里面要使用匿名函数(类似Python的Lambda表达式),并且数组要用对象定义的方式;
-
①一维数组:
Integer[] q= new Integer[n]; //Integer是对象类型的整数
Arrays.sort(q, (x,y) ->{ //加一个匿名函数 return x-y; // x-y从小到大排序,y-x从大到小排序 })
-
②二维数组:
二维数组中的每一个一维数组本身就是一个对象,所以他不需要用Integer
Arrays.sort(q, (x,y) ->{ //加一个匿名函数
return x[0]-y[0];
} ) -
Arrays.toString(q):将数组q转化为字符串展开
- Arrays.deepToString(q):将多维数组q转化为字符串展开
- Arrays.fill(int[] q, int val):填充数组,只能初始化一维数组,不能初始化多维数组
- 数组的范围遍历:
for(数据类型 变量名:数组名)
{
这个变量名就代表数组中的每一项元素了,直接使用变量名进行操作
}
7.字符串
(1)初始化:
String a = "Hello World";
String b = "My name is ";
String x = b; // 存储到了相同地址
String c = b + "yxc"; // String可以通过加号拼接
String d = "My age is " + 18; // int会被隐式转化成字符串"18"
String str = String.format("My age is %d", 18); // 格式化字符串,类似于C++中的sprintf
String money_str = "123.45";
double money = Double.parseDouble(money_str); // String转double
此外String是只读变量,不能修改,所谓的修改是开辟了一块新的空间。
访问String中的字符不能像数组那样,需要调用str.charAt(i)
访问下标为i
的字符;
(2)常用的API:
str.length();
:返回字符串长度;(ps:返回数组长度的length没有括号)str.split(String regex);
:分割字符串,通常以空格或逗号分隔,例如str.split(" ");
str.split(",")
- 在对应字符串中查找相应的字符或字符串:
indexOf(char c)
、indexOf(String str)
、lastIndexOf(char c)
、lastIndexOf(String str)
返回的是首字符的下标,找不到返回-1
equals()
:判断两个字符串是否相等,注意不能直接用==
;str.equals(s1);
,返回布尔变量str.compareTo(s1)
:判断两个字符串的字典序大小,负数表示小于(及str字典序更小),0表示相等,正数表示大于(及str字典序更大);trim()
:去掉首尾的空白字符,包括换行符,在工程中用得较多;replace(char oldChar, char newChar)
:替换字符replace(String oldRegex, String newRegex)
:替换字符串;
这两个有时也可用于删除字符或字符串;
str.reverse():
反转字符串str.substring(int beginIndex, int endIndex)
:返回[beginIndex, endIndex)
中的子串toCharArray()
:将字符串转化成字符数组toLowerCase()
:全部用小写字符toUpperCase()
:全部用大写字符startsWith()
:判断是否以某个前缀开头endsWith()
:判断是否以某个后缀结尾
(3) StringBuilder
、StringBuffer
String
不能被修改,如果打算修改字符串,可以使用StringBuilder
和StringBuffer
。
StringBuffer
线程安全,速度较慢;StringBuilder
线程不安全,速度较快。
(4)输入输出
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str1 = sc.next(); // 输入字符串,遇到空格、回车等空白字符时停止输入
String str2 = sc.nextLine(); // 输入一整行字符串,遇到空格不会停止输入,遇到回车才会停止
System.out.println(str1); // 可以直接输出
System.out.printf("%s\n", str2); // 也可以格式化输出,用 %s 表示字符串
}
}
8.函数
(1)变量的作用域
函数内定义的变量为局部变量,只能在函数内部使用。
定义在类中的变量为成员变量,可以在类的所有成员变量函数中调用。
当局部变量和全局变量重名时,会有优先使用局部变量。
(2)参数传递
1)八大基本数据类型和String类型等采用值传递,将实参的初始值拷贝给形参,此时形参的改动不影响实参的初始值。
2)引用传递,除了String以为的数据类型的对象,例如数组、stringBuilder
等采用引用传递。将实参的引用(地址)传递给形参,通过引用找到变量的地址,然后对地址中的值进行修改。所以此时对形参的修改也会影响实参的值。对象String、Integer等对象是不能被修改的,所谓修改实际是开辟了一段新的内存空间。所以JAVA库里面没有swap函数,想要交换两个数的值一般就传入一个数组。
9.类与接口
9.1 源文件声明规则
- 一个源文件中只能有一个
public
类。 - 一个源文件中可以有多个非
public
类。 - 源文件名和
public
类的类名一致。 - 每个源文件中,应该先写package语句(导包:后面跟的其实就是代码的路径;),在写import语句,最后定义类。
9.2 类的定义
public
:所有对象均可访问;private
:只有奔雷内部可以访问;protected
:同一个包或者子类中可以访问;- 不添加修饰符:在同一个包中可以访问;
-
静态(带static修饰符)成员变量/函数与普通成员变量/函数的却别
-
所有static成员变量/函数在类中只有一份,被所有类的对象共享;
-
所有普通成员变量/函数在类的每个对象中都有独立的一份;
-
静态成员函数中只能调用静态成员变量/函数;普通函数中既可以调用不同成员变量/函数,也可以调用静态成员变量/函数;
-
-
JAVA中每个类只能有一个父类
extends
;
9.3 接口
interface与class类似。主要用来定义类中所需包含的函数。
接口也可以继承其他接口,一个类可以实现多个接口。
(1)接口的定义
接口中不添加任何修饰符,默认为public。
interface Role{
public void greet();
public void move();
public void getSpeed();
}
(2)接口的继承
每个接口可以继承多个接口;
interface Hero extends Role{
public void attack();
}
(3)接口的实现
每个类可以实现多个接口
class Zeus implements Hero{
private final String name = "Zeus";
public void attack(){
...
}
public void move(){
...
}
public void greet(){
...
}
}
(4)接口的多态
接口也可以实现多态,不同的类中可以以不同的逻辑实现同一个接口,程序会先判断这个对象属于哪个类,然后执行相应接口的逻辑。
一个类去实现接口用的是implements
* 一个类去实现一个接口,那么必须包含这个接口中的所有函数
10.常用API
10.1 List
接口:java.util.list<>
。
实现:
java.util.ArrayList<>
:变长数组java.util.LinkedList<>
:双链表
函数:
add()
:在末尾添加一个元素;clear()
:清空size()
:返回长度isEmpty()
:是否为空get(i)
:获取第i个元素set(i,val)
:将第i个元素设置为val
10.2 栈
类:java.util.Stack<>
函数:
push()
:压入元素pop()
:弹出栈顶元素,并返回栈顶元素peek()
:返回栈顶元素size()
:返回长度empty()
:栈是否为空clear()
:清空
10.3 队列
接口:java.util.Queue<>
实现:
java.util.LinkedList<>
:双链表————Queue<Integer> q = new LinkedList<>();
java.util.PriorityQueue<>
:优先队列————Queue<Integer> q = new PriorityQueue<>();
*默认是小根堆,大根堆写法:new PriorityQueue<>(collections.reverseOrder())
函数:
add():
在队尾添加元素remove():
删除并返回队头isEmpty():
是否为空size():
返回长度peek():
返回队头clear():
清空
10.4 set 维护一个集合
接口:java.util.Set<k>
实现:
-java.util.HashSet<K>
:哈希表O(1)——判断一个数是否存在
-java.util.TreeSet<K>
:平衡树O(logn)——动态维护一个有序序列
函数:
add()
:contains()
:remove()
:size()
:isEmpty()
:clear()
:
java.util.TreeSet<K>
多的函数:
ceiling(key)
:返回大于等于key
的最小元素,不存在返回null
floor(key)
:返回小于等于key
的最大元素,不存在返回null
10.5 map维护一个映射
接口:java.util.Map<K,V>
实现:
java.util.HashMap<K,V>
:哈希表java.util.TreeMap<K,V>
:平衡树
函数:
put(key,value)
:添加关键字和对应的值get(key)
:返回关键字对应的值containsKey(key)
:是否包含关键字remove(key)
:删除关键字size()
:返回元素个数isEmpty()
:是否为空clear()
:情况entrySet()
:获取Map中所有对象的集合Map.Entry<K,V>
:Map
中对象的类型getKey()
:获取关键字getValue()
:获取值
java.util.TreeMap<K,V>
多的函数:
ceilingEntry(Key)
:返回大于等于key
的最小元素,不存在返回null
floorEntry(Key)
:返回小于等于key
的最小元素,不存在返回null
11.异常
11.1 Error与Exception的区别
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。此类异常是程序的致命异常,是无法捕获处理的。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 程序中应当尽可能去处理这些异常。
11.2 运行时异常和非运行时异常的区别
运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等, 这些异常是非检查型异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,这些是检查型异常。一般情况下不自定义检查型异常。
11.3 异常捕获
import java.util.Scanner;
public class Main {
private static void foo() {
int[] array = new int[5];
for (int i = 0; i < 5; i ++ )
array[i] = i;
Scanner sc = new Scanner(System.in);
int k = sc.nextInt();
int x = sc.nextInt();
try {
array[k] /= x;
} catch (ArithmeticException e) {
System.out.println("除零错误!");
e.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界!");
e.printStackTrace();
} finally { // finally部分是一定会执行的
for (int i = 0; i < 5; i ++ ) {
System.out.println(array[i]);
}
}
}
public static void main(String[] args) {
foo();
}
}
11.4 异常抛出
throw: 在函数内抛出一个异常。
throws:在函数定义时抛出一些可能的异常。
检查型异常必须被捕获或者抛出。
import java.io.IOException;
import java.util.Scanner;
public class Main {
private static void foo() throws IOException, NoSuchFieldException {
Scanner sc = new Scanner(System.in);
int x = sc.nextInt();
if (x == 1)
throw new IOException("找不到文件!!!");
else
throw new NoSuchFieldException("自定义异常");
}
public static void main(String[] args) {
try {
foo();
} catch (IOException e) {
System.out.println("IOException!");
e.printStackTrace();
} catch (NoSuchFieldException e) {
System.out.println("NoSuchFieldException!");
e.printStackTrace();
}
}
}
11.5. try-with-resources
JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。
try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。
import java.io.*;
public class Main {
public static void main(String[] args) {
String line;
try (
BufferedReader br = new BufferedReader(new FileReader("input.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
) {
while ((line = br.readLine()) != null) {
System.out.println("Line => " + line);
bw.write("copy: " + line + "\n");
}
bw.flush();
} catch (IOException e) {
System.out.println("IOException in try block =>" + e.getMessage());
}
}
}
12.注解与反射
12.1 注解
(1) 注解(Annotation)也被称为元数据(Metadata),用于修饰包、方法、属性、构造器、局部变量等数据信息。
(2) 注解不影响程序逻辑,但注解可以被编译或运行。
(3) 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
12.2 常用注解
(1) @Override
: 限定某个函数必须重写其他函数,该注解只能用于函数。函数名和参数列表必须相同。
(2) @Overload
: 限定某个函数必须重载其他函数,该注解只能用于函数。函数名必须相同,参数列表必须不同。
(3) @Deprecated
:用于表示某个程序元素(类、函数)已过时
(4) @SuppressWarnings
:抑制编译器警告
12.3 元注解
修饰其他注解的注解,就被称为元注解。
(1) Retention
:指定注解的作用范围
(2) Target
:指定注解可以用在哪些地方
(3) Document
:注定注解是否出出现在javadoc中
(4) Inherited
:子类会继承父类的注解
12.4 反射
反射:动态引入类、动态调用实例的成员函数、成员变量等。
12.5 常用API
(1) java.lang.Class
(2) java.lang.reflect.Method
(3) java.lang.reflect.Field
(4) java.lang.reflect.Constructor
package org.ly;
public class Calculator {
public String name;
public Calculator() {}
public Calculator(String name) {
this.name = name;
}
public int add(int a, int b) {
return a + b;
}
}
package org.yxc;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<?> cls = Class.forName("org.yxc.Calculator");
Object o = cls.newInstance();
Method method = cls.getMethod("add", int.class, int.class);
int res = (int)method.invoke(o, 3, 4);
System.out.println(res);
Field field = cls.getField("name");
field.set(o, "My Calculator!");
System.out.println(field.get(o));
Constructor<?> constructor = cls.getConstructor(String.class);
Object new_o = constructor.newInstance("New Calculator!");
System.out.println(new_o);
}
}
优点:可以动态创建和使用对象,使用灵活
缺点:执行速度慢
13 多线程
13.1 实现多线程
写法1:继承Thread类
class Worker extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i ++ ) {
System.out.println("Hello! " + this.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Main {
public static void main(String[] args) {
Worker worker1 = new Worker();
Worker worker2 = new Worker();
worker1.setName("thread-1");
worker2.setName("thread-2");
worker1.start();
worker2.start();
}
}
写法2:实现Runnable接口
class Worker1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i ++ ) {
System.out.println("Hello! " + "thread-1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class Worker2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i ++ ) {
System.out.println("Hello! " + "thread-2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Main {
public static void main(String[] args) {
new Thread(new Worker1()).start();
new Thread(new Worker2()).start();
}
}
13.2 常用API
start()
:开启一个线程
Thread.sleep()
: 休眠一个线程
join()
:等待线程执行结束
interrupt()
:从休眠中中断线程
setDaemon()
:将线程设置为守护线程。当只剩下守护线程时,程序自动退出
14 锁
- lock:获取锁,如果锁已经被其他线程获取,则阻塞
- unlock:释放锁,并唤醒被该锁阻塞的其他线程
import java.util.concurrent.locks.ReentrantLock;
class Worker extends Thread {
public static int cnt = 0;
private static final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
for (int i = 0; i < 100000; i ++ ) {
lock.lock();
try {
cnt ++ ;
} finally {
lock.unlock();
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Worker worker1 = new Worker();
Worker worker2 = new Worker();
worker1.start();
worker2.start();
worker1.join();
worker2.join();
System.out.println(Worker.cnt);
}
}
15 同步(Synchronized)
写法1:将Synchronized加到代码块上
class Count {
public int cnt = 0;
}
class Worker extends Thread {
public final Count count;
public Worker(Count count) {
this.count = count;
}
@Override
public void run() {
synchronized (count) {
for (int i = 0; i < 100000; i ++ ) {
count.cnt ++ ;
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Count count = new Count();
Worker worker1 = new Worker(count);
Worker worker2 = new Worker(count);
worker1.start();
worker2.start();
worker1.join();
worker2.join();
System.out.println(count.cnt);
}
}
写法2:将Synchronized加到函数上(锁加到了this对象上)
class Worker implements Runnable {
public static int cnt = 0;
private synchronized void work() {
for (int i = 0; i < 100000; i ++ ) {
cnt ++ ;
}
}
@Override
public void run() {
work();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Worker worker = new Worker();
Thread worker1 = new Thread(worker);
Thread worker2 = new Thread(worker);
worker1.start();
worker2.start();
worker1.join();
worker2.join();
System.out.println(Worker.cnt);
}
}
15.1 wait与notify
wait
:使线程阻塞
notify
:唤醒线程
package org.yxc;
class Worker extends Thread {
private final Object object;
private final boolean needWait;
public Worker(Object object, boolean needWait) {
this.object = object;
this.needWait = needWait;
}
@Override
public void run() {
synchronized (object) {
try {
if (needWait) {
object.wait();
System.out.println(this.getName() + ": 被唤醒啦!");
} else {
object.notifyAll();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
for (int i = 0; i < 5; i ++ ) {
Worker worker = new Worker(object, true);
worker.setName("thread-" + i);
worker.start();
}
Worker worker = new Worker(object, false);
worker.setName("thread-" + 5);
Thread.sleep(1000);
worker.start();
}
}