最近公司在招新人,作为一个也仅仅工作不到一年的小白被老板安排打电话初步筛选刚毕业的初级java开发人员。对于我来说当然是一个挑战,也是一个锻炼自己的机会,为了不给公司和自己丢脸,认真的准备面试问题,其中我把这个问题放在了第一个,既是我长时间来没有彻彻底底搞明白的问题,也是一个自认为初学者刚开始容易弄混淆的问题。
堆、栈
首先在了解它们区别之前,应该多少的了解一些堆栈的概念,对深刻理解有好处。
首先我们编写的程序都是JVM帮我们编译运行的,运行的时候他会在内存模型中分配两个内存,一个堆内存,一个栈内存。
- 堆内存(heap):通常用来存放由new创建的对象和数组、成员变量的值,它们是编译时未知存储大小的,是运行时动态分配的内存大小,所以它可以任意的改变大小,Java的多态性正是由这种动态分配特性决定的。
- 栈内存(stack):通常用来存储基本数据类型的值、对象的引用(地址,指针)、成员变量的引用、局部变量的引用和值。
区别
有了一些堆栈的概念我们在来谈区别。
- 最主要的区别 "==" 是运算符,比较的是你"真正看到的东西(引用地址)","equals()" 是方法,比较的是"内容"(堆上真正的对象)。
我们先分析一下String 类重写过的equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
自己和自己 做== 肯定返回 true,然后将string 都转换成 char 类型的数组,再通过while循环,来计较每一位的char是否完全相同。如果没有重写过equals方法,那么它的作用跟"=="是完全一样的
举例
public class TestEquals {
class Dog {
String name;
String color;
public Dog () {
};
public Dog (String name, String color) {
this.name = name;
this.color = color;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Dog) {
Dog d = (Dog) obj;
return this.name.equals(d.name) && this.color.equals(d.color);
} else {
return false;
}
}
}
public static void main (String args[]) {
TestEquals testEquals = new TestEquals();
Dog dog1 = testEquals.new Dog("Tom","black");
Dog dog2 = testEquals.new Dog("Tom","black");
String a = new String("abc");
String b = new String ("abc");
String aa = "abc";
String bb = "abc";
Long l1 = 127L;
Long l2 = 127L;
Long l3 = 128L;
Long l4 = 128L;
System.out.println("dog1.equals(dog2): " + dog1.equals(dog2));
System.out.println("a.equals(b): " + a.equals(b));
System.out.println("a == b: " + (a==b));
System.out.println("aa == bb: " + (aa==bb));
System.out.println("l1 == l2: " + (l1 == l2));
System.out.println("l3 == l4: " + (l3 == l4));
}
dog1.equals(dog2): true
a.equals(b): true
a == b: false
aa == bb: true
l1 == l2: true
l3 == l4: false
- 第一个结果: true 我们可以看到在TestEquals类中有一个我自定义的内部类Dog,里边有两个属性 name 和 color ,在Dog类内部我自己重写了equals方法,即便dog1 和 dog2 在堆上是两个不同的对象,在栈上是不同的地址,也是相同的。
- 第二个结果:true 由于String帮我们重写了equals方法,所以它的内容也是相同的。
- 第三个结果 :false 因为 "==" 比较的是栈上的地址引用,它们通过new创建所以指向两个不同的对象,所以为false
- 第四个结果:true 这里String没有通过new创建而是直接="abc"那么,它在编译的时候会从常量池中寻找有没有叫做"abc"的常量值,如果有直接指向,如果没有则在常量池中创建一个"abc",下一次再有新的引用bb = "abc",也同样先去常量池中寻找,有的话直接指向,所以这里aa和bb都指向同一个常量池中的"abc"所以它们是相等的。
-结果四和五放在一起来讲,正常来讲基本数据类型的引用和值都是存在栈上的,它们都是字面值,编译初期就知道它的生命周期和内存大小,所以结果四没有疑问,可是为什么结果五就false了呢,其实这里不管是基本数据类型(int),还是它的包装类(Integer)在做==比较时,在[-128,128]之间的值是直接缓存在内存中的,也就是说这个范围内的值内存已经以数组的形式存储好了,如果找到直接返回引用给你了,如果超过这个范围,就要重新new一个引用,所以 结果5是false了。