• Java笔记04


    反射

    • 程序在运行期间可以拿到一个对象的所有信息
    • 为了解决在运行期, 对某个实例一无所知的情况下, 如何调用其方法

    Class类

    • 除了int等基本类型, 其他类型都是class
    • class的本质是数据类型(Type). 无继承关系的数据类型无法赋值
    • class是JVM在执行过程中动态加载的. 每读取到一种class类型, 就把他加载到内存中
    • 每加载一种class, JVM都会为其创建一个Class类型的实例, 并关联起来
    • 这里的Class类型, 是一个名叫Classclass
    • 加载String类时, 首先读取String.class文件到内存, 然后为String类创建一个Class实例并关联起来
    Class cls = new Class(String)
    
    • Class是内部的, java程序无法创建
    • 所以: JVM持有的每个Class实例都指向一个数据类型(classinterface)

    ┌───────────────────────────┐
    │ Class Instance │──────> String
    ├───────────────────────────┤
    │name = "java.lang.String" │
    └───────────────────────────┘
    ┌───────────────────────────┐
    │ Class Instance │──────> Random
    ├───────────────────────────┤
    │name = "java.util.Random" │
    └───────────────────────────┘
    ┌───────────────────────────┐
    │ Class Instance │──────> Runnable
    ├───────────────────────────┤
    │name = "java.lang.Runnable"│
    └───────────────────────────┘

    • 一个Class实例包含了该class的所有完整信息

    ┌───────────────────────────┐
    │ Class Instance │──────> String
    ├───────────────────────────┤
    │name = "java.lang.String" │
    ├───────────────────────────┤
    │package = "java.lang" │
    ├───────────────────────────┤
    │super = "java.lang.Object" │
    ├───────────────────────────┤
    │interface = CharSequence...│
    ├───────────────────────────┤
    │field = value[],hash,... │
    ├───────────────────────────┤
    │method = indexOf()... │
    └───────────────────────────┘

    • 所以我们可以通过这个Class实例获取到该实例的所有信息.

    如何获取一个classClass

    • 直接通过一个class的静态变量class获取: Class cls = String.class
    • 有一个实例变量, 提供getClass()方法
    • 使用class的完整类名, 通过Class.forName(): Class cls = Class.forName("java.lang.String")

    instanceof

    • instanceof不但匹配指定类型, 还能匹配指定类型的子类

    • == 可以准确判断数据类型, 但不能判断子类

    • String[]也是一种Class, 不同于String.class, 类名是[Ljava.lang.String.

    • 可以通过Class实例, 来创建对应的实例

    Class cls = String.class;
    String s= (String) cls.newInstance();
    
    • 上述代码相当于new String()
    • Class.newInstance 局限: 无法调用带有参数的构造方法或者非public的构造方法.

    动态加载

    • JVM在执行Java时, 并非一次性加载所有用到的class, 第一次需要用到class时才加载
    • 利用动态加载特性, 我们可以在运行期间根据条件加载不同的实现类
    • 这就是为什么Log4j放到classpath, Commons Logging就会自动使用.

    访问字段

    如何获取字段信息

    • Field getField(name): 根据字段名获取某个public的field (包括父类)
    • Field getDeclaredField(name): 根据字段名获取当前类的某个field (不包括父类)
    • Field getFields(): 获取所有public的field (包括父类)
    • Field getDeclaredFields(): 获取当前类的所有字段 (不包括父类)

    Field对象包含的信息

    • getName(): 返回字段名称
    • getType(): 返回字段类型
    • getModifiers()返回字段的修饰符, 它是一个int, 不同数值代表不同的含义

    获取字段值

    • 利用反射拿到字段的一个Field实例, 从而拿到一个实例对应的该字段的值
    • setAccessible(true) 可能会失败, 因为JVM运行期间存在SecurityManager可能不允许java和javax开头的package的类调用.
    • 为了保证java核心库的安全

    设置字段值

    • Field.set(Object, Object)

    调用方法

    • 同样可以用相同的方法, 获取Class所有的Method
    • 一个Method对象包含一个方法所有的信息.
    • 获取到一个Method就可以对其进行调用

    调用静态方法

    • 调用静态方法无需指定实例对象.
    • invoke方法第一参数永远为null
    Method m = Integer.class.getMethod("parseInt", String.class);
    Integer n = (Integer) m.invoke(null, "12345");
    System.out.println(n);
    

    调用非public方法

    • 非public方法, 可以获取,但直接调用, 得到IllegalAccessException.
    • 我们通过Method.setAccessible(true)允许调用
    • 需要用getDeclaredMethod来获取private方法

    多态

    • 使用反射调用方法时, 仍然遵循多态原则
    • 也就是总是调用实际类型的覆写方法

    调用构造方法

    • 可以用个Class提供的newInstance()方法创建新的实例, 但只能调用public的无参构造方法
    • Constructor方法用来调用任意的构造方法, 包含了构造方法的所有信息

    通过Class实例获取Constructor的方法如下

    • getConstructor(Class...): 获取某个publicConstructor

    • getDeclaredConstructor(Class...): 获取某个Constructor

    • getConstructors: 获取所有publicConstructor

    • getDeclaredConstructors() 获取所有的Constructor

    • 调用非Public的Constructor时, 必须通过setAccessible(true)

    获取继承关系

    获取父类的Class

    • Integer => Number => Object => null
    • 任何一种非interfaceClass都必定存在一种父类类型

    获取interface

    • getInterface: 返回当前类直接实现的接口类型, 不包括父类实现的接口类型
    • 所有interfaceClass, 调用getSuperClass()返回null
    • 类没有实现接口, getInterface返回空数组

    继承关系

    • 判断一个实例是否属于某个类型: instanceof
    • 如果两个Class实例, 要判断向上转型是否成功, 使用isAssignable()
    boolean c = Number.class.isAssignableFrom(Integer.class);
    System.out.println(c); // true Integer可以赋值给Number
    
    boolean e = Integer.class.isAssignableFrom(Number.class);
    System.out.println(e); // false Number不能赋值给Integer
    

    动态代理

    • 比较classinterface的区别
      • 可以实例化class
      • 不能实例化interface
    • 所有interface类型的变量总是通过向上转型并指向某个实例 CharSequence cs = new StringBuilder();
    • 动态代理(Dynamic Proxy): 在运行期动态创建某个interface的实例
    • 不编写实现类, 直接通过Proxy.newProxyInstance()创建接口对象
    • 没有实现类, 但在运行期动态创建了一个接口对象的方式, 成为动态代码
    • JDK提供的动态创建接口对象的方式, 成为动态代理
        InvocationHandler handler = new InvocationHandler() { // 1: 创建invocationHandler方法
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(method);
            if (method.getName().equals("morning")) {
              System.out.println("Good morning: " + args[0]);
            }
            return new Object();
          }
        };
        // 2: 创建通过`newProxyInstance`创建实例
        Hello hello = (Hello) Proxy.newProxyInstance( // 3: 强制转换类型
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("bob");
    

    运行期间创建interface实例如下

    1. 创建InvocationHandler实例, 负责方法调用
    2. 通过Proxy.newProxyInstance()创建interface实例, 三个参数如下:
      • 使用的ClassLoader, 通常就是接口类的ClassLoader
      • 需要实现的接口数组.
      • 用来处理回调方法调用的InvocationHandler实例
    3. 将返回的Object类型强制转换

    动态代理本质

    • 动态代理其实就是: JDK在运行期间动态创建class字节码并加载过程.

    • 动态类改为静态实现类:

    • JDK就是编写了这么一个静态类, 不需要源码, 直接生成字节码

    interface Hello {
      void morning(String name);
    }
    
    class HelloDynamicProxy implements Hello {
      InvocationHandler handler;
      public HelloDynamicProxy(InvocationHandler handler) {
        this.handler = handler;
      }
      public void morning(String name) {
        try {
        handler.invoke(
            this,
            Hello.class.getMethod("mornging"),
            new Object[] {name});
        } catch (Throwable t) {
        }
      }
    }
    

    疑问

    • Class类型的class是什么意思?
    • String s= (String) cls.newInstance(); 中间的(String) 如何理解?
    • 完全不理解最后这个动态代理, 以及最后的静态类实现方式??
    • new Person () {}是什么语法??
  • 相关阅读:
    2017-2018-1 20155311 《信息安全系统设计基础》第5周学习总结
    2017-2018-1 20155311 《信息安全系统设计基础》第3周学习总结
    2017-2018-1 20155311 《信息安全系统设计基础》第4周学习总结
    2017-2018-1 20155311 《信息安全系统设计基础》第2周学习总结
    2017-2018-1 20155311 《信息安全系统设计基础》第1周学习总结
    20155308&20155316 2017-2018-1 《信息安全系统设计基础》实验三
    20155308 加分项——C语言实现Linux的pwd命令
    2017-2018-1 20155308 《信息安全系统设计基础》第九周学习总结
    2017-2018-1 20155308&20155321&20155330《信息安全技术》实验三——数字证书应用1
    2017-2018-1 20155308 《信息安全系统设计基础》第八周学习总结
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/12575889.html
Copyright © 2020-2023  润新知