Java - 多态、内部类、包装类

Java - 多态、内部类、包装类

一、多态

  1. 多态的体现格式

在这里插入图片描述

  1. 多态的访问特点

    <1> 成员变量与静态方法:编译(写代码的时候)都是看左边,也就是必须父类中有才不会报错;运行也是看左边,也就是说执行的是父类中方法中的内容。

    <2> 成员方法:编译看左边,用对象名去调用的时候要保证父类里有;运行的话看的是右边,也就是说 执行的是子类方法中的内容,于是呢,就达到了通过对象名调用方法的时候竟然有不同的效果,这就是我们说的 多态 !!!

  2. 多态的应用 - 总之就是一个中心点:将返回值类型等类型定义成父类类型,但是我实际上让它 指向子类的对象,于是就实现了多态的效果

    ~ 须知:Zi、Zi2 都是Fu 类的子类,并且都重写了父类的 method( ) 方法。

    <1> 多态的变量

    //多态的变量  同一个变量,可以指向不同的子类或实现类对象 可以减少变量的定义。
    
    // 之前的话每个子类的对象都需要定义一个变量去接收
    //Zi zi = new Zi();
    //Zi2 zi2 = new Zi2();
    
    //现在的话只需要先定义一个父类类型的变量,之后重新赋一下值,变量就会指向子类对象,编译器也不会报错。
    Fu f = new Zi();
    f.method();//Zi method
    f = new Zi2();
    f.method();//Zi2 method 
    

    <2> 多态的形参

    //形参多态的使用   修饰符 返回值类型 方法名(父类/接口名 变量名){变量名.方法名(); } 让一个方法,接收不同的子类对象,减少了方法的定义。
    
    //之前要想调用子类重写的方法,只能老老实实定义一个个的方法,然后在方法里调用,如何能接受的参数也很有限,只能单一的类型
    展示Zi对象
        //public static void showZi(Zi zi){
        //    zi.method();
        //}
    展示Zi2对象
        //public static void showZi(Zi2 zi2){
        //    zi2.method();
        //}
        
    //现在的话,我只需要将形参设置为父类,到时候你传不同的子类对象进来我都能接收,为什么呀,因为这里就用了多态的知识,而且到时候调用方法的时候,调的也是子类对象它们各自的方法。   
    展示所有的Fu的子类对象
        public static void showFu(Fu f) {
            f.method();
        }
    
        Zi zi = new Zi();
        showFu(zi);
        Zi2 zi2 = new Zi2();
        showFu(zi2);
    

    <3> 多态的返回值类型

    //返回值类型多态的使用    修饰符 父类/接口名 方法名(参数) { return 子类/实现类对象; } 可以让一个方法,返回不同的子类对象,减少了方法的定义
    
        //通过一个方法,获取不同的子类对象,我把返回值类型定义成父类的类型,那么我这个方法就可以根据传过来的子类类名,返回不同的父类类型的子类对象
        public static Fu getFu(String className) {
            if (className.equals("Zi")) {
                return new Zi();
            } else if (className.equals("Zi2")) {
                return new Zi2();
            } else {
                return null;
            }
        }
    
        Fu f2 = getFu("Zi");
        f2.method();//Zi method
        f2 = getFu("Zi2");
        f2.method();//Zi2 method
    
  3. 多态的弊端

    父类类型的子类对象没办法调用子类对象特有的方法,违背编译看左的规则,并且你是父类的类型,但是父类里压根就找不到特有的方法,如果有那就不叫特有的了。

  4. 解决多态的弊端 - 要用到类型转换的知识

    • 类型转换的分类:

      <1> 向上转型(也就是之前说的自动类型转换):父类类型 变量名 = new 子类类型() 或 子类对象

      <2> 向下转型(也就是之前说的强制类型转换):子类类型 变量名 = (子类类型) 父类变量名;

  5. 如何判断 变量名 对应的对象所属的类型

    <1> 现在有一个问题发生了:在一个方法中,一个父类类型的子类对象要想访问所有子类的特有方法,我们不是可以向下转型吗,如果不加判断,那么就会出现子类之间的互相转换,这是不允许的。

    //初次尝试:想在一个方法中调用子类对象的特有方法,结果却发现了一个问题!!
    public static void main(String[] args) {
        Cat c = new Cat();
        Dog d = new Dog();
    
        showAnimal(c);
        showAnimal(d);
    }
    
    public static void showAnimal(Animal a) {
        a.eat();
        //调用猫特有功能
        Cat c = (Cat) a;
        c.catchMouse();
        //调用狗特有功能
        Dog d = (Dog) a;//error  错误的类型转换
        
        //原因分析:按照代码执行顺序,猫类的对象传进了showAnimal 方法中,但是定义的形参的是父类的,没问题,用到了多态的知识;然后向下转型成功输出了猫类特有方法,也没问题;但是呢我现在也想把狗类的特有方法用一下,直接对 a向下转型,但是我忽视了一个问题,现在a 已经是纯正的猫类对象,所以我现在转型,相当于是从猫类 -> 狗类,那肯定不支持的呀,猫狗之间没有继承的关系!!
        d.lookHome();
    }
    

    <2> 解决办法:先判断传进来对象属于哪一个子类,然后在方法中根据它的类型向下转型成对应它原本的纯正的类型,才能分别调用它们自己特有的方法。

    public static void showAnimal(Animal a) {
    
        //解决办法:变量名 instanceof 数据类型 (判断变量名对应的对象所属的类型)
        //    如果变量属于该数据类型,返回true。
        //    如果变量不属于该数据类型,返回false。
        if (a  instanceof  Cat) {//判断a变量对应的对象,是否是 Cat类型
            //调用猫特有功能
            Cat c = (Cat) a;
            c.catchMouse();
        } else if (a instanceof Dog) {
            //调用狗特有功能
            Dog d = (Dog) a;
            d.lookHome();
        }
    }
    

二、内部类

  • 将类B定义在类A里面,类B就称为内部类,类A则称为类B的外部类
  • 内部类分为,成员内部类、匿名内部类、局部内部类
  • 内部类是一个独立的类,编译后,有单独的class文件,命名为:外部类名+$+内部类类名标识
  1. 成员内部类

    <1> 使用格式

    ​ 外部类名.内部类名 对象名 = new 外部类型().new 内部类型();

    <2>访问特点

    • 内部类可以直接访问外部类的成员,包括私有成员。

    • 外部类要访问内部类的成员,必须要建立内部类的对象。

    public static void main(String[] args) {
        //内部类一般和外部类有内在关联。  汽车类-引擎  电脑-cpu  人-心脏
    
       //创建心脏对象  外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
       //Person.Heart h = new Person().new Heart(); 这样连着写不推荐,因为不够灵活,分开写的话可以应对外部类有不同的情况,比如外部类的状态要改变,内部类要能够分开处理才会比较好。例子:人活着心才会跳,人死了心就停了,所以外部类的状态可能会有多种。
        Person p = new Person();
        Person.Heart h = p.new Heart();
        h.jump();
        System.out.println("一百年");
        p.setLive(false);
        h.jump();
    }
    
  2. 匿名内部类 - 本质上是一个子类的对象或者实现类的对象

    <1> 之前,使用一个父类或接口中的方法需要4步

    ​ ①定义子(实现)类 -> ②重写方法 -> ③创建子(实现)对象 -> ④调用方法

    <2> 现在,只需要写一次

    ​ 使用前提:重写父类或者接口中的方法,但是我又只用一次,可以避免多写实现类

    <3> 格式

    ​ new 父类名或者接口名() { // 方法重写 };

    //这是一个“飞行”接口的匿名实现类对象
    new Flyable() {
        @Override
        public void fly() {
            System.out.println("螺旋式起飞");
        }
    };
    

    <4> 匿名内部类的应用 - 利用了多态的特性

    package com.itheima.demo02内部类.p02匿名内部类;
    //使用场景
    public class Test2 {
        public static void main(String[] args) {
            //通过多态的形式指向父类引用
            Fu f = new Fu(){
                @Override
                public void method(){
                    System.out.println("method1");
                }
            };
            f.method();
    
            //直接调用方法
            new Fu(){
                @Override
                public void method(){
                    System.out.println("method2");
                }
            }.method();
    
            //作为方法参数(实参)传递
            showFu(new Fu(){
                @Override
                public void method(){
                    System.out.println("method3");
                }
            });
        }
        public static void showFu(Fu f){
            f.method();
        }
    }
    
    class Fu{
        public void method(){}
    }
    
  3. 局部内部类 - 定义在方法中的类,实用性不大,不常用

三、包装类

  1. 介绍

    • Java提供了两个类型系统,基本类型与引用类型,

    ​ 基本类型效率更高,引用类型封装了很多的方法。

    • 为了便于操作,java为在lang包下为基本类型创建了对应的引用类型,称为包装类。

  2. 对应关系 - int 与 char 有点不同

    类型byteshortintlongfloatdoublecharboolean
    包装类ByteShortIntegerLongFloatDoubleCharacterBoolean
  3. 常用方法

    • public static Integer valueOf (int i) 返回 表示指定的 int 值的 Integer 实例

    • public static Integer valueOf (String s) 返回 Integer 对象 s必须是 “100” 这种

    • public int intValue() 返回 Integer对象的int形式

  4. 装箱与拆箱

    • 装箱:从基本类型转换为对应的包装类对象 (构造方法/ valueOf)。

    • 拆箱:从包装类对象转换为对应的基本类型 ( xxxValue )。

  5. 自动装箱与自动拆箱

    由于这两个操作使用频率高,Java 5开始,装箱、拆箱动作可以自动完成。也就是直接写个赋值就可以转换了。

    • 自动装箱:基本类型传递给包装类型

    • 自动拆箱:包装类型传递给基本类型

  6. 基本类型与字符串的转换

    <1> 基本类型转换为String

    • 方式一:直接在基本类型后加一个空字符串 数据+“”

    • 方式二:通过String类静态方法 valueOf (Xxx)

    <2> String转换成基本类型

    • 方式一:指定包装类的静态方法 valueOf (String s) 将字符串转为对应包装类

    • 方式二:通过包装类的静态方法 parseXxx (String s) 将字符串转为对应基本类型

    <3> 注意事项:

    • value0f 返回的是包装类型,能完成转换,是因为最后一步系统自动转换的

    • parseXxx 才是一步到位

    • String 转 char 类型只能使用String类中非静态方法 charAt (int index)

    • 数据要符合对应数据的类型格式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值