# Java笔记

# Flag

  • 有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。
  • 无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。

# 多行字符串

  • Multiline String 多行字符串
  • Template String 模板字符串
  • Text Blocks 文本块

Java 13 Text Blocks 第一次预览版,Java 14 Text Blocks 第二次预览版,Java 15 Text Blocks 正式版

# 函数重载

  • 方法名相同,方法参数的个数和类型不同,通过个数和类型的不同来区分不同的函数;
  • 方法的重载跟返回值类型和修饰符无关,Java的重载是发生在本类中的,重载的条件是在本类中有多个方法名相同;
  • 参数列表不同(参数个数不同、参数类型不同)跟返回值无关;

# 关键保留字

名称 说明
private 一种访问控制方式:私用模式
protected 一种访问控制方式:保护模式
public 一种访问控制方式:共用模式
abstract 表明类或者成员方法具有抽象属性
class
extends 表明一个类型是另一个类型的子类型,这里常见的类型有类和接口
final 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变
implements 表明一个类实现了给定的接口
interface 接口
native 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
new 用来创建新实例对象
static 表明具有静态属性
strictfp 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范
synchronized 表明一段代码需要同步执行
transient 声明不用序列化的成员域
volatile 表明两个或者多个变量必须同步地发生变化
break 提前跳出一个块
continue 回到一个块的开始处
return 从成员方法中返回数据
do 用在do-while循环结构中
while 用在循环结构中
if 条件语句的引导词
else 用在条件语句中,表明当条件不成立时的分支
for 一种循环结构的引导词
instanceof 用来测试一个对象是否是指定类型的实例对象
switch 分支语句结构的引导词
case 用在switch语句之中,表示其中的一个分支
default 默认,例如,用在switch语句中,表明一个默认的分支
try 尝试一个可能抛出异常的程序块
catch 用在异常处理中,用来捕捉异常
throw 抛出一个异常
throws 声明在当前定义的成员方法中所有需要抛出的异常
import 表明要访问指定的类或包
package
boolean 基本数据类型之一,布尔类型
byte 基本数据类型之一,字节类型
char 基本数据类型之一,字符类型
double 基本数据类型之一,双精度浮点数类型
float 基本数据类型之一,单精度浮点数类型
int 基本数据类型之一,整数类型
long 基本数据类型之一,长整数类型
short 基本数据类型之一,短整数类型
super 表明当前对象的父类型的引用或者父类型的构造方法
this 指向当前实例对象的引用
void 声明当前成员方法没有返回值
goto 保留关键字,没有具体含义
const 保留关键字,没有具体含义

# 访问控制修饰符

修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
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/下任意层的目录

前缀 例子 说明
classpath: classpath:com/myapp/config.xml 从classpath中加载。
file: file:/data/config.xml 作为 URL 从文件系统中加载。
http: http://myserver/logo.png 作为 URL 加载。
(none) /data/config.xml 根据 ApplicationContext 进行判断。

# 日期时间

  • 可变性 : 像日期和时间这样的类应该是不可变的,返回一个值,原来的对象不变
  • 偏移性 : 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异常

未经检查的异常 说明
ArithmeticException 算术错误,如被0除
ArrayIndexOutOfBoundsException 数组下标出界
ArrayStoreException 数组元素赋值类型不兼容
ClassCastException 非法强制转换类型
EnumConstantNotPresentException 枚举常量不存在异常。
EOFException 文件已结束异常
Exception 根异常。用以描述应用程序希望捕获的情况。
FileNotFoundException 文件未找到异常
IllegalArgumentException 调用方法的参数非法
IllegalMonitorStateException 非法监控操作,如等待一个未锁定线程
IllegalStateException 环境或应用状态不正确
IllegalThreadStateException 请求操作与当前线程状态不兼容
IndexOutOfBoundsException 某些类型索引越界
IOException 输入输出异常
NegativeArrayException 数组负下标异常
NegativeArraySizeException 数组大小为负值异常。当使用负数大小值创建数组时抛出该异常。
NullPointerException 非法使用空引用
NumberFormatException 字符串到数字格式非法转换
RuntimeException 运行时异常。是所有Java虚拟机正常操作期间可以被抛出的异常的父类。
SecurityException 试图违反安全性
SQLException 操作数据库异常
StringIndexOutOfBoundsException 试图在字符串边界之外索引
TypeNotPresentException 类型不存在异常。
UnsupportedOperationException 遇到不支持的操作
检查的异常 说明
ClassNotFoundException 找不到类
CloneNotSupportedException 试图克隆一个不能实现Cloneable接口的对象
IllegalAccessException 对一个类的访问被拒绝
InstantiationException 试图创建一个抽象类或者抽象接口的对象
InterruptedException 一个线程被另一个线程中断
NoSuchFieldException 请求的字段不存在
NoSuchMethodException 请求的方法不存在
错误 说明
java.lang.AbstractMethodError 抽象方法错误
java.lang.AssertionError 断言错
java.lang.ClassCircularityError 类循环依赖错误
java.lang.ClassFormatError 类格式错误
java.lang.Error 错误
java.lang.ExceptionInInitializerError 初始化程序错误
java.lang.IllegalAccessError 违法访问错误
java.lang.IncompatibleClassChangeError 不兼容的类变化错误
java.lang.InstantiationError 实例化错误
java.lang.InternalError 内部错误
java.lang.LinkageError 链接错误
java.lang.NoClassDefFoundError 未找到类定义错误
java.lang.NoSuchFieldError 域不存在错误
java.lang.NoSuchMethodError 方法不存在错误
java.lang.OutOfMemoryError 内存不足错误
java.lang.StackOverflowError 堆栈溢出错误
java.lang.UnknownError 未知错误
java.lang.UnsatisfiedLinkError 未满足的链接错误
java.lang.UnsupportedClassVersionError 不支持的类版本错误
java.lang.VerifyError 验证错误
java.lang.VirtualMachineError 虚拟机错误
java.lang.ThreadDeath 线程结束

# 协程

Java语言并没有对协程的原生支持,但是某些开源框架模拟出了协程的功能,Project Loom AJDK-Wisp

  • coroutine解决的是"协作式多任务"
  • visitor(访问者)模式解决的是"对修改关闭,对扩展开放", "它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作"

# HTTP

常量

  • java.net.HttpURLConnection
  • io.netty.handler.codec.http.HttpResponseStatus
  • org.springframework.http.HttpStatus
  • org.apache.http.HttpStatus
  • org.asynchttpclient.util.HttpConstants
  • org.apache.http.protocol.HTTP
  • org.springframework.http.HttpHeaders
  • javax.ws.rs.HttpMethod
  • org.springframework.http.HttpMethod

Mime/Content-Type/Media-Type

  • com.google.common.net.MediaType guava
  • javax.ws.rs.core.MediaType Jersey框架
  • org.springframework.http.MediaTypeorg.springframework.util.MimeTypeUtils spring框架
  • 在Tomcat配置文件conf/web.xml中的Default MIME Type Mappings部分定义
  • nginx配置文件conf/mime.types中定义

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)

泛型的通配符

  • ? 表示不确定的类型
  • T (type) 表示具体的类型
  • K V (key value) 分别代表键值中的Key Value
  • E (element) 代表Element

泛型三种常用的使用方式:

可以有多个类型变量

  • 泛型类:在类名后指定类型变量,如:public class Pair<T, U> {
  • 泛型接口:在接口名后指定类型变量,如:public interface Generator<T, U> {
  • 泛型方法:在修饰符后,返回类型前指定类型变量,如:public static <T extends Object, E> T test(Class<T> a, Class<E> b) {