Java多态原理
- 概念:多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
- 实现方式:
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
interface Shape {
double getArea();
}
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
- 原理:在Java中,多态通过方法重写和对象的向上转型实现。JVM在运行时根据对象的实际类型来调用相应的方法,这就是动态绑定。例如:
Animal animal = new Dog();
animal.makeSound();// 实际调用的是Dog类的makeSound方法
动态代理原理
- 概念:动态代理是一种在运行时创建代理对象来代理目标对象的机制,代理对象可以在不改变目标对象的情况下,对目标对象的方法进行增强。
- 实现方式:
- JDK动态代理:基于接口实现,使用
Proxy
类和InvocationHandler
接口。例如:
interface HelloService {
void sayHello();
}
class HelloServiceImpl implements HelloService {
@Override
public void sayHello() {
System.out.println("Hello!");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
public class Main {
public static void main(String[] args) {
HelloServiceImpl target = new HelloServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(target);
HelloService proxy = (HelloService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
proxy.sayHello();
}
}
- CGLIB动态代理:基于继承实现,对目标类进行继承并增强方法。需要引入CGLIB库。例如:
class TargetObject {
public void execute() {
System.out.println("TargetObject execute method");
}
}
class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before CGLIB method call");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After CGLIB method call");
return result;
}
}
public class CglibMain {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetObject.class);
enhancer.setCallback(new CglibProxy());
TargetObject proxy = (TargetObject) enhancer.create();
proxy.execute();
}
}
- 原理:JDK动态代理通过反射在运行时创建代理类字节码并加载,代理类实现了目标接口,方法调用时会转发到
InvocationHandler
的invoke
方法。CGLIB动态代理通过继承目标类,在子类中采用方法拦截的方式,使用MethodInterceptor
对方法进行增强。
反射机制原理
- 概念:反射是指在运行时获取一个类的所有信息,包括类的属性、方法、构造函数等,并可以在运行时操作这些成员。
- 实现方式:
// 方式一
Class<?> clazz1 = Class.forName("java.lang.String");
// 方式二
Class<String> clazz2 = String.class;
// 方式三
String str = "Hello";
Class<? extends String> clazz3 = str.getClass();
try {
Class<?> clazz = Class.forName("com.example.MyClass");
// 获取构造函数并创建对象
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
// 获取方法并调用
Method method = clazz.getMethod("myMethod");
method.invoke(obj);
// 获取属性并操作
Field field = clazz.getField("myField");
field.set(obj, "new value");
} catch (Exception e) {
e.printStackTrace();
}
- 原理:Java的反射机制是基于
Class
对象实现的。JVM在加载类时,会为每个类创建一个对应的Class
对象,通过这个Class
对象可以获取类的各种信息。反射操作通过Method
、Field
、Constructor
等类来完成对类成员的动态操作。
在通用框架中融合三者的设计思路
- RPC框架:
- 设计思路:
- 多态:定义服务接口,不同的服务实现类实现该接口,通过多态实现服务的不同业务逻辑。例如,定义
UserService
接口,有UserServiceImpl
和MockUserServiceImpl
等实现类。
- 动态代理:在客户端,使用动态代理创建代理对象来调用远程服务。代理对象负责将方法调用封装成网络请求并发送到服务端,接收服务端响应并返回。在服务端,也可以使用动态代理对服务方法进行增强,如添加权限校验等。
- 反射:服务端通过反射获取服务实现类的方法信息,根据客户端请求调用相应方法。同时,在服务注册时,通过反射获取服务接口和实现类的信息进行注册。
- 核心代码片段:
interface UserService {
String getUserInfo(int id);
}
class UserServiceProxy implements InvocationHandler {
private String serverAddress;
public UserServiceProxy(String serverAddress) {
this.serverAddress = serverAddress;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 封装网络请求
String request = method.getName() + ":" + Arrays.toString(args);
// 发送请求到服务端
String response = sendRequest(request, serverAddress);
return response;
}
private String sendRequest(String request, String serverAddress) {
// 实际网络请求代码,这里简单返回
return "Mock response";
}
}
public class RPCClient {
public static void main(String[] args) {
UserServiceProxy proxyHandler = new UserServiceProxy("127.0.0.1:8080");
UserService userService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
proxyHandler);
String result = userService.getUserInfo(1);
System.out.println(result);
}
}
- **服务端反射调用**:
class UserServiceImpl implements UserService {
@Override
public String getUserInfo(int id) {
return "User info for id " + id;
}
}
public class RPCServer {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
// 模拟接收请求
String request = "getUserInfo:[1]";
String[] parts = request.split(":");
String methodName = parts[0];
String argStr = parts[1].replaceAll("\\[|\\]", "");
int arg = Integer.parseInt(argStr);
try {
Method method = UserServiceImpl.class.getMethod(methodName, int.class);
String response = (String) method.invoke(userService, arg);
System.out.println("Server response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- AOP框架:
- 设计思路:
- 多态:定义切面接口,不同的切面实现类实现该接口,通过多态实现不同的切面逻辑,如日志切面、事务切面等。
- 动态代理:使用动态代理在目标对象方法调用前后插入切面逻辑。无论是JDK动态代理还是CGLIB动态代理都可以实现这一功能,代理对象在调用目标方法前后执行切面逻辑。
- 反射:通过反射获取目标类的方法信息,判断哪些方法需要应用切面逻辑。同时,在切面逻辑中,也可以通过反射获取目标方法的参数、返回值等信息进行更复杂的操作。
- 核心代码片段:
interface LoggingAspect {
void beforeMethod();
void afterMethod();
}
class LoggingAspectImpl implements LoggingAspect {
@Override
public void beforeMethod() {
System.out.println("Before method execution");
}
@Override
public void afterMethod() {
System.out.println("After method execution");
}
}
- **动态代理应用切面**:
class TargetObject {
public void execute() {
System.out.println("TargetObject execute method");
}
}
class AOPProxy implements InvocationHandler {
private Object target;
private LoggingAspect aspect;
public AOPProxy(Object target, LoggingAspect aspect) {
this.target = target;
this.aspect = aspect;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aspect.beforeMethod();
Object result = method.invoke(target, args);
aspect.afterMethod();
return result;
}
}
public class AOPMain {
public static void main(String[] args) {
TargetObject target = new TargetObject();
LoggingAspect aspect = new LoggingAspectImpl();
AOPProxy proxyHandler = new AOPProxy(target, aspect);
TargetObject proxy = (TargetObject) Proxy.newProxyInstance(
TargetObject.class.getClassLoader(),
TargetObject.class.getInterfaces(),
proxyHandler);
proxy.execute();
}
}
性能和可维护性影响分析
- 性能影响:
- 多态:多态本身对性能影响较小,由于动态绑定是JVM优化过的机制,运行时确定方法调用开销不大。但如果继承体系复杂,可能导致类加载时间变长。
- 动态代理:JDK动态代理由于使用反射,在方法调用时会有一定的性能开销,特别是在高频调用场景下。CGLIB动态代理虽然避免了接口限制,但由于采用继承方式,生成的代理类字节码较大,也会带来一定性能损耗。不过,现代JVM对反射和动态代理都有优化,合理使用对性能影响可控。
- 反射:反射操作性能开销较大,因为需要在运行时解析类信息、查找方法和字段等。频繁使用反射会显著降低性能,如在循环中进行反射操作应尽量避免。在通用框架中,应尽量减少反射的使用频率,如在初始化阶段完成大部分反射操作,而不是在运行时频繁调用。
- 可维护性影响:
- 多态:提高了可维护性,通过接口和抽象类,代码结构更清晰,不同实现类的功能易于扩展和修改,符合开闭原则。
- 动态代理:增强了代码的可维护性,将通用的功能(如日志、事务)从业务代码中分离出来,通过代理统一管理。但代理逻辑复杂时,调试可能会变得困难,需要清晰的文档和良好的代码结构。
- 反射:降低了代码的可读性和可维护性,因为反射操作使代码的静态结构不明显,难以直观理解。但在通用框架中,合理封装反射操作,提供清晰的接口,可以在一定程度上缓解这一问题。例如,在RPC框架中,将反射操作封装在服务注册和调用的内部逻辑中,对外提供简单的服务调用接口。