目录
一、为什么重写equals()一定要重写hashCode()
一、为什么重写equals()一定要重写hashCode()
equals()方法:作用:用来比较该类的两个对象是否相等
实现:equals未被重写就直接进行引用比较
public boolean equals(Object obj) {
return (this == obj);
}
public class TestDemo {
public static void main(String[] args) {
Person p1 = new Person("阿伦");
Person p2 = new Person("阿伦");
System.out.println(p1.equals(p2));
}
static class Person {
public Person(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
}
}
输出为true
可以看到重写了equals,只要类型相同,name相同,就认定两个对象是同一个,而不是默认用内存地址比对。
为什么要重写hashCode?
根据hashCode的key是唯一的,去重的,我们打印上边代码,通过两个相同应该就是同一个对象
public static void main(String[] args) {
Person p1 = new Person("阿伦");
Person p2 = new Person("阿伦");
Map<Person, String> map = new HashMap<>();
map.put(p1, p1.getName());
map.put(p2, p2.getName());
System.out.println("map长度:" + map.size());
map.forEach((key, value) -> {
System.out.println(key.getName());
});
}
原因:hashcode的内部是一个数组,但是它的查找速度非常快,因为hashmap对key进行hash后,得到一个整数值
我们没有重写hashcode,虽然equals以两个对象name值是否相同,但是hashmao存值的时候是通过hashcode进行计算的,所以上面的两个值返回的hashcode是不同的
所以我们要把hashcode 的计算方法改为name计算:
public class TestDemo {
public static void main(String[] args) {
Person p1 = new Person("阿伦");
Person p2 = new Person("阿伦");
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
Map<Person, String> map = new HashMap<>();
map.put(p1, p1.getName());
map.put(p2, p2.getName());
map.get(p1);
System.out.println("map长度:" + map.size());
map.forEach((key, value) -> {
System.out.println(key.getName());
});
}
static class Person {
public Person(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
}
总结:根据业务重写equals后,一定要将hashcode用相同规则做hash,防止在一些需要用到对象的hashcode的地方造成问题。
重写equals和hashcode的原则:
- 自反性:x.equals(x)==true,自己调用自己相等
- 对称性:x.equals(y)==y.equals(x),两个对象结果应该一样
- 传递性:如果x.equals(y) == true y.equals(z) == true 则 x.equals(z) == true,x和y相等,y和z相等,则x和z相等
- 一致性:如果x对象和y对象有成员变量num1和num2,其中重写了equals方法,只有num1参加运算,修改num2的值不影响结果。
二、==和equals比较的区别
- equals()方法比较的是两个对象值 而==比较的两个引用是否指向同一个对象 有些同学要问了 为什么直接=字符串和new()字符串难道不同吗? 对的 第二点就是new和=的区别
- 用=时 会在字符常量串常量池中创建 如 string s="123"; 字符串常量池不属于堆也不属于栈 这样就会在字符串常量池创建“123”(如果字符串常量池中已经有“123” 就把s引用指向它)。再创建一个string s1=“123”;会把s1的引用指向已经存在的“123”;而不会再创建一个 这样s和s1的引用都是一样的 用==也可以判断出来用new()时 不管字符串常量池中有没有 都到从堆内存中开辟一块空间存放 new一次 开辟一次 所以 String str1=new String("str"); String str2=new String ("str");是不同的两块空间 所以引用也就不相同的
- ==只会判断两个引用是否指向同一个对象(也就是同一块内存地址) 所以这时候用==是false
结论:
- ==用于比较两个引用是否指向同一个对象 equals用于比较两个对象是否相等
- 由于字符串特殊 在字符串常量池中的字符串可以使用==也可以使用equals 推荐不管哪种方式 只要是比较字符串就用equals
三、为啥有时会出现4.0-3.6等于0.000001
浮点数采用二进制数表示,而在二进制系统中无法精确表示分数1/10
就好像十进制无法精确表示分数1/3一样
0.1不能写成1/(2^n)的和的形式
一般精确用:biginteger 和 bigdecimal 这两个类。
四、final关键字作用
- 修饰一个引用:
- 引用为常量,该值无法改变
- 引用为数组,该对象本身属性能改变,但指向的地址不能修改
- 引用的类成员必须赋值
- 修饰方法:
- 最终方法,无法没重写
- 修饰类:
- 最终类,无法被继承