美文网首页
JAVA面试--杂篇

JAVA面试--杂篇

作者: 日落西风碎 | 来源:发表于2019-12-19 09:52 被阅读0次

1. Integer与 ==

Integer one = 128;
Integer two = 128;
System.out.println(one == two);
  Integer one = 100;
  Integer two = 100;
  System.out.printf(one == two);

问:上面两种打印结果有何不同
答:Integer类型当正整数小于128时是在内存栈中创建值的,并讲对象指向这个值,这样在比较两个栈引用时因为是同一地址引用两者则相等。当大于127时将会调用new Integer(),两个整数对象地址引用不相等。因此第一种会打印false,第二种打印true。

2. 数组转list

下图代码执行为何出错

public static void main(String[] args){
  String[] array = new String[]{"张三","李四","王五"};
  List<String> list = Arrays.asList(array);
  list.add("孙六");
  System.out.printf(list.size());
}

问:上图代码执行为何出错
答:因为数组转换的列表其实不是arrayList,它是数组内部定义的一种数据结构类型,本质上还是数组而非列表,因此当向了你报添加元素就会出现错误。

3.float精度丢失问题

两个float类型数据相减会丢失精度,尾部会带着长长的一串数字。

4. JAVA中实现多线程的几种方式

继承Thread
实现runable
实现Callable,重写call()方法
Callable对象属于Executor框架中的功能类,Callable与Runnable接口类似,但提供了比Runnable更强大的功能:
1.Callable可以在任务结束后提供一个返回值,Runnable无法提供
2.Callable中的call()方法可以抛出异常,而Runnable的run()方法则不能
3.运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果,可以使用Future来监视目标线程调用call()方法的使用情况,当调用Future的get()方法以获取结果时,当前线程会阻塞,知道call方法结束返回结果为止。

5.序列化

:什么情况下需要序列化,RPC服务中的参数为啥都需要序列化,参数中的日期参数类型到底用sql.Date还是util.Date?
:序列化的目的是依赖为了进行网络传输,确保传过去的字节流还能被反编译找到对应的类,二来是为了方便本地硬盘存储。RPC服务为异步服务都是通过网络传输数据,当然需要序列化数据。Java.sql.Date类继承java.util.Date类,但是并未实现序列化因此作为参数不能再RPC服务中心传输。

6.final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?

:使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例

final StringBuffer a = new StringBuffer("immutable");

执行如下语句将提示错误

a = new StringBuffer("");

但执行如下语句则可以通过编译

a.append("broken!");

有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:

public void method(final StringBuffer param){}

实际上这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:

param.append("a");

7.抽象类与普通类的区别

抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。

8.Collection和Collections的区别

Collection是集合类的上级接口,继承他的接口主要有Set和List
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作。

9.final、finally、finalize的区别

final用于声明属性、方法和类,分别表示属性不可变,方法不可覆盖,类不能被继承。内部类要访问局部变量,局部变量必须定义成final类型。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,但是JVM不保证此方法总被调用。

10.wait和sleep的区别

1.归属不同:sleep()方法属于Thread类,而wait()方法,则是属于Object类;
2.sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。所以在调用sleep()方法的过程中,线程不会释放对象锁。
3.调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后线程才进入对象锁定池准备获取对象锁进行入运行状态。

11.springmvc的核心是什么,请求的流程时怎么处理的,控制反转怎么实现?

核心:控制反转和面向切面
请求处理流程
1.首先用户发送请求到前段控制器,前端控制器根据请求信息(如URL)老决定选择哪一个页面控制器进行处理并发请求委托给它。
2.页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕返回一个ModelAndView(模型数据和逻辑视图名);
3.前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;
4.前端控制器再次回收控制权,将相应返回给客户。
控制反转如何实现
我们每次使用spring框架都要配置xml文件,这个xml配置了bean的id和class。spring中默认的bean为单实例模式,通过bean的class引用反射机制可以创建这个实例。
因此,spring框架通过反射替我们创建好了实例并且替我们维护他们。
A需要引用B类,spring框架就会通过xml把B实例的引用传给了A的成员变量。

12.mybatis如何处理结果集

MyBatis的结果集是通过反射来实现的。并不是通过get/set方法。在实体类中无论是否定义get/set()方法,都是可以接受到的。

13.JAVA中堆和栈,分别是什么数据结构,为什么要分堆和栈来存储数据?

栈是一种具有后进先出性质的数据结构。堆是一种经过排序的树形数据结构。通常所说的堆的数据结构是指二叉堆。堆的特点是根节点的值最小(或最大),且根节点的两个字数也是一个堆。
为什么分堆和栈:1.从软件设计角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。2.堆与栈的分离,使得堆中的内容可以被多个栈共享。一方面这种共享提供了一种有效的数据交互方式,另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。3.栈栈因为运行的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象可以根据需要动态增长的,因为栈和堆的拆分,是的动态增长成为可能,相应栈中只需要记录堆中的一个地址即可。4.体现了JAVA面向对象这一核心特点。

14.mysql优化经验

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引
2.应尽量避免在where子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
3.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这样会降低查找和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
4.任务地方都不要使用select * from t,用具体的字段列表代替“*”,不要返回用不到的任务字段。
5.避免频繁创建和删除临时表,以减少系统表资源的消耗。

15.Spring的核心模块

☆ Spring Core【核心容器】:核心容器提供了Spring的基本功能。核心容器的核心功能是用IOC容器来管理类的依赖关系
☆Spring AOP【面向切面】:Spring的AOP模块提供了面向切面编程的支持。Spring AOP采用的是纯JAVA实现,采用基于代理的AOP实现方案,AOP代理有IOC容器负责生成,管理,依赖关系也一并有IOC容器管理。
☆Spring ORM【对象试题映射】:提供了与多个第三方持久层框架的良好整合。
☆Spring DAO【持久层模块】:Spring进一步简化DAO开发步骤,能以一致的的方式使用数据库访问技术,用统一的方式调用事务管理,避免具体的实现侵入业务逻辑层的代码中。
☆Spring Context【应用上下文】:它是一个配置文件,为Spring提供上下文信息,提供了框架式的对象访问方法。
Spring Web【Web模块】:提供了基础的针对Web开发的集成特性。
☆Spring MVC【MVC模块】:提供了Web应用的MVC实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型。

16 redis常用的五种数据类型

1.String(字符串)、2.Hash(哈希)、3.List(列表)、4.set(集合)、5.zset(有序集合)

17 进程间的通信方式

pipe【管道】
popen【高级管道】
named pipe【有名管道】
MessageQueue【消息队列】
SHaredMemory【共享存储】
Semaphore【信号量】
Socket【套接字】
sinal【信号】

17.堆内存分类

目前大部分JVM采用“分带收集算法”,所谓分带收集就是根据对象的生命周期把内存分为几块,一般把堆分为年轻代、年老代、持久代(1.8之后有空间取代)。分配比例为:年轻代:年老代 = 1 :2
年轻代:“朝生夕死”,存活率低,使用复制算法。可划分为三个区域:原始区(Eden)和两个小的存活区(Survivor),两个存活区按功能分为From和To。绝大多数对象都在原始区分配,超过一个垃圾回收操作仍然存活的对象放到存活区。垃圾回收绝大部分发生在年轻代。
年老代:存活率高,使用“标记-清楚”或“标记-整理”算法。存储年轻代中经过多个回收周期仍然存活的对象,对于一些大的内存分配,也可能会直接分配到年老代。
持久代:存储类、方法以及它们的描述信息。注:1.8之后再无持久代而是用元空间代替,元空间不存在虚拟机中而是在本地内存。

18 堆 年轻代

年轻代可分三区:Eden、SurvivorFrom、SurvivorTo。默认划分比例 8:1:1
GC过程:Eden区满触发Minor GC 将还活着的对象拷贝至SurvivorFrom区,当Eden再次触发GC时扫描Eden区和From区,将存活的对象复制至To区,同时对象年龄+1。完成复制后清空Eden区和From区。此时清空的From区和To区发生交换。即始终保持To区为空,原To去变为下一次GC的From区。部分对象在From和To中来回复制,如此当对象年龄>某数值(JVM参数可设)时,将还在存活的对象放入年老代。

19 堆 年老代

年老代存储存活比较久的对象或者是大对象。
GC过程 年老代GC是采用标记清除算法,当堆中有效内存被耗尽时,它会让整个程序stop然后进行标记在清除。
标记:标记的过程其实就是,遍历所有的GC Roots,然后将所有的GC Roots可达的对象标记为存活对象。
清除:遍历堆中所有对象中没有标记的对象全部清除掉
缺点-1:效率比较低(递归与全堆对象遍历),而且在进行GC的时候,需要停止应用程序。
缺点-2:清理出的内存不连续,JVM就不得不维持一个内存的空闲列表,这又是一个开销
标记-整理:首先标记所有需要回收的对象,让所有存活的对象都向一段移动,然后直接清理掉边界意外的内存。
优点:不会产生空间碎片,但整理需要一点时间;
适用:适合年老代进行垃圾收集,parallel Old gc 和Serial old就是采用的标记-整理。

20 复制回收算法、标记清除算法

复制回收算法-优点:在存活对象不多的情况下,性能高,可解决内存碎片和标记清除算法中导致的引用更新问题。
缺点:会造成内存浪费,可根据实际情况调整内存块大小比例。但是如果存活对象的数量比较大,coping的性能也会变的很差。

21.垃圾回收

Minor GC:从年轻代空间回收内存,复制算法
Major GC:老年代GC
Full GC:Full GC是对整个堆来说的。标记清除算法
触发Full GC的条件
1.System.gc() 建议jvm执行full gc 并不一定执行
2.执行了jmap -histo:live pid命令 立即触发Full GC
3.使用了大对象 大对象会直接进入老年代
4.在程序中长期持有对象的引用 //对象年龄达到指定阈值也会进入老年代
5.执行minor gc时进行一系列检查:如下
a.执行Minor GC的时候,JVM会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小。
b.如果大于,则直接执行Minor GC(这个时候执行是没有风险的)。
c.如果小于了,JVM会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行Full GC。
d.如果开启了,则JVM会检查老年代中最大连续可用空间是否大于了历次晋升到老年代中的平均大小,如果小于则执行改为执行Full GC。
e.如果大于则会执行Minor GC,如果Minor GC执行失败则会执行Full GC

33.Quartz任务调度框架

定义:Quatz是一个完全由Java编写的开源任务框架,通过触发器设置作业定时运行时间,控制作业的运行时间。
集群:Quatz集群通过故障切换和负载平衡的功能,能给调度器带来高可用性和伸缩性。
功能:主要用来执行定时任务,如:定时发送消息、定时生成报表等。
核心组件:调度器(scheduler)、触发器(trigger)、工作(JobDetail)
  调度器(scheduler):一个调度器可以注册众多的触发器和工作,调度器启动后,里面的每个工作会根据调度器按部就班的自动执行
  触发器(trigger):代表调度参数的配置,什么时候去调工作
  工作(JobDetail):一个可执行的作业,它本身可能是有状态的。
工作过程:当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger组成一对儿),就可以伴随容器启动而调度执行了。scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提供容器效率。
优缺点:
  quartz作为组件可嵌入任务独立的系统中运行
  quartz可在应用服务器或者servlet容器中实例化
  quartz可独立运行在本身所带的虚拟机中
  quartz可以被实例化独立项目的集群(有负载均衡和容错能力)
Quartz与spring自带定时器(Timer Task)的差别:
  精确度:Quartz可通过cron表达式精确到特定的时间执行,而TimerTask不行。
  任务数量:Quartz每次执行都会创建一个新的任务对象,但TimerTash则每次都使用同一个任务类对象。
  异常处理:Quartz执行异常时,不会影响下一次任务的执行;但TimerTask不同,一旦某个任务在执行过程中抛出异常,则整个定时器生命周期就结束,不会再执行定时任务。

34.什么是一致性Hash算法?

一致性hash是为了解决分布式而提出的。在动态变化的分布式环境中判断Hash算法好坏的主要有四个方面:平衡性单调性分散性负载
环形Hash空间
  按照常用的Hash算法将对应的值哈希到一个具有2^32次方个桶的空间中,即0 ~ (2^32)- 1的数字空间,将这些数字首尾相连形成一个闭合的环形。
数据映射
  将对象通过特定的Hash函数计算出对应的key,然后散列到hash环上
设备映射
  将设备通过特定Hash算法计算出对应的key,并散列到Hash换上,然后以顺时针方向计算,将所有对象存储到离自己最近的机器中去。这样在面对机器个数发生改变时,只会有一小部分的数据的存储位置需要发生改变
解决平衡问题
上述算法存在一个问题,即当存储设备过少时极有可能造成数据分配不平衡。为了解决这种问题,引入了虚拟节点。所谓虚拟节点是实际节点(机器)在hash空间的复制品。一个实际节点对对应若干个虚拟节点,这个对应个数也称为“复制个数”,虚拟节点在hash环空间中以hash值排列。
虚拟节点
  虚拟节点的hash计算可以采用对应节点的IP地址加数字后缀的方式
详情解读: https://www.cnblogs.com/myseries/p/10956835.html

35. maven优势

Maven是JAVA平台下的一款项目构建和依赖管理的自动化工具
优点
  1. 自动导入jar及其依赖;
  2. maven支持插件可实现配置文件管理;根据环境的不同加载不同的配置文件
  3. 实现项目多模块化;让每个模块独立,高内聚,低耦合,不同的模块之间保持自己的完整性,可以互不干扰,方便以后的维护开发。
  4. 生命周期管理,自动完成工程的基础构建配置。

相关文章

网友评论

      本文标题:JAVA面试--杂篇

      本文链接:https://www.haomeiwen.com/subject/uxixgctx.html