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方法
- 无法被重写的方法
- 一个抽象类中可以没有抽象方法,但是意义不是很大
作业题
- 结合多态发生的条件,及继承的相关知识,自己总结并验证下,哪些情况下是无法发生多态的。
多态产生的条件:要有继承,存在父类和子类,其次要有父类的引用指向子类的对象,子类中要重写父类的成员方法,父类引用要调用被重写的方法
- 首先成员变量不能产生多态
- final修饰的类不能够被继承,所以也无法产生多态
- 无法被重写的方法不能产生多态,比如被private,final,static修饰的方法,以及构造方法,都无法被重写,也就不存在多态
- 如果没有父类引用指向子类的对象,也无法产生多态
- 问题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的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程
- 2024-11-26Springboot单体架构搭建资料:新手入门教程
- 2024-11-26Springboot单体架构搭建资料详解与实战教程
- 2024-11-26Springboot框架资料:新手入门教程
- 2024-11-26Springboot企业级开发资料入门教程
- 2024-11-26SpringBoot企业级开发资料详解与实战教程
- 2024-11-26Springboot微服务资料:新手入门全攻略