解读王垠博客“⼀道Java⾯试题”
偶然拜读IT界知名⼤佬王垠⽼师的博客,发现⼀个有意思的题⽬:
1// 这段代码⾥⾯到底哪⼀⾏错了?为什么?
2// 原⽂:/blog-cn/2020/02/13/java-type-system
3public static void f() {
4    String[] a = new String[2];
5    Object[] b = a;
6    a[0] = "hi";
7    b[1] = Integer.valueOf(42);
字符串是什么类型的
8 }
虽然⼩菜才疏学浅,但本着学习交流的态度,写下此篇⽂章来分析⼀下这个问题。
⾸先我们要读懂每⼀⾏代码在做什么:
String[] a = new String[2]; 定义⼀个字符串类型的数组a,并初始化。
Object[] b = a; 定义⼀个对象类型的数组b,并将字符串类型数组a赋值给b。
a[0] = "hi"; 使⽤变量a访问数组中的第⼀个元素,赋值。
b[1] = Integer.valueOf(42); 使⽤变量b访问数组中的第⼆个元素,赋值。
只有简单的四⾏代码,相信读者都可以看的懂。
先不考虑太多,直接执⾏⼀下代码,编译通过,运⾏报错:java.lang.ArrayStoreException: java.lang.Integer。
错误提⽰我们第四⾏代码有问题,不可以将整型数据存储到数组b中,⽽b是⼀个Object类型的数组,编译通过,却⽆法赋值。
分析⼀下原因,数组b的引⽤指向数组a,我们操作数组b,实际在内存中,访问的应该是数组a,⽽数组a是⼀个字符串类型数组,整个过程中,并不存在Object类型的数组,仅有⼀个字符串类型的数组在内存中被创建,如图:
变量a和变量b只不过是门⾯,通过这两道门,到达的是同⼀个房间。只不过a门只允许String类型通过,⽽b门没有任何限制。
因此,假如我们写下a[0] = Integer.valueOf(42);,编译器⽴刻会发现错误,提⽰类型错误,⽽b[1] = Integer.valueOf(42);的写法是符合规则的,但由于实际数据结构是String数组,所以运⾏肯定⽆法通过。
为什么会这样?出现这种问题的根本原因,在于Object[] b = a;,严格来说,这种语法是错误的,但是在JDK规范中却被认可。
为什么说是错误的?⾯向对象中的继承我们再熟悉不过了,⼦类完全具有⽗类的能⼒,⼦类可以退化成为⽗类。
单说String的确是Object的⼦类,完全符合规则,但数组是另⼀回事,本例中String数组仅仅能容纳String类型的元素,⽽Object数组可以容纳任意类型的元素,String数组并⾮完全具有Object数组的能⼒。
从另⼀个⾓度看,⽆论是String[] a还是Object[] b,这两种写法中的变量a和变量b,仅仅能决定指针的指向(引⽤哪个具体的数组),⽽⽆法控制数组内的元素,只能整体操作,⽽数组必然要涉及某个元素的部分操作,这就造成数组内部数据结构的“逸出”,必然会出现问题。
综上,数组之间的抽象是错误的,数组之间没有直接的继承的能⼒,不属于⾯向对象继承的讨论范畴。
实际编写代码时,不必过分纠结这个问题,尽量不使⽤这种危险的操作,⽽是⽤更加优雅的⽅式去实现:
1// 这样就能很好的发现错误,避免给⾃⼰挖坑
2public static void f() {
3    String[] a = new String[2];
4    Object b = a;  //数组本⾝也是对象
5    a[0] = "hi";
6    ((String[]) b)[1] = Integer.valueOf(42);
7 }

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。