上一篇地址:赶紧收藏!2024 年最常见 100道 Java 基础面试题(二十八)-CSDN博客
五十七、什么是Java序列化?什么情况下需要序列化?
什么是Java序列化?
Java序列化是一种将对象的状态信息转换为可以被存储或传输的格式的过程。在Java中,序列化涉及将对象转换为字节流,这些字节流可以保存到文件、写入到网络连接或传递给其他系统。序列化是实现Java对象持久化的一种方式。
为了使一个类的对象可以被序列化,该类需要实现java.io.Serializable
接口。这个接口是一个标记接口,不包含任何方法,但它允许对象的实例被序列化。当一个对象被序列化时,它的状态(包括所有属性,但不包括方法和类变量)被转换为字节流。
什么情况下需要序列化?
-
持久化: 将对象的状态保存到磁盘上,以便在程序下次运行时可以恢复对象的状态。
-
网络传输: 在网络上发送对象的状态,例如,通过套接字(Socket)将对象从一个系统发送到另一个系统。
-
分布式系统: 在分布式系统中,对象序列化用于在不同的节点之间传递对象状态。
-
RMI(Remote Method Invocation): RMI允许在不同的Java虚拟机上运行的对象进行通信,它依赖于对象序列化来传输对象。
-
缓存: 将对象序列化后存储到缓存中,可以提高性能,因为从缓存中反序列化对象比重新创建对象要快。
-
对象数据库: 在某些情况下,对象数据库会使用序列化来存储对象的状态。
示例代码:
import java.io.*;
class SerializableExample implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String name;
// 构造方法、getter和setter省略
}
public class SerializationDemo {
public static void main(String[] args) {
SerializableExample obj = new SerializableExample();
obj.setId(1);
obj.setName("Kimi");
try (ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("object.data"))) {
out.writeObject(obj);
} catch (IOException e) {
e.printStackTrace();
}
// 从文件中反序列化对象
try (ObjectInputStream in = new ObjectInputStream(
new FileInputStream("object.data"))) {
SerializableExample deserializedObj = (SerializableExample) in.readObject();
System.out.println(deserializedObj.getId()); // 输出: 1
System.out.println(deserializedObj.getName()); // 输出: Kimi
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在这个示例中,SerializableExample
类实现了Serializable
接口,因此可以被序列化。对象被写入到文件中,然后从文件中反序列化。
注意事项:
- 只有实现了
Serializable
接口的类的对象才能被序列化。 - 序列化过程中,对象的非静态和非瞬时字段(transient)不会被序列化。
- 序列化版本统一标识符(
serialVersionUID
)用于确保序列化和反序列化的兼容性。
总结:
- Java序列化是一种将对象状态转换为字节流的机制,用于对象的持久化和网络传输。
- 需要序列化的情况包括数据持久化、网络通信、分布式系统、RMI、缓存和对象数据库等。
- 实现
Serializable
接口的类可以被序列化,但需要谨慎处理非静态和非瞬时字段。
五十八、动态代理是什么?有哪些应用?
动态代理是什么?
动态代理是Java中的一种设计模式,它允许在运行时动态地创建代理对象,以控制对其他对象的访问。Java提供了一个java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现动态代理。
动态代理主要有两种类型:
-
基于接口的代理:在Java中,如果要创建动态代理,被代理的对象必须实现一个或多个接口。
Proxy
类使用被代理对象的接口创建代理实例,并定义如何拦截代理实例上的方法调用。 -
基于类的代理:这种代理是通过使用某些库(如CGLIB - Code Generation Library)实现的,它允许为没有接口的类创建代理。
动态代理的组成:
-
主题(Subject):被代理的实际对象,它实现了特定的业务逻辑。
-
代理(Proxy):一个代理对象,它包装了主题对象,并在内部维护对主题对象的引用。
-
请求(Request):客户端通过代理对象发出的请求。
-
处理器(InvocationHandler):一个实现了
InvocationHandler
接口的对象,它定义了在代理对象上进行方法调用时的实际逻辑。可以在此处理器中添加额外的处理逻辑,如日志记录、事务管理等。
应用场景:
-
访问控制:在访问主题对象之前,可以检查权限,确保只有授权的调用者才能访问。
-
延迟初始化:代理对象可以在需要时才创建主题对象,从而提高系统的性能。
-
日志记录:在代理对象中添加日志记录功能,记录每次对主题对象的调用。
-
事务管理:在代理对象中添加事务管理逻辑,确保每次调用都是在一个事务的上下文中执行。
-
远程代理:在分布式系统中,远程代理可以代表一个远程服务,隐藏网络通信的细节。
-
懒加载:在访问数据库或执行I/O操作时,可以通过代理实现懒加载,仅在需要时才加载数据。
-
AOP(面向切面编程):动态代理是实现AOP的一种技术手段,可以在不修改主题对象的情况下,为系统添加日志、事务、安全性等切面功能。
示例代码:
import java.lang.reflect.*;
interface GreetingService {
void sayHello(String name);
}
class GreetingServiceImpl implements GreetingService {
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before the actual method call");
Object result = method.invoke(target, args);
System.out.println("After the actual method call");
return result;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
GreetingService service = new GreetingServiceImpl();
InvocationHandler handler = new ProxyHandler(service);
GreetingService proxyService = (GreetingService)
java.lang.reflect.Proxy.newProxyInstance(
service.getClass().getClassLoader(),
new Class[]{GreetingService.class},
handler
);
proxyService.sayHello("Kimi");
}
}
在这个示例中,GreetingService
是一个接口,GreetingServiceImpl
是它的一个实现。ProxyHandler
是一个实现了InvocationHandler
接口的类,它定义了代理对象的行为。通过Proxy.newProxyInstance
方法创建了GreetingService
的代理对象proxyService
。
总结:
- 动态代理允许在运行时创建代理对象,以控制对其他对象的访问。
- 动态代理广泛应用于访问控制、日志记录、事务管理、远程代理和AOP等领域。
- Java提供了
Proxy
和InvocationHandler
类来实现基于接口的动态代理。