面向对象(五)

一、final
1.继承的代码实现:
由于继承中方法有一个现象:方法重写。
所以,父类的功能,就会被子类给覆盖掉
有些时候,我们不想让子类去覆盖掉父类的功能,只能让他使用。
这个时候,针对这种情况,java就提供了一个关键字:final

final:"最终"的意思,常见的是它可以修饰类,方法,变量。
class Fu{
// Zi中的show()无法覆盖Fu中的show()
    public final void show(){
        System.out.println("绝密文件,任何人都不能修改");
    }
}

class Zi extends Fu{
    public void show(){
        System.out.println("这是一堆垃圾");
    }
}

class FinalDemo{
    public static void main(String[] args){
        Zi z = new Zi();
        z.show();
    }
}

2.final修饰类,方法,变量
final可以修饰类,方法,变量

    特点:
    final可以修饰类:该类不能被继承。
    final可以修饰方法:该方法不能被重写(覆盖,复写)
    final可以修饰变量:该变量不能被重新赋值,因为这个变量其实就是常量。

    常量:
        A、字面值常量
            "hello"、10、true
        B、自定义常量
            final int x = 10
class Fu{
        public final int num= 100; 
}

class Zi extends Fu{
    public void show(){
    //无法为最终变量num分配值
         num = 10;
        System.out.println(num);
    }
}

class FinalDemo{
    public static void main(String[] args){
        Zi z = new Zi();
        z.show();

    }
}

3.final修饰局部变量的问题
基本类型:基本类型的值不能发生改变
引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。
比如:教室的地址不能变,但是教室里面的学生可以改变。

class Student{
    int age = 10;
}

class FinalDemo{
    public static void main(String[] args){
        //局部变量是基本数据类型
        int x = 10;
        x = 100;
        System.out.println(x);
        final int y = 10;
        //错误: 无法为最终变量y分配值
        //y = 10;
        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);
        System.out.println("-----------");

        //不能这样写,因为引用变量传递是地址值,
        //我们这样做,是在重新开辟一个空间
        //而final修饰的引用变量是无法重新开辟空间的。
        //重新分配内存空间
        //错误: 无法为最终变量ss分配值
        ss = new Student();

    }
}

4.final修饰变量的初始化时机
A:被final修饰的变量只能赋值一次
B:在构造方法完毕前(非静态的常量)

class Demo{
    //int num = 10;
    //final int num2 = 20;

    int num;
    final int num2;


    public Demo(){
        num = 100;

        num2 = 200;
    }
}

class FinalDemo{
    public static void main(String[] args){

        Demo d = new Demo();
        System.out.println(d.num);
    }
}

二、多态
1.概述和前提条件
多态:同一个对象(事物),在不同时刻体现出来的不同状态。
举例:
猫是猫,猫是动物。
水(液态,固态,气态)。

多态的前提:
    A:要有继承关系
    B:要有方法重写。
        其实没有也是可以的,但是如果没有这个就没有意义。
    C:要有父类引用指向子类对象。
        父   f   =   new  子();

用代码体现一下 

多态中的成员访问特点:
A:成员变量:编译看左边,运行看左边。
B:构造方法
    创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
C:成员方法
        编译看左边,运行看右边。(因为方法存在方法覆盖)
    **如果子类中存在方法重写,那么
        父类    f =  new  子类
        中f调用的就是子类中的方法,如果调用的方法子类中没有,那么就调用父类中的方法
D:静态方法
    编译看左边,运行看左边
(静态和类相关,算不上重写,所以,访问还是左边的)

由于成员方法存在方法重写,所以它运行看右边。
class Fu{

    public int num = 100;
    public void show(){
        System.out.println("showFu");
    }
}

class Zi extends Fu{

    public int num = 1000;
    public int num2 = 200;

    public void show(){
        System.out.println("showZi");
    }

    public void method(){
        System.out.println("method");
    }
}

class DuoTaiDemo{
    public static void main(String[] args){

        //要有父类引用指向子类对象。
        //父   f = new  子();
        Fu f = new Zi();
        System.out.println(f.num);
        //结果是100
        // 错误: 找不到符号
        //System.out.println(f.num2);

        f.show();
        // 错误: 找不到符号
        //f.method();
    }
}

2.多态的好处
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 DuoTaiDemo{
    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(c1);
        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();
    }
    */
}

3.多态的弊端
不能使用子类的特有功能

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

class Zi extends Fu{
    public void show(){
        System.out.println("show Zi");
    }

    public void method(){
        System.out.println("method");
    }
}

class DuoTaiDemo{
    public static void main(String[] args){
        Fu fu = new Zi();
        fu.show();
        //错误: 找不到符号
        fu.method();
    }
}

4.多态中向上转型和向下转型
多态的弊端:
不能使用子类的特有功能

我就想使用子类的特有功能?行不行?
行。

怎么用呢?
A:创建子类对象调用方法即可。(可以,但是很多时候不合理,而且,太占内存了)
B:把父类的引用强制转换为子类的引用。(向下转型)

对象间的转换问题:
    向上转型:
            Fu  f = new zi();
    向下转型:
            Zi z = (Zi)f;//要求这里的f必须是能够转换为子的。
class Fu{
    public void show(){
        System.out.println("show Fu");
    }
}

class Zi extends Fu{
    public void show(){
        System.out.println("show Zi");
    }

    public void method(){
        System.out.println("method");
    }
}

class DuoTaiDemo{
    public static void main(String[] args){
        Fu fu = new Zi();
        fu.show();
        //错误: 找不到符号
        //fu.method();

        //创建子类对象
        //方式1:使用这种方法,内存是两个对象
        //Zi z = new Zi();
        //z.show();
        //z.method();

        //方式2:使用这种方法,内存中是一个对象。
        //你能够把子的对象赋值给父亲,那么我能不能
        //把父的引用赋值给子的引用呢?
        Zi z = (Zi)fu;
        z.show();
        z.method();

    }
}

4.多态问题理解——孔子装爹案例

class 孔子爹  {
    public int age = 40;

    public void teach(){
        System.out.println("讲解javase");
    }
}

class 孔子 extends 孔子爹 {
    public int age = 20;

    public void teach(){
        System.out.println("讲解javase");
    }

    public void playGame(){
        System.out.println("英雄联盟");
    }
}

//java培训特别火,很多人来请孔子爹去讲课,这一天孔子爹被请走了,
//但是还有人来请,就剩孔子在家,价格还挺高,孔子一想,我是不是可以考虑去呢?
//然互就穿上爹的衣服,带上爹的眼镜,粘上爹的胡子。就开始装爹。
//向上转型
孔子爹   k爹  =  new  孔子();
//到人家那里去了
System.out.println(k爹.age);
k爹.teach();
k爹.playGame();//这是儿子才能做的

//讲完了,下班回家了
//脱下爹的装备,换上自己的装备
孔子  K = (孔子)k爹;
System.out.println(k.age);//20
k.teach();//讲解论语
k.playGame();//英雄联盟

5.多态的内存图解
面向对象(五)_第1张图片

6.多态中的对象变化内存图解
面向对象(五)_第2张图片

/*
    ClassCastException:类型转换异常
    一般在多态的向下转型中容易出现

*/

class Animal{
    public void eat(){}
}

class  Dog extends Animal{
    public void eat(){}
    public void lookDoor(){}
}

class Cat extends Animal{
    public void eat(){}

    public void playGame(){}
}

class DuoTaiDemo{
    public static void main(String[] args){
        //内存中的是狗
        Animal a = new Dog();
        Dog d = (Dog)a;

        //内存中的是猫
        a = new Cat();
        Cat c = (Cat)a;

        //内存中的是猫
        Dog  dd = (Dog)a;//.ClassCastException
        //java中类型转换不匹配的错误
    }
}

7.面试题

/*
    看程序写结果:先判断有没有问题,如果没有,写出结果

    多态的成员访问特点:
        方法:编译看左边,运行看 右边

    继承的时候:
        子类中有和父类中一样的方法,叫重写。
        子类中没有和父亲中出现过的方法,方法就被继承
*/

class A{
    public void show(){
        show2();
    }

    public void show2(){
        System.out.println("我");
    }
}

class B extends A{
    /*
    public void show(){
        show2();
    }
    */
    public void show2(){
        System.out.println("爱");
    }
}

class C extends B{
    public void show(){
        super.show();
    }

    public void show2(){
        System.out.println("你");
    }
}

public class DuoTaiTest2{
    public static void main(String[] args){
        A a = new B();
        a.show();

        B b = new C();
        b.show();

    }
}

/*
    结果:爱你
*/

你可能感兴趣的:(JavaSE)