javaSE_14_抽象类丶接口

抽象类概述

父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有 意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。类和类之间具有共同特征,将这些共同特征提取出来,形成的就是抽象类。类本身是不存在的,所以抽象类无法创建对象《无法实例化》。

抽象类与抽象方法

  • 用abstract关键字来修饰一个类,这个类叫做抽象类。
  • 用abstract来修饰一个方法,该方法叫做抽象方法。 抽象方法:只有方法的声明,没有方法的实现。以分号结束: 比如:public abstract void talk();

抽象方法

使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

定义格式:

抽象类

如果一个类包含抽象方法,那么该类必须是抽象类。抽象的方法只需在抽象类中,提供声明,不需要实现,起到了一个强制的约束作用,要求子类必须实现

定义格式:

抽象类的成员特点

  • 成员变量:既可以是变量也可以是常量
  • 构造方法:可以有空参构造也可以有参构造
  • 成员方法:可以有抽象方法也可以有普通方法

抽象的使用

继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父 类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。

代码演示

定义抽象类

/*
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可。
如何使用抽象类和抽象方法:
1. 不能直接创建new抽象类对象。
2. 必须用一个子类来继承抽象父类。
3. 子类必须覆盖重写抽象父类当中所有的抽象方法。
覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
4. 创建子类对象进行使用。
 */
public abstract class Animal {
 
    // 这是一个抽象方法,代表吃东西,但是具体吃什么(大括号的内容)不确定。
    public abstract void eat();
 
    // 这是普通的成员方法
    public void normalMethod() {
    }
 
}

定义实现类

package cn.itcast.day09.demo11;
 
public class Cat extends Animal {
 
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
 
}

此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。

定义测试类

package cn.itcast.day09.demo11;
 
public class DemoMain {
 
    public static void main(String[] args) {
    //  Animal animal = new Animal(); // 错误写法!不能直接创建抽象类对象
        Cat cat = new Cat();
        cat.eat();
    }
 
}

注意事项

抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

  • 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

  • 理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设 计。

抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

  • 理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。 

抽象类不能被 final 修饰

  • 抽象方法不能被 final 修饰,因为抽象方法就是被子类实现的

Java语言中凡是没有方法体的方法都是抽象方法。不对,错误的。

  • Object类中就有很多方法都没有方法体,都是以“;”结尾的,但他们都不是抽象方法,例如:public native int hashCode();这个方法底层调用了C++写的动态链接库程序。前面修饰符列表中没有:abstract。有一个native。表示调用JVM本地程序。

应用

  • 抽象类中可以包含方法实现,可以将一些公共的代码放到抽象类中,另外在抽象类中可以定义一些抽象的方法,这样就会存在一个约束,而子类必须实现我们定义的方法,如:teacher 必须实现 printInfo 方法,Student 也必须实现 printInfo 方法,方法名称不能修改,必须为 printInfo,这样就能实现多态的机制,有了多态的机制,我们在运行期就可以动态的调用子类的方法。所以在运行期可以灵活的互换实现。

接口

什么是接口?

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方 法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又 没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能" 的关系。
  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。接口(interface)是抽象方法和常量值定义的集合。

接口的特点

  • 用interface来定义。
  • 接口中的所有成员变量都默认是由public static final修饰的。
  • 接口中的所有抽象方法都默认是由public abstract修饰的。
  • 接口中没有构造器。
  • 接口采用多继承机制。
  • 接口支持多继承,一个接口可以继承多个接口。

定义格式

 

示例:

/*
    接口:
        1、接口也是一种“引用数据类型”。编译之后也是一个class字节码文件。
        2、接口是完全抽象的。(抽象类是半抽象。)或者也可以说接口是特殊的抽象类。
        3、接口怎么定义,语法是什么?
            [修饰符列表] interface 接口名{}
        4、接口支持多继承,一个接口可以继承多个接口。
        5、接口中只包含两部分内容,一部分是:常量。一部分是:抽象方法。接口中没有其它内容了。只有以上两部分。
        6、接口中所有的元素都是public修饰的。(都是公开的。)
        7、接口中的抽象方法定义时:public abstract修饰符可以省略。
        8、接口中的方法都是抽象方法,所以接口中的方法不能有方法体。
        9、接口中的常量的public static final可以省略。
*/
public class Test01{
    public static void main(String[] args){

        // 访问接口的常量。
        System.out.println(MyMath.PI);

        // 常量能重新赋值吗?
        //错误: 无法为最终变量PI分配值
        //MyMath.PI = 3.1415928;

        //错误: 无法为最终变量k分配值
        //MyMath.k = 111;
    }
}

// 定义接口
interface A{

}

// 接口支持继承
interface B extends A{

}

// 一个接口可以继承多个接口(支持多继承)
interface C extends A, B{
}

// 我的数学接口
interface MyMath{

    // 常量
    //public static final double PI = 3.1415926;

    // public static final可以省略吗?
    double PI = 3.1415926;

    // k是不是常量????是。
    // 接口中随便写一个变量就是常量。
    // 常量:值不能发生改变的变量。
    int k = 100;

    // 抽象方法
    //public abstract int sum(int a, int b);

    // 接口当中既然都是抽象方法,那么在编写代码的时候,public abstract可以省略吗?可以的
    int sum(int a, int b);

    // 接口中的方法可以有方法体吗?错误: 接口抽象方法不能带有主体 

    // 相减的抽象方法
    int sub(int a, int b);

}

注意事项:

  • 定义Java类的语法格式:先写extends,后写implements class SubClass extends SuperClass implements InterfaceA{ }
  • 一个类可以实现多个接口,接口也可以继承其它接口。
  • 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
  • 接口的主要用途就是被实现类实现。(面向接口编程)
  • 与继承关系类似,接口与实现类之间存在多态性
  • 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲, 接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义 (JDK7.0及之前),而没有变量和方法的实现。
  • 接口A和接口B虽然没有继承关系,但是写代码的时候,可以互转。编译器没意见。但是运行时可能出现:ClassCastException

接口和抽象类的区别

  • 接口描述了方法的特征,不给出实现,一方面解决 java 的单继承问题,实现了强大的可接插性
  • 抽象类提供了部分实现,抽象类是不能实例化的,抽象类的存在主要是可以把公共的代码移植到抽象类中
  • 面向接口编程,而不要面向具体编程(面向抽象编程,而不要面向具体编程)
  • 优先选择接口(因为继承抽象类后,此类将无法再继承,所以会丧失此类的灵活性) 

继承和实现都存在的话,代码应该怎么写?

/*
    继承和实现都存在的话,代码应该怎么写?
        extends 关键字在前。
        implements 关键字在后。
*/
public class Test04{
    public static void main(String[] args){
        // 创建对象(表面看Animal类没起作用!)
        Flyable f = new Cat(); //多态。
        f.fly();

        // 同一个接口
        Flyable f2 = new Pig();
        // 调用同一个fly()方法,最后的执行效果不同。
        f2.fly();

        Flyable f3 = new Fish();
        f3.fly();
    }
}

// 动物类:父类
class Animal{
}

// 可飞翔的接口(是一对翅膀)
// 能插拔的就是接口。(没有接口你怎么插拔。)
// 内存条插到主板上,他们之间有接口。内存条可以更换。
// 接口通常提取的是行为动作。
interface Flyable{
    void fly();
}

// 动物类子类:猫类
// Flyable是一个接口,是一对翅膀的接口,通过接口插到猫身上,让猫变的可以飞翔。
class Cat extends Animal implements Flyable{
    public void fly(){
        System.out.println("飞猫起飞,翱翔太空的一只猫,很神奇,我想做一只猫!!");
    }
}

// 蛇类,如果你不想让它飞,可以不实现Flyable接口
// 没有实现这个接口表示你没有翅膀,没有给你插翅膀,你肯定不能飞。
class Snake extends Animal{
}

// 想飞就插翅膀这个接口。
class Pig extends Animal implements Flyable{
    public void fly(){
        System.out.println("我是一只会飞的猪!!!");
    }
}

// 鱼(默认实际上是存在继承的,默认继承Object。)
/*
class Fish extends Object implements Flyable{
}
*/
class Fish implements Flyable{ //没写extends,也是有的,默认继承Object。
    public void fly(){
        System.out.println("我是六眼飞鱼(流言蜚语)!!!");
    }
}

Java 7及以前的JDK

那么接口中可以包含的内容有常量:

/*
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:
public static final 数据类型 常量名称 = 数据值;
备注:
一旦使用final关键字进行修饰,说明不可改变。
注意事项:
1. 接口当中的常量,可以省略public static final,注意:不写也照样是这样。
2. 接口当中的常量,必须进行赋值;不能不赋值。
3. 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
 */
public interface MyInterfaceConst {
 
    // 这其实就是一个常量,一旦赋值,不可以修改
    public static final int NUM_OF_MY_CLASS = 12;
 
}

也可以包含的内容抽象方法

/*
在任何版本的Java中,接口都能定义抽象方法。
格式:
public abstract 返回值类型 方法名称(参数列表);
注意事项:
1. 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
2. 这两个关键字修饰符,可以选择性地省略。
3. 方法的三要素,可以随意定义。
 */
public interface MyInterfaceAbstract {
 
    // 这是一个抽象方法
    public abstract void methodAbs1();
 
    // 这也是抽象方法
    abstract void methodAbs2();
 
    // 这也是抽象方法
    public void methodAbs3();
 
    // 这也是抽象方法
    void methodAbs4();
 
}

从Java 8开始

接口里允许定义默认方法

/*
从Java 8开始,接口里允许定义默认方法。
格式:
public default 返回值类型 方法名称(参数列表) {
    方法体
}
备注:接口当中的默认方法,可以解决接口升级的问题。
 */
public interface MyInterfaceDefault {
 
    // 抽象方法
    public abstract void methodAbs();
 
 
    // 新添加的默认方法
    public default void methodDefault() {
        System.out.println("这是新添加的默认方法");
    }
 
}

从Java 9开始

接口当中允许定义私有方法

/*
问题描述:
我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。
但是这个共有方法不应该让实现类使用,应该是私有化的。
1. 普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表) {
    方法体
}
2. 静态私有方法,解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表) {
    方法体
}
 */
public interface MyInterfacePrivateA {
 
    public default void methodDefault1() {
        System.out.println("默认方法1");
        methodCommon();
    }
 
    public default void methodDefault2() {
        System.out.println("默认方法2");
        methodCommon();
    }
 
    private void methodCommon() {
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
 
}

注意事项

  • 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。 解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
  • 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。

接口的使用

1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。

格式:

public class 实现类名称 implements 接口名称 {
                  // ...
}

2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。实现:去掉abstract关键字,加上方法体大括号。

3. 创建实现类的对象,进行使用。

接口在开发中的作用

  • 接口在开发中的作用,类似于多态在开发中的作用。多态:面向抽象编程,不要面向具体编程。降低程序的耦合度。提高程序的扩展力。

总结一句话:三个字“解耦合”

  • 面向接口编程,可以降低程序的耦合度,提高程序的扩展力。符合OCP开发原则。接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度。)
  • 接口可以解耦合,解开的是谁和谁的耦合!!!任何一个接口都有调用者和实现者。接口可以将调用者和实现者解耦合。调用者面向接口调用。实现者面向接口编写实现。
  • 以后进行大项目的开发,一般都是将项目分离成一个模块一个模块的,模块和模块之间采用接口衔接。降低耦合度。

类型和类型之间的关系:

is a(继承)、has a(关联)、like a(实现)

  • is a:Cat is a Animal(猫是一个动物)凡是能够满足is a的  表示“继承关系”A extends B
  • has a:I has a Pen(我有一支笔)凡是能够满足has a关系的表示“关联关系” 关联关系通常以“属性”的形式存在。
  • like a: Cooker like a FoodMenu(厨师像一个菜单一样)凡是能够满足like a关系的表示“实现关系”实现关系通常是:类实现接口。A implements B

类和接口的关系

  • 类与类的关系:继承关系,只能单继承,但是可以多层继承
  • 类与接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
  • 接口与接口的关系:​ 继承关系,可以单继承,也可以多继承
原文地址:https://www.cnblogs.com/wurengen/p/13261438.html