继承
定义
- 提高代码的复用性。
- 类与类之间产生了关系,是多态的前提。
继承的格式:
代码举例
定义父类
package demo01; // 定义一个父类:员工 public class Employee { public void method() { System.out.println("方法执行!"); } }
定义子类
package demo01; // 定义了一个员工的子类:讲师 public class Teacher extends Employee { }
定义测试类
package demo01; /* 在继承的关系中,“子类就是一个父类”。也就是说,子类可以被当做父类看待。 例如父类是员工,子类是讲师,那么“讲师就是一个员工”。关系:is-a。 定义父类的格式:(一个普通的类定义) public class 父类名称 { // ... } 定义子类的格式: public class 子类名称 extends 父类名称 { // ... } */ public class Demo01Extends { public static void main(String[] args) { // 创建了一个子类对象 Teacher teacher = new Teacher(); // Teacher类当中虽然什么都没写,但是会继承来自父类的method方法。 teacher.method(); } }
执行结果
继承中成员变量的访问特点
成员变量不重名
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。
- 子类对象可以通过不同的成员变量名访问对应的成员变量
- 父类对象只能使用父类的东西,不能访问任何子类内容
成员变量重名
如果子类父类中出现重名的成员变量,这时的访问是有影响的。
父类对象只能使用父类的东西,不能访问任何子类内容
创建子类对象时,访问有两种方式:
直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找。
间接通过成员方法访问成员变量:该方法属于谁,就优先用谁,没有则向上找。
代码举例
定义父类
package demo02; public class Fu { int numFu = 10; int num = 100; public void methodFu() { // 使用的是本类当中的,不会向下找子类的 System.out.println(num); } }
定义子类
package demo02; public class Zi extends Fu { int numZi = 20; int num = 200; public void methodZi() { // 因为本类当中有num,所以这里用的是本类的num System.out.println(num); } }
定义测试类
package demo02; /* 在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式: 直接通过子类对象访问成员变量: 等号左边是谁,就优先用谁,没有则向上找。 间接通过成员方法访问成员变量: 该方法属于谁,就优先用谁,没有则向上找。 */ public class Demo01ExtendsField { public static void main(String[] args) { Fu fu = new Fu(); // 创建父类对象 System.out.println(fu.numFu); // 只能使用父类的东西,没有任何子类内容 System.out.println("==========="); Zi zi = new Zi(); System.out.println(zi.numFu); System.out.println(zi.numZi); System.out.println("==========="); // 等号左边是谁,就优先用谁 System.out.println(zi.num); // System.out.println(zi.abc); // 到处都没有,编译报错! System.out.println("==========="); // 这个方法是子类的,优先用子类的,没有再向上找 zi.methodZi(); // 这个方法是在父类当中定义的, zi.methodFu(); } }
执行结果
区分 子类方法中的局部变量,子类成员变量,父类成员变量重名问题
- 访问局部变量: 直接写成员变量名
- 访问本类的成员变量: this.成员变量名
- 访问父类的成员变量: super.成员变量名
代码举例
package demo03; public class Zi extends Fu { int num = 20; public void method() { int num = 10; System.out.println(num); // 30,局部变量 System.out.println(this.num); // 20,本类的成员变量 System.out.println(super.num); // 10,父类的成员变量 } }
继承中成员变量的访问特点
成员方法不重名
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。子类对象调用方法时,会先在子类中查找有没有对 应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。父类对象只能使用父类的东西,不能访问任何子类内容。
成员方法重名——重写(Override)
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。 方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效 果,也称为重写或者复写。声明不变,重新实现。
重写的应用
子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从 而进行扩展增强。
创建子类对象时,访问规则:
在父子类的继承关系当中,创建子类对象,访问成员方法的规则:创建的对象是谁,就优先用谁,如果没有则向上找。
注意事项:
无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。
-
- 重写(Override):在继承关系当中(父类和子类),方法的名称一样,参数列表也一样。
- 重载(Overload):在同一个类中 。方法的名称一样,参数列表【不一样】。
代码举例
定义父类
package demo04; public class Fu { public void methodFu() { System.out.println("父类方法执行!"); } public void method() { System.out.println("父类重名方法执行!"); } }
定义子类
package demo04; public class Zi extends Fu { public void methodZi() { System.out.println("子类方法执行!"); } public void method() { System.out.println("子类重名方法执行!"); } }
定义测试类
package demo04; /* 在父子类的继承关系当中,创建子类对象,访问成员方法的规则: 创建的对象是谁,就优先用谁,如果没有则向上找。 注意事项: 无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。 重写(Override) 概念:在继承关系当中,方法的名称一样,参数列表也一样。 重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。 重载(Overload):方法的名称一样,参数列表【不一样】。 方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。 */ public class Demo01ExtendsMethod { public static void main(String[] args) { Zi zi = new Zi(); zi.methodFu(); zi.methodZi(); // 创建的是new了子类对象,所以优先用子类方法 zi.method(); } }
执行结果
方法覆盖重写的注意事项:
1. 必须保证父子类之间方法的名称相同,参数列表也相同。@Override:写在方法前面,用来检测是不是有效的正确覆盖重写。这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。
2. 子类方法的返回值必须【小于等于】父类方法的返回值范围。小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类。
3. 子类方法的权限必须【大于等于】父类方法的权限修饰符。小扩展提示:public > protected > (default) > private。备注:(default)不是关键字default,而是什么都不写,留空。
继承中构造方法的访问特点
- 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
- 子类构造方法可以通过super关键字来调用父类重载构造。super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。
- 总结:子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。
代码举例
定义父类
package demo07; public class Fu { public Fu() { System.out.println("父类无参构造"); } public Fu(int num) { System.out.println("父类有参构造!"); } }
定义子类
package demo07; public class Zi extends Fu { public Zi() { super(); // 在调用父类无参构造方法 // super(20); // 在调用父类重载的构造方法 System.out.println("子类构造方法!"); } public void method() { // super(); // 错误写法!只有子类构造方法,才能调用父类构造方法。 } }
定义测试类
package demo07; /* 继承关系中,父子类构造方法的访问特点: 1. 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。 2. 子类构造可以通过super关键字来调用父类重载构造。 3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。 总结: 子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。 */ public class Demo01Constructor { public static void main(String[] args) { Zi zi = new Zi(); } }
执行结果
super关键字的三种用法
- 在子类的成员方法中,访问父类的成员变量。格式:super.成员变量名
- 在子类的成员方法中,访问父类的成员方法。格式:super.父类成员方法名
- 在子类的构造方法中,访问父类的构造方法。格式:super.父类构造方法名
this关键字用来访问本类内容。用法也有三种:
- 在本类的成员方法中,访问本类的成员变量。格式:this.成员变量名
- 在本类的成员方法中,访问本类的另一个成员方法。格式:this.成员方法名
- 在本类的构造方法中,访问本类的另一个构造方法。格式:this.构造方法名
在第三种用法当中要注意:
- A. this(...)调用也必须是构造方法的第一个语句,唯一一个。
- B. super和this两种构造调用,不能同时使用。
继承的特点
- Java只支持单继承,不支持多继承
- Java支持多层继承(继承体系)
- 子类和父类是一种相对的概念。顶层父类是Object类。所有的类默认继承Object,作为父类。