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.mzph.cn/web/62264.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

部署loki,grafana 以及springcloud用法举例

文章目录 场景docker 部署grafanadocker-compose部署loki维护配置文件 local-config.yaml维护docker-compose.yml配置启动 grafana 添加loki数据源springcloud用法举例查看loki的explore,查看日志 场景 小公司缺少运维岗位&#xff0c;需要研发自己部署日志系统&#xff0c;elk…

keil报错---connection refused due to device mismatch

解决办法如下&#xff1a; 记得改成1 把Enable取消

第三节、电机定速转动【51单片机-TB6600驱动器-步进电机教程】

摘要&#xff1a;本节介绍用定时器定时的方式&#xff0c;精准控制脉冲时间&#xff0c;从而控制步进电机速度 一、计算过程 1.1 电机每一步的角速度等于走这一步所花费的时间&#xff0c;走一步角度等于步距角&#xff0c;走一步的时间等于一个脉冲的时间 w s t e p t … ……

vue中pdf.js的使用,包括pdf显示,跳转指定页面,高亮关键词

目录 一、下载pdf.js 二、引入到本地的项目中 三、实现预览pdf 四、跳转到指定页面 五、利用pdf里面的find查找关键词 六、修改页面大小为实际大小 一、下载pdf.js https://github.com/mozilla/pdf.js 里面有很多的版本&#xff0c; 高版本的可能浏览器不兼容或者还要考…

Qt 小项目 学生管理信息系统

主要是对数据库的增删查改的操作 登录/注册界面&#xff1a; 主页面&#xff1a; 添加信息&#xff1a; 删除信息&#xff1a; 删除第一行&#xff08;支持多行删除&#xff09; 需求分析&#xff1a; 用QT实现一个学生管理信息系统&#xff0c;数据库为MySQL 要求&#xf…

“量子跃迁与数据织网:深入探索K最近邻算法在高维空间中的优化路径、神经网络融合技术及未来机器学习生态系统的构建“

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

硬件选型规则

光源选型: 先用型号中带H的&#xff0c;没有的选标准的. 光源和光源控制器的搭配需要确保接口一致。 根据型号表中的最佳工作距离和相机的尺寸。 光源控制器选型&#xff1a; 首先选择海康风格系列光源控制器考虑与光源的接口匹配。功率应该满足接近光源功率。检查是否退市…

【计算机网络】 —— 数据链路层(壹)

文章目录 前言 一、概述 1. 基本概念 2. 数据链路层的三个主要问题 二、封装成帧 1. 概念 2. 帧头、帧尾的作用 3. 透明传输 4. 提高效率 三、差错检测 1. 概念 2. 奇偶校验 3. 循环冗余校验CRC 1. 步骤 2. 生成多项式 3. 例题 4. 总结 四、可靠传输 1. 基本…

golang实现简单的redis服务

golang 手搓redis服务器仓库地址:实现思路: golang 手搓redis服务器 仓库地址: 仓库: https://github.com/dengjiayue/my-redis.git 实现思路: ● 协议: tcp通信 ● 数据包: 长度(4byte)方法(1byte)数据json ● 数据处理: 单线程map读写 ○ 依次处理待处理队列的请求(chan)…

智慧银行反欺诈大数据管控平台方案(八)

智慧银行反欺诈大数据管控平台的核心理念&#xff0c;在于通过整合先进的大数据技术、算法模型和人工智能技术&#xff0c;构建一个全面、智能、动态的反欺诈管理框架&#xff0c;以实现对金融交易的全方位监控、欺诈行为的精准识别和高效处理。这一理念强调数据驱动决策&#…

3D 生成重建019-LERF用文本在Nerf中开启上帝之眼

3D 生成重建019-LERF用文本在Nerf中开启上帝之眼 文章目录 0 论文工作1 论文方法2 实验结果 0 论文工作 人类利用自然语言描述物理世界&#xff0c;根据各种特性&#xff08;视觉外观、语义、抽象关联&#xff09;寻找具体的3D位置。在这项工作中&#xff0c;作者提出了语言嵌…

如何选择合适的期刊投稿?从课题组经验到在线工具的使用全解析

~~~本文是作者个人的经验分享&#xff0c;建立在导师让自己选刊的情况下~~~ 投稿选刊是科研过程中至关重要的一步&#xff0c;选刊过程可能让许多初投稿的研究者感到迷茫和困惑&#xff1a;期刊那么多&#xff0c;如何找到最合适的&#xff1f; 本文将从多个角度介绍如何选择投…

.NET MAUI与.NET for Android/IOS的关系

2024年11月13日微软发布了.Net9.0,我打算体验一下。安装好.Net9.0 SDK后发现Visual Studio识别不到9.0&#xff0c;但是通过命令行dotnet --info查看是正常的&#xff0c;后面看到了VS有版本可以升级&#xff0c;把VS升级到17.12.0就可以了。更新完打开以后看到如下界面 这里…

【vivado】时序报告--best时序和worst时序

利用vivado进行开发时&#xff0c;生成best时序报告和worst时序报告。 best时序报告 slow选择min_max&#xff0c;fast选择none。 worst时序报告 fast选择min_max&#xff0c;slow选择none。

FastAPI 响应状态码:管理和自定义 HTTP Status Code

FastAPI 响应状态码&#xff1a;管理和自定义 HTTP Status Code 本文介绍了如何在 FastAPI 中声明、使用和修改 HTTP 状态码&#xff0c;涵盖了常见的 HTTP 状态码分类&#xff0c;如信息响应&#xff08;1xx&#xff09;、成功状态&#xff08;2xx&#xff09;、客户端错误&a…

第P1周:Pytorch实现mnist手写数字识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 1. 实现pytorch环境配置 2. 实现mnist手写数字识别 3. 自己写几个数字识别试试具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python…

网络安全技术详解:虚拟专用网络(VPN) 安全信息与事件管理(SIEM)

虚拟专用网络&#xff08;VPN&#xff09;详细介绍 虚拟专用网络&#xff08;VPN&#xff09;通过在公共网络上创建加密连接来保护数据传输的安全性和隐私性。 工作原理 VPN的工作原理涉及建立安全隧道和数据加密&#xff1a; 隧道协议&#xff1a;使用协议如PPTP、L2TP/IP…

河南省的教育部科技查新工作站有哪些?

郑州大学图书馆&#xff08;Z12&#xff09;&#xff1a;2007年1月被批准设立“教育部综合类科技查新工作站”&#xff0c;同年12月被河南省科技厅认定为河南省省级科技查新机构。主要面向河南省的高校、科研机构、企业提供科技查新、查收查引等服务。 河南大学图书馆&#xf…

Leetcode经典题6--买卖股票的最佳时机

买卖股票的最佳时机 题目描述&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。…

【Pytorch】torch.reshape与torch.Tensor.reshape区别

问题引入&#xff1a; 在Pytorch文档中&#xff0c;有torch.reshape与torch.Tensor.reshape两个reshape操作&#xff0c;他们的区别是什么呢&#xff1f; 我们先来看一下官方文档的定义&#xff1a; torch.reshape&#xff1a; torch.Tensor.reshape: 解释&#xff1a; 在p…