Effective JAVA学习心得

effective java学习总结

创建和销毁对象
1、静态工厂方法结合单例代替构造器
1) 避免每次都创建一个新对象
2) 能确切地描述正被返回的对象
3) 可以返回原返回类型的任何子类对象
但也要考虑到静态工厂创建的实例不能作为父类被其他类继承,同时其命名也需规范化。
思考:Spring ioc的对bean也是单例的,创建过程中把bean放在三级HashMap缓存中,每次使用注入使用从缓存中拿去,没有对应实例则创建,多方autowired使用同一bean。对于那些不用Spring容器管理,变动少,却常使用的类,使用静态工厂创建是性能优化的方式之一

2、遇到多个构造器参数时要考虑用Builder模式
静态工厂和构造器不能很好地扩展到大量的可选参数
1)使用必须的参数调用构造器,得到一个 Builder 对象,再在 builder 对象上调用类似 setter 的方法设置各个可选参数,最后调用无参的 build 方法生成不可变对象,new Instance.Builder(必须参数).setter(可选参数).build()。
Student student = new Student.Builder()
.name(“张三”).age(35).build();
Builder 模式让类的创建和表示分离,链式调用,简洁明了,易读性高,灵活性高。对单元测试构建测试用例的效率与可读性明显提高。同时 idea支持builder的构建模式。

3、避免创建不必要的对象
一般来说,我们要重用一个相同功能的新对象。重用方式既快速,又流行。如果对象是不可变的,它就始终可以被重用。
比如,String str = new String("") 和String str ="",String是不可变的,如果使用第一种,jvm会重新创建一个String对象,而第二种则会复用字符冲池的对象
在方法中使用局部变量是,优先使用基本类型而不是装箱的基本类型,也要注意无意识的自动装箱。比如 int sum = 0 和 Integer sum =0;

4、消除过期的对象引用
我们知道Java的垃圾回收机制会进行内存管理,但对象的回收是根据可达性分析策略的,如果栈中堆积了大量临时变量,没有及时清理,是会影响服务性能的,甚至可能造成oom。
对于一旦对象引用过期,将它们设置为 null。使用Map缓存数据时优先使用WeakHashMap,LinkedHashMap 这些数据结构,及时清掉没用的项。

对于所有方法都通用的方法
1、 覆盖 equals
类Object中有equals()这个方法,该方法用于比较两个对象内存地址是否相等,即两个引用是否指向同一个对象。但某些业务场景下我们想根据对象的某些属性判定对象是否相等,比如guid,使用equals时比较就需要重写了,

2、覆盖 equals 时总是覆盖 hashCode
在object规范中,相等的对象必须具有相等的散列码,如果没有一起去覆盖 hashcode,则会导致俩个相等的对象未必有相等的散列码,造成该类无法结合所有基于散列的集合一起工作。比如使用HashMap存储object时,其存放规则一般是拉链法hash(key)%len,以object的hash值求余

类和接口
1、使类和成员的可访问性最小化
我们在抽象封装类时,要尽可能的对类的属性隐藏内部实现细节,有效解耦,尽量少的对外暴露,避免安全问题,private、protected、默认、public 四种修饰符在不同的jdk版本中可访问性是有调整的

2、复合优于继承
继承就是父类和子类的关系,复合则是引用原本的超类,在选择使用复合还是继承时要考虑父类是否还有拓展性,父类中api实现是否有缺陷,这些都会传递到子类的,

枚举和注解
1、用 enum 代替 int 常量
在业务中经常有status字段,或者一些不可变的常量,通常在Consts类中用public static fianl… 定义,用enum去替代一组固定的常量,更安全,使用也更方便。jdk8还至此set和map的enum,拓展了其使用范围

Lambda和Stream
1、 谨慎使用Stream
lambda表达式结合stream无疑使代码更简洁,其提供的api,fileter、map、collect等一系列api,对代码书写是十分方便的。过度使用流去处理复杂的计算是难以阅读和维护。但在统一转换元素序列、
过滤元素序列、使用单个操作组合元素序列 (例如添加、连接或计算最小值)、
将元素序列累积到一个集合中,可能通过一些公共属性将它们分组
在元素序列中搜索满足某些条件的元素。等场景使用是十分方便的。

2、优先选择Stream中无副作用的函数

方法
1、 检查参数的有效性
在调用方法前先对参数进行有效性检验,避免因为参数的不规范造成接口调用的失败

2、 返回零长度的数组或者集合,而不是null
在代码扫描中,有许多都是返回值为空,对放回结果忽略了而造成的bug, 引起NullPointerException异常,有效的处理方法之一就是返回空集合或空对象。

3、 为API元素编写文档注释
规范的文档注释是对于代码的阅读于后期的维护是帮助极大的,需要养成写有效性注释的习惯,idea的可定制类、方法等的注释模板。

通用规范
1、将局部变量的作用域最小化
在方法中我们往往在方法一开始就定义局部变量,这对方法的维护性和阅读性是不友好的,将局部变量的作用域最小化,在需要使用某局部变量的时候,才定义它,使逻辑小而集中,如果一个方法包含多种操作,便于抽取小方法。

2、for-each循环优先于传统的for循环
传统for循环
for (int i = 0; i < a.length; i++) {
… // Do something with a[i]
}

增强for语句(for-each)
for (Element e : elements) {
… // Do something with e
}

很明显传统for循环容易在索引与列表大小方面出问题,同时在嵌套循环中也可能出现过界。增强for语句在清晰度,灵活性和错误预防方面都优于传统for循环

3、接口优先于反射机制
反射机制 java.lang.reflect 提供对任意类的编程访问。给定一个 Class 对象,可获取对象的构造器、方法、字段。但如果一个程序试图反射性地调用一个不存在的或不可访问的方法,它将在运行时失败,同时阅读困难,性能也较低。平常业务代码是不建议使用反射的,运用于设计部分

4、了解字符串连接的性能,字符串大量拼接时避免使用String
String字符串是不可变的,每一次拼接都是创建一个新对象,当大量字符串进行拼接时时间复杂度为n的平方,且耗内存。可使用StringBuilder 或StringBuffer代替

异常
1、 对可恢复的情况使用受检异常,对编程错误使用运行时异常
java有三种可抛出异常,受检的异常(checked exception),运行时异常(run-time exception)和错误(error)。对于调用或逻辑方面的异常,catch捕获后能够给予开发者提示。而运行时异常和错误则应该抛出,不让其执行下去,如果想要自定义其抛出格式,可继承runtimeException。

2、 抛出与抽象对应的异常
当底层抽象抛出的异常与所执行的任务没有明显联系时,会困扰对高层api的实现形成困扰,底层抛出的异常应该是实际可能产生的,而不是用Exception大包大揽,代码中这种问题很常见,同时,高层应该对底层的异常进行捕获,避免异常的直接抛出

3、努力使失败保持原子性
异常抛出后,我们要保证数据恢复到执行前的状态,保证数据操作的原子性,我们第一反应可能是通过事务,操作失败时数据回滚。一下方法在编写代码时也是可以考虑的。
调整计算处理过程的顺序,使得任何可能会失败的计算部分都在对象状态被修改之前发生,即提前检查执行条件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值