注解的使用
jdk5.0开始出现注解。Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
使用"name=value"保存信息
框架 = 注解 + 反射 + 设计模式
常见注解
文档中的注解
@author 标明开发该类模块的作者,多个作者之间使用,分割
@version 标明该类模块的版本
@see 参考转向,也就是相关主题
@since 从哪个版本开始增加的
@param 对方法中某参数的说明,如果没有参数就不能写
@return 对方法返回值的说明,如果方法的返回值类型是void就不能写
@exception 对方法可能抛出的异常进行说明 ,如果方法没有用throws显式抛出的异常就不能写
其中
@param @return 和 @exception 这三个标记都是只用于方法的。
@param的格式要求:@param 形参名 形参类型 形参说明
@return 的格式要求:@return 返回值类型 返回值说明
@exception的格式要求:@exception 异常类型 异常说明
@param和@exception可以并列多个
编译时的注解
@Override: 限定重写父类方法, 该注解只能用于方法
@Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
@SuppressWarnings: 抑制编译器警告
自定义注解
- 定义新的 Annotation 类型使用 @interface 关键字
- 自定义注解自动继承了java.lang.annotation.Annotation接口
- Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称为配置参数。类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组。
- 可以在定义 Annotation 的成员变量时为其指定初始值, 指定成员变量的初始值可使用 default 关键字
- 如果只有一个参数成员,建议使用参数名为value
- 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是“参数名 = 参数值”,如果只有一个参数成员,且名称为value,可以省略“value=”
- 没有成员定义的 Annotation 称为标记(例@Override); 包含成员变量的Annotation 称为元数据 Annotation
jdk中四个元注解
- Retention
- Target
- Documented
- Inherited
Retention
@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 的生命周期,@Rentention 包含一个 RetentionPolicy 类型的成员变量, 使用@Rentention 时必须为该 value 成员变量指定值:
- RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的
注释 - RetentionPolicy.CLASS:在class文件中有效(即class保留) , 当运行 Java 程序时, JVM
不会保留注解。 这是默认值 - RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行 Java 程序时, JVM 会
保留注释。程序可以通过反射获取该注释。
package java.lang.annotation;
/**
* Indicates how long annotations with the annotated type are to
* be retained. If no Retention annotation is present on
* an annotation type declaration, the retention policy defaults to
* {@code RetentionPolicy.CLASS}.
*
* <p>A Retention meta-annotation has effect only if the
* meta-annotated type is used directly for annotation. It has no
* effect if the meta-annotated type is used as a member type in
* another annotation type.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.2 @Retention
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
// 生命周期的枚举类
package java.lang.annotation;
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
Target
@Target: 用于修饰 Annotation 定义, 用于指定被修饰的 Annotation 能用于修饰哪些程序元素。
@Target 也包含一个名为 value 的成员变量。
可修饰结构的枚举类
package java.lang.annotation;
/**
*/
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
Documented
- @Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被javadoc 工具提取成文档。默认情况下,javadoc是不包括注解的。
- 定义为Documented的注解必须设置Retention值为RUNTIME。
Inherited
@Inherited: 被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation, 则其子类将自动具有该注解。
- 比如:如果把标有@Inherited注解的自定义的注解标注在类级别上,子类则可以
继承父类类级别的注解 - 实际应用中,使用较少
jdk8注解中新特性
- 可重复的注解
- 用于类型的注解
可重复的注解
jdk8之前:新建一个注解,成员变量为要注解的数组,使用新建的注解内多个value对。
jdk8:原注解中加@Repeatable(新注解.class),这样在使用原注解的时候可以注释多个相同的注解(而不是使用多个value)
用于类型的注解
TYPE_PARAMETER:标识该注解可以写在类型变量的声明语句中(如泛型中)
public class TestTypeDefine<@TypeDefine() U> {
private U u;
public <@TypeDefine() T> void test(T t){
} }
@Target({ElementType.TYPE_PARAMETER})
@interface TypeDefine{ }
TYPE_USE:可以修饰在使用类型的任何语句中
@MyAnnotation
public class AnnotationTest<U> {
@MyAnnotation
private String name;
public static void main(String[] args) {
AnnotationTest<@MyAnnotation String> t = null;
int a = (@MyAnnotation int) 2L;
@MyAnnotation
int b = 10;
}
public static <@MyAnnotation T> void method(T t) { }
public static void test(@MyAnnotation String arg)throws @MyAnnotation Exception { } }
@Target(ElementType.TYPE_USE)
@interface MyAnnotation { }
准备的Person类
package com.example.annotationAndReflex;
import org.jetbrains.annotations.NotNull;
/**
* @author :yhc
* @date :2021/8/29
**/
@MyAnnotation(value = "hi")
public class Person extends Creature<String>
implements Comparable<String>,MyInterface{
private String name;
int age;
public int id;
public Person() {
}
@MyAnnotation(value = "abc")
public Person(String name){
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@MyAnnotation(value = "hello")
private String show(String nation){
System.out.println("我的国籍是" + nation);
return nation;
}
public String display(String interests) throws NullPointerException{
return interests;
}
public void show(){
System.out.println("我是example的show方法");
}
private String showPrivate(String word){
System.out.println("传进来的word是" + word);
return word;
}
@Override
public String toString() {
return "Example{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(@NotNull String o) {
return 0;
}
@Override
public void info() {
System.out.println("我是一个人");
}
}
反射的使用
反射:可以实现在运行时可以知道任意一个类的属性和方法。
反射的功能
- 运行时判断一个对象所属的类
- 运行时构造任意一个类的对象
- 运行时判断任意一个类所具有的成员变量和方法
- 运行时获取泛型信息
- 运行时调用任意一个对象的成员变量和方法
- 运行时处理注解
- 生成动态代理
反射相关的API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- java.lang.reflect.Modifier:代表修饰符
通过类获取Class对象来新建对象
package com.example.annotationAndReflex;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author :yhc
* @date :2021/8/29
**/
public class ReflexTest {
@Test
public void reflexTest1() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
// 通过反射创建对象
Class pClass = Person.class;
Constructor constructor = pClass.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("实例name", 15);
System.out.println(obj);
Person p = (Person) obj;
System.out.println(p);
System.out.println("---------------------------");
// 通过反射调用对象指定属性、方法
// 调用属性
Field age = pClass.getDeclaredField("age");
age.set(p,10);
System.out.println(p);
// 调用方法(空参)
Method show = pClass.getDeclaredMethod("show");
show.invoke(p);
}
@Test
public void reflexTest2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
// 通过反射获取私有的构造器、方法、属性。
Class pClass = Person.class;
Constructor pCon = pClass.getDeclaredConstructor(String.class);
pCon.setAccessible(true);
Person p = (Person) pCon.newInstance("Tom");
System.out.println(p);
// 调用私有属性
Field name = pClass.getDeclaredField("name");
name.setAccessible(true);
name.set(p,"Jerry");
System.out.println(p);
// 调用私有的方法
Method show = pClass.getDeclaredMethod("showPrivate",String.class);
show.setAccessible(true);
String s = (String) show.invoke(p,"!!word!!");
System.out.println(s);
}
}
Class实例对应着一个运行时类
获取Class对象的4中方式
// 四种获取Class实例的方式
@Test
public void reflexTest3() throws ClassNotFoundException {
//方式一: 调用运行时类的属性
Class<Person> personClass = Person.class;
//方式二: 通过运行时类的对象
Person person = new Person();
Class personClass2 = person.getClass();
//方式三: 通过Class的静态方法: forName(String classPath) 类的全类名
Class personClass3 = Class.forName("com.example.annotationAndReflex.Person");
//方式四: 使用类的加载器 ClassLoader
ClassLoader classLoader = ReflexTest.class.getClassLoader();
Class personClass4 = classLoader.loadClass("com.example.annotationAndReflex.Person");
System.out.println(personClass == personClass2);
}
Class可以是哪些结构
- Class c1 = Object.class;
- Class c2 = Comparable.class;
- Class c3 = String[].class;
- Class c4 = int[][].class;
- Class c5 = ElementType.class;
- Class c6 = Override.class;
- Class c7 = int.class;
- Class c8 = void.class;
- Class c9 = Class.class;
ClassLoader
//认识ClassLoader
@Test
public void reflexTest4(){
ClassLoader classLoader = ReflexTest.class.getClassLoader();
System.out.println(classLoader);
//sun.misc.Launcher$AppClassLoader@dad5dc 系统类加载器
System.out.println(classLoader.getParent());
// sun.misc.Launcher$ExtClassLoader@d46ca6 扩展类加载器
// getParent()无法获取Bootstrap ClassLoader 引导类加载器(加载核心类库)
ClassLoader bootstrapClassLoader = String.class.getClassLoader();
System.out.println(bootstrapClassLoader); // null 无法获取
}
- 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
- 扩展类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
- 系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载器
// 使用Properties读取文件
@Test
public void reflexTest5() throws IOException {
Properties pros = new Properties();
FileInputStream fis = new FileInputStream("src/test/java/com/example/annotationAndReflex/jdbc.properties");
// pros.load(fis);
// String user = pros.getProperty("user");
// String password = pros.getProperty("password");
// System.out.println("账号: " + user + "\n密码: " + password);
ClassLoader classLoader = ReflexTest.class.getClassLoader();
// 获取输入流 默认相对路径从src下
InputStream is = classLoader.getResourceAsStream("com/example/annotationAndReflex/jdbc.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("账号: " + user + "\n密码: " + password);
}
@Test
public void reflexTest6() throws InstantiationException, IllegalAccessException {
Class personClass = Person.class;
Object o = (Person)personClass.newInstance(); // 默认调用非private空参构造器(无则报异常)
System.out.println(o);
}
动态决定创建对象(反射的动态性)
/**
* 创建一个指定类的对象
* @param classPath
* @return
* @throws ClassNotFoundException
* @throws InstantiationException
* @throws IllegalAccessException
*/
@Test
public Object reflexCreateObj(String classPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class objClass = Class.forName(classPath);
return objClass.newInstance();
}
// 创建对应运行时的对象
@Test
public void reflexTest7() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
int num = new Random().nextInt(3);
String classPath = "";
switch (num){
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.lang.Object";
break;
case 2:
classPath = "com.example.annotationAndReflex.Person";
break;
}
System.out.println(num);
Object o = reflexCreateObj(classPath);
System.out.println(o);
}
获取运行时类Person的完整结构
// 获取运行时类Person的完整结构
// 获取当前类的属性
@Test
public void reflexTest8(){
Class personClass = Person.class;
// 获取属性结构(只能拿到public的) 父类的也可以拿到
Field[] fields = personClass.getFields();
for (Field f : fields){
System.out.println(f);
}
System.out.println("-----------------------");
// 获取声明过的属性(获取所有权限的属性) 父类属性无法拿到
Field[] declaredFields = personClass.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f);
}
}
//获取当前类的 权限修饰符 数据类型 变量名
@Test
public void reflexTestFiled(){
Class personClass = Person.class;
Field[] declaredFields = personClass.getDeclaredFields();
for (Field f: declaredFields) {
// 1.权限修饰符
// 默认 - 0
// public - 1
// private - 2
int modifiers = f.getModifiers();
System.out.print(modifiers + "\t");
System.out.print(Modifier.toString(modifiers) + "\t");
// 2.变量的数据类型
Class type = f.getType();
System.out.print(type + "\t");
// 3.变量名
String name = f.getName();
System.out.print(name + "\t");
System.out.println();
}
}
//运行时方法测试
@Test
public void reflexTestMethod(){
Class<Person> personClass = Person.class;
// getMethods() 获取当前运行时类及其父类里的public方法
Method[] methods = personClass.getMethods();
for (Method m: methods) {
System.out.println(m);
}
System.out.println("--------------------------------------------");
// 获取当前运行时类所有(非父类)方法
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method m: declaredMethods) {
System.out.println(m);
}
}
//方法: 权限修饰符 返回值类型 形参列表
@Test
public void reflexTestMethodDetail(){
Class<Person> personClass = Person.class;
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method m : declaredMethods) {
// 1.获取方法声明的注解信息
Annotation[] annos = m.getAnnotations();
for (Annotation a: annos) {
System.out.print(a + "\t");
}
// 2.权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
// 3.返回值类型
System.out.print(m.getReturnType().getName() + "\t");
// 4.方法名
System.out.print(m.getName() + "\t");
// 5.形参列表
System.out.print("----(");
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null && parameterTypes.length == 0)){
for (int i = 0; i < parameterTypes.length; i++) {
System.out.print(parameterTypes[i].getName() + " args_ " + i);
}
}
System.out.print(")");
// 6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(!(exceptionTypes == null && exceptionTypes.length == 0)){
for (int i = 0; i < exceptionTypes.length; i++) {
System.out.print(exceptionTypes[i] + ",");
}
}
System.out.println();
}
}
// 获取构造器结构
@Test
public void reflexTestConDetail(){
Class<Person> personClass = Person.class;
// 获取当前类的public构造器
Constructor[] constructors = personClass.getConstructors();
for (Constructor c: constructors) {
System.out.println(c);
}
System.out.println("----------------------------");
// 获取当前类所有构造器
Constructor[] declaredConstructors = personClass.getDeclaredConstructors();
for (Constructor c: declaredConstructors) {
System.out.println(c);
}
}
// 获取运行时的父类
@Test
public void reflexTestParent(){
Class<Person> personClass = Person.class;
// 不带泛型
Class<? super Person> superclass = personClass.getSuperclass();
System.out.println(superclass);
// 带泛型
Type genericSuperclass = personClass.getGenericSuperclass();
System.out.println(genericSuperclass);
//输出泛型
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
System.out.println(actualTypeArguments[0]);
}
// 获取运行时的接口
@Test
public void reflexTestInterface(){
Class<Person> personClass = Person.class;
// 可能为多实现
Class[] interfaces = personClass.getInterfaces();
for (Class c: interfaces){
System.out.println(c);
}
System.out.println("-----------------------");
// 获取父类的接口
Class[] superInterfaces = personClass.getSuperclass().getInterfaces();
for (Class c: superInterfaces) {
System.out.println(c);
}
}
// 获取运行时类的包
@Test
public void packageTest(){
Class<Person> personClass = Person.class;
Package aPackage = personClass.getPackage();
System.out.println(aPackage);
}
// 获取运行时类的注解
@Test
public void getAnnotation(){
Class<Person> personClass = Person.class;
Annotation[] annotations = personClass.getAnnotations();
for (Annotation annotation: annotations) {
System.out.println(annotation);
}
}
调用运行时类的指定结构(属性、方法)
// 调用运行时类的指定结构
// 获取、设置相应的属性
@Test
public void fieldTest() throws NoSuchFieldException, InstantiationException, IllegalAccessException {
Class<Person> personClass = Person.class;
// 获取对应对象
Person person = personClass.newInstance();
// 获取指定的属性:要求属性为public(通常不采用)
Field id = personClass.getField("id");
/**
* 获取所有权限的属性
*/
Field name = personClass.getDeclaredField("name");
// 开启访问
name.setAccessible(true);
/**
* set()
* 参数一: 指明要设置的对象
* 参数二: 指明要设置的数值
*/
id.set(person,1001);
name.set(person,"tom");
/**
* get(): 要获取的对象
*/
int pid = (int) id.get(person);
System.out.println(pid);
System.out.println(name.get(person));
}
// 操作运行时类指定的方法
@Test
public void methodTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class<Person> personClass = Person.class;
// 创建对象
Person person = personClass.newInstance();
/**
* 获取指定方法
* 参数一: 方法名
* 参数二: 方法的参数
*/
Method showMethod = personClass.getDeclaredMethod("show",String.class);
// 开启访问权限
showMethod.setAccessible(true);
/**
* 传入对象调用方法
* invoke()
* 参数一:需要调用方法的对象
* 参数二:传入调用方法的参数
* 返回值 == 调用方法的返回值
*
* 调用静态方法传入 Person.class 作为参数一
*/
String nation = (String) showMethod.invoke(person, "CHN");
System.out.println(nation);
}
// 调用运行时类中指定的构造器
@Test
public void getConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Person> personClass = Person.class;
/**
* 指明参数列表
*/
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
// 确保可访问
constructor.setAccessible(true);
Person person = constructor.newInstance("Tom", 11);
System.out.println(person);
}