Java Development Experience Sharing

This article introduces some experiences and suggestions for Java development from several aspects, including development specifications, selection of third-party libraries, project structure design, and testing-related matters.

About Java

Quoting Wang Yin (http://www.yinwang.org/)'s article:

"A Fair Word for Java"

  • Java has surpassed all the "dynamic languages" that curse it.
  • Java's "successors" have failed to surpass it.
  • Java has no particularly annoying aspects.

"Kotlin and Checked Exception"

"If you analyze rationally, you will find that Java is not that annoying. On the contrary, some of Java's designs seem 'complex and redundant', but they are actually well-considered decisions. The designers of Java know that some things can be omitted, but they deliberately made them redundant. Those who do not understand the 'usability' of a language often blindly think that brevity is good, and that writing a few more words is ugly and inelegant, but that is not the case."

1. Development Specifications

Naming Conventions

  • Camel Case: Type names, method names, variable names

    • Anti-example:
      java
      public static class XmlError {
      }
      private String Key;
      private String Code;
      public static class XmlError {
      }
      private String Key;
      private String Code;
  • Uppercase Letters and Underscores Between Words: Enum values, constants

    • Anti-example:
      java
      public enum BodyType {
          TEXT, BINRAY, NTFS, EncryptNTFS, NTFSFDS
      }
      public enum BodyType {
          TEXT, BINRAY, NTFS, EncryptNTFS, NTFSFDS
      }
  • Variable names should be meaningful and not use ambiguous abbreviations.

    • Anti-example:
      java
      AppException e = new AppException(……);
      private String scName;
      public void creatDomain() {
      }
      List<NameValuePair> nvps = new ArrayList<NameValuePair>();
      Properties p;
      AppException e = new AppException(……);
      private String scName;
      public void creatDomain() {
      }
      List<NameValuePair> nvps = new ArrayList<NameValuePair>();
      Properties p;
  • For Unit Tests:

    • Test classes and classes under test should be in the same package.
      • No need to import the class under test.
      • Can directly test methods modified by protected and default access modifiers.
      • Many testing plugins and tools have default configurations.
    • Test class naming: "Class Under Test Name" + "Test"
    • Test method naming: "test" + "Method Name (with first letter capitalized)"
    • Anti-example:
      java
      public class PostDomainServiceTest extends PostDomainService {
          @Test
          public void TestFuncParseAndCheck() {
              //....
          }
      }
      public class PostDomainServiceTest extends PostDomainService {
          @Test
          public void TestFuncParseAndCheck() {
              //....
          }
      }

Formatting Specifications

  • Common Specifications:
    • Use four spaces for indentation instead of TAB.
    • Left curly brace should not be on a new line.
    • Binary operators should have spaces on both sides.
    • There should be a space after commas that separate parameters.
  • Team Unity

Commenting Specifications

  • Single-line Comments

    java
    // This is a single-line comment
    Record result = doRequest(httpPost, Record.class); // This is an end-of-line comment, avoid using this type of comment
    // This is a single-line comment
    Record result = doRequest(httpPost, Record.class); // This is an end-of-line comment, avoid using this type of comment
  • Multi-line Comments

    java
    /*
    If the comment is relatively long, you can use multi-line comments.
    Note: Do not write it as Javadoc comments.
    */
    /*
    If the comment is relatively long, you can use multi-line comments.
    Note: Do not write it as Javadoc comments.
    */
  • TODO and FIXME

    java
    // TODO This feature is not yet complete
    // FIXME This piece of code needs to be fixed
    // TODO This feature is not yet complete
    // FIXME This piece of code needs to be fixed
  • Javadoc

    • Applicable to: classes, member variables, member methods.
    • At least all non-private methods need to have Javadoc comments.
    • Example:
      java
      /**
       * Create a CNAME record
       * @param srcDomain Source domain name
       * @param dstDomain Target domain name
       * @return Task ID
       * @throws DnsException If the response status code is not 200
       * @throws IOException IO Exception
       */
      public String createCname(String srcDomain, String dstDomain)
          throws DnsException, IOException {
          //...
      }
      /**
       * Create a CNAME record
       * @param srcDomain Source domain name
       * @param dstDomain Target domain name
       * @return Task ID
       * @throws DnsException If the response status code is not 200
       * @throws IOException IO Exception
       */
      public String createCname(String srcDomain, String dstDomain)
          throws DnsException, IOException {
          //...
      }

Java Specifications

  • Each new version of Java releases a corresponding language specification and virtual machine specification.
  • Example: Field modifier order
    • The Java Language Specification (8.3.1. Field Modifiers) recommends, but does not enforce, that field modifiers follow this order: Annotation public protected private static final transient volatile
    • If multiple (different) field modifiers are present, they should be listed in the above order by convention (though not mandatory).

Static Code Analysis Tools

  • Tools: Built-in IDE tools, PMD, CheckStyle, Sonar
  • What it can do:
    • Naming Conventions: Check if naming conforms to naming standards.
    • Javadoc Comments: Check Javadoc comments for classes and methods.
    • Duplicate Code: Check for duplicate code.
    • Resource Closure: Check if resources like Connect, Result, Statement are closed after use.
    • Potential Performance Issues: For example, using string concatenation inside loops.
    • Possible Bugs: Check for potential code errors, such as empty try/catch/finally/switch statements.
  • Rule Sets: Tools like PMD and Sonar provide detailed rule libraries.
  • Handling Rules:
    • Some rules may be "annoying", such as requiring private constructors in utility classes, using try-with-resources for resource management, or requiring reduced access control levels.
    • You can handle this by using the @SuppressWarnings annotation or disabling specific rules in the tools.
      java
      @SuppressWarnings("all") // Not recommended
      
      @SuppressWarnings("unchecked")
      List<String> strings = (List<String>) object;
      @SuppressWarnings("all") // Not recommended
      
      @SuppressWarnings("unchecked")
      List<String> strings = (List<String>) object;

Useless Code

  • Redundant Modifiers: For example, adding final before local variables, adding private to enum constructors, etc.

  • Useless Assignments:

    • For example, initializing a String type member variable to null, initializing an int type member variable to 0.
    • Initializing local variables when all branches reassign them.
      java
      int a = 0; // Initialization of 'a' is redundant
      if (Math.random() > 0.5) {
          a = 1;
      } else {
          a = 2;
      }
      int a = 0; // Initialization of 'a' is redundant
      if (Math.random() > 0.5) {
          a = 1;
      } else {
          a = 2;
      }
  • Commented-out Code: Should be deleted; if you need to retrieve it, please use version control.

  • Main Functions for Testing: Should be removed, as they may affect performance.

  • Unnecessary toString() or Explicit Boxing/Unboxing:

    • Explicit boxing func1(a) and unboxing func2(b) may lead to NullPointerExceptions.
  • About boolean values: Avoid redundant boolean comparisons.

    • Anti-example:
      java
      boolean isDedup = (bucket.getDedup() == AppConstant.DEDUPLICATE_ENABLED) ? true : false;
      if (SIGNED_PARAMETERS.contains(parameterName) == false)
      
      // Can be simplified to return bbs != null && bbs.getBanstatus() == 1;
      private boolean isBan(long bid) {
        BucketBanStatus bbs = AppBucketBanStatusCacheManager.get(bid);
        if (bbs != null && bbs.getBanstatus() == 1) {
          return true;
        }
        return false;
      }
      boolean isDedup = (bucket.getDedup() == AppConstant.DEDUPLICATE_ENABLED) ? true : false;
      if (SIGNED_PARAMETERS.contains(parameterName) == false)
      
      // Can be simplified to return bbs != null && bbs.getBanstatus() == 1;
      private boolean isBan(long bid) {
        BucketBanStatus bbs = AppBucketBanStatusCacheManager.get(bid);
        if (bbs != null && bbs.getBanstatus() == 1) {
          return true;
        }
        return false;
      }
  • Duplicate Code

== vs .equals

  • All non-primitive types should use .equals() to check for equality, unless the intention is to compare memory addresses.
  • Be aware of the effects of auto-unboxing, wrapper type caching (Integer caching range -128 to 127), and string constant pools.
    java
    new Integer(123) == 123; // true, auto-unboxing
    new Integer(123) == new Integer(123); // false, different object addresses
    (Integer) 123 == (Integer) 123; // true, auto-boxing, uses cache
    (Integer) 128 == (Integer) 128; // false, auto-boxing, exceeds cache range
    "Hello world" == ("Hello" + " " + "world"); // true, compile-time optimization, points to the same object in the constant pool
    new String("Hello world") == new String("Hello world"); // false, different object addresses
    new Integer(123) == 123; // true, auto-unboxing
    new Integer(123) == new Integer(123); // false, different object addresses
    (Integer) 123 == (Integer) 123; // true, auto-boxing, uses cache
    (Integer) 128 == (Integer) 128; // false, auto-boxing, exceeds cache range
    "Hello world" == ("Hello" + " " + "world"); // true, compile-time optimization, points to the same object in the constant pool
    new String("Hello world") == new String("Hello world"); // false, different object addresses

Code Readability

  • Magic Numbers:
    • Extract into clearly named constants.
    • Do not use numeric values to represent stages, states, errors, etc.; use enums instead.
    • Use utility classes like TimeUnit to improve readability.
      • Anti-example: Thread.sleep(interval * 60 * 1000);
      • Correct example: TimeUnit.MINUTES.sleep(intervalInMinutes);
  • Constants vs Enums:
    • It is recommended to use enums instead of constant classes because enum types are safer, can carry more information (like descriptions), and can include business logic.
    • Do not rely on ordinal() or name() of enums for logical judgments; add properties like value and name to enums.

Lambda Expressions

  • Advantages: Concise, better readability, parallel advantages.
  • Note:
    • There are initialization overheads and boxing/unboxing overheads.
    • To ensure readability, the call chain should not be too long.

Advanced Java Technologies

  • Includes: Reflection, bytecode modification, dynamic proxies, annotation processing tools, code generation.
  • Avoid using as much as possible:
    • Code Design: Well-designed business code rarely needs to use these technologies.
    • Compatibility: These technologies often act on non-public APIs, violating the developer's design intent, making it difficult to ensure compatibility.
    • Performance: They can impact application startup or runtime performance.

Others

  • Recommended Reading: "Alibaba Java Development Manual".

2. Third-party Libraries

Library Selection

  • Performance: Refer to official and unofficial benchmarks, and test for application scenarios.
  • Compatibility: Consider forward and backward compatibility.
  • Community: Investigate the project's usage volume and users.
  • Ecosystem: Consider functional extensions and integration with other libraries.
  • Server-side Applications: Pay attention to security.
  • SDK: Should aim for minimal dependencies.

Lombok

  • Advantages: Concise, easy to modify.
  • Common Annotations:
    • For data classes: @Data, @NoArgsConstructor, @AllArgsConstructor
    • For non-data classes: @Getter, @Setter, @Slf4j
  • Not Recommended:
    • @Builder (for data classes): Because it does not comply with JavaBean specifications and has additional memory overhead. The intent of the builder pattern is to separate the construction of complex objects from their representation, such as HttpClientBuilder.
    • @SneakyThrows

Slf4j

  • It is a logging facade that can bridge specific logging implementations like log4j, log4j2, Logback.
  • Lombok's @Slf4j annotation can automatically generate private static final Logger log = ....
  • Logging Method:
    • It is recommended to use placeholders: log.debug("info: {}", info);, as this approach is more readable and incurs no performance overhead from string concatenation when the log level is off.
    • Not Recommended: log.debug("info: " + info); or log.debug(String.format(...));, because even if the log is not printed, there will be unnecessary string concatenation and method calls, affecting performance.
  • Printing Exception Stacks: Do not catch Throwable; do not use e.printStackTrace().
    • Recommended way: log.error("Fail to create domain, cause: ", e); or use Guava's Throwables.getStackTraceAsString(e).

Guava

  • Provides a wealth of utility tools: exception handling, preconditions, string processing, collection extensions, caching, concurrency libraries, I/O, etc.

Other Libraries

  • Caffeine Cache: A high-performance Java caching library that replaces Guava Cache, offering better performance, lower memory usage, and higher hit rates.
  • HttpClient: Be sure to use connection pools and understand its default retry mechanism of 3 times. After using the response, call EntityUtils.toString() or EntityUtils.consume().
  • Joda Time: Has entered maintenance mode; it is recommended to use the Date API provided by Java 8.
  • JSON Libraries: Recommend Jackson, Gson; do not recommend fastjson, Json-lib. For Spring Boot projects, it is recommended to use only Jackson.
  • XStream: Is thread-safe when not using the automatic detection of annotations feature; avoid creating XStream objects repeatedly.

3. Project Structure

Document and Database Management

  • Document Management: Create a doc directory in the project root to store requirements, designs, and interface documents.
  • Database Change Control: Create a db directory in the project root to store SQL scripts and perform manual version control. Create a new SQL file with a sequence number for each change.

.gitignore

  • The .gitignore file should be properly configured to ignore IDE-generated files (such as .idea, .iml) and compiled products (such as target/).

Maven

  • Plugins: Configure maven-compiler-plugin, maven-resources-plugin, maven-javadoc-plugin, maven-source-plugin, etc.
  • Properties: Use <properties> to manage dependency versions uniformly, eliminate duplication, and facilitate management.
  • Modules: Common code such as constants, enums, RPC interfaces, utility classes, and DAOs can be extracted into independent Maven modules.
  • Parent: It is not recommended to use spring-boot-starter-parent.

Application Layer Structure

  • Layering: It is recommended to adopt the layered structure in the "Alibaba Java Development Manual".
    • Open Interface Layer: Encapsulate Service methods as RPC interfaces or Web interfaces.
    • Web Layer: Access control, parameter validation, simple processing.
    • Service Layer: Relatively specific business logic service layer.
    • Manager Layer: General business processing layer, used to encapsulate third-party platforms, sink common capabilities (like caching), and combine multiple DAOs.
    • DAO Layer: Data access layer, interacting with the underlying database.
  • Dependency Handling: It is recommended to use constructor injection for dependencies, avoiding direct new of dependent classes in the class.
  • DTO and Model:
    • DTO (Data Transfer Object) is oriented towards interaction, while Model is oriented towards business.
    • It is not recommended to use BeanUtils for copying between the two due to performance and modifiability issues. The recommended approach is to provide conversion methods in their respective classes or define a dedicated Converter.
  • Controller:
    • Parameter Validation: It is not recommended to use Bean Validation or Spring Validation.
    • Serialization: Do not rely on DTO field naming; use annotations (like @SerializedName) to specify. For complex/nested responses, use a custom JsonSerializer.
  • Service:
    • Threads: Must be provided through a thread pool, and using Executors to create them is not allowed; use ThreadPoolExecutor to avoid resource exhaustion risks.
  • Dao:
    • Implementation: Recommended Mybatis, JDBC.
    • SQL: Avoid concatenating SQL strings in Java code to prevent SQL injection.

Basic Principles

  • Test Code Invasion: Test code should not invade business code.
  • Mock Tools: Avoid using PowerMock, JMock.

Unit Testing

  • Definition: Test the smallest functional unit, such as a single method. The difficulty of unit testing is closely related to the quality of code design.
  • DAO Layer Testing: Can switch data sources, using a test database or an in-memory database (like H2).
  • Manager Layer Testing: Mock the DAO layer and third-party dependencies.
  • Service Layer Testing: Mock the Manager layer and other Services.
  • Controller Layer Testing: Mock the Service layer.
  • Mock Third-party Rest API: Can start an embedded container, using a mock platform or Spring Boot's MockRestServiceServer.

Integration Testing and Others

  • Integration Testing: Combine code units to test the combined results.
  • Tools:
    • Interface Testing: Postman
    • Load Testing: Jmeter
    • Functional Testing: Fiddler
  • Test Coverage: Measure the completeness of tests.

Comments

Pleaseto continueComments require admin approval before being visible

No comments yet. Be the first to comment!