Java进阶(注解,设计模式,对象克隆)

Java进阶(注解,设计模式,对象克隆)

一. 注解

1.1 什么是注解

java中注解(Annotation),又称java标注,是一种特殊的注释

可以添加在包,类,成员变量,方法,参数等内容上

注解会随同代码被编译到字节码文件中

在运行时,可以通过反射机制获取到类中注解,然后根据不同的注解进行相应的解析

1.2 内置注解

java中已经定义好的注解

//检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中没有该方法时,会发生编译报错
@Override	
//标记过时方法。如果使用该方法,会报编译警告
@Deprecated
//指示编译器去忽略注解中声明的警告
@SuppressWarnings
//用于指示被修饰的接口是函数式接口
//函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,具体实现可以用Lambda表达式
@Functionallnterface

@Functionallnterface:

比如Runnable中有个@Functionallnterface注解,当我们在使用Thread时,需要实现Runnable中的run。而Runnable就是函数式接口,需要创建内部类实现。

new Thread(new Runnable(){@Overridepublic void run(){}
})

1.3 元注解

是注解的注解,用来定义其他注解的注解

@Target(ElementType.METHOD)	//标注此注解可以作用在哪些内容上面
@Target(ElementType.TYPE,ElementType.METHOD)	//标注此注解可以作用在方法和类上@Retention(RetentionPolicy.SOURCE)	//在编译阶段有用的,可以不编译到字节码中
@Retention(RetentionPolicy.RUNTIME)	//在运行中有用,编译到字节码中

1.4 自定义注解

声明注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {//声明注解的参数String message() default "";int minlength() default 0;String lengthmessage() default "";
}

解析注解:

public class Test {public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception {User user = new User();//user.setName("jim");//通过反射解析User类中注解Field[] fields = user.getClass().getDeclaredFields();//拿到类中所有的成员变量 连同私有的也可以获取//循环所有的属性for (Field field : fields) {NotNull notNull = field.getAnnotation(NotNull.class);//获取属性上面 名字为NotNull注解if (notNull != null) {//通过属性,生成对应的get方法Method m = user.getClass().getMethod("get" + getMethodName(field.getName()));//调用方法  obj就是get方法的返回值Object obj=m.invoke(user);if (obj==null) {System.err.println(field.getName() +notNull.message());throw new NullPointerException(notNull.message());}else{if(String.valueOf(obj).length()<(notNull.minlength())){System.err.println(field.getName() +notNull.lengthmessage());throw new NullPointerException(notNull.lengthmessage());}}}}}/*** 把一个字符串的第一个字母大写*/private static String getMethodName(String fildeName) throws Exception {byte[] items = fildeName.getBytes();items[0] = (byte) ((char) items[0] - 'a' + 'A');return new String(items);}
}

使用注解:

public class User {private int num=0;@NotNull(message="姓名不能为空",minlength=3,lengthmessage="长度不能小于3")private String name=null;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}}

二. 对象克隆

2.1 为什么要克隆

为什么不可以直接new一个对象呢?因为直接new一个对象的属性都是初始化的值,所以当需要一个对象直接来保存当前对象的状态就要clone()了

我们常见的

Student stu1=new Student();
Student stu2=stu1;

这只是复制的引用,也就是对象在内存中的地址被复制,ab对象都指向了同一个对象。只能称为引用复制,两个引用指向的还是一个对象。

2.2 如何实现克隆

有两种克隆方法深克隆和浅克隆

基本类型的值可以直接复制,引用类型的值只能复制引用地址。所以深浅克隆的区别在于是否支持引用类型的成员变量的复制。

@Override
protected Person clone() throws CloneNotSupportedException {Person person = (Person)super.clone();return person;
}
Person p1 = new Person(100,"jim");
Person p2 =  p1.clone(); //克隆一个新的对象System.out.println(p1==p2);//false,说明克隆成功,两个对象地址不一样,是不同的对象
2.2.1 浅克隆和深克隆

对于基本类型,在对象克隆时,可以将值直接复制到新对象中。

int a=10;
int b=a;
b=20;	//b改变了a不受影响

只想把值属性克隆然后只把对象属性的地址复制则就是浅克隆

如果克隆还想要复制一个对象属性,在克隆一个对象时同时克隆了它的关联对象则就是深克隆

2.2.2 如何实现深克隆

方式1:在克隆对象时,将对象中关联的对象也一同进行克隆,虽然能实现,但是要逐级进行克隆,层级较多时,比较麻烦

@Override
protected Person clone() throws CloneNotSupportedException {Person person = (Person)super.clone();person.address = (Address)person.address.clone();   //深度复制  联同person中关联的对象也一同克隆.return person;
}

方式2:使用对象序列化(IO) 需要我们定义一个克隆方法,先将对象序列化再反序列化,自动将多级关联的对象也一并重新创建,使用起来比较方便

​ 对象序列化:将java中的对象输出到一个文件中

​ ObjectOutputStream

​ 反序列化:将文件中信息输入到程序,创建一个新的对象

​ ObjectInputStream

public Person myclone() {Person person = null;try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(this);// 将流序列化成对象ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);person = (Person) ois.readObject();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return person;
}

三. 设计模式

3.1 概述

设计模式起源于建筑领域,在1990年软件领域也诞生设计模式。直到1995年在《设计模式:可复用面向对象软件的基础》中收纳总结了23种设计模式。

什么是设计模式?

在长期编程的过程中,针对某一类问题经过反复的优化,最终总结出一个固定的解决方案,这些方案经过反复地使用,具有普遍性。

为什么要学设计模式?
使设计的代码可重用性高,可扩展性高,使程序设计标准化,提高效率。能够更好的去理解源码架构

3.2 建模语言

3.2.1 类

统一建模语言(Unified Modeling Language,UML)是一套软件设计和分析的语言工具,用图形化的方式记录类与类,类与接口,接口与接口之间的关系。

(1) 类名(Name)是一个字符串,例如,Student。

(2) 属性(Attribute)是指类的特性,即类的成员变量。UML 按以下格式表示: [可见性]属性名:类型[=默认值] 例如:-name:String 注意:“可见性”表示该属性对类外的元素是否可见,包括公有(Public)、私 有(Private)、受保护(Protected)和朋友(Friendly)4 种,在类图中分别 用符号+、-、#、~表示。

(3) 操作(Operations)是类的任意一个实例对象都可以使用的行为,是类的成 员方法。UML 按以下格式表示: [可见性]名称(参数列表)[:返回类型] 例如:+display():void。

在这里插入图片描述

3.2.2 接口

接口(Interface)是一种特殊的类,它具有类的结构但不可被实例化,只可以 被子类实现。它包含抽象操作,但不包含属性。它描述了类或组件对外可见的动 作。在 UML 中,接口使用一个带有名称的小圆圈来进行表示。

在这里插入图片描述

3.2.3 类之间的关系

在软件系统中,类不是孤立存在的,类与类之间存在各种关系。根据类与类 之间的耦合度从弱到强排列,UML 中的类图有以下几种关系:依赖关系、关联 关系、聚合关系、组合关系、泛化关系和实现关系。其中泛化和实现的耦合度相 等,它们是最强的

3.2.3.1 依赖关系

在一个类中的方法,把另一个类作为参数进行使用,具有临时性。

方法执行结束后,依赖关系就不存在了。

一般把xxx类用到了yyy类,这种关系称为依赖关系,也成为use-a关系

在这里插入图片描述

3.2.3.2 关联关系

在一个类中,把另一个当做自己的成员,比如老师和学生,师傅和徒弟。关联可以是单向关联,双向关联,自关联。

自关联:

在这里插入图片描述

3.2.3.3 聚合关系

聚合关系也是一种关联关系,是强关联关系,是整体和部分之间的关系。

比如学校与老师,学校包含老师,如果学校没了,老师依然存在。

在这里插入图片描述

3.2.3.4 组合关系

组合关系表示类之间整体与部分的关系,是一种更强烈的聚合关系。

比如头和嘴,一旦头不存在,嘴也就不存在。

在这里插入图片描述

3.2.3.5 继承关系

继承关系是对象之间耦合度最大的一种关系,是父类与子类的关系,is-a关系

在这里插入图片描述

3.2.3.6 实现关系

实现关系是接口和实现类之间的关系。类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。

在UML类图中,实现关系使用空心三角箭头的虚线来表示,箭头从实现类指向接口。比如汽车和船实现了交通工具。其类图如下:

在这里插入图片描述

3.3 面向对象设计原则

3.3.1 单一职责原则

一个类只负责某一个具体功能,细化类的颗粒度

3.3.2 开闭原则

对修改关闭,对扩展开放

尽可能在扩展功能时,不要修改已有代码,尽可能扩展一个新类实现新功能

3.3.3 里氏替换原则

继承优势:提高代码复用性,子类继承父类的功能

​ 提高代码的扩展性,子类还可以扩展自己功能,不影响其他类,重写父类方法

​ 劣势:继承使得类的体系结构变得复杂了

里氏替换:当子类继承了父类后,在使用时,用子类替换父类后,要确保父类中的功能不受影响。比如父类的方法目的是要完成一个乘法,子类重写后变成先乘再加了,这样就改变了父类这个方法的功能了,子类重写方法的前提是不改变父类方法的目的,子类可以通过别的方式完成这个乘法,但是不能改变它这个乘法的功能。

所以可以把父类方法写成抽象方法,不具体实现,等子类继承后实现

主要的思想:保证程序的稳定性

3.3.4 组合/聚合(关联关系)复用原则

继承使得类的体系变得复杂,如果我们只是想使用某个类中的方法,可以优先选择关联/依赖关系,降低类之间的耦合度。

比如B中使用A的某个方法,可以直接在B里声明一个A属性

class b{A a;public void use(){a.method();}
}

或者在B里写一个使用A中方法的方法,把A当参数传进来

class b{public void use(A a){a.method();}
}
3.3.5 依赖倒置

面向抽象编程,不要面向具体实现变成

具体实现应该依赖抽象层(多态,抽象层表示,具体的子实现类)

3.3.6 接口隔离

不要把所有的功能都定义到一个总的接口中,应该把不同的种类的功能定义在不同的接口中,让实现类根据自己的需要去灵活的选择

3.3.7 迪米特

只跟朋友联系,不跟陌生人说话。

在程序之间相互调用时,如果两个类没有直接联系,但是想相互调用,可以通过第三方进行转发调用。降低模块之间的耦合度。

四. 23种设计模式

4.1 单例模式

在一个项目中,如何确保一个类始终只有一个对象

4.1.1 饿汉式(急切式单例)

单例模式中的饿汉式(急切式单例)

在加载此类时,就已经将唯一的一个对象创建出来

好处:不会存在线程安全问题

不足:在类加载时,就会创建单例对象,有可能一段时间内还用不到它

public class MyWindow {//用static修饰,在第一次加载此类时已经把唯一的一个对象创建出来,后面getMyWindow都是这同一个MyWindow//在内部自己创建的一个单例对象static MyWindow myWindow=new MyWindow();private MyWindow(){}public static MyWindow getMyWindow(){return myWindow;}
}

eg:Runtime单例

4.1.2 懒汉式

在类加载时,并没有创建单例对象,在第一次获取单例对象时,才创建了单例对象

好处:类加载时先不创建,在第一次使用获取时才会创建

不足:会出现线程安全问题,所以需要加锁解决

public class MyWindow {private static MyWindow myWindow;//构造方法私有化,不让外界访问private MyWindow(){}
}

写法1:会出现线程安全问题,多个线程同时进入,会返回多个对象

public static MyWindow getMyWindow(){if(myWindow==null){myWindow = new MyWindow();}return myWindow;
}

写法2:为方法加锁,效率低 一次只能有一个线程进入到该方法

public class MyWindow2 {private static MyWindow2 myWindow2;private static synchronized MyWindow2(){}public static MyWindow2 getMyWindow2(){if(myWindow2==null){myWindow2=new MyWindow2();}return myWindow2;}
}

写法3:双重检索

public static    MyWindow getMyWindow(){if(myWindow==null){synchronized (MyWindow.class){if(myWindow==null){myWindow = new MyWindow();}}}return myWindow;
}

写法4:

双重检索 + volatile(可见性,避免重排序)
A a = new A();
创建对象这一条语句编译为指令时,可以分为三个指令

  1. new 申请空间
  2. 调用构造方法初始化对象
  3. 把对象地址 赋给引用变量
    如果按照这个正常的顺序执行,是没有问题的,
    但是执行时,如果2,3条指令顺序发生变化,导致把没有初始化完成的对象地址返回了,拿去使用了,这么做会出问题,
    因为对象没有初始化完成.
    所有需要使用volatile关键修饰单例成员变量,确保对其赋值时,指令不重新排序
private volatile static MyWindow myWindow;
public static    MyWindow getMyWindow(){if(myWindow==null){synchronized (MyWindow.class){if(myWindow==null){myWindow = new MyWindow();}}}return myWindow;
}

4.2 工厂模式

4.2.1 简单工厂模式

简单工厂并不是一种设计模式,违背了开闭原则

主要是引出了工厂方法和抽象工厂模式

涉及的角色

工厂角色:根据我们的需求负责创建对应的对象

抽象产品:具体产品的抽象,具体产品实现/继承抽象产品

​ 可以使用上层的抽象父类,表示任意的子类对象

具体产品:具体的对象

优点:创建对象和使用对象分离了

缺点:只能创建实现同一个父类/接口的子类对象,扩展新的类型,需要修改工厂,违背了开闭原则

​ 适合简单的,子类较少的场景

CarFactory:

public class CarFactory {public static Car createCar(String name){if(name.equals("aodi")){return new Aodi();}if(name.equals("bmw")){return new Bmw();}return null;}
}

Car:

public interface Car {void run();
}

Bmw:

public class Bmw implements Car{@Overridepublic void run() {System.out.println("宝马汽车行驶");}
}

Aodi:

public class Aodi implements Car{@Overridepublic void run() {System.out.println("奥迪汽车行驶");}
}

这样子设计可以使得我们把创建对象的任务交给工厂,我们只需要输入创建谁,由工厂给我们创建。可是这样子,我们每有一个新品牌的车,就要写一行代码创建这个车。这样子违背了开闭原则,所以简单工厂模式不属于23种设计模式。

4.2.2 工厂方法模式

由于简单工厂中,一个工厂,可以造同一类型的所有具体产品,导致简单工厂比较复杂,扩展一个新类型时,需要修改工厂代码。

工厂方法模式,为工厂也进行抽象,并且为同类型每个具体产品都创建了一个具体的工厂。

每一个工厂负责创建一个具体的产品(类型)对象

这样扩展一个新的类型,与之对应一个工厂,就不需要修改工厂了,遵守了开闭原则,单一职责原则

CarFactory:

public interface CarFactory {Car createCar();
}

Car:

public interface Car {void run();
}

AodiFactory:

public class AodiFactory implements  CarFactory{@Overridepublic Car createCar() {return new Aodi();}   
}

BmwFactory:

public class BmwFactory implements  CarFactory{@Overridepublic Car createCar() {return new Bmw();}   
}

Aodi:

public class Aodi implements Car {@Overridepublic void run() {System.out.println("奥迪汽车行驶");}
}

test:

CarFactory aodicarFactory = new AodiFactory();
Car aodi =  aodicarFactory.createCar();
aodi.run();CarFactory bmwcarFactory = new BmwFactory();
Car bmw = bmwcarFactory.createCar();
bmw.run();

现在我们用什么工厂造什么汽车。

4.2.3 抽象工厂模式

工厂方法模式,是按照产品类型进行分类的,一类产品对应一类工厂,不同类型产品之间,相互隔离的。

例如华为和小米,既要造汽车又要造手机,都是属于同一家的产品。但是工厂方法这种设计,同一个公司产品与产品之间没有联系。抽象工长对工厂重新进行分类,以公司为单位进行工厂的抽象(提取),一个工厂内,可以创建不同的产品。这样我们就可以创建出像华为工厂,小米工厂这样的具体工厂。

在这里插入图片描述

AbstractFactory:

public interface AbstractFactory {Car getCar();Phone getPhone();
}

Car:

public interface Car {void run();
}

Phone:

public interface Phone {void  call();
}

AodiFactory:

public class AodiFactory implements  AbstractFactory{@Overridepublic Car getCar() {return new AodiCar();}@Overridepublic Phone getPhone() {return new AodiPhone();}
}

AodiCar:

public class AodiCar implements Car{@Overridepublic void run() {System.out.println("奥迪汽车行驶");}
}

AodiPhone:

public class AodiPhone implements Phone{@Overridepublic void call() {System.out.println("奥迪手机打电话");}
}

BmwFactory

public class BmwFactory implements AbstractFactory{@Overridepublic Car getCar() {return new BmwCar();}@Overridepublic Phone getPhone() {return new BmwPhone();}
}

BmwCar:

public class BmwCar implements Car{@Overridepublic void run() {System.out.println("宝马汽车行驶");}
}

BmwPhone:

public class BmwPhone implements Phone {@Overridepublic void call() {System.out.println("宝马手机打电话");}
}

我们创建一个抽象工厂,让新车或者新手机牌子拥有一个新的自己的工厂来继承这个抽象工厂。每次需要某个对象,就调用对应工厂的方法。这样子我们就可以从扩展代码变成扩展一个新类。

4.3 原型模式

在某些场景下,为避免自己手动new对象,我们可以使用对象克隆方式,创建并返回一个新的对象,这种克隆新对象的效率比我们自己new的效率要高。

对象克隆实现方式:

1.实现Cloneable接口,重写Clone

2.使用对象序列化 反序列化重新生成对象

注意深克隆浅克隆问题

4.4 代理模式

在不修改原来代码的前提下,为我们方法添加额外的功能。通过代理对象帮助我们进行调用。

有些时候,目标对象(比如汽车厂)不想或不能直接与客户打交道,通过代理对象进行访问,代理对象可以保护目标对象,对目标对象功能进行扩展,降低了模块之间的耦合度。

涉及到三个主题:

  1. 抽象主题:抽取的功能,让目标对象进行实现,以及代理对象进行实现
  2. 具体主题:真正要实现功能的类
  3. 代理对象

代理模式实现方式又有两种:

4.4.1 静态代理:

创建一个代理类,代理实现与具体对象相同的接口/抽象类,重写抽象方法。

还有一个成员变量,可以用于接受具体的主题

在代理对象中重写的抽象方法中,调用真实主题方法,这样就可以在调用之前和之后添加额外的功能。

CarFactoryImpl:

/*汽车厂*/
public class CarFactoryImpl implements Sell {@Overridepublic void sell() {System.out.println("汽车厂卖汽车");}}

Sell:

/*抽象操作定义 卖东西*/
public interface Sell {void sell();}

StaticProxy:

/*静态代理,实际中很少使用静态代理,因为其代理类实现的接口必须与目标类实现接口一致,扩展起来就比较麻烦*/
public class StaticProxy  implements Sell {Sell sell;public StaticProxy(Sell sell) {this.sell = sell;}@Overridepublic void sell() {System.out.println("汽车介绍");sell.sell();System.out.println("办理手续");}}

Test:

public class Test {public static void main(String[] args) {Sell carFactory = new CarFactoryImpl();//创建汽车厂代理对象StaticProxy staticProxy = new StaticProxy(carFactory);staticProxy.sell();}
}

不好的地方:一个代理对象,只能代理一个接口类型的对象,不灵活

4.4.2 动态代理:
  1. jdk代理:

    jdk代理实现是通过反射机制实现的,目标类必须要实现一个接口,通过接口动态获得目标类中的信息

CarFactoryImpl:

/*汽车厂*/
public class CarFactoryImpl implements Sell {@Overridepublic void sell() {System.out.println("汽车厂卖汽车");}}

DynamicProxy:

/*动态代理类代理类不需要实现与目标类相同的接口,这样就可以代理任意的目标类但是是有要求的,目标类必需实现接口,此种方式是动态代理的实现方式之一: jdk代理 是一种纯反射机制实现(动态获取目标类接口方法)*/
public class DynamicProxy implements InvocationHandler {Object object;//真实对象,接收任何的目标类对象public DynamicProxy(Object object) {this.object = object;}/*在代理类中调用目标类中的具体方法,动态的将代理动态对象,目标类中要调用的方法,及方法中的参数传递过来Method method  就是动态获取的真正要执行的方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("之前开启事务");method.invoke(object);System.out.println("之后提交事务");return proxy;}public Object getProxy(){return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);}}

Sell:

/*抽象操作定义 卖东西*/
public interface Sell {void sell();
}

Test:

public class Test {public static void main(String[] args) {CarFactoryImpl vip = new CarFactoryImpl();DynamicProxy dtproxy =  new DynamicProxy(vip);//自己创建的代理类对象//这才是真正的创建动态代理对象   获取目标类所实现的接口Sell carfactory =    (Sell)dtproxy.getProxy();carfactory.sell();//使用代理对象调用接口中的方法,获取当前调用的方法,最终调用invoke方法}
}

总结:动态代理对象需要继承一个接口叫InvocationHandler,然后重写invoke方法。先创建一个具体实现CarFactoryImpl让vip进入到动态代理对象dtproxy中。vip在动态代理对象中可以利用反射机制调用各种不同的方法。但是这个方法名我们还需要用getProxy找到vip实现的接口,根据这个接口找到要完成的方法名,然后才可以使得carfactory.sell()进入到invoke中传进来的method是sell。

2.cglib代理:

是spring中提供的一种代理技术,目标类可以不实现任何接口。采用字节码生成子类的方式,对方法进行拦截,实现机制不同。

CarFactoryImpl:

//具体主题
public  class CarFactoryImpl {public  void sell() {System.out.println("汽车厂卖汽车");}}

CGLibProxy:

/** 动态代理类*/
public class CGLibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();public Object getProxy(Class<?> clazz){  enhancer.setSuperclass(clazz);  enhancer.setCallback(this);  return enhancer.create();  }  /** 拦截所有目标类方法的调用 * 参数: * obj  目标实例对象 * method 目标方法的反射对象 * args 方法的参数 * proxy 代理类的实例 */public Object intercept(Object obj, Method method, Object[] args,  MethodProxy proxy) throws Throwable {//代理类调用父类的方法  System.out.println("开始事务");  Object obj1 = proxy.invokeSuper(obj, args);  System.out.println("关闭事务");  return obj1;  }
}

Test:

public class Test {public static void main(String[] args) {CGLibProxy proxy = new CGLibProxy();CarFactoryImpl carFactory = (CarFactoryImpl) proxy.getProxy(CarFactoryImpl.class);carFactory.sell();}
}

4.5 模板方法模式

JdbcTemplate 执行sql时,步骤也是固定:1.链接数据库。2.发送sql。 3.提交事务,关闭链接

模板方法模式,使一个在类中,定义好一个算法骨架,设定好实现步骤,把一些公共的通用的方法在父类中实现,然后一些不确定的实现在具体的子类中实现

结构:

​ 抽象类:

​ 模板方法:定义好执行顺序的算法骨架,确定好执行流程顺序

​ 抽象方法:不确定的功能,定义为抽象的,交给子类实现

​ 具体方法:都一样的公共的通用的方法,在抽象父类中直接实现

​ 具体子类:实现抽象类中的抽象方法的具体类,有不同的实现方式,就可以用多个子类

​ new具体子类对象,用具体子类对象调用模板方法,把父类中具体方法与自己实现的抽象方法一起执行

适合流程相对固定,其中有变化的场景

AbstractBank:

public abstract class AbstractBank {//办理业务方法 -- 模板方法public void handle(){this.offerNumber();this.lineup();this.business();this.score();}//抽号public void offerNumber(){System.out.println("抽号");}//排队public void lineup(){System.out.println("排队");}//办理具体业务--抽象方法,由具体子类实现public abstract void business();//评分public void score(){System.out.println("评分");}
}

StoreBusiness:

/*存钱业务*/
public class StoreBusiness extends AbstractBank{//办理的具体业务public void business() {System.out.println("我要存钱");}
}

TransferBusiness:

/*转账业务类*/
public class TransferBusiness  extends AbstractBank{//转账public void business() {System.out.println("我要转账");}}

test:

public class Test {public static void main(String[] args) {StoreBusiness storeBusiness = new StoreBusiness();storeBusiness.handle();System.out.println("===================================");TransferBusiness transferBusiness = new TransferBusiness();transferBusiness.handle();}
}

由于银行办理业务这个模块需要细分,具体办理转账,存钱还是什么业务,所以在抽象类中不实现business()而是通过子类实现。存钱子类实现存钱功能,转账子类实现转账功能。handle是骨架,声明了方法实现的顺序

4.6 策略模式

将不同的实现算进行封装,将功能的实现与使用相分离

在使用时,可以用不同的策略实现类进行替换,需要用到继承多态

SalesMan:

//环境角色
public class SalesMan {//持有抽象策略角色的引用private Strategy strategy;public SalesMan(Strategy strategy) {this.strategy = strategy;}//向客户展示促销活动public void salesManShow(){strategy.show();}
}

Strategy:

public interface Strategy {void show();}

SA:

/*为春节准备的促销活动A*/
public class StrategyA implements Strategy {public void show() {System.out.println("春节活动: 买一送一");}}

SB:

/*为中秋准备的促销活动B*/
public class StrategyB implements Strategy {public void show() {System.out.println("中秋活动: 满200元减50元");}}

SC:

/*为国庆准备的促销活动C*/
public class StrategyC implements Strategy {public void show() {System.out.println("国庆活动:满1000元加一元换购任意200元以下商品");}}

Test:

public class Test {public static void main(String[] args) {SalesMan salesManA = new SalesMan(new StrategyA());salesManA.salesManShow();SalesMan salesManB = new SalesMan(new StrategyB());salesManB.salesManShow();SalesMan salesManC = new SalesMan(new StrategyC());salesManC.salesManShow();}
}

通过SalesMan使用具体的实现方法,用SA的show方法就把SA传进SalesMan中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/36264.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

使用 Gin 框架构建 RESTful 博客 API

使用 Gin 框架构建 RESTful 博客 API 引言 在现代 Web 开发中&#xff0c;RESTful API 是一种非常流行的设计风格&#xff0c;它通过 HTTP 协议与客户端进行通信&#xff0c;提供了灵活且易于扩展的接口。Go 语言以其高效的并发处理能力和简洁的语法&#xff0c;成为了构建高…

Leecode刷题C语言之骑士在棋盘上的概率

执行结果:通过 执行用时和内存消耗如下&#xff1a; 代码如下&#xff1a; static int dirs[8][2] {{-2, -1}, {-2, 1}, {2, -1}, {2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}};double knightProbability(int n, int k, int row, int column){double dp[200][30][30];mem…

21. C++STL 7(8000字详解list及其迭代器的模拟实现)

⭐本篇重点&#xff1a;STL中的list及其迭代器的模拟实现和测试 ⭐本篇代码&#xff1a;c学习 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) 目录 一. list的节点 二. list的迭代器 2.1 迭代器框架 2.2 迭代器实现 三. list的实现 3.1 list的构造函数 3.…

Docker打包SpringBoot项目

一、项目打成jar包 在进行docker打包之前&#xff0c;先确定一下&#xff0c;项目能够正常的打成JAR包&#xff0c;并且启动之后能够正常的访问。这一步看似是可有可无&#xff0c;但是能避免后期的一些无厘头问题。 二、Dockerfile 项目打包成功之后&#xff0c;需要编写Doc…

零基础学鸿蒙开发--第九篇--网络请求

12. ⽹络请求 鸿蒙系统提供了 http 模块 ⽤于发送 http 请求&#xff0c;另外&#xff0c; OpenHarmony社区基于该模块将前端开发中常⽤的⽹络请 求库 axios 移植到了鸿蒙系统&#xff0c;因此我们也可以在鸿蒙系统中使⽤ axios 发送 http 请求&#xff0c;下⾯重点为⼤家介绍…

133.WEB渗透测试-信息收集-小程序、app(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;132.WEB渗透测试-信息收集-小程序、app&#xff08;3&#xff09; 输入命令&#xff1a;…

Pointnet++改进71:添加LFE模块|高效长距离注意力网络

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入LFE模块,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤三 1.理…

Android仿美团左右联动购物列表

Android仿美团左右联动购物列表 左右联动购物列表&#xff0c;不难。 一、思路&#xff1a; 两个RecycleView 二、效果图&#xff1a; 三、关键代码&#xff1a; public class MainActivity extends AppCompatActivity {private RecyclerView rl_left;private RecyclerVie…

Mitel MiCollab 企业协作平台 任意文件读取漏洞复现(CVE-2024-41713)

0x01 产品简介 Mitel MiCollab是加拿大Mitel(敏迪)公司推出的一款企业级协作平台,旨在为企业提供统一、高效、安全的通信与协作解决方案。通过该平台,员工可以在任何时间、任何地点,使用任何设备,实现即时通信、语音通话、视频会议、文件共享等功能,从而提升工作效率和…

深度学习camp-第J3-1周:DenseNet算法 实现乳腺癌识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 我的环境 语言环境&#xff1a;Python 3.12编译器&#xff1a;Jupyter Lab深度学习环境&#xff1a;Pytorch 2.4.1 Torchvision 0.19.1数据集&#xff1a;乳腺…

Elasticsearch 单节点安全配置与用户认证

Elasticsearch 单节点安全配置与用户认证 安全扫描时发现了一个高危漏洞&#xff1a;Elasticsearch 未授权访问 。在使用 Elasticsearch 构建搜索引擎或处理大规模数据时&#xff0c;需要启用基本的安全功能来防止未经授权的访问。本文将通过简单的配置步骤&#xff0c;为单节…

Vulhub:Shiro[漏洞复现]

目录 CVE-2010-3863(Shiro未授权) 使用浏览器访问靶场主页面 使用Yakit进行抓包 使用ffuf对靶机8080端口进行根路径FUZZ CVE-2016-4437(Shiro-550) 使用浏览器访问靶场主页面 使用Yakit进行抓包 使用Yakit反连中自带的Yso-Java Hack进行漏洞利用 首先运行脚本生成一个…

数学拯救世界(一)———寻“数”记

一、 很久很久以前&#xff0c;在一个只认识整数和小数的国度&#xff0c;有一个很残暴的国王提了一个要求&#xff1a;要是不能表示出把一段1米的绳子三等分后的大小&#xff0c;就要把所有的大臣杀掉。 1➗3 0.333&#xff0c;怎么办呀&#xff1f;怎么办呀&#xff1f; 袁q…

Codeforces Round 991 (Div. 3)题解

先随随便便写一点东西吧&#xff0c;毕竟只是一场div3 A. Line Breaks 思路&#xff1a;一道很简单的模拟题吧&#xff0c;就是遍历一遍&#xff0c;当大于x的时候就break&#xff0c;然后前面那个就是找到的前x个字的总长度不超过m #include<bits/stdc.h> using names…

掌握谈判技巧,达成双赢协议

在当今竞争激烈且合作频繁的社会环境中&#xff0c;谈判成为了我们解决分歧、谋求共同发展的重要手段。无论是商业合作、职场交流&#xff0c;还是国际事务协商&#xff0c;掌握谈判技巧以达成双赢协议都具有极其关键的意义。它不仅能够让各方在利益分配上找到平衡点&#xff0…

基于Matlab特征提取与浅层神经网络的数字图像处理乳腺癌检测系统(GUI界面+训练代码+数据集)

本研究提出了一种结合数字图像处理技术、特征提取与浅层神经网络的创新癌症检测系统&#xff0c;旨在为医学图像的分析和早期癌症检测提供有效支持。系统主要处理癌症与正常组织的医学图像&#xff0c;通过灰度共生矩阵&#xff08;GLCM&#xff09;等方法&#xff0c;从图像中…

Backblaze 2024 Q3硬盘故障质量报告解读

作为一家在2021年在美国纳斯达克上市的云端备份公司&#xff0c;Backblaze一直保持着对外定期发布HDD和SSD的故障率稳定性质量报告&#xff0c;给大家提供了一份真实应用场景下的稳定性分析参考数据&#xff1a; 以往报告解读系列参考&#xff1a; Backblaze发布2024 Q2硬盘故障…

河工oj第七周补题题解2024

A.GO LecturesⅠ—— Victory GO LecturesⅠ—— Victory - 问题 - 软件学院OJ 代码 统计 #include<bits/stdc.h> using namespace std;double b, w;int main() {for(int i 1; i < 19; i ) {for(int j 1; j < 19; j ) {char ch; cin >> ch;if(ch B) b …

[ABC234A] Weird Function

解题思路 这是一道模拟题…… 设置一个函数 &#xff0c;返回值为 。 最后答案就是 。 代码 记得开 long long ! #include<bits/stdc.h> using namespace std;long long t; long long f(long long x) {return x*xx*23; }int main() {cin>>t;cout<<f(f(f…

蓝牙键鼠无法被电脑识别

起因是我的键鼠是三模的&#xff0c;但是我蓝牙模式我只用过几次&#xff0c;基本一直使用的是有线模式&#xff0c;最近突然要用无线连接&#xff0c;如果使用收发器就显得过于繁琐&#xff0c;还占用usb口&#xff0c;因此想用蓝牙连&#xff0c;但是由于 win10更新了英特尔…