• 重踏学习Java路上_Day09(final,多态,抽象类,接口)


    引入final的缘由:由于继承中方法有一个现象:方法重写;所以,父类的功能会被子类所覆盖掉。有些时候,我们不想让子类去覆盖掉父类的功能,就是关闭方法重写的功能,只能让该方法使用,此时,针对这种状况,Java就提供了一个关键字:final

    1:final关键字(掌握)
        (1)是最终的意思,可以修饰类,方法,变量
        (2)特点
            A:它修饰的类(编译器称其为最终类),该类不能被继承,一般是最底层的类才用final,因为不想让其被其他类所继承。
            B:它修饰的方法,不能被重写(覆盖)。
            C:它修饰的变量,该修饰后的变量不能被重新赋值(或者说是分配新值),因为这个被修饰的变量是一个常量(自定义常量)。

                     常量有两种,(1)字面值常量:"hello",10,true等;(2)自定义常量:final int x = 10;
        (3)面试相关:
            A:局部变量
                a:基本类型 值不能发生改变
                b:引用类型 地址值不能发生改变,但是对象的内容是可以改变的

            B:初始化时机
                a:只能初始化一次(默认赋值那一次不算,显示赋值或构造方法赋值,包含构造方法块,这三地方只能有一处赋值点),被final修饰的变量只能赋值一次。
                b:常见的给值
                    定义的时候。(推荐)
                    构造方法中,在构造方法完毕前(注意该变量不能是静态的变量,如果静态final变量不赋初值运行,会出现"可能尚未初始化变量xxx",因为静态变量比构造方法运行早,没有默认赋值就执行,所以报错)。

    面试题,局部变量问题例子:

    /*
        面试题:final修饰局部变量的问题
            基本类型:基本类型的值不能发生改变。
            引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。
    */
    class Student {
        int age = 10;
    }

    class FinalTest {
        public static void main(String[] args) {
            //局部变量是基本数据类型
            int x = 10;
            x = 100;
            System.out.println(x);
            final int y = 10;
            //无法为最终变量y分配值
            //y = 100;
            System.out.println(y);
            System.out.println("--------------");
            
            //局部变量是引用数据类型
            Student s = new Student();
            System.out.println(s.age);
            s.age = 100;
            System.out.println(s.age);
            System.out.println("--------------");
            
            final Student ss = new Student();
            System.out.println(ss.age);
            ss.age = 100;
            System.out.println(ss.age);
            
            //重新分配内存空间
            //无法为最终变量ss分配值
            ss = new Student();
        }
    }

    面试题,初始化时机问题例子:

    /*
          final修饰变量的初始化时机
            A:被final修饰的变量只能赋值一次。
            B:在构造方法完毕前。(非静态的常量)
    */
    class Demo {
        //int num = 10;
        //final int num2 = 20;
        
        int num;
        final int num2;
        
        {
            //num2 = 10;
        }
        
        public Demo() {
            num = 100;
            //无法为最终变量num2分配值
            num2 = 200;
        }
    }

    class FinalTest2 {
        public static void main(String[] args) {
            Demo d = new Demo();
            System.out.println(d.num);
            System.out.println(d.num2);
        }
    }



        
    2:多态(掌握)

       ClassCastException:类型转换异常
        一般在多态的向下转型中容易出现,编译时候不会出错,在运行时会报错,因为语法没有错,编译就不会报错

        子类中没有父亲中出现过的方法,方法就被继承过来了。

        多态的概述:某一个事物,在不同时刻表现出来的不同状态。
        (1)同一个对象在不同时刻体现出来的不同状态。
        (2)多态的前提
       A:要有继承关系。
            B:要有方法重写。
                其实没有也是可以的,但是如果没有这个就没有意义。
                    动物 d = new 猫();
                    d.show();
                    动物 d = new 狗();
                    d.show();
            C:要有父类引用指向子类对象。
                父 f =  new 子();
            
            多态的分类:
                a:具体类多态
                    class Fu {}
                    class Zi extends Fu {}
                    
                    Fu f = new Zi();
                b:抽象类多态
                    abstract class Fu {}
                    class Zi extends Fu {}
                    
                    Fu f = new Zi();
                c:接口多态
                    interface Fu {}
                    class Zi implements Fu {}
                    
                    Fu f = new Zi();
        (3)多态中的成员访问特点(刘意视频:09.07)
            Fu f = new Zi();    f:指的就是左边,Zi()指的就是右边

            A:成员变量
                编译看左边,运行看左边(因为成员变量都是放在堆内存,按父类自己生成一份的变量数据内存)
            B:构造方法
                子类的构造都会默认访问父类构造(创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化,子类构造方法默认首句执行父类无参构造方法,为父类进行数据初始化)
            C:成员方法
                编译看左边,运行看右边(因为有重写的关系,所以运行的子类的重名方法)
            D:静态方法
                编译看左边,运行看左边(静态和类相关,算不上重写,所以,访问还是左边的)
                
            为什么?
               只有一个是运行看右边,就是成员方法: 由于成员方法存在方法重写,所以它运行看右边。

    例子:

    class Fu {
        public int num = 100;

        public void show() {
            System.out.println("show Fu");
        }
        
        public static void function() {
            System.out.println("function Fu");
        }
    }

    class Zi extends Fu {
        public int num = 1000;
        public int num2 = 200;

        public void show() {
            System.out.println("show Zi");
        }
        
        public void method() {
            System.out.println("method zi");
        }
        
        public static void function() {
            System.out.println("function Zi");
        }
    }

    class DuoTaiDemo {
        public static void main(String[] args) {
            //要有父类引用指向子类对象。
            //父 f =  new 子();
            Fu f = new Zi();
            System.out.println(f.num);
            //找不到符号
            //System.out.println(f.num2);
            
            f.show();
            //找不到符号
            //f.method();
            f.function();
        }
    }


        (4)多态的好处:
            A:提高代码的维护性(继承体现)
            B:提高代码的扩展性(多态体现)

    /*
        多态的好处:
            A:提高了代码的维护性(继承保证)
            B:提高了代码的扩展性(由多态保证)
            
        猫狗案例代码
    */
    class Animal {
        public void eat(){
            System.out.println("eat");
        }
        
        public void sleep(){
            System.out.println("sleep");
        }
    }

    class Dog extends Animal {
        public void eat(){
            System.out.println("狗吃肉");
        }
        
        public void sleep(){
            System.out.println("狗站着睡觉");
        }
    }

    class Cat extends Animal {
        public void eat() {
            System.out.println("猫吃鱼");
        }
        
        public void sleep() {
            System.out.println("猫趴着睡觉");
        }
    }

    class Pig extends Animal {
        public void eat() {
            System.out.println("猪吃白菜");
        }
        
        public void sleep() {
            System.out.println("猪侧着睡");
        }
    }

    //针对动物操作的工具类
    class AnimalTool {
        private AnimalTool(){}

        /*
        //调用猫的功能
        public static void useCat(Cat c) {
            c.eat();
            c.sleep();
        }
        
        //调用狗的功能
        public static void useDog(Dog d) {
            d.eat();
            d.sleep();
        }
        
        //调用猪的功能
        public static void usePig(Pig p) {
            p.eat();
            p.sleep();
        }
        */
        public static void useAnimal(Animal a) {
            a.eat();
            a.sleep();
        }
        
    }

    class DuoTaiDemo2 {
        public static void main(String[] args) {
            //我喜欢猫,就养了一只
            Cat c = new Cat();
            c.eat();
            c.sleep();
            
            //我很喜欢猫,所以,又养了一只
            Cat c2 = new Cat();
            c2.eat();
            c2.sleep();
            
            //我特别喜欢猫,又养了一只
            Cat c3 = new Cat();
            c3.eat();
            c3.sleep();
            //...
            System.out.println("--------------");
            //问题来了,我养了很多只猫,每次创建对象是可以接受的
            //但是呢?调用方法,你不觉得很相似吗?仅仅是对象名不一样。
            //我们准备用方法改进
            //调用方式改进版本
            //useCat(c);
            //useCat(c2);
            //useCat(c3);
            
            //AnimalTool.useCat(c);
            //AnimalTool.useCat(c2);
            //AnimalTool.useCat(c3);
            
            AnimalTool.useAnimal(c);
            AnimalTool.useAnimal(c2);
            AnimalTool.useAnimal(c3);
            System.out.println("--------------");
            
            //我喜欢狗
            Dog d = new Dog();
            Dog d2 = new Dog();
            Dog d3 = new Dog();
            //AnimalTool.useDog(d);
            //AnimalTool.useDog(d2);
            //AnimalTool.useDog(d3);
            AnimalTool.useAnimal(d);
            AnimalTool.useAnimal(d2);
            AnimalTool.useAnimal(d3);
            System.out.println("--------------");
            
            //我喜欢宠物猪
            //定义一个猪类,它要继承自动物,提供两个方法,并且还得在工具类中添加该类方法调用
            Pig p = new Pig();
            Pig p2 = new Pig();
            Pig p3 = new Pig();
            //AnimalTool.usePig(p);
            //AnimalTool.usePig(p2);
            //AnimalTool.usePig(p3);
            AnimalTool.useAnimal(p);
            AnimalTool.useAnimal(p2);
            AnimalTool.useAnimal(p3);
            System.out.println("--------------");
            
            //我喜欢宠物狼,老虎,豹子...
            //定义对应的类,继承自动物,提供对应的方法重写,并在工具类添加方法调用
            //前面几个必须写,我是没有意见的
            //但是,工具类每次都改,麻烦不
            //我就想,你能不能不改了
            //太简单:把所有的动物都写上。问题是名字是什么呢?到底哪些需要被加入呢?
            //改用另一种解决方案。
            
        }
        
        /*
        //调用猫的功能
        public static void useCat(Cat c) {
            c.eat();
            c.sleep();
        }
        
        //调用狗的功能
        public static void useDog(Dog d) {
            d.eat();
            d.sleep();
        }
        */
    }


        (5)多态的弊端:
            父不能使用子的特有功能。(因为多态编译成员方法时,编译看左边(即父类),所以左边调用右边方法会出现报错,因为父类不存在该方法)
            
            现象:
                子可以当作父使用,父不能当作子使用。
        (6)多态中的转型
            A:向上转型
                从子到父   父类引用指向子类对象    Fu f = new Zi();
            B:向下转型
                从父到子  父类引用转为子类对象     Zi z = (Zi) f;父类引用赋予给子类变量,打开它的儿子功能。

        注意:多态的弊端
            不能使用子类的特有功能。 
            我就想使用子类的特有功能?行不行?
                 行。    
          怎么用呢?
              A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了)
              B:把父类的引用强制转换为子类的引用。(向下转型)   
        对象间的转型问题:
            向上转型:
                Fu f = new Zi();//外面是一父的形式展现到外面,但内部是子的实例,但只能调用父类的相同东西,子类的特有功能是不能用的。
            向下转型:
                Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。就是说强制转换前必须知道该f是Zi的对象。现在这样写就可以使用子类的特有功能了。


        (7)孔子装爹的案例帮助大家理解多态
        (8)多态的练习
            A:猫狗案例
            B:老师和学生案例

    多态继承中的内存图解:

    多态中的对象变化内存图解



    3:抽象类(掌握)
        (1)抽象类的概述:

           把多个共性的东西提取到一个类中,这是继承的做法。
           但是呢,这多个共性的东西,在有些时候,方法声明一样,但是方法体。
           也就是说,方法声明一样,但是每个具体的对象在具体实现的时候内容不一样。
           所以,我们在定义这些共性的方法的时候,就不能给出具体的方法体。
           而一个没有具体的方法体的方法是抽象的方法。
           在一个类中如果有抽象方法,该类必须定义为抽象类。
        (2)抽象类的特点
            A:抽象类和抽象方法必须用abstract关键字修饰
            B:抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类
            C:抽象类不能实例化
                因为它不是具体的。
                抽象类有构造方法,但是不能实例化?构造方法的作用是什么呢?
                用于子类访问父类数据的初始化,因为子类继承父类,在子类构造方法处会默认调用父类无参构造方法
            D:抽象的子类
                a:如果不想重写抽象方法,该子类也是一个抽象类。
                b:重写所有的抽象方法,这个时候子类是一个具体的类。          
            抽象类的实例化其实是靠具体的子类实现的。是多态的方式。
                Animal a = new Cat();
        (3)抽象类的成员特点
            A:成员变量
                既可以是变量,也可以是常量
            B:构造方法
                有构造方法,用于子类访问父类数据的初始化,抽象类可以有构造方法,构造方法不可继承(所有构造方法都不能继承,只能是调用),但是可以供子类用super()或者super(参数,参数。。。。)调用。
            C:成员方法
                有抽象,有非抽象
                A:抽象方法 强制要求子类做的事情,其实就是要子类重写方法,是方法有意义

                B: 非抽象方法  子类继承父类的事情,提高代码的复用性
        (4)抽象类的练习
            A:猫狗案例练习
            B:老师案例练习
            C:学生案例练习
            D:员工案例练习
        (5)抽象类的几个小问题
            A:抽象类有构造方法,不能实例化,那么构造方法有什么用?
                用于子类访问父类数据的初始化
            B:一个类如果没有抽象方法,却定义为了抽象类,有什么用?
                为了不让创建对象
            C:abstract不能和哪些关键字共存
                a:final    冲突(非法修饰符组合:absract和final,因为final就是不让子类重写覆盖,而abstract就是让自己继承并重写,所以报错)
                b:private 冲突(非法修饰符组合:absract和private,因为private就是不让子类继承,而abstract就是让自己继承并重写,所以报错)
                c:static 无意义(非法修饰符组合:absract和static,因为static是在类加载是加载,直接类名.方法名可以调用,但抽象方法都是没有方法体的,执行一个没有方法体的方法,所以一点用都没有,所以是无意义)

    例子:抽象类的特点:

    //abstract class Animal //抽象类的声明格式
    abstract class Animal {
        //抽象方法
        //public abstract void eat(){} //空方法体,这个会报错。抽象方法不能有主体
        public abstract void eat();
        
        public Animal(){}
    }

    //子类是抽象类
    abstract class Dog extends Animal {}

    //子类是具体类,重写抽象方法
    class Cat extends Animal {
        public void eat() {
            System.out.println("猫吃鱼");
        }
    }

    class AbstractDemo {
        public static void main(String[] args) {
            //创建对象
            //Animal是抽象的; 无法实例化
            //Animal a = new Animal();
            //通过多态的方式
            Animal a = new Cat();
            a.eat();
        }
    }

    4:接口(掌握)
        (1)回顾猫狗案例,它们仅仅提供一些基本功能。
           比如:猫钻火圈,狗跳高等功能,不是动物本身就具备的,
           是在后面的培养中训练出来的,这种额外的功能,java提供了接口表示。
        (2)接口的特点:

            命名方法:接口名+Impl这种格式是接口的实现类格式
            A:接口用关键字interface修饰
                interface 接口名 {}
            B:类实现接口用implements 修饰
                class 类名 implements 接口名 {}
            C:接口不能实例化
            D:接口的实现类
                a:是一个抽象类实现接口,但意义不大,因为抽象类也是不能实例化。
                b:是一个具体类,这个类必须重写接口中的所有抽象方法。(推荐方案)

            实现多态的方法:

                 (1)A:具体类多态(使用很少,机会没有);

          (2)B:抽象类多态(常用);

                 (3)C:接口多态(最常用);
        (3)接口的成员特点:
            A:成员变量
                只能是常量(就算是正常的"public int num=20;"在编译器中也会自动变为public static final形式:"public static final int num = 20;",因为这是接口的对于所有成员变量的默认特点)
                默认修饰符:public static final (所以可以直接通过接口名.成员变量进行访问,写与不写,这三个修饰符都是一样的)
                建议:每次写接口成员变量是都写完整版的,防止自己忘记,即public static final int num03 = 30;

            B:构造方法
                接口并没有构造方法 ,因为它的所有方法都是public abstract,都没有方法体,哪会有构造方法    接口主要是拓展功能的,而没有具体存在 (记住所有的类都是继承于无参Object类,但接口没有构造方法,实现接口的类的构造方法会调用Object的无参构造运行方法,所以不会报错)
            C:成员方法
                只能是抽象的
                默认修饰符:public abstract (默认你写与不写,都默认为public abstract,建议还是写,因为自己会铭记)
        (4)类与类,类与接口,接口与接口
            A:类与类
                继承关系,只能单继承,可以多层继承
            B:类与接口
                实现关系,可以单实现,也可以多实现。
                还可以在继承一个类的同时,实现多个接口
            C:接口与接口
                继承关系,可以单继承,也可以多继承  interface Sister extends Father,Mother{},接口的多继承
        (5)抽象类和接口的区别

          抽象类和接口的区别:
            A:成员区别
                  抽象类:
                      成员变量:可以变量,也可以常量
                      构造方法:有
                      成员方法:可以抽象,也可以非抽象
                接口:
                      成员变量:只可以常量
                      成员方法:只可以抽象
            
            B:关系区别
                    类与类
                          继承,单继承
                    类与接口
                          实现,单实现,多实现
                    接口与接口
                          继承,单继承,多继承
            
            C:设计理念区别
                    抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能 Animal a = new Cat()  猫 是一只(is a) 动物
                    接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能    like a :像,就是说不是他的特有功能,而是因时因地拓展出来的功能
        (6)练习:
            A:猫狗案例,加入跳高功能
            B:老师和学生案例,加入抽烟功能0

    接口定义的例子:

    //定义动物培训接口
    interface AnimalTrain {
        public abstract void jump();
    }

    //抽象类实现接口
    abstract class Dog implements AnimalTrain {
    }

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

    class InterfaceDemo {
        public static void main(String[] args) {
            //AnimalTrain是抽象的; 无法实例化
            //AnimalTrain at = new AnimalTrain();
            //at.jump();
            
            AnimalTrain at = new Cat();//这里利用Cat的对象引用,把引用赋予给接口变量,接口变量引用接口对象
            at.jump();
        }
    }

    类与类,类与接口,接口与接口的关系

    interface Father {
        public abstract void show();
    }

    interface Mother {
        public abstract void show2();
    }

    interface Sister extends Father,Mother {

    }

    //class Son implements Father,Mother //多实现
    class Son extends Object implements Father,Mother {
        public void show() {
            System.out.println("show son");
        }
        
        public void show2() {
            System.out.println("show2 son");
        }
    }

    class InterfaceDemo3 {
        public static void main(String[] args) {
            //创建对象
            Father f = new Son();
            f.show();
            //f.show2(); //报错
        
            Mother m = new Son();
            //m.show(); //报错
            m.show2();
        }
    }

  • 相关阅读:
    swift 第十四课 可视化view: @IBDesignable 、@IBInspectable
    swift 第十三课 GCD 的介绍和使用
    swift 第十二课 as 的使用方法
    swift 第十一课 结构体定义model类
    swift 第十课 cocopod 网络请求 Alamofire
    swift 第九课 用tableview 做一个下拉菜单Menu
    swift 第八课 CollectView的 添加 footerView 、headerView
    swift 第七课 xib 约束的优先级
    swift 第六课 scrollview xib 的使用
    swift 第五课 定义model类 和 导航栏隐藏返回标题
  • 原文地址:https://www.cnblogs.com/canceler/p/4599528.html
Copyright © 2020-2023  润新知