欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

TIJ——Chapter Five:Initialization & Cleanup

程序员文章站 2022-04-19 19:09:30
...

Method overloading

|_Distinguishing overloaded methods

If the methods hava the same name, how can Java know which method you mean? There's a simple rule : Each overloaded method must take a unique list of argument types.

|_Overloading with primitives

We can know that the constant value 5 is treated as int, so if an overloaded method is available that takes as int, it is used. In all other cases, if you hava a data type that is smaller than the argument in the method, that data type is promoted. char produces a sightly different effect, since if it doesn't find an exact char match, it is promoted to int.

Suppose the methods take narrower primitive values. If your argument is wider, then you must perform a narrowing conversion with a cast. If you don't do this, the compiler will issue an error message.

|_Overloading on return values

void f() {}
int f() {return 1;}

If we just want to call a method for its side effect, such as:

f();

 

how can Java determine which f() should be called? And how could someone reading the code see it? Because of this sort of problem, you cannot use return value types to distinguish overloaded methods.

 

Default constructors 

If you create a class that has no constructors, the compiler will automatically create a default constructor for you.

If you define any constructors(with or without arguments), the compiler will not synthesize one for you.

When you don't put in any constructors, it's as if the compiler says, "You are bound to need some constructor so let me make one for you." But if you write a constructor, the compiler says, "You're written a constructor so you know what you're doing; if you didn't put in a default it's because you meant to leave it out."

 

The this keyword 

|_calling constructors from constructors

While you can call one constructor using this, you cannot call two. In addition, the constructor call must be the first thing you do, or you'll get a compiler error message.

|_the meaning of static 

It means that there is no this for that particular method. You cannot call non-static methods from inside static method(although the reverse is possible), and you can call a static method for the class itself, without any object. In fact, that's primarily what a static method if for.

 

Cleanup:finalization and garbage collection

When the garbage collector is ready to release the storage used for your object, it will first call finalize(), and only on the next garbage-collection pass will it reclaim the object's memory.

In Java, objects do not always get garbage collected. Or, put another way:

1、Your objects might not get garbage collected.

2、Garbage collection is not destruction.

3、Garbage collection is only about memory.

You might find that the storage for an object never gets released because your program never nears the point of running out of storage. If your program completes and the garbage collector never gets around to releasing the storage for any of your objects, that storage will be returned to the operating system en masse as the program exits. This is a good thing, because garbage collection has some overhead, and if you never do it, you never incur that expense.

|_What is finalize() for?

It would seem that finalize() is in place because of the possibility that you'll do something Clike by allocating memory using a mechanism other than the normal one in Java. This can happen primarily though native methods, which are a way to call non-Java code from Java. C and C++ are the only languages currently supported by native methods, but since they can call subprograms in other languages, you can effectively call anything. Inside the non-Java code, C'malloc() family of function might be called to allocate storage, and unless you call free(), that storage will not be released, causing a memory leak. Of course, free() is a C and C++ function, so you'd need to call it in a native method inside your finalize().

After reading this, you probably get the idea that you won't use finalize() much(Joshua Bloch goes further:"finalizes are unpredictable, often dangerous, and generally unnecessary."). You are correct; it is not the appropriate place for normal cleanup to occur. So where should normal cleanup be performed?

|_You must perform cleanup

If you want some kind of cleanup performed other than storage release, you must still explicitly call an appropriate method in Java, which is the equivalent of a C++ destructor without the convenience.

Remember that neither garbage collection nor finalization is guaranteed. If the JVM isn't close to running out of memory, then it might not waste time recovering memory through garbage collection.

|_How a garbage collector works

首先,JVM有多种实现形式,不同类型的JVM实现上会有所差别,速度、稳定性也不一样,因此应该辩证的看待下面的文字-:)

在一些JVM中,Java的堆与C++中的堆很是不同。C++中的堆就像一个大院子,每个对象占据一块地方。而Java中的堆则更像是一个传送带,因而加快了为对象分配内存的速度,就如同C++中的栈帧移动一般,随着"heap pointer"的移动其分配内存的速度稍逊与栈(for Java heap, there's a little extra overhead for bookkeeping, but it's nothing like searching for storage)。

一个简单且缓慢的垃圾回收方案是采用reference counting。每个对象包含一个引用计数器,GC遍历整个对象列表,当它发现一个引用计数器为零时,释放该对象的内存。管理计数器引用在程序的整个生命周期中有小的常量的开销。缺点是彼此循环引用的对象虽为垃圾但是因其引用计数器非零,因此得不到释放。该方案通常被用来解释一种可能的垃圾回收机制,但是似乎从没有被任何JVM的具体实现采用过。

一个更快的方案是基于一种思想:non-dead对象必须最终能够被生存在栈上或是静态存储区上的引用所追踪到。这个链条可能穿越基层对象,从栈上和静态存储区上的所有引用开始,跟踪进引用指向的对象,然后跟踪对象里的所有引用,如此反复,可以找到所有生存的对象。值得注意的是,彼此循环引用对象的释放不在是问题了,它们不能被找到,自动成为了垃圾。

JVM采用适合的垃圾回收方案,可以根据不同的情况进行切换。两种主要的机制是"stop-and-copy"、"mark-and-sweep"。stop-and-copy方案暂停当前的程序,留下所有垃圾,只将生存的对象从一个堆拷贝到另一个堆中。mark-and-sweep方案一直被早期的Sun's JVM采用,一般而言,它速度很慢,但是当垃圾很少甚至是没有垃圾产生的时候,其速度是很快的。mark-and-copy方案同样遵循从栈上或是静态存储区上的引用追踪对象的逻辑,每次它发现一个对象,对象就被设置一个标记,当mark程序执行完成后,sweep才能得到执行。sweep执行期间,死亡对象被释放。由于没有copy发生,如果收集器决定压紧碎片堆,只能通过移动对象的方式。

JVM监视GC的效率。如果所有对象长期生存,它切换到mark-and-sweep模式下工作。同样地,如果堆上的碎片变多,它切换回stop-and-copy模式。

JVM中还有很多额外的提速方法,比如just-in-time(JIT) compiler、HotSpot技术等。一部漫长的进化史啊:-)

 

Constructor Initialization

There's one thing to keep in mind, however: You aren't precluding the automatic initialization, which happens before the constructor is entered.

所有的原生数据类型、对象引用(不包括局部变量,如果其未被初始化就使用的话,出现编译错误:variable might not have been initialized)和那些在定义处清晰初始化的变量都被预先初始化为零。

|_Order of initialization

在类内部,变量初始化的顺序取决于它在类中定义的顺序。变量的定义可能分散地贯穿在方法的定义之间,但是变量在构造函数执行前被初始化。

|_static data initialization

静态变量的初始化发生在类对象被创建的时候或者第一次静态访问发生的时候。

To summarize the process of creating an object, consider a class called Dog:

1、Even though it doesn't explicitly use the static keyword, the constructor is actually a static method. So the first time an object of type Dog is created, or the first time a static method or static field of class Dog is accessed, the Java interpreter must locate Dog.class, which it dose by searching through the classpath.

2、As Dog.class is loaded(creating a Class object), all of its static initializers are run. Thus, static initialization takes place only once, as the Class object is loaded for the first time.

3、When you create a new Dog(), the construction process for a Dog object first allocates enough storage for a Dog object on the heap.

4、This storage is wiped to zero, automatically setting all the primitives in that Dog object to their default values(zero for number and the equivalent for boolean and char)and the references to null.

5、Any initializations that occur at the point of field definition are executed.

6、Constructors are executed. This might actually involve a fair amount of activity, especially when inheritance is involved.

|_Explicit static initialization

Java中可以通过静态代码块的方式初始化一组静态变量。

语法形式:static{...}

它只会执行一次——the first time you make an object of that class or the first time you access a static member of that class(even if you never make an object of that class).

|_Non-static instance initialization

Java提供一个简单的语法{...},称为实例初始化,用于初始化每个对象的非静态的变量,在构造函数之前执行。

 

Array initialization

Java通过抛出运行期异常的方式防止数组访问越界,尽管这花费了一些时间,但是并没有方式来关闭它。为了网络安全和程序员的产出,Java设计者做出了他们认为值得的权衡。尽管你可能以你认为更高效的数组访问方式来写代码,这也是浪费时间的,因为自动的编译期和运行期优化将实现快速数组访问。

|_Variable argument lists

与数组作为参数不同,变参列表的参数个数可以是零。

 

(END_XPJIANG).