一,能够分析类能力的程序称为“反射”,反射库(java.lang.reflect)提供了精心设计的工具集,以便编写能够动态操作Java代码的程序。
用一句经典的话概括反射:反射就是把java类中的各种成分映射成相应的java类。
二,在程序运行期间,java运行时系统始终为所有的对象维护一个类型标识。这个标识跟踪着每个对象所属的类。虚拟机利用该标识选择相应的方法执行。可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class类。
如同一个Person对象表示一个特定的人一样,一个Class对象表示一个特定类的属性。
三,创建Class类对象有3中形式:
1,已知某对象,调用该对象的getClass()方法将返回一个Class类型的实例。
Person p;2,调用静态方法forName获得类名对应的Class对象。
Class cc = p.getClass();
String ss = "java.util.Date";3,若T是任意的Java类型,T.class将代表匹配的类对象。
Class cc = Class.forName(ss);
Class cc1 = java.util.Date.class;Class对象表示的是一个类型,而这个类型未必是一种类,如整型(int),数组(double[])
Class cc2 = int.class;
Class cc3 = Double[].class
四,最常见的关于Class对象的两个操作
1,getName()方法获得类的名字
Person pp;2,newInstance()方法快速创建一个类的实例
syso(pp.getClass().getName()); //syso是System.out.println()的简写。
Person pp;pp和pp2是2个不同的对象,newInstance()方法调用默认构造器初始化新对象,若没有默认构造器,会抛出异常。
Person pp2 = pp.getClass().newInstance();
五,在java.lang.reflect包中有3个类Field,Method和Constructor分别用于描述类的域(成员变量),方法和构造器。
Class类中的getField(),getMethod(),getConstructor()方法分别返回类提供的公有域,公有方法和公有构造器。
Class类中的getDeclaredField(),getDeclaredMethod(),getDeclaredConstructor()方法分别返回类提供的私有域,私有方法和私有构造器。
下面是一个关于上述这几个类和方法使用的示例。
package com.jimmy.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
//创建一个类
class Son2{
//私有的成员变量
private double i;
//默认构造函数和有参构造函数
public Son2() {}
public Son2(double i) {this.i = i;}
//成员变量的set和get方法
public void setter(double i) {this.i = i;}
public double getter() {return i;}
}
//测试Class类的函数和反射库中的函数
public class test2 {
public static void main(String[] args) throws Exception {
//获得Son2类的Class对象。
Class<?> cc = Son2.class;
//先得到有参构造器的信息,再根据构造器的信息,由newInstance()函数创建一个Son2对象
Constructor<?> constructor = cc.getConstructor(double.class);//要指定构造器参数的类型
Son2 son = (Son2)constructor.newInstance(123);//newInstance()带上参数类型的实例。
System.out.println(son.getter()); //输出被实例化对象son的成员变量的值:123.0。
//先得到无参构造器的信息,再创建Son2对象
Constructor<?> constructor1 = cc.getConstructor();
Son2 son2 = (Son2)constructor1.newInstance();//Son2类中要有默认构造器
System.out.println(son2.getter());//输出被实例化对象son2的成员变量的值:0.0。
//由无参构造器创建对象时,可不必获得构造器,直接由Class对象调用newInstance()方法。
Class<?> cc2 = Son2.class;
Son2 son22 = (Son2)cc2.newInstance();
System.out.println(son22.getter()); //输出被实例化对象son的成员变量的值:0.0
//下面2个输出语句可看出cc保存类信息,输出的是“class + 类名”。cc.newInstance()是具体类的对象。
System.out.println(cc); //输出:class com.jimmy.reflect.Son2
System.out.println(cc.newInstance()); //输出:com.jimmy.reflect.Son2@15db9742(对象的地址)
//首先得到有参构造函数的信息,然后根据构造函数实例化一个对象。
//由getDeclaredField()函数得到类里面的私有成员变量,访问私有成员变量要用setAccessible()函数设置访问权限。
//Field类对象得到成员变量后还可以设置该变量的值,使用set()方法。
Constructor<?> constructor2 = cc.getConstructor(double.class);
Son2 son23 = (Son2)constructor2.newInstance(321);
Field field = cc.getDeclaredField("i");
field.setAccessible(true);
field.set(son23, 213);
System.out.println(field.get(son23)); //field.get(对象)返回获得的对应域的值。
//首先根据获得的构造函数信息实例化一个对象
//然后由函数名获得类中的公有函数,getMethod("函数名")
//invoke()方法执行由getMethod()获得的函数,这里获得的函数是getter()
//对于获得的无参函数,invoke(对象)里只添加对象名。
Constructor<?> constructor3 = cc.getConstructor(double.class);
Son2 son24 = (Son2)constructor3.newInstance(231);
System.out.println(cc.getMethod("getter").invoke(son24)); //输出函数执行返回的结果
//对于获得到的有参函数,在调用getMethod()函数时,要在getMethod()中指定被获得函数的"函数名"和"参数类型"
//并且在执行该函数(即调用invoke()函数时),要指定对象和参数类型的具体实例。
Son2 son25 = (Son2)cc.newInstance();
Method method = cc.getMethod("setter", double.class);
method.invoke(son25, 123);
System.out.println(son25.getter()); //输出函数执行返回的结果。
//反射包里还有一个重要的类:Modifier,该类是静态类,其中的方法也都是静态方法
//Class类中getModifiers()函数返回一个用于描述类,构造器,方法和域的修饰符的整形数值。、
//调用Modifier.toString()方法将整型数值转变成字符串,也是就我们熟悉的public,private,static,final等修饰符。
System.out.println(Modifier.toString(cc.getModifiers()));
System.out.println(Modifier.toString(constructor.getModifiers()));
System.out.println(Modifier.toString(field.getModifiers()));
System.out.println(Modifier.toString(method.getModifiers()));
//同时,Modifier类还有一些判断修饰符是不是某一类型的方法。
System.out.println(Modifier.isPublic(cc.getModifiers()));
System.out.println(Modifier.isPublic(constructor.getModifiers()));
}
}
六,应用反射调用其他类的main()方法
首先有一个类,main()方法输出信息:
package com.jimmy.reflect;
public class testPrint {
public static void main(String[] args) {
for(String ss:args)
System.out.println(ss);
}
}
在其他类中反射调用上面类的main()方法。
package com.jimmy.reflect;
import java.lang.reflect.Method;
public class getMain {
public static void main(String[] args) throws Exception {
//传统方法
testPrint.main(new String[]{"haha","xixi"});
//反射调用
Class<?> cc = Class.forName("com.jimmy.reflect.testPrint");
Method mm = cc.getMethod("main", String[].class);
mm.invoke(null, (Object)new String[]{"hello","jimmy"});//实例化字符串数组对象时要在前面加(Object),不然会报参数个数不匹配的错误。
}
}
七,数组的反射
package com.jimmy.reflect;
import java.lang.reflect.Array;
public class test3 {
public static void main(String[] args) throws Exception {
//新建4个不同的数组
int[] a = new int[3];
int[] b = new int[4];
int[][] c = new int[3][4];
String[] d = new String[3];
//可以对比各个数组所属的类是否相同
System.out.println(a.getClass() == b.getClass());
//System.out.println(a.getClass() == c.getClass()); //不同的类不能比较是否相等,会报错
//System.out.println(a.getClass() == d.getClass());
System.out.println(a.getClass().getName());
System.out.println(d.getClass().getName());
System.out.println(a.getClass().getSuperclass().getName());
//利用静态方法反射生成一个数组
int[] aa = (int[]) Array.newInstance(int.class, 3);
Array.set(aa, 0, 123);//set()静态方法赋值
Array.set(aa, 1, 321);
Array.set(aa, 2, 213);
System.out.println(Array.get(aa, 0));//get()静态方法取值
System.out.println(Array.get(aa, 1));
System.out.println(Array.get(aa, 2));
//获得数组类型的常用方式
Class<?> cc = String[].class;
Class<?> cc1 = Class.forName("[I");
Class<?> cc2 = Class.forName("[Ljava.lang.String;");
System.out.println(cc);
System.out.println(cc1);
System.out.println(cc2);
//通过已有对象获取数组类型
Class<?> cc3 = aa.getClass();
System.out.println(cc3);
Class<?> cc4 = cc3.getComponentType();//根据类型信息获取数组内成员的类型
System.out.println(cc4);
}
}