如果定义了参数,可通过default关键字声明参数的默认值,若不指定默认值,使用时就一定要显示赋值,而且不允许使用null值,一般会使用空字符串或者0。
如果只有一个参数,一般参数名为value,因为使用注解时,赋值可以不显示写出参数名,直接写参数值。
import java.lang.annotation.*; /** * @Description 自定义注解 * @Author Mr.nobody * @Date 2021/3/30 * @Version 1.0 */ @Target(ElementType.METHOD) // 此注解只能用在方法上。 @Retention(RetentionPolicy.RUNTIME) // 此注解保存在运行时期,可以通过反射访问。 @Inherited // 说明子类可以继承此类的此注解。 @Documented // 此注解包含在用户文档中。 public @interface CustomAnnotation { String value(); // 使用时需要显示赋值 int id() default 0; // 有默认值,使用时可以不赋值 } /** * @Description 测试注解 * @Author Mr.nobody * @Date 2021/3/30 * @Version 1.0 */ public class TestAnnotation { // @CustomAnnotation(value = "test") 只能注解在方法上,这里会报错 private String str = "Hello World!"; @CustomAnnotation(value = "test") public static void main(String[] args) { System.out.println(str); } } Java8 注解在这里讲解下Java8之后的几个注解和新特性,其中一个注解是@FunctionalInterface,它作用在接口上,标识是一个函数式接口,即只有有一个抽象方法,但是可以有默认方法。
@FunctionalInterface public interface Callback<P,R> { public R call(P param); }还有一个注解是@Repeatable,它允许在同一个位置使用多个相同的注解,而在Java8之前是不允许的。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Repeatable(OperTypes.class) public @interface OperType { String[] value(); } // 可以理解@OperTypes注解作为接收同一个类型上重复@OperType注解的容器 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface OperTypes { OperType[] value(); } @OperType("add") @OperType("update") public class MyClass { }注意,对于重复注解,不能再通过clz.getAnnotation(Class<A> annotationClass)方法来获取重复注解,Java8之后,提供了新的方法来获取重复注解,即clz.getAnnotationsByType(Class<A> annotationClass)方法。
package com.nobody; import java.lang.annotation.Annotation; /** * @Description * @Author Mr.nobody * @Date 2021/3/31 * @Version 1.0 */ @OperType("add") @OperType("update") public class MyClass { public static void main(String[] args) { Class<MyClass> clz = MyClass.class; Annotation[] annotations = clz.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation.toString()); } OperType operType = clz.getAnnotation(OperType.class); System.out.println(operType); OperType[] operTypes = clz.getAnnotationsByType(OperType.class); for (OperType type : operTypes) { System.out.println(type.toString()); } } } // 输出结果为 @com.nobody.OperTypes(value=[@com.nobody.OperType(value=[add]), @com.nobody.OperType(value=[update])]) null @com.nobody.OperType(value=[add]) @com.nobody.OperType(value=[update])在Java8中,ElementType枚举新增了两个枚举成员,分别为TYPE_PARAMETER和TYPE_USE,TYPE_PARAMETER标识注解可以作用于类型参数,TYPE_USE标识注解可以作用于标注任意类型(除了Class)。
Java反射机制我们先了解下什么是静态语言和动态语言。动态语言是指在运行时可以改变其自身结构的语言。例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或者结构上的一些变化。简单说即是在运行时代码可以根据某些条件改变自身结构。动态语言主要有C#,Object-C,JavaScript,PHP,Python等。静态语言是指运行时结构不可改变的语言,例如Java,C,C++等。
Java不是动态语言,但是它可以称为准动态语言,因为Java可以利用反射机制获得类似动态语言的特性,Java的动态性让它在编程时更加灵活。
反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法等。类在被加载完之后,会在堆内存的方法区中生成一个Class类型的对象,一个类只有一个Class对象,这个对象包含了类的结构信息。我们可以通过这个对象看到类的结构。
比如我们可以通过Class clz = Class.forName("java.lang.String");获得String类的Class对象。我们知道每个类都隐式继承Object类,Object类有个getClass()方法也能获取Class对象。
Java反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时获取注解
生成动态代理
...
Java反射机制的优缺点
优点:实现动态创建对象和编译,有更加的灵活性。
缺点:对性能有影响。使用反射其实是一种解释操作,即告诉JVM我们想要做什么,然后它满足我们的要求,所以总是慢于直接执行相同的操作。
Java反射相关的主要API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器