总结
虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。
架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。
如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。
三大修饰符
static: 静态的
-
类 : 静态类
-
属性 : 静态属性 全类共享 可以直接使用类名.属性名 直接使用
-
方法 : 静态方法: 全类共享 可以直接使用类型.方法名 直接调用
-
代码块: 静态代码: 静态在类加载时候执行, 只执行一次
final: 最终的
-
类: 最终类: (断子绝孙) 该类不能被继承
-
属性: 最终的属性: 属性一旦被赋值不能被修改
-
方法: 最终方法: 可以被继承,不能被覆盖
try{}catch(Exception e){}finally{} //总是被执行
finallize() //在jvm垃圾回收自动调用
abstract: 抽象的
-
类: 抽象类: 不能通过new关键创建对象
-
方法: 抽象方法: 只有声明没有实现
注意:
1.抽象类中一定含有抽象方法 不对
2.存在抽象方法类一定时抽象类 对
3.抽象类中存在构造方法 对
4.抽象类中因为不能通过new创建对象,因此类中没有构造方法 不对
5.抽象类中构造方法用来干什么? 用来为子类继承时创建子类对象用的
6.String 类能不能被继承? 不能被继承 final 关键字
为什么使用final关键字修饰?
字符串类中所有方法都是线程安全的方法,如果允许继承,可能会破坏string
中线程安全
#面试
-
1.存在抽象方法类一定是抽象类 对
-
2.抽象类一定存在抽象方法 不对
-
3.被static修饰方法方法内只能使用外部静态成员 对
-
4.普通方法可以直接使用static修饰方法 对
-
5.static修饰方法可以直接使用外部普通方法 不对
-
6.jdk中String类可以被继承? 不能 为什么String要设计成final的 String 不可变字符串 String name="小陈" name+"xiaohei";
-
7.抽象类中没有构造方法? 存在,子类继承父类时 创建父类对象使用
静态代码块,动态代码块,构造方法的执行顺序
public class Student extends People{
//静态代码块: 类加载时候执行 只执行一次 类加载: jvm第一次使用到这个.class文件进行类加载 classLoader ===> jvm
static{
System.out.println(“1”);
}
//对象: 初始化属性 调用构造方法
//动态代码块: 作用:用来给类中属性 赋值的
{
System.out.println(“2”);
}
//构造方法: 创建对象的时候自动执行
public Student() {
System.out.println(“3”);
}
public static void main(String[] args) {
Student student = new Student();//5 1 6 4 2 3
}
}
class People{
static {
System.out.println(“5”);
}
{
System.out.println(“6”);
}
public People() {
System.out.println(“4”);
}
}
下面代码的执行顺序
public class Test{
private String name;
static{
System.out.println(“1”);
}
{
name = “小陈”;
System.out.println(“2”);
}
public Test() {
System.out.println(“3”);
}
public static void main(String[] args) {
new Test();
}
}
解释:
-
1.当jvm第一次读取类信息时会执行类加载,static代码块是在类加载时候执行,因此最先执行输出1
-
2.{}代码块: 初始化代码块,在创建对象时为属性进行初始化执行,因此创建对象之前要先经历属性初始化才创建对象因此输出2
-
3.构造方法: 在创建对象时自动调用,最后输出3
什么是类加载?
类加载
- 类加载,在JVM第1次使用某个类时,先在classpath下 找到对应的.class文件,读取 该.class文件中的内容(包/名/属性/方法…)到内存中 并且保存(类对象)起来的过程。 类加载只进行1次
简述 final finalized finally 的区别?
final 最终的
-
类: 修饰不能被继承
-
属性: 最终属性 一旦被赋值不能被修改
-
方法:最终方法 可以被继承不能覆盖
-
String类能不能被继承? 不能 原因是:被final关键字修饰的
-
String:为什么要设计成final? String不希望有子类,子类会破坏父类中方法规则 原因:字符串类中所有方法都是线程安全的,如果存在子类破坏父类中方法线程安全
finalized 垃圾回收时jvm自动执行的方法
finally 最终的 一般 try catch 进行连用 try{}finally{}
- finally 代码中的内容: 无论什么情况总是执行
接口和抽象类区别
interface 接口
-
1.接口之前可以多继承 interface A extends B,C… 2.类 implements A,B,…
-
2.接口中定义变量都是静态常量 接口中变量都是被 public static final String NAME = “xiaochen”; 修饰的 静态常量
-
3.接口中方法公开抽象方法 只有声明没有实现 (jdk8) (jdk8)以后,接口中方法可以存在默认实现
抽象类 abstract
-
1.类中含有构造方法
-
2.抽象类只能单继承
-
3.抽象类存在普通方法
-
4.抽象类中存在抽象方法 只有声明 没有实现的
”==”与equals有何区别?
==
- 比较地址内存地址
user1 == user2
equals
- 比较内容是否一致
user1.equals(user2); //比较对象:必须覆盖euqals 和 hashCode方法
StringBuilder与Stringbuffer的区别?
StringBuilder 、 StringBuffer
-
共同点: 都是用来进行字符串拼接的
-
不同点:
1.StringBuilder 线程不安全 效率高
2.StringBuffer 线程安全 效率低
简述ArrayList、LinkedList、Vector三者的区别?
ArrayList LinkedList Vector 都是List接口实现类 都是集合
-
ArrayList: 底层实现: 数组 特点:一段连续内存空间 根据下标快速查询 查询快(O(1)) 增删慢 O(n) 线程不安全
-
LinkedList: 底层实现: 链表 特点:指针概念将节点连接在一起 增删快(O(1)) 查询慢(O(n))
-
Vector: 底层实现: 数组 特点: 一段连续内存空间 根据下标快速查询 查询快(O(1)) 增删慢 O(n) 线程安全
HashMap和HashTable的区别?
hashmap
- 线程不安全的 允许key value 同时为null
hashtable
- 线程安全的 不允许key value 同时为null
ConcurrentHashMap(并发hashmap) 线程安全 效率 hashtable
- 线程安全的 效率远高于hashtable
Hashtable和ConcurrentHashMap有什么分别呢?它们都可以用于多线程的环境,
但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。数组+链表
因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,对表进行分段加锁16段
而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。
HashSet如何实现元素不重复?
自定义类型
- 需要在类中覆盖hashcode和equals方法
非自定义类型
- 内部底层自动覆盖hashcode 和 equals
简述流的分类?
方向
-
输入流: 将数据读入jvm中
-
输出流: 将jvm中数据写出
功能
-
节点流 : 实际负责传输数据的流
-
过滤流 : 增强节点流功能(处理流、装饰类),依赖节点流
单位
-
字节流: 读取一切数据
-
字符流: 读取文本类型的数据
InputStream is = new FileInputStream(“”)
OutputStream os = new FileOutputStream(“”)
文件拷贝代码
1.定义输入流 定义输出流
InputStream is = new FileInputStream(new File(“d://aa.txt”)); 900byte
OutputStream os = new FileOutputStream(new File(“e://bb.txt”));
2.文件拷贝
byte[] b = new byte[1024]; //1KB
int len = 0;
while(true){
len = is.read(b);
if(len=-1)break;
os.write(b,0,len);
}
//2.IOUtils.copy(is,os); //引入commons-io工具包
3.释放资源
is.close();
os.close();
什么是线程?
线程
-
进程: 一个进程中可以化分为多个线程 线程是程序调度基本单位
-
多线程: 可以提高程序运行效率
new Thread(()=>{
}).start();
java中实现多线程方式: 1.继承Thread类 2.实现runable接口 3.线程池 4.线程辅助类 FeatureTask Callable
线程状态 5种状态(操作系统角度)
-
NEW 新建状态
-
RUNNABLE start方法之后进入 可运行状态
-
RUNNING 获取cpu时间片 运行状态
-
BLOCKED Thread.sleep(1000); IO … 阻塞状态
-
DEAD 死亡状态
什么是线程安全?
线程安全
- 如果你的代码所在的进程中有多个线程在同时运行,
而这些线程可能会同时运行这段代码。
如果每次运行结果和单线程运行的结果是一样的,
而且其他的变量的值也和预期的是一样的,就是线程安全的。
一个线程安全的计数器类的
同一个实例对象在被多个线程使用的情况下也不会出现计算失误。
线程安全案例
- 类对象唯一,可以使用类对象加锁
类对象在jvm就一个,唯一
- 对象锁
public class TestThread {
private static int count = 0;
private static Object o = new Object();
//synchronized (对象) 对象锁
public static synchronized void main(String[] args) throws InterruptedException {
synchronized(TestThread.class){
// getstatic +1 value putstatic t1
Thread t1 = new Thread(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o){
for (int i = 0; i < 10000; i++) {
count++;
}
}
});
Thread t2 = new Thread(() -> { //t2
synchronized (o) {
for (int i = 0; i < 10000; i++) {
count–;
}
}
});
t1.start();
t2.start();
t1.join();//阻塞main等待线程执行完成
t2.join();//阻塞main等待线程执行完成
System.out.println(count);
}
}
实现多线程方式
1.继承Thread类
Thread1 extends Thread {
public void run(){
// 线程的功能代码
}
}
//使用:a. 创建线程对象
Thread1 t1 = new Thread1();
//b. 启动线程
t1.start(); // 启动线程,JVM自动调用run方法
// t1.run(); //error. 相当于调用了一个对象中的方法
2.实现Runable接口
Thread2 implements Runnable{
//实现run方法
@Override
public void run(){
//线程的代码功能
}
}
//使用
Thread thread = new Thread(new Thread2());
thread.start();
3.实现 Callable 接口
Thread1 implements Callable {
public void run(){
// 线程的功能代码
}
}
//使用:a. 创建线程对象
Thread1 t1 = new Thread1();
//b. 启动线程
t1.start(); // 启动线程,JVM自动调用run方法
// t1.run(); //error. 相当于调用了一个对象中的方法
4.使用FeaturTask
- 获取线程执行任务结果
//线程任务对象
FutureTask futureTask = new FutureTask<>(new Callable() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+ “-----------”);
return 10;
}
});
new Thread(futureTask).start();
System.out.println(futureTask.get());
sleep() 和 wait() 有什么区别
共同点: 都是让线程进入等待状态 sleep 有限期等待 wait 无限期等待
sleep() 线程对象中方法
- 线程进入等待之后,不会释放对象锁,等待结束之后恢复线程继续执行
wait() Object类中方法
- 线程进入等待之后,释放掉当前对象锁,只有接收notify() 或者notfiyall() 之后才能恢复运行,恢复运行时重新进入等待队列获取锁表标记
通过反射获取类对象的3种方式
反射获取对象
- 1)通过类名.class 获取 Class对象
Class s = Student.class;
- 2)创建对象,通过对象 . getClass() 获取Class对象
Strudet student = new Student();
Class s = student.getClass();
- 3)通过 Class.forName(“包名.类名”); 获取Class对象
Class s = Class.forName(“java.util.HashSet”);
单例设计模式
//只能创建一个对象
懒汉式
class User{
private static User user;
private User(){}
public synchronized static User getInstance(){ //存在线程安全问题:必须加入线程锁
if(user == null){
user = new User();
}
return user;
}
}
恶汉式
class User{
private static final User user = new User();
private User(){}
public static User getInstance(){
return user;
}
}
单例模式案例
public class TestSingleton {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Dept.getInstance());
}).start();
}
//懒汉式: 每次使用使用创建 存在线程安全的问题
class Dept{
private static Dept dept;
private Dept(){}
//t1 bb t2 aa
public synchronized static Dept getInstance(){
if(dept==null){
dept = new Dept();
}
return dept;
}
}
//饿汉式 : 不管用还是不用 直接创建一个实例 不存在线程安全问题
class Emp{
private static final Emp emp = new Emp();
private Emp(){}//构造方法私有
//返回一个对象
public static Emp getInstance(){
return emp;
}
}
oracle jdbc html css servlet jsp struts2 mybatis maven js jquery
Oracle 中主键的自动生成策略是什么?如何创建?
sequence 序列 oracle中主键生成策略
-
创建 create sequence seq_user start with 1 increment by 2 maxvalue 100 minvalue 1;
-
使用 sequence
序列名.nextval 使用下一个序列值 insert into t_user values(seq_user.nextval
序列名 .currval //获取当前序列的数值 前提序列必须先执行过一次
select 序列名.currval from dual;
注意:序列一旦被创建可以在任意表中使用,值一旦产生不能重复获取。
mysql主键策略 自动生成 auto_increment mysql自动生成
create table t_user(
id int(4) primary key auto_incrment,
name
)
insert into t_user(‘name’) values(‘xiaochen’)
select语句执行顺序
select语句
-
书写顺序: select * from 表名 where 条件 group by 分组 having 条件2 order by 排序
-
执行顺序: from 表名 where 条件 group by 分组 having 条件2 select order by
1.FROM 确定查询的表
2.WHERE 对数据按条件筛选
3.GROUP BY 对筛选后的数据分组
4.HAVING 对分组后的数据再次进行筛选
5.SELECT 生成结果集
6.ORDER BY 对结果集进行排序
什么是 ACID ?
ACID: 原子性 隔离性 一致性 持久性
事务四个特性
- ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。
-
原子性 Atomic 事务必须是原子工作单元 ( 不可分割 ) ;对于其数据修改,要么全都执行,要么全都不执行。
-
一致性 Consistent 操作前的数据,操作后的数据保持一致。
-
隔离性 Insulation 保证多用户并发访问的数据安全性,并发事务所作的修改必须与任何其它并发事务所作的修改隔离。
-
持久性 Duration 事务操作的数据 持久化到数据库当中 , 对于系统的影响是永久性的。
事务隔离级别
事务隔离级别
- read_uncommit 读未提交: 一个客户端读取到了另一个客户端没有提交的数据 脏读现象
client1 insert clinet2
- read_commit 读提交: 一个客户端只能读取另一个客户端提交之后的数据 避免脏读现象
oracle默认隔离级别
- repeat_read 可重复读: 一个客户端在一次事务多次读取同一条记录多次读取结果一致 避免不可重复读现象出现的 mysql数据默认隔离级别
zhangsan 1000
client1 clinet2
100 300 commit
100
- serialiable 序列化读(串度):一个客户端再一次事务中多次读取同一张表记录,多次读取结果一致 避免幻影读现象
table
1 zhangsan
client1 client2
1 insert commit
注意:隔离级别越高查询效率越低
sql优化方案
优化方案
- (1)选择最有效率的表名顺序 user video category
(2)当只需要一行数据时候使用limit 1;
(3)SELECT子句中避免使用‘*’
(4)用Where子句替换HAVING子句
(5)通过内部函数提高SQL效率 concat… max min …
(6)避免在索引列上使用计算。 //在索引列进行计算会导致索引失效
(7)提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉。
什么是sql注入?,如何防止sql注入
sql注入
?name=xiaoor1=1
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。 [1] 比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击.
mybatis 必须使用#{} 避免sql注入
mybatis 什么时候用这个获取数据 ? 如果将获取数据作为 s q l 语句一部分执行就必须使用 {} 什么时候用这个获取数据? 如果将获取数据作为sql语句一部分执行就必须使用 什么时候用这个获取数据?如果将获取数据作为sql语句一部分执行就必须使用{} 存在sql注入 order by ${}
sql注入案例
user=request(“user”) ===> ss or a=a
passwd=request(“passwd”) ===> 1234 or 1=1
sql=“select admin from adminbate where user= ‘ss or a=a’ and passwd=‘1234 or 1=1’” pstm
sql=“select admin from adminbate where user= ss or a=a and passwd= 1234 or 1=1” statement
注意:在使用jdbc时使用statement对象执行sql才会出现sql注入 pstm: 通过占位符形式不会存在sql注入
JDBC核心步骤如何实现?
导入数据库驱动jar
- 1.加载驱动。
Class.forName(“oracle.jdbc.OracleDriver”)
- 2.创建数据库连接对象Connection
Connection conn=DriverManager.getConnection(“”,”root”,”root”);
jdbc:oracle:thin:@localhost:1521:xe
jdbc:mysql://localhost:3306/库名?characterEncoding=UTF-8
- 3.创建Statement对象 PrpepareStatement
String sql=”select * from user whnere username=?”;
PrepareStatement pstm=Conn.prepareStatement(sql);
Pstm.setString(1,name)
- 4.执行Sql
pstm.executeUpdate(); ResultSet rs = executeQuery();
-
5.处理结果集
-
6.释放资源
rs.close();
pstm.close();
conn.close();
jdbc是什么
JDBC 是java的13中规范之一(13个接口)
应用 Java 程序访问和操作数据库的底层代码 ,SUN 公司提出的一组规范 ( 接口 ) 。
1.1 接口规范:多种数据库产品, Sun 公司只是制定程序开发规则。 接口类型 : 屏蔽底层的代码实现差异 (访问不同的数据库)
1.2 实现在哪:驱动 Jar( 由数据库厂商提供 ) oracle ojdbc mysql mysql-connet sql
JDBC = SUN 公司提出的一组规范 ( 接口 ) + 驱动 Jar
Statement 和 PreparedStatement 的区别
共同点: 都是用来执行sql语句的
-
1.Statement是PreparedStatement父接口
-
2.Statement使用sql拼接方式执行sql 存在sql注入
-
3.PreparedStatement 可以使用占位符,是预编译的,批处理比 Statement 效率高 . 防止 SQL 注入
事务控制
事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。原子性
- 1.JDBC中默认事务是一条Sql语句自成一个事务,即一条Sql语句执行完毕会自动提交事务;无法保证业务功能的完整。需要程序员手动控制事务:
1.1 设置手动控制事务: conn.setAutoCommit(false); 手动提交
1.2 手动提交事务:conn.commit();
1.3 手动回滚事务:conn.rollback();
三层架构MVC
M:model 模型层 dao+service+entity jdbc
C:controller 控制层 servlet action 1.收集数据 2.调用业务 3.响应结果
V:view 试图层 展示数据 html/Jsp ajax html —> 接口
MVC
自定义 servlet 的三种方式,及区别
方式一: implements Servlet 接口 ( 不建议 ) 实现接口中所有抽象方法
1 、 destroy()
2 、 init()
3 、 service() //service 方法
4 、 getServletConfig()
5 、 getServletInfo()
方式二: extends GenericServlet , 覆盖 service 方法:不建议使用,
与 http 协议无关 service(ServletRequest req, ServletResponse res)
方式三: extends Httpservlet 在这个抽象类中,所有的方法都是普通方法
只需要覆盖 service 方法 接受请求,处理请求、把结果响应给 Client{
1.收集数据 2.调用业务对象 3.流程跳转
}
连接池的作用
数据库连接的建立是一种耗时、性能低、代价高的操作,频繁的数据库连接的建立和关闭极大的影响了系统的性能。数据库连接池是系统初始化过程中创建一定数量的数据库连接放于连接池中,当程序需要访问数据库时,不再建立一个新的连接,而是从连接池中取出一个已建立的空闲连接,使用完毕后,程序将连接归还到连接池中,供其他请求使用,从而实现的资源的共享,连接的建立、断开都由连接池自身来管理。
数据库连接池为系统的运行带来了以下优势:
昂贵的数据库连接资源得到重用;减少了数据库连接建立和释放的时间开销,
提高了系统响应速度;统一的数据库连接管理,避免了连接资源的泄露。
tomcat: jndi 配置文件
dhcp c3p0 druid(阿里连接池)
Servlet 中的三大作用域对象是什么?及各自的作用范围?
request: 一次请求有效 request
session: 一次回话有效 request.getSession()
application(servletContext): 全局共享 应用级作用域 唯一
request.getSession().getServletContext();
request.getServletContext();
总结
三个工作日收到了offer,头条面试体验还是很棒的,这次的头条面试好像每面技术都问了我算法,然后就是中间件、MySQL、Redis、Kafka、网络等等。
- 第一个是算法
关于算法,我觉得最好的是刷题,作死的刷的,多做多练习,加上自己的理解,还是比较容易拿下的。
而且,我貌似是将《算法刷题LeetCode中文版》、《算法的乐趣》大概都过了一遍,尤其是这本
《算法刷题LeetCode中文版》总共有15个章节:编程技巧、线性表、字符串、栈和队列、树、排序、查找、暴力枚举法、广度优先搜索、深度优先搜索、分治法、贪心法、动态规划、图、细节实现题
《算法的乐趣》共有23个章节:
- 第二个是Redis、MySQL、kafka(给大家看下我都有哪些复习笔记)
基本上都是面试真题解析、笔记和学习大纲图,感觉复习也就需要这些吧(个人意见)
- 第三个是网络(给大家看一本我之前得到的《JAVA核心知识整理》包括30个章节分类,这本283页的JAVA核心知识整理还是很不错的,一次性总结了30个分享的大知识点)
数据库连接的建立是一种耗时、性能低、代价高的操作,频繁的数据库连接的建立和关闭极大的影响了系统的性能。数据库连接池是系统初始化过程中创建一定数量的数据库连接放于连接池中,当程序需要访问数据库时,不再建立一个新的连接,而是从连接池中取出一个已建立的空闲连接,使用完毕后,程序将连接归还到连接池中,供其他请求使用,从而实现的资源的共享,连接的建立、断开都由连接池自身来管理。
数据库连接池为系统的运行带来了以下优势:
昂贵的数据库连接资源得到重用;减少了数据库连接建立和释放的时间开销,
提高了系统响应速度;统一的数据库连接管理,避免了连接资源的泄露。
tomcat: jndi 配置文件
dhcp c3p0 druid(阿里连接池)
Servlet 中的三大作用域对象是什么?及各自的作用范围?
request: 一次请求有效 request
session: 一次回话有效 request.getSession()
application(servletContext): 全局共享 应用级作用域 唯一
request.getSession().getServletContext();
request.getServletContext();
总结
三个工作日收到了offer,头条面试体验还是很棒的,这次的头条面试好像每面技术都问了我算法,然后就是中间件、MySQL、Redis、Kafka、网络等等。
- 第一个是算法
关于算法,我觉得最好的是刷题,作死的刷的,多做多练习,加上自己的理解,还是比较容易拿下的。
而且,我貌似是将《算法刷题LeetCode中文版》、《算法的乐趣》大概都过了一遍,尤其是这本
《算法刷题LeetCode中文版》总共有15个章节:编程技巧、线性表、字符串、栈和队列、树、排序、查找、暴力枚举法、广度优先搜索、深度优先搜索、分治法、贪心法、动态规划、图、细节实现题
[外链图片转存中…(img-QWu2XYbg-1715315381275)]
《算法的乐趣》共有23个章节:
[外链图片转存中…(img-DMxCFFSl-1715315381276)]
[外链图片转存中…(img-MclEpfRI-1715315381276)]
- 第二个是Redis、MySQL、kafka(给大家看下我都有哪些复习笔记)
基本上都是面试真题解析、笔记和学习大纲图,感觉复习也就需要这些吧(个人意见)
[外链图片转存中…(img-ntUdAktU-1715315381276)]
- 第三个是网络(给大家看一本我之前得到的《JAVA核心知识整理》包括30个章节分类,这本283页的JAVA核心知识整理还是很不错的,一次性总结了30个分享的大知识点)
[外链图片转存中…(img-aLluXgOa-1715315381277)]