Java软件开发技术面试题总结一

Java基础数据类型
byte 8位(1个字节)    short16位       int32位    float32位       long64位   boolean 8位    char16位    double64位

    
一、计算机网络
TCP三次握手过程、参数;
第一次握手:创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 
SYN:同步序列编号(Synchronize Sequence Numbers)
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手.

TCP四次挥手过程、参数;
1)客户端进程发出链接释放报文,而且中止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即便不携带数据,也要消耗一个序号。
2)服务器收到链接释放报文,发出确认报文,ACK=1,ack=u+1,而且带上本身的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,可是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送链接释放报文(在这以前还须要接受服务器发送的最后的数据)。
4)服务器将最后的数据发送完毕后,就向客户端发送链接释放报文,FIN=1,ack=u+1,因为在半关闭状态,服务器极可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5)客户端收到服务器的链接释放报文后,必须发出确认,ACK=1,ack=w+1,而本身的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP链接尚未释放,必须通过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)服务器只要收到了客户端发出的确认,当即进入CLOSED状态。一样,撤销TCB后,就结束了此次的TCP链接。能够看到,服务器结束TCP链接的时间要比客户端早一些。

TCP和UDP的区别?应用场景有何不一样?
首先二者都是传输层的协议: TCP(Transmission Control Protocol),又叫传输控制协议,UDP(User Datagram Protocol),又叫用户数据报协议,它们都是传输层的协议,但二者的机制不一样,它们的区别以下:
面向连接: 面向链接就是传输数据前先创建链接而后再传输数据
非面向链接:就是像ip,直接发送数据包,到达一个节点后找下一条路由,一跳跳的下去
TCP保证可靠,面向链接而UDP不保证可靠,非面向链接,UDP的报头长度远远小于TCP的报头长度。TCP使用了三种基础机制来实现面向链接的服务:1 使用序列号进行标记,以便TCP接收服务在向目的应用传递数据以前修正错序的报文排序;2 TCP使用确认,校验,和定时器系统提供可靠性。3 TCP在应用层数据上附加了一个报头,报头包括序列号字段和这些机制的其余一些必要信息,如叫作端口号的地址字段,该字段能够标识数据的源点和目标应用程序。

TCP阻塞控制;
TCP的拥塞控制由4个核心算法组成:“慢启动”(Slow Start)、“拥塞避免”(Congestion voidance)、“快速重传 ”(Fast Retransmit)、“快速恢复”(Fast Recovery)。
-- 慢启动   
早期开发的TCP应用在启动一个链接时会向网络中发送大量的数据包,这样很容易致使路由器缓存空间耗尽,网络发生拥塞,使得TCP链接的吞吐量急剧降低。因为TCP源端一开始并不知道网络资源当前的利用情况,所以新创建的TCP链接不能一开始就发送大量数据,而只能逐步增长每次发送的数据量,以免上述现象的发生,这里有一个“学习”的过程。   假设client要发送5120字节到server,
慢启动过程以下:   
1.初始状态,cwnd=1,seq_num=1;client发送第一个segment;   
2.server接收到512字节(一个segment),回应ack_num=513;   
3.client接收ack(513),cwnd=1+1=2;如今能够一次发送2个数据段而没必要等待ack   
4.server接收到2个segment,回应ack_num=513+512*2=1537   
5.client接收ack(1537),cwnd=2+1;一次发送3个数据段   
6.server接收到3个segment,回应2个ack,分别为ack_num=1537+1024=2561和ack_num=2561+512=3073   
7.client接收ack(2561)和ack(3073),cwnd=3+2=5;一次能够发送5个数据段,可是只用4个就知足要求了   
8.server接收到4个segment,回应2个ack,分别为4097,5121   9.已经发送5120字节,任务完成!
总结一下: 当创建新的TCP链接时,拥塞窗口(congestion window,cwnd)初始化为一个数据包大小。源端按cwnd大小发送数据,每收到一个ACK确认,cwnd就增长一个数据包发送量。  
-- 拥塞避免   能够想象,若是按上述慢启动的逻辑继续下去而不加任何控制的话,必然会发生拥塞,引入一个慢启动阈值ssthresh的概念,当cwnd<ssthresh的时候,tcp处于慢启动状态,不然,进入拥塞避免阶段。(客户端没接收到一个ack,cwnd就会增长4个字节,超过阀值以后,当即将cwnd降为1,作极端处理)
总结一下: 若是当前cwnd达到慢启动阀值,则试探性的发送一个segment,若是server超时未响应,TCP认为网络能力降低,必须下降慢启动阀值,同时,为了不形势恶化,干脆采起极端措施,把发送窗口降为1,我的感受应该有更好的方法。
-- 快速重传和快速恢复   
前面讲过标准的重传,client会等待RTO时间再重传,但有时候,没必要等这么久也能够判断须要重传,
快速重传
例如:client一次发送8个segment,seq_num起始值为100000,可是因为网络缘由,100512丢失,其余的正常,则server会响应4个ack(100512)(为何呢,tcp会把接收到的其余segment缓存起来,ack_num必须是连续的),这时候,client接收到四个重复的ack,它彻底有理由判断100512丢失,进而重传,而没必要傻等RTO时间了。  
快速恢复
咱们一般认为client接收到3个重复的ack后,就会开始快速重传,可是,若是还有更多的重复ack呢,如何处理?这就是快速恢复要作的,事实上,咱们能够把快速恢复看做是快速重传的后续处理,它不是一种单独存在的形态。   
如下是具体的流程:  
假设此时cwnd=70000,client发送4096字节到server,也就是8个segment,起始seq_num = _100000:   
1.client发送seq_num = _100000   
2.seq_num =100512的segment丢失   
3.client发送seq_num = _101024   
4.server接收到两个segment,它意识到100512丢失,先把收到的这两个segment缓存起来   
5.server回应一个ack(100512),表示它还期待这个segment  
6.client发送seq_num = _101536   
7.server接收到一个segment,它判断不是100512,依旧把收到的这个segment缓存起来,并回应ack(100512)   。   。   。  
 8.如下同六、7,直到client收到3个ack(100512),进入快速重发阶段:   
9.重设慢启动阀值ssthresh=当前cwnd/2=70000/2=35000   
10.client发送seq_num = 100512     如下,进入快速恢复阶段:   
11.重设cwnd = ssthresh + 3 segments =35000 + 3*512 = 36536,之因此要加3,是由于咱们已经接收到3个ack(100512)了,根据前面说的,每接收到一个ack,cwnd加1   
12.client接收到第四个、第五个ack(100512),cwnd=36536+2*512=37560  
 13.server接收到100512,响应ack_num = _104096   14.此时,cwnd>ssthresh,进入拥塞避免阶段。

OSI七层模型、各层所用到的协议;
一些常见协议的原理:ARP、ICMP、FTP等(TCP UDP更不用说啦,必定要了解)
icmp: Internet控制 报文协议,网络层协议(ping命令)
arp:地址解析协议,网络层(地址解析,抓包分析)
tcp:文件传输协议,基于的传输协议是tcp,所以也具备tcp的特性,是处于应用层的协议

二、数据库知识
数据库有哪些索引?原理是什么?
B数索引(B+树):create ind_t on t1(id) ;若是加上unique,则表示该索引中没有重复的值
最经常使用的索引,各叶子节点中包括的数据有索引列的值和数据表中对应行的ROWID,简单的说,在B树索引中,是经过在索引中保存排过续的索引列值与相对应记录的ROWID来实现快速查询的目的。
位图索引:create bitmap index ind_t on t1(type);【 注:位图索引不多是惟一索引,也不能进行键值压缩。】
有些字段中使用B树索引的效率仍然不高,例如性别的字段中,只有“男、女”两个值,则即使使用了B树索引,在进行检索时也将返回接近一半的记录。
索引中再也不记录rowid和键值,而是将每一个值做为一列,用0和1表示该行是否等于该键值(0表示否;1表示是)。
反向键索引:create index ind_t on t1(id) reverse;
反向键索引是一种特殊的B树索引,在存储构造中与B树索引彻底相同,可是针对数值时,反向键索引会先反向每一个键值的字节,而后对反向后的新数据进行索引。例如输入2008则转换为8002,这样当数值一次增长时,其反向键在大小中的分布仍然是比较平均的。

索引有什么做用?有什么特色
这是由于,建立索引能够大大提升系统的性能。 
第一,经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。 
第二,能够大大加快 数据的检索速度,这也是建立索引的最主要的缘由。 
第三,能够加速表和表之间的链接,特别是在实现数据的参考完整性方面特别有意义。 
第四,在使用分组和排序 子句进行数据检索时,一样能够显著减小查询中分组和排序的时间。 
第五,经过使用索引,能够在查询的过程当中,使用优化隐藏器,提升系统的性能。

索引为何用B+树?
一、非叶子节点的子树指针与关键字个数相同; 
二、非叶子节点的子树指针p[i],指向关键字值属于[k[i],k[i+1]]的子树.(B树是开区间,也就是说B树不容许关键字重复,B+树容许重复); 
三、为全部叶子节点增长一个链指针; 
四、全部关键字都在叶子节点出现(稠密索引). (且链表中的关键字刚好是有序的); 
五、非叶子节点至关因而叶子节点的索引(稀疏索引),叶子节点至关因而存储(关键字)数据的数据层; 
六、更适合于文件系统;

B+树和B-树有什么区别?(左b+,右b-)
B树(B-树)
       是一种多路搜索树(并非二叉的):
       1.定义任意非叶子结点最多只有M个儿子;且M>2;
       2.根结点的儿子数为[2, M];
       3.除根结点之外的非叶子结点的儿子数为[M/2, M];
       4.每一个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)
       5.非叶子结点的关键字个数=指向儿子的指针个数-1;
       6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
       7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的
子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
       8.全部叶子结点位于同一层;
       如:(M=3)
对于B+树来讲:
  B+树是B-树的变体,也是一种多路搜索树:
       1.其定义基本与B-树同,除了:
       2.非叶子结点的子树指针与关键字个数相同;
       3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树
(B-树是开区间);
       5.为全部叶子结点增长一个链指针;
       6.全部关键字都在叶子结点出现;
       如:(M=3)

mysql中MyISAM和InnoDB的区别?(两种存储引擎)
每个MyISAM表都对应于硬盘上的三个文件。这三个文件有同样的文件名,可是有不一样的扩展名以指示其类型用途:.frm文件保存表的定义,可是这个文件并非MyISAM引擎的一部分,而是服务器的一部分;.MYD保存表的数据;.MYI是表的索引文件。
InnoDB是MySQL的另外一个存储引擎,目前MySQL AB所发行新版的标准,被包含在全部二进制安装包里,5.5以后做为默认的存储引擎。较之于其它的存储引擎它的优势是它支持兼容ACID的事务(相似于PostgreSQL),以及参数 完整性(即对外键的支持)。
    1>.InnoDB支持事物,而MyISAM不支持事物
    2>.InnoDB支持行级锁,而MyISAM支持表级锁
    3>.InnoDB支持MVCC, 而MyISAM不支持
    4>.InnoDB支持外键,而MyISAM不支持
    5>.InnoDB不支持全文索引,而MyISAM支持。
在使用count进行数据库计数的时候,innodb是要所有的计算一遍,而myisam的效率更快,其中有计算的方法,因此在计数方面myisam更快捷

事务的四大特性(常考)
事物是一条或者多条sql语句组成的执行序列,这个序列中的全部语句都属于同一个工做单元,要么同时完成,其中若是有一个失败,则其余操做都要回滚。
原子性 (Atomicity)
事物是一个不可分割的数据库逻辑工做单位,要么所有完成,要不失败回滚。
一致性 (Consistency)
事务执行的结果必须使数据库从一个一致性状态变到另外一个一致性状态。
隔离性 (Isolation)
一个事物的执行不能被别的并发事物所干扰
持久性 (Durability)
事物一旦提交,其对数据库的改变应该是永久的。

数据库优化的一些策略;
1,表结构优化
     表结构优化是数据库优化中最重要的,须要结合实际状况来看怎么设计更加的优化合理
2,sql语句优化
    *sql语法优化,写出更加便捷的sql语句
     *处理逻辑优化,如配合索引和缓存的使用
      一个常见的作法是,将涉及到大数据量的sql语句记录下来,观察日志,有侧重点的优化
3,分区分表
    分区是指将一张表的数据按照必定的规则分到不一样的区来保存。若一张表中有几种类型,能够考虑分表
    举一个例子,分区按照月份来分,将不一样类型的字段分表,这么作的好处就是增删改查数据的时候范围已经大大缩小了
4,索引优化
  索引的原理是在进行增删改的时候就预先按照指定的字段顺序排列后保存了,在查找的时候就能够从索引找到对应的指针找到数据
  优势:查询效率很高 缺点:每次增删改要更新索引,下降增删改的速度
5,分离活跃数据
   将活跃数据单独存放起来
   好比登陆网站,能够将活跃度高的用户单独存放(依据最近登陆时间或者单位时间内登陆次数等),查询时先从活跃数据查找,没有再去不活跃处查找
6,读写分离
   读写分离的本质是对数据库进行集群,在高并发的状况降低低单台服务器的压力。
  通常将写的服务器叫主服务器,写入后同步到读服务器(即从服务器),并将读请求分配到多个服务器上

增删改查要熟悉,随时可能提一个需求让你写一个SQL语句;
select * from tablename where a=1
insert into tablename(a,b,c) values (1,2,3)/insert into tablename select a,b,c from tablename1
update tablename set a=1 where id =2019
delect from tablename where id=1

数据库索引:汇集索引和非汇集索引的区别?
汇集索引的表中记录的物理顺序与索引的排列顺序一致,优势是查询速度快,由于一旦具备第一个索引值的记录被找到,具备连续索引值的记录也必定物理的紧跟其后。
缺点是对表进行修改速度较慢,这是为了保持表中的记录的物理顺序与索引 的顺序一致,而把记录插入到数据页的相应位置,必须在数据页中进行数据重排,下降了执行速度。在插入新记录时数据文件为了维持 B+Tree 的特性而频繁的 分裂调整,十分低效。
 非汇集索引的记录的物理顺序和索引的顺序不一致。 非汇集索引添加记录时,不会引发数据顺序的重组。

三、编程语言基础(以Java为例)
面向对象的特性?(封装继承多态)如何体现出来?
1) 继承: 继承都是从已有的类获得继承信息 建立新的类的过程.提供信息的类被称为父类(超类,基类),获得信息的类被称为子类(派生类).继承让变化中的软件系统有了必定的延续性,同时继承也是封装程序中可变因素的重要手段.
2) 封装: 一般认为封装是把数据和操做数据的方法绑定起来,对数据的访问只能经过已定义的接口.面向对象的本质就是将现实世界描绘成一系列彻底自治,封闭的对象.咱们在类中编写方法就是对实现细节的一种封装;咱们编写了一个类就是对数据的数据操做的封装,能够说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口
3) 多态性: 多态性是指容许不一样子类型的对象对同一消息做出不一样响应,简单的说就是用一样的对象引用调用一样的对象引用调用一样的方法可是作了不一样的事情.多态性分为编译时多态性和运行时多态性.若是将对象的方法视为对象向外界提供服务,那么运行时多态性能够解释为:当A系统访问B系统提供的服务是,B系统有多种提供服务的方式.但一切对于A系统来讲都是透明的.方法重载(overload) 实现的是编译时的多态性(也称为前绑定), 而方法重写(override) 实现的是运行时的多态性(也成后绑定). 运行时的多态是面向对象最精髓的东西,要实现多态须要作两件事: 1. 方法重写(子类继承父类并重写父类中已有的或抽象方法); 2. 对象造型(用父类型引用引用子类型对象,这样一样的引用调用一样的方法就会根据子类对象的不一样而表现出不一样的行为)

重载和重写有什么区别?
重载: 在一个类中,同名的方法若是有不一样的参数列表(参数类型不一样、参数个数不一样甚至是参数顺序不一样)则视为重载。同时,重载对返回类型没有要求,能够相同也能够不一样,但不能经过返回类型是否相同来判断重载。
重载 总结:
1.重载Overload是一个类中多态性的一种表现
2.重载要求同名方法的参数列表不一样(参数类型,参数个数甚至是参数顺序)
3.重载的时候,返回值类型能够相同也能够不相同。没法以返回类型做为重载函数的区分标准
 重写:从字面上看,重写就是 从新写一遍的意思。其实就是在子类中把父类自己有的方法从新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,因此在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的状况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
重写 总结:
1.发生在父类与子类之间
2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
3.访问修饰符的限制必定要大于被重写方法的访问修饰符(public>protected>default>private)
4.重写方法必定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
面试时,问:重载(Overload)和重写(Override)的区别?
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,然后者实现的是运行时的多态性。重载发生在一个类中,同名的方法若是有不一样的参数列表(参数类型不一样、参数个数不一样或者两者都不一样)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分

集合类有哪些?(常考)
Set、List、Map和Queue4大致系。其中,Set表明无序的、不容许有重复元素的集合,List表明有序的、容许有重复元素的集合,Map表明具备映射关系的集合,Queue表明队列集合。Java集合类主要由2个接口派生过来:Collection接口和Map接口!

Set和List的区别;
List 是可重复集合,list是有序的数据结构
Set 是不可重复集合,set是无序的数据结构
这两个接口都实现了 Collection 父接口.

ArrayList、Linkedlist、Vector区别?(常考,建议看下底层实现代码)
一、Vector、ArrayList都是以相似数组的形式存储在内存中,LinkedList则以链表的形式进行存储。
三、Vector线程同步,ArrayList、LinkedList线程不一样步。
四、LinkedList适合指定位置插入、删除操做,不适合查找;ArrayList、Vector适合查找,不适合指定位置的插入、删除操做。
五、ArrayList在元素填满容器时会自动扩充容器大小的50%,而Vector则是100%,所以ArrayList更节省空间。
数组:能够根据下标快速查找,因此大部分状况下,查询快。可是若是要进行增删操做的时候,会须要移动修改元素后面的全部元素,因此增删的开销比较大,数组的对增删操做的执行效率低。而采用数组做为数据存储结构的ArrayList、Vector也存在这些特性,查询速度快(能够根据下标直接取,比迭代查找更快),增删慢。
链表:增长和删除元素方便,增长或删除一个元素,仅需处理结点间的引用便可。就像人手拉手连成一排,要增长或删除某我的只要附近的两我的换一我的牵手,对已经牵好手的人没影响。不管在哪里换人耗费的资源和时间都是同样的。可是查询不方便,须要一个个对比,没法根据下标直接查找。而采用链表结构存储的LinkedList也有这些特性,增删方便,查询慢(指的是随机查询,不是顺序查询)。

ArrayList如何扩容?(常考)
当数组里面的元素一个一个进行增长的时候,每次进行判断,当新增一个元素以后,数组的长度>初定义的长度的话,那么就建立一个新的数组:
int newCapacity = oldCapacity + oldCapacity / 2,这样就是表示新的数组长度是旧的数组长度的1.5倍,而后用copyof的方法将原来数组的内容复制到新的数组中,完成arraylist的动态扩容

Map下Hashmap、TreeMap的区别?
(1)HashMap:适用于在Map中插入、删除和定位元素。
(2)Treemap:适用于按天然顺序或自定义顺序遍历键(key)。
(3)HashMap一般比TreeMap快一点(树和哈希表的数据结构使然),建议多使用HashMap,在须要排序的Map时候才用TreeMap.
(4)HashMap 非线程安全 TreeMap 非线程安全
(5)HashMap的结果是没有排序的,而TreeMap输出的结果是排好序的。
(6)HashMap:底层是哈希表数据结构, TreeMap:底层是二叉树数据结构

TreeMap底层是什么?红黑树仍是二叉树?
红黑树

Map、List下哪些类是线程安全的?(常考)
map的实现类有:hashtable(线程安全) concurrenthashmap(线程安全)hashmap(不安全)treemap(不安全)linkedhashmap(不安全)weakhashmap(不安全)
list的实现类:vector(线程安全)arraylist(不安全)linkendlist(不安全)

Hashmap的扩容机制;
HashMap使用的是懒加载,构造完HashMap对象后,只要不进行put 方法插入元素以前,HashMap并不会去初始化或者扩容table。
当首次调用put方法时,HashMap会发现table为空而后调用resize方法进行初始化,当添加完元素后,若是HashMap发现size(元素总数)大于threshold(阈值),则会调用resize方法进行扩容
默认的负载因子大小为0.75,当一个map填满了75%的bucket时候,就会扩容,扩容后的table大小变为原来的两倍
从新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫做rehashing

Hashmap如何解决哈希冲突?与HashTable有何不一样?
哈希冲突:通俗的讲就是首先咱们进行一次 put 操做,算出了咱们要在 table 数组的 x 位置放入这个值。那么下次再进行一个 put 操做的时候,又算出了咱们要在 table 数组的 x 位置放入这个值
hashmap解决哈希冲突:首先判断二者的key是否是同样的,由于hashmap是不容许有重复值得,若是二者是重复的,就进行覆盖,不然就利用链地址法,将冲突的节点放在链表的最下面。
若是冲突过多,能够将链表转化为红黑树,时间复杂度是 O(logn)
HashMap和HashTable主要有如下5个方面的区别:
1.继承的父类不一样
  Hashtable继承自Dictionary类,而HashMap继承自
类。但两者都实现了Map接口。
2.对null对象的支持不一样
  HashMap是支持null键和null值的,而HashTable在遇到null时,会抛出NullPointerException异常。这并非由于HashTable有什么特殊的实现层面的缘由致使不能支持null键和null值,这仅仅是由于HashMap在实现时对null作了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket中。
3.容量大小及扩容方式不一样
  HashMap和HashTable都使用哈希表来存储键值对。在数据结构上是基本相同的,都建立了一个继承自Map.Entry的私有的内部类Entry,每个Entry对象表示存储在哈希表中的一个键值对。
Entry对象惟一表示一个键值对,有四个属性:
 -K key 键对象
 -V value 值对象
 -int hash 键对象的hash值
 -Entry entry 指向链表中下一个Entry对象,可为null,表示当前Entry对象在链表尾部。
  HashMap/HashTable内部用Entry数组实现哈希表,而对于映射到同一个哈希桶(数组的同一个位置)的键值对,使用Entry链表来存储。
  HashMap/HashTable初始容量大小和每次扩充容量大小的不一样:HashTable默认的初始大小为11,以后每次扩充为原来的2n+1。HashMap默认的初始化大小为16,以后每次扩充为原来的2倍。若是在建立时给定了初始化大小,那么HashTable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。
4.线程安全性不一样
  HashTable是同步的(缘由:公开的方法好比get都使用了synchronized描述符。而遍历视图好比keySet都使用了Collections.synchronizedXXX进行了同步包装),HashMap不是,也就是说HashTable在多线程使用的状况下,不须要作额外的同步,而HashMap则不行。
  因为Hashtable是线程安全的也是synchronized,因此在单线程环境下它比HashMap速度要慢。
  若是要保持线程安全能够选用ConcurrentHashMap,ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅须要锁定map的某个部分,而其它的线程不须要等到迭代完成才能访问map。Hashtable则会锁定整个map,Hashtable的大小增长到必定的时候,性能会急剧降低,由于迭代时须要被锁定很长的时间。简而言之,在迭代的过程当中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map,ConcurrentHashMap比Hashtable高效。
5.Hash值不一样
  Hashtable计算hash值,直接用key的hashCode(),而HashMap从新计算了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里通常先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的是为了将负的hash值转化为正值,由于hash值有可能为负数,而&0x7FFFFFFF后,只有符号位改变,然后面的位都不变。

 \t 的意思是 横向跳到下一制表符位置(tab键)
 \r 的意思是 回车 
\n 的意思是回车换行。

正则表达式;(服务端岗位常考)

接口跟抽象类的区别?
下面比较-下二者的语法区别:
      1.抽象类能够有构造方法,接口中不能有构造方法。
      2.抽象类中能够有普通成员变量,接口中没有普通成员变量
      3.抽象类中能够包含非抽象的普通方法,接口中的全部方法必须都是抽象的,不能有非抽象的普通方法。
      4.抽象类中的抽象方法的访问类型能够是public , protected和(默认类型,虽然eclipse下不报错,但应该也不行 ) , 但接口中的抽象方法只能是public类型的,而且默认即为public abstract类型。
      5.抽象类中能够包含静态方法,接口中不能包含静态方法
      6.抽象类和接口中均可以包含静态成员变量,抽象类中的静态成员变量的访问类型能够任意,但接口中定义的变星只能是public static final类型,而且默认即为public static final类型。
      7.一个类能够实现多个接口,但只能继承一个抽象类。
抽象(abstract)
public abstract class people {
     public abstract void func1();
     public void func11() {
     }
     public void a() {}
     int a;
     public people() {
           int a=0;
     }
     protected void b() {}
     final void c() {}
     public static void cc() {}
     public void aaa() {
     }
}
package com.example.cache.test;
/**
* 类的抽象abstract
* */
public abstract class test {
public int a;//抽象类能够有普通成员变量
public test(){}//抽象的无参构造
public test(int a){}//抽象的有参构造
private int num=0;//抽象的变量
public int num1=0;
final int num2=0;
public abstract int b();//不写方法主体,必须定义为abstract修饰
public int test1(){return 0;}//抽象的非抽象普通方法
protected int test3(){return 0;}//抽象的非抽象普通方法
private int test4(){return 0;}//抽象的非抽象普通方法,能够是私有的,只是子类在继承的时候,没办法继承这个私有的方法
public final int test2(){return 0;}//抽象类能够写final方法,可是不能够被重写,并且在子类中能够调用这个方法
static int a1=0;//能够有静态的成员变量,静态变量就是独立于其余的变量,和全局变量的区别就是在多个类文件之间进行调用时,全局能够调用,静态不能够
}
 
package com.example.cache.test;
/**
* 类的接口
* */
public interface test1 {
public int a;//接口没有普通成员变量,这样写是会报错的,必需要赋值
public test1(){}//接口没有构造函数,这样写是会报错的
abstract int test1();//接口的方法不能有主体;
int test21();//接口不能使用final和protected进行修饰
static int a1=0;//能够有静态的成员变量,静态变量就是独立于其余的变量,和全局变量的区别就是在多个类文件之间进行调用时,全局能够调用,静态不能够
public int b();//不写方法主体,必须定义为abstract修饰
}

Java能够多继承吗?
java不支持多继承,只支持单继承(即一个类只能有一个父类)。可是java能够实现多个接口,即一个子接口能够有多个父接口。

JVM垃圾回收机制;(常考)
是否符合垃圾回收的标准有两个:
(一)给对象赋予了null,之后再没有使用过;
(二)给对象赋予了新值,从新分配了内存空间。
判断对象是否是须要被回收算法:
一、引用计数法:
给对象添加一个引用计数器,每当有一个地方引用它时,计数器的值就加1;当引用失效时,计数器的值就减1;任什么时候刻计数器为0的对象就是不可能被使用的。 可是在主流的JVM中并无选用引用计数法来管理内存,最主要的缘由是它很难解决对象间互相循环引用的问题
二、可达性分析法:
基本思路是经过一系列称为“GC Roots”的对象做为起始点,从这些节点开始向下搜索,搜索走过的路径称为“引用链”,当一个对象到GC Roots没通路时(也就是从GC Roots到这个对象不可达),则证实此对象是不可用的。
垃圾处理算法:
一、标记-清除算法:
主要分为两个阶段:标记和清除两部分,首先就是标记上全部的须要进行回收的对象,而后在标记完成以后统一将须要进行回收的对象进行回收;
    存在两个问题:1)效率问题(效率不高),2)空间问题,标记清除以后会产生大量的不连续的内存碎片。
二、复制算法:
复制算法的出现是为了解决效率问题,它将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活着的对象复制到另外一块上面,而后再把已使用过的内存空间一次性清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑碎片等复杂状况,只要移动堆顶的指针,按顺序分配内存便可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。代价有点高了。
三、标记-整理算法:
标记过程和标记-清除算法是同样的,将须要进行回收的对象进行标记,而后将这些须要进行回收的对象向一端进行移动,而后清除掉端边界意外的内存
四、分代收集算法:
当前商业虚拟机都采用分代收集算法,将对象存活周期的不一样将内存划分为几块。通常是把Java堆分为新生代和老年代,这样就能够根据各个年代的特色采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死亡,只有少许存活,那就选用复制算法,只须要付出少许存活对象的复制成本就能够完成收集。而老年代中由于对象存活率高,没有额外的空间对它进行分配担保,就必须使用标记 - 清除算法或者标记 - 整理算法来进行回收。

Java中是值传递仍是引用传递?
    值传递

Java中锁机制;
乐观锁: 老是认为不会产生并发问题,每次去取数据的时候总认为不会有其余线程对数据进行修改,所以不会上锁,可是在更新时会判断其余线程在这以前有没有对数据进行修改,通常会使用版本号机制或CAS操做实现。
悲观锁:顾名思义就是采用一种悲观的态度来对待事务并发问题,咱们认为系统中的并发更新会很是频繁,而且事务失败了之后重来的开销很大,这样以来,咱们就须要采用真正意义上的锁来进行实现。悲观锁的基本思想就是每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等之前的事务提交或者回滚解除锁。
重入锁: 重入锁,也叫作递归锁,指的是同一线程 外层函数得到锁以后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock(轻量级) 和synchronized(重量级) 都是 可重入锁
读写锁:相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些。假设你的程序中涉及到对一些共享资源的读和写操做,且写操做没有读操做那么频繁。在没有写操做的时候,两个线程同时读一个资源没有任何问题,因此应该容许多个线程能在同时读取共享资源。可是若是有一个线程想去写这些共享资源,就不该该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就须要一个读/写锁来解决这个问题

Lock的底层怎么实现的?源码怎么写的?
 

sychronized的底层实现?
 

sychronized修饰静态方法和修饰普通方法有什么区别?
 

异常类有哪些实现类、子类?
(1)NullPointerException 当应用程序试图访问空对象时,则抛出该异常。
(2)SQLException 提供关于数据库访问错误或其余错误信息的异常。
(3)IndexOutOfBoundsException指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 
(4)NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
(5)FileNotFoundException当试图打开指定路径名表示的文件失败时,抛出此异常。
(6)IOException当发生某种I/O异常时,抛出此异常。此类是失败或中断的I/O操做生成的异常的通用类。
(7)ClassCastException当试图将对象强制转换为不是实例的子类时,抛出该异常。
(8)ArrayStoreException试图将错误类型的对象存储到一个对象数组时抛出的异常。
(9)IllegalArgumentException 抛出的异常代表向方法传递了一个不合法或不正确的参数。
(10)ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。 
(11)NegativeArraySizeException若是应用程序试图建立大小为负的数组,则抛出该异常。
(12)NoSuchMethodException没法找到某一特定方法时,抛出该异常。
(13)SecurityException由安全管理器抛出的异常,指示存在安全侵犯。
(14)UnsupportedOperationException当不支持请求的操做时,抛出该异常。
(15)RuntimeExceptionRuntimeException 是那些可能在Java虚拟机正常运行期间抛出的异常的超类。

多线程中如何保证线程安全?
1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操做,(atomic,synchronized);
2.可见性:一个线程对主内存的修改能够及时地被其余线程看到,(synchronized,volatile);
3.有序性:一个线程观察其余线程中的指令执行顺序,因为指令重排序,该观察结果通常杂乱无序,(happens-before原则)。
 

多线程有哪些常见的线程安全的类?
Timer,HashTable,StringBuffer,TimerTask,Vector,Stack

如何开启一个线程?
3 /** 4  * @desc 第一种开启线程的方式
5  * @author 
6  *
7  */ 8 public class Demo1_Thread extends Thread {
9
10     public void run() {
11         for (int i = 0; i < 10; i++) {
12             System.out.println(i + " run()...");
13         }
14     }
15
16     public static void main(String[] args) {
17         Demo1_Thread demo = new Demo1_Thread();
18         demo.start();
19         for (int i = 0; i < 10; i++) {
20             System.out.println(i + " main()...");
21         }
22     }
24 }
能够继承thread类,新建一个当前类的对象,而后执行start()方法便可

get请求和post请求有什么区别?
GET产生一个TCP数据包;POST产生两个TCP数据包。
GET比POST更不安全,由于参数直接暴露在URL上,因此不能用来传递敏感信息。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET请求会被浏览器主动cache,而POST不会

反射的原理?
java在执行的时候,前后是经过编译、加载、链接进行的
编译就是java文件编译后生成.class字节码文件
加载就是类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,而后将其转换为一个与目标类型对应的java.lang.Class对象实例
Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操做的。.class文件中包含java类的全部信息,当你不知道某个类具体信息时,能够使用反射获取class,而后进行各类操做。
Java反射就是在运行状态中,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意方法和属性;而且能改变它的属性。总结说:反射就是把java类中的各类成分映射成一个个的Java对象,而且能够进行操做。

ClassLoader和Class.forName()这两个有什么区别?(反射源码的考察)

NIO这一块有什么了解?
 

四、项目框架(以Spring为例)
SSH是 struts+spring+hibernate的一个集成框架
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部份内容)
简述Springmvc的流程;
一、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后本身不进行处理,而是委托给其余的解析器进行处理,做为统一访问点,进行全局的流程控制;
二、 DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,经过这种策略模式,很容易添加新的映射策略;
三、 DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持不少类型的处理器;
四、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
五、 ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,经过这种策略模式,很容易更换其余视图技术;
六、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,所以很容易支持其余视图技术;
七、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

控制反转(ioc):
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为什么是反转(有反转就应该有正转了),哪些方面反转了”,那咱们来深刻分析一下:
  ●谁控制谁,控制什么:传统Java SE程序设计,咱们直接在对象内部经过new进行建立对象,是程序主动去建立依赖对象;而IoC是有专门一个容器来建立这些对象,即由Ioc容器来控制对象的建立;谁控制谁?固然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不仅是对象包括好比文件等)。
  ●为什么是反转,哪些方面反转了:有反转就有正转,传统应用程序是由咱们本身在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙建立及注入依赖对象;为什么是反转?由于由容器帮咱们查找及注入依赖对象,对象只是被动的接受依赖对象,因此是反转;哪些方面反转了?依赖对象的获取被反转了。
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导咱们如何设计出松耦合、更优良的程序。传统应用程序都是由咱们在类内部主动建立依赖对象,从而致使类与类之间高耦合,难于测试;有了IoC容器后,把建立和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,因此对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得很是灵活。
  其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序本来是老大,要获取什么资源都是主动出击,可是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来建立并注入它所须要的资源了。
    IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找咱们,咱们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
依赖注入(DI)
“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并不是为软件系统带来更多功能,而是为了提高组件重用的频率,并为系统搭建一个灵活、可扩展的平台。经过依赖注入机制,咱们只须要经过简单的配置,而无需任何代码就可指定目标须要的资源,完成自身的业务逻辑,而不须要关心具体的资源来自何处,由谁实现。

Spring的核心特性是什么?
Ioc和aop

理解AOP、IoC的基本原理;
AOP:面向切面编程,能够经过预编译方式和运行期动态代理实如今不修改源代码的状况下给程序动态统一添加功能的一种技术。主要功能有日志记录,性能统计,安全控制,事务处理,异常处理等等。主要意图就是把以上功能从业务逻辑代码中分离出来,进而改变这些行为的时候不影响业务逻辑的代码。说白了,就是扩展功能不修改源代码实现。
aop有两种代理方式:
java代理:采用java内置的代理API实现(写接口,设置代理)
cglib代理:采用第三方API实现
二者的区别:
cglib底层运用了asm这个很是强大的Java字节码生成框架来生成class, 比jdk代理效率要高
jdk代理只能代理被接口修饰的类,而cglib没有这个限制
IoC上面有总结:
IoC 在 Spring 里,只须要低级容器就能够实现,2 个步骤:
a. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
b. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,若是有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。

AOP的一些场景应用;
主要在作日志记录,性能统计,安全控制,事务处理,异常处理的时候

Springmvc和Springboot有什么区别?
Spring MVC的功能
Spring MVC提供了一种轻度耦合的方式来开发web应用。
Spring MVC是Spring的一个模块,是一个web框架。经过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。
Spring Boot的功能
Spring Boot实现了自动配置,下降了项目搭建的复杂度。
众所周知Spring框架须要进行大量的配置,Spring Boot引入自动配置的概念,让项目设置变得很容易。Spring Boot自己并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并非用来替代Spring的解决方案,而是和Spring框架紧密结合用于提高Spring开发者体验的工具。同时它集成了大量经常使用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎能够零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只须要很是少许的配置代码,开发者可以更加专一于业务逻辑。
Spring Boot只是承载者,辅助你简化项目搭建过程的。若是承载的是WEB项目,使用Spring MVC做为MVC框架,那么工做流程和你上面描述的是彻底同样的,由于这部分工做是Spring MVC作的而不是Spring Boot。
对使用者来讲,换用Spring Boot之后,项目初始化方法变了,配置文件变了,另外就是不须要单独安装Tomcat这类容器服务器了,maven打出jar包直接跑起来就是个网站,但你最核心的业务逻辑实现与业务流程实现没有任何变化。
因此,用最简练的语言归纳就是:
Spring 是一个“引擎”;
Spring MVC 是基于Spring的一个 MVC 框架 ;
Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。

Springboot为何配置简单?(即它自动作了什么操做才能简化程序员的操做)
有不少的xxxautoconfigration,为咱们配置了不少的自动配置

Spring容器的加载顺序?

@Resource 和 @Autowired 区别?分别用在什么场景?
二者均可以写在字段和setter方法上。二者若是都写在字段上,那么就不须要再写setter方法。
不一样点
(1)@Autowired(spring的注解)
@Autowired为Spring提供的注解,须要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
@Autowired注解是按照类型(byType)装配依赖对象,默认状况下它要求依赖对象必须存在,若是容许null值,能够设置它的required属性为false。若是咱们想使用按照名称(byName)来装配,能够结合@Qualifier注解一块儿使用。以下:
(2)@Resource(Java的注解)
@Resource默认按照ByName自动注入,由J2EE提供,须要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。因此,若是使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。若是既不制定name也不制定type属性,这时将经过反射机制使用byName自动注入策略。

静态代理和动态代理的区别?
静态:由程序员建立代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态建立而成

Hibernate和mybatis的区别?
2.1 开发方面
        在项目开发过程中,就速度而言:
            hibernate开发中,sql语句已经被封装,直接能够使用,加快系统开发;
            Mybatis 属于半自动化,sql须要手工完成,稍微繁琐;
        可是,凡事都不是绝对的,若是对于庞大复杂的系统项目来讲,发杂语句较多,选择hibernate 就不是一个好方案。
    2.2 sql优化方面
        Hibernate 自动生成sql,有些语句较为繁琐,会多消耗一些性能;
        Mybatis 手动编写sql,能够避免不须要的查询,提升系统性能;
    2.3 对象管理比对
        Hibernate 是完整的对象-关系映射的框架,开发工程中,无需过多关注底层实现,只要去管理对象便可;
        Mybatis 须要自行管理 映射关系;
    2.4 缓存方面    
            Hibernate 具备良好的管理机制,用户不须要关注SQL,若是二级缓存出现脏数据,系统会保存,;
           Mybatis 在使用的时候要谨慎,避免缓存CAche 的使用。
 

Hibernate优点

  1. Hibernate的DAO层开发比MyBatis简单,Mybatis须要维护SQL和结果映射。
  2. Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
  3. Hibernate数据库移植性很好,MyBatis的数据库移植性很差,不一样的数据库须要写不一样SQL。
  4. Hibernate有更好的二级缓存机制,能够使用第三方缓存。MyBatis自己提供的缓存机制不佳。

Mybatis优点

  1. MyBatis能够进行更为细致的SQL优化,能够减小查询字段。
  2. MyBatis容易掌握,而Hibernate门槛较高。
 

mybatis是如何工做的?
一、加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。
二、SqlSessionFactoryBuilder经过Configuration对象生成SqlSessionFactory,用来开启SqlSession。
三、SqlSession对象完成和数据库的交互:
a、用户程序调用mybatis接口层api(即Mapper接口中的方法, crud)
b、SqlSession经过调用api的Statement ID找到对应的MappedStatement对象
c、经过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象
d、JDBC执行sql。
e、借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。

Hibernate对象有几个状态值?
Hibernate 把对象分为三种状态:Transient(临时状态)、Persistent(持久化状态)、Detached(游离状态)。
1)Transient:刚刚new出来的对象,就是Transient状态的,此时他没有OID。
2)Persistent:有持久化标志OID,已经被归入到session对象的管理
3) Detached:   --有持久化标志OID,可是没有被归入到Session对象的管理

数据库的锁机制:
数据库锁出现的目的:处理并发问题
并发控制的主要采用的技术手段:乐观锁、悲观锁和时间戳。

锁分类

从数据库系统角度分为三种:排他锁、共享锁、更新锁。 
从程序员角度分为两种:一种是悲观锁,一种乐观锁。

redirect和forward的区别
redirect是间接请求转发: 有时也叫重定向,它通常用于避免用户的非正常访问
            response.sendRedirect("../user-jsp/login.jsp");
forward是直接请求转发: 在请求对象request中,保存的对象对于每一个信息资源是共享的。
           request.getRequestDispatcher("/main.jsp").forward(request, response);

oracle数据库的数据表的交集表现用intersect(从两个表中查一些要合在一块儿的数据)
SELECT CODE FROM EMPLOYEE WHERE GENDER = 'M'
INTERSECT
SELECT CODE FROM SALARY WHERE SALARY > 2500

jsp页面传递参数的几种方法:
一、能够使用a标签进行传递,例如: <a href="show.jsp?name=ss">点击</a> //设置参数和赋值,
而后在后台就能够使用requset.getParameter的方式进行接收:String name=request.getParameter("name")
二、能够用request来传递参数
请求做用域,客户端的一次请求。生命周期为一次请求或使用forward方式执行请求转发,也就是使用forward方式跳转多个jsp,在这些页面里你均可以使用这个变量。可是只要刷新页面,这个变量就失效了。
<body>
    <%request.setCharacterEncoding("UTF-8"); %>
    <%request.setAttribute("name", "李白"); %>
    <jsp:forward page="index.jsp"></jsp:forward>
</body>
而后,在另外进行接收 String name= request.getAttribute("name");
3.能够使用session进行传递参数:
session的做用时间简单的说就是从浏览器打开到浏览器关闭这个过程当中有效。
<%session.setAttribute("name", "李白"); %>
另外进行接收:String name= session.getAttribute("name");
 
4.能够用application进行参数的传递(方法相似session)
appllication是全局做用范围,整个应用程序共享,生命周期为从应用程序启动到中止,在这个过程当中application里的变量一直在累加,除非你重启tomcat或是人工删除,不然它会一直变大。
在一边: <%application.setAttribute("name", "李白"); %>
在另外一边进行接收:String name= application.getAttribute("name");
五、使用jstl的方式,前提是引入jstl的jar包:
在一端:
<c:redirect url="main.jsp"> //负责跳转
<c:param name="user" value="wgh"></c:param> //赋值
</c:redirect>
在另外一端接收:{param.user}]您好,欢迎访问我公司的网

String和StringBuffer的区别
String:是对象不是原始类型.
           为不可变对象,一旦被建立,就不能修改它的值.
           对于已经存在的String对象的修改都是从新建立一个新的对象,而后把新的值保存进去.
           String 是final类,即不能被继承.
StringBuffer:
           是一个可变对象,当对他进行修改的时候不会像String那样从新创建对象
           它只能经过构造函数来创建,
          StringBuffer sb = new StringBuffer();
          对象被创建之后,在内存中就会分配内存空间,并初始保存一个null.经过它的append方法向其赋值.
          sb.append("hello");
字符串链接操做中StringBuffer的效率要明显比String高:
String对象是不可变对象,每次操做String 都会从新创建新的对象来保存新的值.
StringBuffer对象实例化后,只对这一个对象操做。
而且StringBuffer的字符操做效率比String的效率高不少。

删除索引:(下面二者等价)
DROP INDEX index_name ON talbe_name
ALTER TABLE table_name DROP INDEX index_name

创建索引的原则:
  1. 定义主键的数据列必定要创建索引。
  2. 定义有外键的数据列必定要创建索引。
  3. 对于常常查询的数据列最好创建索引。
  4. 对于须要在指定范围内的快速或频繁查询的数据列;
  5. 常常用在WHERE子句中的数据列。
  6. 常常出如今关键字order by、group by、distinct后面的字段,创建索引。若是创建的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,不然索引不会被使用。
  7. 对于那些查询中不多涉及的列,重复值比较多的列不要创建索引。
  8. 对于定义为text、image和bit的数据类型的列不要创建索引。
  9. 对于常常存取的列避免创建索引
  10. 限制表上的索引数目。对一个存在大量更新操做的表,所建索引的数目通常不要超过3个,最多不要超过5个。索引虽然说提升了访问速度,但太多索引会影响数据的更新操做。
  11. 对复合索引,按照字段在查询条件中出现的频度创建索引。在复合索引中,记录首先按照第一个字段排序。对于在第一个字段上取值相同的记录,系统再按照第二个字段的取值排序,以此类推。所以只有复合索引的第一个字段出如今查询条件中,该索引才可能被使用,所以将应用频度高的字段,放置在复合索引的前面,会使系统最大可能地使用此索引,发挥索引的做用。

数据库的外键( foreign key)
数据库的外键建立的必要性:
 一、表的组织结构复杂不清晰
    二、浪费空间
    三、扩展性极差
为了解决上述的问题,就须要用多张表来存放数据。
表与表的记录之间存在着三种关系:一对多、多对多、一对一的关系。
处理表之间关系问题就会利用到FOREIGN KEY
create table dep(
    id int primary key auto_increment,
    dep_name char(10),
    dep_comment char(60)
);
create table emp(
    id int primary key auto_increment,
    name char(16),
    gender enum('male','female') not null default 'male',
    dep_id int,
    foreign key(dep_id) references dep(id)
这样在建立的时候,就能够将emp中的dep_id对应上emp中中的id
使用的语法就是
foreign key(xxxb) references tablename(xxxa)

public/private/protected
一、public:public代表该数据成员、成员函数是对全部用户开放的,全部用户均可以直接进行调用
 
二、private:private表示私有,私有的意思就是除了class本身以外,任何人都不能够直接使用,私有财产神圣不可侵犯嘛,即使是子女,朋友,都不能够使用。
 
三、protected:protected对于子女、朋友来讲,就是public的,能够自由使用,没有任何限制,而对于其余的外部class,protected就变成private。

Java是否存在内存泄漏问题

内存泄漏是指一个再也不被程序使用的对象或变量还在内存中占有存储空间

在java语言中,判断一个内存空间是否符合垃圾回收的标准有两个:

(一)给对象赋予了null,之后再没有使用过;
(二)给对象赋予了新值,从新分配了内存空间。

在java语言中,内存泄漏主要有两种状况:

(一)是在堆中申请的空间没有被释放;
(二)是对象已经再也不被使用,但仍然在内存中保留。
垃圾回收能够解决(一),可是没法保证再也不被使用的对象会被释放。

在java语言中,内存泄漏的缘由不少:

1.静态集合类:例如static HashMap和static Vector,因为它们的生命周期与程序一致,那么容器中的对象在程序结束以前将不能被释放。
2.各类链接:例如数据库链接、网络链接和IO链接等,当再也不使用时,需调用close()方法来释放链接。
3.监听器:在释放对象的同时没有删除相应的监听器。
4.变量不合理的做用域:一个变量定义的做用范围大于其使用范围(例如一个本能够定义为方法内局部变量的变量,却被定义为程序对象内的全局变量),而且在使用完后没有及时地把它设为null。   

 
栈:后进先出,从一端进,从一端出
队列:先进先出,从队尾进,从队头出

几种常见的排序算法总结: http://www.javashuo.com/article/p-dfduwged-ht.html
性能比较:
直接插入排序: 直接插入排序的核心思想就是:将数组中的全部元素依次跟前面已经排好的元素相比较,若是选择的元素比已排序的元素小,则交换,直到所有元素都比较过。
嵌套两层循环:
  1. 第一层循环:遍历待比较的全部数组元素
  2. 第二层循环:将本轮选择的元素(selected)与已经排好序的元素(ordered)相比较。
    若是:selected > ordered,那么将两者交换
#直接插入排序def insert_sort(L):
#遍历数组中的全部元素,其中0号索引元素默认已排序,所以从1开始
for x in range(1,len(L)):
#将该元素与已排序好的前序数组依次比较,若是该元素小,则交换
#range(x-1,-1,-1):从x-1倒序循环到0
for i in range(x-1,-1,-1):
#判断:若是符合条件则交换
if L[i] > L[i+1]:
temp = L[i+1]
L[i+1] = L[i]
L[i] = temp
希尔排序的算法思想:将待排序数组按照步长gap进行分组,而后将每组的元素利用直接插入排序的方法进行排序;每次将gap折半减少,循环上述操做;当gap=1时,利用直接插入,完成排序。(步长gap是本身进行设定的,最后必须是1,而且步长的设定必须小于数组的长度)
嵌套三层循环:
一样的:从上面的描述中咱们能够发现:希尔排序的整体实现应该由三个循环完成:
  1. 第一层循环:将gap依次折半,对序列进行分组,直到gap=1
  2. 第2、三层循环:也即直接插入排序所须要的两次循环。具体描述见上。
def insert_shell(L):
#初始化gap值,此处利用序列长度的通常为其赋值
gap = (int)(len(L)/2)
#第一层循环:依次改变gap值对列表进行分组
while (gap >= 1):
#下面:利用直接插入排序的思想对分组数据进行排序
#range(gap,len(L)):从gap开始
for x in range(gap,len(L)):
#range(x-gap,-1,-gap):从x-gap开始与选定元素开始倒序比较,每一个比较元素之间间隔gap
for i in range(x-gap,-1,-gap):
#若是该组当中两个元素知足交换条件,则进行交换
if L[i] > L[i+gap]:
temp = L[i+gap]
L[i+gap] = L[i]
L[i] =temp
#while循环条件折半
gap = (int)(gap/2)
简单选择排序算法:简单选择排序的基本思想:比较+交换。
  1. 从待排序序列中,找到关键字最小的元素;
  2. 若是最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
  3. 从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。
两层嵌套循环:
第一层循环:依次遍历序列当中的每个元素
第二层循环:将遍历获得的当前元素依次与余下的元素进行比较,符合最小元素的条件,则交换。
# 简单选择排序def select_sort(L):#依次遍历序列中的每个元素
for x in range(0,len(L)):
#将当前位置的元素定义此轮循环当中的最小值
minimum = L[x]
#将该元素与剩下的元素依次比较寻找最小元素
for i in range(x+1,len(L)):
if L[i] < minimum:
temp = L[i];
L[i] = minimum;
minimum = temp
#将比较后获得的真正的最小值赋值给当前位置
L[x] = minimum
堆排序:(堆:本质是一种数组对象。特别重要的一点性质:任意的叶子节点小于(或大于)它全部的父节点。对此,又分为大顶堆和小顶堆,大顶堆要求节点的元素都要大于其孩子,小顶堆要求节点元素都小于其左右孩子,二者对左右孩子的大小关系不作任何要求。利用堆排序,就是基于大顶堆或者小顶堆的一种排序方法。下面,咱们经过大顶堆来实现【大顶堆的根节点必定是最大的数字】)
基本思想:
堆排序能够按照如下步骤来完成:
  1. 首先将序列构建称为大顶堆;
    (这样知足了大顶堆那条性质:位于根节点的元素必定是当前序列的最大值)
  2. 取出当前大顶堆的根节点,将其与序列末尾元素进行交换;
    (此时:序列末尾的元素为已排序的最大值;因为交换了元素,当前位于根节点的堆并不必定知足大顶堆的性质)
  3. 对交换后的n-1个序列元素进行调整,使其知足大顶堆的性质;
  4. 重复2.3步骤,直至堆中只有1个元素为止
(说白了就是每次找一个最大的,而后将它放在最后,而后在剩余的元素中在找最大的,放在后面,依次类推)
冒泡排序:
  1. 将序列当中的左右元素,依次比较,保证右边的元素始终大于左边的元素;
    ( 第一轮结束后,序列最后一个元素必定是当前序列的最大值;)
  2. 对序列当中剩下的n-1个元素再次执行步骤1。
  3. 对于长度为n的序列,一共须要执行n-1轮比较
    (利用while循环能够减小执行次数)
 
#冒泡排序def bubble_sort(L):
length = len(L)
#序列长度为length,须要执行length-1轮交换
for x in range(1,length):
#对于每一轮交换,都将序列当中的左右元素进行比较#每轮交换当中,因为序列最后的元素必定是最大的,所以每轮循环到序列未排序的位置便可
for i in range(0,length-x):
if L[i] > L[i+1]:
temp = L[i]
L[i] = L[i+1]
L[i+1] = temp
快速排序:
快排使用的是分治的思想:将要排序的数列分为两部分,设定一个分解值,将小于等于分解质的数放在左边,大于的放在右边,而后在从左边的数列里面设定一个分解值,而后在进行排序,右边同理,而后在拍完的时候就是从小到大的序列顺序
快速排序的基本思想:挖坑填数+分治法
  1. 从序列当中选择一个基准数(pivot)
    在这里咱们选择序列当中第一个数最为基准数
  2. 将序列当中的全部数依次遍历,比基准数大的位于其右侧,比基准数小的位于其左侧
  3. 重复步骤1.2,直到全部子集当中只有一个元素为止。
    用伪代码描述以下:
    1.i =L; j = R; 将基准数挖出造成第一个坑a[i]。
    2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
    3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
    4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中
#快速排序#L:待排序的序列;start排序的开始index,end序列末尾的index#对于长度为length的序列:start = 0;end = length-1def quick_sort(L,start,end):
if start < end:
i , j , pivot = start , end , L[start]
while i < j:#从右开始向左寻找第一个小于pivot的值
while (i < j) and (L[j] >= pivot):
j = j-1#将小于pivot的值移到左边
if (i < j):
L[i] = L[j]
i = i+1
#从左开始向右寻找第一个大于pivot的值
while (i < j) and (L[i] < pivot):
i = i+1#将大于pivot的值移到右边
if (i < j):
L[j] = L[i]
j = j-1#循环结束后,说明 i=j,此时左边的值全都小于pivot,右边的值全都大于pivot#pivot的位置移动正确,那么此时只需对左右两侧的序列调用此函数进一步排序便可#递归调用函数:依次对左侧序列:从0 ~ i-1//右侧序列:从i+1 ~ end
L[i] = pivot
#左侧序列继续排序
quick_sort(L,start,i-1)
#右侧序列继续排序
quick_sort(L,i+1,end)
 
  1. 归并排序是创建在归并操做上的一种有效的排序算法,该算法是采用分治法的一个典型的应用。它的基本操做是:将已有的子序列合并,达到彻底有序的序列;即先使每一个子序列有序,再使子序列段间有序。
  2. 归并排序其实要作两件事:
  • 分解----将序列每次折半拆分
  • 合并----将划分后的序列段两两排序合并
    所以,归并排序实际上就是两个操做,拆分+合并
  1. 如何合并?
    L[first...mid]为第一段,L[mid+1...last]为第二段,而且两端已经有序,如今咱们要将两端合成达到L[first...last]而且也有序。
  • 首先依次从第一段与第二段中取出元素比较,将较小的元素赋值给temp[]
  • 重复执行上一步,当某一段赋值结束,则将另外一段剩下的元素赋值给temp[]
  • 此时将temp[]中的元素复制给L[],则获得的L[first...last]有序
  1. 如何分解?
    在这里,咱们
# 归并排序#这是合并的函数# 将序列L[first...mid]与序列L[mid+1...last]进行合并def mergearray(L,first,mid,last,temp):#对i,j,k分别进行赋值
i,j,k = first,mid+1,0#当左右两边都有数时进行比较,取较小的数
while (i <= mid) and (j <= last):
if L[i] <= L[j]:
temp[k] = L[i]
i = i+1
k = k+1
else:
temp[k] = L[j]
j = j+1
k = k+1#若是左边序列还有数
while (i <= mid):
temp[k] = L[i]
i = i+1
k = k+1#若是右边序列还有数
while (j <= last):
temp[k] = L[j]
j = j+1
k = k+1#将temp当中该段有序元素赋值给L待排序列使之部分有序
for x in range(0,k):
L[first+x] = temp[x]
# 这是分组的函数def merge_sort(L,first,last,temp):
if first < last:
mid = (int)((first + last) / 2)
#使左边序列有序
merge_sort(L,first,mid,temp)
#使右边序列有序
merge_sort(L,mid+1,last,temp)
#将两个有序序列合并
mergearray(L,first,mid,last,temp)
# 归并排序的函数def merge_sort_array(L):#声明一个长度为len(L)的空列表
temp = len(L)*[None]
#调用归并排序
merge_sort(L,0,len(L)-1,temp)
 

泊松分布: 若X服从参数为λ的泊松分布,则EX=DX=λ ,因此在题目中说”EX=DX“获得的结果 为true

ER图中四种基本成分:实体(矩形框),关系(菱形框),属性(椭圆形框),链接(直线)

Java的try,catch,finally的return问题:
先来看一段代码:
public abstract class Test {
    public static void main(String[] args) {
        System.out.println(beforeFinally());
    }
    public static int beforeFinally(){
        int a = 0;
        try{
            a = 1;
            return a;
        }finally{
            a = 2;
        }
    }
}
/**output:
1
*/
从结果上看,貌似`finally` 里的语句是在`return` 以后执行的,其实否则,实际上`finally` 里的语句是在在`return` 以前执行的。那么问题来了,既然是在以前执行,那为何`a` 的值没有被覆盖了?
实际过程是这样的:当程序执行到try{}语句中的return方法时,它会干这么一件事,将要返回的结果存储到一个临时栈中,而后程序不会当即返回,而是去执行finally{}中的程序, 在执行`a = 2`时,程序仅仅是覆盖了a的值,但不会去更新临时栈中的那个要返回的值 。执行完以后,就会通知主程序“finally的程序执行完毕,能够请求返回了”,这时,就会将临时栈中的值取出来返回。这下应该清楚了,要返回的值是保存至临时栈中的。
再来看一个例子,稍微改下上面的程序:
public abstract class Test {
    public static void main(String[] args) {
        System.out.println(beforeFinally());
    }
     
    public static int beforeFinally(){
        int a = 0;
        try{
            a = 1;
            return a;
        }finally{
            a = 2;
            return a;
        }
    }
}
/**output:
2
*/
在这里,finally{}里也有一个return,那么在执行这个return时,就会更新临时栈中的值。一样,在执行完finally以后,就会通知主程序请求返回了,即将临时栈中的值取出来返回。故返回值是2.

继承初始化问题
java初始化顺序。初始化子类必先初始化父类。子类的构造方法会隐式去调用 父类无参的构造方法(不会在代码中显示)。但若是父类没有无参的构造方法,就必须在子类构造方法第一行显示调用父类的有参构造方法。不然编译失败

折半查找断定( 具备12个关键字的有序表,折半查找的平均查找长度())

  一些推荐的小Tips:
  JVM推荐的书籍:周志明的《深刻了解Java虚拟机》,我以为讲得比较明白比较细,我就看了前一部分已经彻底够应付全部的面试问到的JVM问题了;
Spring书籍:《Spring源码深度解析》,我我的以为不是很好啃下来,可能须要一些Spring项目开发经验的会好理解一些,硬啃的话不少地方可能看不太懂,建议更多地与实践相结合;
Java并发:《Java并发编程实战》,我以为这一本讲得也很好,也建议反复地看反复消化,对于面试问到的一些底层原理讲解得很清楚;
数据库:《高性能MySQL》,很厚,慢慢看吧。其实数据库的话,更多问到的是索引机制这一块,还有性能调优之类等,问的方向比较固定。
算法及代码:建议牛客网或者LeeCode,我面试的时候坚持一天一道题,只要消化理解了,其实进步仍是特别大的,特别是思路上的提升真的很快。
相关文章
相关标签/搜索