java 反射 new class_Java高级特性-反射:不写死在代码,还怎么 new 对象?
反射是 Java 的一個(gè)高級(jí)特性,大量用在各種開源框架上。
在開源框架中,往往以同一套算法,來應(yīng)對(duì)不同的數(shù)據(jù)結(jié)構(gòu)。比如,Spring 的依賴注入,我們不用自己 new 對(duì)象了,這工作交給 Spring 去做。
然而,我們要 new 一個(gè)對(duì)象,就得寫在代碼上。但 Spring 肯定猜不到我們的類叫什么名字,那 Spring 又是怎么把對(duì)象給 new 出來的呢?
這就離不開反射。
反射的意義與作用
Java 有兩種操作類的方式,分別是:非反射、反射。
先來說第一種方式,非反射。
非反射,就是根據(jù)代碼,靜態(tài)地操作類。比如,下面這段代碼:public class Application {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)用戶對(duì)象
ClientUser client = new ClientUser();
}
}
這個(gè) main() 方法很簡(jiǎn)單,就是創(chuàng)建一個(gè)用戶對(duì)象。整個(gè)過程是這樣的,在 JVM 運(yùn)行前,你必須先想好要?jiǎng)?chuàng)建哪些對(duì)象,然后寫在代碼上,最后你運(yùn)行 main() 方法,JVM 給你創(chuàng)建一個(gè)用戶對(duì)象。
簡(jiǎn)單來說,你寫好代碼,扔給 JVM 運(yùn)行,運(yùn)行完就沒了。
在這種情況下,程序員必須控制一切,創(chuàng)建什么對(duì)象得提前寫死在代碼上。比如,我要多創(chuàng)建一個(gè)商戶對(duì)象,那就得改代碼:public class Application {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)用戶對(duì)象
ClientUser client = new ClientUser();
// 創(chuàng)建一個(gè)商戶對(duì)象
ShopUser shop = new ShopUser();
// 省略無數(shù) new 操作
}
}
如果按照這種做法,只要需求一變,程序員就得改代碼,工作效率很低。比如說,你碰上復(fù)雜些的項(xiàng)目,不光得創(chuàng)建對(duì)象,還得 set 成員變量。這樣一來,每新加一個(gè)對(duì)象,你就得改一堆代碼,遲早得累死。
那這些工作能簡(jiǎn)化嗎?
這要用到第二種操作類的方式,反射。反射是一種動(dòng)態(tài)操作類的機(jī)制。比如,我要?jiǎng)?chuàng)建一堆對(duì)象,那不用提前寫死在代碼,而是放在配置文件或者數(shù)據(jù)庫上,等到程序運(yùn)行的時(shí)候,再讀取配置文件創(chuàng)建對(duì)象。
還是上面的代碼,經(jīng)過反射的改造,就變成這個(gè)樣子:public class Application {
// 模擬配置文件
private static Set configs = new HashSet<>();
static {
configs.add("com.jiarupc.reflection.ShopUser");
configs.add("com.jiarupc.reflection.ClientUser");
// 省略無數(shù)配置
}
public static void main(String[] args) throws Exception {
// 讀取配置文件
for (String config : configs) {
// 通過配置文件,獲取類的Class對(duì)象
Class clazz = Class.forName(config);
// 創(chuàng)建對(duì)象
Object object = clazz.newInstance();
System.out.println(object);
}
}
}
當(dāng)你運(yùn)行 main() 方法的時(shí)候,程序會(huì)先讀取配置文件,然后根據(jù)配置文件創(chuàng)建對(duì)象。用了反射后,你有沒有發(fā)現(xiàn),工作變輕松了。一旦新加對(duì)象,我們只要加一行配置文件,不用動(dòng)其它地方。
看到這兒,你是不是想起某些開源框架?比如,Spring 的依賴注入。// 加上一行注解,Spring 就接管這個(gè)類的創(chuàng)建工作
@Service
public class UserService {
// 省略業(yè)務(wù)代碼...
}
你在某個(gè)類上加一行注解(這相當(dāng)于加一行配置),Spring 就幫你接管這個(gè)類,你不用操心怎么創(chuàng)建對(duì)象了。而 Spring 之所以能接管你這個(gè)類,是因?yàn)槔昧?Java 的反射。
簡(jiǎn)單來說,我們?nèi)绻煤梅瓷?#xff0c;能減少大量重復(fù)的代碼。
接下來,我們來看看反射能做什么吧~
反射獲取類信息
如果你想操作一個(gè)類,那得知道這個(gè)類的信息。比如,有哪些變量,有哪些構(gòu)造器,有哪些方法...沒有這些信息,你連代碼都沒法寫,更別談反射了。
限于篇幅,我們主要講怎么獲取類的 Class 對(duì)象、成員變量、方法。
Class 對(duì)象只有 JVM 才能創(chuàng)建,里面有一個(gè)類的所有信息,包括:成員變量、方法、構(gòu)造器等等。
換句話說,創(chuàng)建 Class 對(duì)象是 JVM 的事,我們不用管。但想通過反射來操作一個(gè)類,得先拿到這個(gè)類的 Class 對(duì)象,這有三種方式:1. Class.forName("類的全限定名")
2. 實(shí)例對(duì)象.getClass()
3. 類名.class
你可以看下面的代碼:public class User {
public static void main(String[] args) throws Exception {
// 1. Class.forName("類的全限定名")
Class clazz1 = Class.forName("com.jiarupc.reflection.User");
// 2. 實(shí)例對(duì)象.getClass()
User user = new User();
Class clazz2 = user.getClass();
// 3. 類名.class
Class clazz3 = ClientUser.class;
}
}
當(dāng)你通過這三種方式,拿到 Class 對(duì)象后,就可以用反射獲取類的信息了。
Field 對(duì)象代表類的成員變量。我們想拿到一個(gè)類的成員變量,可以調(diào)用 Class 對(duì)象的四個(gè)方法。1. Field getField(String name) - 獲取公共字段
2. Field[] getFields() - 獲取所有公共字段
3. Field getDeclaredField(String name) - 獲取成員變量
4. Field[] getDeclaredFields() - 獲取所有成員變量
我們嘗試下獲取所有成員變量,代碼邏輯是這樣的:通過 User 類的全限定名,獲取 Class 對(duì)象,然后調(diào)用 getDeclaredFields() 方法,拿到 User 類的全部成員變量,最后把變量名、類型輸出到控制臺(tái)。public class User {
// 唯一標(biāo)識(shí)
private Long id;
// 用戶名
private String username;
public static void main(String[] args) throws Exception {
// 獲取類的 Class 對(duì)象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 獲取類的所有成員變量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String msg = String.format("變量名:%s, 類型:%s", field.getName(), field.getType());
System.out.println(msg);
}
}
}
Method 對(duì)象代表類的方法。要拿到一個(gè)類的方法,Class 對(duì)象同樣提供了四個(gè)方法:1. Method getMethod(String name, Class[] params) - 通過方法名、傳入?yún)?shù),獲取公共方法
2. Method[] getMethods() - 獲取所有公共方法
3. Method getDeclaredMethod(String name, Class[] params) - 通過方法名、傳入?yún)?shù),獲取任何方法
4. Method[] getDeclaredMethods() - 獲取所有方法
同樣的,我們嘗試下獲取所有方法,先通過 User 類的全限定名,獲取 Class 對(duì)象,然后調(diào)用 getDeclaredMethods() 方法,拿到 User 類的全部成員方法,最后把方法名、形參數(shù)量輸出到控制臺(tái)。public class User {
// 唯一標(biāo)識(shí)
private Long id;
// 用戶名
private String username;
public static void main(String[] args) throws Exception {
// 獲取類的 Class 對(duì)象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 獲取類的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String msg = String.format("方法名:%s, 形參數(shù)量:%s", method.getName(), method.getParameterCount());
System.out.println(msg);
}
}
}
看到這兒,你應(yīng)該能知道:怎么通過反射獲取類的信息。
首先,獲取類的 Class 對(duì)象有三種方式;然后,獲取類的成員變量,這對(duì)應(yīng)著 Field 對(duì)象;最后,獲取類的方法,這對(duì)應(yīng)著 Method 對(duì)象。
然而,反射不止能拿到類的信息,還能操作類。
反射操作類
反射能玩出很多花樣,但我認(rèn)為最重要的是:創(chuàng)建對(duì)象和調(diào)用方法。
創(chuàng)建對(duì)象是一切的前提。對(duì)反射來說,如果沒有創(chuàng)建對(duì)象,那我們只能看看這個(gè)類的信息。比如,有什么成員變量,有什么方法之類的。而如果你想操作一個(gè)類,那么第一步就是創(chuàng)建對(duì)象。
你想要?jiǎng)?chuàng)建對(duì)象,必須調(diào)用類的構(gòu)造器。這分為兩種情況,最簡(jiǎn)單的是:你寫了一個(gè)類,但沒有寫構(gòu)造器,那這個(gè)類會(huì)自帶一個(gè)無參的構(gòu)造器,這就好辦了。public class User {
// 唯一標(biāo)識(shí)
private Long id;
// 用戶名
private String username;
public static void main(String[] args) throws Exception {
// 獲取類的 Class 對(duì)象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 創(chuàng)建對(duì)象
Object object = clazz.newInstance();
System.out.println(object);
}
}
我們先獲取類的 Class 對(duì)象,然后調(diào)用 newInstance()。
但還有一種情況,我不用 Java 自帶的構(gòu)造器,而是自己寫。這種情況會(huì)復(fù)雜一些,你得指定傳入?yún)?shù)的類型,先拿到構(gòu)造器,再調(diào)用 newInstance() 方法。public class User {
// 唯一標(biāo)識(shí)
private Long id;
// 用戶名
private String username;
// 構(gòu)造器1
public User(Long id) {
this.id = id;
this.username = null;
}
// 構(gòu)造器2
public User(Long id, String username) {
this.id = id;
this.username = username;
}
public static void main(String[] args) throws Exception {
// 獲取類的 Class 對(duì)象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 通過傳入?yún)?shù),獲取構(gòu)造器,再創(chuàng)建對(duì)象
Constructor constructor = clazz.getConstructor(Long.class, String.class);
Object object = constructor.newInstance(1L, "jiarupc");
System.out.println(object);
}
}
我們要在一開始就設(shè)置 id 和 username,那么你得傳入?yún)?shù)的類型,先找到構(gòu)造器2-constructor;然后,傳入 id 和 username 到 constructor.newInstance() 方法,就能得到一個(gè)用戶對(duì)象。
當(dāng)拿到構(gòu)造器,并創(chuàng)建好對(duì)象后,我們就可以調(diào)用對(duì)象的方法了。
調(diào)用對(duì)象的方法分為兩步:第一步,找到方法;第二步,調(diào)用方法。這聽起來是非常簡(jiǎn)單,事實(shí)上也非常簡(jiǎn)單。你可以看下面的代碼。public class User {
// 唯一標(biāo)識(shí)
private Long id;
// 用戶名
private String username;
// ..忽略 set/get 方法
public static void main(String[] args) throws Exception {
// 獲取類的 Class 對(duì)象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 創(chuàng)建對(duì)象
Object object = clazz.newInstance();
System.out.println(object);
// 通過方法名、傳入?yún)?shù),找到方法-setUsername
Method method = clazz.getMethod("setUsername", String.class);
// 調(diào)用 object 對(duì)象的 setUsername() 方法
method.invoke(object, "JerryWu");
// 輸出所有成員變量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String msg = String.format("變量名:%s, 變量值:%s", field.getName(), field.get(object));
System.out.println(msg);
}
}
}
我們通過方法名-setUsername、參數(shù)類型-String,找到 setUsername 方法;然后,傳入?yún)?shù)-username 到 method.invoke(),執(zhí)行setUsername 方法;最后,輸出所有成員變量,驗(yàn)證一下結(jié)果。
寫在最后
反射是一種動(dòng)態(tài)操作類的機(jī)制,它有兩個(gè)用處。
第一個(gè)用處,通過反射,我們可以拿到一個(gè)類的信息,包括:成員變量、方法、構(gòu)造器等等。
第二個(gè)用處,通過反射,我們可以操作一個(gè)類,包括:創(chuàng)建對(duì)象、調(diào)用對(duì)象的方法、修改對(duì)象的成員變量。
因?yàn)榭蚣芤酝惶姿惴?#xff0c;來應(yīng)對(duì)不同的數(shù)據(jù)結(jié)構(gòu)。所以,開源框架大量用到了反射。比如,Spring 的依賴注入就離不開反射。
總結(jié)
以上是生活随笔為你收集整理的java 反射 new class_Java高级特性-反射:不写死在代码,还怎么 new 对象?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 顺产需要多少钱啊?
- 下一篇: java springmvc 数据库事务