【JavaSE java高级技术 反射,注解,动态代理知识总结】

一.反射

1.认识反射

  • 反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造等)
  • 作用: 可以操作一个不存在的类,创建该类的对象,调用该类的方法。所有的Java框架底层都是反射(Spring,Mybatis…)
    代码执行过程
    在这里插入图片描述
  • 1.反射第一步:加载类,获取类的字节码:Class对象
  • 2、获取类的构造器:Constructor对象
  • 3、获取类的成员变量:Field对象
  • 4.获取类的成员方法:Method对象

2.获取加载类三种方法````````````````````````

 //直接通过全类名获取类对象
        Class aClass = Class.forName("com.dream.reflect.Student");
        //通过对象名获取类对象
        Student student = new Student();
        Class aClass1 = student.getClass();

        //通过类名称获取类对象
        Class studentClass = Student.class;
        
        System.out.println(aClass == aClass1);
        System.out.println(aClass1 == studentClass);

3.Class提供了从类中获取构造器的方法以及创建对象

在这里插入图片描述

  • 注意: 如果构造方法是私有化的不能直接通过newInstance()创建对象,需要使用到暴力反射,setAccessible(true)
package com.dream.reflect;
import java.lang.reflect.Constructor;
import java.util.Arrays;

public class GetConstructorAndInstance {
    public static void main(String[] args) throws Exception {
        //1.获取到class字节码文件
        Class clazz = Class.forName("com.dream.reflect.Student");

        //获取所有被public 修饰的构造方法
        Constructor[] constructors = clazz.getConstructors();
        System.out.println(Arrays.toString(constructors));

        //获取到所有的构造方法,包括private
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        System.out.println(Arrays.toString(declaredConstructors));

        //获取到某一个被public 修饰的构造方法
        Constructor cs = clazz.getConstructor(String.class, int.class);
        System.out.println(cs);
        //创建对象使用newInstance()
        Object obj = cs.newInstance("小明", 12);
        System.out.println(obj);

        //获取到某一个被private 修饰的构造方法
        Constructor dc = clazz.getDeclaredConstructor(String.class);
        //由于private修饰的构造方法不能直接使用,需要设置 accessible 为 true,称为暴力反射
        dc.setAccessible(true);
        Object obj2 = dc.newInstance("小红");
        System.out.println(obj2);

        //获取无参构造
        Constructor cs2 = clazz.getConstructor();
        Object obj3 = cs2.newInstance();
        System.out.println(obj3);

    }
}

4.反射获取类中的变量并操作

在这里插入图片描述
代码演示

package com.dream.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;

public class GetFiled {
    public static void main(String[] args) throws Exception {
        //1.获取类的字节码文件对象
        Class clazz = Class.forName("com.dream.reflect.Student");

        //获取到无参构造
        Constructor dsc = clazz.getDeclaredConstructor();
        System.out.println(dsc);

        //创建一个对象
        Object obj = dsc.newInstance();

        // 获取到所有被public修饰的成员变量
        Field[] fields = clazz.getFields();
        System.out.println(Arrays.toString(fields));

        //获取到类中的所有成员变量,无视权限修饰符
        Field[] declaredFields = clazz.getDeclaredFields();
        System.out.println(Arrays.toString(declaredFields));

        //获取到name成员变量
        Field ageFiled = clazz.getField("age");
        System.out.println(ageFiled);
        // 获取到name成员变量的值,无视权限修饰符
        Field nameFiled = clazz.getDeclaredField("name");
        System.out.println(nameFiled);


        //给age赋值
        ageFiled.set(obj, 18);


        //给name赋值,因为name是private修饰的,想要赋值必须使用暴力反射
        nameFiled.setAccessible(true);
        nameFiled.set(obj, "小明");

        //获取name的值
        System.out.println(nameFiled.get(obj));
        System.out.println(ageFiled.get(obj));
    }
}

5.通过反射获取到类中的方法

在这里插入图片描述
代码演示

 public static void main(String[] args) throws Exception{
        //1.获取到类的字节码文件对象
        Class clazz = Class.forName("com.dream.reflect.Student");
        //创建目标对象
        Object target = clazz.getConstructor().newInstance();

        //获取到所有被public修饰的方法
        Method[] methods = clazz.getMethods();
        System.out.println(Arrays.toString(methods));

        //获取到所有的方法,无视权限修饰符
        Method[] declaredMethods = clazz.getDeclaredMethods();
        System.out.println(Arrays.toString(declaredMethods));

        //获取到public 修饰的method方法
        Method method = clazz.getMethod("method", String.class);
        //调用method方法
        method.invoke(target, "hello");

        //获取到指定方法,无视权限修饰符
        Method method2 = clazz.getDeclaredMethod("show");
        //暴力反射
        method2.setAccessible(true);
        method2.invoke(target);
    }

二.注解

就是java代码里的特殊标记,比如:@Override,@Test等,作用是: 让其他程序根据注解信息来决定怎么职系那个该程序

1.自定义注解

  • 1.注解的本质就是接口
  • 2.所有的注解都继承了java.lang.annotation.Annotation接口
  • 3.注解的属性本质上就是接口中的抽象方法
    1. bbbb使用注解,本质上是提供了一个Anno2接口的实现类对象
      
  • 注解就是对程序的特殊标识,给程序看的
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

2.元注解

在这里插入图片描述

在这里插入图片描述
准备操作
定义一个枚举

package com.dream.enumerage;

public enum Modify {
    ADD,
    DELETE,
    MODIFY
}

1.定义注解

package com.dream.anno;

import com.dream.enumerage.Modify;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    String name();
    int age();
    Modify value();
}
package com.dream.anno;


import com.dream.enumerage.Modify;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Fly {

    Modify value();
}

2.使用注解

package com.dream.annoTest;

import com.dream.anno.AutoFill;
import com.dream.anno.Fly;
import com.dream.enumerage.Modify;

public class Message {
    @Fly(value = Modify.ADD)
    @AutoFill(value = Modify.MODIFY,name = "小明", age = 18)
    public void show() {

    }
}

3.注解的解析

package com.dream.Controller;

import com.dream.anno.RequestMapping;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class SpringMvc {
    public static void main(String[] args) throws Exception{
        //0.创建一个map集合
        Map<String,Method> map = new HashMap<>();
        //1.获取到UserController的字节码文件对象
        Class clazz = Class.forName("com.dream.Controller.UserController");
        //2.获取到无参构造并且创建一个目标对象
        Object target = clazz.getConstructor().newInstance();

        //3.获取到字节码文件中的所有方法
        Method[] methods = clazz.getMethods();

        //4.遍历所有方法,过滤出含有@RequestMapping注解的方法
        Arrays.stream(methods).filter(method->method.isAnnotationPresent(RequestMapping.class)).forEach(method->{
            //5.获取到方法上注解的value
            RequestMapping declaredAnnotation = method.getDeclaredAnnotation(RequestMapping.class);
            String value = declaredAnnotation.value();
            //6.将value和对应的方法存放到一个map集合中
            map.put(value,method);
        });

        //7.用户录入访问路径,匹配
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("请输入访问路径:");
            String path = sc.next();
            if ("exit".equals(path)){
                break;
            }
            //8.获取到指定路径的方法
            Method method = map.get(path);
            if (method == null){
                System.out.println("404,方法不存在,访问失败");
                continue;
            }
            //9.激活方法
            method.invoke(target);
        }

    }
}

三.动态代理

  • 动态代理是对象的另一种出创建方式
  • 内存中动态生成一个类,并根据该类创建对象返回
  • 在不修改源码的情况下,增强方法

1.创建代理对象的步骤

(0).准备好要创建代理对象的类或者接口

(1).首先是使用Proxy.newProxyInstance()创建一个代理对象,并且将代理对象返回

  • 参数一: 就是代理对象所在类的类加载器,使用类名.class.getclassLoader()
  • 参数二: 就是你要为哪些接口创建代理对象,将接口的字节码文件传入,接口名.class
  • 参数三: InvocationHandler是一个接口,需要创建它的匿名内部类对象,重写它的invoke方法
  • invoke方法每次通过代理对象调用方法都会执行一次invoke里面的代码
    public static Object newProxyInstance(
                ClassLoader loader, 类加载器 当前所在类类名.class.getClassLoader()
                Class<?>[] interfaces, 代理对象实现的接口,new Class[]{接口名.class}
                InvocationHandler h  接口,使用匿名内部类的方式创建对象,重写invoke方法
                invoke方法每次通过动态代理对象调用方法都会执行该方法
       )

在这里插入图片描述
其中invoke方法也有三个参数
参数一:是一个object代理对象
参数二:是使用代理对象调用方法的那个方法比如sale()通过代理对象调用,此处的method代表的就是sale
参数三: 就是方法传递过来的参数
当然我们最后可以将方的返回值返回出去

(2).使用代理对象

在这里插入图片描述

2.使用一个基本案例剖析动态代理

  • 这里我是事先创建了定义好了一个接口和接口的实现类对象,现在我需要接口中的方法在不改变java源代码的情况下为接中的方法添加一个计算方法执行耗时的功能

  • 1.准备好源代码的jar包
    在这里插入图片描述

  • 2.创建动态代理的对象

package com.dream.proxyCalculateTime;

import com.itheima.proxy.Computer;
import com.itheima.proxy.ThinkPad;

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

public class ProxyCalculateTime {
    public static Computer getComputerProxy() {

        Computer c = new ThinkPad();


        //1.创建一个代理对象
        Computer proxy = (Computer) Proxy.newProxyInstance(ProxyCalculateTime.class.getClassLoader(), new Class[]{Computer.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long start = System.currentTimeMillis();

                //方法返回结果
                Object result = method.invoke(c, args);
                long end = System.currentTimeMillis();

                System.out.println(method.getName() + "执行耗时:" + (end - start));
                return result;
            }

        });
        return proxy;
    }
}

  • 3.使用动态代理对象
    在这里插入图片描述

四.总结

本章总结了java中的框架底层常用到的高级技术知识点,首先是总结了java中的反射,学会如何去创建一个不在的类的对象,去调用方法,给全局变量赋值等如何调用java中有权限修饰符的方法,我们称为暴力反射,其次总结了注解的知识,注解是一种标识,它可以让java框架去识别,去做一些指定的操作,创建注解必须实现两个元注解一个target,一个Retention,最后总结了java中的动态代理,它主要是为我们方法加强功能的,使用动态代理可以在不修改源文件的情况下去批量增强方法的功能。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Transcend oneself

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值