Java中的引用类型包括类(class)、接口(interface)、数组(array)等,在Java中,除了基本数据类型(包括8种:byte、short、int、long、float、double、boolean、char),其余的都是引用类型。当我们声明一个引用类型的变量时,实际上是在栈内存中分配了一个引用变量,该变量指向堆内存中的对象。
引用类型主要包括以下几种:
除了这些,Java还有几种特殊的引用类型:
在Java中,所有的引用类型都继承自java.lang.Object类。这意味着任何引用类型的变量都可以指向一个Object类型的实例。引用类型的变量存储的是对象的引用(内存地址),而不是对象本身。
在Java中,当您声明一个引用类型的变量时,确实是在Java的栈内存(Stack Memory)中创建了一个引用变量。这个引用变量存储了一个指向堆内存(Heap Memory)中实际对象的地址。这里有一些补充和澄清的点:
Java中基本类型直接存储值,它们的大小和操作是固定的。如:int: 32位有符号整数,long: 64位有符号整数等,这些类型的操作(如加法、减法、乘法等)也是由语言规范明确定义的。
引用类型存储的是对对象的引用,即对象在内存中的地址。对象本身可以包含多个值,并且可以拥有操作这些值的方法。
为了进一步说明这一点,可以使用一个简单的比喻:
基本类型就像是你口袋里的现金,你可以直接拿出来使用。
引用类型就像是你钱包里的信用卡,它代表了你的账户,而账户里可以有很多信息和资金,但你需要通过信用卡这个“引用”来访问这些内容。
下面通过一些简单的Java代码示例来说明基本类型和引用类型在赋值和传递参数方面的不同行为。
基本类型的赋值和传递参数
首先,我们来看一个基本类型的例子。在这个例子中,我们将演示基本类型变量的赋值和方法调用时的值传递。
- public class PrimitiveExample {
- public static void main(String[] args) {
- int a = 10;
- int b = a; // 赋值操作,b现在是a的一个副本,与a相互独立
- System.out.println("Before change, a = " + a + " and b = " + b);
-
- changeValue(a); // 尝试在方法中改变a的值
- System.out.println("After change, a = " + a + " and b = " + b);
- }
-
- public static void changeValue(int number) {
- number = 50; // 这里改变的是number的副本,原始的a值不会改变
- }
- }
当你运行这个程序时,你会看到输出结果是:
Before change, a = 10 and b = 10
After change, a = 10 and b = 10
这说明尽管我们在changeValue方法中尝试改变number的值,但实际上a的值并没有改变,因为基本类型是按值传递的。
这说明尽管我们在changeValue方法中尝试改变number的值,但实际上a的值并没有改变,因为基本类型是按值传递的。
引用类型的赋值和传递参数
接下来,我们来看一个引用类型的例子。在这个例子中,我们将演示引用类型变量的赋值和方法调用时的引用传递。
- public class ReferenceExample {
- public static void main(String[] args) {
- int[] a = {10}; // 引用类型,数组
- int[] b = a; // 赋值操作,b现在是a的引用,指向同一个数组对象
- System.out.println("Before change, a[0] = " + a[0] + " and b[0] = " + b[0]);
-
- changeValue(a); // 尝试在方法中改变数组a的第一个元素
- System.out.println("After change, a[0] = " + a[0] + " and b[0] = " + b[0]);
- }
-
- public static void changeValue(int[] array) {
- array[0] = 50; // 改变的是数组对象的第一个元素,a和b都会受到影响
- }
- }
当你运行这个程序时,你会看到输出结果是:
Before change, a[0] = 10 and b[0] = 10
After change, a[0] = 50 and b[0] = 50
这说明当我们在changeValue方法中改变数组对象的第一个元素时,由于a和b都引用同一个数组对象,它们都受到了影响。这是因为引用类型是按引用传递的。
补充:
1.Java中处理字符串的类
在Java中,处理字符串的类主要有以下几种:
String:这是一个不可变的字符序列。由于其不可变性,每次修改String都会生成新的String对象。【https://docs.oracle.com/javase/8/docs/api/java/lang/String.html 】
StringBuilder:这是一个可变的字符序列,提供了一系列方法来修改字符串的内容,适用于单线程环境下的字符串操作。【https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html 】
StringBuffer:StringBuffer和StringBuilder非常相似,也是一个可变的字符序列,但它是线程安全的。StringBuffer的方法都是同步的,可以在多线程环境下安全使用,但这也意味着它比StringBuilder在性能上稍慢。
这三种是Java中最常用的字符串类型。通常,如果不需要可变字符串或者线程安全,就使用String;如果需要可变字符串且运行在单线程环境下,就使用StringBuilder;如果需要在多线程环境中操作字符串,就使用StringBuffer。
不可变(Immutable):不可变对象的状态在创建后就不能被改变。对于String类来说,这意味着一旦一个String对象被创建,它所包含的字符序列就不能被改变。如果你尝试修改String对象,实际上会创建一个新的String对象,原来的字符串对象内容不会改变。这就是为什么执行字符串连接操作时,会消耗更多的内存,因为每次连接操作实际上都在创建新的字符串对象。
可变(Mutable):相反,可变对象可以在创建后改变其状态。StringBuilder类就是一个可变的字符串类,它提供了各种方法来修改字符串的内容,如append、insert、delete等,而不需要每次都创建一个新的对象。这使得StringBuilder在执行大量字符串修改操作时比String更加高效。
2.Java中 “不可变”(Immutable)和“可变” (Mutable)
Java中,“不可变”和“可变”对象的概念是根据对象状态是否可以在创建后被改变来定义的。下面是一些Java中常见的不可变和可变对象的例子:
String对象一旦创建,其值就不能被改变。LocalDate、LocalTime、LocalDateTime、ZonedDateTime等,都是不可变的。int[]、Object[]等,都是可变的,因为你可以改变数组中的元素。ArrayList、LinkedList、HashSet、HashMap等,都是可变的,因为你可以添加、删除或更改集合中的元素。