Last week for a bunch of reasons for preparing for the exam, no time to read, make up a little today.
Classes and interfaces are at the heart of the Java programming language, and they are the basic abstraction unit of the Java language, which provides a number of powerful basic elements for programmers to design classes and interfaces, and this chapter is about guiding principles for designing more useful, robust, and flexible classes and interfaces.
1th: Minimizing the accessibility of classes and members
First of all, a concept: The module can only communicate through their APIs, a module does not need to know the internal work of other modules, the concept is called " information hiding ", or " encapsulation ". (yes, this is the encapsulation of the object-oriented encapsulation of one of the three main characteristics of the inheritance polymorphism)
Information hiding is important because: it can effectively remove the coupling between the various modules of the system, so that these modules can be independently developed, tested, optimized, used, understood and modified. This can speed up the development of the system, while also reducing the burden of maintenance.
The Java language provides many mechanisms to assist in information hiding. The access control mechanism determines the accessibility of classes, interfaces , and members. The accessibility of an entity is determined by the location where the entity is declared, and by the access modifiers (private,protected,public) that appear in the entity declaration. The first rule is that every class or member is not accessible to the outside world as much as possible.
For members, there are 4 access levels, arranged in ascending order of accessibility:
Private: can only be accessed within the top-level class that declares the member.
Package Private: Any class within the bundle that declares the member can access the member, which is the default access level (that is, if the access level is not specified, it is the packages private. )
Protected: The subclass of the class that declares the member can access the member, and any class inside the package that declares the member can be accessed.
Public: This member can be accessed anywhere.
When judging the access level of a member, consider not only the extent to which we want the member to be exposed, but also some caveats:
1. The instance domain must never be public. Once the instance domain becomes common, the ability to read the values stored in this domain is discarded. Alternatively, for an instance domain, its own internal properties should be in its own hands, but if it is public, then everyone can take control of its internal values, and two countries can imagine that if a country's internal affairs are involved in another country, it feels good humiliating ... So, the object also has self-esteem! It must not be declared as public.
2. For final fields, if the final decoration is a constant, the constants can be exposed through public static final. However, if the final decoration is a reference, you cannot use public, because final is meant to not be modified, although the reference itself does not change, but the object it points to may change, which violates final intent.
3. Note that arrays that are not 0 in length are always mutable, so the class has a public static final array field, or an access method that returns this array field, which is always wrong. This is a common cause of security vulnerabilities because it allows the client to modify the contents of the array. For example:
public static fnal thing[] values= {...}; This is wrong.
This can be improved:
Private Static Final Thing[] private_values={...}; Public Static Final list<thing> VALUES = collections.unmodifiablelist (arrays.aslist (private_values));
or this:
Private Static Final Thing[] private_values={...}; Public Static Final thing[] VALUES () { return private_values.clone ();}
2nd: Use access methods instead of common domains in a common class
The simple thing to do is to use getter and setter methods to access the data in a common class instead of making the data public. Example:
class point{ // This is a bad notation for public double x ; Public Double y;}
classpoint{//This is a good way to use access methods to access common domains Private Doublex; Private Doubley; PublicPoint (DoubleXDoubley) { This. x =x; This. y =y; } Public DoubleGetX () {returnx;} Public DoubleGetY () {returny;} Public voidSetX (Doublex) { This. x =x;} Public voidSety (DoubleY) { This. y =y;} }
3rd: Make the variability to minimize
The immutable classes are introduced first: immutable classes are classes whose instances cannot be modified. All information contained in each instance must be provided at the time the instance is created and fixed throughout the lifetime of the object. There are many immutable classes in Java, such as String,biginteger,bigdecimal. Immutable analogy mutable classes are easier to design, implement, and use, and they are not error-prone and more secure.
There are 5 principles for designing immutable classes:
1. Any method that modifies the state of an object is not provided.
2. Ensure that the class is not extended. (Can be declared as final)
3. Make all domains final.
4. Make all domains private. This prevents clients from gaining access to mutable objects that are referenced by the domain being accessed, and prevents clients from modifying those objects directly.
5. Ensure mutually exclusive access to any mutable component. If your class has a domain that points to mutable objects, you must ensure that clients of that class cannot obtain a reference to those objects, and never initialize such a domain with an object reference provided by the client, or return the object reference from any access method. (For example, you domesticated a dog, you certainly do not want others can casually take it away, or you go to work, walking home is a samoyed, back to become a husky, please do not cry faint in the toilet ... )
Here is an example of an immutable class, plural, but too long I'm too lazy to play ... All right, here's a small one.
Public Final classcomplex{Private Final Doublere; Private Final Doubleim; PublicComplex (DoubleReDoubleim) {...} Public DoubleRealpart () {returnre;} Public DoubleImaginarypart () {returnim;}//here is the first one, and will not return the modified method PublicComplex Add (Complex c) {return NewComplex (re+c.re,im+c.im); } PublicComplex Subtract (Complex c) {return NewComplex (re-c.re,im-c.im); } ... @Override Public Booleanequals (Object o) {...} @Override Public inthashcode () {...} @Override PublicString toString () {...}
Well, this class is right here, so take a look at the basic arithmetic here: subtraction. Instead of modifying the current instance, they create and return a new instance, which is called the functional method, because these methods return the result of a function that operates on the operand but does not modify it. This corresponds to the "procedural of the Process" method, which causes the state of the operand to change.
Immutable objects are inherently thread-safe, and they do not require synchronization, so immutable objects can be freely shared. Not only do grams share immutable objects, they can even share their internal information.
The real only disadvantage of immutable classes is that a separate object is required for each different value. The cost of creating such an object can be high. If you perform an operation that takes a lot of steps, each step produces a new object, but we only use the final result, and the other objects are eventually discarded, which creates a performance problem. There are 2 ways to deal with this problem: 1. Guess the common operations and provide them as I basic type. 2. Provide a common variable companion class. such as String and StringBuilder.
If a class cannot be made immutable, it should still limit its variability as much as possible. So, unless there is a compelling reason to make the domain non-final, make each domain final.
4th: Compound takes precedence over inheritance
Unlike method invocations, inheritance breaks encapsulation, in other words, subclasses rely on implementation details for some of the features in the parent class. The implementation of the parent class may change due to changes in the version, and the functionality of the subclass may be broken, even if its code does not change.
Compositing does not extend an existing class, but instead adds a private domain to the new class that references an instance of an existing class. The existing class becomes a component of the new class.
In a composite, each instance method in the new class can invoke the corresponding method in an existing class instance that is contained and return its result, which is referred to as "forwarding." Like what
Public classForwardingset<e>ImplementsSet<e>{ Private FinalSet<e>s; PublicForwardingset (set<e> s) { This. S =s;} Public voidClear () {s.clear;}//forwarding Public BooleanContains (Object o) {returnS.contans (o);}//forwarding Public BooleanIsEmpty () {returnS.isempty ();}//forwarding ...}
Inheritance can cause problems: 1. A function in a subclass calls the parent class's SUPER.F (), and the parent class's F () is actually called the parent class's F2 () function, which is the implementation details, not the commitment, and does not guarantee that all implementations of the Java platform will be unchanged and may change depending on the version.
2. If a class that is not in the parent class is added to the subclass, unfortunately, the parent class also has a class with the same name in the next version, it may become an overriding method, or an overloaded method, or the method in the subclass cannot follow the conventions of the methods in the parent class.
If inheritance is used where it is appropriate to use the compound, the implementation details are exposed, so that the resulting API restricts you to the original implementation, which limits the performance of the class forever. More seriously, due to the exposure of internal details, it is possible for the client to directly access these details, resulting in semantic confusion, or even directly modifying the superclass, thus breaking the subclass constraints.
Before deciding to use inheritance instead of conforming, ask yourself the last set of questions: Is there a flaw in the AI for the class you are trying to extend? If so, would you like to spread those flaws into the API of the class?
In summary, you should use inheritance only if it is really a relationship.
5th: Either design for inheritance, provide documentation, or prohibit inheritance
For classes designed to inherit, the document must be comprehensive:
1. The document must accurately describe the impact of overwriting each method. For each public or protected method or constructor, the document must indicate which overridden methods are called by the method, in what order, and how the results of each call affect subsequent processing.
2. It must be documented in the documentation, and in which cases it will invoke a method that can be overridden.
For classes designed for inheritance, the only test method is to write subclasses.
6th: interface is better than abstract class
The difference between an interface and an abstract class is that an abstract class allows implementations of some methods, but interfaces are not allowed.
1. The existing classes can easily be updated to achieve new amounts of port.
2. Interfaces are ideal for defining mixed types.
3. The interface allows us to construct a non-hierarchical type framework.
By providing an abstract skeleton implementation class for each important interface you export, you can combine the advantages of an interface with an abstract class. The skeleton implementation class must study the interface carefully to determine which methods are the most basic, and other methods can be implemented according to them, and these basic methods will become the abstract methods in the skeleton implementation class. Then, provide a concrete implementation for the other methods in the interface.
7th: Interfaces are only used to define types
When a class implements an interface, the interface acts as a type that can reference instances of the class. Therefore, the class implements the interface, which indicates that the client can implement certain actions on instances of this class. It is inappropriate to define an interface for any other purpose.
The reason that interfaces should only be used to define types is because there is a bad use of interfaces in Java: constant interfaces. This is typical of the opposite, such as java.io.ObjectStreamConstants. Not worth the fire.
Effective Java reading notes-classes and interfaces