继承的概念和实现:
继承需要 is-a 的关系,父类更通用更抽象,子类更特殊更具体。
继承背后的思想就是基于已存在的类来构建新类。
当从已存在类继承是,就重用了它的方法和属性,还可以添加新的方法和属性来定制新类以应对需求。
父类拥有的属性和方法 子类都可以拥有,但子类拥有的属性和方法父类未必有。
约定:从其他类导出的类叫子类,被导出的类叫父类。
在Java中,除了 Object 类之外,所有类都是子类,都有唯一的父类。
一个子类只能有一个父类。 //只能有一个亲生父亲
类之间的关系:
is -a 继承体现
Has -a 组合体现
Like -a 实现接口体现
继承的意义:
代码重用、体现不同的抽象层次
父子类关系
父类更抽象、更一般
子类更具体、更特殊
extends 关键字:
在java语言中,用 extends 关键字来表示一个类继承了另一个类
继承和多态都是面向对象程序设计的特点。使用继承可以在一个父类的基础上再创建一个子类,这个子类不但拥有父类已有的属性和方法,还可以创建属于自己的属性和方法。由于子类和父类之间的关系,从而引出了方法重写和方法重载的问题。方法重写及方法重载在继承和多态性方面的应用中会存在很多问题,这些概念很容易混淆,掌握重写和重载的区别对学会使用多态的方式编写程序、提高程序的可维护性奠定了基础。
一、方法重写(0veriding)
如何定义重写:在Java程序中,类的继承关系可以产生一个子类,子类继承父类,它具备了父类所有的特征,继承了父类所有的方法和变量。子类可以定义新的特征,当子类需要修改父类的一些方法进行扩展,增大功能,程序设计者常常把这样的一种操作方法称为重写,也叫称为覆写或覆盖。重写体现了Java优越
性,重写是建立在继承关系上,它使语言结构更加丰富。在Java中的继承中,子类既可以隐藏和访问父类的方法,也可以覆盖继承父类的方法。在Java中覆盖继承父类的方法就是通过方法的重写来实现的。
所谓方法的重写是指子类中的方法与父类中继承的方法有完全相同的返回值类型、方法名、参数个数以及参数类型。这样,就可以实现对父类方法的覆盖。例如:下面的代码实现了方法的重写。
class Person//定义父类
fpublic void print(){//父类中的方法
System.out.println( 父类Person的print方法! );
}
}
class Student extends Person//定义子类继承Person类
{public void print(){//方法的重写
System.out.println( 子类Student的print方法! );
}
}
public class 0verrideExampleO1
{public static void main(String args[])
{Student s=new Student();
S.print();
}
}
运行结果:子类Student的print方法!
可以看出,当子类重写了父类中的print()方法后,使用S调用的是子类的print()方法,父类中的print()方法被覆盖了。也就是说,如果现在子类将父类中的方法重写了,调用的时候肯定是调用被重写过的方法,那么如果现在一定要调用父类中的方法该怎么办呢?此时,通过使用.. super关键就可以实现这个功能,super关键字可以从子类访问父类中的内容,如果要访问被重写过的方法,使用“super.方法名(参数列表)”的形式调用。
例如:
Class Person
{public void print () {
System.out.println( 父类Person的print方法! );
}
}
class Student extends Person
{public void print () {
super.print(://访问父类中被子类覆写过的方法
System.out.println(" 子类Student的print方法!" );
}
}
public class OverrideExample02
{public static void main(String args[])
{Student s=new Student();
S.print();
}
}
运行结果:父类Person的print方法!
子类Student的print方法 !
如果要使用super关键字不一定非要在方法重写之后使用,
也可以明确地表示某个方法是从父类中继承而来的。使用super
只是更加明确的说,要从父类中查找,就不在子类查找了。
二、重写规则
在重写方法时,需要遵循以下的规则:
(一) 父类方法的参数列表必须完全与被子类重写的方法的参数列表相同,否则不能称其为重写而是重载。..
(二) 父类的返回类型必须与被子类重写的方法返回类型相同,否则不能称其为重写而是重载。..
(三) Java中规定,被子类重写的方法不能拥有比父类方法更加严格的访问权限。访问权限大小关系为:
编写过Java程序的人就知道,父类中的方法并不是在任何情况下都可以重写的,当父类中方法的访问权限修饰符为private时,该方法只能被自己的类访问,不能被外部的类访问,在子类是不能被重写的。如果定义父类的方法为public,在子类定义为private,程序运行时就会报错。例如:
class Person
{public void print()(//public访问权限
System.out.println( "父类Person的print方法! ");
}
}
Class Stedent extends Person
{private void print()(//重写方法降低了访问权限,错误
System.out.println( "子类Student的print方法!" );
}
}
(四) 由于父类的访问权限修饰符的限制一定要大于被子类重写方法的访问权限修饰符,而private权限最小。所以如果某一个方法在父类中的访问权限是private,那么就不能在子类中对其进行重写。如果重新定义,也只是定义了一个新的方法,不会达到重写的效果。
(五) 在继承过程中如果父类当中的方法抛出异常,那么在子类中重写父类的该方法时,也要抛出异常,而且抛出的异常不能多于父类中抛出的异常(可以等于父类中抛出的异常)。换句话说,重写方法一定不能抛出新的检查异常,或者比被重写方法声明更加宽泛的检查型异常。例如,父类的一个方法申明了一个检查异常IOException,在重写这个方法时就不能抛出Exception,只能抛出IOException的子类异常,可以抛出非检查异常。
同样的道理,如果子类中创建了一个成员变量,而该变量和
父类中的一个变量名称相同,称作变量重写或属性覆盖。但是此
概念一般很少有人去研究它,因为意义不大。
三、方法重载(Overloading)
(一) 如何定义重载。方法的重写和重载只有一个字不同,很多初学者认为这两者十分相似,其实不然。方法重载是让类以统一的方式处理不同类型数据的一种手段。调用方法时通过传递给它们的不同个数和类型的参数来决定具体使用哪个方法,这就是多态性。
所谓方法重载是指在一个类中,多个方法的方法名相同,但是参数列表不同。参数列表不同指的是参数个数、参数类型或者参数的顺序不同。方法的重载在实际应用中也会经常用到。不仅是一般的方法,构造方法也可以重载。下面通过一个实例来分析。
重载的定义和使用方法。
Class Person {
{String name;
int age;
void print(){
System.out.println("姓名:" +name+"年龄:" +age);
}
void print(String a,int b){
System.out.println("姓名:" +a+"年龄:"+b);
void print(String a,int b,intC){
System.out.println("姓名:"+a+"年龄:" +b+"ID号:" +c);
}
void print(String a,int b,doubleC){
System.out.println("姓名:"+a+"年龄:" +b+"ID号:"+c);
}
}
public class OverLoadExampleOL
{publicstaticvoidmain(String args[])
{Personpl=newPerson();
p1.nanle="李明";
p1.age=22;
p1.print();
p1.print("王小早",19);
p1.print("金波",18,100325);
p1.print("婉宁",25,110903);
}
}
在上面的程序中,可以看到Person类中有多个名为 void print的方法,这就是方法的重载。执行程序,运行结果如下:
姓名:李明年龄:22
姓名:王小早年龄:l9
姓名:金波年龄:18ID号:10 00325
姓名:婉宁年龄:25ID号:110903
在方法重载时,方法之间需要存在一定的联系,因为这样可以提高程序的可读性,一般只重载功能相似的方法重载规则。重载是指我们可以定义一些名称相同的方法,通过定义不同的参数来区分这些方法,然后再调用时,Java虚拟机就会根
据不同的参数列表来选择合适的方法执行。也就是说,当一个重载方法被调用时,Java用参数的类型和.. (或)个数来决定实际调用的重载方法。因此,每个重载方法的参数的类型或个数必须是不同。虽然每个重载方法可以有不同的返回类型,
但返回类型并不足以区分所使用的是哪个方法。当Java调用一个重载方法是,参数与调用参数匹配的方法被执行。在使用重载要注意以下的几点:
1.在使用重载时只能通过不同的参数列表,必须具有不同的参数列表。例如,不同的参数类型,不同的参数个数,不同的参数顺序。当然,同一方法内的几个参数类型必须不一样,例如可以是 fun(int,float),但是不能为 fun(int,int)。
2.不能通过访问权限、返回类型、抛出的异常进行重载。
3.方法的异常类型和数目不会对重载造成影响。..
4.可以有不同的返回类型,只要参数列表不同就可以了。
5.可以有不同的访问修饰符。
6.可以抛出不同的异常。
四、方法重写与方法重载的区别
通过上面例子的分析,我们可以将方法重写和重载的区别总
结成这样一个表格,如下:
区别点 |
重载 |
重写(覆写) |
英文 |
Overloading |
Overiding |
定义 |
方法名称相同,参数的类型或个数不同 |
方法名称、参数类型、返回值类型全部相同 |
对权限没有要求 |
被重写的方法不能拥有更严格的权限 |
|
范围 |
发生在一个类中 |
发生在继承类中 |
五、结束语
在面向对象程序设计的思想中,类的继承和多态性主要就是体现在子类重写父类的方法。而构造方法的重载作为方法重载的一个典型特例,可以通过重载构造方法来表达对象的多种初始化行为。灵活的运用方法重写与方法重载,不仅能减少编码的工作量,也能大大提高程序的可维护性及可扩展性。用好重写和重载
可以设计一个结构清晰而简洁的类,可以说重写和重载在编写代码过程中的作用非同一般。
参考文献
this关键字:当前对象的引用
当子父类的成员出现同名时,可以通过super 来进行区分。
子类的构造方法中,通过super关键字调用父类的构造方法 先产生父类的对象
通过调用父类的构造方法,完成对相关属性值的初始化。
强调:
当构造一个子类对象的时候一定会先调用父类的构造方法来构造父类的对象。调用父类的构造方法的语句必须是子类构造方法中的第一条指令。
- 抽象类和抽象方法都通过abstract关键字来修饰。
- 抽象类不能实例化。 抽象类中可以没有、可以有一个或多个抽象方法,甚至可以全部为抽象方法。
- 抽象方法只有方法声明,没有方法实现。有抽象方法的类声明为抽象类。子类必须重写所有的抽象方法才能实例化,否则子类还是一个抽象类。
1 package cn.jbit.car; 2 3 /** 4 * 汽车抽象类。 5 */ 6 public abstract class MotoVehicle { 7 private String no;// 汽车牌号 8 private String brand;// 汽车品牌 9 /** 10 * 无参构造方法。 11 */ 12 public MotoVehicle() { 13 } 14 /** 15 * 有参构造方法。 16 * @param no 汽车牌号 17 * @param brand 汽车品牌 18 */ 19 public MotoVehicle(String no, String brand) { 20 this.no = no; 21 this.brand = brand; 22 } 23 public String getNo() { 24 return no; 25 } 26 public String getBrand() { 27 return brand; 28 } 29 /** 30 * 抽象方法,计算汽车租赁价。 31 * */ 32 public abstract int calRent(int days); 33 34 }
1 package cn.jbit.car; 2 3 /** 4 * 轿车类,继承汽车类。 5 */ 6 public final class Car extends MotoVehicle { 7 private String type;// 汽车型号 8 public Car() { 9 } 10 public Car(String no, String brand, String type) { 11 super(no, brand); 12 this.type = type; 13 } 14 public String getType() { 15 return type; 16 } 17 public void setType(String type) { 18 this.type = type; 19 } 20 /** 21 * 计算轿车租赁价 22 */ 23 public int calRent(int days) { 24 if ("1".equals(type)) {// 代表550i 25 return days * 500; 26 } else if ("2".equals(type)) {// 2代表商务舱GL8 27 return 600 * days; 28 } else { 29 return 300 * days; 30 } 31 } 32 }
1 package cn.jbit.car; 2 3 /** 4 * 客车类,继承汽车类。 5 */ 6 public final class Bus extends MotoVehicle { 7 private int seatCount;// 座位数 8 public Bus() { 9 } 10 public Bus(String no, String brand, int seatCount) { 11 super(no, brand); 12 this.seatCount = seatCount; 13 } 14 public int getSeatCount() { 15 return seatCount; 16 } 17 public void setSeatCount(int seatCount) { 18 this.seatCount = seatCount; 19 } 20 /** 21 * 计算客车租赁价 22 */ 23 public int calRent(int days) { 24 if (seatCount <= 16) { 25 return days * 800; 26 } else { 27 return days * 1500; 28 } 29 } 30 }
package cn.jbit.car; import java.util.Scanner; /** * 测试类。 */ public class TestRent { public static void main(String[] args) { String no,brand,mtype,type; int seatCount,days,rent; Car car; Bus bus; Scanner input = new Scanner(System.in); System.out.println("欢迎您来到汽车租赁公司!"); System.out.print("请输入要租赁的天数:"); days=input.nextInt(); System.out.print("请输入要租赁的汽车类型(1:轿车 2、客车):"); mtype = input.next(); if("1".equals(mtype)){ System.out.print("请输入要租赁的汽车品牌(1、宝马 2、别克):"); brand=input.next(); System.out.print("请输入轿车的型号 "); if("1".equals(brand)) System.out.print("1、550i:"); else System.out.print("2、商务舱GL8 3、林荫大道"); type=input.next(); no="京BK5543";//简单起见,直接指定汽车牌号 System.out.println("分配给您的汽车牌号是:"+no); car =new Car(no,brand,type); rent=car.calRent(days); } else{ System.out.print("请输入要租赁的客车品牌(1、金杯 2、金龙):"); brand=input.next(); System.out.print("请输入客车的座位数:"); seatCount=input.nextInt(); no="京AU8769";//简单起见,直接指定汽车牌号 System.out.println("分配给您的汽车牌号是:"+no); bus=new Bus(no,brand,seatCount); rent=bus.calRent(days); } System.out.println(" 顾客您好!您需要支付的租赁费用是"+rent+"。"); } }