Java中高级面试题总览(一)(2)

简介: Java中高级面试题总览(一)

34、Java中的关键字 transient

Java中transient关键字的作用,简单地说,就是让某些被修饰的成员属性变量不被序列化

Java中的关键字 transient - 风一样的码农 - 博客园

35、Reader与InputStream两个类中的read()的区别

InputStream类的read()方法是从流里面取出一个字节,他的函数原型是 int read(); 
Reader类的read()方法则是从流里面取出一个字符(一个char),他的函数原型也是 int read(); 

36、Java Integer的缓存策略

package com.javapapers.java;
public class JavaIntegerCache {
    public static void main(String... strings) {
        Integer integer1 = 3;
        Integer integer2 = 3;
        if (integer1 == integer2)
            System.out.println("integer1 == integer2");
        else
            System.out.println("integer1 != integer2");
        Integer integer3 = 300;
        Integer integer4 = 300;
        if (integer3 == integer4)
            System.out.println("integer3 == integer4");
        else
            System.out.println("integer3 != integer4");
    }
}

大多数人都认为上面的两个判断的结果都是 false。虽然它们的值相等,但由于比较的是对象,而对象的引用不一样,所以会认为两个 if 判断都是 false 的。在 Java 中,== 比较的是对象引用,而 equals 比较的是值。因此,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都应该返回 false。但是奇怪的是,这里两个相似的 if 条件判断却返回不同的布尔值。

下面是上面代码真正的输出结果,

integer1 == integer2

integer3 != integer4

Java 中 Integer 缓存实现

在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。

上面的规则适用于整数区间 -128 到 +127。

37.反射能获取到父类的私有方法吗?怎么防止反射破坏单例模式

反射能获取到父类的私有方法

单例模式一般构造方法都是private,目的就是为了防止外界调用私有构造器创建多个实例

但是反射能够访问私有的构造方法,只要反射获取的构造器调用setAccessible(true)方法即可。这样调用一次就会产生一个实例,调用多次就时多个实例,从而破坏单例。

只要在单例的私有构造器中添加判断单例是否已经构造的代码,如果单例之前已经构造,则抛出异常,如果没有构造,则无所谓。

还有一种就是枚举单例,枚举单例没有构造函数无法通过反射来攻击。

 

38.拦截器与过滤器的区别

  • 拦截器是基于java的反射机制的,而过滤器是基本函数回调。
  • 拦截器不依赖于servlet容器,过滤器依赖于servlet容器
  • 拦截器只能对action请求起作用,过滤器可以对几乎所有的请求起作用
  • 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问
  • 在action的生命周期中,拦截器可以被多次调用,而过滤器只能在容器初始化时被调用过一次。

JVM 知识

1. 什么情况下会发生栈内存溢出。

至于是堆内存溢出还是方法区内存溢出还是栈内存溢出,其实可以用一些工具比如
JConsole来监视

2. JVM 的内存结构,Eden 和 Survivor 比例。

3. jvm 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的 jvm 参数。

4. 你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms,包括原理,流程,优缺点

5. 垃圾回收算法的实现原理。

6. 当出现了内存溢出,你怎么排错。

内存溢出分析
JAVA dump查看线程运行情况:
=====查看栈信息=======================
1.查询java程序pid 
  netstat -ntpl | grep 8080
2.使用jstack [-l] 进程pid > xxx.log将进程里所有线程信息输入到指定文件中
  1)如果程序正常运行:使用jstack [-l] 进程pid > xxx.log将所有线程信息输入到指定文件中 
  2)如果程序无响应:使用 jstack -F [-m] [-l] 进程pid >xxx.log强制打印栈信息 
=====查看堆信息=======================
3.使用top命令找出占用cpu高(或者执行时间很长)的进程pid 
4.使用top -H -p 线程pid 找出占用cpu高(或执行时间长)的线程pid 
5.将占用cpu高的线程pid转换成16进制(window自带计算器) 
6.dump该进程的内存
  jmap -dump:format=b,file=文件名 [进程pid]   将转换后的pid在开始输出的dump文件(xxx.log)中搜索对应线程信息 
7.eclipse Memory Analyzer 对dump文件分析

7. JVM 内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存等。

volatile关键字禁止指令重排序(内存栅栏)有两层意思
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对
后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语
句放到其前面执行。
Happen-Before 原则
1. 程序次序原则:一个线程内,按照程序代码顺序,书写在前面的操作先发生于书写在后面的操作。 
2. volatile 规则:volatile 变量的写,先发生于读,这保证了 volatile 变量的可见性。 
3. 锁规则:解锁(unlock) 必然发生在随后的加锁(lock)前。 
4. 传递性:A先于B,B先于C,那么A必然先于C。 
5. 线程的 start 方法先于他的每一个动作。 
6. 线程的所有操作先于线程的终结。 
7. 线程的中断(interrupt())先于被中断的代码。 
8. 对象的构造函数,结束先于 finalize 方法。

并发编程之 Java 内存模型 + volatile 关键字 + Happen-Before 规则_击水三千里的专栏-CSDN博客

8. 简单说说你了解的类加载器。

JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)
链接又分为三个步骤
验证:确保被加载类的正确性;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap 
ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一
次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
1)Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
2)Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下
的jar包
3)App ClassLoader
负责记载classpath中指定的jar包及目录中class
4)Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

9.spring里创建出来对象的存活时间

目前的系统,大部分是spring容器的对象,spring默认单实例方式加载,这些对象可能会存在老年代中。

但是方法内部new出来的对象不会存活太长时间,方法结束,引用消息,对象也会在下一次gc被回收。

10. 你们线上应用的 JVM 参数有哪些。

-Xms512m 
-Xmx512m 
-XX:MetaspaceSize=256m 
-XX:MaxMetaspaceSize=256m 
-Xmn256m 
-XX:MaxDirectMemorySize=1g 
-XX:SurvivorRatio=10 
-XX:+UseConcMarkSweepGC 
-XX:CMSMaxAbortablePrecleanTime=5000 
##垃圾回收会清理持久代,移除不再使用的classes,只有在 UseConcMarkSweepGC  也启用的情况下才有用
-XX:+CMSClassUnloadingEnabled 
##使用cms作为垃圾回收使用80%后开始CMS收集
-XX:CMSInitiatingOccupancyFraction=80 
##使用手动定义初始化定义开始CMS收集
-XX:+UseCMSInitiatingOccupancyOnly 
##使用并发的方式执行FGC
-XX:+ExplicitGCInvokesConcurrent 
## 设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相等
-XX:ParallelGCThreads=4 
-Xloggc:$work_dir/logs/gc.log 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=$work_dir/logs/java.hprof

11. g1 和 cms 区别,吞吐量优先和响应优先的垃圾收集器选择。

Cms是以获取最短回收停顿时间为目标的收集器。基于标记-清除算法实现。比较占用cpu资源,切易造成碎片。
G1是面向服务端的垃圾收集器,是jdk9默认的收集器,基于标记-整理算法实现。可利用多核、多cpu,保留分
代,实现可预测停顿,可控。 

12. 请解释如下 jvm 参数的含义:

-server 
-Xms512m 
-Xmx512m 
-Xss1024K
-XX:PermSize=256m 
-XX:MaxPermSize=512m 
-XX:MaxTenuringThreshold=20 
-XX:CMSInitiatingOccupancyFraction=80 
-XX:+UseCMSInitiatingOccupancyOnly。
##设置JVM最大可用内存为3550M。
-Xmx3550m
#设置JVM初始内存为3550m,可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xms3550m
#设置每个线程的堆栈大小。在相同物理内 存下,减小这个值能生成更多的线程。但是操作系统对一
#个进程内的线程数还是有限制的,不能无限生成,
#经验值在3000~5000左右
-Xss128k 
#设置持久代大小为16m
-XX:MaxPermSize=16m
#设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直
#接进入年老代。对于年老代比较多的应用,可以提高效率。
#如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活
#时间,增加在年轻代即被回收的概论。
-XX:MaxTenuringThreshold=0

系统学习JVM知识这篇文章讲的比较清楚

JVM原理讲解和调优_击水三千里的专栏-CSDN博客

问题和实战

https://mp.weixin.qq.com/s/5lJdrsj_CGnG_w3CjJ8iPg

https://mp.weixin.qq.com/s/sI2qQ8e7TOa4qGqi6-kQgA

开源框架知识

1. 简单讲讲 tomcat 结构,以及其类加载器流程。

tomcat结构

Tomcat的核心组件就Connector和Container,一个Connector+一个Container(Engine)构成一个Service,Service就是对外提供服务的组件,有了Service组件Tomcat就能对外提供服务了,但是光有服务还不行,还需要有环境让你提供服务才行,所以最外层的Server就是为Service提供了生存的土壤。

Container 是容器的父接口,所有子容器都必须实现这个接口,Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine、Host、Context、Wrapper,这四个组件不是平 行的,而是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一个 Servlet class 对应一个 Wrapper,如果有多个 Servlet 就可以定义多个 Wrapper,如果有多个 Wrapper 就要定义一个更高的 Container 了

在Tomcat 6中默认情况下,Jar包的加载顺序是:

1)JRE中的Java基础包
2)Web应用WEB-INF\lib下的包
3)Tomcat\lib下的包

线程模型:支持以下四种线程模型。

3. 讲讲 Spring 加载流程。

4. 讲讲 Spring 事务的传播属性。

PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

5. Spring 如何管理事务的。

6. Spring 怎么配置事务(具体说出一些关键的 xml元素)。

tx:advice,aop:config

7. 说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop 的实现原理,说说 aop 中的几个术语,它们是怎么相互工作的。

8. SpringMVC 中 DispatcherServlet初始化过程。

1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
4.  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5.  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6.  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
7. ViewResolver 结合Model和View,来渲染视图

9.SpringBoot主要解决了什么问题

1. 创建独立的 Spring 应用程序

2. 嵌入的 Tomcat,无需部署 WAR 文件

3. 简化 Maven 配置

4. 自动配置 Spring

5. 供生产就绪型功能,如指标,健康检查和外部配置

6. 自定义代码配置代替XML

操作系统

1. Linux 系统下你关注过哪些内核参数,说说你知道的。

2. Linux 下 IO 模型有几种,各自的含义是什么。

1.BIO(blocking IO):同步阻塞 I/O
2.NIO(nonblocking IO):同步非阻塞 I/O
3.多路复用IO( IO multiplexing)
4.信号驱动I/O( signal driven IO)
5.异步 I/O(asynchronous IO)

【Linux基础】Linux的5种IO模型详解_白夜行-CSDN博客_基本io模型

3. epoll 和 poll 有什么区别。

select、poll、epoll都是IO多路复用的机制,先是监听多个文件描述符FD,一旦某个FD就绪,就可以进行相应

的读写操作。但是select、poll、epoll本质都是同步I/O,他们都需要在读写事件就绪之后自己负责读写,即这个读写过程是阻塞的

总结:

(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用 epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的 时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要 一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

4. 平时用到哪些 Linux 命令。

递归计算当前目录的文件,包括隐藏文件。

# find . -type f | wc -l

语法:

find : 搜索目录结构中的文件

-type : 文件类型

f : 常规文件

-l : 输出换行符的数量

wc [选项] 文件 :该命令统计给定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所有指定文件的总统计数。字是由空格字符区分开的最大字符串。

该命令各选项含义如下

- c 统计字节数。

- l 统计行数。

- w 统计字数。

$ wc - lcw file1 file2

4 33 file1

7 52 file2

11 11 85 total

批量替换文本内容

sed -i 's/^Str/String/'  replace.txt      把每行第一个Str替换为String

sed -i 's/^Str/String/g'  replace.txt    把Str全文替换为String

sed -i 's/\.$/\;/'  replace.txt                把.结尾替换为;

sed -i 's/^ *$/d'  replace.txt               删除文本中的空行

sed -i 's/Integer/d'  replace.txt          删除Integer所在行

检索文件内容常用指令

image.png

image.png'

5. 用一行命令查看文件的最后五行。

输出test文件的后五行:
liyi@liyi:~/Desktop > tail -n 5 test
输出test文件的前五行:
liyi@liyi:~/Desktop > head -n 5 test

6. 用一行命令输出正在运行的 java 进程。

jps

7. 介绍下你理解的操作系统中线程切换过程。

线程的切换只有指令的切换,同处于一个进程里面,不存在映射表的切换。进程的切换就是在线程切换的基础上加上映射表的切换。

8. 进程和线程的区别。

9.用户态和内核态是什么?

内核态: CPU可以访问内存所有数据, 包括外围设备, 例如硬盘, 网卡. CPU也可以将自己从一个程序切换到另一个程序

用户态: 只能受限的访问内存, 且不允许访问外围设备. 占用CPU的能力被剥夺, CPU资源可以被其他程序获取

10.怎么批量替换一个文件夹下所有文件中的一个字符?

sed -i "s/oldString/newString/g" `grep oldString -rl /path`

多线程

1. 多线程的几种实现方式,什么是线程安全。

2. volatile 的原理,作用,能代替锁么。

3. 画一个线程的生命周期状态图。

4. sleep 和 wait 的区别。

5. Lock 与 Synchronized 的区别。

  总结来说,Lock和synchronized有以下几点不同:
  1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异
常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放
锁;
  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直
等待下去,不能够响应中断;
  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
  5)Lock可以提高多个线程进行读操作的效率。
  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时
竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

6. synchronized 的原理是什么,解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁。

synchronized 的原理

monitor对象存在于每个Java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的

偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引人了偏向锁

轻量级锁

如果没有竞争,轻量级锁使用CAS操作,避免使用互斥量

如果存在竞争,除了互斥量的开销,还有 CAS的操作,不仅没有提升,反而性能会下降

重量级锁

重量级锁,使用的是系统互斥量实现的

Synchronized 用法和底层原理_击水三千里的专栏-CSDN博客

轻量级锁、重量级锁都是啥玩意_叫我刘三青-CSDN博客_轻量级锁

7. 用过哪些原子类,他们的原理是什么。

8. 用过线程池吗,newCache 和 newFixed 有什么区别,他们的原理简单概括下,构造函数的各个参数的含义是什么,比如 coreSize,maxsize 等。

Executors.newCachedThreadPool():无限线程池。
Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。
Executors.newSingleThreadExecutor():创建单个线程的线程池。
1.ExecutorService threadPool = Executors.newCachedThreadPool();  
SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移
除操作take。所以newCachedThreadPool实际项目一般也很少运用
2.ExecutorService executor = Executors.newFixedThreadPool(3); 
这个线程池的队列LinkedBlockingQueue没有指定默认大小,高并发环境下
在对内存压力很大,所以生产环境一般都不使用这个

9. 线程池的关闭方式有几种,各自的区别是什么。

shutdown() 执行后停止接受新任务,会把队列的任务执行完毕。
shutdownNow() 也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。

详细介绍见:讲解线程池的一篇干货,很干很干!

10. 假如有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有 10 个线程同时调用它,如何做到。

基于redis分布式限流超时时间设为1s

详细介绍见:单机限流和分布式应用限流_击水三千里的专栏-CSDN博客_分布式限流和单机限流

11. spring 的 controller 是单例还是多例,怎么保证并发的安全。

singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
相关文章
|
3天前
|
Java
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
14 0
|
3天前
|
安全 Java 程序员
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
7 0
|
6天前
|
Java
三个可能的Java面试题
Java垃圾回收机制自动管理内存,回收无引用对象的内存,确保内存有效利用。多态性允许父类引用操作不同子类对象,如Animal引用可调用Dog的方法。异常处理机制通过try-catch块捕获和处理程序异常,例如尝试执行可能导致ArithmeticException的代码,catch块则负责处理异常。
29 9
|
16天前
|
Java
【JAVA面试题】static的作用是什么?详细介绍
【JAVA面试题】static的作用是什么?详细介绍
|
16天前
|
Java
【JAVA面试题】final关键字的作用有哪些
【JAVA面试题】final关键字的作用有哪些
|
16天前
|
JavaScript 前端开发 Java
【JAVA面试题】什么是引用传递?什么是值传递?
【JAVA面试题】什么是引用传递?什么是值传递?
|
16天前
|
安全 Java
【JAVA面试题】什么是对象锁?什么是类锁?
【JAVA面试题】什么是对象锁?什么是类锁?
|
3天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
13 1
|
1天前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
10 3
|
1天前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
34 2