Java常见面试题

目录

Java基础

谈谈对面向对象的理解?

访问修饰符的作用范围

String和StringBuilder、StringBuffer的区别? 

重载(Overload)和重写(Override)的区别?

throw和throws的区别?

接口与抽象类的区别?

Java中final关键字?

wait() 和 sleep() 方法的区别?

Thread 调用 start() 方法和调用 run() 方法的区别

synchronized 和 Lock 的区别

JVM常见问题

谈谈对JVM的理解?

什么情况下回发生栈内存溢出?

JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代?

你知道哪几种垃圾收集器,各自的优缺点?

CMS收集器和G1收集器的区别?

谈谈双亲委派机制?

强引用、软引用、弱引用、虚引用的区别?

spring常见问题

谈谈你对Spring的理解?

BeanFactory和ApplicationContext有什么区别?

谈谈spring中bean的生命周期?

SpringMVC常见问题

谈谈你对springmvc的理解?

SpringBoot常见问题

谈谈你对SpringBoot的理解?

说说SpringBoot的优点?

介绍一下 @SpringBootApplication 注解?

Spring Boot 的四大核心?

Spring Boot 自动配置原理是什么?

Spring Boot起步依赖?


Java基础

谈谈对面向对象的理解?

面向对象是一种编程思想,它注重的是结果而非过程,不需要所有事情都亲力亲为。面向对象是对一类事物共有的特点和行为的概括,是抽象的、不存在的。

面向对象有三大特征:封装、继承和多态

封装:将属性私有化,对外提供getter、setter方法,隐藏对象的属性和实现细节,对内部数据提供不同级别的保护。

继承:所有类都默认继承顶级父类Object,使用extends实现继承关系,子类拥有父类的所有非私有方法及属性,在Java中都是单继承,如果父类中的方法不能满足子类的需求,子类可以重写父类方法进行功能扩展。

多态:多态的前提是要有继承关系,并重写了父类中的方法,父类型指向子类型对象。遵循编译时看左运行时看右原则。

访问修饰符的作用范围

String和StringBuilder、StringBuffer的区别? 

String的值是常量,一旦被创建就无法修改,当对多个字符串拼接时,每次拼接得到的字符串都是一个新的对象,会占用大量内存。

StringBuilder是非线程安全的,它的append()方法对字符串进行操作时不会浪费内存。在单线程情况下推荐使用,效率较高。

StringBuffer是线程安全的,但是效率较低。

重载(Overload)和重写(Override)的区别?

重载:

  1. 方法名相同,参数列表不同(参数类型、参数个数、参数顺序)
  2. 目的,让方法在接收不同参数时实现不同功能

重写:

  1. 方法名和参数列表相同
  2. 返回值和抛出异常范围要小于等于父类
  3. 访问修饰符范围大于等于父类
  4. 有private和final修饰的方法不能被重写

throw和throws的区别?

  1. throw出现在方法中,用于方法执行时抛出一个指定的异常对象,而throws出现在参数列表和方法体之间,用于通知开发人员当前方法运行时,可能抛出的异常。
  2. 一个throw一次只能携带一个异常对象,throws后面可以携带多个异常类型
  3. 当方法中存在throw命令时,在调用时可以不考虑异常捕捉问题,当方法被throws修饰时,调用方法时必须考虑异常捕捉问题

接口与抽象类的区别?

接口:

  1. 作用是指定规则、降低耦合度
  2. 接口中属性默认都是静态常量属性
  3. 接口中方法都是抽象的,如果需要定义具体方法实现,要使用default修饰
  4. 接口中访问修饰符不能是private
  5. 接口与接口可以多继承,但接口之间不能互相实现
  6. 接口不存在构造方法

抽象类:

  1. 作用是降低接口实现类与接口之间实现难度
  2. 抽象类可以声明抽象方法,也可以生成具体方法
  3. 抽象类声明抽象方法必须有子类进行重写
  4. 抽象类实现接口时,不需要对接口方法进行重写
  5. 抽象类有构造方法,但不能使用

Java中final关键字?

被final修饰的类不能被继承

被final修饰的方法不能被重写

被final修饰的属性不能被修改,是常量

wait() 和 sleep() 方法的区别?

  1. sleep()来自Object类,wait()来自Thread类
  2. sleep()不会释放占有的锁,wait()会释放占有的锁
  3. sleep()可以再任何地方使用,wait()只能在同步控制方法或者同步控制块里面使用,否则会抛 IllegalMonitorStateException。
  4. sleep()在时间到了之后会重新恢复,wait()需要其他线程调用同一对象的notify()/nofityAll() 才能重新恢复。

Thread 调用 start() 方法和调用 run() 方法的区别

run():普通的方法调用,在主线程中执行,不会新建一个线程来执行

start():新启动一个线程,这时此线程处于就绪状态,并没有运行,一旦到的CPU时间片就开始执行run()方法。

synchronized 和 Lock 的区别

  1. Lock是一个接口,synchronized 是Java中的关键字
  2. Lock在发生异常时,如果没有主动通过unLock()释放锁,很可能会造成死锁现象,因此使用Lock时需要在finally块中释放锁,synchronized 不需要手动释放锁,在发生异常时会自动释放锁,因此不会导致死锁现象。
  3. Lock的使用更加灵活,可以有响应中断、超时时间等;而synchronized会一直等待下去,直到获取到锁。

JVM常见问题

谈谈对JVM的理解?

 JVM是可运行Java代码的假想计算机,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆和一个存储方法域、JVM是运行在操作系统之上的,它与硬件没有直接的交互。

什么情况下回发生栈内存溢出?

  • 栈是线程私有的,他的生命周期与线程相同,每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表,操作数栈,动态链接,方法出口等信息。局部变量表又包含基本数据类型,对象引用类型
  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常,方法递归调用产生这种结果。
  • 如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。(线程启动过多)
  • 参数 -Xss 去调整JVM栈的大小

JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代?

  • 当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。
  • 大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态;
  • 如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。即长期存活的对象进入老年态。
  • 老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和年老代。
  • Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10倍以上。

你知道哪几种垃圾收集器,各自的优缺点?

  • Serial收集器: 单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。
  • ParNew收集器: Serial收集器的多线程版本,也需要stop the world,复制算法。
  • Parallel Scavenge收集器: 新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量。如果虚拟机总共运行100分钟,其中垃圾花掉1分钟,吞吐量就是99%。
  • Serial Old收集器: 是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。
  • Parallel Old收集器: 是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。
  • CMS(Concurrent Mark Sweep) 收集器: 是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片。
  • G1收集器: 标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选标记。不会产生空间碎片,可以精确地控制停顿。

CMS收集器和G1收集器的区别?

  • CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
  • G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
  • CMS收集器以最小的停顿时间为目标的收集器;
  • G1收集器可预测垃圾回收的停顿时间
  • CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
  • G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。

谈谈双亲委派机制?

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

  • Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
  • ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
  • AppClassLoader:主要负责加载应用程序的主函数类

这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

强引用、软引用、弱引用、虚引用的区别?

  • 强引用:我们平时new了一个对象就是强引用,例如 Object obj = new Object();即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。
  • 软引用:如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
  • 弱引用:具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
  • 虚引用:如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

spring常见问题

谈谈你对Spring的理解?

Spring框架是一个轻量级的开源框架,是核心容器、数据访问与集成、AOP、Web、消息、测试六个模块的集成,主要是为了简化企业级应用的后台开发,降低耦合性。平时接触到最多的还是IoC和AOP两个特性。IoC指的是控制反转,把对象的创建和依赖关系的维护交给Spring容器去管理。Spring通过工厂模式、反射机制等技术管理对象的作用域和生命周期。AOP一般称为面向切面编程,是面向对象的一种补充,将程序中独立于其他功能的方法抽取出来,使Java开发模块化,仅需专注于主业务即可

BeanFactory和ApplicationContext有什么区别?

  1. BeanFactory是spring的原始接口,它的功能单一,只提供了实例化对象和取对象的功能。ApplicationContext由BeanFactory接口派生而来,因而提供BeanFactory所有的功能,并且扩展了很多高级特性:国际化(MessageSource)、访问资源,如URL和文件(ResourceLoader)、载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层、事件机制、AOP(拦截器)
  2. BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext在启动的时候就把所有的Bean都实例化了,这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
  3. .BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但BeanFactory需要手动注册,而ApplicationContext则是自动注册

谈谈spring中bean的生命周期?

大致分为五个阶段:

  1. 创建前准备
  2. 创建实例
  3. 依赖注入
  4. 容器缓存
  5. 销毁实例
  • 创建前准备:Bean在开始加载前要从上下文和一些配置中去解析并且查找Bean有关的扩展实现,比如init-method(初始化Bean时调用的方法)、destroy-method(销毁Bean时调用的方法)、BeanFactoryPostProcessor(加载过程中前置和后置的处理扩展实现)。
  • 创建实例:通过反射创建Bean的实例对象,并扫描解析Bean声明的属性
  • 依赖注入:被实例化的Bean如果依赖其他Bean对象,则需要对这些Bean进行对象注入。在这个阶段还会触发一些扩展的调用,比如BeanPostProcessor(实现Bean初始化前、后的扩展回调)
  • 容器缓存:将Bean保存到容器以及Spring的缓存中,此时Bean可以被使用了。init-method配置的方法在这个阶段被调用,以及BeanPostProcessor的后置处理器方法也被触发
  • 销毁实例:当ApplicationContext被关闭时,ApplicationContext中所有的Bean被销毁,如果Bean实现了DisposableBean接口或配置了destroy-method属性的方法会在这个阶段被调用

SpringMVC常见问题

谈谈你对springmvc的理解?

springmvc是一款基于MVC设计模式实现的轻量级web框架,可以帮助我们进行更简洁的web层开发,并且它天生与 Spring 框架集成,通过把Model,View,Controller分离,将web层进行职责解耦。Spring MVC 下我们一般把后端项目分为 Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。

SpringBoot常见问题

谈谈你对SpringBoot的理解?

SpringBoot主要用来简化使用Spring的难度和繁重的XML配置,它是Spring组件的一站式解决方案,采取了约定大于配置的方法。通过.properties或.yml文件替代了Spring繁杂的XML配置文件,同时支持@ImportResource注解加载XML配置。提供了各种启动器,使开发者能快速上手。

说说SpringBoot的优点?

  1. 简化开发,提高整体生产力。
  2. Spring Boot 使用JavaConfig有助于避免使用XML,同时避免大量的Maven导入和各种版本冲突。
  3. Spring Boot 应用程序提供嵌入式HTTP服务器,如Tomcat和Jetty,可以轻松地开发和测试web应用程序。
  4. Spring Boot 提供了多种插件,可以使用内置Maven工具开发和测试 应用程序
  5. Spring Boot 没有单独的 Web 服务器需要,这意味着不再需要启动 Tomcat或其他任何东西

介绍一下 @SpringBootApplication 注解?

Spring Boot 的核心注解是@SpringBootApplication,它也是启动类使用的注解,主要包含了 3 个注解:
@SpringBootConfiguration:它组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:具有打开自动配置的功能,也可以关闭某个自动配置的选项。
@ComponentScan:用于Spring组件扫描。

Spring Boot 的四大核心?

  • 自动装配:简单配置甚至零配置即可运行项目
  • 起步依赖:场景启动器
  • Actuator:指标监控
  • 命令行界面 :命令行

Spring Boot 自动配置原理是什么?

@EnableAutoConfiguration注解、 @Configuration注解和 @ConditionalOnClass注解组成了Spring Boot自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。具体是通过maven读取每个starter中的spring.factories文件,该文件配置了所有需要被创建在spring容器中的bean。

Spring Boot起步依赖?

在Spring Boot项目中,必须继承依赖spring-boot-starter-parent,其pom文件又继承了一个依赖spring-boot-dependencies,该文件管理了所有依赖的版本号;解决了我们原有项目中可能存在依赖版本冲突的问题,它来真正管理spring boot应用里面的所有依赖版本。spring boot帮我们打包了各个依赖让我们不用再像之前那样自己导入一大堆的依赖,只要引入起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。