Java Theory and Practice: changing or not?

Source: Internet
Author: User
Tags color representation
Unchanged objects have many features that make it easier to use, including non-strict synchronization requirements and free sharing and high-speed cache object reference without the need to consider data pivoting. Although immutability may not necessarily make sense to all classes, at least some classes in most programs will benefit from immutability. In this month's Java Theory and PracticeBrian Goetz illustrates some of the strengths of immutability and some principles for constructing immutable classes. Please share your experiences in this article with the author and other readers in the attached forum. (You can also click "discussion" at the top or bottom of the article to visit the Forum .)

Unchanged objects refer to objects whose external visibility status cannot be changed after instantiation. Java class libraryString,IntegerAndBigDecimalClass is an example of an unchanged object-they represent a single value that cannot be changed during the lifetime of the object.

Immutable strengths
If you use the unchanged classes correctly, they greatly simplify programming. Because they can only be in one State, as long as they are constructed correctly, they will never be in an inconsistent state. You do not have to copy or clone unchanged objects to freely share and cache references to them. You can cache the results of their fields or their methods, you don't have to worry about whether the value will become invalid or inconsistent with other States of the object. A constant class usually produces the best ing key. Moreover, they are inherently thread-safe, so they do not need to be accessed simultaneously between threads.

Free High-speed cache
Because the value of the unchanged object is not changed, you can freely cache references to them, and you can be sure that the same value will still be referenced in future references. Similarly, because their features cannot be changed, you can cache their fields and the results of their methods.

If the object is variable, you must pay attention when storing the reference to it. Consider the code in Listing 1, which lists two tasks executed by the scheduler. Purpose: start the first task and the second task on a certain day.

Listing 1. potential problems with variable date objects

 Date d = new Date();  Scheduler.scheduleTask(task1, d);  d.setTime(d.getTime() + ONE_DAY);  scheduler.scheduleTask(task2, d);

BecauseDateIs variable, soscheduleTaskThe method must be careful to use preventive measures to copy the date parameter (possibly throughclone()) To its internal data structure. Otherwise,task1Andtask2It may all be executed tomorrow, which is not expected. Even worse, the internal data structure used by the task scheduler may become incorrect. Compile the imagescheduleTask()In this way, it is extremely easy to forget to use preventive measures to copy the date parameter. If you forget to do this, you will create an insurmountable error that will not be immediately displayed, and it will take a long time for people to capture it when it is exposed. UnchangedDateClass.

Inherent thread security
Most thread security problems occur when multiple threads are attempting to concurrently modify the state of an object (write-write conflict), or when a thread is attempting to access the state of an object, while another thread is modifying it (read-write conflict. To prevent such conflicts, you must synchronize access to shared objects so that other threads cannot access them when the objects are inconsistent. It is difficult to do this correctly. A large number of documents are required to ensure that the program is correctly extended, which may have adverse effects on performance. As long as the unchanged objects are correctly constructed (this means that object references are not allowed to be transited from the constructor), they are exempt from the requirements for Synchronous access because they cannot be changed, therefore, there is no possible write-write conflict or read-write conflict.

Without synchronization, You can freely share references to unchanged objects among threads. This greatly simplifies the process of writing concurrent programs and reduces the number of potential concurrent errors that may exist in programs.

Security in the face of malicious code
Methods that treat objects as parameters should not change the state of those objects unless explicitly stated in the document that they can be done, or actually these methods have the ownership of the object. When we pass an object to a common method, we usually do not want the object to be returned to be changed. However, this is exactly the case when a mutable object is used. Ifjava.awt.PointPassedComponent.setLocation()Does not blocksetLocationModifyPointWill not block setlocation from storing the reference to this point and change it later in another method. (Of course,ComponentDo not do this because it is not reckless, but not all classes are so polite .) Now,PointThe status has been changed without our knowledge, and the result is potentially dangerous-when the point is actually in another position, we still think it is in the original position. However, ifPointIs unchanged, so this malicious code cannot modify the state of our program in such a confusing and dangerous way.

Good keys
Unchanged object produces the bestHashMapOrHashSetKey. Some mutable objects change theirhashCode()Value (for exampleStringHolderEXAMPLE Class ). If you use this variable objectHashSetAnd then the object changes its status.HashSetImplementation causes confusion-if the enumeration set is used, the object will still appear, but ifcontains()Query a set, which may not appear. Needless to say, this will cause some chaotic behavior. The code in Listing 2 that illustrates this situation will print "false", "1", and "moo ".

Listing 2. Variable stringholder class, not suitable for use as a key

   public class StringHolder {        private String string;        public StringHolder(String s) {            this.string = s;        }        public String getString() {            return string;        }        public void setString(String string) {            this.string = string;        }        public boolean equals(Object o) {            if (this == o)                return true;            else if (o == null || !(o instanceof StringHolder))                return false;            else {                final StringHolder other = (StringHolder) o;                if (string == null)                    return (other.string == null);                else                    return string.equals(other.string);            }        }        public int hashCode() {            return (string != null ? string.hashCode() : 0);        }        public String toString() {            return string;        }        ...        StringHolder sh = new StringHolder("blert");        HashSet h = new HashSet();        h.add(sh);        sh.setString("moo");        System.out.println(h.contains(sh));        System.out.println(h.size());        System.out.println(h.iterator().next());    }

When to use the unchanged class
The unchanged type is most suitable for representing the values of abstract data types (such as numbers, enumeration types, or colors. Java class library (suchInteger,LongAndFloat) Are unchanged, and other standard numeric types (suchBigIntegerAndBigDecimal. Classes that represent the plural or any rational number of precision will be more suitable for immutability. Even abstract types (such as vectors or matrices) that contain many discrete values are suitable for implementation as immutable classes, depending on your application.

Flyweight Mode
The flyweight mode is enabled for immutability. Sharing makes it easy to use objects to effectively represent a large number of fine-grained objects. For example, you may want to use an object to represent each character in a word processing document or each pixel in an image, however, the naive implementation of this policy will produce a staggering cost for memory usage and memory management overhead. In the flyweight mode, the factory method is used to allocate references to unchanged fine-grained objects, and share the number of objects by matching only one object instance with the letter ". For more information about the flyweight mode, see classic books.Design Patterns(Gamma waits; see references ).

Another good example of immutability in Java class libraries is:java.awt.Color. In some color representation (such as RGB, HSB, or CMYK), color is usually expressed as a group of ordered numeric values, but a color is treated as a specific value in the color space, instead of an orderly set of independent addressable valuesColorIt makes sense to implement as a constant class.

If the object to be represented is a container with multiple basic values (such as vertex, vector, matrix, or RGB color), is it represented by a variable object or a constant object? The answer is ...... Depends on the situation. How to use them? Are they mainly used to represent multi-dimensional values (such as pixel colors), or are they only used as containers for a set of related features (such as window height and width) of other objects? How often will these features be changed? If you change them, does each component value have its own meaning in the application?

An event is another good example that is applicable to the implementation of the same class. Events have a short life cycle and are often consumed in threads other than the threads where they are created. So what makes them unchanged is the advantage and disadvantage. Most AWT event classes are not implemented as strictly unchanged classes, but can be slightly modified. Similarly, it may be wise to make the message object unchanged in a system that uses a certain form of message transmission for inter-component communication.

Compile the principle of unchanged classes
It is easy to write unchanged classes. If the following values are true, the class will remain unchanged:

  • All its fields are final
  • This class is declared as final
  • Not AllowedthisReferences escape during construction
  • Any contains variable objects (such as arrays, collections, or similarDate) Referenced fields:
    • Is Private
    • Never returned or exposed to the caller in other ways
    • Is a unique reference to the objects they reference.
    • After construction, the status of the referenced object will not be changed

The last set of requirements seems complicated, but it basically means that if you want to store references to arrays or other mutable objects, make sure that your class has exclusive access to the variable object (because otherwise, other classes can change its status), and you do not modify its status after the construction. This complexity is necessary to allow constant object storage to reference arrays, because the Java language cannot force the modification of final array elements. NOTE: If array references or other variable fields are initialized from the parameters passed to the constructor, you must use preventive measures to copy the parameters provided by the calling program or other information that you cannot ensure exclusive access to the array. Otherwise, the caller modifies the array status after calling the constructor. Listing 3 shows the correct methods (and Error Methods) for compiling the constructor of an unchanged object that stores the array provided by the calling program ).

Listing 3. correct and incorrect methods for encoding unchanged objects

class ImmutableArrayHolder {  private final int[] theArray;  // Right way to write a constructor -- copy the array  public ImmutableArrayHolder(int[] anArray) {    this.theArray = (int[]) anArray.clone();  }  // Wrong way to write a constructor -- copy the reference  // The caller could change the array after the call to the constructor  public ImmutableArrayHolder(int[] anArray) {    this.theArray = anArray;  }  // Right way to write an accessor -- don't expose the array reference  public int getArrayLength() { return theArray.length }  public int getArray(int n)  { return theArray[n]; }  // Right way to write an accessor -- use clone()  public int[] getArray()       { return (int[]) theArray.clone(); }  // Wrong way to write an accessor -- expose the array reference  // A caller could get the array reference and then change the contents  public int[] getArray()       { return theArray }}

Through some other work, you can write and use some non-final field unchanged classes (for example,StringStandard implementation and usehashCodeValue calculation), which may be better than strict final class execution. If the class represents the value of an abstract type (such as a number or color), you will also want to implementhashCode()Andequals()Method.HashMapOrHashSetOne of the keys works well. To ensure thread security, do not allowthisIt is very important to refer from the constructor.

Occasionally changed data
Some data items keep constants during the life of the program, and some change frequently. Constant data is obviously immutable, and objects with complex states and frequent changes are usually not implemented using the same class. What about the data that sometimes changes but is not frequently changed? Is there any way to makeSometimesWhat are the advantages of immutability convenience and thread security for changed data?

util.concurrentPackageCopyOnWriteArrayListClass is a good example of how to utilize immutability and allow occasional modifications. It is most suitable for the use of classes that support event listening programs (such as user interface components. Although the list of Event Listeners can be changed, the list of changes is usually much less frequent than that of events.

Except when you modify the list,CopyOnWriteArrayListInstead of changing the basic array, it creates a new array and discards the old array.ArrayListClasses are very similar. This means that when the caller obtains the iterator (the iterator saves reference to the basic array internally), the array referenced by the iterator is actually unchanged, this eliminates the risk of synchronization or concurrent modification. This eliminates the need to clone the list before traversal or synchronize the list during the traversal. Both operations are troublesome, error-prone, and completely degrade the performance. If traversal is more frequent than insertion or removal (which is common in some cases ),CopyOnWriteArrayListWill provide better performance and more convenient access.

Conclusion
Using unchanged objects is much easier than using mutable objects. They can only be in one state, so they are always consistent. They are inherently thread-safe and can be freely shared. Using a constant object can completely eliminate many programming errors that are prone to but difficult to detect. For example, you cannot access these errors synchronously between threads or clone the array or object before storing the reference to the array or object. When writing a class, it is always worthwhile to ask whether the class can be effectively implemented as a constant class. You may be surprised when the answer is always positive.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.