当前位置: 首页 > news >正文

设计模式七大设计原则

文章目录

  • 1、什么是设计模式
  • 2、单一职责原则
  • 3、开闭原则
  • 4、接口隔离原则
  • 5、依赖倒置原则
  • 6、迪米特法则(最少知道原则)
  • 7、里式替换原则
  • 8、组合优于继承

设计模式主要是为了满足一个字 ,这个字,可能是需求变更、可能是场景变更,但是运用好设计模式后我们写出的代码就能很好的应对不断变化的场景。

1、什么是设计模式

设计模式是前辈们不断总结、优化、打磨出来的设计方法,不同设计模式适用于不同的场景

但要明确一点,没有任何一种设计模式,能达到适用于所有场景的效果!

只有运用好设计原则和设计模式,才能让我们写出更加优秀的代码或者设计更好软件架构

设计模式有23种,其中每个设计模式又依赖于七大设计原则中的一个或多个

  • 单一职责原则
  • 开闭原则
  • 接口隔离原则
  • 里氏替换原则
  • 依赖倒置原则
  • 组合优于继承原则
  • 迪米特法则(最少知道原则)

下面我们详细聊聊七大设计原则

2、单一职责原则

单一职责是什么呢?

核心思想:每个方法、每个类、每个框架都只负责一件事情

举个栗子:

  • Math.round() ,只负责完成四舍五入的功能,其他的不管(方法)

  • Reader类,只负责读取文本文件(类)

  • Spring MVC,只负责简化MVC开发(框架)

单一职责讲究一个”“字,将功能尽可能的拆分,然后使用的时候进行组合

优点:

​ 1.代码重用性提高

​ 2.代码可读性提高,此时的代码,就像一个大纲一样

现在需求来了:统计一个文本文件中有多少个单词

我们先来看一个栗子

public class nagtive {public static void main(String[] args) {try{//统计一个文本文件中有多少个单词//Reader默认查询的码表是与操作系统一致的码表,我们的操作系统是中文的,所以Reader就会使用GBK码表//GBK码表一个汉字占2个字节 ,且汉字的两个字节都是以1开头,utf8码表一个汉字占3个字节//读取到记事本中的数字45489---> GBK --->北 --->unicode ---> 21271//总之一句话:字符流读取文件会查询码表,字节流不会查询码表Reader in = new FileReader("E:\\1.txt");BufferedReader bufferedReader = new BufferedReader(in);String line = null;StringBuilder sb = new StringBuilder("");while((line =bufferedReader.readLine()) != null){sb.append(line);sb.append(" ");}//对内容进行分割String[] words = sb.toString().split("[^a-zA-Z]+");System.out.println(words.length);bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}
}

相信很多同学拿到需求上来就是梭哈搞定,同一个方法将所有的事做完了(让它去做文件读取,还让它去做内容分割)。这就违背了单一职责原则。

之前聊到设计模式讲究一个”“字,那现在需求变了,我们需要统计文本文件中有多少个句子,那我们的做法是什么呢?重新写一个方法,将读取文件的内容部分的代码复制粘贴过去?然后改一下分割条件?这样虽然能解决问题,但是你有没有发现代码变得很臃肿呀,这显然是不合理的。

那正确的做法是什么呢?应该是将读取文件内容部分封装成一个方法,将内容分割也封装成一个方法,然后根据需求进行组合

看下面这个栗子:

public class demo {//读取文件的内容public static StringBuilder loadFile(String path) throws IOException {Reader in = new FileReader(path);BufferedReader bufferedReader = new BufferedReader(in);String line = null;StringBuilder sb = new StringBuilder("");while ((line = bufferedReader.readLine()) != null) {sb.append(line);sb.append(" ");}bufferedReader.close();return sb;}//对内容进行分割public static String[] getSplit(String regex, StringBuilder sb){return  sb.toString().split(regex);}//--------------------------------------------------------------------------//需求://统计一个文本文件中有多少个单词public static Integer getWords() throws IOException {//读取文件的内容StringBuilder sb = loadFile("E:\\1.txt");//对内容进行分割String[] words = getSplit("[^a-zA-Z]+", sb);return words.length;}//统计一个文本文件中有多少个句子public static Integer getSentence() throws IOException {//读取文件的内容StringBuilder sb = loadFile("E:\\1.txt");//对内容进行分割String[] words = getSplit("[.,!?]", sb);return words.length;}public static void main(String[] args) throws IOException {System.out.println(getWords());System.out.println(getSentence());}
}

遵守单一原则,可以给我们带来的好处是,提高了代码的可重用性,同时还让得到的数据不再有耦合,完成我们的需求。

3、开闭原则

简单来说就是

对扩展开放,对修改关闭

在程序需要进行拓展的时候,不能去修改原有的代码。

举个栗子,我现在有一个刮胡刀,刮胡刀的功能应该就是刮胡子,但是我现在想要它拥有吹风机的能力

  • 违法开闭原则的做法是,把吹风机的功能加上了,可能就不能刮胡子了
  • 符合开闭原则的做法是,把吹风功能加上,且没有影响之前刮胡子的功能

例如我现在有一个商品类Goods,这个类之前有一个方法是获取它的价格,例如:

public class Goods {private BigDecimal price;public void setPrice(BigDecimal price) {this.price = price;}public BigDecimal getPrice() {return this.price;}
}

现在变化来了,当前商品需要打8折进行销售,不符合开闭原则的做法就是直接在原来的代码中进行修改

public BigDecimal getPrice() {// BigDecimal可以防止精度丢失return this.price.multiply(new BigDecimal("0.8"));
}

这样显然是不合理的,因为我们对源代码进行了修改,如果下次是打七折,那是不是又要去改源代码呢

正确的做法应该是写一个子类DiscountGoods来拓展父类的功能,再在子类上进行修改,这样就不会破坏父类的功能,又能满足需求

public class DiscountGoods extends Goods{@Overridepublic BigDecimal getPrice() {return super.getPrice().multiply(new BigDecimal("0.8"));}
}

这就叫对扩展开发,对修改关闭。我们在用设计模式编码时应该时刻注意的是,改源码是一件非常危险的事情,因为一个功能并不是只有你在使用,很容易造成牵一发而动全身的效果

但是如果我们因为要遵守开闭原则,每次对功能进行修改的时候,都去新写一个类,这样的会很繁琐,所以我们的准则是:

  • 如果这个类是自己写的,自己修改不会影响该类在其他地方的效果(不会牵一发而动全身),那就可以随意修改
  • 如果这个类不是自己写的,自己不清楚修改后会带来什么样的影响,那就不要修改,要符合开闭原则

4、接口隔离原则

接口隔离原则也是满足一个字 ”“,将接口的功能尽可能的拆分

应该使用多个专门的接口,而不是使用单一的总接口

即客户端不应该依赖于那些它不需要的接口

举个栗子:现在设计一个动物的接口,统一动物的行为,可能会这样写

public interface Animal {void eat();void fiy(); void swim(); 
}

这三个行为分别是 吃、飞和游泳,似乎并没有什么问题,但是动物这个接口太广了,并不是所有的动物都有着这三种行为

例如小狗的栗子:

public class Dog implements Animal {@Overridepublic void eat() {System.out.println("小狗啃骨头");}@Overridepublic void swim() {System.out.println("小狗会狗刨");}@Overridepublic void fly() {throw new UnsupportedOperationException("小狗不会飞,你行你来");}
}

小狗并不具备飞的属性

正确的做法是将动物这个总接口拆分成多个单独的小接口

interface Eatable{void eat();
}interface Swimable{void swim();
}interface Flyable{void fly();
}

再不断的组合,实现不同的接口

核心思想还是高内聚,低耦合,通过不断组合不可分割的功能完成最终需要的功能

我们改进一下小狗的栗子

public class Dog implements Eatable, Swimable {@Overridepublic void eat() {System.out.println("小狗啃骨头");}@Overridepublic void swim() {System.out.println("小狗会狗刨");}
}

客户端依赖的接口中不应该存在他所不需要的方法。

如果某一接口太大导致这一情况发生,应该拆分这一接口,使用接口的客户端只需要知道它需要使用的接口及该接口中的方法即可。

5、依赖倒置原则

面向接口编程,依赖于抽象而不依赖于具体

  • 上层不应该依赖于下层
  • 它们都应该依赖于抽象

区分上下层的方法为:调用别的方法的就是上层,被调用的就是下层

举个栗子:人喂养动物

class Person {public void feed(Dog dog) {System.out.println("开始喂dog...");}
}class Dog {public void eat() {System.out.println("狗啃骨头");}
}------------------------------------------------------------
public class AppTest {public static void main(String[] args) {Person person = new Person();Dog dog = new Dog();person.feed(dog);}
}

上述代码好像并没有什么问题,但是设计模式是为了应对变化,现在变化来了,现在客户端Person不仅需要喂狗,还需要喂猫。

直接添加一个Cat

class Cat {public void eat() {System.out.println("小猫吃鱼");}
}
public class AppTest {public static void main(String[] args) {Person person = new Person();Dog dog = new Dog();Cat cat = new Cat();// 喂狗person.feed(dog);// 喂猫person.feed(cat);}
}

这样明显会报错,因为之前的代码中只能喂狗,不能喂猫!
在这里插入图片描述

那怎么办呢?我直接重载一个方法,让Person类可以喂猫不就好了?

class Person {public void feed(Dog dog) {System.out.println("开始喂dog...");}public void feed(Cat dog) {System.out.println("开始喂Cat...");}
}

好家伙,这是不是为了应对变化直接改源码了?首当其冲的就是破坏了开闭原则,其次如果每次要多喂养一种动物就要去重载一个方法,似乎并不合理。

每当一个新的类需要依赖时,就要重载一个方法,这里就违反了依赖倒置原则,每当下层发生改变时,上层要一起改变(下层多个猫,上层要重载喂猫),这样的设计没有拓展性我们不应该依赖于具体的类,而应该依赖于抽象的接口!

我们聊回来,猫和狗都属于什么?是动物,狗和猫只是动物的实现,人应该去喂养动物,而不是具体的实现,所以我们应该进行依赖倒置,依赖抽象不依赖实现,这里我们只需要依赖一个抽象的动物类或者接口即可

class Person {public void feed(Animal animal) {System.out.println("开始喂动物...");}
}interface Animal {void eat();
}class Dog implements Animal{@Overridepublic void eat() {System.out.println("狗啃骨头");}
}class Cat implements Animal{@Overridepublic void eat() {System.out.println("小猫吃鱼");}
}---------------------------------------------------
public class AppTest {public static void main(String[] args) {Person person = new Person();Dog dog = new Dog();Cat cat = new Cat();// 喂狗person.feed(dog);// 喂猫person.feed(cat);}
}

看一下类图的变化

image-20221003003659847

这里有读者可能有疑问了?为什么是依赖倒置呢?

看类图,之前箭头是向下的,依赖于具体实现;之后大家都指向抽象,面向抽象编程,这就是依赖倒置。

6、迪米特法则(最少知道原则)

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立

一个类,对于其他类,要知道的越少越好,封装的思想,封装内部细节,向外暴露提供功能的接口

只和朋友通讯,朋友是指:

  • 类中的字段
  • 方法的参数
  • 方法的返回值
  • 方法中实例化出来的对象
  • 对象本身
  • 集合中的泛型

我们来看一个栗子:现在有一个电脑,需要关闭它

class Compute {public void saveData() {System.out.println("正在保存数据");}public void killProcess() {System.out.println("正在关闭程序");}public void closeScreen() {System.out.println("正在关闭屏幕");}public void powerOff() {System.out.println("正在断电");}
}class Person {Compute compute = new Compute();public void shutDownCompute() {compute.saveData();compute.killProcess();compute.closeScreen();compute.powerOff();}
}

好像没有什么问题,

但对于用户来说,知道的细节太多了,要是不小心搞错了步骤,那岂不是玩完?所以他不想知道关闭电脑的具体步骤,只想按一下按钮(封装)就好了

我们改一下上面的代码:

class Compute {private void saveData() {System.out.println("正在保存数据");}private void killProcess() {System.out.println("正在关闭程序");}private void closeScreen() {System.out.println("正在关闭屏幕");}private void powerOff() {System.out.println("正在断电");}//封装细节public void shutDownCompute() {this.saveData();this.killProcess();this.closeScreen();this.powerOff();}
}class Person {Compute compute = new Compute();public void shutDown() {compute.shutDownCompute();}
}

那么对于朋友而言的最少知道原则是什么呢?

  • 如果对于作为返回类型、方法参数、成员属性、局部变量的类,不需要过多的封装,应该提供应有的细节,由调用者自己弄清楚细节并承担异常的后果,这样由我们直接创造的对象,我们就能把它称为我们的朋友

  • 但是如果这个对象不是我们自己获得的,而是由被人提供的,就不是朋友,即朋友的朋友并不是自己的朋友

public class AppTest {public void func() {AppBean appBean = BeanFactory.getAppBean();// 朋友的朋友就不是朋友了appBean.getStr();}}class BeanFactory {public static AppBean getAppBean() {return new AppBean();}
}class AppBean {public String getStr() {return "";}
}

那么想要和这个AppBean做朋友该怎么办呢?比如给它转换成方法参数

public class AppTest {public void func() {AppBean appBean = BeanFactory.getAppBean();// 朋友的朋友就不是朋友了this.getStr(appBean);}/* 将朋友的朋友的细节转换为自己熟悉的方法 */public String getStr(AppBean appBean){return appBean.getStr();}
}

相信很多同学看到这里很谜,这不是制造了很多小方法吗?确实迪米特法则的缺点就是如此,在系统里造出大量的小方法,这些方法仅仅是传递间接的调用,与系统的业务逻辑无关。所以在开发中适当的违反一下也是可以的。

因此,前人总结出一些方法论以供我们参考:

  1. 优先考虑将一个类设置成不变类。
  2. 尽量降低一个类的访问权限。
  3. 谨慎使用Serializable
  4. 尽量降低成员的访问权限。

虽然规矩很多,但是理论需要深刻理解,实战需要经验积累。路还很长。

7、里式替换原则

任何能够使用父类对象的地方,都应该能透明的替换为子类

也就是说:子类对象能够随时随地替换父类对象,并且替换完之后,语法不会报错,业务逻辑也不会出现问题

我们先聊一下方法重写的定义:

  • 在子类和父类中,出现了返回类型相同、方法名相同、方法参数相同的方法时,构成了方法重写。

方法重写的两个限制:

  1. 子类重写父类的方法时,子类方法的访问修饰符不能比父类更严格
  2. 子类重写父类的方法时,子类方法不能抛出比父类更多的异常

为什么要有这两个限制呢?

就是为了保证代码符合里氏替换原则

举个栗子:
在这里插入图片描述

正常情况下,如果子类抛出的异常比父类少,父类在执行方法时就会进行catch,并且能够捕获子类中的异常,所以这样进行替换时,就不会影响代码的结构,做到透明、无感知

有很多的例子都可以用里式替换进行解释,著名的例子有:长方形正方形问题

接下来我们具体看看长方形正方形的问题,先来回顾下继承方面的知识

继承的作用:

  • 提高代码重用性
  • 多态的前提

两个类能发生继承关系的依据是什么?

  • 先看两个类有咩有” is a “ 关系
  • 在两个类有了 is a 关系之后,还要考虑子类对象在替换了父类对象之后,业务逻辑是否发生变化。如果变化,就不能发生继承关系

正方形和长方形是 is a 关系,那么我们能不能让正方形类直接去继承长方形类呢?

答案是不能,为什么呢?因为还要考虑具体的业务场景,看看在具体的业务场景下,正方形替换了长方形之后,业务逻辑是否变化

举个栗子:

public class AppTest {//长方形@Getter@Setterstatic class Rectangular {private Integer width;private Integer length;}//正方形static class Square extends Rectangular {private Integer sideWidth;@Overridepublic Integer getWidth() {return sideWidth;}@Overridepublic void setWidth(Integer width) {this.sideWidth = width;}@Overridepublic Integer getLength() {return sideWidth;}@Overridepublic void setLength(Integer length) {this.sideWidth = length;}}static class Utils{public static void transform(Rectangular graph){while ( graph.getWidth() <= graph.getLength() ){graph.setWidth(graph.getWidth() + 1);System.out.println("长:"+graph.getLength()+" : " +"宽:"+graph.getWidth());}}}public static void main(String[] args) {// Rectangular graph = new Rectangular();Rectangular graph = new Square();graph.setWidth(20);graph.setLength(30);Utils.transform(graph);}
}

替换后运行将是无限死循环。

要知道,在向上转型的时候,方法的调用只和new的对象有关,才会造成不同的结果。在使用场景下,需要考虑替换后业务逻辑是否受影响。

由此引出里氏替换原则的使用需要考虑的条件:

  • 是否有is-a关系
  • 子类可以扩展父类的功能,但是不能改变父类原有的功能。

鸵鸟非鸟问题

  • 在我们看来,鸵鸟属于鸟科,但是现在有个需求是送信(飞鸽传书),这个业务场景能将鸵鸟(子类)替换为鸟(父类)吗?鸵鸟不会飞,所以这显然是不可以的。

8、组合优于继承

复用别人的代码时,不宜使用继承,应该使用组合。

  • 组合,是一种强关联关系,整体对象和局部对象的生命周期是一样的,类似于大雁和翅膀的关系
    • 整体对象负责局部对象的生命周期
    • 局部对象不能被其他对象共享;
    • 如果整体对象被销毁或破坏,那么局部对象也一定会被销毁或破坏
  • 聚和,它是一种弱关联,是 【整体和局部】之间的关系,且局部可以脱离整体独立存在,类似于雁群和其中一只大雁的关系
    • 代表局部的对象有可能会被多个代表整体的对象所共享,而且不一定会随着某个代表整体的对象被销毁或破坏而被销毁或破坏,甚至代表局部的对象的生命周期可以超越整体

总而言之,组合是值的关联(Aggregation by Value),而聚合是引用的关联(Aggregation by Reference)

实心菱形的是组合空心菱形的是聚和,如果不区分就用虚线指向,组合是作为成员变量作为另一个类的引用,聚和是作为形参或者局部变量作为另一个类的引用

image-20221003000126551

组合大家在平时编码的时候一定经常使用,举一个简单的例子,如果我们现在要有链表实现队列应该怎么做呢?队列的特点就是先进先出,完全可以用链表实现,我们可以用继承关系来做:

public class Queue <E> extends LinkedList<E> {/*** 入队*/public void enQueue(E element){this.add(element);}/*** 出队*/public E deQueue(){return this.remove(0);}}

似乎并没有什么问题,队列类继承自链表类,并暴露自己提供给外界的方法,但是当我们调用这个Queue时就会发现问题:

image-20221003154018680

好家伙,我的Queue本来只需要入队和出队两个方法,但是居然有这么多细节的方法供我使用,这就违背了迪米特法则,一个类的内部实现应该不要提供给外界,只暴露该提供的方法,这就是继承的问题,继承复用破坏包装,因为继承将基类的实现都暴露给派生类

如果我们换成组合该怎么做呢?

public class Queue<E> {// 成员变量 -> 组合关系LinkedList<E> list = new LinkedList<>();/*** 入队*/public void enQueue(E element) {list.add(element);}/*** 出队*/public E deQueue() {return list.remove(0);}
}

所以如果我们仅仅只是为了复用代码,可以优先考虑组合,如果是为了实现多态,可以优先继承

我们也来看一个反例叭,其实在Java中有很多不合理的设计,例如Serializable接口,Date类等等,这里就讲一个java.util.Stack的糟糕设计

image-20221003160116593

点进源码中看我们发现,原来是继承了Vector类,让其拥有了链表的能力,看着这个兄弟设计模式也没学好

image-20221003160243437

官方也注意到了这个设计不合理的地方,推荐我们使用Deque来实现栈

image-20221003160720506

其实我们看完了这些设计原则,就会发现其实都是为了应对不断变化的,在看一些源码中,例如Spring的源码、dubbo的源码、netty的源码中也是非常严谨的遵守这些开发规范的。

本文部分内容参考了我老大的博文:设计模式学习(汇总版

大佬的文章写的太好了

关于设计模式,我们后面接着聊…

相关文章:

设计模式七大设计原则

文章目录 1、什么是设计模式2、单一职责原则3、开闭原则4、接口隔离原则5、依赖倒置原则6、迪米特法则&#xff08;最少知道原则&#xff09;7、里式替换原则8、组合优于继承 设计模式主要是为了满足一个字 变&#xff0c;这个字&#xff0c;可能是需求变更、可能是场景变更&a…...

【Hello Network】TCP协议相关理解

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;补充下对于TCP协议的各种理解 TCP协议相关实验 TCP相关试验理解CLOSE_WAIT状态理解TIME_WAIT状态解决TIME_WAIT状态引起的bind失败的方法理解listen的…...

实施CRM目标有哪几步?如何制定CRM目标?

在当今竞争激烈的商业环境中&#xff0c;与客户建立持久的关系是企业重要的工作。CRM客户管理系统能有效帮助企业管理优化流程、管理客户&#xff0c;提高销售成功率&#xff0c;推动收入增长。那么您了解如何实施CRM吗&#xff1f;下面说说实施CRM目标是什么&#xff0c;如何设…...

船舶建造概论(船舶建造工艺任务与现代造船模式)

船舶建造概论 1 船舶建造概论1.1 船舶建造工艺主要任务1.2 船舶建造流程&#xff08;1&#xff09;钢材料预处理&#xff08;2&#xff09; 钢材料加工&#xff08;3&#xff09;分段制作&#xff08;4&#xff09;总段制作&#xff08;5&#xff09;船台合拢&#xff08;6&…...

项目内训(2023.5.6)

目录 Nacos是什么&#xff1f; 领域模型是什么&#xff1f; domain模块一般是干什么的&#xff1f; 在小乌龟中合并其他分支的作用是什么&#xff1f; nacos的配置文件 服务集群、服务提供、服务更加灵活庞大、消费服务、访问比较麻烦&#xff0c;A和B服务一起访问 系统结…...

【操作系统OS】学习笔记第二章 进程与线程(下)【哈工大李治军老师】

基于本人观看学习 哈工大李治军老师主讲的操作系统课程 所做的笔记&#xff0c;仅进行交流分享。 特此鸣谢李治军老师&#xff0c;操作系统的神作&#xff01; 如果本篇笔记帮助到了你&#xff0c;还请点赞 关注 支持一下 ♡>&#x16966;<)!! 主页专栏有更多&#xff0…...

Linux命令集(Linux文件管理命令--rmdir指令篇)

Linux命令集&#xff08;Linux文件管理命令--rmdir指令篇&#xff09; Linux文件管理命令集&#xff08;rmdir指令篇&#xff09;5. rmdir(remove directory)1. 删除空的目录 folder12. 强制删除目录 folder1&#xff08;包括非空目录&#xff09;3. 递归删除目录及其目录下所有…...

在技术圈超卷的当下,学历到底是敲门砖还是枷锁?

前言 最近&#xff0c;突然之间被“孔乙己文学”刷屏了&#xff0c;短时间内“孔乙己文学”迅速走红&#xff0c;孔乙己是中国文学中的一位经典人物&#xff0c;他的长衫被认为是他的象征之一&#xff0c;孔乙己的长衫折射出很多现象&#xff0c;既有社会的&#xff0c;也有教育…...

Linux cgroup

前言 Cgroup和namespace类似&#xff0c;也是将进程进程分组&#xff0c;但是目的与namespace不一样&#xff0c;namespace是为了隔离进程组之前的资源&#xff0c;而Cgroup是为了对一组进程进行统一的资源监控和限制。 Cgroup的组成 subsystem 一个subsystem就是一个内核模…...

PID整定二:基于Ziegler-Nichols的频域响应

PID整定二&#xff1a;基于Ziegler-Nichols的频域响应 1参考2连续Ziegler-Nichols方法的PID整定2.1整定方法2.2仿真示例 1参考 1.1根轨迹图的绘制及分析 1.2计算机控制技术01-3.4离散系统的根轨迹分析法 1.3PID控制算法学习笔记 2连续Ziegler-Nichols方法的PID整定 2.1整定…...

【tkinter 专栏】专栏前言

文章目录 前言本章内容导图1. tkinter 工具及特点2. 为什么使用 Python 进行 GUI 设计?2.1 Python 可以做什么2.2 使用 tkinter 可以干什么?3. 如何学习使用 tkinter 进行 GUI 设计?4. 开发环境搭建4.1 Python 的版本4.2 安装 Python4.2.1 下载 Python 安装包4.2.2 安装 Pyt…...

解决Linux中文字体模糊的4种方法

在Linux中&#xff0c;字体是非常重要的一部分&#xff0c;因为它们直接影响到用户的视觉体验。如果Linux字体模糊不清&#xff0c;那么用户将很难阅读文本&#xff0c;这将极大地降低用户的工作效率。本文将介绍Linux Mint中文字体模糊的问题&#xff0c;并提供一些解决方案。…...

【Android入门到项目实战-- 7.3】—— 如何调用手机摄像头和相册

目录 一、调用摄像头拍照 二、打开相册选择照片 学完本篇文章可以收获如何调用手机的摄像头和打开手机相册选择图片功能。 一、调用摄像头拍照 先新建一个CameraAlbumTest项目。 修改activity_main.xml,代码如下&#xff1a; 按钮打开摄像头&#xff0c;ImageView将拍到的…...

浅聊AIOT

引言 IoT是(Internet of Things)的简称&#xff0c;也就是人们常说的物联网&#xff1b;随着智能硬件的发展和推广&#xff0c;制造成本也随之下降&#xff0c;很多的厂家也慢慢地拥抱网络互联&#xff0c;逐步实现设备互联&#xff0c;也就进入了人们常说的万物互联时代。虽然…...

Python之模块和包(九)

1、模块 1、模块概述 模块是一个包含了定义的函数和变量等的文件。模块可以被程序引入&#xff0c;以使用该模块中的函数等功能。通俗讲&#xff1a;模块就好比是工具包&#xff0c;要想使用这个工具包中的工具(就好比函数)&#xff0c;就需要导入这个模块。 2、import 在P…...

C++-----动态规划

目录 一、动态规划的基本思想 二、设计动态规划法的步骤 三、动态规划问题的特征 4.1 矩阵连乘积问题 4.1.1 分析最优解的结构 4.1.2 建立递归关系 4.1.3 计算最优值 4.1.3 计算最优值 4.1.3 构造最优解 4.2 动态规划算法的基本要素 4.2.1 最优子结构 4.2.2 重叠子问题 …...

2.2 Linux控制台访问CLI

系列文章目录 第1章 Linux Shell简介 第2章 Shell基础 <本章所在位置> 第3章 Bash Shell基础命令 第4章 Bash Shell命令进阶 第5章 Linux Shell深度理解 第6章 Linux环境变量 第7章 Linux文件权限 第8章 Linux文件系统的管理 第9章 Linux软件安装 第10章 Linux文本编辑器…...

代码随想录补打卡 509 斐波那契数列

代码如下 //斐波那契数列的第0项是0 第一项是1 func fib(n int) int { if n < 1 { return n } dp : make([]int,n1) dp[0] 0 dp[1] 1 for i : 2 ; i < n ; i { dp[i] dp[i-1] dp[i-2] } return dp[n] } 70 爬楼梯 代码如下 func climbStairs(n int) int …...

【每日一题Day195】LC1003检查替换后的词是否有效 | 栈

检查替换后的词是否有效【LC1003】 给你一个字符串 s &#xff0c;请你判断它是否 有效 。 字符串 s 有效 需要满足&#xff1a;假设开始有一个空字符串 t "" &#xff0c;你可以执行 任意次 下述操作将 t 转换为 s &#xff1a; 将字符串 "abc" 插入到 t…...

简单理解什么是序列化

为什么要序列化 序列化的目的就是为了对象可以在网络层进行传输&#xff0c; 比如通过后端传给前端数据。 什么是序列化 我们以Java为例。 序列化就是把对象转化为可传输的字节序列过程&#xff0c;这个字节序列可以是字符串&#xff0c;比如JSON格式的字符串&#xff0c;把…...

Django初识

1、简介 Django&#xff0c;是用python语言写的开源web开发框架&#xff0c;并遵循MVC设计。劳伦斯出版集团为了开发以新闻内容为主的网站&#xff0c;而开发出来了这个框架&#xff0c;于2005年7月在BSD许可证下发布。这个名称来源于比利时的爵士音乐家DjangoReinhardt&#…...

ARM嵌入式编译器-volatile关键字对编译器优化的影响

volatile限定符告知计算机&#xff0c;其他agent&#xff08;而不是变量所在的程序&#xff09;可以改变该变量的值。通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。要求编译器不要对其描述的对象作优化处理&#xff0c;对它的读写都需要从内存中访问。 使用…...

销售数据分析怎么做?这篇文章说清楚了

如何分析销售数据&#xff1f;分析销售数据有哪些指标&#xff1f;销售数据分析有什么作用&#xff1f; 销售数据是不是得通过数据分析软件啊&#xff1f; 本文将为您解答疑惑—— 一、分析销售数据的指标 从两个层面上来讲&#xff0c;一个是对销售情况的整体把控&#xf…...

二十六、ISIS技术总结

文章目录 ISIS 概述一、路由协议总结1、路由优先级2、分类 二、ISIS 协议特点1、特点2、ISIS 路由器的种类 三、ISIS 配置1、基础配置2、network-entity含义3、router id 和系统id转换规则 四、ISIS 开销计算1、Narrow 模式2、Wide 模式 五、 ISIS 和 OSPF 的区别 ISIS 概述 I…...

三菱m70 m80系统解密 三菱m80机床到期解锁

我们从操作系统的发展讲起&#xff0c;为什么要有线程这个概念出现。《Java多线程学习笔记(一) 初遇篇》讲Java平台下的线程&#xff0c;如何使用和创建&#xff0c;以及引入线程后所面临的问题&#xff0c;为了解决线程安全问题&#xff0c;Java引入的机制&#xff0c;这也是《…...

InnoDB 磁盘结构之数据字典和双写缓冲区

数据字典&#xff08;InnoDB Data Dictionary&#xff09; MySQL中&#xff0c;数据字典包括了: 表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、MySQL版本信息、存储过程、触发器等内容 InnoDB数据字典由内部系统表组成&#xff0c;这些表包含用于查找表…...

Django模型层part two - 多表关系创建和多表操作

前言 继续上面一篇文章的内容&#xff0c;本文介绍多表操作。使用django ORM可以创建多表关系&#xff0c;并且也支持多张表之间的操作&#xff0c;以创建表关系和查询两部分说明django ORM的多表操作。以作者、图书、出版社和作者信息几张表作为案例进行说明。 创建表关系 …...

智能优化算法:浣熊优化算法-附代码

智能优化算法&#xff1a;浣熊优化算法 文章目录 智能优化算法&#xff1a;浣熊优化算法1.浣熊优化算法1.1 初始化1.2 阶段一&#xff1a;狩猎和攻击&#xff08;探索阶段&#xff09; 2.实验结果3.参考文献4. Matlab 摘要&#xff1a;浣熊优化算法&#xff08;Coati Optimizat…...

【51单片机】数码管显示(样例展示以及异常分析)

🎊专栏【51单片机】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 ⭐数码管 比如要显示“6”,那么下面图片中,AFEDCG=1,B=0 对应到数码管上,就是 ⭐原理 🎊P22~P24控制LED1~...

Android InputChannel事件发送接收系统分析

本文基于Android12。 InputChannel表示其他进程通过文件描述符传递输入事件到View的通道&#xff0c;因为需要跨进程传输&#xff0c;实现了Parcelable序列化接口&#xff0c;所以也能够理解Java层的InputChannel后面为什么使用copyTo()方法初始化。 输入事件的接收方是View&…...

四川网站建设和优化/巨量引擎app

需求描述&#xff1a; 在工作中&#xff0c;有的时候需要在cmd中进行盘符的切换&#xff0c;以前总有些时候 通过cd来切&#xff0c;就是切换不过去&#xff0c;每次都要进行百度查询&#xff0c;所以&#xff0c;这次就记录下&#xff0c; 留着以后再用。 操作过程&#xff1a…...

黄冈网站建设费用/网站建站哪家公司好

2011年07月12日16:32 下面介绍在Linux操作系统下安装配置maven和搭建nexus私服。一、安装前的准备下载 jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u26-download-400750.htmljdk-6u26-linux-x64.bin下载maven http://mirrors.geoexpat.com/apache//mav…...

加强学院网站的建设与管理/发稿

2019独角兽企业重金招聘Python工程师标准>>> http_status_bad_request (400) the request could not be processed by the server due to invalid syntax. 因为语法不能被服务器处理 http_status_denied (401) the requested resource requires user authentica…...

一个新网站要怎么做seo/西部数码域名注册官网

jsp使用jdk8时&#xff0c;需要tomcat7以及以上版本&#xff0c;jsp在使用jdk7的时候&#xff0c;tomcat使用tomcat6即可...

内蒙古建设工程交易中心网站/手机百度浏览器

TimeLimitingCollector 包装其他的收集器&#xff0c;当查询超过指定时间时通过抛出TimeExceededException异常来中止搜索。通过一个被包装的收集器&#xff0c;一个时钟定时器和超时时间来构造TimeLimitingCollector对象。setBaseline(long clockTime)&#xff1a;在包…...

网站开发建设准备工作/济南百度推广代理商

实验内容 本次实践项目就是将 Linux 0.11 中采用的 TSS 切换部分去掉&#xff0c;取而代之的是基于堆栈的切换程序。具体的说&#xff0c;就是将 Linux 0.11 中的 switch_to 实现去掉&#xff0c;写成一段基于堆栈切换的代码。 本次实验包括如下内容&#xff1a; 编写汇编程…...