Java入門系列-27-反射
我們們可能都用過 Spring AOP ,底層的實現原理是怎樣的呢?
反射常用於編寫工具,企業級開發要用到的 Mybatis、Spring 等框架,底層的實現都用到了反射。能用好反射,就能提高我們編碼的核心能力。
反射機制
JAVA反射機制是在執行狀態中,對於任意一個實體類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性。
作用:
- 在執行時判斷任意一個物件所屬的類
- 在執行時構造任意一個類的物件
- 在執行時判斷任意一個類所具有的成員變數和方法
- 在執行時呼叫任意一個物件的成員變數和方法
- 生成動態代理
常用的類:
- java.lang.Class:代表一個類
- java.lang.reflect.Method:代表類的方法
- java.lang.reflect.Field:代表類的成員變數
- java.lang.reflect.Constructor:代表類的構造方法
Class 類
Class 類的例項表示正在執行的 Java 應用程式中的類和介面,Class 沒有公共構造方法,Class 物件是在載入類時由 Java 虛擬機器及通過呼叫類載入器中的 defineClass 方法自動構造的。
- 一個類在 JVM 中只會有一個 Class 例項
- 一個 Class 物件對應的是一個載入到 JVM 中的一個 .class 檔案
- 每個類的例項都會記得自己是由哪個 Class 例項所生成
- 通過 Class 可以完整地得到一個類中的完整結構
獲取 Class 物件
獲取 Class 物件有4種方式,前三種比較常用。
首先建立一個類用於測試
package com.jikedaquan.reflection;
public class User {
private int id;
private String username;
private String password;
public User() {
}
public User(int id, String username, String password) {
super();
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void show() {
System.out.println("Hello");
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
}
}
編寫測試
package com.jikedaquan.reflection;
public class GetClass {
public static void main(String[] args) {
//方法1
try {
Class clz1=Class.forName("com.jikedaquan.reflection.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("找不到指定類");
}
//方法2
Class clz2=User.class;
//方法3
User user=new User();
Class clz3=user.getClass();
//方法4 類的載入器
try {
Class clz4=GetClass.class.getClassLoader().loadClass("com.jikedaquan.reflection.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("找不到指定類");
}
}
}
方法1語法:
Class Class物件 = Class.forName(包名+類名);
方法2語法:
Class Class物件 = 類名.class;
方法3語法:
Class Class物件 = 物件.getClass();
getClass() 方法是從 Object 類中繼承過來的
獲取類的結構
Class 類常用方法
方法名稱 | 說明 |
---|---|
Annotation[] getAnnotations() | 返回此元素上存在的所有註解 |
Constructor getConstructor(Class<?>… parameterTypes) | 獲取指定引數的建構函式 |
Constructor<?>[] getConstructors() | 返回包含的公有構造方法 |
Constructor<?>[] getDeclaredConstructors() | 返回所有構造方法 |
Field getDeclaredField(String name) | 返回一個 Field 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告欄位 |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 根據方法名和引數獲取方法物件 |
API 中可以看到有兩種獲取結構的方式:getDeclaredXxx()和getXxx();getDeclaredXxx()可以獲取所有包括私有的
獲取類的結構
package com.jikedaquan.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class GetClassStruct {
public static void main(String[] args) {
try {
Class clz=Class.forName("com.jikedaquan.reflection.User");
System.out.println("===========構造===========");
//獲取構造方法
Constructor[] cons=clz.getDeclaredConstructors();
for (Constructor constructor : cons) {
System.out.println(constructor);
}
//獲取欄位
System.out.println("===========欄位===========");
Field[] fields=clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//獲取方法
System.out.println("===========方法===========");
Method[] methods=clz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
//獲取父類
System.out.println("===========父類===========");
Class supperClass=clz.getSuperclass();
System.out.println(supperClass.getName());
//獲取實現的介面
System.out.println("===========介面===========");
Class[] interfaces=clz.getInterfaces();
for (Class interf : interfaces) {
System.out.println(interf);
}
//獲取註解
System.out.println("===========註解===========");
Annotation[] annotations=clz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
呼叫類的指定方法、屬性
獲取構造方法並例項化物件
注意:jdk1.9棄用此方式例項化物件Object obj=clz.newInstance();
通過反射獲取有參或無參構造後方可例項化化物件
package com.jikedaquan.reflection;
import java.lang.reflect.Constructor;
public class CallConstructor {
public static void main(String[] args) {
//獲取User 的 Class
Class<User> clz=User.class;
//獲取無參構造方法並例項化
try {
//getConstructor()方法不傳參即無參
Constructor<User> constructor=clz.getConstructor();
User user=constructor.newInstance();
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
//獲取有參構造方法並例項化
try {
Constructor<User> constructor=clz.getConstructor(int.class,String.class,String.class);
User user=constructor.newInstance(18,"張三","abc123");
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
}
}
獲取指定構造方法時,第二個引數為動態引數,不填寫即獲取無參構造方法,填寫指定個數和指定型別.class可獲取對應方式的構造方法。
呼叫類中的方法
package com.jikedaquan.reflection;
import java.lang.reflect.Method;
public class CallMethod {
public static void main(String[] args) {
//獲取User 的 Class
Class<User> clz=User.class;
//獲取無參方法 show
try {
Method method=clz.getMethod("show");
//執行clz中的方法
method.invoke(clz.getConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
//獲取一個引數為String的方法
try {
Method method=clz.getMethod("setUsername", String.class);
//反射例項化物件
User user=clz.getConstructor().newInstance();
//執行這個物件的方法
method.invoke(user, "反射");
//測試結果
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
}
}
如果有多個引數,獲取方法:
getMethod("方法名稱",引數1.class,引數2.class,引數3.class)
多個引數執行時:
method.invoke(物件,引數1,引數2,引數3);
動態代理
動態代理是指客戶通過代理類來呼叫其他物件的方法,並且是在程式執行時根據需要建立目標類的代理物件。
原理:
使用一個代理將物件包裝起來,然後用該代理物件取代原物件,任何對原始物件的呼叫都要通過dialing,代理物件決定是否以及何時將方法呼叫轉到原始物件上。
生活中海外代購其實就用到了代理,你可能不方便出國,但是代購可以,最終幫你完成購買行為。
以代購為例子完成靜態代理
package com.jikedaquan.reflection;
//購買介面(約定)
interface Buy{
void buyProduct();
}
//被代理的
class Customer implements Buy{
@Override
public void buyProduct() {
System.out.println("購買商品");
}
}
//代理
class ProxyBuy implements Buy{
private Customer customer;
public ProxyBuy(Customer customer) {
this.customer=customer;
}
@Override
public void buyProduct() {
System.out.println("代理:出國");
//被代理的物件的行為
customer.buyProduct();
System.out.println("代理:回國");
}
}
public class TestStaticProxy {
public static void main(String[] args) {
Customer customer=new Customer();
ProxyBuy proxyBuy=new ProxyBuy(customer);
proxyBuy.buyProduct();
}
}
那麼動態代理意味著不能只代理 Customer 類的行為,還可以代理其他類的行為
package com.jikedaquan.reflection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//工廠介面
interface Factory{
void product();
}
//電腦工廠
class ComputerFactory implements Factory{
@Override
public void product() {
System.out.println("生產電腦");
}
}
//動態代理處理器
class MyInvocationHandler implements InvocationHandler{
//要被代理的物件
private Object proxyObj;
//產生代理物件
public Object bind(Object proxyObj) {
this.proxyObj=proxyObj;
return Proxy.newProxyInstance(
proxyObj.getClass().getClassLoader(),
proxyObj.getClass().getInterfaces(),
this
);
}
//代理物件實際執行的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理:收收費");
Object result=method.invoke(proxyObj, args);
System.out.println("代理:代理完成");
return result;
}
}
public class TestDynamicProxy {
public static void main(String[] args) {
//建立代理物件生產器
MyInvocationHandler invocationHandler=new MyInvocationHandler();
//建立要被代理的物件
ComputerFactory computerFactory=new ComputerFactory();
//生產代理物件
Object factoryProxy=invocationHandler.bind(computerFactory);
Factory factory=(Factory) factoryProxy;
factory.product();
//建立另一個要被代理的物件(上個示例靜態代理的物件和介面)
Customer customer=new Customer();
//生產代理物件
Object buyProxy=invocationHandler.bind(customer);
Buy buy=(Buy) buyProxy;
buy.buyProduct();
}
}
在 main 方法中,建立了一個 MyInvocationHandler 物件,通過 bind 方法可以傳入任意要被代理的物件,實現了動態。
重點來了,拿好小本子筆記!!!!!
實現動態代理的步驟:
1.建立要被代理的類的介面
2.建立要被代理的類實現類
3.建立代理物件處理器(MyInvocationHandler),實現 InvocationHandler 介面
4.編寫生產代理物件的方法,方法內呼叫 Proxy.newInstance() 方法,返回代理物件
5.重寫 InvocationHandler 的 invoke 方法
6.測試:建立代理物件生產器,生產代理物件
相關文章
- Java 從入門到精通-反射機制Java反射
- Java基礎系列—Java反射Java反射
- 反射快速入門反射
- Java入門系列之finalJava
- Java入門系列之重寫Java
- Java反射詳解:入門+使用+原理+應用場景Java反射
- Java入門系列-16-繼承Java繼承
- Java入門系列-10-陣列Java陣列
- Java入門系列-22-IO流Java
- Java入門系列-06-運算子Java
- Java學習筆記系列-反射Java筆記反射
- 探索JAVA系列(一)探祕Java反射(Reflect)Java反射
- Java jvm 類載入 反射JavaJVM反射
- Golang反射技術初始入門Golang反射
- Java入門系列-04-java中的變數Java變數
- Java入門到實踐系列(1)——Java簡介Java
- Java入門系列-12-成員方法Java
- Java入門系列-13-String 和 StringBufferJava
- Java入門系列-11-類和物件Java物件
- Java教程:nacos入門系列之配置中心Java
- Java春招面試複習:Java反射的入門到實踐,再到原理Java面試反射
- Java入門系列-14-深入類和物件Java物件
- Java入門系列-09-迴圈結構Java
- Java入門系列-08-選擇結構Java
- Java入門系列-21-多執行緒Java執行緒
- Java入門系列-07-從控制檯中接收輸入Java
- shell入門系列
- 【Java 反射學習】Java 反射基礎Java反射
- [Java 反射學習] Java 反射基礎Java反射
- Java 反射Java反射
- Java——反射Java反射
- Java反射Java反射
- Go系列之反射Go反射
- Java反射—初探反射基礎Java反射
- Java NIO.2系列文章之非同步通道API入門Java非同步API
- Java入門系列之訪問修飾符作用範圍Java
- Java反射詳解篇--一篇入魂Java反射
- JAVA入門程式Java