我在VScode学Java继承(Java继承的特点、super关键字、super和this对比、方法重写、子类构造器)贰

类的成员包括:成员变量;成员方法;构造方法

构造方法:不管什么修饰符都不可继承
成员变量:都可以继承,但注意一点(继承!=调用,私有的就不可调用)
成员方法:非私有可以。私有不可。

我的个人博客主页:如果’'真能转义1️⃣说1️⃣的博客主页
关于Java基本语法学习---->可以参考我的这篇博客:《我在VScode学Java》
关于—>可以参考我的这篇文章《我在VScode学Java继承(Java继承是什么、特点、子类继承什么)壹》

文章目录

继承中成员变量访问的特点:就近原则

这意味着如果在子类中存在与父类同名的成员变量,子类将优先访问自己的成员变量,而不是继承自父类的同名成员变量。

具体来说,当在子类中使用一个成员变量时,系统首先查找子类自身是否有这个成员变量,如果有就直接使用子类的成员变量。如果子类中没有这个成员变量,系统会继续向上查找,去父类中寻找是否有同名的成员变量。如果在父类中找到了同名的成员变量,那么就会使用父类的成员变量。

这个特点确保了在继承关系中,子类可以通过定义与父类同名的成员变量来隐藏或重写父类的成员变量。这样,子类可以根据自身的需求,对继承自父类的成员变量进行个性化的处理。

需要注意的是,尽管子类可以隐藏父类的成员变量,但这并不会影响父类的成员变量本身,父类的成员变量仍然存在于父类中。如果需要在子类中访问父类的成员变量,可以使用super关键字来引用父类的成员变量。
在这里插入图片描述

super关键字

在Java中,super是一个关键字,用于表示父类或超类(基类)的引用。它可以在子类中使用,用于访问父类的成员变量、成员方法或构造方法。主要有以下几个用途:

  1. 访问父类的成员变量:使用super关键字可以在子类中访问父类的成员变量,即使子类中有同名的成员变量。

  2. 调用父类的成员方法:在子类中,使用super关键字可以调用父类中被子类覆盖的方法,或者调用父类中未被子类覆盖的方法。

  3. 调用父类的构造方法:在子类的构造方法中,通过使用super关键字可以显式调用父类的构造方法,确保父类中的初始化逻辑被执行。

使用super关键字可以帮助子类与父类之间进行交互和继承,从而有效地扩展和重用代码。

super和this对比

在Java中,this 关键字和 super 关键字都是用于访问类的成员或调用构造方法,但它们有不同的作用和用法。

关键字:
- `this`:用于引用当前类的实例,用于访问当前类的成员变量、方法和构造方法。
- `super`:用于引用当前类的父类,用于访问父类的成员变量、方法和构造方法。

1. this 关键字:

`this` 关键字用于引用当前类的实例。可以理解为一个变量。
		表示当前对象的地址,并且用于访问或调用当前类的成员变量和方法。
实际上是通过这个方法的隐式参数来传递当前对象的引用。
		这个隐式参数就是 this,它指向当前调用该方法的对象。
  • 用于引用当前对象的实例,即当前正在调用方法或构造方法的对象。
  • 可以用来消除成员变量与局部变量之间的歧义,如果它们具有相同的名称。
  • 可以用来调用类的其他构造方法,以便在一个构造方法中重用代码(构造器重载)。

2. super 关键字:

`super` 关键字用于引用当前类的父类(超类)。
	它表示父类的存储空间,并且用于访问或调用父类的成员变量、方法或构造方法。
  • 用于引用父类(超类)的成员或调用父类的构造方法。
  • 可以通过 super 关键字访问父类的方法和成员,特别是在子类中覆盖了父类的方法时。
  • 在子类的构造方法中,可以使用 super 关键字调用父类的构造方法,确保父类的初始化工作得以执行。

3.示例:

class Parent {
    
    
    int value = 10;

    void display() {
    
    
        System.out.println("Parent class method");
    }
}

class Child extends Parent {
    
    
    int value = 20;

    void display() {
    
    
        System.out.println("Child class method");
    }

    void printValues() {
    
    
        int value = 30;
        System.out.println(value); // Prints local variable value (30)
        System.out.println(this.value); // Prints Child class variable value (20)
        System.out.println(super.value); // Prints Parent class variable value (10)

        this.display(); // Calls Child class method
        super.display(); // Calls Parent class method
    }
}
总结:
  • this 关键字用于当前对象的引用和构造器的调用。
  • super 关键字用于访问父类成员和调用父类构造器。
用法:
  • 访问成员变量:this.成员变量名 表示当前类的成员变量,super.成员变量名 表示父类的成员变量。
  • 访问成员方法:this.方法名(...) 表示当前类的成员方法,super.方法名(...) 表示父类的成员方法。
  • 访问构造方法:this(...) 表示当前类的构造方法,super(...) 表示父类的构造方法。
1.>当局部变量和成员变量的名字不同的时候,可以省略使用this 关键字。

在 Java 中,如果方法内的局部变量与类的成员变量没有同名,编译器会自动区分它们,优先使用局部变量。所以,当没有命名冲突时,你可以省略this关键字来引用类的成员变量。

然而,当局部变量和成员变量同名时,为了明确指定使用的是类的成员变量,你需要使用 this 关键字来区分它们。

public class Student {
    
    
    private String name; // 成员变量

    public Student(String name) {
    
    
        // 参数name和成员变量name同名,需要使用this关键字来区分
        this.name = name;
    }

    public void displayInfo(String name) {
    
    
        // 参数name和成员变量name不同名,可以省略this关键字
        System.out.println("传入的参数name:" + name);
        System.out.println("成员变量name:" + this.name);
    }
}

在上面的示例中,构造方法中的参数 name 和类的成员变量 name 同名,因此我们使用this.name来指代成员变量
而在 displayInfo 方法中,参数name和成员变量name 不同名,所以我们可以省略 this 关键字,直接使用局部变量 name

2.>this(...) 用于在一个构造方法中调用同一个类的其他构造方法。它只能在构造方法内部使用,且必须是构造方法的第一条语句。
 // Constructor with three parameters___具有三个参数的构造函数
    public Student(String name, int age, String school) {
    
    
        this.name = name;
        this.age = age;
        this.school = school;
    }

    // Constructor with no parameters, calling the three-parameter constructor using this(...)
    //没有参数的构造函数,使用以下(...)调用三参数构造函数
    public Student() {
    
    
        this(null, 0, "Magic_School");
    }

在Java中,如果出现了重名的成员变量,可以通过使用特定的关键字来区分它们。通常有以下三种方式:

就近原则

1. 从局部位置开始往上找:

如果在当前作用域(方法或代码块)内定义了一个同名的局部变量,它会覆盖同名的成员变量。在这种情况下,要访问成员变量,可以使用this关键字来明确指示是要访问当前类的成员变量。

public class MyClass {
    
    
    private String name = "Class member variable";

    public void printName(String name) {
    
    
        System.out.println(name);       // 局部变量name
        System.out.println(this.name);  // 类的成员变量name
    }
}

2. 从本类成员位置开始往上找:

如果在当前类的不同方法中出现同名的成员变量,可以直接使用成员变量的名称访问,编译器会自动选择正确的成员变量。

public class MyClass {
    
    
    private String name = "Class member variable";

    public void method1() {
    
    
        System.out.println(name);  // 类的成员变量name
    }

    public void method2() {
    
    
        System.out.println(name);  // 类的成员变量name
    }
}

3. 从父类成员位置开始往上找:

如果当前类继承自其他类,而父类和子类中有同名的成员变量,可以使用super关键字来访问父类的成员变量。

public class ParentClass {
    
    
    protected String name = "Parent class member variable";
}

public class ChildClass extends ParentClass {
    
    
    private String name = "Child class member variable";

    public void printNames() {
    
    
        System.out.println(name);          // 子类的成员变量name
        System.out.println(this.name);     // 子类的成员变量name
        System.out.println(super.name);    // 父类的成员变量name
    }
}

总结:在Java中,通过使用this关键字可以指向当前类的成员变量,而super关键字可以指向父类的成员变量。使用不同的关键字,可以访问到正确的成员变量。

this 关键字 + super 关键字

在Java中,如果在不同的作用域(例如实例变量和局部变量)中出现了同名的变量,
	可以使用`this`关键字来引用实例变量并访问正确的变量。`
this`关键字是对当前类实例的引用。

例如:

public class MyClass {
    
    
    private String name; // 这是实例变量

    public MyClass(String name) {
    
    
        this.name = name; // 使用"this"关键字来给实例变量赋值
    }

    public void printName() {
    
    
        String name = "局部变量"; // 这是局部变量
        System.out.println(name); // 这里引用的是局部变量
        System.out.println(this.name); // 这里引用的是实例变量
    }
}
类似地,如果一个类继承自另一个父类,并且两个类中有同名的变量,
	可以使用`super`关键字来引用父类的变量。

例如:

public class ParentClass {
    
    
    protected String name = "父类成员变量";
}

public class ChildClass extends ParentClass {
    
    
    private String name = "子类成员变量";

    public void printNames() {
    
    
        System.out.println(name); // 这里引用的是子类的变量
        System.out.println(this.name); // 这里也引用的是子类的变量
        System.out.println(super.name); // 这里引用的是父类的变量
    }
}

总结:在Java中,this关键字用于引用当前类实例,super关键字用于引用父类。这些关键字在需要访问具有相同名称但属于不同作用域或类的变量时非常有用。

继承中成员方法访问的特点:就近原则+super调用:

在Java继承中,成员方法的访问有两个重要的特点:就近原则和使用super关键字调用。

1. 就近原则:

Java中的就近原则是指在方法调用或变量访问时,会优先选择离当前位置最近的方法或变量。
	这意味着如果在当前类中存在与父类相同名称的方法或变量,Java会优先使用当前类中的方法或变量。	

就近原则指的是在继承链中,如果子类和父类中有相同名称的成员方法,那么子类将优先调用自己的方法,而不是父类的方法。换句话说,子类的方法会"遮盖"(覆盖)掉父类的方法。这种特性允许子类在需要时对继承的方法进行定制或修改。

class Parent {
    
    
    void print() {
    
    
        System.out.println("This is the parent's print method.");
    }
}

class Child extends Parent {
    
    
    void print() {
    
    
        System.out.println("This is the child's print method.");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Child child = new Child();
        child.print(); // Output: This is the child's print method.
    }
}

在上面的例子中,子类Child中的print方法覆盖了父类Parentprint方法。

2. 使用super关键字调用:

通过super关键字,可以直接访问父类中的方法或变量。

有时,子类的方法可能需要调用父类的被遮盖方法,可以使用super关键字来实现。super关键字允许在子类中调用父类的方法,即使该方法被子类覆盖。

class Parent {
    
    
    void print() {
    
    
        System.out.println("This is the parent's print method.");
    }
}

class Child extends Parent {
    
    
    void print() {
    
    
        super.print(); // Call the parent's print method
        System.out.println("This is the child's print method.");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Child child = new Child();
        child.print();
        /* Output:
           This is the parent's print method.
           This is the child's print method.
        */
    }
}

在上面的例子中,子类Child中的print方法首先使用super.print()调用了父类Parentprint方法,然后再进行子类自己的输出。

总结:
在Java继承中,成员方法遵循就近原则,子类的方法会优先调用自己的方法,而不是父类的方法。但如果需要调用父类的方法,可以使用super关键字来实现。

代码举例:

class Person {
    
    
    public void eat() {
    
    
        System.out.println("Person eats rice and vegetables");
    }

    public void drink() {
    
    
        System.out.println("Person drinks water");
    }
}

class Student extends Person {
    
    
    public void lunch() {
    
    
        // 首先,检查在本类中是否有"eat"和"drink"方法,如果有,则调用本类中的方法。
        this.eat();
        this.drink();
        // 然后,直接调用父类(Person)中的"eat"和"drink"方法。
        super.eat();
        super.drink();
    }
}

// OverseasStudent类,继承自Person类
class OverseasStudent extends Person {
    
    
    public void lunch() {
    
    
        // 首先,检查在本类(OverseasStudent)中是否有"eat"和"drink"方法, 如果有,则调用本类中的方法。

        this.eat();
        this.drink();
        // 然后,直接调用父类(Person)中的"eat"和"drink"方法。

        super.eat();
        super.drink();
    }
    // 重写Person类中的"eat"方法
    @Override
    public void eat() {
    
    
        System.out.println("OverseasStudent eats spaghetti");
    }
    
    // 重写Person类中的"drink"方法
    @Override
    public void drink() {
    
    
        System.out.println("OverseasStudent drinks cold water");
    }
}

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Student localStudent = new Student();
        localStudent.lunch();

        System.out.println("--------------------");

        OverseasStudent overseasStudent = new OverseasStudent();
        overseasStudent.lunch();
    }
}

输出情况:

Person eats rice and vegetables
Person drinks water
Person eats rice and vegetables
Person drinks water
--------------------
OverseasStudent eats spaghetti
OverseasStudent drinks cold water
Person eats rice and vegetables
Person drinks water

解释代码:

在Java中,关键字this用于调用当前类中的方法或访问当前类的成员变量,而关键字super用于调用父类中的方法或访问父类的成员变量。

在lunch方法中,this.eat()和this.drink()调用的是当前类(OverseasStudent或Student)中重写的eat和drink方法。而super.eat()和super.drink()调用的是父类(Person)中的eat和drink方法。

所以,当OverseasStudent或Student调用lunch方法时,会先调用当前类中重写的方法,然后再调用父类中的方法。

在代码中,OverseasStudent类重写了Person类中的eat和drink方法。当OverseasStudent对象调用lunch方法时,首先会检查在OverseasStudent类中是否有eat和drink方法,如果有,则调用OverseasStudent类中的方法。因此,OverseasStudent对象调用lunch方法时会输出"OverseasStudent eats spaghetti"和"OverseasStudent drinks cold water"。

然后,OverseasStudent类中的lunch方法直接调用了父类Person中的eat和drink方法,使用super关键字。这会导致父类中的eat和drink方法被调用。因此,OverseasStudent对象调用lunch方法后会输出"Person eats rice and vegetables"和"Person drinks water"。

相反,localStudent对象是Student类的实例,没有重写Person类中的eat和drink方法。因此,当localStudent对象调用lunch方法时,首先会检查在Student类中是否有eat和drink方法,发现没有,然后会直接调用父类Person中的eat和drink方法。这就解释了为什么localStudent对象调用lunch方法后输出的结果与OverseasStudent对象不同,而与父类Person的输出结果相同。

方法重写(Method Overriding)是面向对象编程中的一个重要概念,它允许子类重新定义与父类中具有相同名称、参数列表和返回类型的方法,以实现自己的特定实现。方法重写是实现运行时多态性的关键。

重写方法的核心目的是允许子类提供自己特定的实现,以便更好地适应子类的行为和需求。
	在运行时,当子类对象调用被重写的方法时,会优先执行子类中的实现,而不是父类中的实现,
这实现了运行时多态性。这意味着父类引用指向子类对象时,根据对象的实际类型来决定调用哪个方法。

1.重写方法名称和形参列表必须一致:

当子类需要重写父类的方法时,必须确保重写的方法名称和形参列表与父类方法完全一致。这意味着子类中的方法名称和参数类型、参数个数以及参数顺序都必须与父类方法相同。这是为了确保子类能够正确地覆盖(重写)父类的方法,以便在运行时根据对象的实际类型调用正确的方法。

class Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("Animal is making a sound");
    }
}

class Dog extends Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("Dog is barking");
    }
}

在这个例子中,Dog 类继承了 Animal 类,并重写了 makeSound 方法。子类中的方法名称和形参列表与父类中的方法完全一致。

说明:

在这里插入图片描述

注意问题:

为什么Animal可以new一个Dog,而Dog不可以new一个Animal()
Animal ccc = new Dog();
ccc.makeSound();
Dog eee = new Animal();
eee.makeSound();

在Java中,一个类可以创建另一个类的实例,只要被创建的类是被创建类的子类。这是因为子类继承了父类的属性和方法,所以可以通过父类的引用来引用子类的对象。

在你的代码中,Dog是Animal的子类,所以可以使用Animal类的引用来创建一个Dog对象。
	这是因为Dog继承了Animal的属性和方法。
然而,Animal不是Dog的子类,所以不能使用Dog类的引用来创建一个Animal对象。
	这是因为Animal类可能没有Dog类特有的属性和方法。
所以,你可以使用Animal类的引用来创建一个Dog对象,
	但不能使用Dog类的引用来创建一个Animal对象。

不能将超类的引用赋值给子类:

在这里插入图片描述
如果你想要调用 Dog 类的 fetch() 方法,你需要先将 Animal 类型的对象转型为 Dog 类型,然后再调用 fetch() 方法。这可以通过使用强制类型转换来实现((Dog)ddd).fetch();

其他代码举例:
class Animal {
    
    
    public void eat() {
    
    
        System.out.println("Animal is eating.");
    }
}

class Dog extends Animal {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("Dog is eating.");
    }

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

public class Mains {
    
    
    public static void main(String[] args) {
    
    
        Animal animal = new Animal();
        animal.eat(); // 输出: Animal is eating.

        Dog dog = new Dog();
        dog.eat(); // 输出: Dog is eating.
        dog.bark(); // 输出: Dog is barking.

        Animal animalDog = new Dog(); // 使用父类引用指向子类对象
        animalDog.eat(); // 输出: Dog is eating.
        // animalDog.bark(); // 错误,Animal类型的引用不能访问子类特有的方法

        ((Dog) animalDog).bark(); // 使用强制类型转换调用子类特有的方法
    }
}

在Java中,不支持将超类(父类)的引用直接赋值给子类变量的原因涉及到继承和多态的概念。

Java中的继承是单向的,子类可以继承父类的属性和方法,但是反过来并不成立。这是因为子类可能引入新的属性和方法,而父类不具备这些特性。如果将超类引用直接赋值给子类变量,可能导致无法访问子类特有的属性和方法,从而破坏了类型安全性。

Java中的多态性是通过父类引用指向子类对象来实现的。这样做是为了允许在运行时动态地选择不同的子类实例,从而实现更灵活的编程。但是,由于类型兼容性的考虑,Java不允许直接将超类引用赋值给子类变量。

如果你希望将超类的引用转换为子类类型,你可以使用类型转换(Type Casting)。但是在进行类型转换时,需要注意对象的真实类型,避免在运行时发生类型转换异常(ClassCastException)。

例如:

Superclass superClassInstance = new Subclass();
Subclass subclassInstance = (Subclass) superClassInstance; // Type Casting

这样做需要确保superClassInstance实际上是Subclass的一个实例,否则将会抛出ClassCastException异常。因此,在进行类型转换时要小心谨慎。

当我们使用一个例子来说明为什么在Java中不允许将超类(父类)的引用赋值给子类变量时,假设我们有一个超类叫做Animal和一个继承自Animal的子类叫做Dog。这两个类都有一个名为makeSound()的方法。

class Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("一些通用的动物声音。");
    }
}

class Dog extends Animal {
    
    
    @Override
    public void makeSound() {
    
    
        System.out.println("汪汪!汪汪!");
    }

    public void fetch() {
    
    
        System.out.println("在找球玩耍。");
    }
}

现在,如果我们试图将一个Dog对象赋值给一个Animal引用,这是可以的,因为Dog也是一个Animal

Animal animal = new Dog(); // 这是有效的,因为Dog是Animal的子类

然而,如果我们尝试使用animal引用来访问fetch()方法,将会导致编译错误,因为animal引用是Animal类型,而fetch()Dog类特有的方法。

animal.fetch(); // 错误:无法为Animal类型找到fetch()方法

通过不允许这种类型的赋值,Java确保你只能访问在实际对象类型中确实存在的方法和属性。这有助于保持类型安全性,并避免运行时错误。

如果你需要访问fetch()方法或其他子类特有的功能,你可以进行类型转换为子类类型。但是,你必须确保所引用的对象确实是正确的子类类型,以避免运行时异常。

要访问子类特有的方法或其他功能,你可以使用类型转换将超类引用转换为子类类型。这样做可以让你在编译时将超类引用视为子类引用,从而可以调用子类特有的方法。

在Java中,类型转换可以通过将超类引用强制转换为子类类型来实现。这样,你就可以访问子类特有的方法。

class Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("一些通用的动物声音。");
    }
}

class Dog extends Animal {
    
    
    @Override
    public void makeSound() {
    
    
        System.out.println("汪汪!汪汪!");
    }

    public void fetch() {
    
    
        System.out.println("在找球玩耍。");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Animal animal = new Dog(); // 创建一个Dog对象,并用Animal引用引用它

        animal.makeSound(); // 调用的是Dog类的makeSound()方法,因为它是动态绑定的

        // 使用类型转换将Animal引用转换为Dog类型
        if (animal instanceof Dog) {
    
    
            Dog dog = (Dog) animal;
            dog.fetch(); // 现在我们可以访问Dog类特有的fetch()方法
        }
    }
}

在这个例子中,我们先用Animal引用animal引用了一个Dog对象。然后我们调用了animal.makeSound(),这将调用Dog类中的makeSound()方法,因为Java支持动态绑定(dynamic binding),会在运行时根据实际对象类型调用相应的方法。

接着,我们使用instanceof运算符检查animal引用是否是Dog类型的对象。如果是,我们就将animal引用进行类型转换为Dog类型,并将它赋值给dog引用。现在,我们可以通过dog引用调用fetch()方法,因为它是Dog类特有的方法。

需要注意的是,在进行类型转换时,要确保引用指向的对象实际上是目标子类类型,否则会在运行时抛出ClassCastException异常。因此,在进行类型转换前最好使用instanceof进行检查,以确保安全地进行类型转换。

2. 访问权限必须大于等于父类:

在子类中重写父类的方法时,子类中重写方法的访问修饰符可以比父类中的方法更大(更宽松),但不能比父类中的方法更小(更严格)。访问修饰符的顺序是:private < 默认(package-private) < protected < public。例如,如果父类方法是protected,那么子类中的重写方法可以是protectedpublic,但不能是default或private。这是为了确保子类不会限制对父类方法的访问权限,保证了继承关系的正确性。

 class Animal {
    
    
    protected void eat() {
    
    
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {
    
    
    public void eat() {
    
    
        System.out.println("Dog is eating");
    }
}

在这个例子中,Dog 类继承了 Animal 类,并将父类的 protected 方法改为了 public 方法。子类中的重写方法的访问权限大于等于父类方法的访问权限。

3. 返回值类型必须小于等于父类:

当子类重写父类的方法时,子类重写方法的返回值类型必须是父类方法返回值类型的子类型(协变),或者与父类方法返回值类型完全一致。这是为了确保子类的方法在类型上兼容于父类方法,使得子类对象可以作为父类对象使用。如果子类重写方法的返回值类型与父类方法不兼容,会导致编译错误。

class Animal {
    
    
    public Animal giveBirth() {
    
    
        return new Animal();
    }
}

class Dog extends Animal {
    
    
    public Dog giveBirth() {
    
    
        return new Dog();
    }
}

在这个例子中,Dog 类继承了 Animal 类,并重写了 giveBirth 方法。子类中的返回值类型是父类方法返回值类型的子类。

4. 保持方法签名一致:

在重写父类方法时,建议子类的重写方法尽量与父类方法保持一致,包括方法名称、参数列表和返回值类型。这样做有助于代码的可读性和维护性,使得代码更易于理解和修改。如果子类的重写方法与父类方法的方法签名不一致,虽然编译器不会报错,但会导致混淆和错误的发生。

class Animal {
    
    
    public void move() {
    
    
        System.out.println("Animal is moving");
    }
}

class Dog extends Animal {
    
    
    public void move(int distance) {
    
    
        System.out.println("Dog is moving " + distance + " meters");
    }
}

在这个例子中,Dog 类继承了 Animal 类,但重写的方法签名与父类方法不一致。这样的重写是不推荐的,因为它会导致混淆和错误的发生。

对比:

在这里插入图片描述
和有参数的:
在这里插入图片描述
在Java中,Animal ccc = new Dog();这行代码的含义是创建一个新的Dog对象,并将其引用赋值给一个类型为Animal的变量ccc。这是Java的多态性的一个表现。

在这种情况下,ccc是一个Animal类型的引用,但它实际上引用的是一个Dog对象。这意味着你可以调用Animal类定义的所有方法,以及Dog类重写的Animal类的方法。

例如,当你调用ccc.move(1);时,实际上调用的是Dog类中重写的move(int distance)方法,而不是Animal类中的move(int distance)方法。这是因为Dog类重写了Animal类的move(int distance)方法,所以当你通过一个Animal类型的引用调用move(int distance)方法时,实际上调用的是Dog类中的版本。

这就是为什么在你的代码中,((Animal) ccc).move(1);实际上输出的是"Dog is moving 1 meters",而不是"Animal is moving 1 meters"。因为ccc实际上引用的是一个Dog对象,所以调用的是Dog类中的move(int distance)方法。

5. 私有方法不能被重写:

私有方法只能在声明它们的类内部访问,因此无法在子类中访问,更别提重写了。私有方法也不能被重写,因为它们只在其所属类内部可见。如果在子类中定义了与父类私有方法相同的方法,那么实际上是在子类中创建了一个新的方法,而不是重写父类的方法。

class Animal {
    
    
    private void eat() {
    
    
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {
    
    
    public void eat() {
    
    
        System.out.println("Dog is eating");
    }
}

在这里插入图片描述

在这个例子中,Dog 类试图重写父类的私有方法 eat,但由于私有方法只能在声明它们的类内部访问,所以无法在子类中访问和重写。

6. 静态方法不能被重写:

static修饰的方法可以被继承,但是不能被重写

静态方法是与类相关而不是与实例相关的方法,因此子类不能重写父类的静态方法。但是,子类可以在自己的类中声明一个与父类静态方法同名的新方法,这被称为方法隐藏(method hiding)。方法隐藏与方法重写不同,它是在子类中创建一个新的静态方法,而不是重写父类的静态方法。

class Animal {
    
    
    public static void makeSound() {
    
    
        System.out.println("Animal is making a sound");
    }
}

class Dog extends Animal {
    
    
    public static void makeSound() {
    
    
        System.out.println("Dog is barking");
    }
}

在这个例子中,Dog 类试图重写父类的静态方法 makeSound,但由于静态方法是与类相关而不是与实例相关的,所以无法在子

详细:

在Java中,静态方法是与类关联的,而不是与类的实例关联的。当一个类定义了一个静态方法,它属于类本身,而不是类的实例。因此,静态方法是通过类名直接调用的,而不是通过类的实例调用的。

当子类定义了一个与父类相同名称和参数列表的静态方法时,
	实际上是在子类中创建了一个新的静态方法,而不是重写父类的静态方法。
在调用静态方法时,编译器会根据引用类型来确定要调用的方法。
	所以,无论是父类引用指向子类对象,还是子类引用指向父类对象,
调用的都是引用类型所属类的静态方法,而不是实际对象的类型。

由于静态方法是与类关联的,它们不能被子类重写。子类可以定义与父类相同名称和参数列表的静态方法,但实际上这是在子类中创建了一个新的静态方法,而不是重写父类的静态方法。

当通过类名调用静态方法时,编译器会根据引用类型来确定要调用的方法。无论是父类引用指向子类对象,还是子类引用指向父类对象,调用的都是引用类型所属类的静态方法,而不是实际对象的类型。

因此,由于静态方法是与类关联的,并且无法通过子类重写,所以在Java中不可以重写静态方法。

7. 异常:

重写方法不能抛出比父类方法更多的异常,但可以抛出更具体的异常或者不抛出异常。这是为了确保子类的重写方法不会引入新的异常,以兼容父类方法的异常处理。如果子类的重写方法抛出了比父类方法更多的异常,会导致编译错误。

class Animal {
    
    
    public void makeSound() throws IOException {
    
    
        // code that may throw IOException
    }
}

class Dog extends Animal {
    
    
    public void makeSound() throws FileNotFoundException {
    
    
        // code that may throw FileNotFoundException
    }
}

在这个例子中,Dog 类继承了 Animal 类,并重写了 makeSound 方法。子类中的重写方法可以抛出更具体的异常(FileNotFoundException),或者不抛出异常。但不能抛出比父类方法更多的异常。

8. 使用 @Override:

在Java中,可以使用 @Override 注解来标记重写的方法。这个注解可以帮助编译器检查是否满足重写的条件,如果不满足,编译器会报错。使用 @Override 注解可以提高代码的可读性和可维护性,明确地表示该方法是重写父类的方法。

class Animal {
    
    
    public void move() {
    
    
        System.out.println("Animal is moving");
    }
}

class Dog extends Animal {
    
    
    @Override
    public void move() {
    
    
        System.out.println("Dog is running");
    }
}

在这个例子中,Dog 类使用 @Override 注解标记了重写的方法。这样可以帮助编译器检查是否满足重写的条件,如果不满足,编译器会报错。使用 @Override 注解可以提高代码的可读性和可维护性,明确地表示该方法是重写父类的方法。

9. Super关键字:

在子类中,可以使用 super 关键字来调用父类的方法。通过使用 super 关键字,子类可以在重写父类方法的同时扩展父类方法的功能。子类可以在重写方法中先调用父类的方法,然后再添加自己的逻辑。这样可以保留父类方法的原有功能,并在此基础上进行扩展。

class Animal {
    
    
    public void eat() {
    
    
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {
    
    
    public void eat() {
    
    
        super.eat(); // 调用父类的eat方法
        System.out.println("Dog is eating bones");
    }
}

在这个例子中,Dog 类重写了父类的 eat 方法,并使用 super 关键字调用了父类的 eat 方法。这样可以在子类中扩展父类方法的功能,保留了父类方法的原有功能,并在此基础上添加了新的逻辑。
在这里插入图片描述

10. final方法:

如果父类中的方法被声明为 final,那么该方法不能被子类重写。final 方法是无法被修改的最终方法,它的实现在父类中是最终的,子类无法对其进行修改或重写。这是为了确保父类中的某个方法在子类中保持不变,不被修改。

class Animal {
    
    
    public final void eat() {
    
    
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {
    
    
    public void eat() {
    
    
        System.out.println("Dog is eating");
    }
}

在这个例子中,Animal 类中的 eat 方法被声明为 final,表示该方法是最终的,不能被子类重写。因此,Dog 类中的 eat

11. 目的和多态性:

重写方法的主要目的是在子类中提供特定于子类的实现,实现多态性和继承特性。通过重写父类的方法,子类可以根据自己的需求来实现特定的功能,而不需要重新编写整个方法。这样可以提高代码的复用性和可维护性,同时也实现了多态性,即通过父类引用指向子类对象,并根据实际对象的类型来调用相应的方法。

class Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("Animal is making a sound");
    }
}

class Dog extends Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("Dog is barking");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Animal animal = new Animal();
        Animal dog = new Dog();

        animal.makeSound(); // 输出:Animal is making a sound
        dog.makeSound(); // 输出:Dog is barking
    }
}

在这个例子中,Animal 类和 Dog 类分别定义了 makeSound 方法。通过创建 Animal 类和 Dog 类的对象,并调用它们的 makeSound 方法,可以实现多态性。在运行时,根据对象的实际类型来调用相应的方法,即使使用父类的引用指向子类的对象,仍然可以调用子类重写的方法。

以上是关于重写方法的详细说明,每一点都是为了确保子类正确地重写父类的方法,并实现多态性和继承特性。这些规则和注意事项在Java中非常重要,需要在实际编程中加以遵守。

class Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    
    
    @Override
    public void makeSound() {
    
    
        System.out.println("Dog barks");
    }
}

在这个例子中,Dog 类继承自 Animal 类,并重写了 makeSound() 方法。当调用 makeSound() 方法时,如果是 Animal 对象,则输出 “Animal makes a sound”;如果是 Dog 对象,则输出 “Dog barks”。这是因为在运行时,Java 会动态地选择调用对象类型对应的方法。

使用方法重写时要注意遵循上述规则,确保方法重写在继承关系中能够正确地覆盖父类的方法,并且能够符合子类的需求。

方法重写(Override)的本质是子类提供了与父类相同签名的方法,以实现不同的功能。
	在运行时,JVM通过虚方法表(Virtual Method Table)来确定调用哪个方法,这就是动态绑定或者后期绑定。

当我们创建一个子类对象并调用一个方法时,JVM会首先在子类的虚方法表中查找该方法。
	如果找到,就执行该方法;如果没有找到,就在父类的虚方法表中查找。这就是为什么子类可以重写父类的方法的原因。

当子类重写父类的方法时,子类的虚方法表中的对应条目会被更新为指向子类的方法,而不是父类的方法。
	这就是所谓的“覆盖虚方法表中的方法”。

子类构造器:

在Java中,子类的构造器通常会调用父类的构造器,以确保父类的所有字段都被正确地初始化。这是通过使用super关键字来实现的。以下是一个例子:

public class Animal {
    
    
    private String name;

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

public class Dog extends Animal {
    
    
    private String breed;

    public Dog(String name, String breed) {
    
    
        super(name);  // 调用父类的构造器
        this.breed = breed;
    }
}

在这个例子中,Dog类是Animal类的子类。Dog类的构造器首先调用super(name)来调用父类Animal的构造器,然后初始化自己的breed字段。

在Java中,你可以通过继承来创建具有继承结构的JavaBean。

你的要求是创建一个JavaBean,它需要满足以下条件:

1. 类名见名知意
2. 所有的成员变量都需要私有
3. 构造方法(空参和带全部参数的构造)
4. get/set方法

首先,我们创建一个基础的JavaBean,称为Person:

public class Person {
    
    
    private String name;
    private int age;

    public Person() {
    
    
    }

    public Person(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;
    }
}

然后,我们创建一个继承自Person的Student类:

public class Student extends Person {
    
    
    private String school;

    public Student() {
    
    
    }

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

    public String getSchool() {
    
    
        return school;
    }

    public void setSchool(String school) {
    
    
        this.school = school;
    }
}

在这个例子中,Student类继承了Person类的属性和方法,并添加了自己的属性school。这就是如何创建带有继承结构的JavaBean。

猜你喜欢

转载自blog.csdn.net/m0_74154295/article/details/132049321
今日推荐