在本篇文章中,详解了什么是Object类,一些Object类中的方法,以及两种接口的实现从而达到对象的比较,文章中的所讲的Object类中的方法以及两种比较的接口都应该熟悉的掌握!!!
object类是所有类的父类,在Java中是默认的,即使在定义类的时候没有显示的继承某个类,但是在Java中,每个类都会默认的继承Object类,所以,Object类的级别也就相当于所有类的祖宗!就像下面这种情况,实参在传对象时,形参的类型都可以定义成Object类型进行接受。
abstract class Experiment1{
public abstract void action();
}
class Experiment2 extends Experiment1{
public void action() {
System.out.println("哈哈");
}
}
public class Test {
//定义形参为Object类型
public static void method(Object obj) {
//因为Object里没有action方法,所以转换成Experiment类型
((Experiment1)obj).action();
}
public static void main(String[] args) {
Experiment2 experiment2 = new Experiment2();
method(experiment2);
}
}
可以看到并没有报错,所以证明了Object类就是所有类的父类,而在Object类中,又有许多可以经常用到的方法,下面我们看一下;按住Ctrl+点击Object
以上这些方法都是经常可以用到的,在这篇文章中会讲到getClass();equal();toString();clone();方法,请好好理解:
toString()方法的作用是获取对象的信息,实例化一个对象后,使用toString()方法可以将字符串的信息在控制台打印出来,如下代码:
public class Test {
private String name;
private int age;
public Test(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Test test = new Test("李四",19);
System.out.println(test);
}
}
这是个什么东西,这并不是我实例化的对象内容啊,为什么会这样呢?下面看我解释:
首先我们先按住Ctrl+鼠标点击println这个方法,看看里面的是如何实现的。
所以刚刚最后打印出来的不是对象中的内容,那么要想打印出对象中的内容的话,需要在类中重写toString方法,因为:println方法最后是调用了Object类中的toString方法,所以才打印出了地址(这里可以理解成一个地址),因为所有的类都是Object类的子类,所以,通过动态绑定,利用在子类中重写父类的方法,最后调用子类中重写的方法,从而实现对象的打印:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
//看起来一模一样对不对?
Student student1 = new Student("李四",18);
Student student2 = new Student("李四",18);
//不能通过==进行判断
//System.out.println(student1==student2);
System.out.println(student1.equals(student2));
}
}
这里不能通过==进行判断,因为两遍表达式都是基本类型的数据时,才能用 ==进行比较,而现在两边表达式都是引用类型的,所以不能比较,而这里换成equals进行比较的话也是false,这是为什么呢?那么,就要看一下这个equals的实现方式了,按住Ctrl+鼠标点击equals;
因为阿,在代码中,new了两次对象,虽然对象中的内容都是一样的,但他本质上是不一样的,它是在堆上开辟了两块空间,所以,在这里比较的是两块空间的地址,所以,得到的结果是false,如果要想对这两个对象进行比较,则要重写equals方法,下面重写:
@Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
}else if(this == obj) {
return true;
}else{
//因为obj时object类型,里面没有name和age成员,所以要转换成Student
return ((Student)obj).name.equals(((Student) obj).name) && ((Student) obj).age == ((Student) obj).age;
}
}
结论:如果要比较对象中的内容是否相同时,一定要重写equals方法
现在回到刚刚调用的toString方法
在这里hashCode是计算出具体的对象位置,那么,请看下面代码:
public static void main(String[] args) {
//看起来一模一样对不对?
Student student1 = new Student("李四", 18);
Student student2 = new Student("李四", 18);
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
}
}
算出了两串数字,这个数字是表示hashCode将地址以十六进制的形式打印出来,而这两个对象,因为内容是一样的,所以在逻辑上应该认为它们两个也应该存在一个地方,但是,本质上是不一样的,而如果想要在逻辑上把它们两个放在同一个地方,需要重写hashCode方法:
@Override
public int hashCode() {
return Objects.hash(name, age);
}
结论:hashCode方法时用来确定对象在内存中存储的位置是否相同
Comparable比较两个对象的大小,如果对象之间要想比较谁大谁小,需要实现Comparable这个接口,然后重写里面的comparto方法,指定两个对象的比较方式。具体代码实现:
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//通过年龄进行比较
@Override
public int compareTo(Student o) {
return this.age-o.age;
}
public static void main(String[] args) {
Student student1 = new Student("李四", 18);
Student student2 = new Student("王五", 20);
System.out.println(student1.compareTo(student2));
}
}
下面根据名字进行比较
@Override
//只需要重写里面的comparto方法即可
public int compareTo(Student o) {
return this.name.compareTo(o.name);
}
可以看到重写的comparto方法,与前面的requals不同,conparto方法是比较两个对象的大小,只要在comparto的重写方法中指定根据对象中的什么内容进行比较,就可以比较出两个对象的大小。所以返回值是int,而equals是比较两个对象是否相等,返回值是bolean类型的;
而在这里还需要注意一点问题需要知道:
下面再举个例:对数组中的内容进行排序
如果在没有重写comparto方法的前提下只用正常的Arrays.sort方法对数组进行排序是会报错的,因为排序的是一个对象,需要实现Comparable接口,重写comparto方法,指定根据什么进行排序,年龄或者姓名,下面请看代码:
//需要实现Comparable接口
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//根据名字进行排序
@Override
public int compareTo(Student o) {
return this.name.compareTo(o.name);
}
//根据年龄进行排序
//@Override
//public int compareTo(Student o) {
// return this.age-o.age;
// }
public static void main(String[] args) {
Student[] arrays = new Student[]{new Student("zhangsan",14),new Student("lisi",11),new Student("wangwu",19)};
Arrays.sort(arrays);
System.out.println(Arrays.toString(arrays));
}
}
Comparator接口也是实现两个对象进行比较,但是,这种比较方法比较独立,对类的侵入性不强,而上一种比较方法的缺点就是,当需要根据对象中不同的内容进行比较时,需要修改comparto方法中的内容,可能会对已经写好的代码造成影响,而这comparator这个接口则不会,下面通过代码讲解:
//根据年龄进行比较
class AgeCompar implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
//根据姓名进行比较
class NameCompar implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
Student student1 = new Student("李四", 18);
Student student2 = new Student("王五", 20);
//只需要实例化两个比较器的对象即可
//通过引用调用比较器里面的方法实现内容的比较
AgeCompar ageCompar = new AgeCompar();
NameCompar nameCompar = new NameCompar();
System.out.println(ageCompar.compare(student1, student2));
System.out.println(nameCompar.compare(student1,student2));
}
以上代码就是**比较器比较,**优点是所有的比较方法都可以共存,想根据什么比较只需要写一个比较器就可以了,不需要对已经写好的代码进行修改。
作一下总结:
1、如果==两边是引用数据类型,则需要注意;这比较的是两个引用是否指向同一个对象
2、如果两个对象要进行比较是否相等,则要调用equals方法并且重写equals方法,指定根据对象中的什么内容进行比较,
3、如果是两个字符串吹进行比较是否相等,则要调用equals方法,不用重写;
4、如果比较两个对象的大小,则要实现Comparable接口,重写里面的comparto方法,指定根据什么进行比较
5,如果是字符串比较大小,只需调用comparto方法即可
点是所有的比较方法都可以共存,想根据什么比较只需要写一个比较器就可以了,不需要对已经写好的代码进行修改。