平安科技面经 2018.12.3
一面(电话面试45min)
1、自我介绍(简单提到项目)
2~3 min
2、spring启动时的容器启动流程(对配置的加载顺序)?
容器启动流程中涉及的主要类——
ContextLoaderListener:注册在web.xml中,web应用启动时,会创建它,并回调它的initWebApplicationContext()方法,从而创建并启动spring容器。必须继承ServletContextListener。
WebApplicationContext:用于web应用的spring容器上下文,它代表了spring容器,继承自ApplicationContext。是一个接口,ContextLoader.properties配置文件中可以声明它的实现类。默认实现类为XmlWebApplicationContext。 ApplicationContext继承自BeanFactory,并扩展了它的很多功能。
ServletContext:web容器(如tomcat)的上下文,不要和ApplicationContext搞混了。
3、spring使用到的设计模式?最好说出在什么地方使用,举例说明。
4、手撕设计模式,写一种你熟悉的设计模式的demo。
写了简单工厂模式demo(包含测试类)
无代码提示,所以在平时书写时注意代码的格式以及细节处,避免出现小问题。
5、String s=new String(“abc”),包含几个对象?
(两个或一个都有可能,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。NewString每写一遍,就创建一个新的对象,它使用常量”xyz”对象的内容来创建出一个新String对象。如果以前就用过’xyz’,那么这里就不会创建”xyz”了,直接从缓冲区拿,这时创建了一个StringObject;但如果以前没有用过"xyz",那么此时就会创建一个对象并放入缓冲区,这种情况它创建两个对象。)
创建了两个对象,一个是new String 创建的一个新的对象,一个是常量“abc”对象的内容创建出的一个新的String对象。
创建过程分析:当执行String s = new String(“abc”);时,JVM首先在String Pool中查看是否存在字符串对象“abc”,如果不存在该对象,则先在String Pool中创建一个新的字符串对象“abc”,然后执行new String(“abc”)构造方法,在Heap里又创建一个新的字符串对象“abc”(new出来的对象都放在Heap堆里面),并将引用s指向Heap中创建的新对象;如果已存在该对象,则不用创建新的字符串对象“abc”,而直接使用String Pool中已存在的对象“abc”, 然后执行new String(“abc”)构造方法,在Heap里又创建一个新的字符串对象“abc”,并将引用s指向Heap中创建的新对象。
注意:使用new String(“”)创建的字符串对象时,会在运行期创建新对象存储到Heap中。因此,new String(“abc”)创建字符串对象时,会创建2个对象,编译期在String Pool(字符串常量池)中创建一个,运行时Heap中创建一个。
6、static可以修饰什么?有什么作用?
作用于方法上:方法属于类,不属于类的实例对象;Static方法不需要实例对象就可以通过类名调用,且Static方法中不能有实例成员。
作用于变量上:……
作用于类上:……
在什么时候执行:……
注:非static——
作用于方法上:属于类的实例对象,可以有static成员。
作用于变量上:属于实例对象或者是局部变量,创建类的实例对象才能引用。
作用于类上:可以有Static成员也可以有非Static成员。
static是在编译的时候被绑定,加载的时候就会被执行的。按顺序执行static变量和static代码块。
Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。
7、HashMap的源码简述。
8、Hashtable与HashMap的区别?
HashMap和Hashtable的相同点——
HashMap和Hashtable都是存储“键值对(key-value)”的散列表,而且都是采用拉链法实现的。
存储的思想都是:通过table数组存储,数组的每一个元素都是一个Entry;而一个Entry就是一个单向链表,Entry链表中的每一个节点就保存了key-value键值对数据。
添加key-value键值对:首先,根据key值计算出哈希值,再计算出数组索引(即,该key-value在table中的索引)。然后,根据数组索引找到Entry(即,单向链表),再遍历单向链表,将key和链表中的每一个节点的key进行对比。若key已经存在Entry链表中,则用该value值取代旧的value值;若key不存在Entry链表中,则新建一个key-value节点,并将该节点插入Entry链表的表头位置。
删除key-value键值对:删除键值对,相比于“添加键值对”来说,简单很多。首先,还是根据key计算出哈希值,再计算出数组索引(即,该key-value在table中的索引)。然后,根据索引找出Entry(即,单向链表)。若节点key-value存在与链表Entry中,则删除链表中的节点即可。
HashMap和Hashtable的不同点——
1、线程安全不同
Hashtable的几乎所有函数都是同步的,即它是线程安全的,支持多线程。而HashMap的函数则是非同步的,它不是线程安全的。
若要在多线程中使用HashMap,需要我们额外的进行同步处理。 对HashMap的同步处理可以使用Collections类提供的synchronizedMap静态方法,或者直接使用JDK 5.0之后提供的java.util.concurrent包里的ConcurrentHashMap类。
2、对null值的处理不同
HashMap的key、value都可以为null。
Hashtable的key、value都不可以为null。(value为null,直接抛异常,要用key计算hashCode,那key也不能为null)
3、通过Iterator迭代器遍历时,遍历的顺序不同
HashMap是“从前向后”的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历。
Hashtabl是“从后往前”的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历。
4、容量的初始值 和 增加方式都不一样
HashMap默认的容量大小是16;扩容时,每次将容量变为“原始容量x2”。
Hashtable默认的容量大小是11;扩容时,每次将容量变为“原始容量x2 + 1”。
HashMap默认的“加载因子”是0.75, 默认的容量大小是16
5、添加key-value时的hash值算法不同
HashMap添加元素时,是使用自定义的哈希算法。
Hashtable没有自定义哈希算法,而直接采用的key的hashCode()。
9、LinkedList、ArrayList简述及区别?
简述:
ArrayList的直接父类是AbstractList,数据结构是大小可变的数组, 它不是同步的。LinkedList的直接父类是AbstractSquentialList,数据结构是双向链表,它不是同步的,它同时实现了Deque(双向队列)和Queue(队 列)接口。同时它还提供了push和pop这两个堆栈操作的接口。
区别:
(1) ArrayList是实现了基于动态数组的数据结构,LinkedList基于双向循环链表的数据结构。
(2) 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
(3) 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
(4) 查找操作indexOf,lastIndexOf,contains等,两者差不多。
(5) 随机查找指定节点的操作get,ArrayList速度要快于LinkedList. 当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在 一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
扩容: 针对ArrayList,在新增的时候,容量不够就需要扩容,2倍。
10、数据库事务概念,以及隔离级别?
***事务:
①事务的概念:
一组不可被分割执行的SQL语句集合。
数据库区分于文件系统的重要特征之一。
②事务的基本原理及作用:
开启事务的本质是——关闭了自动提交的功能,改为commit手动提交。
自动提交的特征——保存在服务的一个autocommit的变量里,可修改。
设置自动提交模式:set autocommit=0;
如果不开启事务,只执行一条sql语句,马上就会持久化数据。即:普通执行就是立即提交,提交就会将结果持久化。
原因—— MySQL默认对sql语句的执行是自动提交。
每条sql语句都是同一事务的不同命令,之间由commit或rollback隔开。掉线(宕机)后,无commit的事务都被放弃。
③注意:事务类似于外键约束,纸杯InnoDB引擎支持。
***事务的四大特性(“ACID”原则)
1. 原子性(Atomicity)
事务的操作要么完全成功,要么就回滚。只有事务中所有的数据库操作都执行成功,才能算整个事务成功。对数据库要么就是完全正确的数据,要么就不记录任何数据。
2. 一致性(Consistency)
事务发生前后,数据库只能从一致性的一个状态,转换为另一个一致性的状态。
在事务开始之前和结束之后,数据库的完整性约束没有被破坏。
3. 隔离性(Isolation)
多个事务并发操作时,事务之间不会相互影响。
每一个读写事务的对象对其他事务的操作对象相互分离。即该事物提交之前,对其他事务都不可见(实现方法:锁)
4. 持久性(Durability)
一旦事务提交,数据就会持久化在数据库中,不可能再被回滚的。(执行成功后必须全部写入磁盘)
***事务隔离级别(顺序是对应的隔离级别从低到高):
①、None 不使用事务(一般可省略)
②、Read uncommitted 读未提交(一个事务可以读取另一个未提交事务的数据)
可能出现的问题:脏读、不可重复读、幻读
——当一个事务正在进行时,更改了数据库的数据,但还未提交。另一个事务就读取了数据库中那个事务刚刚修改过的数据,这就是脏读。
③、 Read committed 读提交(一个事务要等另一个事务提交后才能读取数据)
(Sql Server,Oracle默认的事务隔离级别)
可能出现的问题:不可重复读、幻读
——在一个事务中,前后两次对数据库中同一个数据进行读取,但是读取的结果前后不同。这是因为在这个事务执行的时候,另一个事务恰好修改了这块数据。
④、Repeatable read 重复读 (在开始读取数据(事务开启)时,不允许修改的操作)
(mysql默认的事务隔离级别)
可能出现的问题:幻读(对应insert操作)
——当一个事务,批量修改数据库中记录的某些或者某个列的数据时,比如把visibility从0修改到了1,但是在正在执行,还未提交之前。有另一个事务插入了一条数据,其中的visibility值为0,且进行了提交。当那个事务提交之后,发现数据库中为什么还会存在一条visibility值为0的数据,难道是我没有修改他么???这就出现了幻觉,也就是所谓的幻读。
⑤、Serializable 串行化
不会出现前面三种问题
——好处:在该级别下,事务串行化顺序执行,避免前三种问题;
——缺点:效率低下,比较消耗数据库性能,是以“锁表”这种粗暴的方法避免的。(一般情况下不使用)
11、脏读、不可重复读、幻读的概念及区别?(最好举例说明)
概念:
①脏读:
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
②不可重复读:
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)
③幻读:
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象
发生了幻觉一样。
举例:
①脏读:
公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资已经到账,是5000元整,非常高 兴。可是不幸的是,领导发现发给singo的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后singo实际的工资只有 2000元,singo空欢喜一场。
出现上述情况,即我们所说的脏读 ,两个并发的事务,“事务A:领导给singo发工资”、“事务B:singo查询工资账户”,事务B读取了事务A尚未提交的数据。
②不可重复读:
singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在 singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为什么突然没有了?
出现上述情况,即我们所说的不可重复读 ,两个并发的事务,“事务A:singo消费”、“事务B:singo的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
③幻读:
singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额 (select sum(amount) from transaction where month = 本月)为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction ... ),并提交了事务,随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出 现了幻觉,幻读就这样产生了。
12、脏读、不可重复读、幻读的区别?
区别:
三者的区别——
脏读是事务未提交状态下的问题。
不可重复读和幻读都是事务提交状态下的问题。
不可重复读和幻读的区别——
不可重复读考虑的是同一块数据,而幻读是一批数据。
13、MyISAM与InnDB的区别、应用场景以及查询速度?
(MyISAM存储引擎是mysql5.5.8版本之前默认的存储引擎(除了Windows版本),之后默认的存储引擎是InnoDB。)
两种存储引擎的大致区别表现在:
1)InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
2)MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
3)InnoDB支持外键,MyISAM不支持
4)InnoDB支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like '%lee%'
5)清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM则会重建表
6)InnoDB不支持FULLTEXT类型的索引
7)InnoDB中不保存表的行数,如select count(*) from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含where条件时MyISAM也需要扫描整个表
8)对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引
应用场景:
MyISAM适合:(1)做很多count 的计算;(2)插入不频繁,查询非常频繁;(3)没有事务。
InnoDB适合:(1)可靠性要求比较高,或者要求事务;(2)表更新和查询都相当的频繁,并且行锁定的机会比较大的情况。
为什么MyISAM会比Innodb 的查询速度快:
INNODB在做SELECT的时候,要维护的东西比MYISAM引擎多很多;
1)数据块,INNODB要缓存,MYISAM只缓存索引块, 这中间还有换进换出的减少;
2)innodb寻址要映射到块,再到行,MYISAM 记录的直接是文件的OFFSET,定位比INNODB要快
3)INNODB还需要维护MVCC一致;
14、JVM内存模型?
15、每个分区的功能,以及分别存放什么?
一般来说,Java程序在运行时会涉及到以下内存区域:
- 寄存器:
JVM内部虚拟寄存器,存取速度非常快,程序不可控制。
2.Java虚拟机栈(通俗就是我们常说的“栈”):
它是线程私有的,它的生命周期与线程相同。每个方法被执行的时候都会同时创建一个栈帧(StackFrame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。它存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型),它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
3.堆:
Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。 Java堆是垃圾收集器管理的主要区域。
4.方法区:
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来,但它还是属于堆里面的。
5.常量池(其实是方法区的一部分):
JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用(1)。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于堆中.
需要注意的一些:
1、对象所拥有的方法以及里面涉及到的变量都存储在栈里面,方法里面使用到的全局变量是随着对象实例一起存储在堆里面,在方法中使用的时候也是使用该全局变量的副本.
2、对于一个对象的成员变量,不管他是原始类型还是包装类型,都会被存贮在堆区.
3、方法区和堆是一样,是各个线程共享的区域,里面存放java虚拟机加载的类信息,常量,静态变量,即使编译器编译后的代码等数据.
4、当调用一个对象的方法时会在java(虚拟机栈)栈里面创建属于自己的栈空间,方法走完即被释放
5、分清什么是实例什么是对象。Class a = new Class();此时a叫实例,而不能说a是对象。实例在栈中,对象在堆中,操作实例实际上是通过实例的指针间接操作对象。多个实例可以指向同一个对象。
16、synchronize的使用场景,以及如何保证线程安全?
synchronized关键字最主要有以下3种应用方式——
- 修饰实例方法(不包括静态方法),作用于当前实例加锁,进入同步代码前要获得当前实例的锁
- 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
- 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
synchronized方法底层原理——
方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。
JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法———
———当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词), 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。在方法执行期间,执行线程持有了monitor,其他任何线程都无法再获得同一个monitor。如果一个同步方法执行期间抛 出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的monitor将在异常抛到同步方法之外时自动释放。
17、对进程、线程的理解?
进程和线程的概念:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位。
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)处理机分给线程,即真正在处理机上运行的是线程。
(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
进程与线程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
18、对多线程、线程池的理解
19、线程间通信?