- 浏览: 1135092 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (411)
- Java Foundation (41)
- AI/机器学习/数据挖掘/模式识别/自然语言处理/信息检索 (2)
- 云计算/NoSQL/数据分析 (11)
- Linux (13)
- Open Source (12)
- J2EE (52)
- Data Structures (4)
- other (10)
- Dev Error (41)
- Ajax/JS/JSP/HTML5 (47)
- Oracle (68)
- FLEX (19)
- Tools (19)
- 设计模式 (4)
- Database (12)
- SQL Server (9)
- 例子程序 (4)
- mysql (2)
- Web Services (4)
- 面试 (8)
- 嵌入式/移动开发 (18)
- 软件工程/UML (15)
- C/C++ (7)
- 架构Architecture/分布式Distributed (1)
最新评论
-
a535114641:
LZ你好, 用了这个方法后子页面里的JS方法就全不能用了呀
页面局部刷新的两种方式:form+iframe 和 ajax -
di1984HIT:
学习了,真不错,做个记号啊
Machine Learning -
赵师傅临死前:
我一台老机器,myeclipse9 + FB3.5 可以正常使 ...
myeclipse 10 安装 flash builder 4.6 -
Wu_Jiang:
触发时间在将来的某个时间 但是第一次触发的时间超出了失效时间, ...
Based on configured schedule, the given trigger will never fire. -
cylove007:
找了好久,顶你
Editable Select 可编辑select
未完 Java各种比较 : == | equals | compareTo | compare | instanceof
- 博客分类:
- Java Foundation
Equality Operator == :
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.21
一 基本数字类型之间、基本数字类型和其包装类对象之间使用 “==”,比较的是它们的数字值。
引用
称为 Numerical Equality Operator。具体点说:
如果参与==的两个操作数都是基本数字类型,或者一个是基本数字类型一个是基本数字类型的包装类时,做的是两个操作数的数字值的比较:有包装类,则Unboxing;Unboxing完后若存在基本类型不匹配,则Binary Numeric Promotion,直到两个操作数类型一致后,再做数字值的比较。
二 boolean与boolean、boolean与Boolean之间使用 “==”,比较的是它们的布尔值。
如果参与==的两个操作数都是基本数字类型,或者一个是基本数字类型一个是基本数字类型的包装类时,做的是两个操作数的数字值的比较:有包装类,则Unboxing;Unboxing完后若存在基本类型不匹配,则Binary Numeric Promotion,直到两个操作数类型一致后,再做数字值的比较。
int i = 2; double d = 2; System.out.println(i == d); //true System.out.println(i == new Float(2)); //true System.out.println(d == new Byte((byte)2)); //true
三 两个引用类型之间使用 “==”,比较的是他们是否指向同一个对象。
或者表述为:两个对象之间使用 “==”,比较的是这两个对象的内存地址值。
注意:
1 当通过Autoboxing,而不是new的方式创建包装类 Byte / Short / Integer / Long / Character / Boolean 的实例时,由于Java对整数包装类常用区间上包装类实例的缓存和对Boolean取值的常量化,将会导致出现一些意想不到的结果:
http://wuaner.iteye.com/blog/1668172
equals(Object obj):
== 做对象比较,得出“相等”的结论所表达的是“左右两边就是完完全全的同一个对象”。但在很多场景下,这种严格意义上的“对象相等”并不是我们想要的,我们更加期望通过对象的状态来区别它们是否相等,如用户注册与登录时“邮箱地址相等且登录名相同的用户,就是同一个用户”。这样的场景在实际中比比皆是,于是Java为Object类提供了equals()方法,让你可以通过重写它,来定制自己认可的“相等”。
equals()方法在Object类中的默认实现,是完全等价于 == 的。
Java也给出了重写equals方法的五条约定;这些约定确实是很有必要的,为了满足这些约定,你应该通过测试来检验自己的equals方法,以使它足够健壮。其中的自反性和非空性(For any non-null reference value x, x.equals(null) should return false)还好说,另外三个对称性、传递性和一致性需要在重写时着重检验。
equals() 和 hashcode()的关系 - 为什么重写equals()必须重写hashcode()? (Bloch,Effective Java 2nd)
引用
JDK src中对对象hashcode()方法产生的hash code,有以下约定:
1 只要对象的equals()方法做比较所用的状态(字段)没有被修改,那么对该对象调用多次hashcode()方法都应该始终返回同一个hash code;
2 equals()方法比较结果为相等的两个对象,他们的hashcode()方法返回的hash code必须相同;
3 equals()方法比较结果为不相等的两个对象,不要求你必须为他们产生不相同的hash code;但程序员应该明白为不相等的对象产生不相同的hash code可以提高基于散列表的容器类(HashMap/Hashtable/HashSet)的性能。
首先hashcode()方法返回的int类型hash code是什么,干什么用的?
看名便知,对象的hash code是与散列表(Hash Table, http://wuaner.iteye.com/blog/553007)有关的。散列表插入删除快,以及常数级的快速查找等优点,使java多个容器类都是用了散列表作为其内部的数据结构实现方式,如HashMap/Hashtable/HashSet etc。Java里对象的hash code,正是散列表相关概念里的“关键字”(key)。记录在散列表中的存储地址address,正是通过散列函数H(key)产生的。
约定 1 告诉我们:
equals()方法和hashcode()方法应该是基于对象相同的状态字段;对不可变类(immutable class,实例不能被修改的类),如Java中的Integer、String、BigDecimal等,他们的实例对象的状态永远不会变,重写equals和hashcode相对容易;对可变类(mutable class,状态字段会在使用中被修改),我们应该记住:不要让equals和hashcode方法依赖于那些变化的状态(字段)。
为什么重写equals()必须重写hashcode()?
原因源自JDK的要求: equals()相等的对象其hash code必须相同。
未重写hashcode()的话,Object类的hashcode()方法返回的hash code,是对象的内存地址通过一定方式转换成的integer,你可以通过System.identityHashCode(obj)取得这个默认的hash code;这种机制保证了不互相 == 的对象,其hash code一定不相同(As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects。但这句话也不完全正确,默认hash code也是存在相同的可能的,参见:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6321873 )。那么,状态上符合equals()相等的两个不同对象,其hash code也将是不同的。所以,我们应该在重写equals时,也必须重写hashcode()。
为什么equals()相等的对象其hash code必须相同?
1 举一个反例:equals相等的对象,其hash code不等,会出现什么后果那?我们知道,对象在散列桶(hash buckets)中的位置(address或说index)是通过hashcode经散列函数计算出来的;equals相等,hash code不相同的对象,则他们会被散列到不同的bucket中。
2 Set接口元素不能重复、Map接口作为key的对象不能重复,这个"重复"的判断依据是通过调用equals()方法来判断的。但同时,这些以散列表为底层实现的容器类,为了性能的需要,缓存了对象的hash code值(确切说是hash code经过second hash后的值),并将这个缓存值和equals一起,也作为对象重复的判断依据。那么,如果两个对象equals相等、hash code却不同,则会出现两个对象被重复装入容器的问题;且你想通过一个对象获取容器中一个equals相等(但hash code不同)的对象时,是无法获取到的。
为什么equals()不相等的对象其hash code应该尽可能地不相同?
对象的hash code对应散列表概念里的“关键字”。既然是“关键字”,关键字的选取策略,遵循的就是唯一性原则:不同记录,其关键字不应该相同。不同的关键字尚且有冲突的可能(参见散列表冲突的定义),关键字本身都存在相同的话冲突更是严重,最终导致散列表性能的大幅下降。
举一个极端的例子:假设某类的所有对象,其hash code都是一个固定的、完全相同的整数,则经过散列函数H(hash code)得到的存储地址address则也将自始至终都是同一个,导致散列表退化为一个彻头彻尾的链表,丧失其快速查找定位的优点。
通过以上分析看的出来,如果你重写了类的equals方法,并试图将该类的对象放入基于散列表的容器类(作为Set元素,或作为Map的key)的话,则你也必须重写它的hashcode方法!!其他时候,hashcode方法重不重写其实关系不大。但作为一个好的习惯,你应该在重写equals方法的同时去重写hashcode方法,保证equals相等的对象其hash code也相等 (万一这个类的对象被别人放入基于散列表的容器类,也就不会出问题了)。
重写了equals&hashcode方法的JKD类有:
String:串一样的String对象都equals相等、有相同的hash code;
所有的基本类型包装类:数值基本类型包装类,数字值一样的本类对象都equals相等、hash code相同;Boolean布尔值一样就equals相等、hash code相同;
Math包下的BigDecimal等。
true
true
true
96354 96354 96354
Srcs:
Integer Hash Function:
http://www.concentric.net/~ttwang/tech/inthash.htm
参考 : Java Collections | 容器:
1 只要对象的equals()方法做比较所用的状态(字段)没有被修改,那么对该对象调用多次hashcode()方法都应该始终返回同一个hash code;
2 equals()方法比较结果为相等的两个对象,他们的hashcode()方法返回的hash code必须相同;
3 equals()方法比较结果为不相等的两个对象,不要求你必须为他们产生不相同的hash code;但程序员应该明白为不相等的对象产生不相同的hash code可以提高基于散列表的容器类(HashMap/Hashtable/HashSet)的性能。
首先hashcode()方法返回的int类型hash code是什么,干什么用的?
看名便知,对象的hash code是与散列表(Hash Table, http://wuaner.iteye.com/blog/553007)有关的。散列表插入删除快,以及常数级的快速查找等优点,使java多个容器类都是用了散列表作为其内部的数据结构实现方式,如HashMap/Hashtable/HashSet etc。Java里对象的hash code,正是散列表相关概念里的“关键字”(key)。记录在散列表中的存储地址address,正是通过散列函数H(key)产生的。
约定 1 告诉我们:
equals()方法和hashcode()方法应该是基于对象相同的状态字段;对不可变类(immutable class,实例不能被修改的类),如Java中的Integer、String、BigDecimal等,他们的实例对象的状态永远不会变,重写equals和hashcode相对容易;对可变类(mutable class,状态字段会在使用中被修改),我们应该记住:不要让equals和hashcode方法依赖于那些变化的状态(字段)。
为什么重写equals()必须重写hashcode()?
原因源自JDK的要求: equals()相等的对象其hash code必须相同。
未重写hashcode()的话,Object类的hashcode()方法返回的hash code,是对象的内存地址通过一定方式转换成的integer,你可以通过System.identityHashCode(obj)取得这个默认的hash code;这种机制保证了不互相 == 的对象,其hash code一定不相同(As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects。但这句话也不完全正确,默认hash code也是存在相同的可能的,参见:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6321873 )。那么,状态上符合equals()相等的两个不同对象,其hash code也将是不同的。所以,我们应该在重写equals时,也必须重写hashcode()。
为什么equals()相等的对象其hash code必须相同?
1 举一个反例:equals相等的对象,其hash code不等,会出现什么后果那?我们知道,对象在散列桶(hash buckets)中的位置(address或说index)是通过hashcode经散列函数计算出来的;equals相等,hash code不相同的对象,则他们会被散列到不同的bucket中。
2 Set接口元素不能重复、Map接口作为key的对象不能重复,这个"重复"的判断依据是通过调用equals()方法来判断的。但同时,这些以散列表为底层实现的容器类,为了性能的需要,缓存了对象的hash code值(确切说是hash code经过second hash后的值),并将这个缓存值和equals一起,也作为对象重复的判断依据。那么,如果两个对象equals相等、hash code却不同,则会出现两个对象被重复装入容器的问题;且你想通过一个对象获取容器中一个equals相等(但hash code不同)的对象时,是无法获取到的。
为什么equals()不相等的对象其hash code应该尽可能地不相同?
对象的hash code对应散列表概念里的“关键字”。既然是“关键字”,关键字的选取策略,遵循的就是唯一性原则:不同记录,其关键字不应该相同。不同的关键字尚且有冲突的可能(参见散列表冲突的定义),关键字本身都存在相同的话冲突更是严重,最终导致散列表性能的大幅下降。
举一个极端的例子:假设某类的所有对象,其hash code都是一个固定的、完全相同的整数,则经过散列函数H(hash code)得到的存储地址address则也将自始至终都是同一个,导致散列表退化为一个彻头彻尾的链表,丧失其快速查找定位的优点。
通过以上分析看的出来,如果你重写了类的equals方法,并试图将该类的对象放入基于散列表的容器类(作为Set元素,或作为Map的key)的话,则你也必须重写它的hashcode方法!!其他时候,hashcode方法重不重写其实关系不大。但作为一个好的习惯,你应该在重写equals方法的同时去重写hashcode方法,保证equals相等的对象其hash code也相等 (万一这个类的对象被别人放入基于散列表的容器类,也就不会出问题了)。
重写了equals&hashcode方法的JKD类有:
String:串一样的String对象都equals相等、有相同的hash code;
所有的基本类型包装类:数值基本类型包装类,数字值一样的本类对象都equals相等、hash code相同;Boolean布尔值一样就equals相等、hash code相同;
Math包下的BigDecimal等。
String s1= new String("abc"); String s2 = new String("abc"); String s3 = "abc"; System.out.println(s1.equals(s2)); System.out.println(s1.equals(s3)); System.out.println(s2.equals(s3)); System.out.println(s1.hashCode() + " " + s2.hashCode() + " " + s3.hashCode());输出结果:
true
true
true
96354 96354 96354
Srcs:
Integer Hash Function:
http://www.concentric.net/~ttwang/tech/inthash.htm
http://wuaner.iteye.com/blog/1672580
Comparable<T> 's compareTo(T o):
http://download.java.net/jdk7/archive/b123/docs/api/java/lang/Comparable.html
你可能已经注意到,Java里基本类型的包装类、String、BigDecimal等类都实现了Comparable接口。这个接口是干什么的那?
正如其名,实现Comparable接口,是为了告诉外界该类的对象之间是“可以比较的”。“可以比较的”这里比较的意义在于,当你在Array及有序的容器类如ArrayList、LinkedList、Vector、TreeMap、TreeSet中放入的是实现了Comparable接口的类的对象时,你可以基于该类中对compareTo(T o)方法的实现,对容器类中的元素对象做排序等操作。Comparable接口为它的实现类所提供的排序方式,我们习惯上称其为自然排序(natural ordering),类所重写的compareTo(T o)方法我们称为它的自然比较方法(natural comparison method)。JDK里几个重要的类的自然排序如下:
关于Comparable接口的int compareTo(T o)方法:
引用
以x.compareTo(y)为例,其int类型返回值与比较结果的关系是:
若返回值小于0,表示 x < y;
若返回值等于0,表示 x = y;
若返回值大于0,表示 x > y。
Comparable接口的实现类必须保证其重写的compareTo方法满足以下四个重要的限制条件:
1. sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
2. (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.
3. x.compareTo(y)==0 implies sgn(x.compareTo(z)) == sgn(y.compareTo(z))
4. (x.compareTo(y)==0) == (x.equals(y))(IOW, Natural ordering should be consistent with equals. - Not required,But strongly recommended)
若返回值小于0,表示 x < y;
若返回值等于0,表示 x = y;
若返回值大于0,表示 x > y。
Comparable接口的实现类必须保证其重写的compareTo方法满足以下四个重要的限制条件:
1. sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
2. (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.
3. x.compareTo(y)==0 implies sgn(x.compareTo(z)) == sgn(y.compareTo(z))
4. (x.compareTo(y)==0) == (x.equals(y))(IOW, Natural ordering should be consistent with equals. - Not required,But strongly recommended)
Comparable<T>接口是参数化的(泛型),这在一定程度上意味着,实现该接口的类,可以去做跨类的比较(即Class A implements Cmparable<B>);但跨类比较这种特性仔细想想既没必要,又使compareTo方法的四个限制条件难以被保证。在Java默认提供的类的自然排序中没有跨类的比较。
JDK中依赖于元素对象所在类的自然排序的类有:
工具类Arrays和Collections;
有序容器类TreeSet和TreeMap;
用来做自然比较的两个元素对象,必须是“可以互相比较的”(mutually comparable)。“可以互相比较”从两个方面理解:
1 容器内元素对象都来自一个类的实例的情况下:如果作为元素的对象其类没有实现Comparable接口,则元素间肯定是无法相互比较的,所以会出现:对于Arrays和Collections,会在调用它们的sort()方法时报ClassCastException; 对于TreeSet和TreeMap,如果你使用的是默认的无参构造方法构建这两个容器类的实例,它们元素对象的“有序”正是通过元素或key的自然排序来做的,在试图添加第二个元素时,这种自然排序的比较就会被用到,从而报ClassCastException。所以,元素所在的类必须实现Comparable接口,否则报ClassCastException;
2 如果放入一个有序容器类里的元素对象来自不同的类,此时哪怕这几个类都实现了Comparable接口定义了(针对本类对象的)自己的自然排序,跨类的比较仍然是无法做的,还是会报ClassCastException。
总之,一句话:使用自然排序时只能向集合中加入同类型的对象,并且这些对象的类必须实现Comparable接口。
Srcs:
http://www.coderanch.com/t/557926/java-programmer-SCJP/certification/Collections-sort-throws-ClassCastException
Comparator<T> 's compare(T o1, T o2):
http://docs.oracle.com/javase/6/docs/api/java/util/Comparator.html
Comparator接口是个比较器,它同样适用于工具类Arrays和Collections、有序容器类TreeSet和TreeMap。当你需要一个不是基于类的自然排序来做的排序时(比如,你想单独基于Person的name,id,age分别做排序),可以用它来做。
和Comparable实现的自然排序相比,基于比较器的排序更加的灵活。
int compare(T o1, T o2)方法:
引用
和Comparable接口的compareTo方法很像:
若o1 < o2, 则返回值 < 0;
若o1 = o2, 则返回值 = 0;
若o1 > o2, 则返回值 > 0;
注意:若o1 < o2, 则返回值 < 0;
若o1 = o2, 则返回值 = 0;
若o1 > o2, 则返回值 > 0;
引用
不论是 Comparable<T> 's compareTo(T o) 还是 Comparator<T> 's compare(T o1, T o2),当使用 java 排序 API(如 Collections.sort) 做排序时,都是按照 asc 正序,即 from smaller (o1) to greater (o2)。参见:
http://stackoverflow.com/a/17641949/1635855
http://stackoverflow.com/a/17641949/1635855
Keyword - instanceof:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2
用来比较一个对象是否是指定类型(类或接口)的实例。格式:
ref instanceof ReferenceType
ref 必须是引用类型变量,ReferenceType必须是引用类型;当且仅当ref不为null并且可以强制转换为ReferenceType类型时,返回true。
需要注意的是,不要在代码中使用instanceof做一些诸如条件判断之类的事情,因为那样做违反里氏替换原则,使代码变的不易维护,难以扩展。事实上,instanceof唯一正确的使用场景就是在重写equals方法时,其他时候都应该尽量避免使用它。
Sources:
Java Tutorials - Collections - Object Ordering:
http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html
发表评论
-
J2SE Evolution
2013-04-11 15:39 1172Java 7 New Features Java SE 7 ... -
未完 Java: IO & NIO(new I/O)
2013-01-11 20:56 2006适用: event and data-driven apps ... -
未完 java设计: naming convention | 命名规范
2012-11-20 16:45 2107应该遵循的规范: 类/接口/属性名,使用名词或形容词 ... -
未完 Java: enum 枚举
2012-11-19 20:29 1780http://stackoverflow.com/que ... -
Java多线程之 concurrent 并发包
2012-11-01 07:47 1978Java Tutorials -> Concur ... -
未完 Java Tips & Tricks & Notes
2012-09-12 10:00 1094Hidden Features of Java: h ... -
未完 Java Socket
2012-09-12 08:42 994Java SocketJava SocketJava Sock ... -
Java For-each Loop & Iterable | 增强型For循环和Iterable接口
2012-09-11 21:50 2025增强型For循环没什么好说的,Just see link ... -
未完 Java Collections | 容器
2012-09-06 11:35 1808Sources: http://docs.oracle.com ... -
Java object Initialization (class Instantiation) | 对象的初始化(即类的实例化)
2012-09-03 09:12 2963类实例即对象 ... -
未完Java class&interfac 's Loading, Linking and Initializing | 类与接口的加载、链接和初始化
2012-08-31 19:01 1635JVM装载一个类的时候,首先检查他有没有父类,如果有父类则装载 ... -
未完 java Static 总结
2012-08-31 18:47 1376static可以用来修饰: 字段 Fields 方法 Meth ... -
未完 JVM Runtime Data Areas & Java Memory Model | 内存分配模型 & Java数据存储
2012-08-31 18:43 1861Java虚拟机内存分配模型 需精读:Chapter 5 of ... -
Java Data Types & Literals | 数据类型 和 字面量
2012-08-30 18:12 3876Java数据类型划分: OR http:// ... -
未完 Variables 变量 (Instance/Class/Local)
2012-08-29 10:59 1658Local/Instance/Class Variables ... -
未完 Regular Expressions | 正则表达式
2011-08-25 11:43 1498Extended Regular Expression ... -
java Date(util.Date/sql.Date/sql.Timestamp/sql.Time) & Oracle DATE Type 时分秒 精度问题
2011-05-17 09:32 3905遇到的问题描述: 数据库为Oracle,其jdbc驱动为ojd ... -
Java byte code (bytecode)
2011-05-04 02:55 3857keys: bytecode, byte code, opco ... -
Java Classloading Mechanism : ClassLoader & ASM & 动态字节码增强
2011-04-21 13:29 2391Setting the class path: http:// ... -
class literal & instance.getClass() & Class.forName(String className)
2011-04-20 12:33 2304常用的几种取得Class类实例的方式: 1 class lit ...
相关推荐
看下面这个“简单”的例子:if(newStatusCode.equals("SD") && (sellOffDate == null || todayDate.compareTo(sellOffDate)|| (lastUsedDate != null && todayDate.compareTo(lastUsedDate)>0)) || (newStatusCode....
java中equals和==的区别.doc java中equals和==的区别.doc
能够加强对java中equals与==区别的理解。
详细介绍和讲解Java中的==和equals区别
本篇文章介绍了,在java中"==" 与equals方法的使用。需要的朋友参考下
java中比较值大小,==和equals的区别,基本数据类型和引用数据类型比较值方法
主要介绍了java基础之 “==”与“equals”区别详解,需要的朋友可以参考下
简单介绍java中的“==”和equals
Java中的==与equals()实例方法Java中测试两个变量是否相等的方法有两个,一个是用==运算符,另一个就是object类提供的equals()方法。2
if(s==null||s.equals("")) return ""; String newstring=null; newstring=new String(s.getBytes("ISO8859_1"),"gb2312"); return newstring; } catch(UnsupportedEncodingException e) { return s; } } ...
if (key == null || key.equals("") || key.equals("null")) { return ""; } String result = ""; try { result = resourceBundle.getString(key); } catch (MissingResourceException e) { e....
equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码: String a=new String("foo"); String b=new String("foo"); 两条...
equals是用来比较对象的内容,而==是用来比较java对象的地址。 貌似这个问题大家都已经十分的了解了?为什么我还要单独拿出来说呢?主要有两个方面的原因: 大家对于equals这个函数的理解不够深入,只凭上面的一句话...
关于JSP的电子课件ppt,仅供参考...(username==null||"".equals(username)||password==null||"".equals(password))) { session.setAttribute("username",username); response.sendRedirect("welcome.jsp"); } %>
关于重写equals、hashcode以及compareTo方法! equals()方法是Object类中的一个方法,它用于比较两个对象是否相等。然而,它的默认实现是比较对象的引用(地址),而不是比较对象的实际内容。因此,在某些情况下,...
关于Java中==的用法与equals的用法,醉重要的是比较它们的不同之处
java中的比较运算符== 与 equals()方法 值类型、引用类型
java 资料 equals 与== 的区别
知识点 比较运算符==和equals方法的比较 知识点 比较运算符==和equals方法的比较