接口"/>
《JAVASE系列》抽象类与接口
《JAVASE系列》抽象类与接口
文章目录
- 《JAVASE系列》抽象类与接口
- 前言
- 1.抽象类
- 1.1抽象类是什么?
- 1.2 抽象的语法与细节
- 1.3抽象类的意义与作用
- 2.接口
- 2.1 接口是什么?
- 2.2 接口的语法于细节
- 2.3三个重要的接口(重点)
- 2.3.1 Clonable接口与深拷贝
- 2.3.2 接口Comparable<T>
- 2.3.3比较器comparator<T>
- 3. 抽象类与接口的区别
- 4. Object类
- 4.1equals方法
- 总结:
前言
本章重点讲解java语言中的抽象类与接口,本章依然是javase的重点难点
参考书籍:《java核心技术卷1》
你若盛开,清风自来。
1.抽象类
1.1抽象类是什么?
在我们之前学习类的继承与多态的时候,我们会注意到,我们希望子类去继承父类的方法并发生重写与向上转型来实现多态,我们为了编译器不报错,每次都实现了父类的方法。
例如:
class Animal{public void eat(){System.out.println("动物吃");}
}
class Dog extends Animal{public void eat(){System.out.println("吃骨头");}
}
public class Main{public static void main(String[] args) {Animal animal = new Dog();animal.eat();}
}
我们不得不去写Animal这个父类的eat方法。当我们希望父类只是用来被实现多态,就显得不是那么方便了。因此引入了抽象类的概念,使得抽象类成为一个单纯为了实现继承与实现多态的类。
抽象类概念:
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
1.2 抽象的语法与细节
关键字 abstract实现抽象类
被abstract 修饰的类就是抽象类,被abstract修饰的方法就是抽象方法
abstract class Animal{private int age;private String name;public void sleep(){System.out.println("睡大觉");}public static void hehe(){System.out.println("笑一笑");}abstract public void eat();
}class Dog extends Animal{public void eat(){System.out.println("吃骨头");}}
public class Main{public static void main(String[] args) {Animal animal = new Dog();animal.eat();}
}![在这里插入图片描述](.png#pic_center)class Dog extends Animal{public void eat(){System.out.println("吃骨头");}}
public class Main{public static void main(String[] args) {Animal animal = new Dog();animal.eat();}
}
-
可见,一个类被abstract修饰后变为抽象类,里面的成员变量与成员方法与普通类一样。
-
我们通过抽象类来实现向上转型,成功地实现了多态。
-
但是抽象类不能被实例化
-
并且继承该抽象类的子类必须重写父类的所有抽象方法,否则编译不通过。
值得注意的是:
- abstract 修饰的方法的目的是为了被子类实现重写进而实现多态,而final关键字修饰的方法则不能重写,所以不能出现以下代码:
abstract public final void eat();
报错信息:(比较直接明了)
-
abstract修饰的方法也不能用private修饰,不能将抽象方法定义为私有的,要不然如何实现向上转型呢?所以不能出现以下代码:
abstract private void eat();
报错信息:(比较直接明了)
抽象类继承抽象类
抽象类是可以继承抽象类的。
abstract class Animal{private int age;private String name;public void sleep(){System.out.println("睡大觉");}public static void hehe(){System.out.println("笑一笑");}abstract public void eat();
}
abstract class mammal extends Animal{abstract public void run();
}
class dog extends mammal{public void eat(){System.out.println("狗狗吃骨头");}public void run(){System.out.println("狗狗在跑");}
}
当一个抽象类A继承了抽象类B,可以不重写抽象类B的抽象方法。
普通类C继承了A,就得重写A和B的抽象方法(欠下的债,迟早得还)
1.3抽象类的意义与作用
抽象的意义是为了被继承,实现多态。
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
使用抽象类相当于多了一重编译器的校验.
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类的时候,编译器是不会报错的. 如果父类是抽象类的时候,就会在实例化的时候提示错误, 让我们尽早发现问题.
2.接口
2.1 接口是什么?
接口在我们日常生活中比比皆是,例如USB插口,电源插口,可以说这是具有规范的接口。
也就是说接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
2.2 接口的语法于细节
关键字Interface实现接口
语法注意:
接口中的语法于细节
- 接口名一般以大写字母 I 开头
- 接口中的成员方法只能是抽象方法,所以的方法默认都是public abstract
- 接口中的成员变量默认为public static final,也就是说只能为常量,并且必须初始化
- 接口中也可以实现方法,但是必须用default修饰。
- 接口中的静态方法可以有具体的实现。
- 接口不能进行实例化对象,也不能有构造方法。
- 接口中不能有静态代码块
interface Itest{public static final int c = 20; //正确定义方式,注意初始化int a = 10; // 默认实现方式default void method2(){ //default修饰的接口普通方法System.out.println("接口默认方法");}public abstract void method1(); //正确定义接口的抽象方法void method(); //默认实现方式public static void method3(){ //接口的静态方法实现System.out.println("接口静态方法");}}
接口使用时的语法与细节:
-
普通类可以通过implements继承接口。
-
一个类可以继承一个抽象类,同时实现多个接口
-
接口也可以实现向上转型,进而实现多态
-
类和接口之间是implements,接口与接口之间是extends(拓展),接口与接口之间用逗号隔开
interface Itest2{public abstract void heihei();
}
interface Itest1{public abstract void haha();
}
interface Itest extends Itest1,Itest2{public static final int c = 20; //正确定义方式int a = 10; // 默认实现方式default void method2(){ //default修饰的接口普通方法System.out.println("接口默认方法");}public abstract void method1(); //正确定义接口的抽象方法void method(); //默认实现方式public static void method3(){ //接口的静态方法实现System.out.println("接口静态方法");}}
abstract class Animal{private int age;private String name;public void sleep(){System.out.println("睡大觉");}public static void hehe(){System.out.println("笑一笑");}abstract public void eat();
}
abstract class mammal extends Animal{abstract public void run();
}
class Dog extends mammal implements Itest{public void eat(){System.out.println("狗狗吃骨头");}public void run(){System.out.println("狗狗在跑");}@Overridepublic void method1() {System.out.println("Itext接口的抽象方法重写");}@Overridepublic void method() {System.out.println("Itext接口的抽象方法重写");}public void haha(){System.out.println("Itext1的接口抽象方法重写");}public void heihei(){System.out.println("Itest2的接口抽象方法重写");}
}
2.3三个重要的接口(重点)
2.3.1 Clonable接口与深拷贝
一个对象要能够被克隆,需要先有克隆接口。
class Student implements Cloneable{private String name;private int age;private double sorce;public void eat(){System.out.println("吃饭");}public void study(){System.out.println("学习");}public void sleep(){System.out.println("睡觉");}public Student clone(){}
}
我们可以查看这个接口内容
可以看到这个接口中没有任何内容。Cloneable 也就是作为一个标记接口的作用存在,它的作用是使得对象已被标记,是可克隆对象。
我们需要重写的是Object的克隆方法,并且抛出处理克隆异常。
class Dog implements Cloneable{public void eat(){System.out.println("狗狗吃骨头");}public Object clone()throws CloneNotSupportedException{return super.clone();}
}
但是如果认为克隆就如此简单,那就错误了,克隆并不是那么聪明,当需要克隆的对象中含有引用成员变量时,则容易出现问题。
例如:
class Dog implements Cloneable{public void eat(){System.out.println("狗狗吃骨头");}public Object clone()throws CloneNotSupportedException{return super.clone();}
}
class Animal implements Cloneable{public int age;public Dog dog = new Dog();Animal(){System.out.println("构造方法");}public void method(){System.out.println("普通方法");}public Object clone() throws CloneNotSupportedException{return super.clone();}
}
public class Main {public static void main(String[] args) throws CloneNotSupportedException {Animal animal = new Animal();Animal animal1 = (Animal) animal.clone();System.out.println(animal.dog);System.out.println(animal1.dog);}
}
运行结果:
这两个对象中的引用成员变量同时指向了同一个对象,与我们想要克隆的想法大相径庭
所以我们需要实现深拷贝,深拷贝是在代码层面上完成的
public Object clone() throws CloneNotSupportedException{Animal animal = (Animal) super.clone();animal.dog = (Dog) this.dog.clone();return animal;}
这样我们地让每一次克隆时,dog这个变量都不会指向同一个对象,达到克隆的目的。
2.3.2 接口Comparable
在java中,我们不能直接用对象进行比较,因为java不支持地址比较大小。
我们想要利用对Array.sort()进行对对象进行排序也显得困难。
为此引入了comparable接口,来实现对对象之间的比较。
为什么String成员能进行排序呢?
查看String的源码:
String类已经实现了comparable接口了。
comparable接口:
我们在实现了Comparable接口,需要重写compareTo方法来实现比较的功能:
如果返回值大于0,则对象1>对象2
如果返回值等于0,则对象1=对象2
如果返回值小于0,则对象1<对象2
例如:实现对象之间年龄的比较与排序:
class Student implements Comparable<Student>{private String name;private int age;private double sorce;Student(String name,int age,double sorce){this.age = age;this.name = name;this.sorce = sorce;}@Overridepublic int compareTo(Student o) {return this.age - o.age;}public void print1(){System.out.println(name+" "+age+" "+sorce);}
}
public class Main {public static void main(String[] args) {Student student1 = new Student("jiajia",21,88.5);Student student2 = new Student("jiejie",22,88.9);if(student1.compareTo(student2)>0){System.out.println("yes");}else{System.out.println("NO");}Student[] students = new Student[3];students[0] = new Student("xiaobai",22,18.5);students[1] = new Student("xiaoming",20,18.5);students[2] = new Student("xiaoqiang",21,18.5);Arrays.sort(students);for (int i = 0; i < 3; i++) {students[i].print1();}}
}
成功地实现了比较与排序的功能。
但是它的缺点也十分明显,直接在类中实现Comparable接口并重写compareTo方法,对类的侵入性十分强。
2.3.3比较器comparator
为了避免对对象的比较时,对类产生侵略性,所以我们可以采用比较器的方式来实现。
实现对学生类的年龄比较
class Agecompare implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {return o1.getAge()-o2.getAge();}
}
进而直接用比较器实现对象之间的比较与排序。
class Student{private String name;private int age;private double sorce;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 double getSorce() {return sorce;}public void setSorce(double sorce) {this.sorce = sorce;}Student(String name, int age, double sorce){this.age = age;this.name = name;this.sorce = sorce;}public void print1(){System.out.println(name+" "+age+" "+sorce);}
}
class Agecompare implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {return o1.getAge()-o2.getAge();}
}
public class Main {public static void main(String[] args) {Student student1 = new Student("jiajia",21,88.5);Student student2 = new Student("jiejie",22,88.9);Agecompare agecompare = new Agecompare();if(agecompare.compare(student1,student2)>0){System.out.println("yes");}else{System.out.println("NO");}Student[] students = new Student[3];students[0] = new Student("xiaobai",22,18.5);students[1] = new Student("xiaoming",20,18.5);students[2] = new Student("xiaoqiang",21,18.5);Arrays.sort(students,agecompare);for (int i = 0; i < 3; i++) {students[i].print1();}}
}
3. 抽象类与接口的区别
抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别(重要!).
核心区别:
抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.
抽象类存在的意义是为了让编译器更好的校验, 像 Animal 这样的类我们并不会直接使用, 而是使用它的子类.万一不小心创建了 Animal 的实例, 编译器会及时提醒我们.
4. Object类
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。进而实现多态也是可以的。
4.1equals方法
equals方法是Object类的重要方法,他做到了直接实现对象的比较,例如String类的比较。
在java当中的比较:(重点)
在Java中,==进行比较时:
-
如果==左右两侧是基本类型变量,比较的是变量中值是否相同
-
如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同
-
如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:
查看Object类中的equal源码:
我们如果要做到对象之间进行比较,需要重写equals方法
如果没有重写:即便两个对象的属性相同,返回的结果也是flase
// Object类中的toString()方法实现:public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
// Object类中的equals方法public boolean equals(Object obj) {return (this == obj); // 使用引用中的地址直接来进行比较}
class Person{private String name ; private int age ; public Person(String name, int age) {this.age = age ; this.name = name ;}
}
public class Test {public static void main(String[] args) {Person p1 = new Person("gaobo", 20) ; Person p2 = new Person("gaobo", 20) ; int a = 10;int b = 10;System.out.println(a == b); // 输出trueSystem.out.println(p1 == p2); // 输出falseSystem.out.println(p1.equals(p2)); // 输出false}
}
重写equals方法:
class Person{private String name ; private int age ; public Person(String name, int age) {this.age = age ; this.name = name ;}@Overridepublic boolean equals(Object obj) {if (obj == null) {return false ; }if(this == obj) {return true ; }// 不是Person类对象if (!(obj instanceof Person)) {return false ; }Person person = (Person) obj ; // 向下转型,比较属性值return this.name.equals(person.name) && this.age==person.age ; }
}
public class Test {public static void main(String[] args) {Person p1 = new Person("gaobo", 20) ; Person p2 = new Person("gaobo", 20) ; int a = 10;int b = 10;System.out.println(a == b); // 输出trueSystem.out.println(p1 == p2); // 输出flaseSystem.out.println(p1.equals(p2)); // 输出true}
}
总结:
本章主要注意抽象类与接口的细节语法,以及两者的差别,同时学习三个常用接口。equals方法等
感谢阅读,一起进步!
更多推荐
《JAVASE系列》抽象类与接口
发布评论