zoukankan      html  css  js  c++  java
  • Java的反射机制

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

    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反射机制

    https://www.cnblogs.com/qdhxhz/p/9230805.html

    希望本文章对您有帮助,您的转发、点赞是我的创作动力,十分感谢。更多好文推荐,请关注我的微信公众号--JustJavaIt
  • 相关阅读:
    基于Java的地铁线路查询系统设计思路
    个人总结05
    构建之法读书笔记03
    Java 8 (二) 新的时间API
    MySql基础笔记(三)其他重要的事情
    MySql基础笔记(二)Mysql语句优化---索引
    JavaScript基础笔记(十四)最佳实践
    JavaScript基础笔记(十三)测试和调试
    MySql基础笔记(一)Mysql快速入门
    JavaScript基础笔记(十二)Ajax
  • 原文地址:https://www.cnblogs.com/liaowenhui/p/12571362.html
Copyright © 2011-2022 走看看