Java Knowledge series 4

倖福魔咒の 提交于 2020-01-01 02:03:57

JVM & Bytecode

Has-a or Is-a relationship(inheritance or composition)

  1. 如果想利用新类内部一个现有类的特性,而不想使用它的接口,通常应选择合成。也就是说,我们可嵌入一个对象,使自己能用它实现新类的特性。但新类的用户会看到我们已定义的接口,而不是来自嵌入对象的接口。考虑到这种效果,我们需在新类里嵌入现有类的private对象。有些时候,我们想让类用户直接访问新类的合成。也就是说,需要将成员对象的属性变为public。成员对象会将自身隐藏起来,所以这是一种安全的做法。
  2. 如选择继承,就需要取得一个现成的类,并制作它的一个特殊版本。通常,这意味着我们准备使用一个常规用途的类,并根据特定的需求对其进行定制。只需稍加想象,就知道自己不能用一个车辆对象来合成一辆汽车——汽车并不“包含”车辆;相反,它“属于”车辆的一种类别。“属于”(belong to, pertain to)关系是用继承来表达的 is-a relationship,而“包含”(cover, contain, include)关系是用合成来表达的has-a relationship。
  3. 现在我们已理解了继承的概念,protected这个关键字最后终于有了意义。在理想情况下,private成员随时都是“私有”的,任何人不得访问。但在实际应用中,经常想把某些东西深深地藏起来,但同时允许访问衍生类的成员。protected关键字可帮助我们做到这一点。它的意思是“它本身是私有的,但可由从这个类继承的任何东西或者同一个包内的其他任何东西访问”。也就是说,Java中的protected会成为进入“友好”状态。
  4. 继承的一个好处是它支持“累积开发”,允许我们引入新的代码,同时不会为现有代码造成错误。这样可将新错误隔离到新代码里。
  5. 继承最值得注意的地方就是它没有为新类提供方法。继承是对新类和基础类之间的关系的一种表达is-a。可这样总结该关系:“新类属于现有类的一种类型”。这种表达并不仅仅是对继承的一种形象化解释,继承是直接由语言提供支持的。
  6. 由于造型的方向是从衍生类到基础类,箭头朝上,所以通常把它叫作“上溯造型”up cast,即Upcasting。上溯造型肯定是安全的,因为我们是从一个更特殊的类型到一个更常规的类型。换言之,衍生类是基础类的一个超集。它可以包含比基础类更多的方法,但它至少包含了基础类的方法。进行上溯造型的时候,类接口可能出现的唯一一个问题是它可能丢失方法,而不是赢得这些方法。
  7. 将数据和方法统一封装到一个类里,并且使用那个类的对象。有些时候,需通过“合成”(compose, comprise)技术用现成的类来构造新类 has-a relationship。而继承是最少见的一种做法。因此,尽管继承在学习OOP的过程中得到了大量的强调,但并不意味着应该尽可能地到处使用它。

Final Keywords

  1.  但它最一般的意思就是声明“这个东西不能改变”。之所以要禁止改变,可能是考虑到两方面的因素:设计或效率。由于这两个原因颇有些区别,所以也许会造成final关键字的误用。The variant with final keywords can be saved at ROM or use inline mechanism for final methods in order to get better efficiency.
  2. 常数主要应用于下述两个方面:(1) 编译期常数,它永远不会改变. Const (final) variable maybe is allocated in stack, never be changed.(2) 在运行期初始化的一个值,我们不希望它发生变化.
  3. 对于基本数据类型,final会将值变成一个常数;但对于对象句柄,final会将句柄变成一个常数。Handle is a const variable.进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。Java对此未提供任何手段,可将一个对象直接变成一个常数(但是,我们可自己编写一个类,使其中的对象具有“常数”效果)。这一限制也适用于数组,它也属于对象。Handle of object will never be changed, but the content which handle pointer to could be changed.
  4. Public表示它们可在包外使用;Static强调它们只有一个;而final表明它是一个常数。
  5. 现在强行要求我们对final进行赋值处理——要么在定义字段时使用一个表达 式,要么在每个构建器中。这样就可以确保final字段在使用前获得正确的初,允许我们将自变量设成final属性,方法是在自变量列表中对它们进行适当的声明。这意味着在一个方法的内部,我们不能改变自变量句柄指向的东西。
  6. 之所以要使用final方法,可能是出于对两方面理由的考虑。第一个是为方法“上锁”,防止任何继承类改变它的本来含义。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。
  7. 采用final方法的第二个理由是程序执行的效率。将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。Like inline function in C++.
  8. 类内所有private方法都自动成为final。由于我们不能访问一个private方法,所以它绝对不会被其他方法覆盖(若强行这样做,编译器会给出错误提示)。可为一个private方法添加final指示符,但却不能为那个方法提供任何额外的含义。
  9. 如果说整个类都是final(在它的定义前冠以final关键字),就表明自己不希望从这个类继承,或者不允许其他任何人采取这种操作。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类化(子类处理)。除此以外,我们或许还考虑到执行效率的问题,并想确保涉及这个类各对象的所有行动都要尽可能地有效。
  10. 将类定义成final后,结果只是禁止进行继承——没有更多的限制。然而,由于它禁止了继承,所以一个final类中的所有方法都默认为final。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为final一样,编译器此时有相同的效率选择。可为final类内的一个方法添加final指示符,但这样做没有任何意义。
  11. 首次使用的地方也是static初始化发生的地方。装载的时候,所有static对象和static代码块都会按照本来的顺序初始化(亦即它们在类定义代码里写入的顺序)。当然,static数据只会初始化一次。Static variable is initianlized before constructor invocation.
  12. 在装载过程中,装载程序注意它有一个基础类(即extends关键字要表达的意思),所以随之将其载入。无论是否准备生成那个基础类的一个对象,这个过程都会发生。若基础类含有另一个基础类,则另一个基础类随即也会载入,以此类推.

Polymorphism

  1. “对于面向对象的程序设计语言,多型性是第三种最基本的特征(前两种是数据抽象和继承. Data abstract(Encapsulation), inheritance; polymorphism;
  2. “多形性”(Polymorphism)从另一个角度将接口从具体的实施细节中分离separate出来,亦即实现了“是什么”与“怎样做”两个模块的分离。
  3. Separate interface from implementation and insert a indirection to get more flexibility in face of frequent changes.
  4. 大家要由浅入深地学习有关多形性的问题(也叫作动态绑定dynamic binding、推迟绑定later binding或者运行期绑定run-time binding)
  5. 大家已知道可将一个对象作为它自己的类型使用,或者作为它的基础类型的一个对象使用。取得一个对象句柄,并将其作为基础类型句柄使用的行为就叫作“上溯造型” up casting——因为继承树的画法是基础类位于最上方.
  6. 将一个方法调用同一个方法主体连接到一起就称为“绑定”(Binding)。若在程序运行以前执行绑定(由编译器和链接程序,如果有的话),就叫作“早期绑定”early binding/ static binding/compiling time binding。大家以前或许从未听说过这个术语,因为它在任何程序化语言里都是不可能的。C编译器只有一种方法调用,那就是“早期绑定” early binding。
  7. 解决的方法就是“后期绑定”,它意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫作“动态绑定”或“运行期绑定”。若一种语言实现了后期绑定,同时必须提供一些机制mechanism,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。
  8. A pointer to a table of function pertaining to derived class.
  9. Pointer to type info which may be inserted in the first slot in vtbl.
  10. Java中绑定的所有方法都采用后期绑定技术 (except for final function),除非except/ apart from一个方法已被声明成final。这意味着我们通常不必决定是否应进行后期绑定——它是自动发生的automatically。为什么要把一个方法声明成final呢?它能防止prevent其他人覆盖override那个方法。但也许更重要的一点是,它可有效地“关闭”动态绑定 turn off dynamic binding,或者告诉编译器不需要进行动态绑定。这样一来,编译器就可为final方法调用生成效率更高的代码(inline function or through stack unwinding)。
  11. 知道Java里绑定的所有方法都通过后期绑定具有多形性以后,就可以相应地编写自己的代码,令其与基础类沟通。此时,所有的衍生类都保证能用相同的代码正常地工作。
  12. “过载” overloaded是指同一样东西在不同的地方具有多种含义;而“覆盖”override是指它随时随地都只有一种含义,只是原先的含义完全被后来的含义取代了rewrite/redefine。
  13. 之所以要建立这个通用接口,唯一的原因就是它能为不同的子类型作出不同的表示。它为我们建立了一种基本形式,使我们能定义在所有衍生类里“通用”的一些东西。为阐述这个观念,另一个方法是把Instrument称为“抽象基础类”(简称“抽象类” abstract class)。若想通过该通用接口处理一系列类,就需要创建一个抽象类。对所有与基础类声明的签名相符的衍生类方法,都可以通过动态绑定机制进行调用.
  14. 如果方法名与基础类相同,但自变量或参数不同,就会出现过载现象,那或许并非我们所愿意的)。Overloaded not override.
  15. 构建器和多形性.同往常一样,构建器与其他种类的方法是有区别的。在涉及到多形性的问题后,这种方法依然成立。尽管构建器并不具有多形性(即便可以使用一种“虚拟构建器”——将在第11章介绍),但仍然非常有必要理解构建器如何在复杂的分级结构中以及随同多形性使用。这一理解将有助于大家避免陷入一些令人不快的纠纷。
  16. 构建器的调用遵照下面的顺序:(1) 调用基础类构建器。这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然后是下一个衍生类,等等。直到抵达最深一层的衍生类。Parent Class constructor. Super in Java.(2) 按声明顺序调用成员初始化模块。(3) 调用衍生构建器的主体。
  17. 构建器调用的顺序是非常重要的。进行继承时,我们知道关于基础类的一切,并且能访问基础类的任何public和protected成员。这意味着当我们在衍生类的时候,必须能假定基础类的所有成员都是有效的。采用一种标准方法,构建行动已经进行,所以对象所有部分的成员均已得到构建。但在构建器内部,必须保证使用的所有成员都已构建。为达到这个要求,唯一的办法就是首先调用基础类构建器。然后在进入衍生类构建器以后,我们在基础类能够访问的所有成员都已得到初始化。此外,所有成员对象(亦即通过合成方法置于类内的对象)在类内进行定义的时候(比如上例中的b,c和l),由于我们应尽可能地对它们进行初始化,所以也应保证构建器内部的所有成员均为有效。
  18. In constructor, only the method of class can be called, not of derived class, so this function invoked in constructor will be bound statically.
  19. 但在进行初始化的时候,必须覆盖衍生类中的finalize()方法——如果已经设计了某个特殊的清除进程,要求它必须作为垃圾收集的一部分进行。覆盖衍生类的finalize()时,务必记住调用finalize()的基础类版本。否则,基础类的初始化根本不会发生。
  20. 用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法 in constructor。在构建器内唯一能够安全调用的是在基础类中具有final属性的那些方法(也适用于private方法,它们自动具有final属性)。Final function can not be override or overloaded in derived calss.这些final方法不能被覆盖,所以不会出现上述潜在的问题。Turn off dynamic binding.
  21. Polymorphism provides another dimension of separation of interface from implementation, to decouple what from how, to allows improved code organization and readability as well as the creation of extensible programs that can be ‘grown’ not only during the original creation of the project, but also when new features are desired.
  22. The polymorphic method call allows one type to express its distinction from another, similar type, as long as they are both derived from the same base type. This distinction is expressed through differences in behavior of the methods that you can call through the base class.
  23. Taking an object reference and treating it as a reference to its base class type is called upcating because of the way inheritance trees and drawn with the base class at the top.
  24. Most programmers who come from a procedural programming backgaround have a bit of trouble with the way polymorphism works.
  25. To get a deeper understanding of the works of polymorphism is helpful to examine the subject of binding.
  26. When a language implements late binding, there must be some mechanism to determine the type of the object at run time and to call the appropriate method. That is, the compiler still doesn’t know the object ype, but the method-call mechanism finds out and calls the correct method body. The late-binding mechanism varies from language to language, but you can imagine that some sort of type information must be installed in the objects. (vptr a pointer to vtble).
  27. All method binding in Java uses late binding unless the method is static or final (private methods are implicitly final).
  28. ‘Final’ prevents anyone from overriding this method, perhaps more important, it effectively turn off dynamic binding, or that it tells the compiler that dynamic binding is not necessary.
  29. In a well-designed OOP program, most or all of your methods will follow the model of using base class as parameter and communicate only with the base-class interface.
  30. The only reason to establish this common interface is so it can be expressed differently for each different subtype. It establishes a basic form, so you can say what is in common with all the derived classes.
  31. If the method’s name is same as the base class but the arguments are different, you have got overloading, which probably isn’t what you want.
  32. It is totally different from C++ where the method with different argument in derived class hides all the methods with same name in base class. Never overload the virtural methods in C++.
  33. A class containing abstract methods is called an abstract class. If a class contains one or more abstact methods, the class itself must be qualified as abstract. Creating an object of an abstract class is not allowed by compiler.
  34. If you inherit from an abstract class and you want to make objects of the new type, you must provide method definitions for all the abstract methods in the base class. If you don’t (and you may choose not to), then the derived class is also abstract, and the compiler will force you to qualify that class with the abstract keyword.
  35. It’s possible to create a class as abstract without including any abstract methods. This is useful when you’ve got a class in which it doesn’t make sense to have any abstract methods, and yet you want to prevent any instances of that class.
  36. It is helpful to create abstract classes and methods because they make the abstractness of a class explicit, and tell both the user and the compiler how it was intended to be used.
  37. As usual, constructors are different from other kinds of methods. This is also true when polymorphism is involved. Even though constructors are not polymorphic (they’re actually static methods, but the static declaration is implicit), it’s important to understand the way constructors work in complex hierarchies and with polymorphism. This understanding will help you avoid unpleasant entanglements.
  38. A derived class has access to its own member only, and not to those of the base class (whose members are typically private). Only the base-class constructor has the proper knowledge and access to initialize its own elements.
  39. Order of constructor calls: 1. The base-class constructor is called in recursive way. 2. Member initializers are called in the order of declaration. 3. The body of the derived-class constructor is called.
  40. Inside the constructor, however, you must be able to assume that all members that you use have been built. The only way to guarantee this is for the base-class constructor to be called first. Then when you are in the derived-class constructor, all the members you can access in the base class have been initialized.
  41. The order of disposal should be the reverse of the order of initialization, in case one subobject is dependent on another.
  42. For fields, this means the reverse of the order of declaration (since fields are initialized in declaration order).
  43. For base classes (following the form that’s used in C++ for destructors), you should perform the derived-class cleanup first, then the base-class cleanup. That’s because the derived-class cleanup could call some methods in the base class that require the base-class components to be alive, so you must not destroy them prematurely.
  44. Conceptually, the constructor’s job is to bring the object into existence (which is hardly an ordinary feat). Inside any constructor, the entire object might be only partially formed—you can know only that the base-class objects have been initialized, but you cannot know which classes are inherited from you. A dynamically bound method call, however, reaches “outward” into the inheritance hierarchy. It calls a method in a derived class. If you do this inside a constructor, you call a method that might manipulate members that haven’t been initialized yet—a sure recipe for disaster.
  45. The only safe methods to call inside a constructor are those that are final in the base class. Never call the methods which are possible to be overridden in derived class.
  46. A better approach is to choose composition first, especially when it’s not obvious which one you should use. Composition does not force a design into an inheritance hierarchy. But composition is also more flexible since it’s possible to dynamically choose a type (and thus behavior) when using composition, whereas inheritance requires an exact type to be known at compile time.
  47. If you choose inheritance first when you are using an existing class to make a new class, things can become needlessly complicated.
  48. It is the best way to use composition indead of inheritance to design at first.
  49. A general guideline is “Use inheritance to express differences in behavior, and fields to express variations in state.”
  50. This can be called a pure “is-a” relationship because the interface of a class establishes what it is. Inheritance guarantees that any derived class will have the interface of the base class and nothing less. If you follow this diagram, derived classes will also have no more than the base-class interface.
  51. To use polymorphism- and thus object-oriented techniques-effectively in your programs, you must expand your view of programming to include not just members and messages of an individual class, but also the commonality among classes and their relationships with each other. Although this requires significant effort, it is a worthy struggle, because the results are faster program development, better code organization, extensible programs, and easier code maiantenance.

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!