反射


一、反射

1.1 创建运行时类的对象

Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();
System.out.print(obj);

注意:
    要想clazz.newInstance()运行成功,需要满足以下的需求
    1、运行时类必须提供无参构造器
    2、无参构造器的访问权限必须是public
    

在javaBean当中,一般要求提供一个public的无参构造器

原因:

​ 1、便于通过反射,创建运行时类的对象

​ 2、便于子类集成父类时,通过super()调用父类构造器

1.2 获取运行时类的完整结构

我们可以通过反射,回去运行时类中所有的属性、方法、构造器、父类、接口、包、泛型、注解等

public void test2(){
		Class clazz = Person.class;
		// 获取属性结构:当前运行类及父类的的所有public访问权限的属性
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.println(field);
		}

		// 获得当前运行类当中申明的所有属性(不包含父类的)
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			System.out.println(declaredField);
		}
	}

	public void test3(){
		Class clazz = Person.class;
		// 当前运行类包名
		Package pack = clazz.getPackage();
		System.out.println(pack);
	}

	public void test4(){
		Class clazz = Person.class;
		// 当前运行类注解
		Annotation []  annotations = clazz.getAnnotations();
		for (Annotation annotation : annotations) {
			System.out.println(annotation);
		}
	}

1.3 调用运行时类的指定结构

// 属性
public void test5(){
		Class clazz = Person.class;

		// 创建运行时类对象
		Person p = (Person) clazz.newInstance();

		// 获取运行时类当中指定变量名的属性
		Field name =clazz.getDeclaredField("name");

		// 保证当前属性是可以访问的
		name.setAccessible(true);

		// 设置、获取指定对象的此属性值
		name.set(p,"Tom");
		System.out.println(name.get(p));
	}

// 方法
public void test6(){
		Class clazz = Person.class;

		// 创建运行时类对象
		Person p = (Person) clazz.newInstance();

		// 获取运行时类当中指定的某个方法:参数1--方法名,参数2--入参
		Method method =clazz.getDeclaredMethod("name",String.class);

		// 保证当前方法是可以访问的
		method.setAccessible(true);

		// 调用方法: 返回值就是方法的返回值
		Object object= method.invoke(p,"CHN");
		System.out.println(object);
	}

// 构造器
public void test8(){
		Class clazz = Person.class;
		
		// 获取运行时类当中有参构造器
		Constructor constructor =clazz.getDeclaredConstructor(String.class);

		// 保证当前构造器是可以访问的
		constructor.setAccessible(true);

		// 调用次构造器创建运行时类对象
		Person person = constructor.newInstance("Toms");
		System.out.println(person);
	}

二、代理

使用一个代理将对象包装起来,之后人后对原始对象的访问都要经过代理,代理决定是否以及何时去请求原始对象,代理分为动态代理和静态代理

静态代理:代理类和目标类在编译期间就确定了,同时,没一个代理类只能为一个接口服务,这样会需要很多的代理

动态里:使用一个代理完成所有的代理功能、在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。

2.1 静态代理

  1. 创建一个接口
  2. 创建厂家类,实现1步骤的接口
  3. 创建代理,也需要实现1步骤中的接口。
  4. 创建客户端类,调用代理
public interface Human {
    int eat(int count);
}


//目标类:
public class AppleFactory implements Human {
    @Override
    public int eat(int count) {
        System.out.println("目标类中的方法调用 , AppleFactory 中的eat ");
        return count;
    }
}


public class JD implements Human {
    //声明 被代理类
    private Human factory = new AppleFactory();

    @Override
    public int eat(int count) {
        //调用被代理类
        int count = factory.eat(count);
        //代理增加。
        count += 40;//增强功能,代理类在完成目标类方法调用后,增强了功能。
        System.out.println("吃了!");
        return count;
    }
}



public class Customer {
    public static void main(String[] args) {
        JD j = new JD();
        int count = j.eat(1);
        System.out.println(count);
    }
}

2.2 动态代理

////////////////////////////////// 接口  //////////////////////////////////////////////////

/**
 * @ClassName Human
 * @description: 动态代理:接口
 */
public interface Human {
    String getBelief();
    void eat(String food);
}

///////////////////////////////////////// 被代理类  /////////////////////////////////////////////////////////

/**
 * @ClassName SuperMan
 * @description: 被代理类
 */
public class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly";
    }

    @Override
    public void eat(String food) {
        System.out.println("I love eat "+ food);
    }
}


//////////////////////////////// 动态代理类  /////////////////////////////////////////////////////////
/**
 * @ClassName ProxyTest
 * @description: 代理类
 *               问题一:如何根据加载到内存当中的代理类,动态的创建一个代理类及其对象(动态创建代理类及对象)
 *               问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类的同名方法(动态创建被代理类方法)
 * @author: bozhiqiang
 * @Version 1.0.0
 * @createTime: 2022-10-04 18:00:49
 */
public class ProxyFactory {

    /**
     * obj:被代理类对象(需要知道被代理类是啥,才可以创建代理类)
     * @return:返回一个代理类对象
     */
    public static Object getProxyInstance(Object obj){

        MyInvocationHandler handler = new MyInvocationHandler();
        // 被代理对象赋值
        handler.bind(obj);
        /**
         * 根据被代理类对象动态的创建代理类
         * 参数1:被代理类由什么类加载器加载
         * 参数2:被代理类实现了什么接口(代理类和被代理类必须实现相同的接口)
         * 参数3:动态调用方法实现
         */
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handler);
    }
}

public class MyInvocationHandler implements InvocationHandler {
    // 被代理类
    private Object obj;
    public void bind(Object obj){
        this.obj = obj;
    }

    // 当我们通过代理类的对象,调用该方法时,就会自动调用如下的invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * 方法调用
         * method:被代理对象方法
         * obj:被代理对象
         * args:被代理方法入参
         */
        Object returnValue = method.invoke(obj,args);
        return returnValue;
    }
}



////////////////////////////////// 测试  /////////////////////////////////////////////////////////
/**
 * @ClassName ProxyTest
 * @description: 测试
 */
public class ProxyTest {

    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        // 代理类对象
        Human human = (Human) ProxyFactory.getProxyInstance(superMan);

        // 自动调用被代理类当中同名的方法---invoke
        human.getBelief();
        human.eat("apple");
    }
}


文章作者: superzqbo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 superzqbo !
评论
  目录