1. 继承
1.1 概述
由来:
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。
继承:就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
好处:
- 提高代码的复用性。
- 类与类之间产生了关系,是多态的前提。
1.2 继承(子类与父类)的格式
在继承的关系中,“子类就是一个父类”。也就是说子类可以被当做父类看待。
例如父类是员工,子类是讲师,那么“讲师就是一个员工“,关系:is-a。
定义父类的格式:(一个普通的类定义)【就是一个普通的类】
public class 父类名称{
//…
}
定义子类的格式:【有区别】
public class 子类名称 extends 父类名称{
//…
}
例子:
package cn.itcast.day09.demo01;
//定义一个父类:员工
public class Employee {
public void method(){
System.out.println("方法执行!");
}
}
package cn.itcast.day09.demo01;
//定义了一个员工的子类:讲师
public class Teacher extends Employee{
//空白的有东西吗?有,继承父类的
}
package cn.itcast.day09.demo01;
/*
定义了员工的另一个子类:助教
*/
public class Assistant extends Employee{
}
public class Demo01Extends {//Extends 继承 扩展
public static void main(String[] args) {
//创建了一个子类对象
Teacher tea = new Teacher();
//Teacher类中虽然什么都没有,但继承了来自父类的method().
tea.method();
//创建另一个子类助教的对象
Assistant ass = new Assistant();
ass.method();
}
}
1.3 继承后的特定——成员变量
成员变量不重名:
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。
成员变量重名:
如果子类父类中出现重名的成员变量,这时的访问是有影响的。因为歧义出现了,一般优先使用本类当中的,没有再向上找。
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
1. 直接通过子类对象访问成员变量:【直接访问】
用“." 等号左边是谁,就优先用谁【子类】,没有则向上找。
2. 间接通过成员方法访问成员变量:【间接访问】
该方法属于谁,就优先用谁,没有则向上找。属于父类,就会优先用父类的参数。
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,所以这里用的是本类的num【没有才往上找】
System.out.println(num);
}
}
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);//10
System.out.println(zi.numZi);//20
System.out.println("----------------------");
//等号左边是谁,就优先用谁 Zi zi = new Zi();
System.out.println(zi.num);//优先用子类:200
// System.out.println(zi.abc);//到处都没有,报错
System.out.println("----------------------");
//这个方法是子类的,优先用子类的,没有再网上找。
zi.methodZi();//200
//这个方法是父类的,num值就优先用本类中的
zi.methodFu();//100
}
}
3个变量重名:
局部变量: 直接写成员变量名
本类的成员变量: this.成员变量名 【this 本类】
父类的成员变量: super.成员变量名 【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 父类的成员变量
}
}
public class Demo01ExtendsField {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
成员方法重名:
成员方法重名——重写
重写(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 Demo01ExtendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu();
zi.methodZi();
zi.method();
}
}
方法覆盖重写的注意事项:
-
必须保证父子类之间方法的名称相同,参数列表也相同。
@Override,写在方法面前,用来检测是不是有效的正确覆盖重写。@代表注解 annotation
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写,仅仅是一种安全检测手段。 -
子类方法的返回值必须【小于等于】父类方法的返回值范围。一般都是相等关系。
小扩展提示:Object类是所有类的公共最高父类(祖宗类),位于继承关系的顶端,金字塔的塔尖。
所以,java.long.String就是Object的子类。
3.修饰符就是权限:public private
子类方法的权限修饰符必须【大于等于】父类方法的权限修饰符。一般都是相等关系。
小扩展提示: public > protected > (default) > private
备注:(default)不是关键字default,而是什么都不写,留空。
对于方法来说,去掉修饰符:
一般方法(静态方法)还剩下:static 返回值类型 方法名(参数列表)
成员方法还剩下:返回值类型 方法名(参数列表)
构造方法还剩下:方法名(参数列表)//构造方法用于实体化对象而存在,就是一块空间,返回值没用,静态也没用
内存中先存在静态,后存在非静态。
注意事项:
- 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
例子:
public class Fu {
public Object method(){//【提问】方法不能用private吗?
return null;
}
}
public class Zi extends Fu{
@Override //覆盖重写:安全检查的作用
public String method(){
return null;
}
}
public class Demo01Override {
public int num; //成员变量前用public
protected int num1;//成员变量前用protected
int num2;//留空默认就是(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 {
//打电话方法:call();继承
//发端性方法:send();继承
//关键是显示方法的更新:show()
@Override //在子类中直接打出方法名回车,会有提示
public void show() {
// System.out.println("显示号码!");//本来的功能
// (可能代表100条代码)用【super关键字】调用父类方法
//之所以用继承,就是为了解决重复代码的问题
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.4 父子类构造方法的访问特点
继承关系中,父子类构造方法的访问特点:
1.子类构造方法当中,有一个默认隐含的“super()”调用(无参构造),所以一定是先调用的父类构造,后执行的是子类构造。
2.可以通过super关键字在子类构造中调用父类重载构造。
3.super的父类构造调用,只能是子类构造方法中的【第一条】语句。不能一个子类构造调用多次super构造。【重要】
总结:
子类必须调用父类构造方法,不写则赠送super();写了则用指定的super调用。super只能有一个,还不必须是第一个。
public class Fu {
public Fu(){
System.out.println("父类无参构造!");
}
public Fu(int a){
System.out.println("父类有参构造!");
}
}
public class Zi extends Fu{
public Zi(){
// super();//编译器赠送的调用父类无参构造方法
//如果父类构造方法有参数,那么是否还赠送呢?不再,那么怎么办?
super(10);//在调用父类重载的构造方法。谁能对得上,就用谁。
//使用了一个super之后,编译器将不再赠送super
//因为super调用语句在构造方法中只能调用一次。所以只能选择其中一个调用。
System.out.println("子类构造方法!");
}
public void method(){
// super(); //错误写法,只有子类构造方法,才能调用父类构造方法。
}
}
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();//构造方法也会继承
//先出的是父类的构造方法
// Fu fu = new Fu();
}
}
super关键字的典型用法:
super关键字的典型用法有三种:
- 在子类的成员方法中,访问父类的成员变量。
- 在子类的成员方法中,访问父类的成员方法。
- 在子类的构造方法中,访问父类的构造方法。
例子:
public class Fu {
int num = 10;
public Fu(){
}
public void method(){
System.out.println("父类的成员方法method!");
}
}
public class Zi extends Fu{
int num = 20;
public Zi(){
super();//这条不写也默认有(编译器赠送)
}
public void methodZi(){
System.out.println("本类中的num值:" + num);//本类中的num
System.out.println("父类中的num值:" + super.num);//父类中的num
}
public void method(){
super.method();//访问父类中的method
System.out.println("子类中的成员方法method!");
}
}
this的三种用法:
super和this的含义
super :代表父类的存储空间标识(可以理解为父亲的用)。
this :代表当前对象的引用(谁调用就代表谁)。
super关键字用来访问父类内容,而this关键字用来访问本类内容。
用法也有三种:
- 在本类的成员方法中,访问本类的成员变量。
- 在本类的成员方法中,访问本类的另一个成员方法。
- 在本类的构造方法中,访问本类另一个构造方法。
在第三种用法中要注意:
a. this(…)也必须是构造方法中的【第一条】语句。且只能有一个。此时默认的super()不再赠送。
b. (谁是第一个?)super和this不能同时使用。
c. 没使用this()的构造方法中默认存在调用父类无参构造的super()。
例子:
public class Fu {
int num = 30;
}
public class Zi extends Fu{
public Zi(){
// this()//错误写法,自己调用自己了
this(25);//调用了有参构造
//构造方法的重载调用:本类中的无参构造,调用本类中的有参构造。
}
public Zi(int num){
this.num = num; //这种设计图(类)中成员变量
}
int num = 20;//成员变量(全局变量)
public void showNumber(){
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(){
methodA();//可以这样访问
this.methodA();//也可以这样访问,强调是本类成员方法的作用,
System.out.println("BBB~");
}
}
super与this关键字内存图解:
public class Fu {
int num = 10;
public void method(){
System.out.println("父类方法!");
}
}
public class Zi extends Fu{
int num = 20;
@Override//都知道只是覆盖重写;能写Override尽量都写。
public void method() {
super.method();//调用父类方法
System.out.println("子类方法!");
}
public void show(){
int num = 30;
System.out.println("局部变量:" + num); //局部变量
System.out.println("本类成员变量:" + this.num);//本类成员变量
System.out.println("父类成员变量:" + super.num);//父类成员变量
}
}
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
System.out.println("-----------------");
zi.method();
}
}
继承的特点:
java语言中三个最为重要的继承相关的特点:
——此文档为学习笔记!