JAVA面试题目(一)--阿里巴巴--2019-08-04

这篇博客深入剖析了Java面试中常见的问题,涵盖Java基础、SpringMVC工作流程及其组件说明,以及Java多线程常见问题。讨论了String类为何为final、HashMap的实现原理、反射中Class.forName与ClassLoader的区别、session与cookie的对比、队列的不同类型及其实现原理。此外,还探讨了Java内存模型、GC算法、Spring框架的jar包用途、SpringMVC的处理流程以及线程池的工作模式。文章最后涉及线程间通信的同步机制,如互斥锁、信号量和条件变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Java基础和高级

1.String类为什举是final的。

   用处有三:1)常量池共享 2)线程安全 3)hashCode的键值最好选择

2.HashMap的源码,实现原理,底层结构。

   HashMap底层使用数组+链表+红黑树实现,putVal在链表结点数大于8的时候自旋成红黑树,使得查询速度由O(n)提升为O(log n)

3.反射中,Class.forName 和classloader的区别

   Class.forName不但会加载.class文件到jvm中还会执行static块代码,classloader只会将.class文件加载到jvm只有当执行newInstance的时候都会执行static代码。

4.session和cookie的区别和联系,session的生命周期,多个服务部署时session管理。

   session存储在server端,cookie存储在客户端,cookie利用server返回的信息保存,cookie如果不设过期时间会长久保留在客户端,否则浏览器关闭的时候就会清空cookie信息。server当浏览器关闭的时候session注销。

   多个服务器部署时可以有几种方案:1)session信息保存到cookie中,浏览器访问服务时带回即可。2)session请求粘贴,利用负载将同一个客户端的请求都发到一个server上。3)利用独立的redis缓存服务器来存储session信息,实现共享。redis可以做成集群,定时copy session信息

5.Java中的队列都有哪些,有什举区别。

   Java中队列是一个先进先出的数据结构,通常不能随机访问,根据实现的方式不同主要有两种方式:数组 & 链表。如下图:

在并发编程中,保证线程安全的队列有阻塞队列和非阻塞队列之分。

   阻塞队列在队列为空时或队列满的时候,获取元素的线程会等待队列变为非空,当队列为满时,存储元素的线程会等待队列可用。阻塞队列有以下几种:

  • ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
  • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

阻塞队列的实现原理:添加元素的最后一行,notEmpty.signal();表示添加完元素后,调用signal通知等待的线程,队列不空了,可以来取东西的。移除元素的最后一行,也是这样的。

非阻塞的方式可以循环使用CAS的方式来实现,非阻塞队列的一个实现方式为concurrentLinkedQueue,这是一个基于链接节点的无界线程安全队列,入队和出队操作均使用CAS(Compare and set) 更新,这样允许多个线程并发执行,并且不会因为加锁而阻塞线程。 

6.Java的内存模型以及GC算法

   JAVA 1.8内存模型,JVM内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈,如下图所示:

1. 每个线程都有一个私有的栈,随着线程的创建而创建。栈里面存放着栈桢的东西,栈桢中存放了局部变量表。当栈调用深度超过JVM所允许的范围内之后,会抛出StackOverflow异常。

2.本地方法栈,这里面主要存放着虚拟机用到的native方法相关,一般不需要考虑。

3.PC寄存器,JVM支持多个线程同时运行,每个线程都有自己的程序计数器。

4. 堆,堆内存是JVM所有线程共享的部分,在虚拟机启动的时候创建。所有的对象和数组都在堆上进行分配,这部分的空间可以通过GC来回收,当申请不到空间后就会抛出OutOfMemoryError.

5.方法区,该区也是所有线程共享,主要存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。

GC算法主要分为:1)标记-清理。 2)复制算法。3)标记-整理算法 4)分类算法。

1)标记-清理,分为两个阶段,一:标记所需要回收的对象,二:回收被标记的对象。标记算法分为引用计数算法和可达性算法,由于引用计数算法无法解决循环引用 的问题,所以 一般会采用可达性算法。这种算法虽然清理了空间,但是空间不连续,利用率低。

2)复制算法,它将内存分为现块相等的区域,每次使用一块,当这一块使用完了,就将还存活的对象按照原有的顺序复制到另一块内存中,然后就当前空间一次性清理掉。内存开销较大。

3)标记-整理算法,标记跟1)一样,整理是将存活的对象内存一段移动,整理出一块较大的连续内存空间

 

7.Java7、Java8的新特性

  Java7. 子类自动装箱。整型类型表达可用"_”拼接表达。catch多个exception. file文件读自动关闭。

   Java8 lamada表达式。信息stream的使用

8.Java 数组和链表两种结构的操作效率,在哪些情冴下(从开头开始,从结尾开始,从中间开始),哪些操

作(插入,查找,删除)的效率高

   数组查询时间O(1), 链表查询时间O(n) , 插入、删除效率高。

9.Java内存泄露的问题调查定位:jmap,jstack的使用等等

 

二、spring框架

spring框架中需要引用哪些jar包,以及这些jar包的用途

spring-web:开发web应用的时候会用到,包括自动载入webapplicationContext特性的类。

springg-core: 这个jar文件包含spring框架基本的核心工具类,

spring-aop:若要使用spring aop特性所需要的类

spring-beans:它包含访问配置文件,创建和管理bean以及进行控制反转的各个工具类。

spring-webmvc:包含mvc框架相关的核心类,包括国际化、标签、theme、视图展示、

spring-expression

spring-context: 这个包为spring提供扩展

srpingMVC的原理:

SpringMVC的工作原理图:

SpringMVC流程

1、  用户发送请求至前端控制器DispatcherServlet。

2、  DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、  处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、  DispatcherServlet调用HandlerAdapter处理器适配器。

5、  HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

6、  Controller执行完成返回ModelAndView。

7、  HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8、  DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9、  ViewReslover解析后返回具体View。

10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11、 DispatcherServlet响应用户。

组件说明:

以下组件通常使用框架提供实现:

DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。

HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 

HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器。

ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。

springMVC注解的意思:

spring中beanFactory和ApplicationContext的联系和区别

beanFactory是spring最底层的功能,提供了容器功能,只提供了实例化对象和取对象的基本功能,它是延迟加载的策略,只有当调用的时候spring才会创建对象。

ApplicationContext继承beanFactory接口,是一种更高级的容器,提供了更多的功能:1)国际化 2)访问资源 3)消息发送及响应。4)aop.

spring注入的几种方式

三种方式:1)属性的set方法注入 2)使用构造器注入 3)使用Field注入(注解方式)

spring如何实现事物管理的

spring aop 面向切面编程实现需要开户事务的service拦截。

springIOC和AOP的原理

IOC 原理参考:https://mp.youkuaiyun.com/postedit/97909819

AOP原理参考:https://mp.youkuaiyun.com/postedit/97926469

spring中循环注入的方式

Spring 循环注入其实就是循环依赖。比如多个对象之间的相互依赖。出现循环注入的方式有三种:

构造器注入,setting注入,自动注入。

构造器注入: 此依赖无法解决、

setting是通过spring容器提前暴露刚完成构造器注入但是未完成其他步骤(如setting注入等)的bean来完成的,而且只能解决单例作用域的bean循环依赖。

Spring的beanFactory和factoryBean的区别

beanFactory是Ioc容器的核心接口,它的职责包括:实例化 、定位、配置应用程序中的对象的建立这些对象间的依赖、

ApplicationContext是beanFactory的扩展,功能得到进一步的增强,与aop集成,用得最多的beanFactory的子类是ClassPathXmlApplicationContext..

Spring为我们提供了两种bean,一种是普通的bean,一种是FactoryBean,实现了FactoryBean接口的类有能力改变bean的行为。FactoryBean希望你实现了它之后返回一些内容。spring会按照这些内容生成bean

Spring的事务隔离级别,实现原理

Spring事务的隔离级别是与数据库保持一致的

隔离级别隔离级别的值导致的问题
Read-Uncommitted0导致脏读
Read-Committed1避免脏读,允许不可重复读和幻读
Repeatable-Read2避免脏读,不可重复读,允许幻读
Serializable3串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重

Spring事务的传播机制,有大概7种:

常量名称常量解释
PROPAGATION_REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 Spring 默认的事务的传播。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚之后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也可以不处理回滚操作
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED

如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop 的实现原理,说说 aop 中的几个术语,它们是怎举相互工作的?

spring boot特性,优势,适用场景等

 

三、java多线程常见问题

1.Java创建线程后,直接调用start()方法和run()的区别

   在同一个线程内部可多次执行run方法,start只能启动一次。

    start真正的多线程运行,这里无须等待run方法体执行完成,就可以继续执行新的thread.start方法 。

    run只是普通方法来使用,程序还要顺序执行,要等待run方法执行完毕后,才可以继续执行下面的代码。

2.常用的线程池模式以及不同线程池的使用场景

 java使用Executor管理线程池,底层使用ThreadPoolExecutor

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

corePoolSize:池中所保存的线程数,包括空闲线程

maximumPoolSize-池中允许的最大线程数。

keepAliveTime - 当线程数大于核心时,多于的空闲线程最多存活时间

unit - keepAliveTime 参数的时间单位。

workQueue - 当线程数目超过核心线程数时用于保存任务的队列

RejectedExecutionHandler 线程的饱和策略,默认的是abortPolicy(表示无法处理新任务),这时会抛出handler里面的rejectedExecution方法通知调用者,并抛出rejectedExecutionException。还有另外3个策略:DiscardPolicy/DiscardOldestPolicy/CallerRunsPolicy,分别解释如下:

(1)CallerRunsPolicy:用调用者所在的线程由处理原任务改为处理新任务
(2)DiscardPolicy:抛弃新任务
(3)DiscardOldestPolicy:丢弃队列中最近的任务,并执行新任务

其它的还有几个executor实现类:

newCachedThreadPoo l创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool  创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool  创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor  创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 

 

3.newFixedThreadPool此种线程池如果线程数达到最大值后会怎举办,底层原理。

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,它采用的是LinkedBlockingQueue,是无界队列,任务可以一起添加进去。所以newFixedThreadPool会保持nThread个线程在线程池中,而且是按顺序从LinkedBlockingQueue中取任务。若提交的任务比完成的任务时间快时,会一直添加任务,导致LinkedBlockingQueue一直增长,消耗大量内存。

 

4.多线程之间通信的同步问题。

     

多线程之间的通信主要是为了解决同步和异步的问题。

   实现方法:线程间的通信可以通过:1)互斥锁(mutex)。 2)信号量(semphore)。 3)条件变量(condition)。4)读写锁(reader-writer lock)

   互斥锁:是一个变量,它有Lock和unlock两个状态。互斥锁一般会被设置成全局变量,打开的互斥锁可以由某个线程获得,一旦获得,这个互斥锁会再锁上,这样,只有该线程才能有权打开。其它想要获取互斥锁的线程,要等到互斥锁再次打开的时候。

   信号量。信号量和互斥锁的区别是,互斥锁只允许一个线程进行临界区,而信号量允许多个线程同时进入临界区。

   条件变量:condition

   读写锁:volatile修饰的变量直接存放在main memory里面,volatile能保证所修饰的变量对于多个线程可见,即只要被修改,其它线程就一定能读到最新的值。

   JAVA通过wait . notify, notifyAll三个方法来解决线程间通信问题。wait可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到再次被唤醒。notify可以使调用该方法的线程获取共享资源的锁,进行运行状态。 

5.了解可重入锁的含义以及ReentrantLock 和synchronized的区别

    可重入锁的举例:当一个线程执行到synchronized方法method1时,而method1中调用了另一个synchronized方法,method2,此线程不需要重新申请一把锁可以直接执行method2. 

   Synchronized 和 ReentrantLock都是可重入锁。Synchronized是不可中断锁,而ReentrantLock是可中断锁,如果线程a正在执行锁中的代码时间过长,可以自己中断或者在别的线程上中中断它。而synchronized是不可以的。Synchornized和ReentrantLock都是非公平锁,但是ReentrantLock可以开启公平锁。

6.同步的数据结构,例如 concurrentHashMap 的源码理解以及内部实现原理,为什举他是同步的且效率高

   ConcurrentHashMap: 
   

static class Node<K,V>{

      final int hash;

      final K key;

      volatile V val;

     volatile Node<K,V> next;

   }

7.atomicinteger 和volatile等线程安全操作的关键字的理解和使用

8.线程间通信,wait和notify

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值