注解
概念
注解可以理解为注释的一种:
我们常说的注释就是在代码中的一些解释,纯粹的为了增强代码的可读性。编译器不会编译这些注释。
注解在程序中是有一定的意义,有一些注解会一致持续到程序运行都是有效的。
JDK已经提供的注解:
1.@Override 标记一个方法是重写的父类的方法
2.@Deprecated 标记一个方法是过时方法,不建议使用的方法
3.@SuppressWarnings 消除警告的注解
4.@SafeVarargs 消除对污染警告
在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java编译器会报unchecked警告。鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其varargs参数执行潜在的不安全的操作,可使用@SafeVarargs进行标记,这样的话,Java编译器就不会报unchecked警告。
5.@FunctionalInterface 函数式接口
注解都是@Xxxxxxx。
注解可以注解类,局部变量,成员变量,方法,参数
注解可以在源码阶段,编译阶段,运行阶段生效
自定义注解
了解几个JKD的注解(源注解):
@Target
@Target使用来注解其他的注解的,主要是表示其他的注解可以使用的位置(目标)。
@Retention
自己写一个自定义注解:
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午9:44:58
*/
//@Target //St这个注解可以注解的位置(类,接口,枚举,注解,方法,构造方法,变量,参数等等)
//@Retention //表示St这个注解的存活范围(源码阶段,编译阶段,运行阶段)
public @interface St {
}
使用:
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午9:49:27
*/
@St
public class Test {
@St
public Test() {}
@St
private String name;
@St
public static void main(@St String[] args) {
@St
int age;
}
}
@Target注解
@Target标记一个注解的可注解位置。
有几个值:
TYPE:java中的四大类型(类,枚举,接口,注解)
FIELD:成员变量
METHOD:成员方法
PARAMETER:方法的参数
CONSTRUCTOR:构造方法
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE
如果我们自定义的注解不适用@Target注解,那么我们的注解可以写在任何为止。
案例:
除过成员变量之外的其他位置的注解全部报错:
可以在Target中同时多指定几个位置:
@Retention注解
标注一个注解的存活范围:
SOURCE:源码阶段,
CLASS:编译阶段,
RUNTIME:运行阶段
自定义注解的属性
注解中是可以写属性的,属性的写法和类的写法略有不同。
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午10:12:28
*/
public @interface StValue {
//注解的自定义的属性(没有setget方法)
String name();
int age();
String tel();
}
注解的属性都是public修饰的。
注解中属性的写法 类型 属性名();没有setter和getter方法。
注解中属性的赋值:
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午10:14:53
*/
@StValue(name="卡卡西",age=28,tel="13813813838")
public class Demo2 {
}
如果注解中申明一个属性,并且这个属性没有默认值,则使用注解时必须给这个属性赋值。
如果有多个属性需要赋值,可以使用“,”隔开进行赋值。赋值的方式和java中的一致。
注解中的属性也可以有默认值:
public @interface StValue {
//注解的自定义的属性(没有setget方法)
String name();
int age() default 18; //有默认值的属性
String tel() default "110";//有默认值的属性
}
如果属性有默认值,那么在使用这个注解的时候可以选择性的赋值。
//这里的age就使用了默认值
@StValue(name="佐助",tel="120")
private String name;
注解中的属性也可以是数组
public @interface StValue {
//注解的自定义的属性(没有setget方法)
String name();
int age() default 18; //有默认值的属性
String tel() default "110";//有默认值的属性
String [] email();//数组属性
}
给数组的属性赋值,可以使用“{}”。 如果只有一个值就可以省略“{}”
//给数组形式的属性赋值(赋多个值)
@StValue(name="年龄",email={"abc@12.com","def@345.com"})
private int age;
//给数组属性赋值(只有一个值)
@StValue(name="电话",email="st@st.com")
private String tel;
特殊的value属性:value属性是注解中的一个非常特殊的属性,可以是任何类型,可有默认值或者没有默认值。
public @interface StNames {
//value属性
String value();
}
使用注解,并且给value属性赋值,不需要写value=""
@StNames("demo3")
public class Demo3 {
}
这种情况只限于只有一个value属性赋值的时候使用。
如果出现了多个属性需要赋值,则需要写value=""
public @interface StNames {
//value属性
String value();
int age();//其他需要赋值的属性
}
使用:
@StNames(value="demo3",age=18)
public class Demo3 {
}
tips:注解的属性类型可以是任何java类型。
使用:
使用反射机制获取注解
Class类中有一些可以获取一个类的注解的API:
A getAnnotation(Class<A> annotationClass)
如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
Annotation[] getAnnotations()
返回此元素上存在的所有注释(包含从其他地方继承的注解)。
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注释。
除过Class类之外其他的几个反射相关的类都有getAnnotation和getDeclaredAnnotations方法。
Field,Method,Constructor等。
案例:
tips:如果希望通过反射获取到注解,则这个的存活范围必须是RUNTIME。
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午10:42:32
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface DefValue {
String value();
int age();
}
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午10:42:50
*/
public class Test {
public static void main(String[] args) {
//通过Class获取类上方的注解
Class clz = Person.class;
Annotation annotation = clz.getAnnotation(DefValue.class);
System.out.println(annotation);
}
}
@DefValue(value="PERSON",age=45)
class Person{}
通过getAnnotation方法获取注解,参数是注解的本身的class对象,返回值(没有泛型)是Annotation接口。默认所有的注解都继承自Annotation接口。
获取注解的属性值:
其他的几个获取注解的API
//获取类上方的所有注解
clz = Student.class;
//获取自己申明的注解以及继承的注解。 当然继承的注解必须有@Inherited源注解
Annotation[] ans = clz.getAnnotations();
for (int i = 0; i < ans.length; i++) {
System.out.println(ans[i]);
}
//获取自己申明的注解
System.out.println("----------");
ans = clz.getDeclaredAnnotations();
for (int i = 0; i < ans.length; i++) {
System.out.println(ans[i]);
}
案例
我们可以在配置文件中给对象的属性赋初始值。使用注解和放射实现。
[1]准备一个配置文件config.txt
内容如下:
brand=路基亚
cost=500000
[2]在程序启动的时候将文件中的内容读取到缓存中,准备一个Data类
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午11:12:04
*/
public class Data {
public static Map<String,String> datas = new HashMap<String,String>();
static {
FileReader reader = null;
BufferedReader bufReader = null;
try {
reader = new FileReader("config.txt");
bufReader = new BufferedReader(reader);
String line = null;
while((line = bufReader.readLine())!=null) {
String[] infos = line.split("=");
datas.put(infos[0], infos[1]);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(reader!=null)
reader.close();
if(bufReader!=null)
bufReader.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
[3]准备一个注解 @Value
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午11:16:06
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
//要读取的配置文件中的属性的名字
String value();
}
[4]自定义一个类,使用@Value配置值
[5]在Car中添加一个非静态代码块,在非静态代码块中初始化这些属性值
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午11:09:26
*/
public class Car {
@Value("brand")//将配置文件中的name为brand的值赋值给这个属性
private String brand;
@Value("cost")//将配置文件中的cost的值赋值给cost
private int cost;
private String addr;
//非静态代码块,初始化属性值
{
//获取当前类的Class
Class clz = this.getClass();
//获取所有的域
Field[] fields = clz.getDeclaredFields();
//遍历赋值
for (Field field : fields) {
//设置这个域为可见
field.setAccessible(true);
//获取域上方的value注解
Value annotation = field.getDeclaredAnnotation(Value.class);
if(annotation!=null) {//有@Value注解
//取出@Value中的属性值
String key = annotation.value();
//通过key从map中取出值
String value = Data.datas.get(key);
//获取属性的类型
Class typeClz = field.getType();
try {
if(typeClz==int.class) {
//将取出的value赋值给当前的属性Field
field.set(this, Integer.parseInt(value));
}else if(typeClz == String.class) {
field.set(this, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public String toString() {
return "Car [brand=" + brand + ", cost=" + cost + "]";
}
//setter和getter省略
}
测试:
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午11:25:36
*/
public class Test {
public static void main(String[] args) {
Car car = new Car();
System.out.println(car);
}
}
{
field.set(this, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public String toString() {
return "Car [brand=" + brand + ", cost=" + cost + "]";
}
//setter和getter省略
}
测试:
```java
/**
* @author 戴着假发的程序员
* @TODO
* @organization 飞虎队
* 2020年9月2日 上午11:25:36
*/
public class Test {
public static void main(String[] args) {
Car car = new Car();
System.out.println(car);
}
}