1 概念
注解:annotation
1.8以后的新特性
注解:程序元素的元数据
解释:程序元素:程序的组成:包+类+接口+方法+属性+形参+局部变量...
元数据:给成员元素添加的数据
理解:给程序的元素添加的"注释",此注释的数据可以通过反射获取 指挥编译器的编译和干涉虚拟机的运行
根接口:java.lang.annotation.Annotaition
关键字 @interface
2 常见的注解
2.1 @Override
@Override//注解:重写:表示此方法是重写父类的方法::指挥编译器的编译
public String toString() {
return super.toString();
}
2.2 @Deprecated
@Deprecated //注解:过时::表示此方法是过时 有更好的替代者::指挥编译器的编译
public void hehe(){}
@Deprecated
int a=1;
2.3 @SuppressWarnings
//@SuppressWarnings({"unused","static-access","deprecation"})//取消指定的警告
@SuppressWarnings({"all"})//注解:取消编译警告::取消编译器对当前方法的指定/所有警告::指挥编译器的编译
public void hai(){
int a=1;//The value of the local variable a is not used
Date date=new Date(2013, 01, 1);//The constructor Date(int, int, int) is deprecated
this.b=1;//The static field Demo01.b should be accessed in a static way
List list=null;// List is a raw type. References to generic type List<E> should be parameterized
try{
return;
}finally{//finally block does not complete normally
return;
}
}
static int b=1;
2.4 @Test
//单元测试: 1 引入junit的jar包
// 2 在方法上添加注解:::干涉虚拟机的运行
// @Test
// public void hh1(){
// System.out.println("public void hh1()"+1/1+":::"+this);
// }
// //java.lang.Exception: Method hh should have no parameters
// @Test
// public void hh2(int a){
// System.out.println("public void hh2(a)"+1/1+":::"+this);
// }
//
//java.lang.Exception: Method hh3() should be void
// @Test
// public int hh3(){
// System.out.println("public int hh3()"+1/1+":::"+this);
// return 1;
// }
// java.lang.Exception: Method hh4() should not be static
// @Test
// public static void hh4()throws Exception{
// System.out.println("public static void hh4()throws Exception"+1/1+":::");
// }
@Test
public final void hh5()throws Exception{
System.out.println("public final void hh5()throws Exception"+1/1+":::");
}
// java.lang.Exception: Method hh6() should be public
// @Test
// protected final void hh6()throws Exception{
// System.out.println("protected final void hh6()throws Exception"+1/1+":::");
// }
//@Test单元测试的方法要求:不能有返回值 不能有参数列表 不能是静态的 必须是public 可以是final的
3 自定义注解
@My1Anniotation(name="韩梅梅",age=11)//使用注解 并给属性赋值
public class Demo02 {
public static void main(String[] args) {
}
@My2Anniotation(value="v_1")
public void test11(){}
@My2Anniotation("v_1")//如果一个注解的属性 只有value属性需要赋值时 value=可以省略
public void test21(){}
}
//创建一个注解 可以加载类上
@interface My1Anniotation{
//注解中定义属性
String name();//定义一个属性:属性类型是String 属性名字是name
//属性类型仅限于:String,Class,基本数据类型,枚举,注解,这些类型的一维数组
//only primitive type, String, Class, annotation, enumeration are permitted or 1-dimensional arrays thereof
int age() default 1;//给属性赋默认值
}
//创建一个注解 可以加载类上
@interface My2Anniotation{
String value();//定义一个属性:
}
4 注解解析
4.1 自定义注解
@interface MyClassAnnotation{
String name();
}
@interface MyFieldAnnotation{
String name();
}
@interface MyMethodAnnotation{
String value();
Class type();
}
4.2 创建实体类
@MyClassAnnotation(name="学生")
public class Student {
@MyFieldAnnotation(name="学号")
private int id;
@MyFieldAnnotation(name="名字")
private String name;
@MyFieldAnnotation(name="分数")
private float score;
@MyFieldAnnotation(name="性别")
private char sex;
public int getId() {
return id;
}
@MyMethodAnnotation(value="1001",type=int.class)
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
@MyMethodAnnotation(value="韩梅梅",type=String.class)
public void setName(String name) {
this.name = name;
}
public float getScore() {
return score;
}
@MyMethodAnnotation(value="99.5f",type=float.class)
public void setScore(float score) {
this.score = score;
}
public char getSex() {
return sex;
}
@MyMethodAnnotation(value="女",type=char.class)
public void setSex(char sex) {
this.sex = sex;
}
public Student(int id, String name, float score, char sex) {
super();
this.id = id;
this.name = name;
this.score = score;
this.sex = sex;
}
public Student() {
//如果调用无参数的构造方法:通过反射获取set方法省的注解 并执行set方法给属性赋值
}
@Override
public String toString() {
//return "Student [id=" + id + ", name=" + name + ", score=" + score + ", sex=" + sex + "]";
//通过反射 动态获取类元素上的注解的信息 拼凑字符串::重写toString
return "学生 [学号=" + id + ", 名字=" + name + ", 分数=" + score + ", 性别=" + sex + "]";
}
}
4.3 目标
1: 重写toString方法
@Override
public String toString() {
//return "Student [id=" + id + ", name=" + name + ", score=" + score + ", sex=" + sex + "]";
//通过反射 动态获取类元素上的注解的信息 拼凑字符串::重写toString
return "学生 [学号=" + id + ", 名字=" + name + ", 分数=" + score + ", 性别=" + sex + "]";
}
2 实现无参数的构造方法
public Student() {
//如果调用无参数的构造方法:通过反射获取set方法省的注解 并执行set方法给属性赋值
}
4.4 元注解
注解:用来修饰类元素
元注解:用来修饰注解的注解
修饰:给被修饰者添加一些数据/一些特性
元注解1:@Retention
//@Retention //注定注解保留范围 属性value取值:RetentionPolicy.CLASS(默认) RUNTIME SOURCE
// RetentionPolicy.SOURCE:只保留在源文件中:编译器不编译
// RetentionPolicy.CLASS:编译器编译到字节码文件中 但虚拟机不解析 反射获取不到
// RetentionPolicy.RUNTIME:编译器编译到字节码文件中 虚拟机解析 反射可以获取
元注解2:@Target
//@Target //指示注释类型所适用的程序元素的种类:取值:
// ElementType.ANNOTATION_TYPE ::只能作用于注解类上:::针对于元注解
// ElementType.CONSTRUCTOR ::只能作用于构造方法
// ElementType.FIELD ::只能作用于成员变量
// ElementType.METHOD ::只能作用于方法
// ElementType.LOCAL_VARIABLE ::只能作用于局部变量
// ElementType.PACKAGE ::只能作用于包
// ElementType.PARAMETER ::只能作用于参数列表
// ElementType.TYPE ::只能作用于类,接口,枚举
4.5 对自定义的注解进行改进:添加元注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyClassAnnotation{
String name();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyFieldAnnotation{
String name();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyMethodAnnotation{
String value();
Class type();
}
4.6 重写toString方法
//return "学生 [学号=" + id + ", 名字=" + name + ", 分数=" + score + ", 性别=" + sex + "]";
public static String parseAnnotationFoToString(Object obj){
//1 获取字节码文件对象
Class cla=obj.getClass();
StringBuffer stb=new StringBuffer();
//2 获取类上的注解
if(!cla.isAnnotationPresent(MyClassAnnotation.class)){
throw new RuntimeException("类上没有注解!MyClassAnnotation");
}
MyClassAnnotation classAnno=(MyClassAnnotation)cla.getAnnotation(MyClassAnnotation.class);
//获取name属性的值
String classAnnoName=classAnno.name();
stb.append(classAnnoName+"[");
//3 获取所有属性上的注解
Field[] fields=cla.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);//暴力访问
Object fieldValue;
try {
fieldValue=field.get(obj);//获取属性的值
} catch (Exception e) {
throw new RuntimeException(e);
}
//获取属性上的注解
if(!field.isAnnotationPresent(MyFieldAnnotation.class)){
throw new RuntimeException("属性("+field.getName()+")上没有注解!MyFieldAnnotation");
}
MyFieldAnnotation fieldAnno=field.getAnnotation(MyFieldAnnotation.class);
String fieldAnnoName=fieldAnno.name();
stb.append(fieldAnnoName+"="+fieldValue+",");
}
//更改最后一个,为]
stb.setCharAt(stb.length()-1, ']');
return stb.toString();
}
- 重写toString方法
@Override
public String toString() {
//return "Student [id=" + id + ", name=" + name + ", score=" + score + ", sex=" + sex + "]";
//通过反射 动态获取类元素上的注解的信息 拼凑字符串::重写toString
//return "学生 [学号=" + id + ", 名字=" + name + ", 分数=" + score + ", 性别=" + sex + "]";
return Demo03.parseAnnotationFoToString(this);
}
- 测试
4.7 实现无参数的构造方法
public static void parseAnnotationFoConstructor(Object obj){
//1 获取字节码文件对象
Class cla=obj.getClass();
//2 获取所有的方法
Method[] methods=cla.getDeclaredMethods();
//3 获取所有的setXxx开头的方法
for (Method method : methods) {
if(method.getName().startsWith("set")){
//获取其上的注解MyMethodAnnotation
if(!method.isAnnotationPresent(MyMethodAnnotation.class)){
throw new RuntimeException("方法("+method.getName()+")上没有注解!MyMethodAnnotation");
}
MyMethodAnnotation methodAnno=method.getAnnotation(MyMethodAnnotation.class);
//获取注解的两个属性 value和type
String value=methodAnno.value();
Class type=methodAnno.type();
try {
Object valueObj=changeType(value, type);//把字符串转化为指定的类型
method.invoke(obj, valueObj);//执行当前方法
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
//写一个方法:把参数字符串的值 转化为指定的类型
private static Object changeType(String value,Class type)throws Exception{
if(type==byte.class || type==Byte.class){
return Byte.parseByte(value);
}
if(type==short.class || type==Short.class){
return Short.parseShort(value);
}
if(type==int.class || type==Integer.class){
return Integer.parseInt(value);
}
if(type==char.class || type==Character.class){
return value.charAt(0);
}
if(type==long.class || type==Long.class){
return Long.parseLong(value);
}
if(type==float.class || type==Float.class){
return Float.parseFloat(value);
}
if(type==double.class || type==Double.class){
return Double.parseDouble(value);
}
if(type==boolean.class || type==Boolean.class){
return Boolean.parseBoolean(value);
}
if(type==Date.class){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
}
return value;
}
- 实现无参数的构造方法
public Student() {
//如果调用无参数的构造方法:通过反射获取set方法省的注解 并执行set方法给属性赋值
Demo03.parseAnnotationFoConstructor(this);
}
- 测试