Writing high-quality code: 151 suggestions for improving Java programs (Chapter 1: classes, objects, and Methods ___ suggestion 47 ~ 51), java151

Source: Internet
Author: User

Writing high-quality code: 151 suggestions for improving Java programs (Chapter 1: classes, objects, and Methods ___ suggestion 47 ~ 51), java151
Recommendation 47: Use getClass in equals for type determination

In this section, we will continue to discuss the problem of overwriting equals. This time we write an Employee class to inherit the Person class. This is normal. The Employee is also a Person, and the inheritance in the JavaBean is also widely seen, the Code is as follows:

 1 public class Employee extends Person { 2     private int id; 3  4     public Employee(String _name, int _id) { 5         super(_name); 6         id = _id; 7     } 8  9     public int getId() {10         return id;11     }12 13     public void setId(int id) {14         this.id = id;15     }16 17     @Override18     public boolean equals(Object obj) {19         if (obj instanceof Employee) {20             Employee e = (Employee) obj;21             return super.equals(obj) && e.getId() == id;22         }23         return false;24     }25 26 }27 28 class Person {29     private String name;30 31     public Person(String _name) {32         name = _name;33     }34 35     public String getName() {36         return name;37     }38 39     public void setName(String name) {40         this.name = name;41     }42 43     @Override44     public boolean equals(Object obj) {45         if (obj instanceof Person) {46             Person p = (Person) obj;47             if (null == p.getName() || null == name) {48                 return false;49             } else {50                 return name.equalsIgnoreCase(p.getName());51             }52         }53         return false;54     }55 }

The employee class adds the employee ID attribute and overwrites the equals method. It indicates the same employee only when the name and ID are the same, this is to avoid employees with the same name and surname in a company. Take a look at the code above. The conditions here are quite complete and there should be no errors. Let's test the Code as follows:

1 public static void main (String [] args) {2 Employee e1 = new Employee ("Zhang San", 100); 3 Employee e2 = new Employee ("Zhang San", 1000 ); 4 Person p1 = new Person ("Zhang San"); 5 System. out. println (p1.equals (e1); 6 System. out. println (p1.equals (e2); 7 System. out. println (e1.equals (e2); 8}

The preceding two employees and one social worker are defined. Although they have the same name and surname, they are certainly not the same. The output should be false, but the result after running is true false.

It is not awesome. p1 is equivalent to e1 and e2. Why are two instances of different classes even equal? This is very simple, because p1.equals (e1) is used to call the equals method of the parent class Person for determination. It uses the instanceof keyword to check whether e1 is an instance of Person. Because of the village inheritance relationship between the two, the result is true, and equality is no problem, but in turn it is not true. e1 and e2 cannot be equal to p1, which is a typical case against the symmetry principle.

What is more mysterious is that p1 is equal to e1 and e2, but e1 and e2 are not equal. It seems that a simple symbol transfer cannot be achieved. This is the focus of our analysis: e1.equals (e2) the equals method of the sub-class Employee is called, not only to determine the same name, but also to determine the same Id, the two Employee numbers are different, not equal is also natural. The equation is not passed because it violates the principle of pass-through of equals,For instance objects x, y, and z, if x. equals (y) returns true, y. equals (z) returns true, then x. equals (z) should also return true.

The key to this situation is that the parent class references the instanceof keyword, which is used to determine the instance object of a class, which makes it easy for the subclass to drill down. To solve this problem, it is easy to use getClass instead of instanceof for type determination. After the equals method of Person is modified, it is shown as follows:

@Override    public boolean equals(Object obj) {        if (null != obj && obj.getClass() == this.getClass()) {            Person p = (Person) obj;            if (null == p.getName() || null == name) {                return false;            } else {                return name.equalsIgnoreCase(p.getName());            }        }        return false;    }

Of course, considering that the Employee may also be inherited, you also need to change its instanceof to getClass. In short, we recommend that you use getClass to determine the type when overwriting equals, instead of using instanceof.

Suggestion 48: The hashCode method must be overwritten when the equals method is overwritten.

To override the equals method, you must override the hasCode method. Basically, every Java er knows this rule. This is also explained repeatedly on the jdk api. But why do you want to do this? What is the relationship between the two methods? This suggestion is to explain the problem. Let's look at the code first:

Public class Client48 {public static void main (String [] args) {// the instance of the Person class serves as the map key Map <Person, Object> map = new HashMap <Person, object> () {put (new Person ("Zhang San"), new Object ());}}; // The instance of the Person class acts as the List element List <Person> list = new ArrayList <Person> () {add (new Person ("Zhang San "));}}; boolean b1 = list. contains (new Person ("Zhang San"); boolean b2 = map. containsKey (new Person ("James"); System. out. println (b1); System. out. println (b2 );}}

The Person class in the Code is the same as the Person class in the previous suggestion, and The equals method is perfect. In this Code, we call the method assignment directly during the declaration, which is actually an internal Anonymous class. The problem is whether the values of b1 and b2 are both true?

Let's take a look at b1. the equals of the Person class is overwritten. Instead of judging whether two addresses are equal, we can judge whether two objects are equal based on the Person's name, therefore, no matter how many objects are generated by our new Person ("James"), they are all equal. Put Michael Jacob into the List and check whether the List contains it. The result is true.

Next, let's look at b2. We use Michael's object as the Key of Map and put it in Michael Jacob. Check whether the object is Michael Jacob, which should be the same as the result of List, unfortunately, the result is false. Why?

  The reason is that the underlying Processing Mechanism of HashMap stores Map entries in arrays. The key isArray subscript processing mechanism: determines the subscript of the Array Based on the returned value of the input element hashCode method. If a Map entry already exists in the array position and is equal to the input value, it is not processed, if they are not equal, they will be overwritten. If there are no entries in the array position, they will be inserted and added to the linked list of Map entries. Similarly, checking whether the key exists also determines the Location Based on the hash code, and then traversing to find the key value.

  Next, let's dive into the value returned by the hashCode method of the object element? It is an Object's hash code generated by the local method of the Object class, ensuring that each Object has a hash code (alsoBasic requirements of the hash algorithm: Any input k is converted to non-reversible output by using a certain algorithm f (k). For two inputs k1 and k2, If k1 = k2, f (k1) = f (k2) is required, but k1 is also allowed! = K2, f (k1) = f (k2)).

In our example, because we didn't overwrite the hashCode method, the values returned by the hashCode methods of two three objects (that is, the hash code) must be different, the corresponding Map entries cannot be found in the HashMap array, so false is returned.

The problem is clear and the modification is simple. Just rewrite the hashCode method in the Person class. The Code is as follows:

class Person{   @Override    public int hashCode() {        return new HashCodeBuilder().append(name).toHashCode();    }   }

HashCodeBuilder is a hash code generation tool under the org. apache. commons. lang. builder package. It is very convenient to use. You can integrate it directly in the project (why not directly write the hashCode method? Because there are many algorithms used to generate hash codes, which are difficult to write and have many things on your own, it is the best way to get "tailism" only when necessary .)

Suggestion 49: We recommend that you override the toString method.

Why do we need to override the toString method? This problem is very simple, because the default toString method provided by Java is unfriendly and cannot be printed out, but it cannot be overwritten. Read the following code:

Public class Client49 {public static void main (String [] args) {System. out. println (new Person ("James") ;}} class Person {private String name; public Person (String _ name) {name = _ name;} public String getName () {return name;} public void setName (String name) {this. name = name ;}}

The output result is: Perso @ 188edd79. if the machine is different, the content after @ will also be different, but the format is the same: class name + @ + hashCode, this stuff is for the machine, how can people understand it! This is because we did not override the toString method of the Object class. Modify the Code as follows:

@Override    public String toString() {        return String.format("%s.name=%s", this.getClass(),name);    }

In this way, debugging information can be output as needed, and it is very friendly, especially in popular bean projects (this is the case for general Web projects ), with this output, debugging can be better done; otherwise, it will be a little troublesome to find errors! Of course, when there are many bean attributes, it is not desirable to implement it by yourself. However, you can directly use the ToStringBuilder class in the apache commons toolkit, which is simple, practical, and convenient. Some may say, why does the toString method call to print an object through the println method? That is because of the println printer: if it is an original type, it is printed directly. If it is a class type, the return value of its toString method is printed. At the same time, IDE is also very advanced. You can view the object variables during debugging, but we recommend that you override the toString method, which makes debugging easier.

Suggestion 50: Use the package-info class to serve the package

Java has a special class: package-info class, which is specifically for this package service. Why is it special? It is mainly reflected in three aspects:

The package-info class has several special features, such as the inability to inherit, the absence of interfaces, and the absence of inter-class relationships (associations, combinations, and aggregation, since there is such a special class in Java, it must have a special role. Let's take a look at its special role, mainly in the following three aspects:

  • Declare a friendly class and an access constant in the package: This is relatively simple and practical. For example, if a package contains many internal access classes or constants, they can be put in the package-info class, this is convenient and easy to manage in a centralized manner. The Code is as follows:
class PkgClazz {        public void test() {        }    }        class PkgConstant {        static final String PACKAGE_CONST = "ABC";    }

Note that the above Code is placed in the package-info.java, although it does not write the package-info implementation, but the package-info.class class file will still be generated. With this definition, we place all the constants and classes required by a package under this package, which makes programmers more adaptable in terms of semantics and habits.

  • Convenience is provided for Annotation on the package: for example, if you want to write an Annotation and view the objects under the package, you only need to add the Annotation to the package-info file, this method is also used in many open-source projects, such as @ namespace of struts2 and @ FilterDef of hibernate.
  • Provide an overall description of the package: If a package is under development, that is to say, a package implements a business logic, Function Point, module, or component, the package requires a good description, this document describes the usage of this package, the version change history, and the logical relationship with other packages. The role of the package-info file has been brought into play here, these can be directly defined in this file. When a document is generated through javadoc, these instructions are used as the homepage of the package document, making it easier for readers to understand the package as a whole. Of course, the role of volume is the same as that of package.html. However, package-info can maintain the integrity of the document in the Code and update the code and document synchronously.

You can also use the IDE tool to create package-info, for example:

  

After so much explanation, we can conclude that the special class package-info can be considered where the package needs to be used, which may play a multiplier effect.

Suggestion 51: do not take the initiative to recycle garbage

A long time ago, in the age of java1.1, we often saw System. gc-a call like this-actively recycles garbage. However, after a deep understanding of Java, such code gradually disappears-this is a good phenomenon, because it is a very dangerous action to take the initiative to recycle garbage.

The danger is caused by System. gc must stop all the responses to check whether there are recyclable objects in the memory. This is highly risky for an application system. If it is a Web application, all requests will be paused, after the garbage collector is executed, it is acceptable if the heap memory (heap) contains fewer objects. 1. There are more objects (the larger the current web project is, there are more frameworks and tools, and more objects are loaded into the memory.) This process is very time-consuming, which may be 0.01 seconds, 1 second, or even 20 seconds, this seriously affects the business operation.

For example, we write a code like this: new String ("abc"). This object has no reference and is a junk object for JVM. When the JVM Garbage Collector thread scans for the first time (the scan time is uncertain and is executed when the system is not busy), It tags it and says, "You can recycle it ", during the second scan, the object is recycled and the memory space is released. If we call System directly. gc, it means "Hi, you, the garbage collector, come and check if there are any garbage objects and recycle them ". You can see that the program takes the initiative to find the Garbage Collector, which means that the running system will give up resources for the garbage collector to execute. Think about it, it will check all the objects, and then dispose of those spam objects. Note: Check every object.

Do not call System. gc. Do not call it even if there is frequent memory overflow. memory overflow can be analyzed to find the cause. GC is not a good practice.

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.