java面试题目积累 01

目录

一、简述一下 GDB 常见的调试命令

二、说一说线程同步的方式

三、内存溢出问题该如何解决

四、TCP/IP四层模型是什么?

五、说一说HashMap的实现原理

六、如何打开一个大文件

七、请描述Spring Boot自动装配的过程

八、说一说 select 的原理以及缺点

九、创建线程有哪几种方式

十、缓存穿透:


一、简述一下 GDB 常见的调试命令


GDB是在linux/unix操作系统下的命令行窗口
常用命令
(gdb)help:查看命令帮助,具体命令查询在gdb中输入help + 命令,简写h
(gdb)run:重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件),简写r
(gdb)start:单步执行,运行程序,停在第一执行语句
(gdb)list:查看原代码(list-n,从第n行开始查看代码。list+ 函数名:查看具体函数),简写l
(gdb)set:设置变量的值
(gdb)next:单步调试(逐过程,函数直接执行),简写n
(gdb)step:单步调试(逐语句:跳入自定义函数内部执行),简写s
(gdb)backtrace:查看函数的调用的栈帧和层级关系,简写bt
(gdb)frame:切换函数的栈帧,简写f
(gdb)info:查看函数内部局部变量的数值,简写i
(gdb)finish:结束当前函数,返回到函数调用点
(gdb)continue:继续运行,简写c
(gdb)print:打印值及地址,简写p
(gdb)quit:退出gdb,简写q

二、说一说线程同步的方式


1. 互斥锁 2. 读写锁 3. 条件变量 4. 信号量 5. 自旋锁 6. 屏障(barrier)

三、内存溢出问题该如何解决


 内存溢出,简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出。引起内存溢出的原因有很多种,常见的有以下几种:
1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3. 代码中存在死循环或循环产生过多重复的对象实体;
4. 使用的第三方软件中的BUG;
5. 启动参数内存值设定的过小。
    内存溢出的解决方案:
· 第一步,修改JVM启动参数,直接增加内存。
· 第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
· 第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
· 第四步,使用内存查看工具动态查看内存使用情况。

四、TCP/IP四层模型是什么?


物理层、数据链路层、网络层、应用层


五、说一说HashMap的实现原理


在JDK8中,HashMap底层是采用“数组+链表+红黑树”来实现的。

HashMap是基于哈希算法来确定元素的位置(槽)的,当我们向集合中存入数据时,它会计算传入的Key的哈希值,并利用哈希值取余来确定槽的位置。如果元素发生碰撞,也就是这个槽已经存在其他的元素了,则HashMap会通过链表将这些元素组织起来。如果碰撞进一步加剧,某个链表的长度达到了8,则HashMap会创建红黑树来代替这个链表,从而提高对这个槽中数据的查找的速度。
HashMap中,数组的默认初始容量为16,这个容量会以2的指数进行扩容。具体来说,当数组中的元素达到一定比例的时候HashMap就会扩容,这个比例叫做负载因子,默认为0.75。自动扩容机制,是为了保证HashMap初始时不必占据太大的内存,而在使用期间又可以实时保证有足够大的空间。采用2的指数进行扩容,是为了利用位运算,提高扩容运算的效率。
加分回答
HashMap是非线程安全的,所以在多线程环境下,各线程同时触发HashMap的改变时,都有可能会发生冲突。所以,在多线程环境下不建议使用HashMap,可以考虑使用Collections将HashMap转为线程安全的HashMap,更为推荐的方式则是使用ConcurrentHashMap。

六、如何打开一个大文件


    分次处理
【参考答案】
标准回答
    打开大文件的关键在于,不能直接将文件中的数据全部读取到内存中,以免引发OOM。重点要考虑内存的利用问题,就是如何使用较小的内存空间来解决问题。可以考虑的方式是,每次读取文件中的一部分内容,分多次处理这个文件,具体还要看打开文件的目的。
1. 如果我们打开的是文本文件,期望读取甚至分析该文件中的内容,则可以采用java.util.Scanner来逐行读取文件的内容。在Scanner遍历文件的过程中,每处理一行之后,我们都要丢弃对该行的引用,以节约内存。
2. 如果我们打开的是字节文件,期望拷贝或者搬运该文件中的内容,则可以采用缓冲流或NIO。每次利用缓冲区处理文件中的一小段数据,这样在处理过程中使用的内存空间便是很有限的,不会造成内存溢出的问题。

加分回答
    如果访问的是文本文件,我们还可以使用第三方类库来处理问题,例如Apache Commones IO库就提供了遍历文件的工具:LineIterator。它在迭代的过程中不会读取完整的文件,只会消耗较小的内存空间。

七、请描述Spring Boot自动装配的过程


整个自动装配的过程是:Spring Boot通过@EnableAutoConfiguration注解开启自动配置,加载spring.factories中注册的各种AutoConfiguration类,当某个AutoConfiguration类满足其注解@Conditional指定的生效条件时,实例化该AutoConfiguration类中定义的Bean(组件等),并注入Spring容器,就可以完成依赖框架的自动配置。

加分回答
    @EnableAutoConfiguration的作用是,从classpath中搜索所有的spring.factories配置文件,然后将其中的配置项加载到spring容器。只有spring.boot.enableautoconfiguration参数为true(默认为true)的时候,才启用自动配置。@EnableAutoConfiguration还可以根据class来排除,或是根据class name来排除,其内部实现的关键点有:
1. ImportSelector:该接口的方法的返回值都会被纳入到spring容器管理中;
2. SpringFactoriesLoader:该类可以从classpath中搜索所有spring.factories配置文件,并读取配置

八、说一说 select 的原理以及缺点


select 是 一种 IO 多路复用技术,它的主旨思想是:
首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中,这个文件描述符的列表数据类型为 fd_set,它是一个整型数组,总共是 1024 个比特位,每一个比特位代表一个文件描述符的状态。比如当需要 select 检测时,这一位为 0 就表示不检测对应的文件描述符的事件,为 1 表示检测对应的文件描述符的事件。
调用 select() 系统调用,监听该列表中的文件描述符的事件,这个函数是阻塞的,直到这些描述符中的一个或者多个进行 I/O 操作时,该函数才返回,并修改文件描述符的列表中对应的值,0 表示没有检测到该事件,1 表示检测到该事件。函数对文件描述符的检测的操作是由内核完成的。
select() 返回时,会告诉进程有多少描述符要进行 I/O 操作,接下来遍历文件描述符的列表进行 I/O 操作。

select 的缺点:
每次调用select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大;
同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大;
select 支持的文件描述符数量太小了,默认是 1024(由 fd_set 决定);
文件描述符集合不能重用,因为内核每次检测到事件都会修改,所以每次都需要重置;
每次 select 返回后,只能知道有几个 fd 发生了事件,但是具体哪几个还需要遍历文件描述符集合进一步判断。

九、创建线程有哪几种方式


创建线程有三种方式,分别是继承Thread类、实现Runnable接口、实现Callable接口。
1. 通过继承Thread类来创建线程的步骤如下:
o 定义Thread类的子类,并重写该类的run()方法,该方法将作为线程执行体。
o 创建Thread子类的实例,即创建了线程对象。
o 调用线程对象的start()方法来启动该线程。

2. 通过实现Runnable接口来创建线程的步骤如下:
o 定义Runnable接口的实现类,并实现该接口的run()方法,该方法将作为线程执行体。
o 创建Runnable实现类的实例,并将其作为参数来创建Thread对象,Thread对象为线程对象。
o 调用线程对象的start()方法来启动该线程。

3. 通过实现Callable接口来创建线程的步骤如下:
o 定义Callable接口的实现类,并实现call()方法,该方法将作为线程执行体。
o 创建Callable实现类的实例,并以该实例作为参数,创建FutureTask对象。
o 使用FutureTask对象作为参数,创建Thread对象,然后启动线程。
o 调用FutureTask对象的get()方法,获得子线程执行结束后的返回值。

加分回答
    采用接口的方式创建线程,优点是线程类还可以继承于其他类,并且多个线程可以共享一个线程体,适合多个线程处理同一份资源的情况。缺点是编程稍微麻烦一点点。
    采用继承的方式创建线程,优点是编程稍微简单一点点。缺点是因为线程类已经继承了Thread类,所以就不能继承其他的父类了。
    所以,通常情况下,更推荐采用接口的方式来创建线程。如果需要返回值,就使用Callable接口,否则使用Runnable接口即可。


十、
缓存穿透:


· 问题描述:
客户端查询根本不存在的数据,使得请求直达存储层,导致其负载过大,甚至宕机。出现这种情况的原因,可能是业务层误将缓存和库中的数据删除了,也可能是有人恶意攻击,专门访问库中不存在的数据
· 解决方案:
1. 缓存空对象:存储层未命中后,仍将空值存入缓存层,客户端再次访问数据时,缓存层直接返回空值
2. 布隆过滤器:将数据存入布隆过滤器,访问缓存之前以过滤器拦截,如果数据不存在则直接返回空值

缓存击穿:
· 问题描述:
一份热点数据,它的访问量非常大。在其缓存失效的瞬间,大量请求直达存储层,导致服务崩溃。
· 解决方案:
1. 永不过期:热点数据不设置过期时间,所以不会出现上述问题,这是“物理”上的永不过期。或者为每个数据设置逻辑过期时间,当发现该数据逻辑过期时,使用单独的线程重建缓存。
2. 加互斥锁:对数据的访问加互斥锁,当一个线程访问该数据时,其他线程只能等待。这个线程访问过后,缓存中的数据将被重建,届时其他线程就可以直接从缓存中取值。

缓存雪崩:
· 问题描述:
在某一时刻,缓存层无法继续提供服务,导致所有的请求直达存储层,造成数据库宕机。可能是缓存中有大量数据同时过期,也可能是Redis节点发生故障,导致大量请求无法得到处理。
· 解决方案:
1. 避免数据同时过期:设置过期时间时,附加一个随机数,避免大量的key同时过期。
2. 启用降级和熔断措施:在发生雪崩时,若应用访问的不是核心数据,则直接返回预定义信息/空值/错误信息。或者在发生雪崩时,对于访问缓存接口的请求,客户端并不会把请求发给Redis,而是直接返回
3. 构建高可用的Redis服务:采用哨兵或集群模式,部署多个Redis实例,个别节点宕机,依然可以保持服务的整体可用。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
烽火Java面试题主要包括Java基础知识、常用框架、数据库和网络编程等内容。下面我将以300字回答这些问题。 1. Java基础知识:Java是一种面向对象的编程语言,它具有跨平台、高性能和易学习等特点。在Java中,类是一切的基础,通过编写类,我们可以创建对象并进行各种操作。同时,Java提供了众多的数据类型、控制语句和异常处理机制,方便我们进行程序设计和开发。 2. 常用框架:常用的Java框架有Spring、Spring MVC、MyBatis和Hibernate等。Spring框架是一个轻量级的IoC容器,它可以帮助我们管理应用程序中的对象依赖关系。Spring MVC是一个Web框架,它基于MVC模式,可以简化Web应用程序的开发。MyBatis是一个持久层框架,它可以帮助我们将数据库操作与Java对象进行映射。Hibernate是一个对象关系映射框架,它可以帮助我们将Java对象和数据库表进行映射。 3. 数据库:常用的关系型数据库有MySQL、Oracle和SQL Server等,它们都支持SQL语言。在Java中,我们可以使用JDBC来连接数据库,并进行数据库操作。JDBC是Java数据库之间的一个标准接口,它提供了一套统一的API,方便我们对数据库进行操作。 4. 网络编程:在Java中,我们可以使用Socket来进行网络编程。Socket是网络编程的基础,它提供了一套底层的API,可以帮助我们建立和管理网络连接。通过Socket,我们可以实现客户端和服务器之间的通信。 综上所述,烽火Java面试题涉及了Java基础知识、常用框架、数据库和网络编程等方面的内容。在面试中,我们需要具备扎实的Java编程基础和相关框架的使用经验,同时还需要了解数据库和网络编程的基本原理和操作方法。希望我的回答能够帮助到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值