2.5实现多个接口
在
Java
中,类和类之间是单继承的,一个类只能有一个父类,即
Java
中不支持多继承
,但是
一个类可以实现多个接
口
。下面通过类来表示一组动物
.
public abstract class Animal { public String name; public int age; public Animal(String name, int age) { this.name = name; this.age = age; } public abstract void eat(); }
public interface IRunning { void run(); }
public interface ISwimming { void swim(); }
public interface IFlying { void fly(); }
public class Dog extends Animal implements IRunning,ISwimming{ public Dog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println(name+"吃狗粮!"); } @Override public void run() { System.out.println(name+"用狗腿在跑"); } @Override public void swim() { System.out.println(name+"用狗腿游泳"); } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
public class Bird extends Animal implements IRunning,IFlying{ public Bird(String name, int age) { super(name, age); } @Override public void eat() { System.out.println(name+"吃鸟粮"); } @Override public void fly() { System.out.println(name+"用翅膀飞"); } @Override public void run() { System.out.println(name+"用鸟腿跑"); } }
public class Test { public static void test1(Animal animal) { animal.eat(); } public static void test2(IRunning iRunning) { iRunning.run(); } public static void test3(ISwimming iSwimming) { iSwimming.swim(); } public static void test4(IFlying iFlying) { iFlying.fly(); } public static void main(String[] args) { test1(new Dog("狗",4)); test2(new Dog("狗",4)); test3(new Dog("狗",4)); System.out.println("====================="); test1(new Bird("鸟",2)); test2(new Bird("鸟",2)); test4(new Bird("鸟",2)); } }
注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类
。
上面的代码展示了
Java
面向对象编程中最常见的用法
:
一个类继承一个父类
,
同时实现多种接口
2.6接口间的继承
在
Java
中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。
接口可以继承一个接口
,
达到复用的效果
.
使用
extends
关键字
.
public interface IRunning { void run(); }
public interface ISwimming { void swim(); }
public interface IFlying extends IRunning,ISwimming{ void fly(); }//继承了两个接口还可以有自己的接口
2.7抽象类的接口的区别(面试题)
1.接口中的成员都被public static final修饰,而抽象类中的可以有普通成员.
2.接口中的方法默认被public abstract修饰,而抽象类中的方法可以是抽象的也可以是非抽象的.
3.一个类可以继承一个类并执行多个接口,接口之间只有extends关系.
2.8接口使用实例
给对象数组排序实例:
实例化两个学生对象,比较大小并将其排序.
按照我们之前的理解, 数组我们有一个现成的 sort 方法,sort依据数值大小可以排序,但不能用来排两个学生的顺序,学生的大小关系需要我们额外指定变量(名字长度,年龄大小还是身高等等).
我们先来比较学生对象的大小,再利用冒泡排序排好
class Student implements Comparable<Student>{ public String name; public int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Student o) { /*法一: if(this.age > o.age) { return 1; }else if(this.age < o.age) { return -1; }else { return 0; }*/ //法二: return this.age - o.age; } } //比较年龄大小 class AgeComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.age - o2.age; } } //比较名字拼音长度 class NameComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.name.compareTo(o2.name); } }
public class Test {
public static void bubbleSort(Comparable[] comparable) { for (int i = 0; i < comparable.length - 1; i++) { NameComparator nameComparator = new NameComparator(); for (int j = 0; j < comparable.length - 1 - i; j++) { if(nameComparator.compare((Student) comparable[j], (Student)comparable[j+1]) > 0) { Comparable tmp = comparable[j]; comparable[j] = comparable[j+1]; comparable[j+1] = tmp; } } } }
public static void main(String[] args) { Student student1 = new Student("zhangsan",10); Student student2 = new Student("lisi",15); System.out.println(student1); System.out.println(student2); System.out.println("==============="); System.out.println(student1.compareTo(student2)); Student[] student = new Student[3]; student[0] = new Student("zhangsan",10); student[1] = new Student("lisi",2); student[2] = new Student("wangwu",18); bubbleSort(student); System.out.println(Arrays.toString(student)); } }
2.9Clonable接口和深浅拷贝
Java
中内置了一些很有用的接口
, Clonable
就是其中之一
.
Object
类中存在一个
clone
方法
,
调用这个方法可以创建一个对象的
"
拷贝
".
但是要想合法调用
clone
方法
,
必须要 先实现 Clonable 接口
,
否则就会抛出
CloneNotSupportedException
异常
.
我想拷贝一份person对象,但发现clone方法不能直接调用.如下:
class Person { int age; public Person(int age) { this.age = age; } @Override public String toString() { return "Person{" + "age=" + age + '}'; } }
public class Test2 { public static void main(String[] args) { Person person1 = new Person(3); Person person2 = person1.clone(); } }
在person类当中重写clone方法:
class Person { int age; public Person(int age) { this.age = age; } @Override public String toString() { return "Person{" + "age=" + age + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public class Test2 { public static void main(String[] args) { Person person1 = new Person(3); Person person2 = person1.clone();//仍就调用不了clone方法 } }
分析解决:
正确代码:
class Person implements Cloneable{ int age; public Person(int age) { this.age = age; } @Override public String toString() { return "Person{" + "age=" + age + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
public class Test2 { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person(3); Person person2 = (Person)person1.clone(); } }
那么什么是浅拷贝呢?
在上述代码的基础上我定义一个类Money,在这个类中定义成员money = 14.3.
class Person implements Cloneable{ int age; public Money m; public Person(int age) { this.age = age; this.m = new Money(); } @Override public String toString() { return "Person{" + "age=" + age + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } //克隆方法在Object当中,并且由protected修饰,所写类默认继承Object类 //protected:不同包中的子类.同时使用时,需要super调用 public class Test2 { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person(3); Person person2 = (Person)person1.clone(); System.out.println(person1.m.money);//14.3 System.out.println(person2.m.money);//14.3 System.out.println("========================="); person2.m.money = 99.0; System.out.println(person1.m.money);//99.0 System.out.println(person2.m.money);//99.0 //person2.m.money = 99.0;这句代码原本应该是只改变person2.m.money的值,但是person1也改变了 } }
原因:
那怎么实现只改变person2中的money值?代码如下:
class Person implements Cloneable{ int age; public Money m; public Person(int age) { this.age = age; this.m = new Money(); } @Override public String toString() { return "Person{" + "age=" + age + '}'; } @Override protected Object clone() throws CloneNotSupportedException { Person tmp = (Person)super.clone(); tmp.m = (Money)this.m.clone();//克隆的返回值类型为Object需要强转 return tmp; } } //克隆方法在Object当中,并且由protected修饰,所写类默认继承Object类 //protected:不同包中的子类.同时使用时,需要super调用 public class Test2 { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person(3); Person person2 = (Person)person1.clone(); System.out.println(person1.m.money); System.out.println(person2.m.money); System.out.println("========================="); person2.m.money = 99.0; System.out.println(person1.m.money); System.out.println(person2.m.money); } }
3.Object类
//双击shift 搜Object
Object
是
Java
默认提供的一个类。
Java中,
Object
类是所有类的父类(哪怕没有写明extends Object),默认会继承
Object
父类。即所有类的对象都可以使用Object
的引用进行接收。
代码示例:
class Person{ } class Student{ } public class Test { public static void main(String[] args) { function(new Person()); function(new Student()); } public static void function(Object obj) { System.out.println(obj); } }
//执行结果:
Person@1b6d3586
Student@4554617c
对于整个Object类中的方法需要实现全部掌握。
3.2获取对象信息
如果要打印对象中的内容,可以直接重写
Object
类中的
toString()
方法,
class Person{ String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } //1.重写了父类Object中的toString方法, 2.System.out.println(person);person处,父类引用,调用的是子类对象,最终成功打印出想要的子类(向上转型) 发生了动态绑定 @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class Test { public static void main(String[] args) { Person person = new Person("小吴",21); System.out.println(person); } }
3.3对象比较equals方法
在
Java
中,
==
进行比较时:
a.
如果
==
左右两侧是基本类型变量,比较的是变量中值是否相同
b.
如果
==
左右两侧是引用类型变量,比较的是引用变量地址是否相同
c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:
当比较对象为引用类型对象并且不止一个时:
class Person{ String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } } public class Test { public static void main(String[] args) { Person person1 = new Person("小吴",21); Person person2 = new Person("小吴",21); System.out.println(person1); System.out.println(person2); /*Object中,equals的方法为: public boolean equals(Object obj) { return (this == obj); }*/ System.out.println(person1 == person2); System.out.println(person1.equals(person2)); //所以在子类还未重写equals方法时,上述两行代码是一样的,无法正确比较 System.out.println("重写equals方法后:"); System.out.println(person1.equals(person2));//得到正确的比较结果 } }
结论:比较对象中内容是否相同的时候,一定要重写
equals
方法。