Java 反射建立類的例項物件(預設構造方法和私有構造方法)
阿新 • • 發佈:2018-12-31
反射之建立類的例項物件
通過反射可以建立Class<?>
中”?”對應的型別的例項物件,眾所眾知,建立類物件,會呼叫構造方法,構造器可以有多個,預設構造方法,多引數構造方法等。
這裡演示,通過反射建立預設構造方法的例項物件,和帶引數的構造方法的例項物件。
案例實戰
1. 反射訪問預設構造方法,建立類例項物件
大概思路如下:
- 先在類中構建一個預設的構造方法
- 然後獲取到Class物件
- 通過
newInstance()
獲取到類例項物件。
2. 反射訪問Private修飾的帶引數構造方法,建立類例項物件
大概思路如下:
- 先在類中建立一個private修飾的帶引數的構造方法,
- 然後獲取Class物件
- 通過
getDeclaredConstructor(Class<?>... parameterTypes):
獲取到指定引數型別的構造方法 - 接下來呼叫
setAccessible(true)
繞過private修飾符檢查 - 最後呼叫
newInstance()
傳入相關引數,獲取到類例項物件。
這裡使用到了Constructor類,介紹一下其API:
getModifiers()
:獲取構造方法的修飾符,private或者public等。getParameterTypes()
:獲取到構造方法中引數的型別toString()
:獲取到構造方法中資訊newInstance(Object ... initargs)
3. 編寫程式碼如下,進行驗證
package com.xingen.classdemo;
public class ClassTest2 {
private String name;
private String work;
/**
* 構建一個預設的構造方法
*/
public ClassTest2() {
}
/**
* 構建有引數構造方法
*
* @param name
* @param work
*/
private ClassTest2 (String name, String work) {
this.name = name;
this.work = work;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getWork() {
return work;
}
public void setWork(String work) {
this.work = work;
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("name: ");
stringBuffer.append(getName());
stringBuffer.append("\n");
stringBuffer.append("work: ");
stringBuffer.append(getWork());
return stringBuffer.toString();
}
/**
* 通過Class類的newInstance()建立一個類的例項物件,
* 該類必須要要有一個預設的構造方法,
* 反之,會丟擲一個 java.lang.InstantiationException
*/
public static ClassTest2 createDefaultInstance() {
ClassTest2 test = null;
try {
Class<ClassTest2> mClass = ClassTest2.class;
test = mClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return test;
}
/**
* 反射一個帶有引數的構造方法,傳入指定引數
* @param name
* @param work
* @return
*/
public static ClassTest2 createInstance(String name,String work){
ClassTest2 test = null;
try {
Class<ClassTest2> mClass = ClassTest2.class;
Constructor<ClassTest2> constructor= mClass.getDeclaredConstructor(String.class,String.class);
//設定允許訪問,防止private修飾的構造方法
constructor.setAccessible(true);
test= constructor.newInstance(name,work);
}catch (Exception e){
e.printStackTrace();
}
return test;
}
}
呼叫程式主入口,執行測試。
package com.xingen.classdemo;
public class Client {
public static void main(String[] args) {
useClassInstance();
useClassInstance("xinGen","Java Developer");
}
/**
* 使用Class類例項物件:去建立另外一個類的物件
*/
public static void useClassInstance(){
ClassTest2 test2=ClassTest2.createDefaultInstance();
test2.setName("xinGen");
test2.setWork("Android Library Developer");
System.out.println("使用反射建立一個類的物件 \n"+test2.toString());
}
/**
* 使用Class類例項物件:去建立另外一個類的物件,且傳入構造引數
*
* @param name
* @param work
*/
public static void useClassInstance(String name,String work){
ClassTest2 test2= ClassTest2.createInstance(name,work);
System.out.println("使用反射建立一個類的物件 \n"+test2.toString());
}
}
控制檯輸出結果:
使用反射建立一個類的物件
name: xinGen
work: Android Library Developer
使用反射建立一個類的物件
name: xinGen
work: Java Developer
一點思考:
通過反射可以繞過檢查是否被Private修飾,訪問到構造方法,因此也可以突破常見的單例模式,因此需要針對性處理。單例模式如何預防反射和反序列化攻擊,請找度娘。