Java笔记----反射及动态代理

一、获取Class对象的四种方法
		//方式一:调用运行时类的属性: .class
        Class class1 = Person.class;
        //方式二:通过运行时类的对象,调用getClass()
        Person p = new Person("hah", 12);
        Class class2 = p.getClass();
        //方式三:调用Class的静态方法,forName(String classPath)
        Class class3 = Class.forName("Reflection.Person");
        //方式四:使用类的加载器:ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class class4 = classLoader.loadClass("Reflection.Person");
二、NewInstance()的用法和注意事项

newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器,要想此方法正常的创建运行时类的对象,要求:

  1. 运行时类必须提供空参的构造器
  2. 空参的构造器的访问权限得够。通常,设置为public.

在javabean中要求提供一个public的空参构造器。 原因:

  1. 便于通过反射,创建运行时类的对象
  2. 便于子类继承此运行类时,默认调用super()时,保证父类有此构造器
public class NewInstanceTest {
    public static void main(String[] args) {

        Class<Person> class1 = Person.class;
        Person p = null;
        try {
            p = class1.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        System.out.println(p);    
    }
}
三、获取属性、方法、构造器

Person.class

package Reflection;

/**
 * @author zhaoyongchen
 * @create 2020-08-11-15:43
 */
public class Person {
    private String name;
    public int age;

    public Person() {
    }
    public Person(String name, int age) {
        this.age = age;
        this.name = name;
    }

    public void show(){
        System.out.println("喔哈哈哈哈哈");
    }
    public String show(String s){
        return s;
    }
    private static void showDesc(){
        System.out.println("我是一个男孩子");
    }
    private void showNation(String nation){
        System.out.println("我的国籍是"+nation);
    }

    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 "Reflection.Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在调用方法时,尤其是静态方法时,传入的对象值得关注

@Test
    public void testField() throws Exception {
        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));

    }

    @Test
    public void testMethod() throws Exception {
        Class clazz = Person.class;
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
        /*
        1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称参数2: 指明获取的方法的形参列
         */
        Method show = clazz. getDeclaredMethod("show", String.class);
        //2.保证当前方法是可访问的
         show. setAccessible(true);
        /*
        2.调用方法的invoke():参数1: 方法的调用者参数2: 给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
        */
        Object returnValue = show.invoke(p, "CHN");//String nation = p. show( "CHN");
        System.out.println(returnValue);

        System.out.println("----------调用静态方法------------");

        //private static void showDesc()
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //invoke()肯定有返回值,被调用方法没有返回值则返回null
        //Object returnVal = showDesc.invoke(Person.class);
        //调用invoke的showDesc通过clazz拿到,clazz通过Person.class得到,里面静态内容都知道
        Object returnVal = showDesc.invoke(null);
        System.out.println(returnVal);
    }

    @Test
    public void testConstructor() throws Exception {
		//还是newInstance实在
        Class clazz = Person.class;
        Constructor constructor = clazz.getConstructor(String.class,int.class);
        constructor.setAccessible(true);
        Person person = (Person) constructor.newInstance("tom", 12);
        System.out.println(person);

    }
四、反射前后对比
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Reflection {
    //反射之前,对Person的操作
    @Test
    public void test1() {
        //1.创建Person类的对象
        Person p1 = new Person("Tom", 12);
        //2.通过对象,调用其内部的属性、方法
        p1.age = 10;
        System.out.println(p1.toString());
        p1.show();
        //在Person类外部,不可以通过Person类的对象调用其内部私有结构。
        //比如: name、showNation() 以及私有的构造器
    }

    //反射之后,对于Person的操作
    @Test
    public void test2() throws Exception{
        Class clazz = Person.class;
        //1.通过反射,创建Person类的对象
        Constructor cons = clazz . getConstructor(String. class, int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person)obj;
        System.out.println(p.toString());
        //2.通过反射,调用对象指定的属性、方法
        //调用属性
        Field age = clazz. getDeclaredField( "age" );
        age.set(p,10);
        System. out . println(p.toString());
        //调用方法
        Method show = clazz . getDeclaredMethod( "show");
        show.invoke(p);

        //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
        Constructor cons1 = clazz. getDeclaredConstructor(String.class,int.class);
        cons1. setAccessible(true);
        Person p1 = (Person) cons1. newInstance( "Jerry",13);
        System. out . println(p1);
        //调用私有的属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1, "ppgod");
        System.out.println(p1);
        //调用私有的方法
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        showNation.invoke(p1,"China");
    }
}
五、封装性和反射的思考

反射可以调用私有结构,那么封装性和反射矛盾吗?

封装性告诉我们私有的别的地方不能用,而反射告诉我们可以用私有的,这样看不就白封装了吗?其实两者之间不矛盾。

封装性告诉我们的是:当我们看到一个类写了一些私有的方法,一些公共的方法时,就告诉我们私有的方法就不要用了,就用公共的方法就可以了,因为私有的方法可能在外部用不着,是用来辅助类内部公共方法的,公共的方法做得更好。比如单例模式:你要想造对象,就不要用私有的构造器了,我已经用公有的方法把对象造好了,能满足你程序的要求,你直接调就行了
反射告诉我们的是:私有的方法你也可以调,但是不建议调私有的方法,因为可能公共的方法更好,加了一些逻辑 。

封装性解决的问题是:建议调哪个的问题。公共的调就可以了,私有的不要就不要调了,私有的属性不建议你直接修改,建议你通过get set方法修改。
反射解决的的问题是:能不能调的问题。

所以两者不矛盾。

六、静态代理和动态代理

静态代理:缺点:
工厂类写死,不够灵活,随着需求越多,工厂类也是越来越多。

interface ClothFactory{//共同接口
    void ProduceCloth();
}

class LowerClothFactory implements ClothFactory{//被代理类

    @Override
    public void ProduceCloth() {
        System.out.println("工厂接收订单");
        System.out.println("工厂生产衣服");
        System.out.println("工厂提交订单");
    }
}

class ProxyNike implements ClothFactory{//代理类

    private LowerClothFactory lowerClothFactory;

    public ProxyNike() {
    }

    public ProxyNike(LowerClothFactory lowerClothFactory) {
        this.lowerClothFactory = lowerClothFactory;
    }

    @Override
    public void ProduceCloth() {
        System.out.println("Nike提交订单");
        lowerClothFactory.ProduceCloth();
        System.out.println("Nike接收订单");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        LowerClothFactory lowerClothFactory = new LowerClothFactory();

        ProxyNike nike = new ProxyNike(lowerClothFactory);
        nike.ProduceCloth();
    }
}

动态代理
要想实现动态代理,需要解决的问题?

  • 如何根据加载到内存中的被代理类,动态的创建-一个代理类及其对象。
  • 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。

通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

  • 加载器直接使用当前类的加载器就行了–obj.getClass().getClassLoader()
  • 当前类实现的接口–obj.getClass().getInterfaces()
  • 通过Handler将要运行的方法动态的绑定在一起
    • 通过代理类的对象,调用方法a时,就会自动调用Handler中的invoke()方法
      invoke(Object o, Method method, Object[] objects)
      • o:被代理类的对象,一般用不着
      • method:即为代理类对象调用的方法,此方法也就作为了被代理类要调用的方法
      • objects[]:被代理类要调用的方法的参数列表
    • 将被代理类要执行的方法a的功能就声明在invoke()中
interface Human{
    String getBelief();
    void eat(String food);
}

class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "I do believe I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}

class ProxyFactory{

    //调用此方法,返回一个代理类的对象,解决问题一
    public static Object getProxyInstance(Object obj){//obj被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();

        handler.bind(obj);

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

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

class MyInvocationHandler implements InvocationHandler{

    private Object obj;//需要使用被代理类的对象进行赋值

    public void bind(Object obj){
        this.obj = obj;
    }
    
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

        //method:即为代理类对象调用的方法,此方法也就作为了被代理类要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj, objects);

        return returnValue;
    }
}

public class DynamicProxyTest {

    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        proxyInstance.eat("奥里给");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值