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类型.
在 Java 中,super 和 this 都是关键字,用于引用父类和当前对象。它们的主要区别如下:
综上所述,super 和 this 都是关键字,用于引用父类和当前对象,它们的使用场景不同,但都可以实现类的继承和方法的调用。需要根据具体的场景来选择使用哪一个。
都是List列表的实现类/都是线程不安全的
不同之处:
1.底层不同:
ArrayList 底层是数组,LinkedList 底层是双向链表
2.占内存不同:
LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据.还存储了相邻元素的两个引用
3.效率不同:
ArrayList根据索引查询遍历效率较高,增删效率低. LinkedList查询效率低,增删效率高.
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);
存储过程 :
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()实现数组的扩容
1. 链表法(Separate Chaining):链表法是一种在哈希表的每个位置
存储一个链表的方法,当哈希函数将键映射到同一位置时,就将该键插入到
链表中。在查找键时,只需要遍历该位置的链表即可。如果链表的长度很大,
查找效率就会降低,因此通常会设置一个阈值,当链表长度超过该阈值时,将链
表转换为红黑树(Java 8及以上版本),以提高查找效率。
2. 开放地址法(Open Addressing):开放地址法是一种将冲突的
元素存储在其他位置的方法,而不是在哈希表的同一位置。当哈希函
数将键映射到同一位置时,就将该键存储在哈希表的下一个空位置上。如
果下一个位置也已被占用,就继续寻找下一个空位置。开放地址法有几
种变体,如线性探测、二次探测和双重散列等。
需要注意的是,哈希表的性能取决于哈希函数的好坏和解决冲
突的算法的选择。如果哈希函数的散列分布不均匀或解决冲突
的算法选择不当,可能会导致哈希表的性能下降。因此,在使用哈
希表存储数据时,需要选择合适的哈希函数和解决冲突的算法,以
提高哈希表的性能。
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:增强稳定性与可读性
进程 : 资源分配的最小单位,每个进程都具有自己的代码与数据空间(进程上下文),进程之间的切换资源消耗大
线程 : 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,资源对象
流的分类 :
流向分 : 以程序为中心
输入流
输出流
操作单元 :
字节流 : 万能流
字符流 : 只能操作纯文本文件
功能分 :
节点流 : 真实实现读写的
功能流(包装流|处理流) : 加强节点流的功能,提高节点流的性能
所有的功能流都需要包裹节点流进行使用
都表示字符串,字符序列
是否可变 :
String : 不可变
StringBuilder : 可变
StringBuffer : 可变
线程安全|同步
StringBuilder : 线程不安全|不同步
StringBuffer : 线程安全|同步
执行效率
StringBuilder > StringBuffer > String
字节流和字符流都是Java IO中的基本流类型,二者的主要区别在于处理的数据类型不同。字节流主要用于处理二进制数据,而字符流则主要用于处理文本数据。下面从以下几个方面简单分析一下它们的区别和使用场景:
1. 1. 数据类型:字节流处理的是二进制数据,可以处理任意类型的数据,包括图像、音频、视频等非文本类型数据;字符流则主要用于处理文本数据,因为文本数据是以字符为单位组成的,因此字符流对于处理文本数据更为方便。
2. 输入输出:字节流以字节为单位进行读写,而字符流以字符为单位进行读写。因为在不同的操作系统上,一个字符可能占用不同数量的字节,因此字符流提供了字符集转换的功能,可以将字节转换为字符,也可以将字符转换为字节。
3. 处理效率:字节流的处理效率通常比字符流高,因为在底层操作系统中,文件和网络传输都是以字节为单位进行的,因此字节流更接近底层数据的处理方式,可以提高处理效率。
4. 使用场景:字节流适用于处理二进制数据,例如文件复制、网络传输等操作;而字符流适用于处理文本数据,例如读取和写入文本文件、读取和写入控制台输入输出等。
综上所述,字节流和字符流各有优劣,根据具体的使用场景选择相应的流类型更为合适。通常情况下,如果处理的是非文本类型数据,使用字节流更为合适;如果处理的是文本数据,使用字符流更为方便。如果需要进行字符集转换或者是处理其他特殊情况,也可以使用字符流。在实际应用中,往往需要根据具体的情况选择最适合的流类型,不能一概而论哪个流更好。
序列化(Serialization)指的是将对象转换为字节序列的过程,这样就可以将对象存储到磁盘中或通过网络传输到另一台计算机上。序列化的逆过程称为反序列化(Deserialization),即将字节序列转换为对象的过程。
在 Java 中,实现序列化可以通过实现 Serializable 接口来实现。Serializable 接口是一个标记接口,即该接口没有任何方法,只是作为一个标记来表示该类可以被序列化。实现 Serializable 接口的类需要满足以下要求:
实现序列化的步骤如下:
例如,将一个对象序列化并存储到磁盘中,可以按照以下步骤实现:
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 值,以保证序列化和反序列化的兼容性。
JDK 1.8 是 Java 语言的一个重要版本,引入了许多新增的特性和改进。下面列举了 JDK 1.8 中的一些主要特性:
@FunctionalInterface
,用于标识函数式接口。函数式接口指的是只有一个抽象方法的接口,用于支持 Lambda 表达式的使用。除了以上列举的特性外,JDK 1.8 还包含了许多其他的改进和优化,例如重复注解、类型注解、改进的类型推断、新的 JVM 默认编译器(JVMCI)等。
这些新增的特性和改进使得开发人员在 JDK 1.8 及之后的版本中能够更加方便地进行函数式编程、并发编程和日期时间处理,提高代码的可读性和性能。
Lambda 表达式是 Java 8 引入的一种函数式编程的特性,它提供了一种简洁、灵活的方式来表示匿名函数。Lambda 表达式可以用于替代使用匿名内部类的情况,使得代码更加简洁、易读,并且能够方便地进行函数式编程。
Lambda 表达式的语法如下:
cssCopy code(parameters) -> { body }
其中,parameters 是参数列表,可以为空或包含一个或多个参数。如果只有一个参数,可以省略括号。body 是 Lambda 表达式的主体,可以是单个表达式或一个代码块。如果主体是一个表达式,可以省略大括号。
Lambda 表达式可以用于函数式接口,即只有一个抽象方法的接口。在使用 Lambda 表达式时,需要根据接口定义的抽象方法的参数和返回类型来匹配 Lambda 表达式的参数和主体。
下面是一些示例:
javaCopy code() -> System.out.println("Hello, Lambda!");
javaCopy code(name) -> System.out.println("Hello, " + name + "!");
javaCopy code(x, y) -> {
int sum = x + y;
System.out.println("Sum: " + sum);
}
Lambda 表达式可以与函数式接口一起使用,例如使用 Java 提供的 java.util.function
包中的函数式接口,如 Consumer
、Predicate
、Function
等,以及自定义的函数式接口。
Lambda 表达式使得函数式编程更加方便,可以直接传递行为(即函数)作为参数,简化了代码的编写和维护。它在集合操作、并行编程、事件处理等场景中有广泛的应用,提高了代码的可读性和可维护性
笔记:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iH8bWmBA-1684204326244)(项目学习及其总结.assets/1684144604594.png)]
在子查询较多,而外查询得出的结果较少的时候,又有索引时,我们通常使用exists;
在子查询较少,而外查询较多的时候,我们通常使用in来进行使用;
具体用法: in()外层查询得到的值和in里面进行匹配,有相同的进行输出,不相同不进行输出;
exists()外层的值和内层的值进行匹配,但是当()里面是没有匹配的值时,将得不到任何结果;
还有一种情况,当()里的子查询和外层循环没有任何关联时,得到是全部的外循环,不会有任何影响,这时,我们就要让子查询和外查询建立联系,这样的存在,exists才有意义;
常见的聚合函数:
avg(),求平均值; sum() 求和;
count(),计算和; min()求最小值;
max()求最大值
聚合函数的应用场景:
COUNT:用于统计指定列的行数,可以用于统计表中的记录数或者去重后的记录数
SUM:用于计算指定列的总和,可以用于计算某个时间段内的销售总额等。
AVG:用于计算指定列的平均值,可以用于计算某个时间段内的平均销售额等。
MAX:用于返回指定列的最大值,可以用于查找最高分数或者最大年龄等。
MIN:用于返回指定列的最小值,可以用于查找最低分数或者最小年龄等。
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:范式化的数据库中,每个事实依据只会并只出现一次,而在反范式化数据库
中,信息是冗余的,可能会存储多个地方;
Oracle 默认的隔离级别是 read committed,读已提交。
Oracle 支持上述四种隔离级别中的两种:read committed 和 serializable。除此之外, Oralce 中还定义Read only 和 Read write 隔离级别。
Read only:事务中不能有任何修改数据库中数据的操作语句,是 Serializable 的一个子集。
Read write:它是默认设置,该选项表示在事务中可以有访问语句、修改语句,但不经常使用。
1:加载驱动
2:创建和数据库的连接
3:准备sql语句
4:构造预处理快(一般为预处理块,防止sql注入)
5:发送sql,获取结果
6:处理结果,对结果进行转换处理,并将其返回;
7:连接关闭
四大特性: 1: 原子性:事务中的所有数据要么全部执行,要么全部不执行;
2:一致性:事物完成后,要使数据的状态保持一致;
3:隔离性:指多个并发事务之间的相互隔离程度,不能相互影响;
4:持久性:保证事务对数据库的修改应该持久有效,即使发生系统故障,也不能丢失;
SQL注入的概念:SQL注入是一种将SQL代码添加到输入参数中,传递到SQL服务器解析并执行的一种攻击手法,输入参数未经过过滤,直接拼接到SQL语句中,解析执行,达到预想之外的行为;
JDBC的应对方法:
PrepareStatement接口: 先进行
SQL语句的预编译,再进行传值操作,可以有效避免
SQL`注入问题
正则表达式
字符串的拼接
联系: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适用于需要灵活控制SQL语句和数据库访问的项目,特别是对于复杂的数据库查询操作。MyBatis的适用场景和应用范围非常广泛,包括Web开发、大数据处理、数据分析等领域。以下是MyBatis的一些适用场景和应用范围:
总的来说,MyBatis的适用场景和应用范围非常广泛,可以应用于各种Java项目中,包括大型企业级应用、微服务架构、数据库访问API等
基本文件配置:1:基本内容
2:全局配置文件–指定开发环境和数据库的端口,密码,和数据库名称
3:关联sql数据库的映射文件
4:一般还会有取别名(typeAliases标签)
5:加载日志(log4j)
基本文件配置: 1:基本的映射文件语句;
2:namesapace 命名空间 ,根据增删改查选取不同的标签
3:sql语句—》mapper标签
常见调用:增(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>
参数传递: parameterType=“Dept”,这就是参数传递,将要查询的条件的类型,传入里面;
结果参数:( resultType):
参数类型:基本数据类型,包装类,String,数组,list集合,map集合,javaBean(简单的说就是实体类);
结果参数配置:根据不同的查询结果选用不同的查询方式,以及不同输出结果选用不同的方式
在Oracle中,DDL(数据定义语言)和DML(数据操作语言)是两种不同的SQL语言类型,它们用于不同的操作。
因此,DDL和DML的主要区别在于它们操作的对象不同。DDL用于定义数据库中的对象,而DML用于操作数据库中的数据。此外,DDL语句执行后会隐式提交事务,而DML语句需要显式提交事务才能将修改保存到数据库中。
DDL(Data Definition Language 数据定义语言)用于操作对象和对象的属性 (建表语句)
DML(Data Manipulation Language 数据操控语言)用于操作数据库对象中包含的数据 (sql 增删改查)
DCL :(Data Contorlation Language数据控制语句) 用于操作数据库 事务回滚,控制 等
内连接、左连接和右连接是数据库中常见的三种连接方式,它们之间的区别如下:
事物的特性:
原子性
持久性
一致性
隔离性
事物的隔离级别
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 执⾏结果返回。
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语句
group by
语句的结果没有排序要求,要在语句后面加 order by null
(group 默认会排序)group by
过程用上表的索引,确认方法是explain结果里没有Using temporary
和 `Using filesort尽量使用exits减少使用or
不使用select*
在满足情况的前提下使用inner join 减少使用左右连接
where子句的作用是在对查询结果进行分组前,将不符合where条件的行去掉,即在分组之前过滤数据,条件中不能包含聚组函数,使用where条件显示特定的行。
having子句的作用是筛选满足条件的组,即在分组之后过滤数据,条件中经常包含聚组函数,使用having条件显示特定的组,也可以使用多个分组标准进行分组。
where:
where是一个约束声明,使用where来约束来自数据库的数据;
where是在结果返回之前起作用的;
where中不能使用聚合函数。
having:
having是一个过滤声明;
在查询返回结果集以后,对查询结果进行的过滤操作;
在having中可以使用聚合函数。
where和having的执行顺序:where早于group by早于having。
WHERE和HAVING的主要区别在于它们筛选数据的时机和范围不同。WHERE用于对行数据进行筛选,HAVING用于对聚合数据进行筛选。在使用这两个关键字时,需要根据需要选择合适的关键字,以达到预期的数据过滤和查询效果;
在 SQL 中,DELETE 和 DROP 都是用于操作数据库中的表的关键字,但它们有着不同的作用。
因此,DELETE 和 DROP 的主要区别是删除的范围不同,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]]
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;
}
}
用于返回单条数据或者单个属性值
返回的多条数据,使用L ist来接收
用的最多,单条语句也可以使用list来查询
返回的多条数据,使用Map来接收
有三个参数:
sql语句的地址:命名空间+sql的id
sql语句查询需要的参数(和数据表中的大小写保持一致),否则查不到数据
返回值的键值
注意事项:
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();
JDBC需要手动编写SQL语句,并使用JDBC API来执行SQL语句,而MyBatis使用XML或注解配置SQL语句,并提供了自动映射的功能,可以将SQL语句的结果集映射为Java对象,从而简化了SQL的处理。
在JDBC中,需要手动管理数据库连接的创建、释放以及连接池的使用。而MyBatis封装了JDBC的连接池,提供了简单的配置方式,使得连接的管理变得更加方便。
MyBatis将SQL语句和Java代码分离,使得代码的可维护性更强。开发人员可以在XML或注解中对SQL语句进行管理,而Java代码只需要调用MyBatis提供的API来执行SQL语句,从而使得代码更加清晰、易于维护。
MyBatis提供了一系列的性能优化机制,例如缓存、批处理、延迟加载等,可以帮助开发人员提升系统的性能。
总的来说,MyBatis是在JDBC基础上进行封装和扩展的框架,它将Java对象和SQL语句进行映射,提供了更加简单、方便、高效的数据访问方式。
$.ajax({
type:"get/post",
url:"url",
data:{
uname:"zhangsan",
password:"123456"
},
async:true,
success:function(data){
console.log(data);
}
})
type:请求方式GET/POST
url:请求地址url
async:是否异步,默认是true表示异步
data:发送到服务器的数据
dataType:预期服务器返回的数据类型
contentType:设置请求头
success:请求成功时调用此函数
error:请求失败时调用此函数
1.同步中⼀个线程要等上⼀个线程执⾏完才能开始执⾏,⽽异步中⼀个线程在执⾏中,下⼀个线
程不⽤等它执⾏完就可以开始执⾏
2.同步是单线程,异步是多线程操作
3.异步的效率要⾼于同步
1.get请求时参数会跟在浏览器地址栏的后面,而post请求不会(post请求会将数据存放在请求体中)
2.get请求相对于post而言,不那么安全
3.get请求传递的数据长度是有限的,而post请求基本没有限制(长度与服务器相关)
4.get请求比post请求快(2倍左右)
5.get请求有缓存(会将数据存放在浏览器中,即本地磁盘中),而post请求无缓存
表单提交:通过HTML表单元素来提交数据,常见的提交方式有GET和POST。表单中的元素包括文本框、下拉框、单选框、多选框等,用户在填写表单后,点击提交按钮即可向服务器提交表单数据。
AJAX请求:使用JavaScript发起异步HTTP请求,可以在不刷新页面的情况下向后端发送请求和接收响应。通过AJAX请求可以向后端提交各种数据格式,如JSON、XML等,同时也可以在请求中添加请求头信息,实现更加灵活的请求和响应处理。
超链接提交:
phpCopy code点击跳转
在这个例子中,超链接会向 “/user” 路径发送一个GET请求,同时将参数 name 和 age 的值分别设置为 “test” 和 “20”,后端可以通过 HttpServletRequest 对象的 getParameter() 方法来获取这些参数的值。
使用POST请求方式:虽然超链接本身不支持POST请求方式,但是可以通过JS来模拟POST请求,实现将数据提交到后端。具体实现方式如下:
例如:
phpCopy code点击提交
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也有一些限制,例如无法直接操作客户端的Web浏览器,无法与客户端直接交互,必须依赖于HTTP协议进行通信等。
支持客户/服务器模式。
简单快速
灵活:HTTP 允许传输任意类型的数据对象
无连接:无连接是表示每次连接只处理一个请求
无状态:HTTP 协议是无状态协议
Cookie 和 Session都是⽤来跟踪浏览器⽤户身份的会话⽅式,但是两者的应⽤场景不太⼀样。 维基百科是这样定义 Cookie 的:Cookies是某些⽹站为了辨别⽤户身份⽽储存在⽤户本地终端上 的数据(通常经过加密)。
简单来说: Cookie 存放在客户端,⼀般⽤来保存⽤户信
Cookie和Session都是Web应用程序中存储用户数据的机制,它们有一些不同之处。
1:Cookie是在客户端存储数据的机制,而Session是在服务器端存储数据的机制。
2:Cookie是将数据存储在浏览器中,每次请求都会将Cookie发送到服务器端,而Session是将数据存储在服务器端,每次请求只需要发送Session的ID即可。
3:Cookie可以设置过期时间,而Session默认是在用户关闭浏览器后过期。
4:Session的的客户器端底层原理就是cookie;
Servlet的三大域指的是:应用程序域(ServletContext)、会话域(HttpSession)和请求域(HttpServletRequest)。
这些作用域的使用范围和生命周期不同,可以根据具体的需求来选择使用哪种作用域。
过期失效:当用户长时间没有操作或处于闲置状态时,Session可能会因为超过预设的过期时间而自动失效。这种失效方式是最常见的,可以通过在服务器端配置Session的过期时间来控制,一般为30分钟。
主动失效:当应用程序需要主动销毁Session时,可以调用Session.invalidate()方法,将当前Session标记为无效。这种失效方式通常用于用户退出登录、切换账号等情况下。
设置失效:session.setMaxInactiveInterval(15),主动设置失效时间
d服务器重启失效:当服务器重启或应用程序重新部署时,所有正在运行的Session都会被销毁,这种失效方式是无法避免的。
Session作用域通常在Web应用程序中用于存储和管理用户的会话信息,以便在不同的页面和请求之间共享和访问这些信息。下面是一些使用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,从而提升应用程序的性能和安全性。
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
在类中直接实例化对象的确可以完成对象的创建和使用,但是如果在一个复杂的系统中,类与类之间的关系变得越来越复杂,对象的创建、管理和调用也变得越来越困难。这时候,我们需要一个框架来帮助我们更好地组织和管理对象。
Spring框架就是一个非常流行的Java框架,它提供了依赖注入(Dependency Injection)和面向切面编程(Aspect Oriented Programming)等功能,可以帮助开发者更好地组织和管理对象。其中,依赖注入可以帮助我们实现松耦合,使得不同的组件之间可以更加独立地开发和维护,而面向切面编程则可以帮助我们实现横切关注点的功能,使得我们可以更好地实现日志记录、事务处理、安全控制等功能。
所以,尽管我们可以在类中直接实例化对象,但是引入Spring框架可以帮助我们更好地组织和管理对象,并且提供了更加灵活和强大的功能,使得我们可以更加高效地开发和维护系统。
总的来说,Spring框架是一个非常强大和灵活的Java开发框架,它可以帮助开发人员更容易地构建高质量的企业级应用程序。
Spring框架是一个非常流行的开源Java框架,其主要优点包括:
优点:
缺点:
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)]
控制反转(Inversion of Control,简称IoC)是一种编程思想和设计模式,它的主要思想是将对象的创建和管理工作交给框架来完成,从而降低对象之间的耦合性,提高代码的可维护性和可扩展性。
在Spring框架中,IoC主要体现在依赖注入(Dependency Injection,简称DI)上。DI是指通过构造器、setter方法或者接口注入的方式,将依赖的对象注入到需要使用它的对象中,从而实现对象之间的解耦。通过DI,我们可以将代码中的依赖关系从代码中移除,从而使得代码更加简洁、清晰和易于维护。
IOC的好处不仅仅在于对象的管理和依赖注入,还可以帮助我们实现面向切面编程(Aspect-Oriented Programming,简称AOP),通过对程序的横向切割,将一些通用的功能(如事务管理、日志记录等)从应用程序的核心代码中剥离出来,使得代码更加简洁、可读性更好。
总的来说,IoC是一种很好的编程思想,它能够帮助我们降低代码的耦合性,提高代码的可维护性和可扩展性,使得程序的架构更加清晰、简洁和易于理解。
Spring的生命周期主要包括以下几个阶段:
实例化Bean:Spring容器根据Bean定义创建Bean实例,并通过构造函数或工厂方法进行初始化。
设置Bean属性:Spring容器将配置文件中的属性值或注解中的属性值设置到Bean实例中,完成Bean的属性注入。
调用Bean的初始化方法:如果Bean实现了InitializingBean接口或者配置了init-method方法,则在初始化Bean之后,Spring容器会自动调用Bean的初始化方法。
Bean可以使用:Bean初始化之后,可以被其他Bean或者容器使用。
调用Bean的销毁方法:如果Bean实现了DisposableBean接口或者配置了destroy-method方法,则在关闭容器或者销毁Bean时,Spring容器会自动调用Bean的销毁方法。
销毁Bean:当Spring容器关闭或者销毁Bean时,会将Bean实例销毁。
总的来说,Spring的生命周期可以帮助我们理解Spring容器如何管理Bean的创建、初始化和销毁过程,同时也可以在需要时通过实现InitializingBean和DisposableBean接口或者配置init-method和destroy-method方法来控制Bean的初始化和销毁行为。
面向切面编程(Aspect-Oriented Programming,简称AOP)是一种编程思想和设计模式,它主要用于解决横切关注点(Cross-cutting Concerns)的问题,即那些存在于系统各个层次、各个模块中的通用功能,如日志记录、安全检查、事务管理等。
AOP通过将这些通用功能从应用程序的核心逻辑中分离出来,形成独立的切面(Aspect),然后将切面和核心逻辑进行合并,最终生成一个完整的应用程序。这种方式能够帮助我们实现代码的重用、模块化和可维护性。
在AOP中,我们使用切面(Aspect)来描述通用功能,切面可以定义一个或多个切点(Pointcut),切点定义了在何处应用切面。在切点所指定的方法或者类中,我们可以使用通知(Advice)来描述切面所要执行的行为,通知可以在目标方法执行之前、之后或者抛出异常时执行。
除了切面、切点和通知之外,AOP还涉及到另外两个概念:连接点(Joinpoint)和织入(Weaving)。连接点是指程序执行过程中的一个特定点,如方法调用、异常处理等。织入是指将切面与目标对象的连接点进行结合的过程。
总的来说,AOP是一种很好的编程思想和设计模式,它能够帮助我们解决横切关注点的问题,提高代码的重用性和可维护性。在Spring框架中,AOP是一个重要的特性,它可以帮助我们实现事务管理、日志记录、安全检查等通用功能。
代理模式在 Java 开发中是一种比较常见的设计模式。设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用
JDK动态代理实现接口,Cglib动态代理继承思想
JDK动态代理(目标对象存在接口时)执行效率高于Ciglib
如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理
动态代理
代理类增强委托类的行为可不理解为子类继承父类,对父类功能能进行修改以及对父类进行扩充?两者之间是否有一定的相似之处?
代理类增强委托类的行为可以看作是一种类似于子类继承父类的行为,但是它们之间还存在一些差异。
代理模式中的代理类和委托类之间是一种合作关系,代理类会调用委托类的方法,并在此基础上进行增强,比如添加缓存、记录日志等操作。代理类和委托类之间没有继承关系,代理类只是委托类的一个代理,它不会修改委托类的原有功能,也不会扩充委托类的功能。代理类和委托类实现的接口通常相同,代理类通过实现相同的接口来对委托类的功能进行增强。因此,代理类和委托类之间的关系更像是一种装饰者模式,通过代理类来对委托类的功能进行增强。
相比之下,子类继承父类则是一种典型的面向对象编程的方式。子类会继承父类的属性和方法,并可以对父类的功能进行修改和扩充。子类可以重写父类的方法,也可以添加新的方法和属性,从而实现更加灵活和多样化的功能。子类继承父类的关系是一种"是一种"(is-a)的关系,也就是说,子类是父类的一种特殊类型。
因此,虽然代理类增强委托类的行为和子类继承父类的方式在某些方面有一定的相似之处,但它们之间仍然存在较大的差异。代理模式和装饰者模式主要用于增强对象的功能,而子类继承父类则是一种基于类的代码重用和扩展方式。
静态代理是一种代理模式,它可以在不修改原始对象的情况下,增强原始对象的功能或者控制原始对象的访问。静态代理的优缺点如下:
优点:
缺点:
总体来说,静态代理适用于增强或者控制原始对象的访问,但代理类的数量和代码维护成本较高,同时运行效率也有一定的损失。因此,在实际应用中,需要根据具体的业务需求和系统性能要求,综合考虑选择是否使用静态代理
JDK的动态代理主要使用了反射机制和代理类的生成机制来实现。当调用Proxy.newProxyInstance()方法时,JDK会在运行时动态生成一个代理类,并返回一个实现了指定接口的代理对象。
具体的实现步骤如下:
底层实现中:
JDK会动态生成一个类,该类实现了代理类需要实现的接口,同时继承了Proxy类。这个代理类中包含一个InvocationHandler类型的字段,用于存储代理对象的回调函数。当代理对象调用方法时,JVM会将方法调用转发给InvocationHandler接口的实现类的invoke()方法,从而实现对原方法的代理控制。
相比于CGLIB动态代理,JDK动态代理要求被代理类实现至少一个接口,但它的实现相对简单,同时也更加直观易懂。因此,在需要对已有接口进行代理的情况下,JDK动态代理是一个非常好的选择
CGLIB(Code Generation Library)是一种基于ASM框架的字节码生成库,它可以在运行时生成一个被代理类的子类作为代理类,从而实现动态代理。相对于JDK动态代理只能代理实现了接口的类,CGLIB可以代理没有实现任何接口的类,因此使用场景更加广泛。
CGLIB的动态代理工作步骤如下:
CGLIB的底层实现主要分为三个部分:
静态代理和动态代理是两种不同的代理方式,下面从多个方面来阐述它们的区别:
动态代理的优点包括:
动态代理的缺点包括:
总的来说,动态代理是一种强大的技术,可以提高代码的可维护性和复用性,但也需要在实际应用中根据具体情况进行选择。
两者都是对大量数据进行操作的方式
批处理的优点包括:
批处理的缺点包括:
存储过程的主要优点包括:
但是存储过程也存在一些缺点:
批处理和存储过程都是用来提高数据库操作效率的技术,但它们在实现方式、使用场景和优缺点等方面存在一些差异,具体如下:
虽然我们可以使用第三方日志框架(如Log4j)来记录应用程序的日志,但是在实际应用中,我们通常会需要对某些关键方法或者类进行额外的日志记录,如记录方法的入参、返回值、异常信息等。这时候,如果我们使用传统的日志记录方式,就需要在每个关键方法或者类中都添加日志记录的代码,这样会使代码变得冗余,难以维护。
使用AOP的日志记录方式,可以解决这个问题。在AOP中,我们可以定义一个日志切面,在这个切面中,我们可以定义一个通知,来实现额外的日志记录。然后,我们只需要将这个切面应用到需要记录日志的方法或者类中,就可以自动实现日志记录了,避免了代码的冗余和维护难度。
除了日志记录,AOP还可以应用于其他的通用功能,如安全检查、事务管理等。这些通用功能可以在切面中定义,然后通过将切面应用到需要使用的方法或者类中,来实现通用功能的重用和可维护性的提高。
综上所述,虽然我们可以使用第三方日志框架来记录应用程序的日志,但是使用AOP的日志记录方式,可以帮助我们实现额外的日志记录,同时提高代码的重用性和可维护性。
两者都是实现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:装配顺序不同
…………没写完……建议看畅通分享的资料………………………………………………………………
首先,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指定的视图,并把数据显示到客户端。
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中的方法)之前、之后或渲染视图之前、之后对请求进行拦截和处理。在拦截器中,可以对请求进行预处理、后处理和异常处理等操作,常见的拦截器操作包括:鉴权、日志记录、参数校验、数据加解密等。
拦截器的使用场景:
鉴权:通过拦截器来验证用户是否有权限访问某些特定的请求,例如,需要登录才能访问某些页面,需要管理员权限才能进行某些操作等。
参数校验:通过拦截器来验证请求参数是否符合要求,例如,请求参数不能为空、参数类型必须是指定的类型等。
数据加解密:通过拦截器来对请求或响应中的数据进行加密或解密,确保数据的安全性。
日志记录:通过拦截器来记录请求的详细信息,例如,请求URL、请求参数、请求头信息等,便于后续的排查和分析。
缓存控制:通过拦截器来控制响应结果的缓存,例如,设置响应结果的过期时间,控制缓存是否可以被浏览器缓存等。
总之,拦截器的使用场景非常广泛,可以根据实际需要来进行扩展和应用。
①拦截器是基于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","文件不存在!");
}
}
}
@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 项目,默认都是有 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。
idea 下配置 clean compile package -Dmaven.test.skip=true 执⾏打包命令,target ⽬录得到待部署的项⽬⽂件。
运行:
打开本地 dos 窗⼝,执⾏ java -jar 命令 部署已打好的 jar 包⽂件。
命令如下: java -jar jar包所在⽬录
1:修改pom文件,添加依赖,因为默认的打包方式是jar包,修改为war包
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);
}
}
7:改webapp路径下,直接运行cmd
Ehcache是一个Java开源的分布式缓存框架,它的好处和作用如下:
总之,Ehcache作为一个高性能、可扩展、可配置的缓存框架,可以有效地提高系统性能、降低系统负载,从而提高系统的可用性和可靠性
Spring Boot 中的起步依赖是一种特殊类型的依赖,用于引入一组相关的依赖项,以方便开发人员快速集成某种功能。这些起步依赖通常包括所需的库、配置和默认值,使得开发人员可以在无需复杂配置的情况下快速集成所需的功能。
Spring Boot 提供了很多常用的起步依赖,包括但不限于:
通过使用起步依赖,可以减少开发人员的工作量,提高开发效率,同时也可以确保依赖项的版本兼容性和稳定性。
起步依赖简单地说就是pom依赖!!
Spring Boot 的自动配置是基于条件化配置(Conditional Configuration)实现的。条件化配置是一种基于条件进行配置的机制,通过配置不同的条件来控制某些配置是否生效。
Spring Boot 在启动时会扫描 classpath 下的 META-INF/spring.factories 文件,根据其中配置的各个自动配置类来进行自动配置。每个自动配置类都是一个 @Configuration 类,其中会使用 @Conditional 注解来配置生效的条件。如果当前应用程序的环境满足该条件,自动配置就会生效,否则就会被忽略。
通过自动配置,Spring Boot 可以根据当前环境的不同,自动配置各种不同的组件,例如数据库连接池、Web 服务器、消息队列、缓存等,简化了应用程序的开发配置过程,提高了开发效率。同时,Spring Boot 的自动配置也提供了很好的扩展性,允许用户根据自己的需求自定义配置。
进行 Spring Boot 应用程序的性能调优可以采取以下措施:
RESTful 是一种基于 HTTP 协议的 Web 应用程序开发风格。其核心思想是将资源作为第一类元素进行管理,通过 URL 来标识资源,并通过 HTTP 方法来定义对资源的操作。
RESTful 的开发风格主要有以下几个特点:
总之,RESTful 的开发风格是一种轻量级、灵活的 Web 应用程序开发方式,可以提高系统的可维护性和可扩展性,被广泛应用于 Web 服务、移动应用程序等领域。
官方的定义:
Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后Maven可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件,它可以将项目开发和管理过程抽象成一个项目对象模型(POM)
GPT:Maven 是一个开源的项目管理工具,基于 Java 平台,可以实现项目的构建、依赖管理、版本控制和发布等功能。
Maven 的核心思想是约定优于配置,它通过规定一些默认的目录结构、命名规范和约定的插件等,来减少项目的配置和编写工作,提高开发效率。同时,Maven 还提供了丰富的插件和模板,可以快速构建和发布 Java 项目。
Maven 的主要功能包括:
总的来说,Maven 是一个功能强大、易于使用、开源免费的项目管理工具,可以帮助开发人员快速构建、测试和发布 Java 项目,提高开发效率和项目质量。
我理解的Spring、Spring MVC和Spring Boot之间的关系如下:
因此,可以总结出以下关系:
可以使用Spring MVC开发Web应用程序,而Spring Boot可以进一步简化Spring MVC应用程序的配置和开发,使开发者能够更快地启动和运行应用程序。Spring Boot提供了自动配置、内嵌的Servlet容器、约定优于配置等特性,同时也能够集成其他Spring组件,如Spring MVC、Spring Data、Spring Security等。因此,Spring Boot可以被视为一种简化和加速Spring应用程序开发的工具,而Spring MVC则是在Web应用程序开发中具体负责处理请求和视图的组件。
@Autowired
:这是Spring框架的核心注解之一,用于自动装配(依赖注入)Bean。它可以标记在构造函数、属性、Setter方法或方法参数上,使得Spring容器可以自动解析并注入相应的Bean。
@Resource:与@Autowired注解的使用方式一样,该注解是JDK内置的注解,可以达到依赖注入的效果;
@Controller
:这是Spring MVC框架中用于标记控制器(Controller)类的注解。通过在控制器类上添加@Controller
注解,Spring MVC可以识别该类作为请求处理的组件,并进行相应的请求映射。
@RequestMapping
:这是Spring MVC中用于定义请求映射的注解。可以将它应用于控制器方法上,指定处理请求的URL和HTTP方法。通过@RequestMapping
注解,可以灵活地定义URL路径和请求方法与对应方法的映射关系。
@RestController
:这是Spring MVC中用于标记RESTful风格控制器(Controller)类的注解。与@Controller
注解不同,@RestController
注解将控制器类的所有方法默认视为返回JSON或XML等RESTful风格的响应数据。
@Service
:这是Spring框架中用于标记服务层(Service)类的注解。通过在服务层类上添加@Service
注解,Spring容器可以识别该类作为业务逻辑处理的组件,并进行相应的依赖注入。
@Repository
:这是Spring框架中用于标记数据访问层(Repository)类的注解。通过在数据访问层类上添加@Repository
注解,Spring容器可以识别该类作为数据访问组件,并进行相应的依赖注入和异常转换。
@Configuration
:这是Spring框架中用于定义配置类的注解。通过在配置类上添加@Configuration
注解,可以告诉Spring容器该类是用于配置Bean的,其中可能包含了一些@Bean
方法来定义Bean的创建和配置。
@ComponentScan
:这是Spring框架中用于指定组件扫描路径的注解。可以将它应用于配置类上,指定要扫描的包路径,以便Spring容器能够自动发现和注册相应的组件。
@MapperScan:配置类注解,加在启动类上,再注明dao包的路径,即可省略dao层的@Repository注解;
@ResponseBody
是Spring MVC框架中常用的注解之一,用于指示方法返回的结果直接作为响应体内容,而不是通过视图解析器进行页面渲染。
当一个方法被标记为@ResponseBody
时,Spring MVC将会将方法返回的对象转换为合适的响应体格式,如JSON、XML等,并通过HTTP响应发送给客户端。这使得开发者能够更方便地构建RESTful风格的Web服务,将数据直接以结构化的格式返回给客户端
@RequestBody
是Spring MVC框架中常用的注解之一,用于将请求体内容绑定到方法参数上。当一个方法参数被标记为@RequestBody
时,Spring MVC将会自动将请求体中的数据反序列化为对应的Java对象,并将其作为方法参数传递给方法进行处理。这样可以方便地接收和处理来自客户端的结构化数据,如JSON、XML等
@RequestParam
是Spring MVC框架中常用的注解之一,用于从请求中获取参数值并绑定到方法参数上。
@RequestParam
可以用于方法的参数或方法的参数上,用于指定要获取的请求参数的名称和其他属性。绑定的同时也可以给变量进行初始赋值,例如:@RequestParam(value = “page”, defaultValue = “1”
@PathVariable
是Spring MVC框架中常用的注解之一,用于从请求路径中获取占位符的值并绑定到方法参数上。
在RESTful风格的Web服务中,URL路径常常包含占位符,表示某个变量的值。@PathVariable
可以用于方法的参数或方法的参数上,用于指定要获取的路径变量的名称和其他属性。
@Transactional
是Spring框架中常用的注解之一,用于标记方法或类的事务性行为,@Transactional(propagation = Propagation.REQUIRED),在方法体上增加这个注解用于开启事务。
……………………………………………等等等等,自行补充…………………………………………………
全局异常是指在应用程序中未被捕获的异常,可以通过全局异常处理器进行统一的处理,从而达到对应用程序中所有异常的统一处理。
全局异常处理器通常用于以下几个方面:
统一异常处理:全局异常处理器可以将应用程序中所有未被捕获的异常进行捕获和处理,避免因为某一个异常没有被处理导致整个应用程序崩溃。
2. 友好的错误提示:全局异常处理器可以将错误信息进行包装,使得用户可以看到更加友好的错误提示,而不是一堆错误代码。
3. 日志记录:全局异常处理器可以将异常信息记录到日志中,以方便开发人员进行错误排查和系统分析。
通过全局异常处理器,我们可以对应用程序中的所有异常进行统一的处理,从而提高应用程序的可靠性和可维护性。
登陆成功后获取用户信息,判断是否勾选登陆成功,勾选了就将用户的密码等信息放入到作用域中;设置7天免登录;
前台界面:(整个CRM统一采用Ajax模式进行向后台传输数据)
URL 为前台传向后台的接口,CTX为baseController中定义的全局上下文,在加载过程中要先执行ctx
后台(主要描述多删):
调用多删的sql语句即可
授权
主要表结构 t_permission,t_user_role,t_module
步骤:
2:这个界面的每个可选项都是module表在此之前进行存入的
3:没个id都是固定的,因为这是t_module表中存入的
4:然后在存入时进行遍历存入,将每个mid进行通过module表查询进行存入到集合,集合进行insertBatch进程插入操作;
5:角色授权结束
6:用户登录之后会把userId进行存入到隐藏域中,在进入得到主界面的时候就会根据userId进行权限的查询,然后设置到Session会话中;
7:前端界面的获取权限的操作,判断是否具有该权限,没有则不执行
8:后台的验证,合成注解RequiredPermission()
9:解释注解:RequiredPermission()自创注解
具体传入的参数:PageNum,PageSize,count(总数量)
/**
* 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使用工厂模式通过 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