学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • 设计模式

  • 算法思想

  • 编码规范

    • 创建和销毁对象
    • 类和接口
      • 类和成员的可访问性最小化
      • 使可变性最小化
      • 复合优先于继承
      • 接口优先抽象类
      • 参考
  • 技术思想
  • 编码规范
旭日
2023-04-06
目录

类和接口

# 类和成员的可访问性最小化

对于类的属性:尽可能使每个类或者成员不被外界访问,最好使用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;
    }
}

骨架类的实现为抽象类提供了实现上的帮助,但又不会因为抽象类被用作类型定义的严格限制。

# 参考

Effective Java (opens new window)

上次更新: 2024/06/29, 15:13:44
创建和销毁对象

← 创建和销毁对象

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式