OOP Rules
Rules ¶
1. A static field or method should be directly referred to by its class name instead of its corresponding object name. [Mandatory] ¶
2. An overridden method from an interface or abstract class must be marked with @Override annotation. [Mandatory] ¶
For
getObject()andget0bject(), the first one has a letterO, and the second one has a number0. To accurately determine whether the overriding is successful, an@Overrideannotation is necessary. Meanwhile, once the method signature in the abstract class is changed, the implementation class will report a compile-time error immediately.
3. varargs is recommended only if all parameters are of the same type and semantics. Parameters with Object type should be avoided. [Mandatory] ¶
Arguments with the
varargsfeature must be at the end of the argument list. (Programming with thevarargsfeature is not recommended.)
![]()
public User getUsers(String type, Integer... ids);
4. 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. [Mandatory] ¶
5. Using a deprecated class or method is prohibited. [Mandatory] ¶
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. 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. [Mandatory] ¶
![]()
"test".equals(object);
![]()
object.equals("test");
7. Use the equals method, rather than reference equality ==, to compare primitive wrapper classes. [Mandatory] ¶
Consider this assignment:
Integer var = ?. When it fits the range from-128to127, we can use == directly for a comparison. Because theIntegerobject will be generated byIntegerCache.cache, which reuses an existing object. Nevertheless, when it fits the complementary set of the former range, theIntegerobject will be allocated in the heap, which does not reuse an existing object. This is a pitfall. Hence theequalsmethod is mandatory.
8. Rules for using primitive data types and wrapper classes: ¶
- Members of a POJO class must be wrapper classes. [Mandatory]
- The return value and arguments of a RPC method must be wrapper classes. [Mandatory]
- Local variables should be primitive data types. [Recommended]
In order to remind the user of explicit assignments, there are no initial values for members in a POJO class. As a user, you should check problems such as
NullPointerExceptionand warehouse entries for yourself.
As the result of a database query may be null, assigning it to a primitive date type will cause a risk of
NullPointerExceptionbecause of autoboxing.
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. While defining POJO classes like DO, DTO, VO, etc., do not assign any default values to the members. [Mandatory] ¶
10. 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. [Mandatory] ¶
The inconsistency of serialVersionUID may cause an `InvalidClassException`` at runtime.
11. Business logic in constructor methods is prohibited. All initializations should be implemented in the init method. [Mandatory] ¶
12. 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. [Mandatory] ¶
We can call the
toStringmethod in a POJO directly to print property values in order to check the problem when a method throws an exception in runtime.
It is not recommended to use Lombok plug-in, which may cause dependency issue.
13. When using attributes of POJO in velocity, use attribute names directly. Velocity engine will invoke getXxx() of POJO automatically. In terms of boolean attributes, velocity engine will invoke isXxx() (Do not use is as prefix when naming boolean attributes). [Mandatory] ¶
For wrapper class
Boolean, velocity engine will invokegetXxx()first.
14. Multiple constructor methods or homonymous methods in a class should be put together for better readability. This rules is prior to rule 15 [Recommended] ¶
15. The order of methods declared within a class is: public or protected methods -> private methods -> getter/setter methods. [Recommended] ¶
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. For a setter method, the argument name should be the same as the field name, this.memberName = paramName. Implementations of business logics in getter/setter methods, which will increase difficulties of the troubleshooting, are not recommended. [Recommended] ¶
![]()
public Integer getData() { if (true) { return data + 100; } else { return data - 100; } }
17. Use the append method in StringBuilder inside a loop body when concatenating multiple strings. [Recommended] ¶
![]()
String str = "start"; for (int i = 0; i < 100; i++) { str = str + "hello"; }
According to the decompiled bytecode file, for each loop, it allocates a
StringBuilderobject, appends a string, and finally returns aStringobject via thetoStringmethod. This is a tremendous waste of memory.
18. Keyword final should be used in the following situations: [Recommended] ¶
- A class which is not allow to be inherited. e.g.
Stringclass. - An argument which is not allow to be modified.
- A method which is not allow to be overridden. e.g.
settermethod in POJO class. - A local variable is not allowed to be reassigned at runtime.
- Avoid reusing a variable in context, use
finalkeyword to force redefinition of a variable, which is more convenient and better for refactoring.
19. Be cautious to copy an object using the clone method in Object. [Recommended] ¶
The default implementation of
cloneinObjectis a shallow (not deep) copy, which copies fields as pointers to the same objects in memory.
20. Define the access level of members in class with severe restrictions: [Recommended] ¶
- Constructor methods must be
privateif an allocation usingnewkeyword outside of the class is forbidden. - Constructor methods are not allowed to be
publicordefaultin a utility class. - Non-static class variables that are accessed from inheritants must be
protected. - Non-static class variables that no one can access except the class that contains them must be
private. - Static variables that no one can access except the class that contains them must be
private. - Static variables should be considered in determining whether they are
final. - Class methods that no one can access except the class that contains them must be
private. - Class methods that are accessed from inheritants must be
protected.
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
privateclass member, we can remove it as soon as we want. However, when it comes to apublicclass member, we have to think twice before any updates happen to it.