文章目录
一、前情引入:
即使你刚学习编程语言,你肯定知道,=是赋值,==是判断是否相等,比如下面的程序:
可见,==判断了a和b是相等的,
如下图,内存中其实是这样存储a和b的,在栈中,经过比较两个10,是相等的。
二、对于数组:
接下来,让我们来看看==号用在数组中是什么情况:
可以看到,数组c和e为真,但c和d假,为什么呢?
原来,数组名是对数组的引用,数组实际上是存放在堆中的,栈中只存着堆首的地址,
在栈中,经过比较,c和e相等,但c和d不相等。
三、对于字符串:
再来看看今天最需要解决的问题——字符串:
结果是不是有点奇怪呢?怎么只有s1=s2,t1和t2不等吗?
这里先补充一下常量池的概念。
内存中除了堆和栈,还有一个区域叫方法区,
它一部分可以存放类的信息,还有一部分就是常量池了。
ps: 《堆、栈、方法区、常量池的概念》https://www.cnblogs.com/xiaowangbangzhu/p/10366200.html
如下图:
s1和s2都指向常量池的同一个地址,所以相等,
但t1和t2强行在堆中new了空间,所以不相等。
四、equals()方法:
在学习字符串中,你一定用到了equals()方法,它的作用是比较两个字符串内容是否相等,
还是上面的例子:
这回,s1=s2,并且t1=t2了,
所以,对于字符串,equals()就是判断两个字符串的值是否相等。
但实际上,equals()写在Object类中,和==作用是一样的,都是比较引用是否相等,
boolean b = obj1.equals(obj2);
只不过,字符串类将equals()进行了重写,才有了上图的结果。
五、本文总结:
1.java中的==和equals():
①如果是基本类型,如int、double,==比较的是值;
②如果是数组对象,==比较地址是否相等;
③字符串用equals()函数来比较是否相等。
ps:如果需要比较两个数组内容是否相等,也可以用equals()方法:
具体用法如下:
但是,还需要考虑到数组的顺序哦,所以,应该先排序后比较,如下图:
2.创建字符串的方法
①用new构造方法:
参考文档中给出了许多构造方法,比如用字符数组:
char[] chars= {'a','b','c','d','e'};
String s0=new String(chars);
//s0输出abcde
String s1=new String(new char[] {'a','b','c'});
//s1输出abcde
String s2=new String(chars,1,2);
//s2输出bc
或者用字符串:
String s3=new String("Hello World");
//s3输出Hello World
②也可以直接赋值,这叫做字符串常量:
String s4="hello";
//s4输出hello
上边,我们已经把常用到的情况做了区分,但有时候,我们也想根据自己的意愿改写equals()方法,
如果你想更深一步了解,请让我们一起接着往下:
P.S1. 改写equal()方法(原理)
请看下面的程序:这个程序new了两个对象,并用equals()方法比较。
前面我们已经知道,equals()方法在Object类中,
如果我们创建了一个类,就会自动继承了Object类,也就可以直接使用equals()方法了。
那这个程序的运行结果是什么呢?答案是False,因为new了不同的区域。
我们按住Alt,用鼠标点开equals()函数,查看源码如下。
可见,源码写的是——this当前对象(也就是a1)==传入的参数对象(也就是a2),
因为new了两个区域,所以应该返回False。
接下来,我们在A类中对equals()进行重写,如下:
可以看到,经过重写后,我们改变了equals()方法,于是对继承来的Object类中的equals()方法进行了覆盖,结果返回了True。
把上边的例子改写的正式一点:
P.S2. eclipse开发工具equals()自动生成(实用)
上面是从原理上分析,但并不实用,这一段落,我们将举一个生动的例子,并介绍实用的eclipse编译器自动生成equals()方法。
下图,创建一个Student类,有四个属性,添加上构造函数和get、set方法:
在另外一个类中的main方法中调用,得到False:
如果想让程序更加贴近生活,学号一样,就返回True,
即让对象有一个(或多个)标识,通过这个标识,
用生成equals()函数和hashCode方法(不用手写,直接用eclipse工具生成)。
步骤如下:
谁是标识你就选谁:
现在,编译器就自动为我们添加了hashCode()与equals(),
hashCode()方法我们可以注释掉,只用equals()方法,
现在再去比较,返回就是True了:
P.S3. 对于字符串的连接的==(拓展)(StringBuilder)
我们知道,字符串是可以通过 “+” 连接的,请看下面的一些案例:
案例1:
s1使用了字符串常量拼接,s2直接字符串常量赋值,内存地址是一样的。
ps:原理解释: jvm对
String s1="abc"+"def"+"asde";
进行了优化,实际上的编码就是
String s1="abcdefasde";
我们对上述的说法进行验证:
首先编译得到StringTest.class文件,
再使用javap -v 对生成的class文件进行反编译,可以看到下图红色方框的内容,
再结合前文常量池的概念,就可以得出结论——True了。
案例2:
相信你也有这样的疑问——用s3进行拼接为什么会出现false呢?
同样的,我们还是用刚才的方法:
得到.class文件后进行反编译:
内容太多,我就截取一部分:
可以看到,这里用到了StringBuilder,而且用到了它的append方法,
即:
String s9=s3+"world";
实际上的编码是:
String s9=new StringBuilder(s3).append("world");
这就意味着new了新的对象,有了新的地址空间,内存地址自然也就不同了。
PS:StringBuffer 类:
创建一个StringBuffer:
StringBuffer sBuffer1 = new StringBuffer();//空字符串
StringBuffer sBuffer = new StringBuffer("我爱");//有初值的字符串
sBuffer.append("中国");
sBuffer.append("!");
System.out.println(sBuffer); //打印字符串
互相转化:
StringBuilder a = new StringBuilder("I love you");
String b = ""+a; //将StringBuilder类型转换成String类型
StringBuilder a = new StringBuilder(a); //将String类型转换为StringBuilder类型