(1)Java语言支持四种类型:接口、类、数组、基本类型(primitive),前三种为引用类型,而基本类型的值不是对象。
(2)方法的签名包括它的名称和所有参数的类型,签名不包括它的返回类型。
(1)静态工厂有名称
(2)不必再每次调用都创建一个新的对象
public static Boolean valueOf(boolean b){ return b? Boolean.TRUE : Boolean.FALSE; }
(3)可以返回原返回类型的任何子类对象
适用于返回类型为接口/抽象类
public interface Service { // Service-specific methods go here } public interface Provider { Service newService(); } public class Services { private Services() { } // Prevents instantiation (Item 4) // Maps service names to services private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>(); public static final String DEFAULT_PROVIDER_NAME = "<def>"; // Provider registration API public static void registerDefaultProvider(Provider p) { registerProvider(DEFAULT_PROVIDER_NAME, p); } public static void registerProvider(String name, Provider p){ providers.put(name, p); } // Service access API public static Service newInstance() { return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance(String name) { Provider p = providers.get(name); if (p == null) throw new IllegalArgumentException( "No provider registered with name: " + name); return p.newService(); } }
客户端
public class Test { public static void main(String[] args) { // Providers would execute these lines Services.registerDefaultProvider(DEFAULT_PROVIDER); Services.registerProvider("comp", COMP_PROVIDER); Services.registerProvider("armed", ARMED_PROVIDER); // Clients would execute these lines Service s1 = Services.newInstance(); Service s2 = Services.newInstance("comp"); Service s3 = Services.newInstance("armed"); System.out.printf("%s, %s, %s%n", s1, s2, s3); } private static Provider DEFAULT_PROVIDER = new Provider() { public Service newService() { return new Service() { @Override public String toString() { return "Default service"; } }; } }; private static Provider COMP_PROVIDER = new Provider() { public Service newService() { return new Service() { @Override public String toString() { return "Complementary service"; } }; } }; private static Provider ARMED_PROVIDER = new Provider() { public Service newService() { return new Service() { @Override public String toString() { return "Armed service"; } }; } }; }
(4)创建参数化实例更简洁
public static <K,V> HashMap<K,V> newInstance(){ return new HashMap<K,V)(); }
(5)静态工厂惯用名称
valueOf,of,getInstance,newInstance,getType,newType
构造器的参数太多的话,调用方容易混淆,特别是针对相邻的同类型的参数,如果不小心将顺序颠倒,则编译时难以发现,运行时出问题。
(1) 重叠构造器版本,好处是安全性,坏处是难以阅读
public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // optional private final int fat; // (g) optional private final int sodium; // (mg) optional private final int carbohydrate; // (g) optional public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); } public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27); } }
(2)JavaBean版本,好处是可读性,坏处是自己要保证线程安全
public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // " " " " private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() { } // Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27); } }
(3)Builder版本,兼容二者好处
public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder { // Required parameters private final int servingSize; private final int servings; // Optional parameters - initialized to default values private int calories = 0; private int fat = 0; private int carbohydrate = 0; private int sodium = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this; } public Builder fat(int val) { fat = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). calories(100).sodium(35).carbohydrate(27).build(); } }
newInstance方法可充当build方法的一部分,但是它总是企图去调用类的无参构造器,如果类没有无参构造器,编译时是不会暴露出来的,只能到客户端调用运行时才暴露。
(4)实践版
一般比如sql构造之类的,参数太多,可以用builder模式,普通bean的赋值,采用builder反而搞得太过负责,工业上的最佳实践,一般是采用JavaBean + Design by Contract 的模式,要求开发者根据约定传参数,service层再进行一层校验,来确保必填参数是不为null的。
(1)私有构造器能够防止通过反射调用去构造实例
(2)为确保反序列化之后还是单例,需要重写readResolve方法,return INSTANCE,或者直接使用枚举单例,它内置了反序列化单例的功能。
(1)达到尽量重用对象的目的,比如字符串字面常量,不可变的对象。
反例
public class Person { private final Date birthDate; public Person(Date birthDate) { // Defensive copy - see Item 39 this.birthDate = new Date(birthDate.getTime()); } // Other fields, methods omitted // DON'T DO THIS! public boolean isBabyBoomer() { // Unnecessary allocation of expensive object Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); Date boomStart = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); Date boomEnd = gmtCal.getTime(); return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0; } }
正解:使用静态初始化器,初始化起止日期
class Person { private final Date birthDate; public Person(Date birthDate) { // Defensive copy - see Item 39 this.birthDate = new Date(birthDate.getTime()); } // Other fields, methods /** * The starting and ending dates of the baby boom. */ private static final Date BOOM_START; private static final Date BOOM_END; static { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); BOOM_START = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); BOOM_END = gmtCal.getTime(); } public boolean isBabyBoomer() { return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0; } }
(2)优先使用基本类型,而不是封装类型,当心无意识的自动装箱额外产生的对象,下面的sum声明为long,即可减少2的31次方个多余的Long实例。
public class Sum { // Hideously slow program! Can you spot the object creation? public static void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } }
(3)对象创建的误解
误解:对象创建的代价非常昂贵,应该避免创建对象。
正解:小对象的创建,构造器只做很少量的显式工作,其创建和回收都是非常廉价的,因而对小对象没必要搞什么对象池之类的;只有重量级的对象,比如数据库连接池等,连接数据库代价是昂贵的,采用对象池正好。
public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { 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]; } /** * Ensure space for at least one more element, roughly * doubling the capacity each time the array needs to grow. */ private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } }
出栈的时候没有清空引用
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result; }
(1)清空对象引用应该是一种例外,而不是一种行为规范,不必每次使用变量的时候,都紧张兮兮的,考虑要不要在使用后赋值为null
(2)内存泄露的常见来源
A、自己管理内存的容器
B、缓存
C、监听器和回调方法(注册监听,不需要的时候没有取消监听)
(1)JVM不保证finalize方法会被及时执行,而且根本不保证它们会被执行
(2)不要依赖finalize方法去关闭重要资源,比如关闭文件、关闭数据库连接,一般采用finally语句即可
(3)使用finalize可能会造成额外的性能损失