黑马程序员技术交流社区

标题: == 问题求助 [打印本页]

作者: barlay    时间: 2013-12-12 21:45
标题: == 问题求助
本帖最后由 barlay 于 2013-12-13 00:51 编辑

public class StringTest{
public static void main(String[] args) {
String a = "abc";
String b = new String("abc");
System.out.println(a == b);
int c = 1;
Integer d = new Integer(1);
//c = 4;
System.out.println(c == d);
}
}
== 不是比较的地址吗?第一个返回false,第二个为啥返回true,原生类型与引用类型比。


作者: 松子    时间: 2013-12-12 21:53
本帖最后由 松子 于 2013-12-12 21:57 编辑

string 是引用类型,用==进行比较时,比较他们在内存中的地址值,a和b的地址值当然是不同的,所以是false;
int 值类型,比较的是二者的值,c和d的值都是1,结果为true。

作者: 杨增坤    时间: 2013-12-12 21:55
这是包装类的特性,String不是包装类,Integer是包装类!
作者: 潘金锋    时间: 2013-12-12 22:00
这个毕老师的视频里有解释,对于引用变量,==比较的是两个对象的内存地址值,如果两个引用指向同一个对象,则铁定它们是相等的,谁会不等于自己呢?
当Java程序中直接使用形如"abc"的字符串直接量时,JVM将会使用常量池来管理这些字符串;当使用new String("abc")时,JVM会先使用常量池来管理"abc"直接量,再调用String类的构造方法来创建一个新的String对象,新创建的String对象被保存在堆内存中。换句话说,new String("abc")一共产生了两个对象。所以两者在内存中的位置是不一样的,通过==比较,结果即为false。
d与c比较时,d被自动拆箱了,意即它不再是一个对象而变成了一个整型数值。此时两者均为基本类型变量,只要两个变量的值相等,结果就返回true。
哎,Java挺闹心啊。

作者: 風諾    时间: 2013-12-12 22:07
视频讲到Integer有个自动拆包封包特性
第一个new String("abc")就是一个新对象,String s = "abc";只是赋给地址值
你可以试试String s = "abc";String s1 = "abc";比较s和s1
作者: 郭涛    时间: 2013-12-12 22:09
Integer是包装类
包装类有一个特性:
基本数据类型和包装类之间可以自动的相互转换,这个就是自动装箱和自动拆箱
例子:
Integer in = 60; 自动装箱
int x = in + 10; 自动拆箱
作者: Kyle    时间: 2013-12-12 23:07
本帖最后由 Kyle 于 2013-12-13 01:02 编辑

String a ="abc"                      这种创建方式是在常量池中创建了一个常量abc,然后a作为引用变量指向它。
String b = new String("abc");   这种创建方式是在堆内存中创建了一个对象“abc”,然后b作为引用变量指向它。
所以a和b所保存的地址值不同,==的结果返回false。

int c = 1                                   是在栈内存中保存了一个局部基本型变量。
Integer d = new Integer(1);    当d和c比较的时候,因为c是基本型变量,d会自动拆箱与c比较,所以返回的是true。


作者: 半夜雨半夜雨    时间: 2013-12-12 23:13
简单地说,“==”,用于比较原生类型,当然也可以比较自动装箱拆箱的Integer包装类型,还可以比较在引用类型在栈内存中的地址值
“equals”不可以比较原生类型,比较引用类型时,实际上比较的是引用变量指向的对象的内容。
作者: icyyaww    时间: 2013-12-12 23:33
String: 是地址比较所以是false。
integer:①无论如何,Integer a=1;与Integer b = new Integer(1);不会相等。不会经历拆箱过程,b 的引用指向堆,而 a 指向专门存放他的内存(常量池),他们的内存地址不一样,所以为false
  ②两个都是非new出来的Integer,Integer a =1; Integer b = 1;如果数在-128到127之间,则是true,否则为false
  java在编译Integer a = 128的时候,被翻译成-> Integer a = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存
  ③两个都是new出来的new Integer(1),new Integer(1),都为false
  ④int和integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比
作者: barlay    时间: 2013-12-12 23:45
Kyle 发表于 2013-12-12 23:07
String a ="abc"                      这种创建方式是在常量池中创建了一个常量abc,然后a作为引用变量指 ...

int c = 1;是在栈中存的1吧
楼下说的挺对的,“==”,用于比较原生类型,当然也可以比较自动装箱拆箱的Integer包装类型,还可以比较在引用类型在栈内存中的地址值
当原生类型和包装类型比较是,包装类型自动拆箱
作者: barlay    时间: 2013-12-12 23:51
icyyaww 发表于 2013-12-12 23:33
String: 是地址比较所以是false。
integer:①无论如何,Integer a=1;与Integer b = new Integer(1); ...

分析的太详细了,非常感谢!
另外有一个问题:你确定Integer a=1;中a 指向专门存放 1 的内存(常量池),而不是存在栈中?int a = 1的话a肯定存在栈中啊。
作者: Kyle    时间: 2013-12-12 23:59
barlay 发表于 2013-12-12 23:45
int c = 1;是在栈中存的1吧
楼下说的挺对的,“==”,用于比较原生类型,当然也可以比较自动装箱拆箱的I ...

嗯,你说的对。 是我没回答完善。

Integer a = 127;
Integer b = 127;
System.out.println(a ==  b);

这种情况就是在常量池中创建127。

Integer a = 128;
Integer b = 128;
System.out.println(a ==  b);

这种情况是在堆内存中创建两个对象。
作者: barlay    时间: 2013-12-13 00:06
Kyle 发表于 2013-12-12 23:59
嗯,你说的对。 是我没回答完善。

Integer a = 127;

还是不对,请参见icyyaww帖子第二条:java在编译Integer a = 128的时候,被翻译成-> Integer a = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存
是进行缓存。不是放到常量池中好不好,更不要说在常量池中创建了。
作者: icyyaww    时间: 2013-12-13 00:06
barlay 发表于 2013-12-12 23:51
分析的太详细了,非常感谢!
另外有一个问题:你确定Integer a=1;中a 指向专门存放 1 的内存(常量池 ...

确定。:lol:lol:lol
作者: Kyle    时间: 2013-12-13 00:15
barlay 发表于 2013-12-13 00:06
还是不对,请参见icyyaww帖子第二条:java在编译Integer a = 128的时候,被翻译成-> Integer a = Integer. ...

是的,因为128是特殊情况。
Integer a = 127;
这是因为整数类型在常量池中有一定的范围,范围是-128到127之间,也就是说在这个范围内是不会去堆内存创建对象,所以127是在常量池中创建的。
Integer b = 128;
则是超出了127这个数值,所以是在堆内存中创建对象的,缓存也属于在堆内存,概念跟StringBuffer的缓存相同。
若我说的不对,欢迎指正,共同学习!

作者: barlay    时间: 2013-12-13 00:24
icyyaww 发表于 2013-12-13 00:06
确定。

我认为是这样的,Integer把-128到127的值缓存起来了,Integer a=1;实际上是把1的缓存地址赋给了a,就是说a中存的是1这个值的地址,而不是1本身,因为a是局部变量,所以a的值存在栈中。和b进行比较的时候,因为b中存的是一个堆中对象的地址,所以和a中存的地址不同,为false。
你说的是对的,我原先理解有点偏差。
分析的太详细了,你是怎么做到的?佩服!
收工。
作者: barlay    时间: 2013-12-13 00:43
Kyle 发表于 2013-12-13 00:15
是的,因为128是特殊情况。
Integer a = 127;
这是因为整数类型在常量池中有一定的范围,范围是-128到127 ...

不要再提常量池了,本来就没有常量池,非得造出这么一个概念来。你们所说的常量池,不就是一块只能读不能写的内存区域么!只能读不能写 你告诉我怎么创建!
二进制文件中存放的有code和data数据,code和data分别被加载到不同的地方,由于data中存放的是程序运行时要用到的数据,这些数据在编写代码时就已经确定了,所以是不可变的,被加载到只读内存,这应该就是你们所说的常量池了吧。
应该是这样的,如有别的想法可以跟帖回复。
作者: Kyle    时间: 2013-12-13 00:44
icyyaww 发表于 2013-12-12 23:33
String: 是地址比较所以是false。
integer:①无论如何,Integer a=1;与Integer b = new Integer(1); ...

barlay的问题结束了。 我的问题来了。
我的观点是非new的情况,Integer a = 1;
只要数值在-128~127之间,这个数就会在常量池中创建。
而你所说的第二点:
  ②两个都是非new出来的Integer,Integer a =1; Integer b = 1;如果数在-128到127之间,则是true,否则为false
  java在编译Integer a = 128的时候,被翻译成-> Integer a = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存

即Integer a = 128才会被翻译成valueOf不是吗? 最后一句话反而说对-128~127之间的数进行缓存,这不是自相矛盾了吗?
求解惑一下。
作者: barlay    时间: 2013-12-13 00:50
本帖最后由 barlay 于 2013-12-13 00:53 编辑
Kyle 发表于 2013-12-13 00:44
barlay的问题结束了。 我的问题来了。
我的观点是非new的情况,Integer a = 1;
只要数值在-128~127之间 ...


你理解有误,正确的理解是:
valueOf()函数会对-128到127之间的数进行缓存,所以直接返回缓存地址
128不在缓存中,所以新建对象,返回新建对象的地址
不管要赋值的数是多少,都执行valueOf()函数。
圆满搞定,理解又加深了一步,论坛牛人很多啊,收工!
作者: Kyle    时间: 2013-12-13 00:55
barlay 发表于 2013-12-13 00:43
不要再提常量池了,本来就没有常量池,非得造出这么一个概念来。你们所说的常量池,不就是一块只能读不能 ...

你对常量池的概念理解有偏差,不要激动,冷静分析问题。
Java当中是有常量池的,这个概念我可以100%的跟你保证。
只能读不能写的理解也不对。
常量池里的东西是一旦创建了就不能改变,出现不一样的就再创建一个。
比如
String a = "a";         //这是在常量池当中创建了一个常量“a”
String a =“ab”;  //这并不是在a的基础上加上字符b。 而是舍弃了常量池中的“a”,重新创建了一个常量“ab”。 “a”还是在常量池里。
String c ="a";         //c变量指向了一开始就创建出来的"a"

作者: barlay    时间: 2013-12-13 20:35
Kyle 发表于 2013-12-13 00:55
你对常量池的概念理解有偏差,不要激动,冷静分析问题。
Java当中是有常量池的,这个概念我可以100%的跟 ...

你是按照语句执行顺序一步一步分析的,但实际情况是这样的:
1、Java源文件被编译器编译成class字节码文件,在编译的过程中,编译器把所遇到的常量都统一存储于字节码文件中的data段中;
2、程序执行时,字节码文件被加载到内存中,这时data段中的内容被加载到只能读不能写的内存区域,所以当字节码文件被解释执行时,肯定是能在这个区域中找到所有常量的。
总结:
String a = "a";         //将常量区的“a”的地址赋给 a
String a =“ab”;  //将常量区的“ab”的地址赋给 a
String c ="a";         //将常量区的“a”的地址赋给 c
作者: 楞个里格朗    时间: 2013-12-13 22:25
在张孝祥老师的视频中有讲到一个自动拆箱和自动装箱原理,对于在128到-127之间常用的integer封装对象,由于经常使用,java就赋予了一个自动转换功能,当需要和int类型的变量进行比较的时候,就会自动拆箱转换成int类型的局部变量,但是超出这个范围是不可以的,比如:
nt c = 200;
Integer d = new Integer(200);
System.out.println(c == d);//此时,打印为false。
作者: Kyle    时间: 2013-12-13 23:18
barlay 发表于 2013-12-13 20:35
你是按照语句执行顺序一步一步分析的,但实际情况是这样的:
1、Java源文件被编译器编译成class字节码文 ...

加载class字节码的内存区域叫做方法区,常量池是在方法区内的。
只能读不能写这个解释我不知道你是从哪里查来的,这解释是肯定有问题的。
若按照你的说法:
我们知道,被static修饰过的成员变量是随着class的加载而加载的,它也存于方法区中,
很显然,static修饰过的成员变量当中的值是可以修改的,而且一旦修改就会被其他对象共享。
你觉得你的只能读不能写的解释能说通吗?

另,上述Integer包装类的问题,关于-128~127的整型数值是存在常量池当中的概念你理解了吗?
作者: barlay    时间: 2013-12-13 23:36
Kyle 发表于 2013-12-13 23:18
加载class字节码的内存区域叫做方法区,常量池是在方法区内的。
只能读不能写这个解释我不知道你是从哪里 ...

方法区中应该包括:
1、只可读不可写的常量区;
2、可读可写的静态区
3、可被各个类对象共享的静态方法区
4、和每个类对象相关的代码区
大概是这个样子的,也许有误,这得深入JVM虚拟机了,功力不够,还要慢慢积累。这个问题留待解决,OK?
作者: Kyle    时间: 2013-12-13 23:44
功力大成之日记得分享下经验并@我一下
共同学习 {:soso_e144:}
作者: barlay    时间: 2013-12-13 23:49
Kyle 发表于 2013-12-13 23:44
功力大成之日记得分享下经验并@我一下
共同学习

和你聊得很愉快啊,加个好友吧,O(∩_∩)O哈哈~




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2