您的位置 首页 java

分享Java进阶-反射机制的详细学习

什么是反射

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

java反射机制提供的功能:

  • 在运行时判定任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判定任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法

反射应用场景

操作因访问权限限制的属性和方法 如private属性和方法,又如在android开发中,阅读sdk源码会发现有什么多方法加了hide注解,是google为了安全起见加的隐藏,应用层是无法直接访问的,这时可以通过反射在运行时调用( android 9.0 加了白名单机制,在黑名单的接口即使通过反射都无法调用)。

实现自定义注解 android有很多火爆的开源框架都应用了自定义注解和反射,如EventBus,Retrofit;

动态加载插件 很多大型App都用了动态加载和热修复的插件化技术,也利用了反射的方式,实现宿主(Host)对插件(LibPlugin)的调用,详细可以了解插件化开源框架,如VirtualApp,DroidPlugin,RePlugin

Java反射机制

反射涉及到四个核心类

  • java.lang.Class.java:类对象;
  • java.lang.reflect.Constructor.java:类的构造器对象;
  • java.lang.reflect.Method.java:类的方法对象;
  • java.lang.reflect.Field.java:类的属性对象;

反射工作原理 要正确理解Java反射机制就得了解Class这个类,反射正是对Class类进行操作。当我们编写好一个Java程序(Java文件),会先将其编译(生成class文件),而运行程序, JVM 会加载class文件到内存,并产生一个Class对象,通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的父类、接口、方法、成员以及构造方法的声明和定义等信息,我们通过new的形式创建对象实际上就是通过这些Class来创建。

反射的工作原理就是借助Class.java、Constructor.java、Method.java、Field.java这四个类在程序运行时动态访问和修改任何类的行为和状态。

反射的原理

反射常用的API

1、获取反射中的Class对象

Java API 中,获取 Class 类 对象有三种方法:

第一种,使用 Class.forName 静态方法。 当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

 Class clz = Class.forName("java.lang.String");
  

第二种,使用 .class 方法。 这种方法只适合在编译前就知道操作的 Class。

 Class clz = String.class;
  

第三种,使用类对象的 getClass() 方法。

 String str = new String("Hello");
Class clz = str.getClass();
  

2、通过反射创建类对象

通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

第一种:通过 Class 对象的 newInstance() 方法。

 Class clz = Stock.class;
Stock stock = (Stock)clz.newInstance();
  

第二种:通过 Constructor 对象的 newInstance() 方法

 Class clz = Stock.class;
 Constructor  constructor = clz.getConstructor();
Stock stock = (Stock)constructor.newInstance();
  

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

 Class clz = Stock.class;
Constructor constructor = clz.getConstructor(String.class, String.class,String.class);
Stock stock= (Stock)constructor.newInstance("000001", " 平安银行 ","sz");
  

3、通过反射获取类属性、方法、构造器

我们通过 Class 对象的 getFields() 、getDeclaredFields()方法获取属性,通过getMethod()、getDeclaredMethod获取方法。

 Class clz = Stock.class;
Field[] fields = clz.getFields();//包括父类的所有公有属性(不包括私有的)
Field[] declaredFields = clz.getDeclaredFields();//自身的公有、私有属性

Method[] methods = clz.getMethods();//包括父类的所有公有方法(不包括私有的)
Method[] declaredMethods = clz.getDeclaredMethods();//自身的公有、私有方法
  

4、通过反射获取属性值及执行方法

 Class stockClass= Stock.class;
Object stockObject = stockClass.newInstance();

Field marketField = stockClass.getDeclaredField("market");
marketField.setAccessible(true);//私有属性或方法,必须设置accessible=true,否则抛出IllegalAccessException异常
Log.i(TAG, "reflect private Field:" + marketField.get(stockObject));

 Method method = stockClass.getDeclaredMethod("getTrend", String.class);
 method.setAccessible(true);
 Object trend = method.invoke(stockObject, "5");
 Log.i(TAG, "reflectPrivateMethod:" + trend);
  

完整代码示例

Prodct.java

 public class Product {
    private String mCode;
    private String mName;
    public String type;

    public void setCode(String code) {
        mCode = code;
    }

    public void setName(String name) {
        mName = name;
    }

    public String getCode() {
        return mCode;
    }

    public String getName() {
        return mName;
    }

    public String toString() {
        return "Product{code=" + mCode + ",name=" + mName + ",type=" + type + "}";
    }
}
  

Stock.java

 public class Stock extends Product {

    private String market = "sz";

    private int getTrend(String price) {
        if (TextUtils.isEmpty(price)) {
            return 0;
        } else if (price.contains("-")) {
            return -1;
        } else {
            return 1;
        }
    }

    @Call("do_sth_call")
    public void doSth() {
        Log.i("Stock", "do sth call");
    }

    public String toString() {
        return "Stock{code=" + getCode() + ",name=" + getName() + ",type=" + type + ",market=" + market + "}";
    }
}
  

Call.java

 @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Call {
    String value();
}
  

ReflectTest.java

 public class ReflectTest {
    public static final String TAG = "reflect";

    public static void testReflect() {
        reflectNewInstance();
        reflectPrivateField();
        reflectPrivateMethod();
        reflectAnnotation();
        reboot();
    }

    /**
     * 创建对象
     */    public static void reflectNewInstance() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Product product = (Product) stockObject;
            product.setCode("000001");
            product.setName("平安银行");
            product.type = "1";
            Log.i(TAG, "reflectNewInstance stock=" + product.toString());
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * 反射访问私有属性
     */    public static void reflectPrivateField() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Field marketField = stockClass.getDeclaredField("market");
            marketField.setAccessible(true);
            Log.i(TAG, "reflectPrivateField:" + marketField.get(stockObject));
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * 反射访问私有方法
     */    public static void reflectPrivateMethod() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Method method = stockClass.getDeclaredMethod("getTrend", String.class);
            method.setAccessible(true);
            Object trend = method.invoke(stockObject, "5");
            Log.i(TAG, "reflectPrivateMethod:" + trend);
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * 反射调用注解方法
     */    public static void reflectAnnotation() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Method[] methods = stockClass.getMethods();
            for (Method method : methods) {
                Call call = method.getAnnotation(Call.class);
                if (call != null && "do_sth_call".equals(call.value())) {
                    method.invoke(stockObject);
                }
            }
        } catch (Exception e) {

        }
    }

    // 重启手机
    public static void reboot() {
        try {
            Class<?> cServiceManager = Class.forName("android.os.ServiceManager");
            Method mGetService = cServiceManager.getMethod("getService", String.class);
            Object oPowerManagerService = mGetService.invoke(null, Context.POWER_SERVICE);
            Class<?> cIPowerManagerStub = Class.forName("android.os.IPowerManager$Stub");
            Method mReboot = cIPowerManagerStub.getMethod("reboot", boolean.class, String.class, boolean.class);
            Method mAsInterface = cIPowerManagerStub.getMethod("asInterface", IBinder.class);
            Object oIPowerManager = mAsInterface.invoke(null, oPowerManagerService);
            mReboot.invoke(oIPowerManager, true, null, true);
        } catch ( Exception  ex) {
            ex.printStackTrace();
        }
    }
}
  

反射执行reboot方法,会抛出异常,java.lang.reflect.InvocationTargetException,异常原因是java.lang.SecurityException: Neither user 10547 nor current process has android.permission.REBOOT。原因是没有reboot的操作权限。

  Caused by: java.lang.SecurityException: Neither user 10547 nor current process has android.permission.REBOOT.
W/System.err:     at android.os.IPowerManager$Stub$Proxy.reboot(IPowerManager.java:1596)
W/System.err:     at com.android.server.power.PowerManagerService$BinderService.reboot(PowerManagerService.java:5662)
  

总结

本文总结了反射的作用及应用场景,列举了常用的API,并给出代码示例。反射既有优点也有缺点。

优点是提供了灵活的机制,想对类做啥就做啥,一些黑科技就是用了反射实现了一般开发无法做到的功能,另外很多框架应用了反射使得开发更加便利高效。

缺点是运行时反射操作方法会比直接调用性能慢,取决于如何反射,一般可以忽略,另外一个问题就是安全性,随意修改私有属性和访问私有方法,破坏了类的封装性,可能潜在逻辑隐患,再一个是在安卓上应用,可能出现适配问题。因此应用反射前要充分了解这些缺点带来的影响。

文章来源:智云一二三科技

文章标题:分享Java进阶-反射机制的详细学习

文章地址:https://www.zhihuclub.com/195841.shtml

关于作者: 智云科技

热门文章

网站地图