27-动态代理和反射

参考视频链接
反射黑马教程

反射

反射初识

反射允许对封装类的字段,方法和构造函数的信息进行编程访问
它可以将一个类里面的方法和成员变量获取出来
image.png

image.png

反射是通过 class 文件中获取这字段,构造方法和成员方法,然后在从这三个当中进行解剖,获取更多的信息。

反射获取

获取 class 对象

获取 class 对象的三种方式

  1. Class. ForName (“全类名”); (全类名就是包名加类名)
  2. 类名. Class
  3. 对象.getClass ();
    三个阶段, 分别对应三种获取 class 的方法
    image.png
    举例
        Class cl1 = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");
        System.out.println(cl1);
        // 第二种方法
        Class cl2 = Student.class;
        System.out.println(cl2);
        // 第三种方法
        Class cl3 = new Student().getClass();
        System.out.println(cl3);

        System.out.println(cl1 == cl2);
        System.out.println(cl2 == cl3);

总结
第一种方式是最为常用的, 第二种方式我们一般是用来当作参数进行传递的, 第三种方式只有当我们已经有了这个类的对象之后, 才可以使用.

获取构造方法

image.png

public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.获得整体(class字节码文件对象)
        Class clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");


        //2.获取构造方法对象
        //获取所有构造方法(public)
        Constructor[] constructors1 = clazz.getConstructors();
        for (Constructor constructor : constructors1) {
            System.out.println(constructor);
        }

        System.out.println("=======================");

        //获取所有构造(带私有的)
        Constructor[] constructors2 = clazz.getDeclaredConstructors();

        for (Constructor constructor : constructors2) {
            System.out.println(constructor);
        }
        System.out.println("=======================");

        //获取指定的空参构造
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);

        Constructor con2 = clazz.getConstructor(String.class,int.class);
        System.out.println(con2);

        System.out.println("=======================");
        //获取指定的构造(所有构造都可以获取到,包括public包括private)
        Constructor con3 = clazz.getDeclaredConstructor();
        System.out.println(con3);
        //了解 System.out.println(con3 == con1);
        //每一次获取构造方法对象的时候,都会新new一个。

        Constructor con4 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con4);
    }
}

获取成员变量

规则:
get表示获取
Declared表示私有
最后的s表示所有,复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
常用方法

方法名说明
Field[] getFields()返回所有成员变量对象的数组(只能拿 public 的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到
public class ReflectDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //获取成员变量对象

        //1.获取class对象
        Class clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");

        //2.获取成员变量的对象(Field对象)只能获取public修饰的
        Field[] fields1 = clazz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }

        System.out.println("===============================");

        //获取成员变量的对象(public + private)
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        System.out.println("===============================");
        //获得单个成员变量对象
        //如果获取的属性是不存在的,那么会报异常
        //Field field3 = clazz.getField("aaa");
        //System.out.println(field3);//NoSuchFieldException

        Field field4 = clazz.getField("gender");
        System.out.println(field4);

        System.out.println("===============================");
        //获取单个成员变量(私有)
        Field field5 = clazz.getDeclaredField("name");
        System.out.println(field5);

    }
}

获取成员变量并获取值和修改值

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Student s = new Student("zhangsan",23,"广州");
        Student ss = new Student("lisi",24,"北京");

        //需求:
        //利用反射获取成员变量并获取值和修改值

        //1.获取class对象
        Class clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");

        //2.获取name成员变量
        //field就表示name这个属性的对象
        Field field = clazz.getDeclaredField("name");
        //临时修饰他的访问权限
        field.setAccessible(true);

        //3.设置(修改)name的值
        //参数一:表示要修改哪个对象的name?
        //参数二:表示要修改为多少?
        field.set(s,"wangwu");

        //3.获取name的值
        //表示我要获取这个对象的name的值
        String result = (String)field.get(s);

        //4.打印结果
        System.out.println(result);

        System.out.println(s);
        System.out.println(ss);

    }

获取成员方法

常用方法

方法名说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到
        //1.获取class对象
        Class<?> clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");


        //2.获取方法
        //getMethods可以获取父类中public修饰的方法
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }

        System.out.println("===========================");
        //获取所有的方法(包含私有)
        //但是只能获取自己类中的方法
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }

        System.out.println("===========================");
        //获取指定的方法(空参)
        Method method3 = clazz.getMethod("sleep");
        System.out.println(method3);

        Method method4 = clazz.getMethod("eat",String.class);
        System.out.println(method4);

        //获取指定的私有方法
        Method method5 = clazz.getDeclaredMethod("playGame");
        System.out.println(method5);

获取成员方法并运行

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1.获取字节码文件对象
        Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
		
        //2.获取一个对象
        //需要用这个对象去调用方法
        Student s = new Student();
        
        //3.获取一个指定的方法
        //参数一:方法名
        //参数二:参数列表,如果没有可以不写
        Method eatMethod = clazz.getMethod("eat",String.class);
        
        //运行
        //参数一:表示方法的调用对象
        //参数二:方法在运行时需要的实际参数
        //注意点:如果方法有返回值,那么需要接收invoke的结果
        //如果方法没有返回值,则不需要接收
        String result = (String) eatMethod.invoke(s, "重庆小面");
        System.out.println(result);

    }

动态代理

代理的初步认识

平常我们修改代码的时候我们一般是进行侵入式修改, 也就是直接修改源代码
当我们既不想修改修改源代码, 可是又想要增加额外的功能, 这种情况下我们就可以使用这个动态代理.
程序为什么需要代理, 代理它长什么样

  1. 如果对象认为自己身上的事情太多的话, 我们就可以通过代理来转移部分的职责
  2. 代理里面并不能够实现某些功能, 但是它可以通过调用可以实现这功能的对象函数的代码来实现这个功能.
  3. 对象有什么方法想被代理, 代理里就一定要有对应的方法
  4. 我们通过这个接口来实现这个代理, 想要代理什么方法, 就要把这个方法写在这个接口当中
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如何为 java 对象创建一个代理呢

Java. Long. Reflect. Proxy 类中提供了为对象产生代理对象的方法
image.png
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InnovationHandler h

举例

明星对象 (我省略了 setter 和 getter 方法)

public class Star implements daili {
    private String name;
    @Override
    public String sing(String name) {
        System.out.println(this.name + " 正在演唱 " + name);
        return "谢谢";
    }
    @Override
    public String dance() {
        System.out.println(this.name + " 正在跳舞");
        return "谢谢";
    }
}

代理 (这是一个接口, 包含了明星对象的几个功能代码)

public interface daili {
    public abstract String sing(String name);
    public abstract String dance();
}

Proxy 类

public class ProxyTest {
    public static daili createProxy(Star star) {
        daili starDaili = (daili) Proxy.newProxyInstance(
                ProxyTest.class.getClassLoader(), // 参数一:指定用哪个类加载器,去加载生成的类
                new Class[] { daili.class }, // 参数二:指定接口,这些接口用于指定代理是什么样的,也就是有哪些方法
                // 参数三,用于指定这个代理做什么事情.
                new InvocationHandler() {
                    /**
                     * 参数一: 代理的对象
                     * 参数二: 要运行的方法sing
                     * 参数三: 调用sing方法的时候传递的实参
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if ("sing".equals(method.getName())) {
                            System.out.println("准备场地,收钱可以开始唱歌了");
                        } else if ("dance".equals(method.getName())) {
                            System.out.println("准备场地,收钱可以开始跳舞了");
                        }
                        // 之后这个代理,去找这个明星类中的唱歌和跳舞的方法,这里需要利用反射的知识
                        method.invoke(star, args);
                        return "谢谢";
                    }
                });
        return starDaili;
    }
}

测试代码

public class Test {

    /**
     * 如果想要让这个明星唱歌:
     * 我们首先要找到这个代理
     * ProxyTest.createProxy(Star star);
     * 然后通过这个代理去执行这个唱歌的方法
     * 代理对象.唱歌("鸡你太美") //也就是我们的第三个参数
     */
    public static void main(String[] args) {
        Star star = new Star("坤哥");
        daili createProxy = ProxyTest.createProxy(star);
        String result = createProxy.sing("鸡你太美");
        System.out.println(result);
        createProxy.dance();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值