Java

Java 知识量:11 - 45 - 220

2.9 引用类型><

引用类型与基本类型- 2.9.1 -

引用类型(Reference Types)是指在Java中需要通过引用来访问的数据类型。引用类型在Java中主要包括类(Class)、接口(Interface)、数组(Array)和枚举(Enum)。

引用类型的值本质上是一个指针,它存储的是实际数据对象在内存中的地址。当我们声明一个引用类型的变量时,实际上是在声明一个指针,该指针指向实际的数据对象。

在Java中,所有的对象都是引用类型,包括自定义的类和接口、数组以及枚举类型。创建一个对象时,实际上是在堆内存中创建了一个实际的数据对象,然后将其地址赋值给引用类型的变量。

引用类型在Java中有很多重要的特性,例如继承、多态和垃圾回收等。继承允许创建一个新类,它可以从已有的类派生出来,并添加或覆盖属性和方法。多态允许以统一的方式处理不同的对象,垃圾回收则自动管理内存,释放不再使用的对象所占用的内存空间。

基本类型(又称原始类型)和引用类型的主要区别有:

  • 存储位置:基本类型的数据直接存储在栈内存中,而引用类型的数据存储在堆内存中。

  • 存储方式:基本类型存储的是实际的数据值,而引用类型存储的是实际数据的内存地址。

  • 赋值:基本类型赋值时,会创建一个新的值,原来的值不会改变;而引用类型赋值时,只是将引用指向新的对象,原来的对象不会被改变。

  • 继承:基本类型不可以被继承,而引用类型可以被继承。

  • 默认值:基本类型有默认的初始值(例如,int的默认值为0),而引用类型的默认值为null。

  • 包装类:基本类型有对应的包装类,例如int的包装类为Integer,而引用类型没有包装类。

  • 性能:基本类型的操作通常比引用类型的操作更快,因为它们在栈中直接存储数据,而引用类型需要在堆中查找实际数据。

  • 安全性:基本类型比引用类型更安全,因为基本类型不可能引用到null。

处理对象和引用副本- 2.9.2 -

在Java中,对象和引用是面向对象编程的两个核心概念。对象是类的实例,而引用是对象在内存中的地址。

Java中的对象是通过引用进行操作的。当创建一个对象并将其赋值给一个变量时,实际上是将该对象的引用赋值给了变量。变量中存储的是对象的内存地址,而不是对象本身。

例如,下面的代码创建了一个字符串对象,并将其赋值给了两个不同的变量:

String str1 = "Hello";  
String str2 = "Hello";

实际上,这段代码在内存中创建了一个字符串对象,然后将该对象的引用分别赋值给了str1和str2。因此,str1和str2都指向同一个字符串对象。

当需要复制一个对象时,实际上需要复制该对象的内容。这可以通过使用clone()方法或copy constructor来实现。这些方法会创建一个新的对象,并将原始对象的值复制到新对象中。

例如,下面的代码使用clone()方法复制了一个字符串对象:

String str1 = "Hello";  
String str2 = str1.clone();

这段代码创建了两个不同的字符串对象,它们具有相同的值。

需要注意的是,对于可变的对象(例如数组或对象的字段),复制引用并不会复制对象的内容。因此,如果修改了一个对象的内容,另一个引用该对象的变量也会受到影响。这是因为它们都指向同一个对象。

为了避免这种情况,可以使用深拷贝(deep copy)来复制对象及其内容。深拷贝会创建一个新的对象,并将原始对象的所有字段复制到新对象中。这可以通过手动实现,或者使用库如Apache Commons Lang的SerializationUtils.clone()方法来实现。

比较对象- 2.9.3 -

比较对象通常需要使用equals()方法。equals()方法用于比较两个对象是否相等。默认情况下,equals()方法比较的是两个对象的引用,即它们是否指向内存中的同一个对象。但是,可以重写equals()方法来比较两个对象的内容是否相等。

例如,如果有一个Person类,其中包含name和age属性,可以重写equals()方法来比较两个Person对象的name和age属性是否相等:

public class Person {  
    private String name;  
    private int age;  
  
    public Person(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
  
    @Override  
    public boolean equals(Object obj) {  
        if (obj == this) {  
            return true;  
        }  
        if (!(obj instanceof Person)) {  
            return false;  
        }  
        Person other = (Person) obj;  
        return this.name.equals(other.name) && this.age == other.age;  
    }  
}

在上面的例子中,首先检查两个对象是否是同一个对象(即它们的引用是否相等),如果是,则返回true。然后,检查传入的对象是否是Person类型的,如果不是,则返回false。最后,将传入的对象转换为Person类型,并比较两个Person对象的name和age属性是否相等。如果相等,则返回true,否则返回false。

除了使用equals()方法外,还可以使用==操作符来比较对象的引用是否相等。但是,在比较对象的内容是否相等时,应该使用equals()方法。

包装类的装包和拆包转换- 2.9.4 -

Java的包装类是用来封装基本数据类型的对象,以便在面向对象编程中使用。每个包装类都对应一个基本数据类型,例如Integer对应int,Double对应double等。

装包(boxing)和拆包(unboxing)是Java中特有的术语,分别表示将基本数据类型转换为包装类对象,以及将包装类对象转换为基本数据类型。

装包(boxing):

int num = 10;  
Integer integerNum = Integer.valueOf(num); // 装包

拆包(unboxing):

Integer integerNum = Integer.valueOf(10);  
int num = integerNum; // 拆包

在上述例子中,装包是将int类型的变量num转换为Integer对象,而拆包则是将Integer对象integerNum转换为int类型的变量num。

值得注意的是,从Java 5开始,装包和拆包操作可以自动进行,无需显式调用valueOf()方法。例如:

int num = 10;  
Integer integerNum = num; // 自动装包  
  
Integer integerNum = Integer.valueOf(10);  
int num = integerNum; // 自动拆包

在上述例子中,编译器会自动进行装包和拆包操作,使得代码更加简洁易读。