引用数据类型
引用数据类型存储的是对象的引用(内存地址),而不是对象本身的值。
引用类型分类
| 类型 | 说明 | 示例 |
|---|---|---|
| 类 (Class) | 自定义类或 Java 内置类 | String, Object, User |
| 接口 (Interface) | 接口类型 | Runnable, Comparable |
| 数组 (Array) | 同类型数据的集合 | int[], String[] |
| 枚举 (Enum) | 固定常量集合 | Season, Status |
| 注解 (Annotation) | 元数据标记 | @Override, @Service |
引用类型与基本类型的区别
| 对比项 | 基本数据类型 | 引用数据类型 |
|---|---|---|
| 存储位置 | 栈内存 | 堆内存 |
| 存储内容 | 实际值 | 内存地址(引用) |
| 默认值 | 有(0/false 等) | null |
| 内存分配 | 自动(栈) | 手动 new(堆) |
| 传递方式 | 值传递 | 引用传递(地址值) |
| 比较方式 | == 比较值 | == 比较地址 |
┌─────────────────────────────────────────────────────────┐
│ 基本数据类型 │
├─────────────────────────────────────────────────────────┤
│ 栈内存 │
│ ┌─────────┐ │
│ │ age = 25│ │
│ └─────────┘ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 引用数据类型 │
├─────────────────────────────────────────────────────────┤
│ 栈内存 │ 堆内存 │
│ ┌─────────┐ │ ┌─────────────────┐ │
│ │ obj ────┼───────→│ │ User 对象 │ │
│ └─────────┘ │ │ name = "Java" │ │
│ │ │ age = 25 │ │
│ │ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘String(字符串)
java
// 创建方式
String s1 = "Hello"; // 字面量方式
String s2 = new String("Hello"); // new 方式
// 常用方法
String s = "Hello World";
s.length(); // 11
s.charAt(0); // 'H'
s.substring(0, 5); // "Hello"
s.indexOf("World"); // 6
s.toLowerCase(); // "hello world"
s.toUpperCase(); // "HELLO WORLD"
s.split(" "); // ["Hello", "World"]数组(Array)
java
// 一维数组
int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[5];
String[] names = new String[]{"Alice", "Bob"};
// 二维数组
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
// 常用属性和方法
arr1.length; // 5
Arrays.sort(arr1);
Arrays.toString(arr1);类(Class)
java
// 自定义类
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// getter/setter
}
// 使用
User user = new User("Java", 25);接口(Interface)
java
// 定义接口
public interface Runnable {
void run();
}
// 实现接口
public class MyTask implements Runnable {
@Override
public void run() {
System.out.println("Task running");
}
}
// 使用
Runnable task = new MyTask();
task.run();枚举(Enum)
java
// 定义枚举
public enum Status {
SUCCESS,
FAIL,
PENDING;
}
// 使用
Status status = Status.SUCCESS;参数传递
基本类型传递:传递的是值的副本,方法内部修改不影响原值。
java
public class Test {
public static void main(String[] args) {
int num = 10;
changePrimitive(num);
System.out.println(num); // 输出 10,值未改变
}
static void changePrimitive(int num) {
num = 20;
}
}引用类型传递:传递的是引用的副本,方法内部可以通过引用副本修改对象内容。
java
public class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
changeArray(arr);
System.out.println(Arrays.toString(arr)); // 输出 [10, 2, 3]
String str = "Hello";
changeString(str);
System.out.println(str); // 输出 Hello,未改变
}
static void changeArray(int[] arr) {
arr[0] = 10; // 改变了数组内容
}
static void changeString(String str) {
str = "World"; // 只是改变了局部变量引用
}
}null 值
null 表示引用不指向任何对象,是引用类型的默认值。使用前应检查是否为 null,否则会抛出 NullPointerException。
java
String str = null; // 未指向任何对象
str.length(); // NullPointerException
// 检查后再使用
if (str != null) {
System.out.println(str.length());
}null 的比较:
java
String s1 = null;
String s2 = null;
String s3 = "Hello";
s1 == s2; // true(都是 null)
s1 == s3; // false
s1 == null; // true
// Objects.isNull() / Objects.nonNull()
Objects.isNull(s1); // true
Objects.nonNull(s3); // true包装类
基本类型对应的引用类型:
| 基本类型 | 包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
java
// 自动装箱(基本类型 → 包装类)
Integer obj = 10;
// 自动拆箱(包装类 → 基本类型)
int num = obj;
// 常用方法
Integer.parseInt("123");
Integer.valueOf("456");
obj.toString();
obj.hashCode();内存分析
java
public class MemoryDemo {
public static void main(String[] args) {
// 基本类型 - 栈
int a = 10;
int b = a;
b = 20;
System.out.println(a); // 10
// 引用类型 - 栈 + 堆
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // arr2 和 arr1 指向同一对象
arr2[0] = 10;
System.out.println(arr1[0]); // 10
// String 特殊(字符串常量池)
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
s1 == s2; // true(字符串常量池)
s1 == s3; // false(new 创建的新对象)
s1.equals(s3); // true(内容相同)
}
}