你的位置:首页 > Java教程

[Java教程]TIJ英文原版书籍阅读之旅——Chapter Seven:Reusing Classes


Reusing Classes

有两种常用方式实现类的重用,组件(在新类中创建存在类的对象)和继承。

Composition syntax

Every non-primitive object has a toString() method, and it’s called in special situations when the compiler wants a String but it has an object.

Inheritance syntax

You’re always doing inheritance when you create a class, because unless you explicitly inherit from some other class, you implicitly inherit from Java’s standard root class Object.

To allow for inheritance, as a general rule make all fields private and all methods public. Of course, in particular cases you must adjustments, but this is a useful guideline.

|_Initializing the base class

Even if you don’t create a constructor for derived-class, the compiler will synthesize a default constructor for you that calls the base constructor.

If your class doesn’t have default arguments, or if you want to call a base-class constructor that has an argument, you must explicitly write the calls to the base-class constructor using the super keyword and the appropriate argument list. In addition, the call to the base-class constructor must be the first thing you do in the derived-class constructor.

Delegation

委托是重用类的第三种方式,介于继承和组件之间。在新建类中放置成员对象(像组件),同时暴露该对象的所有方法(像继承)。You have more control with delegation because you can choose to provide only a subset of the methods in the member object.

Combining composition and inherit

|_Guaranteeing proper cleanup

Note that in your cleanup method, you must also pay attention to the calling order for the base-class and member-object cleanup methods in case one subobject depends on another. In general, you should follow the same form that is imposed by a C++ compiler on its destructors: First perform all of the cleanup work specific to your class, in the reverse order of creation.

There can be many cases in which the cleanup issue is not a problem; you just let the garbage collector do the work. But when you must do it explicitly, diligence and attention are required, because there’s not much you can rely on when it comes to garbage collection. The garbage collector might never be called. If it is, it can reclaim objects in any order it wants. You can’t rely on garbage collection for anything but memory reclamation. If you want cleanup to take place, make your own cleanup methods and don’t use on finalize().

Choosing composition vs. inheritance

Both composition and inheritance allow you to place subobject inside your new class(composition explicitly does this-with inheritance it’s implicit).

The is-a relationship is expressed with inheritance, and the has-a relationship is expressed with composition.

Upcasting

In object-oriented programming, the most likely way that you’ll create and use code is by simply packaging data and methods together into a class, and using objects of that class. You’ll also use existing classes to build new classes with composition. Less frequently, you’ll use inheritance. So although inheritance gets a lot of emphasis while learning OOP, it doesn’t mean that you should use it everywhere you possibly can. On the contrary, you should use it sparingly, only when it’s clear that inheritance is useful. One of the clearest ways to determine whether you should use composition or inheritance is to ask whether you’ll ever need to upcast from your new class to the base class. If you must upcast, then inheritance is necessary. The Polymorphism provides one of the most compelling reasons for upcasting, but if you remember to ask “Do I need to upcast?” you’ll hava a good tool for deciding between composition and inheritance.

The final keyword

There are there places where final can be used: for data, methods and classes.

|_final data

That a piece of data is constant is useful for two reasons:

1. It can be a compile-time constant that won’t ever change.

2. It can be a value initialized at run time that you don’t want changed.

In the case of a compile-time constant, the compiler is allowed to “fold” the constant value into any calculations in which it’s used; that is, the calculation can be performed at compile time, eliminating some run-time overhead. In Java, these sorts of constants must be primitives and are expressed with the final keyword. A value must be given at the time of definition of such a constant.

当final被用于对象引用上时,对象的引用不能被更改,即该引用不能指向其他对象,但是对象本身可以被修改。这个规则对隶属于对象的数组引用同样适用。

在定义时不赋初值的final变量必须在使用之前被初始化(比如在构造体中初始化,static final不能在构造体中初始化,因为static不依赖于对象而存在),否则编译报错。You’re forced to perform assignments to finals either with an expression at the point of definition of the field or in every constructor. That way it’s guaranteed that the final field is always initialized before use.

|_final arguments

参数被final修饰时可读不可写。This feature is primarily used to pass data to anonymous inner classes.

|_final methods

用final修饰的方法不能被重写。

|_final and private

Any private methods in a class are implicitly final. Because you can’t access a private method, you can’t override it. You can add the final specifier to a private method, but it doesn’t give that method any extra meaning.

“Overriding” can only occur if something is part of the base-class interface. That is, you must be able to upcast an object to its base type and call the same method. If a method is private, it isn’t part of the base-class interface. It is just some code that’s hidden away inside the class, and it just happens to hava the name, but if you create a public, protected, or package-access method with the same name in the derived class, there’s no connection to the method that might happen to have that name in the base class. You haven’t overridden the method; you’ve just created a new method. Since a private method is unreachable and effectively invisible, it doesn’t factor into anything except for the code organization of the class for which it was defined.

|_final classes

When you say that an entire class is final(by preceding its definition with the final keyword), you state that you don’t want to inherit from this class or allow anyone else to do so. In other words, for some reason the design of your class is such that there is never a need to make any changes, or for safety or security reasons you don’t want subclassing.

Note that the field of a final class can be final or not, as you choose. The same rules apply to final for fields regardless of whether the class is defined as final. However, because it prevents inheritance, all methods in a final class are implicitly final, since there’s no way to override them. You can add the final specifier to a method in a final class, but it doesn’t add any meaning.

Initialization and class loading

In Java, the compiled code for each class exists in its own separate file. That file isn’t loaded until the code is needed. In general, you can say that “class code is loaded at the point of first use.” This is usually when the first object of that class is constructed, but loading also occurs when a static field or static method is accessed(The constructor is also a static methods even though the static keyword is not explicit. So to be precise, a class is first loaded when any one of its static members is accessed.).

|_Initialization with inheritance

第一次运行主类的main方法时,装载器找到该类对应的class文件,在装载的过程中如果发现它有父类,则继续装载其父类,父类还有父类,继续装载。接下来,在根类开始执行静态初始化,接下来是其派生类,等等。这是重要的,因为派生类静态初始化可能依赖父类成员被合理的初始化。

At this point, the necessary classes hava all been loaded so the object can be created. First, all the primitives in this object are set to their default values and the object references are set to null-this happens in one fell swoop by setting the memory in the object to binary zero. Then the base-class constructor will be called. In this case the call is automatic, but you can also specify the base-class constructor call by using super. The base class construction goes through the same process in the same order as the derived-class constructor. After the base-class constructor completes, the instance variables are initialized in textual order. Finally, the rest of the body of the constructor is executed.

Summary

Both inheritance and composition allow you to create a new type from existing types. Composition reuses existing types as part of the underlying implementation of the new type, and inheritance reuses the interface.

 


(END_XPJIANG)