抽象类与接口

抽象类与接口

抽象类的定义与使用

接口的定义与使用

抽象类与接口类的区别

写在前面:

(1)项目开发中,尽量不要直接继承实现好的类,尽量继承抽象类和接口。(因普通类无法强制要求子类覆写其抽象方法,抽象类和接口可以强制要求子类进行覆写操作)

(2)同时可以用抽象类和接口时,应遵循接口优先原则。(原因是抽象类必须要应用于继承关系中,接口应用不局限于继承,应用范围较广,且可避免抽象类单继承缺陷)

抽象类的定义与使用(最大缺陷是应用场景单一(继承),单继承)

定义:抽象类是普通类的超集(即抽象类有的普通类一定有(构成结构))。抽象类只是在普通类的基础上增加了一些抽象方法(抽象类中含抽象方法个数可从0-N个)。所谓抽象方法是指只声明未定义的方法(即没有方法体)。

抽象方法必须满足两个要求:(1)没有方法体。(2)必须用abstract关键字定义,表示抽象类(抽象方法)。

若只满足没有方法体,不一定是抽象方法,还有可能是本地方法,本地方法使用native关键字定义,作用是调用C或其他语言本地方法。

public abstract void fun();//抽象方法
public native void fun();//本地方法

注意:含有抽象方法的类一定是抽象类,抽象类中不一定有抽象方法。

我们通过抽象类的定义可知,抽象类中一定包含抽象方法,但因为抽象方法只定义并未实现,故抽象类不能直接实例化对象,需通过子类(非抽象类)向上转型实例化对象。

//简单使用抽象类
abstract class Person{//抽象类Person
    private String name;
    private int age;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String getName(){
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
    public abstract void print();//抽象方法
    public abstract void fun();
}
 //非抽象子类Student继承抽象类Person类,并在子类中具体实现父类中的抽象方法
class Student extends Person{
    public Student(String name,int age){
        super(name,age);
    }
    public void print(){//方法重载(父类抽象方法具体实现)
        System.out.println("你今天真好看!");
    }
    public void fun(){
        System.out.println(this.getName()+"今年"+this.getAge());
    }
}
public class Test{
    public static void main(String[] args){
        Person per=new Student("张三",18);//抽象类通过子类向上转型实例化
        per.print();
        per.fun();
    }
}

抽象类与接口

//若抽象子类继承抽象父类,则可以选择性继承父类中的抽象方法
abstract class Person{//父类Person
    private String name;
    private int age;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String getName(){
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
    public abstract void print();
    public abstract void fun();
}
abstract class Student extends Person{//抽象子类Student继承父类
    public Student(String name,int age){
        super(name,age);
    }
    public void print(){//只覆写抽象方法print()
        System.out.println("你今天真好看!");
    }
}
class Worker extends Student{
    public Worker(String name,int age){
        super(name,age);
    }
    public void fun(){//非抽象类子类必须强制覆写抽象类未覆写的抽象方法
        System.out.println(this.getName()+"今年"+this.getAge());
    }
}
public class Test{
    public static void main(String[] args){
        Person per=new Worker("张三",18);
        per.print();
        per.fun();
    }
}

抽象类与接口

注意:

(1)抽象类不可直接实例化对象,若出现则编译报错。

抽象类与接口

(2)若抽象类中定义了抽象类,子类必须全部覆写,缺一不可。

抽象类与接口

(3)抽象类中抽象方法访问权限不能使用private,因抽象类必须强制要求子类覆写抽象方法,故private与abstract不能共同使用,若同时使用则编译报错。

抽象类与接口

对于抽象类适用原则的总结:

(1)所有抽象类必须有子类,且abstract与private不可同时使用;abstract与final不可同时使用,因final定义只能继承不能修改。

(2)抽象类的子类必须覆写抽象类的所有抽象方法(子类不是抽象类);若子类为抽象类,则可以选择性覆写。(方法覆写建议使用public权限)

(3)抽象类的对象可以通过对象多态性利用子类为其实例化。

开发中抽象类的另一种存在:

abstract class Person{
    private String name="张三";
    private int age=18;
    public String getName(){
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
    public abstract void print();
    public abstract void fun();
    public static Person StudentA(){//获取抽象类对象
        class Student extends Person{//方法内部类
            public void print(){//抽象方法实现
                System.out.println(this.getName()+"今年"+this.getAge());
            }
            public void fun(){
                System.out.println("你今天真好看!");
            }
        }
        return new Student();//方法内部类只能在定义此内部类的类中使用,对其类隐藏,故Student类必须在此进行实例化
    }
}
public class Test{
    public static void main(String[] args){
        Person per=Person.StudentA();//调用Person抽象类中StudentA方法,间接调用Student对象,因抽象类不能直接实例化对象。
        per.print();
        per.fun();
    }
}

此类模式属于非正常模式,但对于子类封装具有一定好处。

抽象类的相关规定

1.抽象类是普通方法的超集,故抽象类中也可用有构造方法,也遵循调用子类构造方法时先调用父类构造方法;如果父类中无无参构造,子类构造方法中一定要使用super()语句指出要调用哪个父类构造方法。

//看一段抽象类的特殊代码
abstract class Person{
    public Person(){
        this.print();
    }
    public abstract void print();
}
class Student extends Person{
    private String name="野原新之助";
    public Student(String name){
        //super();
        //String name="野原新之助";
        this.name=name;
    }
    public void print(){
        System.out.println(this.name);
    }
}
public class Test{
    public static void main(String[] args){
        Person per=new Student("野原向日葵");
    }
}

抽象类与接口

程序运行分析图

抽象类与接口

对以上程序做修改:

abstract class Person{
    public Person(){
        this.print();
    }
    public abstract void print();
}
class Student extends Person{
    private String name="野原新之助";
    public Student(String name){
        this.name=name;
    }
    public void print(){
        System.out.println(this.name);
    }
}
public class Test{
    public static void main(String[] args){
        Person per=new Student("野原向日葵");
        per.print();//调用print()方法
    }
}

抽象类与接口
2.抽象类中允许不定义任何的抽象方法,但是此时抽象方法依然无法直接创建实例化对象。

抽象类与接口

3.抽象类中abstract与final不能同时使用,abstract与private不能同时使用。

4.抽象类也分为内部抽象类和外部抽象类,内部类可使用static修饰。

//抽象内外部类的简单实用
abstract class A{//外部抽象类
    public abstract void print();
    abstract class B{//内部抽象类
        public abstract void fun();
    }
}
class C extends A{//外部类C继承外部抽象类A
    public void print(){//抽象方法实现
        System.out.println("你今天真好看!");
    }
    class D extends B{//内部类D继承内部抽象类
        public void fun(){
            System.out.println("你明天真好看!");
        }
    }
}
public class Test{
    public static void main(String[] args){
        C c=new C();
        c.print();
        C.D d=new C().new D();
        d.fun();
    }
}

抽象类与接口

其实关于外部抽象类与内部抽象类的继承来说,子类继承哪个类实现哪个类的所有抽象方法即可。(也就是说,内部抽象类的抽象方法与外部抽象类的抽象方法无关)

//子类只继承内部抽象类
abstract class A{
    public abstract void print();
    static abstract class B{//静态内部类可直接实例化对象,与外部类无关,故要直接且只继承内部抽象类时,内部抽象类必须定义为静态内部类
        public abstract void fun();
    }
}
class C extends A.B{//此时A.B表示B为A的抽象内部类
    public void fun(){
        System.out.println("你今天真好看!");
    }
}
public class Test{
    public static void main(String[] args){
        C c=new C();
        c.fun();
    }
}

//子类只继承外部抽象类
abstract class A{
    public abstract void print();
    abstract class B{
        public abstract void fun();
    }
}
class C extends A{//子类只继承外部抽象类时可直接继承
    public void print(){
        System.out.println("你今天真好看!");
    }
}
public class Test{
    public static void main(String[] args){
        C c=new C();
        c.print();
    }
}

抽象类与接口

接口的定义与使用(解决抽象类单继承缺陷,可实现多继承,且应用场景广泛)

定义:接口就是抽象方法和全局变量的集合(JDK8之后接口扩展了一些普通方法),在JAVA中接口使用interface关键字定义。

接口命名规则:

(1)定义接口时为了区分建议在接口前添加字目I;

(2)子类若想使用接口,那么就必须使用implements关键字来实现接口(因接口是更纯粹的抽象类),且一个子类可实现多个接口(子类命名一般使用Impl结尾,表示该类实现接口)。

接口实现多继承概念:对于接口的子类(非抽象类)必须覆写(1)接口中全部的抽象方法,(2)子类命名一般使用Impl结尾,表示该类实现接口;再通过子类的向上转型实例化子类来得到接口的实例化对象。

//接口实现多继承的简单实现
interface IA{
    void printA();
}
interface IB{
    void printB();
}
class AImpl implements IA,IB{
    public void printA(){
        System.out.println("你今天真好看!");
    }
    public void printB(){
        System.out.println("你明天也好看!");
    }
}
public class Test{
    public static void main(String[] args){
        IA a=new AImpl();
        a.printA();
        IB b=new AImpl();
        b.printB();
    }
}

抽象类与接口

注意:(1)通过子类向上转型实例化对象后只能调用本接口的抽象方法。

抽象类与接口

(2)父接口之间可相互转化(两个接口有了共同子类后就建立了某种联系,可通过子类相互转换;若无共同子类则不可以相互转换)。

interface IA{
    void printA();
}
interface IB{
    void printB();
}
class AImpl implements IA,IB{
    public void printA(){
        System.out.println("你今天真好看!");
    }
    public void printB(){
        System.out.println("你明天也好看!");
    }
}
public class Test{
    public static void main(String[] args){
        IA a=new AImpl();
        a.printA();
        IB b=(IB) a;//将对象a强转为IB类型
        b.printB();
    }
}

抽象类与接口

接口使用限制

1.接口中无论属性还是方法只允许使用public权限(在接口中public static abstract final均可以省略)。

阿里规范:接口中属性与方法不加任何修饰符,public也不加。

//完整格式
interface IA{
    public static final String MSG="I am a student!";
    public abstract void print(); 
}

//简化格式
interface IA{
    String MSG="I am a student!";
    void print(); 
}

2.当一个接口既需要继承抽象类又需要实现接口时,先使用extends继承抽象类再使用implements实现接口。

//非抽象子类继承抽象类又同时实现接口时简单应用
interface IA{
    void printA();
}
abstract class B{
    public abstract void printB();
}
class CImpl extends B implements IA{
    public void printB(){
        System.out.println("你今天真好看!");
    }
    public void printA(){
        System.out.println("你明天也好看!");
    }
}
public class Test{
    public static void main(String[] args){
        B b=new CImpl();
        b.printB();
        IA a=new CImpl();
        a.printA();
    }
}

注意:

(1)非抽象子类继承抽象类又同时实现接口时,若先使用implements实现接口再继承抽象类,编译报错。

抽象类与接口

(2)父接口与父抽象类直接也可相互转换(前提是必须有同一子类)。

interface IA{
    void printA();
}
abstract class B{
    public abstract void printB();
}
class CImpl extends B implements IA{
    public void printB(){
        System.out.println("你今天真好看!");
    }
    public void printA(){
        System.out.println("你明天也好看!");
    }
}
public class Test{
    public static void main(String[] args){
        B b=new CImpl();
        b.printB();
        IA a=(IA) b;//父接口与父抽象类相互转化
        a.printA();
    }
}

(3)子类若为抽象类继承抽象类同时又实现接口时。

interface IA{
    void printA();
}
abstract class B{
    public abstract void printB();
}
//抽象子类继承抽象类与实现接口,此时implements代表实现接口IA
abstract class CImpl extends B implements IA{
   
}
class D extends CImpl implements IA{//非抽象子类继承抽象父类时,implements此时代表该类可实现接口IA,只是声明为了增强代码的可读性,并无实际意义
    public void printB(){
        System.out.println("你今天真好看!");
    }
    public void printA(){
        System.out.println("你明天也好看!");
    }
}
public class Test{
    public static void main(String[] args){
        B b=new D();
        b.printB();
        IA a=new D();
        a.printA();
    }
}

(4)抽象类可以使用implements实现多个接口,反之,接口无法继承抽象类(原因是接口是更纯粹的抽象类,结构构成只有全局常量和抽象方法,若能继承抽象类,则接口中就有其他成员方法,与接口定义矛盾,故接口无法继承抽象类)。

interface IA{
    void printA();
}
abstract class B implements IA{//抽象类可实现接口
    public abstract void printB();
}
class CImpl extends B{
    public void printA(){
        System.out.println("你今天真好看!");
    }
    public void printB(){
        System.out.println("你明天也好看!");
    }
}
public class Test{
    public static void main(String[] args){
        IA a=new CImpl();
        a.printA();
        B b=new CImpl();
        b.printB();
    }
}

抽象类与接口

(5)一个接口可以使用extends继承多个父接口。

//一个接口实现多继承简单实用
interface IA{
    void printA();
}
interface IB{
    void printB();
}
interface CImpl extends IA,IB{//接口的多继承
    void printC();
}
class D implements CImpl{
    public void printA(){
        System.out.println("haha");
    }
    public void printB(){
        System.out.println("xixi");
    }
    public void printC(){
        System.out.println("wawa");
    }
}
public class Test{
    public static void main(String[] args){
        IA a=new D();
        a.printA();
        IB b=new D();
        b.printB();
        CImpl c=new D();
        c.printC();
    }
}

抽象类与接口

接口与抽象类的区别(重要)

抽象类与接口