文章目录
分享一篇 【Java注解+Java反射+Java类加载机制+javaweb 技术搭建MVC框架并用于项目】,更加深入的学习和配置使用所学知识点。
Java注解
Annotation,注解,对于Java基础不扎实的我,只会用而不知道其中的原理,今天让我好好了解一下你。
注解就如我们日常生活中看到的标签,去超时看见商品标签,去服装店看到服装标签。它用来标注类或属性。
1.注解的定义
注解通过 @interface
关键字进行定义,下面创建一个TestAnnotation注解类:
public @interface TestAnnotation {
}
@interface
就相当于接口,不过前面多了一个@
符号。而此注解,就可以用到类上了。
2.元注解
元注解是一种基本注解,用于定义注解,常在自定义注解的时候使用。它可定义注解的各种属性,元注解有5种:@Retention、@Documented、@Target、@Inherited和@Repeatable。
2.1 @Retention(保留)
Retention意为 保留,解释这个注解的保留的时间(存活的时间)。它的取值由RetentionPolicy类提供
Java代码保留(存活)的时间段分为source -> class -> runtime三个。
- RetentionPolicy.SOURCE:注解只在源码阶段保留,将被编译器丢弃。
- RetentionPolicy.CLASS:默认行为,注解只被保留到编译进行的时候,不会被加载到 JVM 中。
- RetentionPolicy.RUNTIME:注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,程序运行时可读取,可被反射读取到。
2.2 @Documented(文档)
Documented意为 文档,解释此注解中的元素在 Javadoc 中去。【点击我查看javadoc解释】
2.3 @Target(目标)
Target意为 目标,指定定义的注解起作用的场景。限定注解的使用场景、使用对象等,注解的使用变得十分明确。它的取值由ElementType类提供
- ElementType.TYPE :作用于类、接口(包括注解类型接口)或者枚举类型。
- ElementType.FIELD:作用于字段属性。
- ElementType.METHOD:作用于方法。
- ElementType.PARAMETER:作用于参数。
- ElementType.CONSTRUCTOR:作用于构造器。
- ElementType.LOCAL_VARIABLE:作用于局部变量。
- ElementType.ANNOTATION_TYPE:作用于注解。
- ElementType.PACKAGE:作用于包。
- ElementType.TYPE_PARAMETER:作用于类型参数(since jdk1.8)。
- ElementType.TYPE_USE:作用于使用的类型(since jdk1.8)。
- ElementType.MODULE:作用于模块声明(since jdk9)。
2.4 @Inherited(继承)
Inherited意为 继承,它不是指此注解本身会被继承,而是被@Inherited修饰的类的子类,在没有被任何注解修饰的话,那么这个子类就继承了父类的注解。例:
// 1.先定义一个注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
// 2.B继承A,那么B没有被任何注解修饰,那么B继承A的注解
@Test
public class A {}
public class B extends A {}
2.5 @Repeatable(可重复)
Repeatable意为 可重复,jdk1.8新特性。它表示修饰的注解可以重复被使用。例如:
一个开发人员会使用c、c#和java
// 1.定义容器注解,存储各种各样的Language类型
@Retention(RetentionPolicy.RUNTIME)
@interface Languages{
Language[] value();
}
// 2.定义Language注解
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Languages.class)
public @interface Language {
String tool() default "";
String value() default "";
}
// 3.使用自定义的注解修饰类
@Language(tool ="c")
@Language(tool ="c#")
@Language(tool ="java")
public class Developer{
// ...
}
上面的代码中,@Repeatable 注解了 Language。而 @Repeatable 后面括号中的类相当于一个 容器注解。容器注解 是用来存放其它注解的地方,它本身也是一个注解。
代码中的相关容器注解:
@interface Languages{
Language[] value();
}
按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是 数组。
3.注解的属性
注解的属性又称成员变量。
3.1 使用
一般使用
定义的成员变量,应该在使用注解的是赋值,以键值对形式赋值(这里还是以为上面的代码为例):
// 使用@Language
@Language(tool ="c",value = "master")
public class Developer{
// ...
}
多个属性需要赋值,使用逗号隔开。
单个参数传递
在只需要传递一个参数的时候,如果需要传递数据的属性名为value,在对其进行赋值的时候,可以不写value=
,直接写一个值即可,如下:
@Language(value = "master")
// 等价于
@Language("master")
无参数传递
当使用的注解,不需要传递参数的时候,就可以不写括号,如下:
@Language()
// 等价于
@Language
3.2 定义
注解当中只有成员变量,没有方法(虽然是方法的声明格式)。注解的成员变量在注解的定义中以普遍Java代码中方法的声明形式来声明,方法明定义了该成员变量的名字,返回值类型定义了该成员变量的类型。
在注解中定义属性时,它的类型必须是 8种基本类型 或 类、接口、注解及它们的数组。
8种基本类型:boolean、byte、short、char、int、long、float、double
且属性可以有默认值,使用defalut设置,若属性没有设置默认值,在使用该注解的时候,就必须传递参数。示例:
// 1.有属性的自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Languages.class)
public @interface Language {
String tool() default "";
String value() default "";
}
// 2.无属性的自定义注解
public @interface Environment {
}
注解也可以不做任何属性声明,这个时候使用该注解的是,就不用些括号。
至此,就可以自定义注解了。
4.注解的获取(反射)
使用注解修饰类或方法,就是为了指定的场景下能够达到指定的目的,但是注解毕竟只是一个标签,真正实现功能的还得借助Java的反射获取注解信息来实现。
先查看获取注解对象的方法(Class、Method、Field都有):
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
:获取指定类型的注解对象。public Annotation[] getAnnotations()
:获取全部注解,返回注解数组。
需要使用反射获取到的注解,一定的声明@Retention(RetentionPolicy.RUNTIME)
,只有保留到RUNTIME才能获取到,否则是会抹去此注解的。
4.1 获取类上注解对象
这里就以获取class类上的注解为例(还是使用上面声明的注解),Developer声明如下:
@Language(tool = "java",value = "master")
public class Developer {
}
下面就是获取并打印
Class c = Developer.class;
// 1.使用getAnnotation() 获取指定的注解
Language language = (Language) c.getAnnotation(Language.class);
System.out.println("tool:"+language.tool());
System.out.println("value:"+language.value());
// 2.使用getAnnotations() 获取全部注解
Annotation[] as = c.getAnnotations();
for(Annotation annotation:as){
if(annotation instanceof Language){
System.out.println("tool:"+language.tool());
System.out.println("value:"+language.value());
}
}
输出的结果都是:
tool:java
value:master
4.2 类、方法上注解对象
在了解了如何获取类上的注解对象之后,获取属性、方法上的注解对象就简单了,只要获取到Class、Method、Field对象之后,就 调用getAnnotation()
或 getAnnotations()
方法即可获取到对应的注解对象。
5.注解的使用需求
学习注解之后,如果没有实际应用,忘记肯定比学的快。如果我们明确了它的使用需求,再加以使用,那当然最好不过。
官方文档对注解的描述:
注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
注解有许多用处,主要如下:
- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
- 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
值得注意的是,注解不是代码本身的一部分。
简单点说就是:注解无法改变代码本身,注解只是工具。同时,开发者开发的用于处理 Annotation 的代码统称为APT(Annotation Processing Tool)。
说了这么多的概念,不好理解,也记不住,解决方法来了:【点击跳转阅读注解+反射的实际应用】。
同时,spring框架中使用大量的注解,感兴趣可以去看一下源码。
6.Java预设的注解
做一个扩展,点到为止
相信在我们日常写代码的是,有几个注解是接触很多的。
- @Deprecated:Deprecated意为 弃用、过时,Java语言在不断的迭代中,针对同一需求不断的优化解决方案,旧的解决方案就会使用@Deprecated标记过时,但是还是可以正常使用。
- @Override:重写父类方法需要使用@Override。
- @SuppressWarnings:阻止警告。
- @SafeVarargs:参数安全类型注解。
- @FunctionalInterface:函数式接口注解,这个是 Java 1.8 版本引入的新特性
函数式接口:就是接口中只有一个抽象方法,其他方法都不能是抽象的。