Do you know? 10 exquisite Java coding Best Practices

Source: Internet
Author: User
Tags representational state transfer

This is a list of 10 more refined Java coding practices than Josh Bloch's valid Java rules. Compared to the list of Josh Bloch, which is easier to learn and focus on daily situations, this list will include uncommon situations involving API/SPI design and may have a great impact.

I have encountered this in writing and maintaining jOOQ (SQL for internal DSL Modeling in Java. As an internal DSL, jOOQ has challenged Java compilers and generics to the maximum extent, combining generics, variable parameters, and overloading, josh Bloch may not recommend this too broad API.

Let me share with you 10 subtle Java coding best practices:

  1. Keep in mind the C ++ destructor

Do you remember the C ++ destructor? Don't remember? So you are really lucky because you don't have to debug the code that causes memory leakage because the memory allocated after the object is deleted is not released. Thanks to the garbage collection mechanism implemented by Sun/Oracle!

Despite this, destructor still provide an interesting feature. It understands that memory is released in reverse allocation order. Remember that this is also true in Java. When you operate the class destructor Syntax:

  • Use @ Before and @ After annotations of JUnit
  • Allocate and release JDBC Resources
  • Call the super Method

There are other use cases. Here is a specific example to illustrate how to implement the SPI of some event listeners:

1234567891011 @Overridepublic void beforeEvent(EventContext e) {    super.beforeEvent(e);    // Super code before my code} @Overridepublic void afterEvent(EventContext e) {    // Super code after my code    super.afterEvent(e);}

The notorious philosopher's dining question is another good example of why it is important. For more information about dining with philosophers, see the link:

Http://adit.io/posts/2013-05-11-The-Dining-Philosophers-Problem-With-Ron-Swanson.html

Rule: whether or not to execute the after/free/return operation in reverse order when before/after, allocate/free, and take/return semantics are used to implement logic.

2. Do not trust your early SPI evolution judgment

Providing the customer with SPI allows them to easily inject custom behavior methods into your library/code. Beware that your SPI evolution judgment may confuse you and make you think that you (not) intend to need additional parameters. Of course, you should not add features too early. But once you release your SPI, once you decide to follow the semantic version control, when you realize that in some circumstances you may need another parameter, you will really regret adding a stupid single-Parameter Method In SPI:

1234 interface EventListener {    // Bad    void message(String message);}

What if you also need message IDs and sources? API evolution will prevent you from adding parameters to the above type. Of course, with Java 8, you can add a defender method to "Defend" your earlier bad design decisions:

123456789101112 interface EventListener {    // Bad    default void message(String message) {        message(message, null, null);    }    // Better?    void message(        String message,        Integer id,        MessageSource source    );}

Note: Unfortunately, the defender method cannot use the final modifier.

However, compared to using many methods to pollute your SPI, using context objects (or parameter objects) is much better.

12345678910 interface MessageContext {    String message();    Integer id();    MessageSource source();} interface EventListener {    // Awesome!    void message(MessageContext context);}

Compared with EventListner SPI, you can easily evolve the MessageContext API, because few users will implement it.

Rule: When you specify SPI, consider using context/parameter objects instead of writing methods with fixed parameters.

Note: It is also a good idea to use a dedicated MessageResult type exchange result. This type can be constructed using the builder API. This will greatly increase the flexibility of SPI evolution.

3. Avoid returning anonymous, local, or internal classes

Swing programmers usually only need to press a few shortcuts to generate hundreds of anonymous classes. In most cases, as long as you follow the interface and do not violate the SPI subtype lifecycle, this is fine. But do not use anonymous, local, or internal classes frequently because they save external class references. Because no matter where they go, external classes have to follow. For example, in case of improper operations outside the local class, the entire object graph may be slightly changed, which may cause memory leakage.

Rule: before writing an anonymous, local, or internal class, think twice whether it can be converted to a static or common top-level class, so as to avoid returning their objects to the outer domain by using methods.

Note: Use double curly braces to initialize simple objects:

1234 new HashMap<String, String>() {{  put("1", "a");  put("2", "b");}}

This method utilizes the instance initialization method (initializer) described in the JLS § 8. 6 specification ). It looks good on the surface, but in fact it is not recommended. Because if a completely independent HashMap object is used, the instance will not keep the reference of the external object. In addition, this allows the class loader to manage more classes.

4. Write SAM now!

Java 8 is approaching. Java 8 brings lambda expressions, whether you like them or not. Although your API users may like it, you 'd better ensure that they can be used as often as possible. Therefore, unless your API receives simple "scalar" types, such as int, long, String, and Date, your API will receive SAM as often as possible.

What is SAM? SAM is a single abstract method [type]. It is also called a function interface and will soon be annotated as @ FunctionalInterface. This works well with rule 2. EventListener is actually a SAM. The best SAM has only one parameter, because this will further simplify the compilation of lambda expressions. Imagine writing

1 listeners.add(c -> System.out.println(c.message()));

To replace

123456 listeners.add(new EventListener() {  @Override  public void message(MessageContext c) {    System.out.println(c.message()));  }});

Imagine Processing XML in JOOX format. JOOX contains a lot of SAM:

1234567 $(document)  // Find elements with an ID  .find(c -> $(c).id() != null)  // Find their child elements  .children(c -> $(c).tag().equals("order"))  // Print all matches  .each(c -> System.out.println($(c)))

Rule: it is better for your API users to write SAM/function interfaces from now on.

Note: There are many interesting blogs about Java 8 lambda expressions and improved Collections API:

  • Http://blog.informatech.cr/2013/04/10/java-optional-objects/
  • Http://blog.informatech.cr/2013/03/25/java-streams-api-preview/
  • Http://blog.informatech.cr/2013/03/24/java-streams-preview-vs-net-linq/
  • Http://blog.informatech.cr/2013/03/11/java-infinite-streams/
5. Avoid returning null to the method.

I have written 1 and 2 articles about java NULLs and introduced the new Optional class in java 8. From an academic or practical perspective, these topics are quite interesting.

Although Null and NullPointerException are still Java attacks, you can still design APIs without any problems. When designing an API, try to avoid returning null to the method, because your user may chain the call method:

1 initialise(someArgument).calculate(data).dispatch();

From the code above, we can see that no method should return null. In fact, using null is generally considered to be quite heterogeneous. Libraries such as jQuery and jOOX have completely abandoned null on iteratable objects.

Null is usually used in delayed initialization. In many cases, delayed initialization should also be avoided without seriously affecting the performance. In fact, if the involved data structure is too large, you need to use the delay initialization with caution.

Rule: avoid returning null whenever the method is used. Null is only used to indicate the meaning of "not initialized" or "not exist.

6. Never return an empty (null) array or List when designing an API

Although the return value of a method is null in some cases, do not return an empty array or an empty set! See the java. io. File. list () method. It is designed as follows:

This method returns a string array of all files or directories in the specified directory. If the directory is empty (empty), the returned array is also empty (empty ). If the specified path does not exist or an I/O error occurs, null is returned.

Therefore, this method is usually used as follows:

1234567891011 File directory = // ... if (directory.isDirectory()) {  String[] list = directory.list();   if (list != null) {    for (String file : list) {      // ...    }  }}

Do you think the null check is necessary? Most I/O operations produce IOExceptions, but this method only returns null. Null cannot store I/O error messages. Therefore, such a design has the following three shortcomings:

  • Null does not help to detect errors
  • Null cannot indicate that the I/O error is caused by incorrect path of the File instance.
  • Everyone may forget to judge null.

If you look at the problem with a set of thinking, then the empty (empty) array or set is the best implementation of "nonexistent. Returning an empty (null) array or set has almost no practical significance unless it is used for delayed initialization.

Rule: The returned array or set should not be null.

7. Avoid status. Use the Function

The advantage of HTTP is stateless. All relevant statuses are transferred in each request and response. This is the essence of REST naming: Representational state transfer ). This is also awesome in Java. When the method receives status parameter objects, consider this from the perspective of rule 2. If the status is transmitted through this object, rather than operating from outside, the process will be simpler. Take JDBC as an example. The following example reads a cursor from a stored program.

123456789101112 CallableStatement s =  connection.prepareCall("{ ? = ... }"); // Verbose manipulation of statement state:s.registerOutParameter(1, cursor);s.setString(2, "abc");s.execute();ResultSet rs = s.getObject(1); // Verbose manipulation of result set state:rs.next();rs.next();

This makes the jdbc api so strange. Each object is stateful and difficult to operate. Specifically, there are two main problems:

  • In a multi-threaded environment, it is difficult to correctly process stateful APIs.
  • It is difficult to make stateful resources available globally because the status is not described.

Rule: more functions are implemented. Transfer status using method parameters. Very few operation object statuses.

8. Short-circuit equals ()

This is a relatively easy way to operate. In a complex object system, you can achieve significant performance improvement, as long as you first make equal judgments in the equals () method of all objects:

12345 @Overridepublic boolean equals(Object other) {  if (this == other) return true;  // Other equal judgment logic...}

Note that other short-circuit checks may involve null values, so you should also add the following:

123456 @Overridepublic boolean equals(Object other) {  if (this == other) return true;  if (other == null) return false;  // Rest of equality logic...}

Rule: use short circuits in all your equals () methods to improve performance.

9. Use final as the default method.

Some may disagree with this article, because the default method is final, which is contrary to the Java developer's habits. However, if you have full control over the code, it is certainly true to set the method to final by default:

  • If you really need to overwrite a method (do you really need it ?), You can still remove the final keyword.
  • You will never overwrite any method.

This is especially suitable for static methods. In this case, "overwriting" (actually masking) is hardly effective. I recently encountered a very bad example of masking static methods in Apache Tika. Take a look:

  • TaggedInputStream. get (InputStream)
  • TikaInputStream. get (InputStream)

TikaInputStream extends TaggedInputStream and masks its static get () method with a different implementation.

Unlike conventional methods, static methods cannot overwrite each other, because static method calls are bound at compilation. If you are unlucky, you may accidentally get the wrong method.

Rule: If you have full control over your API, use final as the default method.

10. Avoidance Method (T ...) Signature

In special cases, the "accept-all" variable parameter method is used to receive an Object... The parameter is correct:

1 void acceptAll(Object... all);

Writing this method brings a little JavaScript feeling to the Java ecosystem. Of course, you may want to limit the actual type according to the actual situation, such as String .... Because you don't want to limit too much, you may think that using generic T to replace objects is a good idea:

1 void acceptAll(T... all);

But not. T is always inferred as an Object. In fact, you may just think that the above method cannot use generics. More importantly, you may think that you can reload the above method, but you cannot:

12 void acceptAll(T... all);void acceptAll(String message, T... all);

It seems that you can choose to pass a String message to the method. But what will happen to this call?

1 acceptAll("Message", 123, "abc");

The compiler deduced T as <? Extends Serializable & Comparable <? >>, This will make the call ambiguous!

So whenever you have an "accept-all" signature (even if it is generic), you will never be able to reload it type securely. API users may only make the compiler "accidentally" Select the "correct" method when they are lucky. However, you may also use the accept-all method or cannot call any method.

Rule: if possible, avoid "accept-all" signature. If not, do not reload this method.

Conclusion

Java is a beast. Unlike other more idealistic languages, it gradually evolves into today's form. This may be a good thing, because there are already hundreds of warnings at the speed of Java development, and these warnings can only be grasped through years of experience.

Original article: jooq Translation: ImportNew-liken

Link: http://www.cnblogs.com/oooweb/p/10-subtle-best-practices-when-coding-java.html

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.