一、 定义类与类之间的交互关系
1、 泛化(Generalization)可以简单理解为继承关系。具体到 Java 代码就是下面这样。
public class A { ... }
public class B extends A { ... }
2、实现(Realization)一般是指接口和实现类之间的关系
public interface A {…}
public class B implements A { … }
3、聚合(Aggregation)是一种包含关系,A 类对象包含 B 类对象,B 类对象的生命周期可以不依赖 A 类对象的生命周期,也就是说可以单独销毁 A 类对象而不影响 B 对象,比如课程与学生之间的关系。具体到 Java 代码就是下面这样:
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
4、组合(Composition)也是一种包含关系。A 类对象包含 B 类对象,B 类对象的生命周期跟依赖 A 类对象的生命周期,B 类对象不可单独存在,比如鸟与翅膀之间的关系。具体到 Java 代码就是下面这样:
public class A {
private B b;
public A() {
this.b = new B();
}
}
6、设计思想
1.单一职责原则
适用对象:模块,类,接口
侧重点:高内聚,低耦合
思考角度:自身
2.接口隔离原则
适用对象:接口,函数
侧重点:低耦合
思考角度:调用者
3.基于接口而非实现编程
适用对象:接口,抽象类
侧重点:低耦合
思考角度:调用者
4.迪米特法则
适用对象:模块,类
侧重点:低耦合
思考角度:类关系
依赖反转原则
高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。
控制反转
“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。
Java 中比较出名的单元测试框架有 Junit、TestNG、Spring Test
关于命名:
1、作用域小的变量(比如临时变量),可以适当地选择短一些的命名方式
2、函数包含 3、4 个参数的时候还是能接受的,大于等于 5 个的时候,参数有点过多了
代码检测工具:checkstyle,findbugs, pmd, jacaco, sonar
异常处理
1、 直接吞掉。具体的代码示例如下所示:
public void func1() throws Exception1 {
// ...
}
public void func2() {
//...
try {
func1();
} catch(Exception1 e) {
log.warn("...", e); //吐掉:try-catch打印日志
}
//...
}
2、原封不动地 re-throw。具体的代码示例如下所示:
public void func1() throws Exception1 {
// ...
}
public void func2() throws Exception1 {//原封不动的re-throw Exception1
//...
func1();
//...
}
3、包装成新的异常 re-throw。具体的代码示例如下所示:
public void func1() throws Exception1 {
// ...
}
public void func2() throws Exception2 {
//...
try {
func1();
} catch(Exception1 e) {
throw new Exception2("...", e); // wrap成新的Exception2然后re-throw
}
//...
}
总之,是否往上继续抛出,要看上层代码是否关心这个异常。关心就将它抛出,否则就直接吞掉。是否需要包装成新的异常抛出,看上层代码是否能理解这个异常、是否业务相关。如果能理解、业务相关就可以直接抛出,否则就封装成新的异常抛出。
经验:
入参和中间不可靠变量的异常校验都放在public方法,所有私有方法都以契约的方式不再做参数校验。也就是说 public方法干 1.参数校验 2. 系统一级流程编排 3.统一异常处理 这三件事。
可以不在 private 函数中做 NULL 值或空字符串的判断
如果函数是 public 的,无法掌控会被谁调用以及如何调用,需要校验。
建造者模式(build模式)
一、 使用场景:
1)类的构造函数必填属性很多,通过set设置,没有办法校验必填属性
2)如果类的属性之间有一定的依赖关系,构造函数配合set方式,无法进行依赖关系和约束条件校验
3)需要创建不可变对象,不能暴露set方法。
(前提是需要传递很多的属性,如果属性很少,可以不需要建造者模式)
二、实现方式:
把构造函数定义为private,定义public static class Builder 内部类,通过Builder 类的set方法设置属性,调用build方法创建对象。
三、和工厂模式的区别:
1)工厂模式:创建不同的同一类型对象(集成同一个父类或是接口的一组子类),由给定的参数来创建哪种类型的对象;
2)建造者模式:创建一种类型的复杂对象,通过很多可设置参数,“定制化”的创建对象
原型模式
原型模式有两种实现方法,深拷贝和浅拷贝。浅拷贝只会复制对象中基本数据类型数据和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象……而深拷贝得到的是一份完完全全独立的对象。所以,深拷贝比起浅拷贝来说,更加耗时,更加耗内存空间。
深拷贝:
三、泛型
泛型:
1、extends
T extends Number
在这里extends后的BoundingType可以是类,也可以是接口,意思是说,T是在BoundingType基础上创建的,具有BoundingType的功能。
目测是JAVA的开发人员不想再引入一个关键字,所以用已有的extends来代替而已。
重点: 如果你想从一个数据类型里获取数据,使用 ? extends 通配符(能取不能存)
2、?通配符
无边界通配符? 只能用于填充泛型变量T,表示通配任何类型 只是填充方式的一种!!! Box<?> box= new Box(); 但是不能声明 ? x (不行) box=new Box<?> (不行)
3、super
重点: 如果你想把对象写入一个数据结构里,使用 ? super 通配符(能存不能取)
如果你既想存,又想取,那就别用通配符