Common exception classes

Keywords: Java JavaSE

Common exception classes

1 abnormal

Poorly structured code cannot run, which is the basic concept of Java.

The ideal time to find errors is at compile time. However, the compiler cannot find all the errors, and the remaining problems need to be solved when the program runs. This requires that the error can pass the appropriate information to the specific receiver in some way. The purpose of exception handling in Java is to simplify the generation of large and reliable programs by using a small amount of code. In this way, there are no unhandled errors in your application, and it also brings an obvious benefit: reducing the complexity of error handling code.

Exception, according to the literal understanding, has the meaning of accident. Put it at the code level, that is, it prevents the current method or scope from continuing to execute.

2 Diagram of abnormal inheritance structure

Throwable is the base class of Exception and derives Exception and Error.

Among them, Error indicates that the system has a serious Error during operation, which cannot be recovered by the program itself, such as insufficient memory, stack memory overflow, etc. Exception indicates an exception that can be recovered. You can skip the part where the exception occurs through exception handling.

Exception s are divided into compile time exceptions (check exceptions) and run-time exceptions:

  1. RuntimeException and its subclasses (runtime exception): runtime exception (the program can not handle it, and an exception occurs during runtime, resulting in the collapse of the whole program)
    Common runtime exceptions:

    • NullPointerException: null pointer exception. Use a reference type with null value to call its method or property.

      String str=null;
      str.equals("123");//Null pointer exception
      
    • ArithmeticException: mathematical exception, such as denominator 0

    • IndexOutOfBoundsException: out of bounds exception; ArrayIndexOutOfBoundsException (array out of bounds exception),
      Stringindexoutofboundsexception (string subscript out of bounds exception)

    • Bufferoverflow exception: buffer overflow exception

    • ClassCastException: cast exception

    • Concurrent modificationexception: loop through the list and modify it at the same time

    https://www.cnblogs.com/zhuyeshen/p/10956822.html

  2. Non RuntimeException (compile time exception): handle l runtime exceptions. Other exceptions are compile time exceptions.

    Common compile time exceptions:

    • IOException
    • SQLException
    • NumberFormatException: number formatting exception

3 exception classes to understand

The following four are runtime exceptions:

java.lang.ArrayIndexOutOfBoundsException (array out of bounds)

java.lang.NullPointerException null pointer exception

java.lang.ArithmeticException contains an exception with divisor 0

java.lang.ClassCastException (java cast exception)

java.util.ConcurrentModificationException loop through the list and modify it at the same time

3.1 parsing ConcurrentModificationException

The following is from https://www.cnblogs.com/zhuyeshen/p/10956822.html

1, The reason why the ConcurrentModificationException occurred

Look at the following code first:

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                list.remove(integer);
        }
    }
}

Operation results:

From the exception information, it can be found that the exception appears in the checkforconfirmation () method.

We are not busy looking at the specific implementation of the checkForComodification() method. First, we look at the implementation of the ArrayList source code step by step according to the program code:

First, look at the specific implementation of the iterator() method of ArrayList. Looking at the source code, it is found that there is no iterator() method in the source code of ArrayList. Obviously, this method should be a method in its parent class or implemented interface. We found the specific implementation of the iterator() method in its parent class AbstractList. The following is its implementation code

public Iterator<E> iterator() {
    return new Itr();
}

From this code, we can see that what is returned is a reference to Itr type object. We then look at the specific implementation of Itr. We find the specific implementation of Itr class in AbstractList class, which is a member internal class of AbstractList. The following code is all the implementations of Itr class:

private class Itr implements Iterator<E> {
    int cursor = 0;
    int lastRet = -1;
    int expectedModCount = modCount;
    public boolean hasNext() {
           return cursor != size();
    }
    public E next() {
           checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
    }
    public void remove() {
        if (lastRet == -1)
        throw new IllegalStateException();
           checkForComodification();
 
        try {
        AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
            cursor--;
        lastRet = -1;
        expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
        }
    }
 
    final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }
}

First, let's look at several member variables:

cursor: indicates the index of the next element to be accessed, as can be seen from the concrete implementation of the next() method

lastRet: indicates the index of the last accessed element

expectedModCount: indicates the expected number of modifications to ArrayList. Its initial value is modCount.

modCount is a member variable in the AbstractList class

protected transient int modCount = 0;

This value indicates the number of modifications to the List. You can find by looking at the add() and remove() methods of ArrayList that each time you call the add() method or remove() method, you will add 1 to the modCount.

Well, here we look at the above program:

After calling list.iterator() to return an iterator, judge whether there are any elements that have not been accessed through the hashNext() method of the iterator. Let's take a look at the hashNext() method. The implementation of the hashNext() method is very simple:

public boolean hasNext() {
    return cursor != size();
}

If the subscript of the next accessed element is not equal to the size of ArrayList, it means that there are elements to access. This is easy to understand. If the subscript of the next accessed element is equal to the size of ArrayList, it must reach the end.

Then get the element with subscript 0 through the next() method of Iterator. Let's take a look at the specific implementation of the next() method:

public E next() {
    checkForComodification();
 try {
    E next = get(cursor);
    lastRet = cursor++;
    return next;
 } catch (IndexOutOfBoundsException e) {
    checkForComodification();
    throw new NoSuchElementException();
 }
}

......

    final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }

This is a key point: first, the checkForComodification() method will be called in the next() method (an exception will be thrown if the actual modification times are inconsistent with the required modification times). After passing the inspection, the element will be obtained according to the value of cursor, then the value of cursor will be assigned to lastRet, and the value of cursor will be added by 1. Initially, cursor is 0 and lastRet is - 1. After one call, cursor is 1 and lastRet is 0. Note that at this time, modCount is 0 and expectedModCount is 0.

Next, look down. In the program, judge whether the value of the current element is 2. If it is 2, call the list.remove() method to delete the element.

Let's take a look at what the remove() method in ArrayList does:

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
 
 
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                numMoved);
    elementData[--size] = null; // Let gc do its work
}

To delete an element through the remove method, the fastRemove() method is finally called. In the fastRemove() method, first add 1 to the modCount (because the collection is modified once), then delete the element, and finally subtract 1 from the size and set the reference to null to facilitate the garbage collector's collection.

Note the values of each variable at this time: for the iterator, its expectedModCount is 0, the value of cursor is 1, and the value of lastRet is 0.

For list, its modCount is 1 and its size is 0.

Then look at the program code. After the deletion operation, continue the while loop and call the hasNext method () to judge. Since the cursor is 1 and the size is 0, it returns true, so continue the while loop and call the next() method of the iterator:

Note that at this time, pay attention to the first sentence in the next() method: checkforconfirmation().

The operations in the checkForComodification method are:

final void checkForComodification() {
    if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
}

If modCount is not equal to expectedModCount, a ConcurrentModificationException is thrown.

Obviously, at this time, modCount is 1 and expectedModCount is 0, so the program throws a ConcurrentModificationException.

At this point, you must understand why the above code throws a ConcurrentModificationException.

The key point is that calling the list.remove() method results in inconsistent values of modCount and expectedModCount.

Note that iterations like using for each actually have this problem.

2, Solutions in a single threaded environment

Now that you know the reason, how to solve it?

In fact, it's very simple. Careful friends may find that a remove() method is also given in Itr class:

public void remove() {
    if (lastRet == -1)
    throw new IllegalStateException();
       checkForComodification();
 
    try {
    AbstractList.this.remove(lastRet);
    if (lastRet < cursor)
        cursor--;
    lastRet = -1;
    expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
    throw new ConcurrentModificationException();
    }
}

In this method, the list.remove() method is actually called to delete elements, but it has one more operation:

expectedModCount = modCount;

Therefore, if you want to delete an element in the iterator, you need to call the remove method of the Itr class.

Change the above code to the following so that no error will be reported:

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                iterator.remove();   //Note that this place used to be list.remove(integer)
        }
    }
}
3, Solutions in multithreaded environment

The above solution works in a single threaded environment, but does it work in a multi-threaded environment? Take the following example:

public class Test {
    static ArrayList<Integer> list = new ArrayList<Integer>();
    public static void main(String[] args)  {
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        Thread thread1 = new Thread(){
            public void run() {
                Iterator<Integer> iterator = list.iterator();
                while(iterator.hasNext()){
                    Integer integer = iterator.next();
                    System.out.println(integer);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        };
        Thread thread2 = new Thread(){
            public void run() {
                Iterator<Integer> iterator = list.iterator();
                while(iterator.hasNext()){
                    Integer integer = iterator.next();
                    if(integer==2)
                        iterator.remove(); 
                }
            };
        };
        thread1.start();
        thread2.start();
    }
}

Operation results:

Some friends may say that ArrayList is a non thread safe container. It is OK to replace it with Vector. In fact, this error will still occur when replacing it with Vector.

The reason is that although the Vector method uses synchronized for synchronization, in fact, when accessed through the iterator, different iterators are returned in each thread, that is, expectedModCount is private to each thread. If there are two threads at this time, thread 1 is traversing and thread 2 is modifying, it is likely that the modification of thread 2 will cause the modCount in the Vector to increase automatically, and the expectedModCount of thread 2 to increase automatically, but the expectedModCount of thread 1 does not increase automatically. At this time, the duration of thread 1 will appear that the expectedModCount is not equal to the modCount.

Therefore, there are generally two solutions:

1) When iterating with iterator, use synchronized or Lock to synchronize;

In fact, this error will still occur if you change to Vector.

The reason is that although the Vector method uses synchronized for synchronization, in fact, when accessed through the iterator, different iterators are returned in each thread, that is, expectedModCount is private to each thread. If there are two threads at this time, thread 1 is traversing and thread 2 is modifying, it is likely that the modification of thread 2 will cause the modCount in the Vector to increase automatically, and the expectedModCount of thread 2 to increase automatically, but the expectedModCount of thread 1 does not increase automatically. At this time, the duration of thread 1 will appear that the expectedModCount is not equal to the modCount.

Therefore, there are generally two solutions:

1) When iterating with iterator, use synchronized or Lock to synchronize;

2) Use the concurrent container CopyOnWriteArrayList instead of ArrayList and Vector.

Posted by jburfield on Sat, 09 Oct 2021 23:23:45 -0700