java
引入
浏览器输入一个地址,就能获取一个页面,页面资源可以由Java程序提供。
Java程序经过开发、编译、运行才能提供资源,整个过程由JDK(Java SE Development Kit,Java开发工具包)支持。
程序是指令集合,声明解决问题的步骤。Java语言实现的程序可以被计算机识别
程序要素:表达式(变量、操作符)、控制流(顺序、选择if、循环for)和代码块;用于展示有组织(分工、条件)的信息
JDK8 提供的功能,参考 Java8 Docs (oracle.com) Java Downloads | Oracle
JDK8文件结构
├── bin
├── jre
│ ├── bin
│ ├──server
│ ├── lib
│ │ ├── rt.jar
- Java语言 java.lang(The Java Programming Language ):特定的语法规则,组成Java API
- Java API /jre/rt.java(The Java Application Programming Interface):组件集合+三方库;(runtime.jar 含运行时类库)
- JVM /jre/server(The Java Virtual Machine):用于运行程序,提供内存管理和访问机制,内存管理包括 分配、回收和溢出,能避免大部分的内存泄漏和指针越界问题
- 工具 /bin:文档Javadoc;编译Javac;运行java;监控(javap 反编译class ,jcmd ,jconsole,jmap(H eap Dump 堆转储,堆内存信息) ,jstack (Thread Dump 线程转储,线程信息:根据线程状态判断线程停顿原因,wait 线程等待,block 线程阻塞,可以定位到具体方法) ,jstatd,jvisualvm)
JRE提供了Java 运行需要的东西:API+JVM;JDK包含JRE,还包含编译器和 运维监控工具
组件的概念:软件提供产品;组件提供可重复使用的半成品;程序员加工组件,做成产品
JDK8 中的8 指的版本,JDK8 是最常用的版本,考虑到兼容性,企业一般不会升级JDK版本,但新的JDK版本有很多易用的新特性。其他支持比较久的版本有 JDK11,JDK17。-> 程序通过迭代方式增加新特性,注意版本兼容问题
Java API
主要三类:
basic:泛型,Lambda,面向对象
run:注解,反射,异常
tool:集合,并发,IO,网络,安全
具体模块:
lang and utils:
java.lang:
注解: Annotation,
反射,动态代理: Type,InvocationHandler,
类加载: ClassLoader,
异常: NoSuchMethodException
类型: Void
java.math:BigDecimal
java.util:
并发: ThreadPoolExecutor, concurrent+atomic+locks
集合: Comparator
Lambda: Function,Stream,
Matcher, EnumMap, Objects
java.time:LocalDateTime
func lib:
java.io:File, Path, InputStream, Reader, Serializable
java.nio:ByteBuffer, Path, Charset, Channel
java.net:Socket, Inet4Address
java.sql:Statement, Connection
java.security:KeyPair
java.beans:BeanInfo, MetaData
Serializable: objects to stream of bytes.
JavaBeans:
- 元信息:描述Descriptor 类Class 字段名Property;字段类型 Type;方法 Method;构造器Constructor
- 字段类型转换 PropertyEditor
POJO(Plain Ordinary Java Object) 简单的Java对象,对象只有setter/getter基本操作,没有复杂的业务操作
jvm
Java程序写在.java文件中,经过 javac compiler 编译成 .class文件(bytecodes,JVM的机器语言),由JVM实例运行
因为JVM在不同的平台上有不同的实现,对Java程序隐藏平台差异性,因此Java程序编写一次就可以运行在不同的平台上,即Java程序有 #JVM跨平台 的特性
平台:平台是运行程序的硬件或软件环境。如 Microsoft Windows,Linux,Solaris OS和Mac OS。大多数平台是操作系统和底层硬件的组合
强类型
NULL
null,表示缺失。存在的问题是,不可参与计算。
Optional,明确表达,值可能是null,可用于默认值(数据库是 coalesce,co al esce;)、值存在后的计算、异常抛出 NullPointerException。
类型
- 数字
- 整型没有小数部分,允许负值,如 int, long
- 浮点型有小数部分,如 float, double
- 字符:char
参考:Java基本类型
基本数据类型的运算:
- 数值:算数;比较;转换:函数、类型、舍入
- 字符:编码;比较;转换:字符字符串、大小写
- 布尔:逻辑;条件
引用类型
- Number
- String
- Enum枚举(EnumMap)
- Collections
- LocalDateTime
- ClassLoader
#Java值传递
基本数据类型 传递值副本,引用类型 传递引用副本,原引用 与 引用副本 指向的是 同一个字段值
- 基本数据类型参数传递给方法,方法内赋值,方法外无感
- 引用类型参数传递给方法,方法内赋值,方法外有感
- 引用类型参数传递给方法,方法内交换引用,方法外无感:形参初始化为实参值的副本
String
immutable,保证线程安全 #String不可变
StringBuffer:修改数据的方法加锁
Java9 String存储方式从 char[] 改为 byte[],节省存储空间
Compact Strings 的设计主要基于以下两个观察:
- 大多数字符串都是由ASCII字符组成的:在实际应用中,大多数字符串都是由ASCII字符(Unicode码点范围在0-127之间)组成的,而不是包含较大范围的Unicode字符
- 字符串中的字符通常情况下无需额外的填充空间:由于UTF-16编码的限制,每个字符在 String 对象中都需要占用两个字节的内存空间,即使这些字符的实际编码只需要一个字节
String 应用
- 正则,用于校验
Date and Time
线程安全的不可变类,不用考虑线程安全
Enum
枚举最佳实践
- 枚举可代替常量
- 枚举整体是一个常量,可用于注解 @MyAnnotation(value = ResultEnum.SUCCESS) √;但如果获取一个枚举的某个值是不被允许的 @MyAnnotation(value = ResultEnum.SUCCESS.code) x 、@MyAnnotation(value = ResultEnum.SUCCESS.getCode)
- 使用枚举方法和属性,枚举与lambda结合
- 枚举线程安全,可实现单例模式
- 使用枚举集合和映射:EnumSet 和 EnumMap 来处理特定枚举项的集合和映射操作,Excel处理
- 小心序列化。跨不同应用程序或不同版本之间进行序列化时可能引发兼容性问题
Collections
集合容器可以存放的类型:List<Bean>,List<Interface> ,Bean 和 Interface 声明了通用方法
Queue
ArrayList
fast fail
HashMap
重写 hashcode
HashMap内部实现
- 数组+链表/红黑树:链表长度>8,转为 红黑树。避免长链表,提高查找、插入和删除操作的性能
- 哈希码与索引映射:通过键的哈希码(通过调用键对象的
hashCode()方法获取)和HashMap的容量计算出 数组下标 - 扩容与重新哈希:当HashMap中的元素数量达到负载因子(Load Factor)乘以容量时,会自动触发扩容操作。扩容涉及创建一个更大的数组,并将所有键值对重新分配到新的桶中,这也就是所谓的重新哈希。
- 并发性支持:Java 8引入了基于CAS(Compare and Swap)和synchronized等机制,使得HashMap在多线程并发环境下具备一定的线程安全性。而对于高并发场景,可以使用ConcurrentHashMap来替代HashMap。
ConcurrentHashMap 线程安全
- 分段锁(Segment-Level Locking):ConcurrentHashMap将内部的数据结构分为一系列独立的段(Segments),每个段维护着一部分键值对。不同的段可以由不同的线程同时访问,相互之间不会产生影响。在读取和写入操作时,只需要获取特定段的锁,而不是整个 ConcurrentHashMap 的锁,从而提高并发性能。
- 原子性操作(Atomic Operations):ConcurrentHashMap使用了原子性操作来确保读取、写入和更新操作的原子性。例如,
put()和remove()等方法使用了原子性的 CAS(Compare and Swap)操作来保证线程安全。 - 写入安全性(Visibility Guarantees):ConcurrentHashMap利用volatile关键字和内存屏障(Memory Barriers)来保证写入操作的可见性。当一个线程修改了某个段中的键值对后,其他线程可以立即看到这个修改,从而避免了线程间的数据不一致问题。
- 读写并发安全:volatile,散列表数据结构、线程并发,jdk1.8
- 迭代器支持:ConcurrentHashMap的迭代器(Iterator)是弱一致性的,可以允许在迭代期间进行修改操作,而不会抛出ConcurrentModificationException异常。这样可以实现高效的并发迭代操作
基本类型及包装类:基本类型无字段和方法,自然无法get/set
泛型
data-structure-and-algorithms
面向对象
面向对象很好的实现了复用和隔离,基于这样的认识:
- 解决问题的方式就是描述问题
- 单一功能组合
- 类型层次结构体现了形状间的相似性和差异性。基类表示思想核心,派生类表示核心的不同方式
- 重用时要考虑到业务扩展性
Class类、抽象类、Interfaces接口;
Generics泛型、Annotation注解(可重复、类型)、Reflection反射;
Throwable异常;
Class
类不能多继承的原因:防止两个相同的方法被子类继承,如果是两个相同的继承 既不会知道重写哪个被继承的父类,又不是重载.且会导致方法体合并
接口可以多继承的原因:当有相同的方法时候 二合一,因为接口里面的方法没有方法体
访问修饰符
protected:抽象类的一些默认配置(模板行为)方法 ,子类可用(包内或跨包)
Reflection
- 反射用类加载生成的Class,运行时操作类
- 反射与方法:多用于方法增强,根据输入或配置调用不同方法,调用私有方法和泛型方法
应用:OR Mappting,AOP(事务处理、日志、用户鉴权、全局异常处理、性能监控)
Annotation
.class:
//only support Class; interface is a class? y;
Lambda既然能用接口承接,可以 interface.class ,但无法 lamda.class
注解
- 注解为什么不能引用注解
- 注解为什么会有元注解
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解
constant:final static 声明。A constant is a variable whose value won't change after it's been defined.
Exception
清晰的表达 what-异常类型, where-异常堆栈, why-异常描述
Java异常架构
- Throable:功能,记录线程执行堆栈,写程序时不需要捕获会自动抛出
- Exception-异常
- RuntimeException JVM运行时异常,代码逻辑问题,编译器不会检查,不需要显示声明异常处理
- NullPointException
- ClassCastException
- 编译时异常(受检异常):显示声明异常处理,是 throw 还是 try-catch
- ClassNotFoundException
- IOException
- SocketTimeoutException 不需要重试
- 异常处理 try(资源释放)-catch(){ throw }-finally{// 日志保存} ;throws
- try/catch 都可以写 return , 不要在 finally 做 逻辑修改;先执行 finally 在返回 return 值
- throw early 提前检查null, catch late 尽量向外抛出
- 异常越明确越好,给处理异常的同伴足够信息,添加异常文档说明
- RuntimeException JVM运行时异常,代码逻辑问题,编译器不会检查,不需要显示声明异常处理
- Error-错误:JVM出问题,业务程序无法处理
- VirtualMachineError 虚拟机运行错误
- NoClassDefFoundError 类定义错误:编译期类存在,但运行期找不到
- OutOfMemoryError 内存不足错误
- StackOverflowError 栈溢出错误
- Exception-异常
NullPointException。解决:Java8 Optional,用于
- 返回类型为基本类型 int
- 数据库查询结果/远程调用返回-单条
- 集合里的元素取出来时
- Session 数据获取
- 级联调用
- 字典 key-value
- 替换 String.valueOf() (obj == null) ? "null" : obj.toString()
Stream 不支持异常:函数式接口内部捕获异常
NoClassDefFoundError 类定义错误:编译期类存在,但运行期找不到了,如 Maven的 scope的错误使用
- java.lang.NoClassDefFoundError: org/apache/catalina/WebResourceRoot
- IDEA 启动配置,add dependencies with "provided" scope to classpath
- scope不适用于加载模块,改用 profile
NoSuchMethodError
RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable 类来进行拦截 。 说 明 : 通过反射机制来调用方法 , 如果找不到方法 , 抛 出NoSuchMethodException。什么情况会抛出 NoSuchMethodError 呢?
二方包在类冲突时,仲裁机制(见Maven)能导致引入非预期的版本使类的方法签名不匹配,或者在字节码修改框架(比如:ASM)动态创建或修改类时,修改了相应的方法签名。这些情况,即使代码编译期是正确的,但在代码运行期时,会抛出 NoSuchMethodError
Jar包冲突问题及解决方案!-腾讯云开发者社区-腾讯云 (tencent.com)
异常与事务:@Transactional(rollbackFor = Exception.class) :声明事务回滚的异常类型
异常与父子类:父子类同名字段编译期不会检查,运行期会检查
异常与序列化
异常与泛型:为什么泛型类无法继承自 Throwable_揪克的博客-CSDN博客
延伸
finalize 优雅的启停项目
Spring Boot 系列:最新版优雅停机详解-阿里云开发者社区 (aliyun.com)
异常处理的最佳实践和要点
尽量缩小 try-catch 范围
try-with-resources
Exception 兜底
尽量缩小异常的影响范围,别让程序一遇异常就崩。
program-pattern
函数式
目标:
- 编程语言的整体目标是操作值,类、方法有助于表达值结构,值结构更方便扩展和复用。而传递简洁灵活的方法/代码(描述做什么 what,而不是怎么做 how),能更好地应对变化的需求
- 面向对象一定要用类包装,函数式减去了这一写法
特性:
- Lambda/方法引用 +FunctionalInterface,简化匿名实现类,实现方法参数化 (只要有接口,就可以使用函数式实现回调)
- 高阶函数、柯里化、持久化数据结构、延迟列表、模式匹配
- Stream:合并多个操作,易并行;模式有:filter, 切片, 查找, 匹配 ,map, reduce
- 数据集合,一个个处理,输入-输出(输出结果作为下一个输入)-输出
- 接口默认方法:代码向后兼容,避免接口中的方法都要声明实现类
- Optional
- CompletableFuture 声明异步逻辑
- LocalDateTime
// 功能:过滤绿苹果
// lambda 表达式 表达匿名实现类
List<SortApples.Apple> greenApples = filterApples(inventory, (SortApples.Apple a) -> SortApples.Color.GREEN.equals(a.getColor()));
public List<SortApples.Apple> filterApples(List<SortApples.Apple> inventory, Predicate<SortApples.Apple> p) {
List<SortApples.Apple> result = new ArrayList<>();
for (SortApples.Apple apple : inventory) {
//接口p 本身代表一个匿名实现类的声明,匿名实现类用[lambda/方法引用]表达
//接口.方法名 调用匿名实现类的方法。不需要重复写类名和方法定义,只需要和方法的参数类型和返回类型匹配即可
//Predicate接口,输入一个数据,返回boolean值,实现判断。
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
//方法引用 表达匿名实现类 可以和接口实现类方法名不同,只需要参数类型和返回值类型相同
List<SortApples.Apple> greenApples = filterApples(inventory, FilterApple::isGreenApple);
public static boolean isGreenApple(SortApples.Apple apple) {
return SortApples.Color.GREEN.equals(apple.getColor());
}
//Stream流,批量操作
List<SortApples.Apple> greenApples = inventory.stream()
.filter((SortApples.Apple a) -> SortApples.Color.GREEN.equals(a.getColor()))
.collect(Collectors.toList());
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R, A> R collect(Collector<? super T, A, R> collector);
要求:
- 参数不共享不可变
应用于 设计模式,重构,测试,调试
- lambda 用于包装变动的代码;关注逻辑,控制(通用)和逻辑(变动)分离;方法引用 使代码更简短
/**
* 1. 先删后增/先查后更新(逻辑),为保证数据正确性,需加锁(控制)
* 2. 非核心功能(逻辑),为避免异常中断流程,需要全局try-catch(控制)
*/
// 1. 面向对象,模板模式,方法嵌套
saveOrUpdate() {
// lock
// new method: remove then save
// unlock
}
// 2. 函数式
withDistributedLock(lockKey, () -> {
// remove then save
});
// new method: lock then unlock
private LatestStageOrderStatusDTO withDistributedLock(String lockKey, Supplier<LatestStageOrderStatusDTO> s) {
RLock rlock = redissonClient.getLock(lockKey);
try {
rlock.lock(systemConfiguration.getAssistant().getUpdateStageStatusLockTimeout(), TimeUnit.SECONDS);
// 业务逻辑
return s.get();
} catch (Exception e) {
log.error("更新分期订单状态异常,error:", e);
throw e;
} finally {
if (rlock.isLocked() && rlock.isHeldByCurrentThread()) {
rlock.unlock();
}
}
}
并发
计算机的组成:CPU+存储+I/O,都是资源;一个CPU一次只能执行一条指令
操作系统将资源分配给进程。进程是资源分配的基本单位;N核CPU可以使N个进程并行(Parallelism);线程附属于进程,是任务并发(Concurrency)执行的基本单元。进程和线程的产生,可以充分利用多核CPU
tomcat 是一个Java程序,启动后是一个进程, 可以运行多个WEB应用,映射到一个虚拟机进程,
通过独立上下文,类加载,包命名空间(应用名称)确保WEB应用的类名唯一
线程模型
- 线程对象成本高
- 生命周期
- 优劣
- 注意,解决
Java线程模型:java.util.concurrent包
1个线程是1个Thread 对象,用于执行任务。线程对象创建成本高,通常用 线程池管理,避免线程频繁创建。
成本高的原因:
- 需分配调用栈内存,用于跟踪方法间的调用和对本地C代码的调用
- 基于Java虚拟机的实现,Java线程映射到一个内核线程
生命周期:
- New:thread#start
- Ready:获取到 cpu 外的资源,等待 jvm 依靠操作系统 分配 cpu 时间片
- Running:获取到 cpu 时间片,执行任务 thread#run
- ready 和 running 不是 Java程序能控制的,合成一个状态 runnable
- Blocked:
- 进入条件是:1. 等锁 synchronized 2. 调用 object#wait Thread#sleep() thread#join 3. IO 操作;满足条件后,放弃CPU
- wait 需锁。进入 Blocked 后,会放弃锁
- 结束,则进入 ready 状态
- 进入条件是:1. 等锁 synchronized 2. 调用 object#wait Thread#sleep() thread#join 3. IO 操作;满足条件后,放弃CPU
- Terminated:进入条件是 1. run 执行完毕 2. 线程异常;终止后,线程无效,即线程只能执行一次
线程优势和劣势 - 优势:
- 劣势 1. 线程对象成本高 2. 上下文切换
线程使用中的问题
- 多个线程共享CPU资源,因CPU时间片的分配不确定,所以线程执行顺序和时间不可预测,难以调试
- 多个线程访问共享资源,因执行顺序(时间)导致值错误(竞态条件),需保证同一时刻只有一个线程能够访问共享资源(同步)
- 共享资源同步:1. 锁 synchronized,ReentrantLock->ThreadPoolExecutor 线程池,ForkJoinPool 异步任务 2. volatile 变量值因为线程操作而变化,有易变性 3. atomic#incrementAndGet 原子变量
- 不共享资源(无锁):1. 每个线程使用不同的对象,如 ThreadLocalRandom 2. 单例对象,不过共享字段需要同步 3. CAS,适用于无竞争访问、只读
- 不可变资源:1. 不可变类 private final,或 无全局变量 2. 不可变集合 unmodifiable/CopyOnWrite list ,set map
- 锁的活性和上下文切换
- 活性:死锁-互等、饥饿-长时等待、活锁-互让
- 注意:锁粒度尽量小
- 锁的内部优化:1. 偏向。无竞争时标记为偏向锁,一直持有 2. 轻量,CAS获取锁,失败则自旋直到获取锁,避免进入阻塞状态;适用于同步块执行时间短 3. 重量级,自旋时间过长或CAS释放锁失败,则挂起释放锁进入Blocked状态
- 自旋用于等待资源,可以是锁,变量状态变化
- 资源争用,资源持有而不释放。 1. CPU、数据库连接、文件资源稀缺;常见方案是排队,默认非公平(允许不公平的资源调度出现)可减少上下文切换次数 2. BUG:资源使用完不释放 3. 任务无法进展,如高并发,任务积压
共享内存-Java内存模型,共享内存(堆内存,涉及对象、静态、数组)而不是消息传递,显式声明同步。
确保多线程下共享内存的正确访问(同步)。满足共享内存的可见性、有序性、原子性
- 可见性Visibility:共享变量修改结果可见;锁,volatile,atomic 实现。
- 有序性Ordering:程序执行顺序符合预期,禁止指令重排;锁,volatile实现。Java内存模型有序性语义 happens-before
- synchronized 保证的顺序性,是程序串行化执行。是不彻底的顺序
- volatile 保证的顺序性,是禁止JVM编译器和处理器的重排,是彻底的顺序 program-pattern#单例
- 原子性Atomicity:对共享变量的操作是原子的,要么发生,要么未发生,不会看见中间状态。锁,CAS(原子类atomic)实现
atomic 确保原子性,多用于变量更新值依赖当前值,如自增;volatile 确保有序性,适用于单个变量的读写操作
happens-before:描述两个操作的内存可见性。操作X happens-before Y,则 X 的结果对 Y 可见
- 线程内:字节码的顺序表达了 happens-before,单如果后者不数据依赖前者,可能指令重排
- 线程间:
- 加锁->解锁;lovatile 读->写
- 线程启动->第一个操作;线程最后一个操作->终止;线程中断->接收中断事件
- 构造器最后一个操作->析构器第一个操作
指令重排:为加快执行速度,改进编译顺序/处理器指令并行/缓存,不保证多线程中的代码顺序和执行顺序,造成可见和有序问题。一个操作的执行结果对另一个操作可见,则两者必须满足先行先发生原则。1. 代码顺序、传递 abc 2. volatile 写先发生于后面的读 前面确保顺序一致和数据依赖的前后关系 3. 线程启动、中断(调用检测)、终结;对象的初始化和结束
同步原语:涉及到虚拟机,操作系统和硬件
* 锁 synchronized:monitor enter,exit
* volatile:变量易变,走主内存(线程共享内存),不走工作内存(线程独有内存,副本无效)。确保数据一致性
* 主内存,L1 L2 缓存,工作内存
* final:初始化避免溢出
* static:初始化先于调用
锁
- synchronized,ReentrantLock
- synchronized 自身优化,减少CPU(自旋)和上下文切换(阻塞)开销
- 偏向,无竞争时;对象头存线程ID,加锁释放锁消耗小
- 轻量级,竞争;自旋消耗CPU等待
- 重量级,自旋获取锁失败;阻塞,阻塞和唤醒引起上下文切换
- CountDownLatch:计数器为0,所有线程结束等待,如火箭发射:所有的检查工作准备完成后才能点火
- CyclicBarrier/Phaser:可重用,所有线程到达一处,可以执行一个屏障动作。如分段任务有序执行 司令命令所有士兵完成任务,每个士兵完成任务后报道,所有士兵完成任务,这个任务才执行完成
- Semaphore:共享资源同时被访问的线程数,如流量控制,启动30个线程读取几万个文件到内存,并保存到数据库,数据库连接数只有10个;控制只允许10个线程并发执行获取数据库连接,即最大并发数为10
- Exchanger:两个线程交换数据,如生产者消费者模式
监控
jps、jstack
- 程序卡住,检查 1. 死锁 2. 线程阻塞在锁上 3. 线程长时间运行,查看调用栈 4. 线程池配置 5. 结合jmap 确认内存泄漏
常用
- 线程池:ThreadPoolExecutor,等待队列无界,OOM
- 配置:corePoolSize 核心线程数,CPU密集则 CPU数,IO密集则 CPU2倍+;maximumPoolSize;keepAliveTime 线程空闲时间,线程数量>corePoolSize,多余线程等待新任务的最长时间,任务生命周期短,小点,如30s;workQueue,等待执行任务的阻塞队列,已知任务量,用有界 ArrayBlockingQueue;RejectedExecutionHandler 拒绝策略,线程池和工作队列都满时,拒绝新任务的策略,AbortPolicy 默认,直接抛异常
- 并发集合:Queue
- fork/join 1. 大量图片处理,缩放、裁剪、滤镜应用;2. 日志文件,每个文件块单独处理。提取关键词,如访问统计、错误日志
- ThreadLocal,避免内存泄漏
文件网络
java.io:File, Path, InputStream, Reader, Serializable
java.nio:ByteBuffer, Path, Charset, Channel
java.net:Socket, Inet4Address
java.sql:Statement, Connection
工具:
Commons IO – Commons IO Overview (apache.org)
https://github.com/srikanth-lingala/zip4j
Security
Java提供的安全:
- 运行时安全机制
- 类加载字节码验证
- ClassLoader 隔离模块
- SecurityManager 机制,限制代码运行时能力,可以定制 policy 文件和权限定义
- Java 安全框架 API
- 加解密 API
- 授权鉴权
其他常见安全问题
- SQL 注入、XML 注入
- 调用操作系统接口,输入文字
- XSS 攻击
应用开发
解决方案
广告,搜索,支付,电商,秒杀
游戏,通讯,社交,CMS,ERP,OA
工具:代码生成,权限管理
data
dev-tools
框架
spring
日志
记录
- logback:日志打印保存
- sleuth
linux
日志工具
elastic, kibana,
抓包
- 浏览器:请求和响应,时间统计
- Charles:安卓抓包
- metersphere:流程、并发测试
响应处理
http 接口设计:application/json
接口设计上要考虑扩展性,查询类接口尽量不要直接用基础数据类型或包装类做入参,而应该设计为类
老接口更新要注意向下兼容,不要随便就删除字段或者修改字段名。
文件上传类接口尽量避免使用base64传输,而应该用form-data请求方式,后端spring mvc使用MultipartFile接收
接口url命名
带上版本号,但不要带服务名称,微服务时代,都是独立服务独立部署,ip+端口即服务。
规则:/{版本号}/{业务名称}
推荐的接口url形式:/v1/biz/xxx
不推荐的接口url形式:/loan/bms/v1/biz/xxx
校验
统一使用spring mvc validation机制,额外的参数校验可以硬编码或者使用jsonschema工具实现
- 响应统一使用 ApiResponse<>
- 正常响应,@RestControllerAdvice + implements ResponseBodyAdvice+自定义注解,将响应包装成 ApiResponse
- 异常响应,使用 spring-web 异常处理机制 @RestControllerAdvice/ @ExceptionHandler,将异常包装为 ApiResponse
- 业务异常定义
- ApiException:controller层使用
- BizException:业务层使用
- IBaseCodeType:异常编码
- 异常捕获时
- 需继续处理,要打印warn日志
- 向外抛出,抛出整个异常 throw new BizException(BaseCodeEnum.UN_AUTHORIZED, e);
队列
资料
map:docs/roadmap/Java学习路线.md · 程序员鱼皮/code-roadmap - Gitee.com
练习:https://www.nowcoder.com/exam/intelligent
书籍:
Java编程思想_第5版_中文(gitee.com)_2020-07-30
《Effective Java (高效 Java) 第三版》 - 书栈网 · BookStack_2020-01-09
core java 12th
java8实战
Head first Java
深入理解Java虚拟机3
Java并发编程实战
Java性能优化权威指南
Spring实战
Netty 实战
Could Native Java
大型分布式网站架构设计与实战
深入分布式缓存,从原理到实践
career-anchor-左耳听风