在Java开发中,我们实际上会利用Java这种面向对象语言,在无意中写出很多面向过程风格的代码。
譬如我们违背了面向对象的 三大特性
,违背了面向对象的定义,这样子的代码都会变成面向过程风格的代码。
下面列举三种面向过程风格的代码
getter
,setter
未作封装
public class ShoppingCart { private int itemsCount; private double totalPrice; private List<ShoppingCartItem> items = new ArrayList<>(); public int getItemsCount() { return this.itemsCount; } public void setItemsCount(int itemsCount) { this.itemsCount = itemsCount; } public double getTotalPrice() { return this.totalPrice; } public void setTotalPrice(double totalPrice) { this.totalPrice = totalPrice; } public List<ShoppingCartItem> getItems() { return this.items; } public void addItem(ShoppingCartItem item) { items.add(item); itemsCount++; totalPrice += item.getPrice(); } } 复制代码
例如上述的代码,getter
和 setter
未作任何封装,数据没有访问权限,可以随意被修改。可见该代码违背面向对象中封装的定义,其为一段面向过程风格的代码。
那么,如何利用封装的特性,将其改为一段 面向对象 风格的代码呢?
首先我们去除所有的 setter
方法,使其不与 addItem(ShoppingCartItem item)
起冲突,即只留 addItem
这一个修改数据的通道。这样子也许你会认为代码已封装完毕,数据已不会再被其他的方法随意修改了。
但事实上,这样子还是没有封装好,我们的数据依旧会被修改。我们可以这样子操作:
public static void main(String[] args) { ShoppingCartItem item1,item2; item1 = new ShoppingCartItem("no1",19); item2 = new ShoppingCartItem("no2",30); ShoppingCart cart = new ShoppingCart(); cart.addItem(item1); cart.addItem(item2); cart.getItems().clear();//清空list,但是总价格,总数量未改变。封装不彻底 System.out.println(cart.getTotalPrice()); } 复制代码
上述的代码还是会在我们的设计的方法之外随意改变我们的数据,且导致数据不同步、出错
我们需要借用Collections.unmodifiableList()
方法,做到彻底的封装。
public List<ShoppingCartItem> getItems() { return Collections.unmodifiableList(this.items); } 复制代码
我们查看一下 Collections.unmodifiableList
() 的源码,就可知道为何这样子可以防止修改了。
public static <T> List<T> unmodifiableList(List<? extends T> list) { return (list instanceof RandomAccess ? new UnmodifiableRandomAccessList<>(list) : new UnmodifiableList<>(list)); } /** * @serial include */ static class UnmodifiableList<E> extends UnmodifiableCollection<E> implements List<E> { @java.io.Serial private static final long serialVersionUID = -283967356065247728L; @SuppressWarnings("serial") // Conditionally serializable final List<? extends E> list; UnmodifiableList(List<? extends E> list) { super(list); this.list = list; } public boolean equals(Object o) {return o == this || list.equals(o);} public int hashCode() {return list.hashCode();} public E get(int index) {return list.get(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) {return list.indexOf(o);} public int lastIndexOf(Object o) {return list.lastIndexOf(o);} public boolean addAll(int index, Collection<? extends E> c) { throw new UnsupportedOperationException(); } @Override public void replaceAll(UnaryOperator<E> operator) { throw new UnsupportedOperationException(); } @Override public void sort(Comparator<? super E> c) { throw new UnsupportedOperationException(); } public ListIterator<E> listIterator() {return listIterator(0);} public ListIterator<E> listIterator(final int index) { return new ListIterator<E>() { private final ListIterator<? extends E> i = list.listIterator(index); public boolean hasNext() {return i.hasNext();} public E next() {return i.next();} public boolean hasPrevious() {return i.hasPrevious();} public E previous() {return i.previous();} public int nextIndex() {return i.nextIndex();} public int previousIndex() {return i.previousIndex();} public void remove() { throw new UnsupportedOperationException(); } public void set(E e) { throw new UnsupportedOperationException(); } public void add(E e) { throw new UnsupportedOperationException(); } @Override public void forEachRemaining(Consumer<? super E> action) { i.forEachRemaining(action); } }; } 复制代码
UnmodifiableList
对会对 list 的修改类型方法 进行重写,令其抛出异常,就会让其他使用者无法调用 list 中修改类型的方法来修改数据了
但是在 调用 list 的 get 方法之后,还是会修改到单项的数据,例如
ShoppingCartItem item = items.get(0); item.setPrice(19.0); 复制代码
这时候,我们应该在返回list的时候,返回一个数据的 拷贝(深拷贝) ,这样子就不会对原数据做出修改了。
我们可以如下修改
//数据类继承Cloneable接口,重写clone()方法,使得其可继承 public class ShoppingCartItem implements Cloneable{ private String name; private int price; public void setName(String name) { this.name = name; } public void setPrice(int price) { this.price = price; } public ShoppingCartItem(String name, int price) { this.name = name; this.price = price; } public String getName() { return name; } public int getPrice() { return price; } @Override public ShoppingCartItem clone() { try { return (ShoppingCartItem) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); } } } public class ShoppingCart { ... public List<ShoppingCartItem> getItems() { List<ShoppingCartItem> copyItems = new ArrayList<>();; for (ShoppingCartItem i : items){ copyItems.add(i.clone()); } return Collections.unmodifiableList(copyItems); } ... } 复制代码
如上实现深拷贝,数据就不可修改原数据的单项数据了😁