Java—爆肝1W字❤只为弄懂 抽象类和接口
Posted 一朵花花
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java—爆肝1W字❤只为弄懂 抽象类和接口相关的知识,希望对你有一定的参考价值。
抽象类
定义和语法
包含抽象方法的类,叫做抽象类 需要用abstract修饰这个类
在Java中,一个类如果被 abstract 修饰类称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法可以没有具体的实现
抽象类中可以包含其他非抽象方法,也可以包含字段,非抽象方法和普通方法的规则是一样的,可以被重写,也可以被子类直接调用
//抽象类
abstract class Shape2{
// 抽象方法
public abstract void draw();
//抽象类可以增加属性
public int a;
// 抽象类可以增加方法
public void func(){
}
}
理解抽象类
1.不能实例化对象
/* abstract class Shape2{ public abstract void draw(); } */ Shape2 shape = new Shape(); 编译出错 error:shape 是抽象的,无法实例化
2.抽象方法不能为 private的
抽象方法没有加访问限定符时,默认是public
abstract class Shape2{ private abstract void draw(); } 编译出错 error:非法的修饰限定符组合:private + abstract
3.抽象方法不能被 fina l和 static 修饰
因为抽象方法要被子类重写
abstract class Shape { abstract final void methodA(); abstract public static void methodB(); } 编译出错 error:非法的修饰限定符组合:final + abstract 非法的修饰限定符组合:static + abstract
4.抽象类必须被继承
且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,就要使用 abstract修饰
abstract class Shape2{ //如果一个方法没有具体实现,那么这个方法可以是一个抽象方法 public abstract void draw(); //一个方法是抽象方法,那么存放它的类一定要声明成 抽象类 } class Cycle extends Shape2{ @Override public void draw() { System.out.println("画一个⚪"); } } //如果一个类继承了抽象类,那么该类一定要实现抽象类里的方法 class Triangle extends Shape2{ @Override public void draw() { System.out.println("画一个▲"); } } class Flower extends Shape2{ @Override public void draw() { System.out.println("画一个❀"); } }
作用
抽象类存在最大的意义:就是为了被继承
抽象类本身不能被实例化,要想使用,必须创建该抽象类的子类,然后让子类重写抽象类中的抽象方法
抽象类总结:
- 抽象类不可以被实例化,即 不能:Shape2 shape = new Shape();
- 类内的数据成员和普通类没有区别,即:抽象类内部可以包含普通方法和属性,甚至构造方法
唯一的就是其成员不能被实例化 - 抽象类生来就是被继承的
- 若一个类继承了抽象类,那么该类必须重写抽象类当中的抽象方法
- 抽象类 / 抽象方法一定不能被 final 修饰
- 一个方法是抽象方法,那么存放它的类一定要声明成 抽象类
抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类 - 当抽象类A 继承抽象类B,A可以不重写B的方法,但一旦A再被继承,那么一定还要重写抽象方法
- 抽象方法一定不能被 final 和 static 修饰
接口
概念
在现实生活中,接口的例子很多,比如电脑的USB接口,电源插座…
电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备
电源插座插孔上,可以插:电脑、电视机、电饭煲…所有符合规范的设备
通过上述例子可以看出:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型
接口是抽象类的更进一步,抽象类中还可以包含非抽象方法,和字段,而接口中包含的方法都是抽象方法,字段只能包含静态常量
接口特性
1.接口当中的方法,都是抽象方法
2.接口中可以有具体实现的方法,需要用default修饰,JDK1.8 加入的
.
3.接口中定义的成员变量,默认是常量
.
4.接口是不可以用来实例化的
.
5.接口和类的关系:implements
一旦一个类实现了接口,那么一定要重写接口当中的方法
.
6.可解决Java单继承问题
.
7.可发生向上转型
.
前提是:把一个对象赋值给接口类型之前,一定要保证这个类实现了这个接口
例如下边代码:Cycle实现了Shape,故可以发生向上转型
.
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
.
注意事项:
- 使用 interface 定义一个接口
- 接口中的方法一定是抽象方法,故 abstract 可以省略
接口中的方法一定格式 public,故 public 可以省略 - 类使用 implements 继承接口,此处的含义是"实现"
- 接口不能被实例化
- 只要这个类实现了该接口,那么就可发生向上转型
- 一旦一个类实现了接口,那么一定要重写接口当中的方法
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
实现多个接口
有时,我们需要一个类同时继承多个父类,在有些编程语言中可通过多继承
方式来实现,但Java中,只支持"单继承",一个类只能 extends 一个父类,但可同时实现多个接口,也能达到类似多继承的效果
Java 面向对象编程中最常见的用法:一个类继承一个父类,同时实现多种接口
代码理解:
class Animal{
protected String name;
//提供构造方法
public Animal(String name){
this.name = name;
}
}
//定义接口
interface IFlying{
void fly();
}
interface IRunning{
void run();
}
interface ISwimming{
void swim();
}
//猫
// 先 extends 后 implements
class Cat extends Animal implements IRunning {
public Cat(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在用四条腿跑");
}
}
//鱼
class Fish extends Animal implements ISwimming {
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(this.name + "正在用尾巴游泳");
}
}
//青蛙
class Frog extends Animal implements IRunning, ISwimming {
public Frog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在往前跳");
}
@Override
public void swim() {
System.out.println(this.name + "正在蹬腿游泳");
}
}
//鸭子
class Duck extends Animal implements IRunning, ISwimming,IFlying{
public Duck(String name){
super(name);
}
@Override
public void fly() {
System.out.println(this.name + "正在飞飞飞");
}
@Override
public void run() {
System.out.println(this.name + "正在跑啊跑");
}
@Override
public void swim() {
System.out.println(this.name + "正在游啊泳");
}
}
猫🐱:具有跑能力
鱼🐟:具有游泳能力
青蛙🐸:具有跑、游泳能力
鸭子🦆:具有飞、跑、游泳能力
.
可见,把 run,swim,fly 写在Animal里是不合适的,因为不是每一种动物都具备这些能力
接口主要是对方法的一个抽象,使人时刻牢记多态的好处,让程序猿忘记类型,有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力
再看一个例子:
//机器人
class Robot implements IRunning{
@Override
public void run() {
System.out.println("我是机器人,看谁跑得快!");
}
}
public class InterfaceDemo2 {
public static void walk(IRunning running) {
System.out.println("dudududdududu");
running.run();
}
public static void main(String[] args) {
IRunning iRunning = new Robot();
//iRunning.run();
walk(iRunning);
}
}
输出结果:
在上述 walk 方法内部,我们并不关注到底是哪种动物,只要参数是会跑的就可以,甚至参数可以不是 “动物”,只要会跑即可
接口使用实例
给一个数组排序,直接使用Array.sort即可
public static void main(String[] args) { int[] array = {8,6,2,3,4}; Arrays.sort(array); System.out.println(Arrays.toString(array)); }
假设给定一个学生类 Student
class Student {
public String name;
public int age;
public int score;
public Student(String name,int age,int score){
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\\'' +
", age=" + age +
", score=" + score +'}';
}
}
再创建一个学生数组,对这个数组中的元素进行排序
public static void main(String[] args) {
Student student1 = new Student("花",18,100);
Student student2 = new Student("A",20,95);
Student student3 = new Student("B",23,88);
Student[] students = new Student[3];
students[0] = student1;
students[1] = student2;
students[2] = student3;
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
思考:如果使用 Array.sort 方法能否成功对 student 数组排序?
显然,是不可以的 ,类型转换异常
普通数组,是可以使用 sort 方法进行比较数组成员的,但上述比较的是两个学生对象,就不能直接进行比较了,需要我们实现 Comparable 接口,并实现其中的 compareTo 方法
鼠标放在 implements 后按 Alt + Enter
选择 Implement methods
选定之后,会出现如下代码,只需自己重新实现即可
以年龄比较为例:
class Student implements Comparable<Student> {
public String name;
public int age;
public int score;
public Student(String name,int age,int score){
this.name = name;
this.age = age;
this.score = score;
}
@Override
public int compareTo(Student o) {
if (this.age > o.age){
return 1;
}
else if(this.age < o.age){
return -1;
}
else{
return 0;
}
}
}
public static void main(String[] args) {
Student student1 = new Student("花",18,100);
Student student2 = new Student("A",20,95);
Student student3 = new Student("B",17,88);
if(student1.compareTo(student2) < 0) {
System.out.println("student1的年龄 < student2的年龄");
}
}
这样便可以根据年龄对 Student 进行比较了
此时,再加上 sort 方法,也可实现
public static void main(String[] args) {
Student student1 = new Student("花",18,100);
Student student2 = new Student("A",20,95);
Student student3 = new Student("B",17,88);
Student[] students = new Student[3];
students[0] = student1;
students[1] = student2;
students[2] = student3;
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
会发现输出结果,是以年龄由小到大排列的
在 sort 方法中会自动调用 compareTo 方法,compareTo 的参数是 Object,其实传入的就是 Student 类型的对象
自定义类型要可比较,必须实现 comparable 或 comparator 接口
Clonable 接口和深拷贝
Clonable 是 Java 内置的一个接口
Object 类中存在一个 clone 方法,调用这个方法可以创建一个对象的 “拷贝”,但是要想合法调用 clone 方法,必须要先实现 Clonable 接口,否则就会抛出 CloneNotSupportedException 异常
数组的拷贝:
public static void main(String[] args) {
int[] array = {8,5,2,7,4,6,9};
int[] array2 = array.clone();
array2[0] = 66;
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(array2));
}
由输出结果,可得,上述拷贝为深拷贝
理解浅拷贝:
两个引用同时指向一个对象
通过array2[0] ,把person里的某一个数据修改了,那么array[0]去访问这个数据时,也被修改了!
要想达到深拷贝,不仅要克隆数组本身,还要把对象也克隆一份
举例:
而上述的 array 可使用 clone 方法,由于 array 的父类是 object,即相当于 array 是默认继承于 object,故 array就有clone方法
当前的person也是默认继承于object,但 person 是自定义类型,不能直接使用 clone 方法
若想克隆自定义类型:
1.实现接口 Cloneable
class Person implements Cloneable {
public int age;
}
但会发现,Cloneable 是一个空接口
空接口 也叫做:标记接口
含义:只要一个类实现了这个接口,那么就标记这个类是可以进行clone的
2.重写 clone 方法,默认的即可
class Person implements Cloneable {
public int age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
此时person就拥有了clone方法
选中之后会有报错,鼠标放在clone后,按Alt + Enter,选异常(此处随便选一个,后面会详细讲解异常)
再次报错!!!
分析:clone 方法的返回值是 object,因此需要进行强制转换
此时就可以验证深拷贝了:
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
System.out.println(person1.age);
System.out.println(person2.age);
System.out.println("=======修改========");
person2.age = 52;
System.out.println(person1.age);
System.out.println(person2.age);
}
输出结果:
可以发现,拷贝后,没有发生改变,考虑 age是简单类型的原因,
再新写一个 Money类
Cloneable 拷贝出的对象是一份 “浅拷贝”
class Money{
double money = 13.14;
}
class Person implements Cloneable {
public int age;
Money m = new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
验证浅拷贝:
public static void main(String[] args) throws CloneNotSupportedException Java 基础语法爆肝两万字解析 Java 的多态抽象类和接口