thinking in java 笔记4

p.114 Overloading on return values
It is common to wonder, “Why only class names and method argument lists? Why not
distinguish between methods based on their return values?” For example, these two
methods, which have the same name and arguments, are easily distinguished from each
other:
void f() {}
int f() { return 1; }
This might work fine as long as the compiler could unequivocally determine the meaning
from the context, as in int x = f( ). However, you can also call a method and ignore the
return value. This is often referred to as calling a method for its side effect, since you don’t
care about the return value, but instead want the other effects of the method call. So if you
call the method this way:
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.


 p.125 Member initialization
Java goes out of its way to guarantee that variables are properly initialized before they are
used. In the case of a method’s local variables, this guarantee comes in the form of a compile-
time error. So if you say:
void f() {
int i;
i++; // Error -- i not initialized
}
you’ll get an error message that says that i might not have been initialized. Of course, the
compiler could have given i a default value, but an uninitialized local variable is probably a
programmer error, and a default value would have covered that up. Forcing the programmer
to provide an initialization value is more likely to catch a bug.
If a primitive is a field in a class, however, things are a bit different. As you saw in the
Everything Is an Object chapter, each primitive field of a class is guaranteed to get an initial
value. Here’s a program that verifies this, and shows the values:
//: initialization/InitialValues.java
// Shows default initial values.
import static net.mindview.util.Print.*;
public class InitialValues {
boolean t;
char c;
byte b;
short s;
int i;
long l;
float f;
double d;
InitialValues reference;
void printInitialValues() {
print("Data type Initial value");
print("boolean " + t);
print("char [" + c + "]");
print("byte " + b);
print("short " + s);
print("int " + i);
print("long " + l);
print("float " + f);
print("double " + d);
print("reference " + reference);
}
public static void main(String[] args) {
InitialValues iv = new InitialValues();
iv.printInitialValues();
/* You could also say:
new InitialValues().printInitialValues();
*/
}
} /* Output:
Data type Initial value
Initialization & Cleanup 125 
boolean false
char [ ]
byte 0
short 0
int 0
long 0
float 0.0
double 0.0
reference null
*///:~
You can see that even though the values are not specified, they automatically get initialized
(the char value is a zero, which prints as a space). So at least there’s no threat of working
with uninitialized variables.
When you define an object reference inside a class without initializing it to a new object, that
reference is given a special value of null.


p.128 Order of initialization

JAVA类的初始化顺序依次是:(静态变量、静态初始化块)->(变量、初始化块)->构造函数,相同级别的以定义顺序为准,  且静态变量和静态初始化块只初始化一次。
Within a class, the order of initialization is determined by the order that the variables are
defined within the class. The variable definitions may be scattered throughout and in
Initialization & Cleanup 127 
between method definitions, but the variables are initialized before any methods can be
called—even the constructor. For example:
//: initialization/OrderOfInitialization.java
// Demonstrates initialization order.
import static net.mindview.util.Print.*;
// When the constructor is called to create a
// Window object, you’ll see a message:
class Window {
Window(int marker) { print("Window(" + marker + ")"); }
}
class House {
Window w1 = new Window(1); // Before constructor
House() {
// Show that we’re in the constructor:
print("House()");
w3 = new Window(33); // Reinitialize w3
}
Window w2 = new Window(2); // After constructor
void f() { print("f()"); }
Window w3 = new Window(3); // At end
}
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f(); // Shows that construction is done
}
} /* Output:
Window(1)
Window(2)
Window(3)
House()
Window(33)
f()
*///:~
In House, the definitions of the Window objects are intentionally scattered about to prove
that they’ll all get initialized before the constructor is entered or anything else can happen. In
addition, w3 is reinitialized inside the constructor.
From the output, you can see that the w3 reference gets initialized twice: once before and
once during the constructor call. (The first object is dropped, so it can be garbage collected
later.) This might not seem efficient at first, but it guarantees proper initialization—what
would happen if an overloaded constructor were defined that did not initialize w3 and there
wasn’t a “default” initialization for w3 in its definition?


p.130 static data initialization
There’s only a single piece of storage for a static, regardless of how many objects are created.
You can’t apply the static keyword to local variables, so it only applies to fields. If a field is a
static primitive and you don’t initialize it, it gets the standard initial value for its type. If it’s
a reference to an object, the default initialization value is null.
If you want to place initialization at the point of definition, it looks the same as for non-
statics.

To see when the static storage gets initialized, here’s an example:
//: initialization/StaticInitialization.java
// Specifying initial values in a class definition.
import static net.mindview.util.Print.*;
class Bowl {
Bowl(int marker) {
print("Bowl(" + marker + ")");
}
void f1(int marker) {
print("f1(" + marker + ")");
}
}
class Table {
static Bowl bowl1 = new Bowl(1);
Table() {
print("Table()");
bowl2.f1(1);
}
void f2(int marker) {
print("f2(" + marker + ")");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupboard {
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard() {
print("Cupboard()");
bowl4.f1(2);
}
void f3(int marker) {
print("f3(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
public class StaticInitialization {
public static void main(String[] args) {
print("Creating new Cupboard() in main");
new Cupboard();
print("Creating new Cupboard() in main");
new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
} /* Output:
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Initialization & Cleanup 129 
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)
*///:~
Bowl allows you to view the creation of a class, and Table and Cupboard have static
members of Bowl scattered through their class definitions. Note that Cupboard creates a
non-static Bowl bowl3 prior to the static definitions.
From the output, you can see that the static initialization occurs only if it’s necessary. If you
don’t create a Table object and you never refer to Table.bowl1 or Table.bowl2, the static
Bowl bowl1 and bowl2 will never be created. They are initialized only when the first Table
object is created (or the first static access occurs). After that, the static objects are not
reinitialized.
The order of initialization is statics first, if they haven’t already been initialized by a previous
object creation, and then the non-static objects. You can see the evidence of this in the
output. To execute main( ) (a static method), the StaticInitialization class must be
loaded, and its static fields table and cupboard are then initialized, which causes those
classes to be loaded, and since they both contain static Bowl objects, Bowl is then loaded.
Thus, all the classes in this particular program get loaded before main( ) starts. This is
usually not the case, because in typical programs you won’t have everything linked together
by statics as you do in this example.

顺序:StaticInitialization class -》static fields table and cupboard -》main()

main是一个static method


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 does by searching through the classpath.
2. As Dog.class is loaded (creating a Class object, which you’ll learn about later), 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 numbers 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. As you shall see in the Reusing Classes chapter, this might
actually involve a fair amount of activity, especially when inheritance is involved.


Explicit static initialization
Java allows you to group other static initializations inside a special “static clause”
(sometimes called a static block) in a class. It looks like this:
130 Thinking in Java Bruce Eckel
//: initialization/Spoon.java
public class Spoon {
static int i;
static {
i = 47;
}
} ///:~
It appears to be a method, but it’s just the static keyword followed by a block of code. This
code, like other static initializations, is executed only once: 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). For example:
//: initialization/ExplicitStatic.java
// Explicit static initialization with the "static" clause.
import static net.mindview.util.Print.*;
class Cup {
Cup(int marker) {
print("Cup(" + marker + ")");
}
void f(int marker) {
print("f(" + marker + ")");
}
}
class Cups {
static Cup cup1;
static Cup cup2;
static {
cup1 = new Cup(1);
cup2 = new Cup(2);
}
Cups() {
print("Cups()");
}
}
public class ExplicitStatic {
public static void main(String[] args) {
print("Inside main()");
Cups.cup1.f(99); // (1)
}
// static Cups cups1 = new Cups(); // (2)
// static Cups cups2 = new Cups(); // (2)
} /* Output:
Inside main()
Cup(1)
Cup(2)
f(99)
*///:~
The static initializers for Cups run when either the access of the static object cup1 occurs
on the line marked (1), or if line (1) is commented out and the lines marked (2) are
uncommented. If both (1) and (2) are commented out, the static initialization for Cups
never occurs, as you can see from the output. Also, it doesn’t matter if one or both of the lines
marked (2) are uncommented; the static initialization only occurs once.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值