JavaSE
2.1JavaSE
- 面对对象五大基本原则
- 数据类型
- 对象引用
- 关键字
- 类与接口
- 异常
- 集合类
- 反射
- 多线程
- 版本新特性
- JavaSE问题
面对对象五大基本原则
单一职责原则
一个类只负责一类事,不能包罗万象。
实际操作中职责的大小根据自己的经验来。可尝试定义死板的规则,如:一个方法不能超过10行!
开放封闭原则
对扩展开放,对修改封闭。即增加功能时,增加代码,而不是修改代码。目的:不影响原有代码
里氏替换原则
依赖倒置原则
接口分离原则
数据类型
基本数据类型,数据类型转型,基础数据类型与 wrapper 数据类型的自动装箱与拆箱,String类型(String,StringBuffer,StringBuilder)
基本数据类型
基础类型堆上占用内存空间:
blooean
byte 1字节 -2的8次方~2的8次方-1
char 2字节
short 2字节
int 4字节
long 8字节
float
double
boolean、byte、char、short 这四种类型,在栈上占用的空间和 int一样
32位机器,都是4个字节
64位机器,都是8个字节
String
对象引用
四种引用,强引用内存泄漏
四种引用介绍
四种:强引用、软引用、弱引用、虚引用。
GC 时对四种引用处理策略不同。强引用不会被 GC 回收;软引用内存空间不足时会被 GC 回收;弱引用则在每次 GC 时被回收;虚引用必须和引用队列联合使用,主要用于跟踪一个对象被垃圾回收的过程。
强引用
场景:一般都使用强引用
软引用
场景:缓存,内存不足时
HashMap直接做缓存容易引发内存泄漏
弱引用
场景:对象缓存,如:ThreadLocal,WeakHashMap
WeakHashMap会在系统内存范围内,保存所有表项目,一旦内存不够,在每次GC时,没有被引用的表项很快会被清除掉,从而避免系统内存溢出。
虚引用
用途:跟踪对象被垃圾回收的情况。
场景:对象被回收前执行某些清理操作。
关键字
static,final,finally,finilize(),volatile,this,supper
static
可以修饰类、变量、方法。
作用:形成静态代码块优化程序性能
特点:在类加载的时候执行一次
场景:字符串常量,枚举中的常量,factory对象赋值静态方法,单例对象,策略模式策略类查找定义
final ![[final关键字]]
finally
finally代码块 中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码
volatile
- 阻止指令重排序,这样就可以保证多线程变量读写的有序性。
- 强制变量的赋值会同步刷新回主内存,强制变量的读取会从主内存重新加载,保证不同的线程总是能够看到该变量的最新值。
this
this表示当前对象引用。
只能在实例方法中使用,无法在静态方法中使用。
supper
表示父类(超类)对象的引用。
- 调用父类的方法: 使用
super关键字可以在子类中调用父类的方法。通常用于在子类中覆盖(重写)父类的方法,但又想在子类中调用父类的方法。 - 访问父类的实例变量
transient
用于标记类的成员变量,表示这些变量不应该被序列化。
场景:
接口与抽象方法
| 接口 | 抽象类 | |
|---|---|---|
| 1.8 增加了默认方法。可以在向现有接口添加方法,而不影响现有实现。 | ||
| 类继承 | 多继承 | 单继承 |
| 构造器 | 可以有构造器 | 不能有构造器 |
| 场景 | 更适合定义行为规范。 | 更适合共享部分通用实现。 |
异常 [[深入拆解Java虚拟机-Java虚拟机基本原理#6.异常]]
集合
hashmap,ArrayList,LinkedList
hashmap扩容原理
1.7
- 生成新数组,长度为原数组两倍
- 遍历原hashmap的每个元素,重新计算hash值,将其添加到新数组中
- 转移完成后,将新数组赋值给HashMap的table属性
1.8
- 元素个数超过了负载因子
*当前容量时,触发扩容 - 生成新数组,长度为原数组的两倍
- 遍历老数组的所有元素,重新计算哈希码,将其放入新数组中。
- 如果元素在新桶中的索引位置上已经有其他元素存在了,那么新的元素会以链表或红黑树的形式插入到该位置。如果桶中的元素数量小于等于 8(TREEIFY_THRESHOLD),则会以链表的形式存储,如果大于 8,则会将链表转换为红黑树并插入。
- 转移完成后,将新数组赋值给HashMap的table属性。
- 元素个数超过了负载因子
反射 [[深入拆解Java虚拟机-Java虚拟机基本原理#7.反射]]
如何使用,使用场景
多线程 TODO
作用,场景,线程状态转换,同步机制(CAS,synchronized和lock),线程池,java并发工具包 JUC,其他概念,线程分析工具,实战
作用
最大化利用 CPU 和 I/O。
场景
- web容器处理请求
- 异步,记录日志,操作记录。
- 同步数据,同步下游系统、企业微信数据。
- 定时向大量(100w以上)的用户发送邮件
线程状态转换
6种状态
sleep,wait
相同:都会暂停线程
不同:
1.sleep是thread的静态方法,wait是object类的方法
2.线程sleep期间,占有资源的锁,wait释放了资源锁
3.sleep在指定时间后继续执行,wait需要调用notify方法唤醒。
线程的状态:新建,就绪,运行,阻塞,死亡。
线程的创建:1.thread类 2.runnable接口3.callable接口
bug管理:mantis,ones
同步机制
锁概念,CAS,synchronized,Lock,volatile关键字
锁概念
重量级锁、轻量级锁、自旋锁、偏向锁、重入锁、读写锁
CAS
乐观锁的一种实现方式。
缺点:
- 并发高时CPU消耗大。并发高时,可能重复执行 读取数据→操作数据→值是否改变的过程,消耗CPU。

问题:CAS中的ABA
描述:线程 T1 读取值 A 之后;发生两次写入,先由线程 T2 写回了 B,又由 T3 写回了 A;此时 T1 在写回比较时,值还是 A。
解决:
- 状态位,版本号
- JUC中的相关类。
synchronized
记录了对象信息。
Java1.6为Synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度。
锁升级过程:
第一个线程获得的是偏向锁。
第二个线程获得的是轻量级锁,底层是在用自旋锁。
自旋一定次数后,轻量级锁升级为重量级锁,线程进入blocked状态
性能低的原因:
- 重量级锁的线程会进入
BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,其中涉及到操作系统用户模式和内核模式的转换,代价比较高。
Lock
AQS,ReentrantLock
synchronized与lock的区别?TODO
相同:都用来实现线程同步
不同:
1.sy是Java中的关键字,lock是接口
2.sy不会导致死锁,线程异常会释放锁。lock需要调用unlock主动释放。
3.大量锁竞争时lock性能高很多。少量锁竞争性能差不多。
sy和reentrantlock(可重入锁)的区别?
相同:都是可重入锁,支持一个线程对资源重复加锁。
不同:
1.sy依赖jvm,re依赖Java的api
2.re有高级功能。1.等待可中断。2.支持公平锁。3.选择性通知。
线程池
作用,特点和使用场景,线程池参数,线程池执行流程,工作原理
作用
复用线程,创建线程、销毁线程都需要大量资源,用线程池复用线程节约资源,也提高了处理速度。
使用场景
常见的线程池和使用场景?
1.newCachedThreadPool:新建可缓存线程池,线程池中的线程数量超过处理需要时,可灵活回收。
2.创建一个定长线程池,超出时在队列中等待。
线程池的工作队列?
1 | Executors.newSingleThreadExecutor() // 单线程的线程池。保证顺序执行并且只有一个线程执行。定时拉取考勤。旧系统人员、部门、岗位数据 |
线程池参数
1 | /** |
线程池执行流程

工作原理
内部通过队列+线程实现,需要线程处理新的任务时:
- 线程池中的线程数量小于corePoolSize时,创建新的线程处理。
- 线程池的线程数量等于corePoolsize时,缓冲度列workQueue未满,任务加入缓冲队列。
- 线程池的线程数量大于等于corePoolsize时,缓冲度列workQueue已满,线程池的数量小于maximumPoolSize,新建线程处理。
- 线程池的线程数量等于corePoolsize时,缓冲度列workQueue已满,线程池的数量等于maximumPoolSize,通过handler指定策略处理。
- 当线程池中的线程数量⼤于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终⽌。
线程池个数
计算操作需要5ms,DB操作需要 100ms,对于一台 8个CPU的服务器,怎么设置数据库连接池线程数呢?
答:线程数=8*(1+100/5) = 168个。
上题的基础上如果DB的 QPS(Query Per Second)上限是1000,此时这个线程数又该设置为多大呢?
答:1个线程每秒处理的任务数是 1000/105。 1000QPS / (1000/105) = 105 个线程。
并发工具包: JUC
(concurrentHashMap,CopyOnWriteArrayList,Locks(Reentrantlock),AtomicXXX,Executor,Caller&Future,Queue,LongAdder)
ConcurrentHashMap
扩容原理
- 1.7
- 基于Segment分段实现
- 每个Segment相当于一个小的Hashmap
- 每个Segment内容会进行扩容,与HashMap扩容类似
segament
- 1.8
- 某个线程put时,发现别的线程在扩容,加入该线程一块扩容。
- 某个线程put后,容量超过阈值,进行扩容。
- 扩容时先复制数组,将数据分段给子线程分别扩容。
Reentrantlock
特点:
使用 int 标记加锁
可重入锁,有公平锁和非公平锁两种实现,公平和非公平是对获取锁,阻塞唤醒是一样的。
可以多次加锁,需要多次释放锁。
需要手动加解锁。
CopyOnWriteArrayList
底层原理:
- 内部使用数组实现
- 添加元素的实现,加Reentrantlock锁,复制数组为两倍容量,添加元素,旧数组指向新数组。
- 添加元素时,写操作加锁,在新数组上进行,读操作在原数组进行
总结:
CopyOnWriteArrayList允许在写操作时来读取数据,⼤⼤提⾼了读的性能,因此适合读多写少的应 ⽤场景。
CopyOnWriteArrayList⽐较占内存,同时可能读到的数据不是实时最新的数据,所 以不适合实时性要求很⾼的场景 。
其他概念
死锁的4个基本条件,竞争条件与临界区,线程间通信方式,JAVA提供的多线程机制,线程同步与异步、阻塞与非阻塞
线程间通信方式
wait,notify,notifyAll
JAVA提供的多线程机制
ThreadLocal 用来保存线程独享的数据,Fork/Join 机制用于大任务的分割与汇总,Volatile 对多线程数据可见性的保证,线程的中断机制
线程同步与异步,阻塞与非阻塞
线程的同步与异步、阻塞与非阻塞,同步和异步的区别在于任务是否是同一个线程执行,阻塞与非阻塞的区别在于异步执行任务时,线程是会阻塞等待结果,还是会继续执行后续逻辑。
线程分析工具
线程分析工具与方法,例如会用 jstack 分析线程的运行状态,查找锁对象持有状况等。
实战
https://zhuanlan.zhihu.com/p/374561361
版本特性 TODO
Java特性:函数式接口
V1.8
Lambda表达式,Stream API,方法引用,接口默认方法,Metaspace(元空间)替换PermGen
V1.9
模块系统,默认G1回收器(1.7推出),接口私有方法,局部变量推断,Graal编译器
V1.11
ZGC,字符串API增强,内建HTTP Client
JavaSE问题
面向过程和面向对象的区别
相同:都是解决问题的思路、方法
不同:- 面向过程将解决问题拆解为一个个步骤,偏向底层和细节,强调对问题进行分解。
- 面对对像是把一个个问题抽象为一个个对象,通过对象之间的交互来实现功能。 如:HR来负责协调面试时间。
类访问权限
重载与重写
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。 重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分。比如:构造函数的重载,无参构造+有参构造;比如:字符串的截取,一个是从开始位置截取到最后;还有一个同名方法是指定开始位置和结束位置。有意思的是,JVM运行字节码可以区分不同的返回类型,但Java转字节码是不允许相同参数不同返回值的。 重写:发生在父子类中,方法名、参数列表相同,如:对象重写equals、hashcode方法; 返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。equals,hashcode方法
1
2
3
4默认情况下,equals()方法比较的是两个对象的引用是否相等,即是否指向同一个对象实例。
默认情况下,hashCode()方法返回的是堆上的实例对象的哈希值。相同的对象实例内容,hashCode()返回不同。
HashSet、HashMap、HashTable等集合类,都会先根据对象的hashCode()方法来判断对象是否相等,不相等时,放入集合中;相等时,再根据equals()方法来判断是否真的相等。
我们日常比较对象一般也是希望比较对象内容是否相同String、StringBuillder、StringBuffer
介绍
String是线程安全的,不可被继承。会在静态区分配内存,new 对象时,会在堆上也分配。
StringBuilder是线程不安全的,比StringBuffer性能高10%-15%。在堆上分配
StringBuffer是线程安全的,在StringBuilder的基础上对方法增加了synchronized。场景
单线程大量操作使用StringBuilder。
多线程大量操作使用StringBuffer。
ArrayList和LinkedList的区别
ArrayList底层是数组,按占下标查找快,删除慢,需要移动删除位置之后的所有元素。我们一般查数据库返回的就是用ArrayList接收。
LinkedList底层是双向链表,查询慢,增删改快。以前使用Hutool导出excel数据无序使用过。深拷贝和浅拷贝的区别是什么?
浅拷贝:两个对象属性中的对象引用都指向同一个对象。修改一个对象的值,另一个也会改变。我们一般需要用深拷贝
深拷贝:拷贝的时候,所有对象都会再new一次,会多占一些空间