zoukankan      html  css  js  c++  java
  • Java反射机制笔记

    1 反射的概念

    1.1、java反射机制:

    在程序运行时,程序有能力获取一个类的所有方法和属性;并且对于任意一个对象,可以调用它的任意方法或者获取其属性

    1.2、通俗解释:

    java文件需要编译成.class文件才能被jvm加载使用,对象的.class数据在jvm里就是Class;我们如果能拿到这个Class对象,就能获取该Class对应的对象类型,及其在该类型声明的方法和属性值;还可以根据Class创建相应的类型对象,通过Field,Method反过来操作对象

    1.3、java反射相关类介绍:

    类名 描述
    Class 代表类的实体,在运行的Java应用程序中表示类或者接口
    Field 类的成员变量(成员变量也称为类的属性)
    Method 类的方法
    Constructor 类的构造方法

    2 获取Class的三种方法

    2.1 通过已知的类型获取class

    // 根据Example 获取Class ==> Example.class
    public Class<Example> getExample(){
        Class<Example> clazz = Example.class;
        return clazz;
    }
    
    

    2.2 通过实例对象获取class

    public Class<Example> getExampleByInstance(){
        // 先实例化对象
        Example example = new Example();
        // getClass是Object类里面的方法;《?》 是通配符
        Class<?> clazz = example.getClass();
        return (Class<Example>)clazz;
    }
    

    2.3 通过Class.forName获取全路径指定类名的class

        /** forName0 本地方法,本地方法用native标识,C++实现,jvm调用
        * 1 className 是个类名  2 initialize 是否延迟加载  3 loader 加载器
        */
        private static native Class<?> forName0(String className, 
                boolean initialize,
                 ClassLoader loader, 
                 Class<?> caller) throws ClassNotFoundException;
    
        // 会调用本地方法forName0
        public static Class<?> forName(String className) throws ClassNotFoundException     
        {
            Class<?> caller = Reflection.getCallerClass();
            return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
        }
        // 两个forName方法最终都会调用forName0方法去加载class   
        public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException 
            {
            ....
            return forName0(name, initialize, loader, caller);
            }
    
    
    // 示例:通过java.lang.Integer 
    public Class<Integer> getInteger()throws ClassNotFoundException{
        Class<?> clazz = Class.forName("java.lang.Integer");
        return (Class<Integer>)clazz;
    }
    
    

    3 Java反射API介绍-按维度分

    3.1 Class常用操作方法

    //获取所有的构造方法 / private public
    public Constructor<?>[] getDeclaredConstructors()
    //获取特定的构造方法 / private public
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)    
    //获取类的父类
    public native Class<? super T> getSuperclass()    
    //获取类实现的接口
    private Class<?>[] getInterfaces(boolean cloneArray)  
    //获取在类内定义的内部类或接口
    public Class<?>[] getDeclaredClasses()
    //获取所有的方法
    public Method[] getDeclaredMethods() throws SecurityException
    //根据方法名和参数获得特定的方法
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)  
    //获取类型的定义的所有属性
    public Field[] getFields() throws SecurityException
    // 根据属性命名获得特定的Field
    public Field getField(String name) 
    
    

    3.2 Method常用的操作方法

    //获得方法的返回类型
    public Class<?> getReturnType()   
    //获得方法的传入参数类型
    public Class<?>[] getParameterTypes()   
    //obj是实例对象,args是方法,反过来由Method控制对象的方法调用
    public Object invoke(Object obj, Object... args)
    

    3.3 Field常用的操作方法

    //属性与obj相等则返回true
    public boolean equals(Object obj)
    //获得obj中对应的属性值
    public Object get(Object obj)
    //设置obj中对应属性值
    public void set(Object obj, Object value) 
    

    3.4 Constructo

    //根据传递的参数创建类的对象:initargs 构造方法参数
    public T newInstance(Object... initargs) 
    

    3.5 组合使用一(根据class创建对象)

    //方式一 clazz.newInstance()
    Class<Example> clazz = Example.class;
    Example example = clazz.newInstance();
    //方式二 先获取再由Constructor:clazz.getConstructors()/getConstructor(...) 
    //再由Constructor.newInstance 方法构造对象
    -----------------------------------------
    public class Example {
        private int value;
        public Example(){ } // 如果只声明有参构造函数,clazz.newInstance()会报错
        public Example(Integer value){  this.value  = value;  }
        static public void main(String[] args) throws Exception{
            Class<Example> clazz = Example.class;
            //根据指定构造函数参数获取Constructor
            Constructor<Example> constructor = clazz.getConstructor(Integer.class);
            Example example = constructor.newInstance(100);
            System.out.println(example.value);
        }
    }    
    

    3.6 组合使用二(由class获取Field,并操作实例的属性)

    public class Example {
        private int value , count;
        static public void main(String[] args) throws Exception{
            Class<Example> clazz = Example.class;
            //获取所有的属性,getField只能获取public的属性
            Field[] fs = clazz.getDeclaredFields();
            //根据名称获取指定 Field
            Field value = clazz.getDeclaredField("value");
            Example example = clazz.newInstance();
            //使用反射机制可以打破封装性,导致了java对象的属性不安全  
            value.setAccessible(true); //setAccessible(true)让private的参数可赋值操作
            //由Field反过去设置example的值
            value.set(example,100);
            System.out.println(example.value);
        }
    }
    

    3.7 组合使用三(由class获取Method,并反射调用实例方法)

    public class Example {
        public static void main(String[] args) throws Exception {
            Class<Example> clazz = Example.class;
            Example example = clazz.newInstance();
            Method[] methods = clazz.getDeclaredMethods();
            //getDeclaredMethod和getMethod是:getMethod只能返回public的方法
            Method method = clazz.getDeclaredMethod("hello", String.class);
            method.setAccessible(true);
            method.invoke(example, "cscw");
        }
        private void hello(String name) { System.out.println(name + " Hello!"); }
    }
    -----
    cscw Hello!
    

    4 反射机制应用的场景

    4.1 动态拓展

    假设有同一组类是实现相同的接口,并且类的加载方式不限制。当我们需要那种具体类实现的功能时,只需加载.class文件,并获取对应的Class对象。可以由Class或者Constructor实例化对象instance;根据接口定义,可以获取Class里的某一方法Method,并配合instance反射调用功能方法

    4.2 Spring的IOC

    Spring的IOC就是基于反射机制实现

    4.3 JDK的动态代理

    JDK的动态代理就是基于反射机制实现

    5 反射和JDK动态代理

    在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口。通过这个类和接口可以生成JDK动态代理类或动态代理对象

    public interface InvocationHandler {
        //所有方法都会调用此代理方法
        Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
    }  
    public class Proxy implements Serializable{
        ...
        
        //根据interfaces和InvocationHandler生成代理对象
        public static Object newProxyInstance(ClassLoader loader,
          Class<?>[] interfaces, InvocationHandler h) 
          
        ...    
    }
    

    JDK的动态代理由Proxy和InvocationHandler实现;而被代理对象必须实现一个接口。代理对象由Proxy生成,可转为接口interface的实现类对象OBJ。当调用OBJ的方法时,则会触发InvocationHandler.invoke,参数依次为代理对象,Method对象,和方法Method所需的参数。在invoke方法可以加入拓展的逻辑,如日志记录操作;并可以在invoke里利用反射的技术调用被代理对象方法

    示例:

    public class ExampleFactory<T> implements InvocationHandler{
        private T target; //被代理对象
        public T bind(T obj){
            target = obj;
            return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
               obj.getClass().getInterfaces(),this);
        }
        /** Object o 是代理对象; o的方法调用 -> ExampleFactory.invoke 
        *  invoke(...) -> 在invoke方法里面 反射调用代理对象方法+增强逻辑
        */
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            //增强逻辑
            System.out.println("log start");
            //反射调用被代理对象方法
            Object result = method.invoke(target,objects);
            System.out.println("log end");
            return result;
        }
    }
    -----------
    public interface Face {
        void hello(String name);
    }
    ---------
    //被代理对象必须实现一个接口,并由接口方法对方提供功能
    public class Example implements Face {
     public void hello(String name) {
            System.out.println(name + " Hello!");
        }
        public static void main(String[] args)  {
           //ExampleFactory<Face> 相当于一个中介人
            ExampleFactory<Face> factory = new ExampleFactory<>();
            //example 是代理对象
            Face example = exampleProxy.bind(new Example());
            example.hello("TEST");
        }
    }
    -----
    log start
    TEST Hello!
    log end
    

    原文地址: https://juejin.im/post/6876981051390951437

  • 相关阅读:
    使用redis,zookeeper实现分布式锁
    基于线程池的多线程售票demo(原创)
    springboot全局异常处理
    IDEA2017.3.4破解方式及lombok图文配置详解
    LeetCode 120——三角形最小路径和
    LeetCode 1——两数之和
    LeetCode 445——两数相加 II
    在本地电脑使用远程服务器的图形界面——包括 MATLAB、PyCharm 等各种软件
    LeetCode 141——环形链表
    LeetCode 142——环形链表 II
  • 原文地址:https://www.cnblogs.com/darope/p/13745590.html
Copyright © 2011-2022 走看看