高频java工程师面试题及答案_第1页
高频java工程师面试题及答案_第2页
高频java工程师面试题及答案_第3页
高频java工程师面试题及答案_第4页
高频java工程师面试题及答案_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

高频java工程师面试题及答案Java基本数据类型包括byte(1字节)、short(2字节)、int(4字节)、long(8字节)、float(4字节)、double(8字节)、char(2字节)、boolean(未明确定义,通常认为1位或1字节)。基本数据类型存储在栈中(除long和double外,部分JVM可能将其存储为64位),而包装类是对象,存储在堆中。自动装箱(如Integeri=10)和拆箱(intj=i)由编译器自动提供代码完成,但需注意空指针问题——当包装类对象为null时,拆箱会抛出NullPointerException。例如,Integera=null;intb=a;会直接报错。String、StringBuilder、StringBuffer的核心区别在于可变性和线程安全性。String内部使用finalchar[]value存储,因此不可变,每次修改都会提供新对象;StringBuilder内部使用非final的char[],支持可变操作,且无同步机制,线程不安全;StringBuffer的方法大多被synchronized修饰,线程安全,但性能略低于StringBuilder。实际开发中,单线程字符串拼接推荐StringBuilder(如循环内拼接),多线程环境用StringBuffer,而字符串常量拼接(如"a"+"b")编译器会优化为String,避免频繁创建对象。面向对象的四大特性是封装、继承、多态、抽象。封装通过访问控制符(private、protected、public)隐藏内部实现,仅暴露必要接口;继承允许子类复用父类代码,通过extends关键字实现,但需注意Java单继承限制(类只能继承一个父类,可实现多个接口);多态表现为同一方法在不同对象上有不同行为,依赖继承、重写和父类引用指向子类对象(如Animala=newCat();a.speak());抽象通过抽象类(含抽象方法)或接口定义规范,强制子类实现具体逻辑。例如,定义Shape抽象类包含area()方法,Circle和Rectangle子类各自实现计算面积的逻辑,体现抽象与多态。重载(Overload)和重写(Override)的区别:重载发生在同一类中,方法名相同但参数列表不同(参数类型、个数、顺序),与返回值和访问修饰符无关(如publicvoidtest(inta)和publicStringtest(Stringb)构成重载);重写发生在子类与父类之间,方法名、参数列表、返回值(协变返回类型允许子类返回值是父类返回值的子类)必须相同,访问修饰符不能比父类更严格(父类protected,子类不能是private),且不能重写父类的final、static方法(static方法属于类,子类重写实际是隐藏)。例如,Object的equals方法被String重写,实现内容比较而非引用比较。接口(Interface)和抽象类(AbstractClass)的设计目的不同:接口是对行为的抽象,定义“能做什么”,类可实现多个接口;抽象类是对结构的抽象,定义“是什么”,类只能继承一个抽象类。接口中的方法默认是publicabstract(Java8前),Java8后支持default和static方法;抽象类中可包含普通方法、抽象方法、成员变量。接口的变量默认是publicstaticfinal(常量);抽象类的变量可以是任意访问修饰符。例如,定义Fly接口包含fly()方法,Bird和Plane类实现该接口;而Animal抽象类包含name属性和抽象方法eat(),Dog和Cat子类继承并实现eat()。异常处理中,Throwable是所有异常的父类,分为Error(JVM无法处理的严重问题,如OutOfMemoryError、StackOverflowError,不可捕获)和Exception(可处理的异常)。Exception又分为RuntimeException(运行时异常,如NullPointerException、ArrayIndexOutOfBoundsException,可捕获可不捕获)和检查型异常(CheckedException,如IOException、SQLException,必须显式处理)。try-with-resources是Java7引入的语法,用于自动关闭实现AutoCloseable接口的资源(如文件流、数据库连接),避免资源泄漏。例如:try(FileInputStreamfis=newFileInputStream("test.txt")){...}会自动调用fis.close(),即使发生异常。泛型的作用是在编译期提供类型安全检查,避免运行时类型转换异常。类型擦除是泛型的实现机制,编译后的字节码中泛型信息会被擦除,替换为上限类型(无限制时为Object)。例如,List<String>和List<Integer>在运行时是相同的Class对象(List.class)。泛型方法通过<T>声明,可定义在普通类或泛型类中;泛型类通过类名后加<T>声明。通配符包括?(无界通配符,如List<?>)、?extendsT(上界通配符,允许T及其子类)、?superT(下界通配符,允许T及其父类)。例如,使用List<?extendsNumber>接收List<Integer>或List<Double>,但无法添加元素(除null),因为无法确定具体类型;而List<?superInteger>可添加Integer及其子类对象,但取出时只能作为Object处理。反射机制允许程序在运行时获取类的信息(如字段、方法、构造器)并操作对象。核心类包括Class(表示类的元数据)、Field(字段)、Method(方法)、Constructor(构造器)。获取Class对象的方式有:类名.class(如User.class)、对象.getClass()(如newUser().getClass())、Class.forName("全类名")(如Class.forName("com.example.User"))。反射的应用场景包括框架(如Spring通过反射实例化Bean)、注解处理(如JUnit通过反射调用测试方法)、动态代理(如JDK动态代理基于反射实现)。但反射会绕过编译期检查,可能破坏封装性,且性能低于直接调用(可通过设置setAccessible(true)提高访问效率,但受安全管理器限制)。线程的创建方式有三种:继承Thread类(重写run()方法)、实现Runnable接口(重写run(),传入Thread构造器)、实现Callable接口(重写call(),可返回结果并抛出异常,通过FutureTask包装后传入Thread)。其中,Runnable和Callable更推荐,因为Java单继承限制,继承Thread会导致无法继承其他类。线程的生命周期包括新建(NEW)、就绪(RUNNABLE,等待CPU调度)、运行(RUNNING,实际执行)、阻塞(BLOCKED,等待锁)、等待(WAITING,调用wait()/join()无超时)、超时等待(TIMED_WAITING,调用sleep(time)/wait(time)/join(time))、终止(TERMINATED)。注意,Java线程状态由Thread.State枚举定义,其中RUNNABLE包含操作系统中的运行态和就绪态,BLOCKED仅指等待监视器锁的状态。synchronized和Lock的区别主要体现在以下方面:synchronized是关键字,JVM层面实现,自动释放锁(同步块/方法执行完毕或异常退出);Lock是接口(如ReentrantLock),用户手动释放(需在finally中调用unlock())。synchronized是非公平锁(默认不保证等待线程获取锁的顺序),Lock可通过构造函数指定公平锁(ReentrantLock(true))。synchronized不可中断,除非抛出异常;Lock的lockInterruptibly()方法支持可中断获取锁。synchronized只能有一个等待队列;Lock通过Condition可创建多个等待队列(如生产者-消费者模型中,用不同Condition控制生产和消费线程的等待)。性能方面,早期synchronized因重量锁效率低,但JDK6引入偏向锁、轻量级锁、自旋锁优化后,与Lock性能接近,高竞争场景下Lock更灵活。volatile的作用是保证可见性和禁止指令重排。可见性指当一个线程修改了volatile变量的值,其他线程能立即看到最新值(通过内存屏障实现,写操作后添加Store屏障,读操作前添加Load屏障,强制从主内存读写)。指令重排是JVM为优化性能对指令顺序的调整,volatile变量通过内存屏障禁止编译器和处理器对其相关指令重排。但volatile不保证原子性,例如i++(读取-修改-写入三步操作)在多线程下仍会出现数据不一致。适用场景:状态标志(如booleanisRunning)、单例模式的双重检查锁定(DCL)中防止指令重排导致的空指针(需将实例变量声明为volatile)。线程池的核心参数包括corePoolSize(核心线程数,即使空闲也保留)、maximumPoolSize(最大线程数)、keepAliveTime(非核心线程空闲超时时间)、unit(时间单位)、workQueue(工作队列,存储待执行任务)、threadFactory(线程工厂,创建工作线程)、handler(拒绝策略,任务无法处理时的处理方式)。工作队列常见类型:ArrayBlockingQueue(有界,需指定容量)、LinkedBlockingQueue(无界/有界,无界可能导致OOM)、SynchronousQueue(无缓冲,直接提交给线程)、PriorityBlockingQueue(优先队列,按任务优先级排序)。拒绝策略包括AbortPolicy(默认,抛出RejectedExecutionException)、CallerRunsPolicy(调用者线程执行任务)、DiscardPolicy(丢弃任务)、DiscardOldestPolicy(丢弃队列中最旧任务)。线程池的使用场景:控制资源消耗(避免频繁创建销毁线程)、提高响应速度(任务到达时直接使用空闲线程)、统一管理线程(监控、调优)。AQS(AbstractQueuedSynchronizer)是Java并发包的基础框架,通过维护一个volatileintstate(同步状态)和一个FIFO等待队列(CLH队列的变种)实现锁和同步工具。子类需重写tryAcquire(int)、tryRelease(int)(独占模式)或tryAcquireShared(int)、tryReleaseShared(int)(共享模式)来定义状态变更逻辑。ReentrantLock(独占锁)、CountDownLatch(共享锁)、Semaphore(共享锁)等均基于AQS实现。例如,ReentrantLock的公平锁实现中,tryAcquire方法会检查等待队列是否有前驱节点,确保先到先得;非公平锁则直接尝试CAS修改state,提高吞吐量。CountDownLatch和CyclicBarrier的区别:CountDownLatch是减计数,构造时指定计数器值(count),线程调用countDown()递减,await()等待计数器到0;计数器不可重置,适用于一个/多个线程等待其他线程完成操作(如主线程等待所有子线程初始化完成)。CyclicBarrier是加计数,构造时指定parties(需等待的线程数),线程调用await()等待,当到达parties个线程时,触发barrierAction(可选,由最后一个到达的线程执行);计数器可通过reset()重置,适用于多线程任务分步执行(如多个线程处理数据分片,全部完成后合并结果)。例如,5个线程处理不同文件,全部处理完成后由CyclicBarrier触发合并操作,合并后可重置Barrier处理下一批文件。死锁的四个必要条件:互斥(资源同一时间只能被一个线程占用)、请求与保持(线程持有资源并请求其他资源)、不可抢占(资源只能由持有者释放)、循环等待(线程A等线程B的资源,线程B等线程A的资源)。解决方法包括破坏其中一个条件:破坏互斥(不可行,因部分资源本身互斥)、破坏请求与保持(一次性申请所有资源)、破坏不可抢占(允许抢占,如使用Lock的tryLock(timeout))、破坏循环等待(按固定顺序申请资源)。排查工具:jstack命令可提供线程dump,查看是否有死锁信息;JConsole的线程标签可可视化线程状态及锁持有情况。CAS(Compare-And-Swap)是乐观锁的实现,包含三个操作数:内存位置(V)、预期旧值(A)、新值(B)。当V等于A时,将V更新为B,否则不操作。CAS通过Unsafe类的native方法(如compareAndSwapInt)实现,属于CPU指令级别的原子操作。但CAS存在ABA问题(V的值从A→B→A,此时CAS认为未修改),可通过版本号(如AtomicStampedReference,存储值和版本号)或时间戳解决。另外,CAS在高竞争场景下可能导致自旋次数过多(如多个线程反复修改同一变量),增加CPU开销,此时更适合使用锁(如synchronized)。JVM内存模型分为堆、栈(虚拟机栈和本地方法栈)、方法区、程序计数器。堆(Heap)是最大的内存区域,所有对象实例和数组在此分配,被所有线程共享,是GC的主要区域(分为新生代Eden、Survivor区和老年代)。虚拟机栈(JavaVirtualMachineStacks)每个线程独有,存储栈帧(局部变量表、操作数栈、动态链接、方法出口),每个方法调用对应一个栈帧入栈,方法返回则出栈;若栈深度超过限制(如递归未终止),抛出StackOverflowError。本地方法栈与虚拟机栈类似,用于本地方法(native方法)。方法区(MethodArea)存储类信息、常量、静态变量、即时编译后的代码等,JDK7前是永久代(PermGen),JDK8后改为元空间(MetaSpace,使用本地内存)。程序计数器(ProgramCounterRegister)记录当前线程执行的字节码行号,线程私有,唯一无OOM的区域。类加载过程分为加载、连接(验证、准备、解析)、初始化三个阶段。加载阶段:通过类加载器将.class文件的二进制流读入内存,提供Class对象。连接的验证阶段:检查字节码格式、语义、符号引用等是否合法;准备阶段:为类静态变量分配内存并设置初始值(如int类型初始为0,引用类型为null);解析阶段:将符号引用(如字符串形式的类名)替换为直接引用(内存地址)。初始化阶段:执行类构造器<clinit>()方法(由编译器收集静态变量赋值和静态代码块的顺序提供),按父类到子类的顺序初始化。类加载的触发条件包括:new实例化对象、访问类静态变量/方法、反射调用类、主类(包含main方法的类)启动。双亲委派模型的工作流程:类加载器收到加载请求时,先委托给父类加载器(非继承关系,是组合关系),父类加载器继续向上委托,直到启动类加载器(BootstrapClassLoader);若父类加载器无法加载(如不在其加载路径),则当前类加载器尝试加载。优势:避免类的重复加载(如java.lang.Object只会被Bootstrap加载器加载一次),防止核心类被篡改(如用户自定义的java.lang.String会被父类加载器拦截,使用JDK的String)。自定义类加载器需重写findClass()方法,避免破坏双亲委派(若需破坏,可重写loadClass())。垃圾回收的标记算法有引用计数法(对象有引用时计数+1,无引用时-1,计数为0时回收,无法处理循环引用)和可达性分析(从GCRoots对象出发,标记不可达的对象为可回收)。GCRoots包括:虚拟机栈中局部变量引用的对象、方法区中静态变量引用的对象、方法区中常量引用的对象、本地方法栈中native方法引用的对象。回收算法有标记-清除(Mark-Sweep,标记后清除,产生内存碎片)、标记-复制(Mark-Copy,将存活对象复制到另一块区域,适用于新生代,Eden和Survivor区的8:1:1分配)、标记-整理(Mark-Compact,标记后将存活对象移动到内存一端,适用于老年代)。常见的垃圾收集器:Serial(单线程,新生代,标记-复制,适合客户端)、ParallelScavenge(多线程,新生代,标记-复制,关注吞吐量)、SerialOld(单线程,老年代,标记-整理,配合Serial或作为CMS的后备)、ParallelOld(多线程,老年代,标记-整理,配合ParallelScavenge)、CMS(ConcurrentMarkSweep,多线程,老年代,标记-清除,关注停顿时间,流程:初始标记→并发标记→重新标记→并发清除,缺点是内存碎片和浮动垃圾)、G1(Garbage-First,分代不分区,将堆划分为多个Region,优先回收价值高的Region,标记-整理+标记-复制,适合大内存)、ZGC(低延迟,使用颜色指针和读屏障,停顿时间不超过10ms,支持TB级内存)。内存泄漏的常见场景:静态集合类(如HashMap作为静态变量,对象加入后未移除,导致无法被回收)、未关闭的资源(如数据库连接、IO流、Socket,即使对象被回收,资源未释放可能导致句柄泄漏)、内部类持有外部类引用(非静态内部类默认持有外部类引用,若内部类生命周期长于外部类,外部类无法被回收)、缓存未设置过期时间(如使用HashMap做缓存,未清理旧数据)。排查工具:JProfiler可跟踪对象存活状态,MAT(EclipseMemoryAnalyzer)分析堆dump文件,找出大对象或重复对象;jmap-dump:format=b,file=heap.bin<pid>提供堆转储,jhat分析。OOM(OutOfMemoryError)的常见原因及解决:堆内存不足(Java.lang.OutOfMemoryError:Javaheapspace),可能因对象存活时间过长、内存泄漏、数据量过大;解决方法是调大-Xmx和-Xms参数,优化对象生命周期,排查内存泄漏。元空间不足(Java.lang.OutOfMemoryError:Metaspace),JDK8后方法区移至元空间,存储类元数据,可能因动态提供类(如反射、CGLIB)过多;解决方法是限制动态类提供,调大-XX:MaxMetaspaceSize(默认无限制,受本地内存限制)。栈溢出(Java.lang.StackOverflowError),因方法调用栈过深(如递归无终止条件);解决方法是增加-Xss参数(线程栈大小),优化递归逻辑(改为迭代或尾递归)。直接内存溢出(Java.lang.OutOfMemoryError:Directbuffermemory),因NIO使用ByteBuffer.allocateDirect()分配堆外内存,未及时释放;解决方法是限制直接内存使用,显式调用cleaner().clean()释放。SpringIOC(控制反转)的核心是将对象的创建、依赖注入、生命周期管理交给容器,降低耦合。实现方式包括XML配置、注解(@Component、@Autowired)、Java配置(@Configuration+@Bean)。IOC容器的关键类是BeanFactory(基础容器)和ApplicationContext(扩展容器,支持国际化、事件发布、资源加载)。Bean的实例化过程:容器读取配置元数据→解析Bean定义→通过构造器/工厂方法实例化对象→填充依赖(属性注入,@Autowired通过AutowiredAnnotationBeanPostProcessor实现)→调用初始化方法(@PostConstruct或InitializingBean的afterPropertiesSet())→注册销毁方法(@PreDestroy或DisposableBean的destroy())。SpringAOP(面向切面编程)通过动态代理实现,将横切逻辑(如日志、事务、权限)与业务逻辑解耦。代理方式包括JDK动态代理(基于接口,通过InvocationHandler)和CGLIB代理(基于类,通过MethodInterceptor,需引入cglib库)。AOP的核心概念:连接点(Joinpoint,方法调用或异常抛出)、切点(Pointcut,匹配连接点的表达式,如execution(com.example.service..(..)))、通知(Advice,切面在特定连接点执行的操作,包括前置@Before、后置@After、返回@AfterReturning、异常@AfterThrowing、环绕@Around)、切面(Aspect,切点+通知的组合)、目标对象(Target,被代理的对象)、代理(Proxy,包含目标对象和切面逻辑的对象)、织入(Weaving,将切面应用到目标对象提供代理的过程)。Bean的作用域包括singleton(默认,容器中仅一个实例)、prototype(每次获取新实例)、request(HTTP请求作用域,仅Web容器有效)、session(HTTP会话作用域)、application(ServletContext作用域)、websocket(WebSocket作用域)。singleton的Bean在容器启动时初始化(除非设置lazy-init=true),prototype的Bean在每次获取时初始化,且容器不管理其生命周期(需手动释放资源)。Spring解决循环依赖的关键是三级缓存:singletonObjects(一级缓存,存储已初始化完成的Bean)、earlySingletonObjects(二级缓存,存储完成实例化但未初始化的Bean)、singletonFactories(三级缓存,存储Bean工厂,用于提供早期代理对象)。流程:A实例化后(调用构造器),将A的工厂(ObjectFactory)存入三级缓存;A需要注入B,触发B的实例化,B实例化后存入三级缓存;B需要注入A,从三级缓存获取A的工厂,提供A的早期对象(可能是代理),存入二级缓存并移除三级缓存;B完成依赖注入和初始化,存入一级缓存并移除二级缓存;A获取B(已在一级缓存),完成依赖注入和初始化,存入一级缓存并移除二级缓存。注意,循环依赖仅支持singleton作用域的Bean通过setter注入或构造器注入(构造器注入会在实例化阶段就需要依赖,无法通过三级缓存解决)。Spring事务管理分为编程式(通过TransactionTemplate手动管理)和声明式(通过@Transactional注解或XML配置)。@Transactional的常用属性:propagation(传播行为,默认REQUIRED,当前有事务则加入,无则新建)、isolation(隔离级别,默认DEFAULT使用数据库隔离级别)、readOnly(是否只读,优化数据库性能)、rollbackFor(指定异常回滚,默认RuntimeException和Error)、timeout(超时时间,单位秒)。事务的实现基于AOP,通过TransactionInterceptor拦截方法调用,在方法执行前获取连接并开启事务,执行后根据是否异常决定提交或回滚。SpringMVC的执行流程:客户端发送请求到DispatcherServlet(前端控制器);DispatcherServlet通过HandlerMapping(如RequestMappingHandlerMapping)找到对应的Handler(控制器方法)及拦截器;DispatcherServlet调用HandlerAdapter(如RequestMappingHandlerAdapter)执行Handler;Handler处理完成返回ModelAndView;DispatcherServlet通过ViewResolver(如InternalResourceViewResolver)解析View;DispatcherServlet将Model数据渲染到View并返回响应。关键组件:DispatcherServlet(核心调度)、HandlerMapping(映射请求到处理器)、HandlerAdapter(适配处理器执行)、ViewResolver(解析视图)、Interceptor(请求预处理/后处理)。MyBatis的一级缓存是SqlSession级别的,默认开启,存储同一SqlSession中相同查询(相同SQL、参数、环境)的结果,若执行更新操作(insert/update/delete)会清空缓存。二级缓存是Mapper级别的(namespace范围),需在全局配置中开启<settingname="cacheEnabled"value="true"/>,并在Mapper文件中添加<cache/>,或通过@CacheNamespace注解。二级缓存存储不同SqlSession(同一Mapper)的相同查询结果,需注意缓存一致性(更新操作会清空对应Mapper的二级缓存)。二级缓存可与第三方缓存集成(如Redis),通过实现Cache接口。MyBatis动态SQL通过标签实现:<if>(条件判断,如<iftest="name!=null">ANDname={name}</if>)、<where>(自动处理AND/OR前缀,无条件时不提供WHERE)、<set>(用于更新,自动处理逗号后缀)、<foreach>(遍历集合,如批量插入<foreachcollection="list"item="item"separator=",">({item.id},{})</foreach>)、<choose>/<when>/<

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论