写在前面

今天在需求评审的时候,遇到了挺有意思的要求。需求是什么样子就不说了。总之完成这个需求需要一个调用系统api的操作。然而这个api因为并不稳定的原因。被谷歌hide掉了。
这个时候我们最直接的方式就是去通过反射去调用这个系统api。(当然这种方式治标不治本,因为既然被hide,就说明这个api很不稳定。所以这个版本可以用,有可能下个版本就没了)
不过这里我们不考虑这个问题,因为如题所说,这次主角是反射。
之前在用反射的时候,其实并没有去思考反射所带来的东西。而这次在用反射的时候,开始去思考为什么要这么用,它和Java的运行机制有什么关系。
我们在看JVM的时候:多少都能了解到Java的类加载机制,那就是我们在new一个对象的时候,JVM会先使用双亲委派机制去加载这个class文件,class文件的代码结构会被封装成Class对象唯一的出现在方法区中。然后才是我们的实例对象创建到堆中。
之前我去上述的理解没有任何概念,仅仅是当做文字给记了下来。直到我今天在使用反射的时候,突然对上述的概念有了实质性的认识。
不过不着急,让我们一点点的深入....

进入正文

声明一个类

首先我们先声明一个class,内部包含了另一个对象,并且写了一些私有/公有/静态的变量及方法,一会将针对这个class进行反射的操作:

这里的日志使用了Log是Android里的打印输出,而非Java中的System.out.print。

public class ReflectModel {private ReflectBean mReflectBean;private String mContent = "A";public int mNum = 1;public static int sNum = 666;public ReflectModel() {LogUtils.d(TAG, "ReflectModel(),无参构造方法执行");}public ReflectModel(String content) {LogUtils.d(TAG, "ReflectModel(String content),一个String参数的构造方法执行");mContent = content;}private ReflectModel(String content, int num) {LogUtils.d(TAG, "ReflectModel(String content,int num),俩个参数的私有构造方法执行");mContent = content;mNum = num;}public void fun() {LogUtils.d(TAG, "我就是一个方法,在本例子中。我是被反射生成的对象调用的->mContent:" + mContent + "-mNum:" + mNum);}private void printContent() {LogUtils.d(TAG, "我就是私有的打印方法->mContent:" + mContent + "-mNum:" + mNum);}private void setContent(String content) {LogUtils.d(TAG, "我就是一个带有一个String参数的私有方法->setContent:" + content);mContent = content;}public void printBean() {LogUtils.d(TAG, "我是ReflectBean的打印方法->mName:" + mReflectBean.getName());}public static void staticFun() {LogUtils.d(TAG, "我是静态方法staticFun");}
}

内部的ReflectBean类

public class ReflectBean {private String mName = "a";public ReflectBean(String name) {mName = name;}public String getName() {return mName;}
}

获取拥有真正对象代码结构的Class:

不知道各位小伙伴们有没有思考过,为什么我们new一个类的时候这么方便,反而到了反射的时候会如此的麻烦?

public void fun() {//第一种方式获取Class对象:产生一个Student对象的实例,以及一个唯一的Class对象。ReflectModel model1 = new ReflectModel();//获取Class对象:这种方式没什么意义,既然有了对象的实例,何必再去反射Class modelClass = model1.getClass();//第二种方式获取Class对象:需要我们导包,但是有些时候这个类是隐藏的(比如很多系统不稳定的类,@Hide)Class modelClass2 = ReflectModel.class;Class modelClass3 = null;try {//第三种方式获取Class对象:类的全路径modelClass3 = Class.forName("com.example.mbenben.studydemo.basenote.reflect.ReflectModel");} catch (ClassNotFoundException e) {e.printStackTrace();}

将Class实例化

//开始进行具体内部操作,此时我们仅仅是拿到了ReflectModel的class这个对象,而非是这个ReflectModel对象的实例if (modelClass3 != null) {//获取ReflectModel.class对应的所有构造方法(public的)Constructor[] constructors = modelClass3.getConstructors();for (Constructor constructor : constructors) {LogUtils.d(TAG, "当前获取的构造方法:" + constructor.getName());}LogUtils.d(TAG, "----以上是public的----");//所有构造方法(包括:私有、受保护、默认、公有)constructors = modelClass3.getDeclaredConstructors();for (Constructor constructor : constructors) {LogUtils.d(TAG, "当前获取的构造方法:" + constructor);}Object object = null;try {// 获取共有无参构造函数Constructor con = modelClass3.getConstructor(null);// 调用此无参构造方法,那么此时我们就会获取到Reflect的实例对象了(默认返回object)ReflectModel reflectModel = (ReflectModel) con.newInstance();// 有了ReflectModel对象实例,我们就可以正常使用了/*** 但是此时我们知道,我们强制类型转成了我们想要的类型,但是我们上文中提到,有些类是hide的。* 因此我们很多情况下,我们被限制只能得到object对象。*/reflectModel.fun();// 获取私有的含有String和int参数的构造方法con = modelClass3.getDeclaredConstructor(new Class[]{String.class, int.class});// 此时我们获取到了这个private的含参构造方法对象,但是因为private权限原因,我们没办法直接调用newInstance()// 我们需要调用下面的方法,无视private修饰符(调用后,我们就可以执行private的构造方法)con.setAccessible(true);//调用私有俩参构造方法object = con.newInstance("B", 2);

反射调用方法

                //上诉提到,如果我们反射的类是hide,此时我们肯定没办法把Object转成ReflectModel类型。因此,我们拿到这个实例后想要调用其方法,还需要使用反射的方式// private方法应该使用getDeclaredMethod()去获取Method printContent = modelClass3.getDeclaredMethod("printContent", new Class[]{});//同样因为private的原因,我们在执行方法时,要先清除权限问题printContent.setAccessible(true);//使用object实例对象,调用printContent方法,因为没有参数,所以传nullprintContent.invoke(object, null);//生成名为setContent的含有一个String参数的方法对象Method setContent = modelClass3.getDeclaredMethod("setContent", new Class[]{String.class});setContent.setAccessible(true);//调用setContent方法,去改变mContent的值setContent.invoke(object, "C");//再次执行打印操作printContent.invoke(object, null);//调用静态方法Method staticMethod = modelClass3.getMethod("staticFun", new Class[]{});//因为静态方法属于类,所以我们不需要传实例对象,因为它在class被加载的时候,就已经被创建了。staticMethod.invoke(null, null);

反射调用变量/static

                /*** 反射调用变量*///获取所有public变量封装的Field对象Field[] fields = modelClass3.getFields();//获取所有变量封装的Field对象fields = modelClass3.getDeclaredFields();//获取private的mContent的变量Field content = modelClass3.getDeclaredField("mContent");content.setAccessible(true);Field num = modelClass3.getField("mNum");num.set(object, 3);//获取static变量Field sNum = modelClass3.getField("sNum");LogUtils.d(TAG, "mNum在object实例对象中的值:" + num.get(object) + "-static变量sNum的值:" + sNum.get(null));//将object实例的mContent对象,设置为Dcontent.set(object, "D");printContent.invoke(object, null);Field reflectBean = modelClass3.getDeclaredField("mReflectBean");Class reflectBeanClass = Class.forName("com.example.mbenben.studydemo.basenote.reflect.ReflectBean");Constructor beanCon = reflectBeanClass.getConstructor(new Class[]{String.class});Object beanObject = beanCon.newInstance("b");//给mReflectBean赋值reflectBean.setAccessible(true);reflectBean.set(object, beanObject);Method printBean = modelClass3.getMethod("printBean", new Class[]{});printBean.invoke(object, null);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}}

继续理解

我们知道JVM在使用一个类的时候,会先去加载这个类。也就是生成唯一的Class对象。这个Class对象拥有我们的java代码的变量,方法结构。但是它并不是一个实例。因此我们在反射的时候,要先获取构造方法对象,也就是Class返回给我们的Constructor。此时我们运行这个对象的newInstance,我们就初始化了这个Class,获取了这个Class的实例。(这也就是为什么我们反射操作会如此的麻烦)
实例化了这个对象之后,虽然只是一个Object对象,但是它和我们真正new的对象没有任何区别,那么此时我们就可以正常的调用方法了。
我们知道,static是属于类,在类被加载的时候就已经出现了。那么此时,反射也侧面证实了这个问题:
在我们反射调用static的变量和方法时,set或者invoke的参数传的是null,也就是说我们没有传递任何对象实例,所以此时我们使用的是这个在类被加载时就被创建的Class对象中的变量和方法,而非操作的实例对象。因此,对static是不是有了一个更为深刻的理解呢?

尾声

时隔很久都还没有写博客了,是因为自己的确不知道该写些写什么,唉,迷茫,好菜...
希望可以对各位看官有所帮助吧。


我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

个人公众号:IT面试填坑小分队

Java反射实践:从反射中理解class相关推荐

  1. Java最佳实践–多线程环境中的DateFormat

    这是有关使用Java编程语言时的拟议实践的系列文章的第一篇. 所有讨论的主题均基于用例,这些用例来自于电信行业的关键任务超高性能生产系统的开发. 在阅读本文的每个部分之前,强烈建议您参考相关的Java ...

  2. 游戏装备强化java机制_从游戏中理解Java特性,我悟了!

    想学好一门开发语言,掌握了它的语言特性,学习起来往往能达到事半功倍的效果,自从接触到Java这个词,想必被灌输最多的就是Java是面向对象的编程的,要记住一点万物皆可对象,所有的特性都是基于对象来展开 ...

  3. Java最佳实践–高性能序列化

    在使用Java编程语言时,我们将继续讨论与建议的实践有关的系列文章,我们将讨论并演示如何将对象序列化用于高性能应用程序. 所有讨论的主题均基于用例,这些用例来自于电信行业的关键任务超高性能生产系统的开 ...

  4. Java最佳实践–字符串性能和精确字符串匹配

    在使用Java编程语言时,我们将继续讨论与建议的实践有关的系列文章,我们将讨论String性能调优. 我们将专注于如何有效地处理字符串创建, 字符串更改和字符串匹配操作. 此外,我们将提供我们自己的用 ...

  5. Java最佳实践– Vector vs ArrayList vs HashSet

    在使用Java编程语言时,我们将继续讨论与建议的实践有关的系列文章,我们将在三个最常用的Collection实现类之间进行性能比较. 为了使事情变得更现实,我们将在多线程环境下进行测试,以讨论和演示如 ...

  6. Java最佳实践–队列之战和链接的ConcurrentHashMap

    在使用Java编程语言时,我们将继续讨论与建议的实践有关的系列文章,我们将在四个具有相关语义的流行Queue实现类之间进行性能比较. 为了使事情变得更现实,我们将在多线程环境下进行测试,以讨论和演示如 ...

  7. Java最佳实践– Char到Byte和Byte到Char的转换

    在使用Java编程语言时,我们将继续讨论与建议的实践有关的系列文章,我们将讨论String性能调优. 特别是,我们将重点介绍使用默认编码时如何有效地处理字符到字节和字节到字符的转换. 本文总结了两种提 ...

  8. 反射在java中的应用_java反射机制在项目中的运用

    定义:Reflection是java开发语言特性之一,它允许运行中的java程序对自身进行检测,自审,并能操作程序内部的属性和方法,Reflection是java被视为动态语言关键之一.允许程序从执行 ...

  9. 关于Java的反射机制,你需要理解这些...

    2019独角兽企业重金招聘Python工程师标准>>> 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性: ...

  10. 关于Java的反射机制,你需要理解这些..

    转载请标明出处: http://blog.csdn.net/forezp/article/details/53730429 本文出自方志朋的博客 反射机制是在运行状态中,对于任意一个类,都能够知道这个 ...

最新文章

  1. .net平台性能很不错的轻型ORM类Dapper(转)
  2. 正则表达式、事件调用
  3. python内置函数中的 IO文件系列 open和os
  4. Git 常用命令速查表(图文+表格)【转】
  5. fotify php审计,代码安全审计(二)Fortify介绍及使用教程
  6. 不行是谁决定的呢,明明你眼前就有这么多的可能啊
  7. zabbix 3.2.1 安装 graphtree3.0.4 或 graphtree3.2.x
  8. Android群英传笔记——摘要,概述,新的出发点,温故而知新,能够为师矣!
  9. 中国剩余定理-模版(互质版)
  10. php和python-php与python谁更适合web开发?为什么?
  11. DecimalFormat的使用
  12. mariadb 存储引擎mysql_MySQL/MariaDB---查询缓存与存储引擎
  13. 中国大学MOOC 视频字幕获取及处理方法
  14. 『中安网培』***游戏过关攻略
  15. eterm单人订座流程
  16. 记录一次使用ParallelGC导致线上FGC频繁、耗时长的原因
  17. 腾讯web引用skey g_tk bkn和日期显示分析
  18. python判断手机号运营商_匹配手机号码及运营商校验
  19. OKLink行业观察:投资数字资产的机构版图(三)——Stone Ridge和纽约数字投资集团
  20. 仿QQ即时通讯聊天软件Windows新版可定制客户端演示

热门文章

  1. RedHat6.5-Linux安装telnet服务
  2. ButterKnife基本使用
  3. Windows Server 2008域中组的成员关系
  4. CSMA/CD在全双工和半双工模式下的区别
  5. Ghost XP基本介绍
  6. DataReader和DataSet区别
  7. (原創) 标准的makefile写法 (C/C++)
  8. 多层科目任意组合汇总报表的性能优化 (上)
  9. Linux下利用backtrace追踪函数调用堆栈以及定位段错误【转】
  10. redis底层数据结构之intset