Java编程思想(十六) —— 联系JVM再谈Class

编程思想这个专栏停了好久了,主要是把精力放在了其他知识上,现在继续补上。


前面两篇写到RTTI和简单的反射介绍,先回顾一下:

RTTI,运行时类型信息,多态的应用,类型转换其实是发生在运行期间。


Class对象:

编程思想讲到的定义,Java使用Class对象来执行其RTTI,类是程序的一部分,每个类都有一个Class对象,其实每编写和编译一个新类,就会产生一个Class对象,其实这个对象时被保存在同名的.class文件中的。生成这个类对象,其实是JVM(Java虚拟机)使用了“类加载器”的子系统。


补充一下百度的定义:

在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。最后一句是重点。


而class是一个类名的标志,class A,A是一个类,而Class是一个对象。


是不是很模糊,这些概念,Class哪里冒出来,事实上还有一个ClassLoader。为了搞清楚关系,这种肯定是虚拟机方面的除了《深入理解JVM》,特定去问了RednaxelaFX大哥还有什么书籍比较好。他推荐了《Inside the Java Virtual Machine》。看了五章之后,大体有个方向,这本书也推荐其他人,不是太难懂,在我大二的时候看一本红色封面的JVM的书看得真心蛋疼,一来就是代码直接转汇编,所以说,合适的时候看合适的书是很重要的。



《Inside the Java Virtual Machine》一书笔记:

先把握整个体系结构的4个技术:

语言
class文件
api
jvm

书中有点分析得很有道理,所谓的跨平台,是因为class文件 ,二进制文件 ,但是像c++ c这些, 编译和链接之后的二进制文件是在系统直接运行,为目标处理器的机器语言
不具有跨平台性,而class文件是虚拟机的“机器语言”。

而为了处处能运行的原因,使得java图形界面的api比较复杂,为什么awt和swing没有广泛的使用开来,个人认为一个是需要对应的java环境,一个是开发效率问题。

还有很有趣的一点:在dos窗口运行的java文件时,java HelloWorld的时候,是不是从来没有想过java是什么含义,其实java就是叫系统运行JVM,加载HelloWorld后运行其main方法。


现在看一下JVM的体系结构图:


                                  Java编程思想(十六) —— 联系JVM再谈Class_第1张图片

这个图中的东西又可以和Java编程思想(一) —— 一切都是对象及内存分配联系起来。

每个虚拟机实例的方法区和堆由该实例里面的所有线程共享,堆存放对象。
新线程的创建 会有自己的pc寄存器(程序计数器)以及一个java栈(保存线程的局部变量 参数  返回值 运算中间结果)。
java栈由栈帧stack frame或者说是帧frame组成 ,线程调用一个方法,压入一个新的栈帧,返回时弹出。

到了学习的重点所在,对于每个被装载的类型,虚拟机都会相应的为其创建Class类的实例,
Class.forName(java.lang.Object) 的到代表Object的Class对象的引用 。
如果无法将请求的类型转载进当今命名空间,forName会抛出classnotfoundexception。

另外一个方法,getClass 也可以拿到类的Class对象。Class对象中有getName和getClassLoader方法。
直接上例子:
public class TestClass {
	public static void main(String[] args) {
		Class c = Object.class.getClass();
		Class c1 = Object.class;
		Object o = new Object();
		Class oc = o.getClass();
		Class ct = null;
		try {
			ct = Class.forName("java.lang.Object");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		TestClass t = new TestClass();
		Class tclass = t.getClass();
		System.out.println(tclass.getClassLoader());
		System.out.println(c.getClassLoader());
		System.out.println(oc.getClassLoader());
		System.out.println(ct.getClassLoader());
		System.out.println(c1.getClassLoader());
		System.out.println(tclass.getName());
		System.out.println(c.getName());
		System.out.println(oc.getName());
		System.out.println(ct.getName());
		System.out.println(c1.getName());
	}
}
output:

sun.misc.Launcher$AppClassLoader@da4a1c9
null
null
null
null
TestClass
java.lang.Class
java.lang.Object
java.lang.Object
java.lang.Object

两个方法返回的是已装载类型的信息,getName拿到的是全限定名 ,类所属包+类名,如果是Object类,其实就是java.lang.object。
object.class指的是Class对象引用了,装载的为object,再getClass的时候,装载的就变成了Class引用,所以getName拿到的就是Class了,而Object.class.getName()装载的是Object,所以getName拿到的全限定名为Object。

类装载器有两种:启动类 和 自定义类 ,前者是jvm定义好的  ,后面是自己写的。
getClassLoader 返回classLoader引用 ,如果为启动类装载器 ,则为null。



new与Class.newInstace的区别。
都是创建实例,具体的不同是什么?
参考了:http://www.coderanch.com/t/516460/java/java/newInstance-difference-bet
T. Huy Nguyen说的
An obvious difference is that the class's fully qualified name can be dynamically constructed/read from file/changed when you use "newInstance".

"newInstance" also requires the Java class to have an no-arg constructor, else you will get error at runtime. If you do the same with "new", you will immediately get error at compile time.

明显的区别就是你用newInstance的时候,class的名字可以动态的从文件/变化中动态地构造/读取。
newInstance要求Java类需要有一个无参构造器,不然会出现运行期错误。同样的情况在new中,编译期间就出现错误了。

总结:
对于每个被装载的类型,虚拟机都会相应的为其创建Class类的实例。
虚拟机在方法区会存储类型信息:
类型的全限定名。
直接超类的全限定名。
类型为接口类型还是类类型。
访问修饰符。

而这些东西,在编程的时候怎么拿到,就靠Class类了,接下来你会问,拿到又怎样呢?
用处就是反射,光靠一个类名,我可以拿到很多信息,也可以对其进行操控,这就是作用所在了。

你可能感兴趣的:(Java,Java编程思想)