如何快速掌握JAVA反射机制 ?此篇将开启您的知识盲区!!!

1.1 反射机制

反射机制的作用和功能以及获取Class实例的方法

1.说白了就是可以对任意类的方法,对象,属性,构造器,变量进行操作(私有属性也可以)

2.反射相关的API:

java.lang.Class:代表一个类 java.lang.reflect.Method:代表类的方法 java.lang.reflect.Field:代表类的成员变量 java.lang.reflect.Constructor:代表类的构造器

3.获取Class的实例的理解和方式(四种方法)

   理解: 哪些类型可以有Class对象?

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

  • 接口

  • 数组

  • 枚举

  • 注解

  • 基本数据类型

  • void

方法一:调用运行时类的属性:.class
Class clazz1  = Person.class; 方法二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();方法三:调用Class的静态方法:forName(String classPath)
Class clazz3=  Class.forName ("路径地址" );//使用频率多
​
方法四:使用类的加载器:ClassLoader
ClassLoader classLoader =类.class.getClassLoader();
Class clazz4 = classLoader.loadClass("目标类的地址")

反射机制的优缺点

优点

  • 灵活性:可以在运行时动态地创建和操作对象,适用于需要灵活性和扩展性的场景。
  • 调试和测试:可以方便地获取和修改类的内部状态,便于调试和测试。

缺点

  • 性能开销:反射操作比直接调用方法或访问字段要慢,因为涉及到动态解析和安全检查。
  • 安全性:反射可以绕过访问修饰符的限制,可能会破坏类的封装性。
  • 可读性:反射代码通常比直接调用方法或访问字段的代码更复杂,可读性较差。

1.2 了解:类的加载过程

1.3ClassLoader具体用法

使用ClassLoader加载配置文件Properties

ClassLoader与反射机制的应用

1.4通过反射创建运行时类的对象

举例体会反射的动态性

1.5获取运行时类的指定结构

提供具有丰富内容的Person

//接口
public interface MyInterface {
    void info();
}
​
//注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello";
​
}
​
//父类
public class Creature<T> implements Serializable {
    private char gender;
    public double weight;
​
    private void breath(){
        System.out.println("生物呼吸");
    }
​
    public void eat(){
        System.out.println("生物吃东西");
    }
​
}
​
//Person类
@MyAnnotation(value="hi")
public class Person extends Creature<String> implements Comparable<String>,MyInterface{
​
    private String name;
    int age;
    public int id;
​
    public Person(){}
​
    @MyAnnotation(value="abc")
    private Person(String name){
        this.name = name;
    }
​
     Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    @MyAnnotation
    private String show(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }
​
    public String display(String interests,int age) throws NullPointerException,ClassCastException{
        return interests + age;
    }
​
​
    @Override
    public void info() {
        System.out.println("我是一个人");
    }
​
    @Override
    public int compareTo(String o) {
        return 0;
    }
​
    private static void showDesc(){
        System.out.println("我是一个可爱的人");
    }
​
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}
1. 获取当前运行时类的属性结构
方法作用
public Constructor<T>[] getConstructors()返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors()返回此 Class 对象表示的类声明的所有构造方法。
  • Field方法中:

方法作用
public int getModifiers()以整数形式返回此Field的修饰符
public Class<?> getType()得到Field的属性类型
public String getName()返回Field的名称
2. 获取当前运行时类的方法结构
方法作用
public Method[] getMethods()返回此Class对象所表示的类或接口的public的方法
public Method[] getDeclaredMethods()返回此Class对象所表示的类或接口的全部方法
  • Method类中:

方法**作用
public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的参数
public int getModifiers()取得修饰符
public Class<?>[] getExceptionTypes()取得异常信息
3. 获取当前运行时类的构造器结构
方法作用
public Constructor<T>[] getConstructors()返回此 Class 对象所表示的类的所有public构造方法
public Constructor<T>[] getDeclaredConstructors()返回此 Class 对象表示的类声明的所有构造方法。
  • Constructor类中:

方法作用
public int getModifiers()取得修饰符
public String getName()取得方法名称
public Class<?>[] getParameterTypes()取得参数的类型

1.6调用运行时类的指定结构(了解)

1.关于setAccessible方法的使用
  • Method和Field、Constructor对象都有setAccessible()方法。

  • setAccessible启动和禁用访问安全检查的开关。

  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

  • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被 调用,那么请设置为true,使得原本无法访问的私有成员也可以访问,参数值为false则指示反射的对象应该实施Java语言访问检查。

2. 调用运行时类中指定的属性:
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()get()方法就可以完成设置和取得属性内容的操作。
方法作用
public Field getField(String name)返回此Class对象表示的类或接口的指定的publicField
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field

在Field中:

方法作用
public Object get(Object obj)取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value)设置指定对象obj上此Field的属性内容
3. 调用运行时类中的指定的方法
通过反射,调用类中的方法,通过Method类完成。步骤:

1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得 一个Method对象,并设置此方法操作时所需要的参数类型。 2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中 传递要设置的obj对象的参数信息。

在这里插入图片描述

bject invoke(Object obj, Object … args) 说明: ⭕ Object 对应原方法的返回值,若原方法无返回值,此时返回null

⭕ 若原方法若为静态方法,此时形参Object obj可为null

⭕ 若原方法形参列表为空,则Object[] args为null

⭕ 若原方法声明为private,则需要在调用此invoke()方法前,显式调用 方法对象的setAccessible(true)方法,将可访问private的方法。

4. 调用运行时类中的指定的构造器
*
    如何调用运行时类中的指定的构造器
     */
    @Test
    public void testConstructor() throws Exception {
        Class clazz = Person.class;
​
        //private Person(String name)
        /*
        1.获取指定的构造器
        getDeclaredConstructor():参数:指明构造器的参数列表
         */
​
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
​
        //2.保证此构造器是可访问的
        constructor.setAccessible(true);
​
        //3.调用此构造器创建运行时类的对象
        Person per = (Person) constructor.newInstance("Tom");
        System.out.println(per);
​
    }
​
}

1.7代理

1.静态代理的理解:

在Java中,静态代理是一种设计模式,用于在不修改目标对象的情况下,通过代理对象来控制对目标对象的访问。静态代理的特点是在编译时就已经确定了代理类和目标类的关系,因此称为静态代理。

2.静态代理的基本思想是:

  1. 定义接口:代理类和目标类都实现同一个接口。

  2. 创建目标对象:目标对象是实际执行任务的对象。

  3. 创建代理对象:代理对象包含一个目标对象的引用,并在调用目标对象的方法前后执行一些额外的逻辑

1. 定义接口
public interface Subject {
    void request();
}
CopyInsert
接口 Subject:定义了一个方法 request(),这个方法将在目标对象和代理对象中实现。接口是代理模式的核心,因为它确保了代理对象和目标对象具有相同的行为。
2. 创建目标对象
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}
CopyInsert
目标对象 RealSubject:实现了 Subject 接口,并提供了 request() 方法的具体实现。在这个例子中,RealSubject 只是简单地打印一条消息,表示它正在处理请求。
3. 创建代理对象
public class ProxySubject implements Subject {
    private RealSubject realSubject;
​
    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }
​
    @Override
    public void request() {
        // 在调用目标对象的方法前执行一些逻辑
        System.out.println("ProxySubject: Preparing to handle request.");
​
        // 调用目标对象的方法
        realSubject.request();
​
        // 在调用目标对象的方法后执行一些逻辑
        System.out.println("ProxySubject: Finished handling request.");
    }
}
CopyInsert
代理对象 ProxySubject:也实现了 Subject 接口,并包含一个 RealSubject 对象的引用。
构造方法:ProxySubject(RealSubject realSubject) 用于初始化代理对象,将目标对象传递给代理对象。
request() 方法:在调用目标对象的 request() 方法前后,代理对象可以执行一些额外的逻辑。在这个例子中,代理对象在调用目标对象的方法前打印一条准备消息,在调用目标对象的方法后打印一条完成消息。
4. 使用代理对象
public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        RealSubject realSubject = new RealSubject();
​
        // 创建代理对象,并将目标对象传递给代理对象
        ProxySubject proxySubject = new ProxySubject(realSubject);
​
        // 通过代理对象调用目标对象的方法
        proxySubject.request();
    }
}
主类 Main:包含 main 方法,用于演示如何使用代理对象。
创建目标对象:RealSubject realSubject = new RealSubject(); 创建一个 RealSubject 对象。
创建代理对象:ProxySubject proxySubject = new ProxySubject(realSubject); 创建一个 ProxySubject 对象,并将目标对象传递给代理对象。
通过代理对象调用目标对象的方法:proxySubject.request(); 通过代理对象调用 request() 方法。在这个过程中,代理对象会在调用目标对象的方法前后执行一些额外的逻辑。

3.动态代理

动态代理是Java中一种高级编程技术,它允许你在运行时创建一个代理对象,这个代理对象可以用来代替另一个对象。动态代理主要用于在不修改原有代码的情况下,增加或修改对象的行为。

1.基本概念

  1. 代理对象:一个代理对象是客户端和真实对象之间的中介。客户端通过代理对象来访问真实对象。

  2. 真实对象:这是你实际想要操作的对象。

  3. InvocationHandler:这是一个接口,你需要实现它来定义代理对象在调用方法时应该执行的操作。

2.实现步骤

  1. 定义接口:首先,你需要定义一个接口,这个接口包含了你希望代理对象能够调用的方法。

  2. 实现接口:创建一个类来实现这个接口,这个类就是你的真实对象。

  3. 创建InvocationHandler:实现InvocationHandler接口,重写invoke方法,在这个方法中定义代理对象在调用方法时应该执行的操作。

  4. 创建代理对象

    使用Proxy.newProxyInstance方法来创建代理对象。这个方法需要三个参数:

    • 类加载器(ClassLoader)

    • 代理对象实现的接口数组

    • InvocationHandler实例

以下是一个简单的动态代理示例:
​
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
​
// 定义一个接口
interface Hello {
    void sayHello();
}
​
// 实现接口的目标类
class HelloImpl implements Hello {
    @Override
    public void sayHello() {
        System.out.println("Hello, world!");
    }
}
​
// 实现InvocationHandler接口
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 invocation");
        Object result = method.invoke(target, args);
        System.out.println("After method invocation");
        return result;
    }
}
​
public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        HelloImpl target = new HelloImpl();
​
        // 创建InvocationHandler实例
        MyInvocationHandler handler = new MyInvocationHandler(target);
​
        // 创建代理对象
        Hello proxy = (Hello) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
​
        // 调用代理对象的方法
        proxy.sayHello();
    }
}
CopyInsert
运行结果
Before method invocation
Hello, world!
After method invocation

动态代理是Java中一种强大的机制,可以在运行时动态地创建代理对象,并在不修改原有代码的情况下增强或修改对象的行为。通过结合反射机制和InvocationHandler接口,可以实现灵活的方法调用处理逻辑。

1.8 简洁性总结全文

反射机制(Reflection)是Java语言的一项强大特性,它允许程序在运行时检查和操作类的结构、方法、字段等信息。通过反射,程序可以动态地创建对象、调用方法、访问字段,甚至可以修改类的行为。反射机制主要通过java.lang.reflect包中的类来实现。

反射机制的主要用途

  1. 动态加载类:在运行时加载类,而不是在编译时。

  2. 获取类的信息:获取类的构造方法、方法、字段等信息。

  3. 创建对象:通过类的构造方法动态创建对象。

  4. 调用方法:通过方法名动态调用对象的方法。

  5. 访问字段:通过字段名动态访问和修改对象的字段。

  6. 实现动态代理:结合InvocationHandler接口,实现动态代理模式。

  反射机制的主要类和接口

  1. Class:表示类的实体,在运行的Java应用程序中表示类和接口。

  2. Field:表示类的字段(成员变量)。

  3. Method:表示类的方法。

  4. Constructor:表示类的构造方法。

  5. Modifier:表示类和成员访问修饰符。

反射机制的基本操作

1. 获取Class对象

有三种主要方法可以获取Class对象:

  • 使用类名.class

    Class<?> clazz = String.class;
  • 使用对象.getClass()

    String str = "Hello";
    Class<?> clazz = str.getClass();
  • 使用Class.forName(String className)

    Class<?> clazz = Class.forName("java.lang.String");
2. 获取类的信息
  • 获取类的构造方法:

    Constructor<?> constructor = clazz.getConstructor(String.class);
  • 获取类的方法:

    Method method = clazz.getMethod("toString");
  • 获取类的字段:

    Field field = clazz.getField("value");
3. 创建对象
  • 使用无参构造方法创建对象:

    Object obj = clazz.newInstance();
  • 使用有参构造方法创建对象:

    Constructor<?> constructor = clazz.getConstructor(String.class);
    Object obj = constructor.newInstance("Hello");
4. 调用方法
  • 调用无参方法:

    Method method = clazz.getMethod("toString");
    Object result = method.invoke(obj);
  • 调用有参方法:

    Method method = clazz.getMethod("concat", String.class);
    Object result = method.invoke(obj, " World");
5. 访问字段
  • 获取字段值:

    Field field = clazz.getField("value");
    Object value = field.get(obj);
  • 设置字段值:

    Field field = clazz.getField("value");
    field.set(obj, "New Value");
    

  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值