美文网首页
Java编程思想(十三) 类型信息

Java编程思想(十三) 类型信息

作者: kaiker | 来源:发表于2022-06-19 12:56 被阅读0次
    • 运行时类型信息使得你可以在程序运行时发现和使用类型信息。

    1、为什么需要RTTI

    • 在运行时,识别一个对象的类型。

    2、Class对象

    • Class对象就是用来创建所有类的常规对象的。它包含了与类有关的信息。
    • 类是程序的一部分,每个类都有一个Class对象。被保存在同名的.class文件中。
    • 为了生成这个类的对象,运行这个程序的Java虚拟机将使用类加载器子系统。
    • 所有的类都是在对其第一次使用时,动态加载到JVM中。当程序创建第一个对类的静态成员的引用时,就会加载这个类。
    • 一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
    class Candy {
      static { print("Loading Candy"); }
    }
    
    class Gum {
      static { print("Loading Gum"); }
    }
    
    class Cookie {
      static { print("Loading Cookie"); }
    }
    
    public class SweetShop {
      public static void main(String[] args) {  
        print("inside main");
        new Candy();
        print("After creating Candy");
        try {
          Class.forName("Gum");
        } catch(ClassNotFoundException e) {
          print("Couldn't find Gum");
        }
        print("After Class.forName(\"Gum\")");
        new Cookie();
        print("After creating Cookie");
      }
    } /* Output:
    inside main
    Loading Candy
    After creating Candy
    Loading Gum
    After Class.forName("Gum")
    Loading Cookie
    After creating Cookie
    *///:~
    
    • Class.forName()是取得Class对象的引用的一种方法。
    • 只要想在运行时使用类型信息,就必须首先获得对前档的Class对象的引用。
    • Class.getInterface()方法返回的是CLass对象,表示所包含的接口。
    • Class的newInstance()方法是实现虚拟构造的一种途径。使用这种方式创建对象必须有默认构造器。

    类字面常量

    • FancyToy.class,这个也可以获取Class对象引用,叫做类字面常量。
    • 当使用.class来创建Class对象引用时,不会自动初始化该Class对象。
    class Initable {
      static final int staticFinal = 47;
      static final int staticFinal2 =
        ClassInitialization.rand.nextInt(1000);
      static {
        System.out.println("Initializing Initable");
      }
    }
    
    class Initable2 {
      static int staticNonFinal = 147;
      static {
        System.out.println("Initializing Initable2");
      }
    }
    
    class Initable3 {
      static int staticNonFinal = 74;
      static {
        System.out.println("Initializing Initable3");
      }
    }
    
    public class ClassInitialization {
      public static Random rand = new Random(47);
      public static void main(String[] args) throws Exception {
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        // Does not trigger initialization:
        System.out.println(Initable.staticFinal);
        // Does trigger initialization:
        System.out.println(Initable.staticFinal2);
        // Does trigger initialization:
        System.out.println(Initable2.staticNonFinal);
        Class initable3 = Class.forName("Initable3");
        System.out.println("After creating Initable3 ref");
        System.out.println(Initable3.staticNonFinal);
      }
    } /* Output:
    After creating Initable ref
    47
    Initializing Initable
    258
    Initializing Initable2
    147
    Initializing Initable3
    After creating Initable3 ref
    74
    *///:~
    

    泛化的Class引用

    • Class<?>表示使用了一个非具体的类引用,优于Class。?表示任何事物。
    public class WildcardClassReferences {
      public static void main(String[] args) {
        Class<?> intClass = int.class;
        intClass = double.class;
      }
    } ///:~
    
    • 当你将泛型语法用于Class对象时,newInstance()将返回该对象的确切类型。

    3、类型转换前先做检查

    已知的RTTI形式包括:

    • 传统类型转换 (xxx)这种。
    • 代表对象的类型的Class对象。
    • instanceof关键字,返回布尔值表示对象是否是特定类型的实例。只可将其与命名类型进行比较,不能与Class对象比较。

    4、注册工厂

    public interface Factory<T> { T create(); } ///:~
    
    class Part {
      public String toString() {
        return getClass().getSimpleName();
      }
      static List<Factory<? extends Part>> partFactories =
        new ArrayList<Factory<? extends Part>>();   
      static {
        // Collections.addAll() gives an "unchecked generic
        // array creation ... for varargs parameter" warning.
        partFactories.add(new FuelFilter.Factory());
        partFactories.add(new AirFilter.Factory());
        partFactories.add(new CabinAirFilter.Factory());
        partFactories.add(new OilFilter.Factory());
        partFactories.add(new FanBelt.Factory());
        partFactories.add(new PowerSteeringBelt.Factory());
        partFactories.add(new GeneratorBelt.Factory());
      }
      private static Random rand = new Random(47);
      public static Part createRandom() {
        int n = rand.nextInt(partFactories.size());
        return partFactories.get(n).create();
      }
    }   
    
    class Filter extends Part {}
    
    class FuelFilter extends Filter {
      // Create a Class Factory for each specific type:
      public static class Factory
      implements typeinfo.factory.Factory<FuelFilter> {
        public FuelFilter create() { return new FuelFilter(); }
      }
    }
    
    class AirFilter extends Filter {
      public static class Factory
      implements typeinfo.factory.Factory<AirFilter> {
        public AirFilter create() { return new AirFilter(); }
      }
    }   
    
    class CabinAirFilter extends Filter {
      public static class Factory
      implements typeinfo.factory.Factory<CabinAirFilter> {
        public CabinAirFilter create() {
          return new CabinAirFilter();
        }
      }
    }
    
    class OilFilter extends Filter {
      public static class Factory
      implements typeinfo.factory.Factory<OilFilter> {
        public OilFilter create() { return new OilFilter(); }
      }
    }   
    
    class Belt extends Part {}
    
    class FanBelt extends Belt {
      public static class Factory
      implements typeinfo.factory.Factory<FanBelt> {
        public FanBelt create() { return new FanBelt(); }
      }
    }
    
    class GeneratorBelt extends Belt {
      public static class Factory
      implements typeinfo.factory.Factory<GeneratorBelt> {
        public GeneratorBelt create() {
          return new GeneratorBelt();
        }
      }
    }   
    
    class PowerSteeringBelt extends Belt {
      public static class Factory
      implements typeinfo.factory.Factory<PowerSteeringBelt> {
        public PowerSteeringBelt create() {
          return new PowerSteeringBelt();
        }
      }
    }   
    
    public class RegisteredFactories {
      public static void main(String[] args) {
        for(int i = 0; i < 10; i++)
          System.out.println(Part.createRandom());
      }
    } /* Output:
    GeneratorBelt
    CabinAirFilter
    GeneratorBelt
    AirFilter
    PowerSteeringBelt
    CabinAirFilter
    FuelFilter
    PowerSteeringBelt
    PowerSteeringBelt
    FuelFilter
    *///:~
    

    5、instanceof与Class的等价性

    • instanceof和isInstance()生成的结果完全一样。
    • equals == 没有办法判定子类。
    class Base {}
    class Derived extends Base {}   
    
    public class FamilyVsExactType {
      static void test(Object x) {
        print("Testing x of type " + x.getClass());
        print("x instanceof Base " + (x instanceof Base));
        print("x instanceof Derived "+ (x instanceof Derived));
        print("Base.isInstance(x) "+ Base.class.isInstance(x));
        print("Derived.isInstance(x) " +
          Derived.class.isInstance(x));
        print("x.getClass() == Base.class " +
          (x.getClass() == Base.class));
        print("x.getClass() == Derived.class " +
          (x.getClass() == Derived.class));
        print("x.getClass().equals(Base.class)) "+
          (x.getClass().equals(Base.class)));
        print("x.getClass().equals(Derived.class)) " +
          (x.getClass().equals(Derived.class)));
      }
      public static void main(String[] args) {
        test(new Base());
        test(new Derived());
      } 
    } /* Output:
    Testing x of type class typeinfo.Base
    x instanceof Base true
    x instanceof Derived false
    Base.isInstance(x) true
    Derived.isInstance(x) false
    x.getClass() == Base.class true
    x.getClass() == Derived.class false
    x.getClass().equals(Base.class)) true
    x.getClass().equals(Derived.class)) false
    Testing x of type class typeinfo.Derived
    x instanceof Base true
    x instanceof Derived true
    Base.isInstance(x) true
    Derived.isInstance(x) true
    x.getClass() == Base.class false
    x.getClass() == Derived.class true
    x.getClass().equals(Base.class)) false
    x.getClass().equals(Derived.class)) true
    *///:~
    

    6、反射

    • 如果不知道某个对象的确切类型,RTTI可以告诉你,但是有限制:这个类型在编译时必须已知,这样RTTI才能识别。
    • 如果获取了一个指向某个并不在你的程序空间中对象的引用,比如从网络获取了一串字节并被告知这是个类,如何才能使用。
    • Class类与java.lang.reflect类库一起对反射概念进行了支持,该类库包含了Field、Method以及Constructor类。可以使用Constructor创建新对象,用get set方法读取修改Field对象关联字段,用invoke()方法调用与Method对象关联的方法。
    • 反射机制并没有什么神奇之处,用它做其他事情之前必须先加载那个类的Class对象,因此那个类的.class文件对于JVM来说必须是可获取的。对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查文件。
    public class ShowMethods {
      private static String usage =
        "usage:\n" +
        "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+\\.");
      public static void main(String[] args) {
        if(args.length < 1) {
          print(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)
              print(
                p.matcher(method.toString()).replaceAll(""));
            for(Constructor ctor : ctors)
              print(p.matcher(ctor.toString()).replaceAll(""));
            lines = methods.length + ctors.length;
          } else {
            for(Method method : methods)
              if(method.toString().indexOf(args[1]) != -1) {
                print(
                  p.matcher(method.toString()).replaceAll(""));
                lines++;
              }
            for(Constructor ctor : ctors)
              if(ctor.toString().indexOf(args[1]) != -1) {
                print(p.matcher(
                  ctor.toString()).replaceAll(""));
                lines++;
              }
          }
        } catch(ClassNotFoundException e) {
          print("No such class: " + e);
        }
      }
    } /* Output:
    public static void main(String[])
    public native int hashCode()
    public final native Class getClass()
    public final void wait(long,int) throws InterruptedException
    public final void wait() throws InterruptedException
    public final native void wait(long) throws InterruptedException
    public boolean equals(Object)
    public String toString()
    public final native void notify()
    public final native void notifyAll()
    public ShowMethods()
    *///:~
    

    7、动态代理

    • 代理是基本的设计模式之一,它是你为了提供额外或不同的操作,而插入的用来代替实际对象的对象。
    • 传了需要代理的对象,通过反射调用方法。
    class MethodSelector implements InvocationHandler {
      private Object proxied;
      public MethodSelector(Object proxied) {
        this.proxied = proxied;
      }
      public Object
      invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
        if(method.getName().equals("interesting"))
          print("Proxy detected the interesting method");
        return method.invoke(proxied, args);
      }
    }   
    
    interface SomeMethods {
      void boring1();
      void boring2();
      void interesting(String arg);
      void boring3();
    }
    
    class Implementation implements SomeMethods {
      public void boring1() { print("boring1"); }
      public void boring2() { print("boring2"); }
      public void interesting(String arg) {
        print("interesting " + arg);
      }
      public void boring3() { print("boring3"); }
    }   
    
    class SelectingMethods {
      public static void main(String[] args) {
        SomeMethods proxy= (SomeMethods)Proxy.newProxyInstance(
          SomeMethods.class.getClassLoader(),
          new Class[]{ SomeMethods.class },
          new MethodSelector(new Implementation()));
        proxy.boring1();
        proxy.boring2();
        proxy.interesting("bonobo");
        proxy.boring3();
      }
    } /* Output:
    boring1
    boring2
    Proxy detected the interesting method
    interesting bonobo
    boring3
    *///:~
    

    8、接口与类型信息

    • interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合性。但是通过类型信息,这种耦合性还是会传播出去。
    • 可以看到,private方法都可以通过反射调用。
    • 私有内部类、private域都不能阻止反射访问。
    class C implements A {
      public void f() { print("public C.f()"); }
      public void g() { print("public C.g()"); }
      void u() { print("package C.u()"); }
      protected void v() { print("protected C.v()"); }
      private void w() { print("private C.w()"); }
    }
    
    public class HiddenC {
      public static A makeA() { return new C(); }
    } ///:~
    
    public class HiddenImplementation {
      public static void main(String[] args) throws Exception {
        A a = HiddenC.makeA();
        a.f();
        System.out.println(a.getClass().getName());
        // Compile error: cannot find symbol 'C':
        /* if(a instanceof C) {
          C c = (C)a;
          c.g();
        } */
        // Oops! Reflection still allows us to call g():
        callHiddenMethod(a, "g");
        // And even methods that are less accessible!
        callHiddenMethod(a, "u");
        callHiddenMethod(a, "v");
        callHiddenMethod(a, "w");
      }
      static void callHiddenMethod(Object a, String methodName)
      throws Exception {
        Method g = a.getClass().getDeclaredMethod(methodName);
        g.setAccessible(true);
        g.invoke(a);
      }
    } /* Output:
    public C.f()
    typeinfo.packageaccess.C
    public C.g()
    package C.u()
    protected C.v()
    private C.w()
    *///:~
    

    相关文章

      网友评论

          本文标题:Java编程思想(十三) 类型信息

          本文链接:https://www.haomeiwen.com/subject/kdvkvrtx.html