Java抽象类和接口

抽象类

抽象类的概念

在面对对象的概念中,所以的对象都是通过类来描述的,而普通的类是一个完善的功能类,可以直接实例化对象
并且在类中可以包含常量、成员变量(属性)、成员方法、构造方法等内容

抽象类和普通类不一样的是,抽象类当中可以包含抽象方法
抽象方法是指没有具体实现方法的内容,同时抽象方法必须使用abstract关键字来修饰
例如:
抽象类的语法形式:
一个类如果被 abstract 修饰就被称为抽象类
抽象类中使用 abstract 修饰的方法称为抽象方法,抽象方法可以不用给出具体的实现

// 抽象类必须使用 abstract 关键字来修饰
abstract class A{
    // 抽象类也是类,里面可以包含普通方法、属性、构造方法
    int a;
    //普通的方法
    public void fun(){
        System.out.println("方法具体实现的内容");
    }
    
    //抽象方法  没有具体实现的内容 并且要用 abstract 来修饰
    public abstract void fun1();
}

抽象类的使用限制

  1. 抽象类不能实例化对象

例如:把上面创建的抽象类进行实例化

public static void main(String[] args) {
        A a = new A();
    }

结果:
由于A是抽象类,所以不能实例化,编译器会报错
在这里插入图片描述

  1. 抽象方法里的成员方法不能是 private

注意:是抽象方法不能是 private(私有) 的权限
抽象方法没有加访问限定符时,默认是public

在这里插入图片描述

  1. 抽象方法不能被 final static 修饰,因为抽象方法要被子类重写

在这里插入图片描述

  1. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰
```java
//定义的 抽象类
abstract class A{
    // 构造方法,用来显示执行的顺序
    public A(){
        System.out.println("正在执行A类的构造方法");
    }
    // 定义的 抽象方法
    public abstract void fun();
}

class B extends A{
    // 构造方法,用来显示执行的顺序
    public B(){
        System.out.println("正在执行B类的构造方法");
    }
    @Override
    public void fun() { //必须重写抽象类的方法
        System.out.println("Hello China!");
    }
}

public class Test {
    public static void main(String[] args) {
        A a = new B(); // 向上转型
    }
}
  1. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
  2. 抽象类中可以没有抽象方法,但是有抽象方法的类一定是抽象类

练习

使用抽象类来实现一个程序,可以实现三种不同事物的行
三种的事物为:
孩子:吃饭、去学校学习知识、睡觉
父亲:吃饭、上班工作、睡觉
母亲:吃饭、做家务、睡觉

  • 定义一个抽象类
    功能和父类差不多,是对共性的抽取
    不过抽象类可以定义抽象方法,可以不用写方法具体实现过程
// 抽象类
abstract class Action{
    // 抽象方法
    public abstract void eat();  // 吃饭
    public abstract void sleep(); // 睡觉
    public abstract void action(); //行为
}
  • 定义 子类(孩子)
// 定义一个孩子的类
class Child extends Action{
    // 他的行为
    @Override
    public void eat() {
        System.out.println("孩子:吃饭!");
    }
    @Override
    public void action() {
        System.out.println("孩子:去学校学习知识!");
    }
    @Override
    public void sleep() {
        System.out.println("孩子:睡觉!");
    }
}
  • 定义 子类(父亲)
// 定义一个 父亲 的类
class Dod extends Action{
    // 他的行为
    @Override
    public void eat() {
        System.out.println("父亲:吃饭!");
    }
    @Override
    public void action() {
        System.out.println("父亲:上班工作!");
    }
    @Override
    public void sleep() {
        System.out.println("父亲:睡觉!");
    }
}
  • 定义 子类(母亲)
// 定义一个 母亲 的类
class Mom extends Action{
    // 他的行为
    @Override
    public void eat() {
        System.out.println("母亲:吃饭!");
    }
    @Override
    public void action() {
        System.out.println("母亲:做家务!");
    }
    @Override
    public void sleep() {
        System.out.println("母亲:睡觉!");
    }
}
  • 引用抽象类来调用子类
public static void main(String[] args) {
       //通过静态方法来创建对象
        fun(new Child());
        fun(new Dod());
        fun(new Mom());
    }
    public static void fun(Action a){
        a.eat();
        a.action();
        a.sleep();
    }


// 或者可以写成,不过下面的方法比较繁琐,需要一个一个引用,推荐写上面
public static void main(String[] args) {
    Action a = new Dod();
         a.eat();
         a.sleep();
         a.action();
    Action b = new Mom();
    ........
    Action c = new Child();
    ........
}

接口

接口的概念

接口可以理解成一个特殊的类,用来解决Java不能多继承的一种方法
接口里面全部是由全局常量抽象方法所组成
接口在日常使用的过程中是用来制定规范标准,在使用时,只要符合规范标准,就可以通用
在Java中,接口可以看成是:一种行为的规范(标准),是一种引用数据类型

接口语法规则

定义接口需要使用 interface关键字,其语法的形式和 类的定义差不多
在接口里不能有非抽象方法,不能有具体的实现,如果非要写进去,那么可以在方法的最前面加上 defint,加上后就可以有具体的实现

public interface 接口名称{
    public abstract void func();
    // 在接口里所有的方法是默认为 公开的抽象类
    // public abstract 是默认的,可以不写
    //例如:
    void func();
}

默认规范:

在创建接口时, 接口的命名一般以大写字母 I 开头
阿里编码规范中约定, 接口中的方法属性不要加任何修饰符号, 保持代码的简洁性

接口的使用

接口是不能直接使用的,必须要有一个 实现 接口,在类中实现接口的所以抽象方法
子类和父类是使用 extends 来继承关系,类和接口是使用 implements实现关系
接口的语法形式:

class 类的名称 implements 接口名称{
     ..........
}

例如:

// 定义一个接口
interface IShape{
    // 抽象方法
    void func();

    default void func2(){
        System.out.println("在接口使用非抽象类!必须使用 defaule 修饰");
    }

    public static void func3(){
        System.out.println("接口里可以有静态方法!");
    }
}

// A 类实现接口 IShape 必须要重写接口里所以的抽象方法
class A implements IShape{

    @Override  // 用来判断名字是否有写错
    // 重写接口里抽象方法的方法
    public void func() {
        System.out.println("接口的抽象方法必须重写!");
    }
   @Override
    public void func2() {
        System.out.println("default 写不写是看情况,需要就写!");
    }
    // static 修饰的方法不可以重写
}

接下用接口来实现多态
例如:

// 定义一个接口,使用关键字 interface
interface IShape{
    // 定义一个抽象方法!
    void draw();
}

// 使用类来实现接口的所以方法
class A implements IShape{
    // 快速的写抽象类的方法快捷键: CTRL + i(鼠标要点到类名那里,在按快捷键)
    @Override  // 用来判断名字是否有写错
    // 重写接口里抽象方法的方法
    public void draw() {
        System.out.println("用接口来实现的矩形!");
    }
}
// 定义一个 花 类来实现接口的方法
class Flower implements IShape{

    @Override
    public void draw() {
        System.out.println("花❀!");
    }
}

public class Test {
    // 这是用来调用接口的方法
    public static void func(IShape a){
        a.draw();
    }

    public static void main(String[] args) {
        // 这是向上转型来实现多态
        func(new A());
        func(new Flower());
        
        //******方法二*********
        IShape b = new A();
        func(b);
        IShape c = new Flower();
        func(c);
    }
}

实现多个接口

在Java中,类和类之间是不能多继承的,一个类只能继承一个类
但是一个类可以实现多个接口
实现多个接口是使用逗号
IDEA 中使用 ctrl + i 快速实现接口
例如:

定义多个接口

// 接口
interface IA{
    void Flying();//飞行
}
interface IB{
    void swimming();//游泳
}
interface IC{
    void run();//奔跑
}

定义一个父类

// 父类
class Animal {
    // 成员变量
    public String name;
    int age;
    // 构造方法
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 方法
    public void eat(){
        System.out.println("吃饭!");
    }
}

定义一个子类来继承父类,来实现多接口

// 子类继承父类,再实现接口
class Dog extends Animal  implements IC,IB{
    //构造方法
    public Dog(String name, int age) {
        // 初始化父类的成员变量
        super(name, age);
    }
    
     // 父类调用子类方法
    public void eat(){
        System.out.println(name + "在吃饭!");
    }
    
    // 重写接口的抽象方法
    @Override
    public void swimming() {
        System.out.println(name + "在游泳");
    }
    @Override
    public void run() {
        System.out.println(name + "在奔跑");
    }
}

最后测试:

public class Test {
 public static void walk(IC a){
        a.run();
    }
    public static void func(Animal a){
        a.eat();
    }
    public static void main(String[] args) {
        // 接口的使用,实现多态
        walk(new Dog("小黑",8));
        //父类调用子类来实现多态
        func(new Dog("花花",6));
    }
 }

结果:

在这里插入图片描述

接口间的继承

在Java中,接口和接口之间是可以多继承的
接口可以继承多个接口, 达到复用的效果. 使用extends关键字

interface A{
    void func1();
}
interface B{
    void func2();
}
// 接口和接口之间的继承
interface C extends A,B{
  void func3();
}
// 类继承接口
class D implements C{

    @Override
    public void func1() {  
    }
    @Override
    public void func2() {
    }
    @Override
    public void func3() {
    }
}
  • 如果一个接口里继承了多个接口,那么如果一个类继承了这个接口,就要重写继承接口的所以方法
    以上例子中,C 接口继承了 A 和 B接口相当于把多个接口合并在一起了,那么 D类 继承 C接口就要重写 3 个方法
  • 接口和接口使用的是 extends
    类和接口使用的是 implements
    在这里插入图片描述

接口使用实例

例如:
给对象的数组进行排序

class Student {
    public String name;
    public int age;
   // 构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "A{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class Test2 {
    public static void main(String[] args) {
        Student[] s = new Student[3];
        s[0] = new Student("小明",18);
        s[1] = new Student("小红",16);
        s[2] = new Student("小刚",23);

        Arrays.sort(s);
        System.out.println(Arrays.toString(s));
    }
}

结果:运行出错
在这里插入图片描述
以上出错的原因是没有指定根据什么进行比较,是 姓名 还是 年龄
所以需要自定义比较的内容,需要重新一个 CompartTo方法

import java.util.Arrays;

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "A{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public int compareTo(Student o) {
        if(this.age - o.age > 0){
            return 1;
        } else if (this.age - o.age < 0) {
            return -1;
        }else {
            return 0;
        }
    }
}
public class Test2 {
    public static void main(String[] args) {
        Student[] s = new Student[3];
        s[0] = new Student("小明",18);
        s[1] = new Student("小红",16);
        s[2] = new Student("小刚",23);

        Arrays.sort(s);
        System.out.println(Arrays.toString(s));
    }
}

结果:

在这里插入图片描述

在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.
然后比较当前对象和参数对象的大小关系(按年龄来算).
如果当前对象应排在参数对象之前, 返回小于 0 的数字;
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
如果当前对象和参数对象不分先后, 返回 0;
再次执行程序, 结果就符合预期了

对于 sort 方法来说, 需要传入的数组的每个对象都是 "可比较" 的, 需要具备compareTo这样的能力. 通
重写 compareTo 方法的方式, 就可以定义比较规则

下面是为了进一步加深对接口的理解,尝试自己实现一个 sort 方法来完成刚才的排序过程(使用冒泡排序)


 public static void bubbleSort(Comparable[] array){
        for (int i = 0; i < array.length-1 ;i++) {
            for (int j = 0; j < array.length-1-i; j++) {
                 // compareTo 是比较二个变量的大小
                if(array[j].compareTo(array[j+1]) > 0){
                    Comparable tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                }
            }
        }
    }
    public static void main1(String[] args) {
        Student[] s = new Student[3];
        s[0] = new Student("小明",18);
        s[1] = new Student("小红",16);
        s[2] = new Student("小刚",23);
        bubbleSort(s);
        //Arrays.sort(s);
        System.out.println(Arrays.toString(s));
    }
}

抽象类和接口的区别

1.实现方法
抽象类:抽象类里可以有具体实现的方法,和被 abstract(抽象方法),因为存在抽象方法,所以该类是抽象类
接口:接口里只能有有抽象方法,不能有方法的具体实现

2.子类使用的关键字的不同
抽象类:定义是使用abstract ,子类继承抽象类是使用 extends 关键字,如果子类不是抽象类,则需要重写抽象类的具体实现方法,其他看情况可写可不写
接口:定义是使用 intesface,子类是通过 implements 关键字来实现,需要实现接口里所以的方法

3.权限的区别
抽象类:可以使用public、protected和default修饰符
接口:只能使用 public修饰符

4.关系的不同
抽象类:一个抽象类可以实现多个接口
接口:接口是不能继承抽象类的,但是接口可以使用 extends关键字类继承多个父类接口

5.子类继承的限制
抽象类:一个子类只能继承一个抽象类
接口:一个子类可以实现多个接口

Object 类

Object 是由 Java默认提供的一个类,Object 是所有类的父类,即所有类可以使用 Object 类来引用
例如:
可以直接使用 Object 来引用类

class A{
    
}
class B{

}
public class Test2 {
    public static void func(Object obj){
    }
    public static void main(String[] args) {
        func(new A());
        func(new B());
    }
}

Object类是参数的最高统一类型,但是Object类也存在有定义好的一些方法。如下:
在这里插入图片描述

对象比较equals方法

在Java中,进行 == 比较时,
1.如果==左右两侧是基本类型变量,比较的是变量中值是否相同
2.如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同
3.如果要比较对象中内容,必须重写 Object 中的equals方法,因为equals方法默认也是按照地址比较
在这里插入图片描述
如果想比较内容就要重写一个 equals

class A{
    private String name;
    private int age;
    // 构造方法,初始化成员变量
    public A(String name,int age){
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object obj) {
        // 判断 obj 是否是空类型
        if(obj == null){
            return  false;
        }
        //判断是二个是否相等
        if (this == obj){
            return true;
        }
        // 判断是否是同一类型
        if (!(obj instanceof A)){
            return false;
        }
        // 强制类型转换
        A s = (A)obj;
        // 判断是否相等,重写的是判断名字是否相等
        if(this.name.equals(s.name) && this.age == s.age){
            return true;
        }
        return false;
    }
}
class B{

}

public class Test {
    public static void main(String[] args) {
        A a = new A("小黑",8);
        A b = new A("小黑",8);

        System.out.println(a.equals(b));
    }
}

如果比较内容是否相等时,一定要重写 equals 方法

hashcode 方法

hashcode 方法是帮忙算出一个具体对象的位置
不重写的情况下,默认两个对象的hash值不一样在这里插入图片描述
像重写equals方法一样,我们也可以重写hashcode()方法。此时我们再来看看
在这里插入图片描述

结果:

在这里插入图片描述
上面的哈希值是一样的

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

总结

抽象类

  1. 抽象类和普通的类的区别在于抽象类里有抽象方法,如果除开抽象类里的抽象方法,那么和普通类没什么区别
    抽象类可以没有抽象方法,但是有抽象方法的必须是抽象类
  2. 一个类如果被 abstract 修饰就被称为抽象类
    抽象类中使用 abstract 修饰的方法称为抽象方法,抽象方法可以不用给出具体的实现
  3. 抽象方法是不能实例化对象
    抽象方法里的成员方法不能是 private(私有的)
    抽象方法不能被 final static 修饰,因为抽象方法要被子类重写
    抽象方法必须被子类继承,继承的子类必须重写父类的抽象方法
    抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

接口

  1. 接口里面全部是由全局常量抽象方法所组成,使用接口需要使用 interface 来修饰接口
    在创建接口时, 接口的命名一般以大写字母 I 开头
  2. 接口当中的成员方法不能有具体的实现
    在JDK 1.8 开始,允许接口里有非抽象方法,但是这个方法要用 defaule 所修饰
  3. 接口里可以有静态方法
  4. 接口中的成员变量默认是 public static final 所修饰
    成员方法默认是 public abstract 所修饰,都可以不用写,编译器是默认填写了
    所以接口中的方法属性不要加任何修饰符号, 保持代码的简洁性
  5. 接口是不能实例化的,类和接口之间使用implements来实现多个接口
  6. 子类必须重写抽象方法,而且要加上 public
    接口和接口是可以进行多继承的,使用 extends 关键字
  7. 接口中是不能有静态代码构造方法
  8. 接口虽然不是类,但是在编译时也会生成一个字节码文件,后缀也是 .class
  9. 在定义一个类来实现接口,如果不想实现接口里的抽象方法,可以把这个类定义为抽象类(abstract)
    但是如果这个类被其他的类 继承,那么必须重写它的抽象方法
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值