您的位置:首页 > 移动开发 > Objective-C

Class and object initialization-part1

2016-05-19 11:54 417 查看
This article marks a milestone in Java 101's exploration of client-side Java, as well as the one year anniversary of the series. Jeff begins moving away from simpler Java language concepts toward more advanced
theories -- and toward a grand tour of Java's standard class library. Begin the journey this month by learning all about Java initialization.

Initialization prepares classes and objects for use during a program's execution. Although we tend to think of initialization in terms of assigning values to variables, initialization is so much more. For example, initialization might involve opening a file
and reading its contents into a memory buffer, registering a database driver, preparing a memory buffer to hold an image's contents, acquiring the resources necessary for playing a video, and so on. Think of anything that prepares a class or an object for
use in a program as initialization.

Java supports initialization via language features collectively known as initializers.Because Java handles class initialization differently from object initialization, and because classes initialize before objects, we first explore class initialization
and class-oriented initializers. Later, we explore object initialization and object-oriented initializers.


Class initialization

A program consists of classes. Before a Java application runs, Java's class loader loads its starting class -- the class with a
public static void main(String [] args)
method
-- and Java's byte code verifier verifies the class. Then that class initializes. The simplest kind of class initialization is automatic initialization of class fields to default values. Listing 1 demonstrates that initialization:

Listing 1. ClassInitializationDemo1.java
// ClassInitializationDemo1.java
class ClassInitializationDemo1
{
static boolean b;
static byte by;
static char c;
static double d;
static float f;
static int i;
static long l;
static short s;
static String st;
public static void main (String [] args)
{
System.out.println ("b = " + b);
System.out.println ("by = " + by);
System.out.println ("c = " + c);
System.out.println ("d = " + d);
System.out.println ("f = " + f);
System.out.println ("i = " + i);
System.out.println ("l = " + l);
System.out.println ("s = " + s);
System.out.println ("st = " + st);
}
}


ClassInitializationDemo1
's
static
keyword introduces a variety
of class fields. As you can see, no explicit values assign to any of those fields. And yet, when you run
ClassInitializationDemo1
, you see the following output:
b = false
by = 0
c =
d = 0.0
f = 0.0
i = 0
l = 0
s = 0
st = null


The
false
,
0
,
0.0
,
and
null
values are the type-oriented representations of default values. They represent the result of all bits automatically set to zero in each class field. And
what automatically set those bits to zero? The JVM, after a class is verified. (Note: In the preceding output, you do not see a value beside
c =
because
the JVM interprets
c
's default value as the nondisplayable null value.)


Class field initializers

After automatic initialization, the next simplest kind of class initialization is the explicit initialization of class fields to values. Each class field explicitly initializes to a value via a class field initializer. Listing 2 illustrates several
class field initializers:

Listing 2. ClassInitializationDemo2.java
// ClassInitializationDemo2.java
class ClassInitializationDemo2
{
static boolean b = true;
static byte by = 1;
static char c = 'A';
static double d = 1.2;
static float f = 3.4f;
static int i = 2;
static long l = 3;
static short s = 4;
static String st = "abc";
public static void main (String [] args)
{
System.out.println ("b = " + b);
System.out.println ("by = " + by);
System.out.println ("c = " + c);
System.out.println ("d = " + d);
System.out.println ("f = " + f);
System.out.println ("i = " + i);
System.out.println ("l = " + l);
System.out.println ("s = " + s);
System.out.println ("st = " + st);
}
}


In contrast to
ClassInitializationDemo1
, in
ClassInitializationDemo2
a
class field initializer explicitly assigns a nondefault value to each class field. Simply put, a class field initializer consists of the assignment operator (
=
)
and an expression that the JVM evaluates after a class loads and before any of that class's developer-specified methods execute. The assignment operator assigns the expression's value to the associated class field. When run,
ClassInitializationDemo2
produces
the following output:
b = true
by = 1
c = A
d = 1.2
f = 3.4
i = 2
l = 3
s = 4
st = abc


Although the output is as expected, what executes the class field initializers that explicitly initialize
ClassInitializationDemo2
's class fields? The answer: After
the JVM zeroes the bits in all class fields, it calls a special JVM-level method to execute the byte code instructions that comprise the class's class field initializers. That method is known as
<clinit>
.

When you compile a class containing at least one class field initializer, the compiler generates code for
<clinit>
. After the JVM's class loader loads the class,
after the byte code verifier verifies the class's byte codes, and after the JVM allocates memory for the class fields and zeroes all bits in those class fields, the JVM calls the class's
<clinit>
method
(if present). The
<clinit>
method's byte code instructions execute all class field initializers. To see what those byte code instructions look like for the above
ClassInitializationDemo2
class,
check out Listing 3:

Listing 3. ClassInitializationDemo2's <clinit> method
0   iconst_1
1   putstatic ClassInitializationDemo2/b Z          // boolean b = true;
4   iconst_1
5   putstatic ClassInitializationDemo2/by B         // byte by = 1;
8   bipush 65
10   putstatic ClassInitializationDemo2/c C          // char c = 'A';
13   ldc2_w #1.200000
16   putstatic ClassInitializationDemo2/d D          // double d = 1.2;
19   ldc #3.400000
21   putstatic ClassInitializationDemo2/f F          // float f = 3.4f;
24   iconst_2
25   putstatic ClassInitializationDemo2/i I          // int i = 2;
28   ldc2_w #3
31   putstatic ClassInitializationDemo2/l J          // long l = 3;
34   iconst_4
35   putstatic ClassInitializationDemo2/s S          // short s = 4;
38   ldc "abc"
40   putstatic ClassInitializationDemo2/st Ljava/lang/String;  // String st = "abc";
43   return


Listing 3 presents some insight into how
ClassInitializationDemo2
's
<clinit>
method
works. Each line presents a number and a byte code instruction. The number represents the instruction's zero-based address and is not important to this discussion. The first instruction,
iconst_1
,
pushes integer constant 1 onto a stack, and the second instruction,
putstatic ClassInitializationDemo2/b Z
, pops that constant from the stack and assigns it to
boolean
class
field
b
. (At the JVM level, at least with Sun's JVM, the Boolean true value is represented as integer constant 1.) The
Z
appearing
to the right of the
putstatic
instruction identifies the type of
b
as
Boolean. Similarly,
B
identifies the byte type,
C
identifies the
character type,
D
identifies the double-precision floating-point type,
F
identifies
the floating-point type,
J
identifies the long integer type, and
S
identifies
the short integer type. The
bipush
,
ldc2_w
,
ldc
,
and other
iconst
instructions push other constants onto the stack, and their respective
putstatic
instructions
pop those values from the stack before assigning them to various class fields. The final
return
instruction causes execution to leave the
<clinit>
method.
At that point, the
main()
method starts to execute.

Some programs require class fields to refer to previously declared class fields. Java supports that activity by letting you specify the name of a previously declared class field in the expression portion of a subsequently declared class field's class field
initializer, as Listing 4 demonstrates:

Listing 4. ClassInitializationDemo3.java
// ClassInitializationDemo3.java
class ClassInitializationDemo3
{
static int first = 3;
static int second = 1 + first;
public static void main (String [] args)
{
System.out.println ("first = " + first);
System.out.println ("second = " + second);
}
}


ClassInitializationDemo3
declares class field
first
and explicitly
assigns
3
to that field. Then,
ClassInitializationDemo3
declares
class field
second
and refers to
first
in
second
's
class initializer expression. When you run the program, you see the following output:
first = 3
second = 4


If you examine the byte code instructions for
ClassInitializationDemo3
's
<clinit>
method,
you see something similar to Listing 5:

Listing 5. ClassInitializationDemo3's <clinit> method
0   iconst_3
1   putstatic ClassInitializationDemo3/first I   // first = 3;
4   iconst_1
5   getstatic ClassInitializationDemo3/first I
8   iadd
9   putstatic ClassInitializationDemo3/second I  // second = 1 + first;
12   return


Listing 5 shows byte code instructions that assign
3
to
first
and
add constant
1
to
first
's contents. The result of that addition
ends up in a temporary stack-based variable. The instructions subsequently assign the contents of the temporary stack-based variable to
second
.

Although a subsequently declared class field can refer to a previously declared class field, the reverse is not true: You cannot declare a class field initializer that refers to a class field declared later in source code. In other words, Java does not permit
forward references with class field initializers, as the following code fragment demonstrates:
static int second = 1 + first;
static int first = 3;


When the compiler encounters either the code above or another forward reference code fragment during compilation, it generates an error message because the developer's intention is unclear. Should the compiler regard
first
as
containing 0, which results in
second
initializing to 1, or should the compiler regard
first
as
containing 3, which results in
second
initializing to 4? To prevent that confusion, Java prohibits class field initializers from making forward references to other
class fields.


Class block initializers

Although sufficient for class field initialization, class field initializers prove inadequate for more complex class initialization. For example, suppose you need to read a file's contents into a buffer before the
main()
method
executes. What do you do? Java meets that challenge by providing the class block initializer. A class block initializer consists of keyword
static
followed
by an open brace character (
{
), initialization code, and a close brace character (
}
).
Furthermore, a class block initializer appears within a class, but not within any of that class's methods, as Listing 6 demonstrates:

Listing 6. ClassInitializationDemo4.java
// ClassInitializationDemo4.java
import java.io.*;
class ClassInitializationDemo4
{
static String [] filenames;
static
{
System.out.println ("Acquiring filenames");
filenames = new File (".").list ();
System.out.println ("Filenames acquired");
}
public static void main (String [] args)
{
System.out.println ("Displaying filenames\n");
for (int i = 0; i < filenames.length; i++)
System.out.println (filenames [i]);
}
}


ClassInitializationDemo4
declares a
filenames
array field variable
and then introduces a class block initializer. That initializer displays a status message, obtains a list of all filenames for those files that appear in the current directory, and displays a second status message. All that activity takes place before the
main()
method
executes. When
main()
executes, a status message displays, along with a list of filenames. The result resembles the following output:
Acquiring filenames
Filenames acquired
Displaying filenames
ClassInitializationDemo4.java
ClassInitializationDemo4.class


In addition to placing the class field initializers' byte code instructions in a class's
<clinit>
method, the compiler places the byte code instructions of each
encountered class block initializer in that same method. The compiler places these instructions in the
<clinit>
method according to a specific order. Because the
compiler compiles a class in a top-to-bottom fashion, it places in the
<clinit>
method, in a top-to-bottom order, the equivalent byte code instructions to each
encountered class field initializer and class block initializer. Refer back to Listing 6: The compiler places the class block initializer's byte code instructions in
ClassInitializationDemo4
's
<clinit>
method.
If that class specified a class field initializer for its
filenames
class field, the byte code instructions comprising that class field initializer would appear
before the class block initializer's byte code instructions in the
<clinit>
method.

原文链接:http://www.javaworld.com/article/2075796/java-platform/java-101--class-and-object-initialization.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: