A small case tells you why it is necessary to enforce that basic data types cannot be used in POJO?

Alibaba JAVA Development Manual wrote:

The usage standards for basic data types and wrapper data types are as follows:

  • [Mandatory] All POJO class attributes must use wrapper data types.
  • [Mandatory] The return value and parameters of the RPC method must use the wrapper data type.
  • [Recommendation] All local variables use basic data types.

Note: The POJO class attribute has no initial value to remind users that they must explicitly assign values ​​when they need to use them. Any NPE problems or storage inspections are guaranteed by users.

  • Positive example: The query result of the database may be null, because of automatic unboxing, there is a risk of NPE when receiving basic data types.
  • Counter-example: For example, display the rise and fall of the total transaction amount, that is, plus or minus x%, where x is the basic data type. When the RPC service called is unsuccessful, the default value is returned, and the page displays 0%, which is unreasonable. Should be displayed as a dash. Therefore, the null value of the packaging data type can represent additional information, such as: remote call failure, abnormal exit.

test case

poo:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Test {
    
    
    private Integer id;
    private String name;
    private Integer age;
    private int num;
}

service:

public void selTest(){
    
    
   Test t = (Test) testMapper.selectById(1);
    System.out.println(t.getAge());//Integer
    System.out.println(t.getNum());//int

}

database:

picture

The rest of the simple code is not shown.

request service

Test Results:

picture

Here, since the num field is of type int, even if the data retrieved from the database is Null, due to automatic unboxing, the data of type int has a default value, so the null in the database is mapped to the variable num of type int, which becomes 0, which is incorrect.

So the fields in the pojo should all be wrapper types.




Ali recommends that all POJO class attributes use wrapper classes, but have you noticed these pitfalls?

Wrapper classes Java 5are introduced together with generics in , and there are two reasons for introducing wrapper classes:

  1. Solve the problem of not being able to create generic collections of basic types
  2. Added support for primitive types with nullthis semantic

It also provides boxingand unboxingsyntactic sugar, allowing the compiler to support automatic conversion of basic types and wrapper classes, reducing the workload of developers. However, students often have tragic online problems due to misuse of packaging classes. When using packaging classes, you must pay attention to the following four points:

  1. distinct ==and equalssemantics from the base class
  2. poor performance
  3. Subtle NPEproblems
  4. confusing APIdesign

1. Equal or not equal? This is a problem

For example the following code snippet

class Biziclop {
    
      
    public static void main(String\[\] args) {
    
      
        System.out.println(new Integer(5) == new Integer(5)); // false  
        System.out.println(new Integer(500) == new Integer(500)); // false  
  
        System.out.println(Integer.valueOf(5) == Integer.valueOf(5)); // true  
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500)); // false  
    }  
}  

The return of the first and second statements falseis relatively easy to understand, because Javathe call to the object in =is actually comparing the address of the object on the heap. Since the two objects are newly created, the addresses must be different, so return false.

What is more puzzling is the third statement. According to our previous analysis, it should also return false, but Integer.valueOf(5) == Integer.valueOf(5)the result of the comparison is that truethis is because JVMthe integer of -128-127 is cached, so when the value is in this range , the returned objects are all the same. The fourth statement is returned because the value is no longer in the range of -128-127 false.

The above examples are relatively classic examples, which are familiar to everyone and generally difficult to fall into the pit, but the following examples are more confusing

class Biziclop {
    
      
     public static void main(String\[\] args) {
    
      
        List<Long> list = new ArrayList<>();  
        list.add(Long.valueOf(200));  
        System.out.println(list.contains(200)); // false  
          
        Long temp = 0L;  
    System.out.println(temp.equals(0)); // false  
       System.out.orintln(0==0L); // true  
 }  
}   

the reason is

public boolean equals(Object obj) {
    
      
    if (obj instanceof Long) {
    
      
      return value == ((Long)obj).longValue();  
    }  
    return false;  
}  

The wrapper class rewrites the method, resulting in inconsistent results with the basic type even if the equalscall method of the wrapper class is relatively small. equalsThe completely different ==and equalssemantics from the basic class often lead the code to an unexpected branch. Coupled with the JVMunique caching strategy for numbers, it is very easy to have different running results between the test environment and the official environment.

2. Poor performance

" Effective Java" has the following example:

public static void main(String\[\] args) {
    
      
    Long sum = 0L; // uses Long, not long  
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
    
      
        sum += i;  
    }  
    System.out.println(sum);  
}  

This code takes long6 times slower than the version using primitive types ( 43 seconds sum for declaring a variable Longof and 6.8 seconds for declaring a variable sumof primitive type ). longThe reason for this is that the wrapper class has to go through the process of opening up memory space on the heap, initialization, memory addressing, and data loading into registers. It is not surprising that the performance is poor. So Joshua Blochthe advice to developers is: Avoid creating unnecessary objects.

JMH workbenchThe performance comparison between the wrapper class and the basic class running on the classic platform is shown in the following figure:

picturepicture

It can be seen that compared to the base class, the wrapper class is generally much slower.

Image address: https://www.baeldung.com/java-primitives-vs-objects

3. SubtleNPE

Unlike basic types, it is possible to be a wrapper class as an object null, which means that an exception will be thrown when a pointer nullto a wrapper class , such as the following code:unboxingNPE

Integer in = null;  
...  
...  
int i = in; // NPE at runtime  

This code is also relatively obvious, but if the wrapper class encounters a ternary operator, more complicatedNPE

class Biziclop {
    
      
    public static void main(String\[\] args) {
    
      
      Boolean b = true ? returnsNull() : false; // NPE on this line.  
      System.out.println(b);  
    }  
   public static Boolean returnsNull() {
    
      
     return null;  
  }  
}  

This has something to do with the judgment of the ternary operator type in Java. There is a judgment rule,

If the second or third parameter of the ternary operator is a primitive type T, and the other is the corresponding wrapper type, then the return type of the ternary operator is the primitive type T

So in the above code, the return value of returnsNull has to be performed again unboxing, so the NPE.

4. Confusing API

In Longthis class, there is one apithat getLongis declared as follows:

/**  
 * Determines the {@code long} value of the system property  
 * with the specified name.  
 */  
public static Long getLong(String nm) {
    
      
    return getLong(nm, null);  
}  

The function of this apiis to get JVMthe attribute value in and convert it to Longtype, for example:

class Biziclop {
    
      
     public static void main(String\[\] args) {
    
      
        System.setProperty("22", "22");  
        System.setProperty("23", "hello world!");  
        System.out.println(Long.getLong("22")); // 22  
        System.out.println(Long.getLong("23")); // null  
        System.out.println(Long.getLong("24")); // null  
  }  
}  

The proper design of this apiis a counterexample, and students often misuse it as Long.valueOfor Long.parseLong, and the result returns a value that does not meet expectations.

5. Best Practices

The "Alibaba JavaProgramming Manual" has the following three suggestions for the use of packaging classes:

  1. All POJOclass attributes use the wrapper class
  2. The return value and parameters of the RPC method use the wrapper class
  3. All local variables use primitive data types

Explanation: POJOThere is no initial value for class attributes to remind users that they must explicitly assign values ​​when they need to be used. Any NPEproblems, or storage inspections, are guaranteed by the user.

Positive example: the query result of the database may be null, because of the automatic unboxing, the risk NPEof

Counter-example: The transaction report of a certain business shows the rise and fall of the transaction volume, that is, x%, where x is the basic data type. When the called HSFservice fails to call, the default value is returned, and the page displays 0%, which is unreasonable , should be displayed as a dash-, so nullthe value of the wrapper class can represent additional information, such as: remote call failure, abnormal exit.

Guess you like

Origin blog.csdn.net/qq_43842093/article/details/131144501