前言
此博客为供个人学习总结备用,如有错误请指正。
正文
Annotation(注解)
java自带的注解包含以下三个:
1.@Override 注解表示重写父类的方法
2.@Deprecated 声明被注解的方法或字段等废弃,不建议使用
3.@SuppressWarnings 抑制所有的警告
其中@Deprecated和@SuppressWarnings注解可以配置多个参数,用来表达不同的对象范围,如@SuppressWarnings({“unchecked”,“unused”})表示抑制警告。
点开SuppressWarnings 注解源代码查看:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();//表示注解定义时传递的参数
}
@SuppressWarnings 注解是使用@interface定义来定义的,而该注解又被java元注解所修饰,元注解表示用来解释注解的注解,常见@Target和@Retention使用:
Target注解表示注解能配置的范围,如:
@Target({TYPE, FIELD, METHOD})表示注解配置范围为type(类型)、field(字段)、METHOD(方法)
@Retention用于描述注解的生命周期如:@Retention(RetentionPolicy.RUNTIME)
SOURCE表示在源文件中有效
CLASS表示在CLASS文件有效
RUNTIME表示运行时有效,可以被反射机制读取
下面实现一个简单的自定义注解:
首先使用@interface定义注解:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(value={FIELD, METHOD, TYPE})//配置了注解的使用范围,字段、方法和类型
public @interface MyAnnotation {
String[] value();//只有一个值的时候一般命名为value(),且注解传值时可省略value=
int age() default -1;//不传值时age相当于不存在未定义,不强求传值
String StudentName() default "小明";//默认值小明
}
然后在类中进行使用:
import java.util.Date;
public class Demo01 {
//使用自定义注解并传值
@MyAnnotation(value = {"d","f"}, StudentName = "小兰")
private int i;
//@Override注解表示重写父类的方法
@Override
public String toString(){
return i +"";
}
//表示废弃,不建议使用
@Deprecated
public static void test(){
System.out.println("test");
}
//抑制所有的警告,参数可以配置多个,如:
//@SuppressWarnings("all")
@SuppressWarnings({"all","unused"})
public static void main(String[] args) {
Date date = new Date();
test();
}
}
可能到这里有些迷惑,自定义注解然后呢,有个毛用啊。这就要涉及到反射了,在反射中取到注解注入的信息,能够减少开发的工作量。下面再举个注解的例子:
自定义两个注解,Table和Field用来对数据库表名和字段产生对应关系。
SxtTable注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SxtTable {
String value();//用来接收对应数据库表名
}
SxtField注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SxtField {
String columnName();//对应表字段名
String type();//字段类型
int length();//长度
}
接下来定义Student类,包括学号、姓名和年龄三个属性,并生成get、set方法:
@SxtTable("tb_student")
public class SxtStudent {
public SxtStudent() {
}
public SxtStudent(int id) {
this.id = id;
}
@SxtField(columnName = "id", type = "int", length = 10)
private int id;
@SxtField(columnName = "sname", type = "nvarchar", length = 50)
private String StudentName;
@SxtField(columnName = "age", type = "int", length = 10)
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStudentName() {
return StudentName;
}
public void setStudentName(String studentName) {
StudentName = studentName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
那么现在已经将学生类通过注解对应到数据库了,由@SxtTable(“tb_student”)知对应表名tb_student,由@SxtField看到学生类的各属性分别对应表字段。
接下来就可以通过反射读取注解进行相应的操作了,那就有可能插入学生信息到表就像mybatis插入数据那么方便了,注解的价值就得以体现。
注解怎么通过反射进行获取?
反射
反射暂时也说不出个所以然,来看看大概怎么使用吧:
//加载类对象
Class clazz = Class.forName("annotation.testAnnotation.SxtStudent");
System.out.println(clazz);
//获取类的属性
Field f01 = clazz.getField("id");//只能获取到公有的属性
System.out.println(f01);
Field f02 = clazz.getDeclaredField("id");//获取所有声明的属性
System.out.println(f02);
//获取到所以属性
Field[] fields01 = clazz.getFields();//同样只能获取公开的属性
Field[] fields = clazz.getDeclaredFields();//获取所有声明的属性
for (Field temp : fields) {
System.out.println("属性:" + temp);
}
//获得类的方法,和属性类似
//第二个参数传递方法参数类型,用以区分当重写方法名字相同的情况
Method m01 = clazz.getDeclaredMethod("setAge", int.class);
//获得构造器信息
Constructor[] constructors = clazz.getDeclaredConstructors();
//获得无参构造器
Constructor constructor = clazz.getDeclaredConstructor(null);
//获取参数为int的构造器
Constructor constructor1 = clazz.getDeclaredConstructor(int.class);
System.out.println(constructor);
//调用了SxtStudent无参的构造函数,
// 注意如果类中实现了有参构造器,调用方法会有异常,一般保留无参的构造函数
SxtStudent student = clazz.newInstance();
System.out.println(student);
//动态使用构造器
Constructor<SxtStudent> c =
clazz.getDeclaredConstructor(int.class);
SxtStudent s1 = c.newInstance(3);
System.out.println(s1.getId());
//使用方法
SxtStudent s2 = clazz.newInstance();
Method m = clazz.getDeclaredMethod("setId", int.class);
m.invoke(s2, 5); //等价于s2.setId(7);
System.out.println(s2.getId());
//直接设置属性的值
SxtStudent s3 = clazz.newInstance();
Field f = clazz.getDeclaredField("id");
f.setAccessible(true);//表示不做安全性检查,可以提交速度,看情况使用
f.set(s3,9);//跳过安全性检查才能直接设置私有属性的值
System.out.println(s3.getId());
//------------------反射获取注解--------------------------
//获得类的所有有效注解
Class clazz = Class.forName("annotation.testAnnotation.SxtStudent");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation: annotations){
System.out.println(annotation);
}
//获得指定注解
SxtTable table = (SxtTable) clazz.getAnnotation(SxtTable.class);
System.out.println(table.value());
//获得类属性的注解
Field f = clazz.getDeclaredField("id");
SxtField SxtField = f.getAnnotation(SxtField.class);
System.out.println(SxtField.columnName() + "---" +
SxtField.type() + "---" + SxtField.length());
基础的使用大致就是这样,再说明一下注意点:
Class.forName(path) JVM会加载一个类的Class对象,并且同一个类只会有一个Class对象,多次加载的对象相同。
反射会影响程序性能,让程序变慢。setAccessible(true);//表示不做安全性检查,能提高反射的效率,利弊需自己衡量,在不需要安全检查的时候设置true能大大提高效率。
System.out.println("sdfj".getClass());
//"相同类型加载的类是一样的,JVM只会加载一个实例"
System.out.println("jdksll".getClass() == "sdfj".getClass());
//不同空间的Class实例不同
int[] j = {1,2,4};
int[][] k = new int[2][3];
System.out.println(j.getClass().hashCode() + "|" + k.getClass().hashCode());
java反射还能够获取到泛型,感兴趣可以自行搜索。
java基础知识之注解、反射(二)