EffectiveJava study notes (d)

Article 15: making class and member accessibility minimized

One of the basic principles of software design: information hiding - a module does not need to know the inner workings of other modules.
Implement this principle is very simple, that is, as much as possible so that each class member or not be accessed externally.

For the top of the class, there are only two levels of access: package-level private (package-private), public (public).
When a class is declared as a private packet-level, it is actually part of the implementation of this package, rather than external interfaces in future versions can be freely modified or deleted. If declared as public, we need to always support it, to maintain its compatibility.

  • Public class instance fields must not be public. If the instance fields point to a non-final or final object reference variable, if set to the public, to give up the stored values ​​to be limiting in this field capacity.
  • Let class has an array of public domain or for final return an array of field return method is wrong, the client can modify the contents of the array . There are two ways to fix this problem:

The public becomes an array of private, public increased immutable list

private static final String[] PRIVATE_VALUES = {...}
public static final List<String> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES);

Array becomes private, public add method returns a copy of the array

private static final String[] PRIVATE_VALUES = {...}
public static final String[] values(){
    return PRIVATE_VALUES.clone();
}

Article 16: To use access methods in public classes instead of the public domain

Public class should never be exposed variable domains. Although there are still problems, but let immutable class exposes public domain, you can reduce the harm by adding constraints. It is sometimes needed private or private packet-level nested class exposure area, whether this class is variable or invariable.

Article 17: Increases minimize variability

Immutable class the following five principles:

  • Do not provide any method modifies the state of an object (also known as setter)
  • Ensure that category will not be extended
  • All fields are final statement of
  • All fields are declared as private
  • Ensure exclusive access for any variable component. If the class field with a variable pointing to an object, you must ensure that the use of such clients can not obtain references to these objects, the object never use provided by the client to initialize this domain.

Immutable objects features:

  • State immutable objects is relatively simple, namely the creation of only one state at the time of
  • Is thread-safe on immutable objects in nature, do not require synchronization can be shared freely.
  • Immutable object provides a number of other objects member
  • Cons: For each different value requires a separate object, if there is a bit BigInteger million, wants to change its low, it is necessary to create a new BigInteger instance, there are over one million, but with the original only a different object. (May be used to solve this problem BigSet, can change the state of only a single bit in a fixed time) which can not predict if a complex operation to be performed on the client immutable classes, may provide a public variable matching class , such as String- -> StringBuilder

Immutable class configured in two ways, one method is to become the final class, and the other method is to let the class constructor is private becomes, and provides public static method instead of public constructors.

public class Point{
    private final int x;
    private final int y;
    private Point(int x,int y){
        this.x = x;
        this.y = y;
    }
    public static Point valueOf(int x,int y){
        return new Point(x,y);
    }
}

to sum up:

  1. Unless there is good reason to make the class a class variable, otherwise it should be immutable
  2. If the class can not be made immutable, to limit its possible variability
  3. The constructor should create fully initialized object and set up all the constraints

Article 18: Composite precedence over inheritance

Different inheritance and method call is inherited break encapsulation, if the parent changes, subclass must be followed by changes in the parent class is updated, unless the parent class is to expand and design, casual inherited class does not clearly documented, and You may not get the expected results.

Example: Want to know some programs, Set collection since its inception, to add too many elements altogether.

Creating InstrumentedHashSet class

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++;
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}

main method

public static void main(String[] args) {

        InstrumentedHashSet instrumentedHashSet = new InstrumentedHashSet();
        instrumentedHashSet.addAll(Arrays.asList("a", "b", "c"));
        System.out.println(instrumentedHashSet.getAddCount());
    }

Run output is 6, but we only add three elements, the desired result is 3, because addAll HashSet method is based on the method add implemented, the program first call addAll method addCount increased by 3, then add three times the cycle call method increments 1.

If you want to fix this method, we can cover addAll method, which can ensure the accuracy of the results, but we do so based on "HashSet of addAll way to add method implemented based on the" fact that the rules and does not guarantee future in java the release will not change, so we designed out InstrumentedHashSet is fragile, poor robustness.

Another reason is that subclass fragile: the parent class may add new methods. Assuming that a safety program based on the fact: the ability to insert an element are a set of prerequisites must be met. We can set the sub-category to cover all interpolation method is to ensure that when the additive elements satisfying the conditions. If the parent does not add a new insertion method in future releases, we do that can ensure security, but if the parent class adds a new insertion method, the program will be inserted does not meet the conditions by this method are not subclasses covered data, so there have been security breaches.

"Composite" can solve all the problems mentioned above, this design means that no pattern extend existing classes, but to add a private domain in the new class to an instance of an existing class. Existing classes into a new class of components, existing methods class all the methods of the new class can be included by calling achieved in this way is also called forward.

To InstrumentedHashSet class just for example, using a composite manner:

class InstrumentedHashSet2<E> extends ForwardingSet<E> {

    private int addCount = 0;

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

    @Override
    public boolean add(E e) {
        addCount++;
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}

class ForwardingSet<E> implements Set<E> {

    private final Set<E> s;

    public ForwardingSet(Set<E> set) {
        this.s = set;
    }

    @Override
    public int size() {
        return s.size();
    }

    @Override
    public boolean isEmpty() {
        return s.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return s.contains(o);
    }

    @Override
    public Iterator<E> iterator() {
        return s.iterator();
    }

    @Override
    public Object[] toArray() {
        return s.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return s.toArray(a);
    }

    @Override
    public boolean add(E e) {
        return s.add(e);
    }

    @Override
    public boolean remove(Object o) {
        return s.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return s.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return s.addAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return s.removeAll(c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return s.removeAll(c);
    }

    @Override
    public void clear() {
        s.clear();
    }
}

Another InstrumentedHashSet2 corresponds to a Set Set converted to a counting function, and may be compatible with any of the constructor Set, this mode is also referred to as "modified by mode", InstrumentedHashSet2 example a Set packaged, so InstrumentedHashSet2 also known as packaging.

Note that the packaging is not suitable for correction frame, the frame in the callback object to pass a reference to itself to other objects, to be packaged object does not know its outer packaging, so that it is itself passed references.

Article 19: Either inherited and provide design documentation, or prohibit inheritance

Specially designed classes to inherit, to be covered with a clear description of the impact of each method brings in the document. For each public or protected methods, the method to be described can be covered if the method call, what is called the order, the results of each call is how to handle the subsequent effects.

The constructor must not call method can be covered , the parent class's constructor run before the constructor subclass, the methods covered version of the sub-class will be executed before the subclass constructor run.

For example:

class Super {

    public Super() {
        overrideMe();
    }

    public void overrideMe() {
    }
}

final class Sub extends Super {

    private final Instant instant;

    Sub() {
        instant = Instant.now();
    }

    @Override
    public void overrideMe() {
        System.out.println(instant);
    }
}

main methods:

public static void main(String[] args) {
        new Sub().overrideMe();
    }

Output:

null

2020-03-22T08:43:29.201Z

The first line will print null, the program will first call the parent class constructor, and then call the overrideMe subclass, in which case the sub-class constructor has not run, Instant has not been initialized, it is null.

Invoked by private constructor method, final methods or static methods are safe, these methods are not covered.

Guess you like

Origin www.cnblogs.com/youtang/p/12641574.html