适用于有其他编程语言基础的Java小白零基础教程(三)

包括:继承、spuer关键字、sealed类、多态、方法覆写、final关键字等

输出结果都写在了注释中

1. 继承(Inheritance)

1.1 基本概念

子类(Subclass) :扩展类(Extended Class)

父类(Superclass) :基类(Base Class)或超类(Super Class)

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

// 继承使用关键字extends
class Student extends Person {
	// 不要重复name和age字段/方法,
    // 只需要定义新增score字段/方法:
    private int score;
	
	// 父类的private字段对子类不可见
	public String hello(){
        //return "Hello," + this.name;  编译错误:无法访问name字段
    }
}

1.2 protected

若希望子类能访问父类字段,可将字段声明为protected

public class demo19 {
    public static void main(String[] args){
        Student s = new Student();
        s.setName("Xiaoming");
        System.out.println(s.hello());
		// Hello,Xiaoming Xiaoming Xiaoming
    }
}

class Person{
    protected String name;
    protected int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    } 
}

class Student extends Person{
    protected int score;

    public String hello(){
		// super用于引用父类的字段、方法或构造器
		// this.name 和 super.name 效果一样
		// 但在重写方法时,如果想明确表示调用父类方法,就必须使用super
        return "Hello," + name +" " + super.name +" " + this.name;
    }
}

protected修饰的成员可以被:本类访问、同包类访问、子类访问(不管是否同包)


1.3 继承树

Java中每个类最终都会继承自Object类:

Object
  ↑
Person
  ↑
Student

如果定义Teacher也是继承自Person

    Object
      ↑
    Person
     ↑   ↑
Student  Teacher

1.4 super关键字

public class demo20 {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 12, 89);
    }
}

class Person {
    protected String name;
    protected int age;

    //如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Student extends Person {
    protected int score;
    // 任何class的构造方法,第一行语句必须是调用父类的构造方法
    // 如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();
    public Student(String name, int age, int score) {
        // 如果父类没有无参构造器,子类必须显式调用一个带参数的父类构造器,否则编译报错
        // super(); 自动调用父类的构造方法,编译错误
        super(name, age); // 调用父类的有参构造方法
        this.score = score;
    }
}

1.5 sealed类

sealed 是一种新引入的类修饰符(从 Java 15 开始作为预览功能,Java 17 起正式支持),它可以限制哪些类可以继承某个类,防止类被滥用继承,适用于框架开发和复杂体系结构。

使用规则
  1. 使用关键字 sealed 修饰父类。

  2. 使用 permits 指定允许继承的子类。

  3. 被允许继承的子类,必须用以下之一修饰:

    • final:子类不能再被继承。
    • sealed:子类继续限制继承。
    • non-sealed:子类继承自由,不再受限制。

如果子类没有指定这些修饰符,将会编译错误。

定义sealed父类
// Shape是一个sealed类,只允许Circle和Rectangle继承它
public sealed class Shape permits Circle, Rectangle {
    // 父类代码
}
定义允许的子类
public final class Circle extends Shape {
    // Circle被声明为final,不能再被继承
}

public sealed class Rectangle extends Shape permits FilledRectangle, EmptyRectangle {
    // Rectangle仍是sealed,继续控制自己的子类
}

public final class FilledRectangle extends Rectangle {
    // 终结继承链
}

public non-sealed class EmptyRectangle extends Rectangle {
    // EmptyRectangle是non-sealed,任何类可以继承它	
}
继承树
Shape (sealed)
 ├── Circle (final)
 └── Rectangle (sealed)
        ├── FilledRectangle (final)
        └── EmptyRectangle (non-sealed)
                └── 任何子类...
特性总结
特性说明
sealed限制可继承子类,必须列出允许的子类列表
final子类不能再被继承
non-sealed子类可以被自由继承,不再受sealed控制
关键字permits指定允许继承的类,写在类定义后面
sealed接口(Java 17起)
public sealed interface Animal permits Dog, Cat {}

public final class Dog implements Animal {}

public final class Cat implements Animal {}

1.6 向上转型和向下转型

向上转型(Upcasting)

向上转型实际上是把一个子类型安全地变为更加抽象的父类型,所以,可以把Student类型转型为Person,或者更高层次的Object

Student s = new Student();
Person p = s; // 子类对象赋值给父类引用,总是安全的
Object o = p; // 继续向上
向下转型(Downcasting)
Person p = new Student();
Student s = (Student) p; // 父类引用强制转为子类引用

Person p1 = new Person();
// 如果父类引用指向的不是子类对象,强制转型将抛出ClassCastException
Student s1 = (Student) p1; // 编译错误
instanceof

instanceof用于判断一个变量所指向的实例是否是指定类型,或者这个类型的子类。如果一个引用变量为null,那么对任何instanceof的判断都为false

Person p = new Student();
if (p instanceof Student) {
    Student s = (Student) p;
}

Java 14起支持简化写法:

Person p = new Student();
if (p instanceof Student s) {
    // 直接使用s
}

1.7 继承与组合

继承(is关系) :子类是父类的一种。

示例:Student 是 Person

组合(has关系) :一个类拥有另一个类的实例。

示例:Student 有一本 Book

class Book {
    protected String name;
    public String getName() {...}
    public void setName(String name) {...}
}

// 错误
class Student extends Book { 
	protected int score;
}

// 正确
class Student extends Person {	
    protected Book book;
    protected int score;
}


1.8 访问修饰符的访问范围总结

修饰符同一类中同一包中不同包的子类中不同包的非子类中
public
protected×
default(无修饰符)××
private×××

public:权限最大,任何地方都可以访问。

protected:主要用于继承,允许在同一包内和不同包的子类中访问。

(default) (包私有):只允许在同一个包内访问。

private:权限最小,只能在声明它的类内部访问。


2. 多态(Polymorphism)

2.1 方法覆写(Override)

子类重新定义一个与父类方法签名完全相同的方法,称为方法覆写。

方法签名包括方法名参数列表,例如:

public int calculateSum(int a, int b) {
    return a + b;
}

calculateSum(int a, int b)是方法的签名。它包括方法名calculateSum和参数列表(int a, int b)

class Person {
    public void run() {
        System.out.println("Person.run");
    }
}

class Student extends Person {
	// 加上@Override注解,可以让编译器帮助检查是否正确覆写,避免笔误
    @Override
	// 返回类型也必须一致
    public void run() {
        System.out.println("Student.run");
    }
	
	// 希望进行覆写,但是不小心写错了方法签名,编译器会报错
	 public void run(String s) {	//编译错误
	}
}

方法重载(Overload)与方法覆写(Override)的区别:

特性重载 (Overload)覆写 (Override)
发生位置同一个类或子类中父类与子类之间
方法名相同相同
参数列表不同(参数数量或类型不同)完全相同
返回类型可以不同通常必须相同(或子类型)
关键注解无需 @Override推荐加上 @Override

2.2 多态

多态指:在运行时,方法调用执行的是实际对象的版本,而不是变量声明类型的方法,也可以说是同一个行为在不同对象上有不同的实现方式。

方法调用在运行期动态决定。

引用类型不决定调用的方法,实际对象类型才决定调用哪个方法。

public class demo21 {
    public static void main(String[] args){
        Person p = new Student();
        p.run();	// // 调用的是Student的run(),不是Person的run()
        // Student run.
    }
}

class Person{
    public void run(){
        System.out.println("Person.run.");
    }
}

class Student extends Person{
    @Override
    public void run(){
        System.out.println("Student run.");
    }
}
编译时多态(静态多态)

通过方法重载实现:

public class Demo {
    public void print(String s) {
        System.out.println("字符串:" + s);
    }

    public void print(int i) {
        System.out.println("整数:" + i);
    }
}

特点:方法名相同,参数不同(类型或个数),在编译时确定调用哪个方法。

运行时多态(动态多态)

通过方法覆写 + 父类引用指向子类对象实现:

class Animal {
    public void makeSound() {
        System.out.println("动物叫");
    }
}

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

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

public class Test {
    public static void main(String[] args) {
        Animal a1 = new Dog();
        Animal a2 = new Cat();

        a1.makeSound(); // 输出:汪汪
        a2.makeSound(); // 输出:喵喵
    }
}

2.3 super调用父类方法

在子类覆写的方法中,可以通过super调用父类原有的方法

class Person {
    protected String name;
    public String hello() {
        return "Hello, " + name;
    }
}

class Student extends Person {
    @Override
    public String hello() {
        return super.hello() + "!";
    }
}

2.4 final关键字

方法中

继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override

class Person {
    protected String name;
    public final String hello() {
        return "Hello, " + name;
    }
}

class Student extends Person {
    // 编译错误: 不允许覆写
    @Override
    public String hello() {
    }
}
类中

如果一个类不希望任何其他类继承自它,那么可以把这个类本身标记为final。用final修饰的类不能被继承。

final class Person {
    protected String name;
}

// 编译错误: 不允许继承自Person
class Student extends Person {
}
字段中

对于一个类的实例字段,同样可以用final修饰。用final修饰的字段在初始化后不能被修改。

class Person {
    public final String name = "Unamed";
}
Person p = new Person();
p.name = "New Name"; // 编译错误!

可以在构造方法中初始化final字段,可以保证实例一旦创建,其final字段就不可修改。

class Person {
    public final String name;
    public Person(String name) {
        this.name = name;
    }
}

2.5 覆写Object的方法

所有类默认继承自Object,可以覆写其方法以增强功能:

toString():自定义对象字符串输出。

equals(Object):自定义对象逻辑比较。

hashCode():自定义对象哈希值。

public class demo22 {
    public static void main(String[] args){
        Person p = new Person("Xiaoming",18);
        Person p1 = new Person("Xiaoming",18);
        System.out.println(p);  
        // Exp02.Person@314c508a
        // 几乎对阅读没用,所以我们常常覆写它

        p.toString();
        // Person{name='Xiaoming', age=18}
        // 调试和打印对象时更友好

        System.out.println(p == p1);
        // false
        System.out.println(p.equals(p1));
        // true
    }
}

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

    protected int age;
    protected String name;

    @Override
    public String toString(){
        return "Person{name='" + name + "', age=" + age + "}";
    }

    @Override
    public boolean equals(Object o){
        // 当且仅当o为Person类型
        if(o instanceof Person){
            Person p = (Person) o;
            return age == p.age&&name.equals(p.name);
        }
        return false;
    }

    @Override
    public int hashCode(){
        return this.name.hashCode();
    }
}

廖雪峰的学习网站

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值