Effective.Java第12-22条
12. 始终重写toString()方法
如果不重写toString()方法,打印的时候是 类名+@+哈希码的无符号十六进制。我们查看 Object的toString()方法如下:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
阿里规约也有一条:
POJO类必须重写toString方法。如果继承了另一个类,在前面加super.toString()。这么做的好处是在方法抛出异常时,可以直接调用toString()方法打印其属性,便于排查。
13. 谨慎地重写clone方法
所有的JavaBean都继承自Java.lang.Object,而Object类提供一个clone方法,可以将一个JavaBean对象复制一份。但是这个JavaBean必须实现一个表示接口Cloneable,表明这个JavaBean支持复制。如果一个对象没有实现这个接口而调用clone()方法,Java编译器会抛出CloneNotSupportedException异常。
阿里规约也有一条:
慎用Object的clone方法来拷贝对象。对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。
14. 考虑实现Comparable接口
看到tree集合,可以按顺序进行排列,就要想到两个接口。Comparable(集合中元素实现这个接口,元素自身具备可比性),Comparator(比较器,传入容器构造方法中,容器具备可比性)。
对于Arrays.sort和Collections.sort有两种用法,第一个是只传递一个arrays或list参数,第二个是传递一个加 Comparator 比较器的参数。
比如我们想比较人的时候按年龄倒序排列:
public class Person implements Comparable<Person> {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Person comparedUser) {
// 返回负数表示this对象排在comparedUser前面
if (this.age > comparedUser.getAge()) {
return -1;
}
// 返回正数表示this对象排在comparedUser后面
if (this.age < comparedUser.getAge()) {
return 1;
}
return 0;
}
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
测试代码如下:
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person(20, "张三"));
list.add(new Person(22, "李四"));
list.add(new Person(19, "王五"));
list.add(new Person(19, "张是"));
list.add(new Person(17, "开发"));
list.add(new Person(29, "看"));
Collections.sort(list);
for (Person p : list) {
System.out.println(p);
}
}
结果:
Person [age=29, name=看]
Person [age=22, name=李四]
Person [age=20, name=张三]
Person [age=19, name=王五]
Person [age=19, name=张是]
Person [age=17, name=开发]
当然了,我们可以编写一个比较器,也就是实现Comparator接口:(逆序比较器)
import java.util.Comparator;
public class PersonComparator implements Comparator<Person> {
/**
* 第一个参数可以理解为基准,第二个是与之比较的元素 。返回负数表示排在其前面,返回正数表示排在其后面
*/
@Override
public int compare(Person o1, Person o2) {
// 返回负数表示o1排在o2前面
if (o1.getAge() > o2.getAge()) {
return -1;
}
// 返回正数表示o1排在o2后面
if (o1.getAge() < o2.getAge()) {
return 1;
}
return 0;
}
}
如果想变成正序比较器使用如下方法:
Comparator<Person> reverseOrder = Collections.reverseOrder(new PersonComparator());
测试代码:(使用比较器进行排序)
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person(20, "张三"));
list.add(new Person(22, "李四"));
list.add(new Person(19, "王五"));
list.add(new Person(19, "张是"));
list.add(new Person(17, "开发"));
list.add(new Person(29, "看"));
Collections.sort(list, new PersonComparator());
for (Person p : list) {
System.out.println(p);
}
}
结果:
Person [age=29, name=看]
Person [age=22, name=李四]
Person [age=20, name=张三]
Person [age=19, name=王五]
Person [age=19, name=张是]
Person [age=17, name=开发]
15. 使类和成员的可访问性最小化
让类或成员尽可能地不被访问。
1、private修饰词,表示成员是私有的,只有自身可以访问;
2、protected,表示受保护权限,体现在继承,即子类可以访问父类受保护成员,同时相同包内的其他类也可以访问protected成员。
3、无修饰词(默认),表示包访问权限(friendly, java语言中是没有friendly这个修饰符的,这样称呼应该是来源于c++ ),同一个包内可以访问,访问权限是包级访问权限;
4、public修饰词,表示成员是公开的,所有其他类都可以访问;
阿里规约也有详细的描述: 类成员与方法访问控制从严
1) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。
2) 工具类不允许有 public 或 default 构造方法。
3) 类非 static 成员变量并且与子类共享,必须是 protected。
4) 类非 static 成员变量并且仅在本类使用,必须是 private。
5) 类 static 成员变量如果仅在本类使用,必须是 private。
6) 若是 static 成员变量,必须考虑是否为 final。
7) 类成员方法只供类内部调用,必须是 private。
8) 类成员方法只对继承类公开,那么限制为 protected。
说明:任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。 思考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 service 方法,或者 一个 public 的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的 视线内,变量作用域太大,无限制的到处跑,那么你会担心的。
16. 在公共类中使用访问方法而不是公共属性
如下x属性和y属性切勿写成public直接访问。
public class Point {
private double x;
private double y;
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
}
17. 最小可变性
不可变类简单来说就是它的实例不能被修改的类。Java中有String、基本数据类型的包装类型以及BigInteger和BigDecimal。
要使一个类不可变,需要遵循下面五条规则:
1.不要提供修改对象状态的方法
2.确保这个类不能被继承
3.把所有属性设置为final
4.把所有属性设置为private
5.确保对任何可变组件的互斥访问
不可变对象本质上是线程安全的,它们不需要同步。
不可变对象提供了免费的原子失败机制。它们的状态永远不会改变,所以不可能出现临时的不一致。
不可变类的主要缺点是对于每个不同的值都需要一个单独的对象。
一条最基本的原则是:不共享可变的数据,要么压根不共享。换句话说,将可变数据限制在单个线程中。
18. 组合优于继承
继承是实现代码重用的有效方式,但并不是最好的工具。
在包中使用继承是安全的,其中子类和父类都在同一个程序员的控制之下。对应专门为了继承而设计的,并且有文档说明的类来说,使用继承也是安全的。然后,从普通的具体类跨越包边界继承是危险的。
与方法调用不同,继承打破了封装。换句话说,一个子类依赖于其父类的实现细节来保证其正确的功能。父类的实现可能会从发布版本不断变化,如果是这样,子类可能被破坏。因此,子类必须与其父类一起更新和变化。
解决办法:不要继承一个现有的类,而是给新类增加一个私有属性,该属性是现有类的实例引用,这种设计被称为组合,因为现有的类成为新类的组成部分(有点类似于对象型适配器模式)。新类中的每个实例方法调用现有类的包含实例上的相应方法并返回结果,这种被称为转发,而新类中的方法被称为转发方法。有时组合和转发的结合被不精确地称为委托。
如下:
目标接口:
package cn.qlq.adapter;
public interface Target {
void option();
void option2();
}
现有的类:
package cn.qlq.adapter;
public class Adaptee {
public void option2() {
System.out.println("option2");
}
}
新类:(组合模式将option2()方法委托给Adaptee)
package cn.qlq.adapter;
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
super();
this.adaptee = adaptee;
}
@Override
public void option() {
System.out.println("option");
}
@Override
public void option2() {
adaptee.option2();
}
}
19. 要么设计继承并提供文档说明,要么禁用继承
设计继承的时候必须文档说明所有的自用模式,必须承诺他们为整个生命周期。如果你不这样做,子类可能会依赖于父类的实现细节,并且如果父类的实现发生改变,子类可能会损坏。为了允许其他人编写高效的子类,肯能还需要导出一个或多个受保护的方法。除非你知道有一个真正的子类需要,否则最好声明你的类为final静止继承,或者确保没有可访问的构造方法(也就是声明一个private的构造方法)。
补充:
我们知道当我们不写构造方法的时候编译器会自动加一个无参的public修饰的构造方法,但是当我们用private声明一个构造方法之后编译器不会再添加构造方法。子类的对象也是父类的对象,所以如果父类没有无参的构造方法,在子类的构造方法必须显示的调用父类的带参数的构造方法。
20. 接口优于抽象类
Java8中引入了接口的默认方法(default)方法,抽象类本身可以具有具体的方法。因为Java只允许单一继承,所以对抽象类的这种限制严格限定了它们作为类型定义的使用。
21. 为后代设计接口
22. 接口仅用来定义类型
尽量不在接口中使用变量,接口只能用于定义类型。更不能将接口用于工具类。
Effective.Java第12-22条的更多相关文章
- <<Effective Java>> 第四十三条
<<Effective Java>> 第四十三条:返回零长度的数组或者集合,而不是null 如果一个方法的返回值类型是集合或者数组 ,如果在方法内部需要返回的集合或者数组是零长 ...
- 【Effective Java】12、避免过度同步
这里有一个辅助基础类 package cn.xf.cp.ch02.item16; import java.util.Collection; import java.util.Iterator; imp ...
- [Effective JavaScript 笔记]第22条:使用arguments创建可变参数的函数
第21条讲述使用可变参数的函数average.该函数可处理任意数量的参数并返回这些参数的平均值. 如何创建可变参数的函数 1.实现固定元数的函数 书上的版本 function averageOfArr ...
- Effective java读书札记第一条之 考虑用静态工厂方法取代构造器
对于类而言,为了让client获取它自身的一个实例,最经常使用的方法就是提供一个共同拥有的构造器. 另一种放你发,也应该子每一个程序猿的工具箱中占有一席之地.类能够提供一个共同拥有的静态 工厂方法.它 ...
- <<Effective Java>>之善用组合而不是继承
使用JAVA这门OO语言,第一要义就是,如果类不是专门设计来用于被继承的就尽量不要使用继承而应该使用组合 从上图2看,我们的类B复写了类A的add喝addALL方法,目的是每次调用的时候,我们就能统计 ...
- 【《Effective C#》提炼总结】提高Unity中C#代码质量的22条准则
引言 原则1尽可能地使用属性而不是可直接访问的数据成员 原则2偏向于使用运行时常量而不是编译时常量 原则3 推荐使用is 或as操作符而不是强制类型转换 原则4 推荐使用条件属性而不是if条件编译 原 ...
- 高性能、高流量Java Web站点打造的22条建议
@http://www.csdn.net/article/2013-12-20/2817861-22-recommendations-for-building-effective-high-traff ...
- Effective Java 第三版——12. 始终重写 toString 方法
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 阅读《Effective Java》每条tips的理解和总结(1)
<Effective Java>这本书的结构是90来条tips,有长有短,每条tip都值的学习.这里根据对书中每条tip的理解做简短的总结,方便日后回顾.持续更新~ 1. 考虑用静态方法代 ...
- Effective Java 第三版——22. 接口仅用来定义类型
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
随机推荐
- Vue.js最佳实践--VueRouter的beforeEnter与beforeRouteLeave冲突解决
用Vue做应用管理系统,通常会在离开某个页面的时候,需要检测用户是否有修改,询问用户需要不需要保存之类的需求 这时候,在读VueRouter文档:组件内的守卫 的时候,发现beforeRouteLea ...
- [转]C++ 堆栈溢出的原因以及可行的解决方法
栈溢出(stackoverflow)的原因及解决办法 大家都知道,Windows程序的内存机制大概是这样的:全局变量(局部的静态变量本质也属于此范围)存储于堆内存,该段内存较大,一般不会溢出: 函数地 ...
- 【转】WPF 异步执行方法后对 UI 进行更新的几种方法
使用 async/await 的情况: private async void Button_Click(object sender, RoutedEventArgs e) { (sender as B ...
- Ueditor文字和echarts图片 生成 word 前端解决方案
编程就像搭积木,少了任何一个就拼接不起来,所有积木都找到就只剩下调试. 一.echarts 获取图片方法getDataURL 详细配置:https://www.echartsjs.com/zh/a ...
- MySQL Install--CentOS 7配置MySQL服务和开启启动
创建MySQL服务 编辑文件: vim /usr/lib/systemd/system/mysql.service 录入下面内容: PS: 注意修改ExecStart脚本 [Unit]Descript ...
- JDK8 Steam流操作
原文:https://github.com/niumoo/jdk-feature/blob/master/src/main/java/net/codingme/feature/jdk8/Jdk8Str ...
- oracle 导入导出表
imp username/pwd@orcl file=c:\temp\exp.dmp tables=(table1, table2)#imp username/pwd@ip:1521/orcl ful ...
- nodejs,npm 安装配置步骤
http://xiaoyaojones.blog.163.com/blog/static/28370125201351501113581/ 参照上述网址中的方法 特别强调一下,在第三步的时候,在命令行 ...
- shell脚本启动所有集群节点
#profile变量追加到.bashrc中 cat /etc/profile >> ~/.bashrc #start-all-cluster.sh 启动脚本 #!/bin/bash ec ...
- 【题解】CF161B Discounts
目录 题目 思路 \(Code\) 题目 CF161B Discounts 思路 贪心.很显然对于一个板凳(价格为c)所能使我们最多少花费\(\frac{c}{2}\)的金钱. 原因如下: 如果你将一 ...