1.类与类之间的关系:
依赖,泛化,实现,关联,聚合,组合
1.1依赖关系(Dependence/Dependency)
只要是在类中用到了对方,那么他们之间就存在依赖关系,例如方法的返回类型,方法的参数类型,在方法中使用到的符号(虚箭头)。

1.2泛化关系(Ganeralization)
泛化关系也就是继承关系符号(带三角形的实线):

1.3实现关系(Realization)
实现关系就是依赖关系的特例,表示一个类实现了一个接口(interface)中定义的所有抽象方法。

1.4关联关系(Association)
类与类之间的关系,依赖关系的一种特例,通常通过成员变量来实现这种关系。

1.5聚合关系
关联关系的一种,聚合关系是整体和个体的关系,例如键盘、鼠标和键盘的关系。即聚合关系在关联关系的基础上,成员变量还是类的一部分。但是成员变量还可以独立访问。

1.6组合关系
关联关系的一种,整体和部分不能分开。和聚合关系的区别就在这里。

2.七大原则
2.1单一职责原则
- 降低类的复杂度,一个类只负责一项职责
- 提高类的可读性,可维护性
- 降低更变引起的风险
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以违反单一职责原则,只有类中方法数量足够少,可以在方法级别保持单一职责原则
java
// 错误:一个类做两件事
class UserManager {
void addUser(String name) {
System.out.println("添加用户:" + name);
}
void sendEmail(String email) { // ❌ 发送邮件是另一件事
System.out.println("发邮件到:" + email);
}
}
// 正确:拆分成两个类
class UserService { // 只负责用户管理
void addUser(String name) {
System.out.println("添加用户:" + name);
}
}
class EmailService { // 只负责邮件
void sendEmail(String email) {
System.out.println("发邮件到:" + email);
}
}
2.2接口隔离原则
- 客户端不应该依赖他不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
java
// 错误:胖接口
interface Worker {
void work();
void eat();
void code(); // 工人不需要会写代码
}
class Programmer implements Worker { // 必须实现所有方法
public void work() { System.out.println("工作"); }
public void eat() { System.out.println("吃饭"); }
public void code() { System.out.println("写代码"); }
}
class Chef implements Worker { // 厨师也要实现code(),不合理!
public void work() { System.out.println("做菜"); }
public void eat() { System.out.println("吃饭"); }
public void code() { throw new UnsupportedOperationException(); } // 厨师不会写代码
}
// 正确:拆分接口
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Codable { void code(); }
class GoodProgrammer implements Workable, Eatable, Codable { // 程序员全要
public void work() { System.out.println("工作"); }
public void eat() { System.out.println("吃饭"); }
public void code() { System.out.println("写代码"); }
}
class GoodChef implements Workable, Eatable { // 厨师不需要会写代码
public void work() { System.out.println("做菜"); }
public void eat() { System.out.println("吃饭"); }
}
2.3依赖倒转原则
高层模块不要直接依赖低层模块,都依赖抽象接口。
java
// 错误的:高层依赖低层
class Dog {
void bark() { System.out.println("汪汪"); }
}
class Person1 {
private Dog dog = new Dog(); // 直接依赖具体的狗
void callPet() { dog.bark(); }
}
// 正确的:都依赖抽象
interface Pet { void sound(); } // 抽象接口
class Dog2 implements Pet { public void sound() { System.out.println("汪汪"); } }
class Cat implements Pet { public void sound() { System.out.println("喵喵"); } }
class Person2 {
private Pet pet; // 依赖抽象
Person2(Pet pet) { this.pet = pet; } // 通过构造器注入
void callPet() { pet.sound(); }
}
2.4里氏替换原则
- 子类中尽量不要重写父类的方法
- 在适当的情况下,可以通过聚合、组合、依赖代替继承
java
// 错误的:子类改变了父类行为
class Rectangle {
int width;
int height;
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
int getArea() { return width * height; }
}
class Square extends Rectangle { // 正方形继承长方形 ❌
void setWidth(int w) {
width = w;
height = w; // 改变了父类的逻辑!
}
void setHeight(int h) {
width = h;
height = h; // 改变了父类的逻辑!
}
}
// 正确:用接口或抽象类
interface Shape { // 抽象
int getArea();
}
class GoodRectangle implements Shape {
int width, height;
public int getArea() { return width * height; }
}
class GoodSquare implements Shape {
int side;
public int getArea() { return side * side; }
}
2.5迪米特原则
- 也叫最少知道原则,即一个类对自己依赖的类知道的越少越好,也就是说,对于被依赖的类不管有多复杂,都尽量将逻辑封装在类的内部,对外除了提供的public方法,不对外泄露任何信息
java
// 错误的:直接和陌生人说话
class A {
B b = new B();
C getC() { return b.getC(); } // A 通过 B 拿到了 C
void doSomething() {
getC().doWork(); // ❌ A 直接调用了 C 的方法,认识太多人了
}
}
// 正确的:只和直接朋友说话
class GoodA {
B b = new B();
void doSomething() {
b.doSomething(); // ✅ 我只告诉B做什么,不关心他怎么做的
}
}
class GoodB {
C c = new C();
void doSomething() {
c.doWork(); // B负责和C沟通
}
}
class C {
void doWork() { System.out.println("C在工作"); }
}
2.6合成复用原则
- 尽量使用合成/聚合的方法,而不是使用继承
java
// 错误的:过度使用继承
class Engine { void start() { System.out.println("引擎启动"); } }
class Car extends Engine { // ❌ 继承:汽车"是一个"引擎?不合理
void drive() {
start(); // 继承引擎的方法
System.out.println("汽车行驶");
}
}
// 正确的:使用组合
class GoodCar {
private Engine engine = new Engine(); // ✅ 组合:汽车"有一个"引擎
void drive() {
engine.start(); // 调用引擎的方法
System.out.println("汽车行驶");
}
// 可以随时更换引擎
void setEngine(Engine e) { this.engine = e; }
}
2.7开闭原则
当代码需要变化时,尽量通过扩展软件实体的行为来实现变换,而不是通过修改已有的代码来实现变化,简单来说,软件要像乐高积木一样,可以随意添加新功能,但不用修改原来的代码。
java
// 扩展开放:新增支付方式不改原来的代码
interface Payment { // 抽象
void pay();
}
class Alipay implements Payment { // 支付宝
public void pay() { System.out.println("支付宝支付"); }
}
class WechatPay implements Payment { // 微信支付
public void pay() { System.out.println("微信支付"); }
}
// 新增支付方式:不用改下面这个类
class BankPay implements Payment { // 新增银行支付
public void pay() { System.out.println("银行支付"); }
}
class PaymentService {
public void process(Payment payment) { // 依赖抽象
payment.pay(); // 这里永远不用改
}
}
3.单例模式
单例模式是一种创建型设计模式, 确保一个类只有一个实例, 并且自行实例化冰箱整个系统提供这个实例。
3.1懒汉式
java
public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
减小项目内存开销,但是线程不安全。
java
public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全,但是每次获取单例对象的时候,都需要进行synchronized判断,性能降低
3.2饿汉式
java
public class Singleton {
private Singleton() {}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
增加系统开销
3.3双检锁
java
public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
由于instance = new Singleton()在指令界面不是一个原子性操作,即先进行分配内存,再进行初始化对象,再将对象指向内存地址。由于jvm可能对指令进行重排序,会先进行初始化地址,再进行指令指向地址对象。此时另一个线程依然会创建Single对象。
java
public class Singleton {
private Singleton() {}
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
推荐
3.4静态内部类
java
public class Singleton {
private static class SingleonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
能被反射破坏
3.5枚举类
java
public enum Singleton {
INSTANCE;
}