我是靠谱客的博主 平常小丸子,最近开发中收集的这篇文章主要介绍JAVA高级-反射反射,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

反射

概述

  • java是一门静态语言,但是反射机制让java有了动态机制。

  • Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期

    借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内

    部属性及方法。

  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个

    类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可

    以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看

    到类的结构,所以,我们形象的称之为:反射

  • 正常方式:引入需要的”包类”名称 ->通过new实例化 ->取得实例化对象

  • 反射方式:实例化对象 ->getClass()方法 ->得到完整的“包类”名称

*Class

  • 我们可以将它定义为类的类

  • 在Object类中定义了以下的方法,此方法将被所有子类继承(java反射源头):

    public final Class getClass();

  • 常用方法

    方法名功能说明
    static Class forName(String name)返回指定类名 name 的 Class 对象
    Object newInstance()调用缺省构造函数,返回该Class对象的一个实例
    getName()返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
    Class getSuperClass()返回当前Class对象的父类的Class对象
    Class [] getInterfaces()获取当前Class对象的接口
    ClassLoader getClassLoader()返回该类的类加载器
    Class getSuperclass()返回表示此Class所表示的实体的超类的Class
    Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
    Field[] getDeclaredFields()返回Field对象的一个数组
    Method getMethod(String name,Class … paramTypes)返回一个Method对象,此对象的形参类型为paramType

类加载与ClassLoader

  • 类加载过程程序经过javac.exe文件后,会成以class结尾的字节或字节码文件,接着我们使用java.exe命令对某个字节码文件解析运行。相当于某个字节码文件加载到内存中。这个过程就叫做类的加载,被称为运行时类,是一个Class实例。
   //先建立一个Person
	@Test
    public void test2(){
//        对于自定义类,使用系统类加载器加载(
        	//类加载器是负责加载类的一个对象,ClassLoader是一个抽象类。最常见的加载策略是根据的类的全名,然后找到这个类的class文件,然后从文件读取这个类的数据加载到JVM。每个类都能通过getClassLoader方法获取加载这个类的类加载器。ClassLoader classLoader = Person.class.getClassLoader();
        System.out.println(classLoader);

//        调用系统类加载器的getParent获取扩展类加载器
        System.out.println(classLoader.getParent());

//        通过扩展类加载器获取getParent无法获取扩展类加载器
        System.out.println(classLoader.getParent().getParent());

//        System.out.println(classLoader.getParent().getParent().getParent());报错,因为上一条代码已经为空了
    }
  • 哪些类型可以为Class的一个实例?

    (1)class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

    (2)interface:接口

    (3)[]:数组

    (4)enum:枚举

    (5)annotation:注解@interface

    (6)primitive type:基本数据类型

    (7)void

  • 下面提供如何获取Class实例的例子。

package com.hyb.Reflection;

import org.junit.Test;

/**
 * @program: getClassTest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-16 22:14
 **/

public class getClassTest {
    
    /*新建一个Person类*/
    @Test
    public void test1(){

        /*public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement
        */

//        方式一:通过运行时类的属性
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
//        class com.hyb.Reflection.Person

//        方式二:通过运行时类的对象
        Person person = new Person();
        System.out.println(person.getClass());
//        class com.hyb.Reflection.Person

//        方式三:调用Class的静态方法,forName(String classPath)
        Class<?> aClass=null;
        try {
            aClass = Class.forName("com.hyb.Reflection.Person");
            System.out.println(aClass);
//            class com.hyb.Reflection.Person
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

//        比较
        System.out.println(personClass==person.getClass());
        System.out.println(person.getClass()==aClass);
        System.out.println(personClass==aClass);
//        true
//        true
//        true
        /*可见三种方式产生的 Class实例都是一样的,也就是说,加载到内存中的类会缓存一段时间,我们可以
        * 通过不同的方式获得该类,而且该类只加载一次,没有必要去加载多次。*/

//        方式四:利用类的加载器获取一个Class的实例,getClassLoader(了解)
        ClassLoader classLoader = Person.class.getClassLoader();
        Class<?> aClass1 = null;
        try {
            aClass1 = classLoader.loadClass("com.hyb.Reflection.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(aClass1);
//        class com.hyb.Reflection.Person


    }
}
  • 利用Properties和类的加载器读取文件
//之前的方式    
	@Test
    public void test1(){

//        配置文件在当前工程或者module下,不要在src里
        Properties properties = new Properties();

        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream("myTest.properties");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        try {
            properties.load(fileInputStream);

            String user=properties.getProperty("user");
            String password=properties.getProperty("password");
            System.out.println(user + "->" + password);
            
//            hyb->123456789
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            assert fileInputStream != null;
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        properties.clear();

    }
    //方式二
	@Test
    public void test2(){
        
        Properties properties = new Properties();

        ClassLoader classLoader = getFileTest.class.getClassLoader();

        //这里唯一的区别就是这个文件目录必须在当前module下的src下,前面是说不要在src下,这里不一样
        InputStream resourceAsStream = classLoader.getResourceAsStream("myTest.properties");

        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        String user=properties.getProperty("user");
        String password=properties.getProperty("password");
        System.out.println(user + "->" + password);
//        hyb->123456789

        try {
            assert resourceAsStream != null;
            resourceAsStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        properties.clear();
    }

*创建运行时类的对象

  • 调用Class对象的newInstance()方法
package com.hyb.Reflection;

import org.junit.Test;

/**
 * @program: NewInstanceTest
 * @description:创建运行时类的对象
 * @author: Huang Yubin
 * @create: 2021-06-17 10:21
 **/

public class NewInstanceTest {
    @Test
    public void test1(){

//        首先要拿到这个类
        Class<Person> personClass = Person.class;

//        利用newInstance创建运行时类的对象

        Person person = null;
        try {
            person = personClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        System.out.println(person);
    }

}
  • 思考
    1. 既然可以通过调用newInstance方法造对象,那说明是否还存在除了调用空参构造器创造对象这种方法外,还存在另一种方法?

    2. 如果Person对象里的空参构造器是私有的,这个方法还管用吗?

    3. 提示:直接查看源码便可以得到答案,或者你可以在Person类中的空参构造器加上输出,看是否有运行结果就可以了。看了源代码可以知道,原来这个方法里,还是调用了运行时类的空参构造器而且当你修改Person类的空参构造器为私有时,你会发现程序报错,这就说明,newInstance的方法必须保证javabean类里的构造器是私有的而且无参

    4. 那么没有无参构造器真的不能创建一个对象了吗?

      • 不是!只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。步骤如下:

        • 通过Class类的**getDeclaredConstructor(Class … parameterTypes)**取得本类的指定形参类

          型的构造器

        • 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

        • 通过Constructor实例化对象。

                //1.根据全类名获取对应的Class对象
                String name =atguigu.java.Person";
                Class clazz = null;
                clazz = Class.forName(name);
        //2.调用指定参数结构的构造器,生成Constructor的实例
                Constructor con = clazz.getConstructor(String.class,Integer.class);
        //3.通过Constructor的实例创建对应类的对象,并初始化类属性
                Person p2 = (Person) con.newInstance("Peter",20);
                System.out.println(p2);
        

运行时类结构

  • 在这里不做获取类属性的例子,只做获取运行时类方法的例子。

  • 首先我们创建一个Person父类。

    package com.hyb.Reflection;
    
    /**
     * @program: PersonFather
     * @description:
     * @author: Huang Yubin
     * @create: 2021-06-17 13:03
     **/
    
    public class PersonFather {
    
        private String fatherName;
        private int fatherAge;
    
        public PersonFather() {
        }
    
        public PersonFather(String fatherName, int fatherAge) {
            this.fatherName = fatherName;
            this.fatherAge = fatherAge;
        }
    
        public String getFatherName() {
            return fatherName;
        }
    
        public void setFatherName(String fatherName) {
            this.fatherName = fatherName;
        }
    
        public int getFatherAge() {
            return fatherAge;
        }
    
        public void setFatherAge(int fatherAge) {
            this.fatherAge = fatherAge;
        }
    
        @Override
        public String toString() {
            return "PersonFather{" +
                    "fatherName='" + fatherName + ''' +
                    ", fatherAge=" + fatherAge +
                    '}';
        }
    }
    
  • 之后我们将之前创建的Person类补全,尽量符合一个javabean结构。

    package com.hyb.Reflection;
    
    /**
     * @program: Person
     * @description:
     * @author: Huang Yubin
     * @create: 2021-06-16 22:14
     **/
    
    public class Person extends PersonFather{
    
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String fatherName, int fatherAge, String name, int age) {
            super(fatherName, fatherAge);
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + ''' +
                    ", age=" + age +
                    '}';
        }
    
    }
    
  • 然后我们来测试,看如何获取Person类的方法,是否通过它能获取父类的方法。

        @Test
        public void test1(){
    //        获取Person类
            Class<Person> personClass = Person.class;
    //        getMethods方法
            Method[] methods = personClass.getMethods();
            for (Object obj:methods){
                System.out.println(obj);
            }
    //        public java.lang.String com.hyb.Reflection.Person.toString()
    //        public java.lang.String com.hyb.Reflection.Person.getName()
    //        public void com.hyb.Reflection.Person.setName(java.lang.String)
    //        public int com.hyb.Reflection.Person.getAge()
    //        public void com.hyb.Reflection.Person.setAge(int)
    //        public void com.hyb.Reflection.PersonFather.setFatherAge(int)
    //        public int com.hyb.Reflection.PersonFather.getFatherAge()
    //        public java.lang.String com.hyb.Reflection.PersonFather.getFatherName()
    //        public void com.hyb.Reflection.PersonFather.setFatherName(java.lang.String)
    //        public final void java.lang.Object.wait() throws java.lang.InterruptedException
    //        public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    //        public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    //        public boolean java.lang.Object.equals(java.lang.Object)
    //        public native int java.lang.Object.hashCode()
    //        public final native java.lang.Class java.lang.Object.getClass()
    //        public final native void java.lang.Object.notify()
    //        public final native void java.lang.Object.notifyAll()
    
        }
    
    • 我们从输出可以看到,getMethods()方法可以获得一个类的极其父类的所有public方法。
  • 通过getDeclaredMethods,获得该类的所有的方法,不包括父类。

        @Test
        public void test2(){
            Class<Person> personClass = Person.class;
            Method[] declaredMethods = personClass.getDeclaredMethods();
            for (Object obj:declaredMethods){
                System.out.println(obj);
            }
    //        public java.lang.String com.hyb.Reflection.Person.toString()
    //        public java.lang.String com.hyb.Reflection.Person.getName()
    //        public void com.hyb.Reflection.Person.setName(java.lang.String)
    //        public int com.hyb.Reflection.Person.getAge()
    //        public void com.hyb.Reflection.Person.setAge(int)
        }
    
  • 前面说到,我们可以调用类里的属性和方法结构,那方法的属性和结构我们是否可以调用呢?答案是肯定的

    • 一个方法的基本结构:

      @注解

      **权限修饰符 类型 方法名(类型 参数,类型 参数……)throws 异常/接口/泛型/

    • public Class<?>[] getInterfaces() 确定此对象所表示的类或接口实现的接口。

    public Class<? Super T> getSuperclass()返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的Class。

    public Constructor[] getConstructors()返回此 Class 对象所表示的类的所有public构造方法。

    public Constructor[] getDeclaredConstructors()返回此 Class 对象表示的类声明的所有构造方法。

    取得修饰符: public int getModifiers();

    取得方法名称: public String getName();

    取得参数的类型:public Class<?>[] getParameterTypes();

    public Class<?> getReturnType()取得全部的返回值

    public Class<?>[] getParameterTypes()取得全部的参数

    public int getModifiers()取得修饰符

    public Class<?>[] getExceptionTypes()取得异常信息

    public Field[] getFields() 返回此Class对象所表示的类或父类或接口的public的字段。返回此Class对象所表示的类或接口的public的Field。(getDeclaredFields返回当前类不包括父类,包括私有和公有)

    public int getModifiers() 以整数形式返回此Field的修饰符

    public Class<?> getType() 得到Field的属性类型

    public String getName() 返回Field的名称。

    get Annotation(Class annotationClass)

    getDeclaredAnnotations()

    获取父类泛型类型:Type getGenericSuperclass()

    泛型类型:ParameterizedType

    获取实际的泛型类型参数数组:getActualTypeArguments()

    类所在的包 Package getPackage()

**调用运行时类实例

*如何获取指定属性并修改?

  • 不建议使用getField,该方法要确保被调属性是公有的。
package com.hyb.Reflection;

import org.junit.Test;

import java.lang.reflect.Field;

/**
 * @program: ReflectionTest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-17 15:20
 **/

public class ReflectionTest {

    @Test
    public void test1(){

//        获得一个类
        Class<Person> personclass = Person.class;



        try {
            // 创建运行时类的对象
            Person personClass = personclass.newInstance();

//        获取想要的类的属性
            Field name = personclass.getDeclaredField("name");

//        保证属性可修改
            name.setAccessible(true);

//        修改属性
            name.set(personClass,"hyb");

//        查看修改后的属性
            System.out.println(name.get(personClass));
        } catch (NoSuchFieldException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }


    }
}

*如何获取并操作类的方法?

    @Test
    public void testMethod(){

//        获得一个类
        Class<Person> personClass = Person.class;

        try {
//        获得该类的对象
            Person person = personClass.newInstance();

//        指定要调用的类的方法
//        private String PersonPrivateMethod(String name){
//            return this.name=name;
//        }
            Method personPrivateMethod = personClass.getDeclaredMethod("PersonPrivateMethod", String.class);//方法名,类型所在类

//        将该权限设置为可访问的
            personPrivateMethod.setAccessible(true);

//        操作该私有方法,返回类型即该私有方法的类型
            Object newHyb = personPrivateMethod.invoke(person, "newHyb");

            System.out.println(newHyb);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }

        /*多个参数,且无返回值
        * private void PersonPrivateNullReturnMethod(String name,int age){
              this.name=name;
              this.age=age;
           }
        * */

        try {
            Person person1 = personClass.newInstance();

            Method personPrivateNullReturnMethod = personClass.getDeclaredMethod("PersonPrivateNullReturnMethod", String.class, int.class);

            personPrivateNullReturnMethod.setAccessible(true);

            personPrivateNullReturnMethod.invoke(person1,"newHybNew",0);//返回null

            System.out.println(person1.toString());
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException | NoSuchFieldException e) {
            e.printStackTrace();
        }

    }

如何获取指定构造器并操作

   @Test
    public void testConstructor(){
        Person person = null;

        try {
//        获取该类
            Class<Person> personClass = Person.class;

//        调用指定构造器
            Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class, String.class, int.class);

//        使得该构造器可以访问
            declaredConstructor.setAccessible(true);

            person = declaredConstructor.newInstance("hfy", 60, "hyb", 20);
        } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }

        System.out.println(person);
//        PersonFather{fatherName='hfy', fatherAge=60}Person{name='hyb', age=20}
    }

**反射应用

什么是代理?

  • 当两个对象之间需要交互,可通过第三方对象进行代理,这个时候极大地节省了两个对象之间的时间和资源。

  • 在Java里:

    客户对象产生需求:要目标对象去干一件事。

    目标对象不想做,委托代理对象去做。期间,始终都是客户对象和目标对象在交涉。

静态代理

  • 我们了解了什么是代理,我们便可以设计出这种模式。

    目标对象让自己的代理对象去做,那么其实本意都是在干同一件事,而客户对象产生的也就一个目标,不管你到底是目标对象做的还是代理对象做的。所以,客户对象可以定义为一个接口,而目标对象实现这个接口,让代理对象去做,所以代理对象也得实现这个接口。

package com.hyb.Reflection;

/**
 * @program: StaticProxyTest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-17 16:37
 **/

//设计客户接口
interface Customer{
//    客户产生目标,做一些事情
    void doSomething();
}

//有目标对象为客户做一些事
class Target implements Customer{
    @Override
    public void doSomething() {
        System.out.println("没问题,我叫我代理去为你完成这些事情!");
    }
}

//代理人去做

class proxy implements Customer{

//    实例化接口
    private Customer customer;

//    产生构造器,也就产生这件事
    public proxy(Customer customer) {
        this.customer = customer;
    }

    @Override
    public void doSomething() {
        System.out.println("我已经做好了你的事情");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
//        目标对象实例化
        Target target = new Target();
        target.doSomething();
//        没问题,我叫我代理去为你完成这些事情!
        
        
//        代理接受这个目标对象命令
        proxy proxy = new proxy(target);
//        代理执行命令
        proxy.doSomething();
//        我已经做好了你的事情
    }
}
  • 好处,可以在不改变目标对象的前提下,对目标对象进行功能扩展。
  • 坏处:静态代理只是一对一的,若是产生很多过程,便会出现很多代理类。
  • 动态代理便恰到好处解决了这个问题。

**动态代理

  • 上面提到静态代理会产生多个接口,造成繁琐,那么可以升级我们的思维,我们将代理抽象成一个大类,但是我们知道每次产生的要求都是不一样的,所以我们可以动态操作,每次只要产生了要求,就让代理大类去处理,就叫做动态处理。
  • 产生需求,有人去做。
//客户产生需求
interface NewCustomer{
        void DoSomething();
}

//目标对象需要做这些事

class NewTarget implements NewCustomer{

    @Override
    public void DoSomething() {
        System.out.println("我挺忙的,但也没有固定代理,我立马去找一个代理帮你做!");
    }
    
}
  • 代理市场是一个大类,需要产生具体代理实例。

    • 首先我们要生成一个代理市场,而且这个代理市场里必须有实现可代理任何事情的方法
    class ProxyFactory{
    
        public static Object ProxyAnythingMethod(){
            
            return null;
        }
    }
    
    • 这个代理市场的基本架构,要实现两个问题:

      • 哪个目标对象?
      • 目标对象产生了什么需求?
      • 如何解决目标对象的需求?(解决目标对象的方法)
    • java提供了一个专门生成代理对象的代理类Proxy,我们就能通过这个代理类去产生为目标对象服务的代理对象

      而这个代理对象依旧要实现上面的问题。

      • 哪个是目标对象?

        //        1,目标对象进来了,但要提供自己的信息,**哪个类的加载器**,具体为什么需要加载器,待会再说。
                ClassLoader classLoader = obj.getClass().getClassLoader();
        
      • 目标对象产生了什么需求?

        //        2,然后目标对象描述自己要干的事情,即是这个类实现客户的接口
                Class<?>[] interfaces = obj.getClass().getInterfaces();
        
      • 如何解决目标对象的需求?

        //        3,具体实现这件事的过程
        //              1.在java里,Proxy类下的静态方法(newProxyInstance(目标类加载器, 目标对象接口, 自己的接口))用来生成一个代理对象,
        //              2,而这个代理对象的代理类实现了InvocationHandler接口,代表可以做任何事情
        //              3,所以我们要实现这个接口也即是实现目标对象的需求,即我们要定义一个类(myInvocationHandler)去实现这个接口。
        
        //        我们需要创建3.3这个类的对象。
        //        myInvocationHandler myHandler = new myInvocationHandler();
        
        //        我们也需要将目标对象传进去,才能知道目标对象的需求。
                myInvocationHandler myHandler = new myInvocationHandler(obj);
        
        
        //        然后我们的条件足够了,产生代理对象去执行需求
                Object o = Proxy.newProxyInstance(classLoader, interfaces, myHandler);
        
        class myInvocationHandler implements InvocationHandler{
        
        //    该接口里就提供了一个实现任何需求的方法,而我们具体要实现什么需求,就要通过反射的方式去获取目标对象有什么需求。
        //    当我们调用代理对象时,就会自动调用如下方法去实现具体需求。
        
        //    那么我们要通过反射实现。1,要知道是什么目标对象,由于这里上面没有传实参,所以我们得声名。
            Object object;
        
        //    2.通过构造器赋值得知是什么目标对象
            public myInvocationHandler(Object object) {
                this.object = object;
            }
        
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        //        args:既然是反射机制,我们自然要知道反射的方法的形参数,而这个参数可能是空的,也可能是多个,不知道是什么类型,所以是个Object数组
                Object invoke = method.invoke(object, args);//获取了目标对象里的方法,即需求
        
                return invoke;
            }
        }
        
    • 解决完这些需求,就是在main函数里具体实现过程了!

      public class DynamicProxyTest {
          public static void main(String[] args) {
      //        产生一个目标对象
              NewTarget newTarget = new NewTarget();
      
      //        找到代理市场,产生一个代理对象
      //        Object o = ProxyFactory.ProxyAnythingMethod(newTarget);
      //        这个是Object类型肯定是没有错的,但具体类型是接口,不是NewTarget,因为它实现是接口中的方法,即客户的方法。
              NewCustomer o = (NewCustomer) ProxyFactory.ProxyAnythingMethod(newTarget);
      
              o.DoSomething();
          }
      }
      
  • 全部代码:

package com.hyb.Reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @program: DynamicProxyTest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-17 17:31
 **/
//客户产生需求
interface NewCustomer{
        void DoSomething();
}

//目标对象需要做这些事

class NewTarget implements NewCustomer{

    @Override
    public void DoSomething() {
        System.out.println("我挺忙的,但也没有固定代理,我立马去找一个代理帮你做!");
        System.out.println("代理已经帮你做完了!");
    }

}

class ProxyFactory{

    public static Object ProxyAnythingMethod(Object obj){

//        1,目标对象进来了,但要提供自己的信息,**哪个类的加载器**,具体为什么需要加载器,待会再说。
        ClassLoader classLoader = obj.getClass().getClassLoader();

//        2,然后目标对象描述自己要干的事情,即是这个类的实现客户的接口
        Class<?>[] interfaces = obj.getClass().getInterfaces();

//        3,具体实现这件事的过程
//              1.在java里,Proxy类下的静态方法(newProxyInstance(目标类加载器, 目标对象接口, 自己的接口))用来生成一个代理对象,
//              2,而这个代理对象实现了InvocationHandler接口,代表可以做任何事情
//              3,所以我们要实现这个接口也即是实现目标对象的需求,即我们要定义一个类去实现这个接口。

//        我们需要创建3.3这个类的对象。
//        myInvocationHandler myHandler = new myInvocationHandler();

//        我们也需要将目标对象传进去,才能知道目标对象的需求。
        myInvocationHandler myHandler = new myInvocationHandler(obj);


//        然后我们的条件足够了,产生代理对象去执行需求
        Object o = Proxy.newProxyInstance(classLoader, interfaces, myHandler);

//        之后将这个代理对象返回
        return o;
    }
}

class myInvocationHandler implements InvocationHandler{

//    该接口里就提供了一个实现任何需求的方法,而我们具体要实现什么需求,就要通过反射的方式去获取目标对象有什么需求。
//    当我们调用代理对象时,就会自动调用如下方法去实现具体需求。

//    那么我们要通过反射实现。1,要知道是什么目标对象,由于这里上面没有传实参,所以我们得声名。
    Object object;

//    通过构造器赋值得知是什么目标对象
    public myInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//        args:既然是反射机制,我们自然要知道反射的方法的形参数,而这个参数可能是空的,也可能是多个,不知道是什么类型,所以是个Object数组
        Object invoke = method.invoke(object, args);//获取了目标对象里的方法,即需求

        return invoke;
    }
}

public class DynamicProxyTest {
    public static void main(String[] args) {
//        产生一个目标对象
        NewTarget newTarget = new NewTarget();

//        找到代理市场,产生一个代理对象
//        Object o = ProxyFactory.ProxyAnythingMethod(newTarget);
//        这个是Object类型肯定是没有错的,但具体类型是接口,不是NewTarget,因为它实现是接口中的方法,即客户的方法。
        NewCustomer o = (NewCustomer) ProxyFactory.ProxyAnythingMethod(newTarget);

        o.DoSomething();
        //我挺忙的,但也没有固定代理,我立马去找一个代理帮你做!
        //代理已经帮你做完了!
    }
}
  • 为了体现动态性,我们可以将上面的静态代理改为动态代理,即我们不用去描述具体的代理类了。
        Target target = new Target();
        Customer o1 = (Customer) ProxyFactory.ProxyAnythingMethod(target);

        o1.doSomething();
//        没问题,我叫我代理去为你完成这些事情!
//        代理已经做好了这些事情!
  • 所以,我们可以引入这样的思考:

    在构建项目的时候,有很多大体实现都一样,但具体细节不同而要造很多方法的情况,极大地造成了代码的冗余,为了解决这种方法,我们也可以引用上面动态处理的思想。

    我们都知道上面的代理市场可以实现任何事情,那么我们不妨让代理市场做这些大体实现。

    • 我们先缔造一个类,表明这是通用方法。

      class AllMethod{
          public void test1(){
              System.out.println("通用方法一!");
          }
      
          public void test2(){
              System.out.println("通用方法二!");
          }
      }
      
    • 然后在代理市场里的具体实现任何事情的方法调用

              AllMethod allMethod = new AllMethod();
              allMethod.test1();
      
      //        args:既然是反射机制,我们自然要知道反射的方法的形参数,而这个参数可能是空的,也可能是多个,不知道是什么类型,所以是个Object数组
              Object invoke = method.invoke(object, args);//获取了目标对象里的方法,即需求
      
              allMethod.test2();
              return invoke;
      
    • 然后我们看一下输出

      通用方法一!
      我挺忙的,但也没有固定代理,我立马去找一个代理帮你做!
      代理已经帮你做完了!
      通用方法二!
          
      通用方法一!
      没问题,我叫我代理去为你完成这些事情!
      代理已经做好了这些事情!
      通用方法二!
      

      我们可以发现,这也如同,无论我们是造NewTarget对象还是Target对象,都共用了AllMethod方法

建议

矛盾

  • 既然反射可以调用私有结构,那是不是破坏了封装性?
    • 不矛盾,封装性是建议你的调不调的问题,而反射反应的是我能不能调。
    • 当然,反射破没破坏封装性,没有官方的语言,但是反射和封装都可以存在。
    • 为了解读我的观点,你也可以这样想,封装只是一种思想,类里面的私有和公有属性只是在表达给外部建议不建议用的关系,不存在破不破坏的思想。
    • 总的来说,封装只是一种规范,若是反射真的破坏了,但那也只是一种规范,有时规范是要打破的。

最后

以上就是平常小丸子为你收集整理的JAVA高级-反射反射的全部内容,希望文章能够帮你解决JAVA高级-反射反射所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(74)

评论列表共有 0 条评论

立即
投稿
返回
顶部