在《Thinking in Java》中,作者提到了RTTI这个概念。但是这个概念是源于C++的,在Java的官方文档中并没有出现这个概念,所以在这一章中,并不会涉及到RTTI的内容。
Class对象是一种特殊的对象,它包含了与类有关的信息。事实上,Class对象就是用来创建类的所有的“常规”对象的。
每当编写并且编译了一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中)。
在Java中,类都是在第一次使用的时候,动态的加载到JVM中的。并非在运行时就将全部内容加载,只有在需要时,才会进行加载。
我们看一段代码
public class Chapter14 {
public static void main(String[] args) {
new Cat();
try {
Class.forName("com.coreJava.test.Dog");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Cat{
static {
System.out.println("得到一只小猫");
}
}
class Dog{
static {
System.out.println("得到一只小狗");
}
}
console:
得到一只小猫
-----------------
得到一只小狗
给Cat和Dog都添加了静态代码块,会在第一次加载的时候输出信息。
首先创建了一个Cat的实例对象,然后使用Class去获取Dog类。可以看到,只有在使用Dog类的时候,静态代码块中的内容才会执行。
注意: 在使用Class.forName(classname)的时候,要使用完全限定名。
class中存储的是类型的信息,我们可以使用获得的class对象创建具体类的实例对象。
public static void main(String[] args) {
Class catClass = Cat.class;
try {
Cat cat = catClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
我们首先获取了Cat的类型信息catClass,随后,我们使用catClass创建了Cat对象。
注意: Class是可以添加泛型的。
可以观察到,我们在使用Class.forName(classname)的时候,会执行静态代码块中的内容,而使用类型.class的时候,并不会执行静态代码块中的内容。
在Java中使用反射,我们主要使用的Class类和java.lang.reflect包。在我看来,反射就是将类中的,方法,成员变量等实例化成对象去使用。
使用反射可以帮助我们做很多平时不能做的事情,例如,调用私有方法。
反射机制是在运行中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射机制主要有以下功能:
举个例子:
public class Cat {
public String name;
private String age;
public void eat() {
System.out.println("小猫吃食物");
}
private void run() {
System.out.println("小猫跑");
}
}
public class Chapter14 {
public static void main(String[] args) {
Class cat = Cat.class;
Field[] fieddArray = cat.getFields();
for (Field field : fieddArray) {
System.out.println(field.toString());
}
Method[] methodArray = cat.getMethods();
for (Method method : methodArray) {
System.out.println(method.toString());
}
Method method;
try {
method = cat.getMethod("eat");
method.invoke(cat.newInstance());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
通常,我们不能调用类的私有方法,但是通过反射,我们是不是可以去使用这些私有方法呢?我们来尝试一下。
如果像上面那样调用public方法的方式去调用private方法会发生什么呢?
console:
java.lang.NoSuchMethodException: com.coreJava.test.Cat.run()
at java.lang.Class.getMethod(Unknown Source)
at com.coreJava.test.Chapter14.main(Chapter14.java:23)
程序会报方法未找到的异常,看起来这样并不能找到私有方法。我们查看下API中的说明:
我们看到API中还有两个方法:
好了,这样我们就可以获取到类中的私有方法并调用了。
Class cat = Cat.class;
try {
Method method = cat.getDeclaredMethod("run");
method.setAccessible(true);
method.invoke(cat.newInstance());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
我们在调用私有方法前使用了setAccessible()这个方法,这是调用私有方法必须的一步,具体大家可以去查看API。
使用反射,可以帮助我们做到很多平时不能做的事情,在这里我也只是起到抛砖引玉的作用,以一个简单的例子来说明如何使用反射,具体更多的用法,大家可以参考官方API。
至于反射在JVM中是如何实现的,在这里就不多说了,一是内容实在太多,放在一篇文章中比较难;二是我自己对这块内容也是处在一知半解的状态,担心出现错误,对大家起到一个错误的引导。