JAVA核心技术学习笔记--反射

作者: Limmerence | 来源:发表于2018-08-09 18:42 被阅读0次

    反射

    反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵Java代码的程序。
    使用反射,能在设计或者在添加新类的时候,能够快速应用开发工具动态的查询新添加类的能力。

    能够分析类能力的程序称为 反射 反射功能极其强大可以用来

    • 在运行时分析类的能力
    • 在运行时查看对象
    • 实现通用的数组来操作代码
    • 利用Method对象,此对象很类似C++中的函数指针

    1,前提介绍: Class 类

    在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标识,这个信息跟踪着每个对象专属的类。
    Class类就是用来专门保存这些类的信息,通过Object类中的getClass()方法得到每个类的Class类实例

    Person p;
    Class cl = p.getClass();
    

    一个Class类可以用来保存一个类的属性,如:

    p.setName("ZhangSan");
    System.out.print(cl.getName()+" "+p.getName());
    

    此时程序会输出

    Person ZhangSan
    

    Class的getName()方法可以得到这个类的类名。

    获得类的Class实例的第2种方法:Class.forName(类名)

    String className = "com.henu.Person";
    Class cl = Class.forName(className);
    

    第三种方法,直接 类名.class

    Class class1 = Person.class;
    Class class2 = Random.class;
    

    通过获得的Class实例再创建类的实例

    Class cl = p.getClass();
    Perons p = cl.newInstance();
    

    以上代码在使用时要放在try catch代码块里,来处理捕获的异常


    2,利用反射分析类的能力,检查类的结构

    java.lang.reflect包中有三个类

    • Field 描述类的域 getType()方法可以返回描述域所属类型的Class对象
    • Method 描述类的方法
    • Constructor 描述类的构造器

    这三个类都有一个getModifiers的方法,描述各自的访问修饰符,public private final...等,
    再通过Modifier类中的isPublic,isPrivate,isXxx方法对其进行判断.

    • getFields

    • getMethods

    • getConstructors
      这三个方法分别返回类提供的public域,方法,构造器数组,其中包括超类的共有成员.

    • getDeclareFields

    • getDeclareMethods

    • getDeclaredConstructors
      这三个方法将返回类中声明的全部域,方法和构造器,包括private,protect,但不包括超类的共有成员

    在运行时使用反射分析对象

    接下来进一步查看数据域的实际内容给

    Person p = new Person("张三",20);
    Class cl = p.getClass();
    Field name = cl.getDeclaredField("name");//获得Person 类的name域;
    
    Object value = name.get(p); // 此时value = "张三"
    
    
    

    值的注意的一点是,若name域在Person类中是私有的,代码会抛出异常,我们需要对获得的数据域进行设置

    
    Person p = new Person("张三",20);
    Class cl = p.getClass();
    Field name = cl.getDeclaredField("name");//获得Person 类的name域;
    
    name.setAccessible(true);
    
    Object value = name.get(p); // 此时value = "张三"
    
    

    setAccessible方法是AccessibleObject类中的一个方法,它是Field,Method,Constructor类的共有超类,是为了调试,持久存储和相似调试提供的.

    若数据域是基本数据类型,可以使用域.getDouble(类的实例),getInt(类的实例),也可以继续使用get( )方法,反射机制会自动的将这个域值装箱,变成Double,Integer。

    .set(obj,value)方法可以将obj对象的指定域进行赋值

    name.set(p,"李四")//此时p对象的name为李四
    

    3,使用反射编写泛型数组代码

    java.lang.reflect包中的Array类允许动态的创建数组
    以前我们学过Arrays类中有个copyOf()方法,可以用来拓展已经满了的数组.

    Person[ ] team = new Person[10];
    
    team = Arrays.copyOf(team,2*team.length);  
    

    我们可以用反射写一个通用的拓展数组长度的方法,能拓展任意类型的数组。
    首先我们写一个错误的例子

    public static Object[] badCopyOf(Object[] a,int newLength) {
        Object[] newArray = new Object[newLength];
        System.arrayCopy(a,0,newArray,0,Math.min(a.length,newLength));//第四个参数是要从原数组复制元素的个数
        return newArray;
    }
    

    为什么这个代码是错的呢,因为这个函数返回的数组类型是对象数组类型(Object [])
    一个对象数组不能转化成特定的类的数组(Object[]->person[]),这样做会产生异常,其实,当我们把Person[]转化成 Object[] 再转化成Person[]是可以的,但是一个一开始就是Object[]的数组是不能转的,我们可以通过下面这个方法

    Object newArray = Array.newInstance(componentType,int newLength);
    

    java.lang.reflect.Array中的静态方法newInstance方法能够构造新的数组,第一个参数是元素的类型,第二个是数组长度。所以现在我们需要获得新数组的元素类型和数组长度。

    • 获得数组的长度可以通过.getLength()或者Array.getLength(数组)获得
    • 获得数组的元素类型可以通过Class类的getComponentType()获得

    所以我们现在要实现一个通用的拓展任意类型的方法要经过3步

    1. 首先获得数组的Class对象
    2. 确认它是一个数组类型(通过Class类的isArray()方法)
    3. 使用Class类的getComponentType()方法确定数组的类型
    public static Object goodCopyOf(Object a,int newLength) {
        Class cl = p.getClass();
        if (cl.isArray()) return null;//如果不是数组类型就结束
        
        Class componentType = cl.getComponentType();//获得数组的元素类型
        int length = a.getLength();
        Object newArray = Array.newInstance(componentType,length);
        System.arrayCopy(a,0,newArray,0,Math.min(length,newLength));
        return newArray;
    }
    

    这样这个方法就可以用了来拓展任意类型的数组了

    int[] a = {1,2,3,4,5};
    a = (int [])goodCopyOf(a,10);
    

    通过Method的invoke方法调用类的任意方法

    两个方法
    Method method = Person.getClass().getMethod(方法名,方法的参数类型.class)
    method.invoke(方法隶属的类的实例,方法的参数值)

    //动态构造InvokeTest类的实例
    Class<?> classType = InvokeTest.class;
    Object invokeTest = classType.newInstance();
    
    //动态构造InvokeTest类的add(int num1, int num2)方法,标记为addMethod的Method对象
    Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
    
    //动态构造的Method对象invoke委托动态构造的InvokeTest对象,执行对应形参的add方法
    Object result = addMethod.invoke(invokeTest, new Object[]{1, 2});
    

    相关文章

      网友评论

        本文标题:JAVA核心技术学习笔记--反射

        本文链接:https://www.haomeiwen.com/subject/nfccbftx.html