Effective Java(Item: 57 to 78)

本文探讨了异常处理及并发编程中的关键概念和技术细节,包括合理使用异常、避免不必要的同步锁、优选执行器和任务模型等,为开发者提供了实用的指导。

Eight: Exceptions
Item 57: Use exceptions only for exceptional conditions
Exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow:
1).Because exception are designed for exceptional circumstances, there is little incentive for JVM implementors to make them as fast as explicit tests;
2).Placing code inside a try-catch block inhibits certain optimizations that modern JVM implementations might otherwise perform;
3).The standard idiom for looping through an array doesn’t necessarily result in redundant checks. Modern JVM implementations optimize them away;
A will-designed API must not force its clients to use exceptions for ordinary control flow:
1).A class with a “state-dependent” method that can be invoked only under certain unpredictable conditions should generally have a separate “state-testing” method indicating whether it is appropriate to invoke the state-dependent method;
2).An alternative to providing a separate state-testing method is to have the state-dependent method return a distinguished value such as null if is invoked with the object in an inappropriate state;
For example:

...
for (Iterator<Foo> i = collection.iterator(); i.hasNext();) {
    Foo foo = i.next();
    ...
}

// Do not use this hideous code for iterator over a collection!\
try {
    Iterator<Foo> i = collection.iterator();
    while(true) {
        Foo foo = i.next();
        ...
    }
} catch(NoSuchElementException e) {
}

Item 58: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors
The java programming language provides three kinds of throwables: checked exceptions, runtime exceptions, and errors. There are some rules that provide strong guidance.
1).Use checked exceptions for conditions from which the caller can reasonably by expected to recover;
2).If a program throws an unchecked exception or an error, it is generally the case that recovery is impossible and continued execution would do more harm than good;
3).If a program does not catch such a throwable, it will cause the current thread to halt with an appropriate error message;
4).Use runtime exceptions to indicate programming errors;
5).All of the unchecked throwables you implement should subclass RuntimeException;
6).It has no benefits over an ordinary checked exception and would merely serve to confuse the user of your API;
7).If you believe a condition is likely to allow for recovery, use a checked exception; if not, use a runtime exception. If it isn’t clear whether recovery is possible, you’re probably better off using an unchecked exception.

Item 59: Avoid unnecessary use of checked exception
Checked exceptions are a wonderful feature of the Java programming. They force the programmer to deal with exception conditions.
1).If the exceptional condition cannot be prevented by proper use of the API and the programmer using the API can take some useful action once confronted with the exception. Unless both of these conditions hold, an unchecked exception is more appropriate;
2).If the programmer using the API can do no better, an unchecked exception would be more appropriate;
3).The additional burden on the programmer caused by a checked exception is substantially higher if it is the sole checked exception thrown by a method;
4).One technique for turning a checked exception into an unchecked exception to break the method that throws the exception into two methods, the first of which returns a boolean that indicates whether the exception would be thrown;
Here’s the code:

...
// Invocation with checked exception
try {
    obj.action(args);
} catch(TheCheckedException e) {
    //Handle exceptional condition
    ...
}
...

// Invocation with state-testing method and unchecked exception
if(obj.actionPermitted(args)) {
    obj.action(args);
} else {
    //Handle exceptional condition
    ...
}

Item 60: Favor the use of standard exception
Exceptions are no exception to the general rule that code reuse is good. The Java platform libraries provide a basic set of unchecked exceptions that cover a large fraction of the exception-throwing needs of most APIs. Not only it make your API easier to learn and use but also programs using your API are easier to read.
The commonly reused exception are here:
1).IllegalArgumentException–This is generally the exception to throw when the caller passes in an argument hose value is inappropriate;
2).IllegalStateException—This is generally the exception to throw if the invocation is illegal because of the state of the receiving object;
3).If a caller passes null in some parameter for which null values are prohibited, convention dictates that NullPointerException be thrown;
4).If a caller passes an out-of-range value in a parameter representing an index into a sequence, IndexOutOgBoundsException should be thrown;
5).ConcurrentModificationException—This exception should be thrown if an object that was designed for use by a single thread or with external synchronization detects that it is being(or has been) concurrently modified;
6).UnsupportedOperationException—This is the exception to throw if an object does not support an attempted operation;
In summary, if an exception fits your needs, go ahead and use it, but only if the conditions under which you would throw it are consistent with the exception’s documentation.

Item 61: Throw exceptions appropriate to the abstraction
Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction. This idiom is known as exception translation. A special form of exception translation called exception chaining is appropriate in cases where the lower-level exception might be helpful to someone debugging the problem that caused the higher-level exception.
While exception translation is superior to mindless propagation of exception from lower layers, it should not be overused:
1).The best way to deal with exceptions from lower layers is to avoid them, by ensuring that lower-level methods succeed;
2).Checking the validity of the higher-level method’s parameters before passing them on to lower layers;
In summary, if it isn’t feasible to prevent or to handle exception from lower layers, use exception translation, unless the lower-level method happens to guarantee that all of its exceptions are appropriate to the higher-level. Chaining provides the best of both worlds: it allows you to throw an appropriate higher-level exception, while capturing the underlying cause for failure analysis.
1).Exception translation:

public class AbstractList {
    /**
     * Return the element at the specified position in this list.
     * @throws IndexOutOfBoundsException if the index is out of range
     *         ({@code index < 0 || index >= size()}).
     */
    public E get(int index) {
        ListIterator<E> i = listIterator(index);
        try {
            return i.next();
        } catch(NoSuchElementException e) {
            throw new IndexOutOfBoundsException("Index: " + index);
        }
    }
}///:~

2).Exception Chaining:

...
try {
    ...// Use lower-level abstraction to do bidding
} catch(LowerLevelException cause) {
    throw new HigherLevelException(cause);
}

// Exception with chaining-aware constructor
class HigherLevelException extendes Exception {
    HigherLevelException(Throwable cause) {
        super(cause);
    }
}

Item 62: Document all exceptions thrown by each method
1).Always declare checked exception individually, and document precisely the conditions under which each one is thrown using the Javadoc @throws tag;
2).Use the Javadoc @throws tag to documents each unchecked exception that a method can throw, but do not use throws keyword to include unchecked exceptions in the method declaration;
3).If an exception is thrown by many methods in a class for the same reason, it is acceptable to document the exception in the class’s documentation comment;
In summary, document every exception that can be thrown by each method that you write. This is true for unchecked as well as checked exceptions, and for abstract as well as concrete methods. Provide individual throws clauses for each checked exception and do not provide throws clauses for unchecked exception. If you fail to document the exceptions that your methods can throw, it will be difficult or impossible for others to make effective use of you classes and interfaces.

Item 63: Include failure-capture information in detail messages
1).To capture the failure, the detail message of an exception should contain the value of all parameters and fields that “contributed to the exception.”;
2).One way to ensure that exceptions contain adequate failure-capture information in their detail message is to require this information in their constructors instead of a string detail message;
For example:

/**
     *Construct an IndexOutOfBoundsException.
     *
     *@param lowerBound the lowest legal index value.
     *@param upperBound the highest legal index value plus one.
     *@param index      the actual index value.
     */
    public IndexOutOfBoundsException(int lowerBound, int upperBound,
            int index) {
        // Generate a detail message that captures the failure
        super("Lower bound: " + lowerBound +
                ", Upper bound: " + upperBound +
                ", Index: " +index);

        // save failure information for programmatic access
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
        this.index = index;
    }

Item 64: Strive for failure atomicity
Generally specking, a failed method invocation should leave the object in the state that it was in prior to the invocation.
1).The simplest is to design immutable objects;
2).For methods that operate on mutable objects, the most common way to achieve failure atomicity is to check parameters for validity before performing the operation. A closely relate approach to achieving failure atomicity is to order the computation so that any part that may fail takes place before any part that modifies the object;
3).A third and far less common approach to achieving failure atomicity is to write recovery code that intercepts a failure that occurs in the midst of an operation begin;
4).A final approach to achieving failure atomicity is to perform the operation on a temporary copy of the object and to replace the contents of the object with the temporary copy once the operation is complete;
As a rule, any generated exception that is part of a method’s specification should leave the object in the same state it was in prior to the method invocation. Where this rule is violated, the API documentation should clearly indicate what state the object will be left in.
For example:

...
    public Object pop() {
        if(size == 0) 
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null; // Eliminate obsolete reference
        return result;
    }
...

Item 65: Don’t ignore exception
An empty catch block defeats the purpose of exceptions. At the least, the catch block should contain a comment explaining why it is appropriate to ignore the exception.
The advice in this item applies equally to checked and unchecked exceptions. Whether an exception represents a predictable exceptional condition or a programming error, ignoring it with an empty catch block will result in a program that continues silently in the face of error. The program might then fail at an arbitrary time in the future, at a point in the code that bears no apparent relation to the source of the problem. Properly handing an exception can avert failure entirely. Merely letting an exception propagate outward can at least cause the program to fail swiftly, preserving information to aid in debugging the failure.
Like this:

...
//Empty catch block ignores exception – Highly suspect!
try {
    …
} catch(SomeException e) {
}
…

Nine: Concurrency
Item 66: Synchronize access to shared mutable data
You may hear it said that to improve performance, you should avoid synchronization when reading or writing atomic data. This advice is dangerously wrong.
Here’s the important rules:
1).Synchronization is required for reliable communication between threads as well as for mutual exclusion;
2).Synchronization has no effect unless both read and write operations are synchronized;
3).Confine mutable data to a single thread;
4).There are many ways to safely publish an object reference: you can store it in a static fields as part of class initialization; you can store it in a volatile field, a final field, or a field that is accessed with normal locking; or you can put it into a concurrent collection;
5).When multiple threads share mutable data, each thread that reads or write the data must perform synchronization;
For example:

import java.util.concurrent.TimeUnit;

/** Broken! - How long would you except this program to run? 
 *  The program never terminates: the background thread loops forever!
 *  It's quite acceptable for the virtual machine to transform this code:
 *  while(!done)
 *      i++;
 *  into this code:
 *  if(!done)
 *      while(true)
 *          i++
 *  This optimization is known as hoisting, and it is precisely what the
 *  HotSpot server VM does.
 */
public class OrdinalStopThread {
    private static boolean stopRequested;

    public static void main(String[] args) 
            throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while(!stopRequested)
                    i++;
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}///:~

One way to fix the problem is to synchronize access to the stopRequested field:

import java.util.concurrent.TimeUnit;

// Properly synchronized cooperative thread termination 
public class SynchronizeStopThread {
    private static boolean stopRequested;
    private static synchronized void requestStop() {
        stopRequested = true;
    }
    private static synchronized boolean stopRequested() {
        return stopRequested;
    }

    public static void main(String[] args) 
            throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while(!stopRequested())
                    i++;
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        requestStop();
    }
}///:~

Or you can fix the problem use volatile:

import java.util.concurrent.TimeUnit;

// Cooperative thread termination with a volatile field
public class VolatileStopThread {
    private static volatile boolean stopRequested;

    public static void main(String[] args) 
            throws InterruptedException{
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while(!stopRequested)
                    i++;
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}///:~ 

Item 67: Avoid excessive synchronization
1).To avoid liveness and safety failures, never cede control to the client within a synchronized method or block;
2).As a rule, you should do as little work as possible inside synchronized regions;
3).In multicore world, the real cost of excessive synchronization is not the CPU time spent obtaining locks; it is the lost opportunities for parallelism and the delays imposed by the need to ensure that every core has a consistent view of memory. Another hidden cost of oversynchronization is that it can limit the VM’s ability to optimize code execution;
In summary, to avoid deadlock and data corruption, never call an alien method from within a synchronized region. More generally, try to limit the amount of work that you do from within synchronized regions. When you are designing a mutable class, think about whether it should do its own synchronization. In the modern multicore era, it is more important than ever not to synchronization excessively. Synchronize your class internally only if there is a good reason to do so, and document your decision clerly.
Example:

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExcessiveSynchronization {
    public static void main(String[] args) {
        ObservableSet<Integer> set = 
            new ObservableSet<Integer>(new HashSet<Integer>());
        /* the following program prints the number from 0 to 9
        set.addObserver(new SetObserver<Integer>() {
            public void added(ObservableSet<Integer> s, Integer e) {
                System.out.println(e);
            }
        });
        */
        /* the following program will throw a ConcurrentModificationException
        set.addObserver(new SetObserver<Integer>() {
            public void added(ObservableSet<Integer> s, Integer e) {
                System.out.println(e);
                if(e == 3) s.removeObserver(this);
            }
        });*/
        /* the following program won't throw a exception but get a deadlock
         *  observer that uses a background thread beedlessly
        set.addObserver(new SetObserver<Integer>() {
            public void added(final ObservableSet<Integer> s, Integer e) {
                System.out.println(e);
                if(e == 3) {
                    ExecutorService executor =
                        Executors.newSingleThreadExecutor();
                    final SetObserver<Integer> observer = this;
                    try {
                        executor.submit(new Runnable() {
                            public void run() {
                                s.removeObserver(observer);
                            }
                        }).get();
                    } catch(ExecutionException ex) {
                        throw new AssertionError(ex.getCause());
                    } catch(InterruptedException ex) {
                        throw new AssertionError(ex.getCause());
                    } finally {
                        executor.shutdown();
                    }
                }
            }
        });*/
        for(int i = 0; i < 10; i++)
            set.add(i);
    }
}

class ObservableSet<E> extends ForwardingSet<E> {
    public ObservableSet(Set<E> set) { super(set); }

    private final List<SetObserver<E>> observers =
        new ArrayList<SetObserver<E>>();

    public void addObserver(SetObserver<E> observer) {
        synchronized(observers) {
            observers.add(observer);
        }
    }
    public boolean removeObserver(SetObserver<E> observer) {
        synchronized(observer) {
            return observers.remove(observer);
        }
    }
    private void notifyElementAdded(E element) {
        synchronized(observers) {
            for(SetObserver<E> observer : observers)
                observer.added(this, element);
        }
    }
    @Override public boolean add(E element) {
        boolean added = super.add(element);
        if(added)
            notifyElementAdded(element);
        return added;
    }
}

interface SetObserver<E> {
    void added(ObservableSet<E> set, E element);
}///:~

To fix the problem:
1).

// Alien method moved outside of synchronized block - open calls
 private void notifyElementAdded(E element) {
        List<SetObserver<E>> snapshot = null;
        synchronized(observers) {
            snapshot = new ArrayList<SetObserver<E>>(observers);
        }
            for(SetObserver<E> observer : snapshot)
                observer.added(this, element);
    }
…

2).

class ObservableSet<E> extends ForwardingSet<E> {
    public ObservableSet(Set<E> set) { super(set); }

    // Thread-safe observable set with CopyOnWriteArrayList
    private final List<SetObserver<E>> observers =
        new CopyOnWriteArrayList<SetObserver<E>>();
    public void addObserver(SetObserver<E> observer) {
            observers.add(observer);
    }
    public boolean removeObserver(SetObserver<E> observer) {
            return observers.remove(observer);
    }
    private void notifyElementAdded(E element) {
            for(SetObserver<E> observer : observers)
                observer.added(this, element);
    } 
    @Override public boolean add(E element) {
        boolean added = super.add(element);
        if(added)
            notifyElementAdded(element);
        return added;
    }
}
…

Item 68: Prefer executors and tasks to threads
java.util.concurrent, this package contains an Executor Framework, which is a flexible interface-based task execution facility and including these hits:
1).If you want more one thread to process requests from the queue, simply call a different static factory that creates a different kind of executor service called a thread pool;
2).If you’re writing a small program, or a lightly loaded server, using Executors.newCachedThreadPool is a generally a good choice;
3).In a heavily loaded production server, you are mush better off using Executors.newFixedThreadPool;
4).The Framework also has a replacement for java.util.Timer, which is ScheduledThreadPoolExecutor;
The key abstraction is no longer Thread, which served as both the unit of work and the mechanism for executing it. Now the unit of work and mechanism are separate. The key abstraction is the unit of work, which is called a task. There are two kinds of tasks; Runnable and its close cousin, Callable(which is like Runnable, except that it returns a value).

Item 69: Prefer concurrency to wait and notify
Given the difficulty of using wait and notify correctly you should use the higher-level concurrency utilities instead. The higher-level utilities in java.util.concurrency fall unto three categories: the Executor Framework, concurrent collections and synchronizers.
It is impossible to exclude concurrent activity from a concurrent collection; locking it will have no effect. Using concurrentHashMap in preference to Collections.synchronizedMap or Hashtable. Like this example:

  import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ConcurrentUtility {
    // Concurrent canonicalizing map atop ConcurrentMap - not optimal
    private static final ConcurrentMap<String, String> map =
        new ConcurrentHashMap<String, String>();

    public static String intern(String s) {
        String previousValue = map.putIfAbsent(s, s);
        return previousValue == null ? s : previousValue;
    }

    // concurrent canonicalizing map atop ConcurrentMap - faster!
    public static String enhenceIntern(String s) {
        String result = map.get(s);
        if(result == null) {
            result = map.putIfAbsent(s, s);
            if(result == null)
                result = s;
        }
        return result;
    }
}///:~

In summary, using wait and notify directly is like programming in “concurrency assembly language,” as compared to the higher-level language provided by java.util.concurrent. There is seldom, if ever, a reason to use wait and notify in new code. If you maintain code that uses wait and notify, make sure that it always invokes wait from within a while loop using the standard idiom. The notifyAll method should generally be used in preference to notify. If notify is used, great care must be taken to ensure liveness.
Example code:
1).CountDownLatch:
`import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

// Simple framework for timing concurrent execution
public class ConcurrentExecution {

public static void main(String[] args) throws InterruptedException {
    Executor executor = Executors.newFixedThreadPool(1000);
    System.out.println(time(executor, 1000, new Runnable() {
        public void run() {
        }
    }));
}

public static long time(Executor executor, int concurrency,
        final Runnable action) throws InterruptedException {
    final CountDownLatch ready = new CountDownLatch(concurrency);
    final CountDownLatch start = new CountDownLatch(1);
    final CountDownLatch done = new CountDownLatch(concurrency);
    for(int i = 0; i < concurrency; i++) {
        executor.execute(new Runnable() {
            public void run() {
                ready.countDown(); // Tell timer we're ready
                try {
                    start.await(); // Wait till peers are ready
                    action.run();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    done.countDown();
                }
            }
        });
    }
    ready.await(); // Wait for all workers to be ready
    long startNanos = System.nanoTime();
    start.countDown(); // And they're off!
    done.await(); //Wait for all workers to finish
    return System.nanoTime() - startNanos;
        } 

}/*Output:
21569113
*///:~

`2).wait():

…
// The standard iiom for using the wait method
synchronized(obj) {
    while(<condition does not hold>)
        obj.wait(); // (Release lock, and reacquires no wakeup)

    ...//Perform action appropriate to condition
}
…

Item 70: Document thread safety
1).The presence of the synchronized modifier in a method declaration in an implementation detail, not a part of its exported API.
2).To enable safe concurrent use, a class must clearly document what level of thread safety it supports.
(1).immutable—Instances of this class appear constant. No external synchronization is necessary;
(2).unconditionally thread-safe—Instances of this class are mutable, but the class has sufficient internal synchronization that its instances can be used concurrently without the need for any external synchronization;

// Private lock object idiom - thwarts denial -of-service attack
private final Object lock = new Object();

public void foo() {
    synchronized(lock) {
        …
    }
}
…

(3).conditionally thread-safe—Like unconditionally thread-safe, except that some methods require external synchronization for safe concurrent use.
(4).not thread-safe—Instances of this class are mutable, To use them concurrently, clients must surround each method invocation(or invocation sequence) with external synchronization of the client’s choosing;
(5).thread-hostile—This class is not safe for concurrent use event if all method in vocations are surrounded by external synchronization.
To summarized, every class should clearly document its thread safety properties a carefully worded prose description or a thread safety annotation. The synchronized modifier plays no part in this documentation. Conditionally thread-safe classes must document which method invocation sequences require external synchronization, and which lock to acquire when executing these sequences. If you write an unconditionally thread-safe class, consider using a private lock object in place of synchronized methods. This protects you the flexibility to adopt a more sophisticated approach to concurrency control in a latter release.
For example:
It is imperative that the user manually synchronize on the returned map when
iterating over any of its collection views:


Map<K, V> m = Collections.synchronizedMap(new HashMap<K, V>());
        ...
Set<K> s = m.keySet(); // Needn't be in synchronized block
        ...
synchronized(m) { // Synchronizing on m, not s!
    for (K key : s)
        key.f();
}

Item 71: Use lazy initialization judiciously
Lazy initialization is the act of delaying the initialization of a field until its value is needed. However, lazy initialization is a double-edged sword. It decreases the cost of initializing a class or creating an instance, at the expense of increasing the cost of accessing the lazily initialized field. Following these hits:
1).Under most circumstances, normal initialization is preferable to lazy initialization;

// Normal most circumstances of an instance field
private final FieldType field = computeFieldValue();
…

2).If you lazy initialization to break an initialization circularity, use a synchronized accessor;

// Lazy initialization of instance field - synchronized accessor
private FieldType field;

synchronized FieldType getField() {
    if(field == null)
        field = computeFieldValue();
    return field;
}
…

3).If you need to use lazy initialization for performance on a static field, use the lazy initialization holder class idiom;

// Lazy initialization holder class idiom for static fields
private static class FieldHoler {
    static final FieldType field = computeFieldValue();
}
static FieldType getField() { return FieldHolder.field; }
…

4).If you need to use lazy initialization for performance on an instance field, use the double-check idiom;

// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;
FieldType getField() {
    FieldType result = field;
    if(result == null) {  // first check (no locking)
        synchronized(this) {
            result = field;
            if(result == null)  // Second check (with locking)
                field = result = computeFieldValue();
        }
    }
    return result;
}
…

5).If you can use a variant of the double-check idiom that dispenses with the second check, which is known as single-check idiom;

// Single-check idiom - can cause repeated initialization!
private volatile FieldType field;
private FieldType getField() {
    FieldType result = field;
    if(result == null)
        field = result = computeFieldValue();
    return result;
}
…

6).If you must initialize a field lazily in order to achieve your performance goals, or to break a harmful initialization circularity, then use the appropriate lazy initialization technique;

Item 72: Don’t depend on the thread scheduler
Here’s the rules:
1).Any program that relies on the thread scheduler for correctness or performance is likely to be nonportable;
2).Threads should not run if they aren’t doing useful work;
3).Thread.yield has no testable semantics;
4).Thread priorities are among the least portable features of the Java platform;
5).You should use Thread.sleep(1) instead of Thread.yield for concurrency testing;
Here’s the busy-wait idiom, which very bad!

//Awful CountDownLatch implementation - busy-waits incessantly!
public class SlowCountDownLatch {
    private int count;
    public SlowCountDownLatch(int count) {
        if(count < 0)
            throw new IllegalArgumentException(count + "< 0");
        this.count = count;
    }

    public void await() {
        while(true) {
            synchronized(this) {
                if(count == 0) return;
            }
        }
    }

    public synchronized void countDown() {
        if(count != 0)
            count--;
    }
}///:~

Item 73: Avoid thread groups
Thread groups are obsolete, which don’t provide much in the way of useful functionality, and much of the functionality they do provide is flawed. Thread groups are best viewed as an unsuccessful experiment, and you should simply ignore their existence. If you design a class that deals with logical groups of threads, you should probably use thread pool executors.SlowCount.

Ten: Serialization
Item 74: Implement Serializable judiciously
Cost to make a class serializable can be negligible, the long-term costs are often substantial. Here’s the disadvantages:
1).A major cost of implementing Serializable is that it decreases the flexibility to change a class’s implementation once it has been released;
2).A second cost of implementing serializable is that it increases the likelihood of bugs and security holes;
3).A third cost of implementing Serializable is that it increase the testing burden associated with releasing a new version of a class;
But you need to Serializable sometime, follow these hits:
1).Implementing the Serializable interface is not a decision to be undertaken lightly;
2).Classes designed for inheritance should rarely implement Serializable, and interfaces should rarely extends it;
3).You should providing a parameterless constructor on nonserializable classes designed for inheritance;
Like this:

import java.io.*;
import java.util.concurrent.atomic.AtomicReference;

// Nonserializable stateful class allowing serializable subclass
abstract class AbstractFoo {
    private int x, y; // Our state

    // This enum and field are used to track initialization
    private enum State { NEW, INITIALIZING, INITIALIZED };
    private final AtomicReference<State> init = 
        new AtomicReference<State>(State.NEW);

    public AbstractFoo(int x, int y) { initialize(x, y); }

    // This constructor and the following method allow
    // subclass's readObject method initialize our state.
    protected AbstractFoo() {  }
    protected final void initialize(int x, int y) {
        if(!init.compareAndSet(State.NEW, State.INITIALIZING))
            throw new IllegalStateException(
                    "Already initialized");
        this.x = x;
        this.y = y;
        init.set(State.INITIALIZED);
    }

    // These methods provide access to internal state so it can
    // be manually serialized by subclass's writeObject method.
    protected final int getX() { checkInit(); return x; }
    protected final int getY() { checkInit(); return y; }
    // Must call from all public and protected instance methods
    private void checkInit() {
        if(init.get() != State.INITIALIZED)
            throw new IllegalStateException("Uninitialized");
    }
}

// Serializable subclass of nonserializable stateful class
public class Foo extends AbstractFoo implements Serializable {
    private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            // Manually deserialize and initialize superclass state
            int x = s.readInt();
            int y = s.readInt();
        }

    private void writeObject(ObjectOutputStream s)
        throws IOException {
            s.defaultWriteObject();
            //Manually serialize superclass state
            s.writeInt(getX());
            s.writeInt(getY());
        }

    // Constructor does not use the fancy mechanism
    public Foo(int x, int y) { super(x, y); }

    private static final long serialVersionUID = 1856835860954L;
}///:~

4).Inner classes should not implement Serializable, the default serialized form of an inner class is ill-defined;
To summarized, the ease of implementing Serializable is specious. Unless a class is to be thrown away after a short period of use, implementing Serializable is a serious commitment that should be made with care. Extra caution is warranted if a class is designed for inheritance. For such classes, an intermediate design point between implementing Serializable and prohibiting it in subclasses is to provide an accessible parameterless constructor. This design point permit, but does not require, subclasses to implement Serializable.

Item 75: Consider using a custom serialized form
1).Do not accept the default serialized form without first considering whether it is appropriate;
2).The default serialized form is likely to be appropriate if an object’s physical representation is identical to its logical content;
For example:

// Good condidate for default serialized form
public class Name implements Serializable {
    /**
     *Last name. Must be not-null
     *@serial
     */
    private final String lastName;

    /**
     *First name. Must be non-null
     *@serial
     */
    private final String firstName;

    /**
     *Middle name, or null if there is none.
     *@serial
     */
    private final String middleName;
}
…

3).Even if you decide that the default serialized form is appropriate, you often must provide a readObject method to ensure invariant and security;
4).Using the default serialized form when an object’s physical representation differs substantially form its logical data conent has four disadvantages:
(1).It permanently ties the exported API to the current internal representation;
(2).It can consume excessive space;
(3).It can consume excessive time;
(4).It can cause stack overflow;
5).If all instance fields are transient, it is technically permissible to dispense with invoking defaultWriteObject and defaultReadObject, but it is not recommended;
6).Before deciding to make a field nontransient, convince yourself that its value is part of the logical state of the object;
7).You must impose any synchronization on object serialization that you would impose on any other method that reads the entire state of the object;
8).Regardless of what serialized from you choose, declare an explict serial version UID in every serializable class you write;
Example of custom serialize form:

import java.io.*;

// StringList with a reasonable custom serialized form
public class StringList implements Serializable {
    private transient int size = 0;
    private transient Entry head = null;

    // No longer Serializable
    private static class Entry {
        String data;
        Entry next;
        Entry previous;
    }

    //Appends the specified string to the list
    public final void add(String s) {}

    /**
     *Serialize this {@code StringList} instance.
     *
     *@serialData The size of the list (the number of strings
     *is contains) is emitted ({@code int}), followed by all of
     *its elements (each a {@code String}), in the proper
     *sequence.
     */
    private void writeObject(ObjectOutputStream s) 
        throws IOException {
        s.defaultWriteObject();
        s.writeInt(size);

        // Write out all elements in the proper order.
        for(Entry e = head; e != null; e = e.next)
            s.writeObject(e.data);
    }

    private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        int numElements = s.readInt();

        // Read in all elements and insert them in list
        for(int i = 0; i < numElements; i++)
            add((String)s.readObject());
    }
}///:~

Item 76: Write readObject methods defensively
In summary, anytime you write a readObject method, adopt the mide-set that you are writing a public constructor that must produce a valid instance regardless of what byte stream it is given. Do not assume that the byte stream represents an actual serialized instance. Here, in summary form, are the guidelines for writing a bulletproof readObject method:
1).For classes with object reference fields that must remain private, defensively copy each object in such a field. Mutable components of immutable classes fall into this category;
2).Check any invariants and throw an InvalidObjectException if a check fails. The checks should follow any defensively copying;
3).If an entire object graph must be validated after it is deserialized, use the objectInputValidation interface;
4).Do not invoke any overridable methods in the class, directly or indirectly;
Here’s the example come from item29:

//Broken "immutable" time period class
class Period {
    private final Date start;
    private final Date end;

    /**
     *@param start the beginning of the period
     *@param end the end of the period; must not precede start
     *@throws IllegalArgumentException if start is after end
     *@throws NullPointerException if start or end is null
     */

    /*
    public Period (Date start, Date end) {
        if (start.compareTo(end) > 0)
            throw new IllegalArgumentException(
                    start + " after " + end);
        this.start = start;
        this.end = end;
    }*/ 

    //Repaired constructor - make defensive copies of parameters
    public Period (Date start, Date end) {
        this.start = new Date(start.getTime());
        this.end = new Date(end.getTime());
        if (this.start.compareTo(this.end) > 0)
            throw new IllegalArgumentException(
                    start + " after " + end);
    }

    /*
    public Date start() {
        return start;
    }

    public Date end() {
        return end;
    }*/

    //Repaired accessors - make defensive copies of internal fields
    public Date start() {
        return new Date(start.getTime());
    }

    public Date end() {
        return new Date(end.getTime());
    }

}
…

To make readObject mehod defensively, which can prevent being attacked:

 // readObject method with defensive copying and validity checking
    private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        s.defaultReadObject();

        // Defensively copy our mutable components
        start = new Date(start.getTime());
        end = new Date(end.getTime());

        // Check that our invariants are satisfied
        if(start.compareTo(end) > 0)
            throw new InvalidObjectException(start + "after" + end);
    }

Item 77: For instance control, prefer enum types to readResolve
If you depend on readResolve for instance control, all instance fields with object reference types must be declared transient. The accessibility of readResolve is significant.
To summarized, you should use enum types to enforce instance control invariants whether possible. If this is not possible and you need a class to be both serializable and instance-controlled, you must provide a readResolve method and ensure that all of the class’s instance fields are either primitive or transient.
For example, the class Elvis can be attacked by ElvisStealer:

import java.io.Serializable;
import java.io.ObjectStreamException;
import java.util.Arrays;

// Broken singleton - has nontransient object reference field!
public class Elvis implements Serializable {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {}

    private String[] favoriteSongs = 
    { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }

    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }
}

class ElvisStealer implements Serializable {
    static Elvis impersonator;
    private Elvis payload;

    private Object readResolve() {
        // Save a reference to the "unresolved" Elvis instance
        impersonator = payload;

        //Return an object of correct type for favorites field
        return new String[] { "A Fool Such As I" };
    }
    private static final long serialVersionUID = 0;
}///:~

To fix the attack, we use enum type:

// Enum singleton - the preferred approach
public enum Elvis {
    INSTANCE;
    private String[] favoriteSongs = 
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}
…

Item 78: Consider serialization proxies instead of serialized
The advantages of serialization proxy:
1).Stopping the bogus byte-stream attack and the internal field theft attack dead in their tracks;
2).Allowing the deserialized instance to have a different class from the originally serialized instance;
The disadvantages of serialization proxy:
1).It is not compatible with classes that are extendable by their clients. Also, it is not compatible with some classes whose object graphs contain circularities;
2).The added power and safety of the serialization proxy pattern are not free.
In summary, consider the serialization proxy pattern whenever you find yourself having to write a readObject or writeObject method on a class that is not extendable by its clients. This pattern is perhaps the easiest way to robustly serialize objects with nontrivial invariants.
Talk is cheap, show you the code:
1).

//Serialization proxy for Peroid class
private static class SerializationProxy implements Serializable {
    private final Date start;
    private final Date end;

    SerializationProxy(Period p) {
        this.start = p.start;
        this.end = p.end;
    }

    // writeReplace method for the serialization proxy pattern
    private Object writeReplace() {
        return new SerializationProxy(this);
    }

    // readReplace method for the serialization proxy pattern
    private void readObject(ObjectInputStream stream)
        throws InvalidObjectException {
        throw new InvalidObjectException("Proxy required"); 
    }

    // readResolve method for Period.SerializationProxy
    private Object readResolve() {
        return new Period(start, end);
    }

    private static final long serialVersionUID =
        23409824823485285L;
}
…

2).

// EnumSet's serialization proxy
private static class EnumSetSerializationProxy <E extends Enum<E>>
    implements Serializable {
    // The element type of this enum set.
    private final Class<E> elementType;

    // The elements contained in this enum set
    private final Enum[] elements;

    SerializationProxy(EnumSet<E> set) {
        elementType = set.elementType;
        elements = set.toArray(EMPTY_ENUM_ARRAY);
    }

    private Object readResolve() {
        EnumSet<E> result = EnumSet.noneOf(elementType);
        for(Enum e : elements)
            result.add((E)e);
        return result;
    }
    private static final long serialVersionUID =
        362491234563181265L;
}
…
Your feedback is requested on changes under consideration for SLF4J version 2.1.0. SLF4J warning or error messages and their meanings No SLF4J providers were found. This message is a warning and not an error. It is reported when no SLF4J providers could be found on the class path. SLF4J requires a logging provider because it is a logging API and not an logging implementation. Placing one (and only one) of the many available providers such as slf4j-nop.jar slf4j-simple.jar, slf4j-reload4j.jar, slf4j-jdk14.jar or logback-classic.jar on the class path will solve the problem. In the absence of a provider, SLF4J will default to a no-operation (NOP) logger provider. Please note that slf4j-api version 2.0.x and later use the ServiceLoader mechanism. Earlier versions relied on the static binder mechanism which is no longer honored by slf4j-api. Please read the FAQ entry What has changed in SLF4J version 2.0.0? for further important details. If you are responsible for packaging an application and do not care about logging, then placing slf4j-nop.jar on the class path of your application will get rid of this warning message. Note that embedded components such as libraries or frameworks should not declare a dependency on any SLF4J providers but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J provider, it imposes that provider on the end-user, thus negating SLF4J's purpose. Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier Planning for the advent of Jigsaw (Java 9), slf4j-api version 2.0.x and later use the ServiceLoader mechanism. Earlier versions of SLF4J relied on the static binder mechanism which is no longer honored by slf4j-api version 2.0.x. In case SLF4J 2.x finds no providers targeting SLF4J 2.x but finds instead bindings targeting SLF4J 1.7 or earlier, it will list the bindings it finds but otherwise will ignore them. This can be solved by placing an SLF4J provider on your classpath, such providers include logback version 1.3.x and later, as well as one of slf4j-reload4j, slf4j-jdk14, slf4j-simple version 2.0.0 or later. See also the FAQ entry What has changed in SLF4J version 2.0.0? for further important details. IllegalStateException: org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. This IllegalStateException is thrown post-initialization and informs the user that initialization of LoggerFactory has failed. Note It is important to realize that the exception causing the failure was thrown at an earlier stage. This earlier exception should provide more valuable information about the root cause of the problem. The method o.a.commons.logging.impl.SLF4FLogFactory#release was invoked. Given the structure of the commons-logging API, in particular as implemented by SLF4J, the o.a.commons.logging.impl.SLF4FLogFactory#release() method should never be called. However, depending on the deployment of commons-logging.jar files in your servlet container, release() method may be unexpectedly invoked by a copy of org.apache.commons.logging.LogFactory class shipping with commons-logging.jar. This is a relatively common occurrence with recent versions of Tomcat, especially if you place jcl-over-slf4j.jar in WEB-INF/lib directory of your web-application instead of $TOMCAT_HOME/common/lib, where $TOMCAT_HOME stands for the directory where Tomcat is installed. In order to fully benefit from the stability offered by jcl-over-slf4j.jar, we recommend that you place jcl-over-slf4j.jar in $TOMCAT_HOME/common/lib without placing a copy in your web-applications. Please also see bug #22. Operation [suchAndSuch] is not supported in jcl-over-slf4j. An UnsupportedOperationException is thrown whenever one of the protected methods introduced in JCL 1.1 are invoked. These methods are invoked by LogFactory implementations shipping with commons-logging.jar. However, the LogFactory implemented by jcl-over-slf4j.jar, namely SLF4FLogFactory, does not call any of these methods. If you observe this problem, then it is highly probable that you have a copy of commons-logging.jar in your class path overriding the classes shipping with jcl-over-slf4j.jar. Note that this issue is very similar in nature to the warning issued when the "o.a.commons.logging.impl.SLF4FLogFactory.release()" method is invoked, discussed in the previous item. Detected logger name mismatch Logger name mismatch warnings are printed only if the slf4j.detectLoggerNameMismatch system property is set to true. By default, this property is not set and no warnings will be printed even in case of a logger name mismatch. since 1.7.9 The warning will be printed in case the name of the logger specified via a class passed as an argument to the LoggerFactory.getLogger(Class) method differs from the name of the caller as computed internally by SLF4J. For example, the following code snippet package com.acme; import com.foo.Kangaroo; class Fruit { Logger logger = LoggerFactory.getLogger(Kangaroo.class); } will result in the warning SLF4J: Detected logger name mismatch. Given name: "com.foo.Kangaroo"; computed name: "com.acme.Fruit". but only if slf4j.detectLoggerNameMismatch system property is set to true. No warning will be issued for the special case where the class in which the logger is defined is a super-type of the class parameter passed as argument. For example, class A { Logger logger = LoggerFactory.getLogger(getClass()); } class B extends A { // no mismatch warning will be issued when B is instantiated // given that class A is a super-type of class B } If you come across a mismatch warning which cannot be explained, then you might have spotted a white elephant, that is a very rare occurrence where SLF4J cannot correctly compute the name of the class where a logger is defined. We are very interested to learn about such cases. If and when you spot an inexplicable mismatch, please do file a bug report with us. Failed to load class org.slf4j.impl.StaticLoggerBinder This warning message is reported by slf4j-api version 1.7.x and earlier when the org.slf4j.impl.StaticLoggerBinder class could not be loaded into memory. This happens when no appropriate SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem. If you are seeing this message, then you are NOT using slf4j-api version 2.0 or later but slf4j-api version 1.7.x or earlier. Slf4j-api versions 2.0.x and later use the ServiceLoader mechanism. Backends such as logback 1.3 and later which target slf4j-api 2.x, do not ship with org.slf4j.impl.StaticLoggerBinder. If you place a logging backend which targets slf4j-api 2.0.x, you need slf4j-api-2.x.jar on the classpath. See also relevant faq entry. since 1.6.0 As of SLF4J version 1.6, in the absence of a binding, SLF4J will default to a no-operation (NOP) logger implementation. If you are responsible for packaging an application and do not care about logging, then placing slf4j-nop.jar on the class path of your application will get rid of this warning message. Note that embedded components such as libraries or frameworks should not declare a dependency on any SLF4J binding (or provider) but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J binding (or provider), it imposes that binding (or provider) on the end-user, thus negating SLF4J's purpose. Multiple bindings were found on the class path SLF4J API is designed to bind with one and only one underlying logging framework at a time. If more than one binding is present on the class path, SLF4J will emit a warning, listing the location of those bindings. When multiple bindings are available on the class path, select one and only one binding you wish to use, and remove the other bindings. For example, if you have both slf4j-simple-2.0.17.jar and slf4j-nop-2.0.17.jar on the class path and you wish to use the nop (no-operation) binding, then remove slf4j-simple-2.0.17.jar from the class path. The list of locations that SLF4J provides in this warning usually provides sufficient information to identify the dependency transitively pulling in an unwanted SLF4J binding into your project. In your project's pom.xml file, exclude this SLF4J binding when declaring the unscrupulous dependency. For example, cassandra-all version 0.8.1 declares both log4j and slf4j-log4j12 as compile-time dependencies. Thus, when you include cassandra-all as a dependency in your project, the cassandra-all declaration will cause both slf4j-log4j12.jar and log4j.jar to be pulled in as dependencies. In case you do not wish to use log4j as the SLF4J backend, you can instruct Maven to exclude these two artifacts as shown next: <dependencies> <dependency> <groupId> org.apache.cassandra</groupId> <artifactId>cassandra-all</artifactId> <version>0.8.1</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency> </dependencies> Note The warning emitted by SLF4J is just that, a warning. Even when multiple bindings are present, SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random. As of version 1.6.6, SLF4J will name the framework/implementation class it is actually bound to. Embedded components such as libraries or frameworks should not declare a dependency on any SLF4J binding but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J binding, it imposes that binding on the end-user, thus negating SLF4J's purpose. When you come across an embedded component declaring a compile-time dependency on any SLF4J binding, please take the time to contact the authors of said component/library and kindly ask them to mend their ways. slf4j-api version does not match that of the binding An SLF4J binding designates an artifact such as slf4j-jdk14.jar or slf4j-log4j12.jar used to bind slf4j to an underlying logging framework, say, java.util.logging and respectively log4j. Mixing different versions of slf4j-api.jar and SLF4J binding (a.k.a. provider since 2.0.0) can cause problems. For example, if you are using slf4j-api-2.0.17.jar, then you should also use slf4j-simple-2.0.17.jar, using slf4j-simple-1.5.5.jar will not work. Note From the client's perspective all versions of slf4j-api are compatible. Client code compiled with slf4j-api-N.jar will run perfectly fine with slf4j-api-M.jar for any N and M. You only need to ensure that the version of your binding matches that of the slf4j-api.jar. You do not have to worry about the version of slf4j-api.jar used by a given dependency in your project. You can always use any version of slf4j-api.jar, and as long as the version of slf4j-api.jar and its binding match, you should be fine. At initialization time, if SLF4J suspects that there may be an api vs. binding version mismatch problem, it will emit a warning about the suspected mismatch. Logging factory implementation cannot be null This error is reported when the LoggerFactory class could not find an appropriate binding. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should prove to be an effective remedy. Detected both log4j-over-slf4j.jar AND slf4j-reload4j on the class path, preempting StackOverflowError. The purpose of slf4j-reload4j module is to delegate or redirect calls made to an SLF4J logger to log4j/reload4j. The purpose of the log4j-over-slf4j module is to redirect calls made to a log4j logger to SLF4J. If SLF4J is bound withslf4j-reload4j.jar and log4j-over-slf4j.jar is also present on the class path, a StackOverflowError will inevitably occur immediately after the first invocation of an SLF4J or a log4j logger. Here is how the exception might look like: Exception in thread "main" java.lang.StackOverflowError at java.util.Hashtable.containsKey(Hashtable.java:306) at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:36) at org.apache.log4j.LogManager.getLogger(LogManager.java:39) at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:73) at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249) at org.apache.log4j.Category.<init>(Category.java:53) at org.apache.log4j.Logger..<init>(Logger.java:35) at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:39) at org.apache.log4j.LogManager.getLogger(LogManager.java:39) at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:73) at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249) at org.apache.log4j.Category..<init>(Category.java:53) at org.apache.log4j.Logger..<init>(Logger.java:35) at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:39) at org.apache.log4j.LogManager.getLogger(LogManager.java:39) subsequent lines omitted... Since 1.5.11 SLF4J software preempts the inevitable stack overflow error by throwing an exception with details about the actual cause of the problem. This is deemed to be better than leaving the user wondering about the reasons of the StackOverflowError. Note that since reload4j provides the log4j 1.x API, reload4j.jar and log4j-over-slf4j.jar cannot be present simultaneously on your classpath. For more background on this topic see Bridging legacy APIs. Detected both jcl-over-slf4j.jar AND slf4j-jcl.jar on the class path, preempting StackOverflowError. The purpose of slf4j-jcl module is to delegate or redirect calls made to an SLF4J logger to jakarta commons logging (JCL). The purpose of the jcl-over-slf4j module is to redirect calls made to a JCL logger to SLF4J. If SLF4J is bound with slf4j-jcl.jar and jcl-over-slf4j.jar is also present on the class path, then a StackOverflowError will inevitably occur immediately after the first invocation of an SLF4J or a JCL logger. Here is how the exception might look like: Exception in thread "main" java.lang.StackOverflowError at java.lang.String.hashCode(String.java:1482) at java.util.HashMap.get(HashMap.java:300) at org.slf4j.impl.JCLLoggerFactory.getLogger(JCLLoggerFactory.java:67) at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249) at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:155) at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:289) at org.slf4j.impl.JCLLoggerFactory.getLogger(JCLLoggerFactory.java:69) at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249) at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:155) subsequent lines omitted... Since 1.5.11 SLF4J software preempts the inevitable stack overflow error by throwing an exception with details about the actual cause of the problem. This is deemed to be better than leaving the user wondering about the reasons of the StackOverflowError. For more background on this topic see Bridging legacy APIs. Exception in thread "main" java.lang.NoSuchFieldError: tlm Exception in thread "main" java.lang.NoSuchFieldError: tlm at org.apache.log4j.MDCFriend.fixForJava9(MDCFriend.java:11) at org.slf4j.impl.Log4jMDCAdapter.(Log4jMDCAdapter.java:38) at Main.main(Main.java:5) The NoSuchFieldError is thrown when slf4j-log4j12 attempts to access the 'tlm' package private field in org.apache.log4j.MDC which was specified as being of type java.lang.Object in log4j 1.2.x but was changed to java.lang.ThreadLocal in reload4j. Moreover, such access to package private fields from different modules is not authorized by default in modularized applications in Java 9 and later. To keep a long story short, the NoSuchFieldError can be avoided by using slf4j-reload4j.jar with reload4j.jar. Stated differently, org.slf4j.MDC cannot be used with the slf4j-log4j12.jar and reload4j.jar combination. Update: The issue described above was fixed in reload4j 1.2.21. Although it is still recommended that you use slf4j-reload4j as the preferred adapter for the slf4j/reload4j combination, with reload4j version 1.2.21 and later you can freely mix any version of slf4j-log4j12, if you need to. Failed to load class "org.slf4j.impl.StaticMDCBinder" This error indicates that appropriate SLF4J binding could not be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem. MDCAdapter cannot be null This error is reported when org.slf4j.MDC class has not been initialized correctly. Same cause and remedy as the previously listed item. A number (N) of logging calls during the initialization phase have been intercepted and are now being replayed. These are subject to the filtering rules of the underlying logging system. since 1.7.15 Logging calls made during the initialization phase are recorded and replayed post-initialization. Note that the replayed logging calls are subject to filtering by the underlying logging system. In principle, replaying only occurs for applications which are already multithreaded at the time the first logging call occurs. See also substitute loggers. Substitute loggers were created during the default configuration phase of the underlying logging system Highly configurable logging systems such as logback and log4j may create components which invoke loggers during their own initialization. See issue LOGBACK-127 for a typical occurrence. However, since the binding process with SLF4J has not yet completed (because the underlying logging system was not yet completely loaded into memory), it is not possible to honor such logger creation requests. To avoid this chicken-and-egg problem, SLF4J creates substitute loggers during this phase (initialization). Calls made to the substitute loggers during this phase are simply dropped. After the initialization completes, the substitute logger will delegate logging calls to the appropriate logger implementation and otherwise will function as any other logger returned by LoggerFactory. If any substitute logger had to be created, SLF4J will emit a listing of such loggers. This list is intended to let you know that any logging calls made to these loggers during initialization have been dropped. See also intercepted and replayed logging calls. SLF4J versions 1.4.0 and later requires log4j 1.2.12 or later The trace level was added to log4j in version 1.2.12 released on August 29, 2005. The trace level was added to the SLF4J API in version 1.4.0 on May 16th, 2007. Thus, starting with SLF4J 1.4.0, the log4j binding for SLF4J requires log4j version 1.2.12 or above. However, as reported in issue 59, in some environments it may be difficult to upgrade the log4j version. To accommodate such circumstances, SLF4J's Log4jLoggerAdapter will map the TRACE level as DEBUG. java.lang.NoClassDefFoundError: org/slf4j/event/LoggingEvent Logback-classic version 1.1.4 and later require slf4j-api version 1.7.15 or later. With an earlier slf4j-api.jar in the classpath, attempting introspection of a Logger instance returned by logback version 1.1.4 or later will result in a NoClassDefFoundError similar to that shown below. Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/event/LoggingEvent at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2451) at java.lang.Class.privateGetPublicMethods(Class.java:2571) at java.lang.Class.getMethods(Class.java:1429) at java.beans.Introspector.getPublicDeclaredMethods(Introspector.java:1261) at java.beans.Introspector.getTargetMethodInfo(Introspector.java:1122) at java.beans.Introspector.getBeanInfo(Introspector.java:414) at java.beans.Introspector.getBeanInfo(Introspector.java:161) Placing slf4j-api.jar version 1.7.15 or later in the classpath should solve the issue. Note that this problem only occurs with logback version 1.1.4 and later, other bindings such as slf4j-log4j, slf4j-jdk14 and slf4j-simple are unaffected.
最新发布
09-24
### 常见 SLF4J 警告和错误消息及解决办法 #### 1. `No SLF4J providers were found` - **含义**:在类路径中找不到 SLF4J 提供程序。SLF4J 是日志记录 API,需要日志记录提供程序来实现具体的日志功能[^2]。 - **解决办法**: - 更换 slf4j 版本,使用 1.8.0 之前的版本,例如在 Maven 项目的 `pom.xml` 中修改依赖: ```xml <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> </dependency> ``` - 放置一个(且仅放置一个)在众多可用提供商中,例如 `slf4j-nop.jar`、`slf4j-simple.jar`、`slf4j-reload4j.jar`、`slf4j-jdk14.jar` 或 `logback-classic.jar` 到类路径中 [^2]。 #### 2. `Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier` - **含义**:类路径中存在针对 slf4j-api 1.7.x 或更早版本的 SLF4J 绑定,可能会导致版本不兼容问题。 - **解决办法**:去掉不需要的依赖。例如在 `pom.xml` 中查找并删除多余的依赖,如 `slf4j-log4j12`: ```xml <!-- 删除以下依赖 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.1</version> </dependency> ``` #### 3. `Class path contains multiple SLF4J providers` - **含义**:类路径中包含多个 SLF4J 提供程序,SLF4J 会随机选取一个日志框架/实现进行绑定 [^1][^3]。 - **解决办法**:检查并移除多余的 SLF4J 提供程序依赖。例如在 `pom.xml` 中只保留需要的日志实现依赖,移除其他不必要的实现依赖。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值