# Java笔记

# Flag

JNDI是Java Naming and Directory Interface(Java命名与目录接口)其实和Spring依赖注入差不多的效果, 就是我们可以将我们需要的类注册进去,然后一般我们都是使用该类的实例对象,这时候因为刚才我们已经给每个类按照JNDI的规范进行了注入, 这时候我们直接通过JNDI的使用规则取出来我们想要的数据(实例对象)即可。

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

Windows隐藏窗口后台运行jar包(推荐vbs可设置JVM参数)

# 代码块文档注释

一定要用<pre>{@code }</pre>包裹代码块,或者进行HTML编码转义,否则将丢失格式

  • {@code ... } 用来包裹代码块,这样可以在JavaDoc中保留代码的格式,并且正确处理HTML标签和相关的特殊字符,如小于号<和大于号>
  • <pre> 是预格式化文本的HTML标签,它告诉JavaDoc这里的文本应该按照预设格式显示,保留空白字符,如空格和换行符。
/**
 * <pre>
 * List<Map<String, Object>> n = new ArrayList<>();
 * Map<String, Object> map = new HashMap<>();
 * {@code
 * List<Map<String, Object>> n = new ArrayList<>();
 * Map<String, Object> map = new HashMap<>();
 * }
 * List&lt;Map&lt;String, Object&gt;&gt; n = new ArrayList&lt;&gt;();
 * Map&lt;String, Object&gt; map = new HashMap&lt;&gt;();
 * </pre>
 *
 */

# 多行字符串

  • 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.version Java运行时环境版本
java.vendor Java运行时环境供应商
java.vendor.url Java供应商的 URL
java.home Java安装目录
java.vm.specification.version Java虚拟机规范版本
java.vm.specification.vendor Java虚拟机规范供应商
java.vm.specification.name Java虚拟机规范名称
java.vm.version Java虚拟机实现版本
java.vm.vendor Java虚拟机实现供应商
java.vm.name Java虚拟机实现名称
java.specification.version Java运行时环境规范版本
java.specification.vendor Java运行时环境规范供应商
java.specification.name Java运行时环境规范名称
java.class.version Java类格式版本号
java.class.path Java类路径
java.library.path 加载库时搜索的路径列表
java.io.tmpdir 默认的临时文件路径
java.compiler 要使用的JIT编译器的名称
java.ext.dirs 一个或多个扩展目录的路径
os.name 操作系统的名称
os.arch 操作系统的架构
os.version 操作系统的版本
file.separator 文件分隔符(在UNIX中是“/”)
path.separator 路径分隔符(在UNIX中是“:”)
line.separator 行分隔符(在UNIX中是“/n”)
user.name 用户的账户名称
user.home 用户的主目录
user.dir 用户的当前工作目录

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 线程结束

# 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实现依赖库

  • HttpURLConnection Java自带API
  • HttpClient JDK11的API Java11 HttpClient小试牛刀 (opens new window)
  • RestTemplate 默认实现是HttpURLConnection,默认使用HttpMessageConverter将Http消息转换成POJO 或POJO转化成Http消息
    • ForEntity返回ResponseEntity响应码、响应消息体等
    • ForObject只返回消息体
    • exchange 配合HttpEntityRequestEntity使用,返回ResponseEntity
  • WebClientSpring 5.0开始提供的非阻塞响应式编程的Http工具。

Apache HttpClient GET拼接URL参数

Map<String, Object> params = new HashMap<>();
params.put("key1", "value1");
params.put("key2", "value2");

// 方式一:使用setParameters
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,带参数

URIBuilder uriBuilder = new URIBuilder(url);
// 封装请求参数
uriBuilder.setParameters(nvps);
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) {