JAVA基础
1.标识符
1.1.什么是标识符
Java 标识符(Identifier)是程序员在代码中为类、方法、变量、包、接口、标签等自定义命名的符号。与关键字不同,标识符完全由开发者定义,但必须严格遵守 Java 的命名规则
1.2.标识符可以标识什么
变量(Variable),方法(Method),类(Class),接口(Interface),枚举(Enum),注解(Annotation),包(Package),类型参数(Type Parameter),类型名称(Type Name),常量(Constant)
1.3.命名规则
- 组成字符:只能由 字母(A-Z, a-z)、数字(0-9)、下划线(
_)和 美元符号($)组成 - 首字符限制:不能以数字开头。必须以字母、
_或$开头。 - 不能是关键字:不能使用 Java 的关键字(如
class,public)和保留字(如goto,const)作为标识符。 - 大小写敏感:
MyClass、myclass、MYCLASS是三个完全不同的标识符。 - 长度无上限:理论上没有长度限制,但应保持合理可读性。
1.4.标识符命名规范**(约定俗成)**
| 类型 | 规范 | 示例 |
|---|---|---|
| 包名 | 全小写,单词间用 . 分隔,通常使用反向域名 | com.alibaba.fastjson |
| 类名 / 接口名 | 大驼峰(UpperCamelCase),名词或名词短语 | UserService, Serializable |
| 方法名 | 小驼峰(lowerCamelCase),动词或动宾短语 | getUserById(), calculateTotal() |
| 变量名 | 小驼峰,有意义的名称,避免单字母(循环变量除外) | orderList, isActive |
| 常量名 | 全大写 + 下划线分隔 | MAX_RETRY_COUNT, DEFAULT_TIMEOUT |
| 枚举值 | 同常量,全大写+下划线 | PENDING, COMPLETED |
| 泛型参数 | 单个大写字母或有意义的大驼峰缩写 | T, E, K, V, Req |
2.关键字
Java 关键字(Keywords)是 Java 语言中预定义的、具有特殊含义的保留字。它们全部为小写,不能用作变量名、类名、方法名等标识符
3.字面量
3.1.什么是字面量
Java 字面量(Literal)是指在源代码中直接书写的固定值。它们不需要通过变量或表达式计算,编译器在编译阶段就能确定其值和类型
3.2.Java中都有哪些字面量
- 整数字面量:100,-42
- 浮点字面量:3.14,3.14f
- 字符字面量:‘A’
- 字符串字面量:"Hello, World!"
- 布尔字面量:true,false
- null 字面量:null(唯一值)
4.变量
4.1.什么是变量
Java 变量是程序中用于存储数据值的命名内存空间。每个变量都有明确的类型、名称和作用域,且在使用前必须先声明
4.2.变量的分类
根据声明位置和生命周期,Java 变量分为三大类
| 类型 | 声明位置 | 生命周期 | 默认值 | 存储位置 |
|---|---|---|---|---|
| 局部变量 | 方法/代码块/构造器内部 | 方法调用开始 → 结束 | ❌ 无默认值(必须手动初始化) | 栈(Stack) |
| 成员变量(实例变量) | 类中、方法外,无 static | 对象创建 → 被GC回收 | ✅ 有默认值 | 堆(Heap) |
| 静态变量(类变量) | 类中、方法外,有 static | 类加载 → 程序结束 | ✅ 有默认值 | 方法区/元空间 |
示例对比
public class VariableDemo {
static int classVar = 10; // 静态变量:属于类,所有实例共享
int instanceVar; // 实例变量:默认值为 0
public void method() {
int localVar = 5; // 局部变量:必须初始化
// int uninit; // ❌ 编译错误:未初始化就使用
System.out.println(localVar);
}
}
4.3.基本数据类型与默认值
| 类型分类 | 数据类型 | 占用空间 | 默认值 (成员变量) | 取值范围 | 备注 |
|---|---|---|---|---|---|
| 整数型 | byte | 1 字节 | 0 | -2⁷ ~ 2⁷-1 (-128 ~ 127) | 最小整型,适合节省内存的场景 |
short | 2 字节 | 0 | -2¹⁵ ~ 2¹⁵-1 (-32,768 ~ 32,767) | 较少使用 | |
int | 4 字节 | 0 | -2³¹ ~ 2³¹-1 (-2,147,483,648 ~ 2,147,483,647) | Java 中最常用的整型 | |
long | 8 字节 | 0L | -2⁶³ ~ 2⁶³-1 (-9.2×10¹⁸ ~ 9.2×10¹⁸) | 字面量需加 L 后缀 | |
| 浮点型 | float | 4 字节 | 0.0f | ±1.4E-45 ~ ±3.4E+38(有效位数约 6~7 位) | IEEE 754 单精度,字面量需加 F/f |
double | 8 字节 | 0.0d | ±4.9E-324 ~ ±1.7E+308(有效位数约 15~16 位) | IEEE 754 双精度,默认浮点类型 | |
| 字符型 | char | 2 字节 | '\u0000' | '\u0000' ~ '\uffff' (0 ~ 65,535) | 无符号,采用 UTF-16 编码单元 |
| 布尔型 | boolean | JVM 实现相关 | false | 仅 true / false | 不直接对应数值,不可与整数互转 |
4.4.核心规则
-
先声明后使用:Java 不允许隐式声明,
x = 10;在未声明x时直接报错 -
局部变量无默认值:这是新手最常踩的坑。编译器会严格检查" definite assignment "(确定性赋值),未赋值的局部变量连读取都不允许
-
作用域限制
-- 局部变量仅在 {} 内可见 -- 同名变量不能在同一作用域内重复声明 -- 内层块可以声明与外层同名的变量(遮蔽),但不推荐 -
final 修饰符:
final变量只能赋值一次,之后不可修改。对于引用类型,final仅保证引用不变,对象内容仍可修改
4.5.命名规范速查
| 场景 | 规范 | 正例 | 反例 |
|---|---|---|---|
| 普通变量 | 小驼峰,名词/形容词 | userName, isActive | UserName, user_name |
| 常量 | 全大写+下划线 | MAX_SIZE, DEFAULT_TIMEOUT | maxSize, Max_Size |
| 循环变量 | 单字母可接受 | i, j, k, row, col | loopCounterVariable |
| 布尔变量 | is/has/can/should 开头 | isValid, hasPermission | valid, permissionFlag |
| 集合变量 | 复数或带类型后缀 | users, orderList | data, temp |
4.6.常见陷阱与最佳实践
- Integer 缓存池:
Integer a = 128; Integer b = 128; a == b结果为false!因为超出 [-128, 127] 缓存范围,每次new了新对象。比较包装类永远用.equals() - 浮点精度丢失:
0.1 + 0.2 != 0.3。金融计算必须用BigDecimal,禁止用float/double - 线程安全:实例变量和静态变量在多线程环境下不安全。局部变量天然线程安全(每个线程有自己的栈帧)
- 避免魔法值:不要直接在代码中写
if (status == 3),应定义为常量ORDER_COMPLETED = 3或使用枚举 - 最小作用域原则:变量尽量在最靠近使用的地方声明,减少认知负担和误用风险
5.二进制
5.1.二进制字面量表示
从 Java 7 开始,支持直接使用 0b 或 0B 前缀书写二进制整数
int bin = 0b1010; // 十进制 10
long binLong = 0B1111_0000L; // 支持下划线分隔,增强可读性
byte b = (byte) 0b11111111; // byte 需要强制转换(因为默认是 int)
5.2. 常用进制转换方法(Integer / Long 类)
| 功能 | 方法 | 示例 |
|---|---|---|
| 十进制 → 二进制字符串 | Integer.toBinaryString(n) | toBinaryString(10) → "1010" |
| 二进制字符串 → 十进制 | Integer.parseInt(s, 2) | parseInt("1010", 2) → 10 |
| 指定宽度补零 | String.format("%32s", ...).replace(' ', '0') | 输出完整32位二进制 |
| 其他进制互转 | Integer.toString(n, radix) | radix=8(八进制), 16(十六进制) |
5.3. 位运算符(Bitwise Operators)
Java 提供完整的位操作,直接对二进制位进行处理:
| 运算符 | 含义 | 示例 | ||
|---|---|---|---|---|
& | 按位与 | 0b1100 & 0b1010 = 0b1000 | ||
| ` | ` | 按位或 | `0b1100 | 0b1010 = 0b1110` |
^ | 按位异或 | 0b1100 ^ 0b1010 = 0b0110 | ||
~ | 按位取反 | ~0b1100 = ...11110011(补码) | ||
<< | 左移(低位补0) | 0b0001 << 2 = 0b0100 | ||
>> | 算术右移(高位补符号位) | -8 >> 1 = -4 | ||
>>> | 逻辑右移(高位补0) | -8 >>> 1 = 2147483644 |
5.4.Java 整数的二进制存储:补码(Two's Complement)
- 正数:原码 = 反码 = 补码
- 负数:补码 = 反码 + 1
- 符号位:最高位为 1 表示负数,0 表示正数
6.数据类型
6.1.基本数据类型(8种)
基本类型直接存储值,分配在栈内存中,不是对象,没有方法。
| 类型 | 关键字 | 字节数 | 取值范围 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 整型 | byte | 1 | -128 ~ 127 | 0 | 节省空间,常用于IO流/网络传输 |
short | 2 | -32,768 ~ 32,767 | 0 | 较少使用 | |
int | 4 | -2³¹ ~ 2³¹-1 (约±21亿) | 0 | 最常用的整数类型 | |
long | 8 | -2⁶³ ~ 2⁶³-1 | 0L | 时间戳、大计数等,字面量需加 L | |
| 浮点型 | float | 4 | IEEE 754 单精度 | 0.0f | 字面量需加 F/f,精度约6-7位有效数字 |
double | 8 | IEEE 754 双精度 | 0.0d | 默认浮点类型,精度约15-16位有效数字 | |
| 字符型 | char | 2 | 0 ~ 65,535 (Unicode) | '\u0000' | 无符号,用单引号 'A',支持中文 |
| 布尔型 | boolean | JVM未明确规定 | true / false | false | 逻辑判断专用,不能与整数互转 |
6.2.引用数据类型
引用类型存储的是对象的内存地址(引用),对象本身在堆内存中。
| 类别 | 示例 | 说明 |
|---|---|---|
| 类(Class) | String, ArrayList, 自定义类 | 所有类的实例都是引用类型 |
| 接口(Interface) | List, Runnable | 变量声明为接口,实际指向实现类对象 |
| 数组(Array) | int[], String[] | 数组是对象,即使是基本类型数组 |
| 枚举(Enum) | Color.RED | 本质是特殊的类 |
| 注解(Annotation) | @Override | 元数据标记 |
6.3.自动装箱 / 拆箱(Autoboxing & Unboxing)
每个基本类型都有对应的包装类(Wrapper Class)
| 基本类型 | 包装类 | 缓存范围 |
|---|---|---|
| byte | Byte | -128 ~ 127 |
| short | Short | -128 ~ 127 |
| int | Integer | -128 ~ 127 ⭐高频考点 |
| long | Long | -128 ~ 127 |
| float | Float | 无缓存 |
| double | Double | 无缓存 |
| char | Character | 0 ~ 127 |
| boolean | Boolean | TRUE/FALSE 两个常量 |
6.4.如何选择数据类型
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 一般整数运算 | int | CPU 原生字长,性能最优 |
| 时间戳/文件大小/ID | long | int 范围不够 |
| 金额/汇率/税率 | BigDecimal | 避免浮点精度灾难 |
| 科学计算/图形学 | double | 精度和性能的平衡 |
| 大量标志位/状态压缩 | byte + 位运算 | 节省内存 |
| 文本处理 | String / StringBuilder | 不可变安全 / 可变高效拼接 |
| 集合容器元素 | 包装类 | 泛型不支持基本类型 |
7.运算符
Java 运算符是执行数据操作的核心符号。为了便于理解和记忆,我们可以将其分为 算术、关系、逻辑、位运算、赋值、三元 六大类,并重点关注优先级和易错陷阱
7.1.算术运算符
用于基本的数学计算
| 运算符 | 说明 | 示例 | ⚠️ 注意事项 |
|---|---|---|---|
+ - * / | 加减乘除 | 10 / 3 = 3 | 整数相除结果仍为整数(截断小数) |
% | 取模(求余) | 10 % 3 = 1 | 结果符号与被除数相同:-10 % 3 = -1 |
++ -- | 自增/自减 | a++ vs ++a | 见下方详解 |
+ | 字符串拼接 | "age:" + 25 | 只要一侧是 String,另一侧自动调用 toString() |
int a = 5;
int b = a++; // b=5, a=6 (先赋值给b,再自增)
int c = ++a; // c=7, a=7 (先自增,再赋值给c)
7.2.关系运算符(比较运算符)
结果为 boolean 类型。
| 运算符 | 说明 | 适用类型 |
|---|---|---|
== != | 等于 / 不等于 | 基本类型比值;引用类型比地址 |
> < >= <= | 大于/小于/大于等于/小于等于 | 仅基本数值类型 |
instanceof | 判断对象是否为某类的实例 | 仅引用类型 |
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false(不同堆对象)
System.out.println(s1.equals(s2)); // true(内容相同)
7.3.逻辑运算符
用于组合布尔表达式
| 运算符 | 名称 | 短路? | 说明 | ||
|---|---|---|---|---|---|
&& | 短路与 | ✅ | 左侧为 false 则不计算右侧 | ||
| ` | ` | 短路或 | ✅ | 左侧为 true 则不计算右侧 | |
& | 非短路与 | ❌ | 两侧都计算 | ||
| ` | ` | 非短路或 | ❌ | 两侧都计算 | |
! | 逻辑非 | - | 取反 | ||
^ | 异或 | - | 两值不同为 true,相同为 false |
7.4.位运算符
| 运算符 | 含义 | 典型用途 | |
|---|---|---|---|
& ` | ^ ~` | 与/或/异或/取反 | 掩码、标志位、加密 |
<< | 左移 | 乘以 2ⁿ | |
>> | 算术右移 | 除以 2ⁿ(保留符号) | |
>>> | 逻辑右移 | 无符号除法、哈希计算 |
7.5.赋值运算符
| 运算符 | 等价形式 | ⚠️ 隐藏行为 | |
|---|---|---|---|
= | 基本赋值 | 引用类型赋的是地址,不是拷贝 | |
+= -= *= /= %= | 复合赋值 | 自带隐式强制转换 | |
&= ` | = ^= <<= >>= >>>=` | 位复合赋值 | 同上 |
byte b = 10;
b += 200; // ✅ 编译通过!等价于 b = (byte)(b + 200),结果溢出为 -46
b = b + 200; // ❌ 编译报错!int 不能自动转回 byte
7.6.三元运算符
唯一的三目运算符,是 if-else 的简洁替代
result = condition ? valueIfTrue : valueIfFalse;
7.7.运算符优先级(从高到低)
无需死记硬背,掌握核心原则即可
- 括号
()优先级最高 → 不确定时就加括号 - 一元 > 算术 > 移位 > 关系 > 位 > 逻辑 > 三元 > 赋值
- 同级别从左到右(赋值运算符除外,从右到左)
// ❌ 难以阅读且容易出错
int r = a + b << 2 & 0xFF;
// ✅ 清晰明确
int r = ((a + b) << 2) & 0xFF;
7.8.实战避坑清单
| # | 陷阱 | 正确做法 |
|---|---|---|
| 1 | == 比较字符串/对象 | 用 .equals(),且常量放前面:"hello".equals(var) |
| 2 | 整数除法丢精度 | 至少一侧转 double:(double)a / b |
| 3 | & 代替 && 判空 | 始终用 && 做条件组合 |
| 4 | 三元运算符拆箱 NPE | 确保两侧类型安全,或对包装类做空检查 |
| 5 | 复合赋值静默溢出 | 关键计算显式强转并校验范围 |
| 6 | 浮点数比较 | 不用 ==,用 Math.abs(a-b) < epsilon 或 BigDecimal |
8.控制语句
8.1.选择语句(分支)
-
if-else
最基础的分支结构
if (condition) { // ... } else if (anotherCondition) { // ... } else { // ... } -
switch
根据表达式的值匹配多个分支
特性 传统 switch 增强 switch (Java 14+) 语法 case X:+break;case X ->(无需 break)穿透问题 ❌ 容易忘记 break 导致 fall-through ✅ 天然无穿透 返回值 ❌ 不支持 ✅ 可作为表达式赋值 多值匹配 多个 case 重复写 case A, B, C ->null 处理 ❌ NPE ✅ case null ->(Java 17+)支持类型 byte/short/int/char/String/enum 同上 + sealed class 模式匹配(预览)
8.2.循环语句
-
for 循环
// 经典 for for (int i = 0; i < n; i++) { ... } // 增强 for (for-each) for (String s : list) { ... } -
while / do-while
while (condition) { ... } // 先判断后执行 do { ... } while (condition); // 先执行后判断(至少执行一次) -
循环性能对比
场景 推荐方式 原因 ArrayList 遍历 经典 for / for-each 随机访问 O(1),两者性能接近 LinkedList 遍历 for-each / Iterator 避免 get(i) 的 O(n) 开销 已知次数 经典 for 最直接 流式处理/并行 Stream API 声明式 + 可并行化 大数据量数值计算 经典 for 避免装箱和 Stream 开销
8.3.跳转语句
| 语句 | 作用 | 注意事项 |
|---|---|---|
break | 跳出当前循环/switch | 支持标签:break outerLoop; |
continue | 跳过本次迭代,进入下一次 | 支持标签:continue outerLoop; |
return | 退出当前方法 | finally 块仍会执行 |
yield | switch 表达式中返回值 (Java 14+) | 仅用于 switch 表达式内部 |
8.4.异常控制流(补充)
try-catch-finally 本质上也是一种控制语句,影响程序执行路径
try {
// 可能异常的代码
} catch (IOException | SQLException e) { // 多异常捕获 (Java 7+)
// 处理
} finally {
// 必定执行(即使 try/catch 中有 return)
}
8.5.常见陷阱与最佳实践
| # | 陷阱 | 正确做法 |
|---|---|---|
| 1 | switch 忘记 break 导致穿透 | 使用 Java 14+ -> 语法 |
| 2 | for-each 中修改集合 | 用 Iterator.remove() 或 removeIf() |
| 3 | if (obj == null) 放后面 | 常量/null 放左侧:if (null == obj) 防误写成 = |
| 4 | 浮点数做循环条件 | for(double d=0; d!=1.0; d+=0.1) 可能死循环,改用整数计数 |
| 5 | 嵌套过深(>3层) | 提取方法 / 提前 return / 策略模式重构 |
| 6 | switch 对 null 传参 | 传统 switch 会 NPE;Java 17+ 可用 case null |
| 7 | finally 中 return | 永远不要这样做,会吞掉 try/catch 中的异常和返回值 |
9.方法
Java 方法(Method)是类或对象的行为抽象,是代码复用和模块化的基本单元。以下是从基础语法到高级特性的系统性梳理
9.1.方法定义与核心要素
[修饰符] 返回类型 方法名(参数列表) [throws 异常列表] {
// 方法体
}
| 要素 | 说明 | ⚠️ 关键点 |
|---|---|---|
| 修饰符 | public/protected/private/static/final/abstract/synchronized/native | 可组合使用,顺序无强制要求但建议按规范排列 |
| 返回类型 | 任意类型或 void | void 方法不能用 return 返回值(但可用 return; 提前退出) |
| 方法名 | 标识符,遵循驼峰命名 | 构造器不是方法(无返回类型、名称=类名) |
| 参数列表 | (类型 名称, ...) | Java 只有值传递,见下方详解 |
| throws | 声明可能抛出的受检异常 | 运行时异常(RuntimeException)无需声明 |
9.2.参数传递机制:只有值传递
这是 Java 最重要的概念之一,也是面试高频考点
| 参数类型 | 传递的内容 | 方法内修改的影响 |
|---|---|---|
| 基本类型 | 值的副本 | ❌ 不影响原始变量 |
| 引用类型 | 引用的副本(地址拷贝) | ✅ 通过引用修改对象属性 → 影响原对象 ❌ 重新赋值引用本身 → 不影响原引用 |
// 经典示例
void modify(int num, StringBuilder sb) {
num = 100; // 修改副本,外部不变
sb.append(" world"); // 通过副本引用操作同一对象,外部可见
sb = new StringBuilder("new"); // 重新赋值副本引用,外部不受影响
}
9.3.方法重载 vs 重写
| 对比维度 | 重载 (Overload) | 重写 (Override) |
|---|---|---|
| 位置 | 同一个类中 | 父子类之间 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 必须不同 | 必须相同 |
| 返回类型 | 可以不同(不能仅靠返回类型区分) | 相同或是其子类型(协变返回) |
| 访问修饰符 | 无限制 | 不能比父类更严格 |
| 绑定时机 | 编译期静态绑定 | 运行期动态绑定(多态) |
| 注解 | 无需 | 建议加 @Override 防误写 |
9.4.特殊方法类型
构造器
- 名称与类名相同,无返回类型
- 不写则编译器自动生成无参构造器;写了有参构造器后,无参构造器不再自动生成
- 支持
this(...)调用同类其他构造器,super(...)调用父类构造器(必须是第一条语句)
static 方法
- 属于类而非实例,可通过类名直接调用
- 不能访问非静态成员、不能使用
this/super - 不能被重写(只能被隐藏),不参与多态
- 典型用途:工具方法、工厂方法、main 入口
final 方法
- 不能被子类重写
- JVM 可进行内联优化(现代 JIT 已能自动判断,手动加 final 的性能收益有限)
abstract 方法
- 只有声明没有实现,以
;结尾 - 所在类必须是抽象类
- 子类必须实现,否则子类也必须是抽象类
native 方法
- 由 C/C++ 等本地语言实现(JNI)
- 无方法体,以
;结尾 - 典型:
System.arraycopy(),Thread.sleep()
可变参数 (Varargs)
void print(String... args) { } // 本质是 String[] 数组
// 可变参数必须是最后一个参数;避免与重载混用导致歧义。
9.5.Java 新方法特性演进
| 版本 | 特性 | 说明 |
|---|---|---|
| 8 | Lambda / 方法引用 | list.forEach(System.out::println) 函数式编程 |
| 8 | default 方法 | 接口中可提供默认实现,解决接口演化问题 |
| 9 | private 接口方法 | 接口中抽取私有辅助方法,避免 default 方法代码重复 |
| 16+ | Record 紧凑构造器 | 自动生成全参构造器/getter/equals/hashCode/toString |
| 21 | 未命名变量 _ | void process(int _, String name) 忽略不需要的参数 |
9.6.实战避坑清单
| # | 陷阱 | 正确做法 |
|---|---|---|
| 1 | 重写时签名不一致变成重载 | 始终加 @Override 注解 |
| 2 | 父类构造器带参,子类忘记 super() | 显式调用 super(args) |
| 3 | static 方法中用 this | static 方法无 this 上下文 |
| 4 | 可变参数传 null | method(null) 可能匹配歧义,明确传 new Type[]{} |
| 5 | 方法过长 (>50行) | 拆分为多个职责单一的私有方法 |
| 6 | 参数过多 (>4个) | 封装为参数对象或使用 Builder 模式 |
| 7 | 在构造器中调用可重写方法 | 子类字段尚未初始化,导致 NPE 或逻辑错误 |
| 8 | 用 boolean 参数控制行为 | process(true) 可读性差,改用枚举或策略模式 |
9.7.方法设计原则速查
- 单一职责:一个方法只做一件事
- 最小权限:能用 private 就不用 protected,能用 protected 就不用 public
- 防御性编程:公开方法校验参数(
Objects.requireNonNull()),私有方法可用断言 - 命名即文档:
calculateTotalPrice()优于calc(),isValid()优于check() - 避免副作用:查询方法不应修改状态,修改方法应有明确命名(如
set/add/remove)