1. 代理模式
1.1. 静态代理和动态代理
静态代码的问题与缺陷
静态代理违反了开闭原则
可扩展性差
可维护性差
2. 面向对象有六大设计原则
1) 单一职责原则 -- SRP
2) 开闭原则 -- OCP
3) 里式替换原则 -- LSP
4) 依赖导致原则 -- DIP
5) 迪米特原则 -- LOD
2.1 单一职责原则:
概述:Single Responsibility Principle : 应该有且仅有一个类引起类的变更,就是一个类只担负一个职责
单一职责原则的优点:
* 类的复杂性降低,实现任何职责都有明确的定义
* 逻辑变得简单,类的可读性提高了,而且,因为逻辑简单,代码的可维护性也提高了
* 变更的风险降低,因为只会在单一的类中的修改
2.2. 开闭原则:
概述:Open Closed Principle . 是java世界里最基础的设计原则,其定义是: 一个软件实体:如类,模块和函数应该对扩展开放,对修改关闭
也就是说,一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码实现功能,这是为了软件实体的未来事件而制定的对现行开发设计进行约束一个原则
(东西已经写好了,就在原来的基础上进行扩展)
2.3. 里式替换原则
Liskov Substitution Principle 定义是:
如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1都替换成o2的时候,程序P的行为都没有发生变化,那么类型T2是类型T1的子类型
简单定义 : 所有引用基类的地方必须能够透明的使用其子类的对象
假如一个类A被B继承,而A又被C继承,那么此时B就可以替换A被C继承,反之则行不通
里式替换原则为良好的继承定义了一个规范,它包含了4层含义
* 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
* 子类可以有自己的个性,可以有自己的属性和方法
* 子类覆盖或重载父类的方法时输入的参数可以被放大
加入父类有个方法
public class Father{
public void test(HashMap map){
System.out.println("父类执行");
}
}
子类继承父类的方法, 那么同名方法的参数类型可以被扩大 我们可以输入Map
public class Son extends Father{
public void test(Map map){
System.out.println("子类执行");
}
}
写个测试类
public class Test{
public static void main(String [ ] args){
Father f = new Father();
HashMap map = new HashMap();
f.test(map);
}
}
运行结果为"父类执行"
public class Test{
public static void main(String [ ] args){
Son son = new Son();
HashMap map = new HashMap();
son.test(map);
}
}
结果还是"父类执行"
所以说: 只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何异常
* 子类覆盖或重载父类的方法时输出结果可以缩小,也就是说返回值类型可以小于或者等于父类的方法返回值类型
比如父类的方法时int类型,那么子类的返回值可以是int 可以是byte ,但是不能为long,应该返回值类型比int大
2.4. 依赖倒置原则
依赖倒置原则: Dependence Inversion Principle , 简称DIP ,它的定义是 :
* 高层模块不应该依赖底层模块,两者都应该依赖其抽象;
* 抽象不应该依赖细节
* 细节应该依赖抽象
什么是高层模块和底层模块呢? 不可分割的原子逻辑就是底层模块,原子逻辑的再组装就是高层模块.
在java语言中,抽象就是指接口或抽象类,两者都不能被实例化,而细节就是实现接口或集成抽象类产生的类,也就是可以被实例化的实现类
依赖倒置原则是指的模块间的依赖是通过抽象来发生的,实现类之间不发生直接的依赖关系,其依赖关系是通过接口时来实现的,这就是俗称的
面向接口编程
2.5. 接口隔离原则
接口隔离原则 ,Interface Segregation Principle , 简称ISP,其定义是:
客户端不应该依赖它不需要的接口
意思就是客户端要什么接口就提供什么接口,把不需要的接口剔除掉,这就是需要对接口进行细化,保证接口的纯洁性.
换成另一种说法就是,类间的依赖关系应该建立在最小的接口上,也就是建立单一的接口
你可能会疑惑,建立单一接口,这不是单一职责原则嘛?其实不是,单一职责原则要求的是类和接口职责单一,注重的是职责,一个职责的接口时可以
有多个方法的,而接口隔离原则要求的是接口的方法尽量少,模块尽量单一,如果需要提供给客户端很多的模块,那么就要相应的定义多个接口
不要把所有的模块功能都定义在一个接口中,那样会显得臃肿
2.6. 迪米特原则
迪米特原则 : Low of Demeter ,简称LOD ,也被称为最少知识原则,它描述的规则是:
一个对象应该对其他对象有最少的了解
也就是一个类应该对自己需要的耦合或者调用的类知道的最少,类与类之间的关系越密切,耦合度越大,那么类的变化对其耦合的类
的影响也会越大,这也是我们的面向设计的核心原则: 低耦合,高内聚
通俗的解释: 只和直接的朋友通信
什么是直接的朋友? 每个对象必然与其他对象有耦合关系,两个对象的耦合就成为朋友关系,这种关系的类型很多,例如组合,聚合,
依赖等,其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现的局部变量中的类则不是直接朋友.也就是说
陌生的类最好不要作为局部变量的形式出现在类的内部.
举个例子: 体育老师让班长去体育室拿20个篮球,待会要用
布置这个场景有三个类: Teacher , Monitor 和 Basketball 以及发布命令的方法command 和拿篮球的方法takeBall
class Teacher{
public void command(Monitor monitor){
//创建List集合生成20个篮球, 但是老师不是班长不需要去生产篮球的所有有问题
List<Basketball> list = new ArrayList<Basketball>();
for (int i = 0; i <20; i++) {
list.add(new Basketball());
}
monitor.takeBall(list);
}
}
class Monitor{
public void takeBall( List<Basketball> list){
System.out.println("篮球数量为:"+list.size());
}
}
class Basketball{
}
我们做修改之后 , 我们把生产篮球的功能交给班长让班长去实现
lass Teacher{
public void command(Monitor monitor){
//创建List集合生成20个篮球, 但是老师不是班长不需要去生产篮球的所有有问题
monitor.takeBall();
}
}
class Monitor{
public void takeBall(){
List<Basketball> list = new ArrayList<Basketball>();
for (int i = 0; i <20; i++) {
list.add(new Basketball());
}
System.out.println("篮球数量为:"+list.size());
}
}
class Basketball{
}
这一Teacher和Basketball直接就不会有依赖了从而降低了耦合度,这一修改Basketball的时候也不会影响Teacher的功能
===========================
设计模式:
1.单例模式 : 分为饿汉式和懒汉式
定义: 单例模式是比较常见的设计模式,目的是保证一个类只能有一个实例,而且自行实例化并向整个系统提供这个实例,避免频繁创建对象,节约内存
1.1 类加载顺序
类加载器(classLoader) 机制一般遵从下面的加载顺序
如果类还没有被加载:
* 先执行父类的静态代码块和静态变量初始化(int类型默认为0,String类型默认为null),静态代码块和静态变量的执行顺序跟代码中出现的顺序有关
* 执行子类的静态代码块和静态变量初始化
* 执行父类的实例变量初始化(必须要赋予初值才能被使用)
* 执行父类的构造函数
* 执行子类的实例变量初始化
* 执行子类的构造函数
1).存储区域不同:静态变量存储在静态存储区,普通变量存储在堆中;
2).静态变量与类相关,普通变量则与实例相关
3).内存分配方式不同。
4).生命周期不同
5).一个被static修饰一个没有被修饰
同时,加载类的过程是线程私有的别的线程无法进入
如果类已经被加载
静态代码块的静态变量不在重复执行,再创建对象的时,只执行与实例相关的变量初始化和构造方法
1.2static关键字
懒汉式和饿汉式
在程序编写上,一般单例分为两种 : 饿汉式和懒汉式
饿汉式: 在类加载时就完成了初始化,所以类加载比较慢,但是获取对象的时候速度快
懒汉式: 在类加载时不初始化,等到第一次被使用时才初始化
1). 饿汉式(可用)
public class NewTest {
private final static NewTest NEWTEST = new NewTest();
private NewTest(){
}
public static NewTest getInstance(){
return NEWTEST;
}
}
比较常见的写法,在类加载的时候就完成实例化,避免了线程的同步问题
缺点是: 因为类加载的时候就实例化,没有达到LazyLodaing(懒加载)的效果,如果该实例没有被使用,就浪费内存
2). 普通的懒汉式(线程不安全,不可用)
private static NewTest NEWTESTs = null;
public static NewTest getInstance2(){
if (NEWTESTs==null){
NEWTESTs = new NewTest();
}
return NEWTESTs;
}
这是懒汉式中最简单的一种写法,只有在方法第一次被访问的时候才会实例化,达到了懒加载的效果,但是这种写法有个
致命的问题,就是多线程的问题,假设对象还没有实例化,然后又两个线程同时访问,那么就可能出现多次实例化的结果,不采用
3). 同步代码块懒汉式
/**
* 懒汉式同步代码块方法(可用)
*/
public static NewTest newTest = null;
public synchronized static NewTest getInstance3(){
if (newTest==null){
newTest = new NewTest();
}
return newTest;
}
这种写法是对getInstance() 加了锁的处理,保证了同一时刻只能有一个线程访问并获得实例,但是缺点也很明显,因为synchronized是继续是
整个方法,每个线程访问都要进行同步,而其实这个方法只能执行一次实例化代码就够了,每次同步方法显然效率降低了,为了修改这个方法,于是
有了双重检查懒汉式
4). 双重检查懒汉式
/**
* 双重检查懒汉式 (推荐使用)]
*/
public static NewTest newTest2 = null;
public static NewTest getInstance4(){
if (newTest2==null){
synchronized (NewTest.class){
if(newTest2==null){
newTest2 = new NewTest();
}
}
}
return newTest2;
}
这种方法用了两个if判断,也就是Double-Check ,并且同步的不是方法,而是代码块,效率高,是对第三种写法的概念,为什么要两次判断呢?
这是为了线程安全考虑,还是那个场景,两个线程A和B同时访问静态方法并同时运行到第一个if判断语句,这是线程A先进入同步代码块中
实例化对象,结束后线程B进入代码块,如果没有第二个if判断语句,那么线程B也同样的会执行实例化对象操作了
5). 静态内部类实例化对象
/**
* 使用静态内部实例化对象
*/
private static class SingletonInstance{
private static final NewTest newtest = new NewTest();
}
public static NewTest getInstance5(){
return SingletonInstance.newtest;
}
很多开发者推荐使用这个方法,这种静态内部类方法在Songleton类被装载时并不会立即实例化,而是需要实例化时,调用getInstance方法
才会被装载SingletonInstance类,从而完成对象的实例化,因为类的静态属性只会在第一次加载类的时候初始化,也就保证了SingletonInstance中
的对象只会被实例化异常,并且这个过程也是线程安全的
6) 枚举(可用 推荐)
public enmu Singleton{
instance;
}
这种写法<Effective Java>中大为推崇,它可以解决两个问题
1) 线程安全问题,因为java虚拟机在加载枚举类的时候会使用ClassLoader方法,这个方法使用了同步代码块来保证线程安全
2) 避免反序列化破坏对象,因为枚举的反序列化并不通过反射实现
单例模式的优缺点:
优点:单例类只有一个实例,节省了内存资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能;
单例模式可以在系统设置全局的访问点,优化和共享数据
缺点: 单例模式一般没有接口,扩展的话处理修改代码基本没什么其他途径
========================
JDK动态代理
Proxy : 里面的某个静态方法帮助我们创建class文件和实例(是一个类)
InvocationHandler : 定义业务规则和流程(是个接口)invoke( )
类的完整生命周期
* java源文件(java文件)通过javac命令编译成 --> java字节码文件(class文件) 再通过java命令运行
--> Class对象 --> 实例化之后就是实例对象了 --> 卸载(再对象不可达,无引用的时候清除对象)
视频播放记录:1小时06分钟
网友评论