Java - 反射 & 注解 & 动态代理

本文详细介绍了Java中的反射机制,包括获取字节码对象、字段、构造器和方法的反射操作,以及如何进行暴力访问。接着讲解了注解的应用,如JUnit单元测试,注解的定义、使用、注意事项以及元注解。最后,探讨了动态代理的概念,展示了静态代理和动态代理模式的实现。
摘要由CSDN通过智能技术生成

反射 & 注解 & 动态代理

一、反射

  1. 简介

  2. 字节码对象

    public static void main(String[] args) throws ClassNotFoundException {
    
        //获取字节码对象
        //方式1:
        Student s = new Student();
        Class clazz1 = s.getClass();
        //方式2:不需要导包  软编码
        Class clazz2 = Class.forName("com.itheima.demo01反射.p01字节码反射.Student");
        //方式3:
        Class clazz3 = Student.class;
    
        //字节码对象是唯一的
        System.out.println(clazz1==clazz2);
        System.out.println(clazz1==clazz3);
    
    
        //获取类名
        System.out.println(clazz3.getSimpleName());
        //获取全类名
        System.out.println(clazz3.getName());
    }
    
  3. 字段的反射

    public class Test {
        public static void main(String[] args) throws Exception {
    
            //前提:获取字节码对象
            Class clazz = Zi.class;
    
            //获取本类和父类中所有公共权限的字段
            Field[] fields = clazz.getFields();
            System.out.println("fields.length:"+fields.length);
            //获取本类中所有权限的字段
            Field[] declaredFields = clazz.getDeclaredFields();
            System.out.println("declaredFields.length:"+declaredFields.length);
            //获取本类和父类某个公共权限的字段
            Field pubFuNum = clazz.getField("pubFuNum");
            Field pubZiNum = clazz.getField("pubZiNum");
            //Field priZiNum = clazz.getField("priZiNum");//error
            //获取本类中某个任意权限的字段
            Field pubZiNum2 = clazz.getDeclaredField("pubZiNum");
            Field priZiNum = clazz.getDeclaredField("priZiNum");
    
    
            //给字段设置值
            Zi z = new Zi();
            pubZiNum.set(z,100);
            System.out.println(z.pubZiNum);
            //获取字段的值
            System.out.println(pubZiNum.get(z));
    
            //操作私有的内容(暴力访问)
            priZiNum.setAccessible(true);
            priZiNum.set(z,200);
            System.out.println(priZiNum.get(z));
    
        }
    }
    class Fu{
        public int pubFuNum;
        private int priFuNum;
    }
    class Zi extends Fu{
        public int pubZiNum;
        private int priZiNum;
    }
    
  4. 构造方法的反射

    • 注意参数是 类型.class 。

    public class Test {
        public static void main(String[] args) throws Exception {
    
            //获取字节码对象
            Class clazz = Zi.class;
    
            //获取本类所有公共权限构造器
            Constructor[] cons = clazz.getConstructors();
            System.out.println("cons.length:" + cons.length);
    
            //获取本类所有权限构造器
            Constructor[] declaredCons = clazz.getDeclaredConstructors();
            System.out.println("declaredCons.length:" + declaredCons.length);
    
            //获取本类某个公共权限构造器
            Constructor con1 = clazz.getConstructor(null);
            Constructor con2 = clazz.getConstructor(int.class);
            //Constructor con3 = clazz.getConstructor(int.class,int.class);//error
            //获取本类某个任意权限构造器
            Constructor con22 = clazz.getDeclaredConstructor(int.class);
            Constructor con3 = clazz.getDeclaredConstructor(int.class, int.class);
    
            //创建对象
            //无参
            //Object o = con1.newInstance(null);
            Zi z1 = (Zi) con1.newInstance(null);
            System.out.println(z1);
            //有参
            Zi z2 = (Zi) con2.newInstance(100);
            System.out.println(z2);
            //私有 (暴力访问)
            con3.setAccessible(true);
            Zi z3 = (Zi) con3.newInstance(100, 200);
            System.out.println(z3);
    
        }
    }
    
    class Fu {
        public Fu() {
        }
    }
    
    class Zi extends Fu {
        public Zi() {
        }
    
        public Zi(int num) {
            System.out.println("Zi..."+num);
        }
    
        private Zi(int num, int num2) {
            System.out.println("Zi..."+num + "..." + num2);
        }
    }
    
  5. 方法的反射

    • 获取没事,但是调用的时候私有方法需要暴力访问

    public class Test {
        public static void main(String[] args) throws Exception {
            
            //获取字节码对象
            Class clazz = Zi.class;
    
            //获取本类和父类所有公共权限方法(包含了Object继承下来的)
            Method[] methods = clazz.getMethods();
            System.out.println("methods.length:"+methods.length);//12
    
            //取本类中所有任意权限的方法
            Method[] declaredMethods = clazz.getDeclaredMethods();
            System.out.println("declaredMethods.length:"+declaredMethods.length);//3
    
            //获取本类某个公共权限方法
            Method pubZiMethod = clazz.getMethod("pubZiMethod",null);
            Method pubZiMethod2 = clazz.getMethod("pubZiMethod",int.class);
            //Method priZiMethod3 = clazz.getMethod("priZiMethod",null);//error
            //获取本类某个任意权限方法
            Method pubZiMethod22 = clazz.getDeclaredMethod("pubZiMethod", int.class);
            Method priZiMethod3 = clazz.getDeclaredMethod("priZiMethod", null);
    
            //使用方法
            //公共权限
            Zi z = new Zi();
            pubZiMethod.invoke(z,null);
            pubZiMethod2.invoke(z,100);
            //私有权限(暴力访问)
            priZiMethod3.setAccessible(true);
            priZiMethod3.invoke(z,null);
    
        }
    }
    
    //Object还有9个公共的方法
    class Fu extends Object{
        public void pubFuMethod(){}
        private void priFuMethod(){}
    }
    class Zi extends Fu{
        public void pubZiMethod(){
            System.out.println("pubZiMethod...");
        }
        public void pubZiMethod(int num){
            System.out.println("pubZiMethod..."+num);
        }
        private void priZiMethod(){
            System.out.println("priZiMethod...");
        }
    }
    

二、注解

  1. JUnit单元测试

    public class TestDemo {
        @Test
        public void test1() {
            System.out.println("test1");
        }
    
        @Test
        public void test2() {
            System.out.println("test2");
        }
    
        @Before
        public void before1() {
            System.out.println("before1");
        }
        @Before
        public void before2() {
            System.out.println("before2");
        }
        @After
        public void after1() {
            System.out.println("after1");
        }
        @After
        public void after2() {
            System.out.println("after2");
        }
        @BeforeClass
        public static void staticBefore1() {
            System.out.println("staticBefore1");
        }
        @BeforeClass
        public static void staticBefore2() {
            System.out.println("staticBefore2");
        }
        @AfterClass
        public static void staticAfter1() {
            System.out.println("staticAfter1");
        }
        @AfterClass
        public static void staticAfter2() {
            System.out.println("staticAfter2");
        }
    }
    
    • @Test 相关注解执行顺序

      <1> 运行 test1 方法执行顺序的结果

      <2> 运行 TestDemo 类,会自动执行所有方法

  2. 注解简介

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zYb4sBVy-1662564991631)(C:\Users\ASUS\Desktop\学习笔记\自己\笔记截图\image-20220907103553503.png)]

  3. 注解的定义与使用

    public @interface MyAnno {
        //注解属性 :属性类型  属性名();
        //基本类型
        int i();
        //字符串类型
        String s();
        //枚举类型
        Color co();
        //注解类型
        Override o();
        //Class类
        Class cl();
        //一维数组类型
        String[] ss();
        //Demo[] ds();//error 一维数组类型只能是上述5种。
    }
    
    //使用注解
    @MyAnno(i=1,s="abc",co=Color.RED,o=@Override,cl=Object.class,ss={"abc","def"})
    public class Test {
    }
    
  4. 注解的注意事项

    //注解属性若是一维数组类型,只有一个元素的值时,可以省略{}
    @MyAnno(ss = "abc", i2 = 100)
    //若注解属性名为value,且只有一个属性时,赋值时属性名可以省略
    //@MyAnno2(value = {"abc"})
    //@MyAnno2(value = "abc")
    @MyAnno2("abc")
    public class Test {
    
    }
    
    @interface MyAnno {
        String[] ss();
    
        //注解属性可以通过default指定默认值,有默认值的属性可以不赋值
        int i() default 0;
    
        //没有默认值的注解属性必须赋值
        int i2();
    }
    
    @interface MyAnno2 {
        String[] value();
    }
    
  5. 元注解

    @MyAnno
    public class Fu {
        @MyAnno
        int num;
    
        @MyAnno
        public void method() {
        }
    }
    
    class Zi extends Fu {
    
    }
    
    @Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
    //@Retention(value = RetentionPolicy.SOURCE)//源码期间保留
    //@Retention(value = RetentionPolicy.CLASS)//字节码期间保留
    @Retention(value = RetentionPolicy.RUNTIME)//运行期间保留
    @Inherited//当前的注解使用后,可以继承给子类
    @Documented  //表示当前注解将来在文档中显示
    @interface MyAnno {
    
    }
    
  6. 注解解析

    public class Test {
        public static void main(String[] args) throws Exception {
            //获取父类字节码对象
            Class fuClazz = Fu.class;
            //获取父类成员上的注解
            Annotation[] fuClassAnnos = fuClazz.getAnnotations();
            System.out.println("fuClassAnnos.length:"+fuClassAnnos.length);
            Annotation[] fuClassDeclareAnnos = fuClazz.getDeclaredAnnotations();
            System.out.println("fuClassDeclareAnnos.length:"+fuClassAnnos.length);
    
            //获取方法的注解
            Method fuMethod = fuClazz.getMethod("method", null);
            Annotation[] fuMethodAnnos = fuMethod.getAnnotations();
            System.out.println("annos2.length:"+fuMethodAnnos.length);
            Annotation[] fuMethodDeclaredAnnos = fuMethod.getDeclaredAnnotations();
            System.out.println("fuMethodDeclaredAnnos.length:"+fuMethodDeclaredAnnos.length);
    
    
            //获取指定的注解
            //类上的注解
            MyAnno anno = (MyAnno) fuClazz.getAnnotation(MyAnno.class);
            MyAnno anno2 = (MyAnno) fuClazz.getDeclaredAnnotation(MyAnno.class);
            System.out.println(anno==anno2);
            //方法上的注解
            MyAnno anno3 = fuMethod.getAnnotation(MyAnno.class);
            MyAnno anno4 = fuMethod.getDeclaredAnnotation(MyAnno.class);
            System.out.println(anno3==anno4);
    
            //获取注解属性的值
            System.out.println(anno.i());
            System.out.println(anno3.i());
        }
    }
    
    @MyAnno(i=100)
    @MyAnno2
    class Fu {
        @MyAnno(i=200)
        public void method() {
        }
    }
    
    //获取注解的时候,是在运行阶段获取的,必须保证注解有效时期是到运行时期。
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnno {
        int i();
    }
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnno2 {
    }
    
  7. 模拟Test注解练习

    • Demo 类

      public class Demo {
          @MyTest
          public void method1(){
              System.out.println("method1");
          }
      
          @MyTest
          public void method2(){
              System.out.println("method2");
          }
      
          @MyTest
          public void method3(){
              System.out.println("method3");
          }
      
      
          public void method4(){
              System.out.println("method3");
          }
      }
      
    • @MyTest 注解

      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface MyTest {
      }
      
    • 测试类

      //模拟Junit测试注解功能
      public class Test {
          public static void main(String[] args) throws Exception {
      
              //获取字节码对象
              Class clazz = Demo.class;
              //根据字节码对象创建对象  T newInstance()  创建由此 类对象表示的类的新实例。
              Demo td = (Demo) clazz.newInstance();
      
              //获取所有的方法的Method对象
              Method[] methods = clazz.getMethods();
      
              //遍历所有的Method对象
              for (Method method : methods) {
                  //判断该元素上是否包含MyTest注解(如果包含,就让这个方法运行)
                  //default boolean isAnnotationPresent(类  annotationClass) 如果此元素上 存在指定类型的注释,则返回true,否则返回false。
                  //System.out.println(method.getName() + ".." + method.isAnnotationPresent(MyTest.class));
                  if (method.isAnnotationPresent(MyTest.class)) {
                      //执行当前方法
                      method.invoke(td, null);
                  }
              }
          }
      }
      

三、动态代理

  1. 概述 - 代理只适用于加强final修饰的类,就是那种我们没法改变的类,但是我们在它的基础上加一些东西

    public class FunProxy implements TicketInterface {
        private Fan fan;
    
        public FunProxy(Fan fan) {
            this.fan = fan;
        }
    
        @Override
        public int buyTicket() {
            System.out.println("开启6G模式");
            fan.buyTicket();
            System.out.println("买了一堆票");
            return 10;
        }
    }
    
  2. 静态代理模式

  3. 动态代理模式

    public class Test {
        public static void main(String[] args) {
            Fan fan = new Fan();
    
            //static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
            //准备数据
            //类加载器
            ClassLoader classLoader = fan.getClass().getClassLoader();
            //被代理的类的所有的父接口
            Class<?>[] interfaces = fan.getClass().getInterfaces();
            //使用匿名内部类创建InvocationHandler实现类对象
            InvocationHandler handler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //System.out.println(method.getName() + "方法被调用了");
    				
                    //执行原来的任何方法都会调用加强后的方法,所以可以加个判断
                    if (method.getName().equals("buyTicket")) {
                        System.out.println("开启高速通道");
                        System.out.println("买到票了");
                        return 10;
                    } else {
                        return null;
                    }
    
                    //method.invoke(fan,null);
                    //不能通过proxy调用方法,会造成死循环操作。
                    //method.invoke(proxy,null);// error 递归调用
                    //如果有返回值,返回值与代理的方法返回结果不一致,可能异常。
                    //return null;
                }
            };
            //获取代理对象
            TicketInterface ti = (TicketInterface) Proxy.newProxyInstance(classLoader, interfaces, handler);
            //执行操作
            int count = ti.buyTicket();
            System.out.println(count);//10
    
            ti.toString();
    
    
        }
    }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值