注解和反射
一、什么是注解
1.1 注解简介
- Annotation是从JDK5开始引入的新技术
- Annotation的作用
- 不是程序本身,可以对程序做出解释,
- 可以被其他程序(比如编译器)读取
- Anntation的格式:
- 注释是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:
@SuppressWarnings(value=“unchecked”)
.
- 注释是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:
- Annotation在哪使用:
- 可以附加在
package,class,method,field
等上面,相当于给他们添加了额外的辅助信息,我们就可以通过反射机制编程实现对这些元数据的访问
- 可以附加在
1.2 内置注解
@Override
:定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个声明。@Deprecated
:定义在java.lang.Deprecated中,此注释可以用于修饰方法,属性,类,表示不建议使用,通常是因为它很危险或者存在更好的选择。@SuppressWarnings
:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息,带有参数:@SuppressWarnings("all")
@SuppressWarnings("unchecked")
@SuppressWarnings(value={"unchecked","deprecation"})
- 等等
1.3 定义注解
java语言使用@interface
语法来定义注解,格式如下:
public @interface Demo03 {
int type() default 0;
String level() default "info";
String level() default "";
}
注解的参数类似无参数方法,可以用default
设定一个默认值,最常用的参数应当命名为value
分析:
@interface
用来声明一个注解,格式:public @interface 注解名{定义内容}
- 其中每一个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型Class,String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
二、元注解
@Target
@Retention
@Documented
@Inherited
2.1 @Target
使用@Target
可以定义Annotation
能够被应用与于源码的哪些位置
- 类或接口:
ElementType.TYPE
; - 字段:
ElementType:FILED
; - 方法:
ElementType.METHOD
; - 构造方法:
ElementType.CONSTRUCTOR
; - 方法参数:
ElementType.PARAMETER
;
2.2 @Retention
另一个重要的元注解@Retention
定义了Annotation
的生命周期:
- 仅编译期:
RetentionPolicy.SOURCE
; - 仅class文件:
RetentionPolicy.CLASS
; - 运行期:
RetentionPolicy.RUNTIME
;
如果@Retention
不存在,则该Annotation
默认为CLASS
,
因为通常我们自定义的Annotation
都是RUNTIME
,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)
这个元注解
2.3 @Repeatable
这个元注解可以定义Annotation
是否可重复,这个注解应用不是特别广泛
@Repeatable(Demo03.class)
@Target(ElementType.METHOD)
public @interface Demo03 {
int type() default 0;
String level() default "info";
String value() default "";
}
经过@Repeatable
修饰后,在某个类型声明处,就可以添加多个@Report
注解:
@Report(type=1,level="debug")
@Report(type=2,level="warning")
public class Hello{
}
2.4 @Inherited
定义子类是否可继承父类定义的Annotation
,@Inherited仅针对@Target(ElementType.TYPE)
类型的annotation
有效,并且仅针对class的继承,对interface的继承无效:
三、反射
3.1 得到Class类的几种方式
- 已知具体的类,通过类的class属性获取
Class class = Person.class;
- 已知某一个类的实例,调用实例的
getClass()
方法获取
Class class = person.getClass();
- 已知一个类的全类名,且该类在类路径下,通过Class类的静态方法
forName()
获取,可能抛出ClassNotFoundException
Class class = Class.forName("demo.Student");
- 内置基本数据类型可以直接使用
类名.Type
- 还可以利用
ClassLoader
- 还可以获取父类的Class类:
class.getSuperclass();
3.2 Class类常用方法
getName()
:返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称newInstance()
:为类创建一个实例,但只能调用默认构造器(无参数构造器)getClassLoader()
:返回该类的类加载器getComponentType()
:返回表示数组组件类型的ClassgetSuperClass()
:返回表示此Class所表示的实体(类、接口、基本类型或void)的超类的ClassisArray()
判定此Class对象是否表示一个数组类。
3.3 获取类运行时的结构
获取属性:
1. getFields()
:获取所有的公有字段
2. getDeclaredFields()
:获取所有字段,包括私有,受保护,默认,公有。
3. getField(String fieldName)
获取某个公有字段
4. getDeclareField(String fieldName)
获取方法:
1. public Method[] getMethods()
:获取所有"公有方法";(包含了父类的方法也包含Object类)
2. public Method[] getDeclaredMethods()
:获取所有的成员方法,包括私有的(不包括继承的)
获取构造器:
1. public Constructor[] getConstructors():
所有"公有的"构造方法
2. public Constructor[] getDeclaredConstructors():
获取所有的构造方法(包括私有、受保护的、默认的、公有的)
3.4 动态创建对象执行方法
package com.zlf;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestClassLoader {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取系统类加载器可以加载的路径
/* System.out.println(System.getProperty("java.class.path"));
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent());
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());*/
Class personClass = Class.forName("com.zlf.Person");
// 构造一个对象
Person person = (Person) personClass.newInstance();//默认调用无参构造
System.out.println(person);
// 通过构造器创建对象
Constructor declaredConstructor = personClass.getDeclaredConstructor(String.class);
Person person2 = (Person) declaredConstructor.newInstance("周霖峰");
System.out.println(person2);
// 通过反射调用方法
Person person3 = (Person) personClass.newInstance();
Method setName = personClass.getDeclaredMethod("setName", String.class);
// 使用invoke调用方法
setName.invoke(person3,"周旋");
System.out.println(person3.getName());
// 通过反射操作属性
Person person4 = (Person) personClass.newInstance();
Field name = personClass.getDeclaredField("name");
name.setAccessible(true);//暴力访问(忽略访问修饰父)
name.set(person4,"person4");
System.out.println(person4.getName());
}
}
3.5 获取泛型的信息
ParameterizedType
:表示一种参数化类型比如Collection<String>
GenericArrayType
:表示一种元素类型是参数化类型或者类型变量的数组类型TypeVariable
:是各种类型变量的公共父接口WildcardType
:代表一种通配符类型表达式
package com.zlf;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class GenericGetReflect {
public static void main(String[] args) throws NoSuchMethodException {
Method method = GenericGetReflect.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType:genericParameterTypes
) {
System.out.println("#" + genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument:actualTypeArguments
) {
System.out.println(actualTypeArgument);
}
}
}
}
public void test01(Map<String,Person> map, List<Person> list){
System.out.println("test01");
}
public Map<String,Person> test02(){
System.out.println("test02");
return null;
}
}
3.6 获取注解信息
package com.zlf;
import java.lang.annotation.*;
public class AnnotationReflect {
public static void main(String[] args) throws NoSuchFieldException {
Class StuClass = Students.class;
// 通过反射获取注解
Annotation[] annotations = StuClass.getAnnotations();
for (Annotation annotation: annotations
) {
System.out.println(annotation);
}
// 获取注解的value值
Table table = (Table) StuClass.getAnnotation(Table.class);
String value = table.value();
System.out.println(value);
// 获取指定注解
java.lang.reflect.Field name = StuClass.getDeclaredField("name");
Field annotation = name.getAnnotation(Field.class);
System.out.println(annotation.colName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Table("db_stu")
class Students{
@Field(colName = "db_id",type = "int",length = 10)
private int id;
@Field(colName = "db_name",type = "varChar",length = 10)
private String name;
@Field(colName = "db_age",type = "int",length = 10)
private int age;
public Students(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Students() {
}
public int getId() {
return id;
}
public void setId(int id) {
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;
}
@Override
public String toString() {
return "Students{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field{
String colName();
String type();
int length();
}