获取Java类型系统,主要有两个方式:一种是传统的RTTI(Run-Time Type Identification),它假定咱们在编译时已经知道了全部的类型信息;另外一种是反射(Reflect),它容许咱们在程序运行时获取并使用类型信息。
假若有一个简单的继承体系,让咱们看下在RTTI和Reflect不一样状况下如何获取类型信息。java
Animal为接口,定义getType以返回不一样动物的类型,Cat、Dog、Elephant等为具体实现类,均实现getType接口。通常状况下,咱们会建立一个具体的对象(Cat,Dog,Elephant等),把它向上转型为Animal,并在程序后面直接使用Animal引用。设计模式
具体样例代码以下:数组
/** * 动物 */ public interface Animal { /** * 获取动物类型 * @return */ String getType(); } /** * 动物具体子类 猫 */ public class Cat implements Animal{ @Override public String getType() { return "猫"; } } /** * 动物具体子类 狗 */ public class Dog implements Animal{ @Override public String getType() { return "狗"; } } /** * 动物具体实现 大象 */ public class Elephant implements Animal{ @Override public String getType() { return "大象"; } }
让咱们看下相同的功能经过硬编码与反射两个机制如何实现。安全
RTTI假定在编译期,已经知道了全部的类型信息。在编码时,能够直接使用具体的类型信息,这是咱们最多见的类型用法。
在编译期,编译器经过容器、泛型保障类型系统的完整性;在运行时,由类型转换操做来确保这一点。app
硬编码样例以下:框架
public static void main(String... args){ List<Animal> animals = createAnimals(); for (Animal animal : animals){ System.out.println(animal.getType()); } } /** * RTTI假定咱们在编译时已经知道了全部的类型 * @return */ private static List<Animal> createAnimals() { List<Animal> animals = new ArrayList<>(); animals.add(new Cat()); // 已知类型Cat animals.add(new Elephant()); // 已知类型Elephant animals.add(new Dog()); // 已知类型 Dog return animals; }
在这个例子中,咱们把Cat、Elephant、Dog等向上转型为Animal并存放于List<Animal>中,在转型过程当中丢失了具体的类型信息(只保留了接口信息Animal);当咱们从List<Animal>中取出元素时,这时容器(容器内部全部的元素都被当作Object)会自动将结果转型成Animal。这是RTTI最基本的用法,在Java中全部的类型转换都是在运行时进行有效性检查。这也是RTTI的含义,在运行时,识别一个对象的类型。ide
Reflect容许咱们在运行时获取并使用类型信息,它主要用于在编译阶段没法得到全部的类型信息的场景,如各种框架。
反射样例以下:函数
private static final String[] ANIMAL_TYPES = new String[]{ "com.example.reflectdemo.base.Cat", "com.example.reflectdemo.base.Elephant", "com.example.reflectdemo.base.Dog" }; public static void main(String... args){ List<Object> animals = createAnimals(); for (Object animal : animals){ System.out.println(invokeGetType(animal)); } } /** * 利用反射API执行getType方法(等同于animal.getType) * @param animal * @return */ private static String invokeGetType(Object animal){ try { Method getTypeMethod = Animal.class.getMethod("getType"); return (String) getTypeMethod.invoke(animal); }catch (Exception e){ return null; } } /** * 反射容许咱们在运行时获取类型信息 * @return */ private static List<Object> createAnimals() { List<Object> animals = new ArrayList<>(); for (String cls : ANIMAL_TYPES){ animals.add(instanceByReflect(cls)); } return animals; } /** * 使用反射机制,在运行时动态的实例化对象(等同于new关键字) * @param clsStr * @return */ private static Object instanceByReflect(String clsStr) { try { // 经过反射获取类型信息 Class cls = Class.forName(clsStr); // 经过Class实例化对象 Object object = cls.newInstance(); return object; }catch (Exception e){ e.printStackTrace(); return null; } }
反射,能够经过一组特殊的API,在运行时,动态执行全部Java硬编码完成的功能(如对象建立、方法调用等)。性能
相比硬编码,Java反射API要复杂的多,但其给咱们带来了更大的灵活性。this
要理解RTTI在Java中的工做原理,首先须要知道类型信息在Java中是如何表示的。这个工做是由称为Class对象的特殊对象完成的,它包含了与类相关的全部信息。Java使用Class对象来执行RTTI。
类是程序的一部分,每一个类都会有一个Class对象。每当编写并编译一个新类(动态代理、CGLIB、运行时编译都能建立新类),就会产生一个Class对象,为了生成这个类的对象,运行这个程序的JVM将使用称为“类加载器”的子系统。
类加载器子系统,是JVM体系重要的一环,主要完成将class二进制文件加载到JVM中,并将其转换为Class对象的过程。
类加载器子系统其实是一条类加载器链,可是只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的是可信类,包括Java API类,他们一般是从本地加载。在这条链中,一般不须要添加额外的类加载器,可是若是有特殊需求,能够挂载新的类加载器(好比Web容器)。
全部的类都是在第一次使用时,动态加载到JVM中的,当程序建立第一次对类的静态成员引用时,就会加载这个类。实际上构造函数也是类的静态方法,所以使用new关键字建立类的新对象也会被当作对类的静态引用,从而触发类加载器对类的加载。
Java程序在它开始运行以前并不是被所有加载,各个部分是在须要时按需加载的。类加载器在加载类以前,首先检查这个类的Class是否已经加载,若是还没有加载,加载器会按照类名查找class文件,并对字节码进行有效性校验,一旦Class对象被载入内存,它就用来建立这个类的全部对象。
static初始化块在类加载时调用,所以能够用于观察类在何时进行加载,样例以下:
static class C1{ static { System.out.println("C1"); } } static class C2{ static { System.out.println("C2"); } } static class C3{ static { System.out.println("C3"); } } public static void main(String... args) throws Exception{ System.out.println("new start"); // 构造函数为类的静态引用,触发类型加载 new C1(); new C1(); System.out.println("new end"); System.out.println(); System.out.println("Class.forName start"); // Class.forName为Class上的静态函数,用于强制加载Class Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2"); Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2"); System.out.println("Class.forName end"); System.out.println(); System.out.println("C3.class start"); // Class引用,会触发Class加载,可是不会触发初始化 Class c1 = C3.class; Class c2 = C3.class; System.out.println("C3.class end"); System.out.println(); System.out.println("c1.newInstance start"); // 调用class上的方法,触发初始化逻辑 c1.newInstance(); System.out.println("c1.newInstance end"); }
输出结果为:
new start C1 new end Class.forName start C2 Class.forName end C3.class start C3.class end c1.newInstance start C3 c1.newInstance end
看结果,C3.class的调用不会自动的初始化该Class对象(调用static块)。为了使用Class而作的准备工做主要包括三个步骤:
Class对象做为Java类型体系的入口,如何获取实例成为第一个要解决的问题。
Class对象的获取主要有如下几种途径:
对于基本数据类型对于的包装器类,还提供了一个TYPE字段,指向对应的基本类型的Class对象。
基本类型 | TYPE类型 |
---|---|
boolean.class | Boolean.TYPE |
char.class | Char.TYPE |
byte.class | Byte.TYPE |
short.class | Short.TYPE |
int.class | Integer.TYPE |
long.class | Long.TYPE |
float.class | Float.TYPE |
double.class | Double.TYPE |
void.class | Void.TYPE |
Class对象存储了一个class的全部类型信息,当获取到Class对象后,便能经过API获取到全部信息。
在进入Class类型信息以前,须要简单的了解下几个反射的基类,以便更好的理解反射实现体系。
Class API基础主要是为反射API提供通用特性的接口或基类。因为其通用性,现统一介绍,在具体的API中将对其进行忽略。
AnnotatedElement为Java1.5新增接口,该接口表明程序中能够接受注解的程序元素,并提供统一的Annotation访问方式,赋予API经过反射获取Annotation的能力,当一个Annotation类型被定义为运行时后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement接口是全部注解元素(Class、Method、Field、Package和Constructor)的父接口,因此程序经过反射获取了某个类的AnnotatedElement对象以后,程序就能够调用该对象的下列方法来访问Annotation信息:
方法 | 含义 |
---|---|
<T extends Annotation> T getAnnotation(Class<T> annotationClass) | 返回程序元素上存在的、指定类型的注解,若是该类型注解不存在,则返回null |
Annotation[] getAnnotations() | 返回该程序元素上存在的全部注解 |
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass) | 判断该程序元素上是否包含指定类型的注解,存在则返回true,不然返回false |
Annotation[] getDeclaredAnnotations() | 返回直接存在于此元素上的全部注释。与此接口中的其余方法不一样,该方法将忽略继承的注释。(若是没有注释直接存在于此元素上,则返回长度为零的一个数组)该方法的调用者能够随意修改返回的数组;这不会对其余调用者返回的数组产生任何影响。 |
AnnotatedElement子类涵盖全部能够出现Annotation的地方,其中包括:
样例以下:
public class AnnotatedElementTest { public static void main(String... args){ System.out.println("getAnnotations:"); for (Annotation annotation : A.class.getAnnotations()){ System.out.println(annotation); } System.out.println(); System.out.println("getAnnotation:" + A.class.getAnnotation(TestAnn1.class)); System.out.println(); System.out.println("isAnnotationPresent:" + A.class.isAnnotationPresent(TestAnn1.class)); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnn1{ } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnn2{ } @TestAnn1 @TestAnn2 public class A{ } }
输出结果以下:
getAnnotations: @com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1() @com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn2() getAnnotation:@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1() isAnnotationPresent:true
Member用于标记反射中简单元素。
所涉及方法以下:
方法 | 含义 |
---|---|
getDeclaringClass | 元素所在类 |
getName | 元素名称 |
getModifiers | 元素修饰 |
isSynthetic | 是否为Synthetic,synthetic是由编译器引入的字段、方法、类或其余结构,主要用于JVM内部使用。 |
其子类主要包括:
AccessibleObject可访问对象,其对元素的可见性进行统一封装。同时实现AnnotatedElement接口,提供对Annotation元素的访问。
所涉及方法以下:
方法 | 含义 |
---|---|
isAccessible | 是否可访问 |
setAccessible | 从新访问性 |
其中AccessibleObject所涉及的子类主要包括:
AccessibleObject 对可见性提供了强大的支持,使咱们可以经过反射扩展访问限制,甚至能够对private成员进行访问。
样例代码以下:
public class TestBean { private String id; public String getId() { return id; } private void setId(String id) { this.id = id; } } public class AccessibleObjectBase { public static void main(String... args) throws Exception{ TestBean testBean = new TestBean(); // private方法, 不能直接调用 Method setId = TestBean.class.getDeclaredMethod("setId", String.class); System.out.println("setId:" + setId.isAccessible()); try { setId.invoke(testBean, "111"); }catch (Exception e){ System.out.println("private不能直接调用"); } setId.setAccessible(true); System.out.println("设置可访问:" + setId.isAccessible()); setId.invoke(testBean, "111"); System.out.println("设置可访问后,能够绕过private限制,进行调用,结果为:" + testBean.getId()); } }
输出结果以下:
setId:false private不能直接调用 设置可访问:true 设置可访问后,能够绕过private限制,进行调用,结果为:111
Executable表示可执行元素的一种封装,能够获取方法签名相关信息。
所涉及方法以下:
方法 | 含义 |
---|---|
getName | 获取名称 |
getModifiers | 获取修饰符 |
getTypeParameters | 获取类型参数(泛型) |
getParameterTypes | 获取参数列表 |
getParameterCount | 获取参数数量 |
getGenericParameterTypes | 获取参数类型 |
getExceptionTypes | 获取异常列表 |
getGenericExceptionTypes | 获取异常列表 |
锁涉及的子类主要有:
样例代码以下:
public class TestBean { private String id; public <T, R>TestBean(String id) throws IllegalArgumentException, NotImplementedException { this.id = id; } public String getId() { return id; } private void setId(String id) { this.id = id; } } public class ExecutableTest { public static void main(String... args) throws Exception{ for (Constructor constructor : TestBean.class.getConstructors()){ System.out.println("getName: " + constructor.getName()); System.out.println(); System.out.println("getModifiers: " + Modifier.toString(constructor.getModifiers())); System.out.println(); System.out.println("getTypeParameters:"); for (TypeVariable<Constructor> t : constructor.getTypeParameters()){ System.out.println("type var:" + t.getName()); } System.out.println(); System.out.println("getParameterCount:" + constructor.getParameterCount()); System.out.println(); System.out.println("getParameterTypes:"); for (Class cls : constructor.getParameterTypes()){ System.out.println(cls.getName()); } System.out.println(); System.out.println("getExceptionTypes:"); for (Class cls : constructor.getExceptionTypes()){ System.out.println(cls.getName()); } } } }
输出结果为:
getName: com.example.reflectdemo.reflectbase.TestBean getModifiers: public getTypeParameters: type var:T type var:R getParameterCount:1 getParameterTypes: java.lang.String getExceptionTypes: java.lang.IllegalArgumentException sun.reflect.generics.reflectiveObjects.NotImplementedException
整个反射机制存在着通用的命名规则,了解这些规则,能够大大减小理解方法的阻力。
getXXX和getDeclaredXXX, 二者主要区别在于获取元素的可见性不一样,通常状况下getXXX返回public类型的元素,而getDeclaredXXX获取全部的元素,其中包括private、protected、public和package。
Class自身信息包括类名、包名、父类以及实现的接口等。
Class类实现AnnotatedElement接口,以提供对注解的支持。除此之外,涉及方法以下:
方法 | 含义 |
---|---|
getName | 获取类名 |
getCanonicalName | 获得目标类的全名(包名+类名) |
getSimpleName | 等同于getCanonicalName |
getTypeParameters | 获取类型参数(泛型) |
getSuperclass | 获取父类 |
getPackage | 获取包信息 |
getInterfaces | 获取实现接口 |
getModifiers | 获取修饰符 |
isAnonymousClass | 是否匿名类 |
isLocalClass | 是否局部类 |
isMemberClass | 是否成员类 |
isEnum | 是否枚举 |
isInterface | 是不是接口 |
isArray | 是不是数组 |
getComponentType | 获取数组元素类型 |
isPrimitive | 是不是基本类型 |
isAnnotation | 是不是注解 |
getEnumConstants | 获取枚举全部类型 |
getClasses | 获取定义在该类中的public类型 |
getDeclaredClasses | 获取定义在该类中的类型 |
实例以下:
class Base<T> implements Callable<T> { @Override public T call() throws Exception { return null; } } public final class BaseClassInfo<T, R extends Runnable> extends Base<T> implements Runnable, Serializable { @Override public void run() { } public static void main(String... args){ Class<BaseClassInfo> cls = BaseClassInfo.class; System.out.println("getName:" + cls.getName()); System.out.println(); System.out.println("getCanonicalName:" + cls.getCanonicalName()); System.out.println(); System.out.println("getSimpleName:" + cls.getSimpleName()); System.out.println(); System.out.println("getSuperclass:" + cls.getSuperclass()); System.out.println(); System.out.println("getPackage:" + cls.getPackage()); System.out.println(); for (Class c : cls.getInterfaces()){ System.out.println("interface : " + c.getSimpleName()); } System.out.println(); for (TypeVariable<Class<BaseClassInfo>> typeVariable : cls.getTypeParameters()){ System.out.println("type var : " + typeVariable.getTypeName()); } System.out.println(); System.out.println("getModifiers:" + Modifier.toString(cls.getModifiers())); } }
输出结果为:
getName:com.example.reflectdemo.classdetail.BaseClassInfo getCanonicalName:com.example.reflectdemo.classdetail.BaseClassInfo getSimpleName:BaseClassInfo getSuperclass:class com.example.reflectdemo.classdetail.Base getPackage:package com.example.reflectdemo.classdetail interface : Runnable interface : Serializable type var : T type var : R getModifiers:public final
Class类型判断,实例以下:
public class ClassTypeTest { public static void main(String... args){ Runnable runnable = new Runnable() { @Override public void run() { printClassType(getClass()); } }; System.out.println("匿名内部类"); runnable.run(); class M implements Runnable{ @Override public void run() { printClassType(getClass()); } } System.out.println("方法内部类"); new M().run(); System.out.println("内部类"); new ClassTypeTest().new T().run(); System.out.println("静态内部类"); new S().run(); System.out.println("枚举"); printClassType(EnumTest.class); System.out.println("接口"); printClassType(Runnable.class); System.out.println("数组"); printClassType(int[].class); System.out.println("int"); printClassType(int.class); System.out.println("注解"); printClassType(AnnTest.class); } class T implements Runnable{ @Override public void run() { printClassType(getClass()); } } static class S implements Runnable{ @Override public void run() { printClassType(getClass()); } } enum EnumTest{ A, B, C } @interface AnnTest{ } private static void printClassType(Class cls){ System.out.println("Class:" + cls.getName()); System.out.println("isAnonymousClass:" + cls.isAnonymousClass()); System.out.println("isLocalClass:" + cls.isLocalClass()); System.out.println("isMemberClass:" + cls.isMemberClass()); System.out.println("isEnum:" + cls.isEnum()); System.out.println("isInterface:" + cls.isInterface()); System.out.println("isArray:" + cls.isArray()); System.out.println("isPrimitive:" + cls.isPrimitive()); System.out.println("isAnnotation:" + cls.isAnnotation()); if (cls.isEnum()){ System.out.println("getEnumConstants:"); for (Object o : cls.getEnumConstants()){ System.out.println(o); } } if (cls.isArray()){ System.out.println("getComponentType:" + cls.getComponentType()); } System.out.println(); } }
输出结果以下:
匿名内部类 Class:com.example.reflectdemo.classdetail.ClassTypeTest$1 isAnonymousClass:true isLocalClass:false isMemberClass:false isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 方法内部类 Class:com.example.reflectdemo.classdetail.ClassTypeTest$1M isAnonymousClass:false isLocalClass:true isMemberClass:false isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 内部类 Class:com.example.reflectdemo.classdetail.ClassTypeTest$T isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 静态内部类 Class:com.example.reflectdemo.classdetail.ClassTypeTest$S isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 枚举 Class:com.example.reflectdemo.classdetail.ClassTypeTest$EnumTest isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:true isInterface:false isArray:false isPrimitive:false isAnnotation:false getEnumConstants: A B C 接口 Class:java.lang.Runnable isAnonymousClass:false isLocalClass:false isMemberClass:false isEnum:false isInterface:true isArray:false isPrimitive:false isAnnotation:false 数组 Class:[I isAnonymousClass:false isLocalClass:false isMemberClass:false isEnum:false isInterface:false isArray:true isPrimitive:false isAnnotation:false getComponentType:int int Class:int isAnonymousClass:false isLocalClass:false isMemberClass:false isEnum:false isInterface:false isArray:false isPrimitive:true isAnnotation:false 注解 Class:com.example.reflectdemo.classdetail.ClassTypeTest$AnnTest isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:false isInterface:true isArray:false isPrimitive:false isAnnotation:true
内部类型样例以下:
public class InnerClassTest { public static void main(String... args){ System.out.println("getClasses"); for (Class cls : InnerClassTest.class.getClasses()){ System.out.println(cls.getName()); } } public interface I{ } public class A implements I{ } public class B implements I{ } }
输出结果以下:
getClasses com.example.reflectdemo.classdetail.InnerClassTest$B com.example.reflectdemo.classdetail.InnerClassTest$A com.example.reflectdemo.classdetail.InnerClassTest$I
对象实例化,主要经过Constructor实例完成,首先经过相关方法获取Constructor对象,而后进行实例化操做。
所涉及的方法以下:
方法 | 含义 |
---|---|
newInstance | 使用默认构造函数实例化对象 |
getConstructors | 获取public构造函数 |
getConstructor(Class<?>... parameterTypes) | 获取特定public构造函数 |
getDeclaredConstructors | 获取全部的构造函数 |
getDeclaredConstructor | 获取特定构造函数 |
实例化涉及的核心类为Constructor,Constructor继承自Executable,拥有AnnotatedElement、AccessibleObject、Executable等相关功能,其核心方法以下:
方法 | 含义 |
---|---|
newInstance | 调用构造函数,实例化对象 |
样例以下:
public class TestBean { private final Integer id; private final String name; public <T, R>TestBean(Integer id, String name) throws IllegalArgumentException, NotImplementedException { this.id = id; this.name = name; } @Override public String toString() { return "TestBean{" + "id=" + id + ", name='" + name + '\'' + '}'; } } public class ConstructorTest { public static void main(String... args) throws Exception{ for (Constructor constructor : TestBean.class.getConstructors()){ TestBean bean = (TestBean) constructor.newInstance(1, "Test"); System.out.println("newInstance:" + bean); } } }
输出结果为:
newInstance:TestBean{id=1, name='Test'}
对象属性是类型中最主要的信息之一,主要经过Field表示,首先经过相关方法获取Field实例,而后进行属性值操做。
所涉及的方法以下:
方法 | 含义 |
---|---|
getFields | 获取public字段 |
getField(String name) | 获取特定public字段 |
getDeclaredFields | 获取全部的的属性 |
getDeclaredField | 获取特定字段 |
Field继承自AccessibleObject实现Member接口,拥有AccessibleObject、AnnotatedElement、Member相关功能,其核心方法以下:
方法 | 含义 |
---|---|
isEnumConstant | 是否枚举常量 |
getType | 获取类型 |
get | 获取属性值 |
getBoolean | 获取boolean值 |
getByte | 获取byte值 |
getChar | 获取chat值 |
getShort | 获取short值 |
getInt | 获取int值 |
getLong | 获取long值 |
getFloat | 获取float值 |
getDouble | 获取double值 |
set | 设置属性值 |
setBoolean | 设置boolean值 |
setByte | 设置byte值 |
setChar | 设置char值 |
setShort | 设置short值 |
setInt | 设置int值 |
setLong | 设置long值 |
setFloat | 设置float值 |
setDouble | 设置double值 |
实例以下:
public enum EnumTest { A } public class FieldBean { private EnumTest aEnum; private String aString; private boolean aBoolean; private byte aByte; private char aChar; private short aShort; private int anInt; private long aLong; private float aFloat; private double aDouble; } public class FieldTest { public static void main(String... args) throws NoSuchFieldException, IllegalAccessException { FieldBean fieldBean = new FieldBean(); Field aEnum = getByName("aEnum"); Field aString = getByName("aString"); Field aBoolean = getByName("aBoolean"); Field aByte = getByName("aByte"); Field aChar = getByName("aChar"); Field aShort = getByName("aShort"); Field anInt = getByName("anInt"); Field aLong = getByName("aLong"); Field aFloat = getByName("aFloat"); Field aDouble = getByName("aDouble"); aEnum.set(fieldBean, EnumTest.A); System.out.println("isEnumConstant: " + aEnum.isEnumConstant()); System.out.println("set and get enum : " + aEnum.get(fieldBean)); aString.set(fieldBean, "Test"); System.out.println("set and get String : " + aString.get(fieldBean)); aBoolean.setBoolean(fieldBean, true); System.out.println("set and get Boolean : " + aBoolean.getBoolean(fieldBean)); aByte.setByte(fieldBean, (byte) 1); System.out.println("set and get Byte : " + aByte.getByte(fieldBean)); aChar.setChar(fieldBean, 'a'); System.out.println("set and get Char : " + aChar.getChar(fieldBean)); aShort.setShort(fieldBean, (short) 1); System.out.println("set and get Short : " + aShort.getShort(fieldBean)); anInt.setInt(fieldBean, 1); System.out.println("set and get Int : " + anInt.getInt(fieldBean)); aLong.setLong(fieldBean, 1L); System.out.println("set and get Long : " + aLong.getLong(fieldBean)); aFloat.setFloat(fieldBean, 1f); System.out.println("set and get Float : " + aLong.getFloat(fieldBean)); aDouble.setDouble(fieldBean, 1.1); System.out.println("set and get Double : " + aLong.getDouble(fieldBean)); } private static Field getByName(String name) throws NoSuchFieldException { Field field = FieldBean.class.getDeclaredField(name); field.setAccessible(true); return field; } }
类型中的方法经过Method表示,首先经过相关方法获取Method实现,而后经过反射执行方法。
所涉及的方法以下:
方法 | 含义 |
---|---|
getMethods | 获取public方法 |
getMethod(String name, Class<?>... parameterTypes) | 获取特定public方法 |
getDeclaredMethods | 获取全部方法 |
getDeclaredMethod | 获取特定方法 |
Method继承自Executable,拥有AnnotatedElement、AccessibleObject、Executable等相关功能,其核心方法以下:
方法 | 含义 |
---|---|
getReturnType | 获取方法返回类型 |
invoke | 调用方法 |
isBridge | 是否为桥接方法。桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。咱们能够经过Method.isBridge()方法来判断一个方法是不是桥接方法。 |
isDefault | 是否为默认方法 |
实例以下:
public interface SayHi { String get(); default void hi(){ System.out.println("Hi " + get()); } } public class MethodBean implements Function<String, String>, SayHi { private final String name; public MethodBean(String name) { this.name = name; } @Override public String get() { return "Hi " + name; } @Override public String apply(String s) { return s + name; } } public class MethodTest { public static void main(String... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method strMethod = MethodBean.class.getDeclaredMethod("apply", String.class); Method objMethod = MethodBean.class.getDeclaredMethod("apply", Object.class); Method hiMethod = SayHi.class.getDeclaredMethod("hi"); MethodBean methodBean = new MethodBean("张三"); System.out.println("Return Type:"); System.out.println("getMethod(String):" + strMethod.getReturnType()); System.out.println("getMethod(Object):" + objMethod.getReturnType()); System.out.println("hi():" + hiMethod.getReturnType()); System.out.println(); System.out.println("isBridge:"); System.out.println("getMethod(String):" + strMethod.isBridge()); System.out.println("getMethod(Object):" + objMethod.isBridge()); System.out.println("hi():" + hiMethod.isBridge()); System.out.println(); System.out.println("isDefault:"); System.out.println("getMethod(String):" + strMethod.isDefault()); System.out.println("getMethod(Object):" + objMethod.isDefault()); System.out.println("hi():" + hiMethod.isDefault()); System.out.println(); System.out.println("invoke:"); System.out.println("invoke(String):" + strMethod.invoke(methodBean, "Test")); System.out.println("invoke(Object):" + objMethod.invoke(methodBean, "Test")); System.out.println("hi():" + hiMethod.invoke(methodBean)); } }
输出结果:
Return Type: getMethod(String):class java.lang.String getMethod(Object):class java.lang.Object hi():void isBridge: getMethod(String):false getMethod(Object):true hi():false isDefault: getMethod(String):false getMethod(Object):false hi():true invoke: invoke(String):Test张三 invoke(Object):Test张三 Hi Hi 张三 hi():null
除上述核心方法外,Class对象提供了一些使用方法。
所涉及方法以下:
方法 | 含义 |
---|---|
isInstance | 判断某对象是不是该类的实例 |
isAssignableFrom | 断定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是不是其超类或超接口。若是是则返回 true;不然返回 false。 |
getClassLoader | 获取加载当前类的ClassLoader |
getResourceAsStream | 根据该ClassLoader加载资源 |
getResource | 根据该ClassLoader加载资源 |
public class Task implements Runnable{ @Override public void run() { } } public class OtherTest { public static void main(String...args){ Task task = new Task(); System.out.println("Runnable isInstance Task:" + Runnable.class.isInstance(task)); System.out.println("Task isInstance Task:" + Task.class.isInstance(task)); System.out.println("Task isAssignableFrom Task:" + Task.class.isAssignableFrom(Task.class)); System.out.println("Runnable isAssignableFrom Task :" + Runnable.class.isAssignableFrom(Task.class)); } }
输出结果:
Runnable isInstance Task:true Task isInstance Task:true Task isAssignableFrom Task:true Runnable isAssignableFrom Task :true
代理是基本的设计模式之一,它是咱们为了提供额外的或不一样的操做,而插入的用来代替“实际”对象的对象。这些操做一般与“实际”对象通讯,所以代理一般充当中间人的角色。
例如,咱们已有一个Handler接口,和一个实现类HandlerImpl,现须要对其进行性能统计,使用代理模式,代码以下:
/** * handler接口 */ public interface Handler { /** * 数据处理 * @param data */ void handle(String data); } /** * Handler 实现 */ public class HandlerImpl implements Handler{ @Override public void handle(String data) { try { TimeUnit.MILLISECONDS.sleep(100); System.out.println(data); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * Handler代理<br /> * 实现Handler接口,记录耗时状况,并将请求发送给目标对象 */ public class HandlerProxy implements Handler{ private final Handler handler; public HandlerProxy(Handler handler) { this.handler = handler; } @Override public void handle(String data) { long start = System.currentTimeMillis(); this.handler.handle(data); long end = System.currentTimeMillis(); System.out.println("cost " + (end - start) + " ms"); } } public static void main(String... args){ Handler handler = new HandlerImpl(); Handler proxy = new HandlerProxy(handler); proxy.handle("Test"); }
采用代理模式,比较优雅的解决了该问题,但若是Handler接口存在多个方法,而且须要对全部方法进行性能监控,那HandlerProxy的复杂性将会提升。
Java动态代理比代理更进一步,由于它能够动态的建立代理并动态的处理对所代理方法的调用。在动态代理上所作的全部调用都会被重定向到单一的调用处理器上。
InvocationHandler 是由动态代理处理器实现的接口,对代理对象的方法调用,会路由到该处理器上进行统一处理。
其只有一个核心方法:
/** * proxy : 代理对象 * method : 调用方法 * args : 调用方法参数 **/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Proxy 用于生成代理对象。
其核心方法为:
/** * 获取代理类<br /> * loader : 类加载器 * interfaces: 类实现的接口 * */ Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces); /* * 生成代理对象<br /> * loader : 类加载器 * interfaces : 类实现的接口 * h : 动态代理回调 */ Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h); /* * 判断是否为代理类<br /> * * cl : 待判断类 */ public static boolean isProxyClass(Class<?> cl); /* * 获取代理对象的InvocationHandler <br /> * * proxy : 代理对象 */ InvocationHandler getInvocationHandler(Object proxy);
对于以前的性能监控,使用Java动态代理怎么实现?
/** * 定义代理方法回调处理器 */ public class CostInvocationHandler implements InvocationHandler { // 目标对象 private final Object target; public CostInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("call method " + method + " ,args " + args); long start = System.currentTimeMillis(); try { // 将请求转发给目标对象 return method.invoke(this.target, args); }finally { long end = System.currentTimeMillis(); System.out.println("cost " + (end - start) + "ms"); } } } public static void main(String... args){ Handler handler = new HandlerImpl(); CostInvocationHandler invocationHandler = new CostInvocationHandler(handler); Class cls = Proxy.getProxyClass(DHandlerMain.class.getClassLoader(), Handler.class); Handler proxy = (Handler) Proxy.newProxyInstance(DHandlerMain.class.getClassLoader(), new Class[]{Handler.class}, invocationHandler); System.out.println("invoke method"); proxy.handle("Test"); System.out.println("isProxyClass: " + Proxy.isProxyClass(cls)); System.out.println("getInvocationHandler: " + (invocationHandler == Proxy.getInvocationHandler(proxy))); }
SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 目前有很多框架用它来作服务的扩展发现,它是一种动态替换发现的机制。
具体用法是在JAR包的"META-INF/services/"目录下创建一个文件,文件名是接口的全限定名,文件的内容能够有多行,每行都是该接口对应的具体实现类的全限定名。而后使用 ServiceLoader.load(Interface.class) 对插件进行加载。
假定,现有个场景,须要对消息进行处理,但消息处理器的实现须要放开,及能够动态的对处理器进行加载,当有新消息到达时,依次调用处理器对消息进行处理,让咱们结合SPI和反射构造一个简单的Plugin系统。
首先咱们须要一个插件接口和若干个实现类:
/** * 插件接口 */ public interface Handler { void handle(String msg); } /** * 实现1 */ public class Handler1 implements Handler{ @Override public void handle(String msg) { System.out.println("Handler1:" + msg); } } /** * 实现2 */ public class Handler2 implements Handler{ @Override public void handle(String msg) { System.out.println("Handler2:" + msg); } }
而后,咱们添加SPI配置,及在META-INF/services/com.example.reflectdemo.plugin.Handler添加配置信息:
com.example.reflectdemo.plugin.Handler1 com.example.reflectdemo.plugin.Handler2
其次,咱们实现DispatcherInvocationHandler类继承自InvocationHandler接口,将方法调用分发给目标对象。
/** * 分发处理器<br /> * 将请求挨个转发给目标对象 */ public class DispatcherInvocationHandler implements InvocationHandler { // 目标对象集合 private final List<Object> targets; public DispatcherInvocationHandler(List<Object> targets) { this.targets = targets; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for (Object target : targets){ // 将请求转发给目标对象 method.invoke(target, args); } return null; } }
实现主流程,经过SPI加装插件,将插件做为转发对象实例化DispatcherInvocationHandler,在经过Proxy构建动态代理对象,最后调用handle方法进行业务处理。
public static void main(String... args){ // 使用SPI加载插件 ServiceLoader<Handler> serviceLoader = ServiceLoader.load(Handler.class); List<Object> handlers = new ArrayList<>(); Iterator<Handler> handlerIterator = serviceLoader.iterator(); while (handlerIterator.hasNext()){ Handler handler = handlerIterator.next(); handlers.add(handler); } // 将加载的插件组装成InvocationHandler,以进行分发处理 DispatcherInvocationHandler invocationHandler = new DispatcherInvocationHandler(handlers); // 生成代理对象 Handler proxy = (Handler) Proxy.newProxyInstance(HandlerMain.class.getClassLoader(), new Class[]{Handler.class}, invocationHandler); // 调用handle方法 proxy.handle("Test"); }
运行结果以下:
Handler1:Test Handler2:Test
Java类型系统、反射、动态代理,做为Java的高级应用,大量用于各大框架中。对其的掌握有助于加深对框架的理解。