Java反射学习笔记

一、概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

1. Class对象

所有类都是在对其第一次使用时,类加载器首先会检查这个类的Class对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class文件。了使用类而做的准备工作一般有以下3个步骤:

  • 加载:由类加载器完成,找到对应的字节码,创建一个Class对象
  • 链接:验证类中的字节码,为静态域分配空间
  • 初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块

所以,运行着的每个类都会有一个对应的Class对象,这个对象包含着类的几乎所有有关信息。

想要在运行时获取一个类的信息,首先需要获取它的Class对象的引用,使用功能Class.forName(“Base”)可以实现该目的,或者使用base.class。

注意,使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象,所以建议使用forName和getClass方法:

        //第一种方法:forName
        try {
            Class<?> class1 = Class.forName("com.app.Person");

            System.out.println( class1 );
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //第二种方法:getClass
        Person person = new Person();  
        Class<?> class3 = person.getClass();
2. 向下转型与instanceof
    Person per = new Student(); //向上转型

    Student stu = (Student) per; //向下转型

因为Java支持多态,一个父类类型的引用可以指向任一它的子类的实例,但向下转型就不一样,父类类型的引用可以指向任一子类的实例,所以per还可能指向的是Teacher子类的实例,如果贸然把per强制转换为Student类型的引用,有可能会出错:

Exception in thread "main" java.lang.ClassCastException: human.Person cannot be cast to human.Student at human.TestMain.main(TestMain.java:15)

所以,在进行向下转型的时候一定要先用instanceof判断引用指向的实例的类型:

if(stu instanceof Student){
  Student stu = (Student) per;
}
3. 反射:获取运行时类信息

Class类与java.lang.reflect类库一起对反射进行了支持,该类库包含Field、Method和Constructor类,这些类的对象由JVM在启动时创建,用以表示未知类里对应的成员。这样的话就可以使用Contructor创建新的对象,用get()和set()方法获取和修改类中与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等许多便利的方法,以返回表示字段、方法、以及构造器对象的数组,这样,对象信息可以在运行时被完全确定下来,而在编译时不需要知道关于类的任何事情。


二、反射使用总结

(1)获取成员的方法:

Class<?> class1 = Class.forName("com.app.Person");

//获取所有的public方法
Method[] methods =  class1.getMethods();
Method getMethod(String name, Class[] params) //根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods() //返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class[] params) //根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods() //返回该类中的所有的方法数组(不分public和非public属性)

(2)获取所有实现的接口:getInterfaces()

Class<?> class1 = Class.forName("com.app.Person");

//获取所有的接口
Class<?>[] interS = class1.getInterfaces() ;

(3)获取父类:getSuperclass()

Class<?> superclass = class1.getSuperclass() ;

(4)获取所有的构造函数:getConstructors()

Constructor<?>[] constructors = class1.getConstructors();
Constructor getConstructor(Class[] params)     //根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors()     //返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class[] params)     //根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors()    //返回该类中所有的构造函数数组(不分public和非public属性)

代码示例:

        try {

            //创建类
            Class<?> class1 = Class.forName("com.app.Person");

            //无参构造函数
            Object object = class1.newInstance() ;

            //有参构造函数:一个参数
            Constructor<?> constructor =  class1.getDeclaredConstructor( String.class ) ;
            constructor.newInstance( "1000" ) ;

            //有参构造函数:二个参数
            Constructor<?> constructor2 =  class1.getDeclaredConstructor( String.class , String.class ) ;
            constructor2.newInstance( "1001" , "jack" ) ;

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace() ;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

(5)获取所有的属性:getDeclaredFields()

Field[] field = class1.getDeclaredFields();
  • getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
  • getFields()获得某个类的所有的公共(public)的字段,包括父类。
Field getField(String name)    //根据变量名,返回一个具体的具有public属性的成员变量
Field[] getFields()    //返回具有public属性的成员变量的数组
Field getDeclaredField(String name)    //根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredField()    //返回所有成员变量组成的数组(不分public和非public属性)

代码示例:

  public static void main(String[] args) throws Exception {
    Class<?> clz = Class.forName("A");
    Object o = clz.newInstance();
    Method m = clz.getMethod("foo", String.class);
    for (int i = 0; i < 16; i++) {
      m.invoke(o, Integer.toString(i));
    }
  }

(6)创建实例:newInstance()

//创建实例化:相当于 new 了一个对象
Object object = class1.newInstance() ;
//向下转型
Person person = (Person) object ;

三、常见场景

1. 动态代理

代理模式是为了提供额外或不同的操作,而插入的用来替代”实际”对象的对象,这些操作涉及到与”实际”对象的通信,因此代理通常充当中间人角色。Java的动态代理比代理的思想更前进了一步,它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的策略。以下是一个动态代理示例:

接口和实现类:

public interface Interface {
    void doSomething();
    void somethingElse(String arg);
}
public class RealObject implements Interface {
    public void doSomething() {
        System.out.println("doSomething.");
    }
    public void somethingElse(String arg) {
        System.out.println("somethingElse " + arg);
    }
}

动态代理对象处理器:

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxyed;

    public DynamicProxyHandler(Object proxyed) {
        this.proxyed = proxyed;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        System.out.println("代理工作了.");
        return method.invoke(proxyed, args);
    }
}

测试类:

public class Main {
    public static void main(String[] args) {
        RealObject real = new RealObject();
        Interface proxy = (Interface) Proxy.newProxyInstance(
                Interface.class.getClassLoader(), new Class[] {Interface.class},
                new DynamicProxyHandler(real));

        proxy.doSomething();
        proxy.somethingElse("luoxn28");
    }
}
2. 通过反射获取泛型的类型

(1)如果是继承基类而来的泛型,就用 getGenericSuperclass() , 转型为 ParameterizedType 来获得实际类型:

ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericSuperclass();  
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();  
for(Type actualTypeArgument: actualTypeArguments) {  
    System.out.println(actualTypeArgument);  
}  

(2)如果是实现接口而来的泛型,就用 getGenericInterfaces() , 针对其中的元素转型为 ParameterizedType 来获得实际类型:

ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];  
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();  
for (Type actualTypeArgument : actualTypeArguments) {  
    System.out.println(actualTypeArgument);  
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值