Effective Java学习笔记

静态工厂方法

考虑使用静态工厂方法代替构造
静态工厂方法与构造器不同的第一优势在于,它们有名字
第二个优势,不用每次被调用时都创建新对象
第三个优势,可以返回原返回类型的子类
第四个优势,在创建带泛型的实例时,能使代码变得简洁(jdk1.8已经解决)
除此之外
可以有多个参数相同但名称不同的工厂方法
可以减少对外暴露的属性
多了一层控制,方便统一修改

Java 中,获得一个类实例最简单的方法就是使用 new 关键字,通过构造函数来实现对象的创建。

就像这样:
Fragment fragment = new MyFragment();
// or
Date date = new Date();

不过在实际的开发中,我们经常还会见到另外一种获取类实例的方法:
Fragment fragment = MyFragment.newIntance();
// or
Calendar calendar = Calendar.getInstance();
// or
Integer number = Integer.valueOf(“3”);

↑ 像这样的:不通过 new,而是用一个静态方法来对外提供自身实例的方法,即为我们所说的静态工厂方法(Static factory method)。
惯用名称:
• from——类型转换方法,它接受单个参数并返回此类型的相应实例,例如:Dated=Date.from(instant);
• of ——聚合方法,接受多个参数并返回该类型的实例,并把他们合并在一起,例如:Set faceCards=EnumSet.of(JACK,QUEEN,KING);
• valueOf —— from 和 to 更为详细的替代方式,例如:BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
• instance或getinstance ——返回一个由其参数(如果有的话)描述的实例,但不能说它具有相同的值, 例如:StackWalkerluke=StackWalker.getInstance(options);
createnewInstance——与instance或getInstance类似,除此之外该方法保证每次调用返回一个新 的实例,例如:ObjectnewArray=Array.newInstance(classObject,arrayLen);
• getType ——与getInstance类似,但是在工厂方法处于不同的类中的时候使用。getType中的Type 是工厂方法返回的对象类型,例如:FileStorefs=Files.getFileStore(path);
• newType——与newInstance类似,但是在工厂方法处于不同的类中的时候使用。newType中的Type 是工厂方法返回的对象类型,例如:BufferedReaderbr=Files.newBufferedReader(path);
• type —— getType 和 newType 简洁的替代方式,例如:List litany = Collections.list(legacyLitany);

当构造参数过多时使用Builder模式

NutritionFactscocaCola=newNutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();
客户端调用 builder 对象的与 setter 相似方法来设置你 想设置的可选参数。最后,客户端调用 builder 对象的一个无参的 build 方法来生成对象,该对象通常是不可变 的。Builder 通常是它所构建的类的一个静态成员类。上述为利用 Builder 模式模拟实现的可选参数。未优化之前需要对所有参数传参或者写很多不同的排列组合形式的构造函数,优化后可以实现较方便的可选参数构造类对象。
通过协变返回类型可以使父类方法在子类中直接调用而不需要强制转换
通过协变返回类型(重写方法的返回类型可以是重写方法返回类型的子类)可以使父类方法在子类中直接调用而不需要强制转换
另外还可以将多个子类调用的参数聚合到单个属性中(跟继承相关)
通常只有在参数数量四个及以上时才使用builder构建方法,并且出现这种情况时 为避免方法弃用带来的错误,最好第一次就直接用builder.

可以通过私有构造方法的方式使该类不能是实例化,而不要通过抽象类的方式,因为该类可以子类化,子类可以实例化

依赖注入优于硬连接资源

许多类依赖于一个或多个底层资源(对象实例),可以通过提供带参的构造函数注入资源,用户可以根据需要选择注入的资源类的具体实例,也可以通过spring的@autowire自动注入。
依赖注入也适用于构造方法、静态工厂和builder模式
静态实用类和单例类不适合于需要引用底层资源的类。

避免创建不必要的对象

尤其是对象不可变时,尽量重用对象而不要创建新的对象,避免资源浪费
适配器是一个隐形的对象不可变对象
优先使用基本类型而不是装箱基本类型(包装器),因为装箱基本类型

消除过期对象的引用

及时消除非活动元素的引用,置null
当缓存中某一项生面周期有key’决定而不是value决定时,使用weakhashmap,可以自动将过期的项删除

避免使用finalizer和cleaner机制

jvm的gc机制在什么时候被调用,被不被调用是不可控的;对于非final类,finalizer机制可以在静态属性记录对对象的引用,防止被垃圾收集,是一种finalizer机制攻击。

使用try-with-resources语句代替try-finally语句

try-with-resource的close方法不可见,try-with-resource避免了显示调用close时对try中的异常内容覆盖

重写equals方法时遵守通用约定

通常在值类(表示值的类)中才需要重写equals方法,否则需要满足五大要求才重写equals方法。
一般配合==和instanceof使用
重写equals必须重写hashcode方法

始终重写tostring方法

最好重写tostring就加上文档注释,以避免后期需要tostring格式更改带来的麻烦

谨慎重写clone方法

尽量不要去用cloneable接口重写clone的方法实现复制,尽量使用复制构造方法或工厂的方法实现复制功能

考虑实现comparable接口

实现coparato方法后,对象数组排序会更简单,尤其具有明显自然顺序(字母顺序、数字顺序、时间顺序)的值类,都应该实现comparable接口,被比较对象通常工行时同类型对象

  1. == : 基本数据类型都用这个比较,
    Java里面包含8个基本数据类型,分别是:
    boolean、byte、char、short、int、float、double、long
    注意String 可不属于基本数据类型,它是个类…

2…equals() 用于引用数据类型(除了上面的8个,都是引用数据类型,包括封装类,Object子类等), 比较是否指向相同的对象,
例如 String str = “abc”;
等效于: char data[] = {‘a’, ‘b’, ‘c’}; String str = new String(data);
就是str 指向了 new String(data) 这个对象. 如果再有其他变量也指向这个对象,他们比较的结果就返回true;
由于此方法在Object里, 所以它有很多重写, 具体要看说明;
另外```equalsIgnoreCase()可以忽略大小写;
Object和String的equals()实现存在区别,所以上面的例子不太友好。有demo在最后
3. compareTO 在基本数据中,compareTo()是比较两个Character 对象;string中可以比较两个字符串并且得到顺序.
按字典顺序比较两个字符串。该比较基于字符串中各个字符的 Unicode 值。将此 String 对象表示的字符序列与参数字符串所表示的字符序列进行比较。
==操作符并不涉及对象内容的比较。若要对对象内容进行比较,则用equals;==是对对象地址的比较,而equals是对对象内容的比较。对于基本数据类型,一般用==,而对于字符串的比较,一般用equals

2、对于compareTo(), 在API中,java.lang包下面的基本数据类型的封装类都提供了该方法,如 Integer,Float,Byte,Short,Character 等

使类和成员的可访问性最小化

很少用public修饰公共类的实例字段,因为线程不安全

在公共类中使用访问方法而不是公共属性

因为暴露方法比暴露变量更具有实现的灵活性,同时前者客户端的破坏性相对较小。

最小化可变性

不可变类:实例一但被创建,成员变量的值不可修改。实例不能被修改的类,实例中的所有信息在对象的生命周期中是固定的
使用不可变类设计更简单,实现使用更容易和安全
使类不可变:使用final private修饰,类不能被继承,不要提供修改对象状态的方法,确保对可变组件的互斥访问
防御性拷贝:一个类有从他的客户端得到或者返回的可变组件,那么这个类必须防御性的拷贝这些组件,使用拷贝的对象进行操作防止对原数据进行修改
如果不可变类中包含mutable类对象,那么返回给客户端的时候,返回该对象的一个拷贝,而不是该对象本身
不可变对象线程安全,不需要同步

组合优于继承

不去继承一个类,而是将新类中增加私有属性属性为现有类的实例引用,现有类成为新类的组成部分,叫组合。
继承会不必要的公开实现细节,产生的api将与原始实现联系在一起,限制类的性能;暴露类内部,客户端可以直接访问,进而改变了底层实现破坏类子类不变性。(与里氏替换原则中的思想一致:父类可以完成的事情,子类也可以完成,如果重写父类方法并且由此出错,将来如果用子类替换父类就会出现问题)

设计继承类相对辛苦,必须文档说明所有自用模式

接口优于抽象类

接口可以定义混合类型,所谓混合类型就是混合多个类的能力,产生的可以表示混型中所有类型的类。另外接口操作会更灵活,尤其面对非层级类型框架。
对于接口的公共方法,通过骨架实现类去实现,从而子类不需重复实现,骨架实现类作为一个抽象类再令需要实现接口的类去继承。

为后代设计接口

接口仅用来定义类型

不要去写用于导出常量的糟糕接口,想导出常量:
1.常量与现有类或接口密切相关,则直接将其添加到类或接口中;
2.使用枚举导出
2.用不可实例化的工具类导出

类层次结构优于标签类

不要将所有标签罗列在一个类中,然后多个实现混杂在一个类中,将标签类转换为类层次,将标签类拆分开,每个类型的实现由自己的类来实现

支持使用静态成员类而不是非静态类

四种嵌套类:静态成员类,非静态成员类,匿名类,局部类
静态成员类:仅在与外部类一起使用时才有用,如果嵌套类的实例可以与宿主类的实例隔离存在,那么嵌套类必须是静态成员类,因为不可能再没有宿主实例的情况下创建非静态成员类的实例,因此声明一个不需要访问宿主实例的成员类的话就加上static修饰符,使它成为静态成员类,避免宿主引用带来的时间和空间浪费
匿名类:适用于只存在一次,并且只为创建一个对象

将源文件限制为单个顶级类

一个源文件中有多个顶级类容易产生在其他源文件也有同名类时产生调用冲突,所以不要将多个顶级类或接口放在一个源文件中,从而编译时不会有多个定义,如果这些顶级类从属于同一个类,则把他们变为静态成员类,并且声明为私有来减少类的可访问性。

不要使用原始类型

使用泛型,如果不知道实际类型参数是什么,则使用无限制通配符<?>;Set<Object>是参数化类型,表示一个可以包含任何类型对象的集合,Set是一个原始类型,他不在反省类型系统之列。

消除非检查警告

尽可能消除每一个未经检查的警告(由泛型带来),不能消除的警告,如果确定代码安全,使用注解抑制警告(@SuppressWarnings("unchecked")),并且添加注释,为什么安全

列表优于数组

数组和泛型一般不能结合在一起用,例如创建泛型类型的数组(new List<E>[ ]),参数化类型的数组(new List<String>[])以及类型参数的数组(new E[])都是非法的,但是创建无限定通配符类型的数组是合法的,因为擦除机制,编译时可以唯一确定参数化类型。数组是协变和具体化的,泛型是不变的,类型可擦除的,如果将两者混合产生编译时错误或警告,首先想到使用列表List<T>替换数组T[]

优先考虑泛型

将类泛型化,泛型化类的第一步是在其声明中添加一个或多个类型参数,通常使用E作为类型参数表示堆栈的元素类型

优先使用泛型方法

形如: public static <E> Set<E> union(Set<E> S1,Set<E> S2)
限定类型:public static <E extends Comparable<E>> E max(Collection<E> c)

使用限定通配符来增加api的灵活性

<? extends E>
<? super E>

为了获得最大的灵活性,对代表生产者或消费者的输入参数使用通配符类型,使用规则:PECS(producer-extends,consumer-super),作为生产者的参数化类型使用extends,作为消费者的参数化类型则使用super。
例如Comparable实例总是消费者,所以通常使用Comparable<? super T>优于Comparable<T>;Comparator也总是消费者,同理,Comparator<? super T>
tips:不要将限定通配符作为返回类型

合理的结合泛型和可变参数

尤其是在给另一个方法访问一个泛型可变参数数组的情况下,这种操作是不安全的,除非两种情况:1.确保数组传递给另一个可变参数方法是类型安全的,并用@SafeVarargs标注的;2.数组传递给一个非可变参数的方法是安全的,该方法仅计算数组内容的一些方法。

优先考虑类型安全异构容器

Favorites:允许客户端保存和检索任意多种类型的favorite实例,该类型的Class对象是参数化键的一部分,因为Class类是泛型的,也就是不是简单的Class,而是Class<T>,也称其为类型令牌,类型令牌无限制,如果需要有限制可以通过限定参数类型或限定通配符。Favorites实例是类型安全的,请求什么类型返回就是什么类型,他也是异构的,与Map不同,所有的键都是不同类型,所以被称为类型安全异构容器。
Class类中有asSubclass,是一种安全(动态)执行其参数表示的类的子类的类型转换实例方法。
安全异构容器使用场合:泛型API中限制了每个容器的固定数量的类型参数,此时有需要的话可以使用类型安全异构容器,他通过将类型参数放在键上而不是容器上没解决了此限制。

使用枚举类型代替整型常量

枚举类型允许添加任意方法和属性并实现任意接口,他们提供了所有Object方法的高质量实现,实现了Comparable和Serializable,不单单可以作为枚举常量的简单集合。
有一种更好的方法可以将不同的行为与每个枚举常量关联起来:在枚举类型中声明一个抽象的方法,并用常量特定的类主体中的每个常量的具体方法重写它。这种方法被称为特定于常量(constant-specific)的方法实现:

//Enum type with constant specific method implementations 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值