• 抽象类和接口


    目录

    抽象类

    接口

    基本概念

     多接口使用

    为什么接口解决了Java的多继承问题?

    接口的继承 

    克隆

    Clonable接口

    拷贝 

    Object类


    抽象类

    1.使用abstract修饰的方法称为抽象方法

    2.使用abstract修饰的类称为抽象类

    3.抽象类不可以被实例化 e.g.Shape shape = new Shape()//err

    4.抽象类当中可以和普通类一样定义成员变量和方法

    5.继承抽象类?当一个普通的类继承了抽象类,需要重写这个抽象类中所有的抽象方法!

    所以抽象类不能和final共存,因为final这个关键字就是不让类重写的

    6.抽象类的出现为了被继承:如果有些工作应由子类完成而非父类,那么父类是抽象类的话可以在实例化的时候报错,因为抽象类本身不能被实例化,要想使用,只能创建该抽象类的子类,然后重写抽象方法,这样可以让我们更容易发现问题。(作用)

    7.不可以用private,static修饰抽象类方法


    一开始没有重写方法,后面也需要重写方法

    1. abstract class A extends Shape {
    2. public abstract void testA();
    3. }
    4. class B extends A {
    5. @Override
    6. public void draw() {
    7. }
    8. @Override
    9. public void testA() {
    10. }
    11. }

    接口

    接口就是公共的行为规范标准

    基本概念

    1.接口是使用interface修饰的

    2.接口当中不能有被实现的方法,只能有抽象方法,但是被static和default方法修饰的方法除外

    3.接口当中的抽象方法默认都是public abstract修饰的!

    4.接口当中的成员变量默认都是public static final修饰的

    5.接口不能进行实例化

    6.类和接口之间的关系,可以使用implements来进行关联

    7.系统给出两种选择,要么抽象要么重写

    重写接口方法的时候不能使用默认的访问权限

    因为子类的访问权限要大于等于父类的,父类默认是public abstract,子类有可能是private,就error了,子类必须是public

    8.接口虽然不是类,但也是有对应的字节码文件.class的

    1. private static void drawMap(Shape shape){
    2. shape.draw();
    3. }
    4. public static void main(String[] args) {
    5. //Shape shape = new Shape();//无法实例化,抽象的抽象类
    6. Shape shape1 = new Rect();
    7. Shape shape2 = new Flower();
    8. drawMap(shape1);
    9. drawMap(shape2);
    10. System.out.println("===========");
    11. Shape[] shapes = {new Rect(), new Rect(), new Flower(), new Flower()};//多态,接口也可以发生向上转型和动态绑定
    12. for(Shape shape: shapes){
    13. shape.draw();
    14. }
    15. }

    9.接口中不能有代码块和构造方法 


    例子: 

    1. //接口USB
    2. package demo1;
    3. public interface USB {
    4. void openDevice();
    5. void closeDevice();
    6. }
    7. //类KeyBoard
    8. public class KeyBoard implements USB{
    9. @Override
    10. public void openDevice() {
    11. System.out.println("插上键盘");
    12. }
    13. @Override
    14. public void closeDevice() {
    15. System.out.println("关上键盘");
    16. }
    17. public void input(){
    18. System.out.println("疯狂敲打键盘.......");
    19. }
    20. }
    21. //类Mouse
    22. public class Mouse implements USB{
    23. @Override
    24. public void openDevice() {
    25. System.out.println("鼠标开始工作");
    26. }
    27. public void click(){
    28. System.out.println("疯狂点击鼠标......");
    29. }
    30. @Override
    31. public void closeDevice() {
    32. System.out.println("鼠标停止工作");
    33. }
    34. }

     多接口使用

    为什么接口解决了Java的多继承问题?

    1. //抽象类Animal
    2. abstract class Animal{
    3. public String name;
    4. public int age;
    5. public Animal(String name, int age){
    6. this.name=name;
    7. this.age=age;
    8. }
    9. // public void fly(){}
    10. // public void run(){}
    11. // public void swim(){}
    12. public abstract void eat();
    13. }

    在动物里面,有的会飞有的会游有的会跑
    但我们不能在Animal类里面定义这三个方法因为不是每个动物都会飞或游或跑

    因为Java不能同时继承多个类,所以这三个方法还不能直接写成不同的类

    一个类可以实现多个接口,所以我们把他封装成接口如下:

    1. //三个房卡其实可以看作个行为标准
    2. //三个接口
    3. interface IFly{
    4. void fly();
    5. }
    6. interface IRun{
    7. void run();
    8. }
    9. interface ISwim{
    10. void swim();
    11. }

    想办法让三种动物执行多继承 

    1. //狗是一个动物,具备跑的功能
    2. //鼠标放extends ALT+ENTER可以快速构造方法
    3. //鼠标放Animal抽象类 ALT+ENTER可以快速重写方法
    4. class Dog extends Animal implements IRun{
    5. Dog(String name, int age){
    6. super(name,age);
    7. }
    8. @Override
    9. public void run() {
    10. System.out.println(this.name+" 在用四条腿跑步");
    11. }
    12. @Override
    13. public void eat() {
    14. System.out.println(this.name+" 正在吃");
    15. }
    16. }
    17. //蛙会跑会游
    18. class Frog extends Animal implements IRun,ISwim{
    19. public Frog(String name, int age){
    20. super(name, age);
    21. }
    22. @Override
    23. public void run() {
    24. System.out.println(this.name+" 正在用两条腿跑");
    25. }
    26. @Override
    27. public void swim() {
    28. System.out.println(this.name+" 正在蛙泳");
    29. }
    30. @Override
    31. public void eat() {
    32. System.out.println(this.name+" 正在吃蛙粮");
    33. }
    34. }
    35. //鸭会跑会游会飞
    36. class Duck extends Animal implements IRun, ISwim, IFly{
    37. public Duck(String name, int age) {
    38. super(name, age);
    39. }
    40. @Override
    41. public void fly() {
    42. System.out.println(this.name+" 正在用翅膀飞");
    43. }
    44. @Override
    45. public void run() {
    46. System.out.println(this.name+" 正在撅着屁股跑");
    47. }
    48. @Override
    49. public void swim() {
    50. System.out.println(this.name+" 正在用两只脚划水");
    51. }
    52. @Override
    53. public void eat() {
    54. System.out.println(this.name+" 正在吃鸭粮");
    55. }
    56. }
    1. public class Test {
    2. public static void func1(Animal animal){
    3. animal.eat();
    4. }
    5. public static void main1(String[] args) {
    6. //多态
    7. func1(new Duck("唐老鸭",10));
    8. func1(new Dog("二狗子",10));
    9. func1(new Frog("青蛙",10));
    10. }
    1. class Robot implements IRun{
    2. @Override
    3. public void run() {
    4. System.out.println("机器人 正在用两只脚跑路");
    5. }
    6. }
    7. public class Test {
    8. public static void func1(Animal animal){
    9. animal.eat();
    10. }
    11. public static void running(IRun iRun){
    12. iRun.run();
    13. }
    14. public static void flying(IFly iFly){
    15. iFly.fly();
    16. }
    17. public static void swimming(ISwim iSwim){
    18. iSwim.swim();
    19. }
    20. //接口的意义:只要具备这个功能,我都能用
    21. public static void main(String[] args) {
    22. running(new Duck("唐老鸭",10));
    23. running(new Dog("二狗子",10));
    24. running(new Frog("青蛙",10));
    25. running(new Robot());//不管是不是动物
    26. System.out.println("================");
    27. flying(new Duck("唐老鸭",10));
    28. System.out.println("================");
    29. swimming(new Duck("唐老鸭",10));
    30. swimming(new Frog("青蛙",10));
    31. }

    接口的继承 

    1. interface A{
    2. void testA();
    3. }
    4. interface B extends A{
    5. void testB();
    6. }

    B接口不仅仅具备了B自己的功能,还具备了A接口的功能

    用一个test来测试,发现既得重写A的方法,还得重写B的方法

    关系:

    1.类和接口:implements 实现

    2.接口和接口:extends 拓展

    接口使用实例

    正常数字比较大小

    1. public static void main1(String[] args) {
    2. int a = 10;
    3. int b = 20;
    4. System.out.println(a>b);//输出false
    5. }

    但是到了类的引用,就比较不了了,因为地址无法比较

    1. class Student{
    2. public String name;
    3. public int age;
    4. public Student(String name, int age) {
    5. this.name = name;
    6. this.age = age;
    7. }
    8. @Override
    9. public String toString() {
    10. return "Student{" +
    11. "name='" + name + '\'' +
    12. ", age=" + age +
    13. '}';
    14. }
    15. }

    我们要解决这两个学生要根据什么比较?年龄?姓名?

    我们需要实现一个接口 

    Java里面有一个Comparable接口,表示当前的类是可以比较的

    重写compareTo方法

    1. @Override
    2. public int compareTo(Student o) {
    3. if (this.age > o.age){
    4. return 1;
    5. } else if (this.age == o.age) {
    6. return 0;
    7. }else{
    8. return -1;
    9. }
    10. }

    调用

    分析:

    返回结果: 


    现在我想根据姓名比较,但如果要改变compareTo的逻辑得先复制后注释掉原来的方法再进行修改后单个比较,这样巨麻烦,(这也叫做对类的侵入性比较强,一旦写好了规定的比较方式,那么以后只能以这种方式进行比较),这不是我们想要的,那有没有另一种方法可以及能按照年龄比较又能按照姓名比较呢?

    我们可以设计一个年龄比较器

    comparator里面有一个方法

    重写这个方法

    1. class AgeComparator implements Comparator{
    2. @Override
    3. public int compare(Student o1, Student o2) {
    4. return o1.age-o2.age;
    5. }
    6. }

    调用

    姓名也是同理

    1. class NameComparator implements Comparator{
    2. @Override
    3. public int compare(Student o1, Student o2) {
    4. return o1.name.compareTo(o2.name);
    5. }
    6. }

    这里的compareTo我们点进去看是这样的

    因为String类实现了Compare接口(用ASCII码来计算),所以我们调用的是String自己重写的compareTo方法

    调用方法

    这种方法好处就是比较灵活,只需要传入两个要比较的对象


    数组的排序方法

    1. /* int[] array = {1,2,31,14};
    2. Arrays.sort(array);
    3. System.out.println(Arrays.toString(array));
    4. */

    定义五个学生并组成数组

    1. Student[] students = new Student[] {
    2. new Student("张三", 95),
    3. new Student("李四", 96),
    4. new Student("王五", 97),
    5. new Student("赵六", 92),
    6. };

    我们是否可以沿用数组的排序思想给学生排序呢?

    1. Arrays.sort(students);
    2. System.out.println(Arrays.toString(students));
    3. // 运行出错, 抛出异常.
    4. Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
    对于 sort 方法来说 , 需要传入的数组的每个对象都是 " 可比较 " , 需要具备 compareTo 这样的能力 . 通过重写 compareTo 方法的方式 , 就可以定义比较规则 .
    1. //修改comparable的sort方法底层
    2. public static void bubbleSort(Comparable[] comparables) {
    3. for (int i = 0; i < comparables.length-1; i++) {
    4. for (int j = 0; j < comparables.length-1-i; j++) {
    5. if(comparables[j].compareTo(comparables[j+1]) > 0) {
    6. Comparable tmp = comparables[j];
    7. comparables[j] = comparables[j+1];
    8. comparables[j+1] = tmp;
    9. }
    10. }
    11. }
    12. }
    13. public static void main(String[] args) {
    14. Student[] students = new Student[3];
    15. students[0] = new Student("zhangsan",10);
    16. students[1] = new Student("lisi",4);
    17. students[2] = new Student("abc",5);
    18. System.out.println("排序前: "+Arrays.toString(students));
    19. //bubbleSort(students);
    20. Arrays.sort(students);
    21. System.out.println("排序后: "+Arrays.toString(students));
    22. }

    克隆

    Clonable接口

    1. Person person = new Person("张三",10);
    2. Person person1 = person;//clone

     现在想用第一个person克隆一个新的person1出来,Java的Obeject类里面有一个克隆方法


    因为是protected修饰的,我们没办法直接在person后面.clone调用方法,所以需要super.clone()

    1. //可能会抛出异常,这个异常叫做不知处克隆异常,是编译时期的异常
    2. @Override
    3. protected Object clone() throws CloneNotSupportedException {
    4. return super.clone();
    5. }

     

    所以我们改一下main函数?

    还报错?克隆方法的返回值是Object,person1的类是Person,父类Object给子类Person是不允许的,所以我们需要强转

            Person person1 = (Person) person.clone();//clone

    当我们来打印这两个person的时候,程序爆出问题

    这说明我们自己写的类型要支持克隆的时候,要给它实现一个克隆接口Cloneable

    但是点进去这个接口我们发现居然是空的,结果也没错,那实现这个接口意义在哪

     这种接口叫做空接口或者标记接口,作用是表明当前类是可以被克隆的


    拷贝 

    打印结果是什么呢?

    分析:

    目前这种情况叫做浅拷贝(只有一个对象的时候)就像两个人抢一个遥控器,遥控器变了(19.9->99.99),两个人抢的遥控器也跟着改变

    深拷贝:把对象里面引用等信息拷贝出来一份

    接下来我们要来实现深拷贝

    Person类里面的super.clone()还在克隆person,还没有克隆money,我们可以借助一个中间变量来帮助克隆

    Person tmp = (Person) super.clone();

     再把Money类复制一份

    tmp.money = (Money)this.money.clone();

    这里的this就是person ,money的地址改成0x999(假设),再把这个0x999给到tmp

    person1用来接收tmp的值

    整个的深拷贝分析流程 

    深拷贝完成,打印结果如下

    深拷贝相当于把一个遥控器复制一份分给另一个人,一个人拿到新的遥控器(99.99),另一个人还拿着老的遥控器(19.9)


    Object类

    红色🔒代表private修饰,绿色🔒代表public修饰,一把🔑代表protected修饰

    equals方法

     

    1. public static void main(String[] args) {
    2. Person person = new Person("张三",10);
    3. Person person1 = new Person("张三",10);
    4. //System.out.println(person == person1);
    5. System.out.println(person.equals(person1));//直接调用跟上一句没区别
    6. }

    两个学生名字年龄都一样,但是因为地址不一样所以比较结果false(equals默认比较对象的地址),这背离我们的初衷(这两个学生得相等)

    我们就在Person类里面重写这个方法

    1. //重写equals方法
    2. @Override
    3. public boolean equals(Object obj) {
    4. Person tm = (Person) obj;
    5. return this.name.equals(tm.name) && this.age == tm.age;
    6. }

    也可以再generate里面重写

    1. @Override
    2. public boolean equals(Object o) {
    3. if (this == o) return true;
    4. if (o == null || getClass() != o.getClass()) return false;
    5. Person person = (Person) o;
    6. return age == person.age && Objects.equals(name, person.name);
    7. }
    8. @Override
    9. public int hashCode() {
    10. return Objects.hash(name, age);
    11. }

    hashcode方法可以看我以后的博客😀

  • 相关阅读:
    玄机平台应急响应—Linux入侵排查
    删除链表结点类问题
    Haproxy配合Nginx搭建Web集群
    国产音频放大器工作原理以及应用领域
    PS文字创建工作路径矢量化后变细,导出的svg也变细的解决方案
    C#sharp数据库连接字符串
    使用 Electron 来替代本地调试线上代理的场景
    数据挖掘与机器学习:循环结构
    YOLOv8独家改进:分层特征融合策略MSBlock | 南开大学提出YOLO-MS |超越YOLOv8与RTMDet,即插即用打破性能瓶颈
    论文阅读笔记(五)——利用增强掩模R-CNN实例分割方法从图像中提取个体牛的轮廓
  • 原文地址:https://blog.csdn.net/hellg/article/details/132865216