Tips
"Effective Java, third Edition" an English version has been published, the second edition of this book presumably many people have read, known as one of the four major Java books, but the second edition of 2009 published, to now nearly 8 years, but with Java 6, 7, 8, and even 9 of the release, the Java language has undergone profound changes.
In the first time here translated into Chinese version. For everyone to learn to share.
Classes and interfaces are at the heart of the Java programming language. They are the basic units of abstraction. The language provides a number of powerful elements that can be used to design classes and interfaces. This chapter contains guidelines to help you make the most of these elements, making your classes and interfaces usable, robust, and flexible.
15. Minimizing the accessibility of classes and members
The most important factor separating the well-designed components from the poorly designed components is that the components hide their internal data and other implementation details of other components. A well-designed component hides all of its implementation details, cleanly separating its API from its implementation. The components then communicate only through their APIs, and are unaware of each other's internal work. This concept, known as information hiding or encapsulation, is the basic principle of software design [PARNAS72].
Information hiding is important for many reasons, most of which come from the separation of components that make up the system, allowing them to be independently developed, tested, optimized, used, understood, and modified. This accelerates system development because components can be developed in parallel. It relieves the burden of maintenance because components can be understood more quickly, debugging or replacing components without worrying about damaging other components. Although the information hiding itself does not lead to good performance, it can be effectively tuned: Once the system is complete and the analysis determines which components are causing performance issues (entry 67), you can optimize these components without affecting the correct components of others. Information hiding adds to software reuse, because loosely coupled components are often proven useful in environments other than developing them. Finally, hiding information reduces the risk of building large systems, because individual components may be available even if the system is not operational.
Java provides a number of mechanisms to help with information hiding. Access control mechanism [jls,6.6] Specifies the accessibility of classes, interfaces, and members. The accessibility of an entity depends on where it is declared and which access modifiers (private,protected and public) exist in the declaration. Proper use of these modifiers is critical to information hiding.
The rule of thumb is simple: make every class or member as inaccessible as possible . In other words, use the lowest possible level of access, consistent with the corresponding functionality of the software you are writing.
For top-level (non-nested) classes and interfaces, there are only two possible access levels: package-level Private (package-private) and public. If you declare a top-level class or interface with the public modifier, it is exposed; otherwise, it is package-level private. If a top-level class or interface can be made as a package-level private, then it should be. By setting it as a package-level private, you can use it as part of the implementation, rather than the exported API, you can modify it, replace it, or eliminate it in subsequent releases without worrying about damaging existing clients. If you make it public, you are obligated to support it forever to maintain compatibility.
If a package-level private top-level class or interface is used by only one class, consider this class as a private static nested class (entry 24) that uses its unique class. This reduces the accessibility of it from all classes at the package level to one class that uses it. However, reducing the accessibility of unnecessary public classes is more important than the top-level classes that are private to the package level: The public class is part of the package's API, and the package-level private top-level class is already part of the package implementation.
For members (attributes, methods, nested classes, and nested interfaces), there are four possible levels of access, which are listed from small to large in terms of accessibility:
- private--This member can only be accessed within the top-level class in which it is declared.
- The package-private--member can be accessed from any class in the declared package. Technically, if you do not specify an access modifier (except for interface members, it is public by default), this is the default access level.
- protected--members can be accessed from subclasses of the class being declared (subject to some restrictions, jls,6.6.2), and any class in the package it declares.
- public--the member can be accessed from anywhere.
After carefully designing your class's public API, your response should be to have all other members designed to be private. Only if other classes in the same package really need to access members, you need to remove the private modifier, which makes the member package private at the package level. If you find yourself doing this often, you should re-examine the design of your system and see if another decomposition may produce better decoupled classes. That is, private members and package-level private members are part of the class implementation and generally do not affect their exported APIs. However, if the class implements the Serializable interface (entries 86 and 87), these properties can be "leaked (leak)" into the exported API.
For members of public classes, accessibility increases significantly when the access level is private to the protected level from the package. The protected (protected) member is part of the class-exported API and must always be supported. In addition, the protected members of the exported class represent a public commitment to the implementation details (entry 19). The need for protected members should be relatively small.
There is a key rule that limits your ability to reduce the accessibility of your methods. If a method overrides a superclass method, its access level in the subclass cannot be lower than the access level in the parent class [jls,8.4.8.3]. This is necessary to ensure that instances of subclasses are available in instances of the parent class (Liskov substitution principle, see entry 15). If this rule is violated, the compiler generates an error message when it attempts to compile the subclass. A special case of this rule is that if a class implements an interface, all class methods in the interface must be declared public in the class.
To facilitate testing your code, you may want to make it easier for a class, interface or member to be accessed. That's fine. It is acceptable to test that private members of the public class are designated as package-level private, but it is unacceptable to raise to a higher level of access. In other words, it is unacceptable to promote testing as part of a class, interface, or member as an API for package-level export. Fortunately, this is not necessary because the test can be run as part of the package being tested, thus gaining access to the private elements of the package.
instance properties of public classes are seldom exposed (item:). If an instance property is a non-final or a reference to a Mutable object, by exposing it, you discard the ability to limit the values that can be stored in the attribute. This means that you give up the ability to execute invariants that involve the property. In addition, when a property is modified, the ability to take any action is discarded, so the class of the public mutable property is usually not thread-safe . Even if the property is final and references an immutable object, by making it public, you discard the flexibility to switch to the new internal data representation of the nonexistent attribute.
The same recommendation applies to static properties, with one exception. Suppose constants are an integral part of the abstraction of a class, and you can public static final
expose constants through properties. By convention, the names of these attributes are made up of uppercase letters, which are separated by underscores (entry 68). It is important that these properties contain values of the base type or references to immutable objects (entry 17). A property that contains a reference to a Mutable object has all the drawbacks of a non-final property. Although references cannot be modified, the referenced objects can be modified and result in catastrophic results.
Note that arrays that are not 0-length are always mutable, so the class has a public static final array property, or an accessor to return such a property is wrong . If a class has such a property or access method, the client will be able to modify the contents of the array. This is a common source of security vulnerabilities:
// Potential security hole!public static final Thing[] VALUES = { ... };
Be careful with the fact that some IDE-generated access methods return references to private array properties, causing the problem. There are two ways to solve this problem. You can make the public array private and add a common immutable list:
private static final Thing[] PRIVATE_VALUES = { ... };public static final List<Thing> VALUES =Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
Alternatively, you can set the array to private and add a public method that returns a copy of the private array:
private static final Thing[] PRIVATE_VALUES = { ... };public static final Thing[] values() { return PRIVATE_VALUES.clone();}
To choose between these methods, consider how the client might handle the returned results. Which type of return is more convenient? Which one would be better to perform?
In Java 9, two additional implicit access levels were introduced as part of the module system. A module contains a set of packages, just as a package contains a set of classes. The module can explicitly export some packages (this is the Convention included in the source file of the Module-info.java) through the export sound in the module declaration. The public and protected members of the non-exported package in the module are inaccessible outside the module, and accessibility is not affected by the export declaration in the module. Using the module system allows you to share classes between modules without making them visible to the entire system. In a package that is not exported, the members of public and protected public classes produce two implicit access levels, which are similar internally to the ordinary public and protected levels. This shared requirement is relatively rare and can be eliminated by rearranging the classes in the package.
Unlike the four primary access levels, these two module-based levels are primarily recommendations (advisory). If the module's jar file is placed in the application's classpath instead of its module path, the package in the module reverts to the non-modular behavior: All public classes and protected members of the public class of the package have their ordinary accessibility, regardless of whether the package is exported by the module [reinhold,1.2]. The newly introduced access level is strictly enforced by the JDK itself: the non-exported packages in the Java class Library are truly inaccessible outside the module.
For a typical Java programmer, not only the access protection provided by the program module is limited, but is largely recommended in nature; In order to use it, you must combine your package into modules, clear all dependencies in the module declaration, rearrange your source tree hierarchy, and take special action to accommodate any access to non-modular packages within your module [Reinhold, 3]. It is too early to say whether the module will be widely used outside the JDK. At the same time, unless you have an urgent need, it seems best to avoid them.
In summary, the accessibility of the program elements (within reasonable limits) should be minimized as much as possible. After carefully designing a minimized public API, you should prevent any scattered classes, interfaces, or members from becoming part of the API. In addition to the public static final property as a constant, public classes should not have public properties. Make sure that public static final
the object referenced by the property is immutable.
Effective Java Third edition--15. Minimizing the accessibility of classes and members