面试问题集锦

1.为什么一次编译到处运行

首先目标平台必须要安装JVM(java虚拟机),JVM会将字节码翻译为相应平台的计算机指令,JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。

2.synchronized 和Lock 有什么区别?

1、lock是一个接口,而synchronized是java的一个关键字。
2、synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
3.synchronized:在需要同步的对象中加入此控制,可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
lock:一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。

3.线程通信方法

  1. volatile 关键字,多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式
  2. Object 类提供了线程间通信的方法:wait()、notify()、notifyAll(),使用共享内存的思想,大致意思就是多个线程需要访问同一个共享变量,谁拿到了锁(获得了访问权限),谁就可以执行。通过Object的wait()方法使当前线程进入阻塞状态并释放锁,再调用Object的notify()方法时阻塞的线程进入就绪状态。
  3. 通过wait使得线程挂起等待某个条件满足,当其他线程得运行,等这个条件满足时,就可以调用notify或者notifyAll来唤醒这个进程。
  4. 使用ReentrantLock 结合 Condition,原理类似synchronized/wait/notify。

4.微服务架构的优缺点

优点
1、微服务架构是将系统中的不同功能模块拆分成多个不同的服务,这些服务进行独立地开发和部署,每个服务都运行在自己的进程内,这样每个服务的更新都不会影响其他服务的运行;
2、由于每个服务是独立部署的,可以更准确地监控每个服务的资源消耗情况也很容易发现各个服务间的性能瓶颈所在;
3、由于每个服务都是独立开发,项目的开发也比较方便,减少代码的冲突、代码的重复,逻辑处理流程也更加清晰,让后续的维护与扩展更加容易;
4、微服务可以使用不同的编程语言进行开发;
缺点:
1、微服务架构增加了系统维护、部署的难度,导致一些功能模块或代码无法复用;
2、微服务在一定程度上也会导致系统变得越来越复杂,增加了集成测试的复杂度;

5.数据库持久层

常用的的持久层框架有 JDBC 、 MyBatis 、 Hibernate 、 TopLink 、 Guzz 、 jOOQ 、 Spring Data”

6.Redis用途

Redis支持数据的持久化,内存高速缓存数据库,其值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型。
众多语言都支持Redis,因为Redis交换数据快,在服务器中常用来存储一些需要频繁调取的数据,节省内存开销,也极大的提升了速度。将一些热点数据存储到Redis中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。
1、会话缓存(最常用)

2、消息队列(支付)

3、活动排行榜或计数

4、发布,订阅消息(消息通知)

5、商品列表,评论列表

7.Redis各数据类型的应用场景

String(字符串)类型
String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类

Hash对象的键是一个字符串类型,值是一个键值对集合。
应用场景:该类型非常适合于存储对象的信息(结构体信息)。如一个用户有姓名,密码,年龄等信息。

list列表结构常用来做异步队列使用
list可用于秒杀抢购场景

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。可以存储一些集合性的数据,对上面的所有集合操作。

sort sets每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。当需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构。

8.缓存穿透

先来描述一下缓存穿透的过程:
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。
解决办法:
1 对url中的key id值进行对称加密,不能轻易暴露出真实的key值,防止黑客攻击
2 不管数据实际上存不存在,我们都把这个键存到缓存中(有效期设置的短一些,比如一分钟到三分钟),然后值设置为一个特定值,业务中如果获取到的结果是这个特定值,则报错返回。

9.缓存击穿

缓存击穿是指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db。
解决办法:
1.预先设置热门数据(例如先缓存好制定商品的信息)
2.实施调整(人工延长key过期时间)
3.使用锁(影响效率)

10.缓存雪崩

缓存雪崩是指当缓存失效或过期后引起系统性能急剧下降的情况。缓存中数据大批量过期时间一直,当同时到过期时间时,将失效或被清除。
当缓存失效或过期被清除后,系统需要再次访问数据库,再次进行运算重新生成缓存,这个处理步骤耗时比较长,上百毫秒甚至更长时间。而对于一个高并发的系统来说,几百毫秒内可能会接到几百个请求。
由于旧的缓存已经被清除,新的缓存还未生成,并且处理这些请求的线程都不知道另外有一个线程正在生成缓存,所以所有的请求都会去重新生成缓存,都会去访问数据库,对数据库造成巨大的压力和不必要的性能损耗。
这些对数据库的访问压力又会拖慢整个系统,严重的会造成数据库宕机,形成一系列连锁反应,造成整个系统崩溃。
以上的过程就是雪崩。

解决办法:
1.错开key的过期时间(建议3 4结合使用)

11.TCp拆包、粘包

TCP在传输字节流时,只会根据缓冲区的大小和实际情况进行数据包的分割,发送方发送的若干包数据到达接收方时粘成了一个包,从接受方来看就是前一包数据的尾紧接着后一包数据的头。起因存在于发送方也存在于接收方。
如果数据包长度太大,超过MSS(最大TCP报文长度,不包括TCP和IP的首部)的长度,TCP就会将报文分开传输,这样就会造成一个完整的数据报文在传输过程中被分成了几部分,这就是拆包。起因:应用层程序写入数据时的数据大于套接字缓冲区的大小。

12.String保存

String s=new String(“ abc”)保存存在哪里的? new出来的对象保存在堆里
String s=“abc”保存在哪里? 字符串常量池

13.运行时内存

  1. 方法区:类加载好后放在方法区中:JDK7及以前,习惯把方法区称为永久代,JDK8开始,使用元空间取代永久代。方法区看作是一块独立于Java堆的内存空间。主要存储已被虚拟机中classloader加载到字节码的类型信息,常量,静态变量,即时编译器编译后的代码缓存等。
  2. 程序计数器:PC寄存器,任何一个时间一个线程都只有一个方法在执行,也就是所谓的“当前方法”。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址。
  3. 本地方法栈:关键字为native,本地方法是一个Java方法,是一个Java调用非Java代码的接口,该方法的实现由非Java语言实现,当某个线程调用本地方法时,它就进入了本地方法栈。
  4. 堆:堆也是Java内存管理的核心区域,所有线程共享Java堆。几乎所有的对象实例和数组都在堆上分配内存,因为栈帧中保存的是引用,引用指向对象或者数组在堆中的位置。堆,是GC执行垃圾回收的重点区域。
  5. 虚拟机栈:每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应一次次的Java方法调用,主管Java程序的运行,它保存方法的局部变量(8种基本数据类型,对象的引用地址),部分结果,并参与方法的调用和返回。

14.lru

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

15.Redis持久化

redis持久化是指在指定的时间间隔内将内存中的数据集快照(snapshotting)写入磁盘。
RDB:redis会单独创建一个子进程(使用fork函数)来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
fork函数:fork函数的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。
生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作

RDB是一种快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据。

AOF:以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

16.GC root

通过一系列名为“GC Roots”的对象作为起始点,从“GC Roots”对象开始向下搜索,如果一个对象到“GC Roots”没有任何引用链相连,说明此对象可以被回收。

17.HashMap与ConcurrentHashMap

(1)ConcurrentHashMap对整个桶数组进行了分段,而HashMap则没有
(2)ConcurrentHashMap在每一个分段上都用锁进行保护,从而让锁的粒度更精细一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。。。

18.GET和POST的区别

从url可见性来看:get,参数url可见;post,url参数不可见;
从数据传输上来看:get,通过拼接url进行传递参数;post,通过body体传输参数,
从缓存性来看:get请求是可以缓存的;post请求不可以缓存。
get和post本质上都是TCP链接。

19.线程安全的set

Set的三个子类分别是:HaseSet、TreeSet、LinkedHashSet.这三个都是线程不安全的。
如何实现线程安全:一.使用Colletcions这个工具类syn方法类创建个线程安全的set.

Set<String> synSet = Collections.synchronizedSet(new HashSet<>());

二.使用JUC包里面的CopyOnWriteArraySet

20.创建线程的方法

  1. 通过继承Thread类创建线程,复写run方法,创建Thread的子类对象,调用start()方法,开启线程
  2. 实现Runnable接口的方法
  3. 实现Callable接口,复写call方法,Callable接口提供了一个call()方法来作为线程的执行体,call()方法比run()方法功能要更加强大,call()方法可以有返回值,call()方法可以声明抛出异常(前两种如果要抛异常只能通过try,catch来实现)
  4. 通过线程池来创建线程

21.synchronized能修饰哪些东西

synchronized关键字可以修饰方法,也可以修饰代码块。

22.Spring Cloud

https://www.cnblogs.com/wmyskxz/p/10992686.html

23.数据库创建索引的指令

CREATE INDEX indexName ON mytable(username(length));

24.Redis数据淘汰策略

当内存不足时,Redis会根据配置的缓存策略淘汰部分keys,以保证写入成功。当无淘汰策略时或没有找到适合淘汰的key时,Redis直接返回out of memory错误。
LRU,即:最近最少使用淘汰算法(Least Recently Used)。LRU是淘汰最长时间没有被使用的页面。
最后一次使用时间越早越淘汰

25.形成死锁的四个必要条件

  1. 互斥条件:一个资源一次只能被一个进程访问。

  2. 请求与保持: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  3. 不可剥夺:进程已获得的资源,在未使用完之前,不得强行剥夺。

  4. 循环等待:若干进程之间形成一种头尾相接的循环等待资源关系。

26.https的请求过程

  1. 客户端向服务器发起HTTPS请求,连接到服务器的443端口;
  2. 服务器端有一个密钥对,即公钥(即数字证书)和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人;
  3. 服务器将自己的公钥发送给客户端;
  4. 客户端收到服务器端的公钥之后,检查其合法性,如果发现发现公钥有问题,那么HTTPS传输就无法继续,如果公钥合格,则客户端会生成一个客户端密钥,然后用服务器的公钥对客户端密钥进行非对称加密成密文,至此,HTTPS中的第一次HTTP请求结束;
  5. 客户端发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器;
  6. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文;
  7. 然后服务器将加密后的密文发送给客户端;
  8. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。

27.输入一个网址的请求过程

  1. 校验URL:浏览器会检测这个url是否正确存在,如果不合法,返回一个默认的页面。(比如,如果输入的不是合法的网址,浏览器将调用默认搜索引擎比如百度,对输入的内容进行搜索,返回搜索结果页)。
  2. DNS解析:浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址。解析优先级依次为:浏览器缓存 => 系统缓存 => 路由器缓存 => 本地域名服务器(LDNS) => 顶级域名服务器
  3. 建立TCP连接(三次握手): 当解析得到目的地的IP地址之后,在进行http请求之前,需要先建立TCP连接。TCP建立连接的过程,即三次握手。
  4. 发送HTTP请求:TTP协议定义了几种请求方式,比如:GET/POST/PUT/DELETE等等
  5. 服务器处理请求并返回HTTP报文
  6. 浏览器解析渲染页面

28.bean的生命周期

实例化、属性赋值、初始化、生存期、销毁。
在这里插入图片描述
Spring 对bean 进行实例化。
Spring 将值和bean的引用注入到bean对应的属性中。
Spring将bean的ID传递给setBean-Name() 方法。
Spring将调用setBeanFactory() 方法,将BeanFactory容器实例传入。
Spring将调用setApplicationContext() 方法,将bean所在的应用上下文的引用传入进来。
Spring将调用它们的post-ProcessBeforeInitialization() 方法
Spring将调用它们的after-PropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用。
Spring将调用它们的post-ProcessAfterInitialization() 方法。
此时, bean 已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁。
如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

29.cookie和session

cookie:
位于用户的计算机上,用来维护用户计算机中的信息,直到用户删除。比如我们在网页上登录某个软件时输入用户名及密码时如果保存为cookie,则每次我们访问的时候就不需要登录网站了。我们可以在浏览器上保存任何文本,而且我们还可以随时随地的去阻止它或者删除。我们同样也可以禁用或者编辑cookie,但是有一点需要注意不要使用cookie来存储一些隐私数据,以防隐私泄露
session:
session称为会话信息,位于web服务器上,主要负责访问者与网站之间的交互,当访问浏览器请求http地址时,将传递到web服务器上并与访问信息进行匹配, 当关闭网站时就表示会话已经结束,网站无法访问该信息了,所以它无法保存永久数据,我们无法访问以及禁用网站

30.ArrayList和LinkedList的区别

ArrayList是基于数组的,LinkedList是基于链表的。

查询:对于ArrayList,它真正的优点是按下标查询元素,相比于LinkedList,LinkedList也可以按下标查询元素,但是LinkedList需要对底层链表进行遍历,才能找到指定下标的元素,而ArrayList不用,所以这是ArrayList的优点。

插入:ArrayList可以插入到指定下标位置,或者数组末尾,这种插入普通情况下是很快的,但是如果某次插入操作触发了扩容,那么本次插入就增加了额外的扩容成本。
对于LinkedList,如果是插在链表的头部或者是尾部都是很快的,因为LinkedList中有单独的属性记录的链表的头结点和尾结点,不过,如果是插在指定下标位置,那么就需要遍历链表找到指定位置,从而降低了效率。

31.Java共同父类及常见方法

Object类中常见方法有:

getClass(),用于返回当前运行时对象的Class对象。
hashCode(),用于返回对象的哈希码,主要使用在哈希表中。
equals(),用于比较两个对象的内存地址是否相等。
clone(),用于创建并返回当前对象的一份拷贝。
toString(),返回类名实例的哈希码的16进制的字符串。
notify(),用于唤醒一个在此对象监视器上等待的线程。
notifyAll(),和notify类似,唤醒所有等待的线程。
wait(),暂停线程的执行。
finalize(),实例被垃圾回收器回收的时候触发的操作。
此外还有两个wait()方法的重载。

32.hashcode和equals的关系

equals() 的作用是用来判断两个对象是否相等。
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
1、如果两个对象equals相等,那么这两个对象的HashCode一定也相同。
2、在重写了equals方法后,尽量也重写了hashcode方法,通过一定的算法,使他们在equals相等时,也会有相同的hashcode值。
3、hashcode就是在hash表中对应的位置。

33.StringBuffer和StringBuilder的区别

string当修改字符串时,是在内存中创建一个新的字符串,并把地址传给string 对象,因此比较浪费空间,而stringbuffer和stringbuilder是在初始时创建一个容器,当修改的时候会修改容器中的内容,而不是创建一个新的容器

StringBuffer线程安全,StringBuilder线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 synchronized 修饰。

StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以, StringBuffer 对缓存区优化,不过 StringBuffer 的这个toString 方法仍然是同步的。

34.默认大小

ArrayList的10,HashMap的是16

35.重载和重写的区别

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。
重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

36.线程池

线程池的创建:ThreadPoolExecutor
1,降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
2,提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
3,提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值