Java面试题大全
一.JavaSE 部分
基础部分
Java如何实现跨平台原理?
在不同的平台上安装不同版本的JVM虚拟机,只需要一次编译即可在不同平台的JVM中运行;(一次编译多处运行)
注:JVM只能运行编译以后的字节码文件 不能运行源文件
JDK JRE JVM三者的作用和关系?
编译器(jdk【整个java的核心】):把程序源代码转换成java字节码的中间语言
JRE:Java运行时环境, 也就是我们说的JAVA平台,所有的Java程序都要在JRE下才能运行
java虚拟机(jvm):再把字节码转变成机器可以识别的机器码
JDK包含((JRE包含JVM))
配置环境变量的目的?
告知windows系统 去到某个路径执行第三方安装程序中的相关命令
系统环境变量:所有用户都能使用
用户环境变量:只能应用当前登录用户
Java中基本数据类型有哪些?
byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。
short:16位,
int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。
long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。
float:32位,
double:64位,
boolean:只有true和false两个取值。
char:16位,存储Unicode码,用单引号赋值。
java 基本数据类型初始值(默认值)
niguang09 · 最新推荐文章于 2023-02-20 06:27:41 发布
若基本数据类型作为类成员的时候,即使没有进行初始化。java也会给定默认的初始值。默认是如下所示:
boolean false
char '/uoooo'(null)
byte (byte)0
short (short)0
int 0
long 0L
float 0.0f
double 0.0d
Integer 和 int的区别
- int是基本数据类型,变量中直接存放数值,变量初始化时值是0
- Integer是引用数据类型,变量中存放的是该对象的引用,变量初始化时值时null
- Integer是int类型的包装类,将int封装成Integer,符合java面向对象的特性,可以使用各种方法比如和其他数据类型间的转换
Integer和int的深入对比:
- 通过new生成的Integer对象和非通过new生成的Integer对象相比较时,由于前者存放在堆中,后者存放在Java常量池中,所以永远不相等
- 两个非通过new生成的Integer对象比较时,如果两个变量的数值相等且在-128到127之间,结果就相等(因为享元模式)。这是因为给Integer对象赋一个int值,java在编译时,会自动调用静态方法valueOf(),根据java api中对Integer类型的valueOf的定义,对于-128到127之间的整数,会进行缓存,如果下次再赋相同的值会直接从缓存中取,即享元模式
String和StringBuilder和StringBuffer区别
- 三者底层都是char[]存储数据,JDK1.9之后使用的是byte[] ,因为往往我们存储都是短字符串,使用byte[]这样更节约空间。
- 由于String底层的char[]有final修饰,因此每次对String的操作都会在内存中开辟空间,生成新的对象,所以String不可变
- StringBuilder和StringBuffer是可变字符串,没有final修饰,适合字符串拼接,另外StringBuffer是线程安全的,方法有synchronized修饰,但是性能较低,StringBuilder是线程不安全的,方法没有synchronized修饰,性能较高
String a = "A" 和 String a = new String("A") 创建字符串的区别
- String a = "A"是使用字符串常量池机制,尽可能地重用现有的字符串对象,而String a = new String("A")则总是创建一个新的字符串对象。推荐使用字符串常量池机制,因为它更高效并且节省内存,而对象创建这种方式会占用更多的内存,不建议频繁使用
== 和 equals 的区别是什么
- "=="比较基本数据类型时比较的是表面值内容,而比较两个对象时比较的是两个对象的内存地址值
- == 在基本数据类型:值内容, 引用类型时:地址
equals 重写:值内容 , equals不重写:地址 - 注意:equals方法不能作用于基本数据类型的变量
final 和 finally 和 finalize 的区别
- 当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰
当用final修饰一个类时,表明这个类不能被继承。
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在 对其初始化之后便不能再让其指向另一个对象。
用final修饰方法,使其不能被重写、但可以重载,可以提高安全性和性能
- finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下
- finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在【垃圾回收器】gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
JDK 和 JRE 有什么区别?
- JRE(Java Runtime Enviroment) :是Java的运行环境,JRE是运行Java程序所必须环境的集合
- JDK(Java Development Kit) :是Java开发工具包,它提供了Java的开发环境等
????面向对象四大特性
- 抽象 : 在一堆类中抽取相同的内容,内容包括对象属性和行为。
抽象的特征是只能被继承,而不能被实例化。
- 举例:定义一个persion类,了就是对人这种事物的抽象
- 封装:保护内部数据的安全
- 生活举例:电脑主机就是把主板等封装到机壳,提供USB接口,网卡接口, 电源接口等。 JavaBean就是一种封装。
1、创建私有化的属性
2、给每一个属性提供public修饰的get和set方法 用于获取和设置值
- 继承:父类存放共性,子类存放特性,子类继承父类,可以继承父类的所有公有属性和方法
- 多态:理解:看成一类事务多种形态
作用:可以屏蔽子类差异性,提高代码的扩展性
向上造型/向上转型:
只能调用父类东西,或者重写父类已有的,但是用不到子类已有的
语法:父类类型 父类变量 = new 子类类型();
父类变量.方法();//子类若重写,则会执行子类重写后的方法
向下造型/向下转型(多了判断加强转): =>就是为了调用子类特有方法
既可以用到父类的东西,也可以用到子类自己的东西
强制转换语法:
数据类型 变量 = (数据类型)值/变量;
【注意】在向下造型前,必须进行类型判断,需要判断当前父类变量中装的是哪一个子类类型的对象
方法覆盖和重载
重载:
前提:同一个类中
同名不同参,不同参包括类型、顺序、个数有一个不同就可以
与返回值无关
重写:
前提:在继承关系中
同名同参
访问权限:子类大于等于父类
返回值权限:子类小于等于父类
static|private修饰的方法不能够被重写(java语法)【这句没道理可言死记硬背】
PS:@Override 注解 是给JVM看的,表示当前这个方法是重写的父类方法
普通类和抽象类
- 抽象类不能被实例化, 需要通过子类实例化
- 抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态。
- 抽象方法只需申明,而无需实现,抽象类中可以允许普通方法有主体
- 含有抽象方法的类必须申明为抽象类
- 抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象持久化类
Object类里面有哪些方法5个
1.String toString()
返回该对象的字符串表示。
2.boolean equals(Object obj)
指示其他某个对象是否与此对象“相等”。
3.protected void finalize()
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
4. int hashCode()
返回该对象的哈希码值。
5. Class<?> getClass()
返回此 Object 的运行时类。 获取类的class对象。
String类里面有哪些方法5个
1. String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。
2. String replace(CharSequence target, CharSequence replacement) 【瑞普雷斯】
使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
3. int length()
返回此字符串的长度。
4. String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。
5.String trim() 【吹木】
返回字符串的副本,忽略前导空白和尾部空白。
6.int indexOf(String str, int fromIndex)
返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
JDK8的新特性有哪些6个
- Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中
- 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法
- Date Time API − 加强对日期与时间的处理。
- Optional 【饿不醒乐】类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常
- Stream【斯嘴麽】 API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中
streamAPI有哪些 5个(串行流,打印有序)
- 'forEach' 来迭代流中的每个数据
- parallelStream【趴喽斯嘴麽】 是流并行处理程序的代替方法。(并行流效率高,随机打印)
- filter 方法用于通过设置的条件过滤出元素。
- Sorted【索特的】 方法用于对流进行排序。
- Collectors【康莱克腾日】 类实现了很多归约操作,例如将流转换成集合和聚合元素。//归约操作:操作元素之后返回新的集合
接口和抽象类
- 定义接口使用interface,定义抽象类使用abstract class
- 接口(无属性)由全局常量,抽象方法,(java8后:静态方法,默认方法)
- 抽象类由构造方法,抽象方法,普通方法
- 接口和类是实现关系,抽象类和类是继承关系
IO流
??你知道AIO,BIO,NIO么?讲一下你的理解
- AIO ( Asynchronous I/O):异步非阻塞I/O 模型,适用于连接数目多且连接比较长(重操作)的架构
- BIO (Blocking I/O):同步阻塞I/O 模式,以流的方式处理数据,数据的读取写入必须阻塞在一个线程内等待其完成。适用于连接数目比较小且固定的架构
- NIO (New I/O):同时支持阻塞与非阻塞模式,以块的方式处理数据,适用于连接数目多且连接比较短(轻操作)的架构,比如聊天器
java 中四大基础流
- InputStream : 输入字节流, 也就是说它既属于输入流, 也属于字节流 ,
- OutputStream: 输出字节流, 既属于输出流, 也属于字节流
- Reader: 输入字符流, 既属于输入流, 又属于字符流
- Writer: 输出字符流, 既属于输出流, 又属于字符流
带缓冲区的流
- BufferedInputStream 带缓冲区的字节输入
- BufferedOutputStream 带缓冲区的输出流
- BufferedReader : 带缓冲区的字符输入流
- BufferedWriter : 带缓冲区的字符输出流
为什么使用缓存区?(了解)
不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!
同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream写完数据后,要调用flush()方法或close()方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReader和BufferedWriter两个类。
读文本用什么流,读图片用什么流
文本用字符输入流Reader,读图片用字节输入流InputStream
字符流和字节流有什么区别
- 字符流适用于读文本,字节流适用于读图片,视频,文件等。
- 字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元也就是一个字符。
- 字节流默认不使用缓冲区;字符流使用缓冲区。
- 字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据;
- 字符流通常处理文本数据,它支持写入及读取Unicode码元
为什么字符流更适合读取文本:因为字符流以字符为单位进行数据传输;自然高效
BufferedInputStream 用到什么设计模式
主要运用了俩个设计模式,适配器和装饰者模式
怎么实现一张图片拷贝【都是先读后写】
需要一个FileInputStream指向读取的文件,然后把它包装到BufferInputStream,使用BufferInputStream#read方法去读byte[],然后创建一个FileOutputStream指向输出文件,然后把它包装到BufferOutputStream,使用BufferOutputStream#write方法写byte[]到另外一个文件
怎么实现文本拷贝
和文件拷贝思路一样,只不过读的时候需要使用BufferedReader和FileReader,使用readline来读 , 写的时候需要BufferedWriter和 FileWriter,用wite来写
集合篇
List和Set和Map的区别
- List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
- Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。
- Map是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不要求有序,允许重复。Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap
数据库的三范式:
数据库设置3NF是什么,为什么要使用反第三范式
第一范式:列不可分割 【如:学生表中的姓名学号等不可分割,联系方式可分割,因 可能分为电话号码、QQ邮箱等】
第二范式:行唯一 【确保行唯一的方式主要是通过消除非主键字段对主键的传递依赖关系】
第三范式:A表中不能出现可以关联查询出来的字段
反第三范式:A表中可以出现根据关联查询出来的字段(多表查询变单表查询)
提高数据的一致性和准确性
说一下Java中的集合体系
Collection接口
以下是类
List:
- ArrayList:底层数据结构是数组,查询性能高,增删性能低
- Vector:底层数据结构是数组,查询性能高,增删性能低
- LinkedList:底层数据结构是双向链表,查询性能低,增删性能高
Set:
- HashSet:无序不重复的,使用HashMap的key存储元素,判断重复依据是hashCode()和equals()
- TreeSet:有序不重复的,底层使用TreeMap的key存储元素,排序方式分为自然排序,比较器排序
Map接口
- HashMap:key的值没有顺序,线程不安全
- TreeMap:key的值可以自然排序,线程不安全
- HashTable:它的key和value都不允许为null,线程安全
- Properties:它的key和value都是String类型的,线程安全
HashMap和HashTable的区别---------
HashMap和HashTable都是实现了Map接口的集合框架,他们的区别
- HashTable是线程安全的,它的实现方法都加了synchronized关键字,因此它的性能较低
- HashMap是线程不安全的,它实现方法没有加synchronized,因此它的性能较高
- HashMap的key和value都允许为null,HashTable中的key和value都不能为null,如果不考虑线程安全,建议使用HashMap,如果需要考虑线程安全的高并发实现,建议使用ConcurrentHashMap【肯卡瑞特 哈希麦普】
HashMap的底层原理
jdk1.8中HashMap的底层主要是采用数组+链表+红黑树默认是数组长度为16,
每一次存值的时候使用key的(hashcode)值模数组的长度得到数组下标,
当出现下标相同但是值不同就形成链表结构当链表长度大于8个,
数组长度大于64就形成红黑树当链表长度小于6个重新以链表的结构存储
ArrayList和LinkedList区别
- 都属于线性结构(线性结构是一个有序数据元素的集合),ArrayList是基于数组实现的
- LinkedList是基于双链表实现的
- 根据实际需要,如果项目中使用查找修改较多,使用ArrayList,
如果使用增删较多,请使用LinkedList
相同点:
1、都是 list接口的实现类
2、都可以存放问任意类型任意多个数据
3、都可以存放null,并且数据可重复
4、有序输入的顺序和输出的顺序是一致的
5、都是线程不安全的
不同点:
底层实现的原理不同:
ArrayList底层是基于数组实现
基于数组的操作数据:增加和删除效率低 查询和修改效率高【数组的查询实际上是对引 用地址的访问,不需要遍历】
LinkedList底层是基于链表实现
基于链表的操作数据:增加和删除效率高 查询和修改效率低【根据index的大小判断是从 前开始遍历还是从后开始遍历】
为什么Arraylist查改效率高而linkedlist增改效率高????
https://blog.csdn.net/delicate_pig_liu/article/details/109851957
ArrayList和Vector区别
ArrayList是线程不安全的,
Vector相反是线程安全的,方法加了同步锁【synchronized】,线程安全但是性能差,ArrayList底层数组容量不足时,会自动扩容0.5倍,Vector会自动扩容1倍
一个User的List集合,如何实现根据年龄排序
- 第一种方式,让User类实现Comparable接口,覆写compareTo方法,方法中自定义根据年龄比较的算法
- 第二种方式,调用Collections.sort方法,传入一个比较器,覆写compare方法,方法中自定义根据年龄比较的算法
哪些集合类是线程安全的?
- Vector:就比Arraylist多了个 synchronized (线程安全),因为效率较低,现在已经不太建议使用。
- hashTable:就比hashMap多了个synchronized (线程安全),不建议使用。
- ConcurrentHashMap:它是HashMap的线程安全,支持高并发的版本
ConcurrentHashMap在jdk1.7中,它是通过分段锁的方式来实现线程安全的。意思是将哈希表分成许多片段Segment,而Segment本质是一个可重入的互斥锁,所以叫做分段锁。
在jdk1.8中,它是采用了CAS【乐观锁】操作和synchronized来实现的,而且每个Node节点的value和next都用了volatile关键字修饰,保证了可见性
二.JavaEE&框架&中间件
数据库基础
平局值用什么,分组用什么
统计平局值:avg , 分组:group by
两个相同列的结果集求并集用什么
Union【U你嗯】 并集 , union all(允许重复并集)
完整查询SQL中的关键字的定义顺序
SELECT 列名 FROM 表1 JOIN 表2 ON 条件 WHERE 条件 GROUP BY 列名 HAVING 条件 ORDER BY 列名 LIMIT
完整的多表JOIN查询,SQL中关键字的执行顺序
FROM -> 决定从哪一个表查询
JOIN -> 决定跟哪一个表关联
ON -> 决定关联表的共同条件
WHERE -> 决定从表中哪一些开始查询(条件过滤,学会善用条件过滤)
GROUP BY -> 决定按照什么分组进行组内查询,一般跟聚集函数联合使用
SELECT -> 决定显示哪一些字段
HAVING -> 在组内条件过滤,一般使用SELECT语句中的聚集函数作为过滤条件
DISTINCT -> 根据select中的字段决定显示哪一些不重复的字段
ORDER BY -> 决定显示的排序规则
LIMIT -> 决定每一页显示多少条件记录
子查询的执行流程:
从最里面(深)的查询语句开始执行,逐级向外执行,直到最后一层,
子查询一般是用在where语句和from语句中,执行流程满足上面的执行流程
mysql常用函数 8个
CONCAT(): 连接两个或多个字符串。
NOW(): 返回当前日期和时间
min(col):返回指定列的最小值2。
max(col):返回指定列的最大值2。
sum(col):返回指定列的所有值之和2。
abs(x):返回x的绝对值1。
round(x,y):返回参数x的四舍五入到y位小数的值1。
sqrt(x):返回一个数的平方根1。
avg(col):返回指定列的平均值12。
count()计数
SQL面试题
员工表employee字段有: id, username, amount ,deptname .
- 求每个部门总人数怎么做 ,
select 部门名,count(id) from employee group by deptname
- 求每个部门总工资怎么做?
select 部门名,sum(amount) from employee group by deptname
Javaweb基础
常见Http状态码
200 成功返回状态
301 永久重定向,被请求的资源永久移动到新位置
302 临时重定向,被请求的资源临时移动到新的位置,项目中使用了oauth2,对目标资源访问无权限时就会见到,它是会重定向到授权地址
401 无权限访问
403 禁止访问,服务器已经接收到请求,但拒绝执行
404 找不到该资源
500 服务器内部错误 zuul找不到服务名就会见到(500一般百分之八十是后端错误)
503 服务器内部错误 服务器维护或者过载
504 网关超时
Servlet的生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 初始化后调用 init () 方法。【英嘞特】
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 销毁前调用 destroy() 方法。【德斯嘴A】
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
Servlet的三大职责:
接收请求参数
调用service层方法 处理业务逻辑
响应数据到页面
什么是过滤器?怎么创建一个过滤器 直接背
- 过滤器:在请求发送之后,处理之前对请求的一次拦截,可以更改请求状态或者参数值等。
- 创建过滤器:创建一个过滤器类,实现filter接口,重写doFilter方法,最后在 web.xml中配置过滤器
使用过滤器来帮助我们解决乱码问题等
它是tomcat的
Tomcat的过滤器是基于Java Servlet规范的一种扩展,它通过实现javax.servlet.Filter接口来定义过滤器
拦截器与过滤器的区别、相同点(与上结合背)
- 功能都是一样的
- 过滤器是tomcat的,实现Filter接口
- 拦截器是SpringMVC的,实现HandlerInterceptor接口【憨得 英特赛普特】
创建一个拦截器类,实现HandlerInterceptor接口并实现三个方法
preHandle:拦截器核心方法,会在所有请求进入目标方法之前执行,返回参数true/false, 【呸憨得】 true放行,false拦截
postHandle:在执行完目标方法之后,返回页面之前执行
afterCompletion【啊服特康普厉行】:在页面渲染完毕之后,展示页面之前执行
在spring-mvc.xml中配置拦截器
位置:E:\yuanmashidai\20240228Java\java0228\2024-05-07-框架技术-SpringMVC入门+JSON_Day36\doc 或Day37
什么是会话及作用呢?
会话就是浏览器和服务器之间进行数据交互的过程,当我们打开一个浏览器就相当于开启会话,当我们关闭浏览器时会话就结束了
目的:会话技术就是用来解决Http协议无状态特点的,可以在多次请求之间共享技术
会话技术有:cookie和session
扩展:客户端(Client)是发送请求(request),服务器端(Service)是响应请求(response)
讲一下Session的工作原理
服务端的session id会自动写入客户端的cookie中,每次请求客户端回自动把cookie带入后台,后台自动根据cookie中的sessionid就能找到session
Session和cookie有什么区别
- session和cookie都是解决会话问题
- cookie是将数据存储在客户浏览器端
- session占用服务器的性能,但安全性较高,使用cookie减轻服务器的压力,但有被用户篡改风险因此安全性较低
Ps:了解:四大作用域,八大内置对象
位置:E:\yuanmashidai\20240228Java\java0228\2024-05-24-项目实战-项目3-智慧校园管理系统-登录认证+Maven多模块抽取+本地部署_Day47
说说preparedStatement和Statement的区别
【呸派的 斯得特门特】
- statement的sql语句使用字符串拼接,很容易出错,而preparedStatement使用?作为占位符,不容易出错易于维护
- statement不对sql语句作处理,直接交给数据库,而preparedStatement支持预编译,事先将编译好的sql语句放到数据库端,相当于缓存,因此效率更高
- statement有sql注入风险,preparedStatement没有sql注入风险
补充:预编译的机制是先编译,再传值,用户传递的参数无法改变SQL语法结构,从 根本上解决了SQL注入的问题
补充:硬编码是指在代码中直接使用具体数值或字符串,而不是使用变量或配置文件 来表示
Statement拼接容易出错主要是由于拼接过程繁琐、存在SQL注入风险以及效率问题等原因造成的
位置:E:\yuanmashidai\20240228Java\java0228\2024-05-15-JDBC进阶_Day41\doc
请求转发和重定向的区别 直接背
- 请求转发 * 特点: * 1、地址栏不会发生改变 * 2、会共享同一个请求中的数据 * 3、不管请求了多少次 最后一个resp才会返回数据 * 4、不能请求外部资源 * 5、可以访问web-inf这个安全目录下面的资源
- 重定向 使用resp * 特点: * 1、地址栏会发生改变 * 2、不会共享同一个请求中的数据 要想使用 需要使用一个session * 3、不管请求了多少次 最后一个resp才会返回数据 * 4、能请求外部资源 * 5、不可以访问web-inf这个安全目录下面的资源
位置:E:\yuanmashidai\20240228Java\java0228\2024-04-25-JAVAWEB-Servlet基础入门&Servlet交互_Day31\doc
get和post请求的区别 直接背
- 最直观的区别,get把参数包含在url中,post是把参数放到requestbody中
- post相对于get更安全
- post发送的数据更大,get有url的长度限制
- post能发送更多的数据类型,get只能发送ASCII字符
- 在restful中,get一般用户查询搜索数据,post一般用户添加或者修改数据
JSP的原理
jsp的本质就是servlet,每个JSP文件都回被编译成一个Serverlet去执行,在该Serverlet会对JSP中的动态内容进行替换,静态部分是标准的html,动态部分是java程序
Spring部分
介绍一下Spring
Spring是一个开源的轻量级控制反转和面向切面编程的容器框架。轻量级是说它开发使用简单,功能强大。
控制反转是指将对象的创建,销毁控制交给ioc容器,方便解耦合,降低维护难度。
面向切面编程是指将相同的逻辑横向抽取出来,可以对一些通用业务,如事务、日志进行集中管理
Spring中的Bean具有生命周期。
Spring Bean的生命周期可以分为四个阶段:实例化、属性填充、初始化以及销毁
SpringMVC的执行原理
1.Http请求:客户端请求提交到DispatcherServlet-前端控制器
2.寻找处理器:由DispatcherServlet调用HandlerMapping-处理器映射器,根据url找到对应的的Handler
3.调用处理器:DispatcherServlet指定HandlerAdapter-处理器适配器去调用Handler
4.调用业务处理和返回结果:Handler调用业务逻辑处理完成后,返回ModelAndView
5.处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResoler-视图解析器,找到ModelAndView指定的视图
6.Http响应:将结果显示到客户端
http的无状态
HTTP是一种无状态协议,即服务器不保留与客户交易时的任何状态。
也就是说,上一次的请求对这次的请求没有任何影响,服务端也不会对客户端上一次的请求进行任何记录处理。
用户登录后,切换到其他界面,进行操作,服务器端是无法判断是哪个用户登录的
两种用于保持HTTP状态的技术,一个是 Cookie,而另一个则是 Session
??说下Spring框架的组成
- CoreContain 核心容器模块:包括 beans IOC管理,context上下文,SPEL表达式等
- Web模块:包括SpringMVC基本功能,websocket等
- data 数据/集成模块:包括JDBC,ORM对象关系映射 ,事务管理, JMS消息功能等
- 其他模块 :AOP ,测试等
什么是Spirng的IOC
IOC控制反转,把对象的创建,属性设置,初始化,销毁等工作交给Spirng的IOC容器去管理,解放程序员的劳动力。
对象被注册到Spring的IOC容器中,使用的时候从容器中获取即可,非常方便。
它通过依赖注入,将需要的外部资源注入到组件中,使用IOC使得对象之间的耦合度降低,资源变得容易管理,从而使得代码更加优雅
你对AOP的理解
AOP,Aspect Oriented Programming 英文首字母缩写,意为面向切面编程,是Spring的核心思想之一
AOP是对OOP(面向对象编程)的一种补充,能够做到很多面向对象无法做到的事情,比如需要在所有方法执行前开启事务,打印日志,如果使用面向对象来编程,将会产生大量重复代码,而使用AOP,可以将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,一次解决这些问题。
----而这些重复的代码,一般统称为横切逻辑代码
使用AOP,在不改变原有业务逻辑的情况下,实现解耦合,避免横切逻辑代码重复
AOP的使用场景包括日志记录,性能统计,安全控制,事务处理,异常处理等等
AOP的原理(使用什么设计模式)
执行链和动态代理。
它是基于动态代理实现的,分为JDK动态代理和CGLIB动态代理。JDK动态代理只支持实现了接口的类 ,CGLIB支持没有实现接口的类。Spring默认使用JDK动态代理,如果被代理类没有实现接口,会选择CGLIB动态代理
另外Spring的AOP还用到了执行链模式。
Spring的Bean懒加载和非懒加载有什么区别
懒加载(惰性加载):需要使用对象的时候才创建,节省资源,但不利于提前发现错误。
非懒加载,也叫迫切加载,容器启动时就创建对象,消耗资源,但有利于提前发现错误
spring中默认时迫切加载,即在项目启动时,spring会扫描符合条件的所有bean并将其初始化
如果需要懒加载,可以使用@Lazy注释或者xml中配置属性default-lazy-init="true"
Spring的依赖注入方式有哪些
- 方式一:setter方式注入,通过反射调用无参构造方法生成对象,再通过对于的setter方法注入配置的值,支持注解和xml两种实现方式
- 方式二:构造器方式注入,通过反射调用有参构造方法生成对象,支持注解和xml两种实现方式
- 基于注解实现方式:@Autowired,它是默认按类型匹配的、@Resource,它是默认按名字匹配的
---说一下定义切面相关的注解
@Aspect:定义切面
@Pointcut:定义切点 = cn.xx.service.*
@Before:前置通知,在目标方法运行之前运行
@After:后置通知,在目标方法运行结束之后运行(无论方法正常结束还是异常结束)
@AfterReturning:返回通知,在目标方法正常返回之后运行8
@AfterThrowing:异常通知,在目标方法出现异常以后运行
@Around:动态代理,手动推进目标方法运行
注册Bean(定义Bean、配置Bean)的注解有哪些
@Controller/@RestController 一般用于定义控制层的类
@Service 一般用于定义服务层的类
@Repository 一般用于定义持久层类
@Component 定义一般类
@Configuration 定义配置类
单例多例的区别
- 单例和多例属于对象模式,单例模式指对象在整个容器中只存在一份,多例模式则可以有多个实例(每次都是拿到的一个新的对象)。
- 在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以通过修改scope属性:scope="prototype"
- 如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个ConcurrentHashMap中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。
- 如果是多例(prototype)模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。
spring、springMVC、springboot常用注解有哪些
******
单例模式有哪几种?
饿汉式(是立即加载的方式,无论是否会用到这个对象,都会加载)、懒汉式(是延迟加载的方式,只有使用的时候才会加载。 并且有线程安全的考量)、静态内部类、枚举(最好最简单的方式【这种反射都访问不了】)
什么是单例模式?
回答的时候,要答到三元素
- 构造方法私有化
- 静态属性指向实例
- public static的getlnstance方法,返回第二步的静态属性
Bean被指定为prototype以及singleton有什么区别
- 这两者分别指的是多例和单例模式,singleton即单例模式,指对象在整个系统中只存在一份;prototype即多例模式系统中可以有多个实例。
- 如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个ConcurrentHashMap中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。
- 如果是多例模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。
- 在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以指定scope属性:scope="prototype"
BeanFactory和ApplicationContext有什么区别
- BeanFactory(延迟加载,懒加载)接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法
- ApplicationContext(迫切加载,)接口是BeanFactory接口的子接口,除了继承BeanFactory中所有管理bean的方法,还拥有环境、国际化、资源、事件等服务相关的接口
- BeanFactory是延迟加载,ApplicationContext是迫切加载
Spring事务传播行为(机制)(硬背)
事务传播行为指的是一个方法调用另外一个方法事务是怎么进行传递的在Spring中规定了7种类型的事务传播行为
PROPAGATION_REQUIRED(瑞快尔的) |
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。- 默认 |
PROPAGATION_SUPPORTS(舌泼儿齿) |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY(蛮得腿) |
使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW(瑞快儿试) |
新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED(舌泼儿齿d) |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER(勒屋) |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED(勒斯得的) |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务, 则执行与PROPAGATION_REQUIRED类似的操作 |
事务失效的场景(硬背)
- 未启用spring事务管理功能
- 方法不是public类型的
- 数据源未配置事务管理器
- 自身调用问题,比如:入口方法A没有事务,在方法内部调用该类中的B方法,B方法的事务会失效,因为入口方法没有事务就是在非代理情况下执行,那么B方法也会非代理执行。
- 异常类型错误
- 异常被吞了
- 业务和spring事务代码必须在一个线程中
SpringMVC部分
SpringMVC怎么样设定重定向和转发的
重定向是指将用户从当前请求重新定向到一个视图页面,或者是一个handler处理请求,以前的request域中信息全部失效,同时地址栏会发生变化,它是客户端行为
转发是指将用户从当前请求转发给另一个视图页面或者handler处理请求,以前的request域可以共享,地址栏不会发生变化,它是服务器行为
springmvc默认是使用转发方式跳转的,且会默认经过视图解析器,我们也可以通过指定,转发时在返回值前面加"forward:",重定向时在返回值前面加"redirect:",且此时就不会再经过视图解析器了
SpringMVC如何对时间格式的参数进行格式化
第一种需求,后台接收前台页面返回的string类型时间,要转换成的Date类型数据,可以使用@DateTimeFormat注解来接收参数【前台String返回后台】
第二种需求,后台将Date类型数据返回给前台页面,默认是返回时间戳,如果想要优雅的格式,可以在模型的Date字段或get方法上使用@JsonFormat注解【后台Date返前台】
SpringMVC常用的注解有哪些
@Controller:用来标识一个类是控制器类
@RequestMapping:用来映射请求路径和参数
@ResponseBody:将返回值放到responsebody中,通常返回json或者xml格式数据
@RequestBody:将前台请求参数转换成对象
@PathVariable:接收路径参数,通常用在restful接口中
@RestController:@Controller和@ResponseBody的组合注解
@ControllerAdvice:运用aop的思想,对全局做一些处理,比如结合@ExceptionHandler做全局异常捕获
如何定义SpringMVC的拦截器
SpringMVC 的拦截器主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、判断登录等功能上
第1步,定义拦截器:可以实现 HandlerInterceptor 【憨的int涩噗特】接口来自定义拦截器,接口定义了三个方法,preHandler方法是在请求到达处理器之前执行,postHandler方法是在请求经过处理器之后、解析试图之前执行,afterCompletion方法是在视图渲染之后、返回客户端之前执行
第2步,配置拦截器:在springmvc的配置文件xml中(才生效),配置所有拦截路径,以及需要放行的路径
HandlerInterceptor和HandlerInterceptorAdapter的区别
HandlerInterceptor是接口,我们可以实现该接口来定义拦截器,HandlerInterceptorAdapter是抽象类,它实现了HandlerInterceptor接口的子接口AsyncHandlerInterceptor,我们可以继承该类来定义拦截器,它简化拦截器的实现,默认preHandler返回true
SpringMVC的执行原理
1.Http请求:客户端请求提交到DispatcherServlet-前端控制器
2.寻找处理器:由DispatcherServlet调用HandlerMapping-处理器映射器,根据url找到对应的的Handler
3.调用处理器:DispatcherServlet指定HandlerAdapter-处理器适配器去调用Handler
4.调用业务处理和返回结果:Handler调用业务逻辑处理完成后,返回ModelAndView
5.处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResoler-视图解析器,找到ModelAndView指定的视图
6.Http响应:将结果显示到客户端
SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决
在spring中,bean默认都是单例的,controller也是交给spring容器管理的一个bean,因此它也是单例的。
单例的好处是减少了创建对象和垃圾回收的时间,节省了内存资源,但同时单例会造成线程不安全的问题,因为当所有请求访问同一个controller实例,controller中的成员变量是所有线程公用的,某个线程如果修改了这个变量,别的请求再来拿这个变量就编程修改后的值了
要解决这个问题,最直接有效的方式就是不要在controller中定义成员变量,如果你非要定义成员变量,两种方式
第一种,可以给controller上加注解@Scope("prototype"),将controller设置为多例模式,每次请求都重新实例化一个controller
第二种,使用ThreadLocal变量,让每一个线程都有自己独立的变量
ThreadLocal提供了一种空间换时间的方式来解决线程安全问题
RequestMapping 和 GetMapping有什么区别
@Getmapping是一个组合注解,即是@RequestMapping(method = RequestMethod.GET)的缩写,意思是只接收get请求的方法
@Requestmapping如果没有指定请求方式,可以接收get,put等各种类型的请求
SpringBoot部分
相比Spring,Spring Boot有哪些优点
Springboot是一个基于spring的框架,对spring做了大量简化,使开发流程更快,更高效
它大量简化maven依赖,管理了大量的基础依赖
基于注解配置(JavaConfig),无需xml配置
内嵌Tomcat,部署流程简单
打包和部署更加灵活,允许独立运行
SpringBoot如何做全局异常处理
可以使用@RestControllerAdvice注解,编写一个全局异常处理类,再自定义一个方法使用@ExceptionHandler来捕获具体的异常并作相应的处理
通常情况下后台向前台返回结果时,会把结果封装成包含有错误码,错误信息以及数据本身的json数据,因此我们可以使用自定义异常类,自定义枚举错误码,在捕获全局异常后,向前台返回一个包含错误码的信息
@SpringBootApplication注解的含义
@SpringBootApplication是SprnigBoot项目的核心注解,目的是开启自动配置,并表示该类为主启动类。它包含三个子标签
- @ComponentScan注解:开启ioc自动扫描注解,默认扫描当前包及其子包中@Controller,@Service等,并把这些bean加载到ioc器中
- @EnableAutoConfiguration注解:启用springboot自动配置,自动所有扫描classpath目录下面所有jar中的spring.factories文件实现配置类批量注册
- @SpringBootConfiguration注解:标志该类为springboot配置类
spring-boot-starter-parent的作用
这是SpringBoot的父工程,它的作用是帮我们管理了很多的基础jar包,同时它继承了spring-boot-dependencies,在spring-boot-dependencies项目中通过<dependencyManagement>管理了大量的依赖,同时通过<properties>维护了这些依赖的版本号
但是在项目中,还需要通过<dependencies> 去导入具体的依赖才能使用
spring-boot-starter-web的作用
此项目是Springboot和Springmvc整个的jar包,构建了web项目的基本环境,集成了日志,tomcat,springmvc,json支持等等
你知道spring-boot-starter吗
一个starter包中包含了SpringBoot的自动装配机制,可以自动初始化好一些必要的Bean。我认为一个Starter代表一套环境,比如:spring-boot-starter-web就把WEB环境所需要的包都导入了如Tomcat,log4j,springmvc,springweb,auto-config自动配置包等的呢个。然后它把SpringMVC需要的Bean都进行自动装备,比如前段控制器,编码过滤器等。
SpringBoot中如何读取配置
方式一:使用@Value读取配置文件(属性)
方式二:使用@ConfigurationProperties读取配置文件(文件)
SpringBoot中日志的level有哪些
日志级别从低到高分别为:
TRACE【崔死】 < DEBUG <INFO <WARN < ERROR【诶瑞】
如果设置为 WARN【木嗯】,则低于 WARN 的信息都不会输出
Spring中默认使用INFO级别输出到控制台
SpringBoot中如何管理事务
事务(transaction)是指业务逻辑上对数据库进行的一系列持久化操作,要么全部成功,要么全部失败。
在Springboot中,可以通过xml配置和注解配置
xml方式通过配置DataSourceTransactionManager和transactionManager实现
注解方式配置通过在主启动类上加上@EnableTransactionManagement开启事务管理器,在具体的实现层service类上加上@Transactional 实现事务
Mybatis部分
MyBatis中${}取值和#{}取值的区别
#{}能够防止SQL注入,因为底层使用PreparedStatement对象,预编译,性能较高
${}不能防止SQL注入,因为底层使用Statement对象,不会预编译而是拼接字符串,性能较低
能使用#{}时尽量使用#{},如果需要动态传入表名或者字段名需要用 ${}比如,像 ORDER BY 时只能使用${}
简单的说就是#{}传过来的参数带单引号'',而${}传过来的参数不带单引号。
orderby是肯定只能用 {}了,用#{}会多个' '导致sql语句失效.此外还有一个like 语句后也需要用${}。
JWT【json web token】特点
- 基于JSON,方便解析,因为JSON的通用性,所以JWT可以跨语言支持
- 可以在令牌中定义内容,方便扩展,他不是一个随机token串,而是可以携带自定义内容的token串
- 使用非对称加密算法中提供数字签名,JWT防篡改
- 后端服务使用JWT可以不依赖redis即可完成权限校验
JWT组成
JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了JWT字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分:我们称它为头部(header),用于存放token类型和加密协议,一般都是固定的
第二部分:我们称其为载荷(payload),用户数据就存放在里面(一般用来存放我们自定义的数据,还有声明过期时间,生效时间...)
第三部分:是签证(signature),主要用于服务端的验证
JWT的全称JSON WEB TOKEN
springboot加载配置文件的顺序
???????????????
MyBatis关联查询中,延迟加载和饥饿加载的区别
延迟加载(默认),是先从单表查询,需要使用关联数据的时候才发起关联查询,
不用的时候不查询关联的数据,又叫懒加载,饥饿加载,是在查询时将关联的数据立即查询出来加载进内存,不管用不用
延迟查询:查询学生的时候,只会把学生查出来,至于关联查询的老师,只会在getTeacher(),用到的时候才会查出来
饥饿查询:查学生的时候就会马上发sql把老师也查询出来,然后把对象关联出来
MyBatis对象关联查询和集合关联查询怎么做
单个关联对象用associate【饿C斯诶特】 ,适用于多对一的关联查询,使用javaType来定义实体类型。
集合用collection,适用于一对多的关联查询,使用ofType来定义集合的泛型类型
MyBatis一级缓存和二级缓存的区别
缓存,是指将从数据库查询出的数据存放在缓存中,下次使用相同查询时不必再从数据库查询,而是直接从缓存中读取,从而减轻数据库查询的压力,提高性能
mybaits中的一级缓存,是SqlSession级别,默认开启,使用同一个SqlSession发送相同的SQL时命中;它的生命周期和SqlSession一致,当调用SqlSession.close()方法时会释放缓存
mybatis中的二级缓存,是namespace级别,默认不开启,执行同一个namespace的相同statement,发送相同的SQL时命中;它的生命周期是程序结束
当SQL中执行了update()、delete()、insert()操作,则缓存中的数据都会清空
Mybatis的一级缓存在sqlsession里面存在(那么session叫会话,一次会话就会有一个sqlsession),意味着这里的作用域只是一次会话的多次查询。【不管session、缓存,底层就是map<key,value>结构;缓存这key是啥?Sql+参数;如何命中这个缓存,同一个会话也就是同一个sqlsession去执行sql,而这个sql必须是一样的,才能命中这个缓存】默认开启(一般不用)局部,针对一次会话的
mybatis的二级缓存,可理解是全局的,底层就是map<key,value>结构;只不过key不一样了,是mapper+id+参数(不会变);命中条件key底层就是namespace+id+参数,基本上就直接命得中,只要第一次查询过,第二次它就命得中;
二级缓存才是真正的缓存,说mybatis查询缓存一定说的是二级缓存
MyBaits的Mapper接口没有实现类为什么可以用@Autowired直接注入
动态代理,赋值给mapper接口引用的对象其实是一个代理对象,这个代理对象是由 JDK 动态代理创建的。在解析mapper的时候,mybatis会通过java反射,获取到接口所有的方法
当调用接口中方法时,将通过接口全限定名+方法名对应找到映射文件中namespace和id匹配的sql,然后将执行结果返回
MyBatis的执行流程
主要记中心四个类
就调用mapper接口执行的时候,sqlsessionfactory去创建sqlsession通过executor去执行,而statement Handler(处理的sql语句)连接数据库执行sql语句,执行之后要做结构处理
在MyBatis如何动态修改SQL
使用Mybatis的拦截器可以做到
MyBatis和MyBatisPlus的区别?
Mybatispuls基于mybatis;plus做了代码生成增强,可以自动生成,自动实现分页
MyBatis的分页插件的原理?
pageHelper,自动在sql后面加limit,还可以自动生成查总条数的sql。
怎么做到的?拦截器,可以拦截你的Executor(/ɪɡˈzekjətər/)、sratement Handler,在执行Executor的时候会先进入到拦截器里面,就可以拿到sql,就能在sql后面添加limit;把sql中的*可以换成count()就可以查总条数了
如何动态修改sql?
mybatis拦截器(跟上一题一个意思)
补充:对mybatis做增强,都要用到拦截器