JavaSE-7 继承

一、继承在JVM里面是什么样子的 

 

二、super关键字 

1. 访问父类的成员变量

当子类中定义的成员变量与父类的成员变量同名时,为了在子类中访问父类的成员变量,可以使用 super 关键字。

class Parent {
    int value = 10;
}

class Child extends Parent {
    int value = 20;

    public void printValues() {
        // 访问子类的成员变量
        System.out.println("子类的 value: " + value);
        // 访问父类的成员变量
        System.out.println("父类的 value: " + super.value);
    }
}

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

在上述代码中,Child 类继承自 Parent 类,并且都定义了名为 value 的成员变量。在 Child 类的 printValues 方法中,使用 value 访问的是子类的成员变量,而使用 super.value 访问的是父类的成员变量。

2. 调用父类的方法

当子类重写了父类的方法后,如果需要在子类的方法中调用父类被重写的方法,可以使用 super 关键字。

class Parent {
    public void display() {
        System.out.println("这是父类的 display 方法");
    }
}

class Child extends Parent {
    @Override
    public void display() {
        // 调用父类的 display 方法
        super.display();
        System.out.println("这是子类的 display 方法");
    }
}

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

在上述代码中,Child 类重写了 Parent 类的 display 方法。在 Child 类的 display 方法中,使用 super.display() 调用了父类的 display 方法,然后再执行子类自身的逻辑。

3. 调用父类的构造方法

在子类的构造方法中,可以使用 super 关键字调用父类的构造方法。需要注意的是,super 调用必须是子类构造方法中的第一条语句。

class Parent {
    private int value;

    public Parent(int value) {
        this.value = value;
        System.out.println("父类的构造方法被调用,value = " + value);
    }
}

class Child extends Parent {
    public Child(int value) {
        // 调用父类的构造方法
        super(value);
        System.out.println("子类的构造方法被调用");
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child(10);
    }
}

在上述代码中,Child 类的构造方法中使用 super(value) 调用了父类的构造方法,先执行父类的构造逻辑,然后再执行子类的构造逻辑。

总结

  • super 关键字主要用于在子类中访问父类的成员变量、调用父类的方法和构造方法。
  • 在访问父类成员变量和方法时,super 可以帮助我们区分子类和父类中同名的成员。
  • 在调用父类构造方法时,super 调用必须放在子类构造方法的第一行,以确保父类对象先被正确初始化。

 三、super和this调用构造方法的区别

  • this 关键字:用于在一个构造方法中调用同一个类的其他构造方法,this() 调用必须放在构造方法的第一行。
class Student {
    private String name;
    private int age;

    public Student() {
        // 调用另一个构造方法
        this("未知", 0); 
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

  • super 关键字:用于在子类的构造方法中调用父类的构造方法super() 调用同样必须放在子类构造方法的第一行,以确保父类对象先被正确初始化。
class Parent {
    public Parent() {
        System.out.println("父类的无参构造方法被调用");
    }
}

class Child extends Parent {
    public Child() {
        // 调用父类的无参构造方法
        super(); 
        System.out.println("子类的无参构造方法被调用");
    }
}

四、super和this只能在非静态方法中使用

1. this 关键字只能在非静态方法中使用的原因

  • this 的本质this 关键字代表当前对象的引用,它指向正在调用该方法的对象实例。也就是说,this 依赖于对象的存在,只有当对象被创建后,this 才有实际的指向意义。
  • 静态方法的特性:静态方法属于类本身,不依赖于任何对象实例。在类加载时,静态方法就已经存在于内存中,此时还没有创建任何对象实例。如果在静态方法中使用 this 关键字,由于没有对象实例,this 就没有具体的指向,会导致逻辑错误。
public class ThisInStaticExample {
    private int value;

    // 静态方法
    public static void staticMethod() {
        // 编译错误,不能在静态方法中使用 this
        // System.out.println(this.value); 
    }

    // 非静态方法
    public void nonStaticMethod() {
        // 可以在非静态方法中使用 this
        System.out.println(this.value); 
    }
}

2. super 关键字只能在非静态方法中使用的原因

  • super 的本质super 关键字代表当前对象的父类对象的引用,它同样依赖于对象实例。只有当子类对象被创建后,通过该对象才能访问其父类的成员,super 才有实际的指向和作用。
  • 静态方法的特性:静态方法不与任何对象实例关联,它是类级别的方法。在静态方法执行时,没有对象实例存在,也就无法确定 super 所指向的父类对象,因此在静态方法中使用 super 会导致编译错误。
class Parent {
    protected int parentValue;
}

class Child extends Parent {
    // 静态方法
    public static void staticMethod() {
        // 编译错误,不能在静态方法中使用 super
        // System.out.println(super.parentValue); 
    }

    // 非静态方法
    public void nonStaticMethod() {
        // 可以在非静态方法中使用 super
        System.out.println(super.parentValue); 
    }
}

 

 五、子类构造方法

在 Java 中,子类的构造方法默认会调用父类的无参构造方法。如果父类没有无参构造方法,子类的构造方法必须显式地调用父类的有参构造方法

子类构造方法 super(value)在前,后初始化子类特有的属性,先帮父类部分初始化。

// 父类
class Parent {
    private int value;

    // 有参构造方法
    public Parent(int value) {
        this.value = value;
        System.out.println("Parent 构造方法被调用,value: " + value);
    }
}

// 子类
class Child extends Parent {
    private String name;

    // 子类构造方法
    public Child(int value, String name) {
        // 显式调用父类的有参构造方法
        super(value);
        this.name = name;
        System.out.println("Child 构造方法被调用,name: " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child(10, "Alice");
    }
}

代码解释

  • Parent 类有一个有参构造方法,用于初始化 value 属性。
  • Child 类继承自 Parent 类,它的构造方法中使用 super(value) 显式调用了父类的有参构造方法。
  • 在 main 方法中创建 Child 对象时,会先调用父类的构造方法,再调用子类的构造方法。

字类调用父类的构造方法来初始化,顺序是:父类的静态块,子类的静态块,父类的实例块和构造方法,子类的实例块和构造方法。 

 

 

 

 六、final关键字

1. final 修饰类

当 final 修饰一个类时,意味着这个类不能被继承,即它不能有子类。常用于设计一些不希望被其他类扩展的类,比如 Java 标准库中的 String 类就是 final 类。

final class FinalClass {
    public void display() {
        System.out.println("This is a final class.");
    }
}

// 以下代码会编译错误,因为 FinalClass 不能被继承
// class SubFinalClass extends FinalClass { }

public class Main {
    public static void main(String[] args) {
        FinalClass fc = new FinalClass();
        fc.display();
    }
}
2. final 修饰方法

如果 final 修饰一个方法,那么这个方法不能被其子类重写(覆盖)。这可以保证该方法的实现逻辑不被改变,常用于确保某些核心逻辑的稳定性。

class ParentClass {
    public final void finalMethod() {
        System.out.println("This is a final method.");
    }
}

class ChildClass extends ParentClass {
    // 以下代码会编译错误,因为 finalMethod 不能被重写
    // public void finalMethod() { }
}

public class Main {
    public static void main(String[] args) {
        ParentClass pc = new ChildClass();
        pc.finalMethod();
    }
}
3. final 修饰变量
  • 基本数据类型变量:一旦被赋值,其值就不能再被改变,相当于常量。
public class Main {
    public static void main(String[] args) {
        final int num = 10;
        // 以下代码会编译错误,因为 num 是 final 变量,不能重新赋值
        // num = 20; 
        System.out.println(num);
    }
}
  • 引用数据类型变量:变量所引用的对象不能再改变,但对象的内容可以改变。
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        final List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        // 以下代码会编译错误,因为 list 引用不能再改变
        // list = new ArrayList<>(); 
        System.out.println(list);
    }
}

 

七、protect权限:不同包的子类可以访问

在 Java 中,protected 是一种访问权限限定符,用于控制类的成员(字段、方法等)的访问范围。protected 权限介于 private 和 public 之间,下面详细介绍其使用方法和特点。

1. 基本概念

protected 成员可以被以下几种情况访问:

  • 同一个类内部可以访问。
  • 同一个包内的其他类可以访问。
  • 不同包的子类可以访问(通过继承)。

2. 示例代码

同一个包内的访问
// 同一个包内的父类
package com.example.demo;

public class Parent {
    protected int protectedField = 10;

    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }
}

// 同一个包内的另一个类
package com.example.demo;

public class SamePackageClass {
    public static void main(String[] args) {
        Parent parent = new Parent();
        // 可以访问 protected 字段
        System.out.println(parent.protectedField); 
        // 可以访问 protected 方法
        parent.protectedMethod(); 
    }
}
不同包的子类访问
// 父类,位于 com.example.parent 包
package com.example.parent;

public class Parent {
    protected int protectedField = 10;

    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }
}

// 子类,位于 com.example.child 包
package com.example.child;

import com.example.parent.Parent;

public class Child extends Parent {
    public void accessParentProtectedMembers() {
        // 可以访问从父类继承的 protected 字段
        System.out.println(this.protectedField); 
        // 可以访问从父类继承的 protected 方法
        this.protectedMethod(); 
    }

    public static void main(String[] args) {
        Child child = new Child();
        child.accessParentProtectedMembers();
    }
}
不同包且非子类的访问限制
// 父类,位于 com.example.parent 包
package com.example.parent;

public class Parent {
    protected int protectedField = 10;

    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }
}

// 不同包且非子类的类
package com.example.other;

import com.example.parent.Parent;

public class OtherClass {
    public static void main(String[] args) {
        Parent parent = new Parent();
        // 以下代码会编译错误,因为 OtherClass 不是 Parent 的子类,且不在同一个包内
        // System.out.println(parent.protectedField); 
        // parent.protectedMethod(); 
    }
}

3. 总结

protected 权限限定符提供了一种灵活的访问控制机制,它允许在同一个包内的类以及不同包的子类访问类的成员,同时限制了其他类的访问,有助于封装和信息隐藏,提高代码的安全性和可维护性。

 

 

八、类的修饰符为什么只能是public或者什么都不写

在 Java 里,顶层类(直接定义在包中的类)的修饰符通常只能是 public 或者默认(即什么都不写),这主要和 Java 的访问控制机制以及设计理念有关,下面详细阐述:

访问控制机制

Java 提供了四种访问修饰符,分别是 publicprotectedprivate 和默认(没有显式修饰符),它们各自有不同的访问权限范围:

  • public:被 public 修饰的类、方法或字段可以在任何地方被访问。
  • protected:被 protected 修饰的成员能在同一个包内的类中访问,也能在不同包的子类中访问。
  • 默认(无修饰符):默认访问权限也称为包访问权限,意味着该类、方法或字段只能在同一个包内的其他类中访问。
  • private:被 private 修饰的成员只能在定义它的类内部访问。

对于顶层类而言,使用 protected 和 private 修饰并不符合逻辑:

  • protectedprotected 主要用于控制子类对父类成员的访问权限,而顶层类不存在父类 - 子类这种层次关系,所以使用 protected 修饰顶层类没有实际意义。
  • private:如果顶层类被 private 修饰,那就意味着该类只能在定义它的类内部访问,这使得这个类无法被其他任何类使用,这样的类几乎没有实际用途,所以 Java 不允许使用 private 修饰顶层类。

代码示例说明

  • public 修饰的顶层类
// 定义一个 public 修饰的顶层类
package com.example;

public class PublicClass {
    public void publicMethod() {
        System.out.println("This is a public method in a public class.");
    }
}

这个 PublicClass 可以在任何包的类中被访问和使用,如下:

package another.example;

import com.example.PublicClass;

public class Main {
    public static void main(String[] args) {
        PublicClass pc = new PublicClass();
        pc.publicMethod();
    }
}
  • default 默认修饰的顶层类
// 定义一个默认修饰的顶层类
package com.example;

class DefaultClass {
    public void defaultMethod() {
        System.out.println("This is a method in a default class.");
    }
}

这个 DefaultClass 只能在 com.example 包内的其他类中被访问,若在不同包的类中访问则会编译错误:

// 在同一个包内访问默认类
package com.example;

public class Test {
    public static void main(String[] args) {
        DefaultClass dc = new DefaultClass();
        dc.defaultMethod();
    }
}

总结

顶层类使用 public 或者默认修饰符,是为了确保类的访问控制符合逻辑和实际使用场景。public 允许类在全局范围内被使用,而默认修饰符则将类的使用范围限制在同一个包内,这样的设计保证了 Java 程序的安全性和可维护性。不过,内部类可以使用 protected 和 private 修饰,因为内部类存在于外部类的内部,有明确的层次关系,这些修饰符能更好地控制内部类的访问权限。

 九、继承和组合

继承和组合是面向对象编程中两种重要的代码复用和组织方式,它们各有特点和适用场景,下面将详细介绍这两种方式。

继承(Inheritance)

概念

继承是指一个类(子类、派生类)可以继承另一个类(父类、基类)的属性和方法,子类可以复用父类的代码,并且可以在此基础上添加新的属性和方法,或者重写父类的方法以实现不同的行为。继承体现了 “is-a” 的关系,即子类是父类的一种特殊类型。

语法示例(以 Java 为例)
// 父类
class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

// 子类
class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    public void bark() {
        System.out.println(name + " is barking.");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("Buddy");
        dog.eat(); 
        dog.bark(); 
    }
}
代码解释
  • Animal 是父类,定义了 name 属性和 eat 方法。
  • Dog 是子类,继承自 Animal 类,通过 extends 关键字实现。Dog 类可以使用 Animal 类的 name 属性和 eat 方法,同时还定义了自己的 bark 方法。
优点
  • 代码复用:子类可以直接使用父类的属性和方法,减少了代码的重复编写。
  • 多态性:通过继承可以实现多态,提高代码的灵活性和可扩展性。
缺点
  • 耦合度高:子类与父类之间的耦合度较高,父类的修改可能会影响到子类。
  • 继承层次过深:如果继承层次过多,会导致代码的可读性和可维护性降低。

 

组合(Composition)

概念

组合是指一个类包含另一个类的对象作为其成员变量,通过调用这些对象的方法来实现代码复用。组合体现了 “has-a” 的关系,即一个类拥有另一个类的对象

语法示例(以 Java 为例)
// 类 A
class Engine {
    public void start() {
        System.out.println("Engine is starting.");
    }
}

// 类 B
class Car {
    private Engine engine;

    public Car() {
        this.engine = new Engine();
    }

    public void startCar() {
        engine.start();
        System.out.println("Car is starting.");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.startCar();
    }
}
代码解释
  • Engine 类表示汽车的发动机,有一个 start 方法。
  • Car 类包含一个 Engine 类的对象作为成员变量,通过调用 engine.start() 方法来实现汽车启动的功能。
优点
  • 低耦合:类之间的耦合度较低,一个类的修改不会影响到其他类。
  • 灵活性高:可以在运行时动态地改变组合对象,提高代码的灵活性。
缺点
  • 代码量增加:需要创建和管理多个对象,代码量相对较多。

 

继承和组合的选择

  • 使用继承的场景:当两个类之间存在明显的 “is-a” 关系,并且需要使用多态性时,适合使用继承。例如,猫是动物的一种,狗也是动物的一种,可以使用继承来表示这种关系。
  • 使用组合的场景:当两个类之间存在 “has-a” 关系,并且希望降低类之间的耦合度时,适合使用组合。例如,汽车拥有发动机,电脑拥有硬盘等,可以使用组合来表示这种关系。
  • 在实际编程中,通常会根据具体的需求和设计目标来选择使用继承还是组合,有时候也会将两者结合使用,以达到更好的代码复用和组织效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值