javaSE -面向对象编程(包,继承,组合,多态,抽象类,接口)

news2024/11/25 14:45:56

一、包(package)

1.1、包(package)是组织类的一种方式

包里存的基本上都是类,而这些类都是别人写好的。我们只需要拿着用。前提是导入对应的包

比如说:打印数组

import java.util.Arrays;
public class TestDome {

    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        System.out.println(Arrays.toString(array));
    }

}

在这里插入图片描述

一个包里面有很多个类在这里插入图片描述
用类名 调用的方法就是static修饰的方法
在这里插入图片描述

1.2、什么是 package 和 import

package (包)
指:类所在的包

import (引入包中的类)
引入在类中需要的类(我们写的程序就是一个类,import 的作用就是 在我们写程序的时候, 引入我们所需要的类)

1.3、在java中不同的包可能会有同名的类

比如说:
在这里插入图片描述
那么 “ impor java.util.*; ” 和 “ impor java.util.具体的类名; ” ,那个更好?
impor java.util.具体的类名; 更好!

1.4、import static - 静态导入

在这里插入图片描述

1.5、使用包的主要目的是保证类的唯一性

但是 如果一个项目很大,有50多人参与这个项目,有可能会出现 类名相同的情况。此时包的作用就体现出来了
在这里插入图片描述

1.6、如何创建一个包

  • 在文件的最上方加上一个 package 语句指定该代码在哪个包中.
  • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
  • 包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
  • 如果一个类没有 package 语句, 则该类被放到一个默认包中.

1.7、在包里面创建一个类

在这里插入图片描述

1.8、在另一个包里,导入同名的类

在这里插入图片描述

1.8、包的访问权限控制 - 只能在同一个包里面使用

  • 我们已经了解了类中的 public 和 private. private 中的成员只能被类的内部使用.
  • 如果某个成员不包含 public 和 private 关键字, 此时这个成员可以在包内部的其他类使用, 但是不能在包外部的类使用

在这里插入图片描述

1.9、常见的系统包

  1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
  2. java.lang.reflect:java 反射编程包;
  3. java.net:进行网络编程开发包。
  4. java.sql:进行数据库开发的支持包。
  5. java.util:是java提供的工具程序包。(集合类等) 非常重要
  6. java.io:I/O编程开发包

二、继承 - extends

代码中创建的类, 主要是为了抽象现实中的一些事物(包含属性和方法).
有的时候客观事物之间就存在一些关联关系, 那么在表示成类和对象的时候也会存在一定的关联

封装:不必要公开的数据成员和方法,使用private关键字进行修饰。意义:安全性。
继承:对共性的抽取。使用extends关键字进行处理的。 意义:可以对代码进行重复使用。

定义两个类,Dog和Bird

class Dog{
    public String name;
    public int age;
    
    public void eat(){
        System.out.println(name + "正在吃饭!");
    }
}

class Bird{
    public String name;
    public int age;

    public void eat(){
        System.out.println(name + "正在吃饭!");
    }
    public void Fly(){
        System.out.println(name + "正在飞翔!");
    }
}

这两个类分别表示狗和鸟,他们都有共同的属性,名字吗,年龄,吃饭
我们可以将这些共性抽取出来

class Animal{
    public String name;
    public int age;

    public void eat(){
        System.out.println(name + "正在吃饭!");
    }
}

class Dog extends Animal{
    
}

class Bird extends Animal{
    public void Fly(){
        System.out.println(name + "正在飞翔!");
    }
}

Dog和Bird都继承于Animal,Animal是动物,Dog和Bird都继承于Animal,说明Dog和Bird都具有名字吗,年龄,吃饭这些属性
这就是继承
如何访问Dog和Bird的属性和方法
和以前一样,通过对象的引用来调用

class Animal{
    public String name;
    public int age;

    public void eat(){
        System.out.println(name + "正在吃饭!");
    }
}

class Dog extends Animal{

}

class Bird extends Animal{
    public void Fly(){
        System.out.println(name + "正在飞翔!");
    }
}

public class TestDome {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小狗";
        dog.age = 10;
        System.out.println(dog.name);
        System.out.println(dog.age);
        dog.eat();
        System.out.println("-----------------");
        Bird bird = new Bird();
        bird.name = "小鸟";
        bird.age = 10;
        System.out.println(bird.name);
        System.out.println(bird.age);
        bird.eat();
        bird.Fly();
    }
}

在这里插入图片描述
仔细分析, 我们发现 Animal 和 Dog以及 Bird 这几个类中存在一定的关联关系:

  • 这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的.
  • 这三个类都具备一个相同的 name 属性, 而且意义是完全一样的.
  • 从逻辑上讲, Cat 和 Bird 都是一种 Animal (is - a 语义).

从逻辑上讲, Dog和 Bird 都是一种 Animal (is - a 语义).
狗是一种动物,鸟也是一种动物
此时我们就可以让 Dog和 Bird 分别继承 Animal 类, 来达到代码重用的效果.
此时, Animal 这样被继承的类, 我们称为 父类 , 基类 或 超类, 对于像 Dog和 Bird 这样的类, 我们称为 子类, 派生类
和现实中的儿子继承父亲的财产类似, 子类也会继承父类的字段和方法, 以达到代码重用的效果

2.1、语法规则

在这里插入图片描述

  • 使用 extends 指定父类.
  • Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承).
  • 子类会继承父类的所有 public 的字段和方法.
  • 对于父类的 private 的字段和方法, 子类中是无法访问的.
  • 子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用

2.2、子类在进行构造的时候要先给父类进行构造(super的三种用法)

在这里插入图片描述

  • super(); — 调用父类的构造方法,只能出现在构造方法里面,并且只能在第一行
  • super.eat(); — 调用父类的普通方法
  • super.属性;— 调用父类的属性
  • super(); 不能出现在静态方法中,
    在这里插入图片描述

2.3、在java中只能是单继承

在这里插入图片描述

2.4、protected 关键字

刚才我们发现, 如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 “封装” 的初衷.
两全其美的办法就是 protected 关键字.

  • 对于类的 子类 和 同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的

2.5、小结: Java 中对于字段和方法共有四种访问权限

  • private: 类内部能访问, 类外部不能访问
  • 默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
  • protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
  • public : 类内部和类的调用者都能访问
    在这里插入图片描述

2.6、更复杂的继承关系

举个例子, 只涉及到 Animal, Cat 和 Bird 三种类. 但是如果情况更复杂一些呢?
针对 Cat 这种情况, 我们可能还需要表示更多种类的猫~
在这里插入图片描述
如刚才这样的继承方式称为多层继承, 即子类还可以进一步的再派生出新的子类.
时刻牢记, 我们写的类是现实事物的抽象. 而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多. 类之间的关系也会更加复杂.
但是即使如此, 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了.
如果想从语法上进行限制继承, 就可以使用 final 关键字

2.7、 final 关键字

  • 修饰一个变量或者字段的时候, 表示 常量 (不能修改)

  • 在这里插入图片描述

  • final 关键字也能修饰类, 此时表示被修饰的类就不能被继承.

  • 在这里插入图片描述

final 关键字的功能是 限制 类被继承
“限制” 这件事情意味着 “不灵活”. 在编程中, 灵活往往不见得是一件好事. 灵活可能意味着更容易出错.
是用 final 修饰的类被继承的时候, 就会编译报错, 此时就可以提示我们这样的继承是有悖这个类设计的初衷的

三、组合

组合和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果.

例如表示一个学校:
在这里插入图片描述
组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段.

这是我们设计类的一种常用方式之一.
组合表示 has - a 语义
在刚才的例子中, 我们可以理解成一个学校中 “包含” 若干学生和教师.
继承表示 is - a 语义
在上面的 “动物和猫” 的例子中, 我们可以理解成一只猫也 “是” 一种动物

四、多态

4.1、向上转型 - 父类引用引用子类对象

class Animal{
    String name;
    int age;
    protected int val;
    private int hight;
    public int getHight() {
        return hight;
    }
    public void setHight(int hight) {
        this.hight = hight;
    }
    public Animal(String name, int age){
        System.out.println("调用Animal的构造方法");
    }
    public void eat(String name){
        System.out.println(this.name + "正在吃饭!");
    }
}

class Dog extends Animal {

    public Dog(String name, int age){
        super(name,age);
    }
}

class Bird extends Animal {
    public Bird(String name, int age){
        super(name,age);
    }
    public void fly(String name){
        System.out.println(this.name + "正在飞翔!");
    }
}

public class TestDmoe {
    public static void main(String[] args) {
//        Dog dog = new Dog("小狗", 18);
//        Animal animal = dog; // 父类引用 引用 子类对象
        //我们也可以写成下面这种情况
        Animal ani = new Dog("小狗", 18);  //父类引用 引用 子类对象
    }
}

4.1.1、向上转型发生的时机

1、直接赋值
2、作为函数的参数
3、作为函数的返回值

public class TestDmoe {

    public static void func(Animal animal){

    }

    public static Animal func1(){
        Dog dog = new Dog("小狗", 10);
        return dog;
    }
    public static void main(String[] args) {
        Dog dog = new Dog("小狗", 10);
        func(dog);
        Animal animal = func1();
    }

    public static void main1(String[] args) {
//        Dog dog = new Dog("小狗", 18);
//        Animal animal = dog; // 父类引用 引用 子类对象
        //我们也可以写成下面这种情况
        Animal ani = new Dog("小狗", 18);
    }
}

在这里插入图片描述

4.2、动态绑定 - 多态的基础

动态绑定发生的前提:

  • 父类引用 引用 子类对象
  • 子类 重写父类 同名的方法
    在这里插入图片描述
    如果子类重写类同名的方法,那就会调用子类的方法
    如果没有重写,那就调用父类字节的方法

4.2.1、覆写/重写/覆盖(Override)

1、在继承(父子类)的关系上
2、方法名相同
3、方法的参数列表相同(个数,类型)
4、返回值相同

在这里插入图片描述

super和this的区别

在这里插入图片描述

4.2.2、运行时绑定(也叫动态绑定)

我们反汇编看一下汇编代码
在这里插入图片描述
像这种编译时期不确定调用的是哪个的方法
只有在运行的才能确定调用的是哪个的方法,就称为运行时绑定(动态绑定)

4.2.3、编译时绑定(静态绑定)

重载的条件:
1、方法名相同
2、参数列表不同(个数或者类型)
3、返回值可以不同

class Dog extends Animal {
    @Override
    public Dog ret(){
        return null;
    }

    @Override
    public void eat(){
        System.out.println("hello 正在吃饭!");
    }
    public Dog(String name, int age){
        super(name,age);
    }

    public void func(){
        System.out.println("不带参数的func");
    }

    public void func(int age){
        System.out.println("带一个参数的func");
    }

    public int func(int age, String name){
        return 0;
    }

}

public class TestDmoe {
    public static void main(String[] args) {
        Dog dog = new Dog("小狗", 10);
        dog.func(10);

    }
}

此时看一下反汇编代码
在这里插入图片描述

这种在编译时就知道调用的是哪个方法,就叫做编译时绑定,也叫静态绑定
在编译的时候,通过方法的参数来确定调用的是哪个方法

4.2.4、重写的注意事项

  • 1、方法不能是静态方法
  • 2、子类的访问修饰限定的访问范围一定要大于等于 父类 的 访问修饰限定
  • 3、private修饰的方法不能被重写
  • 4、被final修饰的方法也不能被重写
  • 5、返回值不一样也可以(协变类型

方法不能是静态方法

在这里插入图片描述

子类的访问修饰限定的访问范围一定要大于等于 父类 的 访问修饰限定

重写中子类的方法的访问权限 不能低于 父类的方法访问权限
在这里插入图片描述

private修饰的方法不能被重写

在这里插入图片描述

被final修饰的方法也不能被重写

在这里插入图片描述

返回值不一样也可以(协变类型)

在这里插入图片描述

4.2.5、在构造方法中调用重写的方法(一个坑)

class B {
    public B() {
// do nothing
        func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}

public class Dome {
    public static void main(String[] args) {
        D d = new D();
    }
}

  • 构造 D 对象的同时, 会调用 B 的构造方法.
  • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0

4.3、理解多态

class Shape{
    public void draw(){
        System.out.println("Shape::draw()");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

///分割线/
public class TestDome {

    public static void drawMap(Shape shape){
        shape.draw();
    }
    
    public static void main(String[] args) {
        Flower flower = new Flower();
        drawMap(flower);
        Rect rect = new Rect();
        drawMap(rect);
    }
}    

在这个代码中,分割线上方的代码是 类的实现者 编写的,分割线下方的代码是 类的调用者 编写的
当类的调用者在编写 drawMap 这个方法的时候,参数类型为 Shape (父类),此时在该方法内部并不知道,也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例。此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关),这种行为就称为 多态

4.3.1、使用多态的好处是什么?

1)类调用者对类的使用成本进一步降低.

  • 封装是让类的调用者不需要知道类的实现细节.
  • 多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可.

因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低.
这也贴合了 <<代码大全>> 中关于 “管理代码复杂程度” 的初衷

2)能够降低代码的 “圈复杂度”, 避免使用大量的 if - else

例如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下:

class Shape{
    public void draw(){
        System.out.println("Shape::draw()");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}
class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("△");
    }
}
///分割线/
public class TestDome {

    public static void main(String[] args) {
        Rect rect = new Rect();
        Flower flower = new Flower();
        Triangle triangle = new Triangle();

        String[] strings = {"rect", "flower", "triangle", "rect", "flower"};

        for (String str : strings) {
            if(str.equals("rect")){
                rect.draw();
            }else if(str.equals("flower")){
                flower.draw();
            }else if(str.equals("triangle")){
                triangle.draw();
            }
        }
    }
}

如果使用使用多态, 则不必写这么多的 if - else 分支语句, 代码更简单

public static void main(String[] args) {
        Shape[] shapes = {new Rect(), new Triangle(), new Flower(),
                            new Triangle(),new Rect()};

        for (Shape shape : shapes){
            shape.draw();
        }
    }

什么叫 “圈复杂度” ?
圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.
因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 “圈复杂度”. 如果一个方法的圈复杂度太高, 就需要考虑重构
不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10

4.4、向下转型

向上转型是 is - a 的关系,比如说:鸟是一种动物,狗是一种动物
父类引用 引用 子类对象

向下转型不好,不安全,你总不能说所有的动物是狗吧
子类引用 引用 父类对象

class Animal{
    String name = "hello";
    int age;

    public Animal(String name, int age){
        System.out.println("调用Animal的构造方法");
    }
    public void eat(){
        System.out.println(this.name + "正在吃饭!");
    }
    public Animal ret(){
        return null;
    }
}

class Bird extends Animal {
    public Bird(String name, int age){
        super(name,age);
    }
    public void fly(String name){
        System.out.println(name + "正在飞翔!");
    }
}

public class TestDmoe {

    public static void main(String[] args) {
        Animal animal = new Bird("小鸟", 10);
        //animal.fly();
        Bird bird = (Bird) animal;
        bird.fly("小鸟");
    }
}    

在这里插入图片描述

instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true

向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象. 相比于向上转型来说, 向下转型没那么常见

注意事项
编译过程中, animal 的类型是 Animal, 此时编译器只知道这个类中有一个 eat 方法, 没有 fly 方法.
虽然 animal 实际引用的是一个 Bird 对象, 但是编译器是以 animal 的类型来查看有哪些方法的.
对于 Animal animal = new Bird(“圆圆”) 这样的代码,

  • 编译器检查有哪些方法存在, 看的是 Animal 这个类型
  • 执行时究竟执行父类的方法还是子类的方法, 看的是 Bird 这个类型

但是这样的向下转型有时是不太安全

Animal animal = new Dog("小狗", 10);
        Bird bird = (Bird) animal;
        bird.fly("小狗");

在这里插入图片描述

instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true

public static void main(String[] args) {
        Animal animal = new Dog("小狗", 10);
        if(animal instanceof Bird) {
            Bird bird = (Bird) animal;
            bird.fly("小狗");
        }
    }

4.5、总结

多态是面向对象程序设计中比较难理解的部分. 我们会在后面的抽象类和接口中进一步体会多态的使用. 重点是多态带来的编码上的好处.

另一方面, 如果抛开 Java, 多态其实是一个更广泛的概念, 和 “继承” 这样的语法并没有必然的联系.

  • C++ 中的 “动态多态” 和 Java 的多态类似. 但是 C++ 还有一种 “静态多态”(模板), 就和继承体系没有关系了.
  • Python 中的多态体现的是 “鸭子类型”, 也和继承体系没有关系.
  • Go 语言中没有 “继承” 这样的概念, 同样也能表示多态.

无论是哪种编程语言, 多态的核心都是让调用者不必关注对象的具体类型. 这是降低用户使用成本的一种重要方式

五、抽象类

在刚才的打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为 抽象类(abstract class).

class Shape{
    public void draw(){
        System.out.println("Shape::draw()");
    }
}

那这个方法直接就不实现任何操作就行了
(下面的写法是错误的示范)

class Shape{
      public void draw();
}

此时就要用 abstract 关键字来修饰,用 abstract 关键字修饰的方法 叫做 抽象方法,一个类里面含有抽象方法,那这个类也要用 abstract 关键字类修饰,那这个类就叫做抽象类

abstract class Shape{
    public abstract void draw();
}

5.1、注意事项

1)抽象类不实例化对象
在这里插入图片描述

2)抽象类只能被继承
3)一个普通类 继承 抽象类必须 重写 抽象类中的抽象方法

在这里插入图片描述
4)抽象类中可以包含和普通一样的成员
在这里插入图片描述
5)一个抽象类A 继承 一个 抽象B,就不用重写 父类B的抽象方法
在这里插入图片描述

6)结合第5点,一个普通类继承抽象类A,那这个普通类 就要重写 抽象类A和抽象类B 所有的抽象方法

在这里插入图片描述

7)抽象类不能被final修饰,抽象方法也不能被final修饰
8)抽象类不能被 private 修饰

5.2、抽象类的作用

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

  • 有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

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

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

  • 很多语法存在的意义都是为了 “预防出错”, 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.

充分利用编译器的校验, 在实际开发中是非常有意义的

六、接口

接口是抽象类的更进一步,抽象类中还可以包含非抽象方法,和字段,而接口中包含的方法都是抽象方法,字段只能包含静态常量

6.1、interface 关键字

interface

英 [ˈɪntəfeɪs] 美 [ˈɪntərfeɪs]
n. (人机)界面(尤指屏幕布局和菜单);接口;接口程序;连接电路;(两学科、体系等的)接合点,边缘区域
v. (使通过界面或接口)接合,连接

接口都是由interface 修饰的

在刚才的打印图形的示例中, 我们的父类 Shape 并没有包含别的非抽象方法, 也可以设计成一个接口

interface IShape{
    public abstract void draw();
}

接口中不能有普通方法的具体实现

在这里插入图片描述
如果非要实现,那就用 default 修饰,该方法表示该接口的默认方法

在这里插入图片描述

在接口中静态方法可以有具体的实现

在这里插入图片描述
接口中的方法(默认,静态),只能通过该接口或者 实现该接口 的 类 的引用来调用

interface IShape{
    public abstract void draw();
    default public void func(){
        System.out.println("");
    }
    public static void funcStatic(){
        System.out.println("接口中的静态方法");
    }
}

class Rect implements IShape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

public class Test1 {
    public static void main(String[] args) {
        Rect rect = new Rect();
        IShape.funcStatic();//通过接口来调用静态方法
        rect.draw();//通过实现接口的类  的引用来调用
    }
}

在这里插入图片描述
不知有没有细心的人发现上面程序程序中 修饰方法的关键字public都是灰色的,意味着在接口当中,所有的方法都默认是public。
在这里插入图片描述
因为默认的方法也是public修饰的,所以默认方法的public可以省略
在这里插入图片描述

接口是不能被实例化的

在这里插入图片描述

一个类通过关键字implements来实现接口,这个类要重写接口中所有的抽象方法,默认方法和静态方法不需要重写

implements

英 [ˈɪmplɪments] 美 [ˈɪmplɪments]
v.
使生效;贯彻;执行;实施
n.
工具;器具;(常指)简单的户外用具
implement的第三人称单数和复数

interface IShape{
    public abstract void draw();
    void drawI();//抽象方法中的 public 和 abstract 是可以省略的

    default void func(){
        System.out.println("");
    }
    public static void funcStatic(){
        System.out.println("接口中的静态方法");
    }
}

class Rect implements IShape{
    @Override
    public void draw() {
        System.out.println("♦");
    }

    @Override
    public void drawI() {

    }
}

通过接口可以实现向上转型,动态绑定,多态

interface IShape{
    public abstract void draw();
    default void func(){
        System.out.println("");
    }
    public static void funcStatic(){
        System.out.println("接口中的静态方法");
    }
}

class Rect implements IShape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}
class Flower implements IShape {
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

class Triangle implements IShape {
    @Override
    public void draw() {
        System.out.println("△");
    }
}
class Cycle implements IShape {
    @Override
    public void draw() {
        System.out.println("●");
    }
}

向上转型,动态绑定

public static void main(String[] args) {
        IShape iShape1 = new Rect();
        iShape1.draw();
        iShape1.func();
    }

多态

public class Test1 {
    public static void drawMap(IShape iShape){
        iShape.draw();
    }
    public static void main(String[] args) {
        IShape iShape1 = new Rect();
        IShape iShape2 = new Flower();
        IShape iShape3 = new Cycle();
        IShape iShape4 = new Triangle();

        IShape[] iShapes = {iShape1, iShape2, iShape3, iShape4};

        for (IShape ip : iShapes) {
            ip.draw();
        }
    }
}

在这里插入图片描述
扩展(extends) vs 实现(implements)
扩展指的是当前已经有一定的功能了, 进一步扩充功能.
实现指的是当前啥都没有, 需要从头构造出来

接口中的成员变量默认是 public static final 修饰的(常量)

在这里插入图片描述
接口中只能包含抽象方法. 对于字段来说, 接口中只能包含静态常量(final static).

当一个类实现一个接口的时候,重写接口中的抽象方法,重写的方法前面必须加public修饰

在这里插入图片描述

一个类可以继承一个类,同时也可以实现多个接口,这个类要重写接口中的所有的抽象方法;

在这里插入图片描述

接口和接口之间的关系

接口和接口之间可以使用extends来操作他们的关系,此时,这里面意为:拓展。
一个接口 IB 通过 extends来拓展另一个接口 IC 的功能。此时当一个类D通过implements实现这个接口 IB 的时候,此时重写的方法不仅仅是 IB 的抽象方法,还有他从 IC 接口,拓展来的功能【方法】。

interface IC{
    void draw();
}

interface IB extends IC{
    void func();
}
class D implements IB{
    @Override
    public void draw() {
        System.out.println("hehe");
    }

    @Override
    public void func() {
        System.out.println("hukkk");
    }
}

6.2、实现多个接口

有的时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的.
然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果.

现在我们通过类来表示一组动物:

//动物
class Animal{
    protected String name;
    public Animal(String name){
        this.name = name;
    }
}
//跑
interface IRunning{
    void run();
}
//吃饭
interface IEat{
    void eat();
}
//飞翔
interface IFly{
    void fly();
}
//游泳
interface ISwimming{
    void swim();
}

//鱼
class Fish extends Animal implements ISwimming{

    public Fish(String name) {
        super(name);
    }

    @Override
    public void swim() {
        System.out.println(this.name + " 正在游泳!");
    }
}
//狗
class Dog extends Animal implements IRunning,IEat{
    public Dog(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(this.name + " 正在跑!");
    }

    @Override
    public void eat() {
        System.out.println(this.name + " 正在吃饭!");
    }
}
//鸭子
class Duck extends Animal implements IRunning,IEat,ISwimming,IFly{

    public Duck(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(this.name + " 正在跑!");
    }

    @Override
    public void eat() {
        System.out.println(this.name + " 正在吃饭!");
    }

    @Override
    public void swim() {
        System.out.println(this.name + " 正在游泳!");
    }

    @Override
    public void fly() {
        System.out.println(this.name + " 正在飞翔!");
    }
}

public class TestDome {
    public static void swimming(ISwimming iSwimming){
        iSwimming.swim();
    }

    public static void running(IRunning iRunning){
        iRunning.run();
    }

    public static void fly(IFly iFly){
        iFly.fly();
    }
    public static void main(String[] args) {
        swimming(new Duck("鸭子"));
        running(new Dog("小狗"));
        swimming(new Fish("鱼"));
        fly(new Duck("鸭子"));
    }
}

上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口.
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性 .

  • 狗是一种动物, 具有会跑的特性.
  • 青蛙也是一种动物, 既能跑, 也能游泳
  • 鸭子也是一种动物, 既能跑, 也能游, 还能飞

这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而
只关注某个类是否具备某种能力.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/92836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鲜花店如何数字化转型,鲜花店管理小程序

鲜花的用途非常广泛&#xff0c;除了平时祝福送亲友外&#xff0c;还有七夕/情人节送情侣/爱人等&#xff0c;商圈中的各品牌花店也都不少&#xff0c;并且其收益也相当可观&#xff0c;虽然是非必需品&#xff0c;但却又不可缺。 雨科网观察了鲜花行业相关数据报告后&#xff…

基于java+springmvc+mybatis+vue+mysql的学生竞赛模拟系统

项目介绍 本系统采用java语言开发&#xff0c;后端采用springboot框架&#xff0c;前端采用vue技术&#xff0c;数据库采用mysql进行数据存储。 前台&#xff1a; 首页、公交信息、论坛交流、试卷、校园资讯、个人中心、后台管理 后台&#xff1a; 首页、个人中心、用户管理…

别再随意说 Redis 的 SET 保障原子性,在客户端不一定

分布式系统有一个特点&#xff0c;就是无论你学习积累多少知识点&#xff0c;只要在分布式的战线中&#xff0c;总能遇到各种超出主观意识的神奇问题。比如前文使用Jedis来实现分布式锁的技术知识点储备&#xff0c;本以为很稳不会再遇到什么问题&#xff0c;但实际情况却是啪啪…

Android ASM

文章目录逆波兰表达式与字节码的关系中缀表达式转换为逆波兰表达式&#xff08;后缀表达式&#xff09;的过程逆波兰表达式求值过程ASM 的使用ASM 常用 api 说明ClassWriter构造函数传参 flags 的作用定义类的属性&#xff1a;visit()定义类的方法&#xff1a;visitMethod()定义…

原生小程序canvas生成图片、保存到本地

今天在视频中看到一个跳动的小球的效果&#xff0c;感觉挺好玩的。于是自己也实现了一个&#xff0c;感觉还是好玩&#xff0c;就想来分享一番&#xff1b;小伙伴们可以来看一下。这次主要为大家玩一下radial-gradient和动画阴影的调试。 效果呈上 代码来了 大家可以先仔细阅…

Docker安装(图文教程)

一、Docker简介 1、Docker是什么 &#xff08;1&#xff09;Docker是一种虚拟化容器技术。Docker基于镜像&#xff0c;可以秒级启动各种容器。每一种容器都是一个完整的运行环境&#xff0c;容器之间互相隔离。&#xff08;2&#xff09;在Docker的官方&#xff0c;提供了很多容…

Java培训之Nginx启动

1. Nginx启动 启动问题 进入/usr/local/nginx/sbin目录&#xff0c;运行命令./nginx 即可启动nginx nginx无法启动: libpcre.so.1/libpcre.so.0: cannot open shared object file解决办法 Java培训之Nginx启动 解决方法&#xff1a; ln -s /usr/local/lib/libpcre.so.1 /l…

web前端网页设计期末课程大作业:HTML旅游网页主题网站设计——酒店主题网站设计—酒店阳光温馨网站(5页)HTML+CSS+JavaScript

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

[附源码]Nodejs计算机毕业设计基于的汉服服装租赁系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

32-Vue之ECharts-雷达图

ECharts-雷达图前言雷达图特点雷达图的基本实现雷达图的常见效果显示数值区域面积绘制类型完整代码前言 本篇来学习写雷达图 雷达图特点 可以用来分析多个维度的数据与标准数据的对比情况 雷达图的基本实现 ECharts 最基本的代码结构定义各个维度的最大值准备具体产品的数…

Python编程|手把手教植物大战僵尸,代码开源

前言 如题&#xff0c;手把手教Python实现植物大战僵尸游戏&#xff0c;代码简单易学&#xff0c;无需额外安装Python包&#xff0c;只要有pygame即可&#xff0c;文末获取全部素材及源代码~ 视频演示效果&#xff1a;https://www.bilibili.com/video/BV1cG411u755/?spm_id_…

并发编程之深入理解ReentrantLock和AQS原理

AQS&#xff08;AbstractQueuedSynchronizer&#xff09;在并发编程中占有很重要的地位&#xff0c;可能很多人在平时的开发中并没有看到过它的身影&#xff0c;但是当我们有看过concurrent包一些JDK并发编程的源码的时候&#xff0c;就会发现很多地方都使用了AQS&#xff0c;今…

(文章复现)6计及源荷不确定性的电力系统优化调度(MATLAB程序)

目录 参考文章&#xff1a; 代码主要内容&#xff1a; 主程序&#xff1a; 结果图&#xff1a; 参考文章&#xff1a; 考虑源荷两侧不确定性的含风电电力系统低碳调度——崔杨&#xff08;2020&#xff09; 代码主要内容&#xff1a; 参照考虑源荷两侧不确定性的含风电的…

JAVA基础讲义06-面向对象

面向对象一、编程思想什么是编程思想面向过程和面向对象面向过程编程思想面向过程思想面向过程实现应用场景面向过程特点面向过程代表语言面向对象介绍面向对象编程思想面向对象的三大特征面向对象思想总结什么是编程面向对象分析方法分析问题的思路和步骤二、类和对象类类的概…

它破解了AI作画的中文语料难题,AIGC模型讲解(以世界杯足球为例)

目录1 扩散模型与AI绘画2 中文语料的挑战3 昆仑天工&#xff1a;AIGC新思路3.1 主要特色3.2 模型蒸馏3.3 编解码与GPT3.4 stable-diffusion3.5 性能指标4 体验中文AI绘画模型5 展望1 扩散模型与AI绘画 AI绘画发展历史始于20世纪60年代&#xff0c;当时人工智能研究者们尝试使用…

springboot启动流程源码分析

一、引入思考的问题 1、springboot未出现之前&#xff0c;我们在在spring项目中如果要使用数据源&#xff08;比如我们使用druid&#xff09;&#xff0c;需要做哪些事情呢&#xff1f; &#xff08;1&#xff09;引入druid的jar包 &#xff08;2&#xff09;配置数据源的参…

微服务调用工具

微服务调用工具目录概述需求&#xff1a;设计思路实现思路分析1.A2.B3.C参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,challenge Survive…

Postman API测试工具 - 初认知 基本使用(一)

Postman - API测试工具 初认知&#xff08;一&#xff09; 文章目录Postman - API测试工具 初认知&#xff08;一&#xff09;一、什么是Postman&#xff1f;二、如何下载Postman&#xff1f;三、Postman的使用四、处理GET请求&#xff1a;五、处理POST请求总结一、什么是Postm…

Python 缩进语法的起源:上世纪 60-70 年代的大胆创意

上个月&#xff0c;Python 之父 Guido van Rossum 在推特上转发了一篇文章《The Origins of Python》&#xff0c;引起了我的强烈兴趣。 众所周知&#xff0c;Guido 在 1989 年圣诞节期间开始创造 Python&#xff0c;当时他就职于荷兰数学和计算机科学研究学会&#xff08;简称…

MySQL之聚合查询和联合查询

一、聚合查询&#xff08;行与行之间的计算&#xff09; 1.常见的聚合函数有&#xff1a; 函数 说明 count 查询到的数据的数量 sum 查询到的数据的总和&#xff08;针对数值&#xff0c;否则无意义&#xff09; avg 查询到的数据的平均值&#xff08;针对数值&#xf…