变量与数据类型

文章目录

一、变量定义与分类

[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))

空指针问题

五、实践练习建议

练习1:类型转换实验

练习2:包装类方法使用

练习3:变量作用域理解

六、常见面试问题思考

定义

底层实现原理

关键注意点(面试高频坑)

结果:true

[核心原因: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.intInteger有什么区别?

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 = 100Integer i = Integer.valueOf(100)
自动拆箱 包装类的xxxValue()实例方法(如intValue() int j = iint j = i.intValue()
关键注意点(面试高频坑)
  • 拆箱时若包装类对象为null,会抛出NullPointerException(NPE):示例:Integer i = null; int j = i;(运行时抛 NPE,因为i.intValue()调用了 null 对象的方法)。
  • 自动装箱 / 拆箱仅发生在 "基本类型与包装类直接赋值" 场景,不涉及跨类型(如int不能直接装箱为Long)。

3.Integer i = 100Integer j = 100i == j的结果是什么?为什么?

结果:true
核心原因:Integer 的「缓存池机制」(IntegerCache)

Java 为了优化性能,在 Integer 类中内置了一个静态缓存池 (IntegerCache),底层是一个Integer[]数组,默认缓存范围是 -128 ~ 127 (可通过 JVM 参数-XX:AutoBoxCacheMax=<value>调整上限)。

底层逻辑:
  1. 当执行Integer i = 100时,触发自动装箱,调用Integer.valueOf(100)
  2. valueOf()方法会判断数值是否在缓存池范围内:
    • 若在范围内(如 100),直接返回缓存池中的已有 Integer 对象,不新建;
    • 若超出范围(如 200),则新建Integer对象(new Integer(200));
  3. 因此ij指向的是缓存池中的同一个对象==比较的是引用地址,结果为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 缓存TRUEFALSE两个常量;
  • 若用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 → intdouble → 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 Lang3NumberUtilsSpringConversionService),避免手动硬编码。
相关推荐
Y***h18739 分钟前
eclipse配置Spring
java·spring·eclipse
p***629942 分钟前
CVE-2024-38819:Spring 框架路径遍历 PoC 漏洞复现
java·后端·spring
Lisonseekpan42 分钟前
Java分词器深度评测与实战指南
java·开发语言·后端
饕餮争锋1 小时前
Kotlin: [Internal Error] java.lang.NoSuchFieldError: FILE_HASHING_STRATEGY
java·kotlin
明洞日记1 小时前
【设计模式手册014】解释器模式 - 语言解释的优雅实现
java·设计模式·解释器模式
百***35481 小时前
JavaScript在Node.js中的集群部署
开发语言·javascript·node.js
光影少年1 小时前
node.js和nest.js做智能体开发需要会哪些东西
开发语言·javascript·人工智能·node.js
lichong9511 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端
xu_yule2 小时前
Linux_14(多线程)线程控制+C++多线程
java·开发语言·jvm