前言
今天去了一家国内领先的可视化智能硬件公司面试。面试的我是技术总监。为人和蔼,和他交谈中,我还有一股紧张。面试中,能感觉他功力深厚,同时也学到了很多东西。个人感觉,我对自己的面试结果不是很满意。技术总监问的问题比较深入,也是我平时比较疏忽的知识点。
关于Int类型的理解
面试官问我int类型占几个字节。我是这样说的: "占4
个字节,在内存中占32
位。可能不同的操作系统占的字节不一样。" 我真的是强行装逼,给自己挖坑。面试官说:"为什么不一样"。 然后我说:"我记得博客上面是这样说的。"
可能是面试官说的意思是在Java
语言中int
类型占几个字节。而我印象中的那篇博客说的是int
类型跟OS
有关,所以面试的要老老实实回答问题。
操作系统是
32
位/64
位,寻址空间不同。寻址空间一般指的是CPU对于内存寻址的能力。也就是最多用到多少内存的一个问题。32
位的CPU
一次就能处理4
个字节,64位的CPU
一次可以处理8
个字节。int
类型在32
位和64
位系统中都是4
个字节。Java
程序不是直接运行在OS
上,而是运行在JVM
上,JVM
将class
程序在不同OS
上的基础类型长度固定,也就是int
类型就是4
个字节。所有平台上的JVM
向上提供给Java
字节码程序的接口完全相同,但向下适应不同平台的接口则互不相同。
Int类型的使用
忘记是我主动抛出Int类型的使用,还是面试官给我抛出的。请原谅我的记性太差了。所以不要强行显摆自己的实力,你在技术总监面前什么都不是。我举的是二分法,寻找中间元素下标的例子。" int mid = (a + b) / 2
。int类型的范围是-2^31 ~ 2^31 - 1
。我是这样说的:a
如果是一个足够大的int
类型数据,b
如果也是一个足够大的int
类型数据。那么a + b
数据的范围肯定超过了int
类型的范围,会造成内存泄露。我的做法是改成 int mid = a + (b - a) / 2
。这样可以避免造成内存的泄露,同时减少了内存的开销。"
说出这个答案,我心中一阵窃喜。然后面试官又给我抛出了这样的问题,“那你为什么不用int mid = a /2 + b/2
"。但是我觉得这个问题还好,不是特别难。我就说: "这种做法的性能没有我的好,因为 a / 2
做了一次运算,然后 b / 2
又做了一次运算,然后把他们加在一起又做了一次运算,内存开销比较大。"
然后面试官说,“这种回答并不能说服我,可能你的做法性能上确实比较好,但是根本原因是内存开销的问题吗?”。当时我就懵了,不知道说什么了。最后面试官告诉了答案:“计算机不擅长做除法运算!”
所有数字在计算机底层都是以二进制存在的。计算机以补码的形式保存所有的整数。计算机不擅长做除法。除法一般都是减法和移位的综合体。
-
Java
支持的位运算符有7
个:-
&
:按位与。当两位同时为1
时才返回1
。 -
|
:按位或。只要有一位为1
即可返回1
。 -
~
:按位非。单目运算符,将操作数的每个位(包括符号位)全部取反。 -
^
:按位异或。当两位相同时返回0
,不同时返回1
。 -
<<
:左移运算符。 -
>>
:右移运算符。 -
>>>
:无符号位右移运算符。比如-5>>>2
,-5
无符号右移动2
位后,左边空出2
位后,空出来的2
位用0
去补充。
-
突然想到
int mid = a / 2 + b / 2
这种做法还有其他的问题,就是如果a
和b
都是一个偶数,这样得出的结果不会有影响,这样假设a = 2
,b = 2
,那么算式1
,算式2
得出的结论mid
都是等于2
。 那么假如a = 1
,b = 3
,算式1
得出的结论是2
,算式2
得出的结论是1
。显而易见,算式2
会造成精度缺失,最后就会导致二分法错误。
字符型
- 字符型通常用于表示单个的字符,字符型值必须使用单引号括起来,
Java
语言使用16
位的Unicode
字符集作为编码方式,而Unicode
被设计成支持世界上任何书面语言的字符,包括中文字符,因此Java
程序支持各种语言的字符。
表单重复提交
后面,我就不具体讲和面试官的细节了。直接概要出面试官抛出的问题和复盘分析。
我们在添加数据的时候,如果表单重复提交,肯定会造成数据库表的数据重复。
客户端生成
token
,存在于表单的hidden
域,接着把token
存入session
中。表单提交后,后台比较表单的token
和session
中的token
,如果相等的话,就表示表单没有重复提交,如果不相等的话,证明表单重复提交。如果表单没有重复提交的话,就把session
中的token
清空。假如发送100
条请求,第一条请求肯定是会通过的,后面的99
条请求会因为session
中的token
为null
,造成请求失败,从而防止了表单重复提交。当客户端输入的数据提交到后台,后台添加的数据会与数据库表中的数据进行比较,如果不重复则写入,可以防止表单重复提交。如果
2
个对象相同的话,那么它们的hashCode
肯定相同。如果2个对象的hashCode
相同,那么它们不一定是相同的对象。重写equals()
方法,必须要重写hashCode()
方法。数据库中,增加一个
int
类型的hash
值字段,加上索引。将所有的业务信息(排除主键id
,create_time
之类的字段)计算hash
值。往表里面添加数据的时候,先计算hash
值,然后用hash
值去数据库中查询,查询结果为多个的时候,可以去做精确比较。
用hashCode()
解决数据重复,是一个不错的选择。
MySQL数据库
基本概念
DML
(Data Manipulation Language
): 数据操作语言,主要由insert
,delete
,update
三个关键字完成。DDL
(Data Definition Language
): 数据定义语言,主要由create
,alter, drop
,truncate
这四个关键字完成。DCL
(Data Control Language
):数据控制语言,主要由grant
(准许)和revoke
(撤销)两个关键字组成。primary key
:PRIMARY KEY(id)
, 有2
个作用,一是约束作用(constraint
),用来规范一个存储主键和唯一性,但是同时也在该key
上建立了一个索引。unique key
:UNIQUE KEY deal_id_uk(deal_id)
,CONSTRAINT deal_id_uk UNIQUE(deal_id)
两种写法都可以,有2
个作用。一是规范数据的唯一性,建立索引。key
: 建立索引。index
:create index deal_id_idx on employees(deal_id)
。index
是数据库的物理结构,索引总是属于数据表,当它和数据表一样都是属于数据库对象。创建索引的唯一作用是加速对表的查询,索引通过使用快速路径访问方法来快速定位数据,从而减少了磁盘的I/O
。
基础概念说完了,怎么去规范的创建数据表呢。再此部分引用阿里巴巴Java
开发手册的的建表规约索引规约。
表达是与否的概率的字段,必须使用
is_xxx
的方式命名,数据类型是unsigned
tinyint
(0-255
),1
代表删除,0
表示未删除。任何字段如果是非负数,必须是unsigned
。char
的长度是固定的,而varchar
的长度是可变的,比如我存一个字符串,叫“cmazxiaoma”
,对于char(20)
来说表示你存储的字符将占20
个字节,其中包括10
个空闲字符,而同样varchar(20)
只会占用10
个字节,20
个字节只是最大值而已。varchar
是可变长字符串,不预先分配空间,长度不要超过5000
,如果存储长度大于此值,定义字段类型为text
,独立出来一张表。用主键去对应,避免影响其他字段的索引效率。字段允许冗余,以提高查询性能,但必须考虑数据的一致,冗余字段应该遵循:不是频繁修改的字段,不是
varchar
超长字段,更不是text
字段。业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。不要以为唯一索引影响
insert
的速度,这个速度的损耗可以忽略,但是提高查询速度是明显的,另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据的产生。区分度与索引长度的权衡。索引长度越低,索引在内存中占的长度越小,排序越快。然而区分度就很低了,不利于查找。索引长度越长,区分度越高,虽然利于查找,但是索引在内存占的空间就越多了。
数据库三大范式
- 列名不可再分,保持原子性。
- 每一个非主属性必须依赖于主键。消除部分函数依赖
- 除了主键之外,其他属性之间不能相互依赖。消除传递依赖。
尾言
心之所向,素履以往。生如逆旅,一苇以航。