写了这么久代码你了解Java面向对象的设计原则吗?,线程同步面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

  • 例如有两个类,一个类为 BaseClass,另一个是SubClass类,并且SubClass类是BaseClass类的子类,那么一个方法如果可以接受一个 BaseClass类型的基类对象base的话,如 method1 ( base),那么它必然可以接受一个 BaseClass类型的子类对象sub,即method1 (sub)能够正常运行。反过来的代换不成立,如方法 method2接受BaseClass类型的子类对象sub为参数(即 method2(sub))后,则一般情况下不可以有 method2(base),除非是重载方法。

  • 里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

在使用里氏替换原则时需要注意如下几个问题:

  1. 子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏替换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,父类中不提供相应的声明,则无法在父类对象中直接使用该方法。如果在父类 BaseClass 中声明了方法 method1() ,在子类SubClass中实现了方法 method1(),并增加了新的方法 method2() ,如果客户端针对父类编程,则无法使用子类中新增方法 method2() ,此时无法直接使用父类来定义,只能使用子类,则说明该设计违背了里氏替换原则,需要在设计父类时声明方法 method2(),以确保客户端可以透明地使用父类和子类对象。

  2. 在运用里氏替换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法。运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。里氏替换原则是开闭原则的具体实现手段之一。

  3. Java语言中,在编译阶段,Java编译器会检查一个程序是否符合里氏替换原则,这是一个与实现无关的,纯语法意义上的检查,但Java编译器的检查是有局限的。

里氏替换原则实例

  • 下面通过一个简单实例来加深对里氏代换原则的理解。

实例说明

  • 某系统需要实现对重要数据(如用户密码)的加密处理,在数据操作类(DataOperator)中需要调用加密类中定义的加密算法,系统提供了两个不同的加密类CipherA和CipherB,它们实现不同的加密方法,在 DataOperator中可以选择其中的一个实现加密操作,如图下图所示。

在这里插入图片描述

  • 在DataOperator类的encrypt(O)方法中,将调用加密类CipherA 或CipherB的加密方法encrypt()。如在客户类Client的 main()函数中可能存在如下代码片段:

CipherA cipherA = new CipherA();

DataOperator do = new DataOperator();

do.setCipherA(cipherA);

  • 与之对应,在:DataOperator类的encryptO)方法中可能存在如下代码片段:

return cipherA.encrypt(plainText)

  • 如果需要更换一个加密算法类或者增加并使用一个新的加密算法类,如将上述CipherA改为CipherB,则需要修改客户类Client和数据操作类DataOperator的源代码,违背了开闭原则。

现使用里氏替换原则对其进行重构,使得系统可以灵活扩展,符合开闭原则。

实例解析

  • 在本实例中,导致系统灵活性和可扩展性差的本质原因是Client类和 DataOperator类都针对每一个具体类进行编程,每增加一个具体类都将修改源代码,此时,可以将CipherB作为CipherA的子类,Client类和 DataOperator类都针对CipherA进行编程,根据里氏代换原则,所有能够接受CipherA类对象的地方都可以接受CipherB类的对象,因此可以简化DataOperator类和Client类的代码,而且将CipherA类对象替换成CipherB类对象很方便,无须修改任何源代码。如果需要增加一个新的加密算法类,如CipherC,只须将CipherC类作为CipherA类或CipherB类的子类即可。重构后的类图如下图所示。

在这里插入图片描述

  • 在上图中,由于CipherB是CipherA的子类,因此所有能够使用CipherA对象的地方都可以使用CipherB对象来替换,且可以将具体类的类名存储至配置文件中,如果需要使用CipherA 的encrypt()方法﹐则配置文件中存储的类名为CipherA,如果需要使用CipherB的encrypt()方法,则配置文件中存储的类名为CipherB。

  • 如果需要增加一个新的加密类﹐如CipherC,则可将CipherC继承CipherA或CipherB,并覆盖其中定义的encrypt()方法,并将配置文件中存储的类名改为CipherC,所有现有类的代码无须做任何改变,完全符合开闭原则。


依赖倒转原则

  • 如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是实现面向对象设计的主要机制。依赖倒转原则是系统抽象化的具体实现。

依赖倒转原则定义

  • 高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

  • 另一种表述:要针对接口编程,不要针对实现编程。

依赖倒转原则分析

  • 简单来说,依赖倒转原则就是指:代码要依赖于抽象的类,而不要依赖于具体的类﹔要针对接口或抽象类编程,而不是针对具体类编程。也就是说,在程序代码中传递参数时或在组合聚合关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明,参数类型声明,方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口和抽象类中声明过的方法,而不要给出多余的方法。否则将无法调用到在子类中增加的新方法。

  • 实现开闭原则的关键是抽象化,并且从抽象化导出具体化实现,如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要手段。有了抽象层,可以使得系统具有很好的灵活性,在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需要扩展抽象层,并修改配置文件,而无须修改原有系统的源代码,在不修改的情况下来扩展系统的功能,满足开闭原则的要求。依赖倒转原则是COM,CORBA,EJB、Spring 等技术和框架背后的基本原则之一。

下面简单介绍一下依赖倒转原则中经常提到的两个概念,类之间的耦合依赖注入

1.类之间的耦合

在面向对象系统中,两个类之间通常可以发生三种不同的耦合关系(依赖关系)。

  1. 零耦合关系:如果两个类之间没有任何耦合关系,称为零耦合。

  2. 具体耦合关系:具体耦合发生在两个具体类(可实例化的类)之间,由一个类对另一个具体类实例的直接引用产生。

  3. 抽象耦合关系:抽象耦合关系发生在一个具体类和一个抽象类之间,也可以发生在两个抽象类之间,使两个发生关系的类之间存有最大的灵活性。由于在抽象耦合中至少有一端是抽象的,因此可以通过不同的具体实现来进行扩展。

依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。

由于一个抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以替换成其子类,因此,里氏代换原则是依赖倒转原则的基础。

2.依赖注入

  • 对象与对象之间的依赖关系是可以传递的,通过传递依赖,在一个对象中可以调用另一个对象的方法,在传递时要做好抽象依赖,针对抽象层编程。简单来说,依赖注入就是将一个类的对象传入另一个类,注入时应该尽量注入父类对象,而在程序运行时再通过子类对象来覆盖父类对象。依赖注入有以下三种方式。

1.构造注入

  • 构造注入是通过构造函数注入实例变量,代码如下:

public interface AbstractBook

{

public void view();

}

public interface AbstractReader

{

public void read();

}

public class ConcreteBook implements AbstractBook

{

public void view(){

}

}

public class ConcreteReader implements AbstractReader

{

private AbstractBook book;

public ConcreteReader(AbstractBook book){

this.book = book;

}

public void read(){

book.view();

}

}

2.设值注入

  • 设值注入是通过Setter方法注入实例变量,代码如下:

public interface AbstractBook

{

public void view();

}

public interface AbstractReader

{

public void setBook(AbstractBook book);

public void read();

}

public class ConcreteBoak implements AbstractBook

{

public void view(){

}

}

public class ConcreteReader implements AbstractReader

{

private AbstractBook book;

public void setBook(AbstractBook book){

this.book = book;

}

public void read(){

book.view();

}

}

2.接口注入

  • 接口注入是通过接口方法注人实例变量,代码如下:

public interface AbstractBook

{

public void view();

}

public interface AbstractReader

{

public void view(){

}

}

public class ConcretReader implements AbstractReader

{

public void read(AbstractBook book){

book.view();

}

}

依赖倒转实例

  • 下面通过一个简单实例来加深对依赖倒转原则的理解。

实例说明

  • 某系统提供一个数据转换模块,可以将来自不同数据源的数据转换成多种格式,如可以转换来自数据库的数据(DatabaseSource),也可以转换来自文本文件的数据(TextSource),转换后的格式可以是XMI文件(XMI.Transformer),也可以是XL.S文件( XLSTransformer)等。

某设计人员设计如下原始类图,用于实现该数据转换模块,如下图所示。

在这里插入图片描述

  • 由于需求的变化,该系统可能需要增加新的数据源或者新的文件格式,每增加一个新的类型的数据源或者新的类型的文件格式,客户类 MainClass都需要修改源代码,以便使用新的类,违背了开闭原则。现使用依赖倒转原则对其进行重构。

实例解析

  • 在本实例中,MainClass类针对具体类编程,如果增加新的具体类必须修改 MainClass类的源代码,系统的可扩展性和灵活性受到局限﹐因此可以对这些具体类进行抽象化,使得 MainClass类针对抽象层进行编程,而将具体类放在配置文件中,重构后的系统类图如下图所示。

在这里插入图片描述

  • 在上图中,引入了两个抽象类(或接口)AbstractSource和AbstractTransformer,MainClass依赖于这两个抽象类,针对抽象类进行编程,而将具体类类名存储在配置文件config.xml中,通过XML解析技术和Java反射机制生成具体类的实例,代换 MainClass类中的抽象对象,实现真正的业务处理。在这个过程中使用了里氏代换原则,依赖倒转原则必须以里氏代换原则为基础。增加新的数据源或文件格式时,只需要增加一个AbstractSource或 AbstractTransformer类的子类,同时修改config.xml 配置文件,更换具体类类名,无须对原有类的代码进行任何修改,满足开闭原则的要求。

接口隔离原则

  • 接口隔离原则要求我们将一些较大的接口进行细化,使用多个专门的接口来替换单一的总接口。

接口隔离原则的定义

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

  • 注意,在该定义中的接口指的是所定义的方法。

  • 另一种定义,一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。

接口隔离原则分析

  • 实质上,接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。这里的“接口”往往有两种不同的含义:一种是指一个类型所具有的方法特征的集合,仅仅是一种逻辑上的抽象﹔另外一种是指某种语言具体的“接口”定义,有严格的定义和结构,如Java语言里面的interface。对于这两种不同的含﹐ISP的表达方式以及含义都有所不同。

  • 当把“接口”理解成一个类型所提供的所有方法特征的集合的时候,这就是一种逻辑上的概念,接口的划分将直接带来类型的划分。此时,可以把接口理解成角色,一个接口就只代表一个角色,每个角色都有它特定的一个接口,此时这个原则可以叫做“角色隔离原则”。

  • 如果把“接口”理解成狭义的特定语言的接口,那么ISP表达的意思是指接口仅仅提供客户端需要的行为,即所需的方法,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。在面向对象编程语言中,如果需要实现一个接口,就需要实现该接口中定义的所有方法,因此大的总接口使用起来不一定很方便。为了使接口的职责单一,需要将大接口中的方法根据其职责不同分别放在不同的小接口中,以确保每个接口使用起来都较为方便,并都承担某一单一角色。接口应该尽量细化,同时接口中的方法应该尽量少,每个接口中只包含一个客户端(如子模块或业务逻辑类)所需的方法即可。

  • 使用接口隔离原则拆分接口时,首先必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下﹐接口中的方法越少越好。可以在进行系统设计时采用定制服务的方式,即为不同的客户端提供宽窄不同的接口,只提供用户需要的行为,而隐藏用户不需要的行为。

接口隔离原则实例

  • 下面通过一个简单的实例来加深对接口隔离原则的理解。

实例说明

  • 下图展示了一个拥有多个客户类的系统,在系统中定义了一个巨大的接口AbstractService来服务所有的客户类。

在这里插入图片描述

  • 如果客户类ClientA只须针对方法 operatorA()进行编程,但由于提供的是一个胖接口,AbstractService的实现类ConcreteService必须实现在AbstractService中声明的所有三个方法,而且在ClientA中除了能够看到方法 operatorA() ,还能够看到与之不相关的方法operatorB()和 operatorC() ,在一定程度上影响系统的封装性。因此,可以使用接口隔离原则对其进行重构。

实例解析

  • 由于在接口AbstractService中三个不同的方法分别对应三类不同的客户端,因此需要将该接口进行细化,以确保每一类用户都具有与之对应的专门的接口,可以将该接口分割成三个小接口,如图下图所示。

在这里插入图片描述

  • 通过对AbstractService接口的细化,我们可以将其分割为三个专门的接口;AbstractServiceA ,A bstractServiceB和AbstractServiceC,在每个接口中只包含一个方法,用于对应一个客户端。在实际使用过程中,如果一个客户端对应多个方法,可以将这几个方法封装在同一个小接口中。接口实现类ConcreteService可以一次性实现这三个接口,也可以提供三个接口实现类分别实现这三个接口。无论是使用一个实现类还是使用三个实现类,对于ClientA等客户端类而言没有任何区别,因为它们是针对抽象的接口编程,只能看到与自己相关的业务方法,不能访问其他方法,因此保证系统具有良好的封装性。同时,无须关心一个业务方法的改变会给一些不相关的类造成影响,因为这些类根本无法访问该方法。

  • 在使用接口隔离原则时需要注意接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护,接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可。


合成复用原则

  • 合成复用原则是面向对象设计中非常重要的一条原则。为了降低系统中类之间的耦合度,该原则倡导在复用功能时多用关联关系,少用继承关系。

合成复用原则定义

  • 合成复用原则(Composite Reuse Principle,CRP)又称为组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP),其定义为:尽量使用对象组合,而不是继承来达到复用的目的。

合成复用原则分析

  • GoF提倡在实现复用时更多考虑用对象组合机制,而不是用类继承机制。通俗地说,合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分﹔新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之,要尽量使用组合/聚合关系,少用继承。

在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承,这两种复用机制的特点如下:

  1. 通过继承来实现复用很简单,而且子类可以覆盖父类的方法,易于扩展。但其主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的某些内部细节对子类来说是可见的,所以这种复用又称为“白箱”复用。如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;而且继承只能在有限的环境中使用(例如类不能被声明为final类)。

  2. 通过组合/聚合来复用是将一个类的对象作为另一个类的对象的一部分,或者说一个对象是由另一个或几个对象组合而成。由于组合或聚合关系可以将已有的对象(也可称为成员对象)纳人到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象是不可见的,所以这种复用又称为“黑箱”复用。相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作﹔合成复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。

  • 组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用,其次才考虑继承。在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

合成复用原则实例

  • 下面通过一个简单实例来加深对合成复用原则的理解。

实例说明

  • 某教学管理系统的部分数据库访问类设计如下图所示。

在这里插入图片描述

  • 在该类图中, DBUtil类用于连接数据库,它提供了一个 getConnection()方法,用于返回一个Connection类型的数据库连接对象。由于在StudentDAO、TeacherDAO等类中都需要连接数据库,因此需要复用getConnection()方法,在本设计方案中, StudentDAO、TeacherDAO等数据访问类直接继承 DBUtil 类,复用其中定义的方法。

  • 如果需要更换数据库连接方式,如原来采用JDBC连接数据库,现在采用数据库连接池连接,则需要修改 DBUtil类源代码。如果StudentDAO采用JDEC连接,但是TeacherDAO采用连接池连接,则需要增加一个新的 DBUtil类,并修改StudentDAO或TeacherDAO的源代码,使之继承新的数据库连接类,这将违背开闭原则,系统扩展性较差。

下面使用合成复用原则对其进行重构

实例解析

  • 根据合成复用原则,我们可以使用组合/聚合复用来取代继承复用,如下图所示。

在这里插入图片描述

  • StudentDAO和TeacherDAO类与DBUril 类不再是继承关系,而改为聚合关联关系,并增加一个setDBOperator()方法来给DBUtil类型的成员变量dBOperator赋值。如果需要改为另一种数据库连接方式,只需要给DBUtil 增加一个子类,如NewDBUtil,在该子类中覆盖getConnection()方法,再在客户类中调用setDBOperator()方法时注入子类对象即可。如果希望系统更加灵活一点,可以在客户类中针对 DBUtil编程,而将具体类类名存储在配置文件中,DBUtil类及其子类都可以直接应用于该系统。用户无须修改任何源代码,只需修改配置文件即可完成新的数据库连接方式的使用,完全符合开闭原则。

迪米特法则

  • 迪米特法则用于降低系统的耦合度,使类与类之间保持松散的耦合关系。

迪米特法则定义

迪米特法则(Law of Demeter,LoD)又称为最少知识原则(Least Knowledge Principle,LKP),它有多种定义方法,其中几种典型定义如下:

  1. 不要和 “陌生人” 说话。

  2. 只与你的直接朋友通信。

  3. 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

迪米特法则分析

  • 简单地说,迪米特法则就是指一个软件实体应当尽可能少地与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少地影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。

在迪米特法则中,对于一个对象,其朋友包括以下几类:

  1. 当前对象本身 this ;

  2. 以参数形式传入到当前对象方法中的对象;

  3. 当前对象的成员对象;

  4. 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;

  5. 当前对象所创建的对象。

任何一个对象如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。迪米特法则可分为狭义法则和广义法则。在狭义的迪米特法则中,如果两个类之间不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用,如图下图所示。

在这里插入图片描述

  • 在上图中,Object A 与Object B存在依赖关系,ObjectC是Object B的成员对象,根据迪米特法则,Object A只能调用ObjectB中的方法,而不允许调用Object C中的方法,因为它们之间不存在直接引用关系。根据迪米特法则,不允许出现a.method1(). method2()或者a. b. method()这样的调用方式,只允许出现 a. method(),也就是在方法调用时只能够出现一个“.”(点号)。

  • 狭义的迪米特法则可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。

  • 广义的迪米特法则就是指对对象之间的信息流量﹑流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化,使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。

迪米特法则的主要用途在于控制信息的过载。在将迪米特法则运用到系统设计中时,要注意下面的几点:

  1. 在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低﹐就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及。

  2. 在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限。

  3. 在类的设计上,只要有可能,一个类型应当设计成不变类。

  4. 在对其他类的引用上,一个对象对其他对象的引用应当降到最低。

迪米特法则实例

  • 下面通过一个简单实例来加深对迪米特法则的理解。

实例说明

  • 某系统界面类(如Form1 ,Form2等类)与数据访问类(如DAO1,DAO2等类)之间的调用关系较为复杂,如下图所示。

在这里插入图片描述

  • 由于存在复杂的调用关系,将导致系统的耦合度非常大,重用现有类比较困难,增加新的界面类或数据访问类也比较麻烦。现需要降低界面类和业务逻辑类之间的耦合度,可使用迪米特法则对系统进行重构。

实例解析

  • 为了降低界面类与数据访问类之间的耦合度,可以在它们之间引人一系列控制类(如Controller1,Controller2等类),由控制类来负责控制界面类对业务逻辑类的访问,重构之后的类图如下图所示。

在这里插入图片描述

  • 在重构过的上图中,由于控制类的引入,界面类与数据访问类之间不存在直接引用关系。如果增加一个新的界面类如Form6,需要引用DAO2,DAO3和DAO4,原来需要建立三个引用关系,而有了控制类后,只需要直接引用控制类Controller2即可。如果需要增加新的数据访问类,可以对应增加新的控制类或者修改现有控制类,无须修改原有界面类。系统具有较好的灵活性,且可以很方便地重用现有的界面类和数据访问类。

最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

WJyYXJ5,size_20,color_FFFFFF,t_70,g_se,x_16)

  • 在重构过的上图中,由于控制类的引入,界面类与数据访问类之间不存在直接引用关系。如果增加一个新的界面类如Form6,需要引用DAO2,DAO3和DAO4,原来需要建立三个引用关系,而有了控制类后,只需要直接引用控制类Controller2即可。如果需要增加新的数据访问类,可以对应增加新的控制类或者修改现有控制类,无须修改原有界面类。系统具有较好的灵活性,且可以很方便地重用现有的界面类和数据访问类。

最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-ydBwfcb2-1713684003042)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值