java反射机制与动态代理

本文深入讲解Java反射机制,包括如何动态获取类的信息和调用方法。介绍了反射的基础Class类及其常用方法,演示了如何通过反射操作成员变量、构造方法和成员方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反射相关:

  • Java反射机制是在运行状态中
  • 对于任意一个类,都能知道这个类的所以属性和方法;
  • 对于任何一个对象,都能够调用它的任何一个方法和属性;
  • 这样动态获取新的以及动态调用对象方法的功能就叫做反射。
  • 反射的基础是Class(java.lang.Class)类。

一、java代码在计算机里的三个时期

    class文件其实是字节码文件,也可认为是二进制文件。

    class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。注意:这里的Class类型是一个名叫Class的class。它长这样:

public final class Class { 
    private Class() {} ... //getField、getConstructor等方法皆在此处定义 
}

    三种时期都可以抽离出在某个类的Class(java.lang.Class)对象,Class对象会将java的一个类对象的内容概括为三个对象,包含所有成员变量的Field[]对象,包含所有构造函数的Constructor[]对象,包含所有成员方法的Method[]对象。

    由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。

    这种通过Class实例获取class信息的方法称为反射(Reflection)。

  • 源码阶段使用Class.forName("全包名")的方法加载,即要有src下的路径和类名,适合配置文件使用,因为这种方式是靠字符串加载类对象的。
  • Class类对象阶段使用类名.class的方法加载,多用于参数传递。
  • 运行阶段使用对象名.getClass()加载进去,适用于对象已经建立的。

二、设置 Class cls为从student类中抽取的Class对象。

    cls有常用的几种方法,有cls.getName()获取类名等。

1. 针对成员变量的有(以下四种方法返回field或field[]的类型):

field:getField(String name) //获取类型为public名字为name的成员变量 
field[]:getFields() //获取所有类型为public的成员变量 
field:getDeclaredField(String name) //获取名字为name的成员变量,类型不限 
field[]:getDeclaredFields() //获取所有的成员变量,只要声明过的,类型不限

    对于field对象常用的有set和get方法,

//该方法返回类型Object对象,实际为field所保存的成员变量的类型,因此可向下转型。
//obj为student实例化出来的对象。返回值为obj对象上字段field所表示的值。
get(Object obj)  

//该方法返回类型为void对象。obj为student实例化出来的对象。
//value为将在obj对象的 Field 字段上所设置的新值。
set(Object obj,Object value)  

2. 针对构造方法的有(以下四种方法返回Constructor或Constructor<?>[]的类型):

//获取类型为public,参数为Class<?>... parameterTypes的构造方法
//Class<?>... parameterTypes取值String.class、Int.class这样的类型
//具体参数看实际构造方法。
Constructor:getConstructor(Class<?>... parameterTypes)   

//获取所有类型为public的构造方法。 
Constructor<?>[]:getConstructors()

//获取所有构造方法。类型不限。
Constructor<?>[]:getDeclaredConstructors() 

//获取所有构造方法。类型不限,参数同上。
Constructor:getDeclaredConstructor(Class<?>... parameterTypes) 

    对于Constructor对象常用的有newInstance方法
 

//该方法返回自类型为Object,如果该构造函数有参数要在后面加上实参去实例化对象。
constructor.newInstance(Object... initargs)  

//该方法更简单直接,但只适用于无参的构造函数实例化
//cls为Class类,例如Student.class,该实例化方法省去了获取构造方法的过程
cls.newInstance()

3. 针对成员方法的有(以下四种方法返回Method或Method[]的类型):

//获取类型为public,名字为name,参数为Class<?>... parameterTypes的成员方法方法。 
//Class<?>... parameterTypes取值如在获取构造函数所述。
Method:getMethod(String name,Class<?>... parameterTypes)  

Method[]:getMethods() //获取获取类型为public的所有成员方法。 

//获取名字为name,参数为Class<?>... parameterTypes的成员方法方法。 
//Class<?>... parameterTypes取值如在获取构造函数所述。类型不限。 
Method:getDeclaredMethod(String name,Class<?>... parameterTypes) 

Method[]:getDeclaredMethods() //获取所有成员方法,类型不限。

    对于Method对象常用的有invoke方法

//该方法返回类型Object对象
//实际为Method所代表的成员方法所返回的类型,因此可向下转型
//obj为student实例化出来的对象。Object... args为传入方法的实参
//返回值为obj执行Method后的结果,如果原方法返回值为void则返回null
invoke(Object obj,Object... args)

    注:AccessibleObject 类是Field、Constructor和Method都基类,因此这三者都可以通过 名字.setAccessible(true)的形式取消Java 语言访问控制检查,从而导致反射机制具有了能够更改私有变量的能力。请注意使用。有些安全框架会禁用setAccessible(true)方法,因此它也不是什么时候都能起作用的。

4. 获取父类及接口

Class cla = Integer.class; 

//获取父类的Class对象 
Class n = cla.getSuperclass(); 

//获取所有继承接口的Class对象 
Class[] is = s.getInterfaces(); 

//instanceof可用来判断两个对象间是否具有继承关系
Object n = Integer.valueOf(123); 
boolean isDouble = n instanceof Double; // false

动态代理相关:

//动态代理的底层实现依靠反射
public class Main {
    public static void main(String[] args) {
        //代理的具体内容
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}
//动态代理实际上是JDK在运行期根据代理内容动态创建class字节码并加载的过程。
//从而产生接口的实例
interface Hello {
    void morning(String name);
}

    把上面的动态代理改写为静态实现类大概长这样(这个类由JDK帮我们编写):

public class HelloDynamicProxy implements Hello {
    InvocationHandler handler;
    public HelloDynamicProxy(InvocationHandler handler) {
        this.handler = handler;
    }
    public void morning(String name) {
        handler.invoke(
           this,
           Hello.class.getMethod("morning"),
           new Object[] { name });
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值