The following for each error with a text description combined with the code to explain the way to show you, the specific contents are as follows:
1. Excessive use of Null
Avoiding excessive use of null values is a best practice. For example, it would be a better practice to have the method return an empty array or collection instead of a null value, because it prevents the program from throwing NullPointerException. The following code fragment obtains a collection from another method:
list<string> accountids = Person.getaccountids ();
for (String accountid:accountids) {
processaccount (accountid);
}
When a person has no account, Getaccountids () returns a null value, and the program throws a NullPointerException exception. Therefore, it is necessary to add an empty check to solve this problem. If you replace the returned null value with an empty list, then NullPointerException does not appear. And, because we no longer need to check the variable accountid short, the code will become more concise.
When you want to avoid null values, different scenarios may take a different approach. One way to do this is to use the Optional type, which can be either an empty object or a package of values.
optional<string> optionalstring = optional.ofnullable (nullablestring);
if (Optionalstring.ispresent ()) {
System.out.println (Optionalstring.get ());
}
In fact, JAVA8 provides a more concise approach:
optional<string> optionalstring = optional.ofnullable (nullablestring);
Optionalstring.ifpresent (System.out::p rintln);
Java is supported by the Optional type from the JAVA8 version, but it is already well known in the functional programming world. Prior to this, it had been used in Google guava for earlier versions of Java.
2. Ignoring anomalies
We often ignore the anomaly. However, for beginners and experienced Java programmers, best practices still deal with them. Exception throwing is usually purposeful, so in most cases it is necessary to record the event that caused the exception. Don't underestimate the matter, if necessary, you can throw it back, display the error message to the user in a dialog box or log the error message in the journal. At the very least, in order for other developers to know the cause and the consequences, you should explain why you didn't handle the anomaly.
Selfie = Person.shootaselfie ();
try {
selfie.show ();
} catch (NullPointerException e) {
//Maybe, Invisible Man. Who cares, anyway?
}
An easy way to emphasize that an exception is unimportant is to use this information as the variable name for the exception, like this:
Copy Code code as follows:
try {selfie.delete ();} catch (NullPointerException unimportant) {}
3. Concurrency Modification Exception
This exception occurs when the collection object is modified without using the method provided by the iterator object to update the contents of the collection. For example, here is a list of hats and you want to delete all the values that contain the ear flaps:
list<ihat> hats = new arraylist<> ();
Hats.add (New Ushanka ()); That one has ear flaps
hats.add (New Fedora ());
Hats.add (New Sombrero ());
for (Ihat hat:hats) {
if (Hat.hasearflaps ()) {
hats.remove (hat);
}
}
If you run this code, Concurrentmodificationexception will be thrown because the code modifies it as it traverses the collection. The same exception may occur when multiple processes are acting on the same list, and when one of the processes traverses the list, another process attempts to modify the contents of the list.
It is very common to modify the collection content concurrently in multiple threads, so you need to use methods commonly used in concurrent programming, such as synchronous locks, special collections for concurrent modifications, and so on. Java solves this problem in a single-threaded and multi-threaded situation with a slight difference.
Collect objects and delete them in another loop
The immediate solution is to put hats with the ear flaps into a list and then remove it with another loop. However, this requires an additional collection to hold hats that will be removed.
list<ihat> hatstoremove = new linkedlist<> ();
for (Ihat hat:hats) {
if (Hat.hasearflaps ()) {
hatstoremove.add (hat);
}
}
for (Ihat hat:hatstoremove) {
hats.remove (hat);
}
Using the Iterator.remove method
This method is simpler and does not require the creation of additional collections:
iterator<ihat> hatiterator = Hats.iterator ();
while (Hatiterator.hasnext ()) {
Ihat hat = Hatiterator.next ();
if (Hat.hasearflaps ()) {
hatiterator.remove ();
}
}
How to use Listiterator
The list iterator is a very appropriate choice when the set that needs to be modified implements the list interface. The iterator that implements the Listiterator interface not only supports delete operations, but also add and set operations. The Listiterator interface implements the iterator interface, so this example looks like the iterator remove method. The only difference is the type of hat iterator and the way we get iterator-using the Listiterator () method. The following fragment shows how to use the Listiterator.remove and Listiterator.add methods to replace hat with an ear flaps with a sombreros.
Ihat sombrero = new Sombrero ();
listiterator<ihat> hatiterator = Hats.listiterator ();
while (Hatiterator.hasnext ()) {
Ihat hat = Hatiterator.next ();
if (Hat.hasearflaps ()) {
hatiterator.remove ();
Hatiterator.add (sombrero);
}
Using Listiterator, the Remove and add methods are invoked to replace the call to only one set method:
Ihat sombrero = new Sombrero ();
listiterator<ihat> hatiterator = Hats.listiterator ();
while (Hatiterator.hasnext ()) {
Ihat hat = Hatiterator.next ();
if (Hat.hasearflaps ()) {
hatiterator.set (sombrero);//Set instead of Remove and add
}
}
Using the Stream method in Java 8
In Java8, a developer can convert a collection to a stream and filter the stream according to some conditions. This example describes how the stream API filters hats and avoids concurrentmodificationexception. Hats = Hats.stream (). Filter ((Hat->!hat.hasearflaps ())
Copy Code code as follows:
. Collect (Collectors.tocollection (arraylist::new));
The Collectors.tocollection method will create a new ArrayList, which is responsible for storing the filtered hats values. If filter conditions filter out a large number of entries, there will be a large ArrayList. Therefore, careful use is required.
Using the List.removeif method in Java 8
You can use another, simpler and clearer method in Java 8 to--removeif methods:
Copy Code code as follows:
Hats.removeif (Ihat::hasearflaps);
At the bottom, it uses Iterator.remove to complete this operation.
Use a special collection
If you decide to use copyonwritearraylist instead of ArrayList at the outset, there is no problem. Because Copyonwritearraylist provides a modified method (for example, Set,add,remove), it does not change the original collection array, but instead creates a new modified version. This allows modifications to be made while traversing the original collection of versions, thus not throwing concurrentmodificationexception exceptions. The drawbacks of this collection are also obvious--a new set is generated for each modification.
There are other sets that apply to different scenarios, such as Copyonwriteset and Concurrenthashmap.
About another error that might occur when you modify a collection concurrently is to create a stream from a collection, while traversing the stream, modifying the collection at the back end. The general rule for stream is to avoid modifying the collection of the backend when querying the stream. The next example shows how to handle the stream correctly:
list<ihat> filteredhats = Hats.stream (). Peek (hat-> {
if (Hat.hasearflaps ()) {
hats.remove (hat);
}
}). Collect (Collectors.tocollection (arraylist::new));
The Peek method collects all the elements and performs a set action on each element. Here, the action is obviously wrong to try to remove data from an underlying list. To avoid such an operation, you can try some of the methods explained above.
4. Breach of contract
Sometimes, in order to better collaborate, code provided by a standard library or a third party must adhere to a common dependency guideline. For example, you must adhere to the common conventions of hashcode and equals to ensure that a series of collection classes and other classes that use the hashcode and Equals methods in the Java collection framework work correctly. Failure to comply does not produce exception or corrupt code-compilation errors; it is insidious because it can change application behavior at any time without any hint of danger.
Error codes can sneak into the production environment, causing a lot of bad effects. This includes poor UI experience, bad data reporting, poor application performance, data loss, or more. Thankfully, these catastrophic mistakes don't happen very often. The hashcode and equals conventions have been mentioned before, and the scenario may be that the collection relies on hashing or comparing objects, like HashMap and HashSet. In short, there are two guidelines for this Convention:
If two objects are equal, then the hash code must be equal.
If two objects have the same hash code, they may or may not be equal.
Breaking the first rule of engagement, when you try to retrieve data from a HASHMAP will cause an error. The second guideline means that objects with the same hash code are not necessarily equal.
Here's a look at the consequences of breaking the first rule:
public static class Boat {
private String name;
Boat (String name) {
this.name = name;
}
@Override public
Boolean equals (Object o) {
if (this = O) return true;
if (o = = NULL | | getclass ()!= O.getclass ()) return false;
Boat boat = (boat) o;
Return! (Name!= null!name.equals (boat.name): Boat.name!= null);
@Override public
int hashcode () {return
(int) (Math.random () * 5000);
}
As you can see, the boat class overrides the Equals and Hashcode methods. However, it breaks the convention because Hashcode returns a random value for the same object that is invoked each time. The following code is likely to not find a boat named Enterprise in HashSet, although in fact we have joined this type of boat in advance:
public static void Main (string[] args) {
set<boat> boats = new hashset<> ();
Boats.add (New boat ("Enterprise"));
System.out.printf ("We have a boat named ' Enterprise ':%b\n", Boats.contains (New boat ("Enterprise"));
}
Another example of a convention is the finalize method. Here is a reference to the official Java documentation about its functional description:
The general Convention for finalize is that when the JAVATM virtual machine determines that any thread can no longer access the specified object in any way, the method is invoked, and thereafter the object can be used as a result of some other (ready to terminate) object or class when it is terminated. The Finalize method has several features, including the ability to make this object available to other threads again, but the main purpose of finalize is to perform a purge before the object is irrevocably discarded. For example, a Finalize method that represents an input/output connection object can perform an explicit I/O transaction to break the connection before the object is permanently discarded.
You can decide to use the Finalize method in a file processor to release resources, but this usage is bad. Because it is invoked during garbage collection, and the time of the GC is uncertain, the time of the finalize call is not guaranteed.
5. Use the original type instead of the parameterized
According to the Java document description: The original type is either parameterized or is a non-static member of Class R (which is also a non-inherited r parent class or parent interface). Before the Java generics were introduced, there was no substitute type for the original type. Java supports generic programming starting with version 1.5, which is undoubtedly an important feature boost. However, for backward compatibility reasons, there is a trap that could potentially disrupt the entire type system. Focus on the following example:
List listofnumbers = new ArrayList ();
Listofnumbers.add (ten);
Listofnumbers.add ("twenty");
Listofnumbers.foreach (n-> System.out.println ((int) n * 2));
This is a list of numbers that is defined as the original ArrayList. Because it does not specify a type parameter, you can add any object to it. But the last line maps the elements it contains to the int type and multiplies it by 2, printing out the doubled data to standard output.
This code compiles without errors, but when run, it throws a run-time error because it attempts to map the character type to an integer. Obviously, if the necessary information is hidden, the type system will not be able to help write the security code.
To solve this problem, you need to specify a specific type for the objects that are stored in the collection:
list<integer> listofnumbers = new arraylist<> ();
Listofnumbers.add (ten);
Listofnumbers.add ("twenty");
Listofnumbers.foreach (n-> System.out.println ((int) n * 2));
The only difference from the previous code is the line that defines the collection:
Copy Code code as follows:
list<integer> listofnumbers = new arraylist<> ();
The modified code compilation cannot be passed because it attempts to add a string to the collection that only expects to store the reshaping. The compiler will display an error message and point to the line that you are trying to add twenty characters to the list. A parameterized generic type is a good idea. In this way, the compiler will be able to check all possible types, thus resulting in a significant reduction in run-time exceptions due to inconsistent types.
The main summary of the above five Java programmers often make mistakes, I hope you can enjoy.