一、二哥进阶路-面向对象编程
1.java中类与对象
创建 Java 对象时,需要用到 new 关键字。
Person person = new Person();
这行代码就通过 Person 类创建了一个 Person 对象。所有对象在创建的时候都会在堆内存中分配空间。
创建对象的时候,需要一个 main() 方法作为入口, main() 方法可以在当前类中,也可以在另外一个类中。
初始化对象三种方法:
person.name = "沉默王二";//通过对象的引用变量
public void initialize(String n, int a, int s) { name = n; age = a; sex = s; }//方法初始化
Person person = new Person("沉默王二", 18, 1);//构造方法初始化
每个对象都可以调用 Object 的 wait/notify 方法来实现等待/通知机制:
public class WaitNotifyDemo {
public static void main(String[] args) {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("线程1:我要等待");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1:我被唤醒了");
}
}).start();
new Thread(() -> {
synchronized (lock) {
System.out.println("线程2:我要唤醒");
lock.notify();
System.out.println("线程2:我已经唤醒了");
}
}).start();
}
}
解释一下:
- 线程 1 先执行,它调用了
lock.wait()方法,然后进入了等待状态。 - 线程 2 后执行,它调用了
lock.notify()方法,然后线程 1 被唤醒了。
①、public final void wait() throws InterruptedException:调用该方法会导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法。
②、public final native void notify():唤醒在此对象监视器上等待的单个线程。如果有多个线程等待,选择一个线程被唤醒。
③、public final native void notifyAll():唤醒在此对象监视器上等待的所有线程。
④、public final native void wait(long timeout) throws InterruptedException:等待 timeout 毫秒,如果在 timeout 毫秒内没有被唤醒,会自动唤醒。
⑥、public final void wait(long timeout, int nanos) throws InterruptedException:更加精确了,等待 timeout 毫秒和 nanos 纳秒,如果在 timeout 毫秒和 nanos 纳秒内没有被唤醒,会自动唤醒。
2.java包
位于同一个包的类,可以访问包作用域的字段和方法。
不用public、protected、private修饰的字段和方法就是包作用域。
Java 内建的package机制是为了避免class命名冲突;
JDK 的核心类使用java.lang包,编译器会自动导入;
JDK 的其它常用类定义在java.util.*,java.math.*,java.text.*,……;
包名推荐使用倒置的域名,例如org.apache。
3.java中变量
局部变量:在方法体内声明的变量被称为局部变量,该变量只能在该方法内使用,类中的其他方法并不知道该变量。
- 局部变量声明在方法、构造方法或者语句块中。
- 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,将会被销毁。
- 访问修饰符不能用于局部变量。
- 局部变量只在声明它的方法、构造方法或者语句块中可见。
- 局部变量是在栈上分配的。
- 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
成员变量:在类内部但在方法体外声明的变量称为成员变量,或者实例变量,或者字段。之所以称为实例变量,是因为该变量只能通过类的实例(对象)来访问。
- 成员变量声明在一个类中,但在方法、构造方法和语句块之外。
- 当一个对象被实例化之后,每个成员变量的值就跟着确定。
- 成员变量在对象创建的时候创建,在对象被销毁的时候销毁。
- 成员变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息。
- 成员变量可以声明在使用前或者使用后。
- 访问修饰符可以修饰成员变量。
- 成员变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把成员变量设为私有。通过使用访问修饰符可以使成员变量对子类可见;成员变量具有默认值。数值型变量的默认值是 0,布尔型变量的默认值是 false,引用类型变量的默认值是 null。变量的值可以在声明时指定,也可以在构造方法中指定。
静态变量:通过 static 关键字声明的变量被称为静态变量(类变量),它可以直接被类访问
- 静态变量在类中以 static 关键字声明,但必须在方法构造方法和语句块之外。
- 无论一个类创建了多少个对象,类只拥有静态变量的一份拷贝。
- 静态变量除了被声明为常量外很少使用。
- 静态变量储存在静态存储区。
- 静态变量在程序开始时创建,在程序结束时销毁。
- 与成员变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为 public 类型。
- 静态变量的默认值和实例变量相似。
- 静态变量还可以在静态语句块中初始化。
二、小博哥-设计模式
1.工厂模式
工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的只是知道调用即可,同时,这也是去掉众多ifelse的方式。当然这可能也有一些缺点,比如需要实现的类非常多,如何去维护,怎样减低开发成本。但这些问题都可以在后续的设计模式结合使用中,逐步降低。
例子:抽奖系统
优化前:
itstack-demo-design-1-01
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── AwardReq.java
│ ├── AwardRes.java
│ └── PrizeController.java
└── test
└── java
└── org.itstack.demo.design.test
└── ApiTest.java
优化后:
itstack-demo-design-1-02
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── store
│ │ ├── impl
│ │ │ ├── CardCommodityService.java
│ │ │ ├── CouponCommodityService.java
│ │ │ └── GoodsCommodityService.java
│ │ └── ICommodity.java
│ └── StoreFactory.java
└── test
└── java
└── org.itstack.demo.design.test
└── ApiTest.java
2.抽象工厂实例
例子:
第一次优化前:
itstack-demo-design-2-00
└── src
└── main
└── java
└── org.itstack.demo.design
├── matter
│ ├── EGM.java
│ └── IIR.java
└── RedisUtils.java
第一次优化后:
itstack-demo-design-2-01
└── src
└── main
└── java
└── org.itstack.demo.design
├── impl
│ └── CacheServiceImpl.java
└── CacheService.java
- 这里的实现过程非常简单,主要ifelse根据类型判断是哪个Redis集群。
- 虽然实现是简单了,但是对使用者来说就麻烦了,并且也很难应对后期的拓展和不停的维护。
这里的抽象工厂的创建和获取方式,会采用代理类的方式进行实现。所被代理的类就是目前的Redis操作方法类,让这个类在不需要任何修改下,就可以实现调用集群A和集群B的数据服务。
并且这里还有一点非常重要,由于集群A和集群B在部分方法提供上是不同的,因此需要做一个接口适配,而这个适配类就相当于工厂中的工厂,用于创建把不同的服务抽象为统一的接口做相同的业务。这一块与我们上一章节中的工厂方法模型类似,可以翻阅参考。
再次优化:
itstack-demo-design-2-02
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── factory
│ │ ├── impl
│ │ │ ├── EGMCacheAdapter.java
│ │ │ └── IIRCacheAdapter.java
│ │ ├── ICacheAdapter.java
│ │ ├── JDKInvocationHandler.java
│ │ └── JDKProxy.java
│ ├── impl
│ │ └── CacheServiceImpl.java
│ └── CacheService.java
└── test
└── java
└── org.itstack.demo.design.test
└── ApiTest.java
- 抽象工厂模式,所要解决的问题就是在一个产品族,存在多个不同类型的产品(Redis集群、操作系统)情况下,接口选择的问题。而这种场景在业务开发中也是非常多见的,只不过可能有时候没有将它们抽象化出来。
你的代码只是被ifelse埋上了!当你知道什么场景下何时可以被抽象工程优化代码,那么你的代码层级结构以及满足业务需求上,都可以得到很好的完成功能实现并提升扩展性和优雅度。- 那么这个设计模式满足了;单一职责、开闭原则、解耦等优点,但如果说随着业务的不断拓展,可能会造成类实现上的复杂度。但也可以说算不上缺点,因为可以随着其他设计方式的引入和代理类以及自动生成加载的方式降低此项缺点。
3.建造者模式
建造者模式所完成的内容就是通过将多个简单对象通过一步步的组装构建出一个复杂对象的过程。
那么,哪里有这样的场景呢?
例如你玩王者荣耀的时的初始化界面;有三条路、有树木、有野怪、有守卫塔等等,甚至依赖于你的网络情况会控制清晰度。而当你换一个场景进行其他不同模式的选择时,同样会建设道路、树木、野怪等等,但是他们的摆放和大小都有不同。这里就可以用到建造者模式来初始化游戏元素。
而这样的根据相同的物料,不同的组装所产生出的具体的内容,就是建造者模式的最终意图,也就是;将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
优化前:
itstack-demo-design-3-00
└── src
└── main
└── java
└── org.itstack.demo.design
├── ceilling
│ ├── LevelOneCeiling.java
│ └── LevelTwoCeiling.java
├── coat
│ ├── DuluxCoat.java
│ └── LiBangCoat.java
│ └── LevelTwoCeiling.java
├── floor
│ ├── DerFloor.java
│ └── ShengXiangFloor.java
├── tile
│ ├── DongPengTile.java
│ └── MarcoPoloTile.java
└── Matter.java
第一次优化:
itstack-demo-design-3-01
└── src
└── main
└── java
└── org.itstack.demo.design
└── DecorationPackageController.java
建造者模式重构:
itstack-demo-design-3-02
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── Builder.java
│ ├── DecorationPackageMenu.java
│ └── IMenu.java
└── test
└── java
└── org.itstack.demo.design.test
└── ApiTest.java
- 通过上面对建造者模式的使用,已经可以摸索出一点心得。那就是什么时候会选择这样的设计模式,当:
一些基本物料不会变,而其组合经常变化的时候,就可以选择这样的设计模式来构建代码。 - 此设计模式满足了单一职责原则以及可复用的技术、建造者独立、易扩展、便于控制细节风险。但同时当出现特别多的物料以及很多的组合后,类的不断扩展也会造成难以维护的问题。但这种设计结构模型可以把重复的内容抽象到数据库中,按照需要配置。这样就可以减少代码中大量的重复。
- 设计模式能带给你的是一些思想,但在平时的开发中怎么样清晰的提炼出符合此思路的建造模块,是比较难的。需要经过一些锻炼和不断承接更多的项目,从而获得这部分经验。有的时候你的代码写的好,往往是倒逼的,复杂的业务频繁的变化,不断的挑战!
三、小型支付商城系统
这是一套小型的支付电商系统,提取实际生产中核心的真实模块作为咱们的开发需求,同时也是面试中最为常问的流程。包括;如何微信扫码鉴权登录 + 模板消息通知、怎么做支付宝交易打通、商品支付掉单如何处理、相关的任务补偿怎么操作等。 把这些需求分别通过 MVC 架构、DDD 架构,进行设计实现。让学习的伙伴,对照出不同架构的设计思路和开发差异,即完成业务需求,也提高编程架构思维。