?
1.?????
The only place a label is useful in Java is right before an iteration
statement. And that means right before—it does no good to put any other
statement between the label and the iteration. And the sole reason to put
a label before an iteration is if you’re going to nest another iteration or a
switch inside it. That’s because the break and continue keywords will
normally interrupt only the current loop, but when used with a label
they’ll interrupt the loops up to where the label exists:
label1:
outer-iteration {
inner-iteration {
//…
break
; // 1
//…
continue
; // 2
//…
continue
label1; // 3
//…
break
label1; // 4
}
}
In case 1, the break breaks out of the inner iteration and you end up in
the outer iteration. In case 2, the continue moves back to the beginning
of the inner iteration. But in case 3, the continue label1 breaks out of
the inner iteration and the outer iteration, all the way back to label1.
Then it does in fact continue the iteration, but starting at the outer
iteration. In case 4, the break label1 also breaks all the way out to
label1
, but it does not re-enter the iteration. It actually does break out of
both iterations.
?
2.???????
It’s important to remember that the only reason to use labels in Java is
when you have nested loops and you want to break or continue through
more than one nested level
?3.???????
switch(
integral-selector) {
case
integral-value1 : statement; break;
case
integral-value2 : statement; break;
case
integral-value3 : statement; break;
case
integral-value4 : statement; break;
case
integral-value5 : statement; break;
// ...
default:
statement;
}
Integral-selector
is an expression that produces an integral value.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
4. Suppose you’re inside a method and you’d like to get the reference to the
current object. Since that reference is passed secretly by the compiler,
there’s no identifier for it. However, for this purpose there’s a keyword:
this
. The this keyword—which can be used only inside a method—
produces the reference to the object the method has been called for. You
can treat this reference just like any other object reference
?4.???????
Normally, when you say this, it is in the sense of “this object” or “the
current object,” and by itself it produces the reference to the current
object. In a constructor, the this keyword takes on a different meaning
when you give it an argument list: it makes an explicit call to the
constructor that matches that argument list Thus you have a
straightforward way to call other constructors
?
?5.????? 垃圾回收器并不一定會回收未用的對象(因為垃圾回收器只有在內存不夠用時才啟動,所以如果未用對象已經產生了,但是內存還有很多,這時垃圾回收器就不會執行,垃圾對象就不會被回收了)。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
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. So if you choose to use finalize( ), it gives you the ability to perform some important cleanup at the time of garbage collection.
當你調用,
System.gc()
時,這時虛擬機會盡最大可能去回收內存,但是它也不能保證100%的去掉用垃圾回收器去收集垃圾對象,如果調用了垃圾回收器 ,他的
finalize()
函數也會被調用,但是如果是系統運行完了以后也沒有瀕臨內存用完時,這是
garbage collector
就不會被調用來回收內存的。這樣對象也就沒有被回收。
?
<!--[if !supportLists]--> 6.????? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
If you remember this, you will stay out of trouble. What it means is that if
there is some activity that must be performed before you no longer need
an object, you must perform that activity yourself. Java has no destructor
or similar concept, so you must create an ordinary method to perform this
cleanup
<!--[if !supportLists]--> 7.????? <!--[endif]--> 一個文件可以沒有public class,如果有public class,則必須要與文件名同名
<!--[if !supportLists]--> 8.????? <!--[endif]--> class 不能是private ,或protected,但是inner class 是特例
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
chapter 6 reuse class
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 9.????? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
You can create a main( ) for each one of your classes, and it’s often recommended to code this way so that your test code is wrapped in with the class. Even if you have a lot of classes in a program, only the main( ) for the class invoked on the command line will be called. (As long as main( ) is public, it doesn’t matter whether the class that it’s part of is public.)
<!--[if !supportLists]--> 10.? <!--[endif]--> 調用同一個class內的構造函數用this(參數),調用base class 的函數用super.函數(參數)就可以了。調用base class 的構造函數用super(parameter)
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 11.? <!--[endif]--> ?
When you create an object of the derived class, it contains within it a subobject of the base class. This subobject is the same as if you had created an object of the base class by itself. It’s just that, from the outside, the subobject of the base class is wrapped within
the derived-class object.
(
baseclass
對象是包含在
subclass
的對象內的)
?
<!--[if !supportLists]--> 12.???? <!--[endif]-->
Java automatically inserts calls to the base-class constructor in the derived-class constructor
?
<!--[if !supportLists]--> 13.? <!--[endif]--> 總結(personal):默認情況下,derived class的默認構造函數會在其第一條語句加入super()來調用base class 的默認構造函數。如果base class 沒有默認構造函數就會報錯。但是如果要在derived class中調用非默認的base class 構造函數,則必須手工調用super(parameter),且這句必須在derived class 構造函數的第一句。如果你在derived class中沒有調用base class 的構造函數,則derived class 構造函數會調用base class 的默認構造函數
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 14.? <!--[endif]--> 不能在繼承過程中降低權限。
<!--[if !supportLists]--> 15.? <!--[endif]-->
Other part of subclass except the part in base class |
對于一個derived class 對象而言,他已經包含base class對象,如上圖所示,可以用測試內存地址驗證 Class A {A(){System.out.println(this); } Class B extends A {B(){System.out.println(this);} } ? |
<!--[if !vml]-->?
<!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 16.? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
Composition is generally used when you want the features of an existing class inside your new class, but not its interface. That is, you embed an object so that you can use it to implement functionality in your new class, but the user of your new class sees the interface you’ve defined for the new class rather than the interface from the embedded object. For this effect, you embed private objects of existing classes inside your new class.
<!--[if !supportLists]--> 17.? <!--[endif]-->
That is, protected in Java is automatically “friendly.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 18.? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
you should use inheritance sparingly,(
只有在明顯能夠產生實用價值時,才使用繼承,還有要考慮
polymorphism),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, but if you don’t need to upcast, then you should
look closely at whether you need inheritance.
?
<!--[if !supportLists]--> 19.? <!--[endif]--> java 沒有提供任何的機制可以保持對象的內容不變。對array也是這樣的
<!--[if !supportLists]--> 20.? <!--[endif]--> 初始化的順序
base class 裝載-〉base class中的static 變量初始化->derived class static變量初始化-〉base class 對象的初始化(1,數據成員初始化為默認值,for reference ,it is null. 2, 構造函數執行)-> derived class 對象初始化
注:當你在derived class 中時,base class 應該已經可以初始化完畢,可以使用了。
<!--[if !supportLists]--> 21.? <!--[endif]--> java 中的所有函數,除了被聲明為final,皆使用后期綁定。
<!--[if !supportLists]--> 22.??? <!--[endif]--> Inheritance vs composition
A better approach is to choose composition first, 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.(當你不知道該選擇“繼承“,或“組合“是,最好先選擇組合,組合不會強迫你的設計出現一大串繼承階層架構。組合手法彈性比較大)
<!--[if !supportLists]--> 23.??? <!--[endif]-->
a good guideline for constructors is, “Do as little as possible to set the object into a good state, and if you can possibly avoid it, don’t call any methods.”
<!--[if !supportLists]--> 24.? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
import java.util.*;
class Useful {
public void f() {}
Chapter 7: Polymorphism 345
public void g() {}
}
class MoreUseful extends Useful {
public void f() {}
public void g() {}
public void u() {}
public void v() {}
public void w() {}
}
public class RTTI {
public static void main(String[] args) {
Useful[] x = {
new Useful(),
new MoreUseful()
};
x[0].f();
x[1].g();
// Compile-time: method not found in Useful:
/* x[1]
在執行時期才可以知道其類型是
MoreUseful,
所以這里編譯不了
//! x[1].u();
((MoreUseful)x[1]).u(); // Downcast/RTTI
((MoreUseful)x[0]).u(); // Exception thrown
}
}
<!--[if !supportLists]--> 25.? <!--[endif]--> interface 中的data 默認是public static final.函數為public .
請千萬記住,interface 存在的根本理由是: 能夠被向上轉型至多個基本型別。
如果你的base class 可以不帶任何函數定義或任何成員變量,應優先使用interface
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 26.? <!--[endif]--> 所有的class的定義,無論是在函數內,或任意{}語句內,該class的定義都會和其他class一起被編譯出來
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 27.??? <!--[endif]-->
If you’re defining an anonymous inner class and want to use an object
that’s defined outside the anonymous inner class, the compiler requires
that the outside object be final
<!--[if !supportLists]--> 28.? <!--[endif]--> 總結(personal):non-static inner class 不能包含任何static 數據、method
static inner class 卻可以包含static data,or static methond,當然也可以包含non-static 成員。
static inner class 就說明它與外圍class沒有聯系,和一個單獨的class 一樣。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
??? E.g 

<!--[if !supportLists]--> 29.??? <!--[endif]-->
So one way to look at the inner class is as the completion of the solution of the multiple-inheritance problem. Interfaces solve part of the problem, but inner classes effectively allow “multiple implementation inheritance.” That is, inner classes effectively allow you to inherit from more than one non-interface.
<!--[if !supportLists]--> 30.<!--[endif]-->
chapter 9
<!--[if !supportLists]--> 31.???? <!--[endif]-->
Arrays provides the overloaded method equals( ) to compare entire arrays for equality. Again, these are overloaded for all the primitives, and for Object. To be equal, the arrays must have the same number of elements and each element must be equivalent to each corresponding element in the other array, using the equals( ) for each element. (For primitives, that primitive’s wrapper class equals( ) is used; Integer.equals( ) for int.
<!--[if !supportLists]--> 32.??? <!--[endif]--> 使某個class具有比較能力有兩種方法,1、可以實現java.lang.Comparable interface,只有一個函數compareTo()在這個interface內,接受另外一個Object做為parameter.2、實現Comparator interface, 兩個函數在這個interface 內,: compare() 和equal().
by creating a separateclass that implements an interface called Comparator. This has two methods, compare( ) and equals( ). However, you don’t have to implement equals( ) except for special performance needs, because anytime you create a class it is implicitly inherited from Object, which has an equals( ). So you can just use the default Object equals( ) and satisfy the contract imposed by the interface.
?
<!--[if !supportLists]--> 33.??? <!--[endif]-->
Unlike arrays, the containers print nicely without any help
<!--[if !supportLists]--> 34.? <!--[endif]--> remember the following diagram
<!--[if !vml]--><!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 35.??? <!--[endif]-->
Notice that there’s no get( ) function for random-access element selection. That’s because Collection also includes Set, which maintains its own internal ordering (and thus makes random-access lookup meaningless). Thus, if you want to examine all the elements of a Collection you must use an iterator; that’s the only way to fetch things back
<!--[if !supportLists]--> 36.??? <!--[endif]-->
The form for the definitions for equals( ) and hashCode( ) will be described later in this chapter. You must define an equals( ) in both cases, but the hashCode( ) is absolutely necessary only if the class will be placed in a HashSet (which is likely, since that should generally be your first choice as a Set implementation). However, as a programming style you should always override hashCode( ) when you override equals( ). This process will be fully detailed later in this chapter.
?
<!--[if !supportLists]--> 37.??? <!--[endif]-->
In the previous example, a standard library class (Integer) was used as a
key for the HashMap. It worked fine as a key, because it has all the necessary wiring to make it work correctly as a key. But a common pitfall occurs with HashMaps when you create your own classes to be used as keys. For example, consider a weather predicting system that matches Groundhog objects to Prediction objects. It seems fairly
straightforward—you create the two classes, and use Groundhog as the
key and Prediction as the value:
//: c09:SpringDetector.java
// Looks plausible, but doesn't work.
import java.util.*;
class Groundhog {
int ghNumber;
Groundhog(int n) { ghNumber = n; }
}
class Prediction {
boolean shadow = Math.random() > 0.5;
public String toString() {
if(shadow)
return "Six more weeks of Winter!";
else
return "Early Spring!";
}
}
public class SpringDetector {
public static void main(String[] args) {
HashMap hm = new HashMap();
for(int i = 0; i < 10; i++)
hm.put(new Groundhog(i), new Prediction());
System.out.println("hm = " + hm + "\n");
System.out.println(
"Looking up prediction for Groundhog #3:");
Groundhog gh = new Groundhog(3);
if(hm.containsKey(gh))
System.out.println((Prediction)hm.get(gh));
else
System.out.println("Key not found: " + gh);
}
} ///:~
Each Groundhog is given an identity number, so you can look up a
Prediction
in the HashMap by saying, “Give me the Prediction
associated with Groundhog number 3.” The Prediction class contains
a boolean that is initialized using Math.random( ), and a toString( )
that interprets the result for you. In main( ), a HashMap is filled with
Groundhog
s and their associated Predictions. The HashMap is
printed so you can see that it has been filled. Then a Groundhog with an
identity number of 3 is used as a key to look up the prediction for
Groundhog
#3 (which you can see must be in the Map).
It seems simple enough, but it doesn’t work. The problem is that
Groundhog
is inherited from the common root class Object (which is
what happens if you don’t specify a base class, thus all classes are
ultimately inherited from Object) ?It is Object’s hashCode( ) method
that is used to generate the hash code for each object, and by default it
just uses the address of its object. Thus, the first instance of Groundhog(3) does not produce a hash code equal to the hash code forthe second instance of Groundhog(3) that we tried to use as a lookup.
You might think that all you need to do is write an appropriate override
for hashCode( ). But it still won’t work until you’ve done one more
thing: override the equals( ) that is also part of Object. This method is
used by the HashMap when trying to determine if your key is equal to
any of the keys in the table. Again, the default Object.equals( ) simply
compares object addresses, so one Groundhog(3) is not equal to
another Groundhog(3).
Thus, to use your own classes as keys in a HashMap, you must override
both hashCode( ) and equals( )
?
<!--[if !supportLists]--> 38.??? <!--[endif]-->
in hashset or hashmap ,first of all you? produce the hashcode, then,use hashcode /capacity(number of buckets) to get the index of the array,A[index]refer to a array of elements? with the same hashcode value<!--[if !vml]-->
<!--[endif]-->
The most important factor in creating a hashCode( ) is that, regardless
of when hashCode( ) is called, it produces the same value for a
particular object every time it is called.
So if your hashCode( ) depends on mutable data in the object the user must
be made aware that changing the data will effectively produce a different
key by generating a different hashCode( ).
?
<!--[if !supportLists]--> 39.??? <!--[endif]-->
Strings have the special characteristic that if a program has several String objects that contain identical character sequences, then those String objects all map to the
same memory
chapter 10 Exception
<!--[if !supportLists]--> 40.?? <!--[endif]-->
When you throw an exception, several things happen. First, the exception
object is created in the same way that any Java object is created: on the
heap, with new. Then the current path of execution (the one you couldn’t
continue) is stopped and the reference for the exception object is ejected
from the current context. At this point the exception handling mechanism
takes over and begins to look for an appropriate place to continue
executing the program
<!--[if !supportLists]--> 41.??? <!--[endif]-->
Like any object in Java, you always create exceptions on the heap using
new
, which allocates storage and calls a constructor. There are two constructors in all standard exceptions: the first is the default constructor,
and the second takes a string argument so you can place pertinent
information in the exception:
?
<!--[if !supportLists]--> 42.? <!--[endif]--> 總結(personal): 1、override 函數只能擲出base class 明確列出的異常,當interface 中的某個函數也存在base class中時,interface 中的函數異常是不能在實現類中改變在實現類中這個函數的異常。2、在derived class中的函數可以不擲出base class規定的異常,但是如果要throws exception,則,這個exception 必須是base class中明確列出來的。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
<!--[if !supportLists]--> 43.??? <!--[endif]-->
The restriction on exceptions does not apply to constructors. You can see in StormyInning that a constructor can throw anything it wants, regardless of what the base-class constructor throws. However, since a base-class constructor must always be called one way or another (here, the default constructor is called automatically), the derived-class constructor must declare any base-class constructor exceptions in its exception specification. Note that a derived-class constructor cannot catch exceptions thrown by its base-class constructor.
chapter 11 : java i/o
<!--[if !supportLists]--> 44.?? <!--[endif]-->
Serializing an object is quite simple, as long as the object implements the
Serializable
interface (this interface is just a flag and has no methods).
When serialization was added to the language, many standard library
classes were changed to make them serializable, including all of the
wrappers for the primitive types, all of the container classes, and many
others. Even Class objects can be serialized.
<!--[if !supportLists]--> 45.? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
To serialize an object, you create some sort of OutputStream object and
then wrap it inside an ObjectOutputStream object. At this point you
need only call writeObject( ) and your object is serialized and sent to
the OutputStream. To reverse the process, you wrap an InputStream
inside an ObjectInputStream and call readObject( ). What comes
back is, as usual, a reference to an upcast Object, so you must downcast
to set things straight.
A particularly clever aspect of object serialization is that it not only saves
an image of your object but it also follows all the references contained in
your object and saves those objects, and follows all the references in each
of those objects
?
<!--[if !supportLists]--> 46.??? <!--[endif]-->
Note that no constructor, not even the default constructor, is called in the
process of deserializing a Serializable object. The entire object is
restored by recovering data from the InputStream.
Object serialization is byte-oriented (not Unicode -oriented), and thus uses the InputStream and OutputStream hierarchies.
?
<!--[if !supportLists]--> 47.???????????? <!--[endif]--> 使用Externalizable
//: c11:Blip3.java
// Reconstructing an externalizable object.
import java.io.*;
import java.util.*;
class Blip3 implements Externalizable {
int i;
String s; // No initialization
public Blip3() {
System.out.println("Blip3 Constructor");
// s, i not initialized
}
public Blip3(String x, int a) {
System.out.println("Blip3(String x, int a)");
s = x;
i = a;
// s & i initialized only in nondefault
// constructor.
}
public String toString() { return s + i; }
public void writeExternal(ObjectOutput out)
throws IOException {
System.out.println("Blip3.writeExternal");
// You must do this:
out.writeObject(s);
out.writeInt(i);
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
System.out.println("Blip3.readExternal");
// You must do this:
s = (String)in.readObject();
i =in.readInt();
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
System.out.println("Constructing objects:");
Blip3 b3 = new Blip3("A String ", 47);
System.out.println(b3);
ObjectOutputStream o =
new ObjectOutputStream(
new FileOutputStream("Blip3.out"));
System.out.println("Saving object:");
o.writeObject(b3);
o.close();
// Now get it back:
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream("Blip3.out"));
System.out.println("Recovering b3:");
b3 = (Blip3)in.readObject();
System.out.println(b3);
}
} ///:~
You can control the process of serialization by implementing the
Externalizable
interface instead of the Serializable interface. The
Externalizable
interface extends the Serializable interface and adds
two methods, writeExternal( ) and readExternal( ), that are
automatically called for your object during serialization and
deserialization so that you can perform your special operations.
?
When b1 is recovered, the Blip1 default constructor is called. This is
different from recovering a Serializable object, in which the object is
constructed entirely from its stored bits, with no constructor calls. With
an Externalizable object, all the normal default construction behavior
occurs (including the initializations at the point of field definition), and
then
readExternal( )
is called. You need to be aware of this—in
particular, the fact that all the default construction always takes place—to
produce the correct behavior in your Externalizable objects.
If you are inheriting from an Externalizable object, you’ll typically call
the base-class versions of writeExternal( ) and readExternal( ) to
provide proper storage and retrieval of the base-class components.
?
So to make things work correctly you must not only write the important
data from the object during the writeExternal( ) method (there is no
default behavior that writes any of the member objects for an
Externalizable
object), but you must also recover that data in the
readExternal( )
method. This can be a bit confusing at first because the
default construction behavior for an Externalizable object can make it
seem like some kind of storage and retrieval takes place automatically. It
does not.
?
?
<!--[if !supportLists]--> 48.??? <!--[endif]-->
One way to prevent sensitive parts of your object from being serialized is
to implement your class as Externalizable, as shown previously. Then
nothing is automatically serialized and you can explicitly serialize only the
necessary parts inside writeExternal( ).
If you’re working with a Serializable object, however, all serialization
happens automatically. To control this, you can turn off serialization on a
field-by-field basis using the transient keyword, which says “Don’t
bother saving or restoring this—I’ll take care of it.
<!--[if !supportLists]--> 49.??? <!--[endif]-->
Since Externalizable objects do not store any of their fields by default,
the transient keyword is for use with Serializable objects only.
<!--[if !supportLists]--> 50.? <!--[endif]--> Externalizable 的替代寫法
?
?
?
?
If you’re not keen on implementing the Externalizable interface, there’s
another approach. You can implement the Serializable interface and
add
(notice I say “add” and not “override” or “implement”) methods
called writeObject( ) and readObject( ) that will automatically be
called when the object is serialized and deserialized, respectively. That is,
if you provide these two methods they will be used instead of the default
serialization.
The methods must have these exact signatures:
private
void
writeObject(ObjectOutputStream stream)
throws IOException;
private
void
readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
?
It would appear that when you call
ObjectOutputStream.writeObject( )
, the Serializable object that
you pass it to is interrogated (using reflection, no doubt) to see if it
implements its own writeObject( ). If so, the normal serialization
process is skipped and the writeObject( ) is called. The same sort of
situation exists for readObject( ).
?
There’s one other twist. Inside your writeObject( ), you can choose to
perform the default writeObject( ) action by calling
defaultWriteObject( )
. Likewise, inside readObject( ) you can call
defaultReadObject( )
If you are going to use the default mechanism to write the non-transient
parts of your object, you must call defaultWriteObject( ) as the first
operation in writeObject( ) and defaultReadObject( ) as the first
operation in readObject( ).
?
<!--[if !supportLists]--> 51.? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
animals: [Bosco the dog[Animal@1cc76c], House@1cc769
, Ralph the hamster[Animal@1cc76d], House@1cc769
, Fronk the cat[Animal@1cc76e], House@1cc769
]
animals1: [Bosco the dog[Animal@1cca0c], House@1cca16
, Ralph the hamster[Animal@1cca17], House@1cca16
, Fronk the cat[Animal@1cca1b], House@1cca16
]
animals2: [Bosco the dog[Animal@1cca0c], House@1cca16
, Ralph the hamster[Animal@1cca17], House@1cca16
, Fronk the cat[Animal@1cca1b], House@1cca16
]
animals3: [Bosco the dog[Animal@1cca52], House@1cca5c
, Ralph the hamster[Animal@1cca5d], House@1cca5c
, Fronk the cat[Animal@1cca61], House@1cca5c
]
?
As long as you’re serializing everything to a single stream, you’ll be able to
recover the same web of objects that you wrote, with no accidental
duplication of objects. Of course, you can change the state of your objects
in between the time you write the first and the last, but that’s your
responsibility—the objects will be written in whatever state they are in
(and with whatever connections they have to other objects) at the time
you serialize them.
?
Chapter 12 RTTI
<!--[if !supportLists]--> 52.??? <!--[endif]-->
There’s a Class object for each class that is part of your program. That is,
each time you write and compile a new class, a single Class object is also
created (and stored, appropriately enough, in an identically named .class
file). At run-time, when you want to make an object of that class, the Java
Virtual Machine (JVM) that’s executing your program first checks to see if
the Class object for that type is loaded. If not, the JVM loads it by finding
the .class file with that name. Thus, a Java program isn’t completely
loaded before it begins, which is different from many traditional
languages.
Once the Class object for that type is in memory, it is used to create all
objects of that type.
?
<!--[if !supportLists]--> 53.? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
Java provides a second way to produce the reference to the Class object,
using a class literal. In the above program this would look like:
?
Gum.class;
?
which is not only simpler, but also safer since it’s checked at compiletime
<!--[if !vml]-->?
<!--[endif]-->
<!--[if !supportLists]--> 54.??? <!--[endif]-->
instanceof says “are you this class, or a class derived from this class?” On the other hand, if you compare the actual Class objects using ==, there is no concern with
inheritance—it’s either the exact type or it isn’
?
<!--[if !supportLists]--> 1.???????? <!--[endif]--> chapter 14 multithread
<!--[if !supportLists]--> 2.??????? <!--[endif]--> ?
regardless of whether you’re explicitly using threads, you can produce the current
thread used by your program with Thread and the static sleep( ) method.
<!--[if !supportLists]--> 3.??????? <!--[endif]-->
The simplest way to create a thread is to inherit from class Thread, which has all the wiring necessary to create and run threads. The most important method for Thread is run( ), which you must override to make the thread do your bidding. Thus, run( ) is the code that will be executed “simultaneously” with the other threads in a program.
<!--[if !supportLists]--> 4.??????? <!--[endif]-->
When main( ) creates the Thread objects it isn’t capturing the
references for any of them. An ordinary object would be fair game for
garbage collection, but not a Thread. Each Thread “registers” itself so
there is actually a reference to it someplace and the garbage collector can’t
clean it up.
<!--[if !supportLists]--> 5.??????? <!--[endif]-->
.A “daemon” thread is one that is supposed to provide a general service in
the background as long as the program is running, but is not part of the
essence of the program. Thus, when all of the non-daemon threads
complete, the program is terminated. Conversely, if there are any nondaemon
threads still running, the program doesn’t terminate. (There is,
for instance, a thread that runs main( ).)
<!--[if !supportLists]--> 6.??????? <!--[endif]-->
Java has built-in support to prevent collisions over one kind of resource:
the memory in an object. Since you typically make the data elements of a
class private and access that memory only through methods, you can
prevent collisions by making a particular method synchronized. Only
one thread at a time can call a synchronized method for a particular
object (although that thread can call more than one of the object’s
synchronized methods). Here are simple synchronized methods:
synchronized void f() { /* ... */ }
synchronized void g(){ /* ... */ }
Each object contains a single lock (also called a monitor) that is
automatically part of the object (you don’t have to write any special code).
When you call any synchronized method, that object is locked and no
other synchronized method of that object can be called until the first
one finishes and releases the lock. In the example above, if f( ) is called
for an object, g( ) cannot be called for the same object until f( ) is
completed and releases the lock. Thus, there’s a single lock that’s shared
by all the synchronized methods of a particular object, and this lock
prevents common memory from being written by more than one method
at a time (i.e., more than one thread at a time).
There’s also a single lock per class (as part of the Class object for the
class), so that synchronized static methods can lock each other out
from simultaneous access of static data on a class-wide basis.
<!--[if !supportLists]--> 7.??????? <!--[endif]-->
You’ll notice that both run( ) and synchTest( ) are synchronized. If
you synchronize only one of the methods, then the other is free to ignore
the object lock and can be called with impunity. This is an important
point: Every method that accesses a critical shared resource must be
synchronized
or it won’t work right
<!--[if !supportLists]--> 8.??????? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
What we’d like for this example is a way to isolate only part of the code
inside run( ). The section of code you want to isolate this way is called a
critical section
and you use the synchronized keyword in a different
way to set up a critical section. Java supports critical sections with the
synchronized block;
this time synchronized is used to specify the object
whose lock is being used to synchronize the enclosed code:
synchronized(syncObject) {
// This code can be accessed
// by only one thread at a time
}
Before the synchronized block can be entered, the lock must be acquired
on syncObject. If some other thread already has this lock, then the block
cannot be entered until the lock is given up.
<!--[if !supportLists]--> 9.??????? <!--[endif]-->
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
Blocking
A thread can be in any one of four states:
1.
New
: The thread object has been created but it hasn’t been started
yet so it cannot run.
2.
Runnable
: This means that a thread can be run when the timeslicing
mechanism has CPU cycles available for the thread. Thus,
the thread might or might not be running, but there’s nothing to
prevent it from being run if the scheduler can arrange it; it’s not
dead or blocked.
3.
Dead
: The normal way for a thread to die is by returning from its
run( )
method. You can also call stop( ), but this throws an
exception that’s a subclass of Error (which means you aren’t
forced to put the call in a try block). Remember that throwing an
exception should be a special event and not part of normal program
execution; thus the use of stop( ) is deprecated in Java 2. There’s
also a destroy( ) method (which has never been implemented)
that you should never call if you can avoid it since it’s drastic and
doesn’t release object locks.
4.
Blocked
: The thread could be run but there’s something that
prevents it. While a thread is in the blocked state the scheduler will
simply skip over it and not give it any CPU time. Until a thread
reenters the runnable state it won’t perform any operations.
Becoming blocked
The blocked state is the most interesting one, and is worth further
examination. A thread can become blocked for five reasons:
1.
You’ve put the thread to sleep by calling sleep(milliseconds), in
which case it will not be run for the specified time.
2.
You’ve suspended the execution of the thread with suspend( ). It
will not become runnable again until the thread gets the
resume( )
message (these are deprecated in Java 2, and will be
examined further).
3.
You’ve suspended the execution of the thread with wait( ). It will
not become runnable again until the thread gets the notify( ) or
notifyAll( )
message. (Yes, this looks just like number 2, but
there’s a distinct difference that will be revealed.)
4.
The thread is waiting for some I/O to complete.
5.
The thread is trying to call a synchronized method on another
object, and that object’s lock is not available.
You can also call yield( ) (a method of the Thread class) to voluntarily
give up the CPU so that other threads can run. However, the same thing
happens if the scheduler decides that your thread has had enough time
and jumps to another thread. That is, nothing prevents the scheduler
from moving your thread and giving time to some other thread. When a
thread is blocked, there’s some reason that it cannot continue running.
<!--[if !supportLists]--> 10.?? <!--[endif]-->
it’s important to understand that both sleep( )
and suspend( ) do not release the lock as they are called. You must be
aware of this when working with locks. On the other hand, the method
wait( )
does
release the lock when it is called, which means that other
synchronized
methods in the thread object could be called during a
wait( )
.
?
<!--[if !supportLists]--> 11.???? <!--[endif]--> ?
One fairly unique aspect of wait( ) and notify( ) is that both methods are
part of the base class Object and not part of Thread as are sleep( ),
suspend( )
, and resume( ). Although this seems a bit strange at first—
to have something that’s exclusively for threading as part of the universal
base class—it’s essential because they manipulate the lock that’s also part
of every object. As a result, you can put a wait( ) inside any
synchronized
method, regardless of whether there’s any threading
going on inside that particular class. In fact, the only place you can call
wait( )
is within a synchronized method or block. If you call wait( ) or
notify( )
within a method that’s not synchronized, the program will
compile, but when you run it you’ll get an
IllegalMonitorStateException
with the somewhat nonintuitive
message “current thread not owner.” Note that sleep( ), suspend( ),
and resume( ) can all be called within non-synchronized methods
since they don’t manipulate the lock.
?
?
?
?