为何初始化长度为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中数组引用的理解误区,我们一步步拆解来看:
数组是引用类型
在Java里,数组是对象,Treet[] treets = new Treet[0];这行代码做了两件事:- 在堆内存中创建一个长度为0的
Treet数组对象 - 将这个对象的引用赋值给局部变量
treets
- 在堆内存中创建一个长度为0的
反序列化返回全新的数组对象
调用ois.readObject()时,它会根据序列化时保存的完整信息,在堆内存中新建一个长度为2的Treet数组对象,同时把之前序列化的两个Treet实例填充到这个新数组里。赋值操作替换了引用
执行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
相关产品推荐
相关产品推荐

