进程、线程、虚拟线程
进程:就是操作系统中的一个独立单元。进程之间是相互隔离的
线程:绑定到某一个进程内部的,是进程中的一个最小的执行单元。是cpu直接调度的
虚拟线程(普通线程调,没有可配置参数,用着简单):
在java中,如果存在IO密集的任务时,线程个数不太好控制,如果用了虚拟线程,就不需要关注个数了。
正常线程占用的内存资源在MB级别。虚拟线程占用的资源在KB资源。
正常线程由CPU直接调度,存在上下文切换带来的时间成本,而且存在用户态跟内核态切换的时间成本。虚拟线程不是由CPU直接调度的,所以不存在所谓的上下文切换成本,而且没有所谓的挂起跟唤醒去切换状态的时间成本。
虚拟线程无法解决CPU密集的任务,因为你CPU资源就那么多,不可能处理性能变高的。
重写、重载
重写:(Override)
重写是子类重写符类、父接口钟的方法,要求方法名,方法参数等必须跟父类一模一样。
但是返回结构需要适用父类方法,访问修饰符不能小于父类的方法。
重载:(OverLoad)
重载是在一个类里,方法名一致,方法参数不同。
返回结果,方法修饰符无所谓
String,Buffer,Builder
String:
不可变长度的,使用 + 字符串连接,他也是构建一个全新的字符串,对内存占用比较大。
线程安全的
StringBuffer:
长度可变的,底层采用char[]去实现字符串拼接(1.9之后用的是byte,目的节约空间),可以规避String那种每次创建新的字符串
线程安全的,加了synchronized,如果涉及到成员变量的拼接,记得用
StringBuilder:
长度可变的,底层采用char[]去实现字符串拼接(1.9之后用的是byte,目的节约空间),可以规避String那种每次创建新的字符串
线程不安全的,如果在局部变量自己玩的时候,可以用
Autowired和Resource注解
@Autowired
Spring提供的
默认类型注入,类型找到一个,直接注入;找不到或者找到多个,直接报错。
如果找到多个要匹配,可以追加@Qualifier注解,指定名称。
@Resource
Java规范提供的
默认基于属性名注入,如果名字找不到,会再根据类型去找,类型到不到匹配的,也报错
Java中常见的集合
java中主要就是单列集合跟双列集合
单列集合
List:存取有序,允许重复。底层有数组ArrayList和链表LinkedList集合(修改要用迭代器)
Set:存取无序,不允许重复。底层是HashMap的key
双列集合
Map:存储双列,key不允许重复,value无所谓。(建议扩展Hash)
Map可以展开去聊一些底层实现,存储结构,线程安全问题等等。
线程安全的集合
比较传统的线程安全集合:Vector,Hashtable,Collections.synch…这种,完全基于synchronized,锁全局,性能贼慢,基本不用
CopyOnWrite系列:CopyOnWriteArrayList这种,他是利用空间换时间,来保证查询的时候不会阻塞,但是在写入的时候,依赖是基于lock锁保证线程安全的。
Consurrent系列:这个优化的程度会比较大,一般会结合CAS+synchronized去保证写操作的线程安全,同事查询依然不会阻塞。建议扩展(ConcurrentHashMap)
对象为啥要有hashcode方法
java中对象的HashCode方法就是提供给HashSet,HashMap这种集合,做一些hash寻址之类的操作。也可以配合eq做数据的对比。
hashCode啥时候生成
如果hashCode方法重写了,那你每次调用的时候,他才会生成对应的hash值。
如果hashCode没重写,他固定就是你的全路径跟地址的信息,这个是不会变的。
eq重写了,必须重写hashCode么?
不是,根据通途考虑,何时重写,怎么重写
Session和Cookie
Cookie集合Session一般是实现会话的。
Cookie:
存储在客户端
存储由大小的限制
不太安全,毕竟可以在客户端直接查看到
Session:
存储在服务端
存储大小理论上没限制,跟你的JVM内存走
安全,毕竟在服务端
比较传统的项目会做会话控制的方式,现在用的不多,可以聊一些框架SpringSecurity,JWT……
HashMap,为啥key可以为null
一般这个问题最好是结合ConcurrentHashMap去聊
因为ConcurrentHashMap中,key-value不允许为null。而HashMap中没有这个限制。
因为ConcurrentHashMap一般是作为成员变量,多个线程都会操作,如果一个线程null存储特殊的标识,其他线程不知道
而HashMap是线程不安全的,必然是局部使用,就一个线程自己玩,所以就没关系
线程安全问题怎么解决
多个线程 同时修改 临界资源 导致的线程安全问题。
多个线程:就一个线程玩,自然没有线程安全问题,一些资源不是共享的,用ThreadLocal
同时修改:
不让他同时修改,直接加锁!CAS,sync,lock……(扩展点一定在这,其次volatile也可以聊)
不修改,final修改的常量
临界资源:不去做写操作,或者自己玩自己的局部资源,直接从根源解决问题。
Spring声明式事务时效情况
时效情况很多:
方法基于private修改,导致AOP无法增强这个方法
类没有实现的接口,并且基于final修改,导致AOP无法创建代理对象
因为声明式事务默认搞RuntimeException,如果玩其他的自定义异常,也会失效
异常被自己的try-catch了,没往上抛,导致异常增强不走
在方法内部基于this.方式,或者方法内部搞多线程,都没走Spring增强后的代理对象
确保数据源的目标数据库支持事务
事务传播特性
存在声明式事务增强的方法直接进行相互调用,存在事务传播特性的问题:
REQUIRED:比如在一个事务里!走外层事务。(默认的)
SUPPORT:随缘,有事务就加入外层事务,没事务,那就不走事务。
REQIORED_NEW:不管怎么玩,都创建自己的新事物。
NOT_SUPPORT:不走事务
NESTED:嵌套事务的概念,自己回滚不影响外部,外部回滚,内部也回滚。
NEBER:不走事务,你有事务,丢异常
MANDATORY:走事务,没事务,丢异常
AOP实现的原理
可以从代理模式去聊。无非是
JDK动态代理:需要基于接口实现。被代理类必须要有实现的接口
Cglib动态代理:需要基于继承实现,被代理类不能被final修饰
JDK跟Cglib几乎大差不差,虽然Cglib基于字节增强去玩,但是JDK代理也在优化。
扩展:聊Spring的源码,BeanPostPricessir……
Spring中涉及的设计模式
单例、工厂、代理、观察者、模板、原型、责任链……
一个要求,聊到的,要知道大概再哪个位置用到,点到的必须知道在哪用的。
类加载器都有什么
java中自己带了三,自己也可以主动去实现,所以4钟
BootstrapClassLoader:默认 jre(jdk21里面没有jre了,包有优化)->lib下,比如 rt.jar
ExtClassLoader:默认 ext 目录下
AppClassLoader:默认 classpath 下
CustomClassLoader:指哪里打哪里
双亲委派:本质就是在加载.class文件时,会向上委托的方式,优先让最高层的ClassLoader去加载,如果加载不到,再往下去委派
代码实现本直就是基于递归走的。
第一次走是从Custom-App-Ext-Bs,这次走的过程是询问加载过没?加载过,直接用。
第二次是从Bs-Ext-App-Custom,这次是去加载,根据每个ClassLoader负责的位置,去加载。
核心目的是为了规避核心类被篡改。
JVM内存结构
一定要去区分好,java内存模式问的是jvm
从两个维度聊:
线程私有:虚拟机栈、本地方法栈、程序计数器(记录的是下一条指令)
线程共享:堆、方法区(方法区是JVM虚拟机的规范,一般实现是元空间)
垃圾回收算法
常见的三个
复制清除:理论上将内存分为两片区域,只在一片区存放对象,一片满了,保留可用对应复制到另一片,然后清空当前片内存。空间浪费、回收速度快。
标记清除:根据不同的算法,比如:三色标记对可用对象进行标记,将没标记到的,直接干掉。空间没问题,回收速度可以,但是有内存碎片问题。
标记压缩、整理:根据不同的算法,比如:三色标记对可用对象进行标记,将没标记到的,直接干掉。然后整理空间,避免碎片化问题。空间ok,速度稍慢,没有碎片化问题。
常见垃圾回收器
从古老的,聊到新的
Serial New、Serial Old:非常远古的垃圾回收器,单线程回收,速度嘎嘎慢。
Parallel New、Parallel Old:JDK1.8默认的垃圾回收器,多线程回收,速度稍快,有STW。
ParNwe、CMS:CMS是并行的垃圾回收器,除了初始标记、重新标记位置需要STW之外,回收的时候是没有并行的,没有STW。
G1:分区回收,把整个堆分成上千个区域,每次就回收垃圾最多的位置,STW时间短。(GDK默认垃圾回收器)
ZGC:毫秒级别的回收,不需要做JVM调优相关的操作。
Shenandoah:速度也很快,毫秒级别的。

被折叠的 条评论
为什么被折叠?



