Java反射

1. 反射机制是什么

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

2. 反射机制能做什么

反射机制主要提供了以下功能: 

  • 在运行时判断任意一个对象所属的类;

  • 在运行时构造任意一个类的对象;

  • 在运行时判断任意一个类所具有的成员变量和方法;

  • 在运行时调用任意一个对象的方法;

  • 生成动态代理。

3. 开始小例子了

1) 编写一个英雄类,包含英雄的基本信息

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========
")
          .append("Hero Name : ").append(name).append("
")
          .append("Level : ").append(level).append("
")
          .append("HP : ").append(hitPoint).append("
")
          .append("MP : ").append(manaPoint).append("
")
          .append("ATK : ").append(minAttack).append("~").append(maxAttack).append("
")
          .append("ARMOR : ").append(armor).append("
")
          .append("=========INFO========
");    
        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) 写个光法继承英雄类

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()

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("
");
        
        //获取父类,由于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("=======================
")
          .append("Method Belongs : ").append(clazz.getName()).append("
")
          .append("Method Modifiers : ")
          //获取方法修饰符
          .append(Modifier.toString(method.getModifiers())).append("
")
          //获取方法返回值
          .append("Return Type : ").append(method.getReturnType()).append("
")
          .append("Method Name : ").append(method.getName()).append("
");
        
        Parameter[] params = method.getParameters();
        
        if(params != null && params.length > 0){
            sb.append("Method Params : 
");
            for(Parameter param : params){
                sb.append("Param Name: ").append(param.getName()).append("
")
                  .append("Param Type: ").append(param.getType().getName()).append("
");
            }
        }
        
        sb.append("=======================
");
    }
    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("
");
        
        //获取父类,由于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("=======================
")
              .append("Method Belongs : ").append(clazz.getName()).append("
")
              .append("Method Modifiers : ")
              //获取方法修饰符
              .append(Modifier.toString(method.getModifiers())).append("
")
              .append("Return Type : ").append(method.getReturnType()).append("
")
              .append("Method Name : ").append(method.getName()).append("
");
            
            Parameter[] params = method.getParameters();
            
            if(params != null && params.length > 0){
                sb.append("Method Params : 
");
                for(Parameter param : params){
                    sb.append("Param Name: ").append(param.getName()).append("
")
                      .append("Param Type: ").append(param.getType().getName()).append("
");
                }
            }
            
            sb.append("=======================
");
        }
        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("=======================
")
              .append("Method Belongs : ").append(clazz.getName()).append("
")
              .append("Method Modifiers : ")
              //获取方法修饰符
              .append(Modifier.toString(method.getModifiers())).append("
")
              //获取方法返回值
              .append("Return Type : ").append(method.getReturnType()).append("
")
              .append("Method Name : ").append(method.getName()).append("
");
            
            Parameter[] params = method.getParameters();
            
            if(params != null && params.length > 0){
                sb.append("Method Params : 
");
                for(Parameter param : params){
                    sb.append("Param Name: ").append(param.getName()).append("
")
                      .append("Param Type: ").append(param.getType().getName()).append("
");
                }
            }
            
            sb.append("=======================
");
        }
        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 :)

原文地址:https://www.cnblogs.com/Jeb-Sun/p/6838516.html