• 功能:Java注解的介绍和反射使用


    功能:Java注解的介绍和反射使用

    一、注解

    1、注解介绍

    java注解(Annotation),又称为java标注,是jdk5.0引入的一种机制。

    Java 语言中的类、方法、变量、参数和包等都可以被标注,对这些代码段进行解释,编译时生成class时,标注也可以被编译。在运行时,java可以通过反射获取到注解内容,进行一些骚操作,进而简化开发。

    2、注解分类

    Java 定义了一些注解,有些比较常见

    1. @Override:检查方法是否重写父类方法
    2. @Deprecated:标记方法过时
    3. @SuppressWarnings:忽略警告

    元注解,标注注解的注解,一切注解的开始

    1. @Retention:使用范围,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问
    2. @Documented:标记这些注解是否包含在用户文档中
    3. @Target:作用范围,可以标记哪些代码块,方法,类或者是字段等其他
    4. @Inherited:标记这个注解是继承于哪个注解类

    java7后加入的注解

    1. @SafeVarargs:Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告
    2. @FunctionalInterface:Java 8 开始支持,标识一个匿名函数或函数式接口
    3. @Repeatable:Java 8 开始支持,标识某注解可以在同一个声明上使用多次

    3、自定义注解

    1)定义语法

    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        
        String value() default "";
        
        String[] params() default {};
        
    }
    

    如上:

    1. @Documented:此注解将包含在用户文档中
    2. @Target: ElementType.Type是说,该注解可以在类、接口(包含注解)、枚举上使用
    3. @Retention:此注解将编译至class文件中,在运行时,会被虚拟机读取使用
    4. 和定义接口不同的是,注解的定义前添加@
    5. 如果是字段名是value,则使用注解时可以省略字段名

    2)RetentionPolicy,作用范围枚举

    package java.lang.annotation;
    
    public enum RetentionPolicy {
        SOURCE,  // Annotation信息仅存在于编译器处理期间,编译后该注解不存在
        CLASS,   // 编译器将Annotation存储于类对应的class文件中
        RUNTIME  // 编译器将Annotation存储于class文件中,并且可由JVM读入 
    }
    

    3)ElementType,使用范围枚举

    package java.lang.annotation;
    
    public enum ElementType {
        // 类,接口(包括注解),枚举
        TYPE,
        // 字段,包括枚举字段
        FIELD,
        // 方法
        METHOD,
        // 方法参数,括号内的形参
        PARAMETER,
        // 构造方法
        CONSTRUCTOR,
        // 局部变量
        LOCAL_VARIABLE,
        // 注解
        ANNOTATION_TYPE,
        // 包
        PACKAGE,
        // Type parameter declaration,@since 1.8
        TYPE_PARAMETER,
        // Use of a type,@since 1.8
        TYPE_USE
    }
    

    二、java反射

    1、反射介绍

    1)反射是什么

    简单的来说,反射就是运行时才知道操作的类是什么,并且在运行阶段有虚拟机进行实例化,可知道内部所有的(包括private私有的)属性和方法,这种机制叫做反射

    java之所以有了这种机制,才会成为一门准动态语言

    动态语言和静态语言的区别

    • 动态语言:是指一类在运行时,也可以改变程序结构的语言,加入新的函数,对象,甚至是代码都可以被引入,可以根据某些条件改变自身结构
      • 主要语言有:C#、JavaScript、PHP、Python
    • 静态语言:相对于动态语言,在运行时结构不可改变的语言就是静态语言
      • 主要语言有:Java、C、C++

    在java有了反射之后,java就可以称为准动态语言,反射使得java有了一定的动态性,我们可以通过这种机制,让编程更加灵活,玩出骚操作。

    2)简单明白反射作用

    在程序开发之初,程序员往往都知道自己需要使用到某些类,这样实例化对象是没问题的,程序也是可以正常访问的,如下

    程序员知道要把东西给学生,所以new Student()进行实例化

    public class SomeThingTest {
        public static void main(String[] args) {
            Student student = new Student();
            student.give("一个红包");
        }
    }
    
    abstract class Person {
        public abstract void give(String some);
    }
    
    class Student extends Person{
        @Override
        public void give(String some) {
            System.out.println("给了学生:" + some);
        }
    }
    
    class Teacher extends Person{
        @Override
        public void give(String some) {
            System.out.println("给了老师:" + some);
        }
    }
    

    那程序员不知道要把东西交给谁呢?程序进行不下去了,所以为了解决此类问题,反射机制诞生了,如下

    运行结果相差不大,但内部实现机制完全不一致

    public class SomeThingTest {
        public static void main(String[] args) throws Exception {
            Class cls = Class.forName("com.banmoon.something.Teacher");
            Method giveMethod = cls.getMethod("give", String.class);
            Object teacher = cls.newInstance();
            giveMethod.invoke(teacher, "一个苹果");
        }
    }
    

    问题:com.banmoon.something.Teacher这段字符串还不是程序员写死的,就算内部是反射实现的,那这样岂不是多此一举?

    非也非也,我给大家看一段常用的配置文件,大家就明白了

    spring:
    datasource:
         type: com.alibaba.druid.pool.DruidDataSource
     driver-class-name: com.mysql.jdbc.Driver
     url: jdbc:mysql://127.0.0.1:3306/test
     username: root
     password: 1234
    

    熟悉吗,那个数据库连接驱动数据库连接池,那些开发框架的程序员,他们可不知道我们使用的是什么数据库和什么连接池,所以在我们指定对应的驱动路径后,java虚拟机才反射去获取对应的驱动实例。

    这样一来,可以说反射机制是框架设计的灵魂,若没有反射,也没有如此丰富全面的java框架,庞大的java生态系统

    2、反射使用

    1)反射获取Class对象

    在java中,万物皆对象。所以类在反射出来后产生的对象便是Class

    获取反射的3种方式,其中2、3种方法的使用是在编码阶段都清楚类的前提下使用的

    1. 使用Class对象的静态方法,forName(),根据类的全路径进行加载
    2. 通过类名.class获取该类的Class对象
    3. 使用实例对象.getClass()获取该类的Class对象
    public class SomeThingTest {
        public static void main(String[] args) throws ClassNotFoundException {
            Class cla1 = Class.forName("java.lang.String");
            Class cla2 = String.class;
            Class cla3 = "abc".getClass();
        }
    }
    

    问题:以下cla3cla4是否为同个对象?

    public class SomeThingTest {
        public static void main(String[] args) throws ClassNotFoundException {
            Class cla3 = "abc".getClass();
            Class cla4 = new String("123").getClass();
            System.out.println(cla3.hashCode());
            System.out.println(cla4.hashCode());
        }
    }
    

    哪些类型可以有Class对象

    1. class:普通类,内部类,静态内部类,局部内部类,匿名内部类
    2. interface:接口
    3. []:数组
    4. enum:枚举
    5. annotation:注解
    6. 基本数据类型:int等
    7. void

    2)反射获取类的属性和方法

    2.1)写几个类
    abstract class Person implements Serializable {
    
        private String name;
    
        public Person() {
        }
    
        public Person(String name) {
            this.name = name;
        }
    
        public abstract void give(String some);
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    class Student extends Person{
    
        @Override
        public void give(String some) {
            System.out.println("给了学生:" + some);
        }
    
        public static void staticMothed(){
            System.out.println("静态方法");
        }
    
    }
    
    class Teacher extends Person{
    
        public String subject;
    
        public String getPost() {
            return post;
        }
    
        public void setPost(String post) {
            this.post = post;
        }
    
        private String post;
    
        public Teacher() {
        }
    
        private Teacher(String name, String subject, String post) {
            super(name);
            this.subject = subject;
            this.post = post;
        }
    
        @Override
        public void give(String some) {
            System.out.println("给了老师:" + some);
        }
    
        public void teach(String content){
            System.out.println("教学方法一");
        }
    
        private void teach(String content, Person person){
            System.out.println("教学方法二");
        }
    
    }
    
    2.2)反射使用
    import java.io.Serializable;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    
    public class SomeThingTest {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
            // 获取Teacher类的class对象
            Class teacherClass = Teacher.class;
    
            // 获取Teacher类的名字
            System.out.println(teacherClass.getName());
            System.out.println(teacherClass.getSimpleName());
    
            System.out.println("============= 分割线 ==============");
            // 获取Teacher类的属性,只能获取到public权限的
            Field[] fieldArr = teacherClass.getFields();
            for (Field field : fieldArr) {
                System.out.println(field);
            }
    
            System.out.println("============= 分割线 ==============");
            // 获取Teacher类的属性,可以获取所有权限的属性
            fieldArr = teacherClass.getDeclaredFields();
            for (Field field : fieldArr) {
                System.out.println(field);
            }
    
            System.out.println("============= 分割线 ==============");
            // 获取Teacher类的方法,只能获取到public权限的,且可以获取到继承父类的方法
            Method[] methodArr = teacherClass.getMethods();
            for (Method method : methodArr) {
                System.out.println(method);
            }
    
            System.out.println("============= 分割线 ==============");
            // 获取Teacher类的方法,可以获取所有权限的方法,获取不到继承父类的方法
            methodArr = teacherClass.getDeclaredMethods();
            for (Method method : methodArr) {
                System.out.println(method);
            }
    
            System.out.println("============= 分割线 ==============");
            // 指定获取Teacher类的方法
            System.out.println(teacherClass.getMethod("teach", String.class));
            System.out.println(teacherClass.getDeclaredMethod("teach", String.class, Person.class));
    
            System.out.println("============= 分割线 ==============");
            // 获取Teacher类的构造器,只能获取到public权限的
            Constructor[] constructorArr = teacherClass.getConstructors();
            for (Constructor constructor : constructorArr) {
                System.out.println(constructor);
            }
    
            System.out.println("============= 分割线 ==============");
            // 获取Teacher类的构造器,可以获取所有权限的构造器
            constructorArr = teacherClass.getDeclaredConstructors();
            for (Constructor constructor : constructorArr) {
                System.out.println(constructor);
            }
    
    
            System.out.println("============= 分割线 ==============");
            // 拥有无参构造器时,直接进行实例化,不推荐
            Teacher teacher = (Teacher) teacherClass.newInstance();
            // 指定获取Teacher类的构造器,并实例化对象
            Constructor constructor = teacherClass.getConstructor();
            teacher = (Teacher) constructor.newInstance();
            System.out.println(String.format("姓名:%s,职务:%s,科目:%s", teacher.getName(), teacher.getPost(), teacher.subject));
            constructor = teacherClass.getDeclaredConstructor(String.class, String.class, String.class);
            constructor.setAccessible(true);// 设置访问权限,为true时可访问私有private
            teacher = (Teacher) constructor.newInstance("半月无霜", "教导主任", "计算机");
            System.out.println(String.format("姓名:%s,职务:%s,科目:%s", teacher.getName(), teacher.getPost(), teacher.subject));
    
            System.out.println("============= 分割线 ==============");
            // 调用方法
            Method teachMethod = teacherClass.getDeclaredMethod("teach", String.class, Person.class);
            teachMethod.setAccessible(true);// 设置访问权限,为true时可访问私有private
            teachMethod.invoke(teacherClass.newInstance(), "教学", new Student());
    
        }
    }
    

    3)反射获取注解

    3.1)写两个注解和类
    import lombok.Data;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyClassInfo{
        String value();
    }
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyFieldInfo{
        String value();
        String mappingType();
    }
    
    @Data
    @MyClassInfo("用户类")
    class User{
        @MyFieldInfo(value = "主键ID", mappingType = "int")
        private Integer id;
        @MyFieldInfo(value = "用户名", mappingType = "varchar")
        private String username;
        @MyFieldInfo(value = "密码", mappingType = "varchar")
        private String password;
    }
    
    3.2)反射使用注解
    public class SomeThingTest {
        public static void main(String[] args) {
            Class<User> userClass = User.class;
            Annotation[] annotationArr = userClass.getDeclaredAnnotations();
            for (Annotation annotation : annotationArr) {
                System.out.println(annotation);
            }
            MyClassInfo myClassInfo = userClass.getAnnotation(MyClassInfo.class);
            System.out.println(myClassInfo.value());
    
            Field[] fieldArr = userClass.getDeclaredFields();
            for (Field field : fieldArr) {
                MyFieldInfo myFieldInfo = field.getDeclaredAnnotation(MyFieldInfo.class);
                System.out.println(String.format("%s[value:%s,mappingType:%s]", field.getName(), myFieldInfo.value(), myFieldInfo.mappingType()));
            }
        }
    }
    

    4)反射获取泛型

    4.1)写两个泛型的方法
    public class Person{
        
        public void setList(List<String> list){
        }
        
        public Map<String, Object> returnMap() {
            return null;
        }
        
    }
    
    4.2)反射使用泛型
    public class SomeThingTest {
        
        public static void main(String[] args) throws NoSuchMethodException {
            Method setList = Person.class.getDeclaredMethod("setList", List.class);
            Type[] genericParameterTypes = setList.getGenericParameterTypes();
            for (Type genericParameterType : genericParameterTypes) {
                System.out.println(genericParameterType);
                if (genericParameterType instanceof ParameterizedType){
                    // 获取List中的泛型
                    Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                    for (Type actualTypeArgument : actualTypeArguments) {
                        System.out.println(actualTypeArgument.getTypeName());
                    }
                }
            }
            System.out.println("============= 分割线 ==============");
            Method returnMap = Person.class.getDeclaredMethod("returnMap");
            Type type = returnMap.getGenericReturnType();
            System.out.println(type);
            if (type instanceof ParameterizedType){
                // 获取Map中的泛型
                Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument.getTypeName());
                }
            }
        }
        
    }
    

    5)在线JDK8API文档

    现在,已经知道了反射使用,去剖析框架源码时的你真帅

    附上jdk8的在线API文档,祝你前程似锦

  • 相关阅读:
    adobe acrobat 无效批注对象
    分享下今天研究的流量上限DDos攻击分析和解决方式
    【二】【HTML列表、表格与框架】
    大话计算机中的流水作业
    texinfo
    texindex
    texi2dvi
    tex, virtex, initex
    testprns printername [printcapname]
    testparm
  • 原文地址:https://www.cnblogs.com/banmoon/p/14255256.html
Copyright © 2020-2023  润新知