java反射机制
1 反射的基本概念
1.1 什么是反射
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射的作用:
-
在运行时判断任意一个对象所属的类
-
在运行时构造任意一个类的对象
-
在运行时判断任意一个类所具有的成员变量和方法
-
在运行时调用任意一个对象的方法
-
生成动态代理(proxy)
1.2 反射在实际开发中的应用
-
开发IDE(集成开发环境)
-
以上的IDE内部都大量使用了反射机制,我们在使用这些IDE写代码也无时无刻的使用着反射机制,一个常用反射机制的地方就是当我们通过对象调用方法或访问属性时,开发工具都会以列表的形式显示出该对象所有的方法或属性,以供方便我们选择使用,如下图:
-
这些开发工具之所有能够把该对象的方法和属性展示出来就使用利用了反射机制对该对象所有类进行了解剖获取到了类中的所有方法和属性信息,这是反射在IDE中的一个使用场景。
-
各种框架的设计
-
以上三个图标上面的名字就是Java的三大框架,简称SSH.
-
这三大框架的内部实现也大量使用到了反射机制,所有要想学好这些框架,则必须要求对反射机制熟练了。
1.3 使用反射机制解剖类的前提
必须先要获取到该类的字节码文件对象,即Class类型对象。关于Class描述字节码文件如下图所示:
说明:
1)Java中使用Class类表示某个class文件.
2)任何一个class文件都是Class这个类的一个实例对象.
2. 获取Class对象的三种方式
-
创建测试类:Student
public class Student { static { System.out.println("静态代码块"); } { System.out.println("构造代码块"); } }
2.1 方式1:通过类名.class获取
1 public class Demo01 { 2 public static void main(String[] args) { 3 // 获得Student的Class的对象 4 Class c = Student.class; 5 // 打印输出:class com.itheima.reflect.Student 6 System.out.println(c); 7 } 8 }
2.2 方式2:通过Object类的getClass()方法获取
1 public class Demo01 { 2 public static void main(String[] args) { 3 // 创建学生对象 4 Student stu = new Student(); 5 // 获得学生类的Class对象 6 Class c = stu.getClass(); 7 // 打印输出:class com.itheima.reflect.Student 8 System.out.println(c); 9 } 10 }
2.3 方式3:通过Class.forName("全限定类名")方法获取
1 public class Demo01 { 2 public static void main(String[] args) throws Exception { 3 // 获得字符串的Class对象 4 Class c = Class.forName("java.lang.String"); 5 // 打印输出:class java.lang.String 6 System.out.println(c); 7 } 8 }
3 获取Class对象的信息
知道怎么获取Class对象之后,接下来就介绍几个Class类中常用的方法了。
3.1 Class对象相关方法
-
String getSimpleName(); 获得简单类名,只是类名,没有包
String getName(); 获取完整类名,包含包名+类名
T newInstance() ;创建此 Class 对象所表示的类的一个新实例。要求:类必须有public的无参数构造方法
3.2 方法演示
1 public class Demo02 { 2 public static void main(String[] args) throws Exception { 3 // 获得字符串的Class对象 4 Class c = Class.forName("java.lang.String"); 5 // 获得简单类名 6 String simplename = c.getSimpleName(); 7 // 打印输入:simplename = String 8 System.out.println("simplename = " + simplename); 9 // 获得完整类名(包含包名和类名) 10 String name = c.getName(); 11 // 打印输入:name = java.lang.String 12 System.out.println("name = " + name); 13 // 创建字符串对象 14 String str = (String) c.newInstance(); 15 // 输出str:空字符串 "" 16 System.out.println(str); 17 } 18 }
4 获取Class对象的Constructor信息
一开始在阐述反射概念的时候,我们说到利用反射可以在程序运行过程中对类进行解剖并操作里面的成员。而一般常用来操作成员的构造方法,成员方法,成员变量等等,那么接下来就来看看怎么利用反射来操作这些成员以及操作这些成员能干什么,先来看看怎么操作构造方法。而要通过反射操作类的构造方法,我们需要先知道一个Constructor类。
4.1 Constructor类概述
Constructor是构造方法类,类中的每一个构造方法都是Constructor的对象,通过Constructor对象可以实例化对象。
4.2 Class类中与Constructor相关方法
1. Constructor getConstructor(Class... parameterTypes)
根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
2. Constructor getDeclaredConstructor(Class... parameterTypes)
根据参数类型获取构造方法对象,包括private修饰的构造方法。
如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
3. Constructor[] getConstructors()
获取所有的public修饰的构造方法
4. Constructor[] getDeclaredConstructors()
获取所有构造方法,包括privat修饰的
4.3 Constructor类中常用方法
1. T newInstance(Object... initargs)
根据指定参数创建对象。
2. void setAccessible(true)
暴力反射,设置为可以直接访问私有类型的构造方法。
4.4 示例代码
-
学生类
1 /** 2 * 3 * @version 1.0 4 * @description com.itheima 5 * @date 2018/1/25 6 */ 7 public class Student { 8 // 姓名 9 private String name; 10 // 性别 11 public String gender; 12 // 年龄 13 private int age; 14 15 16 // public 有参构造方法 17 public Student(String name, String gender, int age) { 18 System.out.println("public 修饰有参数构造方法"); 19 this.name = name; 20 this.gender = gender; 21 this.age = age; 22 } 23 24 // public 无参构造方法 25 public Student() { 26 System.out.println("public 修饰无参数构造方法"); 27 } 28 29 // private 有参构造方法 30 private Student(String name,String gender){ 31 System.out.println("private 修饰构造方法"); 32 this.name = name; 33 this.gender = gender; 34 } 35 36 // getter & setter 方法 37 /* 38 * 此处为 getter & setter方法 省略... 39 */ 40 41 // 普通方法 42 public void sleep(){ 43 System.out.println("睡觉"); 44 } 45 46 public void sleep(int hour){ 47 System.out.println("public修饰---sleep---睡" + hour + "小时"); 48 } 49 50 private void eat(){ 51 System.out.println("private修饰---eat方法---吃饭"); 52 } 53 54 // 静态方法 55 public static void study(){ 56 System.out.println("静态方法---study方法---好好学习Java"); 57 } 58 59 @Override 60 public String toString() { 61 return "Student{" + 62 "name='" + name + '\'' + 63 ", gender='" + gender + '\'' + 64 ", age=" + age + 65 '}'; 66 } 67 } 68 69 测试类 70 71 /** 72 * 73 * @version 1.0 74 * @description 获取Class对象的Constructor信息 75 * @date 2018/1/26 76 */ 77 public class Demo03 { 78 public static void main(String[] args)throws Exception{ 79 test01(); 80 test02(); 81 test03(); 82 test04(); 83 } 84 85 /** 86 4. Constructor[] getDeclaredConstructors() 87 获取所有构造方法,包括privat修饰的 88 */ 89 public static void test04() throws Exception{ 90 System.out.println("----------- test04() -----------"); 91 // 获取Student类的Class对象 92 Class clazz = Student.class; 93 // 获取所有的public修饰的构造方法和私有的构造方法 94 Constructor[] cons = clazz.getDeclaredConstructors(); 95 // 遍历构造方法数组 96 for(Constructor con:cons) { 97 // 输出con 98 System.out.println(con); 99 } 100 } 101 102 /** 103 3. Constructor[] getConstructors() 104 获取所有的public修饰的构造方法 105 */ 106 public static void test03() throws Exception{ 107 System.out.println("----------- test03() -----------"); 108 // 获取Student类的Class对象 109 Class c = Student.class; 110 // 获取所有的public修饰的构造方法 111 Constructor[] cons = c.getConstructors(); 112 // 遍历构造方法数组 113 for(Constructor con:cons) { 114 // 输出con 115 System.out.println(con); 116 } 117 } 118 119 /** 120 2. Constructor getDeclaredConstructor(Class... parameterTypes) 121 根据参数类型获取构造方法对象,包括private修饰的构造方法。 122 如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。 123 */ 124 public static void test02() throws Exception{ 125 System.out.println("----------- test02() -----------"); 126 // 获取Student类的Class对象 127 Class c = Student.class; 128 // 根据参数获取对应的private修饰构造方法对象 129 Constructor cons = c.getDeclaredConstructor(String.class,String.class); 130 // 注意:private的构造方法不能直接调用newInstance创建对象,需要暴力反射才可以 131 // 设置取消权限检查(暴力反射)Accessible(可使用的,可到达的) 132 cons.setAccessible(true); 133 // 调用Constructor方法创建学生对象 134 Student stu = (Student) cons.newInstance("林青霞","女"); 135 // 输出stu 136 System.out.println(stu); 137 } 138 /** 139 1. Constructor getConstructor(Class... parameterTypes) 140 根据参数类型获取构造方法对象,只能获得public修饰的构造方法。 141 如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。 142 */ 143 public static void test01() throws Exception{ 144 System.out.println("----------- test01() -----------"); 145 // 获取Student类的Class对象 146 Class c = Student.class; 147 // 根据参数获取对应的构造方法对象 148 Constructor cons = c.getConstructor(String.class,String.class,int.class); 149 // 调用Constructor方法创建学生对象 150 Student stu = (Student) cons.newInstance("张曼玉","女",28); 151 // 输出stu 152 System.out.println(stu); 153 } 154 }
5 获取Class对象的Method信息
操作完构造方法之后,就来看看反射怎么操作成员方法了。同样的在操作成员方法之前我们需要学习一个类:Method类。
5.1 Method类概述
Method是方法类,类中的每一个方法都是Method的对象,通过Method对象可以调用方法。
5.2 Class类中与Method相关方法
1. Method getMethod("方法名", 方法的参数类型... 类型)
根据方法名和参数类型获得一个方法对象,只能是获取public修饰的
2. Method getDeclaredMethod("方法名", 方法的参数类型... 类型)
根据方法名和参数类型获得一个方法对象,包括private修饰的
3. Method[] getMethods() (了解)
获取所有的public修饰的成员方法,包括父类中。
4. Method[] getDeclaredMethods() (了解)
获取当前类中所有的方法,包含私有的,不包括父类中。
5.3 Method类中常用方法
1. Object invoke(Object obj, Object... args)
根据参数args调用对象obj的该成员方法
如果obj=null,则表示该方法是静态方法
2. void setAccessible(boolean flag)
暴力反射,设置为可以直接调用私有修饰的成员方法
5.4 示例代码
1 /** 2 * 3 * @version 1.0 4 * @description 获取Class对象的Method信息 5 * @date 2018/1/26 6 */ 7 public class Demo04 { 8 public static void main(String[] args)throws Exception{ 9 // 获得Class对象 10 Class c = Student.class; 11 // 快速创建一个学生对象 12 Student stu = (Student ) c.newInstance(); 13 14 // 获得public修饰的方法对象 15 Method m1 = c.getMethod("sleep",int.class); 16 // 调用方法m1 17 m1.invoke(stu,8); 18 19 // 获得private修饰的方法对象 20 Method m2 = c.getDeclaredMethod("eat"); 21 // 注意:private的成员方法不能直接调用,需要暴力反射才可以 22 // 设置取消权限检查(暴力反射) 23 m2.setAccessible(true); 24 // 调用方法m2 25 m2.invoke(stu); 26 27 // 获得静态方法对象 28 Method m3 = c.getDeclaredMethod("study"); 29 // 调用方法m3 30 // 注意:调用静态方法时,obj可以为null 31 m3.invoke(null); 32 33 System.out.println("--------获得所有public的方法,不包括private,包括父类的----------"); 34 // 获得所有public的方法,包括父类的 35 Method[] ms = c.getMethods(); 36 // 遍历方法数组 37 for(Method m : ms) { 38 System.out.println(m); 39 } 40 41 System.out.println("--------获得所有方法,包括private,不包括父类---------------"); 42 // 获得所有方法,包括private,不包括父 43 Method[] ms2 = c.getDeclaredMethods(); 44 // 遍历方法数组 45 for(Method m : ms2) { 46 System.out.println(m); 47 } 48 } 49 } 50 输出结果:
2.6 获取Class对象的Field信息(了解)
2.6.1 Field类概述
Field是属性类,类中的每一个属性(成员变量)都是Field的对象,通过Field对象可以给对应的成员变量赋值和取值。
2.6.2 Class类中与Field相关方法
1. Field getDeclaredField(String name)
根据属性名获得属性对象,包括private修饰的
2. Field getField(String name)
根据属性名获得属性对象,只能获取public修饰的
3. Field[] getFields()
获取所有的public修饰的属性对象,返回数组。
4. Field[] getDeclaredFields()
获取所有的属性对象,包括private修饰的,返回数组。
2.6.3 Field类中常用方法
void set(Object obj, Object value)
void setInt(Object obj, int i)
void setLong(Object obj, long l)
void setBoolean(Object obj, boolean z)
void setDouble(Object obj, double d)
Object get(Object obj)
int getInt(Object obj)
long getLong(Object obj)
boolean getBoolean(Object ob)
double getDouble(Object obj)
void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
Class getType(); 获取属性的类型,返回Class对象。
-
setXxx方法都是给对象obj的属性设置使用,针对不同的类型选取不同的方法。
-
getXxx方法是获取对象obj对应的属性值的,针对不同的类型选取不同的方法。
2.6.4 示例代码
1 /** 2 * 3 * @version 1.0 4 * @description 获取Class对象的Field信息 5 * @date 2018/1/26 6 */ 7 public class Demo05 { 8 public static void main(String[] args)throws Exception{ 9 // 获得Class对象 10 Class c = Student.class; 11 // 快速创建一个学生对象 12 Student stu = (Student ) c.newInstance(); 13 14 // 获得public修饰Field对象 15 Field f1 = c.getField("gender"); 16 // 通过f1对象给对象stu的gender属性赋值 17 f1.set(stu,"风清扬"); 18 // 通过f1对象获取对象stu的gender属性值 19 String gender = (String) f1.get(stu); 20 System.out.println("性别:" + gender); 21 22 // 获得private修饰Field对象 23 Field f2 = c.getDeclaredField("age"); 24 // 注意:private的属性不能直接访问,需要暴力反射才可以 25 // 设置取消权限检查(暴力反射) 26 f2.setAccessible(true); 27 // 通过f1对象给对象stu的age属性赋值 28 f2.setInt(stu,30); 29 // 通过f2对象获取对象stu的age属性值 30 int age = f2.getInt(stu); 31 System.out.println("年龄:" + age); 32 33 34 System.out.println("-------获得所有public修饰的属性--------"); 35 // 获得所有public修饰的属性 36 Field[] fs1 = c.getFields(); 37 // 遍历数组 38 for(Field f : fs1) { 39 System.out.println(f); 40 } 41 42 System.out.println("-------获得所有的属性,包括private修饰--------"); 43 // 获得所有的属性,包括private修饰 44 Field[] fs2 = c.getDeclaredFields(); 45 // 遍历数组 46 for(Field f : fs2) { 47 System.out.println(f); 48 } 49 } 50 }
-
输出结果
7 小结
反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的方法,属性,构造方法等成员。
我们使用反射的时候 , 都需要先获取类的字节码对象 ,最常用的方式就是 Class.forName("类的全限定名") .
我们可以通过字节码对象就可以获取类的构造器 , 从而创建对象 ,最常用的方式是通过字节码对象获取无参构造器 getConstructor, 就可以调用构造器对象的newInstance() 方法创建对象 , 当然这种方式简化为:字节码对象直接调用newInstance() 方法创建对象 .
也可以通过字节码对象获取指定的方法 , 然后调用方法对象的invoke(Object obj, Object... args)来执行此方法.
也可以通过字节码对象获取类中的所有字段 , 从而给字段赋值和获取字段的值 .
有了以上的基础 , 我们就可以通过一个指定全限定名 , 就可以创建该类对象的实例对象 , 执行里面的方法 , 给其字段进行赋值或者获取值.

浙公网安备 33010602011771号