Read Catalogue
- Recommendation 31: No implementation code exists in the interface
- Recommendation 32: Static variables must first be declared after assignment
- Recommendation 33: Do not overwrite static methods
- Recommendation 34: Constructors are as simple as possible
- Recommendation 35: Avoid initializing other classes in the constructor
If you read more books without thinking, you will feel that you know a lot.
Books read more and think, you will feel that you do not understand more and more.
——— Voltaire
In the world of object-oriented programming (object-oriented programming, OOP), classes and objects are the real-world description tools, which are the representation of behavior and actions, encapsulation, inheritance, polymorphism are the main ways of its colorful, this chapter mainly about Java objects , methods of the various rules, limitations and recommendations.
Back to top recommendation 31: No implementation code exists in the interface
Do you feel depressed when you see such a headline? Do you have implementation code in the interface? How is that possible? It is true that an interface can declare constants, declare abstract methods, and inherit the parent interface, but there is no specific implementation, because an interface is a contract (contract), which is a framework protocol, which indicates that its implementation classes are of the same type, or a set with similar characteristics. For a generic program, the interface does not have any implementations, but in those special programs The exception is, read the following code:
1 public class Client31 {2 public static void Main (string[] args) {3 //invocation Interface Implementation 4 b.s.dosomething (); 5 } 6} 7 8//The implementation code exists in the interface 9 interface B {Ten public static final s S = new S () {one public void DoSomething () {12
SYSTEM.OUT.PRINTLN ("I implemented in the interface"); }14 };15}16 17//implemented interface interface S {public void dosomething () ; 20}
Take a closer look at the main method and note the B interface. It calls the interface constant, and without implementing any display implementation class, it prints out the result, where is the S constant (the interface s) in the B-interface being implemented? The answer is in the B interface.
A static constant s is declared in the B interface whose value is an instance object of an anonymous inner class (Anonymous Inner Class), that is, the anonymous inner class (or, of course, without anonymity, which is also allowed in the implementation of the inner class directly in the interface) implements the S interface. You see, there is also the implementation code in the interface!
This is really good, very powerful, but in general projects, this type of code is strictly forbidden, the reason is very simple: this is a very bad coding habits, interface is used for what? Interface is a contract, not only constrained implementation, but also a guarantee that the service provided (constants and methods) is stable and reliable, if the implementation code is written into the interface, the interface is bound to the factors that may change, which will lead to the implementation is no longer stable and reliable, can be discarded, changed, and reconstructed at any time. Therefore, although the interface can be implemented, but should avoid the use.
Note: The implementation code cannot appear in the interface.
Back to top recommendation 32: Static variables must first be declared after assignment
is the title as depressing as the last suggested title? What is called a variable must first be declared after the assignment? are variables in Java not all declared and used before? Is it possible to use the post-declaration first? Can we take a look at an example, the code is as follows:
1 public class Client32 {2 public static int i = 1; 3 4 static {5 i = + 6} 7 public Static V OID Main (string[] args) {8 System.out.println (i); 9 }10}
This program is very simple, output 100 well, yes, it is indeed 100, we slightly modified, the code is as follows:
1 public class Client32 {2 static {3 i = +; 4 } 5 6 public static int i = 1; 7 8 PU Blic static void Main (string[] args) {9 System.out.println (i); }11}
Notice that the Declaration and assignment of the variable I are reversed, and now the question is: Can this program be compiled? If you can compile, what is the output? Also note that this variable i is declared after the first use (i.e. assignment).
The answer is: can compile, without any problems, the output is 1. Yes, the output is 1, not 100. The output is changed, and the variable i is declared after the use of the position, is it reversed?
From the birth of a static variable, a static variable is assigned to the data area when the class is loaded, it has only one copy in memory, is not assigned multiple times, and all subsequent assignment operations are value changes, and the address remains unchanged. We know that the JVM initializes the variables by declaring the space first, then assigning the value, that is, executing separately in the JVM, equivalent to:
int i; Allocate space
i = 100; Assign value
A static variable is loaded first when the class is initialized, the JVM finds all the static declarations in the class, allocates space, and notices that this is just the assignment of the address space, which is not assigned, and then the JVM executes according to the order of static assignment in the class, including static class assignments and static block assignments. For a program, the address space of the int type is declared first, the address is passed to I, then the assignment is performed according to the order of the class, first i = 100 is executed in the static block, then I = 1, and the final result is i = 1.
Oh, that's all, what if there are multiple static blocks that continue to be assigned to I? I am still equal to 1, who has the final decision on who is in the right place.
Some programmers like to put the variable definition at the bottom of the class, if this is an instance variable good to say, there is no problem, but if it is a static variable, but also in the static block assignment, then the result is not the same as expected, so follow the Java Common Development specification "variable declaration after the assignment of value", is a good coding style.
Note: It is not a nonsense to reiterate that variables are to be declared after use.
Back to top tip 33: Do not overwrite static methods
We know that the methods and behavior of the parent class can be enhanced or weakened by overwrite (override) in Java, but that override is for non-static methods (also known as instance methods, only methods that generate instances to invoke), not for static methods (static-decorated methods, also called class methods). Let's look at an example with the following code:
1 public class Client33 {2 public static void Main (string[] args) {3 base base = new Sub (); 4 //Call non-static method 5
base.doanything (); 6 //Call static method 7 base.dosomething (); 8 } 9}10 one class Base { ///I am the parent static method public static void Dosometh ing () { System.out.println ("I am the static method of the parent class"); }16 //Parent non-static method public void Doanything () {19 System.out.println ("I am a non-static method of the parent class"); }22-Class sub extends Base {a //subclass with the same name, static method of the same parameter, public static void DoSomething () { System.out.pri Ntln ("I am a subclass static method"); }28 //Overwrite parent non-static method @Override31 public void Doanything () { System.out.println ("I am a non-static method of subclasses"); }34}
Pay attention to the program, sub-class Doanything method overwrite the parent class method, really no problem, then DoSomething method? It is the same as the method name of the parent class, input, output is the same, according to the truth should be overwrite, but in the end is not overwrite it? Let's take a look at the output: I am a subclass non-static method I am a parent class static method
This result is very confusing, the same is called the subclass method, one executes the parent class method, the difference between the two is not static modification, but get different results, why?
We know that an instance object has two types: the surface type (apparent type) and the actual type (Actual type), the surface type is the declared type, the actual type is the type when the object is produced, such as our example, the surface type of the variable base is base, and the actual type is a sub. For non-static methods, it is performed according to the actual type of the object, that is, the Doanything method in the Sub class is executed. For static methods It is very special, first static method does not depend on the instance object, it is accessed by the class name, second, the static method can be accessed through the object, if the static method is accessed through the object, the JVM will look for the object's surface type to find the entry of the static method, and then execute it. So it's no surprise that the program above prints out "I'm a non-static method of the parent class."
In a subclass, you build the same method name, input parameters, output parameters, access permissions (permissions can be extended) as the parent class method, and the parent class, the subclass is a static method, which is called hide, and it differs from the overwrite with two points:
(1), the manifestation of different: hidden for static methods, overwrite for non-static methods, the performance of the code is @override annotations can be used to overwrite, not for hiding.
(2), the responsibility is different: the purpose of hiding is to abandon the parent class static method, reproduce the subclass method, for example, our example, The appearance of sub.dosomething is to obscure the Base.dosomething method of the parent class, that is, I expect the static method of the parent class not to break the business behavior of the subclass, and overwrite is to enhance or weaken the behavior of the parent class, and continue the duties of the parent class.
Explaining so much, we look back at the title of this proposal, static method cannot overwrite, can continue a sentence, although cannot overwrite, but can hide. By the way, accessing static or static properties through an instance object is not a good habit, it brings a "bad taste" to the code and suggests that you read the precepts.
Back to top recommendation 34: Constructors to simplify as much as possible
We know that the object generated by the New keyword is bound to call the constructor, the simple complexity of the constructor will directly affect the creation of the instance object is cumbersome, in the project development, we will generally make the constructor as simple as possible, not to throw the exception, as far as possible not to do complex operations and other specifications, So what if a constructor is really complicated? Let's take a look at some code:
1 public class Client34 {2 public static void Main (string[] args) {3 Server s= new Simpleserver (+); 4 } 5 } 6 7 abstract Class Server {8 public final static int default_port = 40000, 9 public Server () {One // Obtain the port number provided by the subclass int port = Getport (), System.out.println ("Port number:" + port), and/ * for monitoring the action */15 }16 Sub-class provides port number, and for usability check protected abstract int getport ();}20 class Simpleserver extends Server {$ private I NT Port = 100;23 //Initialize pass a port number of public simpleserver (int _port) { port = _port;27 }28 // Check that the port is valid, the default port is used, the random number is used here to simulate the @Override31 protected int getport () { return math.random () > 0.5? port:default_port;33 }34 35}
The code is a simple simulation of a service class that implements the server's creation logic, and the subclass is going to create a service that listens to a port by passing a port number when the instance object is generated, the code with the following intent:
- Receive port parameters through the Simpleserver constructor;
- The constructor of the subclass calls the constructor of the parent class by default;
- The parent class constructor calls the subclass's Getport method to obtain the port number;
- The constructor of the parent class establishes the port listening mechanism;
- After the object is created, the service listener starts and runs normally.
Seems very reasonable, and then carefully look at the code, and we really agree with the intention, then we try to run a few times to see that the output is either "Port number: 40000", or "port number: 0", never appear "port number: 100" or "Port number: 1000", it is strange, 40000 fortunately said , how did that 0 come out? What's the problem with the neglect of the place?
To explain the problem, let's start by saying how subclasses are instantiated. When a subclass is instantiated, the parent class is initialized first (note that this is the initialization, not the parent object), that is, the variable of the parent class is initialized, the constructor of the parent class is called, and then the variable of the child class is initialized, the constructor of the subclass is called, and finally an instance object is generated. Knowing the relevant knowledge, let's take a look at the above procedure, which is executed as follows:
- The constructor of the subclass Simpleserver receives the parameter 1000 of the int type;
- The parent class initializes the constant, which is the Default_port initialization, and sets the value to 40000;
- Executes the parent-class parameterless constructor, which means that the subclass has a parameter constructor that contains the super () method by default;
- The parent class parameterless constructor executes to the "int port = Getport ()" method, calling the Getport method implementation of the subclass;
- The Getport method of the subclass returns the port value (note that at this point the port variable is not assigned, is 0) or Default_port (which is now 40000);
- The parent class initializes, initializes the instance variable of the subclass, and the port value is assigned a value of 100;
- Executes the subclass constructor, and the port value is re-assigned to 1000;
- Subclass Simpleserver instantiation ends and object creation is complete.
Finally, when class initialization is Getport, the return value of the method is not assigned, the port simply obtains the default initial value (the default initial value for an instance variable of type int is 0), so the server is always listening on 40000 ports (0 ports are meaningless). The problem arises from a shallow point in the initial order of the class elements, from the depths because the constructors are too complex to cause. Constructors are used as initialization variables, declaring the context of an instance, which is simple to implement, without any problems, but our example implements a complex logic that is not appropriate in a constructor.
The problem knows, the modification is also very simple, the parent class of the parameterless constructor of all the implementation is moved to a method called Start, the Simpleserver class initialization is complete, and then call its Start method can realize the server start-up work, concise and intuitive, This is how most JEE servers are implemented.
Note: The simplification and simplification of the construction function should reach the realm of "glancing through the eye" .
Back to top tip 35: Avoid initializing other classes in constructors
A constructor is a class that initializes the code that must be executed, which determines the efficiency of class initialization, and if the constructor is complex and has other classes associated with it, it can produce unexpected problems, and let's look at the following code:
1 public class Client35 {2 public static void Main (string[] args) {3 son son = new Son (); 4 son.dosomething ( ); 5 } 6} 7 8//Parent Class 9 class Father {Ten public Father () {One new Other (), }13}14 15//Related class Othe R {n ' other () { new Son () }20}21 22//subclass * Class Son extends Father {public void Dosome Thing () { System.out.println ("Hi, Show Me something!"); }27}
This code is not complicated, just initialize the other classes in the constructor, think about what the result of this code is running? Will print "Hi, show Me something!" It?
The answer is that this code does not run, reporting Statckoverflowerror exception, stack memory overflow, because when declaring the variable son, called the son of the parameterless constructor, the JVM by default called the parent class constructor, Then father initializes the other class, and the other class calls the Son class, and a dead loop is born, knowing that the memory is consumed and stopped.
You may think that this scenario does not appear in development, we think of such a scenario, father is provided by the framework, the son class is our own code of extension, and the other class is the framework requirements of the interception class (Interceptor class or handle class or Hook method) , and then look at the problem, is this scenario impossible to appear?
You may find that such a scenario does not occur, as long as the system runs, it is impossible to affect the project.
That's because the code we're showing here is simple, it's easy to penetrate, there are more than one or two constructors in a project, and the relationships between classes are not so simple, and to glance at one can see if there are flaws that are impossible for all people, The best way to solve this kind of problem is not to declare other classes in the constructor, and to develop good habits.
Reprint-Write high-quality code: 151 Suggestions for improving Java programs (3rd: Classes, objects and methods ___ recommended 31~35)