(Java)多态与内部类 + package与import关键字

一、多态

       Java中的多态我们可以通过字面上的意思去理解,多态顾名思义就是多种形态,什么多种形态呢?比如我们的水就有固态、液态、气态三种形态,这就是一种多态,那我们是不是可以这样去理解,多态就是同一个物种的多种形态,那么在Java中是如何去表示的呢?通过之前的笔记我们可以知道继承的相关要点,我们的狗(dog)这个类就继承自动物类(animal),那么在Java中我们可以用狗来表示它自己,也可以向上造型animal来引用指向子类的对象,具体如何去做我们看下面。

      总之我们只需要知道多态是指多种形态就好了,有两种方式去做:

1、向上造型/自动类型转换

        就是说我们用超类引用指向子类对象,那么怎么做呢?如下代码:

Animal o1 = new Dog("小黑",2,"黑");

       可以看到在上面代码,我们首先声明了一个Animal引用类型的值为o1,并且把o1进行new(实例化)对象为dog类型,那么我们就可以 认为这就是一种多态,我们的dog类向上造型为了animal,但是我们的o1这个变量可以访问到声明方法呢,就看Animal类有什么,o1就能用什么,这是我需要注意的,即我们能点出来(o1.方法),看我们的引用类型(animal这里是)--记住啦!

2、向下转型/强制类型转换 

      这有什么用呢?我们从上面是不是发现了,我们向上造型只能访问到超类的方法,那我们想访问或者用到dog独有的方法(animal没有)怎么做呢?这种情况我们就用到了向下转型,代码如下:

//Animal o = new Animal(); //编译错误,抽象类不能被实例化
        Animal[] animals = new Animal[5];
        animals[0] = new Dog("小黑",2,"黑"); //向上造型

        if(animals[0] instanceof Dog){
                Dog dog = (Dog)animals[i];
                dog.lookHome();
            }

     那么instanceof这个关键字有什么用呢?在说之前我们需要知道向下转型的条件:

     条件有两个:

1、引用所指向的对象,就是该类型

2、引用所指向的对象,实现了该接口或继承了该类

     强转时若不符合如上条件,则发生ClassCastException类型转换异常

建议:在强转之前先通过 instanceof 来判断引用的对象是否是该类型
注意: instanceof 返回 boolean 结果,它为 true 的条件就是强转成功的条件
何时需要强转:若想访问的属性 / 行为在超类中没有,则需要强制类型转换

 所以我们通过instanceof关键字先进行判断,如果可以符合条件再强制转换以避免异常,这样我们就可以进行强制转换来访问我们的dog类中特有的方法,这就是我们多态的向下转型。

那我们是不是就又有疑问啦,既然我们向上造型的多态不能访问我们子类的方法,我们统一用子类引用去实例化子类对象不就好了,为什么多此一举呢?实际上我们多态是为了减少代码的重复性,从前面的例子(没有学习多态之前),我们可以知道当我们想创建多个dog,fish,chick类对象,并且访问其方法的时候,我们需要依次的去调用,有多少个对象与方法,就应该有多少调用方法,那么你就会发现会有大量的重复代码比如:dog1.eat();dog2.eat();dog3.eat();fish1.eat();chick1.eat();chick2.eat()........方法,这么相似的方法为什么不可以用一个for循环去解决呢?在我们没有学多态之前我们是不可以用for的,但是学了之后我们就可以用到for循环了代码如下:

 Animal[] animals = new Animal[5];
        animals[0] = new Dog("小黑",2,"黑"); //向上造型
        animals[1] = new Dog("小白",1,"白");
        animals[2] = new Fish("小金",1,"金");
        animals[3] = new Fish("小花",2,"花");
        animals[4] = new Chick("小灰",3,"灰");
        for(int i=0;i<animals.length;i++){ //遍历所有动物
            System.out.println(animals[i].name); //输出每个动物的名字
            animals[i].eat();   //每个动物吃饭
            animals[i].drink(); //每个动物喝水

            if(animals[i] instanceof Dog){
                Dog dog = (Dog)animals[i];
                dog.lookHome();
            }
            if(animals[i] instanceof Chick){
                Chick chick = (Chick)animals[i];
                chick.layEggs();
            }
            if(animals[i] instanceof Swim){ //适用于所有实现Swim接口的(会游泳的)
                Swim s = (Swim)animals[i];
                s.swim();
            }
        }

输出结果如下:

通过上面代码我们可以看到,我们用向上造型来创建了诸多对象 ,并且通过共有的超类方法来做for循环以此来减少代码的重复性,提高复用性!并且我们还可以通过向下转型的方式来访问子类特有的方法,因此我们可以看到,多态的存在是十分有必要的,与继承,封装一起构成了Java最重要的三大特性。

整体的代码如下:定义一个抽象的animal类,其下有dog,fish,chick类去继承,已经有个Test去做测试。

public abstract class Animal {
    String name;
    int age;
    String color;
    Animal(){
    }
    Animal(String name,int age,String color){
        this.name = name;
        this.age = age;
        this.color = color;
    }

    void drink(){
        System.out.println(color+"色的"+age+"岁的"+name+"正在喝水...");
    }
    abstract void eat();
}
public interface Swim {
    /** 游泳 */
    void swim();
}
public class Dog extends Animal implements Swim {
    Dog(){ 
    }
    Dog(String name,int age,String color){
        super(name,age,color);
    }

    void lookHome(){
        System.out.println(color+"色的"+age+"岁的狗狗"+name+"正在看家...");
    }
    void eat(){
        System.out.println(color+"色的"+age+"岁的狗狗"+name+"正在吃肯头...");
    }
    public void swim(){
        System.out.println(color+"色的"+age+"岁的狗狗"+name+"正在游泳...");
    }
}
public class Fish extends Animal implements Swim {
    Fish(){
    }
    Fish(String name,int age,String color){
        super(name,age,color);
    }

    void eat(){
        System.out.println(color+"色的"+age+"岁的小鱼"+name+"正在吃小虾...");
    }
    public void swim(){
        System.out.println(color+"色的"+age+"岁的小鱼"+name+"正在游泳...");
    }
}
public class Chick extends Animal {
    Chick(){
    }
    Chick(String name,int age,String color){
        super(name,age,color);
    }
    void layEggs(){
        System.out.println(color+"色的"+age+"岁的小鸡"+name+"正在下蛋...");
    }
    void eat(){
        System.out.println(color+"色的"+age+"岁的小鸡"+name+"正在吃小米...");
    }
}
public class Master {
    void feed(Animal animal){ //喂动物,将范围扩大了,所有动物都可以
        animal.eat();
    }
}

package ooday04;
/**
 * 演示多态
 */
public class Test {
    public static void main(String[] args) {
        /*
        Animal o1 = new Dog("小黑",2,"黑");
        //o1能强转为:Dog,Swim,Animal
        Animal o2 = new Fish("小黑",2,"黑");
        //o2能强转为:Fish,Swim,Animal
        Animal o3 = new Chick("小黑",2,"黑");
        //o3能强转为:Chick,Animal
        */
        
        
        /*
        //强转成功的条件:
        //1.引用所指向的对象,就是该类型
        //2.引用所指向的对象,实现了该接口或继承了该类型
        Animal o = new Dog("小黑",2,"黑"); //向上造型
        Dog g = (Dog)o;   //引用o所指向的对象,就是Dog类型
        Swim s = (Swim)o; //引用o所指向的对象,实现了Swim接口
        //Fish f = (Fish)o; //运行时会发生ClassCastException类型转换异常

        System.out.println(o instanceof Dog);  //true
        System.out.println(o instanceof Swim); //true
        System.out.println(o instanceof Fish); //false
        if(o instanceof Fish){ //false
            Fish f = (Fish)o;
        }
        */
        
        
        
        

        /*
        //演示向上造型(多态)的第2点应用:
        Master master = new Master();
        Dog dog = new Dog("小黑",2,"黑");
        Chick chick = new Chick("小花",3,"花");
        Fish fish = new Fish("小金",1,"金");
        master.feed(dog); //在传参的同时,系统自动做了向上造型
        master.feed(chick);
        master.feed(fish);
        */

        //演示向上造型(多态)的第1点应用:
        //Animal o = new Animal(); //编译错误,抽象类不能被实例化
        Animal[] animals = new Animal[5];
        animals[0] = new Dog("小黑",2,"黑"); //向上造型
        animals[1] = new Dog("小白",1,"白");
        animals[2] = new Fish("小金",1,"金");
        animals[3] = new Fish("小花",2,"花");
        animals[4] = new Chick("小灰",3,"灰");
        for(int i=0;i<animals.length;i++){ //遍历所有动物
            System.out.println(animals[i].name); //输出每个动物的名字
            animals[i].eat();   //每个动物吃饭
            animals[i].drink(); //每个动物喝水

            if(animals[i] instanceof Dog){
                Dog dog = (Dog)animals[i];
                dog.lookHome();
            }
            if(animals[i] instanceof Chick){
                Chick chick = (Chick)animals[i];
                chick.layEggs();
            }
            if(animals[i] instanceof Swim){ //适用于所有实现Swim接口的(会游泳的)
                Swim s = (Swim)animals[i];
                s.swim();
            }
        }
    }
}

二、内部类

1、成员内部类

在实际开发中我们的成员内部类应用率低,了解一下就行 

成员内部类就是类中套类,外面的称为外部类,里面的称为内部类
内部类通常只服务于外部类,对外不具备可见性
内部类对象通常在外部类中创建,如下面的name属性
内部类可以直接访问外部类的成员,在内部类中有个隐式的引用指向创建它的外部类对象
隐式的引用:外部类名 .this,就是你不写,也默认为用到了
何时用:若 A (Baby) 只让 B (Mama) 用,并且 A (Baby) 还想访问 B (Mama) 的成员时,可
以设计成员内部类
//成员内部类
public class InnerClassDemo {
    public static void main(String[] args) {
        Mama m = new Mama();
        //Baby b = new Baby(); //编译错误,内部类对外不具备可见性
    }
}

//Baby类只能Mama类用,并且Baby类中还想访问Mama类的成员,可以将Baby设计为成员内部类
class Mama{ //外部类
    String name;
    void create(){
        Baby b = new Baby(); //正确,内部类对象通常在外部类中创建
    }
    class Baby{ //成员内部类
        void show(){
            System.out.println(name); //简写
            System.out.println(Mama.this.name); //完整写法,Mama.this指代外部类对象
            //System.out.println(this.name); //编译错误,this指当前Baby对象
        }
    }
}

2、匿名内部类

匿名内部类的应用率比较高,需要我们去掌握

那么匿名内部类是什么去创建的呢?代码如下:

        //1)创建了InterInter的一个派生类,但是没有名字
        //2)为该派生类创建了一个对象,名为o3,向上造型为InterInter类型
        //3)大括号中的为派生类的类体
        InterInter o3 = new InterInter(){
            public void show(){
                System.out.println("showshow");
                //num = 6; //编译错误,匿名内部类中不能修改外面局部变量的值
                            //因为该变量在此处会默认为final的
            }
        };
        o3.show();




//定义一个类,接口也是类哦!

interface InterInter{
    void show();
}


匿名内部类就是new对象差不多,只是我们在其后面叫了大括号,以来写出这个内部类具体干什么,即派生类的类体,那么我们什么时候去使用匿名内部类呢?在我们若想创建一个派生类的对象,并且对象只创建一次,这时我们就可以设计为匿名内部类,还可以大大简化代码。

但是我们需要注意的是匿名内部类中不能修改外面局部变量的值,上面注释的num就是个外部变量,我们不能在匿名内部类中去进行修改;还有就是我们要记住内部类有独立的class。

三、package和import

package与import是两关键字,具体是干嘛的:

package :声明包
        作用:避免类的命名冲突
        规定:同包中的类不能同名,但不同包中的类可以同名。
        类的全称:包名. 类名。包名常常有层次结构
        建议:包名所有字母都小写
import :导入类
        同包中的类可以直接访问,但不同包中的类不能直接访问,若想访问:
        先import 导入类,再访问类 -------------- 建议
        类的全称--------------------------------------- 太繁琐、不建议(就是在我们要用到其它包的类时,我们通过包名.类名这种全称来访问,但是就有一个弊端,当我使用频繁的时候,要一直写全称,这是不是有的麻烦,所以我们建议在要用到其他包的类时,我们就可以先导包再访问类)
package day04;
import java.util.Scanner; //导包
//Scanner的演示
public class ScannerDemo {
    public static void main(String[] args) {
        /*
          package java.util;
          class Scanner{
            Scanner(InputStream s){ ... }
            int nextInt(){ ... }
            double nextDouble(){ ... }
          }
         */
        Scanner scan = new Scanner(System.in); //2
        int age = scan.nextInt(); //3
        double price = scan.nextDouble(); //3
    }
}

 

四、补充 

1. 多态的实际应用:
        将不同对象( 狗、鱼、鸡 ) 统一封装到超类数组 ( 动物数组 ) 中来访问,实现代码复用 ,将超类型(Animal) 作为参数或返回值类型,传递或返回派生类 (Dog/Fish/Chick) 对象,以扩大 方法的应用范围( 所有 Animal) ,实现代码复用
2. 隐式的引用:
        this:指代当前对象
        super:指代当前对象的超类对象
        外部类名.this :指代当前对象的外部类对象
3. 关键字顺序:先 package ,再 import ,最后 class
4. 抽象类和接口的区别: ----------- 常见面试题
        抽象类:
                由abstract修饰
                可以包含变量、常量、构造方法、普通方法、抽象方法、静态方法
                派生类通过extends来继承
                只能继承一个(单一继承 )
                抽象类中的成员任何访问权限都可以
        接口:
                由interface定义
                可以包抽象方法、常量、静态方法、默认方法
                实现类通过implements来实现
                可以实现多个(多实现 )
                接口中的成员的访问权限都是public
ok啦!就写到这了,建议把案例看一看哦!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值