高频java互联网公司面试题及答案_第1页
高频java互联网公司面试题及答案_第2页
高频java互联网公司面试题及答案_第3页
高频java互联网公司面试题及答案_第4页
高频java互联网公司面试题及答案_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

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

文档简介

高频java互联网公司面试题及答案面向对象编程中多态的实现机制是什么?多态的核心是“同一行为,不同实现”,分为编译时多态(静态多态)和运行时多态(动态多态)。编译时多态通过方法重载实现,编译器根据参数类型、数量和顺序确定调用的方法版本;运行时多态通过方法重写和向上转型实现,JVM在运行时根据实际对象类型动态绑定方法。例如,父类Animal有eat()方法,子类Dog和Cat重写该方法,当用Animal类型变量指向Dog对象时,调用eat()会执行Dog的实现。其底层依赖JVM的动态分派机制:通过对象的实际类型查找方法表(vtable),找到具体的方法地址。String、StringBuilder、StringBuffer的区别及使用场景?String是不可变类,内部通过finalchar[]存储字符,每次修改会提供新对象,适合字符串不频繁修改的场景(如常量、参数传递)。StringBuilder和StringBuffer继承自AbstractStringBuilder,内部使用可扩容的char[](初始容量16,扩容时为原容量2+2),支持高效修改。区别在于StringBuffer的方法用synchronized修饰,线程安全但性能略低;StringBuilder无同步,线程不安全但性能更高。因此,单线程下字符串频繁拼接用StringBuilder(如循环内拼接日志),多线程环境用StringBuffer(如多线程提供报表时拼接字符串)。HashMap的底层结构在JDK7和JDK8中有哪些差异?JDK7中HashMap采用“数组+链表”结构,数组是Entry<K,V>[],链表解决哈希冲突(链地址法)。插入元素时,新节点插入链表头部(头插法),扩容时重新计算哈希并转移节点,可能导致多线程下的链表成环问题(如resize时的并发操作)。JDK8优化为“数组+链表+红黑树”结构,数组是Node<K,V>[](与Entry同功能),当链表长度≥8且数组长度≥64时,链表转为红黑树(时间复杂度从O(n)降至O(logn));链表长度≤6时,红黑树退化为链表(避免频繁转换)。插入时改为尾插法,扩容时通过(e.hash&oldCap)是否为0决定节点在新数组中的位置(无需重新计算哈希),提升效率。此外,JDK8的HashMap允许key为null(存储在数组0号位置),而Hashtable不允许。ConcurrentHashMap如何保证线程安全?JDK7中采用分段锁(Segment),继承ReentrantLock,默认16个Segment,每个Segment维护一个HashEntry链表数组,锁的粒度是Segment,支持16个线程并发写(不同Segment可并行)。JDK8弃用Segment,采用“数组+链表+红黑树”结构,通过synchronized+CAS保证线程安全:插入节点时,若数组对应位置为空(tabAt方法通过Unsafe.getObjectVolatile获取,保证可见性),用CAS尝试放入新节点;若已存在节点,用synchronized锁定该节点(链表头或红黑树根),避免锁整个数组。扩容时采用多线程协作,每个线程处理一段旧数组的迁移,通过sizeCtl变量标记扩容状态(-1表示正在初始化,-N表示N-1个线程参与扩容)。统计size时,JDK7遍历所有Segment累加count;JDK8通过baseCount(基础值)和counterCells(冲突时的增量数组)组合计算,减少CAS竞争。ArrayList的扩容机制是怎样的?ArrayList底层是Object[]elementData,默认初始容量为10(调用无参构造时,初始elementData为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,首次add时扩容为10)。调用add(Ee)时,先检查是否需要扩容:计算最小所需容量(当前size+1),若超过elementData长度,触发扩容。扩容逻辑:新容量=旧容量+(旧容量>>1)(即1.5倍),若新容量仍小于最小所需容量,直接用最小所需容量;若超过Integer.MAX_VALUE8(数组最大容量限制),则用Integer.MAX_VALUE。通过Arrays.copyOf(elementData,newCapacity)复制数组。需要注意,频繁扩容会导致多次数组复制,因此若已知数据量,建议调用ArrayList(intinitialCapacity)指定初始容量,减少扩容次数。volatile的作用及底层实现?volatile是轻量级同步机制,保证变量的可见性(线程修改后立即刷新到主内存,其他线程读取时从主内存获取最新值)和禁止指令重排序(通过内存屏障实现)。可见性的实现依赖JVM的内存模型:每个线程有本地内存(缓存、寄存器等),共享变量存储在主内存。普通变量修改后,本地内存的值不会立即同步到主内存,其他线程可能读取到旧值;volatile变量修改后,JVM会插入StoreStore屏障,强制将本地内存的修改刷回主内存;读取时插入LoadLoad屏障,强制从主内存读取。禁止重排序通过内存屏障指令实现:写volatile变量前插入StoreStore屏障(保证前面的写操作不被重排序到volatile写之后),写后插入StoreLoad屏障(防止volatile写与后续读/写操作重排序);读volatile变量前插入LoadLoad屏障(防止前面的读操作重排序到volatile读之后),读后插入LoadStore屏障(防止volatile读与后续写操作重排序)。典型应用场景是状态标志(如线程终止标志位)、单例模式的双重检查锁定(防止指令重排导致其他线程获取到未初始化的对象)。synchronized的底层实现和锁升级过程?synchronized的锁对象存储在对象头中(MarkWord),JVM基于对象头的锁状态标记实现锁升级。对象头包含MarkWord(存储哈希码、GC分代年龄、锁状态等)和类型指针(指向类元数据)。锁升级过程为:无锁→偏向锁→轻量级锁→重量级锁。偏向锁:记录第一个获取锁的线程ID,后续该线程再次获取锁时无需CAS操作(只需检查MarkWord中的线程ID是否匹配),适用于单线程重复获取锁的场景。若其他线程尝试获取锁,偏向锁撤销(升级为轻量级锁),撤销需要暂停持有偏向锁的线程,检查其是否存活:若已结束,恢复为无锁;若存活,升级为轻量级锁。轻量级锁:线程在栈帧中创建锁记录(LockRecord),存储MarkWord的拷贝,然后用CAS尝试将对象头的MarkWord替换为指向锁记录的指针。若成功,获取轻量级锁;若失败(说明有竞争),进入自旋(默认自旋10次,可通过-XX:PreBlockSpin调整),自旋成功则获取锁,否则升级为重量级锁。重量级锁:依赖操作系统的互斥量(Mutex),线程竞争失败后进入阻塞状态,唤醒需操作系统切换上下文,开销大。锁只能升级不能降级,目的是减少低竞争场景下的性能损耗。线程池的核心参数有哪些?如何合理配置?线程池的核心类是ThreadPoolExecutor,构造参数包括:corePoolSize(核心线程数,即使空闲也不会被销毁,除非设置allowCoreThreadTimeOut为true)、maximumPoolSize(最大线程数,核心线程+临时线程的上限)、keepAliveTime(临时线程空闲存活时间)、TimeUnit(时间单位)、workQueue(任务队列,常用ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue)、threadFactory(线程工厂,用于创建线程)、rejectedExecutionHandler(拒绝策略)。拒绝策略包括:AbortPolicy(默认,抛出RejectedExecutionException)、CallerRunsPolicy(调用者线程执行任务)、DiscardPolicy(丢弃新任务)、DiscardOldestPolicy(丢弃队列中最老的任务,尝试重新提交)。配置建议:CPU密集型任务(如复杂计算),corePoolSize设为CPU核心数+1(避免上下文切换);IO密集型任务(如数据库访问、网络请求),corePoolSize设为CPU核心数2或根据IO阻塞时间调整(公式:核心数/(1-阻塞系数),阻塞系数=阻塞时间/(阻塞时间+计算时间))。任务队列选择:若任务量可控,用有界队列(如ArrayBlockingQueue)防止内存溢出;若任务处理快,用SynchronousQueue(不存储任务,直接提交给线程);若任务有优先级,用PriorityBlockingQueue。AQS(AbstractQueuedSynchronizer)的核心原理是什么?AQS是构建锁和同步器的框架(如ReentrantLock、CountDownLatch、Semaphore),内部通过volatileintstate(同步状态)和CLH变体的双向队列(等待队列)实现。state的访问通过getState()、setState()、compareAndSetState()(CAS操作)保证原子性。AQS支持独占模式(如ReentrantLock)和共享模式(如CountDownLatch)。独占模式下,线程尝试通过tryAcquire(intarg)获取锁(需子类实现),成功则持有锁,否则加入等待队列,通过LockSupport.park()阻塞。释放锁时调用tryRelease(intarg)(子类实现),成功则唤醒队列中的后继节点。共享模式下,线程通过tryAcquireShared(intarg)尝试获取共享资源(返回值>0表示成功,≤0表示失败),失败则加入队列,释放时调用tryReleaseShared(intarg),成功则唤醒后续节点。AQS的条件变量(Condition)通过ConditionObject实现,每个Condition有独立的等待队列,调用await()时将线程从同步队列移到条件队列,signal()时将线程从条件队列移到同步队列,等待获取锁。JVM的内存区域是如何划分的?各区域的作用及常见异常?JVM内存分为线程私有和线程共享区域。线程私有:程序计数器(PC寄存器),记录当前线程执行的字节码行号(Native方法时为undefined),无OOM;虚拟机栈(Java栈),每个方法调用创建栈帧(包含局部变量表、操作数栈、动态链接、方法出口),栈深度超过限制抛StackOverflowError(如递归未终止),扩展失败抛OutOfMemoryError(OOM)。本地方法栈,为Native方法服务,与虚拟机栈类似,HotSpot直接合并了两者。线程共享:堆(最大区域),存储对象实例和数组,GC的主要区域,分年轻代(Eden区+2个Survivor区,默认比例8:1:1)和老年代,内存不足抛OOM。方法区(JDK7前称永久代,JDK8后为元空间MetaSpace),存储类元数据(类名、方法、字段)、常量池、静态变量,JDK7将字符串常量池移到堆,JDK8用本地内存实现元空间(避免永久代内存限制),内存不足抛OOM(元空间默认无上限,可通过-XX:MaxMetaspaceSize限制)。运行时常量池(方法区的一部分),存储编译期提供的字面量和符号引用,JDK7后字符串常量池移到堆。直接内存(非JVM规范区域),通过Unsafe或NIO的ByteBuffer.allocateDirect()分配,不受堆内存限制,但受系统内存限制,分配过多抛OOM。CMS和G1垃圾收集器的区别及适用场景?CMS(ConcurrentMarkSweep)是老年代收集器,基于“标记-清除”算法,目标是降低停顿时间。步骤:初始标记(STW,标记GCRoots直接关联的对象)→并发标记(与用户线程并发,遍历可达对象)→重新标记(STW,修正并发标记期间的变动)→并发清除(与用户线程并发,清除不可达对象)。优点是停顿时间短(并发阶段不停止用户线程),适合对响应时间敏感的场景(如Web服务器)。缺点:标记-清除会产生内存碎片(可能触发FullGC),并发阶段占用CPU资源(CPU核数少会影响性能),无法处理浮动垃圾(并发清除时用户线程新产生的垃圾,需预留空间,若预留不足会触发ConcurrentModeFailure,降级为SerialOld收集器,停顿时间变长)。G1(GarbageFirst)是跨年轻代和老年代的收集器,基于“标记-整理”算法,将堆划分为多个大小相等的Region(默认2048个,大小1-32MB),每个Region可属于Eden、Survivor、Old或Humongous(存储大对象,超过Region50%视为大对象)。步骤:初始标记(STW)→并发标记→最终标记(STW,用RememberedSet记录跨Region引用)→筛选回收(STW,根据各Region的回收价值(回收空间/停顿时间)排序,优先回收收益高的Region,用复制算法将存活对象移到新Region,避免内存碎片)。优点:可预测的停顿时间(通过-XX:MaxGCPauseMillis设置目标停顿时间),适合大内存(≥8GB)、高响应要求的场景(如大数据处理、微服务集群)。缺点:内存占用和计算开销较大(RememberedSet维护复杂)。SpringIOC的实现原理是什么?循环依赖如何解决?IOC(控制反转)的核心是将对象的创建、依赖注入、生命周期管理交给Spring容器,降低代码耦合。实现步骤:容器启动时,通过BeanDefinitionReader(如XmlBeanDefinitionReader、AnnotatedBeanDefinitionReader)读取配置(XML、注解),解析提供BeanDefinition(存储Bean的类名、作用域、依赖等信息),存入BeanDefinitionRegistry。然后通过BeanFactoryPostProcessor(如PropertyPlaceholderConfigurer)修改BeanDefinition(如替换占位符)。接着实例化Bean:通过反射(无参构造或工厂方法)创建对象,处理依赖注入(通过AutowiredAnnotationBeanPostProcessor解析@Autowired、@Resource等注解,从容器获取依赖Bean),应用BeanPostProcessor(如AOP的AnnotationAwareAspectJAutoProxyCreator提供代理对象),最后调用初始化方法(InitializingBean的afterPropertiesSet或@PostConstruct)。循环依赖指A依赖B,B依赖A。Spring通过三级缓存解决单例Bean的循环依赖(仅支持setter注入或字段注入,构造函数注入无法解决)。三级缓存:singletonObjects(一级,存储已初始化完成的单例Bean)、earlySingletonObjects(二级,存储未完成初始化但已创建实例的Bean,用于解决AOP代理问题)、singletonFactories(三级,存储ObjectFactory,用于提供早期Bean引用)。创建A时,先将A的ObjectFactory(()->getEarlyBeanReference(beanName,mbd,bean))放入singletonFactories,然后注入B;创建B时需要A,从singletonFactories获取A的早期引用(可能是代理对象),放入earlySingletonObjects,B完成初始化后放入singletonObjects;A获取到B后完成初始化,从singletonFactories移除,最终A也放入singletonObjects。若存在AOP,getEarlyBeanReference会提前提供代理对象,避免循环依赖时拿到原始对象。SpringAOP的实现方式及应用场景?AOP(面向切面编程)通过动态代理将横切逻辑(如日志、事务、权限)与业务逻辑解耦。实现方式:JDK动态代理(基于接口,通过java.lang.reflect.Proxy提供代理类,重写接口方法,调用InvocationHandler的invoke方法插入切面逻辑)、CGLIB动态代理(基于类,通过ASM修改字节码提供子类,重写父类方法,调用MethodInterceptor的intercept方法插入逻辑)。Spring默认选择JDK动态代理(若目标对象实现接口),否则用CGLIB。核心概念:切面(Aspect,包含切点和通知)、切点(Pointcut,定义匹配的连接点)、通知(Advice,包括前置@Before、后置@After、返回@AfterReturning、异常@AfterThrowing、环绕@Around)、连接点(Joinpoint,程序执行的某个点,如方法调用)。应用场景:日志记录(记录方法调用参数、耗时)、事务管理(@Transactional标记方法,通过@Around通知开启/提交事务)、权限校验(@Before检查用户权限)、性能监控(统计方法执行时间)、参数校验(@Around验证入参合法性)。需要注意,AOP无法增强静态方法(静态方法属于类,动态代理基于对象)和final方法(CGLIB无法重写final方法)。MySQL索引的类型及B+树索引的优势?索引类型按数据结构分:B+树索引(最常用,InnoDB和MyISAM的默认索引)、哈希索引(Memory引擎支持,等值查询快,范围查询慢)、全文索引(MyISAM和InnoDB支持,用于文本内容搜索)、R树索引(用于空间数据)。按逻辑分:主键索引(唯一标识行,InnoDB的聚簇索引)、唯一索引(字段值唯一,允许NULL)、普通索引(无唯一性约束)、联合索引(多字段组合,遵循最左匹配原则)。B+树索引的优势:内部节点存储索引键(不存储数据),叶子节点存储索引键和数据(InnoDB聚簇索引叶子节点存储整行数据,非聚簇索引存储主键值),所有叶子节点通过指针连接(支持范围查询的顺序访问)。相比B树,B+树的内部节点无数据,可存储更多索引键,减少树的高度;叶子节点的链表结构使范围查询只需遍历叶子节点,效率更高。聚簇索引(主键索引)的表数据按主键顺序存储,查询时可直接获取数据;非聚簇索引(二级索引)需回表(通过主键值查询聚簇索引获取数据),可通过覆盖索引(查询字段包含在索引中)避免回表(如SELECTid,nameFROMuserWHEREname='xxx',若name是索引且索引包含id和name,则无需回表)。MySQL事务的隔离级别及各自解决的问题?事务的ACID特性:原子性(Atomicity,操作要么全做要么全不做)、一致性(Consistency,从一个一致状态到另一个一致状态)、隔离性(Isolation,事务间互不干扰)、持久性(Durability,提交后永久保存)。隔离级别从低到高:READUNCOMMITTED(读未提交),允许读取其他事务未提交的修改(脏读)。READCOMMITTED(读已提交,大多数数据库默认),只读取已提交的数据,解决脏读,但可能出现不可重复读(同一事务两次读取同一行结果不同)。REPEATABLEREAD(可重复读,MySQLInnoDB默认),保证同一事务多次读取同一行结果一致,通过MVCC(多版本并发控制)和间隙锁解决不可重复读,但可能出现幻读(同一事务两次查询范围,结果行数不同)。SERIALIZABLE(可串行化),最高隔离级别,事务串行执行,解决幻读(通过锁表),但并发性能差。InnoDB通过MVCC(记录的隐藏字段:DB_TRX_ID事务ID,DB_ROLL_PTR回滚指针)实现读已提交和可重复读:读已提交每次查询提供新的ReadView(记录活跃事务ID),可重复读在事务开始时提供ReadView。幻读的解决依赖Next-KeyLock(行锁+间隙锁),锁定记录和记录间的间隙,防止其他事务插入新记录。单例模式的几种实现方式及优缺点?单例模式保证一个类仅有一个实例,并提供全局访问点。常见实现:饿汉式(staticfinal实例,类加载时初始化):```javapublicclassSingleton{privatestaticfinalSingletonINSTANCE=newSingleton();privateSingleton(){}publicstaticSingletongetInstance(){returnINSTANCE;}}```优点:线程安全(类加载机制保证),简单;缺点:类加载时即初始化,可能浪费资源(若未使用)。懒汉式(延迟初始化):```javapublicclassSingleton{privatestaticSingletonINSTANCE;privateSingleton(){}publicstaticsynchronizedSingletongetInstance(){if(INSTANCE==null){INSTANCE=newSingleton();}returnINSTANCE;}}```优点:延迟加载;缺点:synchronized导致性能开销(每次调用都加锁)。双重检查锁定(DCL):

温馨提示

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

评论

0/150

提交评论