中科联芯面试-Java
1、i++和++i的区别
在Java中,i++ 和 ++i 都是用来对变量 i 进行自增操作的,但是它们在表达式中使用时的行为有所不同。
i++(后缀增量):返回变量的原始值(自增前的值),在表达式执行完毕后,变量的值会增加1。
++i(前缀增量):先将变量的值增加1,返回变量的新值(自增后的值)。
int i = 0; // 使用 i++ 在复合赋值中的行为 int c = 10 + i++; // c 被赋值为 10 + 0 = 10, 然后 i 变为 1 // 使用 ++i 在复合赋值中的行为 int d = 10 + ++i; // i 先变为 2, 然后 d 被赋值为 10 + 2 = 12
2、简单介绍一下SpringIOC
·IOC(inversion of control)控制反转,在IOC之前对象的控制权在我们的代码中,我们自己new的对象,在Spring中,应用程序不再控制对象的创建,而是被动地接受由容器注入的对象。
Spring的IOC对象由IOC容器创建并管理,我们只需要在想要使用的时候从容器中获取就行了;@Resource 、@Autowired
*【拓展:Autowired和Resource的关系:
·Autowired在获取bean的时候,先是byType的方式,再是byName的方式;
·Resource在获取bean的时候,和Autowired恰好相反,先是byName方式,然后再是byType方式】
3、判断对象的判断是否相同
·在Java中,可以使用==运算符来比较两个对象的引用是否相同,也就是它们是否指向同一个对象实例。(比对内存地址);
·默认情况下,equals()方法比较的是对象的引用。但你可以根据需求重写这个方法,让它比较对象的实际内容。同时重写equals方法时,也必须重写hashCode方法,以确保两个相等的对象拥有相同的哈希码。
·使用java.util.Objects类的deepEquals()方法
如果想比较两个对象数组是否深度相等(即数组中的所有元素都相等),可以使用java.util.Objects类的deepEquals()方法。
4、Spring bean中如何实现懒加载
在Spring框架中,可以通过【使用XML配置】【使用注解配置】【全局懒加载】的方式实现懒加载。
使用XML配置
<bean id="myBean" class="com.example.MyBean" lazy-init="true"/>
使用注解配置
@Component @Lazy public class MyBean { // ... }
全局懒加载
<beans default-lazy-init="true"> <!-- 其他Bean定义 --> </beans>
5、类的加载过程是怎么样的
Java中类的加载阶段分为加载(Loading)、链接(Linking)和初始化(Initialization)。其中连接过程又包含了验证、准备和解析。
·加载阶段的目的是将类的.class文件加载到JVM中。
·在链接阶段,Java类加载器对类进行验证、准备和解析操作。
·初始化是类加载的最后一步,也是真正执行类中定义的 Java 程序代码(字节码),初始化阶段是执行类构造器 <clinit> ()方法的过程。这里利用了一种懒加载的思想,所有Java虚拟机实现(如HotSpot等)必须在每个类或接口被Java程序首次主动使用时才初始化,但类加载不一定,静态代码块在类初始化时执行。
6、一个类中有静态代码块、静态方法、构造函数,他们的执行顺序是怎么样的,构造函数中是否可以使用静态代码块和静态方法
在Java中,类加载和初始化的过程遵循以下顺序:
静态代码块:当类被加载到JVM时,静态代码块(static initializer)会被执行,并且只执行一次。静态代码块通常用于初始化静态变量。
静态方法:静态方法(static method)可以在类加载之后的任何时间被调用,且不需要类的实例。
构造函数:当创建类的实例时,构造函数(constructor)会被调用。
以下是他们的执行顺序:
当类第一次被使用时(例如,创建类的实例或访问类的静态成员),JVM会加载这个类。
在类加载过程中,静态代码块会被执行(如果有的话)。
随后,如果创建了类的实例,构造函数会被执行。
构造函数不能直接调用静态代码块,因为静态代码块是类初始化的一部分,它不由构造函数控制。
构造函数可以调用静态方法,因为静态方法是类的成员,可以在类的任何地方被调用,包括构造函数。
7、java的byte基本数据类型范围
byte 8bit位 2^7 -> -128 ~ 127
拓展:
short 16位: -2^15 ~2^15-1
int 32位: -2^31 ~ 2^31 -1
long 64位 : -2^63 ~ 2^63 -1
float 32位,单精度 32 位 IEEE 754 浮点数。
double 64位,双精度 64 位 IEEE 754 浮点数。
char - 16位,表示 Unicode 字符,取值范围从 ‘\u0000’(即为 0)到 ‘\uffff’(即为 65,535)
boolean 表示逻辑值 true 和 false。
8、单例模式的线程安全写法
饿汉式(线程安全)在类加载时就立即初始化并创建单例对象。
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }
懒汉式(线程安全)
在类加载时不初始化,在第一次使用时初始化,使用同步方法保证线程安全。
public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
双重校验锁(线程安全)
结合了懒汉式和饿汉式的优点,既实现了延迟加载,又保证了线程安全(注意volatile)
public class Singleton { private volatile static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
静态内部类(线程安全)
利用静态内部类来实现延迟加载,并且保证了线程安全。
public class Singleton { private Singleton() {} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
枚举(线程安全)
使用枚举来实现单例是最简单、最安全的方式。
public enum Singleton { INSTANCE; public void someMethod() { // 方法实现 } }
*9、JDBC的参数
JDBC操作数据库的流程:
加载JDBC驱动:
早期版本需要显式加载驱动类:Class.forName("com.mysql.jdbc.Driver");
从JDBC 4.0(Java 6)开始,驱动加载是自动的,无需显式加载。
建立连接:
使用DriverManager.getConnection()方法建立到数据库的连接。
需要提供数据库的URL、用户名和密码。
示例:Connection conn = DriverManager.getConnection(url, username, password);
创建Statement或PreparedStatement:
Statement用于执行不带参数的简单SQL语句。
PreparedStatement用于执行带参数的SQL语句,可以防止SQL注入攻击,并且可以高效地执行多次。
执行SQL语句:
使用executeQuery()方法执行查询语句,返回ResultSet对象。
使用executeUpdate()方法执行更新(INSERT、UPDATE、DELETE)语句,返回影响的行数。
对于PreparedStatement,需要先设置参数。
处理结果:
对于查询操作,通过ResultSet对象遍历查询结果。
对于更新操作,处理executeUpdate()方法返回的影响行数。
关闭资源:
依次关闭ResultSet、Statement/PreparedStatement和Connection对象。
通常在finally块中关闭资源以确保即使在发生异常时也能关闭。
执行SQL需要的参数:
数据库URL:用于指定数据库的位置,格式通常是jdbc:subprotocol:subname。
例如:jdbc:mysql://localhost:3306/mydatabase
用户名:用于登录数据库的用户名。
密码:用于登录数据库的密码。
SQL语句:要执行的SQL命令。
参数(仅对于PreparedStatement):
如果SQL语句包含参数(如?),则需要为每个参数提供值。
使用setXXX()方法(如setInt(), setString()等)设置参数值。
*10、数据库的优化策略
1. 设计优化
规范化:确保数据库设计遵循规范化规则,减少数据冗余。
反规范化:在某些情况下,为了提高性能,可以适当反规范化,例如存储预先计算好的数据。
索引:合理创建索引,以加速查询速度,但要避免过度索引。
数据分区:将大表分割成更小、更易于管理的部分。
数据分片:将数据分布到不同的数据库实例中,提高并发处理能力。
2. 查询优化
选择合适的索引:根据查询条件创建索引,避免全表扫描。
优化SQL语句:重写SQL查询,避免使用子查询、联合查询等复杂结构。
减少数据量:使用LIMIT语句限制返回的数据量。
使用存储过程:将常用的查询逻辑封装为存储过程,减少网络传输。
**避免使用SELECT ***:只查询需要的列,减少数据传输量。
3. 硬件优化
增加内存:提高数据库缓存,减少磁盘I/O。
使用SSD:使用固态硬盘替换机械硬盘,提高I/O性能。
网络优化:优化网络配置,减少数据传输延迟。
4. 配置优化
调整缓冲池大小:根据系统内存调整数据库缓冲池大小。
调整日志文件大小:适当调整事务日志大小,避免频繁写入。
配置合理的连接池:使用连接池来管理数据库连接,避免频繁建立和关闭连接。
5. 性能监控
使用监控工具:使用数据库自带的监控工具或第三方工具监控数据库性能。
分析慢查询日志:定期分析慢查询日志,找出性能瓶颈。
系统性能分析:使用操作系统工具监控CPU、内存、磁盘I/O等指标。
6. 数据库维护
定期更新统计信息:确保数据库优化器有准确的统计信息来生成执行计划。
定期重建索引:重建碎片化的索引,提高查询效率。
数据归档:定期将不活跃的数据归档,减少数据库大小。
7. 应用层优化
缓存:在应用层使用缓存减少数据库访问。
批处理:使用批处理减少数据库交互次数。
读写分离:在高并发场景下,使用读写分离减轻数据库压力。
11、介绍一下Spring的事务,有哪些类型
编程式事务管理
TransactionTemplate:通过使用TransactionTemplate,可以将事务管理代码与业务逻辑代码分离,通过调用TransactionTemplate的execute方法来执行业务逻辑。
PlatformTransactionManager:直接使用PlatformTransactionManager实现类进行编程式事务管理,需要手动编写事务开始、提交和回滚的代码。
声明式事务管理
声明式事务管理是通过AOP(面向切面编程)实现的,它允许开发者通过声明的方式管理事务,而不需要编写事务管理代码。
基于XML配置:在Spring的配置文件中使用<tx>命名空间定义事务规则。
基于注解:使用@Transactional注解来声明事务边界。
事务传播行为
Spring事务的传播行为定义了事务方法之间的调用关系,以下是Spring支持的七种事务传播行为:
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入这个事务中。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。
事务隔离级别
Spring支持以下几种事务隔离级别,以避免脏读、不可重复读和幻读等问题:
DEFAULT:使用底层数据库的默认隔离级别。
READ_UNCOMMITTED:允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ_COMMITTED:允许读取并发事务已经提交的数据,可以防止脏读,但幻读或不可重复读仍有可能发生。
REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以防止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE:确保事务可以从数据库中读取到的数据均符合事务开始时的状态,或者事务在操作过程中可以使用的数据库内容其他事务均不能访问,可以防止脏读、不可重复读以及幻读。
【@Transactional 是Spring框架中用于声明式事务管理的关键注解。它可以应用于类或方法上,以指定事务的属性。以下是 @Transactional 注解的一些常用参数:
value (或 transactionManager):
类型:String
描述:用于指定事务管理器的名称。在存在多个事务管理器的情况下,可以用来指定具体使用哪一个事务管理器。
propagation:
类型:Propagation
描述:定义事务的传播行为。默认值为 REQUIRED。
isolation:
类型:Isolation
描述:定义事务的隔离级别。默认值为 DEFAULT。
timeout:
类型:int
描述:定义事务的超时时间,单位为秒。默认值为 -1,表示不超时。
readOnly:
类型:boolean
描述:表示当前事务是否为只读事务。默认值为 false。对于只读操作,可以设置为 true,这样可以帮助数据库引擎优化事务。
rollbackFor:
类型:Class<? extends Throwable>[]
描述:定义导致事务回滚的异常类型。默认情况下,Spring 只会回滚运行时异常(RuntimeException)和错误(Error)。
rollbackForClassName:
类型:String[]
描述:与 rollbackFor 类似,但是使用异常类的名称来指定。
noRollbackFor:
类型:Class<? extends Throwable>[]
描述:定义不会导致事务回滚的异常类型。
noRollbackForClassName:
类型:String[]
描述:与 noRollbackFor 类似,但是使用异常类的名称来指定。】
12、项目架构
不怎么问项目的业务内容,基本上就是根据你简历写的项目和技术架构来问八股
有好多答的不对,后面面试官也没耐心了,感觉悬,应该没过。
(2024/9/13更新:已经被感谢了,在意料之中,哈哈)
公司主要是给电网做人力资源系统,还有给电网做低代码的外包开发,主要是驻场开发。
上面问的问题我都整理答案出来了,想面试的同志可以参考。
#Java面试##中科联芯#
面试经验(八股去si,再也不想背八股了)