Effective Java第二版第4章

4 类和接口

第13条 使类和成员的可访问性最小化

当访问级别从default变成protected的时候,会大大增加可访问性。protected的成员是类导出的API的一部分,必须永远得到支持。导出的类的protected成员也代表了该类对于某个实现细节的公开承诺。受保护的成员应该尽量少用。

**实例域决不能是公有的。**如果域是非final的,或者是一个指向可变对象的final引用,那么一旦使这个域成为共有的,就放弃了对存储在这个域中的值进行限制的能力;这意味着,你也放弃了强制这个域不可变的能力。同时,当这个域被修改的时候,你失去了对它采取任何行动的能力。因此,包含共有可变域的类并不是线程安全的。 即使域是final的,并且引用不可变的对象,当把这个域变成公有的时候,也就放弃了“切换到一种新的内部数据表示法”的灵活性。

长度非零的数组总是可变的。所以,类具有public的static的final的数组域,或者返回这种域的访问方法,这几乎总是错误的。

// 这是有潜在的安全漏洞的。
public static final Ting[] values = {}

许多ide会产生返回指向私有数组的引用的访问方法,这回导致上述问题产生。此问题有两种解决办法:
(1)使数组私有,并增加一个公有的不可变列表。

private static final Ting[] 	PRIVATE_VALUES = {}
public static final List<Thing> VALUES = Collections.unmodifidableList(Arrays.asList(PRIVATE_VALUES ))

(2)使数组私有,并添加一个公有方法,它返回私有数组的一个备份。

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

除了public static final常量域的特殊情形外,共有类都不应该包含公有域。并且要确保public static final常量域所引用的对象都是不可变的。

第14条 在公有类中使用访问方法而非公有域

第15条 使可变性最小化

(1)不要提供任何会修改对象状态的方法(也成为mutator)。
(2)保证类不会被扩展。一般做法是使这个类成为final的。
(3)使所有的域都是final的。
(4)使所有的域都成为私有的。虽然从技术上讲,允许不可变的类具有公有的final域,只要这些域包含基本类型的值或者是指向不可变对象的引用;但是不建议这样做。因为这样会使得在以后的版本中无法再改变内部的表示法。
(5)确保对于任何可变组件的互斥访问。如果类有指向可变对象的引用域,则必须确保这些引用域是非public的。永远不要用客户端提供的对象来初始化这样的域,也不要从任何访问方法中返回这样的域。在构造器、访问方法和readObject方法中使用保护性拷贝(defensive copy)技术

// 不可变类的一个例子
package book.effective.rule15;

public final class Complex {
    private final double re;
    private final double im;

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

    public double realPart() {
        return re;
    }

    public double imaginaryPart() {
        return im;
    }

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

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

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

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

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Complex)) {
            return false;
        }

        Complex c = (Complex) obj;

        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 + "i)";
    }
}

不可变的对象本质上是线程安全的,它们不要求同步。不可变对象可以被自由地共享。 不可变的类可以提供一些静态工厂方法,他们把频繁被请求的实例缓存起来。

不仅可以共享不可变对象,甚至也可以共享他们的内部信息。 BigInteger内部使用了符号数值表示法。符号用一个int值来表示,数值则用int数组来表示。negate方法会产生一个相反符号的新的BigInteger。新的BigInteger并不需要拷贝数组,指向了原始实例中的同一个内部数组。

    /**
     * Returns a BigInteger whose value is {@code (-this)}.
     *
     * @return {@code -this}
     */
    public BigInteger negate() {
        return new BigInteger(this.mag, -this.signum);
    }

不可变对象为其他对象提供了大量的构建(building blocks)。

不可变对象真正的唯一缺点是:对于每一个不同的值都需要一个单独的对象。 性能问题有两种解决办法:(1)将经常用到的多步骤操作作为基本类型提供。例如,BigInteger有一个包级私有的可变配套类(Companing class),加速模指数这类的运算。(2)提供公有的可变配套类。例如,StringBuilder。

类不可被继承的两种方法:
(1)使用final进行修饰
(2)构造器声明为private或者protected,然后提供静态工厂方法
静态工厂方法比较灵活,很容易扩展。

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

    /**
     * 使用极坐标的方式来创建Complex
     *
     * @param r 极径
     * @param theta 角度
     * @return
     */
    public static Complex valueOfPolar(double r, double theta) {
        return new Complex(r * Math.cos(theta),
            r * Math.sin(theta));
    }

第16条:复合优先于继承

继承打破了封装性。

继承是有风险的:子类不得不随着父类的演进而跟随演进。

package book.effective.rule16;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

public class InstrumentedHashSet<E> extends HashSet<E> {
    /**
     * 记录添加的元素的个数
     */
    private int addCount;

    public InstrumentedHashSet() {

    }

    public InstrumentedHashSet(int initCapacity, float loadFactor) {
        super(initCapacity, 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;
    }

    public static void main(String[] args) {
        InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
        s.addAll(Arrays.asList("A", "B", "C"));
        /**
         * 返回值是6:因为hashset实现addAll的时候是调用了自身的add方法的
         */
        System.out.println(s.getAddCount());
    }
}

复合的实现方式:

package book.effective.rule16;

import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

public class InstrumentedHashSet<E> extends ForwardingSet<E> {
    /**
     * 记录添加的元素的个数
     */
    private int addCount;

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

    @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;
    }

    public static void main(String[] args) {
        InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
        s.addAll(Arrays.asList("A", "B", "C"));
        /**
         * 返回值是6:因为hashset实现addAll的时候是调用了自身的add方法的
         */
        System.out.println(s.getAddCount());
    }
}

package book.effective.rule16;

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

public class ForwardingSet<E> implements Set<E> {
    private final Set<E> s;

    public ForwardingSet(Set s) {
        this.s = s;
    }

    @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.retainAll(c);
    }

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

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

第17条:要么为继承而设计,并提供说明文档,要么就禁止继承

可以被继承的类必须有文档说明它可覆盖的方法的自用性。
例如:java.util.AbstractCollection的规范文档。remove方法的文档中清楚地说明了覆盖iterator方法将会影响remove方法的行为,并且描述了将会怎样影响remove方法的行为。

    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over the collection looking for the
     * specified element.  If it finds the element, it removes the element
     * from the collection using the iterator's remove method.
     *
     * <p>Note that this implementation throws an
     * <tt>UnsupportedOperationException</tt> if the iterator returned by this
     * collection's iterator method does not implement the <tt>remove</tt>
     * method and this collection contains the specified object.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     */
    public boolean remove(Object o) {

可被继承的类必须通过某种形式提供适当的钩子,以便能够进入到它的内部工作流程中,这种形式可以是protected方法,也可以是protected域(这个相对比较少见)。
例如:java.util.AbstractList中的removeRange方法

    /**
     * Removes from this list all of the elements whose index is between
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
     * (If {@code toIndex==fromIndex}, this operation has no effect.)
     *
     * <p>This method is called by the {@code clear} operation on this list
     * and its subLists.  Overriding this method to take advantage of
     * the internals of the list implementation can <i>substantially</i>
     * improve the performance of the {@code clear} operation on this list
     * and its subLists.
     *
     * <p>This implementation gets a list iterator positioned before
     * {@code fromIndex}, and repeatedly calls {@code ListIterator.next}
     * followed by {@code ListIterator.remove} until the entire range has
     * been removed.  <b>Note: if {@code ListIterator.remove} requires linear
     * time, this implementation requires quadratic time.</b>
     *
     * @param fromIndex index of first element to be removed
     * @param toIndex index after last element to be removed
     */
    protected void removeRange(int fromIndex, int toIndex) {
        ListIterator<E> it = listIterator(fromIndex);
        for (int i=0, n=toIndex-fromIndex; i<n; i++) {
            it.next();
            it.remove();
        }
    }

这个方法对于List实现的最终用户没有意义。提供该方法的唯一目的在于,使子类更易于提供针对子列表的快速clear方法。如果没有removeRange方法,当在子List上调用clear方法时,子类不得不用平方级的时间来完成它的工作。否则,就得重新编写整个subList机制——这个就不太现实了。

对于为了继承而设计的类,唯一的测试方法就是编写子类。并且要在发布之前先编写子类来对超类进行测试。

构造器决不能调用可被覆盖的方法,无论是直接调用还是间接调用。举个例子:

package book.effective.rule17;

import java.util.Date;

public final class Sub extends Super {
    private final Date date;

    Sub() {
        date = new Date();
    }

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

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

运行结果:

null
Mon May 16 20:55:10 CST 2022

无论是clone还是readObject,都不可以调用可覆盖的方法,无论是直接还是间接的方式。 如果你决定在一个为了继承而设计的类中实现Serializeble,并且该类中有一个readResolve或者writeReplace方法,就必须使这两个方法声明为protected,而不是private。如果这两个方法是private的,那么子类会忽略掉折叠两个方法。

确保可被继承的类永远不会调用它的可被覆盖的方法。换言之,消除类中的可自用性。

第18条:接口优于抽象类

接口的优势:
(1)现有的类很容易被修改以实现新的接口
(2)接口是定义mixin(混合类型)的理想选择
Java中类不可能有一个以上的父亲,类层次结构中也没有适当的地方插入mixin
(3)接口允许我们构造非层次结构的类型框架

虽然接口不允许包含方法的实现,但是, 使用接口来定义类型并不妨碍你为程序员提供实现上的帮助。通过对你导出的每个重要接口都提供一个抽象的骨架实现(skeletal implementation)类,把接口和抽象类的优点结合起来。 接口的作用仍然是定义类型,骨架实现类接管了所有与接口实现相关的工作。

一般来说,骨架实现被称为AbstractInterface,例如AbstractCollection、AbstractList、AbstractSet和AbstractMap。将它们称为SkeletalCollection、SkeletalList、SkeletalSet和SkeletalMap也是有道理的,但是Abstract的用法已经深入人心。

第19条:接口只用于定义类型

当类实现接口时,接口就充当可以引用这个类的实例的类型(type)。因此,类实现了接口,就表明客户端可以对这个类的实例实施某些动作。为了其他目的而定义的接口都是不恰当的。

常量接口是对接口的不良使用。

public interface PhysicalConstants {
static final int CONSTANT_1;
static final int CONSTANT_2;
static final int CONSTANT_3;
......
}

定义常量的比较好的几种方案:
(1)如果常量与类紧密相关,直接定义在类中。例如, Integer.MIN_VALUE
(2)枚举
(3)直接定义不可实例化的工具类来导出常量

第20条:类层次优于标签类

标签类的缺点:
过于冗长、容易出错并且效率低下
(1)充斥着样板代码:包括枚举声明、标签域以及条件语句
(2)类中充斥多种实现,破坏了可读性
(3)内存占用增加,因为实例承担着属于其他风格的不相关的域。

package book.effective.rule20;

public class Figure {
    enum Shape {RECTANGLE, CIRCLE};

    // Tag域
    final Shape shape;

    // 矩形才使用的域
    double length;
    double width;

    // 圆才使用的域
    double radius;

    // 圆的构造函数
    Figure(double radius) {
        shape = Shape.CIRCLE;
        this.radius = radius;
    }

    Figure(double length, double width) {
        shape = Shape.RECTANGLE;
        this.length = length;
        this.width = width;
    }

    double area() {
        switch (shape) {
            case CIRCLE:
                return Math.PI * (radius * radius);
            case RECTANGLE:
                return length * width;
            default:
                throw new AssertionError();
        }
    }
}

进行类的层次化改造:

package book.effective.rule20;

public abstract class Figure {
    abstract double area();
}

package book.effective.rule20;

public class Circle extends Figure {
    // 圆才使用的域
    final double radius;


    // 圆的构造函数
    Circle(double radius) {
        this.radius = radius;
    }

    double area() {
        return Math.PI * (radius * radius);
    }
}

package book.effective.rule20;

public class Rectangle {
    // 矩形才使用的域
    double length;
    double width;


    Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }


    double area() {
        return length * width;
    }
}

package book.effective.rule20;

public class Square extends Rectangle {
    Square(double side) {
        super(side, side);
    }
}

第21条:用函数对象表示策略

例如Comparator函数。通过传递不同的比较器函数,可以获得各种不同的排列顺序。这也是策略模式的一个案例。比较器函数代表一种为元素排序的一种策略。

函数指针的主要用途就是实现策略模式。**Java,中声明一个接口来表示策略,并且每个具体的策略实现了一个类。当一个具体的策略只被使用一次的时候,通常使用匿名类来声明和实例化这个具体策略。当一个具体的策略是设计用来重复使用的时候,它的类通常就被实现为私有的静态成员类,并通过公有的静态final域被导出,其类型为该策略接口。 **

java的String类就是通过这种方式导出了一个不区分大小写的字符串比较器。

  /**
     * A Comparator that orders {@code String} objects as by
     * {@code compareToIgnoreCase}. This comparator is serializable.
     * <p>
     * Note that this Comparator does <em>not</em> take locale into account,
     * and will result in an unsatisfactory ordering for certain locales.
     * The java.text package provides <em>Collators</em> to allow
     * locale-sensitive ordering.
     *
     * @see     java.text.Collator#compare(String, String)
     * @since   1.2
     */
    public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
    private static class CaseInsensitiveComparator
            implements Comparator<String>, java.io.Serializable {
        // use serialVersionUID from JDK 1.2.2 for interoperability
        private static final long serialVersionUID = 8575799808933029326L;

        public int compare(String s1, String s2) {
            int n1 = s1.length();
            int n2 = s2.length();
            int min = Math.min(n1, n2);
            for (int i = 0; i < min; i++) {
                char c1 = s1.charAt(i);
                char c2 = s2.charAt(i);
                if (c1 != c2) {
                    c1 = Character.toUpperCase(c1);
                    c2 = Character.toUpperCase(c2);
                    if (c1 != c2) {
                        c1 = Character.toLowerCase(c1);
                        c2 = Character.toLowerCase(c2);
                        if (c1 != c2) {
                            // No overflow because of numeric promotion
                            return c1 - c2;
                        }
                    }
                }
            }
            return n1 - n2;
        }

        /** Replaces the de-serialized object. */
        private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
    }

第22条:优先考虑静态成员类

嵌套类有四种:静态成员类(static member class)、非静态成员类(nonstatic member class)、匿名类(anonymous class)和局部类(local class)。后三种就被成为内部类(inner class)。

非静态成员类的一种常见用法就是定义一个Adaper,它允许外部类的实例被看作是另一个不相关的类的实例。例如Map接口的实现往往使用非静态成员类来实现它们的集合视图(collection view),这些集合视图是由Map的keySet、entrySet和values方法返回的。 同样地,诸如Set和List这种集合接口的实现往往也使用费静态成员类来实现它们的迭代器(iterator)。

    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet();
            keySet = ks;
        }
        return ks;
    }

    final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

 public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es;
        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
    }

    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator();
        }
        public final boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Node<K,V> candidate = getNode(hash(key), key);
            return candidate != null && candidate.equals(e);
        }
        public final boolean remove(Object o) {
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
                Object key = e.getKey();
                Object value = e.getValue();
                return removeNode(hash(key), key, value, true, true) != null;
            }
            return false;
        }
        public final Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

 public Collection<V> values() {
        Collection<V> vs = values;
        if (vs == null) {
            vs = new Values();
            values = vs;
        }
        return vs;
    }

    final class Values extends AbstractCollection<V> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<V> iterator()     { return new ValueIterator(); }
        public final boolean contains(Object o) { return containsValue(o); }
        public final Spliterator<V> spliterator() {
            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super V> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.value);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

package book.effective.rule22;

import java.util.AbstractSet;
import java.util.Iterator;

/**
 * 非静态内部类的典型使用
 */
public class MySet extends AbstractSet {
    @Override
    public Iterator iterator() {
        return new MyIterator();
    }

    private class MyIterator<E> implements Iterator<E> {

        @Override
        public boolean hasNext() {
            return false;
        }

        @Override
        public E next() {
            return null;
        }
    }
    
    @Override
    public int size() {
        return 0;
    }
}

如果声明成员类不需要访问外围实例,就要始终把static修饰符放在它的声明中,使其成为静态成员类。非静态成员类实例都会包含一个额外指向外围对象的引用,保存这份引用需要额外的空间和时间,并且会导致外围实例在符合gc条件时仍被保留。如果在没有外围实例的情况下,也需要分配外围实例。

Map内部的Entry对象。虽然每一个entry都与一个Map关联,但是Entry上面的方法并不需要访问该Map,此时,私有静态成员是最佳的选择。如果去掉static修饰符,Map仍然可以工作,但是每个entry都包含一个指向map的引用,这就浪费了时间和空间。

/**
* HashMap里面的Node类就是Entry
     * Basic hash bin node, used for most entries.  (See below for
     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
     */
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

当且仅当匿名类出现在非静态的环境中,它才有外围实例。但是即使它们出现再静态的环境中,也不可能拥有任何静态成员。 匿名类的一种常见用法是动态地创建函数对象,例如sort函数里面的Comparator;另一种常见用法是创建过程对象(process Object),例如Runnable、Thread或者TimerTask实例;第三种常见的就是在静态工厂方法的内部。

局部类用的最少。在任何可以声明局部变量的地方都可以声明局部类,局部类遵守同样的作用域规则。

如果一个嵌套类需要在单个方法外仍然是可见的, 或者它太长了,不适合放在方法内部,就应该使用成员类。如果成员类的每一个实例都需要一个指向其外围实例的引用,就要把成员类做成非静态的,否则就要做成静态的。假设这个嵌套类属于一个方法的内部,如果你只需要在一个地方创建实例,并且已经有了一个预置的类型可以说明这个类的特征,就要把它做成匿名类,否则,就要做成局部类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值