Java学习 day10_oop3

2021/6/19 17:27:08

本文主要是介绍Java学习 day10_oop3,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

子类的方法覆盖(override)

子父类中能否拥有同名的方法呢?

如果可以,请尝试

  • 在父子类中声明两个个一模一样的方法,但是方法体输出不同
    • 创建子类对象,直接调用该方法,结果是什么呢?
  • 再在父子类中定义两个方法,分别在方法体中调用自身方法名一样的方法
    • 创建子类对象,分别调用两个方法,结果是什么呢?
  • 我们发现无论怎么操作,都只能访问子类中的同名方法,这就是方法的覆盖

用对象名点成员方法的方式来调用成员方法,它的编译器检索机制和访问成员变量类似

  • 编译器会先从引用的类型中查找,查找不到就去父类中查找,再找不到就会报错
  • 所以说对于用对象名点访问成员这种方式,编译时期能够访问的成员都是根据父类来定的

但是调用方法时,没有办法在编译时期就确定调用哪个方法,需要程序运行时才能确定

  • 程序运行起来,方法会表现出对象的真实类型的行为,如果子类访问父子类同名方法就体现为方法覆盖

如果想在子类的方法中,访问父类同名方法,应该怎么办?

  • super关键字

哪些方法无法被覆盖?
1,私有的方法虽然能继承,但是没有访问权限,访问不到,那么私有private方法也不能被重写
2,构造方法不能被继承,更不能被重写
3,static修饰的静态方法可以被继承,但是如果出现同名静态方法,两个方法处在不同的区域,不考虑重写或者覆盖, 隐藏的问题, 它也不是重写


  • 重写 VS 重载
重载(overload)重写(override)
发生的类不同发生在同类中发生在子父类之间,肯定不是一个类
方法名必须相同必须相同
参数列表必须不同必须相同
权限修饰符不影响重写的方法访问权限必须大于等于原方法
异常不影响重写的方法不能抛出更多的异常
返回值类型不影响重写的方法的返回值类型必须和原方法兼容,代表可以不是完全一致

补充一下,重载的形参列表一定不相同,可以是参数个数不同,可以是类型不同,也可以是传参的顺序不同

  • 被static、final、private修饰的父类方法无法被重写

如果父类引用指向子类对象,要使用子类独有的方法,也必须强制转换后才能使用,这点和成员变量是一样的


final和限制继承

final是一个修饰符,可以用来修饰类、方法和变量(包括成员变量和局部变量)

final修饰类

表示”最终的类“

当用final修饰一个类时,表明这个类不能被继承,除了这一点外,这个类照常使用,比如创建对象,比如访问方法,比如继承别的类

  • 常见的final类
    • String
    • System
    • Math
    • 所有基本数据类型的包装类和一个Void
      • Boolean,Character,Short,Integer,Long,Float,Double,Byte,Void
  • 除非你十分确定这个类以后不会被用来继承 ,为了保证安全,可以设置一个类为final类
    • 不然一般情况下,尽量不要把一个类设置成final

final修饰方法

表示”最终的方法“

当一个方法被final修饰后,可以被继承,但是无法被重写(覆盖)

  • final修饰方法,就把方法锁住了,任何继承这个方法的类都无法覆盖该方法
  • 如果一个方法已经能够满足需求,并且明确知道它不应该被修改,修改会产生问题
    • 可以将方法设置成final方法
    • 否则,正常情况下,不要随便使用final修饰方法

需要注意的是,静态方法属于类的,它本身就无法被重写,所以无需给它加一个final修饰


final修饰变量

表示”最终的变量“,是一种自定义常量,普遍来说,命名应该采用全大写,下划线连接的方式

总体来说,final修饰变量后

  • 如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改
  • 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象
    • 因为final修饰的是引用,而引用中装的是地址
    • 但是其引用的对象的状态是可以改变的

final修饰普通成员变量

  • final修饰普通成员变量,表示常量,在整个创建对象过程中它们的值,只能修改一次也必须显式赋值一次
  • 总结给普通成员变量赋值的方式
    • 显式初始化语句
    • 构造代码块
    • 构造器
    • 对于final修饰的普通成员变量,前三种必然要有一种
  • 对于同一个类的同一个final修饰的变量,不同对象可能会有不同的值

final修饰静态成员变量(常用)

  • final修饰静态成员变量,表示全局常量,在整个静态成员赋值过程中,只能修改一次也必须显式赋值一次
    • 访问一个类的基本数据类型和String类型的final静态全局常量不触发类加载
    • 被所有对象共享,每个对象都必须有同一个值,且不可改变
  • 给final静态成员变量赋值
    • 显式赋值语句
    • 静态代码块
  • final static 还是static final?
    • 建议用static final
  • 静态常量一般全部大写,用下划线隔开

用代码证明使用static final修饰的常量不会触发类加载:

package _methodFinal;

public class TestForQuiz {
    public static void main(String[] args) {
        System.out.println(A.MAX);   //直接使用A类的静态全局变量,并未触发A的类加载
    }
}

class A{
    static final int MAX = 100;
    static{
        System.out.println("A的类加载");
    }
}

在这里插入图片描述


final修饰局部变量

  • 在方法体和方法参数列表中定义final常量,表示该常量不可更改
  • 必须显式的赋值初始化,不然也没别的方式初始化了
  • 在形参列表中声明final常量,表示该参数是一个常量,在接收后无法修改,并不意味着实际参数是常量

特别的final修饰引用数据类型

  • 表示引用指向的对象不可变,但对象的状态可变
  • 例如final修饰Student类型的变量s,则s的地址值不能改变,
  • 也就是说s要始终指向同一个对象。 但是所指对象的属性值可以改变,只要对象的地址不变即可

final修饰匿名对象 会咋样?

final new Student() 语法不允许

final修饰普通成员变量,表示常量,创建对象的时候,也只能初始化它的值一次,这个过程中也不能改了。创建对象以后,它们的值就不可改变了

注意:

  • 常量必须显示初始化,即必须赋值一次,不用能默认值。

  • 只能赋值一次,即使之后的赋值和原来的值一样也不行

杀牛用鸡刀

读下列代码,分析执行结果

public int test(final int a){ //如果传入的是常量的值,而形参的类型不是final的?
    //return a++;
    return a+1;
}
public class Test{
    public static void main(String[] args){
        Student s = new Student();
        new Test().test(s);
    }
    public void test(final Student s){
        //s = new Student();报错,因为s已经是常量了
        s.age++;   // 可以实现,因为引用虽然是常量,但是所指内容是可以改变的
    }
}
class Student{
    public int age;
}


多态的引入(polymorphic)

什么是多态?

  • 某同一个事物,在不同的时刻表现出不同的状态

    • 对于Java而言,指的是同一个类型引用,在指向不同的具体对象时,运行时表现不同的行为

    • 其实就是父类引用指向了不同的子类对象

    • 例如

      • Animal a = new Cat();
        Animal a = new Dog();
        
  • 多态的前提条件

    • 要有继承关系
    • 继承后发生子父类的方法重写
    • 父类的引用指向子类的对象

不能发生多态的情况

  • 不能够被继承,final类
  • 没有父类指向子类对象的引用
  • 不能被方法覆盖
    - final方法
    - static方法
    - private方法
    - 构造方法无法被继承
  • 当父类的引用,指向不同的实际对象时,调用方法,结果不同

多态的访问特征

发生多态后,通过引用调用成员的方式发生了改变

父类 引用 = new 子类();

  • 对于成员变量而言
    • 编译时看左边,运行时看左边
    • 也就是说成员变量是没有“多态”的(因为多态的前提就是发生方法重写)
    • 多态现象是发生在方法之间,和成员变量没有关系
  • 对于成员方法而言
    • 编译时看左边(指能够访问的范围),运行时看右边(指执行的结果)

解释

父类 引用 = new 子类();

  • 编译看左边
    • 编译看左边是说通过引用变量可以访问到的子类成员的范围
      • 是由引用类型来决定的,也就是说由父类中定义的成员决定
    • 我们只能通过引用去访问,堆上的对象,引用中必然装有该类型对象的成员信息
    • 只有通过引用变量,我才能访问到堆上的对象
      • 也就是说,对象的访问受限于引用变量本身的类型

解释方法的多态性,一个很贴切的例子

  • 我家中的一台电视机,贼贵,功能很丰富
  • 对于电视机而言,我们只能使用,遥控器去操作电视机
  • 这也就是说,只有遥控器上提供的功能我们才能使用
    • 即便电视机本身功能多么强大,如果遥控器上只有音量键,那我们也毫无办法
    • 遥控器有啥功能,决定了我们能使用的功能
  • 即使电视机本身功能再丰富,没有遥控器的支持,我们啥也用不了

把这个例子转换到Java程序中

  • 电视机就相当于对象本身,而遥控器就是引用变量
  • 实际对象的功能再强大,如果引用中没有这个功能,那也无法调用该功能
  • 对象的行为,受限于引用变量,和对象本身没有直接关系
  • 对象的引用类型决定了可以访问对象的成员范围
  • 编译时看左边,运行时看右边

简单的一个例子

package _methodFinal;

public class TestForQuiz2 {
    public static void main(String[] args) {
        Father fs = new Son();
        System.out.println(fs.a);
        System.out.println(((Son) fs).b);  // 强转    
        fs.test();

        Father fs2 = new Son2();
        System.out.println(fs2.a);
        fs2.test();

    }
}

class Father{
    int a = 999;
    public void test(){
        System.out.println("Father");
    }
}

class Son extends Father{
    int a = 888;
    int b = 1111;
    @Override
    public void test() {
        System.out.println("Son");
    }
}

class Son2 extends Father{
    int a = 777;

    @Override
    public void test() {
        System.out.println("Son2");
    }
}

运行结果

引用类型的类型转换

基本数据类型之间是可以发生数据类型转换的,引用数据类型也是可以的

但是引用数据类型发生转换的条件比较苛刻,出错后的问题也更严重

做引用数据类型的转换 小心小心再小心

引用数据类型要发生类型转换

  • 前提:具有父子关系的两个类型之间
  • 没有父子关系的两个类型之间不能发生类型转换,通过不了编译

自动类型转换

  • 子类的引用转换成父类的引用,在继承链中属于向上,编译器默认允许

    • 称之为自动类型转换或者向上转型

    • 语法

    • 父类 引用名 = new 子类();
      
    • 子类一定可以看成父类,所以能够自动转型

    • 向上转型是安全,可以放心使用

强制类型转换

  • 父类的引用转换成子类的引用,在继承链中属于向下,编译器默认不允许,需要显式强行转换

    • 称之为强制类型转换或者向下转型

    • 语法

    • 子类 引用名 = (子类)父类引用;
      
    • 子类继承和扩展了父类,父类大多数情况下都不能看成子类,所以需要强制类型转换

    • 重要前提:强制类型转换若想成功,必须是该父类引用指向的对象本身就是一个要强转的子类对象

  • 强制类型转换是不安全的,要想转型成功,必须真实的对象和要转型的类型一致

    • 父类引用指向的不一定就是那个你要强转的子类的对象
    • 比如动物类的引用指向了一个猫对象,现在把引用强转成一个狗引用,能成功吗?
  • 为了保障安全,向下转型推荐使用instanceof关键字校验

    • 语法

    • 引用名 instanceof 类名
      
    • 这个表达式返回一个布尔类型的值

      • true代表该引用指向的对象,是一个后面类名的对象
      • null instanceof 任何类 结果都是false

ClassCastException

强制类型转换一旦失败,就会抛出ClassCastException,程序报错终止

  • 没有人会故意给自己找麻烦
  • 不到万不得已,不要使用强制类型转换

代码(包含instanceof的使用)

public class Demo {
    public static void main(String[] args) {
        /*collectDogVoice(new Dog());
        collectCatVoice(new Cat());*/
       /* Dog d = new Dog();
        collectAnimalVoice(d);
        collectAnimalVoice(new Cat());
        collectAnimalVoice(new Pig());*/

        //---------------------------------------
        //测试
        Animal a = new Person();
        System.out.println(((Person) a));
        // 这三行代码写出来虽然不会报错,但是运行时会产生ClassCastException异常
        System.out.println(((Cat) a));
        System.out.println(((Dog) a));
        System.out.println(((Pig) a));
        // System.out.println(((Demo) a));

        collectAnimalVoice(new Person());
        collectAnimalVoice(new Pig());
        collectAnimalVoice(new Cat());
        collectAnimalVoice(new Dog());
    }

    //定义收集狗叫声的方法
    public static void collectDogVoice(Dog d) {
        //抓一条狗来
        d.shout();
        //收集研究
    }

    //定义收集猫叫声的方法
    public static void collectCatVoice(Cat c) {
        //抓一条猫来
        c.shout();
        //收集研究
    }

    //定义收集动物叫声的方法
    public static void collectAnimalVoice(Animal a){
        //抓一个动物来
        //如果是人,就需要加钱
        //这里显然,引用是父类的引用,该引用无法直接调用子类独有的方法
        //需要把父类引用强转成子类引用,需要做一次强制类型转换
        //我们需要判断a所指向的对象到底是不是Person对象,是就做强转,否者不做
        //用instanceof判断
        if (a instanceof Person){
            //如果a指向的是Person对象,返回true,进入if
            Person p = (Person) a;
            p.addMoney();
        }
        a.shout();
        //收集研究
    }
}

class Animal {
    public void shout() {
        System.out.println("Animal Shout");
    }
}

class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("狗叫");
    }
}

class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("猫叫");
    }
}

class Pig extends Animal{
    @Override
    public void shout() {
        System.out.println("猪叫");
    }
}
class Person extends Animal{
    @Override
    public void shout() {
        System.out.println("人叫");
    }

    //人类独特的方法
    public void addMoney(){
        System.out.println("加钱后,你可以随便");
    }
}

举一个形象的例子

​ 孔子(Confucius)装爹

孔子他爹可以教JavaSE,他爹48岁

孔子会教《论语》,孔子28岁,孔子还会打游戏

Java培训很火,有很多人请孔子爹去上课,有一天孔子爹被人请走了

但是又来了一个人来请孔子爹去讲课,给的钱很多

于是孔子就穿上他爹的衣服,沾上胡子,戴上眼镜,就开始装爹

向上转型

ConfuciusFather cf = new Confucius();

这个时候,别人问孔子,您多大岁数了啊?

孔子不敢说实话,只好说我今年48岁

cf.age = 48;

到了地方,开始上课

cf.teach();

这时候就露馅了,因为孔子不会教JavaSE,于是孔子就开始教授论语,毕竟半步论语治天下

程序员学点论语没毛病

到了下课时间,孔子很想打游戏,但是想着自己现在的身份是老爹,老爹从来不打游戏

于是孔子也不敢打游戏

// cf.playGame(); 无法调用

终于,把时间熬完了,终于下班了

孔子赶紧跑回家

脱下爹的衣服,摘下眼镜,拔掉胡子

向下转型

Confucius c = (Confucius)cf;

这个时候”变年轻“的孔子打开手机,玩了一把王者荣耀

c.age = 28;

c.playGame() ;

public class Demo {
    public static void main(String[] args) {
        ConfuciusFather cf = new Confucius();
        System.out.println(cf.age);//48
        cf.teach();//论语
        //cf.playGame(); 不能打游戏,因为父类没这个成员
        Confucius c = (Confucius) cf;
        System.out.println(c.age);//28
        c.playGame();
    }
}

class ConfuciusFather{
    int age = 48;
    public void teach(){
        System.out.println("我要教Java");
    }
}
class Confucius extends ConfuciusFather{
    int age = 28;
    public void playGame(){
        System.out.println("孔子喜欢玩大乱斗!");
    }
    @Override
    public void teach() {
        System.out.println("我会教论语");
    }
}

牛刀小试

看代码,说出执行结果

public class Demo {
    public static void main(String[] args) {
        Father f = new Son();
        System.out.println(f.a);  // 10
        System.out.println(f.b);  // 100
        System.out.println(f.c);  // 1000
        //System.out.println(f.aa); 父类中没有aa
        System.out.println("----------------");
        Son s = new Son();
        System.out.println(s.a);// 20
        System.out.println(s.b); // 200
        System.out.println(s.c); // 10000
        System.out.println(s.aa); // 20
        System.out.println("----------------");
        //f.testSon(); 父类中没有该方法
        f.test(); // son
        f.testFather(); // Only Father
        System.out.println("----------------");
        s.test(); // son
        s.testSon(); // Only Son
        s.testFather(); // only father
    }
}
class Father {
    int a = 10;
    final int b = 100;
    static final int c = 1000;
    public void test() {
        System.out.println("Father");
    }
    public void testFather() {
        System.out.println("Only Father");
    }
}
class Son extends Father {
    int a = 20;
    final int b = 200;
    static final int c = 10000;
    int aa = 20;
    @Override
    public void test() {
        System.out.println("Son");
    }
    public void testSon() {
        System.out.println("Only Son");
    }
}
  • 课下练习
  • 这个题目没有太大意义,但是比较好玩
  • 重点是要记住,只有子类父类方法参数列表完全一致时,才能构成重载,完全一致,兼容都不行
public class Demo {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        // 第6个,在匹配时发现本身有参数为D类的,更符合,所以不去用子类的
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));
    }
}

class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    }
}

class B extends A {
    public String show(B obj) {
        return ("B and B");
    }

    @Override
    public String show(A obj) {
        return ("B and A");
    }
}

class C extends B {
}

class D extends B {
}


//结果:
//1--A and A
//2--A and A
//3--A and D
//4--B and A
//5--B and A
//6--A and D
//7--B and B
//8--B and B
//9--A and D

抽象类(abstract)

思考

Animal类的继承体系,有什么问题?

  • 从代码角度上,有没有冗余代码?
  • 从设计角度上,在这个案例中,会存在一个“动物”对象吗?
class Animal {
    public void shout() {
        System.out.println("动物叫");
    }
}
class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("猫叫");
    }
}
class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("狗叫");
    }
}

我们很容易就能够发现以下问题

  • Animal类中的shout()方法,实际上是不会被调用的

    • 最好的情况是这个方法只需要声明然后被重写,不需要方法体
  • 这个案例中,并不会真的存在一个Animal对象,Animal是一个抽象的概念

    • 我们不能也不应该去描述一个“Animal”的叫声
    • 我们不会实例化一个Animal对象,这个类不是用来创建对象的,而是用来被继承的
  • 总结:

    • Animal类不应该创建实例对象,该类只需要用来被继承
    • Animal类中的方法不应该有方法体,该方法只需要用来被重写

抽象类定义

在继承体系中,从祖先类开始,随着一个个子类的定义,子类变得越来越具体

而祖先类则更具有一般性和抽象性

在这种情况下,为了体现祖先类在设计上的抽象性

我们只将该类作为派生其他类的父类,而不能创建对象实例

这个时候,这样的类,我们称之为抽象类

  • 抽象类使用abstract关键字标记

    • [访问权限修饰符] abstract class 类名{
      }
      
  • 表示该类是一个抽象类

  • 抽象类无法被实例化,抽象类不能实例化,但是开发中会提供子类,可以用多态的方式访问

  • 抽象方法使用abstract关键字标记

    • [权限修饰符] abstract 返回值类型 方法名(形参列表);
      
    • 表示该方法是一个抽象方法

    • 抽象方法必须在一个抽象类中

    • 抽象方法没有方法体,只有方法声明,不要忘记写分号

自此,我们已经引入了抽象类的概念

接下来,我们重点来研究一个抽象类的特点和使用

我们从下面两个角度分析

  • 抽象类的成员

    • 成员变量
    • 成员方法
    • 构造方法
  • 抽象类的子类

抽象类的成员特点:

  • 抽象类具有普通构造方法,成员变量和成员方法

  • 抽象类具体普通类都可以有的成员变量,静态成员变量,常量

  • 抽象类可以有抽象方法,也可以有普通方法

    • 抽象方法起着占位的作用,该方法必须在子类中实现
    • 抽象方法不能是private、static、final修饰的
    • 普通方法可以作为一个默认实现,子类可以使用这个方法,也可以自行重写
    • 抽象类中可以没有抽象方法,这样做的目的是不让创建对象
      • 但是一般不要这么做,抽象类是一个设计上的概念,应该从设计上正确的使用它
      • 如果仅仅是为了不让外界创建对象,私有化构造方法即可
      • 普遍来说,既然是抽象类就应该有抽象方法,不然没啥意义
  • 抽象类虽然不能实例化,但仍然有构造方法

    • 抽象类中的构造方法是留给子类初始化时调用的
    • Java当中,只要是类都具有构造方法
  • 总结抽象类成员

    • 构造方法:同普通类
    • 成员变量:同普通类
    • 成员方法:可以是抽象方法,也可以是非抽象方法

抽象类的子类的特点:

  • 抽象类的子类可以是抽象类,也可以是具体类
  • 只有当子类重写了,所有的继承自抽象类的方法,该子类才能被定义为具体类
  • 反之,若任一抽象方法没有被重写,该类都必须定义为抽象类

abstract关键字使用上的注意点(notice)

  • 不能用来修饰构造器、属性、代码块等结构
  • 不能用来修饰final类
    • 无法被继承的类
  • 不能用来修饰私有方法、静态方法 、final方法
    • 无法被重写的方法
  • 一个抽象类中可以没有抽象方法,但是意义不是很大

作业题

  • 结合多态发生的条件,及继承的相关知识,自己总结并验证下,哪些情况下是无法发生多态的。

多态产生的条件:要有继承,存在父类和子类,其次要有父类的引用指向子类的对象,子类中要重写父类的成员方法,父类引用要调用被重写的方法

  1. 首先成员变量不能产生多态
  2. final修饰的类不能够被继承,所以也无法产生多态
  3. 无法被重写的方法不能产生多态,比如被private,final,static修饰的方法,以及构造方法,都无法被重写,也就不存在多态
  4. 如果没有父类引用指向子类的对象,也无法产生多态

  • 问题1: 哪些方法是无法发生方法覆盖?
    被private修饰的方法是无法产生覆盖的,因为private修饰的方法虽然能继承,但是无法被子类访问到,所以不能覆盖
    构造方法不可以被继承,自然也不能重写
    static修饰的静态的方法,在类加载时就会被放置在不同的区域中独立存在,也不存在重写的关系
  • 问题2: 方法覆盖时子类的方法返回值并不要求完全一致而是兼容,请测试后详细描述.
    总的来说就是子类的方法返回值类型要小于父类方法的返回值类型,比如父类的方法返回值类型是父类A,那么子类重写方法的返回值类型可以是A或者是A的子类
    package homework;
// 验证方法重载返回值类型可以不一致但是可以兼容

public class Demo1 {
    public static void main(String[] args) {
        Son son = new Son();
        son.test();
        Father fs = new Son();
        fs.test();
    }
}
class A {}

class B extends A{}

class C extends B{}

class Father{
    public B test(){
        return new B();
    }
}
class Son extends Father{
    @Override
    public B test(){
         // return new A();   // 无法返回A类型,因为在我的代码中A是B的父类
         return new B();  // 可以返回B类型
        // return new C();    // 可以返回C,因为C是B的子类
    }
}

  • 自定义一个类,类中定义三个成员变量a,b,c,用final修饰这三个成员变量
    再定义两个静态成员变量staticA和staticB,也用final修饰这两个静态成员变量
    然后:
    1,请用三种不同的方式,分别为a,b,c赋值
    2,请用两种不同的方式,分别为staticA和staticB赋值
package homework;

public class FinalExercise {
    public static void main(String[] args) {
        Test test1 = new Test(777);
        System.out.println(test1.A);
        System.out.println(test1.B);
        System.out.println(test1.C);
        System.out.println(Test.staticA);
        System.out.println(Test.staticB);

    }
}
class Test{

    static final String staticA = "lalalala";
    static final String staticB;
    static {
         staticB = "李四";
    }
    final int A = 100;   // 显式赋值方法
    final int B;         // 构造器赋值方法
    final int C;         // 构造代码块赋值方法
    {
        C = 99;
    }
    public Test(int B){
        this.B = B;
    }
}

在这里插入图片描述


请根据题目,作出合理设计,定义如下类:
Person
属性:String name,int age
行为:eat();

SouthPerson
属性:String name,int age,double salary
行为:eat(),swim()

NorthPerson
属性:String name,int age,double salary,double height
行为:eat(),drink()

写代码实现,eat()方法的多态效果
1,人都要吃饭
2,南方人喜欢吃米饭
3,北方人喜欢吃面食

最后,在测试类中,编写测试代码,要求进行如下测试:
1,编写测试方法,要求该方法可以传入SouthPerson对象和NorthPerson对象,并调用eat()方法
2,用父类引用指向子类对象的方式创建SouthPerson对象,能否直接访问salary属性和swim()方法?
如果不能,应该怎么写代码让它能够正常调用?
3,用父类引用指向子类对象的方式创建NorthPerson对象,能否(直接或写代码)访问salary属性和swim()方法?
如果不能,将该对象引用强转为SouthPerson引用,能否成功?为什么?

public class Demo3 {
    public static void main(String[] args) {
        NorthPerson northP = new NorthPerson();
        SouthPerson southP = new SouthPerson();
        test(northP);
        test(southP);

        Person p = new SouthPerson();
        // System.out.println(p.salary); 无法直接使用p.salary
        // p.swim(); 也无法直接调用swim方法
        // 如果要使用salary和swim,必须父类向下转型,也就是类型强转
        System.out.println(((SouthPerson) p).salary);
        ((SouthPerson) p).swim();

        Person np = new NorthPerson();
        // System.out.println(((SouthPerson) np).salary); 虽然代码不报错,但是运行时会产生ClassCastException
        // np.swim(); 无法用到swim方法和SouthPerson的salary,因为两者都是Person的子类,本质上没有任何关系,强转也无法做到




    }
    public static void test(Person p){
        p.eat();
    }
}

class Person{
    String name;
    int age;
    public void eat(){
        System.out.println("人都要吃饭!");
    }
}

class NorthPerson extends Person{
    double salary = 9999.9;
    double height = 180;
    @Override
    public void eat() {
        System.out.println("北方人爱吃面食!");
    }

    public void drink(){
        System.out.println("北方人爱喝酒!");
    }
}

class SouthPerson extends Person{
    double salary = 10000;
    @Override
    public void eat() {
        System.out.println("南方人爱吃米饭!");
    }

    public void swim(){
        System.out.println("南方人爱游泳!");
    }
}

定义抽象类A,抽象类B继承A,普通类C继承B。
A类中,定义成员变量a赋值为10,抽象showA方法
B类中,定义成员变量b赋值为20,抽象showB方法
C类中,定义成员变量c赋值为30,重写showA方法打印a,重写showB方法打印b,定义showC方法,打印c
测试类中,创建C对象,调用showA方法,showB方法,showC方法

package homework;

public class Demo4 {
    public static void main(String[] args) {
        C c = new C();
        c.showA();
        c.showB();
        c.showC();
    }
}
abstract class A{
    int a = 10;
    public void showA(){
        System.out.println("showA");
    }
}

abstract class B extends A{
    int b = 20;
    public void showB(){
        System.out.println("showB");
    }
}

class C extends B{
    int c = 30;

    @Override
    public void showA() {
        System.out.println("showA a = " + a);
    }

    @Override
    public void showB() {
        System.out.println("showB b = " + b);
    }

    public void showC(){
        System.out.println("showC c = " + c);
    }
}


这篇关于Java学习 day10_oop3的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程