0%

JavaSE

JavaSE

2.1JavaSE

  1. 面对对象五大基本原则
  2. 数据类型
  3. 对象引用
  4. 关键字
  5. 类与接口
  6. 异常
  7. 集合类
  8. 反射
  9. 多线程
  10. 版本新特性
  11. JavaSE问题

面对对象五大基本原则

  1. 单一职责原则

    一个类只负责一类事,不能包罗万象。

    实际操作中职责的大小根据自己的经验来。可尝试定义死板的规则,如:一个方法不能超过10行!

  2. 开放封闭原则

    对扩展开放,对修改封闭。即增加功能时,增加代码,而不是修改代码。目的:不影响原有代码

  3. 里氏替换原则

  4. 依赖倒置原则

  5. 接口分离原则

数据类型

基本数据类型,数据类型转型,基础数据类型与 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

表示父类(超类)对象的引用。

  1. 调用父类的方法: 使用 super 关键字可以在子类中调用父类的方法。通常用于在子类中覆盖(重写)父类的方法,但又想在子类中调用父类的方法。
  2. 访问父类的实例变量
transient

用于标记类的成员变量,表示这些变量不应该被序列化。
场景:

  1. 安全性,将密码、密钥等标记为 transient,可以防止它们被序列化。
  2. 减少序列化的时间,有些字段可能包含大量的数据,但在某些情况下并不需要被序列化。

    类与接口

接口与抽象方法
接口 抽象类
1.8 增加了默认方法。可以在向现有接口添加方法,而不影响现有实现。
类继承 多继承 单继承
构造器 可以有构造器 不能有构造器
场景 更适合定义行为规范。 更适合共享部分通用实现。

异常 [[深入拆解Java虚拟机-Java虚拟机基本原理#6.异常]]

集合

hashmap,ArrayList,LinkedList

hashmap扩容原理
  • 1.7

    1. 生成新数组,长度为原数组两倍
    2. 遍历原hashmap的每个元素,重新计算hash值,将其添加到新数组中
    3. 转移完成后,将新数组赋值给HashMap的table属性
  • 1.8

    1. 元素个数超过了负载因子*当前容量时,触发扩容
    2. 生成新数组,长度为原数组的两倍
    3. 遍历老数组的所有元素,重新计算哈希码,将其放入新数组中。
    4. 如果元素在新桶中的索引位置上已经有其他元素存在了,那么新的元素会以链表或红黑树的形式插入到该位置。如果桶中的元素数量小于等于 8(TREEIFY_THRESHOLD),则会以链表的形式存储,如果大于 8,则会将链表转换为红黑树并插入。
    5. 转移完成后,将新数组赋值给HashMap的table属性。

反射 [[深入拆解Java虚拟机-Java虚拟机基本原理#7.反射]]

如何使用,使用场景

多线程 TODO

作用,场景,线程状态转换,同步机制(CAS,synchronized和lock),线程池,java并发工具包 JUC,其他概念,线程分析工具,实战

作用

最大化利用 CPU 和 I/O。

场景
  1. web容器处理请求
  2. 异步,记录日志,操作记录。
  3. 同步数据,同步下游系统、企业微信数据。
  4. 定时向大量(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
image-20210615090422551

同步机制

锁概念,CAS,synchronized,Lock,volatile关键字

锁概念

重量级锁、轻量级锁、自旋锁、偏向锁、重入锁、读写锁

CAS

乐观锁的一种实现方式。

缺点:

  1. 并发高时CPU消耗大。并发高时,可能重复执行 读取数据→操作数据→值是否改变的过程,消耗CPU。

CAS流程

问题:CAS中的ABA

描述:线程 T1 读取值 A 之后;发生两次写入,先由线程 T2 写回了 B,又由 T3 写回了 A;此时 T1 在写回比较时,值还是 A。

解决:

  1. 状态位,版本号
  2. JUC中的相关类。
synchronized

记录了对象信息。

Java1.6为Synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度。

锁升级过程

第一个线程获得的是偏向锁。

第二个线程获得的是轻量级锁,底层是在用自旋锁。

自旋一定次数后,轻量级锁升级为重量级锁,线程进入blocked状态

性能低的原因

  1. 重量级锁的线程会进入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
2
3
4
5
6
Executors.newSingleThreadExecutor() // 单线程的线程池。保证顺序执行并且只有一个线程执行。定时拉取考勤。旧系统人员、部门、岗位数据

Executors.newFixedThreadPool()// 固定大小的线程池。
/* 1.cpu密集型。线程数=CPU核数+1(计算(CPU)密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作)。
2.I/O 密集型。线程数=CPU核心数 * (1 + (I/O耗时 / CPU耗时))
*/
线程池参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
线程池执行流程

image-20210828095532683

工作原理

内部通过队列+线程实现,需要线程处理新的任务时:

  1. 线程池中的线程数量小于corePoolSize时,创建新的线程处理。
  2. 线程池的线程数量等于corePoolsize时,缓冲度列workQueue未满,任务加入缓冲队列。
  3. 线程池的线程数量大于等于corePoolsize时,缓冲度列workQueue已满,线程池的数量小于maximumPoolSize,新建线程处理。
  4. 线程池的线程数量等于corePoolsize时,缓冲度列workQueue已满,线程池的数量等于maximumPoolSize,通过handler指定策略处理。
  5. 当线程池中的线程数量⼤于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终⽌。
线程池个数
  1. 计算操作需要5ms,DB操作需要 100ms,对于一台 8个CPU的服务器,怎么设置数据库连接池线程数呢?

    答:线程数=8*(1+100/5) = 168个。

  2. 上题的基础上如果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
    1. 基于Segment分段实现
    2. 每个Segment相当于一个小的Hashmap
    3. 每个Segment内容会进行扩容,与HashMap扩容类似

segament

  • 1.8
    1. 某个线程put时,发现别的线程在扩容,加入该线程一块扩容。
    2. 某个线程put后,容量超过阈值,进行扩容。
    3. 扩容时先复制数组,将数据分段给子线程分别扩容。
Reentrantlock

特点:

  • 使用 int 标记加锁

  • 可重入锁,有公平锁和非公平锁两种实现,公平和非公平是对获取锁,阻塞唤醒是一样的。

  • 可以多次加锁,需要多次释放锁。

  • 需要手动加解锁。

CopyOnWriteArrayList

底层原理:

  1. 内部使用数组实现
  2. 添加元素的实现,加Reentrantlock锁,复制数组为两倍容量,添加元素,旧数组指向新数组。
  3. 添加元素时,写操作加锁,在新数组上进行,读操作在原数组进行

总结:

  • 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问题

  1. 面向过程和面向对象的区别
    相同:都是解决问题的思路、方法
    不同:

    1. 面向过程将解决问题拆解为一个个步骤,偏向底层和细节,强调对问题进行分解。
    2. 面对对像是把一个个问题抽象为一个个对象,通过对象之间的交互来实现功能。 如:HR来负责协调面试时间。
  2. 类访问权限

  3. 重载与重写

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
    重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分。比如:构造函数的重载,无参构造+有参构造;比如:字符串的截取,一个是从开始位置截取到最后;还有一个同名方法是指定开始位置和结束位置。有意思的是,JVM运行字节码可以区分不同的返回类型,但Java转字节码是不允许相同参数不同返回值的。
    重写:发生在父子类中,方法名、参数列表相同,如:对象重写equals、hashcode方法;  返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。
    
  4. equals,hashcode方法

    1
    2
    3
    4
    默认情况下,equals()方法比较的是两个对象的引用是否相等,即是否指向同一个对象实例。
    默认情况下,hashCode()方法返回的是堆上的实例对象的哈希值。相同的对象实例内容,hashCode()返回不同。
    HashSet、HashMap、HashTable等集合类,都会先根据对象的hashCode()方法来判断对象是否相等,不相等时,放入集合中;相等时,再根据equals()方法来判断是否真的相等。
    我们日常比较对象一般也是希望比较对象内容是否相同
  5. String、StringBuillder、StringBuffer

    • 介绍
      String是线程安全的,不可被继承。会在静态区分配内存,new 对象时,会在堆上也分配。
      StringBuilder是线程不安全的,比StringBuffer性能高10%-15%。在堆上分配
      StringBuffer是线程安全的,在StringBuilder的基础上对方法增加了synchronized。

    • 场景
      单线程大量操作使用StringBuilder。
      多线程大量操作使用StringBuffer。

  6. ArrayList和LinkedList的区别
    ArrayList底层是数组,按占下标查找快,删除慢,需要移动删除位置之后的所有元素。我们一般查数据库返回的就是用ArrayList接收。
    LinkedList底层是双向链表,查询慢,增删改快。以前使用Hutool导出excel数据无序使用过。

  7. 深拷贝和浅拷贝的区别是什么?
    浅拷贝:两个对象属性中的对象引用都指向同一个对象。修改一个对象的值,另一个也会改变。我们一般需要用深拷贝
    深拷贝:拷贝的时候,所有对象都会再new一次,会多占一些空间