第六章:访问权限控制
本章讨论了类是如何被构建成类库的:首先,介绍了一组类是如何被打包到一个类库中的;其次,类是如何控制对其成员访问的。在Java中,关键字package、包的命名模式和关键字import,可以使你对名称进行完全的控制,因此名称冲突的问题是很容易避免的。
控制对成员的访问权限有两个原因:第一是为了使用户不要碰触那些他们不该碰触的部分,这些部分对于类内部的操作是必要的,但是它并不属于客户端程序员所需接口的一部分。因此将方法和域指定为private,对客户端程序员而言是一种服务。二是为了让类库设计者可以更改类的内部工作方式,而不必担心这样会对客户端程序员产生重大的影响。
第七章:复用类
在本章介绍了两种代码重用机制,分别是组合和继承。在新的类中产生现有类的对象,由于新的类是由现有类的对象组成,所以这种方法称为组合。该方法只是复用了现有程序代码的功能。第二种方式则是按照现有类的类型来创建新类,无需改变现有类的形式,采用现有类的形式并在其中添加新的代码,这种方式称为继承。
在使用继承时,由于导出类具有基类接口,因此它可以向上转型至基类,这对多态来说至关重要。
final关键字
可能使用到final的三种情况:属性,方法和类。
final属性:对于基本类型,final使数值恒定不变;而用于对象引用,final使引用恒定不变。一但引用被初始化指向一个对象,就无法再把它改为指向另外一对象,然而,对象其自身却是可以被修改的。
final方法:把方法锁定,以防任何继承类修改它的含义。(类中所有的private方法都是隐式地指定为是final的,由于无法取用private方法,所以也就无法在导出类中覆盖它。当然你可以对private方法添加final修饰,但这并不能给该方法增加任何额外的意义)
final类:当将某个类的整体定义为final时,就表明了你不打算继承该类,而且也不允许别人这么做 。换句话说,出于某种考虑,你对该类的设计永不需要做任何变动,或者出于安全的考虑,你不希望它有子类。(由于final类禁止继承,所以final类中的所有方法都隐式指定为是final的,因为无法覆盖他们。在final类中可以给方法添加final修饰词,但这并不会增添任何意义。)
第八章:多态
“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节“私有化”把接口和实现分离开来。多态的作用则是消除类型之间的耦合关系,由于继承允许将对象视为他自己本身的类型或其基类型来加以处理,因此它允许将许多种类型(从同一基类导出的)视为同一类型来处理,而同一份代码也就可以毫无差别地运行在这些不同类型之上了。
方法调用绑定
将一个方法调用 同 一个方法主体关联起来被称作绑定。若在程序执行前进行绑定,就叫做前期绑定(面向过程语言的默认绑定方式)。若在程序运行时根据对象的类型进行绑定就叫做后期绑定(也叫动态绑定和运行时绑定)。
Java中除了static方法和final方法(private方法属于final方法)之外,其他的所有方法都是后期绑定。由于Java中所有方法都是通过动态绑定来实现多态,我们就可以编写只与基类打交道的程序代码,并且这些代码对所有的导出类都可以正确运行。或者换一种说法,发送消息给某个对象,让该对象去断定应该做什么事。
构造器和多态
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用,这样做是有意义的,因为构造器具有一项特殊任务:检查对象是都被正确构造。导出类只能访问它自己的成员,不能访问基类中的成员(基类成员通常是private类型)。只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。因此,必须令所有的构造器都得到调用,否咋就不能可能正确构造完整对象。这正是编译器为什么要强制每个导出类部分都必须调用构造器的原因。
让我们来看看下面这个例子,他展示了组合、继承以及多态在构建顺序上的作用:
public class Sandwich extends PortableLunch{
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
Sandwich() {
System.out.println("sandwich()");
}
public static void main(String[] args) {
new Sandwich();
}
}
class Meal {
Meal() {
System.out.println("Meal()");
}
}
class Bread {
Bread() {
System.out.println("Bread()");
}
}
class Cheese {
Cheese() {
System.out.println("Cheese()");
}
}
class Lettuce {
Lettuce() {
System.out.println("Lettuce()");
}
}
class Lunch extends Meal {
Lunch() {
System.out.println("Lunch()");
}
}
class PortableLunch extends Lunch {
PortableLunch() {
System.out.println("PortableLunch()");
}
}
/* Output:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
sandwich()
*/
复杂对象调用构造器要遵照如下顺序:
调用基类的构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最底层的导出类。
按声明顺序调用成员的初始化方法
调用导出类的构造器主体
构造器内部的多态方法的行为:构造器调用的层次结构带来了一个有趣的两难问题,如果在一个构造器的内部调用正在构造的对象的某个动态绑定方法,那会发生什么情况呢?一个动态绑定的方法调用会向外深入到继承层次结构内部,它可以调用导出类里的方法。如果我们是在构造器内部这样做,那么就可能会调用某个方法,而这个方法所操作的成员变量可能还未进行初始化——这肯定会招致灾难,如下例:
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
class Glyph{
void draw() {
System.out.println("Glyph.draw()");
}
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
this.radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
}
@Override
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
/* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*/
由该示例可以看出,上面说的初始化顺序并不完整,初始化实际过程的第一步应该是:在其它任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
构造器的编写准则:用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法。在构造器内唯一能够安全调用的那些方法就是基类中的final方法(也适用于private方法)。
标签:Java,思想,编程,System,构造,println,方法,final,out From: https://www.cnblogs.com/LvJinshuai/p/16998354.html