JavaSE基礎學習筆記及案例(三)反射
1.反射
1.1反射概述
1.java反射機制是在執行狀態時,對於任意一個類,都可以知道這個類的所有屬性和方法;
動態獲取資訊以及動態呼叫物件的方法稱為java反射機制;
2.反射的三種方式
(1)Object類的getClass()方法,判斷兩個物件是否是同一個位元組碼檔案;
(2)靜態屬性class,鎖物件;
(3)Class類中靜態方法forName(),讀取配置檔案;
public class demo2_Ref { /** * @param args * 反射的三種獲取方式 * @throws ClassNotFoundException */ public static void main(String[] args) throws ClassNotFoundException { Class clazz1 = Class.forName("day8.Fac_Method.Person");//1 Class clazz2 = Person.class;//2 Person p = new Person(); Class clazz3 = p.getClass();//3 System.out.println(clazz1==clazz2); System.out.println(clazz2==clazz3); } }
1.2反射之Class.forName()讀取配置檔案
—newInstance為class建立一個例項物件
//反射實現
BufferedReader br = new BufferedReader(new FileReader("config.properties"));
Class clazz = Class.forName(br.readLine());
Fruit f = (Fruit) clazz.newInstance();//為class建立一個例項物件
程式碼實現:
第一步:建立配置檔案config.properties;
day8.Fac_Method.Apple
第二步:
package day8.Fac_Method; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class demo3 { public static void main(String[] args) throws Exception{ //反射實現 BufferedReader br = new BufferedReader(new FileReader("config.properties")); Class clazz = Class.forName(br.readLine()); Fruit f = (Fruit) clazz.newInstance(); Juicer j = new Juicer(); j.run(f); } } interface Fruit{ public void squeeze(); } class Apple implements Fruit{ public void squeeze(){//榨汁方法 System.out.println("榨蘋果汁"); } } class Orange implements Fruit{ public void squeeze(){ System.out.println("榨橘子汁"); } } //榨汁機 class Juicer{ public void run(Fruit f){//榨汁機執行方法 f.squeeze(); } }
1.3通過反射獲取構造方法
無參構造與有參構造獲取物件的方法
1.獲取位元組碼檔案
Class clazz = Class.forName("day8.Fac_Method.Person");
2.獲取有參構造
Constructor c = clazz.getConstructor(String.class,int.class);
3.通過有參構造建立物件
Person p = (Person) c.newInstance("張三",23);
4.列印輸出p;
Person [name=張三, age=23]
5.程式碼實現:
第一步:建立Person類
public class Person {
private String name;
private int age;
/*public Person() {
super();//註釋掉無參,在反射中就只能通過有參來實現
}*/
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
第二步:通過反射獲取物件(無參與有參)
/**
* @author ZHENG
* 通過反射獲取無參構造和獲取有參構造的區別
*/
public class demo4_Constrct {
public static void main(String[] args) throws Exception {
//1.獲取位元組碼檔案
Class clazz = Class.forName("day8.Fac_Method.Person");
/*
//2.通過無參構造獲取物件
Person p = (Person) clazz.newInstance();
System.out.println(p);
*/
//2.獲取有參構造
Constructor c = clazz.getConstructor(String.class,int.class);
//通過有參構造建立物件
Person p = (Person) c.newInstance("張三",23);
System.out.println(p);
}
}
1.4通過反射獲取成員變數
1.獲取位元組碼檔案
Class clazz = Class.forName("day8.Fac_Method.Person");
2.獲取有參構造
Constructor c = clazz.getConstructor(String.class,int.class);
3.通過有參構造建立物件
Person p = (Person) c.newInstance("張三",23);
4.在Person類中成員變數為私有的,所以反射需要通過暴力來獲取私有欄位
Field f = clazz.getDeclaredField("name");
5.暴力獲取到欄位,需要去除私有許可權>>>>修改成員變數
f.setAccessible(true);//去除私有許可權
f.set(p, "李四");
6.輸出p的結結果為
Person [name=李四, age=23]
完整程式碼:
public class demo5_Filed {
public static void main(String[] args) throws Exception{
//1.獲取位元組碼檔案
Class clazz = Class.forName("day8.Fac_Method.Person");
//2.獲取有參構造
Constructor c = clazz.getConstructor(String.class,int.class);
//3.通過有參構造建立物件
Person p = (Person) c.newInstance("張三",23);
//4.獲取name欄位,即使私有也無關(暴力反射)
/*
Field f = clazz.getField("name");
f.set(p, "李四");//修改獲取的成員變數
*/
Field f = clazz.getDeclaredField("name");//暴力獲取私有的欄位
f.setAccessible(true);//去除私有許可權
f.set(p, "李四");//修改獲取的成員變數
System.out.println(p);
}
}
1.5通過反射獲取方法並且執行
1.根據1.3的Person類,完善Person類,添加了eat()方法【無參】
public void eat(){
System.out.println("今天吃了頓霸王餐");
}//如果是私有的方法需要暴力獲取,本例不需要
2.獲取位元組碼檔案【Person】
Class clazz = Class.forName("day8.Fac_Method.Person");
3.獲取有參構造
Constructor c = clazz.getConstructor(String.class,int.class);
4.通過有參建立物件
Person p = (Person) c.newInstance("張三",23);
5.通過getMethod獲取Person類裡的方法
Method m = clazz.getMethod("eat");
6.執行獲取到的方法
m.invoke(p);
7.輸出結果
今天吃了頓霸王餐
完整程式碼:
/**
* 無參執行方法
*/
public class demo6 {
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("day8.Fac_Method.Person");
Constructor c = clazz.getConstructor(String.class,int.class);
Person p = (Person) c.newInstance("張三",23);
//獲取eat方法
Method m = clazz.getMethod("eat");
//執行該方法
m.invoke(p);
}
}
1.根據1.3的Person類,完善Person類,添加了eat(int num)方法【有參】
public void eat(int num){
System.out.println("今天吃了"+num+"頓霸王餐");
}//如果是私有的方法需要暴力獲取,本例不需要
2.獲取位元組碼檔案【Person】
Class clazz = Class.forName("day8.Fac_Method.Person");
3.獲取有參構造
Constructor c = clazz.getConstructor(String.class,int.class);
4.通過有參建立物件
Person p = (Person) c.newInstance("張三",23);
5.通過getMethod獲取Person類裡的方法
Method m = clazz.getMethod("eat", int.class);//引數為位元組碼檔案
6.執行獲取到的方法
m.invoke(p, 3);
7.輸出結果
今天吃了3頓霸王餐
1.6通過泛型擦除實現列表追加資料
public static void main(String[] args) throws Exception{
ArrayList<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
Class clazz = Class.forName("java.util.ArrayList");
Method m = clazz.getMethod("add", Object.class);//add方法的引數為Object型別
m.invoke(list, "abc");//執行
System.out.println(list);
}
結果
[111, 222, abc]
1.7反射練習
1定義一個Test_exec類;
2.寫一個Properties格式的配置檔案,配置類的完整名稱;
3.寫一個程式讀取這個配置檔案,獲得完整名稱並載入這個類,用反射執行run方法;
第一步:建立一個Test_exec類,裡面包括run方法;
package day9_fanshe;
public class Test_exec {
public void run(){
System.out.println("Welcome to Bj");
}
}
第二步:建立配置檔案xxx.properties,內容為Test_exec的完整路徑
day9_fanshe.Test_exec
第三步:建立Test_demo類,通過反射執行run()方法
public class Test_demo {
public static void main(String[] args) throws Exception{
//1.建立流物件,讀取配置檔案
BufferedReader br = new BufferedReader(new FileReader("xxx.properties"));
//2.讀取類名,獲取位元組碼物件
Class clazz = Class.forName(br.readLine());
//3.通過位元組碼物件建立物件【無參】
Test_exec tc = (Test_exec) clazz.newInstance();
//4.呼叫方法
tc.run();
}
}
1.8反射之動態代理【很重要】
第一步:建立一個User介面;
通過UserImpl實現該介面
public interface User {
public void add();
public void delete();
}
public class UserImpl implements User {
@Override
public void add() {
System.out.println("新增功能");
}
@Override
public void delete() {
System.out.println("刪除功能");
}
}
第三步:建立MyInvocationHandler類實現InvocationHandler【實現代理】
/**
* @author ZHENG
* 動態代理
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("許可權校驗");
//執行被代理target的方法
method.invoke(target, args);
System.out.println("日誌記錄");
return null;
}
}
第四步:建立測試類,實現程式碼
public class Test {
public static void main(String[] args){
UserImpl ui = new UserImpl();
ui.add();
ui.delete();
System.out.println("===================");
/*
ui.getClass()獲取位元組碼檔案;
Proxy.newProxyInstance:接收的三個引數>>>>
* 第一個引數:所有類載入器;
* 第二個引數:所有介面
* 第三個引數:MyInvocationHandler這個類的子類物件
ui.getClass().getClassLoader()獲取類載入器
ui.getClass().getInterfaces()獲取所有的介面
*/
MyInvocationHandler m = new MyInvocationHandler(ui);
User u = (User) Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(),m);
u.add();
u.delete();
}
}
結果如下:
新增功能
刪除功能
===================
許可權校驗
新增功能
日誌記錄
許可權校驗
刪除功能
日誌記錄