一、λ表达式
λ表达式 是java8引入的新特性,本质上是一个匿名方法
解决问题:匿名内部类的使用
1.1 使用条件
1.2 具体使用
package com.woniuxy.java23.study;
/**
* 随便定义的接口
* 当前接口中,有且仅有1个方法
* @author Administrator
*
*/
public interface Initable {
int meth01(int num1,int num2);
}
private static void study04() {
// TODO Auto-generated method stub
//最初的写法
// Initable init = new InitClass();
// init.meth01();
//接口实例的第2种方式(匿名内部类)
// Initable init = new Initable() {
//
// @Override
// public void meth01() {
// // TODO Auto-generated method stub
// System.out.println("asdfasdfasdfasdf");
// }
// };
//
// init.meth01();
// λ 表达式的标准语法
// Initable init = () ->{
// System.out.println("asdfasdfasdfasdfasdf");
// };
//如果方法体中,只有1行代码,可以省略{ }
// Initable init = () -> System.out.println("asdfasdfasdfasdfasdf");
//
// init.meth01();
//如果接口中仅有1个参数,() 是可以省略的
// Initable init = e -> {
// System.out.println("输出的结果:" + e);
// };
//
// init.meth01(12);
//如果有接口中,多个参数,使用() 将参数包起来
// Initable init = (e1,e2) ->{
// System.out.println("参数1:" + e1);
// System.out.println("参数2:" + e2);
// };
//
// init.meth01(12,23);
//如果接口中,多个参数,并且有返回,使用return来表示,此时不能省略{ }
// Initable init = (e1,e2) ->{
// return e1 + e2;
// };
// System.out.println(init.meth01(12, 23));
//如果接口中,多个参数,有返回,不使用return的情况,可以省略{ }
Initable init = (e1,e2) -> e1 + e2;
System.out.println(init.meth01(12, 23));
}
二、类的加载机制
JVM内存空间
**
2.1 类的加载顺序
一个类,如果要在JVM中运行,需要执行以下步骤:
具体的加载步骤:
2.2 类加载的步骤
第一步 加载
第一步,白话版:就是将class文件中的内容,使用IO流读取到JVM所在的内存空间
加载时,需要使用到3个类加载器,分别是:根类加载器,扩展类加载器,系统类加载器
根类加载器 是C语言编写的,他们3个之间存在父子关系
类加载的具体流程
1、java.exe程序搜索jre目录,寻找JVM.dll,并启动JVM。
2、JVM运行根类加载器,该根类加载器加载Java核心API。
3、根类加载器运行后,它会自动加载扩展类加载器和系统类加载器,并将扩展类加载器的父类设置为根类加载器,将应用加载器的父类设置为扩展类加载器。
4、扩展类加载器加载搜索JAVA_HOME/jre/lib/ext目录,加载扩展API。
5、应用加载器搜索CLASSPATH目录,加载我们要运行的类。
6、类的class文件读入内存后,就会创建一个java.lang.Class对象。也就是说,当Java程序中使用任何类时,系统都会为之创建一个java.lang.Class对象。一旦某个类被载入JVM中,同一个类就不会再次被载入。
注意:一旦某个类被载入JVM中,同一个类就不会再次被载入。
**
第二步 连接
当类被加载后,系统就为之创建一个对应的Class对象,接着就会进入连接阶段。连接阶段会负责把类的二进制数据合并到JRE中。
类的连接,也可以分为三个阶段:
验证: 判断class中的内容,能否运行在当前JVM版本上,你不要做JDK1.8的代码,放到JDK1.7去运行
准备:给static修饰的类变量赋初值,基本数据类型赋值为0,引用数据类型复制为null
解析:将类的二进制数据中的符号引用替换成直接引用
第三步 初始化
类初始化,第一个事情,就是针对静态常量赋初始值,例如PI = 3.14;
第二个事情,执行类中 由static修饰的 静态初始化块代码
初始化的步骤
1) 假如这个类还没有被加载和连接,程序先加载并连接该类。
2) 假如该类的直接父类还没有被初始化,则先初始化其直接父类。
3) 假如该类中有初始化语句,则系统依次执行这些初始化语句
2.3 类的加载机制-双亲委派
双亲委派的目的
防止程序员乱改它的核心代码,或者扩展代码
2.4 有6种情况,需要进行类的初始化
1、创建类的实例。
2、使用反射方式强制创建某个类或接口对应的java.lang.Class对象。
3、调用某个类的静态方法。
4、访问某个类或接口的静态属性,或者为静态属性赋值。
5、初始化某个类的子类。
6、直接使用java.exe命令运行某个主类。
三、GC垃圾回收
3.1 堆的分配
注意:所有的new 出来的对象,初始位置都是Eden
3.2 具体的回收
Eden 和 Survive 的内存是: 8 : 2
新生代和老年代的内存比例是:1 :2
回收的步骤
第一次回收
当Eden空间满了,GC线程,将会检查每个对象的使用次数标记,当标记为0时,表示该对象已经没有被引用,可以被GC回收。
当前标记不为0时,表示该对象依旧有被引用,不能被回收,将使用 “复制算法” 将对象复制到From区间,并记录它的复制次数为1
第二次回收
当Eden再次空间满了,GC线程,将同时检查Eden以及From区间,
判断每个对象的使用次数标记,当标记为0时,表示该对象已经没有被引用,可以被GC回收。
当前标记不为0时,表示该对象依旧有被引用,不能被回收,将使用 “复制算法”将
对象复制到To区间,并记录它的复制次数为2 或 1
并将From 改名为To ,将To 改名为From
第三次回收
当Eden再次空间满了,GC线程,将同时检查Eden以及From区间,
判断每个对象的使用次数标记,当标记为0时,表示该对象已经没有被引用,可以被GC回收。
当前标记不为0时,表示该对象依旧有被引用,不能被回收,将使用 “复制算法”将
对象复制到To区间,并记录它的复制次数为3 或 2 或 1
并将From 改名为To ,将To 改名为From
第N次回收,效果一样
但是对象不能永远都在新生代,当对象的复制次数 > 15 ,依旧再被引用。此时对象将被复制到老年代
老年代如果满了,就麻烦大了,将会触发:Full GC(全量垃圾回收)
3.3 垃圾回收的目的
为了清理内存空间,给新生的对象腾位置
3.4 垃圾回收的算法**
标记-清除算法:标记对象的引用次数 当次数>0 表示对象正在被使用,当==0时,表示对象可以被清理,标记完成后统一回收所有被标记的对象。(标记和清除效率不高,会产生大量不连续的内存碎片)
复制算法:把内存分为大小相等的两块,每次存储只用一块,用完后把存活的对象复制到另一块上,把使用过的内存全部清理(实际可用内存变为原来的一半)
标记整理算法:对可用对象进行标记,所有被标记对象向一端移动,最后清除可用对象边界以外的内存。
3.5 如何优化JVM(以后具体学习)
总体思想:尽量的减少 Full GC()的次数,方法:调整新生代 和老年代的内存比例,调整新生代中Eden 和 Survive的内存比例,以及尽量不要存在那种长时间持有对象引用的类
3.6 GC
JVM有一个称为垃圾回收器的低级线程,这个线程在后台不断地运行,自动寻找在Java程序中不再被使用的对象,并释放这些对象的内存。这种内存回收的过程被称为垃圾回收(Garbage Collection)
3.7 如何进行垃圾回收
调用对象身上的finalize()方法,进行销毁对象。
3.8 System.gc()和Runtime.gc()
程序员可以通过System.gc()或者Runtime.gc(),通知JVM开始回收垃圾。这也是程序员唯一可以参与垃圾回收的地方
注意:System.gc()或者Runtime.gc() 并不一定,就正儿八经可以让JVM进行GC回收,原因是:JVM回收有它自己的条件(Eden满,或老年代满)