You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何初始化长度为0的数组反序列化时未抛出ArrayIndexOutOfBoundsException?

为什么反序列化时给初始长度为0的数组赋值不会抛出ArrayIndexOutOfBoundsException?

我在Treets类的load()方法中声明了一个长度为0的Treet对象数组,但反序列化时把readObject()的结果赋值给了这个数组。之前我序列化了2个对象,反序列化也得到了2个对象,为什么没有抛出ArrayIndexOutOfBoundsException?毕竟看起来像是把2个对象放进了初始长度为0的数组里?

Treet.java

import java.util.Date;
import java.io.Serializable;
public class Treet implements Comparable<Treet>, Serializable {
 private String mAuthor;
 private String mDescription;
 private Date mCreationDate;
 Treet(String author, String description, Date creationDate) {
 mAuthor = author;
 mDescription = description;
 mCreationDate = creationDate;
 }
 public Date getCreationDate() {
 return mCreationDate;
 }
 @Override
 public String toString() {
 return String.format("Treet by %s: \"%s\" on %s", mAuthor, mDescription, mCreationDate);
 }
 @Override
 public int compareTo(Treet anotherTreet) {
 if(equals(anotherTreet)) {
 return 0;
 }
 return mCreationDate.compareTo(anotherTreet.getCreationDate());
 }
}

Treets.java

import java.io.*;
public class Treets {
 public static void save(Treet[] treets) {
 try(
 FileOutputStream fos = new FileOutputStream("treets.ser");
 ObjectOutputStream oos = new ObjectOutputStream(fos);
 ) {
 oos.writeObject(treets);
 } catch(IOException ioe) {
 System.out.println(ioe.getMessage());
 }
 }
 public static Treet[] load() {
 Treet[] treets = new Treet[0];
 try(
 FileInputStream fis = new FileInputStream("treets.ser");
 ObjectInputStream ois = new ObjectInputStream(fis);
 ) {
 treets = (Treet[])ois.readObject();
 } catch(IOException ioe) {
 System.out.println(ioe.getMessage());
 } catch(ClassNotFoundException cnfe) {
 System.out.println(cnfe.getMessage());
 }
 return treets;
 }
}

Main.java

import java.util.Date;
import java.util.Arrays;
public class Main {
 public static void main(String[] args) {
 Treet treetOne = new Treet("John", "This is treet one", new Date());
 Treet treetTwo = new Treet("Jane", "This is treet two", new Date(1526074720000L));
 Treet[] treets = {treetOne, treetTwo};
 Arrays.sort(treets);
 Treets.save(treets);
 Treet[] loadedTreets = Treets.load();
 for(Treet treet : loadedTreets) {
 System.out.println(treet.toString());
 }
 }
}

这其实是对Java中数组引用的理解误区,我们一步步拆解来看:

  1. 数组是引用类型
    在Java里,数组是对象,Treet[] treets = new Treet[0];这行代码做了两件事:

    • 在堆内存中创建一个长度为0的Treet数组对象
    • 将这个对象的引用赋值给局部变量treets
  2. 反序列化返回全新的数组对象
    调用ois.readObject()时,它会根据序列化时保存的完整信息,在堆内存中新建一个长度为2的Treet数组对象,同时把之前序列化的两个Treet实例填充到这个新数组里。

  3. 赋值操作替换了引用
    执行treets = (Treet[])ois.readObject();时,你并不是把2个对象塞进原来那个长度为0的数组,而是把treets变量的引用,从原来的长度0数组,切换指向了反序列化出来的那个长度为2的新数组。原来的长度0数组因为没有任何引用指向它,后续会被垃圾回收器自动回收。

简单来说:你从来没有往初始的长度0数组里添加任何元素,只是替换了treets变量指向的数组对象,自然不会触发数组越界异常。

如果想直观验证这一点,可以在load()方法中添加哈希码打印:

public static Treet[] load() {
    Treet[] treets = new Treet[0];
    System.out.println("初始数组哈希码:" + treets.hashCode());
    try(
        FileInputStream fis = new FileInputStream("treets.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
    ) {
        treets = (Treet[])ois.readObject();
        System.out.println("反序列化后数组哈希码:" + treets.hashCode());
    } catch(IOException ioe) {
        System.out.println(ioe.getMessage());
    } catch(ClassNotFoundException cnfe) {
        System.out.println(cnfe.getMessage());
    }
    return treets;
}

运行后你会看到两个哈希码完全不同,这就证明它们是两个完全独立的数组对象。

内容的提问来源于stack exchange,提问作者user7418448

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:21:02