《Java编程思想》笔记14-类型信息

  1. RTTI
  2. Class 对象
    1. 获取 Class 对象
    2. Class 类的常用方法
    3. 泛化 Class 对象
  3. 类型检查
    1. instanceof
    2. isInstance()
    3. isAssignableFrom
  4. 反射
    1. 类方法提取器
  5. 代理
    1. 代理设计模式
    2. 动态代理

运行时类型信息使得你可以在程序运行时发现和使用类型信息。

Java 是如何让我们在运行时识别对象和类的信息的。主要是两种方式:

  1. “传统的”RTTI,它假定我们在编译时就已经知道了所有的类型。
  2. “反射”机制,运行我们在运行时发现和使用类的信息。

RTTI

为什么要使用 RTTI:如果想知道某个泛化引用的确切类型,如 list 中存放着多种 Shape ,想将其中的三角形变种颜色。多态无法解决这种问题,使用 RTTI,可以查询某个 Shape 引用所指向的对象的确切类型,然后进行操作。

Class 对象

每个类都用一个 Class 对象。为了生成这个类的对象,运行这个程序的 Java 虚拟机(JVM)将使用被称为“类加载器”的子系统。

当程序创建第一个对类的静态成员的引用时,就会加载这个类(new 也一样,可以理解为构造器也是静态的)初始化。

如果类中有一个 static final 值是编译期常量,则不会引发对该类进行初始化就能读取

获取 Class 对象

  1. Class 类中存在静态方法 static Class<?> forName(String className):返回与带有给定字符串名的类或接口相关联的 Class 对象。会立即进行初始化
  2. 类名.class:获得 Class 对象引用。不会引发初始化
  3. 如果已经有了该类的对象,调用 getClass() 方法来获得 Class 引用。

Class 类的常用方法

  • String getName():返回此类的全限定名(包名+类名)
  • String getSimpleName():返回此类的类名
  • boolean isInterface():判定此类是不是接口
  • Class<?>[] getInterfaces():返回此类实现的接口的 Class 类对象
  • Class<? super T> getSuperClass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。如果此 Class 表示 Object 类、一个接口、一个基本类型或 void,则返回 null。
  • T newInstance():返回此类的一个对象。此类必须有默认构造器

泛化 Class 对象

Class intClass = int.class;
Class<Integer> genericIntClass = int.class;

// Class<Number> genericNumberClass = int.class; //编译不通过,因为 Integer.class 不是 Number.class 的子类

// 可以用“通配符”来代替。代表随便什么类型的 Class 类对象。
Class<?> c = int.class;
// Class<?> 等同与平凡的 Class,因为使用 Class<?> 代表了你就是选择了非具体类型的版本,而不是因为疏忽。

Class<? extends Number> bounded = int.class; // 该类型或其子类
bounded = double.class;
bounded = Number.class;
Class<FancyToy> ftClass = FancyToy.class;
FancyToy fancyToy = ftClass.newInstance(); // 可以指定具体类型
Class<? super FancyToy> up = ftClass.getSuperclass();
// Class<Toy> up2 = ftClass.getSuperclass(); // 无法编译
Object obj = up.newInstance(); // 无法指定具体类型

类型检查

instanceof

instanceof 关键字可以判断对象是不是某个类型的实例。

if (x instanceof Dog) {
    ((Dog)x).bark();
}

isInstance()

另一种类型检查方式: Class 对象的 isInstance() 方法:检查是否是某个类的实例。

Triangle t = new Triangle();
Class c = Circle.class;
c.isInstance(t);

isAssignableFrom

boolean isAssignableFrom(Class<?> cls):判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。

Class s = Pet.class;
Class c = Dog.class;
c.isAssignableFrom(s); // false
s.isAssignableFrom(c); // true

反射

如果不知道某个对象的具体类型,RTTI 可以告诉你。但是这个类型必须在编译时已知,这样 RTTI 才能识别它。

有时根本无法获知这个对象的所属类。

Claas 类与 java.lang.reflect 类库一起对反射提供了支持,该类库包含了:

  • Field:用 get() 和 set() 方法读取和修改与 Field 对象关联的字段。
  • Method:用 invoke() 方法调用与 Method 相关的方法
  • Constructor:可以创建新的对象

每个类都实现了 Member 接口。

在 Method 对象上调用 setAccessible(true),就可以调用所有的方法,包含 private 方法。

类方法提取器

public class ShowMethods {
  private static Pattern p = Pattern.compile("\\w+\\.");
  public static void main(String[] args) {
    String classname = "ShowMethods";
    try {
      Class<?> c = Class.forName(classname);
      Method[] methods = c.getMethods();
      Constructor[] ctors = c.getConstructors();
      for(Method method : methods)
        print(p.matcher(method.toString()).replaceAll(""));
      print();

      for(Constructor ctor : ctors)
        print(p.matcher(ctor.toString()).replaceAll(""));
    } catch(ClassNotFoundException e) {
      print("No such class: " + e);
    }
  }
} /* Output:
public static void main(String[])
public final void wait() throws InterruptedException
public final void wait(long,int) throws InterruptedException
public final native void wait(long) throws InterruptedException
public boolean equals(Object)
public String toString()
public native int hashCode()
public final native Class getClass()
public final native void notify()
public final native void notifyAll()

public ShowMethods()
*///:~

代理

代理设计模式

interface Interface {
  void doSomething();
  void somethingElse(String arg);
}

class RealObject implements Interface {
  public void doSomething() { print("doSomething"); }
  public void somethingElse(String arg) {
    print("somethingElse " + arg);
  }
}
class SimpleProxy implements Interface {
  private Interface proxied;
  public SimpleProxy(Interface proxied) {
    this.proxied = proxied;
  }
  public void doSomething() {
    print("SimpleProxy doSomething");
    proxied.doSomething();
  }
  public void somethingElse(String arg) {
    print("SimpleProxy somethingElse " + arg);
    proxied.somethingElse(arg);
  }
}
public class SimpleProxyDemo {
  public static void consumer(Interface iface) {
    iface.doSomething();
    iface.somethingElse("bonobo");
  }
  public static void main(String[] args) {
    consumer(new RealObject());
    consumer(new SimpleProxy(new RealObject()));
  }
} /* Output:
doSomething
somethingElse bonobo
SimpleProxy doSomething
doSomething
SimpleProxy somethingElse bonobo
somethingElse bonobo
*///:~

动态代理

Java 的动态代理比代理的思想更近了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的政策。

class DynamicProxyHandler implements InvocationHandler {
  private Object proxied;
  public DynamicProxyHandler(Object proxied) {
    this.proxied = proxied;
  }
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args);
    if(args != null)
      for(Object arg : args)
        System.out.println("参数值:" + arg);
    return method.invoke(proxied, args);
  }
}

public class SimpleDynamicProxy {
  public static void consumer(Interface iface) {
    iface.doSomething();
    iface.somethingElse("bonobo");
  }
  public static void main(String[] args) {
    RealObject real = new RealObject();
    consumer(real);

    Interface proxy = (Interface)Proxy.newProxyInstance(
      Interface.class.getClassLoader(),
      new Class[]{ Interface.class },
      new DynamicProxyHandler(real)); // 创建动态代理

    consumer(proxy);
  }
} /* Output: (95% match)
doSomething
somethingElse bonobo
**** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null
doSomething
**** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816
参数值:bonobo
somethingElse bonobo
*///:~


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 bin07280@qq.com

文章标题:《Java编程思想》笔记14-类型信息

文章字数:1.5k

本文作者:Bin

发布时间:2018-05-08, 21:47:45

最后更新:2019-08-06, 00:46:42

原始链接:http://coolview.github.io/2018/05/08/Java%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3/%E3%80%8AJava%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E3%80%8B%E7%AC%94%E8%AE%B014-%E7%B1%BB%E5%9E%8B%E4%BF%A1%E6%81%AF/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录