目录
一、注解
注解:位于源代码中,使用其他工作进行处理的标签;注解用来修饰程序元素,但不会对修饰的对象有直接的影响;只有通过某种配套的工作才会对注解信息进行访问和处理。
千万不要把注解和注释弄混淆!!!
注解的主要用途:
- 提供信息给编译器和IDE工具
- 可用于其他工具来产生额外的代码/配置文件等
- 有一些注解可在程序运行时访问,增加程序的动态性
二、自带注解:
自带注解举例:
- @SuppressWarning():压制警告
- @Override():重写方法标记,若该方法父类里面没有则会报错。
- @Deprecated:表示废弃的,建议不再使用所修饰的变量/方法
- @SafeVarargs:表示不会对不定项参数做危险的操作
- @FunctionInterface:声明功能性接口
补充:all:忽略所有警告k;cast:忽略类转型警告;serial:忽略实现Serializable接口的,没有定义serialVersionUID
三、自定义注解
jdk1.5引入,注解定义扩展java.lang.annotation.Annotation注解接口
注解中可以包含的类型有:
——String/Class/enum类型/注解类型/由这些类型组成的数组。
自定义注解定义:
@interface修饰,并且变量名后需要加(),可设默认值。
public @interface BugReport {
enum Status {UNCONFIRMED, CONFIRMED, FIXED, NOTABUG};
boolean showStopper() default true;
String assiganedTo() default "[none]";
Status status() default Status.UNCONFIRMED;
String[] reportedBy();
}
注解的使用:格式为:@注解名(注解内变量赋值)如@test(a=1,b=2);
用元注解修饰自定义注解:
注解应用示例:
1.注解定义
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
//表示该注解会保留在class文件中
@Target(ElementType.METHOD)
//表示该注解只能用于方法
public @interface MultipleTest {
int a() default 0;
int b() default 0;
}
2.注解标记
public class Foo {
@MultipleTest(a=1,b=1)
public static void m1(int a, int b) {
if(a+b<0)
{
throw new RuntimeException("Crash");
}
}
@MultipleTest
public static void m2(int a, int b) {
//全部采用默认值
if(a+b<0)
{
throw new RuntimeException("Broken");
}
}
@MultipleTest(b=-2,a=1)
public static void m3(int a, int b) {
if(a+b<0)
{
throw new RuntimeException("Boom");
}
}
}
3.利用反射来判断注解
package annotations.multiple;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
public class Main {
public static void main(String[] args) throws Exception {
int passed = 0, failed = 0;
String className = "annotations.multiple.Foo";
for (Method m : Class.forName(className).getMethods()) {//获取所有方法
if (m.isAnnotationPresent(MultipleTest.class)) {//判断该方法是否存在注解
System.out.println(m.getName());
MultipleTest st = m.getAnnotation(MultipleTest.class); //获取注解
try {
m.invoke(null,st.a(),st.b());
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
输出:
m3
Test public static void annotations.multiple.Foo.m3(int,int) failed: java.lang.RuntimeException: Boom
m2
m1
Passed: 2, Failed 1
四、元注解
元注解举例:
- @Target:设置目标范围
- @Retention:设置保持性
- @Documented:文档
- @Inherited:注解继承
- @Repeatable:此注解可以重复修饰。
说明:元注解是用来修饰注解的注解,一般修饰自定很诡异注解。
@Target:
4
constructor构造函数/field成员变量/local_varible本地变量/method方法/package包/parameter参数
可以应用于多例测试场合。
示例:
1.定义注解:
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(RepeatableAnnotations.class)//绑定容器注解
public @interface RepeatableAnnotation {
int a() default 0;
int b() default 0;
int c() default 0;
}
2.定义容器注解:
在里面定义原始注解的数组就行
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatableAnnotations {
RepeatableAnnotation[] value();
}
3.重复注解:
public class Student {
@RepeatableAnnotation(a=1,b=2,c=3)
@RepeatableAnnotation(a=1,b=2,c=4)
public static void add(int a, int b, int c)
{
if(c != a+b)
{
throw new ArithmeticException("Wrong");
}
}
}
4.获取注解并且测试:
package repeatable;
import java.lang.reflect.Method;
@CommonAnnotation
@RepeatableAnnotation
@RepeatableAnnotation
public class Main {
public static void main(String[] a) throws Exception
{
String className = "repeatable.Student";
for (Method m : Class.forName(className).getMethods())
{
if (m.isAnnotationPresent(RepeatableAnnotations.class))
{
RepeatableAnnotation[] annos = m.getAnnotationsByType(RepeatableAnnotation.class);
for (RepeatableAnnotation anno : annos)
{
System.out.println(anno.a() + "," + anno.b() + "," + anno.c());
try
{
m.invoke(null,anno.a(),anno.b(),anno.c());
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
}
}
}
}
}
}
输出:
1,2,3
1,2,4
Test public static void repeatable.Student.add(int,int,int) failed: java.lang.ArithmeticException: Wrong
五、注解的解析
- Class.getAnnotations():类的有哪些注解修饰
- Class.isAnnotation():类是否有注解修饰
- Class.isAnnotationPresent(Class BClass):注解Bclass是否在此类上
- Method.getAnnotations():方法的有哪些注解修饰
- Method.isAnnotation():方法是否有注解修饰
- Method.isAnnotationPresent(Class BClass):注解Bclass是否在此方法上
- Field.getAnnotations():变量的有哪些注解修饰
- Field.isAnnotation():变量是否有注解修饰
- Field.isAnnotationPresent(Class BClass):注解Bclass是否在此变量上
- Constructor.getAnnotations():构造方法的有哪些注解修饰
- Constructor.isAnnotation():构造方法是否有注解修饰
- Constructor.isAnnotationPresent(Class BClass):注解Bclass是否在此构造方法上
用javac -processor命令来重新生成。注解处理器怎么写还不懂后续添加。
六、Runtime注解实现本质
注解经过反编译会生成一个接口类,接口类中的方法就是注解中的变量。
调用路线,注解的本质实现是通过调用路线:
- 采用接口中的方法来表示变量,注解。
- java为注解产生一个代理类。这个代理类包括一个AnnotationInvocationHandler变量。
- AnnotationInvocationHandler有一个Map的成员变量,用来存储所有的注解的属性赋值。
解释:在程序中,调用注解接口的方法,将会被代理类接管,然后根据方法名字,到Map里拿到相应的value返回。
七、注解的应用
第三方库,Lombok
加载此第三方库后,在对应得类中使用注解@Data和@AllArgsConstructor可以不写构造函数和setter/getter方法
依赖方法,在maven项目pox.xml中加上依赖.
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
备注:此方法最好不要使用,我试了之后失败了,还是老老实实写构造函数和setter/getter方法吧
参考中国大学mooc《java核心技术》