java面向对象编程【高级篇】之继承

目录

🚀前言

大家好!我是 EnigmaCoder

本文主要介绍java面向对象编程中的继承部分,包括定义、权限修饰符、特点、方法重写等。

🤔什么是继承?

在java中,继承是面向对象的三大特性之一,它允许一个类(称为子类或派生类)直接获取另一个类(称为父类或超类或基类)的属性和方法,从而实现代码重用和建立类之间的层次关系。

  • 使用extends关键字完成继承,格式如下:
java 复制代码
class A{}                      //父类A
class B extends A{}            //子类B继承父类A
  • 子类能继承父类的非私有成员(成员变量、成员方法)。
  • 子类的对象是由子类和父类共同完成的。

代码案例:

java 复制代码
class Person {
   private String name;
   private int age;
   public void setName(String name){
      this.name=name;
   }
   public String getName(){
      return name;
   }
   public void setAge(int age){
      this.age=age;
   }
   public int getAge(){
      return age;
   }
}

class Student extends Person{
}

class Test{
  public static void main(String[] args){
      Student stu =new Student();
      stu.setName("张三");
      stu.setAge("18");
      System.out.println("姓名:"+stu.getName()+",年龄:"+stu.getAge());
  }
}

运行结果:

java 复制代码
姓名:张三,年龄:18

可见,通过继承的功能可以直接将父类中的操作拿到子类中使用,即使子类中没有定义任何操作。

当然,子类也可以定义自己的属性和方法,可以通过子类扩展父类的功能,例如:

java 复制代码
class Student extends Person{
    private String school;
    public void setSchool(String school){
       this.school=school;
    }
    public String getSchool(){
       return school;
    } 
}

这样,主方法中也能setSchool()getSchool()方法。

🌟权限修饰符

💯private 修饰符

  • 访问权限:只能在定义它的类内部被访问。
  • 使用场景:常用于隐藏类的内部数据和实现细节,防止外部直接访问和修改,保证数据的安全性和封装性。
  • 示例代码
java 复制代码
class PrivateExample {
    // 私有成员变量
    private int privateVariable = 10;

    // 私有方法
    private void privateMethod() {
        System.out.println("This is a private method.");
    }

    public void accessPrivateMembers() {
        // 可以在类内部访问私有成员
        System.out.println(privateVariable);
        privateMethod();
    }
}

public class Main {
    public static void main(String[] args) {
        PrivateExample example = new PrivateExample();
        // 以下代码会报错,因为不能在类外部访问私有成员
        // System.out.println(example.privateVariable); 
        // example.privateMethod(); 
        example.accessPrivateMembers();
    }
}

💯默认(无修饰符)

  • 访问权限:也称为包访问权限,只能被同一个包中的类访问。
  • 使用场景:当你希望某些类、方法或变量仅在同一个包内可见时使用,有助于组织和管理代码,将相关的类放在同一个包中并相互访问。
  • 示例代码
java 复制代码
// 假设在 com.example 包下
package com.example;

class DefaultExample {
    // 默认成员变量
    int defaultVariable = 20;

    // 默认方法
    void defaultMethod() {
        System.out.println("This is a default method.");
    }
}

// 同一个包中的另一个类
class AnotherClassInSamePackage {
    public static void main(String[] args) {
        DefaultExample example = new DefaultExample();
        // 可以在同一个包中访问默认成员
        System.out.println(example.defaultVariable);
        example.defaultMethod();
    }
}

💯protected 修饰符

  • 访问权限:可以被同一个包中的类访问,也可以被不同包中的子类访问。
  • 使用场景:当你希望某个类的成员不仅能在同一个包中被访问,还能被不同包的子类继承和使用时使用。
  • 示例代码
java 复制代码
// 假设在 com.parent 包下
package com.parent;

public class ParentClass {
    // 受保护的成员变量
    protected int protectedVariable = 30;

    // 受保护的方法
    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }
}

// 假设在 com.child 包下
package com.child;

import com.parent.ParentClass;

class ChildClass extends ParentClass {
    public void accessProtectedMembers() {
        // 可以在不同包的子类中访问受保护成员
        System.out.println(protectedVariable);
        protectedMethod();
    }
}

public class Main {
    public static void main(String[] args) {
        ChildClass child = new ChildClass();
        child.accessProtectedMembers();
    }
}

💯public 修饰符

  • 访问权限:可以被任何类访问,无论这些类是否在同一个包中,也无论它们是否存在继承关系。
  • 使用场景:常用于定义公共的 API,如公共类、公共方法和公共常量等,方便其他类使用。
  • 示例代码
java 复制代码
// 假设在 com.publicapi 包下
package com.publicapi;

public class PublicExample {
    // 公共成员变量
    public int publicVariable = 40;

    // 公共方法
    public void publicMethod() {
        System.out.println("This is a public method.");
    }
}

// 可以在任何包中的类中访问公共成员
public class Main {
    public static void main(String[] args) {
        PublicExample example = new PublicExample();
        System.out.println(example.publicVariable);
        example.publicMethod();
    }
}

💯归纳

权限修饰符 本类 同一个包中的类 不同包中的子类 不同包中的非子类
private 可以访问 不可访问 不可访问 不可访问
默认(无修饰符) 可以访问 可以访问 不可访问 不可访问
protected 可以访问 可以访问 可以访问 不可访问
public 可以访问 可以访问 可以访问 可以访问

🦜继承的特点

💯单继承

java只允许单继承,不能使用多重继承,即一个子类只能继承一个父类;也允许多层继承,即一个子类可以继承一个父类,这个父类也可以继承一个父类。

  • 错误代码
java 复制代码
class A{}
class B{}
class C extends A,B{}    //同时继承两个类,代码错误
  • 多层继承
java 复制代码
class A{}
class B extends A{}
class C extends B{}

💯Object类

在 Java 里,Object 类是所有类的父类,即每个类都直接或间接地继承自 Object 类。这意味着 Java 中的任何对象都能调用 Object 类所定义的方法。

  • 顶级父类:作为 Java 类层次结构的顶端,所有类默认继承它,无需显式声明。
  • 多态基础:由于所有类都继承自 Object 类,所以可以将任何对象赋值给 Object 类型的变量,为多态性提供了基础。

💯就近原则

在子类方法中访问其它成员,是依照就近原则的。

  • 先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果都未找到就报错。

注意:如果子父类中,出现了重名成员,会优先使用子类的。如果想要在子类中使用父类的,就需要用到super关键字。

格式如下

java 复制代码
super.父类成员变量/父类成员方法

代码示例如下

java 复制代码
public class Test {
    public static void main(String[] args) {
        Zi zi=new Zi();
        zi.show();
    }
}
class Fu {
      String name="父类的name";
      public void run(){
          System.out.println("父类的run方法");
      }
}
class Zi extends Fu{
      String name="子类的name";
      public void show(){
          String name="show的name";
          System.out.println(name);//show的name
          System.out.println(this.name);//子类的name
          System.out.println(super.name);//父类的name

          super.run();//父类的run方法
          run();//子类的run方法
      }

      public void run(){
          System.out.println("子类的run方法");
      }

}

运行结果:

java 复制代码
show的name
子类的name
父类的name
父类的run方法
子类的run方法

✍️方法重写

当子类觉得父类中的某一个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写(声明不变,重新实现)。

  • 代码示例
java 复制代码
public class Test {

    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
    }
}

class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}
  • 运行结果
java 复制代码
猫吃鱼

这段代码中,子类Cat继承父类Animal,子类中的eat方法重写了父类中的eat方法,所以输出结果为子类方法中的内容。

💯@Override

在 Java 里,@Override属于注解,其用途是告知编译器,我们打算对父类中的一个方法进行重写。

用途:

  • 编译器检查:借助 @Override 注解,编译器会核查该方法是否确实重写了父类的方法。若没有重写,编译器就会报错,这样能避免因拼写错误或者方法签名不匹配引发的潜在问题。
  • 增强代码可读性:明确表明这个方法是对父类方法的重写,使代码的意图更加清晰。

💯方法重写的注意事项

  • 子类重写父类方法时,访问权限必须大于或等于父类该方法的权限。(public>protected>默认)
  • 重写的方法返回值类型必须与被重写方法的返回值类型一样,或者范围更小。
  • 私有方法、静态方法不能被重写,如果重写会报错。

💯toString方法重写

在 Java 中,toString 方法是 Object 类中的一个方法,所有类都继承自 Object 类,因此都拥有 toString 方法。toString 方法的主要作用是返回对象的字符串表示形式。默认情况下,toString 方法返回的是对象的类名和哈希码,这通常对开发者和用户来说没有太大的实际意义。所以,我们经常会重写 toString 方法,以提供更有意义的对象信息。

注意:

  • 直接输出对象,默认会调用Object类的toString方法(可以省略不写toString的方法)。

以下是一个重写 toString 方法的示例代码,我会为其添加详细的注释并进行简单说明:

java 复制代码
// 定义一个名为 Person 的类,用于表示一个人的信息
class Person {
    // 定义两个私有属性,分别表示人的姓名和年龄
    private String name;
    private int age;

    // 构造函数,用于初始化 Person 对象的姓名和年龄
    public Person(String name, int age) {
        // 使用 this 关键字来区分成员变量和局部变量
        this.name = name;
        this.age = age;
    }

    // 重写 Object 类的 toString 方法,以提供更有意义的对象信息
    @Override
    public String toString() {
        // 返回一个包含姓名和年龄信息的字符串
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建一个 Person 对象,并传入姓名和年龄
        Person person = new Person("Alice", 25);
        // 调用 person 对象的 toString 方法,并将结果打印输出
        System.out.println(person); 
    }
}

代码说明

  1. 类的定义 :定义了一个名为 Person 的类,该类有两个私有属性 nameage,分别表示人的姓名和年龄。
  2. 构造函数Person 类的构造函数用于初始化 nameage 属性。
  3. 重写 toString 方法 :在 Person 类中重写了 toString 方法,使用 @Override 注解告诉编译器这是一个重写的方法。重写后的 toString 方法返回一个包含 nameage 信息的字符串。
  4. 主方法 :在 Test 类的 main 方法中,创建了一个 Person 对象,并调用其 toString 方法将结果打印输出。由于 System.out.println 方法会自动调用对象的 toString 方法,所以可以直接传入 person 对象。

通过重写 toString 方法,我们可以方便地获取对象的详细信息,这在调试和日志记录中非常有用。

补充

  • Object 类中toString方法的源码如下:
java 复制代码
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

🐧子类构造器的特点

  • 子类中的全部构造器,都必须先调用父类的构造器,再执行自己。
  • 使用super(...)可以调用父类有参数构造器,为对象中包含父类这部分的成员变量进行赋值。
java 复制代码
public class Test {
    public static void main(String[] args) {
          Zi z = new Zi();
    }
}
class Zi extends Fu {
    public Zi() {
        super(666);//指定调用父类的有参构造器
        System.out.println("子类构造器");
    }
}

class Fu{
    public Fu() {
        System.out.println("父类构造器");
    }
    public Fu(int a) {
        System.out.println("父类有参构造器");
    }
}

运行结果:

java 复制代码
父类有参构造器
子类构造器

注意:super关键字必须写在首行。

💯this与super的区别

比较项 this super
基本含义 指代当前对象的引用,即调用方法或构造器的对象本身 代表父类(直接超类)的引用,用于访问父类中被覆盖的成员或构造器
主要作用 1. 区分成员变量与局部变量(两者同名时);2. 在构造器中调用同一类的其他构造器(需为构造器首行);3. 在方法中引用当前对象 1. 在子类构造器中调用父类的构造器(需为子类构造方法首行);2. 访问父类中被覆盖的成员变量或方法(子类重写父类成员时)
使用限制 1. 不能在静态方法或静态代码块中使用;2. 构造器中调用其他构造方法时,只能出现一次且必须为第一行 1. 子类构造器若未显式调用 super(),编译器会自动插入无参的父类构造器(若父类存在无参构造器);2. 若父类没有无参构造器,子类必须显式调用父类的有参构造器(通过 super(参数)
本质区别 操作当前类的成员(包括从父类继承的成员) 操作父类的成员(即使子类重写了父类成员,也能通过 super 访问父类的版本)
适用场景 访问当前对象成员或构造器 访问父类成员或构造器
相关推荐
Evand J15 分钟前
MATLAB技巧——平滑滤波,给出一定的例程和输出参考
开发语言·matlab
妙极矣17 分钟前
JAVAEE初阶01
java·学习·java-ee
碎叶城李白32 分钟前
NIO简单群聊
java·nio
LCY13344 分钟前
python 与Redis操作整理
开发语言·redis·python
暮乘白帝过重山1 小时前
路由逻辑由 Exchange 和 Binding(绑定) 决定” 的含义
开发语言·后端·中间件·路由流程
xxjiaz1 小时前
水果成篮--LeetCode
java·算法·leetcode·职场和发展
PingdiGuo_guo1 小时前
C++动态分配内存知识点!
开发语言·c++
CodeFox1 小时前
动态线程池 v1.2.1 版本发布,告警规则重构,bytebuddy 替换 cglib,新增 jmh 基准测试等!
java·后端
人类群星闪耀时1 小时前
5G赋能远程医疗:从愿景到现实的技术变革
开发语言·5g·php