JVM(Java Virtual Machine)
1-1、ClassLoader基本概念
Java程序并不是一个可执行文件,而是由许多独立的类文件组成的,每一个文件的对应一个Java类,而且,这些类文件并非全部装入内存,而是根据程序需要逐渐装入。
ClassLoader是JVM实现的一部分,它包括bootstrap classloader(启动类加载器)。
ClassLoader在JVM运行是加载Java核心API,以满足Java程序最基本的需求,其中包括用户自定义的ClassLoader。
用户自定义的ClassLoader:是指通过Java程序实现的2个ClassLoader:
- ExtClassLoader:加载Java的扩展API,即/jre/lib/ext中的类
- AppClassLoader:加载用户机器上CLASSPATH设置目录中的Class
1-2、ClassLoader加载流程
当运行一个程序时:
- JVM启动
- 运行bootstrap classloader
- 该ClassLoader加载Java核心API和ExtClassLoader、AppClassLoader
- ExtClassLoader加载扩展API & AppClassLoader加载CLASSPATH目录下定义的Class
一个类加载的过程使用一种父类委托的模式,这样的的原因如下:
- 避免重复加载
- 考虑到安全因素
1-3、一些重要的方法
- loadClass:是ClassLoader的入口点
- defineClass:接受由原始字节组成的数组,并把它转换成Class对象
- findSystemClass:从本地文件系统装入文件
- resolveClass:可以不带解析装入类,也可以带解析装入类
- findLoadedClass:充当一个缓存,当请求loadclass装入类时,调用该方法查看ClassLoader是否已经装入这个类
- findClass: 从本地文件系统使用实现的类装载器装载一个类
- getSystemClassLoader:如果覆盖findClass或loadClass,该方法能以实际的ClassLoader对象来访问系统ClassLoader
- forName:用来加载class
JVM加载类的时候,需要经过三个步骤:
- 装载:找到相应的class文件,读入JVM
- 连接:
- 初始化:1-验证class是否符合规格;2-为类变量分配内存的同时是设置默认初始值;3-解释(可选)
2-1、 JVM使用的字符
在Java中字符只以一种形式存在,那就是Unicode(不选择任何特定的编码,直接使用它们在字符集中的编号,这是统一的唯一方法)
在Java中指的是,JVM中,内存中,在代码里声明的每一个char、String类型的变量中
例如:
编写如下代码
public class T1 {
public static void main(String[] args) {
char achar = '爱';
System.out.println(achar);
System.out.format("%x",(int)achar);//%d 则输出十进制
System.out.println();
char bchar = 0x7231;//十六进制
System.out.println(bchar);
}
}
输出结果
爱
7231
爱
在内存中的相应区域,这个字符就表示为0x7231,同时用Unicode码也找到了这个字符
只要正确读入一个字符,那么它在内存中的表示形式就是唯一的,使用Unicode码
2-2、字符编码方案
JVM的这种约定使得一个字符分为两部分:
- JVM内部:统一使用Unicode表示,当这个字符被从JVM内部移到外部(即保存到系统的文件系统中的一个文件的内容时)就进行编码转换,使用具体的编码方案
- OS的文件系统:所以的编码转换只发生在边界的地方,JVM和OS的交界处(各种输入/输出流即Reader、Writer类起作用的地方)
Java中文件与流参考这里:http://blog.youkuaiyun.com/wee_mita/article/details/71157964
所有的I/O基本上可以分为2大阵营:
- 面向字节的输入输出:如果面向字节,那么这类工作要保证系统中的文件二进制内容和读入JVM内部的二进制内容一致,不能变化任何0和1的顺序【这种输入/输出方式适合读入视频音频文件,或者任何不需要变换的文件内容】
- 面向字符的输入输出:如果面向字符,那么这类工作要保证系统中的文件的字符和读入JVM的字符要一致
例如:
假设在文件系统中有一个GBK保存的文本文件,其中保存了一个汉字,不论这个汉字的GBK码是什么,当我们使用面向字符的I/O把它读入内存并保存在一个char型变量中时,不关心这个char变量具体的二进制内容是什么,只要这个字符读进来之后仍然是该汉字就行。
从中可以看出,面向字符的I/O(Reader和Writer类)实际上隐式地做了编码转换,在输出时,将内存中Unicode字符使用系统默认的编码方式进行编码,在输入时,将系统已经编码过的字符使用默认编码方案进行还原
Reader和Writer只进行默认的编码转换,即GBK<—>Unicode,如果使用其他编码方案的文件,必须采用编码转换(字符与字节之间的转换,InputStreamReader和OutputStreamWriter)
3-1、关于i++
i++和++i的区别在于,前者是程序完毕后自增,后者是程序开始前自增;
int i= 0 ;
i = i++ + ++i;
- 执行i++,并不自增,i=0
- 执行++i,执行自增,i=1
- 此时,程序执行完毕,执行i++的自增,i=2
- i = 0+2
int j = 0;
j = j++;
Java中用了中间缓存变量的机制,j = j++;转变为如下:
temp = j;
j = j+1;
j = temp;
所以j依然为0;