Java多态

x33g5p2x  于2022-05-05 转载在 Java  
字(4.5k)|赞(0)|评价(0)|浏览(225)

一.多态的概述

1.面向对象三大特征:封装性、继承性、多态性,extends继承和implements实现是多态的前提

2.多态现实意义理解:

  • 现实事物往往会体现多种形态,如小明即是人,也是一个学生,小明拥有两种形态,这就是多态
  • Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便是Student,又是person

3.多态体现为父类引用变量可以指向子类对象

4.前提条件:必须有子父类关系

二.多态的格式与使用

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

或者

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

访问成员方法:new谁就优先用谁

访问成员变量的两种方式:
1.直接通过对象名称访问成员变量:看等号左边是谁优先用谁,没有则往上找
2.间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则往上找
访问成员方法规则口诀:编译看左边,运行看右边
访问成员变量规则口诀:编译看左边,运行看左边

好处:无论右边new的时候换成哪个子类对象,等号左边调用的方法都不会变化

三.使用多态的好处

如果不用多态,只用子类,那么写法是:

Teacher one = new Teacher();
one.work();//讲课
Assitant two new Assistant();
two.work();//辅导

现在要唯一要做的事情,就是调用work方法,其他的功能不关心
如果使用多态的写法,对比一下:

Employee one = new Teacher();
one.work();//讲课
Employee two = new Assitant();
two.work();//辅导

好处:我们不需要关心子类的具体方法,他们肯定会继承父类的work()方法,无论new的时候换成哪个子类对象,等号左边调用的方法都不会变,基于运行看右边原则,到时候调用的依旧是子类覆盖重写父类的方法;会是代码更加灵活

在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法

四.向上转型和向下转型

1.向上转型(其实就是多态的写法)
格式:父类名称 对象名 = new 子类名称();
含义:右侧创建一个对象,把它当做父类来看待使用
注意事项:向上转型一定是安全的

类似于:double num = 100;//正确,int–>double,自动类型转换
例如:

Animal animal = new Cat();
创建了一只猫,当做动物看点,没问题

弊端:一旦向上转型为父类,就无法调用子类特有的方法(比如将猫向上转型为动物,如果猫特有吃鱼的方法,那么此方法不能调用,因为不是所有的动物都吃鱼)

2.向下转型(其实就是还原向上转型)
格式:子类名称 对象名 = (子类名称) 父类对象
含义:将父类对象,【还原】称为本来的子类对象

Animal animal = new Cat();//本来是猫,向上转型成为动物
Cat cat = (Cat) Animal;//本来是猫,已经被当作动物了,还原回来成为本来的猫
注意事项:
A.必须保证对象本来创建的时候就是猫,才能向下转型成为猫
B.对象如果原本不是猫,现在非要向下转型成为猫,就会报错(ClassCastException)
类似于: 
int num = (int)10.0//可以     
int num = (int)10.5//不可以,精度损失

如何才能知道一个父类引用的对象,本来是什么子类?
格式:对象 instanceof 类名称
会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例

扩展:

抽象类名作为形参和返回值

  • 方法的形参是抽象类名,其实需要的是该抽象类的子类对象
  • 方法的返回值是抽象类名,其实返回的是该抽象类的子类对象
// Animal.java
// 抽象类
public abstract class Animal {
    public abstract void eat();
}

// AnimalOperator.java
public class AnimalOperator {
    public void useAnimal(Animal a){
        a.eat();
    }
    public Animal getAnimal(){
        Animal a = new Cat();
        return a;
    }
}

// Cat.java
// 抽象类的子类
public class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

// AnimalDemo.java
public class AnimalDemo {
    public static void main(String[] args) {
        AnimalOperator ao = new AnimalOperator();
        Animal a  = new Cat();
        ao.useAnimal(a);

        Animal a2 = ao.getAnimal();
        a2.eat(); // Animal a = new Cat();编译看左边,执行看右边
    }
}

接口名作为形参和返回值

  • 方法的形参是接口名,其实需要的是该接口的实现类对象
  • 方法的返回值是接口名,其实返回的是该接口的实现类对象
// Jumpping.java
// 接口
public interface Jumpping {
    void jump(); // 省略了public abstract
}

// JumppingOperator.java
public class JumppingOperator {
    public void useJumpping(Jumpping j){
        j.jump();
    }
    public Jumpping getJumpping(){
        Jumpping j = new Cat();
        return j;
    }
}

// Cat.java
// 接口实现类
public class Cat implements Jumpping {
    @Override
    public void jump() {
        System.out.println("猫可以跳高了");
    }
}

// JumppingDemo.java
public class JumppingDemo {
    public static void main(String[] args) {
        JumppingOperator jo = new JumppingOperator();
        Jumpping j = new Cat();
        jo.useJumpping(j);

        Jumpping j2 = jo.getJumpping(); // Jumpping j2 = new Cat();
        j2.jump();
    }
}

内部类

  • 内部类可以直接访问外部类的成员,包括私有
  • 外部类要访问内部类的成员,必须创建对象
public class Outer {

    private int num =10; //内部类可以直接访问外部类的成员,包括私有

    public class Inner{  
        public void show(){
            System.out.println(num);
        }
    }

    public void method(){
//        show();  // 报错
        Inner i = new Inner();
        i.show();
    }
}

成员内部类

// Outer.java
public class Outer {
    private int num =10;
    public class Inner{
        public void show(){
            System.out.println(num);
        }
    }
}

//InnerDemo.java
public class InnerDemo {
    public static void main(String[] args) {
//        Inner i = new Inner(); //报错
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}

一般写法如下:

// Outer.java
public class Outer {

    private int num =10;

    private class Inner{  // 内部类设置为私有
        public void show(){
            System.out.println(num);
        }
    }

    public void method(){
        Inner i = new Inner();
        i.show();
    }

}

// InnerDemo.java
public class InnerDemo {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

局部内部类

局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用。该类可以直接访问外部类的成员,也可以访问方法内的局部变量。

// Outer.java
public class Outer {
    private int num =10;
    public void method(){
        int num2 = 20;
        class Inner{
            public void show(){
                System.out.println(num);
                System.out.println(num2);
            }
        }
        Inner i = new Inner();
        i.show();
    }
}

// OuterDemo.java
public class OuterDemo {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

匿名内部类

本质:是一个继承了该类或者实现了该接口的子类匿名对象。匿名内部类是局部类

// Inter.java
// 接口
public interface Inter {
    void show();
}

// Outer.java
public class Outer {
    public void method(){
        Inter i = new Inter(){
            @Override
            public void show() {
                System.out.println("匿名内部类");
            }
        };
        i.show(); // 编译看左边Inter,执行看右边的匿名内部类(重写了show方法)
    }
}

// OuterDemo.java
public class OuterDemo {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

匿名内部类在开发中的使用

// Jumpping.java
public interface Jumpping {
    void jump(); // 省略了public abstract
}

// JumppingOperator.java
public class JumppingOperator {
    public void method(Jumpping j){
        j.jump();
    }
}

// JumppingDemo.java
public class JumppingDemo {
    public static void main(String[] args) {
        JumppingOperator jo = new JumppingOperator();

        jo.method(new Jumpping() {
            @Override
            public void jump() {
                System.out.println("猫可以跳高了");
            }
        });
    }
}

相关文章