您的位置:首页 > 编程语言 > Java开发

高效java

2020-02-17 05:17 148 查看

1.考虑用静态工厂方法替代构造器 
原因:

  • 有名称,便于理解所创建的对象
  • 可以使用单例,不必重复创建,节省内存
  • 相同的方法可以返回一个对象的不同类型的子类,适用于适配器等设计模式的场景

缺点: 无法识别一个方法是否是独特的静态工厂方法而不是一个静态方法,除非在方法上增加注释并在方法的命名上保留独有的instance关键字。

静态工厂方法的惯用名称:

  • valueOf
  • of(valueOf的简洁写法)
  • getInstance
  • newInstance
  • getType
  • newType

2.遇到多个构造器参数时考虑使用构建器 
原因: 多个构造器可行,但是代码量过多,且使用不太方便。且当参数个数过多的时候需要小心翼翼的审查构造器上所定义的参数类型和参数名称的排列顺序,否则很容易将2种相同参数类型的参数错位传入。 
还有一种替代方式即所谓的javabean模式通过setter方法在构造一个空对象后将属性一一set进去。 
致命缺陷:这种方式构造的对象必须和使用对象的方法处于相同线程,否则线程A在构造对象,线程B,C,D在使用对象。那么三个线程获取到的对象状态都可能不一致。不过这种使用场景也很罕见

构建器通过创建一个内置类Builder 并声明和目标类一致的属性,然后通过创建这个Builder的实例后将当前实例作为参数传递给目标类的构造方法。

public class A{
private long a;
private long b;
public static class Builder{
private long a;
private long b;
public Builder(){

}
public Builder setA(long a){
this.a = a;
return this;
}
public Builder setB(long b){
this.b = b;
return this;
}
public A build(){
return new A(this);
}
}
private A(Builder builder){
this.a = builder.a;
this.b = builder.b;
}
}

3.用枚举类型强化Singleton 
原因:一般的使用是提供一个静态公共方法调用私有构造器构造类的单例,但是这种形式可以通过反射和反序列化依旧生成多个实例。

  • 反射攻击的防御方式为,在构造器中添加异常或不作为,当单例对象存在的时候阻止新对象的构造。
  • 反序列化的防御方式为,增加readResolve方法返回当前类的单例,这样当反序列化构造对象的时候将使用单例对象接收序列化对象中的属性值。
private Object readResolve(){
return INSTANCE;
}

枚举单例无偿提供了序列化机制,本能的阻止多实例的构造,而且反射方式也没用,因为枚举类没有构造器,只在编译阶段才会动态生成Enum< T >类,除非你直接反射Enum< T >类但是那样调用的将不再是枚举类而是Enum< T >类。

4.避免创建不必要的对象 
善于利用单例对象和常量池

5.消除过期的对象引用 
消除过期的对象引用这一论述有误,只有特定场景下才存在过期引用。数组中的引用保留的目的是重复使用,即使没有重复使用的需求,当数组对象被GC后,它自身的那些过期引用一样会被GC掉,除非一个数组在整个程序运行期间都不销毁,那么它确实可能存在过期引用。 
所谓的过期引用的原因是:当一个整个程序运行期间都不销毁的数组曾经扩张到10000的size,然后现在有用的size只有10,那么10-10000下标之间存储的引用对象都是过期引用,但是引用本身就很小,除非一个变态的数组曾经扩张到上亿的下标,而当前有用下标只有10,且数组不会被销毁,那么确实就存在大量内存泄漏

6.避免使用终结方法 
每个对象都隐式的包含终结方法finalizer(),当GC回收对象的时候触发调用该方法。 
原因:GC的时间不可预测,finalizer方法调用的时机也就不可预测。

7.覆盖equals方法请遵守通用约定

  • 自反性---x.equals(x)必须返回true
  • 对称性---如果x.equals(y) = = true,则y.equals(x) = = true
  • 传递性---如果x.equals(y) = = true,且y.equals(z) = = true,则x.equals(z) = = true
  • 一致性---不修改对象属性的前提下,多次调用equals方法返回结果应当一致

8.覆盖equals时必须覆盖hashCode方法 
原因:导致无法同基于散列(hash)的集合一起工作

9.谨慎的覆盖clone方法 
原因:首先clone方法是浅克隆,其次该方法的使用前提是集成自Cloneable接口,并且以一种java底层极端的方式篡改了超类Object.clone方法的行为。

10.考虑实现Comparable接口

11.在公有类中使用访问方法而不是直接访问属性(setter,getter调用) 
原因:公有类不该直接暴露属性。一旦这样做了,这个类将是不可扩展的,因为很多地方直接使用了该类的属性,当这个属性被取代,将无法再为之前使用过的地方服务,而提供访问方法,可以在不改变方法名的情况下修改执行内容,达到扩展兼容的能力

12.不可变的类使可变性最小化(final class && final property) 
但是不理解为何要搞final类,一个不可扩展的类怎么适应变化的需求?所以不可变类一般不提供对外访问。

13.复合类优先于继承类 
原因:集成是代码重用的有力手段,但是随意的继承会导致软件变得脆弱。不过在同一个包下使用继承是非常安全的。因为都是同一个程序员的控制之下。对于具有很好的文档说明的超类来说,继承它也是非常安全的。但是对于随意的继承,肯定会引发严重的后果,继承打破了封装性,将一个功能的职能分摊到子类和超类的头上,当一个其它的类调用子类的继承方法,实际上它也同时在使用着这个子类的超类,那么当一个不是专职于提供继承的超类被继承了,甚至它自己都不知道自己有几个儿子,那么这个超类发生了变化,所有的子类的使用都将陷入桎梏之中!!!这种情况下,使用复合类。复合类是一个新的类,但是它的属性中包含了你需要使用的某个类的实例,当你需要使用该类方法的时候直接新声明一个方法再去调用自身属性中该类原本的方法。而当你需要修改该类方法的时候,直接重写一个新方法就行。不过这种方式可以对超类中的方法进行前置,后置或环绕增强,但是无法达到下面这样的效果:

public class Utils{
public void a(){
//do something
b();
//do something
}

public void b(){

}
}
public class NewUtils{
private Utils utils;

public void b(){
//增强行为
utils.b();// good work!
//增强行为
}

public void a(){
//增强行为
utils.a();//bad work!因为方法a中调用的方法b还是超类中的旧的方法。
//增强行为
}
↓ ----必须要完全的将超类中的方法重写一遍(麻烦)
public void a(){
//do something
b();
//do something
}
}

14.针对于上一条的问题禁用子类化 
对于设计目的本来就不是为了让别人继承的类,最好可以设计为final或者包含私有构造器

15.接口优于抽象类 
原因:继承只可以单继承。抽象类适合规范层次化的对象,而不属于层次化的东西用多个接口定义,按需实现它们。 例如: 
爷爷,爸爸,儿子。 
辈分是一个层次化的对象,因为一个对象不可能同时处于2个辈分,好吧,也许你既是某个人的儿子又是某个人的爸爸。

那么: 
男人,女人 性别总是一个层次化的对象了吧,你不可能既是男人又是女人,所以性别可以抽象类指代,男人和女人继承它

16.接口只用于定义类型(不要使用常量接口) 
原因:常量属于类的实现细节的组成部分,放在接口中,当这个接口需要提供给第三方调用,则暴露了内部细节。

17.类层次优于标签类 
原因:不要大锅饭一样的将不同对象的属性和职能臃肿的都塞入一个类中,这种做法极其不面向对象,应当抽出臃肿内容中具有共性的地方,将其设置为抽象类,然后不同的地方分门别类的继承这个抽象类,各自创建为不同的子类。 好处是:提高了可读性,按需构造不同的对象 20000 ,内存使用缩小,且避免了臃肿环境中容易触发的错误和异常。

18.用函数对象表示策略

待续......

lblin

转载于:https://my.oschina.net/u/3009826/blog/1623524

  • 点赞
  • 收藏
  • 分享
  • 文章举报
chilie7326 发布了0 篇原创文章 · 获赞 0 · 访问量 135 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: