项目学习及其总结(黎明)

高级面试准备文档

1.JavaSE部分

说说Java的53个关键字项目学习及其总结(黎明)_第1张图片

abstract :表明类或者成员方法具有抽象属性
assert: 断言,用来进行程序调试
boolean: 基本数据类型之一,布尔类型
break :提前跳出一个块
byte :基本数据类型之一,字节类型
case :用在switch语句之中,表示其中的一个分支
catch :用在异常处理中,用来捕捉异常
char :基本数据类型之一,字符类型
class :声明一个类
const :保留关键字,没有具体含义
continue :回到一个块的开始处
default :默认,例如,用在switch语句中,表明一个默认的分支
do :用在do-while循环结构中
double :基本数据类型之一,双精度浮点数类型
else:用在条件语句中,表明当条件不成立时的分支
enum :枚举
extends :表明一个类型是另一个类型的子类型,这里常见的类型有类和接口
final :用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量
finally :用于处理异常情况,用来声明一个基本肯定会被执行到的语句块
float :基本数据类型之一,单精度浮点数类型
for :一种循环结构的引导词
goto :保留关键字,没有具体含义
if :条件语句的引导词
implements: 表明一个类实现了给定的接口
import :表明要访问指定的类或包
instanceof :用来测试一个对象是否是指定类型的实例对象
int :基本数据类型之一,整数类型
interface: 接口
long :基本数据类型之一,长整数类型
native :用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
new :用来创建新实例对象
package: 包
private:一种访问控制方式:私用模式
protected: 一种访问控制方式:保护模式
public :一种访问控制方式:共用模式
return :从成员方法中返回数据
short :基本数据类型之一,短整数类型
static :表明具有静态属性
strictfp :用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 [1]
super :表明当前对象的父类型的引用或者父类型的构造方法
switch :分支语句结构的引导词
synchronized: 表明一段代码需要同步执行
this :指向当前实例对象的引用
throw :抛出一个异常
throws :声明在当前定义的成员方法中所有需要抛出的异常
transient :声明不用序列化的成员域
try :尝试一个可能抛出异常的程序块
void :声明当前成员方法没有返回值
volatile :表明两个或者多个变量必须同步地发生变化
while :用在循环结构中

简述一下面向对象

​ java是一种面向对象的语言,万物皆可为对象。面向对象是相对与面向过程的一种编程思想,通过封装、继承、多态和抽象等特性,可以更好地组织和管理程序的代码,提高代码的可读性、可复用性和可维护性。其中,封装是实现面向对象的基础,它可以保护对象的数据,同时也可以隐藏对象的实现细节,继承和多态是面向对象的核心,它们可以减少代码的重复,提高代码的复用性和灵活性;抽象是面向对象的关键,它可以将复杂的系统抽象成简单的概念和模型,从而更好地理解和分析系统。同时,面向对象也有其局限性,例如过度使用继承和多态会导致代码的复杂性增加,抽象的过度使用可能会影响代码的可读性。因此,在实际编程中,需要根据具体的需求和场景来选择使用面向对象的特性。

​ 关键词: 继承,封装,多态,抽象

重写与重载的区别

​ 1.重载方法参数列表必须不同,而重写方法参数列表必须相同。
​ 2.重载方法发生在同一个类中,而重写方法发生在子类的继承或接口的实现类中。
​ 3.重载方法与返回值无关,而重写方法的返回值必须是当前类类型或子类类型或接口的实现类类型。

什么是递归,优缺点是什么?

概念:递归就是一种算法或函数调用自身的过程。在递归过程中,程序会将问题分解成更小的子问题,并通过递归调用自身来解决这些子问题,直到子问题规模足够小并能够直接求解,从而得到原始问题的解;

优点:是它可以帮助简化复杂的问题,因为它可以将大问题分解为小问题。递归还可以使代码更加简洁和易于理解,因为它可以更自然地表达问题本身。

缺点:递归算法的效率通常较低,因为在递归过程中会产生额外的开销,如函数调用和栈空间的使用。如果递归深度太大,程序可能会因为栈溢出而崩溃。此外,递归往往需要较多的内存,因为每次递归调用都需要存储一些信息

什么是构造器,作用是什么,有哪些特点,如何使用

构造器就是一个特殊的方法: 专门用来创建的对象

​ 1:方法名和类名同名 .
​ 2:不需要void或者其他返回值类型.
​ 3:不需要显示通过return语句返回结果.
​ 4:不能随便调用 外部只能通过new关键词调用.
​ 5: JVM会自动给每个类添加一个没有参数的空构造器 方便通过这个构造器能够创建对象.
​ 6: 在构造器层面是是允许方法重载的,也就意味着可以通过参数给当前对象进行初始化动 作,让对象在创建之后就存在某些属性的值.
​ 7: JVM会给当前类中默认防止一个空构造器,但是有个前提,就是当前这个类中没有构造器,如果类中存在任意一个构造器,JVM提供的空构造器就不存在了。 [建议:都给类中增加一个空构造器]

接口与抽象类区别

​ 1.抽象类可以有构造方法,接口中不能有构造方法.
​ 2.抽象类中可以有普通成员变量,接口中没有普通成员变量.
​ 3.抽象类中可以包含非抽象的普通方法,接口中所有方法必须是抽象的,不能有非抽象的普通方法.
​ 4.抽象类中的抽象方法的访问权限可以是public,protected和默认,接口中的抽象方法只能 是public abstract类型.

super和this的区别

在 Java 中,super 和 this 都是关键字,用于引用父类和当前对象。它们的主要区别如下:

  1. super 关键字用于引用父类的成员变量、成员方法和构造方法,它可以调用父类的构造方法,实现子类对父类的继承。例如,super.method() 表示调用父类的方法,super() 表示调用父类的构造方法。
  2. this 关键字用于引用当前对象的成员变量、成员方法和构造方法,它可以实现当前对象的自我调用。例如,this.method() 表示调用当前对象的方法,this() 表示调用当前对象的构造方法。
  3. super 和 this 都可以用来调用构造方法,但是它们的使用方式不同。super() 必须放在构造方法的第一行,用于调用父类的构造方法,this() 也必须放在构造方法的第一行,用于调用当前类的其他构造方法。
  4. super 和 this 可以在同一个构造方法中共存,但是必须遵循规则:如果在构造方法中调用了 this(),则不能再调用 super(),因为 this() 和 super() 都必须放在构造方法的第一行,而且不能同时出现。

综上所述,super 和 this 都是关键字,用于引用父类和当前对象,它们的使用场景不同,但都可以实现类的继承和方法的调用。需要根据具体的场景来选择使用哪一个。

List、Set、Map的区别

  1. List和Set都是继承Collection 接口的集合,是存储单列元素的集合,Map是存储k-v键值对双列数据的集合
  2. List中存储的数据有序,允许重复 ;Set中存储的数据无序,不允许重复,但是元素的位置是由元素的hashCode决定的

ArrayList 和 LinkedList 的区别?

都是List列表的实现类/都是线程不安全的

不同之处:

1.底层不同:

​ ArrayList 底层是数组,LinkedList 底层是双向链表

2.占内存不同:

​ LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据.还存储了相邻元素的两个引用

3.效率不同:

​ ArrayList根据索引查询遍历效率较高,增删效率低. LinkedList查询效率低,增删效率高.

ArrayList与Vector

ArrayList与Vector区别 :
    共同点 : 都是List接口的实现类,有序可重复的
             底层结构都是数组,查询效率高,增删效率低
    不同点 :
        线程安全|同步问题 :
            ArrayList线程不安全|不同步
            Vector 线程安全|同步
            如果不需要线程安全实现,建议使用ArrayList代替Vector 。
        初始容量与扩容机制 :
            ArrayList 初始容量默认10|可以在构造器中指定,在add方法中创建
            Vector 初始容量默认10|可以在构造器中指定,在构造器中直接创建

            ArrayList 每次扩容原容量的1.5倍
                int newCapacity = oldCapacity + (oldCapacity>>1);
            Vector 默认每次扩容原容量的2倍|可以根据需求指定每次扩容多少-->容量增量
                int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                 capacityIncrement : oldCapacity);

HashMap的底层

存储过程 :
    1.调用put方法添加键值对key,value数据
    2.根据key计算hash值
        int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    3.调用putVal(hash,key,value)实现存储键值对数据
    4.执行putVal方法,第一步就判断哈希表底层的节点数组是否==null,或者底层数组的长度==0,如果是证明这是第一次添加,底层没有数组或者没有带有容量的数组,先调用resize()方法实现创建新数组
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
    5.根据key的hash值计算位桶索引int index = (n - 1) & hash,判断table[index]==null是否存在单向链表的首节点
        if ((p = tab[i = (n - 1) & hash]) == null)
    6.如果=null,证明不存在单向链表,直接创建新节点,存储这一次要添加的键值对数据,直接放在数组table[index]作为单向链表的首节点
        tab[i] = newNode(hash, key, value, null);   --> new Node<>(hash, key, value, next)
    7.如果!=null,证明存在单向链表的首节点,遍历这个链表,判断链表中每一个节点的存储的key与这一次要添加的键值对的key比较是否相等,
        if (p.hash == hash &&  ((k = p.key) == key || (key != null && key.equals(k))))
    如果相同value覆盖
        V oldValue = e.value;
        e.value = value;
        return oldValue;
    如果不相同创建新节点,挂在原单向链表的最后
        p.next = newNode(hash, key, value, null);

    8.如果以上的步骤中执行到了new Node()创建新节点放入哈希表中,数据个数+1,判断是否>扩容阈值threshold,如果满足,就调用resize方法实现扩容
        if (++size > threshold)
        resize();
        当单向链表的节点个数>=8(static final int TREEIFY_THRESHOLD = 8;),并且同时哈希表的节点数组的容量>=64(static final int 
        MIN_TREEIFY_CAPACITY = 64;),就把单向链表优化成红黑树
        但是如果数组的容量<64,就调用resize()实现数组的扩容

项目学习及其总结(黎明)_第2张图片

如何解决hash碰撞

1. 链表法(Separate Chaining):链表法是一种在哈希表的每个位置
存储一个链表的方法,当哈希函数将键映射到同一位置时,就将该键插入到
链表中。在查找键时,只需要遍历该位置的链表即可。如果链表的长度很大,
查找效率就会降低,因此通常会设置一个阈值,当链表长度超过该阈值时,将链
表转换为红黑树(Java 8及以上版本),以提高查找效率。
2. 	开放地址法(Open Addressing):开放地址法是一种将冲突的
	元素存储在其他位置的方法,而不是在哈希表的同一位置。当哈希函
	数将键映射到同一位置时,就将该键存储在哈希表的下一个空位置上。如
	果下一个位置也已被占用,就继续寻找下一个空位置。开放地址法有几
	种变体,如线性探测、二次探测和双重散列等。

	需要注意的是,哈希表的性能取决于哈希函数的好坏和解决冲
突的算法的选择。如果哈希函数的散列分布不均匀或解决冲突
的算法选择不当,可能会导致哈希表的性能下降。因此,在使用哈
希表存储数据时,需要选择合适的哈希函数和解决冲突的算法,以
提高哈希表的性能。

HashTable和HashMap的区别

Hashtable 与 HashMap之间区别 :
    共同点 :
        都是Map接口的实现类,都存储键值对的数据,底层都是哈希表,特点与应用场景类似
    异同点 :
        1.继承体系不同
            Hashtable-->父类-->java.util.Dictionary
            HashMap -->父类-->java.util.AbstractMap

        2.线程安全|不同问题
            HashMap : 线程不安全|不同步的,但是效率相对较高
            Hashtable : 线程安全|同步,但是效率相对较低

        3.null值的处理不同
            Hashtable  : 任何非null对象都可以用作键或值。
            HashMap : 并允许null值和null键。

        4.初始容量与扩容机制不同
            初始容量 :
                Hashtable  : 11
                HashMap : 16
            扩容机制
                Hashtable  : 每次扩容原容量的2倍+1  int newCapacity = (oldCapacity << 1) + 1;
                HashMap : 每次扩容原容量的2倍  newCap = oldCap << 1

        5.计算hash值与位桶索引index的方式不同
                Hashtable  :
                    int hash = key.hashCode();
                    int index = (hash & 0x7FFFFFFF) % tab.length;
                HashMap :
                    int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
                    int index = (n - 1) & hash;

泛型

泛型: jdk1.5

参数化类型 : 数据类型作为参数传递 只能配置引用数据类型

<>定义泛型 泛型的行为发生在编译期间,运行期间发型配置的所有内容无效,泛型擦除 要求先定义泛型,才能使用泛型

泛型类 : 类型的后面定义泛型,在使用类型的时候可以通过泛型传递具体的类型,类中可以进行使用泛型方法

泛型的优点: 1:代码简单简洁

​ 2:增强程序健壮性,避免类型转换异常的出现

​ 3:增强稳定性与可读性

并发和并行的区别

  • 并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
  • 并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。
    1. 并发是指多个任务在同一个时间段内交替执行,每个任务在一段时间内执行一部分,然后暂停执行,让其他任务继续执行。而并行是指多个任务同时执行,每个任务在同一个时间段内执行不同的部分。
    2. 并发通常是通过操作系统的时间片轮转机制实现的,而并行通常是通过多核CPU或分布式系统实现的。
    3. 并发可以提高资源利用率和响应速度,但并不一定能够提高任务的执行速度,因为多个任务之间需要进行切换和调度。而并行可以加快任务的执行速度,但需要考虑数据同步和通信的问题。
    4. 并发通常适用于IO密集型的任务,例如网络通信和文件读写,而并行适用于计算密集型的任务,例如图像处理和科学计算。

线程,进程,程序的区别

进程 : 资源分配的最小单位,每个进程都具有自己的代码与数据空间(进程上下文),进程之间的切换资源消耗大
线程 : cpu调度与执行的最小单位,一个进程至少包含1~n个线程,同一个进程的多个线程共享这个进程的代码数
       据空间,每个线程都有自己的运行栈与程序计数器,线程的切换消耗小

创建线程的三种方式

1:继承thread类

创建thread对象+重写run()方法

使用start方法开启线程

2.实现runnable接口

创建runnable接口的实现类对象+重写run()方法

创建thread对象,并把runnable实现类对象作为参数传入

调用start方法开启线程。(这里使用了静态代理)

3.实现juc包下的callable接口,重写call方法+创建线程池

优点,可以抛出异常和获取返回值(线程通信)

线程的五种状态

新建:new创建一个线程

就绪:.start()后,该线程处于就绪状态,只是表示可以运行,等待jvm调用

运行:线程获得cpu开始执行run()方法体

阻塞:如果其他线程抢占CPU就会阻塞,或者调用sleep()

死亡:run()、call()执行完毕,线程正常结束,或者是抛出异常、调用stop()但是容易死锁

锁的使用方式

同步方法 : 在方法上使用synchronized修饰
    同步的代码段为整个方法体
    成员方法 : 锁的是调用成员方法的对象
    静态方法 : 锁的是类的Class对象
同步块 :
    synchronized(条件){
        同步的代码段;
    }
    条件 : 类的Class对象,this,资源对象

反射

项目学习及其总结(黎明)_第3张图片

流的 分类

流的分类 :
    流向分 :  以程序为中心
        输入流
        输出流
    操作单元 :
        字节流 : 万能流
        字符流 : 只能操作纯文本文件
    功能分 :
        节点流 : 真实实现读写的
        功能流(包装流|处理流) : 加强节点流的功能,提高节点流的性能
                              所有的功能流都需要包裹节点流进行使用

String, StringBuilder,StringBuffer 的区别

都表示字符串,字符序列
是否可变 :
    String : 不可变
    StringBuilder : 可变
    StringBuffer : 可变
线程安全|同步
    StringBuilder : 线程不安全|不同步
    StringBuffer : 线程安全|同步
执行效率
    StringBuilder > StringBuffer > String

简单分析字节流和字符流的区别以及使用场景,并分析哪个流更好

字节流和字符流都是Java IO中的基本流类型,二者的主要区别在于处理的数据类型不同。字节流主要用于处理二进制数据,而字符流则主要用于处理文本数据。下面从以下几个方面简单分析一下它们的区别和使用场景:

1. 1. 数据类型:字节流处理的是二进制数据,可以处理任意类型的数据,包括图像、音频、视频等非文本类型数据;字符流则主要用于处理文本数据,因为文本数据是以字符为单位组成的,因此字符流对于处理文本数据更为方便。 

   2. 输入输出:字节流以字节为单位进行读写,而字符流以字符为单位进行读写。因为在不同的操作系统上,一个字符可能占用不同数量的字节,因此字符流提供了字符集转换的功能,可以将字节转换为字符,也可以将字符转换为字节。 

   3. 处理效率:字节流的处理效率通常比字符流高,因为在底层操作系统中,文件和网络传输都是以字节为单位进行的,因此字节流更接近底层数据的处理方式,可以提高处理效率。 

   4. 使用场景:字节流适用于处理二进制数据,例如文件复制、网络传输等操作;而字符流适用于处理文本数据,例如读取和写入文本文件、读取和写入控制台输入输出等。

综上所述,字节流和字符流各有优劣,根据具体的使用场景选择相应的流类型更为合适。通常情况下,如果处理的是非文本类型数据,使用字节流更为合适;如果处理的是文本数据,使用字符流更为方便。如果需要进行字符集转换或者是处理其他特殊情况,也可以使用字符流。在实际应用中,往往需要根据具体的情况选择最适合的流类型,不能一概而论哪个流更好。

序列化和序列化ID

序列化(Serialization)指的是将对象转换为字节序列的过程,这样就可以将对象存储到磁盘中或通过网络传输到另一台计算机上。序列化的逆过程称为反序列化(Deserialization),即将字节序列转换为对象的过程。

在 Java 中,实现序列化可以通过实现 Serializable 接口来实现。Serializable 接口是一个标记接口,即该接口没有任何方法,只是作为一个标记来表示该类可以被序列化。实现 Serializable 接口的类需要满足以下要求:

  1. 类必须是可序列化的,即必须实现 Serializable 接口。
  2. 类的所有成员变量必须是可序列化的,如果成员变量不是可序列化的,则需要将其标记为 transient。
  3. 类必须有一个无参的构造函数。

实现序列化的步骤如下:

  1. 创建一个对象输出流(ObjectOutputStream)或对象输入流(ObjectInputStream)。
  2. 将对象写入输出流中,或从输入流中读取对象。
  3. 如果需要将序列化的对象存储到磁盘中,则可以使用文件输出流(FileOutputStream)将对象写入磁盘。

例如,将一个对象序列化并存储到磁盘中,可以按照以下步骤实现:

javaCopy code// 定义一个需要序列化的类
class Person implements Serializable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
// 序列化对象并存储到磁盘中
Person person = new Person("张三", 20);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));
out.writeObject(person);
out.close();

反序列化一个对象并读取其属性,可以按照以下步骤实现:

javaCopy code// 反序列化对象并读取其属性
ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"));
Person person = (Person) in.readObject();
in.close();
System.out.println(person.name + "," + person.age);

序列化标识id

​ 在 Java 序列化中,每个可序列化类都有一个 serialVersionUID 字段,它是一个 long 类型的常量。该字段用于标识序列化类的版本,以便在反序列化时能够确定序列化对象和反序列化对象是否兼容。

​ 如果序列化类的 serialVersionUID 值未显式指定,则会自动生成。当类的属性或方法发生变化时,会导致自动生成的 serialVersionUID 值发生变化,这将影响到反序列化操作。因此,为了避免这种情况,建议手动指定 serialVersionUID 值,以保证序列化和反序列化的兼容性。

简单介绍JDK1.8之后的新增特性

JDK 1.8 是 Java 语言的一个重要版本,引入了许多新增的特性和改进。下面列举了 JDK 1.8 中的一些主要特性:

  1. Lambda 表达式:Lambda 表达式是 JDK 1.8 最引人注目的特性之一。它提供了一种简洁而灵活的方式来表示匿名函数,使得函数式编程在 Java 中变得更加方便。Lambda 表达式可以用于函数式接口、集合操作(如 forEach、map、filter 等)以及并行编程。
  2. 函数式接口:JDK 1.8 引入了新的注解 @FunctionalInterface,用于标识函数式接口。函数式接口指的是只有一个抽象方法的接口,用于支持 Lambda 表达式的使用。
  3. Stream API:Stream API 提供了一种流式操作集合的方式,可以更方便地对集合进行过滤、映射、排序和归约等操作。它支持串行和并行两种操作模式,可以提高集合操作的效率和简洁性。
  4. 接口的默认方法和静态方法:JDK 1.8 允许接口中定义默认方法和静态方法。默认方法可以在接口中直接提供默认的实现,而静态方法可以在接口中定义和调用静态方法。
  5. 方法引用:方法引用是一种简化 Lambda 表达式的语法,可以直接引用已有方法的方式来代替 Lambda 表达式。它提供了对特定类型方法的简洁引用方式,例如对象的实例方法、静态方法或构造方法。
  6. Optional 类:Optional 类是一种用于处理可能为空的对象的容器类。它提供了一些便捷的方法来判断对象是否为空,并可以避免出现空指针异常。
  7. Date/Time API:JDK 1.8 引入了全新的日期和时间 API,提供了更好的日期和时间处理方式,解决了旧的 Date 和 Calendar 类的一些问题。
  8. CompletableFuture 类:CompletableFuture 类是用于异步编程的工具类,它提供了一种更加灵活和功能强大的方式来处理异步任务和异步回调。

除了以上列举的特性外,JDK 1.8 还包含了许多其他的改进和优化,例如重复注解、类型注解、改进的类型推断、新的 JVM 默认编译器(JVMCI)等。

这些新增的特性和改进使得开发人员在 JDK 1.8 及之后的版本中能够更加方便地进行函数式编程、并发编程和日期时间处理,提高代码的可读性和性能。

简单介绍LAMBDA表达式

Lambda 表达式是 Java 8 引入的一种函数式编程的特性,它提供了一种简洁、灵活的方式来表示匿名函数。Lambda 表达式可以用于替代使用匿名内部类的情况,使得代码更加简洁、易读,并且能够方便地进行函数式编程。

Lambda 表达式的语法如下:

cssCopy code(parameters) -> { body }

其中,parameters 是参数列表,可以为空或包含一个或多个参数。如果只有一个参数,可以省略括号。body 是 Lambda 表达式的主体,可以是单个表达式或一个代码块。如果主体是一个表达式,可以省略大括号。

Lambda 表达式可以用于函数式接口,即只有一个抽象方法的接口。在使用 Lambda 表达式时,需要根据接口定义的抽象方法的参数和返回类型来匹配 Lambda 表达式的参数和主体。

下面是一些示例:

  1. Lambda 表达式表示一个没有参数的函数:
javaCopy code() -> System.out.println("Hello, Lambda!");
  1. Lambda 表达式表示一个带参数的函数:
javaCopy code(name) -> System.out.println("Hello, " + name + "!");
  1. Lambda 表达式表示一个带多个参数的函数:
javaCopy code(x, y) -> {
    int sum = x + y;
    System.out.println("Sum: " + sum);
}

Lambda 表达式可以与函数式接口一起使用,例如使用 Java 提供的 java.util.function 包中的函数式接口,如 ConsumerPredicateFunction 等,以及自定义的函数式接口。

Lambda 表达式使得函数式编程更加方便,可以直接传递行为(即函数)作为参数,简化了代码的编写和维护。它在集合操作、并行编程、事件处理等场景中有广泛的应用,提高了代码的可读性和可维护性

笔记:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iH8bWmBA-1684204326244)(项目学习及其总结.assets/1684144604594.png)]

2.数据库阶段

in()和exists()的区别和具体用法

在子查询较多,而外查询得出的结果较少的时候,又有索引时,我们通常使用exists;

在子查询较少,而外查询较多的时候,我们通常使用in来进行使用;

具体用法: in()外层查询得到的值和in里面进行匹配,有相同的进行输出,不相同不进行输出;
exists()外层的值和内层的值进行匹配,但是当()里面是没有匹配的值时,将得不到任何结果;
还有一种情况,当()里的子查询和外层循环没有任何关联时,得到是全部的外循环,不会有任何影响,这时,我们就要让子查询和外查询建立联系,这样的存在,exists才有意义;

SQL 常见聚合函数与聚合查询场景

常见的聚合函数:

avg(),求平均值; sum() 求和;

count(),计算和; min()求最小值;

max()求最大值

聚合函数的应用场景:

COUNT:用于统计指定列的行数,可以用于统计表中的记录数或者去重后的记录数

      • SUM:用于计算指定列的总和,可以用于计算某个时间段内的销售总额等。

      • AVG:用于计算指定列的平均值,可以用于计算某个时间段内的平均销售额等。

      • MAX:用于返回指定列的最大值,可以用于查找最高分数或者最大年龄等。

      • MIN:用于返回指定列的最小值,可以用于查找最低分数或者最小年龄等。


92查询和99查询的区别和联系

92查询: 1992年推出的sql语句的规范;

99查询: 1999年推出的sql语句的规范;

99和92查询的区别:

功能方面:sql99支持的较多

可 读 性:sql99实现了连接条件和筛选条件的分离,因此可读性较高。

具体语法上: 1: 在进行左右连接时我们通常使用(+)来进行确定左右连接,不带有(+)的表为主表;

2: 笛卡尔积:我们在92连接上我们不进行关键字的使用,直接用逗号隔开两表,就可以进行笛卡尔积的运算;而99连接,要使

用关键字cross join 来进行;

3:等值连接和非等值连接:在92进行等值连接时,会放在where语句的后面,而99连接时,我们会引用一个新的关键字on进行对

表的连接;

表和表之间的关系

​ 一对一:

​ 多对一|| 一对多:该类型表的设计过程中,数据量多的一方设计为外键,数据量少的一方设置为主键;

​ 多对多:在多对多的模型中,我们一般会增加个中间表进行多对多之间连接;

表设计的三范式和表的反范式

最终的目的避免数据重复冗余

​ 1NF–>列不可再分最小原子 (避免重复);

​ 2NF–>主键依赖(确定唯一);

​ 3NF–>消除传递依赖(建立主外键关联 拆分表);

三范式的各个范式的目的是:

第一范式:每个字段的数据不能再被拆分

第二范式:非主键字段都完全依赖于主键

第三范式:属性不依赖非主属性

三范式的优缺点:

优点:三大范式既减少数据冗余,也避免了一些更新数据时的异常。

​ 满足范式的表通常较小,可以更好的放入内存,执行操作更快;

缺点:按照范式设计出来的表在数据冗余的问题虽然得到解决,但是会生成许多表,导致了表数量的

复杂性,其二,查询数据的时候,多表查询的时间远远高于单表查询的时间。

反范式的目的:减小数据冗余,而反范式指的是在一定程度上允许数据冗余,目的是加快数据操作。

反范式的概念:不满足范式的模型,就是反范式模型

反范式和范式的区别:

​ 1:范式和反范式是时间和空间的较量,满足范式条件,节省空间,满

足非范式条件加快操作速度;

​ 2:范式化的数据库中,每个事实依据只会并只出现一次,而在反范式化数据库

中,信息是冗余的,可能会存储多个地方;


事物的隔离级别

项目学习及其总结(黎明)_第4张图片

Oracle 默认的隔离级别是 read committed,读已提交。

Oracle 支持上述四种隔离级别中的两种:read committed 和 serializable。除此之外, Oralce 中还定义Read only 和 Read write 隔离级别。

Read only:事务中不能有任何修改数据库中数据的操作语句,是 Serializable 的一个子集。

Read write:它是默认设置,该选项表示在事务中可以有访问语句、修改语句,但不经常使用。

JDBC实现的实现流程

1:加载驱动

2:创建和数据库的连接

3:准备sql语句

4:构造预处理快(一般为预处理块,防止sql注入)

5:发送sql,获取结果

6:处理结果,对结果进行转换处理,并将其返回;

7:连接关闭

事物控制ACID的特点

四大特性: 1: 原子性:事务中的所有数据要么全部执行,要么全部不执行;

​ 2:一致性:事物完成后,要使数据的状态保持一致;

3:隔离性:指多个并发事务之间的相互隔离程度,不能相互影响;

4:持久性:保证事务对数据库的修改应该持久有效,即使发生系统故障,也不能丢失;


什么是sql注入?JDBC应对sql注入的应对方法有哪些?

SQL注入的概念:SQL注入是一种将SQL代码添加到输入参数中,传递到SQL服务器解析并执行的一种攻击手法,输入参数未经过过滤,直接拼接到SQL语句中,解析执行,达到预想之外的行为;

JDBC的应对方法:

  • PrepareStatement接口: 先进行SQL语句的预编译,再进行传值操作,可以有效避免SQL`注入问题

  • 正则表达式

  • 字符串的拼接


Mybatis框架与JDBC各自特点与联系

联系:mybatis的框架下是在JDBC的基础上进行封装的,框架内部自行封装了JDBC

Mybatis特点:

1:Mybatis省略了一般模式下对JDBC的驱动的加载、创建连接、创建Statement等对象的繁杂的操作,将对数据库进行操作的语句集中到了映射文件中(xxx.xml),实现了代码的高内聚低耦合。

2:MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

3:灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响,sql写在xml里面,便于统一的管理;

4:解除了sql和程序代码的耦合性;

5:提供了XML文件,支持编写动态的sql;
6:提供了对象关系之间的映射标签,支持对象和数据库的ORM字段关系映射;

JDBC的特点:

1:性能:如果进行底层编程,而且对性能要求极高的话,应该采用JDBC的方式

2:平台独立性:是基于java语言的API,因此具有平台无关性,可以支持任何java的操作系统和硬件在平台上运行

3:可拓展性强:JDBC的框架的可扩展性强,可以通过编写自定义的JDBC来进行驱动程序;

4: 面向对象:JDBC是面向对象的,他使用java的对象来表示数据库的实体;

5:安全性 :JDBC使用了连接池,可以使用连接池技术来提高应用程序的性能

6:易用性:简单好上手


了解Mybatis 框架底层架构,Mybatis框架常见使用场景

MyBatis适用于需要灵活控制SQL语句和数据库访问的项目,特别是对于复杂的数据库查询操作。MyBatis的适用场景和应用范围非常广泛,包括Web开发、大数据处理、数据分析等领域。以下是MyBatis的一些适用场景和应用范围:

    • 数据库访问层:
      MyBatis是一种轻量级的持久层框架,可以用于实现Java应用程序的数据库访问层,包括数据查询、数据修改、数据删除等操作。
    • 大型企业级应用
      MyBatis的灵活性和可扩展性使其非常适合用于大型企业级应用的开发中,可以满足复杂的业务需求。
    • 微服务架构
      MyBatis可以与Spring Cloud等微服务架构组件进行集成,使得开发人员可以更加便捷地实现微服务架构。
    • 数据库访问API
      MyBatis可以作为数据库访问API使用,可以与各种数据库进行兼容,包括Oracle、MySQL、PostgreSQL等主流数据库。
    • 数据库访问工具
      MyBatis提供了一系列的工具和插件,可以帮助开发人员更加便捷地访问数据库,包括自动生成Java对象、自动生成SQL语句等。

总的来说,MyBatis的适用场景和应用范围非常广泛,可以应用于各种Java项目中,包括大型企业级应用、微服务架构、数据库访问API等


了解mybatis的基本文件配置

基本文件配置:1:基本内容

​ 2:全局配置文件–指定开发环境和数据库的端口,密码,和数据库名称

​ 3:关联sql数据库的映射文件

​ 4:一般还会有取别名(typeAliases标签)

​ 5:加载日志(log4j)


Mybatis SQL映射配置文件基本标签配置

基本文件配置: 1:基本的映射文件语句;

		  2:namesapace 命名空间 ,根据增删改查选取不同的标签

​ 3:sql语句—》mapper标签


SQLSession 常见CRUD 方法调用

常见调用:增(insert)、删(delete)、改(update);

在映射文件中的写法如下:


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="studymybatis.mappers.DeptnoMapper">
    
    <select id="queryAll" resultType="Dept">
        select * from dept
    select>

    <insert id="insertDept" parameterType="Dept">
        insert into dept values(#{deptno},#{dname},#{loc})
    insert>

    <update id="updateDept" parameterType="Dept">
        update dept set dname= #{dname} where deptno= #{deptno}
    update>

    <delete id="deleteDept" parameterType="int">
        delete from dept where deptno = #{deptno}
    delete>
mapper>

Mybatis SQL 参数传递与结果参数配置

​ 参数传递: parameterType=“Dept”,这就是参数传递,将要查询的条件的类型,传入里面;

​ 结果参数:( resultType):

​ 参数类型:基本数据类型,包装类,String,数组,list集合,map集合,javaBean(简单的说就是实体类);

​ 结果参数配置:根据不同的查询结果选用不同的查询方式,以及不同输出结果选用不同的方式


Oracle中的DDL和DML有什么不同?

在Oracle中,DDL(数据定义语言)和DML(数据操作语言)是两种不同的SQL语言类型,它们用于不同的操作。

  1. 对事务的影响
    DDL语句执行后会隐式提交事务,这意味着DDL操作会自动提交事务并将其永久保存到数据库中。而DML语句则需要显式提交事务才能将修改保存到数据库中。因此,在使用DDL语句时需要注意,确保已经做好了备份和恢复准备工作。
  2. 对数据库对象的影响
    DDL语句可以创建、修改和删除数据库对象,例如表、视图、索引等。而DML语句只能操作数据库对象中的数据,例如插入、更新和删除数据。
  3. 对性能的影响
    DDL操作通常需要较长的时间来执行,因为它们涉及到对数据库结构的更改,需要重建索引和其他相关对象。因此,使用DDL语句时需要考虑到对性能的影响,确保在合适的时间执行。
  4. 对安全性的影响
    DDL语句通常需要较高的权限才能执行,因为它们涉及到对数据库结构的更改。而DML语句通常需要较低的权限才能执行,因为它们只涉及到对数据的操作。

​ 因此,DDL和DML的主要区别在于它们操作的对象不同。DDL用于定义数据库中的对象,而DML用于操作数据库中的数据。此外,DDL语句执行后会隐式提交事务,而DML语句需要显式提交事务才能将修改保存到数据库中。

DDL(Data Definition Language 数据定义语言)用于操作对象和对象的属性 (建表语句)

DML(Data Manipulation Language 数据操控语言)用于操作数据库对象中包含的数据 (sql 增删改查)

DCL :(Data Contorlation Language数据控制语句) 用于操作数据库 事务回滚,控制 等


内连接、左连接和右连接有什么区别?

内连接、左连接和右连接是数据库中常见的三种连接方式,它们之间的区别如下:

  1. 内连接(Inner Join):内连接是通过两个或多个表中的共同列将它们连接起来,只返回满足连接条件的行。如果表 A 和表 B 中有相同列,那么内连接会返回这两个表中列值相等的行。内连接可以通过使用 WHERE 或 JOIN 关键字来实现。
  2. 左连接(Left Join):左连接返回左边表格(左侧表格)中的所有行以及与右表格(右侧表格)中匹配的行。如果右表格中没有与左表格匹配的行,则返回 NULL 值。左连接可以通过使用 LEFT JOIN 或 LEFT OUTER JOIN 关键字来实现。
  3. 右连接(Right Join):右连接与左连接类似,但是返回的是右表格中的所有行以及与左表格中匹配的行。如果左表格中没有与右表格匹配的行,则返回 NULL 值。右连接可以通过使用 RIGHT JOIN 或 RIGHT OUTER JOIN 关键字来实现。
    总的来说,内连接是只返回满足连接条件的行,左连接是返回左表格中的所有行以及与右表格匹配的行,右连接是返回右表格中的所有行以及与左表格匹配的行,否则其他的值为null值!

数据库中事务的特性和事务隔离级别分别是什么?

事物的特性:

  1. 原子性

  2. 持久性

  3. 一致性

  4. 隔离性

事物的隔离级别

    1. 读未提交
    2. 读已提交
    3. 可重复读
    4. 串行读

通常一个Xml映射文件,都会写一个 Dao 接口与之对应,请问这个Dao接口的工作原理是什么? Dao接口里的方法参数不同时方法能重载吗?

​ Dao 接⼝,就是⼈们常说的 Mapper 接⼝,接⼝的全限名,就是映射⽂件中的 namespace

的值,接⼝的⽅法名,就是映射⽂件中 MappedStatement 的 id 值,接⼝⽅法内的参数,就是传递

给 sql 的参数。 Mapper 接⼝是没有实现类的,当调⽤接⼝⽅法时,接⼝全限名+⽅法名拼接字符

串作为 key 值,可唯⼀定位⼀个 MappedStatement ,举

例: com.mybatis3.mappers.StudentDao.findStudentById ,可以唯⼀找到 namespace

为 com.mybatis3.mappers.StudentDao 下⾯ id = findStudentById 的 MappedStatement 。在 Mybatis

中,每⼀个 、 、 、 标签,都会被解析为⼀

个 MappedStatement 对象。

Dao 接⼝⾥的⽅法,是不能重载的,因为是全限名+⽅法名的保存和寻找策略。

Dao 接⼝的⼯作原理是 JDK 动态代理,Mybatis 运⾏时会使⽤ JDK 动态代理为 Dao 接⼝⽣成代

理 proxy 对象,代理对象 proxy 会拦截接⼝⽅法,转⽽执⾏ MappedStatement 所代表的 sql,然后

将 sql 执⾏结果返回。

SQL优化方案

  1. 尽量减少使用子查询
  2. 在or大于一的情况下,使用in来代替
  3. 读取适当的记录LIMIT M,N,而不要读多余的记录
  4. 禁止不必要的Order By排序
  5. 将多次插入换成批量Insert插入
INSERT INTO t(id, name) VALUES(1, 'aaa');
INSERT INTO t(id, name) VALUES(2, 'bbb');
INSERT INTO t(id, name) VALUES(3, 'ccc');
INSERT INTO t(id, name) VALUES(1, 'aaa'),(2, 'bbb'),(3, 'ccc');

只返回必要的列,用具体的字段列表代替 select * 语句----减少没必要的多余的查询

区分in和exists —exists的运行效率更高

优化Group By语句

    1. 如果对group by语句的结果没有排序要求,要在语句后面加 order by null(group 默认会排序)
    2. 尽量让group by过程用上表的索引,确认方法是explain结果里没有Using temporary 和 `Using filesort
    3. 使用where子句替换Having子句:避免使用having子句,having只会在检索出所有记录之后才会对结果集进行过滤,这个处理需要排序分组,如果能通过where子句提前过滤查询的数目,就可以减少这方面的开销。

尽量使用exits减少使用or

不使用select*

在满足情况的前提下使用inner join 减少使用左右连接


where和having的区别

where子句的作用是在对查询结果进行分组前,将不符合where条件的行去掉,即在分组之前过滤数据,条件中不能包含聚组函数,使用where条件显示特定的行。

having子句的作用是筛选满足条件的组,即在分组之后过滤数据,条件中经常包含聚组函数,使用having条件显示特定的组,也可以使用多个分组标准进行分组。

where:

where是一个约束声明,使用where来约束来自数据库的数据;

where是在结果返回之前起作用的;

where中不能使用聚合函数。

having:

having是一个过滤声明;

在查询返回结果集以后,对查询结果进行的过滤操作;

在having中可以使用聚合函数。

where和having的执行顺序:where早于group by早于having。

​ WHERE和HAVING的主要区别在于它们筛选数据的时机和范围不同。WHERE用于对行数据进行筛选,HAVING用于对聚合数据进行筛选。在使用这两个关键字时,需要根据需要选择合适的关键字,以达到预期的数据过滤和查询效果;


sql语句的执行顺序

    1. FROM:查询的数据来源,即需要查询的表或视图。
    2. JOIN:将多个表中的数据连接起来,生成虚拟的联合表。
    3. WHERE:按照查询条件进行数据过滤。
    4. GROUP BY:按照指定的字段对数据进行分组。
    5. 聚合函数
    6. HAVING:对分组结果进行数据过滤。
    7. SELECT:指定需要查询的字段。
    8. DISTINCT:去重操作。
    9. ORDER BY:对查询结果进行排序。
    10. LIMIT:限制返回结果的数量。

使用Group by 的注意事项

  1. GROUP BY 子句中的列必须是 SELECT 语句中出现的列,或者是表达式(如函数调用、数学运算等)。
  2. SELECT 语句中不能包含聚合函数以外的列,除非在 GROUP BY 子句中使用了该列。
  3. GROUP BY 子句的顺序必须与 SELECT 子句中的顺序相同,或者是 SELECT 中的表达式顺序的子集。
  4. 对于 GROUP BY 语句中的每个分组,都可以使用聚合函数进行计算,例如 COUNT、SUM、AVG、MIN、MAX 等。
  5. 对于包含 NULL 值的列,GROUP BY 语句会将其作为一个分组进行计算,如果需要排除 NULL 值,则需要使用 WHERE 子句或者在聚合函数中使用 IFNULL、COALESCE 等函数进行处理。
  6. 如果 SELECT 语句中包含了 HAVING 子句,则需要注意 HAVING 子句中的条件必须使用聚合函数进行比较,否则会导致语法错误。
  7. 如果需要按照多个列进行分组,可以在 GROUP BY 子句中指定多个列,多个列之间需要用逗号分隔。

delete和drop的区别:

在 SQL 中,DELETEDROP 都是用于操作数据库中的表的关键字,但它们有着不同的作用。

  • DELETE:删除表中的一行或多行记录,但保留表结构和定义,可以使用 ROLLBAC****K 回滚,即可恢复被删除的数据。
  • DROP:完全删除整张表,包括表的结构和定义,同时删除与该表相关的索引、触发器、约束等相关对象。DROP 操作是不可回滚的,一旦执行,数据将永久丢失。

因此,DELETEDROP 的主要区别是删除的范围不同,DELETE 只删除表中的记录,而 DROP 删除整个表以及与之相关的对象。同时,DELETE 是一种 DML(Data Manipulation Language)语言,而 DROP 是一种 DDL(Data Definition Language)语言。

简单的说,delete是对数据内容的操作,而drop则是对表层面的操作!

增删改查的方式

1.增:insert into <表名> [列名] values [列值]

2.删:delete from <表名> [where <删除条件>]

3.改:update <表名> set <列名=更新值> [where <更新条件>]

4.查:select <列名> from <表名> [where <查询条件表达式>] [order by<排序的列名>[asc或

desc]]

mybatis的使用步骤

1.加载mybatis全局核心配置文件

InputStream is = Resources.getResourceAsStream("mybatis.xml");

2.构建SqlSessionFactory对象

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

3.通过工厂获取会话SqlSession

SqlSession session = factory.openSession();

4.通过session调用方法执行查询,然后执行输出操作

List<User> list =session.selectList("com.yjxxt.mappers.UserMapper.queryAll");
System.out.println(list);

5.关闭会话资源

session.close();

tips:会话封装:

//就是把上面开启会话的过程(固定的重复流程)封装成一个方法,使用的时候直接调用
public class SessionUtil {
    public static SqlSession getSession(){
        InputStream input = null;
        try {
            input = Resources.getResourceAsStream("mybatis.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);
        SqlSession session = factory.openSession();//事务手动提交
        //SqlSession session = factory.openSession(true);自动提交
        return session;
    }
}

Mybatis查询的三种方法:

selectOne

用于返回单条数据或者单个属性值

selectList

返回的多条数据,使用L ist来接收
用的最多,单条语句也可以使用list来查询

selectMap

返回的多条数据,使用Map来接收

有三个参数:
sql语句的地址:命名空间+sql的id
sql语句查询需要的参数(和数据表中的大小写保持一致),否则查不到数据
返回值的键值

Mybatis接口绑定xml映射文件的步骤:

    1. 定义接口:
    2. 创建绑定的映射文件:
    3. 配置映射路径
    4. 配置xml文件的接口扫描

注意事项:

      1. xml文件名要与接口名保持一致
      2. namespace属性值 必须与接口的权限定名一致
      3. id属性必须与抽象方法名保持一致
      4. 返回值类型和参数类型与方法的返回值和参数保持一致

3:Mybatis核心配置文件中添加接口扫描地址

<!-- mapper配置扫描接口-->
<mappers>
	<package name="com.yjxxt.mapper"/>
</mappers>

4:使用

通过SqlSession的getMapper() 方法,返回接口代理对象,从而调用以实现的抽象方法

//getMapper()获取接口实现类对象,参数为接口的class对象,通过参数指定接口
UserMapper mapper =session.getMapper(UserMapper.class);
//通过对象调用方法
List<User> list = mapper.queryAll();

Mybatis和JDBC的区别

      1. SQL的处理方式

JDBC需要手动编写SQL语句,并使用JDBC API来执行SQL语句,而MyBatis使用XML或注解配置SQL语句,并提供了自动映射的功能,可以将SQL语句的结果集映射为Java对象,从而简化了SQL的处理。

      1. 数据库连接的管理

在JDBC中,需要手动管理数据库连接的创建、释放以及连接池的使用。而MyBatis封装了JDBC的连接池,提供了简单的配置方式,使得连接的管理变得更加方便。

      1. 代码的可维护性

MyBatis将SQL语句和Java代码分离,使得代码的可维护性更强。开发人员可以在XML或注解中对SQL语句进行管理,而Java代码只需要调用MyBatis提供的API来执行SQL语句,从而使得代码更加清晰、易于维护。

      1. 性能的优化

MyBatis提供了一系列的性能优化机制,例如缓存、批处理、延迟加载等,可以帮助开发人员提升系统的性能。

总的来说,MyBatis是在JDBC基础上进行封装和扩展的框架,它将Java对象和SQL语句进行映射,提供了更加简单、方便、高效的数据访问方式。

数据库中表与表中间的关系

  1. 一对一 (人->身份证)
  2. 一对多 (夏令营->学生)
  3. 多对一 (学生->班级)
  4. 多对多 (学生->课程)

3.前端

Ajax的使用方式和数据类型

$.ajax({
    type:"get/post",
    url:"url",
    data:{
        uname:"zhangsan",
        password:"123456"
    },
    async:true,
    success:function(data){
        console.log(data);
    }
})
    1. type:请求方式GET/POST

    2. url:请求地址url

    3. async:是否异步,默认是true表示异步

    4. data:发送到服务器的数据

    5. dataType:预期服务器返回的数据类型

    6. contentType:设置请求头

    7. success:请求成功时调用此函数

    8. error:请求失败时调用此函数

      AJAX同步和异步的区别是什么?

      1.同步中⼀个线程要等上⼀个线程执⾏完才能开始执⾏,⽽异步中⼀个线程在执⾏中,下⼀个线

      程不⽤等它执⾏完就可以开始执⾏

      2.同步是单线程,异步是多线程操作

      3.异步的效率要⾼于同步

post 和get 两种请求方式的区别

1.get请求时参数会跟在浏览器地址栏的后面,而post请求不会(post请求会将数据存放在请求体中)

2.get请求相对于post而言,不那么安全

3.get请求传递的数据长度是有限的,而post请求基本没有限制(长度与服务器相关)

4.get请求比post请求快(2倍左右)

5.get请求有缓存(会将数据存放在浏览器中,即本地磁盘中),而post请求无缓存

前端是怎么发送请求的(前后端如何交互)

  1. 表单提交:通过HTML表单元素来提交数据,常见的提交方式有GET和POST。表单中的元素包括文本框、下拉框、单选框、多选框等,用户在填写表单后,点击提交按钮即可向服务器提交表单数据。

  2. AJAX请求:使用JavaScript发起异步HTTP请求,可以在不刷新页面的情况下向后端发送请求和接收响应。通过AJAX请求可以向后端提交各种数据格式,如JSON、XML等,同时也可以在请求中添加请求头信息,实现更加灵活的请求和响应处理。

  3. 超链接提交:

    1. 超链接可以设置URL参数,当使用GET请求方式时,这些参数会被拼接在URL后面进行传递。后端可以通过解析URL参数获取前端提交的数据。例如:
phpCopy code点击跳转

​ 在这个例子中,超链接会向 “/user” 路径发送一个GET请求,同时将参数 name 和 age 的值分别设置为 “test” 和 “20”,后端可以通过 HttpServletRequest 对象的 getParameter() 方法来获取这些参数的值。

使用POST请求方式:虽然超链接本身不支持POST请求方式,但是可以通过JS来模拟POST请求,实现将数据提交到后端。具体实现方式如下:

  • 在页面中定义一个隐藏的表单元素,将需要提交的数据放在该元素中;
  • 在JS中动态创建一个表单,设置表单的action为后端处理数据的地址,设置表单的method为POST;
  • 将隐藏表单元素中的数据复制到动态表单中,并提交该表单。

例如:

phpCopy code点击提交

后端是怎么接收请求的

    1. 表单提交:对于使用表单元素提交数据的情况,后端可以通过接收HTTP请求中的表单数据来获取前端提交的数据。在Java中,可以通过HttpServletRequest对象的getParameter()方法获取表单数据。
    2. AJAX请求:对于通过AJAX方式提交数据的情况,后端可以通过接收HTTP请求中的请求体来获取前端提交的数据。在Java中,可以通过HttpServletRequest对象的getReader()方法获取请求体数据。
    3. RESTful API:对于使用RESTful API规范提交数据的情况,后端可以通过接收HTTP请求中的URL参数和请求体来获取前端提交的数据。在Java中,可以使用SpringMVC等框架提供的注解和参数绑定机制来获取数据。
    4. WebSocket:对于使用WebSocket协议提交数据的情况,后端可以通过接收WebSocket连接发送的数据来获取前端提交的数据。在Java中,可以使用Spring WebSocket等框架来处理WebSocket连接和数据传输。

4.Servlet阶段

什么是Servlet

Servlet是一种Java编写的服务器端程序,主要用于动态生成Web页面或处理HTTP请求和响应。Servlet运行在Web服务器上,可以接收HTTP请求,处理请求后生成HTML页面并返回给客户端,或者向客户端返回数据。

Servlet是Java EE(Enterprise Edition)规范的一部分,是一种标准的服务器端编程模型。Servlet可以用于实现各种服务器端应用程序,例如Web应用程序、企业级应用程序和后台处理程序等。

Servlet通常是通过Java Servlet容器来运行的,例如常用的Tomcat、Jetty和JBoss等容器,它们提供了Servlet API以及运行Servlet的环境。

Servlet的优点包括:

  • 轻量级:Servlet通常比传统的CGI脚本和JavaServer Pages(JSP)页面更快速、更节省资源。
  • 可移植性:Servlet代码不依赖于任何特定平台,可以在任何支持Java的平台上运行。
  • 可扩展性:Servlet可以通过继承和实现Servlet API中的接口来进行自定义扩展。

Servlet也有一些限制,例如无法直接操作客户端的Web浏览器,无法与客户端直接交互,必须依赖于HTTP协议进行通信等。

Servlet 转发与重定向的区别

  1. 请求转发的地址栏不会发生改变,重定向的地址栏会发生改变
  2. 请求转发只有一次请求,重定向有两次请求
  3. 请求转发时request对象可共享,重定向时request对象不共享
  4. 请求转发是服务端行为,重定向是客户端行为
  5. 请求转发时的地址只能是当前站点下(当前项目)的资源,重定向时地址可以是任何地址

Http协议的特点

    1. 支持客户/服务器模式。

    2. 简单快速

    3. 灵活:HTTP 允许传输任意类型的数据对象

    4. 无连接:无连接是表示每次连接只处理一个请求

    5. 无状态:HTTP 协议是无状态协议

      什么是Cookie

      Cookie 和 Session都是⽤来跟踪浏览器⽤户身份的会话⽅式,但是两者的应⽤场景不太⼀样。 维基百科是这样定义 Cookie 的:Cookies是某些⽹站为了辨别⽤户身份⽽储存在⽤户本地终端上 的数据(通常经过加密)。

      简单来说: Cookie 存放在客户端,⼀般⽤来保存⽤户信

cookie和session的区别

Cookie和Session都是Web应用程序中存储用户数据的机制,它们有一些不同之处。

1:Cookie是在客户端存储数据的机制,而Session是在服务器端存储数据的机制。

2:Cookie是将数据存储在浏览器中,每次请求都会将Cookie发送到服务器端,而Session是将数据存储在服务器端,每次请求只需要发送Session的ID即可。

3:Cookie可以设置过期时间,而Session默认是在用户关闭浏览器后过期。

4:Session的的客户器端底层原理就是cookie;

jsp的四大域及其作用范围

  1. Page作用域:指当前JSP页面,它的生命周期是一个页面,也就是说,只要页面没有跳转或者刷新,页面的所有变量都可以在该页面中共享。在页面的不同部分,如在JSP的脚本区和表单区,页面作用域都是相同的。
  2. Request作用域:指的是一次HTTP请求,只要请求还没有结束,请求作用域中的变量就可以在同一个请求中的不同页面或者Servlet之间共享。在JSP中,可以使用request对象访问这个作用域中的变量。
  3. Session作用域:指的是一次用户会话,从用户第一次访问站点开始到用户关闭浏览器为止。在这个过程中,服务器会为每个会话创建一个唯一的ID号,用于标识这个会话,并把这个ID号存储在cookie中,如果浏览器禁用了cookie,那么就会把这个ID号附加到URL后面。在JSP中,可以使用session对象访问这个作用域中的变量。
  4. Application作用域:指的是整个web应用,所有用户共享同一个作用域。在JSP中,可以使用application对象访问这个作用域中的变量。

Servlet的三大域及其作用域

Servlet的三大域指的是:应用程序域(ServletContext)、会话域(HttpSession)和请求域(HttpServletRequest)。

  • 应用程序域(ServletContext):表示整个web应用程序的范围,即在同一个web应用程序下的所有Servlet都可以访问它,可以用于在应用程序中共享数据。ServletContext对象的生命周期与Web应用程序的生命周期一致,它的初始化在Web应用程序启动时进行,销毁在Web应用程序停止时进行。
  • 会话域(HttpSession):表示与客户端交互的会话的范围,即同一个会话中的所有Servlet都可以访问它,可以用于在会话中共享数据。HttpSession对象的生命周期从客户端第一次发送请求开始,直到客户端关闭浏览器或超时,或者在服务器端调用invalidate()方法时结束。
  • 请求域(HttpServletRequest):表示一次HTTP请求的范围,即同一个请求中的所有Servlet都可以访问它,可以用于在请求中传递数据。HttpServletRequest对象的生命周期从客户端发起请求开始,直到服务器端响应请求结束。

这些作用域的使用范围和生命周期不同,可以根据具体的需求来选择使用哪种作用域。

Session失效的五种方式

过期失效:当用户长时间没有操作或处于闲置状态时,Session可能会因为超过预设的过期时间而自动失效。这种失效方式是最常见的,可以通过在服务器端配置Session的过期时间来控制,一般为30分钟。

主动失效:当应用程序需要主动销毁Session时,可以调用Session.invalidate()方法,将当前Session标记为无效。这种失效方式通常用于用户退出登录、切换账号等情况下。

设置失效:session.setMaxInactiveInterval(15),主动设置失效时间

d服务器重启失效:当服务器重启或应用程序重新部署时,所有正在运行的Session都会被销毁,这种失效方式是无法避免的。

浏览器关闭失效web中,什么时候会用到session作用域

Session作用域通常在Web应用程序中用于存储和管理用户的会话信息,以便在不同的页面和请求之间共享和访问这些信息。下面是一些使用Session作用域的常见场景:

        1. 用户登录验证:在用户成功登录后,可以将用户的身份信息(如用户名、角色等)存储在Session中,以便在后续的页面和请求中进行验证。
        2. 购物车管理:当用户在网站上添加商品到购物车中时,可以将购物车信息(如商品名称、价格、数量等)存储在Session中,以便在用户结算时进行结算和清空购物车。
        3. 多步表单提交:在表单中包含多个步骤时,可以将用户填写的信息(如姓名、地址、联系方式等)存储在Session中,以便在最终提交时将所有信息一次性提交到服务器端。

需要注意的是,过度地使用Session作用域可能会占用过多的服务器资源,影响应用程序的性能和扩展性。因此,应该在使用Session作用域时,尽量减小Session的数据量和存储时间,以保证应用程序的高效性。同时,也应该对Session的数据进行合理的管理和清理,以防止数据泄露和安全问题

Session的底层是什么

​ Session是Web开发中常用的一种管理用户会话的机制。它的底层实现可以分为两个部分:服务器端和客户端。

​ 在服务器端,Session是通过一种数据结构(如哈希表或树)来存储用户的会话数据的。服务器端会给每个Session分配一个唯一的Session ID,用于区分不同用户的会话。当用户发送请求时,服务器端会根据Session ID查找对应的Session数据,然后进行相关的处理,例如验证用户身份、读取用户配置信息等。同时,服务器端还会根据一定的策略(如过期时间、空闲时间等)来管理Session的生命周期。

​ 在客户端,Session ID通常是通过Cookie来传递的。服务器端会在响应中设置一个名为“Session ID”的Cookie,浏览器会将这个Cookie保存下来,然后在每次发送请求时自动带上这个Cookie,以便服务器端能够识别出当前用户的Session信息。当用户关闭浏览器时,这个Cookie就会被浏览器自动清除,对应的Session也就失效了。

​ 总的来说,Session的底层实现涉及到服务器端的数据结构、Session ID的管理和生命周期控制,以及客户端的Cookie传递机制。深入理解Session的底层实现对于Web开发人员来说是非常重要的,因为这可以帮助他们更好地使用和管理Session,从而提升应用程序的性能和安全性。

Servlet的工作流程

1:Web Client 向 Servlet 容器(Tomcat)发出 Http 请求

2:Servlet 容器接收 Web Client 的请求

3:Servlet 容器创建一个 HttpServletRequest 对象,将 Web Client请求的信息封装到这个对象 中

4:Servlet 容器创建一个 HttpServletResponse 对象

5:Servlet 容器调HttpServlet 对象service 方法,把 Request 与Response 作为参数,传给 HttpServlet

6:HttpServlet 调用 HttpServletRequest 对象的有关方法,获取 Http 请求信息:

7:HttpServlet 调用HttpServletResponse 对象的有关方法,生成响应数据

8:Servlet 容器把 HttpServlet 的响应结果传给 Web Client

5.框架阶段

Spring框架

:getBeean能做的获取对象实例,可以在类中进行实例化对象也能做,为什么还要引入Spring框架呢?

​ 在类中直接实例化对象的确可以完成对象的创建和使用,但是如果在一个复杂的系统中,类与类之间的关系变得越来越复杂,对象的创建、管理和调用也变得越来越困难。这时候,我们需要一个框架来帮助我们更好地组织和管理对象。

​ Spring框架就是一个非常流行的Java框架,它提供了依赖注入(Dependency Injection)和面向切面编程(Aspect Oriented Programming)等功能,可以帮助开发者更好地组织和管理对象。其中,依赖注入可以帮助我们实现松耦合,使得不同的组件之间可以更加独立地开发和维护,而面向切面编程则可以帮助我们实现横切关注点的功能,使得我们可以更好地实现日志记录、事务处理、安全控制等功能。

​ 所以,尽管我们可以在类中直接实例化对象,但是引入Spring框架可以帮助我们更好地组织和管理对象,并且提供了更加灵活和强大的功能,使得我们可以更加高效地开发和维护系统。


:Spring框架的特点

    1. 轻量级:Spring框架是一个轻量级的框架,它不依赖于其他的框架或工具,因此可以很容易地集成到其他的应用程序中。
    2. IOC容器:Spring框架提供了一个IOC容器,可以帮助开发人员管理Java对象的生命周期,包括创建、销毁和依赖注入等。
    3. AOP支持:Spring框架提供了AOP(面向切面编程)支持,可以帮助开发人员解耦和优化应用程序。
    4. Web框架:Spring框架提供了一个全功能的Web框架,可以帮助开发人员构建Web应用程序和REST API。
    5. 数据访问:Spring框架提供了一系列的数据访问技术,包括JDBC、ORM、事务管理等,可以帮助开发人员更容易地访问和管理数据库。
    6. 消息传递:Spring框架提供了一个强大的消息传递框架,可以帮助开发人员构建高效的消息传递应用程序。
    7. 测试支持:Spring框架提供了一个全功能的测试框架,可以帮助开发人员更容易地编写和运行测试用例。

总的来说,Spring框架是一个非常强大和灵活的Java开发框架,它可以帮助开发人员更容易地构建高质量的企业级应用程序。


:Spring框架的优缺点?

Spring框架是一个非常流行的开源Java框架,其主要优点包括:

优点:

    1. 轻量级:Spring框架采用的是基于POJO的轻量级容器,不依赖于EJB容器,使得开发者可以轻松地开发和测试应用程序。
    2. 面向切面编程(AOP)支持:Spring框架提供了AOP支持,可以将横切关注点从业务逻辑代码中分离出来,使得代码更加模块化和可维护。
    3. 依赖注入(DI)和控制反转(IOC)容器:Spring框架的核心是IOC容器,它可以自动装配应用程序中的组件和对象,简化了开发和配置过程。
    4. 集成各种框架和技术:Spring框架可以集成各种框架和技术,如Hibernate、MyBatis、Struts等,使得开发者可以更加灵活地选择和组合不同的技术来开发应用程序。
    5. 丰富的功能库:Spring框架提供了大量的功能库,如Spring MVC、Spring Security、Spring Batch等,可以满足不同的应用场景和需求。

缺点:

    1. 学习曲线较陡峭:由于Spring框架提供了众多的功能和选项,因此学习起来可能需要一定的时间和精力。
    2. 配置文件较为繁琐:Spring框架使用XML配置文件进行依赖注入和AOP配置,对于初学者来说,可能需要花费更多的时间和精力来编写和维护这些配置文件。
    3. 运行效率较低:相比于轻量级框架,Spring框架的运行效率较低,可能会对应用程序的性能产生一定的影响。

:Spring如何解决循环依赖问题

​ Spring 使用了“提前暴露”和“延迟注入”这两种技术来解决循环依赖问题。

​ 首先,Spring 允许通过“提前暴露”一个对象的引用来解决循环依赖问题。当一个 bean A 依赖于另一个 bean B,而 bean B 又依赖于 bean A 时,Spring 容器在创建 bean A 和 bean B 的过程中会先创建出它们的实例,但此时还没有完成属性的注入,因此在创建完 bean A 后,Spring 容器会将其实例暴露出来,以便其他 bean 可以使用它,然后再去创建 bean B,此时会注入 bean A 的引用,从而解决了循环依赖问题。

​ 其次,Spring 也支持“延迟注入”的方式来解决循环依赖问题。当一个 bean A 依赖于另一个 bean B,而 bean B 又依赖于 bean A 时,Spring 容器可以使用代理对象来延迟 bean A 的注入。即先创建一个代理对象作为 bean A 的占位符,等到 bean B 创建完成后,再去注入 bean A 的实例,从而避免了循环依赖的问题。

​ 需要注意的是,Spring 只能解决单例作用域下的循环依赖问题,因为在创建多例作用域的 bean 时,Spring 每次都会创建新的实例,无法通过提前暴露或延迟注入的方式解决循环依赖问题。

三级缓存解决循环依赖问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYRFH6fI-1684204326246)(项目学习及其总结.assets/Spring三级缓存的名称和主要作用.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21LYIHkm-1684204326246)(项目学习及其总结.assets/Spirng解决循环依赖问题.jpg)]


:谈谈你对IOC的理解

​ 控制反转(Inversion of Control,简称IoC)是一种编程思想和设计模式,它的主要思想是将对象的创建和管理工作交给框架来完成,从而降低对象之间的耦合性,提高代码的可维护性和可扩展性。

​ 在Spring框架中,IoC主要体现在依赖注入(Dependency Injection,简称DI)上。DI是指通过构造器、setter方法或者接口注入的方式,将依赖的对象注入到需要使用它的对象中,从而实现对象之间的解耦。通过DI,我们可以将代码中的依赖关系从代码中移除,从而使得代码更加简洁、清晰和易于维护。

​ IOC的好处不仅仅在于对象的管理和依赖注入,还可以帮助我们实现面向切面编程(Aspect-Oriented Programming,简称AOP),通过对程序的横向切割,将一些通用的功能(如事务管理、日志记录等)从应用程序的核心代码中剥离出来,使得代码更加简洁、可读性更好。

​ 总的来说,IoC是一种很好的编程思想,它能够帮助我们降低代码的耦合性,提高代码的可维护性和可扩展性,使得程序的架构更加清晰、简洁和易于理解。


SpringBean的生命周期

Spring的生命周期主要包括以下几个阶段:

  1. 实例化Bean:Spring容器根据Bean定义创建Bean实例,并通过构造函数或工厂方法进行初始化。

  2. 设置Bean属性:Spring容器将配置文件中的属性值或注解中的属性值设置到Bean实例中,完成Bean的属性注入。

  3. 调用Bean的初始化方法:如果Bean实现了InitializingBean接口或者配置了init-method方法,则在初始化Bean之后,Spring容器会自动调用Bean的初始化方法。

  4. Bean可以使用:Bean初始化之后,可以被其他Bean或者容器使用。

  5. 调用Bean的销毁方法:如果Bean实现了DisposableBean接口或者配置了destroy-method方法,则在关闭容器或者销毁Bean时,Spring容器会自动调用Bean的销毁方法。

  6. 销毁Bean:当Spring容器关闭或者销毁Bean时,会将Bean实例销毁。

    ​ 总的来说,Spring的生命周期可以帮助我们理解Spring容器如何管理Bean的创建、初始化和销毁过程,同时也可以在需要时通过实现InitializingBean和DisposableBean接口或者配置init-method和destroy-method方法来控制Bean的初始化和销毁行为。


懒加载更加节省内存,而且不影响运行,为什么不使用懒加载

      1. 首次访问时会有性能损耗。懒加载需要在第一次访问时才会去加载数据,如果数据量过大,加载时间会比较长,从而导致用户体验不佳。
      2. 可以提前发现潜在的配置问题
      3. Bean 对象存在于缓存中,使用时不用再去实例化bean, 加快程序运行效率
      4. 难以处理并发问题。在多线程环境下,如果多个线程同时访问一个尚未加载的对象,就可能会导致多个线程同时去加载数据,从而引起并发问题,比如数据重复加载、数据不一致等。
      5. 代码复杂度较高。在实现懒加载时,需要进行一些复杂的逻辑判断,比如判断数据是否已经加载、是否需要重新加载等,这会增加代码的复杂度,降低代码的可读性和可维护性

谈谈你对AOP的理解

​ 面向切面编程(Aspect-Oriented Programming,简称AOP)是一种编程思想和设计模式,它主要用于解决横切关注点(Cross-cutting Concerns)的问题,即那些存在于系统各个层次、各个模块中的通用功能,如日志记录、安全检查、事务管理等。

​ AOP通过将这些通用功能从应用程序的核心逻辑中分离出来,形成独立的切面(Aspect),然后将切面和核心逻辑进行合并,最终生成一个完整的应用程序。这种方式能够帮助我们实现代码的重用、模块化和可维护性。

​ 在AOP中,我们使用切面(Aspect)来描述通用功能,切面可以定义一个或多个切点(Pointcut),切点定义了在何处应用切面。在切点所指定的方法或者类中,我们可以使用通知(Advice)来描述切面所要执行的行为,通知可以在目标方法执行之前、之后或者抛出异常时执行。

​ 除了切面、切点和通知之外,AOP还涉及到另外两个概念:连接点(Joinpoint)和织入(Weaving)。连接点是指程序执行过程中的一个特定点,如方法调用、异常处理等。织入是指将切面与目标对象的连接点进行结合的过程。

​ 总的来说,AOP是一种很好的编程思想和设计模式,它能够帮助我们解决横切关注点的问题,提高代码的重用性和可维护性。在Spring框架中,AOP是一个重要的特性,它可以帮助我们实现事务管理、日志记录、安全检查等通用功能。


什么是代理模式

代理模式在 Java 开发中是一种比较常见的设计模式。设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用


JDK代理和CGLIB代理的区别

JDK动态代理实现接口,Cglib动态代理继承思想

JDK动态代理(目标对象存在接口时)执行效率高于Ciglib

如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理


AOP的底层实现方式

动态代理

​ 代理类增强委托类的行为可不理解为子类继承父类,对父类功能能进行修改以及对父类进行扩充?两者之间是否有一定的相似之处?

代理类增强委托类的行为可以看作是一种类似于子类继承父类的行为,但是它们之间还存在一些差异。

​ 代理模式中的代理类和委托类之间是一种合作关系,代理类会调用委托类的方法,并在此基础上进行增强,比如添加缓存、记录日志等操作。代理类和委托类之间没有继承关系,代理类只是委托类的一个代理,它不会修改委托类的原有功能,也不会扩充委托类的功能。代理类和委托类实现的接口通常相同,代理类通过实现相同的接口来对委托类的功能进行增强。因此,代理类和委托类之间的关系更像是一种装饰者模式,通过代理类来对委托类的功能进行增强。

​ 相比之下,子类继承父类则是一种典型的面向对象编程的方式。子类会继承父类的属性和方法,并可以对父类的功能进行修改和扩充。子类可以重写父类的方法,也可以添加新的方法和属性,从而实现更加灵活和多样化的功能。子类继承父类的关系是一种"是一种"(is-a)的关系,也就是说,子类是父类的一种特殊类型。

​ 因此,虽然代理类增强委托类的行为和子类继承父类的方式在某些方面有一定的相似之处,但它们之间仍然存在较大的差异。代理模式和装饰者模式主要用于增强对象的功能,而子类继承父类则是一种基于类的代码重用和扩展方式。


:静态代理的优缺点

​ 静态代理是一种代理模式,它可以在不修改原始对象的情况下,增强原始对象的功能或者控制原始对象的访问。静态代理的优缺点如下:

优点:

    1. 增强原始对象的功能:静态代理可以在不修改原始对象的情况下,增加一些额外的功能,比如添加日志、权限控制等。
    2. 控制原始对象的访问:静态代理可以对原始对象的访问进行控制,比如限制访问时间、限制访问次数等。
    3. 降低耦合性:通过代理对象来访问原始对象,可以将原始对象与代理对象解耦,降低系统的耦合性。
    4. 代码复用:如果多个原始对象需要增强同样的功能,可以复用同一个代理类。

缺点:

    1. 代理类与原始对象一一对应:每个原始对象都需要一个对应的代理类,如果原始对象比较多,代理类的数量也会随之增加。
    2. 代码维护成本高:如果原始对象的接口发生变化,代理类的接口也需要同步变化,这会增加代码维护的成本。
    3. 运行效率低:由于代理类需要调用原始对象的方法,再进行额外的操作,因此会导致运行效率低下。

总体来说,静态代理适用于增强或者控制原始对象的访问,但代理类的数量和代码维护成本较高,同时运行效率也有一定的损失。因此,在实际应用中,需要根据具体的业务需求和系统性能要求,综合考虑选择是否使用静态代理


:JDK代理实现动态代理的步骤和底层原理

JDK的动态代理主要使用了反射机制和代理类的生成机制来实现。当调用Proxy.newProxyInstance()方法时,JDK会在运行时动态生成一个代理类,并返回一个实现了指定接口的代理对象。

具体的实现步骤如下:

    1. 定义一个InvocationHandler接口的实现类,这个类需要实现invoke()方法,用于处理代理对象上的方法调用。
    2. 调用Proxy.newProxyInstance()方法,该方法接受三个参数:类加载器、代理类需要实现的接口数组和InvocationHandler接口的实现类。JDK会在运行时动态生成一个代理类,并返回一个实现了指定接口的代理对象。
    3. 当代理对象的方法被调用时,代理对象会将方法调用转发给InvocationHandler接口的实现类的invoke()方法。
    4. 在invoke()方法中,可以根据需要进行一些操作,例如打印日志、统计方法执行时间等,然后调用Method.invoke()方法来实际执行代理对象的方法。
    5. 在invoke()方法中,可以根据需要返回一个结果。

底层实现中:

JDK会动态生成一个类,该类实现了代理类需要实现的接口,同时继承了Proxy类。这个代理类中包含一个InvocationHandler类型的字段,用于存储代理对象的回调函数。当代理对象调用方法时,JVM会将方法调用转发给InvocationHandler接口的实现类的invoke()方法,从而实现对原方法的代理控制。

相比于CGLIB动态代理,JDK动态代理要求被代理类实现至少一个接口,但它的实现相对简单,同时也更加直观易懂。因此,在需要对已有接口进行代理的情况下,JDK动态代理是一个非常好的选择


:CGLIB的动态代理的工作步骤和底层实现

CGLIB(Code Generation Library)是一种基于ASM框架的字节码生成库,它可以在运行时生成一个被代理类的子类作为代理类,从而实现动态代理。相对于JDK动态代理只能代理实现了接口的类,CGLIB可以代理没有实现任何接口的类,因此使用场景更加广泛。

CGLIB的动态代理工作步骤如下:

    1. 创建Enhancer对象,该对象继承了net.sf.cglib.proxy.InvocationHandler接口,该接口是由JDK动态代理提供的,同时实现了MethodInterceptor接口,MethodInterceptor接口是CGLIB提供的。
    2. 通过Enhancer对象设置代理类的父类、回调方法等。
    3. 调用Enhancer.create()方法生成代理类的实例对象。
    4. 当代理对象调用方法时,将会进入到MethodInterceptor.intercept()方法中,在该方法中,可以对方法进行增强处理,例如在方法执行前后进行日志输出、事务处理等。

CGLIB的底层实现主要分为三个部分:

    1. 通过Enhancer.create()方法生成一个代理类的子类,该子类重写了父类中的所有非final方法,并将这些方法的访问级别设置为public或protected,同时在方法中调用MethodInterceptor.intercept()方法进行方法增强处理。
    2. 在代理类的子类中,将父类的所有非private字段复制到子类中,从而保证代理类能够访问这些字段。
    3. 在代理类的子类中,重写Object.equals()和Object.hashCode()方法,从而保证代理类能够正确地进行对象的比较和哈希值的计算

:静态代理和动态代理的区别

静态代理和动态代理是两种不同的代理方式,下面从多个方面来阐述它们的区别:

    1. 实现方式
    2. ​ 静态代理在编译期间就已经确定代理类的代码,即代理类和委托类的关系在程序运行前就已经确定了。而动态代理是在运行时动态生成代理类,即代理类的代码是在程序运行时生成的。
    1. 委托类个数
    2. ​ 静态代理需要为每个委托类创建一个代理类,因此当委托类数量增多时,会导致代理类也增多,工作量比较大。而动态代理可以代理多个委托类,代理类只需要一个即可。
    1. 运行效率
    2. ​ 静态代理由于在编译期间已经生成代理类,因此运行效率比较高。而动态代理在运行时需要动态生成代理类,因此会比静态代理略慢。
    1. 动态性
    2. ​ 静态代理在程序编译后代理类和委托类的关系就已经确定,无法在运行时改变代理行为。而动态代理在运行时可以动态地添加、删除和修改代理行为。
    1. 可扩展性
    2. ​ 由于静态代理需要为每个委托类创建一个代理类,因此当需要增加新的委托类时,需要修改代理类的代码,不太灵活。而动态代理可以通过增加新的委托类接口,然后实现该接口并注册到InvocationHandler中,即可完成代理。
    1. 使用范围
    2. ​ 静态代理适用于只有少量类需要代理的场景,而动态代理适用于需要代理大量类的场景,例如AOP。

动态代理的优缺点

动态代理的优点包括:

  1. 代码复用性高:通过动态代理可以将目标对象和代理对象的公共代码抽象出来,提高了代码的复用性和可维护性。
  2. 扩展性强:通过动态代理可以灵活地扩展目标对象的功能,无需修改原有代码,降低了代码的耦合性。
  3. 代码简洁:动态代理可以减少冗余代码,提高代码的可读性和可维护性。
  4. 可以实现横切逻辑:通过AOP(面向切面编程)实现了横切逻辑的复用,如日志记录、权限控制、事务管理等。

动态代理的缺点包括:

  1. 运行时效率较低:动态代理需要在运行时动态生成代理类,会比静态代理多出一些运行时开销,降低了程序的效率。
  2. 只能代理接口或父类方法:基于接口的动态代理只能代理接口中定义的方法,基于类的动态代理只能代理父类中的方法,无法代理类中的私有方法和静态方法。
  3. 难以调试:动态代理会生成大量的中间代码,难以直接进行调试,需要通过工具来查看生成的代码。

总的来说,动态代理是一种强大的技术,可以提高代码的可维护性和复用性,但也需要在实际应用中根据具体情况进行选择。


批处理和存储过程

概念

两者都是对大量数据进行操作的方式


批处理的优缺点

批处理的优点包括:

  1. 提高性能:批处理可以减少与数据库的交互次数,提高数据库的操作效率。
  2. 降低资源消耗:批处理可以减少服务器的资源消耗,例如减少网络带宽、减少数据库连接数等。
  3. 简化代码:批处理可以简化代码,减少代码量,提高代码可读性和可维护性。
  4. 支持原子操作:批处理可以将多个操作打包成一个原子操作,避免了在多个操作之间的数据不一致性问题。

批处理的缺点包括:

  1. 内存消耗:批处理需要将数据缓存在内存中,如果处理的数据量非常大,可能会导致内存消耗过大。
  2. 可能出现超时问题:如果批处理的操作量过大,可能会导致数据库执行时间过长,从而出现超时问题。
  3. 可能出现锁等待问题:如果批处理的操作包含大量的更新操作,可能会导致锁等待问题,从而降低数据库的性能。
  4. 不支持实时性:批处理需要将一定数量的操作打包后才能进行处理,因此无法做到实时处理数据的需求

存储过程的优缺点

存储过程的主要优点包括:

  1. 减少网络传输量:由于存储过程在数据库服务器端执行,因此可以减少网络传输量。这对于处理大量数据的应用程序来说非常重要。
  2. 提高执行速度:由于存储过程在数据库服务器端执行,因此可以利用数据库的优化技术和缓存机制,提高执行速度。
  3. 减少数据库访问:存储过程可以把多个 SQL 语句封装成一个过程,从而减少了数据库访问的次数。
  4. 简化开发:存储过程可以封装复杂的业务逻辑,从而简化了应用程序的开发。
  5. 提高安全性:存储过程可以限制对数据库的直接访问,从而提高了安全性。

但是存储过程也存在一些缺点:

  1. 存储过程的编写需要一定的专业知识和技能,因此开发成本相对较高。
  2. 存储过程的调试比较困难,因为存储过程在数据库服务器端执行,开发者不能直接调试。
  3. 存储过程的维护和更新比较困难,因为存储过程的变更可能会对应用程序产生影响。
  4. 存储过程的性能问题可能会导致应用程序的性能下降。例如,存储过程可能会因为过度复杂而降低性能。

两者之间的区别

​ 批处理和存储过程都是用来提高数据库操作效率的技术,但它们在实现方式、使用场景和优缺点等方面存在一些差异,具体如下:

  1. 实现方式: 批处理是通过JDBC等数据访问框架提供的API实现的,可以用一条SQL语句同时执行多个操作,或者将多个相同或者类似的SQL语句合并成一个批量操作,执行效率比逐条执行高。 存储过程是一种数据库对象,它由一组预编译的SQL语句组成,被存储在数据库中。存储过程可以通过调用其名称来执行,其优势在于减少网络传输的开销和SQL解析的开销,提高了数据库操作的效率。
  2. 使用场景: 批处理适用于需要对同一张表进行批量操作的情况,如批量插入、批量更新、批量删除等。 存储过程适用于处理业务逻辑复杂的数据库操作,如涉及多个表之间关联查询和数据处理的复杂查询操作等。
  3. 优缺点: 批处理的优点在于简单易用、灵活性高、可以提高数据库操作效率,但需要手动编写SQL语句,缺乏可读性和可维护性。 存储过程的优点在于封装了复杂的业务逻辑,可以提高数据库操作效率,减少网络开销,同时也有较好的可读性和可维护性,但需要专门的存储过程语言和工具支持,不适合简单的数据操作。
  4. 安全性 :存储过程可以对数据库操作进行权限控制,可以在存储过程内部设置权限,确保安全性。而批处理相对于存储过程,较为简单,权限控制会相对较弱。
  5. 跨平台支持 :批处理通常由应用程序实现,基于JDBC等标准协议,具有良好的跨平台性,支持不同数据库的操作。 而存储过程是依赖于具体数据库管理系统的特定实现,不同数据库系统的存储过程语言和语法可能不同,跨平台性不如批处理。

多删除操作的思想
  1. 将要多操作的数据进行封装进集合中
  2. 调用spring+jdbc的批处理方法进行批处理
  3. 返回受影响行数

我么能从外部引入日志,比如log4j,为何还要使用AOP的日志呢?

​ 虽然我们可以使用第三方日志框架(如Log4j)来记录应用程序的日志,但是在实际应用中,我们通常会需要对某些关键方法或者类进行额外的日志记录,如记录方法的入参、返回值、异常信息等。这时候,如果我们使用传统的日志记录方式,就需要在每个关键方法或者类中都添加日志记录的代码,这样会使代码变得冗余,难以维护。

​ 使用AOP的日志记录方式,可以解决这个问题。在AOP中,我们可以定义一个日志切面,在这个切面中,我们可以定义一个通知,来实现额外的日志记录。然后,我们只需要将这个切面应用到需要记录日志的方法或者类中,就可以自动实现日志记录了,避免了代码的冗余和维护难度。

​ 除了日志记录,AOP还可以应用于其他的通用功能,如安全检查、事务管理等。这些通用功能可以在切面中定义,然后通过将切面应用到需要使用的方法或者类中,来实现通用功能的重用和可维护性的提高。

​ 综上所述,虽然我们可以使用第三方日志框架来记录应用程序的日志,但是使用AOP的日志记录方式,可以帮助我们实现额外的日志记录,同时提高代码的重用性和可维护性。

@Resource和@Autowired的区别

共同点

​ 两者都是实现Bean的注入功能

​ 两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法, setter方法可以省略掉。注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的 思想,通过set、get去操作属性,而不是直接去操作属性

不同点:

1:注解应用的范围不同

@Autowired能够用在构造方法、成员变量、方法、参数以及注解上,而@Resource能用在类、成员变 量和方法上,这点从源码也能看得出来。

2: 出处不同

@Autowied是Spirng框架的注解,而@Resource注解是JDK内置的注解

3:注解内部定义的参数不同

@Autowired只包含一个required参数,默认为true,表示开启自动注入。如果允许null值,可以设置 它的required属性为false。

public @interface Autowired { 

//是否开启自动注入,不开启自动装配,可设为false。 

boolean required() default true; 

} 

而@Resource 包含七个参数,其中最重要的两个属性是name和type,Spring将@Resource注解的 name属性解析为bean的名字,而type属性则解析为bean的类型

4:装配方式的默认值不同

@Autowired 默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配 并注入 Bean (接口的实现类),但是当byType方式找到了多个符合的bean,又是怎么处理的? Autowired默认先按byType,如果发现找到多个bean,则又按照byName方式比对,如果还有多个, 则报出异常。

@Autowired默认按type自动装配,而@Resource默认按name自动装配。当然,@Resource注解可以 自定义选择,如果指定了name,则根据name自动装配,如果指定了type,则用type自动装配

5:装配顺序不同

…………没写完……建议看畅通分享的资料………………………………………………………………

SpringMVC

谈谈你对SpirngMVC的理解

​ 首先,Spring MVC是是属于Spring Framework生态里面的一个模块,它是在Servlet基础上构建并且使用MVC模式设计的一个Web框架,主要的目的是简化传统Servlet+JSP模式下的Web开发方式。

其次, Spring MVC的整体架构设计对Java Web里面的MVC架构模式做了增强和扩展,主要有几个方面。

把传统MVC框架里面的Controller控制器做了拆分,分成了前端控制器DispatcherServlet和后端控制器Controller。

把Model模型拆分成业务层Service和数据访问层Repository。

在视图层,可以支持不同的视图,比如Freemark、velocity、JSP等等。

所以,Spring MVC天生就是为了MVC模式而设计的,因此在开发MVC应用的时候会更加方便和灵活。

Spring MVC的具体工作流程是,浏览器的请求首先会经过SpringMVC里面的核心控制器DispatcherServlet,它负责对请求进行分发到对应的Controller。Controller里面处理完业务逻辑之后,返回ModeAndView。然后DispatcherServlet寻找一个或者多个ViewResolver视图解析器,找到ModeAndView指定的视图,并把数据显示到客户端。

SpringMVC的请求流程

项目学习及其总结(黎明)_第5张图片

  1. 1:⾸先⽤⼾发送请求,请求被SpringMvc前端控制器(DispatherServlet)捕获; 
    
    1. 前端控制器(DispatherServlet)对请求URL解析获取请求URI,根据URI, 调⽤HandlerMapping; 
    2. 前端控制器(DispatherServlet)获得返回的HandlerExecutionChain(包括Handler对象以及Handler对象对应 
    
    的拦截器); 
    
    4. DispatcherServlet 根据获得的HandlerExecutionChain,选择⼀个合适的HandlerAdapter。(附注:如果成 
    
    功获得HandlerAdapter后,此时将开始执⾏拦截器的preHandler(...)⽅法); 
    
    5. HandlerAdapter根据请求的Handler适配并执⾏对应的Handler;HandlerAdapter(提取Request中的模型数 
    
       据,填充Handler⼊参,开始执⾏Handler(Controller)。 在填充Handler的⼊参过程中,根据配置,Spring 
    
       将做⼀些额外的⼯作: 
    
       ​		HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成⼀个对象,将对象转换为指定的响应信 
    
       息。 
    
       ​		数据转换:对请求消息进⾏数据转换。如String转换成Integer、Double等数据格式化: 
    
       ​		数据格式化。 如将字符串转换成格式化数字或格式化⽇期等 
    
       ​		数据验证: 验证数据的有效性(⻓度、格式等),验证结果存储到BindingResult或Error中) 
    
    6. Handler执⾏完毕,返回⼀个ModelAndView(即模型和视图)给HandlerAdaptor 
    
    7. HandlerAdaptor适配器将执⾏结果ModelAndView返回给前端控制器。 
    
    8. 前端控制器接收到ModelAndView后,请求对应的视图解析器。9. 视图解析器解析ModelAndView后返回对应View; 
    
    9. 渲染视图并返回渲染后的视图给前端控制器。 
    
    10. 最终前端控制器将渲染后的⻚⾯响应给⽤户或客户端
    

拦截器的作用及其应用场景

​ 拦截器(Interceptor)是Spring框架提供的一种机制,用于在执行处理器方法(Controller中的方法)之前、之后或渲染视图之前、之后对请求进行拦截和处理。在拦截器中,可以对请求进行预处理、后处理和异常处理等操作,常见的拦截器操作包括:鉴权、日志记录、参数校验、数据加解密等。

拦截器的使用场景:

  1. 鉴权:通过拦截器来验证用户是否有权限访问某些特定的请求,例如,需要登录才能访问某些页面,需要管理员权限才能进行某些操作等。

  2. 参数校验:通过拦截器来验证请求参数是否符合要求,例如,请求参数不能为空、参数类型必须是指定的类型等。

  3. 数据加解密:通过拦截器来对请求或响应中的数据进行加密或解密,确保数据的安全性。

  4. 日志记录:通过拦截器来记录请求的详细信息,例如,请求URL、请求参数、请求头信息等,便于后续的排查和分析。

  5. 缓存控制:通过拦截器来控制响应结果的缓存,例如,设置响应结果的过期时间,控制缓存是否可以被浏览器缓存等。

    总之,拦截器的使用场景非常广泛,可以根据实际需要来进行扩展和应用。


SpirngMVC中的拦截器和过滤器的区别

①拦截器是基于java的反射机制的,而过滤器是基于函数回调。

②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。

③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

文章链接:http://t.csdn.cn/PpJeZ


文件的上传和多文件上传的步骤

文件上传的步骤

​ 1:从前端获取文件参数 使用注解:@RequestParam(“name值”)

​ 2:判断文件是否为空,为空则直接提示文件不存在

​ 3:不为空,获取文件的绝对路径 老方法request.getServletContext().getRealPath(“/”)

​ 4: 设置上传文件存放的目录

​ 5:获取文件的绝对文件名,调用 file.getOriginalFilename()方法即可获取,然后进行获取文件的名字suffx

​ 6:新文件名称的拼接 使用时间戳的形式进行文件名称的获取-----System.currentTimeMillis() + suffx ,拼接文件的名称

​ 7:直接进文件的上传, 上传到指定路径 file.transferTo(new File(uploadFile, fileName))

具体的代码
/**
 * Date:2023/4/20
 * Author:黎明
 * Description:  文件上传和下载
 */
@Controller
public class FileController {
         //单文件上传
    @RequestMapping("/uploadFile")
    public String uploadFile(HttpServletRequest request ,@RequestParam("file") MultipartFile file){
        //上传文件
        saveFile(file,request);
        return "result";
    }   

    //多文件的上传操作
    @RequestMapping("/uploadFiles")
    public String uploadFiles(HttpServletRequest request, @RequestParam("files") List<MultipartFile> files){

        // 判断文件集合是否为空
        if (files != null && files.size() > 0) {
            for (MultipartFile file : files ) {
                // 上传文件
                saveFile(file, request);
            }
        }
        return "result";
    }



    /*文件的上传操作
        * MultipartFile该类名可以提供获取文件名的方法
        * */
    private void saveFile(MultipartFile file, HttpServletRequest request) {
        // 判断文件是否为空,如果不为空则进行对应的文件上传操作
        if (!file.isEmpty()) {
            try {
                // 获取项目所在的路径 (绝对路径)
                String path = request.getServletContext().getRealPath("/");
                // 设置上传文件存放的目录
                File uploadFile = new File(path + "/upload");
                // 判断文件目录是否存在,如果不存在则新建对应的目录
                if (!uploadFile.exists()) {
                    // 新建目录
                    uploadFile.mkdir();
                }

                // 获取上传文件的文件名
                String originalName = file.getOriginalFilename();
                // 获取上传文件的后缀名,结合suffx,获取文件物理路径
                String suffix = originalName.substring(originalName.lastIndexOf("."));
                // 通过系统当前时间的毫秒数,生成随机的文件名
                String fileName = System.currentTimeMillis() + suffix;
                // 上传文件 (转存文件到指定目录)
                file.transferTo(new File(uploadFile, fileName));

                // 如果上传成功,设置作用域
                request.setAttribute("msg","文件上传成功!");

            } catch (IOException e) {
                e.printStackTrace();
                // 如果上传失败,设置作用域
                request.setAttribute("msg","文件上传失败!");
            }
        } else {
            // 如果上传文件不存在,设置作用域
            request.setAttribute("msg","文件不存在!");
        }
    }

}

Springboot框架

Springboot中的Starter的启动机制

项目学习及其总结(黎明)_第6张图片

@SpringbootApplication注解的启动步骤
     @Configuration: 表示这是一个配置类,类似于XML配置文件中的标签。
     @ComponentScan: 表示自动扫描并加载符合条件的组件类,类似于XML配置文件中的context:component-scan标签。
     @EnableAutoConfiguration: 表示开启自动配置,根据classpath中的jar包自动完成配置,类似于XML配置文件
		中的context:annotation-config和context:component-scan的结合
	
具体流程:
	自动扫描并加载符合条件的组件类。
	加载classpath中的jar包并自动完成配置。
	使用内嵌的Tomcat容器启动Web应用程序,监听默认的端口8080。
	
	具体来说,@EnableAutoConfiguration注解通过加载META-INF/spring.factories文件中的配置信息,完成了自动配置的工作,这个文件中包含了各种条件判断和自动配置类的全限定名,根据这些信息,自动完成各种Bean的创建和配置工作。而@ComponentScan注解则自动扫描并加载符合条件的组件类,它的默认行为是扫描启动类所在的包及其子包下的所有类,并将它们作为Bean注册到Spring IoC容器中。



spring-boot-starter-parent 有什么用 ?

我们都知道,新创建一个 Spring Boot 项目,默认都是有 parent 的,这个 parent 就是 spring-boot-starter-parent ,spring-boot-starter-parent 主要有如下作用:

​ 定义了 Java 编译版本为 1.8 。
​ 使用 UTF-8 格式编码。
​ 继承自 spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号。
​ 执行打包操作的配置。
​ 自动化的资源过滤。
​ 自动化的插件配置。
​ 针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml。


Springboot打包的两种方式及其具体步骤

jar包部署

​ idea 下配置 clean compile package -Dmaven.test.skip=true 执⾏打包命令,target ⽬录得到待部署的项⽬⽂件。

项目学习及其总结(黎明)_第7张图片

运行:

​ 打开本地 dos 窗⼝,执⾏ java -jar 命令 部署已打好的 jar 包⽂件。

​ 命令如下: java -jar jar包所在⽬录

war包部署

1:修改pom文件,添加依赖,因为默认的打包方式是jar包,修改为war包

项目学习及其总结(黎明)_第8张图片

2:忽略Tomcat
项目学习及其总结(黎明)_第9张图片

3:设置包的重命名
项目学习及其总结(黎明)_第10张图片

4:修改启动容器

@SpringBootApplication
public class Starter extends SpringBootServletInitializer {
	 private static Logger logger = LoggerFactory.getLogger(Starter.class);
    
 	 public static void main(String[] args) {
	 logger.info("SpringBoot 应⽤开始启动...");
	 SpringApplication.run(Starter.class);
	 }
    
 @Override
 protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
 	return builder.sources(Starter.class);
 	}
}

5:直接jar包的命令
项目学习及其总结(黎明)_第11张图片

6:放在tomcat容器下
项目学习及其总结(黎明)_第12张图片

7:改webapp路径下,直接运行cmd


Ehcache缓存的好处和作用

Ehcache是一个Java开源的分布式缓存框架,它的好处和作用如下:

  1. 提高系统性能:缓存的使用可以减少对数据库等资源的访问,从而提高系统的性能和响应速度。
  2. 减少系统资源消耗:缓存可以减少对系统资源的消耗,如减少对数据库、磁盘等I/O操作,从而降低系统的负载。
  3. 支持分布式环境:Ehcache支持分布式缓存,可以将缓存数据分布在多个节点上,从而支持分布式系统的使用。
  4. 可配置性高:Ehcache提供了多种缓存配置选项,如缓存大小、过期时间、清理策略等,可以根据实际需求进行配置。
  5. 支持多种数据源:Ehcache不仅支持本地缓存,还可以通过插件的方式支持多种数据源,如Redis、Hazelcast等,从而提供了更加灵活的使用方式。
  6. 提高系统可用性:Ehcache支持数据备份和恢复,可以在系统故障或宕机时保障缓存数据的安全性和可用性。

总之,Ehcache作为一个高性能、可扩展、可配置的缓存框架,可以有效地提高系统性能、降低系统负载,从而提高系统的可用性和可靠性


pring Boot 中的起步依赖是什么,有哪些常用的起步依赖?

Spring Boot 中的起步依赖是一种特殊类型的依赖,用于引入一组相关的依赖项,以方便开发人员快速集成某种功能。这些起步依赖通常包括所需的库、配置和默认值,使得开发人员可以在无需复杂配置的情况下快速集成所需的功能。

Spring Boot 提供了很多常用的起步依赖,包括但不限于:

  • spring-boot-starter-web:用于构建 Web 应用程序的起步依赖,包含了 Spring MVC、Tomcat、Jackson 等。
  • spring-boot-starter-data-jpa:用于使用 JPA 访问数据库的起步依赖,包含了 Hibernate、Spring Data JPA 等。
  • spring-boot-starter-data-redis:用于使用 Redis 的起步依赖,包含了 Jedis、Lettuce 等。
  • spring-boot-starter-test:用于编写测试的起步依赖,包含了 JUnit、Spring Test 等。
  • spring-boot-starter-actuator:用于监控应用程序运行状况的起步依赖,包含了 Health、Metrics 等。

通过使用起步依赖,可以减少开发人员的工作量,提高开发效率,同时也可以确保依赖项的版本兼容性和稳定性。

起步依赖简单地说就是pom依赖!!


请简述 Spring Boot 的自动配置原理。

Spring Boot 的自动配置是基于条件化配置(Conditional Configuration)实现的。条件化配置是一种基于条件进行配置的机制,通过配置不同的条件来控制某些配置是否生效。

Spring Boot 在启动时会扫描 classpath 下的 META-INF/spring.factories 文件,根据其中配置的各个自动配置类来进行自动配置。每个自动配置类都是一个 @Configuration 类,其中会使用 @Conditional 注解来配置生效的条件。如果当前应用程序的环境满足该条件,自动配置就会生效,否则就会被忽略。

通过自动配置,Spring Boot 可以根据当前环境的不同,自动配置各种不同的组件,例如数据库连接池、Web 服务器、消息队列、缓存等,简化了应用程序的开发配置过程,提高了开发效率。同时,Spring Boot 的自动配置也提供了很好的扩展性,允许用户根据自己的需求自定义配置。


如何进行 Spring Boot 应用程序的性能调优?

进行 Spring Boot 应用程序的性能调优可以采取以下措施:

  1. 合理配置连接池:通过合理配置连接池,可以充分利用连接资源,提高应用程序的性能。可以通过设置连接池大小、最大等待时间、最小空闲连接数等参数来优化连接池。
  2. 优化 SQL 语句:通过优化 SQL 语句,可以提高数据库查询性能。可以通过使用索引、避免使用不必要的子查询等方式来优化 SQL 语句。
  3. 缓存数据:通过缓存数据,可以减少数据库查询次数,提高应用程序的性能。可以使用 Spring Cache 或第三方缓存库来实现数据缓存。
  4. 使用异步编程:通过使用异步编程,可以提高应用程序的并发性能。可以使用 Spring Boot 的异步特性或者其他框架(如 Netty)来实现异步编程。
  5. 使用分布式架构:通过使用分布式架构,可以将负载分摊到多个服务器上,提高应用程序的性能。可以使用 Spring Cloud 或者其他分布式框架来实现分布式架构。
  6. 进行代码优化:通过优化代码,可以减少不必要的计算和内存消耗,提高应用程序的性能。可以使用工具进行代码检查和性能分析,找出性能瓶颈并进行优化。
  7. 监控和调优:通过监控和调优,可以实时了解应用程序的性能状况,并进行调整。可以使用工具进行监控和分析,如 JProfiler、VisualVM 等。同时,可以利用 Spring Boot Actuator 来实现应用程序的监控和管理。

restful的开发风格

RESTful 是一种基于 HTTP 协议的 Web 应用程序开发风格。其核心思想是将资源作为第一类元素进行管理,通过 URL 来标识资源,并通过 HTTP 方法来定义对资源的操作。

RESTful 的开发风格主要有以下几个特点:

  1. 资源的统一标识:每个资源都应该有一个唯一的 URL,可以通过 URL 来访问该资源。
  2. 以资源为中心:RESTful 风格的应用程序将资源作为第一类元素,通过 URL 对资源进行管理,而不是通过对数据表和数据字段进行管理。
  3. 使用 HTTP 方法来定义对资源的操作:HTTP 协议定义了多种方法,包括 GET、POST、PUT、DELETE 等。RESTful 应用程序使用这些方法来定义对资源的操作。
  4. 使用 JSON 或 XML 来传输数据:RESTful 应用程序通常使用 JSON 或 XML 格式来传输数据,这些格式都是轻量级的,可以快速地进行解析和生成。
  5. 无状态的通信:RESTful 应用程序使用无状态的通信方式,每个请求都是独立的,不依赖于之前的请求。这样可以降低系统的复杂性,提高系统的可伸缩性。

总之,RESTful 的开发风格是一种轻量级、灵活的 Web 应用程序开发方式,可以提高系统的可维护性和可扩展性,被广泛应用于 Web 服务、移动应用程序等领域。

谈谈你对MAVEN的理解

官方的定义:

Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后Maven可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件,它可以将项目开发和管理过程抽象成一个项目对象模型(POM)

GPT:Maven 是一个开源的项目管理工具,基于 Java 平台,可以实现项目的构建、依赖管理、版本控制和发布等功能。

Maven 的核心思想是约定优于配置,它通过规定一些默认的目录结构、命名规范和约定的插件等,来减少项目的配置和编写工作,提高开发效率。同时,Maven 还提供了丰富的插件和模板,可以快速构建和发布 Java 项目。

Maven 的主要功能包括:

  1. 依赖管理:Maven 可以通过 pom.xml 文件自动管理项目的依赖,支持自动下载依赖包、自动解决依赖冲突等功能。
  2. 构建工具:Maven 可以自动构建项目,包括编译、测试、打包等操作,同时还支持插件扩展和自定义构建流程。
  3. 发布工具:Maven 可以将构建好的项目发布到本地或远程仓库,便于其他开发人员共享和使用。
  4. 版本控制:Maven 可以对项目的版本进行管理,包括依赖版本和项目版本等。

总的来说,Maven 是一个功能强大、易于使用、开源免费的项目管理工具,可以帮助开发人员快速构建、测试和发布 Java 项目,提高开发效率和项目质量。

框架的总结阶段

谈谈你对Spring,SpringMVC和Spirngboot之间关系的理解

我理解的Spring、Spring MVC和Spring Boot之间的关系如下:

  1. Spring是一个开源的Java应用程序开发框架,旨在简化企业级应用程序的开发。它提供了大量的功能和组件,包括依赖注入(Dependency Injection)、面向切面编程(Aspect-Oriented Programming)、数据访问、事务管理、安全性等。
  2. Spring MVC是Spring框架的一部分,它是基于模型-视图-控制器(MVC)模式的Web应用程序开发框架。它提供了一套处理Web请求、呈现视图、处理表单数据等的机制,同时与其他Spring组件无缝集成,使得开发者能够轻松地构建灵活和可维护的Web应用程序。
  3. Spring Boot是基于Spring框架的一个开箱即用的快速应用程序开发框架。它旨在简化Spring应用程序的初始配置和开发过程,提供了自动配置、约定优于配置的原则,以及大量的开发人员友好的特性。Spring Boot通过提供默认配置和自动化配置,使得开发者可以更快地启动和运行应用程序,同时保持了与Spring框架的无缝集成。

因此,可以总结出以下关系:

  • Spring是一个全面的Java应用程序开发框架,提供了依赖注入、面向切面编程、数据访问、事务管理等功能和组件。
  • Spring MVC是Spring框架的一部分,专注于Web应用程序开发,提供了基于MVC模式的机制来处理Web请求和视图呈现。
  • Spring Boot是基于Spring框架的快速应用程序开发框架,旨在简化Spring应用程序的配置和开发过程,提供了默认配置和自动化配置的特性。

可以使用Spring MVC开发Web应用程序,而Spring Boot可以进一步简化Spring MVC应用程序的配置和开发,使开发者能够更快地启动和运行应用程序。Spring Boot提供了自动配置、内嵌的Servlet容器、约定优于配置等特性,同时也能够集成其他Spring组件,如Spring MVC、Spring Data、Spring Security等。因此,Spring Boot可以被视为一种简化和加速Spring应用程序开发的工具,而Spring MVC则是在Web应用程序开发中具体负责处理请求和视图的组件。

说说你熟知的几个注解

  1. @Autowired:这是Spring框架的核心注解之一,用于自动装配(依赖注入)Bean。它可以标记在构造函数、属性、Setter方法或方法参数上,使得Spring容器可以自动解析并注入相应的Bean。

  2. @Resource:与@Autowired注解的使用方式一样,该注解是JDK内置的注解,可以达到依赖注入的效果;

  3. @Controller:这是Spring MVC框架中用于标记控制器(Controller)类的注解。通过在控制器类上添加@Controller注解,Spring MVC可以识别该类作为请求处理的组件,并进行相应的请求映射。

  4. @RequestMapping:这是Spring MVC中用于定义请求映射的注解。可以将它应用于控制器方法上,指定处理请求的URL和HTTP方法。通过@RequestMapping注解,可以灵活地定义URL路径和请求方法与对应方法的映射关系。

  5. @RestController:这是Spring MVC中用于标记RESTful风格控制器(Controller)类的注解。与@Controller注解不同,@RestController注解将控制器类的所有方法默认视为返回JSON或XML等RESTful风格的响应数据。

  6. @Service:这是Spring框架中用于标记服务层(Service)类的注解。通过在服务层类上添加@Service注解,Spring容器可以识别该类作为业务逻辑处理的组件,并进行相应的依赖注入。

  7. @Repository:这是Spring框架中用于标记数据访问层(Repository)类的注解。通过在数据访问层类上添加@Repository注解,Spring容器可以识别该类作为数据访问组件,并进行相应的依赖注入和异常转换。

  8. @Configuration:这是Spring框架中用于定义配置类的注解。通过在配置类上添加@Configuration注解,可以告诉Spring容器该类是用于配置Bean的,其中可能包含了一些@Bean方法来定义Bean的创建和配置。

  9. @ComponentScan:这是Spring框架中用于指定组件扫描路径的注解。可以将它应用于配置类上,指定要扫描的包路径,以便Spring容器能够自动发现和注册相应的组件。

  10. @MapperScan:配置类注解,加在启动类上,再注明dao包的路径,即可省略dao层的@Repository注解;

  11. @ResponseBody是Spring MVC框架中常用的注解之一,用于指示方法返回的结果直接作为响应体内容,而不是通过视图解析器进行页面渲染。

    当一个方法被标记为@ResponseBody时,Spring MVC将会将方法返回的对象转换为合适的响应体格式,如JSON、XML等,并通过HTTP响应发送给客户端。这使得开发者能够更方便地构建RESTful风格的Web服务,将数据直接以结构化的格式返回给客户端

  12. @RequestBody是Spring MVC框架中常用的注解之一,用于将请求体内容绑定到方法参数上。当一个方法参数被标记为@RequestBody时,Spring MVC将会自动将请求体中的数据反序列化为对应的Java对象,并将其作为方法参数传递给方法进行处理。这样可以方便地接收和处理来自客户端的结构化数据,如JSON、XML等

  13. @RequestParam是Spring MVC框架中常用的注解之一,用于从请求中获取参数值并绑定到方法参数上。

    @RequestParam可以用于方法的参数或方法的参数上,用于指定要获取的请求参数的名称和其他属性。绑定的同时也可以给变量进行初始赋值,例如:@RequestParam(value = “page”, defaultValue = “1”

  14. @PathVariable是Spring MVC框架中常用的注解之一,用于从请求路径中获取占位符的值并绑定到方法参数上。

    在RESTful风格的Web服务中,URL路径常常包含占位符,表示某个变量的值。@PathVariable可以用于方法的参数或方法的参数上,用于指定要获取的路径变量的名称和其他属性。

  15. @Transactional是Spring框架中常用的注解之一,用于标记方法或类的事务性行为,@Transactional(propagation = Propagation.REQUIRED),在方法体上增加这个注解用于开启事务。

    ……………………………………………等等等等,自行补充…………………………………………………

8.项目阶段

简述全局异常的作用

  全局异常是指在应用程序中未被捕获的异常,可以通过全局异常处理器进行统一的处理,从而达到对应用程序中所有异常的统一处理。

全局异常处理器通常用于以下几个方面:

 统一异常处理:全局异常处理器可以将应用程序中所有未被捕获的异常进行捕获和处理,避免因为某一个异常没有被处理导致整个应用程序崩溃。
    2. 友好的错误提示:全局异常处理器可以将错误信息进行包装,使得用户可以看到更加友好的错误提示,而不是一堆错误代码。
    3. 日志记录:全局异常处理器可以将异常信息记录到日志中,以方便开发人员进行错误排查和系统分析。

通过全局异常处理器,我们可以对应用程序中的所有异常进行统一的处理,从而提高应用程序的可靠性和可维护性。

CRM如何实现免登录的

登陆成功后获取用户信息,判断是否勾选登陆成功,勾选了就将用户的密码等信息放入到作用域中;设置7天免登录;

项目学习及其总结(黎明)_第13张图片

CRM如实现后台的CRUD的(以saleChance模块为例)

前台界面:(整个CRM统一采用Ajax模式进行向后台传输数据)

URL 为前台传向后台的接口,CTX为baseController中定义的全局上下文,在加载过程中要先执行ctx
项目学习及其总结(黎明)_第14张图片
项目学习及其总结(黎明)_第15张图片
项目学习及其总结(黎明)_第16张图片

前台的多个删除
项目学习及其总结(黎明)_第17张图片

后台(主要描述多删):

调用多删的sql语句即可

项目学习及其总结(黎明)_第18张图片

授权操作

授权

主要表结构 t_permission,t_user_role,t_module

步骤:

1:授权界面打开
项目学习及其总结(黎明)_第19张图片

2:这个界面的每个可选项都是module表在此之前进行存入的

3:没个id都是固定的,因为这是t_module表中存入的

4:然后在存入时进行遍历存入,将每个mid进行通过module表查询进行存入到集合,集合进行insertBatch进程插入操作;
项目学习及其总结(黎明)_第20张图片

5:角色授权结束

6:用户登录之后会把userId进行存入到隐藏域中,在进入得到主界面的时候就会根据userId进行权限的查询,然后设置到Session会话中;
项目学习及其总结(黎明)_第21张图片

7:前端界面的获取权限的操作,判断是否具有该权限,没有则不执行
项目学习及其总结(黎明)_第22张图片

8:后台的验证,合成注解RequiredPermission()
项目学习及其总结(黎明)_第23张图片

9:解释注解:RequiredPermission()自创注解
项目学习及其总结(黎明)_第24张图片

10:自创注解的使用
项目学习及其总结(黎明)_第25张图片

分页流程

在CRM项目中采用了分页的插件,只需要将Size和PageNum传入即可(如图所示)

项目学习及其总结(黎明)_第26张图片

不引入插件的情况下的分页(详情参考云日记项目代码)

具体传入的参数:PageNum,PageSize,count(总数量)
项目学习及其总结(黎明)_第27张图片


/**
 * Date:2023/4/10
 * Author:黎明
 * Description: 分页工具
 *     根据分页逻辑,进行分页
 */
@Setter
@Getter

public class Page<T> {

    private Integer pageNum; // 当前页  (前台传递的参数;如果前台未传递,则默认第一页)
    private Integer pageSize; // 每页显示的数量 (前台传递或后台设定)
    private long totalCount; // 总记录数 (后台数据库中查询得到;count()函数)

    private Integer totalPages; // 总页数 (总记录数/每页显示的数量;将参数转换为浮点型,执行除法操作,向上取整)
    private Integer prePage; // 上一页  (当前页-1;如果当前页-1小于1,则上一页为1)
    private Integer nextPage; // 下一页  (当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)

    private Integer startNavPage; // 导航开始页 (当前页-5;如果当前页-5小于1,则导航开始页为1,此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数)
    private Integer endNavPage; // 导航结束页 (当前页+4;如果当前页+4大于总页数,则导航结束页为总页数,此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1)

    private List<T> dataList; // 当前页的数据集合 (查询数据库中指定页的数据列表)

 /*
 * 带参构造,通过参数,得到起其他分页的值
 * */

    public Page(Integer pageNum, Integer pageSize, long totalCount) {
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.totalCount = totalCount;
        //总页数 (总记录数/每页显示的数量;将参数转换为浮点型,执行除法操作,向上取整)
        this.totalPages=(int)Math.ceil(totalCount/(pageSize*1.0));
        //上一页,(当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)
        this.nextPage= pageNum-1<1?1:pageNum-1;
        // 下一页  (当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)
        this.nextPage = pageNum + 1 > totalPages ? totalPages : pageNum + 1;

        this.startNavPage = pageNum - 5; // 导航开始页  (当前页-5)
        this.endNavPage = pageNum + 4; // 导航结束页 (当前页+4)

        // 导航开始页 (当前页-5;如果当前页-5小于1,则导航开始页为1,此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数)
        if (this.startNavPage < 1) {
            // 如果当前页-5小于1,则导航开始页为1
            this.startNavPage = 1;
            // 此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数
            this.endNavPage = this.startNavPage + 9 > totalPages ? totalPages : this.startNavPage + 9;
        }
        // 导航结束页 (当前页+4;如果当前页+4大于总页数,则导航结束页为总页数,此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1)
        if (this.endNavPage > totalPages) {
            // 如果当前页+4大于总页数,则导航结束页为总页数
            this.endNavPage = totalPages;
            // 此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1
            this.startNavPage = this.endNavPage - 9 < 1 ? 1 : this.endNavPage - 9;
        }
    }
}

修改添加传到前台的数据差距

​ 将数据转换为实体类进行数据的传输,这样容易进行操作

可能会问到的设计模式

Spring中用到了哪些设计模式

工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。

代理设计模式 : Spring AOP 功能的实现。

单例设计模式 : Spring 中的 Bean 默认都是单例的。

模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。

包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。

适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

nteger totalPages; // 总页数 (总记录数/每页显示的数量;将参数转换为浮点型,执行除法操作,向上取整)
private Integer prePage; // 上一页 (当前页-1;如果当前页-1小于1,则上一页为1)
private Integer nextPage; // 下一页 (当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)

private Integer startNavPage; // 导航开始页 (当前页-5;如果当前页-5小于1,则导航开始页为1,此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数)
private Integer endNavPage; // 导航结束页 (当前页+4;如果当前页+4大于总页数,则导航结束页为总页数,此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1)

private List dataList; // 当前页的数据集合 (查询数据库中指定页的数据列表)

/*

  • 带参构造,通过参数,得到起其他分页的值

  • */

    public Page(Integer pageNum, Integer pageSize, long totalCount) {
    this.pageNum = pageNum;
    this.pageSize = pageSize;
    this.totalCount = totalCount;
    //总页数 (总记录数/每页显示的数量;将参数转换为浮点型,执行除法操作,向上取整)
    this.totalPages=(int)Math.ceil(totalCount/(pageSize*1.0));
    //上一页,(当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)
    this.nextPage= pageNum-1<1?1:pageNum-1;
    // 下一页 (当前页+1;如果当前页+1大于总页数,则下一页为总页数的值)
    this.nextPage = pageNum + 1 > totalPages ? totalPages : pageNum + 1;

     this.startNavPage = pageNum - 5; // 导航开始页  (当前页-5)
     this.endNavPage = pageNum + 4; // 导航结束页 (当前页+4)
    
     // 导航开始页 (当前页-5;如果当前页-5小于1,则导航开始页为1,此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数)
     if (this.startNavPage < 1) {
         // 如果当前页-5小于1,则导航开始页为1
         this.startNavPage = 1;
         // 此时导航结束页为导航开始数+9;如果导航开始数+9大于总页数,则导航结束页为总页数
         this.endNavPage = this.startNavPage + 9 > totalPages ? totalPages : this.startNavPage + 9;
     }
     // 导航结束页 (当前页+4;如果当前页+4大于总页数,则导航结束页为总页数,此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1)
     if (this.endNavPage > totalPages) {
         // 如果当前页+4大于总页数,则导航结束页为总页数
         this.endNavPage = totalPages;
         // 此时导航开始页为导航结束页-9;如果导航结束页-9小于1,则导航开始页为1
         this.startNavPage = this.endNavPage - 9 < 1 ? 1 : this.endNavPage - 9;
     }
    

    }
    }


### 修改添加传到前台的数据差距

​		将数据转换为实体类进行数据的传输,这样容易进行操作



## 可能会问到的设计模式

### Spring中用到了哪些设计模式

工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。

代理设计模式 : Spring AOP 功能的实现。

单例设计模式 : Spring 中的 Bean 默认都是单例的。

模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。

包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。

适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

单例设计模式

分页查询

项目学习及其总结(黎明)_第28张图片

分页的参数

项目学习及其总结(黎明)_第29张图片

多条件删除

项目学习及其总结(黎明)_第30张图片

你可能感兴趣的:(java)