Java基础面试题

1.集合类:ListSet比较,各自的子类比较(ArrayListVectorLinkedListHashSetTreeSet

List:元素是有顺序的且可以重复,因为每个元素有自己的角标(索引)

  • ArrayList:底层是数组结构,特点是查询很快,增删稍微慢点。线程不同步,A线程将元素放在索引0位置,CPU调度线程A停止,B运行,也将元素放在索引0位置,当A和B同时运行的时候Size就变成了2.
  • LinkedList:底层是链表结构,特点是增删很快,查询慢。线程不安全,线程安全问题是由多个线程同时写或同时读写同一个资源造成的。
  • Vector:底层是数组结构,线程同步,Vector的方法前面加了synchronized关键字,被ArrayList代替了,现在用的只有他的枚举。

Set:元素是无序的且不可以重复(存入和取出的顺序不一定一致),线程不同步。Set底层是使用Map实现的,故可以通过ConcurrentHashMap的方式实现线程安全的Set。

  • HashSet:底层是哈希表结构。根据HashCode和equals方法来确定元素的唯一性。
  1. HashCode和equals的作用是一样的,都是用来比较两个对象是否相等一致。
  2. equals比较的更为全面,而利用HashCode()进行对比,则只要生成一个hash值进行比较就可以了,而且效率高。
  3. equal()相等的两个对象他们的HashCode()肯定相等,也就是equal()是绝对可靠的。
  4. HashCode()相等的两个对象他们的equal()不一定相等,HashCode()不是绝对可靠的。
  5. 当两个对象需要对比的时候,首先用HashCode()去对比,如果不一样,则表示这两个对象肯定不相等(也就不用再比较equal(0)了),如果HashCode()相同,再比较equal(),如果equal()相同,那两个对象就是相同的。
  • TreeSet:可以对Set集合中的元素进行排序(自然循序),底层结构是二叉树。

Map:这个集合是存储键值对的,一个键(key)对应一个值(value)进行存储,而且要确保键的唯一性(01,张三)这样的形式打印出来就是01 = 张三。

  • HashTable:底层是哈希表结构,不可以存入null键和null值,该集合线程是同步的,效率比较低。出现于JDK1.0中,线程安全,使用synchronized锁住整张Hash表实现线程安全,即每次锁住整张表让线程独占。
  • HashMap:底层是哈希表结构,可以存入null键和null值,线程不同步,效率较高,代替了HashTable,出现于JDK 1.2中。
  • TreeMap:底层是二叉树结构,线程不同步,可以用来对Map集合中的键进行排序。

ConcurrentHashMap:线程安全,允许多个修改操作并发进行,其关键在于使用了锁分离技术,它使用了多个锁来控制对Hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

 

2.HashMap的底层实现和ConcurrentHashMap的底层实现

HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。允许使用null值和null键。它的底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。HashMap是基于hash算法实现的,通过put(key,value)存储对象到HashMap中,也可以通过get(key)从HashMap中获取对象。

  • 当我们使用put的时候,首先HashMap会对key的HashCode()的值进行hash计算,根据hash值得到这个元素在数组中的位置,将元素存储在该位置的链表上。
  • 当我们使用get的时候,首先HashMap会对key的HashCode()的值进行hash计算,根据hash值得到这个元素在数组中的位置,将元素从该位置上的链表中取出。

在多线程的情况下,可能产生条件竞争。当重新调整HashMap大小的时候,确实存在条件竞争,如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的数组位置的时候,HashMap并不会将元素放在LinkedList的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。

  • ConcurrentHashMap基于双数组和链表的Map接口的同步实现
  • ConcurrentHashMap中元素的key是唯一的、value值可重复
  • ConcurrentHashMap不允许使用null值和null键
  • ConcurrentHashMap是无序的

为什么使用ConcurrentHashMap?

我们都知道HashMap是非线程安全的,当我们只有一个线程在使用HashMap的时候,自然不会有问题,但如果涉及到多个线程,并且有读有写的过程中,HashMap就会fail-fast。要解决HashMap同步的问题,我们的解决方案有:Hashtable、Collections.synchronizedMap(HashMap),这两种方式基本都是对整个Hash表结构加上同步锁,这样在锁表的期间,别的线程就需要等待了,无疑性能不高,所以我们引入ConcurrentHashMap,既能同步又能多线程访问。

ConcurrentHashMap的数据结构是什么?

ConcurrentHashMap的数据结构为一个Segment数组,Segment的数据结构为HashEntry的数组,而HashEntry存的是我们的键值对,可以构成链表。可以简单的理解为数组里装的是HashMap。

 

3.如何实现HashMap顺序存储?

可以参考LinkedHashMap的底层实现,LinkedHashMap底层使用哈希表与双向链表来保存所有元素,它维护着一个运行于所有条目的双向链表(如果学过双向链表的同学会更好的理解它的源代码),此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者访问顺序。

  • 按插入顺序的链表:在LinkedHashMap调用get方法后,输出的顺序和输入时的相同,这就是按插入顺序的链表,默认是按插入顺序排序。
  • 按访问顺序的链表:在LinkedHashMap调用get方法后,会将这次访问的元素移至链表尾部,不断访问可以形成按访问顺序排序的链表。简单的说,按最近最少访问的元素进行排序(类似LRU算法)。

 

4.StringStringBufferStringBuilder的区别?

  • 运行速度快慢为:StringBuilder > StringBuffer > String
  • String最慢的原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。
  • String:适用于少量的字符串操作的情况。
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况(线程不安全)。
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况(线程安全)。

 

5.Object的方法有哪些:比如有wait方法,为什么会有waitnotifynotifuAll

  • 使用wait()、notify()和notifyAll()时需要首先调用对象加锁。
  • 调用wait()方法后,线程状态会从running变为waiting,并将当前线程加入到lock对象的等待队列中。
  • 调用notify()或者notifyAll()方法后,等待在lock对象的等待队列的线程不会马上从wait()方法返回,必须要等到调用notify()或者notifyAll()方法的线程将lock锁释放,等待线程才有机会从等待队列返回。这里只是有机会,因为锁释放后,等待线程会出现竞争,只有竞争到该锁的线程才会从wait()方法返回,其他的线程只能继续等待。
  • notify()方法将等待队列中的一个线程移到lock对象的同步队列,notifyAll()方法则是将等待队列中所有线程移到lock对象的同步队列,被移动的线程的状态由waiting变为blocked。
  • wait()方法上等待锁,可以通过wait(long timeout)设置等待的超时时间。

 

6.waitsleep的区别?(必须理解)

sleep方法属于线程,wait方法属于对象。sleep休眠当前线程,不会释放对象锁,wait使当前线程进入等待状态,释放对象锁,只有针对此对象调用notify()方法(且共享对象资源释放)后本线程才会继续执行。

参考:https://www.cnblogs.com/hongten/p/hongten_java_sleep_wait.html

 

7.JVM的内存结构和JVM的算法?

JVM内存结构主要有三大块:

  • 堆内存:几乎所有的对象实例都存放在堆里,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
  • 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
  • 栈:每个方法被执行的时候都会同时创建一个栈帧(StackFrame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。如果线程请求栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。

 

8.强引用、软引用和弱引用的区别?

  • 强引用:以前我们使用的大部分引用实际上都是强引用,这种引用使用最普遍。如果一个对象具有强引用,那该对象就不可或缺,垃圾回收器绝不会回收它。当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
  • 软引用:如果一个对象只具有软引用,那该对象就可有可无。如果内存空间足够,垃圾回收器就不会回收它,若内存空间不足,就会回收这些对象的内存。
  • 弱引用:弱引用与软引用的区别在于只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

总结:

强引用:String str = “abc”; list.add(str);

软引用:如果弱引用对象回收完之后,内存还是报警,继续回收软引用对象。

弱引用:如果虚引用对象回收完之后,内存还是报警,继续回收弱引用对象。

虚引用:虚拟机的内存不够使用,开始报警,这时候垃圾回收机制开始执行System.gc(); String s = “abc”;如果没有对象回收了,就回收虚引用的对象。

 

9.数组在内存中如何分配?

当一个对象使用关键字“new”创建时,会在堆上分配内存空间,然后返回对象的引用,这对数组来说也是一样的,因为数组也是一个对象。简单的值类型的数组,每个数组成员都是一个引用(指针),引用到栈上的空间。

 

10.SpringMvc的核心是什么,请求的流程是怎么处理的,控制反转怎么实现的AOPIOC

  • 流程:用户发送请求给服务器。url:user.do--->Dispatchservlet处理-->DispatchServlet通过HandleMapping调用这个url对应的Controller,Controller执行完毕后,如果返回字符串,则ViewResolver将字符串转化成相应的视图对象;如果返回ModelAndView对象,该对象本身就包含了视图对象信息。DispatchServlet将执视图对象中的数据,输出给服务器并呈现给客户。
  • IOC控制反转:典型的工厂模式就是具有依赖注入功能的容器,是可以创建对象的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者。
  • AOP依赖注入:典型的代理模式,面向切面编程将程序中的交叉业务逻辑(比如安全,日志,事务)封装成一个切面,然后注入到目标业务逻辑中去。AOP框架具有的两个特征:各个步骤之间的良好隔离性和源代码无关性

 

11.Java的多态表现在哪里?

多态是同一个行为具有多个不同表现形式或形态的能力,多态就是同一个接口使用不同的实例而执行不同操作;(比如同一个打印机,可以打印黑白的纸张也可以打印彩色的,同样是人,却有黑人白人之分)

 

12.接口有什么用?

接口是一种规范。在这里举个例子:比如电插孔,多是两孔和三孔的那种,如果没有这种规范那每家电器公司都来做一种插孔的话,试想一下插头换了怎么办?是不是只能买原装的来替换了。

 

13.什么是TCP三次握手,四次挥手?

三次握手:

  • 第一次握手:客户端向服务器发送一个syn包,进入发送状态;
  • 第二次握手:服务器收到syn包,确认客户的syn,并向客户端发送syn+ack包,进入接受状态;
  • 第三次握手:客户端接收到来自服务的syn包信息,并向服务的发出ack包,之后两者进入TCP连接成功状态。

四次挥手:

  • 第一次挥手:客户端发送一个FIN,用来关闭客户端到服务器的数据传送,客户端进入结束等待状态;
  • 第二次挥手:服务器收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(SYN相同,一个FIN占用一个序号),服务器进入关闭等待状态;
  • 第三次挥手:服务器发送一个FIN,用来关闭服务器到客户端的数据传送,服务器进入最后确认状态;
  • 第四次挥手:客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个ACK给服务器,确认序号为收到序号+1,服务器进入关闭状态,完成四次挥手。

 

14.CookieSession有什么区别,分布式环境怎么保存用户状态?

Cookie存在客户端,Session存在服务端。

分布式Session的几种实现方式:

  • 基于数据库的Session共享
  • 基于NFS共享文件系统
  • 基于Memcached的Session,如何保证Memcached本身的高可用性
  • 基于Resin/Tomcat Web容器本身的Session复制机制
  • 基于TT/Redis或JBossCache进行Session共享
  • 基于Cookie进行Session共享(唯一值Token)

 

15.GitSvn有什么区别?

  • Git是分布式的,而Svn不是分布的。
  • Git下载下来后,在OffLine状态下可以看到所有的Log,Svn不可以。
  • Svn的特点是简单,只是需要一个放代码的地方时用是OK的,Git的特点是版本控制可以不依赖网络做任何事情,对分支和合并有更好的支持。

 

16.ThreadLocal可以用来共享数据吗?(答案是可以)

  • ThreadLocal主要用来解决多线程中数据因并发产生不一致问题。ThreadLocal为每个线程中并发访问的数据提供一个副本,通过访问副本来运行业务,这样耗费了内存,大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
  • ThreadLocal和Synchonized都用于解决多线程并发访问Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的共享。而Synchronized却正好相反,它用于多个线程间通信时能够获得数据共享。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值