通过extends关键字,可以声明一个子类继承另一个父类,定义格式如下:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
例如:
// 定义一个父类:员工
public class Employee {
public void method() {
System.out.println("方法执行");
}
}
// 定义了一个员工的子类:讲师
public class Teacher extends Employee{
}
// 定义了员工的另一个子类:助教
public class Assistant extends Employee {
}
/*
* 在继承的关系中,子类就是一个父类,也就是说,子类可以被当作父类看待。
* 例如:父类是员工,子类是讲师,那么讲师就是一个员工
*
* 定义父类的格式:(一个普通的类的定义)
* public class 父类名称 {
* //...
* }
*
* 定义子类的格式:
* public class 子类名称 extends 父类名称 {
* //...
* }
*
*
* */
public class Demo01Extends {
public static void main(String[] args) {
// 创建了一个子类对象
Teacher teacher = new Teacher();
// teacher中虽然什么都没写,但是会继承来自父类的method方法
teacher.method();
// 创建另一个子类助教的对象
Assistant assistant = new Assistant();
//
assistant.method();
}
}
下面我们来看一下,当类之间产生了关系后,其中各类中的成员变量产生了什么关系?
public class Fu {
int num = 10;
}
/*
* 局部变量:直接写成员变量名
* 本类的成员变量:this.成员变量名
* 父类的成员变量:super.成员变量名
*
*
*/
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, 父类的成员变量
}
}
public class Demo01ExtendsField {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
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() {
// 因为本类当中有num,所以这里用的是本类的num
System.out.println(num);
}
}
/*
* 在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
* 直接:
* 直接通过子类对象访问成员变量
* 等号左边是谁,就优先用谁,没有则向上找。
* 间接:
* 通过成员方法访问成员变量
* 该方法属于谁,就优先用谁,没有则向上找
*
* */
public class Demo01ExtendsField {
public static void main(String[] args) {
Fu fu = new Fu(); // 创建父类对象
System.out.println(fu.numFu); // 只能使用父类的东西,没有任何子类内容
Zi zi = new Zi();
System.out.println(zi.numFu);
System.out.println(zi.numZi);
System.out.println("=====================");
// 等号左边是谁,就优先用谁
System.out.println(zi.num); // 优先使用子类:200
// System.out.println(zi.aaa); // 到处都没有就编译报错。
// 这个方法是子类的,优先使用子类的,没有再向上找
zi.methodZi();
// 这个方法是在父类当中定义的
zi.methodFu();
}
}
super.父类中成员变量名;
下面我们来看一下,当类产生了关系之后,各类的成员方法又产生了哪些影响?
/*
* 在父子类的继承关系中,创建子类对象,访问成员方法的规则:
* 创建的对象是谁,就优先使用谁,如果没有就向上找。
*
* 注意事项:
* 无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。
*
* 重写(Override)
* 概念:在继承关系当中,方法的名称一样,参数列表也一样。
*
* 重写(Override):方法的名称一样,参数列表也一样,又称为覆盖,覆写。
* 重载(Overload):方法的名称一样,参数列表不一样。
*
* */
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 DemoExtendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodZi();
zi.methodFu();
// 创建的是new了子类对象,所以优先使用子类方法。
zi.method();
}
}
代码如下:
/*
* 覆盖重写的注意事项:
* 1、必须保证父子类之间方法的名称相同,参数列表也相同。
* @Override:写在方法前面,用来检测是不是有效的正确覆盖重写。这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。
* 2、子类方法的返回值必须小于等于父类方法的返回值范围。
* 3、子类方法的权限必须大于等于父类方法的权限修饰符
* 提示:public > protected > (default) > private
* 备注:不是关键字default,而是什么都不写,留空
*
* */
public class Fu {
public Object method() {
return null;
}
}
public class Zi extends Fu {
@Override
public String method() {
return null;
}
}
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() {
super.show(); // 把父类的show方法拿过来重复利用
// 自己子类再来添加更多内容
System.out.println("显示姓名");
System.out.println("显示头像");
}
}
public class Demo01Phone {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.send();
phone.show();
System.out.println("===================");
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.send();
newPhone.show();
}
}
下面我们来看一下,当类之间产生了关系之后,对于各个类中的构造方法产生了哪些影响?
首先我们先回忆一下构造方法定义的格式和作用:
/*
* 继承关系中,父子类构造方法的访问特点:
* 1、子类构造方法当中有一个默认隐含的"super()" 调用,所以一定是先调用的父类的构造方法,后调用的子类构造方法
* 2、可以通过super关键字来子类构造调用父类重载构造。
* 3、super的父类构造调用,必须是子类构造方法的第一个语句,不能一个子类构造调用多次super构造
* 总结:
* 子类必须调用父类构造方法,不写则赠送super(),写了则用写的指定super调用,super只能有一个还必须是第一个
*
* */
public class Fu {
public Fu() {
System.out.println("父类无参构造");
}
public Fu(int num) {
System.out.println("父类构造方法!!!");
}
}
ublic class Zi extends Fu {
public Zi() {
super(20); // 在调用父类重载的构造方法
System.out.println("子类构造方法!!!");
}
// public void method() {
// super(); // 错误写法,只有子类构造方法,才能调用父类构造方法。
// }
}
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
父类空间优先于子类对象产生
super和this的含义:
super和this的用法
this.成员变量 --本类的
super.成员变量 --父类的
this.成员方法名() --本类的
super.成员方法名() --父类的
/*
* super关键字用来访问父类内容,而this关键字
* 1、在本类的成员方法中,访问本类的成员变量。
* 2、在本类的成员方法中,访问本类的另一个成员方法。
* 3、在本类的构造方法中,访问本类的另一个构造方法。
* 注意:在第三种用法中,this(...)调用也必须是构造方法的第一个语句,唯一一个
* super和this两种构造调用,不能同时使用。
* */
public class Fu {
int num = 40;
}
public class Zi extends Fu {
int num = 20;
public Zi() {
this(123); // 本类的无参构造,调用本类的有参构造。
}
public Zi(int n) {
}
public Zi(int n, int m) {
}
public void showNum() {
int num = 10;
System.out.println(num); // 局部变量
System.out.println(this.num); // 本类中的成员变量
System.out.println(super.num); // 父类中的成员变量
}
public void methodA() {
System.out.println("AAA");
}
public void methodB() {
this.methodA();
System.out.println("BBB");
}
}
this(...) --子类的构造方法
super(...) --父类的构造方法
注意:子类的每个构造方法中均有默认的super(),调用父类的空参构造,手动调用父类构造会覆盖默认的super()。super()和this()都必须是在构造方法的第一行,所以不能同时出现。
// 一个类只能又一个父类,不可以又多个父类
class C extends A{} // OK
class C extends A, B{} // error
class A{}
class B extends A{}
class C extends B{}
由来:父类中的方法,被他们的子类们重写,子类各自的实现都不尽相同,那么父类的方法声明和方法主题,只有声明还有意义,而方法主题则没有存在的意义了,我们把没有方法主题的方法成为抽象方法,Java语法规定,包含抽象方法的类就是抽象类。
定义:
抽象方法:使用abstract关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
修饰符 abstract 返回值类型 方法名 (参数列表);
public abstract void run();
抽象类:如果一个类包含抽象方法,那么该类必须是抽象类。
abstract class 类名 {
...
}
pulbic abstract class Animal {
public abstract void run();
}
抽象的使用:
继承抽象方法的子类必须重写父类所有的抽象方法。否则,该子类也必须为抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
代码举例:
/*
* 抽象方法:就是加上abstract关键字分,然后去掉大括号,直接分号结束。
* 抽象类:抽象方法所在的类,必须是抽象类才行,在class之前写上abstract即可。
*
* 如何使用抽象类和抽象方法:
* 1、不能直接创建抽象类对象。
* 2、必须用一个子类来继承抽象父类。
* 3、子类必须覆盖重写抽象父类当中所有的抽象方法。
* 覆盖重写:去掉抽象方法的abstract关键字,然后补上大括号。
* 4、创建子类对象进行使用。
* */
public abstract class Animal {
// 这是一个抽象方法,代表吃东西,但是具体吃什么,不确定
public abstract void eat();
// 普通的成员方法
public void normalMethod() {
}
}
public class Cat extends Animal {
@Override
public void eat() {
}
}
public class DemoMain {
public static void main(String[] args) {
// Animal animal = new Animal(); // 错误,不能直接创建抽象类。
Cat cat = new Cat();
}
}
关于抽象类的使用,以下为语法上要注意的细节。
群主发普通红包,某群有多名成员,群主给成员发普通红包,普通红包的规则:
定义用户类:
public class User {
private String name;
private int money;
public User() {
}
public User(String name, int money) {
this.name = name;
this.money = money;
}
public void show() {
System.out.println("我叫:" + name + ",我有" + money + "元。");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
定义群主类:
import java.util.ArrayList;
public class Manager extends User{
public Manager() {
}
public Manager(String name, int money) {
super(name, money);
}
public ArrayList send(int totalMoney, int count) {
// 首先需要一个集合,用来存储若干个红包的金额
ArrayList redList = new ArrayList<>();
// 首先看一下群主当前的余额
int leftMoney = super.getMoney(); // 群主当前余额
if (totalMoney > leftMoney) {
System.out.println("余额不足!");
return redList;
}
// 扣钱,其实就是重新设置余额
super.setMoney(leftMoney - totalMoney);
// 发红包平均拆分
int avg = totalMoney / count;
int mod = totalMoney % count; // 余数,也就是剩下的零头
// 除不开的零头,包在最后一个红包当中
// 下面把红包放在一个集合中
for (int i = 0; i < count - 1; i++) {
redList.add(avg);
}
// 最后一个红包
int last = avg + mod;
redList.add(last);
return redList;
}
}
定义成员类:
import java.util.ArrayList;
import java.util.Random;
public class Member extends User {
public Member() {
}
public Member(String name, int money) {
super(name, money);
}
public void receive(ArrayList list) {
// 从多个红包当中随便抽取一个,给我自己
// 随便获取一个集合当中的索引编号
int index = new Random().nextInt(list.size());
// 根据索引,从集合当中删除,并且得到被删除的红包,给我自己
int delta = list.remove(index);
// 当前成员自己本来有多少钱
int money = super.getMoney();
super.setMoney(money + delta);
}
}