如果你感觉到图片里有东西在转动,那么在看完这篇博客之后休息吧,你已经累了。
反射,很容易让人与RTTI混淆起来。虽然二者都是获取类型信息的机制,但是二者是存在本质区别的。RTTI(Run-Time Type Information,运行时类型信息)是在编译时获取.class文件,而反射机制在编译阶段是获取不到.class文件的,只有在运行时才能去得到.class文件(当然也有可能找不到)。
接下来,将全面介绍一下反射机制。
什么是反射?
反射,是个简单且神奇的东西。说它简单,是因为它真的不难,虽然很多人一直觉得反射的概念很抽象,而且在普通的编程中很少用到,但是只要鼓起勇气往前走几步,就会看到,原来反射就是用来检查未知类中的基本信息,并予以返回。Eckel曰过:当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类。看吧,仅仅是检查一下而已,简单吧。说它神奇,是因为反射可以让我们获取进行中的类的基本信息,这在C和C++里是不可想象的,而且反射也可以实现RMI(Remote Method Invoke,远程方法调用),我们可以呆在北京调用纽约的类创建和运用对象,想想就觉得神奇额,世界因为我们而变小,不愧是技术宅拯救全世界。我给反射下的定义是:反射,就是获取运行中的程序中一个未知类的基本的信息并对这些基本信息进行操作的机制。
反射怎么用?
那么,既然反射这么有用,要怎么用呢?
RTTI中得到类型信息有两种方法:Class.forName()和使用类字面常量。在反射机制里,考虑到我们在编译时不知道该类是否存在,因此类字面常量的使用在反射里就无从说起了,我们在反射里使用的是Class.forName()方法(而在RTTI中,使用类字面常量效果更好),你会不会在想:如果找不到这个类,会不会在编译时抛出ClassNotFoundException呢?答案是No。因为forName方法在编译时的结果是不可知的。具体的使用,我们将通过以下几个示例介绍:
// 父类
class Father{
String fName;
public int a;
public Father(){}
public Father(String fName){
this.fName = fName;
} // end constructor
void fFun1(){System.out.println("Father.fFun1()");}
void fFun2(String str){System.out.println("Father.fFun2()" + str);}
public int fFun3(){System.out.println("Father.fFun3()");return 0;}
} // end class Father
// 子类
class Son extends Father{
String sStr;
public Son(String sStr){this.sStr = sStr;}
public void sFun1(){System.out.println("Son.sFun1()");}
public void sFun2(int i){System.out.println("Son.sFun2()");}
void sFun3(){System.out.println("Son.sFun3()");}
} // end class Son
以上就是两个测试类的声明。由于反射的概念是靠Class类和java.lang.reflect类库支撑起来的。因此我们要得到类的信息,就需要借助Method、Field以及Constructor等类。首先,我们需要先得到一个Class对象(我们先假设我们不知道这个类里面有什么方法和属性,甚至不知道有木有这个.class文件):
Class c = Class.forName("com.tuyage.typeInfo.Son");
forName方法的参数是Son类的全限定名。接下来,我们先试图得到Son对象的构造器:
Constructor[] cons1 = c.getConstructors();
Constructor[] cons2 = c.getDeclaredConstructors();
System.out.println("#c.getConstructors()");
for(Constructor con: cons1){
System.out.println(con.toString());
} // end for
System.out.println("#c.getDeclaredConstructors()");
for(Constructor con : cons2){
System.out.println(con.toString());
} // end for
可以看到,在这里,我们有两个取得构造器的方法:getConstructors()和getDeclaredConstructors()。等会也会看到Method和Field也有两种方法,下面就是这段代码运行的结果:
#c.getConstructors()
public com.tuyage.typeInfo.Son(java.lang.String)
#c.getDeclaredConstructors()
public com.tuyage.typeInfo.Son(java.lang.String)
两个结果是一样的,其实是有区别的,getConstructors()方法返回的是当前类的所有public的构造器,而getDeclaredConstructors()方法返回的是当前类的所有构造器。此处表现不明显,大家可以自己动手尝试。
接着看Method的获取:
Method[] meth1 = c.getMethods();
Method[] meth2 = c.getDeclaredMethods();
System.out.println("#c.getMethods()");
for(Method m: meth1){
System.out.println(m.toString());
} // end for
System.out.println("#c.getDeclaredMethods()");
for(Method m : meth2){
System.out.println(m.toString());
} // end for
看看运行的结果:
#c.getMethods()
public void com.tuyage.typeInfo.Son.sFun1()
public void com.tuyage.typeInfo.Son.sFun2(int)
public int com.tuyage.typeInfo.Father.fFun3()
public final native java.lang.Class java.lang.Object.getClass()
public native int java.lang.Object.hashCode()
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
#c.getDeclaredMethods()
public void com.tuyage.typeInfo.Son.sFun1()
public void com.tuyage.typeInfo.Son.sFun2(int)
void com.tuyage.typeInfo.Son.sFun3()
看到没,有很多方法名字冒出来了,而且还有很多我们很熟悉的,比如toString()、notify()等等,它们都是Object类的方法,因为,getMethods()方法得到的是该类及其父类的所有public方法,Object作为万物之父,它的方法出现在这里也就不足为奇了。大家还可以看到void com.tuyage.typeInfo.Son.sFun3()方法只出现在了getDeclaredMethods()方法里,因为这个方法是用来得到当前类所有方法的。
接下来,轮到Field出场了,当然是大同小异了,请看代码:
Field[] field1 = c.getFields();
Field[] field2 = c.getDeclaredFields();
System.out.println("#c.getFields()");
for(Field f: field1){
System.out.println(f.toString());
} // end for
System.out.println("#c.getDeclaredFields()");
for(Field f : field2){
System.out.println(f.toString());
} // end for
运行结果如下:
#c.getFields()
public int com.tuyage.typeInfo.Father.a
#c.getDeclaredFields()
java.lang.String com.tuyage.typeInfo.Son.sStr
这个与Method的测试结果又有出入,使用getFields()方法时,并没有出现本类的field,而是父类的public属性,而getDeclaredFields()方法还是一样返回本类的所有属性(此处不明显,大家可以加几个属性试试)。
当然,建议大家一般使用getDeclaredXXXXX()方法。
小贴士:
main方法也可以被继承,因此也会在子类getMethods()方法的结果中也会出现(前提是父类有main方法,但是一般没有人这么做);
获得恰当的Class对象的引用是反射和RTTI的前提和基石;
Class对象仅在需要的时候才加载;
java.lang.reflect类库中的类的对象是由JVM在运行时创建的。
当然,以上还是只是反射的一点点皮毛,还有其他很多东西在此没有一一列举出来。如果想要真正的理解反射,还得多看看相关的资料,多动手实践。另外一个方法就是看java.lang.reflect类库和Class类的源代码了。反射的精彩,等着大家一起去探索!
- 大小: 78.5 KB
分享到:
相关推荐
"浅析Java 反射机制的用途和缺点" Java 反射机制是一种强大的技术,可以让应用程序做一些几乎不可能做到的事情。它可以让程序在运行时检测或修改程序行为,是 Java 语言中一个相对高级的特性。开发者可以使用反射...
Java反射机制是Java编程语言中一个强大的特性,它允许程序在运行时动态地获取类的信息并操作类的对象。反射机制的核心在于,它打破了编译时的束缚,使得代码具有更高的灵活性和可扩展性。在Java中,反射主要依赖于`...
Java反射机制是Java编程语言中一个强大的工具,它允许程序在运行时动态地获取类的信息并操作类的对象。反射机制的核心在于,它打破了程序编译时的静态性,提供了对类、接口、字段和方法的动态访问能力。通过反射,...
Java反射机制(Reflection)是Java语言提供的一种强大的能力,它允许程序在运行时动态地获取类的信息并操作类的对象。这种机制使得Java具备了高度的灵活性和动态性,尤其是在处理未知类或者需要根据条件动态调用类的...
### Android框架浅析之锁屏(Keyguard)机制原理 #### 一、锁屏界面的组成 锁屏(Keyguard)是Android系统中的一个重要组成部分,它主要用于保护用户的隐私数据不被未授权访问。锁屏功能主要由两个部分组成:解锁...
注解的本质是与程序代码关联的特殊标记,它们在编译时或运行时通过反射机制被解析和处理。 1. **元注解** 元注解是用于修饰其他注解的注解,Java内置了四种基本的元注解: - **@Target**:定义注解能被应用到...
7. **Java中的反射机制**:反射允许程序在运行时检查类、接口、字段和方法的信息,动态创建对象并调用方法。它是Java动态性的重要体现,广泛应用于插件系统、框架和测试工具。 8. **Java中Heap与Stack的区别**:堆...
1. **依赖关系**:拦截器是基于Spring框架,依赖于反射机制,仅能处理请求;而过滤器基于Servlet规范,通过回调函数实现,过滤范围更广,可以处理请求和响应。 2. **控制流程**:拦截器可以决定是否继续处理请求,...
概念 代理模式是基本的设计模式之一,它是开发者为了提供额外的或... Java动态代理实现机制采用了反射的思想,有关于反射的基础知识,可以参见博客Java发射机制浅析。 原理 Spring核心AOP实现技术之一是采用
JFinal 是一个基于Java语言的轻量级Web开发框架,其设计目标是简化开发、提高效率,让开发者能够更专注于业务逻辑的实现。本篇文档将对JFinal的技术架构进行浅析,帮助读者理解其核心设计理念和工作原理。 1. **...
首先,我们了解Java的类加载机制。在Java程序运行时,如果要使用某个类,该类必须先被加载到JVM(Java Virtual Machine)中。加载过程包括将类的二进制字节流转化为内存中的数据结构,并创建一个`java.lang.Class`...