Java面试:2021.05.14

171 阅读10分钟

1、"=="和equals方法究竟有什么区别?

== 的作用:

基本类型:比较的是值是否相同

引用类型:比较的是地址值是否相同

equals 的作用:

Object类中的 equals 就是利用了 ==,比较的是地址值是否相同

通常我们希望比较的是对象的内容是否相同,因此需要重写 equals 方法,例如String,Integer,Date等类中都有重写,在自定义类中也可以实现自己的比较规则

如果没有重写,使用的仍然是继承自Object父类的equals,比较的是地址值。

2、数据库的隔离级别及说明?

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它可以避免脏读问题,一个事务只能看见其它事务已经提交的修改。这种隔离级别,会有不可重复读(Nonrepeatable Read)问题,即一次事务中的两次相同查询拿到了不同的结果

Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它能避免脏读及不可重复读问题。但还是会有幻读 (Phantom Read)现象。幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行。MySQL 提供了间隙锁可以避免幻读,例如

select * from 表 where id >= 10 这时其它事务无法插入id >=10以上的新行

Serializable(可串行化)

这是最高的隔离级别,它利用了共享锁解决幻读,即两个事务可以同时读数据,而不能一个读、一个写,或者两个都去写数据。但性能太低

3、线程池的核心参数?

corePoolSize:核心线程池的大小 maximumPoolSize:线程池能创建线程的最大个数 keepAliveTime:空闲线程存活时间 unit:时间单位,为keepAliveTime指定时间单位 workQueue:阻塞队列,用于保存任务的阻塞队列 threadFactory:创建线程的工程类 handler:饱和策略(拒绝策略)

4、volatile与synchronized的区别?

volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。

volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。

volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

5、RabbitMQ的五种模式?

简单队列(模式):一个生产者对应一个消费者!

work模式:一个生产者对应多个消费者,但是只能有一个消费者获得消息!

发布/订阅模式:一个消费者将消息首先发送到交换器,交换器绑定多个队列,然后被监听该队列的消费者所接收并消费。

路由模式:生产者将消息发送到direct交换器,在绑定队列和交换器的时候有一个路由key,生产者发送的消息会指定一个路由key,那么消息只会发送到相应key相同的队列,接着监听该队列的消费者消费信息。

主题模式:路由模式是根据路由key进行完整的匹配(完全相等才发送消息),这里的通配符模式通俗的来讲就是模糊匹配.符号"#"表示匹配一个或多个词,符号"*"表示匹配一个词。

6、静态为什么不能调用非静态?

因为非static方法是对象级别的,必须创建一个对象后,才能调用此方法,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,因为没有任何实例对象,所以无法调用非static方法。

7、消息队列消息丢失问题该如何解决?(消息队列)

消息丢失的三种情况: 生产者发送消息到交换机时数据丢失:消息确认模式confirmCallback,返回给生产者,提示消息发送交换机成功; 交换机发送消息到队列时数据丢失:同样是消息确认模式,ReturnCallback,如果发送失败将消息退回给生产者; 消费者消费消息是数据丢失:默认是自动确认,我们只要配置成手动确认即可,消费者接收消息以后调用channel.basicAck()方法出现异常则获取消息失败,需要重新发送消息。

8、什么是泛型?

泛型将接口的概念进一步延伸,“泛型”的字面意思就是广泛的类型。类、接口和方法代码可以应用于非常广泛的类型,代码与它们能够操作的数据类型不再绑定在一起,同一套代码可以用于多种数据类型,这样不仅可以复用代码,降低耦合性,而且还提高了代码的可读性以及安全性。

使用泛型的好处: 1.代码复用:我们一套代码可以支持不同的类性。 2.降低了耦合性:代码逻辑和数据类型之间分离,实现了解耦。 3.更好的可读性:我们在使用集合的时候,定义了一个list 如List,一看便知道这个一个存放String类型的list。 4.更高的安全性:语言和程序设计的一个重要目标就是将bug消灭在摇篮里,能在写的时候消灭,就不要留在运行的时候。如我们定义一个List这样的一个list。当我们往list里面放其他非String类型的数据时,我们的IDE(如Eclipse)就会报错提示。就算没有IDE。编译时,Java编译器也会提示,这称之为类型安全。这样就为程序设置了一道安全防护。同样的,使用泛型还可以省去使用普通对象时繁琐的强制类型转换。相反,使用普通对象,编译时并不会提示。假如传入的参数类型和最后强制类型转换的类型不一致。运行时就会出现ClassCastException,使用泛型则不会。

9、什么是 java 序列化?什么情况下需要序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,将数据分解成字节流,以便存储在文件中或在网络上传输。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

什么时候使用序列化: 1:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。 2:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。

10、在并发情况下,Elasticsearch如果保证读写一致?

1、可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突 2、对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。 但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。 3、对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回; 如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。

11、hashmap是如何扩容的?

HashMap的默认数组长度大小是16,负载因子是0.75,当HashMap的元素数量大于当前数组长度乘以负载因子就会触发HashMap的扩容机制.也就是当数组中的元素>12时会触发扩容机制。

扩容的大小为2的n次方. 并且对HashMap中的元素进行重新hash,并有部分存到新位置,一部分存到原来的位置,所以说扩容的过程是非常耗费性能的。

首次扩容, 是扩容到原来2倍。

源码上作者给了一张表可以看出当桶中元素达到8个的时候,概率已经非常的小了,也就是说用0.75作为负载因子,每个碰撞位置的连表长度超过8个几乎是不可能的. HashMap负载因子为0.75是空间可时间成本的一种折中。

12、JVM垃圾回收算法。

Java中用于常见算法有4种:

  1. 标记-清除算法(mark and sweep)

分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成之后统一回收掉所有被标记的对象。

缺点:标记清除之后会产生大量的不连续的内存碎片。

  1. 标记-整理算法

是在标记-清除算法基础上做了改进,标记阶段是相同的,但标记完成之后不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,在移动过程中清理掉可回收的对象,这个过程叫做整理。

优点:内存被整理后不会产生大量不连续内存碎片。

缺点:相对标记清除,垃圾回收时间更长(对象移动需要花费更长时间)

  1. 复制算法(copying)

将可用内存按容量分成大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另一块内存上去,然后把使用过的内存空间一次清理掉。适用于只有少量对象存活的场景,顺带还可以完成整理 缺点:占用了成倍的内存空间

  1. 分代收集算法(generation)

当前主流JVM都采用分代收集(Generational Collection)算法, 这种算法会根据对象存活周期的不同将内存 划分为年轻代、年老代、永久代,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。

Java面试百分百.jpg