1. Careful coverage of clone
1. Common object overrides clone
PhoneNumber
Method of implementation:public class PhoneNumber implements Cloneable{ private final short areaCode; private final short prefix; private final short lineNumber; public PhoneNumber(short areaCode, short prefix, short lineNumber) { super(); this.areaCode = areaCode; this.prefix = prefix; this.lineNumber = lineNumber; } public PhoneNumber(int i, int j, int k) { this.areaCode = (short)i; this.prefix = (short)j; this.lineNumber = (short)k; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PhoneNumber)) { return false; } PhoneNumber pn = (PhoneNumber) obj; return pn.areaCode==areaCode&&pn.prefix==prefix&&pn.lineNumber==lineNumber; } @Override public int hashCode() { return 42; } @Override public String toString() { return "PhoneNumber [areaCode=" + areaCode + ", prefix=" + prefix + ", lineNumber=" + lineNumber + "]"; } //Add clone method @Override protected PhoneNumber clone() throws CloneNotSupportedException { return (PhoneNumber) super.clone(); } }
Implementation results:@Test public void test() throws CloneNotSupportedException { PhoneNumber pn = new PhoneNumber(1, 2, 3); PhoneNumber pn2 = pn.clone(); System.out.println("pn.clone()!=pn2-----"+(pn.clone()!=pn2)); System.out.println("pn.clone().getClass()==pn2.getClass()-----"+(pn.clone().getClass()==pn2.getClass())); System.out.println("pn.clone().equals(pn2)-----"+(pn.clone().equals(pn2))); }
pn.clone()!=pn2-----true
pn.clone().getClass()==pn2.getClass()-----true
pn.clone().equals(pn2)-----true
It seems that a new cloned object can be created by a simple strong transformation.2. Objects contain references to variable objects and override clones. Simple clones can cause problems.
Look at another example:
Stack2
Execute code://Adding generic constraints to classes public class Stack2<E> implements Cloneable{ //Array of generic definition classes private E[] elements; private int size = 0; private static final int DEFAULT_CAPACITY=16; @SuppressWarnings("unchecked") public Stack2(){ //Here, because the array must be of a specific type, it needs to be overridden elements = (E[]) new Object[DEFAULT_CAPACITY]; } //push pushes into elements of generic classes public void push(E e){ ensureCapacity(); elements[size++] = e; System.out.println("Stack size"+size); } private void ensureCapacity() { // TOD ensures self-increasing size if (elements.length==size) { elements = Arrays.copyOf(elements, 2*size+1); System.out.println("Stack Length"+elements.length); } } //Pop-up Generic Result Set public E pop(){ if(isEmpty()){ throw new EmptyStackException(); } E result = elements[--size]; System.out.println("Stack size"+size); //Pop-up element empty elements[size] = null; return result; } public boolean isEmpty() { // TODO Auto-generated method stub return size==0; } @Override protected Stack2<E> clone() throws CloneNotSupportedException { return (Stack2<E>) super.clone(); } }
Implementation results:@Test public void test1(){ Stack2<String> stack2 = new Stack2<>(); stack2.push("hello0"); stack2.push("hello1"); try { //Careful coverage of clone Stack2<String> stack22 = stack2.clone(); System.out.println(stack22.pop()); //System.out.println(stack22.pop()); System.out.println(stack2.pop()); //System.out.println(stack2.pop()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } }
Stack size 1
Stack size 2
Stack size 1
hello1
Stack size 1
null
I found a strange problem: common elements.E[]elements
Other attributes, such as size, have been copied successfully. Causes me to pop one object, another object to pop the current location object again, take out empty.In fact, the clone method is another constructor that must ensure that the original object is not harmed. And ensure that the constraints of cloned objects are created correctly.
Execution results after modification:@Override protected Stack2<E> clone() throws CloneNotSupportedException { Stack2<E> result = (Stack2<E>) super.clone(); result.elements = elements.clone(); return result; }
Stack size 1
Stack size 2
Stack size 1
hello1
Stack size 1
hello1
3. If the elements domain is final
The code is as follows:
public class Stack2<E> implements Cloneable{ //Array of generic definition classes private final E[] elements; ...slightly... @Override protected Stack2<E> clone() throws CloneNotSupportedException { Stack2<E> result = (Stack2<E>) super.clone(); //This is not allowed if the elements domain is final result.elements = elements.clone(); return result; } }
4. Shallow and deep copies:
4.1. What is shallow copy?
Shallow copy is a bit-by-bit copy of an object, which creates a new object with an exact copy of the original object's attribute values. If the attribute is a basic type, the copy is the value of the basic type; if the attribute is a memory address (reference type), the copy is a memory address, so if one of the objects changes this address, it will affect the other object.4.2. What is Deep Copy
Deep copy copies all attributes and copies the dynamically allocated memory that attributes point to. A deep copy occurs when an object is copied with the object it refers to. Deep copy is slower and more expensive than shallow copy.
The book takes the example of HashTable as a deep copy, just as we can look at the code of HashTable:
public synchronized Object clone() { try { Hashtable<?,?> t = (Hashtable<?,?>)super.clone(); t.table = new Entry<?,?>[table.length]; for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entry<?,?>) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; return t; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }
Suggestion: Because deep copy is needed in general, and the matters needing attention in deep copy, such as final domain, such as internal traversal, etc. So it's better to expose a copy constructor's static factory directly to the public. Similar to the following:
//Through Static Factory Processing public static PhoneNumber newInstance(PhoneNumber pn){ return new PhoneNumber(pn.areaCode,pn.prefix,pn.lineNumber); }
2. Consider Implementing Comparable Interface
Look at a date comparison class I wrote to implement the Comparable interface
Execute code:public class DateBean implements Serializable,Comparable<DateBean>{ private int year; private int month; private int date; public DateBean(int year, int month, int date) { this.year = year; this.month = month; this.date = date; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDate() { return date; } public void setDate(int date) { this.date = date; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DateBean dateBean = (DateBean) o; if (year != dateBean.year) return false; if (month != dateBean.month) return false; return date == dateBean.date; } @Override public int hashCode() { int result = year; result = 31 * result + month; result = 31 * result + date; return result; } @Override public int compareTo(DateBean dateBean) { // TODO Auto-generated method stub if (dateBean.getYear()>getYear()){ return -1; }else if (dateBean.getYear()<getYear()){ return 1; }else { if (dateBean.getMonth()>getMonth()){ return -1; }else if (dateBean.getMonth()<getMonth()){ return 1; }else { if (dateBean.getDate()>getDate()){ return -1; }else if (dateBean.getDate()<getDate()){ return 1; }else { return 0; } } } } @Override public String toString() { String time = getStrTime(getTime(year+"-"+month+"-"+date,"yyyy-M-dd"),"yyyy-MM-dd"); return time; } public static String getTime(String user_time, String type) { String re_time = null; SimpleDateFormat sdf = new SimpleDateFormat(type); Date d; try { d = sdf.parse(user_time); long l = d.getTime(); String str = String.valueOf(l); re_time = str.substring(0, 10); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } return re_time; } public static String getStrTime(String cc_time, String type) { String re_StrTime = null; SimpleDateFormat sdf = new SimpleDateFormat(type); // For example, cc_time=1291778220 long lcc_time = Long.valueOf(cc_time); re_StrTime = sdf.format(new Date(lcc_time * 1000L)); return re_StrTime; } }
Implementation results:@Test public void test() { Set<DateBean> dateBeans = new TreeSet<>(); dateBeans.add(new DateBean(2017, 5, 1)); dateBeans.add(new DateBean(2017, 4, 1)); dateBeans.add(new DateBean(2017, 5, 20)); dateBeans.add(new DateBean(2015, 3, 20)); dateBeans.add(new DateBean(2015, 4, 20)); System.out.println(dateBeans.toString()); }
[2015-03-20, 2015-04-20, 2017-04-01, 2017-05-01, 2017-05-20]
The compareTo method returns results: Returns - 1, 0, 1 according to the negative, zero and positive values of the expression, and must satisfy symmetry, transitivity and reflexivity as equals; besides TreeSet and TreeMap, it can also be used in Arrays.sort(a) method to sort the array of objects.
The following code:
Implementation results:@Test public void test1() { DateBean[] dateBeans =new DateBean[5]; dateBeans[0]=new DateBean(2017, 5, 1); dateBeans[1]=new DateBean(2017, 4, 1); dateBeans[2]=new DateBean(2017, 5, 20); dateBeans[3]=new DateBean(2015, 3, 20); dateBeans[4]=new DateBean(2015, 4, 20); for (DateBean b:dateBeans) { System.out.println(b.toString()); } Arrays.sort(dateBeans); System.out.println("---------"); for (DateBean b:dateBeans) { System.out.println(b.toString()); } }
2017-05-01
2017-04-01
2017-05-20
2015-03-20
2015-04-20
---------
2015-03-20
2015-04-20
2017-04-01
2017-05-01
2017-05-20
Cautious optimization: Since the return value only needs positive and negative numbers or zero, and does not require size, the optimization efficiency can be appropriately processed by subtraction of two values.
But we need to consider the boundary problem. For example, if the difference between the maximum and minimum values is greater than the boundary, it will overflow and return a negative value, indicating an error.