正文:
抽象类:
1. 抽象类的语法形式:
只需在class前加上一个abstract关键字。
public abstract class 类名 {
}
2. 为什么会有抽象类的出现?
在我们写在一个类中写一个没有具体实现的方法,在正常类中是无法实现的,但是在抽象类中就可以实现,这大大提高了开发的效率。
3. 这种没有具体实现的方法叫作抽象方法,也需要abstract关键字来修饰,抽象方法只能出现在抽象类中,在普通类中会报错,这也是普通类和抽象类的区别所在。
4. 抽象类不能进行实例化,所以也就不能用final来修饰。
原因:被final修饰的类不能被继承,但是抽象类不能进行实体化,两者之间存在矛盾。
5. 如果一个普通类继承了这个抽象类,那么这个普通类就必须重写这个抽象类当中的所有抽象方法。如果一号抽象类继承了二号抽象类,那继承一号的普通类也要重写二号抽象类的抽象方法。
接口:
语法:
public interface 接口名 {
}
1. 接口当中的成员变量,默认是public static final修饰的,不能是别的修饰,在定义的时候任何一个关键字都可以省略。
2. 接口中的抽象方法默认都是public abstract修饰的,定义方式可与接口中的成员变量类比。
3. 接口中的方法只能是抽象方法,不能有具体实现,如果需要定义一个有具体实现的方法,需要用default或者static修饰,那么可以有具体实现。
4. 接口是不能进行实例化的。
5. 类和接口之间可以使用关键字implements来实现接口,并且类中必须实现接口中的抽象方法。
6. 一个接口对应一个字节码文件。
7. 如果一个类不想实现接口中的方法,那么此时这个类可以定义为抽象类,但这个抽象类如果被继承,他的子类就必须实现所有没有被实现的方法。
8. 接口的出现解决了Java不能多继承的问题。
Bird类:
public class Bird extends Animal implements IMove, ILive{
@Override
public void move() {
System.out.println(this.name + "正在飞...");
}
@Override
public void eat() {
System.out.println(this.name + "正在吃虫子...");
}
@Override
public void live() {
System.out.println(this.name + "住在鸟窝里...");
}
}
Dog类:
public class Dog extends Animal implements IMove,ILive{
@Override
public void move() {
System.out.println(this.name + "正在跑...");
}
@Override
public void eat() {
System.out.println(this.name + "正在狗粮...");
}
@Override
public void live() {
System.out.println(this.name + "住在狗窝里...");
}
}
IMove接口:
public interface IMove {
void move();
}
ILive接口:
public interface ILive {
void live();
}
9. 接口与接口之间也存在继承关系,也是用extends关键字。
ILive接口:
public interface ILive extends IMove{
void live();
}
IMove接口:
public interface IMove {
void move();
}
接下来介绍一些比较常用的接口:
comparable接口:
Student类:
public class Student{
private String name = "xxx";
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Students类:
import java.util.Arrays;
public class Students {
Student[] students;
public Students(Student[] students) {
this.students = students;
}
public Student[] getStudents() {
return students;
}
public void setStudents(Student[] students) {
this.students = students;
}
@Override
public String toString() {
return "Students{" +
"students=" + Arrays.toString(students) +
'}';
}
}
如果我们想要对Student[]数组中的元素进行排序,刚开始可能会这样写:
Test类:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Student student1 = new Student("小明", 15);
Student student2 = new Student("大宏", 12);
Student[] students = new Student[2];
students[0] = student1;
students[1] = student2;
Students students1 = new Students(students);
System.out.println("排序前:" + students1);
Arrays.sort(students1.students);
System.out.println("排序后:" + students1);
}
}
运行起来就会报错:
解决问题:
让Student类实现Comparable接口:
public class Student implements Comparable<Student>{
private String name = "xxx";
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
编译器有报错:
按住Ctrl鼠标左键点进去观察Comparable的源代码:
观察发现在Comparable接口中存在一个抽象方法,我们需要对其重写:
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
编译器不报错后我们可以再次尝试运行:
将代码修改一下:
@Override
public int compareTo(Student o) {
return o.age - this.age;
}
运行:
发现Arrays.sort()的底层其实是compareTo() 。
那如果我们想要根据姓名来进行排序,可以这样修改:
@Override
public int compareTo(Student o) {
return this.name.compareTo(o.name);
}
运行:
观察这种方法是可行的,那就会产生一个痛点,我们只能选择一个参考系,如果想更改就只能对代码动手,这里我们引入Comparator接口可以解决这一问题:
Comparator接口:
CompareByAge类:
import java.util.Comparator;
public class CompareByAge implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
}
CompareByName类:
import java.util.Comparator;
public class CompareByName implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
Student类:
public class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Students类:
import java.util.Arrays;
public class Students {
Student[] students;
public Students(Student[] students) {
this.students = students;
}
public Student[] getStudents() {
return students;
}
public void setStudents(Student[] students) {
this.students = students;
}
@Override
public String toString() {
return "Students{" +
"students=" + Arrays.toString(students) +
'}';
}
}
Test类:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Student student1 = new Student("小明", 15);
Student student2 = new Student("大宏", 16);
Student[] students = new Student[]{student1,student2};
Students students1 = new Students(students);
System.out.println("排序前:" + students1);
CompareByName compareByName = new CompareByName();
Arrays.sort(students1.students, compareByName);
System.out.println("排序后:" + students1);
}
}
运行结果:
我们可以通过写成多个类来选择我们比较的根据。
Clone接口:
当我们需要创建一个一模一样的对象时可以使用Clone接口实现,举个例子:
Test类:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("wyh", 13);
Person person2 = (Person) person1.clone();
System.out.println(person1);
System.out.println(person2);
}
}
Person类:
public class Person implements Cloneable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
运行结果: 克隆成功
我们还可以去测试一下克隆的具体实现是什么样的:
Test类:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person("wyh", 13);
Person person2 = (Person) person1.clone();
person1.setName("tjy");
System.out.println(person1);
System.out.println(person2);
}
}
运行结果:
我们改变了person1,但person2并没有跟着改变,由此可以看出Clone是重新在堆中开辟一块空间并将值放进去的:
深拷贝:
Test类:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Deposit deposit = new Deposit(5000);
Person person1 = new Person("wyh", 13, deposit);
Person person2 = (Person) person1.clone();
person1.setName("tjy");
System.out.println(person1);
System.out.println(person2);
}
}
Deposit类:
public class Deposit {
double money;
public Deposit(double money) {
this.money = money;
}
}
Person类:
public class Person implements Cloneable{
private String name;
private int age;
private Deposit deposit;
public Person(String name, int age, Deposit deposit) {
this.name = name;
this.age = age;
this.deposit = deposit;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Deposit getDeposit() {
return deposit;
}
public void setDeposit(Deposit deposit) {
this.deposit = deposit;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", deposit=" + deposit +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
运行结果:
我们发现当一个类的成员变量中存在另一种类,在克隆的时候就会出现克隆出来后的成员变量在同一块空间。
原因:
这时候我们就需要对其深拷贝:
Test类:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Deposit deposit = new Deposit(5000);
Person person1 = new Person("wyh", 13, deposit);
Person person2 = (Person) person1.clone();
person1.setName("tjy");
System.out.println(person1);
System.out.println(person2);
}
}
Deposit类:
public class Deposit implements Cloneable{
double money;
public Deposit(double money) {
this.money = money;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Person类:
public class Person implements Cloneable{
private String name;
private int age;
private Deposit deposit;
public Person(String name, int age, Deposit deposit) {
this.name = name;
this.age = age;
this.deposit = deposit;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Deposit getDeposit() {
return deposit;
}
public void setDeposit(Deposit deposit) {
this.deposit = deposit;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", deposit=" + deposit +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person tmp = (Person) super.clone();
tmp.deposit = (Deposit) this.deposit.clone();
return tmp;
}
}
运行结果:
不再指向同一块区域,问题成功解决。