Java反射

1. 反射機制是什麼

反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。java

2. 反射機制能作什麼

反射機制主要提供瞭如下功能: sass

  • 在運行時判斷任意一個對象所屬的類;app

  • 在運行時構造任意一個類的對象;eclipse

  • 在運行時判斷任意一個類所具備的成員變量和方法;ide

  • 在運行時調用任意一個對象的方法;函數

  • 生成動態代理。測試

3. 開始小例子了

1) 編寫一個英雄類,包含英雄的基本信息ui

public abstract class Hero implements Runnable{

    public static final String WARRIOR  = "Warrior";
    
    public static final String ASSASSIN  = "Assassin";
    
    public static final String WIZARD  = "Wizard";
    
    //HP
    private float hitPoint;
    
    //MP
    private float manaPoint;
    
    //攻擊力下限
    private float minAttack;
    
    //攻擊力上限
    private float maxAttack;
    
    //護甲
    private float armor;
    
    //移動速度
    private int speed;
    
    //級別
    private int level = 1;
    
    //力量
    private float power;
    
    //力量成長
    private float  powerGrow;
    
    //敏捷
    private float agile;
    
    //敏捷成長
    private float agileGrow;
    
    //智力
    private float intelligence;
    
    //智力成長
    private float intelligenceGrow;
    
    //Hero Name
    protected String name;
    
    //英雄職業 
    private String heroJob;
    
    //QWER選手
    public abstract void A();
    
    public abstract void Q();
    
    public abstract void W();
    
    public abstract void E();
    
    public abstract void R();
    
    public Hero(String name, String heroJob, float hitPoint
        ,float manaPoint, float minAttack, float maxAttack, float armor
        ,int speed, float power, float powerGrow
        ,float agile, float agileGrow, float intelligence
        ,float intelligenceGrow){
        
        this.name = name;
        this.heroJob = heroJob;
        this.hitPoint = hitPoint;
        this.manaPoint = manaPoint;
        this.minAttack = minAttack;
        this.maxAttack = maxAttack;
        this.armor = armor;
        this.speed = speed;
        this.power = power;
        this.powerGrow = powerGrow;
        this.agile = agile;
        this.agileGrow = agileGrow;
        this.intelligence = intelligence;
        this.intelligenceGrow = intelligenceGrow;
        
    }
    
    //打印基本信息
    public void showHeroInfo(){
        StringBuilder sb = new StringBuilder();
        sb.append("=========INFO========\n")
          .append("Hero Name : ").append(name).append("\n")
          .append("Level : ").append(level).append("\n")
          .append("HP : ").append(hitPoint).append("\n")
          .append("MP : ").append(manaPoint).append("\n")
          .append("ATK : ").append(minAttack).append("~").append(maxAttack).append("\n")
          .append("ARMOR : ").append(armor).append("\n")
          .append("=========INFO========\n");    
        System.out.println(sb.toString());
    }
    
    //升級
    private void levelUp(){
        if(level >= 25){
            System.out.println("已經升至滿級");
            return;
        }
        power += powerGrow;
        agile += agileGrow;
        intelligence += intelligenceGrow;
        //1點力量19點血
        hitPoint +=  powerGrow*19.0f;
        //1點智力13點魔
        manaPoint += intelligenceGrow*13.0f;
        //7點敏捷1點護甲
        armor += agileGrow/7.0f;
        
        growUpAttack();
        
        level++;
        System.out.println("=================Level UP===============");
        
        showHeroInfo();
    }
    
    //提高攻擊力
    private void growUpAttack(){
        switch(heroJob){
           case WARRIOR : minAttack += powerGrow;
                          maxAttack += powerGrow;
                          break;
           case ASSASSIN : minAttack += agileGrow; 
                           maxAttack += agileGrow;
                           break;
           case WIZARD : minAttack += intelligenceGrow;  
                         maxAttack += intelligenceGrow;
                         break;
        }
    }
    
    //實現升級線程方法
    @Override
    public void run(){
        while(level < 25){
            levelUp();
            try {
                //10秒升一級
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

2) 寫個光法繼承英雄類this

public class Ezalor extends Hero{
    
    public Ezalor(){
        super("光之守衛", Hero.WIZARD, 454f, 286f, 38f, 54f, 1.1f, 315
                ,14f, 1.8f, 15f, 1.6f, 22f, 2.8f );
    } 
    
    public Ezalor(String heroName){
        super(heroName, Hero.WIZARD, 454f, 286f, 38f, 54f, 1.1f, 315
                ,14f, 1.8f, 15f, 1.6f, 22f, 2.8f );
    } 

    @Override
    public void A() {
        System.out.println(name+" 攻擊");
    }

    @Override
    public void Q() {
        System.out.println(name+" 釋放了衝擊波");
    }

    @Override
    public void W() {
        System.out.println(name+" 釋放了法力流失");
    }

    @Override
    public void E() {
        System.out.println(name+" 釋放了查克拉魔法");
    }

    @Override
    public void R() {
        System.out.println(name+" 釋放了變身");
    }
}

 

 3) 經過類名建立光之守衛的實例 -- Class.forName()spa

public class MyTest {

    public static void main(String[] args) {
    
        Class<?> ezalorClazz = null;
        Ezalor ezalor = null;
        try {
            //根據類獲取類管理器,若是沒有這個類,會拋出ClassNotFoundException
            ezalorClazz = Class.forName("jeb.Ezalor");
            //根據類管理器建立實例,若是沒有默認的構造函數會拋出NoSuchMethodException
            ezalor = (Ezalor) ezalorClazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        //類管理器是單例的嗎 Ezalor.class == ezalor.getClass
        System.out.println("Is Class Manager Singleton : " 
                + ( ezalorClazz == ezalor.getClass()) );
        
        //看下初始屬性,放幾個技能
        ezalor.showHeroInfo();
        ezalor.Q();
        ezalor.W();
        ezalor.E();
        ezalor.R();
        
        getClassInfo(ezalorClazz);
    }

    //獲取類基本信息
    private static void getClassInfo(Class<?> clazz){
        StringBuilder sb = new  StringBuilder();
        
        //clazz.getName()獲取類名
        sb.append("Class Name : ").append(clazz.getName()).append("\n");
        
        //獲取父類,因爲Java只容許單向繼承,因此僅返回一個Class對象
        if(clazz.getSuperclass() != null){
            //若是有父類打印父類信息
            getClassInfo(clazz.getSuperclass());
        }
        
        System.out.println(sb.toString());
    }
}

輸出結果:

Is Class Manager Singleton : true
=========INFO========
Hero Name : 光之守衛
Level : 1
HP : 454.0
MP : 286.0
ATK : 38.0~54.0
ARMOR : 1.1
=========INFO========

光之守衛 釋放了衝擊波
光之守衛 釋放了法力流失
光之守衛 釋放了查克拉魔法
光之守衛 釋放了變身
Class Name : java.lang.Object

Class Name : jeb.Hero

Class Name : jeb.Ezalor

 

4) 獲取類的構造方法,並調用指定構造方法建立實例--getConstructor(Class<?>... arg0)

上面的例子利用了反射,經過光法的類名,調用默認的構造函數,建立了光法的實例。若是但願使用第二個構造函數建立一個有自定義名稱的光法應該怎樣呢?

private static void createMyEzalor(){
    try {
        Class<?> ezalorClazz = Class.forName("jeb.Ezalor");
        //經過參數類型,查找參數爲String的構造函數
        Class<?>[] parmType = new Class<?>[]{String.class};
        Constructor constructor = ezalorClazz.getConstructor(parmType);
        //經過制定的構造函數建立實例
        Ezalor ezalor = (Ezalor) constructor.newInstance("甘道夫");
        ezalor.showHeroInfo();
        
        //啓動一個線程開始打怪升級
        Thread tr = new Thread(ezalor);
        tr.start();
        
    } catch (ClassNotFoundException | NoSuchMethodException
            | SecurityException | InstantiationException 
            | IllegalAccessException | IllegalArgumentException 
            | InvocationTargetException e) {
        e.printStackTrace();
    }
}

輸出結果: 建立了名稱爲甘道夫的光法,並開始打怪升級

=========INFO========
Hero Name : 甘道夫
Level : 1
HP : 454.0
MP : 286.0
ATK : 38.0~54.0
ARMOR : 1.1
=========INFO========

=================Level UP===============
=========INFO========
Hero Name : 甘道夫
Level : 2
HP : 488.2
MP : 322.4
ATK : 40.8~56.8
ARMOR : 1.3285714
=========INFO========

=================Level UP===============
=========INFO========
Hero Name : 甘道夫
Level : 3
HP : 522.4
MP : 358.8
ATK : 43.6~59.6
ARMOR : 1.5571429
=========INFO========

....

4) 經過反射看看光法類有什麼函數--getDeclaredMethods

每10秒鐘升一級,做爲一個掛逼,來找找光法有什麼做弊的可能。

private static void getDeclaredMethods(Ezalor ezalor, Class<?> clazz){
    
    //獲取父類方法
    if(clazz.getSuperclass()!= null){
        getDeclaredMethods(ezalor, clazz.getSuperclass());
    }
    
    //獲取全部方法
    Method[] methods = clazz.getDeclaredMethods();
    StringBuilder sb = new  StringBuilder();
    for(Method method : methods){
        sb.append("=======================\n")
          .append("Method Belongs : ").append(clazz.getName()).append("\n")
          .append("Method Modifiers : ")
          //獲取方法修飾符
          .append(Modifier.toString(method.getModifiers())).append("\n")
          //獲取方法返回值
          .append("Return Type : ").append(method.getReturnType()).append("\n")
          .append("Method Name : ").append(method.getName()).append("\n");
        
        Parameter[] params = method.getParameters();
        
        if(params != null && params.length > 0){
            sb.append("Method Params : \n");
            for(Parameter param : params){
                sb.append("Param Name: ").append(param.getName()).append("\n")
                  .append("Param Type: ").append(param.getType().getName()).append("\n");
            }
        }
        
        sb.append("=======================\n");
    }
    System.out.println(sb.toString());
}

建立一個實例,調用這個方法

public static void main(String[] args){
    Ezalor ezalor = new Ezalor();
    getDeclaredMethods(ezalor, ezalor.getClass());
}

輸出結果:

... 省略掉了Object的方法 ...

=======================
Method Belongs : jeb.Hero
Method Modifiers : public
Return Type : void
Method Name : run
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : public abstract
Return Type : void
Method Name : E
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : public abstract
Return Type : void
Method Name : A
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : public abstract
Return Type : void
Method Name : R
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : public
Return Type : void
Method Name : showHeroInfo
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : public abstract
Return Type : void
Method Name : W
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : public abstract
Return Type : void
Method Name : Q
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : private
Return Type : void
Method Name : levelUp
=======================
=======================
Method Belongs : jeb.Hero
Method Modifiers : private
Return Type : void
Method Name : growUpAttack
=======================

=======================
Method Belongs : jeb.Ezalor
Method Modifiers : public
Return Type : void
Method Name : E
=======================
=======================
Method Belongs : jeb.Ezalor
Method Modifiers : public
Return Type : void
Method Name : A
=======================
=======================
Method Belongs : jeb.Ezalor
Method Modifiers : public
Return Type : void
Method Name : R
=======================
=======================
Method Belongs : jeb.Ezalor
Method Modifiers : public
Return Type : void
Method Name : W
=======================
=======================
Method Belongs : jeb.Ezalor
Method Modifiers : public
Return Type : void
Method Name : Q
=======================

輸出了Ezalor類及其父類的全部方法。

試試將getDeclaredMethods 換成 getMethods 有什麼區別?

getMethods 僅返回當前類的public方法,getDeclaredMethods返回當前類全部方法。

5) 開始做弊了 -- method.invoke(Object target, Object .. args)

=======================
Method Belongs : jeb.Hero
Method Modifiers : private
Return Type : void
Method Name : levelUp
=======================

咱們發現了下面這個方法,看着名字就像是升級。這個方法是private的,沒法經過實例直接調用。

可是反射依然是能夠暴力調用的 :

private static Method getLevelUpMethod(Ezalor ezalor, Class<?> clazz) 
        throws SecurityException{
    
    //查找名爲levelUp的方法
    Method lvlupMethod;
    try {
        lvlupMethod = clazz.getDeclaredMethod("levelUp", null);
    } catch (NoSuchMethodException e) {
        //子類沒有這個方法便查找父類
        if(clazz.getSuperclass() != null){
            lvlupMethod = getLevelUpMethod(ezalor, clazz.getSuperclass());
        }else{
            return null;
        }
    }
    return lvlupMethod;
}

private static void whoIsYourDaddy(Ezalor ezalor){
    try {
        //查找升級方法
        Method lvlupMethod = getLevelUpMethod(ezalor, ezalor.getClass());
        //反射調用方法25次
        for(int i = 0; i < 25; i++){
            lvlupMethod.invoke(ezalor, null);
        }
    } catch ( SecurityException | IllegalAccessException 
            | IllegalArgumentException | InvocationTargetException e) {
        System.out.println("Something Wrong !");
        e.printStackTrace();
    }
}

public static void main(String[] args){
    Ezalor ezalor = new Ezalor();
    whoIsYourDaddy(ezalor);
}

運行結果:

 Something Wrong !

 java.lang.IllegalAccessException: Class jeb.MyTest can not access a member of class jeb.Hero with modifiers "private"

因爲這個方法是private的,Java的訪問檢查控制,拋出了沒有權限調用方法的異常!

解決方法:

使用 method.setAccessible(true)來禁用訪問檢查控制。

修改whoIsYourDaddy代碼以下:

private static void whoIsYourDaddy(Ezalor ezalor){
    try {
        //查找升級方法
        Method lvlupMethod = getLevelUpMethod(ezalor, ezalor.getClass());
        //禁用訪問權限檢查
        lvlupMethod.setAccessible(true);
        //反射調用方法25次
        for(int i = 0; i < 25; i++){
            lvlupMethod.invoke(ezalor, null);
        }
    } catch ( SecurityException | IllegalAccessException 
            | IllegalArgumentException | InvocationTargetException e) {
        System.out.println("Something Wrong !");
        e.printStackTrace();
    }
}

運行結果:

...

=================Level UP===============
=========INFO========
Hero Name : 光之守衛
Level : 25
HP : 1274.7998
MP : 1159.6003
ATK : 105.200035~121.20006
ARMOR : 6.5857143
=========INFO========

已經升至滿級

是否是很炫酷,做弊升到了滿級。可是做爲一個開掛狂魔,不能止步於25級,有沒有什麼辦法能直接修改屬性值呢?

5) 做弊狂魔,直接獲取修改實例私有屬性--getDeclaredFields()

private static void whoIsYourGrandPa(Ezalor ezalor, Class<?> clazz) 
        throws IllegalArgumentException, IllegalAccessException{
    
    //獲取父類屬性
    if(clazz.getSuperclass()!= null){
        whoIsYourGrandPa(ezalor, clazz.getSuperclass());
    }
    
    //獲取全部屬性
    Field[] fields = clazz.getDeclaredFields();
    
    for(Field field : fields){
        switch (field.getName()){
           case "hitPoint" :  
           case "manaPoint" :  
           case "minAttack" :  
           case "maxAttack" :  
           case "armor" :  
               //禁用訪問權限檢查
               field.setAccessible(true);
               //設置屬性值
               field.set(ezalor, 9999f);
        }
    }
}

public static void main(String[] args){
    Ezalor ezalor = new Ezalor("暴走甘道夫");
    try {
        whoIsYourGrandPa(ezalor, ezalor.getClass());
        ezalor.showHeroInfo();
    } catch (IllegalArgumentException | IllegalAccessException e) {
        e.printStackTrace();
    }
}

輸出結果:

=========INFO========
Hero Name : 暴走甘道夫
Level : 1
HP : 9999.0
MP : 9999.0
ATK : 9999.0~9999.0
ARMOR : 9999.0
=========INFO========

試試將getDeclaredFields 換成 getFields有什麼區別?

與getDeclaredMehods/getMethods類似,getFields僅返回當前類的public屬性,getDeclaredFields返回當前類全部屬性。

使用getFields方法就不能獲得暴走甘道夫了。

6) 貼個完整的測試類

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

public class MyTest {

    public static void main(String[] args) {
    
        Class<?> ezalorClazz = null;
        Ezalor ezalor = null;
        try {
            //根據類獲取類管理器,若是沒有這個類,會拋出ClassNotFoundException
            ezalorClazz = Class.forName("jeb.Ezalor");
            //根據類管理器建立實例,若是沒有默認的構造函數會拋出NoSuchMethodException
            ezalor = (Ezalor) ezalorClazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        //類管理器是單例的嗎 Ezalor.class == ezalor.getClass
        System.out.println("Is Class Manager Singleton : " 
                + ( ezalorClazz == ezalor.getClass()) );
        
        //看下初始屬性,放幾個技能
        ezalor.showHeroInfo();
        ezalor.Q();
        ezalor.W();
        ezalor.E();
        ezalor.R();
        
        getClassInfo(ezalorClazz);
        //getMethods(ezalor, ezalorClazz);
        getDeclaredMethods(ezalor, ezalorClazz);
        //createMyEzalor();
    } 
  
    //獲取類基本信息
    private static void getClassInfo(Class<?> clazz){
        StringBuilder sb = new  StringBuilder();
        
        //clazz.getName()獲取類名
        sb.append("Class Name : ").append(clazz.getName()).append("\n");
        
        //獲取父類,因爲Java只容許單向繼承,因此僅返回一個Class對象
        if(clazz.getSuperclass() != null){
            //若是有父類打印父類信息
            getClassInfo(clazz.getSuperclass());
        }
        
        System.out.println(sb.toString());
    }
    
    private static void createMyEzalor(){
        try {
            Class<?> ezalorClazz = Class.forName("jeb.Ezalor");
            //經過參數類型,查找參數爲String的構造函數
            Class<?>[] parmType = new Class<?>[]{String.class};
            Constructor constructor = ezalorClazz.getConstructor(parmType);
            //經過制定的構造函數建立實例
            Ezalor ezalor = (Ezalor) constructor.newInstance("甘道夫");
            ezalor.showHeroInfo();
            
            //啓動一個線程開始打怪升級
            Thread tr = new Thread(ezalor);
            tr.start();
            
        } catch (ClassNotFoundException | NoSuchMethodException
                | SecurityException | InstantiationException 
                | IllegalAccessException | IllegalArgumentException 
                | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    
    
    private static void getMethods(Ezalor ezalor, Class<?> clazz){
        
        //獲取父類方法
        if(clazz.getSuperclass()!= null){
            getMethods(ezalor, clazz.getSuperclass());
        }
        
        //獲取public方法
        Method[] methods = clazz.getMethods();
        StringBuilder sb = new  StringBuilder();
        for(Method method : methods){
            sb.append("=======================\n")
              .append("Method Belongs : ").append(clazz.getName()).append("\n")
              .append("Method Modifiers : ")
              //獲取方法修飾符
              .append(Modifier.toString(method.getModifiers())).append("\n")
              .append("Return Type : ").append(method.getReturnType()).append("\n")
              .append("Method Name : ").append(method.getName()).append("\n");
            
            Parameter[] params = method.getParameters();
            
            if(params != null && params.length > 0){
                sb.append("Method Params : \n");
                for(Parameter param : params){
                    sb.append("Param Name: ").append(param.getName()).append("\n")
                      .append("Param Type: ").append(param.getType().getName()).append("\n");
                }
            }
            
            sb.append("=======================\n");
        }
        System.out.println(sb.toString());
    }
    
    private static void getDeclaredMethods(Ezalor ezalor, Class<?> clazz){
        
        //獲取父類方法
        if(clazz.getSuperclass()!= null){
            getDeclaredMethods(ezalor, clazz.getSuperclass());
        }
        
        //獲取全部方法
        Method[] methods = clazz.getDeclaredMethods();
        StringBuilder sb = new  StringBuilder();
        for(Method method : methods){
            sb.append("=======================\n")
              .append("Method Belongs : ").append(clazz.getName()).append("\n")
              .append("Method Modifiers : ")
              //獲取方法修飾符
              .append(Modifier.toString(method.getModifiers())).append("\n")
              //獲取方法返回值
              .append("Return Type : ").append(method.getReturnType()).append("\n")
              .append("Method Name : ").append(method.getName()).append("\n");
            
            Parameter[] params = method.getParameters();
            
            if(params != null && params.length > 0){
                sb.append("Method Params : \n");
                for(Parameter param : params){
                    sb.append("Param Name: ").append(param.getName()).append("\n")
                      .append("Param Type: ").append(param.getType().getName()).append("\n");
                }
            }
            
            sb.append("=======================\n");
        }
        System.out.println(sb.toString());
    }
    
    private static Method getLevelUpMethod(Ezalor ezalor, Class<?> clazz) 
            throws SecurityException{
        
        //查找名爲levelUp的方法
        Method lvlupMethod;
        try {
            lvlupMethod = clazz.getDeclaredMethod("levelUp", null);
        } catch (NoSuchMethodException e) {
            //子類沒有這個方法便查找父類
            if(clazz.getSuperclass() != null){
                lvlupMethod = getLevelUpMethod(ezalor, clazz.getSuperclass());
            }else{
                return null;
            }
        }
        return lvlupMethod;
    }
    
    private static void whoIsYourDaddy(Ezalor ezalor){
        try {
            //查找升級方法
            Method lvlupMethod = getLevelUpMethod(ezalor, ezalor.getClass());
            //禁用訪問權限檢查
            lvlupMethod.setAccessible(true);
            //反射調用方法25次
            for(int i = 0; i < 25; i++){
                lvlupMethod.invoke(ezalor, null);
            }
        } catch ( SecurityException | IllegalAccessException 
                | IllegalArgumentException | InvocationTargetException e) {
            System.out.println("Something Wrong !");
            e.printStackTrace();
        }
    }
    
    private static void whoIsYourGrandPa(Ezalor ezalor, Class<?> clazz) 
            throws IllegalArgumentException, IllegalAccessException{
        
        //獲取父類屬性
        if(clazz.getSuperclass()!= null){
            whoIsYourGrandPa(ezalor, clazz.getSuperclass());
        }
        
        //獲取全部屬性
        Field[] fields = clazz.getFields();
        
        for(Field field : fields){
            switch (field.getName()){
               case "hitPoint" :  
               case "manaPoint" :  
               case "minAttack" :  
               case "maxAttack" :  
               case "armor" :  
                   //禁用訪問權限檢查
                   field.setAccessible(true);
                   //設置屬性值
                   field.set(ezalor, 9999f);
            }
        }
    }
    
}

7) 最後

Class類中還提供了一些其餘的方法,用於獲取類/方法/屬性註解等功能。這裏不所有列出了。

過了打遊戲的年紀,懷念下那些年一塊兒打過的Dota :)

相關文章
相關標籤/搜索