一、区分jre和jdk
jre:Java runtime environment,Java运行时环境。
其中包含了运行一个Java程序所需要的基本类库,以及最重要的平台jvm(Java虚拟机)
无论在什么设备上面,只要需要运行Java程序,就必须要有jre
jdk:Java Development Kit,Java开发工具包。
jdk中包含了一系列开发Java程序所需要的一系列工具,例如:java.exe,javac.exe,
javap.exe(反编译工具) ,jar.exe(归档工具);除了上述工具之外,jdk中也包含了jre
二、Java语言的优点:
1.提供了一种解释执行的环境(指的是jvm)
1)加速开发 ,2)一次编译,到处运行,3)简单易用的多线程操作。
2.提供了一种更简单的编程方式
1)更健壮 robust :鲁棒性,健壮性,2)纯面向对象编程 object oriented programming
① JVM(Java Virtual Machine,Java虚拟机)
② GC(Garbage Collection,垃圾回收器)
③ 代码安全验证机制
三、Java语言具有编译型语言的特点:源代码不能直接执行,需要经过编译生成文件。
Java语言也具有解释型语言的特点:字节码的执行过程不会再次生成文件,属于解释执行过程。
Java可以看做一种半编译、半解释型的语言。
四、GC(garbage collection)垃圾回收器
1.Java中取消了手动内存分配机制
2.Java系统线程自动管理内存
被GC认定为是垃圾的标准:是一个没有任何引用指向的对象
GC本质上就是一个守护线程(精灵线程、后台线程),随着JVM虚拟机的启动而启动,工作在后台。
GC在工作期间,会按照一定的间隔或条件,关注内存的使用情况。只会在它认为有必要的时候进行垃圾内存 的回收和释放。
注意,GC只会回收堆区中的内存,不会关心栈区中的内存使用情况。
我们可以调用下面的两个方法【建议】GC进行垃圾回收:
java.lang.System.gc();
java.lang.Runtime.gc();
常用的垃圾回收算法:标记清理法,内存搬移法。
可以修改eclipse.ini文件的xms 和xmx 堆内存分配往大里调;
五、双亲委托机制/双亲委派机制
一个Java程序是由很多类组成的,在程序中需要使用到某个类的时候,必须先把它加载到JVM内存中。
什么时候算是需要用到某个类:
- 使用java.exe工具启动某个Java类
- 需要创建该类的对象
- 访问类中的静态变量、静态方法
- 使用Class.forName()获取某个类的类镜像
类本身是一种比较抽象的概念,在Java程序声明周期各个阶段,有不同的存在形式。
加载之前:就是磁盘中的一个.class字节码文件。.class文件就是.java源文件经过编译生成的结果。
加载之后:变成JVM内存中一个Class类型的对象。
Class是什么?
类:描述现实生活中一组具有相同特点的事物
所有的Java类本身是不是一种具有相同特点的事物?
所有的类都有修饰符、类名、包名、变量、方法、构造器…
也可以用一个Java类来描述“所有的Java类”这一类事物。
类加载:读取一个.class字节码文件中保存的十六进制的类的描述信息,根据这些信息在JVM内存中对应地创建一个Class对象。一个Class对象就代表Java中的一个类。
只有在内存中存在一个类对应的Class对象,在程序中才能对这个类进行各种操作。
类加载器:类加载过程的执行者。
Java中的类加载器一共有三种:
类加载器名称 | 加载路径 | 说明 |
---|---|---|
BootstrapClassLoader 启动类加载器/引导类加载器 | %JAVA_HOME%\jre\lib | 负责加载系统中的核心类库,使用C++编写,本身就是JVM的一部分,用户不能直接调用。 |
ExtClassLoader 扩展类加载器 | %JAVA_HOME%\jre\lib\ext | 负责加载系统中的扩展类,用Java编写的。 |
AppClassLoader 本地类加载器/应用类加载器 | %CLASSPATH% | 负责加载用户自己编写的Java类,用Java编写的。 |
上述的三种类加载器虽然加载的内容不同,但是并非完全没有关系。
【启动类加载器】是【扩展类加载器】的父加载器。
【扩展类加载器】是【本地类加载器】的父加载器。
所谓的双亲委托机制指的是Java中默认的也是推荐的类加载的方式,但是并不强制要求。
如果我们自己定义类加载器,是可以破坏双亲委托模型的,但是不建议。
指的是每一个类的加载过程都分为两个重要的阶段:
1、向上委派
每种加载器都有自己的缓存区,保存已经加载过的类信息。如果某个类已经被加载过,那么一定能够在缓存区中找到对应的记录。直接取出使用即可,不需要重复加载。
假设,现在有一个自己编写的Java类HelloWorld需要被加载。
首先【本地类加载器】会先接收到加载请求。
但是它不会立即执行加载,它会先查看自己的缓存区,看之前是否加载过该类。
如果找到了加载的记录,就可以直接拿出来使用,整个加载过程结束。
如果没有找到加载记录,说明本加载器从来没有加载过这个类。这时候,会向上委派。
向上委派指的是把加载请求委托给了父加载器【扩展类加载器】。
【扩展类加载器】接收到委托,也不会立即执行加载。而是先查看自己的缓存区,如果之前已经加载过该类则直接返回,类加载成功。如果自己的缓存中没有,会把加载请求继续向上委派。
【启动类加载器】接收到委托,也不会立即执行加载,也是先查看自己的缓存区。如果有,则直接返回。
如果启动类加载器的缓存区中都没有找到加载记录,那就说明整个系统是第一次加载这个类。
向上委派的整个过程其实就是在逐级查询缓存区,判断是否有任何一个类加载器加载过现在要加载的类。如果找到就直接返回使用,避免重复加载。如果启动类加载器缓存区中不存在类信息,则向上委派失败。开始执行向下加载过程。
2、向下加载
顺序和向上委派的过程是相反的。
从上到下尝试加载,谁能加载,就由谁加载。
尝试加载:查找自己的类加载路径下是否有HelloWorld.class文件。
首先,【启动类加载器】会先查看自己的类加载路径中是否存在保存有该类描述信息的字节码文件(HelloWorld.class),如果有则执行加载并将该类信息保存到缓存区中,类加载结束(成功)。
如果在自己的类加载路径下没有找到指定的字节码,则向下通知子加载器【扩展类加载器】。
【扩展类加载器】收到通知,会查看自己的类加载路径中是否存在该字节码,如果有则直接执行加载并将结果放入缓存区,类加载结束(成功)。
如果没有,则继续向下通知子加载器【本地类加载器】。
这时候【本地类加载器】才会自己尝试执行加载。它会到%CLASSPATH%下查找是否有对应的字节码文件,如果有则执行加载并将结果放入缓存,类加载结束(成功)。
如果执行完了向上委派和向下加载完整过程,最后在本地类加载器这里都没有找到对应的字节码文件,就代表整个类加载失败。抛出异常:ClassNotFoundException
思考:实际上,Java程序中大部分自定义的Java类最终还是要通过AppClassLoader执行加载,那何必再费这么大劲折腾一趟?
双亲委托机制主要目的是防止一个类被重复加载,主要指的是系统级别的类。主要是为了Java的安全性考虑。
假设一个场景:java.lang.System属于系统级别的类,本身会被BootStrapClassLoader加载。如果现在我们自己编写一个包叫做java.lang,在这个包下定义一个类叫做System(这样的做法不违反Java的语法)。如果让AppClassLoader重复加载自己写的java.lang.System,可能会存在系统中一个类有两种不同的版本,产生一些混乱。或者后者会把前者覆盖掉,篡改了核心Java类库中的部分功能。动摇到了Java语言的根基。
如果有一些别有用心的人,借助这一机制编写一些病毒代码植入到我们的应用程序中代替系统级别类库,就可能会对程序甚至是操作系统产生毁灭性的打击。