Chapter 4 Initialization and Clear
"With the progress of the computer, the program design of 'unsafe' has become one of the culprits that have a high pricing cost."
"Initialization" and "Clear" are two of these security issues. Most of the errors in many C processes are caused by programmers forgetting to initialize a variable. For ready-made libraries, if users don't know how to initialize a component, this is often the error. Clearance is another special problem, because after using an element, it is easy to forget because it is no longer concerned. In this way, the resource occupied by that element will keep it, and it is easy to generate the consequences of resources (mainly memory).
C introduces the concept of "builder" for us. This is a special method that is automatically called after an object creation. Java also used this concept, but added its own "garbage collector" to automatically release them when resources are no longer needed. This chapter will discuss the issues of initialization and clearing, and how Java provides their support.
4.1 Automatically initialize with builder
For the creation of the method, you can imagine that each class you write is invoked once inTIALIZE (). This name reminds us that such calls should be made first before using the object. But unfortunately, this also means that users must remember the calling method. In Java, since a special approach called "builder" is provided, the class designer guarantees that each object will be properly initialized. If a class has a builder, Java will automatically call the builder - even in the case where the user does not accept. So this is a guarantee!
The next question is how to name this method. There are two problems. The first is that any name we use may conflict with the names intended to use for a class member. The second is that because the compiler's responsibility is to call the builder, it must know which method to be called. The program taken by C seems to be the simplest and more logical, so it is also available in Java: The name of the builder is identical to the class name. In this way, it is guaranteed that one method like this will automatically call during initialization.
Below is a simple class with builder (if you have problems with this program, please refer to Chapter 3 "Assignment" section).
148-149 page program
Now, once an object is created:
NEW ROCK ();
The corresponding storage space is assigned and the builder is called. This ensures that the object gets the correct initialization before we handle.
Please note that the coding rules of all methods of letters do not apply to builders. This is because the name of the builder must be identical to the class name!
As with any other way, builders can also use arguments so that we specify the specific creation of objects. The above examples can be easily changed so that the builder uses its own argument. As follows:
Page 149
With the self-variable of the builder, we can set the corresponding parameters for the initialization of an object. For example, it is assumed that the class tree has a builder that uses an integer from the variable tag of the height of the tree, then you can create a TREE object below:
Tree T = New Tree (12); // 12 feet high
If Tree (int) is our unique builder, the compiler will not allow us to create a Tree object in any other way.
The builder helps to eliminate a large number of problems involving classes and make the code easier to read. For example, in the aforementioned code segment, we have not seen a clear call to the initialize () method - those methods are conceptually independent of defined content. In Java, definition and initialization is a unified concept - the two are unable.
The builder belongs to a more special method type because it does not return a value. This has a significant difference from the VOID return value. For Void returns, although the method itself does not automatically returns anything, but still allows it to return to other things. The builder is different, but it will not return anything automatically, but it can't have any options at all. If there is a return value, and suppose we can choose return content yourself, then how much the compiler knows how to process the return value. 4.2 Method Overload
In any programming language, an important feature is the application of the name. When we create an object, we assign the name of a save area. The method name represents a specific action. By describing your own system with name, you can make your own procedures more prone to people understand and modify. It is very like writing prose - the purpose is to communicate with the reader.
We use names or describe all objects and methods. If the name is selected, you can make yourself and others easily understand your code.
There are some special issues when there is a detailed concept "mapping" in the human language to a programming language. In daily life, we express a variety of different meanings with the same words - 即 word "overload". We say "wash shirt", "car wash" and "washing dog". But if the force is like this, it is very stupid: "shirt wash shirt", "car wash" and "dog wash dog". This is because the audience does not need to distinguish any clear distinction between the act of execution. Most languages of human beings have a strong "redundant", so even if several words are missing, they can still be inferred. We don't need a unique identifier - inference from a specific context.
Most programming languages (especially C) require us to set a unique identifier for each function. So absolutely can't display an integer with a function named print (), and another print () display the floating point number - each function requires a unique name.
In Java, another factor is forced to have an overload: builder. Since the name of the builder is determined by the class name, there can only be a builder name. But if we want to create an object in a variety of ways? For example, suppose we want to create a class that initializes it in a standard manner and is initialized from the file. At this point, we need two builders, one has no argument (default builder), and another string as an argument - the name of the file used to initialize the object. Because they are constructors, they must have the same name, which is called. Therefore, in order to make the same method name, "Method Overload" is a very important measure. At the same time, although the method overload is required, it can also be applied to any other way, and it is very convenient.
In the following example, we show you the original method of overload builder and overload:
151-152 page
Tree can be created into a seed without any argument; it can also be created into plants grown in the nursery. To support this creation, two builders have been used, one has no argument (we refer to the "default builder", annotation 1), and another high.
1: In some Java data published in Sun, use a rude but very explained the problem of the term "NO-ARG Constructors). But "Default Builder" said that many years have been used, so I chose it.
We also likely to call the info () method through a variety of ways. For example, suppose we have an additional message to display, use String's own variables; and if you don't have other words, it is not used. Since two independent names are given to obviously the same concept, it may be a little quirky. Fortunately, method overload allows us to use the same name for both. 4.2.1 Distinguishing overload method
If the method has the same name, how does Java know which method we refer to? Here is a simple rule: each overload must take a list of unique self-variable types.
If you think a few seconds, you will think of a problem: in addition to the type of the self-variable, how can the programmer distinguish between two synonymous methods?
Even if the order of the variable is also enough to distinguish between two methods (although we usually do not want to adopt this method, because it produces difficult-to-maintain code):
152-153 pages
The two print () methods have exact independent variables, but the order is different, which can be distinguished from them.
4.2.2 Overload of the main type
The main (data) type can automatically convert from a "smaller" type into a "larger" type. This will cause some confusion when it is involved in overload problems. The following example discloses the situation when the main type is passed to the method:
153-155 page
If the output of this program is observed, it will find that the constant value 5 is treated as an int value. Therefore, if an overload method can be used, it can get the INT value it used. In all other cases, if the self-variable used in our data type "less than" method, "transformation" processing will be processed. The effect of char is slightly different, this is due to the holiday it did not find an accurate char match, it turned into int.
What happens when our self-variable "greater than" overload method is expected, what happens? A modification of the aforementioned procedure shows the answer:
Page 155-157
Here, the method is used in a primary type value, a narrower range. If our own variable range is wide than it, it must be converted to the appropriate type in parentheses. If you don't do this, the compiler will report an error.
Everyone can notice this is a "reduced conversion". That is, some information may be lost during the shape or transformation process. This is the reason why the compiler forced us to clearly define - we need to express the desire to transform.
4.2.3 Return value overload
We are very easy to confuse the following: Why is there only class names and methods to list the variables? Why not distinguish between methods according to return values? For example, although they have the same name and independent variables, it is actually very easy to distinguish:
Void f () {}
INT f () {}
If the compiler can clearly determine the sense of judgment according to the context (context), such as in INT X = F (), then there is no problem in this. However, we may also call a method while ignoring the return value; we usually call this "to call a method for its side effects", because we care about the return value, but the other effect called. So if we call the method below:
f ();
How does Java judge the specific call mode of F ()? And how do others identify and understand the code? Due to the problem of this class, the overloaded method cannot be distinguished according to the return value type.
4.2.4 Default Builder
As early as possible, the default builder is no argument. Their role is to create an "empty object". If you create a class without a builder, the compiler will help us automatically create a default builder. E.g:
158 page
For the following line:
NEW bird ();
Its role is to create a new object and call the default builder - even if there is not clearly defined a builder like this. If there is no, there is no way to call, and our object cannot be constructed. However, if a builder has been defined (whether there is a variety of variables), the compiler will not help us automatically synthesize: Class Bush {
Bush (INT I) {}
Bush (double d) {}
}
Now, if you use the following code:
New bush ();
The compiler will report that you can't find a constructed builder. Just as we have no builder, the compiler will say: "You seem to need a builder, so let us create one." But if we wrote a builder, the compiler will say: "Ah, you have written a builder, so I know what you want; if you don't place a default, it is because you intend to omit it."
4.2.5 THIS keyword
If there are two kinds of objects, it is called A and B, then you may not know how to call a f () method for these two objects:
Class banana {void f (int i) {/ * ... * /}}
Banana a = new banana (), b = new banana ();
A.f (1);
B.F (2);
If there is only one way to name f (), how can it know that he is still a or B call?
In order to write code with a simple, object-oriented syntax - ie "send a message to the object", the compiler has completed some behind-the-scenes work for us. The secret is the first self-variable transmission to method f (), and that index is the handle of the object that is ready to operate. Therefore, the two method calls will become in the form of the following:
Banana.f (a, 1);
Banana.f (b, 2);
This is the form of internal expression, we can't write an expression like this and try to allow the compiler to accept it. However, what happened by it after it can understand the scene.
Assume that we are internally and want to get the handle of the current object. Since the handle is passed by the compiler "Secret", there is no identifier available. However, there is a dedicated keyword for this purpose: THIS. The THIS keyword (note only within the method) can generate the corresponding handle for that object that has been called its method. It can be treated like this handle like any other object handle. But note that if you are ready to call a class method from the other method of your class, you don't have to use this. Simply call that method simply. The current THIS handle is automatically applied to other methods. So we can use the following code:
Class Apricot {
Void pick () {/ * ... * /}
Void piT () {pick (); / * ... * /}
}
In the PIT (), we can say this.pick (), but it is necessary. The compiler can help us complete. The THIS keyword can only be used for those special classes - the handle of the current object needs to be explicitly used. For example, if you want to return the handle to the current object, then it is often used in the returnite statement.
160-page program
Since INCREMENT () returns the handle of the current object via the THIS keyword, you can easily perform multiple operations for the same object.
1. Call the builder in the builder
If you write multiple builders for a class, you often need to call another builder in a builder to avoid writing duplicate code. This can be used to do this with this keyword.
Usually, when we say this, it refers to "this object" or "current object". And it will generate a handle of the current object. In a builder, if it gives it an argument list, the THIS keyword will have different meanings: it will make a clear call to the builder that matches the argument list. In this way, we can call other builders through a direct approach. As shown below: 160-161 Page Program
Among them, the builder Flower (String S, INT PETALS) reveals this question: Although a builder can be called, it is not called two. In addition, builder calls must be the first thing we do, otherwise it will receive an error message for the compiler.
This example also shows everyone another purpose. Since the name of the argument S and the name of the member data S are the same, it will be confused. To solve this problem, this.s can be used to reference member data. This form of application is often seen in Java code, and this practice is also used.
In Print (), we found that the compiler does not allow us to call a builder inside any other method other than a builder.
2. STATIC's meaning
After understanding the THIS keyword, we can understand the meaning of the Static (static) method more completely. It means that a specific method does not have this. We cannot send calls to non-Static methods within a static method (annotation 2), although it is ok. And without any object, we can send a call to a static method for the class itself. In fact, it is the most basic meaning of the Static method. It seems to be an equivalent of a global function (in the C language). In addition to the global function is not allowed to be used in Java, if a STIC method is placed inside the class, it can access other Static methods and the Static field.
2: One of the possibilities of such calls is that we pass an object handle to the STATIC method inside. Subsequently, through the handle (this is actually this), we can invoke a non-Static method and access non-Static fields. But in general, if you really want to do this, just make an ordinary, non-Static method.
Some people complain that the Static approach is not "object-oriented" because they have some features of global functions; using the Static method, we do not have to send a message to the object because there is no tris. This may be a clear self-variable. If you find that you have used a lot of static methods, you should rethink your own strategy. However, the concept of static is very practical, and it needs to be used in many times. Therefore, as for the true "object-oriented", it should be left to the theorism to discuss. In fact, even if SmallTalk has something similar to static in your own "class method".
4.3 Clear: Take the end and garbage collection
Programmers know the importance of "initialization", but usually forget the importance of clearance. After all, who needs to remove an int? However, for the library, it is not always safe after use after using it. Of course, Java can use garbage collectors to recycle memory occupied by no longer use. Now consider a very special and uncommon situation. Assuming that our object is assigned a "special" memory area, no New is used. The garbage collector only knows that the memory allocated by the NEW is released, so I don't know how to release the "special" memory of the object. To solve this problem, Java provides a method called Finalize () to define it for our class. Ideally, its working principle should be like this: Once the garbage collector is ready to release the storage space occupied by the object, it first calls finalize (), and only the memory of the object is real recycled during the next garbage collection. . So if you use Finalize (), you can do some important clearance or cleaning during the garbage collection. But it is also a potential programming trap because some programmers (especially in the C development background) just started to be wrong, it is Finalize ()-destruction ()-destruction (Clear "for" destructor) in C (DESTRUCTOR) ) When an object is, it will definitely call this function. However, it is necessary to distinguish between C and Java, because C objects will definitely be cleared (the factors of the programming error), and Java objects are not certain as garbage to be "collected". Or in other words:
Garbage collection is not equal to "destruction"!
If you keep in mind this, the possibility of stepping onto the trap will be greatly reduced. It means that some actions must be taken before we no longer need an object, and these actions must be taken by yourself. Java does not provide "destroyer" or similar concepts, so you must create an original method to make this clearance. For example, assuming that during the object creation process, it will depict himself on the screen. If it is not clearly deleted from the screen, it may never be cleared. If a certain delete mechanism is placed in Finalize (), it is assumed that the object is taken as garbage, and the image will first remove itself from the screen. But if it is not received, the image will remain. So the second focus on remember is:
Our objects may not be treated as garbage!
Sometimes it may be found that the storage space of an object will never be released, because your program is always close to the critical point of the light space. If the program is completed, and the garbage collector has never released the storage space of any object we created, those resources will return to the operating system as the programs are exited. This is a good thing because the garbage collection itself has to consume some overhead. If you never need it, you will never have to expense this part of this overhead.
4.3.1 Finalize () What is the purpose?
At this point, everyone may have believed that it should use Finalize () as a clear method of conventional use. What is the benefit of it?
The third focus to remember is:
Garbage collection is only related to memory!
That is, the only reason for the existence of garbage collectors is to use memory that is no longer used. Therefore, for any activities related to garbage collection, the most worthless of the Finalize () method, which must also be related to the memory and its recycling.
But does this mean that if an object contains other objects, Finalize () should explicitly release those objects? The answer is that the negative-garbage collector will be responsible for releasing the memory occupied by all objects, regardless of how these objects are created. It will limit the requirements of Finalize () to special situations. In this case, our object can assign some storage space with different methods when creating objects. But everyone may notice that everything in Java is an object, so what is this? The reason why it is necessary to use Finalize (), it seems to be because there is a way to take a different approach to Java's ordinary approach to do some C style. This can primarily be performed by "inherent methods", which is a way to call non-Java methods in Java (the problem of inherent methods is discussed in Appendix A). C and C are the only language that is currently supported by inherent methods. However, because they can call subroutines written in other languages, anything can be effectively called. Inside the non-Java code, you may call the malloc () series function to allocate storage space with it. Moreover, unless free () is called, the storage space will not be released, resulting in the appearance of memory "vulnerability". Of course, Free () is a C and C functions, so we need to call it in a inherent method within Finalize ().
After reading the above text, everyone may have figured out that you don't have to use Finalize (). This idea is correct; it is not an ideal place for normal clearance. So what should ordinary clearance work?
4.3.2 Must perform clear
To clear an object, the user of the object must call a clear method in a location where you want to clean it. This seems to be easy to do, but it is slightly conflicted with the concept of C "destroyer". In C , all objects are broken (clear). Alternatively, in other words, all objects should be "should" destroy. If C object creates a local object, for example, created in the stack (it is impossible in Java), then clear or destroy the work of the "endparent bracket", create this object's scope of the field get on. If the object is created with NEW (similar to Java), then the corresponding destroyer is called when the programmer calls the C DELETE command (Java without this command). If the programmer has forgotten, then never call the destroyer, we will finally get a memory "vulnerability", and other parts of the object will never be cleared.
Instead, Java does not allow us to create local (local) objects - no matter how you use New. But in Java, there is no "delete" command to release the object, because the garbage collector will help us freely release the storage space. So if we stand in a more simplified position, we can say that Java has no destructive device because there is a waste collection mechanism. However, as learned in the future, it will know that the existence of the garbage collector does not completely eliminate the needs of the destroyer, or the need to eliminate the mechanism of the destroyer (and absolutely can't call Finalize () So try to avoid it). If you want to perform some other forms of cleaning other than releasing storage space, one method in Java must still be called. It is equivalent to C destroyer, but it is not convenient.
One of the most useful places of Finalize () is to observe the process of garbage collection. The following example shows you the process of garbage collection, and summarized the previous statement.
Page 165-166
The above program created a lot of Chair objects, and some time after the garbage collector started running, the program stopped creating a chair. Since the garbage collector may run at any time, we can't know when it starts. Therefore, the program uses a mark named GCRUN to indicate whether the garbage collector has started running. With the second tag F, Chair can tell main () it should stop the generation of the object. Both of these tags are set in Finalize (), which is called during garbage collection. The other two static variables --created and finalized - the number of objects used to track the number of objects that have been created and the number of objects that have been finished in the garbage collector. Finally, every chair has its own (non-Static) INT I, so how much it can be tracked to understand the specific number. After the number of 47-numbered CHAIR is finished, the tag will be set to TRUE, and finally end the CHAIR object's creation process.
All of this is carried out inside Main () - in the following cycle:
While (! chair.f) {
New chair ();
New String ("To Take Up Space");
}
Everyone may puzzle this loop will stop, because there is no statement that changes the chair.f value. However, the Finalize () process changes this value until the object of the number 47 is finally processed.
String objects created during each cycle are only extra garbage, which is used to attract garbage collectors - once the garbage collector feels "nervous" to the capacity of available memory, it will pay attention to it.
When running this program, it provides a command line to variable "before" or "instator". Among them, "Before" argument calls the System.gc () method (forcing the garbage collector) while also calling the System.RunFinalization () method for the end of the finish. These methods can be used in Java 1.0, but the RunfinalizersoneXIT () method called by using an "AFTER" argument is only the Java 1.1 and subsequent versions provide support (notes 3). Note that this method can be called at any time of the program, and the execution of the end of the end is not related to whether the garbage collector is running.
3: Unfortunately, Java 1.0 uses the garbage collector program never call finalize (). Therefore, the Finalize () method (especially those used to close files) do not be called. Some articles now claim that all the final modules will be called when the program exits - even when it is aborted, the garbage collector has not taken action for those objects. This is not true, so we can't expect femalize () to call all objects. In particular, Finalize () is almost useless in Java 1.0.
The previous program reveals us: In Java 1.1, the final module will definitely run this promise has become a reality - but the premise is that we have clearly enforced this operation. If you use an argument that is not a "before" or "after" (such as "none"), then the two end do not do, and we will get an output like this:
Created 47
Page 167-168
Therefore, when the program is completed, not all the end of the final module will be called (notes 4). To force the end, you can call System.gc () first, then call System.RunFinalization (). This can be cleared to all objects that are not used so far. This is a slightly strange place to call GC () before calling runfinalization (), which seems to be a bit conflicted with Sun's documentation, which claims to run the finals module and release the storage space. However, if RunFinalization () is first called here, the GC () is called, and the final module will not be executed at all. 4: When you read this book, some Java Virtual Machines (JVM) may have begun to show different behaviors.
For all objects, Java 1.1 sometimes will default to skipping the finishing work, because it thinks that this is too big. Regardless of which method for mandatory garbage collection, it is possible to note that the longer time delay is longer than no additional ending.
4.4 Members Initialization
Java does our best to ensure that all variables can be properly initialized before use. If it is defined as a "local" variable relative to a method, this guarantee is expressed by the error prompt of the compile period. Therefore, if you use the following code:
Void f () {
INT I;
i ;
}
I will receive an error message and tell you that I may have not been initialized. Of course, the compiler can also give a default value for i, but it looks more like a programmer's mistake, at this time, the default value will "help". If the programmer provides an initial value, it is often able to help him / her to correct the "bug" in the program.
However, if the basic type (main type) is set to a class of data members, the situation will become slightly different. Since any method can initialize or use that data, it may not be an actual practice before formally use the data, if it is also forced programmers to initialize it into an appropriate value. However, if it gives it to a garbage value, it is also very unsafe. Therefore, all basic types of data members of a class ensure a initial value. You can use the following small programs to see these values:
Page 169
The input results are as follows:
170 pages
Among them, the char value is empty (NULL), no data is printed.
You will see later: When you define an object handle internally, if you do not initialize it into a new object, the handle will get an null value.
4.4.1 Initialization
What happens if you want to give an initial value for the variable? To achieve this purpose, a most direct approach is to assign a variable inside the class (notice that C can not do this, although C novices always "want to" do this). In the following, the field definition inside the Measurement class has changed, and the initial value is provided:
170-page program
An object of non-basic (main) type can also be initialized by the same method. If Depth is a class, then a variable is inserted below and initializes:
Class measurement {
DEPTH O = New Depth ();
Boolean b = true;
//.
If you have not specified an initial value for o, you will get a run error prompt regardless of everything, telling you an error named "illegal" (details of Chapter 9).
You can even provide an initial value by calling a method: Class Cinit {
INT i = f ();
// ...
}
Of course, this method can also use arguments, but those self-variables are not already initialized. Therefore, this is legal:
Class cinit {
INT i = f ();
INT j = g (i);
// ...
}
But this is illegal below:
Class cinit {
INT j = g (i);
INT i = f ();
// ...
}
This is exactly one place that the compiler is not adapted to "forward reference" because it is related to the order of initialization, not related to the program's compilation.
This initialization method is very simple and intuitive. One of its restrictions is that each object of the type Measurement will obtain the same initialization value. Sometimes this is the result of our hopes, but sometimes it needs to be more flexible.
4.4.2 Builder Initialization
The initialization process can be performed with the builder. This can get a bigger flexibility when programming, as we can call the method and take action in the running period, so that the "field" determines the initialization value. But pay attention to this: Does not prevent automatic initialization, it will happen before the builder enters. Therefore, if you use the following code:
Class counter {
INT I;
Counter () {i = 7;
//.
Then i will first initialize zero, then become 7. This is true for all basic types and object handles, including those that have been explicitly initialized when defined. Considering this reason, the compiler does not try to force us to initialize the elements any particular place for the builder, or before they use (notes 5).
5: Instead, C has its own "builder initial module list", which can be initialized before entering the builder body, and it is forcibly for objects. See "Thinking In C ".
Initialization order
In one class, the order in which the initialization is determined by the definition order of the variables within the class. Even if the variable defines a large amount spread to the middle of the method, those variables will still be initialized before calling any way - even before the builder calls. E.g:
172-173 page program
In the Card, the definition of Tag objects deliberately spread to prove that they will be initialized before the builder enters or occurs anything else. In addition, T3 is reinitialized inside the builder. Its input results are as follows:
173 pages
Therefore, the T3 handle will be initialized twice, once before the builder calls, during the call (the first object will be discarded, so it can be treated as garbage). From the surface, this seems to be inefficient, but it guarantees the correct initialization - if a overloaded builder is defined, it does not initialize T3; at the same time, it does not specify the "default" initialization mode in the definition of T3, then What consequence will it produce?
2. Initialization of static data
If the data is static, then the same thing will happen; if it belongs to a basic type (main type), it will automatically obtain its own standard basic type initial value; if it point to A null value (NULL) is obtained unless a new object is created unless a new object is created.
If you want to initialize while definition, the method taken is the same as the non-static value surface. However, because the static value has only one storage area, no matter how many objects created, it will inevitably encounter when the storage area is initialized. The following example can be more clear about this question: 174-175
Bowl allows us to check a category creation process, while Table and Cupboard can create Static members that spread to Bowl in class definitions. Note Before the Static definition, Cupboard first created a non-Static Bowl B3. Its output is as follows:
175 pages
STATIC initialization is only available when necessary. If you do not create a Table object, but never reference Table.b1 or Table.b2, Static Bowl B1 and B2 will never be created. However, they will only create after the first Table object is created (or the first Static Access). After that, the Static object will not reiniterate.
The order of initialization is first static (if they have not been initialized by the previous object creation process), then the non-Static object. Everyone can find the corresponding evidence from the output results.
It is necessary to summarize the creation process of the object. Consider a class named Dog:
(1) When an object of type DOG is created for first time, or the STATIC method / static field of the Dog class is first accessed, the Java interpreter must find Dog.Class (search in advance in advance).
(2) After finding dog.class (it creates a Class object, this will be learned later), all of its STATIC initial modules will run. Therefore, STATIC initialization only occurs once - when the Class object is loaded first.
(3) When you create a New Dog (), the build process of the DOG object will first assign enough storage space for a DOG object in the memory stack (HEAP).
(4) This storage space will be zero, set all the basic types in the Dog to their default values (zero for numbers, and equivalence settings of Boolean and Char).
(5) All initialization that occurs when the field definition is performed.
(6) Execute the builder. As will be said in Chapter 6, this actually requires considerable operation, especially when involving inheritance.
3. Clear static initialization
Java allows us to divide other Static initialization efforts to a special "Static Build Sentence" in the class (sometimes called "static block"). It looks like this:
Page 176
Although it looks like a method, it is actually just a static keyword, followed by a method body. Like other static initialization, this code is only once - when an object of that class is generated, or when a STIC member belongs to That class is first visited (even if it has never generated the object). E.g:
176-177 page program
When the Static object C1 is accessed within the line marked (1), or when the row (1) is marked as a comment, the STATIC initialization module for CUPS is running when the line (2) is not marked. If (1) and (2) are marked into a comment, the STATIC initialization process used for CUPS will never happen.
4. Initialization of non-static instances
Java 1.1 provides a similar grammar format for the initialization of the non-static variable of each object. Below is an example:
177-178 page program
Everyone can see the instance initialization clause:
178 page program
It looks extremely similar to static initialization, just the static keyword disappears. In order to support the initialization of "anonymous internal classes" (see Chapter 7), this language must be used. 4.5 array initialization
The initialization of the initialization in C is very easy to make mistakes, and quite troublesome. C makes it safer (annotated 6) via "set initialization". Java does not have a "collection" concept like C , because everything in Java is an object. But it does have your own array, support by array initialization.
Arranges represent a series of objects or basic data types, all the same types are encapsulated - use a unified identifier name. The definition of arrays is done by square bracket index operators ([]). To define an array, just simply follow the pair of empty brackets after the type name:
Int [] Al;
You can also place square brackets behind the identifier to get a fully consistent result:
Int al [];
This format is consistent with the format of C and C programmer habits. However, the most "Tongshou" may be a former syntax because it indicates that the type is "an int array". This book will take the format.
The compiler does not allow us to tell it how big is an array. This makes us return to the "handle" issue. At this point, everything we have is to point to a handle of an array, and have not allocated any space to the array. In order to create a corresponding storage space to array, a initialization expression must be written. For arrays, initialization work can appear anywhere in the code, but a special initialization expression can be used, which must appear in the array created. This special initialization is a value that is closed by the curly bracket. Allocation of storage space (equivalent to using new) will be performed by the compiler in this case. E.g:
int [] a1 = {1, 2, 3, 4, 5};
So why do you want to define an array handle without array?
int [] A2;
In fact, in Java, you can assign an array to another, so you can use the following statement:
A2 = a1;
We are really ready to do to copy a handle, just like the demonstration below:
Page 180
Everyone see A1 gets an initial value, and A2 is not; A2 will assign a value in the future - this is assigned to another array.
There are also new things here: all arrays have an essential member (whether they are an object array or a basic type array), can query it - but not change, so that how many elements have been included in the array. This member is Length. Similar to C and C , since the Java array is counted from element 0, the maximum element number that can be indexed is "Length-1". If you exceed the boundaries, C and C will "silently", and allow us to use our own memory, this is the root of many program errors. However, Java can retain our damage to this problem, and once the border exceeds the boundary, it generates a running period error (ie a "violation", this is the topic of Chapter 9). Of course, since the access to each array is required, it consumes a certain amount of time and excess amount, and there is no way to turn it off. This means that array access may become an important reason for low program efficiency - if they are in critical occasions. However, considering the security of Internet access, as well as programmers programming efficiency, Java designers should still think it is worth it.
During the programming, if you don't know how much element is needed in your own array, what should I do? At this point, you only need to create an element in an array with NEW. Here, even if you are ready to create an array of basic data types, New also works normally (New does not create basic types of non-array): 180-181 Page
Since the size of the array is randomly determined (using the previously defined PRAND () method), it is very clear that the creation of the array is actually performed during operation. In addition, from the output of this program, you can see that the array element of the basic data type will automatically initialize "empty" value (for the value, the null value is zero; for char, it is null; for Boolean, it It is false.
Of course, arrays may have been defined and initialized in the same statements, as shown below:
Int [] a = new int [PRAND (20)];
If you operate, a non-basic type object is an array of objects, then use New in anyway. Here, we will once again encounter a handle, because we create a handle array. Please observe the type of Packaging type Integer, it is a class, not the basic data type:
181-182 page
Here, even after the New is called, you will start to create arrays:
Integer [] a = new integer [PRAND (20)];
It is just a handle array, unless you initialize the object handle by creating a new Integer object, otherwise the initialization process will not end:
a [i] = new integer (PRAND (500));
But if you forget to create an object, you will get a "violation" error when you try to read the empty array position.
Let's take a look at the composition of the String object in the print statement. Everyone can see the handle to the Integer object, will automatically convert, resulting in a string, which represents the value within the object.
You can also enclose the list of objects to initialize the array of objects. Two forms can be used, the first is the only form allowed by Java 1.0. The second (equivalent) form is supported from Java 1.1:
Page 182-183
This approach is useful to most, but the limit is also the greatest, because the size of the array is determined during compilation. The last comma of the initialization list is optional (this feature makes the long list more easily).
The second form of the initialization of the array (Java 1.1 start support) provides a simpler grammar, can create and call methods, obtain "variable parameter list" with C (c usually refer to it as "changing parameters") Consistent effect. These effects include unknown parameter (independent variable) and unknown types (if such choice). Since all classes are ultimately inherited from the general root Object, you can create a method to get an Object array, and call it below:
Page 183
At this point, we do not take too much operation on these unknown objects, and this program uses automatic string to do some useful things to each Object. In Chapter 11 (Runtime Type Identification or RTTI), you will also learn how to investigate the exact type of such objects, so that you can do some interesting things to them.
4.5.1 multi-dimensional array
Multi-dimensional arrays can be easily created in Java:
Page 184-185
LENGTH is used in the code used to print, so it does not have to rely on the fixed array size. The first example shows a multi-dimensional array of basic data types. We can set the boundary of each vector in the array:
int [] [] a1 = {
{1, 2, 3,},
{4, 5, 6,},
}
Each square bracket pair will move us to the next level of the array.
The second example shows a three-dimensional array of NEW assigned. Here, all arrays are allocated immediately:
int [] [] [] a2 = new int [2] [2] [4];
However, the third example has revealed that each vector constituting the matrix can have any length:
Page 186
For arrays created by the first NEW, its first element is random, and the length of other elements is not defined. The second new New in the for loop will fill in the elements, but keep the third index of the undetermined state - until the third NEW is encountered.
Depending on the output result, you can see: If you don't make a clear specified initialization value, the array value will automatically initialize into zero.
An array of non-basic type objects can be processed with similar forms. This can be seen from the fourth example that it demonstrates us to collect multiple new expressions with curly brackets:
Page 186
The fifth example shows how to gradually build an array of non-basic types:
186
i * J is just an interesting value in Integer.
4.6 Summary
As an initialization, the builder should make you clearly feel the importance of initialization in the language. Like the C programming, it is judged how a program efficiency is to see if it has caused a serious programming error (bug) due to incorrect induction of variables. These forms of errors are difficult to find, and similar problems also apply to incorrect clear or finishing work. Since the builder allows us to ensure proper initialization and clear (if there is no correct builder call, the compiler does not allow object creation), so complete control and security can be obtained.
In C , it is also important to work with the "Destruction" (Destruction) work as "construct" because the object created with NEW must be clearly cleared. In Java, the garbage collector will automatically release memory for all objects, so the clearance method in Java is not often used. If you don't need to be similar to the builder, Java's garbage collector can greatly simplify programming, and add greater security during the management process of memory. Some garbage collectors can even clear other resources such as graphics and file handles. However, the garbage collector has indeed increased the overhead of the runtime. But this overhead has caused a lot of impact, it is difficult to see, because so far, the overall running speed of the Java interpreter is still slow. With the change of this situation, we should be able to determine whether the overhead of the garbage collector makes Java unsuitable to do some specific work (one of the problems is the non-predictable nature of the garbage collector).
Since all objects are certainly able to get the right build, the fact that the builder is actually doing much more. In particular, when we generate new classes through "Creation" or "Inherit", the guarantee of the build is still valid, and some additional syntax is required to provide support for it. Everyone will learn more about creation, inherit, and their impact on the builder in the subsequent chapters.
4.7 exercises
(1) Create a class (no argument) with the default builder, print a message with it. Create an object belonging to this class.
(2) Add an overloaded builder on the basis of practice 1, which uses a string independent variable and prints out with your own message.
(3) Based on the class created by Exercise 2, create an array belonging to its object handle, but don't actually create objects and assign it into an array. When running the program, pay attention to whether to print an initialization message from the builder call. (4) Create an object linked to the array of handles, and finally complete the exercise 3.
(5) Run the program with the variable "before", "After" and "None", test Garbage.java. Repeat this operation, observe whether some fixed modes have been seen from the output. Change the code so that system.runfinalization () is called before system.gc (), and then observes the result.