创建和销毁对象
概览
静态工厂代替构造器
优点:
静态工厂方法与构造器不同的第一大优势是,静态工厂拥有自己的名称。
静态工厂的第二大优势是,不必在每次调用它们的时候都创建一个新的对象。
静态工厂方法与构造器第三大优势是,可以返回原返回类型任何子类型的对象。
静态工厂的第四大优势是,在创建参数化实例时,他们可以使代码变得更简洁。
缺点:
类如何不含有公有的或者受保护的构造器,就不能被子类化
它们与其他的静态方法实际上没有任何区别。
举个栗子:
public class StaticFoodFactory {
// public StaticFoodFactory(String name){
// 在静态工厂中,由于不用实例化工厂类所以不需要
// }
public static Food createFood(String foodName){
if("香肠".equals(foodName)){
return new WangzhongWang();
}else if("胡辣汤".equals(foodName)){
return new hulatang();
}
return null;
}
}
当需要使用某一个类的时候采用:
Food hulatang=StaticFoodFactory.createFood(“胡辣汤”);
多个构造器参数是要考虑使用build
当某一个类的构造器拥有超级多的入参时,每次新建一个对象就要往里面传入多多多个根本不用设着的参数,或是为这些入参排列组合成一个新的构造器,这显然是很麻烦的。又或者采用javabean模式,采用Setter和getter方法实现,但由于JavaBean模式的构造过程是分散的,你可以在任何时候去set,这样这份Bean不是确定的,你怎么能确保这个对象是安全的.所以要采用build模式。
Build模式非常的灵活,但也有自身的不足,为了创建对象必须穿件它的build器,可以是内部类,也可以是外部类。会有性能的损耗,简而言之,如果类的构造器或者静态工厂中有多个参数,在设计这种类的时候,Build模式应该是首选的
//创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://fanyi.youdao.com/")
// 设置 网络请求 Url
.addConverterFactory(GsonConverterFactory.create())
//设置使用Gson解析(记得加入依赖)
.build();
Retrofit中的build是采用的内部类
/**
* Build a new {@link Retrofit}.
* <p>
* Calling {@link #baseUrl} is required before calling {@link #build()}. All other methods
* are optional.
*/
public static final class Builder {
private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
}
public Builder() {
this(Platform.get());
}
利用私有构造器或者枚举类型强化Singleton属性
Singleton指的是仅仅被实例化一次的类,优势也通常被用来表示那些本质上唯一的系统组件,比如文件系统和窗口管理器。
例如android的四种启动模式之一的SingleTask,以"singleTask"方式启动的Activity,全局只有唯一一个此Activity实例存在,当且仅当第一次启动这个Activity时,系统便会创建一个新的任务,并且初始化一个这样的Activity的实例。
对于Java实现单例模式:
public static final StaticFoodFactory Instance=new StaticFoodFactory();
private StaticFoodFactory(){
}
public static final StaticFoodFactory Instance=new StaticFoodFactory();
private StaticFoodFactory(){
}
public static StaticFoodFactory getInstance(){
return Instance;
}
使用构造器强化不可实例化的能力
再开发过程中会遇到共有变量类,他只包含静态方法和静态变量,他们作为工具类不需要被实例化就可以被使用,在缺少显示构造器的情况,编译器会提供一个共有的、无参的缺省构造器,需要保证其他开发人员不会错误的实例化此种工具类,采用私有的构造器。
class PubMessagerUtils{
public static String message="失效数据";
public static String Code="-999";
private PubMessagerUtils(){}
}
由于显示的构造器是私有的,所以不能在该类的外部访问它
避免创建不必要的对象
在开发过程中,如果经常操作字符串
public static void main(String[] args) {
String x=new String("dasdlakd");
String y=x+"sdahkd";
String z=new String(x+y+"djasdhaj");
StringBuilder t=new StringBuilder("hdkjasjhdka");
StringBuffer k=new StringBuffer("dakkdsa");
}
不要错误的使用并产生成千上万个String类型的对象,可以直接String str=”sas“;
但不要错误的理解“创建对象的代价非常高,所以不要创建对象”,每一个对象都是不同的,对于小对象来说创建和回收动作是非常廉价的。
注意:StirngBuilder线程不安全,高并发情况下考虑采用Stringbuffer。
消除过期的对象引用
在数据结构与算法一书中,经常出现这样Stack.clear和Queue.clear这些操作,因为这些数据结构会出现内存泄漏问题,随着垃圾回收器活动的增加,或者由于内存占用的不断增加,程序的性能降低会逐渐表现出来,在极端情况下,这种内存泄漏会导致磁盘交换设置导致OOM错误。
比如一个栈先增长在收缩,那么从栈中弹出来的对象不会被当作垃圾回收,即使使用的栈的程序不在引用这些对象,他们也不会被回收,因为栈中还维护对这些对象的过期引用。所谓过期引用就是再也不会被解除的引用
public class StackTest {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY=16;
public StackTest(){
elements = new Object[DEFAULT_INITAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop() throws Exception {
if(size==0){
throw new Exception();
}
return elements[size--];
}
/***
* 确保容量保持正常
*/
private void ensureCapacity(){
if(elements.length == size){
elements= Arrays.copyOf(elements,2*size+1);
}
}
}
如果想消除过期引用,则需改进
public Object pop() throws Exception {
if(size==0){
throw new Exception();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
清除栈中的过期引用。
当然内存泄漏还有可能来自于缓存,一旦把对象引用放到缓存中,就很容易被遗忘,从而是的它不再有用之后很长一段时间仍然留在缓存之中
内存泄漏第三个来源是监听器和其他回调。如果实现了一个API,客户端就会在API中注册回调,却没有显示的取消注册,除非采取某些动作,否则他们就会聚集,确保回调立即被处理的方法就是只保存他们的弱引用,这样可以减少垃圾清理的次数。
避免使用终结方法
终结方法finalizer通常是不可预测的,一般情况下是不必要的,也是很危险的
public static void main(String[] args) throws Throwable {
StackTest stackTest=new StackTest();
stackTest.finalize();
}
使用终结方法会导致行为不稳定,降低性能,以及可移植的问题。,终结方法的缺点是不能保证它会被及时的执行,从一个对象变得不可达开始,到他的终结方法被执行,所花费的这段时间是任意的,同时使用终结方法会导致严重的Server的性能损失。