通过上两节的介绍,我们已经知道:当要使用某个类时,JVM的类加载器会根据类名找到对应的.class文件,类加载器读取.class文件的内容,并根据该内容在内存中创建一个Class对象,这个Class对象包含了类的全部信息。
看到这里有些小伙伴们激动的说:既然Class对象包含了类的全部信息,那么Class类中肯定有很多方法是用来获取和设置对象类型的吧,我们快去查查Java API文档吧!
这时Class突然从API文档中跳出来,愤怒的大叫:你们这帮傻X,现在看我能干啥有个毛用!你要想用哥,先去研究研究怎么获取哥的引用好不,不然你拿毛来和我通信啊?
Class跷起二郎腿继续说道:其实哥的对象和其它普通对象是一样滴,如果你要调用一个对象的方法,是不是要通过这个对象的引用啊?同理,你要调用哥的对象的方法,也要通过哥的对象的引用。
有三种方法可以获取Class对象的引用,下面分别介绍。
场景一:
当你的程序正在内存中欢快的运行时,突然socket接收到一连串的字节流数据,并被告知这堆字节数据就是Xxx类的.class文件数据,你快去构造一个Xxx类的对象,这样你能成大事。——我擦!!成你妹啊!老子现在都已经被编译好并在内存中运行了,你现在才给我这个.class文件,已经晚了!!!Socket内牛满面的说:sorry啊,可是在你编译的时候,我还没有这个.class文件啊,我怎么才能给你啊!!
正当大家都一筹莫展的时候,Class类发话了:擦!小时一桩啊,哥来帮你们解决这个问题。哥有一个静态方法叫做forName,你只要把类的名字以String的形式告诉它,并且可以在ClassPath的指引下找到对应的.class文件,forName就可以把这个类加载到内存,并为其产生Class对象,然后将Class对象的引用作为返回值交给你们。是不是很牛X啊!好吧,哥给你们看个例子。
public class Test
{
public static void main(String[] args) throws ClassNotFoundException
{
Scanner scan = new Scanner(System.in);
String className = scan.nextLine();
Class clazz = Class.forName(className);
}
}
这个程序在运行时接受用户的输入字符串作为类的名字,然后调用Class的静态方法forName加载这个类。好比说有一个类A的.class文件正躺在ClassPath的某个路径下。类A的源代码如下:
class A
{
static
{
System.out.println("AAAAAA");
}
}
用户在终端输入数据A,可以从输出结果中看到,类A被加载了,并且clazz变量保存着类A的Class对象的引用。
但是你除了告诉我要加载类的名字以外,你还要保证类的.class文件可以在ClassPath路径下被找到,如果找不到对应的.class文件,那我只能翻脸抛出ClassNotFoundException异常了。擦的!告诉我名字,我却找不到对应的.class文件,你TMD逗我玩呢?做事小心点,别逼我翻脸啊!!!!
拿到了一个类的Class对象的引用,就相当于你获得了这个类的全部信息,当然可以构造这个类的对象实例啦!我有一个成员方法叫newInstance,调用这个方法就可以构造相关类的实例对象。我们稍微更改一下上面的代码继续看个例子。
import java.util.*;
public class Test
{
public static void main(String[] args)
throws ClassNotFoundException,InstantiationException,IllegalAccessException
{
Scanner scan = new Scanner(System.in);
String className = scan.nextLine();
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();//构造实例对象
System.out.println(obj);
}
}
class A
{
static
{
System.out.println("AAAAAA");
}
public String toString()
{
return "I am A";
}
}
从输出结果中看以看到,已经构造了类A的实例对象。
总结一下:Class类中有一个静态方法forName,这个方法可以在程序运行期间动态的加载类Xxx产生Class对象,然后通过Class对象的newInstance方法就可以构造Xxx的对象实例了。
场景二:
你已经拥有了一个感兴趣的类型的对象,想要知道这个对象的类型信息,那就可以通过调用对象的getClass方法获取Class对象的应用了。getClass方法是在Object类中定义的,这也就是说,在Java的世界中,任何对象都具有这个方法。下面是一个相关例子和输出结果:
import java.util.*;
public class Test
{
public static void main(String[] args)
throws ClassNotFoundException,InstantiationException,IllegalAccessException
{
A a = new A();
Class clazz = a.getClass();//通过getClass方法获取Class对象的引用
Object o = clazz.newInstance();
System.out.println(o);
}
}
class A
{
static
{
System.out.println("AAAAAA");
}
public String toString()
{
return "I am A";
}
}
场景三、类字面常量
Java还可以使用类字面常量的方式来生成对Class对象的引用,对上面的例子来说就是A.class就是类A的Class对象的引用。这种方式更加简单,而且因为没有方法调用,所以效率会高一些。
import java.util.*;
public class Test
{
public static void main(String[] args)
throws ClassNotFoundException,InstantiationException,IllegalAccessException
{
Class clazz = A.class; //获取Class对象的应用
Object o = clazz.newInstance();
System.out.println(o);
}
}
class A
{
static
{
System.out.println("AAAAAA");
}
public String toString()
{
return "I am A";
}
}
总结:只要你想在运行时获取类型信息,那么第一个想法就是我该如何获取到Class对象的引用,可以通过上面3中方式的任意一种。