面向对象特征之二:继承性
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
那么多个类无需再定义这些属性和行为,只要继承那个类即可。
类继承语法规则:
class Subclass extends SuperClass{ }
一、继承性的好处
1.减少了代码的冗余,提高了代码的复用性
2.便于功能的扩展
3.为之后多态性的使用,提供了前提
二、继承性的格式:class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
体现:
2.1一旦子类A继承父类B以后,子类A中就获取了父类B中声明的结构:难性、方法
》特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父菲中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
2.2子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。子类和竿类的关系,不同于子集和集合的关系。
三、Java中关于继承性的规定:
1.一个类可以被多个子类继承。
2.Java中类的单继承性:一个类只能有一个父类
3.子父类是相对的概念。
4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法。
四、1.如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.object类
2.所有的java类(除java.lang.object类之外)都直接或间接的特承于java.lang.object类
3.意味着,所有的java类具有java.lang.object类声明的功能。
练习1
(1)定义一个ManKind类,包括
成员变量int sex和int salary;
方法void manOrWoman():根据sex的值显示“man” (sex1)或者“woman”(sex0);
方法void employeed():根据salary的值显示“no job” (salary==0)或者“ job”(salary!=0)。
(2)定义类Kids继承ManKind,并包括
成员变量int yearsOld;
方法printAge()打印yearsOld的值。
(3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量及方法。
根据下图实现类。在CylinderTest类中创建Cylinder类的对象,设置圆
柱的底面半径和高,并输出圆柱的体积。
Circle (圆)(父类)
-radius :double
Circle(): 构造器,将radius属性初始化为1
+setRadius(double radius) : void
+getRadius(): double
+findArea():double 计算圆的面积
Cylinder (圆柱)(子类)
-length:double
Cylinder(): 构造器,将length属性初始化为1
+setLength(double length):void
+getLength():double
+findVolume() :double 计算圆柱体积
下面展示一些 Circle
。
package ToObject.Test3;
/**
* @author dell
* @version 11.0
* @ClassnameCircle
* @Description Do
* @date 2021/7/12 20:46
* @Project Java2018
**/
/*
根据下图实现类。在CylinderTest类中创建Cylinder类的对象,设置圆
柱的底面半径和高,并输出圆柱的体积。
Circle (圆)
-radius :double
Circle(): 构造器,将radius属性初始化为1
+setRadius(double radius) : void
+getRadius(): double
+findArea():double 计算圆的面积
*/
public class Circle {
private double radius;
public Circle(){
super();
}
public Circle(double radius){
this.radius = radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public double findArea(){
double area = Math.PI * radius *radius;
return area;
}
}
下面展示一些 Cylinder extends Circle
。
package ToObject.Test3;
/**
* @author dell
* @version 11.0
* @ClassnameCylinder
* @Description Do
* @date 2021/7/12 20:46
* @Project Java2018
**/
/*
Cylinder (圆柱)
-length:double
Cylinder(): 构造器,将length属性初始化为1
+setLength(double length):void
+getLength():double
+findVolume() :double 计算圆柱体积
*/
public class Cylinder extends Circle{
private double length;
public Cylinder(){
super();
}
public Cylinder(double radius,double length){
super(radius);
this.length = length;
}
public void setLength(double length) {
this.length = length;
}
public double getLength() {
return length;
}
public double findVolume(){
double volume = findArea() * length;
// double volume = Math.PI * getRadius() * getRadius() * length;
return volume;
}
//计算圆柱表面积
public double findArea(){
double area = Math.PI * 2 * getRadius() * length + super.findArea() * 2;
return area;
}
}
下面展示一些 CylinderTest
。
package ToObject.Test3;
/**
* @author dell
* @version 11.0
* @ClassnameCylinderTest
* @Description Do
* @date 2021/7/12 20:47
* @Project Java2018
**/
public class CylinderTest {
public static void main(String[] args) {
Cylinder cy = new Cylinder(1.2,3.2);
System.out.println("圆柱的体积是:" + cy.findVolume());
System.out.println("圆柱的表面积是:" + cy.findArea());
}
}
方法的重写(override/overwrite)(直接原有基础上复制)
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
⒉.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
面试题:区分方法的重载与重写
重写的规定:
方法的声明:权限修饰符返回值类型方法名(形参列表)throws异常类型{
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
》子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
》子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为privnte权限的方法
》返回值类型:
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以A类或A类的子类
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以A类或A类的子类
父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
父类被重写的方法的返回值类型是基本数据类型(比如: double),则子类重写的方法的返回值类型必须是相同
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的。(不是重写)
关键字: super
一、super的使用,调用属性和方法
1我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
2特殊情况。当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"supe[.属性"的方式,表明调用的是父类中声明的属性。
3特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
二、super的使用:对于构造器
1我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
3我们在类的构造器中,针对于"this(形参列表)“或"super(形参列表)“只能二选一,不能同时出现
4在构造器的首行,没有显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器super()
5在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)”,调用父类中的构造器
下面展示一些 Account
。
package ToObject.Test3;
/**
* @author dell
* @version 11.0
* @ClassnameAccount
* @Description Do
* @date 2021/7/12 19:51
* @Project Java2018
**/
/*
写一个名为 Account 的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:
账号 id,余额 balance,年利率 annualInterestRate;包含的方法:访问器方法(getter 和
setter 方法),返回月利率的方法 getMonthlyInterest(),取款方法 withdraw(),存款方法
deposit()。
Account
private int id
private double balance
private double annualInterestRate
public Account (int id, double balance, double annualInterestRate )
public int getId()
public double getBalance()
public double getAnnualInterestRate()
public void setId( int id)
public void setBalance(double balance)
public void setAnnualInterestRate(double annualInterestRate)
public double getMonthlyInterest()
public void withdraw (double amount)
public void deposit (double amount)
*/
public class Account {
private int id;
private double balance;
private double annualInterestRate;
public Account(){
}
public Account(int id,double balance,double annualInterestRate){
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public int getId() {
return id;
}
public double getBalance() {
return balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setId(int id) {
this.id = id;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
public double getMonthlyInterest(){
double monthlyInterest = annualInterestRate / 12;
return monthlyInterest;
}
//提钱
public void withdraw (double amount){
if (balance >= amount){
balance -= amount;
return;
}
System.out.println("余额不足!");
return;
}
//取钱
public void deposit (double amount){
if (amount >= 0){
balance += amount;
return;
}
System.out.println("取钱失败!");
return;
}
}
下面展示一些 AccountTest
。
package ToObject.Test3;
/**
* @author dell
* @version 11.0
* @ClassnameAccountTest
* @Description Do
* @date 2021/7/12 19:52
* @Project Java2018
**/
/*
写一个用户程序测试 Account 类。在用户程序中,创建一个账号为 1122、余额为 20000、
年利率 4.5%的 Account 对象。使用 withdraw 方法提款 30000 元,并打印余额。
再使用 withdraw 方法提款 2500 元,使用 deposit 方法存款 3000 元,然后打印余额和月利
率。
*/
public class AccountTest {
public static void main(String[] args) {
Account acc = new Account(1122,20000,0.045);
acc.withdraw(30000);
System.out.println("您的账户余额为:" + acc.getBalance());
acc.withdraw(2500);
acc.deposit(3000);
System.out.println("您的账户余额为:" + acc.getBalance());
System.out.println("月利率为:" + acc.getMonthlyInterest());
}
}
下面展示一些 CheckAccount extends Account
。
package ToObject.Test3;
/**
* @author dell
* @version 11.0
* @ClassnameCheckAccount
* @Description Do
* @date 2021/7/12 20:03
* @Project Java2018
**/
/*
创建 Account 类的一个子类 CheckAccount 代表可透支的账户,该账户中定义一个属性
overdraft 代表可透支限额。在 CheckAccount 类中重写 withdraw 方法,其算法如下:
如果(取款金额<账户余额),
可直接取款
如果(取款金额>账户余额),
计算需要透支的额度
判断可透支额 overdraft 是否足够支付本次透支需要,如果可以
将账户余额修改为 0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额
*/
public class CheckAccount extends Account {
private double overdraft;
public CheckAccount(){
super();
}
public CheckAccount(int id,double balance,double annualInterestRate,double overdraft){
super(id,balance,annualInterestRate);
this.overdraft = overdraft;
}
public double getOverdraft() {
return overdraft;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
//子类中重写提钱
public void withdraw (double amount){
if (getBalance() >= amount){
// getBalance() -= amount;
//方式一
//setBalance(getBalance() - amount);
//方式二,由于和父类中结果一样,可以直接调用父类的之行结构
super.withdraw(amount);
}else if (overdraft >= amount - getBalance()){
overdraft -= (amount - getBalance());
//由于已经调用信用资金,余额为0
setBalance(0);
//super.withdraw(getBalance());
}else{
System.out.println("超过可提取限额!");
}
}
}
下面展示一些 CheckAccountTest
。
package ToObject.Test3;
/**
* @author dell
* @version 11.0
* @ClassnameCheckAccountTest
* @Description Do
* @date 2021/7/12 19:53
* @Project Java2018
**/
/*
写一个用户程序测试 CheckAccount 类。在用户程序中,创建一个账号为 1122、余
额为 20000、年利率 4.5%,可透支限额为 5000 元的 CheckAccount 对象。
使用 withdraw 方法提款 5000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 18000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 3000 元,并打印账户余额和可透支额。
提示:
(1) 子类 CheckAccount 的构造方法需要将从父类继承的 3 个属性和子类自己的属性全
部初始化。
(2) 父类Account的属性balance被设置为private,但在子类CheckAccount的withdraw
方法中需要修改它的值,因此应修改父类的 balance 属性,定义其为 protected。
*/
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount checkAccount = new CheckAccount(1122,20000,0.045,5000);
checkAccount.withdraw(5000);
System.out.println("您的账户余额为:" + checkAccount.getBalance());
System.out.println("您的可透支额度为:" + checkAccount.getOverdraft());
checkAccount.withdraw(18000);
System.out.println("您的账户余额为:" + checkAccount.getBalance());
System.out.println("您的可透支额度为:" + checkAccount.getOverdraft());
checkAccount.withdraw(3000);
System.out.println("您的账户余额为:" + checkAccount.getBalance());
System.out.println("您的可透支额度为:" + checkAccount.getOverdraft());
}
}
子类对象实例化过程
一、从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
二、从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会也接或间接的调用其父类的构造器,进而调用父类的父类的构造器…直到调用了java.lang.object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调思了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。