`
su1216
  • 浏览: 672018 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Group-logo
深入入门正则表达式(jav...
浏览量:72012
E60283d7-4822-3dfb-9de4-f2377e30189c
android手机的安全问...
浏览量:128927
社区版块
存档分类
最新评论

java 运行时类型识别(RTTI) - 2 - 反射

    博客分类:
  • java
阅读更多

java 运行时类型识别(RTTI) - 1 - Class与instanceof

java 运行时类型识别(RTTI) - 2 - 反射

 

本文将叙述如何运行时查看类信息,其中包括变量,方法,以及通过反射修改变量,执行方法等

包括如何反射匿名内部类及如何执行其方法,但是笔者强烈不建议这么做,这里只是演示反射而已

 

下面是一个测试类

package reflect;

public class Outer {

	static{
		System.out.println("Testing..");
	}
	
	TestInterface ti = new TestInterface() {
		public String test() {
			return "this is ti";
		}
	};
	
	public Outer(String name){
		System.out.println("Outer");
	}

	public String toString(){
		System.out.println("Outer toString");
		return new TestInterface() {
			public String test() {
				return "this is a test!";
			}
		}.test();
	}
	
	@SuppressWarnings("unused")
	private void privateMethod(){
		System.out.println("privateMethod");
	}
	
	protected void protectedMethod(){
		System.out.println("protectedMethod");
	}
	
	void packageMethod(){
		System.out.println("packageMethod");
	}
	
	public static void staticMethod(){
		System.out.println("staticMethod");
	}
	
	public interface TestInterface {
		public String test();
	}
	
	public static class StaticInner {
		private static final String TAG = "StaticInnerTAG";
		public StaticInner(){
			System.out.println("StaticInner");
		}
		
		public String toString(){
			System.out.println("StaticInner toString");
			return TAG;
		}
	}
	
	private class Inner{
		String name;
		public Inner(){
			System.out.println("Inner");
		}
		
		public Inner(String name){
			System.out.println("reflect.Outer.Inner.Inner(String name)");
			this.name = name;
		}
	}
}
  

其中包括了普通内部类,静态内部类,内部匿名类,接口等

其中外部类Outer没有默认构造器,String reflect.Outer.Inner.getName()是private的……

 

外部类

Constructor,Method,Class.forName,newInstance

下面来看如何对上述的类进行反射

首先先看外部类Outer

public static void main(String[] args) {
	try {
		Class<?> outer = Class.forName("reflect.Outer");
		outer = Outer.class;
		Constructor<?>[] constructors = outer.getConstructors();
		for (Constructor<?> constructor : constructors) {
			Class<?>[] types = constructor.getParameterTypes();
			System.out.println(constructor.getName() + "(" + arrayToString(types) + ")");
		}
		
		Constructor<?> outerConstructor = outer.getConstructor(String.class);
		Outer outerInstance = (Outer) outerConstructor.newInstance("a outer");
		System.out.println(outerInstance);
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SecurityException e) {
		e.printStackTrace();
	} catch (NoSuchMethodException e) {
		e.printStackTrace();
	} catch (IllegalArgumentException e) {
		e.printStackTrace();
	} catch (InstantiationException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	} catch (InvocationTargetException e) {
		e.printStackTrace();
	}
}

public static String arrayToString(Class<?>[] classes){
	StringBuilder stringBuilder = new StringBuilder();
	for (Class<?> type : classes) {
		stringBuilder.append(type.getCanonicalName());
		stringBuilder.append(",");
	}
	if(classes.length > 0){
		stringBuilder.deleteCharAt(stringBuilder.length() - 1);
	}
	return stringBuilder.toString();
}

 

打印结果

Testing..
reflect.Outer(java.lang.String)
Outer
Outer toString
this is a test!
 

 

下面简单分析下

 

Class<?> outer = Class.forName("reflect.Outer");
//outer = Outer.class;

我们对类reflect.Outer进行反射,这里使用Outer.class也可以获得Class对象

之前说过使用类字面量不会初始化该类,而Class.forName则会初始化 ,当使用Outer.class时候log如下

 

reflect.Outer(java.lang.String)
Testing..
Outer
Outer toString
this is a test!

 

顺序改变了,打印的时候Testing..实际是下面这句触发的

 

Outer outerInstance = (Outer) outerConstructor.newInstance("a outer");

这时候加载Outer这个类,然后进行初始化

 

 

接下来我们查看了Outer的所有构造器Constructor,并打印出所需要的参数(与普通方法区别开来)

构造器只有一个,参数是String

如何执行这个带有String作为参数的构造器来返回一个Outer实例

 

Constructor<?> outerConstructor = outer.getConstructor(String.class);
Outer outerInstance = (Outer) outerConstructor.newInstance("a outer");
System.out.println(outerInstance);

 

我们先取得我们需要的Constructor,然后,Constructor提供了newInstance方法,这样就可以获得Outer实例

最后打印实例,调用了toString函数

如果你尝试调用

 

outer.newInstance(); 

 

则会看到如下异常

 

java.lang.InstantiationException: reflect.Outer

at java.lang.Class.newInstance0(Class.java:357)

at java.lang.Class.newInstance(Class.java:325)

at reflect.Test.main(Test.java:30)

 

这是因为Outer没有提供默认构造器

在获得Constructor时其实是有下面两个选择的,至于区别,我们放在Method里说

 

outer.getConstructors();
outer.getDeclaredConstructors()

 

 

现在看看如何获得类的方法,顺便看看Declared的作用

不如直接打印出来,这样比较直观

 

Method[] methods = outer.getMethods();
for (Method method : methods) {
	Class<?>[] types = method.getParameterTypes();
	System.out.println(method.getReturnType().getName() + " " + outer.getName() + "." + method.getName() + "(" + arrayToString(types) + ")");
}
System.out.println("--------------------------------------");
methods = outer.getDeclaredMethods();
for (Method method : methods) {
	Class<?>[] types = method.getParameterTypes();
	System.out.println(method.getReturnType().getName() + " " + outer.getName() + "." + method.getName() + "(" + arrayToString(types) + ")");
}Class<?> outer = Outer.class;

 

 

结果如下

void reflect.Outer.staticMethod()
java.lang.String reflect.Outer.toString()
void reflect.Outer.wait(long)
void reflect.Outer.wait()
void reflect.Outer.wait(long,int)
boolean reflect.Outer.equals(java.lang.Object)
int reflect.Outer.hashCode()
java.lang.Class reflect.Outer.getClass()
void reflect.Outer.notify()
void reflect.Outer.notifyAll()
--------------------------------------
void reflect.Outer.privateMethod()
void reflect.Outer.protectedMethod()
void reflect.Outer.packageMethod()
void reflect.Outer.staticMethod()
java.lang.String reflect.Outer.toString()
  

调用getMethods时,Outer的方法只打印出了toString,其余结果均为父类Object的公有方法

调用getDeclaredMethods时,则只打印出在Outer中声明定义的方法

他们均打印出了公共的静态方法

所有带Declared和不带Declared的成对的方法都和上面的类似

 

如何利用反射执行一个方法,下面以void reflect.Outer.privateMethod()为例

Method method = outer.getDeclaredMethod("privateMethod");
method.setAccessible(true);
method.invoke(outerInstance, (Object[])null);
method.setAccessible(false);

首先privateMethod是私有方法,所以为了能得到对应的Method对象,我们需要调用getDeclaredMethod

 

为了能访问私有方法,我们需要进行setAccessible(true)的设置

然后需要有个对象来执行,我们选择刚才的outerInstance

这时控制台输出

 

privateMethod

 

干完活后,恢复其设置setAccessible(false)

 

下面是静态方法的执行

 

Method method = outer.getDeclaredMethod("staticMethod");
method.setAccessible(true);
method.invoke(Outer.class, (Object[])null);
method.setAccessible(false);

 

 ps:使用method.invoke(outerInstance, (Object[])null);也能执行,但是不该这样

 

内部类

isInterface,Field,isAccessible,setAccessible,getModifiers,Modifier.toString()

下面看看内部类的反射

Class<?>[] classes = outer.getDeclaredClasses();
for (Class<?> clazz : classes) {
	if(clazz.isInterface()){
		System.out.println("interface : " + clazz.getName());
	}else{
		System.out.println("class : " + clazz.getName());
	}
}

 

输出

class : reflect.Outer$Inner
class : reflect.Outer$StaticInner
interface : reflect.Outer$TestInterface

 

看来反射提供的api功能还是很全的

 

下面是如何反射静态内部类

try {
	Class<StaticInner> staticInner = StaticInner.class;
	StaticInner staticInnerInstance = staticInner.newInstance();
}catch (SecurityException e) {
	e.printStackTrace();
} catch (IllegalArgumentException e) {
	e.printStackTrace();
} catch (InstantiationException e) {
	e.printStackTrace();
} catch (IllegalAccessException e) {
	e.printStackTrace();
}

 

也可以使用Class.forName方式,如果StaticInner是不可见的,那么上面的代码则无法编译,下面看看Class.forName

try {
	Class<StaticInner> staticInner = (Class<StaticInner>) Class.forName("reflect.Outer.StaticInner");
	StaticInner staticInnerInstance = staticInner.newInstance();
}catch (SecurityException e) {
	e.printStackTrace();
} catch (IllegalArgumentException e) {
	e.printStackTrace();
} catch (InstantiationException e) {
	e.printStackTrace();
} catch (IllegalAccessException e) {
	e.printStackTrace();
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

 

如果你和上面的写法一样,那么很不幸,运行时会得到如下错误

java.lang.ClassNotFoundException: reflect.Outer.StaticInner

at java.net.URLClassLoader$1.run(URLClassLoader.java:217)

at java.security.AccessController.doPrivileged(Native Method)

at java.net.URLClassLoader.findClass(URLClassLoader.java:205)

at java.lang.ClassLoader.loadClass(ClassLoader.java:321)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)

at java.lang.ClassLoader.loadClass(ClassLoader.java:266)

at java.lang.Class.forName0(Native Method)

at java.lang.Class.forName(Class.java:186)

at reflect.Test.main(Test.java:11)

 

我们去bin目录下可以发现这样的文件Outer$StaticInner.class

反射代码只需做如下改动

Class<StaticInner> staticInner = (Class<StaticInner>) Class.forName("reflect.Outer$StaticInner");

.变成了$

这里newInstance之所以能执行,是因为StaticInner有默认构造器

下面看看StaticInner中生命了哪些变量

Class<StaticInner> staticInner = StaticInner.class;
Field[] fields = staticInner.getDeclaredFields();
for (Field field : fields) {
	Class<?> type = field.getType();
	field.setAccessible(true);
	System.out.println(Modifier.toString(field.getModifiers()) + " " + type.getName() + " " + field.getName());
}

StaticInner StaticInnerInstance = staticInner.newInstance();
Field field = staticInner.getDeclaredField("TAG");
boolean accessible = field.isAccessible();
if(!accessible){
	field.setAccessible(true);
}
System.out.println(field.get(StaticInnerInstance));
field.set(StaticInnerInstance, "TAG");
System.out.println(field.get(StaticInnerInstance));
if(!accessible){
	field.setAccessible(false);
}

结果如下

private java.lang.String TAG
StaticInner
StaticInnerTAG
TAG

StaticInner只声明了一个变量,名字为TAG,类型为String

即使是private变量,设置setAccessible(true)之后也能改变其值

普通内部类的反射和上面类似

 

最后说下匿名内部类

匿名内部类的作用是仅仅使用一次,所以也没有必要进行反射,实际上也很难进行反射

例子中有两个匿名内部类

 

TestInterface ti = new TestInterface() {
	public String test() {
		return "this is ti";
	}
};

public String toString(){
	System.out.println("Outer toString");
	return new TestInterface() {
		public String test() {
			return "this is a test!";
		}
	}.test();
}

在bin目录下发现有Outer$1.class和Outer$2.class

 

getDeclaredClasses发现也没有匿名内部类的信息

 

try {
	Class<Outer> outer = Outer.class;
	Class<?>[] classes = outer.getDeclaredClasses();
	for (Class<?> clazz : classes) {
		System.out.println(clazz.getName());
	}
}catch (SecurityException e) {
	e.printStackTrace();
} catch (IllegalArgumentException e) {
	e.printStackTrace();
}

 

结果

 

reflect.Outer$Inner
reflect.Outer$StaticInner
reflect.Outer$TestInterface

 

只有这三个,包括一个接口

如果非要对匿名内部类进行反射,也不是不可能

下面演示如何反射一个匿名内部类,仅仅是演示,没有任何意义,也不推荐这么做

我们以上面的toString方法里面的内部类做例子

 

try {
	Class<?> clazz = Class.forName("reflect.Outer$2");
	Constructor<?>[] constructors = clazz.getDeclaredConstructors();
	constructors[0].setAccessible(true);
	Class<?>[] types = constructors[0].getParameterTypes();
	for (Class<?> type : types) {
		System.out.println("Parameter Types:" + type.getName());
	}
	TestInterface testInterface = (TestInterface) constructors[0].newInstance(new Outer(""));
	Method[] methods = clazz.getDeclaredMethods();
	for (Method method : methods) {
		Class<?>[] parameterTypes = method.getParameterTypes();
		System.out.println(method.getName() + "(" + arrayToString(parameterTypes) + ")");
	}
	Method method = clazz.getDeclaredMethod("test");
	String string = (String)method.invoke(testInterface);
	System.out.println(string);
}catch (SecurityException e) {
	e.printStackTrace();
} catch (IllegalArgumentException e) {
	e.printStackTrace();
} catch (ClassNotFoundException e) {
	e.printStackTrace();
} catch (IllegalAccessException e) {
	e.printStackTrace();
} catch (InvocationTargetException e) {
	e.printStackTrace();
} catch (InstantiationException e) {
	e.printStackTrace();
} catch (NoSuchMethodException e) {
	e.printStackTrace();
}

 

结果如下

 

Parameter Types:reflect.Outer
Testing..
Outer
test()
this is a test!

下面看看代码都做了什么,依次说明

首先获取Outer$2的Class

然后获取其构造器,查看所需要的参数,我们发现内部匿名类也需要一个外部类的支持

然后我们newInstance生成实例,声明为一个接口,由于要传入外部类,这里我们直接new一个,所以会初始化外部类,打印第2,3句

接下来查看内部类的方法,找到test(),这是第4句打印的结果

最后用实例化好的内部匿名类执行这个方法,所以打印出了最后一句

 

执行第一个匿名内部类的方法代码简单些,同样不推荐这么做

 

try {
	Class<Outer> clazz = Outer.class;
	Outer o = new Outer("");
	//System.out.println(o.ti.test());
	Field field = clazz.getDeclaredField("ti");
	TestInterface testInterface = (TestInterface) field.get(o);
	System.out.println(testInterface.test());
}catch (SecurityException e) {
	e.printStackTrace();
} catch (IllegalArgumentException e) {
	e.printStackTrace();
} catch (NoSuchFieldException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} catch (IllegalAccessException e) {
	e.printStackTrace();
}

 

为了演示反射,这里没有使用System.out.println(o.ti.test());

 

至此,反射演示结束

由于写本篇blog的时候多次修改代码,所以上面可能有些地方不太一致,如果发现,敬请指正

 

 

转贴请保留以下链接

本人blog地址

http://su1216.iteye.com/

http://blog.csdn.net/su1216/

分享到:
评论

相关推荐

    Java进阶教程之运行时类型识别RTTI机制

    在Java编程中,运行时类型识别(Runtime Type Identification,简称RTTI)是一种关键特性,它允许程序在运行时检查对象的实际类型。RTTI是Java多态性的重要基础,使得代码能够根据对象的实际类型执行不同的操作。...

    举例讲解Java的RTTI运行时类型识别机制

    Java的RTTI(Runtime Type Information,运行时类型信息)机制是Java语言的一个重要特性,它允许程序在运行时检查对象的实际类型。RTTI使得在编译时未知类型的对象能够在运行时进行适当的处理,从而增强了代码的灵活...

    深入理解Java的反射机制Java系列2021.pdf

    RTTI是Run-time Type Identification的缩写,即运行时类型识别。Java语言通过RTTI可以在运行时知道一个对象的类型信息。RTTI在Java中主要通过两种形式存在:一种是传统的类型转换,也就是instanceof关键字和类的...

    Thinking_in_java(chapter10).pdf

    从给定文件的【部分内容】中我们可以了解到,该文档主要讲述了Java中运行时类型识别(RTTI)的原理和用途,以及Java中的反射机制。在此基础上,我们进一步探讨Java中RTTI和反射的相关知识点。 知识点一:运行时类型...

    Java的RTTI和反射机制代码分析

    Java的RTTI(运行时类型识别)和反射机制是两种强大的工具,它们允许程序在运行时检查和操作类的信息及对象。RTTI是Java的一种特性,它使得在编译后的程序中仍然可以在运行时获取对象的实际类型信息。这在处理多态性...

    JAVA反射机制

    RTTI(RunTime Type Information)是Java在运行时获取类型信息的机制,其使用一种类型对象(Type Object)来实现,这种对象在程序运行时可用于识别类和接口类型。Java使用Class对象来执行RTTI。每个类都有一个Class...

    第11章 运行期类型鉴定.pdf

    Java提供了两种获取运行时类型信息的方式。除了上述的“传统”RTTI,还有Java 1.1引入的**反射(Reflection)**机制。反射允许程序在运行时动态地访问和操作类、接口、字段和方法的信息,即使在编译时这些信息未知。...

    深入理解Java反射

    Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。  1、Class对象  理解RTTI...

    深入理解Java类型信息(Class对象)与反射机制

    本篇主要是深入对Java中的Class对象进行分析,这对后续深入理解反射技术非常重要,主要内容如下:认识Class对象之前,先来了解一个概念,RTTI(Run-TimeTypeIdentification)运行时类型识别,对于这个词一直是C++中...

    Practicla Java.pdf(简体中文版)

    - 动态类型系统:支持运行时类型识别(RTTI)和反射(Reflection)机制。 - 标准库:提供广泛且强大的功能集合。 #### 三、Java编程中的关键概念 - **对象创建与初始化**:讲解如何创建Java对象以及对象初始化的过程...

    Java编程思想学习笔记

    RTTI(Runtime Type Identification)和反射机制则赋予了程序在运行时识别对象类型以及访问类内部信息的能力。泛型编程提高了Java代码的复用性和类型安全性。容器类如List、Set和Map为处理数据集合提供了丰富的方法...

    8第八章知识点整理1

    在Java中,运行时识别对象类型的两种主要方法是RTTI(运行时类型识别)和Java反射。RTTI通常在类型转换时使用,例如从数组中取出元素并自动转型。而反射则更加强大,它允许程序在运行时获取和操作任何类的属性、方法...

    Java编程思想(第四版)1

    第十一章“运行期类型识别”讲解了运行期类型信息(RTTI)和反射,这是动态获取类信息和操作的关键技术。 第十二章“传递和返回对象”讨论了对象的传递方式,包括引用传递、克隆、只读类的设计等,这些都是处理对象...

    第6章补充内容第6章补充内容第6章补充内容

    4. **类型信息**:Java支持运行时类型识别(RTTI),即在程序运行时获取对象和类的信息。RTTI包括传统的类型转换和反射机制。反射允许在运行时动态访问类、接口、字段和方法的信息,创建和调用对象,实现强大的动态...

    Practical Java

    此外,包(package)和导入(import)机制提供了清晰的代码组织结构,而动态类型系统(dynamictype system)则增强了语言的灵活性,支持运行时类型识别(RTTI)和反射(Reflection)机制。 #### Java标准库 Java的标准库是其...

    C++ 反射机制详解及实例代码

    然而C++是不支持反射机制,虽然C++有RTTI(运行时类型识别)。但是想要实现C++对象序列化,序列化就是存储到磁盘上,将对象变成一定格式的二进制编码,然后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的...

    Java开发技术大全 电子版

    10.8.3运行时类型识别320 10.8.4强制类型转换321 10.8.5继承规则322 10.9擦拭323 10.10擦拭带来的错误324 10.10.1静态成员共享问题325 10.10.2重载冲突问题325 10.10.3接口实现问题326 10.11泛型的局限326 ...

Global site tag (gtag.js) - Google Analytics