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

    版权声明:本文系博主原创,未经博主许可,禁止转载。保留所有权利。

    引用网址:https://www.cnblogs.com/zhizaixingzou/p/10023612.html

    目录

    1. 反射

    1.1. 什么是反射

    我们知道,类被JVM加载进入内存之后,JVM就会为该类生成一个对应的java.lang.Class对象(Class为元类)。这个对象包含了该类的完整的结构信息,包括字段名、方法名及参数情况等。通过它,我们能获取到这些结构信息,就像通过一面镜子“反射”(相比于类实例的点号直接访问)过来的影像。通过该Class对象,程序可以获得检测、访问和修改该类的状态或行为的能力

    1.2. 怎么使用反射

    1.2.1. 反射的核心类:java.lang.Class<T>

    我们使用反射的入口是java.lang.Class<T>

    java.lang.Class<T>Java反射机制的核心,它java_homejrelib t.jar.javalang eflect目录下定义

    Class<T>的用途是充当元类,它好像是一面镜子,哪个类作为它声明变量的值,哪个类就可以通过该变量获得其自身结构信息。例如,Class<String> clazz = String.classclazz就是String类型的模型了,通过它可以获得String类型的结构信息。

    Class<T>可以表示一个类(包含枚举)、接口(包含注解)、各型数组、基本类型(包含void)。

    1.2.2. 获取类对应的Class实例的3种方法

    Class并无公共构造方法,JVM在加载一个类时,是通过调用类加载器ClassLoaderdefineClass方法来获得该类的Class实例的,而该方法最终又调用如下本地方法:

    private native Class<?> defineClass1(String name, byte[] b, int off, int len,
                                         ProtectionDomain pd, String source);

    下面是3种获取该类对应Class实例的方法:

    1Class<?> clazz = Class.forName(“com.phewy.Employee”),参数须是全限定名形式的。

    2Class<?> clazz = Employee.class

    3Class<?> clazz = new Employee().getClass()

    1.2.3. Class<T>的定义和使用

    1.2.3.1. 获取类的组成

    getFields(),返回类的所有公共字段,包括继承来的公共字段,Field[]

    getMethods(),返回类的所有公共方法,包括继承来的公共方法,Method[]

    getConstructors(),返回类的所有公共构造方法,构造方法谈不上继承,Constructor<?>[]

    getAnnotations(),返回类的所有注解,包括继承来的注解,Annotation[]

    getClasses(),返回类的所有公共内部类,如“com.cjw.studying.Person$Capacity”,Class<?>[]

    getField(String name)Field

    getMethod(String name, Class<?>... parameterTypes)Method

    getConstructor(Class<?>... parameterTypes)Constructor<T>

    getAnnotation(Class<A> annotationClass)<A extends Annotation> A

    返回类的指定参数的成员,公共的,包括继承来的。

    getDeclaredFields()Field[]

    getDeclaredMethods()Method[]

    getDeclaredConstructors()Constructor<?>[]

    getDeclaredAnnotations()Annotation[]

    getDeclaredClasses()Class<?>[]

    返回类的所有成员,不只公共的,不包括继承来的。

    getDeclaredField(String name) Field

    getDeclaredMethod(String name, Class<?>... parameterTypes) Method

    getDeclaredConstructor(Class<?>... parameterTypes) Constructor<T>

    getDeclaredAnnotation(Class<A> annotationClass)<A extends Annotation> A

    返回类的指定参数的成员,不只公共的,不包括继承来的。

    getEnumConstants()T[]

    如果是枚举类型,则返回枚举类型的各个枚举值。

    getModifiers()int

    返回类的修饰符整型表示Modifier.toString(modifiers)可以转换该整型为字符串表示。

    getTypeParameters()TypeVariable<Class<T>>[]

    如果类是泛型,返回它的类型参数数组,每个元素代表一个类型参数。

    getSuperclass()Class<? super T>

    返回类父类。

    getPackage()Package

    返回类所在包。

    getInterfaces()Class<?>[]

    返回类实现或扩展的接口集合,数组内顺序与声明顺序一致。

    getComponentType()Class<?>

    如果类是数组,则返回类的组件类型,否则返回null

    new Thread() {}.getClass().getEnclosingMethod()Method

    如果类是匿名类,则返回定义该匿名类的方法(匿名类在该方法中)。

    getEnclosingConstructor()Constructor<?>

    如果类是匿名类,则返回该匿名类所在的构造方法(匿名类在该构造方法中)。

    getDeclaringClass()Class<?>

    返回直接定义(而非在方法内定义)类的外部类方法内定义的匿名类的该方法返回null

    getEnclosingClass()Class<?>

    返回类的外部类,无论本类定义在外部类的字段处还是方法内

    getEnclosingClass()getDeclaringClass()区别见:http://lee-govern.iteye.com/blog/1776046

    getName()

    返回类的全限定名,对于数组则用描述符的形式learning.demo.Entry”、“[I”、“[Ljava.lang.Integer;

    getSimpleName()

    返回简单的类名,如Employee”、“Vector[][]”。

    getCanonicalName()

    返回Java语言规范汇中规定的规范化名称,如learning.demo.Entry”、java.util.Vector[][]也就是getSimpleName()返回的前面加上包名路径

    1.2.3.2. 类特征的判断

    isInterface()

    isArray()

    isPrimitive()

    isAnnotation()

    isSynthetic()

    isEnum()

    判断类是否是接口、数组、基本类型(包括void)、注解、复合类、枚举。

    isAnonymousClass()

    判断类是否匿名类。

    isLocalClass()

    判断类是否本地类即定义于方法内的非匿名的类

     1 public class Demo {
     2     public static void main(String[] args) {
     3         class Entry {
     4             private int value;
     5             
     6             public int getValue() {
     7                 return value;
     8             }
     9 
    10             public void setValue(int value) {
    11                 this.value = value;
    12             }
    13         }
    14     }
    15 }

    isMemberClass()

    判断类是否是内部成员非方法内定义,也非匿名类

    isAnnotationPresent(Class<? extends Annotation> annotationClass)

    如果指定注解有在类上,则返回true

    1.2.3.3. 类其他基本语法

    toString()

    1 public String toString() {
    2     return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
    3         + getName();
    4 }

    返回如下各种形式的字符串,枚举是一种类,注解是一种接口:

    Class.forName("com.cjw.studying.Person")class com.cjw.studying.Person

    Class.forName("com.cjw.studying.IRun")interface com.cjw.studying.IRun

    Vector[][].classclass [[Ljava.util.Vector;

    int.classint

    void.classvoid

    Class.forName("com.cjw.studying.Person")

    返回指定全限定名对应的类,它等效于

    Class.forName("com.cjw.studying.Person", true, this.getClass().getClassLoader())

    true表示如果之前没有初始化过该类,则初始化它,第三个参数如果是null,则使用引导类加载器加载该类,它会引发目标类的定位、加载和链接。

    newInstance()

    调用类的无参构造函数创建一个实例。如果没有无参构造函数,则会报异常,而且该构造函数必须是非private的,可以是protectedpublic的,Spring框架就是用这种方式创建实例的。

    isInstance(object)

    如果object不为null,且可以转换为类,则返回true,它是instanceOf的动态形式。

    cast(object)

    object强制转换为类。

    isAssignableFrom(Employee.class)

    判断参数是否是类的子类。

    <U> Class<? extends U> asSubclass(Class<U> clazz)

    父类为参数,转换为子类。

    结果为:java.util.ArrayList

    getClassLoader()

    返回类的类加载器。

    getResource(String name)

    返回的类型为URL

     1 package com.cjw.learning.reflect;
     2 
     3 public class Demo03 {
     4     public static void main(String[] args) {
     5         System.out.println(Person.class.getResource(""));
     6         System.out.println(Person.class.getResource("/"));
     7         System.out.println(Person.class.getClassLoader().getResource(""));
     8         System.out.println(Person.class.getClassLoader().getResource("/"));
     9     }
    10 }

    getResourceAsStream(String name)

    返回的是InputStream类型。

    name指定资源的路径如果不以/”开头,则是相对路径可以使用.或者..两个符号),即与本.class在同一个包目录下否则是从classPath下取即本类所在包的顶级分项父目录下,这个父目录包含了许多的Jar

    org.ethereum.solidity.compiler.Solc

    com.x.y 下有类me.class同时有资源文件myfile.xmlme.class.getResourceAsStream("myfile.xml")

    com.x.y 下有类me.class 同时在 com.x.y.file 目录下有资源文件myfile.xmlme.class.getResourceAsStream("file/myfile.xml")

    com.x.y 下有类me.class同时在 com.x.file 目录下有资源文件myfile.xmlme.class.getResourceAsStream("/com/x/file/myfile.xml")

    Class.getClassLoader.getResourceAsStream(String path) :默认则是从classPath根下获取,path不能以/开头,因为最终是由ClassLoader获取资源ClassLoader又是逐层向上委托的,最后到根类加载器,是C++实现的,它不许以/开头 

    ServletContext.getResourceAsStream(String path):默认从WebAPP根目录下取资源,Tomcatpath是否以/开头无所谓

    1.2.3.4. 使用举例

    根据构造方法类创建实例:

    Constructor<?> con = String.class.getConstructor(char[].class)

    (String) con.newInstance({'h','e','l','l','o'})

    可以修改字段的可访问性,private也可以访问:

    field.setAccessible(true)

    field.set(object, "110")

    field.get(object)

    java.lang.reflect.Modifier.toString(field.getModifiers())

    field.getType().getSimpleName()

    field.getName()

    获取方法的返回类型:

    method.getReturnType()

    获取方法的形参类型:

    method.getParameterTypes()

    获取方法的抛出异常列表:

    method.getExceptionTypes()

    调用方法(如果是静态方法,object就用null代替):

    method.invoke(object, 参数, ……)

    利用反射创建数组:

     1 package com.cjw.learning.reflect;
     2 
     3 import java.lang.reflect.Array;
     4 
     5 public class Demo02 {
     6     public static void main(String[] args) {
     7         Class<?> clazz = int.class;
     8         Object array = Array.newInstance(clazz, 4);
     9         Array.set(array, 0, 7);
    10         Array.set(array, 1, 9);
    11         System.out.println(Array.get(array, 2));
    12     }
    13 }

    Array类为java.lang.reflect.Array类。

    int.class == Integer.class返回falseint.class == Integer.TYPE返回true

    1.3. 解决什么问题

    1.3.1. 编写通用DAO

    DAO,即Data Access Object,数据访问对象,乃是用来隔离业务逻辑层和持久层的一个接口、对象,或者说服务。业务逻辑层使用DAO,可以只关注数据应该怎么处理(即怎么组合使用CRUD几个操作),不用关注数据怎么持久化(持久化的过程都由DAO代劳了)。

    传统上,对于每一个数据库表我们会在应用软件端准备一个对应的DAO类访问它,实现的组成结构如下:

    一个管理数据库连接的类,可以是连接工厂,可以是连接池。

    一个与数据库表对应的POJO类。

    一个DAO类,封装了对表的增删改查等数据库访问。

    但这有个问题是,数据库若有多张不同的表,我们就需要为每一张表建立对应的DAO。为此,我们可以使用反射只建立一个通用DAO,用它一个可以代替许多个。但要做到这些,需要遵循一定的规则:

    1)数据库的每一个表对应一个POJO类,表中的每一个字段对应POJO类的中的一个属性。并且POJO类的名字和表的名字相同,属性名和字段名相同,大小写没有关系,因为MySQL数据库不区分大小写 

    2)为POJO类中的每一个属性添加标准的setget方法。

    ......

    编写通用DAO的基本原理是:保存数据时,把需要保存的对象的属性值全部取出来再拼凑sql语句,查询时,将查询到的数据全部包装成一个java对象,......能这样也必须定制规则,这是框架的必要代价 实际上,目前已有许多框架正是这样做的,如ibatis、Spring的模板方法等

    1.3.2. 各种框架如JUnit

    好多框架都使用了反射机制代码自动生成器JUnit。JUnit的工作原理是,在测试运行器中,使用反射获取测试类,并获取注解,根据注解安排测试的执行和顺序控制等。

    反射一般会与属性配置文件.properies一起使用,用来增强程序的灵活性。

    1.3.3. 反射使用的优缺点

    反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性J2EE中优势明显,因为他可以不卸载的情况下增加功能,如OSGi规范就应用了这点

    它的缺点是对性能有影响。但仍然可以考虑性能提升的可能:

    1)善用API

    比如,尽量不要getMethods()后再遍历筛选,而直接用getMethod(methodName)来根据方法名获取方法。

    2)使用缓存

    需要多次动态创建一个类的实例的时候,有缓存的写法会比没有缓存要快很多

    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>2.6.2</version>
    </dependency>

     1 package com.cjw.learning.reflect;
     2 
     3 import com.github.benmanes.caffeine.cache.Cache;
     4 import com.github.benmanes.caffeine.cache.Caffeine;
     5 
     6 import java.util.concurrent.TimeUnit;
     7 
     8 public class ClassCache {
     9     private Cache<String, Class<?>> cache =
    10             Caffeine.newBuilder().maximumSize(100).expireAfterAccess(1, TimeUnit.DAYS).build();
    11 
    12     public Class<?> getClass(String fullClassName) {
    13         return cache.get(fullClassName, clazzName -> {
    14             Class clazz = null;
    15             try {
    16                 clazz = Class.forName(clazzName);
    17                 cache.put(clazzName, clazz);
    18             } catch (ClassNotFoundException e) {
    19                 e.printStackTrace();
    20             }
    21             return clazz;
    22         });
    23     }
    24 }

    1.4. 参考资料

    http://www.importnew.com/17616.html

    http://blog.csdn.net/qq_20745827/article/details/50891606

    http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html

    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>2.6.2</version>
    </dependency>

     

    package com.cjw.learning.reflect;

    import com.github.benmanes.caffeine.cache.Cache;
    import com.github.benmanes.caffeine.cache.Caffeine;

    import java.util.concurrent.TimeUnit;

    public class ClassCache {
        private Cache<String, Class<?>> cache =
                Caffeine.newBuilder().maximumSize(100).expireAfterAccess(1, TimeUnit.DAYS).build();

        public Class<?> getClass(String fullClassName) {
            return cache.get(fullClassName, clazzName -> {
                Class clazz = null;
                try {
                    clazz = Class.forName(clazzName);
                    cache.put(clazzName, clazz);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return clazz;
            });
        }
    }

  • 相关阅读:
    条件极值(1):隐函数极值问题
    从隐函数存在定理到隐函数定理
    吐槽教科书:不先行解释表达偏导数的符号
    8237dma的四种传送方式简介
    多线程中wait和notify的理解与使用
    ★三个和尚与机构臃肿的故事
    ★三个和尚与机构臃肿的故事
    常用的组播保留地址列表
    常用的组播保留地址列表
    谈谈IT界8大恐怖预言!
  • 原文地址:https://www.cnblogs.com/zhizaixingzou/p/10023612.html
Copyright © 2011-2022 走看看