zoukankan      html  css  js  c++  java
  • 【Java编程思想】类型信息

    这是《Java 编程思想》第四版的阅读笔记,Java 版本可能有点过时了,但是思想不过时。这一章主要介绍 RTTI 和反射机制。下面的内容太过于概括,小心查看~

    思维导图

    为什么需要 RTTI

    一句话:因为我们确实需要在运行时获取类型信息。

    RTTI,是 Run-Time Type Information 的缩写。为什么需要?因为在使用继承的时候,免不了将类进行向上转型,在转型后,我们就不知道具体的类型了。使用 RTTI 的作用就是,帮助我们获取这个具体的类型。举个例子,有个挑食的人,他不想吃 peach 了,怎么办呢?

    我们假设我们有如下的类结构。

    abstract class Food {}
    
    class Apple extends Food {}
    
    class Peach extends Food {}
    
    class Pear extends Food {}
    

    现在,我们创建一个 List,里面存放各种食物,要把 Peach 丢掉。可以使用 instanceof 关键字。判断一个对象是否属于某一个类型。举这个例子的目的是:我们确实需要运行时类型信息(RTTI)

    List<Food> foods = new ArrayList<Food>();
    foods.add(new Apple());
    foods.add(new Peach());
    foods.add(new Pear());
    
    for (Food food : foods) {
        if (food instanceof Peach) {
            System.out.println("不想吃 peach");
        }
    }
    

    Class 类

    每个类都有一个 Class 对象,这个 Class 对象用于保存这个类相关的信息。

    获取 Class 对象的方法

    有三种。Class 类中的 ForName,类的 .class 字段(对于基本类型的包装类还有 TYPE 字段),对象的 getClass 方法。

    BTW,Boolean.class 不等于 Boolean.TYPE,boolean.class 才等于 Boolean.Type。

    动态加载和初始化

    JVM 提供了类的动态加载机制,当类第一次被使用的时候,进行加载。比如,使用 ForName 的时候,如果 JVM 里面没有这个类,那么会加载它。

    使用 .class 类字面常量的时候,初始化被推迟到了对静态方法或者非常数静态域(非 final 的静态变量)进行首次引用时才进行。

    泛化的 Class 引用

    提供了编译期类型检查,防止 Class 的对象被赋予了不同的类型。

    类型检查

    有两种方法,一个是使用关键字 instanceof, 一个是使用 Class 的方法 isInstance。

    等价性

    14.5 节的内容提到这里来。14.5 讲的是,instanceof 和 isInstance 使用的结果上是一致的,但是如果使用 equals== 来直接比较类的对象的时候,这和 instanceof 是有区别的。instanceof 保存了类的概念,但是 Class 对象的比较是没有类的概念的。我们有 apple.getClass() != Food.class,但是如果使用 instanceof 我们是可以得到 apple 确实是 food 的实例(instance)。

    反射:运行时的类信息

    使用 Class 和 java.lang.reflect 类库,可以获取运行时类的信息,这个类库有 Field、Method、Constructor 类。在需要使用类内部的属性、方法、构造器的时候,查查 Class 的文档,看看它提供的方法是否可以获取到想要的信息。

    动态代理

    使用方法

    有点类似 AOP 的一个东西。书中进行讲了个用法的例子。

    1. 我们需要实现一个 InvocationHandler,覆盖其中的 invoke 方法。当对象被调用的时候,就会进入这个方法
    2. 创建被代理类的对象。
    3. 使用 Proxy 的静态方法 newProxyInstance。传入需要的三个参数:classLoader, Class 数组, Handler。

    使用例子:

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    interface Interface {
        void doSomething();
        void somethingElse(String arg);
    }
    
    
    class RealObject implements Interface {
    
        public void doSomething() {
            System.out.println("do something");
        }
    
        public void somethingElse(String arg) {
            System.out.println("do something else: " + arg);
        }
    }
    
    
    class DynamicProxyHandler implements InvocationHandler {
        private Object proxied;
    
        public DynamicProxyHandler(Object proxied) {
            this.proxied = proxied;
        }
    
        public Object invoke(Object object, Method method, Object[] args) throws Throwable {
            if (method.getName() == "doSomething") {
                return null;
            }
            System.out.println("proxy...");
            return method.invoke(proxied, args);
        }
    }
    
    public class SimpleDynamicProxy {
        public static void consumer(Interface iface) {
            iface.doSomething();
            iface.somethingElse("simple");
        }
    
        public static void main(String[] args) {
            RealObject real = new RealObject();
            consumer(real);
    
            Object proxy = Proxy.newProxyInstance(
                    Interface.class.getClassLoader(),
                    new Class[] {Interface.class},
                    new DynamicProxyHandler(real)
            );
            consumer((Interface) proxy);
        }
    }
    
    

    原理

    也许你会好奇,当我们调用代理的方法的时候,我是怎么进入到 invoke 中的呢?

    这涉及动态代理的实现原理。如果进入到 Proxy.newProxyInstance 去看一眼,不断地跟踪下去,就会发现,这个动态代理的类,是生成的!网上的一些讲解,都会先产生一个字节码,然后进行一下反编译,具体可以参考[1]

    空对象

    使用 null 可能带来一些不便,我们可以使用一个空对象来代替它,这样我们就可以避免了每次都去检查 null。

    在很多地方,我们还是需要检查是否空对象,引入了空对象还是有一些好处的,比如 toString 方法就不需要检查了。

    实现方法是,搞一个 Null 接口,然后实现一个静态内部类,用这个静态内部类创建一个空对象的单例模式。下面做出了简化。当需要搞一个空对象,使用 Person.NULL;当需要检查是否空对象,使用 instanceof Null

    这一节讲的具有一定的启发性,但是在实际开发中,究竟该如何正确使用呢?这还需要多多实践看看。

    interface Null {}
    
    class Person {
        public Person() {}
        private static class NullPerson extends Person implements Null {}
        public static final Person NULL = new NullPerson();
    }
    
    

    接口和类型信息

    这一节,概括起来那就是,反射牛逼,任你怎么封装继承,都阻止不了反射获取内部信息。

    对了,使用私有成员的时候,记得调用 setAccessible(true),这样就可以获取类内部的任何信息啦。

    参考链接

    [1] https://www.jianshu.com/p/23d3f1a2b3c7

  • 相关阅读:
    Ubuntu搭建FTP服务器
    【wireshark】wireshark 出现There are no interfaces on which a capture can be done.的解决方法
    那些强悍的PHP一句话后门
    Ubuntu下SSH设置
    Servlet&JSP中的知识点
    String s=new String("abc")创建了几个对象?
    JavaEE的13种核心技术
    ++i和i++
    构造方法
    TCP/IP
  • 原文地址:https://www.cnblogs.com/zzk0/p/14075935.html
Copyright © 2011-2022 走看看