设计模式
定义
前⼈总结出来的对⼀些常⻅问题的解决⽅案,后⼈直接拿来解决特定问题⽽存在的解题思路。
分类
常⽤的设计模式:单例,⼯⼚,代理,适配器,装饰,模板,观察者等,⼀共有23种
第⼀:创建型模式:如何创建对象以及何时创建对象
包括:
⼯⼚模式(FACTORY METHOD)
抽象⼯⼚模式
建造(BUILDER)模式
代理模式(SINGLETON)
原型模式(Prototype)
第⼆:结构型模式:对象该如何组织以及采⽤什么样的结构更合理
包括:
适配器(Adapter)模式
合成(Composite)模式
装饰(Decorator)模式
代理(Proxy)模式
享元(Flyweight Pattern)模式
⻔⾯(Facade)模式
桥梁(Bridge)模式
第三:⾏为型模式:规定了各个对象应该具备的职责以及对象间的通信模式
包括:
策略(Strategy)模式
模板⽅法(Template Method)模式
观察者(observer)模式
迭代⼦(Iterator)模式
责任链模式
命令模式
备忘录模式
状态模式
访问者模式
解释器模式
调停者模式
六大原则
1.单⼀职责原则(SRP)
定义:系统中的每⼀个类都应该只有⼀个职责
好处:⾼内聚低耦合
2.开闭原则(OCP)
定义:对扩展开放,对修改关闭
好处:适应性和灵活性,稳定性和延续性,可复⽤性和可稳定性
3.⾥⽒替换原则(LSP)
定义:在任何⽗类出现的地⽅都可以⽤它的⼦类来替换,且不影响功能
好处:是加强程序的健壮性,同时版本升级也可以做到⾮常好的兼容性,增加⼦
类,原有的⼦类还可以继续运⾏。
4.依赖倒置原则(DIP)
定义:⾼层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象
好处:提⾼程序的稳定性,可维护性,可扩展性
5.接⼝隔离原则(ISP)
定义:使⽤多个专⻔的接⼝⽐使⽤单⼀的总接⼝要好
好处:不强迫新功能实现不需要的⽅法
6.迪⽶特原则(LOP)
定义:⼀个对象应该对其他对象尽可能少的了解
好处:⾼内聚,低耦合
缺点:通信效率降低,产⽣⼤量的中介类
单例设计模式
定义
⼀个类只允许有⼀个对象,建⽴⼀个全局的访问点,提供出去供⼤家使⽤.
分析
1.我们肯定要建⽴⼀个单例类来描述
2.只允许有⼀个对象
3.全局的访问点:说的就是当前的s----通过static实现的
4.提供出去
5.给⼤家使⽤
分类
饿汉式单例:在定义类中的变量时,直接给值(看下⾯的代码)
懒汉式单例:当通过公共⽅法调⽤s2的时候才给值(看下⾯的代码)
作⽤
总括:1.传值 .作为全局的访问点.
解决⼀个全局使⽤的类,频繁创建和销毁。拥有对象的唯⼀性,并保证内存中对象的唯⼀。可以节省内存,因为单例共⽤⼀个实例,有利于Java的垃圾回收机制。也就是控制资源使⽤,通过线程同步来控制资源的并发访问;
控制实例产⽣的数量,达到节约资源的⽬的;
使⽤场景
1.统计当前在线⼈数(⽹站计数器):⽤⼀个全局对象来记录。
2.打印机(设备管理器):当有两台打印机,在输出同⼀个⽂件的时候只⼀台打印机进⾏输出。
3.数据库连接池(控制资源):⼀般是采⽤单例模式,因为数据库连接是⼀种连接数据库资源,不易频繁创建和销毁。
数据库软件系统中使⽤数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率的损耗还是⾮常昂贵的,因此⽤单例模式来维护,就可以⼤⼤降低这种损耗。
4.应⽤程序的⽇志(资源共享):⼀般⽇志内容是共享操作,需要在后⾯不断写⼊内容所以通常单例设计。
package test;
import jdk.nashorn.internal.ir.CallNode;
/*
单例设计模式
*/
//饿汉式:当我们在使用成员变量的时候赋值
//饿汉式不会产生线程安全问题,因为共享的代码只有一行
//通常也会在单例类中会重写clone方法
//但是一般情况下,我们写单例类时,不需要clone
class SingleInstance{
//一般还会给当前的对象加final
private final static SingleInstance s = new SingleInstance();
//将构造方法私有化,达到外部方法无法访问的目的
private SingleInstance(){}
//写一个静态公共的方法,将单例的对象提供出去
public static SingleInstance getInstance(){
return s;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return s;
}
}
//懒汉式:当我们在使用的时候才会赋值
//因为共享的代码有多行,有可能发生线程安全问题
class S2{
private static S2 s2;
private S2(){}
//1、将方法变成同步的
//2、同步代码块
public static S2 getS2(){
//保证珊瑚橘同步的同时,尽量减少同步代码块的执行次数,一次来提高效率
if (s2 == null) {
//保证数据的同步
synchronized (S2.class) {
if (s2 == null) {
s2 = new S2();
}
}
}
return s2;
}
}
public class Demo1 {
public static void main(String[] args) {
/* SingleInstance s1 = new SingleInstance();
SingleInstance s2 = new SingleInstance();
System.out.println(s1 == s2);//false*/
SingleInstance s1 = SingleInstance.getInstance();
SingleInstance s2 = SingleInstance.getInstance();
System.out.println(s1==s2);
}
}
适配器设计模式
定义:
通常可以变相的理解成装饰设计模式
作⽤:
更好的复⽤性 如果功能已经存在,只是接⼝不兼容,通过适配器模式就可以让这些功能得到更好的复⽤。
更好的扩展性 在实现适配器功能时,可以调⽤⾃⼰开发的功能,从⽽⾃然的扩展系统的功能。
使⽤场景
系统需要使⽤现有已投产的类,⽽这些类的接⼝不符合系统的需要。 想要建⽴⼀个可以重复使⽤的类,⽤于与⼀些彼此之间没有太⼤关联的⼀些类,包括⼀些可能在将来引进的类⼀起⼯作。 Java中的数据库连接⼯具JDBC,JDBC定义⼀个客户端通⽤的
抽象接⼝,每⼀个具体数据库引擎(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是⼀个介于JDBC接⼝和数据库引擎接⼝之间的适配器软件
分析:Dog是继承了ZiMidel类,ZiMidel类实现了Inter接⼝
interface Inter{
public void play();
public void song();
public void run();
public void eat();
public void jump();
}
//适配器类
class ZiMidel implements Inter{
@Override
public void play() {
// TODO Auto-generated method stub
}
@Override
public void song() {
// TODO Auto-generated method stub
}
@Override
public void run() {
// TODO Auto-generated method stub
}
@Override
public void eat() {
// TODO Auto-generated method stub
}
@Override
public void jump() {
// TODO Auto-generated method stub
}
}
//创建狗类,我只想让她实现play⽅法?
class Dog extends ZiMidel{
public void play() {
// TODO Auto-generated method stub
}
}
class Cat extends ZiMidel{
public void song() {
}
}
模板设计模式
定义
我们在实现⼀个功能的时候,功能分成两部分,⼀部分是确定的,⼀部分是不确定的.将确定的部分交给当前类实现,将不确定的部分交给⼦类实现.⼦类实现的结果⼜会反过来影响确定部分的功能.
作⽤
模板设计模式是通过把不变的⾏为挪到⼀个统⼀的⽗类,从⽽达到去除⼦类中重复代码的⽬的、⼦类实现模板⽗类的某些细节,有助于模板⽗类的扩展.通过⼀个⽗类调⽤⼦类实现的操作,通过⼦类扩展增加新的⾏为,符合“开放-封闭原则”
package test;
public class Demo2 {
public static void main(String[] args) {
Zi fu = new Zi();
long time = fu.getTime();
System.out.println("-------------Time--------------");
System.out.println(time);
}
}
abstract class Fu{
abstract public void function();
public long getTime(){
long startTime = System.nanoTime();
function();
long endTime = System.nanoTime();
return endTime-startTime;
}
}
class Zi extends Fu{
@Override
public void function() {
for (int i = 0; i < 100; i++) {
System.out.println("i:"+i);
}
}
}
代理设计模式
定义
我很忙,忙的没空理你,那你要找我呢就先找我的代理⼈吧,那代理⼈总要知道被代理⼈能做哪些事情不能做哪些事情吧,那就是两个⼈具备同⼀个接⼝,代理⼈虽然不能⼲活,但是被代理的⼈能⼲活呀。
作⽤
代理模式主要使⽤了 Java 的多态,⼲活的是被代理类,代理类主要是接活,你让我⼲活,好,我交给幕后的类去⼲,你满意就成,那怎么知道被代理类能不能⼲呢?同根就成,⼤家知根知底,你能做啥,我能做啥都清楚的很,同⼀个接⼝呗。
代理⽅法分类
静态代理
动态代理
静态代理
作⽤:可以实现简单代理 根据OCP(对扩展开放,对修改关闭)的原则,在不改变原来类的基础上,给这个类增加额外的功能
缺点:代理对象要保证跟⽬标对象实现同样的接⼝,在维护的时候两个对象都要维护,⽽且代理对象实现的接⼝是死的,这时如果要给想实现不同功能的多个⽬标对象添加代理对象的话,要添加很多个类
package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Demo3 {
public static void main(String[] args) {
TestInter bingbing = new Bingbing();
//动态代理
//调⽤动态代理的⽅法实现功能
/**
*动态⽣成代理对象的⽅法--通过JDK内置的
java.lang.reflect.Proxy动态代理类完成代理对象的创建
*参数⼀:这⾥代表类加载器,代理类的类加载器要与⽬标类的类加载器⼀
致,类加载器⽤来装载内存中的字节码⽂件
*参数⼆:代理类与⽬标类实现的接⼝必须有相同的,即指定给代理类的接
⼝,⽬标类必须实现了
*参数三:代理类的构造⽅法⽣成的对象--注意:指定给构造⽅法的参数要使
⽤Object
*
*/
TestInter o = (TestInter)Proxy.newProxyInstance(bingbing.getClass().getClassLoader(), new Class[]{TestInter.class},
new InvocationHandler() {
/**
* 接⼝中的⽅法
* 主要:这个⽅法在调⽤接⼝⽅法的时候,会被⾃动调动
* 参数⼀:代理对象的引⽤
* 参数⼆:⽬标对象的⽅法
* 参数三:⽬标对象的⽅法参数
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("先交中介费");
Object o = method.invoke(bingbing, args);
System.out.println("开心的笑");
return o;
}
});
o.findHouse();
System.out.println("------------------------------------------------");
o.findGirl();
System.out.println("--------------------------------------------------");
TestEat testEat = new LangLang();
TestEat t = (TestEat)Proxy.newProxyInstance(testEat.getClass().getClassLoader(), new Class[]{TestEat.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("先洗个手");
Object o1 = method.invoke(testEat, args);
System.out.println("开心的玩耍");
return o1;
}
});
t.eat();
}
}
interface TestInter{
void findHouse();
void findGirl();
}
interface TestEat{
void eat();
}
class Bingbing implements TestInter{
@Override
public void findHouse() {
System.out.println("冰冰在西三旗找房");
}
@Override
public void findGirl() {
System.out.println("冰冰找陪玩");
}
}
class LangLang implements TestEat{
@Override
public void eat() {
System.out.println("郎朗吃大米饭啦");
}
}
反射
动态获取类的字节码⽂件,并对其成员进⾏抽象
整体的含义:就是想通过字节码⽂件直接创建对象.
JAVA反射机制是在运⾏状态中,对于任意⼀个实体类,都能够知道这个类的所有属性和⽅法;对于任意⼀个对象,都能够调⽤它的任意⽅法和属性;这种动态获取信息以及动态调⽤对象⽅法的功能称为java语⾔的反射机制。JAVA反射(放射)机制:“程序运⾏时,允许改变程序结构或变量类型,这种语⾔称为动态语⾔”。从这个观点看,Perl,Python,Ruby是动态语⾔,C++,Java,C#不是动态语⾔。但是JAVA有着⼀个⾮常突出的动态相关机制:Reflection,⽤在Java身上指的是我们可以于运⾏时加载、探知、使⽤编译期间完全未知的classes。换句话说,Java程序可以加载⼀个运⾏时才得知名称的class,获悉其完整构造(但不包括methods定义),并⽣成其对象实体、或对其fields设值、或唤起其method
Class: 不是⽤来声明类的时候⽤到的关键字。(注意:⾸字⺟⼤写)。他是⼀个类。是⼀个⽐较特殊的类。继承⾃Object类的。可以⽤Class对象来描述⼀个类中的成员
Class对象的获取:
1.通过对象.getClass()
通过类.class
3.通过Class类的静态⽅法 Class.forName(String name)
注意:参数name需要是类的全限定名
如果要访问的是⼀个内部类,则需要写编译后.class⽂件的名字
Class类常⽤⽅法:
setAccessible(boolean flag);
// 从字⾯⾮常容易被理解为设置⼀个属性、⽅法、构造⽅法的可访问性。
// 这个表示的是是否需要进⾏访问权限的检查。
// 如果参数是true:则在访问对应的成员的时候,不去进⾏访问权限的检查,直接访问。
// 如果参数是false: 则在访问对应的成员之前,先去检查访问权限
实现原理分析
实现过程:1.获取字节码⽂件对象 2.通过字节码⽂件对象获取对应的实例对象 3.给属性赋值(通过从属性中提取出来的类–Field) 4.调⽤⽅法(通过从⽅法中提取出来的类–Method)
获取字节码⽂件对象
//1.通过Object提供的getClass()⽅法
// ⾸先必须要有⼀个对象 XXX
//2.通过每种数据类型都有的⼀个class属性
// 在使⽤的位置必须当前的类是可⻅的,因为这⾥要显示的使⽤这个类名,对类的依
赖性太强,使⽤不⽅便 XXX
//3.Class类提供的⼀个静态⽅法forName(字符串) 字符串:包名+类名
// 我们只需要提供⼀个当前类的字符串形式即可
public static void fun1() {
Person person = new Person();
Person person1 = new Person();
Class<?> class1 = person.getClass();
Class<?> class2 = person.getClass();
System.out.println(class1 == class2);//true
System.out.println("value:"+(person.getClass() ==
person1.getClass()));
}
通过字节码⽂件对象获取对应的实例对象
public static void main(String[] args){
//普通⽅式
//Person person = new Person();
//通过反射创建普通对象
Class<?> class1 = Class.forName("com.qf.refect.Person");
//⽅法⼀:通过⽆参的构造⽅法创建实例对象
fun1(class1);
//⽅法⼆:通过有参的构造⽅法创建实例对象
fun2(class1);
}
//⽅法⼀:通过⽆参的构造⽅法创建实例对象
public static void fun1(Class<?> cls) throws
InstantiationException, IllegalAccessException {
//创建实例对象
//这⾥相当于在newInstance⽅法的内部调⽤了⽆参的构造⽅法
Object object = cls.newInstance();
Person person = (Person)object;
person.setName("bingbing");
System.out.println(person.getName());
}
//⽅法⼆:通过有参的构造⽅法创建实例对象
public static void fun2(Class<?> cls) throws
NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
//先得到有参的构造⽅法
//这⾥要写参数的字节码⽂件对象形式 所有的类型都有字节码⽂
件对象
//相当于 public Person(String name, int age)
Constructor constructor =
cls.getConstructor(String.class,int.class);
Object object = constructor.newInstance("bingbing",18);
System.out.println(object);
}
给属性赋值(通过从属性中提取出来的类–Field)
public static void main(String[] args) throws
ClassNotFoundException, InstantiationException,
IllegalAccessException, NoSuchFieldException, SecurityException
{
Person person = new Person();
//person.name = "bingbing";
//使⽤反射实现
//1.获取字节码⽂件对象
Class<?> class1 = Class.forName("com.qf.refect.Person");
//2.获取实例对象
Object object = class1.newInstance();
//3.调⽤属性
//注意:如果想使⽤getField,name属性必须是public的
//Field field1 = class1.getField("name");
//如果name是私有的,我们可以这样做 ,忽略权限
Field field1 = class1.getDeclaredField("name");
field1.setAccessible(true);
//赋值
//第⼀个参数:关联的具体对象
//第⼆个参数:赋的值
field1.set(object, "bing");
System.out.println(field1.get(object));
}
调⽤⽅法(通过从⽅法中提取出来的类–Method)
//调⽤⾮静态⽆参
public static void fun1(Class<?> cla) throws
InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException,
IllegalArgumentException, InvocationTargetException {
//2.获取实例对象
Object object = cla.newInstance();
//3.通过反射得到⽅法
Method method = cla.getMethod("show");
//4.调⽤⽅法,通过调⽤invoke⽅法实现
Object obj = method.invoke(object);
System.out.println("obj:"+obj);//3
}
//调⽤⾮静态有参
public static void fun2(Class<?> cla) throws
NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
//2.先得到有参的构造⽅法
Constructor<?> constructor =
cla.getConstructor(String.class,int.class);
Object object = constructor.newInstance("bingibn",10);
//3.通过反射得到⽅法
Method method = cla.getMethod("callPhone",String.class);
//4.调⽤⽅法,通过调⽤invoke⽅法实现
Object obj = method.invoke(object,"110");
System.out.println("obj:"+obj);//null
}
//调⽤静态有参
public static void fun3(Class<?> cla) throws
NoSuchMethodException, SecurityException,
IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
//3.通过反射得到⽅法
Method method = cla.getMethod("run",int.class);
//4.调⽤⽅法,通过调⽤invoke⽅法实现
method.invoke(null,11);
}