java基础之反射机制

一、从底层理解java反射机制

了解java程序从编译到执行的全过程,从底层理解学习,java代码编译和执行主要包含以下3个重要机制,有助于我们更好的理解学习程序的运行机制和原理。

(一)java源码编译机制

1、java源码编译是通过java源码编译器完成,将源码文件(.java)编译成字节码文件(.class即二进制文件)。

2、java源码编译由以下三个过程组成:

(1)分析和输入到符号表;

(2)注解处理;

(3)语义分析和生成class文件。

3、class文件主要包括两部分:

(1)常量池:记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等);

(2)方法字节码:类中各个方法的字节码。

(二)类加载机制

1、JVM(java虚拟机)的类加载是通过ClassLoader及其子类来完成的,默认采用的是双亲委派机制。

2、双亲委派机制:类的加载是自顶向下加载,检测该类是否加载是从下往上检查是否已加载,即就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次往上传递加载请求 (本质上就是loadClass函数的递归调用)。因此,所有的加载请求最终都会传到顶层的启动类加载器中。如果父类加载器已完成这个类加载请求,就成功返回;只有当父类加载器无法完成此加载请求时,子加载器才会尝试自己去加载。

注意:双亲委派机制保证了类加载过程的安全性,避免黑客利用同名类植入程序。

3、JVM预定义的三种类型的类加载器:

(1)启动(Bootstrap)类加载器:负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类;

(2)扩展(Extension)类加载器:扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的,它负责将 <JAVA_HOME >/lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库加载到内存中。

(3)系统(System)类加载器:系统类加载器是由 Sun的AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,它负责将用户类路径(java -classpath或-Djava.class.path变量所指的目录,即当前类所在路径及其引用的第三方类库的路径下的类库 加载到内存中。开发者可以直接使用系统类加载器。

(4)Custom ClassLoader(自定义类加载器):程序员可以根据自身需要自定义类加载器。

注意:JVM主要在程序第一次主动使用类的时候,才会去加载该类。也就是说,JVM并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。

注意:上述三个JDK提供的类加载器虽然是父子类加载器关系,但是没有使用继承,而是使用了组合关系。

  理解ClassLoader类对象的getParent()方法:获得他的上级类加载器。

(三)类执行机制

1、JVM是基于栈的体系结构来执行class字节码文件。

2、当线程创建后,JVM根据字节码为类的各种信息分配内存区域,分别为程序计数器、栈、堆和方法区。

(1)程序计数器(PC)存放下一条要执行的指令在方法内的偏移量即当前线程所执行的字节码的行号指示器。

(2)堆用于存储所有类实例和数组对象。

注意:堆内存中类实例有指向方法区中类方法的指针。

(3)栈用来存放基本类型的对象和自定义对象的引用。

(4)方法区又称为静态区,和堆一样被所有线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

(四)实例说明java源码编译执行的过程

1、在编译好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为AppMain.class的二进制文件,将MainApp的类信息加载到运行时数据区的方法区内,这个过程叫做MainApp类的加载。

注意:可以通过指令:-verbose:class   可以观察类的具体加载过程。

注意:static 语句块在加载后只执行一次;动态语句块(dynamic语句块即格式:{  //代码语句  })每次new新的对象都会执行,等同于构造方法中的语句。

2、然后JVM找到AppMain的主函数入口,开始执行main函数。

3、main函数的第一条命令是Animal  animal = new Animal("Puppy");就是让JVM创建一个Animal对象,但是这时候方法区中没有Animal类的信息,所以JVM马上加载Animal类,把Animal类的类型信息放到方法区中。

4、加载完Animal类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Animal实例分配内存, 然后调用构造函数初始化Animal实例,这个Animal实例持有着指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。

5、当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息的方法表,获得printName()函数的字节码的地址。

6、开始运行printName()函数。

(五)反射机制原理

1、反射机制原理:在程序运行状态下,可以动态获取或者动态调用任意一个类的方法和属性。当一个类被加载以后,Java虚拟机就会自动产生一个对应的Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的类的方法、成员变量以及构造方法等信息。

注意:java反射本质:通过Class类实例获取指定类的字节码信息,进而获取或者调用该类的属性和方法。底层就是Class实例存储指向方法区中存储着该类方法的指针,进而调用该类的方法。

2、获取Class类实例的三种方法:

(1)利用Object的getClass()方法,返回Object运行时的实例。

(2)利用类字面常量:通过类名.class获取class对象。

(3)利用Class类的forName(String 类全名)方法获取指定类的实例,其中,类全名=包路径+类名。(常用方法)

总结:第一种已经创建了对象,那么这个时候就不需要去进行反射了,显得有点多此一举。第二种需要导入类的包,依赖性太强。所以我们一般选择第三种方式。

3、利用java反射机制获取指定类的方法并调用该方法:

(1)getMethod(String 方法名,Class 方法参数)方法返回指定方法名的方法对象。

(2)getMethods()方法返回指定类的方法对象数组。

(3)Method类的invoke(Object obj,Object... args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

注意:调用本质:Method类实例即方法对象存储着指向该类内存方法区方法的指针,进而直接调用该方法。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一位远方的诗人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值