美文网首页
Java进阶-反射机制的详细学习指南

Java进阶-反射机制的详细学习指南

作者: 码农翻身记 | 来源:发表于2021-05-26 18:53 被阅读0次

    什么是反射

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

    java反射机制提供的功能:

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

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

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

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

    反射应用场景

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

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

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

    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获取方法。

    \color{red}{Declared}的方法只能获取类本身包括私有在内的属性或方法,而不带Declared的方法不能获取私有属性或方法,但包括父类及自身的所有公有属性或方法。

    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, "reflectPrivateField:" + 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,并给出代码示例。反射既有优点也有缺点。

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

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

    参考

    深入浅出反射
    https://zhuanlan.zhihu.com/p/21423208
    大白话说Java反射:入门、使用、原理
    https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
    java.lang.Class
    java.lang.reflect.Method

    相关文章

      网友评论

          本文标题:Java进阶-反射机制的详细学习指南

          本文链接:https://www.haomeiwen.com/subject/duvcsltx.html