java基礎第12期——反射、註解
阿新 • • 發佈:2020-12-10
一. 反射
反射: 將類的各個組成部分封裝為其他物件.
1.1 獲取class物件的方式
Class.forName("全類名"):
將位元組碼檔案載入進記憶體,返回class物件
多用於配置檔案,將類名定義在配置檔案中,讀取檔案,載入類
類名.class:
通過類名的屬性class獲取
多用於引數的傳遞
物件.getClass():
多用於物件的獲取位元組碼的方式
注意:
以上三種方法獲得的位元組碼檔案地址相同
同一個位元組碼檔案在一次程式執行中只會載入一次.
1.1.1 程式碼演示
示例person:
package reflecting; public class demo00_person { public String name; protected String name1; String name2; private String name3; //成員方法 public static void method1(){ System.out.println("我是空參方法"); } public static void method11(String s){ System.out.println("我是有參方法"+s); } public demo00_person() { } public demo00_person(String name) { this.name = name; } @Override public String toString() { return "demo00_person{" + "name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
使用:
public static void main(String[] args) throws ClassNotFoundException {//異常 Class cl1 = Class.forName("reflecting.demo00_person");//從包名開始 Class cl2 = demo00_person.class; Class cl3 = demo00_person.class; System.out.println(cl1 ==cl2);//true System.out.println(cl1==cl3);//true }
1.2 class物件的功能
1.2.1 獲取
1.成員變數: Filed[] getFields(): 獲取public修飾的成員變數 Filed getFiled(String name): 獲取public修飾的指定名稱的成員變數 Filed[] getDeclaredFields() Filed getDeclaredField(String name) 2.構造方法: Constructor<?>[] getConstructors() Constructor<T> getConstructor(類<?>... parameterTypes) Constructor<?>[] getDeclaredConstructors() Constructor<T> getDeclaredConstructor(類<?>... parameterTypes) 3.成員方法: Method getMethod(String name, 類<?>... parameterTypes),方法名和按宣告順序標識該方法形參型別 Method[] getMethods() Method getDeclaredMethod(String name, 類<?>... parameterTypes) method[] getDeclaredMethods() 4.類名 String getName()
1.2.2 使用
一.成員變數:Field物件操作:
1.設定值:
void set(Object obj, Object value)
2.獲取值:
Object get(Object obj)
3.忽略訪問許可權修飾符的安全檢查:
SetAccessible(true): 暴力反射
二. 構造方法Constructor物件
1.建立物件:
T newInstance(Object... initargs)
使用此Constructor物件表示的建構函式,使用指定的初始化引數來建立和初始化建構函式的宣告類的新例項。
2.如果建立空引數構造器,可以使用:
class.getDeclaredConstructor().newInstance()
getDeclaredConstructor()根據他的引數對該類的建構函式進行搜尋並返回對應的建構函式,
沒有引數就返回該類的無參建構函式,然後再通過newInstance進行例項化。
同樣有SetAccessible(true)暴力反射.
三. 成員方法Method物件
1. 獲取成員方法後,執行方法:
Object invoke(Object obj, Object... args), 傳入呼叫該方法的類的物件和實參
同樣有SetAccessible(true).
2. 獲取方法名
String getName()
1.2.3 程式碼演示
public static void main(String[] args) throws Exception {
//獲取person的Class物件
Class<demo00_person> p1 = demo00_person.class;
//獲取成員變數
Field[] fields = p1.getFields();
for (Field field : fields) {
System.out.println(field);//person寫了四個許可權,只獲取到public修飾的
}
//獲取Field物件:名為name的成員變數
Field name = p1.getField("name");
//獲取值,傳入person物件
demo00_person person = new demo00_person();
Object value = name.get(person);//獲得值,字串預設值null
//設定值
name.set(person,"bronya");
System.out.println(person);//列印結果name="bronya"
System.out.println("---------");
//忽略訪問許可權的安全檢查
Field dc = p1.getDeclaredField("name3");
dc.setAccessible(true);//忽略許可權
Object o = dc.get(person);
System.out.println(o);//null
System.out.println("---獲取構造方法---");
Constructor<demo00_person> constructor = p1.getConstructor(String.class);
System.out.println("構造器:"+constructor);
//T newInstance(Object... initargs)方法建立物件
demo00_person person1 = constructor.newInstance("板鴨");
System.out.println("建立的物件:"+person);
//建立空引數構造器
demo00_person person2 = p1.getDeclaredConstructor().newInstance();
System.out.println("空引數:"+person2);
//獲取成員方法,傳入指定方法名,和形參型別
Method method1 = p1.getMethod("method1");//空參的
Method method11 = p1.getMethod("method11", String.class);//有參寫法
//執行方法,傳入類物件和實參
method1.invoke(p1);
method11.invoke(p1,"666");
//獲取所有public修飾的成員方法
Method[] methods = p1.getMethods();
for (Method method : methods) {
System.out.println(method);//還包括Object類的方法
System.out.println(
method.getName()
);
}
}
1.2.4 框架應用
寫個框架,建立任意類的物件,執行其中任意方法.
1.將要建立物件的全類名和需要執行的方法定義在配置檔案中
2.在程式中載入配置檔案
3.用反射技術載入類檔案進記憶體
4.建立物件, 執行方法
src目錄下新建一個properties配置檔案:
className = reflecting.demo00_person
methodName = method11
使用:
public class demo03_Test {
public static void main(String[] args) throws Exception {
//載入配置檔案,先建立Properties物件,用load方法載入
Properties properties = new Properties();
//獲取該位元組碼檔案對應的類載入器,
ClassLoader classLoader = demo03_Test.class.getClassLoader();
//呼叫getResourceAsStream()傳入配置檔名稱,獲取位元組輸入流
InputStream ras = classLoader.getResourceAsStream("pro.properties");
//位元組輸入流給屬性集合
properties.load(ras);
//獲取配置檔案中定義的資料
String classname = properties.getProperty("className");
String method = properties.getProperty("methodName");
//載入該類進記憶體
Class<?> aClass = Class.forName(classname);
//建立類物件
Object obj = aClass.getDeclaredConstructor().newInstance();
//獲取方法物件,傳入方法名和引數
Method method1 = aClass.getMethod(method,String.class);
//執行方法,傳入類物件和實參
method1.invoke(obj,"111");
}
}
二. 註解
2.1 定義
註解(Annotation),也叫元資料。一種程式碼級別的說明。
它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。
它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明,註釋。
作用分類:
①編寫文件:通過程式碼裡標識的元資料生成文件【生成文件doc文件】java doc命令
②程式碼分析:通過程式碼裡標識的元資料對程式碼進行分析【使用反射】
③編譯檢查:通過程式碼裡標識的元資料讓編譯器能夠實現基本的編譯檢查【Override】
2.2 jdk內建的註解
@Override: 檢查該方法是否是繼承自父類的
@Deprecated: 該註解標註的內容已經過時(但仍能使用)
@SuppressWarnings: 告訴編譯器忽略指定的警告,不用在編譯完成後出現警告資訊。引數傳入all表示全部
2.3 自定義註解
2.3.1 格式
元註解
public @interface 名稱{}
註解本質是介面,繼承java.lang.annotation.Annotation介面.
2.3.2 元註解
元註解是用於描述註解的註解
@Target: 表示註解能夠作用的位置
引數ElementType.
TYPE: 表示註解可以作用到類上
METHOD: 表示可以作用到方法
FIELD: 可以作用於成員變數上
@Retention: 描述註解被保留的階段
引數RetentionPolicy.
SOURCE: 不會保留到Class位元組碼檔案中
CLASS: 會保留到Class位元組碼檔案中
(常用) RUNTIME: 當前被描述的註解會保留到Class位元組碼檔案中並被JVM讀取到
@Documented: 描述註解是否被抽取到api文件中
@Inherited: 描述註解是否被子類繼承
2.3.3 註解屬性
註解裡的抽象方法稱作註解屬性,
註解屬性的要求:
1.返回值型別必須為之一: 基本資料型別\String\列舉\註解\以上型別的陣列
2.使用時需要賦值, 或者定義屬性時用default初始化預設值
2.3.4 程式碼演示
定義一個列舉類:
public enum meiju {
p1,p2,p3;
}
定義一個註解類:
public @interface an0 {
}
自定義註解, 比如用到了列舉類和註解類:
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
@Documented
@Inherited
public @interface an1 {
//定義一個註解裡的抽象方法,稱註解的屬性
String mt3() default "111";
int mt4();//基本資料型別
String mt5();//String型別
meiju mt6();//列舉型別
an0 mt7();//註解型別
String[] mt8();//陣列型別
}
給註解賦值:
//壓制該類全部警告
@SuppressWarnings("all")
public class demo00 {
//表示已經過時
@Deprecated
public void mt1(){
System.out.println("1");
}
//注意如何賦值:基本資料型別\String\列舉\註解\以上型別的陣列
@an1(mt4 = 1,mt5 = "1",mt6 = meiju.p1,mt7 = @an0,mt8 = {"1","2","a"})
public void mt2(){
mt1();
}
}
2.4 解析註解
在程式中使用(解析)註解: 用來替換配置檔案
步驟:
1. 獲取註解定義的位置的物件
2. 獲取指定的註解: getAnnotation(class) 方法
3. 呼叫註解的抽象方法獲取配置的屬性值
首先自定義一個註解:
//該註解用來描述需要執行的類名和方法名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface demo01 {
String className();
String methodName();
}
我們要使用的類Annotation.demo00, 假如我們要使用其中的mt1()
//壓制該類全部警告
@SuppressWarnings("all")
public class demo00 {
//表示已經過時
@Deprecated
public void mt1(){
System.out.println("1");
}
//注意如何賦值:基本資料型別\String\列舉\註解\以上型別的陣列
@an1(mt4 = 1,mt5 = "1",mt6 = meiju.p1,mt7 = @an0,mt8 = {"1","2","a"})
public void mt2(){
mt1();
}
}
最後的具體操作:
@demo01(className = "Annotation.demo00",methodName = "mt1")
public class demo011 {
public static void main(String[] args) throws Exception {
//解析註解
//獲取該類位元組碼檔案物件
Class<demo011> cls = demo011.class;
//獲取註釋物件
demo01 an = cls.getAnnotation(demo01.class);//在記憶體中生成了該註解介面的子類實現物件
//呼叫註解物件中定義的抽象方法獲取返回值
String className = an.className();
String methodName = an.methodName();
//載入該類進記憶體
Class<?> aClass = Class.forName(className);
//建立類物件
Object obj = aClass.getDeclaredConstructor().newInstance();
//獲取方法物件,傳入方法名和引數
Method method1 = aClass.getMethod(methodName);
//執行方法,傳入類物件和實參
method1.invoke(obj);
}
}