类和接口
# 类和成员的可访问性最小化
对于类的属性:尽可能使每个类或者成员不被外界访问,最好使用private
对于类的修改:使用访问方法而非公有域
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
# 使可变性最小化
不可变类是指其实例不能被修改的类:
- String
- 基本类型的包装类型
- BigInteger
- ...
这类实例包含的所有信息都必须在创建该实例的时候就提供,并且对象的整个生命周期内固定不变。
不可变类有以下五条规则:
1、不要提供任何会修改对象状态的方法。
// set方法(尽量不要提供)
// get方法
2、包装类不会扩展
为了防止子类假装对象的状态已经改变,从而破环该类的不可变行为。
- 方式1:声明这个类为final
- 方式2:让类的所有构造器成为私有的,添加公有的静态工厂来代替公有的构造器
3、声明所有的域都是final的
通过系统的强制方式可以清楚表明你想让这个类成为不可变类。
4、声明所有的域都为私有的
5、确保对于任何可变组件的互斥访问
# 复合优先于继承
子类依赖于其超类中特定功能的实现细节,超类的实现有可能会随着发行版本的不同而有所变化,如果真的发生变化,子类可能会遭到破坏。
现在有如下类TestHashSet
,来记录对象创建以来添加了多少个元素:
public class TestHashSet<E> extends HashSet<E> {
private int addCount = 0;
public TestHashSet() {
}
public TestHashSet(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;
}
}
public class Main {
public static void main(String[] args) {
TestHashSet<String> objects = new TestHashSet<>();
objects.addAll(Arrays.asList("a", "b", "c"));
System.out.println(objects.getAddCount());
}
}
// output
// 6
我们只添加了三个元素,但是最终结果为6,这是为什么呢?
因为HashSet的addAll
方法,最终也是调用的add
方法,这样导致我们一共调用了6次add
方法
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
可以发现这样的继承存在一些问题:
- 对于部分核心代码,需要阅读源码,但是因为只是添加部分功能,就额外有更多的学习成本
- 如果我们对
addAll
进行修改后,满足我们的业务功能,但是这个业务功能依赖于:HashSet的addAll
方法实在add
方法上实现的。这并非一个承诺,不能保证随着发行版本的不同而发生变化。
针对上诉的问题,可以在新的类中增加一个私有域,让其引用现有类的一个实例。
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> 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();
}
}
public class TestHashSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public TestHashSet(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 class Main {
public static void main(String[] args) {
TestHashSet<String> objects = new TestHashSet<>(new HashSet<>());
objects.addAll(Arrays.asList("a", "b", "c"));
System.out.println(objects.getAddCount());
}
}
// output
// 3
- 创建一个转发类,去实现核心接口
- 新类继承转发类,去重写核心接口
# 接口优先抽象类
Java提供了两种机制,可以用来定义多个实现的类型:
- 接口
- 抽象类
但是因为Java只允许单继承,所以使用抽象类作为类型定义会有所限制,使用接口的方式更容易对Java程序进行扩展。
接口和抽象类进行结合,可以对外提供一个抽象的骨架实现(AbstractInterface):
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
protected AbstractList() {
}
public boolean add(E e) {
add(size(), e);
return true;
}
abstract public E get(int index);
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {
ListIterator<E> it = listIterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
public int lastIndexOf(Object o) {
ListIterator<E> it = listIterator(size());
if (o==null) {
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}
public void clear() {
removeRange(0, size());
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}
}
骨架类的实现为抽象类提供了实现上的帮助,但又不会因为抽象类被用作类型定义的严格限制。
# 参考
上次更新: 2024/06/29, 15:13:44