java基础

MySQL数据库

一、SQL

SQL是结构化查询语言,用于关系型数据库中数据的各种操作

SQL它是一种语言标准,没有强制性

二、DBMS

DBMS指数据库产品,常见有Oracle、SQL Server、DB2、MySQL、PostgreSQL

三、数据的类型

数据的类型,分为:

  • 数值

    int 整数型

    double 浮点型

  • 文本

    varchar 可变长度

    char 不可变长度

  • 时间

    date 日期

    time 时间日期

    times 时间戳

  • 二进制数据

  • 字符流数据....

四、表的约束

4.1 主键约束

主键约束列数据,具有一行数据的“代表”资格

【特点】:必须录入且不可重复,一张表最多只能设置一个主键约束

  • 一个主键约束可以同时约束多个列

【规范】:进了使用无现实意义的数据列为主键列

4.2 外键约束

某一个列的数据,来源于其他表的主键列

4.3 唯一约束

约束所在列数据唯一,不可重复,但能为空

4.4 非空约束

约束所在列的数据必须录入,不能为空

4.5 检查约束(Mysql不支持)

五、SQL的分类

5.1 DDL语句

数据定义语句,对数据库对象(表,视图,索引)进行创建,修改,删除等操作的语句

  • create、alter、drop、truncate

5.2 DML语句

数据库操作语句,对数据进行增删改操作

  • delete、insert、update

5.3 DQL语句

数据库查询语句,对数据进行查询

  • select

5.4 DCL语句

数据控制语句,对用户进行授权、收回权限等

  • grant、revoke

5.5 TCL语句

事务控制语句,对事务操作

  • commit、rollback、savepoint

六、DML语句

6.1 增加 insert

insert into 表名 values(值1,值2,值3)
insert into 表名(列1,列2,列3) values(值1,值2,值3)

6.2 删除 delete

delete from 表名 where 条件

6.3 修改 update

update 表名 set 列1=新值1,列2=新值2,列3=新值3 where 条件

七、查询

WHERE不能用聚合函数+别名

7.1去除空 IFNULL

IFNULL(a,b)如果a为null,设为b

7.2 去重 DISTINCT

写在Select后

7.3 逻辑查询

7.4 特殊查询

(1)BETWEEN...AND

查询在...之间的数据,如:时间/日期

Select * from table where 字段 BETWEEN 值1 AND 值2
(2)IN

查询在....之中的数据,满足其中一个

select * from table where 字段 in {a1,a2,a3}
(3)LIKE

查询像...一样,模糊查询

  • 通配符

    • % 此处有任意数量字符

    • _ 此处有一个数量字符

select * from table where 字段 like '%xxx%'
(4)IS NULL

查询为NULL的行

  • NULL值进行计算,结果一定为NULL

  • 任意两个NULL不相等,因而不能写 = NULL

select * from table where 字段 is NULL
(5)NOT
  • 用于以上特殊的筛选中, 表示不....

select * from table where 字段 is NOT NULL
--查询字段不为空的

七、查询结果排序

7.1比较规则

  • 数值:自然大小

  • 文本:字典顺序

  • 事件:越未来的越大

7.2 ORDER BY子句

ORDER BY(数据列|表达式|别名|查询结果序号)(ASC|DESC)
-- 数据列:列名
-- 表达式:经过表达式得到的新值
-- 别名:利用as设置的别名
-- 查询结果序号:列名的编号 1开始(不建议,可读性差)
  • 单个列排序

  • 多个列排序(先对列1进行排序,再对列2进行排序)

八、分组查询

8.1 聚合函数(分组函数)

单行函数:一行数据经过单行函数的计算得到一行结果 如:IFNULL(大多数单行函数,都是方言)

聚合函数:由多行数据经过计算得到一行结果 (常用于统计数据)

1️⃣常用函数

求平均--AVG

求和--SUM

求最大--MAX

求最小--MIN

计数--COUNT

  • COUNT(列名/*)都可。COUT( * )效率更低

  • NULL值不参与计数

8.2 分组语句--GROUP BY

GROUP BY 列

  • 对“列“中数据分组归类,相同数据归类为一组

  • “列”中有N中数据,数据就被分为N组

    • 列值为NULL,也会单独进行统计

--查询各个部门的平均薪水
select depton,AVG(sal) from table group by depton;

❗:只有出现在GROPY BY后的列/聚合函数,才可以出现在SELECT中

8.3 对聚合函数的筛选--HAVING

聚合函数不允许在WHERE子句中进行筛选,只能利用HAVING进行筛选

  • HAVING子句是在group by后执行的

select 列名,聚合函数 from table group by 列名 having 条件

九、DQL各个子句的执行顺序

9.1书写顺序

select .. from table where .. gourp by ... having ... order by...

9.2执行顺序

先执行 from 
再执行 where
再执行 group by
再执行 having
再执行 select
再执行 order by

9.3 产生的问题?

  • 不能在WHERE中使用聚合函數?

  • 不能在WHERE中使用別名?

【最終案例】

--查询月薪高于1000的员工中,各个职位的人数
--显示人数多于1人的职位和人数,按照人数升序排序
select job,count(empno) as num from emp where sql > 1000 group by job having num > 1 order by num asc; 

十、多表查询

通过join将多张表的连接在一起

  • 表別名只能用空格生成

10.1 笛卡尔积 JOIN

将左侧所有数据和右侧所有数据进行一次关联

  • JOIN进行连接

SELECT *
FROM a 
JOIN b 
WHERE 条件

10.2 等值连接

将左侧右侧进行一次关联,并通过ON进行筛选

  • ON进行筛选

SELECT *
FROM a 
JOIN b ON (a.xx = b.xx)
....(可再添加JOIN 表名 ON ...=...)
WHERE 条件

10.3 外连接

外连接包含LEFT JOIN、RIGHT JOIN 、FULL JOIN(MySQL不支持)

LEFT JOIN :左侧数据显示完整

RIGHT JOIN :右侧数据显示完整

FULL JOIN:将两侧数据显示完整

【主要解决表连接时,NULL被忽略的问题】

--左连接:将JOION左侧表数据显示完整
SELECT *
FROM 表1 
LEFT JOIN 表2 
ON (表1.xxx = 表2.xxx)
WHERE ....
--右连接:将JOIN右侧表数据显示完整
SELECT *
FROM 表1
RIGHT JOIN 表2
ON (表1.xxx = 表2.xxx)
WHERE ....

【查询各个部门的员工人数,没有员工的部门也显示】

SELECT d.deptname,COUNT(e.deptno) FROM emp e RIGHT JOIN dept d ON e.empno = d.empno GROUP BY d.depname;

10.4 自连接

表关联自己

  • 即要进行的查询,是发生在同一张表上的

  • 只需要将一张表看做两张表,进行JOIN表连接

查询员工比上司入职早的记录

SELECT * 
FROM emp e1 
JOIN emp e2 ON e1.mgr = e2.empno
WHERE e1.hirerate > e2.hirerate

十一、子查询

当WHERE中的查询条件,是动态变化的值

  • 即是从另一次查询中得到的,就需要使用子查询

    • 【使用:】将子查询的语句,用()括起来即可

11.1 普通子查询

子查询结果只有一行时,称为普通子查询

  • 结果只有一个,可以使用常规符号 = > < <>

SELECT * 
FROM table
WHERE 列名 > (SELECT 仅一个列名 FROM table WHERE 条件)

11.2 多行子查询

子查询查询结果有多行时,称为多行子查询

  • 结果有多个,不能使用普通符号

  • 要使用 IN 或 NOT IN进行筛选

【注意】

  • 使用IN:子查询中包含NULL,NULL行不显示

  • 使用NOT IN:子查询中包含NULL,全部什么都不显示

(1)ANY

子查询结果其中之一

  • 比子查询任一结果更大的

(2)ALL

子查询结果的全部

  • 比子查询全部结果都大的

11.3 相关子查询

【作用】利用子查询逐条验证主查询的数据是否符合某种条件

①子查询必须依赖主查询才能执行,不能独立执行

②主查询的每一条记录都会触发子查询执行结果

  • 相关子查询

--利用emp表的0
​
 *
FROM emp e1
WHERE e1.sql > (SELECT AVG(sal) FROM emp e2 WHERE e2.deptno = e1.deptno)

11.4 EXISTS 和NOT EXISTS

EXISTS:验证子查询是否能查到结果

NOT EXISTS:验证子查询是否不能查到结果

11.5 IN 和 NOT IN

子查询为多行结果时

IN:不显示子查询结果中的NULL行

NOT IN:子查询结果有NULL行,直接不显示查询结果

Java

一、Java的优点

主要优点是跨平台(OS):一处开发、处处使用

  • 在Windwo平台上开发后,可在Linux、Unix使用

    因为存在JRE中的JVM虚拟机,JVM是一个java虚拟机,专门用于转译编译java。

    达到开发java后,在任何地方都可以运行

①常用window平台:闭源,漏洞问题修改不及时,我们不将其作为服务器平台

  • 因为常用的window平台是闭源的,因而使用其作为服务器,出现安全漏洞时,只能向微软提供一个漏洞报告,微软再对该漏洞进行处理,形成一个更新的版本给用户,修改漏洞时间很长,会产生巨大风险

②常用的服务器平台:Linux、Unix平台:开源,漏洞修改及时

  • 此类开源的操作系统,源码是开放的,可对内核外的代码进行修改操作

③因此就会出现平台不兼容问题,而java就很好地解决了

Java语言,主要用于WEB服务器应用类的开发

  • 因为web服务器应用开发时,通常利用windows操作系统,而实际上线部署则在linux和unix

二、JDK与JRE

2.1 JDK是什么?

JDK(Java Development ToolKit):Java开发工具

  • 需要开发Java程序的计算机,要安装JDK

2.2 JRE是什么?

JRE(Java Runtime Environment):Java运行环境,包含JVM(Java虚拟机)

  • 需要运行java程序的计算机,至少要安装JRE

[安装jdk时会默认自带同版本的jre]

2.3 两者关系

JDK = JRE + 开发工具(java、javac 等编译工具)

JRE = JVM+JavaSE标准类库(核心类库)

三、开发工具

建议使用eclipse,因为开源免费,商业行为无法律风险

四、Java的数据类型

Java的数据类型有两大类: 基本数据类型引用数据类型

  • 基本数据类型,数据保存在栈中

  • 引用数据类型,栈中保存地址,数据保存在堆中

4.1 基本数据类型(8种)

4.1.1 有哪些?

基本数据类型有8种:

  • 整数类:byte short int long

  • 小数类:float double

  • 字符类:char

    • 单个符号,用‘ ’表示

  • 逻辑类:boolean

4.1.2 占多少字节

b是bit、B是Bit,而1Byte=8bit

数据类型字节数位数范围
byte1Byte1字节 = 8位(bit)[-2^7~2^7)
short2Byte2字节 = 16位(bit)[-2^15~2^15)
int4Byte4字节= 32位(bit)[-2^31~2^31)
long8Byte8字节 = 64位(bit)[-2^63~2^63)
float4Byte4字节 = 32位(bit)[-2^31~2^31)
double8Byte8字节= 64位(bit)[-2^63~2^63)
char2Byte2字节 = 16位(bit)[-2^15~2^15)
boolean符合JVM规范的虚拟机中 单独使用占4个字节 以boolean数组形式占1个字节看情况看情况
4.1.3 int和long

int表示整型,long表示长整型,long需在末尾+L(不推荐l,容易看成1)

  • 超出long型最大的数字,可加后缀L,变为long型

4.1.4 float和double

float表示单精度浮点数,double表示双精度浮点数,默认为double,float需在末尾+F (建议统一,都写大写)

4.2 引用数据类型

除基本数据类型的8种外,都是引用数据类型

五、变量

变量:为内存中存放数据的一块空间而取的名

①声明方法

数据类型  变量名  =  赋值
int a = 34;

②变量命名要求

字母为开头,其他可为字母,数字,下划线

1)以下划线、字母、美元符开头。

2)后面跟下划线、字母、美元符以及数字。

3) 没有长度限制(但也不能太长! )。

4)对大小写敏感(意思是大小写代表不同含义)

【建议】:以动词英文字母,见名知意,避免拼音缩写

六、类型转换

6.1自动转换

(1)同类型,范围小可以转换为范围大的

byte-> short-> int-> long

float-> double

(2)整数可以自动转化为浮点数

byte->short->int->long->float->double

  • 但可能存在精度丢失

    int->float

    long->float

    long->double

(3)char可以自动转换成int,得到代表字符的Unicode码(包含ASC码,ASC只包含英文)

byte->short->int->long->float->double

char->int

6.2 强制转换

范围大(精度大)的转化为范围小(精度小)的,就叫做强制转化,可能发生精度丢失

数据类型  变量名  =  (强转类型) 待转的值

6.3精度丢失问题

(1)浮点数转整数

浮点数转整数,易丢失小数部分数据

(2)大范围转小范围

大范围转小范围,易丢失高bit的数据

  • 因为数值以二进制存储,则高bit位表示高进制数

  • 精度大的值,强行转为精度小的值,会损失一部分存不下的值

6.4 boolean不参与类型转换

boolean只有两个取值,true和false

  • 不同于其他语言,true当做1,false当做0,java中boolean类型就表示真假

七、程序的控制结构

7.1 条件控制

  • 某段程序运行的前提条件:

①单独条件控制
if(条件表达式){
   ....
}
②互斥条件控制
if(条件表达式){
    ...
}else{
    ...
}
③多互斥条件控制
if(条件表达式){
   ...
}else if(条件表达式){
   ...
}else if(条件表达式){
   ...
}
④switch句式(了解)
  • 条件判断数据类型,必须为byte,short,int,char,String

  • 只能判断“点状“数据

switch(){
  case :xx;
        break;
  case :xx
        break;
  case :xx
        break;
  default:xx
}

7.2 循环控制

  • 某段需要反复执行的程序

①前置条件循环-先检查条件
  • 当条件表达式为真,一直执行

while(条件表达式){
   //....
}

【适用于:有明确循环条件的情况】

②后置条件循环-后检查条件
  • 先执行do,当条件表达式为真,再继续执行

do{
  //......
}while(逻辑表达式)
③for循环(次数循环)
  • 第一次对增量进行初始化,若满足循环条件,执行代码,增量随之变化;若不满足循环条件,则退出循环

for(初始化增量;增量的循环条件;增量的变化){
  //.....
}

【适用于:有明确循环次数】

④循环的中断

(1)break:

退出当前所处的循环

  • 中断,立即结束整个循环执行,后续不再执行

(2)continue:

结束当前的循环,进行下一次循环

  • 轮空,结束当前轮次循环,继续下一个循环

⑤循环的嵌套

多层循环是指将多个数字,进行各种组合

  • 外层循环执行一轮后,内层循环会完整执行一遍

八、数组

8.1 一维数组

任何数组的变量,均是引用数据类型,表示一组定长的、同类型的、在内存空间中连续分配的数据

  • 定长

  • 同类型

  • 内存空间中连续分配

8.2 数组的声明与创建

  • 声明数组变量:

int[] a ---推荐
int a[]
  • 创建(实例化):

    • 设置长度

    • *new表示在堆空间中开辟一块内存使用

//①方式一:实例化但不设置值
//开辟一块容量n的数组空间
int[] a = new int[n];

//②方式二:实例化并设置值
//初始化长度为4的数组,内容为1,2,3,4
int[] a = new int[]{1,2,3,4};

//③方式三:
int[] a = {1,2,3,4} 

8.3 数组的特点

数组的特点是定长、同类型、在内存中连续分配

  • 为引用数据类型

打印一个数组名,输出的是数组所对的堆地址

int[] c = new int[]{1,2,4,5,6};
System.out.println(c);

8.4 数组内存模型

  • 为什么要连续分配?

    • 为方便快速的利用下标定位到每个元素位置

      • 因为引用数据类型是通过数组下标进行访问的

  • 为什么要定长?

    • 知道数组的存储位置

  • 为什么要同类型?

    • 同类型才能通过下标值,进行偏移量计算,从而得到每个下标所对的不同元素

8.5 数组下标

  • 下标从0开始计算

    • 代码编写时,超过下标范围,代码不会报红

      • 但运行时,会报下标越界

①通过下标获取元素
数组名[下标值]
②得到数组的长度
数组名.length

8.6 与数组操作相关的API

①Arrays.fill-数组初始化

对传入的数组进行操作

  • 用于为数组所有元素初始化为同一个内容

Arrays.fill(数组名,值)
  • 示例:

//将数组a的全部元素都设置为10
int[] a = new int[5]    
Arrays.fil(a,10)    
②Arrays.sort-数组排序

对传入的数组进行操作

【升序】
  • 对数组元素,进行升序排序(用的是快速排序)

Arrays.sort(数组名)//为数组进行升序排序
Arrays.sort(数组名,起始下标,排序个数)//在指定下标开始,为数组进行排序    

实现Comparator接口,并重写compare方法,就可改写排序规则

【降序】
  • 方法一:利用Arrays.sort+Collections.reverseOrder

    • 注意此时数组元素不能为基本数据类型

Integer[] a = {2,4,5,712,1,2}
//先进行降序,再反转=》升序
Arrays.sort(a.Collections.reverseOrder());
int[] res = new int[a.length];
//将结果,放入res数组
for(int i = 0; i < a.length;i++ ){
    res[i] = Integer.valueOf(a[i]);
}
//打印,输出结果
for(int i = 0; i <res.length,i++){
  System.out.print(res[i]+" ");
}
//或利用Arrays.toString(res)替代输出
  • 方法二:实现Comparator接口

    1)创建一个类,重写compare方法

    2)实现接口之匿名内部类

    3)实现接口之lambda

③Arrays.binarySearch-二分查找
  • 元素的二分查找(前提数组已是升序排序)

int res = Arrays.binarySearch(数组,要查找的值)
    //res为负数--》表示没找到
    //res为整数--》表示查找元素对应的下标值
④Arrays.toString()
  • 快速输出数组内容

int res = {1,3,3,4}
res.toString()//[1,3,3,4]
⑤Arrays.equals()
  • 比较数组的内容是否相等

Arrays.equals(a数组,b数组)//a数组和b数组内容是否相等
  • eg:

int[] a = {1,2,3}
int[] b = {1,2,3}
Arrays.equals(a,b)//a、b是否相等
-------------------------------
注意区分:Arrays.equals方法 和 数组名.equals方法
  Arrays.equals重写了,用于比较两个数组内容是否相同--[内容]
  数组名.equals未重写,用于比较两个数组引用地址是否相同--[地址]
⑥Arrays.copyOf()
  • 将原数组拷贝一份后返回

Arrays.copyOf(原数组,新数组长度)

九、==和equals区别

9.1 ==

  • 比较基本数据类型时:比较内容是否相同

  • 比较引用数据类型时:比较地址是否相同(即是否指向同一个对象实体)

==符号左右两边变量类型必须一致

9.2 equals

  • 未重写equals方法:比较两个对象的引用地址是否相同

  • 重写equals方法:比较两个对象的内容是否相同

    • 【常见重写】:String、Date、File、包装类都重写equals(),都比较内容!!

    • 【toString()】:String、Date、File、包装类也重写了toString(),输出为实体的内容,而非类名+引用地址

只能用在引用数据类型中

随机数

java中可以利用以下两种生成随机数的方法:

  • Random类

  • Math类的random()

两者范围[0,n)

9.1 Random类

9.2Math类的random()

十、方法

方法用于封装某段重复被调用的逻辑代码

10.1 方法的声明和调用

访问权限修饰符 限定修饰符 返回值类型 方法名(形参列表){
         ...方法体代码
}

1️⃣基础语法

①方法名
  • 以字母/下划线开头,其余字符可为数字、字母、下划线

  • 【规范】:

    • 驼峰原则

      • 首字母小写,其他首字母大写

    • 动词开始

      • addUser

      • deleteUser .....

②访问权限修饰符
  • public 公共的 --无论同不同包,所有类都可访问

  • private 私有的--仅本类,可访问

  • default 默认的--仅同包即本类,可访问

  • protected 受保护的--同包可访问,不同包子类可访问

③限定修饰符

-非访问修饰符

  • static 修饰符,用来修饰方法和变量

  • final 修饰符,用来修饰类、方法和变量

  • abstact 修饰符,用来修饰抽象类和抽象方法

  • synchronized 和 volatile 修饰符,用来修饰线程的编程

④返回类型
  • 无返回值时,可用void修饰,可省略return语句

    • ❗编码规范,建议利用return;

  • 有返回值时,必须设置返回值类型 ,否则报红

访问权限修饰符 [限定修饰符] 返回类型 方法名(参数列表){
   //方法体
    return 返回值;
}

2️⃣示例

//有返回值
public static int  add(int a ,int b){
  int c = a + b;
   return c;
}
//无返回值
public static void  add(int a ,int b){
  int c = a + b;
   return;
}

10.2 方法的参数

①形参

形参全称形式参数,是方法声明时参数的变量名

  • 叫什么无所谓

②实参

实参全称实际参数,是方法调用时提供给方法的数据

  • 不同实参传入会影响结果

③可变参数

可变参数又叫不定项参数

利用可变参数作为方法形参,形参为接受到的实参数组

  • 当方法传入的参数个数不确定时,可以使用 “数据类型...“方式传入可变参数

//表示可接受任意数量格int类型的实参,进行求和操作
public static int sum(int...a){
  int sum = 0;
  for(int i = 0; i < a.length;i++){
      sum + = a[i];
  }  
    return sum; 
}
​
//向sum方法,传入不同个数参数
public static void main(String[] args){
      int x1 = sum(5)
      int x2 = sum(5,6)
      int x3 = sum(6,7,4,5)    
}

10.3 方法的重载

1️⃣使用

期望传入不同类型参数,进行操作,可利用方法重载

  • 定义好重载方法时,调用时会自动适配符合的方法

2️⃣定义

同一个方法在类中被定义多次,叫做方法重载(Overload)

3️⃣规定

  • 方法名必须一致

  • 参数列表必须不一致

    • 参数个数不同

    • 参数类型不同

    • 参数类型的顺序不同

方法重写

1️⃣前提

存在继承关系

2️⃣规定

方法参数列表、返回值要一致

子类权限权限修饰符只大不小

子类抛出异常更小

10.4 方法栈的内存模型*

1️⃣如何管理?

java通过“栈”结构去管理方法

方法调用时入栈,方法运行完毕后出栈,栈顶为正在运行的方法

  • 调用一个方法时,会将其放入栈中,如果该方法中还调用其他方法,会依次将其都放入栈中

  • 而执行方法时,会依次将栈中的方法取出,遵循“先进后出“的原则

10.5 方法参数的值传递*

  • 基本数据类型,值存储在栈中

    • 传值时,传递的是值分身

  • 引用数据类型,栈中存储的是地址,而堆才是真正的值

    • 传值时,传递的是值地址

public static void test(int a,int[] b){
  a = 32;
  b[0] = 32;
}
​
public static void main(String[] args){
  int a = 10;
  int[] b = {10};
  
  test(a,b);
  
  System.out.println(a); //10
  System.out.println(b[0]); //32
}

10.6 方法的递归

方法递归:指方法自己调用自己去解决问题

  • 类似数据中递推思想,将一个大的复杂问题,转化为多个小的相似步骤问题

1️⃣要素

第一:递归的推导公式

  • 得到递归的规律是什么=》f(n) = n * f(n-1)

第二:递归的边界

  • 得到递归满足什么条件时,出栈=》具体的解

①阶乘问题

5!=5 *4 *3 *2 *1

4!=4 *3 *2 *1

∴n!=n *(n-1)! f(n) = n * f(n-1)

十一、面向对象

11.1 面向对象的概念

(1)概念

面向对象是一种以人类自然归纳总结能力进行软件开发的开发方法

  • 【概括】面向对象允许开发者自定定义数据类型

(2)类抽象步骤
  • ①确定共性

  • ②排除不相关

经过以上步骤得到的特征,就是事物概念的描述

11.2 类与对象

类是对象的模板,对象是类的实例化

通过类创建的变量,叫“对象/实例“

  • 用于描述共性中的具体个体

11.3 类的成员

类的成员有属性+方法

属性:类型概念中名词性的特征--属性

方法:类型概念中动词性的特征--行为

11.4 构造方法

构造方法是一种特殊方法,在对象构建实例时调用

只能通过new方式调用

1️⃣基础语法

  • 构造方法名必须和class类名一致

  • 构造方法没有返回值

    • 并非void,而是无

public 构造方法名(){
  
}

2️⃣注意

①若不写构造方法,系统会提供默认的无参构造方法

②若写了构造方法,则默认无参构造方法会被覆盖

③构造方法允许重载

  • 可有多个

  • 重载:方法名同,参数列表不同

十二、面向对象-继承

12.1 引用数据类型的内存模型

metaspace元空间,早期叫方法区

①内存类型

当第一次使用Triangle类时,会将其对应Triangle.class文件(包含类的属性+方法)加载到元空间中

当每次执行new时,堆会存在一个句柄指向元空间的Triangle.class,依照其结构去堆中开辟相应的内存空间

  • 每次new都会在堆中开辟一块新的空间,这几个new的内存空间指向的都是同一个字节码文件

②引用值解析

当new实例化一个类后,输出对象会得到下图:

  • 左侧表示metaspace(元空间),即方法区指向的字节码文件类型

  • 右侧表示哈希码,是通过对象指向堆内存地址计算出来的数

12.2 this关键字

class中使用,表示类当前的引用

当前是哪个对象在执行this,this就是哪个对象

  • 即包含 = 堆句柄指向的字节码文件类型+堆地址哈希码

12.3 super关键字

  • super和this都不能放在静态方法中

  • super必须放在构造方法第一行

    • 系统默认为构造方法第一行添加无参super()

12.4 类的继承 extends

继承用于表示现实世界中,一般性和特殊性的关系

描述一般性的类,叫做“父类/超类(super class)”

描述特殊型的类,叫做“子类(subclass)”

①什么是继承?

通过extends关键字,可令子类获得父类的所有特征,同时子类可以扩展自己独有的特征,叫做“继承”

  • 避免了同种特征的重复定义

  • 如:动物 呼吸 ; 鸟 呼吸,飞;两者的共同特征是 呼吸

②语法格式
public 子类 extends 父类{
  //...代码
}
③特点

1️⃣Java只支持单继承

  • 一个类只能由一个直接的父类,可以有多个间接的子类

2️⃣Java类默认继承java.lang.Object类,即java.lang.Object是所有类的父类(超类)

  • 一个类未extends任何类,默认继承Object

④继承的内存模型

new子类时,堆的句柄会指向元空间(方法区),根据.class文件得到子类类的属性+方法,同时子类还会有一个句柄指向父类,最后在堆中,将子类+继承自父类的属性+方法,都放入创建的内存空间中

  • 同时super.属性和this.属性指向同一块内存空间

  • 同时super.方法和super.方法不一定指向同一块内存空间

    • super.xxx()指代父类方法,this.xxx()指代子类方法

十三、面向对象-多态

多态用于表现一种事物的不同表现形式

13.1 引用数据类型的类型转换

主要是父子类的向上,向下转型

【使用场景】:主要用于方法调用接受参数

【主要用于满足开闭原则:对扩展开放,对修改关闭】

前提:两个类之间存在继承关系

(1)上溯造型(自动转换)

将子类实例化,看做父类声明

将子类的实例,当做父类的对象看待,失去子类的特殊性

  • 即向上转型,子类类型->父类类型,但不能再使用子类特殊属性+方法

父类 a = new 子类();

(2)下溯造型(强制转换)

将父类对象强制转化为子类对象

有三条转换路线:

  • 父类->子类 (转换失败)

//编译成功,运行失败
Animal a = new Animal()
Brid b = (Brid) a //✖
  • 子类A->父类->子类B (转换失败)

//编译成功,运行失败
Animal a = new Fish()
Brid b = (Brid) a //✖
  • 子类A->父类->子类A (转换成功)

//正确写法
Animal a = new Brid()
Brid b = (Brid) a //✔

不爆红,但运行报错

13.2 方法的重写(方法覆盖)

方法重写用于体现父子类在同一行为的不同表现细节

1️⃣概念

子类重写或实现父类的某个方法,叫方法重写(Override)

2️⃣要求

子类方法和父类方法同名,同参数列表,同返回值

子类访问权限不低于父类

子类抛出异常为父类抛出异常的子类

3️⃣注意

①重写方法,不添加@Override也行,只是为了方便他人阅读代码

②上溯造型是将父类声明指向子类实例,因而调用方法时,优先执行子类的

【拓展】运行时多态 和 编译时多态

方法重载也被称为编译时多态,发生在同一个类中,要求方法名同,参数列表不同

方法重写也被称为运行时多态,发生在父子类中,要求方法名同,参数列表同,返回值同,访问权限不低于父类,抛出异常为父类抛出异常的子类

13.3 多态的实现

(1)实现步骤

条件1:必须存在继承关系

  • 为其共通属性设置一个父类

条件2:利用多态设计父类对象参数(向上转型)

  • 将传入的方法参数,设置为父类

条件3:利用重写机制实现共通逻辑

  • 利用方法重写子类的每个具体实现

十四、面向对象-抽象类

14.1 抽象类 abstract

主要用于解决多态的遗留问题 + 用于“父类”声明

  • 问题1:多态的子类,常常只有一个父类概念,而没有具体实体

  • 问题2:父类的方法,其中具体实现是由子类去书写的

  • 问题3:重写非强制性,子类可能忘记重写父类方法

(1)抽象是什么?

形象越模糊,越概念化,叫做抽象

形象越具体,越实体化,叫做泛化

因而没有实体的类,不应该new,而应该变为抽象类!

而没有具体实现的方法,不应该设置方法体,而应该变为抽象方法!

(2)抽象存在的意义

抽象可以解决多态遗留的以下问题,用于“概念性”的父类声明:

  • ①抽象概念的父类被实例化

    • 通过各种子类抽象出来的父类概念,本身没有实体,因而不应该去为其创建实例,但没有约束的规则

  • ②简化父类中被重写的通用方法

    • 父类中被子类重写的通用方法,方法体内容并没有实际含义

  • ③避免子类忘记重写父类的通用方法

(3)基础语法
  • abstract关键字修饰类

abstract class 类名{
}
(4)抽象类定义

①抽象类不能实例化

②抽象类中的抽象方法,可以只声明方法头,无方法体

③子类(非抽象的)继承抽象类后,必须重写抽象类的所有抽象方法

  • 以上未满足,都会报红

14.2 接口 interface

接口表示“具备某种能力”的事物,但事物间看起来毫无关系

当两者有共同属性,但并不能看做父子关系,如:飞机/鸟,都会飞,但并不存在父子关系

(1)基础语法

Java中支持单继承(extends),可实现(implements)多个接口

  • 1️⃣接口中只能定义常量值、抽象方法、lambda表达式

  • 2️⃣接口名建议使用“动词+able”命名

  • 3️⃣接口中方法默认为public abstract类型,可省略

public interface Flyable{
    void fly();//省略了public abstract
}

4️⃣一个类即可继承一个父类,又可实现多个接口

(2)代码建议

进行开发时,建议能使用接口就不要使用父类(抽象类)

  • 因为父类只能继承一个,接口能实现好多个

十五、静态和最终关键字

15.1 static静态关键字

static修饰类(内部类),属性、方法、代码块

被static修饰的内容不属于动态的实例,而属于静态的类

  • 实例的数量是变化的,可看做动态

  • 类在元空间的数量是固定的,可看做静态

  • 所以静态修饰的属性、方法、代码块,实际上是存储在元空间的字节码文件内

(1)静态属性

①所有实例访问的是静态属性唯一的一块内存空间

②静态属性应该通过“类名.xxx”访问,而非通过实例访问

  • 实例访问也不报错,仅提示警告

(2)静态方法

①静态方法中只能访问静态的属性和方法

  • why?因为在类被加载到元空间时,静态内容是被存储到元空间中的,此时静态内容已经就绪,但此时无法确定非静态内容是否就绪

②静态方法中访问非静态属性和方法,会报红

(3)类何时被加载到元空间

类加载只执行一次

①第一次创建类的实例时--非静态内容就绪

  • 类创建的实例的句柄,会指向元空间对应的.class文件,此时类会被加载到元空间中

②第一次使用了类的静态方法--非静态内容未就绪

  • 因为类的静态属性和方法,是存储在元空间中的,因而要使用就需要先将类的.class文件加载至元空间中

③第一次使用Java反射的Class.forName(“类路径”)

  • 此代码表示将类加载到元空间中

根据类被加载到元空间的几种情况,可以得知,当类被加载到元空间时,类实例不一定被创建,因而如果此时调用元空间的方法,就会报错!

15.2 静态代码块

(1)概念

静态代码块中,只能访问静态内容,在类加载时自动执行且只执行一次

(2)作用

常用于复杂的静态变量进行初始化操作

如:读取配置文件,设置项目的端口+运行地址

第一步:编写一个xxxx.properties配置文件

  • 让用户能打开,同时设置配置

第二步:将配置文件读取到类中,获取其中要设置的值

15.3 final最终关键字

final关键字可修饰类、属性、方法。表示“最终”的含义

(1)被final修饰的类

被final修饰的类不允许有子类

  • 如:String、System、Math都是被final修饰的,因而不允许再设置子类继承它

(2)被final修饰的属性/局部变量

被final修饰的属性/局部变量,第一次赋值后不能重新赋值,被称为“常量”

  • 命名:通常使用全大写

(3)被final修饰的方法

被final修饰的方法不允许被重写

  • 不常用,没有人能保证自己的代码完美不必再修改

15.4 final和abstract

①问题1:abstract和final能够同时修饰类或方法?

【不被允许,编译会错误】

  • 不能,abstract常用于修饰父类,而子类必须重写父类中的继承方法

  • 但final修饰的类不可被继承,同时也不能重写final方法

②问题2:为什么修饰常量属性要用static+final?

【final是为了不被修改,statci保证了调用便捷,又保证内存中只有一个,节省内存空间】

  • final:表示常量不可修改

  • static:

    • static修饰的属性+方法,可通过类名.xx直接调用

      【调用简便】

    • static修饰的属性+方法,放在方法区中,可保证内存中不会随着实例数量增长,而产生多个静态,保证它始终只有一个内存

      【节省内存空间】

十六、面向对象-封装

封装是为了方便对代码进行隔离、便于管理

【 模块间要“高内聚,低耦合”】

16.1 package级别的封装

将不同业务不同功能的class文件放到不同包下,叫做package级别的封装

(1)分包的形式

常采用以下三种形式进行分包:

①按照业务逻辑划分【高内聚、低耦合】

  • 便于区分不同功能

  • 使得软件模块化,便于解耦

②按照代码功能划分【MVC模式】

  • V:View 视图层,用于显示用户操作界面的代码

  • C:Control 控制层,用户数据流转,界面切换的代码

  • M:Model 模型层

    • Service 业务层-》用户实现基本业务逻辑和事务控制

    • DAO 持久层-》用于数据的持久化,数据库操作

③将①+②结合【分布式项目】

  • 将每个业务分包,然后再将其分为mvc结构

16.2 类级别的封装

将零散的参数封装成一个类

(1)VO

映射view层提交或显示的数据格式

  • 参数过多时,容易不明确每个参数的意义,因而将其封装成一个类,便于标识参数含义

(2)PO

映射数据库表的数据格式

16.3 访问权限修饰符*

访问权限修饰符可修饰类、属性、方法

  • 修饰类:默认和public

  • 修饰属性和方法:四种都可

(1)常用四种访问修饰符
权限访问范围
private私有的仅本类访问
default/package/friendly默认的本类及同包下类访问
protected受保护的本类及同包/不同包子类可访问
public公开的所有类可访问
(2)访问修饰符范围
类的内部同一包不同包的子类任意类
私有OKNGNGNG
默认OKOKNGNG
保护OKOKOKNG
公有OKOKOKOK

(3)注意

①问题1:类以默认权限修饰,目的?

  • 使得类只可以在同一包下被访问

②问题2:类构造方法以私有权限修饰,目的?

  • 不让类外部创建类的实例,控制实例的数量

③问题3:某些父类方法使用Protected修饰,目的?

  • 期望子类能重写父类方法,并提升访问权限

    • 使得同包/不同包的子类都能访问父类,达到父类完成第一步,子类完成第二步的情况

16.4 访问器方法

访问器方法即将类的属性设为私有,而提供公共的get/set方法

  • 要利用系统生成这些方法,不要手写

(1)意义

①为了遵循“Bean”规范,其被广泛应用在各种框架的底层支持

②利用访问器方法,可便于提供“一半”权限

  • 只读:只提供get方法

  • 只写:只提供set方法

(2)使用注意

❗boolean类型的自动生成get方法,命名为isXxx()

  • 而有些框架中可能不识别,需改成setXxx()

16.5 单例模式

单例模式约束一个类在软件系统中最多只有一个实例

(1)饿汉式

无论是否使用,都先创建一个实例

public class MyHungrySingleton{
   //2.在类的内部创建一个唯一的实例-先new
   //私有+实例 阻止外部修改
   //static 让静态方法能调用
    private static final MyHungrySingleton instance = new MyHungrySingleton();
   
   //1.私有构造,阻止外部创建实例,控制实例数量
   private MyHungrySingleton(){}
   
   //3.提供get方法以供外部使用
   //static:方法不依赖实例即可调用
   public static MyHungrySingleton getInstance(){
     return instance;
   }
}
(2)懒汉式

等到使用时,再创建一个实例

十七、实例的五个常见操作

通过在类中重写各种方法,达到“相等”,字符串输出,"可比较","克隆"等特性

17.1 判断实例在业务逻辑上“相等”*

重写Object类的equals方法,可以实现业务逻辑上“相等”的判断

1️⃣需求描述

比较两个实例时,比较的都是两个实例的地址

若要比较两个实例的属性是否相等该怎么办呢?

2️⃣解决方法

重写Object类的equals方法

@Override
public boolean equals(Object obj){
   if(obj instanceof 类){
       //进行向下转型
       类 e = (类) obj;
       //如果当前类属性与传入类属性相同
       reutrn true;
   }
   return false
}
3️⃣示例

【如】:String字符串

== 判断地址是否相同

equals 判断字符串内容是否相同

【String重写了equals,因为Object类默认的equals也是==,判断地址相同】

17.2 实例的字符串形式表示

重写Object类的toString方法,返回需要显示的字符串内容

1️⃣需求描述

当正常打印输出实例对象时,输出 的是com.neusoft.demo.xx@1c4f82c 此类值

如果想显示实例中属性值该怎么办呢?

2️⃣解决方法

重写Object类的toString()方法

  • 输出实例时,再去调用其.toString()方法即可

17.3 实例的哈希码值

哈希码值(hascode)是实例的内存地址经过计算得到的一个整数,通常以十六进制展示

若希望改写实例的哈希码值,可重写Object的hashcode方法

  • 不建议轻易重写,哈希码值是获取内存调用计算而来,不建议更改

1️⃣基础语法

同一块内存地址,多次取得哈希码值都是相同的

  • 内存地址相同,哈希码值相同

两个对象哈希码值相同,但可能是不同的内存地址

  • 哈希码值相同≠内存地址相同

  • 可能存在不同的两个内存地址算出同一个哈希码值

2️⃣需求描述

需要程序开发者控制实例的哈希值时,可通过重写Object类的hashcode方法,返回新的哈希码值

17.4 实例的“可比较“*

要实现两个实例间的比较,可重写java.lang.Comparable接口,重写接口中的compareTo方法

1️⃣需求描述

若要对自行编写的类实例数组进行排序,不能使用Arrays.sort进行排序,会报错,因为没有排序规则

2️⃣解决方法

第一步:将类实现Comparable<T>接口

  • T为泛型 ,为比较的类型

    表示类实现Comparable<T>接口后想与哪种类型进行比较

public class Employee implements Comparable<Employee>{
 //表示实现接口,完成Employee类型与Employee类型比较
}

第二步:重写compareTo方法,进行比较

  • 返回值 int

    • this小于o,返回-1

    • this等于o ,返回 0

    • this大于o,返回1

@Override
public int compareTo(Employee o){
  //自定义排序规则
    //在此以Employee的empno排序
    if(this.empno > o.empno){
        return 1;
    }else if(this.empno < o.empno){
        return -1;
    }
    return 0;
}

第三步:再使用Arrays.sort方法,进行排序

【拓展:】comparator

comparable是默认的比较规则

  • 默认大于,返回正数

  • 小于,返回负数

  • 等于,返回0

而comparator是额外的比较规则

17.5 实例的“克隆“

当想克隆一个类实例,可通过在类中实现Cloneable接口,同时重写其clone方法

  • 此处克隆,各自指向自己的内存空间

1️⃣需求描述

原先利用此方式将一个类实例,复制给另一个实例,并未达到真正的克隆,两者仍指向同一块内存空间

修改e1,e2也会随之更改,该如何达到e1,e2各自指向自己的内存空间呢?

2️⃣解决方法

第一步:让类实现java.lang.Cloneable接口

  • Cloneable接口是个空接口,表示身份接口

    • 一旦实现该接口,就表示自身 可以被克隆

public class Employee implements Cloneable{
 //表示实现接口,完成Employee类型与Employee类型比较
}

第二步:重写Object的clone方法

  • 并把方法变为public

  • 更改返回类型,为要克隆的类型

第三步:再调用 对象实例.clone()方法进行克隆

3️⃣深浅克隆

在重写clone方法要注意,如果是引用数据类型,需要重新new一个实例,再赋给要克隆的对象

①浅克隆

两个实例,仍指向同一块地址

修改其中一个,另一个也会发生变化

②深克隆

两个实例克隆后指向不同的内存空间

修改其中一个,另一个也不发生变化

十八 、异常处理

程序在编译和运行过程中,由于各种原因造成的程序中断,称为异常

当异常发生时,程序会产生异常对应的一个异常实例,通过操作异常实例可以实现异常的处理

18.1 异常的继承体系

异常的顶级类 Throwable类

包含以下两种类:Error类 和 Exception类

(1)Error类(错误类)
  • 它的子类命名以Error为后缀

  • 少数也是程序的问题,如:递归栈内存溢出错误

    • 多数任务错误无法通过软件手段修复。如:网费到期,服务器到期此类物理硬件问题

(2)Exception类(异常类)*
  • 它的子类命名以Exception为后缀

    • 如:空指针异常 NullPointerException....

18.2运行时异常和检查性异常

运行时异常和检查性异常,都是Exception的子类

(1)运行时异常

RuntimeException及其子类,叫做运行时异常

【发生原因】:程序逻辑不严谨,可避免

1️⃣基础语法
  • 编译时可能发生异常的代码,不给出异常处理预案【运行再报错】

  • 发生异常时,直接中断

2️⃣常见异常类型

①算术运算异常 (ArithmeticException):

  • 整数除法除数为0

②下标越界异常(IndexOutOfBoundsException)

  • 数组,集合,字符串等下标超过数组长度,但并没有产生报红

③类型转换异常(ClassCastException)

  • 发生在下溯造型转换的父转子类

④空指针异常(NullPointerException)

  • 调用了null值的属性和方法

(2)检查性异常

非RuntimeException体系下其他的Exception子类,叫做检查性异常

【发生原因】:大多是意外状况造成,有时 不可避免

1️⃣基础语法
  • 编译时可能发生异常的代码,必须给出异常处理预案【编译时报错】

  • 发生异常时,程序执行相应的异常处理预案,确保程序恢复运行

    • 异常处理预案,就是try...catch

2️⃣常见异常

①Thread.sleep

18.3 异常处理-抛出异常

将可能发生的异常种类,通过throws关键字声明在方法上。由方法调用者决定异常如何处理

抛出异常:本方法不管,谁调用谁管

1️⃣throws抛出异常

①一个方法允许抛出多种异常,但调用者必须处理所有异常

②若一致抛出异常,直到main也抛出异常,相当于异常没有提供任何预案

2️⃣throw 人为制造异常

利用throw抛出一个异常实例,可以制造人为异常,该异常被认为一定会被发生异常。

18.4 异常处理-捕获异常

捕获异常,提供异常处理预案

1️⃣try..catch...finally
  • catch可有多个

  • 支持一下语法:

    try...finally....

    try...catch...

    try...catch...finally

try{
  //尝试执行可能发生异常的代码
}catch{
  //try发生异常,执行catch中方法
}finally{
  //无论是否发生异常,最后都执行finally
}

2️⃣实例

  • 两种异常的处理预案不同,分别设置一个预案

  • 两种异常的处理预案一致,设置同一个预案

  • 用大异常兜底,但注意不可以放在小异常前

十九、字符串类

String虽然是引用数据类型,但看起来与基本数据很像

19.1 字符串常量类-String

String被称为“字符串常量类”,因为它并非在原来的空间中修改值,而是新建一块内存空间,再修改指向实现的

当字符串常量类被重复赋值时,内存的变化如下:

(1)String xx = "xxx"

利用等号赋值的,存储在常量池中

字符串每次利用=赋值时,会先去找常量池中是否有相同的字符串实例存在

  • 如果有,该变量则指向这块内存地址

  • 如果无,该变量重新创建一块内存地址,再指向该地址

【内存模型】

(2)String xx = new String()

利用new的,存储在堆中

字符串每次利用new赋值时,直接在堆中新建一块内存空间

【内存模型】

  • 常考:

      String s1 = new String("123");
      String s2 = new String("123");
      System.out.println(s1==s2);//false
      
      String s3 = "123";
      String s4 = "123";
      System.out.println(s3==s4);//true

19.4 字符串变量类-StringBuffer和StirngBulider

String类型在for循环内进行字符串拼接时,效率极低,耗时呈指数型增长;因为每次都需要新建一块内存空间

  • 原先字符串常量类String,需要不停该指向,同时不停创建新的内存空间

因而采取字符串变量类能很好的解决该问题,因为字符串常量类是采取每次将值覆盖进行的赋值

  • 如今字符串变量类,只需在原先的内存空间中,将值覆盖,同时不必更改指向

    • 节省了创建内存地址+改变量引用的时间

可通过以下程序,测试字符串拼接的速度

(1)StringBuffer

StringBuffer是"线程安全",性能稍低

(2)StringBuilder

StringBuilder是"线程不安全",性能稍高

两者在实际应用中差距不大,执行性能差不了多少

(3)使用

①创建字符串变量类实例

②实例.append()用于拼接值

③实例.toString()用于转回String类型

StringBuffer s = new StringBuffer();
//拼接
s.append("要拼接的值")
//转回String
s.toString();

19.5 对比

当进行多次字符串拼接时,建议用字符串变量类

而线程安全的情况,使用StringBuffer

而线程不安全的情况,使用StringBuilder

19.6 String类的API

字符串的本质是一个char[]数组,用String类型对这个char数组进行了包装,最后形成了字符串类

(1)字符串实例.length()

字符串实例.length()用于获取字符串的长度

String str = "ABC ACBIA";
int len = str.length();
(2)字符串实例.toCharArray()

字符串实例.toCharArray()用于获取字符串所有字符组成的数组

  • 返回值为char[]类型

String str = "ABC ACBIA";
char[] cs = str.toCharArray();
//可利用数组的Array.toString(数组)输出其中内容
(3)new String(数组)

利用new String(数组名),可将char[]数组,转化为字符串

  • 返回值为String类型

      char[] xs = {'a','v','c'};
      String string = new String(xs);
      System.out.println(string);
(4)字符串实例.charAt(下标)

字符串实例.charAt(下标),用于获取字符串特定位置的字符

  • 返回值为char类型

  • 下标从0开始,注意不要越界

	  String str = "asdasda";
	  char x = str.charAt(0);
	  System.out.println(x);//a
(5)字符串实例1.equals(字符串实例2)

用于比较两个字符串内容是否相等

(6)字符串实例1.equalsIgnoreCase(字符串实例2)

用于比较两个字符串内容是否相等,并忽略大小写

(7)字符串实例.startsWith()

用于判断字符串是否以xxx开头

  • 返回值为boolean

(8)字符串实例.endsWith()

用于判断字符串是否以xxx结尾

  • 返回值为boolean

(9)字符串实例.substring(startIndx,endIndex)

字符串实例.substring(startIndx,endIndex),用于截取字符串

  • 范围 [ startIndex , endIndex )

  • endIndex不传入,表示截取到结尾

1️⃣常规用法

  • 截取xx-xx

      String str = "asdasda";
      String subStr = str.substring(1,4);
      System.out.println(subStr);//sda
  • 截取到结尾

	  String str = "asdasda";
	  String subStr = str.substring(1,str.length());
	  System.out.println(subStr);//sdasda
(10)字符串实例.contains("xxx")

字符串实例.contains("字符串"),用于判断字符串中是否包含xxx

  • 返回结果为boolean

  String str = "asdasda";
  boolean b = str.contains("asd");
  System.out.println(b);//false
(11)字符串实例.indexOf("xxx",startIndex)

字符串实例.indexOf("xxx"),用于查找xxx在字符串中第一次出现的下标位置

  • 传入startIndex,表示传入开始查找的下标值

1️⃣不传入startIndex

  String str = "asdasda";
  int b = str.indexOf("asd");
  System.out.println(b);//0

2️⃣传入startIndex

  • 从下标为xx,开始找xxx第一次出现的位置

  String str = "asdasda";
  int b = str.indexOf("asd",1);
  System.out.println(b);//3
(12)字符串实例.lastIndexOf("xxx")

字符串实例.lastIndexOf("xxx"),用于查找xxx在字符串中最后一次出现的下标位置

  String str = "asdasda";
  int b = str.lastIndexOf("asd");
  System.out.println(b);//3
(13)字符串实例.isEmpty()

字符串实例.isEmpty(),用于判断字符串是否为空字符串

  • 返回值:boolean

常采用以下方式:

  • 注意其中两者位置不能互换,否则为null时无法判断为空

if(str == null || str.isEmpty()){
  //输入的字符串为空
}
(14)字符串实例.trim()

用于消除字符串的前后空格

  • 返回值为消除空格的字符串

  String str = "  das a  ";
  String newStr = str.trim();
  System.out.println(newStr);//das a
(15)字符串实例.replaceAll("oldStr","newStr")

字符串实例.replaceAll("oldStr","newStr"),用于将字符串中oldStr替换为newStr

  String str = "aaaabbbb";
  String newStr = str.replace("ab","*");
  System.out.println(newStr);//aaa*bbb
(16)字符串实例.split("xxx")

字符串实例.split(“以何字符进行分隔”),用于以某个分隔符将字符串拆成字符数组

  • 返回值为String[],字符串数组

	  String str = "aa aa bb bb";
	  String[] x = str.split(" ");
	  System.out.println(Arrays.toString(x));//[aa, aa, bb, bb]

二十、包装类

包装类是对基本数据类型进行包装,得到的是基本数据类型在引用类型中的投影

20.1 意义

Java中一些场景,必须使用“引用数据类型”的数据,如:集合中只能存储引用类型的数据,即如果要将int类型数据放入集合中,就需要使用int类型的包装类来实现

20.2 常用的包装类

基本数据类型对应包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

20.3 拆箱与装箱

装箱:指将基本数据类型变为其包装类对象 (Integer->int)

拆箱:指将包装类对象变为基本数据类型(int->Integer)

(1)如何拆箱

利用包装类实例.intValue(),进行拆箱

int x = 1;
Integer i = Integer.valueOf(x);//jdk9后支持
int a = i.intValue();
(2)如何装箱

早期jdk通过构造方法,进行装箱

int x = 1;
Integer i = new Integer(x);//如今已被抛弃

jdk9之后,利用Integer.valueOf(),进行装箱

int x = 1;
Integer i = Integer.valueOf(x);//jdk9后支持

在实际使用中,JDK5后就已经提供了自动拆箱和自动装箱(语法糖),以上代码较少使用

(3)实际应用

JDK5后,允许包装类和基本数据类型直接进行赋值

Integer i = 11;//自动装箱
int j = i;//自动拆箱

20.4 包装类提供的API方法

(1)字符串与基本类型的转换

Integer.parseInt(String s)

可将String类型,转化为int类型

  • 前提是满足数字格式

Double.parseDouble(String s)

可将String类型,转化为double类型

  • 前提是满足数字格式

(2)Integer转进制表示

Integer.toBinaryString(x) //可将x数值,转化为二进制表示

Integer.toOctalString(X)//可将x数值,转化为八进制表示

Integer.toHexString(X)//可将x数值,转化为十六进制表示

20.5 各种数据格式的转换

二十一、集合类-线性集合

21.1 集合的分类

(1)Collection(单独数据存储)

Collection可分为List集合和Set集合

List集合:有序(号),可重复

Set集合:无序(号),不可重复

①List集合

有序号、可重复

  • 序号:

List中下标范围 [0,集合长度),允许开发者通过下标操作List集合中的数据,数组保存在集合中的位置是可控的

  • 可重复

Lsit集合中允许同时保存多个“重复”的数据

  • 如果判断数据重复?

两者equals判定为true时,认为两者“重复”

  • 变长

    • 因而向集合中添加数据时,集合会自动维护长度

与数组定长不同,集合的长度是动态变化的,集合的长度取决于存储数据的长度

②Set集合

无序号,不重复

  • 无序号

开发者不能通过集合下标操作Set集合中的数据,Set集合无下标,其中的数据,依据类的算法决定摆放的位置

  • 不重复

多个重复数据,只保留一个(通常为最后一个保留,其他覆盖)

HashSet

存入集合的数据,按照hashcode大小决定存储位置

  • hashcode码大的在后

TreeSet

存储集合的数据,按照数值自然大小决定存储文职

  • 底层是基于二叉排序树,从小到大

    • 要求存入数据必须是可比较的,否则抛异常(实现了comparable接口)

(2)Map(键值对存储)

Map集合:自定义序(号)类型的List集合

  • 每一数据由key-value(键-值)组成

  • 所有key必须录入,且key不能重复

  • 所有value允许重复

①Map集合本质是“自定义下标”的List集合

②Map集合的核心,是通过自定义的“key”作为下标,操作对应的value

③为了约束key的排序方法,产生了HashMap和TreeMap

HashMap

key按照hashcode值排序

  • hashcode值大的在后

TreeMap

key按照自然大小排序

  • 从小到大

21.2 继承体系

二十二、List集合

22.1 List集合的实现类

(1)ArrayList

底层实现基于数组

  • 数组:在内存中是连续分布的

【特点】查找效率高,插入删除效率低

ArrayList存储的是第一个元素的地址。

  • 当查找时,可以按照下标进行查找;

  • 当插入/删除时,插入一个数据,就得将之后所有数据向后/前移

(2)LinkedList

底层实现基于双向循环链表

  • 链表:在内存中是分散分布的

【特点】查找效率低,插入删除效率高

LinkedList存储的是第一个元素的地址,且最后一个元素会指向第一个元素

  • 当查找数据,必须从第一个开始,通过链域找到下一个,再依次进行查找,得到最终数据

  • 当插入/删除时,只需将链域的指向进行更改即可

而未被指向的元素,则被视为“垃圾”,后续会被垃圾回收机制扫描并进行回收

①双向链表

分为前链域、数据域、后链域

②循环

最后一个元素,指向第一个元素

22.2 实际使用

List集合在实际使用中较多的为ArrayList,因为LinkedList再进行插入时,需先查找,效率过低

  • 插入前,还是需要先找到元素

模拟数据结构(栈、队列)时,可能使用LinkedList

23.3 集合中的泛型

集合的泛型:可以用于约束集合中的数据结构

  • 只允许某类型存储在集合中

1️⃣作用

2️⃣实例

23.4 ArrayList常用方法

23.4.1 添加集合元素
(1)list实例.add(xxx)

向list集合的末尾处添加xxx数据

(2)list实例.add(index,value)

向list集合的index下标值处,插入数据value

(3)list实例1.addAll( index , list实例2 )

向list集合中添加另一个list集合,插入在index下标处

  • 不写index,默认添加在结尾处

23.4.2 删除集合元素
(4)list实例.remove(index)

根据下标,移除集合中的元素

  • 当想通过remove移除list集合的整除值,需要转换为包装类,避免被识别为下标

    • 利用Integer.valueOf( int类型值 )进行包装

(5)list实例.clear()

表示清空集合中的所有元素

23.4.3 修改集合元素
(6)list实例.set( index , value)

根据index下标值,修改集合中对应的元素为value

23.4.4 查找集合元素
(7)list实例.size()

得到集合中数据的数量

(8)list实例.get(index)

通过index下标,查看集合中某个位置的元素

(9)list实例.contains(xxx)

查看元素中是否包括某元素

  • 返回值boolean

(10)list实例.containAll(xxx)

查看元素中是否包含某xxx集合内容

  • 返回值为boolean

(11)list实例.indexOf(xx)

查找元素在list集合中第一次出现所处的下标位置

  • 不存在得到-1

(12)list实例.lastIndexOf(xx)

查找元素在list集合中最后一次出现所处的下标位置

  • 不存在得到-1

//集合中数据的数量
list实例.size()
//通过index下标,查看集合中某个位置的元素
list实例.get(index)
 //
(13)list实例.isEmpty()

判断是否为空集合

  • 返回值boolean类型

23.4.6 遍历集合元素
(1)利用下标实现遍历

(2)利用for...each
  • 如果集合设置了泛型

(3)迭代器

22.2 Conllections集合工具类

都是对集合本身进行操作

  • 前提:集合中的数据可以比大小(实现了compareble接口,重写了compareTo方法)

  • 否则,不能排序、反序、找最大、找最小

22.2.1 集合的升序排序

Collections.sort(list),可将list集合变为升序

22.2.2 集合的反序

Collections.reverse(list),可将list集合的中每个元素顺序颠倒

22.2.3 集合的最大和最小值

Collections.max(list) ,查询集合的最大值

Collections.min(list),查询集合的最小值

22.2.4 集合的二分查找

【拓】自定义比较器规则

在Collections集合工具类中的.sort(集合,比较器)方法,支持传入一个自定义的比较器,用于制定临时的比较规则

见list集合56:46分!!!

二十三、Set和Map集合

23.1 Set集合

Set集合是无序(号), 不可重复的

  • 程序员无法通过下标值得到集合的值

  • 基于Set集合的排序规则,根据不同排序规则,分为HashSet和TreeSet

(1)HashSet

HashSet根据哈希值进行排序

(2)TreeSet
  • TreeSet根据升序排序

    • 前提是“可比较”

    • 原理是二叉排序树(红黑树)

      • 规定:一个节点,两个分叉,值大的放右边

  • 二叉树的遍历方式:

    中序遍历:左-根-右

(3)Set集合API

与List集合差不多,但抛弃所有与下标有关的API

遍历Set集合
  • 方法一:如果约束了泛型,使用for-each进行遍历

  • 方法二:利用迭代器

将Set集合变为List集合

可通过list.addAll(set),将其装入list集合中

23.2 Map集合

Map又称为键值对集合(key-value)

23.2.1 基础语法

核心思想:利用key去操作value值

key:键 value:值

--key可以理解为自定义下标

--value可以理解为下标所对的数据

23.2.2 Map集合的实现类
(1)HashMap

数据按照key的hashcode大小决定存放顺序

(2)TreeMap

数据按照key的自然大小决定存放顺序

23.2.3 常用方法
①map.containsKey("xx")

判断map中是否有key为xx的数据

  • 返回值true / false

②map.put(key,value)

key没有时新增,将Map新增一条key-value

key有时修改,将Map的key对应的值修改为value

③遍历
  • 方法一:取出key组成的Set集合,根据Set集合key取value

  • 方法二:取出Entry组成的Set集合,Entry表示一个键值对实例

④map.remove('key')

用于移除Map集合中key所对的键值对数据

二十四、日期类

二十五、正则表达式和BigDecimal类

作业题

【小和尚大和尚】

【水仙花数】

【统计字符出现个数】

【回文数】

【随机生成HEX颜色】

HEX颜色由 (#+ A~F+数字)共5位组成

如:#FFF001

【阶乘问题】

【斐波那契数列】

【方法重载】

【上楼梯问题】

【定义三角形类】

【类的多态】

【搭建面向对象继承体系】

搭建图中的继承体系

【时间类检查性异常】

【字符串验证邮箱合法】

【输入一串字符串,输出每个字符出现次数】

【统计出现次数最多的】

【判断素数】

单体测试

一、编码流程

1.1需求分析

1.2详细设计

详细设计是必须的,但有时如果简单/做过类似的,可直接进行开发

1.3编码

编码需要具有详细设计的每个步骤,都要体现在编码中,但不要多,少、漏

代码完整,还需做:

  • ①代码review

    自行检查,避免出现低级Miss,如:保证代码和详细设计书一致、避免逻辑的错误、多余或遗漏

  • ②代码编译

    是否能够生成可执行文件

1.3.1低级Miss

低级Miss无需经验和技巧,只需要细心按部就班就能发现的错误,叫“低级Miss”

如:详细设计是输出“哈哈哈”,但代码中却输出“哈哈哈。”多了个句号,这就属于低级Miss

  • 若编码阶段未解决,就需要依靠PT阶段发现,而PT阶段成本高很多;在编码阶段发现,只需要直接修改;而进入PT阶段后,就需要添加bug票,截取相应证据,还需要向客户说明,做bug分析和对策等等。

  • 若PT(单体测试)还未发现,只能到客户进行IT或其他测试才发现,成本更高,需要再次进行PT阶段所有事,加上和客户说明制造阶段没能发现的原因,全项目组横向展开,五个Why分析,包括向高层领导汇报。

【解决方法】

个人通过多次review、使用工具、项目组优化流程

  • review关注点:review哪些内容、review基准是什么、review标准

二、单体测试

2.1单体测试的目的

  • 详细设计自身的问题

  • 代码中存在的问题

最重要的部分是单体测试设计,即单体测试书(要写明每个测试内容的条件预期结果

测试No函数Input函数预期output预期结果
输入的内容函数执行时的预期结果一致:OK 不一致:NG

2.2单体测试的内容

单体测试设计需要

1️⃣对函数要实现的所有功能进行确认

2️⃣对设计或程序代码自身可能存在的不足进行确认

  • 常采用空值,最大值,最小值进行测试

2.3测试观点

为了确认城市是否能正确运行,所使用的着眼点、构思方法等,即进入测试的“切入点”

  • 测试观点即从哪方面进行测试,可以发现问题,通常是大家总结出来最容易出错的情况

  • 类似的测试观点:边界值、错误/异常、数值正确性、性能、最大值、最小值

2.4测试结果

单体测试结果,会记载在以下三个文档:单体测试成绩书,测试证据和bug票

①单体测试成绩书

-相比于单体测试,具有预期结果

  • 给客户时,都要OK,不能存在NG

测试No函数Input函数预期output预期结果
编号输入的内容函数执行时的预期结果一致:OK 不一致:NG
②测试证据

保存程序的运行结果和判断正确与否的过程,需要保留,目的是为了:

1)证明相应测试点被测试过了

2)通过证据,依次对测试结果进行review

下面是xxx函数的单体测试证据
○证据NO1
○证据NO2
③Bug票

将详细问题记录,并进行后续工作,包括:Bug确认,分析和修改

三、单体测试概念

什么是单体测试?是对单元模块逻辑是否正确进行确认

单体测试的目的?为了发现制造阶段的问题,不仅是代码,还包含设计文档的问题

  • 问题包含:

详细设计问题(基本设计书的内容未正确体现在详细设计中)        详细设计错误,遗漏
编码问题(编码未正确反应详细设计)                        代码错误、实现遗漏、理解详细设计有误
代码自身脆弱性                                        变量初始化,空指针,缺少异常处理*
性能问题

Springboot

一、Controller层

   //①不允许在Controller设置私有的属性使用
    //private String test = "1"

二、Mapper层

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
 
    <!-- 1.sql标签:用于便于重复拼接sql语句 参数:①id为当前sql的名字,之后可利用<include refid="sql名称"将其引入 />-->
    <sql id="该sql的名称">
        <!-- 常写一些表字段,方便之后查询,更新的时候引用 -->
         id
        ,NAME
        ,url 
    </sql>
    
    <!-- 2.trim标签 用于删除拼接sql时的AND/OR,防止拼接错误 
                     ①prefix表示要添加的前缀
                     ②suffix表示后缀
                     ③prefixOverrides表示要忽略的前缀字符
                     ④suffixOverrides表示要忽略的后缀字符
    <trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
    </trim>  
    -->
    <select id="selectWebsite" resultType="net.biancheng.po.Website">
	    SELECT id,name,url,age,country
	    FROM website
	    <trim prefix="where" prefixOverrides="and">
	        <if test="name != null and name !=''">
	            AND name LIKE CONCAT ('%',#{name},'%')
	        </if>
	        <if test="url!= null">
	            AND url like concat ('%',#{url},'%')
	        </if>
	    </trim>
   </select>
   
    <!-- 3.where-if标签,用于查询sql语句,有一个if满足,就将其拼接在where后 -->
	    <select id="对应接口的类名" resultType="返回的参数类型">
		    select * from tbl_employee
		    <!-- 如果where中的if有满足的,则sql为 [where if中内容] -->
		    <where>
		        <if test="id!=null">
		            id=#{id}
		        </if>
		        <if test="lastName!=null and lastName!=''">
		            and last_name like #{lastName}
		        </if>
		        <if test="email!=null and email.trim()!=''">
		            and email=#{email}
		        </if>
		        <if test="gender==0 or gender==1">
		            and gender=#{gender}
		        </if>
		    </where>
	    </select>
	    
    <!-- 4.select标签,参数  ①对应接口方法名称:id="接口名(全限定名)" 
                           ②传入参数类型:parameterType="参数是基本数据类型"【可省略】
                                         parameterMap="参数为需要转化的类型【不建议使用,一般会用java进行处理,再传入】
                                                       会在ResultMap标签中,将传入需要进行二次编程数据,进行转化。如:传入的是List<User>"   
                           ③返回结果集:resultType="返回结果直接是基本数据类型"
                                       resultMap="返回结果需要转化
                                                  会在ResultMap标签中,将其进行处理,如返回的数据为List<User>类型,我们需要得到其中User的属性"                                         
       【注意:参数写详细的表字段名,不要写select *】
    -->
    <select id="对应接口类名" parameterType="传入参数类型" resultType="返回结果类型">
        SELECT 
	     <include refid="对应sql标签的名称" />
        FROM website WHERE name LIKE CONCAT ('%',#{name},'%')
    </select>
    
    <!-- 5.查询的注意事项
    【注意】insert和updata中都要写jdbcType,否则不写jdbcType时,传入值为null,会造成Mybatis无法识别null对应的类型,无法转为对应类型存入数据库
               ①# $的区别
	               #表示放?先占位,再将参数放入
	               $表示直接将参数放入
               ②模糊查询
               	利用concat函数,进行%%拼接
                eg: SELECT * FROM user WHERE name LIKE CONCAT('%', #{value}, '%')
    -->
    
 
  
    <!-- 6.insert标签 参数:①parameterType:传入参数的类型,可为实体类/普通数据类型 ②返回参数为更新的条数-->
    <insert id="对应接口类名"  parameterType="全限定包名.类名">
       INSERT  INTO 表名	(
         字段名1
         ,字段名2
         ,字段名3 
       )VALUE (
           #{参数名1,jdbcType=dui'ying}
           ,#{参数名2,jdbcType=对应数据库类型}
           ,#{参数名3,jdbcType=VARCHAR}
       )
    </insert>
     
     <!-- 7.update标签 参数:①parameterType:传入参数的类型,可为实体类/普通数据类型 ②返回参数为更新的条数-->
     <update id="对应接口的类名" parameterType="传入的参数类型" >
      UPDATE 表名 
	      SET 
	     	 name = #{参数1,jdbcType=VARCHAR}
	     	,url= #{参数2,jdbcType=对应数据库类型} 
          WHERE id = #{id,jdbcType=对应数据库类型}
     </update>
     
     <!-- 8.delete标签 -->
     <delete id="对应接口的类名">
        DELETE FROM 表名 WHERE name = #{name}
     </delete>
     
     <!-- 9.resultMap标签:用于解决实体类属性名与数据库表中字段名  
        ①property 是 net.biancheng.po.Website 类中的属性 ②column是查询结果的列名,可以来自不同的表
     -->
     <resultMap type="标识要映射的POJO" id="对应select的resultMap属性的值">
	    <id property="id" column="id" />
        <result property="uname" column="name" />
     </resultMap>
</mapper>



<!-- sql建议三条之内解决
 一、利用SEQUENCE设置自增主键的好处?
	 DROP SEQUENCE IF EXISTS "public"."clockRecoredId";
	 CREATE SEQUENCE 
 
     【自增方式】
		 ①方式一: SELECT coalesce(max(id),0) + 1 as max_id from clock_record
		 ②方式二: SELECT nextval('clockRecoredId')
	 【两者区别】
	  两种方式,在数据库中生成的数有区别,因为事务回滚时,SEQUENCE并不会回滚
		 方式一:一定是连续的数值 eg:123456
		 方式二:会存在空缺的值,eg:12467
 二、事务
 使得事务能发生回滚,保证数据的一致性 
  1.事务的好处
  		1)各事务间具有隔离性:不同的事务之间的操作都不知道,类似拷贝一份数据库,对拷贝数据库进行处理,等提交后,才将修改内容覆盖到原数据库
  		2)保证数据一致性:发生异常时,支持回滚,会回退之前的操作。同时会等待事务提交,所有操作才提交
  2.开启事务和不开启事务的区别
	 【未开启事务时:】
	    ①执行一个操作提交一次
		 用户A-》 以ID=1登录 未开启事务,就会自动提交auto commit
	 	 同时用户B-》 查询ID=1登录,可查询到,因A未开启使用,采取了自动提交
	 
	 【开启事务时:】
		 ①每个事务之间是独立的
		 ②提交事务后,对数据库的一系列操作才生效
		
		  用户A-》 开启事务,表示放弃默认提交-》以ID=1登录
		  同时用户B-》 开启事务,表示放弃默认提交 -》无法查询到ID=1登录,因为用户A的事务还未提交
  3.何时开启事务?
     ❗:只有对数据进行增删改操作,才需要开启事务;查询不要开!!数据库损耗很大		  
 三、Spring在哪开启事务?
  ①【Contoller层调用Service注意】
   Controller层调用Service层方法时,就会Service层方法代码中 >开头自动开启事务,>结尾自动提交事务(未异常的情况下)   
   因而注意在Controller层,不要同时调用两个Service层的方法,否则一个执行完,抛出异常,会导致另一个无法执行,但前一个执行成功
  
   1)错误写法:Spring的Service层方法中添加了事务,只有当Controller层调用每一个Service层方法都是一个事务
              而Controller层一个方法调用Service多个方法,会导致userMapper.deleteUser()仍然执行
   eg:✖
   ①Controller层
       public void User(){
         userMapper.deleteUser()
         throw new Exception(")//出现异常
         userMapper.updateUser()
       }
       
    2)错误写法:Controller调用Service层时,Controller需要添加@Transactional,否则Service方法出现异常,执行一半时,才会执行事务回滚
       
   【Service层内嵌Service层注意】
     Service可注入其他Service,但不要出现两个Service层互相注入的情况!!
     eg:✖
       TestService1{
          @Autowired
          private TestService2 testService;
      }
       TestService2{
          @Autowired
          private TestService1 testService;
      }
 四、@Transactional 自动事务【也可手动,用的不多】
      1)可添加到Service层类/方法上
      2)只能应用到public方法才有效
 五、排他处理
 【方法一:】
   设置创建者,创建时间,更新者,更新时间 这四个字段
   
	 根据更新时间去表中进行通过判断更新记录数是不是1
	 将新的更新时间作为update的where条件
	 select from 表名 where 更新时间
	 ①用户A=>查询bd,id=1(查询时得到该条记录的更新时间)=>返回前端画面(将更新时间保存到浏览器)=>用户编辑数据(5分钟)=》发送服务器端
	 (根据之前查询的更新时间作为条件,查询表中是否有该更新时间的记录,有?将新的更新时间替换!|无?表示该条记录已经被修改,请重新刷新在操作)
	 =》update db,id=1=>commit
	 用户B=>查询bd,id=1=>返回前端画面=>用户编辑数据(6分钟)=》发送服务器端=》update db,id=1=>commit    
	 
 【方法二:事务+for update 用户查询编辑数据时,不能用这种方式,时间太久了】
 在查询该记录时先将其锁上,再等待该条记录更新时将其自动解锁;而其他用户使用,就需要等待该条记录解锁,达到超时时间则异常  (for update)
    
    用户A=》开启事务 = 》查询db,id=1 for update(锁住) = > 编辑=>update db,id=1(解锁)=> commit;
    用户B=》开启事务 = 》查询db,id=1 for update = > 编辑=>update db,id=1=> commit;
    
   【no wait:不进行等待,要是之前被锁了直接抛出异常】
    用户A=》开启事务 = 》查询db,id=1 for update(锁住) = > 编辑=>update db,id=1(解锁)=> commit;
    用户B=》开启事务 = 》查询db,id=1 for update no wait = > 编辑=>update db,id=1=> commit;
    
   【如果锁住后,不update,db会自行将其在commit解锁】
    用户A=》开启事务 = 》查询db,id=1 for update(锁住 => commit;
    用户B=》开启事务 = 》查询db,id=1 for update = > 编辑=>update db,id=1=> commit;
    
  六、表连接
   1)内连接+外连接
   ①A inner join B 查询的表数据,AB字段都不为null
   ②A left join  B 查询的表数据, A必须有,B可以null
   ③A right join  B 查询的表数据,A可以null,B必须有
   ④A full join B 查询的表数据,AB都可null  
   【full join查询结果】 
     头      头
     ...    ....
     
 七、UNION和UNION ALL
   ①UNION——去重
    将两个表的数据结合为一个表
      两个sql语句可以查询各自的表
      用UNION连接的前后两个SQL语句查询出来的结果在字段顺序,字段名称,字段类型都要一致
      
      【查询时两个sql语句】
       1)列名不同->设置别名 
       SELECT text1,text2 FROM user1 WHERE ID=1
       UNION 
       SELECT text1,text3 FROM user2 WHERE ID=2 
      2)类型不同->强转类型
      【查询结果】
       头
       ...
       ...
       
   ②UNION ALL ——不去重    
   
   ③UNION和UNION ALL结合排序👇
     UNION连接的两个sql各自排序时,并不能实现排序,若存在,仅是偶然
     【实现UNION结合两个表查询结果,同时将两表分别进行排序】
     -各自查询两个表数据,UNION ALL将两表数据结合(注意UNION ALL会将两个表查询的数据打乱)
     -将结合后的表数据,根据设置的group_no字段,排序=》区分表1,表2
     -利用postgre的row_num设置一个序列字段data_index,1~....,根据设置的group_no和data_index字段一起进行排序=》即区分表1,表2,又按照各表进行相应排序
     SELECT * FROM(
      SLECT '1' as group_no , 1 as data_index ,text1,text2 FROM user1 WHERE ID=1 
      UNION ALL
       SLECT '2' as group_no ,2 as  data_index ,text1,text2 FROM user2 WHERE ID=2
      ) data
      ORDER BY group_no
      
      实现结果集,为以下情况:(group_no区分哪些表1,哪些表2;data_index区分各表内的排序)
      group_no  data_index 
         1         1
         1         2
         1         3
         1         4 
         2         1
         2         2
         2         3
         2         4            
 八、Group by
   ①group by条件后加的字段,必须存在于SELECT后,即出现在查询结果表的列名中
   ②order by排序后加的字段,要放在group by聚合条件后
   SELECT classId FROM user WHERE classId = 1 ORDER BY classNa(当在结果表中找不到classNa会找主表中的classNa,然后再结果中删除classNa列名)
   SELECT classId FROM user WHERE classId = 1 GROUP BY classId ORDER BY classNa(✖,因为在聚合后的结果中,未将classNa一起作为聚合条件,聚合时容易将其合并,就难以在进行排序)
   SELECT classId,classNa FROM user WHERE classId = 1 GROUP BY classId,classNa ORDER BY classNa
		   classId  classNa
		     1         1班名称1
		     1         1班名称2
		     2         2班名称1   
  九、where和having
	   where是在分组前执行
	   having是在分组后执行	
   !优先使用where再使用having,因为having是在group by后执行,在数据库查询表后,会将查询结果表从硬盘->内存中在进行group by->having【占运行内存】
   【数据库执行时,变select查询边where筛选】
   【数据库执行时,group by和having一起执行】	     
 -->

三、Service层

package com.neu.kq.service;

public class API接口 {
/*接口包含,
	①协议:get/post 
	②路径 : /xx/xx 
	③参数 
	④返回值
	
	一、用户权限表
	协议    路径             参数       返回值(json类型)
	get    /user/getUser    userid     {   
	                                       status: 当前请求状态码
	                                      ①请求头
	                                       headers:{
	                                      
	                                       },
	                                       ②请求体
	                                       body:{
	                                           code:400  //方便浏览器端知道本次请求的状态信息,一个code对应一个函数
	                                           //返回的页面信息
	                                           userinfo:{
	                                             key:value
	                                           }
	                                           //返回的错误/提示消息
	                                           message:[{
			                                             level:"error"//消息级别,是提示/错误消息
			                                             info:"用户不可以为空"
			                                           },{
			                                             level:"error"//消息级别,是提示/错误消息
			                                             info:"用户不可以为空"
			                                           }]
	                                            }
	                                   }
	                                   
	                                                        Ⅱ、返回的错误/提示消息,不传入内容,传入对应CODE值,交由前端进行解析,将CODE转化为对应的语言(英文、中文)
	                                   message{
	                                       info:"ERROR-CODE1"或info:"INFO-CODE2"
	                                       params:[
	                                         {0:"1"},
	                                         {1:"admin"}
	                                       ]
	                                       
	                                   }
	                                  对应的后端发送值
	                                   {
	                                     "信息CODE":"信息内容”,
	                                     "CODE1":"用户名不为空",
	                                     "信息CODE":"信息内容”,
	                                     "CODE2":"密碼不为空",
	                                   }
	                                   
	       /user/listUser
*/	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邓六日

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值