java反射教程(最全)

来源:互联网 发布:网络覆盖公司 编辑:程序博客网 时间:2024/05/20 12:49

1.反射


在软件中,反射的概念意味着在运行时检查,分析和修改其它代码的能力。例如想象一个应用程序把输入文件当做包含的源代码(我们不关心源代码到底是什么)。这个应用程序的目的是统计在每个被传递的类中包含的芳芳的数量。这能通过使用反射的分析代码和统计它正真的方法,忽略其它类型的元素,像属性,接口等等,并且按照类别分组。


纯粹来说,这个例子不是真正的反射,因为代码没有必要的在运行时被分析并且这个任务在任何阶段能被做,但是它也能在运行时被做然后我们就能真的考虑反射了。


另一个例子将是一个应用程序分析被给的类的内容并且执行注解@Test。这的确是Junit做的事情;而且它使用的是反射。


2.介绍java中的反射


在Java中,在运行时它可能检查域,类,方法,注解,接口等等。你不需要知道类或者方法如何被调用,也不需要参数,在运行时所有能被获取的都用反射。它也可能初始化新类,创建新实例并且执行他们的方法,也都是用反射。


java的反射是本世纪初通过反射API被提出来的。类Class包含所有反射的相关方法,这些方法能被应用到类和对象,像这些是允许程序获取类名,获取类的公共方法等等。其它重要的类是Method,Field和Type,这些包含了我们将接下来看的指定的反射方法。


尽管反射在许多场景下是非常有用的,但是它不应当用来做每件事情。如果一些操作在没有使用反射的情况下能被执行,那么我们应当使用它。这是一些原因:

通过使用反射会影响性能,因为所有的编译最佳化不能被应用:反射在运行时被解决而不是编译阶段。


由于使用反射,安全缺陷必须考虑,因为它也许不能运行在安全的上下文中,像Applets。


另一个重要的缺点是对代码维护提出的好处。如果你的代码大量使用反射那么它将维护起来更困难。类和方法在代码中不是直接被暴露的并且也许非常动态以至于它会在改变参数数量方面变的困难,如果代码调用这个方法被调用通过反射。


当许多反射被呈现时自动反射器或者分析代码工具也许会有麻烦。


3.用例



尽管有上面那些限制,但是反射在java中还是非常有用的工具,在下面情形下可考虑。


通常来说,反射能被用于观察和修改运行时程序的行为。这是最多情况下使用的案例列表:


IDEs为了提供解决自动完成特性,动态类型,层级结构等会严重的利用反射。例如,像Eclipse或者PHP Storm的IDEs提供一种机制去动态的回收想要回收的参数给这个在被给的实例中以get开头的被给的方法或者公共方法列表。所有那些都是用反射实现。

Debuggers是用反射动态的检查正在被执行的代码。

像Junit或者Mockito的测试工具是用反射,为了调用期望的方法,包含了指定语法或者模仿指定类,接口和方法。

依赖注入框架是用反射在应用程序上下文的运行时和初始化注入beans和properties。

像PMD或者Findbugs这样的代码分析工具使用反射为了分析冲突的代码,这种违背是当前被配置的。

扩展工具利用动态的代码也许也使用反射。


这这个教程中,我们将看几个在java中使用反射的例子。我们将看到怎么样从被给的实例,在不知道这个实例的类的类型的情况下获取所有方法并且我们将依赖他们的语法调用不同的方法。


我们不仅仅是展示其它教程,但是我们将一步步的向前验证当用generics,注解,数组,集合以及其它对象类型使用反射时怎么样处理。最后我们将解释在java 8中关于这个话题的主要的新特性。


4.反射组件和原理


为了开始使用java反射写代码我们必须解释一些相关的概念。


    

java中的接口是应用程序使用的一种契约。接口包含一个方法列表,这些方法被暴露并且必须被实现这个接口的子类实现。接口不能被初始化。从java8开始,他们包含了默认的方法实现景观这不是经常使用。

Class是一系列方法的实现和一系列属性的容器。它能被初始化。

Object是被给类的一个实例(PS:作者的这个描述好像不怎么通顺)。

Method是一些功能的实现。他们返回输出类型和输入参数。

Field是类的属性

Enums是包含了一组预先定义的常量的元素。

Private是仅仅在本类中可见的元素并且不能从外部访问。它会是一个方法,一个域...

Static元素是属于类并且不是一个指定的实例。静态元素可能是通过所有被给类的实例访问的域,方法,这些在不需要初始化类的情况下可以被调用。当你使用反射时,这是非常有趣的,因为调用一个静态方法和调用一个非静态方法是不同的,非静态方法你需要一个类的实例执行它。

Annotation是代码元数据的自我描述。

Collection是一组元素,可能是List,Map,Queue等等。

Array是包含固定值数量的一个对象。它的长度是固定的,并且在创建时被指定。

Dynamic proxy是在运行时一组列表的实现。他们使用类java.lang.reflect.Proxy。我们将看到更详细的描述在下一章。

Class loader是负责载入被给的类名的一个对象。在java中,每个类提供方法去取回这个类载入器:Class.getClassLoader()。

Generics在java5中被介绍。他们提供编译时安全通过表明一个集合的类型或者子类型将用于什么。例如使用泛型你能保护一个应用程序在运行时使用包含字符串的列表尝试添加Double到列表中。


那些组件的不同时重要的以至于在他们内部使用反射。调用私有方法和公共方法是不同的;获取一个注解名字或者一个接口也是不同的。我们将在下一章给出所有的例子。


5.类


java中的每件事情都是关于类,还有反射的。类当我们讨论反射的开始点。类java.lang.Class包含几个方法,这些方法允许程序员去获取在运行时的关于类和对象的信息。


为了从一个单例中获取类信息我们能写(在这个例子中,是String类):


Class<? extends String> stringGetClass = stringer.getClass();


或者在没有初始化情况直接从类名:

Class<String> stringclass = String.class;


或者使用java.lang.Class.forName(String)方法:

Class.forName("java.lang.String");


从一个类对象中我们能获取所有种类的信息,像被定义的方法,构造器,可见域,注解,类型...在这个教程中所有那些将在下面章节被解释。


检查给定类的属性也是可能的,像例如如果一个类是原始的或者一个实例:


stringGetClass.isInstance("dani");

stringGetClass.isPrimitive();


使用方法java.lang.Class.newInstance()传递正确的参数创建给定类的实例也是可能的:


String newInstanceStringClass = stringclass.newInstance();

String otherInstance = (String)Class.forName("java.lang.String").newInstance();


当类包含一个公共默认的构造器或者一个没有参数的构造器时,java.lang.Class.newInstance()方法能被使用,如果这不是那样的,这个方法不能被使用。在这个例子中java.lang.Class.newInstance()方法不能被使用来在运行时解决获取一个合适的构造器和使用这个带有参数的构造器创建一个它所期望的实例。我们将在下一章看到相关的构造器。


6.接口


接口是不能被初始化并且包含了应当有他们的子类实现的暴露的方法的元素。与反射有关的没有相关的接口。


接口能像一个类使用他们的描述的名字一样被访问。所有方法对类是有效的,对接口也是。这是个例子怎么样在运行时访问接口类的信息。


System.out.println( "interface name: " + InterfaceExample.class.getName() );


假设InterfaceExample原始是一个接口。


在类和接口之间一个明显的不同是接口不能使用反射通过newInstance()方法被实例化:

//cannot be instantiated: java.lang.InstantiationException

InterfaceExample.class.newInstance();


上面的代码段将在运行时抛出异常。在编译时没有错误出现。


7.枚举


枚举是java类型中的特殊类型,它允许变量被设置为常量。那些常量在枚举中被预先定义:


enum ExampleEnum

{

ONE, TWO, THREE, FOUR

};


java包含几个枚举特别方法:


java.lang.Class.isEnum():如果元素是枚举类型,则返回true,否则返回false

java.lang.Class.getEnumConstants():获取给定元素(它是枚举)的所有常量。假如元素不是枚举,则将抛出异常。

java.lang.reflect.Field.isEnumConstant():假如使用的域是个枚举常量,则返回true,否则返回false。仅仅用于域。


我们将看到一个怎么样使用相关反射的主要的枚举方法的例子。首先我们创建一个枚举实例:

ExampleEnum value = ExampleEnum.FOUR;


如果元素师一个枚举,我们能使用isEnum()方法检查:


System.out.println( "isEnum " + value.getClass().isEnum() );


为了获取所有枚举常量,我们能像下面使用方法getEnumConstants()做一些事情:

ExampleEnum[] enumConstants = value.getClass().getEnumConstants();

for( ExampleEnum exampleEnum : enumConstants )

{

        System.out.println( "enum constant " + exampleEnum );

}


最后,我们能检查怎么样使用域的相关的isEnumConstants()方法。首先我们从给定类中获取所有被定义的域(我们将在下一章节中看到所有有关反射功能更详细的描述)接着如果这个域是一个枚举常量或者不是,我们能检查:

Field[] flds = value.getClass().getDeclaredFields();

for( Field f : flds )

{

// check for each field if it is an enum constant or not

System.out.println( f.getName() + " " + f.isEnumConstant() );

}


所有代码的输出将是下面信息:


isEnum true

enum constant ONE

enum constant TWO

enum constant THREE

enum constant FOUR

ONE true

TWO true

THREE true

FOUR true

ENUM$VALUES false


字符串ENUM$VALUES false是内部枚举值域。想要了解更多的枚举信息,请访问

http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html.


8.原始类型


在java中,有一组类型被持有由于它的性质和行为:当我们在谈论反射至,原始类型像int,float,double等几乎像任何其它类一样能被访问和使用。这是一组当我们使用原始类型工作时怎么样使用反射的例子:


跟其它的非原始类型一样,从一个原始类型中获得一个类对象是可能的:


Class<Integer> intClass = int.class;


但是不能使用反射给原始类型创建实例:

Integer intInstance = intClass.newInstance();


使用方法java.lang.Class.isPrimitive()可以检查是否给定类属于原始类型:


System.out.println( "is primitive: " + intClass.isPrimitive() );


在这个例子中一个类型异常java.lang.InstantiationException将被抛出。


9.域


Class域使用反射能在运行时被持有。类提供几个方法在运行时来访问他们的域。最重要的几个是:

java.lang.Class.getDeclaredFields():它返回这个类的所有被定义的域数组。它也返回所有的私有域。

java.lang.Class.getFields():它返回这个类的所有可访问域的数组。

java.lang.Class.getField(String):它通过传递一个名称参数来返回一个域。如果域不存在或者不可访问,它会抛出一个异常。


这是一个使用这些功能的类:

String stringer = "this is a String called stringer";Class<? extends String> stringGetClass = stringer.getClass();Class<String> stringclass = String.class;Field[] fields = stringclass.getDeclaredFields();for( Field field : fields ){System.out.println( "*************************" );System.out.println( "Name: " + field.getName() );System.out.println( "Type: " + field.getType() );// valuesif( field.isAccessible() ){System.out.println( "Get: " + field.get( stringer ) );// depending on the type we can access the fields using these methods// System.out.println( "Get boolean: " + field.getBoolean( stringer ) );// System.out.println( "Get short: " + field.getShort( stringer ) );// ...}System.out.println( "Modifiers:" + field.getModifiers() );System.out.println( "isAccesible: " + field.isAccessible() );}// stringclass.getField( "hashCode" );//exceptionField fieldHashCode = stringclass.getDeclaredField( "hash" );// all fields can be // accessed this way// fieldHashCode.get( stringer ); // this produces an java.lang.IllegalAccessException// we change the visibilityfieldHashCode.setAccessible( true );// and we can access itObject value = fieldHashCode.get( stringer );int valueInt = fieldHashCode.getInt( stringer );System.out.println( value );System.out.println( valueInt );


在这个上面的片段中你能看到,Field包含几个方法获取给定域的值,像get()或者指定类型的getInt()。我们也能看到在代码中我们能改变一个给定域的可见性通过使用方法setAccessible()。


这也不总是可能的,并且在特定情况和环境下也许被保护。然后通过反射允许我们来做一个私有域访问并且访问它的值和属性。这在测试框架像Mockito或者PowerMock中时非常有用的。


输出或者程序将是:

    

*************************Name: valueType: class [CModifiers:18isAccesible: false*************************Name: hashType: intModifiers:2isAccesible: false*************************Name: serialVersionUIDType: longModifiers:26isAccesible: false*************************Name: serialPersistentFieldsType: class [Ljava.io.ObjectStreamField;Modifiers:26isAccesible: false*************************Name: CASE_INSENSITIVE_ORDERType: interface java.util.ComparatorModifiers:25isAccesible: false00


10.方法


为了获取给定类的所有可见方法,我们能做下面的事情:

Class<String> stringclass = String.class;

Method[] methods = stringclass.getMethods();


使用方法java.lang.Class.getMethods()给定类所有可见或者可访问的方法被获取。我们也能获取一个指定方法使用它的名字和它期望接收的参数类型,下面一个例子:


Method methodIndexOf = stringclass.getMethod( "indexOf", String.class );


对于一个给定的方法(java.lang.reflect.Method类型的实例),我们能访问所有它的属性。下面的片段展示了一组它们像名字,默认值,返回类型,修改,参数,参数类型或者异常,我们也能检查一个方法是否是可访问的:

// All methods for the String classfor( Method method : methods ){System.out.println( "****************************************************" );System.out.println( "name: " + method.getName() );System.out.println( "defaultValue: " + method.getDefaultValue() );System.out.println( "generic return type: " + method.getGenericReturnType() );System.out.println( "return type: " + method.getReturnType() );System.out.println( "modifiers: " + method.getModifiers() );// ParametersParameter[] parameters = method.getParameters();System.out.println( parameters.length + " parameters:" );// also method.getParameterCount() is possiblefor( Parameter parameter : parameters ){System.out.println( "parameter name: " + parameter.getName() );System.out.println( "parameter type: " + parameter.getType() );}Class<?>[] parameterTypes = method.getParameterTypes();System.out.println( parameterTypes.length + " parameters:" );for( Class<?> parameterType : parameterTypes ){System.out.println( "parameter type name: " + parameterType.getName() );}// ExceptionsClass<?>[] exceptionTypes = method.getExceptionTypes();System.out.println( exceptionTypes.length + " exception types: " );for( Class<?> exceptionType : exceptionTypes ){System.out.println( "exception name " + exceptionType.getName() );}System.out.println( "is accesible: " + method.isAccessible() );System.out.println( "is varArgs: " + method.isVarArgs() );}


给指定的对象传递我们想传递的参数实例化方法也是可以的,我们应当确保参数的数量和类型是正确的:


Object indexOf = methodIndexOf.invoke( stringer, "called" );


这个最后的特性是非常有趣的,当我们想要在运行时在特别的环境执行特定方法。还有在为动态代理创建回调句柄也是非常有用的,我们将在教程的末尾看到这个点。


11.构造器


构造器也能通过反射使用。像其它的类方法一样他们能在运行时被获取,一些属性能被分析,还有检查可访问性,参数数量,它们的类型等。


为了从一个类中获取所有可见构造器,我们能做像:

// get all visible constructorsConstructor<?>[] constructors = stringGetClass.getConstructors();


在上面的片段中,我们获取所有可见的构造器。如果我们想得到包含私有构造器的所有构造器,我们能做像:

//all constructorsConstructor<?>[] declaredConstructors =   stringclass.getDeclaredConstructors();


用下面的方式可以获取生成的关于构造器的像参数,类型,名字,可见性,相关注解等信息:

for( Constructor<?> constructor : constructors ){int numberParams = constructor.getParameterCount() ;System.out.println( "constructor " + constructor.getName() );System.out.println( "number of arguments " + numberParams);// public, private, etc.int modifiersConstructor = constructor.getModifiers();System.out.println( "modifiers " + modifiersConstructor );// array of parameters, more info in the methods sectionParameter[] parameters = constructor.getParameters();// annotations array, more info in the annotations sectionAnnotation[] annotations = constructor.getAnnotations();}


构造器也能被用于创建实例。为了访问私有或者不可见构造器,这也许是非常有用的。这应当仅仅在非常特殊的环境下才被做并且依赖于操作系统的正在运行的应用程序也许不工作了,由于在教程开始出被解释的安全原因。


为了使用一个特定构造器创建一个类的实例,我们能做下面的事情:

// can be used to create new instances (no params in this case)String danibuizaString = (String)constructor.newInstance(  );


在必须考虑参数数量和它们的类型上,应当匹配构造器实例。还有构造器的可访问性必须设置为true为了调用它(如果它不是可访问的)。当我们操作给定类的方法和域的时候这可能用相同的方式被做。


12.Getters和Setters(我这里就叫读取器和设置器吧)


读取器和设置器与其它类在类内部的方法是不同的。主要的不同是他们是一个标准的方式去访问私有域。


这是两个描述:

读取器被用来取得一个类内部的私有域的值。它的名字以get开始,并且以属性名的驼峰式结尾。他们不获取任何参数并且他们返回相同类型的属性。他们是公有的。

设置器被用于修改一个类内部的私有域的值。它的名字以set开始,并且以属性名的驼峰式结尾。它们接收一个相同属性类型的参数,而且他们不会返回任何值,他们是公共的。


例如,对于下面的私有属性private int count;我们能设置getter和setter方法:

public int getCount(){return this.count;}public void setCount(int count){this.count = count;}


下面那些标准我们能使用反射在运行时访问(读和修改)一个类通过getter和setter暴露的所有私有属性。这个机制被用于几个呗知道的库,像Spring Framework或者Hibernate,他们希望类使用这种方式暴露它们的属性。


这是一个如何使用反射使用getters和setters的例子:

Car car = new Car( "vw touran", "2010", "12000" );Method[] methods = car.getClass().getDeclaredMethods();// all getters, original valuesfor( Method method : methods ){if( method.getName().startsWith( "get" ) ){System.out.println( method.invoke( car ) );}}// setting valuesfor( Method method : methods ){if( method.getName().startsWith( "set" ) ){method.invoke( car, "destroyed" );}}// get new valuesfor( Method method : methods ){if( method.getName().startsWith( "get" ) ){System.out.println( method.invoke( car ) );}}


我们的类Car是下面的样子:

public class Car{private String name;private Object price;private Object year;public Car( String name, String year, String price ){this.name = name;this.price = price;this.year = year;}public String getName(){return name;}public void setName( String name ){this.name = name;}public Object getPrice(){return price;}public void setPrice( Object price ){this.price = price;}public Object getYear(){return year;}public void setYear( Object year ){this.year = year;}}


输出会是:

vw touran201012000destroyeddestroyeddestroyed


13.静态元素


静态类,方法和域行为与实例完全不同。主要原因是他们不需要在被调用以前被实例化或者被创建。他们能在没有初始化以前被使用。


这个事实是改变每件事:在没有初始化它的容器类情况下静态方法被调用,静态类的域是有状态的(因此线程安全),创建单例和工厂静态元素是非常有用的...总体来说,在java中静态元素是非常重要的机制。


在这一章我们将展示关于反射在静态和实例元素之间主要的不同:怎么样在运行时创建静态元素并且怎么样调用它们。


例如,下面的静态内部类:

public class StaticReflection{   static class StaticExample   {       int counter;   }...


为了获取静态内部类我们有下面的建议:

// 1 access static classSystem.out.println( "directly " + StaticExample.class.getName() );//2 using for name directly throws an exceptionClass<?> forname = Class.forName("com.danibuiza.javacodegeeks.reflection.StaticReflection.StaticExample" );//3 using $ would work but is not that nice    Class<?> forname = Class.forName("com.danibuiza.javacodegeeks.reflection.StaticReflection$StaticExample" );// 4 another way iterating through all classes declared inside this classClass<?>[] classes = StaticReflection.class.getDeclaredClasses();for( Class<?> class1 : classes ){System.out.println( "iterating through declared classes " + class1.getName() );}


主要的不同是类被包含在另一个类中;除了使用内联类,反射也做不了什么事情。


为了从一个类中得到静态方法,跟访问实例是没有什么不同的(这个也应用与域):

// access static methods in the same way as instance onesMethod mathMethod = Math.class.getDeclaredMethod( "round", double.class );


为了调用静态方法或者域,我们不需要创建或者指定类实例,因为方法(或者域)属于类自己,而不是单个的实例:

// methods: object instance passed can be null since method is staticObject result = mathMethod.invoke( null, new Double( 12.4 ) );// static field access, instance can be nullField counterField = Counter.class.getDeclaredField( "counter" );System.out.println( counterField.get( null ) );


14.数组


这个类java.lang.reflect.Array为句柄arrays提供几个功能;它包含多个静态反射的方法:

java.lang.reflect.Array.newInstance(Class,int):创建一个实例,第一个参数是数组类型,第二个参数是数组长度。它跟java.lang.Class的名字是相似的,除了它包含参数允许程序员设置数组类型和它的长度。

java.lang.reflect.Array.set(Object, int, Object):设置给定数组的元素(传递索引)用object作为传递参数。

java.lang.reflect.Array.getLength(Object):返回数组的整数长度

java.lang.reflect.Array.get(Object, int):返回指定数组下标的元素。返回一个对象

java.lang.reflect.Array.getInt(Object, int):与原始类型int相同的方法。返回一个整数。那些方法对所有的原始类型是有效的。


这是一个我们如何使用所有那些方法的例子:

// using the Array class it is possible to create new arrays passing the type and the length via reflectionString[] strArrayOne = (String[])Array.newInstance( String.class, 10 );// it contains utility methods for setting valuesArray.set( strArrayOne, 0, "member0" );Array.set( strArrayOne, 1, "member1" );Array.set( strArrayOne, 9, "member9" );// and for getting values as wellSystem.out.println( "strArrayOne[0] : " + Array.get( strArrayOne, 0 ) );System.out.println( "strArrayOne[1] : " + Array.get( strArrayOne, 1 ) );System.out.println( "strArrayOne[3] (not initialized) : " + Array.get( strArrayOne, 3 ) );System.out.println( "strArrayOne[9] : " + Array.get( strArrayOne, 9 ) );// also methods to retrieve the lenght of the arraySystem.out.println( "lenght strArrayOne: " + Array.getLength( strArrayOne ) );// primitive types work as wellint[] intArrayOne = (int[])Array.newInstance( int.class, 10 );Array.set( intArrayOne, 0, 1 );Array.set( intArrayOne, 1, 2 );Array.set( intArrayOne, 9, 10 );// and specific getters and setters for primitive typesfor( int i = 0; i < Array.getLength( intArrayOne ); ++i ){System.out.println( "intArrayOne[" + i + "] : " + Array.getInt( intArrayOne, i ) );}// retrieve the class from an instanceClass<String[]> stringArrayClassUsingInstance = String[].class;System.out.println( "stringArrayClassUsingInstance is array: " + stringArrayClassUsingInstance.isArray() );// using class for name and passing [IClass<?> intArrayUsingClassForName = Class.forName( "[I" );System.out.println( "intArrayUsingClassForName is array: " + intArrayUsingClassForName.isArray() );// or [Ljava.lang.StringClass<?> stringArrayClassUsingClassForName = Class.forName( "[Ljava.lang.String;" );System.out.println( "stringArrayClassUsingClassForName is array: "+ stringArrayClassUsingClassForName.isArray() );// this has no much sense in my opinion since we are creating an array at runtime and// getting the class to create a new one...Class<? extends Object> stringArrayClassUsingDoubleLoop = Array.newInstance( String.class, 0 ).getClass();System.out.println( "stringArrayClassUsingClassForName is array: " + stringArrayClassUsingDoubleLoop.isArray() );

  

上面程序的输出:

stringArrayClassUsingInstance is array: trueintArrayUsingClassForName is array: truestringArrayClassUsingClassForName is array: truestringArrayClassUsingClassForName is array: true


15.集合


集合没有许多可标记的明确的与反射相关的特点。这是一个我们如何能持有集合基础元素的例子。正如已经说的,对任何其它java类型有许多不同。


下面的方法打印一个集合的所有元素的所有类名,如果会提前检查传递的元素是否是一个集合实例:

private static void reflectionCollections( Object ref ){//check is collectionif( ref instanceof Collection ){System.out.println( "A collection:  " + ref.getClass().getName() );@SuppressWarnings( "rawtypes" )// not niceIterator items = ( (Collection)ref ).iterator();while( items != null && items.hasNext() ){Object item = items.next();System.out.println( "Element of the collection:  " + item.getClass().getName() );}}else{System.out.println( "Not a collection:  " + ref.getClass().getName() );}}


在代码展示中,反射仅仅被用于检查被传递的实例类型参数并且获取元素的类名在集合内部。我们能调用这个方法在下面的方法中使用不同的元素(一些集合基于,一些不是):

Map<String, String> map = new HashMap<String, String>();map.put( "1", "a" );map.put( "2", "b" );map.put( "3", "c" );map.put( "4", "d" );reflectionCollections( map );reflectionCollections( map.keySet() );reflectionCollections( map.values() );List<String> list = new ArrayList<String>();list.add( "10" );list.add( "20" );list.add( "30" );list.add( "40" );reflectionCollections( list );reflectionCollections( "this is an string" );


并且我们将得到下面的输出:

Not a collection:  java.util.HashMapA collection:  java.util.HashMap$KeySetElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringA collection:  java.util.HashMap$ValuesElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringA collection:  java.util.ArrayListElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringElement of the collection:  java.lang.StringNot a collection:  java.lang.String


16.注解


一个类,包,方法,域的所有注解使用反射都能被获得。注解在运行时对能被获取的每个元素和它们的值都能被评估。下面的片段展示了怎么样从给定类中获取所有注解和怎么样打印它们的属性和值:

Class<ReflectableClass> object = ReflectableClass.class;// Retrieve all annotations from the classAnnotation[] annotations = object.getAnnotations();for( Annotation annotation : annotations ){System.out.println( annotation );}


下面的例子解释了怎么样检查是否一个元素(域,方法,类)用指定的注解标记了:

// Checks if an annotation is presentif( object.isAnnotationPresent( Reflectable.class ) ){// Gets the desired annotationAnnotation annotation = object.getAnnotation( Reflectable.class );System.out.println( annotation + " present in class " + object.getClass() );// java.lang.classSystem.out.println( annotation + " present in class " + object.getTypeName() );// com.danibuiza.javacodegeeks.reflection.ReflectableClass}


那些片段也许可应用于方法,域以及所有能被注解的元素。


你能找到一个非常好的文章和可扩展的教程关于java注解的域反射相关的几个例子:

http://www.javacodegeeks.com/2014/11/java-annotations-tutorial.html.


17.泛型


泛型在java5中被介绍并且从那时起,它就是一个非常重要的特性宝珠维护代码清理并且更有用的。参数化元素与其它java中的元素是不同的,因此在这个教程中解释的所有话题也用于那些元素。


java也包含指定反射机制去持有泛型。

在运行时会检查是否一个指定的元素(类,方法,域)被参数化了。

也可以使用反射获取参数类型。


这是我们该怎么做的例子:

Method getInternalListMethod = GenericsClass.class.getMethod( "getInternalList", null );// we get the return typeType getInternalListMethodGenericReturnType = getInternalListMethod.getGenericReturnType();// we can check if the return type is parameterized (using ParameterizedType)if( getInternalListMethodGenericReturnType instanceof ParameterizedType ){ParameterizedType parameterizedType = (ParameterizedType)getInternalListMethodGenericReturnType;// we get the type of the arguments for the parameterized typeType[] typeArguments = parameterizedType.getActualTypeArguments();for( Type typeArgument : typeArguments ){// warning not nice// we can work with that nowClass typeClass = (Class)typeArgument;System.out.println( "typeArgument = " + typeArgument );System.out.println( "typeClass = " + typeClass );}}

在这个代码列表中我们能看到域反射相关的主类和泛型是java.lang.reflect.ParameterizedType并且其中它最重要的方法是java.lang.reflect.ParameterizedType.getActualTypeArguments()。


PS:后面还有类载入器和动态代理以及java8的新特性描述,不再翻译,如果感兴趣,可以点击原文链接。翻译了三天,虽然翻译的不怎么好,但是给个鼓励点个赞吧


原文:http://www.javacodegeeks.com/2014/11/java-reflection-api-tutorial.html

0 0