Java类型信息

类型信息

1、RTTI

  RTTI(Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息。

  多态(polymorphism)是基于RTTI实现的。RTTI的功能主要是由Class类实现的。

2、Class对象

  类是程序的一部分,每个类都有一个Class对象。Class对象就是用来创建类的所有的“常规”对象的,它包含了与类有关的信息。Java使用Class对象来执行其RTTI。

 1 public class Test {
 2     public static void main(String[] args) {
 3         Class<?> c = null;
 4         try {
 5             c = Class.forName("com.my.chapter14.Man");
 6         } catch(ClassNotFoundException e) {
 7             System.out.println("Can't find Man");
 8             return;
 9         }
10 11         System.out.println("Class name: " + c.getName() +
12                 " is interface? [" + c.isInterface() + "]");
13         System.out.println("Simple name: " + c.getSimpleName());
14         System.out.println("Canonical name : " + c.getCanonicalName());
15         System.out.println("");
16         for(Class<?> face : c.getInterfaces())
17             System.out.println(face.getSimpleName());
18         System.out.println("");
19         
20         Class<?> up = c.getSuperclass();
21         Object obj = null;
22         try {
23             // Requires default constructor:
24             obj = up.newInstance();
25         } catch(InstantiationException e) {
26             System.out.println("Cannot instantiate");
27             return;
28         } catch(IllegalAccessException e) {
29             System.out.println("Cannot access");
30             return;
31         }
32         System.out.println(obj.getClass());
33     }
34 }
35 36 interface HasHouse{}
37 38 class Human{
39     Human(){}
40     Human(int i){}
41 }
42 43 class Man extends Human implements HasHouse{
44     Man(){super(1);}
45 }
46 47 /*Output:
48 Class name: com.my.chapter14.Man is interface? [false]
49 Simple name: Man
50 Canonical name : com.my.chapter14.Man
51 52 HasHouse
53 54 class com.my.chapter14.Human
55 */

 

3、类字面常量

  通过类字面常量可以生成对Class对象的引用。

  当通过“.class”来创建对Class对象的引用时,不会自动地初始化Class对象,初始化延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时。Class.forName()立即就进行了初始化,而仅使用.class语法来获得对类的引用不会引发初始化。

  如果static final值是“编译期常量”,那么不需要初始化就可以被读取0,如staticFinal。如果只是将一个域设置为static和final,访问时将强制进行类的初始化,如staticFinal2。

1 static final int staticFinal = 6;
2 static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);

  泛化的Class引用。Class<?>表示任何类型;Class<? extends Number>表示Number的子类;Class<? super FancyToy>表示FancyToy的父类。

 

4、注册工厂

  一个父类可能会派生出许多子类,当有新的子类产生时,需要将其添加进相应的列表中才便于管理和创建。因此这个列表应置于一个位于中心的、位置明显的地方,而基类就是这个最佳位置。

  因此为了便于随机创建各个子类,需要运用工厂方法设计模式,将对象的创建工作交给类自己去完成。

 1 interface Factory<T>{
 2     T create();
 3 }
 4  5 class Parts{
 6     @Override
 7     public String toString() {
 8         return getClass().getSimpleName();
 9     }
10 11     static List<Factory<? extends Parts>> partFactories = new ArrayList<Factory<? extends Parts>>();
12     static {
13         partFactories.add(new FuelFilter.Factory());
14         partFactories.add(new AirFilter.Factory());
15         partFactories.add(new CabinAirFilter.Factory());
16     }
17     private static Random rand = new Random(47);
18     public static Parts createRandom(){
19         int n = rand.nextInt(partFactories.size());
20         return partFactories.get(n).create();
21     }
22 }
23 24 class Filter extends Parts{}
25 26 class FuelFilter extends Filter{
27     public static class Factory implements com.my.chapter14.Factory<FuelFilter>{
28         @Override
29         public FuelFilter create() {
30             return new FuelFilter();
31         }
32     }
33 }
34 35 class AirFilter extends Filter{
36     public static class Factory implements com.my.chapter14.Factory<AirFilter>{
37         @Override
38         public AirFilter create() {
39             return new AirFilter();
40         }
41     }
42 }
43 44 class CabinAirFilter extends Filter{
45     public static class Factory implements com.my.chapter14.Factory<CabinAirFilter>{
46         @Override
47         public CabinAirFilter create() {
48             return new CabinAirFilter();
49         }
50     }
51 }
52 53 public class Test {
54     public static void main(String[] args) {
55         Parts part;
56         for(int i = 0; i < 20; i++) {
57             part = Parts.createRandom();        System.out.print(part.getClass().getSimpleName() + " ");
58         }
59     }
60 }

 

5、反射:运行时的类信息

  有时你从磁盘文件,或者网络连接中获取了一串字节,并且被告知这些字节代表一个类,而这个类在编译器为你的程序生成代码之后很久才出现,如何使用这样的类呢?通过反射机制可以解决该问题。对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。对RTTI来说,编译器在编译时打开和检查.class文件。

 

6、动态代理

  代理就是将额外的操作从“实际”对象中分离到不同的地方,并且易修改。

  通过Proxy.newProxyInstance()方法可以创建动态代理,这个方法需要一个类加载器,一个你希望该代理实现的接口列表,以及InvocationHandler接口的一个实现。通过Method.invoke()方法将请求转发给被代理的对象,并传入必需的参数。

 1 interface Interface {
 2     void doSomething();
 3     void somethingElse(String arg);
 4 }
 5  6 class RealObject implements Interface {
 7     public void doSomething() {
 8         System.out.println("doSomething"); }
 9     public void somethingElse(String arg) {
10         System.out.println("somethingElse " + arg);
11     }
12 }
13 14 class DynamicProxyHandler implements InvocationHandler {
15     private Object proxied;
16     public DynamicProxyHandler(Object proxied) {
17         this.proxied = proxied;
18     }
19     public Object
20     invoke(Object proxy, Method method, Object[] args)
21             throws Throwable {
22         System.out.println("**** proxy: " + proxy.getClass() +
23                 ", method: " + method + ", args: " + args);
24         if(args != null)
25             for(Object arg : args)
26                 System.out.println(" " + arg);
27         Object ret = method.invoke(proxied, args);
28         return ret;
29     }
30 }
31 32 public class practice22 {
33     public static void consumer(Interface iface) {
34         iface.doSomething();
35         iface.somethingElse("bonobo");
36     }
37 38     public static void main(String[] args) {
39         RealObject real = new RealObject();
40         Interface proxy = (Interface) Proxy.newProxyInstance(
41                 Interface.class.getClassLoader(),
42                 new Class<?>[]{ Interface.class },
43                 new DynamicProxyHandler(real));
44         consumer(proxy);
45     }
46 }

 

7、接口与类型信息

  通过反射可以调用标识为private或包访问权限的方法。

 1 package package1;
 2 public interface A{
 3     void f();
 4 }
 5  6 package package1;
 7 public class HiddenC{
 8     public static A makeA(){
 9         return new C();
10     }
11 }
12 13 class C implements A{
14 15     @Override
16     public void f() {
17         System.out.println("public void C.f()");
18     }
19     public void g(){
20         System.out.println("public void C.g()");
21     }
22     void u(){
23         System.out.println("void C.u()");
24     }
25     protected void v(){
26         System.out.println("protected void C.v()");
27     }
28     private void w(){
29         System.out.println("private void C.w()");
30     }
31 }
32 33 package package2;
34 public class Test {
35     static void callHiddenMethod(Object a, String methodName)
36             throws Exception {
37         Method g = a.getClass().getDeclaredMethod(methodName);
38         g.setAccessible(true);
39         g.invoke(a);
40     }
41 42     public static void main(String[] args) throws Exception {
43         A a = HiddenC.makeA();
44         a.f();
45         callHiddenMethod(a,"g");
46         callHiddenMethod(a,"u");
47         callHiddenMethod(a,"v");
48         callHiddenMethod(a,"w");
49     }
50 }
51 52 /*Output:
53 public void C.f()
54 public void C.g()
55 void C.u()
56 protected void C.v()
57 private void C.w()
58 */

 

 参考于《Java编程思想》,第313~351页

posted @ 2021-06-21 16:34  sumAll  阅读(51)  评论(0编辑  收藏  举报