1.什么是设计模式?
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、代码设计经验的总结。
2.设计模式的作用
使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
3.常见设计模式
常见的设计模式有23种。
下面介绍非常常见的三种模式:单例模式、工厂模式、代理模式。
3.1 单例模式
当类被频繁的创建与销毁的时候,我们使用单例模式,这样可以减少内存的开销,避免对资源的多重占用。
单例模式----保证一个类仅有一个实例对象。
因此单例模式的①构造方法私有;②提供一个静态方法返回创建好的当前类对象。
单例模式有两种方式:懒汉模式、饿汉模式
3.1.1 懒汉模式
/**
* 懒汉式
* 在方法里面判断后才new,比较懒
*
*/
public class SingleObject1 {
private SingleObject1(){
//私有的构造方法,其他类new不出来
}
private static SingleObject1 obj1=null;
//通过静态方法返回对象
public static SingleObject1 getSingleObject1(){
if(obj1==null){
obj1=new SingleObject1();
}
return obj1;
}
}
测试类:
public class Main {
public static void main(String[] args) {
//拿两个对象,看看是不是同一个
SingleObject1 object1 = SingleObject1.getSingleObject1();
SingleObject1 object2 = SingleObject1.getSingleObject1();
if(object1==object2){
System.out.println("是同一个对象");//对象再用==号比较的是引用地址
}
//引用地址查看hashCode
System.out.println(object1.hashCode());
System.out.println(object2.hashCode());
}
}
输出:
是同一个对象
1829164700
1829164700
3.1.2 饿汉模式
/**
* 饿汉模式
* 先在定义里new再返回
*
*/
public class SingleObject2 {
private static SingleObject2 obj2=new SingleObject2();
private SingleObject2(){
}
/*
public static SingleObject2 getSingObject2(){
return obj2;
}
*/
//在多线程情况下,为了保证当前类对象只有一个我们就需要添加“synchronized”
public static synchronized SingleObject2 getSingleObject2(){
return obj2;
}
}
测试类:
public class Main {
public static void main(String[] args) {
// 拿两个对象,看看是不是同一个
SingleObject2 object11 = SingleObject2.getSingleObject2();
SingleObject2 object22 = SingleObject2.getSingleObject2();
if (object11 == object22) {
System.out.println("是同一个对象");// 对象再用==号比较的是引用地址
}
// 引用地址查看hashCode
System.out.println(object11.hashCode());
System.out.println(object22.hashCode());
}
}
输出:
是同一个对象
2018699554
2018699554
3.1.3 懒汉模式和饿汉模式区别****
相同点:
①都保证当前类的对象只有一个;
②构造方法都是私有;
③都是提供一个静态方法返回创建好的当前类对象。
不同点:
①懒汉式中保证当前类的对象变量初始为null;饿汉式中保证当前类的对象变量初始为new好的当前类对象。
②运行速度上懒汉式比饿汉式稍微差一些;
③资源利用率上懒汉式比饿汉式要好一些;
3.2 工厂模式
有一个专门的java类充当生产对象的工厂。
使用工厂模式的条件:
(1)需求量大;
(2)牵一发动全身;
工厂模式中的角色:
工厂角色——生产对象
抽象产品角色——【抽象类/接口】
具体产品——【抽象类/接口子类】
例:有农场生产各种水果,有西瓜、苹果、香蕉……
工厂角色——农场
抽象产品角色——水果
具体产品——西瓜、苹果、香蕉
以前做法:
public interface Fruit {
//抽象方法
void eat();
}
public class Apple implements Fruit {
@Override
public void eat() {
System.out.println("我是苹果,需要清洗以后吃!!");
}
}
/**
* 生产水果的农场
* @author Betty
*
*/
public class Farm {
public static Fruit creatFruit(String name){
Fruit fruit=null;
if(name.equals("苹果")){
fruit=new Apple();
}
return fruit;
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestMain {
public static void main(String[] args) throws Exception {
BufferedReader read=new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入您需要的水果:");
String name = read.readLine();
Fruit apple = Farm.creatFruit(name);
apple.eat();
}
}
输出:
请输入您需要的水果:苹果
我是苹果,需要清洗以后吃!!
当需要继续加一个产品的时候比如香蕉,需要再创建一个香蕉类实现水果接口,然后在农场里修改代码。修改源代码。
改进:
public class XiGua implements Fruit{
@Override
public void eat() {
System.out.println("我是西瓜,需要削皮吃");
}
}
public class XiangJiao implements Fruit{
@Override
public void eat() {
System.out.println("我是香蕉,需要剥开吃");
}
}
创建一个menu.txt文件,写入
苹果=com.weiwei.test2.Apple
西瓜=com.weiwei.test2.XiGua
香蕉=com.weiwei.test2.XiangJiao
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
/**
* 生产水果的农场
*/
public class Farm {
public static Fruit creatFruit(String name){
Fruit fruit=null;
//读取菜单
HashMap<String, String> readMenu = readMenu();
String className = readMenu.get(name);
//利用反射机制创建对象
try {
Class classobj = Class.forName(className);//得到反射对象
fruit=(Fruit)classobj.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return fruit;
}
/**
* 读取菜单
*/
private static HashMap<String,String> readMenu() {
HashMap<String,String> menuMap=new HashMap<String,String>();
try {
BufferedReader read=new BufferedReader(new FileReader(new File("menu.txt")));
String menuitem=null;
while((menuitem=read.readLine())!=null){
//苹果=com.weiwei.test2.Apple
String[] menuarray = menuitem.split("=");
menuMap.put(menuarray[0],menuarray[1]);
}
read.close();
} catch (Exception e) {
e.printStackTrace();
}
return menuMap;
}
}
测试类:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestMain {
public static void main(String[] args) throws Exception {
BufferedReader read=new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.print("请输入您需要的水果:");
String name = read.readLine();
Fruit fru = Farm.creatFruit(name);
fru.eat();
}
}
}
输出:
请输入您需要的水果:西瓜
我是西瓜,需要削皮吃
请输入您需要的水果:苹果
我是苹果,需要清洗以后吃!!
请输入您需要的水果:香蕉
我是香蕉,需要剥开吃
如果还需要再加水果产品,比如葡萄,只需要新建一个葡萄类实现水果接口,另外在菜单文件中写入葡萄=com.weiwei.PuTao即可;不需要修改农场中的代码。
3.3 代理模式
代理模式——为其他对象提供一种代理以控制对这个对象的访问。比如买火车票可以在车站买也可以在代售点买。
代理模式被分为静态代理和动态代理。
3.3.1 静态代理
静态代理被分为兄弟模式和父子模式。兄弟模式就是同一个接口的两个子类;父子模式就是继承关系。
静态代理模式:
兄弟模式——同一个接口的两个子类
public interface SellPiao {
void maipiao();
}
public class DaiShouDian implements SellPiao{
@Override
public void maipiao() {
System.out.println("代售点买票");
}
}
public class HuoCheZhan implements SellPiao{
public void maipiao(){
System.out.println("火车站买票");
}
}
public class Main {
public static void main(String[] args) {
HuoCheZhan hcz = new HuoCheZhan();
hcz.maipiao();
DaiShouDian dsd = new DaiShouDian();
dsd.maipiao();
}
}
父子模式——继承关系
修改代售点为继承火车站
public class DaiShouDian extends HuoCheZhan{
@Override
public void maipiao() {
System.out.println("代售点买票");
}
}
目前是一个代售点,创建了一个代理类,如果有很多代售点,就需要创建很多代理类。因此不是很好。
静态代理缺点:需要额外提供业务功能实现相似的子类。【工作量大】
3.3.2 动态代理
由一个java类类负责创建代理类的的对象。
动态代理分为JDK动态代理和CGlib动态代理。
3.3.2.1 JDK动态代理
JDK动态代理——通过java.lang.reflect包 Class Proxy类来创建代理类对象
【只能为实现过某个接口的java类提供代理类对象】
public interface SellPiao {
void maipiao();
}
public class HuoCheZhan implements SellPiao{
public void maipiao(){
System.out.println("火车站买票");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyObject implements InvocationHandler{
//定义目标对象
private Object targetObject;
public ProxyObject(Object targetObject){
this.targetObject=targetObject;
}
//得到代理对象
public Object getProxy(){
//通过java.lang.reflect包 Class Proxy类来创建代理类对象
/*ClassLoader loader-----类加载器,当前类的反射对象里有一个方法可以得到
* Class<?>[] interfaces-----接口的反射对象,从目标对象的反射对象来
* InvocationHandler h-----这个类的对象,传子类就好,因此可以使用当前类去实现这个接口
*/
//Proxy.newProxyInstance(loader, interfaces, h)
ClassLoader loader = this.getClass().getClassLoader();
Class[] interfaces = this.targetObject.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
}
//上面得到的仅仅是代理对象,下面重写的这个方法就是去调用方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(targetObject, args);
}
}
public class Main {
private static final Object SellPiao = null;
public static void main(String[] args) {
HuoCheZhan hcz=new HuoCheZhan();
ProxyObject proxyobj=new ProxyObject(hcz);
Object obj = proxyobj.getProxy();//这个就相当于代售点对象
SellPiao dsd=(SellPiao)obj;//转换类型
dsd.maipiao();
}
}
3.3.2.2 CGlib动态代理
CGlib动态代理——是一个第三发的开发包,用的时候需要自己事先下载导入到项目中。
【为所有的java类提供代理类对象】
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyObject implements MethodInterceptor{
//定义目标对象
private Object targetObject;
public ProxyObject(Object targetObject){
this.targetObject=targetObject;
}
//得到代理对象
public Object getProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
//上面得到的仅仅是代理对象,下面重写的这个方法就是去调用方法
@Override
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
return methodProxy.invokeSuper(proxy, params);
}
}
public class Main {
private static final Object SellPiao = null;
public static void main(String[] args) {
HuoCheZhan hcz=new HuoCheZhan();
ProxyObject proxyobj=new ProxyObject(hcz);
Object obj = proxyobj.getProxy();//这个就相当于代售点对象
HuoCheZhan dsd=(HuoCheZhan)obj;//转换类型
dsd.maipiao();
}
}
3.3.3 区别
1.静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
缺点:手动创建一个与目标类相同接口的子类,包装目标类。
2.JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;【兄弟模式】
缺点:通过jdk提供的反射包中Proxy这个类,动态的创建一个与目标类实现相同接口的子类对象,包装目标。
3.CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。【父子模式】
缺点:通过CGlib提供的Enhancer这个类,动态的创建一个目标类的子类对象,包装目标类。
静态代理 | JDK动态代理 | CGlib动态代理 |
手动创建代理类 | 动态生成代理类对象 | 动态生成代理类对象 |
jdk提供的反射包中Proxy这个类 | CGlib提供的Enhancer这个类 | |
只能为实现过接口的Java类创建代理对象 | 为任何Java类创建代理对象 |