Java反射

88 阅读3分钟

一、关于反射的理解

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

二、反射能提供的功能有哪些?

  • 运行时判断任意一个对象所属的类
  • 运行时判断任意构造一个类的对象
  • 运行时判断任意一个类所具有的成员变量和方法
  • 运行时获取泛型信息
  • 运行时调用任意一个对象的成员变量和方法
  • 运行时处理注解
  • 生成动态代理

三、获取Class的实例的有几种方式?

  1. 调用运行时类的属性:.class 
Class clazz1 = Person.class;

System.out.println(clazz1);
  1. 通过运行时类的对象,调用getClass()
Person p1 = new Person();

Class clazz2 = p1.getClass();

System.out.println(clazz2);
  1. 调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.atguigu.java.Person");

System.out.println(clazz3);
 
System.out.println(clazz1 == clazz2);

System.out.println(clazz1 == clazz3);
  1. 使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();

Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");

System.out.println(clazz4);

System.out.println(clazz1 == clazz4);

四、如何调用运行时类的指定结构?

  1. 调用指定属性
public void testField1() throws Exception {

    Class clazz = Person.class;

    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();

    //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
    Field name = clazz.getDeclaredField("name");

    //2.保证当前属性是可访问的
    name.setAccessible(true);

    //3.获取、设置指定对象的此属性值
    name.set(p,"Tom");

    System.out.println(name.get(p));
    
}
  1. 调用指定方法
public void testMethod() throws Exception {

        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        /*
        1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
         */
        Method show = clazz.getDeclaredMethod("show", String.class);

        //2.保证当前方法是可访问的
        show.setAccessible(true);


        /*
        3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
         */

        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");

        System.out.println(returnValue);

        System.out.println("*************如何调用静态方法*****************");

        // private static void showDesc()

        Method showDesc = clazz.getDeclaredMethod("showDesc");

        showDesc.setAccessible(true);

        //如果调用的运行时类中的方法没返回值,则此invoke()返回null
//        Object returnVal = showDesc.invoke(null);

        Object returnVal = showDesc.invoke(Person.class);

        System.out.println(returnVal);//null
        
    }
  1. 调用指定构造器
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、代理模式的原理

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。 (代理类和被代理类调用同一方法)

2、静态代理
  • 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。
  • 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。
3、动态代理

特点: 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

实现的两个关键

问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。

(通过Proxy.newProxyInstance()实现)

问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。

(通过InvocationHandler接口的实现类及其方法invoke())

Java代理(Proxy)模式