一个Java小白的面试之旅总结

前言

今天去了一家国内领先的可视化智能硬件公司面试。面试的我是技术总监。为人和蔼,和他交谈中,我还有一股紧张。面试中,能感觉他功力深厚,同时也学到了很多东西。个人感觉,我对自己的面试结果不是很满意。技术总监问的问题比较深入,也是我平时比较疏忽的知识点。


关于Int类型的理解

面试官问我int类型占几个字节。我是这样说的: "占4个字节,在内存中占32位。可能不同的操作系统占的字节不一样。" 我真的是强行装逼,给自己挖坑。面试官说:"为什么不一样"。 然后我说:"我记得博客上面是这样说的。"

可能是面试官说的意思是在Java语言中int类型占几个字节。而我印象中的那篇博客说的是int类型跟OS有关,所以面试的要老老实实回答问题。

  • 操作系统是32位/64位,寻址空间不同。寻址空间一般指的是CPU对于内存寻址的能力。也就是最多用到多少内存的一个问题。32位的CPU一次就能处理4个字节,64位的CPU一次可以处理8个字节。int类型在32位和64位系统中都是4个字节。

  • Java程序不是直接运行在OS上,而是运行在JVM上,JVMclass程序在不同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 这种做法还有其他的问题,就是如果ab都是一个偶数,这样得出的结果不会有影响,这样假设a = 2b = 2,那么算式1算式2得出的结论mid都是等于2。 那么假如a = 1, b = 3算式1得出的结论是2算式2得出的结论是1。显而易见,算式2会造成精度缺失,最后就会导致二分法错误。


字符型

  • 字符型通常用于表示单个的字符,字符型值必须使用单引号括起来,Java语言使用16位的Unicode字符集作为编码方式,而Unicode被设计成支持世界上任何书面语言的字符,包括中文字符,因此Java程序支持各种语言的字符。

表单重复提交

后面,我就不具体讲和面试官的细节了。直接概要出面试官抛出的问题和复盘分析。

我们在添加数据的时候,如果表单重复提交,肯定会造成数据库表的数据重复。

  • 客户端生成token,存在于表单的hidden域,接着把token存入session中。表单提交后,后台比较表单的tokensession中的token,如果相等的话,就表示表单没有重复提交,如果不相等的话,证明表单重复提交。如果表单没有重复提交的话,就把session中的token清空。假如发送100条请求,第一条请求肯定是会通过的,后面的99条请求会因为session中的tokennull,造成请求失败,从而防止了表单重复提交。

  • 当客户端输入的数据提交到后台,后台添加的数据会与数据库表中的数据进行比较,如果不重复则写入,可以防止表单重复提交。如果2个对象相同的话,那么它们的hashCode肯定相同。如果2个对象的hashCode相同,那么它们不一定是相同的对象。重写equals()方法,必须要重写hashCode()方法。

  • 数据库中,增加一个int类型的hash值字段,加上索引。将所有的业务信息(排除主键idcreate_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的速度,这个速度的损耗可以忽略,但是提高查询速度是明显的,另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据的产生

  • 区分度与索引长度的权衡。索引长度越低,索引在内存中占的长度越小,排序越快。然而区分度就很低了,不利于查找。索引长度越长,区分度越高,虽然利于查找,但是索引在内存占的空间就越多了。


数据库三大范式

  • 列名不可再分,保持原子性。
  • 每一个非主属性必须依赖于主键。消除部分函数依赖
  • 除了主键之外,其他属性之间不能相互依赖。消除传递依赖。

尾言

心之所向,素履以往。生如逆旅,一苇以航。

你可能感兴趣的:(一个Java小白的面试之旅总结)