面试官的问题
- 我想问一下你这边有做过什么项目吗?
- 你方便讲一下你做过的那些项目吗,用了什么技术栈,包括你负责开发的内容是什么?(项目经验)
- 八大基本数据类型是什么?(基础)
- 你说一下long类型能直接运转吗?应该是问是否可以直接运算?(基础)
- 你了解单例模式吗?你可以讲一下吗(设计模式)
- 懒汉模式跟饿汉模式有什么区别?
- 说一下事务的几种特性?(事务)
- 说一下你在做项目的过程中都遇到过哪些异常吗?(异常)
- 说一下时间常用的三个类?(基础)
- 说一下日历的三个方法?
- 说一下session的生命周期吧。(session)
- 你在做项目的过程中有遇到过浮点精度的丢失吗?你是怎么解决的?
- 那我们有一个如果通过浮点进行计算的话,你会选择哪个数据类型去进行计算?
- 你能说一下常用的注解吗?spring的?还是其他呢?
- 你对线程了解吗?
- 线程跟进程有什么区别?
- 那你说一下什么是线程安全?
- 说一下==和equals的区别?
- 那你平常有用过linux吗?可以简单说一下一些常用命令吗?
问题解决
- 我想问一下你这边有做过什么项目吗?
我觉得挑一两个比较熟练的项目去说即可
- 你方便讲一下你做过的那些项目吗,用了什么技术栈,包括你负责开发的内容是什么?
挑一个最熟的项目去说。
- 八大基本数据类型是什么?
byte、short、int、long、float、double、char、boolean
- 你说一下long类型能直接运转吗
(我觉得是问long类型的数据可以直接运算吗)
long类型的数据是可以直接运算的,当你在一个表达式中混合使用 int 和 long 类型的值时,int 类型的值会被自动提升为 long 类型,以确保整个表达式的结果是 long 类型的。
- 你了解单例模式吗?你可以讲一下吗
单例模式(Singleton Pattern)是一种常用的软件设计模式,它的目的是确保一个类仅有一个实例,并提供一个全局访问点来获取这个唯一的实例。这种模式在需要控制资源访问,或者当实例化对象很消耗资源或很耗时,且这些资源或实例在全局只需要一个时非常有用。常见的单例模式的实现方式主要有:懒汉模式和饿汉模式。
- 懒汉模式跟饿汉模式有什么区别?
-
可以从对象的初始化时机、线程安全性以及资源利用方面等方面进行对比:
-
- 初始化时机
- 懒汉模式 :懒汉模式是一种
延迟加载
的单例模式。它的特点是在第一次使用时创建实例对象
,而不是在类加载时就创建。这种模式避免了在不需要实例对象时的资源浪费
,只有在需要时才进行创建。 - 饿汉模式 :饿汉模式则是一种
在类加载时就创建实例
的单例模式。它的特点是无论是否会被使用到,实例对象都在类加载时被创建。这种方式可以确保在任何情况下都能获取到同一个实例对象,但是可能会导致一些性能和资源上的浪费
,特别是在某些情况下实例对象并没有被使用到。
-
- 线程安全性
- 懒汉模式 :懒汉模式本身是
非线程安全的
,因为存在多个线程同时调用getInstance()方法并同时进入判断语句的可能性,从而导致创建了多个实例。为了实现线程安全,可以在getInstance()方法上添加synchronized关键字
,但这会带来性能上的开销。此外,还可以使用双重检查锁定(Double-Checked Locking)等方式来减少同步开销。 - 饿汉模式 :饿汉模式是
线程安全
的,因为实例对象已经在类加载时就创建好了,不存在多线程环境下的竞争问题。
-
- 资源利用与性能
- 懒汉模式:懒汉模式可以节省资源,因为它避免了在不需要实例对象时的资源浪费。但是,由于它需要在第一次使用时进行初始化,如果初始化过程比较复杂或者耗时较长,可能会影响到性能。
- 饿汉模式:饿汉模式在资源利用方面可能不如懒汉模式灵活,因为它无论是否会被使用到都会创建实例对象。但是,由于实例对象在类加载时就已经创建好了,所以在第一次调用时速度会更快,因为不需要进行初始化。
-
-
适用场景
- 懒汉模式 :适用于在第一次使用时才进行对象创建的场景,并且在实例对象初始化过程中没有复杂的线程安全要求。例如,文件管理器就是一个典型的例子,因为在应用程序启动时可能不需要立即读写文件,而是在需要的时候才进行相关操作。
- 饿汉模式:适用于需要在程序启动时就初始化的资源,且在整个应用程序的生命周期中都需要使用到的场景。例如,日志记录器就比较适合使用饿汉模式,因为日志记录功能通常需要在应用程序启动之初就准备好,并在整个应用程序的运行过程中记录日志消息。
- 综上所述,懒汉模式和饿汉模式各有优缺点,在实际应用中应根据具体场景进行综合考虑和设计。
- 说一下事务的几种特性?
- 事务具有四个基本特性,这四个特性也简称为ACID特性,具体包括:
- 原子性(Atomicity):原子性是指事务中的所有操作要么全部完成,要么全部不执行,它们是一个不可分割的工作单位。如果在执行事务的过程中发生了任何错误或者故障,那么已经执行的操作将会被撤销(回滚),整个事务就像是一个从未发生过的操作。这一特性确保了事务的完整性和一致性。
- 一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态转变到另一个一致性状态。在事务开始之前和事务结束之后,数据库的完整性约束(如主键约束、外键约束等)没有被破坏,所有的数据都保持逻辑上的一致性。如果在执行事务的过程中,数据库的完整性被破坏,那么事务将被终止,并且已经执行的操作会被回滚。
- 隔离性(Isolation):隔离性是指多个事务并发执行时,各个事务之间是相互隔离的,一个事务的执行不能被其他事务所干扰。数据库系统提供了一定级别的隔离性,使得并发执行的事务之间能够互不干扰,从而保证了数据的正确性和一致性。隔离性通常通过锁(如行锁、表锁等)或者多版本并发控制(MVCC)等机制来实现。
- 持久性(Durability):持久性也称永久性,是指一旦事务被提交,它对数据库的修改就是永久性的,即使系统发生故障也不会丢失。数据库系统通过日志和恢复机制来保证事务的持久性,即使发生系统故障,系统也能够通过日志来恢复已经提交的事务对数据库的修改。
- 说一下你在做项目的过程中都遇到过哪些异常吗?
(随便列举一些)
- 代码异常(Code Exceptions) :
- 空指针异常(NullPointerException):尝试访问或操作一个未被初始化的对象时抛出。常见于忘记检查对象是否为null就直接使用。
- 数组越界异常(ArrayIndexOutOfBoundsException):访问数组时使用了无效的索引(索引小于0或大于等于数组大小)。
- 类型转换异常(ClassCastException):强制类型转换时,被转换的对象不是目标类型或其子类的实例。
- 除数为零异常(ArithmeticException / / by zero):进行除法运算时,除数为零。
- 数据异常(Data Exceptions) :
- 数据格式错误:如解析JSON或XML数据时,数据格式不符合预期。
- 数据完整性异常:如数据库中的外键约束失败,尝试插入或更新数据时违反了数据完整性规则。
- 数据找不到异常:如查询数据库时,根据给定的条件没有找到相应的数据。
- 网络异常(Network Exceptions) :
- 连接超时(ConnectTimeoutException):尝试建立网络连接时,连接请求在等待响应时超时。
- 读取超时(SocketTimeoutException):从连接中读取数据时,操作超时。
- 网络不可达(UnknownHostException):无法解析主机名。
- 连接拒绝(ConnectionRefusedError):目标机器拒绝连接请求。
- 系统资源异常(System Resource Exceptions) :
- 内存溢出(OutOfMemoryError):JVM在尝试分配内存时,没有足够的内存空间可用。
- 文件不存在(FileNotFoundException):尝试访问的文件或目录不存在。
- 权限不足(SecurityException / AccessDeniedException):没有足够的权限执行某个操作,如读写文件、访问网络资源等。
- 第三方服务异常(Third-Party Service Exceptions) :
- 服务不可用(ServiceUnavailableException):依赖的第三方服务暂时不可用。
- API限制(RateLimitException):对第三方API的请求超过了其限制(如请求频率限制)。
- 认证失败(AuthenticationException):访问第三方服务时,认证信息无效或已过期。
- 逻辑异常(Logical Exceptions) :
- 业务逻辑错误:如订单金额计算错误、库存不足但订单仍被处理等。
- 状态不一致:系统的某个状态与预期不符,导致后续操作无法进行。
- 说一下时间常用的三个类?
LocalDate、LocalTime、LocalDateTime。(随便列举三个即可)
- 在Java中,处理时间常用的类分别是在java.util.Date、java.util.Calendar以及Java 8引入的java.time三个包下的类(如LocalDate、LocalTime、LocalDateTime等)
- 说一下日历的三个方法?
提到与日历相关的操作时,我们通常会想到java.util.Calendar类,因为它是一个抽象类,提供了操作日历字段(如年、月、日、小时等)的方法。从Java 8开始,引入了新的日期时间API(位于java.time包下),提供了更好的日期时间处理能力。
-
LocalDateTime常用方法的介绍:
*- 创建LocalDateTime对象
-
now():创建一个表示当前日期和时间的LocalDateTime对象。
-
of():通过指定的年、月、日、时、分、秒(以及可选的纳秒)来创建一个LocalDateTime对象。例如:LocalDateTime.of(2023, Month.JANUARY, 1, 12, 0, 0)。
-
- 获取LocalDateTime对象的属性
- getYear()、getMonth()、getDayOfMonth()、getHour()、getMinute()、getSecond()等:分别用于获取LocalDateTime对象中的年、月、日、小时、分钟和秒等属性。
-
- 修改LocalDateTime对象的属性
- withYear()、withMonth()、withDayOfMonth()、withHour()、withMinute()、withSecond()等:将LocalDateTime对象的某个属性设置为指定的值,并返回一个新的LocalDateTime对象,原对象保持不变(因为LocalDateTime是不可变的)。
-
- 日期时间的加减
- plusYears()、plusMonths()、plusDays()、plusHours()、plusMinutes()、plusSeconds()等:在LocalDateTime对象上添加指定的时间段,并返回一个新的LocalDateTime对象。minusYears()、minusMonths()、minusDays()、minusHours()、minusMinutes()、minusSeconds()等:从LocalDateTime对象中减去指定的时间段,并返回一个新的LocalDateTime对象。
-
- 比较LocalDateTime对象
- isBefore(LocalDateTime other):判断当前对象是否早于指定的LocalDateTime对象。
- isAfter(LocalDateTime other):判断当前对象是否晚于指定的LocalDateTime对象。
-
- 格式化LocalDateTime对象
- format(DateTimeFormatter formatter):将LocalDateTime对象格式化为指定的日期和时间格式的字符串。例如:DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String formattedDateTime = dateTime.format(formatter);。
-
- 计算时间差
- 虽然LocalDateTime本身不提供直接计算时间差的方法,但可以使用Duration类或ChronoUnit类来计算两个LocalDateTime对象之间的时间差。
- 使用Duration类:Duration duration = Duration.between(start, end);,然后可以通过duration对象获取到时间差的具体数值,如秒、毫秒等。
- 使用ChronoUnit类:可以指定计算时间差的单位,如年、月、日、小时等。例如:long days = ChronoUnit.DAYS.between(start, end);。
- 说一下session的生命周期吧。
- Session的创建
- 创建时机 :Session在用户第一次访问服务器并请求JSP、Servlet等
动态资源
时创建。只访问HTML、图片等静态资源
并不会触发Session的创建。如果需要,可以通过调用request.getSession(true)来强制生成Session。 - 存储位置:Session存储在服务器端,通常保存在服务器的内存中,以便快速存取。
- 创建时机 :Session在用户第一次访问服务器并请求JSP、Servlet等
- Session的维护
- 更新最后访问时间:只要用户继续访问服务器,每次请求无论是否读写Session,服务器都会更新Session的最后访问时间,并维护该Session。这表示用户的Session处于"活跃"状态。
- session的唯一性:每个用户都会有一个独立的Session,通过Session ID来唯一标识。Session ID通常通过Cookie(名为JSESSIONID)发送给客户端,以便服务器能够识别不同的用户会话。
- Session的销毁
- 自动销毁:当Session的生命周期超时(即长时间没有活动),服务器会自动将其从内存中清除。这个时间通常是可配置的,默认在Tomcat中为30分钟。
- 手动销毁:开发者可以通过调用Session的invalidate()方法来手动销毁Session。这通常用于登出、超时等场景。
- Session生命周期的设置
- 在Servlet中设置:通过调用session.setMaxInactiveInterval(int interval)方法,以秒为单位设置Session的超时时间。
- 在web.xml中设置:在web应用的web.xml文件中,
通过<session-config>标签的<session-timeout>子标签设置Session的超时时间,单位为分钟。
- 在Tomcat的全局配置中设置:在Tomcat的conf/web.xml文件中,也可以设置全局的Session超时时间,这将影响Tomcat服务器上所有未指定超时时间的web应用。
- Session的注意事项
- 内存管理 :由于Session存储在
服务器端
,如果Session内容过于复杂或数量过多,可能会导致内存溢出。因此,应尽量减少Session中存储的数据量。 - Cookie依赖:Session的正常运行依赖于客户端浏览器的Cookie支持。如果浏览器禁用了Cookie,则需要通过URL地址重写等方式来传递Session ID。
- 内存管理 :由于Session存储在
- 你在做项目的过程中有遇到过浮点精度的丢失吗?你是怎么解决的?
- 那我们有一个如果通过浮点进行计算的话,你会选择哪个数据类型去进行计算?
-
在项目开发过程中,确实经常会遇到
浮点数精度丢失的问题
,这主要是因为计算机内部使用二进制来表示浮点数
,而某些十进制小数在二进制下无法精确表示
,导致精度损失。这种问题在财务计算、科学计算等领域尤为关键。 -
以下是一些我遇到并解决浮点精度丢失的方法:
- 使用BigDecimal类:在Java中,BigDecimal类提供了一种精确的浮点数计算方式。这个类可以表示任意精度的十进制数,并且可以执行精确的数学运算。当需要处理精确的小数运算时,可以使用BigDecimal来代替基本数据类型float和double。
java
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal sum = a.add(b); // 结果为 0.3
-
设置浮点数的精度:如果必须使用float或double类型,并且可以接受一定程度的精度损失,可以通过设置浮点数的小数点后的位数来尽量减小误差。这通常通过四舍五入等方式实现,但需要注意,这并不能完全避免精度丢失。
-
避免不必要的浮点数运算:在算法设计时,尽量避免不必要的浮点数运算,或者通过数学变换将浮点数运算转化为整数运算。例如,在计算百分比时,可以先将百分比乘以100转换为整数进行计算,最后再除以100得到结果。
- 了解浮点数在计算机中的表示:深入理解浮点数在计算机中的表示方式,以及为什么会有精度丢失的问题,有助于在设计算法时考虑到这一点,并采取相应的措施来减少误差。
-
使用第三方库:有些第三方库提供了更精确的浮点数运算功能,可以考虑使用这些库来替代Java标准库中的浮点数运算。
-
测试和验证:在开发过程中,对涉及到浮点数运算的代码进行充分的测试和验证,确保在各种情况下都能得到预期的结果。特别是要关注那些可能导致精度丢失的边界情况。
-
文档和注释:在代码中添加适当的文档和注释,说明哪些部分使用了浮点数运算,并可能存在精度丢失的问题。这样有助于其他开发人员理解代码,并在使用时采取相应的措施来避免或减轻精度丢失的影响。
-
通过上述方法,可以在一定程度上解决或减轻浮点数精度丢失的问题。然而,需要注意的是,由于计算机内部表示的限制,完全避免精度丢失是不可能的。因此,在开发过程中需要根据实际情况选择合适的方法来尽量减小误差。
- 你能说一下常用的注解吗?spring的?
- 你对线程了解吗?
- 线程跟进程有什么区别?
- 进程是指运行中的程序
- 进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程,有它自身的产生、存在和消亡的过程。
- 线程是由进程创建的,线程的进程的一个实体
- 一个进程可有多个线程
- 那你说一下什么是线程安全?
- **线程安全(Thread Safety)**是编程中的一个重要概念,特别是在
并发编程
中。它指的是在多线程环境下,一个类的实例在被多个线程同时访问时,能够表现出正确的行为,并且其内部数据在并发访问时保持一致性、完整性和原子性,不会出现数据污染等意外的副作用
。 - 线程安全性的几个关键方面:
- 原子性:一个或多个操作在执行过程中不会被其他线程中断或干扰。
- 可见性:当一个线程修改了某个共享变量的值,其他线程能够立即得知这个修改。
- 有序性:程序执行的顺序按照代码的先后顺序执行(但在多线程环境下,由于指令重排序等原因,实际执行顺序可能与代码顺序不一致,这需要通过同步机制来确保有序性)。
- 线程安全问题的常见原因:
- 共享资源:多个线程访问同一个资源(如变量、数据结构等),且至少有一个线程会修改这个资源。
- 竞态条件:两个或多个线程在访问共享资源时,它们的执行顺序或执行结果依赖于它们的相对执行速度,这可能导致不可预测的结果。
- 实现线程安全的方法:
- 使用同步机制:如Java中的synchronized关键字、ReentrantLock等,可以确保同一时刻只有一个线程能执行某个方法或代码块。
- 避免共享状态:如果可能,尽量设计避免共享状态的数据结构或类。
- 使用线程安全的类:Java中提供了许多线程安全的类,如Vector、Hashtable(尽管这些类通常因为性能原因不被推荐使用),以及ConcurrentHashMap、CopyOnWriteArrayList等并发集合。
- 不可变对象:创建不可变对象(一旦创建,其状态就不能被改变的对象),这样的对象自然是线程安全的。
- 使用并发工具:如CountDownLatch、CyclicBarrier、Semaphore等,可以帮助管理复杂的并发场景。
- 注意事项:
- 过度使用同步可能会导致性能问题,因为它限制了并发性。
- 在设计线程安全的类时,需要考虑其使用的上下文,因为线程安全通常是有代价的。
- 并不是所有的类都需要是线程安全的,只有那些会被多个线程同时访问且至少有一个线程会修改其状态的类才需要关注线程安全。
- 说一下==和equals的区别?
-
== :
- == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
- 1、比较的是操作符两端的操作数是否是同一个对象。
- 2、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
- 3、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true,如: int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。
- == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
-
equals:
- equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。
-
总结 :
所有比较是否相等时,都是用equals 并且在对常量相比较时,把常量写在前面,因为使用object在前面的话,object可能为null ,则发生空指针异常。
- 在阿里的代码规范中只使用equals ,阿里插件默认会识别,并可以快速修改,推荐安装阿里插件来排查老代码使用"==",替换成equals
- 那你平常有用过linux吗?可以简单说一下一些常用命令吗?
-
cd:进入到指定目录
-
ls:展示目录以及目录下的文件,-a展示隐藏文件
-
date:系统时间命令
-
clear:清屏
-
whoami:查看当前登录用户
-
shutdown:关机
-
init 0:也表示关机
-
reboot:重启
-
init 6:关机
-
grep:搜索命令(通过关键字过滤搜索条件)
-
echo:输出指定内容
-
>:重定向符:将左侧命令的结果,覆盖写入到符号右侧指定的文件中
-
>>:重定向符:将左侧命令的结果,追加写入到符号右侧指定的文件中
-
wc:统计文件的行数、单词数量等
-
|:管道符:将管道符左边命令的结果,作为右边命令的输入(可配合grep命令和wc命令使用)
-
&&:左边命令执行成功,再执行右边的命令
-
||: 左边命令执行失败,再执行右边的命令
-
ps:查看进程的状态
-
kill:关闭进程
-
vi/vim:进入文本编辑器
-
文件操作命令
- touch:主要作用是用来改变文件或目录的访问和修改时间。如果指定的文件或目录不存在,touch 命令会创建一个空的文件。
- cat:查看文本内容并输出到控制台上
- head:从头开始看文件。head -100 1.txt,查看前100行
- tail:从后开始查看文件。tail -100 1.txt,查看后100行。常用来查看日志。
- mkdir 创建文件夹。mkdir -p 当父目录不存在时,同时创建父目录
- mv:移动文件/文件夹
- cp:拷贝文件/文件夹
- rm:删除文件,-r表示删除文件夹
- pwd:显示当前目录
-
文件压缩解压
shell
tar 压缩(解压)命令。常用组合命令
tar -xvf apache-tomcat-9.tar 解压tomcat压缩文件,显示详细过程
tar -zxvf apache-tomcat-9.tar.gz 解压zip格式的压缩文件
tar -zxvf apache-tomcat-9.tar.gz -C mydir 解压到mydir目录下。
tar -cvf my.tar apache/ 压缩apache,并命名为my.tar
-c 创建压缩包
-x 解压
-v 回显
-f <指定包名>
-z 是否使用gzip格式压缩
- 权限控制方面
- groupadd:新增用户组
- groupdel:删除用户组
- groups:查看用户的用户组所属
- useradd -g 组名 用户名:给指定用户组新增用户
- usermod:修改用户的属性
- userdel:删除用户
- password:修改用户密码
- su:切换用户
- chmod:修改用户对文件操作的权限
- 防火墙配置
- systemctl status firewalld:查看防火墙的状态
- systemctl stop firewalld:关闭防火墙
- systemctl disable firewalld:关闭防火墙自启动
- firewall-cmd --list-ports:查看防火墙开放的端口
- irewall-cmd --add-port=80/tcp --permanent:开发指定端口,--permanent表示永久生效
- firewall-cmd --reload:重新加载防火墙配置
- firewall-cmd --remove-port=80/tcp --permanent:移除开放的端口
自我总结
- 自我介绍不够流畅:
- java基础知识不扎实,一些基础概念说不明白
- 回答的时候没有思路,没有逻辑,都是想到什么就答什么的
- 太紧张了