重走JAVA编程之路(三)反射

栏目: Java · 发布时间: 7年前

内容简介:Java反射给我们提供了在运行时检查甚至修改应用行为的机制。 反射是java高级的核心技术,所有有经验的程序员都应该理解。通过反射机制,我们可以在运行时检视 类、接口、枚举,获得他们的结构、方法以及属性信息,即使在编译期类是不可访问的。 我们也可以通过反射创建类实例,调用它的方法,或者改变属性值。Java的反射是一种很强大的机制,在正常的编程中使用并不多,但它是java的主干,很多Java EE 框架均使用了反射技术:

Java反射给我们提供了在运行时检查甚至修改应用行为的机制。 反射是 java 高级的核心技术,所有有经验的 程序员 都应该理解。

通过反射机制,我们可以在运行时检视 类、接口、枚举,获得他们的结构、方法以及属性信息,即使在编译期类是不可访问的。 我们也可以通过反射创建类实例,调用它的方法,或者改变属性值。

Java中的反射

Java的反射是一种很强大的机制,在正常的编程中使用并不多,但它是java的主干,很多Java EE 框架均使用了反射技术:

  • JUnit利用反射技术解析@Test注解,从而得到测试的方法并调用它们。

  • Spring依赖注入是java反射的典型应用

  • Tomcatweb容器通过解析web.xml文件和请求url,将请求正确的转发到对应的模块。

  • Eclipse自动完成方法的名称输入

  • Struts

  • Hibernate

以上这个清单只是小部分,它们全部使用了反射技术,因为正常情况下,它们无法访问用户编写的类、接口以及方法等。

但是我们不建议在正常编程中滥用反射技术,因为我们拥有自己编写的类的访问权限了,反射存在以下几个缺陷:

  • 性能较差尽管反射解决了动态类型的问题,但是也引入了在classpath 扫描类进行加载的过程,会影响性能。

  • 安全限制反射需要在运行时获得访问权限,但是在security manager中可能是不允许的。 这可能会导致应用运行失败。

  • 安全问题通过反射我们可以访问那些不建议我们访问的类,例如我们可以访问private的属性并修改其值。 这可能引发安全问题导致应用异常。

  • 较高的维护代价反射相关的代码难以理解以及调试,代码的错误不能在编译期展现出来,使用反射的代码灵活性不高并难以维护。

反射在类中的使用

在java中,任何对象要么是原始类型或者引用类型。 所有的类、枚举、数据和其他引用类型均继承自Object类。

java.lang.Class 是所有反射操作的入口。对于任何类型的对象,JVM 会初始化其一个不可变的java.lang.Class 实例来提供检查对象的运行时的属性、创建新对象、调用方法、get/set 属性。

我们来看看Class的重要方法,为了方便起见,我们先创建一些类和接口。

package com.byron4j.hightLevel.reflection;

public interface BaseInterface {
	
	public int interfaceInt=0;
	
	void method1();
	
	int method2(String str);
}

复制代码
package com.byron4j.hightLevel.reflection;

public class BaseClass {
	
public int baseInt;
	
	private static void method3(){
		System.out.println("Method3");
	}
	
	public int method4(){
		System.out.println("Method4");
		return 0;
	}
	
	public static int method5(){
		System.out.println("Method5");
		return 0;
	}
	
	void method6(){
		System.out.println("Method6");
	}
	
	// piblic 的内部类
	public class BaseClassInnerClass{}
		
	// public 的枚举
	public enum BaseClassMemberEnum{}
	
}

复制代码
package com.byron4j.hightLevel.reflection;

public class ConcreteClass extends BaseClass implements BaseInterface{

	public int publicInt;
	private String privateString="private string";
	protected boolean protectedBoolean;
	Object defaultObject;
	
	public ConcreteClass(int i){
		this.publicInt=i;
	}

	@Override
	public void method1() {
		System.out.println("Method1 impl.");
	}

	@Override
	public int method2(String str) {
		System.out.println("Method2 impl.");
		return 0;
	}
	
	@Override
	public int method4(){
		System.out.println("Method4 overriden.");
		return 0;
	}
	
	public int method5(int i){
		System.out.println("Method4 overriden.");
		return 0;
	}
	
	// inner classes
	public class ConcreteClassPublicClass{}
	private class ConcreteClassPrivateClass{}
	protected class ConcreteClassProtectedClass{}
	class ConcreteClassDefaultClass{}
	
	//member enum
	enum ConcreteClassDefaultEnum{}
	public enum ConcreteClassPublicEnum{}
	
	//member interface
	public interface ConcreteClassPublicInterface{}

	
}

复制代码

下面来看看使用反射的常用方法。

获得Class对象

我们可以通过三种方式获取对象的Class实例:

  • 通过静态变量class

  • 使用示例的getClass()方法

  • java.lang.Class.forName(String 完整的类名) ,完整的类名包含包名。

原始类型的class、包装类型的TYPE均可以获得Class对象。

package com.byron4j.hightLevel.reflection;

public class ReflectionDemo {
	public static void main(String[] args) throws Exception{
		
		//方式一: 通过累的静态变量class获取Class对象
		Class concreteClass = ConcreteClass.class;
		
		//方式二:通过实例的getClass()方法获取Class对象
		concreteClass = new ConcreteClass(7).getClass();
		
		//方式三:
		concreteClass = Class.forName("com.byron4j.hightLevel.reflection.ConcreteClass");
		
		//打印类相关信息
		System.out.println(concreteClass.getCanonicalName());
		System.out.println(concreteClass.getName());
		
		
		/*++++++++++++++++++++++++++++++++++++++++++++++++
		 * 原始类型的class、包装类型的TYPE
		 * +++++++++++++++++++++++++++++++++++++++++++++++
		 */
		
		Class primative = boolean.class;
		System.out.println(primative.getCanonicalName());
		
		Class doubleClass = Double.TYPE;
		System.out.println(doubleClass.getName());
		
		//数组类型的class示例
		Class<?> arrayClass = Class.forName("[D");
		System.out.println(arrayClass.getCanonicalName());
		arrayClass = Class.forName("[B");
		System.out.println(arrayClass.getCanonicalName());
		arrayClass = Class.forName("[S");
		System.out.println(arrayClass.getCanonicalName());
		arrayClass = Class.forName("[C");
		System.out.println(arrayClass.getCanonicalName());
		arrayClass = Class.forName("[F");
		System.out.println(arrayClass.getCanonicalName());
		
		
		
	}
}

复制代码

输出如下所示:

com.byron4j.hightLevel.reflection.ConcreteClass
com.byron4j.hightLevel.reflection.ConcreteClass
boolean
double
double[]
byte[]
short[]
char[]
float[]

复制代码

Class的getCanonicalName()方法返回类的名称。在泛型中使用 java.lang.Class,可以帮助框架获取子类。

获取超类Super Class

getSuperclass() 方法,返回类的超类(基类、父类)的class实例,如果该类是java.lang.Object、原始类型、接口则返回null。如果该class是数组形式,则该方法返回java.lang.Object。

Class<?> superClass = Class.forName("com.byron4j.hightLevel.reflection.ConcreteClass").getSuperclass();
System.out.println(superClass); 
System.out.println(Object.class.getSuperclass());
System.out.println(String[][].class.getSuperclass());
复制代码

输入如下:

class com.byron4j.hightLevel.reflection.BaseClass
null
class java.lang.Object
复制代码

获取公有的class

Class的 getClasses() 方法可以获取class的所有继承的超类、接口和自己定义的 公有 类、接口、枚举等的数组形式。

Class[] classARR = concreteClass.getClasses();
System.out.println(Arrays.toString(classARR));
复制代码

输出:

[class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicClass, 
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicEnum, 
interface com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicInterface, 
class com.byron4j.hightLevel.reflection.BaseClass$BaseClassInnerClass, 
class com.byron4j.hightLevel.reflection.BaseClass$BaseClassMemberEnum]
复制代码

获取自身声明的类

getDeclaredClasses() 获取当前类型自身定义的所有类、接口,并不包含从父类继承过来的来、接口。

Class[] declareClassARR = concreteClass.getDeclaredClasses();
System.out.println("Arrays.toString(declareClassARR));
复制代码

输出:

[class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassDefaultClass, 
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassDefaultEnum, 
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPrivateClass, 
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassProtectedClass, 
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicClass, 
class com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicEnum,
interface com.byron4j.hightLevel.reflection.ConcreteClass$ConcreteClassPublicInterface]

复制代码

获取定义该class的类

class. getDeclaringClass() 获取定义class的类。如果该类不是任何类或接口的成员,则返回null。

/**================================================
		 * getDeclaringClass
		 * ================================================
		 */
		System.out.println(BaseClassInnerClass.class.getDeclaringClass());
System.out.println(Double.TYPE.getDeclaringClass());
复制代码

该类BaseClassInnerClass是在BaseClass中定义的,于是输出:

class com.byron4j.hightLevel.reflection.BaseClass

null
复制代码

获取包名

getPackage() 方法获取包的class实例。

/*===========================================
* getPackage()
* ==========================================
*/
System.out.println(concreteClass.getPackage().getName());
复制代码

输出:

com.byron4j.hightLevel.reflection
复制代码

获取类的修饰符

getModifiers() 方法可以获取class实例的访问修饰符的个数。 java.lang.reflect.Modifier.toString() 可以获取class的修饰符的字符串形式。

/*===========================================
		 * getModifiers()、Modifier.toString()
		 * ==========================================
		 */
		System.out.println(concreteClass.getModifiers());
		System.out.println(Modifier.toString(concreteClass.getModifiers()));
复制代码

输出:

1
public
复制代码

获取类型参数

getTypeParameters() 方法获取class的类型声明参数,如果有的话。比如集合框架的接口均制定了泛型。

Arrays.asList(Class.forName("java.util.Map").getTypeParameters()).forEach(
				s -> { System.out.println(s); }
		);
复制代码

输出:

K V

获取class实现的接口

getGenericInterfaces() 可以获取class已经实现的接口的数组形式,并包含泛型接口。 getInterfaces() 方法会返回所有实现的接口,但是不包含泛型接口。

/**=============================================
		 * getGenericInterfaces()、getInterfaces()
		 * =============================================
		 */
		Arrays.asList(concreteClass.getInterfaces()).forEach(
				s -> { System.out.println("com.byron4j.hightLevel.reflection.ConcreteClass实现的接口:" + s); }
		);
		System.out.println("========================================");
		Arrays.asList(concreteClass.getGenericInterfaces()).forEach(
				s -> { System.out.println("com.byron4j.hightLevel.reflection.ConcreteClass实现的接口:" + s); }
		);
		
		System.out.println("========================================");
		System.out.println("========================================");
		Arrays.asList(Class.forName("java.util.ArrayList").getInterfaces()).forEach(
				s -> { System.out.println("java.util.ArrayList实现的接口:" + s); }
		);
		System.out.println("========================================");
		Arrays.asList(Class.forName("java.util.ArrayList").getGenericInterfaces()).forEach(
				s -> { System.out.println("java.util.ArrayList实现的接口:" + s); }
		);
		
复制代码

输出:

com.byron4j.hightLevel.reflection.ConcreteClass实现的接口:interface com.byron4j.hightLevel.reflection.BaseInterface
========================================
com.byron4j.hightLevel.reflection.ConcreteClass实现的接口:interface com.byron4j.hightLevel.reflection.BaseInterface
========================================
========================================
java.util.ArrayList实现的接口:interface java.util.List
java.util.ArrayList实现的接口:interface java.util.RandomAccess
java.util.ArrayList实现的接口:interface java.lang.Cloneable
java.util.ArrayList实现的接口:interface java.io.Serializable
========================================
java.util.ArrayList实现的接口:java.util.List<E>
java.util.ArrayList实现的接口:interface java.util.RandomAccess
java.util.ArrayList实现的接口:interface java.lang.Cloneable
java.util.ArrayList实现的接口:interface java.io.Serializable

复制代码

获取所有的public方法

getMethods() 方法可以获取所有的public方法,包含父类、接口中继承来的public方法。

/**=============================================
		 * getMethods()
		 * =============================================
		 */
		System.out.println("========================================");
		Arrays.asList(concreteClass.getMethods()).forEach(
				s -> { System.out.println("public类型的方法:" + s); }
		);
复制代码

输出:

public类型的方法:public void com.byron4j.hightLevel.reflection.ConcreteClass.method1()
public类型的方法:public int com.byron4j.hightLevel.reflection.ConcreteClass.method2(java.lang.String)
public类型的方法:public int com.byron4j.hightLevel.reflection.ConcreteClass.method4()
public类型的方法:public int com.byron4j.hightLevel.reflection.ConcreteClass.method5(int)
public类型的方法:public static int com.byron4j.hightLevel.reflection.BaseClass.method5()
public类型的方法:public final void java.lang.Object.wait() throws java.lang.InterruptedException
public类型的方法:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public类型的方法:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public类型的方法:public boolean java.lang.Object.equals(java.lang.Object)
public类型的方法:public java.lang.String java.lang.Object.toString()
public类型的方法:public native int java.lang.Object.hashCode()
public类型的方法:public final native java.lang.Class java.lang.Object.getClass()
public类型的方法:public final native void java.lang.Object.notify()
public类型的方法:public final native void java.lang.Object.notifyAll()

复制代码

获取class的所有public构造器

getConstructors()方法能够获取所有的public类型构造器

/**=============================================
		 * getConstructors()
		 * =============================================
		 */
		System.out.println("========================================");
		Arrays.asList(concreteClass.getConstructors()).forEach(
				s -> { System.out.println("public类型的构造器:" + s); }
		);
复制代码

输出:

public类型的构造器:public com.byron4j.hightLevel.reflection.ConcreteClass(int)

获取所有的public属性(成员变量)

getFields() 方法可以获取所有的public属性。包含父类、接口中的属性。

/**=============================================
		 * getFields()
		 * =============================================
		 */
		System.out.println("========================================");
		Arrays.asList(concreteClass.getFields()).forEach(
				s -> { System.out.println("public类型的属性:" + s); }
		);
复制代码

输出:

public类型的属性:public int com.byron4j.hightLevel.reflection.ConcreteClass.publicInt
public类型的属性:public static final int com.byron4j.hightLevel.reflection.BaseInterface.interfaceInt
public类型的属性:public int com.byron4j.hightLevel.reflection.BaseClass.baseInt

复制代码

获取所有的注解

getAnnotations() 方法可以获取所有的注解。但是只有保留策略为 RUNTIME 的注解。

我们给 类加上注解@Deprecated。

@Deprecated
public class ConcreteClass extends BaseClass implements BaseInterface{...}

/////////////////////////////////////
/**=============================================
		 * getAnnotations()
		 * =============================================
		 */
		System.out.println("========================================");
		Arrays.asList(concreteClass.getAnnotations()).forEach(
				s -> { System.out.println("注解:" + s); }
		);
复制代码

输出:

注解:@java.lang.Deprecated()

反射在属性(成员变量)中的应用

反射API提供了几个方法可以在运行时分解类的成员变量以及设置其值。

获取public属性

除了前面的getFields()方法能获取全部public属性之外,还提供了一个获取指定属性名称的方法getField()。这个方法会在该class-->接口-->父类的顺序寻找指定的属性。

场景一:我们在ConcreteClass类、BaseClass类、BaseInterface接口分别增加一个属性: public String name = "currClass---NAME";

public String name = "superClass---NAME";

public String name = "superInterface---NAME";

System.out.println(concreteClass.getField("name"));

输出为:
public java.lang.String com.byron4j.hightLevel.reflection.ConcreteClass.name


复制代码

场景二:我们在BaseClass类、BaseInterface接口分别增加一个属性: public String name = "superClass---NAME";

public String name = "superInterface---NAME";

这时候获取的属性name是接口中的属性:

public static final java.lang.String com.byron4j.hightLevel.reflection.BaseInterface.name

场景三:我们仅仅在BaseClass类增加一个属性: public String name = "superClass---NAME";

这时候获取的属性name是父类中的属性:

public java.lang.String com.byron4j.hightLevel.reflection.BaseClass.name

如果是获取不存在的属性,则出现异常:

System.out.println(concreteClass.getField("hello"));

输出:

java.lang.NoSuchFieldException: hello

获取声明属性的类型

try {
			System.out.println( concreteClass.getField("interfaceInt").getDeclaringClass() );
		} catch (NoSuchFieldException | SecurityException e) {
			
			e.printStackTrace();
		}
复制代码

输出:

interface com.byron4j.hightLevel.reflection.BaseInterface

获取属性的类型

getType() 方法返回属性的类型的class实例。

System.out.println(concreteClass.getField("interfaceInt").getType().getCanonicalName());
复制代码

输出:

int

Get/Set public类型的属性值

Field.get(Object) 获取该属性的值。

Field field = concreteClass.getField("publicInt");
System.out.println("属性的类型:"+field.getType());

ConcreteClass obj= new ConcreteClass(7);

System.out.println("获取属性值:" + field.get(obj));

field.set(obj, 77);
System.out.println("获取属性值:" + field.get(obj));
复制代码

输出:

属性的类型:int 获取属性值:7 获取属性值:77

Field.get()返回的是一个Object类型,如果是原始类型则返回其包装类型。如果是final属性,set() 方法抛出 java.lang.IllegalAccessException

Get/Set private类型的属性值

java中在类之外是不能访问private变量的。但是通过反射可以关闭检查访问修饰符的机制。

Field privateField = concreteClass.getDeclaredField("privateString");
		
System.out.println(privateField.get(obj));
复制代码

输出,不能访问private的属性:

java.lang.IllegalAccessException: Class com.byron4j.hightLevel.reflection.ReflectionDemo2 can not access a member of class com.byron4j.hightLevel.reflection.ConcreteClass with modifiers "private"

设置可访问机制Field.setAccessible(true);:

Field privateField = concreteClass.getDeclaredField("privateString");
		privateField.setAccessible(true);
		System.out.println(privateField.get(obj));

		privateField.set(obj, "新的私有属性值[value]");
		System.out.println(privateField.get(obj));
复制代码

输出:

private string 新的私有属性值[value]

与方法相关的反射方法

使用反射技术可以获得方法的信息以及调用执行它。我们来学习获得方法、调用方法并访问私有方法。

获得public方法

我们可以使用getMethod()方法获的public class的方法,需要提供方法的名称、参数类型。如果class找不到指定的方法,则会继续向上从其父类中查找。

下面我们以一个获取HashMap 的put方法的例子来展示如何方法的参数类型、方法访问修饰符和返回类型。

/*=========================================
		 * 方法
		 * ========================================
		 */
		
		Class mapClass = HashMap.class;
		Method mapPut = mapClass.getMethod("put", Object.class, Object.class);
		//获取参数类型
		System.out.println(Arrays.toString(mapPut.getParameterTypes()));
	
		//获取返回类型
		System.out.println(mapPut.getReturnType());
		
		//访问修饰符
		System.out.println(Modifier.toString(mapPut.getModifiers()));
复制代码

输出:

[class java.lang.Object, class java.lang.Object] class java.lang.Object public

调用public方法

可以利用 Method.invoke() 方法调用指定的方法。

//调用方法
Map<String, String> map = new HashMap<String, String>();
mapPut.invoke(map, "key", "val");
System.out.println(map);
复制代码

输出:

{key=val}

调用private方法

我们可以使用 getDeclaredMethod() 方法获取私有方法,然后关闭访问限制,即可调用。

/**
* 访问私有方法
*/
Method method3 = BaseClass.class.getDeclaredMethod("method3", null);

method3.setAccessible(true);
//静态方法的调用对象可以传入null
method3.invoke(null, null);
复制代码

输出:

Method3

反射在构造器中的使用

我们可以使用 getConstructor() 方法获取指定的public构造器。

/**
* 反射在构造器中的使用
*/
Constructor<?> constructor = ConcreteClass.class.getConstructor(int.class);

System.out.println(Arrays.toString(constructor.getParameterTypes())); 
	
Constructor<?> hashMapConstructor = HashMap.class.getConstructor(null);
System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes())); 
		
复制代码

输出:

[int] []

利用构造器初始化对象实例

我们可以利用constructor 实例的 newInstance() 方法获初始化实例。

/**
* 初始化实例
*/

Object myObj = constructor.newInstance(10);
Method myObjMethod = myObj.getClass().getMethod("method1", null);
myObjMethod.invoke(myObj, null); //prints "Method1 impl."

HashMap<String,String> myMap = (HashMap<String,String>)hashMapConstructor.newInstance(null);
复制代码

以上所述就是小编给大家介绍的《重走JAVA编程之路(三)反射》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms of the Intelligent Web

Algorithms of the Intelligent Web

Haralambos Marmanis、Dmitry Babenko / Manning Publications / 2009-7-8 / GBP 28.99

Web 2.0 applications provide a rich user experience, but the parts you can't see are just as important-and impressive. They use powerful techniques to process information intelligently and offer featu......一起来看看 《Algorithms of the Intelligent Web》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具