后端内容仅碎片化记录本人学习过程中碰到的问题,并没有前端与数据库部分那么完整的整理。
前端:Java Web前端要点整理:HTML、CSS、JavaScript、Vue、HTTP协议与Tomcat
数据库与缓存:Java Web数据库与缓存要点整理:MySQL、Redis、Spring Data与MyBatis
Java & JVM
Java这注解跟踏马魔法一样啊。。。
以下是 Java 中类的加载、实例化流程及生命周期的详细解析,以及类加载器的核心作用:
一、类的生命周期
Java 类的生命周期分为 7 个阶段,按顺序触发:
- 加载(Loading)
- 作用:将
.class
字节码文件加载到 JVM 内存。 - 触发条件:首次使用类时(如
new
、调用静态方法、访问静态字段等)。 - 关键产物:生成一个
Class
对象(存储在方法区)。
- 作用:将
- 验证(Verification)
- 作用:检查字节码是否符合 JVM 规范(防止恶意代码)。
- 检查内容:文件格式、元数据、字节码逻辑等。
- 准备(Preparation)
- 作用:为 类变量(静态变量)分配内存,并赋予初始值(零值)。
- 示例:
static int x
初始化为0
,static Object obj
初始化为null
。
- 解析(Resolution)
- 作用:将符号引用(如类名、方法名)转换为直接引用(内存地址)。
- 初始化(Initialization)
- 作用:执行类构造器
<clinit>()
,为静态变量赋真实值,执行静态代码块。 - 触发条件:首次主动使用类时(如
new
、访问静态字段)。 - 线程安全:
<clinit>()
由 JVM 保证同步执行。
- 作用:执行类构造器
- 使用(Using)
- 作用:通过实例化、调用方法等操作使用类。
- 卸载(Unloading)
- 作用:从内存中移除类的
Class
对象。 - 触发条件:类的所有实例被回收,且类加载器被回收(通常只有自定义类加载器加载的类可卸载)。
- 作用:从内存中移除类的
二、类加载器(ClassLoader)的核心作用
类加载器负责 将字节码加载到 JVM,并决定类的唯一性(同一类由不同加载器加载视为不同类)。
1. 类加载器的层次结构
Java 采用 双亲委派模型(Parent Delegation Model),包含三层核心加载器:
加载器 | 作用 | 加载路径示例 |
---|---|---|
Bootstrap ClassLoader | 加载核心 JDK 类(如 java.lang.* ),由 C++ 实现,无 Java
对象 |
jre/lib/rt.jar |
Extension ClassLoader | 加载扩展类(如 javax.* ) |
jre/lib/ext/*.jar |
Application ClassLoader | 加载应用类(用户编写的类,或第三方库) | CLASSPATH 或 -classpath 指定的路径 |
2. 双亲委派模型的工作流程
1 | 用户请求加载类 → 由子加载器逐级向上委派 → 父加载器尝试加载 → 若父加载器无法加载,子加载器自行加载 |
- 优势:
- 避免重复加载(父加载器已加载的类,子加载器不再加载)。
- 安全性(防止用户伪造核心类,如
java.lang.String
)。
3. 自定义类加载器
通过继承 ClassLoader
并重写 findClass()
实现,常见用途: - 热部署(如 Tomcat 的 WebApp 类加载器)。 -
加载非标准来源的类(如网络、加密字节码)。 -
模块化隔离(不同模块使用独立加载器)。
三、类的实例化流程
当通过 new
关键字实例化对象时,流程如下:
- 检查类是否已加载
- 若未加载,触发类加载流程(加载 → 验证 → 准备 → 解析 → 初始化)。
- 分配内存
- 在堆中为对象分配连续内存空间(具体大小由类结构决定)。
- 初始化零值
- 为对象的实例变量赋予默认值(如
int
初始化为0
,引用类型初始化为null
)。
- 为对象的实例变量赋予默认值(如
- 设置对象头(Object Header)
- 存储对象的元数据(如哈希码、GC 分代年龄、锁状态、类元数据指针等)。
- 执行构造方法
<init>()
- 按代码顺序执行实例变量初始化代码和构造方法。
- 示例:
1
2
3
4
5
6public class MyClass {
private int a = 10; // 实例变量初始化
public MyClass() { // 构造方法
a = 20;
}
}
四、关键问题与示例
1. 类初始化触发的场景
- 直接
new
对象。 - 访问类的静态变量或静态方法。
- 反射调用(如
Class.forName("com.example.MyClass")
)。 - 子类初始化时,若父类未初始化,会先触发父类初始化。
2. 类加载器的核心作用总结
- 定位资源:通过类加载器查找
.class
文件或资源(如getResource()
)。 - 隔离性:不同加载器加载的类互相不可见(如 Tomcat 隔离 Web 应用)。
- 安全性:防止恶意代码替换核心类。
3. 常见异常
ClassNotFoundException
:类加载器找不到指定类。NoClassDefFoundError
:类加载成功,但初始化失败(如静态代码块抛出异常)。
五、完整流程示例
1 | public class MyClass { |
输出: 1
2类初始化:静态代码块
对象实例化:构造方法
通过理解类的生命周期和类加载器的作用,可以更好地解决类冲突、资源加载、热部署等实际问题。
Spring
常用注解
Spring常用注解:
@RestController
:等价于@Controller
与@ResponseBody
的组合,置于Controller前@ResponseBody
:将返回值作为响应体,置于方法或Controller前@RequestMapping(URL)
:路由请求,置于方法前@RequestParam
:多请求参数单Controller形参默认封装至数组,形参前加上该注解将封装至集合,如ArrayList@RequestParam(name = "请求参数名")
:将请求参数绑定到Controller形参;该注解默认属性required=true
,要求必须传递目标参数@Component
:IOC(控制反转)注解,将当前类交给IOC容器管理、注册该类为IOC的Bean的基础注解,可通过value
属性为Bean命名,默认名称为类名但首字母小写,置于类定义前@Controller
:标识该类属于Controller组件,专属Controller的Bean注册方式,同样可命名Bean,置于类定义前@Service
:标识该类属于Service组件,专属Service的Bean注册方式,同样可命名Bean,置于类定义前@Repository
:标识该类属于DAO组件,专属DAO的Bean注册方式,同样可命名Bean,置于类定义前,但是这个用的少,用MyBatis的就行
Bean注解要生效必须被组件扫描注解
@ComponentScan
扫描到,启动类src/main/java/com.项目名/utils/SpringBootWebReqRespApplication.java
已声明了默认扫描注解@SpringBootApplication
,默认组件扫描范围是启动类所在包及其子包,要扫描其他包需要在启动类添加注解@ComponentScan({"包名", "包名"})
指定扫描范围,这会覆盖默认扫描范围,但是按规范应该是把要扫描的类放到启动类所在包下,可以移动启动类@Primary
:多个Bean实现同一接口时,优先选择这个Bean,置于Bean声明注解前@Qualifier("Bean名")
:根据Bean名称选择特定Bean注入,置于@Autowired
后、方法前@Resource(name="Bean类名")
:根据Bean类名选择特定Bean注入,置于方法前但这时注入无需@Autowired
,注意该注解不是Spring提供的而是JDK提供的@Autowired
:DI(依赖注入)注解,根据声明的类型依赖注入Bean,默认不允许多个Bean实现同一接口,但可以利用@Primary
、@Qualifier
、@Resource
避免,置于对象声明前
三层架构
Spring开发中通常遵循三层架构:Controller、Service与Dao。
- Controller:控制层,接收前端请求,处理请求并响应数据,前端控制器如
HttpServletRequest
接收响应并传递给Controller,Controller将数据返回给前端控制器如HttpServletResponse
- Service:业务逻辑层、服务层,处理具体的业务逻辑
- DAO(Data Access Object):数据访问层、持久层,对数据进行操作(增删查改),常面向接口编程
IOC & DI
把所有的代码和逻辑都写在Controller当然被允许,但是不方便维护。
控制反转与依赖注入是Spring用来帮助更好地实现分层解耦的。
- 控制反转(Inversion of Control,IOC):对象的创建控制权由程序自身转移至外部容器
- 依赖注入(Dependency
Injection,DI):容器为应用提供运行时所依赖的资源,例如Service的代码不必显式的
new
特定的Dao对象 - Bean对象:IOC容器中创建、管理的对象
通过Spring的注解就可以实现。
解释:一个典型的流程是某个Controller需要某个Service对象,该Service接口被若干不同的ServiceImp实现了不同的逻辑,Spring通过控制反转把ServiceImp放进容器中,Controller从容器中通过依赖注入获取需要的对象,而不必在自身的逻辑中嵌入Service实例化代码,实现高内聚、低耦合