写高效的java代码

如何书写高效的java代码一直以来就是从初级程序员升级为中高级程序员的关键。effective java这本书带给了我很多启发,以下是读书整理:

首先整体上一个java程序运行,从代码上看,有创建对象,对象初始化(赋值),对象执行相关方法,方法运行时需要考虑运行环境(单线程,多线程),方法运行时出现异常,对象跨jvm执行,方法执行完成考虑销毁对象,程序运行结束

因此关于以上的程序运行的每个步骤,effective java都给出了很好的建议:

1.关于创建对象和销毁对象:

(1)考虑使用静态工厂方法替代构造器,每次调用构造器,都会产生一个该类的对象,有时候对象产生极为耗时以至于你想使用以前已经创建好的对象,有时候对象参数相同但构造出来的内容不同(考虑到建立坐标系。极坐标和直角坐标,都是两个参数且参数类型相同),构造器名字都相同以至于在想创建某种更具有相关意义的对象时会表意不清,等等一系列问题,产生了使用静态工厂方法替代构造器的思路

(2)多构造器参数时考虑构建器 即使用builder模式,这很容易理解,虽然在现在的编译器(IDEA)里面这种方式并没有那么有用,但是只要有一点好处,欸什么不用呢?

(3)关于singleton的相关创建方式就不多说了,在这里我有一个同学新提出了一种构建单例的模式,所谓单例,就是在该class下就只有一个对象,因此上static字段实际上只有一个对象拥有,那么static对象作为class的全局对象就没有什么意义了,因此你可以声明一个成员变量和方法均是static修饰的类,隐藏构造器,提供静态工厂来返回实例,因此他是真正意义上的单例,绝不会有第二个,但是这种方式存在一些问题,首先,他彻底泯灭了产生多例的可能性,比方说一家工厂有3台打印机,这种方式无能为力了。第二,在序列化与反序列化时会出现一些问题,不能序列化包含static修饰的字段,那么该实例就完全无法序列化了。第三,存在性能上的一些考虑,当第一次加载类时所有static都会被加载,因此延迟加载对于他完全没有意义,在一些需要延迟加载的场合这种方式就会失败。但还是佩服这位同学。现在构建单例正转向于使用enum类型来实现,没有上述的问题。

(4)通过私有化构造器强化不可实例化的能力,不仅如此还会使其无法子类化,当你确实不想他被继承,不妨添加final关键字彻底拒绝子类化

(5)避免创建不必要的对象,此处主要针对那些大对象。因为对于小对象的创建与销毁,现代jvm已经优化的很不错。纵使如此,这条建议依然有用,最后当遇到某些对象是通过小对象组合起来的,考虑flywefight模式(蝇量模式),对于大对象的创建像数据库连接,线程等考虑相应的池,

(6)消除过期的对象引用,主要是指数组方面的内容


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);

    }

}
 

错误会出现在pop方法中。由于并没有将pop之前的元素置为null他将不会被垃圾处理器回收。正确的做法是:


public Object pop() {

    if (size == 0)

        throw new EmptyStackException();

    Object result = elements[--size];

    elements[size] = null // Eliminate obsolete reference

    return result;

}

实话讲,一开始我也不明白怎么回事,因为我一直以来都是按第二种方法写的,猛然看到第一种写法一时回不过神来,后来仔细想想确实是这么回事,此时考虑下垃圾回收的条件,关键四个字 孤岛效应

(7)避免使用终结方法 ,从来不用。。。。

2.对于所有对象都通用的方法:

(1)equals 离散数学的一致性成立的三个条件:自反性,对称性,传递性 覆盖equals时必须满足这三个。普遍做法是:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Man)) return false;

    Man man = (Man) o;
    //递归调用各个字段的equals方法进行比较
   
}

(2)覆盖equals时总要覆盖hashcode方法,因为两个对象相同的话,他们的hashcode必相同,这是规范,规范的意思就是,你可以不同,但是你不同了之后依赖这些方法的其他方法出现了某些不和语义的问题全是你的错。

(3)始终要覆盖toString,这是一个好习惯,不管是debug还是测试

(4)谨慎覆盖clone, 确实需要谨慎,复习一些深复制与浅复制

(5)考虑实现Comparabale接口,这会让你排序起来很方便,对于依赖此接口的sort等一系列方法来说,这很简单的实现就得到了很大的益处。

3.类与接口

(1)将类和成员的可访问性最小化,一条指导性原则,并没有给出相应的做法,几条建议:实例域决不能是public ,长度非零的数组总是可变的,千万不能使数组域public ,保持被final修饰的域真正不可变,因为final修饰的引用本身不能修改,但是引用的对象却可以修改,不妨对final域private 并提供一个getter返回该域的一份拷贝

(2)在public的类使用访问方法而非public域,原因与做法与上一条相同

(3)努力使可变性最小化,即努力使类不可变,像String,Integer这样的类都是不可变的类,他们容易设计何使用,做法是 不提供域的setter ; 保证类不可extends,即用final修饰该类 ; 使所有域都是final ; 使所有域都private ; 确保互斥访问

(4)复合优于继承 不多说,基本素养,很多设计模式都是基于复合的

(5)上一条的稍微修正,要么为继承而设计类,要么禁止继承

(6)接口优于抽象类 废话 !!!!一个多继承,一个单继承 更何况现在接口允许有默认实现

(7)接口只允许定义类型,因此就不要往接口里面添加添加常量了

(8)类层次由于标签类

(9)用函数对象表示策略 策略模式的复习 

(10)优先考虑静态成员类  是说当一个类明确的只会被一个类使用时,将其设计为静态成员类 

---------------------------------------------------------------分割线---------------------------------------------------------------

4.泛型

(1)请不要在新代码里面使用原生态类型 这不废话么 能用泛型就用泛型啊

(2)消除非受检警告 这一条依赖于IDE 

(3)List由于数组,数组可变,List是不可变的而且List有泛型能消除很多能在编译期就发现的错误保证类型安全

(4)考虑泛型方法 ,以及使用有限制的通配符,尽量不要在支持泛型的地方不加泛型。

比如List list=new ArrayList<Integer>() ;×  尽量这么写List<Integer> list=new ArrayList<Integer>();

(5)优先考虑使用类型安全的异构容器 如ConcurrentMap等等 ,性能同普通Map并不差但是功能上更强

5.枚举和注解 

6.方法篇

(1)检查参数有效性,比方说对象参数不能为null 数组不能越界啦等等,在方法开始的时候就检查。尽量早的检查错误人为的抛出异常而不是等待运行时抛出异常

(2)必要时进行保护性拷贝 即在有可能发生异常的地方预先进行参数拷贝以便发生异常后恢复

(3)谨慎设计方法签名,慎用重载 给出的指导办法:1.参数名称选择JLS规范提及的;2.参数列表保持在4-5个以内,如果不能,那就分解方法或者构造辅助类,参数类型选择接口或者抽象类而不是具体类以降低耦合;对于重载(overloading)方法的选择是静态的在编译时决定,对于被覆盖(overriding)的方法的选择是动摇的,在运行时决定!

(4)返回长度为0的List而不是null ,原话是返回零长度的数组或者集合,而不是null,但是因为List的诸多优点为什么还是使用数组而不是List呢,在这里可能和参数检查那一部分有所覆盖,但是我们要基于这么一种观点,不能纯粹依赖程序员去做所有的参数检查并确定参数检查总是那么正确,只要有那么一点可以确保程序能正确执行且毫不费力,就算是冗余又算得了什么

7.通用程序设计

(1)将局部变量作用域最小化 什么时候用什么时候声明,我们目标是创建结构良好,容易理解的程序 ,不是炫技能!  

(2)尽量使用foreach而非传统的for循环,更不要试图使用异常机制(会发生嵌套的异常的危险)去遍历,这不会显得你很厉害,相反在很多人眼中这样的人很傻

(3)要是希望像货币这样的精确结果,放弃使用float和double 尝试使用int或者专门设计的类

(4)关于字符串连接 当连接次数很少时使用+很简单 ,当连接次数很多时,使用Stringbuilder的append方法,而且StrngBuffer已经是过去式了,或者使用String的concat方法

(5)别总是想着优化,除非有很大的必要,良好的代码结构所带来的速度上的提升远比优化来的大

(6)JLS值得一读 java语言规范

8.

(1)异常只是针对异常的地方才使用异常,像之前那个遍历的例子就很不好,更不要使用异常去控制程序执行流程

(2)优先使用标准异常,并给异常更多的信息,并试图从一场名字上获得大致信息

使用率很大的异常:https://www.cnblogs.com/cvst/p/5822373.html

(3)不要忽略异常,在很多框架中都忽略了异常,异常的产生永远是不能被忽略的,如果不知道怎么处理,那么就在此抛出就好

猜你喜欢

转载自blog.csdn.net/WK_SDU/article/details/81222822
今日推荐