# Java笔记

# Flag

# 多行字符串

  • 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意义

  • classpath:只会到你的class路径中查找文件,和classpath:/是等价的,都是相对于类的根路径
    • src不是classpathWEB-INF/classeslib才是classpathWEB-INF/是资源目录, 客户端不能直接访问
    • WEB-INF/classes目录存放src目录java文件编译之后的class文件、xmlproperties等资源配置文件
    • libclasses同属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.Datejava.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是一个舍入枚举类

# 几个参数详解

  • 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语言并没有对协程的原生支持,但是某些开源框架模拟出了协程的功能

# HTTP

Mime-Type/Content-Type/Media-Type

  • com.google.common.net.MediaType guava
  • javax.ws.rs.core.MediaType Jersey框架
  • org.springframework.http.MediaType spring框架

HTTP实现依赖库

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) {