1. 每个类都有一个Class对象,当我们在运行期第一次生成类的实例时,JVM就加载这个类的Class对象,然后用这个Class对象来创建类实例;Class类似于Delphi中的引用类TClass,用于保存类的运行时信息。
2. Class的static方法forName()用于查找某个类的Class对象,如果forName找不到你要的类的Class对象,会抛出ClassNotFoundException异常:
- class A{
- }
- public class Main {
- public static void main(String[] args) {
- try {
- Class<?> cls = Class.forName("A");
- System.out.println(cls.getName());
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
3. 查找类的Class对象的另一个方法是直接调用类.class,这会在编译期就检查是否正确,因此不需要捕获异常:
- class A{
- }
- public class Main {
- public static void main(String[] args) {
- // 类有class关键字
- Class<A> acls = A.class;
- System.out.println(acls.getName());
- // 普通类型也有
- Class<Integer> icls = int.class;
- System.out.println(icls.getName());
- }
- }
4. 判断两个对象是否属于同一个类,可以通过比较他们的class的方式,通过对象的getClass()方法可以得Class对象:
- class A{
- }
- class B extends A {
- }
- public class Main {
- public static void main(String[] args) {
- A a = new B();
- B b = new B();
- // a和b都B的实例,所以为true
- if (a.getClass() == b.getClass())
- System.out.print("yes");
- else
- System.out.print("no");
- }
- }
5. 要判断对象是否继承或属于某个类,可以通过两个方法:1是使用instanceof关键字,2是使用Class.isInstance方法:
- class A{
- }
- class B extends A {
- }
- public class Main {
- public static void main(String[] args) {
- A a = new B();
- B b = new B();
- // 使用instanceof关键字,a是否A或其子类的对象
- if (a instanceof A)
- System.out.println("Yes");
- // 使用instanceof关键字,b是否B或其子类的对象
- if (b instanceof B)
- System.out.println("Yes");
- // 使用isInstance方法
- if (A.class.isInstance(a))
- System.out.println("Yes");
- if (B.class.isInstance(a))
- System.out.println("Yes");
- }
- }
6. Class.NewInstance可以创建一个类实例,该类必须有默认构造函数,否则会抛出InstantiationException异常:
- class A{
- }
- public class Main {
- public static void main(String[] args) {
- try {
- A a = A.class.newInstance();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- }
7. Class还提供了一些更强大的方法,可以得到类的更多信息,比如:getSuperclass: 取得父类的Class对象;getInterfaces: 取得实现的接口数组;还有其他很多方法,详见JavaDoc。
RTTI(Run-Time Type Identification,通过运行时类型识别)的含义就是在运行时识别一个对象的类型,其对应的类是Class对象,每个java里面的类都对应一个Class对象(在编写并且编译后),这个对象被保存在这个类的同名class文件里。
类加载器在类被第一次静态调用(比如一个静态方法,一个静态代码块或者new关键字调用构造器,注意 contructors其实都是静态的)时会把那个对应的Class对象加载到内存中。
Class类有名的一个static方法是forName 。 Class.forName("classname with packagepath");通过这个静态方法返回的一个对类的Class对象的引用, 用户可以在运行时动态得到大量关于这个类的信息,包括接口,父类,方法,静态成员,甚至是像newInstance()方法这样的一个实现“虚拟构造器”的一种方式。所谓的虚拟构造器,就是声明“我不知道你的确切类型,至少在编译期不知道,但是我就是要正确的创建你的一个对象”。
比如说c是某个类的Class对象
Class superc = c.getSuperClass();
Object obj = null;
obj = c.newInstance();
毫无疑问,这个obj实际指向的对象是c的父类的一个对象。
鉴于java的单继承模式,这个父类显然是唯一确定的,尽管在编译时我完全不知道它的类名。
Java提供的另外一种更加安全的得到Class类的对象的方式是,使用类字面常量。
classname.class;
Java在RTTI中的第三种形式是最常见的。就是instanceof关键字,用法就不多说了。该关键字的调用其实就是使用了Class对象,并且返回一个布尔值。如果说instanceof和Class的比较唯一的不同,那就是instanceof如果是其子类的对象也会返回true,而Class对象比较的会是确切的那个类型。
这样得到类的Class对象可以在编译器就得到检查。
RTTI的限制?显然它在编译时必须知道一个非常重要的东西:类名(甚至是全类名)
二、Java的反射
Java中有时候在编译器为程序生成代码很久之后才会出现要处理的那个类,那么这个时候怎么才能处理这个类呢,即在编译的时候根本无法获知这个对象所属的类。答案就是利用Java的反射机制。Java的反射与RTTI的区别就在于编译时不需要知道任何事情,匿名对象的信息在运行时确定下来就可以。
例如:
- public class ShowMethods {
- private static String usage=
- "usage"+
- "showMethods qualified.class.name\n"+
- "To show all methods in class or:\n"+
- "showMethods qualified.class.name word\n"+
- "To search for methods involving 'word'";
- private static Pattern p=Pattern.compile("\\w+\\.");
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- if(args.length<1){
- System.out.println(usage);
- System.exit(0);
- }
- int lines=0;
- try{
- Class<?> c=Class.forName(args[0]);
- Method[] methods=c.getMethods();
- Constructor[] ctors=c.getConstructors();
- if(args.length==1){
- for(Method method:methods)
- System.out.println(p.matcher(method.toString()));
- for(Constructor cotr:ctors)
- System.out.println(p.matcher(cotr.toString()));
- lines=methods.length+ctors.length;
- }else{
- for(Method method:methods)
- if(method.toString().indexOf(args[1])!=-1){
- System.out.println(method.toString());
- lines++;
- }
- }
- for(Constructor ctor:ctors)
- if(ctor.toString().indexOf(args[1])!=-1){
- System.out.println(p.matcher(ctor.toString()).replaceAll(""));
- lines++;
- }
- }catch(ClassNotFoundException e){
- System.out.println("No such class"+e);
- }
- }
//这个时候完全不知道
- Class<?> c=Class.forName(args[0]);
运行时:java ShowMethods ShowMethods
此时第二个ShowMethods为参数传递进去,即运行时才获得了类型信息。这便是反射。