admin管理员组文章数量:1532714
文章目录
- JAVA
- 第一章、Java概述
- 1、什么是程序
- 2、Java背景
- 3、为社么要用Java
- 4、Java的技术体系
- 5、Java 运行机制及运行过程
- 5.1、Java 语言的特点:跨平台性
- 5.2、Java 核心机制-Java 虚拟机
- 6、什么是JDK,JRE
- 6.1、JDK 基本介绍
- 6.2、JRE 基本介绍
- 6.3、JDK、JRE 和 JVM 的包含关系
- 7、JDK的安装与配置
- 7.1、JDK的安装
- 7.2、环境便量的配置
- 8、IDEA基本用法
- 8.1、创建框架
- 8.2、常用快捷键
- 8.3、Debug工具
- 9、Java入门
- 9.1、“Hello World”
- 9.2、 开发步骤
- 10、 Java 转义字符
- 11、注释
- 11.1、 介绍
- 11.2、 Java 中的注释类型
- 12、Java 代码规范
- 13、相对路径, 绝对路径
- 14、常用的 dos 命令
- 第二章、变量
- 1、变(变化)量(值)的介绍
- 1.1、概念
- 1.2、变量的格式
- 2、变量使用的基本步骤
- 3、注意事项
- 4、字面量
- 5、数据类型
- 5.1、基本数据类型:四类八种
- 5.2、引用数据类型
- 5.3、自动类型转换
- 5.4、强制类型转换
- 5.5、基本数据类型和 String 类型的转换
- 6、Java API 文档
- 7、ASCII 码介绍
- 8、Unicode 编码介绍
- 9、UTF-8 编码介绍
- 第三章、运算符
- 1、算数运算符
- 2、数值拆分
- 3、赋值运算符
- 4、关系运算符
- 5、逻辑运算符
- 6、三元运算符
- 7、运算符优先级
- 8、关键字,标识符
- 9、键盘插入
- 10、数据存储
- 3.11进制
- 11.1、进制类型
- 11.2、进制转换
- 12、原码、反码、补码
- 13、位运算符
- 第四章、流程控制
- 1、顺序结构
- 2、分支结构
- 2.1、if分支 (适用于区间)
- 2.2、switch分支(适用于值)
- 3、循环结构
- 3.1、for循环(知道循环次数的话用for方便)
- 3.2、while循环
- 3.3、do_while循环
- 3.4、三种循环的差别
- 4、死循环
- 5、嵌套循环
- 6、跳转关键字
- 7、生成随机数
- 第五章、数组
- 1、数组的介绍
- 2、静态初始化数组(知道里面内容)
- 3、数组的引用
- 4、数组的遍历
- 5、动态初始化数组(不知道里面内容)
- 6、数组执行原理
- 7、数组使用注意事项和细节
- 8、数组赋值机制
- 9、数组拷贝
- 10、数组反转
- 11、数组添加/扩容
- 12、排序
- 12.1、内部排序:
- 12.2、外部排序法:
- 12.3、冒泡排序法
- 12.4、选择排序
- 13、查找
- 13.1、顺序查找
- 13.2、二分查找
- 14、多维数组-二维数组
- 14.1、动态初始化
- 14.2、动态初始化-列数不确定
- 14.3、静态初始化
- 14.4、二维数组使用细节和注意事项
- 第六章、面向对象编程
- 1、类与对象
- 1.1、基本介绍
- 1.2、类与对象的关系
- 1.3、类与对象的区别于联系
- 1.4、 属性/成员变量/字段
- 1.5、如何创建对象
- 1.6、如何访问属性
- 1.7、对象在内存中的存在形式
- 2、方法
- 2.1、什么是方法
- 2.2、方法的好处
- 2.3、方法的定义和调用
- 2.4、方法的调用机制原理
- 2.5、带参数方法的定义和调用
- 2.6、带返回值方法的定义和调用
- 2.7、方法在计算机中的执行原理
- 2.8、Java的参数传递机制都是:值传递
- 2.9、引用类型的参数传递:传的是变量中保存的地址
- 3、方法重载
- 3.1、法重载的定义
- 3.2、方法重载的注意事项
- 4、作用域
- 4.1、作用域的基本使用
- 4.2、注意事项
- 5、构造方法/构造器
- 5.1、基本语法
- 5.2基本介绍
- 5.3、注意事项
- 6、this关键字
- 6.1、注意事项
- 7、get,set方法
- 8、访问修饰符
- 8.1、基本介绍
- 8.2、注意事项
- 9、封装
- 9.1、介绍
- 9.2、封装的好处
- 9.3、实现步骤
- 9.4、注意
- 10、static(静态)
- 10.1、介绍
- 10.2、成员变量
- 10.3、成员方法
- 10.4、工具类
- 10.5、注意事项
- 10.6、代码块
- 10.7、总结
- 11、继承
- 11.1、介绍
- 11.2、注意事项
- 11.2.1、权限修饰符
- 11.2.2、单继承
- 11.2.3、方法重写
- 11.2.4、子类中访问其他成员的特点
- 11.2.5、子类构造器的特点
- 12.3、阻止继承
- 12、多态
- 12.1、介绍
- 12.2、多态的应用
- 12.3、好处与弊端
- 13、final
- 14、抽象类
- 14.1、介绍
- 14.2、使用条件
- 14.3、模板方法设计模式
- 15、接口
- 15.1、介绍
- 15.2、好处
- 15.3、新增
- 15.4、接口的多继承
- 15.5、接口和继承的区分
- 16、常量
- 17、枚举
- 18、内部类
- 18.1、介绍
- 18.2、成员内部类
- 18.3、静态内部类
- 18.4、局部内部类
- 18.5、匿名内部类
- 第七章、泛型
- 1、介绍
- 2、泛型类
- 3、泛型接口
- 4、泛型方法
- 5、通配符
- 6、泛型的擦除问题和注意事项
- 7、自定义泛型类
- 8、自定义泛型接口
- 9、自定义泛型方法
- 第八章、常用的API
- 1、包
- 1.1、包的概念与创建方法
- 1.2、注意事项
- 2、Arrays
- 3、equals 方法(Object)
- 4、hashCode 方法
- 5、toString方法(Object)
- 6、finalize方法
- 7、clone克隆(Object)
- 8、Objects
- 9、包装类
- 9.1、介绍
- 9.2、创建对象
- 9.3、Integer 类和 Character 类的常用方法:
- 10、Math
- 11、System
- 12、Runtime
- 13、BigDecimal
- 第九章、日期、时间
- 1、Date
- 2、SimpleDateFormat
- 3、Calendar
- 4、JDK8新增的时间
- 5、LocalDateTime、LocalTime、LocalDate
- 6、转换相关的API
- 7、ZoneId、ZonedDateTime
- 8、DateTimeFormatter
- 第十章、String
- 1、含义与创建方法
- 2、执行原理
- 3、使用方法
- 4、注意事项
- 5、StringBuffer与StringBuilder
- 6、StringJoiner
- 第十一章、异常
- 1、介绍
- 2、常见的运行时异常
- 3、编译异常
- 4、异常处理
- 4.1、抛出异常
- 4.2、捕获异常
- 5、自定义异常
- 6、异常的作用
- 7、throw 和 throws 的区别
- 第十二章、Lambda表达式
- 第十三章、方法引用
- 1、静态方法的引用
- 2、实例方法的引用
- 3、特定类型的方法引用
- 4、构造器引用
- 第十四章、正则表达式
- 第十五章、注解
- 1、介绍
- 2、基本的 Annotation 介绍
- 3、JDK 的元 Annotation(元注解)
- 3.1、元注解的基本介绍
- 3.2、@Retention
- 3.3、@Target
- 3.4、@Documented
- 3.5、@Inherited
- 4、注解的解析
- 5、注解的属性
- 第十六章、集合
- 1、概述
- 2、Collection集合体系
- 3、Collection的常用方法
- 4、Collection的遍历方式
- 4.1、迭代器
- 4.2、增强for循环
- 4.3、Lambda
- 5、Collection的并发修改异常
- 6、List接口
- 6.1、List特有方法
- 6.2ArrayList
- 6.2.1、含义
- 6.2.2、注意事项
- 6.2.3、方法
- 6.2.4、底层原理
- 6.2.5、ArrayList集合适合的应用场景
- 6.3、Vertor
- 6.3.1、基本介绍
- 6.3.2、Vector 和 ArrayList 的比较
- 6.4、LinkedList
- 6.4.1、介绍
- 6.4.2、方法
- 6.4.3、底层原理
- 6.4.3、ArrayList 和 LinkedList 比较
- 7、Set接口
- 7.1、介绍
- 7.2、HashSet
- 7.2.1、介绍
- 7.2.1、 Hash值
- 7.2.2、底层原理
- 7.2.3、树结构
- 7.3、LinkedHashSet
- 7.4TreeSet
- 8、Map接口
- 8.1、介绍
- 8.2、集合体系:
- 8.3、常用方法
- 8.4、遍历方法
- 8.5、HashMap
- 8.5.1、介绍
- 8.5.2、底层机制
- 8.6、HashTable
- 8.6.1、介绍
- 8.6.2、 Hashtable 和 HashMap 对比
- 8.7、Properties
- 8.7.1、介绍
- 8.7.2、使用
- 8.8、LinkedHashMap
- 8.9、TreeMap
- 9、总结
- 10、Collections 工具类
- 10.1、介绍
- 10.2、排序
- 10.3、查找,替换
- 11、集合嵌套
- 12、可变参数
- 13、Stream
- 13.1、含义
- 13.2、步骤
- 13.3、获取Stream流
- 13.4、常见的中间方法
- 13.5、常见的中间方法
- 第十七章、递归
- 第十八章、IO流
- 1、File
- 2、方法
- 3、注意事项
- 4、字符集
- 4.1、介绍
- 4.2、编码,解码
- 5、IO流
- 5.1、概述
- 5.2、分类
- 5.3、FileInputStream(文件字节输入流)
- 5.4、FileOutputStream(文件字节输出流)
- 5.5、FileReader(文件字符输入流)
- 5.6、FileWriter(文件字符输出流)
- 5.7、注意事项
- 5.8、字节缓冲流
- 5.9、字符缓冲流
- 5.10、字符输入转换流
- 5.11、字符输出转换流
- 5.12、PrintStream/PrintWriter(打印流)
- 5.13、DataOutputStream(数据输出流)
- 5.14、DataInputStream(数据输入流)
- 5.15、ObjectOutputStream(对象字节输出流)
- 5.16、ObjectInputStream(对象字节输入流)
- 6、释放资源的方式
- 7 、IO框架
- 第十九章、配置文件
- 1、介绍
- 2、Properties
- 3、XML
- 3.1、认识XML
- 3.2、读取XML
- 3.3XML约束
- 第二十章、日志技术
- 1、概述
- 2、Logback快速入门
- 3、Logback设置日志级别
- 第二十一章、线程
- 1、介绍
- 2、线程的创建方式
- 2.1、继承Thread类
- 2.2实现Runnable接口
- 2.3、实现Callable接口
- 2.4、比较
- 2.5、Thread类的方法
- 3、线程安全问题
- 4、线程同步方案
- 4.1、认识线程同步
- 4.2、方式一:同步代码块
- 4.3、方式二:同步方法
- 4.4、方式三:Lock锁Lock锁
- 4.5、总结
- 5、线程池
- 5. 1、概述
- 5.2、线程池的创建
- 5.3、线程池处理Runnable任务
- 5.4、线程池处理Callable任务
- 5.5、Executors工具类实现线程池
- 6、线程通信
- 7、进程与线程
- 8、并发与并行
- 9、线程生命周期
- 10、互斥锁
- 11、死锁
- 第二十二章、网络通信
- 1、介绍
- 2、网络通信三要素
- 2.1、IP地址
- 2.2、端口号
- 2.3、协议
- 3、UDP通信
- 4、TCP通信
- 5、InetAddress 类
- 6、Socket
- 7、netstat 指令
- 第二十三章、单元测试
- 1、介绍
- 2、Junit框架的常见注解
- 第二十四章、反射
- 1、介绍
- 2、加载类
- 3、获取类构造器
- 4、获取类的成员变量
- 5、获取类的成员方法
- 6、作用、应用场景
- 第二十五章、lombok
- 第二十六章、maven
- 1、介绍
- 2、创建maven工程
- 2.1、配置Maven环境
- 2.2、创建maven项目
- 2.3、导入maven项目
- 3、maven细节
- 3.1、依赖配置
- 3.2、常见命令
- 3.3、生命周期
- 4、分模块构建
- 4.1、服务拆分
- 4.2、创建父子工程
- 4.2.1、创建父工程
- 4.2.2、创建common模块
- 4.2.3、创建sky-pojo模块
- 4.2.4、创建sky-server模块
- 4.3、分模块细节
- 4.3.1、模块继承
- 4.3.2、模块聚合
- 4.3.3、依赖传递
- 4.4、依赖冲突
- 4.4.1、第一声明优先原则
- 4.4.2、路径近者优先原则
- 4.4.3、依赖排除
- 4.4.4、版本锁定
- 5、私服
- 5.1、简介
- 5.2、搭建私服
- 5.3、仓库类型
- 5.4、将项目发布到私服
- 5.5、从私服下载依赖
- 第二十七章、Mybatis
- 1、介绍
- 2、Mybatis实现增删改
- 2.1、增加
- 2.2、修改
- 2.3、删除
- 4、查询
- 4.1、查询结果封装
- 4.2、条件查询
- 4.3、模糊查询
- 5、抽取工具
- 6、xml书写sql
- 7、动态sql
- 7.1、介绍
- 7.2、`<if>`和`<where>`
- 7.4、`<if>`和`<set>`
- 7.5、`<foreach>`
- 7.6、 `<sql>`
- 7.7、 `<include>`
- 8、配置文件
- 9、MyBatis Generator代码生成
- 9.1、概述
- 9.2、环境集成
- 9.3、代码生成
- 第二十八章、MyBatis——Plus
- 1、介绍
- 2、常用注解
- 2.1、@TableName
- 2.2、@TableField
- 2.3、@TableId
- 2.4、小结
- 3、Mapper接口
- 3.1、介绍
- 3.2、条件查询
- 3.2.1、API
- 3.2.2、查询优化-简化判断
- 3.2.3、查询优化-lambda查询
- 3.2.4、查询优化-链式编程
- 3.2.5、设置查询字段
- 3.2.6、分组排序
- 3.3、条件修改
- 3.4、条件删除
- 4、Service接口
- 4.1、介绍
- 4.2、改造Service
- 5、自动填充
- 6、代码生成器
- 第二十九章、Spring框架
- 1、Spring介绍
- 2、控制反转
- 2.1、IOC概念
- 2.2、添加配置类
- 2.3、Bean创建
- 2.4、Bean作用域
- 2.4.1、单例对象
- 2.4.2、多例对象
- 2.5、Bean创建时机
- 2.5.1、单例对象
- 2.5.2、多例对象
- 2.6、Bean获取
- 2.7、依赖注入
- 2.7.1、@Autowired
- 2.7.2、@Qualifier
- 2.7.3、@Primary
- 2.7.4、构造器依赖注入
- 2.8、管理第三方Bean
- 2.8.1、将对象放入容器
- 2.8.2、配置类优化
- 2.9、整合Mybatis
- 2.10、注解总结
- 3、 AOP
- 3.1、AOP介绍
- 3.2AOP核心概念
- 3.3、通知类型
- 3.4、切点表达式
- 3.4.1、execution
- 3.4.2、@annotation
- 3.4.3、记录日志详情
- 4、事务管理
- 4.1、事务回顾
- 4.2、事务管理
- 4.3、事务属性
- 4.3.1、rollbackFor
- 4.3.2、propagation
- 5、SpringMVC
- 5.1、介绍
- 5.2注解
- 5.2.1@RequestMapping
- 5.2.2@ResponseBody
- 5.3接收请求参数
- 5.4统一异常处理
- 第三十章、Restful
- 第三十一章、SpringBoot
- 1、概述
- 2、主要功能
- 3、配置文件
- 3.1自定义配置
- 3.2、YAML介绍
- 3.2.1、语法
- 3.2.2、数据格式
- 3.3、读取配置
- 3.4、多环境配置
- 4、常用功能
- 第三十二章、微服务
- 1、单体架构
- 2、微服务
JAVA
提示:这是Java篇
第一章、Java概述
1、什么是程序
程序:计算机执行某些操作或解决某个问题而编写的一系列有序指令的集合。
2、Java背景
Java是美国sun公司在1995年推出的一款高级编程语言。
Java之父詹姆斯.高斯林。
2009年sun公司被oracle公司收购。
3、为社么要用Java
- 功能最丰富,可以用在大部分的项目开发中。
- 可移植性、安全可靠、性能较好,开发社区最完善。
- 当然Java能做的东西也比较多:企业级应用开发,游戏,电脑桌面开发,移动应用开发,大数据,服务器系统等
- Java 语言是面向对象的
- Java 语言是健壮的。Java 的强类型机制、异常处理、垃圾的自动收集等是 Java 程序健壮性的重要保证
- Java 语言是跨平台性的。[即: 一个编译好的.class 文件可以在多个系统下运行,这种特性称为跨平台]
4、Java的技术体系
JavaSE(整个体系的基础),JavaEE(企业级应用的应用开发),Java ME(移动设备的应用开发)
5、Java 运行机制及运行过程
5.1、Java 语言的特点:跨平台性
Java可以一次编译,处处使用的原因
Java在每个操作系统的jdk中都有一个各自类型的jvm(java的虚拟机)所以我们只需要将写好的文件编译好就可以在不同的平台运行了。
5.2、Java 核心机制-Java 虚拟机
基本介绍
- JVM 是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器,包含在
JDK 中. - 对于不同的平台,有不同的虚拟机。
- Java 虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行” [说明]
6、什么是JDK,JRE
6.1、JDK 基本介绍
- JDK 的全称(Java Development Kit Java 开发工具包)
JDK = JRE + java 的开发工具 [java, javac,javadoc,javap 等] - JDK 是提供给 Java 开发人员使用的,其中包含了 java 的开发工具,也包括了 JRE。所以安装了 JDK,就不用在单独安装 JRE 了。
6.2、JRE 基本介绍
- JRE(Java Runtime Environment Java 运行环境)
JRE = JVM + Java 的核心类库[类] - 包括 Java 虚拟机(JVM Java Virtual Machine)和 Java 程序所需的核心类库等,如果想要运行一个开发好的 Java 程序,计算机中只需要安装 JRE 即可。
6.3、JDK、JRE 和 JVM 的包含关系
- JDK = JRE + 开发工具集(例如 Javac,java 编译工具等)
- JRE = JVM + Java核心类库
- 如果只想运行开发好的 .class 文件 只需要 JRE
7、JDK的安装与配置
7.1、JDK的安装
官网下载,一定要记住安装到的位置,安装路径中不能有中文或特殊符号。
7.2、环境便量的配置
配置环境变量将%JAVA_HOME%配置到path下面这样你就可以在控制台下输入Java -version来打开JDK。
8、IDEA基本用法
8.1、创建框架
先创建一个项目(project)并设置jdk,在项目中创建一个模块(module),在模块中创建一个包(package),在包中创建一个类(class)。
8.2、常用快捷键
Ctrl+C(复制), Ctrl+V(粘贴), Ctrl+X(剪贴),Ctrl+D(将本行文字复制到下一行),Ctrl+Alt+L(将代码格式化),Alt+Shift+上箭头/下箭头(上下移动该行代码)
8.3、Debug工具
在需要控制的代码行左侧,点击一下,形成断点
选择使用Debug方式启动程序,启动后程序会在断点暂停
控制代码一行一行的往下执行
9、Java入门
9.1、“Hello World”
public class Demo {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
9.2、 开发步骤
- 将 Java 代码编写到扩展名为 Hello.java 的文件中。
- 通过 javac 命令对该 java 文件进行编译,生成 .class 文件。
- 通过 java 命令对生成的 class 文件进行运行。
10、 Java 转义字符
在控制台,输入 tab 键,可以实现命令补全
\t :一个制表位,实现对齐的功能
\n :换行符
\ :一个\
" :一个"
’ :一个’
\r :一个回车(r后面的字会挨个顶掉前面的字)
11、注释
11.1、 介绍
用于注解说明解释程序的文字就是注释,注释提高了代码的阅读性(可读性);注释是一个程序员必须要具有的良好编程习惯。将自己的思想通过注释先整理出来,再用代码去体现。
11.2、 Java 中的注释类型
- 单行注释://
- 多行注释:/* 注释文字 */
- 文本注释:/** 注释文字 */(注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档,一般写在类)
快捷键:Ctrl+/(对选中文本进行单行注释),Ctrl+Shift+/(对选中文本进行多行注释)
注意:
- 被注释的文字,不会被 JVM(java 虚拟机)解释执行。
- 多行注释里面不允许有多行注释嵌套。
12、Java 代码规范
- 运算符和=两边记得加空格。
- 代码编写风格分为两种次行风格和行尾风格。
- 源文件使用UTF-8编码
- 行宽不得超过80个字符
(记得经常按Ctrl+Alt+l)
13、相对路径, 绝对路径
14、常用的 dos 命令
- 查看当前目录是有什么内容 dir
- 切换到其他盘下:切换到 c 盘 cd /D c:
- 切换到当前盘的其他目录下 (使用相对路径和绝对路径演示), …\表示上一级目录
- 切换到上一级: cd …
- 切换到根目录:cd \
- 查看指定的目录下所有的子级目录 tree
- 清屏 cls
- 退出 DOS exit
- md [创建目录]
- rd [删除目录]
- copy [拷贝文件]
- del [删除文件]
- echo [输入内容到文件]
- type,move [剪切]
第二章、变量
1、变(变化)量(值)的介绍
1.1、概念
变量相当于内存中一个数据存储空间的表示,用来记录程序要处理的数据。
1.2、变量的格式
数据类型 + 变量名称 = 数据
2、变量使用的基本步骤
- 声明变量
int a; - 赋值
a = 60;
System.out.println(a);
3、注意事项
- 变量要先声明才能使用。
- 变量是从定义开始到“}”截止的范围内有效;且同一个范围内,定义的多个变量,它们的名称不能一样。
- 变量定义的时候可以不赋初始值;但在使用时,变量里必须有。
4、字面量
数据在程序中的书写方式
字符:’ '(只能为单个字符)
字符串:" " (可以是任意字符)
整数,小数正常输入就行
布尔值:true,false
空值:null
5、数据类型
5.1、基本数据类型:四类八种
整数型(byte(占用1字节),shout(占用2字节),int(占用4字节),long(占用8字节))整数默认int类型,如想用long须在数据末尾加上l。
浮点型(float(占用4字节),double(占用8字节))浮点数默认double类型,如想用float须在数据末尾加上f。关于浮点数在机器中存放形式的简单说明,浮点数=符号位+指数位+尾数位。
布尔型(boolean)ture,false。 占用1字节
字符型(char)单个字符。 占用8字节(char类型是可以运算的,相当于一个整数,因为它都有对应的Unicode码)
5.2、引用数据类型
字符串(String),类(class),接口(interface),数组([ ])
5.3、自动类型转换
byte → sout →(char)→ int → long → float → double
小变大是自由变换数据不会丢失
5.4、强制类型转换
大变小(数据会出先丢失) " 数据类型 变量名称 =(数据类型)数据 "
5.5、基本数据类型和 String 类型的转换
注意:在将 String 类型转成 基本数据类型时,要确保String类型能够转成有效的数据。
6、Java API 文档
7、ASCII 码介绍
0对应数字48,A对应数字65,a对应数字97
8、Unicode 编码介绍
9、UTF-8 编码介绍
第三章、运算符
1、算数运算符
正号(+),负号( - ),加号(+),减号( - ),乘号( * ) ,除号( / ) , 取余 ( % ),自增(++),自减( - -),字符串相加( + )
自增自减:单独使用前后相同,不是单独使用前后不一致(放在变量前先加减1,放在变量后先运算再加减1)
对于/,如果输入的是整数输出的也是整数,如果输入的是小数输出的也是整数。
2、数值拆分
个位 :数据 % 10 ;
十位 :数据 / 10 % 10 ;
(之后同理)
3、赋值运算符
+=,-=,*=,/=,%=
赋值运算符左边只能是变量
4、关系运算符
=,>,<,<=,!=,==
5、逻辑运算符
- a&b : & 叫逻辑与:规则:当 a 和 b 同时为 true ,则结果为 true, 否则为 false。
- a&&b : && 叫短路与:规则:当 a 和 b 同时为 true ,则结果为 true,否则为 false,如果左面为false则直接输出false不继续执行
- a|b : | 叫逻辑或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false。
- a||b : || 叫短路或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false,如果左边为true则直接输出true不继续执行。
- !a : 叫取反,或者非运算。当 a 为 true, 则结果为 false, 当 a 为 false 是,结果为 true。
- a^b: 叫逻辑异或,当 a 和 b 不同时,则结果为 true, 否则为 false。
6、三元运算符
格式:条件表达式 ? 值1 :值2;
如果条件表达式为 true,运算后的结果是值 1;如果条件表达式为 false,运算后的结果是值 2。
7、运算符优先级
8、关键字,标识符
关键词(48个关键词,2个保留字,3个特殊直接变量)
标识符:我们自己定义的名字。
基本组成:数字(1),字母(a),下划线(_),美元符号($)
强制要求:
数字不能放在首位,区分大小写,不能用关键词作为标识符。
大小驼峰原则:变量名称:满足“小驼峰模式”,例如:studyNumber
类名称: 满足“大驼峰模式”,例如: HelloWorld, Student
9、键盘插入
Scanner
导包:import java.util.Scanner;
扫描器对象:Scanner scanner = new Scanner(System.in); (.var)
等待用户输入:int i = scanner.nextInt();
10、数据存储
计算机内存储的数据都是以二进制的形式存储的。
二进制存储的基本单位是字节(byte)
二进制存储的最小的单位是比特(bit)
一字节等于八比特
3.11进制
11.1、进制类型
二进制:0,1 ,满 2 进 1.以 0b 或 0B 开头。
十进制:0-9 ,满 10 进 1。
八进制:0-7 ,满 8 进 1. 以数字 0 开头表示。
十六进制:0-9 及 A(10)-F(15),满 16 进 1. 以 0x 或 0X 开头表示。此处的 A-F 不区分大小写。
11.2、进制转换
x进制转十进制:第一位 * x的(1-1)次方 + 第二位 * x的(2-1)次方 + 第三位 * x的(3-1)次方 + …
十进制转x进制:短除法,除x取余整数倒着取,小数正着取。
x进制转二进制:8421码。(八进制三位,十六进制四位)
12、原码、反码、补码
13、位运算符
java 中有 7 个位运算(&、|、^、~、>>、<<和 >>>)
- 按位与&: 两位全为1,结果为1,否则为0
- 按位或|: 两位有一位为1,结果为1,否则为0
- 按位异或^: 两位一个为0,一个为1,结果为1,否则为0
- 按位取反~: 0 ->1,1->0
- 算术右移 >>:低位溢出,符号位不变,并用符号位补溢出的高位
- 算术左移 <<: 符号位不变,低位补 0
- 逻辑右移>>>:也叫无符号右移,运算规则是: 低位溢出,高位补 0
- 特别说明:没有 <<< 符号
第四章、流程控制
1、顺序结构
平常用的都是顺序
2、分支结构
2.1、if分支 (适用于区间)
//单分支if:
if (条件) {
语句体;
}
//双分支if:
if (条件){
语句体;
}else{
}
//多分支if:
if (条件){
语句体;
}else if (条件) {
语句体;
}else{
}
2.2、switch分支(适用于值)
switch(表达式){
case 值1:
语句体;
break;
case 值2:
语句体;
break;
default;
}
注:
- 表达式类型可以是byte、short、int、char、枚举、String,不能是double、float、long
- case给出的值不允许重复,且只能是字面量,不能是变量。
- 正常使用switch的时候,不要忘记写break,否则会出现穿透现象。
3、循环结构
3.1、for循环(知道循环次数的话用for方便)
for(初始化语句 ; 条件判断语句; 条件变更语句 ) {
循环体语句 ; //要重复执行的代码
}
3.2、while循环
初始化语句;
while (条件判断语句) {
循环体语句;//被重复执行的代码
条件变更语句;
}
3.3、do_while循环
初始化语句;
do {
循环体语句;
条件变更语句;
} while (条件判断语句);
3.4、三种循环的差别
for循环 和 while循环(先判断后执行); do…while (先执行后判断)。
for循环和while循环的执行流程是一模一样的,功能上无区别,for能做的while也能做,反之亦然。
使用规范:如果已知循环次数建议使用for循环,如果不清楚要循环多少次建议使用while循环。
其他区别:for循环中,控制循环的变量只在循环中使用。while循环中,控制循环的变量在循环后还可以继续使用。
4、死循环
for ( ; ; ) {
System.out.println("Hello World1");
}
// 经典写法
while (true) {
System.out.println("Hello World2");
}
//其他写法
do {
System.out.println("Hello World3");
} while (true);
5、嵌套循环
for(i = 1; i <= 3; i++){
for(j = 0; j <= 4; j++){
System.out.println('#');
}
}
内部循环一轮外部循环一次。
6、跳转关键字
break: 跳出并结束当前所在循环的执行。(只能用于结束所在循环, 或者结束所在switch分支的执行。)
continue: 用于跳出当前循环的当次执行,直接进入循环的下一次执行。(只能在循环中使用。)
return:+使用在方法,表示跳出所在的方法
7、生成随机数
Random:
导包:import java.util.Random;
拿取:Random r = new Random();
调用:int number = r.nextInt(10);
第五章、数组
1、数组的介绍
定义:数组就是一个容器,用来存储一批同种类型的数据。
使用情景:批量操作同类型数据时,建议使用数组。
效果:使用数组可以减少代码开发,而且代码逻辑更清晰。
数组是引用数据类型。
2、静态初始化数组(知道里面内容)
定义:定义数组的时候直接给数组赋值。
//完整格式
数据类型[ ] 数组名 = new 数据类型[{元素1,元素2 ,元素3… };
int[ ] ages = new int[ ] {12, 24, 36};
// 简化格式
数据类型[ ] 数组名 = { 元素1,元素2 ,元素3,… };
int[ ] ages = {12, 24, 36};
注意:
“数据类型[] 数组名”也可写成 “数据类型 数组名[] ”。
什么类型的数组只能存放什么类型的数据。
3、数组的引用
数组的引用:数组名[ ]
数组的长度:length
最大索引:数组名. length – 1 (元素个数大于零)
4、数组的遍历
定义:遍历:就是一个一个数据的访问。
int[ ] ages = {20, 30, 40, 50};
for (int i = 0; i < ages.length; i++) {
System.out.println(ages[i]);
}
遍历数组的原因:求和,最大值,最小值,元素搜索
5、动态初始化数组(不知道里面内容)
定义:定义数组时先不存入具体的元素值,只确定数组存储的数据类型和数组的长度。
数据类型[] 数组名 = new 数据类型[长度];
int[] arr = new int[3];
// 后赋值
arr[0] = 10;
动态初始化数组元素默认值规则:
6、数组执行原理
7、数组使用注意事项和细节
- 数组是多个相同类型数据的组合,实现对这些数据的统一管理
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用。
- 数组创建后,如果没有赋值,有默认值int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
- 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值 3 使用数组
- 数组的下标是从 0 开始的。
- 数组下标必须在指定范围内使用,否则报:下标越界异常。
- 数组属引用类型,数组型数据是对象(object)
8、数组赋值机制
- 基本数据类型赋值,这个值就是具体的数据,而且相互不影响。
- 数组在默认情况下是引用传递,赋的值是地址。
9、数组拷贝
(数据空间独立)
int[ ] arr1 = {10,20,30};
int[ ] arr2 = new int[arr1.length];
for(int i = 0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
10、数组反转
//方法1:(首尾互换)
int[ ] arr = {11, 22, 33, 44, 55, 66};
int temp = 0;
int len = arr.length;
for( int i = 0; i < len / 2; i++) {
temp = arr[len - 1 - i];
arr[len - 1 - i] = arr[i];
arr[i] = temp;
}
//方法2:(逆序赋值)
int[ ] arr = {11, 22, 33, 44, 55, 66};
int[ ] arr2 = new int[arr.length];
for(int i = arr.length - 1, j = 0; i >= 0; i--, j++) {
arr2[j] = arr[i];
}
11、数组添加/扩容
int[] arr = {1, 2, 3};
Scanner scanner = new Scanner(System.in);
do {
int[] arrNew = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++) {
arrNew[i] = arr[i];
}
int addNum = scanner.nextInt();
arrNew[arrNew.length - 1] = addNum;
for (int i = 0; i < arrNew.length; i++) {
System.out.println(arrNew[i]);
}
arr = new int[arrNew.length];
for (int i = 0; i < arrNew.length; i++) {
arr[i] = arrNew[i];
}
}while (true);
12、排序
12.1、内部排序:
指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法);
12.2、外部排序法:
数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。
12.3、冒泡排序法
int[ ] arr = new int[10];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9 - i; j++) {
if (arr[j] > arr[j + 1]) {
int b = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = b;
}
}
}
12.4、选择排序
int[] arr = {5, 2, 3, 1};
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int a = arr[i];
arr[i] = arr[j];
arr[j] = a;
}
}
}
13、查找
13.1、顺序查找
int index = -1;
for(int i = 0; i < names.length; i++) {
//比较 字符串比较 equals, 如果要找到名字就是当前元素
if(findName.equals(names[i])) {
System.out.println("恭喜你找到 " + findName);
System.out.println("下标为= " + i);
index = i;
break;
}
}
if(index == -1) {
System.out.println("sorry ,没有找到 " + findName);
}
13.2、二分查找
优点是比较次数少,查找速度快,平均性能好;
其缺点是要求待查表为有序表,且插入删除困难。
因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
// @param arr 有序数组
// @param key 待查找关键字
// @return 找到的位置
public static int two(int[] arr,int key,int low,int high){
if(key < arr[low] || key > arr[high] || low > high){
return -1;
}
int middle = (low + high) / 2;//初始中间位置
if(arr[middle] > key){
//比关键字大则关键字在左区域
return two(arr, key, low, middle - 1);
}else if(arr[middle] < key){
//比关键字小则关键字在右区域
return two(arr, key, middle + 1, high);
}else {
return middle;
}
}
14、多维数组-二维数组
14.1、动态初始化
语法: 类型[ ][ ] 数组名=new 类型[大小][大小]
//遍历 arr 数组
for(int i = 0; i < arr.length; i++) {
for(int j = 0; j < arr[i].length; j++) {//对每个一维数组遍历
System.out.print(arr[i][j] +" ");
}
System.out.println();//换行
}
14.2、动态初始化-列数不确定
int[ ][ ] arr = new int[3][];
for(int i = 0; i < arr.length; i++) {
//遍历 arr 每个一维数组,给每个一维数组开空间 new
//如果没有给一维数组 new ,那么 arr[i]就是 null
arr[i] = new int[i + 1];
//遍历一维数组,并给一维数组的每个元素赋值
for(int j = 0; j < arr[i].length; j++) {
arr[i][j] = i + 1;//赋值
System.out.print(arr[i][j] +" ");
}
System.out.println();//换行
}
14.3、静态初始化
定义类型[ ][ ] 数组名 = {{值 1,值 2..},{值 1,值 2..},{值 1,值 2..}}
int arr[ ][ ]= {{4,6},{1,4,5,7},{-2}};
int sum = 0;
for(int i = 0; i < arr.length; i++) {
//遍历每个一维数组
for(int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] +" ");
}
System.out.println();
}
14.4、二维数组使用细节和注意事项
- 一维数组的声明方式有:
int[ ] x 或者 int x[ ] - 二维数组的声明方式有:
int[ ][ ] y 或者 int[ ] y[ ] 或者 int y[ ][ ] - 二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同。
比如:
map[ ][ ] 是一个二维数组
int map [ ][ ] = {{1,2},{3,4,5}}
由 map[0] 是一个含有两个元素的一维数组 ,map[1] 是一个含有三个元素的一维数组构成,我们也称为列数不等的二维数组
第六章、面向对象编程
1、类与对象
1.1、基本介绍
类与对象的概念
类:是一个数据类型。
对象:是一个具体实例。
1.2、类与对象的关系
把类的特性提取处理→某类:属性、行为→对象:具体某个事物
把人的特性提取出来→人类:属性:名字、工作、性别…、行为:吃饭、睡觉…→对象:秦始皇
从类到对象有几种说法:
- 创建一个对象。
- 实例化一个对象。
- 把类实例化。
…
1.3、类与对象的区别于联系
- 类是抽象的,概念的,代表一类事物,比如人类,猫类…, 即它是数据类型。
- 对象是具体的,实际的,代表一个具体事物, 即 是实例。
- 类是对象的模板,对象是类的一个个体,对应一个实例。
1.4、 属性/成员变量/字段
介绍:
- 从概念或叫法上看: 成员变量 = 属性 = field(字段)
- 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。比如我们前面定义猫类 的 int age 就是属性。
注意事项和细节说明:
- 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
- 属性的定义类型可以为任意类型,包含基本类型或引用类型
- 属性如果不赋值,有默认值,规则和数组一致。具体说: int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
1.5、如何创建对象
//先声明再创建
Cat cat ; //声明对象 cat
cat = new Cat(); //创建
//直接创建
Cat cat = new Cat();
1.6、如何访问属性
基本语法
对象名.属性名;
1.7、对象在内存中的存在形式
Java 内存的结构分析:
- 栈: 一般存放基本数据类型(局部变量)
- 堆: 存放对象(Cat cat , 数组等)
- 方法区:常量池(常量,比如字符串), 类加载信息
2、方法
2.1、什么是方法
- 成员方法简称方法
- 方法就是一些代码打包起来,然后在需要的地方可以重复使用。
- 方法是程序中最小的执行单元。
- 方法只有在被调用时才会执行。
2.2、方法的好处
- 提高代码的复用性(抽取重复代码,重复使用。
- 提高代码的可维护性(方便修改)。
2.3、方法的定义和调用
//方法定义:把一些代码打包在一起,该过程称为方法定义。
访问修饰符 static 返回值类型 方法名 ( ) {
方法体;// 就是打包起来的代码
}
//方法调用:法定义后并不是直接运行的,需要手动使用才能执行,该过程称为方法调用。
方法名 ( ) ;
2.4、方法的调用机制原理
2.5、带参数方法的定义和调用
定义:
访问修饰符 static void 方法名 ( 参数1,参数2,…… )
调用:
对象名.方法名 ( 参数1,参数2,…… );
形参:全称形式参数,是指方法定义中的参数
实参:全称实际参数,方法调用中的参数
2.6、带返回值方法的定义和调用
定义:
访问修饰符 static 返回值类型 方法名 (数据类型 参数) {
方法体;
return 返回值;
}
调用:
赋值调用:数据类型 变量名 = 方法名 (实参);
直接调用:方法名 (实参);
输出调用:System.out.println(方法名 (实参));
注意:
- return部分返回数据的类型必须和方法定义时声明的返回值类型一致。
- 方法不调用就不执行
- 方法调用时,实参必须跟形参匹配;返回值必须跟return结果匹配
- 方法与方法之间是平级关系,不能嵌套定义
- 方法的执行顺序和编写顺序无关,但是和调用顺序有关
- 方法的返回值类型为void,表示该方法没有返回值,可以省略return不写
- 在void方法中可以单独书写return,表示结束方法,return后面不能跟具体内容
- return语句下面,不能编写代码,因为永远执行不到,属于无效的代码
2.7、方法在计算机中的执行原理
栈:先进后出
2.8、Java的参数传递机制都是:值传递
所谓值传递:指的是在传输实参给方法的形参的时候,传输的是实参变量中存储的值的副本。
基本类型的参数传递:传的是变量中保存的值
2.9、引用类型的参数传递:传的是变量中保存的地址
3、方法重载
3.1、法重载的定义
在同一个类中,出现了名称相同,但是形参列表不同的多个方法,那么这些方法就构成了重载关系。
public static String a(String b, String c, String d){
String q = "1";
System.out.println(b+c+d);
return q;
}
public static String a(int b, int c, int d ){
String q = "1";
System.out.println(b+c+d);
return q;
}
3.2、方法重载的注意事项
识别方法重载,只看方法名(相同)和参数列表(不同),跟返回值无关形参列表不同,主要包括三个维度:类型、数量、顺序。
调用方法的时候,Java虚拟机会通过参数的不同来区分同名的方法。
4、作用域
4.1、作用域的基本使用
- Java中主要的变量就是属性(成员变量)和局部变量。
- 局部变量:在成员方法中定义的变量。也就是除去属性之外的其他变量,作用域为定义它的代码块中。
- 全部变量:也就是属性,作用域整个类体。
- 全局变量可以不赋值 直接使用,因为有默认值;局部变量必须先赋值在使用,因为没有默认值。
4.2、注意事项
- 属性和局部变量可以重名,访问时遵循就近原则。
- 在一个作用域中,两个局部变量不能重名。
- 属性的生命周期很长,随着对象的创建而创建,随着对象的销毁而销毁
- 局部变量的生命周期很短,随着代码块的创建而创建,随着代码块的销毁而销毁。
- 全局变量可以被本类和其他类使用。(通过对象调用)
- 局部变量只能在本类的对应方法中被使用。
- 全局变量可以加修饰符,局部变量不可以加修饰符。
5、构造方法/构造器
5.1、基本语法
//无参
[修饰符] 方法名(){
}
//含参
[修饰符] 方法名(形参列表){
方法体;
}
说明:
- 构造器的修饰符可以默认, 也可以是 public protected private。
- 构造器没有返回值。
- 方法名 和类名字必须一样。
- 参数列表 和 成员方法一样的规则。
- 构造器的调用, 由系统完成。
- 快捷键Alt+insert。
5.2基本介绍
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。
5.3、注意事项
- 一个类可以定义多个不同的构造器,即构造重载。
- 构造器名和类名必须相同。
- 构造器没有返回值。
- 构造器是完成对象的初始化,并不是创建对象。
- 在创建对象时,系统自动调用该类的构造方法。
- 如果程序员没有定义构造器,系统会自动给一个无参构造器。
- 一旦自定义了构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器了,除非自定义。
6、this关键字
哪个对象调用,this就是哪个对象。
6.1、注意事项
- this 关键字可以用来访问本类的属性、方法、构造器。
- this 用于区分当前类的属性和局部变量。
- 访问成员方法的语法:this.方法名(参数列表)。
7、get,set方法
- 快捷键:Alti+insert
- 在Javabean中使用的访问修饰符是private,想要在别的类中调用这些属性就需要用到get和set。
- get是调用该属性的属性值。
- set是修改该属性的属性值。
8、访问修饰符
8.1、基本介绍
- 公开级别:用 public 修饰,对外公开。(同类,同包,子类,不同包)
- 受保护级别:用 protected 修饰,对子类和同一个包中的类公开。(同类,同包,子类)
- 默认级别:没有修饰符号,向同一个包的类公开。(同类,同包)
- 私有级别:用 private 修饰,只有类本身可以访问,不对外公开。(同类)
8.2、注意事项
- 访问修饰符可以用来修饰类中的属性,成员方法和类。
- 只有public和默认能修饰类。
- 子类只能继承父类非私有的属性和方法。
- 成员方法的访问规则和属性完全一样。
9、封装
9.1、介绍
封装就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他方法只能通过被授权的操作[方法]才能对数据进行操作。
9.2、封装的好处
- 隐藏实现细节。
- 可以对数据进行验证。
9.3、实现步骤
- 将属性私有化(private)
- 写入无参方法和全参方法
- 提供一个公用的get,set方法
9.4、注意
加入全参构造器后,要想在set方法中定义的验证生效需要将this.name = name;改为setName(name);
10、static(静态)
10.1、介绍
static叫静态,可以修饰成员方法和成员变量。
10.2、成员变量
成员变量按照有无static修饰,分为两种:
- 类变量,属于类,与类一起加载一次,在内存中只有一份,会被类的所有对象共享。
- 实例变量,属于对象,每个对象中都有一份。
类变量访问:
- 类名.类变量(推荐)
- 对象名.类变量(不推荐)
实例变量访问:
对象.实例变量
10.3、成员方法
成员方法按照有无static修饰,分为两种:
- 类方法,有static修饰的成员方法,属于类,可以直接用类名访问,也可以用对象访问。
- 成员方法,无static修饰的成员方法,属于对象,只能用对象访问。
类方法访问:
- 类名.类方法(推荐)
- 对象名.类方法(不推荐)
实例方法访问:
对象.实例方法
10.4、工具类
含义:工具类就是一些完整功能、经常使用的方法所在的类
工具类要用类方法的原因:
类方法可以不创建对象,直接调用,使用方便,节省内存;实例方法需要创建对象来调用,会浪费内存
注意:工具类不需要创建对象, 建议将工具类的构造器私有化。
10.5、注意事项
- 类方法中可以直接访问类的成员,不可以直接访问实例成员。
- 实例方法中既可以直接访问类成员,也可以直接访问实例成员。
- 实例方法中可以出现this关键字,类方法中不可以出现this关键字的。
10.6、代码块
含义:代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)。
代码块分为两种:
静态代码块:
格式:static { }
特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。
作用:完成类的初始化,例如:对类变量的初始化赋值。
实例代码块:
格式:{ }
特点:每次创建对象时,执行实例代码块,并在构造器前执行。
作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值。
10.7、总结
11、继承
11.1、介绍
含义:Java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立起父子关系。
特点:子类能继承并使用父类的非私有成员(成员变量、成员方法)。
继承后对象的创建:子类的对象是由子类、父类共同完成的。
好处:减少了重复代码的编写,提高了代码的复用性。
11.2、注意事项
11.2.1、权限修饰符
用来限制类中的成员(成员变量、成员方法、构造器、代码块…)能够被访问的范围。
11.2.2、单继承
Java是单继承的,Java中的类不支持多继承,但是支持多层继承。
11.2.3、方法重写
含义:当子类觉得从父类中继承到的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖掉继承到的这个方法,这就是方法重写。
注意:重写后,方法的访问会遵循就近原则 。
注意事项:
- 在重写的方法上标注Override注解,可以指定java编译器,检查我们方法重写的格式是否正确
- 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限( public > protected > 缺省 )
- 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
- 私有方法、静态方法不能被重写,如果重写会报错的。
11.2.4、子类中访问其他成员的特点
- 在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。
- 先子类局部范围找。
- 然后子类成员范围找。
- 然后父类成员范围找,如果父类范围还没有找到则报错。
- 如果子父类中,出现了重名的成员,会优先使用子类的,如果想要使用父类可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
11.2.5、子类构造器的特点
子类的全部构造器,都会先调用父类的构造器,再执行自己。
子类构造器是如何实现调用父类构造器的:(应用场景:给父类构造器初始化)
- 默认情况下,子类全部构造器的第一行代码都是 super() (写不写都有) ,它会调用父类的无参数构造器。
- 如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(….),指定去调用父类的有参数构造器。
class Q extends C{
public Q(String id, String name, double unit, String press) {
super(id, name, unit, press);
}
}
补:任意类的构造器中,是可以通过this(…) 去调用该类的其他构造器的。
注意:this(…) 、super(…) 都只能放在构造器的第一行,因此,有了this(…)就不能写super(…)了,反之亦然。
12.3、阻止继承
正常情况下,只要某个class没有 fina1 修饰符,那么任何类都可以从该class继承。
从Java 15开始,允许使用 sealed 修饰class,并通过 permits 明确写出能够从该class继承的子类名称。
public sealed class Shape permits Rect, Circle, Triangle {
}
12、多态
12.1、介绍
含义:多态是在继承或者实现情况下的一种现象,表现为:对象多态、行为多态。
代码体现:
People p1 = new Student();
p1.run();
People p2 = new Teacher();
p2.run();
前提:有继承/实现关系;存在父类引用指向子类对象;存在方法重写。
注意事项:多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。
12.2、多态的应用
- 多态数组:数组的定义类型为父类型,里面实际保存的使用类型为子类型。
- 多态参数:方法定义的形参类为父类类型,实参类型允许为子类类型。
12.3、好处与弊端
好处:
- 在多态形式下,等号左右两边松耦合,更便于修改和维护
- 定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利
弊端:
多态下不能使用子类的独有功能。
类型转换:
自动类型转换:父类 变量名 = new 子类();
强制类型转换:子类 变量名 = (子类) 父类变量
注意事项:
- 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。
- 运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException)的错误出来。
- 如果想避免风险,可以所在使用前使用instenceof判断数据类型
13、final
含义:
final 关键字是最终的意思,可以修饰(类、方法、变量)
修饰类:该类被称为最终类, 类不能再被继承
修饰方法:该方法被称为最终方法, 方法不能被重写
修饰变量:该变量只能被赋值一次, 赋值完毕之后不能再修改
成员变量: 声明时或者在构造方法结束之前完成赋值
局部变量: 在使用之前完成赋值
注意:
final修饰基本类型的变量,变量存储的数据不能被改变。
final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。
14、抽象类
14.1、介绍
含义:在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法。
abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。
代码:
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名称(形参列表);
}
特点:
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
- 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
- 抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
- 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
14.2、使用条件
多个类中只要有重复代码(包括相同的方法签名),我们都应该抽取到父类中去,此时,父类中就有可能存在只有方法签名的方法,这时,父类必定是一个抽象类了,我们抽出这样的抽象类,就是为了更好的支持多态。
14.3、模板方法设计模式
介绍:一个功能的完成需要经过一系列步骤,这些步骤是固定的,但是中间某些步骤具体行为是待定的,在不同的场景中行为不同。
步骤:
- 定义一个抽象类(父类),提供模板方法
- 模板方法中,需要让子类自己实现的地方,定义为抽象方法
- 子类只需要继承该抽象类,重写抽象方法即可完成些完成的功能
多学一招:建议使用final关健字修饰模板方法
模板方法是给对象直接使用的,不能被子类重写。
一旦子类重写了模板方法,模板方法就失效了。
15、接口
15.1、介绍
介绍:Java提供了一个关键字interface,用这个关键字我们可以定义出一个特殊的结构:接口。
代码:
public interface 接口名 {
成员变量(接口中的成员变量都是常量, 默认是被public static final修饰的)
成员方法(接口中的成员方法都是抽象方法, 默认是被public abstract修饰的)
注意: 接口中不能有构造方法和代码块
}
注意事项:
- 接口不能直接创建对象
- 接口是用来被类实现(implements)的,实现接口的类称为实现类。
- 一个类可以实现多个接口,类实现多个接口,必须重写完全部接口的全部抽象方法,
否则实现类需要定义成抽象类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {
}
15.2、好处
让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。(解耦合)
15.3、新增
public interface A{
/**
* 1、默认方法(jdk8开始支持):对接口中的方法提供默认实现
* 使用default修饰,有方法体,可以但是不强制要求实现类重写, 只能通过实现类的对象调用 */
default void test1(){
...
}
/**
* 2、静态方法(jdk8开始支持):方便调用
* 使用static修饰,有方法体,只能通过接口名调用 */
static void test2(){
...
}
/**
* 3、私有方法(jdk9开始支持):提高代码复用性
* 使用private修饰,服务于接口内部,用于抽取相同的功能代码 */
private void test3(){
...
}
15.4、接口的多继承
一个接口是可以继承多个接口的,因此我们说接口是支持多继承的,这样做的目的是方便类去实现。
注意事项:
- 一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
- 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
- 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
- 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
15.5、接口和继承的区分
接口和继承解决的问题不同
接口的价值主要在于:设计好各种规范(方法),让其它类去实现这些方法。
继承的价值主要在于:解决代码的复用性和可维护性。
接口在一定程度上实现代码的解耦合。
16、常量
使用了 static final 修饰的成员变量就被称为常量;
作用:通常用于记录系统的配置信息。
public class Constant {
public static final String SCHOOL_NAME = "下课";
}
注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。
使用常量记录系统配置信息的优势、执行原理:
- 代码可读性更好,可维护性也更好。
- 程序编译后,出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
17、枚举
含义:枚举是一种特殊类,常用于简洁的标识一些固定的值
格式:
修饰符 enum 枚举类名{
枚举项1 , 枚举项2, ... ;
其他成员…
}
注意:
- 枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开。
- 这些名称,本质是常量,每个常量都会记住枚举类的一个对象。
特点:
public enum Sex{
MAN, WOMEN;
}
- 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
- 枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
- 枚举都是最终类,不可以被继承。
- 枚举类中,从第二行开始,可以定义类的其他各种成员。
- 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法。
18、内部类
18.1、介绍
内部类是类中的五大成分之一(成员变量、方法、构造器、代码块、内部类)
如果一个类定义在另一个类的内部,这个类就是内部类。
场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个
事物设计成内部类。
public class Car{
// 内部类
public class Engine{
}
}
18.2、成员内部类
就是定义在一个类中成员位置的类,在内部类中也可以定义成员属性和方法
代码:
public class Outer {
// 成员内部类
public class Inner {
// 成员属性和方法
}
}
创建对象的格式:
外部类名.内部类名 对象名 = new 外部类(…).new 内部类(…);
Outer.Inner in = new Outer().new Inner();
成员内部类中访问其他成员的特点:
- 成员内部类中可以定义实例成员,静态成员 (注意: 静态成员从JDK16开始支持)
- 成员内部类中的实例方法中,可以直接访问外部类的实例成员,静态成员
- 如果内部类和外部类出现了重名的成员,可以通过(外部类名.this.xxx) 强行访问外部类的成员
18.3、静态内部类
使用static修饰的成员内部类
代码:
public class Outer{
// 静态内部类
public static class Inner{
}
}
创建对象的格式:
外部类名.内部类名 对象名 = new 外部类.内部类(…);
Outer.Inner in = new Outer.Inner();
静态内部类中访问外部类成员的特点:
可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员。
18.4、局部内部类
局部内部类是定义在方法中、代码块中、构造器等执行体中
代码:
public class Test {
public static void main(String[] args) {
}
public static void go(){
class A{
}
abstract class B{
}
interface C{
}
}
}
18.5、匿名内部类
这是一种特殊的局部内部类
所谓匿名:指的是程序员不需要为这个类声明名字
代码:
new 类或接口(参数值…) {
方法实现(){}
};
作用:更方便的创建一个子类对象(简化操作类、接口的代码)
本质:匿名内部类本质就是一个子类,并会立即创建出一个子类对象
场景: 通常作为一个参数传输给方法
第七章、泛型
1、介绍
定义类、接口、方法时,同时声明的类型变量(如:<E>
) ,称为泛型。
public class ArrayList<E>{
}
好处:
- 编译时,检查添加元素的类型,提高了安全性。
- 减少了类型转换的次数,提高效率。
- 不再提示编译警告。
作用:
- 泛型又称参数化类型,是Jdk5.0 出现的新特性,解决数据类型的安全性问题。
- 在类声明或实例化时只要指定好需要的具体的类型即可。
- Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。
泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
本质:把具体的数据类型作为参数传给类型变量。
分类:泛型类、泛型接口、泛型方法
2、泛型类
修饰符 class 类名<类型变量,类型变量,…> {
}
注意:类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
3、泛型接口
interface 接口<类型变量,类型变量,…> {
}
注意:类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
4、泛型方法
修饰符 <类型变量,类型变量,…> 返回值类型 方法名(形参列表) {
}
5、通配符
就是 <?> ,可以在“使用泛型”的时候代表一切类型; E T K V 是在定义泛型的时候使用。
泛型的上下限:
泛型上限: <? extends A > ? 能接收的必须是A或者其子类 。
泛型下限: <? super A > ? 能接收的必须是A或者其父类。
6、泛型的擦除问题和注意事项
泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
7、自定义泛型类
修饰符 接口<T,R,…> {
}
注意细节:
- 普通成员可以使用泛型(属性、方法)。
- 使用泛型的数组,不能初始化。
- 静态方法中不能使用类的泛型。
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)。
- 如果在创建对象时,没有指定类型,默认为Object。
8、自定义泛型接口
interface 接口<T,R,…> {
}
注意细节
- 接口中,静态成员也不能使用泛型(这个和泛型类规定一样)。
- 泛型接口的类型,,在继承接口或者实现接口时确定。
- 没有指定类型,默认为Object。
9、自定义泛型方法
修饰符 <T,R,…>返回类型 方法名(参数列表){
}
注意细节
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中。
- 当泛型方法被调用时,类型会确定。
- public void eat(E e){},修饰符后没有<T,R…>eat方法不是泛型方法,而是使用了泛型。
第八章、常用的API
1、包
1.1、包的概念与创建方法
包是用来分门别类的管理各种不同程序的,类似于文件夹,建包有利于程序的管理和维护。
建包的语法格式:
package com.itheima.javabean;
public class 类名 {
}
1.2、注意事项
- 要调用自己所在包下的其他类,可以直接调用。(同一个包下的类,可以直接使用)
- 要调用其他包下的类,则必须在当前类中导包, 才可以访问!导包格式:import 包名.类名;(idea自动导包)
- 要调用Java提供的类,也需要先导包才可以使用;但是Java.lang包下的类是不需要我们导包的,可以直接使用。
- 要调用多个不同包下的类,而这些类名正好一样,此时默认只能导入一个类,另一个类必须带包名访问。
2、Arrays
介绍:用来操作数组的一个工具类。
Arrays类提供的的常见方法:
排序方法:
- 自然排序:让该对象的类实现Comparable(比较规则)接口,重写compareTo方法,制定比较规则
- 比较器排序:使用下面这个sort方法,创建Comparator比较器接口的匿名内部类对象,制定比较规则
自定义排序规则时,需要遵循的官方约定如下:
左边对象大于右边对象,返回正整数; 左边对象小于右边对象,返回负整数; 两边对象相等,返回0 这样就可以得到升序
3、equals 方法(Object)
public boolean equals(Object o)
= =和equals的对比:
- ==是一个比较运算符,既可以判断基本类型,也可以判断引用类型。
- 基本类型判断的是值是否相等,引用类型判断的是地址是否相等。
- equal:是Object类中的方法,只能判断引用类型。
- 默认判断的是地址是否相等,子类往往改写方法,用于判断内容是否相等(integer,String)
4、hashCode 方法
public int hashCode()
返回该对象的哈希码值。支持此方法是为了提高哈希表性能。
hashCode的常规协定是:
- 在Java应用程序执行期间,在对同一个对象多次调用hashCode方法时,必须一致的返回相同的整数,前提是将对象进行equals比较时说用的信息没有被修改。从某一应用程序的依次执行到同一应用程序的另一次执行,该整数无需保持一致。
- 如果根据equals(Object)方法,两个对象是相等的,那么这两个对象每个对象调用hashCode方法都必须生成相同的整数结果。
- 如果根据equals(Java.lang.Object)方法,两个对象不相等,那么对这两个对象的任意一个对象上调用hashCode方法不要求一定要生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数的结果可以提高哈希表的性能。
实际上,由Object类定义的hashCode方法确实会针对不同的对象返回并于同的整数
小结:
- 提高具有哈希结构的容器的效率!
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的!,不能完全将哈希值等价于地址。
5、toString方法(Object)
基本介绍:
public String toString()
默认返回:全类名+@+哈希值的十六进制。
子类往往重写toString方法,用于返回对象的属性信息。
重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式。
当直接输出一个对象时,toString方法会被默认的调用。
6、finalize方法
- 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作。
- 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来。销毁该对象,在销毁该对象前,会先调用finalize方法。
- 垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制。
7、clone克隆(Object)
介绍:
protected Object clone()
浅克隆: 将基本类型数值、引用类型的地址都拷贝一份
- 子类必须实现cloneable接口(标记接口),否则运行报CloneNotSupportedException
- 子类重写clone方法, 在里面直接调用父类提供的clone方法
class Student implements Cloneable{
int id;
String username;
String password;
double[] scores;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
深克降: 将基本类型数值、字符串的地址都拷贝一份; 其他引用类型的数据,会创建新对象完成拷贝(拷贝出新的地址)
- 子类必须实现cloneable接口(标记接口),否则运行报CloneNotSupportedException
- 子类重写clone方法, 在里面直接调用父类提供的clone方法
- 在clone方法中, 将克隆得到的对象中的引用类型重新手动clone一下再复制到对象中
class Student implements Cloneable{
int id;
String username;
String password;
double[] scores;
@Override
protected Object clone() throws CloneNotSupportedException {
Object clone = super.clone();
Student student = (Student) clone;
student.scores = this.scores.clone();
return student;
}
}
8、Objects
Objects类的常见方法
9、包装类
9.1、介绍
概述:为了更好的支持面向对象, java为每一种基本类型都提供了一种对应的包装类型
具体:
byte–>Byte short–>Short long–>Long float–>Float double–>Double boolean–>Boolean int–>Integer char–>Character
9.2、创建对象
(下面以Integer的角度学习, 其它都是类似的)
//构造方法(过时),接收int或string封装成Integer对象
Integer(int value/String value)
//替代构造方法,接收int或string封装成Integer对象
static Integer valueOf(int i/String value)
拆箱和装箱(基本类型和包装类的相互转换)
自动装箱: java支将基本类型直接值给对应包装类,底层使用的是valueOf()方法
自动拆箱: java支持将包装类直接赋值给对应基本类型,底层调intValue()方法
包装类跟字符串的互相转换:
static String toString(int i) 将Integer象封装的数值转为String类型
static int parseInt(String s) 将字符串数值转为int数值
9.3、Integer 类和 Character 类的常用方法:
System.out.println(Integer.MIN_VALUE); //返回最小值
System.out.println(Integer.MAX_VALUE);//返回最大值
System.out.println(Character.isDigit(‘a’));//判断是不是数字
System.out.println(Character.isLetter(‘a’));//判断是不是字母
System.out.println(Character.isUpperCase(‘a’));//判断是不是大写
System.out.println(Character.isLowerCase(‘a’));//判断是不是小写
System.out.println(Character.isWhitespace(‘a’));//判断是不是空格
System.out.println(Character.toUpperCase(‘a’));//转成大写
System.out.println(Character.toLowerCase(‘A’));//转成小写
10、Math
代表数学,是一个工具类,里面提供的都是对数据进行操作的一些静态方法。
11、System
System代表程序所在的系统,也是一个工具类。
这里的毫秒值指的是从1970年1月1日 00:00:00走到此刻的总的毫秒数(1s = 1000ms)。
12、Runtime
代表程序所在的运行环境。
13、BigDecimal
用于解决浮点型运算时,出现结果失真的问题。
第九章、日期、时间
1、Date
代表的是日期和时间。
2、SimpleDateFormat
代表简单日期格式化,可以用来把日期对象、时间毫秒值格式化成我们想要的形式。
y 年;M 月;d 日;H 时;m 分;s 秒;EEE 星期几;a 上午/下午
SimpleDateFormat解析字符串时间成为日期对象
3、Calendar
代表的是系统此刻时间对应的日历,通过它可以单独获取、修改时间中的年、月、日、时、分、秒等。
注意:calendar是可变对象,一旦修改后其对象本身表示的时间将产生变化。
4、JDK8新增的时间
LocalDate:年、月、日
LocalTime:时、分、秒
LocalDateTime:年、月、日、时、分、秒
ZoneId:时区
ZonedDateTime:带时区的时间
DateTimeFormatter:用于时间的格式化和解析
5、LocalDateTime、LocalTime、LocalDate
LocalDate:代表本地日期(年、月、日、星期)
LocalTime:代表本地时间(时、分、秒、纳秒)
LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
它们获取对象的方案
6、转换相关的API
LocalDateTime的转换成LocalDate、LocalTime
LocalDate的常用API(都是处理年、月、日、星期相关的)
LocalTime的常用API (都是处理时、分、秒、纳秒相关的)。
LocalDateTime的常用API(可以处理年、月、日、星期、时、分、秒、纳秒等信息)
7、ZoneId、ZonedDateTime
ZoneId:代表时区Id
中国标准时间:世界标准时间(UTC) + 8小时
ZoneId 时区的常见方法
ZonedDateTime 带时区时间的常见方法
8、DateTimeFormatter
LocalDateTime提供的格式化、解析时间的方法
第十章、String
1、含义与创建方法
String代表字符串对象,可以用来封装字符串数据,并提供了很多操作字符串的方法。
字符串的特性:
- String是一个final类,代表不可变的字符序列
- 字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的
String创建对象封装字符串数据的方式:
方式一: Java 程序中的所有字符串文字(例如“abc”)都为此类的对象。
- 直接使用双引号创建 String s = “ABC”;
- 使用构造函数创建 String s = new String(“ABC”)
方式二: 调用String类的构造器初始化字符串对象。
2、执行原理
通过“”定义字符串
通过new构造器得到字符串对象
3、使用方法
4、注意事项
- 只要是以“…”方式写出的字符串对象,会存储到字符串常量池,且相同内容的字符串只存储一份
- 通过new方式创建字符串对象,每new一次还会产生一个新的对象放在堆内存中
- String对象的内容不可改变,被称为不可变字符串对象。
5、StringBuffer与StringBuilder
StringBuilder代表可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,就是用来操作字符串的。
StringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁。
对于字符串相关的操作,如频繁的拼接、修改等,建议用StringBuidler,效率更高!
注意:如果操作字符串较少,或者不需要操作,以及定义字符串变量,还是建议用String。
StringBuffer与StringBuilder注意:
- StringBuffer的用法与StringBuilder是一模一样的
- 但StringBuilder是线程不安全的 StringBuffer是线程安全的
6、StringJoiner
JDK8开始才有的,跟StringBuilder一样,也是用来操作字符串的,也可以看成是一个容器,创建之后里面的内容是可变的。
好处:不仅能提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁
第十一章、异常
1、介绍
Error:代表的系统级别错误(属于严重问题),也就是说系统一旦出现问题,sun公司会把这些问题封装成Error对象给出来,说白了,Error是给sun公司自己用的,不是给我们程序员用的,因此我们开发人员不用管它。
Exception:叫异常,它代表的才是我们程序可能出现的问题,所以,我们程序员通常会用Exception以及它的孩子来封装程序出现的问题。
运行时异常:RuntimeException及其子类,编译阶段不会出现错误提醒,运行时出现的异常(如:数组索引越界异常)
编译时异常:编译阶段就会出现错误提醒的。(如:日期解析异常)
2、常见的运行时异常
- NullPointerException 空指针异常(当应用程序试图在需要对象的地方使用 null 时,抛出该异常)
- ArithmeticException 数学运算异常( 当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例)
- ArrayIndexOutOfBoundsException 数组下标越界异常(用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引)
- ClassCastException 类型转换异常(当试图将对象强制转换为不是实例的子类时,抛出该异常。)
- NumberFormatException 数字格式不正确异常(当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 => 使用异常我们可以确保输入是满足条件数字)
3、编译异常
介绍:编译异常通常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
常见的编译异常:
- SQLException //操作数据库时,查询表可能发生异常
- IOException //操作文件时,发生的异常
- FileNotFoundException //当操作一个不存在的文件时,发生异常
- ClassNotFoundException //加载类,而该类不存在时,异常
- EOFException // 操作文件,到文件末尾,发生异常
- IllegalArguementException //参数异常
4、异常处理
4.1、抛出异常
在Java的方法调用中,如果一个方法中出现了异常,本方法自己不处理,默认是会抛给调用方法去处理的
此时要注意的是,如果发生的是非运行时异常,需要在方法上明确使用throws关键字声明抛出
方法 throws 异常1 ,异常2 ,异常3 ..{
}
// 推荐方式
方法 throws Exception{
}
// Exception代表可以捕获一切异常
注意:
- 对于编译异常,程序中必须处理,比如 try-catch 或者 throws。
- 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理。
- 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型。
- 在throws 过程中,如果有方法 try-catch,就相当于处理异常,就可以不必throws。
4.2、捕获异常
直接在当前方法,发生异常的代码外使用try–catch–结构捕获并处理异常
异常处理后,后续的代码是可以继续执行的
try {
代码/可能有异常
}catch(Exception e){
//捕获到异常
//1.当异常发生时
//2.系统将异常封装成Exception 对象e,传递给catch
//3得到异常对象后,程序员,自己处
//4.注意,如果没有发生异常catch代码块不执行
}finally{
//1.不管try代码块是否有异常发生,始终要执行finally
//2.所以,通常将释放资源的代码,放在finally
}
try{
// 监视可能出现异常的代码!
}catch(异常类型1 变量){
// 处理异常
}catch(异常类型2 变量){
// 处理异常
}...
注意:
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块。
- 如果异常没有发生,则顺序执行try的代码块,不会进入到catch。
- 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),则使用如下代码-finally { }
- 可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前。
- 可以进行 try-finally 配合使用, 这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。
5、自定义异常
介绍:
Java无法为这个世界上全部的问题都提供异常类来代表, 如果以后我们自己写的代码中的某种问题。
想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类了。
种类:
自定义运行时异常:
- 定义一个异常类继承RuntimeException
- 重写构造器
- 通过throw new 异常类(xxx)来创建异常对象并抛出
- 编译阶段不报错,提醒不强烈,运行时才可能出现!!
自定义编译时异常
- 定义一个异常类继承Exception
- 重写构造器
- 通过throw new 异常类(xxx)来创建异常对象并抛出
- 编译阶段就报错,提醒更加强烈!!
6、异常的作用
- 异常是用来查询系统Bug关键参考信息。
- 异常可以作为方法内部的一种特殊返回值,以便通知上层调用者底层的执行情况。
7、throw 和 throws 的区别
第十二章、Lambda表达式
介绍: Lambda表达式是JDK 8开始新增的一种语法形式; 作用:用于简化匿名内部类的代码写法。
格式:
(被重写方法的形参列表) -> {
被重写方法的方法体代码。
}
new 类或接口(参数值…) {
方法实现(被重写方法的形参列表){
被重写方法的方法体代码
}
};
Lambda表达式只能简化函数式接口的匿名内部类!!!
函数式接口:
有且仅有一个抽象方法的接口。
注意:大部分函数式接口上面都会有一个@FunctionalInterface的注解,有该注解的接口就必定是函数式接口。
进一步简化:
- 参数类型可以省略不写
- 如果只有一个参数,小括号()也可以省略。
- 如果Lambda表达式中的方法体代码只有一行代码,可以省略花括号{}不写,同时要省略分号!
- 此时,如果这行代码是return语句,也必须去掉return不写。
第十三章、方法引用
1、静态方法的引用
语法 类名::静态方法
场景 如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静
态方法引用。
2、实例方法的引用
语法 对象名::实例方法
场景 如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用。
3、特定类型的方法引用
语法 类型::方法
场景 如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用。
4、构造器引用
语法 类名::new
场景 如果某个Lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用。
第十四章、正则表达式
就是由一些特定的字符组成,代表的是一个规则,一般用来对字符串进行格式校验、内容查找、和内容分隔替换
作用:
- 用来校验数据格式是否合法
- 在一段文本中查找满足要求的内容
- 将文本中的身份证号进行替换
匹配正则表达式的规则:
字符类(只匹配单个字符)
预定义字符(只匹配单个字符)
数量词
第十五章、注解
1、介绍
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
- 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等
2、基本的 Annotation 介绍
使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素
三个基本的 Annotation:
- @Override: 限定某个方法,是重写父类方法, 该注解只能用于方法
- @Deprecated: 用于表示某个程序元素(类, 方法等)已过时
- @SuppressWarnings: 抑制编译器警告
注:@interface的说明,@interface不是interface,是注释类
3、JDK 的元 Annotation(元注解)
3.1、元注解的基本介绍
JDK 的元 Annotation 用于修饰其他 Annotation
元注解: 本身作用不大,讲这个原因希望同学们,看源码时,可以知道他是干什么. 11.15.2 元注解的种类 (使用不多,了解, 不用深入研究)
- Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
- Target // 指定注解可以在哪些地方使用
- Documented //指定该注解是否会在 javadoc 体现
- Inherited //子类会继承父类注解
3.2、@Retention
说明:
只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留多长时间, @Rentention 包含一个 RetentionPolicy
类型的成员变量, 使用 @Rentention 时必须为该 value 成员变量指定值:
@Retention 的三种值
- RetentionPolicy.SOURCE: 编译器使用后,直接丢弃这种策略的注释
- RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认值
- RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注解
3.3、@Target
作用:声明被修饰的注解只能在哪些位置使用。
- TYPE,类,接口
- FIELD, 成员变量
- METHOD, 成员方法
- PARAMETER, 方法参数
- CONSTRUCTOR, 构造器
- LOCAL_VARIABLE, 局部变量
3.4、@Documented
基本说明:
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被javadoc 工具提取成文档,即在生成文档时,可以看到该注解。
说明: 定义为Documented的注解必须设置Retention值为RUNTIME.
3.5、@Inherited
被它修饰的 Annotation 将具有继承性,如果某个类使用了被 @Inherited 修饰的 Annotation,则其子类将自动具有该注解。
说明:实际应用中,使用较少,了解即可。
4、注解的解析
就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。
如何解析:
- 指导思想:要解析谁上面的注解,就应该先拿到谁。
- 比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解。
- 比如要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解。
- Class 、 Method 、 Field , Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。
5、注解的属性
在定义注解的时候,还可以通过属性来进一步描述注解的细节
public @interface 注解名称 {
public 属性类型 属性名() default 默认值 ;
}
特殊属性名: value
如果注解中只有一个value属性,使用注解时,value名称可以不写!!
第十六章、集合
1、概述
- 可以动态保存任意多个对象,使用比较方便。
- 提供了一系列方便的操作对象的方法:add、remove、set、get等。
- 使用集合添加,删除新元素的示意代码-简洁了。
Collection(单列集合)Collection代表单列集合,每个元素(数据)只包含一个值。
Map(双列集合)Map代表双列集合,每个元素包含两个值(键值对)。
2、Collection集合体系
List系列集合:有序、可重复
ArrayList、LinekdList 、vector
Set系列集合:无序、不重复
HashSet
LinkedHashSet: 存取有序
TreeSet:可排序
3、Collection的常用方法
特点:
- Collection实现子类可以存放多个元素,每个元素可以是Object。
- 有些Collection的实现类,可以存放重复的元素,有些不可以。
- 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)。
- Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的。
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
4、Collection的遍历方式
4.1、迭代器
基本介绍:
- Iterator对象称为迭代器,主要用于遍历 Collection 集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了lterator接口的对象,即可以返回一个选代器。
- lterator 仅用于遍历集合,lterator 本身并不存放对象。
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
获取迭代器方法:
迭代器常用方法:
//快捷键itit
Iterator<String> it = lists.iterator();
while(it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
4.2、增强for循环
增强for循环,可以代替iterator选代器。
特点:增强for就是简化版的iterator,本质一样。只能用于遍历集合或数组。
格式:
for (元素的数据类型 变量名 : 数组或者集合) {
}
Collection<String> c = new ArrayList<>();
...
for(String s : c) {
System.out.println(s);
}
增强for可以用来遍历集合或者数组。
增强for遍历集合,本质就是迭代器遍历集合的简化写法。
4.3、Lambda
Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。
方法:
Collection<String> lists = new ArrayList<>();
...
lists.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
lists.forEach(s -> {System.out.println(s);
});
5、Collection的并发修改异常
集合的并发修改异常:
- 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
- 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误。
怎么保证遍历集合同时删除数据时不出bug?
- 使用迭代器遍历集合:但用迭代器自己的删除方法删除数据即可。
- 使用增强for循环遍历集合:无法解决这个问题
- 使用普通for循环遍历集合:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。
6、List接口
6.1、List特有方法
List 接口是 Collection 接口的子接口 List .java4
- List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
常用方法:
List集合支持的遍历方式:
- 迭代器
- 增强for循环
- Lambda表达式
- for循环(因为List集合有索引)
6.2ArrayList
6.2.1、含义
ArrayList是集合,集合是一种容器,用来装数据,类似于数组。
数组长度固定,集合长度不固定。
6.2.2、注意事项
- permits all elements, including null ,ArrayList 可以加入null,并且多个。
- ArrayList 是由数组来实现数据存储的。
- ArrayList 基本等同于Vector,除了ArrayList是线程不安全(执行效率高) 在多线程情况下,不建议使用ArrayList。
6.2.3、方法
6.2.4、底层原理
是基于数组实现的。
查询速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
删除效率低:可能需要把后面很多的数据进行前移。
添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
底层原理:
- ArrayList中维护了一个Object类型的数组elementData.transient Object[l elementData。
//transient 表示瞬间,短暂的, 表示该属性不会被序列号。 - 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第2次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容则直接扩容elementData为1.5倍。
List<String> list1 = new ArrayList<>();
list1.add("a");
...
List<String> list2 = new ArrayList<>();
list2.add("a11");
...
list1.addAll(list2); // 把集合list2的数据全部倒入到list1集合
6.2.5、ArrayList集合适合的应用场景
- ArrayList适合:根据索引查询数据比如根据随机索引取数据(高效)!或者数据量不是很大时。
- ArrayList不适合:数据量大的同时又要频繁的进行增删操作。
6.3、Vertor
6.3.1、基本介绍
- Vector类的定义说明。
public class Vector<E>
extends AbstractList<E>
implements List<E>,RandomAccess, Cloneable, Serializable
- Vector底层也是一个对象数组,protected Object[lelementData。
- Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized。
public synchronized E get(int index){
if(index>= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
- 在开发中,需要线程同步安全时,考虑使用Vector。
6.3.2、Vector 和 ArrayList 的比较
6.4、LinkedList
6.4.1、介绍
- LinkedList底层实现了双向链表和双端队列特点。
- 可以添加任意元素(元素可以重复),包括null。
- 线程不安全,没有实现同步。
链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
链表的特点1:查询慢,无论查询哪个数据都要从头开始找。
链表的特点2:链表增删相对快。
特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
6.4.2、方法
6.4.3、底层原理
- LinkedList底层维护了一个双向链表。
- LinkedList中维护了两个属性first和last分别指向 首节点和尾节点。
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表。
- 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
- 模拟一个简单的双向链表。
class Node {
public Object item;
public Node next;
public Node pre;
public Node(Object name) {
this.item =name;
}
public String toString() {
return "Node name=" + item;
}
}
6.4.3、ArrayList 和 LinkedList 比较
如何选择ArrayList和LinkedList:
- 如果我们改查的操作多,选择ArrayList。
- 如果我们增删的操作多,选择LinkedList。
- 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList。
- 在一个项目中,根据业务灵活选择,也可能这样,一个块使用的是ArrayList,另外一个模块是LinkedList,也就是说,要根据业务来进行选择。
7、Set接口
7.1、介绍
Set系列集合特点:
- 无序(添加和取出的顺序不一致),没有索引。
- 不允许重复元素,所以最多包含一个null。
- JDK API中Set接口的实现类有:
HashSe、LinkedHashSet:存取有序、TreeSet:可排序
注意:
- Set要用到的常用方法,基本上就是Collection提供的。
- 自己几乎没有额外新增一些常用功能。
同Collection的遍历方式一样,因为Set接口是Colection接口的子接口:
- 可以使用迭代器。
- 增强for。
- 不能使用索引的方式来获取。
7.2、HashSet
7.2.1、介绍
- Hashset实现了Set接口。
- HashSet实际上是HashMap。
public Hashset(){
map = new HashMap<>();
}
- 可以存放null值,但是只能有一个null。
- Hashset不保证元素是有序的,取决于hash后,再确定索引的结果.(即,不保证存放元素的顺序和职出顺序一致)。
- 不能有重复元素/对象。
7.2.1、 Hash值
就是一个int类型的数值,Java中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
public int hashCode():返回对象的哈希码值。
特点:
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
- 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
- Object的hashCode方法根据"对象地址值"计算哈希值
子类重写后的hashCode方法可以根据"对象属性值"计算哈希值
HashSet集合判定两个对象的标准就是两个对象的hash值是否一致, 因此我们经常重写hashcode实现集合中对象去重
7.2.2、底层原理
基于哈希表实现。
哈希表是一种增删改查数据,性能都较好的数据结构。
哈希表
JDK8之前,哈希表 = 数组+链表
- 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
- 使用元素的哈希值对数组的长度求余计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入,如果不为null,表示有元素,则调用equals方法比较属性值,相等,则不存;不相等,则存入数组
- 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面
JDK 8开始之后,新元素直接挂在老元素下面
JDK8开始,哈希表 = 数组+链表+红黑树
JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树
7.2.3、树结构
二叉树中,任意节点的度<=2
存在的问题:
当数据已经是排好序的,导致查询的性能与单链表一样,查询速度变慢!
平衡二叉树:
在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
红黑树,就是可以自平衡的二叉树
红黑树是一种增删改查数据性能相对都较好的结构。
7.3、LinkedHashSet
底层原理:
- LinkedHashSet是不可重复的,存取有序的,底层是基于哈希表(数组、链表、红黑树)实现的。
- 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。
7.4TreeSet
特点:不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序)
底层是基于红黑树实现的排序。
注意:
- 对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如Teacher对象,TreeSet默认是无法直接排序的。
自定义排序规则:
TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。
方式一:自然排序
让自定义的类(如教师类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
方式二:比较器排序
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则)。
public TreeSet(Comparator<? super E> comparator)
8、Map接口
8.1、介绍
含义:
- Map集合称为双列集合,一次需要存一对数据做为一个元素, 格式:{key1=value1 , key2=value2 , key3=value3 , …} 。
- Map集合的每个元素分为两部分:key和value,key称为键,value称为值,整体叫键值对,因此Map也叫“键值对集合”。
- Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值。
特点:
- Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value。
- Map 中的 key 和 value 可以是任何引用类型的数据,会封装到HashMap$Node对象中。
- Map 中的 key 不允许重复,原因和Hashset 一样,前面分析过源码。
- Map 中的 value 可以重复。
- Map 的key 可以为 null, value 也可以为null ,注意 key 为null, 只能有一个value 为null,可以多个。
- 常用String类作为Map的 key。
- key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value。
- Map存放数据的key-value示意图,一对 k-v是放在一个HashMap$Node中的,有因为Node 实现了 Entry 接口,有些书上也说 一对k-v就是一个Entry。
8.2、集合体系:
集合体系的特点:
注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的。
- HashMap: 无序、不重复 (用的最多)。
- LinkedHashMap :有序、不重复。
- TreeMap: 按照大小默认升序排序、不重复。
8.3、常用方法
8.4、遍历方法
- containsKey:查找键是否存在。
- keySet:获取所有的键。
- entrySet:获取所有关系k-V。
- values:获取所有的值。
Map map = new HashMap();
//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keyset = map.keySet();
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}
Map map = new HashMap();
//第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
//(1) 增强 for
System.out.println("---取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("---取出所有的 value 迭代器----");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
Map map = new HashMap();
//第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
//(1) 增强 for
System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
Map map = new HashMap();
//第四组: 通过 forEach 来获取 k-v
map.forEach((k , v) -> {
System.out.println(k +"----->" + v);
});
8.5、HashMap
8.5.1、介绍
- Map接口的常用实现类:HashMap、Hashtable和Properties。
- HashMap是 Map 接口使用频率最高的实现类。
- HashMap 是以 key-val 对的方式来存储数据(HashMap$Node类型)。
- key 不能重复,但是值可以重复,允许使用null键和null值。
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改.(key不会替换,val会替换)。
- 与Hashset一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.(idk8的hashMap 底层 数组+链表+红黑树)。
- HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized。
8.5.2、底层机制
- (k,v)是一个Node 实现了Map.Entry<K,V>,查看 HashMap 的源码可以看到。
- jdk7.0的hashmap 底层实现[数组+链]。
- jdk8.0 底层[数组+ 链表+红黑树]。
- 哈希表是一种增删改查数据,性能都较好的数据结构。
扩容机制[和HashSet相同]:
- HashMap底层维护了Node类型的数组table,默认为null。
- 当创建对象时,将加载因子(loadfactor)初始化为0.75。
- 当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key和准备加入的key相是否等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。
- 第1次添加,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)。
- 以后再扩容,则需要扩容table容量为原来的2倍(32),临界值为原来的2倍,即24,依次类推。
- 在Java8中,如果一条链表的元素个数超过 TREEIFY THRESHOLD(默认是8),并且table的大小>= MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树)。
8.6、HashTable
8.6.1、介绍
- 存放的元素是键值对: 即 K-V
- hashtable的键和值都不能为null,否则会抛出NullPointerException
- hashTable 使用方法基本上和HashMap一样
- hashTable 是线程安全的(synchronized),hashMap 是线程不安全的
8.6.2、 Hashtable 和 HashMap 对比
8.7、Properties
8.7.1、介绍
- Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
- 他的使用特点和Hashtable类似。
- Properties 还可以用于 从 xxx.properties 文件中,加载数据到Properties类对象并进行读取和修改。
- 说明: 工作后 xxx.properties 文件通常作为配置文件
8.7.2、使用
- Properties 继承 Hashtable。
- 可以通过 k-v 存放数据,当然 key 和 value 不能为 null。
8.8、LinkedHashMap
底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。
实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。
8.9、TreeMap
特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
TreeMap集合同样也支持两种方式来指定排序规则:
- 让类实现Comparable接口,重写比较规则。
- TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。
9、总结
- 先判断存储的类型(一组对象[单列]或一组键值对[双列])
- 一组对象[单列]:Collection接口
A.允许重复:List
增删多:LinkedList【底层维护了一个双向链表】
改查多:ArrayList[底层维护 Object类型的可变数组]
B.不允许重复:Set
无序:HashSet[底层是HashMap ,维护了一个哈希表即(数组+链表+红黑树)]
排序:TreeSet插入和取出顺序一致:LinkedHashSet,维护数组+双向链表 - 一组键值对[双列]:Map
键无序:HashMap[底层是:哈希表jdk7:数组+链表,jdk8: 数组+链表+红黑树]
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件 Properties
10、Collections 工具类
10.1、介绍
- Collections 是一个操作 Set、List 和 Map 等集合的工具类。
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作。
//给集合批量添加元素
public static <T> boolean addAll(Collection<? super T> c, T... elements)
//可以存放多个元素,但是会固定长度。
List<Object> list = List.of()
10.2、排序
- reverse(List):反转 List 中元素的顺序。
- shuffle(List):对 List 集合元素进行随机排序。
- sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序。
//如果报错就需要在类中引入Comparable接口
class Student implements Comparable<Student> {
//然后重写compareTo方法
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
//或者直接在main方法里创建匿名内部类重写compare方法
Collections.sort(stuList, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
});
- sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序。
- swap(List,int, int):将指定 list 集合中的i处元素和j处元素进行交换。
10.3、查找,替换
- 0bject max(Colection):根据元素的自然顺序,返回给定集合中的最大元素。
- Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素。
- Object min(Collection)。
- Object min(Collection, Comparator)。
- int frequency(Colection,Object):返回指定集合中指定元素的出现次数。
- void copy(List dest,List src):将src中的内容复制到dest中。
- boolean replaceAll(List list, Object oldVal, Object newVal): 使用新值替换 List 对象的所有旧值。
11、集合嵌套
public static void main(String[] args) {
String a = "江苏省";
List<String> list1 = List.of("南京市", "扬州市", "苏州市", "无锡市", "常州市");
String b = "湖北省";
List<String> list2 = List.of("武汉市", "孝感市", "十堰市", "宜昌市", "鄂州市");
String c = "河北省";
List<String> list3 = List.of("石家庄市", "唐山市", "邢台市", "保定市", "张家口市");
HashMap<String, List<String>> map1 = new HashMap<>();
map1.put(a,list1);
map1.put(b,list2);
map1.put(c,list3);
System.out.println(map1);
}
12、可变参数
含义:就是一种特殊形参,定义在方法、构造器的形参列表里。
定义格式是:
方法名(数据类型... 形参名称){
}
可变参数的特点和好处:
特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
好处:常常用来灵活的接收数据。
可变参数的注意事项:
- 可变参数在方法内部就是一个数组。
- 一个形参列表中可变参数只能有一个。
- 可变参数必须放在形参列表的最后面。
13、Stream
13.1、含义
Stream也叫Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。
优势: Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。
13.2、步骤
13.3、获取Stream流
获取单列集合 的Stream流:
获取 数组 的Stream流:
获取 零散数据 的Stream流:
13.4、常见的中间方法
中间方法指的是对stream流进行操作的方法, 他们调用完成后会返回新的Stream流,可以继续使用(支持链式编程)
13.5、常见的中间方法
终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
它的作用是对流中的数据进行筛选(遍历、最大、最小、统计个数)
收集方法就是把Stream流操作后的结果转回到集合或者数组中去返回。
第十七章、递归
概念:
递归是一种算法,在程序设计语言中广泛应用。
从形式上说:方法调用自身的形式称为方法递归( recursion)。
递归的形式:
直接递归:方法自己调用自己。
间接递归:方法调用其他方法,其他方法又回调方法自己。
使用方法递归时需要注意的问题:
递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误。
第十八章、IO流
1、File
含义:
File对象既可以代表文件、也可以代表文件夹。它封装的对象仅仅是一个路径名,这个路径可以存在,也可以不存在
创建File类的对象:
路径写法:
绝对路径:从盘符开始。
File file = new File(“D:\\itheima\\a.txt”);
相对路径:不带盘符,默认直接到当前工程下的目录寻找文件。
File file = new File(“模块名\\a.txt”);
2、方法
File提供的判断文件类型、获取文件信息功能:
File类创建文件的功能:
File类删除文件的功能:
注意:delete方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站。
File类提供的遍历文件夹的功能:
3、注意事项
- 当主调是文件,或者路径不存在时,返回null。
- 当主调是空文件夹时,返回一个长度为0的数组。
- 当主调是一个非空文件夹,但是没有权限访问该文件夹时,返回null。
4、字符集
4.1、介绍
- 标准ASCII字符集:
ASCII(American Standard Code for Information Interchange): 美国信息交换标准代码,包括了英文、符号等。
标准ASCII使用1个字节存储一个字符,首尾是0,总共可表示128个字符,对美国佬来说完全够用。 - GBK(汉字内码扩展规范,国标):
汉字编码字符集,包含了2万多个汉字等字符,GBK中一个中文字符编码成两个字节的形式存储。
注意:GBK兼容了ASCII字符集。 - Unicode字符集(统一码,也叫万国码):
Unicode是国际组织制定的,可以容纳世界上所有文字、符号的字符集。
UTF-8:
是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。
英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节。
4.2、编码,解码
Java代码完成对字符的编码:
Java代码完成对字符的解码。
注意:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。
5、IO流
5.1、概述
5.2、分类
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流。
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流。
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流。
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。
5.3、FileInputStream(文件字节输入流)
作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中来。
注意: 使用FileInputStream每次读取一个字节,读取性能差,并且读取汉字输出会乱码。
-
每次读取一个字节:
-
每次读取多个字节:
-
一次读取完全部字节
方式一:自己定义一个字节数组与被读取的文件大小一样大,然后使用该字节数组,一次读完文件的全部字节。
方式二:Java官方为InputStream提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回。
如果文件过大,创建的字节数组也会过大,可能引起内存溢出。
5.4、FileOutputStream(文件字节输出流)
作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去。
5.5、FileReader(文件字符输入流)
作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中来。
5.6、FileWriter(文件字符输出流)
作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去。
5.7、注意事项
字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效。
字节流、字符流的使用场景小结:
字节流适合做一切文件数据的拷贝(音视频,文本);字节流不适合读取中文内容输出。
字符流适合做文本文件的操作(读,写)。
5.8、字节缓冲流
字节缓冲输入流自带了8KB缓冲池;字节缓冲输出流也自带了8KB缓冲池。
5.9、字符缓冲流
自带8K的字符缓冲池,可以提高字符输入流读取字符数据的性能。
BufferedReader(字符缓冲输入流)
BufferedWriter(字符缓冲输出流)
5.10、字符输入转换流
解决不同编码时,字符流读取文本内容乱码的问题。
思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了。
5.11、字符输出转换流
作用:可以控制写出去的字符使用什么字符集编码。
获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。
5.12、PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。
PrintStream和PrintWriter的区别:
打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
PrintWriter继承自字符输出流Writer,因此支持写字符数据出去。
5.13、DataOutputStream(数据输出流)
允许把数据和其类型一并写出去。
5.14、DataInputStream(数据输入流)
用于读取数据输出流写出去的数据。
5.15、ObjectOutputStream(对象字节输出流)
可以把Java对象进行序列化:把Java对象存入到文件中去。
5.16、ObjectInputStream(对象字节输入流)
可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来。
注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable)
序列化多个对象:
用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可
注意:ArrayList集合已经实现了序列化接口!
6、释放资源的方式
try-catch-finally
try {
...
...
} catch (IOException e) {
e.printStackTrace();
}finally{
}
finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)。
try-with-resource (JDK7开始提供)
//() 中只能放置资源,否则报错
try(定义资源1;定义资源2;…){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
该方式声明的资源使用完毕后,会自动调用其close()方法,完成释放!
资源一般指的是最终实现了AutoCloseable接口。
public abstract class InputStream implements Closeable{ }
public abstract class OutputStream implements Closeable, Flushable { }
public interface Closeable extends AutoCloseable { }
7 、IO框架
框架:
解决某类问题,编写的一套类、接口等,可以理解成一个半成品。
好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。
IO框架:
封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。
Commons-io
Commons-io是apache开源基金组织提供的一组有关IO操作的小框架,目的是提高IO流的开发效率。
第十九章、配置文件
1、介绍
在企业开发过程中,我们习惯把一些需要灵活配置的数据放在一些文本文件中,而不是在Java代码
写死。
我们把这种存放程序配置信息的文件,统称为配置文件。
配置文件一般要求有明确的格式,以方便读写操作。
普通文件.txt、属性文件.properties、XML文件.xml
2、Properties
是一个Map集合(键值对集合),但是我们一般不会当集合使用。
核心作用:Properties是用来代表属性文件的,通过Properties可以读写属性文件里的内容。
使用Properties读取属性文件里的键值对数据
使用Properties把键值对数据写出到属性文件里去
3、XML
3.1、认识XML
全称Extensible Markup Language, 可扩展标记语言。
本质是一种数据的格式,可以用来存储复杂的数据结构,和数据关系。
XML的特点:
XML中的“<标签名>” 称为一个标签或一个元素,一般是成对出现的。
XML中的标签名可以自己定义(可扩展),但必须要正确的嵌套。
XML中只能有一个根标签。
XML中的标签可以有属性。
如果一个文件中放置的是XML格式的数据,这个文件就是XML文件,后缀一般要写成.xml。
XML的语法规则:
XML文件的后缀名为:xml,文档声明必须是第一行
<?xml version="1.0" encoding="UTF-8" ?>
version:XML默认的版本号码、该属性是必须存在的
encoding:本XML文件的编码
XML中可以定义注释信息:<!–- 注释内容 -->,快捷键是Ctrl+shift+/
XML中书写”<”、“&”等,可能会出现冲突,导致报错,此时可以用如下特殊字符替代。
< < 小于
> > 大于
& & 和号
' ' 单引号
" " 引号
XML中可以写一个叫CDATA的数据区: <![CDATA[ …内容… ]]>,里面的内容可以随便写。
XML的作用和应用场景:
本质是一种数据格式,可以存储复杂的数据结构,和数据关系。
应用场景:经常用来做为系统的配置文件;或者作为一种特殊的数据结构,在网络中进行传输。
3.2、读取XML
解析XML文件。
使用程序读取XML文件中的数据。
注意:程序员并不需要自己写原始的IO流代码来解析XML,难度较大!也相当繁琐!
其实,有很多开源的,好用的,解析XML的框架,最知名的是:Dom4j(第三方研发的)
DOM4J解析XML文件的思想:文档对象模型
Dom4j解析XML-得到Document对象:
SAXReader:Dom4j提供的解析器,可以认为是代表整个Dom4j框架。
Document:
Element提供的方法:
3.3XML约束
约束文档:
专门用来限制xml书写格式的文档,比如:限制标签、属性应该怎么写。
约束文档的分类:
- DTD文档
- Schema文档
第二十章、日志技术
1、概述
程序中的日志,通常就是一个文件,里面记录的是程序运行过程中的各种信息。
目前记录日志的方案:
public static void test(String number){
try {
int result = Integer.parseInt(number);
System.out.println("输入的数字为" + result);
} catch (NumberFormatException e) {
System.out.println("输入的数字有误,请输入一个整数");
}
}
输出语句的弊端:
- 日志会展示在控制台。
- 不能更方便的将日志记录到其他的位置(文件,数据库)。
- 想取消日志,需要修改源代码才可以完成。
日志技术:
- 可以将系统执行的信息,方便的记录到指定的位置(控制台、文件中、数据库中)。
- 可以随时以开关的形式控制日志的启停,无需侵入到源代码中去进行修改。
日志技术的体系结构:
日志接口:设计日志框架的一套标准,日志框架需要实现这些接口。
日志框架:牛人或者第三方公司已经做好的实现代码,后来者直接可以拿去使用。
注意1:因为对Commons Logging接口不满意,有人就搞了SLF4J;因为对Log4j的性能不满意,有人就搞了Logback。
注意2:Logback是基于slf4j的日志规范实现的框架。
Logback日志框架有以下几个模块:
想使用Logback日志框架,至少需要在项目中整合如下三个模块:
- slf4j-api:日志接口
- logback-core
- logback-classic
2、Logback快速入门
需求:
使用Logback日志框架,纪录系统的运行信息。
实现步骤:
- :导入Logback框架到项目中去。
slf4j-api:日志接口、logback-core、logback-classic - :将Logback框架的核心配置文件logback.xml直接拷贝到src目录下(必须是src下)。
- :创建Logback框架提供的Logger对象,然后用Logger对象调用其提供的方法就可以记录系统的日志信息。
public static final Logger LOGGER = LoggerFactory.getLogger("当前类类名");
核心配置文件logback.xml:
对Logback日志框架进行控制的。
日志的输出位置、输出格式的设置
通常可以设置2个输出日志的位置:一个是控制台、一个是系统文件中
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
开启日志(ALL),取消日志(OFF)
<root level="ALL">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</root>
3、Logback设置日志级别
日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级依次升高):
只有日志的级别是大于或等于核心配置文件配置的日志级别,才会被记录,否则不记录。
第二十一章、线程
1、介绍
线程:简单的说,就是计算机在做一件事
单线程:在计算机中同一时间只能做一件事
多线程:在计算机中同一时间可以做多件事
其实多线程在我们的生活中的使用场景很多,比如网盘的上传下载,12306的多窗口售票等等
它的主要好处有:1. 减少队列阻塞带来的影响 2. 提高CPU的利用率
2、线程的创建方式
2.1、继承Thread类
多线程的创建方式一:继承Thread类
- 定义一个子类继承线程类java.lang.Thread,重写run()方法
- 创建子类的对象
- 调用子类对象的start()方法启动线程(底层会自动去执行run方法)
方式一优缺点:
优点:编码简单。
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。
注意事项:
- 启动线程必须是调用start方法,不是调用run方法。
- 不要把主线程任务放在启动子线程之前。
- 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。
- 只有调用start方法才是启动一个新的线程执行。
- 这样主线程一直是先跑完的,相当于是一个单线程的效果了。
2.2实现Runnable接口
多线程的创建方式二:实现Runnable接口
- 定义一个线程任务类实现Runnable接口,重写run()方法
- 创建任务类对象
- 把任务类对象交给Thread处理
- 调用线程对象的start()方法启动线程
方式二的优缺点:
优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
缺点:需要多一个Runnable对象。
线程创建方式二的匿名内部类写法:
- 可以创建Runnable的匿名内部类对象。
- 再交给Thread线程对象。
- 再调用线程对象的start()启动线程。
前两种线程创建方式都存在的一个问题:
假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。
如何解决:
JDK 5.0提供了Callable接口和FutureTask类来实现(多线程的第三种创建方式)。
这种方式最大的优点:可以返回线程执行完毕后的结果。
2.3、实现Callable接口
多线程的第三种创建方式:利用Callable接口、FutureTask类来实现。
- 创建任务对象
定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据。
把Callable类型的对象封装成FutureTask(线程任务对象)。 - 把线程任务对象交给Thread对象。
- 调用Thread对象的start方法启动线程。
- 线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。
FutureTask的API
线程创建方式三的优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。
缺点:编码复杂一点。
2.4、比较
2.5、Thread类的方法
方法说明:
Thread类还提供了诸如:yield、interrupt、守护线程、线程优先级等线程的控制方法,在开发中很少使用,这些方法会后续需要用到的时候再讲解。
3、线程安全问题
线程安全问题:
多线程给我们的程序带来了很大性能上的提升,但是也可能引发线程安全问题。
线程安全问题指的是当多个线程同时操作同一个共享资源的时候,可能会出现的操作结果不符预期问题。
4、线程同步方案
4.1、认识线程同步
线程同步:线程同步就是让多个线程实现先后依次访问共享资源,这样就解决了安全问题,它最常见的方案就是加锁。
加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。
4.2、方式一:同步代码块
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
synchronized(同步锁) {
访问共享资源的核心代码
}
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行。
同步锁的注意事项:
- 对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug
- 对于实例方法建议使用this作为锁对象
- 对于静态方法建议使用字节码(类名.class)对象作为锁对象
4.3、方式二:同步方法
作用:把访问共享资源的核心方法给上锁,以此保证线程安全。
修饰符 synchronized 返回值类型 方法名称(形参列表) {
操作共享资源的代码
}
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
同步方法底层原理:
- 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
- 如果方法是实例方法:同步方法默认用this作为的锁对象。
- 如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
4.4、方式三:Lock锁Lock锁
Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。
Lock的常用方法
4.5、总结
5、线程池
5. 1、概述
当前创建线程的问题 :
用户每发起一个请求,后台就需要创建一个新线程来处理,任务处理完毕之后,线程就会被销毁。
下次新任务来了肯定又要创建新线程处理的,用完又要被销毁。
而创建和销毁线程的开销是很大的,当请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
什么是线程池:
线程池就是一个可以复用线程的技术
它就像一个大的池子一样,里面可以放置一些线程,当需要的时候,就从里面取出来用,用完了就还回去。
如此一来,就不必频繁的创建和销毁线程了,大大的提高了线程的利用率,提供系统的性能。
线程池的执行流程:
判断核心线程数是否已满,如果没满,则创建一个新的核心线程来执行任务。
如果核心线程满了,则判断工作队列是否已满,如果没满,则将任务存储在这个工作队列。
如果工作队列满了,则判断最大线程数是否已满,如果没满,则创建临时线程执行任务。
如果最大线程数已满,则执行拒绝策略。
5.2、线程池的创建
JDK 5.0起提供了代表线程池的接口:ExecutorService。
使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize : 指定线程池的核心线程的数量(3个)
- maximumPoolSize:指定线程池的最大线程数量(5个)
- keepAliveTime :指定临时线程的存活时间
- unit:指定临时线程存活的时间单位(秒、分、时、天)
- workQueue:指定线程池的任务队列
- threadFactory:指定线程池的线程工厂
- handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
任务缓冲队列:
任务拒绝策略
5.3、线程池处理Runnable任务
ExecutorService的常用方法
5.4、线程池处理Callable任务
ExecutorService的常用方法
5.5、Executors工具类实现线程池
Executors:
是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。
注意 :这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。
Executors使用可能存在的陷阱
大型并发系统环境中使用Executors如果不注意可能会出现系统风险。
6、线程通信
当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。
线程通信的常见模型(生产者与消费者模型)
生产者线程负责生产数据。
消费者线程负责消费生产者生产的数据。
注意:生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产!
Object类的等待和唤醒方法
注意:上述方法应该使用当前同步锁对象进行调用。
7、进程与线程
进程:正在运行的程序(软件)就是一个独立的进程
线程:线程是属于进程的,一个进程中可以同时运行很多个线程
关系:进程=火车 线程=车厢
8、并发与并行
并发的含义:
进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
并行的含义:
在同一个时刻上,同时有多个线程在被CPU调度执行。
并发和并行同时执行。
9、线程生命周期
也就是线程从生到死的过程中,经历的各种状态及状态转换,Java总共定义了6种状态
public class Thread{
...
public enum State {
NEW, 线程刚被创建,但是并未启动
RUNNABLE, 线程已经调用了start(),等待CPU调度
BLOCKED, 线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态
WAITING, 一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒
TIMED_WAITING, 同waiting状态,有几个方法(sleep,wait)有超时参数,调用他们将进入Timed Waiting状态
TERMINATED; 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡
}
...
}
线程的6种状态互相转换
10、互斥锁
- java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
- 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,能有一个线程访问该对象。
- 关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时表明该对象在任一时刻只能由一个线程访问。
- 同步的局限性:导致程序的执行效率要降低。
- 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)。
- 同步方法(静态的)的锁为当前类本身。
注意事项:
- 同步方法如果没有使用static修饰:默认锁对象为this。
- 如果方法使用static修饰,默认锁对象:当前类.class。
- 实现的落地步骤:
需要先分析上锁的代码。
选择同步代码块或同步方法。
要求多个线程的锁对象为同一个即可。
11、死锁
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生。
第二十二章、网络通信
1、介绍
网络通信:可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)
网络通信架构:基本的通信架构有2种形式:CS架构( Client客户端/Server服务端 ) 、 BS架构(Browser浏览器/Server服务端)
CS:
BS:
无论是CS架构,还是BS架构的软件都必须依赖网络通信!
2、网络通信三要素
2.1、IP地址
IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志。
IP地址有两种形式:IPv4、IPv6
IPv4:
IPv6:共128位,号称可以为地球每一粒沙子编号。
IPv6分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开。
公网IP:是可以连接互联网的IP地址
内网IP:也叫局域网IP,只能组织机构内部使用
192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用
特殊IP地址:127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机
IP常用命令:
ipconfig:查看本机IP地址
ping IP地址:检查网络是否连通
IP域名:
InetAddress:代表IP地址
常用方法:
2.2、端口号
端口:标记正在计算机设备上运行的应用程序的一个数字,范围是 0~65535。
分类:
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序。
动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
2.3、协议
通信协议:网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
开放式网络互联标准:OSI网络参考模型
OSI网络参考模型:全球网络互联标准
TCP/IP网络模型:事实上的国际标准
传输层的2个通信协议:
UDP(User Datagram Protocol):用户数据报协议
TCP(Transmission Control Protocol) :传输控制协议
UDP协议:
特点:无连接、不可靠通信。
不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。
发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的 。
常用于语音通话和视频直播。
TCP协议:
特点:面向连接、可靠通信。
TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。
可靠连接:确定通信双方,收发消息都是正常无问题的!
常用于网页,文件下载和支付。
3、UDP通信
特点:无连接、不可靠通信.
不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了。
Java提供了一个java.DatagramSocket类来实现UDP通信。
DatagramSocket: 用于创建客户端、服务端
DatagramPacket:创建数据包
客户端实现步骤:
- 创建DatagramSocket对象(客户端对象)。
- 使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
- 如果用户输入的不是exit, 把数据封装成DatagramPacket
- 使用DatagramSocket对象的send方法,传入DatagramPacket对象。
- 释放资源。
服务端实现步骤:
- 创建DatagramSocket对象并指定端口(服务端对象)。
- 创建DatagramPacket对象接收数据(数据包对象)。
- 使用DatagramSocket对象的receive方法,传入DatagramPacket对象。
- 使用while死循环不断的进行第3步。
4、TCP通信
特点:面向连接、可靠通信。
通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。
Java提供了一个java.Socket类来实现TCP通信。
TCP通信之-客户端开发
客户端程序就是通过java包下的Socket类来实现的。
客户端实现步骤:
- 创建客户端的Socket对象,请求与服务端的连接。
- 使用socket对象调用getOutputStream()方法得到字节输出流。
- 使用字节输出流完成数据的发送。
- 释放资源:关闭socket管道。
TCP通信-服务端程序的开发
服务端是通过java包下的ServerSocket类来实现的。
服务端实现步骤:
- 创建ServerSocket对象,注册服务端端口。
- 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
- 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
- 释放资源:关闭socket管道。
实现服务端同时接收多个客户端的消息:主线程定义了循环负责接收客户端Socket管道连接,每接收到一个Socket通信管道后分配一个独立的线程负责处理它。
bs程序开发:
注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据。
BS架构的基本原理:HTTP协议规定:响应给浏览器的数据格式必须满足如下格式
HTTP/1.1 200 OK
Content-Type : text/html;charset=UTF-8
必须换行
<div style="color: red;font-size: 120px;text-align: center">
学习Java
</div>
5、InetAddress 类
- 获取本机InetAddress对象 getLocalHost。
- 根据指定主机名/域名获取ip地址对象 getByName。
- 获取InetAddress对象的主机名 getHostName。
- 获取InetAddress对象的地址 getHostAddress。
6、Socket
- 套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准。
- 通信的两端都要有Socket,是两台机器间通信的端点。
- 网络通信其实就是Socket间的通信。
- Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
- 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
7、netstat 指令
- netstat -an | more 可以分页显示。
- 要求在dos控制台下执行 win+r。
- netstat -an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况。
说明:
- Listening 表示某个端口在监听
- 可以输入ctrl +c退出指令
- 如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息!
第二十三章、单元测试
1、介绍
单元测试:就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。
Junit单元测试框架:可以用来对方法进行测试,它是由Junit公司开源出来的。
优点:
可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
不需要程序员去分析测试的结果,会自动生成测试报告出来。
2、Junit框架的常见注解
Junit单元测试框架的常用注解(Junit 4.xxxx版本)
在测试方法执行前执行的方法,常用于:初始化资源。
在测试方法执行完后再执行的方法,常用于:释放资源。
Junit单元测试框架的常用注解(Junit 5.xxxx版本)
开始执行的方法:初始化资源。
执行完之后的方法:释放资源。
第二十四章、反射
1、介绍
反射(Reflection):反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。
使用:
- 反射第一步:加载类,获取类的字节码:Class对象
- 获取类的构造器:Constructor对象。
- 获取类的成员变量:Field对象。
- 获取类的成员方法:Method对象。
2、加载类
反射第一步:加载类,获取类的字节码:Class对象
获取Class对象的三种方式:
5. 直接使用类名.class获取:Class c1 = 类名.class
6. 调用Class提供的方法:Class c2 = Class.forName(“全类名”)
7. 调用Object提供的方法:Class c3 = 对象.getClass()
3、获取类构造器
Class提供了从类中获取构造器的方法。
获取类构造器的作用:依然是初始化对象返回
4、获取类的成员变量
Class提供了从类中获取成员变量的方法。
获取到成员变量的作用:依然是赋值、取值
5、获取类的成员方法
Class提供了从类中获取成员方法的API。
成员方法的作用:依然是执行
6、作用、应用场景
基本作用:可以得到一个类的全部成分然后操作。
可以破坏封装性。
最重要的用途是:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能。
第二十五章、lombok
Lombok是一个实用的Java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率。
Lombok会在编译时,自动生成对应的java代码。我们使用lombok时,还需要安装一个lombok的插件(idea自带)。
第二十六章、maven
1、介绍
Apache Maven 是一个项目管理和构建工具,它的主要作用如下:
- 统一项目结构:提供标准、统一的项目结构。
- 项目构建:标准跨平台(Linux、Windows、MacOS)的自动化项目构建方式。
- 依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题。
maven仓库:
仓库:用于存储资源,管理各种jar包。
本地仓库:自己计算机上的一个目录。
中央仓库:由Maven团队维护的全球唯一的。 仓库地址:https://repo1.maven/maven2/
远程仓库(私服):一般由公司团队搭建的私有仓库。
jar包在maven本地仓库中的存放结构:
jar包在maven本地仓库是按照三层目录结构存放的
项目引入jar包:
maven项目中在pom.xml中通过dependency标签引入jar包
在此标签中需要声明三个要素:groupId、artifactId、version
2、创建maven工程
2.1、配置Maven环境
2.2、创建maven项目
- 创建一个web阶段的空工程。
- 创建模块,选择Maven,点击Next。
- 填写模块名称,坐标信息,点击finish,创建完成。
- 编写 HelloWorld,并运行。
maven坐标:
Maven 中的坐标是资源的唯一标识,通过该坐标可以唯一定位资源位置。
使用坐标来定义项目或引入项目中需要的依赖。
Maven 坐标主要组成:
groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.Baidu)
artifactId:定义当前Maven项目名称(通常是模块名称,例如 order-service、goods-service)
version:定义当前项目版本号
maven结构:
使用maven构建的项目,必须按照maven规定的结构来存放我们的代码
2.3、导入maven项目
打开IDEA,选择右侧Maven面板,点击 + 号,选中对应项目的pom.xml文件,双击即可。
3、maven细节
3.1、依赖配置
依赖:指当前项目运行所需要的jar包,一个项目中可以引入多个依赖。
配置:
- 在 pom.xml 中编写 标签
- 在 标签中 使用 引入坐标
- 定义坐标的 groupId,artifactId,version
- 点击刷新按钮,引入最新加入的坐标
注意事项:
- 如果引入的依赖,在本地仓库不存在,将会连接远程仓库/中央仓库,然后下载依赖。(这个过程会比较耗时,耐心等待)
- 如果不知道依赖的坐标信息,可以到https://mvnrepository/中搜索。
依赖范围:依赖的jar包,默认情况下,可以在任何地方使用。可以通过 …</ scope > 设置其作用范围。
作用范围:
- 主程序范围有效。(main文件夹范围内)
- 测试程序范围有效。(test文件夹范围内)
- 是否参与打包运行。(package指令范围内)
3.2、常见命令
maven可以基于命令快速完成项目构建,下面来看一些常见的命令。
clean: 清理命令,作用是清理掉上一次项目构建产生的文件,也就是删除target目录。
compile: 编译命令,作用是将 src/main/java 下的文件编译为class文件输出到target目录下。
test: 测试命令,作用是执行 src/test/java 下的测试类。
package: 打包命令,作用是将 src/main 下的文件进行打包。
install: 安装命令,将打好包安装到本地仓库。
deploy: 部署命令,将打好的包安装到私服。
3.3、生命周期
Maven的生命周期就是为了对所有的maven项目构建过程进行抽象和统一。
每套生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。
Maven中有3套相互独立的生命周期:
clean:清理工作。
- pre-clean
- clean
- post-clean
default:核心工作,如:编译、测试、打包、安装、部署等。
- validate
- initialize
- generate-sources
- process-sources
- generate-resources
- process-resources
- compile
- process-classes
- generate-test-sources
- process-test-sources
- generate-test-resources
- process-test-resources
- test-compile
- process-test-classes
- test
- prepare-package
- package
- verify
- install
- deploy
site:生成报告、发布站点等。
- pre-site
- site
- post-site
- site-deploy
注意事项:在同一套生命周期中,当运行后面的阶段时,前面的阶段都会运行。
生命周期阶段:
- clean:移除上一次构建生成的文件
- compile:编译项目源代码
- test:使用合适的单元测试框架运行测试(junit)
- package:将编译后的文件打包,如:jar、war等
- install:安装项目到本地仓库
4、分模块构建
4.1、服务拆分
因此需要对项目进行模块拆分,这样各个模块完成自己业务内的功能,这些模块组装起来达成整个项目的功能
分模块的好处:
- 模块之间边界清晰,每个模块可以单独开发,协作人员随之减少,出现代码冲突情况也会大大降低
- 开发更加灵活,模块体积更小,任务安排和问题排查更加方便
怎么拆分模块:
大部分情况都是按照功能拆分,各模块具备的功能相对独立:
- sky-common(项目中通用代码)
- sky-pojo (实体代码)
- sky-server(业务相关代码)
拆分之后,但是sky-server模块需要使用sky-pojo和sky-common ,可以直接在pom文件中直接使用<dependency>进行依赖即可
4.2、创建父子工程
4.2.1、创建父工程
父工程不需要写任何代码,只负责子模块的构建工作,因此可以把src目录删除掉
4.2.2、创建common模块
sky-common 作为通用工具模块,可以放一些工具类、配置类、常量…
4.2.3、创建sky-pojo模块
sky-pojo存放Entity、VO、DTO
4.2.4、创建sky-server模块
sky-server模块用于实现各个模块业务,例如用户模块实现用户的增删改查,也就是在这个模块中完成三层的代码
sky-server需要使用sky-common、sky-pojo中的代码,因此需要添加这两个模块的依赖
4.3、分模块细节
4.3.1、模块继承
每个子模块中都有大量的依赖,并且会出现相同的依赖,不同的模块中使用的版本可能会出现不一致的情况,
例如service、dao都会使用到spring-context的依赖,如果使用的不同的版本,则可能导致真个项目的依赖版本混乱。
这时候就需要一个工程统一管理子模块中的依赖,此时可以将这些依赖统一放在父工程中进行管理,这样既可统一版本,子模块只需要从父工程继承依赖即可。
4.3.2、模块聚合
在我们创建模块的时候,IDEA已经自动帮我们完成的子模块的聚合,这样只要父工程执行构建操作,
例如install,子模块都会执行install,不需要在每个中一个个执行install了
4.3.3、依赖传递
在maven中,依赖是可以传递的,假设存在三个项目,分别是项目A,项目B以及项目C
假设C依赖B,B依赖A,那么我们可以根据maven项目依赖的特征不难推出项目C也依赖A
4.4、依赖冲突
在依赖的传递过程中,很容易出现同一jar包的版本冲突问题,这个就称为依赖冲突
4.4.1、第一声明优先原则
在pom文件定义依赖,先声明的依赖为准。
4.4.2、路径近者优先原则
从依赖程序开始算起,到被依赖的程序,以路径短的为准。
4.4.3、依赖排除
依赖排除就是在依赖引入的过程中,通过exclusions
标签排掉指定的跟随依赖
4.4.4、版本锁定
面对众多的依赖,有一种方法不用考虑依赖路径、声明优先等因素,可以采用直接锁定版本的方法确定依赖构件的版本
版本锁定后,系统会以锁定的版本的为准添加到工程中,此方法在企业开发中常用。
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<!--提取版本号-->
<spring.version>5.3.22</spring.version>
</properties>
<!--依赖管理: 仅仅声明依赖版本,不会引入-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--依赖引入: 不在设置版本号-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
</dependencies>
5、私服
5.1、简介
公司在自己的局域网内搭建自己的私服,私服服务器即是公司内部的maven远程仓库
每个员工的电脑上安装maven软件并且连接私服,可以将自己开发的项目发布到私服,也可以从私服下载其它成员上传的项目包。
私服的软件有多款,比如:Apache Archiva,Artifactory,Sonatype Nexus,绝大部分使用的是nexus
nexus是一个强大的maven仓库管理器,它极大的简化了本地内部仓库的维护和外部仓库的访问
5.2、搭建私服
第1步: 解压nexus-3.30.1-01-win64.zip到一个没有中文没有空格的目录
第2步: 以管理员身份运行cmd命令,然后进入软件的bin目录,通过命令启动nexus软件
第3步: 访问地址:http://localhost:8081,默认提供了一个admin的管理员账户,密码在给出的提示文件中
注意:登录之后提示修改密码,这里修改为admin
是否开启匿名账户访问,就是不登录的情况下是否可以访问私服,匿名账户可以查询但是不能修改服务中的内容
5.3、仓库类型
hosted:本地仓库,通常我们会部署自己的构件到这一类型的仓库
proxy:代理仓库,它们被用来代理远程的公共仓库,如maven中央仓库
group:仓库组,用来合并多个hosted/proxy仓库,主要用于下载依赖
默认中央仓库地址是国外地址,下载非常慢,设置:https://maven.aliyun/repository/public
5.4、将项目发布到私服
企业中多个团队协作开发通常会将一些公用的组件、开发模块等发布到私服供其它团队或模块开发人员使用。
IDEA是不能直接和私服打交道的,而是先将组件打包到本地仓库,再由本地仓库上传到私服
第1步:在maven的settings.xml文件中, 添加下面配置
<servers>
<server>
<!-- 自定义ID -->
<id>heima</id>
<username>admin</username>
<password>admin</password>
</server>
</servers>
第2步:在父工程的pom.xml文件中, 添加下面配置
<!--配置访问私服的仓库地址-->
<distributionManagement>
<repository>
<!--账号的ID-->
<id>heima</id>
<url>http://localhost:8081/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>heima</id>
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
注意url 是从nexus仓库中复制过来的,注意snapshots和releases仓库的URL不要复制错了
第3步: 父工程执行install,把模块安装到本地仓库,然后选择需要上传的模块(sky-pojo), 执行deploy命令
第4步:验证,在nexus控制台查看仓库是否有上传的模块
5.5、从私服下载依赖
配置nexus之后,本地仓库没有jar包,就去私服下载,私服如果没有, 再由私服去中央下载。
第1步: 在maven的settings.xml文件中, 添加下面配置
<profile>
<id>default_profile</id>
<repositories>
<!--包含需要连接到远程仓库的信息 -->
<repository>
<!--远程仓库唯一标识 -->
<id>nexus-repository</id>
<!--远程仓库组的URL,仓库组中任意仓库有需要的依赖都可以下载 -->
<url>http://localhost:8081/repository/maven-public/</url>
<!--如何处理远程仓库里发布版本的下载 -->
<releases>
<!--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -->
<enabled>true</enabled>
<!--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:always(一直),daily(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。 -->
<updatePolicy>never</updatePolicy>
<!--当Maven验证构件校验文件失败时该怎么做-ignore(忽略),fail(失败),或者warn(警告)。 -->
<checksumPolicy>warn</checksumPolicy>
</releases>
<!--如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组配置,POM就可以在每个单独的仓库中,为每种类型的构件采取不同的策略。例如,可能有人会决定只为开发目的开启对快照版本下载的支持。参见repositories/repository/releases元素 -->
<snapshots>
<!--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -->
<enabled>true</enabled>
<!--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:always(一直),daily(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。 -->
<updatePolicy>always</updatePolicy>
<!--当Maven验证构件校验文件失败时该怎么做-ignore(忽略),fail(失败),或者warn(警告)。 -->
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<!-- 插件仓库,maven的运行依赖插件,也从私服下载插件 -->
<pluginRepository>
<id>public</id>
<name>Public Repositories</name>
<url>http://localhost:8081/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
<!--使用profile定义仓库需要激活才可生效-->
<activeProfiles>
<activeProfile>default_profile</activeProfile>
</activeProfiles>
第2步: 测试
- 删除本地仓库中的sky-pojo的文件
- 在sky-server模块上执行compile命令
- 通过控制台观察效果
第二十七章、Mybatis
1、介绍
MyBatis是一款优秀的框架,用于实现向数据库发送SQL语句,实现增删改查。
MyBatis本是Apache的一个开源项目iBatis, 后改名为MyBatis。
官网:https://mybatis/mybatis-3/zh/index.html
使用Mybatis向数据库保存一个User对象:
//1
public class User {
private Integer id;
private String name;
}
//2
user对象
id:1 name:张三
//3
insert into user values(1,’张三’)
类 ---- 数据表
属性 ---- 字段
对象 ---- 记录
- 准备数据环境(创建数据库、数据表)
- 创建工程,添加依赖
- 准备实体类(实体类要根据数据表书写)
- 创建Mapper接口(在接口中的方法上使用注解编写SQL语句)
- 创建配置文件(配置数据库连接信息)
- 测试
2、Mybatis实现增删改
2.1、增加
SQL语句:
insert into user(null,name,age,gender,phone) values (null,'张三',50,1,'13900139000');
接口方法:
@Insert("insert into user values(null, #{name}, #{age}, #{gender}, #{phone})")
void save(User user);
主键返回:
@Options(keyProperty = "id", useGeneratedKeys = true)
@Insert("insert into user values(null, #{name}, #{age}, #{gender}, #{phone})")
void save(User user);
useGeneratedKeys=true 告诉mybatis,当执行完保存操作之后,需要将数据库中新增记录的主键查询回来。
keyProperty = “id” 查询回来的主键值需要封装到方法参数对象的哪个属性上。
2.2、修改
SQL语句:
update user set name = '李四', age = 20, gender = 1, phone = '13700137000' where id = 1;
接口方法:
@Update("update user set name=#{name}, age=#{age}, gender=#{gender}, phone=#{phone} where id=#{id}")
void update(User user);
2.3、删除
SQL语句:
delete from user where id = 1;
接口方法:
@Delete("delete from user where id = #{id}")
void delete(Integer id);
注意事项:如果mapper接口方法形参只有一个简单类型的参数,#{…} 里面的属性名可以随便写,但是推荐写成方法形参。
4、查询
4.1、查询结果封装
SQL语句:
select * from emp;
接口方法:
@Select("select * from emp")
public List<Emp> findAll();
数据封装:
- 实体类属性名 和 数据库表查询返回的字段名一致,mybatis会自动封装。
- 如果实体类属性名 和 数据库表查询返回的字段名不一致,不能自动封装。
- 开启驼峰命名:如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射。
#开启驼峰命名自动映射,即从数据库字段名 a_column 映射到Java 属性名 aColumn。
<setting name="mapUnderscoreToCamelCase" value="true"/>
- 起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样。
@Select("select id, username, password, name, gender, image, job, entrydate ed, dept_id deptId, create_time createTime, update_time updateTime from emp")]
List<Emp> findAll();
- 手动结果映射:通过 @Results及@Result 进行手动结果映射。
@Select("select * from emp")
@Results({
@Result(column = "dept_id", property = "deptId"),
@Result(column = "entrydate", property = "ed")
})
List<Emp> findAll();
4.2、条件查询
SQL语句:
select * from emp where name = '张三丰' and gender = 1 and entrydate between '2010-01-01' and '2020-01-01 ';
接口方法:
@Select("select * from emp where name = #{name} and gender = #{gender} and entrydate between #{begin} and #{end}")
List<Emp> findList(@Param("name") String name, @Param("gender") Short gender,
@Param("begin") LocalDate begin, @Param("end") LocalDate end);
@Param 标注在方法参数的前面,用于声明参数在#{}中的名字。
后面SpringBoot2.x整合mybatis之后,这个注解可以不再添加。
4.3、模糊查询
SQL语句:
select * from emp where name like '%张%' and gender = 1 and entrydate between '2010-01-01' and '2020-01-01 ';
接口方法:
@Select("select * from emp where name like '%${name}%' and gender = #{gender} and entrydate between #{begin} and #{end}")List<Emp> findList(@Param("name") String name, @Param("gender") Short gender,
@Param("begin") LocalDate begin, @Param("end") LocalDate end);
@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and entrydate between #{begin} and #{end}")List<Emp> findList(@Param("name") String name, @Param("gender") Short gender,
@Param("begin") LocalDate begin, @Param("end") LocalDate end);
5、抽取工具
操作步骤:
- 先观察测试类,找到方法中共同的代码
- 创建一个工具类,在类中创建静态方法,然后将公共代码提取到静态方法中
- 使用工具类中的方法替换公共代码
在Mybatis中#和$的区别:
#{…}
- 表示占位符
- 同类型的SQL只编译一次
- 使用它可以防止SQL注入问题
${…}
- 表示字符串拼接
- 每次SQL都会重新编译
- 使用它存在SQL注入问题
6、xml书写sql
XML映射文件:
- 使用Mybatis的注解,主要是来完成一些简单的增删改查功能。
- 如果需要实现复杂的SQL功能,建议使用XML来配置映射语句。
- XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
- XML映射文件的namespace属性为Mapper接口全限定名一致。
- XML映射文件中sql语句的id与Mapper 接口中的方法名一致,并保持返回类型一致。
//Mapper接口
@Mapper
public interface EmpMapper {
public List<Emp> findById (Integer id);
}
//XML映射文件
<mapper namespace="com.itheima.mapper.EmpMapper">
<select id="findById" resultType="com.itheima.domain.Emp">
select * from emp where id = #{id}
</select>
</mapper>
MybatisX 是一款基于 IDEA 的快速开发Mybatis的插件,为效率而生。
7、动态sql
7.1、介绍
随着用户的输入或外部条件的变化而变化的SQL语句,我们称为 动态SQL。
7.2、<if>
和<where>
if 用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
where 元素只会在子元素有内容的情况下才插入where子句。而且会自动去除子句的开头的AND 或OR。
select * from emp
<where>
<if test="name != null">
name = #{name}
</if>
</where>
7.4、<if>
和<set>
if 用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)
update emp
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
</set>
7.5、<foreach>
SQL语句:
delete from emp where id in (1,2,3);
接口方法:
//批量删除
public void deleteByIds(@Param("ids") List<Integer> ids);
XML映射文件:
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
collection:集合名称
item:集合遍历出来的元素/项
separator:每一次遍历使用的分隔符
open:遍历开始前拼接的片段
close:遍历结束后拼接的片段
sql片段:
7.6、 <sql>
定义可重用的 SQL 片段。
* 定义SQL片段: <sql id="selectUser"></sql>
* 引用SQL片段: <include refid="selectUser"></include>
7.7、 <include>
通过属性refid,指定包含的sql片段。
8、配置文件
SqlMapConfig.xml:
settings:控制一些全局配置项的开闭
mappers:用于指定Mapper接口的位置
environments: 配置事务管理器和数据库连接信息
数据库连接池:
- 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
- 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
优势:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
标准接口:DataSource
官方(sun)提供的数据库连接池接口,由第三方组织实现此接口。
功能:获取连接 归还连接
Druid(德鲁伊): 阿里巴巴提供的数据库连接池技术,国内使用率很高,提供了完善的监控机制。
HikariCP: 日本人开发的连接池技术,号称性能之王,速度最快,SpringBoot2.0默认使用此连接池。
9、MyBatis Generator代码生成
9.1、概述
MyBatis Generator 作为一个基于 MyBatis 的独立工具,它可以通过简单的配置去帮我们生成数据表所对应的 PO、DAO、XML 等文件,减去我们手动去生成这些文件的时间,有效提高开发效率。
9.2、环境集成
使用idea打开资料中提供好的项目:mybatis-gen
然后修改代码生成的核心配置文件:mybatis-generator.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis/dtd/mybatis-generator-config_1_0.dtd">
<!--生成实体类 mapper mapper.xml-->
<generatorConfiguration>
<!--导入mysql的驱动-->
<classPathEntry
location="D:\class194\respository_zzyl\mysql\mysql-connector-java\8.0.19\mysql-connector-java-8.0.19.jar"/>
<!-- context 是逆向工程的主要配置信息 -->
<!-- id:起个名字 -->
<!-- targetRuntime:设置生成的文件适用于那个 mybatis 版本 -->
<context id="default" targetRuntime="MyBatis3">
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 批量插入插件 -->
<plugin type="com.itfsw.mybatis.generator.plugins.BatchInsertPlugin"/>
<!-- 去掉生成出来的代码的注解 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
</commentGenerator>
<!--jdbc的数据库连接-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql:///zzyl"
userId="root"
password="root">
<!-- 8.0以后得mysql有个问题,会把mapper.xml内容生成两遍,加上这个就好-->
<property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>
<!--非必须,类型处理器,在数据库类型和java类型之间的转换控制-->
<javaTypeResolver>
<!-- 默认情况下数据库中的 decimal,bigInt 在 Java 对应是 sql 下的 BigDecimal 类 -->
<!-- 不是 double 和 long 类型 -->
<!-- 使用常用的基本类型代替 sql 包下的引用类型 -->
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- targetPackage:生成的实体类所在的包 -->
<!-- targetProject:生成的实体类所在的硬盘位置 -->
<javaModelGenerator targetPackage="com.zzyl.entity"
targetProject="src/main/java">
<!-- 是否允许子包
<property name="enableSubPackages" value="false"/>-->
<!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- targetPackage 和 targetProject:生成的 mapper 文件的包和位置 -->
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources">
<!-- 针对数据库的一个配置,是否把 schema 作为字包名
<property name="enableSubPackages" value="false"/>-->
</sqlMapGenerator>
<!-- targetPackage 和 targetProject:生成的 interface 文件的包和位置 XMLMAPPER:生成XML方式,ANNOTATEDMAPPER:生成注解方式-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.zzyl.mapper" targetProject="src/main/java">
<!-- 针对 oracle 数据库的一个配置,是否把 schema 作为字包名
<property name="enableSubPackages" value="false"/>-->
</javaClientGenerator>
<table tableName="sys_resource"
domainObjectName="Resource"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false">
<generatedKey column="id" sqlStatement="Mysql" identity="true"/>
<columnOverride column="create_time" javaType="java.time.LocalDateTime"/>
<columnOverride column="update_time" javaType="java.time.LocalDateTime"/>
</table>
</context>
</generatorConfiguration>
- 配置mysql的驱动位置,找到maven本地仓库中的mysql驱动
- 配置数据库链接
- 设置实体类、mapper、mapper映射文件的目录
- table标签可以设置对应数据库中的表,可配置多个
9.3、代码生成
第二十八章、MyBatis——Plus
1、介绍
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
Mybatis-Plus已经封装好了大量增删改查的方法,程序员只需要继承BaseMapper就可以使用这些方法了,无需自己再开发。
官网:https://baomidou/,下面是其特性:
- 无侵入:只做增强不做改变,不会对现有工程产生影响
- 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD操作
- 支持 Lambda:编写查询条件无需担心字段写错
- 支持主键自动生成
- 内置分页插件
2、常用注解
2.1、@TableName
作用:表名注解,标识实体类对应的表,如果表名和类名一致可以省略
使用位置:实体类
@TableName("tbl_product") //绑定表关系
public class Product {}
如果每个表都是以固定前缀开头,可以全局配置表前缀
属性设置 > 全局设置: 如果使用了 @TableName 指定表名,则会忽略全局的表前缀
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_ #表前缀
2.2、@TableField
作用:其他属性注解
使用位置:实体类普通属性
// 商品详细信息字段(详情表) 排除跟商品信息表映射关系
@TableField(exist = false)
private String desc;
2.3、@TableId
作用:主键注解
使用位置:实体类主键属性
//IdType 指定主键类型
@TableId(value = "id",type = IdType.AUTO)
private Long id;
如果大部分表主键都是自增,可以进行全局设置
属性上的优先级 > 全局设置
mybatis-plus:
global-config:
db-config:
id-type: auto #主键策略
table-prefix: tbl_ #表前缀
2.4、小结
3、Mapper接口
3.1、介绍
BaseMapper:通用 CRUD 封装BaseMapper接口, 泛型T为任意实体对象
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
MyBatisPlus 提供了 Wrapper 条件构造器
接口,用于设置条件
- QueryWrapper LambdaQueryWrapper 设置查询、删除条件
- UpdateWrapper LambdaUpdateWrapper 设置修改条件
3.2、条件查询
3.2.1、API
AbstractWrapper提供了大量设置条件的方法
@SpringBootTest
public class ProductMapperQueryTest {
@Autowired
private ProductMapper productMapper;
@Test
public void query1(){
Double price = null;
String pname = " 华 为 ";
// select * from tbl_product where price > ? and pname like '%?%'
QueryWrapper<Product> queryWrapper = new QueryWrapper<Product>();
// great than
if(price!=null){
queryWrapper.gt("price",price);
}
if(StrUtil.isNotBlank(pname.trim())){ //isNotBlank 排除了 前后空格
queryWrapper.like("pname",pname.trim());
}
List<Product> products = productMapper.selectList(queryWrapper);
products.forEach(System.out::println);
}
}
3.2.2、查询优化-简化判断
@Test
public void query1(){
Double price = null;
String pname = " 华 为 ";
// select * from tbl_product where price > ? and pname like '%?%'
QueryWrapper<Product> queryWrapper = new QueryWrapper<Product>();
// great than
queryWrapper.gt(price!=null,"price",price);
queryWrapper.like(StrUtil.isNotBlank(pname.trim()),"pname",pname.trim());
List<Product> products = productMapper.selectList(queryWrapper);
products.forEach(System.out::println);
}
3.2.3、查询优化-lambda查询
@Test
public void query2(){
Double price = null;
String pname = " 华 为 ";
// select * from tbl_product where price > ? and pname like '%?%'
LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<Product>();
queryWrapper.gt(price!=null,Product::getPrice,price);
queryWrapper.like(StrUtil.isNotBlank(pname.trim()),Product::getPname,pname.trim());
List<Product> products = productMapper.selectList(queryWrapper);
products.forEach(System.out::println);
}
3.2.4、查询优化-链式编程
@Test
public void query2(){
Double price = null;
String pname = " 华 为 ";
// select * from tbl_product where price > ? and pname like '%?%'
LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<Product>();
queryWrapper.select(Product::getId,Product::getPname,Product::getBrand)
.gt(price!=null,Product::getPrice,price)
.like(StrUtil.isNotBlank(pname.trim()),Product::getPname,pname.trim());
List<Product> products = productMapper.selectList(queryWrapper);
products.forEach(System.out::println);
}
3.2.5、设置查询字段
// 设置查询字段 select id,pname,price,num from tbl_product where num > 25 or price > 3000
@Test
public void test05() throws Exception {
// 构建条件对象
LambdaQueryWrapper<Product> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// select id,pname,price,num (只查询四个字段)
lambdaQueryWrapper.select(Product::getId,Product::getPname,Product::getPrice,Product::getNum);
// 链式编程
lambdaQueryWrapper.gt(Product::getNum,25) // num > 25
.or()
.gt(Product::getPrice,3000); // or price > 3000
List<Product> list = productMapper.selectList(lambdaQueryWrapper);
list.forEach(System.out::println);
}
3.2.6、分组排序
//SELECT brand , AVG(price) AS avgPrice FROM tbl_product GROUP BY brand HAVING avgPrice>3000 ORDER BY avgPrice desc
@Test
public void query3(){
QueryWrapper<Product> queryWrapper = new QueryWrapper<Product>();
queryWrapper.select("brand","AVG(price) AS avgPrice");
queryWrapper.groupBy("brand");
queryWrapper.having(" avgPrice>3000");
queryWrapper.orderByDesc("avgPrice");
List<Map<String,Object>> products = productMapper.selectMaps(queryWrapper);
products.forEach(System.out::println);
}
3.3、条件修改
// 需求:将小米10S手机价格设置为3999
@Test
public void testUpdate(){
Double price = 3999D;
String pname = "小米10S";
// update tbl_product set price=3999 where pname="小米10S"
LambdaUpdateWrapper<Product> updateWrapper = new LambdaUpdateWrapper<Product>();
updateWrapper.eq(Product::getPname, pname);
Product product = new Product();
product.setPrice(price);
productMapper.update(product,updateWrapper);
}
3.4、条件删除
// 条件删除:DELETE FROM tbl_product WHERE pname = "OPPO";
@Test
public void test08()throws Exception{
// 条件对象
QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("pname","OPPO K9");
productMapper.delete(queryWrapper);
}
4、Service接口
4.1、介绍
为了简化service代码编写,mybatisPlus提供了通用 Service CRUD 封装IService接口
进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆
4.2、改造Service
使用Service 接口使用
- 接口继承 IService
- 实现类继承 ServiceImpl<M,T>
5、自动填充
官网很全
6、代码生成器
第二十九章、Spring框架
1、Spring介绍
Spring 是一款目前主流的JavaEE开源框架,由Rod Johnson提出并创立.
Spring 框架的目的是用于简化开发,目前流行版本是5.x系列,官网地址https://spring.io
Spring全称 Spring Framework,其主要功能有:
- IOC: 控制反转,轻松实现层间解耦。
- AOP: 面向切面编程,轻松实现公共代码抽取。
- MVC: 开发web应用程序。
- 事务: 无需编写代码,即可实现数据库事务管理。
- 测试: 与测试框架集成、web 单元测试。
2、控制反转
2.1、IOC概念
IOC( 控制 反转 )是一种设计思想,它的目的是指导我们设计出更加松耦合的程序
控制:指的是对象创建权
反转:指的对象的创建由程序员在类中主动创建反转到由Spring容器来创建
通俗的说:对象以前是我们程序员自己new的,但是现在对象都是由Spring创建并放入到了IOC容器中,如果需要使用某个对象的时候直接从容器中获取
2.2、添加配置类
@ComponentScan(“com.itheima”) 注解扫描(也叫组件扫描), 只有这里指定的包(及其子包)下类中的注解才会被Spring扫描
2.3、Bean创建
@Component
用于实例化对象,它支持一个value属性类定义bean在容器中的唯一标识
如果不写,默认值为类名的首字母小写
@Controller @Service @Repository
这三个注解的功能跟@Component类似,他们分别标注在不同的层上。
@Controller 标注在表示层的类上
@Service 标注在业务层的类上
@Repository 标注在持久层的类上
推荐使用这三个,当一个类实在不好归属在这三个层上时,再使用@Component
2.4、Bean作用域
singleton 单例,对于一个类,只会创建一个对象,每次从容器中获取,拿到的都是这个对象
prototype 多例,对于一个类,每次从容器中获取,都会创建一次,得到一个新的对象
request 在web环境中,每个请求范围内会创建新的对象
session 在web环境中,每个会话范围内会创建新的对象
application 在web环境中,每个应用范围内会创建新的对象
在类上使用@Scope注解定义Bean的作用域,Spring支持如下五种作用域,我们目前常用的是第一种
2.4.1、单例对象
对于一个类来说,只会创建一个对象,每次从容器中获取,拿到的都是这个对象。
2.4.2、多例对象
对于一个类,每次从容器中获取都会创建一次,得到一个新的对象。
2.5、Bean创建时机
2.5.1、单例对象
默认情况下,单例对象会在容器创建时就创建;也可以使用@Lazy将创建时机延迟到第;一次获取的时候创建。
2.5.2、多例对象
多例对象会在每次获取的时候才创建
2.6、Bean获取
getBean(“id”) 使用bean的id从容器中查找对象
getBean(Bean.class) 使用bean的class类型从容器中查找对象
getBean(“id”,Bean.class) 使用bean的id和class类型从容器中查找对象
Spring容器启动时,会把其中的bean都创建好,如果想要主动获取这些 bean,可以使用容器getBean()方法
常见错误:
-
如果查找的id在容器中不存在,会报错
NoSuchBeanDefinitionException: No bean named ‘deptDaoImpl1’ available -
如果查找的类型在容器中不存在,会报错
NoSuchBeanDefinitionException: No qualifying bean of type ‘DeptDao’ available -
如果查找的类型在容器中存在多个,会报错
NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.itheima.dao.DeptDao’
available: expected single matching bean but found 2: deptDaoImpl,deptDaoImpl2
2.7、依赖注入
2.7.1、@Autowired
@Autowired注解,默认是按照类型进行属性的赋值,如果存在多个相同类型的bean,就会报错。
2.7.2、@Qualifier
跟@Autowired联合使用,标注在要赋值的属性上,代表在按照类型匹配的基础上,再按照指定名称匹配。
2.7.3、@Primary
@Primary标注在实现类上,表示当一个接口有多个实现的类时,默认使用哪个作为主实现。
2.7.4、构造器依赖注入
依赖注入不止@Autowired注解这一种方式,还有构造器方式
构造器方式
2.8、管理第三方Bean
2.8.1、将对象放入容器
@Bean:写在方法上,表示将该方法的返回对象放到Spring容器中,在容器中的标识默认是方法名。
2.8.2、配置类优化
@Configuration:写在类上,表示该类是一个配置类,Spring在启动的时候会自动扫描加载类中的配置。
2.9、整合Mybatis
使用Mybatis,最关键是要让它产生Mapper的代理对象,流程如下:
Mapper代理对象–>sqlSession对象–>SqlSessionFactory对象
因此只要想办法让Spring创建出SqlSessionFactory,自然就可以获得Mapper对象
而要让Spring创建SqlSessionFactory就需要将数据源和mapper位置提前告诉Spring
2.10、注解总结
3、 AOP
3.1、AOP介绍
AOP( 面向切面编程 )是一种思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强。
SpringAOP是对AOP思想的一种实现,Spring底层同时支持jdk和cglib动态代理。
Spring会根据被代理的类是否有接口自动选择代理方式:
如果有接口,就采用jdk动态代理
如果没接口,就采用cglib的方式
3.2AOP核心概念
目标对象(Target)
被代理的对象
连接点(JoinPoint)
目标对象中得所有方法
切入点(PointCut)
目标对象中得要进行功能增强那部分方法
增强 (Advice 通知)
一个具体增强功能(增强对象 增强方法)
切面 (Aspect)
切面是一种描述,描述的是: 哪个增强方法加入到了哪个切点的什么位置,增强方法和切点方法的执行顺序
3.3、通知类型
四大通知描述的就是增强方法在切点方法的什么位置上执行:
- 前置通知(before):增强方法在切点运行之前执行。
- 返回后通知(after-returning):增强方法在某切点正常完成后执行的通知,不包括抛出异常的情况。
- 异常后通知(after-throwing):增强方法在某切点抛出异常退出时执行的通知。
- 后置通知(after):增强方法在某切点退出的时候执行的通知(不论是正常返回还是异常退出)。
try{
前置通知(before)
//切点执行位置
返回后通知(after-returning)
}catch(Execption e){
异常后通知(after-throwing)
}finally{
后置通知(after)
}
环绕通知:它是一种特殊的通知,他允许以编码的形式实现四大通知。
3.4、切点表达式
3.4.1、execution
execution() :指定一组规则来匹配某些类中的方法,匹配中的就是切点
* 代表一个 如果在参数位置代表只有一个参数 如果在包的位置 代表一层包
.. 代表任意
3.4.2、@annotation
@annotation:指定一个注解,凡是标有此注解的方法都是切点
3.4.3、记录日志详情
4、事务管理
4.1、事务回顾
事务是一组操作的集合,它是一个不可分割的工作单位,这些操作 要么同时成功,要么同时失败。
开启事务(一组操作开始前,开启事务):start transaction / begin ;
提交事务(这组操作全部成功后,提交事务):commit ;
回滚事务(中间任何一个操作出现异常,回滚事务):rollback ;
4.2、事务管理
注解:@Transactional
位置:业务(service)层的方法上、类上、接口上
作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务
4.3、事务属性
4.3.1、rollbackFor
默认情况下,只有出现 RuntimeException 才回滚异常,rollbackFor属性用于控制让非运行时异常也回滚。
4.3.2、propagation
propagation称为事务传播行为,表示当一个事务方法被另一个事务方法调用时,应该如何进行事务控制
Spring支持通过配置的形式来实现7种事务传播行为,我们需要掌握其中的前两种
5、SpringMVC
5.1、介绍
SpringMVC将Servlet一些通用功能进行了抽取和封装,使用它之后,代码主要有两部分组成:
-
前端控制器:由SpringMVC提供,主要负责接收参数和返回页面和数据。
-
处理器:由程序员编写,主要负责参数的处理和业务层调用。
5.2注解
5.2.1@RequestMapping
@RequestMapping注解用于建立请求URL和处理方法之间的对应关系, 常见属性如下:
- value: 等同于path,用于为当前方法绑定访问路径
- method:用于限制请求类型,如果省略此选项,代表不对请求类型做限制
注意:此注解可以标注在方法上,也可以标注在类上,标注在类上代表类中的所有方法都可以共用一段URL。
5.2.2@ResponseBody
@ResponseBody注解用于将方法返回值直接响应给浏览器,如果返回值类型是对象或集合,将会转换为JSON格式响应
位置:Controller方法上/类上(如果标在类上,代表类中所有方法上都生效)
简化:@RestController = @Controller + @ResponseBody
5.3接收请求参数
在SpringMVC中,可以使用多种数据类型来接收前端传入的参数
- 简单类型:需要保证前端传递的参数名称跟方法的形参名称一致
- 对象类型:需要保证前端传递的参数名称跟实体类的属性名称一致
- 数组类型:需要保证前端传递的参数名称跟方法中的数组形参名称一致
- 集合类型:请求参数名与形参集合名称相同且请求参数为多个,使用@RequestParam绑定参数关系
- 日期类型:接收参数为日期类型,需要使用@DateTimeFormat定义参数格式
- json参数:SpringMVC也可以接收请求体中的json字符串为参数,并且自动封装为指定对象,此时要求JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要使用@RequestBody标识
- 路径参数:springmvc还支持通过请求路径直接传递参数,后台使用{…}来标识该路径参数,需要使用@PathVariable获取
- @RequestParam:@RequestParam标注在方法参数之前,用于对传入的参数做一些限制,常见属性如下:
name:用于指定前端传入的参数名称
defaultValue:当参数为非必传参数且前端没有传入参数时,指定一个默认值
5.4统一异常处理
使用SpringMVC后,异常就不必在三层代码去处理了,而是直接抛给框架,框架会将所有的异常统一转交给我们自定义的统一异常处理类来处理。
SpringMVC支持下面两个注解来实现全局异常处理:
- @RestControllerAdvice 标注在类上,声明当前类是一个用于专门处理异常的类
- @ExceptionHandler 标注在方法上,声明当前方法可以处理哪些异常
第三十章、Restful
REST是一种软件架构风格,其强调HTTP应当以资源为中心在请求地址中尽量的不要出现动词
REST规范了HTTP请求动作,使用四个词语分别表示对资源的CRUD操作: GET(获取)、POST(新建)、PUT(更新)、DELETE(删除)
第三十一章、SpringBoot
1、概述
SpringBoot对Spring的改善和优化,它基于约定优于配置的思想,提供了大量的默认配置和实现
使用SpringBoot之后,程序员只需按照它规定的方式去进行程序代码的开发即可,而无需再去编写一堆复杂的配置
SpringBoot的主要功能如下:
起步依赖:SpringBoot以功能化的方式将需要的依赖进行组装,并且允许程序员以start的方式进行引入
默认配置:SpringBoot实现了大量依赖框架的默认配置项,程序员无须再进行自己配置
内置Tomcat:SpringBoot内置了一个tomcat,使用它开发的程序无需再进行tomcat部署,可直接运行
总之:SpringBoot最主要作用就是帮我们快速的构建庞大的spring项目,并且尽可能的减少配置,让程序员去关注业务而非配置。
2、主要功能
- 版本锁定:我们的项目继承了spring-boot-starter-parent父工程,它内部已经锁定了一些常见依赖的版本号,故而在我们自己开发的工程中无需再指定依赖的版本。
- 起步依赖:SpringBoot根据场景将各种依赖组装成了一个个的集合(starter),我们根据功能引入指定的starter即可。
- 内置tomcat:SpringBoot在中引入一个内置的tomcat,故而我们无需再将程序部署到位置的tomcat中即可运行。
- 默认配置:SpringBoot的约定大于配置,即SpringBoot的大量配置都有默认值,如果我们不去写配置就使用默认的。
3、配置文件
3.1自定义配置
SpringBoot是基于约定的,很多配置都有默认值,但也允许自定义配置,具体做法是在resources下创建文件:
application.yaml 或者 application.yml 或者 application.properties
注意:目前版本中, SpringBoot启动时会依次加载:yaml、yml、properties文件,优先级依次升高
3.2、YAML介绍
YAML(YAML Ain’t Markup Language),一种数据序列化格式
3.2.1、语法
大小写敏感
使用缩进表示层级关系
缩进的空格数目不重要,但是相同层级的元素必须左侧对齐
参数值和冒号之间必须有空格
#
表示注释,从这个字符一直到行尾,都会被解析器忽略
server:
port: 8082
servlet:
context-path: /itheima
3.2.2、数据格式
对象:键值对的集合
user:
username: '张三'
password: '123'
addressList: # 数组:一组按次序排列的值
'北京'
'上海'
'广州'
3.3、读取配置
方式1: @Value
此注解是Spring框架提供的,用来读取配置文件中的属性值并逐个注入到Bean对象的对应属性中
方式2: @ConfigurationProperties
此注解是SpringBoot框架提供的,用来快速将配置文件中的属性值批量注入到某个Bean对象的多个对应属性中
3.4、多环境配置
在实际开发中,项目的开发环境、测试环境中配置可能不一致,因此SpringBoot支持多环境配置 java -jar xxx,jar
4、常用功能
日志输出
Spring支持多种日志级别,通过配置文件,可以输出指定级别的日志
包名: 日志级别(常用的级别有4个: debug info warn error)
logging:
level:
org.springframework: info
com.itheima: info
设置 输出
debug debug info warn error
info info warn error
warn warn error
error error
静态资源:SpringBoot默认的静态资源是在classpath:/static/目录
第三十二章、微服务
1、单体架构
单体架构(monolithic structure):顾名思义,整个项目中所有功能模块都在一个工程中开发;项目部署时需要对所有模块一起编译、打包;项目的架构设计、开发模式都非常简单。
当项目规模较小时,这种模式上手快,部署、运维也都很方便,因此早期很多小型项目都采用这种模式。
但随着项目的业务规模越来越大,团队开发人员也不断增加,单体架构就呈现出越来越多的问题:
- 团队协作成本高:试想一下,你们团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
- 系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。
- 系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。
2、微服务
微服务架构,首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:
- 单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
- 团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人(2张披萨能喂饱)
- 服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响
本文标签: Java
版权声明:本文标题:Java. 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1725925227a1049290.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论