說到Java
反射
,必須先把 Java 的位元組碼搞明白了,也就是
Class
, 大
Class
在之前的文章中,我們知道了Java的大
Class
就是類的位元組碼,就是一個普通的類,裡面儲存的是類的資訊,還不太明白Java的大
Class
的,可以先看一下之前的文章 一篇文章徹底搞懂Java的大Class到底是什麼
先想一個問題
1. 給我們一個類,我們如何使用?
這還不簡單,通過這個類,建立一個類的對象,再通過這個對象,調用類的方法或者屬性
比如有一個類叫
Student
, 裡面有一個
name
字段和一個
age
字段,還有3個方法, 源碼如下:
package com.model;
public class Student {
private String name;
private int age;
public Student(){
}
public Student(String name,int age){
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;
}
public void show(){
System.out.println("name=" + this.name + " age=" + this.age);
}
}
上面的代碼很簡單,應該都能看懂,我們以這個
Student
類來實驗
回到上面的問題:如何使用這個類? ,代碼如下:
//1 建立一個對象
Student s = new Student();
//2 調用對象的方法
s.setName("李雷");
s.setAge(23);
//3 列印一下
s.show();
列印的結果下:
name=李雷 age=23
上面就是和
反射
相反的通過正常的方式建立一個類的對象,然後通過對象調用類的方法
其實我們還可以根據類的位元組碼來建立對象,然後調用類的方法
也就是通過某個類的 Class ,來建立對象,然後調用類的方法
2. 如何擷取類的 Class 呢?
有
3
個方法,以
Student
為例,示範如下:
第一種:通過 Class.forName("com.model.Student") 來擷取Student的 Class
代碼如下:
Class cls = Class.forName("com.model.Student");
第二種:通過 Student.class
Class cls = Student.class
第三種:通過類的對象來擷取,調用類的對象的 getClass()方法
Student s = new Student();
Class cls = s.getClass()
以上就是三種方法擷取一個類的
Class
的方法,必須要牢記,尤其是前 2 個,用的最多
3. 如何通過 Class
來建立對象,進而來調用類的方法或者屬性呢?
Class
- 第一步:擷取類的 Class 對象
- 第二步:擷取對應的方法的位元組碼
以及 構造函數的位元組碼Method
,或者字段的位元組碼Constructor
Field
- 第三步:通過
的Constructor
方法生成一個類的對象newInstance()
- 第四步:通過調用
的Method
方法完成調用類的代碼invoke()
代碼示範如下:
//第一步:擷取Student的 Class 對象,即Student的位元組碼
Class cls = Class.forName("com.model.Student");
//第二步:擷取無參的構造方法的位元組碼,當然也可以擷取有參的
Constructor constructor = cls.getConstructor();
//第三步:調用構造函數的位元組碼對象的 newInstance 方法建立 Student的對象 obj
Object obj = constructor.newInstance();
//第四步:擷取 setName 方法的位元組碼,注意參數傳方法的名字以及方法中參數的位元組碼
// 擷取了setName的位元組碼 method,調用方法必須要有一個對象,是以上面的obj對象就是用來此處的
// 一定要傳進行
Method method = cls.getMethod("setName", String.class);
method.invoke(obj,"待兔");
//和上面類似,隻不過這次 getMethod 的第二個參數傳的是 int.class
//因為第二個參數是int類型
Method method1 = cls.getMethod("setAge", int.class);
method1.invoke(obj,23);
//和上面類似 ,隻不過 show()方法是無參的,是以 getMethod 隻需要傳方法的名字"show" 即可
Method showMethod = cls.getMethod("show");
//最後:調用showMethod方法,通過調用showMethod的invoke方法,裡面傳入前面建立的obj對象
//就達到了調用對象的show方法
showMethod.invoke(obj);
通過上面的代碼示範可以看出,在不知道
Student
類型的情況下,我們隻需要知道
Student
類的全類名(包名+類名)
也就是
com.model.Student
,就可以擷取到
Student
類的
和直接通過
Student s = new Student(); s.show();
這種方法不一樣的是,上面是在運作時通過字元串值知道要運作的類是
com.model.Student
是以,反射就是在運作的時候 ,才知道這個類是什麼,并且可以在運作的時候 ,擷取這個類的完整資訊,并調用對應的方法
4. 常用的反射API
4.1 在反射中,要擷取一個類或調用一個類的方法,我們首先需要擷取到該類的 Class 對象
擷取Class對象有三種方法,上面已經講過,這裡再次貼出來,加深印象
- 使用 Class.forName 靜态方法,前提是你知道類的全類名
,其實這種方法就是加載類的Class cls = Class.forName("com.model.Student");
- 使用類的 .class 方法
不過這種方法,隻适合在編譯時就知道操作的 ClassClass cls = Student.class
- 使用類對象的 getClass() 方法。
Student s = new Student();
Class cls = s.getClass()
4.2 擷取所有類的的方法
可以通過 Class對象 getMethods()或者 cls.getDeclaredMethods() 來擷取所有的方法的位元組碼
兩者的差別是:getMethods()擷取的方法包括父類的,getDeclaredMethods() 擷取的是子類的
示範 getMethods()
Method[] methods = cls.getMethods();
for (Method m : methods) {
System.out.println(m.getName());
}
輸出出下:
getName
setName
setAge
show
getAge
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
可以看到,輸出了很多父類中的方法(Object類中的方法)
再來看一下 getDeclaredMethods() 方法
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m.getName());
}
輸出如下:
getName
setName
setAge
show
getAge
可以看到,隻有子類自己的方法,并沒有父類的方法
5. 通過反射建立類的對象需要注意的點
上面我們通過
Constructor
對象的
newInstance()
方法,來建立對象
其實還有一種方法,也可以使用
Class
對象的
newInstance()
5.1 第一種:通過 Class 對象 newInstance()
方法
newInstance()
Class cls = Class.forName("com.model.Student");
Student obj = (Student) cls.newInstance();
5.2 第二種:通過 Constructor 對象的 newInstance() 方法
//第一步:擷取Student的 Class 對象,即Student的位元組碼
Class cls = Class.forName("com.model.Student");
//第二步:擷取無參的構造方法的位元組碼,當然也可以擷取有參的
Constructor constructor = cls.getConstructor();
//第三步:調用構造函數的位元組碼對象的 newInstance 方法建立 Student的對象 obj
Object obj = constructor.newInstance();
::: warning
通過 Constructor 對象建立類對象可以選擇特定構造方法,而通過 Class 對象則隻能使用預設的無參數構造方法。
下面的代碼就調用了一個有參數的構造方法進行了類對象的初始化。
:::
Class cls = Class.forName("com.model.Student");
Constructor constructor = cls.getConstructor(String.class,int.class);
Object obj = constructor.newInstance("tom",23);
通過上面的講解,應該對反射的用法有了個大緻的了解了,Class有很多方法,感興趣的可以自己寫個
helloworld
調試一下
不過怎麼說,還是要先弄明白
Class
到底是什麼,知道了
Class
的本質 ,再來看反射,就很容易了
還不太明白的一定要看看下面的文章
一篇文章徹底搞懂Java的大Class到底是什麼