Android 反射 - 通过 java 反射获取应用进程 Application 的实例化对象

本文详细探讨了Android中反射机制的应用,特别是在创建Application对象的过程。同时,深入分析了反射与new关键字的区别,以及Java泛型在集合中的作用与限制。通过实例,解释了动态加载与静态加载的概念。

        反射 - 平时没使用过,但是最近在复习(划掉,学习) framework 东西的时候,发现应用进程 Application 在被实例化的时候,最终是在 Instrumentation.java 中调用如下方法去实例化(Android 8.1.0):

 

   /**
     * Perform instantiation of the process's {@link Application} object.  The
     * default implementation provides the normal system behavior.
     * 
     * @param clazz The class used to create an Application object from.
     * @param context The context to initialize the application with
     * 
     * @return The newly instantiated Application object.  // 最终方法会被调用到这里
     */
    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

 

        到这里才知道,原来Android 系统中,应用进程的 Application 对象的创建,是通过反射整出来的。并且自己写了一个例子,发现,在通过反射调用  (Coder) clazz.newInstance();  方法实例化对象的时候,是去调用了 Coder 的无参构造器去实例化了一个 Coder 对象。并且如果开发人员忘记写了 Coder 的无参构造器的情况下还去使用反射获取 Coder 实例化对象的时候会报错。具体尝试如下:

        try {
            Log.d("heguodong","----- begin -----");
            Class clazz = Class.forName("com.hegd.spinnerdemo.Coder");
            Coder coder = (Coder)clazz.newInstance();

            coder.setAge(18);
            coder.setName("HeGuodong");

            Log.d("heguodong","coder 的名字是:" + coder.getName() + " ; 年龄是:" +  coder.getAge());

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            Log.d("heguodong","----- end -----");
        }

在 Coder 中添加无参构造器之后,日志打印:

07-27 17:11:00.928 32384-32384/com.hegd.spinnerdemo D/heguodong: ----- begin -----
07-27 17:11:00.929 32384-32384/com.hegd.spinnerdemo D/heguodong: coder 的名字是:HeGuodong ; 年龄是:18
07-27 17:11:00.929 32384-32384/com.hegd.spinnerdemo D/heguodong: ----- end -----

而如果在 Coder 中没写无参构造器的话,日志打印是:

07-27 17:12:40.999 32515-32515/? I/zygote: Late-enabling -Xcheck:jni
07-27 17:12:41.241 32515-32515/com.hegd.spinnerdemo D/heguodong: ----- begin -----
07-27 17:12:41.242 32515-32515/com.hegd.spinnerdemo W/System.err: java.lang.InstantiationException: java.lang.Class<com.hegd.spinnerdemo.Coder> has no zero argument constructor
07-27 17:12:41.242 32515-32515/com.hegd.spinnerdemo W/System.err:     at java.lang.Class.newInstance(Native Method)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at com.hegd.spinnerdemo.MainActivity.onCreate(MainActivity.java:49)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.Activity.performCreate(Activity.java:7036)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.Activity.performCreate(Activity.java:7027)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.-wrap11(Unknown Source:0)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:106)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.os.Looper.loop(Looper.java:164)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6494)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
07-27 17:12:41.246 32515-32515/com.hegd.spinnerdemo W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
07-27 17:12:41.246 32515-32515/com.hegd.spinnerdemo W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
07-27 17:12:41.246 32515-32515/com.hegd.spinnerdemo D/heguodong: ----- end -----

后来去慕课网恶补了一课,终于反射入门。

在 Android 中,万事万物都是对象(只有 静态的成员(属于某个类的)、普通的数据类型(包装类弥补了这点) 不是对象)。

那类是不是一个对象呢?类是一个对象,类是 java.lang.Class 的实例对象。  

那写的 Coder.java 这个类是不是一个对象呢?是的,它是 Class 类实例对象。Android 官方文档中管这个对象叫做 Coder  类的类类型。

那怎么去表示这个类对象?比如表示一下 Coder.java 的类对象:

private void test() {
        
        //Coder 的实例对象怎么表示
        Coder vach = new Coder("vach", 18);

        //Coder 这个类本身也是一个对象:是 Class 类的实例对象,并且有三种方式表示 Class 类的实例对象

        /**方式一*/
        Class<Coder> c1 = Coder.class;//这种表达方式也告诉我们了任何一个类都有一个隐含的静态成员变量class.

        /**方式二*/
        Class<? extends Coder> c2 = vach.getClass();//已知该类的对象,通过 getClass(); 方法获取到 Class 类的实例对象

        /*Note: Android 官网说 c1 、c2 表示了 Coder 类的类类型(Class type) */
        /*Note:并且打印 c1 == c2 ,发现他们是相等的,也证明了一个类有且只有一个  Class 类的实例对象*/

        /**方式三*/
        try {
            Log.d("heguodong","----- begin -----");
            Class<?> c3 = null;
            c3 = Class.forName("com.hegd.spinnerdemo.Coder");

            /*Note:并且打印 c3 == c2 ,发现他们完全相等 */
            /*Note:我们完全可以通过这个类类型 (c1/c2/c3 都可以) 获取到这个类的实例对象,但是注意做强制类型转换 */
            
            Coder coder1 = (Coder) c3.newInstance();//必须保证 Coder 类有无参构造器
            coder1.setAge(18);
            coder1.setName("HeGuodong");

            Log.d("heguodong","coder 的名字是:" + coder1.getName() + " ; 年龄是:" +  coder1.getAge());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            Log.d("heguodong","----- end -----");
        }
    }

        另外知识点延伸,看到 Coder 类的实例对象,可以通过new 出来,还可以通过反射创建出来,那这两个对象到底有什么异同?这两种实例化对象的方式分别有什么限制条件,分别可以解决什么问题?

        是这样的,通过 new 的方式创建实例对象是类的静态加载,在编译时刻就需要加载可能用到的所有类。不管用不用到这个类的实例,只要是通过new 的方式创建对象,都需要在编译的时候去加载这个类。而通过反射的方式创建实例化对象的时候,是动态加载类,在运行时加载(所以反射创建对象,在编译的时候是不会报错加载不到类的,而如果代码中真的没有目标类,那么会在代码运行的时候报错  ClassNotFoundException )。功能性的类一般都是动态加载,而不是静态加载。

        再次知识点延伸,上面看到使用了泛型,那么泛型在集合中( 类 / 方法中)的本质是什么?

        泛型在集合中使用的本质:是在编译时限制往集合中放置数据的类型的。

        那就有意思了:通过做实验,两个集合,第一个集合不使用泛型,第二个集合使用泛型限制存放数据的类型:后来通过反射拿到这两个集合的类类型,发现他们是相等的。所以:java 集合中的泛型是防止数据错误传入的,只是在代码编译时对数据做限制操作。而我们又知道反射是在代码运行时操作,那我们岂不是可以通过反射越过泛型的限制 ?当然是可以的,但是一般没人这么做,没啥意义,因为当跨过泛型的限制向集合中添加一些乱七八糟类型的数据之后,集合中的数据就混乱了,代码中对集合数据循环遍历的时候就很麻烦。

        再次知识点延伸,说说对泛型的理解?

 

 

其他参考链接:

https://www.imooc.com/learn/199 (反射——Java高级开发)

https://www.cnblogs.com/yrstudy/p/6500982.html   (Java反射机制--是什么,为什么,怎么用。

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值