本文目录
- 写在前面
- 试题总览
- 题目解析
- 1.说一下SpringBoot中常用的注解
- 2.Redis中的基本数据类型
- 3.TCP的网络协议
- 4.java中常见的锁
- 5.Hashmap的底层数据结构、底层源码、扩容机制+源码
- 6.java面向对象的特点
写在前面
关于这个专栏:
本专栏记录一些互联网大厂、小厂的面试实录!包含校招、社招!
内容主要来源:博主面试总结以及博主从牛客网等平台摘录
专栏地址:2023Java面试实录
试题总览
题目解析
与项目相关的参考性不大,就不做记录了
1.说一下SpringBoot中常用的注解
这题考查的是对springBoot框架的熟悉程度,可以说出几个常用的注解,详细说明。你可以这样答:
Spring Boot中的常用注解有:@SpringBootApplication、@Repository、@Service、@RestController、@ResponseBody、@Component、@ComponentScan等等。下面我就来给您详细介绍一下:
1.@SpringBootApplication
这个注解是Spring Boot最核心的注解,用在 Spring Boot的主类上,标识这是一个 Spring Boot 应用,用来开启 Spring Boot 的各项能力。实际上这个注解是@Configuration,@EnableAutoConfiguration,@ComponentScan三个注解的组合。由于这些注解一般都是一起使用,所以Spring Boot提供了一个统一的注解@SpringBootApplication。
2.@EnableAutoConfiguration
允许 Spring Boot 自动配置注解,开启这个注解之后,Spring Boot 就能根据当前类路径下的包或者类来配置 Spring Bean。
如:当前类路径下有 Mybatis 这个 JAR 包,MybatisAutoConfiguration 注解就能根据相关参数来配置 Mybatis 的各个 Spring Bean。
@EnableAutoConfiguration实现的关键在于引入了AutoConfigurationImportSelector,其核心逻辑为selectImports方法,逻辑大致如:
● 从配置文件META-INF/spring.factories加载所有可能用到的自动配置类;
● 去重,并将exclude和excludeName属性携带的类排除;
● 过滤,将满足条件(@Conditional)的自动配置类返回;
3.@Configuration
用于定义配置类,指出该类是 Bean 配置的信息源,相当于传统的xml配置文件,一般加在主类上。如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。
4.@ComponentScan
组件扫描。让spring Boot扫描到Configuration类并把它加入到程序上下文。
5.@Repository
用于标注数据访问组件,即DAO组件。
使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,不需要为它们提供XML配置。
6.@Service
一般用于修饰service层的组件
7.@RestController
用于标注控制层组件(如struts中的action),表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器;它是@Controller和@ResponseBody的合集。
8.@ResponseBody
表示该方法的返回结果直接写入HTTP response body中
一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。
9.@Component
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
10.@Bean
相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
11.@AutoWired
byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
12.@Qualifier
当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用
13.@Resource(name=“name”,type=“type”)
没有括号内内容的话,默认byName。与@Autowired干类似的事。
14.@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解;提供路由信息,负责URL到Controller中的具体函数的映射,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
2.Redis中的基本数据类型
redis的五种数据类型是:1、string(字符串);2、hash(哈希);3、list(列表);4、set(集合);5、sort set (有序集合)。其中,string(字符串)是redis中最基本的数据类型,一个key对应一个value,string 可以包含任何数据。
详细内容见博主的另一篇文章:【Redis基础】redis基础知识总结——数据类型(字符串,列表,集合,哈希,有序集合)
【Redis基础】Redis新数据类型(Bitmaps,HyperLoglog,Geospatial)命令简介与案例演示
3.TCP的网络协议
TCP/IP协议定义
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。
TCP/IP协议组成
TCP/IP结构模型分为应用层、传输层、网络层、链路层(网络接口层)四层,以下是各层的详细介绍:
(1)应用层
应用层是TCP/IP协议的第一层,是直接为应用进程提供服务的。
a. 对不同种类的应用程序它们会根据自己的需要来使用应用层的不同协议,邮件传输应用使用了SMTP协议、万维网应用使用了HTTP协议、远程登录服务应用使用了有TELNET协议。
b. 应用层还能加密、解密、格式化数据。
c. 应用层可以建立或解除与其他节点的联系,这样可以充分节省网络资源。
(2)传输层
作为TCP/IP协议的第二层,运输层在整个TCP/IP协议中起到了中流砥柱的作用。且在运输层中,TCP和UDP也同样起到了中流砥柱的作用。
(3)网络层
网络层在TCP/IP协议中的位于第三层。在TCP/IP协议中网络层可以进行网络连接的建立和终止以及IP地址的寻找等功能。
(4)链路层(网络接口层)
在TCP/IP协议中,网络接口层位于第四层。由于网络接口层兼并了物理层和数据链路层。所以,网络接口层既是传输数据的物理媒介,也可以为网络层提供一条准确无误的线路。
TCP/IP协议特点
TCP/IP协议能够迅速发展起来并成为事实上的标准,是它恰好适应了世界范围内数据通信的需要。它有以下特点:
(1)协议标准是完全开放的,可以供用户免费使用,并且独立于特定的计算机硬件与操作系统;
(2)独立于网络硬件系统,可以运行在广域网,更适合于互联网;
(3)网络地址统一分配,网络中每一设备和终端都具有一个唯一地址;
(4)高层协议标准化,可以提供多种多样可靠网络服务。
4.java中常见的锁
- synchronized锁
在Java中,synchronized是一种非常常见的,使用广泛的锁机制。在多线程环境下,多个线程同时访问同一个资源的时候,会造成数据的混乱和不一致,这个时候就需要使用锁来保证资源的同步性。synchronized锁可以保证同步性,提高程序的可靠性。在Java中,对普通方法和代码块的访问都可以加上synchronized锁。
- ReentrantLock锁
除了synchronized锁以外,在Java中还有一种比较常见的锁就是ReentrantLock锁。ReentrantLock锁是一种基于CAS(Compare And Swap)原理实现的锁机制。它和synchronized锁相比,可以实现更加灵活和高效的锁控制。ReentrantLock锁可以使用在多线程环境中,同步相关的代码,保证程序的正确性和稳定性。
- ReadWriteLock锁
在Java中,除了普通的锁以外,还有一种读写锁(ReadWriteLock)。这种锁是一种比较特殊的锁,在读多写少的情况下,可以提升程序的性能。在使用ReadWriteLock时,对于读的操作,可以同时进行读取,而对于写的操作,需要等待所有的读操作全部结束才能进行写操作。可以说,ReadWriteLock是一种比较高效的锁机制。
- Condition锁
当我们使用synchronized锁或者ReentrantLock锁时,可以使用Condition锁来进一步控制上锁和释放锁的逻辑。Condition锁可以用来控制锁的释放和获取,使程序的逻辑更加明确和清晰。通过Condition锁的使用,可以简化程序的代码维护和开发。
- StampedLock锁
在Java8以后,又新增了一种锁机制,叫做StampedLock锁。相较于ReadWriteLock锁而言,StampedLock锁更加高效。在使用StampedLock锁时,我们不需要像ReadWriteLock锁那样使用两个锁去控制读和写,只需要一个StampedLock锁即可。在多线程环境下,使用StampedLock锁可以提高程序的性能。
- synchronizedLock和ReentrantLock锁的区别
synchronized锁和ReentrantLock锁两者都可以用来保证多线程环境下的同步性和正确性。但两者之间有很大的区别,synchronized锁是Java语言内建的一种锁机制,而ReentrantLock锁则是JDK提供的一种锁机制。另外,synchronized锁仅支持非公平锁,而ReentrantLock锁则同时支持公平锁和非公平锁。使用公平锁可以保证线程获取锁的顺序合理,不会出现饥饿的情况。
- ReadWriteLock锁和StampedLock锁的区别
ReadWriteLock锁和StampedLock锁都适合在读多写少的多线程环境下使用。两者之间的区别在于,ReadWriteLock锁使用两把锁来控制读和写,而StampedLock锁只需要一把锁。StampedLock锁使用了一种更加高效的乐观读的机制,可以进一步提升程序的性能和效率。同时,StampedLock锁的读和写并不是严格的分开的,而是具有一定的交互关系,挑选合适的锁机制可以大大提高程序的效率。
5.Hashmap的底层数据结构、底层源码、扩容机制+源码
hashmap由数组链表加红黑树组成,每添加一个键值对,计算键的hash值,将获取到的值高16位和低16位进行异或处理,得到的值与数组长度减一进行按位与运算,获取到的最终值即该元素所在数组位置,若该位置有元素,即存在hash冲突,采用链式存储法存储该元素,当链表长度超过8时,链表转为红黑树以增加其查询效率。当hashmap当前所占数组数量超过hashmap容量乘以装载因子时,且其新加的元素出现了hash冲突,触发扩容机制。扩容长度为两倍扩容。将每个键的hash值与新的数组长度减一进行按位与运算,所得的值如果与原位值一样,则放于原位置,反之则放于原位置加旧容量
一般讲到这里面试官通常会追问:
为什么要对hashcode方法获得的值进行异或处理。
用异或处理是为了保证在length较小的时候,高位也能参与hash值的计算,用异或hash值中有任何一位改变得到的值就会改变。同时,调用hashcode方法所获得的32位数字不具备数学意义上的随机性,高16位低16位的异或处理后,最终的16位数字每位0或1的概率都是百分之50。
为什么落于数组的位置,取的的是异或处理获得的值和数组长度-1进行按位与运算,且数组长度必须是二的幂次方。
数组长度为2的幂并且按位与运算是为了保证数据均匀分布在数组上,因为二进制中2的幂次方减一所有位数均为1,按位与运算可保证获得的值位于0到数组长度减一之间。且得到的二进制值每一位0或1的概率都是百分之50,固数据落在每个格子的概率都是一样的。若容量不是2的幂数组中某个格会出现一直为空的情况,即便设置的不是2的倍数,java也会将容量更改为距离最近的2的幂。若不采用异或运算,会导致最终值超出数组界线。
JDK1.8对hashmap扩容做了什么优化
jdk1.8将hashcode方法高低16位异或运算所获取的值与新的数组长度减一进行按照位与运算,新的数组与原数组在二进制中变化是新数组比原数组多了个1,所以按位与运算后只有首位有可能发生变化,且首位发生变化的概率是百分之五十,因为原hash值是异或运算得出,每一位是零或一的概率是百分之五十),如果与原位置一样,则放在原位置,如果与原位置不一样,再放于原位置加旧容量。这样做的目的是让元素均匀分布在新数组中,因为首位变化率是百分之50,固原位置数据是否不变的概率是百分之50,而原位置加旧容量处是否有数据的概率也是百分之50。
为什么默认装载因子是0.75
装载因子太大链表过多查询效率低,太小数组利用率低,数据分散。0.75符合数学泊松分布。
6.java面向对象的特点
一、封装(Encapsulation)
封装是Java面向对象编程的基本特征之一。它将数据和相关的方法封装在一个对象中,并对外部提供公共接口来访问这些数据和方法。通过封装,对象的内部细节被隐藏起来,外部只能通过接口来与对象进行交互,而无需关心对象内部的实现细节。
封装的优势在于它提高了代码的可维护性和复用性。封装将数据和行为封装在一起,使得代码更加模块化和可组织。这种模块化的设计有助于代码的维护和调试,并且可以减少对其他代码的依赖性。此外,封装还可以保护数据的安全性,防止数据被意外修改或破坏。通过提供合适的访问控制,可以限制对数据的直接访问,从而确保数据的完整性和一致性。
二、继承(Inheritance)
继承是Java面向对象编程的另一个重要特征。继承允许创建一个新类,通过继承已有类的属性和方法,从而实现代码的重用和扩展。在继承关系中,被继承的类称为父类或基类,而继承父类的类称为子类或派生类。
继承的优势在于它提供了代码的重用性和层次化的设计。通过继承,子类可以直接使用父类中已经定义的方法和变量,无需重新编写相同的代码。这样可以减少代码的冗余,提高代码的复用性和可维护性。此外,继承还允许我们建立对象之间的层次结构,通过使用父类引用指向子类对象,可以实现多态性。
三、多态(Polymorphism)
多态是面向对象编程中的一个重要概念,也是Java的核心特征之一。多态是指在面向对象编程中,同一消息可以被不同类型的对象解释和执行的能力。具体来说,当多个对象都可以以某种方式响应相同的消息时,就存在多态。
多态的优势在于它提高了代码的灵活性和可扩展性。通过多态,我们可以编写更通用、更可复用的代码。多态使得代码可以适应不同类型的对象,而无需修改已有的代码逻辑。这样,我们可以通过定义通用的接口或抽象类来操作对象,而不需要关心具体的对象类型。这种灵活性使得我们可以在运行时动态地决定使用哪个对象的实现,从而实现更高层次的代码组织和扩展。
在Java中,多态可以通过方法的重写和接口的实现来实现。方法的重写指的是子类可以重写父类的方法,并根据自己的需要重新实现方法的逻辑。接口的实现则是指一个类可以实现一个或多个接口,并实现接口中定义的方法。通过多态,我们可以使用父类引用指向子类对象,实现统一的调用接口,而在运行时根据实际对象的类型来决定调用哪个对象的方法。