2.6.3 static keyword
Generally, when creating a class, we will point out the appearance and behavior of the Class Object. Unless you use new to create an object for that class,
Otherwise, you will not actually get anything. The data storage space will be formally generated and corresponding methods can be used only after new is executed.
However, in two special cases, the above method is not usable.
One case is that you only want to use a storage area to save a specific data-no matter how many objects you want to create,
Even no object is created. Another scenario is that we need a special method that is not associated with any objects in this class.
That is to say, a method that can be called is required even if no object is created. To meet these two requirements, you can use the static keyword.
Once something is set to static, data or methods will not be associated with any object instance of that class.
So even if you have never created an object of that class, you can still call a static method or access some static data.
Before that, for non-static data and methods, we must create an object and use that object to access data or methods.
This is because non-static data and methods must know the specific objects they operate on. Before official use,
Since static methods do not need to create any objects, they cannot simply call other members without referencing a named object,
To directly access non-static members or methods (because non-static members and methods must be associated with a specific object ).
Some object-oriented languages use the terms "class data" and "class method.
They mean that data and methods exist only for the class as a whole, not for any specific object of that class.
Sometimes, you may find such a name in other Java books.
To set the data member or method to static, you only need to define the prefix and the keyword.
Bytes -----------------------------------------------------------------------------------------------------
1. You can call other constructors in the constructor. You must first call the constructor before initializing the variable. You cannot call more than two constructor instances.
2. 4.4 initialize members
4.4.1 required Initialization
4.4.2 builder Initialization
Bytes ---------------------------------------------------------------------------------------------
1. Initialization order
In a class, the initialization sequence is determined by the sequence of variables defined in the class. Even if a large number of variable definitions exist in the middle of a method definition,
Those variables will still be initialized before any method is called-even before the builder calls them.
2. Static Data Initialization
If the data is static, the same thing will happen;
If it belongs to a basic type (primary type) and is not initialized, it will automatically obtain its own standard basic type initial value;
If it is a handle to an object, a null value is obtained unless you create an object and connect the handle with it ).
If you want to initialize at the same time as the definition, the method taken looks the same as the non-static value surface.
However, since the static value has only one storage region, no matter how many objects are created,
Will inevitably encounter the problem of when to initialize the storage area.
Static Initialization is performed only when necessary.
The initialization sequence is static first (if they are not initialized by the previous object creation process), followed by non-static objects.
It is necessary to summarize the object creation process here. Consider a class named dog:
(1) When a dog-type object is first created, or when the static method or static field of the Dog class is accessed for the first time, the Java interpreter must find the dog. class (search in the preset class path ).
(2) After finding dog. Class (it creates a class object, which will be learned later), all its static initialization modules will run. Therefore, static initialization only occurs once-when the class object is loaded for the first time.
(3) when creating a new dog (), the building process of the dog object first allocates enough storage space for a dog object in the memory heap.
(4) the bucket is cleared to zero, and all basic types in dog are set to their default values (0 is used for numbers, and the equivalent settings of Boolean and char ).
(5) All initialization tasks that occur during field definition are executed.
(6) execute the builder. As described in Chapter 6th, this may actually require a considerable number of operations, especially when inheritance is involved.
3. Explicit static Initialization
Java allows us to divide other static initialization work into a special "static build clause" (sometimes called "static block") in the class.
It looks like the following:
Class spoon {
Static int I;
Static {
I = 47;
}
4. Non-static instance Initialization
Java 1.1 provides a similar syntax format for initializing non-static variables of each object.
Bytes ---------------------------------------------------------------------------------------------
To initialize the handle, perform the following operations:
(1) When the object is defined. This means they will certainly be initialized before the builder calls them.
(2) In the builder of that class.
(3) It is immediately prior to the actual use of that object. This reduces unnecessary overhead-if the object does not need to be created.
6.9 initialization and class loading
In many traditional languages, programs are loaded at one time as part of the startup process. Initialization is followed by the formal execution of the program.
In these languages, the initialization process must be carefully controlled to ensure that static data initialization will not cause any trouble.
For example, if another static data is expected to be a valid value before a static data is obtained for initialization, it may cause problems in C ++.
Java does not have this problem because it uses different loading methods. Because everything in Java is an object,
So many activities become simpler. This problem is one of them. As described in the next chapter,
The Code of each object is stored in an independent file. Unless you really need code, the file will not be loaded.
Generally, we can think that the Code will not be loaded unless an object of that class is constructed.
Because the static method has some slight ambiguity, it can also be considered that "class code is loaded when it is used for the first time ".
The first time static initialization occurs. When loading,
All static objects and static code blocks are initialized in the original order (that is, the order in which they are written in the class definition code ).
Of course, static data is initialized only once.
6.9.1 inheritance Initialization
We need to understand the entire initialization process, including inheritance, and have a holistic concept of what happens in this process.
Observe the following code:
//: Beetle. Java
// The full process of initialization.
Class insect {
Int I = 9;
Int J;
Insect (){
PRT ("I =" + I + ", j =" + J );
J = 39;
}
Static int X1 =
PRT ("static insect. X1 initialized ");
Static int PRT (string s ){
System. Out. println (s );
Return 47;
}
}
Public class beetle extends insect {
Int K = PRT ("beetle. K initialized ");
Beetle (){
PRT ("k =" + k );
PRT ("J =" + J );
}
Static int X2 =
PRT ("static beetle. X2 initialized ");
Static int PRT (string s ){
System. Out. println (s );
Return 63;
}
Public static void main (string [] ARGs ){
PRT ("beetle constructor ");
Beetle B = new beetle ();
}
}///:~
The output of this program is as follows:
Static insect. x initialized
Static beetle. x initialized
Beetle Constructor
I = 9, j = 0
Beetle. K initialized
K = 63
J = 39
When running Java on beetle, the first thing that happens is to load the program and find the class outside. During the loading process,
The loader notices that it has a basic class (that is, the meaning of the extends keyword), so it loads it.
This process will occur no matter whether you are preparing to generate an object of that basic class (please try to annotate the object creation code as a comment,
Confirm it by yourself ).
If the base class contains another base class, the other base class is loaded immediately, and so on. Next,
Static initialization will be performed in the root base class (insect at this time), and then in the next category class, and so on.
It is critical to ensure this order, because the initialization of the category class may depend on the correct initialization of the base class members.
In this case, all necessary classes have been loaded, so you can create objects.
First, all basic data types in this object are set to their default values, and the object handle is set to null.
Then, the basic class builder is called. In this case, the call is automatically performed.
However, super can also be used to specify the builder call from the line (just like the first operation in the beetle () builder ).
The construction of the basic class adopts the same processing process as the pipeline class builder. After the basic shun builder is complete,
Instance variables are initialized in the original order. Finally, execute the remaining body parts of the builder.
--------------------------------------------------------------------------------
6.8 final keywords
Due to different contexts (application environments), the meanings of final keywords may be slightly different. But its most common meaning is to declare
"This thing cannot be changed ". To prohibit changes, two factors may be taken into account: design or efficiency. There are some differences between the two reasons,
So it may cause misuse of final keywords.
In the next section, we will discuss three Use Cases of final keywords: data, methods, and classes.
6.8.1 final data
Many programming languages have their own solutions to tell the compiler that a piece of data is a constant ". Constants are mainly used in the following two aspects:
(1) compile-time constant, which will never change
(2) A value initialized at runtime. We do not want it to change.
For constants in the compilation period, the compiler (Program) can "encapsulate" the constant values into the required computing process. That is to say,
Computing can be executed in advance during compilation, saving some runtime expenses. In Java,
Constants in these forms must belong to the basic data type (primitives) and must be expressed using the final keyword.
When defining such a constant, a value must be given.
Either the static or final fields can store only one data and cannot be changed.
If final is used along with the object handle, rather than the basic data type, its meaning is a bit confusing.
For the basic data type, final converts the value to a constant, but for the object handle, final converts the handle to a constant.
. During the declaration, the handle must be initialized to a specific object. In addition, the handle cannot be changed to another object.
However, the object itself can be modified. Java does not provide any means to convert an object directly into a constant (however,
We can write a class by ourselves to make the objects have the "constant" effect ). This restriction applies to arrays, which are also objects.
2. Blank final
Java 1.1 allows us to create "blank final", which belongs to some special fields. Although declared as final,
However, no initial value is obtained. In either case, blank final must be correctly initialized before actual use.
And the compiler will take the initiative to ensure that this provision is implemented. However, blank final has the maximum flexibility for various final keyword applications.
For example, a final field inside the class can be different for each object, while still keeping its "unchanged" nature.
3. Final independent variable
Java 1.1 allows us to set the independent variables to the final attribute by declaring them in the list of independent variables.
This means that within a method, we cannot change what the independent variable handle points.
Note that a null (null) handle can still be assigned to the final independent variable at this time, and the compiler will not capture it.
This is the same as the operation we take on non-final independent variables.
We can only read the independent variable and cannot change it.
6.8.2 final Method
The final method may be used for two reasons. The first is to "Lock" the method ",
Prevent any inheritance class from changing its original meaning. When designing a program, if you want the behavior of a method to remain unchanged during the inheritance period,
In addition, this approach can be taken without being overwritten or rewritten.
The second reason for using the final method is the efficiency of program execution. After you set a method to final,
The compiler can put all calls to that method into "embedded" calls. As long as the compiler finds a final method call,
The general code Insertion Method (pushing the independent variables into the stack) adopted for the execution method call mechanism will be ignored (based on its own judgment;
Jump to the method code and execute it; jump back; clear the stack independent variable; and finally process the returned value ). On the contrary,
It replaces the method call with a copy of the actual code in the method body. This avoids system overhead during method calls.
Of course, if the method size is too large, the program will become swollen and may not be able to achieve any performance improvement caused by embedded code.
Because any improvement is offset by the time spent in the method. The Java compiler can automatically detect these situations and is "wise"
Determines whether to embed a final method. However, it is best not to fully trust the compiler to make all the judgments correctly.
Generally, you should consider setting a method as final only when the amount of code for the method is very small or you want to explicitly prohibit the method from being overwritten.
All private methods in the class are automatically final. Because we cannot access a private method,
So it will never be overwritten by other methods (if forced, the compiler will give an error message ).
You can add a final indicator for a private method, but cannot provide any additional meaning for that method.
6.8.3 final class
If the entire class is final (with the final keyword before its definition), it indicates that you do not want to inherit from this class,
Or no one else is allowed to perform this operation. In other words, for one reason or another, we certainly do not need to make any changes to the class;
Or for security reasons, we do not want to subclass it ).
In addition, we may also consider execution efficiency and want to ensure that all actions involving all objects of this class are as effective as possible.
Note that the data member can be either final or not, depending on our specific choice. Rules applied to final are also applicable to data members,
Whether the class is defined as final or not. After a class is defined as final, the result is that inheritance is not allowed-there are no more restrictions. However,
Because it prohibits inheritance, all methods in a final class are final by default. Because they cannot be overwritten at this time.
Therefore, the compiler has the same efficiency choice as we explicitly declare a method as final.
You can add a final indicator to a method in the final class, but this does not make any sense.
6.8.4 considerations for final
When designing a class, you often need to consider whether to set a method as final. You may think that the execution efficiency is very important when using your own classes,
No one wants to overwrite their methods. This idea is correct in some cases.
But be careful when making your own assumptions. Generally, it is difficult to predict the form in which a class will be recycled or reused in the future.
This is especially true for general-purpose classes. If you define a method as final,
This may eliminate the way in which other programmers inherit their classes in their projects, because we didn't even think it would be used like that.
The standard Java library is the best example to illustrate this point of view. One of the most common classes is vector. If we consider the code execution efficiency,
You will find that you can play a greater role only by not setting any method as final.
We can easily think of a class that we should inherit and override, but its designers deny our ideas.
But we can refute them for at least two reasons. First, stack is inherited from vector,
That is, stack "is" a vector, which is inaccurate. Secondly, for many important methods of vector,
For example, addelement () and elementat () are both synchronized (synchronous ).
As mentioned in chapter 14th, this will cause significant performance overhead and may offset the performance improvements provided by final.
Therefore, programmers have to guess where optimization should be made. This clumsy design was adopted in the Standard library,
I can't imagine what kind of emotion will be triggered by programmers.
Another important thing to note is hashtable, which is another important standard class. This class does not use any final method.
As we mentioned elsewhere in this book, it is clear that some class designers have completely different qualities from other designers.
(Note: Compare hashtable's extremely short method name with vecor's method name ). For class library users, this is obviously not so easy to see.
When the design of a product becomes inconsistent, the user workload will be increased.
This also emphasizes the strong sense of responsibility in code design and inspection from another aspect.
Bytes --------------------------------------------------------------------------------------------------
7.6 internal class
--------------------------------------------------------------------
In Java 1.1, one class definition can be placed in another class definition. This is called "internal class ".
Internal classes are very useful to us, because they can be used to group logically associated classes,
You can also control the "visibility" of a class in another class ". However,
We must realize that there is a fundamental difference between the internal class and the previously described "synthesis" method.
In general, the internal Department class needs are not particularly obvious, at least they do not immediately feel that they need to use the internal class.
At the end of this chapter, we will find a special example after introducing all the syntaxes of internal classes.
It should be able to clearly recognize the benefits of internal classes.
The process of creating an internal class is unremarkable: place the class definition into a class that encapsulates it.
If you want to generate an object of the internal class anywhere outside of the non-static method of the except Department class,
You must set the object type to "external class name. Internal class name ",
7.6.1 internal class and traceability
So far, the internal class still seems to have nothing special.
After all, it seems a little difficult to hide it.
Java already has an excellent hiding mechanism-only allow classes to be "friendly" (only visible in one package ),
Instead of creating an internal class.
However, when we are going to create a basic class (especially an interface,
The internal class begins to play its key role (from the interface handle generated by the object to be implemented, it has the same effect as the last shape to a basic class ).
This is because the internal class can then completely enter the invisible or unavailable state-this will happen to anyone.
Therefore, we can easily hide implementation details. All we get is the handle of a base class or interface,
And you may not even know the exact type.
7.6.2 internal classes in methods and scopes
So far, we have basically understood the typical use of internal classes. For code that involves internal classes,
It is usually a "simple" internal class, which is very simple and easy to understand.
However, the design of internal classes is very comprehensive,
Inevitably, there will be a lot of other usage of them-if we create an internal class in a method or even an arbitrary scope.
There are two reasons for us to do this:
(1) As shown above, we are ready to implement some form of interface so that we can create and return a handle.
(2) to solve a complicated problem and create a class to assist your program solution. At the same time, I am not willing to make it public.
In the following example, the previous code is modified for use:
(1) class defined in a method
(2) classes defined within the scope of a Method
(3) An anonymous class used to implement an Interface
(4) An anonymous class is used to extend a class with a non-default builder
(5) An anonymous class for field Initialization
(6) construct an anonymous class through instance initialization (the anonymous internal class cannot own the builder)
7.6.4 static internal class
To correctly understand the meaning of static when applied to an internal class, you must remember that the object of the internal class holds the handle of an object of the encapsulated class that created it by default.
However, if we say an internal class is static, this statement is not true. Static internal class means:
(1) To create a static internal class object, we do not need an external class object.
(2) An external class object cannot be accessed from an object of the static internal class.
But there are some restrictions: Because static members can only be at the external level of a class,
Therefore, internal classes cannot have static data or static internal classes.
If you do not need to create an object of an external class to create an internal class object, you can set everything to static.
To work properly, you must also set the internal class to static
7.7 builder and multipleth
As usual, the builder differs from other types of methods. This method is still valid after the problem of polymorphism is involved.
Although the builder does not have the polymorphism (even if you can use a "virtual builder"-as described in Chapter 11th ),
However, it is still necessary to understand how the builder can be used in a complex hierarchical structure and along with the construction.
This understanding will help you avoid some unpleasant disputes.
7.7.1 sequence of builder calls
The sequence of builder calls has been briefly described in Chapter 4th, but that is what was said before the introduction of inheritance and polymorphism issues.
The builder used for the basic class must be called in the builder of the worker class, and is gradually linked up,
The builder used by each basic class can be called. This is because the builder has a special task:
Check whether the object is correctly built. A member class can only access its own members, but cannot access members of the basic class.
(These members usually have private attributes ).
Only the base class builder knows the correct method and has the appropriate permissions when initializing its own elements.
Therefore, all constructors must be called, otherwise the entire object construction may be incorrect.
That is why the compiler forces the builder to call every part of the Compiler class.
In the pipeline builder body, if we do not explicitly specify a call to a basic class builder,
It calls the default builder silently. If the default builder does not exist,
The compiler reports an error (if a class does not have a builder, the compiler automatically organizes a default builder ).
For a complex object, the builder calls the following sequence:
(1) call the base class builder. This step will be repeated. The first step is to build the root of the hierarchical structure,
Then there is the next category class, and so on. Until the category class reaches the deepest layer.
(2) Call the member initialization module in declared order.
(3) call the principal of the derivative builder.
The sequence of builder calls is very important. When carrying out inheritance, we know everything about the basic class,
And can access any public and protected members of the base class. This means that when we are in the runtime class,
It must be assumed that all members of the base class are valid. A standard method has been adopted to build an action,
Therefore, the members of all parts of the object have been constructed. However, inside the builder, make sure that all the Members used are built.
To meet this requirement, the only way is to call the basic class builder first. Then, after entering the future class builder,
All members that can be accessed in the base class have been initialized. In addition,
All member objects (objects placed in the class through the synthesis method) are defined in the class (such as B, C, and l in the preceding example ),
Since we should initialize them as much as possible, we should also ensure that all members inside the Builder are valid.
Adhering to this rule will help us determine that all basic class members and Member objects of the current object have been correctly initialized.
Unfortunately, this approach does not apply to all situations, which will be described in the next section.
The initialization sequence described in the previous section is not complete, but it is the key to solving the problem. The actual initialization process is as follows:
(1) Before any other operations are performed, the storage space allocated to the object is initialized to binary zero.
(2) call the base class Builder as described earlier. In this case, the overwritten draw () method will be called (indeed before the roundglyph builder call), and the radius value is 0, because step (1).
(3) Call the member initialization code in the Order stated previously.
(4) Call the subject of the builders class.
There is a premise to take these operations, that is, everything must be initialized to zero at least.