Java面试干货
简述:本文涵盖了JavaSE,JavaEE,数据库,框架等方面的知识点,包括JVM、JIT和AOT、编译性语言和解释性语言区别、常见异常、集合、Spring、数据库范式、事务特征、索引、SpringBoot、IOC、AOP、Vue生命周期、跨域问题、悲观锁和乐观锁等。内容全面,涵盖面广,有助于对Java面试干货的全面了解。
文章目录
- Java面试干货
-
- JavaSE
-
- javaSE和JavaEE
- jdk jre jvm
- JVM是什么
- JVM内存结构
- JIT和AOT
- 编译性语言和解释性语言区别
- Java常用的6个包
- 标识符和关键字的区别
- JAVA和C++的区别
- 对类,方法,变量命名规则
- JDK版本区别
- Java跳出循环的三种方式
- 基本数据类型和引用数据类型
- 基本类型和包装类型的区别
- 包装类型的缓存机制
- 类型的自动转换
- 引用数据类型和基本数据类型
- final关键字的作用?
- 局部变量和成员变量的区别
- 接口和抽象类的区别
- 常见的异常有哪些
- throw和throws的区别?
- Java8有哪些新特性
- 阻塞和非阻塞的区别
- 静态方法为什么不能调用非静态的成员
- 谈谈对String的理解
- i++ 和 ++i
- 集合
- 如何集合线程安全
- 常用集合的扩容机制
- 为什么会有负载因子
- IO
- Java是什么
- Hashcode
- GC垃圾回收
- 重载和重写
- 可变长参数
- static
- 面向过程编程
- 数据库
- Spring框架
-
- Spring是什么
- AOP实现原理
- Bean的生命周期
- SpringBoot 中的 starter 到底是什么 ?
- Spring用到了那些设计模式
- SpringMVC是什么
- SpringMVC的执行流程是什么
- Spring AOP的相关术语
- Bean注入容器的方式
- Bean的作用域
- Spring自动装配
- 循环依赖与解决
- SpringBoot
- IOC控制反转
- Spring 自动装配的三种方式,以及如何使用注解进行装配
- Springboot的核心注解
- 拦截器和过滤器是什么,有什么区别
- HandlerInterceptor和WebMvcConfigurer有什么关联
- Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
- Spring cloud 五大组件,分别有什么用
- 前端高频问题
- JavaWeb高频面试题
- 补充
JavaSE
javaSE和JavaEE
**javaSE:**是Java的基础版本。跟家偏向于开发一些桌面应用程序,和简单的服务器程序。
JavaEE:是在SE的基础上进行了功能性的增强,比如有(Servlet、JSP、EJB、JDBC、JPA、JTA、JavaMail、JMS)。用于开发复杂的业务和Web应用程序。
jdk jre jvm
jdk是开发工具类包,包含了运行环境和开发
jre是运行环境和核心类库
jvm是Java的虚拟机
JVM是什么
jvm是虚拟出来的计算机,所以也叫JVM虚拟机。
JVM负责将编译好的class文件,编译成机器码运行在各个系统程序上运行。达到了高可移植性,只有生成了.class文件,就可以在多个平台上运行
JVM内存结构
- 堆(Stack)
堆是Java虚拟机中用于存储对象实例的内存区域。所有的对象实例以及数组都在堆上分配。堆是Java内存管理中最大的一块区域,也是线程共享的。 - 栈(Heap)
Java虚拟机栈用于存储方法运行时的局部变量、操作数栈、动态链接、方法出口等信息。每个方法执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、方法出口等信息。栈是线程私有的,每个线程在执行方法时都会创建对应的栈帧。 - 方法区(Method Area)
**方法区存储类中的数据结构、变量、静态变量,编译后数据存储的内存区域。**在方法区中,包含了运行时常量池(存储编译期生成的各种字面量和符号引用)、类信息、字段信息、方法信息、构造函数等。方法区是线程共享的内存区域,它在程序启动时被创建,并且在程序结束时被销毁。 - 本地方法栈(Native Method Stack)
本地方法栈与Java虚拟机栈类似,区别在于本地方法栈是为本地(native)方法服务的。它存储了调用本地方法时的信息。 - 程序计数器(Program Counter Register)
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的多线程环境下,程序计数器是线程私有的,每个线程都会拥有一个程序计数器。 - 运行时常量池(Runtime Constant Pool)
运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
在JVM内存结构中,以下是私有的部分和共享的部分:
私有部分:
-
Java虚拟机栈(Stack):
- 每个线程都有自己的栈,用于存储方法运行时的局部变量、操作数栈、动态链接、方法出口等信息。
-
本地方法栈(Native Method Stack):
- 每个线程拥有自己的本地方法栈,存储本地方法调用相关信息。
-
程序计数器(Program Counter Register):
- 每个线程都有自己的程序计数器,用于存储当前线程执行的字节码行号指示器。
共享部分:
-
堆(Heap):
- 所有线程共享的内存区域,用于存储对象实例和数组。
-
方法区(Method Area):
- 存储类的结构信息、常量、静态变量、即时编译器编译后的代码等数据。
- 运行时常量池等。
- 是所有线程共享的。
JIT和AOT
为了加快代码的运行速度,JIT会把热点代码会在JVM编译class文件为机器码的时候给记录下来。下次运行可以直接运行机器码,速度会更加快。
JIT采用的是延迟评估(Lazy Evaluation)的做法,每次执行的时候收集运行信息。把常用到的热点代码给记录下来,所以Java代码会越执行越快。
AOT是JDK 9引入的一种新的编译模式,和JIT不同的是。他会在程序被执行前就将其编译成机器码。提高Java的启动速度,减少内存占用增强Java的安全性(AOT 编译后的代码不容易被反编译和修改)但它只是在系统执行之前进行操作的
编译性语言和解释性语言区别
编译性语言:将代码全部转换为机器码,给设备进行运行。开发效率慢,运行速度快。比如:C,C++,Rust
解释性语言:将代码一句一句的解释为机器代码后在进行执行。开发效率快,运行速度慢。比如:Java,python,PHP
但是现在Java已经具备这两个的特征了。因为Java程序要先编译,生成.class文件。然后通过JVM进行解释。
Java常用的6个包
- java.lang
- 包含了Java中基本的数据包装类型,String,Object
- 提供了Java语言的基本支持。
- java.util
- 包含了集合框架、日期时间处理、随机数生成器、Scanner等常用的实用工具类。
- java.io
- 提供了对系统输入输出的支持,包括文件读写、流处理等。
- java.net
- 包含网络编程相关的类,用于实现网络通信、Socket编程、URL处理等。
- java.sql
- 包含了Java对于数据连接的相关类,用于对数据库交互
- java.awt和java.swing
- 抽象窗口工具类,用于创建图形用户界面(GUI)应用程序的基本组件和功能。
- 建立在java.awt之上的,提供了更丰富的GUI组件,用于创建现代化的GUI应用程序。
标识符和关键字的区别
标识符:是指为类,方法名,变量取名的。这就叫标识符
关键字:系统中的有特殊含义和使用方法的名称,如IF,FOR等等。也被称作特殊的标识符
JAVA和C++的区别
相同:Java 和 C++ 都是面向对象的语言,都支持封装、继承和多态
不同:
- Java不提供指针直接访问内存
- Java的类只能单继承,接口可以多继承。C++可以多继承
- Java有垃圾回收机制(GC)
- Java和C++都可以进行方法重载,C++多了一个操作符重载,可以重新定义运算符的规则
对类,方法,变量命名规则
类名大驼峰规则
方法是小驼峰规则
变量是是全小写,常量是全大写
JDK版本区别
JDK8:引入了Lambda表达式、Stream API、新的日期时间API等重要功能。
JDK9:在9往后,就不需要去区分JRE和JDK的关系了,取而代之的是模块系统
Java跳出循环的三种方式
break
跳出循环
retrun
跳出方法
continue
结束本次循环
基本数据类型和引用数据类型
基本数据有四大类,分为字符型,布尔型,浮点型,整形
其中细分有byte,short,int,long(整形),char,Boolean,float,double
为什么取值范围负数会多一个,其实就是符号位被用来做了个补充,用来代表多出来的那个负数!
基本类型和包装类型的区别
在介绍两个之间的区别之前,还有两个概念。拆箱与装箱,拆箱就是包装类型转换为基础类型过程。反之,装箱就是基础类型转换为包装类型的过程。
-
用途:除去定义基本的变量以外,使用方法形参尽量使用包装类型。因为包装类型可以用于泛型。
-
存储方式:基本类型的局部变量存放在栈中,成员变量存放在堆当中。而包装类型是对象类型**,所以存放在堆当中。
-
占用空间:相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小。
-
默认值:成员变量包装类型不赋值就是
null
,而基本类型有默认值且不是null
。 -
比较方式:对于基本数据类型来说,
==
比较的是值。对于包装数据类型来说,==
比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用equals()
方法。
byte
:默认值是0short
:默认值是0int
:默认值是0long
:默认值是0Lfloat
:默认值是0.0fdouble
:默认值是0.0dchar
:默认值是’\u0000’,即空字符boolean
:默认值是false
包装类型的缓存机制
Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False
。
类型的自动转换
当表达式中,混合多个变量类型。程序将会把所有变量编程容量最大的数据类型进行计算
精度大的变量赋值给小的会报错,反之转换
(byte,short)和char之间不会相互自动转换
boolean不参与转换
引用数据类型和基本数据类型
基本数据类型是直接存储在栈上的,本身就是数据。
引用数据类型,是在堆内存中。开辟了一片空间,对象进行数据的引用。使用了new实际上就是在堆内存中开辟了一片空间,等号位指向。
常见的引用数据类型有:类,数组,字符串
final关键字的作用?
修饰类:类不能被继承
修饰方法:方法不能被重写
修饰变量:基本数据变量值不能被修改,引用数据变量的地址不能被修改。但是地址中的值可以被修改的。
局部变量和成员变量的区别
局部变量创建于代码块中,也只会生效于该代码块中。代码块执行完毕会进行销毁。不会自动进行赋值。
成员变量是写在类内部,方法外面的变量,可以在类中进行使用,或者实例化类后,通过类名调用。如果没有被final修饰,不需要显示的赋值,系统会给默认值。
接口和抽象类的区别
- 接口中只有抽象方法,抽象类中由已经实现的方法
- 一个类只能继承一个抽象类,但是可以实现多个接口
- 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
- 子类继承抽象类时,必须实现所有抽象方法,除非子类也声明为抽象类。
- 类可以实现多个接口,但只能继承一个类。
常见的异常有哪些
ClassCastException
类型转换异常IndexOutOfBoundsException
数组越界异常NullPointerException
空指针SQLException
:SQL 异常,通常在数据库操作中抛出。NoSuchBeanDefinitionException
:没有找到bean异常
throw和throws的区别?
throw:抛出一个具体的异常
hrows的:方法声明中指定可能会抛出的异常类型。可能会抛出那些异常
Java8有哪些新特性
- Lambda表达式,允许这个函数作为方法的参数
- Date Time API :增强了对时间的处理
- Optional 类,用来解决空指针异常
- Stream API:提供了一个新的方式去处理集合,简化代码提高性能。流式操作
阻塞和非阻塞的区别
这两个东西关注的是线程的状态
阻塞:调用的结果没有返回结果之前,这个线程会被挂起。只有的到返回结果的时候,才恢复运行
非阻塞:不能立刻得到结果的时候,不会阻塞当前的线程
静态方法为什么不能调用非静态的成员
静态方法是属于类的,在类加载的时候。就会分配内存。可以直接通过类名去进行访问。而非静态的成员,需要跟着类的实例化才功能去进行访问和使用。
谈谈对String的理解
String的底层是一个被Final修饰的char数组
初始化String有三种初始化方法,一个直接赋值,一个是无参构造来赋值,一个用有参构造来赋值。存储在内存中的位置不同。
直接赋值看内存有无一样的字符串,没有会进行创建有会直接引用
String str1 = "hello";
String str2 = "hello";
// true
System.out.println(str1 == str2);
使用无参构造创建,后续赋值会查看常量池里面是否有值
String str1 = new String();
String str2 = "hello";
str1 = "hello";
System.out.println(str1 == str2);// true
使用有参构造创建,必须分配内存空间。
String str1 = new String("hello");
String str2 = "hello";
// false
System.out.println(str1 == str2);
i++ 和 ++i
i++ 先执行再加 ++i 先加再执行
集合
集合分为单列集合和双列集合
List接口有ArrayList和LinkedList实现,是有序可重复的。
Set 无序集合,不能重复。实现类有HashSet和LinkedHashSet
Map 键值对集合,是双列集合。
Collection包含了 List和Set子接口,不包括map的接口。
如何集合线程安全
1:可以使用线程安全的集合
比如Vector
、Hashtable
、ConcurrentHashMap
、CopyOnWriteArrayList
2:使用同步代码块(synchronized)
在多线程环境下,通过同步关键字synchronized
或Lock
接口来保护对集合的访问,确保同一时间只有一个线程可以访问集合,从而保证线程安全性。
List<String> list = new ArrayList<>();
synchronized(list) {
// 在同步块内对集合进行操作
}
// 或者使用Lock接口
Lock lock = new ReentrantLock();
lock