11.7动手动脑作业
1.继承条件下的构造方法调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
class Grandparent
{
public Grandparent()
{
System.out.println( "GrandParent Created." );
}
public Grandparent(String string)
{
System.out.println( "GrandParent Created.String:" + string);
}
}
class Parent extends Grandparent
{
public Parent()
{
//super("Hello.Grandparent.");①
System.out.println( "Parent Created" );
//super("Hello.Grandparent.");②
}
}
class Child extends Parent
{
public Child()
{
System.out.println( "Child Created" );
}
}
public class TestInherits
{
public static void main(String args[])
{
Child c = new Child();
}
}
|
① 结果:
② 结果:
结论:通过super调用基类构造方法,必须是子类构造方法中的第一句。
2.为什么子类构造方法运行之前,必须调用父类的构造方法?
构造方法的主要作用是初始化,如果子类先运行,没有初始化,会出错。
3.使用javap –c命令反编译。
1
2
3
4
5
6
7
8
9
|
public class ExplorationJDKSource {
/**
* @param args
*/
public static void main(String[] args) {
System.out.println( new A());
}
}
class A{}
|
结果:
反编译:
分析:main方法实际调用了public void println(Object x),这一方法内部调用了String类的valueOf()方法。
valueOf方法又调用了Object.toString方法:
public String toString(){
return getClass().getName()+”@”+
Integer.toHexString(hashCode());
}
结论:为明确继承的类,都继承类Object。
4.子类,父类的覆盖关系
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class Fruit
{
public String toString()
{
return "Fruit toString." ;
}
public static void main(String args[])
{
Fruit f= new Fruit();
System.out.println( "f=" +f);①
// System.out.println("f="+f.toString());②
}
}
|
① 结果:
② 结果:
分析:
Fruit类覆盖类Object类的toString方法。
“+”运算任何一个对象与一个String对象连接时会隐式调用toString()方法。
默认情况下方法返回“类名@+方法hashCode”。为了返回有意义的信息,子类可以重写toString()方法.所以两次结果为:f = Fruit toString。
5.调用父类被覆盖的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Hello{
public void printTest(){
System.out.println( "Hello!" );
}
}
class Hi extends Hello{
public void printTest(){
System.out.println( "Hi!" );
}
public void test(){
super .printTest(); //使用super调用父类方法
printTest();
}
}
public class Test{
public static void main(String[] args){
Hi h = new Hi();
h.test();
}
}
|
结果:
6.类型转换,运行代码
class Mammal{}
class Dog extends Mammal {}
class Cat extends Mammal{}
public class TestCast
{
public static void main(String args[])
{
Mammal m;
Dog d=new Dog();
Cat c=new Cat();
m=d;
d=m;//会报错
d=(Dog)m;
d=c;//会报错
c=(Cat)m;
}
}
结果:
7.运行代码思考问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class Parent{
public int myValue= 100 ;
public void printValue() {
System.out.println( "Parent.printValue(),myValue=" +myValue);
}
}
class Child extends Parent{
public int myValue= 200 ;
public void printValue() {
System.out.println( "Child.printValue(),myValue=" +myValue);
}
}
猜想以下代码运行结果:
public class ParentChildTest {
public static void main(String[] args) {
Parent parent= new Parent();
parent.printValue();
Child child= new Child();
child.printValue();
parent=child;
parent.printValue();
parent.myValue++;
parent.printValue();
((Child)parent).myValue++;
parent.printValue();
}
}
|
猜想:100 200 200 201 202
结果:
分析:
Parent parent=new Parent();
parent.printValue(); 调用了父类方法输出为100
Child child=new Child();
child.printValue();调用子类方法输出为200
parent=child;子类赋给父类
parent.printValue();输出200
parent.myValue++;父类的myValue+1 为101
parent.printValue();这里输出的是子类赋给父类的方法输出200
((Child)parent).myValue++;父类强转为子类+1 为201
parent.printValue();输出201
总结:
① 当子类与父类拥有一样的方法,并且让一个父类变量引用一个子类对象时,到底调用哪个方法,由对象自己的“真实”类型所决定,这就是说:对象是子类型的,它就调用子类型的方法,是父类型的,它就调用父类型的方法。
② 如果子类与父类有相同的字段,则子类中的字段会代替或隐藏父类的字段,子类方法中访问的是子类中的字段(而不是父类中的字段)。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。
③ 如果子类被当作父类使用,则通过子类访问的字段是父类的。
8.多态代码生成字节指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
class Parent
{
public int value= 100 ;
public void Introduce()
{
System.out.println( "I'm father" );
}
}
class Son extends Parent
{
public int value= 101 ;
public void Introduce()
{
System.out.println( "I'm son" );
}
}
class Daughter extends Parent
{
public int value= 102 ;
public void Introduce()
{
System.out.println( "I'm daughter" );
}
}
public class TestPolymorphism
{
public static void main(String args[])
{
Parent p= new Parent();
p.Introduce();
System.out.println(p.value);
p= new Son();
p.Introduce();
System.out.println(p.value);
p= new Daughter();
p.Introduce();
System.out.println(p.value);
}
}
|
多态实现:
JAVA使用了后期绑定的概念。当向对象发送消息时,在编译阶段,编译器只保证被调用方法的存在,并对调用参数和返回类型进行检查,但是并不知道将被执行的确切代码,被调用的代码直到运行时才能确定。
将一个方法调用同一个方法主体关联起来被称作绑定,JAVA中分为前期绑定和后期绑定(动态绑定或运行时绑定),在程序执行之前进行绑定(由编译器和连接程序实现)叫做前期绑定,因为在编译阶段被调用方法的直接地址就已经存储在方法所属类的常量池中了,程序执行时直接调用,具体解释请看最后参考资料地址。后期绑定含义就是在程序运行时根据对象的类型进行绑定,想实现后期绑定,就必须具有某种机制,以便在运行时能判断对象的类型,从而找到对应的方法,简言之就是必须在对象中安置某种“类型信”,JAVA中除了static方法、final方法(private方法属于)之外,其他的方法都是后期绑定。后期绑定会涉及到JVM管理下的一个重要的数据结构——方法表,方法表以数组的形式记录当前类及其所有父类的可见方法字节码在内存中的直接地址。
动态绑定具体的调用过程为:
1.首先会找到被调用方法所属类的全限定名
2.在此类的方法表中寻找被调用方法,如果找到,会将方法表中此方法的索引项记录到常量池中(这个过程叫常量池解析),如果没有,编译失败。
3.根据具体实例化的对象找到方法区中此对象的方法表,再找到方法表中的被调用方法,最后通过直接地址找到字节码所在的内存空间。
9.多态含义和用途
让我们看一个开发场景:
某动物园有一饲养员小李,
每天需要给他所负责饲养的狮子、猴子和鸽子喂食。
请用一个程序来模拟他喂食的过程。
①三种动物对应三个类,每个类定义一个eat()方法,表示吃饲养员给它们的食物。
再设计一个Feeder类代表饲养员,其name字段保存饲养员名字,三个方法分别代表喂养三种不同的动物,其参数分别引用三种动物对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
public class Zoo
{
public static void main(String args[])
{
Feeder f = new Feeder( "小李" );
// 饲养员小李喂养一只狮子
f.feedLion( new Lion());
// 饲养员小李喂养十只猴子
for ( int i = 0 ; i < 10 ; i++)
{
f.feedMonkey( new Monkey());
}
// 饲养员小李喂养5只鸽子
for ( int i = 0 ; i < 5 ; i++)
{
f.feedPigeon( new Pigeon());
}
}
}
class Feeder
{
public String name;
public Feeder(String name)
{
this .name = name;
}
public void feedLion(Lion l)
{
l.eat();
}
public void feedPigeon(Pigeon p)
{
p.eat();
}
public void feedMonkey(Monkey m)
{
m.eat();
}
}
class Lion
{
public void eat()
{
System.out.println( "我不吃肉谁敢吃肉!" );
}
}
class Monkey
{
public void eat()
{
System.out.println( "我什么都吃,尤其喜欢香蕉。" );
}
}
class Pigeon
{
public void eat()
{
System.out.println( "我要减肥,所以每天只吃一点大米。" );
}
}
|
这种编程方式有什么不合理的地方?
每次喂食都要创建一次类。重复步骤多。
①引入继承
定义一个抽象基类Animal,其中定义一个抽象方法eat(),三个子类实现这个抽象方法。
Feeder类的三个喂养方法现在可以合并为一个feedAnimal()方法,注意它接收一个类型为Animal参数,而不是三个具体的动物类型。
依据多态特性,此方法将可以接收任何一个派生自Animal类的子类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
public class Zoo
{
public static void main(String args[])
{
Feeder f = new Feeder( "小李" );
//饲养员小李喂养一只狮子
f.feedAnimal( new Lion());
//饲养员小李喂养十只猴子
for ( int i = 0 ; i < 10 ; i++)
{
f.feedAnimal( new Monkey());
}
//饲养员小李喂养5只鸽子
for ( int i = 0 ; i < 5 ; i++)
{
f.feedAnimal( new Pigeon());
}
}
}
class Feeder
{
public String name;
Feeder(String name)
{
this .name = name;
}
public void feedAnimal(Animal an)
{
an.eat();
}
}
abstract class Animal
{
public abstract void eat();
}
class Lion extends Animal
{
public void eat()
{
System.out.println( "我不吃肉谁敢吃肉!" );
}
}
class Monkey extends Animal
{
public void eat()
{
System.out.println( "我什么都吃,尤其喜欢香蕉。" );
}
}
class Pigeon extends Animal
{
public void eat()
{
System.out.println( "我要减肥,所以每天只吃一点大米。" );
}
}
|
①进一步优化喂养一群动物
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package zoo3;
public class Zoo
public static void main(String args[]) {
Feeder f = new Feeder( "小李" );
Animal[] ans = new Animal[ 16 ];
//饲养员小李喂养一只狮子
ans[ 0 ] = new Lion();
//饲养员小李喂养十只猴子
for ( int i = 0 ; i < 10 ; i++) {
ans[ 1 + i] = new Monkey();
}
//饲养员小李喂养5只鸽子
for ( int i = 0 ; i < 5 ; i++) {
ans[ 11 + i] = new Pigeon();
}
f.feedAnimals(ans);
}
}
class Feeder {
public String name;
Feeder(String name) {
this .name = name;
}
public void feedAnimals(Animal[] ans) {
for (Animal an : ans) {
an.eat();
}
}
}
abstract class Animal {
public abstract void eat();
}
class Lion extends Animal {
public void eat() {
System.out.println( "我不吃肉谁敢吃肉!" );
}
}
class Monkey extends Animal {
public void eat() {
System.out.println( "我什么都吃,尤其喜欢香蕉。" );
}
}
class Pigeon extends Animal {
public void eat() {
System.out.println( "我要减肥,所以每天只吃一点大米。" );
}
}
|
④第二次重构之后,Feeder类的feedAnimals()方法接收的是一个Animal数组,这有一个限制,就是只能创建固定个数的数组,无法动态地增减动物个数。
想想以下场景:
(1)动物园新进了一些动物
(2)某动物生病不幸死亡
(3)……
我们的代码能否应付以上的场景?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
import java.util.Vector;
public class Zoo {
public static void main(String args[]) {
Feeder f = new Feeder( "小李" );
Vector<Animal> ans = new Vector<Animal>();
//饲养员小李喂养一只狮子
ans.add( new Lion());
//饲养员小李喂养十只猴子
for ( int i = 0 ; i < 10 ; i++) {
ans.add( new Monkey());
}
//饲养员小李喂养5只鸽子
for ( int i = 0 ; i < 5 ; i++) {
ans.add( new Pigeon());
}
f.feedAnimals(ans);
}
}
class Feeder {
public String name;
Feeder(String name) {
this .name = name;
}
//Vector<T>是JDK中提供的一个对象集合,可以随时向其中加入或移除对象
public void feedAnimals(Vector<Animal> ans) {
for (Animal an : ans) {
an.eat();
}
}
}
abstract class Animal {
public abstract void eat();
}
class Lion extends Animal {
public void eat() {
System.out.println( "我不吃肉谁敢吃肉!" );
}
}
class Monkey extends Animal {
public void eat() {
System.out.println( "我什么都吃,尤其喜欢香蕉。" );
}
}
class Pigeon extends Animal {
public void eat() {
System.out.println( "我要减肥,所以每天只吃一点大米。" );
}
}
|
总结:
多态编程有两种主要形式:
(1)继承多态:示例程序使用的方法
(2)接口多态:使用接口代替抽象基类。
使用多态最大的好处是:
当你要修改程序并扩充系统时,你需要修改的地方较少,对其它部分代码的影响较小!千万不要小看这两个“较”字!程序规模越大,其优势就越突出。
转载于:https://www.cnblogs.com/yang-qiu/p/9904889.html
11.7动手动脑作业相关推荐
- Java之动手动脑(三)
日期:2018.10.12 星期五 博客期:017 这次留了两个动手动脑作业!我需要一个一个来说!先说第一个吧! Part 1 :随机生成1000个随机数 代码: 1 //以下为 RandomMake ...
- JAVA语法基础 动手动脑及课后作业
动手动脑1: 仔细阅读示例: EnumTest.java,运行它,分析运行结果? public class EnumTest { public static void main(String[] ar ...
- 第二次作业动手动脑的解答
第一个动手动脑让我看老师发 的文件,EnumTest.java.猜它 的运行结果. 我猜嘛,s和 t肯定引用的不同对象,老师上课也讲过是吧. falsefalsetrueSMALLMEDIUMLARG ...
- JAVA语法基础作业——动手动脑以及课后实验性问题(一)
一.枚举类型 public class EnumTest { public static void main(String[] args) { Size s=Size.SMALL; Si ...
- JAVA课上动手动脑问题以及课后测试1,2总结
课堂测试1 像二柱子那样,花二十分钟写一个能自动生成30道小学四则运算题目的 "软件" 2 (1)题目避免重复 (2)可定制(数量/打印方式) 设计思路 1)产生两 ...
- 04-异常处理-动手动脑
1.请阅读并运行AboutException.java示例,然后通过后面的几页PPT了解Java中实现异常处理的基础知识. import javax.swing.*;classAboutExcepti ...
- 动手动脑-Java重载
有以下例子: 例: Using overloaded methods public class MethodOverload { public static void main(String[] ar ...
- 04_类与对象_课程动手动脑问题以及课后实验性问题及解答集锦
Answer: 动手动脑: 1--以下代码为何无法通过编译?哪儿出错了? Answer: 因为类Foo的构造函数是有一个参数的,所以我们在new一个Foo类的对象时必须赋予一个符合条件的实参. 2-- ...
- 动手动脑及课后实践3
1. 动手动脑1: (一)运行结果: 分析原因:在Java中,内容相同的字串常量("Hello")只保存一份以节约内存,所以s0,s1,s2实际上引用的是同一个对象. 编译器 ...
最新文章
- NOIP200905潜伏者
- NIO 之 Channel
- Linux--网卡聚合简单脚本(bond0)
- jQuery 教程01——jQuery安装
- Python replace() 和 re.sub() 字符串字符替换
- 在windows上删除linux文件夹,java-如何从Linux删除远程Windows中的文件夹
- 这可能是有史以来最详细的JNI入门教程
- c#餐饮系统打印机_C# 实现打印机功能
- 雷云3计算机不满足系统要求,电工进网作业许可考试(高压类)4、5、6章题库(无答案)...
- linux微信登陆失败,微信登陆失败原因与解决方法
- 电脑中的cache文件夹可以删除吗?
- fu7推挽胆机音质_终于绕完FU7推挽胆机的输出牛,做了些简单的测试
- SAP HANA Temporal Table (历史表)
- layui表格(table)排序
- Proxifier使用代理ip教程
- 微信小程序自定义键盘|仿微信支付数字键盘
- 万能数据库查询分析器使用技巧之(十三)
- ubuntu16.0 ROS(介绍EAI的YDLIDAR-X4激光雷达在ROS下使用方法)
- 自媒体如何写爆文,这3个网站让你更吸睛
- 调用新浪接口下载实时股票数据
热门文章
- 电脑清理代码_网络安全宣传周丨电脑中病毒,这样处置!
- 百度怎么进行模糊搜索_怎么在百度上准确搜索,提高10倍效率?
- 机器学习中的算法:决策树模型组合之GBDT(Gradient Boost Decision Tree)
- 从决策树学习谈到贝叶斯分类算法、EM、HMM - 结构之法 算法之道
- c语言实现克鲁斯卡尔算法,跪求C语言代码纠错,急~~~,克鲁斯卡尔算法
- python acme_使用 acme-tiny 工具生成 Let's Encrypt 的免费 SSL 证书
- 扬州大学广陵学院c语言试卷,扬州大学广陵学院交直流调速复习题答案(试卷)
- 粉碎文件软件测试大乐,软件测试基础(面试)(27页)-原创力文档
- java rtmp录制视频_red5-rtmp-push
- mysql与其他数据库的区别_mysql与其他数据库的区别