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
  1. Java语言 java.lang(The Java Programming Language ):特定的语法规则,组成Java API
  2. Java API /jre/rt.java(The Java Application Programming Interface):组件集合+三方库;(runtime.jar 含运行时类库)
  3. JVM /jre/server(The Java Virtual Machine):用于运行程序,提供内存管理和访问机制,内存管理包括 分配、回收和溢出,能避免大部分的内存泄漏和指针越界问题
  4. 工具 /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:

  1. 元信息:描述Descriptor 类Class 字段名Property;字段类型 Type;方法 Method;构造器Constructor
  2. 字段类型转换 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。

类型

基本数据类型的运算:

引用类型

#Java值传递
基本数据类型 传递值副本,引用类型 传递引用副本,原引用 与 引用副本 指向的是 同一个字段值

String

immutable,保证线程安全 #String不可变
StringBuffer:修改数据的方法加锁

Java9 String存储方式从 char[] 改为 byte[],节省存储空间
Compact Strings 的设计主要基于以下两个观察:

  1. 大多数字符串都是由ASCII字符组成的:在实际应用中,大多数字符串都是由ASCII字符(Unicode码点范围在0-127之间)组成的,而不是包含较大范围的Unicode字符
  2. 字符串中的字符通常情况下无需额外的填充空间:由于UTF-16编码的限制,每个字符在 String 对象中都需要占用两个字节的内存空间,即使这些字符的实际编码只需要一个字节

String 应用

Date and Time

线程安全的不可变类,不用考虑线程安全

Enum

枚举最佳实践

Collections

集合容器可以存放的类型:List<Bean>,List<Interface> ,Bean 和 Interface 声明了通用方法

Queue

ArrayList

fast fail

HashMap

重写 hashcode

HashMap内部实现

  1. 数组+链表/红黑树:链表长度>8,转为 红黑树。避免长链表,提高查找、插入和删除操作的性能
  2. 哈希码与索引映射:通过键的哈希码(通过调用键对象的hashCode()方法获取)和HashMap的容量计算出 数组下标
  3. 扩容与重新哈希:当HashMap中的元素数量达到负载因子(Load Factor)乘以容量时,会自动触发扩容操作。扩容涉及创建一个更大的数组,并将所有键值对重新分配到新的桶中,这也就是所谓的重新哈希。
  4. 并发性支持:Java 8引入了基于CAS(Compare and Swap)和synchronized等机制,使得HashMap在多线程并发环境下具备一定的线程安全性。而对于高并发场景,可以使用ConcurrentHashMap来替代HashMap。

ConcurrentHashMap 线程安全

  1. 分段锁(Segment-Level Locking):ConcurrentHashMap将内部的数据结构分为一系列独立的段(Segments),每个段维护着一部分键值对。不同的段可以由不同的线程同时访问,相互之间不会产生影响。在读取和写入操作时,只需要获取特定段的锁,而不是整个 ConcurrentHashMap 的锁,从而提高并发性能。
  2. 原子性操作(Atomic Operations):ConcurrentHashMap使用了原子性操作来确保读取、写入和更新操作的原子性。例如,put()remove()等方法使用了原子性的 CAS(Compare and Swap)操作来保证线程安全。
  3. 写入安全性(Visibility Guarantees):ConcurrentHashMap利用volatile关键字和内存屏障(Memory Barriers)来保证写入操作的可见性。当一个线程修改了某个段中的键值对后,其他线程可以立即看到这个修改,从而避免了线程间的数据不一致问题。
  1. 迭代器支持:ConcurrentHashMap的迭代器(Iterator)是弱一致性的,可以允许在迭代期间进行修改操作,而不会抛出ConcurrentModificationException异常。这样可以实现高效的并发迭代操作

基本类型及包装类:基本类型无字段和方法,自然无法get/set

类加载

泛型

data-structure-and-algorithms

面向对象

面向对象很好的实现了复用和隔离,基于这样的认识:

Class类、抽象类、Interfaces接口;
Generics泛型、Annotation注解(可重复、类型)、Reflection反射;
Throwable异常;

Class

类不能多继承的原因:防止两个相同的方法被子类继承,如果是两个相同的继承 既不会知道重写哪个被继承的父类,又不是重载.且会导致方法体合并
接口可以多继承的原因:当有相同的方法时候 二合一,因为接口里面的方法没有方法体

访问修饰符

protected:抽象类的一些默认配置(模板行为)方法 ,子类可用(包内或跨包)

Reflection

应用:OR Mappting,AOP(事务处理、日志、用户鉴权、全局异常处理、性能监控)

Annotation

.class:
//only support Class; interface is a class? y;
Lambda既然能用接口承接,可以 interface.class ,但无法 lamda.class

注解

  1. 注解为什么不能引用注解
  2. 注解为什么会有元注解

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

注解通过反射获取。首先可以通过 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异常架构

NullPointException。解决:Java8 Optional,用于

Stream 不支持异常:函数式接口内部捕获异常

NoClassDefFoundError 类定义错误:编译期类存在,但运行期找不到了,如 Maven的 scope的错误使用

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

函数式

目标:

  1. 编程语言的整体目标是操作值,类、方法有助于表达值结构,值结构更方便扩展和复用。而传递简洁灵活的方法/代码(描述做什么 what,而不是怎么做 how),能更好地应对变化的需求
  2. 面向对象一定要用类包装,函数式减去了这一写法

特性:

// 功能:过滤绿苹果

// 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);

要求:

  1. 参数不共享不可变

应用于 设计模式,重构,测试,调试

  1. 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 对象,用于执行任务。线程对象创建成本高,通常用 线程池管理,避免线程频繁创建。
成本高的原因:

  1. 需分配调用栈内存,用于跟踪方法间的调用和对本地C代码的调用
  2. 基于Java虚拟机的实现,Java线程映射到一个内核线程
    生命周期:

线程使用中的问题

共享内存-Java内存模型,共享内存(堆内存,涉及对象、静态、数组)而不是消息传递,显式声明同步。
确保多线程下共享内存的正确访问(同步)。满足共享内存的可见性、有序性、原子性

atomic 确保原子性,多用于变量更新值依赖当前值,如自增;volatile 确保有序性,适用于单个变量的读写操作

happens-before:描述两个操作的内存可见性。操作X happens-before Y,则 X 的结果对 Y 可见

  1. 加锁->解锁;lovatile 读->写
  2. 线程启动->第一个操作;线程最后一个操作->终止;线程中断->接收中断事件
  3. 构造器最后一个操作->析构器第一个操作

指令重排:为加快执行速度,改进编译顺序/处理器指令并行/缓存,不保证多线程中的代码顺序和执行顺序,造成可见和有序问题。一个操作的执行结果对另一个操作可见,则两者必须满足先行先发生原则。1. 代码顺序、传递 abc 2. volatile 写先发生于后面的读 前面确保顺序一致和数据依赖的前后关系 3. 线程启动、中断(调用检测)、终结;对象的初始化和结束

同步原语:涉及到虚拟机,操作系统和硬件
 * 锁 synchronized:monitor enter,exit
 * volatile:变量易变,走主内存(线程共享内存),不走工作内存(线程独有内存,副本无效)。确保数据一致性
 * 主内存,L1 L2 缓存,工作内存
 * final:初始化避免溢出
 * static:初始化先于调用

  1. 偏向,无竞争时;对象头存线程ID,加锁释放锁消耗小
  2. 轻量级,竞争;自旋消耗CPU等待
  3. 重量级,自旋获取锁失败;阻塞,阻塞和唤醒引起上下文切换

监控
jps、jstack

常用

文件网络

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提供的安全:

其他常见安全问题

应用开发

解决方案

广告,搜索,支付,电商,秒杀
游戏,通讯,社交,CMS,ERP,OA
工具:代码生成,权限管理

data

dev-tools

框架

spring

日志

记录

日志工具
elastic, kibana,

抓包

响应处理

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工具实现

队列

lib-kafka

资料

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-左耳听风