目录
1. 接口使用实例
1.1 给对象数组排序
1.2 Clonable接口和深拷贝
Cloneable
浅拷贝
深拷贝
1.3 抽象类和接口的区别
2. Object类
2.1 Object类的介绍
2.2 toString()
2.3 equals()
2.4 hashcode()
1. 接口使用实例
1.1 给对象数组排序
现有一个学生类:
class Student{
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "["+this.name+":"+this.score+"]";
}
}
public static void main(String[] args) {
Student[] students={
new Student("zhangsan",95),
new Student("lisi",92),
new Student("wangwu",97),
new Student("zhaoliu",90)
};
}
在数组类 Arrays 里,我们有一个现成的方法 sort ,比如以下案例:
int[] arr={1,3,2,4,6,5,9,8,7};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
对于学生类Student组成的数组,不能直接使用这个方法:
原因:两个整数是可以进行比较的,但是两个学生对象的大小关系无法直接确定...
如果当前对象应排在参数对象之前 , 返回小于 0 的数字 ;如果当前对象应排在参数对象之后 , 返回大于 0 的数字 ;如果当前对象和参数对象不分先后 , 返回 0;
public static void sort(Comparable array[]){
for (int bound = 0; bound < array.length; bound++) {
for (int cur = array.length-1; cur > bound; cur--) {
if(array[cur-1].compareTo(array[cur])>0){
Comparable tmp=array[cur];
array[cur]=array[cur-1];
array[cur-1]=tmp;
}
}
}
}
sort(students);
System.out.println(Arrays.toString(students));
如果我们不想要按照分数的高低来排序,而是按照名字来排序,应该如何写?
重写compareTo方法如下:
@Override
public int compareTo(Student o) {
return this.name.compareTo(o.name);
}
然而,这种写法有一个明显的缺陷:
不适合灵活的比较,只适合用于固定的比较。
比如,想要从按照分数比较换成按名字比较,就需要重新写一个compareTo方法,使用起来不方便...
解决方法:换一个接口。(使用 Comparator 接口)
另外写两个类,实现Comparator接口,分别实现按名字比较和按分数比较的功能:
class ScoreComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.getScore()-o2.getScore();
}
}
class NameComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
然后将以上两个类分别实例化,用哪个类作为参数传入Arrays.sort中,就按哪种方式比较:
//实例化两个类
ScoreComparator scoreComparator=new ScoreComparator();
NameComparator nameComparator=new NameComparator();
//按照分数比较
Arrays.sort(students,scoreComparator);
System.out.println(Arrays.toString(students));
//按照名字比较
Arrays.sort(students,nameComparator);
System.out.println(Arrays.toString(students));
1.2 Clonable接口和深拷贝
Cloneable
Java 中内置了一些很有用的接口 , Clonable 就是其中之一 .Object 类中存在一个 clone 方法 , 调用这个方法可以创建一个对象的 " 拷贝 ". 但是要想合法调用 clone 方法 , 必须要先实现 Clonable 接口 , 否则就会抛出 CloneNotSupportedException 异常 .
案例:
class Animal implements Cloneable{
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Demo3 {
public static void main(String[] args) throws CloneNotSupportedException {
Animal animal1=new Animal("小财",3);
Animal animal2=(Animal) animal1.clone();
System.out.println(animal1);
System.out.println(animal2);
}
}
【注意事项】
1.要调用对象的clone()方法,首先要在类里面重写clone()方法。
2.clone方法要处理CloneNotSupportedException异常,否则编译器会报错。
3.clone()方法的返回值是Object类型,需要向下转型才能接收。
4.类要实现Cloneable接口,否则运行编译器会抛出 CloneNotSupportedException异常.
克隆过程图解:
增加一个类Money,并且该类实例化对象作为类Person的成员变量:
class Money{
public double val=9.9;
}
class Person implements Cloneable{
private String name;
private int age;
public Money money=new Money();
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
浅拷贝
案例:
public class Demo3 {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1= new Person("zhangsan",10);
Person p2= (Person) p1.clone();
System.out.println("修改前:"+p1.money.val);
System.out.println("修改前:"+p2.money.val);
p1.money.val=99;
System.out.println("修改后:"+p1.money.val);
System.out.println("修改后:"+p1.money.val);
}
}
运行结果:
可以看到:改变p1的成员money后,p2的成员money也跟着改变了——这就是潜拷贝。
分析原因:
原因:money是一个引用变量,可以理解为p1的引用变量money里面储存了对象new Money()的地址,而克隆出来的p2的引用变量跟p1的引用变量储存了同一个地址,因此修改p1的money,p2的money也会改变。
深拷贝
更改Person类里的重写方法,将克隆出来的新对象的引用变量成员money指向另一个对象(不是跟p1的对象同一个):
@Override
protected Object clone() throws CloneNotSupportedException {
Person tmp=(Person) super.clone();
//tmp.money=new Money();//写法1
tmp.money=(Money) this.money.clone();//写法2
return tmp;
}
再次运行的结果:
1.3 抽象类和接口的区别
核心区别 : 抽象类中可以包含普通方法和普通字段 , 这样的普通方法和字段可以被子类直接使用 ( 不必重写 ), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法 .
【注意】抽象类存在的意义是为了让编译器更好的校验, 像 Animal 这样的类我们并不会直接使用, 而是使用它的子类.万一不小心创建了 Animal 的实例, 编译器会及时提醒我们.
No | 区别 | 抽象类(abstract) | 接口(interface) |
1 | 结构组成 | 普通类+抽象方法 | 抽象方法+全局变量 |
2 | 权限 | 各种权限 | public |
3 | 子类使用 | 使用extends关键字继承抽象类 | 使用implements关键字实现接口 |
4 | 关系 | 一个抽象类可以实现若干个接口 | 接口不能继承抽象类,但是接口可以使用extends关键字继承多个父接口 |
5 | 子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
2. Object类
2.1 Object类的介绍
class Animal {
}
class Person{
}
public class Demo3 {
public static void function(Object o){
System.out.println(o);
}
public static void main(String[] args) {
function(new Animal());
function(new Person());
}
}
Object类中包含了很多方法。
本次学习中,我们主要学习这几个方法:toString()方法,equals()方法,hashcode()方法
2.2 toString()
Animal类默认继承Object类,重写toString方法如下:
class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Demo3 {
public static void main(String[] args) {
System.out.println(new Animal("旺财",3));
}
}
2.3 equals()
在 Java 中, == 进行比较时:a. 如果 == 左右两侧是基本类型变量,比较的是变量中值是否相同b. 如果 == 左右两侧是引用类型变量,比较的是引用变量地址是否相同c. 如果要比较对象中内容,必须重写Object中的equals方法,因为equals 方法默认也是按照地址比较的:// Object 类中的 equals 方法public boolean equals ( Object obj ) {return ( this == obj ); // 使用引用中的地址直接来进行比较}
使用案例:
没有重写equals方法如下:
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Demo3 {
public static void main(String[] args) {
Person p1=new Person("张三",18);
Person p2=new Person("张三",18);
System.out.println(p1.equals(p2));
}
}
重写了equals方法如下:
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null ) return false;
//不是Person类
if(!(o instanceof Person)) return false;
Person person = (Person) o;
return this.age == person.age && this.name.equals(person.name);
}
}
2.4 hashcode()
public static void main(String[] args) {
Person p1=new Person("张三",18);
Person p2=new Person("张三",18);
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
}
如果要使算出来的位置一样,就需要重写 hashcode() 方法:
@Override
public int hashCode() {
return Objects.hash(name, age);
}
对象p1和p2的name和age都相同,那么将name和age作为条件传入hash方法中,算出来的hash值就是一致的:
完
如果哪里有疑问的话欢迎来评论区指出和讨论,如果觉得文章有价值的话就请给我点个关注还有免费的收藏和赞吧,谢谢大家!