Java反射

1.什么是反射

 Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。

2.反射能做什么

 我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!

3.反射的具体实现

下面是一个基本的类 Person

 
 

package com.ys.reflex; public class Person { //私有属性 private String name = "Tom"; //公有属性 public int age = 18; //构造方法 public Person() { } //私有方法 private void say(){ System.out.println("private say()..."); } //公有方法 public void work(){ System.out.println("public work()..."); } }

 

得到class的三种形式

 
 

//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object // 类型的对象,而我不知道你具体是什么类,用这种方法   Person p1 = new Person();   Class c1 = p1.getClass(); //2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高 // 这说明任何一个类都有一个隐含的静态成员变量 class   Class c2 = Person.class; //3、通过 Class 对象的 forName() 静态方法来获取,用的最多, // 但可能抛出 ClassNotFoundException 异常   Class c3 = Class.forName("com.ys.reflex.Person"); 需要注意的是:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 equals 比较,发现都是true

 

②、通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等

 

 
 

查阅 API 可以看到 Class 有很多方法:   getName():获得类的完整名字。   getFields():获得类的public类型的属性。   getDeclaredFields():获得类的所有属性。包括private 声明的和继承类   getMethods():获得类的public类型的方法。   getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类   getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。   getConstructors():获得类的public类型的构造方法。   getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。   newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

 

 
 

//获得类完整的名字 String className = c2.getName(); System.out.println(className);//输出com.ys.reflex.Person //获得类的public类型的属性。 Field[] fields = c2.getFields(); for(Field field : fields){ System.out.println(field.getName());//age } //获得类的所有属性。包括私有的 Field [] allFields = c2.getDeclaredFields(); for(Field field : allFields){ System.out.println(field.getName());//name age } //获得类的public类型的方法。这里包括 Object 类的一些方法 Method [] methods = c2.getMethods(); for(Method method : methods){ System.out.println(method.getName());//work waid equls toString hashCode等 } //获得类的所有方法。 Method [] allMethods = c2.getDeclaredMethods(); for(Method method : allMethods){ System.out.println(method.getName());//work say } //获得指定的属性 Field f1 = c2.getField("age"); System.out.println(f1); //获得指定的私有属性 Field f2 = c2.getDeclaredField("name"); //启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消 f2.setAccessible(true); System.out.println(f2); //创建这个类的一个对象 Object p2 = c2.newInstance(); //将 p2 对象的 f2 属性赋值为 Bob,f2 属性即为 私有属性 name f2.set(p2,"Bob"); //使用反射机制可以打破封装性,导致了java对象的属性不安全。 System.out.println(f2.get(p2)); //Bob //获取构造方法 Constructor [] constructors = c2.getConstructors(); for(Constructor constructor : constructors){ System.out.println(constructor.toString());//public com.ys.reflex.Person() } 4.根据反射获取父类属性 父类 Parent.java public class Parent { public String publicField = "parent_publicField"; protected String protectField = "parent_protectField"; String defaultField = "parent_defaultField"; private String privateField = "parent_privateField"; } 子类 Son.java public class Son extends Parent { }  测试类: public class ReflectionTest { @Test public void testGetParentField() throws Exception{ Class c1 = Class.forName("com.ys.model.Son"); //获取父类私有属性值 System.out.println(getFieldValue(c1.newInstance(),"privateField")); } public static Field getDeclaredField(Object obj,String fieldName) { Field field = null; Class c = obj.getClass(); for(; c != Object.class ; c = c.getSuperclass()){ try { field = c.getDeclaredField(fieldName); field.setAccessible(true); return field; }catch (Exception e){ //这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。 //如果这里的异常打印或者往外抛,则就不会执行c = c.getSuperclass(),最后就不会进入到父类中了 } } return null; } public static Object getFieldValue(Object object,String fieldName) throws Exception{ Field field = getDeclaredField(object,fieldName); return field.get(object); } }

 

示例1

 
 

public static void main(String args[]) { try { Class cls = Class.forName("MyTest.ConstructorDemo"); Class partypes[] = new Class[2]; partypes[0] = Integer.TYPE; partypes[1] = Integer.TYPE; Method meth = cls.getMethod("add", partypes); ConstructorDemo methobj = new ConstructorDemo(); Object arglist[] = new Object[2]; arglist[0] = new Integer(37); arglist[1] = new Integer(47); Object retobj= meth.invoke(methobj, arglist); Integer retval = (Integer)retobj; System.out.println(retval.intValue()); } catch (Throwable e) { System.err.println(e); } }

示例2

 
 

public static void main(String args[]) { try { Class clz = Class.forName("java.lang.String"); System.out.println("开始:" + clz.getName()); Field field [] = clz.getDeclaredFields(); System.out.println("Field开始:"); for (Field f : field){ System.out.println(":" + f.getName() + ":" + f.toString()); } System.out.println("Field结束:"); Method method []= clz.getDeclaredMethods(); System.out.println("Method开始:"); for (Method m : method){ System.out.println(":" + m.getName()); } System.out.println("Method结束:"); } catch (Throwable e) { System.err.println(e); } }

 

示例3

 
 

public int add(int a, int b) { return a + b; } public public static void main(String args[]) { try { Class clz = Class.forName("MyTest.ConstructorDemo"); Class partypes[] = new Class[2]; partypes[0] = Integer.TYPE; partypes[1] = Integer.TYPE; Method met = clz.getMethod("add", partypes); Constructor constructor = clz.getConstructor(); Object con = constructor.newInstance(); Object obj[] = new Object[2]; obj[0] = new Integer(10); obj[1] = new Integer(10); Object obj1 = met.invoke(con, obj); Integer retval = (Integer) obj1; System.out.println(":" + retval.intValue()); } catch (Throwable e) { System.err.println(e); } }

 

示例4

 
 

public static void main(String[] args) { ArrayList<Consumer> arr = new ArrayList<Consumer>(); ArrayList<Producer> arr1 = new ArrayList<Producer>(); Class clz1 = arr.getClass(); Class clz2 = arr1.getClass(); System.out.println(clz1==clz2); //true System.out.println(clz1.getName()); //类全名 System.out.println(clz2.getSimpleName()); //只有类名 }

 

示例5

 
 

Modifier.isAbstract(int modifiers) Modifier.isFinal(int modifiers) Modifier.isInterface(int modifiers) Modifier.isNative(int modifiers) Modifier.isPrivate(int modifiers) Modifier.isProtected(int modifiers) Modifier.isPublic(int modifiers) Modifier.isStatic(int modifiers) Modifier.isStrict(int modifiers) Modifier.isSynchronized(int modifiers) Modifier.isTransient(int modifiers) Modifier.isVolatile(int modifiers)

===========================================================================================

  • Package Info

 
 

  你可以像这样从Class对象中获取包的相关信息: Class aClass = ... //obtain Class object. See prev. section Package package = aClass.getPackage(); 从Package对象中,你可以访问像包名这样的信息。你也可以访问在classpath中的JAR包中Manifest文件所指定的该包的信息。例如,你可以在Manifest文件中指定包的版本号。你可以从java.lang.Package中阅读更多和Package类相关的信息。

===========================================================================================

  • Superclass

 
 

  你可以用Class对象访问超类。如下: Class superclass = aClass.getSuperclass(); 超类的Class对象和其他类的Class对象一样,所以你也可以继续在超类上用类的反射。

===========================================================================================

  • Implemented Interfaces

 
 

可以得到一个给定类实现的接口的列表。如下: Class aClass = ... //obtain Class object. See prev. section Class[] interfaces = aClass.getInterfaces(); 一个类可以实现多个接口。因此会返回一个Class类型的数组。在Java反射机制中,接口也是由Class对象来表示的。 注:只有被给定类特别声明实现过的接口才会被返回。如果一个超类实现了一个接口,但是该类没有特别声明它已经实现了这个接口,那么这个接口不会出现在返回的数组中。即使该类实际上在它的超类中实现过该接口。 要获取一个给定类的完整的所实现接口的列表,你将不得不递归的去查询该类以及它的超类。

===========================================================================================

  • Constructors

 
 

你可以像这样访问一个类的构造函数: Constructor[] constructors = aClass.getConstructors(); 构造函数在 Java反射——构造函数 中会有详细介绍。

===========================================================================================

  • Methods

 
 

你可以像这样访问一个类的方法: Method[] method = aClass.getMethods();   方法在 Java反射——方法 中会有详细介绍。

===========================================================================================

  • Fields

 
 

你可以像这样访问类的字段(成员变量): Field[] method = aClass.getFields(); 字段在 Java反射——字段 中会有详细介绍。

===========================================================================================

  • Annotations

 
 

  你可以像这样访问类的注解: Annotation[] annotations = aClass.getAnnotations(); 注解在 Java反射——注解 中会有详细介绍。

========================================================================================

 

 

构造方法

使用Java反射,你可以在运行时检查类的构造函数和实例化对象。这是通过Java类java.lang.reflect.Constructor做的。本文将会更详细的介绍Java中的Constructor对象。下面是所涵盖的主题列表:

 
 

Obtaining Constructor Objects (获取Constructor对象) Constructor Parameters (Constructor的参数) Instantiating Objects using Constructor Object (通过Constructor对象实例化对象)

=====================================================================================================

  • Obtaining Constructor Objects

Constructor对象可以从Class对象中获取。下面是一个例子:

 
 

Class aClass = ...//obtain class object Constructor[] constructors = aClass.getConstructors();

数组Constructor[ ]将会存储在类中所有声明为public的构造函数的Constructor实例。 如果你知道你要访问的构造函数的精确参数类型,你可以这样做而不是获取所有的构造函数。这个例子返回给定类的一个public的且接收一个String类型的变量作为参数的构造函数:

 
 

Class aClass = ...//obtain class object Constructor constructor = aClass.getConstructor(new Class[]{String.class});

如果没有和给定的参数相匹配的构造函数,会抛出NoSuchMethodException异常。

=====================================================================================================

  • Constructor Parameters

你可以像这样读取一个给定的构造函数所接收的参数:

 
 

Constructor constructor = ... // obtain constructor - see above Class[] parameterTypes = constructor.getParameterTypes();

=====================================================================================================

  • Instantiating Objects using Constructor Object

你可以像这样实例化一个对象:

 
 

//get constructor that takes a String as argument Constructor constructor = MyObject.class.getConstructor(String.class); MyObject myObject = (MyObject) constructor.newInstance("constructor-arg1");

Constructor.newInstance( )方法接受不定个数的参数,但是你必须提供你调用的构造函数需要的每个参数。在这种情况下,调用接受一个String类型参数的构造函数,你必须提供一个String类型的参数。

========================================================================================

 

 

字段

 

使用java反射,你可以在运行时检查类的字段(成员变量)并且get/set它们的值。这些是通过Java类java.lang.reflect.Field做的。本文将会给出更详细的关于Field对象的信息。请记住也去查阅Sun的JavaDoc。下面是主题列表:

  1. Obtaining Field Objects (获取Field对象)

  2. Field Name (字段名)

  3. Field Type (字段类型)

  4. Getting and Setting Field Values (get/set字段的值)

========================================================================================

  • Obtaining Field Objects

从Class对象中获取Field对象。这里有一个例子:

Class aClass = ...//obtain class object Field[] methods = aClass.getFields();

数组Field[ ]将会存储在类中声明的所有为public的字段的Field对象。

如果你知道你想要访问的字段的字段名,你可以像这样来访问它:

Class aClass = MyObject.class Field field = aClass.getField("someField");

上面的例子将会返回Field实例,和下面MyObject声明的字段someField对应:

public class MyObject{ public String someField = null; }

如果不存在和方法getField( )接受的参数一致的字段,会抛出NoSuchFieldException异常。

========================================================================================

  • Field Name

  一旦你获取了一个Field实例,你可以像这样通过Field.getName()方法取得字段名:

Field field = ... //obtain field object String fieldName = field.getName();

========================================================================================

  • Field Type

你可以通过Field.getType() 方法取得字段类型(String, int etc.) :

Field field = aClass.getField("someField"); Object fieldType = field.getType();

========================================================================================

  • Getting and Setting Field Values

一旦你获取了Field对象的引用,你可以像这样通过Field.get()方法和Field.set()方法get/set字段的值:

Class aClass = MyObject.class Field field = aClass.getField("someField"); MyObject objectInstance = new MyObject(); Object value = field.get(objectInstance); field.set(objetInstance, value);

传给get和set方法的参数objectInstance必须是拥有该字段的实例。在上面的例子中,用了一个MyObject的实例,因为someField是类MyObject的成员实例。 如果字段是静态字段(public static …),则传null作为get和set方法的参数,而不是上面传的objectInstance。

========================================================================================

 

 

方法

 

使用Java反射,你可以检查类的方法并在运行时调用它们。这是通过Java类java.lang.reflect.Method做的。本将会比较详细的讲述Java的Method对象。下面是涵盖的主题列表:

  1. Obtaining Method Objects (获取Method对象)

  2. Method Parameters and Return Types (Method的参数和返回值类型)

  3. Instantiating Objects using Constructor Object (使用构造函数对象实例化对象)

============================================================================================

  • Obtaining Method Objects

从Class对象中获取Method对象。这里有个例子:

Class aClass = ...//obtain class object Method[] methods = aClass.getMethods();

数组Method[ ]将存储类中声明的每个public方法的Method实例。

  如果你知道你要访问的方法的准确的参数类型,你可以这样获取方法而不是所有的方法。这个例子返回一个方法名叫doSomething的public的方法,在给定的类中指定了接受一个String类型的参数:

Class aClass = ...//obtain class object Method method = aClass.getMethod("doSomething", new Class[]{String.class});

如果没有和给定参数相匹配的的方法,会抛出NoSuchMethodException异常。 如果你尝试去访问的方法没有参数,传入null作为参数,像这样:

Class aClass = ...//obtain class object Method method = aClass.getMethod("doSomething", null);

============================================================================================

  • Method Parameters and Return Types

你可以像这样读取一个给定的方法所接收的参数:

Method method = ... // obtain method - see above Class[] parameterTypes = method.getParameterTypes();

你可以像这样访问一个方法的返回值类型:

Method method = ... // obtain method - see above Class returnType = method.getReturnType();

============================================================================================

  • Invoking Methods using Method Object

你可以像这样调用一个方法:

//get method that takes a String as argument Method method = MyObject.class.getMethod("doSomething", String.class); Object returnValue = method.invoke(null, "parameter-value1");

参数null是你想要调用方法的对象。如果是静态方法,你需要提供null作为参数而不是一个对象的实例。在这个例子里,如果doSomething(String.class)不是静态的,你需要提供validMyObject作为实例而不是null。

方法Method.invoke(Object target, Object … parameters)接受不定个数的参数, 但是你必须提供与你所调用的方法对应的每个参数。在这里,这个方法接受一个String类型的参数,所以必须提供一个String类型的参数。

 

 

Getter和Setter方法

使用Java反射你可以检查类的方法并在运行时调用它们。这个可以用来检测一个给定类所包含的Getter和Setter方法。你不能明确的获取Getter和Setter,所以你不得不通过扫描类里面的所有方法,然后检查每个方法是不是Getter方法或者Setter方法。 首先,让我们建立一些描述Getter和Setter特征的规则:

  • Getter

  一个getter方法的方法名易“get”开头,不接受参数,返回一个值。

  • Setter

  一个setter方法的方法名以“set”开头,接受一个参数。 setter方法既可以返回值也可以不返回值。一些setter方法返回void,一些返回传入的值,其他的为了是使用方法链返回调用该setter的对象。因此,你不应该对一个setter方法的返回值类型做假设。 这里是找出一个类的getter方法和setter方法的代码示例:

 
 

public static void printGettersSetters(Class aClass){ Method[] methods = aClass.getMethods(); for(Method method : methods){ if(isGetter(method)) System.out.println("getter: " + method); if(isSetter(method)) System.out.println("setter: " + method); } } public static boolean isGetter(Method method){ if(!method.getName().startsWith("get")) return false; if(method.getParameterTypes().length != 0) return false; if(void.class.equals(method.getReturnType()) return false; return true; } public static boolean isSetter(Method method){ if(!method.getName().startsWith("set")) return false; if(method.getParameterTypes().length != 1) return false; return true;

 

} 私有变量和私有方法

尽管普遍的观点是不能直接访问私有字段和私有方法的,实际上通过Java反射是可以访问其他类的私有字段和私有方法的。它甚至不是那么困难。在单元测试期间很容易使用。本文将介绍你怎么做。 注:这个只会作用在运行独立的Java应用程序时,如写单元测试和常规应用。如果你尝试在Java Applet内使用它,你需要处理好SecurityManager。但是,因为这不是你经常要做的事情,所以在本文中将不会涉及它。 下面是本文的主题列表:

  1. Accessing Private Fields (访问私有字段)

  2. Accessing Private Methods (访问私有方法)

===========================================================================================

  • Accessing Private Fields

为了访问私有字段,你需要调用Class.getDeclaredField(String name)方法或者Class.getDeclaredFields()方法。Class.getField(String name)和Class.getFields()方法只会返回public的字段,所有它们不会工作。下面是一个简单的例子,通过Java反射去访问一个类的私有字段:

 
 

public class PrivateObject { private String privateString = null; public PrivateObject(String privateString) { this.privateString = privateString; } } PrivateObject privateObject = new PrivateObject("The Private Value"); Field privateStringField = PrivateObject.class.getDeclaredField("privateString"); privateStringField.setAccessible(true); String fieldValue = (String) privateStringField.get(privateObject); System.out.println("fieldValue = " + fieldValue);

这段代码示例将会打印出文本“fieldValue = The Private Value”,是PrivateObject实例在代码示例最开始赋值给私有字段privateString的。 注意这里使用的方法PrivateObject.class.getDeclaredField("privateString")。是这个方法调用返回了私有字段的值。这个方法只会返回在给定类里声明的字段的值,而不是其他任何在超类里声明的字段的值。 注意字体加粗的一行。仅仅针对反射,通过调用Field.setAccessible(true)方法,关闭了对特定的Field实例的访问检查。现在你可以访问它,尽管它是private,或者protected,或者是package scope,即使调用者不在这个范围内。你仍然不能通过一般的代码去访问这些字段。编译器不允许这样干。 ===========================================================================================

  • Accessing Private Methods

为了访问私有方法,你需要调用Class.getDeclaredMethod(String name, Class[ ] parameterTypes)方法或者Class.getDeclaredMethods()方法。Class.getMethod(String name, Class[ ] parameterTypes)方法和Class.getMethods()只会返回共有的方法,所以它们不会工作。下面是一个简单的代码示例,通过Java反射访问一个类的私有方法: public class PrivateObject {

 
 

private String privateString = null; public PrivateObject(String privateString) { this.privateString = privateString; } private String getPrivateString(){ return this.privateString; } } PrivateObject privateObject = new PrivateObject("The Private Value"); Method privateStringMethod = PrivateObject.class.getDeclaredMethod("getPrivateString", null); privateStringMethod.setAccessible(true); String returnValue = (String) privateStringMethod.invoke(privateObject, null); System.out.println("returnValue = " + returnValue);

这段代码示例将会打印出文本“returnValue = The Private Value”,是在代码示例最开始创建的PrivateObject实例调用getPrivateString()方法时的返回值。

注意这里使用的方法PrivateObject.class.getDeclaredMethod("privateString")。是这个方法调用返回的私有方法。这个方法只会返回在给定的类里面声明的方法,而不是在任何超类里声明的方法。 注意字体加粗的一行。仅仅针对反射,通过调用Method.setAccessible(true)方法,关闭了对特定的Method实例的访问检查。现在你可以访问它了,尽管它是private,或者protected,或者package scope,即使调用者不在这个范围内。你仍然不能通过一般的代码访问这些方法。编译器不允许这样干。

========================================================================================

 

 

 

注解

 

【译】8. Java反射--注解​www.cnblogs.com

泛型

【译】9. Java反射--泛型​www.cnblogs.com

数组

【译】10. Java反射--数组​www.cnblogs.com

动态加载

【译】11. Java反射--动态代理​www.cnblogs.com

动态加载和重新加载

【译】12. Java反射--类的动态加载和重新加载​www.cnblogs.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值