Writing high-quality code: 151 suggestions for improving Java programs (Chapter 1: Basic Types ___ suggestions 26 ~ 30), java151

Source: Internet
Author: User
Tags random seed

Writing high-quality code: 151 suggestions for improving Java programs (Chapter 1: Basic Types ___ suggestions 26 ~ 30), java151
Recommendation 26: Beware of null values of the packaging type

We know that Wrapper Types is introduced to Java to solve the instantiation problem of basic Types, so that a basic type can also be involved in the object-oriented programming world. In Java 5, the generic type says "no" to the basic type. If an Integer is put into the List, the Integer packaging type must be used. Let's look at a piece of code:

 1 import java.util.ArrayList; 2 import java.util.List; 3  4 public class Client26 { 5  6     public static int testMethod(List<Integer> list) { 7         int count = 0; 8         for (int i : list) { 9             count += i;10         }11         return count;12     }13 14     public static void main(String[] args) {15         List<Integer> list = new ArrayList<Integer>();16         list.add(1);17         list.add(2);18         list.add(null);19         System.out.println(testMethod(list));20     }21 }
TestMethod receives a List parameter whose element is an integer and calculates the sum of all elements. This is common in statistics and projects. Then, compile a test testMethod, in the main method, place values 1, 2, and 2 in the List and call the method calculation. Now, let's think about whether an error will be returned. I guess not. Both the basic and packaging types can be converted freely through Autoboxing and AutoUnboxing. Can null be converted to 0? The result after running is: Exception in thread "main" java. lang. nullPointerException failed to run, and a null pointer exception was reported. We thought for a moment and soon we knew the cause: in the program for loop, A unpacking process was hidden, in this process, the packaging type is converted to the basic type. We know that the unpacking process is implemented by calling the intValue method of the packaging object. Because the packaging type is null, it is inevitable to report a null pointer exception when accessing its intValue method. The problem is clear, and the modification is very simple. Just add the null value check. The Code is as follows:
public static int testMethod(List<Integer> list) {        int count = 0;        for (Integer i : list) {            count += (i != null) ? i : 0;        }        return count;    }

The preceding example uses Integer and int to illustrate the problem of unpacking. The same problem exists in the unpacking process of the other seven packaging objects. The wrapper and binning objects can be converted freely. This is not false, but the null value must be excluded. The null value cannot be converted to the basic type. To solve this problem, we should keep in mind that the null value verification is required when the packaging type is involved in the calculation.

Recommendation 27: Careful packaging type size comparison

The basic type can be compared with the size. The corresponding packaging type implements the Comparable interface, which also illustrates this problem. Let's compare the size of the two packaging types. The Code is as follows:

 1 public class Client27 { 2     public static void main(String[] args) { 3         Integer i = new Integer(100); 4         Integer j = new Integer(100); 5         compare(i, j); 6     } 7  8     public static void compare(Integer i, Integer j) { 9         System.out.println(i == j);10         System.out.println(i > j);11         System.out.println(i < j);12 13     }14 }

The code is very simple. Two Integer objects are generated, and then the two sizes are compared. Since the packaging type and basic type can be freely converted, can the above Code print two equal values? The running result is as follows:


It turns out to be three false values. That is to say, the two values are not equal and there is no size Relationship between them. This is too strange. Not surprisingly, Let's explain:

The problem is clear and it is always easy to modify. You can directly use the Integer instance compareTo method. However, this type of problem is more common, as long as the comparison between two objects should adopt the corresponding method, instead of using the default mechanism of Java, unless you are sure you understand this very well.

Suggestion 28: Use the integer pool preferentially

The previous suggestion explains the comparison of packaging objects. This suggestion will continue to discuss related issues in depth. First, let's take a look at the following code:

1 import java. util. required; 2 3 public class Client28 {4 public static void main (String [] args) {5 bytes input = new bytes (System. in); 6 while (input. hasNextInt () {7 int tempInt = input. nextInt (); 8 System. out. println ("\ n ====" + tempInt + "equal judgment ==== "); 9 // two objects generated through new 10 Integer I = new Integer (tempInt); 11 Integer j = new Integer (tempInt); 12 System. out. println ("new generated object:" + (I = j); 13 // after the basic type is converted to the packaging type, compare 14 I = tempInt; 15 j = tempInt; 16 System. out. println ("basic type conversion object:" + (I = j); 17 // generate an instance 18 I = Integer using a static method. valueOf (tempInt); 19 j = Integer. valueOf (tempInt); 20 System. out. println ("valueOf generated object:" + (I = j); 21} 22} 23}

Enter multiple numbers and then generate Integer objects in different ways in 3 to determine whether they are equal. Note that "=" is used here, which indicates that the judgment is not the same object. We enter three numbers 127, 128, and 555. The result is as follows:

===== 127 equal judgment ====
New generation object: false
Object of basic type conversion: true
ValueOf: true
===== 128 equal judgment ====
New generation object: false
Object of basic type conversion: false
ValueOf: false
===== 555 equal judgment ====
New generation object: false
Object of basic type conversion: false
ValueOf: false

It is incredible that the comparison result of number 127 is different from the other two numbers. Its packing action produces the same object, and valueOf produces the same object, however, the comparison between numbers greater than 127 and numbers greater than 128 does not produce the same object. Why? Let's explain it one by one.

(1) Integer object generated by new

The new statement is to generate a new object. If there are only two objects, the address must be different. The comparison result is false.

(2) bind the generated object

To solve this problem, we must first note that the packing action is implemented through the valueOf method. That is to say, if the last two algorithms are the same, the result is certainly the same. Now the problem is: how does valueOf generate objects? Let's read the source code of Integer. valueOf below:

 1  /** 2      * Returns an {@code Integer} instance representing the specified 3      * {@code int} value.  If a new {@code Integer} instance is not 4      * required, this method should generally be used in preference to 5      * the constructor {@link #Integer(int)}, as this method is likely 6      * to yield significantly better space and time performance by 7      * caching frequently requested values. 8      * 9      * This method will always cache values in the range -128 to 127,10      * inclusive, and may cache other values outside of this range.11      *12      * @param  i an {@code int} value.13      * @return an {@code Integer} instance representing {@code i}.14      * @since  1.515      */16     public static Integer valueOf(int i) {17         assert IntegerCache.high >= 127;18         if (i >= IntegerCache.low && i <= IntegerCache.high)19             return IntegerCache.cache[i + (-IntegerCache.low)];20         return new Integer(i);21     }

The meaning of this Code is clear. If the int type between-128 and 127 is converted to an Integer object, it is obtained directly from the cache array. What is in the cache array, the source code of JDK 7 is as follows:

 1  /** 2      * Cache to support the object identity semantics of autoboxing for values between 3      * -128 and 127 (inclusive) as required by JLS. 4      * 5      * The cache is initialized on first usage.  The size of the cache 6      * may be controlled by the -XX:AutoBoxCacheMax=<size> option. 7      * During VM initialization, java.lang.Integer.IntegerCache.high property 8      * may be set and saved in the private system properties in the 9      * sun.misc.VM class.10      */11 12     private static class IntegerCache {13         static final int low = -128;14         static final int high;15         static final Integer cache[];16 17         static {18             // high value may be configured by property19             int h = 127;20             String integerCacheHighPropValue =21                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");22             if (integerCacheHighPropValue != null) {23                 int i = parseInt(integerCacheHighPropValue);24                 i = Math.max(i, 127);25                 // Maximum array size is Integer.MAX_VALUE26                 h = Math.min(i, Integer.MAX_VALUE - (-low));27             }28             high = h;29 30             cache = new Integer[(high - low) + 1];31             int j = low;32             for(int k = 0; k < cache.length; k++)33                 cache[k] = new Integer(j++);34         }35 36         private IntegerCache() {}37     }

Cache is a static array of the internal classes of IntegerCache, which contains Integer objects ranging from-128 to 127. When a wrap object is generated through valueOf, if the int parameter is between-128 and 127, the object is obtained directly from the integer pool. If the int type is not in this range, the new type is used to generate the wrap object.

If you understand this, you can understand the above output results. 127 of the packaging objects are obtained directly from the integer pool, no matter how many times you enter the number 127, the obtained object is the same, and the address is equal. While 128 and 555 are beyond the range of the integer pool, a new object is generated through new, and the addresses are different, of course, they are not equal.

The above understanding is also the principle of the integer pool. The existence of the integer pool not only improves the system performance, but also saves memory space. This is why we use the integer pool, that is why valueOf is used to generate the encapsulated object, rather than the constructor. By the way, it is recommended that you use the equals method to avoid unexpected results when determining whether an object is equal.

Note: The packaging instance generated by the valueOf package type can significantly improve the space and time performance.

Recommendation 29: select the basic type first.

The packaging type is a class that provides very practical functions such as constructor, type conversion, and comparison. Besides, it achieves conversion from the basic type after Java 5, this makes the packaging type even more powerful and widely used. In development, the packaging type can be seen everywhere, but in terms of security and performance, or stability, the basic type is the preferred solution. Let's look at a piece of code:

1 public class Client29 {2 public static void main (String [] args) {3 Client29 c = new Client29 (); 4 int I = 140; 5 // pass the int type and Integer type 6 c. testMethod (I); 7 c. testMethod (new Integer (I); 8} 9 10 public void testMethod (long a) {11 System. out. println ("basic type method called"); 12} 13 14 public void testMethod (Long a) {15 System. out. println ("method of packaging type called"); 16} 17}

In the above program, we first declare an int variable I, then widen it into the long type, then call the testMethod () method, and pass the basic types and packaging types of int and long respectively, do you think this program can be compiled? What is the output result if it can be compiled?

First, this program can be compiled. However, the students that cannot be compiled are still confused. You may guess that compilation cannot be performed in the following areas:

(1) testMethod method overloading. The two testMethod () methods defined implement overload. One parameter is the basic type, and the other is the packaging type. This type of overload is normal. Although the basic and packaging types have the functions of automatic packing and automatic unpacking, they do not affect their overloading. Automatic unpacking only occurs when values are assigned, and it has nothing to do with compilation and overloading.

(2) c. testMethod (I) error. I is of the int type. It is no problem to pass it to testMethod (long a). The compiler will automatically widen the I type and convert it to the long type, this is the conversion rule for basic types, and there is no problem.

(3) c. testMethod (new Integer (I) error. Without the testMethod (Integer I) method in the code, it is impossible to receive a parameter of the Integer type. Besides, the two packaging types Integer and Long are brothers, not inheritance relations. That is to say, the compilation must have failed? No, the compilation is successful. I will explain why the compilation is successful later.

Now that the compilation is successful, let's take a look at the output:

Methods of the basic type are called.
Methods of the basic type are called.

C. The output of testMethod (I) is normal. As we have explained, the second output is confusing. Why does it call the testMethod (long a) method? This is because automatic packing has an important principle: the basic type can be widened first, and then converted to a wide type of packaging type, but cannot be directly converted to a wide type of packaging type. In short, int can be extended to long and then converted to a Long object, but cannot be directly converted to the packaging type. Note that all the values here refer to automatic conversion, it is not generated by the constructor. To explain this principle, let's look at an example:

1 public class Client29 {2 public static void main (String [] args) {3 Client29 c = new Client29 (); 4 int I = 140; 5 c. testMethod (I); 6} 7 8 public void testMethod (Long a) {9 System. out. println ("method of packaging type called"); 10} 11}

The compilation of this program fails, Because I is an int type and cannot be automatically converted to the Long type. However, you can modify it to the following code:

Int I = 140; long a = (long) I; c. testMethod ();
This means that int Is first widened to long type, and then automatically converted to Long type. The rule shows, let's continue to look at testMethod (Integer. how is valueOf (I) called, Integer. valueOf (I) returns an Integer object, which is correct, but Integer and int can be converted to each other. No testMethod (Integer I) method? It doesn't matter. The compiler will try to convert it to a real parameter of the int type for calling. OK. This time it is successful, it is the same as testMethod (I, as a result, it is obvious that the data is converted from a widening string to a long string. The execution process of the entire testMethod (Integer. valueOf (I) is as follows:

(1) I wrap it into an Integer object through the valueOf Method

(2) because there is no testMethod (Integer I) method, the compiler will "smartly" convert an Integer object to an int.

(3) int is automatically extended to long and compilation ends.

There are indeed convenient methods to use the packaging type, but it also causes unnecessary confusion. For example, in this example, if the two overload methods of testMethod () use the basic type, real parameters are also the basic type, so the above problems will not occur, and the program is more readable. Automatic packing (unpacking) is very convenient, but the problem is also very serious. We do not even know which method to execute.

Note: The basic type is preferred.

Suggestion 30: Do not set random Seeds

Random numbers are used in many places, such as encryption and obfuscation calculation. We use random numbers to expect a unique and non-counterfeited number to avoid confusion caused by the same business data. In Java projects, random numbers are usually obtained through the Math. Random Method and the random class. Let's look at a piece of code:

1 import java. util. random; 2 3 public class Client30 {4 public static void main (String [] args) {5 Random r = new Random (); 6 for (int I = 1; I <= 4; I ++) {7 System. out. println ("times" + I + ":" + r. nextInt (); 8 9} 10} 11}

The code is very simple. We generally obtain random numbers in this way. Running this program shows that the random numbers are different for three times of printing, even if multiple times of running results are different, this is exactly why we want a random number. Let's take a look at the following program:

1 public class Client30 {2 public static void main (String [] args) {3 Random r = new Random (1000); 4 for (int I = 1; I <= 4; I ++) {5 System. out. println ("times" + I + ":" + r. nextInt (); 6 7} 8} 9}

The preceding example uses the parameter structure of Random. The running result is as follows:

1st times:-1244746321
2nd times: 1060493871
3rd Times:-1826063944
4th times: 1976922248

Different computer outputs different random numbers, but one thing is the same: on the same machine, the number of times the render Manager runs, the printed random numbers are the same, that is, the first run, these random numbers will be printed, and the three random numbers will be printed during the second operation. As long as they are on the same machine, the same random number will always be printed, it seems that the random number is not random. What's the problem?

That is because the seed of the random number is fixed. in Java, the generation of the random number depends on the seed. The relationship between the random number and the seed follows the following two principles:

After reading the above two rules, let's take a look at this example. We will find that the problem lies in the Construction of parameters. The default seed of the Random class (No parameter construction) is System. the return value of nonoTime () (the default seed before JDK is System. currentTimeMillis () returned value). Note that this value is the number of nanoseconds from a fixed time point. different operating systems and hardware have different fixed time points, that is to say, different operating systems have different nanosecond values, and the same operating system has different nanosecond values, and the random numbers are naturally different. (By The Way, System. nonoTime cannot be used to calculate a date. It is because the "fixed" time is uncertain, and the nanosecond value may even be a negative value. currentTiemMillis is different ).

New Random (1000) sets the Random Seed to 1000 and runs multiple times. Although the instances are different, the same four Random numbers are obtained. Therefore, unless necessary, otherwise, do not set random seeds.

By the way, there are two methods in Java to obtain different random numbers: Through, java. util. the principle of Random number obtained by the Random class and Math. the random method is the same, Math. the random method also generates an instance of the Random class and then delegates the nextDouble () method. There is no difference between the two.

Note: Do not set Random Seed unless necessary.

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.