美文网首页java基础
java基础-day28-注解

java基础-day28-注解

作者: 触手不可及 | 来源:发表于2021-06-29 09:34 被阅读0次

    注解

    1. 注解概述

    1.1 注解解释
    注释:
        解释代码,给程序员看
    
    注解:
        Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java虚拟机可以保留标注内容,在运行时可以获取到标注内容。 当然它也支持自定义Java标注
        JDK1.5之后的特征
        用于说明程序
        一般在框架中使用
        格式:
            @AnnotationName
    
    文档注释:
        @param @return @Exeception 从根本上是一个注释,不存在代码编译,不会生成对应的.class字节码问题,只是提供给JavaDoc API文件生成工具。作为标记生成对应的文档。
        
    注解是有一部分参与编译
        @Override并不是没编译就有效果了,是因为不管是Eclipse还是IDEA都可以预编译Java代码生成对应的.class文件的
    
    1.2 注解作用
    生成文档:
        代码中生成对应的JavaDoc API文档
        @param @return 
        
        【IDEA JavaDoc工具使用参数】
            Other  Command Line Arguments : -encoding utf-8 -charset utf-8
            解决中文乱码,因为IDEA默认编码集为UTF-8 Windows GKB
    
    代码检查:
        继承重写,或者说接口遵从之后的实现中,存在@Override
        
    代码数据获取: [小框架]
        通过反射获取指定注解中的一些内容,例如 配置,数据,操作,验证。。。
    
    1.3 Java中预定义的一些注解
    @Override:
        重写/实现方法的情况下,检查方法声明是否和父类或者接口中的方法声明一致。强制格式检查。
        
    @Deprecated
        标注当前方法已过时,例如 Data日期类内的一些方法
        
    @SuppressWarnings("all")
        压制警告,可以用于一些代码中存在明确无异常的情况下,压制一些警告
    

    2. Java中自定义注解

    2.1 Java中自定义注解的方式
    格式:
        public @interface AnnotationName {
            属性列表;
        }
        
    Annotation注解是可以编译得到对应的.class字节码文件,验证了注解是可以参与编译过程的
    
    通过反编译工具可以得到一下内容
    【Annotation本质】
    public interface MyAnnotation1 extends java.lang.annotation.Annotation {
    }
    
    MyAnnotation1
        本质是一个interface,同时java.lang.annotation.Annotation 子接口
    
    package com.qfedu.a_annotation.MyAnnotation;
    
    /**
     * 自定义注解!!!
     * public interface MyAnnotation1 extends java.lang.annotation.Annotation {
     * }
     *
     * @author Anonymous
     * @date 2020/3/10 11:01
     */
    public @interface MyAnnotation1 {
        // 属性 ==> 方法形式
    }
    
    
    2.2 Annotation注解属性【难点】
    属性:
        开发书写代码使用注解的方式中,数据使用方式更加偏向于属性概念。
            使用 
                1. 在书写代码中使用
                    @MyAnnotation(id=1, name="骚磊", age=16)
                2. 利用反射时,会涉及到getXXX方法
                    通过属性名获取对应值的概念来完成的
                    
        【但是实际上是利用abstract方法来完成属性概念的】
    
    属性使用的格式[实际按照方法格式操作]
        1. 属性的值数据类型和对应具体数据 ==> 返回值类型和返回的数据
            属性类型支持:
                a. 基本数据类型
                b. String类型
                c. 其他的注解类型
                d. 枚举类型 
                    枚举就是一个带有名字的常量,为了更好的域阅读性和操作
                e. 以上类型对相应的数组
            
            属性值要求
                a. 定义属性时可以使用default关键字,加上默认值,该属性在使用的过程中是
                没有强制要求属性值,如果没有赋予属性值,采用对应的默认值操作,如果赋
                值,使用对应值
                
                b. 如果注解中有且只有一个value属性,或者说注解中除value属性之外,都有
                默认值,不管是类,方法,成员变量,包使用当前注解是可以直接在括号内加入
                对应数据类型数值、
                
                c. 如果属性是数组类型, {}大括号保存,并且不同的内容,使用,隔开
        2. 属性的键名字 ==> 方法的名字
    
    2.3 元注解
    给予注解的解释,用于约束注解的一些操作问题
    @Retention - 
        标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
        RetentionPolicy.RUNTIME:当前注解会编译生成对应的.class字节码文件,并且可以加
        载到JVM中,参与代码执行
        RetentionPolicy.CLASS: 
        
    别纠结,记下就好:
        RetentionPolicy.SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)
        @Override
        对应属性RetentionPolicy.SOURCE
        在代码编译过程中,检查方法格式是否正确,不参与代码运行和解析。
        
    @Documented 
        标记这些注解是否包含在用户文档中。
        是否可以通过JavaDoc工具,生成对应的API文档
        
    @Target 
        标记这个注解应该是哪种 Java 成员。
        属性:
            ElementType
                TYPE: 当前注解可以用于类声明
                METHOD: 当前注解可以用于方法声明位置
                FIELD:当前注解可以用于成员变量声明位置
    @Inherited 
        标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
        
    【重点】
        @Target目标
            可以作用范围 类,方法,成员变量...
        @Retention
            RetentionPolicy.RUNTIME 常用
    
    image.png
    2.4 使用反射获取注解中的内容【用途】
    driverClass=com.mysql.jdbc.Driver
    user=root
    password=123456
    url=jdbc:mysql://localhost:3306/db_name
    # 配置文件保存到properties文件中,使用文件中内容读取配置获取属性
    # properties Properties System.getProperties();
    # Properties ==> driverClass ==> com.mysql.jdbc.Driver ==> 完整的包名.类名
    # Class.forName 
    
    package com.qfedu.a_annotation.reflect;
    
    import com.qfedu.a_annotation.Person;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.util.Properties;
    
    /**
     * 解析properties文件读取内容,使用反射创建对象,赋值成员变量
     *
     * @author Anonymous
     */
    public class ReflectFile {
        public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
            Properties properties = new Properties();
            properties.load(new FileInputStream("./src/1.properties"));
    
            String className = properties.getProperty("className");
            String id = properties.getProperty("id");
            String name = properties.getProperty("name");
            System.out.println(className);
            Class<?> aClass = Class.forName(className);
    
            Person person = (Person) aClass.getConstructor().newInstance();
    
            System.out.println(person);
    
            Field declaredField = aClass.getDeclaredField("id");
            declaredField.setAccessible(true);
            declaredField.set(person, Integer.parseInt(id));
    
            Field declaredField2 = aClass.getDeclaredField("name");
            declaredField2.setAccessible(true);
            declaredField2.set(person, name);
    
            System.out.println(person);
        }
    }
    
    package com.qfedu.a_annotation.reflect;
    
    import com.qfedu.a_annotation.MyAnnotation.MyAnnotation5;
    
    import java.io.IOException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    /**
     * 通过注解方式,获取注解的属性,创建对象,读取数据
     * 通常用于代码配置
     *      Servlet Listener Filter
     * @author Anonymous
     */
    @MyAnnotation5(className = "com.qfedu.a_annotation.Person",
            id = 2,
            name = "骚磊")
    public class ReflectAnnotatiaon {
        public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
            // 加载ReflectAnnotatiaon
            Class<ReflectAnnotatiaon> cls = ReflectAnnotatiaon.class;
    
            // 因为注解是在类名之上,通过Class获取对应的Annotation
            MyAnnotation5 annotation = cls.getAnnotation(MyAnnotation5.class);
            /*
            类似于注解的实现类
            class XXX implements MyAnnotation5 {
                public String className() {
                    return "com.qfedu.a_annotation.Person";
                }
                public int id() {
                    return 2;
                }
                public String name() {
                    return "骚磊";
                }
            }
             */
            // 感觉是在执行对应的方法,获取注解的属性
            String s = annotation.className();
            int id = annotation.id();
            String name = annotation.name();
    
            System.out.println(s);
            System.out.println(id);
            System.out.println(name);
    
            Class<?> aClass = Class.forName(s);
    
            Constructor<?> constructor = aClass.getConstructor(Integer.class, String.class);
    
            Object o = constructor.newInstance(id, name);
            System.out.println(o);
        }
    }
    
    2.5 使用注解测试代码运行【用途】
    package com.qfedu.a_annotation.checkMethod;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 没有任何的属性,只是作为是否需要测试的标记
     *
     * @author Anonymous
     */
    // 注解有且只能用于方法上
    @Target(ElementType.METHOD)
    // 参与编译和运行
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Check {
    }
    
    package com.qfedu.a_annotation.checkMethod;
    
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * 测试Tools类内的方法,如果方法带有Check注解,执行测试,记录异常
     *
     * @author Anonymous
     */
    @ClassAnnotation(className = "com.qfedu.a_annotation.checkMethod.Utils")
    public class TestProject {
        public static void main(String[] args)
                throws IOException, InvocationTargetException,
                IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InstantiationException {
    
            // 从注解中获取对应的属性
            Class<TestProject> cls  = TestProject.class;
            ClassAnnotation annotation = cls.getAnnotation(ClassAnnotation.class);
            String s = annotation.className();
            Class<?> aClass = Class.forName(s);
    
            Object tools = aClass.getConstructor().newInstance();
            // 获取所有Tools类内的方法,不包括父类方法
            Method[] declaredMethods = aClass.getDeclaredMethods();
    
            // 出现错误的次数
            int count = 0;
            long l = System.currentTimeMillis();
            BufferedWriter br = new BufferedWriter(new FileWriter("./src/log.txt"));
    
            // 遍历方法数组
            for (Method declaredMethod : declaredMethods) {
                declaredMethod.setAccessible(true);
    
                // 判断当前方法是否带有注解@Check,标记
                if (declaredMethod.isAnnotationPresent(Check.class)) {
                    try {
                        // 执行方法
                        declaredMethod.invoke(tools);
                    } catch (Exception e) {
                        count += 1;
    
                        // 1. 哪一个方法出现异常
                        br.write("方法:" + declaredMethod.getName());
                        br.newLine();
                        // 2. 发生什么异常 获取原因,获取对应的类型
                        br.write("异常类型:" + e.getCause().getClass().getSimpleName());
                        br.newLine();
                        // 3. 异常信息
                        br.write("异常信息:" + e.getCause().getMessage());
                        br.newLine();
                        br.write("-----------------------------------------------------------------");
                        br.newLine();
                    }
                }
            }
    
            long l1 = System.currentTimeMillis();
            br.write("出现错误的次数:" + count);
            br.newLine();
            br.write("总耗时:" + (l1 - l));
            br.newLine();
    
            br.close();
        }
    }
    

    3. 注解使用总结

    1. 注解以后大多数情况下,都是使用过程,而不是自定义,会使用到框架中预处理好的注解。
    2. 注解是给谁用的?
        a. 编译器
        b. 解析代码使用
        c. JVM运行代码使用
    3. 注解是一个标签,有时候是做标记的,有时候标签是有属性的。
    

    相关文章

      网友评论

        本文标题:java基础-day28-注解

        本文链接:https://www.haomeiwen.com/subject/sehqultx.html