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.