一、继承性的理解
/*
* 面向对象的特征之二:继承性 why?
*
* 一、继承性的好处
* 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类声明的功能
*
*/
public class Person {
String name;
private int age;
public Person() {
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public void running() {
System.out.println("奔跑");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Student extends Person{
String hobby;
public Student() {
}
public Student(String name,int age,String hobby) {
this.name = name;
// this.age = age;
this.hobby = hobby;
}
public void study() {
System.out.println("学习");
}
}
public class ExtendsTest {
public static void main(String[] args) {
Person person = new Person();
person.name = "张三";
person.setAge(18);
person.eat();
Student student = new Student();
student.name = "李四";
student.setAge(20);
student.hobby = "唱歌";
student.eat();
student.study();
}
}
二、继承性的练习
例题一
(1)定义一个ManKind类,包括
>成员变量int sex和int salary;
>方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);
>方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
(2)定义类Kids继承ManKind,并包括
>成员变量int yearsOld;
>方法printAge()打印yearsOld的值。
(3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问
其父类的成员变量及方法。
package com.zhe.exer;
/*
(1)定义一个ManKind类,包括
>成员变量int sex和int salary;
>方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);
>方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
*/
public class ManKind {
private int sex;//性别
private int salary;//薪资
public ManKind() {
}
public ManKind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public void manOrWoman() {
if(sex == 1) {
System.out.println("man");
}else if (sex == 0) {
System.out.println("woman");
}
}
public void employeed() {
if (salary == 0) {
System.out.println("no job");
} else {
System.out.println("job");
}
}
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;
}
}
package com.zhe.exer;
/*
* (2)定义类Kids继承ManKind,并包括
>成员变量int yearsOld;
>方法printAge()打印yearsOld的值。
*/
public class Kids extends ManKind{
private int yearOld;
public Kids() {
}
public Kids(int yearOld) {
this.yearOld = yearOld;
}
public void printAge() {
System.out.println("年龄为:" + yearOld + "岁");
}
public int getYearOld() {
return yearOld;
}
public void setYearOld(int yearOld) {
this.yearOld = yearOld;
}
}
package com.zhe.exer;
/*
* (3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问
其父类的成员变量及方法。
*/
public class KidsTest {
public static void main(String[] args) {
Kids someKids = new Kids(18);
someKids.printAge();
someKids.setSalary(0);
someKids.setSex(1);
someKids.employeed();
someKids.manOrWoman();
}
}
例题二
根据下图实现类。在CylinderTest类中创建Cylinder类的对象,设置圆
柱的底面半径和高,并输出圆柱的体积。
package com.zhe.exer1;
public class Circle {
private double radius;//半径
public Circle() {
radius = 1.0;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea() {
return Math.PI * radius * radius;
}
}
package com.zhe.exer1;
public class Cylinder extends Circle{
private double length;//高
public Cylinder() {
length = 1.0;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double findVolume() {
//return Math.PI * getRadius() * getRadius() * getLength();
//或者
return findArea() * getLength();
}
}
package com.zhe.exer1;
public class CylinderTest {
public static void main(String[] args) {
Cylinder cy = new Cylinder();
cy.setRadius(2.1);
cy.setLength(3.5);
double volume = cy.findVolume();
System.out.println("圆柱的体积为:" + volume);
double area = cy.findArea();
System.out.println("圆的面积为:" + area);
}
}
三、方法的重写
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称
为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
>子类不能重写父类中声明为private权限的方法
4. 子类方法抛出的异常不能大于父类被重写方法的异常
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为
static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
方法重写举例:
package com.zhe.java1;
//父类Person
public class Person {
String name;
int age;
public Person() {
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public void walk(int km) {
System.out.println("走路,走了" + km + "公里");
}
}
package com.zhe.java1;
//子类Student
public class Student extends Person{
String subject;
public Student() {
}
public Student(String subject) {
this.subject = subject;
}
public void study() {
System.out.println("学习的科目为:" + subject);
}
//方法的重写
public void eat() {
System.out.println("学生要吃有营养的食物");
}
}
package com.zhe.java1;
//测试类
public class StudentTest {
public static void main(String[] args) {
Student student = new Student("计算机");
student.eat();//学生要吃有营养的食物
student.walk(10);
student.study();
Person person = new Person();
person.eat();//吃饭
}
}
四、四种访问权限修饰符
Java权限修饰符public、protected、 (缺省)、 private置于类的成员定义
前,用来限定对象对该类成员的访问权限。
对于class的权限修饰只可以用public和default(缺省)。
> public类可以在任意地方被访问。
> default类只可以被同一个包内部的类访问。
五、super关键字
super关键字:
在Java类中使用super来调用父类中的指定操作:
>super可用于访问父类中定义的属性
>super可用于调用父类中定义的成员方法
>super可用于在子类构造器中调用父类的构造器
注意:
>尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
>super的追溯不仅限于直接父类
>super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
super调用构造器:
1.子类中所有的构造器默认都会访问父类中空参数的构造器
2.当父类中没有空参数的构造器时,子类的构造器必须通过this(参
数列表)或者super(参数列表)语句指定调用本类或者父类中相应的
构造器。同时,只能”二选一”,且必须放在构造器的首行
3.如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
package com.zhe.java2;
public class Person {
String name;
int age;
int id=1001;//身份证
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("人:吃饭");
}
public void walk() {
System.out.println("人:走路");
}
}
package com.zhe.java2;
public class Student extends Person{
String major;
int id=1002;//学号
public Student() {
}
public Student(String major) {
this.major = major;
}
public Student(String name,int age,String major) {
super(name, age);
this.major = major;
}
@Override
public void eat() {
System.out.println("学生要多吃点水果和蔬菜");
}
public void study() {
System.out.println("学生要学习");
eat();
super.eat();
}
public void show() {
System.out.println("name:" + name + ",age=" + age);
System.out.println("id:"+id);//1002
System.out.println("id:"+super.id);//1001
}
}
package com.zhe.java2;
public class SuperTest {
public static void main(String[] args) {
Student s=new Student();
s.show();
s.study();
System.out.println("****************");
Student s1 = new Student("Tom",20,"计算机");
s1.show();
}
}
子类对象实例化过程:
子类对象实例化的全过程
1.从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2.从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,
进而调用父类的父类的构造器...,直到调用了java.lang.Object类中空参的构造器为止。
正因为加载过所有的父类的结构,所以才可以看到内存中有父类的结构,子类对象才可以
考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
六、总结练习
1、写一个名为 Account 的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:
账号 id,余额 balance,年利率 annualInterestRate;包含的方法:访问器方法(getter 和
setter 方法),返回月利率的方法 getMonthlyInterest(),取款方法 withdraw(),存款方法
deposit()。
写一个用户程序测试 Account 类。在用户程序中,创建一个账号为 1122、余额为 20000、
年利率 4.5%的 Account 对象。使用 withdraw 方法提款 30000 元,并打印余额。
再使用 withdraw 方法提款 2500 元,使用 deposit 方法存款 3000 元,然后打印余额和月利
率。
提示:在提款方法 withdraw 中,需要判断用户余额是否能够满足提款数额的要求,如果不
能,应给出提示。
运行结果如图所示:
2、创建 Account 类的一个子类 CheckAccount 代表可透支的账户,该账户中定义一个属性
overdraft 代表可透支限额。在 CheckAccount 类中重写 withdraw 方法,其算法如下:
如果(取款金额<账户余额),
可直接取款
如果(取款金额>账户余额),
计算需要透支的额度
判断可透支额 overdraft 是否足够支付本次透支需要,如果可以
将账户余额修改为 0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额
要求:写一个用户程序测试 CheckAccount 类。在用户程序中,创建一个账号为 1122、余
额为 20000、年利率 4.5%,可透支限额为 5000 元的 CheckAccount 对象。
使用 withdraw 方法提款 5000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 18000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 3000 元,并打印账户余额和可透支额。
提示:
(1) 子类 CheckAccount 的构造方法需要将从父类继承的 3 个属性和子类自己的属性全
部初始化。
(2) 父类Account的属性balance被设置为private,但在子类CheckAccount的withdraw
方法中需要修改它的值,因此应修改父类的 balance 属性,定义其为 protected。
运行结果如下图所示:
代码如下:
package com.zhe.exer2;
public class Account {
private int id;//账号
private double balance;//账户余额
private double annualInterestRate;//年利率
public Account(int id, double balance, double annualInterestRate) {
super();
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 annualInterestRate / 12;
}
//取钱操作
public void withdraw (double amount) {
if (balance >= amount) {
balance -= amount;
return;
}
System.out.println("余额不足");
}
//存钱
public void deposit (double amount) {
if (amount > 0) {
balance += amount;
}
}
}
package com.zhe.exer2;
/*
* 写一个用户程序测试 Account 类。在用户程序中,创建一个账号为 1122、余额为 20000、
年利率 4.5%的 Account 对象。使用 withdraw 方法提款 30000 元,并打印余额。
再使用 withdraw 方法提款 2500 元,使用 deposit 方法存款 3000 元,然后打印余额和月利
率。
*/
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());
System.out.println();
account.withdraw(2500);
account.deposit(3000);
System.out.println("您的余额为:"+ account.getBalance());
System.out.println("月利率为:"+ (account.getMonthlyInterest())*100+"%");
}
}
package com.zhe.exer2;
/*
创建 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;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
@Override
public void withdraw(double amount) {
if (getBalance() >= amount) {//余额足够
//方式一:
//setBalance(getBalance() - amount);
//方式二:
super.withdraw(amount);
}else if (overdraft >= amount-getBalance()) {//透支额度+余额足够消费
overdraft -= (amount - getBalance());
//setBalance(0);
//或
super.withdraw(getBalance());
}else {
System.out.println("超过可透支限额!");
}
}
}
package com.zhe.exer2;
/*
要求:写一个用户程序测试 CheckAccount 类。在用户程序中,创建一个账号为 1122、余
额为 20000、年利率 4.5%,可透支限额为 5000 元的 CheckAccount 对象。
使用 withdraw 方法提款 5000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 18000 元,并打印账户余额和可透支额。
再使用 withdraw 方法提款 3000 元,并打印账户余额和可透支额。
*/
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount acct = new CheckAccount(1122, 20000, 0.045, 5000);
acct.withdraw(5000);
System.out.println("您的账户余额为:" + acct.getBalance());
System.out.println("您的可透支额度为:"+ acct.getOverdraft());
System.out.println();
acct.withdraw(18000);
System.out.println("您的账户余额为:" + acct.getBalance());
System.out.println("您的可透支额度为:"+ acct.getOverdraft());
System.out.println();
acct.withdraw(3000);
System.out.println("您的账户余额为:" + acct.getBalance());
System.out.println("您的可透支额度为:"+ acct.getOverdraft());
}
}