# Java笔记
# Flag
- 什么是 hash (opens new window)
- 什么是散列表(Hash Table) (opens new window)
- HashMap在Jdk1.7和1.8中的实现 (opens new window)
- HashMap的底层结构和实现原理 (opens new window)
- ConcurrentHashMap实现原理及源码分析 (opens new window)
- 初学者应该了解的数据结构:Array、HashMap 与 List (opens new window)
- 强一致性、弱一致性、最终一致性、读写一致性、单调读、因果一致性 的区别与联系 (opens new window)
- CAP 定理的含义 (opens new window)
- 二进制运算 (opens new window)
- 什么是位运算 (opens new window)
- java运算符 (opens new window)
- 数据库扩展性设计:使用二进制解决一条记录关联多个状态的问题 (opens new window)
# 多行字符串
- Multiline String 多行字符串
- Template String 模板字符串
- Text Blocks 文本块
Java 13 Text Blocks 第一次预览版,Java 14 Text Blocks 第二次预览版,Java 15 Text Blocks 正式版
# 函数重载
- 方法名相同,方法参数的个数和类型不同,通过个数和类型的不同来区分不同的函数;
- 方法的重载跟返回值类型和修饰符无关,Java的重载是发生在本类中的,重载的条件是在本类中有多个方法名相同;
- 参数列表不同(参数个数不同、参数类型不同)跟返回值无关;
# 访问控制修饰符
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public(interface default) | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
protected
需要从以下两个点来分析说明
子类与基类在同一包中:被声明为
protected
的变量、方法和构造器能被同一个包中的任何其他类访问;
子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的
protected
方法,而不能访问基类实例的protected
方法。
# classpath意义
- java项目中的classpath到底是什么 (opens new window)
- spring classpath:和classpath*:区别和实际应用 (opens new window)
- java项目中的classpath到底指向的哪里 (opens new window)
- eclipse项目中.classpath文件详解 (opens new window)
classpath
:只会到你的class
路径中查找文件,和classpath:/
是等价的,都是相对于类的根路径src
不是classpath
,WEB-INF/classes
和lib
才是classpath
,WEB-INF/
是资源目录, 客户端不能直接访问WEB-INF/classes
目录存放src
目录java
文件编译之后的class
文件、xml
、properties
等资源配置文件lib
和classes
同属classpath
,两者的访问优先级为:lib
>classes
classpath*
:不仅包含class
路径,还包括jar
文件中(class
路径)进行查找
注意:用
classpath*
需要遍历所有的classpath
,所以加载速度是很慢,尽量避免使用。**
表示在任意目录下,也就是说在WEB-INF/classes/
下任意层的目录
项目模块依赖深度:A –> B –> C,在B中没有配置文件的情况下,A中的
classpath*
加载到B的配置文件, C加载自己配置文件要用classpath*
,否则A加载配置文件无法加载到C的配置文件
# 日期时间
- 可变性 : 像日期和时间这样的类应该是不可变的,返回一个值,原来的对象不变
- 偏移性 : Date中的年份是从1900开始的,而月份是从0开始的
- 日期表示需要减
new Date(2020-1900,9-1,8)
这样才可以表示2020年9月8日 - 格式化: 格式化日期只对Date有用,Calendar则不行
- 线程不安全的,不能处理闰秒等
- Java8吸收了
Joda-Time
(该项目作者参与了Java8的time包开发)精华,开启了新的API
java.lang.System类
// 用于返回当前时间与1970年1月1日0:0:0之间以毫秒为单位的时间戳
public static native long currentTimeMillis();
// 返回正在运行的Java虚拟机的当前值,高分辨率时间源,以纳秒为单位
public static native long nanoTime();
java.util.Date类
- 两个构造器
new Date();
当前时间new Date(Long 毫秒数)
根据毫秒数创建指定日期
- 两个方法的使用
toString(
)` 显示当前的年,月,日,时,分,秒getTime()
获取当前date对象的对应的毫秒数(时间戳)
java.util.Date
和java.sql.Date
互相转换
Date date = new java.sql.Date();
java.sql.Date date2 = (java.sql.Date) date;
java.sql.Date date3 = new java.sql.Date(new Date().getTime());
java.text.SimpleDateFormat类
Date类的API不易于国际化,大部分被废弃,并且不是线程安全的
format()
方法 按照具体的格式格式化时间parse()
方法 将字符串解析成时间
java.time
的基础包
java.time | 包含值对象的基础包 |
---|---|
java.time.chrono | 提供不同日历系统的访问 |
java.time.format | 格式化和解析时间和日期 |
java.time.temporal | 包含底层框架和扩展特性 |
java.time.zone | 包含时区支持的类 |
新的java.time
包含了如下子类
类 | 作用 | 说明 |
---|---|---|
Instant | 表示时刻 | 对应jdk7之前的Date |
LocalDateTime | 获取当前系统的日期时间(内部不记录时区) | 可以认为由LocalDate和LocalTime组成 |
LocalDate | 获取当前系统的日期 | |
LocalTime | 获取当前系统的时间 | |
ZoneId | 时区,"5:00"和"Europe/Paris"、"Asia/Shanghai" | 除了处理与标准时间的时间差还处理地区时(夏令时,冬令时等) |
ZoneOffset | 时区,只处理 "6:00" | ZoneOffset是ZoneId的子类 |
ZoneDateTime | 一个在ISO-8601日历系统特定时区的日期和时间 | 其中每个时区都有对应的Id,每个地区Id都有"{区域}/{城市}" 例如 Asia/Shanghai等 |
ZonedDateTime | 处理日期和时间与相应的时区 | |
Duration | 持续时间,用于计算两个"时间"的间隔 | |
Period | 日期间隔,用于计算两个"日期"的间隔 | |
Clock | 使用时区提供对当前即时,日期和时间的访问 |
方法前缀
前缀 | 含义 | 示例 |
---|---|---|
now | 静态工厂方法, 用当前时间创建实例 | LocalDate.now(); |
of | 静态工厂方法 | LocalDate.of(2018, 12, 20); |
parse | 静态工厂方法, 关注于解析 | LocalDate.parse("2018-12-20"); |
get | 获取某个字段的值 | localDate.getYear(); |
is | 比对判断 | localDate.isAfter(LocalDate.now()); |
with | 基于当前实例创建新的实例, 但部分字段被更新 | localDate.withMonth(3); |
plus | 在当前实例基础上增加(值可负), 返回新实例 | localDate.plusDays(1); |
minus | 在当前实例基础上减小(值可负), 返回新实例 | localDate.minusDays(1); |
to | 基于当前实例转换出另一个类型的实例 | localDateTime.toLocalDate(); |
at | 把当前对象和另一个对象结合, 生成新的类型的实例 | localDate.atTime(21, 30, 50) |
format | 格式化 | localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
# RoundingMode
java.math.RoundingMode
是一个舍入枚举类
- https://blog.csdn.net/alanzyy/article/details/8465098 (opens new window)
- https://my.oschina.net/sunchp/blog/670909 (opens new window)
- https://blog.csdn.net/chendaoqiu/article/details/45841283 (opens new window)
# 几个参数详解
RoundingMode.CEILING
(对应BigDecimal.ROUND_CEILING
):取右边最近的整数RoundingMode.UP
(对应BigDecimal.ROUND_UP
):远离0取整,即负数向左取整,正数向右取整RoundingMode.DOWN
(对应BigDecimal.ROUND_DOWN
):从不在舍弃(即截断)的小数之前增加数字(其实就是截断的意思)RoundingMode.FLOOR
(对应BigDecimal.ROUND_FLOOR
):取左边最近的正数RoundingMode.HALF_UP
(对应BigDecimal.ROUND_HALF_UP
):四舍五入,负数原理同上RoundingMode.HALF_DOWN
(对应BigDecimal.ROUND_HALF_DOWN
):五舍六入,负数先取绝对值再五舍六入再负数RoundingMode.HALF_EVEN
(对应BigDecimal.ROUND_HALF_EVEN
):这个比较绕,整数位若是奇数则四舍五入,若是偶数则五舍六入RoundingMode.UNNECESSARY
(对应BigDecimal.ROUND_UNNECESSARY
):用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入
# Java锁
# synchronized
synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段。
synchronized锁住的是代码还是对象?
答案是:synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。
当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。
即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。
所以我们在用synchronized关键字的时候,尽量缩小代码段的范围,尽量不要在整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。
static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是Class,所以static synchronized方法也相当于全局锁,相当于锁住了代码段。
# Java异常
# Java内置异常
未经检查的RuntimeException异常 | 说明 |
---|---|
ArithmeticException | 算术错误,如被0除 |
ArrayIndexOutOfBoundsException | 数组下标出界 |
ArrayStoreException | 数组元素赋值类型不兼容 |
ClassCastException | 非法强制转换类型 |
IllegalArgumentException | 调用方法的参数非法 |
IllegalMonitorStateException | 非法监控操作,如等待一个未锁定线程 |
IllegalStateException | 环境或应用状态不正确 |
IllegalThreadStateException | 请求操作与当前线程状态不兼容 |
IndexOutOfBoundsException | 某些类型索引越界 |
NullPointerException | 非法使用空引用 |
NumberFormatException | 字符串到数字格式非法转换 |
SecurityException | 试图违反安全性 |
StringIndexOutOfBounds | 试图在字符串边界之外索引 |
UnsupportedOperationException | 遇到不支持的操作 |
Java定义在java.lang中的检查的异常 | 说明 |
---|---|
ClassNotFoundException | 找不到类 |
CloneNotSupportedException | 试图克隆一个不能实现Cloneable接口的对象 |
IllegalAccessException | 对一个类的访问被拒绝 |
InstantiationException | 试图创建一个抽象类或者抽象接口的对象 |
InterruptedException | 一个线程被另一个线程中断 |
NoSuchFieldException | 请求的字段不存在 |
NoSuchMethodException | 请求的方法不存在 |
# 协程
Java语言并没有对协程的原生支持,但是某些开源框架模拟出了协程的功能
- 异步时代-java的协程路在何方 (opens new window)
- 次时代Java编程(一):Java里的协程 (opens new window)
- java协程框架quasar和kotlin中的协程 (opens new window)
- https://github.com/kilim/kilim (opens new window)
- https://github.com/puniverse/quasar (opens new window)
# HTTP
Mime-Type/Content-Type/Media-Type
com.google.common.net.MediaType
guavajavax.ws.rs.core.MediaType
Jersey框架org.springframework.http.MediaType
spring框架
HTTP实现依赖库
HttpURLConnection
Java自带APIHttpClient
JDK11的API Java11 HttpClient小试牛刀 (opens new window)RestTemplate
默认实现是HttpURLConnection
,默认使用HttpMessageConverter将Http消息转换成POJO 或POJO转化成Http消息ForEntity
返回ResponseEntity
响应码、响应消息体等ForObject
只返回消息体exchange
配合HttpEntity
或RequestEntity
使用,返回ResponseEntity
WebClient
是Spring 5.0
开始提供的非阻塞响应式编程的Http工具。Apache HttpComponents
http://hc.apache.org (opens new window)okHttp
https://github.com/square/okhttp (opens new window)Netty
google-http-java-client
https://github.com/googleapis/google-http-java-client (opens new window)WebSocket
https://github.com/eclipse-ee4j/websocket-api (opens new window)- https://github.com/OpenFeign (opens new window)
- https://github.com/square/retrofit (opens new window)
- 使用Java 8,Netty和Reactive原则 https://github.com/ratpack/ratpack (opens new window)
Apache HttpClient GET拼接URL参数
Map<String, Object> params = new HashMap<>();
params.put("key1", "value1");
params.put("key2", "value2");
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
// 通过map集成entrySet方法获取entity循环遍历,获取迭代器
Iterator<Entry<String, Object>> iterator = params.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, Object> mapEntry = iterator.next();
nvps.add(new BasicNameValuePair(mapEntry.getKey(), mapEntry.getValue().toString()));
}
// 由于GET请求的参数都是拼装在URL地址后方,所以我们要构建一个URL,带参数
// 方式一:使用setParameters
URIBuilder uriBuilder = new URIBuilder(url);
// 封装请求参数
uriBuilder.setParameters(nvps);
uriBuilder.build();
// 方式二:使用setParameter
URIBuilder uriBuilder = new URIBuilder(url);
// 封装请求参数
for (String key : params.keySet()) {
uriBuilder.setParameter(key, params.get(key).toString());
}
uriBuilder.build();
// 方式三:转换参数并拼接
url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(nvps, Consts.UTF_8));
URIBuilder uriBuilder = new URIBuilder(url);
uriBuilder.build();
- 根据HttpGet反向获取键值对列表
HttpGet request = new HttpGet("http://example.com/?var=1&var=2");
//获取键值对列表
List<NameValuePair> params = new URIBuilder(request.getURI()).getQueryParams();
//转换为键值对字符串
String str = EntityUtils.toString(new UrlEncodedFormEntity(params, Consts.UTF_8));
# 泛型generics
- 协变(
<? extends T>
) - 逆变(
<? super T>
) - 不变(
T
)
泛型三种常用的使用方式:
可以有多个类型变量
- 泛型类:在类名后指定类型变量,如:
public class Pair<T, U> {
- 泛型接口:在接口名后指定类型变量,如:
public interface Generator<T, U> {
- 泛型方法:在修饰符后,返回类型前指定类型变量,如:
public static <T extends Object, E> T test(Class<T> a, Class<E> b) {