zoukankan      html  css  js  c++  java
  • Java动态性之--反射机制

    1. 动态语言

    程序运行时,可以改变结构或变量类型。典型的语言:

    • Pythonrubyjavascript
    • 如下javascript代码
    function test(){
    
    var s = "var a=3;var b=5;alert(a+b);";
    
    eval(s);
    
    }
    • C,C++,java不是动态语言,java可以称之为“准动态语言”。但是java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性
    • java的动态性让编程的时候更加灵活!

    2. 反射机制reflection

    • 指的是可以于运行时加载、探知、使用编译其完全未知的类。
    • 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;

        Class c = Class.forName(cn.stu.test.User);

    • 加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所有,我们形象的称之为:反射

     例1:

     1 public class Demo01 {
     2     public static void main(String[] args) {
     3         String path = "cn.stu.ref.bean.User";
     4         
     5         try {
     6             //方法一
     7             Class clazz = Class.forName(path);
     8             //
     9             /**
    10              * ==================================
    11              * 对象时表示或封装一些数据。一个类被加载后,
    12              * JVM会创建一个对应该类的Class对象
    13              * 类的整个结构信息会会放到对应的Class对象中。
    14              * 这个Class对象就是一面镜子一样,
    15              * 通过这面镜子我们可以看到对应类的全部信息
    16              * ===================================
    17              */
    18             System.out.println(clazz.hashCode());
    19             //一个类只对应一个Class对象
    20             Class clazz2 = Class.forName(path);
    21             System.out.println(clazz2.hashCode());
    22             
    23             //方法二
    24             Class strClass = String.class;
    25             Class strClass2 = path.getClass();
    26             System.out.println(strClass==strClass2);
    27             
    28         } catch (ClassNotFoundException e) {
    29             e.printStackTrace();
    30         }
    31     }
    32 }

    3. Class类介绍

    当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便自动产生一个Class对象

    Class类似Reflection的根源

    针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

     

    Class类的对象如何获取?

    • 运用getClass()
    • 运用Class.forName()  --常用
    • 运用.class语法

    例2:

     1 public class Demo02 {
     2     
     3     public static void main(String[] args) {
     4         String path = "cn.stu.ref.bean.User";
     5         try {
     6             Class clazz = Class.forName(path);
     7             
     8             //获取类的名称
     9             System.out.println(clazz.getName());//类名+包名
    10             System.out.println(clazz.getSimpleName());//类名
    11             
    12             //获取属性名
    13             Field[] fields = clazz.getFields(); //只能获得public的属性
    14             Field[] fields2 = clazz.getDeclaredFields();//获得所有属性
    15             Field field = clazz.getDeclaredField("name");//获取指定name属性
    16             
    17             //获取方法名称
    18             Method[] method = clazz.getDeclaredMethods();
    19             Method m1 = clazz.getDeclaredMethod("getName", null);
    20             //如果方法有参,则必须传递参数类型对应的class对象
    21             Method m2 = clazz.getDeclaredMethod("setName", String.class);
    22             
    23             //获取构造器
    24             Constructor[] constructors = clazz.getDeclaredConstructors();
    25             Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class);
    26             
    27         } catch (Exception e) {
    28             // TODO Auto-generated catch block
    29             e.printStackTrace();
    30         } 
    31     }
    32 }

     例3:

     1 public class Demo03 {
     2     public static void main(String[] args) {
     3         String path = "cn.stu.ref.bean.User";
     4         
     5         try {
     6             Class clazz = Class.forName(path);
     7             
     8             //通过反射API调用构造方法,构造对象
     9             User u = (User) clazz.newInstance();//其实是调用了User的无参构造方法
    10             
    11             Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class);
    12             User u2 = (User) c.newInstance(1001,18,"张三");
    13             
    14             //通过反射API调用普通方法
    15             User u3 = (User) clazz.newInstance();
    16             Method method = clazz.getDeclaredMethod("setName", String.class);
    17             method.invoke(u3,"李四"); //u3.setName("李四")
    18             
    19             //通过反射API操作属性
    20             User u4 = (User) clazz.newInstance();
    21             Field f = clazz.getDeclaredField("name");
    22             f.setAccessible(true);//不做安全检查,直接访问 
    23             f.set(u4, "二麻子"); //通过反射直接写属性
    24             String name = u4.getName();
    25             String name2 = (String) f.get(u4); //通过反射直接读属性的值
    26         } catch (Exception e) {
    27             e.printStackTrace();
    28         }
    29     }
    30 }

    4. 反射机制性能问题

    • setAccessible
      • 启动和金庸访问安全检查的开关,值为true 则指示反射的对象在使用时应该取消java语言访问检查,值为false 则指示反射的对象应该实施java语言访问检查。并不是true就能访问,false就不能访问。
      • 禁止安全检查,可以提高反射的运行速度
    • 可以考虑使用: cglib/javaassist字节码操作

     例4:

     1 /**
     2  * 通过跳过安全检查,提高反射效率
     3  * 三种执行方法的效率差异比较
     4  */
     5 public class Demo04 {
     6     
     7     public static void test01(){
     8         User u = new User();
     9         
    10         long startTime = System.currentTimeMillis();
    11         
    12         for (int i = 0; i < 1000000000L; i++) {
    13             u.getName();
    14         }
    15         
    16         long endTime = System.currentTimeMillis();
    17         System.out.println("普通方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms"); 
    18     }
    19     
    20     public static void test02() throws Exception{
    21         User u = new User();
    22         Class clazz = u.getClass();
    23         Method m = clazz.getDeclaredMethod("getName", null);
    24 //        m.setAccessible(true);
    25         
    26         long startTime = System.currentTimeMillis();
    27         
    28         for (int i = 0; i < 1000000000L; i++) {
    29             m.invoke(u, null);
    30         }
    31         
    32         long endTime = System.currentTimeMillis();
    33         System.out.println("反射动态方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms");
    34     }
    35     
    36     public static void test03() throws Exception{
    37         User u = new User();
    38         Class clazz = u.getClass();
    39         Method m = clazz.getDeclaredMethod("getName", null);
    40         m.setAccessible(true);    //不需要执行访问安全检查
    41         
    42         long startTime = System.currentTimeMillis();
    43         
    44         for (int i = 0; i < 1000000000L; i++) {
    45             m.invoke(u, null);
    46         }
    47         
    48         long endTime = System.currentTimeMillis();
    49         System.out.println("反射动态方法调用,跳过安全检查,执行10亿次,耗时:"+(endTime-startTime)+"ms");
    50     }
    51     
    52     
    53     public static void main(String[] args) throws Exception {
    54         test01();
    55         test02();
    56         test03();
    57     }
    58 }

    输出结果:

        普通方法调用,执行10亿次,耗时:6665ms
        反射动态方法调用,执行10亿次,耗时:62947ms
        反射动态方法调用,跳过安全检查,执行10亿次,耗时:11864ms

     

    5. 反射操作泛型

    • java采用泛型擦除的机制来引入泛型。java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
    • 为了通过反射操操作这些类型以迎合实际开发的需要,java就新增了ParameterzedTypeGenericArrayTypeTypeVariableWildcardType几种类型来代表不能被诡异到Class类中的类型但是又和原始类型齐名的类型。
    • ParmterizedType: 表示一种参数化的类型,比如Collection<String>
    • GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
    • TypeVariable:是各种类型变量的公共父接口
    • WildcardType:代表一种通配符类型表达式
  • 相关阅读:
    日积月累flex4细节上的改变
    flex3:dragdrop全攻略(二)
    理解自定义组件
    两个mxml页面的跳转问题
    日积月累12个flex常用功能代码(转载)
    flex开源工具整理
    二叉树学习(上)
    实现数据库同步备份 过程结果分析
    ASP.NET服务器对于请求的处理过程
    C#泛型
  • 原文地址:https://www.cnblogs.com/dooor/p/5285572.html
Copyright © 2011-2022 走看看