今天重新学习了java中的反射,写一篇学习笔记总结一下。代码基本都是照着两篇博客敲的:
其中,第一篇比较详细点。作为初学者,我觉得不能根据晦涩的概念强行去理解一个知识点,而应该结合具体的应用实例。
先初步理解一下反射的作用:
(1)反编译,将 .class文件转化为 .java文件
(2)通过class对象来访问其对应类的对象的属性、方法、构造方法。
1、Class类
先来理解一下Class这个特殊的的类,在平时敲代码的时候,偶尔会写到,this.getClass( )这样的方法,但是却不知道是什么意思。查一下API可知,Class类也同样继承了Object类,Class类的实例用来表示正在运行的java应用程序中的类和接口,也就是说jvm中已经存在对象的类、已经被加载进去的类,都会有对应的Class实例(这也包括基本的数据结构),详细图解见第一篇推荐博客。
注意点:
(1)Class类保存的是对应类的信息,比如现在有一个Student类,现在通过new 方法 产生了一个stu1的实例,那么jvm会把Stduent类加载进内存,通过类加载器中等defineClass方法构造 一个对应的Class对象,来保存Student的一些信息,包括其中的一些方法、属性、构造方法等。
(2)同一种类型的对象或者数据类型,共享一个class实例。比如说,Student方法继续产生stu2、stu3.。。。等等的对象,即使再多,也只有一个对应的Class实例来保存他们的信息。
(3)通过Class实例,可以反向地获取对应类的信息,如果指定对象(例如 stu1), 可以反向获得他的属性、方法等信息。
2、反射的使用
下面举的例子都是来自上面推荐的第一篇博客,我自己也敲了一遍,学习一下。
(1)获取Class对象的方法
1 packagefanshe;2 /**
3 *4 * 获取class对象的三种方式5 * 1、object ——> getClass();6 * 2、任何数据类型(包括基本数据类型)都有一个静态的class属性。7 * 3、通过class类的静态方法:forName(String className)(常用)8 *9 */
10 public classDemo_1 {11
12 public static voidmain(String[] args) {13 //第一种获得class对象的方法
14 Student stu1 = new Student();//这一个new产生一个Student对象,一个Class对象
15 Class stuClass = stu1.getClass();//获取class对象
16 System.out.println(stuClass.getName());17
18 //第二种方式获取class对象
19 Class stuClass2 = Student.class;20 System.out.println(stuClass ==stuClass2);21
22 //第三种方式获取class对象
23 try{24 Class stuClass3 = Class.forName("fanshe.Student");//带包名
25 System.out.println(stuClass3 ==stuClass2);26 } catch(ClassNotFoundException e) {27 //TODO Auto-generated catch block
28 e.printStackTrace();29 }30
31 }32 }
Student:
classStudent{
Student(){}
}
结果:
fanshe.Studenttrue
true
总结:第三种方法比较常见,不用导包,也不用知道对象。上面的测试也说明了,同一个对象共享一个Class实例,一个类只产生一个class实例。
(2)通过反射获取构造函数
Student类:
packagefanshe;public classStudent {//默认构造方法
Student(String str){
System.out.println("(默认)的默认构造方法 s = " +str);
}//无参构造方法
publicStudent() {
System.out.println("调用了共有的、无参构造方法执行");
}//有一个参数的构造方法
public Student(charname) {
System.out.println("姓名: " +name);
}//有多个参数的构造方法
public Student(String name, intage) {
System.out.println("姓名: " + name+ "年龄:" +age);//这的执行效率有问题
}//受保护的构造方法
protected Student(booleann) {
System.out.println("受保护的构造方法 n= " +n);
}//私有构造方法
private Student(intage) {
System.out.println("私有构造方法 年龄:" +age);
}
}
测试类:
1 packagefanshe;2 importjava.lang.reflect.Constructor;3 /**
4 *5 * 通过Class对象可以获取某个类中的构造方法、成员变量、成员方法:并访问成员;6 * 1、获取构造方法:7 * (1)批量的方法8 * public Constructor[] getConstructor(); 所有“公有的”构造方法9 * public Constructor[] getDeclaredConstructors(); 获取所有的构造方法10 *11 * (2)获取单个的方法,并调用:12 * public Constructor getConstructor(Class parameterTypes):获取单个公有的构造方法13 * public Constructor getDeclaredConstructor(Class parameterTypes);获取某个构造方法可以是私有的,或者是受保护的14 * 、默认的。公有的:15 * 调用构造方法16 * Construtor -->newInstance(Object、、、initargs)17 *18 *19 */
20 public classTest_1 {21
22 public static void main(String[] args)throwsException{23 //1、加载class对象
24 Class clazz = Class.forName("fanshe.Student");25
26 //2、获取所有公有构造方法
27 System.out.println("*****************************所有公有构造方法*************");28 Constructor[] conArray =clazz.getConstructors();29 for(Constructor c : conArray) {30 System.out.println(c);31 }32
33 System.out.println("********所有的构造方法(包括私有、受保护、默认、公有)********");34 conArray =clazz.getDeclaredConstructors();35 for(Constructor c : conArray) {36 System.out.println(c);37 }38
39 System.out.println("********获取公有的 、无参的构造方法");40 Constructor con = clazz.getConstructor(null);41 //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型42 //2>、返回的是描述这个无参构造函数的类对象。
43
44 System.out.println("con = "+con);45
46 //调用构造方法
47 Object obj =con.newInstance();48
49
50 System.out.println("******************获取私有构造方法,并调用*******************************");51 con = clazz.getDeclaredConstructor(char.class);52 System.out.println(con);53
54 //调用构造函数
55 con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
56 obj = con.newInstance('男');57 }58 }
结果:
*****************************所有公有构造方法*************
public fanshe.Student(java.lang.String,int)public fanshe.Student(char)publicfanshe.Student()********所有的构造方法(包括私有、受保护、默认、公有)********
private fanshe.Student(int)protected fanshe.Student(boolean)public fanshe.Student(java.lang.String,int)public fanshe.Student(char)publicfanshe.Student()
fanshe.Student(java.lang.String)********获取公有的 、无参的构造方法
con= publicfanshe.Student()
调用了共有的、无参构造方法执行******************获取私有构造方法,并调用*******************************
public fanshe.Student(char)
姓名: 男
(3)获取成员变量并调用
Stduent类:
packagefanshe;public classStudent_1 {publicStudent_1() {
}//******字段******// publicString name;protected intage;charsex;privateString phoneNum;publicString toString() {return "Student [name="+name+",age="+age+",sex="+sex+", phoneNum="+phoneNum+"]";
}
}
测试类:
1 /**
2 *3 * 获取成员变量并调用:4 *5 * 1.批量的6 * 1).Field[] getFields():获取所有的"公有字段"7 * 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;8 * 2.获取单个的:9 * 1).public Field getField(String fieldName):获取某个"公有的"字段;10 * 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)11 *12 * 设置字段的值:13 * Field --> public void set(Object obj,Object value):14 * 参数说明:15 * 1.obj:要设置的字段所在的对象;16 * 2.value:要为字段设置的值;17 */
18 public classTest_2 {19
20 public static void main(String[] args) throwsException {21 //1.获取Class对象
22 Class stuClass = Class.forName("fanshe.Student_1");23 //2.获取字段
24 System.out.println("************获取所有公有的字段********************");25 Field[] fieldArray =stuClass.getFields();26 for(Field f : fieldArray){27 System.out.println(f);28 }29 System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");30 fieldArray =stuClass.getDeclaredFields();31 for(Field f : fieldArray){32 System.out.println(f);33 }34 System.out.println("*************获取公有字段**并调用***********************************");35 Field f = stuClass.getField("name");36 System.out.println(f);37 //获取一个对象
38 Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();39 //为字段设置值
40 f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"41 //验证
42 Student_1 stu =(Student_1)obj;43 System.out.println("验证姓名:" +stu.name);44
45
46 System.out.println("**************获取私有字段****并调用********************************");47 f = stuClass.getDeclaredField("phoneNum");48 System.out.println(f);49 f.setAccessible(true);//暴力反射,解除私有限定
50 f.set(obj, "18888889999");51 System.out.println("验证电话:" +stu);52 }53 }
结果:
************获取所有公有的字段********************
publicjava.lang.String fanshe.Student_1.name************获取所有的字段(包括私有、受保护、默认的)********************
publicjava.lang.String fanshe.Student_1.nameprotected intfanshe.Student_1.agecharfanshe.Student_1.sexprivatejava.lang.String fanshe.Student_1.phoneNum*************获取公有字段**并调用***********************************
publicjava.lang.String fanshe.Student_1.name
验证姓名:刘德华**************获取私有字段****并调用********************************
privatejava.lang.String fanshe.Student_1.phoneNum
验证电话:Student [name=刘德华,age=0,sex=
(4)获取成员方法并调用
Stduent类
1 packagefanshe;2
3 public classStudent_3 {4
5 //**************成员方法***************//
6 public voidshow1(String s){7 System.out.println("调用了:公有的,String参数的show1(): s = " +s);8 }9 protected voidshow2(){10 System.out.println("调用了:受保护的,无参的show2()");11 }12 voidshow3(){13 System.out.println("调用了:默认的,无参的show3()");14 }15 private String show4(intage){16 System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " +age);17 return "abcd";18 }19 }
测试类:
1 packagefanshe;2 importjava.lang.reflect.Method;3
4 /**
5 * 获取成员方法并调用:6 *7 * 1.批量的:8 * public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)9 * public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)10 * 2.获取单个的:11 * public Method getMethod(String name,Class>... parameterTypes):12 * 参数:13 * name : 方法名;14 * Class ... : 形参的Class类型对象15 * public Method getDeclaredMethod(String name,Class>... parameterTypes)16 *17 * 调用方法:18 * Method --> public Object invoke(Object obj,Object... args):19 * 参数说明:20 * obj : 要调用方法的对象;21 * args:调用方式时所传递的实参;22 *23 */
24
25 public classTest_3 {26
27 public static void main(String[]args) throwsException{28 //1.获取Class对象
29 Class stuClass = Class.forName("fanshe.Student_3");30 //2.获取所有公有方法
31 System.out.println("***************获取所有的”公有“方法*******************");32 stuClass.getMethods();33 Method[] methodArray =stuClass.getMethods();34 for(Method m : methodArray){35 System.out.println(m);36 }37 System.out.println("***************获取所有的方法,包括私有的*******************");38 methodArray =stuClass.getDeclaredMethods();39 for(Method m : methodArray){40 System.out.println(m);41 }42 System.out.println("***************获取公有的show1()方法*******************");43 Method m = stuClass.getMethod("show1", String.class);44 System.out.println(m);45 //实例化一个Student对象
46 Object obj =stuClass.getConstructor().newInstance();47 m.invoke(obj, "刘德华");48
49 System.out.println("***************获取私有的show4()方法******************");50 m = stuClass.getDeclaredMethod("show4", int.class);51 System.out.println(m);52 m.setAccessible(true);//解除私有限定
53 Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
54 System.out.println("返回值:" +result);55 }56 }
结果:
***************获取所有的”公有“方法*******************
public voidfanshe.Student_3.show1(java.lang.String)public final void java.lang.Object.wait() throwsjava.lang.InterruptedExceptionpublic final void java.lang.Object.wait(long,int) throwsjava.lang.InterruptedExceptionpublic final native void java.lang.Object.wait(long) throwsjava.lang.InterruptedExceptionpublic booleanjava.lang.Object.equals(java.lang.Object)publicjava.lang.String java.lang.Object.toString()public native intjava.lang.Object.hashCode()public final nativejava.lang.Class java.lang.Object.getClass()public final native voidjava.lang.Object.notify()public final native voidjava.lang.Object.notifyAll()***************获取所有的方法,包括私有的*******************
protected voidfanshe.Student_3.show2()private java.lang.String fanshe.Student_3.show4(int)voidfanshe.Student_3.show3()public voidfanshe.Student_3.show1(java.lang.String)***************获取公有的show1()方法*******************
public voidfanshe.Student_3.show1(java.lang.String)
调用了:公有的,String参数的show1(): s=刘德华***************获取私有的show4()方法******************
private java.lang.String fanshe.Student_3.show4(int)
调用了,私有的,并且有返回值的,int参数的show4(): age= 20返回值:abcd
(5)反射main方法
student类:
packagefanshe;public classStudent_4 {public static voidmain(String[]args) {
System.out.println("main 方法执行了。。。");
}
}
测试类:
1 packagefanshe;2 importjava.lang.reflect.Method;3 /**
4 * 获取Student类的main方法、不要与当前的main方法搞混了5 */
6 public classTest_4 {7
8 public static voidmain(String[]args) {9 try{10 //1、获取Student对象的字节码
11 Class clazz = Class.forName("fanshe.Student_4");12
13 //2、获取main方法
14 Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,15 //3、调用main方法16 //methodMain.invoke(null, new String[]{"a","b","c"});17 //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数18 //这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
19 methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一20 //methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
21
22 } catch(Exception e) {23 e.printStackTrace();24 }25 }26 }
结果:
main 方法执行了。。。
(6)通过反射运行配置文件
student类:
1 public classStudent {2 public voidshow(){3 System.out.println("is show()");4 }5 }
配置文件,以txt为例(pro.txt)
1 className =cn.fanshe.Student2 methodName = show
测试类:
importjava.io.FileNotFoundException;importjava.io.FileReader;importjava.io.IOException;importjava.lang.reflect.Method;importjava.util.Properties;/** 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
* 我们只需要将新类发送给客户端,并修改配置文件即可*/
public classDemo {public static void main(String[] args) throwsException {//通过反射获取Class对象
Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"//2获取show()方法
Method m = stuClass.getMethod(getValue("methodName"));//show//3.调用show()方法
m.invoke(stuClass.getConstructor().newInstance());
}//此方法接收一个key,在配置文件中获取相应的value
public static String getValue(String key) throwsIOException{
Properties pro= new Properties();//获取配置文件的对象
FileReader in = new FileReader("pro.txt");//获取输入流
pro.load(in);//将流加载到配置文件对象中
in.close();return pro.getProperty(key);//返回根据key获取的value值
}
}
结果:
is show()
需求:更新系统时,只要再写一个Student2类,更改一下配置文件,而不需要动原来的类。
Student2类:
1 public classStudent2 {2 public voidshow2(){3 System.out.println("is show2()");4 }5 }
配置文件:
className =cn.fanshe.Student2
methodName= show2
(7)通过反射越过泛型检查
测试类:
1 importjava.lang.reflect.Method;2 importjava.util.ArrayList;3
4 /*
5 * 通过反射越过泛型检查6 *7 * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?8 */
9 public classDemo {10 public static void main(String[] args) throwsException{11 ArrayList strList = new ArrayList<>();12 strList.add("aaa");13 strList.add("bbb");14
15 //strList.add(100);16 //获取ArrayList的Class对象,反向的调用add()方法,添加数据
17 Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象18 //获取add()方法
19 Method m = listClass.getMethod("add", Object.class);20 //调用add()方法
21 m.invoke(strList, 100);22
23 //遍历集合
24 for(Object obj : strList){25 System.out.println(obj);26 }27 }28 }
输出:
aaa
bbb100
其清晰的表述,具体的案例,让我进一步了解了反射机制的一些应用,等以后学习框架或者工作的时候,再根据具体情况深入地学习反射的原理。通过反射机制的初步学习,对面向对象这个概念也有了更进一步的体会,原来一个类中的方法,属性,构造方法,也可以抽象并封装为一个类(Field,Method,Constructor),果然面向对象,一切皆对象。
如有错误,欢迎批评指正。