反射(一)-常用方法及載入資源文件

1、初識反射

1.1什麼是反射

反射是指在程式運行期間,能夠觀察和修改類或者類的對象的屬性和行為的特性;

1.2 編譯時與運行時

編譯時

編譯時顧名思義就是正在編譯的時候 . 那啥叫編譯呢?就是編譯器幫你把源程式碼翻譯成機器能識別的程式碼 ;

編譯時就是簡單的作一些翻譯工作 ,比如檢查老兄你有沒有粗心寫錯啥關鍵字了啊.有啥詞法分析,語法分析之類的過程. 就像個老師檢查學生的作文中有沒有錯別字和病句一樣;

運行時

所謂運行時就是程式碼跑起來了.被裝載到記憶體中去了;

(你的程式碼保存在磁碟上沒裝入記憶體之前是個死家伙.只有跑到記憶體中才變成活的);

2、反射獲取Class對象的四種方法

getClass() 適合有對象實例的情況下
.class 僅適合在編譯前就已經明確要操作的 Class
forName(「類的全類名」) 已明確類的全路徑名
loadClass(「類的全類名」); 通過類載入器的loadClass(類的全路徑名)

2.1 getClass()

 Person person = new Person();
//第一種:getClass() 需要有對象實例
Class<Person>  class1 = (Class<Person>)person.getClass();
System.out.println("==對象.getClass()==:"+class1); //com.kgc.reflection.Person

2.2 .class

//第二種:.class  需要明確操作的Class
Class<Person> class1_2 = Person.class;
System.out.println("==類.class==:"+class1_2);//com.kgc.reflection.Person

2.3 forName()

//第三種:forName()  需要類的全路徑名
Class<Person> class1_3 = (Class<Person>)Class.forName("kgc.reflection.TestPerson");
System.out.println("==Class.forName(『類路徑』)==:"+class1_3);//com.kgc.reflection.Person

2.4 loadClass(「類的全類名」);

//4.通過類載入器的loadClass("類的全類名");
ClassLoader classLoader = this.getClass().getClassLoader();
Class<Person> class1_4 = (Class<Person>)classLoader.loadClass("com.kgc.reflection.Person");
System.out.println("通過類載入器的loadClass"+class1_4.getName());//com.kgc.reflection.Person

3、通過Class類初始化對象

3.1 無參構造方法

//先獲得Class對象java
Class<Person> class2 = Person.class;
//創建實例對象,調用默認的空參構造
Person person2 = class2.newInstance();
System.out.println(person2); //Person{name='null', age=null}

3.2 有參構造方法

//先獲得Class對象java
Class<Person> class2 = Person.class;
//通過Class對象獲取有參構造類對象
Constructor<Person> constructor = class2.getConstructor(String.class, Integer.class);
//通過有參構造類對象的newInstance方法初始化對象
Person person3 = constructor.newInstance("化羽", 12);
System.out.println(person3); //Person{name='張三', age=30}

4、獲取並修改屬性值

4.1 對getDeclared…的理解

getField,Mothed,Constructor 獲取自己及父類的屬性,方法,構造器(不包括私有的)
getDeclaredField,Mothed,Constructor 只獲取自己類的屬性,方法,構造器(包括私有的)

4.2 非私有屬性

getField(String name) 獲取非私有屬性
set(對象實例, Object value) 對指定實例的指定屬性賦值
//name的定義:public String name;
//獲取 非私有屬性 name
Field fieldName = class2.getField("name");
//通過 屬性實例名.set(對象實例,屬性值)  對指定實例的指定屬性賦值
fieldName.set(person3,"張三");
System.out.println(person3); //Person{name='張三', age=12}  //name發生了該改變

4.3 私有屬性

getDeclaredField(String name) 獲取私有屬性及其他屬性
setAccessible(boolean flag) 是否取消 Java 語言訪問檢查(true是,false否)
set(對象實例, Object value) 對指定實例的指定屬性賦值
//通過反射,獲取運行時類的屬性,private age,無法使用getField,必須是使用getDeclaredField,設置訪問許可權
//age的定義:private  Integer age;
//獲得 私有屬性age
Field fieldAge = class2.getDeclaredField("age");
//取消 Java 語言訪問檢查
fieldAge.setAccessible(true);
//通過 屬性實例名.set(對象實例,屬性值)  對指定實例的指定屬性賦值
fieldAge.set(person3,30);
System.out.println(person3); //Person{name='張三', age=30}  //age發生了變化

5、獲取並使用方法

5.1 無參方法

getMethod(方法名) 獲取無參方法
invoke(對象實例) 執行無參方法
//sayHi()方法:System.out.println("我是一個人,我的名字叫:"+name+",今年:"+age+"歲");
Method methodHi = class2.getMethod("sayHi");
methodHi.invoke(person3); //我是一個人,我的名字叫:張三,今年:30歲

5.2 有參方法

getMethod(方法名,參數類) 獲取有參方法
invoke(對象實例,參數) 執行有參方法
//sayHello(String nation)方法: System.out.println("我的國際是:"+nation);
Method methodHello = class2.getMethod("sayHello", String.class);
methodHello.invoke(person3,"中國"); //我的國際是:中國

5.3 私有方法

getDeclaredMethod(方法名,參數類) 獲取私有方法及其他方法
setAccessible(boolean flag) 是否取消 Java 語言訪問檢查(true是,false否)
invoke(實例,參數) 執行有參私有方法
//private void myMoney(double money){
//        System.out.println("我有"+money+"私房錢!");
//    }
//調用私有方法
//getDeclaredMethod("myMoney", double.class) 獲取方法
Method myMoney = class2.getDeclaredMethod("myMoney", double.class);
//取消Java語言訪問檢查
myMoney.setAccessible(true);
//執行方法
myMoney.invoke(person3,2.5); //我有2.5私房錢!

6、類載入器

6.1 三種類載入器

BootStrap ClassLoader 引導類載入器(Java的核心庫,都是通過此載入器載入到記憶體的)
Extension ClassLoader 擴展類載入器
System ClassLoader 系統類載入器(所有的自定義載入列,都是系統類載入器)
//1.系統類載入器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2

//2.擴展類載入器:系統類載入器的父類
ClassLoader ExtClassLoader = systemClassLoader.getParent();
System.out.println(ExtClassLoader);//sun.misc.Launcher$ExtClassLoader@8efb846

//3引導類載入器擴展類載入器的引導類,無法直接獲取;(Java的核心庫,都是通過此載入器載入到記憶體的)
ClassLoader bootstapLoader = ExtClassLoader.getParent();
System.out.println(bootstapLoader); //null

//4.所有的自定義載入列,都是系統類載入器
ClassLoader classLoader4 = Person.class.getClassLoader();
System.out.println(classLoader4); //sun.misc.Launcher$AppClassLoader@18b4aac2

//5.Sting類的默認類載入器 ,引導類載入器(Java的核心庫,都是通過此載入器載入到記憶體的)
ClassLoader classLoader5 = String.class.getClassLoader();
System.out.println(classLoader5); //null

幾種類載入器的關係

在這裡插入圖片描述

雙親委派機制
Java虛擬機對class文件採用的是按需載入,載入類的class文件時使用的時雙親委派模式,即把請求交給父類處理,如果父類載入器還有父類,則進一步向上委託,直到啟動類載入器,如果父類載入器載入成功,則返回,否則其子類載入器才會嘗試載入。他是一種任務委派模式;

6.2 通過類載入器讀取配置文件

jdbc.properties中的資訊

#key=value
user_name=kh96
usre_pwd=123123

6.2.1 使用位元組流將配置文件載入到記憶體中

//創建一個properties類對象
Properties properties = new Properties();

//創建一個位元組輸入流
//注意: 使用輸入流來讀取文件時默認在當前項目下查找
FileInputStream fileInputStream = new FileInputStream("src/jdbc.properties");

//調用properties的load()方法來讀取載入到記憶體中的配置文件
properties.load(fileInputStream);

//獲取配置文件中的資訊
Object user_name = properties.get("user_name");
Object usre_pwd = properties.get("usre_pwd");

System.out.println("資料庫的用戶名:"+user_name); //kh96
System.out.println("資料庫的密碼:"+usre_pwd); //123123

6.2.2 使用ClassLoader(類載入器(具體是:系統類載入器))將配置文件載入到記憶體中來

//創建一個properties類對象
Properties properties = new Properties();

//通過當前類獲取類載入器(系統類載入器)
ClassLoader classLoader = this.getClass().getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

//通過系統類載入器對象調用getResourceAsStream()方法以流的形式獲取資源,將配置文件載入到記憶體中
//注意: 我們使用類載入器的getResourceAsStream(String path)方法來獲取資源時默認是在本項目的src文件目錄之下獲取
classLoader.getResourceAsStream("jdbc.properties");

//獲取配置文件中的資訊
Object user_name = properties.get("user_name");
Object usre_pwd = properties.get("usre_pwd");

System.out.println("資料庫的用戶名:"+user_name); //kh96
System.out.println("資料庫的密碼:"+usre_pwd); // 123123

總結

都是先將配置文件以流的形式載入到記憶體,再通過Properties類讀取記憶體中的配置資訊;

Tags: