JavaSE基础补充+JVM学习复习总结记录
写在前面:
新开了一门外教课程,Object-oriented Programming(JAVA),学习了JavaSE基础相关内容,部分内容课程中没有讲到故自己找了一些其他资料进行补充。
Object-oriented Programming(JAVA)的结课project见文章SMAC。
Reference:
- Object-oriented Programming(Java) ----天津大学Marc
- Java高级编程 ---- 阿里云开发者学院
- B站首发:JVM类加载、JDK、内存模型、GC垃圾回收全系知识点
1. JavaSE基础进阶
1.1 多线程
- Thread类继承
- Runnable接口实现
(利用Runnable描述多个线程操作的资源,而Thread描述每一个线程对象) - Callable接口实现
(Callable在JDK1.5之后提出的,Callable重写的call()方法可以有返回值,Runnable重写的run()方法无返回值) - 多线程常用操作方法
// 线程休眠
Thread.sleep() (加trycatch处理异常)
//线程中断
Thread thread = new Thread(()->{
// ......
});
// ......
thread.interrupt();
// ......
// 线程强制执行(获取强制执行对象之后才可以执行join的调用)
Thread mainThread = Thread.currentThread();
// ......
mainThread.join();
// ......
// 线程礼让(每一次调用yield()方法都只会礼让一次当前的资源)
Thread.yield();
// 线程可以设置优先级
// 代码略,道理一样,文档中带static用类调用,普通方法需实例化后调用
-
synchronized(与pthread_mutex_lock一个效果),可以配合wait()和notify()使用
-
优雅停止线程:不用.stop的方式(强停容易造成错误),在线程里加一个boolean的flag
-
守护线程:.setDaemon(true)。(在JVM中,最大的守护线程是GC线程)(和linux守护进程概念一个意思)
-
volatile关键字:不使用副本,直接操作原始变量(直接操作这块内存),相当于节约了拷贝副本,重新放回的步骤。(主要在attribute上使用)
在正常进行变量处理三个步骤:
- 获取变量原有数据内容副本;
- 利用副本为变量进行数学计算;
- 将计算后的变量,保存到原始空间之中;
- ThreadLocal类,并发情况下用(同步问题),保存了线程对象名字和数据对象,使得不会对同一数据对象覆写。每一个线程通过ThreadLocal只允许保存一个数据(多线程资源引用传递)
1.2 反射
< ? extends T >:上界通配符(频繁读取)
< ? super T >:下界通配符(频繁插入)
// Class类对象的三种实例化模式
// 1.
Person per = new Person();
Class<? extends Person> cls = per.getClass();
cls.getName();
// 2.
Class<? extends Person> cls = Person.class;
cls.getName();
// 3.
// 不用import包(只需要个字符串)
Class<?> cls = Class.forName("projetc.a.Person");
cls.getName();
// 通过newInstance()方法实例化类对象(等价关键字new)(只需要个字符串)
Class<?> cls = Class.forName("projetc.a.Person");
Object obj = cls.getDeclaredConstructor().newInstance();
当获取了一个类的Class对象之后,就意味着这个对象可以获取类中的一切继承结构信息、获取调用构造函数(有参无参)、获取调用普通方法(invoke())、获取成员属性(getType())
反射机制最大特征是可以根据其自身的特点(Object类直接操作)实现相同功能类的重复操作的抽象处理。(用字符串来实现类属性的set)。反射也可以实现动态代理设计模式,绑定不同的接口/类。
反射也可以获取Annotation信息,也可以利用Annotation属性配置接口/类。
1.3 集合&泛型
(泛型与C++模版用法相似,集合与C++STL 用法相似)
类集框架核心接口:Collection、List、Set、Map、Iterator、Enumeration、Queue、ListIterator
-
Comparable比较器(c++仿函数)
在Arrays数组中,除了Integer, String等类可以直接用Arrays.sort(),自定义的类需要用比较器处理,自定义的类实现Comparable<>比较器,覆写compareTo()方法。(List用Collections.sort())
Comparator
挽救的比较操作。再定义一个类并实现Comparator接口,覆写compare方法。在Arrays.sort(data, new DataComparator)用这个类。 -
ArrayList(implements RandomAccess)(c++Vector):底层实现是数组
JDK1.9以后,无参构造不开辟空间,add以后开辟默认(10)大小空间,之后超过了的话,需要复制拷贝操作,然后开辟空间成倍增长。
Plus. Vector无参构造上来开辟10个空间,且其他操作与ArrayList一样。Vector线程安全,但性能不如ArrayList高。 -
LinkedList(无RandomAccess):底层实现是双向链表
用iterator迭代输出(用get效率低)
(ArrayList 类和 LinkedList 类都是不同步的,不保证线程安全。)
-
Vector
Vector 类和 ArrayList 类的方法基本是相同的,主要区别是 Vector 类的所有方法都是同步的,因此可以保证线程安全。 -
HashSet/TreeSet之前文章。TreeSet的一个interesting point, 若是自定义类,需实现Compareable接口,并且每种属性都要进行比较。
-
集合输出:
Iterator迭代输出(不同于 Iterator 类型的迭代器只支持单向遍历,ListIterator 类型的迭代器支持双向遍历。)
Enumeration为Vector服务 -
HashMap/HashTable/TreeMap/LinkedHashMap/ConcurrentHashMap
Map的一个interesting point, 若是自定义类型的Key,Key类型所在的类中一定要覆写hashCode()和equals()方法,但一般不这样用。 -
Collections工具类操作Collection (有点#include < alogorithm >的意思)
-
Iterable 接口:Iterable 接口从 JDK 1.5 开始出现,是 Java 容器的最顶级的接口之一,该接口的作用是使容器具备迭代元素的功能。Collection 接口继承了 Iterable 接口。
1.4 网络编程
// TCP
package project
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
// 服务端关键代码(需要Runnable多线程包装)
ServerSocket server = new ServerSocket(9999);
Socket client = server.accept();
// 客户端输入流
Scanner scan = new Scanner(client.getInputStream());
// 客户端输出流
PrintWriter out = new PrintWriter(client.getOutputStream());
// 客户端关键代码
Socket client = new Socket("localhost", 9999);
Scanner scan = new Scanner(client.getInputStream());
PrintStream out = new PrintStream (client.getOutputStream());
// UDP
// 服务端关键代码
DatagramSocket server = new DatagramSocket(9999);
DatagramPacket packet = new DatagramPacket(数据地址端口);
server.send(packet);
// 客户端关键代码
DatagramSocket client = new DatagramSocket(9999);
byte data[] = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
client.receive(packet);
2. JVM
2.1 内存模型
- 栈:栈中有栈帧
- 程序计数器:程序计数器在多线程时可记录执行到哪行代码
- 本地方法栈(native)(调c/c++)
- 方法区:常量,静态变量,类元信息
- 堆
堆内存分代模型
2.2 类加载机制
(复习)c/c++的编译执行过程
预处理阶段(预处理器cpp):处理以 # 开头的预处理命令;
编译阶段(编译器):翻译成汇编文件;
汇编阶段(汇编器):将汇编文件翻译成可重定向目标文件(二进制);
链接阶段(链接器):将可重定向目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。
java类加载
类加载器及双亲委派机制
引导类加载器:JAVA_HOME/lib
扩展类加载器:JAVA_HOME/lib/ext
应用类加载器:classpath
Tomcat可以部署多个服务/war包,打破了双亲委派机制。双亲委派机制,当目前的类加载器加载过,会立即返回;但如果没有加载过,不会立即加载,而是向上委托,当到了引导类加载器时,发现没加载过,会在jre/lib下的核心类库中找,如果没有,则向下扔(后边省略)。
自定义类加载器,重写loadClass()方法,破坏双亲委派机制
2.3 GC垃圾回收机制
1.识别垃圾对象:
- 引用计数法:多个变量指向同一个对象,在对象上记数(Java没有采取,解决不了互相引用的问题)
- 可达性分析算法:
2.垃圾回收算法:
- Mark-Sweep(标记清除):位置不连续,产生碎片
- Copying(复制):没有碎片,浪费空间,因为一半一半切的
- Mark-Compact(标记整理):没有碎片,效率偏低
年轻代一开始Minor GC,老年代也满了触发Full GC,GC会造成STW(stop the word)停下用户线程来GC
3.常用垃圾收集器:
左边6个垃圾收集器:
JDK初期默认:
Serial串行,用户线程停止,单线程GC;
Parallel并行,用户线程停止,多线程GC;
JDK1.8及之前默认:
ParNew在Parallel上改造,配合CMS,原理也是需要暂停用户线程;
CMS(ConcurrentMarkSweep)解决stop the word时间过长问题。初始标记时间快,因为只是标记GC roots,并发标记寻找其他垃圾,重新标记解决两种情况(1.之前没标,运行时变成垃圾的浮动垃圾标记。2.之前标了,后来不是垃圾了,也需要重新标记)
注意,CMS采取标记清除算法,会造成碎片,并行处理可能出现异常,为了解决,CMS 的GC与Serial Old GC配合使用
- 对于垃圾收集器来说,新生代往往采取复制算法,老年代往往采取标记整理算法(CMS采取标记清除算法)
右边垃圾收集器:
分区模型(不是分代模型)
JDK1.9默认:G1
其他
String:不可变
StringBuffer:可变,线程安全(并发)
StringBuilder:可变,线程不安全(迭代)
String的compareTo()方法,先比ASCII码直到第一个不相同,若全相同之后才比长度
charSequence接口描述字符串
AutoCloseable接口关闭各种资源(必须配合try catch)
Runtime类可获取操作系统资源信息(单例设计,只能get一个,个人理解是该类在描述这个JVM进程)(gc手工处理,用Runtime.getRuntime().gc())
Stream数据流类对大数据进行一些操作分析时可用