Java基础之注解和反射

注解和反射是很过开源框架底层的实现逻辑底层都是通过注解和反射。

1.注解

1.1注解入门

注解(Annotation)很重要,现在的开发模式都是基于注解的,JPA是基于注解的,从Spring基于注解的,从Hibernate也是基于注解的,注解是JDK1.5之后才有的新特性.

注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,没有加,则等于没有任何标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。

1.2内置注解

JDK1.5之后内部提供的三个注解

​ @Deprecated 意思是“废弃的,过时的

​ @Override 意思是“重写、覆盖

​ @SuppressWarnings 意思是“压缩警告”,作用:用于抑制编译器产生警告信息。

package com.njit.annotation;

/**
 * @author 朱友德
 */
public class AnnotationTest {
    /**
     * @param args 
     * 这里就是注解,称为压缩警告,这是JDK内部自带的一个注解,一个注解就是一个类,在这里使用了这个注解就是创建了 SuppressWarnings类的一个实例对象
     */
    public static void main(String[] args) {
        sayHello();
        //这里的sayHello()方法画了一条横线表示此方法已经过时了,不建议使用了

        @SuppressWarnings("unused")
        String str = "sdfd";
    }

    /**
     * 这也是JDK内部自带的一个注解,意思就是说这个方法已经废弃了,不建议使用了
     */
    @Deprecated 
    public static void sayHello() {
        System.out.println("hi,xxxx");
    }

    /**
     * 这也是JDK1.5之后内部提供的一个注解,意思就是要重写(覆盖)JDK内部的toString()方法
     * @return
     */
    @Override 
    public String toString() {
        return "xxxx";
    }
}

1.3自定义注解,元注解

元注解

元注解就是用来注解其他注解,java提供了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明.

这些类型和他们所支持的类在java.lang.annotation包中可以找到(@Target, @Retention,@Documented, @Inherited

  • @Target:用于描述注解的使用范围(即被描述的注解可以用在什么地方)
  • @Retention:表示需要在什么级别保存该注释信息,用于描述注解的声明周期(SOURCE<CLASS<RUNTIME
  • @Documented:说明该注解被包含在javadoc中
  • @Inherited:说明子类可以继承父类中的注解

自定义注解

当使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口。

  • @interface用来声明一个注解,格式:public @ inteface 注解名{定义内容}
  • 其中的每一个方法实际上是声明了一个配置参数。
  • 方法名称就是参数的名称
  • 返回值类型就是参数的类型(返回值只能是基本类型,Class,String, enum)
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员,一般参数名为value
  • 注解元素必须有值,我们定义注解元素时,经常使用空字符串,0作为默认值
// 自定义注解
public class MyAnotation {

    // 注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
    @Hello(name = "njit", age = 3)
    public void test() {

    }
}
/**
 * @author 朱友德
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Hello{

    // 注解的参数:参数类型+参数名();
    String name() default "";

    int age();
}

1.4 自定义注解的真实使用场景

  • 场景描述:使用springboot拦截器实现这样一个功能,如果方法上加了@LoginRequired,则提示用户该接口需要登录才能访问,否则不需要登录
//1. 首先先定义一个注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
    
}

//2. 写两个接口
@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @GetMapping("/sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}
// 3. 测试 现在访问两个接口是都可以成功的

// 4. 自定义拦截器,实现拦截功能
public class SourceAccessInterceptor implements HandlerInterceptor {
  @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入拦截器了");

        // 反射获取方法上的LoginRequred注解
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
        if(loginRequired == null){
            return true;
        }

        // 有LoginRequired注解说明需要登录,提示用户登录
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print("你访问的资源需要登录");
        return false;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

// 5. 实现spring类WebMvcConfigurer,创建配置类把拦截器添加到拦截器链中
@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");
    }
}

// 6. 在刚刚的接口中的一个接口添加注解
@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @LoginRequired
    @GetMapping("/sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}
//7. 验证一下,访问接口B就会被拦截

2.反射

image-20201122184638335

反射的主要API

image-20201122185218896

反射的简单案例

package com.njit.reflection;

/**
 * @Author: njitzyd
 * @Date: 2020/11/22 18:53
 * @Description: 反射测试
 * @Version 1.0.0
 */
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 通过反射获取类的Class对象
        Class c1 = Class.forName("com.njit.reflection.User");


        Class c2 = Class.forName("com.njit.reflection.User");
        Class c3 = Class.forName("com.njit.reflection.User");

        //一个类在内存中只有一个Class对象
        // 一个类被加载后,类的整体结构都会被封装在Class对象中
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());

    }

}

/**
 * 实体类
 */
class User {
    private String name;

    private int id;
    private int age;

    public User() {

    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

Class类

image-20201122190422204

从上图也可以得出,一个类只会有一个Class对象。

image-20201122202426446

image-20201122202535405

获取Class类的几种方式

image-20201122202735374

获取Class类的代码演示

package com.njit.reflection;

/**
 * @Author: njitzyd
 * @Date: 2020/11/22 20:28
 * @Description: 测试获取Class对象几种方式
 * @Version 1.0.0
 */
public class Test02 {

    public static void main(String[] args) throws ClassNotFoundException {

        Person person = new Student();
        System.out.println("这个人是:"+person.getName());


        // 方式1:通过对象获取
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        // 方式2: 通过forName()获取
        Class c2 = Class.forName("com.njit.reflection.Student");
        System.out.println(c2.hashCode());

        // 方式3: 通过类名
        Class c3 = Student.class;
        System.out.println(c3.hashCode());

        // 方式4: 基本内置类型的包装类都有一个Type属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);
        
        // 获取父类的Class
        Class c5 =c1.getSuperclass();
        System.out.println(c5.hashCode());

    }
}

class Person{

    private String name;

    public Person() {
    }

    public Person(String name) {

        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    public Student() {
        super("学生");
    }
}

class Teacher extends Person{
    public Teacher() {
        super("老师");
    }
}

哪些类可以有Class对象(后面跟了部分示例)

  • class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • interface:接口 (Comparable.class)
  • []: 数组 (String[].class)
  • enum: 枚举 (ElementType.class)
  • annotation:注解@interface (Override.class)
  • primitive type:基本数据类型 (Integer.class, int.class)
  • void (void.class)

类的加载

类的加载过程

image-20201122211118528

image-20201122211249797

什么时候会发生类初始化

image-20201122214349900

上面被动引用第一条就是,通过子类调用父类的静态变量时,比如(Son.b),注意是Son类,是类不是对象。

第二条 如果定义 Son[] array = new Son[5],则Son类是不会初始化的。

第三条,如果是static final 的常量,则不会触发初始化。

类加载器的作用

image-20201122215637787

image-20201122215838012

package com.njit.reflection;

/**
 * @Author: njitzyd
 * @Date: 2020/11/22 22:01
 * @Description: Test03
 * @Version 1.0.0
 */
public class Test03 {

    public static void main(String[] args) throws ClassNotFoundException {
        // 获取系统的类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的父类加载器--> 扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取扩展类加载器的父类加载器 --> 根加载器(c/c++),会返回null,这个根加载器是无法直接获取的
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        // 测试当前类的类加载器
        ClassLoader classLoader = Class.forName("com.njit.reflection.Test03").getClassLoader();
        System.out.println(classLoader);

        // 测试jdk内置的类加载器 ,也是根类加载器
        ClassLoader commonClassLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(commonClassLoader);

        // 获取系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));

    }
}

双亲委派机制

双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。

双亲委派机制参考

获取类的运行时结构

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

/**
 * @Author: njitzyd
 * @Date: 2020/11/23 20:27
 * @Description: Test04
 * @Version 1.0.0
 */
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {

        Class c1 = Class.forName("com.njit.reflection.User");

        // 获取类名
        // 获取 包名+类名
        System.out.println(c1.getName());
        // 获取类名
        System.out.println(c1.getSimpleName());

        // 获取类的属性
        // 只能获取public的属性
        Field[] fields = c1.getFields();
        // 获取所有的属性
        fields = c1.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        // 获取指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        // 获取类的方法
        // 获取本类以及父类的所有public 方法
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println("正常的获取方法:"+method);
        }

        // 获取本类的所有方法,包括私有方法
        methods = c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("获取本类所有的方法:"+method);
        }
        // 获取指定的方法,需要传入入参的类型
        Method getName = c1.getMethod("getName", null);
        System.out.println(getName);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(setName);

        // 获得构造方法
        Constructor[] constructors = c1.getConstructors();

        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        constructors = c1.getDeclaredConstructors();

        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        //获得指定的构造方法
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println(constructor);

    }
}

动态创建对象执行方法

image-20201124215146289

性能分析

package com.njit.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @Author: njitzyd
 * @Date: 2020/11/24 22:06
 * @Description: 测试性能
 * @Version 1.0.0
 */
public class Test06 {

    /**
     * 普通方式
     */
    public static void test01(){

        User user = new User();

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10_0000_0000; i++) {
            user.getName();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行10亿次:"+(endTime-startTime)+"ms");
    }

    /**
     * 反射方式
     */
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getMethod("getName", null);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10_0000_0000; i++) {
            getName.invoke(user, null);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次:"+(endTime-startTime)+"ms");
    }

    /**
     * 反射方式,关闭检测
     * @throws NoSuchMethodException
     */
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getMethod("getName", null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10_0000_0000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式关闭检测执行10亿次:"+(endTime-startTime)+"ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}

####################
普通方式执行10亿次:3ms
反射方式执行10亿次:1454ms
反射方式关闭检测执行10亿次:977ms

反射操作泛型

package com.njit.reflection;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/**
 * @Author: njitzyd
 * @Date: 2020/11/24 22:15
 * @Description: 通过反射获取泛型
 * @Version 1.0.0
 */
public class Test07 {

    public static void test01(Map<String, User> map, List<Integer> list){
        System.out.println("test01");
    }

    public static Map<Long, User> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {

        Method method = Test07.class.getMethod("test01", Map.class, List.class);

        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("###"+genericParameterType);
            if (genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        method = Test07.class.getMethod("test02", null);
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
        
    }
}
##########
###java.util.Map<java.lang.String, com.njit.reflection.User>
class java.lang.String
class com.njit.reflection.User
###java.util.List<java.lang.Integer>
class java.lang.Integer
class java.lang.Long
class com.njit.reflection.User

下面的图作为了解

image-20201124221442278

反射操作注解

package com.njit.reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;

/**
 * @Author: njitzyd
 * @Date: 2020/11/24 22:30
 * @Description: 反射操作注解
 * @Version 1.0.0
 */
public class Test08 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.njit.reflection.Animal");

        // 通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        System.out.println(annotations);

        // 获取注解的value值
        MyTest myTest = (MyTest) c1.getAnnotation(MyTest.class);
        System.out.println(myTest.value());

        // 获取类的字段的指定注解
        Field field = c1.getDeclaredField("name");
        FieldTest annotation = field.getAnnotation(FieldTest.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@MyTest("db_test")
class Animal{

    @FieldTest(columnName = "id", type = "Integer", length = 10)
    private int id;

    @FieldTest(columnName = "age", type = "Integer", length = 8)
    private int age;

    @FieldTest(columnName = "name", type = "String", length = 10)
    private String name;

    public Animal() {
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

/**
 * 自定义的简单注解,注解在类上
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{
    String value();
}

/**
 * 自定义的简单注解,注解在属性上
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest{
    String columnName();
    String type();
    int length();
}

njitzyd
58 声望7 粉丝