高新技术.反射

时间:2022-06-23 03:55:42
高新技术之反射

反射:需要了解Class。

Class是反射的基石,在反射中作用非常重要。Java程序中各个java类都属于同一类事物,描述这些事物的java类名就是Class。

说简单点,它就是java中所有类的统称,用它来描述每一个类的各种属性。其实,每一个类都有一个Class对象,每当编写并编译程序时,就会生成一个Class对象。


创建Class实例对象有三种方式:

通过类名获取,如:

Class cls=String.class;-------->类名.class

通过对象获取,如:

String str=”abc”;

Class cls1=str.getClass();--------->对象.getClass()

通过Class中的静态方法forName()获取

Class cls2=Class.forName(“java.lang.String”);-------->Class.forName(包名+类名)

对于同一个类来说,通过三种方式得到的Class实例对象,都是同一份。当在获取到字节码需要将字节码进行比较时,采用“==”进行比较,因为jvm只有一份字节码,不需要使用equals进行比较。

主要说后两种的区别:

第二种:获取Class实例对象必须要有对象;

第三种:首先它是返回的一个对字节码的引用,当我们通过它获得对一份字节码的引用或者创建Class对象时,其实它有两种方式,如果JVM的内存中没有所对应的字节码,它就会通过类加载器,将硬盘上的.CLASS文件加载到内存中,并返回。如果内存中有已经有了相应的字节码,则直接返回一份字节码。

反射言简意赅就是将java类中的各成员映射成相应的java类,如:构造方法类(Constructor)、字段类(Field)和方法类(Method)。

Constructor类:

获取java类中的所有构造方法

Constructor[]con=Class.forName(String name).getConstructors();

获取java类中指定的构造方法,以获取String中的new String(StringBuffer)为例

Constructor con=String.class.getConstructor(StringBuffer.class); //获取指定的构造函数

String str2=(String)con.newInstance(new StringBuffer("abc"));//通过newInstance创建实例对象

getConstructor(参数)的参数根据构造方法中的参数而定。在通过获取的构造方法创建对象时,创建对象的参数类型需要和获取构造方法时的参数类型一致。同时,Class为方便无参数构造函数的使用,在Class类中可以直接调用newInstance()进行对象的建立。

Field类:

获取java中的字段有两种方法

Field fieldXx=Class.forName(String name).getField(“Xx”);

Field []fields=Class.forName(String name).getFields();
 

//Field类描述java类中的成员变量

ReflectPoint pt1=new ReflectPoint(3,5);

Field fieldY=pt1.getClass().getField("y");

//fieldY的值是多少??fieldY代表Field类的一个实例对象

System.out.println(fieldY.get(pt1));//fieldY对应的不是在对象里的值,而是Field类中的一个成员变量

//获取x变量,x是私有的无法通过常规方法获取

Field fieldX=pt1.getClass().getDeclaredField("x");//通过使用getDeclaredField()可以获取私有变量

fieldX.setAccessible(true);//暴力反射,通过该方法可以将私有字段值反射输出

System.out.println(fieldX.get(pt1));


private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {

Field[]fields=obj.getClass().getFields();
for(Field field:fields){
if(field.getType()==String.class){//字节码用==进行比较,因为只有同一份字节码
String oldV=(String)field.get(obj);

String newV=oldV.replace('b','a');

field.set(obj, newV);//当需要对原有类中的成员修改值是,记住需要重新设定值,使用set()方法。
}
}
}

Method类

获取Method的主要方式是,以String的charAt为例,如下代码:

//Method类     利用反射调用charAt()

Method methodCharAt=str1.getClass().getMethod("charAt", int.class);

//获取指定方法,getMethod(“name”,.class)

System.out.println(methodCharAt.invoke(str1, 1));//invoke()调用,静态调用invoke(null,...)method对应静态方法

通过反射操作数组

int[]a1=new int[]{1,2,3};//对于数组,只要类型和维数相同,则引用的是同一份字节码  

        int[]a2=new int[3];  

        String[]s5=new String[]{"111","222","333"};  

        String[]s8=new String[4];  

        int[][]a3=new int[3][];  

        System.out.println(a1.getClass()==a2.getClass());  

       // System.out.println(a1.getClass()==a3.getClass());类型不相同,维数不相同的,编译都无法通过  

        System.out.println(Arrays.asList(a1));//由于a1属于object类,而非Object[]类,所以,不能按照jdk1.4标准进行list化,而jkd1.5中的asList()接收的是可变参数,把a1当做 一个参数处理,而int[]数组没有重写toString()方法,故只能调用Object的toString()方法  

        System.out.println(Arrays.asList(s5));//因为在jdk1.4asList(Object[]obj)里面所对应的对象数组,故按照jdk1.4进行数组化  

        System.out.println(Arrays.asList("ddd","ccc","ddc"));  

        printChange(s5);  

        printChange("acb"); 

数组反射的应用  

public static void printChange(Object obj){  

Class cla=obj.getClass();  
if(cla.isArray()){  
            int len=Array.getLength(obj);//得到数组的长度  
            for(int i=0;i<len;i++){  
                System.out.println(Array.get(obj, i));  
            }  
        }  
        else{  
System.out.println(obj);  

}

学入逆水行舟,不进则退——至自己。