1. 程式人生 > >Java 反射建立類的例項物件(預設構造方法和私有構造方法)

Java 反射建立類的例項物件(預設構造方法和私有構造方法)

反射之建立類的例項物件

通過反射可以建立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修飾,訪問到構造方法,因此也可以突破常見的單例模式,因此需要針對性處理。單例模式如何預防反射和反序列化攻擊,請找度娘。