Java中的数据类型分为两大类,基本数据类型和引用数据类型:
基础数据类型
就是常用的:
①整数类型:long、int、short、byte
②浮点类型:float、double
③字符类型:char
④布尔类型:boolean
除了这些 其他的类、 接口类型、 数组类型、 枚举类型、 注解类型、 字符串型都属于
引用数据类型
它们最本质的区别在于:
存储位置
基础数据类型直接存在栈里面, 而引用其具体内容都是存放在堆中的,而栈中存放的是其具体内容所在内存的地址
(类似于房间号存在栈里面,通过房间号 去具体房间里面找人)
理解了这一层就可以理解这个经常出现的问题了:
==和 equals 的区别
对于基本数据类型来说,==比较的是值。对于引用数据类型来说,==比较的是对象的内存地址。
equals() 作用不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类。
注意:
equals() 方法存在两种使用情况:
类没有覆盖 equals()方法 :通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object类equals()方法。
类覆盖了 equals()方法 :一般我们都覆盖 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。
···
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
···
说明:
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。
再看上面的代码:
第一个好理解,他们是相同的。因为在string是被final修饰的,值是不可改变的,相同的字符串在内存中只会存。一份,所以a和b指向的是同一个对象;
第二个为false,(对象的内存地址不同 此时a和b指向不同的对象)
第三个为true,因为String 中的 equals 方法是被重写过的,因为 Object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
为什么重写 equals 时必须重写 hashCode 方法?
如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。
但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。
这里拓展一下:我们经常用的数据结构 set 它是不允许重复的 当你把相同的对象或者说元素加进去的时候 会失败,那它怎么去检测是否重复呢:随便来个例子比如HashSet
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。