effective java 读书笔记

前言

仅记录学习笔记,如有错误欢迎指正。

Java支持4种类型:接口、类、(包括Enum)、数组和基本类型,前面3者都是引用类型,类实例和数组是对象(所以数组是存放在jvm的堆区!),而基本类型的值则不是对象。
区别:基本数据类型指的是值的拷贝,而对象类型指的是值的引用。所以二者的存放区域也不一样。

书中提出以静态工厂方法代替构造器,并列举以下几点优势:

  • 静态工厂方法可以有自己的名称(可以避免多个构造器用户不知道调用哪个的问题)[x]它是单例的,提升性能[x]静态工厂方法可以返回原返回类型的任何子类型对象(返回继承的子对象)[x]返回的参数可以随着每次调用的变化而发生变化(举了一个枚举的例子,在枚举元素数量一定大小内返回这种子类型,超过了返回另一种子类型)
  • 方法返回的对象所属的类,在编写包含该静态工厂方法的类是可以不存在。也有如下几点缺点:[x]类中如果不含共有的或者protected的构造器就不能子类化[x]它们和普通的静态方法没有什么区别,程序员很难注意到这是一个代替构造器的方法。
  • 如果需要一个不可实例化的类,可以建立一个私有的构造函数(private),企图通过将类做成抽象类来强制该类不可实例化是行不通的,该类可以被子类化,并且该子类也可也被实例化!

String.matchs()会创建一个pattern实例,而创建这个实例的代价很高,所以它并不适合在注重性能的情况下使用,不过我们可以为其创建一个final常量,在类初始化的时候就给他缓存起来,以后每次调用就重用同一个实例。

内存泄漏的3种可能性:

  • 过期对象的引用,书中举了一和栈对象弹出的元素无法被回收的例子,这把这些对象的值置为null。
  • 来自缓存的内存泄露,这一部分的对象很容易被遗忘掉,解决办法是用 WeakHashMap来作为缓存对象,它只要没有外部对 key的引用,垃圾回收器就会回收它。
  • 来自监听器的和其他回调: 如果客户端在你实现的API中注册回调,却没有显示的取消,那么就会积聚。需要确保回调立即被 当作垃圾回收的最佳方法是只保存他的若引用,例如将他们保存成为WeakHashMap中的键。

补充网上的一些可能性:

  • 静态集合类,这些对象生命周期都是和程序一致的,在程序结束前不能被释放的话,就会有内存泄漏。 ( 长生命周期的对象持有短生命周期对象的引用)
  • 各种连接:IO、数据库等等,连接没被关闭的话,就会有大量对象无法被回收
  • 变量不合理的作用域:中间变量的作用域可能使用完没置空,导致无法回收。
  • 在jdk1.7后,可以使用try-with-resource来关闭各种连接了, 被自动关闭的资源需要实现Closeable或者AutoCloseable接口内部自带close方法来关闭资源!

对于equals()和hashcode()方法,原本这两个方法都是object里面的,equals原本是用于比较基本类型的值是否相等,引用类型的引用是否相等,但是对用Integer或者String来说,程序员并不关心他们是否只指向同一个对象,他们更希望知道它们是否在逻辑上相等,所以有了重写的必要。
关于重写equals 必须要重写hashcode():
在map或者set中,它们是根据hashcode()的值来标识元素的存储位置的,假若String的equals()方法没有重写hsahcode(),在map中我们用了String类型的字段作为key,当两个对象equals相等时,hashcode的值并不相等,会导致相等的对象并不具备相等的散列码,所以get的时候也不一定会得到你想要的对象。

定义接口时,最好使类的和成员的可访问性最小化,方便解耦。

final对象有很多优点,它本质上使线程安全的,可以自由的共享对象,以及对象的内部信息,原子性它为其他对象提供了大量的构件,比如map的key,set的元素,都无需担心它们的值会变化。缺点就是,每个不同的值都需要一个单独的对象。Stringbuild/String

设计类时,最好让类是“不可变类”,就是成员变量都不能被修改,它们更容易设计,实现和使用,不容易出错并且安全。除非有令人信服的理由,否则每个域都是private final 的

静态成员类优于非静态成员类;

  • [x]静态成员类最好把它看成普通的类,只是碰巧被声明再在另一个类的内部而已,它可以访问外围类的所以有成员,包括 私有的成员变量!~类似于一个静态变量。也要遵守可访问性规则。[x]从语法来讲:静态成员类和非静态成员类的唯一区别是,静态的声明包含static,而非静态的成员类都与外围类的实例相关联, 在非静态成员类的内部可以调用外围类实例的方法,也能创建静态成员变量。
  • [x]如果非静态内部类无需调用外围实例,最好声明为静态内部类,因为在引用非静态内部类时,它们的实例都要维护一个指向 外围对象的引用,这样很容易造成内存泄漏。
  • [x]匿名类也属于内部类的一种,它不和其他成员一起被声明,而是在使用的同时被声明和实例化。当它出现在非静态的环境 中时,他才有外围实例; 它是在某个地方需要特殊的实现,因此在该处编写其实现,并获取它的实例,调用它的方法。不要 在匿名内部类编写其他的方法,是不可见的。
  • [x]局部类也是属于内部类的一种,可以声明局部变量的地方,都可以声明局部类,它有名字,可以被重复使用,但是只有在非 静态的环境下才有外围实例。
  • [x]匿名类和局部类都是在方法内部定义的,如果只需要在一个地方创建实例并且已经有了一个预置的类型可以说明这个类的| 特征,就要把它作为匿名类,否则为局部类!!

对于泛型,为了避免在对象转换之间出现错误,使用泛型可以在编译期间就能被告知是否插入了类型错误的对象,使程序更加安全,也更加简单化。

  • Set是个参数化类型,表示可以包含任何对象的一个集合;Set<?>表示一个通配符类型,表示只能包含未知对象类 型的一个集合;而Set是一个原生态类型,它脱离了泛型系统,前两种是安全的,最后一种不安全。
  • 列表优于数组,因为List还可以使用泛型,而数组是“具体化的”,在运行中你才能知道你插入的数据是否正确。
  • 为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符的类型。 如果参数化类型为生产者T,就使用<? extend T>;为消费者T,就使用<? super T>; 这样最大限度的保证了类型的成功转换!

枚举,没有可访问的构造器,所以它是真正的final类,也没有可创建的实例对象,只存在声明过的枚举常量。它们是单例的,安全的。并且可读性良好。

  • 枚举是在类加载的时候初始化的,当你需要一组固定常量,并且想在编译期间就知道其成员的时候,应该使用枚举[]每隔枚举都会有一个ordinal方法,他保存着枚举的位置下标,但最好不要用它来做其他事,很麻烦。。
  • 虽然无法编写可拓展的枚举类型,却可以通过编写接口以及实现该接口基本类型的方式来拓展
  • 注解优于命名,而对于接口,标记接口要优于标记注解,它可以更精确的找出问题所在!

对于任何一个方法,需要注意传入的参数是否会导致错误的产生(保护性拷贝)

开发中慎用重载和可变参数。。(好像一般也没用过,会有很多意想不到的问题产生)

当数组的值为空时,尽量不要返回null,可以返回Collection.emptyList();

localDateTime 代替 Date ,BigDecimal 代替Long ,Float(前者不太安全不好用,后者计算会有精度问题)

努力编写好的程序而不是快的程序;努力避免会限制性能的API设计。努力使程序失败依旧保持原子性。

同步不仅可以组织一个线程看到对象处于不一致的状态当中,它还可以保证进入同步方法或者同步代码块的每个线程,都能看到由一把锁保护的之前的所有修改效果。

++操作不是原子性的!它需要读取值,再写入新值,在读取旧值的写入新值的过程就可能产生并发问题。(想起了ABA)

有了concurrent包,没有理由再去使用wait和notify去控制线程同步了。

谨慎的实现序列化接口

  • 一旦实现的类被发布,这个类的"灵活性"就被降低了(这个类的序列化形式固定了,会变成导出API的一部分)
  • 增加的出现Bug和安全漏洞的可能性(他是一种语言之外的创建对象的方式,反序列化机制是一个"隐藏的构造函数")对象的约束关系很容易遭到破坏,以及受到非法访问
  • 随着类发行新版本,相关的测试负担也会增加
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值