发布于2020-11-19 20:59 阅读(1590) 评论(0) 点赞(27) 收藏(4)
偶然拜读IT界知名大佬王垠老师的博客,发现一个有意思的题目:
1 // 这段代码里面到底哪一行错了?为什么? 2 // 原文:http://www.yinwang.org/blog-cn/2020/02/13/java-type-system 3 public 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 // 这样就能很好的发现错误,避免给自己挖坑 2 public 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 }
作者:java小王子
链接:http://www.javaheidong.com/blog/article/1369/38715ad68837b5a31612/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!