写在前面: Java反射, 这个东西百度就会出来相关概念:

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

字每一个都认识, 连起来的意思也不是那么的深奥, 但总给人感觉前面蒙着一层面纱看不清, 似懂非懂, 懂又不懂的感觉. 笔者就尝试从自己的理解对Java反射进行一些解释.

0. 类加载过程

要说反射就得从Java的类加载过程说起.

    字丑见谅, Java类加载大体分为这么5个步骤, 有机会详细分析这五个步骤, 我们这里主要说的是第一个阶段–加载, 这个加载和前面的类加载是不一样的概念, 加载是类加载的一个阶段.加载是类加载子系统将类的Class对象加载到JVM的过程, 主要工作是:

  1. 通过全限定名来获取定义此类的二进制字节流.
  2. 将二进制字节流的静态存储结构转化为方法区的运行时数据结构.
  3. 在内存中生成这个类的java.lang.Class对象, 作为方法区这个类的数据访问入口.

以上方法区之类的名称就不解释分析, 那属于另外一部分的内容. 重点看第三点的在内存中生成这个类的java.lang.Class对象. 反射的几乎所有东西都是围绕这个东西在做操作, 所以在讲反射之前我们有必要先对这个东西进行了解.在Java代码中很好获取这种类型的对象实例.

public class TestClass {public TestClass(){};
}public class Test{public static void Main(String ... args) {// 1. 通过对象类型.classClass clazz1 = TestClass.class;// 2. 通过forName 这个名称熟悉吗, 就是JDBC连接其中加载驱动那一步Class class2 = Class.forName("TestClass的权限定类型名");// 3. 通过对象实例的getClass()方法, 这个方法是继承自Object类的方法Class class3 = new TestClass().getClass();// 4. 还有一种通过类加载器获取, 笔者就知道这4种// 另外拓展其实java 8个基础数据类型和void的关键字也有Class类型.// void.class int.class ...读者可以自行尝试打印看看.}
}

万物皆对象, 这是面向对象的语言经常说的. Class是类的类, 这么说好绕, 是不是, 也可以把类的Class对象实例看做蓝图或者建筑图纸, 每当我们要去建房的时候我们就去拿图纸(图纸就是Class对象实例)根据图纸描述一步一步的建设房子(房子就是我们通过new 等方式创建的对象), 不管我们建多少套房子, 只要是房子类型一样的, 就是拿的同一张图纸, 就如上面代码中的TestClass类我们不管创建了多少个这个类型的对象实例, 使用都是TestClass.class这张图纸. 上面这一段主要解释的是无论某个对象在Jvm中被创建了多少实例, 都只有一个Class对象与之对应.
    做个小结, 加载就是类加载子系统把类的Class加载到虚拟机内存, 并生成类的Class对象实例, 而Class对象实例则保存了要加载的类的类信息. 需要提示下在加载阶段完成后类本身还是不可用的, 类加载还要经历后面几个关键步骤.

2. 反射

反射的概念上面其实已经讲了. 现在知道了Class对象是什么了, 以及每一个类都有个Class对象实例, 而类的创建的(上面的建房子)都是从Class对象实例获取相应的信息创建的. new TestClass()这种方式创建对象实例实际上Jvm内部自动去从TestClass的Class对象实例中获取了相应的信息进行创建, 而反射浅显理解就是把上面new的自动的过程变成我们程序员手动操作的过程, 手动去创建类的Class对象实例, 手动去获取构造函数, 手动去创建实例, 你可以这样理解反射, 但事实上还是有细微的差别. 写个例子初体验一下反射:

public class TestClass {  private String param;public TestClass(String param) { this.param = param; }public TestClass() { }public void test(){System.out.println("hello reflect... " +  param);}private void test(String testParam){System.out.println("test:hello reflect... " +  testParam);}
}public class Main {public static void main(String[] args) throws Exception {// 1. 类的加载的加载阶段, 创建类的Class对象实例// 当然这里不止加载阶段, 还有其它阶段进行的.Class<TestClass> testClazz = TestClass.class;// 2. 从Class对象实例中获取TestClass的构造函数// 参数String.class表示获取只有一个参数且为String类型的构造函数// 不传参数默认为无参构造函数Constructor<TestClass> testClazzDeclaredConstructor = testClazz.getDeclaredConstructor(String.class);// 3. 通过调用构造函数的newInstance方法创建实例// newInstance的参数就是构造函数需要的参数TestClass testClass = testClazzDeclaredConstructor.newInstance("param 111");// 4. 调用实例的方法输出 hello reflect... param 111testClass.test();// 作为对比正常调用new TestClass("param 111").test();}
}

上面的代码案例就是反射, 可以看到相比正常的new TestClass("param 111").test()多了好几步更加繁琐了, 很多人就产生了疑问. 法拉第发明圆盘发电机的时候, 贵妇问他有什么用, 他说刚出生的婴儿有什么用呢? 况且反射之于Java可是壮汉而不是婴儿.
    与反射相关的API大都在java.lang.reflect下, 比较核心的类有Method, Field, Constructor,Array还有代理用到Proxy等.在想需不需要介绍下相关API的基本操作.
介绍反射相关的操作:

  1. 反射-方法

    // 还是上面的TestClass类, 现在通过反射分别调用两个test方法
    public class Main{public static void main(String ... args) {// 为了简便这里就不通过反射创建对象了, 直接通过new创建TestClass testClass = new TestClass("param 111");// getMethod的方法可以通过方法名称获取到方法.// 方法签名getMethod(String name, Class<?>... parameterTypes)// 后面的Class类型的可变长参数是指的需要调用的方法参数列表// 必须严格按照顺序, java方法参数是方法签名的一部分,不同顺序的参数是不同方法Method testNoargs = testClass.getClass().getMethod("test");// 通过Method.invoke调用// 要调用实例方法肯定需要对象实例     testNoargs.invoke(testClass);// getDeclaredMethod使用方式和getMethod一样, 参数含义也想通// 只是getDeclaredMethod可以获取private方法Method testArgString = testClass.getClass().getDeclaredMethod("test", String.class);// 设置方法的访问权限可以通过设置为true, 从而可以调用私有方法testArgString.setAccessible(true);// 按照常规正常的操作我们是不可以调用私有方法的, 通过反射可以// 这也是反射的应用之一testArgString.invoke(testClass, "param 222");}
    }
    
  2. 反射-字段
// 还是使用上面的TestClass对象
public class Main {public static void main(String ... args) {TestClass testClass = new TestClass("param 111");Class<? extends TestClass> testClassClass = testClass.getClass();// 1. 通过getField获取字段, 和上面一样只能获取publicField publicField1 = testClassClass.getField("publicField1");// 这里get传入的是对象实例System.out.println(publicField1.get(testClass));// 2. 通过getDeclareField获取字段, 可以获取私有字段Field field1 = testClassClass.getDeclaredField("field1");// 设置访问权限, 把private变成publicfield1.setAccessible(true);System.out.println(field1.get(testClass));// 还可以修改它的值field1.set(testClass, "field22");System.out.println(field1.get(testClass));// 3.这是静态字段的示例Field staticField = testClassClass.getDeclaredField("staticField");staticField.setAccessible(true);// 这一段这几个testClass传null也可以, 想想为什么// 静态字段和非静态字段的区别System.out.println(staticField.get(testClass));staticField.set(testClass, "staticField2");System.out.println(staticField.get(testClass));// 4. 这是常量, 常量也是可以修改. 常量由final修饰Field staticFinalField = testClassClass.getDeclaredField("staticFinalField");staticFinalField.setAccessible(true);// 这句代码我也不明白, 测试发现打开下面的这句代码, 就没办法修改常量了.
//        System.out.println(staticFinalField.get(null));// 把常量改为可修改. Field modifiers = staticFinalField.getClass().getDeclaredField("modifiers");modifiers.setAccessible(true);modifiers.set(staticFinalField, staticFinalField.getModifiers() & ~Modifier.FINAL);staticFinalField.set(null, "waxxd22");System.out.println(staticFinalField.get(TestClass.class));}
}

注意4的例子修改常量在高版本上可能有些问题, 高版本的jdk需要用别的方式修改常量, 高版本大概12以上吧, 并且这个东西了解即可, 一般还是不要去修改常量的好.另外在修改基本类型的常量比如int的时候应该会不起效果. 具体原因public static final int a = 100;定义这么一个变量, 在别的地方使用a的时候它可能直接把a替换为100, 所以你改了a, 别的地方依旧是100.
3. 反射-数组

public class Main {public static void main(String ... args) {// 1. 反射创建数组, 之前说过基础数据类型也有Class对象Object intArr = Array.newInstance(int.class, 3);// 2. 反射赋值Array.set(intArr, 1, 2);// 3. 反射取值System.out.println(Array.get(intArr, 1));// 4. 获取数组的Class类型System.out.println(int[].class);// 如何通过forName获取int[]的Class呢System.out.println(Class.forName("[I"));// boolean[] 是[Z long[] 是[J  对象类型[]是[L-> String[] -> [Ljava.lang.String]}
}

关于数组那里的Class.forName为什么那么怪异的说明, 你认为不怪异那就算了. 我们声明的时候如字符串数组new java.lang.String[]但是经过编译器编译会变成Ljava.lang.String所以Class.forName里面的参数要写成那样的形式. 但是并不能通过这种方式生成一个数组, 这只表示把Ljava.lang.String这么一个Class对象加载到内存中去了, 想想生成一个数组是需要传入长度的, 我们又不知道Ljava.lang.String这个类型的构造函数长什么样.

3.0 反射应用

反射能做什么, 在工作中也有人问过我这样的问题, 反射能做什么. 上面的例子已经回答了这个问题, 当我告诉别人能调用方法, 能读取字段修改字段, 能创建对象的时候, 他们的头脑中的大都大大的疑惑, 这些我不反射也可以做并且更简单. 所以这里我试图解释一下反射到底能做什么

  1. 肉眼可见的一个作用, 读懂源码, 很多框架都大量应用到反射, 比如spring, 随处可见的反射.
  2. 偶尔一些特殊需求做一些骚操作. 就像上面举例的访问私有方法私有字段, 甚至修改常量. 操作一些别人不让你操作的东西. 这里有一个示例:
    // java有个类Unsafe类, 听名字都很唬人, 不安全类
    // 这个类是Java的黑魔法, 之前官方说在JDK8以后要遗弃它, 可是现在到了最新的15,16都还存在
    // 很多优秀的框架或者功能都基于它完成, 比如NIO, Netty, CAS
    // 有人说这个类一半天使一半魔鬼
    // 众所周知Java自动内存管理没有指针
    // 那么Unsafe就可以解放你的天性, 随意申请内存, 指针都给你. 但这也是危险的
    /*这个类正常的方式肯定是没办法创建的, 因为构造函数是私有的.然后有人说不是有个getUnsafe()方法吗, 你去调用就知道会报错.它会检查调用者的类加载器不是Bootstrap就抛异常, 而我们自己编写的类肯定不是Bootstrap这样做也是因为安全性限制我们调用所以非要调用就只能通过反射生成这个类实例或者反射获取theUnsafe字段
    */
    public final class Unsafe {private static native void registerNatives();static {registerNatives();}private Unsafe() {}private static final Unsafe theUnsafe = new Unsafe();public static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException("Unsafe");} else {return theUnsafe;}}// 删除了其它的代码
    }
    /*还有其它骚操作我在工作中实际用到的一个, Gson工具在反序列化时候默认把所有数字都反序列化成double还弄些科学计数法之类的.改源码的话是个很简单的判断就搞定了但是源码不好改啊,所以就通过反射修改相关属性. 替换成自己的实现类
    */
    
  3. 确确实实大量需要用到反射的地方, 写框架. 框架之所以为框架, 就因为它只搭了一个架子, 很多具体的功能就需要框架的使用者去自己做. 这个时候写框架的人员是不知道框架使用者的类叫什么但是他又要创建对象并调用你的方法.
    // 定义接口
    public interface UserFunction { void userWork();}
    // 未来的流弊框架
    public class FrameWaxxd {public FrameWaxxd(){};public void frameDo() {// 1. do frame workd// code// 2. List<UserFunction> userFunctions = 获取所有实现UserFunction接口的类;// 3. 遍历创建对象调用userWork();// 4. do fram work}
    }
    

    上面的示例代码就很好的说明了反射的作用, 首先作为框架开发人员, 并不能知道使用者实现了多少个UserFunction接口, 也不知道使用者UserFunction接口实现类的类名, 所以框架开发者没有办法在框架开发阶段通过new 构造函数()的方式去调用, 这些类都是需要运行时确定的, 所以这里几乎是必须通过反射去调用, 结合实际的工作实例, 我们在Springboot开发中那些Controller或者Service我们并没有进行创建, 但是我们知道对象肯定是被创建了, 就是通过反射的方式进行创建.

  4. Java的注解大概率会用到反射, 注解上面的值需要反射获取. 这里就不举例了.
  5. 获取泛型也需要用到反射, Java的泛型实际不那么泛型, 是一种伪泛型, 只是在编译阶段有用, 可以用来检查代码, List<Integer>和List<String>是一样的类型, 因为在JVM里都是List, 而反射可以用来获取里面泛型类型. 有的时候也挺有用的.
  6. 还记得前文吗, JDK动态代理的目标方法调用实际就是基于反射

最后

这次好像没有最后了, 最后说一个int.class == Integer.class 结果是true还是false, 这个本质就是在问基本类型和包装类型的Class对象是不是同一个, 其实很简单, 明显不是同一个, 如果你理解了上文说讲的Class到底是个什么东西, Class是类的元数据, 保存了类的相关属性信息, 面向对象把事物抽象为类, 而在Java中类的抽象就是Class, Integer对象有各种各样的方法, 而int就只是一个数据类型, 由此它们肯定不是以同一个Class对象为蓝本创建的. 其它基础类型包装类型也类似. 那么另外一个问题int.class == Integer.TYPE吗? true. Integer.TYPE就是保存的int.class.

Java反射底层原理以及应用相关推荐

  1. java反射底层原理。

    Java反射机制是java的这门语言所独有的,这也是面试官喜欢问的知识点之一,我们不能仅仅从会使用反射这个类以及类中方法,来认定自己会这个知识点,我认为这太过于表面,不是我们学习java的正确的方式. ...

  2. java反射底层原理,总结到位

    1 进程 进程与程序 操作系统之中最为通用的概念就是「进程」.与此相关的面试题以及各种技术优化策略也层出不穷,足以够证明它对于理解操作系统中举足轻重的地位.事实上,通过「进程」,程序员可以更为直观的理 ...

  3. java底层原理书籍_不愧是阿里p8大佬!终于把Java 虚拟机底层原理讲清楚了,请签收...

    概述 JVM 的内存模型和 JVM 的垃圾回收机制一直是 Java 业内从业者绕不开的话题(实际调优.面试)JVM是java中很重要的一块知识,也是面试常问的问题之一,直至今天,仍然还有许多面试者在被 ...

  4. java反射原理_java反射原理是什么?java反射机制原理详解

    前面给大家介绍了一下什么是java反射机制,那么下面要给大家介绍的就是java反射机制的原理,那么它的原理究竟是怎样的呢?下面就通过下面来做一下详细的了解吧. 首先我们再来介绍一下java反射机制. ...

  5. java反射原理三种,java反射的原理、作用

    1.什么是反射,反射原理java反射的原理:java类的执行需要经历以下过程,编译:.java文件编译后生成.class字节码文件 加载:类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JV ...

  6. java反射的原理,作用

    什么是反射,反射原理 Java反射的原理:java类的执行需要经历以下过程, 编译:.java文件编译后生成.class字节码文件 加载:类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JV ...

  7. Java CAS底层原理

    Java CAS底层原理 Java CAS底层原理,这一篇就够了!!! CAS全称(Conmpare And Swap)比较并交换,是一种用于在多线程环境下实现同步功能的机制.CAS 操作包含三个操作 ...

  8. java反射机制原理,为什么需要反射,反射的作用

    最近在学java反射以及动态代理,好多博客都写的十分官方.就自己整合些资料.加深java反射机制的理解. 在说反射之前,我们要先了解动态语言和静态语言的概念: 动态类型语言 所谓动态类型语言,就是类型 ...

  9. 10分钟看懂, Java NIO 底层原理

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 写在前面 ...

最新文章

  1. Python爬虫9大入门学习知识点
  2. HDU 4286 Data Handler [栈,双端队列]
  3. Android开发实践:屏幕旋转的处理
  4. input常用输入框限制
  5. 【正一专栏】第1球和第500球价值一样
  6. ensp支持哪个版本virtualbox_如何在 Fedora 中安装 VirtualBox
  7. [渝粤教育] 西北大学 仪器分析 参考 资料
  8. 经典面试题(22):以下代码将输出的结果是什么?
  9. [.NET 三层架构(ADO.NET)+Web Service+多语言]WinForm三层架构重新实现TaskVision,外加WebService提供数据和多语言...
  10. jquery.datatables使用
  11. 【数电】(一) 进制转换编码 原码,反码,补码
  12. [CareerCup] 14.1 Private Constructor 私有构建函数
  13. Intel Quick Sync Video Encoder 2
  14. Windows驱动开发WDM (2)- 一个简单的WDM驱动程序
  15. 分布式定时任务开源方案
  16. CRM项目第一天(2021-12-16)1
  17. php作业批改系统源码,在线|WEB作业批改系统
  18. java里面怎么开根号_新手求解呀!!!!!!!怎么开根号,为嘛我开不了
  19. PX4多传感器优先级判断
  20. ppt 计算机图标不见了,我PPT的图标变成这样了,为什么

热门文章

  1. 天线的S11,S12,S21,S22参数解析(个人理解仅供参考)
  2. 从天宇核心团队出走看山寨“春秋”
  3. FII-PRA006/010开发板硬件实验一
  4. 宁德时代打响增长保卫战
  5. 中集集团高科技企业中集飞瞳,贯彻国家人工智能与实体经济深度融合战略,成熟AI产品智能航运智能化港航智慧港口智能铁路智能多式联运
  6. solid works土炮模型建模
  7. 如何清除PDF中的水印?PDF删除水印的方法
  8. 微服务分布式架构中,如何实现日志链路跟踪?
  9. docker之cgroup、compose
  10. 功能连接分析方法及其解释缺陷的综述