Java中级面试常见题目+答案

@

一.基础知识

1.集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList;HashSet,TreeSet);

2.HashMap的底层实现,之后会问ConcurrentHashMap的底层实现;

3.如何实现HashMap顺序存储:可以参考LinkedHashMap的底层实现;

4.HashTable和ConcurrentHashMap的区别;

5.String,StringBuffer和StringBuilder的区别;

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

7.wait和sleep的区别,必须理解;

8.JVM的内存结构,JVM的算法;

9.强引用,软引用和弱引用的区别;

10.数组在内存中如何分配;

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

13.spring里面的aop的原理是什么;

17.说说http,https协议;

19.osi五层网络协议;

20.tcp,udp区别;

21.用过哪些加密算法:对称加密,非对称加密算法;

22.说说tcp三次握手,四次挥手;

23.cookie和session的区别,分布式环境怎么保存用户状态;

25.请写一段栈溢出、堆溢出的代码;

26.ThreadLocal可以用来共享数据吗

27.springboot的优点

28.Spring 如何解决循环依赖的问题

29.spring项目中静态方法中使用注入的bean

30.Java8新特性:
1、Lambda表达式;
2、方法引用;
3、默认方法;
4、新编译工具;
5、Stream API;
6、Date Time API;
7、Option;
8、Nashorn javascript引擎。

二.IO:

1.bio,nio,aio的区别;

2.nio框架:dubbo的实现原理;

三.算法:

1.java中常说的堆和栈,分别是什么数据结构;另外,为什么要分为堆和栈来存储数据

2.TreeMap如何插入数据:二叉树的左旋,右旋,双旋;

3.一个排序之后的数组,插入数据,可以使用什么方法?答:二分法;问:时间复杂度是多少?

4.平衡二叉树的时间复杂度;

7.快速排序 & 冒泡排序 & 堆排序 &排序复杂度

四. 多线程相关:

1.说说阻塞队列的实现:可以参考ArrayBlockingQueue的底层实现(锁和同步都行);

2.进程通讯的方式:消息队列,共享内存,信号量,socket通讯等;

5.Excutors可以产生哪些线程池;

6.为什么要用线程池;

8.线程池原理

9.高并发下接口幂等性的解决方案

  1. 获取子线程的返回值的方法:1.主线程等待。2.Join方法等待。3.实现Callable接口。

五.数据库相关(mysql)

2.mysql的语句优化,使用什么工具;

3.mysql的索引分类:B+,hash;什么情况用什么索引;

4.mysql的存储引擎有哪些,区别是什么;

5.说说事务的特性和隔离级别;

6.悲观锁和乐观锁的区别,怎么实现

  1. 数据库on where having的区别

8.MySQL的索引类型有哪些

六.mq

1.mq的原理是什么:

2.mq如何保证实时性;

3.mq的持久化是怎么做的;

七.nosql相关(主要是redis)

1.redis和memcache的区别

3.redis是如何持久化的:rdb和aof;

4.redis集群如何同步;

5.redis的数据添加过程是怎样的:哈希槽;

6.redis的淘汰策略有哪些;

7.redis有哪些数据结构;

八.zookeeper:

1.zookeeper是什么

2.zookeeper哪里用到;

3.zookeeper的选主过程;

4.zookeeper集群之间如何通讯;

5.你们的zookeeper的节点加密是用的什么方式;

6.分布式锁的实现过程;

7.springcloud五大组件

  1. 分布式系统数据一致性的解决方案

9.分布式的缺点

九.linux相关

1.linux常用的命令有哪些

2.如何获取java进程的pid;

3.如何获取某个进程的网络端口号;

4.如何实时打印日志;

5.如何统计某个字符串行数;

十.设计与思想

2.一千万的用户实时排名如何实现;

3.万人并发抢票怎么实现;

一.基础知识
1.集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList;HashSet,TreeSet);
ArrayList,LinkedList,Vector都属于List
List:元素是有顺序的,元素可以重复因为每个元素有自己的角标(索引)
|-- ArrayList:底层的数据结构是数组结构,特点是:查询很快,增 删 稍微慢点,线程不同步
|-- LinkedList:底层使用的是链表数据结构,特点是:增 删很快,查询慢。
|–Vector:底层是数组数据结构,线程同步,被ArrayList代替了,现在用的只有他的枚举。
Set:元素是无序的,且不可以重复(存入和取出的顺序不一定一致),线程不同步。
|–HashSet:底层是哈希表数据结构。根据hashCode和equals方法来确定元素的唯一性
|–TreeSet:可以对Set集合中的元素进行排序(自然循序),底层的数据结构是二叉树,
也可以自己写个类实现Comparable 或者 Comparator 接口,定义自己的比较器,将其作为参数传递给TreeSet的构造函数。
Map:这个集合是存储键值对的,一对一对往里存,而且要确保键的唯一性(01,张三)这样的形式打印出来就是 01=张三
|–HashTable:底层是哈希表数据结构,不可以存入null键和null值,该集合线程是同步的,效率比较低。出现于JDK1.0
|–HashMap:底层是哈希表数据结构,可以存入null键和null值,线程不同步,效率较高,代替了HashTable,出现于JDK 1.2
|–TreeMap:底层是二叉树数据结构,线程不同步,可以用于个map集合中的键进行排序
2.HashMap的底层实现,之后会问ConcurrentHashMap的底层实现;
HashMap由数组和链表来实现对数据的存储
HashMap采用Entry数组来存储key-value对,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有Next指针,可以连接下一个Entry实体,以此来解决Hash冲突的问题。
数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;
链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。

JDK 1.8的 改变:HashMap采用数组+链表+红黑树实现。
改变的地方,数据结构的存储由数组+链表的方式,变化为数组+链表+红黑树的存储方式,当链表长度超过阈值(8)时,将链表转换为红黑树。在性能上进一步得到提升。

Hashtable的同步会锁住整个数组。在高并发的情况下,性能会非常差,Java5中引入了java.util.concurrent.ConcurrentHashMap作为高吞吐量的线程安全HashMap实现,它采用了锁分离的技术允许多个修改操作并发进行

ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
Segment的get操作实现非常简单和高效。先经过一次再哈希,然后使用这个哈希值通过哈希运算定位到segment,再通过哈希算法定位到元素。get操作的高效之处在于整个get过程不需要加锁,除非读到的值是空的才会加锁重读,我们知道HashTable容器的get方法是需要加锁的,那么ConcurrentHashMap的get操作是如何做到不加锁的呢?原因是它的get方法里将要使用的共享变量都定义成volatile。
由于put方法里需要对共享变量进行写入操作,所以为了线程安全,在操作共享变量时必须得加锁。Put方法首先定位到Segment,然后在Segment里进行插入操作。

是否需要扩容。在插入元素前会先判断Segment里的HashEntry数组是否超过容量(threshold),如果超过阀值,数组进行扩容。值得一提的是,Segment的扩容判断比HashMap更恰当,因为HashMap是在插入元素后判断元素是否已经到达容量的,如果到达了就进行扩容,但是很有可能扩容之后没有新元素插入,这时HashMap就进行了一次无效的扩容

缺点:ConcurrentHashMap的size操作

3.如何实现HashMap顺序存储:可以参考LinkedHashMap的底层实现;
方法一:维护一张表,存储数据插入的顺序,可以使用vector。但是如果删除数据呢
首先得在vector里面找到那个数据,再删除,而删除又要移动大量数据,性能效率很低
使用list,移动问题可以解决,但是查找数据O的时间消耗,如果删除m次,那查找数据的性能就是0
那总体性能也是0.性能还是没法接受。
方法二:
可以在hashmap里维护插入顺序的id,在value建一个存储id值,在维护一张表vector,并且id对应vector里面的值。
插入的时候,id+ = 1,hashmao.insert, vector.push_back
删除的时候,先hashmao.find(key),得到value,并且从value中得到id,通过id把对应vector值设置为无效。
更新: 删除+ 插入。
维护工作OK了,输出的时候直接输出vector里面的值就可以了,无效的就continue.
算法福再度为O
方法三:
Java里面有个容器LinkedListHashMap,它能实现按照插入的顺序输出结果。
他的原理也是维护一张表,但他是链表,并且hashmap中维护指向链表的指针,这样可以快速定位链表中的元素
进行删除。
他的时间复杂度也是0,空间上比上面少些。
4.HashTable和ConcurrentHashMap的区别;
Hashtable所有的方法都是同步的,因此,它是线程安全的

Synchronized容器和Concurrent容器有什么区别?

在Java语言中,多线程安全的容器主要分为两种:Synchronized和Concurrent,虽然它们都是线程安全的,但是它们在性能方面差距比较大。

Synchronized容器(同步容器)主要通过synchronized关键字来实现线程安全,在使用的时候会对所有的数据加锁。需要注意的是,由于同步容器将所有对容器状态的访问都串行化了,这样虽然保证了线程的安全性,但是这种方法的代价就是严重降低了并发性,当多个线程竞争容器时,吞吐量会严重降低。于是引入了Concurrent容器(并发容器),Concurrent容器采用了更加智能的方案,该方案不是对整个数据加锁,而是采取了更加细粒度的锁机制,因此,在大并发量的情况下,拥有更高的效率。
————————————————

5.String,StringBuffer和StringBuilder的区别;
String类是不可变类
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
6.Object的方法有哪些:比如有wait方法,为什么会有;
锁可以是任意对象,所以任意对象调用方法一定定义在Object类中。
7.wait和sleep的区别,必须理解;
wait():释放资源,释放锁
sleep():释放资源,不释放锁
8.JVM的内存结构,JVM的算法;
1.方法区(Method Area)
2.堆区(Heap)
3.虚拟机栈(VM Stack)
4.本地方法栈(Native Method Stack)
5.程序计数器(Program Counter Register)

方法区存放了要加载的类的信息(如类名、修饰符等)、静态变量、构造函数、final定义的常量、类中的字段和方法等信息。方法区是全局共享的,在一定条件下也会被GC。

堆区是GC最频繁的,也是理解GC机制最重要的区域。堆区由所有线程共享,在虚拟机启动时创建。堆区主要用于存放对象实例及数组,所有new出来的对象都存储在该区域。

虚拟机栈占用的是操作系统内存,每个线程对应一个虚拟机栈,它是线程私有的,生命周期和线程一样,每个方法被执行时产生一个栈帧(Statck Frame),栈帧用于存储局部变量表、动态链接、操作数和方法出口等信息,当方法被调用时,栈帧入栈,当方法调用结束时,栈帧出栈。
局部变量表中存储着方法相关的局部变量,包括各种基本数据类型及对象的引用地址等,因此他有个特点:内存空间可以在编译期间就确定,运行时不再改变。
虚拟机栈定义了两种异常类型:StackOverFlowError(栈溢出)和OutOfMemoryError(内存溢出)。
本地方法栈用于支持native方法的执行,存储了每个native方法的执行状态。本地方法栈和虚拟机栈他们的运行机制一致,唯一的区别是,虚拟机栈执行Java方法,本地方法栈执行native方法。在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将虚拟机栈和本地方法栈一起使用。对于新手小白想更轻松的学好Java提升,Java架构,web开发、大数据,数据分析,人工智能等技术,这里给大家分享系统教学资源,扩列下我尉(同英):CGMX9880 【教程/工具/方法/解疑】

程序计数器是一个很小的内存区域,不在RAM上,而是直接划分在CPU上,程序猿无法操作它,它的作用是:JVM在解释字节码(.class)文件时,存储当前线程执行的字节码行号,只是一种概念模型,各种JVM所采用的方式不一样。
9.强引用,软引用和弱引用的区别;
强引用:new出来的对象都是强引用,GC无论如何都不会回收,即使抛出OOM异常。
软引用:只有当JVM内存不足时才会被回收。
弱引用:只要GC,就会立马回收,不管内存是否充足。
虚引用:它唯一的作用就是做一些跟踪记录,辅助finalize函数的使用。
10.数组在内存中如何分配;
Java数组的初始化,有以下两种方式,
1.静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度,如:
String[] names = new String[]{“多啦A梦”, “大雄”, “静香”};
2.动态初始化:初始化时由程序员显示的指定数组的长度,由系统为数据每个元素分配初始值,如:
String[] cars = new String[4]; //系统会默认给数组元素分配初始值为null
11.
12.springmvc的核心是什么,请求的流程是怎么处理的,控制反转怎么实现的;
1.核心:SpringMVC框架是以请求为驱动,围绕Servlet设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是DispatcherServlet,它是一个Servlet,顶层是实现的Servlet接口。

2.流程说明:
(1)客户端(浏览器)发送请求,直接请求到DispatcherServlet。
(2)DispatcherServlet根据请求信息调用HandlerMapping,

  • 3
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值