设计模式和反射

设计模式

定义

前⼈总结出来的对⼀些常⻅问题的解决⽅案,后⼈直接拿来解决特定问题⽽存在的解题思路。

分类

常⽤的设计模式:单例,⼯⼚,代理,适配器,装饰,模板,观察者等,⼀共有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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值