先看一个长长的代码,其实很简单,就是使用不同的方法迭代Map,对值进行修改,只要遇到foreach就发现赋值看似成功,实则失败。
就想搞清楚为什么,不想直接从搜索引擎搜来别人的总结好的背下来。
1、问题的引出
1.1、测试的源代码
1: public static void main(String[] args) {
2: Map<String, String[]> map = new HashMap<String, String[]>();
3: map.put("key1", new String[] { "值" });
4: System.out.println();
5: System.out.println("#######第一次#######");
6: System.out.println("#######第一种迭代#######");
7: String[] values = null;
8: for (String str : map.keySet()) {
9: values = map.get(str);
10: System.out.println(values.hashCode());
11: for (int i = 0; i < values.length; i++) {
12: System.out.println(values[i].hashCode());
13: }
14: }
15: System.out.println();
16: System.out.println("#######第二次#######");
17: System.out.println("#######第二种迭代#######");
18: for (Entry<String, String[]> entry : map.entrySet()) {
19: values = entry.getValue();
20: System.out.println(values.hashCode());
21: for (String string : values) {
22: System.out.println(string.hashCode());
23: }
24: }
25: System.out.println();
26: System.out.println("#######第三次#######");
27: System.out.println("#######第一种迭代#######");
28: values = null;
29: for (String str : map.keySet()) {
30: values = map.get(str);
31: System.out.println(values.hashCode());
32: for (int i = 0; i < values.length; i++) {
33: System.out.println("旧:" + values[i].hashCode());
34: values[i] = values[i] + "_1";
35: System.out.println("新:" + values[i].hashCode());
36: }
37: }
38: System.out.println("#######第二种迭代#######");
39: for (Entry<String, String[]> entry : map.entrySet()) {
40: values = entry.getValue();
41: System.out.println(values.hashCode());
42: for (String string : values) {
43: System.out.println(string.hashCode() + "-->" + string);
44: }
45: }
46: System.out.println();
47: System.out.println("#######第四次#######");
48: System.out.println("#######第二种迭代#######");
49: values = null;
50: for (Entry<String, String[]> entry : map.entrySet()) {
51: values = entry.getValue();
52: System.out.println(values.hashCode());
53: for (String string : values) {
54: System.out.println("旧:" + string.hashCode());
55: string = string + "_2";
56: System.out.println("新:" + string.hashCode());
57: }
58: }
59: System.out.println("#######第一种迭代#######");
60: for (String str : map.keySet()) {
61: values = map.get(str);
62: System.out.println(values.hashCode());
63: for (int i = 0; i < values.length; i++) {
64: System.out.println(values[i].hashCode() + "-->" + values[i]);
65: }
66: }
67: System.out.println("#######第二种迭代#######");
68: for (Entry<String, String[]> entry : map.entrySet()) {
69: values = entry.getValue();
70: System.out.println(values.hashCode());
71: for (String string : values) {
72: System.out.println(string.hashCode() + "-->" + string);
73: }
74: }
75: System.out.println("值没有改变,还是指向原来的字符串位置");
76: System.out.println();
77: System.out.println("#######第五次#######");
78: System.out.println("#######第二种迭代#######");
79: values = null;
80: for (Entry<String, String[]> entry : map.entrySet()) {
81: values = entry.getValue();
82: System.out.println(values.hashCode());
83: for (int i = 0; i < values.length; i++) {
84: System.out.println("旧:" + values[i].hashCode());
85: values[i] = values[i] + "_3";
86: System.out.println("新:" + values[i].hashCode());
87: }
88: }
89: System.out.println("#######第一种迭代#######");
90: for (String str : map.keySet()) {
91: values = map.get(str);
92: System.out.println(values.hashCode());
93: for (int i = 0; i < values.length; i++) {
94: System.out.println(values[i].hashCode() + "-->" + values[i]);
95: }
96: }
97: System.out.println("#######第二种迭代#######");
98: for (Entry<String, String[]> entry : map.entrySet()) {
99: values = entry.getValue();
100: System.out.println(values.hashCode());
101: for (String string : values) {
102: System.out.println(string.hashCode() + "-->" + string);
103: }
104: }
105: System.out.println("实验结果就是:使用简单for遍历来赋值成功,使用高级for循环取出赋值失败");
106: }
1.2、运行结果
#######第一次#######
#######第一种迭代#######
1951510703
20540
#######第二次#######
#######第二种迭代#######
1951510703
20540
#######第三次#######
#######第一种迭代#######
1951510703
旧:20540
新:19741934
#######第二种迭代#######
1951510703
19741934-->值_1
#######第四次#######
#######第二种迭代#######
1951510703
旧:19741934
新:1792132385
#######第一种迭代#######
1951510703
19741934-->值_1
#######第二种迭代#######
1951510703
19741934-->值_1
值没有改变,还是指向原来的字符串位置
#######第五次#######
#######第二种迭代#######
1951510703
旧:19741934
新:1792132386
#######第一种迭代#######
1951510703
1792132386-->值_1_3
#######第二种迭代#######
1951510703
1792132386-->值_1_3
实验结果就是:使用简单for遍历来赋值成功,使用高级for循环取出赋值失败
实验结果就是:
使用简单for遍历来赋值成功,使用高级for循环取出赋值失败
二、问题
为什么它是只读的呢,编译器做了什么?高级for做了什么?
没有找到合适的理由,先认为高级for它是只读的吧。
三、问题的解决
熬了半宿,只是有一个猜测,那么怎么证实呢?
写一个最最简单的程序,使用foreach,先看字节码。
public class TestFor {
public static void main(String[] args) {
int a[] = new int[] { 1, 2, 3 };
for (int i : a) {
System.out.println(i);
}
}
}
程序简单的,都不用加注释了。
看字节码?javap看了半天没有很乱的,怎么办?
换工具,都是32位的,64位的墙外边不好拿到,怎么办?
安装虚拟机,装32为系统,非要看看里面是什么。
用工具打开一看还是一个乱啊,引用常量表,那个goto乱啊。
无意中看到了反编译的结果,哈哈,我要的东西在里面。
这正是我要的,证实了我的想法,这就是一个常引用啊,这里是常量赋值,就是只读的。
好,那再看看普通for循环
编译成字节码,体积较上一个代码还小了些。
再反编译
那么我们来看Java中的引用类型做了什么。
上源码
看反编译
果然是一个常引用。
四、总结
高级For在JDK 5.0开始引入,用其迭代代码简洁,但是要注意它取出的值是一个常变量,所以高级For循环可以用来遍历查询,不可修改当前取回的元素本身。
我在学习Java的时候,没有看到高级For的特点和缺点,方便的语法屏蔽了底层的实现,却不能让人了解幕后究竟是什么。
也许这个问题是很简单,是显而易见的,但是我就是不到黄河心不死,非要看个究竟。
时间: 2024-10-03 23:16:12