反射机制
Java
中处理基本数据类型,如int
、char
等,其他均为引用类型。
每个引用类型也是Java
中的一个对象,称为类对象,用以记录该类的信息:包括类名、包名、父类、实现的接口、所有方法、字段等。 通过该引用类型创建的实例,称为类的实例对象。
Java
对类对象的加载是动态的,只有当JVM
第一读取到该类Class
(包括Interface
)的信息时,才会将该类对象加载到内存中,并将该Class
的名称与类对象进行关联。
反射:通过类型的名称,构建类对象,获取该类的信息。
public class ReflectTest {
public static void main(String[] args) {
String str = "hello";
///获取类对象
///1
Class cls = str.getClass();
///2
cls = String.class;
///3
try {
cls = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String simpleName = cls.getSimpleName();
String packageName = cls.getPackageName();
String name = cls.getName();
System.out.println(simpleName);//String
System.out.println(packageName);///java.lang
System.out.println(name);//java.lang.String
System.out.println(cls);///class java.lang.String
}
}
- 反射调用示例:
///将要反射的类
package learn.cls;
public class Person extends Object {
public int age;
String name;
public Person(String name) {
this.name = name;
}
public void introduce(String from) {
System.out.printf("我叫%s,今年%d岁,来自%s%n",name,age,from);
}
public String getInfo() {
return name + " : " +age + "岁";
}
}
///反射调用
public class ReflectTest {
public static void main(String[] args) {
///反射创建实例和调用方法
try {
Class cls = Class.forName("learn.cls.Person");
///调用指定的构造函数
Object instance = cls.getDeclaredConstructor(String.class).newInstance("张三");
///反射字段
Field field = cls.getDeclaredField("age");
field.set(instance,18);
///获取该字段的值
Integer age = field.getInt(instance);
System.out.println(age);///18
///反射方法
Method method = cls.getDeclaredMethod("introduce", String.class);
///方法调用
method.invoke(instance,"上海");///我叫张三,今年18岁,来自上海
///反射调用获取返回值
Method method1 = cls.getDeclaredMethod("getInfo");
String info = (String) method1.invoke(instance);
System.out.println(info);///张三 : 18岁
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
///上述反射调用等同于下述代码
Person person = new Person("张三");
person.age = 18;
Integer age = person.age;
System.out.println(age);
person.introduce("上海");
String info = person.getInfo();
System.out.println(info);
动态代理
动态代理:JDK
提供的动态创建接口对象的方式。定义了接口,但是并不去编写实现类,而是直接通过JDK
提供的一个Proxy.newProxyInstance()
创建了一个接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。
- 示例1:
///接口
interface Game {
void play(String toy);
String category();
}
public class DynamicDelegateTest {
public static void main(String[] args) {
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
///等同接口方法的实现
if (method.getName().equals("play")) {
String out = "let us play " + args[0];
System.out.println(out);
} else if (method.getName().equals("category")) {
return "游戏";
}
return null;
}
};
///不写实现类,直接创建接口对象
Game game = (Game) Proxy.newProxyInstance(Game.class.getClassLoader(), new Class[]{Game.class},invocationHandler);
game.play("basketball");///let us play basketball
String str = game.category();//游戏
System.out.println(str);
}
}
///上述代码的本质
public class DynamicDelegateTest implements Game {
public InvocationHandler invocationHandler;
public DynamicDelegateTest(InvocationHandler handler){
this.invocationHandler = handler;
}
@Override
public void play(String toy) {
try {
this.invocationHandler.invoke(this, getClass().getDeclaredMethod("play", String.class), new Object[]{toy});
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public String category() {
String result = null;
try {
result = (String)this.invocationHandler.invoke(this, getClass().getDeclaredMethod("category"), null);
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}
}
示例2:动态代理实现接口方法的监听
public class DynamicDelegateTest {
public static void main(String[] args) {
Game game = new Game() {
@Override
public void play(String toy) {
System.out.println("玩游戏中...");
}
@Override
public String category() {
return "游戏";
}
};
///game:最终执行方法的对象 proxyGame:实现接口方法监听生效的对象
Game proxyGame = (Game) proxyObject(game);
///启用接口方法监听
proxyGame.play("篮球");
///输出:
/*
方法:play,调用前
玩游戏中...
方法:play,调用后
*/
}
public static Object proxyObject(Game target) {
Class[] interfaces = {Game.class};
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf("方法:%s,调用前%n", method.getName());
///对接口方法进行监听后,由被代理的对象去执行
Object objc = method.invoke(target, args);
System.out.printf("方法:%s,调用后%n", method.getName());
return objc;
}
};
return Proxy.newProxyInstance(Game.class.getClassLoader(), interfaces, handler);
}
}
Java注解
注解的概念
Java
中的注解是一种可以放在类、字段、方法、参数前的特殊”注释“。
Java
中的注释会被编译器忽略,注解则可以被编译器打包进.class
文件,因此注解是一种用作标注的元数据。
Java
中使用@interface
来定义注解。注解的参数类似无参方法。声明注解参数时,推荐使用default
关键字为其设定默认值;常用的注解参数命名为value
,可在注解参数只有一个时,省略其参数名称。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface Range {
int min() default 0;
int max() default 100;
int value() default 50;
}
元注解
可以修饰其他注解的注解称为元注解。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。
-
@Target
:定义注解可以被用于源码的哪些位置。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
//注解参数为数组,可以定义多个位置
ElementType[] value();
}
///该注解的参数类型:`ElementType`
public enum ElementType {
TYPE,///类、接口、标注接口,枚举、record 声明
FIELD,///类的字段声明、包括枚举常量
METHOD,///方法声明
PARAMETER,///形式参数声明
CONSTRUCTOR,///构造函数声明
LOCAL_VARIABLE,///本地变量声明
ANNOTATION_TYPE, ///注解类型声明
PACKAGE,///Package声明
TYPE_PARAMETER,///类型参数声明
TYPE_USE,///使用类型
MODULE,///模块声明
RECORD_COMPONENT;///record部分
}
2.@Retention
: 定义注解的生命周期
如果在一个注释接口声明中没有Retention
注释,保留策略默认为RetentionPolicy.CLASS
/*If no Retention annotation is present on an annotation interface declaration, the retention policy defaults to RetentionPolicy.CLASS.*/
public @interface Retention {
RetentionPolicy value();
}
///该注解的参数类型:`RetentionPolicy `
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
///修饰的注解将被编译器记录到`java`类的`.class`文件中,但在运行时不需要被`JVM`保留
CLASS,///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.
/// 修饰的注解将被编译器记录到`java`类的`.class`文件中,并且运行时会被`JVM`保留
RUNTIME///运行期
}
-
@Repeatable
:注释其声明的注释接口是可重复的。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
Class extends Annotation> value();
}
///可重复使用注解的定义
@Repeatable(value = Ranges.class)
@Target(ElementType.TYPE)
public @interface Range {
int min() default 0;
int max() default 100;
int value() default 50;
}
@Target(ElementType.TYPE)
@interface Ranges {
Range[] value();
}
///具体使用
@Range(min = 10,max = 200,value = 120)
@Range(80)
@Range(value = 19)
class Test {
}
-
@ Inherited
使用@Inherited
定义子类是否可继承父类定义的注解。@Inherited
仅针对@Target(ElementType.TYPE)
类型的注解有效,并且仅针对class
的继承,对interface
的继承无效:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
///示例
@Inherited
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
///父类使用了这个被`@Inherited`修饰的注解
@Report(type=1)
public class Person {
}
///子类默认也继承了该父类的注解
public class Student extends Person {
}
注解的定义
第一步:使用@interface
定义注解
public @interface Report {
}
第二步:定义注解的参数、默认值
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
把最常用的参数定义为value()
,推荐所有参数都尽量设置默认值。
第三步:用元注解配置注解
@Target(ElementType.TYPE)///注解类、接口、枚举等
@Retention(RetentionPolicy.RUNTIME)///运行期保留注解
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
其中,必须设置@Target
和@Retention
,@Retention
一般设置为RUNTIME
,因为我们自定义的注解通常要求在运行期读取。一般情况下,不必写@Inherited
和@Repeatable
。
注解的处理
注解定义后也是一种class
,所有的注解都继承自java.lang.annotation.Annotation
,因此,读取注解,需要使用反射API
。
-
Java
中提供的与注解相关的反射API
///-------判断注解是否存在------
///判断类是否有注解
AnnotationTest.class.isAnnotationPresent(Range.class);
///判断类的构造函数是否有注解
AnnotationTest.class.getDeclaredConstructor().isAnnotationPresent(Range.class);
///判断某个方法是否有注解
Method method = AnnotationTest.class.getMethod("getSomeUsefulInfo");
method.isAnnotationPresent(Range.class);
///判断某个字段是否有注解
Field field = AnnotationTest.class.getField("userType");
field.isAnnotationPresent(Range.class);
///-------读取注解信息------
///获取类的注解
AnnotationTest.class.getAnnotation(Range.class);
try {
///获取类的构造函数的注解
AnnotationTest.class.getDeclaredConstructor().getAnnotation(Range.class);
///获取某个方法的注解
AnnotationTest.class.getMethod("getSomeUsefulInfo").getAnnotation(Range.class);
//获取某个字段的注解
Field field = AnnotationTest.class.getField("userType");
field.getAnnotation(Range.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
-
Java
注解的示例
1.定义两个注解:Router
和GetFuncInfo
,分别用来注解类和方法
///注解类,定义类的实例对象需要执行的操作
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Router {
String value() default "";
String operation() default "getInfo";
}
///注解方法,并设置方法调用的默认参数
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GetFuncInfo {
String value() default "";
}
- 注解的类和方法
@Router(operation = "getSomeUsefulInfo",value = "markedClass")
public class AnnotationTest {
public String param0;
public String param1;
public int param2;
@GetFuncInfo("张三的个人信息")
public void getSomeUsefulInfo(String param0){
System.out.printf("getSomeUsefulInfo:%s",param0);
}
public void functionWithSomeParameters(String p1, int p2) {
System.out.println(p1);
System.out.println(p2);
}
}
3.处理注解
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
///定义操作
String operate = "getSomeUsefulInfo";
///扫描`Router`注解的类,此处假设已知
String classStr = "annotation.AnnotationTest";
Class cls = Class.forName(classStr);
if (cls.isAnnotationPresent(Router.class)){
///获取类的注解
Router router = (Router)cls.getAnnotation(Router.class);
System.out.println(router);///@annotation.Router(value="markedClass", operation="getSomeUsefulInfo")
///根据注解的操作,找到对应的方法
String operateStr = router.operation();
for (Method method: cls.getMethods()) {
if (method.getName().equals(operateStr)) {
///获取方法的注解
if (method.isAnnotationPresent(GetFuncInfo.class)){
GetFuncInfo funcInfo = method.getAnnotation(GetFuncInfo.class);
System.out.println(funcInfo);///@annotation.GetFuncInfo("\u5f20\u4e09\u7684\u4e2a\u4eba\u4fe1\u606f")
///读取注解参数
String param = funcInfo.value();
///创建对象实例
Object clsObj = cls.getDeclaredConstructor().newInstance();
///调用方法,传入注解参数
method.invoke(clsObj,param); ///getSomeUsefulInfo:张三的个人信息
}
}
}
}
}