Effective Java(Item: 13 to 22)

Java编程最佳实践
本文探讨了Java编程中的一系列最佳实践,包括最小化类和成员的可访问性、使用访问器方法而非公共字段、减少可变性、优选组合而非继承等关键原则。通过实例展示了如何实现不可变对象、正确设计可继承的类以及如何优选接口和抽象类。

Three:Class and Interfaces
Item 13: Minimize the accessibility of classes and members
The rule is simple: make each class or member as inaccessible as possible. This concept, knows as information hiding or encapsulation.. For members (fields, methods, nested classes and nested interfaces), there are four possible access levels, listed here in order of increasing accessibility:
1).private-The member is accessible only from the top-level class where it is declared;
2).package-private-The member is accessible from any class in the package where it is declared, also known as default access;
3).protected-The member is accessible from subclasses of the class where it is declared;
4).public-The member is accessible from anywhere;
And for minimize the accessibility, you need yo be awared the rules:
1).Instance fields should never be public;
2).Classes with mutable fields are not thread-safe
3).It is wrong for a class to have a public static array field, or an accessor that returns such a field;

Item 14: In public classes, use accessor methods, not public fields
I think you have already know a lot programs like setter and getter, what does that mean?
Remember that if a class is accessible outside its package, provide accessor methods, do not expose fields directly.
In summary, public classes should never expose mutable fields. It is less harmful, though still questionable, for public classes to expose immutable fields. It is, however, sometimes desirable for package-private or private nested classes to expose fields, whether mutable or immutable.
Here is a simple example about Item 13 and Item 14 :

class Point{

    /*wrong
    public double x;
    public double y;
    public static final Thing[] VALUES={1,2,3,4};
    */

    private double x;
    private double y;
    private static final Integer[] VALUES={1,2,3,4};

    public Point(double x, double y){
        this.x=x;
        this.y=y;
    } 

    public double getX(){return x;}
    public double getY(){return y;}

    public void setX(double x){this.x=x;}
    public void setY(double y){this.y=y;}

    public static final Integer[] getValues(){
        return VALUES.clone();
    }

}

Item 15: Minimize mutability
To make a class immutable, follow these five rules:
1).Don’t provide any methods that modify the object’s state;
2).Ensure that the class can’t be executes;
3).Make all fields final;
4).Make all fields private;
5).Ensure exclusive access to any mutable components;
Why would you do that?
1).Immutable objects are simple;
2).Immutable objects are inherently thread-safe;
3).Immutable objects can be shared freely;
4).Immutable objects make great building blocks for other objects;
But you need to be clearly that the only disadvantage of immutable classes is that they require a separate object for each distinct value.
To summarize, resist the urge to write a set method for every get method. Classes should be immutable unless there’s a very good reason to make them mutable. If a class cannot be make immutable, limit its mutability as mush as possible. Trying to make every field final unless there is a compelling reason to it nonfinal.
Example:

public class MinimizeMutability{

    public static void main(String[] args){
        Complex c = Complex.valueOf(1.1, 2.2);
        System.out.println(c);
        c=c.add(c);
        System.out.println(c);
        c=c.subtract(c);
        System.out.println(c);
    }
}

final class Complex{

    private final double re;
    private final double im;

    private Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public static Complex valueOf(double re, double im) {
        return new Complex(re,im);
    }

    //Accessors with no corresponding mutators
    public double realPart() {return re;}
    public double imaginaryPart() {return im;}

    public Complex add(Complex c) {
        return valueOf(re+c.re, im+c.im);
    }

    public Complex subtract(Complex c) {
        return valueOf(re-c.re, im-c.im);
    }

    public Complex multiply(Complex c) {
        return valueOf(re * c.re - im * c.im,
                re * c.im + im * c.im);
    }

    public Complex divide(Complex c) {
        double tmp = c.re * c.re + c.im * c.im;
        return valueOf((re * c.re + im * c.im) / tmp,
                (im * c.re - re * c.im) / tmp);
    }

    @Override
    public boolean equals(Object o) {
        if(o == this)
            return true;
        if(!(o instanceof Complex))
            return false;
        Complex c = (Complex)o;
        return Double.compare(re, c.re) == 0 &&
            Double.compare(im, c.im) == 0;
    }

    @Override
    public int hashCode() {
        int result = 17 + hashDouble(re);
        result = 31 * result + hashDouble(im);
        return result;
    }

    private int hashDouble(double val) {
        long longBits = Double.doubleToLongBits(re);
        return (int)(longBits ^ (longBits >>> 32));
    }

    @Override
    public String toString() {
        return "("+ re + "+" + im + ")";
    }
}/*Output:
(1.1+2.2)
(2.2+4.4)
(0.0+0.0)
*///:~ 

Item 16: Favor composition over inheritance
Inheritance can lead to fragile software and violate encapsulation. To fix the problem that inheritance have bring, A powerful design called composition come out, which make the existing class becomes a component of the new one. Each instance method in the new class invokes the corresponding method on the contained instance of the existing class and return the results. This is knows as forwarding, and the methods in the new class are known as forwarding methods. The resulting class class will be rock soild, with no dependencies on the implementation details of the existing class. Even adding new methods to the existing class will have no impact on the new class.
Of course, inheritance is appropriate only in circumstances where the subclass really is a subtype of the superclass. Inheritance is a “is-a” relationship.
Example:

import java.util.*;
public class CompositionVSInheritance {

    public static void main(String[] args) {

        InstrumentedHashSet<String> s=
            new InstrumentedHashSet<String>();
        s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
        System.out.println("(Inheritance)AddCount of InstrumentedHashSet is-->"+s.getAddCount());
        //composition is flexible
        InstrumentedHashSet2<String> s2 = 
            new InstrumentedHashSet2<String>(new HashSet<String>());
        s2.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
        System.out.println("(Composition)AddCount of InstrumentedHashSet2 is-->"+s2.getAddCount());
        Set<Integer> s3 = new InstrumentedHashSet2<Integer>(new TreeSet<Integer>());
        System.out.println("composition is flexible!");
    }
}

//Broken - Inappropriate use of inheritance!
class InstrumentedHashSet<E> extends HashSet<E> {
    private int addCount = 0;

    public InstrumentedHashSet() {
    }

    public InstrumentedHashSet(int initCap, float loadFactor) {
        super(initCap, loadFactor);
    }

    @Override
    public boolean add(E e) {
        addCount++;
        System.out.println("call me!-->InstrumentedHashSet.add()");
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        System.out.println("call me!-->InstrumentedHashSet.addAll()");
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}

//Wapper class - uses composition in place of inheritance
class InstrumentedHashSet2<E> extends ForwardingSet<E> {
    private int addCount = 0;

    public InstrumentedHashSet2(Set<E> s) {
        super(s);
    }

    @Override
    public boolean add(E e) {
        addCount++;
        System.out.println("call me!-->InstrumentedHashSet2.add()");
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        System.out.println("call me!-->InstrumentedHashSet2.addAll()");
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}

//Reusable forwarding class
class ForwardingSet<E> implements Set<E> {
    private final Set<E> s;
    public ForwardingSet(Set<E> s) {this.s = s; }
    public void clear() {s.clear();}
    public boolean contains(Object o) {return s.contains(o); }
    public boolean isEmpty() {return s.isEmpty(); }
    public int size() {return s.size(); }
    public Iterator<E> iterator() {return s.iterator(); }
    public boolean add(E e)
    {
        System.out.println("call me!-->ForwardingSet.add()");
        return s.add(e); 
    }
    public boolean remove(Object o) {return s.remove(o); }
    public boolean containsAll(Collection<?> c)
    {return s.containsAll(c); }
    public boolean addAll(Collection<? extends E> c)
    {
        System.out.println("call me!-->ForwardingSet.addAll()");
        return s.addAll(c);
    }
    public boolean removeAll(Collection<?> c)
    {return s.removeAll(c); }
    public boolean retainAll(Collection<?> c)
    {return s.retainAll(c); }
    public Object[] toArray() {return s.toArray(); }
    public <T> T[] toArray(T[] a) {return s.toArray(a); }
    @Override
    public boolean equals(Object o) {return s.equals(o); }
    @Override
    public int hashCode() {return s.hashCode(); }
    @Override
    public String toString() {return s.toString(); }
}/*Output:
call me!-->InstrumentedHashSet.addAll()
call me!-->InstrumentedHashSet.add()
call me!-->InstrumentedHashSet.add()
call me!-->InstrumentedHashSet.add()
(Inheritance)AddCount of InstrumentedHashSet is-->6
call me!-->InstrumentedHashSet2.addAll()
call me!-->ForwardingSet.addAll()
(Composition)AddCount of InstrumentedHashSet2 is-->3
composition is flexible!
*///:~

Item 17: Design and document for inheritance or else prohibit it
1).A class which designed and documented for inheritance must document its selff-use of overridable methods;
2).The only way to test a class designed for inheritance is to write subclasses;
3).You must test your class by writing subclasses before you release it;
4).Constructors must not invoke overridable methods;
5).Neither clone nor readObject may invoke an overridable method, directly or indirectly;
6).Designing a class for inheritance places substantial limitation on the class;
7).Prohibiting subclassing in class that are not designed and documented to be safely subclassed, you can declare the class final or add public static factories in place of the constructors;
8).If you feel that you must allow inheritance from such a class, one reasonable approach is to ensure that the class never invokes any of its overridable methods and to document this fact;
Example:

import java.util.Date;

public class ProhibitInheritance {

    /*
     * The overrideMe method is invoked by the Super constructor before
     * the Sub constructor has a chance to initialize the date field.
     * Note that this program observes a final field in two different states!
     */
    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.overrideMe();
    }
}

class Super {
    //Broken - constructor invokes an overridable method
    public Super() {
        overrideMe();
    }
    public void overrideMe() {
        System.out.println("I won't be call for i have be overrided!");
    }
}

final class Sub extends Super {
    private final Date date;
    public Sub() {
        date = new Date();
    }
    //Overriding method invoked by i constructor
    @Override
    public void overrideMe() {
        System.out.println(date);
    }
}/*Output:
null
Mon Oct 10 20:28:14 CST 2016
*///:~

Item 18: Prefer inheritance to abstract classes
1).Existing classes can be easily retrofitted to implement a new interface;
2).Interfaces are ideal for defining mixins;
3).Interface allow the construction of nonhierarchical type frameworks;
4).Interfaces enable safe, powerful functionality enhancements via the wrapper class idiom;
5).You can combine the virtues of interfaces and abstract classes by providing an abstract skeletal implementation class to go with each nontrivial interface that you export;
6).It is far easier to evolve an abstract class than an interface;
7).Once an interface is released and widely implemented, it is almost impossible to change;

public class AbstractSkeletal {
}

//Let's see the primitive code
//Abstract class
abstract class AbrBase {
    abstract void a();
    abstract void b();
}

class Sub1 extends AbrBase {
    public void a() {}
    public void b() {}
} 

//Interface
interface IBase{
    public void c();
    public void d();
}

class Sub2 implements IBase {
    public void c() {}
    public void d() {}
}

//Now we need to add some method in Abstract class and Interface
//Enhance Abstract
abstract class EnhanceAbrBase {
    abstract void a();
    abstract void b();
    //add a new method
    public void e() {}
}

class EnhanceSub1 extends EnhanceAbrBase {
    public void a() {}
    public void b() {}
    /*
    *Abstract don't need to be changed.
    *this is the only adventage compare to interface.
    */
} 

//Enhance Interface
interface EnhanceIBase{
    public void c();
    public void d();
    //add a new method
    public void f();
}

class EnhanceSub2 implements EnhanceIBase {
    public void c() {}
    public void d() {}
    //Subclass must implement the new method of SuperClass
    //And this is not good. We need to fix it.
    public void f() {}
}

//We use skeletal implementation to conjunc Abstract and Interface
interface SkeletalIBase {
    public void c();
    public void d();
}

abstract class SkeletalAbrBase implements SkeletalIBase {
    //primitive
    abstract void a();
    abstract void b();

    //implements the method of SkeletalIBase interface
    public void c() {}
    public void d() {}
}

class SkeletalSub extends SkeletalAbrBase {
    public void a() {}
    public void b() {}
}

//Now we add some method in Skeletal implementation
interface EnhanceSkeletalIBase {
    public void c();
    public void d();
    //add a new method
    public void f();
}

abstract class EnhanceSkeletalAbrBase implements EnhanceSkeletalIBase {
    //primitive
    abstract void a();
    abstract void b();

    //implements the method of EnhanceSkeletalIBase interface
    public void c() {}
    public void d() {}
    //implement the new method
    public void f() {}
}

/*
* you don't need to change anything
*/
class EnhanceSkeletalSub extends EnhanceSkeletalAbrBase {
    public void a() {}
    public void b() {}
}

Item 19: Use interface only to define types
People always use constant interface pattern sometimes, which is a poor use of interface. If in a future release the class is modified so that it no longer needs to use the constant, it still must implement the interface to ensure binary compatibility. If a nonfinal class implement a constant interface, all of its subclasses will have their namespaces polluted by the constant in the interface.
In summary, interfaces should be used only to defind typed. They should not be used to export constants.
For example:

public class InterfaceConstant{
    public static void main(String[] args) {
        System.out.println(PhysicalConstants.AVOGADROS_NUMBER);
    }
}
/*Contants interface antipattern - do not use!
interface PhysicalConstants {
    static final double AVOGADROS_NUMBER = 6.02214199e26;
    static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
    static final double ELECTRON_MASS = 9.10938188e-31;
}
*/
class PhysicalConstants {
    private PhysicalConstants() {} 
    static final double AVOGADROS_NUMBER = 6.02214199e26;
    static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
    static final double ELECTRON_MASS = 9.10938188e-31;
}/*Output:
6.02214199E26
*///:~

Item 20: Prefer class hierarchies to tagged classes
The disadvantage of tagged class:
1).Read-ability is future harmed because multiple implementations are jumbled together in a single class;
2). Memory footprint is increased because instances are burdened with inelevant fields belonging to other flavors;
So we can make a conclusion that tagged class are verbose, error-prrone, and inefficient. A tagged class is just a pallid imitation of a class hierarchy.
The advantage of hierarchies:
1).Hierarchies corrects every shortcoming of tagged classes noted previously;
2).Hierarchies can be make to reflect natural hierarchies relationships among types, allowing for increased flexibility and better compile-time type checking;
Show you the code:

public class TaggedClass {

    public static void main(String[] args) {
        Figure f = new Figure(4.4);
        System.out.println(f.area());
        Figure ff = new Figure(2.2, 1.1);
        System.out.println(ff.area());
    } 
}
class Figure {
    enum Shape { RECTANGLE, CIRCLE };
    //Tag field - the shape of this figure
    final Shape shape;
    //These fields are used only if shape is RECTANGLE
    double length;
    double width;
    //This field is used only if shape is CIRCLE
    double radius;
    //Constructor for circle
    Figure(double radius) {
        shape = Shape.CIRCLE;
        this.radius = radius;
    }
    //Constructor for rectangle
    Figure(double length, double width) {
        shape = Shape.RECTANGLE;
        this.length = length;
        this.width = width;
    }
    double area() {
        switch(shape) {
            case RECTANGLE:
                return length * width;
            case CIRCLE:
                return Math.PI * (radius * radius);
            default:
                throw new RuntimeException();
        }
    }
}/*Output:
60.821233773498406
2.4200000000000004
*///:~
public class ClassHierarchies {
    public static void main(String[] args) {
        Figure f = new Circle(4.4);
        System.out.println(f.area());
        Figure ff = new Rectangle(2.2, 1.1);
        System.out.println(ff.area());
    }
}

abstract class Figure {
    abstract double area();
}

class Circle extends Figure {
    final double radius;
    Circle(double radius) { this.radius = radius; }
    double area() { return Math.PI * (radius * radius); }
}

class Rectangle extends Figure {
    final double length;
    final double width;
    Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
    double area() { return length * width; }
}/*Output:
60.821233773498406
2.4200000000000004
*///:~  

Item 21: Use function objects to represent strategies
To summarize, a primary use of function pointers is to implement the Strategy pattern. To implement this pattern in Java, declare an interface to represent the strategy, and a class that implements this interface for each concrete strategy. When a concrete strategy is used only once, it is typically declare and instantiated as an anonymous class. When a concrete strategy is designed for represent use, it is generally implemented as a private static member class and exported in a public static final field whose type is the strategy interface.

Item 22: Favors static member class over nonstatic
There are four kinds of nested classes: static member classes, nonstatic member classes, anonymous classes, and local classes.
1).A static member class is a static member of its enclosing class and obeys the same accessibility rules as other static members.
2).Within instance methods of a nonstatic member class, you can invoke methods on the enclosing instance or obtain a reference to the enclosing instance using the qualified this construct.
3).If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration.
4).One common use of anonymous classes is to create function and process objects.
5).Local classes are the least frequently used of the four kinds nested classes.
6).When you use anonymous or local class, keep it short or readability will ssuffer.
In conclusion, if a nested class needs to be visible outside of a single method or is too long to fit comfortable inside a method, use a member class. If each instance of the member class needs a reference to its enclosing instance, make it nonstatic;
otherwise, make it static. Assuming the classes belongs inside a method, if you need to create instance from only one location and there is a preexisting type that characterizes the class, make it an anonymous class; otherwise, make it a local class.
Example about Item 21 and Item 22:

//implement interface as strategy
public class Comptype implements Comparable<Comptype>{
    int i;
    int j;
    private static int count = 1;
    public Comptype(int n1, int n2) {
        i = n1;
        j = n2;
    }
    public String toString() {
        String result = "[i=" + i + ", j ="+ j +"]";
        if(count++ % 3 == 0)
            result += "\n";
        return result;
    }
    public int compareTo(Comptype rv) {
        return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));
    }
    private static Random r = new Random(47);
    public static Generator<Comptype> generator() {
        return new Generator<Comptype>() {
            //anonymous class
            public Comptype next() {
                return new Comptype(r.nextInt(100), r.nextInt(100));
            }
        };
    }
    public static void main(String[] args) {
        Comptype[] a =
            Generated.array(new Comptype[12], generator());
        System.out.println("before sorting:");
        System.out.println(Arrays.toString(a));
        Arrays.sort(a);
        System.out.println("after sorting:");
        System.out.println(Arrays.toString(a));
    }
}

class Generated {
    //static member class
    public static <T> T[] array(T[] a, Generator<T> gen) {
        return new CollectionData<T>(gen, a.length).toArray();
    }
}
…
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、付费专栏及课程。

余额充值