415 序列化&反序列化
将Object转为字节流,或反之
- 普通:实现serializable接口 ˈsɪərɪəlaɪzəbl
- 使用Jackon,Obj => json格式
416 Java中的不可变类
final 修饰,例如String类
🎉优点:
- 线程安全
- 缓存友好
缺点
- 性能问题,因为不能修改,所有每次状态变化,都需要生成新的对象。
411 多态特性
- 继承
- 方法重载,函数名相同,但是函数签名需要有差异(参数类型&数量)
- 重写,子类重写父类方法,通过父类调用方法时,调用的是子类重写后的函数。
412 Java参数传值是副本还是引用呢?
- 基本类型是传值副本,int…
- 引用数据类型是传引用副本。 including:obj,array
425 Java中 包装类型和基本类型
🏷️基本类型 => int long float double … 位于栈上(局部变量的话) ,性能好,但不支持为null
(局部变量在栈上,成员变量在堆上,静态字段在方法区)
🏷️包装类型 => 每一个基本类型都对应一个包装类型。包装类型是类,在堆中,支持null
JVM内存模型 ❗❗❗内存堆和数据结构堆不是同一个东西(不是堆的结构)
1+--------------------------------------------+
2| 方法区(Method Area) | ← 线程共享
3| - 类元数据(Class 信息) |
4| - 静态变量(static 变量) |
5| - 常量池(字符串常量等) |
6+--------------------------------------------+
7 ↑ ↑
8 | |
9+----------------+ +-----------------+
10| 栈(Stack) | | 堆(Heap) | ← 线程共享
11| - 局部变量 | | - Java 对象实例 |
12| - 方法调用栈 | | - 数组 |
13+----------------+ +-----------------+
413 interface & abstract class
- interface(自上而下) 知晓某一种行为,基于这些行为约束定义的接口, 一些类需要有这些行为的话,需要实现这些接口
- abstract class(自下而上): 有许多类,它们有共同点,很多代码可以复用,因此将公共逻辑封装为抽象对象。
100 hashCode & equals & ==
-
hashCode用于散列表(hashMap)用于计算hash值,从而计算存储位置;
-
equals比较对象内容是否相等,默认是==。 可能需要重写equals方法逻辑,实现对象成员变量的比较;
-
== 引用类型比较两个引用是否指向同一个对象(内存地址),基本类型则比较值。
431 Java注解
注解就是一个标记,提供元数据机制,给予代码添加说明信息。可以在类,方法,成员变量…上标记,标记本身可以设置一些值。
1 @Target(Class,...)
2@Retention(RetentionPolicy.RUNTIME) // 注解保留时间
3public @interface MyInterface {
4 String value();
5}
6
7// 运行时,通过反射机制拿到对应的注解 -- 结合AOP做事情
8...Obj.getAnnotation("MyInterface")...
432 Java反射
运行时获取类的信息,并操作对象
1Person person = new Person("Emma", 28);
2Class<?> cls = person.getClass();
3
4// 调用无参方法
5Method greetMethod = cls.getDeclaredMethod("greet");
6greetMethod.invoke(person);
7
8// 调用有参方法
9Method setAgeMethod = cls.getDeclaredMethod("setAge", int.class);
10setAgeMethod.invoke(person, 35);
11
12System.out.println(person); // Person{name='Emma', age=35}
434 Java泛型
通过在编译时检查类型安全,使得代码更加通用和灵活,避免运行时发生类型转换错误。 简单理解=> 将运行时的类型转换异常上升到编译时候
- 类型安全
- 代码重用
- 消除显示类型转换,一开始指定好
🏷️泛型类
1public class Box<T> {
2 private T value;
3 public Box(T value) { this.value = value; }
4 public T getValue() { return value; }
5}
6// 这样的话 getValue()就不需要(Type)getValue()了, 直接告诉调用者返回类型 => 避免类型转换,且提高代码复用性
🏷️泛型方法
1// 泛型方法
2public static <T> T getFirst(T[] array) {
3 return array[0];
4}
5
6// <T> 是申明T是泛型哦, 不然谁知道它是不是一个类的名称
7// 🎉更加灵活
8String[] words = {"Hello", "World"};
9Integer[] numbers = {1, 2, 3};
10
11System.out.println(getFirst(words)); // Hello
12System.out.println(getFirst(numbers)); // 1
🏷️限制参数类型 防止非法类型使用
1class Calculator<T extends Number>
6306 Java泛型擦除
Java编译器在编译代码的时候,将所有泛型信息删除的过程。 擦除是为了确保与旧版本兼容
1Class Box<T> {} => class Box {} (T => Object)
2Class Box<T extends Number> {} => class Box {} (T => Number)
泛型类型上界 ? extends T
泛型类型下届 ? super T
限制类型
436 Java深拷贝&浅拷贝
- 深拷贝:不仅复制当前对象本身,而且递归的复制对象内的引用。 重写clone方法,或许序列化和反序列化生成新的对象;
- 浅拷贝:只复制对象的本身和基本类型的成员变量,而不复制引用类型的对象。 Object.clone();
- 引用拷贝:仅复制对应引用 **Object o1 = o2; ** 。
438 Java类加载过程
类加载过程:
- 加载:二进制流读入内存,生成Class对象
- 连接
- 验证:格式是否规范
- 准备:为静态变量设置初始值,为它们在方法区开辟。
- 解析:将常量池的符号引用转化为直接引用。
- 初始化:执行静态代码块,为静态变量赋值(代码逻辑里面的)。
440 BigDecimal
高精度的计算类,处理任意精度的数值。 涉及到钱,用这个
442 Java中的final、finally
final修饰符,指类和变量不能再修改。
1class final MyClass {
2
3}
不论是否异常,都会执行finally代码块
1try{
2
3}catch() {
4
5} finally {
6
7}
825 Java调用外部可执行程序
使用Runtime.exec()
1try {
2 // 执行命令
3 Process process = Runtime.getRuntime().exec("notepad.exe");
4 // 等待外部进程结束
5 int exitCode = process.waitFor();
6 // 检查执行情况
7 if ... else ...
8}
使用ProcessBuilder
1// 配置程序信息
2ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "dir");
3// builder.xxx() ... 配置信息
4
5// 启动进程
6Process process = builder.start();
7
8// 获取进程输出
9InputStream is = process.getInputStream();
10// ...
11
12// 等待结束
13int code = process.waitFor()
14
15// 检查执行情况
938 如果一个线程在Java中被两次调用start方法,会发生什么?
报错 => Java中,一个线程只能被启动一次。
943 IO流
读取数据和输出数据
- 字节流,用于处理二进制文件, InputStream&OutputStream;
- InputStream 子类如下:
- FileInputStream
- BufferedInputStream // 提供缓冲区,提高性能
- InputStream 子类如下:
- 字符流,用于处理文本。 Reader&Writer;
- Reader 子类如下:
- FileReader
- BufferedReader
- Reader 子类如下:
945 Java网络编程
🏷️用于网络通信,网络编程基本概念
- IP地址
- 端口号
- socket(ip:port)
- 协议,TCP & UDP 等等
TCP实践
1// 服务器代码
2class ServerThread extends Thread {
3 private Socket socket = new ServerSocket("localhost", 8080);
4
5 @overwrite
6 public void run() {
7 try {
8 PrintWriter out = new PrintWriter(socket.getOutputStream() ... );
9 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
10
11 String message = in.readLine();
12 out.println("Hello, client!");
13 }
14 }
15}
16
17// Client
18Socket socket = new Socket("localhost", 8080)
19
20// ServerSocket & Socket
949 Java中的自动装箱和拆箱
自动装箱/拆箱让 Java 更加易用,减少了手动转换的冗余代码,同时保持了类型安全
- 自动拆箱和装箱 , 解决泛型不支持基本类型
1List<Integer> list = new ArrayList<>(); // ✅ 使用包装类 Integer
2
3// 手动装箱和拆箱
4list.add(Integer.valueOf(10)); // int => Integer
5int a = list.get(0).intValue(); // Integer => int 手动实现
6
7// 自动装箱和拆箱
8list.add(10)
9int a = list.get(0)
- 基本类型当作对象使用
1Map<Integer,String> map = new HashMap<>();
2map.put(1, "One") <= 自动装箱
3String value = map.get(1) <= 自动装箱
- 简化基本类型和包装类型
1public static int add(Integer a, Integer b) {
2 return a + b; // 这里的 a 和 b 需要自动拆箱
3}
4
5// 手动拆箱
6public static int add(Integer a, Integer b) {
7 return a.intValue() + b.intValue(); // 这里的 a 和 b 需要自动拆箱
8}
988 Java中的迭代器 Iterator ɪtəˈreɪtə
作用用于遍历集合
- hasNext(): 返回是否存在下一个元素
- next(): 返回下一个元素
1Iterator iterator = list.iterator()
2while(iterator.hasNext()) {
3 // ...
4 iterator.next();
5}
993 Java特性
-
封装:将对象的状态和行为封装在一个类内部,并通过公开的接口与外部进行交互。只暴露必要功能。
-
继承:子类继承父类,可以继承它的属性和方法,提高代码重用和扩展。
-
多态:重写&重载
994 Java中访问修饰符
- public:All
- private:当前类
- protected:当前类,同一包(package声明一致),子类
- default,当前类,同一包
995 静态方法&实例方法
-
静态方法 static声明:属于类而非具体的实例,通过类名调用。
- 工厂方法,工具类
- 当类的字节码文件加载到内存,类方法的入口地址就会被分配完成,所以类方法不仅仅被该类的对象调用,也可以直接通过类名称完成调用,类方法的入口地址只有程序退出才消失。
-
实例方法:属于实例,通过实例调用;
- 当类的字节码加载到内存中的时候,类的实例方法并没有被分配到入口地址,只有当类的对象创建之后,实例方法才分配了入口地址,从而实例方法可以被类创建的所有的对象所调用,还有一点要注意,当我们创建第一个类的对象时,实例方法的入口地址会完成分配,当后续在创建对象时,不会被分配新的入口地址,该类的所有的对象共享实例方法的入口地址,当该类的所有的对象被销毁,入口的地址才会消失。
990 java中for和for-each
- for,下标遍历
- for-each,遍历集合,不提供下标,且不能修改集合,否则报错
5900 wait()和sleep()
wait() 需要和 notify()搭配使用。 用于在同步代码块同,阻塞和唤醒
sleep() 使线程进行休眠状态,让出CPU使用权,时间到了自动恢复为就绪态等待系统分配CPU
5908 Java Object类有什么方法及对应作用
-
hashCode() 计算哈希值
-
equals() 默认引用是否一致
- 根据需求重写这个,实现指定对象属性比较
-
toString() 默认返回对象的类名+hashCode的16进制表示
- 重写使其更有描述意义。
-
getClass() 返回对象的Class类对象
-
wait() 挂起当前线程,使其变为等待状态。
-
notify¬ifyAll() 唤醒在对象监视器上等待的一个or全部线程
-
clone() 浅拷贝对象,其中引用属性不拷贝
- 重写实现深拷贝,保证完整性
5909 Java字节码是什么?❌
处于 源代码.java 和 JVM.bin 执行的机器代码.exe(windows)之间的中间表示。
.class 文件 可以被JVM解释器编译为机器码
🎉通过Java反射Api,可以在运行时动态生成或者修改字节码,从而创建代理对象或实现动态方法调用
166 BIO、NIO、AIO
BIO:人一直盯着水烧开,水烧开之后亲自关火 NIO:人在烧水的时候去干别的事情,时不时看着水烧没烧开,烧开之后亲自关火 AIO:人找了一个帮手,帮手在烧水的时候一直盯着,水烧开之后帮手关火,然后提醒人水烧开了。人全程不管烧水的事情
- BIO:Blocking IO。传统阻塞式IO模式,调用方调用BIO会被阻塞,直到IO服务完成才被唤醒。
- 场景:同步、阻塞,适合并发连接较少的场景,小型服务。
1// 客户端
2// 1. 创建服务器 socket,监听端口
3serverSocket = 创建 ServerSocket(端口);
4
5// 2. 服务器循环等待客户端连接
6while (true) {
7 // 3. 阻塞等待客户端连接
8 socket = serverSocket.accept(); ⭐这里会卡住
9
10 // 4. 每个连接创建新线程
11 启动新线程(() -> {
12 inputStream = socket.getInputStream();
13 outputStream = socket.getOutputStream();
14
15 while (true) {
16 // 5. 读取数据(阻塞)
17 data = 读取数据(inputStream);
18 if (data == null) break;
19
20 // 6. 处理数据
21 处理数据(data);
22
23 // 7. 响应客户端(阻塞)
24 outputStream.write(响应数据);
25 }
26 // 8. 关闭连接
27 socket.close();
28 });
29}
30
31
32// 客户端
33// 1. 创建 Socket 连接服务器
34socket = 创建 Socket(服务器IP, 端口);
35
36// 2. 获取输入输出流
37inputStream = socket.getInputStream();
38outputStream = socket.getOutputStream();
39
40// 3. 发送数据(阻塞)
41outputStream.write(请求数据);
42outputStream.flush();
43
44// 4. 读取服务器响应(阻塞)
45response = 读取数据(inputStream);
46
47// 5. 处理响应
48处理数据(response);
49
50// 6. 关闭连接
51socket.close();
一连接一线程
- NIO:Non-blocking IO。非阻塞IO模式,调用方发起IO操作即返回,不论任务是否完成。通常结合IO多路复用技术,使得一个线程可以同时管理多个连接。
1// 1. 创建 Selector(事件选择器)
2selector = 创建 Selector();
3
4// 2. 创建非阻塞 ServerSocketChannel,绑定端口
5serverChannel = 创建 ⭐ServerSocketChannel(端口)⭐;
6serverChannel.configureBlocking(false);
7
8// 3. 注册到 Selector,监听 "accept" 事件
9serverChannel.register(selector, OP_ACCEPT);
10
11// 4. 服务器循环监听事件
12while (true) {
13 // 5. 轮询监听已准备好的 I/O 事件, 监视多个Channel
14 selector.select();
15
16 // 6. 遍历所有发生事件的通道
17 for (key : selector.selectedKeys()) {
18 if (key.isAcceptable()) { // 7. 处理新连接
19 socketChannel = serverChannel.accept();
20 socketChannel.configureBlocking(false);
21 socketChannel.register(selector, OP_READ | OP_WRITE);
22 }
23 else if (key.isReadable()) { // 8. 读取数据
24 socketChannel = key.channel();
25 buffer = 创建 ByteBuffer();
26 bytesRead = socketChannel.read(buffer);
27 if (bytesRead > 0) {
28 处理数据(buffer);
29 }
30 }
31 else if (key.isWritable()) { // 9. 响应客户端
32 socketChannel = key.channel();
33 buffer = 创建 ByteBuffer(响应数据);
34 socketChannel.write(buffer);
35 }
36 }
37}
38
39
40// 客户端
41// 1. 创建 Selector(事件管理器)
42selector = 创建 Selector();
43
44// 2. 创建非阻塞 SocketChannel
45socketChannel = 创建 ⭐SocketChannel()⭐;
46socketChannel.configureBlocking(false);
47
48// 3. 连接服务器(非阻塞)
49socketChannel.connect(服务器IP, 端口);
50
51// 4. 注册到 Selector,监听 "连接完成" 事件
52socketChannel.register(selector, OP_CONNECT | OP_READ | OP_WRITE);
53
54// 5. 轮询 Selector 监听事件
55while (true) {
56 selector.select(); // 轮询等待事件
57 for (key : selector.selectedKeys()) {
58 if (key.isConnectable()) { // 6. 处理连接完成事件
59 if (socketChannel.finishConnect()) {
60 socketChannel.register(selector, OP_READ | OP_WRITE);
61 }
62 }
63 else if (key.isWritable()) { // 7. 发送数据
64 buffer = 创建 ByteBuffer(请求数据);
65 socketChannel.write(buffer);
66 }
67 else if (key.isReadable()) { // 8. 读取服务器响应
68 buffer = 创建 ByteBuffer();
69 bytesRead = socketChannel.read(buffer);
70 if (bytesRead > 0) {
71 处理数据(buffer);
72 }
73 }
74 }
75}
- AIO:Asynchronous IO 异步IO
- 利用回调or通知的方式告知调用方
1// 1. 创建异步 ServerSocketChannel
2serverChannel = 创建 AsynchronousServerSocketChannel(端口);
3serverChannel.bind(端口);
4
5// 2. 监听客户端连接(异步回调)
6serverChannel.accept(null, new CompletionHandler<>() {
7 // 3. 处理新连接
8 completed(socketChannel, attachment) {
9 // 4. 继续监听新的客户端
10 serverChannel.accept(null, this);
11
12 // 5. 读取数据(异步回调)
13 buffer = 创建 ByteBuffer();
14 socketChannel.read(buffer, buffer, new CompletionHandler<>() {
15 // 6. 读取完成,处理数据
16 completed(bytesRead, buffer) {
17 if (bytesRead > 0) {
18 处理数据(buffer);
19
20 // 7. 响应客户端(异步回调)
21 socketChannel.write(ByteBuffer.wrap(响应数据), null, new CompletionHandler<>() {
22 completed(bytesWritten, attachment) {
23 // 响应完成
24 }
25 });
26 }
27 }
28 });
29 }
30});
31
32// 客户端
33// 1. 创建异步 SocketChannel
34socketChannel = 创建 AsynchronousSocketChannel();
35
36// 2. 连接服务器(异步回调)
37socketChannel.connect(服务器IP, 端口, null, new CompletionHandler<>() {
38 // 3. 连接成功回调
39 completed(attachment) {
40 // 4. 发送数据(异步)
41 buffer = 创建 ByteBuffer(请求数据);
42 socketChannel.write(buffer, buffer, new CompletionHandler<>() {
43 completed(bytesWritten, buffer) {
44 // 5. 继续读取服务器响应(异步)
45 buffer.clear();
46 socketChannel.read(buffer, buffer, new CompletionHandler<>() {
47 completed(bytesRead, buffer) {
48 处理数据(buffer);
49 }
50 });
51 }
52 });
53 }
54});
439 Java双亲委派模型 ❌❌❌
Java类加载机制的设计模式之一。 核心思想:类加载器在加载某个类时,先委派父类去加载,父类无法加载时,才由当前类加载器加载。 自定义ClassLoader继承系统的&重写findClass即可
GPT: 双亲委派模型(Parent Delegation Model) 是 Java 类加载机制 的一个规则。 当一个类加载器要加载某个类时,先让父类加载器尝试加载,只有当父类加载器无法加载该类时,才由自身加载
1【Bootstrap ClassLoader】(引导类加载器)
2 ↑ 加载-1,不行的话到2
3【ExtClassLoader】(扩展类加载器)
4 ↑ 加载-2,不行的话到3
5【AppClassLoader】(应用类加载器)
6 ↑ 加载-3,不行的话到4
7【自定义 ClassLoader】(用户自定义加载器) 加载-4
8
9// 倒着的U字形
它解决的问题
- 避免类重复加载,如果没有该机制 => 每个类加载器都可以自行加载同一个类,可能导致 ClassCastException(类转换异常)
1ClassLoader cl1 = new MyClassLoader(); // 自定义类加载器
2ClassLoader cl2 = new MyClassLoader();
3
4Class<?> class1 = cl1.loadClass("com.example.MyClass");
5Class<?> class2 = cl2.loadClass("com.example.MyClass");
6
7// class1 和 class2 虽然名字相同,但是两个不同的类
8Object obj = class1.newInstance();
9MyClass myObj = (MyClass) obj; // 可能抛出 ClassCastException
10// 每个类加载器都有自己独立的类加载空间。如果你使用多个类加载器加载同一个类,它们会认为这是两个不同的类,尽管类的名字和内容完全相同。
- 保证Java核心类的安全
因为委托父类加载
例如
1package java.lang;
2public class String {
3 public static void hack() {
4 System.out.println("Java 被黑了!");
5 }
6}
如果没有双亲委派,那么加载的是自定义的类,可能导致安全漏洞。