Java 中的序列化和[反序列化]是什么?
Java 中的序列化和反序列化
序列化和反序列化是 Java 中用于对象持久化和网络传输的重要机制。
序列化 (Serialization)
序列化是将 Java 对象转换为字节序列的过程,以便可以:
将对象保存到文件中
通过[网络传输]对象
将对象存储在内存缓存中
反序列化 (Deserialization)
反序列化是将字节序列重新转换为 Java 对象的过程,是序列化的逆过程。
实现方式
要使一个类可序列化,需要实现 java.io.Serializable 接口(这是一个标记接口,没有方法):
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
// 构造方法、getter和setter
}
AI写代码java
运行
12345678
序列化示例
// 序列化对象到文件
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
Person person = new Person("张三", 25);
oos.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
AI写代码java
运行
1234567
反序列化示例
// 从文件反序列化对象
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person person = (Person) ois.readObject();
System.out.println(person.getName()); // 输出: 张三
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
AI写代码java
运行
1234567
这份小册是从基础到高级涵盖了足足30个技术栈的,包含了JAVA基础,JAVA集合,JAVA并发,Spring,微服务,Netty,计算机网络,MQ,Zookeeper,Redis,MySQL,数据结构与算法以及设计模式等等,足足200余页,由于掘金篇幅限制我在这里就只展示部分内容了,扫一扫免费获取
重要特性
transient 关键字:标记不被序列化的字段
private transient String password; // 不会被序列化
AI写代码java
运行
1
serialVersionUID:用于版本控制
private static final long serialVersionUID = 1L;
AI写代码java
运行
1
自定义序列化:通过实现 writeObject 和 readObject 方法
使用场景
对象持久化到文件或数据库
远程方法调用(RMI)
分布式计算
缓存对象
什么是 Java 中的不可变类?
不可变类是指其实例在创建后状态不能被修改的类。一旦创建,对象的所有字段值就固定不变。
不可变类的特点
状态不可变:对象创建后,其所有字段值不能被修改
线程安全:天然线程安全,无需同步
可自由共享:可以被多个线程安全地共享
适合作为缓存键:因为状态不变,哈希值也不会变
如何创建不可变类
将类声明为 final:防止被子类修改行为
public final class ImmutablePerson {}
AI写代码java
运行
1
所有字段设为 private final:
private final String name;
private final int age;
AI写代码java
运行
12
不提供 setter 方法:只提供 getter 方法
public String getName() {
return name;
}
AI写代码java
运行
123
通过构造方法初始化所有字段:
public ImmutablePerson(String name, int age) {
this.name = name;
this.age = age;
}
AI写代码java
运行
1234
对可变对象进行防御性拷贝:
// 如果字段是可变对象(如Date、集合等)
private final Date birthDate;
public ImmutablePerson(Date birthDate) {
this.birthDate = new Date(birthDate.getTime()); // 防御性拷贝
}
public Date getBirthDate() {
return new Date(birthDate.getTime()); // 返回拷贝而非原始引用
}
AI写代码java
运行
12345678910
示例:完整的不可变类
public final class ImmutablePerson {
private final String name;
private final int age;
private final List<String> hobbies;
public ImmutablePerson(String name, int age, List<String> hobbies) {
this.name = name;
this.age = age;
this.hobbies = new ArrayList<>(hobbies); // 防御性拷贝
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public List<String> getHobbies() {
return new ArrayList<>(hobbies); // 返回拷贝
}
}
AI写代码java
运行
1234567891011121314151617181920212223
Java 中的不可变类示例
String
基本类型的包装类(Integer, Long, Double 等)
BigInteger 和 BigDecimal
LocalDate, LocalTime, LocalDateTime 等 java.time 类
不可变类的优点
线程安全:无需同步,天然线程安全
简化代码:无需考虑对象状态变化
适合缓存:哈希值不变,适合作为 Map 的键
减少错误:防止意外修改对象状态
不可变类的缺点
可能产生大量临时对象:每次"修改"都需要创建新对象
不适合频繁变更的场景:如需要频繁修改状态的对象
不可变类是函数式编程的重要概念,也是设计安全、可靠系统的重要工具。
Java 中 Exception 和 Error 有什么区别?
Exception 和 Error 都是 Throwable 类的子类,但它们代表了不同性质的异常情况。
主要区别
| 特性 | Exception | Error |
|---|---|---|
| 可恢复性 | 通常可以恢复 | 通常不可恢复 |
| 处理方式 | 应该被捕获处理 | 通常不捕获处理 |
| 来源 | 主要来自应用程序逻辑 | 主要来自JVM或系统底层问题 |
| 子类 | IOException, SQLException等 | OutOfMemoryError, StackOverflowError等 |
| 检查性 | 分为检查型(checked)和非检查型(unchecked) | 都是非检查型(unchecked) |
Exception (异常)
可恢复:通常表示程序可以处理的异常情况
分类:
Checked Exceptions (编译时异常) :必须处理(捕获或声明抛出)
try {
FileReader file = new FileReader("somefile.txt");
} catch (FileNotFoundException e) {
// 处理文件未找到的情况
}
AI写代码java
运行
12345
Unchecked Exceptions (运行时异常/RuntimeException) :不强制处理
// NullPointerException, ArrayIndexOutOfBoundsException等
String str = null;
System.out.println(str.length()); // 抛出NullPointerException
AI写代码java
运行
123
Error (错误)
不可恢复:表示严重问题,通常不应尝试捕获
JVM级别问题:如内存不足、栈溢出等
不强制处理:都是Unchecked类型
这份小册是从基础到高级涵盖了足足30个技术栈的,包含了JAVA基础,JAVA集合,JAVA并发,Spring,微服务,Netty,计算机网络,MQ,Zookeeper,Redis,MySQL,数据结构与算法以及设计模式等等,足足200余页,由于掘金篇幅限制我在这里就只展示部分内容了,扫一扫免费获取
示例:
// 可能导致OutOfMemoryError
List<Object> list = new ArrayList<>();
while(true) {
list.add(new Object());
}
AI写代码java
运行
12345
继承层次
Throwable
├── Error
│ ├── VirtualMachineError
│ │ ├── OutOfMemoryError
│ │ └── StackOverflowError
│ └── ...
└── Exception
├── IOException
├── SQLException
└── RuntimeException
├── NullPointerException
├── IndexOutOfBoundsException
└── ...
AI写代码java
运行
12345678910111213
最佳实践
对于Exception:
检查型异常:必须处理或声明抛出
运行时异常:预防为主(如空指针检查)
对于Error:
通常不捕获处理
应该修复导致Error的根本问题
自定义异常:
通常继承Exception或RuntimeException
一般不继承Error
什么是 Java 的多态特性?
多态(Polymorphism)是面向对象编程的三大特性之一(封装、继承、多态),指同一操作作用于不同对象时,可以产生不同的行为。
多态的核心概念
同一接口,多种实现:通过统一的接口操作不同的对象
运行时绑定:具体调用哪个方法在运行时决定(动态绑定)
代码扩展性:无需修改现有代码即可添加新功能
Java 中多态的两种主要形式
- 编译时多态(静态多态/方法重载)
通过方法重载(Overload)实现:
class Calculator {
// 方法重载 - 同名方法,不同参数
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
String add(String a, String b) {
return a.concat(b);
}
}
AI写代码java
运行
1234567891011121314
2. 运行时多态(动态多态/方法重写)
通过方法重写(Override)和继承/接口实现:
class Animal {
void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("喵喵喵");
}
}
// 使用多态
Animal myAnimal = new Dog();
myAnimal.makeSound(); // 输出"汪汪汪"
myAnimal = new Cat();
myAnimal.makeSound(); // 输出"喵喵喵"
AI写代码java
运行
1234567891011121314151617181920212223242526
多态的实现机制
向上转型(Upcasting):
Animal animal = new Dog(); // 子类转父类
AI写代码java
运行
1
动态绑定:JVM在运行时根据实际对象类型决定调用哪个方法
instanceof 检查:
if (animal instanceof Dog) {
Dog dog = (Dog) animal; // 向下转型
}
AI写代码java
运行
123
多态的应用场景
接口编程:
List<String> list = new ArrayList<>(); // 接口引用指向实现类
list = new LinkedList<>(); // 可随时更换实现
AI写代码java
运行
12
设计模式:
策略模式
工厂模式
模板方法模式
框架扩展:
// Spring框架中的依赖注入
@Autowired
private UserRepository userRepo; // 可能是MySQL或Oracle实现
AI写代码java
运行
123
多态的优势
可替换性:允许用子类对象替换父类对象
可扩展性:容易添加新类而不影响现有代码
接口性:通过父类/接口定义通用接口
灵活性:简化代码结构,提高代码复用
注意事项
不能重写的情况:
private 方法
final 方法
static 方法(实际上是隐藏,不是重写)
字段没有多态:
class Parent {
String name = "Parent";
}
class Child extends Parent {
String name = "Child";
}
Parent obj = new Child();
System.out.println(obj.name); // 输出"Parent"(字段看引用类型)
AI写代码java
运行
12345678910
JAVA注解
注解的本质
注解是一种特殊接口:所有注解类型都隐式继承自 java.lang.annotation.Annotation 接口
编译时处理:注解本身不会改变代码逻辑,但可以通过以下方式发挥作用:
编译时处理(如 @Override)
运行时反射处理(如 Spring 的 @Autowired)
编译时生成代码(如 Lombok)
注解的实现原理
元注解
Java 提供了 5 种元注解(用于注解其他注解):
@Target - 指定注解可以应用的目标(类、方法、字段等)
@Retention - 指定注解的保留策略(SOURCE/CLASS/RUNTIME)
@Documented - 标记是否包含在 Javadoc 中
@Inherited - 标记是否允许子类继承父类的注解
@Repeatable - 标记注解是否可以重复应用于同一目标
AI写代码java
运行
12345
保留策略@Retention
SOURCE:仅存在于源码中,编译时丢弃(如 @Override)
CLASS:保留到 class 文件,但 JVM 不加载(默认行为)
RUNTIME:保留到运行时,可通过反射读取(如 Spring 的注解)
运行时处理机制
运行时注解通过反射 API 处理:
// 获取类注解
Annotation[] annotations = MyClass.class.getAnnotations();
// 获取方法注解
Method method = MyClass.class.getMethod("myMethod");
Annotation[] methodAnnotations = method.getAnnotations();
// 获取字段注解
Field field = MyClass.class.getField("myField");
Annotation[] fieldAnnotations = field.getAnnotations();
AI写代码java
运行
12345678910
编译时处理机制
通过注解处理器(Annotation Processor)实现:
继承 AbstractProcessor 类
重写 process 方法
在 META-INF/services/javax.annotation.processing.Processor 中注册处理器
注解的底层实现
JVM 并不直接支持注解,编译器会将注解转换为:
一个继承 Annotation 的接口
一个实现该接口的代理类(运行时动态生成)
注解信息存储在 class 文件的属性表中
示例:自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
String value() default "";
int count() default 0;
}
AI写代码java
运行
123456
使用示例:
public class MyClass {
@MyAnnotation(value = "test", count = 5)
public void myMethod() {
// ...
}
}
AI写代码java
运行
123456
处理示例:
Method method = MyClass.class.getMethod("myMethod");
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value()); // 输出 "test"
System.out.println(annotation.count()); // 输出 5
AI写代码java
运行
1234
性能考虑
运行时注解处理依赖于反射,可能影响性能。在性能敏感的场景中,可以考虑:
缓存反射结果
使用编译时注解处理生成代码(如 ButterKnife)
使用 AspectJ 等编译时织入技术
JAVA 反射
反射核心 API
反射主要通过 java.lang.reflect 包和 Class 类实现:
获取 Class 对象:
Class<?> clazz1 = Class.forName("完整类名"); // 最常用方式
Class<?> clazz2 = MyClass.class; // 类字面常量
Class<?> clazz3 = obj.getClass(); // 对象实例
AI写代码java
运行
123
创建实例:
Object obj = clazz1.newInstance(); // 调用无参构造器(已过时)
Constructor<?> constructor = clazz1.getConstructor(String.class);
Object obj = constructor.newInstance("参数"); // 推荐方式
AI写代码java
运行
123
实际应用场景
动态加载和调用方法
// 加载类
Class<?> clazz = Class.forName("com.example.MyClass");
// 创建实例
Object instance = clazz.newInstance();
// 获取方法
Method method = clazz.getMethod("methodName", String.class, int.class);
// 调用方法
Object result = method.invoke(instance, "参数1", 123);
AI写代码java
运行
1234567891011
访问和修改字段
// 获取字段(包括私有字段)
Field field = clazz.getDeclaredField("fieldName");
// 对于私有字段需要设置可访问
field.setAccessible(true);
// 获取字段值
Object value = field.get(instance);
// 设置字段值
field.set(instance, "新值");
AI写代码java
运行
1234567891011
注解处理
// 获取类上的注解
Annotation[] annotations = clazz.getAnnotations();
// 获取方法注解
Method method = clazz.getMethod("methodName");
MyAnnotation anno = method.getAnnotation(MyAnnotation.class);
AI写代码java
运行
123456
动态代理
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 前置处理
Object result = method.invoke(target, args); // 调用实际方法
// 后置处理
return result;
}
};
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[]{MyInterface.class},
handler
);
AI写代码java
运行
123456789101112131415
反射的优缺点
优点:
极大的灵活性
支持动态编程
是许多框架的基础
缺点:
性能开销(比直接调用慢)
安全限制(需要运行时权限)
破坏封装性(可以访问私有成员)
调试困难