Java的继承

继承

Object类

  • Object类概述
    Object是类层次结构的根(超类/基类),每个类都可以将Object作
    为超类,所有的类都可以直接或间接继承该类。换句话说,该类中的
    属性和行为,其他类都可以使用。

  • 重写toString方法
    方式一:Alt+Enter选择toString这一行
    方式二:在代码的空白区域点右键==>Generate==>选择toString这一行

  • toString方法的作用
    以良好的格式,方便输出对象中的属性值

  • equals方法

  • hashCode方法

继承的概述

面向对象的核心特征–继承,是面向对象的学习重点
继承是代码复用的重要方式之一,是类与类之间的一种关系,所以继承肯定是子父类关系,所有的类都是继承Object,要么直接继承,要么间接继承,继承Object不用写,是默认的。Object称为超类(基类),

从类与类之间的设计关系来看,子类必须属于父类的一种,才会继承父类会抽取出一些的共性的内容,子类可以在父类的基础上扩展新的属性和行为子类拥有父类的所有属性和行为(除了私有的),无需重新定义,并且可以直接使用非私有的父类成员属性和行为

继承的格式

继承关键字: extends
格式: 修饰符 class 子类名 extends 父类{ … }
注意:子类在前,父类在后 extends是Java关键字 只能单继承

代码演示
public class Person {
// 非私有的属性
String name;
int age;
// 行为
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
public class Coder extends Person{
// 子类有自己的属性
String salary;
// 子类有自己的行为
public void work(){
System.out.println("敲代码");
}
}
public static void main(String[] args) {
// 创建子类对象
Coder c = new Coder();
// 子类对象可以获取到父类的属性和行为(非私有的)
c.name = "张三";
c.age = 18;
c.eat();
c.sleep();
// 子类对象可以用自己的属性和行为
c.salary = "1000";
c.work();
// 创建父类对象
Person p = new Person();
//p.salary; 父类对象不能用子类的属性和行为
//p.work();
}
父类私有的属性
public class Employee {
// 私有的属性
private String name;
private int age;
public Employee() {
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 自己定义普通方法
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
public class Teacher extends Employee{
// 自己的行为
public void teach(){
System.out.println("教程序员敲代码");
}
}
public static void main(String[] args) {
// 创建Teacher对象
Teacher t = new Teacher();
// 给name和age赋值 通过父类的setter
t.setName("张三");
t.setAge(18);
// 通过父类的getter
String name = t.getName();
int age = t.getAge();
System.out.println(name+":"+age);
t.eat();
t.sleep();
t.teach();
}

继承的特点

Java是单继承,就是说只有一个父类
Java是支持多层继承
父类定义了继承家族中共性的内容,子类定义个性的内容结合多态,能使用父类尽量使用父类,提高程序的扩展性,这是多态的体现形式

多层继承代码演示:

public class GrandFather extends Object{}
public class Father extends GrandFather{}
public class Child extends Father{}

继承的好处

① 继承的出现,减少了代码的冗余(重复),提供了代码的复用性。
② 继承的出现,有利于功能的扩展。
③ 继承的出现,让类和类之间产生了关系,提供了多态的前提。
注意:不要为了获取其它类中的某个功能而去继承
总结:
① 当父类的成员变量私有化的时候,在子类中是无法直接访问的,所以是否重名没有任何影响;但是,如果想访问父类的私有成员变量,只能通过父类提供的 setter 和 getter 访问。
② 当父类的成员变量非私有化的时候,在子类中是可以直接访问的,所以如果有重名,就需要加上 super.父类成员变量名 来进行区分。注意:在实际开发中,虽然我们可以通过 super 关键字来实现
区分父子类重名成员变量,但是不建议这么干。

重写

重写的概述

  • 回顾下重载:在一个类中方法名相同,参数个数或参数类型不同,与返回值无关

  • 当子类继承父类后,拥有了父类的成员并可以直接调用父类非私有成员,如果子类觉得父类的方法不够强大或者不能满足自己的功能,子类可以按照自身逻辑需要重写定义继承过来的父类方法,这个重新定义的方法就是子类的重写。

  • 在子类中可以根据需要对从父类中继承而来的方法进行改造,也称为方法的 重置 、 覆盖 。在程序执行的时候,子类的方法将覆盖父类的方法。

格式

//定义一个与父类声明完全相同的方法(包括方法名,参数类型,参数个
//数都要相同),方法体不同(大括号里面不同),子类重写方法上加上
@Override

要求

① 子类重写的方法 必须 和父类被重写的方法具有相同的 方法名称\、 参数列表 。
② 子类重写的方法的返回值类型 不能大于 父类被重写的方法的返回值类型(这对于返回值类型为引用数据类型来说的,如果返回值类型是基本数据类型和 void 类型必须相同,换言之,只有继承,才会有父类和子类)。
③ 子类重写的方法使用的访问权限 不能小于 父类被重写的方法的访问权限( 注意:子类不能重写父类中声明为privat权限的方法或final修饰的方法 )。
④ 子类方法抛出的异常不能大于父类被重写方法的异常。

注意:子类重写父类方法后,子类调用该方法时,只能是调用的重写后的方法,不再调用父类的原先的方法。子类和父类中同名同参
数的方法必须同时声明为非 static的(重写),或者同时声明为static 的(不是重写,因为 static 方法是属于类的,子类无法覆盖父类的方法。

public class Anmail {
// 吃饭
public void eat(){
System.out.println("吃饭");
}
// 睡觉
public void sleep(){
System.out.println("睡觉");
}
}
public class Dog extends Anmail{
@Override
public void eat(){
System.out.println("汪汪叫着吃着骨头");
}
}
public static void main(String[] args) {
Dog d = new Dog();
// 子类重写父类方法后,子类调用该方法时,只能是调用
的重写后的方法
d.eat();
// 没有重写 还是调用的父类的方法
d.sleep();
}

重写的注意事项

肯定是继承关系
方法名相同
参数列表相同(参数类型,参数个数都要相同)
访问权限要么相同要么子类的权限比父类大 (权限 public >protected > 默认 > private
返回值一般必须相同(除特殊情况)
子类和父类中同名同参数的方法必须同时声明为非 static的(重写),或者同时声明为 static 的(不是重写,因为 static 方法是属于类的,子类无法覆盖父类的方法。

public class Anmail {
// 吃饭
public void eat(){
System.out.println("吃饭");
}
// 睡觉
public void sleep(){
System.out.println("睡觉");
}
protected void play(){
System.out.println("玩耍");
}
}
public class Dog extends Anmail{
@Override
public void eat(){
System.out.println("汪汪叫着吃着骨头");
}
// 子类的修饰符权限比父类大
@Override
public void play(){
}
}

继承后子父类构造方法调用先后顺序

子父类构造方法调用

在每次创建子类对象时,会先初始化父类的内容,再初始化子类的内容。目的在于子类对象中包含了其对应的父类存储空间,便可以包含父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。

代码体现在子类的构造方法调用时,一定会先调用父类的构造方法。

代码演示

public class Fu {
private String name;
private int age;
public Fu() {
    System.out.println("我是Fu的无参构造方法");
}
public Fu(String name, int age) {
this.name = name;
this.age = age;
System.out.println("我是Fu的全参构造方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Zhi extends Fu{
private String salary;
public Zhi() {
// 默认一个父类的无参构造方法 默认可以不写 如果写,
必须写在方法的第一行
super();
System.out.println("我是Zhi的无参构造方法");
}
public Zhi(String salary) {
    this.salary = salary;
System.out.println("我是Zhi自有属性的构造方法");
}
public Zhi(String name, int age, String salary)
{
// 默认可以不写 自动调用父类对应构造方法
super(name, age);
this.salary = salary;
System.out.println("我是Zhi的全参构造方法");
}
// 建议不要写了 因为父类中已经有了 super(name,age)
直接从父类获取
/*public Zi(String name, int age) {
super(name, age);
}*/
}
public static void main(String[] args) {
// 子类的构造方法调用时,一定会先调用父类的构造方法
Zhi z = new Zhi();
//先输出:我是父类的无参构造方法 父类
//后输出:我是子类的无参构造方法 子类
System.out.println("====================");
// 创建子类全参构造方法
Zhi b = new Zhi("张三",18,"1000");
//先输出:我是Fu的全参构造方法 父类
//后输出:我是Zhi的全参构造方法 子类
}

子类对象内存图

super关键字

概述

  • 在 Java 类中使用super来调用父类中的指定操作:

  • super 可用于访问父类中定义的属性。

  • super 可用于调用父类中定义的成员方法。

  • super 可用于在子类构造器中调用父类的构造器。
    注意:

  • 当子类和父类出现同名成员的时候,可以用 super 表明调用的是父类的成员。

  • super 的追溯不仅限于直接父类 。

  • super 和 this 的用法很像,this 代表的是本类对象的引用,

  • super 代表的是父类的内存空间的标识。

super与this

调用变量

  • super.成员变量 访问父类的成员变量 this.成员变量 访问本类对象的成员变量

调用方法

  • super.成员方法 访问父类的成员方法

  • this.成员方法 访问本类对象的成员方法

在子类中同时使用this和super:

  • 如果访问的是子类自身的成员 用this

  • 如果访问的是父类的成员 用supper

/**
* Humanity
* 父类
* 属性 i
* @author hcj
* @date 2022-07-11
*/
public class Humanity {
String name = "张三";
int age = 18;
// 父类的测试变量
int i = 100;
// 父类无参构造方法
public Humanity() {
System.out.println("父类的无参构造方法");
}
// 父类全参构造方法
public Humanity(String name, int age) {
this.name = name;
this.age = age;
System.out.println("父类的全参构造方法");
}
// 普通方法
 public void eat(){
System.out.println("吃饭");
}
}
/**
* Chinese
* 属性 i
* @author hcj
* @date 2022-07-11
*/
public class Chinese extends Humanity{
// 自己特有属性
String address = "南京";
// 子类的测试变量
int i = 200;
// 子类无参构造方法
public Chinese() {
}
// 子类全参构造方法
public Chinese(String name, int age, String
address) {
super(name, age);
this.address = address;
System.out.println("子类的全参构造方法");
}
// 重写父类的方法
@Override
public void eat() {
System.out.println("南京人喜欢吃盐水鸭");
}
// 定义一个方法测试成员
public void test(){
// 调用本类的成员变量
System.out.println(this.address);
// 调用本类的成员方法
this.eat();
    // 调用父类的成员变量
System.out.println(super.name);
System.out.println(super.age);
// 调用自己的属性i
// 这里有三个i 一个是父类i 一个是子类i 一个是本方
法里i
int i = 300;
System.out.println(i); // 300
System.out.println(this.i); // 200
System.out.println(super.i); // 100
// 总结就近原则:局部变量>本类的成员变量>父类的成员
变量
// 调用父类的成员方法
eat();
this.eat();
super.eat();
}
}
public static void main(String[] args) {
Chinese c = new Chinese();
c.test();
}

super调用父类的构造器

调用构造方法
this(其他参数) 可以访问本类其他的构造方法
super(其他参数) 可以访问父类其他的构造方法
默认子类调用父类的构造方法:
子类的每个构造方法中均默认super(),调用父类的空参构造方法。
如果你手动调用父类构造会覆盖默认的super()

注意

① 子类中所有的构造器 默认 都会访问父类中的 无参 构造器。
② 当父类中没有无参构造器的时候,子类的构造器必须通过
this(参数列表) 或 super(参数列表) 语句指定调用本类或父类中
相应的构造器。同时,只能 二选一 ,并且放在构造器的首行。
③ 如果子类构造器既没有显示调用父类或本类的构造器,且父类中
又没有无参构造器,则 编译报错 。

测试:子类调用父类的构造方法案例
package com.hcj.extend;
/**
* ManKind
* 作为父类
* @author hcj
* @date 2022-07-11
*/
public class ManKind {
private String name = "李四";
private int age = 20;
public ManKind() {
System.out.println("父类无参构造方法");
}
public ManKind(String name, int age) {
System.out.println("父类全参构造方法");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
    public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println("吃了");
}
}
package com.hcj.extend;
/**
* China
* 子类 继承ManKind
* @author hcj
* @date 2022-07-11
*/
public class China extends ManKind{
// 子类自己的无参构造方法
public China() {
System.out.println("子类的无参构造方法");
}
// 子类自己的全参构造方法
public China(String name, int age) {
// super();
// 手动写super(name,age)就会覆盖掉super()
super(name,age);
System.out.println("子类的全参构造方法");
    }
// 重写父类的方法
@Override
public void eat() {
super.eat();
}
}
public static void main(String[] args) {
China c1 = new China();
System.out.println("=======================");
China c2 = new China("张三",18);
// 先输出:父类无参构造方法
// 后输出:子类的全参构造方法 因为子类全参构造方法
第一行默认super()
System.out.println("=======================");
// 把子类全参构造方法第一行加super(name,age)
China c3 = new China("王五",22);
// 先输出:父类全参构造方法
// 后输出:子类的全参构造方法 因为子类全参构造方法第
一行写super(name,age)就会覆盖掉super()
}

测试:子类调用子类的其他构造方法案例

package com.hcj.extend;
/**
* China
* 子类 继承ManKind
* @author hcj
* @date 2022-07-11
*/
public class China extends ManKind{
// 子类自己的无参构造方法
    public China() {
// super()被覆盖掉了
this("张三",18);
System.out.println("子类的无参构造方法");
}
// 子类自己的全参构造方法
public China(String name, int age) {
// super();
// 手动写super(name,age)就会覆盖掉super()
super(name,age);
System.out.println("子类的全参构造方法");
}
}
public static void main(String[] args) {
China c = new China();
// 先输出 父类全参构造方法 走到子类无参构造方法里
调用this("张三",18);
// 再输出 子类的全参构造方法
// 最后输出 子类的无参构造方法
}

this和super的区别

访问属性:this访问本类中的属性,如果本类中没有找到此属性,则从父类中继续查找;super直接访问父类中的属性

访问方法:this访问本类中的方法,如果本类中没有找到此方法,则从父类中继续查找;super直接访问父类中的方法

调用构造器:this调用本类构造器,必须放在构造器的首行;调用父类构造器,必须放在子类构造器首行

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值