前言
> 每天的代码学习都是成长的积累,坚持下去,让自己变得更强大! > 坚持学习代码,你就是未来科技的塑造者
第一章 Java SE 基础语法----------------------
1.DOS命令(Windows系统 cmd 进入命令提示符窗口)
D: 进入D盘
cd a 进入a文件夹
dir 查看当前文件夹目录
cd /或\ 返回根目录
cls 清屏
exit 退出命令提示符窗口
2.为什么要配置jdk的path环境变量?
为了在开发java应用程序的时候,能够方便的使用javac和java这些命令
如果不配置的话,就只能在jdk安装路径的Bin目录下使用
3.如何配置jdk的path环境变量?
1)此电脑-右键-属性-高级系统设置-高级-环境变量-系统变量-新建-
变量名设置为:JAVA_HOME
变量值设置为:jdk的安装路径 例如:D:\software\jdk8\Java\jdk-1.8
2)返回 系统变量-点击path-新建
输入 %JAVA_HOME%\bin - 点击确定 (注意环境变量窗口也需要点击确定)
3)测试是否配置成功
在任何目录输入javac命令
4.常量
字符串常量 “hellow”
整数常量 12
小数常量 3.14
字符常量 ‘a’
布尔常量 true false
空值常量: null 不能输出空值常量
5.计算机存储单元
最小存储信息元:位(bit) 又称 比特位
最小存储单元:字节(byte) 用’B’表示
1B(字节)= 8bit
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
6.什么是变量?
1)定义:在程序运行过程中,其值可以发生改变的值
2)定义变量写法:变量名=变量值 a=1
3)常见变量 关键字:
byte 字节
short 短整数
int 整数默认
long 长整数
char 字符
boolean 布尔
float 浮点数 默认情况下为double类型 要为浮点类型 需要在值后面加f 例如 f=3.14f
double 浮点数默认,默认值为0.0
7.什么是标识符?
1)定义:就是给类、方法、变量等起名字的符号。
2)不能以数字开头
3)不能是关键字
4)命名约定
小驼峰法:方法和变量 例如 int firstAge=23
大驼峰命名法: 类 class FirstName{}
8.类型转换
1)自动类型转换: 低类型(短字节)转换成高类型(长字节)
低类型 byte-short-int-long-float-double char-int 高类型
2)强制类型转换: 长转短(一般不推荐使用,会丢失精度)
例如: int j=(int) 99.99 //将double强制转换成int
9.运算符
1)字符串的 + 操作
ASCII码值:
‘A’ 65 A-Z是连续的
‘a’ 97 a-z是连续的
‘0’ 48 0-9是连续的
--1)单个字符的操作
例如:System.out.println(10+‘a’); //输出107
注意:高类型才能接受低类型的
--2)字符串的操作
int n1=10;
System.out.println(“mayikt”+n1); //mayikt10
System.out.println(“mayikt”+n1+10); //mayikt1010
System.out.println(“mayikt”+(n1+10));//mayikt20
System.out.println(n1+10+“mayikt”); //20mayikt1010
2) ++ 或 – (自增、自减运算符)
例子1:
int i=10;
System.out.println(i++); //10 先赋值 再加i+1
System.out.println(++i); //11 先i+1 再赋值
注意:i++ 和 ++i的值不同
例子2:
int i=10;
System.out.println(i–); //10 先赋值 再加i-1
System.out.println(–i); //9 先i-1 再赋值
3)关系运算符
==
!=
>
>=
<
<=
4)逻辑运算符
& 与,并且 两者都要满足
| 或 两者满足其一
^ 异域 两边相同 则 返回false
! 非 取反 !true=false
5)短路逻辑运算符
&& 短路与 作用与& 相同,但是有短路效果
|| 短路或 作用与| 相同,但是有短路效果
10.三元表达式?
关系表达式? 表达式1:表达式2
c=a>b?a:b
11.Scanner打印机
/*
我的第二个java程序
*/
//1.导包
import java.util.Scanner;
public class MyScanner{
public static void main(String[] args){
//2.创建一个打印机
Scanner sc=new Scanner(System.in);
System.out.println("请您输入一个数值类型的数据:");
//3.接受用户在控制台中输入int类型的数据
int i=sc.nextInt();
System.out.println("您输入的数据是:" + i);
}
}
12.流程控制语句分类
1)顺序结构
2)分支结构(if,Switch)
switch: 冗余 穿透
例如:
Scanner sc = new Scanner(System.in);
System.out.println(“请输入今天是星期几”);
int num = sc.nextInt();
switch(num) {
case 1:
System.out.println(“星期”+num);
break;
case 2:
System.out.println(“星期”+num);
break;
}
3)循环结构(for,while,do…while)
循环语句:重复做某件事情,具有明确的开始和停止标志
for循环:
// 输出5-1整数
for(int i=5;i>0;i–) {
System.out.println(i);
}
while循环:
//先初始化
int j=1;
//
while(j<=5){
System.out.println("我是贺丽瑾");
//控制条件语句
j++;
}
do {} while(); 循环语句:
先执行循环体语句 再执行条件判断语句
注意:while()后面一定要加分号,否则会报错
13.死循环语句
for(;😉 {循环体}
while(true) {循环体}
do {循环体} while(true);
(尽量控制次数,不然会让CPU飙到100%)
14.三种不同循环的区别
1)for循环中的i只能在结构体中使用
15.跳转控制语句
1) break;
结束了整个循环
2) continue;
结束本次循环,还会执行下次循环
16.Random随机数
作用:用于产生一个随机数
// 创建一个Random
Random rd=new Random();
int num=rd.nextInt(10); // 获取随机数0-9,不包括10
int num=rd.nextInt(10)+1; // 获取随机数1-10,包括10
// 输出随机数
System.out.println(num);
17.成员变量
在类中 方法外 定义的变量
成员方法:在类中的方法
堆内存中
生命周期:随着对象的存在消失而存在消失
有默认初始值 int默认0 , double 0.0 , String null
18.局部变量
在类中 方法内 定义的变量
在同一个方法内不允许存在多个相同的变量名
栈内存中
生命周期:随着方法的存在消失而存在消失
没有初始值 必须先赋值,才能使用
注意:在方法中传递的参数(形参)也属于方法的局部变量
19.访问修饰符 private
private 是一个权限修饰符
可以修饰成员变量和成员方法
被其修饰的成员只能在本类中被访问,保护成员不被其它类使用
get变量名()方法: 用于获取成员变量的值,方法用public修饰
set变量名()方法: 用于设置成员 变量的值,方法用public修饰
20.定义方法名称
遵循驼峰命名,第一个单词小写,第二个单词大写
例如:setAge() {}
21.this关键字
this代表所在的类对象的引用
方法被哪个对象调用,this就代表哪个对象
this使用细节:
1)this关键字可以使用来访问本类的属性、方法、构造函数
2)可用于区分当前类的属性和局部变量
3)访问成员方法的语法:this.方法名(参数表)
4)访问构造器语法:this(参数表)
注意只能在构造器中访问另一个构造函数,而且必须写在代码的第一行
5)只能在定义的类中使用
22.构造方法
构造方法的方法的名称必须与类的名称一致
一般情况下修饰符是public
构造方法的创建:
1)如果没有定义构造方法,系统将给出一个默认的无参构造方法
2)如果定义了构造方法,系统将不再提供默认的构造方法,如果需要默认的就需要自己定义一个
3)方法重载:构造方法相同但是参数列表不同
23.面向对象的三个基本特征:封装、继承、多态
24.什么是封装?
1)定义:封装就是把客观事物封装成抽象的类并且类可以吧自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
2)封装的规则:
将类的某些信息隐藏在类的内部,不允许外部的程序直接访问
通过该类提供的方法来实现对隐藏信息的操作和访问
3)封装的实现:
1.修改属性私有(设置为private)
2.创建getter/setter方法(设为public用于属性的读写)
3.在getter/setter方法中加入属性控制语句(对属性的合法性进行判断)
25.什么是继承?
1)定义:是面向对象的三大特性之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义、追加属性和方法
2)继承的语法格式:
public class 子类名 extends 父类名{}
3)父类:也被称为基类、超类
子类:也被称为派生类
范例: public class People extends Parent{}
4)优缺点
1.好处:可复用性,提高代码维护性
2.弊端:
继承让类与类之间产生了关系,类的耦合性也增加了
当父类发生变化的时候,子类也不得不跟着变化,削弱了子类的独立性
5)注意事项
1.java中只支持单继承,不支持多继承(一个子类不能有两个父类)
2.java中类支持多层继承
26.继承中 在子类中访问一个变量的查找顺序?
局部变量--子类成员属性--父类成员属性--再没有就报错了
方法的访问顺序也是一样,由近到远,父类中没有就报错
super.父类方法名() 可以调用到父类中的方法 写在第一句
27.继承中构造方法的访问特点?
1)子类中所有的构造方法都会默认访问父类中的无参构造方法
2)子类初始化之前,需要对父类初始化
3)每个子类构造方法的第一句默认都是 super() ,只是有时候没显示出来
4)如果父类中没有无参构造方法,只有带参构造方法,
执行时会报错,解决办法如下:
方法一:通过super关键字调用父类的有参构造方法。 例如:super(参数)
方法二:在父类中定义一个无参构造方法。
28.方法的访问顺序?
子类--父类 由近到远,父类中没有就报错
super.父类方法名() 可以调用到父类中的方法
29.方法重写
当子类需要父类的功能,而子类又有自己独特的内容,
就可以重写父类中的方法,
这样几延续了父类的功能,又定义了自己的特有内容。
重写的方法名和传参不能变
@Override 是一个注解,可以帮我们检查重写方法声明的正确性,检查子类中的方法是否可以重写父类中的方法
注意事项:
1)私有的方法不能被重写
2)子类方法访问权限不能比父类低(public >protected >默认的 >私有的)
30.权限修饰符
1)private 私有 只能在本类中访问
2)default 默认(不用写) 在同一个类、同一个包下都可以访问
3)protected 受保护的 在同一个类、同一个包下都可以访问,在子类中也可以跨包访问
4)public 公开 在任何类中都可以访问到
注意:权限修饰符可以使用在方法、属性、类
private 使用最多,再是 public
31.包
1)就是文件
2)作用:对类进行分类管理
3)定义格式:
package 包名 (多级包用.分开)
例如:package com.project1.day1
4)注意:
在相同包下类名称不允许重复,在不同包下可以重复。
5)如果直接在src目录下创建类,是没有包的名称的
自动建包: javac -d . 文件名
6)import (导入包)
使用不同包下的类时,使用的时候需要写类的全路径
为了简化带包的操作,可以使用import
导包的语法:
import com.project1.m1.类名
import com.project1.m1.*
32.状态修饰符
1)final(最终态) 修饰成员方法、成员变量、类
2)修饰变量:该变量的值 不能被修改,必须初始化(手动赋值)
3)修饰方法:该方法 不能被重写
4)修饰类:该类,不能被继承
5)final修饰的局部变量中:
1)基本数据类型:final修饰的局部变量的值不能被修改
2)引用数据类型:final修饰的引用数据类型地址不能发生改变,但是引用类型的地址里面的成员属性值是可以改变的。
33.static
1)解释:静态,可以修饰成员方法、成员变量
2)修饰的特点:被类的所有对象共享访问;
用static修饰定义的成员变量可以通过 类名 访问;
3)访问特点:
非静态成员方法可以访问 静态和非静态额的成员方法、成员变量
静态成员方法 只能访问静态成员变量、成员方法
4)应用场景:
通过static修饰的成员方法或属性,可以直接通过类名来访问
语法:类名.方法() 类名.属性
34.常量
1)概念:定义好之后,值不能被修改(final)
2)常量名称全部都是大写
idea 中变成常量(大写)的快捷键:Ctrl+shift+u 鼠标光标放在常量名称上
35.main方法
public static void main(String[] args)
1)main 方法是java虚拟机在调用
2)Java虚拟机需要调用类的main方法,所以main方法的修饰符必须是public,使外部类能调用
3)虚拟机直接能用 类名.main() 调用,所以方法必须是 static
4)main方法接受String类型的数组参数,该数组中
5)main 方法可以直接访问本类的 静态 成员,但是访问本类的非静态成员就要先创建本类的对象
36.多态
1.概念:同一个对象,在不同的时刻 表现出不同的形态
2.多态的前提条件:
1)有继承或者实现的关系 (如: 狗 动物
2)有方法的重写
3)有父类的引用指向子类
3.多态的访问特点
1)访问成员属性时,编译阶段看左边,执行阶段也看左边
2)成员方法,编译阶段看左边,执行是看右边的
4.多态的优缺点
优点:提高了程序的扩展性
定义方法时使用父类型作为参数,将来在使用的时候,使用具体的子类型作为参数操作
弊端:不能使用子类型的特有功能
5.多态中的转型
1)向上转型(多态机制)
从子到父,父类引用指向子类对象
AnimalParent animal = new Dog();
2)向下转型(强转)
从父到子,父类引用转为子类对象
Dog dog1 = (Dog) animal;
37.抽象类
1)概念:一个没有方法体的方法应该定义为抽象方法,类中如果有抽象方法,该类必须定义为抽象类。
2)语法: abstract class 类名{}
public abstract void eat(); //抽象方法中没有方法体
3)抽象类的特点:(重点)
1.抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
2.抽象类不能被实例化
3.抽象类由具体的子类进行实例化
4.在抽象类中可以定义非抽象方法
5.子类必须对父类中所有抽象方法进行重写
6.子类如果不重写抽象类中的抽象方法,则该子类必须定义为抽象类
7.抽象类中可以有构造方法,用于子类访问父类时的数据初始化
38.abstract 关键字不能和哪些关键字共存?
1)private abstract的方法必须被子类重写,而private不能被继承
2)final final修饰的方法、变量都不能被修改,而abstract的方法必须被子类重写
3)static static是静态的,而abstract方法中无方法体,无法被调用
39.接口特点
1)概念:接口就是一种公共的规范标准,只要符合规范标准,大家都可以用。java中的接口更多体现在对行为的抽象
2)语法: 用关键字 interface 修饰
public interface 接口名{}
3)实现类接口用 implements 表示
public class 类 implements 接口{}
4)接口不能被实例化
接口不能直接被实例化,和抽象类相像,需要通过 实现类 对象实例化,这叫接口多态。
多态守卫形式:具体类多态、抽象类多态、接口多态
5)多态的前提:
有继承或者实现关系;
有方法重写;
有父(类/接口引用)指向(子、实现)类对象。
6)接口的实现类,要么重新接口中所有的抽象方法、要么是抽象类
40.接口成员特点
1)成员变量只能是常量
默认修饰符 public static final (不需要写出来)
2)成员方法只能是抽象方法
默认修饰符 public abstract (不需要写出来)
注意:JDK8开始 是可以在接口中定义非抽象方法的,但是需要加default关键字修饰!
3)接口没有构造方法
因为接口主要对行为进行抽象,没有具体存在。
一个类如果没有父类,默认继承自Object类。
41.接口和类的区别?
1)类和类的继承关系
只能单继承,但是可以多层继承
2)类和接口的实现关系
可以单实现,也可以多实现,还可以继承一个类的同时实现多个接口
3)接口和接口继承关系
可以单继承,也可以多继承
42.内部类
1)概念:在一个类中定义一个类,这个类就是内部类。
2)语法:
public class 外部类名{
public class 内部类名{}
}
3)访问特点
内部类可以直接访问外部类的成员,包括私有的
外部类要想访问内部类的成员,必须创建内部类对象
4)内部类的分类
成员内部类、静态内部类、方法内部类、匿名内部类
或者分为:成员内部类(成员内部类、静态内部类)、局部内部类(方法内部类、匿名内部类)
43.成员内部类 外界对象如何使用?
1)语法:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
例如: MayiktA.MayiktB mayikt1 = new MayiktA().new MayiktB();
2)注意:实际开发中的内部类一般是不能让外界直接访问的,定义内部类时需要用 private 修饰,保证数据的安全性
44.静态内部类
有 static 关键字修饰的内部类为静态内部类。
静态内部类访问 外部的 成员变量或方法 也必须是静态的。
语法:MayiktA.MayiktB mayikt1=new MayiktA.MayiktB();
55.局部内部类
是在方法中定义的类,所有外界无法直接使用,需要在方法内部创建对象再使用。
该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
56.匿名内部类
本质:是一个继承了该类(子类)或者实现了该接口的(实现类)匿名对象。
第二章 多线程篇-----------------------
1.什么是进程?
进程是资源分配的最小单位。
cpu从硬盘中读取一段程序到内存中,该执行程序的实例就叫进程。
一个程序如果被cpu多次读取到内存中,则变成多个独立的进程。
2.什么是线程?
是程序执行的最小单位,在一个进程中可以有多个不同线程同时执行
3.使用多线程一定能提高效率吗?
不一定,需要了解cpu调度的算法
4.多线程的创建方式?
1)继承 Thread 类创建线程
public class Thread01 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"<我是子线程>");
try{
//当前线程阻塞3s时间
Thread.sleep(3000);
}catch (Exception e){}
System.out.println("子线程阻塞完毕!");
}
public static void main(String[] args) {
//启用线程调用 start方法 而不是 run方法
System.out.println(Thread.currentThread().getName());
new Thread01().start();
new Thread01().start();
System.out.println("主线程执行完毕");
}
}
2)实现 Runnable 接口创建线程
public class ThreadRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"<我是子线程>");
}
public static void main(String[] args) {
System.out.println("<正在运行主线程>");
new Thread(() -> System.out.println(Thread.currentThread().getName()+"<我是子线程>")).start();
}
}
3)使用匿名内部函数的形式创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"<我是子线程>");
}
}).start();
4)使用lambda表达形式创建线程
new Thread(() -> System.out.println(Thread.currentThread().getName()+"<我是子线程>")).start();
5)使用 Callable 和 Future 创建线程
Callable 和 Future 线程可以获取到返回结果,底层是基于LockSupport实现的
public class ThreadCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
try {
Thread.sleep(3000);
}catch (Exception e) {
}
System.out.println(Thread.currentThread().getName()+"返回1");
return 1;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadCallable threadCallable = new ThreadCallable();
FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(threadCallable);
new Thread(integerFutureTask).start();
Integer result= integerFutureTask.get();
System.out.println(Thread.currentThread().getName()+","+result);
}
}
6)使用线程池 例如 Executor 创建线程(使用最多)★★★
面试 底层原理:使用复用机制统一去管理线程
public class Thread03 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> System.out.println(Thread.currentThread().getName()+"<我是子线程>"));
}
}
7)spring @Async 异步注解 建议结合线程池
public class AsyncDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> System.out.println(Thread.currentThread().getName()+"返回1"));
executorService.execute(() -> System.out.println(Thread.currentThread().getName()+"返回3"));
executorService.execute(() -> System.out.println(Thread.currentThread().getName()+"返回2"));
}
}
5.多线程面试题
1)线程如何实现同步?(就是 线程如何保证线程安全性问题)
1.使用 synchronized 锁,JDK1.6开始 锁的升级过程:偏向锁-轻量级锁-重量级锁
2.使用 Lock 锁(JUC),需要自己实现锁的升级过程。底层是基于aqs+cas实现的
3.使用 Threadlocal,需要注意内存泄漏的问题
4.原子类 CAS 非阻塞式
6.synchronized 锁的基本用法?
1)修饰代码块,指定加锁对象,对给定对象家锁,进入同步代码块前要获得 给定对象的锁
语法:synchronized(对象锁) {
需要保证线程安全的代码块
}
2)修饰实例的方法,作用于当前实例加锁,进入同步代码前要获得 当前实例 的锁
使用的是this锁,使用更多,方便使用
3)修饰静态方法,作用于当前类对象(当前类.class)加锁,进入同步代码前要获得 当前类对象 的锁
public synchronized static void cal() {}
相当于在静态类中写下面代码:
synchronized(当前类.class) {
需要保证线程安全的代码块
}
7.synchronized 死锁问题
在使用synchronized 需要注意 synchronized锁嵌套的问题 避免死锁的问题发生。
线程1 先获取到自定义对象的lock锁,进入到a方法需要获取this锁
线程2 先获取this锁, 进入到b方法需要自定义对象的lock锁
8.如何诊断 synchronized 死锁问题?
运行 JDK安装路径下bin文件里面的 jconsole.exe
9.使用 wait 和 notify 注意事项
锁名.wait() 释放锁资源,同时当前线程会阻塞
锁名.notify() 唤醒锁资源,当前线程继续执行
两者都需要 放到 synchronized 同步代码中使用。
10.字节码角度 分析线程安全问题
1)字节码
9: getstatic 获取静态变量sum
12: iconst_1 准备一个常量1
13: iadd 自增
14: putstatic 将修改后的值存入静态变量sum
概述原因:
共享变量值为 sum=0
假设现在cpu执行到t1线程,t1线程执行到第13行,就切换到t2线程执行
t2线程将静态变量sum=0改成了sum=1, cpu又切换到t1线程,继续执行第14行代码,此时就又给sum赋值变成了1,这样两次执行后,sum最终值还是1
2)上下文切换
3)Jmm java内存
12.Callable 和 FutureTask 原理分析
1)写一个线程,实现Callable接口
第三章 java集合框架-----------------------
1.集合特点
集合类特点:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变。
2.集合框架组成
1)Collection 存放单列数据
List接口 存放数据可以允许重复的
Arrarylist 底层是基于数组数据结构实现的;
Linkedlist 底层是基于链表数据结构实现的。
Set接口 不允许存入重复数据 Set集合对数据做去重
HashSet 不允许存入重复数据,底层是基于Map集合实现。
2)Map 存入队列数据 key value
HashMap 底层是基于 数组+链表 实现(JDK1.7)
(JDK1.8)基于 数组+链表+红黑树
HashTable
3.Arrarylist类方法
Arrarylist.get(index) 获取value
Arrarylist.set(index, “修改后的value”) 修改value
Arrarylist.remove(index) 删除 Arrarylist.remove(Object o)
4.退出系统的实现方式
1)定义全局静态成员变量 用布尔参数控制while循环系统
2)直接 return
5.Arrarylist
保证数据存入的有序性
默认只能装10个元素
可以存入重复数据
6.Collection 集合的遍历
概述:Iterator(迭代器),集合的专用遍历方式
1)Iterator iterator = collection.iterator();
//返回迭代的下一个元素,没有下一个就会报错
System.out.println(iterator.next());
2)iterator.hasNext() 返回的是一个布尔值, 如果仍有元素可以迭代,则返回true
3)E next() 返回迭代的下一个元素
7.list接口 独有方法
List list = new Arrarylist<>();
list.add(index, element) --在该集合的指定位置 插入指定的元素
list.remove(index) --删除
list.set(index, “修改后的value”) --修改
list.get(index) --返回指定索引处的元素
8.ListIterastor 列表迭代器
1)概念:List集合特有的迭代器
该迭代器继承了Iterator迭代器,可以直接使用hasNext()和next() 方法
2)特有功能
previous() --获取上一个元素
hasPrevious() --判断有没有上一个元素
3)注意:ListIterastor 可以逆向遍历list,但是前提是先正向遍历,然后才逆向遍历。一般情况下,不使用
9.增强for循环
1)概述:也称 for each 循环,是JDK1.5以后出来的一个高级for循环,专门用来遍历数组集合的。
2)内部原理:其实是iterator迭代器,所以在遍历的时候,不能对集合中的元素进行增删操作。
3)基本语法
for (元素类型 元素名(自定义): 集合名或数组名) {
访问元素
}
10.三种不同方法遍历集合
1)传统for循环
2)iterator迭代器遍历
3)增强for循环(底层原理是 迭代器
11.泛型
1)java泛型(generics)是JDK5中引入的一种新特性,它为编译时类型提供了安全检测机制,该机制允许程序员在编译时检测到非法的类型。
2)早期的时候使用的是Object来代替任意类型,向下转型时无法保证安全性
3)泛型的本质是 参数化类型,也就是说被操作的数据类型被指定为一个参数。
4)类型参数只能代表引用型类型,不能是原始类型(如int,double,char)
5)泛型可以使用在方法/接口/类,分别称为:泛型类、泛型方法、泛型接口
6)底层原理:
泛型是在编译阶段限制传递的类型 在运行阶段都是擦除(去除泛型)
在文件运行里面没有泛型
12.泛型类
1)格式
public class Student{}
// 此处T可以随便写为任意标识, T\E\K\V等形式的参数常用于表示泛型
13.泛型方法
格式:
public T show(T t) {
return t;
}
或
public <T> void show(T t) {
}
14.泛型接口
1)格式:
public interface Student {}
2)泛型接口中的方法 (注意★★★)
void show(T t); //没有返回值 T是方法泛型
T show(T t); //T是方法泛型
T show(T t, M m); //M是方法的泛型 T是接口的泛型
15.类型通配符
1)类型通配符<?> 一般用于 接收使用,不能够做添加。
public static void printList(List<?> list) {}
2)List<?> 表示元素类型未知的list,它的元素可以匹配任何类型。
3)带通配符的list仅表示它是各种泛型List的父类,并不能把元素添加到其中。
4)类型通配符上下限
1.上限 表示的类型是继承的类或者该类的子类
语法:public static void printList(List<? extends Parent> list) {
执行代码
}
2.下限 表示的类型是该类或者该类以上的类,如父类
语法:public static void printList1(List<? super Parent> list) {
执行代码
}
16.可变参数
1)又称参数个数可变,用作方法的形参出现,那么这个方法的参数就是可以变化的
2)代码格式:
public static int sum2(int c, int... a) {}
3)注意事项
这里的可变参数变量其实是一个数组;
如果一个方法有多个参数,包含可变参数,可变参数要放到最后。
17.Arrays.asList()
1)这个 asList() 方法用到的原理就是可变参数
利用可变参数,返回一个新的 ArrayList
2)但是这个方法使用之后不能再对 数组做任何形式的修改了
18.ArrayList 底层实现原理(★★★
1)底层基于数组实现,根据index下标查询效率非常高,时间复杂度为o(1),如果根据元素值查询,时间复杂度为 o(n),效率非常的低
2)新增操作: 底层是基于数组实现扩容的, 如果底层数组容量不够的情况下,就会触发动态扩容机制,效率非常低
3)删除操作:执行删除后会将后面的元素向前移动一位,效率也非常低
4)修改操作:
根据index下标位置修改,效率非常高---时间复杂度为 o(1)
根据元素的值修改,元素非常低---时间复杂度为 o(n)
5)时间复杂度:
o(1) 基于数组的下标 index 查询,只需要查询一次
o(n) 从头部查询到尾部,例如:根据元素查询,需要遍历数组,从头到尾部,直到查询到为止
6)扩容机制
arrayList 底层基于数组实现,默认长度为10,超出长度后触发扩容机制,扩容长度是原长度的1.5倍,然后重新计算长度,将原数组长度合并到新的数组上。
19.ArrayList 和 Vector 的区别
相同点:
1)默认初始化容量都为10
2)底层都是基于数组实现的
3)都是List接口下的子类
不同点
1)ArrayList 线程不安全, Vector 线程是安全的
2)ArrayList 每次扩容是原来容量的1.5倍
3)Vector 每次扩容默认是原来容量的2倍,但是可以设置 每次扩容的容量
4)ArrayList 通过懒加载的形式 初始化容量,Vector直接通过构造函数初始化 数组容量=10
20.数据结构
数组数据结构
1)根据 index 下标查询效率非常高
2)增加、删除效率非常低
链表数据结构
1)增加、删除效率非常高
2)查询效率非常低,时间复杂度是o(n)
3)增加操作不需要扩容,直接找到链表中最后一个节点(尾结点).next=新增节点
21.数据结构链表
1)增加、删除效率非常高,查询效率非常低,时间复杂度是o(n)
2)链表分类:单链表、双链表、环形链表等。
22.Linkedlist 源码解读分析 ★
1)是双向链表实现的List
2)是非线程安全的
3)里面的元素允许为null,允许重复
4)是基于链表实现的,因此插入删除效率高(但是如果使用时根据下标来删除,效率还是低),查找效率低
5)是基于链表实现的,因此不存在容量不足的问题,所有没有扩容的方法
6)还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用
23.size >>1 相当于 size/2 ,但是前者的大于后者的效率
24.Map集合
1)映射键值对的形式 key value
2)Map 集合中 key是不允许重复的,但是value值是可以重复的
如果新增的key存在,就直接修改 value 值
3)Map集合是散列存放数据,所以存在散列的问题,就是遍历数据与存储顺序不一致
Map集合存储顺序与Map集合遍历的顺序是无序的–散列的。
25.Map 集合中的常用方法
1)V put(K key,V value) --向集合中存放元素
2)V remove(K key,V value) --删除k对应的键值对
3)void clear() --清空集合中所有键值对
4)boolean containsKey(Object key)
--判断集合中是否包含指定的键
5)boolean containsvalue(Object value)
--判断集合中是否包含指定的值
6)boolean isEmpty() --判断集合是否为空
Map集合的获取方法
1)get(K) --根据键获取值
2)keySet() --获取所有键的集合
3)values() --获取所有值的集合
4)entrySet() --获取所有键值对的集合
5)getOrDefault(key, default) --判断有该k对应的值没有,没有就返回默认值
26.HashSet集合
1)是基于HashMap来实现的,是一个不允许有重复元素的集合
2)HasSet 允许有 null 值
3)是无序的,不会记录插入的顺序。
4)是没有Get方法的,所以不能用普通for循环遍历
5)HashSet集合 去除重复数据,是需要重写元素对应的 equals 和hashCode方法的,idea 可以自动生成
这里的 equals方法 重写是在比较对象的成员属性值是否是一样的。
27.HashCode 方法
1)HashCode 方法是属于 Object 父类提供的方法,该方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.HashTable 提供的哈希表。
2)HashCode 的常规协定是:
在 java 应用程序期间,在同一对象上多次调用 hashCode 方法时,必须一致的返回相同的整数。
3)它的存在主要是用于查找的快捷性,如HashTable / HashMap 等,HashCode 是用来在散列数据结构中确认对象的存储地址的。
4)★如果 equals 方法比较相等,则 HashCode 值也一定相等;但是 HashCode 值相等不代表 equals 比较也相等。
5)★如果类中重写了 equals 方法,就必须重写 HashCode方法
28.HashMap 集合常见面试题★★★
1)HashMap 的key 是否可以是自定义对象?
肯定可以
2)HashMap 存储数据是有序还是无序的?
是无序的,底层采用 散列机制 存放数据
3)HashMap 的 key 是否可以存放 null 值?如果可以存放在哪里呢?
index=0
4)HashMap 底层是基于什么实现的?★
基于hash算法实现的,查询效率高,时间复杂度为 o(1)
重要原理代码:
int index = k.hashCode() % entrys.length;
5)什么是 hash 冲突?
例如 a 和 97 的hashCode值相同,他们计算出来的index也相同,就会发生 hash 冲突,解决的办法是 先判断该hashCode 值的位置是否为空,为空就直接新建,不为空就 将 next值 指向新建的,单向链表
6)HashMap 键值 key 是否可以存放自定义对象呢?★
可以的,当我们的HashMap key 存放的是自定义对象,需要重写 equals 和 hashCode 方法
29.Collections 工具类
Collections 是单列集合操作的工具类,Collection单列集合
Collections类的常用方法:
1)Collections.sort(list) --将指定列表按 升序排序
2)Collections.reverse(list) --反转列表
3)Collections.shuffle(list) --随机排列列表
Collections 不可以操作 Map接口下 子类,只能操作单列集合
30.LinkedHashMap集合(有序 双列)★
概述:继承自 HashMap,他的多种操作都是建立在 HashMap操作的基础上的
与 HashMap 的区别:
LinkedHashMap 维护了一个Entry的双向链表,保证了插入 Entry中的顺序。
31.LinkedHashSet集合(不重复 有序 单列)
1)是Set集合的一个实现,具有Set集合不重复的特点
2)与 HashSet 不同的是,它可以保证元素的有序性,也就是遍历顺序和插入顺序是一致的
3)底层是基于LinkedHashMap实现的
第四章 JavaIO流操作--------------------------------
1.File
1)File 类主要是java为文件相关的操作(删除、新增等)设计的相关类
2)File类的包名是java.io,其实现了Serializable/Comparable两大接口,以便于其对象可序列化和比较
File类 是 java io 包下的
IO 对磁盘中的 文件 操作(增、删、改、查)
3)File类 方法
File(String Pathname) --通过给定的 路径名字符串 来创建新的 File 实例
File(String parent, String child)
--从父路径名字符串和子路径名字符串 创建新的File 实例
File(File parent, String child)
--从抽象父路径名和子路径名字符串 创建新的File 实例
4)File类 创建功能
public boolean createNewFile() --创建文件
public boolean mkdir() --创建文件夹
public boolean mkdirs() --创建一系列文件夹,必须是父子关系
注意:文件和文件夹的名称都不能重复
5)File类 判断方法
boolean exists() --判断存在该文件
boolean isFile() --判断是一个文件
boolean isDirectory() --判断是一个文件夹
返回都是布尔值
6)File类 获取方法
String getAbsolutePath() --获取绝对路径
String getPath() --获取抽象路径名转化路径名字符串
String getName() --获取文件或文件夹的名称
String[] list() --获取目录下所有内容,返回字符串数组(文件名字符串)
File[] listFiles() --获取目录下所有内容,返回File对象数组(File对象 和绝对路径形式一样)
7)File类 删除方法
boolean delete() --删除文件和文件夹(删除目录前 得先删除目录里面所有得内容 不能够直接删除)
2.绝对路径和相对路径的区别
1)绝对路径:物理硬盘真实存在的路径
2)相对路径:是java工具中要访问的文件相对于当前文件的路径
3.递归算法
自己一直调用自己
1)在使用时,需要注意栈空间的深度
2)减少递归调用深度 通过一定条件退出
3)避免栈空间“栈溢出”
JVM设置参数:-Xss10m (java进阶知识 jvm知识)
少用if else ,多用 if returnf
4.IO流概述
1)IO: 输入(Input读取数据)/输出(Output写数据)
2)流:
一种抽象概念,是对数据传输的总称,
也就是说数据在设备间的传输称为流,
流的本质是数据传输
IO流就是用来解决设备间数据传输问题的
3)常见的应用
文件上传,下载,复制等
4)分类
1.根据数据流向分类
输入流:读数据 将硬盘中的数据读取到内存中
输出流:写数据 将程序中的数据写入到硬盘中
2.按照数据类型来分
字节流:字节输入流/字节输出流
字符流:字符输入流/字符输出流
3.IO应用场景
纯文本文件,优先使用字符流
图片/视频/音频等二进制文件,优先使用字节流
不确定文件类型,优先使用字节流,字节流是万能的流
5.字节流如何写入数据
1)InputStream:抽象类 字数输入流的所有类的超类
2)OutputStream:抽象类 字节输出流的所有类的超类
子类名特点:都是以其父类名作为后缀q的
3)FileOutputStream:文件输出流 用于将数据写入file
4)FileOutputStream(Stringname): 创建文件输出流以指定的文件写入文件
5)输出流步骤:
调用系统功能创建了文件
创建字节流输出对象
字节流输出对象指向文件
6.字节流写入数据常用的三种方式
1)void write(int b) --一次写一个字节
2)void write(byte[] b) --一次写一个字节数组数据
3)void write(byte[] b,int off,int len)
--将len 个字节从指定的off(下标index)位置开始,一次写一个字节数组的部分数据
7.字节流写入数据 换行 追加写
1)换行 fileOutputStream.write("\r".getBytes())
windows:\r \n
linux: \n
mac: \r
2)字节流数据通过 new FileOutputStream("xiaojin.java",true); 表示追加写入数据,如果第二个参数为 true ,则字节流写入文件的末尾
3)通过 str.getBytes() 方法,将字符串转换成字节码
8.字节流写入数据异常处理
1)finally: 在异常处理时,提供finally块来执行所有清除操作,释放资源,如file.close()
2)JVM在正常退出的情况下,也会执行finally中的代码
9.字节流读取数据
1)FileInputStream: 从文件系统中的文件获取输入字节
2)FileInputStream(String name):通过打开与实际文件的连接 来创建一个 FileInputStream ,该文件由文件系统中的路径名 name 命名
3)步骤
创建字节流输入对象
调用字节流输入对象的读取方法
释放资源 close
重要代码示例:
int read;
while ((read=f.read()) != -1) {
System.out.print((char) read);
}
10.字节流复制数据
1)先读取目标文件数据
2)再把数据写入新文件
重要代码示例:
int by;
while ((by=inputStream.read()) != -1) {
outputStream.write(by);
}
11.字节流以数组形式读取数据
1)指代不同* 换行占两个字节
10:在ascii码中指代的是换行符
13:在ascii码中指代的是回车键
2)效果不同
10:是光标重新回到本行开头
13:是光标往下一行(不一定到下一行行首
3)不同系统表现不同
在MAC上,\r就表现为回到本行开头 并往下一行
在WIN系统下,这两个字符就是表现的本意
在UNIX系统类,换行\n就表现为光标下一行 并回到行首
12.字节流复制图片
1.程序读取 图片 到内存中
FileInputStream inputStream = new FileInputStream("C:\\Users\\76220\\Desktop\\111.png");
byte[] bytes = new byte[1024];
Integer len;
2.将该图片写入到新的目录中
FileOutputStream outputStream = new FileOutputStream("C:\\Users\\76220\\Desktop\\vue2\\111.png");
while ((len=inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
3.关闭资源
inputStream.close();
outputStream.close();
13.字节缓冲流
1)传统方式一个字节一个字节读取或者写入,会频繁发生系统内核调用(用户态--内核态切换)效率非常低
2)字节缓冲流,缓存区是一个内存区域的概念,类似于池子以“块”的形式写入或者读取数据,减少系统调用频率
3)分类
BufferedInputStream(InputStream in): 字节缓冲输入流
BufferedOutputStream(InputStream out): 字节缓冲输出流
4)构造函数传递字节对象,不是文件路径,缓冲流提供了一个缓冲区 做了封装以块的形式读取数据,读取数据还是依赖于字节流对象
注意:字节缓冲流的缓冲区大小默认是8k,即8192字节。
14.一个汉字存储占用多少个字节
GBK编码,占用两个字节
UTF-8编码(默认),占用三个字节
代码演示:
String str1 = “王小二”;
System.out.println(Arrays.toString(str1.getBytes(“GBK”)));//[-70, -40, -64, -10, -24, -86]
System.out.println(Arrays.toString(str1.getBytes()));//[-24, -76, -70, -28, -72, -67, -25, -111, -66]
-
编码和解码
1)当字节流读取存储中文数据时 可能会发生乱码,这时候我们可以使用字符流
字符流 = 字节流+编码表 ★
2)编码表:可以看做是一个字典,这本字典翻译的是 人类的字符 和 机器语言(二进制) 之间的对应关系
3)编码表(ASCII):就是 人类生活的字符 和计算机二进制的对照关系表。如 a 97 ★
4)ISO8859-1:
欧洲制定的码表。兼容ASCII
多了一些欧洲的语言 它用一个字节的全部来表示数据
没有未知字符的
5)GB2312:识别数千个中文
6)GBK: 识别两万多中文,两个字节表示一个字符★
7)GB18030: GBK的升级
8)Unicode: 世界计算机协会制定通用码表,两个字节表示一个字符
9)UTF-8: Unicode升级版,汉字基本都是三个字节★
16.字符流
1)Writer 是写入字符流的抽象类
OutputStreamWriter(fileOutputStream):是输出转换流,将要写入流的字符编码成字节, 字符流–字节流
2)Reader 是用于读取字符流的抽象类
InputStreamReader(fileInputStream):是输入转换流,读取字节并将其解码成字符流, 字节流–字符流
17.字符流的5种写入方式
public void write(int c) --写出一个字符
public void write(char[] cbuf) --写出字符数组
public void write(char[] cbuf,int off,int len)
--写出cbuf字符中,从下标为off开始的 len个字符
public void write(String str) --写出字符串
public void write(String str,int off,int len)
--写出str字符串中,从下标为off开始的 len个字符
18.转换流的便捷形式
1)输入字符流 FileReader
new FileReader("C:\\Users\\76220\\Desktop\\折线图定制js.txt");
2)输出字符流 FileWriter
new FileWriter("D:\\xiaojin\\xiaojin02\\折线图定制js.txt");
19.字符缓冲流
1)BufferedWriter:字符缓冲写入流
new BufferedWriter(new FileWriter(“D:\xiaojin\xiaojin02\xiaojin.txt”));
2)BufferedReader:字符缓冲输入流
new BufferedReader(new FileReader(“D:\xiaojin\xiaojin02\xiaojin.txt”));
3)默认缓存大小都为8k,可以手动设置缓存大小
20.字符缓冲流换行操作
1)BufferedWriter.newLine()
void newLine()
写一行 行分隔符,行分割符字符串由系统属性定义
重要代码:
for(int i=0;i<10;i++) {
bufferedWriter.write("xiaojin:"+i);
bufferedWriter.newLine();
}
2)BufferedReader.readLine()
String readLine()
读一行文字,结果包含行的内容的字符串,不包括任何终止字符
如果流的结尾已经达到,则为null
重要代码:
String str;
while ((str=bufferedReader.readLine())!=null) {
System.out.println(str);
}
第五章 JavaSocket网络编程篇------------------------
1.http
1)基本概念:
基于HTTP传输协议(超文本传输协议)客户端与服务器之间数据传输规则。
2)HTTP特点:
基于请求(request)与响应(response)的模型;
底层基于tcp协议封装(javase基础知识中网络编程技术);
无状态协议 javaweb技术(cookie/session技术);
数据传输是同步的过程;
2.HTTP请求格式
请求数据格式(三部分)
1)请求行 请求的第一行
组成:请求方法(get、post) url(/首页) http协议版本(HTTP/1.1)
例如:GET /home HTTP/1.1
2)请求头
第二行开始 键值对格式
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Cookie: BAIDUID=7B97F5560200F616651EBFC83A5056BC:FG=1; PSTM=1708664390; BIDUPSID=68A6957A21DD5301EF6880CA1D73EB97; ab_jid=275d75fe2f35b3bdc69eb0e1bff9f6a302df; ab_jid_BFESS=275d75fe2f35b3bdc69eb0e1bff9f6a302df
Host: miao.baidu.com
Pragma: no-cache
Referer: https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9880507388881399133%22%7D&n_type=-1&p_from=-1
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
3)请求体 post请求的参数
3.get请求和post请求的区别
1.get请求的请求参数在请求行中,没有请求体
2.post请求的请求参数在请求体中
3.get请求的请求参数有大小限制,post请求没有
4.http 响应格式(三部分)
1)响应行
响应数据第一行 如:HTTP/1.1 200 OK
2)响应头
第二行开始 键值对格式
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, Host, Content-Type, x-requested-with, X-Custom-Header
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Allow-Origin: https://mbd.baidu.com
Access-Control-Max-Age: 3600
//服务端发送的压缩数据的长度
Content-Length: 2
//服务端响应客户端内容的类型
Content-Type: application/json; charset=utf-8
//服务端响应客户端的时间
Date: Sat, 23 Mar 2024 04:32:02 GMT
3)响应体 存放服务器响应给客户端的内容
http响应状态码
1.404--客户端发送请求达到服务器端地址填写错了
2.500--服务器端发生了错误
5.网络通讯三要素
1)ip地址 192.1682110.1
ipconfig 命令:获取本机的ip地址
127.0.0.1 电脑自己访问自己
localhost 电脑自己访问自己
192.168.0.106 直接获取到局域网的ip 让别人访问电脑
局域网ip 是192.168开头的
2)端口号 80
3)协议 tcp或udp
6.如何获取ip地址?
通过 InetAddress类
InetAddress类 的常用方法:
InetAddress getByname(String host)
--获取给定主机名的IP地址,host表示指定主机
InetAddress getLocalHost() --获取本地主机地址
String getHostName() --获取本地ip地址的主机名
boolean isReachable(int timeout)
--判断在限定时间指定的ip地址是否可以访问
String getHostAddress() --获取字符串格式的原始ip地址
7.dns 域名解析
1)hostName 域名 主机名称
例如:
baidu.com 百度域名
localhost 是域名 dns解析ip地址
2)为了方便记忆 我们使用域名 再通过dns解析成ip地址
3)可以在 C:\Windows\System32\drivers\etc 中的 HOSTS 文件中配置本地dns域名解析
8.什么是UDP协议?★★★
1)核心特点
面向无连接的
不可靠的协议
安全系数很低 容易丢包
但是传输速度非常快
不需要类似于tcp协议三次握手
使用场景:聊天工具
2)发送数据步骤:
//1.创建发送socket对象
DatagramSocket datagramSocket = new DatagramSocket();
//2.提供数据,并将数据封装到 数据包中
byte[] bytes = "xiaojin".getBytes();
InetAddress inetAddress = InetAddress.getByName("xiaojin.com");
/**
* DatagramPacket(bytes, bytes.length, inetAddress, 8080)
* 参数1 发送的数据 byte数组类型
* 参数2 发送数据的长度
* 参数3 发送到服务器端 ip地址
* 参数4 发送服务器端 端口号码
*/
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, inetAddress, 8080);
//3.通过socket服务的发送功能,将数据包发送出去
datagramSocket.send(datagramPacket);
System.out.println("发送数据成功...");
//4.释放资源
datagramSocket.close();
3)接收数据
1.创建接收端socket对象
2.接收数据
3.解析数据
4.输出数据
5.释放数据
9.TCP协议★★★
1)TCP是面向连接的可靠协议,通过三次握手建立连接,通讯完成时拆除连接
2)需要经过三次握手成功之后,再将数据发送给服务器端
3)TCP建立连接中的名词:
syn 建立连接,ack 确认标志,fina 终止标志
4)三次握手:
第一次握手:客户端会向服务器端发送码为syn=1,随机产生一个seq_number=x的数据包到服务器端(syn)
第二次握手:服务端接收到客户端请求之后,确认ack=x+1,于是就像客户端发送syn(服务器端随机生成数字y)+ack
第三次握手:客户端接收syn+ack,向服务器端发送ack=y+1,此包发送完毕即可建立 tcp连接
★综合案例:E:\project\java\demo\src\java_JavaSocket\day2\Udpclient.java
5)底层是基于IP协议封装 默认端口是80 在浏览器中是不需要写的
10.tcp协议发送数据
* 1.创建发送端Socket对象 (创建连接)---三次握手 确保服务器端能接收数据 才开始传输数据
* 2.获取输出流对象 socket.getOutputStream()
* 3.发送数据 outputStream.write()
* 4.释放资源
11.tcp接收数据
* 1.创建接收端socket对象 serverSocket
* 2.监听(阻塞:如果建立连接失败,程序会一直阻塞,不往下执行)
* 3.获取输入流对象 serverSocket.accept()
* 4.获取数据 accept.getInputStream()
* 5.输出数据
* 6.释放资源
★综合案例:E:\project\java\demo\src\java_JavaSocket\day4\TcpClient.java
第六章 Java反射技术----------------
1.什么是反射技术
1)反射技术可以动态方式获取到class相关信息(class中的成员方法、属性)
2)反射技术可以灵活调用方法 或者给成员赋值
3)Class.forName初始化对象(创建我们的对象)
2.双亲委派机制
1)将java源代码编译成class文件
2)触发类加载器,将class文件读取到内存中(程序中)
3)程序内存中存放class相关信息
注意:class相关信息只会存放一份,但是可以通过该class创建多个对象;程序中需要运用到class类的时候,才加载到内存中
3.反射技术原理
1)class什么时候加载到内存中
程序中需要运用到class类的时候,才加载
不是一开始启动程序就加载class
2)Class对象的由来
将class文件读入内存,并为之创建一个Class对象
4.获取class方式
1.类名.class
2.new 对象,再对象名.getClass()方法
3.Class.forName("类的完整路径地址") --使用最多
类的完整路径地址:包的名称.类的名称组合
5.反射常见的核心API
1)class.forName(“类的路径地址”) --反射类初始化
代码示例:Class<?> aClass = Class.forName(“java_fanshe.UserInfo”);
2)aClass.newInstance() --无参构造方法创建对象
代码示例:
UserInfo userInfo1 =(UserInfo) aClass.newInstance();
System.out.println(userInfo1);
3)获取构造方法
1)getConstructor(参数类型.class) --获取公有的有参构造方法, 创建对象 constructor.newInstance(参数)
2)getDeclaredConstructor() --获取所有的有参构造方法(私有和公有的都可以获取)
注意:setAccessible(true) --在用私有方法属性时,都需要将setAccessible设置为true
4)获取成员变量并调用:
1)批量的
getFields() --获取所有的公有字段
getDeclaredFields() --获取所有字段,包括:公有,私有,默认,受保护的
2)获取单个的
getField(String fieldName) --获取所有的公有字段
getDeclaredField(String fieldName) --获取所有字段,包括:公有,私有,默认,受保护的
5)获取成员方法
批量:
1)getMethods() --获取所有的公有(public)方法,包括父类中的
2)getDeclaredMethods() --获取当前类的所有方法(包括私有)
单个
1)getMethod() --获取所有的公有(public)方法,包括父类中的
2)getDeclaredMethod() --获取当前类的所有方法(包括私有)需要设置权限 setAccessible(true)
第七章 JDK8新特性lambda与stream--------------------------
1.使用Lambda表达式 依赖于函数接口
函数接口特点:
1)在接口中只允许有一个抽象方法 ★
2)在函数接口中可以定义object类中方法(相当于重写)
3)可以使用默认(default)或静态(static)方法 ★
4)@FunctionalInterface 注释,表示该接口为 函数接口 ★
Lambda表达式特点
1)使用使用Lambda表达式,方法体只有一条语句的情况下,不需要写{},也可以不写return ★
2)Lambda表达式可以有参数
3)使用场景:
简化函数
实现集合遍历 forEach()
实现集合排序 sort() forEach() 案例:E:\project\java\demo\src\java_jdk8\UserEntityTest.java
实现线程调用(匿名内部类) 案例: E:\project\java\demo\src\java_jdk8\ThreadLambdaTest.java
2.Stream流
1)解释:非常方便精简的形式实现 数据的过滤、排序等
2)分类
串行流 stream() 单线程
并行流 parallelStream() 多线程
并行流比串行流的效率要高
3)应用场景
1)Stream流将list集合转换成set集合 stream.collect(Collectors.toSet())
重要代码:
Stream stream = userEntities.stream();
//转换成set集合 注意,这里去重必须要重写实例类的equals方法和hashCode方法
Set setUserList = stream.collect(Collectors.toSet());
案例代码:E:\project\java\demo\src\java_jdk8\Stream\Test02.java
2)Stream流将list集合转换成map集合
案例代码:E:\project\java\demo\src\java_jdk8\Stream\Test03.java
3)Stream流计算求和 reduce() BinaryOperator() get()
4)Stream流查找最大的和最小的 stream.max() min() get()
3.Stream Match匹配
stream.anyMatch() --任意一个元素匹配成功,返回true
stream.allMatch() --所有元素匹配成功,返回true
4.Stream Filter过滤器
stream.filter() --返回符合条件的元素
5.Stream流实现limit
stream.skip(n).limit(x) --跳过前面的n个元素 取x个元素
6.Stream流 实现对数据排序
stream.sorted()
7.java 系统内置函数接口
1)消费型接口
Consumer
void accept(T t);
BiConsumer<T, U>
void accept(T t, U u); //增加一种入参类型
2)供给型接口
Supplier
T get();
3)函数型接口
Function<T, R>
R apply(T t);
4)断言型接口
Predicate<T>
boolean test(T t);
8.并行流 提高效率
将一个任务分成几个线程同时执行 提高执行效率
9.方法引入
方法引入 就是 Lambda表达式中直接引入的方法
必须遵循规范:引入方法的 参数列表 和 返回数据类型 必须要和函数接口保持一致
1)静态方法引入
类名::方法名
使用前提:有静态方法和接口
2)实例方法引入
类名::方法名
使用前提:存在接口 存在实例,有实例方法
3)构造函数方法引入
函数接口返回类名::new
使用前提:接口中的函数有返回值
注意 一定要和 函数接口返回类型名 保持一致
4)对象方法引入
对象类名:方法名
使用前提:接口中的函数需要传参, 且参数 是 对象类型
10.JDK8新特性 Optional
判断对象是否为空:
Optional.ofNullable(对象名) --可以允许传递一个空值对象
Optional.of() --不允许传递空值对象(不常用)
isPresent() --返回值为false 值为空
get() --返回对象, 值为null时会报错,返回异常
Optional过滤 与 设置默认值
orElse(default) --如果为空值就设置默认值
过滤 filter() isPresent()判断是否过滤出值,没有返回false
ifPresent() --值不为空,直接执行括号里面的代码
orElseGet() --写函数的接口形式 全局赋默认值 orElse只是传递默认值
结合 map()可以多层遍历返回值,无论判断多少个null ,只用在最后加上 orElse(null)
第八章 maven管理jar包-----------------------
1.项目下载jar包顺序
1)maven本地仓库 适用于在一台电脑上开发项目
2)maven私服仓库 搭建在企业内部或部署在国内的,下载速度快,适用于多人开发项目共享使用
★配置步骤:
1.maven安装文件夹下面 conf settings.xml文件 配置以下代码(大概位置在53行左右)
D:\mavenRespo\helijin
//这里的路径可以自定义,是本地下载jar包的存放地址
2.然后(大概位置在160多行),配置以下代码(配置阿里云私服):
nexus-aliyun
central
Nexus aliyun
http://maven.aliyun.com/nexus/content/groups/public
3)maven中央仓库 部署在国外的,外网,下载速度慢
先查找本地仓库,没有该jar包就到私服去下载,私服没有就去中央仓库查找下载。
2.maven环境的安装
1)下载安装包 https://maven.apache.org/download.cgi
2)解压后 配置环境变量:
我的电脑-属性-高级设置-环境变量
添加 MAVEN_HOME 的变量名为 maven安装路径
进入path 添加%MAVEN_HOME%\bin
3)检查是否安装成功:cmd 输入命令 mvn -version
3.maven项目目录
pom.xml --核心 配置很多内容 如当前项目依赖的jar包
4.maven 命令
mvn clean --清除target目录(编译内容)
mvn compile --编译项目源代码
mvn test --运行测试项目
mvn package --打包文件 并存放到target目录下 打包好的文件通常都是编译后的class文件
mvn install --在本地生成仓库的安装包
第九章 MySQL基础--------------------------------
1.SQL语句
1)是用于访问和处理数据库的标准计算机语言
2)意思是结构化查询语言,全称是 Structured Query Language
3)可以编程,比java简单很多
2.SQL语句特点
1)具有统一性,不同数据库支持的SQL稍有不同
数据库操作任务通常包括:增、删、改、查
常见数据库:mysql,sqlserver,oracle,db2等
2)非过程化语言
3)语言简洁,用户容易接受
4)集合性
对数据进行成组的处理语句都接受集合作为输入,并且返回集合作为输出
3.数据库分类
关系型数据库
1)Oracle数据库 (甲骨文公司)收费的
2)MySQL数据库 (最流行的数据库)免费版本 源代码开源
3)SQLServer 数据库 (微软公司)c# windows
4)Sqlite(嵌入式关系数据库)安卓手机端程序开发
非关系型数据库
1)Redis (缓存数据库)需要持久化存到硬盘
2)Mongodb (文档数据库)
4.sql与数据库的关系
SQL属于数据库 编程语言,数据库有MySQL,Oeacle,SQLServer,DB2
5.MySQL数据库特点
1)是用C和C++语言编写的,可以保证源码的可移植性
2)支持多个操作系统,如Windows Linux MacOS
3)支持多线程,可以充分利用cpu资源
4)为多种编程语言提供api
5)优化了SQL算法,提高了查询速度
6)代码开源 无版权制约,自主性强,使用成本低
7)历史悠久,社区及用户非常活跃,遇到问题可以获取到有效帮助
安装相关:
软件下载地址 https://dev.mysql.com/downloads/file/?id=526084
配套工具navicat下载 https://navicat.com.cn/products/
1)在使用 Navicat for Mysql连接mysql8.0时会报如下错误:
mysql8.0 引入了新特性 caching_sha2_password;
2)错误原因:这种密码加密方式客户端不支持;客户端支持的是mysql_native_password 这种加密方式;
3)解决方法(需要配置环境变量):
1.mysql -u root -p
2.USE mysql;
3.ALTER USER 'root'@'localhost' IDENTIFlED WITH mysql_native_password BY 'root';
4.FLUSH PRIVILEGES:
6.DDL 数据库基本操作
– 注释 ddl-数据库基本操作
– 1.创建数据库 名称xiaojin01 判断不存在 再创建
CREATE DATABASE if not EXISTS xiaojin01
– 2.查询数据库
SHOW DATABASES;
-- 还可以打开 cmd 命令提示符窗口, 执行以下操作查询数据库
mysql -u root -p
输入密码(我的密码是root)
SHOW DATABASES; (这里必须加分号)
-- 3.使用database
USE xiaojin01;
-- 4.修改database (GBK UTF8)
ALTER DATABASE xiaojin01 CHARACTER SET UTF8;
-- 5.删除数据库 要判断是否存在 if EXISTS,否则会保存
DROP DATABASE if EXISTS xiaojindata;
7.DDL之表结构创建
– 1.创建表结构之前先要指定数据库
CREATE DATABASE if not EXISTS xiaojindb;
USE xiaojindb;
– 2.创建表结构语句
CREATE TABLE if not EXISTS xiaojin_table(
id int,
name VARCHAR(20),
age int,
addres VARCHAR(100),
CREATE_TIME DATE
);
// 注意 如果之前存在该表结构 就会覆盖之前的数据
– 3.修改表名称
ALTER TABLE xiaojin_table RENAME TO xiaojin_user;
– 4.删除表
DROP TABLE if EXISTS xiaojin_user;
8.数据类型
1)数值类型:
TINYINT 1Bytes 范围有符号(-128, 127) (0, 255) 范围无符号(0,255) 小整数值
INT 4Bytes 大整数值
BIGINT 8Bytes 极大整数值
FLOAT 4Bytes 浮点数值
DOUBLE 8Bytes 双精度 浮点数值
DECIMAL 小数值
2)日期类型:
YEAR 1Bytes 1901/2155 YYYY 年份值
DATE 3Bytes 日期值 使用较多
TIME 3Bytes 时间值
DATETIME 8Bytes 日期和时间值 使用较多
TIMESTAMP 4Bytes 时间戳
3)字符串类型
CHAR 0-255Bytes 定长字符串
VARCHAR 0-65535Bytes 变长字符串
9.DML数据操作
操作表结构中的数据 增(insert)、删(delete)、改(update)、查(select)
DDL是对数据库和表结构的操作。
– 1.增加数据 INSERT
– 对应列数据
INSERT INTO xiaojin_user(id,age) VALUES(4,25);
– 如果不写对应列,就得写出所有值
INSERT INTO xiaojin_user VALUES (6, ‘1’, 1, ‘1’, ‘2024-03-28 16:30:26’);
– 软件可以自动生成INSERT语句
INSERT INTO xiaojinnewdb
.xiaojin_user
(id
, name
, age
, addres
, creat_time
) VALUES (1, ‘小瑾’, 25, ‘四川’, ‘2024-03-28 15:30:26’);
– 2.修改数据 UPDATE
– 直接修改所有行的数据
UPDATE xiaojin_user set name=“xiaojin”, age=14, addres=“北京”;
– 修改指定行的数据
UPDATE xiaojin_user set name=“小瑾”, age=25, addres=“四川” WHERE id=1;
UPDATE xiaojin_user set name=“小瑾” WHERE name=“xiaojin”;
– 软件可以自动生成UPDATE语句
UPDATE xiaojinnewdb
.xiaojin_user
SET name
= ‘1’, age
= 1, addres
= ‘1’, creat_time
= ‘2024-03-28 15:30:26’ WHERE id
= 1;
– 3.删除数据(物理) DELETE
DELETE FROM xiaojin_user WHERE id=6; – 删除id为6这行数据
DELETE FROM xiaojin_user; – 清空数据 逐行删除 每行删除都有日志 可以回滚数据
TRUNCATE xiaojin_user; – 清空表中所有数据,数度快,不可回滚
10.约束
1)作用:
限制数据表中的数据,为了保证表中数据的准确性和可靠性
不符合约束的数据 插入就会失败
2)分类
1.NOT NULL 非空★
不允许值为null,但是可以是''(空字符)
name VARCHAR(20) NOT NULL, -- 不允许为空
-- 下面写法是修改成 非空约束
ALTER TABLE xiaojin_user MODIFY NAME VARCHAR(255) NOT NULL;
2.DEFAULT 默认★
用来指定某列的默认值 如果是字符串类型 默认值一定要用 单引号
name VARCHAR(20) DEFAULT '小瑾', -- 默认为 小瑾
-- 修改手机号码默认值
ALTER TABLE xiaojin_user
CHANGE COLUMN phone phone VARCHAR(11) DEFAULT '1111';
3.PRIMARY KEY 主键★
1.特点
在一张表中只允许有一个主键
数据是唯一的,不允许重复的,且不能为null
2.分类:
1)单列主键
方法一:
id int PRIMARY KEY,
方法二:
-- 指定id列为主键列
PRIMARY KEY (id)
2)多列主键(复合主键,联合主键)
-- 可以指定多列主键 id,phone形成联合主键
-- 联合主键:两列中有一个不一样就行
PRIMARY KEY (id,phone)
3.自动增长约束 auto_increment
1)特点:
数据库根据定义自动赋值,
默认自动增长初始值是1
配合主键使用,每增加一条记录,主键默认从1开始 自动增加1
增加数据时 主键可设置值为null
该字段类型只能是整数型(TINYINT,SMALLINT,INT,BIGINT等)
一张表只能有一个增长字段。
代码示例:
id int PRIMARY KEY auto_increment,
2)自动增长的初始值设置★ 1000 2000
(id int PRIMARY KEY auto_increment,
name char ...
)auto_increment=1000; -- 初始值设置
4.UNIQUE 唯一
-- 创建时写法
name VARCHAR(20) NOT NULL, -- 不允许为空
-- 修改写法
ALTER TABLE xiaojin_user ADD CONSTRAINT UNIQUE_PHONE UNIQUE(phone);
5.CHECK 检查约束
6.FOREIGN KEY 外键
7.ZEROFILL 零填充约束
数据的左边用零填充,知道满了设置的长度
id int(10) ZEROFILL,
11.delete 和 truncate 删除数据的区别★★★
1)truncate xiaojin_user;
清空数据表,AUTO_INCREMENT默认值从1开始
使用TRUNCATE后需要重新设置 AUTO_INCREMENT初始值
ALTER TABLE xiaojin_user AUTO_INCREMENT 2000;
2)delete FROM xiaojin_user;
使用DELETE清空数据表,AUTO_INCREMENT值会接着之前最后一条数据的值开始
12.DQL 数据库查询语言★★★------★★★重点★★★
1)定义和三者之间的区别
-- DQL查询数据库中的数据
SELECT *(列名称) FROM 表的名称 WHERE 查询的条件
-- DDL 创建数据库和表结构
-- DML 增加数据
2)格式
-- 1.查询所有的学生
SELECT * FROM students_list; -- 查询所有列
-- 2.查询学生的姓名和年龄
SELECT name,age FROM students_list; -- 查询指定列
-- 3.别名称查询 使用关键字as
SELECT s.name,s.age FROM students_list as s;
-- 4.列的别名称
SELECT NAME AS 姓名, AGE AS 年龄 FROM students_list;
-- 5.去复查询
SELECT DISTINCT * FROM students_list; -- 整行数据去重查询
SELECT DISTINCT CLASS_ID FROM students_list; -- 指定列数据去重查询
-- 6.查询结果是表达式(运算值);将所有的学生年龄+5岁
SELECT NAME, AGE+5 FROM students_list;
SELECT NAME, AGE+5 AS AGE FROM students_list
13.mysql 运算符
1)符号
-- +
-- -
-- *
-- / 或 DIV
-- % 或 MOD
2)格式
-- 查询学生的名称是为小四
SELECT * FROM students_list WHERE name='小四';
-- 查询学生的名称不是为小四
SELECT * FROM students_list WHERE name!='小四';
-- 查询学生年龄是为17岁
SELECT * FROM students_list WHERE age=17;
-- 查询学生年龄是大于17岁
SELECT * FROM students_list WHERE age>17;
-- 查询学生年龄是小于17岁
SELECT * FROM students_list WHERE age<17;
-- 查询学生年龄是18岁-40岁之间
SELECT * FROM students_list WHERE age>18 && age<40;
SELECT * FROM students_list WHERE age>18 AND age<40;
SELECT * FROM students_list WHERE age BETWEEN 18 AND 40;
-- 查询年龄是在17或者28岁的学生
SELECT * FROM students_list WHERE age=17 || age=28;
SELECT * FROM students_list WHERE age=17 OR age=28;
SELECT * FROM students_list WHERE age IN(17,28);
14.like 模糊匹配查询
-- 查询名称含有“何”
SELECT * FROM students_list WHERE name like '%何%';
-- 查询名称开头“小”
SELECT * FROM students_list WHERE name like '小%';
-- 查询名称第二字“汉”
SELECT * FROM students_list WHERE name like '_汉%';
-- 查询地址是为nu11学生
SELECT * FROM students_list WHERE address is null;
-- 查询地址不是为nu1l的学生
SELECT * FROM students_list WHERE address is not null;
15.排序 ORDER BY
-- 1.根据学生年龄从小到大;
SELECT * FROM students_list ORDER BY AGE; -- 默认是升序排序 ASC
SELECT * FROM students_list ORDER BY AGE ASC;
-- 2.根据学生年龄从大到小; DESC
SELECT * FROM students_list ORDER BY AGE DESC;
-- 3.判断学生的年龄大于18岁,在从小到大排序
SELECT * FROM students_list WHERE AGE>18 ORDER BY AGE;
-- 4.根据学生的年龄从大到小排序,以班级id从小到大排序,当年龄相同 则根据 班级id从小到大排序
SELECT * FROM students_list ORDER BY AGE DESC, CLASS_ID ASC;
-- 5.根据班级id去重,根据班级id从大到小排序
SELECT DISTINCT class_id FROM students_list ORDER BY class_id DESC;
-- 汉字是根据拼音排序的
SELECT * FROM students_list ORDER BY NAME DESC;
16.分页 limit
LIMIT m,n 表示从数据索引为m开始的n条数据
1.查询表中的前5条数据
SELECT * FROM students_list LIMIT 5;
SELECT * FROM students_list LIMIT 1,2;
2.分页查询
SELECT * FROM students_list LIMIT 0,5;
SELECT * FROM students_list LIMIT 5,5;
3.排序之后可以分页吗?
可以的,但是不能分页之后再排序
SELECT * FROM students_list ORDER BY AGE DESC LIMIT 0,5;
17.聚合查询 count
1.分类
count(age) 统计指定列的数据不为null的记录行数 -- count(*) 统计数据条数
-- **如果想根据age统计所有学生人数,但是有些age又没有填写,这个时候可以给age字段设置默认值为0,避免因为忘记填写该字段而统计漏掉
sum()
max()
min()
avg() 平均值
2.用法
-- 1.查询学生表的总人数
SELECT count(*) FROM students_list;
SELECT count(age) FROM students_list;
-- 2.查询学生年龄大于18的总人数
SELECT count(*) FROM students_list WHERE AGE>18;
-- 3.查询classid=4 所有学生年龄总和
SELECT sum(age) FROM students_list WHERE CLASS_ID=4;
-- 4.查询学生最大年龄
SELECT max(age) FROM students_list;
-- 5.查询学生最小年龄
SELECT min(age) FROM students_list;
-- 6.求学生年龄的平均值
SELECT avg(age) FROM students_list;
SELECT count(age) FROM students_list;
SELECT count(*) FROM students_list WHERE address is not null;
-- 9.查询英语成绩不在80-90之间的同学
SELECT name as 姓名, english as 英语 FROM xiaojin_student WHERE not (english >= 80 AND english <= 90);
SELECT name as 姓名, english as 英语 FROM xiaojin_student WHERE english not BETWEEN 80 and 90;
18.分组语句 GROUP BY
1)底层原理是先根据 ORDER BY 排序 再将相同的条数统计出来
2)用法
-- 统计每个班级对应多少个学生
SELECT count(*) FROM students_list WHERE class_id=1;
SELECT count(*) FROM students_list WHERE class_id=2;
SELECT count(*) FROM students_list WHERE class_id is null; -- 这个方法太繁琐了
SELECT class_id as 班级, count(*) as 人数 FROM students_list GROUP BY class_id;
-- 根据地址分组
SELECT address, count(*) as 人数 FROM students_list GROUP BY address;
-- HAVING 过滤数据,是对分组之后的数据做条件过滤,不能用 where ★★★
-- 查询出 人数大于1的班级个数
SELECT class_id as 班级, count(*) as 人数 FROM students_list GROUP BY class_id HAVING COUNT(*)>1;
SELECT class_id as 班级, count(*) as 人数 FROM students_list GROUP BY class_id HAVING COUNT(*)>1 ORDER BY count(*) DESC; -- 再对分组排序
-- 查询1班的人数
SELECT class_id as 班级, count(*) as 人数 FROM students_list GROUP BY class_id HAVING class_id=1;
SELECT class_id as 班级, count(*) as 人数 FROM students_list WHERE class_id=1;
3)★语句执行顺序 FROM - GROUP BY - count - HAVING - ORDER BY - limit
19.多表关系
1)一对一
一个学生只有一个身份证,一个身份证只能对应一个学生
一般一对一关系使用比较少
2)一对多
例如 学生班级表(stu_class)、学生信息表(stu_info)等多张表
一个班级对应n个学生 一个学生对应一个班级
在表中添加一个外链,指向另一方主键,确保一对一关系
3)多对多
一门课程可能被多名学生学习,一个学生可能学习多门课程
原则:多对多关系实现需要借助 中间表 实现,中间表至少包含两个字段,将多对多关系拆分成一对多关系
20.外键约束
1)概念:MySQL外键约束(FOREIGN KEY)
是表的一个特殊字段,经常与主键约束一起使用。
对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表(父表),外键所在的表就是从表(子表)
2)外键
用来建立主表与从表的关联关系,
为两个表的数据建立连接,
约束两个表中数据的一致性和完整性
3)定义外键,需要遵守的规则
1.主表必须存放于数据库中
2.必须在主表定义主键
3.主键不能包含空值,但允许在外键中出现空值
4.外键中列表的数据类型必须和主表主键的数据类型相同
4)外键约束验证
1.先向主表新增数据,再向从表新增数据
2.外键列的值必须是主键列表存在的 或者为空
3.主表的数据不能随便删除,从表数据可以随便删除
可以先删除从表数据关联到主表的,再删除主表
21.联表查询
1.概念联表查询又叫多表查询是指多张表联合一起查询,例如学生信息与学生班级表、部门与员工表
2.分类
1)交叉连接查询(笛卡尔积)
SELECT * FROM mykt_class,mykt_student;-- 交叉连接查询
2)内连接查询
-- 1.查询每个班级下所有学生信息
SELECT * FROM mykt_class, mykt_student WHERE mykt_student.class_id = mykt_class.id;-- 隐式内连接查询
SELECT * FROM mykt_class JOIN mykt_student on mykt_student.class_id = mykt_class.id;-- 显示内连接查询
-- 2.需求查询第一期所有学生
SELECT * FROM mykt_class JOIN mykt_student ON mykt_student.class_id = mykt_class.id AND mykt_class.id=2;
-- 3.查询第一期和第二期所有的学生
SELECT * FROM mykt_class, mykt_student WHERE mykt_student.class_id = mykt_class.id AND (mykt_class.id in (1,2));
SELECT * FROM mykt_class JOIN mykt_student ON mykt_student.class_id = mykt_class.id AND (mykt_class.id in (1,2));
-- 4.查询每个班级下的学生总数 并且学生总数升序排列
SELECT mykt_class.name as 班级, count(*) as 总人数 FROM mykt_class JOIN mykt_student on mykt_student.class_id = mykt_class.id GROUP BY mykt_class.name ORDER BY count(*) ASC;
-- 5.查询班级总人数>2的班级,并且人数降序排列
SELECT mykt_class.name as 班级, count(*) as 总人数 FROM mykt_class
JOIN mykt_student on mykt_student.class_id = mykt_class.id
GROUP BY mykt_class.name HAVING count(*)>2
ORDER BY count(*) DESC;
3)外连接查询
左外连接
右外连接
全外连接(mysql不能实现)
SELECT * FROM mykt_class LEFT OUTER JOIN mykt_student ON mykt_student.class_id = mykt_class.id -- 左外连接
UNION -- UNION关键字将左右连接起来 就是全外连接
SELECT * FROM mykt_class RIGHT OUTER JOIN mykt_student ON mykt_student.class_id = mykt_class.id; -- 右外连接
4)左连接、右连接和内连接的区别
1.左连接:以左边为准,左边有该数据 就会返回,右边没有匹配上就会返回null
SELECT * FROM mykt_class LEFT JOIN mykt_student ON mykt_student.class_id = mykt_class.id;
2.右连接:以右边为准,右边有该数据 就会返回,左边没有匹配上就会返回null
SELECT * FROM mykt_class RIGHT JOIN mykt_student ON mykt_student.class_id = mykt_class.id;
3.内连接(默认的);左边和右边都必须匹配才会返回
SELECT * FROM mykt_class JOIN mykt_student ON mykt_student.class_id = mykt_class.id;
-- 案例需求:
-- 1.查询哪些班级是有学生 哪些班级是没有学生
SELECT * FROM mykt_class LEFT JOIN mykt_student ON mykt_student.class_id = mykt_class.id; -- 左连接
-- 2.查询哪些学生是有班级,哪些学生是没有班级
SELECT * FROM mykt_class RIGHT JOIN mykt_student ON mykt_student.class_id = mykt_class.id WHERE mykt_class.id is not null; -- 右连接
SELECT * FROM mykt_class RIGHT JOIN mykt_student ON mykt_student.class_id = mykt_class.id WHERE mykt_class.id is null;
-- 3.返回工资处于P1级别员工的信息 含部门名称
SELECT * FROM mykt_emp me JOIN mykt_salgrade ms on (me.emp_salary <= ms.hisal AND me.emp_salary >= ms.losal and ms.grade='p1')
JOIN mykt_dept as md on md.dept_no=me.emp_deptno; -- 三连表查询
22.子查询
1)概念:一个查询语句嵌套在另一个查询语句内部的查询
2)常用的操作符有any(some),all,in,EXISTS
1.使用all关键字
底层 多个 and 比较
all关键字用在比较操作符的后面,表示查询结果的多个数据中的所有都满足该比较操作符才算满足 比较操作符:=、>、!=、>=、<= 等
SELECT * FROM mykt_student WHERE age > all(SELECT age FROM mykt_student WHERE class_id=1);
-- 查询没有班级(包括班级填错)的学生信息
-- 先获取有哪些班级
SELECT id FROM mykt_class;
-- 再获取没有班级的学生
SELECT * FROM mykt_student WHERE class_id != all(SELECT id FROM mykt_class);
2.any关键字
底层 多个 or 或者 比较
-- 查询学生年龄大于第一期任意一个学生年龄的学生信息
SELECT * FROM mykt_student WHERE age > any(SELECT age FROM mykt_student WHERE class_id=1);
3.not in 关键字
底层
in 子查询语句中 等于or连接比较
all 子查询语句中 and 比较符
any 子查询语句中 or 比较符
4.exists 关键字
EXISTS 关键字 不返回查询数据,返回的是true 或false
EXISTS 关键字比 in 关键字运算效率高
所以开发中 数据量大的时候 推荐使用★★★
SELECT * FROM mykt_student WHERE EXISTS (
查询语句
-- 查询到结果 就返回true
);
-- 需求1:查询年龄大于18岁的学生信息
SELECT * FROM mykt_student WHERE age>18; -- 简单做法
-- exists方法
SELECT * FROM mykt_student s1 WHERE EXISTS (SELECT * FROM mykt_student s2 WHERE s1.age>18); -- s1中学生年龄大于18才返回true
-- 需求2:查询班级下 有学生的班级
SELECT * FROM mykt_class m1 WHERE EXISTS (SELECT * FROM mykt_student WHERE class_id = m1.id);
-- 需求2:查询有班级的学生信息
SELECT * FROM mykt_student s1 WHERE EXISTS (SELECT * FROM mykt_class WHERE id=s1.class_id);
23.自关联查询
自己关联自己。菜单树、分类表等
-- 创建表
CREATE TABLE `commodity_type` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb3_bin DEFAULT NULL,
`parent_id` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin;
-- 插入数据
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (1, '家用电器', 0);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (2, '电脑办公', 0);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (3, '家居/家具', 0);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (4, '电视机', 1);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (5, '空调', 1);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (6, '4k超清电视', 4);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (7, '教育电视', 4);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (8, '柜机空调', 5);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (9, '挂机啊空调', 5);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (10, '电脑整机', 2);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (11, '笔记本', 10);
INSERT INTO `mykt`.`commodity_type` (`id`, `name`, `parent_id`) VALUES (12, '平板电脑', 10);
-- 查询表下面所有分类
SELECT * FROM commodity_type;
-- 查询一级分类
SELECT * FROM commodity_type WHERE parent_id=0;
-- 查询所有分类下面的子类
SELECT * FROM commodity_type as c1, commodity_type as c2 WHERE c1.id=c2.parent_id;
SELECT c1.parent_id as 分类编码,c1.name as 分类, c2.name as 子类 FROM commodity_type as c1, commodity_type as c2 WHERE c1.id=c2.parent_id ORDER BY c1.parent_id;
第十章 JDBC技术------------------------
1.什么是JDBC
是JAVA数据库连接,(Java Database Connectivity,简称JDBC)
是java语言中用来规范客户端 程序如何来访问数据库的应用程序接口
提供了如查询和更新数据库中数据的方法
通俗来说,就是java操作数据库
2.JDBC技术操作步骤
1.导入mysql驱动jar包,
2.注册驱动 javase 反射机制Class.forName()
Class.forName("com.mysql.cj.jdbc.Driver");
3.获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mykt?serverTimezone=UTC", "root", "root");
4.获取执行者对象
Statement statement = connection.createStatement();
5.执行sql语句并获取返回结果
ResultSet resultSet = statement.executeQuery("select * from mykt_users");
6.对结果进行处理
while (resultSet.next()) {
resultSet.getInt("id");
resultSet.getString("name");
resultSet.getString("pwd");
System.out.println("id:"+resultSet.getInt("id")
+"name:"+resultSet.getString("name")+"pwd:"+resultSet.getString("pwd"));
}
7.释放jdbc资源
connection.close();
statement.close();
3.容易遇到的问题
1)没有引入成功jar包,找不到驱动
import com.mysql.jdbc.Driver;
2)注册驱动填写错误 com.mysql.cj.jdbc.Driver
3)url填写错误 账户密码错误 数据库名称错误
4.分层架构
com.mykt.entity --实体类
com.mykt.dao --数据库访问层
com.mykt.serivce --业务逻辑层
com.mykt.controller --控制层
db数据类型为varchar 对应java定义String
db数据类型为intr 对应java定义Integer
5.jdbc工具类的封装
1.工具类不需要new出来,是通过 类名.方法名 来访问的
2.所有需要将构造方法私有化
3.工具类封装步骤:
1.构造方法私有化
2.定义工具 需要声明变量
3.使用静态代码块(类加载) 来给声明好的jdbc变量赋值
1.读取config.properties 文件 IO 路径 相对路径
2.赋值给前面声明好的变量(成员变量要设置成静态 才可以直接使用)
3.注册驱动类
4.封装连接方法
5.封装释放连接方法
6.使用scannar数据读取问题
in.nextLine(); 不能放在 in.nextInt(); 代码段后面, 因为nextInt后面已经有内容‘\n’, nextLine()会读取换行符,会自动跳过不读
解决方法:nextInt 和 nextLine中间再加一句 in.nextLine();
8.登录注册系统
1)用户注册
实质上是向数据库插入一条用户数据
插入前先要判断用户是否被注册过
2)用户登录
实质上是查询用户是否存在,账号和密码都要正确
9.什么是jdbc SQL注入漏洞
1)SQL注入漏洞
是利用SQL语句的漏洞实现对系统攻击
底层原理:通过传递参数(' or 1='1)拼接SQL语句,导致其查询语句成立 可以查询到数据 容易被黑客攻击
例如:用户输入密码
pwd= ' or 1='1
拼接好的SQL语句是:SELECT * FROM mykt_users WHERE phone='wwww' and pwd='' or 1='1'
2)解决办法★★★
1.使用PreparedStatement(预编译执行者对象)
先明确sql语句执行格式,后再拼接参数
用?占位符的方式 setxxx(参数1,参数2)
xxx是数据类型
参数1:?的位置从编号1开始
参数2:?的实际参数
2.代码示例:
String sqlString = "SELECT * FROM mykt_users WHERE phone='"+user.getPhone()+"' and pwd='"+user.getPwd()+"'";
//预编译
statement = connection.prepareStatement(sqlString);
statement.setString(1, user.getPhone());
statement.setString(2, user.getPwd());
resultSet = statement.executeQuery(sqlString);
3.访问数据库采用预编译的形式,不用sql语句拼接,不安全
10.jdbc事务管理
1)mysql中的事务
必须满足4个条件 ACID
1)原子性(Atomicity 又叫不可分割性)
一个事务中所有并列的操作要么全部完成,要么全部都不完成,不会结束在中间某个环节。
2)一致性(Consistency)
在事务开始之前和事务结束之后,数据库的完整性没有被破坏。
3)隔离性(Isolation 又叫独立性)
4)持久性(Durability)
事务处理结束后,对数据的修改是永久的,即便系统故障也不会丢失
2)提交事务 回滚事务
mysql默认情况下是自动提交操作 AUTOCOMMIT=1
AUTOCOMMIT=0 非自动提交
SET AUTOCOMMIT=0;
BEGIN; -- 开启事务
INSERT INTO `mykt`.`mykt_users` (`id`, `phone`, `pwd`) VALUES (null, '13890780235', '33333');
ROLLBACK; -- 回滚事务,反悔前面的操作,查询不到了
COMMIT; -- 提交事务 ,执行后插入的数据才可以看到
3)jdbc事务管理 设置手动事务步骤
1.设置自动提交模式 为手动提交模式
//false是手动提交模式 true为自动提交模式
connection.setAutoCommit(false);
2.数据操作完后 执行提交事务
connection.commit();
3.若发生错误 执行回滚事务
connection.rollback(); //回滚事务
注意:该事务管理对数据的增,删,改操作有用 对查询没有,查询不需要事务管理
11.mysql行锁
1)思考问题
如果开启了事务,没有提交 也没执行回滚,会发生什么问题?
答案:另外的窗口在处理执行该数据时会被卡住,该行数据会一直会锁住,无法被其他线程修改
– 查询哪些行的行锁没有被释放
SELECT * FROM information_schema.INNODB_TRX;
– 手动释放行锁命令
kill 17; – 17是 trx_mysql_thread_id
12.jdbc数据库连接池
1)什么是数据库连接池
在jdbc编程中,每次创建和断开Connection对象都会消耗一定的时间和IO资源,频繁和数据库打交道,效率非常低
为了避免频繁创建数据库连接,可以通过数据库连接池 负责 分配、管理、释放 数据库连接
2)数据库大致实现原理
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,
当应用程序访问数据库时并不是直接创建Connection,而是向连接池“申请”一个Connection。
如果连接池中有空闲的Connection,则将其返回,否则创建新的Connection。
使用完毕后,连接池会将该Connection回收,并交付其他的线程复用使用,
以减少创建和断开数据库连接的次数,提高数据库的访问效率。
13.整合C3P0数据库连接池
1)步骤
1.创建c3p0数据连接池
//这里可以通过构造函数指定连接哪个连接池 没有指定就会使用 c3p0配置文件 里的默认的 default-config
ComboPooledDataSource pool = new ComboPooledDataSource();
2.设置jdbc连接信息
pool.setUser("root");
pool.setPassword("root");
pool.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/mykt?serverTimezone=UTC");
pool.setDriverClass("com.mysql.cj.jdbc.Driver");
3.获取连接对象
Connection connection = pool.getConnection();
4.获取预编译执行对象,防止sql语句注入
PreparedStatement preparedStatement = connection.prepareStatement("select * from mykt_users where id=?");
5.设置参数
preparedStatement.setLong(1,2);
6.执行SQL语句
ResultSet resultSet = preparedStatement.executeQuery();
//释放资源
connection.close();
preparedStatement.close();
resultSet.close();
2)c3p0配置文件
1.src resources下创建 c3p0-config.xml 文件
2.写入内容
<c3p0-config>
<!--使用默认配置读取连接池对象-->
<default-config>
<!--连接参数-->
<!-- 这里需要修改自己的 数据库路径、用户账号、密码-->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/mykt?serverTimezone=UTC</property>
<property name="user">root</property>
<property name="password">root</property>
<!--初始化连接数量-->
<property name="initialPoolSize">5</property>
<!--最大连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间(单位 毫秒)-->
<property name="checkoutTimeout">3000</property>
</default-config>
</c3p0-config>
3)使用
1.创建c3p0数据连接池
//这里可以通过构造函数指定连接哪个连接池 没有指定就会使用默认的 default-config
ComboPooledDataSource pool = new ComboPooledDataSource();
//从数据库连接池获取连接使用 并没有直接创建连接(创建连接需要tcp,费时间
Connection connection = pool.getConnection();
2.释放连接
//这里的close并不是真正的关闭连接 是归还连接到数据库 该连接还可以被其他用户使用
connection.close();
14.德鲁伊数据库连接池
1)配置文件 druid.properties
E:\project\java\untitled1\src\main\resources\druid.properties
2)使用步骤
重要代码:
1.读取德鲁伊 druid.properties 配置文件
//相对路径 Test03.class.getClassLoader()获取到的是src主目录
//这里是resources 静态资源文件夹下面
Properties properties = new Properties();
InputStream resourceAsStream = Test03.class.getClassLoader().
getResourceAsStream("druid.properties");
properties.load(resourceAsStream);
2.将读取到的配置文件 放入到 德鲁伊数据连接池中
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
3.通过dataSource 获取连接
Connection connection = dataSource.getConnection();
4.案例:E:\project\java\untitled1\src\main\java\com\mykt\test\Test03.java
第十一章 JavaWeb开发--------------------------
1.C/S 架构
C(客户端Client)/S(服务器Server)架构
是桌面应用程序
先下载对应的安装包 安装成功后才可以使用
2.B/S架构
1)Web:全球广域网,又叫万维网(www),也就是能够通过浏览器访问的网站
如 www.baidu.com
JavaWeb开发是使用java技术栈开发Web项目
2)B(浏览器Browser)/S(服务器Server)架构
静态资源:html\css\images
动态资源:jsp/Serblet、ASP、PHP等
在java中,动态web资源开发技术 统称为 JavaWeb
3)特点
B/S架构体系的软件 版本升级的时候 客户端是无需升级的 只需要重新刷新网页即可
缺陷:会占用服务器资源
3.tomcat 服务器 基本使用
1)下载地址:https://tomcat.apache.org/download-10.cgi
2)安装文件夹:
1.bin(文件夹) --可执行文件,例如启动或停止tomcat
*.bat --运行在windows环境批量处理文件
*.sh linux环境中运行文件
2.conf 目录 --存放配置文件
logging.properties、 server.xml
服务端 控制台输出乱码问题,解决方法:
修改logging.properties文件50行左右--删除带有utf-8的文件
因为不确定电脑控制台启动的时候编码格式是否是utf-8,如果不是,就会出现乱码
3.webapps 目录 --存放运行程序 部署war包,jar包,静态资源 ★
访问地址:http://127.0.0.1:8080/mykt/mykt.html(页面文件)
页面文件 没有写就默认是查找 index.html
webapps 目录中 ROOT 文件 是tomcat默认的欢迎界面
4.lib 目录 --存放 tomcat 依赖的jar包
5.temp目录 --存放临时文件
6.work目录 --存放 tomcat 在运行时的编译后的文件
清空该目录,然后重启tomcat 可以达到清除缓存的作用
3)修改tomcat启动端口号
conf 目录下server.xml文件,修改69行左右的端口号
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>
4)解决启动tomcat闪退问题
1.版本过高
2.jdk环境变量配置错误
5)tomcat项目部署方式
方式一:直接在webapps目录创建文件夹 (使用较多)
方式二:conf 目录下server.xml文件 176行左右 host标签结束前插入下面语句
<Context path="/mykt" docBase="D:\mykt" />
方式三:将项目打包成war包放入到 webapps 目录下 自动解压
方式四:webapps目录下/ROOT工程访问 在ROOT目录中访问,不需要写ROOT在ip地址里面 可以直接访问
4.idea 开发 JavaWeb 项目
1)使用idea先创建一个普通的java项目
2)再将该java项目变成web项目
按两次 shift,点击操作,添加框架支持 选择web
3)整合tomcat
5.JavaWeb 项目结构
src-----java代码,核心的配置文件(例如spring配置文件等) servlet
web-----静态资源 或者jsp等
(默认页面是index.html或index.jsp,其它页面都得写)
html------html、js、css、images等静态资源 外部都可以直接访问
WEB-INF-----外界无法访问内部文件
web.xml-----serclet相关配置
index.jsp
6.servlet
1)定义:是基于Java技术的Web组件,由容器管理,并产生动态的内容
servlet与客户端通过 servlet容器 实现请求/响应模型 进行交互
springmvc----底层基于servlet
2)实现步骤(环境搭建)
1.在我们的项目中 创建libs目录 存放第三方的jar包
2.项目中导入servlet-api.jar libs目录中
就在我们tomcat安装的目录 中Iib 目录中
D:\software\apache-tomcat-8.5.100\lib
3.创建servlet包 专门存放就是我们的servlet
4.创建IndexServlet 实现Servet 重写方法
5.IndexServlet 类上加上 @WebServlet("/mayikt")注解 定义 URL访问的路径
@WebServlet(urlPatterns="",loadOnStartup= )
参数一urlPatterns:访问路径
参数二loadOnStartup:设置创建 Servlet 的模式
loadOnStartup=-1 --第一次访问Servlet 才会创建
loadOnStartup=0 --当服务器启动的时候就会创建Servlet 无参构造函数中可以执行
loadOnStartup=1 --当服务器启动的时候就会创建Servlet
loadOnStartup=2 --当服务器启动的时候就会创建Servlet
从0开始排序,越小的,越优先加载创建
6.重写Servlet 类中service 在service中编写 动态资源
3)执行流程
servlet 是 tomcatweb 服务器创建的,service 方法也是tomcatweb服务器在调用
项目是部署在tomcat中,加载servlet.class到内存中
4)生命周期★★★★★
1.创建servlet
是单例模式,也就是 servlet 在创建好之后在jvm内存中只会存在一份
存在线程安全问题,
第一次访问创建了servlet,第二次访问用不用再创建了,直接执行里面的方法
那么第一次的启动速度就比第二次慢,解决办法是 --提前创建servlet
就是在tomcat启动时就创建servlet --这样又会导致 tomcat启动变慢
但是我们更多选择提前创建tomcat启动 加注解 @WebServlet("/mayikt")
但是如果不用的话 就会浪费内存
2.执行servlet 类中 init---初始化方法
当servlet类被创建时,就会执行初始化方法,该方法只会执行一次
3.servlet类---service---执行每次请求
每次客户端发送请求到达服务器 都会执行service方法
4.servlet类---destory---当我们tomcat容器停止时卸载
存放销毁相关代码
5)servlet线程安全性问题
Servlet是单例模式 需要解决线程安全性问题
Servlet 里的全局变量(如:count) 需要注意线程安全性问题
在多线程(多个请求的时候)共享到同一个全局变量 在做写操作 会发生线程安全性问题
解决方法:
加上synchronized锁,在没获取锁的时候就会重试,保证次数被加上
加上synchronized锁就变成了单线程
6)servlet 方法
init(ServletConfig servletConfig) --当servlet创建时会执行初始化方法,只会执行一次
getServletConfig() --在 Servlet 给其它方法提供 根据ServletConfig 获取的初始化参数值
getServletInfo() --返回字符串信息,包含关于servlet的信息,例如作者、版本和版权。该方法返回的是存文本,而不是任何类型的标记
7) httpServlet抽象类 底层实现原理
1.对原生的Servlet接口进行了封装,重写了GenericServlet 的service 方法
2.封装之后重写doGet方法和doPost方法。
手写 httpServlet 代码案例:E:\project\java\mykt-tomcat\src\com\mykt\httpervlet
8)request 与 response 对象
request:获取客户端发送数据给服务器端
response:返回对应数据给客户端(浏览器)
http协议是基于request(请求)与response(响应)的模型
7.request 获取请求数据方法
1)请求行部分
String getMethod() --获取请求方法
String getContextPath() --获取项目访问路径--例如:/mykt
StringBuffer getRequestURL() --获取URL统一资源定位符--例如:http://localhost/mykt_tomcat/myktDemo06
String getRequestURI() --获取URL统一资源标识符--例如:/mykt_tomcat/myktDemo06
String getQueryString() --获取请求参数(get方式)--例如:userName=xiaojin&userPwd=644064
2)请求头部分(键值对形式组成)
String getHeader(String name) --根据请求头名称,获取值
例如:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
getHeader("User-Agent")
3)获取请求体参数
//获取字符输入流
BufferedReader reader = req.getReader();
//获取客户端发送给服务器的请求体内容
String str = reader.readLine();
System.out.println(str);
reader.close();
4)request 通用获取参数方法★★★
Request对象里封装好了一个Map集合,Map集合里面就是所有的参数
Map<String,String[]> getParameterMap() //返回这个Map集合(容易返回内存地址,出错)
String[] getParameterValues(String name) //根据 键名 获取值(可能一个键对应多个值)
String getParameter(string name) //根据 键名 获取值
5)request 请求转发
1.服务器内部资源跳转
req.getRequestDispatcher("/httpServletDemo09").forward(req,resp);
2.分享数据给服务器内部其它资源
req.setAttribute("name","mykt");
//其它服务器获取数据 getAttribute()的到的是Object对象 需要强转成String
String name =(String) req.getAttribute("name");
//展示在浏览器
resp.getWriter().println("wo shi HttpServletDemo09"+name);
8.response 响应数据
1)概念
response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse
在客户端发出每个请求时,服务器都会创建一个response对象,并传入Servlet.service()方法
2)响应格式(3部分)
1.响应行
响应数据的第一行:http协议版本1.1版本 状态码(如200)
//设置响应状态码
resp.setStatus(200);
2.响应头:第二行开始 key value
//设置响应头中的响应地址
resp.setHeader("Location","http://www.mayikt.com/");
3.响应体:响应给客户端的数据资源
//设置响应数据编码格式
resp.setContentType("text/html;charset=utf-8");
//获取写对象
PrintWriter writer = resp.getWriter();
//写入数据
writer.write("小瑾666");
//关闭资源
writer.close();
4)重定向
1.重定向原理 底层
//设置响应状态码 告诉浏览器需要 重定向
resp.setStatus(302);
//设置响应头中的响应地址(也可以是内部资源地址)
resp.setHeader("Location","http://www.mayikt.com");
2.通用方法 设置重定向地址
resp.sendRedirect("httpServletDemo13");
5)转发和重定向的区别★★★
转发只能将请求转发给同一个web应用(项目工程)中的其它组件(servlet程序)
重定向可以重定向到任意地址,网络地址或是文件地址(跨项目文件夹www.taobao.com);
重定向会发起两次url请求,重定向访问结束后,浏览器的url地址发生变化,
转发只发起一次请求,请求结束浏览器显示的url地址不会发生变化;
请求转发调用者和被调用者之间共享相同的请求对象,属于同一个请求和响应过程
重定向是不同的 请求 和 响应 过程
9.java web项目开发
1)添加3个jar包
常用工具包 commons-lang3-3.4.jar
mysql驱动包 mysql-connector-java-8.0.12.jar
servlet-api包 servlet-api.jar
(添加后,要复制到tomcat安装目录的lib文件夹里面)
2)拷贝JdbcUtils工具类 到src下项目文件夹
3)拷贝config.properties文件 到src下项目文件夹
10.常见问题
1)重定向问题 html文件中访问地址要写全
2)参数写错问题
3)java-web项目,加上路径报错404 ,可能是jar包配置错误
4)客户端响应乱码问题解决代码 resp.setContentType("text/html;charset=utf-8");
5)request获取参数的中文乱码问题:
1.get方法获取参数 内部getParameter() 方法解决了
String userName = req.getParameter("userName");
String userPwd = req.getParameter("userPwd");
2.post方法获取参数
通过getParameter( “name” )方法获取页面参数时,当页面参数输入为中文的时候,获取到的中文参数会出现乱码问题 post方式:会出现乱码
配置解决代码:request.setCharacterEncoding("utf-8");
11.jsp底层设计原理
1)(java server pages)和Servlet技术一样,都是用于开发动态web资源的技术
2)最大特点:在html页面中嵌套java 代码,为用户提供动态数据 <% System.out.println("111");%>
3)Servlet技术很难对数据排版,jsp代码产生动态数据的同时,也能排版数据
4)原理:先将jsp文件编译成java代码,再编译成class文件去运行
编译后文件地址 在控制台输出的内容里面找
file=C:\Users\76220\AppData\Local\JetBrains\IntelliJIdea2023.3\tomcat\a7b75c5b-d629-4435-8d95-249ecfed4c3a --work目录,一直往下面点,直到找到java文件
12.jsp脚本
<body>
<%
System.out.println("我是在 jspServlet 方法之中...");
int j=20;
mykt();
%>
<%="直接输出 String类型" + j %>
<%!
void mykt() {
System.out.println("我是在 jspServlet 方法之外...");
}
String userName="mykt";
%>
</body>
13.el 表达式
jsp代码:
<body>
${userList}
</body>
java代码:
ArrayList<String> userList = new ArrayList<>();
userList.add("小瑾");
userList.add("小明");
userList.add("小军");
//建立属性
req.setAttribute("userList", userList);
//共享地址
req.getRequestDispatcher("userList.jsp").forward(req,resp);
14.jstl标签语法
1)用法
1.项目导入三个jar包
jboss-jstl-api_1.2_spec-1.1.4.Final.jar
jsp-api-2.2.jar
servlet-api.jar
★注意:如果安装tomcat的lib目录下面没有这三个包,需要复制进去,不然会报错
D:\software\apache-tomcat-8.5.100\lib
2.jsp文件插入语句:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
2)if判断
<c:if test="${age>18}">
<h1>年龄是大于18岁</h1>
</c:if>
<c:if test="${age==18}">
<h1>年龄是等于18岁,恭喜成年了</h1>
</c:if>
<c:if test="${age<18}">
<h1>年龄是小于18岁,还是未成年</h1>
</c:if>
3)forEach 遍历
<c:forEach items="${airList}" var="air">
<tr style="text-align: center">
<td>${air.flightNum}</td>
<td>${air.airCompany}</td>
<td>${air.startAirport}</td>
<td>${air.endAirport}</td>
<td>${air.startTime}</td>
<td>${air.endTime}</td>
<td>${air.airType}</td>
<td><a href="/mykt_flight_sys_war_exploded/editFlight?id=${air.id}">修改</a> <a href="/mykt_flight_sys_war_exploded/deleletFlight?id=${air.id}">删除</a></td>
</tr>
</c:forEach>
4)form 表格(注意action的值是查询路径 method="post"是提交方法)
<form action="insertFlight" method="post">
<input type="hidden" name="id" value="${flight.id}"></input>
<label>航 号:<input style="width: 200px" type="text" name="flightNum" value="${flight.flightNum}"></label><br>
<input type="submit" value="提交">
</form>
15.jsp-web项目注意和易错点:
1)时间类Date java和sql之间转换,转换成数据库中sql的Date 类型才能做操作
String startTimeStr = req.getParameter("startTime");
//java实体中的util类型的Date 转 数据库中sql类型日期★★★
Date startTime=new Date(DateUtils.parseDate(startTimeStr).getTime());
//DateUtils 需要放在utils工具包中
2)增、删、改操作都要走post请求,增加和修改操作 需要在doGet方法中做 页面回显,就是转发到操作页面
3)修改后需要转发到 系统页面,如果显示乱码,在input中getParameter()获取参数之前,设置utf-8
req.setCharacterEncoding("utf-8");
4)逻辑删除数据:
实际开发中,不是真正的删除,只是从客户端隐藏了该数据,数据库中还真实存在
@param fliId 航班的id
@return 0--隐藏失败 1--隐藏成功
16.会话技术
1)客户端会话跟踪技术 Cookie
Cookie数据 存放在浏览器端(客户端)
客户端不清理数据缓存 该数据就不会丢失, 和服务器端无关
底层原理:客户端从服务器得到 Cookie后就存在本地,该客户端其它资源也能访问该Cookie
1)创建cookie
Cookie cookie = new Cookie("mykt", "每特教育");
//设置cookie的有效存放期 单位是s 超过这个时间 客户端就找不到这条cookie
cookie.setMaxAge(60);
//服务器端会将cookie 返回给客户端 (浏览器 就是客户端 会保存该cookie)
resp.addCookie(cookie);
2)请求客户端 传递回来的 cookies
Cookie[] cookies = req.getCookies();
2)服务端会话跟踪技术 Session
1.原理
数据存放在服务器端,底层原理有Cookie技术
存入Session后,客户端会生成一个SessionId,客户端的其它资源也可以根据SessionId向服务器访问到里面的内容,就是带上下面 的请求头:
Cookie: JSESSIONID=5363D426FD30C5BD49C911E44EED8F02
//获取当前的session
//如果没有设置false,默认值为true 就是允许添加新的session
//值为false代表只能修改值,如果服务器中没有该key 就会报错
HttpSession session = req.getSession(true);
//使用session存入值
session.setAttribute("mykt","xiaojin");
PrintWriter writer = resp.getWriter();
writer.println("ok");
writer.close();
//获取 Session中的值
String mykt = (String) session.getAttribute("mykt");
PrintWriter writer = resp.getWriter();
writer.println("sessionValue:" + mykt);
writer.close();
2.细节
1)当客户端关闭后,服务器不关闭的话,获取到的session是不原来那个
答:不是的,因为客户端关闭后,cookie被销毁,客户端会请求创建新的 session
如果想要相同,可以设置 cookie最大存活时间,让cookie持久化保存 这时两次获取的session可以为同一个,代码如下:
//持久化 SESSIONID
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(60*60);//1个小时
resp.addCookie(cookie);`在这里插入代码片`
2)服务器关闭后,SESSIONID 会发生变化吗?
会发生变化
解决办法,将 SESSIONID 存放在硬盘中
3)token 或者 jwt --新技术
17.session 和 cookie 的区别 ★★★★★
1)session 用于存储一次会话 多次请求数据,存在于服务器端
2)session可以存储任意类型 任意大小的数据
session与cookie的区别
1)session存储数据在服务器端, cookie客户端
2)session没有数据大小限制, cookie有大小限制
3)session数据安全, cookie数据相对不安全
18.session 登录注册功能
E:\project\java\mykt-session
核心代码:
//能在db中查询到该对象 登录成功了,将用户数据存储到 session中
HttpSession session = req.getSession();
session.setAttribute("user", userEntity); //返回的是session中的user
//使用Cookie记住密码★★
String rememberPwd = req.getParameter("rememberPwd");
if("on".equals(rememberPwd)) {
Cookie cookie1 = new Cookie("userName", userName);
cookie1.setMaxAge(60*60*24*7);//一个星期
//永久(100年) 7天 30天
Cookie cookie2 = new Cookie("userPwd", userPwd);
cookie2.setMaxAge(60*60*24*7);//一个星期
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
//转发到首页(这里应该用重定向,地址栏变成首页)★
//req.getRequestDispatcher("index.jsp").forward(req, resp);
resp.sendRedirect("index.jsp");
19.java图形验 注册 证码api
1)RandomValidateCode 工具类(实现随机生成验证码 和图片 的功能)
直接拿来用 E:\project\java\mykt-session\src\com\mykt\utils\RandomValidateCode.java
2)VerifycodeServlet 验证操作类
3)RegisterServlet注册 登录成功前先判断验证码 输入是否正确
将用户输入的验证码 和 工具类里面封装的 session key的值 做比较,
//获取 session key的值
session.getAttribute("MAYIKT_RANDOMVALIDATECODE")
20.过滤器 filter
1)使用场景
判断用户是否登录
过滤器请求记录日志
身份验证
权限控制
2)什么是过滤器
拦截过滤请求,把不符合规则的拦截在外,不让访问
可以减少代码冗余性问题
3)过滤器拦截路径配置
@WebFilter("/*") --拦截所有资源
@WebFilter("/index.jsp") --拦截具体资源路径
@WebFilter("/xiaojin/*") --拦截具体目录下的所有资源
@WebFilter("/*.jsp") --拦截具体后缀名的资源
4)过滤器链
可以有多个过滤器
优先级:按照过滤器类名(字符串)的自然排序
AFilter>BFilter>C..., 按顺序先后执行
5)过滤器里面最好不要字符转化,有些是图片类型的 不是text/html格式的 容易出现bug
6)获取当前上下文api getContextPath()
String contextPath = req.getContextPath(); //得到 /mykt_session_war_exploded
21.AJAX 简介
1)Asynchronous JavaScript and XML.
★★是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
是一种用于创建快速动态网页的技术。
传统的网页(不使用 AJAX)如果需要更新内容,必须重载整个页面。
有很多使用 AJAX 的应用程序案例:Google Maps、Gmail、Youtube 和 Facebook。
2)代码易错点
1.容易在$ 前多加个 / ,模版字符串(contextPath)里面本来就有/
url: "http://localhost:8080${pageContext.request.contextPath}/exUserNameServlet",
2.js代码里面的axios请求参数 必须和 服务一致getParameter()一致
//jsp中 javascript
axios({
method: "get",
url: "http://localhost:8080${pageContext.request.contextPath}/exUserNameServlet",
params: {
name: userName
}
}).then(function (result) {
document.getElementById("error").innerText = result.data;
})
//java语言
String userName = req.getParameter("name");
22.axios
1)Axios是一个基于Promise的JavaScript HTTP客户端,旨在简化前端应用程序与服务器之间的HTTP通信。
2)引用地址
中网:https://unpkg.com/axios/dist/axios.min.js
外网:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
3)AJAX 中文学习网站https://www.runoob.com/php/php-ajax-intro.html
axios官方网站:https://axios-http.com/docs/intro
23.json
JavaScript Object Notation (JavaScript 对象表示语法)
是存储和交换文本信息的语法。类似XML(可扩展标记语言eXtensible Markup Language)
json比xml更小 更快 更易解析★★
1.json是由键值对构成的
2.键要用引号,单双都行,也可以不引
3.取值范围:数字,字符串,逻辑值,数组,对象
第一二章 Spring5-----------------------------
1.Sring 概念★★★★★
是一个JavaEE开源的轻量级别的框架,让编码变得更加简单
核心组件IOC容器和Aop面向切面编程
1)IOC控制反转:
把整个对象创建的过程,统一交给SpringIOC容器管理
底层是使用 反射+工厂 模式实现
2)Aop面向切面编程:
对功能(方法)前后实现增强,比如打印日志、事务管理、权限管理
底层是基于动态代理模式实现的,减少代码的冗余性问题
2.Spring 优势
1)方法解耦,简化开发
2)Aop技术的支持
3)提供事务支持
4Junit 单元测试
5)方便整合其它框架(MyBatis,SpringMVC,SpringBoot,SpringCloud,Redis等)
6)降低JavaEEapi开发使用的难度(Spring对很多复杂的api实现了封装)
3.Sping 与 SpringBoot 关系
SpringBoot直接采用注解化的方式启动,
底层会依赖于Spring/SpringMVC注解方式实现包装的。
比如:@Bean
4.Spring的jar包下载
javadoc --Api文档介绍
Sources --jar的源代码 .java
直接命名为.jar 包的格式 --class文件
5.Spring 环境搭建 步骤
1)引入spring依赖(maven中)
<dependencies>
<!--
这个jar 文件包含Spring 框架基本的核心工具类。Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类。
外部依赖Commons Logging, (Log4J)。
-->
org.springframework spring-core 5.2.1.RELEASE
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!--
这个jar 文件为Spring 核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类,JDNI 所需的全部类,instrumentation组件以及校验Validation 方面的相关类。
外部依赖spring-beans, (spring-aop)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
2)创建 spring.xml配置文件,注入bean信息
<!-- 配置注入spring bean对象
id: 不能重复 首写字母必须小写
class:类的完整路径
-->
<bean id="userEntity" class="com.mykt.entity.UserEntity"></bean>
3)获取bean对象
//1.加载到sring配置文件 sping.xml
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("sping.xml");
//2.根据bean的id获取对象
UserEntity userEntity = classPathXmlApplicationContext.getBean("userEntity", UserEntity.class);
System.out.println(userEntity);
userEntity.addUser();
6.创建对象的方式有哪些?
1)单独new方式 --耦合度太高
每次单独new对象,没有实现统一管理对象,
如果后期类的名称(UserDao)信息发生变化,需要修改的地方较多,耦合度太高
2)工厂模式 --降低耦合度
概念:统一的管理和维护每个对象的创建和使用过程
//dao (使用UserDaoFactory)
class UserDaoFactory{
public static UserDao getUserDao(){
return new UserDao();
}
}
//service
class UserService{
UserDao userDao = UserDaoFactory.getUserDao(){
userDao.addUser();
}
}
3)反射的方式 --class反射技术,降低代码的耦合度
7.SpringIOC 容器底层实现原理
反射+工厂模式+解析xml技术实现
1)使用解析xml技术 解析spring.xml配置文件
2)获取<bean id="" class="" />类的完整路径地址
3)使用到反射技术初始化对象
4)需要使用工厂模式封装初始化对象
Dome4jClass
E:\project\java\mykt-spring5\src\main\java\com\mykt\utils\Dome4jClass.java
8.什么是bean管理?
1)Spring创建对象(使用反射技术实现)
2)向对象注入属性
1.基于xml方式注入
2.DI依赖注入
对象的属性注入值(spring实现)
1.无参构造函数初始化 底层是实体类的set方法
<bean id="bookEntity" class="com.mykt.entity.BookEntity">
<property name="bookName" value="msk"></property>
<property name="price" value="108.00"></property>
</bean>
2.有参构造函数初始化(没有无参构造函数)
<bean id="orderEntity" class="com.mykt.entity.OrderEntity">
<constructor-arg name="orderId" value="123456"></constructor-arg>
<constructor-arg name="orderName" value="msk"></constructor-arg>
<!-- 上述代码可以变成下面代码:这里也可以根据index值 注入属性-->
<constructor-arg index="0" value="666"></constructor-arg>
<constructor-arg index="1" value="xiaojin"></constructor-arg>
</bean>
3.使用p标签注入属性值 底层还是实体类的set方法
//使用这个方法 必须存在set方法
//引入用法
xmlns:p="http://www.springframework.org/schema/p"
//注值
<bean id="bookEntity" class="com.mykt.entity.BookEntity" p:bookName="11111" p:price="66.00"></bean>
4.注入特殊符号给bean属性作为值
<property name="bookName" value="<<北京>>"></property>
方法1.转义特殊符号
<property name="bookName" value="<<北京>>"></property>
方法2.使用cdata
<property name="bookName">
<value><![CDATA[<<北京>>]]> </value>
</property>
5.字面量: java中 直接为属性赋值
private String bookName = null;
9.spring 注入属性外部bean
使用属性注入的方式注入属性值: (必须要有set方法)
1)数据库访问层dao 创建Dao接口类 并创建实现接口类
2)业务逻辑层service 创建Service类 将Dao接口类属性私有化,并注入属性值
private MemberDao memberDao;
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
3)spring.xml 文件配置
1.MemberService 注入到ioc容器中–>
<bean id="memberService" class="com.mykt.service.MemberService">
//ref: 2中memberDao在ioc容器中注入的bean id-->
<property name="memberDao" ref="memberDao"></property>
</bean>
2.MemberDaoImpl 注入到ioc容器中(MemberDao是接口类,不能直接注入)-->
<bean id="memberDao" class="com.mykt.dao.MemberDaoImpl"></bean>
10.spring 注入属性内部bean (表嵌套)
方法1.注入属性内部bean–>
<bean id="empEntity" class="com.mykt.entity.EmpEntity">
<property name="name" value="xiaojin"></property>
<property name="address" value="四川成都"></property>
<property name="deptEntity">
<bean id="deptEntity" class="com.mykt.entity.DeptEntity">
<property name="name" value="xiaojin"></property>
</bean>
</property>
</bean>
方法2.级联赋值的形式
<bean id="empEntity" class="com.mykt.entity.EmpEntity">
<property name="name" value="xiaojin"></property>
<property name="address" value="四川成都"></property>
<!--ref: 2中memberDao在ioc容器中注入的bean id-->
<property name="deptEntity" ref="deptEntity"></property>
//这里必须提前设置 getDeptEntity() 方法
<property name="deptEntity.name" value="IT部门"></property>
</bean>
//2
<bean id="deptEntity" class="com.mykt.entity.DeptEntity"></bean>
11.注入集合类型属性
1)注入数组类型属性
<property name="arrays">
<array>
<value>array01</value>
<value>array02</value>
</array>
</property>
2)注入list集合类型
<property name="list">
<list>
<value>list01</value>
<value>list02</value>
</list>
</property>
3)注入map集合类型属性
<property name="map">
<map>
<entry key="xiaojin" value="小瑾"></entry>
<entry key="xiaojun" value="小军"></entry>
</map>
</property>
4)注入set集合属性
<property name="set">
<set>
<value>set01</value>
<value>set02</value>
</set>
</property>
12.注入集合类型为对象
1)步骤一:将集合类型注入Ioc容器bean
<bean id="courseEntity_java" class="com.mykt.entity.CourseEntity">
<property name="name" value="mykt-java"></property>
</bean>
2)步骤二:注入list类型对象为 ref bean 引入对象
<property name="courses">
<list>
<ref bean="courseEntity_java"></ref>
<ref bean="courseEntity_dashuju"></ref>
</list>
</property>
3)对list公共部分实现提取(公共list配置)
//必须正确引入util标签
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
<util:list id="list">
<value>xj01</value>
<value>xj02</value>
</util:list>
<bean id="stuEntity" class="com.mykt.entity.StuEntity">
<property name="list" ref="list"></property>
</bean>
13.bean的作用域
spring 在默认情况下,bean的作用域是单例, 节约服务器内存
1)单例的作用域
每次调用getbean 方法获取对象都是同一个对象
2)多例的作用域
每次调用getbean 方法获取对象都是一个新的对象
//开启多例模式
<bean id="stuEntity" class="com.mykt.entity.StuEntity" scope="prototype">
14.spring bean的生命周期★★★★★
1.使用反射技术初始化该对象(执行无参构造函数)
2.给该对象的属性赋值(反射技术实现 调用set方法)
3.调用bean init方法
4.获取使用到的MemberEntity
com.mykt.entity.MemberEntity@73846619
5.调用bean destory方法
<!--init-method="init" 初始化的方法设置-->
<bean id="memberEntity" class="com.mykt.entity.MemberEntity" init-method="init" destroy-method="destory">
<property name="name" value="xiaojin"></property>
</bean>
15.bean的生命周期 后置处理器(额外的扩展功能支持)★★★★★
BeanPostProcessor 后置处理器
<!--注册bean对象后置处理器-->
<!--所有的bean都会经过这个后置处理器-->
<bean id="myPostProcessor" class="com.mykt.bean.MyPostProcessor"></bean>
生命周期:
1.使用反射技术初始化该对象(执行无参构造函数)
2.给该对象的属性赋值(反射技术实现 调用set方法)
【后置处理器 调用init方法之前执行操作...】
3.调用bean init方法
【后置处理器 调用init方法之后执行操作...】
4.获取使用到的MemberEntity
com.mykt.entity.MemberEntity@73846619
5.调用bean destory方法
16.看源码的方法
1)报错法
2)断点调用链法
17.spring bean 的自动装配
1)是spring根据装配的规则自动为属性注入值
2)装配的规则:属性的名称 或属性的类型
3)完全自动装配 使用bean 标签属性 autowire
1.autowire="byName" 根据属性的名称自动装配
属性的名称 要与 bean的id的值完全一致
2.autowire="byType" 根据属性的类型自动装配
这时候 属性的名称 与 bean的id的值可以不一致
但是不能配置多个类型一样的对象,不然不知道装配哪一个
3.代码案例
<bean id="empEntity" class="com.mykt.entity.EmpEntity" autowire="byType"></bean>
<bean id="deptEntity1" class="com.mykt.entity.DeptEntity">
<property name="name" value="教育部门"></property>
</bean>
18.java 反射技术
1)作用:
可以动态获取class相关信息(成员、方法、属性)
可以灵活调用成员方法,或者给成员属性赋值
Class.forName 初始化对象(可以创建对象)
2)类加载器:
将class文件读取到内存中(程序中)
19.class的获取方法
1)类名.class
2)new 对象, 对象.getClass()
3)Class.forName(“类的完整路径地址(包名称+类名称组合)”)--使用最多
20.反射机制 应用场景
1)JDBC中Class.forName("com.mysql.jdbc.Driver") --反射技术加载mysql驱动
2)Spring 底层基于反射 初始化对象
3)(写一套自己的)第三方框架扩展功能
21.spring AOP技术
1)底层是基于 代理设计模式 封装的★★
2)aop技术在我们调用目标方法之前和之后 可以做一些处理的操作
3)代理模式:
静态代理
自己编写代理类,两种方法编写静态代理类
实现接口
继承(不常用)目标类
动态代理
jdk动态代理(使用较多)
cglib动态代理
4)使用场景:日志打印 事务管理 自定义实现
22.代理设计模式
1)代理模式 主要对方法执行之前与之后实现增强
2)使用场景:日志打印 spring的事务 自定义注解 代理数据源 全局捕获异常 Rpc远程调用接口 实现aop
23.动态代理
不用自己编写代理类
1)jdk动态代理(实现接口,使用较多)
1)JDK动态代理的一般步骤如下:
1.创建被代理的接口和类;
2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;
实现原理:利用拦截器机制必须实现InvocationHandler接口中的invoke方法实现对我们的目标方法增强。
2)使用jdk动态代理时,注意,要让目标对象实现接口(因为生成代理类时,需要使用到 实现目标对象的接口,方便使用接口调用目标对象的方法)
2)cglib动态代理(继承)
1.底层基于asm生成字节码class实现
2.cglib 与 jdk 动态代理 到底有哪些区别?
jdk动态代理底层基于反射方式 调用目标方法, jdk7之前效率非常低 后期优化了
Cglib 底层是直接采用建立fastclass索引的方式调用目标方法
jdk7之前, Cglib动态代理效率比jdk高
jdk7开始,jdk动态代理效率更高了
3.如何判断spring底层采用 cglib代理类 还是 jdk动态代理?
cglib动态代理生成的代理类 直接继承被代理类(实现继承方式代理)
jdk动态代理生成代理类 实现 被代理实现的接口(实现接口方式代理)
判断方法总结:★★★★★
如果被代理类 没有实现接口 则使用的是cglib动态代理
如果被代理类 有实现接口的情况下 则使用的是jdk动态代理
在开发中 大多使用接口开发 所有使用更多的是jdk动态代理
24.AOP技术
1)连接点
该类中需要增强的方法 该方法称为连接点
2)通知
在调用方法前后执行代码,分为:
1.前置通知
2.后置通知
3.环绕通知★
--在被代理方法前后执行 (前置 + 后置组合 在企业开发中用得更多)
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知调用目标方法之前...");
//调用目标方法的返回结果 如果目标方法没有返回结果,返回null
Object result = proceedingJoinPoint.proceed();
System.out.println("环绕通知调用目标方法之后...");
return result;
}
4.异常通知★--在调用被代理方法时抛出异常了,就会走异常通知
5.最终通知
//通知执行顺序
1.若有异常通知:
环绕通知调用目标方法之前...
在目标方法之前执行...
AfterThrowing 异常通知
在目标方法之后执行...
2.没异常通知的情况
环绕通知调用目标方法之前...
在目标方法之前执行...
addUser
在目标方法之后执行...
环绕通知调用目标方法之后...
3)切点
实际被增强的方法
4)切面
是把通知真正应用到的过程
25.aop 定义切入点 @Before(value = “拦截语句”)
1.拦截语句:
execution(public.String.com.mykt.service.UserService.addUser()) --无参
execution(public.String.com.mykt.service.UserService.addUser(Integer)) --有参 Integer类型
execution(public.String.com.mykt.service.UserService.addUser(..)) --所有参数
execution(* com.mykt.service.UserService.*(..)) --拦截类中所有方法
execution(* com.mykt.service.*.*(..)) --拦截包中所有类 的所有方法
2.举例:
@Before(value = "execution(* com.mykt.service.UserService.*(..))")
public void before() {System.out.println("在目标方法之前执行...");}
@After(value = "execution(* com.mykt.service.UserService.*(..))")
public void After() {
System.out.println("在目标方法之后执行...");
}
3.案例:
E:\project\java\mykt-designmode\src\main\java\com\mykt\proxy\UserProxy.java
第十三章 spring mvc-------------------------------
1.mvc 三层架构模式
1)MVC架构
M Model模型层 (业务逻辑层+数据库访问层组合)
V View视图层 前端 (网页,jsp, 用来展示模型中的数据)
C controller 控制器 (底层基于serclet做封装的)
2.前后端分离开发模式
让专业的人做专业的事。
1)前端:jsp, vue, elementUi vant 网页数据 例如html js css
2)后端:接口中数据 springmvc + mybatis
3)前后端分离开发模式:
View视图层---jsp, js, css
controller --控制层springmvc(底层基于 servlet 控制页面跳转 控制展示数据
后端程序员:
@Restcontroller
controller-----返回json数据给前端
service--------业务逻辑层
dao------------数据库访问层
3.三层架构模式 和mvc模式的区别
1.三层是基于业务逻辑来划分的,mvc是基于页面来分的
2.三层是软件架构,通过接口实现编程。mvc是一种复合设计模式 一种解决方案
3.三层模式是体系结构模式,mvc是设计模式
4.三层模式又可归于部署模式,mvc归于表示模式
4.springmvc项目 注解方式的 启动步骤
E:\project\java\mykt-springmvc-demo01
1)定义 Controller 控制类
@Controller --这个注解表示该类是springmvc控制类
@RequestMapping("/getMyktUser")---定义url映射
@ResponseBody --表示该接口返回json数据
2)配置 SpringMVCConfig.xml
@Configuration --是定义SpringMVCConfig.xml配置文件
@ComponentScan("com.mykt.controller") --规定扫包范围 将控制类注入到IOC容器中
3)Servlet 初始化
继承 AbstractDispatcherServletInitializer 类
// 注册 springmvc confiig 配置类
AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
annotationConfigWebApplicationContext.register(SpringMVCConfig.class);
return annotationConfigWebApplicationContext;
// 拦截所有的 Servlet 请求
return new String[] {"/"};
4)配置maven run
5)易错点
1.扫包范围容易写错,没有将控制类注入到IOC容器中
@ComponentScan("com.mykt.controller")
2.没有在控制类上面加 @Controller 注解
3.前面1和2的错误都会 导致接口访问404
No converter found for return value of type: class java.util.HashMap
5.springmvc项目 xml方式的 启动步骤
E:\project\java\mykt-springmvc-demo02
6.springmvc项目 请求和响应
1)请求
@RequestMapping("/user") 可以在类上面注解 定义根url
@RequestMapping("/add")
url不允许重复, /user/add
2)响应
@ResponseBody
3)代码
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/add")
@ResponseBody
public String add() {
return "add ok";
}
@RequestMapping("/delete")
@ResponseBody
public String delete() {
return "delete ok";
}
}
7.springmvc 请求类型
@RequestMapping("/addMember") --默认是所有请求类型都可以get post delete
@RequestMapping(value = "/addMember", method = RequestMethod.POST) --定义请求类型只能是post
8.springmvc 5种接收参数类型(使用较少)
1)普通参数
//@RequestParam(name = "name") --可以重定义参数的名字
//@RequestParam(required = false) --设置参数 是否是必须要传的,值是false 就可以不用传 返回值为null
@RequestMapping("/userAdd")
@ResponseBody
public String userAdd(@RequestParam(name = "name", required = false) String userName, @RequestParam(name = "age", required = false) Integer userAge) {
return "name:" + userName + "age:" + userAge;
}
2)对象参数
请求参数名与形参对象属性名相同,定义对象类型形参即可接收参数
3)对象嵌套对象参数
对象里面将 嵌套的类 属性私有化
传参的时候,加上嵌套类前缀:
如:?name=xiaojin&age=12&inforUserEntity.address=beijin
4)数组参数
@RequestMapping("/demo04")
@ResponseBody
public String demo04(String[] arrays) {
return Arrays.toString(arrays);
}
传参:?arrays=xiaojin&arrays=c
返回:[xiaojin, hhh]
5)集合普通参数
@RequestMapping("/demo05")
@ResponseBody
//集合必须在参数前加上@RequestParam 不然会报错
public String demo05(@RequestParam List<String> list) {
return Arrays.toString(list.toArray());
}
9.springmvc 接收json数据
1)对象json数据(使用最多★★★
@RequestBody ---表示接收json数据 会根据json数据自动反序列化成对象
@RequestMapping("/demo06")
// @ResponseBody 这个可以在定义类前统一注释 @RestController
public String demo06(@RequestBody UserEntity user) {
return user.toString();
}
2)mapjson数据
//Map<String, Object> 数据类型要一致,大多都是String类型
//但是如果参数中有对象 类型可定义成Object
@RequestMapping("/demo07")
public String demo07(@RequestBody Map<String, Object> paramMap) {
return paramMap.toString();
}
3)listjson数据
public String demo08(@RequestBody List<String> list) {
return list.toString();
}
//listjson数据 里面嵌套对象json
[
{
"name":"xj",
"age":12
},
{
"name":"xhh",
"age":33
}
]
10.springmvc response响应数据
@RequestBody 请求参数转换成json格式
@ResponseBody 这个注解自动把请求后返回的数据转换成 json 数据
如果没有这个注释,该请求的结果是跳转到具体的视图层做页面渲染
@RestController 标记控制类中的所有方法都返回json数据★★★
11.获取 httprequest/response 三种方式
1)public String httprequestDemo01(HttpServletRequest request, HttpServletResponse response)
2)HttpServletRequest httpServletRequest = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
11.RestfulApi 接口
1)获取数据
// @PathVariable("id") Integer id --注明id对应地址里面id的值
@GetMapping("/user/{id}")
public UserEntity getUser(@PathVariable("id") Integer id) {
return new UserEntity("mykt" + id, 22);
}
2)添加数据
@PostMapping("/user")
public String addUser(@RequestBody UserEntity userEntity) {
return "ok";
}
3)修改数据
@PutMapping("/user")
public String updateUser(@RequestBody UserEntity userEntity) {
return "put---ok";
}
4)删除数据
@DeleteMapping("/user/{id}")
public String delUser(@PathVariable("id") Integer id) {
return "del----ok" + id;
}
12.ssm增删改查项目案例
地址:E:\project\java\mykt-ssm
1)整合maven依赖 spring/springmvc/mybatis
2)创建 数据库层
实体类层 --entity
数据库访问层 --mapper
业务逻辑层 --service
控制层 --controller
3)SSM环境的整合之配置整合
JdbcConfig
MybatisConfig
SpringConfig
SpringMVCConfig
ServletConfig
4)接口响应状态码
统一规范返回数据的格式,此处以json格式为例。返回数据应包含:返回状态码、返回状态信息、具体数据。
{
"code":"200",
"msg":"ok",
"data": {
//json格式的具体数据
}
}
5)整合全局捕获异常
13.全局捕获异常
当系统发生错误时,统一将系统错误日志 返回输出
@ControllerAdvice
public class GlobalExceptionHandler extends BaseController {
@ResponseBody //返回json数据
@ExceptionHandler(value = Exception.class) //拦截Exception异常
public HashMap<String, Object> handleException(Exception exception) {
System.out.println("e: " + exception);
return setResultError("系统发生了错误");
}
}
第十四章 mybatis-----------------------
1.什么是mybatis?
是一个用java编写的持久层框架(数据库层框架)
它使用ORM实现了结果集的封装
2.mybatis 环境搭建
1.引入mybatis相关依赖
2.mybatis-config.xml(该配置文件名称是可以改) 存放就是我们数据库相关连接信息
3.定义mapper ----编写我们mybatis 相关 sql语句 每个表 对应一个mapper
4.定义java对象--需要注意下 类中的 成员属性与数据库表中字段 映射 默认 类中的 成员属性数据库表中字段名称对应的。
5.使用 mybatis api开始执行该 sql语句即可 得到结果
常见错误:
Mapped Statements collection does not contain value for getAllUsers
没有找getAllUsers 到对应的sql语句
原因是没有将 Mapped 交给 mybatis 扫描到
6.综合案例 E:\project\java\mtkt-mybetis-demo\mybatis-demo02
3.mapper代理开发模式
1.mapper接口方式开发整合就必须是对应的mapper接口的全限定类名
2.接口中的方法与映射文件中的SQL语句的ID
3.需要在mybatis-config.xml 新增 加载该userMaaper
<mappers>
<mapper resource="mybatis/userMapper.xml"/>
</mappers>
4.定义mapper 接口 需要考虑方法的名称与userMapper.xml的 sql id名称保持一致。
4.MyBatis - 映射文件标签
select:映射查询语句
insert:映射插入语句
update:映射更新语句
delete:映射删除语句
sql:可以重用的 sql 代码块
resultMap:最复杂,最有力量的元素,用来描述如何从数据库结果集中加载你的对象
cache:配置给定命名空间的缓存
cache-ref:从其他命名空间引用缓存配置
5.select 标签的属性信息(userMapper.xml)
<select
<!--
1. id(必须配置)
id是命名空间中的唯一标识符,可被用来代表这条语句
一个命名空间(namespace)对应一个dao接口
这个id也应该对应dao里面的某个方法(sql相当于方法的实现),因此id应该与方法名一致
-->
id="selectUser"
<!--
2. parapeterType(可选配置,默认由mybatis自动选择处理)
将要传入语句的参数的完全限定名或别名,如果不配置,mybatis会通过ParamterHandler根据参数类型默认选择合适的typeHandler进行处理
paramterType 主要指定参数类型,可以是int, short, long, string等类型,也可以是复杂类型(如对象)
-->
parapeterType="int"
<!--
3. resultType(resultType 与 resultMap 二选一配置)
用来指定返回类型,指定的类型可以是基本类型,也可以是java容器,也可以是javabean
-->
resultType="hashmap"
<!--
4. resultMap(resultType 与 resultMap 二选一配置)
用于引用我们通过 resultMap 标签定义的映射类型,这也是mybatis组件高级复杂映射的关键
-->
resultMap="USER_RESULT_MAP"
<!--
5. flushCache(可选配置)
将其设置为true,任何时候语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false
-->
flushCache="false"
<!--
6. useCache(可选配置)
将其设置为true,会导致本条语句的结果被二级缓存,默认值:对select元素为true
-->
useCache="true"
<!--
7. timeout(可选配置)
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数,默认值为:unset(依赖驱动)
-->
timeout="10000"
<!--
8. fetchSize(可选配置)
这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为:unset(依赖驱动)
-->
fetchSize="256"
<!--
9. statementType(可选配置)
STATEMENT, PREPARED或CALLABLE的一种,这会让MyBatis使用选择Statement, PrearedStatement或CallableStatement,默认值:PREPARED
-->
statementType="PREPARED"
<!--
10. resultSetType(可选配置)
FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为:unset(依赖驱动)
-->
resultSetType="FORWORD_ONLY"
></select>
6.resultMap 标签的属性信息
<!--
1. type 对应的返回类型,可以是javabean, 也可以是其它
2. id 必须唯一, 用于标示这个resultMap的唯一性,在使用resultMap的时候,就是通过id引用
3. extends 继承其他resultMap标签
-->
<resultMap type="" id="" extends="">
<!--
1. id 唯一性,注意啦,这个id用于标示这个javabean对象的唯一性, 不一定会是数据库的主键(不要把它理解为数据库对应表的主键)
2. property 属性对应javabean的属性名
3. column 对应数据库表的列名
(这样,当javabean的属性与数据库对应表的列名不一致的时候,就能通过指定这个保持正常映射了)
-->
<id property="" column=""/>
<!--
result 与id相比,对应普通属性
-->
<result property="" column=""/>
<!--
constructor 对应javabean中的构造方法
-->
<constructor>
<!-- idArg 对应构造方法中的id参数 -->
<idArg column=""/>
<!-- arg 对应构造方法中的普通参数 -->
<arg column=""/>
</constructor>
<!--
collection 为关联关系,是实现一对多的关键
1. property 为javabean中容器对应字段名
2. ofType 指定集合中元素的对象类型
3. select 使用另一个查询封装的结果
4. column 为数据库中的列名,与select配合使用
-->
<collection property="" column="" ofType="" select="">
<!--
当使用select属性时,无需下面的配置
-->
<id property="" column=""/>
<result property="" column=""/>
</collection>
<!--
association 为关联关系,是实现一对一的关键
1. property 为javabean中容器对应字段名
2. javaType 指定关联的类型,当使用select属性时,无需指定关联的类型
3. select 使用另一个select查询封装的结果
4. column 为数据库中的列名,与select配合使用
-->
<association property="" column="" javaType="" select="">
<!--
使用select属性时,无需下面的配置
-->
<id property="" column=""/>
<result property="" column=""/>
</association>
</resultMap>
7.insert 标签的属性信息
<insert
<!--
同 select 标签
-->
id="insertProject"
<!--
同 select 标签
-->
paramterType="projectInfo"
<!--
1. useGeneratedKeys(可选配置,与 keyProperty 相配合)
设置为true,并将 keyProperty 属性设为数据库主键对应的实体对象的属性名称
-->
useGeneratedKeys="true"
<!--
2. keyProperty(可选配置,与 useGeneratedKeys 相配合)
用于获取数据库自动生成的主键
-->
keyProperty="projectId"
>
8.重用 sql 标签
<sql id="userColumns">id,username,password</sql>
9.完全限定名使用别名替代
每个 sql 映射文件的要元素中,都需要指定一个名称空间,用以确保每个映射语句的 id 属性不会重复。
如 <mapper namespace="com.mayikt.mapper.UserMapper">
在 Java 代码中引用某个 sql 映射时,使用的亦是含有名称空间的全路径。
如 session.update("com.mayikt.mapper.UserMapper.udpateUser", user);
10.mybatis动态条件查询
1)mysql 加上输出日志
<settings>
<!-- 打印sql日志 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
2)语法:
<select id="getByIdFlightDynamicParameter" parameterType="com.mayikt.entity.FlightEntity"
resultMap="flightEntityMap">
SELECT * from mayikt_flight where
<if test="company!=null and company!=''">
company=#{company}
</if>
<if test="departureAirport!=null and departureAirport!=''">
and departure_airport=#{departureAirport}
</if>
<if test="arriveAirport!=null and arriveAirport!=''">
and arrive_airport=#{arriveAirport};
</if>
</select>
//缺陷 如果没有传递company 导致sql报错
3)可以使用<where></where>
<select id="getByIdFlightDynamicParameter" parameterType="com.mayikt.entity.FlightEntity"
resultMap="flightEntityMap">
SELECT * from mayikt_flight
<where>
<if test="company!=null and company!=''">
and company=#{company}
</if>
<if test="departureAirport!=null and departureAirport!=''">
and departure_airport=#{departureAirport}
</if>
<if test="arriveAirport!=null and arriveAirport!=''">
and arrive_airport=#{arriveAirport};
</if>
</where>
</select>
第十五章 spring boot---------------------------
问题:使用IDEA的Spring Initializr 创建 Spring Boot突然发现JDK版本没有17以下的版本了?
解决:更换Server URL源改如下地址即可,如果想换回来填入网址2
网址1:https://start.aliyun.com/
网址2:https://start.spring.io/
1.spring boot 基本介绍
1)ssm框架 spring springmvc mybatis, xml方式配置整合复杂 繁琐
2)spring boot 是快速开发的框架,能帮助程序员快速整合第三方框架
底层原理是:封装maven依赖实现
3)特性
1.简化xml配置
2.全部采用注解的方式配置
3.内嵌入 Tomcat服务器
4.能快速开发
5.默认依赖组件 spring-boot-starter-web 已经帮我们整合好了已经帮我们整合好了springmvc框架框架
2.spring boot 与spring Cloud之间的区别
spring Cloud 微服务解决框架 微服务技术解决方案
spring Cloud RPC远程调用接口 feign客户端
接口的协议采用http协议
spring Cloud 依赖于spring boot
spring boot 默认情况下已经整合好了springmvc框架
spring Cloud编写接口就用到了springmvc
3.Spring boot依赖引入
//parent 使后面引入的依赖都不需要写版本号
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependencies>
<!--spring-boot里面默认自带了Tomcat-->
<!--默认整合好了spring 和 springmvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
4.相关注释
1)@RestController //响应json数据的 Controller (这个注释是springmvc提供的)
2)@EnableAutoConfiguration //动态开启
3)@ComponentScan("com.mykt.service") //设置扫包范围,可以响应service包下其它页面
4)@SpringBootConfiguration 是下面两个注释的组合
@EnableAutoConfiguration
@ComponentScan
// 扫包范围是 当前 启动类 同级包或者子包下面
//启动方法示例:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
5)@Value() ---给属性注入值(单个注入)
@Value("${mykt.name}") //注入yml文件中的mykt.name的值
private String name;
6)@ConfigurationProperties() --给属性注入值 (yml文件整体注入)
@ConfigurationProperties(prefix = "mykt")
5.SpringBoot 整合静态资源
传统放在resources目录下static目录里 就可以访问到
但是这种方法占当前服务器带宽,访问的人多了,就会比较卡,所以需要结合cdn
微服务项目
前后端分离
前端---vue 前端工程师
后端---SpringBoot 后端工程师
动静分离 部署在cdn上
cdn 减少带宽距离传输 减少自己服务器带宽
6.SpringBoot 支持两种配置方式
1)YML ★
application.yml 配置
bootStrap.yml 会被优先加载,它由父类SpringApplicationContext 加载
bootStrap.yml 用于应用程序上下文的引导阶段
YML 公司使用较多,减少配置文件的重复性
//示例
mykt:
name: xiaojin
age: 22
2)Properties
application.properties 配置
//示例
mykt.name=mykt
mykt.age=22
3)区别
YML公司使用较多,减少配置文件的重复性
4)Properties 文件转YML文件网址: https://www.toyaml.com/
7.SpringBoot 整合模版渲染引擎框架
1)Thyme leaf
2)FreeMarker
3)Velocity
4)Groovy
5)Mustache
8.热部署框架 devtools
1)热部署:修改java类 或页面或静态文件,不需要手动重启服务
原理:类加载器
2)只适合于本地开发环境
9.整合 lombok 注意事项
1)需要安装插件
2)引入lombok 依赖
3)注解
@Data —(@Getter @Setter) 自动生成get和set方法
@Slf4j —自动打印日志 log.info()
4)lombok底层原理
实际上在写代码时,不需要写get和set方法的,成员自带了属性的,
lombok在编译class文件时,帮我们自动生成这两个方法放到class文件中
10.配置文件占位符
在SpringBoot的配置文件中,可以使用SpringBoot提供的一些随机数
服务器开启一次才生成一次?服务器没有重启,访问都是和之前的数一样
age:
${random.int(10)} --生成10以内的随机数
${random.int} --生成随机数
${random.long} ${random.int[1024,65536]}
11.整合多环境不同配置
1)设置读取文件
application.yml--使用spring注解读取配置文件
## application-dev.yml 开发环境配置文件
## application-prd.yml 生产环境配置文件
## application-test.yml 测试环境配置文件
spring:
profiles:
active: prd //读取生产环境配置文件`在这里插入代码片`
2)配置环境文件
application-dev.yml--开发环境配置文件
mykt:
name: devxiaojin
age: ${random.int(10)}
address: dev.xiaojin.com
application-prd.yml--生产环境配置文件
application-test.yml--测试环境配置文件
12.设置端口号和上下文访问路径
application.yml文件中配置:
server:
## 设置端口号
port: 8081
servlet:
## 设置上下文访问路径
context-path: /mykt
13.log4j
log4j,log4j2,logback输出日志区别
学习地址:https://mp.weixin.qq.com/s/Xx7vNZv9qhYhp7zX0FKAvA
14.aop依赖引入
1)maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2)拷贝 WebLogAspect 类 (过滤拦截)
E:\project\java\Springboot-demo00\src\main\java\com\mykt\aop
15.定时任务注解
1)方法一
springboot自带,不需要再引入依赖
//定时任务注解 每隔3秒执行一次
@Scheduled(fixedRate = 3000)
public void taskService() {
log.info("<<定时任务执行>>"+System.currentTimeMillis());
}
//启动类 开启定时任务注解
@EnableScheduling
2)方法二
//@Scheduled 结合 网上生成cron定时语句
@Scheduled(cron = "0/2 * * * * ? ")
16.异步注解
1)@Async
实际上就是多线程封装的 springboot自带
异步线程执行方法可能会非常消耗cpu资源,
所有大的项目建议使用Mq异步实现
不建议使用线程,建议结合线程池使用异步注解 @Async
2)异步注解失效问题:
如果异步注解写在当前自己类,可能aop会失效,
无法拦截注解,导致异步注解失效
3)解决办法:
经过代理类调用接口
也就是 将异步的代码单独抽取成一个类调用接口
但是发起一次请求就会单独开启一个线程,消耗cpu资源
可以结合线程池优化 节约cpu资源
17.整合全局捕获异常
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public Map<String, Object> exceptionHandler() {
HashMap<String, Object> resultJson = new HashMap<>();
resultJson.put("code", 500);
resultJson.put("msg", "系统错误");
return resultJson;
}
}
18.打包运行
1)删除项目主目录下 target文件夹
2)打包 mvm clean package
3)运行 java -jar jar包的全路径
4)注意在maven中配置 程序主入口
<mainClass>com.mykt.App</mainClass>