【Java学习笔记】9.多态与抽象类

构成面向对象程序设计的三个主要编程机制是封装、继承和 多态。多态主要包括重载(Overload)、覆盖(Override)和动态绑定。

1.多态性的概念

多态(Polymorphism)来自于希腊语,意思是"多种形式"。多态性是指面向对象程序设计中的一个基本概念,在软件开发过程中具有独特的价值。多态性是指相同的方法名称具有多种表现形式。即“一个方法名称,多个具体实现”。java语言的多态性体现在两个方面,即运行时的多态性和编译时的多态性。

运行时的多态性(动态多态性)是指子类覆盖父类的同名方法,其调用规则是根据对象实例化时的类型而非定义类型的对象中的方法确定的。
编译时的多态性(静态多态性)是指方法的重载,其调用规则是在编译时根据方法的参数决定调用的方法。

2.重载

重载是程序设计中比较重要的语言机制,它允许同一个类的两个或多个方法具有同样的名字,但是他们具有不同的参数列表。重载被许多面向过程的程序设计语言支持,也被java这种面向对象程序设计的语言支持。

2.1 重载的概念

在java中,编译时的多态性通过方法重载来实现。重载是实现一个接口,多个方法的有效途径。


在不支持方法重载的语言中,每一个方法必须有唯一的方法名。
在C语言中,方法abs()返回整数的绝对值,方法labs()返回long类型的绝对值,方法fabs()返回浮点类型的绝对值。
虽然三个方法的功能相同,但是因为C语言不支持重载,因此方法名必须不同。


我们希望实现方法名相同,参数数据类型不同但可以实现相同功能的操作。
在java语言中所有绝对值的方法可以使用同一个java标准库方法abs,这个方法被Java.lang.Math类重载,可以处理不同类型的数据,java根据参数类型来决定使用哪个版本的方法


重载可以用来 创建一系列功能相似并且具有相同名称的方法,比如

System.out.println()

每一个版本都接受不同的参数,相当于

System.out.println(int i);
System.out.println(String s);
System.out.println(double d);

重载使具有相同功能的方法的表现形式更加的简单、整洁。


重载的表现形式包括:
(1)方法同名,但参数类型不同
(2)方法同名,但参数列表不同‘
(3)方法同名,但参数顺序不同。如果参数列表和参数类型相同,但参数顺序不同,不会实现重载。


【实例1】方法重载

package Overload;

public class OverBase {
	OverBase()		//无参构造方法
	{}; 				//无参构造方法的构造方法为空
	
	//声明没有参数的OverBase()方法
	void OverBase(){
		System.out.println("Base.OverBase()");
	}
	//声明具有int类型参数的OverBase()方法
	public int OverBase(int i) {
		System.out.println("Base.OverBase("+ i+")");
		return i;
	}
	//声明具有String类型参数的OverBase()方法
	public String OverBase(String str) {
		System.out.println("Base.OverBase("+ str+")");
		return str;
	}
	//声明具有int和String类型参数的OverBase()方法
	public void OverBase(int i,String str) {
		System.out.println("Base.OverBase("+ i+")");
		System.out.println("Base.OverBase("+ str+")");
	}
	
	//声明具有tring和Sint类型参数的OverBase()方法
	public void OverBase(String str,int i) {
		System.out.println("Base.OverBase("+ str+")");
		System.out.println("Base.OverBase("+ i+")");
	}
}

多个OverBase()方法具有不同的参数列表和或不同的参数类型,只有这样才能达到重载的目的。

2.2 重载的注意事项

不同通过访问权限的不同、方法返回值的不同、抛出异常的不同来实现方法的重载
【实例2】重载的注意事项

package Overload;

class OverBaseDetail {
	OverBaseDeatil()		//无参构造方法
	{} 				//无参构造方法的构造方法为空
	
	//声明没有参数的OverBase()方法
	void OverBaseDetail(){
		System.out.println("Base.OverBaseDetail()");
	}
	
	public void OverBaseDetail(){
		System.out.println("Base.OverBaseDetail()");
	}
	
	void OverBaseDetail(){
		System.out.println("Base.OverBaseDetail()");
	}
	void OverBaseDetail() throws IOException{
		System.out.println("void OverBaseDetail() throws IOException ");
	}
}

在上述程序的4个OverBaseDetail()方法中,任意两个方法都不能完成重载。

3.覆盖

方法覆盖即方法重写。
覆盖是面向对象的程序设计中一个比较重要的概念,它是多态性的核心,他的概念和使用与重载有相似的特点,但也存在不同之处。

3.1 覆盖的概念

如果子类中定义的某个方法名称、返回值类型及参数列表与父类的某一个方法完全相同,那么子类的方法就覆盖了父类的方法。
比如,TeacherMasterCard覆盖了MasterCard里的printInfo()方法

public void printInfo() {			    
       System.out.println("name="+getName()+"|sex="+getSex()+
                          "|money="+getMoney()+"|storey="+storey+"|title="+title.toString());
	}

注意

  • 在父类中某个方法中用关键词final修饰,则表名该方法中子类不能被重新定义。
  • 如果在一个类的定义中添加关键字final,则表示不能给该类派生其他的子类。

上述程序在定义TeacherMasterCard类时,重新定义了父类MasterCard里的printInfo()方法。这样做是为了让这个方法在子类中在与父类中的定义不同。当父类中被定义的方法在子类中调用时,应该使用关键字super()来实现对象对父类方法的使用。

	public void printInfo() {
		super.printInfo();//调用父类的printInfo()方法
		//输出子类附加信息
		System.out.println("name="+getName()+"|sex="+getSex()+"|money="+getMoney()+
							"|storey="+storey+"|title="+title.toString());
	}

上述程序使用表达式super.printInfo()完成对父类MasterCard中printInfo()方法的调用

不能使用多重super()调用。即:当C继承B、B继承A,不能在C中使用super.super.方法名的形式来实现对A方法的调用


当子类方法覆盖父类方法时,他们的名称、参数列表以及返回值的类型必须与父类的保持一致。如果不一致的话就会变成方法的重载。
【实例3】

package Overload;

public class Override extends Base{
	//声明无参数的BaseMethod方法,该方法覆盖父类的方法
	public void BaseMethod(){
		System.out.println("Coverge1.BaseMethod()");
	}
	//声明参数为int类型的BaseMethod()方法,该重载方法父类的方法
	public void BaseMethod(int i) {
		System.out.println("Coverge1.BaseMethod("+i+")"); //声明字符串信息
	}

}

class Base{
	//声明父类的Base()构造方法。
	Base(){};
	//声明无参数的BaseMethod()方法
	public void BaseMethod() {
		System.out.println("Base.BaseMethod"); //声明字符串信息
	}
	
}

方法覆盖只能发生在子类和父类之间,包括直接父类和间接父类。同一个类中的方法只能有重载的关系,不能有覆盖的关系。

3.2 覆盖与访问权限

子类的某个方法覆盖父类的方法时,这个方法的访问权限可以被扩大,但是不能被缩小。
【实例4】

package Overload;

public class Override2 extends Base2{
	//报错,因为缩小了父类BaseMethod()方法的权限。
	void BaseMethod(){
		System.out.println("Override2.BaseMethod()");
	}
}

class Base2{
	//声明Base2的无参构造方法
	Base2(){};
	
	//声明病实现BaseMethod()方法
	public void BaseMethod() {
		System.out.println("Base2.BaseMethod()");
	}
}

需要将代码中,子类的方法前面的修饰符改为public

package Overload;

public class Override2 extends Base2{
	//报错,因为缩小了父类BaseMethod()方法的权限。
	public void BaseMethod(){
		System.out.println("Override2.BaseMethod()");
	}
}

class Base2{
	//声明Base2的无参构造方法
	Base2(){};
	
	//声明病实现BaseMethod()方法
	public void BaseMethod() {
		System.out.println("Base2.BaseMethod()");
	}
}


如果某一方法在父类中的访问权限是private类型,那么这个方法是私有的,其子类不能完成对这个方法的覆盖,只能算是在子类中重新定义了一个方法

package Overload;

public class Override3 extends Base2{
	//声明Override3的无参构造方法
	Override3(){};
	
	//声明无参数的BaseMethod()方法
	public void BaseMethod() {
		System.out.println("Override3.BaseMethod()");
	}
	
	//声明具有int类型参数的BaseMethod()方法
	public void BaseMethod(int i) {
		System.out.println("Override3.BaseMethod("+i+")");
		
	}	
}
class Base2{
	//声明Base2的无参构造方法
	Base2(){};
	//声明无参数的BaseMethod()方法
	private void BaseMethod() {
		System.out.println("Override3.BaseMethod()");
	}

}

//父类中的BaseMethod()方法是私有方法,所以对于子类来说是不可访问的。所以子类的BaseMethod()方法不会完成对父类方法的覆盖,只存在子类两个同名的方法BaseMethod()重载。


3.3 静态方法覆盖

为了实现子类隐藏父类的静态方法,子类需要定义与父类同名、返回值类型相同、参数列表相同,并且抛出异常比父类少的静态方法。

【实例5】

package Overload;

public class Override4 extends Base4{
	
	//声明覆盖子类的的BaseMethod()方法
	public static void BaseMethod() {
		System.out.println("Override4.BaseMethod()");
	}	
}
class Base4{

	//声明无参数的BaseMethod()方法
	private static void BaseMethod() {
		System.out.println("Base4.BaseMethod()");
	}

}

Override4类中的BaseMethod方法覆盖了父类的BaseMethod方法。但区别在于他们的方法都是静态方法(前面加static关键字)

覆盖现象只能存在于父类和子类的静态方法之间(只有父类和子类的静态方法之间才存在覆盖现象)

3.4 覆盖与异常抛出

子类方法覆盖父类方法时,子类不能抛出父类没有抛出的异常。即子类覆盖方法抛出的异常不能超过父类抛出的异常。

package Overload;

import java.io.IOException;
import java.sql.SQLException;

public class Override5 extends Base5{
	//覆盖父类的BaseMethod()方法,抛出SQLException异常
	public static void BaseMethod() throws SQLException{
		System.out.println("Override4.BaseMethod()");		
	}
}

class Base5{
	//声明Base5的无参构造方法
	Base5(){};
	//声明BaseMethod()方法,抛出SQLException和IOException两种异常
	static void BaseMethod() throws SQLException,IOException{
		System.out.println("Base5.BaseMethod()");
		throw new IOException();		//抛出IOException异常
	}
}

Base5.BaseMethod 中抛出了SQLException和IOException两种异常,而他的子类Override5 中,只抛出了SQLException异常。

3.5 抽象方法覆盖

父类的抽象方法可以通过两种途径实现覆盖:1.子类实现父类的抽象方法 2.子类将父类的抽象方法重新声明为抽象方法 3.父类的非抽象类方法可以被覆盖为抽象方法。
【实例6】抽象方法覆盖

package Overload;

class Override601 extends Base6 {

	//覆盖子类的AbstractBaseMethod()方法
	public void AbstractBaseMethod() {
		System.out.println("Override601.AbstractBaseMethod()");
	}
}

abstract class Override602 extends Base6{
	abstract void AbstractBaseMethod();
	abstract void BaseMethod();	
}
abstract class Base6{
	//构造器
	Base6(){}
	
	//声明抽象方法AbstractBaseMethod();
	abstract void AbstractBaseMethod();
	//声明方法BaseMethod();
	void BaseMethod() {
		System.out.println("Base6.BaseMethod()");
	}
}

3.6 覆盖与重载小结

方法覆盖与方法重载的相似点:

  • 方法覆盖和方法重载的方法必须同名
  • 方法覆盖和方法重载在抽象方法和非抽象方法里都可以使用

方法覆盖与方法重载的不同点:

  • 方法覆盖要求参数列表保持一致,而方法重载的参数列表必须不一致
  • 方法覆盖的返回数据类型必须一致,而方法重载不做要求。
  • 方法覆盖只适用于父子和子类之间,而方法重载适应于子类或父类内部,或者父类与子类之间。
  • 方法覆盖在访问权限和异常抛出方面有限定,而方法重载没有任何限定
  • 方法覆盖针对一个方法只能被子类覆盖一次,而方法重载针对一个方法可以被多次重载。

【实例7】方法覆盖和方法重载

package Overload;

class OverloadAndOverrideBaseclass {
	
	//声明一个BaseMethod(int i)方法
	protected void BaseMethod(int i) {
		System.out.println("BaseMethod("+i+")");
	}
	
	//重载BaseMethod()方法
	protected void BaseMethod(String str)  {
		System.out.println("BaseMethod("+str+")");
	}
}



class OverloadAndOverrideclass extends OverloadAndOverrideBaseclass {
	//覆盖父类OverloadAndOverrideBaseclass 中的 BaseMethod(int i) 方法啊
	public void BaseMethod(int i) {
		System.out.println("OverloadAndOverrideclass.BaseMethod("+i+")");
	}
	
	//重载BaseMethod()方法
	public int BaseMethod(int i ,int v) {
		System.out.println("OverloadAndOverrideclass.BaseMethod(" + i + " " + v +" ");
		return 0;
	} 
	
}

4.绑定

绑定是指一个方法的调用域方法所在的类关联起来的一种行为。java中绑定有两种形式:1.静态绑定 2.动态绑定

  • 在程序运行之前绑定,由编译器实现,称为静态绑定
  • 在程序运行时根据对象的类型进行绑定,根据方法的调用机制实现,称为动态绑定。

静态绑定和动态绑定的区别:

  • 静态绑定发生在编译时,动态绑定发生在运行时
  • 使用private、final、static的变量或方法使用静态绑定,可以被子类重写的方法会根据运行时的对象进行动态绑定。
  • 静态绑定使用类信息来完成,而动态绑定根据使用对象信息来完成。
  • 重载(Overload)的方法使用静态绑定完成,而覆盖(override)的方法则使用动态绑定来完成。

4.1 静态绑定

静态绑定意味着程序在执行前就已经完成了绑定(在编译过程中,就已经直到这个方法到底是哪个类中的方法),然后由编译器或其他连接程序实现。Java类中的域大多采用静态绑定。

输出:an String instance in Caller ,两个参数列表不同,在编译的时候就可以区分开来
【实例8】静态绑定

package OverloadlinkMethod;

public class TestStaticLink {
	public static void main(String[] args) {
		String str = new String();
		Caller caller = new Caller();
		caller.call(str); //调用哪个call方法,在编译时进行决定
	}
	
	static class Caller{ //static类
		public void call(Object obj) { //Object参数类型的call()方法
			System.out.println("an Object instance in Caller");
		}	
		
		public void call(String str) { //Object参数类型的call()方法
			System.out.println("an String instance in Caller");
		}		
	}	
}

4.2 动态绑定

【实例9】覆盖方法的动态绑定

package OverloadlinkMethod;

public class LinkSubClass extends LinkBaseClass{

	public static void main(String[] args) {
		LinkSubClass lsc = new LinkSubClass();
		lsc.Method();
	}
	//覆盖Method()方法
	/*
	public void Method() {
		System.out.println("LinkSubClass.method");
	}*/
}

class LinkBaseClass{
	//声明LinkBaseClass类的构造方法
	LinkBaseClass(){};
	
	//声明Method()方法
	void Method() {
		System.out.println("LinkBaseClass.method");
	}	
}

在调用函数之前,是无法单凭方法的名称和参数事先决定一个Method方法应该和那个类绑定,只有当程序运行到Method()方法调用的时候才能绑定。
如果子类覆盖了父类的方法,则调用子类方法。
父类方法没有被覆盖,则调用父类方法。

4.3 继承类之间的动态绑定

定义了一个类Dog,他继承了Animal类,后者是前者的父类

Dog dog = new Dog () ; //实例化了Dog类的一个对象

也存在如下形式:

Animal ani = new Dog(); //定义了一个Animal类形的引用,指向新建的Dog类型对象。

所有父类类型的引用可以调用父类的所有属性和方法
在子类中定义而父类中没有定义的方法是不可调用的
一个方法只在父类中定义而在子类中没有覆盖,可以被父类类型的引用调用
父类中定义的方法,在子类中重写该方法,则父类类型的引用将调用子类的方法。


【实例10】继承类之间的方法动态绑定

package OverloadlinkMethod;
class BaseClass{
	
	public void method1() {
		System.out.println("BaseClass.method1()");
	}
	
	public void method3() {
		System.out.println("BaseClass.method3()");
	}
}

class SubClass extends BaseClass{
	public void method2() {
		System.out.println("SubClass.method2");
	}
	public void method3() {
		System.out.println("SubClass.method3()");
	}
}

public class PolymorphismDemo {

	public static void main(String[] args) {
		BaseClass sub = new SubClass(); //创建并初始化BaseClass类型的变量sub
		sub.method1();
		//不能调用sub.method2()
		//sub.method2();
		sub.method3();	
	}
}

综上所述:

  • 可以使用父类类型的引用指向子类的对象
  • 该引用只能调用父类中已经定义的方法和变量
  • 如果子类重载父类的一个方法,则引用调用这个方法是子类的方法。

5.抽象类

MasterCard类及其派生类TeacherMasterCard、StudentMasterCard的层次结构中,父类具有通用性甚至可以更抽象。从某种角度来说,祖先类应该更加抽象。在java中,抽象这一概念应该用关键字abstract表示。

5.1 抽象类的概念

如果在TeacherMasterCard和StudentMasterCard类中提供一个方法getDescription()要求返回一些额外的信息,而在MasterCard里没有这样的信息,则可以使用抽象方法实现。

pulic abstract String getDescription();

当一个类包含一个或多个抽象方法时,必须将类本身声明为抽象类。

abstract MasterCard{
	public abstract String getDescription();
}

抽象方法只是一个“占位符”的方法。抽象方法在子类中具体实现。
拓展抽象类有两种方法:1.子类的抽象方法不定义,子类仍然是抽象类。2.实现所有的抽象方法,类就不是抽象类了。

5.2 抽象类的应用

定义一个抽象类Goods和两个实现类Amountgoods、Gravitygoods。Goods类声明通用域(商品名称、商品价格)和通用方法(返回名字、返回价格);Amountgoods类表示按照数量销售的商品,Gravitygoods表示按照重量销售的产品。
【实例11】抽象类的应用

package OverloadlinkMethod;

abstract class Goods{
	//声明goods类的构造方法
	public Goods(String name , double price,double numberorgravity) {
		//对数据域进行初始化
		this.name = name;
		this.price = price;
		this.amountorgravity = numberorgravity;
	}
	//返回名字name
	public String getName() {
		return this.name; // 返回成员变量name
	}
	//返回价格
	public double getPrice() {
		return this.getPrice();
	}
	//声明两个抽象方法
	public abstract double getTotalPrice(); //声明getTotalPrice方法,返回总共消费
	public abstract String ToString(); //声明ToString()方法,输出商品信息
	
	protected String name;		//声明String类型数据域,标识商品名称
	protected double price;		//声明double类型数据域,标识商品价格
	protected double amountorgravity; //声明double类型数据域,标识货物数量或者重量。
}

//按照数量销售的商品
class Amountgoods extends Goods{
	//声明Amount类的构造方法
	public Amountgoods(String name,double price,int amount) 
	{
		super(name,price,amount);  //调用父类的构造方法
	}
	//Amountgoods类的特有方法,放回数量

	public int getamount() {
		return (int)this.amountorgravity;
	}
	
	//返回价格总数
	public double getTotalPrice() {
		// TODO Auto-generated method stub
		return price * amountorgravity;
	}
	@Override
	//价格信息转换为字符串
	public String ToString() {
		return this.getName()+"\t"+this.price+"\t"+"¥/个";
	}
}

//按照重量销售的商品
class Gravitygoods extends Goods{

	public Gravitygoods(String name, double price, int numberorgravity) {
		super(name, price, numberorgravity);
		
	}
	//Gravitygoods特有方法,返回重量
	public double getgravity() {
		return amountorgravity; 
	}
	
	//返回价格总数
	public double getTotalPrice() {
		// TODO Auto-generated method stub
		return price * amountorgravity;
	}

	@Override
	//价格信息转换为字符串
	public String ToString() {
		return this.getName()+"\t"+this.price+"\t"+"¥/kg";
	}
}

public class AbstractDemo {

	public static void main(String[] args) {
			Amountgoods goods1 = new Amountgoods("王老吉",2.5,5);
			Gravitygoods goods2 = new Gravitygoods("橘子",3,2);
			//输出字符串信息
			System.out.println(goods1.ToString()+"\t数量"+goods1.getamount()+"\t计价"+goods1.getTotalPrice());
			System.out.println(goods2.ToString()+"\t数量"+goods2.getgravity()+"\t计价"+goods2.getTotalPrice());
			//输出字符串信息
			System.out.println("共计"+(goods1.getTotalPrice()+goods2.getTotalPrice()));
	}
}

【Java学习笔记】9.多态与抽象类_第1张图片

5.3 抽象类的注意事项

  • 抽象类与具体类是一般与特殊的关系,具体类可以继承抽象类。
  • 抽象类不能定义对象,必须通过具体的子类定义。可以定义0个或多个抽象方法,且必须在子类中实现这些抽象方法。除此之外,抽象类中还可以定义具体的方法。
  • 抽象方法的声明与普通方法的声明不同,抽象方法没有包含大括号的主体部分,并且使用分号(;)结束
  • 抽象类不能被关键字final修饰,abstract不能和private、static、final并列修饰同一方法

6.接口

6.1 接口的概念

在OOP中,封装的对象通过类中定义的public方法与外界交互,这些方法形成了该对象与外部之间的一个操作界面,也称接口。这就像是手机的按钮一样,我们通过这个按钮来控制手机的动作,而不需要了解手机内部的电路是如何构造的。
java中的接口(Interface)是把所需的成员组合起来,用来封装一定功能的集合。它就像是一个模版,在其中定义了对象必须实现的成员及方法,而无需考虑如何集体实现。

java中的接口是一系列变量和空方法的集合。接口的定义采用interface关键字,形式与类的定义相似。
基本数据格式

[修饰符] interface 接口名 [extends 父类接口列表]{
	[public][static][final]变量;
	[public][abstract]方法;
}

接口定义参数说明

参数 说明
修饰符 用于指定接口的访问权限,可选public或默认
接口名 用于指定接口的名称,要求:1.合法字符 2.首字母大写
extends 父接口列表 用于指定接口要继承的父接口,可以继承多个父接口
方法 可选项,接口中的方法只有定义,没有方法体
接口文件名必须与接口名相同,扩展名也是.java

接口举例

public Interface IUseTelevision{
	public void power(); //电视机需要开关方法
	public void switchChannel(int Channel); //电视机需要换台的方法
}

在类中实现接口时,方法名、返回值类型、参数个数和类型必须与接口中定义的完全一致。并且接口中的所有方法都要实现。有些强制的意思。

6.2 java中接口的定义

在java中,接口有以下特点:

  • 没有构造方法,不能实例化。
  • 接口可以继承接口,但不能继承类。
  • 接口中没有普通方法,只有抽象方法
  • 接口中默认修饰符是public、abstract
  • 接口中的变量都是全局常量。默认修饰符是public、static、final

接口有默认方法和静态方法的功能
1.默认方法
java允许给接口添加一个非抽象方法并且实现它。只需要使用deault修饰符即可。

public interface DefaultFunctionInterface{
static String defaultFunction(){
		return "default function";
	}
}

2.静态方法
java还给接口添加了实现的静态方法,用static关键字修饰

public interface StaticFunctionInterface{
	static String staticFunction(){
		return "static function";
	}
}

静态方法可以通过“接口名.方法名”的形式在类中的调用

public Interface IUseTelevision{
	public void power(); //电视机需要开关方法
	public void switchChannel(int Channel); //电视机需要换台的方法
	static void displayInfo(String info){//定义静态方法
		System.out.println("TV is working"+ info);
	}
}
	//调用接口的静态方法
	public class TV implements IUseTelevision{
	public static void main(String[] args){
		final String info = "xx牌";
		IUseTelevision.display(info);
	}
}

6.3 使用接口

接口的定义方法必须在类中实现才有价值。类实现接口时使用implements关键字。
【实例12】实现接口

package OverloadlinkMethod;

public  class Television implements IUseTelevision{

	//实现power()方法
	public void power() {
		if(state == true) {
			System.out.println("Television Power OFF");
			state = false;
		}else {
			System.out.println("Television Power ON");
			state = true;			
		}
	}
	//实现换台方法
	public void switchChannel(int ch) {
		if(state == true) {
			System.out.println("Switch to Channel "+ ch);
		}
	}
	
	private static boolean state = false;
	
	public static void main(String[] args){
		
		 Television myTelevision = new Television();
		 myTelevision.power();//开电视
		 myTelevision.switchChannel(7);//换到7台
		 myTelevision.power();//关电视
		 myTelevision.switchChannel(9);//电视已关。无效
		 myTelevision.power();//开电视
	}

}

【Java学习笔记】9.多态与抽象类_第2张图片

6.4 接口的继承

  1. 接口可以继承父类接口, 一个接口可以继承多个接口。
interface C extends A,B{}
  1. 一个类可以实现多个接口
class D implements A,B,C{}
  1. 一个类只能继承一个父类,在继承类的同时也可以继承接口。
class E extends D implements A,B,C{}
  1. 一个接口继承了父类接口中,即继承了父接口的所有变量和方法

【实例13】接口的继承
IUseTelevision .java

package OverloadlinkMethod;

interface IUseTelevision extends IUseAppliance,IUseElectric{
	public void power(); //电视机需要开关方法
	public void switchChannel(int Channel); //电视机需要换台的方法
	static void displayInfo(String info) {
		System.out.println("television is working "+info);
	}
}
interface IUseAppliance{
	public void install();
}
interface IUseElectric{
	static int voltage = 220;
	public void plug();
}

Television.java

package OverloadlinkMethod;

public  class Television implements IUseTelevision{

	//实现power()方法
	public void power() {
		if(state == true) {
			System.out.println("Television Power OFF");
			state = false;
		}else {
			System.out.println("Television Power ON");
			state = true;			
		}
	}
	//实现换台方法
	public void switchChannel(int ch) {
		if(state == true) {
			System.out.println("Switch to Channel "+ ch);
		}
	}
	
	private static boolean state = false;
	
	public static void main(String[] args){
		
		
		final String info = "xx牌电视";
		 Television myTelevision = new Television();
		 myTelevision.install(); //安装
		 myTelevision.plug();//插电
		 
		 myTelevision.power();//开电视
		 myTelevision.switchChannel(7);//换到7台
		 myTelevision.power();//关电视
		 myTelevision.switchChannel(9);//电视已关。无效
		 myTelevision.power();//开电视
		 IUseTelevision.displayInfo(info);
		 
	}
	@Override
	public void install() {
		System.out.println("Use"+voltage+"Voltage");
		
	}
	@Override
	public void plug() {
		System.out.println("Electric Plugged");
	}

}

【Java学习笔记】9.多态与抽象类_第3张图片

6.5 抽象类和接口

抽象类和接口的对比

抽象类 接口
可以有默认的和静态方法实现 java添加了接口的默认方法和静态方法,并也可以实现
子类使用extends关键字来继承抽象类,如果子类不是抽象类,则需要提供抽象类中所有声明的方法体现 子类使用implements来实现接口。他需要提供接口中所有声明的方法来实现
抽象类可以有构造器 接口不能有构造器
除了不能实例化抽象类,和普通java方法没有什么区别 接口是完全不同的类型
抽象方法可以有public、protected和default修饰符 接口默认的修饰符是public。不能使用其他修饰符
抽象类可以有main方法 接口没有main方法,不能运行
描述一种抽象的类型 描述一类行为的模型

接口和抽象类在使用的时候如何选择,主要从设计模式的角度来考虑。
具体的Television类型可以使用extends关键字继承抽象类Television,或者使用implements关键字实现接口类Television。
1.抽象类

abstract class Television{
	abstract void open();
	abstract void close()
}

2.接口

interface Television{
	void open();
	void close();
}

如果是为了描述世界上的各种电视机,那么将Television定义为抽象类比定义成接口更合适,因为Television是对各种电视机的抽象,包括他们的共同特征。通过继承Television类,可以生成各种各样富有特色的电视机子类
如果设计的目标是为了描述各种各样具有开关的设备,不仅仅是电视机,那么应该采用接口的方式定义open()和close()方法,然后让所有设备都实现这个接口。


6.6 面向接口编程

在面向对象的系统中,整个系统的功能是由许许多多不同对象相互协作完成的。因此,各个对象之间的协作关系是系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计支出都是要着重考虑的,这也是系统设计的主要工作内容。

面向接口编程时面向对象方法的一个设计原则。使用这种编程思想,就是从模块(类)之间的互操作出发来考虑一个复杂的系统的。

比如乘火车这件事:火车、乘客、火车站都是类。要考虑到他们之间的协调问题,就是面向接口。乘客和火车都要在火车站完成功能,所以需要将火车站的使用方法全部放在IStation接口中。具体火车进站、出站,乘客如何检票、上车,火车站如何设计等,都在各自的接口里实现。把这个类定义好以后,再让火车、乘客、火车站3个类都实现IStation接口。
三个类可以分开由不同的人实现,并且不会导致互相之间的交流问题。

总的来说,面向接口编程有以下优点

  • 程序结构清晰,使用方便
  • 接口经过合理设计后,有利于设计的规范化,并且可以并行开发,提高效率
  • 降低程序的耦合度。随着程序复杂化,这一优点会逐渐体现出来。
  • 允许实现多个接口,弥补单继承缺陷。

8 拓展训练——重写(覆盖)父类中的方法

子类在继承了父类后,就可以使用父类中的方法了。如果父类中的方法子类不能完全适用时,可以对父类方法进行修改,这个过程叫覆盖。

package OverrideMethod;

class Employee {
	public String getInfo() {
		return "父类:我是公司的员工";
	}
}

class Manager extends Employee{
	//重写父类的测试方法
	public String getInfo() {
		return "子类:我是公司的经理";
	}
}

public class classOverrideMethodTest{
	public static void main(String[] args) {
		//新建一个父类对象,并输出父类对象的方法
		Employee emp = new Employee();
		System.out.println(emp.getInfo());
		//新建一个子类对象。并输出子类对象方法
		Manager man = new Manager();
		System.out.println(man.getInfo());
	}
} 

【Java学习笔记】9.多态与抽象类_第4张图片
“覆盖”指的是方法名称相同,但在不同的场合做不同的事。当一个子类中的方法的名称、参数、返回值类型和其父类完全一样时,就叫子类的方法覆盖了父类的方法。

Class 父类 {
	访问权限  方法返回值类型 方法1(参数1){

}
}

Class 子类 extends 父类{
	//覆盖父类中的犯法 
	访问权限 方法返回值类型 方法1(参数1){
}
}

方法重载域覆盖的区别

  1. 覆盖是子类和父类之间的关系,重载是同一个类中方法之间的关系
  2. 覆盖是只能由一个方法或一对方法之间的关系。重载是多个方法之间的关系
  3. 覆盖要求参数列表相同,重载要求参数列表不同
  4. 覆盖关系中,调用方法体是由对象的类型决定,在重载关系中,是根据调用时实参列表和形参列表来选择方法体的。

9 技术解惑

9.1 什么是java的动态绑定与静态绑定

绑定是指一个方法调用与所在方法的类关联起来。对于java来说,绑定分为静态绑定动态绑定(也叫前期绑定和后期绑定)。
1.静态绑定是指在程序执行之前方法就已经被绑定。可以理解为在编译过程中,就把方法调用与响应调用所需的代码结合起来。java中只有用关键字final static private修饰的方法和静态方法使用静态绑定,其余都使用静态绑定。
2.动态绑定是指在执行期间判断所引用对象的实际类型。根据实际的类型调用其相应的方法。java中动态绑定由JVM来实现,无需显示声明。

9.2 抽象类有什么用

抽象类的抽象方法,其子类必须实现。在团队开发时,**父类有的设计方法在设计时会发现有多重不同的实现方式,或者不知道具体怎么实现,要留给子类去实现。**这时可以不在父类中实现这些方法,而把它定义为抽象方法。
抽象的目的在于规范内在行为,可以实现部分功能。抽象方法是用来规范继承类的行为的,它必须被实现。抽象类和接口不一样。接口的目的在于扩充功能,实现多态。

你可能感兴趣的:(JAVA学习笔记,java,学习,笔记)