java后端分享整理

2023-11-03

java规范总结


感谢分享:

1. Java 常见的代码规范

1.1. Java 自带的工具方法

1.1.1 比较两个对象是否相等

当我们用equals比较两个对象是否相等的时候,还需要对左边的对象进行判空,不然可能会报空指针异常,我们可以用java.util包下Objects封装好的比较是否相等的方法:

 Objects.equals(strA, strB); 

源码:

public static boolean equals(Object a, Object b) {  
    return (a == b) || (a != null && a.equals(b));  
}  

1.1.2 apache commons工具类库

apache commons是最强大的,也是使用最广泛的工具类库,里面的子库非常多,下面介绍几个最常用的

commons-lang,java.lang的增强版 建议使用commons-lang3,优化了一些api,原来的commons-lang已停止更新:

maven 的依赖:

org.apache.commons commons-lang3 3.12.0
1.1.2.1 字符串判空

传参CharSequence类型是String、StringBuilder、StringBuffer的父类,都可以直接下面方法判空,以下是源码:

public static boolean isEmpty(final CharSequence cs) {  
    return cs == null || cs.length() == 0;  
}  
  
public static boolean isNotEmpty(final CharSequence cs) {  
    return !isEmpty(cs);  
}  
  
// 判空的时候,会去除字符串中的空白字符,比如空格、换行、制表符  
public static boolean isBlank(final CharSequence cs) {  
    final int strLen = length(cs);  
    if (strLen == 0) {  
        return true;  
    }  
    for (int i = 0; i < strLen; i++) {  
        if (!Character.isWhitespace(cs.charAt(i))) {  
            return false;  
        }  
    }  
    return true;  
}  
  
public static boolean isNotBlank(final CharSequence cs) {  
    return !isBlank(cs);  
}  

####1.1.2.2 首字母转大写 (这里可以查询源码看其他stringUtils 提供的其他源码,提供的新的特性)

String str = "yideng";  
String capitalize = StringUtils.capitalize(str);  
System.out.println(capitalize); // 输出Yideng  
1.1.2.3 重复拼接字符串
String str = StringUtils.repeat("ab", 2);  
System.out.println(str); // 输出abab  
1.1.2.4 格式化日期

再也不用手写SimpleDateFormat格式化了

// Date类型转String类型  
String date = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");  
System.out.println(date); // 输出 2021-05-01 01:01:01  
  
// String类型转Date类型  
Date date = DateUtils.parseDate("2021-05-01 01:01:01", "yyyy-MM-dd HH:mm:ss");  
  
// 计算一个小时后的日期  
Date date = DateUtils.addHours(new Date(), 1);  
1.1.2.4 包装临时对象 (不是特别常用)

当一个方法需要返回两个及以上字段时,我们一般会封装成一个临时对象返回,现在有了Pair和Triple就不需要了

// 返回两个字段  
ImmutablePair<Integer, String> pair = ImmutablePair.of(1, "yideng");  
System.out.println(pair.getLeft() + "," + pair.getRight()); // 输出 1,yideng  
// 返回三个字段  
ImmutableTriple<Integer, String, Date> triple = ImmutableTriple.of(1, "yideng", new Date());  
System.out.println(triple.getLeft() + "," + triple.getMiddle() + "," + triple.getRight()); // 输出 1,yideng,Wed Apr 07 23:30:00 CST 2021  

1.1.3 commons-collections 集合工具类

封装了集合判空的方法,以下是源码:

public static boolean isEmpty(final Collection<?> coll) {  
    return coll == null || coll.isEmpty();  
}  
  
public static boolean isNotEmpty(final Collection<?> coll) {  
    return !isEmpty(coll);  
}  
// 两个集合取交集  
Collection<String> collection = CollectionUtils.retainAll(listA, listB);  
// 两个集合取并集  
Collection<String> collection = CollectionUtils.union(listA, listB);  
// 两个集合取差集  
Collection<String> collection = CollectionUtils.subtract(listA, listB);  

1.1.3 common-beanutils 操作对象

Maven依赖:

<dependency>  
    <groupId>commons-beanutils</groupId>  
    <artifactId>commons-beanutils</artifactId>  
    <version>1.9.4</version>  
</dependency>  
public class User {  
    private Integer id;  
    private String name;  
} 

设置对象属性

User user = new User();  
BeanUtils.setProperty(user, "id", 1);  
BeanUtils.setProperty(user, "name", "yideng");  
System.out.println(BeanUtils.getProperty(user, "name")); // 输出 yideng  
System.out.println(user); // 输出 {"id":1,"name":"yideng"}  

对象和map互转

// 对象转map  
Map<String, String> map = BeanUtils.describe(user);  
System.out.println(map); // 输出 {"id":"1","name":"yideng"}  
// map转对象  
User newUser = new User();  
BeanUtils.populate(newUser, map);  
System.out.println(newUser); // 输出 {"id":1,"name":"yideng"}  

1.1.3.1 commons-io 文件流处理

Maven依赖:

commons-io commons-io 2.8.0

文件处理

File file = new File("demo1.txt");  
// 读取文件  
List<String> lines = FileUtils.readLines(file, Charset.defaultCharset());  
// 写入文件  
FileUtils.writeLines(new File("demo2.txt"), lines);  
// 复制文件  
FileUtils.copyFile(srcFile, destFile);

1.1.4 Google Guava 工具类库

maven 依赖:

com.google.guava guava 30.1.1-jre
1.1.4.1 创建集合
List<String> list = Lists.newArrayList();  
List<Integer> list = Lists.newArrayList(1, 2, 3);  
// 反转list  
List<Integer> reverse = Lists.reverse(list);  
System.out.println(reverse); // 输出 [3, 2, 1]  
// list集合元素太多,可以分成若干个集合,每个集合10个元素  
List<List<Integer>> partition = Lists.partition(list, 10);  
  
Map<String, String> map = Maps.newHashMap();  
Set<String> set = Sets.newHashSet();  

1.2 java 常见的代码规范

1.2.1 使用Collection.isEmpty() 检测空

​ 使用Collection.size() 来检测是否为空在逻辑上没有问题,但是使用Collection.isEmpty() 使得代码更易读,并且可以获得更好的性能;除此之外,任何Collection.isEmpty() 实现的时间复杂度都是O(1) ,不需要多次循环遍历,但是某些通过Collection.size() 方法实现的时间复杂度可能是O(n)。O(1)纬度减少循环次数 例子

反例:

LinkedList<Object> collection = new LinkedList<>();
if (collection.size() == 0){
  System.out.println("collection is empty.");
 }

正例

LinkedList<Object> collection = new LinkedList<>();
if (collection.isEmpty()){
      System.out.println("collection is empty.");
  }

//检测是否为null 可以使用CollectionUtils.isEmpty()
if (CollectionUtils.isEmpty(collection)){
      System.out.println("collection is null.");

 

1.2.2 初始化集合时尽量指定其大小

尽量在初始化时指定集合的大小,能有效减少集合的扩容次数,因为集合每次扩容的时间复杂度很可能时O(n),耗费时间和性能。

反例

//初始化list,往list 中添加元素反例:
int[] arr = new int[]{1,2,3,4};
List<Integer> list = new ArrayList<>();
for (int i : arr){
list.add(i);
}

正例:

//初始化list,往list 中添加元素正例:
int[] arr = new int[]{1,2,3,4};
//指定集合list 的容量大小
  List<Integer> list = new ArrayList<>(arr.length);
for (int i : arr){
list.add(i);
  }

1.2.3 若需频繁调用Collection.contains 方法则使用Set

在Java 集合类库中,List的contains 方法普遍时间复杂度为O(n),若代码中需要频繁调用contains 方法查找数据则先将集合list 转换成HashSet 实现,将O(n) 的时间复杂度将为O(1)。

反例:

//频繁调用Collection.contains() 反例
List<Object> list = new ArrayList<>();
for (int i = 0; i <= Integer.MAX_VALUE; i++){
//时间复杂度为O(n)
if (list.contains(i))
  System.out.println("list contains "+ i);
 }

正例:

//频繁调用Collection.contains() 正例
  List<Object> list = new ArrayList<>();
  Set<Object> set = new HashSet<>();
for (int i = 0; i <= Integer.MAX_VALUE; i++){
//时间复杂度为O(1)
if (set.contains(i)){
          System.out.println("list contains "+ i);
      }
  }

1.2.4 使用静态代码块实现赋值静态成员变量

对于集合类型的静态成员变量,应该使用静态代码块赋值,而不是使用集合实现来赋值。

反例:

//赋值静态成员变量反例
private static Map<String, Integer> map = new HashMap<String, Integer>(){
        {
map.put("Leo",1);
map.put("Family-loving",2);
map.put("Cold on the out side passionate on the inside",3);
        }
    };
private static List<String> list = new ArrayList<>(){
        {
list.add("Sagittarius");
list.add("Charming");
list.add("Perfectionist");
        }
    };

正例:

//赋值静态成员变量正例
private static Map<String, Integer> map = new HashMap<String, Integer>();
static {
map.put("Leo",1);
map.put("Family-loving",2);
map.put("Cold on the out side passionate on the inside",3);
    }

private static List<String> list = new ArrayList<>();
static {
list.add("Sagittarius");
list.add("Charming");
list.add("Perfectionist");
    }

1.2.5 工具类中屏蔽构造函数

工具类是一堆静态字段和函数的集合,其不应该被实例化;但是,Java 为每个没有明确定义构造函数的类添加了一个隐式公有构造函数,为了避免不必要的实例化,应该显式定义私有构造函数来屏蔽这个隐式公有构造函数。

反例:

public class PasswordUtils {
//工具类构造函数反例
private static final Logger LOG = LoggerFactory.getLogger(PasswordUtils.class);

public static final String DEFAULT_CRYPT_ALGO = "PBEWithMD5AndDES";

public static String encryptPassword(String aPassword) throws IOException {
return new PasswordUtils(aPassword).encrypt();
  }

正例:

public class PasswordUtils {
//工具类构造函数正例
private static final Logger LOG = LoggerFactory.getLogger(PasswordUtils.class);

//定义私有构造函数来屏蔽这个隐式公有构造函数
private PasswordUtils(){}

public static final String DEFAULT_CRYPT_ALGO = "PBEWithMD5AndDES";

public static String encryptPassword(String aPassword) throws IOException {
return new PasswordUtils(aPassword).encrypt();
  }

1.2.6 删除未使用的局部变量、方法参数、私有方法、字段和多余的括号

### 1.2.7  **删除多余的异常捕获并抛出 ** (需补充)

用catch 语句捕获异常后,若什么也不进行处理,就只是让异常重新抛出,这跟不捕获异常的效果一样,可以删除这块代码或添加别的处理。

//多余异常反例
private static String fileReader(String fileName)throws IOException{

try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
        StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
return builder.toString();
    } catch (Exception e) {
//仅仅是重复抛异常 未作任何处理
throw e;
    }
}

正例:

//多余异常正例
private static String fileReader(String fileName)throws IOException{

try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
        StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
return builder.toString();
//删除多余的抛异常,或增加其他处理:
/*catch (Exception e) {
            return "fileReader exception";
        }*/
    }
}

1.2.8 字符串转化使用String.valueOf(value) 代替 " " + value (需补充)

把其它对象或类型转化为字符串时,使用String.valueOf(value) 比 “”+value 的效率更高。

反例:

//把其它对象或类型转化为字符串反例:
int num = 520;
// "" + value
String strLove = "" + num;

正例:

//把其它对象或类型转化为字符串正例:
int num = 520;
// String.valueOf() 效率更高
String strLove = String.valueOf(num);

1.2.9 避免使用BigDecimal(double)

BigDecimal(double) 存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。

反例:

// BigDecimal 反例
BigDecimal bigDecimal = new BigDecimal(0.11D);

正例:

// BigDecimal 正例
BigDecimal bigDecimal1 = bigDecimal.valueOf(0.11D);

2. 设计模式

2.1 装饰者模式订阅

2.1.1 定义

装饰模式又称,包装模式。装饰模式是以客户端透明的方式扩展对象的功能 是一种继承的替代方案。

2.1.2职责

  • 动态给对象增加新的功能
  • 装饰模式是一种用于替代,继承的技术,无需通过继承增加子类就能扩展 对象的功能。使用对象的关联关系,替代继承关系 更加灵活,同时避免了体系的膨胀。

2.1.3 装饰模式结构

  • 装饰模式类图
image-20220311003942157
  • Component 抽象构建角色 :真是对象(对应上图 ConcreteOperation() 对象)和装饰对象具有相同的接口

    。这样客户端对象就能以与 真是对象相同的方式同装饰对象交互了。

  • ConcreteComponent 具体构建的角色(真是对象) :定义一个接收附加责任的类。

  • Decorator装饰角色 持有构建角色的引用。装饰对象接收客户端的请求,并把这些请求转发给真是的对象。这样,就能在真是对象调用前后增加新的功能。

  • ConcreteDecorate 具体的装饰角色:负责给构建对象增加新的功能。

2.1.3 举一个简单的例子

举一个简单的汽车例子,创造每一种汽车都需要继承父类进行实现,如下图 那么当我们需要,既能路上行驶,又能水上行驶,又得继承父类。

image-20220311003942157

以后每增加一种新功能的汽车都需要增一个类,这样的话会创建大量的类。这时候就能使用装饰类了。

代码示例:

抽象构件

public interface AbstractCar {
    void move();
}  

具体构建

public class Car implements AbstractCar{

    public void move() {
        System.out.println("路上行驶");
    }
}

装饰角色

public class SuperCar implements AbstractCar{
    protected AbstractCar car;
    public SuperCar(AbstractCar car){
        this.car=car;
    }

    public void move() {
        car.move();
    }

}

具体装饰 角色

/**
 * 飞行汽车  
*/   

ublic class FlyCar extends SuperCar{

    public FlyCar(AbstractCar car) {
        super(car);
    }

    public void fly() {
        System.out.println("空中行驶汽车");
    }


    @Override
    public void move() {
        super.move();
         fly();
    }

}

/**
 * 水上汽车
 */
public class WaterCar extends SuperCar{

    public WaterCar(AbstractCar car) {
        super(car);
    }

    public void swim() {
        System.out.println("水上行驶汽车");
    }
    @Override
    public void move() {
        super.move();
        swim();
    }

}

客户端

public class Client {

    public static void main(String[] args) {
        Car car=new Car();
        car.move();

        System.out.println("------增加新功能,飞行------");
        FlyCar flyCar=new FlyCar(car);
        flyCar.move();

        System.out.println("------新增加功能,水上行驶-----");
        WaterCar waterCar=new WaterCar(car);
        waterCar.move();

        System.out.println("-----新增加两个功能,飞行与水上行驶-----");
        WaterCar waterCar2=new WaterCar(flyCar);
        waterCar2.move();

    }

}



//输出结果
路上行驶
------增加新功能,飞行------
路上行驶
空中行驶汽车
------新增加功能,水上行驶-----
路上行驶
水上行驶汽车
-----新增加两个功能,飞行与水上行驶-----
路上行驶
空中行驶汽车
水上行驶汽车

2.1.4 另一个很经典的一个场景

  • 咖啡类型 espresso(意大利咖啡),shortblack,LongBlack(美食咖啡),Decaf(无因咖啡)
  • 调料 Milk ,Soy(豆浆),Chocolate
  • 费用 不同的咖啡价格是不同的,而且有 咖啡+调料的类型组合 每个咖啡,都有自己的单价,咖啡+调料也有自己的单价。
  • 要求 扩展咖啡的种类的时候,具有很好的扩展性,改动方便,维护方便。
  • 总结:看到需求,首先是问题的抽象能力,将问题抽象出来,这个抽象能力非常重要。

2.1.5 需求抽象出来

  1. 看到每个咖啡,都有自己的cost(花费)和description(描述)可以知道咖啡共有的属性和行为。
  2. 看到问题后,手动画图或者用软件画出类图 ,遇到问题,首先是下手,做软件行业,就是要下手设计,大家都能想到的如下类图设计
image-20220302212850518

咖啡类型就像是构件,调料就是具体的装饰类,给构件增加新的功能;

2.1.6 具体的类图

装饰者模式,更像俄罗斯的套娃,一层一层的嵌套
看到类图后你会发现,其实就 当前类A 中 含有A属性。
类图如下:

image-20220311003942157

代码: 见idea

2.1.7 装饰模式的优点

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加。
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象。
  • 具体构 件 类和具体装饰类可以独立变化,用户可以根据需要自己增加新的 具体构件子类和具体装饰子类。

2.1.8 缺点

  • 产生很多小对象。大量小的对象占据内存,一定程度上影响性能。
  • 装饰模式易出错,调试排查比较麻烦。

2.2 桥接者模式

商城中常见的模式 ,以电脑分类,如何处理商品分类销售问题

  • 我们可以使用多层继承实现关系如下

    image-20220311010813084

代码省略: 存在的问题,

  • 扩展问题

    1. 如果要增加新的电脑类型:如平板电脑,则要增加 各个品牌下面的类。
    2. 如果新增加品牌,也要增加各个电脑下的类。
    • 违反了单一职责原则

    ​ 一个类:如联想笔记本电脑,有两个引起这个类变化的原因:品牌及电脑的类型。

    总结:某些由于自身的逻辑,它具有两个或者多个维度的变化,为了应对这种“多维度变化”,并且利用面向对象 的技术来,使得该类能轻松的沿着多个方向变化,而有不引入额外的复杂度。引入bridge模式。

2.2.1 桥接模式概念

概念:把抽象(abstraction)和行为实现分开(implement)从而保持各部分的独立性以及对功能的扩展

2.2.2 结构图

image-20220311012335063

上面场景中有两个变化的维度,电脑类型和电脑品牌,将上面的代码通过桥接模式实现:

2.2.3 具体代码

电脑品牌维度

/**
 * 电脑品牌维度接口
 */
public interface Brand {
    void nane();
}

class Lenovo implements Brand{
    @Override
    public void nane() {
        System.out.println("联想电脑");
    }
}

class Dell implements Brand{
    @Override
    public void nane() {
        System.out.println("戴尔电脑");
    }
}

电脑类型维度

/**
 * 电脑类型维度
 */
public class Computer{
    protected Brand brand;
    public Computer(Brand brand){
        this.brand=brand;
    }
    public void sale(){
        brand.nane();
    }
}
class Desktop extends Computer{

    public Desktop(Brand brand) {
        super(brand);
    }
    @Override
    public void sale() {
        super.sale();
        System.out.println("销售台式机");
    }
}

class Laptop extends Computer{

    public Laptop(Brand brand) {
        super(brand);
    }
    @Override
    public void sale() {
        super.sale();
        System.out.println("销售笔记本");
    }

}

客户端

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        Computer len=new Desktop(new Lenovo());
        len.sale();
    }
}

//输出结果
联想电脑
销售台式机

总结:这样将 电脑品牌和电脑类型两个维度,分离开并且组合使用,这样在增加品牌或类型更加简单。

2.2.4 桥接模式的设计要点和适用性

  • 设计要点

处理多层继承结构,处理多维度变化的场景,将更维度设计成独立的继承模块,使各个维度可以独立扩展在抽象层。

  • 适用性

    1. 如果一个系统需要在构件的抽象化角色和具体化角色之间,增加更多的灵活性,避免在两个层次之间建立静态的关系。
    2. 设计要求实现化角色的任何改变不应该影响客户端,或者说实现化角色的改变对客户端是完全透明的。
    3. 一个构件多余一个抽象化角色和实现化角色,系统需要它们之间互相解耦。
    4. 虽然系统中使用继承没有问题,但由于抽象化角色和具体化角色需要独立化,设计要求独立管理两者。

2.2.5 装饰模式和桥接模式的区别

  • 两个模式都是为了解决过多子类对象的问题。但他们的诱因不同,桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能

2.3 观察者模式

场景: 是一种 一对多的关系,进一步理解为 消息订阅模式

Subject 被观察者 也叫做 主题
Observer  观察者  也叫订阅者
理解:被观察者 通知观察者自己的状态变化   被观察者中注册了,多个观察者对象。

2.4 责任链模式

2.5 建造者模式

2.5.1 概念

将复杂对象的构建和它的表示,使得同样的构建过程有不同的创建方式。

2.6 组合这模式

2.6.1 组合模式的概念

概念 把部分和整体用树形结构来表示 ,从而使客户端可以使用统一的方式对,部分对象和整体对象就行管理。

2.6.2 组合模式结构

抽象构件(Conponent) 角色: 所有类共有接口,定义叶子节点和容器共同点

叶子(Leaf)构件角色:在组合中表示叶子节点对象,叶子节点五子节点。

容器(Composite)构件角色: 有容器特征,可以用来存储子节点,在Component接口中实现与子节点有关操作,如增加(add)和删除(remove)等。

image-20220312192645979

2.6.3组合模式工作分析

  • 组合模式为处理树形结构,提供了完美的解决方案,描述了如何将容器和叶子节点进行递归组合,使得用户在使用时, 可以一致性的对待容器和叶子。
  • 当容器对象的指定方法被调用时,将遍历整个树形结构,寻找包含这个方法的成员,并调用执行。其中使用递归调用机制对整个机构进行处理。

下面是通过简单秒杀的例子,使用组合

抽象构建

public interface AbstractFile {
    void killVirus();//杀毒
}

叶子构件

class ImageFile implements AbstractFile{
    private String name;

    public ImageFile(String name){
        this.name=name;
    }

    public void killVirus() {
        System.out.println("---对图像文件"+name+"杀毒");

    }

}
class TextFile implements AbstractFile{
    private String name;

    public TextFile(String name){
        this.name=name;
    }

    public void killVirus() {
        System.out.println("---对文本文件"+name+"杀毒");

    }

}

容器构建

class Folder implements AbstractFile{
    private String name;
    private ArrayList<AbstractFile> list=new ArrayList<AbstractFile>();
    public Folder(String name){
        this.name=name;
    }

    public void add(AbstractFile file){
        list.add(file);
    }
    public void remove(AbstractFile file){
        list.remove(file);
    }
    public AbstractFile getChild(int index){
        return list.get(index);
    }

    public void killVirus() {
        System.out.println("---对文件夹"+name+"杀毒");
        for(AbstractFile file:list){
            file.killVirus();
        }

    }

}


客户端

public class Client {

    public static void main(String[] args) {
        Folder f1;
        AbstractFile f2,f3;

        f1=new Folder("我的收藏");
        f2=new ImageFile("my.jpg");
        f3=new TextFile("my.txt");
        f1.add(f2);
        f1.add(f3);
        f1.killVirus();

    }

}

//输出结果:
---对文件夹我的收藏杀毒
---对图像文件my.jpg杀毒
---对文本文件my.txt杀毒  

2.7 策略模式

组合关系

2.8 外观模式

2.8.1 概念

  • 外观模式(也称门面模式)定义了一个高层接口,为子系统中的一组接口提供了,一致的界面,从而使子系统更加容易使用。

2.9 命令模式

2.9.1 概念

将一个请求封装为对象,从而使我们可用不同的请求,对客户进行参数化;对请求排队或者记录请求日志,以及支持撤销操作,以及支持撤销操作。也称为:动作(Action)模式,事务(transaction)模式。

2.9.2 模式结构

  • Conmmand 抽象命令类: 声明执行操作接口

  • ConcreteCommand 具体命令类:通常持有 一个接收者对象并绑定一个动作,调用接收者相应的动作,以实现execute方法。

  • invoker 调用者/请求者:请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联。在程序运行时,将调用命令对象的execute() 间接调用接收者的相关工作。

  • Receiver接收者:接受者执行与请求相关的操作,具体实现对请求业务的处理。

2.9.3 结构图

在这里插入图片描述

Receiver接受者

/**
 * 真正的命令执行者
 */
public class Receiver {
    public void action(){
        System.out.println("执行命令----");
    }

    public void unAction(){
        System.out.println("撤销命令----");
    }
    
  
}

command 抽象命令类

/**
 * 抽象命令接口
 */
public interface Command {
     void  execute();//执行命令
     void  cancel();//取消命令 
 }   

ConcreteCommand具体命令类

/**
 * 具体命令类
 */
public class ConcreteCommand implements Command{
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        super();
        this.receiver = receiver;
    }

    public void execute() {
        //可进行执行命令前相关操作

        receiver.action();//执行命令

        //可进行执行命令后相关操作
    }

    public void cancel() {
        receiver.unAction();
    }
}

Invoke 调用者/请求者

public class Invoker {
    private Command command;  //可以是多个命令

    public Invoker(Command command) {
        super();
        this.command = command;
    }
    //执行命令
    public void runCommand(){
        command.execute();
    }

    //取消命令
    public void cancelCommand(){
        command.cancel();
    }

}

客户端

public class Client {

    public static void main(String[] args) {
        //创建接收者
        Receiver receiver=new Receiver();
        //创建命令对象,并设置接收者
        Command command=new ConcreteCommand(receiver);

        //创建调用者,设置命令
        Invoker invoker=new Invoker(command);

        invoker.runCommand();
        invoker.cancelCommand();
    }
}


//输出结果
执行命令---- 
撤销命令----   

2.9.3建议看这个代码

应用 :模拟对电视机的操作有开机、关机、换台命令。代码如下

//执行命令的接口
public interface Command {
  void execute();
}
//命令接收者Receiver
public class Tv {
  public int currentChannel = 0;
 
  public void turnOn() {
     System.out.println("The televisino is on.");
  }
 
  public void turnOff() {
     System.out.println("The television is off.");
  }
 
  public void changeChannel(int channel) {
     this.currentChannel = channel;
     System.out.println("Now TV channel is " + channel);
  }
}
//开机命令ConcreteCommand
public class CommandOn implements Command {
  private Tv myTv;
 
  public CommandOn(Tv tv) {
     myTv = tv;
  }
 
  public void execute() {
     myTv.turnOn();
  }
}
//关机命令ConcreteCommand
public class CommandOff implements Command {
  private Tv myTv;
 
  public CommandOff(Tv tv) {
     myTv = tv;
  }
 
  public void execute() {
     myTv.turnOff();
  }
}
//频道切换命令ConcreteCommand
public class CommandChange implements Command {
  private Tv myTv;
 
  private int channel;
 
  public CommandChange(Tv tv, int channel) {
     myTv = tv;
     this.channel = channel;
  }
 
  public void execute() {
     myTv.changeChannel(channel);
  }
}
//可以看作是遥控器Invoker
public class Control {
  private Command onCommand, offCommand, changeChannel;
 
  public Control(Command on, Command off, Command channel) {
     onCommand = on;
     offCommand = off;
     changeChannel = channel;
  }
 
  public void turnOn() {
     onCommand.execute();
  }
 
  public void turnOff() {
     offCommand.execute();
  }
 
  public void changeChannel() {
     changeChannel.execute();
  }
}
//测试类Client
public class Client {
  public static void main(String[] args) {
     // 命令接收者Receiver
     Tv myTv = new Tv();
     // 开机命令ConcreteCommond
     CommandOn on = new CommandOn(myTv);
     // 关机命令ConcreteCommond
     CommandOff off = new CommandOff(myTv);
     // 频道切换命令ConcreteCommond
     CommandChange channel = new CommandChange(myTv, 2);
     // 命令控制对象Invoker
     Control control = new Control(on, off, channel);
 
     // 开机
     control.turnOn();
     // 切换频道
     control.changeChannel();
     // 关机
     control.turnOff();
  }
}

执行结果

The televisino is on.
Now TV channel is 2
The television is off.

2.9.4 命令模式的优缺点

  • 优点
  1. 降低对象的耦合度。
  2. 新的命令很容易加入到系统中。
  3. 可以很容易的设计一套命令。
  4. 调用同一个方法不同功能
  • 缺点
  1. 使用命令模式可能会导致某些系统有很多具体的命令类,因为针对每一个命令需要设计一个命令类,因此某些系统可能需要大量的命令类,将影响命令模式的使用。
  • 总结

    1. 命令模式本质是对,命令进行封装,将发出命令的责任和执行命令的责任分割开。
    2. 每一个命令都是一个操作,请求一方发出命令,要求执行一个动作;接收方收到请求,并执行请求。
    3. 命令模式允许请求的一方和接收的一方,独立开来,使得请求方不知道接收方的接口,更不知道请求是怎么被接收,以及是否被执行,何时被执行,以及怎么被执行。
    4. 命令模式使请求本身成为对象,这个对象和其他对象可以被存储和传递。
    5. 命令模式的关键在于引入抽象接口,且发送者针对抽象接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java后端分享整理 的相关文章

  • 唯一索引或主键违规:“PRIMARY KEY ON PUBLIC.xxx”; SQL语句

    每当我的应用程序启动时 我都会收到以下错误消息 Caused by org h2 jdbc JdbcSQLException Unique index or primary key violation PRIMARY KEY ON PUBL
  • 在 JTable 中移动行

    我使用 MVC 模式 并且有一个如下所示的 JTable List
  • 如果测试用例失败,Selenium Web 驱动程序无法关闭 Firefox 实例

    我各位 我正在使用 junit 和 selenium web 驱动程序 2 28 问题是 如果我运行成功的测试用例 Web 驱动器能够关闭 Firefox 实例 但是当测试用例失败时 Selenium Web 驱动器无法关闭 Firefox
  • 如何将 Java 赋值表达式转换为 Kotlin

    java中的一些东西就像 int a 1 b 2 c 1 if a b c System out print true 现在它应该转换为 kotlin 就像 var a Int 1 var b Int 2 var c Int 1 if a
  • ElasticBeanstalk Java,Spring 活动配置文件

    我正在尝试通过 AWS ElasticBeanstalk 启动 spring boot jar 一切正常 配置文件为 默认 有谁知道如何为 java ElasticBeanstalk 应用程序 不是 tomcat 设置活动配置文件 spri
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • manifest.mf 文件的附加内容的约定?

    Java JAR 中的 MANIFEST MF 文件是否有任何超出 MANIFEST MF 约定的约定 JAR规范 http download oracle com javase 1 4 2 docs guide jar jar html
  • IntelliJ IDEA 创建的 JAR 文件无法运行

    我在 IntelliJ 中编写了一个跨越几个类的程序 当我在 IDE 中测试它时它运行良好 但是 每当我按照教程将项目制作成 jar 可执行文件时 它就不会运行 双击 out 文件夹中的文件时 该文件不会运行 并显示 无法启动 Java J
  • java中删除字符串中的特殊字符?

    如何删除字符串中除 之外的特殊字符 现在我用 replaceAll w s 它删除了所有特殊字符 但我想保留 谁能告诉我我该怎么办 Use replaceAll w s 我所做的是将下划线和连字符添加到正则表达式中 我添加了一个 连字符之前
  • 当分配给变量时,我可以以某种方式重用 Gremlin GraphTraversals 代码吗?

    我有看起来像这样的 GraphTraversals attrGroup GraphTraversal
  • 一种使用 Java Robot API 和 Selenium WebDriver by Java 进行文件上传的解决方案

    我看到很多人在使用 Selenium WebDriver 的测试环境中上传文件时遇到问题 我使用 selenium WebDriver 和 java 也遇到了同样的问题 我终于找到了解决方案 所以我将其发布在这里希望对其他人有所帮助 当我需
  • 制作java包

    我的 Java 类组织变得有点混乱 所以我要回顾一下我在 Java 学习中跳过的东西 类路径 我无法安静地将心爱的类编译到我为它们创建的包中 这是我的文件夹层次结构 com david Greet java greeter SayHello
  • 尝试使用 Ruby Java Bridge (RJB) gem 时出现错误“无法创建 Java VM”

    我正在尝试实现 Ruby Java Bridge RJB gem 来与 JVM 通信 以便我可以运行 Open NLP gem 我在 Windows 8 上安装并运行了 Java 所有迹象 至少我所知道的 都表明 Java 已安装并可运行
  • org.jdesktop.application 包不存在

    几天以来我一直在构建一个 Java 桌面应用程序 一切都很顺利 但是今天 当我打开Netbeans并编译文件时 出现以下编译错误 Compiling 9 source files to C Documents and Settings Ad
  • 如何测试 spring-security-oauth2 资源服务器安全性?

    随着 Spring Security 4 的发布改进了对测试的支持 http docs spring io spring security site docs 4 0 x reference htmlsingle test我想更新我当前的
  • 将 JTextArea 内容写入文件

    我在 Java Swing 中有一个 JTextArea 和一个 提交 按钮 需要将textarea的内容写入一个带有换行符的文件中 我得到的输出是这样的 它被写为文件中的一个字符串 try BufferedWriter fileOut n
  • 将2-3-4树转换为红黑树

    我正在尝试将 2 3 4 树转换为 java 中的红黑树 但我无法弄清楚它 我将这两个基本类编写如下 以使问题简单明了 但不知道从这里到哪里去 public class TwoThreeFour
  • 休眠以持久保存日期

    有没有办法告诉 Hibernate java util Date 应该持久保存 我需要这个来解决 MySQL 中缺少的毫秒分辨率问题 您能想到这种方法有什么缺点吗 您可以自己创建字段long 或者使用自定义的UserType 实施后User
  • java迭代器内部是如何工作的? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个员工列表 List
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个

随机推荐

  • 网易2018校园招聘编程题真题集合

    1 8 编程题 魔法币 include
  • linux fedora 桌面环境,为Fedora 30系统安装Deepin桌面环境(DDE)

    Fedora 30已经新增两个桌面环境选项 DeepinDE 和 Pantheon Desktop 所以除GNOME KDE Plasma 和 Xfce 之外 因为Deepin桌面环境现在可以在Fedora 30存储库中使用 用户现在还可以
  • ChatGPT应用场景: 基于对话生成的智能客服系统

    关于ChatGPT 今天小编简单说下用在客服服务的要点 ChatGPT可以用于开发基于对话生成的智能客服系统 帮助企业提供高效 便捷 满意的在线客服服务 从而提升客户体验和满意度 以下是ChatGPT应用于智能客服系统的一些场景 1 自动回
  • 基于redis分布式缓存实现

    在网上找了些redis搭建集群的资料 分享给大家 可以仔细看看 了解redis主从复制的逻辑 以及如何构建redis集群 Redis复制流程概述 Redis的复制功能是完全建立在之前我们讨论过的基于内存快照的持久化策略基础上的 也就是说无论
  • flask.ext 的那些事

    偶然发现 在pycharm 使用alter enter 自动修复 未导入的外部模块时 推荐的是 flask ext 包名 的形式 如下例 from flask ext migrate import Migrate MigrateComman
  • 分立元件搭建自举电路解析-高端mos驱动

    分立元件搭建自举电路 上桥mos驱动 高端MOS为什么要自举电路 自举电容 分立元件搭建自举电路 高端MOS为什么要自举电路 众所周知MOS是电压型驱动 只有G极比S极高一个开启电压Vth之后 MOS才会导通 这里指NMOS 但是如下图 我
  • JQuery 页面加载完成后执行事件

    一 document ready function code 二 jQuery document ready function code 三 window nl ad function code 四 将jquery代码放入body的后面 这
  • 使用OpenCV/python进行双目测距

    在做SLAM时 希望用到深度图来辅助生成场景 所以要构建立体视觉 在这里使用OpenCV的Stereo库和python来进行双目立体视觉的图像处理 立体标定 应用标定数据 转换成深度图 标定 在开始之前 需要准备的当然是两个摄相头 根据你的
  • 什么是 agent

    agent 是任何通过sensor感知其环境并通过actuators在此环境中作出行动的东西 比如人agent sensor 是眼睛 耳朵 以及其他器官 actuators 是手 腿 声道等 比如机器人agent sensor 是摄像头 红
  • JVM系列-第8章-执行引擎

    文章目录 toc 执行引擎 执行引擎概述 执行引擎概述 执行引擎工作过程 Java代码编译和执行过程 解释执行和即时编译 什么是解释器 什么是JIT编译器 机器码 指令 汇编语言 机器码 指令和指令集 汇编语言 高级语言 字节码 C C 源
  • Java使用图片压缩工具压缩图片的两种方法

    上代码 pom xml
  • 单元测试是什么?为什么要做单元测试?

    背锅侠 一个有个性的订阅号 1 单元测试是什么 单元测试是开发者编写的一小段代码 用于检验被测代码的一个很小的 很明确的功能是否正确 通常而言 一个单元测试是用于判断某个特定条件 或者场景 下某个特定函数的行为1 长按图片识别二维码 入群
  • [Qt 教程之Widgets模块] —— QButtonGroup抽象容器

    Qt系列教程总目录 文章目录 0 QButtonGroup简介 1 创建QButtonGroup 2 成员函数与信号 3 示例 3 1 为按钮组添加按钮 3 2 为按钮设置id 3 3 按钮组中按钮的互斥状态 3 4 获取组内所有按钮 3
  • 开源图像和视频编辑软件汇总

    1 Belender http www blender org Blender 是一套 三维绘图 及 渲染 软件 它具有跨平台的特性 支持 FreeBSD IRIX GNU Linux Microsoft Windows Mac OS X
  • django自带的序列化组件

    目录 sweetalert前端插件 django自带的序列化组件 简易分页器 带有页码的分页器 优化后的版本 模块代码 后端代码 Forms组件 校验数据 渲染标签 展示信息 widgets 注意 sweetalert前端插件 https
  • 计算机组成原理 及CPU,硬盘,内存三者的关系

    电脑之父 冯 诺伊曼 提出了组成计算机的五大部件 输入设备 输出设备 存储器 运算器和控制器 下图为 现在我们电脑的 键盘鼠标 显示器 机箱 音响等等 这里显示器为比较老的CRT显示器 现在一般都成功了液晶显示器 回想一下 在玩电脑的时候
  • chromium-cronet库的编译用于Android和ios平台实现quic协议

    chromium cronet文档 原文文档写的已经很清楚 最好还是参考官方文档 避免由于版本原因导致的问题 Cronet开发者文档 https developer android com guide topics connectivity
  • oracle学习之路(6)oracle下载地址

    ocacle下载地址 https www oracle com database technologies ocacle19c版本下载地址https www oracle com database technologies oracle d
  • flutter项目中使用

    一些小部件 GestureDetector 手势 手势表示由一个或多个指针移动组成的动作 主要有以下几种 onTap 点击事件触发 Divider 设置分割线 SharedPreferences数据存储 SharedPreferences
  • java后端分享整理

    java规范总结 1 Java 常见的代码规范 1 1 Java 自带的工具方法 1 1 1 比较两个对象是否相等 1 1 2 apache commons工具类库 1 1 2 1 字符串判空 1 1 2 3 重复拼接字符串 1 1 2 4