目录
Object类型的变量
-
可以使用Object类型的变量引用任何类型的对象
Object obj=new Employee();
-
但是Object类型的变量只是一个作为各种值的一个泛型容器。如果想要对其中的值进行具体的操作,还需要清除对象的原始类型并加以强制转换:
Employee e=(Employee) obj;
-
这时可以对·E进行一系列的操作
-
-
在Java中,只有基本类型(数值,字符,布尔类型的值)不是对象
-
像所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object类
//对象数组 Employee[] staff=new Employee[10]; //基本类型的数组 obj=new int[10];
equals方法
-
Object类的equals方法用于检测一个对象是否等于另一个对象
-
在Object类中的equals方法用于确定两个引用是否相等
-
可以看出,在Object类中的equals方法用于==去比较,那么==和equals有什么区别呢?
-
图中可以看出在对基本类型进行赋值相同的值时用==进行比较结果是true,==实际比较的是两个对象的堆内存地址,像123,12.32,他们是作为常量在常量池中以HashSet策略存储起来的,图中的两个引用a,b实际是指向同一个对象地址的
-
其中的字符串常量“123”,虚拟机在发现这个字符串常量之后,他会在内部的字符串常量列表中查找,如果没有找到,那么他会在堆里面创建一个包含字符串序列[123]的String对象s1,,然后把这个字符串序列和对象的String对象作为一个值对([123],s1)保存在字符串常量列表中
-
在后面将“123”赋值给s2,这时虚拟机发现了一个相同的字符串常量123,它会在内部的字符串序列表找到相同的字符串序列然后返回对应的String对象引用,像String s2="123",运行时s2会从内部字符串常量列表内得到s1的返回值,所以s1和s2都指向同一个String对象
-
s3引用了对象new String();实际上new操作为新生的对象分配了内存,这不是引用,而是在Java堆中划出了一片内存供新生的对象。所以s3引用的和s1,s2引用的不同
-
Object的equals类的equals也同样是使用了==来比较两个对象,那么它们还有什么区别呢?
-
-
其实Object类中的equals方法对于大多数类来说就已经足够了,但是,经常需要基于状态检测对象的相等性,如果两个对象有相同的状态才说这两个对象相同。例如两个员工对象,姓名,年龄,工种,工资,雇佣日期都相同,这样在实际的数据库中就需要比较两者的ID才有意义,否则,就认为这两个对象是相等的。
-
这时就需要用到之前的方法的重写这个知识了,我们可以在Object类下我们自己的类中来重写equals方法,来实现我们自己的比较方法
-
在这里的GetClass()方法将返回一个对象所属的类,只有两对象属于一个类时这两个对象才可能相等
-
代码的最后一句的返回值,是在比较字段是否具有相同的值,使用Objects类的equals方法是为了防备name字段可能为null的情况。
-
-
在子类中定义equals方法时首先要调用超类的equals,如果比较失败,那么这两个对象就不可能相等,如果超类的字段相等,就需要比较子类的实例字段是否相等。
public class Manager extends Employee{ private int bouns; public boolean equals(Object Obj){ //这里调用super的equals方法主要比较两个对象是否属于同一个类 if(!super.equals(obj)){return false} Manager other=(Manager) obj; return bonus==other.bouns } }
相等测试与继承
-
Java语言规范要求equals方法要有下面的特性:
-
自反性:对于任何的非空引用x,x.equals(x)应该返回非空引用
-
对称性:对于任何引用x和y,当且仅当x.equals(y)返回为true时,y.equals(x)返回为true
-
传递性:对于任何引用x,y,z,如果x.equals(y),y.equals(z)都为true时,x.equals(z)返回为true
-
一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回相同的结果
-
对于任意非空的引用x,x.equals(null)应该返回false
-
-
编写一个完美的equals方法的建议:
-
显式参数命名obj,稍后还需要将它转换为另一个名为other变量
-
检测this与obj是否相等
if(this==obj) return true;
-
检测obj是否为null,如果为null则返回false
if(obj==null)return false;
-
比较this与obj的类,如果equals的语义可以在子类中改变,就是用getClass检测:
if(getClass()!=obj.getClass()) return false;
-
如果所有的子类都有相同的相等性语义,可以使用instanceof检测
if(!(obj instanceof ClassName)) return false;
-
-
将obj强制转化为相应类类型的变量:
ClassName other=(ClassName) obj;
-
现在根据相等性概念的要求来比较字段。使用==比较基本类型,使用Objects.equals()比较对象字段,如果所有字段都匹配则返回true否则返回false
return filed1=other.filed1 && Objects.equals(filed2,other.filed2)
-
若在子类中更新定义equals就要在其中包含一个super.equals(other)调用
-
对于数组类型的字段,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等,如果两个数组长度相同,并且在对应的位置上数据元素也相同将返回true
-
hashCode方法
-
散列码是由对象导出的一个整型值
-
散列码是没有规律的,两个不同的对象得到的散列码基本上不会相同
-
字符串s和t有相同的散列码,是因为字符串的散列码是由内容导出的,而字符串sb和tb散列码不同,是因为在StringBuilder类中没有定义hashCode方法,而Object类的默认hashCode方法会从对象的存储地址得出散列码
-
如果重新定义了equals方法,就必须为用户可能插入散列表的对象重新定义hashCode方法,当需要组合多个散列值时可以调用Objects.hash并提供所有这些参数,这个方法会对各个参数调用Object.hashCode,并组合这些散列值
public int hashCode(){ return Objects.hash(name.salsary,hireDay); }
-
equals和hashCode的定义必须相容:如果x.equals(y)返回true那么x.hashCode()和y.hashCode()返回相同的值
-
如果存在数组类型的字段,那么可以使用静态的Arrays.hashCode方法计算一个散列码,这散列码由数组元素的散列码组成
toString方法
-
返回表示对象值的一个字符串
-
在Employee类中实现的toString方法
-
可通过调用getClass().getName()获得类名的字符串,而不要将类名随意的编写到toString方法中
-
这样的toString方法可以方便的被子类调用,设计子类的程序员也应该定义自己的toString方法,并加入子类的字段,如果超类使用了getClass().getName()那么子类只要调用super.toString()就可以了
-
结果:
-
在我们的代码中其实随处可见toString方法的主要原因:只要对象与一个字符串通过操作符“+”连接起来,Java编译器就会自动的调用toString方法来获得这个对象的字符串描述
var p=new Point(10,20); String message="the current position is"+p
实际上在这里调用了p.toString()
-
其实可以不携程x.toString(),可以写成“ ”+x
-
数组的打印:Arrays.toString()
-
多维数组的打印:Arrays.deepToString
参考书籍:Java核心技术 卷1(原书第11版)