2022面试题

150 阅读13分钟

1.equals 与 hashCode ?

因为重写的equal()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成
一个hash值进行比较就可以了,效率很高

因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题
),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出:

     1.equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。

     2.hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。

2.Java接口成员变量和方法默认修饰符(缺省属性)

Java的interface接口中,定义成员时由于默认修饰符自动省略,因此,默认修饰符也称为缺省属性。

成员变量默认修饰符为: public static final
成员方法默认修饰符为: public abstract

1、成员变量
从默认修饰符 “public static final” 中可以看出,在接口定义成员变量时,成员变量为最终静态变量,不能被实例化为对象【没有对象】,并且必须赋值,在外面访问该成员变量时无法修改该成员变量的值。

例如:

public static final String name = “张三”;
String name = “张三”;
以上两种定义方式实际上是相等,在实际开发中我们通常不书写 “public static final” ,选择第二种被默认省略方式。

2、成员方法
从默认修饰符 “public abstract” 中可以看出,在接口定义成员方法时,成员方法不能有方法体,且限制接口不能创建对象,这就要求遵从该接口的类必须实现该接口的成员方法。

(注:在Java中被abstract关键字修饰的类称为抽象类,被abstract关键字修饰的方法称为抽象方法,抽象方法只有方法的声明,没有方法体。)

例如:

public abstract vod testMethod();
void testMethod();
以上两种定义方式实际上是相等,在实际开发中我们通常不书写 “public abstract” ,选择第二种被默认省略方式。
总结
接口只是对一类事物属性和行为的更高次抽象;对修改关闭,对扩展开放,可以说是java中开闭原则的一种体现吧。

简单的理解,定义接口的目的是为了解决Java无法使用多继承的一种手段,也可以把接口理解成是一种特殊的 abstract 类 。

3.软件开发中的SOA理念(哲学)

OA在百度百科的定义如下:面向服务的体系结构(Service-Oriented Architecture,SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以使用一种统一和通用的方式进行交互。

  这样也许太国羽理论化,有一篇文章对此做了形象的比喻:

  

玩乐高玩具,体会SOA概念

  SOA的理念与乐高玩具的设计思路很相似,这是最早的一个关于SOA的成功比喻。

  传统的应用好比是普通的玩具,不可拆卸和拼接。而乐高玩具与众不同,可以按照用户自己的想法随意组装,就是因为它是由标准的微小的组件构成。

  基于SOA的应用都是由更小的服务组件组成的,如同乐高玩具的模块;用乐高玩具可以搭建各种不同的形状,就好比SOA架构可以实现不同的应用;乐高玩具的模块式是基于标准化的,因此可以反复利用,SOA架构也是这样。

  这个比喻的好处,是能很快帮助非技术人员在头脑中建立形象的概念,在一个研讨会里面,组织者发了一些乐高玩具的模块让客户们自己做出一些东西来。由此很快让听众明白,他们就是在做和软件开发类似的事情。

  也许有人会说,SOA那么复杂,用小孩玩具来比喻是否太浅显了,这时,进入深层次探讨的机会来了。

  看上去,乐高玩具这么简单,似乎没什么深奥的,但其实这背后隐含了一种设计哲学。设计乐高玩具的团队都是一群拥有博士的设计专家,他们必须解决的一个矛盾是,如何把标准,松耦合、模块的功能以及力学等要素在设计和规划的时候统统解决,留给使用者的,只是纯粹玩的乐趣。一个有趣的事实是,乐高玩具这样一个看上去如此简单的东西一直没有模仿者。

  通过这个比喻,大家可以在10分钟内就明白SOA是怎样一回事,就像所有人看到乐高玩具时就马上能找到玩法一样。然而,当一个人从设计者的角度来理解乐高和SOA类似的设计哲学时就会发现,两者的服务对象其实正是不需要关心技术的人,而且,他们都是为技术买单的人。

  工程师的位置,到底是个玩家,还是设计者?作为玩家,工程师懂得似乎多了一点,作为设计者,他还有太多东西要学,就像要成为乐高玩具的设计者一样。

看看电视与电脑的区别,理解SOA的特点

  修电视比修电脑难多了,虽然电脑比电视要复杂多了。SOA就像电脑,结构复杂,但容易管理、容易维护、容易升级。

  电脑有一个基于标准的结构——总线结构,还遵循即插即用的契约标准,这些IT界的流行语塑造了PC行业。SOA也拥有几个标准的参考结构,总线,松耦合这些理念和PC行业的基本理念完全相同。

  看一看身边那台可以随时拆开修理、更换显卡、升级内存的台式机,任何一个同行都能马上体会到具有相同理念和优势的SOA的好处了。

看丽江古城的演变,明白SOA实施的模式

  技术总是跳跃的,应用系统永远是中庸的。应用在这个例子里面被比喻为普通的建筑物,而作为企业架构的SOA实施则被比喻成为一座城市。

  城市的演进必然是循序渐进的,这比喻了SOA的实施是一个旅程。

  正面的例子的是丽江古城,这座规划于八百年前的城市,现在仍然生机勃勃,因此被称为“活的古城”。用一条河流分成不同水道流经整个古城,每家门前都有流水声,可以用水洗衣,做饭,所有的街道按照经络学说布置,所有的街区都能享受到同一基础设施上的服务;最有代表性的的是,作为市场的四方街,采用了自动水冲洗设施。由于古城地势东高西低,四方街是中间高,边缘低,因此每天人们用挡板截流水,当水位较高时就可以自动冲洗街面了。这种设计,据说,阿姆斯特丹也有一个,只是不知道哪一个更早。

  可以说,丽江古城的设计是面向服务的,水就是他们的公共服务。他们还通过一些规则来规范水的使用,比如在以前,清晨的水在同一时间,所有人可以用来做饭,下午的某个时间,所有人都可以洗衣服。这就是SOA里面讲的“服务契约”,“管控规则”,“基础设施优化”等理念都可以看到。

  而反面的案例可以看看北京的交通。北京的交通结构有点像传统的架构,无论增加多少条高架和道路,总是难以解决拥堵的问题,这是因为几个环路的设计实际上等同于EAI的办法,总会造成总线拥塞。如果把北京城区的许多大院打散成为更小的社区,类似于将应用的颗粒度变小,就象旧金山那样,其实可以做到很多地方至少不会堵死.然而这个改造需要循序渐进的实施方式。

  想想,为了奥运会,北京的地铁花了多少时间来与地下的管道打交道,我们就知道,企业应用架构的改造有多复杂,也就明白,如果一开始就采用SOA架构有多重要了!

 接触了这么多的软件设计理念,其实,一一想来,要在真正的实际应用中,都能实践,才是真正的王道啊!路途遥远,要加足马力啊!
 

4 扩展枚举值与自定义构造函数**

之前,我们讨论枚举类时,主要是针对最简单的枚举类型。每个枚举值只有一个字符串,如:

但是实际使用中,我们可能想给每个枚举值赋予更多的含义,例如,给每个季节一个中文说明和编码等。

即实现:

\

那这样的操作是可以的么?答案是肯定的!可以操作。但是,因为最简单的枚举类型调用了默认的构造方法,如果我们要增加新的含义,则需要自己覆盖原来的构造方法。操作如下:

\

我们在枚举类中增加了name/code两个属性,并重新编写了构造方法。实现了我们的要求。

我们编写测试函数:

得到如下结果:

5.领域模型命名规约

不知道大家在自己的系统中发现很多类都是以VO、DTO、DO结尾的类,这些存储数据的简单类,有什么作用呢?

我理解有二个作用:

一般稍大的系统都是分层设计的,最底层是数据存储层,数据库,最上层是对外提供接口调用的应用层,那每一层都有关联的数据对象,所以需要做相应的区分。 让类语义更明确,很容易知道类的含义。 定义 先来看看阿里巴巴开发规约的定义:

POJO(Plain Ordinary Java Object):在本规约中,POJO专指只有setter/getter/toString的简单类,包括DO/DTO/BO/VO等。

【参考】分层领域模型规约:

DO(Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象。 DTO(Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。 BO(Business Object):业务对象。可以由Service层输出的封装业务逻辑的对象。 Query:数据查询对象,各层接收上层的查询请求。额外规定:【强制】超过2个参数的查询封装,禁止使用Map类来传输。 VO(View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。 —《阿里巴巴Java开发规约》

详细各个词的定义:

DAO: Data Acess Object,数据访问对象,这个我们不陌生,跟数据库打交道的系统都会有这样的DAO类,主要的作用:

封装对数据库的访问,常规的增删改查(CRUD操作)都通过DAO来实现。 PO/DO: Persistent Object / Data Object,持久对象 / 数据对象。

跟数据库表是一一对应的,一个PO/DO 数据是表的一条记录。

以前用Hibernate 的时候PO用的很多,现在普遍喜欢用 DO,这个看各个公司自己的规范。

PO / DO 只是数据的对象,不包含任何的操作。举个例子,学生表是StudentDO,对学生表的增删改查等操作就是StudentDAO。

DTO:Data Transfer Object,数据传输对象

在分布式系统中,系统之间可以通过DTO进行数据传输; DTO也可以在应用内部,核心层和应用层之间传递数据,DTO只是简单的数据传输,没有业务逻辑的处理; 有的场景,比如数据库表有10个字段,id(唯一id)、version(版本号),gmt_create(记录创建时间) 这些字段不需要对外提供,所以DTO 可以只取有含义的业务字段,DO是和数据库记录的一一映射,但是DTO只需要按照业务需要定义需要的字段。 BO : Business Object, 业务对象

BO主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。

在领域模型中,BO是个非常重要的概念, BO是最小的业务单元。

BO 包含数据对象(PO/DO)以及对数据的操作。

VO: View Object, 视图模型,展示层对象

对应页面显示(web页面/移动端H5/Native视图)的数据对象。

举个例子,DTO 中时间Date格式,或者是 yyyyMMddHHmmss的字符串,但是VO需要的是前端展示的格式,需要转成”yyyy年MM月dd月"; 实践 讲了这么多概念,我们再看下实操,先说命名:

领域模型命名规约 1) 数据对象:xxxDO,xxx即为数据表名。 2) 数据传输对象:xxxDTO,xxx为业务领域相关的名称。 3) 展示对象:xxxVO,xxx一般为网页名称。 4) POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO

再以学生档案管理系统为例,我们现在需要做个学生档案管理的后台系统,按照上面的定义,我们能定义出以下一些对象:

如下图所示:

DB层这里应该叫存储层,这里只列了DB,还有类似HBase、Redis,泛指储存数据。

以Student表为例,DB层负责存储。

DAO层提供了查询、删除、写入的接口;

DO 就是DAO操作的对象,这里是StudentDO,有时候也会省略DO,直接写Student;

service层或core层是做业务逻辑处理的,比如查询接口,根据学生学号调用DAO层获取Student信息,之后做一次数据裁剪,只取业务字段,例如版本号、自增id、数据库记录创建时间等非业务字段不取,获取一个StudentDTO,然后查询学生档案相关的ProfileDTO,组装成ProfileBO,作为档案领域模型;

业务层从service 层拿到这个BO,对BO做一个视图转化,转成VO视图对象,提供给前端负责展示。

大学刚毕业实习那会写了一个业务层直接调用DAO层的代码,觉得中间要经过服务层、核心层过于繁琐,字段都差不多,但是后面被Team Leader 指导了。

VO(视图) 和 DO(数据模型)都可能会随着需求变化,软件设计的原则是降低耦合,一杆到底这种设计就是强耦合(把视图和数据直接绑定),DO变化的时候因为DTO(数据传输对象)、BO(领域模型)的存在,不需要修改VO,VO修改也同样不需要修改DO数据模型。

链接:https://blog.csdn.net/zhengwangzw/article/details/113826425