Java基础知识之注解、元注解

目录
  • 注解
  • 注解作用
  • Java预定义的注解
  • 自定义注解
  • 元注解
  • 实例:
  • 注解使用总结
  • 总结

注解

Java注解也称Java标注,是jdk1.5(5.0)后的新特征。Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java注解可以通过反射获取标注内容,在编译器生成类文件时,标注可以被嵌入到字节码中,Java虚拟机可以保留标注内容,在运行时可以获取到标注内容,当然它也支持自定义Java标注

功能:用于说明程序

用途:一般用在框架中使用

格式:@AnnotationName

文档注释:

​ @param @return @Exception从根本上是一个注释,不存在代码编译,不会生成对应的文档

注解:

​ @Override并不是没有编译就有效果,是因为不管是Eclipse还是IDEA都可以预编译Java代码生成对应的.class文件的

注解作用

生成文档

代码中生成对应的JavaDoc API文档

@param @return

【IDEA JavaDoc工具使用参数】

​ Other Command Line Arguments:-encoding utf-8 -charset utf-8

​ 解决中文乱码,因为IDEA默认编码集为UTF-8 windows默认编码集为 GBK

代码检查

继承重写,或者说接口遵从之后的实现中,存在@Override

代码数据获取:【小型框架】

通过反射获取指定注解中的一些内容,例如:配置,数据,操作,验证等

Java预定义的注解

@Override

​ 重写/实现方法的情况下,检查方法声明是否和父类或者接口中的方法声明一致,强制格式检查

@Deprecated

标记当前方法已过时

@SuppressWarnings(“all”)

压制警告,可以用于一些代码中存在明确无异常的情况下,压制一些警告

Annotation注解属性【难点】

属性

​ 开发实际使用注解的方式中,数据使用方式更加偏向于属性概念

​ 使用

  1. 书写代码中使用
    @MyAnnotation(id=1, name=“ocean”, age=16)
  2. 使用反射时,会涉及到getXXX方法
    通过属性名获取对应值的概念来完成的

实际上是利用abstract方法来完成属性概念的

属性使用的格式【实际按照方法格式操作】

  1. 属性的值数据类型和对应的具体数据 => 返回值类型和返回的数据属性类型支持:

    1. 基本数据类型
    2. String类型
    3. 其他数据类型
    4. enmu枚举类型,一个带有名字的常量,为了更好的阅读性和操作
    5. 以上类型对应的数组
  2. 属性值要求
    1. 定义属性时可以使用default关键字,加上默认值,该属性在使用的过程中是没有强制要求属性值,如果没有赋予属性值,采用对应的默认值操作,如果赋值,使用对应值
    2. 如果注解中有且只有一个value属性,或者说注解中除value属性之外,都有默认值,不管是类,方法,成员变量,包使用当前注解是可以直接在括号内加入
      对应数据类型数值
      1. 如果属性是数组类型, {}大括号保存,并且不同的内容,使用,隔开属性的键名字 ==> 方法的名字

自定义注解

格式:
	public @interface AnnotationName {
		属性列表;
	}

Annotation注解是可以编译得到对应的.class字节码文件,验证了注解是可以参与编译过程的

通过反编译工具可以得到一下内容
【Annotation本质】
public interface MyAnnotation1 extends java.lang.annotation.Annotation {
}

MyAnnotation1
	本质是一个interface,同时java.lang.annotation.Annotation 子接口
package cn.ocean888.a_annotation.MyAnnotation;

/**
 * 自定义注解!!!
 * public interface MyAnnotation1 extends java.lang.annotation.Annotation {
 * }
 *
 * @author Anonymous
 * @date 2020/3/10 11:01
 */
public @interface MyAnnotation1 {
    // 属性 ==> 方法形式
}

元注解

基于注解的解释,用来约束注解的的一些操作问题

@Retention

表示这个注解的保存方式,是只在代码中,还是编入class文件中,或者是运行时可以通过反射访问

RetentionPolicy.RUNTIME:当前注解会编译生成对应的.class字节码文件,并且可以加载到JVM中,参与代码执行

RetentionPolicy.CLASS

RetentionPolicy.SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

@Document

标记这些注解是否包含在用户文档中

是否可以通过JavaDoc工具,生成对应的API文档

@Target

标记这个注解应该是那种Java成员

属性:ElementType

​ TYPE:当前注解可以用于类声明

​ METHOD:当前注解可以用于方法声明位置

​ FIELD:当前注解可以用于成员变量声明位置

@Inherited

标记这个注解是继承于那个注解类(默认 注解不继承于任何子类)

获取类上的注解

Java获取类上的注解有下面3个方法:

  • Class.getAnnotations() 获取所有的注解,包括自己声明的以及继承的
  • Class.getAnnotation(Class< A > annotationClass) 获取指定的注解,该注解可以是自己声明的,也可以是继承的
  • Class.getDeclaredAnnotations() 获取自己声明的注解、

java.lang.Class类的isAnnotation()方法用于检查此Class是否为Annotation类型

java.lang.Class类的isAnnotationPresent()方法用于检查此类中是否存在指定注释类型的注释

实例:

实例1:两种属性文件的加载的方式

1.properties

className = cn.ocean888.a_annotation.Person
id = 1
name = "ocean"

Person类

package cn.ocean888.a_annotation;

public class Person {
    Integer id;
    String name;

    public Person() {
    }

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

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

第一种使用反射来完成

package cn.ocean888.a_annotation;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, 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 cn.ocean888.a_annotation;

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 MyAnnotaion1 {
    // 属性
    String className();
    int id();
    String name();
}

ReflectAnnotation.java

package cn.ocean888.a_annotation;

import java.lang.annotation.Annotation;

@MyAnnotaion1(className = "cn.ocean888.a_annotation.Person",
    id = 2,
    name = "ocean")
public class ReflectAnnotation {
    public static void main(String[] args) {
        // 加载ReflectAnnotation
        Class<ReflectAnnotation> cls = ReflectAnnotation.class;

        // 因为注解再类名之上,通过Class获取对应的Annotation
        MyAnnotaion1 annotation = cls.getAnnotation(MyAnnotaion1.class);

        String s = annotation.className();
        int id = annotation.id();
        String name = annotation.name();

        System.out.println(s);
        System.out.println(id);
        System.out.println(name);
    }
}

实例2使用注解测试代码运行

对Tools方法中带有@Check注解标记的方法进行检查

Tools.java

package cn.ocean888.a_annotation_checkMethod;

import java.util.ArrayList;

/**
 * 需要测试的方法
 */
public class Tools {
    @Check
    public void test1() {
        String str = null;
        System.out.println(str.toString());
    }

    @Check
    public void test2() {
        int[] arr = null;
        System.out.println(arr[5]);
    }

    @Check
    public void test3() {
        int[] arr = {1,2,3,4,5};
        System.out.println(arr[3]);
    }

    @Check
    public void test4() {
        ArrayList<Integer> integers = new ArrayList<>();
        System.out.println(integers.get(20).toString());
    }

    @Check
    public void test5() {
        throw new NullPointerException("NullPointException");
    }
}

Utils.java

package cn.ocean888.a_annotation_checkMethod;

public class Utils {
    @Check
    public void test() {
        throw new IndexOutOfBoundsException("下标越界");
    }
}

Check.java

package cn.ocean888.a_annotation_checkMethod;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 该注解没有任何属性,只是作为是否需要测试的标记
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}

ClassAnnotation.java

package cn.ocean888.a_annotation_checkMethod;

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

/**
 * 自定义注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassAnnotation {
    String className();
}

TestProject.java

package cn.ocean888.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注解,执行测试并记录异常
 */
@ClassAnnotation(className = "cn.ocean888.a_annotation_checkMethod.Tools")
public class TestProject {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {

        // 从注解中获取对应的属性
        Class<TestProject> testProjectClass = TestProject.class;
        // 获取所有指定的注解
        ClassAnnotation annotation = testProjectClass.getAnnotation(ClassAnnotation.class);
        String s = annotation.className();
        // s = cn.ocean888.a_annotation_checkMethod.Utils

        Class<?> aClass = Class.forName(s);

        Object tools = aClass.getConstructor().newInstance();
        // 获取所有Tools类内的方法,不包括父类方法
        Method[] declaredMethods = aClass.getDeclaredMethods();

        // 记录错误出现次数
        int count = 0;
        long l = System.currentTimeMillis();
        BufferedWriter bufferedWriter = 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.哪一个方法出现异常
                    bufferedWriter.write("方法:" + declaredMethod.getName());
                    bufferedWriter.newLine();
                    // 2.发生异常原因,获取对应的类型
                    bufferedWriter.write("异常类型:" + e.getCause().getClass().getSimpleName());
                    bufferedWriter.newLine();
                    // 3.异常信息
                    bufferedWriter.write("异常信息:" + e.getCause().getMessage());

                    bufferedWriter.newLine();

                }
            }
        }

        long l1 = System.currentTimeMillis();
        bufferedWriter.write("出现错误的次数" + count);
        bufferedWriter.newLine();
        bufferedWriter.write("总耗时" + (l1 - l));

        bufferedWriter.close();
    }
}

代码的灵活性在于可以对className直接进行替换

注解使用总结

  • 注解在大多数情况下,都是使用过程,而不是自定义,会使用到框架中预处理好的注解
  • 注解使用对象
    • 编译器
    • 解析代码
    • JVM运行代码使用
  • 注解是一个标签,有时候是做标记,有时候标记有属性

注解和python装饰器的区别

先说java的注解(Annotation),实际上是给语法元素打一个标记。比如你可以给一个函数打一个标记,给一个类打一个标记等等。Java只保证记录这个标记,但是不会主动根据这给标记做任何事。

比如,你在Spring里,给一个私有成员打 @Autowired 这个标记。

public class XXXService {

@Autowired
private XXXXRepository xxxxRepository;

// ...
}

如果你不用Spring框架的话,不会有任何事情发生,直接访问这个字段就是空。当如果你配置了合适的处理流程,而这个流程就会根据有没有这个标记干活。比如你要求Spring “Auto Scan” 并且注入依赖,这个处理过程会用反射去读哪些元素被做了某个特定标记。没有标记就不理,有标记就注入。

python里的decorator是一个语法糖,是希望把“decorator”这个形式写得更漂亮。比如,你想记录一个函数开始执行之前和之后的log:

def foo():
print("Hello")

def logit(fn):
def inner():
print("before execute")
fn()
printf("after execute")
return inner

这时,你可以魔改以下foo的实现,用logit这个“装饰器”来部分修改foo的行为,然后执行:

foo = logit(foo)
foo()

但python里的语法可以让这个东西写成:

@logit
def foo():
print("Hello")

foo()

也就是说,python这里的装饰器是一个有逻辑的,可以执行的函数,只不过其写法有些特殊要求;而Java里面的Annotation只是个标记,需要其他代码来“根据标记执行“。

当然,装饰器模式是个很通用的东西,无论是python,java还是其他语言都可以写。只是python提供了特殊的语法糖而已。但java世界里做类似decorator的事情,希望动态魔改一个函数的行为,可以用动态代理或者AOP。

Java的Annotation因为相当于多加了一层(标记 + 处理逻辑),是一把双刃剑。好处是,在不动代码的情况下你可以通过外部配置来修改程序的行为。比如给一个函数打上@Test标。如果通过UT框架运行,这些打标的函数会被当作是测试用例;但如果外部直接用普通的main启动,这些@Test就会没有一样,不会影响代码本身的逻辑。但反过来,也容易引来一些问题。比如有的时候,你很难知道那个根据标记执行的逻辑是不是真的跑了。也许你哪里配置拼错一个字,或者classpath少依赖一个包,就造成那个逻辑并没有真的执行。这时从表面上也许很难看出来出错了。

总结

到此这篇关于Java基础知识之注解、元注解的文章就介绍到这了,更多相关Java注解、元注解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java元注解@Inherited的使用详解

    1.先看源码文档 /** * Indicates that an annotation type is automatically inherited. If * an Inherited meta-annotation is present on an annotation type * declaration, and the user queries the annotation type on a class * declaration, and the class declaratio

  • Java annotation元注解原理实例解析

    元注解是指注解的注解.包括 @Retention @Target @Document @Inherited四种. 1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口. 2. 参数成员只能用public或默认(default)这两个访问权修饰 3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型

  • Java基础知识之注解、元注解

    目录 注解 注解作用 Java预定义的注解 自定义注解 元注解 实例: 注解使用总结 总结 注解 Java注解也称Java标注,是jdk1.5(5.0)后的新特征.Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java注解可以通过反射获取标注内容,在编译器生成类文件时,标注可以被嵌入到字节码中,Java虚拟机可以保留标注内容,在运行时可以获取到标注内容,当然它也支持自定义Java标注 功能:用于说明程序 用途:一般用在框架中使用 格式:@AnnotationNam

  • Java基础之自动装箱,注解操作示例

    本文实例讲述了Java基础之自动装箱,注解操作.分享给大家供大家参考,具体如下: 示例代码: 手动装箱,手动拆箱 Integer iOb=new Integer(100);//手动装箱 int i=iOb.intValue(); //手动拆箱 System.out.println(i+" "+iOb); 自动装箱,自动拆箱 Integer iOb=1000; int i=iOb; System.out.println(i+" "+iOb); 静态导入:可以直接通过静

  • Java基础知识总结之继承

    一.继承的基本概念 什么是继承呢? 我们可以想一下,既然是"继承",那么它必须是在两个或多个类之间所发生的关系吧,这样我们就可以说:这个类继承自什么类,假如只有一个类的话,它既无法继承别的类,而且也没有别的类来继承它,这就构不成什么继承关系了哈,理清楚了这一层关系之后我们再来谈谈它继承了什么呢? 既然说什么什么类继承自什么什么类,那它肯定要从继承的那个类中继承点什么呀!我们把发生继承关系的这两个类称为父类和子类,子类可以继承父类的属性和方法,这个就是继承的基本概念,下面我们来系统的学习

  • Java基础知识汇总

    Java基础知识 1.Java语言的优点: 1)Java是纯面向对象语言 2)与平台无关性,一次编译到处运行 3)Java提供了狠多内置类库 4)提供了对web应用的支持 5)具有较好的安全性(数组边界检测.Bytecode检测)和健壮性(强制型机制.垃圾回收器.异常处理) 6)去除c++难以理解的一些特性(头文件 指针 运算符重载 多重继承) 2.java与c++的异同: 1)Java为解释型语言,c++为编译型语言,java会慢但是跨平台 2)Jave为纯面向对象,c++既面向对象又能面向过

  • java 基础知识之IO总结

    java  基础知识之IO总结     我计划在接下来的几篇文章中快速回顾一下Java,主要是一些基础的JDK相关的内容. 工作后,使用的技术随着项目的变化而变化,时而C#,时而Java,当然还有其他一些零碎的技术.总体而言,C#的使用时间要更长一些,其次是Java.我本身对语言没有什么倾向性,能干活的语言,就是好语言.而且从面向对象的角度来看,我觉得C#和Java对我来说,没什么区别. 这篇文章主要回顾Java中和I/O操作相关的内容,I/O也是编程语言的一个基础特性,Java中的I/O分为两

  • java 基础知识之网络通信(TCP通信、UDP通信、多播以及NIO)总结

    java 基础知识之网路通信总结 在这篇文章里,我们主要讨论如何使用Java实现网络通信,包括TCP通信.UDP通信.多播以及NIO. TCP连接 TCP的基础是Socket,在TCP连接中,我们会使用ServerSocket和Socket,当客户端和服务器建立连接以后,剩下的基本就是对I/O的控制了. 我们先来看一个简单的TCP通信,它分为客户端和服务器端. 客户端代码如下: 简单的TCP客户端 import java.net.*; import java.io.*; public class

  • Java基础知识杂文

    1.基本概念 IO是主存和外部设备(硬盘.终端和网络等)拷贝数据的过程.IO是操作系统的底层功能实现,底层通过I/O指令进行完成. 所有语言运行时系统提供执行I/O较高级别的工具.(c的printfscanf,java的面向对象封装) 2.Java标准io回顾 Java标准IO类库是io面向对象的一种抽象.基于本地方法的底层实现,我们无须关注底层实现.InputStream\OutputStream(字节流):一次传送一个字节.Reader\Writer(字符流):一次一个字符. 3.nio简介

  • java基础知识 super和this使用解析

    这篇文章主要介绍了java基础知识 super和this使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 super关键字的用法有三种: 1.在子类的成员方法中,访问父类的成员变量 2.在子类的成员方法中,访问父类的成员方法 3.在子类的构造方法中,访问父类的构造方法 this和super区别 super关键字用来访问父类内容,this关键字用来访问本类内容. 1.在本类的成员方法中,访问本类的成员变量 2.在本类的成员方法中,访问本类的

  • 详解Java基础知识——JDBC

    JDBC Java DataBase Connectivity,java数据库连接,为了降低操作数据的难度,java提供jdbc,按照java面向对象特点,对操作进行了很多封装. JDBC提供了很多接口,然后不同数据库厂商去实现这个接口,到底底层如何去实现,不同的数据库不一样,不同的数据库厂商需要提供接口实现类(驱动类.驱动程序 Driver.驱动) 我们连接不同的数据库,我们只需要使用不同的驱动即可. J:Java:提供访问数据库的规范(接口), DBC:接口的实现,厂商去实现这个接口. JD

  • 浅谈Java基础知识之BigDecimal

    一.基本使用 使用示例: // 初始化 BigDecimal bd1=new BigDecimal("456"); BigDecimal bd2=new BigDecimal("123"); // 加 BigDecimal add=bd1.add(bd2); // 减 BigDecimal subtract=bd1.subtract(bd2); // 乘 BigDecimal multiply=bd1.multiply(bd2); // 除 BigDecimal d

随机推荐