目录
继承、super、this、抽象类
一、继承
1. 继承的概述
- **面向对象三大特征:**封装性、继承性、多态性。
- 继承是多态的前提,如果没有继承,就没有多态。
- 继承主要解决的问题是:共性抽取。
- 继承关系中的特点:
- 子类可以拥有父类的“内容”。
- 子类还可以拥有自己专有的内容。
- 父类:也叫基类、超类。
- 子类:也叫派生类。
2. 继承的格式
-
在继承关系中,“子类就是一个父类”。也就是说,子类可以被当做父类看待。 例如,父类是“员工”,子类是“讲师”,那么“讲师就是一个员工”。关系:is-a。
-
定义父类的格式:(一个普通的类定义)
public class 父类名称{ //... }
-
定义子类的格式:
public class 子类名称 extends 父类名称{ //... }
//定义一个Employee父类
public class Employee {
public void method() {
System.out.println("方法执行!");
}
}
//定义一个Teacher子类,继承父类Employee
public class Teacher extends Employee{
}
//定义一个Assistant子类,继承父类Employee
public class Assistant extends Employee {
}
public class Test {
public static void main(String[] args) {
Employee employee = new Employee();
employee.method(); // 方法执行!
Teacher teacher = new Teacher();
teacher.method(); // 方法执行!
Assistant assistant = new Assistant();
assistant.method(); // 方法执行!
}
}
3. 继承中成员变量的访问特点
-
在父子类的继承关系中,如果成员变量重名,则创建子类对象时,访问有两种方式:
- **直接通过子类对象访问成员变量:**等号左边是谁,就优先使用谁,没有则向上找。
- **间接通过成员方法访问成员变量:**该方法属于谁,就优先使用谁,没有则向上找。
//父类 public class Fu { int numFu = 10; int num = 100; public void methodFu() { System.out.println(num); } } //子类 public class Zi extends Fu{ int numZi = 20; int num = 200; public void methodZi() { System.out.println(num); } } public class Test { public static void main(String[] args) { Fu fu = new Fu(); System.out.println(fu.numFu); // 10 //优先使用等号左边的 Fu fu = new Fu(); System.out.println(fu.num); // 100 //方法属于谁就优先使用谁 fu.methodFu(); // 100 Zi zi = new Zi(); System.out.println(zi.numFu); // 10 System.out.println(zi.numZi); // 20 //优先使用等号左边的 Zi zi = new Zi(); System.out.println(zi.num); // 200 //方法属于谁就优先使用谁 zi.methodZi(); // 200 zi.methodFu(); // 100 } }
4. 区分子类方法中重名的三种变量
- 局部变量:直接写
局部变量名
- 本类的成员变量:
this.成员变量名
- 父类的成员变量:
super.成员变量名
//定义父类
public class Fu {
int num = 10;
}
//定义子类
public class Zi extends Fu{
int num = 20;
public void method() {
int num = 30;
System.out.println(num); // 30
System.out.println(this.num); // 20
System.out.println(super.num); // 10
}
}
5. 继承中成员方法的访问特点
- 在父子类的继承关系中,创建子类对象,访问成员方法的规则:
- 创建的对象是谁,就优先用谁,如果没有则向上找。
- **注意事项:**无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类。
//父类
public class Fu {
public void methodFu() {
System.out.println("父类方法执行了!");
}
public void method() {
System.out.println("父类同名方法执行了!");
}
}
//子类
public class Zi extends Fu{
public void methodZi() {
System.out.println("子类方法执行了!");
}
public void method() {
System.out.println("子类同名方法执行了!");
}
}
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu(); // 父类方法执行了!
zi.methodZi(); // 子类方法执行了!
//创建的是子类对象 new Zi() ,所以优先使用子类方法。
zi.method(); // 子类方法执行了!
}
}
6.继承中方法的覆盖重写
-
概念与特点
- 重写(Override):在继承关系当中,方法名称一样,参数列表也一样。也叫,覆盖、覆写。
- 重载(Overload):方法名称一样,参数列表不一样。
- **方法的覆盖重写特点:**创建的是子类对象,则优先使用子类方法。
-
注意事项
- 必须保证父子类之间方法的名称相同,参数列表也相同。
@Override
写在方法前面,可以用来检测是不是有效的正确覆盖重写。也可以不使用。
- 子类方法的返回值必须小于等于父类方法的返回值范围。
- 扩展:
java.lang.Object
类是所有类的公共最高父类(祖宗类)。
- 扩展:
- 子类方法的权限必须大于等于父类方法的权限修饰符。
- 扩展:
public
>protected
>(default)
>private
(default)
不是关键字default
,而是什么都不写留空。
- 扩展:
- 必须保证父子类之间方法的名称相同,参数列表也相同。
-
应用场景
//父类 public class Phone { public void call() { System.out.println("打电话"); } public void send() { System.out.println("发短信"); } public void show() { System.out.println("显示号码"); } } //子类 public class NewPhone extends Phone{ @Override public void show() { //System.out.println("显示号码"); super.show(); //调用父类的show方法 System.out.println("显示姓名"); System.out.println("显示头像"); } } public class Test { public static void main(String[] args) { Phone phone = new Phone(); phone.call(); // 打电话 phone.send(); // 发短信 phone.show(); // 显示号码 NewPhone newPhone = new NewPhone(); newPhone.call(); // 打电话 newPhone.send(); // 发短信 newPhone.show(); // 显示号码 // 显示姓名 // 显示头像 } }
7. 继承中构造方法的访问特点
- 子类构造方法当中有一个默认隐含的
super()
调用,所以一定是先调用的父类构造,后执行的子类构造。 - 子类构造可以通过
super
关键字来调用父类重载构造,如super(参数,...)
。 super
的父类构造调用,必须是子类构造方法的第一个语句,并且一个子类构造不能调用多次super
构造。- 子类构造必须调用父类构造方法,不写则默认有
super()
,写了则使用所写的指定的super
调用,super
只能有一个,还必须是第一个。
8. super关键字的三种用法
- 在子类的成员方法中,访问父类的成员变量
super.
; - 在子类的成员方法中,访问父类的成员方法
super.
; - 在子类的构造方法中,访问父类的构造方法
super(参数)
。
9.this关键字的三种用法
- 在本类的成员方法中,访问本类的成员变量
this.
; - 在本类的成员方法中,访问本类的另一个成员方法
this.
; - 在本类的构造方法中,访问本类的另一个构造方法
this(参数)
。this(参数)
调用也必须是构造方法的第一个语句且唯一。super
和this
两种构造调用,不能同时使用。
10. super与this关键字图解
11. Java继承的三个特点
-
Java语言是单继承的。一个类的直接父类只有一个。
class A{} class B extends A{} //正确 class C{} class D extend A, C{} //错误
-
Java语言可以多级继承。
class A{} class B extends A{} //正确 class C extends B{} //正确
-
一个子类的直接父类是唯一的,但一个父类可以有多个子类。
class A{} class B extends A{} //正确 class C extends A{} //正确
二、抽象
1. 概念
- 如果父类中的方法不确定如何进行{}方法体实现,那么这就应该是一个抽象方法。
2.抽象方法和抽象类的格式
- **抽象方法:**在返回值之前加上
abstract
关键字,然后去掉大括号,直接分号结束。 - **抽象类:**抽象方法所在的类,必须是抽象类,在
class
之前加上abstract
即可。 - 抽象类中可以定义普通成员方法。
public abstract class Animal{
public abstract void eat();
public void noemalMethod(){}
}
3. 抽象方法和抽象类的使用
- 不能直接创建抽象类对象。
- 必须用一个子类来继承抽象父类。
- 子类必须覆盖重写抽象父类当中所有的抽象方法。
- 覆盖重写(实现):子类去掉抽象方法的
abstract
关键字,然后补上方法体大括号。
- 覆盖重写(实现):子类去掉抽象方法的
- 创建子类对象进行使用。
//抽象类
public abstract class Animal {
public abstract void eat();
}
//子类
public class Cat extends Animal{
@Override
public void eat(){
System.out.println("猫吃鱼!");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat(); // 猫吃鱼!
}
}
4. 抽象方法和抽象类的注意事项
-
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
-
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用。
//抽象类 public abstract class Animal { public Animal(){ System.out.println("抽象父类的构造方法执行!"); } public abstract void eat(); } //子类 public class Cat extends Animal{ public Cat(){ System.out.println("子类的构造方法执行!"); } @Override public void eat() { System.out.println("猫吃鱼!"); } } public class Test { public static void main(String[] args) { Cat cat = new Cat(); // 抽象父类的构造方法执行! // 子类的构造方法执行! cat.eat(); // 猫吃鱼! } }
-
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
-
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
//抽象父类 public abstract class Animal { public abstract void eat(); public abstract void sleep(); } //抽象子类 public abstract class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头!"); } } //子类 public class DogGolden extends Dog { @Override public void sleep() { System.out.println("金毛要睡了!"); } } //子类 public class Dog2Ha extends Dog{ @Override public void sleep() { System.out.println("二哈要睡了!"); } } public class Test { public static void main(String[] args) { DogGolden dogGolden = new DogGolden(); dogGolden.eat(); // 狗吃骨头! dogGolden.sleep(); // 金毛要睡了! Dog2Ha dog2Ha = new Dog2Ha(); dog2Ha.eat(); // 狗吃骨头! dog2Ha.sleep(); // 二哈要睡了! } }
5. 发红包案例
-
题目描述
群主发普通红包。某群有多名成员,群主给成员发普通红包。红包规则为: 1.群主的一笔金额,从群主的余额中扣除,平均分为n等份,让成员领取。 2.成员领取红包后,保存到成员余额中。
-
分析
-
实现
//用户类
public class User {
private String name; // 用户名
private float money; // 用户持有的金额
//无参构造
public User() {
}
//全参构造
public User(String name, float money) {
this.name = name;
this.money = money;
}
//Getter & Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getMoney() {
return money;
}
public void setMoney(float money) {
this.money = money;
}
}
//群主类
import java.util.ArrayList;
public class GroupOwner extends User{
//无参构造
public GroupOwner() {
}
//全参构造
public GroupOwner(String name, float money){
this.setName(name);
this.setMoney(money);
}
//群主发红包方法
public ArrayList<Float> send(float money, int copies) {
this.setMoney(this.getMoney() - money);
float singleRedBag = money / copies;
ArrayList<Float> redBag = new ArrayList<>();
for (int i = 0; i < copies; i++) {
redBag.add(singleRedBag);
}
return redBag;
}
}
//群成员类
import java.util.ArrayList;
public class GroupMember extends User{
//无参构造
public GroupMember(){
}
//全参构造
public GroupMember(String name, float money){
this.setName(name);
this.setMoney(money);
}
//收红包
public void receive(ArrayList<Float> redBag){
if (redBag.size() != 0){
this.setMoney(this.getMoney() + redBag.remove(0));
}
}
}
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//创建群主
GroupOwner groupOwner = new GroupOwner("language", 5);
//发红包前群主的信息
System.out.println("发红包前群主的信息");
System.out.println("姓名:" + groupOwner.getName() + ",金额:" + groupOwner.getMoney());
//创建群成员
GroupMember groupMember_01 = new GroupMember("C", 1);
GroupMember groupMember_02 = new GroupMember("C++", 2);
GroupMember groupMember_03 = new GroupMember("Java", 3);
//收红包前群成员的信息
System.out.println("收红包前群成员的信息");
System.out.println("姓名:" + groupMember_01.getName() + ",金额:" + groupMember_01.getMoney());
System.out.println("姓名:" + groupMember_02.getName() + ",金额:" + groupMember_02.getMoney());
System.out.println("姓名:" + groupMember_03.getName() + ",金额:" + groupMember_03.getMoney());
System.out.println("=======================================");
//群主发红包
ArrayList<Float> redBag = groupOwner.send(3, 3);
//发红包后群主的信息
System.out.println("发红包后群主的信息");
System.out.println("姓名:" + groupOwner.getName() + ",金额:" + groupOwner.getMoney());
//群成员收红包
groupMember_01.receive(redBag);
groupMember_02.receive(redBag);
groupMember_03.receive(redBag);
//收红包后群成员的信息
System.out.println("收红包后群成员的信息");
System.out.println("姓名:" + groupMember_01.getName() + ",金额:" + groupMember_01.getMoney());
System.out.println("姓名:" + groupMember_02.getName() + ",金额:" + groupMember_02.getMoney());
System.out.println("姓名:" + groupMember_03.getName() + ",金额:" + groupMember_03.getMoney());
}
}