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

    一.反射概述:
      Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,然后直接对类进行实例化,使用类对象进行操作,而反射是一开始并不知道我们要初始化的类的对象是什么,不能够使用new来实例化对象。框架=反射+注解+设计模式
    java反射机制提供的功能:
    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时获取泛型信息
    • 在运行时调用任意一个对象的成员变量和方法
    • 在运行时处理注解
    • 生成动态代理
    反射相关的主要API:
    • java.lang.Class:代表一个类
    • java.lang.reflect.Method:代表类的方法
    • java.lang.reflect.Field:代表类的成员变量
    • java.lang.reflect.Constructor:代表类的构造器
    二.Class类的理解与使用
      Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。Class类是java反射的源头,反射也就是通过对象反射出类的名称。
      类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行揭示运行。相当于将某个字节码文件加载到内存中,此过程就是类的加载。加载到内存中的类,我们就成为运行时的类,此运行时的类,就作为Class的一个实例。
      对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
    Class类注:
    • Class本身也是一个类
    • Class 对象只能由系统建立对象
    • 一个加载的类在 JVM 中只会有一个Class实例
    • 一个Class对象对应的是一个加载到JVM中的一个.class文件
    • 每个类的实例都会记得自己是由哪个 Class 实例所生成
    • 通过Class可以完整地得到一个类中的所有被加载的结构
    • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
    • 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式
      来获取此运行时类。

    1..获取Class实例的方式

     1 @Test
     2     public void test3() throws ClassNotFoundException {
     3         //方式一:调用运行时类的属性:.class
     4         Class clazz1 = Person.class;
     5         System.out.println(clazz1);
     6         //方式二:通过运行时类的对象,调用getClass()
     7         Person p1 = new Person();
     8         Class clazz2 = p1.getClass();
     9         System.out.println(clazz2);
    10 
    11         //方式三:调用Class的静态方法:forName(String classPath)
    12         Class clazz3 = Class.forName("com.atguigu.java.Person");
    13 //        clazz3 = Class.forName("java.lang.String");
    14         System.out.println(clazz3);
    15 
    16         System.out.println(clazz1 == clazz2);
    17         System.out.println(clazz1 == clazz3);
    18 
    19         //方式四:使用类的加载器:ClassLoader  (了解)
    20         ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    21         Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");
    22         System.out.println(clazz4);
    23 
    24         System.out.println(clazz1 == clazz4);
    25 
    26     }

    2.Class类的常用方法

    (1)forName():返回指定类名name的Class对象

    (2)getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

    (3)newInstance():调用缺省的构造函数,返回该Class对象的一个实例

    (4)getClassLoader():返回该类的类加载器

    (5)getSuperclass():返回此Class所表示的实体的超类的Class

    (6)Constructor[] getConstructors():返回一个包含某些Constructor对象的数组

    (7)Field[] getDeclaredFields():返回Field对象的一个数组

    (8) Method getMethod(String name,Class … paramTypes):返回一个Method对象,此对象的形参类型为paramType

    3.哪些类型可以有Class对象?

    (1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
    (2)interface:接口
    (3)[]:数组
    (4)enum:枚举
    (5)annotation:注解@interface
    (6)primitive type:基本数据类型
    (7)void
    三.反射应用
    1.创建运行时类的对象
    创建类的对象:调用Class对象的newInstance()方法
    要求:类必须有一个无参构造器,且类的构造器的访问权限需要足够
    或者调用有参构造器:
    通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
    代码显示:
    1 @Test
    2     public void testField() throws Exception {
    3         Class clazz = Person.class;
    4 
    5         //创建运行时类的对象
    6         Person p = (Person) clazz.newInstance();
    1 //1.根据全类名获取对应的Class对象
    2 String name = “atguigu.java.Person";
    3 Class clazz = null;
    4 clazz = Class.forName(name);
    5 //2.调用指定参数结构的构造器,生成Constructor的实例
    6 Constructor con = clazz.getConstructor(String.class,Integer.class);
    7 //3.通过Constructor的实例创建对应类的对象,并初始化类属性
    8 Person p2 = (Person) con.newInstance("Peter",20);
    9 System.out.println(p2);

    2.获取运行时类的完整结构

    我们可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等。。。。

    获取属性结构:

     1 @Test
     2 public void test1(){
     3 
     4     Class clazz = Person.class;
     5 
     6     //获取属性结构
     7     //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
     8     Field[] fields = clazz.getFields();
     9     for(Field f : fields){
    10         System.out.println(f);
    11     }
    12     System.out.println();
    13 
    14     //getDeclaredFields():获取当前运行时类中声明的所属性。(不包含父类中声明的属性
    15     Field[] declaredFields = clazz.getDeclaredFields();
    16     for(Field f : declaredFields){
    17         System.out.println(f);
    18     }
    19 }

    获取方法:

     1 @Test
     2 public void test1(){
     3 
     4     Class clazz = Person.class;
     5 
     6     //getMethods():获取当前运行时类及其所父类中声明为public权限的方法
     7     Method[] methods = clazz.getMethods();
     8     for(Method m : methods){
     9         System.out.println(m);
    10     }
    11     System.out.println();
    12     //getDeclaredMethods():获取当前运行时类中声明的所方法。(不包含父类中声明的方法
    13     Method[] declaredMethods = clazz.getDeclaredMethods();
    14     for(Method m : declaredMethods){
    15         System.out.println(m);
    16     }
    17 }

    获取构造器结构:

     1 @Test
     2     public void test1(){
     3 
     4         Class clazz = Person.class;
     5         //getConstructors():获取当前运行时类中声明为public的构造器
     6         Constructor[] constructors = clazz.getConstructors();
     7         for(Constructor c : constructors){
     8             System.out.println(c);
     9         }
    10 
    11         System.out.println();
    12         //getDeclaredConstructors():获取当前运行时类中声明的所的构造器
    13         Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    14         for(Constructor c : declaredConstructors){
    15             System.out.println(c);
    16         }
    17 
    18     }

    3.获取运行时类的指定结构

    调用指定方法:

    通过反射,调用类中的方法,通过Method类完成。步骤:
    1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
    2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

    Object invoke(Object obj, Object … args)方法说明:
    • Object 对应原方法的返回值,若原方法无返回值,此时返回null
    • 若原方法若为静态方法,此时形参Object obj可为null
    • 若原方法形参列表为空,则Object[] args为null
    • 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

    代码显示:

    调用指定的属性:

     1 调用指定的属性:
     2 @Test
     3 public void testField1() throws Exception {
     4     Class clazz = Person.class;
     5 
     6     //创建运行时类的对象
     7     Person p = (Person) clazz.newInstance();
     8 
     9     //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
    10     Field name = clazz.getDeclaredField("name");
    11 
    12     //2.保证当前属性是可访问的
    13     name.setAccessible(true);
    14     //3.获取、设置指定对象的此属性值
    15     name.set(p,"Tom");
    16 
    17     System.out.println(name.get(p));
    18 }

    调用指定的方法:

     1 @Test
     2     public void testMethod() throws Exception {
     3 
     4         Class clazz = Person.class;
     5 
     6         //创建运行时类的对象
     7         Person p = (Person) clazz.newInstance();
     8 
     9         /*
    10         1.获取指定的某个方法
    11         getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
    12          */
    13         Method show = clazz.getDeclaredMethod("show", String.class);
    14         //2.保证当前方法是可访问的
    15         show.setAccessible(true);
    16 
    17         /*
    18         3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
    19         invoke()的返回值即为对应类中调用的方法的返回值。
    20          */
    21         Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
    22         System.out.println(returnValue);
    23 
    24         System.out.println("*************如何调用静态方法*****************");
    25 
    26         // private static void showDesc()
    27 
    28         Method showDesc = clazz.getDeclaredMethod("showDesc");
    29         showDesc.setAccessible(true);
    30         //如果调用的运行时类中的方法没返回值,则此invoke()返回null
    31 //        Object returnVal = showDesc.invoke(null);
    32         Object returnVal = showDesc.invoke(Person.class);
    33         System.out.println(returnVal);//null
    34 
    35     }

    调用指定构造器:

     1 @Test
     2 public void testConstructor() throws Exception {
     3     Class clazz = Person.class;
     4 
     5     //private Person(String name)
     6     /*
     7     1.获取指定的构造器
     8     getDeclaredConstructor():参数:指明构造器的参数列表
     9      */
    10 
    11     Constructor constructor = clazz.getDeclaredConstructor(String.class);
    12 
    13     //2.保证此构造器是可访问的
    14     constructor.setAccessible(true);
    15 
    16     //3.调用此构造器创建运行时类的对象
    17     Person per = (Person) constructor.newInstance("Tom");
    18     System.out.println(per);
    19 
    20 }
    关于setAccessible方法的使用:
    1. Method和Field、Constructor对象都有setAccessible()方法。
    2. setAccessible启动和禁用访问安全检查的开关。
    3. 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。使得原本无法访问的私有成员也可以访问
    4. 参数值为false则指示反射的对象应该实施Java语言访问检查。
  • 相关阅读:
    linux安装jdk
    maven工程直接部署在tomcat上
    将maven工程转成dynamic web project
    史上最全最强SpringMVC详细示例实战教程
    06-spring框架—— Spring 与Web
    05-spring框架—— Spring 事务
    04-spring框架—— Spring 集成 MyBatis
    03-spring框架—— AOP 面向切面编程
    01-spring框架——spring概述
    我对于语言只是工具的理解和感悟
  • 原文地址:https://www.cnblogs.com/xiuercui/p/13432174.html
Copyright © 2011-2022 走看看