RTTI
Java中有两种方式让我们在运行时识别对象的类的信息:传统的RTTI(Run-Time Type Identification)和反射。
Class对象
Class对象包含了与类有关的信息,用来创建类的所有“常规”对象的特殊对象。每一个类都有一个Class对象。
创建Class对象的引用
可以用两种方式来创建Class对象引用:Class.forName()和类字面常量。
创建一个类Farther:
class Farther { Farther() {} static {System.out.println("loading farther");} }
用Class.forName()来创建引用,forName()的参数必须是包含报名的全限定名,并且在找不到类时会抛出ClassNotFoundException:
Class c = null; try { c = Class.forName("typeinfo.toys.Farther"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println("已创建引用"); /* * 输出: * loading farther * 已创建引用 */
用类字面常量创建引用:
Class c = null; c = Farther.class; /* * 输出: * 已创建引用 */
可以发现在使用类字面常量时没有执行Farther类的静态初始化块,这是因为初始化被延迟到了对静态方法或非常数静态域(被static 和 final修饰的常量域)进行首次引用时才执行。
class Farther { static final int test = 1; static final int test2 = (int)(Math.random()*10)+1; Farther() {} static {System.out.println("loading farther");} } public class Test { public static void main(String[] args) { Class c = Farther.class; System.out.println(Farther.test); System.out.println(Farther.test2); } } /* * 输出: * 1 * loading farther * 7 */
Class类的一些常用方法
package tests; interface inte1 {} interface inte2 {} interface inte3 {} class Farther { static final int test = 1; static final int test2 = (int)(Math.random()*10)+1; Farther() {} static {System.out.println("loading farther");} } class Child extends Farther implements inte1, inte2 { Child() {} static {System.out.println("loading child");} } public class ThinkInJavaTest { static void print(Object obj) {System.out.println(obj);} public static void main(String[] args) { Class child = null; try { child = Class.forName("tests.Child"); } catch (ClassNotFoundException e) { e.printStackTrace(); } print("Child getName: " + child.getName()); print("Child getSimpleName: " + child.getSimpleName()); print("Child getCanonicalName: " + child.getCanonicalName()); for(Class inte : child.getInterfaces()) print(inte.getSimpleName()); Class farther = child.getSuperclass(); Object obj = null; try { obj = farther.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } print(obj.getClass().getSimpleName()); Child[] a = new Child[10]; print(a.getClass().getName()); print(a.getClass().getSimpleName()); print(a.getClass().getCanonicalName()); } } /* * 输出: * loading farther * loading child * Child getName: tests.Child * Child getSimpleName: Child * Child getCanonicalName: tests.Child * inte1 * inte2 * Farther * [Ltests.Child; * Child[] * tests.Child[] */
getSimpleName()返回不带包名的类名,getName()和getCanonicalName()返回全限定名。最后几行代码演示了getName()和getCanonicalName()用于数组时的不同。
getInterfaces()以数组形式返回这个类所代表的实体(类,接口,基本数据类型或void)实现的接口的Class对象。
getSuperclass()返回这个类所代表的实体(类,接口,基本数据类型或void)的超类。
newInstance()新建一个此类型的实例,这个类必须有无参构造器;如果这个Class代表抽象类、接口、基本类型、数组、void中的其中一个或者没有无参构造器则会抛出InstantiationException。
Class引用和泛型
我们可以通过泛型语法来限定Class引用所指向的Class对象的类型的类型。
Class a = Child.class; Class<?> b = Child.class; Class<Farther> c = Farther.class; Class<? extends Farther> d = Child.class; Class<? super Child> e = Farther.class; a = Farther.class; b = Farther.class; d = Farther.class; e = Child.class;
其中Class<?> 与普通的Class等价,但是普通的Class会出现警告。
Child虽然是Farther的子类但是不能使用 c = Child.class 因为 Child Class不是Farther Class的子类对象。
下面演示了对不同泛型的Class引用调用newInstance所返回的类型。
try{ Object obj = a.newInstance(); obj = b.newInstance(); Farther far = c.newInstance(); far = d.newInstance(); obj = e.newInstance(); } catch(Exception ex) { print("newInstance 失败"); }
类型检查
instanceof
关键字instanceof返回一个布尔值,告诉我们是不是某个特定类型的实例。
Child c = new Child(); if(c instanceof Child) print("is a child"); if(c instanceof Farther) print("is a farther"); /* loading farther loading child child farther */
但是instanceof只能使用类型名称,这样的话在一些情况下需要写很多的instanceof。
动态的instanceof
也可以使用Class对象的isInstance()方法:
public class Test { static void print(Object obj) {System.out.println(obj);} static boolean judge(Class c, Object obj) { return c.isInstance(obj); } public static void main(String[] args) { Class<Farther> fc = Farther.class; Child ch = new Child(); print(judge(fc, ch)); } } /* loading farther loading child true */
这样就不再需要写很多的instanceof了
直接比较Class对象
当然也可以直接比较Class对象:
public class Test { static void print(Object obj) {System.out.println(obj);} public static void main(String[] args) { Class fc = Farther.class; Child ch = new Child(); Farther fa = new Farther(); print(fc == ch.getClass()); print(fc == fa.getClass()); print(fc.equals(ch.getClass())); print(fc.equals(fa.getClass())); } } /* loading farther loading child false true false true */
与instanceof的区别在于直接比较没有考虑继承,并且使用了泛型语法时可能不能用 == 来比较。