注解和反射
1、注解
什么是注解?
类似@Override注解,注解可以被其他程序所读取,进行信息处理
1.1内置注解
就是jdk自带的注解
如@Override:定义在java.lang.Override中,只适用于修辞方法,表示当前被修辞的方法用于从写父类中的另一个方法
@Deprecaed:该注解的作用是修辞方法,属性,类,告诉编译器和程序员,被改注解注释的属性,类,方法已被放弃,已经有了更好的替代了,所以不推荐程序员继续使用,建议新的。
@SuperWarnings:直译过来就是允许错误的意思,定义在java.lang.SuppressWarnings中,用来抑制编译器时警告信息,和上面两个注解不同,该注解需要添加一个参数餐能正确使用,这些参数都是被提前设计好的,我们根据自己的具体需求选择即可
1.2元注解
我们在定义注解的时候,需要使用java提供的元注解,就是负责注解注解的,
- @Target
描述可适用范围,就是可以在那里使用
下面这个Target说明了可以在类、方法、变量上都可以使用 都不会报错
@Target(value = {ElementType.ANNOTATION_TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1 {
//注解的参数:参数类型 + 参数名 ();
// 注解可以显示赋值,如果没有赋值,我们就要给注解赋值
String name() default "";
int age();
int id () default -1;//如果默认值为-1,代表不存在
String[] school () default {"北京大学"};
}
package com.huang.Annotation;
@MyAnnotation
public class Test {
@MyAnnotation
void cl(){
}
}
- @Retention
这个注解的作用就是我们需要告诉编译器我们需要在什么级别保存该注释信息,用于描述注解的生命周期。
取值RetentionPolicy | 作用 |
---|---|
SOURCE | 在源文件中有效(即源文件保留) |
CLASS | 在class文件中有效(即class保留) |
RUNTIME | 在运行时有效(即运行时保留)注:为RUNTIME时可以被反射机制所读取 |
一般我们都使用RUNTIME
- @Documented 表示是否将我们的注解生成在JAVAdoc中
- @Inherited 表示子类可以继承父类的注解
1.3自定义注解
使用@interface自定义注解时,会自动继承java.lang.annotatio.Annotation接口
自定义注解格式:[public] @interface 注解名{ [注解内容] }
其中的每一个方法实际上是声明了一个配置参数
方法的名称就是参数的名称
返回值类型就是参数的类型(返回值类型是基本类型,Class,String,enum),
可以通过default来声明参数的默认值
如果只有一个参数成员,一般参数名为value
注解元素必须要有值,我们定义注解元素时,经常使用空字符串/0作为默认值
public class DefineAnnotation {
@MyAnnotation1(name = "lin",age = 23)
void test(){
}
}
@Target(value = {ElementType.ANNOTATION_TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1 {
//注解的参数:参数类型 + 参数名 ();
//注解可以显示赋值,如果没有赋值,我们就要给注解赋值
String name() default "";
int age();
int id () default -1;//如果默认值为-1,代表不存在
String[] school () default {"北京大学"};
}
2、反射
2.1什么是反射?
-
Reflection(反射)是java被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息(包括类名,类方法,类属性等(包括私有方法和属性)),并且能够直接操作任意对象的内部方法及属性。
-
类加载完之后,堆内存中会产生一个class类型的对象,该对象包括了该类的完整结构体系,我们就可以通过这个class对象得到类的所有信息,class对象像一面镜子一样,可以看到该类的所有结构,所以我们把这种获取类信息的方法称为反射
-
正常方式:引入需要的“包类”名称——》通过new实例化——》取得实例化对象
-
反射方式:实例化对象——》通过getClass()方法——》获得完成的“包类”名称
2.2 java反射机制提供的功能
- 在运行时判断任意一个对象所属的类,就是可以找到一个对象所属的类
- 在运行是可以构造任意一个对象
- 在运行时可以判断一个类所拥有的方法和成员变量
- 在运行时获取反省的信息
- 在运行时可以调用任意一个对象的方法和成员变量
- 在运行时处理注解
- 生成动态代理
2.3反射相关的主要API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的成员方法
java.lang.reflect.Field:代表类的成员属性
java.lang.reflect.Constructor:代表类的构造器
Object是所有类的父类,它封装了一个getClass方法,这意味这所有的类都有这一个方法,而且这个方法的返回值是一个Class(就是一个class对象),这个Class类就是Java反射的源头
对象照镜子后可以得到的信息:某个类的属性、方法、构造器、某个类到底实现了那些接口。
对于每一个类,JRE都为其保留了一个不变的Class类型和对象。一个Class对象包含了特定的某个结构(class/interface/enum/annotation/primitive type/void
Class本身也是一个类
Class对象之只能由系统构建对象,我们只能获取,不能创建
一个加载的类在JVM中只会有一个Class实例,但是一个class实例可以有很多个具体的对象
一个Class对象对应的是加载到JVM中的一个.class文件
每一个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整地得到一个类中的所有被加载结构
Class类是Reflection的根源,针对任何你想要动态加载、运行的类,唯有先获得对应的Class对象
2.4怎么获取class对象
- 已知全类名,就使用Class类的.forName()方法
Class c1 = Class.forName("com.huang.Reflect.User");
- 已知具体类,就使用class属性获取
Class<Student> c1 = Student.class;
- 已知类的具体对象,就使用getClass()方法
Class c2 = person.getClass();
2.5通过反射获取类对象的属性、方法、构造器等
- 获取类对象构造器创建对象
//获取class对象
Class<?> aClass = Class.forName("com.huang.Test.Animal");
//打印class对象的名字
String name = aClass.getName();
System.out.println(name);
//使用newInstance创建对象,newInstance创建对象使用的是无参构造器,没有无参构造器就报错
// Object a1 = aClass.newInstance();
// System.out.println(a1);
//获取Animal类的构造器,使用构造器创建了一个对象
Constructor<?> constructor = aClass.getDeclaredConstructor(String.class, int.class, int.class);
Object dog = constructor.newInstance("SS", 50, 120);
System.out.println(dog);
- 通过反射调用方法
//使用newInstance创建对象
Animal a1 =(Animal) aClass.newInstance();
//类对象使用getMethod()方法获取类对象的setName方法,
Method setName = aClass.getMethod("setName", String.class);
//invoke :激活的意思
//(对象,“方法的值”)
setName.invoke(a1, "san");
System.out.println(a1.getName());
打印结果san
- 通过反射操作属性
//使用newInstance创建对象
Animal a2 =(Animal) aClass.newInstance();
//类对象使用getDeclaredField方法获取类对象的name属性,
Field name1 = aClass.getDeclaredField("name");
//不能直接操作私有属性,我们通过setAccessible(true)关闭方法或者属性的安全检测
name1.setAccessible(true);
name1.set(a2, "二狗子");
System.out.println(a2.getName());
打印结果二狗子
getFileds()获取全部公开的属性包括父类的
getDeclaredFields获取本类所有的属性字段 不包括父类的,可以获取私有的属性
getMethod()获取全部公开的方法,包括父类的
getDeclaredMethod()获取本类所有的方法,不包括父类的,可以获取私有的方法
.........等