文章目录
[1. 什么是变量?](#1. 什么是变量?)
[2. 变量的三要素](#2. 变量的三要素)
[3. 变量的声明与初始化](#3. 变量的声明与初始化)
[4. 变量的分类(按作用域)](#4. 变量的分类(按作用域))
[1. 基本数据类型(8种)](#1. 基本数据类型(8种))
[2. 引用数据类型](#2. 引用数据类型)
[1. 自动类型转换(隐式转换)](#1. 自动类型转换(隐式转换))
[2. 强制类型转换(显式转换)](#2. 强制类型转换(显式转换))
[3. 表达式中的自动提升](#3. 表达式中的自动提升)
[四、包装类(Wrapper Classes)](#四、包装类(Wrapper Classes))
[1. 基本类型与包装类的对应关系](#1. 基本类型与包装类的对应关系)
[2. 自动装箱与拆箱](#2. 自动装箱与拆箱)
[3. 包装类的常用方法](#3. 包装类的常用方法)
[4. 重要注意事项](#4. 重要注意事项)
[缓存机制(Integer Cache)](#缓存机制(Integer Cache))
[核心原因:Integer 的「缓存池机制」(IntegerCache)](#核心原因:Integer 的「缓存池机制」(IntegerCache))
[1. 基本数据类型(int、long、char 等)](#1. 基本数据类型(int、long、char 等))
[2. 引用数据类型(Integer、String、自定义类等)](#2. 引用数据类型(Integer、String、自定义类等))
[1. 数值精度丢失(溢出 / 截断)](#1. 数值精度丢失(溢出 / 截断))
[2. 类型不兼容异常(ClassCastException)](#2. 类型不兼容异常(ClassCastException))
[3. 空指针异常(NPE)](#3. 空指针异常(NPE))
[4. 格式错误异常(NumberFormatException)](#4. 格式错误异常(NumberFormatException))
[1. 避免数值精度丢失](#1. 避免数值精度丢失)
[2. 避免 ClassCastException](#2. 避免 ClassCastException)
[3. 避免拆箱 NPE](#3. 避免拆箱 NPE)
[4. 避免 NumberFormatException](#4. 避免 NumberFormatException)
[5. 通用原则](#5. 通用原则)
一、变量定义与分类
1. 什么是变量?
变量是内存中的一块存储区域,用于保存数据,数据在程序运行期间可以改变。
2. 变量的三要素
-
数据类型:决定变量占用内存大小和存储格式
-
变量名:标识符,用于访问变量
-
值:变量中存储的数据
3. 变量的声明与初始化
// 声明
int age;
String name;
// 初始化
age = 20;
name = "张三";
// 声明并初始化
double salary = 8000.0;
char grade = 'A';
4. 变量的分类(按作用域)
局部变量
-
在方法、构造方法或代码块中定义
-
只在声明它的范围内有效
-
必须显式初始化后才能使用
java
public void show() {
int localVar = 10; // 局部变量
System.out.println(localVar);
}
实例变量(成员变量)
-
在类中定义,但在方法、构造方法、代码块之外
-
随着对象的创建而创建
-
有默认值(数值型为0,布尔型为false,引用类型为null)
java
public class Student {
String name; // 实例变量
int age; // 实例变量,默认值0
public void study() {
int hours = 2; // 局部变量
System.out.println(name + "学习了" + hours + "小时");
}
}
类变量(静态变量)
-
用
static关键字修饰 -
属于类,所有对象共享同一份拷贝
-
在程序开始时创建,程序结束时销毁
java
public class Counter {
static int count = 0; // 类变量
public Counter() {
count++;
}
}
二、数据类型详解
1. 基本数据类型(8种)
| 类型 | 关键字 | 大小 | 范围 | 默认值 | 用途 |
|---|---|---|---|---|---|
| 字节型 | byte | 1字节 | -128 ~ 127 | 0 | 节省空间,处理二进制数据 |
| 短整型 | short | 2字节 | -32768 ~ 32767 | 0 | 较少使用 |
| 整型 | int | 4字节 | -2³¹ ~ 2³¹-1 | 0 | 最常用的整数类型 |
| 长整型 | long | 8字节 | -2⁶³ ~ 2⁶³-1 | 0L | 大整数,需加L后缀 |
| 单精度浮点 | float | 4字节 | ±3.4E38 | 0.0f | 需加f后缀 |
| 双精度浮点 | double | 8字节 | ±1.7E308 | 0.0d | 默认浮点类型 |
| 字符型 | char | 2字节 | 0 ~ 65535 | '\u0000' | 存储单个字符 |
| 布尔型 | boolean | 未定 | true/false | false | 逻辑判断 |
2. 引用数据类型
-
类(如String、自定义类)
-
接口
-
数组
java
String str = "Hello"; // 类类型
int[] arr = new int[5]; // 数组类型
Student stu = new Student(); // 自定义类类型
三、类型转换
1. 自动类型转换(隐式转换)
规则:小范围类型 → 大范围类型(安全转换)
转换方向:
java
byte → short → int → long → float → double
↗
char
java
int i = 100;
long l = i; // 自动转换:int → long
double d = l; // 自动转换:long → double
float f = i; // 自动转换:int → float
char c = 'A';
int charToInt = c; // char → int,得到65(ASCII值)
2. 强制类型转换(显式转换)
规则:大范围类型 → 小范围类型(可能丢失精度)
java
double d = 100.04;
int i = (int) d; // 强制转换,i=100(小数部分丢失)
long l = 123456789L;
int i2 = (int) l; // 可能溢出,如果l超出int范围
// 字符与数字转换
char c = (char) 65; // c = 'A'
int num = (int) 'A'; // num = 65
3. 表达式中的自动提升
java
byte a = 10;
byte b = 20;
// byte result = a + b; // 编译错误!表达式自动提升为int
byte result = (byte)(a + b); // 需要强制转换
int x = 10;
double y = 5.5;
double result2 = x + y; // x自动提升为double
四、包装类(Wrapper Classes)
1. 基本类型与包装类的对应关系
| 基本类型 | 包装类 | 继承关系 |
|---|---|---|
| byte | Byte | → Number → Object |
| short | Short | → Number → Object |
| int | Integer | → Number → Object |
| long | Long | → Number → Object |
| float | Float | → Number → Object |
| double | Double | → Number → Object |
| char | Character | → Object |
| boolean | Boolean | → Object |
2. 自动装箱与拆箱
java
// 自动装箱:基本类型 → 包装类
Integer i = 10; // 相当于 Integer i = Integer.valueOf(10);
Double d = 3.14; // 相当于 Double d = Double.valueOf(3.14);
// 自动拆箱:包装类 → 基本类型
int num = i; // 相当于 int num = i.intValue();
double value = d; // 相当于 double value = d.doubleValue();
3. 包装类的常用方法
字符串转换
java
// 字符串 → 基本类型
int num = Integer.parseInt("123");
double d = Double.parseDouble("3.14");
boolean b = Boolean.parseBoolean("true");
// 字符串 → 包装类
Integer i = Integer.valueOf("456");
Double dObj = Double.valueOf("2.718");
// 基本类型/包装类 → 字符串
String str1 = Integer.toString(123);
String str2 = String.valueOf(456);
String str3 = 789 + ""; // 简单方法,但不推荐大量使用
数值比较与转换
java
Integer a = 100;
Integer b = 200;
// 比较
System.out.println(a.equals(b)); // false,比较值
System.out.println(a.compareTo(b)); // -1,a < b
// 类型转换
int intValue = a.intValue();
double doubleValue = a.doubleValue();
byte byteValue = a.byteValue();
4. 重要注意事项
缓存机制(Integer Cache)
java
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true,使用缓存
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false,超出缓存范围
System.out.println(c.equals(d)); // true,应该用equals比较
缓存范围:
-
Integer: -128 ~ 127
-
Byte: 全部缓存
-
Short: -128 ~ 127
-
Long: -128 ~ 127
-
Character: 0 ~ 127
空指针问题
java
Integer num = null;
// int value = num; // 运行时NullPointerException!
int value = num != null ? num : 0; // 安全写法
五、实践练习建议
练习1:类型转换实验
java
public class TypeConversion {
public static void main(String[] args) {
// 测试各种类型转换
double d = 100.999;
int i = (int) d; // 结果是什么?
System.out.println("double " + d + " -> int " + i);
// 测试溢出
byte b = (byte) 200; // 结果是什么?
System.out.println("200 -> byte: " + b);
}
}
练习2:包装类方法使用
java
public class WrapperPractice {
public static void main(String[] args) {
// 1. 字符串转数字
String input = "123.45";
double value = Double.parseDouble(input);
// 2. 数字转字符串
String str = Integer.toBinaryString(255); // 二进制表示
System.out.println("255的二进制: " + str);
// 3. 比较缓存机制
compareIntegerCache();
}
static void compareIntegerCache() {
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println("100 == 100: " + (a == b)); // true
System.out.println("200 == 200: " + (c == d)); // false
}
}
练习3:变量作用域理解
java
public class VariableScope {
static int classVar = 10; // 类变量
int instanceVar = 20; // 实例变量
public void testMethod() {
int localVar = 30; // 局部变量
if (true) {
int blockVar = 40; // 代码块变量,只在if块内有效
System.out.println(localVar); // 可以访问
System.out.println(blockVar); // 可以访问
}
// System.out.println(blockVar); // 编译错误!超出作用域
}
}
六、常见面试问题思考
1.int和Integer有什么区别?
int 是 Java 的基本数据类型 ,Integer 是 int 的包装类(属于引用数据类型)
| 对比维度 | int(基本数据类型) | Integer(包装类) |
|---|---|---|
| 本质 | 直接存储数值(无对象概念) | 类的实例(对象),包含 int 类型的成员变量存储数值 |
| 默认值 | 0(局部变量未初始化编译报错,成员变量默认 0) | null(所有引用类型默认值) |
| 内存存储位置 | 局部变量→栈帧;成员变量→堆中对象里(直接存值) | 局部变量→栈帧存引用地址;对象本身→堆(缓存池除外) |
| 方法 / 工具支持 | 无(不能调用任何方法) | 提供大量静态方法(如parseInt()、valueOf())、常量(如MAX_VALUE) |
| 使用场景 | 简单数值计算、局部变量、数组元素等 | 泛型(如List<Integer>,泛型不支持基本类型)、集合框架(如Map的 key/value)、需要 null 表示 "无值" 的场景(如数据库字段空值映射) |
| 比较方式 | ==直接比较数值 |
==比较引用地址;equals()比较数值(重写了 Object 的 equals) |
2.自动装箱拆箱是怎么实现的?
自动装箱(Autoboxing)和拆箱(Unboxing)是 Java 5 引入的语法糖,底层通过包装类的特定方法实现,目的是简化基本类型和包装类的转换。
定义
- 自动装箱:基本数据类型 → 对应包装类对象 (如
int → Integer); - 自动拆箱:包装类对象 → 对应基本数据类型 (如
Integer → int)。
底层实现原理
| 操作 | 底层调用的方法 | 示例代码反编译结果 |
|---|---|---|
| 自动装箱 | 包装类的valueOf(基本类型)静态方法 |
Integer i = 100 → Integer i = Integer.valueOf(100) |
| 自动拆箱 | 包装类的xxxValue()实例方法(如intValue()) |
int j = i → int j = i.intValue() |
关键注意点(面试高频坑)
- 拆箱时若包装类对象为
null,会抛出NullPointerException(NPE):示例:Integer i = null; int j = i;(运行时抛 NPE,因为i.intValue()调用了 null 对象的方法)。 - 自动装箱 / 拆箱仅发生在 "基本类型与包装类直接赋值" 场景,不涉及跨类型(如
int不能直接装箱为Long)。
3.Integer i = 100和Integer j = 100,i == j的结果是什么?为什么?
结果:true
核心原因:Integer 的「缓存池机制」(IntegerCache)
Java 为了优化性能,在 Integer 类中内置了一个静态缓存池 (IntegerCache),底层是一个Integer[]数组,默认缓存范围是 -128 ~ 127 (可通过 JVM 参数-XX:AutoBoxCacheMax=<value>调整上限)。
底层逻辑:
- 当执行
Integer i = 100时,触发自动装箱,调用Integer.valueOf(100); valueOf()方法会判断数值是否在缓存池范围内:- 若在范围内(如 100),直接返回缓存池中的已有 Integer 对象,不新建;
- 若超出范围(如 200),则新建
Integer对象(new Integer(200));
- 因此
i和j指向的是缓存池中的同一个对象 ,==比较的是引用地址,结果为true。
反例验证(超出缓存范围):
java
Integer i = 200;
Integer j = 200;
System.out.println(i == j); // 结果:false(各自new了新对象,地址不同)
System.out.println(i.equals(j)); // 结果:true(equals重写后比较数值)
面试延伸:
- 其他包装类的缓存机制:Byte、Short、Long 的缓存池默认都是
-128~127(不可调整),Character 缓存0~127,Boolean 缓存TRUE和FALSE两个常量; - 若用
new Integer(100)创建对象,不会走缓存池,即使数值相同,==也为false:
java
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.println(i == j); // false(两个不同对象,地址不同)
4.基本数据类型和引用数据类型在内存中的存储有什么区别?
Java 内存模型中,核心区域是栈(Stack) 和堆(Heap),两者存储差异的核心是 "是否存储引用地址":
1. 基本数据类型(int、long、char 等)
- 存储规则:直接存储数值,不涉及引用;
- 具体位置:
- 局部变量(方法内定义):存储在栈帧中(栈是线程私有,读写速度快);
- 成员变量(类中定义):存储在堆中的对象实例里(随对象创建而分配,销毁而释放);
- 示例:
int a = 10(栈帧中直接存 "10")、class A { int b = 20; }(A 对象在堆中,b 作为成员变量直接存 "20")。
2. 引用数据类型(Integer、String、自定义类等)
- 存储规则:分两部分存储------ 变量本身存 "引用地址",对象实际数据存 "堆"(常量池除外);
- 具体位置:
- 变量(局部 / 成员):存储在栈帧中,值是对象在堆中的内存地址(类似 "指针");
- 对象实例:存储在堆中(包含对象的成员变量、方法元数据引用等);
- 特殊情况:字符串常量(如
String s = "abc")、Integer 缓存池对象,存储在方法区的常量池中(复用对象,优化内存);
- 示例:
Integer i = 100(栈帧存缓存池对象的地址,常量池存对象本身,数值 100)。
核心区别总结:
| 类型 | 存储方式 | 能否为 null | 内存开销 |
|---|---|---|---|
| 基本数据类型 | 直接存值 | 不能(局部变量未初始化编译报错) | 小(固定字节,如 int 占 4 字节) |
| 引用数据类型 | 栈存地址,堆存对象 | 能(默认 null) | 大(额外存储引用地址 + 对象头) |
5.类型转换时可能遇到哪些问题?如何避免?
Java 中的类型转换分为「自动类型转换」(隐式,安全)和「强制类型转换」(显式,可能不安全),核心问题集中在强制转换场景。
一、常见问题及场景
1. 数值精度丢失(溢出 / 截断)
- 场景:大范围数值转小范围类型(如
long → int、double → int); - 示例:
java
long num = 2147483648L; // 超出int的最大值(2^31-1=2147483647)
int i = (int) num;
System.out.println(i); // 结果:-2147483648(溢出,二进制高位截断导致)
2. 类型不兼容异常(ClassCastException)
- 场景:引用类型强制转换(如父类转子类、无关类型转换);
- 示例:
java
Object obj = "abc";
Integer i = (Integer) obj; // 运行时抛ClassCastException(String和Integer无继承关系)
3. 空指针异常(NPE)
- 场景:包装类拆箱时对象为
null; - 示例:
java
Integer i = null;
int j = i; // 自动拆箱调用i.intValue(),抛NPE
4. 格式错误异常(NumberFormatException)
- 场景:字符串转数值类型(如
String → int)时,字符串格式不符合要求; - 示例:
java
String str = "123a";
int i = Integer.parseInt(str); // 抛NumberFormatException
二、避免方案(面试重点)
1. 避免数值精度丢失
- 转换前先校验范围:用包装类的
MIN_VALUE/MAX_VALUE判断;
java
long num = 2147483648L;
if (num >= Integer.MIN_VALUE && num <= Integer.MAX_VALUE) {
int i = (int) num;
} else {
// 处理溢出逻辑(如抛异常、返回默认值)
}
- 优先使用
BigDecimal处理高精度数值(如金额计算),避免浮点型转换。
2. 避免 ClassCastException
- 引用类型转换前用
instanceof判断类型兼容性;
java
Object obj = "abc";
if (obj instanceof Integer) { // 先判断是否为目标类型
Integer i = (Integer) obj;
}
- 尽量避免跨层级 / 无关类型转换,依赖继承关系设计。
3. 避免拆箱 NPE
拆箱前先判断包装类是否为null;
java
Integer i = null;
int j = (i != null) ? i : 0; // 空值兜底
用 Java 8 + 的Optional工具类优化:
java
Integer i = null;
int j = Optional.ofNullable(i).orElse(0);
4. 避免 NumberFormatException
- 用
try-catch捕获异常,同时校验字符串格式;
java
String str = "123a";
try {
if (str.matches("\\d+")) { // 正则校验是否为纯数字
int i = Integer.parseInt(str);
}
} catch (NumberFormatException e) {
// 处理格式错误(如返回默认值、提示用户)
}
5. 通用原则
- 优先使用自动类型转换(小范围→大范围),减少强制转换;
- 复杂转换用工具类(如
Apache Commons Lang3的NumberUtils、Spring的ConversionService),避免手动硬编码。