java面向对象编程_包_继承_多态_重载和重写_抽象类_接口_this和super

27 篇文章 4 订阅

包(package)组织类的一种方式!
使用包的目的:使类具有唯一性!
包是组织类的一种方式,我们java中的多个类可以放到一个包中。
就好比学校里的班级就是一个包,我们学生就是一个类,相同班级的我们都放在同一个包下!
目的:使类具有唯一性!
就是说不同包下的类我们可以有同名的类,如果没有包的话,类名相同,类就无法区分,不唯一!就好比我们填写我们的信息不止填姓名,还会加上班级等进行限定!

无法在一个包下创建相同类名的类!
在这里插入图片描述
在不同的包下,我们可以创建相同类名的类,所以类保证了类的唯一性!在这里插入图片描述我们可以看到我们的Test类在demo_1demo两个包下都有!
但是我们可以看到两个类的第一行都有声明在那个包下的类!

语法格式
package 包名;
demo_1包下的Test
在这里插入图片描述
demo_2下的Test
在这里插入图片描述

包的命名方法

我们java一般采用域名命名法!
例如我们可以用我们的邮箱域名命名我们的包
eg: 域名:bugguo.qq.com
那我的包名就可以以com.qq.buguo命名!
在这里插入图片描述
在这里插入图片描述在这里插入图片描述我们可以看到.将一个包分成了多层文件夹!
所以也可以说包就是一个文件夹!
命名规范
所有命名规则必须遵循以下规则:

1)、名称只能由字母、数字、下划线、$符号组成

2)、不能以数字开头

3)、名称不能使用JAVA中的关键字。

4)、坚决不允许出现中文及拼音命名。

导入包中的类

java下提供了很多写好的类给我们使用,这也是java便利快捷的编写java程序!
import关键字:
顾名思义:import有导入输入的意思
我们可以通过import关键字导入系统中的类

在这里插入图片描述
在这里插入图片描述
我们有时可能不止使用java.util下的一个类,但我们要使用多个类时!

  • 分别导入
    在这里插入图片描述

  • 将相同包下的所有类一起导入
    在这里插入图片描述值得注意的是java并不是像C语言下的#include一样,java下的导入包中的类,并不会真正的将该包下的全部内容全部导入到代码中,而是该代码会使用到的类java才会去真正意义导入!
    所以不要有整体导入包就会使java代码效率更低的顾虑!

  • 当一个类在不同的包下时

我们已经知道了,java中的包目的就是:使类具有唯一性!
但我们使用java类是两个包下都有该类时,我们就需要正确完整导入该类。
不能整体导入,否者会产生编译错误!
在这里插入图片描述我们可以看到Date类在java.util包下和java.sql包下都有,我们使用时应该导入完整类!
在这里插入图片描述
在这里插入图片描述

系统包的介绍

我们java下已经写好了很多系统包,但我们要使用到某个功能时,只需要将该包下的类导入即可!

  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编程开发包。

可以看到我们就接触了java.util工具包,最常用!
以后的系统包。我们会一一接触!

注意事项

import关键字并不可以导入指定的某个包,
它的作用是导入包中的类,如果要将该包下所有的类导入,就使用.*代替该包下的所有类!

继承

顾名思义java继承就是和我们所说的继承应该是一样的,子女继承父母!
java继承也是如此,java类可以继承父类!~

//简单的一个继承代码!
//父类
class Animal{
    public String name;
    public String age;
}
class Dog extends Animal{
    //extends继承了父类Animal
    //Dog类就有了父类Animal的属性
    //name和age
}
public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog(); //创建一个Dog对象
        dog.name = "bug郭"; //给继承的属性赋值
        dog.age = "18";
        System.out.println(dog.name+":"+dog.age);
    }
}

运行结果
在这里插入图片描述可以看到我们使用extends关键字便实现了继承!
父类Anmial的属性,子类dog便有了该属性!
看完这个代码,我们好像对继承有了一定理解~

继承语法

class 子类名 extends 父类名 {
}

我们知道extends英文意为:扩展,延伸,继承。
那继承有什么用呢?
我们先看下面代码:

class Birdie{
    public String name;
    public int age;
    public void eat(){
        System.out.println(name+"eat()!");
    }

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

    public void run(){
        System.out.println(name+"run()!");
    }
}
class Frog{
    public String name;
    public int age;
    public void eat(){
        System.out.println(name+"eat()!");
    }

    public void jump(){
        System.out.println(name+"jump()!");
    }
}

我们可以看到BirdieDogFrog类都有一些相同的属性和方法!如果我们利用extends继承,就可以使代码更简洁!
我们可以将所有的类相同的属性抽离出来成一个Anmial父类~所有的类都是Anmial的子类,(is a)的关系, 例如 Birdie is a Anmial 我们就可以采用关键字extends继承关键字!

class Animal{
    public String name;
    public int age;
    public void eat(){
        System.out.println(name+"eat()!");
    }
}
class Birdie extends Animal{
    public void fly(){
        System.out.println(name+"fly()!");
    }
}
class Dog extends Animal{
    public void run(){
        System.out.println(name+"run()!");
    }
}
class Frog extends Animal{
    public void jump(){
        System.out.println(name+"jump()!");
    }
}

这就是继承简单的作用!继承实现了代码重写的效果!

基础知识

我们所继承的父类,在java中又被称为超类,基类父类
子类又称为派生类,子类

  • 子类要使用extends关键字指定父类;
  • 子类中通过super指代父类对象的引用;
  • 子类可以继承父类的所有public方法和属性;
  • 子类无法继承父类中的private方法和字段;
  • java只能单继承,只能继承一个父类,无法实现多继承;
  • 子类可以继承父类finalstatic修饰的属性和方法;

父类中的private修饰的私有属性和方法无法被继承

class Anmail{
    public String name;
    public int age;
    private String sex = "男";
    private int height;
    public  void eat(){
        System.out.println(name+":eat()!");
    }
}
class Bired extends Anmail{

}
public class Test {
    public static void main(String[] args) {
       Bired bired = new Bired();
       bired.sex;//无法继承父类中的私有属性和方法
    }
 }

在这里插入图片描述

final修饰的属性无法被更改,可以被子类继承!!

class Anmail{
    public String name;
    public int age;
    public final String sex = "男";
    public static int height;
    public  void eat(){
        System.out.println(name+":eat()!");
    }
}
class Bired extends Anmail{

}
public class Test {
    public static void main(String[] args) {
        Anmail anmail = new Anmail();
        anmail.sex = "女"; //error 无法改变final修饰的属性和方法
        Bired bired = new Bired();
        bired.sex = "女";//error 子类也无法改变父类中的final属性和方法
        System.out.println(bired.sex); //子类可以继承final修饰的属性和方法
    }
}

在这里插入图片描述
子类可以继承static修饰的属性和方法
在这里插入图片描述

构造方法

当我们子类有构造方法时,要先帮助父类进行构造!

基础语法

我们利用sumper关键字帮助父类构造,调用合适的构造方法即可

class 父类{
  public 父类(){
  // 父类构造方法
  }
}
class 子类 extends 父类{
   public 子类(){
     sumper(); //帮助父类构造
     
  }
}
//父类只有不带参数的构造方法时
class Animal{
    protected String name;
    protected int age;
    public void eat(){
        System.out.println("Animal::eat()");
    }
    public Animal(){

    }
}
class Bird extends Animal {
   
}

我们知道编译器会自动帮助我们编写不带参数的构造方法!
所以其实子类中也有构造方法
在这里插入图片描述

//当父类中有含有参数的构造方法时
class Animal{
    protected String name;
    protected int age;
    public void eat(){
        System.out.println("Animal::eat()");
    }
    public Animal(String name,int age){
        this.name = name;
        this.age = age;
    }
}
class Bird extends Animal {
		 //子类也需要帮助父类构造含参数的构造方法
    public Bird(String name, int age){
        super(name,age);
    }
}

在这里插入图片描述

//当父类有多个构造方法时
class Animal{
    protected String name;
    protected int age;
    public void eat(){
        System.out.println("Animal::eat()");
    }
    public Animal(String name,int age){
        this.name = name;
        this.age = age;
    }
    public Animal(){
       
    }
}
class Bird extends Animal {
    public Bird(String name, int age){
        super(); //调用父类不带参数的构造方法
    }
}

在这里插入图片描述如果我们不帮父类构造编译器就会报错
在这里插入图片描述_

protected修饰符

  • 通常我们的父类中的字段或者方法会用protected修饰,但也因情况而定
  • protected修饰的字段和方法权限:相同包的类,不同包中的子类;
    也就是说,protected修饰,无法在不同包中的非子类访问。很好的包装了父类!
package demo;
public class Animal{
    protected String name;
    protected int age;
}
package demo_2;
import demo.Animal;
public class Bird extends Animal {
    public void setName(String name){
        super.name = name; //继承了demo包下Animal类中的protected方法,字段
    }
    public  void setAge(int age){
        super.age = age; 
    }
}

demo包下的Animal
在这里插入图片描述
demo_2包下的Bird
在这里插入图片描述

组合

我们知道继承是is a的关系,就是什么是什么,bired is anmail
组合就是has a的关系,什么有什么!
我们写代码也会使用到
例如一个学校类,我们需要老师和学生类,
而学校对象就有老师和学生对象!

class Student{
   public String name = "bug郭";
   public int age = 18;
}
class Teacher{
    public String name ="CSDN";
    public int age = 66;
}
class School{
    //组合school has student and teacher.
  Student student = new Student();
  Teacher teacher = new Teacher();
  public void show(){
      System.out.println(student.name+":"+student.age);
      System.out.println(teacher.name+":"+teacher.age);
  }
}
public class Test_2 {
    public static void main(String[] args) {
        School school = new School();
        school.show();
    }
}

在这里插入图片描述

thissuper关键字

this

父类对象的引用

  • this() 调用类中的构造方法

只能在构造方法中使用
在这里插入图片描述并且只能在构造方法中的第一行使用
在这里插入图片描述在构造方法中的第一行中使用
在这里插入图片描述

  • this.字段名 调用当前类中的字段
    在这里插入图片描述
  • this.方法名() 调用当前对象的方法
    在这里插入图片描述

super

super 表示获取到父类实例的引用. 涉及到两种常见用法。

  • super()调用父类的构造方法
    在这里插入图片描述

  • super.字段名 调用父类的字段和方法
    在这里插入图片描述

重载和重写

重载

方法重载就是,在一个类中方法名相同,参数列表不同,返回值无关

class Sum{
   public int add(int a,int b){
       return a+b;
   }
    public int add(int a,int b,int c){
        return a+b;
    }
    public double add(double a, double b){
        return a+b;
    }
}

上面add方法构成方法重载!

重写

啥是重写呢?

重写就是子类中有父类同名的方法就构成了重写

package demo;
public class Animal{
    protected String name;
    protected int age;
    public void eat(){
        System.out.println("Animal::eat()");
    }
}
class Bird extends Animal{
    //和父类相同的方法
    public void eat(){
        System.out.println("Bird::eat()"); 
    } 
}

重写注意事项

  • 方法的返回值和参数列表必须和父类相同才能构成重写

方法的返回值不同,重写失败
在这里插入图片描述方法的参数不同,重写失败
在这里插入图片描述
方法名,返回值,参数相同,构成重写
在这里插入图片描述

  • 重写的方法的修饰符的权限要高于或等于父类中的修饰符权限
    在这里插入图片描述在这里插入图片描述
  • 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外)

协变类型

就是子类重写的方法可以返回父类方法返回值的子类!!!
在这里插入图片描述可以看到重写方法的返回值是父子类关系时,也能构成重写!!!

重写和重载的区别

  • 方法重载指的是在一个类中同名的方法
    条件:方法名相同,参数列表不同,返回值一般不做要求!

  • 方法重写指的是子类中的方法与父类中的方法同名,且方法的参数列表和返回值都要与父类相同!且子类重写方法的访问修饰限定符的权限要求大于或等于父类中的权限!
    条件:继承关系,方法名,参数列表,返回值相同,权限高于或等于父类。

  • 重载对方法的权限不做要求!

访问修饰限定符

java中的字段和方法的四种访问权限

  • public可以在不同包中的类访问!

  • protected不同包中继承关系访问!

  • 默认包访问权限,只能在同一包中的类中访问!

  • privated只能在同一个类中访问

NO范围publicprotecteddefaultprivated
1不同包中的非子类✔️
2不同包中的子类✔️✔️
3同一包中的不同类✔️✔️✔️
4同一包中的同一类✔️✔️✔️✔️

多态

在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。 多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。多态(百度词条)

向上转型

子类对象赋值给了父类引用

该对象只能访问父类的字段和方法!

直接赋值

子类对象赋值给了父类引用

class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_1 {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal = new Dog();  //1.子类对象赋值给了父类引用
        Animal animal1 = new Dog(); //2.和 1 等价
        animal.eat(); //调用父类中的方法
        //error animal只能访问父类中的字段和方法!
        animal.height=1;
        animal.running();
    }
}

在这里插入图片描述

方法传参

//方法传参
class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_2 {
    public static void func(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog();
        func(dog);
        func(new Dog()); //子类对象赋值给了父类引用!
    }
}

方法返回

//方法返回
class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_2 {
    public static Animal func(){
       return new Dog();  //子类对对象返回给了父类引用!
    }
    public static void main(String[] args) {
       Animal animal = func();
       animal.eat();
    }
}

在这里插入图片描述

动态绑定

动态绑定就是,当子类重写了父类的方法时,向上转型后,对象调用与重写的方法,访问的是子类对象中的重写方法!

//运行时绑定
class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    @Override
    public void eat() {
        System.out.println("dog eat()!");
    }
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_2 {
    public static void main(String[] args) {
       Animal animal = new Dog();
       animal.eat(); //运行时绑定!
    }
}

在这里插入图片描述为啥要叫运行时绑定呢?
难道说编译的时候没有绑定?
确实如此,当我们查看java的反汇编代码时就会发现,编译期间anmial调用的是自己的eat方法,但是运行时却绑定了子类的eat()!
在这里插入图片描述

java反汇编代码步骤:

  • 找到,我们需要反汇编代码类的字节码文件
  • 在命令符的窗口下,输入javap -c 类名代码 回车即可!

理解多态

我们想一想多态可以帮助我们做些什么!
bug郭的理解

  • 运行时绑定使用场景
    我们可以重写父类的方法,实现多态。调用重写的方法,一个父类可以有多个子类,不同的子类重写了不同的方法,实现了真正意义上的多态!

eg:打印图形

//类的实现者
class Shape {
    public void draw() {
        // 啥都不用干
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("○");
    }
}
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("□");
    }
}
class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("♣");
    }
}
//类的调用者
public class Test_1{
    public static void main(String[] args) {
        Shape shape1 = new Flower();
        Shape shape2 = new Cycle();
        Shape shape3 = new Rect();
        drawShape(shape1);
        drawShape(shape2);
        drawShape(shape3);
    }
    // 打印单个图形
    public static void drawShape(Shape shape) {
        shape.draw();
    }
}

在这里插入图片描述
使用多态的好处是什么?

  • 类调用者对类的使用成本进一步降低。

封装是让类的调用者不需要知道类的实现细节。
多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可。
因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低。

  • 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
    “圈复杂度” :就是代码中的分支和循环;
  • 可扩展能力更强。
    如果要新增一种新的形状,使用多态的方式代码改动成本也比较低。

向下转型

我们知道向上转型是子类对象赋值给了父类引用!
那向下转型莫不就是:父类对象赋值给了子类引用~

并不常见~了解一下即可!

//向下转型
class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}
class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
    public void fly() {
        System.out.println(this.name + "正在飞");
    }
}
public class Test_2 {
    public static void main(String[] args) {
        Animal animal = new Bird("鸽鸽"); //先借助向上转型
        animal.eat("脐橙");
        //animal.fly;  //error
        Bird bird;
        bird = (Bird)animal; //向下转型需要强转
        bird.fly();
    }
}

在这里插入图片描述可以看到向下转型步骤比较繁琐,通常要借助向上转型!
而且我们需要确定是否为父子类关系!避免异常!

利用instanceof 关键字可以判定一个引用是否是某个类的实例。
若真返回true,若假返回false!

在这里插入图片描述

构造方法中调用一个重写的方法(一个坑!)

//坑
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 Test_3{
    public static void main(String[] args) {
        D d = new D();
    }
}

在这里插入图片描述bug郭看了半天愣是没整明白为啥这个代码运行结果是这样!!!
我的理解:创建子类对象d会调用自己的构造方法,子类要先帮助父类构造,而父类中调用了子类重写的方法,动态绑定了;
我们并有执行子类中的 private int num = 1;语句!所以num此时并没有赋值!所以为0
正解

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

结论: “用尽量简单的方式使对象进入可工作状态”, 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏但又及难发现的问题!

抽象类

基本语法

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

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

draw 方法前加上abstract关键字, 表示这是一个抽象方法。同时抽象方法没有方法体(没有{ }, 不能执行具体代码)。
在这里插入图片描述

对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类。
在这里插入图片描述

注意事项

  • 抽象类不能直接实例化。

在这里插入图片描述

  • 抽象类可以有一般类一样的字段和方法,语法相同。
abstract class Shape {
    protected  int longth;
    protected int wide;
    public int area(){
        return longth*wide;
    }
     abstract public  void draw();
}
  • 子类继承父类抽象类,必须重写父类中的抽象方法!
    在这里插入图片描述

  • 抽象方法不能是 private

在这里插入图片描述我们知道private修饰只能在当前类中访问,而抽象方法需要子类实现!

抽象类的作用

抽象类存在的最大意义就是为了被继承。

抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类。然后让子类重写抽象类中的抽象方法。
有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?
确实如此. 但是使用抽象类相当于多了一重编译器的校验
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了,
使用普通类编译器是不会报错的。但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题!

接口

基本语法

//定义接口类型
interface IAnimal{
    //抽象方法
    // public static final 字段
}
  • 使用interface 定义一个接口
    在这里插入图片描述

  • 接口中的方法一定是抽象方法, 因此可以省略 abstract
    接口中的方法一定是public, 因此可以省略public
    在这里插入图片描述

  • 接口中只能包含抽象方法,对于字段来说, 接口中只能包含静态常量(final static)

interface IAnimal{
    // public static final 字段 可以省略public static final
    String name = "animal"; //因为是final 修饰,需要赋初值!
    int age = 18;
    //抽象方法
    public abstract void speak();
    void eat(); //省略public abstract
}
  • 子类 使用 implements继承接口。 此时表达的含义不再是 “扩展”, 而是 “实现”

  • 在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例。

  • 接口不能单独被实例化。

  • 扩展(extends)vs 实现(implements)

扩展指的是当前已经有一定的功能了, 进一步扩充功能。
实现指的是当前啥都没有, 需要从头构造出来。

  • 接口不能用限定符修饰
    在这里插入图片描述

实现多个接口

接口弥补了java无法多继承的缺陷!
我们可以实现多个接口
基本语法

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

class 类名 implement interfa1,interface2,....interfaceN{
     //实现所有接口中的方法!
}

实例一
下面我们通过类来表示张三

//实现多个接口
interface IAnimal{
    String name = "animal";
    int age = 18;
    void speak();
    void eat();
}
interface IPeople{
    String iQ = "140";
    void study();
}
class Zhansan implements IAnimal,IPeople{
    @Override
    public void speak() {
        System.out.println("Speak Chinese!");
    }
    @Override
    public void eat() {
        System.out.println("Eat food!");
    }
    @Override
    public void study() {
        System.out.println("Study java!");
    }
}

public class Test_6 {
    public static void main(String[] args) {
        Zhansan zhansan = new Zhansan();
        zhansan.eat();
        zhansan.speak();
        zhansan.study();
    }
}

在这里插入图片描述实例二
现在我们通过一个类来表示一组动物。

class Animal { 
  protected String name; 
    public Animal(String name) { 
        this.name = name; 
    } 
}

另外我们再提供一组接口, 分别表示 “会飞的”, “会跑的”, “会游泳的”。

interface IFlying { 
    void fly(); 
} 
interface IRunning { 
    void run(); 
} 
interface ISwimming { 
    void swim(); 
}

接下来我们创建几个具体的动物
猫, 是会跑的。

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 + "正在蹬腿游泳"); 
    } 
} 

在这里插入图片描述

提示,IDEA中使用 ctrl + i 快速实现接口
在这里插入图片描述在这里插入图片描述

还有一种神奇的动物, 水陆空三栖, 叫做 “鸭子”

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 + "正在漂在水上"); 
    } 
} 

在这里插入图片描述

上面的代码展示了 Java面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口。

继承表达的含义是 is - a 语义, 而接口表达的含义是 具有xxx 特性 。

猫是一种动物, 具有会跑的特性。
青蛙也是一种动物, 既能跑, 也能游泳
鸭子也是一种动物, 既能跑, 也能游, 还能飞
这样设计有什么好处呢?

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

例如, 现在实现一个方法, 叫 “散步”

public static void walk(IRunning running) { 
    System.out.println("我带着伙伴去散步"); 
    running.run(); 
}

在这个walk方法内部, 我们并不关注到底是哪种动物, 只要参数是会跑的, 就行

Cat cat = new Cat("小猫"); 
walk(cat); 
Frog frog = new Frog("小青蛙"); 
walk(frog); 

在这里插入图片描述
甚至参数可以不是 “动物”, 只要会跑

class Robot implements IRunning { 
    private String name; 
    public Robot(String name) { 
        this.name = name; 
    } 
    @Override 
    public void run() { 
        System.out.println(this.name + "正在用轮子跑"); 
    } 
} 
Robot robot = new Robot("机器人"); 
walk(robot); 

在这里插入图片描述

接口的使用实例

我们java系统包中的很多类都实现了很多接口,使得该类具有某种属性

给对象排序!

//创建student类
class Student{
    private String name;
    private double score;
    public Student(String name,double score){
      this.name = name;
      this.score = score;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}
public static void main(String[] args) {
		//学生对象数组
        Student[] students = new Student[]{
                new Student("张三",88.7),
                new Student("李四",67),
                new Student("王五",98),
        };
        Arrays.sort(students); //排序
        System.out.println(students);
    }

显然,我们无法排序对象类型的数据,
报异常,说我们Student类没有实现Comparable接口!
在这里插入图片描述
Comparable接口中的抽象方法 compareTo
方法详细信息

int compareTo(T o)将此对象与指定的对象进行比较以进行排序。 返回一个负整数,零或正整数,因为该对象小于,等于或大于指定对象。

实现程序必须确保sgn(x.compareTo(y)) == -sgn(y.compareTo(x))所有xy。 (这意味着x.compareTo(y)必须抛出异常if y.compareTo(x)引发异常。)

实施者还必须确保关系是可传递的: (x.compareTo(y)>0 && y.compareTo(z)>0)表示x.compareTo(z)>0

最后,实施者必须确保x.compareTo(y)==0意味着sgn(x.compareTo(z)) == sgn(y.compareTo(z)) ,对于所有z

强烈建议,但不要严格要求(x.compareTo(y)==0) == (x.equals(y)) 。 一般来说,任何实现Comparable接口并违反这种情况的类应清楚地表明这一点。 推荐的语言是“注意:此类具有与equals不一致的自然排序”。

在前面的描述中,符号sgn( ) 表达式表示数学符号函数,其定义根据表达式的值是否为负,零或正返回的-1一个,0,或1

参数
o -要比较的对象。
结果
负整数,零或正整数,因为该对象小于,等于或大于指定对象。
异常
NullPointerException- 如果指定的对象为空
ClassCastException- 如果指定的对象的类型阻止它与该对象进行比较。

看到这么多文字,是不是头都大了!
没有关系,bug郭也头大,不过我知道咋用

//创建student类并且实现Comparable接口
class Student implements Comparable{
    private String name;
    private double score;
    public Student(String name,double score){
      this.name = name;
      this.score = score;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
    @Override  //实现compareTo方法
    public int compareTo(Object o) {
        Student s = (Student)o; //对o强转, o指的是待比较对象
        if (this.score > s.score) {    //当前对象的score值大
            return -1;
        } else if (this.score< s.score) { //对象o的score值大
            return 1;       
        } else {
            return 0;
        }
    }
}

我们可以看到,这时排的升序,如果要降序就只需将大于号变成小于号即可!
在这里插入图片描述sort 方法中会自动调用compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student类型的对象.
然后比较当前对象和参数对象的大小关系(按分数来算).
如果当前对象应排在参数对象之前, 返回小于0 的数字;
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
如果当前对象和参数对象不分先后, 返回0;

注意事项: 对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备compareTo这样的能力. 通过重写compareTo 方法的方式, 就可以定义比较规则。
为了进一步加深对接口的理解, 我们可以尝试自己实现一个sort方法来完成刚才的排序过程(使用冒泡排序)!

public static void sort(Comparable[] array) { 
    for (int bound = 0; bound < array.length; bound++) { 
        for (int cur = array.length - 1; cur > bound; cur--) { 
            if (array[cur - 1].compareTo(array[cur]) > 0) { 
                // 说明顺序不符合要求, 交换两个变量的位置 
                Comparable tmp = array[cur - 1]; 
                array[cur - 1] = array[cur]; 
                array[cur] = tmp; 
            } 
        } 
    } 
} 

在这里插入图片描述

接口间的继承

接口可以继承一个接口, 达到复用的效果. 使用extends关键字!

interface IAmphibious extends IRunning, ISwimming {

}
class Frog implements IAmphibious {
    @Override
    public void run() {
        System.out.println("Frog run!");
    }
    @Override
    public void swim() {
        System.out.println("Frog swim!");
    }
}
public class Test_1 {
    public static void main(String[] args) {
           Frog frog = new Frog();
           frog.run();
           frog.swim();
    }
 }

在这里插入图片描述

通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog类, 就继续要实现 run 方法,也需要实现 swim方法!

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

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bug 郭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值