Java学习记录

Java虚拟机

IDEA

JavaGuide IDEA 高效使用指南

改善体验的设置

  • Change font size with Command + Mouse Wheel:用鼠标滚轮改变字体大小
  • Sync setting across:选择设置同步的范围
    • IntelliJ IDEA instances only
    • All JetBrains IDE Products
  • File adn Code Templates:修改新建文件的默认模板(类,接口)
1
2
3
4
5
6
/**
* @author WYH
* @date ${YEAR}/${MONTH}/${DAY} ${HOUR}:${MINUTE}
**/
public class ${NAME} {
}
  • Live Templates:自定义快捷短语(比如sout)
  • File > New Projects Setup:设置新项目的Settings和Structure等

插件

  • CodeGlance Pro:显示当前文件代码概览

  • Github Copilot:AI

  • Key Promoter X:学习快捷键

  • Translation:翻译

  • Alibaba Java Code Guidelines:阿里巴巴代码规范

  • Rainbow Brackets:彩虹括号,清晰代码

  • Maven Helper:Maven依赖管理

  • Kafkalytic:Kafka管理

  • Zoolytic:ZooKeeper管理

快捷键

  • ctrl + H:查看当前类或接口的层次结构

  • ctrl + shift + B:查看方法或对象的实现

  • ctrl + E:查看最近的文件

  • ctrl + shift + E:查看最近的位置(窗口)

  • alt + home:使用导航

  • shift + esc:隐藏当前活跃窗口

  • 代码编辑

    • ctrl + shift + alt + T:打开重构框
  • alt + 7:查看Structure窗口

  • ctrl + alt + [/]:查看上一个/下一个 IDEA窗口

Maven

JavaGuide-Maven核心概念总结

JavaGuide-Maven使用技巧

Maven in 5 Minites官方上手教程

Java Features

函数式接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Function<T, R> - T作为输入,返回的R作为输出   
Function<String,String> fun = (x) -> {System.out.print(x+": ");return "Function";};
System.out.println(function.apply("hello world"));

// Predicate<T> - T作为输入,返回的boolean值作为输出
Predicate<String> pre = (x) ->{System.out.print(x);return false;};
System.out.println(": "+pre.test("hello World"));

// Consumer<T> - T作为输入,执行某种动作但没有返回值
Consumer<String> con = (x) -> {System.out.println(x);};
con.accept("hello world");

// Supplier<T> - 没有任何输入,返回T
Supplier<String> supp = () -> {return "Supplier";};
System.out.println(supp.get());

// BinaryOperator<T> - 两个T作为输入,返回一个T作为输出,对于“reduce”操作很有用
BinaryOperator<String> bina = (x,y) ->{System.out.print(x+" "+y);return "BinaryOperator";};
System.out.println(" "+bina.apply("hello ","world"));

乐观锁和悲观锁

参考JavaGuide 乐观锁和悲观锁详解

悲观锁:总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放

乐观锁:总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了

高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销。并且,悲观锁还可能会存在死锁问题,影响代码的正常运行。乐观锁相比悲观锁来说,不存在锁竞争造成线程阻塞,也不会有死锁的问题,在性能上往往会更胜一筹。不过,如果冲突频繁发生(写占比非常多的情况),会频繁失败和重试,这样同样会非常影响性能,导致 CPU 飙升

乐观锁一般会使用版本号机制或 CAS 算法实现,CAS 算法相对来说更多一些,这里需要格外注意。CAS 的全称是 Compare And Swap(比较与交换) ,用于实现乐观锁,被广泛应用于各大框架中

CAS 的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新

  • CAS 算法的问题
    • ABA 问题:解决思路是在变量前面追加上版本号或者时间戳。JDK 1.5 以后的 AtomicStampedReference 类就是用来解决 ABA 问题的,其中的 compareAndSet() 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值
    • 循环时间长开销大:CAS 经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功。如果 JVM 能支持处理器提供的 pause 指令那么效率会有一定的提升
    • 只能保证一个共享变量的原子操作:从 JDK 1.5 开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作

Java内存模型

JavaGuide JMM详解

Spring

mini-spring

mini-spring项目地址

视频教程

可拓展部分:只简单实现BeforeAdvice,有兴趣的同学可以帮忙实现另外几种Advice。定义MethodBeforeAdviceInterceptor拦截器,在执行被代理方法之前,先执行BeforeAdvice的方法。BeforeAdvice、AfterAdvice、AfterReturningAdvice、ThrowsAdvice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
└─springframework
├─aop # 切面模块,包括Advisor,Pointcut,MethodMatcher,TargetSource等
│ ├─aspectj # 具体的实现,包括AspectJExpressionPointcut和AspectJExpressionPointcutAdvisor
│ └─framework # 框架实现,包括动态代理部分:Jdk和Cglib,还有拦截器链部分:DefaultAdvisorChainFactory,以及代理类执行的proceed方法:ReflectiveMethodInvocation
│ ├─adapter # 包括MethodBeforeAdviceInterceptor和AfterReturningAdviceInterceptor
│ └─autoproxy # 包括DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware
├─beans # IOC模块
│ └─factory # IOC工厂
│ ├─annotation # 注解,包括Value注解和Autowired注解
│ ├─config # 包括Bean定义部分:BeanDefinition和BeanReference,还有Processor部分:BeanFactoryPostProcessor、BeanPostProcessor和InstantiationAwareBeanPostProcessor
│ ├─support # 包括DefaultListableBeanFactory类,以及这个类继承和实现的类和接口
│ └─xml # 读取xml文件的类
├─context # 上下文模块,包含ApplicationContext、ApplicationEvent和ApplicationListener等接口和类
│ ├─annotation # 包含Scope注解表示Bean是否是单例,包含ClassPathBeanDefinitionScanner类(doScan方法中注册BeanDefinition,处理@Autowired和@Value注解的BeanPostProcessor),包含ClassPathScanningCandidateComponentProvider类扫描Component注解
│ ├─event # 事件模块,包含ContextRefreshedEvent和ContextClosedEvent等类
│ └─support # 包含AbstractApplicationContext类(refresh方法),以及ClassPathXmlApplicationContext类(从xml文件加载的上下文)(继承自AbstractApplicationContext)等
├─core
│ ├─convert # 类型转换模块,使用环境:1. 在xml文件中Bean定义中 2. 在Value注解中。例如将"1000"字符串转换为数字1000
│ │ ├─converter
│ │ └─support
│ └─io # 资源读取模块,包括ClassPath、FileSystem和Url
├─stereotype # Component注解
└─util # 仅包含StringValueResolver类

RPC

JavaGuide的《手写 RPC 框架》

一款基于 Netty+Kyro+Zookeeper 实现的 RPC 框架

guide-rpc-framework

guide-rpc-framework-learning

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
├─example-client:示例客户端
├─example-server:示例服务端
├─hello-service-api:示例服务
├─rpc-framework-common:rpc框架通用组件
│ ├─src
│ │ └─main
│ │ └─java
│ │ └─github
│ │ └─javaguide
│ │ ├─enums:常量
│ │ ├─exception:自定义异常
│ │ ├─extension:dubbo spi(服务发现机制)
│ │ ├─factory:获取单例对象的工厂类
│ │ └─utils:工具集合
│ │ └─concurrent:并行工具
│ │ └─threadpool:线程池工具
└─rpc-framework-simple
├─src
│ ├─main
│ │ ├─java
│ │ │ └─github
│ │ │ └─javaguide
│ │ │ ├─annotation:自定义注解
│ │ │ │ RpcReference.java:用于注解类的实例
│ │ │ │ RpcScan.java:自定义的扫描注解
│ │ │ │ RpcService.java:用于注解类的实现
│ │ │ ├─compress:压缩工具
│ │ │ ├─config
│ │ │ │ CustomShutdownHook.java:服务关闭后的操作
│ │ │ │ RpcServiceConfig.java:Rpc服务的配置类
│ │ │ ├─loadbalance:负载均衡
│ │ │ ├─provider:服务提供类
│ │ │ ├─proxy:服务代理
│ │ │ ├─registry:服务发现和仓库
│ │ │ │ │ ServiceDiscovery.java
│ │ │ │ │ ServiceRegistry.java
│ │ │ │ └─zk:Zookeeper实现
│ │ │ │ │ ZkServiceDiscoveryImpl.java
│ │ │ │ │ ZkServiceRegistryImpl.java
│ │ │ │ └─util
│ │ │ │ CuratorUtils.java:Zookeeper客户端
│ │ │ ├─remoting
│ │ │ │ ├─constants:常量
│ │ │ │ ├─dto:传输消息实体
│ │ │ │ │ RpcMessage.java:消息
│ │ │ │ │ RpcRequest.java:请求
│ │ │ │ │ RpcResponse.java:回应
│ │ │ │ ├─handler:请求处理类
│ │ │ │ └─transport:传输
│ │ │ │ │ RpcRequestTransport.java:传输接口
│ │ │ │ ├─netty:Netty实现
│ │ │ │ │ ├─client:客户端
│ │ │ │ │ ├─codec:编解码器
│ │ │ │ │ └─server:服务端
│ │ │ │ └─socket:Java原生 Socket实现
│ │ │ │ SocketRpcClient.java:客户端
│ │ │ │ SocketRpcRequestHandlerRunnable.java:请求处理
│ │ │ │ SocketRpcServer.java:服务端
│ │ │ ├─serialize:序列化(三种实现)
│ │ │ └─spring
│ │ │ CustomScanner.java:继承ClassPathBeanDefinitionScanner
│ │ │ CustomScannerRegistrar.java:RpcScan注解用到的扫描类
│ │ │ SpringBeanPostProcessor.java:Bean定义前后的处理
│ │ └─resources
│ │ └─META-INF
│ │ └─extensions:dubbo spi使用的资源

dubbo spi:SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。

MISC

类和对象

equals与hashcode

  • equals方法
    • 比较两个对象值是否相等
    • 默认(Objects)与==比较相同,即比较对象地址是否相同
    • 重写equals方法:
      • 例如,String类就重写equals方法,比较的是字符串内容
  • hashcode方法
    • 每个对象都有一个hashcode,默认是对象地址,是一个int值,用于在哈希结构中散列到某个位置
    • 对于hashmap来说,如果两个对象相等,那么它们的hashCode值一定要相同,否则在哈希表的不同位置会出现相等的对象
    • 例如,在hashmap中,索引对象时先通过hashcode找到位置,再通过equals方法比较对象的值是否相同,如果相同,则不再放入hashmap;如果不同,则添加到链尾
  • 重写equals必须重写hashcode
    • 确保equals时的hashcode能覆盖!
    • 重写equals前:因为hashcode的值默认是对象地址,不同的对象它们的地址不同,hashcode就不同,equals默认比较的也是地址,所以equals的结果是false
    • 重写equals后:不同对象它们equals结果则可能为true,而在hashmap中,存入这两个对象(通过hashcode),会存到不同的位置,这就导致了hashmap中出现了两个值相同的对象,违背了hashmap的原则。所以,需要重写hashcode方法,使值相同的对象的hashcode值也必须相同。

Stream