一、什么是反射?反射的作用(红色字)?
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以判断任意一个对象所属的类,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
二、反射的优缺点?
优点:
反射是一种强有力的工具,是面向抽象编程一种实现方式,它能使代码语句更加灵活,极大提高代码的运行时装配能力。
缺点:
1、性能问题。
Java反射机制中包含了一些动态类型,但是Java虚拟机不能够对这些动态代码进行优化。因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。
2、安全限制。
使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。
3、程序健壮性。
反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性。反射原理?
Java在将.class字节码文件读入内存时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制。
三、反射常用方法解析
反射机制所需的类主要有java.lang包中的Class类和java.lang.reflet包中的Constructor类、Field类、Method类和Parameter类。Class类是一个比较特殊的类,它是反射机制的基础,Class类的对象表示正在运行的Java程序中的类或接口,也就是任何一个类被加载时,即将类的.class文件(字节码文件)读入内存的同时,都自动为之创建一个java.lang.Class对象。Class类没有公共构造方法,其对象是JVM在加载类时通过调用类加载器中的defineClass()方法创建的,因此不能显式地创建一个Class对象。通过这个Class对象,才可以获得该对象的其他信息。
在Java中程序获得Class对象有如下3种方式:
(1)使用Class类的静态方法forName(String className),其中参数className表示所需类的全名。forName(String className)方法声明抛出ClassNotFoundException异常,因此调用该方法时必须捕获或抛出该异常。
(2) 用类名调用该类的class属性来获得该类对应的Class对象,即“类名.class”。
(3)用对象调用getClass()方法来获得该类对应的Class对象,即“对象.getClass()”。该方法是Object类中的一个方法,因此所有对象调用该方法都可以返回所属类对应的Class对象。
补充说明:通过类的class属性获得该类所对应的Class对象,会使代码更安全,程序性能更好,因此大部分情况下建议使用第二种方式。但如果只获得一个字符串,例如获得String类对应的Class对象,则不能使用String.class方式,而是使用Class.forName("java.lang.String")。注意:如果要想获得基本数据类型的Class对象,可以使用对应的打包类加上.TYPE,例如,Integer.TYPE可获得int的Class对象,但要获得Integer.class的Class对象,则必须使用Integer.class。
获得class对象后,我们就可以通过class对象获得实际对象
//创建对象的实例,这里需要一个无参的构造函数
Demo obj=(Demo)demo1.newInstance();
常见方法介绍:
获得构造函数
Constructor getConstructor(Class[] params) //根据指定参数获得public构造器
Constructor[] getConstructors() //Demo4 获得public的所有构造器
Constructor getDeclaredConstructor(Class[] params) //根据指定参数获得public和非public的构造器
Constructor[] getDeclaredConstructors() //获得public的所有构造器
获得类方法
Method getMethod(String name, Class[] params) //根据方法名,参数类型获得方法
Method[] getMethods() //获得所有的public方法
Method getDeclaredMethod(String name, Class[] params) //根据方法名和参数类型,获得public和非public的方法
Method[] getDeclaredMethods() //Demo6中 获得所有的public和非public方法
获得类中属性
Field getField(String name) //根据变量名得到相应的public变量
Field[] getFields() //获得类中所以public的方法
Field getDeclaredField(String name) // Demo5中 根据方法名获得public和非public变量
Field[] getDeclaredFields() //Demo6中 获得类中所有的public和非public变量
四、反射的使用应用场景
1) 工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory了。
2) 动态代理模式。
3) 数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动。
4)Web服务器中利用反射调用了Sevlet的服务方法。
实战:WKD项目中根据单据类型使用策略类导出相应的模板数据,通过Class.forName(单据类型对应的类全名),反射得到相应策略类。
五、综合实例分析
Person类
1 package reflect; 2 3 /** 4 * @author 佛大Java程序员 5 * @since 1.0.0 6 */ 7 public class Person { 8 //两个私有属性 9 private int age; 10 private String name; 11 public Person(){ 12 13 } 14 public Person(int age, String name){ 15 this.age = age; 16 this.name = name; 17 } 18 19 public int getAge() { 20 return age; 21 } 22 public void setAge(int age) { 23 this.age = age; 24 } 25 public String getName() { 26 return name; 27 } 28 public void setName(String name) { 29 this.name = name; 30 } 31 }
SuperMan类
1 package reflect; 2 3 /** 4 * @author 佛大Java程序员 5 * @since 1.0.0 6 */ 7 public class SuperMan extends Person implements ActionInterface { 8 private boolean BlueBriefs; 9 10 public void fly() 11 { 12 System.out.println("我是fly方法......"); 13 } 14 15 public boolean isBlueBriefs() { 16 return BlueBriefs; 17 } 18 public void setBlueBriefs(boolean blueBriefs) { 19 BlueBriefs = blueBriefs; 20 } 21 22 @Override 23 public void walk(int m) { 24 System.out.println("我是walk方法,我的int值为:" + m ); 25 } 26 }
ActionInterface接口
1 package reflect; 2 3 /** 4 * @author 佛大Java程序员 5 * @since 1.0.0 6 */ 7 public interface ActionInterface { 8 public void walk(int m); 9 }
Main类
1 package reflect; 2 import java.lang.reflect.Constructor; 3 import java.lang.reflect.Field; 4 import java.lang.reflect.Method; 5 6 /** 7 * @author 佛大Java程序员 8 * @since 1.0.0 9 */ 10 public class Main { 11 /** 12 * 为了看清楚Java反射部分代码,所有异常我都最后抛出来给虚拟机处理! 13 */ 14 public static void main(String[] args) throws Exception { 15 16 System.out.println("Demo1==============================================="); 17 //Demo1. 通过Java反射机制得到类的包名和类名 18 Demo1(); 19 20 System.out.println("Demo2==============================================="); 21 //Demo2. 验证所有的类都是Class类的实例对象 22 Demo2(); 23 24 System.out.println("Demo3==============================================="); 25 //Demo3. 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在],无参构造 26 Demo3(); 27 28 System.out.println("Demo4==============================================="); 29 //Demo4: 通过Java反射机制得到一个类的构造函数,并实现构造带参实例对象 30 Demo4(); 31 32 System.out.println("Demo5==============================================="); 33 //Demo5: 通过Java反射机制操作成员变量, set 和 get 34 Demo5(); 35 36 System.out.println("Demo6==============================================="); 37 //Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等 38 Demo6(); 39 40 System.out.println("Demo7==============================================="); 41 //Demo7: 通过Java反射机制调用类中方法 42 Demo7(); 43 44 System.out.println("Demo8==============================================="); 45 //Demo8: 通过Java反射机制获得类加载器 46 Demo8(); 47 48 } 49 50 /** 51 * Demo1: 通过Java反射机制得到类的包名和类名 52 */ 53 public static void Demo1() 54 { 55 Person person = new Person(); 56 System.out.println("包名:" + person.getClass().getPackage().getName()); 57 System.out.println("完整类名:" + person.getClass().getName()); 58 } 59 60 /** 61 * Demo2: 验证所有的类都是Class类的实例对象 62 */ 63 public static void Demo2() throws ClassNotFoundException 64 { 65 //定义两个类型都未知的Class , 设置初值为null, 看看如何给它们赋值成Person类 66 Class<?> class1 = null; 67 Class<?> class2 = null; 68 69 //写法1, 可能抛出 ClassNotFoundException [多用这个写法] 70 class1 = Class.forName("reflect.Person"); 71 System.out.println("(写法1) 包名: " + class1.getPackage().getName() + "," 72 + "完整类名: " + class1.getName()); 73 74 //写法2 75 class2 = Person.class; 76 System.out.println("(写法2) 包名: " + class2.getPackage().getName() + "," 77 + "完整类名: " + class2.getName()); 78 } 79 80 /** 81 * Demo3: 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在] 82 */ 83 public static void Demo3() throws Exception 84 { 85 Class<?> class1 = null; 86 class1 = Class.forName("reflect.Person"); 87 //由于这里不能带参数,所以你要实例化的这个类Person,一定要有无参构造函数哈~ 88 Person person = (Person) class1.newInstance(); 89 person.setAge(20); 90 person.setName("佛大Java程序员"); 91 System.out.println("name: " + person.getName() + " age: " + person.getAge()); 92 } 93 94 /** 95 * Demo4: 通过Java反射机制得到一个类的构造函数,并实现创建带参实例对象 96 */ 97 public static void Demo4() throws Exception 98 { 99 Class<?> class1 = null; 100 Person person1 = null; 101 Person person2 = null; 102 103 class1 = Class.forName("reflect.Person"); 104 //Constructor[] getConstructors() 获得public的所有构造器 105 Constructor<?>[] constructors = class1.getConstructors(); 106 //无参构造函数 107 person1 = (Person) constructors[0].newInstance(); 108 person1.setAge(30); 109 person1.setName("Java程序员"); 110 //有参构造函数 111 person2 = (Person) constructors[1].newInstance(20,"佛大Java程序员"); 112 113 System.out.println("name: " + person1.getName() + " age: " + person1.getAge() ); 114 System.out.println("name: " + person2.getName() + " age: " + person2.getAge() ); 115 } 116 117 /** 118 * Demo5: 通过Java反射机制操作成员变量, set 和 get 119 * 120 */ 121 public static void Demo5() throws Exception 122 { 123 Class<?> class1 = null; 124 class1 = Class.forName("reflect.Person"); 125 //必须实例化对象 126 Object obj = class1.newInstance(); 127 128 // Field getDeclaredField(String name) 根据方法名获得public和非public变量 129 Field personNameField = class1.getDeclaredField("name"); 130 131 /* public void setAccessible(boolean flag) throws SecurityException;设置是否取消封装。 132 person类中的name是private修饰的,是无法被外部调用的,但是使用setAccessible取消封装就可以使用了。*/ 133 personNameField.setAccessible(true); 134 135 //相当于:Person对象.name = "佛大Java程序员"; 136 personNameField.set(obj, "佛大Java程序员"); 137 138 //相当于:Person对象.name 139 System.out.println("修改属性之后得到属性变量的值:" + personNameField.get(obj)); 140 } 141 142 /** 143 * Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等 144 */ 145 public static void Demo6() throws ClassNotFoundException 146 { 147 Class<?> class1 = null; 148 class1 = Class.forName("reflect.SuperMan"); 149 150 //取得父类名称 151 Class<?> superClass = class1.getSuperclass(); 152 System.out.println("SuperMan类的父类名: " + superClass.getName()); 153 154 155 //Field[] getDeclaredFields() 获得类中所有的public和非public变量 156 Field[] fields = class1.getDeclaredFields(); 157 for (int i = 0; i < fields.length; i++) { 158 System.out.println("类中的成员: " + fields[i]); 159 } 160 161 //Method[] getDeclaredMethods() 获得所有的public和非public方法 162 Method[] methods = class1.getDeclaredMethods(); 163 164 // 取得SuperMan类的方法 165 System.out.println("函数名:" + methods[0].getName()); 166 System.out.println("函数代码写法: " + methods[0]); 167 168 //取得类实现的接口,因为接口类也属于Class,所以得到接口中的方法也是一样的方法得到哈 169 Class<?> interfaces[] = class1.getInterfaces(); 170 for (int i = 0; i < interfaces.length; i++) { 171 System.out.println("实现的接口类名: " + interfaces[i].getName() ); 172 } 173 174 } 175 176 /** 177 * Demo7: 通过Java反射机制调用类方法 178 */ 179 public static void Demo7() throws Exception 180 { 181 Class<?> class1 = null; 182 class1 = Class.forName("reflect.SuperMan"); 183 184 //调用fly()方法 185 Method method = class1.getMethod("fly"); 186 method.invoke(class1.newInstance()); 187 188 //调用walk(int m)方法 189 method = class1.getMethod("walk",int.class); 190 method.invoke(class1.newInstance(),100); 191 } 192 193 /** 194 * Demo8: 通过Java反射机制得到类加载器信息 195 */ 196 public static void Demo8() throws ClassNotFoundException 197 { 198 Class<?> class1 = null; 199 class1 = Class.forName("reflect.SuperMan"); 200 String nameString = class1.getClassLoader().getClass().getName(); 201 202 System.out.println("类加载器类名: " + nameString); 203 } 204 }
运行结果:
说明:
如果运行过程中报了Exception in thread "main" java.lang.ClassNotFoundException,说明调用Class.forName(“类的全名”),中的类的全名不对,你可以通过demo1去获得类的全名,再修改其余demo中的类的全名。
六、常见面试题:
(1)什么是反射?作用?
(2)使用场景?
(3)反射的优缺点?
(4)反射的原理?
(5)获取 Class 对象的 3 种方法?创建对象的方法?
(6)动态代理相关知识
参考/好文:
百度百科 -- JAVA反射机制
https://baike.baidu.com/item/JAVA%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6/6015990?fr=aladdin
掘金 --浅析JAVA反射
https://juejin.im/post/5ab99b10f265da239c7b5e52
博主 -- 雨点的名字 -- java反射机制