分享免费的编程资源和教程

网站首页 > 技术教程 正文

Java反射与反射优化 java反射机制实现

goqiw 2024-10-02 22:07:00 技术教程 12 ℃ 0 评论

先说一下Java的内存模型,也就是java虚拟机在运行时的内存。运行时的内存分为线程私有和线程共享两块。

线程私有的有程序计数器,虚拟机栈,本地方法栈,线程共享的有方法区(包含运行时常量池),java堆。

我们平时说的java内存分为堆和栈,分别对应的是上面的堆和虚拟机栈。

程序计数器:java允许多个线程同时执行指令,如果是有多个线程同时执行指令,那么每个线程都有一个程序计数器,在任意时刻,一个线程只允许执行一个方法的代码,每当执行到一条java方法的代码时,程序计数器保存当前执行字节码的地址,若执行的为native方法,则PC的值为undefined。

虚拟机栈:描述了java方法执行的内存模型,每个方法在执行的时候都会创建出一个帧栈,用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每个方法的从调用到完成,都对应着一个帧栈从入栈到出栈的过程。

本地方法栈:为虚拟机使用到的Native方法提供内存空间,本地方法栈使用传统的C Stack来支持native方法。

java堆:提供线程共享时的内存区域,是java虚拟机管理的最大的一块内存区域,也是gc的主要区域,几乎所有的对象实例和数组实例都要在java堆上分配。java堆的大小可以是固定的,也可以随着需要来扩展,并且在用不到的时候自动收缩。

方法区:存放已被虚拟机加载的类信息,常量,静态变量,编译器编译后的代码等数据。

运行时常量池:存放编译器生成的字面量和符号引用。

1.反射是什么

反射是java语言的特性之一,它允许运行中的程序获取自身的信息,并且可以操作类和对象的内部属性。java反射框架主要提供以下功能:

1.在运行时判断任意对象所属的类;

2.在运行时构造任意一个类的对象;

3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);

4.在运行时调用任意一个对象的方法;

2.反射的用途

1.我们在使用ide,输入一个对象,并想调用它的属性和方法的时候,一按点号,编译器就会自动列出它的属性和方法,这里就会用到反射。

2.通用框架,很多框架都是配置化的(比如Spring通过xml配置Bean或者Action), 为了保证框架的通用性,可能需要根据不同的配置文件加载不同的对象或者类,调用不同的方法,这个时候就需要反射,运行时动态加载需要加载的对象。

3.反射的基本运用

上面提到了提供的一些功能,获取类,调用类的属性或者方法。

3.1.获取类(Class)对象

方法有三种:

  • 使用Class的静态方法

  • 直接获取一个对象的Class

  • 调用某个对象的getClass方法

3.2.判断是否为某个类的实例

一般我们使用instanceof,也可以使用Class.isInstance(obj)

3.3.创建实例

用反射来生成对象的方式主要有两种。

使用Class.newInstance方法

这个方法最终调用的是无参数的构造函数,所以如果对象没有无参数的构造函数就会报错了。使用newInstance必须要保证:1、这个 类已经加载;2、这个类已经连接了。newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。当然构造方法不能是私有的。

  • 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。当然构造方法不能是私有的。

3.4.获取方法(Method)

获取某个Class对象的方法集合,主要有以下几种方法。

可以获取自身的公有,保护,默认,私有的方法,但是不包括继承实现的方法。

可以获取公有和继承实现的方法。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

可以获取特定的自身的公有,保护,默认,私有的方法,但是不包括继承实现的方法。

public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

可以获取特定的公有和继承实现的方法。

3.5.获取构造器信息(Constructor)

通过Class对象的getConstructor方法。

3.6.获取成员变量信息(Field)

主要是这几个方法,在此不再赘述:

getFiled: 访问公有的成员变量

getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量

getFileds和getDeclaredFields用法同上(参照Method)

3.7.调用方法(invoke)

当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。

invoke方法用来在运行时动态地调用某个实例的方法。invoke方法会首先检查AccessibleObject的override属性的值。AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。override的值默认是false,表示需要权限调用规则,调用方法时需要检查权限;我们也可以用setAccessible方法设置为true,若override的值为true,表示忽略权限规则,调用方法时无需检查权限(也就是说可以调用任意的private方法,违反了封装)。

3.8.利用反射创建数组

数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。

3.9.泛型的处理

Java 5中引入了泛型的概念之后,Java反射API也做了相应的修改,以提供对泛型的支持。由于类型擦除机制的存在,泛型类中的类型参数等信息,在运行时刻是不存在的。JVM看到的都是原始类型。

4.反射的优化

4.1.善用API

比如,尽量不要getMethods()后再遍历筛选,而直接用getMethod(methodName)来根据方法名获取方法。

4.2.缓存大法好

比如,需要多次动态创建一个类的实例的时候,有缓存的写法会比没有缓存要快很多。还有将反射得到的method/field/constructor对象做缓存。

为什么?当然是因为forName太耗时了。Cache请自行实现。

4.3.尽量使用高版本JDK

4.4.使用反射框架

例如joor,或者Apach](https://github.com/jOOQ/jOOR),或者Apach) Commons BeanUtils,JAVAASSIST。

4.5.ReflectASM通过字节码生成的方式加快反射速度

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

java的源码在源代码和编译后的类中表现是不一样的。

下面列出java类型对应的类型描述符:

字段描述符示例

方法描述符示例

学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群346942462,我们一起学Java!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表