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;
        });
    }
}

原文地址:https://www.cnblogs.com/zhizaixingzou/p/10023612.html