当前位置: 首页 > news >正文

【JavaSE】抽象类和接口

【JavaSE】抽象类和接口

    • 前言:补充知识 —— 利用类和对象,交换两个数字
  • 一、抽象类
    • 1.1 抽象类是什么?
    • 1.2 抽象类特点
    • 1.3 抽象类举例
    • 1.4 抽象类作用
  • 二、接口
    • 2.1 接口是什么?
    • 2.2 接口的特性
    • 2.3 接口的使用规则
    • 2.4 类可以实现多个接口
    • 2.5 接口之间的继承
    • 2.6 常用接口举例(Java自带的)
      • 2.6.1 Comparable 接口 和 Comparator 接口(与 比较有关的接口)
      • 2.6.2 Clonable 接口
    • 2.7 ==扩展 —— 深拷贝和浅拷贝==
  • 三、抽象类和接口的区别
  • 四、Object 类
    • 4.1 所有类的对象都可以使用Object的引用进行接收
    • 4.2 toString()方法:用来获取对象信息
    • 4.3 equals()方法:用来判断2个对象是否相等
    • 4.4 hashcode()方法

前言:补充知识 —— 利用类和对象,交换两个数字

class Myvalue{public int val;
}
public class Test {public static void swap(Myvalue myvalue1,Myvalue myvalue2){int tmp = myvalue1.val;myvalue1.val = myvalue2.val;myvalue2.val = tmp;}public static void main(String[] args) {Myvalue myvalue1 = new Myvalue();myvalue1.val = 10;Myvalue myvalue2 = new Myvalue();myvalue2.val = 20;System.out.println("交换前:"+ myvalue1.val + " " + myvalue2.val);swap(myvalue1,myvalue2);System.out.println("交换后:"+ myvalue1.val + " " + myvalue2.val);}
}

一、抽象类

1.1 抽象类是什么?

抽象类定义:如果一个类不能描述一个具体的对象的时候,我们就把它称为对象类。

1.2 抽象类特点

1) 抽象类,一般使用关键字abstract 来修饰

在这里插入图片描述

2 )抽象类,不能通过new关键字来实例化对象

在这里插入图片描述

3 )抽象类中,如果一个方法 没有具体的实现,那么就把这个方法修改为abstract修饰。

在这里插入图片描述

4 )如果一个类有抽象方法,那么该类必须是抽象类;相反,如果一个类是抽象类,那么可以没有抽象方法。

在这里插入图片描述

在这里插入图片描述

5 )如果一个类继承了这个抽象类,那么必须重写这个抽象类当中的抽象方法。

在这里插入图片描述

6) 如果一个类 继承了这个抽象类,但是不想重写抽象类当中的方法,只能把当前这个类也设置为抽象类。 但是需要注意的是,这些抽象方法最终都必须被重写! 只要有其他的普通类继承这些抽象类,都会被重写。

在这里插入图片描述

7 )抽象类和普通类的区别

几乎没区别,抽象类和普通类都可以定义成员变量和构造方法。

(1)抽象类不能实例化,普通类可以实例化。

(2)只是抽象类可以定义抽象方法。

8 ) 抽象方法不能是 private 的,抽象方法不能被final和static修饰,因为抽象方法要被子类重写。

1.3 抽象类举例

画图案

在这里插入图片描述

1.4 抽象类作用

抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。

那么我们思考一个问题:如果是这样的话,普通的类也可以被继承 普通的方法也可以被重写, 为什么要用非得用抽象类和抽象方法呢?

确实如此, 但是使用抽象类相当于多了一重编译器的校验。

使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成。
那么此时如果不小心误用成父类了,使用普通类编译器是不会报错的,但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题。

二、接口

2.1 接口是什么?

在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等。
在这里插入图片描述
如上图所示,电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备。

通过上述例子可以看出:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

2.2 接口的特性

1)接口使用 interface 关键字来定义
在这里插入图片描述
2)接口不能被实例化

在这里插入图片描述

3)接口中定义的成员变量,默认是public static final修饰的,不写的时候也是这样。

在这里插入图片描述

4)接口当中的方法,如果要有具体的实现,只能被 static 或者 default 修饰

在这里插入图片描述

5)接口当中的方法,如果没有具体的实现,那么就写为抽象方法,即 public abstract 修饰的方法,此时这个方法默认就是 public abstract 。

在这里插入图片描述

6)一个类 和 接口的关系:使用 implements 实现,

7)实现该接口以后,就要重写该接口当中的抽象方法

在这里插入图片描述

8)在main方法中调用接口IShape

在这里插入图片描述
输出结果为:

在这里插入图片描述

2.3 接口的使用规则

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。

请实现笔记本电脑使用USB鼠标、USB键盘的例子

  1. USB接口:包含打开设备、关闭设备功能
  2. 笔记本类:包含开机功能、关机功能、使用USB设备功能
  3. 鼠标类:实现USB接口,并具备点击功能
  4. 键盘类:实现USB接口,并具备输入功能

// USB接口
在这里插入图片描述

// 鼠标类,实现USB接口

在这里插入图片描述

// 键盘类,实现USB接口

在这里插入图片描述

// 笔记本类:使用USB设备
在这里插入图片描述

// 测试类:
在这里插入图片描述

如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类

在这里插入图片描述

2.4 类可以实现多个接口

  • Animal 类
    在这里插入图片描述
  • Dog类

在这里插入图片描述

  • Bird类
    在这里插入图片描述

  • IFlyable接口
    在这里插入图片描述 - IRunnable接口在这里插入图片描述

  • ISwimmable接口
    在这里插入图片描述

问题1:能不能把刚刚写的 3个接口 改为类,把这些功能写到类里面?

答:不可以,因为Java只能单继承,不能多继承。(不能继承多个类,但是可以实现多个接口)
所以,接口的出现,可以实现多继承。

问题2:不定义IFlyable接口,在Bird类写fly()方法 与 定义IFlyable接口,Bird类实现接口,重写fly()方法的区别?(接口的好处是什么?)
答:如果有更多的类,不用每个类里单独实现fly()方法,可以实现代码复用。
(以新建的Duck类为例)

  • TestDemo类

在这里插入图片描述
对于接口来说,有几个接口,参数就能换几次
在这里插入图片描述

  • 新建Duck类
    在这里插入图片描述
  • TestDemo类中利用接口,实现多态
    在这里插入图片描述

接口 和 定义方法 的区别:
有了接口之后, 让程序员忘记类型。类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力。

在这里插入图片描述

2.5 接口之间的继承

接口间的继承相当于把多个接口合并在一起。

在这里插入图片描述
在这里插入图片描述

2.6 常用接口举例(Java自带的)

2.6.1 Comparable 接口 和 Comparator 接口(与 比较有关的接口)

  1. 实例 1:比较两个学生的成绩大小

在这里插入图片描述

通过大于号 小于号进行比较的时候,此时不能直接比较引用类型
编译器 要求:给出一个比较大小的规则(可以使用接口)

1)通过 Comparable接口 ,给出比较规则:Comparable 接口是 默认自定义接口

  • Student 类

在这里插入图片描述
在这里插入图片描述

  • 测试类Test

在这里插入图片描述

  • 输出结果

在这里插入图片描述

2)Comparator 接口:需要自己定义比较器,对类的侵入性比较低

但是上面的比较,只能用年龄比较。如果需要按照成绩比较,就不能直接在重写的compareTo方法上更改!
因此我们使用Comparator 接口,自己定义比较器 进行比较

  • Student 类
    在这里插入图片描述在这里插入图片描述

  • AgeComparator 接口
    在这里插入图片描述

  • ScoreComparator 接口
    在这里插入图片描述

  • NameComparator 接口
    在这里插入图片描述

  • 测试类Test
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 输出结果
    在这里插入图片描述

  1. 实例 2:对自定义类型数组,进行排序

(1) 对自定义类型数组,进行排序(利用Comparable 接口)

  • Student 类
    在这里插入图片描述
    在这里插入图片描述

  • Test 测试类

在这里插入图片描述

  • 运行Test,输出结果:
    在这里插入图片描述
  • 注释掉,Comparable接口 和 重写的compareTo方法
    在这里插入图片描述
  • 再次运行Test,报错:类型转换异常

在这里插入图片描述

利用 Arrays.sort(students);对自定义类型进行排序,一定会涉及两个对象之间的比较,需要一些比较规则(比如上述中的 Comparable 接口)
这就是实现这些接口的重要性,尤其是自定义类型一定能比较(利用Comparable接口 或者 自定义比较器 )

(2) 对自定义类型数组,进行排序( 自己实现Arrays.sort() )

  1. 利用 默认的Comparable 接口
  • Student 类
    在这里插入图片描述
    在这里插入图片描述

  • Test 测试类
    在这里插入图片描述
    在这里插入图片描述

  • 运行Test,输出结果:

学生按照年龄排序,谁小,谁在前。

在这里插入图片描述

那我们如果想实现:依然按照年龄排序,但 谁年龄大谁在前呢?

  • 方式1:修改 自定义的bubbleSort() 逻辑,其余的不变

-

输出结果:

在这里插入图片描述

  • 方式2:修改 重写的compareTo() 方法,其余的不变

在这里插入图片描述
输出结果:

在这里插入图片描述

但默认的Comparable 接口只能按照年龄比较大小,如果我们需要按照 分数、 姓名比较大小呢?
那么就需要用到我们之前自定义的Comparator 比较器了(ScoreComparator / NameComparator / )

  • 利用 自定义的Comparator 比较器:按照分数比较
    在这里插入图片描述
    在这里插入图片描述
    输出结果:
    在这里插入图片描述
  1. Arrays.sort()在实现排序的时候,存在优先级

Arrays.sort()在实现排序的时候,存在优先级:
第一优先级,是根据当前类 实现的 comparable接口,进行排序。
但是,一旦传入了想要的比较方式,就按照传入的比较器 进行比较。

  • 同理可得,根据 姓名进行比较大小

在这里插入图片描述

输出结果:

在这里插入图片描述

2.6.2 Clonable 接口

Java 中内置了一些很有用的接口,Clonable 就是其中之一。

Object 类中存在一个 clone 方法,调用这个方法可以创建一个对象的 “拷贝”。
但要想合法调用 clone方法, 必须要 先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常。

1.Clonable 接口,实例1

  • Person 类

在这里插入图片描述

  • Test 类 :将person1这个引用 所指的对象,利用clone方法 克隆一份(但报错)
    在这里插入图片描述

  • 之前学到过,所有的类 都默认继承 父类Object
    在这里插入图片描述
    在这里插入图片描述被 protected 修饰:如果是不同包(clone方法在java.lang包,Test类在demo2包下),只能在子类当中访问(可以在Person类和Test类 访问父类的clone方法)。

上面的报错,是因为:

虽然两个类都继承于Object类(java.lang包下),但是克隆的时候,不是在Person (demo包下)中访问,而在Test类(demo包下)通过person1这个引用(是Person类下)在调用,所以无法调用到clone方法。

  • 所以,需要在Person类中重写 clone方法。

在这里插入图片描述

间接访问:Person类中重写 clone方法

在这里插入图片描述

  • 克隆的3个注意事项

    1. protected 关键字:
    2. 向下转型
    3. 必须实现克隆接口

在这里插入图片描述

在这里插入图片描述

2.Clonable 接口,实例 2

  • Person 类

在这里插入图片描述

  • Test 类

在这里插入图片描述

输出结果:

在这里插入图片描述

2.7 扩展 —— 深拷贝和浅拷贝

1. 浅拷贝:Cloneable 拷贝出的对象是一份 “浅拷贝”

  • Person 类:新增加成员变量m

在这里插入图片描述
在这里插入图片描述

  • Test 类中,修改 person2.m.money = 99.5

在这里插入图片描述

发现修改 person2.m.money = 99.5,把 person1.m.money 也变成了 99.5。

结论:
通过clone,我们只是拷贝了Person对象。但是Person对象中的Money对象,并没有拷贝。
通过person2这个引用修改了m的值后,person1这个引用访问m的时候,值也发生了改变。这里就是发生了浅拷贝。

那么如何实现深拷贝呢?

2. 深拷贝

  • Person 类
    在这里插入图片描述
    在这里插入图片描述

  • Test 类

在这里插入图片描述

输出结果:

在这里插入图片描述

3. 深拷贝与浅拷贝 图解

  • 浅拷贝图解

在这里插入图片描述

  • 深拷贝图解

在这里插入图片描述

  • 深拷贝 代码解析:

在这里插入图片描述
在这里插入图片描述

三、抽象类和接口的区别

抽象类和接口都是 Java 中多态的常见使用方式.。

区别抽象类接口
(1)结构组成抽象类 + 抽象方法抽象方法 + 全局常量
(2)权限不限public
(3)子类使用使用extends关键字继承抽象类使用implements关键字实现接口
(4)关系一个抽象类可以实现若干个接口接口不能继承抽象类,但是接口可以使用extends关键字继承多个父借口
(5)子类限制一个子类只能继承一个抽象类一个子类可以实现多个接口

具体解析:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

// 抽象类
abstract class Animal {protected String name;  // 普通字段public static final int LEGS = 4; // 静态常量
}// 接口
interface Runnable {int MAX_SPEED = 100;  // 默认 public static final// String name;       // 错误!不能有普通字段
}

在这里插入图片描述

// 抽象类
abstract class Animal {public void eat() {    // 普通方法System.out.println("Eating...");}public abstract void makeSound(); // 抽象方法
}// 接口(Java8前)
interface Runnable {void run();  // 默认 public abstract// void stop() {}  // 错误!不能有实现
}

在这里插入图片描述

// 抽象类和接口都不能直接实例化
Animal a = new Animal();  // 错误!
Runnable r = new Runnable();  // 错误!// 只能通过子类/实现类实例化
Animal cat = new Cat();    // 假设Cat继承Animal
Runnable rabbit = new Rabbit(); // 假设Rabbit实现Runnable

在这里插入图片描述
在这里插入图片描述
举例:

interface Walkable {default void walk() {  // 默认方法System.out.println("Walking...");}static void printInfo() { // 静态方法System.out.println("Walkable interface");}
}

四、Object 类

Object是Java默认提供的一个类。
Java里面除了Object类,所有的类都是存在继承关系的,默认会继承Object父类。
即所有类的对象都可以使用Object的引用进行接收。

4.1 所有类的对象都可以使用Object的引用进行接收

Object 类,实例1:使用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类是参数的最高统一类型。
但是Object类也存在有定义好的一些方法。

在这里插入图片描述

4.2 toString()方法:用来获取对象信息

如果要打印对象中的内容,可以直接重写Object类中的toString()方法。

// Object类中的toString()方法实现:
public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

4.3 equals()方法:用来判断2个对象是否相等

在Java中,进行比较时:( 区别 == 与equals

a.如果“==”左右两侧是基本类型变量,比较的是变量中值是否相同

b.如果“==”左右两侧是引用类型变量,比较的是引用变量地址是否相同

c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:
在这里插入图片描述

区别于comparable接口 和 comparator接口:

在这里插入图片描述

4.4 hashcode()方法

  1. 回忆刚刚的toString方法的源码:
public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

我们看到了hashCode()这个方法,他帮我算了一个具体的对象位置,这里面涉及数据结构,可以说它是个内存地址。然后调用Integer.toHexString()方法,将这个地址以16进制输出。

  1. hashcode方法源码:
public native int hashCode();

该方法是一个native方法,底层是由C/C++代码写的。
我们认为两个名字相同,年龄相同的对象,将存储在同一个位置,如果不重写hashcode()方法,我们可以来看示例代码,可以发现两个对象的hash值不一样

class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}
}
public class TestDemo4 {public static void main(String[] args) {Person per1 = new Person("zhangsan", 20) ;Person per2 = new Person("zhangsan", 20) ;System.out.println(per1.hashCode());System.out.println(per2.hashCode());}
}
//执行结果
460141958
1163157884
  1. 像重写equals方法一样,我们也可以重写hashcode()方法。此时再来看看,哈希值一样
class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;
}
@Override
public int hashCode() {return Objects.hash(name, age);}
}
public class TestDemo4 {public static void main(String[] args) {Person per1 = new Person("gaobo", 20) ;Person per2 = new Person("gaobo", 20) ;System.out.println(per1.hashCode());System.out.println(per2.hashCode());}
}
//执行结果
460141958
460141958

结论:
1、hashcode方法用来确定对象在内存中存储的位置是否相同。
2、事实上hashCode() 在散列表中才有用,在其它情况下没用。
在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。


http://www.mrgr.cn/news/95969.html

相关文章:

  • uniapp页面列表,详情返回不刷新,新增或编辑后返回刷新
  • mysql中show命令的使用
  • NodeJs之fs模块
  • 【408--复习笔记】计算机组成原理
  • 【模型压缩+推理加速】知识蒸馏综述解读
  • 嵌入式硬件工程师从小白到入门-原理图(三)
  • ofd转pdf报错:org.ofdrw.reader.ZipUtil.unZipFileByApacheCommonCompress【已解决】
  • 语言模型理论基础-持续更新-思路清晰
  • Vue 2 探秘:visible 和 append-to-body 是谁的小秘密?
  • Brainstorm绘制功能连接图(matlab)
  • vue - [Vue warn]: Duplicate keys detected: ‘0‘. This may cause an update error.
  • 第六篇:Setup:组件渲染前的初始化过程是怎样的?
  • 【Linux】交叉编译2
  • OpenCV图像拼接(5)图像拼接模块的用于创建权重图函数createWeightMap()
  • 嵌入式硬件工程师从小白到入门-PCB绘制(二)
  • 进程通信(进程池的模拟实现) read write函数复习 Linux ─── 第23课
  • 数据结构day04
  • python康复日记-request库的使用,爬虫自动化测试
  • 26考研——图_图的存储(6)
  • 26考研——图(6)