Java反射学习:深入学习Java反射机制
Posted 小风微灵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射学习:深入学习Java反射机制相关的知识,希望对你有一定的参考价值。
一、Java反射的理解(反射是研究框架的基础之一)
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
二、逐步分析
参考:https://blog.csdn.net/u012585964/article/details/52011138
1、关于Class
1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。
4、Class 对象只能由系统建立对象
5、一个类在 JVM 中只会有一个Class实例
下面创建一个例子:后面的分析均以此案例为基准。
创建一个空接口:(后面会不断补充)
1 package com.xfwl.reflection; 2 3 public interface IHuman { 4 5 }
创建一个空基类:(后面会不断补充)
1 package com.xfwl.reflection; 2 3 public class Human { 4 5 }
创建一个子类:(后面会不断补充)
1 package com.xfwl.reflection; 2 3 public class Person extends Human implements IHuman { 4 /** 5 * 默认default修饰 6 */ 7 String name; 8 /** 9 * private修饰 10 */ 11 private int age; 12 /** 13 * public修饰 14 */ 15 public char sex=\'M\'; 16 /** 17 * 无参构造 18 */ 19 public Person(){ 20 System.out.println("无参构造!!!"); 21 } 22 /** 23 * 有参构造 24 */ 25 public Person(String name,int age,char sex){ 26 System.out.println("有参构造!!!"); 27 this.name=name; 28 this.age=age; 29 this.sex=sex; 30 } 31 public String getName() { 32 return name; 33 } 34 public void setName(String name) { 35 this.name = name; 36 } 37 public int getAge() { 38 return age; 39 } 40 public void setAge(int age) { 41 this.age = age; 42 } 43 public char getSex() { 44 return sex; 45 } 46 public void setSex(char sex) { 47 this.sex = sex; 48 } 49 public String toString() { 50 return "Person{" + 51 "name=\'" + name + \'\\\'\' + 52 ", age=" + age + 53 ", sex=\'" + sex + \'\\\'\' + 54 \'}\'; 55 } 56 }
2、反射获取类对象的三种方式(通过一个Junit测试来说明)
1 package com.xfwl.reflection; 2 3 import org.junit.Test; 4 /** 5 * 测试类 6 * @function 7 * @author 小风微凉 8 * @time 2018-6-3 下午12:28:38 9 */ 10 public class TestAction { 11 /** 12 * 反射机制获取类有三种方法 13 */ 14 @Test 15 public void testGetClass() throws ClassNotFoundException { 16 Class clazz = null; 17 18 //1 直接通过类名.Class的方式得到 19 clazz = Person.class; 20 System.out.println("通过类名: " + clazz); 21 22 //2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用) 23 Object obj = new Person(); 24 clazz = obj.getClass(); 25 System.out.println("通过getClass(): " + clazz); 26 27 //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常 28 clazz = Class.forName("com.xfwl.reflection.Person"); 29 System.out.println("通过全类名获取: " + clazz); 30 } 31 }
运行结果:
通过类名: class com.xfwl.reflection.Person 无参构造!!! 通过getClass(): class com.xfwl.reflection.Person 通过全类名获取: class com.xfwl.reflection.Person
特别注意:(以下2中方式不会调用构造方法,因为没有实例化操作)
//1 直接通过类名.Class的方式得到 clazz = Person.class; //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常 clazz = Class.forName("com.xfwl.reflection.Person");
3、利用newInstance创建对象:调用的类必须有无参的构造器
1 /** 2 * Class类的newInstance()方法,创建类的一个对象。 3 * @throws ClassNotFoundException 4 * @throws IllegalAccessException 5 * @throws InstantiationException 6 */ 7 @Test 8 public void testNewInstance() 9 throws ClassNotFoundException, IllegalAccessException, InstantiationException { 10 11 Class clazz = Class.forName("com.xfwl.reflection.Person"); 12 13 //使用Class类的newInstance()方法创建类的一个对象 14 //实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的) 15 //一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器 16 Object obj = clazz.newInstance(); 17 System.out.println(obj); 18 }
测试结果:
那么,如果删除Person.java中的无参构造,继续测试,结果如下:
4、ClassLoader类加载器
类加载器详解:
http://blog.csdn.net/ochangwen/article/details/51473120
1 /** 2 * ClassLoader类装载器 3 */ 4 @Test 5 public void testClassLoader1() throws ClassNotFoundException, IOException { 6 //1、获取一个系统的类加载器 7 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 8 System.out.println("系统的类加载器-->" + classLoader); 9 10 //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader)) 11 classLoader = classLoader.getParent(); 12 System.out.println("扩展类加载器-->" + classLoader); 13 14 //3、获取扩展类加载器的父类加载器 15 //输出为Null,无法被Java程序直接引用 16 classLoader = classLoader.getParent(); 17 System.out.println("启动类加载器-->" + classLoader); 18 19 //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器 20 classLoader = Class.forName("com.xfwl.reflection.Person").getClassLoader(); 21 System.out.println("当前类由哪个类加载器进行加载-->"+classLoader); 22 23 //5、测试JDK提供的Object类由哪个类加载器负责加载的 24 //输出为Null,无法被Java程序直接引用 25 classLoader = Class.forName("java.lang.Object").getClassLoader(); 26 System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader); 27 }
测试结果:
系统的类加载器-->sun.misc.Launcher$AppClassLoader@18b4aac2 扩展类加载器-->sun.misc.Launcher$ExtClassLoader@614c5515 启动类加载器-->null 当前类由哪个类加载器进行加载-->sun.misc.Launcher$AppClassLoader@18b4aac2 JDK提供的Object类由哪个类加载器加载-->null
5、反射机制通过加载器获取流对象:getResourceAsStream方法
1 /** 2 * 反射机制通过加载器获取流对象:getResourceAsStream方法 3 * @throws ClassNotFoundException 4 * @throws IOException 5 */ 6 @Test 7 public void testGetResourceAsStream() throws ClassNotFoundException, IOException { 8 //调用getResourceAsStream 获取类路径下的文件对应的输入流 9 /** 10 * 特别说明: 11 * getResourceAsStream("path"),path的路径和new Person()的位置有关 12 */ 13 14 InputStream in = new Person().getClass().getClassLoader() 15 .getResourceAsStream("com/xfwl/reflection/test.properties"); 16 System.out.println("in: " +in); 17 18 Properties properties = new Properties(); 19 properties.load(in); 20 System.out.println("文件内容:"+properties); 21 System.out.println("name: "+properties.getProperty("name")); 22 System.out.println("age: " + properties.getProperty("age")); 23 System.out.println("sex: "+properties.getProperty("sex")); 24 System.out.println("desc: " + properties.getProperty("desc")); 25 }
test.properties文件内容如下:文件编码格式:ISO-8859-1
name=\\u5C0F\\u98CE\\u5FAE\\u51C9\\u0087\\u0089 age=23 sex=M desc=\\u53CD\\u5C04\\u673A\\u5236\\u5B66\\u4E60
运行结果:(直接解析会出现乱码问题,这个可以通过new String(乱码格式处理参数)来处理)
无参构造!!! in: java.io.BufferedInputStream@215be6bb 文件内容:{age=23, name=小风微凉??, sex=M, desc=反射机制学习} name: 小风微凉?? age: 23 sex: M desc: 反射机制学习
6、反射机制获取类中的方法:Method: 对应类中的方法
现在给Person类添加一个private 方法、一个public 方法、一个defaut 方法、一个protected方法
1 /** 2 * Java权限有四个,分别为public,protected,默认,private,其开放程度依次降低 3 * public可供所有类访问 4 * protected继承可见 5 * private只能类本身内部的方法可以访问 6 */ 7 public void method_public(){ 8 System.out.println("method_public"); 9 } 10 public void method_public_2(String name,int age,char sex){//public 带参数 11 System.out.println("method_public_2"); 12 String info="Person{" + 13 "name=\'" + name + \'\\\'\' + 14 ", age=" + age + 15 ", sex=\'" + sex + \'\\\'\' + 16 \'}\'; 17 System.out.println(info); 18 } 19 protected void method_protected(){ 20 System.out.println("method_protected"); 21 } 22 protected void method_protected_2(String info){//protected 带参数 23 System.out.println("method_protected_2:"+info); 24 } 25 void method_default(){ 26 System.out.println("method_default"); 27 } 28 void method_default_2(String info){//默认修饰符 带参数 29 System.out.println("method_default_2:"+info); 30 } 31 private void method_private(){ 32 System.out.println("method_private"); 33 } 34 private void method_private_2(String info){//private 带参数 35 System.out.println("method_private_2:"+info); 36 }
开始测试如何通过反射机制使用这些方法
1 /** 2 * 如何通过反射机制使用这些方法 3 * @throws ClassNotFoundException 4 * @throws NoSuchMethodException 5 * @throws IllegalAccessException 6 * @throws InstantiationException 7 * @throws InvocationTargetException 8 */ 9 @Test 10 public void testMethod() throws ClassNotFoundException, NoSuchMethodException, 11 IllegalAccessException, InstantiationException, InvocationTargetException { 12 Class clazz = Class.forName("com.xfwl.reflection.Person"); 13 14 //1、得到clazz 对应的类中有哪些方法,不能获取private方法 15 Method[] methods =clazz.getMethods(); 16 System.out.println("通过反射机制可以拿到的方法:clazz.getMethods()"); 17 for (Method method : methods){ 18 System.out.println(method.getName()); 19 } 20 System.out.println("<-------------------------->"); 21 22 //2、获取所有的方法(且只获取当着类声明的方法,包括private方法) 23 Method[] methods2 = clazz.getDeclaredMethods(); 24 System.out.println("通过反射机制可以拿到的方法:clazz.getDeclaredMethods()"); 25 for (Method method : methods2){ 26 System.out.println(method.getName()); 27 } 28 System.out.println("<-------------------------->"); 29 System.out.println("通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod()"); 30 //3、获取指定的方法 31 Method method1= clazz.getDeclaredMethod("method_private"); 32 System.out.println("private 无参:"+method1); 33 34 Method method2 = clazz.getDeclaredMethod("method_private_2",String.class);//第一个参数是方法名,后面的是方法里的参数 35 System.out.println("private 有参:"+method2); 36 37 Method method3 = clazz.getDeclaredMethod("method_public_2",String.class,int.class,char.class);//第一个参数是方法名,后面的是方法里的参数 38 System.out.println("public 有参:"+method2); 39 40 //4、执行方法! 41 Object obj = clazz.newInstance(); 42 method3.invoke(obj, "小风微凉", 23,\'M\'); //执行方法:invoke(类对象) 43 }
测试结果:
通过反射机制可以拿到的方法:clazz.getMethods():不能获取private/protected/default方法 toString getName setName method_public_2 setAge method_public getSex getAge setSex wait wait wait equals hashCode getClass notify notifyAll <--------------------------> 通过反射机制可以拿到的方法:clazz.getDeclaredMethods():获取所有修饰权限的方法 toString getName setName method_private_2 method_private method_public_2 setAge method_public method_default method_default_2 getSex getAge setSex method_protected method_protected_2 <--------------------------> 通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod() private 无参:private void com.xfwl.reflection.Person.method_private() private 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String) public 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String) 无参构造!!! method_public_2 Person{name=\'小风微凉\', age=23, sex=\'M\'}
继续分析一下:
JDK中的获取方法
获取方法:默认只能获取public修饰的方法
1 @CallerSensitive 2 public Method[] getMethods() throws SecurityException { 3 checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); 4 return copyMethods(privateGetPublicMethods()); 5 }
获取方法:所有修饰权限的方法都可以获得
@CallerSensitive public Method[] getDeclaredMethods() throws SecurityException { checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); return copyMethods(privateGetDeclaredMethods(false)); }
获取方法:获取指定的方法(所有修饰权限)
1 /** 2 * @jls 8.2 Class Members 3 * @jls 8.4 Method Declarations 4 * @since JDK1.1 5 */ 6 @CallerSensitive 7 public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 8 throws NoSuchMethodException, SecurityException { 9 checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); 10 Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); 11 if (method == null) { 12 throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); 13 } 14 return method; 15 }
分析一下上面这个方法:
String name:方法的名称
Class<?>... parameterTypes:一个或多个方法参数的类型,注意要一一对应,否则会报错的哦
执行方法:invoke(方法对象,方法实际参数)
1 @CallerSensitive 2 public Object invoke(Object obj, Object... args) 3 throws IllegalAccessException, IllegalArgumentException, 4 InvocationTargetException 5 { 6 if (!override) { 7 if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { 8 Class<?> caller = Reflection.getCallerClass(); 9 checkAccess(caller, clazz, obj, modifiers); 10 } 11 } 12 MethodAccessor ma = methodAccessor; // read volatile 13 if (ma == null) { 14 ma = acquireMethodAccessor(); 15 } 16 return ma.invoke(obj, args); 17 }
7、反射机制获取类中的方法:Method: 对应基类或接口中的方法
上面分析了,如何通过反射拿到当前本类里面的各个修饰权限的方法,下面来继续分析一下,如何读取父类或实现的接口中的方法:
现在,给父类添加一些方法,在接口中定义一些方法:
接口中的方法声明:
1 package com.xfwl.reflection; 2 3 public interface IHuman { 4 5 void eat(); 6 void eat(String info); 7 }
Person.java实现接口方法
1 public void eat() { 2 System.out.println("实现接口的方法:eat()无参:"); 3 } 4 public void eat(String info) { 5 System.out.println("实现接口的方法:eat()有参:"+info); 6 }
父类中的方法定义:
1 package com.xfwl.reflection; 2 3 public class Human { 4 public void play_public(){ 5 System.out.println("public无参:play_public"); 6 } 7 public void play_public_2(String info){ 8 System.out.println("public有参:play_public2:"+info); 9 } 10 protected void play_protected(){ 11 System.out.println("protected无参:play_protected"); 12 } 13 protected void play_protected_2(String info){ 14 System.out.println("protected有参:play_protected_2:"+info); 15 } 16 void play_default(){ 17 System.out.println("默认修饰符无参:play_default"); 18 } 19 void play_default_2(String info){//默认修饰符 带参数 20 System.out.println("默认修饰符有参:play_default_2:"+info); 21 } 22 private void play_private(){ 23 System.out.println("private无参:play_private"); 24 } 25 private void play_private_2(String info){ 26 System.out.println("private有参:play_private_2:"+info); 27 } 28 }
开始测试:
1、拿到当前Person类反射对象,能否得到接口中的方法
1 /** 2 * 反射机制获取类中的方法:Method: 对应基类或接口中的方法 3 * @throws ClassNotFoundException 4 * @throws SecurityException 5 * @throws NoSuchMethodException 6 * @throws InstantiationException 7 * @throws InvocationTargetException 8 * @throws IllegalArgumentException 9 * @throws IllegalAccessException 10 */ 11 @Test 12 public void testInterfaceOrSupperClass() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{ 13 Class clazz = Class.forName("com.xfwl.reflection.Person"Java 反射机制