Java--多态;抽象类;接口

一、抽象类

1、抽象类定义

在class前添加 abstract 关键字

public abstract class AbstractClass

2、抽象类是无法实例化的,无法创建对象(抽象类用来被子类继承)

3、final abstract 不能联合使用,这两个关键字对立

4、抽象类的子类可以是抽象类;也可以是非抽象类

5、抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的

6、抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中

7、抽象方法定义

public abstract void doSome();

8、一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现

Java中并不是所有没有方法体的方法都是抽象方法。 Object类中就有很多方法都没有方法体,都是以“;”结尾的,但他们都不是抽象方法,如:

public native int hashCode();

hashCode()方法底层调用了C++写的动态链接库程序

前面修饰符列表中没有:abstract,有一个native,表示调用JVM本地程序

从这里可以看出,抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情。对于一个父类,如果它的某个方法在父类中没有任何意义,必须根据子类的实际需求来进行不同的实现 

包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。

抽象类和普通类的主要有三点区别

1、抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public

2、抽象类不能用来创建对象

3、如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类

在其他方面,抽象类和普通的类并没有区别

二、接口

(一)接口基础语法

接口,英文称作 interface,在软件工程中,接口泛指供别人调用的方法或者函数,它是对行为的抽象

在Java中,定一个接口的形式如下:

[修饰符列表] interface 接口名{}

如:

public interface TestInterface {
    public void doSome();
}

1、接口是一种“引用数据类型”

2、接口是完全抽象的

3、接口支持多继承

4、接口中只有 常量 + 抽象方法

5、接口中所有的元素都是public修饰的

6、接口中抽象方法的public abstract可以省略

7、接口中常量的public static final可以省略

8、接口中方法不能有方法体

9、一个非抽象的类,实现接口的时候,必须将接口中所有方法加以实现

10、一个类可以实现多个接口

11、extends和implements可以共存,extends在前,implements在后

12、使用接口,可以使用多态(父类型引用指向子类型对象)

(二)接口在开发中的作用

接口在开发中的作用,类似于多态在开发中的作用        

多态:面向抽象编程,不要面向具体编程。降低程序的耦合度。提高程序的扩展力
            /*
            public class Master{
                public void feed(Dog d){}
                public void feed(Cat c){}
                //假设又要养其它的宠物,那么这个时候需要再加1个方法。(需要修改代码了)
                //这样扩展力太差了,违背了OCP原则(对扩展开放,对修改关闭。)
            }
            */

            public class Master{
                public void feed(Animal a){
                    // 面向Animal父类编程,父类是比子类更抽象的。
                    //所以我们叫做面向抽象编程,不要面向具体编程。
                    //这样程序的扩展力就强。
                }
            }

“解耦合”,面向抽象编程可理解为:面向接口编程

面向接口编程,可以降低程序的耦合度,提高程序的扩展力;符合OCP开发原则

接口的使用离不开多态机制(接口+多态才可以达到降低耦合度);接口可以解耦合

任何一个接口都有调用者和实现者;接口可以将调用者和实现者解耦合

调用者面向接口调用;实现者面向接口编写实现

接口中可以含有变量和方法

注:

(1)接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误)

(2)接口中的方法会被隐式地指定为public abstract方法,且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现(接口中的方法必须都是抽象方法

接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量

一个类遵循某组特地的接口需要使用implements关键字,具体格式如下:

public class TestInterfaceImpl implements TestInterface{
    @Override
    public void doSome() {
        // TODO Auto-generated method stub
    }
}

大项目的开发,一般都是将项目分离成一个模块一个模块的,模块和模块之间采用接口衔接(降低耦合度)

(三)抽象类和接口的区别

1、语法区别

1、抽象类是半抽象的;接口是完全抽象的

2、抽象类中有构造方法;接口中没有构造方法

3、接口和接口之间支持多继承;类和类之间只能单继承

4、一个类可以同时实现多个接口;一个抽象类只能继承一个类(单继承)

5、接口中只允许出现常量和抽象方法

6、接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法

2、设计区别

(1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

如,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。

从这里可看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

(2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。

如:ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。

而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

参数 抽象类 接口
默认的方法实现 可以有默认的方法实现 完全抽象,根本不存在方法的实现
实现方式

子类用extends关键字来继承抽象类,如果子类

不是抽象类的话,它需要实现父级抽象类中所有抽

象方法,父类中非抽象方法可重写也可不重写

子类用implements去实现接口,需要实现接口中所有方法
构造器 抽象类可以有构造器(构造器不能用abstract修饰) 接口不能有构造器
与正常Java类的区别 正常Java类可被实例化,抽象类不能被实例化,其他区别见上下文 接口和正常java类是不同的类型
访问修饰符  抽象方法可以用public、protected、default修饰 接口默认是public、不能用别的修饰符去修饰
main方法 抽象类中可以有main方法,可以运行它 接口中不能有main方法,因此不能运行它
多继承 抽象类可继承一个类和实现多个接口 接口只能继承一个或者多个接口
速度 抽象类比接口速度快 接口稍微慢点,因为它需要去寻找类中实现的它的方法
添加新方法 如果在抽象类中添加新非abstract的方法,可以直接添加,因为非abstract方法无需在子类中实现,如果是abstact方法,则需要改变子类的代码,也要实现这个方法 只要在接口中添加方法,实现它的类就要改变,去实现这个新添加的方法

(四)抽象类和接口的应用场景

1、如果拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类

2、如果想实现多重继承,那么必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此就可以使用接口来解决(多态)

3、如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类

三、多态

面向对象编程有三大特性:封装、继承、多态

1、封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。

2、继承是为了重用父类代码。两个类若存在IS-A的关系就可以使用继承。同时继承也为实现多态做了铺垫。

3、多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

1、多态的前提:

(1)要有继承或实现关系

(2)要有方法的重写

(3)要有父类引用指向子类对象

2、多态的定义与使用格式

定义格式:
    父类类型 变量名 = new 子类类型();

如:Animal a=new Cat();

动物类

public class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}

猫类,继承动物类

//有继承关系
public class Cat extends Animal{
    //有方法的重写
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    //子类特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

狗类,继承动物类

//有继承关系
public class Dog extends Animal{
    //有方法的重写
    @Override
    public void eat() {
        System.out.println("狗啃骨头");
    }

    //子类特有方法
    public void careHome(){
        System.out.println("狗看家护院");
    }
}

测试代码:

public class AnimalDemo {
    public static void main(String[] args) {

        //编译时是Animal类,运行时内存是Cat类
        Animal a = new Cat();//向上转型;父类引用指向子类对象
        a.eat();//输出 “猫吃鱼”
        //a.catchMouse(); //编译错误,a是Animal类,没有catchMouse()方法

        //Exception in thread "main" java.lang.ClassCastException: multiple.Animal cannot be cast to multiple.Cat 
        //此时内存中a是Cat,猫不能强转为狗
        Dog d = (Dog)a;//向下转型;父类引用转为子类对象,转型的是父类引用所指向的那个子类对象
        d.eat();
    }
}

多态中的成员访问特点

1、成员变量:

编译看父类,运行看父类

2、成员方法:

编译看父类,运行看子类

类型和类型之间的关系:

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

1、is a:
                Cat is a Animal(猫是一个动物)
                能够满足is a的表示“继承关系”
                A extends B

2、has a:
                I has a Cat(我有一支猫)
                能够满足has a关系的表示“关联关系”
                关联关系通常以“属性”的形式存在。
                A{
                    B b;
                }
3、like a:
                能够满足like a关系的表示“实现关系”
                实现关系通常是:类实现接口。
                A implements B

四、final关键字

1、final修饰的类无法继承

2、final修饰的方法无法覆盖

3、final修饰的变量只能赋一次值

4、final修饰的引用一旦指向某个对象,则不能再重新指向其它对象,但该引用指向的对象内部的属性数据是可以修改的

public class FinalTest2 {
    public static void main(String[] args) {

        Person p1 = new Person();
        //可以赋值
        p1.name = "张三111";
        System.out.println(p1.name);

        final Person p2 = new Person();
        p2.name = "李四";
        System.out.println(p2.name);

        p2.name = "张三";
        System.out.println(p2.name);

        //不能编译通过;java: 无法为最终变量p2分配值
        //p2 采用 final 修饰,主要限制了 p2 指向堆区中的地址不能修改(也就是p2 只能指向一个对象)
        //p2 指向的对象的属性是可以修改的
        //p2 = new Person();
    }
}

class Person {
    String name;
}
张三111
李四
张三

5、final修饰的实例变量必须手动初始化,不能采用系统默认值

6、final修饰的实例变量一般和static联合使用,称为常量

public static final double PI = 3.1415926;

7、final 修饰的变量必须显示初始化

// final 修饰的变量必须显示初始化
// Variable 'number' might not have been initialized
//private static final Long number;


int i;
// 局部变量必须初始化,如果不使用可以不初始化
// Variable 'i' might not have been initialized
//System.out.println(i);

8、构造方法不能被 final 修饰 

猜你喜欢

转载自blog.csdn.net/MinggeQingchun/article/details/120733179