从java虚拟机基础看java类的反射机制

版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎批评指正。 https://blog.csdn.net/Sandyxin5208/article/details/82989362

java虚拟机相关基础

    任何一种语言编写的程序,运行在不同的系统上,最终都需要被编译成为机器可以识别的机器码(也就是01010…1这种二进制数据)。对于java语言而言,虚拟机起到了机器语言与该语言自身的桥梁作用,虚拟机可以识别字节码,针对不同的操作系统,可以有适应不同机器、操作系统的虚拟机,如此一来,程序员编写的程序就可以通过虚拟机无差异的运行在不同系统上,而不是针对不同的机器系统要求分别编写符合要求的代码。
     java源代码在被编译后生成.class文件,该文件是按照虚拟机规范编写的字节码(8比特位为单位编译后的文件),最终文件运行在虚拟机上,虚拟机以字节为单位解释执行,这种先编译成字节码,再由虚拟机对字节码进行动态解析运行的语言,相较于其他编译语言(以c++为例,是编译型语言,通过编译连接后是可以直接运行在机器上的),它历经两个过程,一半编译一半解释,称其为半解释型语言。
    class文件作为一种完全按照虚拟机规范编译生成的文件,在被虚拟机加载后才能进入运行时数据区的方法区中,在这里需要区分两个概念:class文件的结构;虚拟机运行时数据区。
    class文件是一组以8位字节为基础单位的二进制流,根据虚拟机规范,采用一种类似于C语言结构体的伪结构来存储数据,这种结构只有两种数据类型:无符号数和表。由这两种数据结构构成了class文件的各种数据项:魔数、常量池、访问标志、类索引、父类索引、接口索引、字段表集合、方法表集合、属性表集合等。这些数据项按照虚拟机的规范紧凑的排列在class文件中,不使用任何分隔符。
    虚拟机运行时数据区域是指虚拟机在运行java程序时,会将虚拟机自身所管理的内存划分为若干用途各不相同的数据区域,具体数据区域如下图所示:
java运行时数据区
如图所示,虚拟机运行时的数据区可以划分为:方法区、虚拟机栈、本地方法栈、堆、程序计数器。

    在对class文件结构和java程序运行时数据区结构有了一定了解之后,我们再来看一下,class文件是如何在虚拟机中“动起来”的。

    class文件中描述的各种信息,最终都需要加载到虚拟机中之后才能运行和被使用,这就涉及到类的加载机制。整个类加载过程包含加载、验证、准备、解析、初始化、使用和卸载,简单来讲,加载阶段根据类的全限定名获取其对应的字节流,将此字节流的静态存储结构转换为虚拟机的运行时数据结构(其实就是运行时数据区的方法区),在方法区中生成代表该类的java.lang.class对象;验证阶段主要是确保Class文件的字节流中包含的信息符合当前虚拟机要求;准备阶段正式为类变量分配内存并设置类变量初始值(数据类型的零值);解析阶段是指虚拟机将class文件中常量池的符号引用替换为直接引用的过程;初始化阶段,开始真正执行类中定义的java程序代码。

java的反射机制

     验证、准备与解析三个阶段就相当于完全编译语言中的连接阶段,在java中这个连接阶段是在类加载过程中完成的,而编译语言中是在编译阶段就完成的。前面讲了那么多虚拟机相关的内容,似乎与虚拟机关系不大,非也非也。通过前面内容我们大体知道了源文件被编译成字节码,字节码被虚拟机使用,因此程序正常运行的前提是类文件被加载,在源文件被编译时就已经确切知道类的存在,这就是典型的RTTI(运行时类型确定),利用class对象,可以了解对象和类运行时的信息。但是反射机制打破了这种在编译时必须知道具体类信息之后才可以使用该类的约束,Class类与java.lang.reflect类库一起对反射的概念进行了支持,可以在运行时动态获取相关类信息,例如可以利用反射类库的一些类来实现创建对象(Constructor类),具体我们来看一个例子:

package javaTest;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.swing.JOptionPane;

public class ReflectTest1 {
	public ReflectTest1() {
	String classinfo=JOptionPane.showInputDialog(null,"输入类全路径");
	try {
		Class<?> class1=Class.forName(classinfo);
		Method[] methods=class1.getDeclaredMethods();
		for(Method med:methods) {
			System.out.println(med.toString());
		}
		System.out.println("************************8");
		Field[] fields=class1.getDeclaredFields();
		for(Field fid:fields) {
			System.out.println(fid.toString());
		}
	}catch(ClassNotFoundException e) {
		e.printStackTrace();
	}
	System.out.println("程序顺利结束。");
}
	public static void main(String[] args) {
		new ReflectTest1();
	}
}

运行以上代码,结果如下:
程序运行结果

可以看到,程序正常运行,这说明编译是通过的,现在我们输入类的全路径:
输入类全路径

最终控制台输出如下:

public void javax.swing.JFrame.remove(java.awt.Component)
public void javax.swing.JFrame.update(java.awt.Graphics)
protected java.lang.String javax.swing.JFrame.paramString()
public javax.accessibility.AccessibleContext javax.swing.JFrame.getAccessibleContext()
public java.awt.Container javax.swing.JFrame.getContentPane()
public javax.swing.JRootPane javax.swing.JFrame.getRootPane()
public static boolean javax.swing.JFrame.isDefaultLookAndFeelDecorated()
public void javax.swing.JFrame.setLayout(java.awt.LayoutManager)
public java.awt.Graphics javax.swing.JFrame.getGraphics()
public javax.swing.TransferHandler javax.swing.JFrame.getTransferHandler()
public void javax.swing.JFrame.repaint(long,int,int,int,int)
public void javax.swing.JFrame.setTransferHandler(javax.swing.TransferHandler)
protected void javax.swing.JFrame.addImpl(java.awt.Component,java.lang.Object,int)
protected javax.swing.JRootPane javax.swing.JFrame.createRootPane()
public int javax.swing.JFrame.getDefaultCloseOperation()
protected boolean javax.swing.JFrame.isRootPaneCheckingEnabled()
public void javax.swing.JFrame.setDefaultCloseOperation(int)
public static void javax.swing.JFrame.setDefaultLookAndFeelDecorated(boolean)
protected void javax.swing.JFrame.setRootPane(javax.swing.JRootPane)
protected void javax.swing.JFrame.setRootPaneCheckingEnabled(boolean)
protected void javax.swing.JFrame.frameInit()
public java.awt.Component javax.swing.JFrame.getGlassPane()
public javax.swing.JMenuBar javax.swing.JFrame.getJMenuBar()
public javax.swing.JLayeredPane javax.swing.JFrame.getLayeredPane()
public void javax.swing.JFrame.setContentPane(java.awt.Container)
public void javax.swing.JFrame.setGlassPane(java.awt.Component)
public void javax.swing.JFrame.setJMenuBar(javax.swing.JMenuBar)
public void javax.swing.JFrame.setLayeredPane(javax.swing.JLayeredPane)
protected void javax.swing.JFrame.processWindowEvent(java.awt.event.WindowEvent)
public void javax.swing.JFrame.setIconImage(java.awt.Image)
***********************************************************************************************
public static final int javax.swing.JFrame.EXIT_ON_CLOSE
private static final java.lang.Object javax.swing.JFrame.defaultLookAndFeelDecoratedKey
private int javax.swing.JFrame.defaultCloseOperation
private javax.swing.TransferHandler javax.swing.JFrame.transferHandler
protected javax.swing.JRootPane javax.swing.JFrame.rootPane
protected boolean javax.swing.JFrame.rootPaneCheckingEnabled
protected javax.accessibility.AccessibleContext javax.swing.JFrame.accessibleContext
程序顺利结束。

     通过以上例子我们可以体会到反射的强大之处:可以在运行时动态加载类,使用类的相关信息,即便是利用Class类动态加载的类不存在,程序也可以捕获处理异常,不会影响程序的正常运行。
再次列举一个例子帮助理解反射的作用:
     在合作开发中有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但是第二个程序员并没有完成其类的构建,这种情况下第一个程序员的代码就不能通过编译。但是利用反射机制,第一个程序员就可以完成其代码的编译,从而可以进行相关测试,如此可以大大的减小程序员之间的相互约束。

Class类与反射类库详解

Class类

猜你喜欢

转载自blog.csdn.net/Sandyxin5208/article/details/82989362