1 七大设计原则
开闭原则(对修改关闭、对扩展开放):学习设计模式或者设计原则,就是为了去扩展代码功能的时候不去修改代码。
理式替换原则(继承):不要破坏类的继承关系,其实就是不要轻易去改动父类已经实现的代码。
接口隔离原则(接口):保证类实现的接口要单一,也是为了方便类的扩展和维护(也是为了开闭原则)
依赖倒置原则(面向接口或者面向继承编程):为了降低类之间的耦合性,尽量的不去面向细节或者实现进行编程。
单一职责原则(类):保证类本身功能职责单一,这样就会减少类的改动几率
合成复用原则(组合或者聚合)
最少认知原则(类与类之间的关系):不要与陌生人交流。如果两个类之间不存在依赖关系,他们直接在沟通的时候最好通过第三者去沟通
2 设计模式(三类23种)
创建型:简单工厂、工厂方法、抽象工厂
为什么使用创建型设计模式呢?
1.因为我们只是想使用对象,不是为了去创建对象。
2.创建对象的细节有时候比较复杂或者是隐蔽的,对于使用者来说,要了解对象的创建细节是比较痛苦且没有必要的。
原型模式、构建者模式、单例模式、 webmvc中(委派、适配器用的多)
2.1 原型模式(copy)
作用:就是给我一个原型,我给你一个对象。
使用原型模式复制出来的对象,和原先的对象,都是在堆空间中互相独立的对象。
s1 = Student();
s2 = s1; 这个属于s2复制s1的堆引用,不是复制对象。
使用场景:
有一个数据对象Student
A线程需要对该对象进行一些操作
B线程也需要对该对象进行一些操作。
以上两个操作都各自需要自己的Student对象。
BeanUtils.copy()。
深拷贝和浅拷贝的理解
浅拷贝:原型对象和复制出来的新对象,他们的引用类型新的属性如果地址值相同则是浅拷贝
Student{
int age;
Teacher t;
}
Student studentA = new Student();
Student studentB = new Student();
深拷贝:原型对象和复制出来的新对象,他们的引用类型新的属性如果地址值不相同则是深拷贝
浅拷贝和深拷贝的实现分析
浅拷贝:
1.只需要给原型对象(也就是被复制的对象)实现一个Cloneable接口
(是一个空接口,是一个标记接口),就可以了
2.需要调用super.clone方法产生新的对象(其实就是Object对象的方法)
深拷贝:
需要使用到对象序列化流进行对象的读取和写出。
经过对象序列化流的读取和写出过程所经历已经是两个不同的对象了。
1.需要实现Serializable接口
浅拷贝,深拷贝: 即 teacher地址值相同则是浅拷贝, teacher地址值不同则是深拷贝
2.2 构建者模式
构建者模式
作用:可以根据客户需要,去帮我们创建指定的对象。
方式1:
Student stu = new Student(1,"zhangsan");
第一种方式创建的对象:比较死板,只能根据构造方法的参数去设置创建对象
比如说Student的构造参数有10个,这10个参数都是配置到配置文件中的,而且都不是必须项(可能配置文件只配置几项)
我想获取几个参数,就根据这几个参数去创建我们的对象。
方式2:
Student stu = new Student();
stu.setId(1);
stu.setName("zhangsan");
使用第二种方式去创建对象,需要等待初始化完成才能正常使用。
构建者模式的角色:
导演类
构建者
产品
效果:
StudentBUilder.id(1).name("lisi").age(20).build();-----这就是构建者模式的玩法
2.3 单例模式
作用:在全局范围内只创建该类的一个对象实例。
使用场景:spring大多数对象都是单例存在的。一般被spring管理的bean,都是业务对象,不是数据对象。
数据源等信息
单例的实现方式分为两种:
懒汉式(也叫延迟加载):面试重灾区,因为该种方式去实现单例,会存在线程安全问题。需要小心处理。
饿汉式:面试不会问这种方式,因为该种方式线程是安全的。
懒汉式和饿汉式的区别:
1.饿汉式会造成资源浪费,懒汉式不会。
2.懒汉式会存在线程安全,饿汉式不会。
单例的实现步骤:
1.构造私有
2.私有静态成员变量(如果声明静态成员变量时就进行初始化,则是饿汉式,否则是懒汉式)
3.公开静态成员方法,获取静态成员变量
类的加载时机(类加载)
1.new Student
2.访问类的静态成员时
JVM的安全机制:
当一个类被JVM加载的时候,该类的加载过程是线程安全的,也就是说,相当于JVM对该过程加锁了。
单CPU的情况,线程在执行时,宏观上是并行的,其实微观上是串行的,线程是通过分配CPU的时间片去执行的。
一个线程的执行需要被CPU分配好几次时间片才能执行完成。
理解一个对象在内存中的创建过程:Student student = new Student();
底层字节码的执行过程(有序性)
1.new 会触发类加载机制(已经被加载过的类不需要再次加载)
2.分配堆内存空间(相当于已经有内存地址)
3.将对象进行初始化(将成员变量初始化为0)
4.将对象引用地址赋值给栈空间中的变量
JVM的JIT即时编译器会对代码的执行过程进行优化,包含代码的执行顺序。
也就是说以上对象的创建过程,可能被JIT即时编译器优化为1-2-4-3(指令重排序)。
原子性
主要针对的不是高级语言的原子性,而是字节码或者机器码的原子性操作。
new Student();//也不是原子性操作
i++;//不是原子性操作
1.先从局部变量表中获取i的值
2.将i的值加入到操作栈中
3.将操作栈中的i进行自加
4.将自加的i值放入操作栈
5.将操作栈的栈顶元素取出并放入局部变量表
如何保证原子性呢?
使用锁机制(synchronized和lock锁)
有序性
int x ;//1
boolean y;//2
x = 10;//3
y = false;//4
当x和y之间没有依赖性(happened-before原则),x和y的执行可以进行指令重排序(字节码指令);
int x= 10;
int y = x +2;
以上的代码不能进行指令重排序,因为y的值依赖于x,所以先要执行x这行代码
volatile关键字的第一个作用:就是禁止被它修饰的变量相关操作进行指令重排序。
可见性
主要的两种实现方式:双重检查锁、内部类
https://blog.csdn.net/whxwkb/article/details/114026204