Java继承

简介: 各位朋友们,大家好!今天我为大家分享的是Java上面一个很重要的知识点:继承,继承也被称为Java的灵魂之一。

为什么要使用继承


在生活中,我们都见过猫和狗这两种动物吧,我们都知道Java是一种面向对象的语言,我们可以创建出任何我们想要的对象,那么如果我们想要用Java来创建出猫和狗该怎么办呢?在创建之前我们首先得知道猫和狗有什么特性。我们都知道猫和狗都有名字、体重、身高,都会吃饭、睡发出叫声,那么我们可以通过创建类将他们的特性包含在内。


//猫
class Cat {
    String name;
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
    public void bark(){
    System.out.println(name+"汪汪汪~~~");
  }
}
//狗
class Dog {
    String name;
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"正在睡觉");
    }
    public void bark(){
    System.out.println(name+"喵喵喵~~~");
  }
}


当我们看到两个类的时候,我们可能会发现问题:那就是这两个代码中有很多重复的成员变量,那么如果我们要想大大降低代码的复用率该怎么办呢?这里最好的解决方法就是使用我们所说的继承,因为猫和狗都属于动物,我们可以将猫和动物以及其他类型的动物的共性都给提出来作为父类,子类只需要继承父类就可以拥有父类所具有的特性。


//Animal作为父类具有子类的共特性
class Animal {
    String name;
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
}

子类怎样继承父类


我们子类要想继承父类,我们需要用到extends这个关键字。格式是

class 子类类名 extends 父类类名{}

所以我们上面的代码可以简化成这样:

class Animal {
    String name;
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
}
class Cat extends Animal{
    public void bark(){
    System.out.println(name+"汪汪汪~~~");
  }
}
class Dog extends Animal{
    public void bark(){
    System.out.println(name+"喵喵喵~~~");
  }
}

而猫和狗的内存分配大概是这样的。


在子类中如何访问父类的成员


一般在子类中我们可以直接访问父类的成员变量。就像这样:

class Animal {
    String name = "Animal";
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
}
class Cat extends Animal{
    public void Method() {
        System.out.println(name);
    }
    public void Bark() {
        System.out.println(name+"喵喵喵~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Cat cat  =new Cat();
        cat.Method();
    }
}

18.png

这种是子类和父类没有相同的成员变量,如果子类和父类有相同的成员变量时,我们访问的又是谁的成员变量呢?

class Animal {
    String name = "Animal";
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
}
class Cat extends Animal{
    String name = "大黄";
    public void Method() {
        System.out.println(name);
    }
    public void Bark() {
        System.out.println(name+"喵喵喵~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Cat cat  =new Cat();
        cat.Method();
    }
}

19.png

我们可以看到这里他访问的是子类自己拥有的成员,也就是说在这里遵守就近原则,方法也是如此。

class Animal {
    String name = "Animal";
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
    public void Method1() {
        System.out.println("this is Animal's Method");
    }
}
class Cat extends Animal{
    String name = "大黄";
    public void Method() {
        System.out.println(name);
    }
    public void Method1() {
        System.out.println("this is Cat's Method");
    }
    public void Bark() {
        System.out.println(name+"喵喵喵~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Cat cat  =new Cat();
        cat.Method1();
    }
}

20.png

那么如果我们想在子类中访问相同的成员的父类的成员该怎么办呢?


super关键字


我们需要用到super这个关键字来访问父类中与子类具有相同名字的成员。

class Animal {
    String name = "Animal";
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
    public void Method1() {
        System.out.println("this is Animal's Method");
    }
}
class Cat extends Animal{
    String name = "大黄";
    public void Method() {
        System.out.println(name);
    }
    public void Method1() {
        System.out.println("this is Cat's Method");
    }
    public void Method2() {
        System.out.println(super.name);
    }
    public void Bark() {
        System.out.println(name+"喵喵喵~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Cat cat  =new Cat();
        cat.Method2();
    }
}

21.png

我们也可以用super来访问父类的方法。

class Animal {
    String name = "Animal";
    float weight;
    float hight;
    public void Eat() {
        System.out.println(name+"正在吃");
    }
    public void Sleep() {
        System.out.println(name+"在睡觉");
    }
    public void Method1() {
        System.out.println("this is Animal's Method");
    }
}
class Cat extends Animal{
    String name = "大黄";
    public void Method() {
        System.out.println(name);
    }
    public void Method1() {
        System.out.println("this is Cat's Method");
    }
    public void Method2() {
        System.out.println(super.name);
    }
    public void Method3() {
        Method1();
        super.Method1();
    }
    public void Bark() {
        System.out.println(name+"喵喵喵~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Cat cat  =new Cat();
        cat.Method3();
    }
}

image.png


当父类中有构造方法时


当我们的父类中有构造方法时,我们在构造子类的时候需要在构造方法的第一行使用super(),来先帮助父类完成构造,然后再完成自己的构造方法。

class Base {
    public Base() {
        System.out.println("Base");
    }
}
class Derived extends Base{
    public Derived() {
        super();
        System.out.println("Derived");
    }
}
public class Main {
    public static void main(String[] args) {
        Derived deriver = new Derived();
    }
}

23.png

不仅如此我们都知道如果你的类中没有构造方法,编译器会默认帮你生成一个无参的构造方法,如果你的类中还有继承关系,那么在构造方法的第一行还会默认有super()。

class Base {
    public Base() {
        System.out.println("Base");
    }
}
class Derived extends Base{
}
public class Main {
    public static void main(String[] args) {
        Derived deriver = new Derived();
    }
}

image.png

但是如果你的父类中有有参数的构造方法,你就必须先帮助父类完成构造,否则编译器会报错。

image.png

image.png

this关键字


我们在实例化一个类时,往往是先完成这个类具有的构造方法,而构造方法往往是给类的成员变量赋值的,那么我们怎样通过构造方法给成员变量赋值呢?


这里就需要this,this代表当前类实例的一个引用,它可以在构造器、成员方法和代码块中使用,表示当前类的实例对象。

class Base {
    String name;
    public Base(String name) {
        this.name = name;
        System.out.println(name);
    }
}
class Derived extends Base{
    public Derived() {
        super("大黄");
    }
}
public class Main {
    public static void main(String[] args) {
        Derived deriver = new Derived();
    }
}

27.png

this的功能不止这些,我们都知道方法可以发生重载,构造方法也是如此。

我们可以使用this(),来完成该类中的其他的构造方法。

class Base {
    String name;
    int age;
    public Base(String name) {
    //完成该类中带有两个参数的构造方法
        this(name,0);
        this.name = name;
        System.out.println(name);
    }
    public Base(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println(name+age);
    }
}
class Derived extends Base{
    public Derived() {
        super("大黄");
    }
}
public class Main {
    public static void main(String[] args) {
        Derived deriver = new Derived();
    }
}

image.png

但是这里我们需要注意的是this()跟super()一样,都必须放在构造方法的第一行,也就是说super()和this()不能同时出现。


初始化

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("构造方法执行");
    }
    {
        System.out.println("实例代码块执行");
    }
    static {
        System.out.println("静态代码块执行");
    }
}
public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("zhangsan",10);
        System.out.println("===========================");
        Person person2 = new Person("lisi",18);
    }
}


我们看看这段代码的结果是什么?


image.png


通过这段代码我们可以知道静态代码块、实例代码块和构造方法的执行顺序是:先执行静态代码块,然后是实例代码块,最后是构造方法,并且静态代码块只会执行一次。


那么如果有继承关系的时候执行顺序又是怎样的呢?

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person:构造方法执行");
    }
    {
        System.out.println("Person:实例代码块执行");
    }
    static {
        System.out.println("Person:静态代码块执行");
    }
}
class Student extends Person{
    public Student(String name,int age) {
        super(name,age);
        System.out.println("Student:构造方法执行");
    }
    {
        System.out.println("Student:实例代码块执行");
    }
    static {
        System.out.println("Student:静态代码块执行");
    }
}
public class Main {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",19);
        System.out.println("===========================");
        Student student2 = new Student("lisi",20);
    }
}

30.png


也就是说先执行父类的静态代码块,然后是子类的静态代码块,然后是父类的实例代码块,父类的构造方法,最后是子类的实例代码块和构造方法。


protected关键字


我们的子类是否可以访问父类的所有成员呢?其实并不是。


31.png


我们可以看到这里的c是不能被访问的,因为父类的c被private修饰的,他只能在当前类中使用,那么如果我们只让父类的子类使用的成员该怎么办呢?我们可以使用protected。


image.png


你其他的没有继承父类的类就不能使用被protected修饰过的成员,这样大大提高了代码的安全性。


33.png

final关键字

如果我们的类不想被继承,我们可以在类的前面加上final修饰,代表他是最终类,该类不可被继承。final修饰变量表明该变量不可被改变,他的属性也就从变量变成了常量。final修饰方法,表明这个方法不可被重写,重写我们后面再讲。


建议

在Java中继承是一个非常实用的特性,我们应该正确的使用它。继承关系可以有很多层,但是不建议超过三层,并且在Java中不可以多继承,也就是说一个类不能继承多个类,就像一个儿子不能有多个父亲一样,父类可以有多个子类。

目录
打赏
0
0
0
0
3
分享
相关文章
|
1月前
|
Java 面向对象编程的三大法宝:封装、继承与多态
本文介绍了Java面向对象编程中的三大核心概念:封装、继承和多态。
112 15
|
3月前
|
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
290 60
Java中的继承和多态:理解面向对象编程的核心概念
【8月更文挑战第22天】在Java的世界中,继承和多态不仅仅是编程技巧,它们是构建可维护、可扩展软件架构的基石。通过本文,我们将深入探讨这两个概念,并揭示它们如何共同作用于面向对象编程(OOP)的实践之中。你将了解继承如何简化代码重用,以及多态如何为程序提供灵活性和扩展性。让我们启程,探索Java语言中这些强大特性的秘密。
|
4月前
|
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
67 3
|
4月前
|
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
96 2
|
4月前
|
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
72 2
|
4月前
|
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
76 1
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
Java零基础-继承详解!
【10月更文挑战第4天】Java零基础教学篇,手把手实践教学!
63 2
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
49 5

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等