- 面向对象
- Java基础知识
- 基本数据类型
- 自动拆装箱
- String
- Java中各种关键字
- 集合类
- Collection和Collections的区别
- 常用集合类的使用
- Set和List的区别
- ArrayList和LinkedList和Vector的区别
- SynchronizedList和Vector的区别
- Set如何保证元素不重复
- HashMap、HashTable、ConcurrentHashMap区别
- Java8中Map相关的红黑树的引用背景、原理等
- Java8中stream相关用法
- Apache集合处理工具类的使用
- 不同版本的JDK中HashMap的实现的区别以及原因
- Arrays.asList获得的List使用时需要注意什么
- Collection和Iterator区别
- 如何在遍历的同时删除ArrayList中的元素
- fail-fast和fail-safe
- CopyOnWriteArrayList
- ConcurrentSkipListMap
- 枚举
- IO
- 反射
- 动态代理
- 序列化
- 注解
- 泛型
面向对象
什么是面向对象
面向对象与面向过程
概述
什么是面向过程: 自顶而下的编程模式
什么是面向对象:将事务高度抽象化的编程模式
举例说明区别
同样一个象棋设计:
面向对象:创建黑白双方的对象负责演算,棋盘的对象负责画布,规则的对象负责判断,
例子可以看出,面向对象更重视不重复造轮子,即创建一次,重复使用。
面向过程:开始—黑走—棋盘—判断—白走—棋盘—判断—循环。只需要关注每一步怎
么实现即可。
优劣对比
面向对象:占用资源相对高,速度相对慢。
面向过程:占用资源相对低,速度相对快。
面向对象的三大基本特征
封装:对象对内部数据提供了不同级别的保护。
继承:它可以使用现有类的所有功能,并在无需重新编写原来的类的
情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”
或“超类”。
继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性
和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供
实现的能力。
多态:一个类实例的相同方法在不同情形有不同表现形式。
面向对象的五大基本原则
单一职责原则(Single-Responsibility Principle)
一个类,最好只做一件事,只有一个引起它的变化。
开放封闭原则(Open-Closed principle)
软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修
改封闭的。
Liskov 替换原则(Liskov-Substitution Principle)
子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只
有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。
依赖倒置原则(Dependecy-Inversion Principle)
依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖
于抽象;抽象不依赖于具体,具体依赖于抽象。
接口隔离原则(Interface-Segregation Principle)
使用多个小的专门的接口,而不要使用一个大的总接口。
封装、继承、多态
什么是多态
同一操作作用于不同的对象,可以有不同的解释,产生不同
的执行结果。 多态是一种运行期的状态。
多态的必要条件:满足三个条件。
即有类继承或者接口实现、子类要重写父类的方法、父类的引用指向子类的对象。
简单来一段代码解释下:
public class Parent{
public void call(){
sout("im Parent");
}
}
public class Son extends Parent{// 1.有类继承或者接口实现
public void call(){// 2.子类要重写父类的方法
sout("im Son");
}
}
public class Daughter extends Parent{// 1.有类继承或者接口实现
public void call(){// 2.子类要重写父类的方法
sout("im Daughter");
}
}
public class Test{
public static void main(String[] args){
Parent p = new Son(); //3.父类的引用指向子类的对象
Parent p1 = new Daughter(); //3.父类的引用指向子类的对象
}
}
重写动态多态,重载静态多态,重载算不算多态,为什么重载不算多态?
解答:因为在编译期已经确定调用哪个方法,所以重载并不是多态。
方法重写与重载
重载:
简单说,就是函数或者方法有同样的名称,但是参数列表不相同的情形,这样的同名不
同参数的函数或者方法之间,互相称之为重载函数或者方法。
重写:
重写指的是在 Java 的子类与父类中有两个名称、参数列表都相同的方法的情况。由于
他们具有相同的方法签名,所以子类中的新方法将覆盖父类中原有的方法。
Java的继承与实现
继承:如果多个类的某个部分的功能相同,那么可以抽象出一个类出来,把他们的相同
部分都放到父类里,让他们都继承这个类。
实现:如果多个类处理的目标是一样的,但是处理的方法方式不同,那么就定义一个接
口,也就是一个标准,让他们的实现这个接口,各自实现自己具体的处理方法来处理那个目
标
继承的根本原因是因为要复用,而实现的根本原因是需要定义一个标准。
继承使用 extends 关键字实现,而实现通过 implements 关键字。
Java的继承与组合
Java 代码的复用有继承,组合以及代理三种具体的表现形式。
继承
继承(Inheritance)是一种联结类与类的层次模型。指的是一个类(称为子类、子接
口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,
继承是类与类或者接口与接口之间最常见的关系;继承是一种 is-a 关系。
组合
组合(Composition)体现的是整体与部分、拥有的关系,即 has-a 的关系。
建议在同样可行的情况下,优先使用组合而不是继承。
因为组合更安全,更简单,更灵活,更高效。
构造函数与默认构造函数
构造函数跟一般的实例方法十分相似;但是与其它方法不同,构造器没有返回类型,不
会被继承,且可以有范围修饰符。构造器的函数名称必须和它所属的类的名称相同。 它承
担着初始化对象数据成员的任务。
如果在编写一个可实例化的类时没有专门编写构造函数,多数编程语言会自动生成缺省
构造器(默认构造函数)。默认构造函数一般会把成员变量的值初始化为默认值,如 int ->
0,Integer -> null。
类变量、成员变量和局部变量
Java 变量 | JVM |
---|---|
类变量 | 方法区 |
成员变量 | 堆内存 |
局部变量 | 栈内存 |
成员变量和方法作用域
public : 表明该成员变量或者方法是对所有类或者对象都是可见的,所有类或者对象都
可以直接访问。
private : 表明该成员变量或者方法是私有的,只有当前类对其具有访问权限,除此之外
其他类或者对象都没有访问权限.子类也没有访问权限。
protected : 表明成员变量或者方法对类自身,与同在一个包中的其他类可见,其他包下
的类不可访问,除非是他的子类。
default : 表明该成员变量或者方法只有自己和其位于同一个包的内可见,其他包内的
类不能访问,即便是它的子类。
平台无关性
Java如何实现的平台无关性的
Java 语言规范
通过规定 Java 语言中基本数据类型的取值范围和行为
Class 文件
所有 Java 文件要编译成统一的 Class 文件
Java 虚拟机
通过 Java 虚拟机将 Class 文件转成对应平台的二进制文件等
JVM还支持哪些语言
Kotlin、 Groovy、JRuby、Jython、Scala 、Fantom、Clojure 、Rhino、Ceylon 等
值传递
值传递、引用传递
为什么说Java中只有值传递
引用数据类型参数(如对象)也按值传递给方法。这意味着,当方法返回时,
传入的引用仍然引用与以前相同的对象。但是,如果对象字段具有适当的访问级别,则可以
在方法中更改这些字段的值。
Java基础知识
基本数据类型
8种基本数据类型
字符型
char
布尔型
boolean
数值型
1.整型:byte、short、int、long
2.浮点型:float、double
String 不是基本数据类型,是引用类型。
整型中byte、short、int、long的取值范围
byte | 1 | -128(-2^7) | 127(2^7-1) |
---|---|---|---|
short | 2 | -32,768 (-2^15) | 32,767 (2^15-1) |
int | 4 | -2,147,483,648 (-2^31) | 2,147,483,647 (2 ^31-1) |
long | 8 | 为-9,223,372,036,854,775,808 (-2^63) | 9,223,372,036, 854,775,807 (2^63-1) |
溢出的时候并不会抛异常,也没有任何提示。所以,在程序中,使
用同类型的数据进行运算的时候,一定要注意数据溢出的问题。
什么是浮点型
什么是单精度和双精度?
单精度浮点数在计算机存储器中占用 4 个字节(32 bits)
双精度浮点数(double)使用 64 位(8 字节) 来存储一个浮点数。
为什么不能用浮点型表示金额?
计算机中保存的小数其实是十进制的小数的近似值,并不是准确值
BigDecimal 或者 Long(单位为分)来表示金额。
自动拆装箱
自动拆装箱
Java 基本类型共有八种,基本类型可以分为三类:
字符类型 char
布尔类型 boolean
数值类型 byte、short、int、long、float、double。
数值类型又可以分为整数类型 byte、short、int、long 和浮点数类型 float、double。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
Integ的缓存机制
如何正确定义接口的返回值(boolean/Boolean)类型及命名(success/Success)
在定义 POJO 中的布尔类型的变量时,不要使用 isSuccess 这种形式,而要
直接使用 success!
Boolean:Java 开发手册建议使用封装类来定义 POJO 和 RPC 返回值中的变量。但是这不意味着
可以随意的使用 null,我们还是要尽量避免出现对 null 的处理的。
String
字符串的不可变性
一旦一个 string 对象在内存(堆)中被创建出来,他就无法被修改。特别要注意的是,
String 类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
如果你需要一个可修改的字符串,应该使用 StringBuffer 或者 StringBuilder。否则
会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的 string 对象被创建出来。
String的长度限制
根据 Integer 类的定义,java.lang.Integer#MAX_VALUE 的最大值是 2^31 - 1;
JDK6和JDK7中的substing的原理及区别
replaceFirt、replaceAll、replace区别
replaceAll() 替换符合正则的所有文字
replaceFirst() 替换第一个符合正则的数据
String、StringBuilder和StringBuffer之间的区别与联系
String对“+”的重载
字符串拼接的几种方式与区别
1、如果不是在循环体中进行字符串拼接的话,直接使用+就好了。
2、如果在并发场景中进行字符串拼接的话,要使用 StringB uffer 来代替
StringBuilder。
String.valueOf和Integer.toString的区别
没有任何区别,因为 String.valueOf(i)也是调用 Integer.toString(i)
来实现的。
switch对String的支持
对int直接支持,其他事ascii码比较,String是通过 equals()和 hashCode()方法来实现的。
常量池
运行时常量池
运行时常量池( Runtime Constant Pool)是每一个类或接口的常量池( Constant_Pool)的运行时表示形式。
运行时常量池中主要保存的是字面量和符号引用
Java 虚拟机规范约定:每一个运行时常量池都在 Java 虚拟机的方法区中分配,
在加载类和接口到虚拟机后,就创建对应的运行时常量池。
Class常量池
Class 常量池可以理解为是 Class 文件中的资源仓库。
常量池中主要存放两大类常量:字面量(literal)和符号引用(symbolic references)。
字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。
字符串池
在 JVM 中,为了减少相同的字符串的重复创建,为了达到节省内存的目的。会单独开
辟一块内存,用于保存字符串常量,这个内存区域被叫做字符串常量池。
当代码中出现双引号形式(字面量)创建字符串对象时,JVM 会先对这个字符串进行
检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回;否则,
创建新的字符串对象,然后将这个引用放入字符串常量池,并返回该引用。
这种机制,就是字符串驻留或池化。
intern
在 JVM 中,为了减少相同的字符串的重复创建,为了达到节省内存的目的。会单独开
辟一块内存,用于保存字符串常量,这个内存区域被叫做字符串常量池。
在每次赋值的时候使用 String 的 intern 方法,如果常量池中有相同值,就会重复使
用该对象,返回对象引用。
Java中各种关键字
transient
instanceof
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类
的实例,返回 boolean 的数据类型。
volatile
volatile 通常被比喻成”轻量级的 synchronized”,也是 Java 并发编程中比较重要的
一个关键字。和 synchronized 不同,volatile 是一个变量修饰符,只能用来修饰变量。无
法修饰方法及代码块等。
synchronized 可以保证原子性、有序性和可见性。而 volatile 却只能保证有序性和可见性。
synchronized
主要有两种用法,分别是同步
方法和同步代码块。也就是说,synchronized 既可以修饰方法也可以修饰代码块。
final
static
表示“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态 static
代码块。
const
集合类
Collection和Collections的区别
Collection 是一个集合接口。
Collections 是一个包装类。
常用集合类的使用
Set和List的区别
ArrayList和LinkedList和Vector的区别
ArrayList 是一个可改变大小的数组.
LinkedList 是一个双链表
Vector 和 ArrayList 类似,但属于强同步类。Vector 每次请求其大
小的双倍空间,而 ArrayList 每次对 size 增长 50%。
SynchronizedList和Vector的区别
Set如何保证元素不重复
HashMap、HashTable、ConcurrentHashMap区别
线程安全:HashTable 中的方法是同步的,而 HashMap 中的方法在默认情况下是
非同步的。
Java8中Map相关的红黑树的引用背景、原理等
Java8中stream相关用法
Stream 的创建
//将一个集合类转换成流。
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis", "H ello", "HelloWorld", "Hollis");
Stream<String> stream = strings.stream();
//直接通过 of 方法,创建并返回一个 Stream。
Stream<String> stream = Stream.of("Hollis", "HollisChuang", "hollis", "Hell o", "HelloWorld", "Hollis");
filter
filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤掉空字
符串:
List<String> strings = Arrays.asList("Hollis", "", "HollisChuang", "H", "ho
llis");
strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::p
rintln);
//Hollis, , HollisChuang, H, hollis
map
map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应
的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().map( i -> i*i).forEach(System.out::println);
//9,4,4,9,49,9,25
limit/skip
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素。以下代码片段使
用 limit 方法保理 4 个元素:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().limit(4).forEach(System.out::println);
//3,2,2,3
sorted
sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法进行排序:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().sorted().forEach(System.out::println);
//2,2,3,3,3,5,7
distinct
distinct 主要用来去重,以下代码片段使用 distinct 对元素进行去重:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().distinct().forEach(System.out::println);
//3,2,7,5
forEach
Stream 提供了方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach
输出了 10 个随机数:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
count
count 用来统计流中的元素个数。
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis","Ho
llis666", "Hello", "HelloWorld", "Hollis");
System.out.println(strings.stream().count());
//7
collect
collect 就是一个归约操作,可以接受各种做法作为参数,将流中的元素累积成一个汇
总结果:
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis","Ho
llis666", "Hello", "HelloWorld", "Hollis");
strings = strings.stream().filter(string -> string.startsWith("Hollis")).c
ollect(Collectors.toList());
System.out.println(strings);
//Hollis, HollisChuang, Hollis666, Hollis
Apache集合处理工具类的使用
- Bag - Bag 界面简化了每个对象具有多个副本的集合。
- BidiMap - BidiMap 接口提供双向映射,可用于使用值使用键或键查找值。
- MapIterator - MapIterator 接口提供简单而容易的迭代迭代。
- Transforming Decorators - 转换装饰器可以在将集合添加到集合时更改集合的每 个对象。
- Composite Collections - 在需要统一处理多个集合的情况下使用复合集合。
- Ordered Map - 有序地图保留添加元素的顺序。
- Ordered Set - 有序集保留了添加元素的顺序。
- Reference map - 参考图允许在密切控制下对键/值进行垃圾收集。
- Comparator implementations - 可以使用许多 Comparator 实现。
- Iterator implementations - 许多 Iterator 实现都可用。
- Adapter Classes - 适配器类可用于将数组和枚举转换为集合。
- Utilities - 实用程序可用于测试测试或创建集合的典型集合论属性,例如 union,intersection。 支持关闭。
不同版本的JDK中HashMap的实现的区别以及原因
Arrays.asList获得的List使用时需要注意什么
Collection和Iterator区别
如何在遍历的同时删除ArrayList中的元素
普通 for 循环
使用 Iterator
filter 过滤
增强 for 循环其实也可以
fail-fast和fail-safe
CopyOnWriteArrayList
从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容
Copy 出去形成一个新的内容然后再改,这是一种延时懒惰策略。
ConcurrentSkipListMap
ConcurrentSkipListMap 是一个内部使用跳表,并且支持排序和并发的一个 Map,
是线程安全的。一般很少会被用到,也是一个比较偏门的数据结构。
跳表是一种允许在一个有顺序的序列中进行快速查询的数据结构。
在普通的顺序链表中查询一个元素,需要从链表头部开始一个一个节点进行遍历,然后找到节点。
跳表可以解决这种查询时间过长,其元素遍历的图示,跳表是一种使用”空间换时间”的概念用
来提高查询效率的链表。
枚举
枚举的用法
枚举的实现
枚举与单例
Enum类
Java枚举如何比较
switch对枚举的支持
枚举的序列化如何实现
枚举的线程安全性问题
IO
字符流、字节流
Bit 最小的二进制单位 ,是计算机的操作部分。取值 0 或者 1
Byte(字节)是计算机操作数据的最小单位由 8 位 bit 组成 取值(-128-127)
Char(字符)是用户的可读写的最小单位,在 Java 里面由 16 位 bit 组成 取值(0-
65535)
字节流
操作 byte 类型数据,主要操作类是 OutputStream、InputStream 的子类;不用缓
冲区,直接对文件本身操作。
字符流
操作字符类型数据,主要操作类是 Reader、Writer 的子类;使用缓冲区缓冲字符,
不关闭流就不会输出任何内容。
OutputStreamWriter:是 Writer 的子类,将输出的字符流变为字节流,即将一个字
符流的输出对象变为字节流输出对象。
InputStreamReader:是 Reader 的子类,将输入的字节流变为字符流,即将一个字
节流的输入对象变为字符流的输入对象。
输入流、输出流
字节流和字符流之间的相互转换
想要实现字符流和字节流之间的相互转换需要用到两个类:
OutputStreamWriter 是字符流通向字节流的桥梁
InputStreamReader 是字节流通向字符流的桥梁
同步、异步
阻塞、非阻塞
Linux5种IO模型
阻塞式 IO 模型
非阻塞 IO 模型
IO 复用模型
信号驱动 IO 模型
异步 IO 模型
BIO、NIO和AIO的区别
BIO
Java BIO 即 Block I/O ,同步并阻塞的 IO。
BIO 就是传统的 java.io 包下面的代码实现。
NIO
什么是NIO? NIO 与原来的 I/O 有同样的作用和目的, 他们之间最重要的区别是数据
打包和传输的方式。原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
AIO
Java AIO 即 Async 非阻塞,是异步非阻塞的 IO。
三种IO的用法与原理
netty
Netty 是一个非阻塞 I/O 客户端-服务器框架,主要用于开发 Java 网络应用程序,如
协议服务器和客户端。
反射
什么是反射
反射机制指的是程序在运行时能够获取自身的信息。在 java 中,只要给定类的名字,
那么就可以通过反射机制来获得类的所有属性和方法。
反射有什么作用
在运行时判断任意一个对象所属的类。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时任意调用一个对象的方法。
在运行时构造任意一个类的对象。
Class类
java.lang.reflect.*
动态代理
静态代理
静态代理,就是代理类是由程序员自己编写的,在编译期就确定好了的。
动态代理
动态代理和反射的关系
动态代理的几种实现方式
1、JDK 动态代理:
java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口提
供了生成动态代理类的能力。
2、Cglib 动态代理:Cglib (Code Generation Library )是一个第三方代码生成类
库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。
Java 实现动态代理的大致步骤
1、定义一个委托类和公共接口。
2、自己定义一个类(调用处理器类,即实现 InvocationHandler 接口),这个类的
目的是指定运行时将生成的代理类需要完成的具体任务(包括 Preproces s 和
Postprocess),即代理类调用任何方法都会经过这个调用处理器类(在本文最后一节对
此进行解释)。
3、生成代理对象(当然也会生成代理类),需要为他指定(1)委托对象(2)实现的一系
列接口(3)调用处理器类的实例。因此可以看出一个代理对象对应一个委托对象,对应一个
调用处理器实例。
Java 实现动态代理主要涉及哪几个类
java.lang.reflect.Proxy: 这是生成代理类的主类,通过 Proxy 类生成的代理类都继
承了 Proxy 类,即 DynamicProxyClass extends Proxy。
java.lang.reflect.InvocationHandler: 这里称他为”调用处理器”,他是一个接口,我
们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现
InvocationHandler 接口。
AOP
Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理。
JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。
JDK 动态代理的核心是 InvocationHandler 接口和 Proxy 类。
如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态
的生成某个类的子类,注意,CGLIB 是通过继承的方式做的动态代理,因此如果某个类被
标记为 final,那么它是无法使用 CGLIB 做动态代理的。
序列化
什么是序列化与反序列化
序列化是将对象转换为可传输格式的过程。 是一种数据的持久化手段。一般广泛应用
于网络传输,RMI 和 RPC 等场景中。
反序列化是序列化的逆操作。
序列化是将对象的状态信息转换为可存储或传输的形式的过程。一般是以字节码或
XML 格式传输。而字节码或 XML 编码格式可以还原为完全相等的对象。这个相反的过程
称为反序列化。
Java如何实现序列号与反序列化
Serializable和Externalize有何不同
为什么需要序列化
serialVersionUID
为什么serialVersionUID不能随便改
它是怎么保证只有实现了该接口的方法才能进行序列化与反序列化的呢?
原因是在执行序列化的过程中,会执行到以下代码:
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
transient
序列化底层原理
通过 ObjectOutputStream 和 ObjectInputStream 对对象进行序列化及反序列
化。
writeObject 和 readObject 方法
ArrayList 的序列化方式
序列化如何破坏单例模式
答:序列化会通过反射调用无参数的构造方法创建一个新的对象。
解决方案:只要在 Singleton 类中定义 readResolve 就可以解决该问题:
private Object readResolve() {
return singleton;
}
在 Singleton 中定义 readResolve 方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。
protobuf
Protocol Buffer (简称 Protobuf) 是 Google 出品的性能优异、跨语言、跨平台的序
列化库。
为什么说序列化并不安全
注解
元注解
@Target(表示该注解可以用于什么地方)、
@Retention(表示在什么级别保存该注解信息)、
@Documented(将此注解包含再 javadoc 中)、
@Inherited(允许子类继承父类中的注解)。
自定义注解
Java中常用的注解使用
@Override 表示当前方法覆盖了父类的方法。
@Deprecation 表示方法已经过时,方法上有横线,使用时会有警告。
@SuppressWarnings 表示关闭一些警告信息(通知 java 编译器忽略特定的编译警
告)。
SafeVarargs (jdk1.7 更新) 表示:专门为抑制“堆污染”警告提供的。
@FunctionalInterface (jdk1.8 更新) 表示:用来指定某个接口必须是函数式接口,
否则就会编译出错。
注解与反射的结合
如何自定义一个注解
在 Java 中,类使用 class 定义,接口使用 interface 定义,注解和接口的定义差不多,
增加了一个@符号,即@interface,
Spring常用注解
@Configuration 把一个类作为一个 IoC 容器,它的某个方法头上如果注册了
@Bean,就会作为这个 Spring 容器中的 Bean。
@Scope 注解 作用域。
@Lazy(true) 表示延迟初始化。
@Service 用于标注业务层组件。
@Controller 用于标注控制层组件@Repository 用于标注数据访问组件,即 DAO 组
件。
@Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Scope 用于指定 scope 作用域的(用在类上)。
@PostConstruct 用于指定初始化方法(用在方法上)。
@PreDestory 用于指定销毁方法(用在方法上)。
@DependsOn:定义 Bean 初始化及销毁时的顺序。
@Primary:自动装配时当出现多个 Bean 候选者时,被注解为@Primary 的 Bean
将作为首选者,否则将抛出异常。
@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier
注解一起使用。如下: @Autowired @Qualifier(“personDaoBean”) 存在多个实例配
合使用。
@Resource 默认按名称装配,当找不到与名称匹配的 bean 才会按类型装配。
@PostConstruct 初始化注解。
@PreDestroy 摧毁注解 默认 单例 启动就加载。
泛型
什么是泛型
Java 泛型( generics) 是 JDK 5 中引⼊的⼀个新特性, 允许在定义类和接⼜的
时候使⽤类型参数( type parameter) 。
类型擦除
泛型带来的问题
泛型中KTVE? object等的含义
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的 java 类型(无限制通配符类型)
S、U、V - 2nd、3rd、4th types
Object - 是所有类的根类,任何类的对象都可以设置给该 Object 引用变量,使用的
时候可能需要类型强制转换,但是用使用了泛型 T、E 等这些标识符后,在实际用之前类型
就已经确定了,不需要再进行类型强制转换。
泛型各种用法
限定通配符和非限定通配符
限定通配符对类型进⾏限制, 泛型中有两种限定通配符:
表示类型的上界,格式为:<? extends T>,即类型必须为 T 类型或者 T 子类 表示
类型的下界,格式为:<? super T>,即类型必须为 T 类型或者 T 的父类
泛型类型必须⽤限定内的类型来进⾏初始化,否则会导致编译错误。
⾮限定通配符表⽰可以⽤任意泛型类型来替代,类型为
上下界限定符extends和super
(Bounds)”的概念。
<? extends T>:是指 “上界通配符(Upper Bounds Wildcards)”,即泛型中
的类必须为当前类的子类或当前类。
<? super T>:是指 “下界通配符(Lower Bounds Wildcards)”,即泛型中的
类必须为当前类或者其父类。
List和原始类型List之间的区别原始类型 List 和带参数类型 List之间的主要区别是,在编译时编译器不会对
原始类型进行类型安全检查,却会对带参数的类型进行检查。
通过使用 Object 作为类型,可以告知编译器该方法可以接受任何类型的对象,比如
String 或 Integer。
它们之间的第二点区别是,你可以把任何带参数的类型传递给原始类型 List,但却不能
把 List传递给接受 List的方法,因为会产生编译错误。
List<?>和List之间的区别是什么?List<?> 是一个未知类型的 List,而 List 其实是任意类型的 List。你可以把
List, List赋值给 List<?>,却不能把 List赋值给 List ct>。
单元测试
junit
特点:
- JUnit 是一个开放的资源框架,用于编写和运行测试。
- 提供注释来识别测试方法。
- 提供断言来测试预期结果。
- 提供测试运行来运行测试。
- JUnit 测试允许你编写代码更快,并能提高质量。
- JUnit 优雅简洁。没那么复杂,花费时间较少。
- JUnit 测试可以自动运行并且检查自身结果并提供即时反馈。所以也没有必要人工梳理测试结果的报告。
- JUnit 测试可以被组织为测试套件,包含测试用例,甚至其他的测试套件。
- JUnit 在一个条中显示进度。如果运行良好则是绿色;如果运行失败,则变成红色。
- JUnit 知识经常 和测试驱动开发的讨论融合在一起。可以参考 Kent Beck 的《Test -Driven Development: By Example》一书(有中文版和影印版)。
junit和Spring的结合
mock
mockito
内存数据库(h2)
正则表达式
java.lang.util.regex.*
常用的Java工具库
apache-commons
google-guava
netty
API&SPI
API
API和SPI的关系和区别
API 和 SPI 都是相对的概念,他们的差别只
在语义上,API 直接被应用开发人员使用,SPI 被框架扩展人员使用
如何定义SPI
SPI的实现原理
异常
Error和Exception
⼆者都是 Java 异常处理的重要⼦类, 各⾃都包含⼤量⼦类。
均继承自 Throwable 类。
Error 表⽰系统级的错误, 是 java 运⾏环境内部错误或者硬件问题, 不能指望程序
来处理这样的问题, 除了退出运⾏外别⽆选择, 它是 Java 虚拟机抛出的。
Exception 表⽰程序需要捕捉、 需要处理的常, 是由与程序设计的不完善⽽出现的
问题, 程序必须处理的问题
异常类型
Java 中的异常, 主要可以分为两⼤类, 即受检异常(checked exception)和非
受检异常( unchecked exception)。
异常相关关键字
throws、 throw、 try、 catch、 finally;
try⽤来指定⼀块预防所有异常的程序;
catch⼦句紧跟在 try 块后⾯, ⽤来指定你想要捕获的异常的类型;
finally 为确保⼀段代码不管发⽣什么异常状况都要被执⾏;
throw 语句⽤来明确地抛出⼀个异常;
throws⽤来声明⼀个⽅法可能抛出的各种异常;
正确处理异常
1、 ⾃⼰处理。 2、 向上抛,交给调⽤者处理。
自定义异常
⼀般通过继承 Exception 的⼦类的⽅式实
现。
异常链
try-with-resources
finally和return的执行顺序
如果 try 中有 return 语句, 那么 finally 中的代码还是会执⾏。因为 return 表⽰的是
要整个⽅法体返回, 所以,finally 中的语句会在 return 之前执⾏。
时间处理
时区
冬令时和夏令时
时间戳
时间戳是指格林威治时间 1970 年 01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 0
1 月 01 日 08 时 00 分 00 秒)起至现在的总秒数。
Java中时间API(Java8)
格林威治时间
GET、UTC、GMT、CST几种常见时间的含义和关系
欧洲中部时间(英語:Central European Time,CET)是比世界标准时间(UTC)
早一个小时的时区名称之一。它被大部分欧洲国家和部分北非国家采用。冬季时间为 UTC
+1,夏季欧洲夏令时为 UTC+2。
格林尼治标准时间—GMT
北京时间—CST
SimpDateFormat的线程安全性问题
Java8中时间处理
Instant: 时间戳
Duration: 持续时间, 时间差
LocalDate: 只包含⽇期, ⽐如: 2016-10-20
LocalTime: 只包含⽇期, ⽐如: 23 10
LocalDateTime: 只包含⽇期, ⽐如: 2016-10-20 23 21
Period: 时间段
ZoneOffset: 时区偏移量, ⽐如: +8:00
ZonedDateTime: 带时区的时间
Clock: 时钟, ⽐如获取⽬前美国纽约的时间
如何在东八区的计算机上获取美国时间
美国洛杉矶时间
LocalDateTime now = LocalDateTime.now(ZoneId.of(“America/Los_Angeles”));
System.out.println(now);
yyyy和YYYY有什么区别?
编码方式
什么是ASCII?
美国信息交换标准代码是基于拉丁字母的⼀套电脑编码系统,主要⽤于显⽰现代英语和其他西欧语⾔。
它是现今最通⽤的单字节编码系统,并等同于国际标准 ISO/IEC646。
Unicode
Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业
界标准。它对世界上大部分的文字系统进行了整理、编码,使得计算机可以用更为简单的方
式来呈现和处理文字。
有了Unicode为啥还需要UTF-8
UTF-8、UTF-16、UTF-32区别
UTF-8 使用一至四个字节为每个字符编码
UTF-16 使用二或四个字节为每个字符编码
UTF-32 使用四个字节为每个字符编码
有了UTF8为什么还需要GBK
其实 UTF8 确实已经是国际通用的字符编码了,但是这种字符标准毕竟是外国定的,
而国内也有类似的标准指定组织,也需要制定一套国内通用的标准,于是 GBK 就诞生了。
GBK、GB2312、GB18030之间的区别
GB2312(1980 年):16 位字符集,收录有 6763 个简体汉字,682 个符号,共 7445 个字符; 优点:适用于简体中文环境,属于中国国家标准,通行于大陆,新加坡等地也使用此编码; 缺点:不兼容繁体中文,其汉字集合过少。
GBK(1995 年):16 位字符集,收录有 21003 个汉字,883 个符号,共 21886 个字符; 优点:适用于简繁中文共存的环境,为简体 Windows 所使用(代码页 cp936),向下完全兼容 gb2312,向上支持 ISO-10646 国际标准 ;所有字符都可以一对一映射到unicode2.0 上; 缺点:不属于官方标准,和 big5 之间需要转换;很多搜索引擎都不能很好地支持 GBK 汉字。
GB18030(2000 年):32 位字符集;收录了 27484 个汉字,同时收录了藏文、蒙文、维吾尔文等主要的少数民族文字。 优点:可以收录所有你能想到的文字和符号,属于中国最新的国家标准; 缺点:目前支持它的软件较少。
URL编解码
网络标准 RFC 1738 做了硬性规定 :只有字母和数字[0-9a-zA-Z]、一些特殊符号
“$-_.+!*’(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于 URL;
U R L 编 码 可 以 使 用 不 同 的 方 式 , 如 e s c a p e , U R L E n c o d e ,
encodeURIComponent。
Big Endian 和Little Endian
字节序,也就是字节的顺序,指的是多字节的数据在内存中的存放顺序。
Big Endian 是指低地址端 存放高位字节。
Little Endian 是指低地址端 存放低位字节。
如何解决乱码问题
语法糖
什么是语法糖
糖块一 、 switch 支持 String 与枚举
糖块二 、 泛型与类型擦除
糖块三 、自动装箱与拆箱
糖块四 、 方法变长参数
糖块五 、 枚举
糖块六 、 内部类
糖块七 、条件编译
糖块八 、 断言
糖块九 、 数值字面量
糖块十 、 for-each
糖块十一 、try-with-resource
糖块十二 、lambda 表达式
糖衣炮弹 —— 语法糖使用过程中需要注意的点综合应用。
Java中语法糖原理、解语法糖
常见语法糖原理
- switch支持String与枚举
- 泛型
- 自动装箱与拆箱
- 方法变长参数
- 枚举
- 内部类
- 条件编译
- 断言
- 数值字面量
- for-each
- try-with-resource
- Lambda表达式
以下是 lambda 表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
本地变量类型推断
record
JMS
什么是Java消息服务
JMS消息传送模型
JMX
java.lang.management.*
javax.management.*
Java 8
Lambda表达式
Stream API
时间API
阅读源码
String
Integer
Long
Enum
Big Decimal
ThreadLocal
ClassLoader&URL ClassLoader
ArrayList & LinkedList
HashMap& LinkedHashMap & TreeMap & CouncurrentHashMap
HashSet & LinkedHashSet & TreeSet
Java并发编程
并发与并行
什么是并发
并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
什么是并行
并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
并发与并行的区别
线程
线程与进程的区别
线程的实现
主流的操作系统都提供了线程实现,实现线程主要有3种方式:使用内核线程实现、使用用户线程实现和使用用户线程加轻量级进程混合实现。
线程的状态
Java中线程的状态分为6种:
- 1.初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 1.运行(RUNNABLE):Java线程中将就绪(READY)和运行中(RUNNING)两种状态笼统的称为“运行”。
- 就绪(READY):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中并分配cpu使用权 。
- 运行中(RUNNING):就绪(READY)的线程获得了cpu 时间片,开始执行程序代码。
- 3.阻塞(BLOCKED):表示线程阻塞于锁(关于锁,在后面章节会介绍)。
- 4.等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 5.超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
-
SCHED_OTHER 分时调度策略,(默认的)
- SCHED_FIFO 实时调度策略,先到先服务
- SCHED_RR 实时调度策略,时间片轮转
Windows 采用基于优先级的、抢占调度算法来调度线程。
在操作系统中,一个Java程序其实就是一个进程。所以,我们说Java是单进程、多线程的!
主要有两种调度模型:协同式线程调度和抢占式调度模型。
多线程如何Debug
守护线程
使用setDaemon()方法通过传递true作为参数,使线程成为一个守护线程
创建线程的多种方式
继承Thread类创建线程
实现Runnable接口创建线程
通过Callable和FutureTask创建线程
通过线程池创建线程
线程池
自己设计线程池
submit()和execute()
线程池原理
为什么不允许使用Executors创建线程池
线程安全
什么是线程安全
多级缓存和一致性问题
CPU时间片和原子性问题
指令重排和有序性问题
线程安全和内存模型的关系
happens-before
as-if-serial
锁
可重入锁
阻塞锁
乐观锁与悲观锁
数据库相关锁机制
分布式锁
无锁
CAS
CAS的ABA问题
锁优化
偏向锁
轻量级锁
重量级锁
锁消除
锁粗化
自旋锁
死锁
什么是死锁
死锁的原因
如何避免死锁
- (互斥)尽量少用互斥锁,能加读锁,不加写锁。当然这条无法避免。
- (请求和保持)采用资源静态分配策略(进程资源静态分配方式是指一个进程在建立时就分配了它需要的全部资源).我们尽量不让线程同时去请求多个锁,或者在拥有一个锁又请求不到下个锁时,不保持等待,先释放资源等待一段时间在重新请求。
- (不剥夺)允许进程剥夺使用其他进程占有的资源。优先级。
- (循环等待)尽量调整获得锁的顺序,不发生嵌套资源请求。加入超时。
写一个死锁的程序
死锁问题如何排查
synchronized
synchronized是如何实现的
synchronized和lock之间关系
不使用synchronized如何实现一个线程安全的单例
synchronized和原子性
synchronized和可见性
synchronized和有序性
volatile
编译器指令重排和CPU指令重排
volatile的实现原理
内存屏障
volatile和原子性
volatile和可见性
volatile和有序性
有了synchronized为什么还需要volatile
线程相关方法
start & run
sleep & wait
notify & notifaAll
ThreadLocal
ThreadLocal原理
ThreadLocal底层的数据结构
写代码解决生产者消费者问题
并发包
Thread
Runnable
Callable
ReentrantLock
ReentrantReadWriteLock
Atomic*
Semaphore
CountDownLatch
ConcurrentHashMap
Executors
原始类型 List 和带参数类型 List List<?> 是一个未知类型的 List,而 List 特点: Instant: 时间戳 美国洛杉矶时间 美国信息交换标准代码是基于拉丁字母的⼀套电脑编码系统,主要⽤于显⽰现代英语和其他西欧语⾔。 Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业 UTF-8 使用一至四个字节为每个字符编码 其实 UTF8 确实已经是国际通用的字符编码了,但是这种字符标准毕竟是外国定的, GB2312(1980 年):16 位字符集,收录有 6763 个简体汉字,682 个符号,共 7445 个字符; 优点:适用于简体中文环境,属于中国国家标准,通行于大陆,新加坡等地也使用此编码; 缺点:不兼容繁体中文,其汉字集合过少。 GBK(1995 年):16 位字符集,收录有 21003 个汉字,883 个符号,共 21886 个字符; 优点:适用于简繁中文共存的环境,为简体 Windows 所使用(代码页 cp936),向下完全兼容 gb2312,向上支持 ISO-10646 国际标准 ;所有字符都可以一对一映射到unicode2.0 上; 缺点:不属于官方标准,和 big5 之间需要转换;很多搜索引擎都不能很好地支持 GBK 汉字。 GB18030(2000 年):32 位字符集;收录了 27484 个汉字,同时收录了藏文、蒙文、维吾尔文等主要的少数民族文字。 优点:可以收录所有你能想到的文字和符号,属于中国最新的国家标准; 缺点:目前支持它的软件较少。
网络标准 RFC 1738 做了硬性规定 :只有字母和数字[0-9a-zA-Z]、一些特殊符号 U R L 编 码 可 以 使 用 不 同 的 方 式 , 如 e s c a p e , U R L E n c o d e , 字节序,也就是字节的顺序,指的是多字节的数据在内存中的存放顺序。 Big Endian 是指低地址端 存放高位字节。 什么是语法糖 糖衣炮弹 —— 语法糖使用过程中需要注意的点综合应用。 以下是 lambda 表达式的重要特征: 并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
主流的操作系统都提供了线程实现,实现线程主要有3种方式:使用内核线程实现、使用用户线程实现和使用用户线程加轻量级进程混合实现。
Java中线程的状态分为6种: SCHED_OTHER 分时调度策略,(默认的) Windows 采用基于优先级的、抢占调度算法来调度线程。 在操作系统中,一个Java程序其实就是一个进程。所以,我们说Java是单进程、多线程的! 使用setDaemon()方法通过传递true作为参数,使线程成为一个守护线程
原始类型进行类型安全检查,却会对带参数的类型进行检查。
通过使用 Object 作为类型,可以告知编译器该方法可以接受任何类型的对象,比如
String 或 Integer。
它们之间的第二点区别是,你可以把任何带参数的类型传递给原始类型 List,但却不能
把 ListList<?>和List
List单元测试
junit
junit和Spring的结合
mock
mockito
内存数据库(h2)
正则表达式
java.lang.util.regex.*
常用的Java工具库
apache-commons
google-guava
netty
API&SPI
API
API和SPI的关系和区别
API 和 SPI 都是相对的概念,他们的差别只
在语义上,API 直接被应用开发人员使用,SPI 被框架扩展人员使用
如何定义SPI
SPI的实现原理
异常
Error和Exception
⼆者都是 Java 异常处理的重要⼦类, 各⾃都包含⼤量⼦类。
均继承自 Throwable 类。
Error 表⽰系统级的错误, 是 java 运⾏环境内部错误或者硬件问题, 不能指望程序
来处理这样的问题, 除了退出运⾏外别⽆选择, 它是 Java 虚拟机抛出的。
Exception 表⽰程序需要捕捉、 需要处理的常, 是由与程序设计的不完善⽽出现的
问题, 程序必须处理的问题
异常类型
Java 中的异常, 主要可以分为两⼤类, 即受检异常(checked exception)和非
受检异常( unchecked exception)。
异常相关关键字
throws、 throw、 try、 catch、 finally;
try⽤来指定⼀块预防所有异常的程序;
catch⼦句紧跟在 try 块后⾯, ⽤来指定你想要捕获的异常的类型;
finally 为确保⼀段代码不管发⽣什么异常状况都要被执⾏;
throw 语句⽤来明确地抛出⼀个异常;
throws⽤来声明⼀个⽅法可能抛出的各种异常;
正确处理异常
1、 ⾃⼰处理。 2、 向上抛,交给调⽤者处理。
自定义异常
⼀般通过继承 Exception 的⼦类的⽅式实
现。
异常链
try-with-resources
finally和return的执行顺序
如果 try 中有 return 语句, 那么 finally 中的代码还是会执⾏。因为 return 表⽰的是
要整个⽅法体返回, 所以,finally 中的语句会在 return 之前执⾏。
时间处理
时区
冬令时和夏令时
时间戳
时间戳是指格林威治时间 1970 年 01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 0
1 月 01 日 08 时 00 分 00 秒)起至现在的总秒数。
Java中时间API(Java8)
格林威治时间
GET、UTC、GMT、CST几种常见时间的含义和关系
欧洲中部时间(英語:Central European Time,CET)是比世界标准时间(UTC)
早一个小时的时区名称之一。它被大部分欧洲国家和部分北非国家采用。冬季时间为 UTC
+1,夏季欧洲夏令时为 UTC+2。
格林尼治标准时间—GMT
北京时间—CST
SimpDateFormat的线程安全性问题
Java8中时间处理
Duration: 持续时间, 时间差
LocalDate: 只包含⽇期, ⽐如: 2016-10-20
LocalTime: 只包含⽇期, ⽐如: 23 10
LocalDateTime: 只包含⽇期, ⽐如: 2016-10-20 23 21
Period: 时间段
ZoneOffset: 时区偏移量, ⽐如: +8:00
ZonedDateTime: 带时区的时间
Clock: 时钟, ⽐如获取⽬前美国纽约的时间
如何在东八区的计算机上获取美国时间
LocalDateTime now = LocalDateTime.now(ZoneId.of(“America/Los_Angeles”));
System.out.println(now);
yyyy和YYYY有什么区别?
编码方式
什么是ASCII?
它是现今最通⽤的单字节编码系统,并等同于国际标准 ISO/IEC646。
Unicode
界标准。它对世界上大部分的文字系统进行了整理、编码,使得计算机可以用更为简单的方
式来呈现和处理文字。
有了Unicode为啥还需要UTF-8
UTF-8、UTF-16、UTF-32区别
UTF-16 使用二或四个字节为每个字符编码
UTF-32 使用四个字节为每个字符编码
有了UTF8为什么还需要GBK
而国内也有类似的标准指定组织,也需要制定一套国内通用的标准,于是 GBK 就诞生了。
GBK、GB2312、GB18030之间的区别
URL编解码
“$-_.+!*’(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于 URL;
encodeURIComponent。
Big Endian 和Little Endian
Little Endian 是指低地址端 存放低位字节。
如何解决乱码问题
语法糖
糖块一 、 switch 支持 String 与枚举
糖块二 、 泛型与类型擦除
糖块三 、自动装箱与拆箱
糖块四 、 方法变长参数
糖块五 、 枚举
糖块六 、 内部类
糖块七 、条件编译
糖块八 、 断言
糖块九 、 数值字面量
糖块十 、 for-each
糖块十一 、try-with-resource
糖块十二 、lambda 表达式 Java中语法糖原理、解语法糖
常见语法糖原理
本地变量类型推断
record
JMS
什么是Java消息服务
JMS消息传送模型
JMX
java.lang.management.*
javax.management.*
Java 8
Lambda表达式
Stream API
时间API
阅读源码
String
Integer
Long
Enum
Big Decimal
ThreadLocal
ClassLoader&URL ClassLoader
ArrayList & LinkedList
HashMap& LinkedHashMap & TreeMap & CouncurrentHashMap
HashSet & LinkedHashSet & TreeSet
Java并发编程
并发与并行
什么是并发
什么是并行
并发与并行的区别
线程
线程与进程的区别
线程的实现
线程的状态
主要有两种调度模型:协同式线程调度和抢占式调度模型。
多线程如何Debug
守护线程
创建线程的多种方式
继承Thread类创建线程
实现Runnable接口创建线程
通过Callable和FutureTask创建线程
通过线程池创建线程
线程池
自己设计线程池
submit()和execute()
线程池原理
为什么不允许使用Executors创建线程池
线程安全
什么是线程安全
多级缓存和一致性问题
CPU时间片和原子性问题
指令重排和有序性问题
线程安全和内存模型的关系
happens-before
as-if-serial
锁
可重入锁
阻塞锁
乐观锁与悲观锁
数据库相关锁机制
分布式锁
无锁
CAS
CAS的ABA问题
锁优化
偏向锁
轻量级锁
重量级锁
锁消除
锁粗化
自旋锁
死锁
什么是死锁
死锁的原因
如何避免死锁
写一个死锁的程序
死锁问题如何排查
synchronized
synchronized是如何实现的
synchronized和lock之间关系
不使用synchronized如何实现一个线程安全的单例
synchronized和原子性
synchronized和可见性
synchronized和有序性
volatile
编译器指令重排和CPU指令重排
volatile的实现原理
内存屏障
volatile和原子性
volatile和可见性
volatile和有序性
有了synchronized为什么还需要volatile
线程相关方法
start & run
sleep & wait
notify & notifaAll
ThreadLocal
ThreadLocal原理
ThreadLocal底层的数据结构
写代码解决生产者消费者问题
并发包
Thread
Runnable
Callable
ReentrantLock
ReentrantReadWriteLock
Atomic*
Semaphore
CountDownLatch
ConcurrentHashMap
Executors