第五章 面向对象(中)
Java 面向对象学习的三条主线:
Java 类及类的成员:属性,方法,构造器,代码块,内部类
面向对象的三大特征:封装性,继承性,多态性,(抽象性)
其他关键字:this,super,static,final,abstract,interface,import等
5.1 面向对象特征之二:继承性
5.1.1 继承的概念 Object类是所有类的父类
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
子类继承父类之后,自己也可以声明一些特有的属性和方法,实现功能的拓展。
5.1.2 类的继承格式
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:class A extends B{ }
A:子类,派生类,subclass B:父类,超类,基类,superclass
class 父类 {
}
class 子类 extends 父类 {
}
一旦子类继承了父类以后,子类就获取到了父类中声明的结构:属性,方法
5.1.3 继承的好处
①.减少了代码的冗余,提高了代码的复用性
②.便于功能的扩展
③.为之后多态性的使用,提供了前提
5.1.4 继承类型
需要注意的是 Java 不支持多继承,但支持多重继承。
一个子类只能有一个父类,一个父类可以派生出多个子类。
直接继承的类称之为:直接父类,间接继承的类称之为:间接父类。
5.1.4 继承性的实力练习
// ManKind 父类
public class ManKind {
private int sex;
private int salary;
public ManKind() {
}
public ManKind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public void manOrman() {
if (this.sex == 1) {
System.out.println("man");
} else {
System.out.println("woman");
}
}
public void employeed() {
if (this.salary == 0) {
System.out.println("no job");
} else {
System.out.println("job");
}
}
}
//Kids 子类
public class Kids extends ManKind {
private int yearsOld;
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge() {
System.out.println(yearsOld);
}
}
//测试类
public class KidsTest {
public static void main(String[] args) {
//创建子类对象
Kids someKid = new Kids();
//子类对象继承父类的成员属性和方法进行设置
someKid.setSex(1);
someKid.setSalary(4500);
someKid.manOrman();//man
someKid.employeed();//job
}
}
//圆
public class Circle {
//半径
private double radius;
public Circle() {
}
public Circle(double radius) {
this.radius = 1.0;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea(){
return radius*radius*Math.PI;
}
}
public class Cylinder extends Circle{
private double length;
public Cylinder(double length) {
this.length = 1.0;
}
public Cylinder() {
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double findVolume(){
return findArea()*length;
}
}
//测试类
public class CylinderTest {
public static void main(String[] args) {
Cylinder cylinder = new Cylinder();
cylinder.setRadius(5);
cylinder.setLength(5);
double volume = cylinder.findVolume();
System.out.println("底面半径为:"+cylinder.getRadius()+"厘米,\n圆柱体的面积为:"+String.format("%.2f", volume)+"立方厘米");
}
}
5.2 方法的重写 (override/overwrite)
5.2.1 定义
在子类中可以根据需要对父类中继承来的方法进行改造,也成为方法的重置,覆盖。在程序执行时,子类的方法将覆盖父类的方法。
子类继承父类以后,可以对父类中同名同参数的方法进行覆盖操作。
5.2.2 重写的规定
①.子类重写的方法必须和父类被重写的方法具有相同的方法名称,参数列表
②.子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
③.子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
子类不能重写父类中声明为private权限的方法
④.子类方法抛出的异常不能大于父类被重写方法的异常
5.2.3 注意
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写),因为static方法属于类的,子类无法覆盖父类的方法。
5.2.5 练习
//在kids类中加入以下重写代码
@Override
public void employeed() {
System.out.println("kids should study and no job!");
}
5.3 关键字:super
5.3.1 super的简述
super理解为:父类的,可以用来调用:属性,方法,构造器
5.3.2 super的使用
①.我们可以在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式,显式的调用父类中声明的属性和方法。但是,通常情况下,我们习惯省略“super.”
②.特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用“super.属性”的方式,表示调用的是父类中声明的属性。
③.特殊情况,当子类重写父类中的方法以后,我们想在子类的方法中调用父类被重写的方法时,则必须显式的使用“super.方法”的方式。表明调用的是父类中被重写的方法。
5.3.3 super调用构造器
①.我们可以在子类的构造器中显式的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器
②.“super(形参列表)”的使用,必须声明在子类构造器中的首行!
③.我们在类的构造器中,针对于“this(形参列表)”或“super.(形参列表)”只能二选一,不能同时出现
④.在构造器的首行,没有显式的声明“this(形参列表)”或“super.(形参列表)”,则默认调用的是父类中空参的构造器:super()
⑤.在类的多个构造器中,至少有一个类的构造器使用了“super.(形参列表)”,调用父类中的构造器
class Person {
public static void prt(String s) {
System.out.println(s);
}
Person() {
prt("父类·无参数构造方法: "+"A Person.");
}//构造方法(1)
Person(String name) {
prt("父类·含一个参数的构造方法: "+"A person's name is " + name);
}//构造方法(2)
}
public class Chinese extends Person {
Chinese() {
super(); // 调用父类构造方法(1)
prt("子类·调用父类"无参数构造方法": "+"A chinese coder.");
}
Chinese(String name) {
super(name);// 调用父类具有相同形参的构造方法(2)
prt("子类·调用父类"含一个参数的构造方法": "+"his name is " + name);
}
Chinese(String name, int age) {
this(name);// 调用具有相同形参的构造方法(3)
prt("子类:调用子类具有相同形参的构造方法:his age is " + age);
}
public static void main(String[] args) {
Chinese cn = new Chinese();
cn = new Chinese("codersai");
cn = new Chinese("codersai", 18);
}
}
5.4 子类对象实例化过程
5.4.1 子类对象实例化的全过程
①.从结果上看:(继承性)
子类继承父类以后,就获取了父类中声明的属性和方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
②.从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Obejct类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以进行调用。
综合练习
public class Account {
private int id;//账号
private double balance;//余额
private double annualInterestRate;//年利率
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;
}
public double getMonthlyInterest(){
return getAnnualInterestRate()/12;
}
//取钱
public void withdraw (double amount){
if (balance>=amount){
balance-=amount;
//System.out.println("取款: "+amount+","+"当前余额为: "+balance);
return;
}
else{
//System.out.println("余额不足!");
//System.out.println("当前余额为: "+balance);
return;
}
}
//存钱
public void deposit (double amount){
balance+=amount;
System.out.println("存款: "+amount+","+"当前余额为: "+balance);
}
}
public class AccountTest {
public static void main(String[] args) {
Account account=new Account(1122,20000,0.045);
account.withdraw(30000);
account.deposit(3000);
System.out.println("月利率为: "+account.getMonthlyInterest());
}
}
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;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
//取钱
public void withdraw (double amount){
if (amount<=getBalance()) {
//方式一,但是银行一般不会提供setblance来设置余额,所以用第二种
//setBalance(getBalance()-amount);
//方式二
super.withdraw(amount);
System.out.println("您的账户余额为: "+getBalance()+"\n"+"您的可透支余额为: "+getOverdraft());
}
else if (amount>getBalance()){
if (amount<=(getBalance()+getOverdraft())){
amount-=getBalance();
setBalance(0);
setOverdraft(getOverdraft()-amount);
System.out.println("您的账户余额为: "+getBalance()+"\n"+"您的可透支余额为: "+getOverdraft());
}else {
System.out.println("超出可透支额度!");
System.out.println("您的账户余额为: " + getBalance() + "\n" + "您的可透支余额为: " + getOverdraft());
}
}
}
}
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount checkAccount=new CheckAccount(1122,20000,0.045,5000);
checkAccount.withdraw(5000);
checkAccount.withdraw(18000);
checkAccount.withdraw(3000);
}
}