
Thinking in Java - 4th Edition
Pré-visualização50 páginas
*/ } } /* 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\u2019s 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. Specifying initialization What happens if you want to give a variable an initial value? One direct way to do this is simply to assign the value at the point you define the variable in the class. (Notice you cannot do this in C++, although C++ novices always try.) Here the field definitions in class InitialValues are changed to provide initial values: //: initialization/InitialValues2.java // Providing explicit initial values. public class InitialValues2 { boolean bool = true; char ch = \u2018x\u2019; byte b = 47; short s = 0xff; int i = 999; long lng = 1; float f = 3.14f; double d = 3.14159; } ///:~ You can also initialize non-primitive objects in this same way. If Depth is a class, you can create a variable and initialize it like so: //: initialization/Measurement.java class Depth {} public class Measurement { Depth d = new Depth(); // ... } ///:~ If you haven\u2019t given d an initial value and you try to use it anyway, you\u2019ll get a runtime error called an exception (covered in the Error Handling with Exceptions chapter). You can even call a method to provide an initialization value: //: initialization/MethodInit.java public class MethodInit { int i = f(); int f() { return 11; } 126 Thinking in Java Bruce Eckel } ///:~ This method can have arguments, of course, but those arguments cannot be other class members that haven\u2019t been initialized yet. Thus, you can do this: //: initialization/MethodInit2.java public class MethodInit2 { int i = f(); int j = g(i); int f() { return 11; } int g(int n) { return n * 10; } } ///:~ But you cannot do this: //: initialization/MethodInit3.java public class MethodInit3 { //! int j = g(i); // Illegal forward reference int i = f(); int f() { return 11; } int g(int n) { return n * 10; } } ///:~ This is one place in which the compiler, appropriately, does complain about forward referencing, since this has to do with the order of initialization and not the way the program is compiled. This approach to initialization is simple and straightforward. It has the limitation that every object of type InitialValues will get these same initialization values. Sometimes this is exactly what you need, but at other times you need more flexibility. Constructor initialization The constructor can be used to perform initialization, and this gives you greater flexibility in your programming because you can call methods and perform actions at run time to determine the initial values. There\u2019s one thing to keep in mind, however: You aren\u2019t precluding the automatic initialization, which happens before the constructor is entered. So, for example, if you say: //: initialization/Counter.java public class Counter { int i; Counter() { i = 7; } // ... } ///:~ then i will first be initialized to 0, then to 7. This is true with all the primitive types and with object references, including those that are given explicit initialization at the point of definition. For this reason, the compiler doesn\u2019t try to force you to initialize elements in the constructor at any particular place, or before they are used\u2014initialization is already guaranteed. Order of initialization 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\u2014even 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\u2019ll see a message: class Window { Window(int marker) { print("Window(" + marker + ")"); } } class House { Window w1 = new Window(1); // Before constructor House() { // Show that we\u2019re 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\u2019ll 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\u2014what would happen if an overloaded constructor were defined that did not initialize w3 and there wasn\u2019t a \u201cdefault\u201d initialization for w3 in its definition? static data initialization There\u2019s only a single piece of storage for a static, regardless of how many objects are created. You can\u2019t apply the static keyword to local variables, so it only applies to fields. If a field is a static primitive and you don\u2019t initialize it, it gets the standard initial value for its type. If it\u2019s 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. 128 Thinking in Java Bruce Eckel To see when the static storage gets initialized, here\u2019s 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,