20172319 2018.04.11-16
《Java程序设计教程》第7周学习总结
目录
教材学习内容总结
第九章 继承:
- 创建子类:
父类(超类、基类):人;
子类(亚类、派生类):黄种人,黑种人、白种人;
父类提供大体框架,子类基于此框架又包含不同特点(eg: 不同人种的颜色);
用extends
指明新类由现有类派生,相当于“是”;
子类继承父类中的方法定义及变量声明 == 这些方法和变量都在子类中声明;(注: 子类可另定义方法和变量以凸显其特征; 继承具有单向性),类不允许多重继承。
(1)protected修饰符:
半公开,在不破坏其封装特性的条件下允许子类及同一包内的其他类引用类中的方法和变量。
注:能引用的方法及变量前必须有protected修饰符 ,子类不会继承父类的构造方法。
在UML类图中 方法和变量前加 # 号表示应用protected修饰符;
子类不能按名引用父类的private成员。
(2)super引用:
调用父类的构造方法,super(需要访问的变量)
(一般情况会在子类构造方法的第一行)——确保父类初始化自身变量在子类执行构造方法之前;
优点: 当父类中使用构造方法改变了设置变量的方式,无需再去子类的构造方法中对其进行修改。
(3)多继承:
当需要使用两种或以上的类来描述对象时使用;
弊端: 遇到父类中成员同名时不知道该继承哪一个。 - 重写方法:
在子类中可以根据需要对从基类继承来的方法进行重写;
重写方法必须和被重写方法具有相同的方法名称、参数列表和返回类型;
重写方法不能使用比被重写方法更严格的访问权限
eg:父类成员权限为private时,方法无法被重写;
注: final修饰符修饰的方法子类也无法重写
优点: 保证子类使用一个特定的方法。
影子变量:
子类中定义与父类名字相同的变量,定义合法,原则上避免。
缺点: 不便于理解代码。 类层次结构:
父类派生子类,子类又可派生其它一个或多个子类(子类个数及层数无限制);
同胞: 同一父类的两个子类;
继承具有传递性;应更合理的将公共特征置于更高层的类层级。
(1)Object类:在类的层级中处于最顶层,如果一个类定义中没有extends
指出其父类,则默认其父类为Object;程序中的任何对象都可调用Object中的public方法;
Object类常用的方法:
equals()
等价于==;toString()
返回代表对象的字符串。
(2)抽象类:
使用了关键词abstract声明的类,可以不包含操作方法;在UML类图中,用斜体表示抽象类名;
如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象);
不能对抽象方法用final
和static
修饰符:
子类无法重写final
方法,static
无需实例化就能用,但方法中无方法体,该方法无意义。
(3)接口层次结构:
接口继承接口,子类继承父类抽象方法及常量。- 可见性:
父类的私有成员能被子类间接地访问。 类间继承关系的设计:
(1)继承关系小结:
1.梳理好每次派生时“是”的关系,一次比一次更具体;
2.设计结构有利于类的复用及未来潜在软件复用;
3.应尽可能使公共属性置于较高类层次;
4.依据需求适当重写方法;
5.依据需求增加新变量(尽量避免出现影子变量);
6.类自己管理自己,进行适当super引用及重写方法;
7.接口模拟多继承;
8.考虑结构前瞻性;
9.子类中应适当重写通用方法;
10.用抽象类为低层类定义公共类接口;
11.慎用修饰符。
(2)继承的限制:
final
修饰符: 用于方法——限制重写;
用于类——限制继承。
教材学习中的问题和解决过程
- 问题1: 类不支持多重继承?
- 解决:自己说的多重继承有好处,然后又明确不支持。
自相矛盾???冲突点: 碰到两个父类中同名成员不知道该继承哪个。简单啊,改方法名不就好咯!合适的名字有利于对代码的理解,许久之后,单独翻出变了名的代码,倘若对其毫无印象,在认知上会变得很困难,所以说这是行不通的;(A是父类,BC是子类,D继承BC)先Cextends
B之后D再extends
C,emmmmm,好像有点道理,其实完全没用BC继承了A的,并在一定基础发展自己的特征,C继承了B,同名的方法会被重写,最后只是D仅仅继承了C。(B=黄种人,C=白种人,预想的D=混血;按上述操作D=白种人)怎么办呢?java能实现多个接口,接口中的方法是抽象的,它没有方法体,因此不能简单通过创建对象调用方法去实现。但这并不能说接口实现是最完美的方案;它有着不能共享实现的缺点。
-
本来只是为了跨越继承层次来共享代码,现在却需要另外生成一个独立对象,而且每次方法调用都要委派给这个对象,这实在是不太合理,而且执行的效率也不高。
——《松本行弘的程序世界》 - 问题2: 关于影子变量
- 解决:书上简单地说了这玩意儿和重写方法差不多,讲得有点玄。自己查了点资料看到这东西。
public class ParentClass
{
public int i = 10;
}
public class SubClass extends ParentClass
{
public int i = 30;
}
public static void main(String[] args)
{
ParentClass parentClass = new SubClass();
SubClass subClass = new SubClass();
System.out.println(parentClass.i + subClass.i);
}
???创建对象里是什么鬼??? 自己去玩了一波
public class h1
{
protected int i = 10;
}
public class h2 extends h1
{
public int i = 20;
}
public static void main(String [] args)
{
h1 a = new h2();
h2 b = new h2();
System.out.println(a.i+b.i);
}
结果输出是30,影子变量并不像是重写数据,它是属于子类的一个变量,名字与父类继承的重合,相当于子类有两个变量i1=10;i2=20,**所以说要尽量避免使用,** 这对代码的理解增加了难度。
a即是一个h1的对象,也是一个h2的对象,
那么调用方法时,是根据对象的实际类型调用的,
实际类型是h2,所以调用子类的方法,即i。
而访问成员变量就不同了,它是h1时,访问的是父类的成员变量i=10,
转型为h2的话,访问的就是子类的成员变量了i=20。
影子变量!=重写,而是并存,相互隐藏。
- 问题3:关于抽象类与接口
解决:抽象类好像除了可以含非抽象方法,除常量外还可声明数据 这两点外与接口并无其他区别,额外地去定义这么一个类是不是有点多余。看似是这样,实则不然:
接口实际上可以算是一个特殊的抽象类,抽象类可以说是对“本质”的定义,这样可以使得子类无需再去重新定义质的公有方法(动物类派生下的猫、狗,无需再去定义它们是动物);接口可以理解为一种“行为”,猫狗发出的“叫”是不同的,为了实现它们不同的行为,可以通过接口来定义“叫”的方法。- 问题4:接口的派生——有用?
解决:实现接口需要实现其定义里的所有方法,如果接口使用继承的话。比如说: 每个接口都定义了一个方法,而需要实现的是经过100多次继承后的接口,要实现它就要翻开所有的父类去查看接口定义的成员名字。
怕不是要累死哦!, 与其这样不如直接将100多个方法定义在一个接口里,这样编程才更有效率。
其实这与其是抽象类有关: 假如所有方法放同一接口中,规定的行为难免会让人感觉有点多了;
1.一个集合,既要定义有序集合的行为,又要定义无序集合的行为,既要定义有重复元素的集合的行为,又要定义无重复元素的集合的行为,假设只有一个方法来定义集合的行为,请问这个方法该怎么实现。千万不要说加上一大堆if else 的判断语句,如果这时候有新的集合类型加入了,难道再加一个if else语句么?这无疑这是一种糟糕极了的方法。
2.而通过接口继承产生的层级接口,层次分析,职责分明,Set就是Set,List就是List,想要实现哪种结构直接实现对应的接口即可。换个角度看,通过接口继承,可以重新定义上层已经定义的行为,也不会影响到同一层级的其他接口中的行为。
代码调试中的问题和解决过程
- 问题1:super引用时出现错误
- 解决: