面向对象4
2.积累完成项目的过程中常见的bug的调试
- 方式一:“硬”看,必要时,添加输出语句。
- 方式二:Debug
6.在类前,方法前,方法内具体逻辑的实现步骤等添加必要的注释.
-
类前、方法前、属性前:文档注释。
-
逻辑步骤:单行、多行注释。
1.为什么要有类的继承性?(继承性的好处)
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为之后多态性的使用,提供了前提
package com.xin.OOPTest.demo04;
/**
* 圆
*/
public class Circle {
/**
* 圆半径
*/
private double radius;
/**
* 空参构造器,初始圆半径为1
*/
public Circle() {
radius=1;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
/**
* 计算圆的面积
* @return 圆面积
*/
public double findArea(){
return Math.PI*radius*radius;
}
}
===================
package com.xin.OOPTest.demo04;
public class Cylinder extends Circle{
private double length;
/**
* 构造器,将length属性初始化为1
*/
public Cylinder() {
length=1;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
/**
* 计算圆柱体积
*/
public double findVolume(){
return findArea()*length;
}
}
=============
package com.xin.OOPTest.demo04;
public class Day72100 {
public static void main(String[] args) {
Cylinder cylinder = new Cylinder();
cylinder.setRadius(5.2);
cylinder.setLength(2.1);
double area = cylinder.findArea();
double volume = cylinder.findVolume();
System.out.println("面积是"+area+" ,体积是"+volume);
}
}
方法的重写(override / overwrite)
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
3.重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表){
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
-
子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
-
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
-
返回值类型:
- 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
- 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
- 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(只能是double)
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
super关键字的使用
1.super理解为:父类的
2.super可以用来调用:属性、方法、构造器
3.super的使用
- 我们可以在子类的方法或构造器中。通过使用"super.属性或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super. "
- 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
- 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
4.super调用构造器
- 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
- "super(形参列表)"的使用,必须声明在子类构造器的首行!
- 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
- 在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器super()。
- 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
子类对象实例化的全过程
1.从结果上来看:(继承性)
- 子类继承父类以后,就获取了父类中声明的属性或方法。
- 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2.从过程上来看:
- 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器.直到调用了java.lang.object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
package com.xin.OOPTest.demo04;
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 void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
/**
* 返回月利率
* @return
*/
public double getMonthlyInterest(){
return (annualInterestRate)/12;
}
/**
* 取钱
* @param amount
*/
public void withdraw(double amount){
if (balance>=amount){
balance-=amount;
return;
}
System.out.println("余额不足");
}
/**
* 存钱
* @param amount
*/
public void deposit(double amount){
if (amount>0)
balance+=amount;
}
}
=========
package com.xin.OOPTest.demo04;
/*
写一个用户程序测试Account类。在用户程序中,
创建一个账号为1122、余额为20000、年利率4.5%的Account对象。使用withdraw方法提款30000元,并打印余额。
再使用withdraw方法提款2500元,
使用deposit方法存款30日0元,然后打印余额和月利率。
*/
public class AccountTest {
public static void main(String[] args) {
Account account = new Account(1122,20000,0.045);
account.withdraw(30000);
System.out.println("您的账户余额为:"+account.getBalance());
account.withdraw(2500);
System.out.println("您的账户余额为:"+account.getBalance());
account.deposit(3000);
System.out.println("您的账户余额为:"+account.getBalance());
System.out.println("月利率为:"+account.getMonthlyInterest());
}
}
============
package com.xin.OOPTest.demo04;
/*
创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。
在CheckAccount类中重写withdraw方法,其算法如下:
如果(取款金额<账户余额)﹐
可直接取款
如果(取款金额>账户余额)﹐计算需要透支的额度
判断可透支额overdraft是否足够支付本次透支需要,如果可以
将账户余额修改为0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额
*/
public class CheckAccount extends Account{
private double overdraft;//可透支限额
public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}
public double getOverdraft() {
return overdraft;
}
/**
* 取款
* @param amount
*/
@Override
public void withdraw(double amount) {
if (getBalance()>amount){//余额够消费
//setBalance(getBalance()-amount);
super.withdraw(amount);
} else if (overdraft >= amount - getBalance()) {//透支额度+余额足够消费
overdraft-= amount - getBalance();//必须先减,否则就被变成0了
setBalance(0);
}else {
System.out.println("超过可透支限额");
}
}
}
============
package com.xin.OOPTest.demo04;
/*
写一个用户程序测试CheckAccount类。
在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。
使用withdraw方法提款5000元,并打印账户余额和可透支额。
再使用withdraw方法提款18000元,并打印账户余额和可透支额。再使用withdraw方法提款3000元,并打印账户余额和可透支额。
*/
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount checkAccount = new CheckAccount(1122, 20000, 0.045, 5000);
checkAccount.withdraw(5000);
double balance = checkAccount.getBalance();
double overdraft = checkAccount.getOverdraft();
System.out.println("账户余额"+balance+"可透支额"+overdraft);
checkAccount.withdraw(18000);
System.out.println("账户余额"+checkAccount.getBalance()+"可透支额"+checkAccount.getOverdraft());
checkAccount.withdraw(3000);
System.out.println("账户余额"+checkAccount.getBalance()+"可透支额"+checkAccount.getOverdraft());
}
}
面向对象特征之三:多态性
1.理解多态性:可以理解为一个事物的多种形态。
2.何为多态性:
- 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
Person p2 = new Man();//编译看左,运行看右
Person p3 =new woman();
3.多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法–--虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。总结:编译,看左边;运行,看右边。
4.多态性的使用前提:
- 类的继承关系
- 方法的重写
package com.xin.OOPTest.demo04;
//一个父类方法,多个子类可用
public class AnimalTest {
public static void main(String[] args) {
AnimalTest animalTest = new AnimalTest();
animalTest.func(new Dog());
animalTest.func(new Cat());
}
public void func(Animal animal){
animal.eat();
animal.shout();
}
}
class Animal{
public void eat(){
System.out.println("动物:进食");
}
public void shout(){
System.out.println("动物:叫");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("够吃骨头");
}
public void shout(){
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void shout(){
System.out.println("喵喵喵");
}
}
5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
小结:方法的重载与重写
2.从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。