1. [Mandatory] A static field or method should be directly referred to by its class name instead of its corresponding object name.
2. [Mandatory] An overridden method from an interface or abstract class must be marked with @Override
annotation.
Counter example: For
getObject()
andget0bject()
, the first one has a letter ‘O’, and the second one has a number ‘0’. To accurately determine whether the overriding is successful, an@Override
annotation is necessary. Meanwhile, once the method signature in the abstract class is changed, the implementation class will report a compile-time error immediately.
3. [Mandatory] varargs is recommended only if all parameters are of the same type and semantics. Parameters with Object
type should be avoided.
Note: Arguments with the varargs feature must be at the end of the argument list. (Programming with the varargs feature is not recommended.)
Positive example:
public User getUsers(String type, Integer... ids);
4. [Mandatory] Modifying the method signature is forbidden to avoid affecting the caller. A @Deprecated
annotation with an explicit description of the new service is necessary when an interface is deprecated.
5. [Mandatory] Using a deprecated class or method is prohibited.
Note: For example,
decode(String source, String encode)
should be used instead of the deprecated methoddecode(String encodeStr)
. Once an interface has been deprecated, the interface provider has the obligation to provide a new one. At the same time, client programmers have the obligation to use the new interface.
6. [Mandatory] Since NullPointerException
can possibly be thrown while calling the equals method of Object
, equals should be invoked by a constant or an object that is definitely not null.
Positive example:
"test".equals(object);
Counter example:
object.equals("test");
Note:
java.util.Objects#equals
(a utility class in JDK7) is recommended.
7. [Mandatory] Use the equals
method, rather than reference equality ‘==’, to compare primitive wrapper classes.
Note: Consider this assignment:
Integer var = ?
. When it fits the range from -128 to 127, we can use==
directly for a comparison. Because theInteger
object will be generated byIntegerCache.cache
, which reuses an existing object. Nevertheless, when it fits the complementary set of the former range, theInteger
object will be allocated in the heap, which does not reuse an existing object. This is a pitfall. Hence theequals
method is recommended.
8. [Mandatory] Rules for using primitive data types and wrapper classes:
1) Members of a POJO class must be wrapper classes.
2) The return value and arguments of a RPC method must be wrapper classes.
3) [Recommended] Local variables should be primitive data types.
Note: In order to remind the consumer of explicit assignments, there are no initial values for members in a POJO class. As a consumer, you should check problems such as NullPointerException
and warehouse entries for yourself.
Positive example: As the result of a database query may be null, assigning it to a primitive date type will cause a risk of NullPointerException
because of autoboxing.
Counter example: Consider the output of a transaction volume’s amplitude, like ±x%. As a primitive data, when it comes to a failure of calling a RPC service, the default return value: 0% will be assigned, which is not correct. A hyphen like - should be assigned instead. Therefore, the null value of a wrapper class can represent additional information, such as a failure of calling a RPC service, an abnormal exit, etc.
9. [Mandatory] While defining POJO classes like DO, DTO, VO, etc., do not assign any default values to the members.
10. [Mandatory] To avoid a deserialization failure, do not change the serialVersionUID when a serialized class needs to be updated, such as adding some new members. If a completely incompatible update is needed, change the value of serialVersionUID in case of a confusion when deserialized.
Note: The inconsistency of serialVersionUID may cause an
InvalidClassException
at runtime.
11. [Mandatory] Business logic in constructor methods is prohibited. All initializations should be implemented in the init
method.
12. [Mandatory] The toString
method must be implemented in a POJO class. The super.toString
method should be called in in the beginning of the implementation if the current class extends another POJO class.
Note: We can call the
toString
method in a POJO directly to print property values in order to check the problem when a method throws an exception in runtime.
13. [Recommended] When accessing an array generated by the split method in String using an index, make sure to check the last separator whether it is null to avoid IndexOutOfBoundException
.
Note:
String str = "a,b,c,,";
String[] ary = str.split(",");
// The expected result exceeds 3. However it turns out to be 3.
System.out.println(ary.length);
14. [Recommended] Multiple constructor methods or homonymous methods in a class should be put together for better readability.
15. [Recommended] The order of methods declared within a class is:
public or protected methods -> private methods -> getter/setter methods.
Note: As the most concerned ones for consumers and providers, public methods should be put on the first screen. Protected methods are only cared for by the subclasses, but they have chances to be vital when it comes to Template Design Pattern. Private methods, the black-box approaches, basically are not significant to clients. Getter/setter methods of a Service or a DAO should be put at the end of the class implementation because of the low significance.
16. [Recommended] For a setter method, the argument name should be the same as the field name. Implementations of business logics in getter/setter methods, which will increase difficulties of the troubleshooting, are not recommended.
Counter example:
public Integer getData() {
if (true) {
return data + 100;
} else {
return data - 100;
}
}
17. [Recommended] Use the append
method in StringBuilder
inside a loop body when concatenating multiple strings.
Counter example:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
Note: According to the decompiled bytecode file, for each loop, it allocates a
StringBuilder
object, appends a string, and finally returns aString
object via thetoString
method. This is a tremendous waste of memory.
18. [Recommended] Keyword final should be used in the following situations:
1) A class which is not allow to be inherited, or a local variable not to be reassigned.
2) An argument which is not allow to be modified.
3) A method which is not allow to be overridden.
19. [Recommended] Be cautious to copy an object using the clone
method in Object
.
Note: The default implementation of
clone
inObject
is a shallow (not deep) copy, which copies fields as pointers to the same objects in memory.
20. [Recommended] Define the access level of members in class with severe restrictions:
1) Constructor methods must be private
if an allocation using new
keyword outside of the class is forbidden.
2) Constructor methods are not allowed to be public
or default
in a utility class.
3) Nonstatic class variables that are accessed from inheritants must be protected
.
4) Nonstatic class variables that no one can access except the class that contains them must be private
.
5) Static variables that no one can access except the class that contains them must be private
.
6) Static variables should be considered in determining whether they are final
.
7) Class methods that no one can access except the class that contains them must be private
.
8) Class methods that are accessed from inheritants must be protected
.
Note: We should strictly control the access for any classes, methods, arguments and variables. Loose access control causes harmful coupling of modules. Imagine the following situations. For a
private
class member, we can remove it as soon as we want. However, when it comes to apublic
class member, we have to think twice before any updates happen to it.