原创

effective Java(item 1-item 10)--小浩带你学Java

One: Creating and Destroying Objects

Item 1:Consider static factory method instead of constructors

why?

1).Static factory methods is that, unlike constructors, they have names.

2).Static factory methods is that,unlike constructors, they are not require to create a new object each    

time they're invoked..

3).Static factory methods can return an object of any subtype of their return type.

4).Static method reduce the verbosity of creating parameterized type instances.

Attention:

1).The main disadvantage of providing only static factory methods is that classes without public or protected constructors cannot be subclassed.

2).Static factory method are not readily distinguishable from other static methods.


For example:

import java.util.*;
public class StaticFactoryMethods{

public static<K,V> HashMap<K,V> newInstance(){

return new HashMap<K,V>();
}

public static void main(String[] args){

//Constructor style:
//Map<String,List<String>> m=new HashMap<String,List<String>>();
//StaticFactoryMethods style:
StaticFactoryMethods hashMaps=new StaticFactoryMethods();
Map<String,List<String>> m=hashMaps.newInstance();
}
}


Item 2:Consider a builder when faced with many constructor parameters

when you faced a situation of that the constructor you created which including a lot parameters and

some of that parameters are optioded. I would consider  to use telescoping constructor pattern or JavaBean. But there is a very cool solution. It was builder pattern.

Show you the code directory first and then I would talk about it:

public class BuilderPattern{
public static void main(String[] args){
Car BMW=new Car.Builder(1,1,1,1).truck(1).
drivenLicense(1).seatBelt(1).hood(1).build();
System.out.println("BMW is a "+BMW.toString());
Car jeep=new Car.Builder(1,1,1,1).truck(1).
drivenLicense(1).build();
System.out.println("jeep is a "+jeep.toString());
}
}
class Car{
private final int brake;
private final int accelerator;
private final int clutch;
private final int gearshift;
private final int truck;
private final int drivenLicense;
private final int seatBelt;
private final int hood;

static class Builder{
//required parameters
private final int brake;
private final int accelerator;
private final int clutch;
private final int gearshift;
//optional parameters
private int truck=0;
private int drivenLicense=0;
private int seatBelt=0;
private int hood=0;

public Builder(int brake,int accelerator,
int clutch,int gearshift){
this.brake=brake;
this.accelerator=accelerator;
this.clutch=clutch;
this.gearshift=gearshift;
}

public Builder truck(int val){
truck=val;
return this;
}
public Builder drivenLicense(int val){
drivenLicense=val;
return this;
}
public Builder seatBelt(int val){
seatBelt=val;
return this;
}
public Builder hood(int val){
hood=val;
return this;
}

public Car build(){
return new Car(this);
}
}

private Car(Builder builder){
brake =builder.brake;
accelerator =builder.accelerator;
clutch =builder.clutch;
gearshift =builder.gearshift;
truck =builder.truck;
drivenLicense =builder.drivenLicense;
seatBelt =builder.seatBelt;
hood =builder.hood;
}
public String toString(){
return "Car: [brake:"+brake+", accelerator:"+accelerator+
", clutch:"+clutch+", gearshift:"+gearshift+
", truck:"+truck+", drivenLicense:"+drivenLicense+
", seatBelt:"+seatBelt+", hood:"+hood+"]";
}
}/*Output:
BMW is a Car: [brake:1, accelerator:1, clutch:1, gearshift:1, truck:1, drivenLicense:1, seatBelt:1, hood:1]
jeep is a Car: [brake:1, accelerator:1, clutch:1, gearshift:1, truck:1, drivenLicense:1, seatBelt:0, hood:0]
*///:~

If you choose telescoping constructor pattern to write the above codes, it is hard to write client code when there are many parameters, and harder still to read it. Of course, if you use a JavaBean to express the 'Car' class, it might be in an inconsistent state partway through its construction. But  Builder pattern can avoid such of problems. Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters, especially if most of those parameters are optional. Client code is mush easier to read and write with builders.

Also look like flexible and the code is absolute cool.

But, remember that Builder pattern should be used only if there are enough parameters.


Item 3:Enforce the singleton property with a private constructor or an enum type

I don't very understand what does that mean. Just simply thought that if you want to make a class to be singleton, you can use a private constructor or an enum type.

For example:

public class SingletonByPrivateConstructor{
public static void main(String[] args){
System.out.println(Water.WaterINSTANCE.toString());
System.out.println(Food.getFoodInstance().toString());
System.out.println(Meat.FISH);
}
}
//private constaructor
class Water{
private String name;
public static final Water WaterINSTANCE =new Water("water");
private Water(String name){this.name=name;}
public String toString(){return name;}
}
class Food{
private String name;
private static final Food FoodINSTANCE =new Food("food");
private Food(String name){this.name=name;}
public static Food getFoodInstance(){return FoodINSTANCE;}
public String toString(){return name;}
}
//enum type
enum Meat{
FISH;
}/*Output:
water
food
FISH
*///:~

Item 4:Enforce noninstantiability with a private constructor

That's very easy to understand. A class can be made noninstantiability by including a private constructor. It's also better to throw an exception in that constructor, for it provides insurance in case the constructor is accidentally invoked from within the class. It guarantees that the class will never be instantiated under any circumstances.

For example:

public class NoNinstantiability{
public static void main(String[] args){
Instantiability ins=new Instantiability();
}
}
class Instantiability{
private Instantiability(){
System.out.println("You cannot access me!");
throw new RuntimeException();
}
}/*Output:
NoNinstantiability.java:3: 错误: Instantiability()可以在Instantiability中访问private
Instantiability ins=new Instantiability();
^
1 个错误
*///:~


Item 5:Avoid creating unnecessary objects

The point is reuse an object instead of always create an object. As you can see from the example:

public class CreatingObject{
public static void main(String[] args){
int j=100000000;
long pre=System.currentTimeMillis();
createNewObject(j);
long post=System.currentTimeMillis();
System.out.println("createNewObject-->"+(post-pre)+"ms");
long Ipre=System.currentTimeMillis();
didnotCreateNewObject(j);
long Ipost=System.currentTimeMillis();
System.out.println("didnotCreateNewObject-->"+(Ipost-Ipre)+"ms");
}

public static void createNewObject(int j){
for(int i=0;i<j;i++){
String s=new String("aaa");
}
}
public static void didnotCreateNewObject(int j){
for(int i=0;i<j;i++){
String s="bbb";
}
}
}/*Output:
createNewObject-->18ms
didnotCreateNewObject-->1ms
*///:~


In my machine, it take 18ms to run the  createNewObject method while only take 1 ms to run the  didnotCreateNewObject method, which is about 18 times faster. So don't create unnecessary objects . There is a point you need to pay attention, which is always make the mistake in programming—the autoboxing. Remember the lesson: prefer primitives to boxed primitives, and watch out for unintentional autoboxing. Here's the prove:

public class AutoBoxing{
public static void main(String[] args){
long pre=System.currentTimeMillis();
boxedPrimitives();
long post=System.currentTimeMillis();
System.out.println("boxedPrimitives-->"+(post-pre)+"ms");
long Ipre=System.currentTimeMillis();
primitives();
long Ipost=System.currentTimeMillis();
System.out.println("primitives-->"+(Ipost-Ipre)+"ms");  
}
public static void boxedPrimitives(){
Long sum=0L;
for(Long i=0L;i<10000000L;i++){
sum+=1;
}
}
public static void primitives(){
Long sum=0L;
for(long i=0L;i<10000000L;i++){
sum+=1;
}
}
}/*Output:
boxedPrimitives-->219ms
primitives-->141ms
*///:~

Item 6: Eliminate obsolete object reference

Java is a garbage-collected language. It can easily lead to the impression that you don't have to think about memory management, but the fact is that this isn't not true. If a stack grows and shrinks, the objects that were popped off the stack will not be garbage collected, even if the program using the stack has no more references to them. For example:

import java.util.*;
public class StackMemoryLeak{
private Object[] elements;
private int size=0;
private static final int DEFAULT_INITIAL_CAPACITY=16;

public StackMemoryLeak(){
elements=new Object[DEFAULT_INITIAL_CAPACITY];
}

public void push(Object e){
ensureCapacity();
elements[size++]=e;
}

public Object pop(){
if(size==0)
throw new EmptyStackException();
return elements[--size];
}

private void ensureCapacity(){
if(elements.length==size)
elements=Arrays.copyOf(elements, 2*size+1);
}
}
The pop method will lead memory leak.
To solve this problem is very simple: null out references once they become obsolete. The corrected version of the pop like this:
public Object pop(){
if(size==0)
throw new EmptyStackException();
Object result= elements[--size];
elements[size]=null;
return result;
}

But, you should keep in mind that nulling out object references should be the exception rather than the norm. Generally speaking, whenever a class manages its own memory, the programmer should be alert for memory leaks. Whenever an element is freed, any object reference contained in the element should be null out. Besides array, there are two situation you need to take care, which are caches,  listener and callbacks for API.


Item 7: Avoid finalizers

Finalizers are unpredictable, often dangerous, and generally unneccessary. Their use can cause erratic behavior, poor performance, and portability problems.

1).Not only does the language specification provide no guarantee that finalizers will get executed promptly; it provides no guarantee that they'll get executed at all;

2).If an uncaught exception is thrown during finalizers, the exception is ignored, and finalization of that object terminates. Uncaught exception can leave object in a corrupt state;


So what should you do instead of of writing a finalizer for a class whose objects encapsulate resources that require termination, such as files or threads? Just provide an explicit termination method. Explicit termination methods are typically used in combination with the try-finally construct to ensure termination. This is very common in coding. for example, I show you the code of loading drive:

...
static{
try{
pp=new Properties();
fis=DBUtil.class.getClassLoader().getResourceAsStream("com/hsp/utils/dbinfo.properties");
pp.load(fis);
url=pp.getProperty("url");
username=pp.getProperty("username");
driver=pp.getProperty("driver");
password=pp.getProperty("password");
Class.forName(driver);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
fis.close();
}catch(Exception e){
e.printStackTrace();
}
fis=null;
}
}
…

However, there are two legitimate uses for finalizers:

1).Acting as a “safety net” in case the owner of an object forgets to call its explicit termination method;

2).Concerning object with native peers;


And you need to note that ”finalizer chaining” is not performed automatically. If a class has a finalizer and a subclass overrides it, the subclass finalizer must invoke the superclass finalizer manually or use finalizer guardian.


Two: Methods Common to All Objects

Item 8:Obey the general contract when overriding equals

This item tell you what you should obey during overriding equals method. Look here:

x, y, z are not-null reference

1).Reflexive: x.equals(x) must return true;

2).Symmetric:x.equals(y) must return true if and only if y.equals(x) returns true;

3).Transitive:if x.equals(y) return true and y.equals(z) return true, then x.equals(z) must return true;

4).Consistent:if x.equals(y) consistently return true or false , provided no information used in equals comparisons on the objects is modified;

5).x.equals(null) must return false;


Here's a recipe for a high-quality equals method:

1).Use the == operator to check if the argument is a reference to this object;

2).Use the instanceof operator to check if the argument has the correct type;

3).Cast the argument to the correct type;

4).For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object;

5).When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent?


Item 9: Always override hashCode when you override equals

Remember that you must override hashCode in every class that override equals because the key provision that is violated when you fail to override hashCode is the second one: equals objects must     have equal hash codes. Override hashCode is more complicated than override equals. A good hash function tends to produce unequal hash codes for unequal objects. This is exactly what is meant by the third provision of the hashCode contract. Here are some point:

1).Store some contract nonzero value, say, 17, in an int variable called result.

2).For each significant field f in your object(each field taken into account by the equals method, that is), and make a compute.

I. If the field is a boolean, compute(f?1:0).

II. If the field is a byte, char, short, or int, compute(int) f.

III. If the field id a long, compute(int) ( f  ^ (f >>>32)).

IV. If the field is a float, compute Float.floatToIntBits(f).

V. If the field is a double, compute Double.doubleToLongBits(f), and then hash the resulting long as in step III.

VI. If the field is an object reference and this class's equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field.

VII. If the field is an array, treat it as if each element were a separate field.

3).Combine the hash code c computed in step 2). into a result as follows:

result =31 * result + c;

4). when you are finished writing the hashCode method, ask yourself whenther equal instances have equal hash codes. Write unit tests to verify your intuition!


Item 10: Always override toString

while java.lang.Object provides an implementation of the toString method, the string that it returns is generally not what the user of your class wants to see. For example:

public class ToString {

public static void main(String[] args){
ToString ts=new ToString();
System.out.println(ts.toString());

}

}/*Output:
ToString@51de8adb
*///~

All right, I don't like the result, its boring. So keep in mime that when practical, the toString return all of the interesting information contained in the object. You can specifying the format that you want to see, such a unambiguous, human-readable representation of the object.


Here is a simple example about how to override equals which include overriding hashCode and toString:

import java.util.*;
public class EqualsHashCodeToString{

public static void main(String[] args){

Set<Bird> Birds=new HashSet<Bird>();
Bird b1=new Bird(new DNA(1001),"lily");
Bird b2=new Bird(new DNA(1001),"lily");
Birds.add(b1);
Birds.add(b2);
for(Bird b : Birds){
System.out.println(b.toString());
}
}
}
class DNA{
private int id;

public int getId(){
return this.id;
}

public void setId(int id){
this.id=id;
}

public DNA(){}

public DNA(int id){this.id=id;}

//override equals
public boolean equals(Object d){
return d instanceof DNA &&
id == ((DNA)d).id;
}

//override toString
public String toString(){
return "DNA id is "+id;
}

//override hashCode
public int hashCode(){
return 17*id;
}
}

class Bird{
private DNA dna;
private String name;

public DNA getDna(){
return this.dna;
}

public void setDna(DNA dna){
this.dna=dna;
}

public String getName(){
return this.name;
}

public void setName(String name){
this.name=name;
}

public Bird(){}

public Bird(DNA dna,String name){
this.dna=dna;
this.name=name;
}

//override equals
public boolean equals(Object b){
return b instanceof Bird &&
name.equals(((Bird)b).name) &&
dna == ((Bird)b).dna;
}

//override toString
public String toString(){
return "This bird is "+name+", "+dna;
}

//override hashCode
public int hashCode(){
int result=17;
result=37*result+dna.hashCode();
result=37*result+name.hashCode();
return result;
}
}/*Output:
This bird is lily, DNA id is 1001
This bird is lily, DNA id is 1001
*///~

Trying to change the equals method return true and see what happened.



关注下方微信公众号“Java精选”(w_z90110),回复关键字领取资料:如HadoopDubboCAS源码等等,免费领取资料视频和项目。 

涵盖:程序人生、搞笑视频、算法与数据结构、黑客技术与网络安全、前端开发、Java、Python、Redis缓存、Spring源码、各大主流框架、Web开发、大数据技术、Storm、Hadoop、MapReduce、Spark、elasticsearch、单点登录统一认证、分布式框架、集群、安卓开发、iOS开发、C/C++、.NET、Linux、Mysql、Oracle、NoSQL非关系型数据库、运维等。

相关推荐

评论

分享:

支付宝

微信