Java的反射机制

一、什么是反射?反射的作用(红色字)

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以判断任意一个对象所属的类,可以调用任意一个对象的属性和方法这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

二.创建对象和反射创建对象的区别?(百度云)

Java中创建对象和反射创建对象的主要区别如下:

(1)创建方式不同:普通的创建对象是通过使用 new 关键字来创建,而反射创建对象是使用 Class 对象的 newInstance() 或 Constructor 类的 newInstance() 方法来创建对象。

(2)类型安全性不同:普通创建对象是在编译时进行类型检查(如果类不存在,则会抛出编译时异常),因此它具有类型安全性。而反射创建对象是在运行时进行类型检查(如果类不存在,则会抛出运行时异常),因此它的类型安全性较差。

(3)访问权限不同:普通创建对象只能访问公共构造函数,而反射创建对象可以访问所有构造函数,包括私有构造函数。

(4)性能不同:反射创建对象比普通创建对象要慢一些,因为它需要在运行时进行类型检查和动态绑定。

(5)使用场景不同:反射创建对象通常用于需要动态创建对象的场景,例如在框架、插件等方面。普通创建对象则适用于大多数情况下,因为它具有更好的类型安全性和更高的性能。

总之,反射创建对象提供了更大的灵活性和动态性,但它的类型安全性和性能都比普通创建对象差。

反射原理?

Java在将.class字节码文件读入内存时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制

三、反射常用方法解析

反射机制所需的类主要有java.lang包中的Class类和java.lang.reflet包中的Constructor类、Field类、Method类和Parameter类。Class类是一个比较特殊的类,它是反射机制的基础,Class类的对象表示正在运行的Java程序中的类或接口,类加载过程即是指JVM虚拟机把.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中的类的全名。

posted @ 2020-03-25 23:10  JustJavaIt  阅读(264)  评论(0编辑  收藏  举报