Java重要知识点

Java重要知识点,第1张

Java重要知识点 为什么 Java 中只有值传递?

两个概念:

  • 形参 & 实参
  • 值传递 & 引用传递
形参 & 实参

方法的定义可能会用到 参数(有参数的方法),参数在程序语言中分为:

  • 实参(实际参数):用于传递给函数 / 方法的参数,必须有确定的值。
  • 形参(形式参数):用于定义函数 / 方法,接受参数,不需要有确定的值。
String hello = "Hello";
// hello 为实参
sayHello(hello);
// str 为形参
void sayHello(String str) {
    System.out.println(str);
}
值传递 & 引用传递

程序设计语言将实参传递给方法(或函数)的方式分为两种:

  • 值传递: 方法接收的是实参值的拷贝,会创建副本
  • 引用传递: 方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。

反射机制 何为反射?

反射之所以被称为框架的灵魂,主要是应为它赋予了我们在运行时分析类以及类中方法的能力。

通过反射可以获取任意一个类的所有属性和方法,还可以调用这些方法和属性

反射的应用场景

代码案例

public class DebugInvocationHandler implements InvocationHandler {
    
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("after method " + method.getName());
        return result;
    }

反射实战 获取 Class 对象的四种方式

如果我们动态获取到这些信息,我们需要依靠 Class 对象。 Class 类对象将一个类的方法,变量等信息告诉运行的程序。Java 提供了四种方式获取 Class 对象。

1. 知道具体类的情况下可是使用

Class clunbarClass = TargetObject.class

但是我们一般不知道具体的类,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化

2. 通过 Class.forName() 传入类的路径获取:

Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");

3. 通过对象实例 instance.getClass() 获取

TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();

4. 通过类加载器 xxxClassLoader.loadClass 传入路径获取

Class clazz = ClassLoader.loadClass("cn.javaguide.TargetObject");

通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一些步骤,静态块和静态对象不会得到执行。

反射基本 *** 作
  1. 创建又给我么你要使用反射 *** 作的类
public class TargetObject {
    private String value;
    
    public TargetObject() {
        value = "javaGuide";
    }
    
    public void publicMethod(String s){
        System.out.println("I love " + s);
    }
    
    private void privateMethod() {
        System.out.println("value is " + value)
    }
}
  1. 使用反射 *** 作这个类的方法以及参数
public class Main {
    public static void main(String[] args) {
        
        Class targetClass = Class.forName("cn.javaguide.TargetObject");
        TargetObject targetObject = (TargetObject) targetClass.newInstance();
    	
        Method[] methods = targetClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName())
        }
    }
}

代理讲解 1. 代理模式

代理模式是一种比较好理解的设计模式。简单来说就是我们使用代理对象来代替对真实对象的访问,这样我们就可以在不修改原目标对象的前提下,提供额外的功能 *** 作,扩展目标对象的功能

代理模式的主要作用是扩展目标对象的功能,如果说在目标对象的某个方法执行前后你可以增加一些自定义的 *** 作。

例如:你找了小红来帮你问话,小红就可以看作是代理你的代理对象,代理的行为(方法)是问话。

2. 静态代理

**静态代理中,我们对目标对象的每一个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。**实际应用场景非常少

从 JVM 层面来说,静态代理在编译时就将接口,实现类,代理类这些都变成了一个个实际的 class 文件

静态代理实现步骤:

  1. 定义一个接口及其实现类
  2. 创建一个代理类同样实现这个接口
  3. 将目标对象注入代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

代码演示

1. 定义发短信的接口

public interface SmsService {
    String send(String message);
}

2. 实现发短信的接口

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + messagge);
        return message;
    }
}

3. 创建代理类并同样实现发送短信的接口

public class SmsProxy implements SmsService {
    
    private fianl SmsService SmsService;
	
    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }
    
    public String send(String message) {
        // 调用方法之前,我们可以添加自己的 *** 作
        System.out.println("before method send()");
        smsService.send(message);
        // 调用方法之后,我们同样可以添加自己的 *** 作
        System.out.println("after method send()");
        return null;
    }
}

4. 实际应用

public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsService();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

运行上述代码之后,控制台打印出:

before method send()
send message:java
after method send()

动态代理

相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类(CGLIB 动态代理机制)。

从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中

动态代理在我们日常开发中使用的相对较少,但是在框架中的几乎是必用的一门技术。掌握动态代理非常有利于各种框架学习。

1. JDK 动态代理机制 1.1 介绍

在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心

Proxy 类中使用频率最高的方法是:newProxyInstance(),这个方法主要用来生成一个代理对象。

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    .........
}

三个参数

  1. loader: 类加载器,用于加载代理对象
  2. **interfaces:**被 代理实现的一些接口
  3. h: 实现了 InvocationHandler 接口对象
1.2 代码实现

1. 定义发短信的接口

public interface SmsService {
	String send(String messagge);
}

2. 实现发短信的接口

public class SmsServiceImpl implements SmsService {
    public String send(String message){
        System.out.println("send message:" + message);
        return message;
    }
}

3. 定义一个 JDK 动态代理类

public class DebuugInvocationHandler implements InvocationHandler {
    
    private final Object target;
    
    public DebugInvocationHandler(Object target) {
        this.target = target;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        // 调用方法之前,我们可以添加自己的 *** 作
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        // 调用方法之后,我们同样可以添加自己的 *** 作
        System.out.println("after method " + method.getName());
        // 调用方法之后,我们同样可以添加自己的 *** 作
        System.out.println("after method " + method.getName());
        return result;
    }
}

invoke() 方法:当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。

4. 获取代理对象的工厂类

public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance (
        	target.getClass().getClassLoader(),	// 目标类的类加载
            target.getClass().getInterfaces(),	// 代理需求实现的接口,可指定多个
            new DebugInvocationHandler(target)	// 代理对象对应的自定义 InvocationHandler
        )
    }
}

getProxy() :主要通过 Proxy.newProxyInstance() 方法获取某个类的代理对象

5. 实际使用

SmsService.smsService = (SmsService) JdkProxyFactory.getProxy(new SmsService());
smsService.send("java");

运行上面代码之后,可以打印出

before method send
send message:java
after method send

2. CGLIB 动态代理机制

2.1 介绍

JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。

为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。

CGLIB是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都用 CGLIB。

在CGLIB 动态代理机制中 methodInterceptor 接口和 Enhancer 类是核心

你需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法

public interface MethodInterceptor extends Callback {
    // 拦截被代理类中的方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}
  1. obj: 被代理的对象(需要增强的对象)
  2. method: 被拦截的方法(需要增强的方法)
  3. args: 方法入参
  4. proxy: 用于调用原始方法

我们可以通过 Enhancer 类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5693186.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存