Java反射机制

Java反射机制

获取Java运行时的类型信息有两种方法

  1. RTTI(Run-Time Type Identification)

  2. Java反射机制

Java反射机制的定义

反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

  • 利用Class类来创建实例

    1. 创建Class类的一个对象,返回一个类的引用

      Class cls = Class.forName("Airplane")

    2. 通过类的引用创建实例

      cls.newInstance()

  • Method类的invoke

    invoke方法对带有指定参数的指定对象,调用由此Method对象表示的基础方法

    public class ClassA {
        public void add(Integer param1, Integer param2) {
            System.out.println(param1.intValue()+param2.intValue());
        }
    
        public void StringAdd(String abc) {
            System.out.println("out" + abc);
        }
        public static void main(String[] args) {
            try {
                Method mth = ClassA.class.getMethod("add", new Class[] {Integer.class, Integer.class});
                mth.invoke(ClassA.class.newInstance(), new Integer(1), new Integer(2));
                Method mth1 = ClassA.class.getMethod("StringAdd", new Class[] {String.class});
                mth1.invoke(ClassA.class.newInstance(), "--test");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

Java静态代理

  • 代理模式

    在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对线之间起到中介的作用。

    代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。

  • 代理模式一般涉及到的角色

    1. 抽象角色

      声明真实对象和代理对象的共同接口

    2. 代理角色

      代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能够代替真实对象。

    3. 真实角色

      代理角色所代表的真实对象,使我们最终要引用的对象。

  • 示例

    //真实对象和代理对象的共同接口
    abstract class Subject {
        public abstract void request();
    }
    
    //真实角色
    class RealSubject extends Subject {
        @Override
        public void request() {
            System.out.println("From Real Subject!");
        }
    }
    
    //代理角色
    class ProxySubject extends Subject {
        //代理角色对象内部含有对真实对象的引用
        private RealSubject realSubject;
        @Override
        public void request() {
            preRequest();
            if (realSubject==null) {
                realSubject = new RealSubject();
            }
            realSubject.request();
            postRequest();
        }
    
        private void preRequest() {
            System.out.println("Pre Request.");
        }
    
        private void postRequest() {
            System.out.println("Post Request.");
        }
    }
    
    
    //客户端
    public class Client {
        public static void main(String[] args) {
            Subject subject = new ProxySubject();
            subject.request();
        }
    }
    

静态代理的优缺点

  • 优点

    业务类只需要关注业务逻辑本身,保证业务类的重用性。这是代理的共有优点。

  • 缺点

    代理对象的一个接口只服务与一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时便无法胜任了。

    如果接口增加一个方法,除了实现类需要新增,代理类也需要新增实现此方法,增加了代码维护的复杂度。

Java动态代理

  • java.lang.reflect.Proxy

    这是java动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

    //该方法用于获取执行代理对象所关联的调用处理器
    static InvocationHandler getInvocationHandler(Object proxy)
    
    //该方法用于获取关联于指定类加载器和一组接口的动态代理类的类对象
    static Class getProxyClass(ClassLoader loader, Class[] interfaces)
    
    //该方法用于判断指定类对象是否是一个动态代理类
    static boolean isProxyClas(Class cl)
    
    //该方法用于为指定类装载器、一组接口及调用处理器生成类动态代理类实例
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
    
  • java.lang.reflect.InvocationHandler

    这是调用处理器接口,它自定义了一个invoke方法,用于集中处理现在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问

    Object invoke(Object proxy, Method method, Object[] args)
    //第一个参数是代理类实例
    //第二个参数是被调用的方法对象
    //第三个参数是调用参数
    //调用处理器根据这三个参数进行预处理或分派到委托类实例上执行
    
  • 示例

    //抽象角色
    interface Subject {
        public void request();
    }
    
    //代理角色
    class RealSubject implements Subject {
        public RealSubject() {}
    
        @Override
        public void request() {
            System.out.println("From real subject");
        }
    }
    
    //代理角色
    class DynamicSubject implements InvocationHandler {
        private Object sub;
        public DynamicSubject() {}
        public DynamicSubject(Object obj) {
            sub = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before calling" + method);
            method.invoke(sub, args);
            System.out.println("after calling" + method);
    
            return null;
        }
    }
    
    //客户端
    public class Client {
        public static void main(String[] args) {
            RealSubject rs = new RealSubject();
            InvocationHandler ds = new DynamicSubject(rs);
            Class cls = rs.getClass();
            Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ds);
            subject.request();
        }
    }
    
  • 动态代理的特点

    1. 包:如果所代理的接口都是public,它将被定义在顶层包,如果所代理的接口中有非public的接口,那么它将被定义在该接口所在包。
    2. 类修饰符:代理聚具有final和public修饰,以为者它可以被所有的类访问,但是不能被再度继承。
    3. 类名:格式是"$ProxyN"
    4. 类继承关系图:

  • 动态代理的优点和缺点

    优点:

    最大的好处是接口中声明的所有方法都被转移到调用处理器中一个集中的方法中处理(InvocationHandler.invoke),在接口方法数量比较多的时候,可以进行灵活处理,不需要像静态代理那样每一个方法进行中转。

    缺点:

    Proxy已经设计地非常优美,但有一点小小的遗憾之处,就是它始终无法摆脱仅支持interface代理的桎梏。

    Java反射扩展-jvm加载类原理

  • Java类加载的种类

    • Java自带的默认加载器

      1. 根类加载器:bootstrap,由C++编写,所有Java程序无法获得。
      2. 扩展类加载器:由Java编写。
      3. 系统类、应用类加载器:由Java编写。
    • 用户自定义的类加载器

      Java.lang.ClassLoader的子类,用户可以定制类的加载方式。每一个类都包含了加载它的ClassLoader的以一个引用--getClassLoader()。

      如果返回的是null,表明它的类加载器是bootstrap

  • 类的加载方式

    1. 本地编译好的class中直接加载

    2. 网络加载:java.net.URLClassLoader可以加载url指定的类

    3. 从jar、zip等等压缩文件加载类,自动解析jar文件找到class文件去加载

    4. 从Java源代码文件动态编译成class文件

  • 类加载的步骤

    1. 加载
    2. 连接,包括:验证,准备,解析
    3. 类的初始化(这里的初始值是指程序员定义的初始值,变量的默认初始值是在准备阶段赋值的)
  • ClassLoader的加载顺序

    1. 加载顺序:

      根加载器-->扩展类加载器-->应用类加载器-->用户自定义类加载器

      如果到最后一层都再加载不了,就出现ClassNotFoundException异常

  • ClassLoader加载Class的过程

原文地址:https://www.cnblogs.com/hunter-w/p/13818035.html