反射基礎
概念
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;
這種動態獲取的信息以及動態調用對象方法的功能稱爲java語言的反射機制。
反射機制是構建框架技術的基礎
無反射 不框架
IDE Eclipse IEDA 代碼提示功能
反射的原理
.java文件 =編譯器=> .class 文件 =虛擬機=> 運行 .class文件
反射把該過程 逆向推理
正在運行的.class 文件 =編譯器=> ?
反射就是把java類中的各種成分映射成一個個的Java對象
例如:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖,把個個組成部分映射成一個個對象。
(其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述)
電腦本身是一個對象
解剖成爲 一個一個的組成部分 CPU 硬盤 GPU 內存 風扇 網卡 …
Class類
- Class類的
類
表示正在運行的Java應用程序中的類和接口。
Object 中 有一個方法 getClass() 返回值 是 Class 類型 表示 正在運行的類的實際類型
所有的類都有該方法
三種Class 對象的獲取方式
第一種/ 根據對象獲取
Student stu1 = new Student();
Student stu2 = new Student();
System.out.println(stu1 == stu2);
Class cla1 = stu1.getClass();
Class cla2 = stu2.getClass();
System.out.println(cla1 == cla2);
程序運行開始 , 在創建對象的時候, 如果同一個類創建多個對象 ,實際運行的類型只有一個
第二種 / 根據類獲取
類名.class 屬性
Class cla = Student.class;
第三種 / 根據字符串獲取
// 通過一個類的 全路徑 獲取該類的字節碼文件對象
Class cla = Class.forName("s0731.ref.Student");
後期使用反射推薦使用第三種, 只依賴於一個字符串 Class類常用方法
屬性類 Field
獲取屬性的方法
getField(屬性名) 只能獲取公共(public)的屬性
getDeclaredField(屬性名) 獲取任意修飾符的屬性
getFields() 獲取所有公共(public)的屬性 返回值爲Field 數組
getDeclaredFields() 獲取任意修飾符的屬性集合
屬性類 Field
構造類 Constructor
獲取構造的方法
getConstructor(參數列表類型 類<?>... parameterTypes)
// 方法重載
getDeclaredConstructor(參數列表類型 類<?>... parameterTypes)
getConstructors()
getDeclaredConstructors()
Constructor 構造包裝類的方法
newInstance(Object... initargs)
使用此 Constructor對象表示的構造函數,使用指定的初始化參數來創建和初始化構造函數的聲明類的新實例。
方法類 Method
invoke(Object obj, Object... args)
在具有指定參數的 方法對象上調用此 方法對象表示的底層方法。
獲取方法的方法
getMethod(String name, 類<?>... parameterTypes)
getDeclaredMethod(String name, 類<?>... parameterTypes)
getMethods()
getDeclaredMethods()
反射應用
基礎數據
public class Student {
public String name;
int age;
private String address;
public Student(){}
public void study(){
System.out.println("正在學習!!");
}
private void play(){
System.out.println("隨便玩");
}
private void play(String thing){
System.out.println("玩"+ thing);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
案例1 公共屬性
獲取對象的公共屬性並且賦值
// 獲取到了 該類的字節碼文件 .class 文件
Class stuCla = Class.forName("s0731.ref.Student");
// 獲取該類的構造方法
Constructor constructor = stuCla.getConstructor();
// 獲取的就是 public Student(){} 的包裝對象
Object obj1 = constructor.newInstance();
Object obj2 = constructor.newInstance();
// 獲取屬性
Field nameField = stuCla.getField("name");
nameField.set(obj1,"張三");
nameField.set(obj2,"李四");
System.out.println(obj1);
System.out.println(obj2);
案例2 私有屬性
獲取對象的私有屬性並且賦值
public static void main(String[] args) throws Exception{
Class stuCla = Class.forName("s0731.ref.Student");
Constructor constructor = stuCla.getConstructor();
Object obj = constructor.newInstance();
// 獲取私有屬性的包裝類
Field addressField = stuCla.getDeclaredField("address");
// 雖然能夠獲取到 private 修飾的屬性
// private 不能被外界訪問和修改
// 設置 取消 private 的 限制
addressField.setAccessible(true);
addressField.set(obj,"青島");
Field nameField = stuCla.getField("name");
nameField.set(obj,"張三");
Field ageField = stuCla.getDeclaredField("age");
ageField.set(obj,20);
System.out.println(obj);
}
案例3 方法
獲取公共的方法並調用
Class stuCla = Class.forName("s0731.ref.Student");
Constructor constructor = stuCla.getConstructor();
Object obj = constructor.newInstance();
/ 獲取公共方法 方法名爲 study 的無參方法
Method study = stuCla.getMethod("study");
study.invoke(obj);
獲取私有的無參方法
// 獲取私有方法 方法名爲 play 的無參方法
Method play = stuCla.getDeclaredMethod("play");
play.setAccessible(true);
play.invoke(obj);
獲取私有的有參方法
// 獲取私有方法 方法名爲 play 的有參方法
Method play = stuCla.getDeclaredMethod("play",String.class);
play.setAccessible(true);
play.invoke(obj,"籃球");
獲取方法的返回值
如果play 方法有返回值
Object Parma = play.invoke(obj,"籃球");
實際應用
避開list 泛型檢查
public static void main(String[] args) throws Exception{
List<String> list = new ArrayList<String>();
list.add("張三");
list.add("李四");
// list.add(123);
// 任務 向 泛型爲 String 的 list 集合中 成功添加一個 123 的整數類型
/* 步驟
1- 獲取list 的運行時 字節碼對象
2- 獲取 list 的 add 方法封裝對象
3, 使用 封裝對象 調用 add 方法添加內容
泛型實際上僅僅是在編譯期生效
*/
Class<?> clazz = Class.forName("java.util.ArrayList");
Method add = clazz.getMethod("add", Object.class);
add.invoke(list,123);
System.out.println(list);
}
properties
基本使用
新建文件
abc.properties
name=於老闆
age=50
通過程序加載
public static void main(String[] args) throws Exception {
// 1- 創建 properties 對象
// 2- 對象 通過 輸入流 加載 本地文件
// 3- 分別通過鍵 獲取值
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("src\\s0731\\abc.properties");
pro.load(fis);
fis.close();
String name = pro.getProperty("name");
String age = pro.getProperty("age");
System.out.println(name + "\t"+age);
通過配置文件自定義執行內容
public class Demo1 {
public static void main(String[] args) throws Exception {
// Test1 t1 = new Test1();
// t1.m1();
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\abc.properties");
pro.load(fis);
fis.close();
String className = pro.getProperty("class");
String methodName = pro.getProperty("method");
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
Method method = clazz.getMethod(methodName);
method.invoke(obj);
}
}
需要把整個項目打成一個可執行 jar 包
可以通過修改配置文件 , 來動態的指定我們需要運行的程序