API
API: 应用程序编程接口
, 即已经写好的东西, 可以直接使用
String
- 字符串的内容是不会更改的
String name = "abc";
name = "def";// name = "def" 是创建了一个新的字符串, 然后把引用赋给了 name
- 构建方法
String s = "abc"; // 直接赋值String s = new String(); // 创建一个空字符串String s = new String("abc"); // 根据传递的字符串内容构建一个新字符串char[] chs = {'a', 'b', 'c'};
String s = new String(chs); // 根据字符数组byte[] bytes = {97, 98, 99, 100};
String s = new String(bytes); // 根据字节数组
- 使用双引号直接赋值时, 如果该字符串在
串池
中已存在, 会复用
字符串
String s1 = "abc";
String s2 = "abc";// s1, s2 指向同一个串池中的地址
- 使用
new
创建时, 每次new
会在堆内存
中开辟一个新空间, 不会复用
char[] chs = {'a', 'b', 'c'};
String s1 = new String(chs);
String s2 = new String(chs);// s1, s2 指向不同的堆内存中的地址
-
字符串比较
-
==
比较基本数据类型时, 比较数据值
; 比较引用数据类型时, 比较地址值
因此对于String
, 比较的是地址值
-
s1.equals(s2)
比较内容, 完全一样才是 true -
s1.equalsIgnoreCase(s2)
比较内容, 忽略大小写
-
-
访问字符
s1.charAt(i)
-
转成字符数组
char[] chs = s1.toCharArray();
StringBuilder
可以看成一个容器, 创建之后里面的内容可以改变
因此拼接字符串时, 不会每次产生一个新的字符串, 提高效率
StringBuilder s = new StringBuilder("abc");s.append("def");s.reverse();int len = s.length();String str = s.toString();
StringJoiner
StringJoiner s = new StringJoiner(间隔符号, 开始符号, 结束符号);e.g. StringJoiner s = new StringJoiner("---", "(", ")");s.add("abc"); // 应该是在中间添加数据int len = s.length();String str = s.toString();
System
System.exit(0); // 虚拟机正常停止, 非 0 是异常停止long l = System.currentTimeMillis(); // 从时间原点到运行这行代码的时间毫秒值形式
// 可以两行代码之间相减检查中间代码的运行时间System.arraycopy(arr1, 0, arr2, 0, 10);
// System.arraycopy(数据源数组(被拷贝), 从数据源第几个索引开始拷贝, 目的地数组, 目的地索引, 拷贝的个数);
// 若 arr1, arr2 都是基本数据类型, 则类型需要一样
// 若 arr1, arr2 都是引用数据类型, 则子类数组可以把地址值拷贝给父类数组
Runtime
Runtime r1 = Runtime.getRuntime(); // 获取 Runtime 对象, 地址值固定(final)r1.exit(0); // 停止虚拟机int cpuProcessors = r1.availableProcessors(); // 获取 CPU 的线程数long maxM = r1.maxMemory(); // JVM 能从系统获取总内存大小(单位 byte)long totM = r1.totalMemory(); // JVM 已从系统获取总内存大小(单位 byte)long freeM = r1.freeMemory(); // JVM 剩余内存(单位 byte)r1.exac("写 cmd 命令");
Object
toString
Student stu = new Student();
String s = stu.toString(); // 返回对象的字符串表示形式(地址值)
sout(s); // 等同于 sout(stu);// 可以重写 toString 方法以打印对象的属性信息
equals
Student s1 = new Student();
Student s2 = new Student();
boolean result = s1.equals(s2); // 比较地址值, 为 false// 可以重写 equals 方法以比较对象的属性信息// String.equals() 先判断参数是否为字符串, 是字符串再比较内容
// StringBuilder.equals() 继承 Object 的, 比较地址值
clone
clone
需要重写, 并让类实现 Cloneable
接口
-
[[D:/java_code/user/User.java|e.g.User]]
public class User implements Cloneable { // 注意 Cloneable...@Overridepublic String toString() {return name + ", " + id;}@Overrideprotected Object clone() throws CloneNotSupportedException { // 注意 throws CloneNotSupportedException// 调用父类中的 clone 方法return super.clone();} }
-
Cloneable
这个接口中没有抽象方法, 表示当前接口是一个标记行接口 -
实现
Cloneable
表示当前类的对象可以被克隆 -
[[D:/java_code/user/Test.java|e.g.Test]]
public static void main(String[] args) throws CloneNotSupportedException { // 注意 throws CloneNotSupportedExceptionUser u1 = new User("zhangsan", "123");User u2 = (User)u1.clone(); // 注意强转// 会调用重写的 toStringSystem.out.println(u1);System.out.println(u2); }
-
浅克隆
- 基本数据类型: 完全拷贝数据值
- 引用数据类型: 完全拷贝地址值
Object.clone()
是浅克隆
-
深克隆
- 基本数据类型: 完全拷贝数据值
- 字符串: 复用
- 引用数据类型: 重新创建新的
- 重写
clone
以深克隆 e.g.
// 假设 User 中有一个成员为 data 数组 @Override protected Object clone() throws CloneNotSupportedException {int[] data = this.data;int[] newData = new int[data.length];for (int i = 0; i < data.length; i++) {newData[i] = data[i];}User u = (User)super.clone(); // 浅克隆u.data = newData; // 对引用数据类型的成员特别处理return u; }
- 实际开发中, 一般使用序列化再反序列化
- 使用第三方工具如
Jackson
或者Gson
, 将对象转换为JSON
字符串, 再从JSON
字符串重建新的对象
Objects
一个对象工具类, 注意不是 Object
, 因为有 s
boolean flag = Objects.isNull(对象);
boolean flag = Objects.nonNull(对象);
boolean flag = Objects.equals(对象1, 对象2);
isNull
与nonNull
相反equals
会先作非空判断, 再比较两个对象
BigInteger
BigInteger 是 Java 提供的高精度计算类, 可以很方便地解决高精度问题。
初始化
import java.io.PrintWriter;
import java.math.BigInteger;class Main {static PrintWriter out = new PrintWriter(System.out);public static void main(String[] args) {// 将字符串以十进制的形式创建 BigInteger 对象BigInteger a = new BigInteger("12345678910");out.println(a); // 12345678910 // 将字符串以指定进制的形式创建 BigInteger 对象BigInteger b = new BigInteger("1E", 16);out.println(b); // 30 // 获取一个范围为 0 ~ 2^31-1 的随机数BigInteger c = new BigInteger(31, new Random()); out.println(c); // 一个范围为 0 ~ 2^31-1 的随机数// 参数不超过 long, 静态方法获取对象, 内部有优化// 会将 -16 ~ 16 先创建好 BigInteger 对象, 多次获取不会重新创建BigInteger d = BigInteger.valueOf(100); out.println(d); // 100out.close();}
}
对象一旦创建, 内部记录的值不能发生改变
基本运算
以下均用 this 代替当前 BigIntger :
函数名 | 功能 |
---|---|
abs() | 返回 this 的绝对值 |
negate() | 返回 - this |
add(BigInteger val) | 返回 this + val |
subtract(BigInteger val) | 返回 this - val |
multiply(BigInteger val) | 返回 this * val |
divide(BigInteger val) | 返回 this / val |
remainder(BigInteger val) | 返回 this % val |
divideAndRemainder(BigInteger val) | 返回一个数组, 包含 this / val 和 this % val |
mod(BigInteger val) | 返回 this mod val |
pow(int e) | 返回 this^e |
and(BigInteger val) | 返回 this & val |
or(BigInteger val) | 返回 this |
not() | 返回 ~ this |
xor(BigInteger val) | 返回 this ^ val |
shiftLeft(int n) | 返回 this << n |
shiftRight(int n) | 返回 this >> n |
max(BigInteger val) | 返回 this 与 val 的较大值 |
min(BigInteger val) | 返回 this 与 val 的较小值 |
bitCount() | 返回 this 的二进制中不包括符号位的 1 的个数 |
bitLength() | 返回 this 的二进制中不包括符号位的长度 |
getLowestSetBit() | 返回 this 的二进制中最右边的位置 |
compareTo(BigInteger val) | 比较 this 和 val 值大小 |
toString() | 返回 this 的 10 进制字符串表示形式 |
toString(int radix) | 返回 this 的 raidx 进制字符串表示形式 |
intValue(BigInteger val) | 转为 int 类型, 超出范围数据出错 |
BigDecima
- 用于小数的精确运算
- 用来表示很大的小数
正则表达式
- 校验字符串是否满足规则
- 在一段文本中查找满足要求的内容
如何书写
字符类
代码 | 范围 |
---|---|
[abc] | a,b,c |
[^abc] | 除 a,b,c |
[a-zA-z] | a-z, A-Z |
[a-d[m-p]] | a-d, m-p |
[a-z&&[def]] | d,e,f |
[a-z&&[^bc]] | a, d-z |
[a-z&&[^m-p]] | a-l, q-z |
预定义字符(只匹配一个字符)
字符 | 范围 |
---|---|
. | 任意字符 |
\d | 数字 |
\D | 非数字 |
\s | 一个空白字符 |
\S | 非空白字符 |
\w | 英文, 数字, 下划线 |
\W | 一个非单词字符(非"英文, 数字, 下划线") |
[[D:/java_code/regex/Test.java|e.g.]]
插件
any-rule
, 支持 idea
, vscode
爬虫
[[D:/java_code/regex/Find.java|基础爬取和有条件的爬取]]
贪婪爬取与非贪婪爬取
暂略
捕获分组和非捕获分组
暂略
时间相关类
暂略
包装类
用一个对象, 把基础数据类型包起来
byte -> Byte
short -> Short
char -> Character
int -> Integer
long -> Long
float -> Float
double -> Double
boolean -> Boolean
创建
public static void main(String[] args) {Integer i1 = new Integer(1);Integer i2 = new Integer("1");Integer i3 = Integer.valueOf(123);Integer i4 = Integer.valueOf("123");Integer i5 = Integer.valueOf("123", 8);sout(i5); // 8 进制的 123 -> 10 进制的 83// valueOf 会获取提前创建的 -128 ~ 127 的对象Integer i6 = Integer.valueOf(127);Integer i7 = Integer.valueOf(127);sout(i6 == i7); // true
}
JDK5 以后可以自动装箱, 自动拆箱
, 赋值和运算直接写即可
Integer i1 = 10;
Integer i2 = 20;
Integer i3 = i1 + i2;
int i4 = i1;
Integer 成员方法
String str1 = Integer.toBinaryString(100); // 把整数转成 2 进制
String str2 = Integer.toOctalString(100); // 把整数转成 8 进制
String str3 = Integer.toHexString(100); // 把整数转成 16 进制int i = Integer.parseInt("123"); // 字符串类型的整数转成 int
// (8 种包装类, 除 Character 都有对应的 parseXxx 方法)
操作数组的工具类 Arrays
int[] a = {1, 2, 3};String s = Arrays.toString(a); // 数组拼接成字符串
int pos = Arrays.binarySearch(a, 2); // 二分查找
int[] b = Arrays.copyOf(a, 10); // 拷贝数组(原数组, 新数组长度)
int[] c = Arrays.copyOfRange(a, 1, 2); // 拷贝数组, 指定范围
Arrays.fill(a, 0); // 填充数组
Arrays.sort(a); // 默认方式排序数组
Arrays.sort(a, cmp); // 指定方式排序数组
cmp 举例
Integer[] arr = {2, 3, 1, 5, 6, 7, 8, 4, 9};// 底层原理:
// 插入排序 + 二分查找
// 认为前面有序, 后面无序
// 遍历无序序列, 记当前遍历元素为 o1
// 向有序序列中插入 o1, 先利用二分查找确定 o1 的插入点
// 比较 o1 与当前二分到的插入点的元素 o2 的规则为 compare
Arrays.sort(arr, new Comparator<Integer>(){@Overridepublic int compare(Integer o1, Integer o2) {return o1 - o2; // 升序排序}// 返回值:// 负数: 表示当前要插入的元素是"小"的, 放在前面// 正数: 表示当前要插入的元素是"大"的, 放在前面// 0 : 表示当前要插入的元素和现在的元素一样, 也会放在后面
});
Lambda 表达式
Arrays.sort(arr, (Integer o1, Integer o2) -> {return o1 - o2;
});
函数式编程
面向对象: 先找对象, 让对象做事情
函数式编程: 忽略面向对象的复杂语法, 强调做什么, 而不是谁去做
格式
() -> {}
-
可以简化匿名内部类的书写
-
只能简化函数式接口的匿名内部类的书写
- 函数式接口: 有且仅有一个抽象方法的接口, 接口上方可以加
@FunctionalInterface
- 若不止一个, 则不清楚是替代哪个方法, 接口上方也不能加
@FunctionalInterface
- 函数式接口: 有且仅有一个抽象方法的接口, 接口上方可以加
-
省略格式
可以(从对应的函数式接口)推导得到的东西可以省略不写- 参数类型可以省略
Arrays.sort(arr, (o1, o2) -> {return o1 - o2; });
- 如果只有一个参数,
()
可以省略 - 如果只有一行, 大括号, 分号, return 可以省略, 需要同时省略
Arrays.sort(arr, (o1, o2) -> o1 - o2);
Java 集合对应 C++ STL
集合类不能直接存储基础数据类型, 因为其设计用于存储对象
, 只能存储对应的包装类
List<Integer> a = new ArrayList<>(); // vector
List<Integer> a = new LinkedList<>(); // list
Set<Integer> s = new HashSet<>(); // unordered_set
Set<Integer> s = new TreeSet<>(); // set
Map<String, Interger> mp = new HashMap<>(); // unordered_map
Map<String, Interger> mp = new TreeMap<>(); // map
Stack<Integer> st = new Stack<>(); // stack
Queue<Integer> q = new LinkedList<>(); // queue
Deque<Integer> q = new ArrayDeque<>(); // deque
PriorityQueue<Integer> q = new PriorityQueue<>(); // priority_queue
集合
单列集合: Collection
, 双列集合: Map
Collection
单列集合的祖宗接口(是个接口, 不能直接创建其对象, 只能创建实现类的对象)
Collection<String> coll = new ArrayList<>();
coll.add("123"); // 向 set 中添加元素, 元素已存在时返回 false
coll.clear();
coll.remove("123"); // 删除成功返回 true
bool flag1 = coll.contains("123"); // 底层依赖 equals 进行判断, 如果集合存储自定义对象, 需要重写 equals 以比较对象属性而非地址值
bool flag2 = coll.isempty();
int size = coll.size();
迭代器
Collection<String> coll = new ArrayList<>();
Iterator<String> it = coll.iterator(); // 默认指向 0 索引对应的位置
while(it.hasNext()) {String str = it.next(); // it.next() 可以大概理解为 C++ 中 *(it++), 返回当前对应的元素, 并移动指针sout(str);
}
- 迭代器遍历时, 不能使用集合的增加或删除操作, 但可以
it.remove()
在遍历时删除元素
增强 for 遍历
for(String s : coll) {s = "xxx";
}// 修改增强 for 中的变量, 不会改变集合中原本的数据
Lambda 遍历
// 匿名内部类
// 底层遍历每一个元素, 传递给 accept 方法coll.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {sout(s);}
});
// Lambdacoll.forEach(s -> sout(s));
List
List<Integer> list = new ArrayList<>();list.add(1);list.remove(0); // 移除 0 索引元素Integer i = Integer.valueOf(1);
list.remove(i); // 移除元素 1// 上面两个 remove, 本质是方法调用时如果有重载现象, 优先调用实参和形参一致的那个方法
List<String> list = new ArrayList<>();
list.add("abc");
String s = list.set(0, "123"); // 返回被修改的元素 abc, 并修改 0 索引上元素为 123
s = list.get(0); // 123
- 列表迭代器: 可以遍历集合时添加元素
ListIterator<String> it = list.listIterator(); // 默认指向 0 索引 while(it.hasNext()) {String s = it.next();if ("bbb".equals(s)) {it.add("qqq");} }
泛型
泛型: 可以在编译阶段约束操作的数据类型, 并进行检查
格式: <数据类型>
注意: 泛型只支持引用数据类型
- 如果没有泛型, 可以给集合添加任意类型的元素, 默认为
Object
, 不能使用类的特有行为 - Java 的泛型是伪泛型
- 指定泛型的具体类型后, 可以传入其子类类型
泛型类
编写一个类, 如果不确定类型, 可以定义为泛型类
修饰符 class 类名<类型> {}
比如
// E 表示类型不确定, 可以写成 T, K, Vpublic class MyArrayList<E> {Object[] obj = new Object[10];int size;public boolean add(E e) {obj[size] = e;size++;return true;}public E get(int index) {return (E)obj[index];}@Overridepublic String toString() {return Arrays.toString(obj);}
}
泛型方法
修饰符 <类型> 返回值类型 方法名(类型 变量名) {}
比如
public static <E> void addAll(ArrayList<E> list1, ArrayList<E> list2) {if (list2 == null) {return;}list2.forEach(e -> list1.add(e));
}
public static <E> void addAll(ArrayList<E> list, E...e) {for (E element : e) {list.add(element);}
}
泛型接口
修饰符 interface 接口名<类型> {}
比如
public interface List<E> extends Collection<E> {}
- 两种使用方式
- 实现类给出具体类型
public class MyArrayList implements List<String> {}
- 实现类延续泛型, 创建实现类对象时再确定泛型
public class MyArrayList implements List<E> {}
注意
泛型不具备继承性, 但是数据具备继承性
(泛型里面写什么类型, 那么只能传递什么类型的数据)
通配符
如何不确定类型, 但又想限定类型的一定 范围, 需要使用通配符
// 可以传入继承 xxx 类的数据(包括 xxx)
public static void method(ArrayList<? extends xxx> list) {}// 可以传入是 xxx 类父类的数据(包括 xxx)
public static void method(ArrayList<? super xxx> list) {}
Hash
hashCode
- 哈希值是根据
Objrct.hashCode()
计算得到的int
类型整数 - 默认使用地址值计算
- 一般要重写
hashCode
, 使用对象内部的属性值计算哈希值
@Override
public int hashCode() {return Objects.hash(name, age);
}
- 哈希表存储自定义对象要重写
equals
和hashCode
LinkedHashSet
相较 HashSet
, 可以保证数据存取有序(底层使用双链表记录添加顺序)
Tree
默认排序
- 默认排序/自然排序:
Javabean
类实现Comparable
接口, 重写里面的抽象方法, 指定比较规则
public class Student implements Comparable<Student> {private int age;public Student() {}public Student(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic iint compareTo(Student o) {return this.getAge() - o.getAge();// 只看年龄, 升序排序}// this 表示当前要添加的元素// o 表示当前比较的已经在红黑树存在的元素// 返回值:// 负数: 要添加元素"小", 存左边// 正数: 要添加元素"大", 存右边// 0: 要添加元素已经存在, 去重舍弃
}
比较器排序
- 比较器排序: 创建 TreeSet 对象时, 传递比较器
Comparator
指定规则
// 当前需求: 先按长度排序, 再按字典序排序
// String 自带的字典序排序方法不满足当前需求, 采用比较器排序
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {int diff = o1.length() - o2.length();return diff == 0 ? o1.compareTo(o2) : diff;}
});// Comparator 是一个函数式接口, 可以 Lambda
TreeSet<String> ts = new TreeSet<>((o1, o2) -> {int diff = o1.length() - o2.length();return diff == 0 ? o1.compareTo(o2) : diff;
});