定义:
由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。
简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
父类定义了创建对象的接口,但是由子类来具体实现,工厂方法让类把实例化的动作推迟到了子类当中,对象的创建由子类来完成。
作用:
实例化对象的时候不再使用 new Object()形式,可以根据用户的选择条件来实例化相关的类。对于客户端来说,去除了具体的类的依赖。只需要给出具体实例的描述给工厂,工厂就会自动返回具体的实例对象。
使用场景:对某一类对象总是要执行相同的流程,但是并不在意这些对象之间的微小差异。
场景对应:公共的父类决定了怎么去处理这一类对象,而子类决定了如何创建这些有着微小差异的不同对象。
简单工厂模式例子:
1、抽象产品类(为子类提供方法)
public abstract class Animal{
//抽象方法,具体实现让子类去实现
public abstract void eat();
}
2、具体实现类
public class Cat extends Animal{
@Override
public void start() {
System.out.println("我要吃猫粮");
}
}
public class Dog extends Animal{
@Override
public void start() {
System.out.println("我要吃狗粮");
}
}
3、工厂类
public class AnimalFactory {
public static Animal createAnimal(String type){
Animal animal = null;
switch (type) {
case "cat":
animal = new Cat();
break;
case "dog":
animal = new Dog();
break;
}
return animal ;
}
}
4、客户端
public class CreatAnimal {
public static void main(String[]args){
AnimalFactory.createAnimal("dog").start();
}
}
缺点:你看工厂类,你应该就知道缺点在哪里了,不做过多名词性解释。
注意事项:
工厂模式分三种:简单工厂模式、工厂模式、抽象工厂模式,简单工厂模式只是其中一种。
网上很多人把简单工厂模式和工厂模式给混淆了,这里需要给出区分:简单工厂模式,通过参数。工厂模式,通过继承。
spring中的例子:
BeanFactory:根据传入一个唯一的标识来获得bean对象。
BeanFactory和FactoryBean的区别:BeanFactory不解释了,FactoryBean就是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。
接口:一个类可以实现多个接口,该子类如果不是抽象类,那么实现了接口就必须重写接口中全部方法。
jdk1.7的接口:
jdk1.8的接口:
jdk1.9的接口:
注意事项:重写也叫覆写,两者一样。
重写与重载的区别:
抽象类:一个子类继承了抽象类,那么抽象类中的非抽象方法不用重写,其他必须重写。
jdk1.8的抽象类:
接口和抽象类的区别:接口解决了一个类只能继承一个抽象类的问题。
什么时候选择接口和抽象类:抽象类更多使用于类层级有共同性的情况,而接口更多使用于方法层级有共同性的情况。
接口只提供方法,不管实现得怎么样,而抽象类方法的实现最好符合父子类之间的关联特征。
为什么要用:可以存键值对,key不能重复,访问速度快,插入删除快,就这么简单。
怎么用:Map
需要注意什么:多线程下安全么?不安全。网上看一些博客专家写的关于hashmap为什么非线程安全,说的头头是道,看到最后都没有给你讲怎么去解决,服了,这不是耍流氓吗?
怎么解决:
jdk1.7:hashmap源码分析结果(原代码不打算贴出来了,读者可自行根据分析对应到源码中去)
jdk1.8:hashmap源码分析结果
jdk1.8:concurrenthashmap源码分析结果
在ConcurrentHashMap中定义了三个原子操作,用于对指定位置的节点进行操作。这三种原子操作被广泛的使用在ConcurrentHashMap的get和put等方法中,正是这些原子操作保证了ConcurrentHashMap的线程安全。
// 获取tab数组的第i个node
static final Node tabAt(Node[] tab, int i) {
return (Node)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
// 利用CAS算法设置i位置上的node节点。在CAS中,会比较内存中的值与你指定的这个值是否相等,如果相等才接受
static final boolean casTabAt(Node[] tab, int i,
Node c, Node v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
// 利用volatile方法设置第i个节点的值,这个操作一定是成功的。
static final void setTabAt(Node[] tab, int i, Node v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node[] tab = table;;) {
Node f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node pred = e;
if ((e = e.next) == null) {
pred.next = new Node(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node p;
binCount = 2;
if ((p = ((TreeBin)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
本文涉及其他知识点:
数组、链表、红黑树、hashcode、equals、CAS、死锁、乐观锁、SVN、锁分离思想、volatile原子性。
反射其实就是通过对class这个类进行展开来获取实例化对象。例子: spring中的bean。
有两种方式:
第一种也就是JDK自带的动态代理,利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,需要指定一个类加载器,然后生成的代理对象实现类的接口或类的类型,接着处理额外功能,JDK是基于接口。
第二种也就是Cglib的动态代理,Cglib是动态代理利用asm的开源包,对代理对象的Class文件加载进来,通过修改其字节码生成的子类来处理,Cglib是基于继承父类生成的代理类。
在Spirng当中动态代理的使用:
如果目标对象实现了接口,默认情况下会采用JDK的动态代理来实现AOP。
如果目标对象实现了接口,也可以强制使用CGlib来实现AOP。
如果目标对象没有实现接口,必须采用Cglib库,Spirng会自动在JDK和CGlib用切换。
如何强制使用CGlib来实现AOP:
添加CGlibjar包:SPRING_HOME/cglib/*.jar。
在Spring的配置文件中加入 //默认是false 也就是用JDK的 改为true就是用Cglib的。
JDK和动态代理和CGlib字节码的生成区别:
JDK动态代理制能对实现了接口的类生成代理,而不是针对类。
CGLIB是针对类实现代理,主要对指定的类生成一个子类,覆盖其中的方法,添加额外功能,因为是继承,所以该类方法不能用final来声明。
springioc :
例如我们在启动spring工程的时候,bean工厂applicationContext会负责创建实例bean,往各个bean注入依赖。 applicationContext会去调用一个叫refresh()方法,这个方法内部是重量级锁synchronized ,在里面会去根据配置来准备容器,完后会解析配置bean,通过类加载器来加载bean,然后全部注册到bean工厂(hashmap)里面,然后做一些初始化,会去初始化所有的bean。bean都是单例的。在除非是懒加载。
spring DI:
getBean方法会从容器中获取bean,然后注入实例。
springaop:
作用:可以在不影响核心业务的情况下,加入一些其他功能。
业务注解: @Aspect @Component @Pointcut(注解中的值便是切点的表达式) @AfterReturning()
aop指示器:
Advice 通知: 在连接点出要执行的代码。
自定义Annotation:
第一步:创建 Annotation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CalculateExecuteTime {
第二步:创建切面
@Aspect
@Component
public class CalculateExecuteTimeAspect {
第三步:创建切点和通知
@Aspect
@Component
public class CalculateExecuteTimeAspect {
@Around("@annotation(CalculateExecuteTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
//这里的 ProceedingJoinPoint 代表连接的的方法
第四步:在方法上加上自定义注解
@Service
public class WeixinService {
@CalculateExecuteTime
public void share(@NotNull String articleUrl) {
涉及知识:spring单例模式、懒加载。
spring事务:
隔离级别:default、read_uncommit、read_commit、repeatable_read、serializable
传播行为:required、supports、mandatory、requires_new、not_supported、never、nested
原理:事务就是去看哪个对象有加上事务注解,找到了就对象注解进行解析,当该操作出现异常的时候,会去执行通知器和拦截器中的方法,拦截器内部会根据事务属性来获取事务管理器,接下来就是在事务管理器里面去做判断和执行了,最后根据异常去调用管理器的回滚方法。回滚首先判断异常是不是RuntimeException和error,接着判断事务对象是不是和异常匹配,匹配就回滚。如果事务没有异常就进行提交,提交就想要去判断事务的状态,也就是判断有没有异常发生,没有就进行jdbc数据库提交。事务其实就是建立在aop之上的。
什么时候需要用到事务:
基于注解的事务使用方法:
事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
事务的超时性:@Transactional(timeout=30) //默认是30秒
事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
回滚:
只读:@Transactional(readOnly=true)
SpringMVC工作流程:
用户发送请求给前端控制器,控制器调用映射器来获取controller上的url,生成controller对象返回给前端控制器,前端控制器调用适配器来执行controller方法,将生成的模型视图返回给前端控制器,控制器调用视图解析器,解析成视图,返回给前端控制器,然后渲染给用户。
前端控制器(dispatcherservlet)
控制器映射器(handlermapping)
控制器(controller)
适配器(handleradapter)
视图解析器(viewreslover)
springmvc线程安全问题:是单例模式的问题
当有多个线程同时调用controller方法的时候尽量不要定义成员变量在controller里面,如果要使用到,可以利用scope=”prototype”进行声明,这样就把单例变成了多例,也可以使用ThreadLocal来定义变量。
常用注解:
@RequestBody:接收http请求的json数据,json转对象。
@ResponseBody:将controller返回对象转化为json对象响应给客户。
注解原理:
注解本身是annotation的扩展,spring反射会获取注解,返回生成的动态代理对象,调用代理对象invoke方法从常量池中获取索引对应值。
涉及知识:单例模式。