1 概述
2 继承的格式
- 创建父类
public class Employee {
public void method(){
System.out.println("方法执行了!");
}
}
- 创建子类
Teacher
public class Teacher extends Employee{
}
Assistant
public class Assistant extends Employee {
}
- 调用
/*
在继承的关系中,“子类就是一个父类”。也就是说,子类可以被当作父类看待
例如:父类是员工,子类是讲师,那么“讲师就是一个员工”。关系is-a.
定义父类的格式:(一个普通的类定义)
public 父类名称 {
//...
}
定义子类格式:
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();//方法执行了!
}
}
3 父类成员变量和子类成员变量重名问题
- 创建父类
public class Fu {
int numFu = 10;
int num = 100;
public void methodFu() {
//这里的num是本类当中的,不会向下找子类的
System.out.println(num);
}
}
- 创建子类
public class Zi extends Fu{
int numZi = 20;
int num = 200;
public void methodZi() {
//因为本类当中有num,所以这里的nums是本类当中的。若本类中没有才会往上找
System.out.println(num);
}
}
- 调用
/*
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
1. 直接:通过子类对象访问成员变量
规则:等号左边是谁就优先用谁,没有则向上找
2. 间接:通过成员方法访问成员变量
规则:方法属于谁(定义在谁中)则优先用谁,没有则向上找
*/
public class Demo01ExtendsField {
public static void main(String[] args) {
Fu fu = new Fu();//创建父类对象
System.out.println(fu.numFu);//10 只能使用父类的东西,没有任何子类类容
Zi zi = new Zi();
System.out.println(zi.numFu);//10
System.out.println(zi.numZi);//20
//1 直接访问重名的成员变量
//num是重名的成员变量
//等号左边是谁就优先用谁
System.out.println(zi.num);//200
System.out.println(fu.num);//100
//2 间接访问重名的成员变量
//这个方法是子类的,优先用子类的,没有再向上找
zi.methodZi();//200
//这个方法是在父类当中定义的,只不过是子类对象借来使用
zi.methodFu();//100
}
}
4 局部变量和成员变量重名问题
下面解决三种变量重名问题
- 父类
public class Fu {
//1. 父类成员变量
int num = 10;
}
- 子类
public class Zi extends Fu {
//2.子类成员变量
int num = 20;
public void method() {
//3. 子类方法的局部变量
int num = 30;
System.out.println(num);//30
System.out.println(this.num);//20 本类的成员变量
System.out.println(super.num);//10 父类的成员变量
}
}
- 调用
/*
局部变量: 直接写成员变量名
本类成员变量: this.成员变量名
父类的成员变量: super.成员变量名
*/
public class Demo01ExtendsField {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
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 Demo01ExtendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu();//父类方法执行!
zi.methodZi();//子类方法执行!
//zi是子类对象,所以优先用子类方法
zi.method();//子类重名方法执行!
}
}
6 重写
- Fu
public class Fu {
public Object method(){
return null;//所有的引用类型都可以用null表示
}
}
- Zi
public class Zi extends Fu {
@Override
public String method(){
return null;
}
}
- 调用
/*
重写(Override)
概念:在继承关系当中,方法的名称一样,参数列表也一样。(又叫覆盖、覆写)
特点:创建的是子类对象则优先用子类方法
而
重载(Overload):方法的名称一样,参数列表不一样。
方法覆盖重写的注意事项:
1. 必须保证父子类之间方法的名称相同,参数列表也相同。
@Override: 写在方法前面,用来检测是不是有效的正确覆盖重写,若程序不报错就证明是重写
2. 子类方法的返回值必须小于等于父类方法返回值范围。
提要:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类
3. 子类方法的权限必须大于等于父类方法的权限修饰符。
public > proctected > (default) > private
备注:(default)不是关键字default,而是什么都不写,留空。
*/
public class Demo01Override {
}
什么时候需要重写呢
对于投入使用的类若想更新,可以将该类继承过来,并对其中的某些方法进行重写。
- 父类
//本来的老款手机
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 Demo01Phone {
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 继承中构造方法的访问特点
特点1
- 父类
public class Fu {
public Fu(){
System.out.println("父类构造方法");
}
}
- 子类
public class Zi extends Fu {
public Zi(){
//默认这里有super();用于调用父类无参构造方法
System.out.println("子类构造方法");
}
}
- 调用
/*
继承关系中,父子类构造方法访问特点:
1. 子类构造方法当中有一个默认隐含的super调用,所以一定是先调用父类构造,后执行子类构造
2. 可以通过super关键字来子类构造调用父类重载构造
*/
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();//先打印父类再打印子类
// 父类构造方法
// 子类构造方法
}
}
特点2
- 父类
public class Fu {
public Fu(){
System.out.println("父类构造方法");
}
public Fu(int num){
System.out.println("父类有参构造方法");
}
}
- 子类
public class Zi extends Fu {
public Zi(){
//此时不再有super()无参的调用了
super(10);//只能调用一个super
System.out.println("子类构造方法");
}
}
- 调用
/*
继承关系中,父子类构造方法访问特点:
1. 子类构造方法当中有一个默认隐含的super调用,所以一定是先调用父类构造,后执行子类构造
2. 子类构造可以通过super关键字来调用父类重载构造
*/
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();//先打印父类再打印子类
// 父类有参构造方法
// 子类构造方法
}
}
特点3
- 父类
public class Fu {
public Fu(){
System.out.println("父类构造方法");
}
public Fu(int num){
System.out.println("父类有参构造方法");
}
}
- 子类
public class Zi extends Fu {
public Zi(){
//super()
super(10);//只能有一个super()
System.out.println("子类构造方法");
}
public void method(){
// super();错误写法!只有子类构造方法才能调用父类构造方法。
}
}
- 调用
/*
继承关系中,父子类构造方法访问特点:
1. 子类构造方法当中有一个默认隐含的super调用,所以一定是先调用父类构造,后执行子类构造
2. 子类构造可以通过super关键字来调用父类重载构造
3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造.
总结: 子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个
*/
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();//先打印父类再打印子类
// 父类有参构造方法
// 子类构造方法
}
}
8 super关键字小结
- 父类
public class Fu {
int num = 10;
public void method(){
System.out.println("父类方法");
}
}
- 子类
/*
super关键字的用法有三种:
1. 在子类的成员方法中,访问父类的成员变量
2. 在子类的成员方法中,访问父类的成员方法
3. 在子类的构造方法中,访问父类的构造方法
*/
public class Zi extends Fu {
int num = 20;
//3.
public Zi(){
super();
}
public void methodZi(){
System.out.println(num);
//1.
System.out.println(super.num);
}
public void method(){
//2.
super.method();//访问父类成员方法
System.out.println("子类方法");
}
}
9 this关键字小结
- 父类
public class Fu {
int num = 30;
}
- 子类
/*
super关键字用来访问父类类容,this关键字用来访问子类内容。用法也有三种:
1. 在本类成员方法中,访问本类成员变量
2. 在本类的成员方法中,访问本类的另一个成员方法
3. 在本类的构造方法中访问本类的另一个构造方法
在第三种方法中要注意:
this(...)调用也必须是构造方法的第一个语句,唯一一个
super()和this()两种构造调用不能同时使用,它们必须都是唯一的。
*/
public class Zi extends Fu {
int num = 20;//成员变量
//3
public Zi(){
//super不会再赠送
this(123);//本类的无参构造调用本类的有参构造
//this(123, 456);错误写法,这里必须是第一个语句
}
public Zi(int n){
this(123, 456);
}
public Zi(int n, int m) {
//this();不可以这样,否则就是循环调用了
}
//1.
public void showNum(){
int num = 10;//局部变量
System.out.println(num);//局部变量
System.out.println(this.num);//本类中的成员变量
System.out.println(super.num);//父类中的成员变量
}
//2.
public void methodA(){
System.out.println("AAA");
}
public void methodB(){
//methodA();或者
this.methodA();//强调一下该方法来源于本类而不是父类
System.out.println("BBB");
}
}
10 super与this关键字图解
- 父类
public class Fu {
int num = 10;
public void method(){
System.out.println("父类方法");
}
}
- 子类
public class Zi extends Fu {
int num = 20;
@Override
public void method(){
super.method();
System.out.println("子类方法");
}
public void show(){
int num = 30;
System.out.println(num);//30
System.out.println(this.num);//20
System.out.println(super.num);//10
}
}
- 调用
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
//30
//20
//10
zi.method();
//父类方法
//子类方法
}
}
- [[super_class]]就是在Zi.class里面做一个标记,告诉其父亲是谁
- 栈里面子内内容的方法和父内内容的方法存放的都是地址值
11 继承的三个特点
注意:爷爷类也称为父类,只不过不是直接父类