欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA面试题1

程序员文章站 2022-05-06 17:49:08
...

JAVA面试题1

(资源来源于网络,内容自己整理)

1.为什么HashMap中的String、Integer这样的包装类适合作为K

1.String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率;

2.都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况;

3.内部已重写了equals()、hashCode()等方法,遵守了HashMap内部的规范(不清楚可以去上面看看putValue的过程),不容易出现Hash值计算错误的情况。

1.1如果想要让自己的Object作为K应该怎么办呢?

1.重写hashCode()和equals()方法

2.重写hashCode()是因为需要计算存储数据的存储位置,需要注意不要试图从散列码计算中排除掉一个对象的关键部分来提高性能,这样虽然能更快但可能会导致更多的Hash碰撞;

3.重写equals()方法,需要遵守自反性、对称性、传递性、一致性以及对于任何非null的引用值x,x.equals(null)必须返回false的这几个特性,目的是为了保证key在哈希表中的唯一性;

2.Array和ArrayList有什么区别?什么时候应该Array而不是ArrayList呢?

区别:

1、存储内容比较:
Array 数组可以包含基本类型和对象类型,
ArrayList 却只能包含对象类型。
Array 数组在存放的时候一定是同种类型的元素。ArrayList 就不一定了 。

2、空间大小比较:
Array 数组的空间大小是固定的,所以需要事前确定合适的空间大小。
ArrayList 的空间是动态增长的,而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。

3.方法上的比较:
ArrayList 方法上比 Array 更多样化,比如添加全部 addAll()、删除全部 removeAll()、返回迭代器 iterator() 等。

适用场景:
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里, 但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择 ArrayList。
如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用 ArrayList 就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择 LinkedList。

3.HashSet是如何保证数据不可重复的?

HashSet底层结构是一个HashMap,HashSet将值放在HashMap的键中,

如果HashMap的键相同时会发生覆盖,因此HashSet的值不会重复。

HashMap检查Key是否相同会通过equles方法,并通过比较hash值判断是否重复。

hashCode()和equals():

如果两个对象相等,其hashcode也相等

如果两个对象相等,其equals方法会返回true

hashcode相等,对象不一定是相等的

如果重写equals方法,那么也要重写hashcode方法

hashcode是堆中对象产生的独特值,如果没有重写,那么该类的对象不会相等

4.HashMap中的put 方法的具体流程?

JDK1.8版本分析

1、put(key, value)中直接调用了内部的putVal方法,并且先对key进行了hash操作;

2、putVal方法中,先检查HashMap数据结构中的索引数组表是否位空,如果是的话则进行一次resize操作;

3、以HashMap索引数组表的长度减一与key的hash值进行与运算,得出在数组中的索引,如果索引指定的位置值为空,则新建一个k-v的新节点;

4、如果不满足的3的条件,则说明索引指定的数组位置的已经存在内容,这个时候称之碰撞出现;

5、在上面判断流程走完之后,计算HashMap全局的modCount值,以便对外部并发的迭代操作提供修改的Fail-fast判断提供依据,于此同时增加map中的记录数,并判断记录数是否触及容量扩充的阈值,触及则进行一轮resize操作;

6、在步骤4中出现碰撞情况时,从步骤7开始展开新一轮逻辑判断和处理;

7、判断key索引到的节点(暂且称作被碰撞节点)的hash、key是否和当前待插入节点(新节点)的一致,如果是一致的话,则先保存记录下该节点;如果新旧节点的内容不一致时,则再看被碰撞节点是否是树(TreeNode)类型,如果是树类型的话,则按照树的操作去追加新节点内容;如果被碰撞节点不是树类型,则说明当前发生的碰撞在链表中(此时链表尚未转为红黑树),此时进入一轮循环处理逻辑中;

8、循环中,先判断被碰撞节点的后继节点是否为空,为空则将新节点作为后继节点,作为后继节点之后并判断当前链表长度是否超过最大允许链表长度8,如果大于的话,需要进行一轮是否转树的操作;如果在一开始后继节点不为空,则先判断后继节点是否与新节点相同,相同的话就记录并跳出循环;如果两个条件判断都满足则继续循环,直至进入某一个条件判断然后跳出循环;

9、步骤8中转树的操作treeifyBin,如果map的索引表为空或者当前索引表长度还小于64(最大转红黑树的索引数组表长度),那么进行resize操作就行了;否则,如果被碰撞节点不为空,那么就顺着被碰撞节点这条树往后新增该新节点;

10、最后,回到那个被记住的被碰撞节点,如果它不为空,默认情况下,新节点的值将会替换被碰撞节点的值,同时返回被碰撞节点的值(V)。

5.ConCurrentHashMap和Hashtable的区别?

ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势。
HashMap 没有考虑同步,HashTable 考虑了同步的问题。

但是 HashTable 在每次同步执行时都要锁住整个结构。
 ConcurrentHashMap 锁的方式是稍微细粒度的。

6.简述一下SpringMVC的工作原理?

简单理解:
客户端发送请求
----前端控制器接受客户端请求DispatcherServlet
----找到处理器映射HandlerMapping
----找到处理器Handler
----处理器返回一个模型视图ModelAndView
----视图解析器进行解析
----返回一个视图对象
----前端控制器的得到视图对象
----显示给用户

详细工作原理:

1.Tomcat在启动时加载解析web.xml,找到spring mvc的前端总控制器DispatcherServlet,并且通过DispatcherServlet来加载相关的配置文件信息。

2.DispatcherServlet接收到客户端请求,找到对应HandlerMapping,根据映射规则,找到对应的处理器(Handler)。

3.调用相应处理器中的处理方法,处理该请求后,会返回一个ModelAndView。

4.DispatcherServlet根据得到的ModelAndView中的视图对象,找到一个合适的ViewResolver(视图解析器),根据视图解析器的配置,DispatcherServlet将要显示的数据传给对应的视图,最后显示给用户。

7.GET请求与POST请求的区别?

首先:
  一般来说,我们发送get是希望从服务器上获取数据,post请求需要向服务器传送数据。
 
1.  get 在浏览器回退时是无害的,post 会再次提交数据
2.  get 产生的url 地址可以被 bookmark,post 则不可以
3.  get 请求会被浏览器主动cache (缓存),post 则不会,除非手动设置
4.  get 请求参数会被完整保留在浏览器历史记录里,而post中参数不会被保留
 
5.  get 只接受ASCII 码字符,而post 没有限制
6.  get 请求只能进行url 编码,而post 支持多种编码方式。
 
7.  get 把请求参数放在url 上,即http协议头上,post 放在Request body请求体中。
     故get 比post 更不安全,不能用来传递敏感信息。
 
     附:get 参数放在url上,以?分割url,参数之间以&相连;英文/数字,不做改变,原样发送;
     空格转换为+;中文/其他字符,则用base64加密,即%加上“十六进制ASCII码”
 
8. get 一般来说提交的数据最大是2k;(原则上url 长度无限制,但大多数浏览器通常都会限制url 长度在2k(2048字节byte))
    post 理论上没有限制,实际上IIS4中最大量为80k,IIS5中为100k。
 
9. get 产生一个tcp 数据包,浏览器会把http header 和data 一并发送出去,服务器响应200(返回数据)
    post 产生两个tcp 数据包,浏览器会先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200(返回数据)
 
    附:Http 是基于tcp/ip 关于数据如何在万维网中通信的协议。所以http 的底层是tcp/ip,get 、post 的底层也是tcp/ip,
          也就是说,get、post 都是tcp 连接。

8.Spring的AOP是什么,他的动态代理有哪两种方式?有什么不同?

Spring(AOP)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

(Spring AOP)动态代理两种方式
1.JDK动态代理
2.cglib代理

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
注:JDK动态代理要比cglib代理执行速度快,但性能不如cglib好。所以在选择用哪种代理还是要看具体情况,一般单例模式用cglib比较好,

9.HashMap在JDK1.7和JDK1.8中有哪些不同?

不同 JDK 1.7 JDK 1.8
存储结构 数组 + 链表 数组 + 链表 + 红黑树
初始化方式 单独函数:inflateTable() 直接集成到了扩容函数resize()中
hash值计算方式 扰动处理 = 9次扰动 = 4次位运算 + 5次异或运算 扰动处理 = 2次扰动 = 1次位运算 + 1次异或运算
存放数据的规则 无冲突时,存放数组;冲突时,存放链表 无冲突时,存放数组;冲突 & 链表长度 < 8:存放单链表;冲突 & 链表长度 > 8:树化并存放红黑树
插入数据方式 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) 尾插法(直接插入到链表尾部/红黑树)
扩容后存储位置的计算方式 全部按照原来方法进行计算(即hashCode ->> 扰动函数 ->> (h&length-1)) 按照扩容后的规律计算(即扩容后的位置=原位置 or 原位置 + 旧容量)

10.Mybatis中的#和$的区别?

区别

1.#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id".

2.将传入的数据直接显示生成在sql中。如:orderby将传入的数据直接显示生成在sql中。如:orderbyuser_id$,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.

3.#方式能够很大程度防止sql注入。

4.$方式无法防止Sql注入。

5.$方式一般用于传入数据库对象,例如传入表名.
6.一般能用#的就别用$.

注:
MyBatis排序时使用order by 动态参数时需要注意,用$而不是#

动态sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析。
mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}。

11.Mybatis 在insert插入操作时如何返回主键ID?

MySQL用法:

<insert id="insert" parameterType="com.test.User"  keyProperty="userId" useGeneratedKeys="true" >

上面配置中,“keyProperty”表示返回的id要保存到对象的那个属性中,“useGeneratedKeys”表示主键id为自增长模式。

Oracle用法:

<insert id="insert" parameterType="com.test.User">
   <selectKey resultType="INTEGER" order="BEFORE" keyProperty="userId">  
       SELECT SEQ_USER.NEXTVAL as userId from DUAL
   </selectKey> 
    insert into user (user_id, user_name, modified, state)
    values (#{userId,jdbcType=INTEGER}, #{userName,jdbcType=VARCHAR},  #{modified,jdbcType=TIMESTAMP}, #{state,jdbcType=INTEGER})
</insert>

Oracle用法中,需要注意的是:由于Oracle没有自增长一说法,只有序列这种模仿自增的形式,所以不能再使用“useGeneratedKeys”属性。而是使用将ID获取并赋值到对象的属性中,insert插入操作时正常插入id。