java编程面试题库及答案_第1页
java编程面试题库及答案_第2页
java编程面试题库及答案_第3页
java编程面试题库及答案_第4页
java编程面试题库及答案_第5页
已阅读5页,还剩22页未读 继续免费阅读

付费下载

下载本文档

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

文档简介

java编程面试题库及答案Java中如何实现跨平台?Java程序的跨平台性依赖于Java虚拟机(JVM)。开发者编写的Java代码首先被编译成与平台无关的字节码(.class文件),不同平台的JVM会将字节码翻译为对应操作系统能识别的机器指令。因此,只要目标系统安装了对应版本的JVM,同一套字节码即可在不同操作系统上运行。需注意,跨平台的是字节码而非JVM本身,不同平台的JVM是独立开发的。基本数据类型有哪些?各自的字节长度是多少?Java有8种基本数据类型:整型:byte(1字节)、short(2字节)、int(4字节)、long(8字节);浮点型:float(4字节)、double(8字节);字符型:char(2字节,Unicode编码);布尔型:boolean(规范未明确长度,实际实现中通常为1字节或4字节)。基本数据类型的包装类分别是Byte、Short、Integer、Long、Float、Double、Character、Boolean,其中Integer和Character是对象,其他为数值型包装类。String、StringBuilder、StringBuffer的区别是什么?String是不可变类,内部通过final修饰的char数组存储字符(JDK9后改为byte数组),每次修改字符串都会提供新对象,适合少量字符串操作。StringBuilder是可变类,内部使用非线程安全的动态数组,适合单线程下大量字符串拼接(如循环内拼接),性能优于String。StringBuffer同样可变,但方法使用synchronized修饰,保证线程安全,适用于多线程环境下的字符串操作。三者性能排序:StringBuilder>StringBuffer>String(大量修改场景)。重载(Overload)和重写(Override)的区别?重载发生在同一类中,指多个方法名相同但参数列表不同(参数类型、顺序或数量不同)的现象,与返回值类型无关。例如:```javapublicvoidprint(intnum){...}publicvoidprint(Stringstr){...}```重写发生在子类与父类之间,子类通过@Override注解覆盖父类的非final、非静态方法,要求方法名、参数列表、返回值类型完全相同(子类返回值可为父类返回值的子类,即协变返回),且访问权限不能严于父类(如父类方法为protected,子类不能为private)。重写用于实现多态。抽象类和接口的区别是什么?抽象类使用abstract修饰,可包含抽象方法(无实现)和具体方法(有实现),成员变量可为任意访问修饰符(private、protected、public)。子类通过extends继承抽象类,只能单继承。接口使用interface定义,JDK8前仅能包含抽象方法和静态常量(publicstaticfinal),JDK8后支持默认方法(default)和静态方法(static),JDK9支持私有方法(private)。接口成员变量默认publicstaticfinal,方法默认publicabstract(JDK8前)。类通过implements实现接口,可多实现。设计层面:抽象类是“is-a”关系,用于抽取子类公共行为;接口是“can-do”关系,定义功能规范。ArrayList和LinkedList的区别?底层结构:ArrayList基于动态数组(Object[]),支持自动扩容(默认初始容量10,扩容时新容量为原1.5倍);LinkedList基于双向链表(JDK1.6前为循环链表),每个节点保存前驱和后继指针。访问效率:ArrayList支持O(1)时间的随机访问(通过索引),但插入/删除元素(尤其中间位置)需移动元素,时间复杂度O(n);LinkedList随机访问需遍历链表(O(n)),但插入/删除仅需修改相邻节点指针(O(1),若已知节点位置)。内存占用:ArrayList需预分配连续内存空间,可能存在空间浪费;LinkedList每个节点需额外存储指针,内存占用更高。使用场景:频繁随机访问选ArrayList,频繁插入删除(尤其首尾)选LinkedList(实际中ArrayList更常用,因数组的缓存局部性更好)。HashMap的底层实现原理(JDK7vsJDK8)?JDK7中HashMap基于“数组+链表”结构:数组(Entry[])作为哈希表,每个元素是链表头节点;插入元素时,通过key的hashCode()计算哈希值(二次哈希防止碰撞),取模数组长度得到桶下标;若发生哈希冲突(不同key映射到同一桶),新元素以头插法(JDK7)添加到链表;当元素数量超过阈值(容量×负载因子,默认16×0.75=12),触发扩容(数组长度翻倍,重新哈希所有元素)。JDK8引入“数组+链表+红黑树”结构:链表长度≥8且数组长度≥64时,链表转换为红黑树(O(n)→O(logn)查找);红黑树节点数≤6时,退化为链表(避免频繁转换);插入元素改用尾插法(防止多线程扩容时链表成环导致死循环);哈希计算优化(hash=key.hashCode()^(hash>>>16)),减少高位信息丢失。ConcurrentHashMap如何保证线程安全(JDK7vsJDK8)?JDK7采用“分段锁(Segment)”机制:继承ReentrantLock的Segment数组作为锁,每个Segment管理一个链表(Entry[]);默认16个Segment(并发度16),不同Segment可并行操作,同一Segment内的操作需加锁;统计size时需锁定所有Segment(或通过重试机制减少锁竞争)。JDK8放弃分段锁,改用“CAS+synchronized”:数组节点(Node)作为锁对象,仅锁定冲突的桶(链表头或红黑树根节点);插入元素时,若桶为空(null),通过CAS原子操作直接插入;若桶被占用(链表或红黑树),使用synchronized锁定头节点,避免锁整个数组;扩容时支持多线程协助迁移(每个线程处理一部分桶),提高并发效率。线程的生命周期有哪些状态?如何转换?Java线程状态由Thread.State枚举定义,共6种:1.NEW(新建):线程对象创建但未调用start()。2.RUNNABLE(可运行):调用start()后进入,包含“就绪”(等待CPU调度)和“运行中”状态。3.BLOCKED(阻塞):线程等待synchronized同步锁时进入此状态。4.WAITING(无限等待):调用Object.wait()、Thread.join()(无超时)或LockSupport.park()后,需其他线程唤醒(notify()/notifyAll()或unpark())。5.TIMED_WAITING(超时等待):调用sleep(long)、wait(long)、join(long)或parkNanos(long)等带超时参数的方法,超时后自动唤醒。6.TERMINATED(终止):线程执行完run()方法或因异常终止。状态转换示例:NEW→start()→RUNNABLE→获得锁→运行中→调用wait()→WAITING→其他线程notify()→RUNNABLE(需重新竞争锁)→锁被占用→BLOCKED→获得锁→RUNNABLE→运行结束→TERMINATED。synchronized和Lock的区别?1.性质:synchronized是Java关键字,依赖JVM实现锁(内置锁);Lock是接口(如ReentrantLock),通过Java代码实现(显式锁)。2.锁获取:synchronized自动释放锁(同步块/方法执行完或异常退出);Lock需手动调用unlock()(通常在finally块中)。3.公平性:synchronized默认非公平锁(吞吐量高);Lock可通过构造函数指定公平锁(FIFO顺序获取)或非公平锁。4.可中断性:synchronized锁获取不可中断;Lock的lockInterruptibly()方法支持中断等待锁的线程。5.条件变量:synchronized配合Object的wait()/notify()实现等待/通知;Lock通过Condition接口(await()/signal())支持多个等待队列(如读写锁的读、写条件分离)。适用场景:简单同步用synchronized(代码简洁),复杂同步逻辑(如可中断、公平锁、多条件)用Lock。JVM内存区域如何划分?各区域的作用及可能的异常?JVM内存分为以下部分:1.程序计数器(PC寄存器):记录当前线程执行的字节码行号,线程私有。唯一不会抛出OOM(OutOfMemoryError)的区域。2.虚拟机栈:存储栈帧(局部变量表、操作数栈、动态链接、方法出口),线程私有。若线程请求的栈深度超过允许值,抛出StackOverflowError;若动态扩展时无法申请内存,抛出OOM。3.本地方法栈:与虚拟机栈类似,用于本地方法(native方法)的调用,线程私有。同样可能抛出StackOverflowError或OOM。4.堆(Heap):存储对象实例和数组,线程共享。JVM管理的最大内存区域,若无法分配新对象且无法扩容,抛出OOM。5.方法区(元空间,JDK8前为永久代):存储类信息(字段、方法、运行时常量池)、静态变量等,线程共享。JDK7前常量池从方法区移至堆;JDK8用本地内存(元空间)替代永久代,减少OOM概率。若元空间无法分配内存(如加载大量类),抛出OOM。此外,直接内存(DirectMemory)不属于JVM规范,但通过Unsafe或NIO的ByteBuffer.allocateDirect()分配,可能导致OOM(总和超过物理内存限制)。垃圾回收(GC)的常见算法有哪些?各有什么优缺点?1.标记-清除(Mark-Sweep):过程:标记所有存活对象,清除未标记的对象。优点:实现简单,无需移动对象。缺点:产生内存碎片(空闲内存不连续),可能导致大对象无法分配。2.复制(Copying):过程:将内存分为大小相等的两块,每次只使用一块。回收时将存活对象复制到另一块,清空原块。优点:无内存碎片,复制成本与存活对象数量成正比。缺点:内存利用率低(仅使用50%),适用于存活对象少的场景(如新生代)。3.标记-整理(Mark-Compact):过程:标记存活对象,将其向内存一端移动,然后清除边界外的内存。优点:无内存碎片,内存利用率高。缺点:需移动对象,成本较高(与存活对象数量成正比),适用于存活对象多的场景(如老年代)。4.分代收集(GenerationalCollection):基于“分代假说”(弱分代假说:多数对象朝生夕灭;强分代假说:存活越久的对象越难消亡),将堆分为新生代(Eden区+两个Survivor区)和老年代。新生代:使用复制算法(Eden:Survivor=8:1,仅10%空间浪费),MinorGC(YoungGC)频繁回收。老年代:对象多次MinorGC后存活(默认15次,-XX:MaxTenuringThreshold)或大对象直接进入,使用标记-整理或标记-清除算法,MajorGC(FullGC)频率低但耗时。类加载的过程分为哪几个阶段?双亲委派模型是什么?类加载过程分为5个阶段(顺序固定):1.加载(Loading):通过类加载器将.class文件的字节流加载到内存,提供java.lang.Class对象。2.验证(Verification):确保字节流符合JVM规范(如文件格式、元数据、字节码、符号引用验证),防止恶意代码。3.准备(Preparation):为类静态变量分配内存并设置初始值(如int类型初始为0,引用类型为null),常量(staticfinal)直接赋实际值。4.解析(Resolution):将符号引用(如类名、方法名)替换为直接引用(内存地址),发生在初始化之后(动态绑定场景)或之前(静态绑定)。5.初始化(Initialization):执行类构造器<clinit>()方法(静态变量赋值和静态代码块的合并),按父类→子类顺序初始化。双亲委派模型指类加载器的层级关系:启动类加载器(BootstrapClassLoader):加载JRE/lib下的核心类(如rt.jar),C++实现,无父加载器。扩展类加载器(ExtensionClassLoader):加载JRE/lib/ext下的扩展类,Java实现,父加载器为启动类加载器。应用类加载器(ApplicationClassLoader):加载用户类路径(classpath)下的类,父加载器为扩展类加载器。自定义类加载器:继承ClassLoader,父加载器通常为应用类加载器。工作流程:类加载时,当前类加载器先委托父加载器加载,父加载器再继续向上委托,直到启动类加载器。若父加载器无法加载(未找到.class文件),则由当前类加载器自己加载。作用:避免重复加载(同一类由同一加载器加载),防止核心类被篡改(如用户自定义java.lang.String不会被加载)。Error和Exception的区别?CheckedException和UncheckedException的区别?Error表示JVM无法处理的严重问题(如OutOfMemoryError、StackOverflowError、NoClassDefFoundError),程序无法恢复,通常不捕获处理。Exception表示程序可处理的异常(如IOException、SQLException),分为:CheckedException(受检异常):编译期强制处理(捕获或声明抛出),如IOException、ClassNotFoundException。UncheckedException(非受检异常):继承自RuntimeException,编译期不强制处理(如NullPointerException、IllegalArgumentException、ArrayIndexOutOfBoundsException)。示例:调用FileInputStream构造方法时,需处理受检异常FileNotFoundException;而访问null对象的方法时,会抛出非受检异常NullPointerException。谈谈对volatile关键字的理解?它能保证原子性吗?volatile是轻量级同步机制,作用:1.保证可见性:被volatile修饰的变量,修改后立即刷新到主内存,其他线程读取时直接从主内存获取,避免使用本地缓存的旧值(解决多线程间的可见性问题)。2.禁止指令重排:通过内存屏障(JVM插入特定指令)限制编译器和CPU对指令的重排序,保证变量操作的顺序性(如单例模式的双重检查锁定)。但volatile不能保证原子性。例如,i++操作(读取i→加1→写入i)是复合操作,即使i被volatile修饰,多线程下仍可能出现数据不一致(如线程A读取i=5,线程B读取i=5,都加1后写入6,丢失一次递增)。原子性需通过synchronized、Lock或AtomicInteger(CAS操作)保证。单例模式有哪些实现方式?如何保证线程安全?常见实现方式:1.饿汉式:类加载时初始化实例,线程安全(类加载机制保证)。```javapublicclassSingleton{privatestaticfinalSingletonINSTANCE=newSingleton();privateSingleton(){}publicstaticSingletongetInstance(){returnINSTANCE;}}```2.懒汉式(线程不安全):首次调用时初始化,但多线程下可能创建多个实例。```javapublicclassSingleton{privatestaticSingletonINSTANCE;privateSingleton(){}publicstaticSingletongetInstance(){if(INSTANCE==null){//多线程可能同时进入INSTANCE=newSingleton();}returnINSTANCE;}}```3.懒汉式(线程安全,synchronized方法):通过同步方法保证线程安全,但性能差(每次调用都加锁)。```javapublicstaticsynchronizedSingletongetInstance(){...}```4.双重检查锁定(DCL,推荐):减少锁竞争,需volatile修饰实例防止指令重排(避免其他线程获取到未初始化的实例)。```javapublicclassSingleton{privatestaticvolatileSingletonINSTANCE;//volatile防止重排privateSingleton(){}publicstaticSingletongetInstance(){if(INSTANCE==null){//第一次检查(无锁)synchronized(Singleton.class){if(INSTANCE==null){//第二次检查(加锁后)INSTANCE=newSingleton();//非原子操作,可能重排}}}returnINSTANCE;}}```5.静态内部类(推荐):利用类加载的延迟性,线程安全(JVM保证类初始化的线程安全)。```javapublicclassSingleton{privateSingleton(){}privatestaticclassHolder{staticfinalSingletonINSTANCE=newSingleton();}publicstaticSingletongetInstance(){returnHolder.INSTANCE;}}```6.枚举(最安全):避免反射攻击和反序列化漏洞,JVM保证单例。```javapublicenumSingleton{INSTANCE;publicvoidmethod(){...}}```推荐使用静态内部类或枚举实现,双重检查锁定需注意volatile的使用,饿汉式可能浪费资源(若实例未使用)。如何实现一个线程池?ThreadPoolExecutor的核心参数有哪些?线程池通过ThreadPoolExecutor类实现,核心参数(构造方法):corePoolSize:核心线程数(即使空闲也不会销毁,除非设置allowCoreThreadTimeOut为true)。maximumPoolSize:最大线程数(线程池允许的最大线程数)。keepAliveTime:非核心线程的空闲存活时间(超过此时间后销毁)。unit:keepAliveTime的时间单位(如TimeUnit.SECONDS)。workQueue:任务队列(存放待执行的Runnable任务),常用类型:ArrayBlockingQueue(有界队列,需指定容量);LinkedBlockingQueue(无界队列,可能导致OOM);SynchronousQueue(同步队列,不存储任务,直接提交给线程);PriorityBlockingQueue(优先队列,按任务优先级排序)。threadFactory:线程工厂(用于创建线程,可自定义线程名、优先级等)。handler:拒绝策略(任务队列满且线程数达到maximumPoolSize时的处理方式),JDK提供4种:AbortPolicy(默认):抛出RejectedExecutionException;CallerRunsPolicy:调用者线程执行任务;DiscardPolicy:静默丢弃新任务;DiscardOldestPolicy:丢弃队列中最旧的任务,尝试提交新任务。线程池工作流程:1.提交任务时,若运行线程数<corePoolSize,创建新核心线程执行任务;2.若核心线程已满,任务加入workQueue;3.若任务队列满且运行线程数<maximumPoolSize,创建非核心线程执行任务;4.若线程数达到maximumPoolSize且任务队列满,触发拒绝策略。equals()和hashCode()的关系?为什么重写equals()时必须重写hashCode()?equals()用于判断两个对象是否“逻辑相等”(默认比较内存地址),hashCode()返回对象的哈希码(用于哈希集合如HashMap、HashSet的桶分配)。关系:若两个对象equals()为true,则它们的hashCode()必须相同;若hashCode()不同,则equals()必为false(逆否命题)。但hashCode()相同,equals()可能为false(哈希冲突)。重写equals()时必须重写hashCode(),否则违反上述约定。例如,自定义类A重写了equals()(按属性判断相等),但未重写hashCode()(默认返回内存地址的哈希码),则将两个逻辑相等的A对象存入HashMap时,可能被分配到不同的桶(hashCode不同),导致containsKey()返回false(因为HashMap先根据hashCode找桶,再遍历链表用equals()比较)。这会破坏哈希集合的正确性。Java反射的作用及应用场景?反射允许程序在运行时获取类的信息(如字段、方法、构造器),并动态调用对象的方法或操作字段。核心类:Class、Field、Method、Constructor。应用场景:框架开发(如Spring的IOC容器通过反射实例化Bean);注解处理(如JUnit的@Test注解通过反射调用测试方法);动态代理(如JDK动态代理通过反射提供代理类);反序列化(如Jackson通过反射设置对象字段);调试/测试工具(如获取私有字段值)。示例:通过反射调用私有方法:```javaClass<?>clazz=MyClass.class;Objectinstance=clazz.getDeclaredConstructor().newInstance();Methodmethod=clazz.getDeclaredMethod("privateMethod");method.setAccessible(true);//取消访问检查method.invoke(instance);```注意:反射会绕过编译期检查,可能破坏封装性,且性能低于直接调用(需额外处理)。IO和NIO的区别是什么?传统IO(BIO,BlockingIO)基于流(InputStream/OutputStream),操作是阻塞的:读取/写入时线程等待数据,无法处理其他任务;每个连接需独立线程(如Tomcat的BIO模式),高并发时线程数爆炸,资源消耗大。NIO(NewIO/Non-bloc

温馨提示

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

评论

0/150

提交评论