多态,向上转型和向下转型以及final关键字

本文详细介绍了Java中的多态概念,包括什么是多态、如何使用多态,以及多态中成员方法和变量的访问特点。接着讨论了向上转型,解释了其安全性和缺点,以及如何通过向下转型和instanceof来弥补。最后,探讨了final关键字的使用,包括final修饰类、方法、局部变量和成员变量的含义及限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.多态

1.什么是多态?

(继承是多态的前提)

多态通俗来讲就是各种形态,具体来讲就是去完成某个行为,当不同的对象去完成时会产生不同的形态。

在代码层面来说就是子类的每个对象也是父类的对象,例如:每个经理都是一个员工,但反之,并不是每位员工都是经理,这就是继承中的“is——a”规则,它的另外一种表述则是替换规则,就i是说程序中出现父类对象的任何地方都可以使用子类对象来替换。

2.如何使用多态呢?

代码中体现的多态性其实就是父类引用指向子类对象

一般格式有两种:

1. 父类名称  对象名 =  new  子类名称( ); 

2.接口名称 对象名  = new 实现类名称( );

多态中的成员方法和成员变量的访问特点

public class People {
     String name;
     int age;

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

    public void eat(){
        System.out.println(age+"岁的"+name+"正在吃饭!");
    }
}
public class Student extends People{
    //student类继承people类
    public Student(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(age+"岁的"+name+"正在吃学生餐!");
    }
}
public class Main {
    public static void eat(People a){
        //用一个People类的变量a来接收变量,我们并不清楚变量a是一个什么类型
        a.eat();
    }
    public static void main(String[] args) {
        People people = new People("张三",38);
        Student student = new Student("李四",15);
        People student2 = new Student("王五",18);
        eat(people);
        eat(student);
        System.out.println(student2.age);
        System.out.println(student2.name);
    }
}

运行结果为:

在上段代码中我们发现,在Java中要实现多态,必须要满足如下几个条件,缺一不可:

1.必须要在继承体系下;

2.子类必须要对父类中的方法进行重写;

3.通过父类的引用调用重写的方法;

在多态中成员方法的访问特点是:

new的是谁就优先调用谁

在多态中成员变量的访问特点是:
1.直接通过对象名来访问成员变量(看等号左边是谁则优先用谁,没有则向上找);

2.间接通过成员方法来访问成员变量(看该方法属于谁则优先用谁,没有则向上找);

事实上,多态写法是一种向上转型操作。

向上转型

向上转型的含义就是创建一个子类对象,把它当作父类来看待使用。写法就是多态的写法。

People student2 = new Student("王五",18);

 在这段代码中,student2是一个父类类型,但可以引用一个子类对象,因为:子类对象是一个父类对象,即可以将一个子类对象当作一个父类对象来使用。因此:向上转型是安全的,因为是从小范围向大范围的转换。

但是向上转型也是有缺点的:

public class Student extends People{
    //student类继承people类
    public Student(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(age+"岁的"+name+"正在吃学生餐!");
    }
    public void sleep(){
        System.out.println(name+"学生上课睡觉");
    }
}
​
public class Main {
    public static void eat(People a){
        //用一个People类的变量a来接收变量,我们并不清楚变量a是一个什么类型
        a.eat();
    }
    public static void main(String[] args) {
        People people = new People("张三",38);
        Student student = new Student("李四",15);
        People student2 = new Student("王五",18);
       student.sleep();
       student2.sleep();
    }
}

​

 我们发现代码报错。

向上转型一旦转型为父类,那么就无法调用子类原有的特有内容。

这种情况该如何解决呢?用对象的向下转型来还原。

向下转型

对象的向下转型其实就是一个还原的动作。

格式:

子类名称 对象名称 = (子类名称)父类对象;

含义是:将父类对象,还原成本来的子类对象

括号里的子类名称应该是其后面父类对象原本指向的类,如果不是则运行时会抛异常。

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛出异常。java中为了提高向下转型的安全性,引入了instanceof,如果表达式为true,则可以安全转换。

格式:

对象名 instanceof 类名称

该表达式的返回值类型为Boolean,也就是判断前面的对象能不能当作后面类型的实例。

例如:

public class Main {
    public static void eat(People a){
        //用一个People类的变量a来接收变量,我们并不清楚变量a是一个什么类型
        a.eat();
    }
    public static void main(String[] args) {
        People people = new People("张三",38);
        Student student = new Student("李四",15);
        People student2 = new Student("王五",18);
       student.sleep();
       if(student2 instanceof Student){
           //使用instanof对向下转型操作进行合法性校验
           Student student1 = (Student) student2;
           student1.sleep();
       }
    }

运行结果:

我们看到这样就可以解决向上转型不能调用到子类特有的方法这一问题。在向下转型的合法性校验中我们将student2还原为了Student类对象student1,并且通过它调用了Student类的特有方法sleep()。

final关键字

 final在英语中是最终的意思

final作为关键字可以用来

1.修饰一个类;

2.修饰一个方法;

3.修饰一个局部变量;

4.修饰一个成员变量;

1.当final修饰一个类的时候

格式:

public final class 类名称{

...............

}

当使用final修饰一个类的时候,含义就是这个类不能有任何子类(我们可以称其为太监类)

不能使用final修饰的类来作为一个父类,因为它是太监类,就不能被继承。

2.当final修饰一个方法的时候,这个方法就不能被覆盖重写。

注意:在java中我们要求被abstract修饰的方法必须被覆盖重写,而final关键字则与之相反,所以abstract和final关键字二者不能同时使用。

3.一旦使用final修饰局部变量,那么这个变量就不能被修改,也就是说“一次赋值,终生不变”,而且只能赋值一次,对于基本数据类型来说,不可变说的是变量当中的数据不可变;对于引用数据类型来说,不可变说的是变量当中的地址值不可变,但类中的变量仍然可以被修改。

4.对于成员变量来说,如果用final关键字对其修饰,则其变量照样不可变。由于成员变量具有默认值,所以使用了final之后必须对其手动赋值,不会再给出默认值了

此时我们有两种方法对其进行赋值

1.使用直接赋值

public class Student extends People{
    final int classroom = 1;
    //直接赋值
    //student类继承people类
    public Student(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(age+"岁的"+name+"正在吃学生餐!");
    }
    public void sleep(){
        System.out.println(name+"学生上课睡觉");
    }
}

2.通过构造方法赋值

public class Student extends People{
    final int classroom ;
    //直接赋值
    //student类继承people类
    public Student(String name, int age) {
        super(name, age);
        classroom = 1;
        //在构造方法中对其赋值
    }
    @Override
    public void eat() {
        System.out.println(age+"岁的"+name+"正在吃学生餐!");
    }
    public void sleep(){
        System.out.println(name+"学生上课睡觉");
    }
}

需要注意的是:

必须保证类当中的所有重载构造方法都最终会对final修饰的变量进行赋值操作!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值