反射機制
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:張三的個人信息
}
}
}
}
}