Java编程基础-面向对象(下)
一、抽象类
1、引入:当定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是无法确定的。Java允许在定义方法时不写方法体,不包含方法体的方法为抽象方法,抽象方法必须使用abstract关键字来修饰。如:abstract void shout();当多个类中出现相同功能,但功能主体不同,这时可以进行向上抽取。这时只抽取功能定义,不抽取功能主体。抽象类往往用来表征我们在对问题领域进行分析、 设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象,我们不能把它们实例化(拿不出一个具体的东西)所以称之为抽象。
举个栗子:我们要描述“水果”,它就是一个抽象,它有质量、体积等一些共性(水果有质量),但又缺乏特性(苹果、橘子都是水果,它们有自己的特性),我们拿不出唯一一种能代表水果的东西(因为苹果、橘子都不能代表水果),可用抽象类来描述它,所以抽象类是不能够实例化的。当我们用某个类来具体描述“苹果”时,这个类就可以继承描述“水果”的抽象类,我们都知道“苹果”是一种“水果”。
2、抽象类的特点:
a)抽象方法一定在抽象类中。
b)抽象方法和抽象类都必须被abstract关键字所修饰。
c)抽象类不可以用new创建对象,因为调用抽象方法没有意义。
d)抽象类中抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用,如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
3、抽象类定义格式
public abstract class AbstractClass //里面至少有一个抽象方法
{
public int t; //普通成员变量
public abstract void method1(); //抽象方法,抽象类的子类在类中必须实现抽象类中的抽象方法
public abstract void method2();
//非抽象方法
publi int method4 (){
…… //抽象类中可以定义具体方法
}
public void method3(){
……
}
}
代码示例:
- abstract class Animal {
- abstract void shout();// 定义抽象方法shout()
- }
- class Dog extends Animal {// 定义Dog类继承抽象类Animal
- @Override
- void shout() {// 实现抽象方法shout()
- System.out.println("汪汪");
- }
- }
- public class Demo {
- public static void main(String[] args) {
- Dog dog = new Dog();// 创建Dog的实例对象
- dog.shout();// 调用dog对象shout()方法
- }
- }
注意:包含抽象方法的类必须声明为抽象类,但抽象类可以不包含任何抽象方法,只需使用abstract关键字来修饰即可。另外,抽象类是不可以被实例化的。因为抽象类中有可能包含抽象方法,抽象方法是没有方法体的,不可以被调用。如果想调用抽象类定义的方法,则需要创建一个子类,在子类中将抽象类的抽象方法进行实现。抽象类中可以定义普通成员方法和成员变量。
特殊:抽象类中可以不定义抽象方法,这样只是不让该类建立对象。
二、接口
1、引入:如果一个抽象类中的所有方法都是抽象的,则可以将这个类用另外一种方式来定义,即接口。在定义接口时,需要使用interface关键字来声明。接口它是一系列方法的声明,是一些方法特征的集合,体现的是事物的扩展功能.并不给出具体实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
2、接口定义格式:
public interface InterfaceName
{
static final int i; //接口中的变量默认使用”public static final”来修饰,即全局常量。必须显示初始化,大写,单词间用下划线分割
public void method1(); //接口中定义的方法默认使用”public abstract”来修饰,即抽象方法。
public void method2();
}
由于接口中的方法都是抽象方法,因此不能通过实例化对象的方式来调用接口中的方法。此时需要定义一个类,并使用implements关键字实现接口中所有的方法。
3、接口特点归纳:
a) 接口中的方法都是抽象的,不能实例化对象。
b) 当一个类实现接口时,如果这个类是抽象类,则实现接口中的部分方法即可,否则需要实现接口中的所有方法。
c) 一个类通过implements关键字实现接口时,可以实现多个接口,被实现的多个接口之间要用逗号隔开。示例:
Interface Run{程序代码..}
Interface Fly{程序代码...}
Class Bird implements Run, Fly{程序代码..}
d) 一个接口可以通过extends关键字继承多个接口,接口之间用逗号隔开。
Interface Run{程序代码..}
Interface Fly{程序代码...}
interfaceBird extends Run, Fly{程序代码..}
e) 一个类继承另一个类的同时还可以实现接口,此时extends关键字必须位于implements关键字之前。
Class Dog extends Canidae implements Animal{程序代码..}//先继承,再实现。
4、接口用法示例:
- interface FlyAnimal {// 飞行动物接口
- void fly();
- }
- class Insect {// 昆虫类
- int legnum = 6;
- }
- class Bird {// 鸟类
- int legnum = 2;
- void egg() {
- }
- }
- class Bee extends Insect implements FlyAnimal {// 定义一个蜜蜂类继承昆虫类并实现飞行动物接口
- public void fly() {// 实现抽象方法
- System.out.println("Bee can fly");
- }
- }
- class Pigeon extends Bird implements FlyAnimal {// 定义一个鸽子类继承鸟类并实现飞行动物接口
- public void fly() {// 实现抽象方法
- System.out.println("pigeon can fly");
- }
- public void egg() {// 重写产蛋方法
- System.out.println("pigeon can lay eggs ");
- }
- }
- class Demo {
- public static void main(String args[]) {
- Bee b = new Bee();
- b.fly();
- System.out.println("Ant's legs are " + b.legnum);
- Pigeon p = new Pigeon();
- p.fly();
- p.egg();
- }
- }
三、多态
1、概念:所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
2、多态实现的三个必要条件
a)要有继承,在多态中必须存在有继承关系的子类和父类。
b)重写,子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
c)向上造型:父类的引用指向子类的对象。只有这样该引用才能够具备技能调用父类的方法和子类的方法。
只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
3、多态成员访问特点:
a) 成员变量,无论是编译还是运行,都参考左边(引用型变量所属的类。)
b) 成员方法,编译看左边,运行看右边对象(这就是多态精华)
c) 构造方法,子类不能继承构造方法,默认访问父类空参构造方法
d) 静态方法,无论编译还是运行,都参考左边。
多态的好处:多态提高了程序的扩展性。
多态的弊端:只能使用父类的引用访问父类中的成员,不能调用子类特有的功能。解决方法:向下转型(强制转换)
我们能转换的是父类的引用指向了自己的子类对象,该引用可以被提升,也可以被强制转换。多态自始至终都是子类对象在做着变化。
向上转型:Animal an=new Cat();
向下转型:Cat cat=(Cat)an;
Java 提供了一个关键字instanceof,它可以判断一个对象是否为某个类(或接口)的实例或者子类实例。
4、多态实现方式
在Java中有两种形式可以实现多态。继承和接口。
a)基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。
b)在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。
5、经典多态案例
- class A {
- public String show(D obj) {
- return ("A and D");
- }
- public String show(A obj) {
- return ("A and A");
- }
- }
- class B extends A {
- public String show(B obj) {
- return ("B and B");
- }
- public String show(A obj) {
- return ("B and A");
- }
- }
- class C extends B {
- }
- class D extends B {
- }
- public class Demo {
- public static void main(String[] args) {
- A a1 = new A();
- A a2 = new B();
- B b = new B();
- C c = new C();
- D d = new D();
- System.out.println("1--" + a1.show(b));
- System.out.println("2--" + a1.show(c));
- System.out.println("3--" + a1.show(d));
- System.out.println("4--" + a2.show(b));
- System.out.println("5--" + a2.show(c));
- System.out.println("6--" + a2.show(d));
- System.out.println("7--" + b.show(b));
- System.out.println("8--" + b.show(c));
- System.out.println("9--" + b.show(d));
- }
- }
输出结果为:
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D