欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java面试题汇总---基础版(附答案)

程序员文章站 2022-04-23 15:13:06
基于我个人对面试的认知和招聘经验,在此我总结一下Java开发者的基础知识掌握要求,及应聘者面试的需要准备的内容。 首先,Java基础是每个面试官都会问到的,可能只是针对工作经验的多少,对问题追踪深度有所差异。基本对初中级开发者来说,基础理论和应用不可缺少。对中高级,面试官会基于基础理论问一些底层的原 ......

基于我个人对面试的认知和招聘经验,在此我总结一下java开发者的基础知识掌握要求,及应聘者面试的需要准备的内容。

首先,java基础是每个面试官都会问到的,可能只是针对工作经验的多少,对问题追踪深度有所差异。基本对初中级开发者来说,基础理论和应用不可缺少。对中高级,面试官会基于基础理论问一些底层的原理甚至对源码的理解。

一,jvm及工作原理

jvm --- java virtual machine,即java虚拟机。大家都知道java具有可跨平台特性,其主要是指字节码(.class文件)可以在任何具有java虚拟机的计算机上运行,java虚拟机中的java解释器负责将字节码文件解释成为特定的机器码进行运行。jvm的体系结构分为三部分,分别是:类加载器(classloader),运行时数据区和执行引擎。

1,类加载器:在jvm启动时或者在类运行时将需要的,class加载到jvm中。

2,运行时数据区:是在jvm运行的时候操作所分配的内存区,主要划分为5个区域:

  • 方法区(method area):用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。

  • java堆(heap):存储java实例或者对象的地方,也是gc的主要区域。

  • java栈(stack):java栈总是和线程关联在一起,每当创建一个线程时,jvm就会为这个线程创建一个对应的java栈。

  • 程序计数器(pc register):用于保存当前线程执行的内存地址。

  • 本地方法栈(native method stack):和java栈的作用差不多,只不过是为jvm使用到的native方法服务的

3,执行引擎:负责执行class文件中包含的字节码指令。

二,java垃圾回收机制

在java中,程序员是不需要去释放一个对象的内存的,而是由虚拟机自行执行。在jvm中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫描那些没有被任何引用的对象,并将它们添加到要回收的集合中进行回收。

  垃圾回收算法

  • 标记-清除 

  • 标记-复制 

  • 标记-整理 

  • 分代回收 

  那些对象应该被回收:

 这就要从对象存活性判断,常用的方法有两种:1.引用计数法;2.对象可达性分析。由于引用计数法存在互相引用导致无法进行gc的问题,所以目前jvm虚拟机多使用对象可达性分析算法。

三,接口和抽象类的区别

  • 默认方法:抽象类可以有默认的方法实现,接口中不存在方法的实现。

  • 实现方式:子类使用extends关键字来继承抽象类,如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现。而接口的子类使用implements来实现接口,需要提供接口中所有声明的实现。

  • 构造函数:抽象类中可以有构造函数,接口中不能。

  • 和正常类区别:抽象类不能被实例化,接口则是完全不同的类型。

  • 访问修饰符:抽象方法可以有public,protected和default等修饰,接口默认是public,不能使用其他修饰符。

  • 多继承:一个子类只能继承在一个抽象类,而一个子类可以实现多个接口。

  • 添加新方法:想在抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码。如果往接口中添加新方法,则子类中需要实现该方法。

四,string相关问题

string相关问题很常见,对初级开发者来说,string的基础使用方法必须掌握。

  1. strings=new string(“xyz”) 创建了几个string object?

    两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。new string每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新string对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。

  2. string与stringbuffer及stringbuilder的区别

    它们都可以储存和操作字符串,即包含多个字符的字符数据,主要区别:

    1)  string类表示内容不可改变的字符串,而stringbuffer和stringbuilder类都表示内容可以被修改的字符串。

    2)string实现了equals方法,new string(“abc”).equals(new string(“abc”)的结果为true。而stringbuffer和stringbuilder没有实现equals方法,所以,new stringbuffer(“abc”).equals(new stringbuffer(“abc”)的结果为false。

  3. stringbuffer与stringbuilder的区别

    stringbuffer和stringbuilder都表示内容可以被修改的字符串,stringbuffer是线程安全的,而stringbuilder是线程不安全的,它相比stringbuffer运行效率高。如果一个字符串变量是在方法里面定义,这种情况只可能有一个线程访问它,不存在不安全因素了,则用stringbuilder。如果要在类里面定义成员变量,并且这个类的实例对象会在多线程环境下使用,那么最好用stringbuffer。

四,线程、进程相关

  1. 线程和进程的区别

    简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。

  2. 如何创建线程,他们有什么区别?

    创建线程可以实现java.lang.runnable或者通过继承java.lang.thread类两种方法。这两种方法的区别:

    1)java不支持多继承。因此扩展thread类就代表这个子类不能扩展其他类。而实现runnable接口的类还可能扩展另一个类。

    2)类可能只要求可执行即可,因此继承整个thread类的开销过大。

  3. thread的start()和run()方法的区别?

    start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

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

    1)sleep()来自thread类,和wait()来自object类。调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁;

    2)sleep()睡眠后不出让系统资源,wait让其他线程可以占用cpu;

    3)sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒,而wait()需要配合notify()或者notifyall()使用;

  5. 什么导致线程阻塞,怎么唤醒一个已经阻塞的线程?

    线程阻塞指的是暂停一个线程的执行以等待某个条件发生。java 提供了大量方法来支持阻塞,下面让我们逐一分析。

    1)sleep() 允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,指定的时间一过,线程重新进入可执行状态。

    2)suspend()和resume() 这两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。

    3)yield()  使当前线程放弃当前已经分得的cpu 时间,但不使当前线程阻塞,即线程仍处于可执行状态,随时可能再次分得 cpu 时间。

    4)wait()和notify() 这两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

    唤醒阻塞线程:如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出interruptedexception来唤醒它;如果线程遇到了io阻塞,无能为力,因为io是操作系统实现的,java代码并没有办法直接接触到操作系统。

  6. 产生死锁的条件

    1)互斥条件:一个资源每次只能被一个进程使用。 
    2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 
    3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 
    4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

  7. 线程锁(threadlocal)的作用

    简单说threadlocal就是一种以空间换时间的做法,在每个thread里面维护了一个threadlocal.threadlocalmap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。

  8. 乐观锁与悲观锁

    乐观锁:乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较、替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。

    悲观锁:它竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管其他,直接上了锁就操作资源了。

五,java集合

关于java中的集合体系是每个人都应该烂熟于心的,尤其是对我们经常使用的list、set、map的原理更该如此。

  1. 集合与数组的区别?

    1)数组是固定长度的,集合可变长度的;

    2)数组可以存储基本数据类型,也可以存储引用数据类型。集合只能存储引用数据类型;

    3)数组存储的元素必须是同一个数据类型,而集合存储的对象可以是不同数据类型;

 

java集合的层次关系

如图所示,图中实线边框的是实现类,折线边框的是抽象类,点线的是接口。Java面试题汇总---基础版(附答案)

 

collection是集合类的根接口,java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口list和set。list是一个有序、可重复的集合,提供了按索引访问的方式。set中不能包含重复的元素。

map是java.util包中的另一个接口,它和collection接口没有关系,是相互独立的,但是都属于集合类的一部分。map包含了key-value对。map不能包含重复的key,但是可以包含相同的value。

iterator,所有的集合类都实现了iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:

    1)hasnext()是否还有下一个元素。

    2)next()返回下一个元素。

    3)remove()删除当前元素

 

  1. list、set及map的区别

    1)list(有序、可重复)

    list里存放的对象是有序的,同时也是可以重复的,list关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。

    2)set(无序、不能重复)

    set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。

    3)map(键值对、键唯一、值不唯一)

    map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。

  2. arraylist和array有什么区别?

    1)array可以容纳基本类型和对象,而arraylist只能容纳引用类型对象;

    2)array是指定大小的,而arraylist大小是固定的;

  3. arraylist和linkedlist的区别?

    arrraylist底层的数据结构是数组,支持随机访问,而 linkedlist 的底层数据结构是双向循环链表,不支持随机访问。使用下标访问一个元素,arraylist 的时间复杂度是 o(1),而 linkedlist 是 o(n)。arrraylist查询快,增删改慢,而linkedlist增删快,却查询慢。

  4. arraylist和vector的区别?

    arraylist和vector都实现了list接口,他们都是有序集合,并且存放的元素是允许重复的。它们的底层都是通过数组来实现的,因此列表这种数据结构检索数据速度快,但增删改速度慢。

    arraylist和vector的区别:

    1)线程安全。vector是线程安全的,而arraylist是线程不安全的。因此在如果集合数据只有单线程访问,那么使用arraylist可以提高效率。而如果有多线程访问你的集合数据,那么就必须要用vector,因为要保证数据安全。

    2)数据增长。arraylist和vector都有一个初始的容量大小,当存储进它们里面的元素超过了容量时,就需要增加它们的存储容量。arraylist每次增长原来的0.5倍,而vector增长原来的一倍。arraylist和vector都可以设置初始空间的大小,vector还可以设置增长的空间大小,而arraylist没有提供设置增长空间的方法。

  5. hashmap和hashtable的区别

    hashmap和hashtable都实现了map接口,并且都是key-value的数据结构。它们的不同点主要在三个方面:

    1)hashtable是java1.1的一个类,它基于陈旧的dictionary类。而hashmap是java1.2引进的map接口的一个实现。

    2)hashtable是线程安全的,也就是说是线程同步的,而hashmap是线程不安全的。也就是说在单线程环境下应该用hashmap,这样效率更高。

    3)hashmap允许将null值作为key或value,但hashtable不允许(会抛出nullpointerexception)。

  6. linkedhashmap及treemap

    linkedhashmap保存了记录的插入顺序,在用iteraor遍历linkedhashmap时,先得到的记录肯定是先插入的,在遍历的时候会比hashmap慢,有hashmap的全部特性。

    treemap实现sortmap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用iterator遍历treemap时,得到的记录是排过序的。不允许key值为空,非同步的;

六,异常及异常处理相关

异常是发生在程序执行过程中阻碍程序正常执行的错误事件。比如:用户输入错误数据、硬件故障、网络阻塞等都会导致出现异常。

  1. error和exception有什么区别

    error表示系统级错误,是java运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除退出运行外别无选择,它是java虚拟机抛出的。

    exception 表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序必须处理的问题。

    Java面试题汇总---基础版(附答案)

     

  2. 运行时异常和一般异常有何不同

    java提供了两类主要的异常:runtimeexception和checkedexception

    一般异常(checkedexception)主要是指io异常、sql异常等。对于这种异常,jvm要求我们必须对其进行处理。

    运行时异常(runtimeexception)一般不处理,当出现这类异常时程序会由虚拟机接管。比如nullpointerexception,而且这个异常也是最常见的异常之一。

    出现运行时异常的时候,程序会将异常一直向上抛,一直抛到遇到处理代码,如果没有catch块进行处理,到了最上层,如果是多线程就有thread.run()抛出,如果不是多线程那么就由main.run()抛出。抛出之后,如果是线程,那么该线程也就终止了,如果是主程序,那么该程序也就终止了。

  3. throw和throws的区别

    throw用于主动抛出java.lang.throwable 类的一个实例化对象,意思是说你可以通过关键字 throw 抛出一个 error 或者 一个exception,如:throw new illegalargumentexception(“size must be multiple of 2′)。

    而throws 的作用是作为方法声明和签名的一部分,方法被抛出相应的异常以便调用者能处理。在java 中,任何未处理的受检查异常强制在 throws 子句中声明。

  4. 你平时在项目中是怎样对异常进行处理的。

    1)尽量避免出现runtimeexception 。例如对于可能出现空指针的代码,带使用对象之前一定要判断一下该对象是否为空,必要的时候对runtimeexception也进行try catch处理。

    2)进行try catch处理的时候要在catch代码块中对异常信息进行记录,通过调用异常类的相关方法获取到异常的相关信息,返回到web端,不仅要给用户良好的用户体验,也要能帮助程序员良好的定位异常出现的位置及原因。

    3)使用自定义异常,在业务代码中将可预测的异常使用自定义异常抛出,最后通过aop在最外层统一捕获,并根据异常类型封装返回。

七,spring框架

spring 是个java企业级应用的开源轻量级开发框架。spring主要用来开发java应用,但是有些扩展是针对构建j2ee平台的web应用。spring 框架目标是简化java企业级应用开发,并通过pojo为基础的编程模型促进良好的编程习惯。

  1. spring框架的主要功能及好处?

    控制反转:spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。

    面向切面的编程(aop):spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。

    容器:spring 包含并管理应用中对象的生命周期和配置。

    mvc框架:spring的web框架是个精心设计的框架,是web框架的一个很好的替代品。

    事务管理:spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(jta)。

    异常处理:spring 提供方便的api把具体技术相关的异常(比如由jdbc,hibernate or jdo抛出的)转化为一致的unchecked 异常。

  2. 谈谈你对spring ioc和aop的理解

    ioc即控制反转。就是对象的创建权反转交给spring,由容器控制程序之间的依赖关系,作用是实现了程序的解耦合,而非传统实现中,由程序代码直接操控。最直观的表达就是,ioc让对象的创建不用去new了,可以由spring自动生产,这里用的就是java的反射机制,通过反射在运行时动态的去创建、调用对象。spring就是根据配置文件在运行时动态的去创建对象,并调用对象的方法的。

    spring的ioc有三种注入方式 : 

           1)根据属性注入,也叫set方法注入; 

           2)根据构造方法进行注入; 

           3)根据注解进行注入(autowired和resource)。

    aop即面向切面编程,作为面向对象的一种补充,用于解剖封装好的对象内部,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”(aspect),切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。

    aop实现的关键在于aop框架自动创建的aop代理,aop代理主要分为静态代理和动态代理。静态代理的代表为aspectj;动态代理则以spring aop为代表。

    1)aspectj是静态代理的增强,所谓静态代理,就是aop框架会在编译阶段生成aop代理类,因此也称为编译时增强,他会在编译阶段将aspectj织入到java字节码中,运行的时候就是增强之后的aop对象。

    2)spring aop使用的动态代理,所谓的动态代理就是说aop框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个aop对象,这个aop对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

    spring aop中的动态代理主要有两种方式,jdk动态代理和cglib动态代理:

        ①jdk动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。jdk动态代理的核心是invocationhandler接口和proxy类。生成的代理对象的方法调用都会委托到invocationhandler.invoke()方法,当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。

         ②如果目标类没有实现接口,那么spring aop会选择使用cglib来动态代理目标类。cglib(code generation library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法,覆盖方法时可以添加增强代码,从而实现aop。cglib是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用cglib做动态代理的。

    3)静态代理与动态代理区别在于生成aop代理对象的时机不同,相对来说aspectj的静态代理方式具有更好的性能,但是aspectj需要特定的编译器进行处理,而spring aop则无需特定的编译器处理。

    ioc让相互协作的组件保持松散的耦合,而aop编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

  3. springmvc的原理及执行流程?

     

    springmvc核心是前端控制器dispatcherservlet,一个请求的执行流程:

    1)用户发送请求到前端控制器dispatcherservlet;

    2)dispatcherservlet收到请求后,调用handlermapping处理器映射器,请求获取handle处理器;

    3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给dispatcherservlet;

    4)dispatcherservlet通过handleradapter处理器适配器调用处理器;

    5)处理器处理业务,完成后返回modelandview;

    6) handleradapter将handler执行结果modelandview返回给dispatcherservlet;

    7)dispatcherservlet再将modelandview传给viewresolver视图解析器进行解析;

    8)viewresolver解析后返回具体view;

    9)dispatcherservlet对view进行渲染视图(即将模型数据填充至视图中),并响应给用户。

八,数据库

数据库也是java开发者的面试中的重点,这里只说说常见的数据库面试题。

  1. 数据库三范式?

    第一范式(1nf):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。 

    第二范式(2nf):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。 

    第三范式(3nf):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在”a → b → c”的决定关系,则c传递函数依赖于a。

  2. 数据库事务的四大特性?

    1) 原子性(atomicity)

    事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。回滚可以用日志来实现,日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。

    2)一致性(consistency)

    数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。

    3)隔离性(isolation)

    一个事务所做的修改在最终提交以前,对其它事务是不可见的。

    4)持久性(durability)

    一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。可以通过数据库备份和恢复来实现,在系统发生崩溃时,使用备份的数据库进行数据恢复。

  3. 索引是什么?有什么作用以及优缺点?

    1)什么是索引【index】

    是一种快速查询表中内容的机制,类似于新华字典的目录;运用在表中某个些字段上,但存储时,独立于表之外;

    2)什么时候【要】创建索引

    (1)表经常进行 select 操作

    (2)表很大(记录超多),记录内容分布范围很广

    (3)列名经常在 where 子句或连接条件中出现

    3什么时候【不要】创建索引

    (1)表经常进行 insert/update/delete 操作

    (2)表很小(记录超少)

    (3)列名不经常作为连接条件或出现在 where 子句中

    4)索引优缺点:

    (1)索引加快数据库的检索速度

    (2)索引降低了插入、删除、修改等维护任务的速度(虽然索引可以提高查询速度,但是它们也会导致数据库系统更新数据的性能下降,因为大部分数据更新需要同时更新索引)

    (3)唯一索引可以确保每一行数据的唯一性,通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能

    (4)索引需要占物理和数据空间

关于数据库表设计等,今天就不多说了,毕竟这需要一定的积累,不会一时不会就能说完的,如果你想了解和学习,可以关注此公众号,添加微信私聊。

Java面试题汇总---基础版(附答案)