IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java—爆肝1W字?只为弄懂 抽象类和接口 -> 正文阅读

[Java知识库]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("画一个?");
   }
}

作用

抽象类存在最大的意义:就是为了被继承
抽象类本身不能被实例化,要想使用,必须创建该抽象类的子类,然后让子类重写抽象类中的抽象方法

抽象类总结:

  1. 抽象类不可以被实例化,即 不能:Shape2 shape = new Shape();
  2. 类内的数据成员和普通类没有区别,即:抽象类内部可以包含普通方法和属性,甚至构造方法
    唯一的就是其成员不能被实例化
  3. 抽象类生来就是被继承的
  4. 若一个类继承了抽象类,那么该类必须重写抽象类当中的抽象方法
  5. 抽象类 / 抽象方法一定不能被 final 修饰
  6. 一个方法是抽象方法,那么存放它的类一定要声明成 抽象类
    抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
  7. 当抽象类A 继承抽象类B,A可以不重写B的方法,但一旦A再被继承,那么一定还要重写抽象方法
  8. 抽象方法一定不能被 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 {
    Person person1 = new Person();
    Person person2 = (Person) person1.clone();
    System.out.println(person1.m.money);
    System.out.println(person2.m.money);
    System.out.println("=======修改========");
    person2.m.money = 99.99;
    System.out.println(person1.m.money);
    System.out.println(person2.m.money);
}

在这里插入图片描述
我们可以看到,通过clone,我们只是拷贝了Person对象,但 Person 对象中的 Money 对象,并没有拷贝,通过 person2 这个引用修改了 m 的值后,person1这个引用访问m的时候,值也发生了改变,即发生了浅拷贝

那么如何使它达到一个深拷贝?

这里只贴代码,不做文字解析,大家下去自行理解~

class Money implements Cloneable {
    double money = 13.14;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable {
    public int age;
    Money m = new Money();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //return super.clone();
        //1.克隆 person
        Person p = (Person) super.clone();
        //2.克隆当前 Money对象
        p.m = (Money) this.m.clone();
        return p;
    }
}

在这里插入图片描述
注意: clone 方法本身就是一个浅拷贝,若对象里还有对象,只克隆最外边一层是不能实现克隆的,里边的也需要被克隆,才能实现深拷贝

抽象类和接口的区别?

参数抽象类接口
结构组成普通类 + 抽象方法抽象方法 + 全局变量
子类使用使用extends关键字继承抽象类使用implements关键字实现接口,它需要提供接口中所有声明的方法的实现
访问修饰符抽象方法可以有public、protected和default这些修饰符接口方法默认修饰符是public,不可以使用其它修饰符
多继承抽象方法可以继承一个类和实现多个接口接口不能继承抽象类,但接口可以使用extends关键字继承一个或多个其它接口
速度比接口速度快接口有点慢,因为接口要花费时间去寻找在类中实现的方法
普通方法抽象类中可以包含普通方法和普通字段,这些普通方法、字段可以被子类直接使用(不必重写)接口中不能包含普通方法,子类必须重写所有的抽象方法

该吃饭了~你们加油!!

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-08 10:32:21  更:2021-09-08 10:36:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 7:44:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码