Java基础
三、Java基础
1. Java
1.1 什么是Java?
- Java
- Java是由sun公司发明的
- Java之父:James Gosling
- Java开发团队:*7(星号7)
- Java诞生记:Green -> Oak(橡树)-> Java
- 1991年Sun公司的James Gosling等人开始为“绿色计划”研发一种全新的可跨平台的语言
- 1994年将本想命名为Oak的语言更名为Java
- Sun Microsystems于1995年正式发布Java
- 一句话定义:
1995年SUN公司倡导的James Gosling发明的面向对象的可跨平台的类的语言
- Sun公司
- SUN:Stanford University Network
- Sun公司名字由来
- SUN公司是斯坦福联合网络学院创办的
- SUN是由斯坦福联合网络学院的首字母缩写组成
- SUN公司在发明Java之前是研发
Solaris
(Linux操作系统的一个版本)和Sparc
(CPU)Solaris + Sqarc = 工作站
- 08年10亿美金收购mysql
- 10年74亿被甲骨文收购
- Java的优缺点
- 优点
- 平台无关、
面向对象
、无指针、分布式应用、多线程开发、自动收集内存
- 平台无关、
- 缺点
- 运行速度慢、占用资源多、复杂
- 中间字节码需要二次编译,解释执行
- JVM占用资源多
- 无指针无法直接操作内存
- 垃圾回收线程占用资源,不能实时手机内存
- 优点
1.2 Java与Internet
- Java基于网络写业务逻辑
- Internet使Java更广泛,Java对Internet具有深远影响
- 在Java出现之前,web只能展现静态的文字、图片等内容,是java让web能够与用户动态的交互
- Applet程序可以对用户的出入和操作做出响应
D/S
与C/S
D/S
:基于web浏览器应用C/S
:基于客户端应用
1.3 Java的三个分支
- SE
-SE:Standard Edition
,标准编译器
,完整保留 - EE
EE:Enterprise Edition
,企业级编译器
- EJB(企业级Javabean)被淘汰,9种变13种,改名为Web开发
- 现在说的Java EE指的是快速开发框架,代替了淘汰的EJB功能
- ME
ME:Mobile Rdition
,移动编译器
,完全被淘汰
1.4 语言执行的三种方式
1.4.1 编译执行
- 源代码一次性被翻译成机器码,然后直接在计算机上执行
- 缺点:无法跨平台
- 优点:速度快
- 如:C语言
1.4.2 转译执行
- 编写一段源代码,在不同的平台或环境中都可以执行
- 一次
编写
到处执行 - 如:html、JavaScript
1.4.3 中间码+虚拟机
- 源代码被翻译成中间码,然后通过虚拟机执行
- 虚拟机是一个可以解释执行中间码的程序,通常用于解决跨平台执行的问题
- 一次
编译
到处执行 - 如:Java
1.5 Java的跨平台
-
平台
-
CPU + 操作系统 = 平台
-
硬平台:
CPU
- 不同的CPU指的是CPU上搭载的不同的指令集
CPU指令集
:CPU中用来计算和控制计算机系统的一套指令的集合,每种CPU都有其特定的指令集- 指令集分类
Windows操作系统只认
CISC指令集
IOS操作系统即认CISC指令集
,又认RISC指令集
- CISC指令集:Intel、AMD
- RISC指令集:PowerPC
-
软平台:
操作系统
操作系统
是充当用户和计算机之间交互的界面软件,不同的操作系统支持不同的CPU(CPU指令集)- 操作系统分类:windows操作系统、 Linux操作系统/Mac操作系统
三种主流的操作系统都支持
CISC指令集
-
-
跨平台
跨平台
指的是编译以后可以跨平台- 只有编译后才知道是“谁”的东西
- 所有的源程序都可以跨平台,但是不会将源程序给别人
-
跨平台原理
不能编译成机器语言,因为那样就与平台相关了,先编译成中间码,再将中间码由解释器二次编译,解释执行,二次编译后就不能跨平台了
- C语言的“不跨平台”
.c源程序
:编译以后不能跨平台winNT编译器
(VS):编译生成winNT程序
Linux编译器
(GCC、ICC):编译生成Linux程序
- 其他OS编译器:编译生成其他OS程序
- Java的“跨平台”
.java源程序
:一次编译到处执行- 先编译成
.class中间字节码
,再通过编译器二次编译(解释)生成对应的程序winNT编译器
(VS):二次编译生成winNT程序
Linux编译器
(GCC、ICC):二次编译生成Linux程序
- 其他OS编译器:二次编译生成其他OS程序
- 先编译成
- 一次编译:与平台无关的编译器生成与平台无关的中间码
- 二次编译(解释):解释器是与平台相关的
2. JDK
2.1 JDK的组成
- JDK:Java Development Kit(Java开发工具包)
JDK
=Java编译器
+Java解释器
+ 其他组件- 开发Java需要从源文件到中间字节码的编译器
- 运行Java需要Java解释器
JRE
:运行时环境(只运行Java可以只使用它)JDK
:运行 + 开发时的环境(包括JRE)
2.2 JDK的部署
- JDK的安装路径:
C:\Program Files\Java
(默认的) - 配置环境变量
-
JDK的安装目录:
JAVA_HOME
,配置的是JDK的安装根目录(此处使用的自定义的JDK安装路径,不是默认安装路径)
-
类路径:
CLASSPASH
,配置的是安装根目录下面的lib
目录下面的dt.jar
和toos.jar
(需要先配置“.
”)
-
编译工具路径:
PATH
,配置的是java安装根路径下的bin
目录(此处的%JAVA_HOME
指的是前面JDK的安装目录)
-
2.3 JDK的包
db
:全世界最小的数据库Derbydemo
:有关样例,不影响使用sample
:有关样例,不影响使用bin
:Java的JDK编译命令(程序员使用的命令)include
:JDK与底层操作系统(Windows)打交道的东西(Java底层自己使用的)lib
:Java在操作系统 (Windows)上要执行的类库(Java底层自己使用的)jre
:运行时环境,只运行Java可以只使用它src.zip
:Java源码压缩包
2.4 常用的cmd命令
cmd
:打开命令提示符java
:执行命令- 如:java MyClass(MyClass是Java程序的类名)
javac
:编译命令- 如:javac MyClass.java (MyClass.java是源代码文件)
javadoc
:查看Java文档- 如:javadoc MyClass.java (MyClass.java是源代码文件,会生成相应的文档)
cd..
:返回上一层文件夹cd
:进入下一层文件夹d
:进入盘符d
2.5 JVM、JRE、SDK
- JVM:Java虚拟机(Java Virtual Machine),包括类加载器、字节码校验器、Java解释器
- JRE:运行时环境,包括JVM和Java运行支持的类库
- Java SDK:Java Software develop kit,Java的另一个称呼
3. IDE和Eclipse
3.1 IDE
- IDE:
Integerted Development Environment
,集成开发环境
- IDE包括代码编辑器、编译器、调试器和图形用户界面工具
- IDE集成了代码编写功能、分析功能、编译功能、调试功能等一体化 的开发软件套
- 任何语言都要经过编辑、编译和调试
- 常用的IDE工具:IDEA、Jbuilder、NetBeans、Eclipse
3.2 Eclipse
源于IBM的优秀的开源的可扩展的插件化的IDE工具
-
工作空间(工作区)
-
本质是个文件夹,Java所写的所有项目都在里面
-
可以有不同的工作区,里面放不同的项目
-
运行Eclipse的基本单位
-
Eclipse配置信息基于工作空间,
%eclipseHome&\configuration\.setting
设置工作空间基本信息
-
-
源文件夹和目标文件夹
- JVM识别的是目标文件夹,将源文件夹里的文件进行编译后,产生的
.class文件
就会自动进入目标文件夹 - 目标文件夹的结构会根据源文件的结构而一摸一样的创建
- 保存就编译,源文件只有在源文件夹里才会被编译
- 分类
源文件夹
(src
):放源文件的(.java文件
)目标文件夹
(bin或classes
):放目标文件的(.class文件
),默认为bin
- JVM识别的是目标文件夹,将源文件夹里的文件进行编译后,产生的
-
包
- Eclipse中先建源文件夹,再建包,再建类,如果没有包,则是默认包,会出现问题的
- 所有的包都以
com
开头,用.
隔开 ,不能个中文 - 包本质就是一级又一级的文件夹
-
快捷键
- Crtl + S :保存 + 编译(Eclipse中保存就编译)
- Alt + ?快捷键:自动生成
-
保存项目的三种方式
- 备份工作空间
- 优点:保存配置信息
- 缺点:文件过大
- 备份项目
- 只备份源文件
- 优点:文件最小
- 备份工作空间
4. 基础语法
4.1 常量与变量
除
作用域
、常量
与C语言不同外,其余一摸一样,可看 C语言基础 中的6. 次小编程单元(数据类型)
java只有一般常量,无符号常量。
-
常量
Java中只有一般常量,无符号常量,可以用
finally
修饰变量,来达到符号常量的用处public finally int NUMBER = 10;
- 什么是常量?
- 常量:在程序中值不能改变的量
- 常量分类
- 一般常量:不能变的常量
- 数值常量,如:10
- 字符常量,如:‘a’
- 字符串常量,如:“OK”
- 什么是常量?
-
变量
除
作用域
外都与C语言一摸一样,但是C语言中的变量必须先声明在使用,且变量的声明语句必须在函数的开始处。- 什么是变量?
- 在程序中,其值可以改变的量
- 变量的本质是一片连续的内存空间
- 变量是一个最小的、离散的、不带结构的容器,用来存放数据
- 变量的使用
- 变量必须先声明后使用
- 方式:
- 方式一:声明语句:
类型 变量名;
执行语句:变量名 = 初始值;
int a; a = 10;
- 方式二:声明初始化语句:
类型 变量名 = 初始化;
int a = 10
- 方式一:声明语句:
- 变量的命名规则
- 变量必须由
数字
、字母
、下划线(_)
组成,且字母
、下划线
开头 - 变量必须见名之意,且首字母小写
- 变量不能和关键字重名,且大小写敏感
- 变量必须由
- 变量作用域 :从定义的地方开始一直到所在的块的结束(与C语言不一样)
- 什么是变量?
4.2 数据类型
除
分类
与C语言不同外,其余一摸一样,可看 C语言基础 中的7.最小的编程单元
C语言中的数据类型 +byte
类型 +boolean
类型 = Java的基本数据类型
- 什么是数据类型?
-
什么样的数据就用什么样的类型来存
-
约束内存空间的大小
-
数据类型代替运算法则
例如:10+10=20“10” + “10” = 1010
-
- 数据类型的分类
- 基本数据类型
- 数值型
- 整数类型:
byte
、int
、short
、long
- 浮点类型:
float
、double
- 整数类型:
- 字符型:
char
- 字符数据存储的时候是把其
ASCII码
存储在内存中 - ‘a’ ~ ‘z’:97 ~ 122
- ‘A’ ~ ‘Z’:65 ~ 90
- ‘0’ ~ ‘9’:48 ~ 57
- 字符数据存储的时候是把其
- 布尔型:
boolean
,true
为真、false
为假
- 数值型
- 引用数据类型:类(
class
)、接口(interface
)、数组- 引用数据类型在使用时传递的是
地址
- 引用数据类型在使用时传递的是
- 基本数据类型
- 类型转换
与C语言一摸一样,但是java中一般用于引用数据类型的转换,同时对象类型的不能自动类型转换- 自动类型转换
- 原则:把表示范围小的类型转换成表示范围大的类型的值
short -> int -> long -> float -> double
- 强制类型转换
- 原则:把表示范围大的类型转换成表示范围小的类型的值
- 语法:类型名 变量或数值
int a = 1; double b = 1.5; int c = (int)b + a;
- 强转类型转换可能出现的问题:丢失精度、乱码、一运行就死机
- 自动类型转换
4.3 运算符
与C语言一摸一样,可看 C语言基础 中的
11.运算符
- 表达式
- 表达式 = 操作数 + 运算符
- 操作数:由常量、变量和子表达式组成
- 由什么样的运算符组成的表达式就叫什么表达式
- 分类
与java一样,但java返回值是ture和false- 算数运算符
- 分类
-
一元运算符:由一个操作数组成,如:“
++
”、“--
” -
二元运算符:由两个操作数组成,如: “
+
”、“-
”、“*
”、“/
”、“%
”"/":当除号两边都是整型时,结果是整型的商; 当除号两边有一个实型或者两个都是实型,结果是精确的值;"%":两边必须是整型
-
- 运算符前缀:操作数在后面(先操作后使用),如:++a
- 运算符后缀:操作数在前面(先使用后操作),如:a++
- 分类
- 赋值运算符
-=
、+=
、-=
、*=
、/=
、%=
- 关系运算符(比较运算符)
<
、<=
、!=
、>
、>=
、==
- 返回值是
true,false
(false
代表假,true
代表真)
- 逻辑运算符
- 用于连接一个或多个条件,判断是否成立
- 分类:
- 与(
&&
):一假即假 - 或(
||
):一真即真 - 非(
!
):取反
- 与(
- 算数运算符
- 运算符优先级
- 从左到右依次降低:
()
-->!,++,--,sizeof
-->*,/,%
-->+,-
--><,<=,>,>=
-->==,!=
-->&&
-->||
-->=,+=,*=,/=,%=,-=
- 从左到右依次降低:
4.4 结构化(顺序、分支、循环)
与C语言一摸一样,可看 C语言基础 中的
10.结构化
-
顺序结构
-
分支选择结构
- 单分支结构
- 简单单分支
if() {}
- 标准单分支
if() {} else {}
- 简单单分支
- 多分支结构
-
否定式:否定了第一个条件,才能进入下一个条件
switch能做的多重if都能做,但是多重if能做的,switch不一定能做,因为多重if可以处理连续的范围,但是switch只能处理离散得到的点。
- 多重
if
:相当于第一次成立的单选if () {} else if () {} else {}
switch
结构:
注意事项:switch() {case 1:111;break;case 2:222;break;default: 000;break; }
- 只能处理离散的点,不能处理连续的范围
case
的顺序可以调换,但是case
不可以重复break
可以不写,语法不会报错,但是逻辑有问题default
可以省略,相当于多重if
里面没有最后的else
switch
的参数必须是整型的,可以是常量、变量、表达式case
后面的数必须是整型常量
- 多重
-
肯定式(递进式):肯定了第一个条件,才能进入下一个条件,相当于第一次不成立的单选
if() {if() {if() {} else {}} else {} } else {}
-
- 单分支结构
-
循环结构
- 循环分类
- 当型循环
- 先判断后执行
- 分类:
while
循环:一般用在未知次数的当型循环while() {}
for
循环:一般用于已知次数的当型循环for(表达式1,表达式2,表达式3) {}
- 直到型循环
do...while
循环:先执行后判断do {} while();
- 当型循环
- 循环分类
4.5 数组
- 数组概念
- 什么是数组?
- 一堆数据放在一起简称
数组
- 数组是一段连续的有序的并存储类型相同的单元
- 数组是一个带结构的大容器,用来存放小容器
- 一堆数据放在一起简称
- 什么是变量?
- 值可以改变的量称为
变量
- 变量的本质是一段连续的内存单元
- 变量是一个离散的容器,用来存储数据
- 值可以改变的量称为
- 什么是数组?
- 一维数组
- 数组大小
- 在声明语句结束之前,存储大小必须确定
- 数组大小是存储整型常量
- 数组下标(索引)
- 下标从
0
开始,一直到存储大小减1
,且中间是连续的 - 数组下标可以是整型常量、变量、表达式
- 下标从
- 命名方式
- 静态初始化(声明初始化)
- 方式一:
int 数组名[10] = {1,2,3,4}
- 方法二:
int 数组名[4] = {1,2,3,4}
- 方法三:
int 数组名[] = {1,2,3,4,}
- 方式一:
- 动态初始化(先声明,后赋值)
int 数组名[10]
;数组名[0]= 1
;数组名[1]=2
- 静态初始化(声明初始化)
- 数组大小
- 二维数组
- 定义
- 把一个数组套在另一个数组里面,且每一层的结构相同,类型也相同
- 可以理解为行列矩阵,前面的
[]
代表行,后面的[]
代表列
- 命名方式
- 静态初始化:
类型名 数组名[size][size]={{},{}}
(第一个size
可以不用写,推荐写上) - 动态初始化:一般使用
for
循环嵌套进行初始化
- 静态初始化:
- 定义
4.6 冒泡排序
-
定义:每一轮把要比的数据的最大的数据冒到最后面
-
外层循环
- 外层循环比的是排序的次数
- 外层循环每进行一次,数组中要比的数据的最大值就被确定了
- 外层循环次数一般为
arr.length - 1
(数组长度 - 1
)
-
内层循环
- 内层循环比的是未确定元素比较的次数
- 内层循环负责不确定数据的比较和位置是否交换
- 内层循环次数一般为
arr.length - i - 1
(数组长度 - 外层循环已经排序的次数 - 1
)
-
代码示例:
public static void main(String[] args) {int[] num = {10, 25, 65, 45, 9};//冒泡排序for(int i = 0; i < num.length - 1; i++) {for(int j = 0; j < num.length - i - 1; j++) {if(num[j] > num[j + 1]) {int temp = num[j];num[j] = num[j + 1];num[j + 1] = temp;}}}for(int i = 0; i < num.length; i++) {System.out.print(num[i] + " ");} }
4.7 二分查找
-
前提:二分查找前提数据必须是
有序
的 -
定义:二分查找是将待查找的元素与数组中间位置的元素进行比较,从而确定待查找元素存在的位置间,然后在区间中继续进行查找,直到找到目标元素或确定目标元素不存在为止
-
索引
- 开始索引:
start = 0;
- 结束索引:
end = arr.length - 1;
- 中间索引:
middle = (start + end)/ 2;
- 开始索引:
-
循环退出条件为:
start > end
(开始索引大于结束索引
) -
代码示例:
public static void main(String[] args) {// 二分查找//排序int[] arr = {12, 45, 56, 48, 89, 456, 48, 56, 25, 2};for(int i = 0; i < arr.length - 1; i++) {for(int j = 0; j < arr.length - i - 1; j++) {if(arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}//查找int num = 0;int start, end, middle;start = 0;end = arr.length - 1;while(true) {middle = (start + end) / 2;if(arr[middle] > num) {end = middle - 1;} else if(arr[middle] < num) {start = middle + 1;} else if(arr[middle] == num) {System.out.println("找到了,该数是" + arr[middle] + ",位置是" + (middle + 1));break;}if(start > end) {System.out.println("很抱歉,该数不存在!");break;}} }
5. 类和对象
5.1 面向对象(OOP
)
- 什么是面向对象?
OOP
:Object Oriented Programming,面向对象编程- 面向对象不是技术,而是一个主流的先进的编程思想
- 面向对象关注的是
做事情的主体
(这些主体就是对象
),即谁在做
- 面向对象宗旨
- 用计算机中的对象(实体)来模拟现实世界中的对象(实体)
- 能够用计算机中实体模拟现实世界中的实体
Everything is object!
- 在
OOP
中现实世界的所有东西都被视为对象
(类
) - 面向对象世界是主观模型(主观世界)
- 面向对象世界是人们抽象过得认识到以后得概念模型组成的
- 在
5.2 类和对象
- 如何描述事物?
- 现实生活中的任何事物,我们都可以用属性(状态)(特征)和方法(行为)(功能)来描述它
- 属性 - 方法
- 特性 - 功能
- 状态 - 行为
- 现实生活中的任何事物,我们都可以用属性(状态)(特征)和方法(行为)(功能)来描述它
- 属性和方法
- 什么是属性?
- 属性是事物(类)(对象)本身的特征,用来描述它的状态
- 什么是方法?
- 方法(行为)用来说明这个事物(类)(对象)所具有的功能
- 什么是属性?
- 什么是对象(类)?
类
:具有相同属性和方法的一组对象(实例)(个体)的集合
- 什么是对象(实例)?
实例
:符合某一类标准的事物的具体的一个个体
- 类和实例的关系
- 类是主观模型,实例是客观模型
- 类是抽象的,实例是具体的
- 类是模糊的,实例是清晰的
- 类是个模子模型,实例是在这个模子里面诞生的个体
OOP
世界是由类组成,而真真正正存在的世界由实例组成
- 类与面向对象编程的关系
- 类是现实世界的实体(实体类)
- 类是面向对象编程的核心
- 类可以完成特定的任务
- 类是现代应用程序的基础
5.3 类的实现
-
类的语法
- 语法:
class 类名 { 类体 }
public class Student{}
- 语法:
-
类的命名规则
和变量的命名规则类似,但是变量的首字母小写
- 必须由
数字
、字母
和下划线
组成,并且下划线
、字母
开头 - 必须见名知意,且首字母大写
- 不能和关键字重名,且大小写敏感
- 必须由
-
类的定义
-
属性
属性
是事物本身的特征,用来描述它的功能- 如何实现类里面的属性?
- 类中的属性就由写在类体里的变量来充当
- 属性名称被所有类的实例所共享
- 属性的命名规则
- 必须由
数字
、字母
和下划线
组成,并且下划线
、字母
开头 - 必须见名知意,且首字母小写
- 不能和关键字重名,且大小写敏感
class Person {String name = null;int age = -1;String sex = null; }
- 必须由
-
方法
与C语言中的函数一样
-
方法
是事物本身所具有的功能 -
如何实现类里面的方法?
- 类中的方法就是写在类体里面的函数
-
方法的语法:
返回值类型 方法名(参数){ 方法体 }
void eat() {System.out.println("吃东西"); }
-
-
-
类的使用
- 使用:先让该类
开空间
(new
),然后用实例名
(空间的别名
)+“.
”调用类里面的属性方法Student student = new Student(); student.study();//调用学生学习方法 System.out.println(student.name + student.age);//输出学生名字和年龄
new
关键字new
就是开空间
- 定义完一个类后,无论类里代码的多少,
new
之前都不开空间 - 见到了
new
就开了空间,没见new
就没开空间 - 在运行过程中,没有
new
,会报错空指针异常
- 点操作符
- "
.
" 操作符 - "
.
"的前面是实例的名字
,“.
”的后面是类里面的属性
和方法
类的实例
+“.
”操作符 可以调用类里面的属性和方法
- "
- 使用:先让该类
5.4 构造方法
- 什么是构造方法?
构造方法
:方法的名字和类名完全相同且没有返回值类型public class Student() {public String name;//空参构造Student() {方法体}//实参构造Student(String name) {this.name = name;方法体} }
- 构造方法的作用
- 语法作用
- 让类能够new(开辟空间)
- 业务逻辑作用
- 在开辟空间的过程中想做的事情都写在构造方法中
- 一般来说是
初始化
的作用
- 语法作用
- 构造方法的调用时机
- 构造方法也是方法,符合方法的调用规则:
见到名字就调用
new
的时候自动调用
,传什么样的参数就调什么样的构造方法
- 构造方法也是方法,符合方法的调用规则:
- 构造方法的分类
- 不带参的构造方法(
隐式构造方法
)- 只能有一个
- 当一个类里面没有任何的构造方法,系统会提供个默认的
空参空方法体
的构造方法,只能用来进行语法作用
- 带参的构造方法(
显式构造方法
)(逻辑构造方法
)- 可以有好多个,理论上可以有200多个
- 参数的个数和类型不同就是不同的构造方法
- 当一个类里面有构造方法,写了谁就是谁,没写的构造方法就没有,系统不再提供默认的空参方法
- 不带参的构造方法(
6. 包和封装
6.1 命令行参数
- 命令行参数(启动时参数)(运行时参数):
String[] args
- 在程序运行的过程中,可以通过传递命令行参数,将信息传递至
main()
方法中 - 启动虚拟机(启动
main()
方法)的时候要添加参数时使用的 - 如:有一个Java程序,可以接收两个整数作为命令行参数,并将它们相加并输出结果
使用命令行工具运行这个程序:将整数public class CommandArgsExample {public static void main(String[] args) {if (args.length < 2) {System.out.println("请输入两个整数作为参数");return;}int num1 = Integer.parseInt(args[0]);int num2 = Integer.parseInt(args[1]);int sum = num1 + num2;System.out.println("相加结果为: " + sum);//30} }
10
和20
作为参数传递给main()
方法java CommandArgsExample 10 20
6.2 包(package
)和导包import
-
什么是个包(java里面的包)?
包的本质
是在硬盘上的文件夹
,具有文件夹的功能- 包是Java命名机制的一部分
- 包名是类名的一部分
- 虚拟机在找类时,找的是
包名 + 类名
- 例如:com.itheima.test1.Person,其中
com.itheima.test1
是包名
,Person
是类名
- 例如:com.itheima.test1.Person,其中
- 导包
- 该类需要用到不同包下面的类时,这个类在能够看到(
访问修饰符允许访问
)的情况下,必须要导包 - 导包关键字:
import
import com.itheima.test1.Person;
- 完整路径名
public com.itheima.test1.Person p1 = null;
- 该类需要用到不同包下面的类时,这个类在能够看到(
- 包是封装机制的一部分
封装
=包
+访问修饰符
- 封装必须用
访问修饰符
和包
联合起来作用的
-
package
关键字- 定义包使用
package
包路径package com.likou.text;
- 定义包使用
-
import
关键字- 导入包使用
import
包路径 - 使用
异包
下面的类都要导包import java.util.ArrayList;
- 导入包使用
-
包的命名
- 公司网络域名的反转,如:淘宝的数据库:com.taobao.db
6.3 Static
静态修饰符
static方法
或static代码块
是属于类
级别的,而不是具体对象的实例级别。因此在静态方法或静态代码块中是不能使用this关键字的
,因为this
代表当前对象的引用,而静态方法或静态代码块是在类加载时
就被调用,此时还没有具体的对象实例存在
-
成员化
- 成员属性
- 成员方法
- 只有
new出来
(开辟空间),才能使用成员属性
和成员方法
-
static
- 可以修饰
属性
,也可以修饰方法
,但是不能修饰类
public class Student{public static String name;public static void print() {System.out.println(name);} }
实例名
也可以调用静态修饰的东西,但是一般都是直接类名 + “.”
调用public static void do() {Student student = new Student();student.print();//实例名调用Student.print();//类名调用 }
- 可以修饰
-
静态属性(类属性)
static修饰符
修饰的属性就叫静态属性
public static String name = null;
- 静态属性和成员属性的区别
成员属性
必须new
(开空间)后才能使用静态属性
不new就可以使用,即类名 + “.“
就可以使用- 保存类的信息,一般就放在
static
里面
-
静态方法
static修饰符
修饰的方法就叫静态方法
-
为什么 类名 +
“.”
就可以用?- 因为成员化的东西必须
new
完开空间了后才能使用,而静态的东西,只要类编译器通过,那么静态的东西就开空间了(静态池中) 成员化的东西
自己调用自己,自己改变,别人访问的值不会改变;静态修饰的东西
强调只有一份,共享一片空间
,自己改变,别人访问的值也随之改变静态池
- Java虚拟机中存在好多池(大概8.9个),静态池是其中一种
静态池
是Java虚拟机启动就存在
- 因为成员化的东西必须
6.4 封装
- 封装
- 有选择的提供数据叫做
封装
(记这个) - 隐藏事物内部的实现细节叫做
封装
- 特征
对象
的特征:属性
、方法
面向对象
特征:封装
、继承
、多态
- 封装的优点
- 内部的修改不影响外部的使用
- 防止外界误修改程序单元
- 对象使用变得更简单
- 有选择的提供数据叫做
- 访问修饰符
- 作用:限制的是访问的作用(范围)
- 分类
private
:私有的- 只有本类内部可以访问
默认(default)
- 本类可以访问
- 同包下的类可以访问
protected
:受保护的- 本类可以访问
- 同包下的类可以访问
- 异包下的子类可以访问
public
:公共的- 本类可以访问
- 同包下的类可以访问
- 异包下的子类可以访问
- 异包下的非子类可以访问
- 访问属性和方法的两种场景
- 本类的内部访问
- 不需要
new
,直接用
- 不需要
- 本类的外部访问
- 将该类
new出来
(开辟空间),然后实例名+“.”
访问该类里面的属性和方法
- 将该类
- 本类的内部访问
- 实体类
- 现实生活里的物体对应的是
实体
,实体在数据库里是表
,在Java里对应类
,Java里面的类对应程序设计里面的表,这样的类称为实体类
- 实体类的名字和表的名字一摸一样,不过要
首字母大写
- 表里面有什么样的字段,实体类里面就得有什么的属性,名字与数据类型一摸一样,但是访问修饰符必须是
私有的
(private
)
- 现实生活里的物体对应的是
- 访问器
set
方法- 给私有属性赋值
public void setId_stu (String id_stu){this.id_stu = id_stu; }
- 给私有属性赋值
get
方法- 获取私有属性的值
public String getId_stu (){return this.id_stu; }
- 获取私有属性的值
7. 方法重写和继承
7.1 继承
- 继承
继承
(派生):在一个类的基础上生成一个新类子类
:新生成的类父类
(超类):原来的那个类
- 继承的优点
- 提高代码的复用性
- 父类的属性方法可以用于子类
- 可以轻松定义子类
- 使设计应用程序变得简单
7.2 继承语法要点
- Java中由
extends
关键字来实现public class Son extends Father {}
- 父子关系是
相对
的- 在Java里,只能认识相邻的两代
- 父类就代表了祖先,继承了所有祖先类的属性和方法
- 父类只能认识子类,子类只能认识父类
- 继承关系由“
子类是父类
”确定- 例如:老虎是动物(老虎是子类,动物是父类)
- Java中只有
单继承
- 一个儿子只有一个父亲
- 一个父亲可以有多个儿子
- 好处:Java的子类不管经过多少代,都可以追根溯源
7.3 方法重写(overwrite
)
- 子类可以拥有父类的属性和方法(私有的暂时无法使用)
- 子类可以有自己的属性和方法
- 子类可以
重写
覆盖父类的方法- 属性也可以被重写(覆盖),但是重写属性没有意义
- 方法重写(
overwrite
):子类的访问修饰符的访问要大于父类的方法重写
:在子类里面有一个和父类方法原型部分完全相同的方法,但是有不同的方法体方法原型
:返回值类型
+方法名
+参数
,与修饰符无关- 方法重写后,外部调用就分不清子类和父类了,无论怎么调用,调用的都是子类
- 方法重写后,内部调用依旧可以分清子类和父类,用
this
调用子类,用super
调用父类public class Father{public void run() {System.out.println("我每天要跑10公里");} } public class Son extends Father{@overwritepublic void run() {System.out.println("我每天要跑5公里");} }
- 可以声明父类,创建子类(
用父类装载子类
)- 父类能出现的地方,子类一定能出来(
装载
)(向上转型
)- 子类转化成父类:
new的是子类,接的是父类类型
父类:Father;子类:Son Father f = new Son();
- 子类转化成父类:
- 特点:
- 声明什么类型(拿什么类型的实例名),就只能调用什么类型的属性和方法
父类:Father;子类:Son Father f = new Son();
- 声明的是
Father类型
,将子类类型的Son
转化成了父类类型的Father
,开的是Son的空间
,里面存放的是Son的属性和方法
,但是拿Father类型
来接的,因此只能调用Father类型
里面的属性和方法。
- 声明的是
- 创建什么类型的空间,就真正运行的什么类型的方法(前提要
方法重写+装载
)- 没有重写时,父类就是父类的方法,子类就是子类的方法
- 方法
重写+重载
后,调用的是父类的方法,完成的是子类的功能 - 子类重写父类方法时,当创建了
子类的空间
,却拿了父类的类型
去接(装载
),当调用父类中被子类重写的方法时,调用的是父类方法的原型
,但是实现的是子类重写方法
的功能。(因为子类重写的方法已经将父类的方法覆盖
了)父类:Father 父类方法:method( ); 子类:Son 子类方法:method( );(重写父类的method)Father f = new Son();
- 当调用
father类型
里面的method方法
时,调用的是父类的方法,实现的却是子类method
的功能,因为子类的method方法
的已经将父类的method
方法覆盖
了。
- 当调用
- 创建的什么类型的空间,就可以强转为什么类型(
强转
)(向下转型
)- 父类转化成子类
父类:Father;子类:Son Father f = new Son(); Son s = (Son)f;
- 在这段代码中,
Father
是Son
的父类,且将一个Son类型
的实例(空间)赋给了Father类型
的f
。因此,可以将Father类型
的f
强制转换为Son类型
的实例 s
,因为s本类
就是Son类型
的空间,只不过之前是拿Father类型
接受的。
- 在这段代码中,
- 父类转化成子类
- 声明什么类型(拿什么类型的实例名),就只能调用什么类型的属性和方法
- 父类能出现的地方,子类一定能出来(
7.4 属性和方法的调用方式
- 当自己的属性名和父类一样时,不加
this
或者super
时,编译器会优先调用自己
的 - 内部调用
- 调用属性
this
:调用本类
属性super
:调用父类
属性什么都不加
:表示传递的参数
- 调用方法
this
:调用本类
方法super
:调用父类
方法
- 调用属性
- 外部调用
实例名 + “ . ”
(但是看不出来哪些是父类的哪些是自己的)- 当
方法重写
后,实例名 + “ . ”
只能调用子类
的
7.5 this
和super
this
this
:指向当前对象的引用
this
表明调用本类声明的属性和方法this
使用在构造方法中表示调用本类其它构造方法
super
super
:指向当前对象的父引用
super
表示调用父类声明的属性和方法
7.6 继承中的构造方法
- 情况一
- 当调用子类的构造方法,诞生自己的空间之前,父类的空间也已经诞生了
- 当调用子类时,传什么样的参数,调用什么样的构造器
- 无论怎样构造子类的空间,都会以默认的方法(
super()
)构造父类public class Animal {public Animal() {System.out.println("父类Animal的构造方法被调用");} }public class Dog extends Animal {public Dog() {System.out.println("子类Dog的无参构造方法被调用");}public Dog(String name) {System.out.println("子类Dog的带参构造方法被调用,参数为:" + name);} }public class Main {public static void main(String[] args) {// 调用子类Dog的无参构造方法Dog dog1 = new Dog();// 输出:// 父类Animal的构造方法被调用// 子类Dog的无参构造方法被调用System.out.println("------------------");// 调用子类Dog的带参构造方法Dog dog2 = new Dog("旺财");// 输出:// 父类Animal的构造方法被调用// 子类Dog的带参构造方法被调用,参数为:旺财} }
- 情况二
- 当子类在构造自己时,需要以某一种
显示的方法
构造父类,就需在子类构造器的第一句按照子类的意愿去调用父类的构造器。class Parent {private int num;public Parent(int num) {this.num = num;System.out.println("Parent: " + this.num);} }class Child extends Parent {private String name;public Child(int num, String name) {super(num); // 调用父类的构造方法this.name = name;System.out.println("Child: " + this.name);} }public class Main {public static void main(String[] args) {Child child = new Child(10, "Alice");} }
- 当子类在构造自己时,需要以某一种
- 情况三
- 如果父类
没有隐式构造器
,这时必须在子类每一个构造器的第一句都显示的调用父类有的构造器
class Animal {private String name;public Animal(String name) {this.name = name;} }class Dog extends Animal {private String breed;public Dog(String name, String breed) {super(name);//显示的调用父类有的构造器this.breed = breed;} }class Cat extends Animal {private String color;public Cat(String name, String color) {super(name);//显示的调用父类有的构造器this.color = color;} }
- 如果父类
8. 方法重载和多态
8.1 方法重载
-
方法重载
(overload
):一个类里面有若干个名字相同而参数(类型或者个数)不同的方法 -
要点
-
与参数名无关
public void T1(int a){} public void T1(int b){return "方法重载"; }
-
与返回值类型无关
public void T1(int a){} public String T1(double b){return "方法重载"; }
-
与修饰符无关
public void T1(int a){} private void T1(double b){}
-
父子类方法也可以重载
public class Person{public void T1(int a){} }public class Student extends Person{ public void T1(double b){ } }
-
-
构造方法是一种特殊的方法重载
public class Student{private String name;private int age;private String gender;public Student() {}public Student(String name) {this.name = name;}public Student(String name,int age) {this.name = name;this.age = age;}public Student(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;} }
8.2 多态
- 多态可以理解为“
多种形态
”的意思 多态
:同一个物体(实例)在不同的情况下,表现出来的不同的状态- 多态在代码中表现为调用同样的方法,每次调用条件不一样,所实现的功能就不一样
- 多态一般用
方法重载
和方法重写
来实现的方法重载
:在一个类中将其它类
表示成方法的参数
,在使用时当传递不同的参数(对象)就会调用该类中对应的方法。类1:class T 类2:class A; 类3:class B; 类4:class C; 类5:class Run;类1中的方法:方法1:public void method() { };方法2:public void method(A a){ };方法3:public void method(B b){ };方法4:public void method(C c){ }; 类5中的代码:T t = new T();t.method(new A());//调用的是类1中的方法2
方法重写
:一般让父类装载子类
(创建子类的空间,用父类来接
),调用父类的方法,实现子类重写后的功能父类:class H2o; 子类1:class GuTaiShui extends H2o; 子类2:class Water extends H2o; 子类3:class YeTaiShui extends H2o;父类方法:public void showType() { }; 子类1方法:public void showType() { }; 子类2方法:public void showType() { }; 子类3方法:public void showType() { };测试类:class Run; 测试类代码:H2o h2o = null;h2o = new Water();h2o.showType();//调用的是子类2中的方法
8.3 Static
和Final
修饰符
static
修饰符- 静态属性
static
修饰的属性叫做静态属性
- 静态方法
static
修饰的方法叫做静态方法
- 任何方法都不开空间
- 特性
- 特性一:静态方法跟类绑定,可以用
类名 + “ . ”
直接调用,不需要new
出来(类编译通过了,static方法就有了,但是不开空间
) - 特性二:静态方法只能调用静态成员,不能调用非静态成员(在调用静态方法时,非静态的东西还不存在)
- 特性三:在静态方法中,
不能有this和super
(this和super是成员化的东西,指的是单独的,而static是和类绑定的,指的是共有的)
- 特性一:静态方法跟类绑定,可以用
static
静态强调只有一份static
只能修饰属性和方法
,不能修饰变量和类
- 静态属性
final
修饰符final
强调最终的,不可改变
的- 特点
- 特点一:
final
修饰变量,变量就变成常量
(C语言中的符号常量在Java中用final
充当)public final int NUMBER = 10;
- 特点二:
final
修饰方法,方法就不能被重写
- 特点三:
final
修饰类,类就不能被继承
(一旦类被final
修饰,里面的所有方法
都会变成隐式的final方法
)
- 特点一:
- static和final联合修饰属性
- 因为属性是
final
,所以加个static
- 因为属性是
final
,不能变的值,如果让每一个实例都来一遍这个值,又不能变,就会浪费空间,所以加个static
,让所有的实例都共享这一片空间
public static final int NUMBER = 10;
- 因为属性是
8.4 程序的内存分配
程序的本质
:正在运行的一片内存空间
8.4.1 C语言开空间
- C语言程序直接向
硬盘
要空间 - 通过
指针
开空间
8.4.2 Java开空间
- Java程序问
JVM
(虚拟机)要空间,JVM再问硬盘
要空间 - JVM开空间的过程:只要虚拟机一启动,不管运行没运行,JVM都会向硬盘请求一部分空间(
初始空间
)初始内存分配
:当程序准备执行时,虚拟机会向操作系统申请分配内存,操作系统会返回内存(初始内存)给JVM,然后JVM会在初始内存中分配内存给程序
分配更多内存
:当初始内存用完时,程序向JVM请求需要更多的内存,JVM向操作系统申请内存,操作系统分配内存返回给JVM,JVM会在初始内存+后续分配的内存中进行内存分配后,返回给程序
分配内存达到上限
:程序接着向JVM请求需要更多内存,JVM发现内存已经达到设置的上限(JVM默认为64M),不再向操作系统请求,而是直接通知内存溢出,返回给程序
8.4.3 谁在占用空间?
- 类的结构
方法区
在JVM启动时就有了- 类的结构定义时就在方法区开空间。(并没有单独给类里面的属性和方法开空间,是给
类的整体
开的空间,相当于是个模子) - 如果类里面也有静态成员,静态成员也在
静态池
中开空间。(静态池位于方法区中
) - 所有的方法都
只开一份空间
(在方法区中),所有的成员共享
方法区里该方法的模子
- 变量的名字(实例名、基本数据类型变量名、引用数据类型变量名)
- 变量的名字在
栈
里面开空间
- 变量的名字在
- new出来的对象(
实例
)- new的东西在
堆
里面开空间
- new的东西在
8.5 引用
- 在Java中,
引用
是指一个对象的内存地址,它可以用来访问对象的属性和方法 - 在Java中,所有的对象都是通过引用来操作的
- 当你创建一个对象时,实际上是在
内存中分配了一块空间
,并返回了该空间的引用
。这个引用可以被赋值给一个变量,然后通过该变量来操作对象 - 在Java中,
引用
和对象
是分开的,对象(new出来的)存在于堆内存中
,而引用则存在于栈内存中
9. 抽象和接口
9.1 传递和交换
9.1.1 传递
值传递
:拷贝的是值
- 值传递:直接在
栈
里面值的拷贝,改变新的值,旧的值不变(方法里面变了,方法外面不变) - 基本数据类型:直接在栈里面拷贝值,做不到方法里面改变,外面值就改变(因为在栈里面做的是
值的拷贝
)
- 值传递:直接在
引用传递
:拷贝的是栈
里面的引用
- 传递是让栈里面的
多个引用指向了堆里面同一片空间
(方法里面的引用指向的空间改了,方法外面的引用指向的空间也改了) - 在栈里面拷贝的是
引用的地址
,这些地址指向堆里面同一片空间
,通过改变栈里面新的引用指向的空间,也能改变原来的那片空间 - 数组
public class Text1 {public void method(int[] arr){for(int i = 0; i < arr.length; i++) {arr[i]++;}} } public class Run {public static void main(String[] args) {int[] num = {1, 2, 3};Test1 text = new Test1();text.method(num);for(int i = 0; i < num.length; i++) {System.out.print(num[i] + " ");//2,3,4}} }
- 在测试类中定义了个数组
num
;将num
传递给方法method
的参数arr
,使得arr的地址=num的地址
; - 在
method
方法中将arr
数组遍历并+1
; - 在测试类中遍历输出数组
num
的值发现每个都+1
了。 - 改变了
method
方法里arr
数组的值,测试类中num
数组的值跟着改变。 arr=num
,arr
拷贝的是num的地址
,使得num
和arr
指向同一片空间,改变方法里arr
指向的那一片空间,方法外num
指向的那一片空间也发生了改变
- 在测试类中定义了个数组
- 对象
public class Person{ public String name = null;public int age = -1;public void showMe() {System.out.println(this.name + "//" + this.age);} } public class Text1 { public void method(Person person){person.name = "改变";person.age++;} } public class Run { public static void main(String[] args) {Person p = new Person("張飛", 10);Test1 text = new Test1();text.method( p);p.showMe();//改变,11} }
- 在测试类中创建了个
Person
的对象;将person
对象p
传递给方法method
的参数person
,使得person的地址=p的地址
; - 在
method
方法中将person
所指向空间的名字改为“改变”
,年龄+1
; - 在测试类中调用
Person
类的showMe
方法,发现原来空间名字改为“改变”
,年龄+1
了。 - 改变了
method
方法里person
所指向空间的名字和年龄,测试类中原来的对象p
所指向空间的值跟着改变。 person=p
,person
拷贝的是p的地址
,使得p
和person
指向同一片空间
,改变方法里person
指向的那一片空间,方法外p
指向的那一片空间也发生了改变
- 在测试类中创建了个
- 传递是让栈里面的
- Java在运算的时候,始终在
栈
里面进行操作(Java虚拟机在底层设计时,栈的操作速度快)
9.1.2 交换
- Java的
交换
是在栈
里面将引用
进行交换,引用所指向的空间没有变(方法运行结束,栈里面的空间就被释放) - Java可以通过
引用
去改变
引用所指向空间(堆
)的值引用
是指向该堆内存空间的地址- 当我们通过引用修改对象或数据时,实际上是在修改堆内存空间中的内容
- 这种方式可以实现数据的修改,但不能实现引用的交换。
- Java不能通过
引用
去交换
引用所指向空间(堆
)的值- 在Java中,
引用
是指向对象或数据的地址,而不是对象或数据本身 - 当我们将两个引用进行交换时,实际上是交换了两个引用在
栈中的存储位置
,而不是交换了引用所指向的堆内存空间的内容 - 因此,在Java中,通过交换引用来交换堆内存空间的值是不可行的。
- 在Java中,
9.2 抽象(abstract
)
9.2.1 抽象方法
- 抽象方法:用
abstract
修饰的方法 - 性质
- 抽象方法
不能有方法的实现
(不能有花括号),只能有声明 - 抽象方法只能写在
抽象类
中public abstract void draw();
- 抽象方法
9.2.2 抽象类
- 抽象类:用
abstract
修饰的类 - 性质
- 抽象类里面可以有一般的属性和方法
- 抽象类里面可以有抽象方法,也可以没有抽象方法
- 抽象类不能被实例化(new),只能被继承
- 如果一个类继承了一个抽象类,那么子类就必须要重写抽象类里所有的抽象方法(如果抽象方法没有重写完,那么将子类也变成抽象类)
public abstract class Shape {}
9.2.3 抽象的作用
- 作用:强制父子类之间的语法关系
对于子类
:子类想要继承父类(抽象类),就必须完成父类的中规定的方法对于父类
:有些类是概念性的,不应该被new出来,只能被继承,如:人,人是抽象出现的,概念性的东西,不应该new出来
构造方法、static方法、final方法
不能是抽象的// 定义一个抽象类 public abstract class Shape {// 定义一个抽象方法public abstract void draw(); }// 具体实现类 public class Circle extends Shape {@Overridepublic void draw() {System.out.println("画一个圆形");} }// 测试类 public class Main {public static void main(String[] args) {Shape circle = new Circle();circle.draw();} }
9.3 接口
9.3.1 什么是接口?
- 为什么要有接口?
- Java里面只有
单继承,没有多继承
- Java用
接口来模拟多继承
,达到多继承的效果,使子类的功能更丰富
- Java里面只有
- 什么是接口?
接口
就是用interface
来定义的Java文件(Java对象),类就是class
定义的Java文件- 接口从本质上来讲是个
纯虚的抽象类
- 接口里面可以定义属性,但是所有的属性都是
常量
(隐式的添加了static final
) - 接口里面可以定义
方法
,但是所有方法都是隐式的抽象方法
(只能有方法声明,不能有方法实现)
- 接口里面可以定义属性,但是所有的属性都是
- 接口就是
功能
(接口里面放的一般都是方法)- 面向对象不关心方法的体,只关心方法原型
- 只要在调用时能调到接口里的方法,就表明这个接口有这个功能
9.3.2 接口的使用
-
定义接口(
interface
)public interface StudentService{void add();void delete();void find(); }
-
类可以实现接口(
implements
)- 类实现接口可以
多实现
- 当一个类实现了一个接口,就必须重写这个接口里所有的方法(如果方法没有重写完,那么将该类也变成抽象类)
- 接口
不能实例化
,但是可以声明一个接口让它指向一个实现自己的类 - 可以使用这个实现了接口的类,这时候此类的实例已经具备了接口中的属性和方法
public class doService implements StudentService, TeacherService, WorkService {}
- 类实现接口可以
-
接口可以继承接口(
extends
)- 接口可以
多继承
public interface StudentService extends StudentDao, StudentUtil {}
- 接口可以
9.3.3 面向接口编程
- 面向接口编程就是
面向功能编程
- 实现某个接口所定义的功能
- 在工程开发过程中合理的使用接口可以提高程序的开发效率,使程序更易于维护
- 接口往往用来完成一些功能,可以把它当作半个类使用
9.3.4 接口实现多态
- 接口的
引用
指向实现类的实例
- 拿谁的引用就调谁里面的属性和方法
10. Java.lang包
10.1 API
- 什么是API?
- API:Application Programming Interface,
应用程序编程接口
(入口)
- API:Application Programming Interface,
- 什么是API文档?
- 所有的java类对象的说明书
- 什么是
java包
?- 将API里的对象以功能的不同,分门别类的放到不同的包里面
- 什么是
lang包
?- 利用java进行程序设计的
基础类
(默认类) lang包
- lang包:java的基础包(默认包)
- lang包里有java最基础的类(对象)
- lang包
不需要导包
就可以用
- 利用java进行程序设计的
- 标准包
java.lang
包:包含java最基础的类,可以直接在程序中使用这些类,不需要引入包java.io
包:包含支持输入/输出操作的类java.awt
包:支持java图形化用户界面的类(GUI)java.util
包:包含一系列标准操作以管理数据集、访问日期以及分析字符串的类java.util.zip
包:包含支持生成.jar
文件的类java.sql
包:包含支持使用标准sql的数据库访问功能的类javax.swing
包:(Java扩展包)提供支持GUI组件
的类,推荐使用
10.2 包装类
10.2.1 数据包装类
- java的
不纯洁性
- java不是纯粹的面向对象的语言
- java里有两个地方不面向对象
main()
方法不面向对象- java里的
8个基本数据类型
不面向对象
- 为什么要使用
数据包装类
?- java为了解决8个基本数据类型的不纯粹性,java就给每一个数据类型设计了一个对应的包装类
- 为了把不是对象的基本数据类当做对象来用
void getAge(Object o){ }
10.2.2 分类
- 数值型
- byte(字节):
Byte
- int(整型):
Interger
- long(长整型):
Long
- short(短整型):
Short
- float(浮点型):
Float
- double(双精度):
Double
- byte(字节):
- 非数值型
- char(字符型):
Character
- boolean(布尔型):
Boolean
- char(字符型):
10.2.3 装箱和拆箱
以后所有的
强制类型转换
都用包装类来
实现
double num = 10.01;
Double num1 = new Double(num);
int num2 = num1.intValue();
- 装箱
- 把
基本数据类型
变成对象型
- 方法一:一般用
构造方法
来装箱
包装类型 实例名 = new 包装类型 (基类型数据)
int a1 = 1; Integer num1 = new Integer(a1);
- 方法二:
包装类型 实例名 = 包装类型 . valueOf(基类型)
int a = 10; Interger aa = Interger.valueOf(a);
- 把
- 拆箱
- 把
对象型
变成基本数据类型
- 用
对象名 . 基类型Value()
方法Integer num1 = new Integer(10); int a2 = num1.intValue();
- 把
10.2.4 数值型的常用方法
-
将
基类型
变成包类型
(基类型一般为String):包装类型 实例名 = 包装类型 . valueOf(基类型)
静态方法,返回值是
包类型
Interger aa = Interger.valueOf("10");
-
将
基类型
转换成另一个基类型
:包装类型 . parse基类型(String)
静态方法,返回值是
基类型
int i = Interger.parseInt("10");
-
把
包类型
变String
:实例名 . toString()
返回值是
String
Interger i = new Interger(10); String s = i.toString();
10.2.5 字符型Character
常用方法
都是
静态方法
,返回值是boolean
isDigit()
:确定字符是否为0~9
之间的数字
boolean b1 = Character.isDigit('1');
isLetter()
:确定字符是否为字母
boolean b2 = Character.isLetter('c');
isLowerCase()
:确定字符是否为小写
形式boolean b3 = Character.isLowerCase('c');
isUpperCase()
:确定字符是否为大写
形式boolean b4 = Character.isUpperCase('A');
isSpace()
:确定字符是否为空格
或换行符
boolean b5 = Character.isSpace('\n');
10.3 String类
String类
用final
修饰,不能被继承
10.3.1 字符串面量和对象
- 字符串面量(字面量)(
字符串池
)- 没有
new
的String变量String s = "OK";
- 任何时候创建字符串字面量,系统都会搜素字符串池,查看是否存在该字符串字面量
- 没有
- 对象
- 见到
new
的StringString str = new String("OK");
- 见到
- 区别
- 字符串对象
new
出来的就是堆
里面的空间,然后用栈
里面的引用去引。(每一次new出来的对象所指向的空间都不同
) - 不带
new
的变量在面量池
中开空间(如果两个变量的值相同,那么这两个变量指向面量池中同一片空间
) - 如果引用相等,那么值一定相等;但是值相等,引用却不一定相等
- 如果是面量就是
多个引用指向同一片空间
,如果是对象就是不同的引用指向不同的空间
- 字符串对象
10.3.2 String常用的构造方法
- String():创建一个空字符串
- String(String value):创建一个字符串作为指定字符串的副本
- String(char[] value):根据字符串数组构造一个新字符串
- String(byte[] value):通过转换指定的字节数组新建一个字符串
10.3.3 String常用方法
- 字符串的长度:
字符串 . length()
;int a = "akfhakfn".length();
- 字符串比较
- “
==
”:恒等比的是引用
s1 == s2
equals()
方法:equals比的是具体的值
(大小写敏感)s1.equals(s2)
compareTo()
方法:一样的返回0
,不一样的返回第一个不一样的先后顺序
"abc".compareTo("abc")//返回0 "abc".compareTo("abd")//返回-1
startsWith()
方法:检查是否以该字符串开头"abcd".startsWith("a")
endsWith()
方法:检查是否以该字符串结尾"abcd".endsWith("cd")
- “
- 字符串提取
charAt()
方法:在字符串里面根据索引提取字符
(索引从0
开始)"abcdefg".charAt(0)//返回值是a
substring()
方法:根据索引截取字符串
(前要后不要
)(索引从0开始)"abcdefg".substring(0, 5) // 返回值是abcde
replace()
方法:字符串替换(替换的是字符
)"abcdecfg".replace('c', 'o') //返回值是abodeofg
replaceAll()
方法:字符串替换(替换的是字符串
)"abcdefg".replaceAll("abc", "wxo") //返回值是wxodefg
trim()
方法:去除空格(只去除开头和结尾
的空格)" a b c".trim() //返回值是a b c
concat()
方法:字符串拼接"abc".concat("def")//返回值是abcdef
- 更改字符串的大小写
toUpperCase()
方法:将小写字符转换成大写字符"Hello".toUpperCase()//返回值是HELLO
toLowerCase()
方法:将大写字符转换成小写字符"HELLO".toLowerCase() //返回值是hello
- 字符串分割
split()
方法:字符串根据方法的参数分割开String s5 = "ab,cd,ef"; String[] arr = s5.split(",");
10.3.4 String不变性
String
:不可变的字符序列- String的空间一旦开好了无论发生什么都不变了
- 缺点:效率低、浪费空间
StringBuffer
类- String的对等类
- 表示可增加和可编写字符的可变序列
- 将字符插入到字符串
中间
或附加到字符串末尾
- StringBuffer的常用方法
append()
方法:向StringBuffer里添加
字符串StringBuffer sb = new StringBuffer(“ok“); sb.append("ok");
toString()
方法:将StringBuffer转换
成StringStringBuffer sb = new StringBuffer(“ok”); sb.append("ok"); System.out.println(sb.toString()); //返回值是okok
10.4 Math类
Math类
用final
修饰,不能被继承
- Math常用方法:(静态方法)
double sqrt (double x)
:计算x的平方根double pow (double x, double y)
:计算x的y次方根double ceil( double x)
:求不小于x的最小整数,并以double
的形式显示(进一法)double floor (double x )
:求不大于x的最大整数,并以double
的形式显示(退一法)
10.5 Object类
10.5.1 Object类
- Object类是所有类的父类(老祖宗类)(超类)
- 所有的java类都会拥有Object类里面所有的方法
10.5.2 Object常用方法
-
toString()
方法- 未重写的
toString
显示的是类名+@+类的空间地址
- 重写后的
toString
显示的是重写的内容
,而不是地址
- 未重写的
-
equrse()
方法- 未重写的
equrse()
方法比较的是引用
- 重写的
equrse()
方法比较的是内容
- 重写了
equrse()
方法,就必须重写hashCode()
方法@Override public boolean equals(Object obj) {if(obj == null) {return false;}if(!(obj instanceof Student)) {return false;}Student temp = (Student)obj;if(!(temp.name.equals(this.name))) {return false;}return true; }
instanceof
用于判断obj是不是Student的实例
- 未重写的
-
hashCode()
方法hashCode
:拿地址
根据哈希值进行运算- 未重写的
hashCode
比较的是引用
- 重写后的
hashCode
比较的是内容
-
finalize()
方法(析构方法
)具体请看
Java基础的12.垃圾回收机制
- 如果存在
finalize()
方法,它将在垃圾收集之前被执行一次,而且每个对象仅执行一次,用于执行最终消亡之前的一些操作 - 消亡就调用
finalize()
方法 - 不推荐使用,因为它的执行时机是不确定的,不能保证对象何时会被垃圾回收
public class FinalizeExample {@Overrideprotected void finalize() throws Throwable {try {// 执行清理操作System.out.println("Finalizing object...");} finally {super.finalize();}}public static void main(String[] args) {FinalizeExample obj = new FinalizeExample();// 让对象变成垃圾obj = null;// 强制执行垃圾回收System.gc();} }
- 如果存在
10.6 Class类
Class类
:类的描述器,类的类- Class类包装正在运行类的运行状态及信息,可用来
动态加载类
,得到类的实例
11. 异常
11.1 异常处理
- 定义
- 异常就是不同寻常的事情。
- 一切不寻常的事情都是
异常
(包括错误) - 错误是异常,但是异常不一定都是错误
- 分类
Error
类(错误
)(不可控的)Exception
类(异常
)(可控的)编译时异常
- 必须处理,不处理代码就无法运行
Ecxeption
下面除了RuntimeException
以外的直接子类
都是编译时异常
RuntimeException类
(运行时异常
):写代码时可以忽略
Error类对象
由Java虚拟机
生成并输出,Exception类对象
由应用程序
处理或抛出
11.2 常见异常
- Java中的常见异常
Throwable类
:所有异常类型都是内置类Throwable的子类
Error类
:用于Java运行时系统来显示与运行时系统本身有关的错误
Exception类
:用于用户程序可能捕获的异常
,也是用来创建用户类子类的类
- 类层次结构图
- 常见的异常
异常类型 | 描述 |
---|---|
RuntimeException异常 | Java.lang包中多数异常的基类 |
ArithmeticException异常 | 算术错误,例如除以0 |
IllegalArgumentException异常 | 方法收到非法参数 |
ArrayIndexOutOfBoundsException异常 | 数组下标出界 |
NullPointerException异常 | 空指针异常 |
ClassNotFoundException异常 | 不能加载请求的类 |
AWTException异常 | AWT中的异常 |
IOException异常 | I/O异常的根类 |
FileNotFoundException异常 | 不能找到文件 |
EOFException异常 | 文件结束 |
NoSuchMethodException异常 | 请求的方法不存在 |
InterruptedException异常 | 线程中断 |
11.3 异常处理机制
11.3.1 异常处理
- 异常处理是方法级别的东西
- 异常处理指的是能够进行处理的异常(Exception异常)
11.3.2 直接处理
关键字:
try
、catch
、final
-
try代码块
- 包含可能引起一个或多个异常的代码
try块
中遇到第一次错误时,try块
中错误以后的代码就不执行了,直接跳转到catch块
中寻找与之对应的异常try代码块
里面必须有能发生异常的语句String s1 = null; try {System.out.println(s1.toString()); } catch(NullPointerException e){System.out.println("发生了异常"); }
- 把将要发生异常的一个整体的业务逻辑放在
try块
里,然后跟对应数目的catch块
try {String s1 = null;System.out.println(s1.toString());System.out.println(10 / 0);int[] num = { 1, 2, 3 };System.out.println(num[3]); }
-
catch代码块
- 包含计划用于处理一个
特定异常类型
的代码 catch块
的数目要与try块
中异常的数目相对应,位置可以互换try {String s1 = null;System.out.println(s1.toString());System.out.println(10 / 0);int[] num = { 1, 2, 3 };System.out.println(num[3]); } catch (NullPointerException e) {System.out.println("发生了异常"); } catch (ArithmeticException e) {System.out.println("发生了异常"); } catch (ArrayIndexOutOfBoundsException e) {System.out.println("发生了异常"); }
catch块
可以是try块中所有异常的父类
try {String s1 = null;System.out.println(s1.toString());System.out.println(10 / 0);int[] num = { 1, 2, 3 };System.out.println(num[3]); } catch (Exception e) {System.out.println("发生了Exception"); }
- 当
try块
里面有多个异常时,catch块
中可以有与try块
异常对应的catch块
,也可以有try块
中其他异常的父类,但是父类的catch块必须在子类的后面
try {String s1 = null;System.out.println(s1.toString());System.out.println(10 / 0);int[] num = { 1, 2, 3 };System.out.println(num[3]); } catch(NullPointerException e) {System.out.println("发生了异常"); } catch (Exception e) {System.out.println("发生了Exception"); }
- 包含计划用于处理一个
-
finally代码块
- 在方法结束之前必须执行的代码
- 无论如何都会执行的代码
finally块
与try块
一起使用- 一个
try、catch
结构只能带一个finally
try {Class.forName("com.k10.ccc");System.out.println("com.k10.ccc"); } catch (ClassNotFoundException e) {throw e; } finally {System.out.println("必须执行的代码"); }
11.3.3 向上抛出
- 注意点
- 方法后面写不写
throws
与catch块
里写不写throw
没有关系,而是和catch块中异常的种类有关
(编译时异常必须写throws,运行时异常可写可不写
) - 调用处写不写
try、catch
与方法处写不写throws
没有关系,而是与方法处throws异常的种类有关
(编译时异常,外面必须捕获,运行时异常可以捕获可以不捕获
) - 对于方法内写不写
throw、throws
,调用处要不要捕获,只与异常的种类有关
(编译时异常,都必须写;运行时异常,都可写可不写
) - 判断的不是写没写关键字,而是看上一次是什么类型的异常
- 方法后面写不写
- 关键字
- 关键字:
throw
、throws
(但是向上抛出前必须先try、catch
捕获异常) throw
关键字- 使用
throw
语句可以在catch块
中将异常抛出给调用处 throw
前面的代码可以执行,后面的代码都不执行了
- 使用
throws
关键字- 使用
throws
可以在方法声明部分添加异常类型,表明该方法可能出现的异常
- 使用
- 关键字:
- 标准规范:
throw
了什么异常,方法就应该throws该异常
,表明该方法可能会抛出该异常,调用处应该对异常进行捕获并处理(有throw,有throws,调用处有捕获
)public void t1() throws ClassNotFoundException {try {Class.forName("com.k10.ccc");System.out.println("com.k10.ccc");} catch (ClassNotFoundException e) {throw e;} }
11.4 抛出异常的种类
11.4.1 编译时异常
- 如果
catch块
中是编译时异常
,则应该throw编译时异常
,方法处必须throws该异常
,调用处必须进行捕获
11.4.2 运行时异常
- 如果
catch块
中是运行时异
常,则应该throw运行时异常
,方法处可以throws
该异常,也可以不throws
,调用处可以进行捕获,也可以不进行捕获 - 不规范情况
- 情况一:有throw,没throws,调用处有捕获
- throw后面的代码不执行,直接调转到调用处,由于调用处进行了捕获,调用处处后面的代码依次照常执行
public class Text01 {public void t1() {System.out.println("t1开始了");try {System.out.println(10 / 0);} catch (ArithmeticException e) {throw e;}System.out.println("t1结束了");} }public class Main01 {public static void main(String[] args) {System.out.println("main开始了");Text01 text01 = new Text01();try {text01.t1();} catch (ArithmeticException e) {System.out.println("main捕获到了ArithmeticException");}System.out.println("main结束了");} } //输出结果 //main开始了 //t1开始了 //main捕获到了ArithmeticException //main结束了
- throw后面的代码不执行,直接调转到调用处,由于调用处进行了捕获,调用处处后面的代码依次照常执行
- 情况二:有throw,没throws,调用处没捕获
- throw后面的代码不执行,直接调转到调用处,由于调用处没有捕获,直接调转到编译器,由编译器控制
public class Text02 {public void t2() {System.out.println("t2开始了");try {System.out.println(10 / 0);} catch (ArithmeticException e) {throw e;}System.out.println("t2结束了");} }public class Main02 {public static void main(String[] args) {System.out.println("main开始了");Text02 text02 = new Text02();text02.t2();System.out.println("main结束了");} } //输出结果 //main开始了 //t2开始了 //Exception in thread "main" java.lang.ArithmeticException: / by zero //at com.k11.text02.Text02.t2(Text02.java:9) //at com.k11.text02.Main02.main(Main02.java:10)
- throw后面的代码不执行,直接调转到调用处,由于调用处没有捕获,直接调转到编译器,由编译器控制
- 情况三:没throw,有throws,调用处有捕获(相当于直接处理,无论有没有捕获,代码依次照常运行)
public class Text01 {public void t1() throws ArithmeticException{System.out.println("t1开始了");try {System.out.println(10 / 0);} catch (ArithmeticException e) {}System.out.println("t1结束了");} }public class Main01 {public static void main(String[] args) {System.out.println("main开始了");Text01 text01 = new Text01();try {text01.t1();} catch (ArithmeticException e) {System.out.println("main捕获到了ArithmeticException");}System.out.println("main结束了");} } //输出结果 //main开始了 //t1开始了 //t1结束了 //main结束了
- 情况四:没throw,有throws,调用处没捕获(相当于直接处理,无论有没有捕获,代码依次照常运行)
public class Text02 {public void t2() throws ArithmeticException{System.out.println("t2开始了");try {System.out.println(10 / 0);} catch (ArithmeticException e) {}System.out.println("t2结束了");} }public class Main02 {public static void main(String[] args) {System.out.println("main开始了");Text02 text02 = new Text02();text02.t2();System.out.println("main结束了");} } //输出结果 //main开始了 //t2开始了 //t2结束了 //main结束了
- 情况五:没throw,没throws,调用处有捕获(相当于直接处理,无论有没有捕获,代码依次照常运行)
public class Text01 {public void t1() {System.out.println("t1方法开始了");try {System.out.println(10 / 0);} catch (ArithmeticException e) {}System.out.println("t1方法结束了");} }public class Main01 {public static void main(String[] args) {System.out.println("main开始了");Text01 text01 = new Text01();try {text01.t1();} catch (ArithmeticException e) {System.out.println("main捕获到了ArithmeticException");}System.out.println("main结束了");} } //输出结果 //main开始了 //t1开始了 //t1结束了 //main结束了
- 情况六:没throw,没throws,调用处没捕获(相当于直接处理,无论有没有捕获,代码依次照常运行)
public class Text02 {public void t2() {System.out.println("t2开始了");try {System.out.println(10 / 0);} catch (ArithmeticException e) {}System.out.println("t2结束了");} }public class Main02 {public static void main(String[] args) {System.out.println("main开始了");Text02 text02 = new Text02();text02.t2();System.out.println("main结束了");} } //输出结果 //main开始了 //t2开始了 //t2结束了 //main结束了
- 情况七:没try、catch块,有throws,调用处没捕获
- 在有问题的代码处直接就跳转到编译器,编译器进行控制
public class Text01 {public void t1() {System.out.println("t1方法开始了");System.out.println(10 / 0);System.out.println("t1方法结束了");} }public class Main01 {public static void main(String[] args) {System.out.println("main开始了");Text01 text01 = new Text01();text01.t1();System.out.println("main结束了");} } //输出结果 //main开始了 //t1开始了 //Exception in thread "main" java.lang.ArithmeticException: / by zero //at com.k11.text05.Text01.t1(Text01.java:8) //at com.k11.text05.Main01.main(Main01.java:9)
- 在有问题的代码处直接就跳转到编译器,编译器进行控制
- 情况八:没try、catch块,有throws,调用处有捕获
- 在有问题的代码处直接跳转到方法调用处,由于调用处进行了捕获,调用处后面的代码依次照常执行
public class Text02 {public void t2() {System.out.println("t2开始了");System.out.println(10 / 0);System.out.println("t2结束了");} }public class Main02 {public static void main(String[] args) {System.out.println("main开始了");Text02 text02 = new Text02();try {text02.t2();} catch (ArithmeticException e) {System.out.println("main方法捕获了ArithmeticException");}System.out.println("main结束了");} } //输出结果 //main开始了 //t2开始了 //main方法捕获了ArithmeticException //main结束了
- 在有问题的代码处直接跳转到方法调用处,由于调用处进行了捕获,调用处后面的代码依次照常执行
- 情况一:有throw,没throws,调用处有捕获
11.5 自定义异常
- 定义自己的异常:可以根据自己的业务逻辑定义我们认为异常的地方,并为这些地方定义自己异常
- 想要定义
编译时异常的类
就继承Exception
;想要定义运行时异常的类
就继承RuntimeException
- 异常对象的常用方法
toString()
:返回异常的信息getMessage()
:返回消息的内容,说明当前的异常printStackTrace()
:把消息和栈跟踪记录输出到标准输出流,对于控制台程序,这个输出流就是屏幕
12. 垃圾回收机制
-
垃圾收集
- 垃圾回收机制是Java健壮性体现的一部分
- 垃圾
- 对于所有程序而言:长时间没有被使用的空间
- 对于Java而言:长时间没有被引用的
堆
里面的空间
垃圾收集
是将分配给对象但不再使用的内存回收或释放的过程- Java
自动释放
不再使用的内存(Java有垃圾回收车
) - 如果一个对象没有指向它的引用或将其赋值为null,则此对象将适于进行垃圾收集
-
内存泄露
- 将一个引用指向一个空间后,又将该引用重新指向另一个空间,之前的那个空间没有被释放,这就容易造成
内存泄露
String s1 = new String("OK"); s1 = new String("aaa"); s1 = new String("bbb"); s1 = new String("ccc");
- 将一个引用指向一个空间后,又将该引用重新指向另一个空间,之前的那个空间没有被释放,这就容易造成
-
finalize()
- 如果存在
finalize()
方法,它将在垃圾收集之前被执行一次,而且每个对象仅执行一次,用于执行最终消亡之前的一些操作 - 消亡就调用
finalize()
方法 - 不推荐使用,因为它的执行时机是不确定的,不能保证对象何时会被垃圾回收
public class FinalizeExample {@Overrideprotected void finalize() throws Throwable {try {// 执行清理操作System.out.println("Finalizing object...");} finally {super.finalize();}}public static void main(String[] args) {FinalizeExample obj = new FinalizeExample();// 让对象变成垃圾obj = null;// 强制执行垃圾回收System.gc();} }
- 如果存在
-
System.gc()
- 建议现在回收,不一定现在立马回收
13. ODBC
与JDBC
ODBC与JDBC都不是技术,两者都是数据库连接的
规范
(标准)
13.1 ODBC
- 一句话概括
- ODBC不是技术,是微软制定的数据库连接的一种规范。
- 如果某一种数据库想要在Windows平台上被语言连上,该数据库就得符合ODBC的标准;
- 如果某一种语言想要在Windows上连接某一种数据库,该语言就得符合ODBC的标准
ODBC
(Open Database Connection):开放式数据库连接
- 由Microsoft(MS)(微软)制定的数据库连接标准
13.2 JDBC
- 一句话概括:
JDBC不是技术,是SUN公司制定的Java连接数据库的一种规范,如果某一种数据库想让Java连接,就得符合JDBC的标准
JDBC
(Java Database Connection):Java数据库连接
- 由SUN公司制定的Java连接数据库的规范
- JDBC是一些
类
和接口
的集合(jar包
)- 压缩包:本质意思是打包
- 如果在打包之前是密度比较大的东西,一打包该东西容量就会变小
- 如果在打包之前是压缩文件,打包之后容量不会变小
ar
:指的是压缩包.ear
:企业级压缩包.jar
:Java压缩包,存放的都是Java文件(类和接口).rar
:操作系统压缩包.war
:web压缩包
- 压缩包:本质意思是打包
- Javabean
- 从广义的角度:所有的文件都是Javabean
- 从狭义的角度:通常所说的Javabean指的是
实体类
13.3 三方关系
- SUN公司提供统一的
接口
(存放在jar包中)以及制定JDBC
(Java连接数据库的规范) - DB生产商学习接口,提供接口的
实现类
(存放在jar包中),提供JDBC驱动程序
(jar包) - 开发人员:学习JDBC(接口)(规范),使用jar编写程序(DB生产商提供的接口的实现类)(只学习一种规范,使用所有数据库)
13.4 JDBC
驱动程序
-
数据库驱动程序的JAR包
:指的是数据库生产商提供的实现类
存放在jar包中SUN公司
制定的JDBC,大多是接口,打包在jar包中;db生厂商
提供的实现了这些接口的实现类,也打包在jar包中,这两个jar包是两个不同的jar包- 我们需要在项目中导入的jar包是
数据库生厂商提供的jar包
,SUN公司提供的jar包已经在Java中内置存在了 - 在使用之前需要将对应的jar包导入到对应的项目下
- sqlServer
msbase.jar
;mssqlserver.jar
;msutil.jar
sqljdbc.jar
:4.0版本之后三个合为一个了
- mysql:
mm.mysql-2.0.14-bin.jar
- oracle:
classes12.jar
-
JDBC驱动程序的种类
- JDBC-ODBC桥连(JDBC-ODBC桥驱动程序):数据源特指Access,如果要使用Access数据库,就必须符合ODBC规范(微软的)
- 本地API部分Java驱动程序:Java要求如果某一种数据库要使用Java语言就得符合JDBC的规范;数据库要求某一种语言想要连接某一种数据库,就必须符合该数据库规范;于是开发人员自己写了个JDBC驱动程序
- JDBC-Net 纯Java驱动程序:将开发人员自己写的驱动程序单独放到一个服务器上
- JDBC直连(纯Java驱动程序):所有数据库(除了Access)都实现了JDBC规范
- JDBC-ODBC桥连(JDBC-ODBC桥驱动程序):数据源特指Access,如果要使用Access数据库,就必须符合ODBC规范(微软的)
13.5 JDBC访问数据库的步骤
13.5.1 访问步骤
- 使用
Class.forName
(驱动程序类)加载数据库驱动//加载SqlServer数据库驱动 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
- 使用
DriverManager.getConnerction(String url,String user,String pwd)
连接数据库//连接SQLServer数据库中mybatisdb库 conn = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName=mybatisdb", "sa", "sa");
- 调用方法,创建
Statement
数据库执行载体对象//创建Statement对象 sta = conn.createStatement();
- 调用方法执行SQL语句
- 调用
Statement
接口中的方法执行SQL语句//返回受sql语句影响的条数 int sum = sta.executeUpdate(sql); //返回查询结果的ResultSet对象 ResultSet rs = sta.executeQuery(sql);
- 调用
PreparedStatement
接口中的方法执行SQL语句//创建预处理的sql语句 String sql = "insert into course (id_course, course_name, course_time, course_teacher_id) values (?, ?, ?, ?)"; //创建PreparedStatement对象 psta = conn.prepareStatement(sql); //为sql语句赋值 psta.setString(1, "c07"); psta.setString(2, "php"); psta.setInt(3, 50); psta.setString(4, "t04"); //返回受sql语句影响的条数 int sum = psta.executeUpdate(); //返回查询结果的ResultSet对象 ResultSet rs = psta.executeQuery();
- 调用
- 获取返回
ResultSet
的结果rs = sta.executeQuery(sql); while(rs.next()) {String id_course = rs.getString("id_course");String course_name = rs.getString("course_name");int course_time = rs.getInt("course_time");String course_teacher_id = rs.getString("course_teacher_id");System.out.println(id_course + " " + course_name + " " + course_time + " " + course_teacher_id); }
- 释放资源
if(rs != null) {rs.close();rs = null; }if(sta != null) {sta.close();sta = null; }if(conn != null) {conn.close();conn = null; }
- 完整举例
package com.k12.text04;import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;public class JDBCText01 {public static void main(String[] args) {Connection conn = null;Statement sta = null;ResultSet rs = null;try {Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");conn = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName=xa202403db", "sa", "sa");System.out.println("数据库连接成功");sta = conn.createStatement();String sql = "select * from course where course_teacher_id = 't01'";rs = sta.executeQuery(sql);while(rs.next()) {String id_course = rs.getString("id_course");String course_name = rs.getString("course_name");int course_time = rs.getInt("course_time");String course_teacher_id = rs.getString("course_teacher_id");System.out.println(id_course + " " + course_name + " " + course_time + " " + course_teacher_id);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();} finally {try {if(rs != null) {rs.close();rs = null;}if(sta != null) {sta.close();sta = null;}if(conn != null) {conn.close();conn = null;}} catch (SQLException e) {e.printStackTrace();}}} } //输出结果: //数据库连接成功 //c01 C语言程序设计 30 t01 //c02 Java与面向对象 30 t01
13.5.2 加载驱动程序
- 使用
Class.forName
(驱动程序类) 来加载驱动程序(决定要连哪一种数据库) - 驱动程序类
- JDBC-ODBC桥:
sun.jdbc.odbc.JdbcOdbcDriver
- SQLSERVER
com.microsoft.jdbc.sqlserver.SQLServerDriver
com.microsoft.sqlserver.jdbc.SQLServerDriver
- MYSQL:
org.gjt.mm.mysql.Driver
- ORACLE:
oracle.jdbc.driver.OracleDriver
- JDBC-ODBC桥:
13.5.3 各种数据库的URL
localhost
:指的是数据库所在主机的ip
;(127.0.0.1无论什么地方都指的是自己的ip)
1433/3306/1521
:指的是数据库安装时候的默认端口
;
DBName
:指的是数据库名字
- JDBC-ODBC:
jdbc:odbc:datasourceName
- SQLSERVER:
jdbc:sqlserver://localhost:1433;DatabaseName=DBName
- MYSQL:
jdbc:mysql://localhost:3306/DBName?user=root&password=pass
- ORACLE:
jdbc:oracle:thin:@localhost:1521:DBname
13.5.4 Driver接口
- 驱动程序类型接口,主要功能是决定连接哪一种数据库
- 使用
Class.forName
(驱动程序类)加载数据库驱动//加载SqlServer数据库驱动 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
- DriverManager类
- DriverManager类是Driver的实现类
- 使用
DriverManager.getConnerction(String url,String user,String pwd)
连接数据库- 返回值是个
Connection
类型 url
:连接的哪种数据库就对应该数据库的url路径名user
:之前连接的数据库的用户名pwd
:之前连接的数据库的密码
//连接SQLServer数据库中xa202403db库 conn = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName=xa202403db", "sa", "sa");
- 返回值是个
13.5.5 Connection接口(coon)
Connection
:数据库连接对象
- 此接口表示与数据库建立连接,只有获得该对象才能访问数据库
- 调用方法,创建数据库执行载体对象
- 使用
Connection.createStatement()
方法创建Statement
对象,该方法创建的Statement
对象对应具体sql
语句(在调用该方法时并没有传递sql语句,说明并没有编译sql语句)- 优点是灵活,缺点是编译慢,效率低
//创建Statement对象 sta = conn.createStatement();
- 调用
Connection.prepareStatement(sql)
获取PreparedStatement
对象,在获取PreparedStatement
时就将sql添加进去,将该PreparedStatement
与该sql
进行绑定
(在调用该方法时就对sql语句进行了编译
),该PreparedStatement只能执行该类型的sql语句- 在调用该方法之前,sql语句必须得有
//创建预处理的sql语句 String sql = "insert into course (id_course, course_name, course_time, course_teacher_id) values (?, ?, ?, ?)"; //创建PreparedStatement对象 psta = conn.prepareStatement(sql);
- 使用
13.5.6 Statement接口和PreparedStatement接口
13.5.6.1 Statement接口(sta)
Statement
:数据库执行载体
(对象),用于执行SQL语句- 此接口用于执行SQL语句并将数据检索到
ResultSet
中 - 调用方法执行SQL语句
-
Statement.
execute(String sql)
:用于执行各种SQL语句 -
Statement.
executeUpdate(String sql)
:用于执行SQL中的增
、删
、改
操作,返回一个int
类型的值,表示数据库中受SQL语句影响的条数//返回受sql语句影响的条数 int sum = sta.executeUpdate(sql);
-
Statement.
executeQuery(String sql)
:用于执行SQL中的查询
操作,返回表示查询结果的ResultSet
对象//返回查询结果的ResultSet对象 ResultSet rs = sta.executeQuery(sql);
-
13.5.6.2 PreparedStatement接口(psta)
- 此接口用于执行
预编译
的 SQL 语句 - 在调用
Connection.prepareStatement(sql)
时,sql语句就已经被编译了,以后再无论在执行多少遍,该sql就不会再编译了,只需要用setter()
方法对sql语句里面的?
给重新赋值就可以 - 在调用
Connection.prepareStatement(sql)
获取PreparedStatement
对象之前,sql语句必须得有String sql = "insert into course (id_course, course_name, course_time, course_teacher_id) values (?, ?, ?, ?)"; //用setter()方法给对应sql语句中的 ? 进行赋值 psta.setString(1, "c07"); psta.setString(2, "php"); psta.setInt(3, 50); psta.setString(4, "t04");
- 调用方法执行SQL语句
- 调用PreparedStatement.
excuteUpdate()
方法:用于执行SQL中的增
、删
、改
操作,返回一个int
类型的值,表示数据库中受SQL语句影响的条数int num = psta.executeUpdate();
- 调用PreparedStatement.
excuteQuery()
方法:用于执行SQL中的查询
操作,返回表示查询结果的ResultSet
对象ResultSet rs = psta.executeQuery();
- 调用PreparedStatement.
13.5.6.3 Statement和PreparedStatement的区别
- Statement接口
- Statement接口在调用
Connection.createStatement()
方法创建Statement
对象时,可以没有sql语句,该句对sql语句没有编译
- 一个Statement对象对应一条
具体
的sql语句
- Statement接口在调用
- PreparedStatement接口
- PreparedStatement接口在调用
Connection.prepareStatement(sql)
时,sql语句就已经被编译
了,在调用该方法之前sql语句必须存在
- 一个PreparedStatement对象对应一种
类型
的sql语句
- PreparedStatement接口在调用
13.5.7 ResultSet接口(rs)
- ResultSet:结果集对象
- 此接口表示了查询出来的数据库数据结果集
- 一般只有
查询
数据库时才会返回ResultSet对象 - 获取返回
ResultSet
的结果- 无论有没有查询到东西,返回ResultSet的结果都
不会为null
- 调用
next()
方法作为while循环的条件来迭代ResultSet结果集
- 在Resultset接口内部有一个指向表格数据行的游标(标记),ResultSet对象初始化的时候游标在表格第一行之前,调用
next()
方法可将游标移动到下一行。如果下一行没有数据,则返回false。
- 在Resultset接口内部有一个指向表格数据行的游标(标记),ResultSet对象初始化的时候游标在表格第一行之前,调用
- 通过
getter()
方法获取数据- ResultSet接口定义了大量的
getter()
方法,采用哪种getter()方法,取决于数据库中该字段的数据类型
rs = sta.executeQuery(sql); while(rs.next()) {String id_course = rs.getString("id_course");String course_name = rs.getString("course_name");int course_time = rs.getInt("course_time");String course_teacher_id = rs.getString("course_teacher_id");System.out.println(id_course + " " + course_name + " " + course_time + " " + course_teacher_id); }
- ResultSet接口定义了大量的
- 无论有没有查询到东西,返回ResultSet的结果都
13.5.8 释放资源
Statement
、ResultSet
、Connection
等需要释放资源- 后开的资源,先释放
close()
方法if(rs != null) {rs.close();rs = null; } if(sta != null) {sta.close();sta = null; } if(conn != null) {conn.close();conn = null; }
14. Util包日期时间类
14.1 Date类
14.1.1 Date类
已过时
:有时候用可以,有时候用就会出问题
- Date类:日期类
- 表示时间轴上的一个瞬时
- 获取的当前时间是
Date类的一个实例
- 用法:获取当前的系统时间(代码运行服务器的时间)
- 时区(CST):每个经度都是一个时区
- 中国是北京时间,东八区
- 时间分量:Date的每一个字段(年、月、日等等)
- 得到时间分量:
get()
方法获取(已过时)date1.getMonth()
- 设置时间分量:
set()
方法设置(已过时)date2.setMonth(0);
- 注意:月份是从
0
开始的- 即获取到月份是4,表示当前是5月
- 即设置的月份是0,表示当前是1月
- 得到时间分量:
14.1.2 构造方法
- 空参构造:
Date date = new Date();
- 带参构造(指定年月日):
Date date = new Date(int year,int month, int day);
- 已过时,由
Calendar.set(year ,month,day)
方法取代
//得到的是1908年的1月1日 Date date2 = new Date(8, 0, 1);
- 已过时,由
14.1.3 常用方法
after(Object when)
:测试此日期是否在指定日期之后date1.after(date2)
before(Object when)
:测试此日期是否在指定日期之前date2.before(date3)
14.2 Calender类
14.2.1 Calendar类
- Calendar类:日历类
- Calendar类是个抽象类,不能new,只能被继承
getInstance()
方法- 可以用
Calendar.getInstance()
方法获取Calendar对象
(实际上该方法返回的是Calendar子类的实例(GregorianCalendar
),以Calendar的形式返回的)Calendar c1 = Calendar.getInstance();
- 可以用
- 注意:月份是从
0
开始的- 即获取到月份是4,表示当前是5月
- 即设置的月份是0,表示当前是1月
14.2.2 常用方法
set(int field, int value)
:设置时间分量的值- field指的是时间分量
- value指的是值
c1.set(Calendar.MONTH, 0);
get(int field)
:返回时间分量的值c1.get(Calendar.DATE)
after(Object when)
:测试此日期是否在指定日期之后c1.after(c2)
before(Object when)
:测试此日期是否在指定日期之前c1.before(c2)
add(int field, int amount)
:日期的计算,计算时间分量的值- field指的是时间分量
- value指的是值
- 值为正数表示加
c1.add(Calendar.YEAR, 2);
- 值为负数表示减
c1.add(Calendar.YEAR, -2);
- 值为正数表示加
setTime(Date date)
:将当前的Date
设置成Calendar
GregorianCalendar gc = new GregorianCalendar(); gc.setTime(date);
getTime()
:把Calender
转换成一个Date
对象GregorianCalendar gc = new GregorianCalendar(); Date date = gc.getTime();
14.2.3 GregorianCalendar类
- GregorianCalendar类:标准的日历类
- GregorianCalendar类是Calendar的
实现类
- 构造方法
- 无参构造:
GregorianCalendar gc1 = new GregorianCalendar();
- 带参构造:
GregorianCalendar gc2 = new GregorianCalendar(int year, int month, int day);
- 无参构造:
- 常用方法
isLeapYear(int year)
:判断给定的年份是否为闰年gc.isLeapYear(2000)
14.3 SimpleDateFormat类
- SimpleDateFormat类:日期的格式化类
- SimpleDateFormat类不在
java.util包
中,而是在java.text包
中
符号 | 描述 |
---|---|
Y | 年 |
M | 月 |
d | 天 |
H | 小时(24小时制) |
h | 小时(12小时制) |
a | 上午/下午 |
m | 分钟 |
s | 秒 |
E | 星期 |
- 常用方法
format(Date date)
:将给定的Date
格式化为字符串
SimpleDateFormat sdf = new SimpleDateFormat("YYYY:MM:dd HH:mm:ss"); Date date = new Date(); String str = sdf.format(date);
parse(String str)
:将给定的字符串
转换成Date对象
String str1 = "2024:10:5 12:25:23"; SimpleDateFormat sdf1 = new SimpleDateFormat("YYYY:MM:dd HH:mm:ss"); Date date1 = sdf1.parse(str1);
15. Util包集合类
具体可看 集合类
15.1 集合框架
- 集合框架
- 集合框架:Java中所有的集合
- 集合:把多个元素变成一个元素的统一结构体
- 集合用于存储、检索和操纵数据
- 集合的根
Collection
接口(单列集合
):将多个元素组成一个单元的对象Map
接口(双列集合
):键值对存储的集合
- 掌握的集合对象
- 泛型
具体请看
泛型
- 优点一:规定集合中存储的对象类型
- 优点二:从集合中取出时就是该对象
- 集合框架的组成
接口
:是表现集合的抽象数据类型实现类
:是接口的实际实现算法
:是对实现接口的对象执行计算的方法
- 集合框架的优点
- 提供有用的数据结构和算法,从而减少编程工作
- 提供了程序速度和质量,因为它提供了高性能的数据结构和算法
- 可以方便的扩展或改写集合
15.2 Collection接口
Collection
接口- 单列集合接口、单列集合的根
- 单对象的、无序的、可重复的、可重复为null的单列集合
- 常用方法
add(E e)
:向Collection中添加元素collection1.add(s1);
addAll(Coiiection c)
:向Collection中添加另一个Collection- 注意:是将另外一个Collection中的元素复制了一份,改变另一个Collection中的元素,该添加后的Collection中元素不变
collection1.addAll(collection2);
clear()
:清空Collection中的元素collection3.clear();
contains(Object o)
:判断该Collection中是否包含该元素- 注意:比较的是具体的值
collection1.contains(s1)
- `containsAll(Collection c):判断该Collection中是否包含该Collection中的所有元素
- 注意:比较的是具体的值
collection1.containsAll(collection2)
equals(Object o)
:比较该Collection是否与指定对象相等- 注意:比较的是两个Collection中具体的值
collection2.equals(collection3)
isEmpty()
:判断该Collection是否为空collection1.isEmpty()
remove(Object o)
:在该Collection中删除第一次出现的该元素- 注意:删除的是该指定元素的单个实例,且每次只能删除一个实例
collection1.remove(s2);
removeAll(Collection c)
:删除这两个Collection中共有的元素(删除交集)- 注意:比较的是具体的值
collection1.removeAll(collection2);
retainAll(Collection c)
:保留这两个Collection中共有的元素(保留交集)- 注意:比较的是具体的值
collection2.retainAll(collection3);
size()
:返回Collection中的元素数目collection2.size();
toArray()
:返回包含此 collection 中所有元素的数组Object[] arr = collection2.toArray();
15.3 List接口
15.3.1 List接口
- 有序的、值可重复的单列集合
Collection
接口的子接口
有序的、值可以重复的、可以重复为null的单列集合
15.3.2 常用方法
List
接口可以使用Collection
父接口的所有方法
-
特有方法
add(int index, E element)
:在指定索引位置添加元素List<Student> list1 = new ArrayList<Student>(); list1.add(1, s3);
addAll(int index, Collection c)
:在指定索引位置添加指定的Collection- 注意:是将另外一个List中的元素复制了一份,改变另一个List中的元素,该添加后的List中元素不变
List<Student> list1 = new ArrayList<Student>(); List<Student> list2 = new ArrayList<Student>(); list1.addAll(0, list2);
get(int index)
:得到该索引位置的元素List<Student> list1 = new ArrayList<Student>(); list1.get(2);
lastIndexOf(Object o)
:返回该List中最后出现该元素的索引位置List<Student> list1 = new ArrayList<Student>(); list1.lastIndexOf(s3);
remove(int index)
:删除该索引位置上的元素List<Student> list1 = new ArrayList<Student>(); list1.remove(2);
set(int index, E element)
:替换指定索引位置上的元素List<Student> list1 = new ArrayList<Student>(); list1.set(1, s5);
subList(int fromIndex, int toIndex)
:返回索引为fromIndex(包括)
和toIndex(不包括)
之间的元素List<Student> list1 = new ArrayList<Student>(); List<Student> list = list1.subList(0, 3);
-
特有遍历方法
- 调用
get(int index)
方法结合for循环进行遍历 - 由于List集合有索引,因此可以调用
get(int index)
获取其中的元素List<Student> list1 = new ArrayList<Student>(); for (int i = 0; i < list1.size(); i++) {Student temp = list1.get(i);System.out.println(temp.toString()); }
- 调用
15.3.3 实现类
15.3.3.1 Vector类
- Vector类:向量表
- 线程是安全的
- elements()
- Vector类中特有的线程方法
- 返回该Vertor集合中的枚举
15.3.3.2 ArrayList类
具体可看
ArrayList集合
- 线程不安全
15.3.3.3 LinkedList类
具体可看
LinkedList集合
- 用链表的形式来实现动态数组
- 非线程程序中效率最高
- 线程不安全
15.4 Set接口
具体请看
Set接口
- Set接口
- 无序的、值不能重复的单列集合)
Collection
接口的子接口- List:无序的、值不可以重复的、可以为null的单列集合
- 值不可以重复是指后面的值会覆盖前面存在的值
Set
接口可以使用Collection
父接口的所有方法
- 实现类
HashSet
类:线程不安全
15.5 Map接口
具体请看
Map集合
15.5.1 Map接口
- Map:无序的、键不能重复可以为null、值可以重复可以为null的键值对集合
- 键不可以重复是指后面的键对应的值会覆盖前面存在的值
- 键值对都可以为null
15.5.2 常用方法
- 常用方法
-
put(K key, V value)
: 添加键值对元素Map<String, Student> map1 = new HashMap<String, Student>(); Student s1 = new Student("悟空", 500); map1.put("悟空", s1);
-
putAll(Map m)
: 将该map集合中添加指定的map集合- 注意:是将另外一个map中的元素复制了一份,改变另一个map中的元素,该添加后的map中元素不变
Map<String, Student> map1 = new HashMap<String, Student>(); Map<String, Student> map2 = new HashMap<String, Student>(); map1.putAll(map2);
-
clear()
: 清空map集合Map<String, Student> map2 = new HashMap<String, Student>(); map2.clear();
-
containsKey(Object key)
: 判断该map集合是否含有key- 注意:比较的是具体的值
Map<String, Student> map1 = new HashMap<String, Student>(); map1.containsKey("悟空");
-
containsValue(Object value)
: 判断该map集合是否含有value- 注意:比较的是具体的值
Map<String, Student> map2 = new HashMap<String, Student>(); map1.containsValue(s1);
-
equals(Object o)
: 比较指定对象与该集合是否相等- 注意:比较的是两个集合中具体的值
Map<String, Student> map1 = new HashMap<String, Student>(); Map<String, Student> map2 = new HashMap<String, Student>(); map1.equals(map2);
-
isEmpty()
: 判断该集合是否为空Map<String, Student> map1 = new HashMap<String, Student>(); map1.isEmpty();
-
remove(Object key)
: 删除指定key的值Map<String, Student> map1 = new HashMap<String, Student>(); map1.remove("白龙");
-
size()
: 返回集合中元素的数量Map<String, Student> map1 = new HashMap<String, Student>(); map1.size(); ```1. 特有方法
-
put(K key, V value)
: 添加键值对元素Map<String, Student> map1 = new HashMap<String, Student>(); Student s1 = new Student("悟空", 500); map1.put("悟空", s1);
-
putAll(Map m)
: 将该map集合中添加指定的map集合- 注意:是将另外一个map中的元素复制了一份,改变另一个map中的元素,该添加后的map中元素不变
Map<String, Student> map1 = new HashMap<String, Student>(); Map<String, Student> map2 = new HashMap<String, Student>(); map1.putAll(map2);
-
clear()
: 清空map集合Map<String, Student> map2 = new HashMap<String, Student>(); map2.clear();
-
containsKey(Object key)
: 判断该map集合是否含有key- 注意:比较的是具体的值
Map<String, Student> map1 = new HashMap<String, Student>(); map1.containsKey("悟空");
-
containsValue(Object value)
: 判断该map集合是否含有value- 注意:比较的是具体的值
Map<String, Student> map2 = new HashMap<String, Student>(); map1.containsValue(s1);
-
equals(Object o)
: 比较指定对象与该集合是否相等- 注意:比较的是两个集合中具体的值
Map<String, Student> map1 = new HashMap<String, Student>(); Map<String, Student> map2 = new HashMap<String, Student>(); map1.equals(map2);
-
isEmpty()
: 判断该集合是否为空Map<String, Student> map1 = new HashMap<String, Student>(); map1.isEmpty();
-
remove(Object key)
: 删除指定key的值Map<String, Student> map1 = new HashMap<String, Student>(); map1.remove("白龙");
-
size()
: 返回集合中元素的数量Map<String, Student> map1 = new HashMap<String, Student>(); map1.size();
-
15.5.3 实现类
- HashMap类
具体请看
HashMap类
- 线程不安全
- 键和值可以存储null
- HashTable类
- 线程安全
- 键不能为null
- 值不能为null
15.6 集合的遍历
具体请看
集合的遍历
15.6.1 双列集合的遍历
15.6.1 键遍历
keySet()
:返回该双列集合中所有的key,返回值是Set类型
Map<String, Student> map1 = new HashMap<String, Student>(); Set<String> set = map1.keySet(); Iterator<String> it = set.iterator(); while(it.hasNext()) {String key = it.next();Student value = map1.get(key);System.out.println(key + value.toString()); }
15.6.2 值遍历
values()
:返回该双列集合中所有的value,返回值是Collection类型
Map<String, Student> map1 = new HashMap<String, Student>(); Collection<Student> values = map1.values(); for (Student temp : values) {System.out.println(temp.toString()); }
15.6.3 键值对遍历
entrySet()
:返回该双列集合中所有的键值对,返回值是Set类型(Set<Map.Entry<K,V>>)
getKey()
:获取keygetValue()
:获取value
Map<String, Student> map1 = new HashMap<String, Student>(); Set<Entry<String, Student>> entries = map1.entrySet(); for (Entry<String, Student> entry : entries) {String key = entry.getKey();Student value = entry.getValue();System.out.println(key + value.toString()); }
15.6.2 单列集合的遍历
15.6.1 迭代器遍历
iterator()
:返回值是Iterator
类型hasNext()
:判断有没有下一个next()
:获取下一个元素
Iterator it3 = collection3.iterator(); while (it3.hasNext()) {Student temp = (Student) it3.next();System.out.println(temp.toString()); }
15.6.2 变成数组遍历
- 调用
toArray()
生成Object[ ]
,遍历该数组Object[] arr = collection2.toArray(); for(int i = 0; i < arr.length; i++) {System.out.println(arr[i].toString()); }
15.6.3 foreach遍历
for (Student temp : collection) {System.out.println(temp.toString());
}
15.6.3 枚举遍历
- 只针对线程安全的集合
elements()
:获取该集合元素,返回值是Enumeration枚举类型hasMoreElements()
:判断有没有下一个元素,返回值是boolean类型nextElement()
:获取下一个元素,返回值是获取到元素的类型
Hashtable<String, Student> ht = new Hashtable<String, Student>(); Enumeration<Student> em = ht.elements(); while(em.hasMoreElements()) {Student temp = em.nextElement();System.out.println(temp.toString()); }
16. I/O流
具体请看
I/O流
16.1 Java I/O流
- 为什么使用I/O流?
- I/O流用来完成Java的
输入输出
- 输入输出的位置:源头、目的地
- 网络上别的机器
- 本地的硬盘
- 别的程序、内存(不是JVM里面的)
- 所有的流都使用相同的模型来编写程序
- 选取想要使用的流
- 正确的方式将要使用的流
new
出来 - 根据业务逻辑进行读写
- 关闭资源
- I/O流用来完成Java的
- 什么是I/O流?
I/O
:Input/Output
(输入/输出)- Input
Input
:从JVM的外面进到JVM的里面来- 代码里面没有的数据
- 从数据源读取数据到程序中
- 源头(数据源):进来的地方
- Output
Output
:从JVM的里面出去到JVM的外面- 代码里面有的数据
- 从程序中输出数据到外部目的地
- 目的地:出去的地方
- 什么是流?
- 生活中的流是一种连续的有序的物质形态
- 程序中的流是一种连续的有序的数据形态
- 特征:连续的、有序的
- 什么是I/O流?
Java完成输入输出的不间断的、连续的、有序的数据形态
- I/O流分类
- 本质上分
字节流
:在传输数据的时候,搬运的最小单元就是字节
字符流
:在传输数据的时候,搬运的最小单元就是字符
- 功能上分(操作什么就是什么流)
- 文件流:专门操作文件
- 对象流:专门操作对象
- 数据流:专门操作数据
- 转换流:专门用来转换数据
- 标准流:专门用来操作控制台
- 缓冲流:专门操作缓冲区
- 本质上和功能上的联系
- 按照功能上分的流,有的既基于字节的又基于字符的,有的只基于字节
- 没有只基于字符,不基于字节的流
- 本质上分
- I/O流作用
- 主要是用来传输数据
- 顺便还可以操作或格式化数据
16.2 字节流
16.2.1 字节流
- 字节流可以直接操作
二进制数据
(字节数据) - 在传输数据的时候,搬运的最小单元就是
字节
节点流
:能够直接给定源头和目的地的流处理流
:不能直接给定源头和目的地的,要用另外一个流来new
该流的流
16.2.2 分类
- 输入字节流
InputStream
是输入字节流的根- 类层次结构图(加粗的是节点流,没加粗的是处理流)
- 输出字节流
- OutputStream是输出字节流的根
- 类层次结构图(加粗的是节点流,没加粗的是处理流)
16.2.3 常用方法
16.2.3.1 不带缓冲区的字节流的读写数据
InputStream.read()
:读取数据- 读取数据时,最开始在文件的Bof,当数据读取完了,就在文件的Eof(
-1
)处 - 每次read时,读取的是个
byte
(8位),返回的是int
(32位),将读取到的数据(byte)放到int的最后一位,前面空的24位用于底层JVM处理别的事情
int data = in.read();
- 读取数据时,最开始在文件的Bof,当数据读取完了,就在文件的Eof(
OutputStream.write(int data)
:写出数据out.write(data);
int data = -2; while((data = in.read()) != -1) {System.out.print((char)data);out.write(data);out.flush(); }
16.2.3.2 带缓冲区的字节流的读写数据
InputStream.read(byte[ ], off, len)
:带缓冲区的读取数据byte[ ]
:缓冲区数组off
:偏移量,即从哪个位置开始装(一般为0)len
:装多少(一般为数组长度)
while((in.read(pool, 0, pool.length)) != -1) {}
OutputStream.write(byte[ ], off, len)
:带缓冲区的写入数据byte[ ]
:缓冲区数组off
:偏移量,即从哪个位置开始装(一般为0)len
:装多少(一般为数组长度)
out.write(pool, 0, pool.length);
15.2.3.3 flush()
OutputStream.flush()
:将缓冲的数据立即写出,而不是等到缓冲区满或关闭流时才写出out.flush();
15.2.3.4 close()
close()
:关闭资源in.close(); out.close();
16.3 字符流
16.3.1 字符流
- 流里面(数据源、目的地)有可能包含
文本
就要使用字符流
- 在传输数据的时候,搬运的最小单元就是
字符
- 作用:解决流里面可能出现的
中文乱码
问题
16.3.2 分类
- 输入字符流
Reader
是输入字符流的根- 类层次结构图(加粗的是节点流,没加粗的是处理流)
- 输出字符流
Writer
是输出字符流的根- 类层次结构图(加粗的是节点流,没加粗的是处理流)
16.3.3 常用方法
16.3.3.1 不带缓冲区的字符流的读写数据
-
Reader.read()
:读取数据- 读取数据时,最开始在文件的Bof,当数据读取完了,就在文件的Eof(-1)处
- 每次
read
时,读取的是个char
(16位),返回的是int
(32位),将读取到的数据(char)放到int的最后两位,前面空的12位用于底层JVM处理别的事情int data = Reader.read();
-
Writer.write(int data)
:写出数据writer.write(data);
int data = -2; while((data = reader.read()) != -1) {System.out.print((char)data);writer.write(data);writer.flush(); }
16.3.3.2 带缓冲区的字符流的读写数据
- 字符缓冲流
- 缓冲流:操作缓冲区的流
- 分类
字符缓冲输入流
:BufferedReader字符缓冲输出流
:BufferedWriter
- 缓冲原理:
一次读取一行,只要不换行就放到一个缓冲区
- 常用方法
- 读取数据:
readLine()
- 写出数据:
write()
- 关闭资源:
close()
- 推出缓冲区数据:
flush()
- 读取数据:
16.3.3.3 flush()
- 将缓冲的数据立即写出,而不是等到缓冲区满或关闭流时才写出
writer.flush();
16.3.3.4 close()
- 关闭资源
Reader.close(); Writer.close();
16.4 常见I/O流
16.4.1 转换流
转换流
:用来转换数据的流,将字节转换成字符,或将字符转换成字节- 字节转换流
- 字节输入转换流:
InputStreamReader
- 字节输出转换流:
OutputStreamWriter
- 字节输入转换流:
16.4.2 数据流
- 数据流:能够操作数据的流
- 数据流只有基于
字节
的,没有基于字符
的 - 字节数据流
- 字节输入数据流:
DataInputStream
- 字节输出数据流:
DataOutputStream
- 字节输入数据流:
- 常用方法
- 读取数据:
readXxx()
- 写出数据:
writeXxx()
- 关闭资源:
close()
- 推出缓冲区数据:
flush()
- 读取数据:
16.4.3 对象流
- 对象流:能够操作对象的流
- 对象流只有基于
字节
的,没有基于字符
的 - 在使用对象流时,传输的对象必须实现
实现序列化接口
(Serializable) - 字节对象流
- 字节输入对象流:
ObjectInputStream
- 字节输出对象流:
ObjectOutputStream
- 字节输入对象流:
- 常用方法
- 读取数据:
readObject()
- 写出数据:
writeObject()
- 关闭资源:
close()
- 推出缓冲区数据:
flush()
- 读取数据:
- 接口
- 功能接口:实现时要重写该接口里所有的方法的接口
- 标识接口:不用完成功能的接口,该接口没有功能(方法),只起到标识作用
16.4.4 标准流
- 标准流:用来操作控制台的流
- 为什么叫标准流?
- 数据源和目的地都不能改变,都是控制台
- 标准流不需要
new
,在JVM启动时就自动new好了
- 分类
- 输入流:
System.in
(字节流,无字符流对象,为了把它变成字符流可以把System.in
包装到InputStreamReader
(转换流)中) - 输出流
System.out
System.err
- 输入流:
- 常用方法
- 读取数据:
read()
- 写出数据:
write()
- 关闭资源:
close()
- 推出缓冲区数据:
flush()
- 读取数据:
16.5 文件操作
16.5.1 File
- 文件操作
- 文件操作:操作文件的本身
- Java不区分文件和文件夹
- File
- File:帮助编写对文件、目录进行操作的依赖于平台的代码
- File位于
io
包中 - 构造方法
File f1 = new File(String pathname)
- 注意:new出来的文件,在运行时只存在于Java中,当运行结束时,该文件就被释放了,与硬盘没有关系,如果想存在硬盘中,可以使用
createNewFile()
方法
- 常用方法
- File.
exists()
:判断该文件是否存在boolean b = f1.exists()
- File.
createNewFile()
:在硬盘中创建该文件f1.createNewFile();
- File.
delete()
:删除该文件f1.delete();
- File.
isDirectory()
:判断该文件是不是文件夹boolean b = files[i].isDirectory()
- File.
isFile()
:判断该文件是不是文件boolean b = files[i].isFile()
- File.
listFiles()
:前提该文件是文件夹,将该文件夹里的文件以文件数组的形式返回File[] files = f2.listFiles();
- File.
getAbsolutePath()
:获取该文件的绝对路径System.out.println(f1.getAbsolutePath());
- File.
getPath()
:获取该文件的路径System.out.println(f1.getPath());
- File.
getName()
:获取该文件的名字System.out.println(f1.getName());
- File.
getParent()
:获取该文件的父文件夹System.out.println(f1.getParent());
- File.
16.5.2 遍历文件夹
- files数组可能为null
listFiles()
方法返回一个File[]
数组,如果目录为空或者发生I/O错误,它可能返回null
- 递归调用中的空对象可能导致空指针异常
- 在递归调用
each(files[i])
时,如果files[i]
是null
(是一个文件(非文件夹)),就会导致空指针异常。
File f2 = new File("C:"); each(f2); public static void each(File f2) {File[] files = f2.listFiles();if (files == null) {//判断文件夹是否为空System.out.println("文件夹或目录为空或无法访问");return;}for (int i = 0; i < files.length; i++) {if (files[i].isFile()) {System.out.println(files[i].getName());continue;}each(files[i]);} }
- 在递归调用
16.5.3 RandomAccessFiles
- RandomAccessFiles:支持随机访问磁盘文件
- RandomAccessFiles:操作文件里面的东西,底层也是IO
- 构造方法
RandomAccessFiles accessFile = new RandomAccessFiles(File file,String mode)
RandomAccessFiles accessFile = new RandomAccessFiles(String name,String mode)
mode
:只读、只写、又读又写
- 常用方法
- RandomAccessFiles.
seek(int i)
:将游标挪到i号位置- RandomAccessFiles对象有个像游标一样的东西,数据读取或写入时,每读(写)一个,游标就移一位
accessFile.seek(0);//将游标移到开始处
- RandomAccessFiles.
writeXx()
:数据写入accessFile.write("你好,文件".getBytes());
- RandomAccessFiles.
readXx()
:数据读取String line = accessFile.readLine();
- RandomAccessFiles.
getFilePointer()
:获取该游标的位置int i = accessFile.getFilePointer();
- RandomAccessFiles.
close()
:关闭资源accessFile.close();
- RandomAccessFiles.
- 数据转码:String str = new String(RandomAccessFiles.
readXx().getBytes("iso-8859-1")
,"GBK");
String line = accessFile.readLine(); //数据转码 String info = new String(line.getBytes("iso-8859-1"), "GBK");
17. 多线程
17.1 进程和线程
17.1.1 进程和线程
- 多任务和单任务
- 多任务(
并发
):多进程和多线程就是多任务 - 单任务(
独占
):只能有一个线程在运行
- 多任务(
- 重量级和轻量级
重量级
:相对而言,两个资源在一起,占用资源多,开辟和消亡的慢,不太稳定的叫做重量级轻量级
:相对而言,两个资源在一起,占用资源少,开辟和消亡的快,较稳定的叫做轻量级
- 进程
- 进程:
一个进程就是操作系统里的一个正在运行的应用程序
- 单进程和多进程
- 单进程:操作系统里的单任务(
独占
) - 多进程:操作系统里面的多任务(
并发
),操作系统是基于进程在做并发
- 单进程:操作系统里的单任务(
- 进程是“
自包容
”的运行程序,有自己的地址空间 自包容
:自己管自己,应用程序一旦启动后,操作系统就不管了,剩下的就是应用程序自己管自己- 进程是重量级的多任务
- 进程:
- 线程
- 线程:
进程(正在运行的应用程序)里的任务
- 单线程和多线程
- 单线程:一个应用程序里面的一个任务
- 多线程:进程(正在运行的应用程序)里的多任务(并发)
- 线程是轻量级的多任务,是CPU使用的基本单元
- 线程属于某一个进程,一个进程中拥有一个或多个线程,多个线程
共享
同一个进程资源 - 操作系统是基于
进程
(应用程序)工作的,然而处理程序的是CPU,但是CPU不认识程序,CPU是基于线程
工作的
- 线程:
17.1.2 多任务处理
- 分类
- 基于进程:计算机同时运行多个进程
- 基于线程:一个进程包含多个线程
- 特点
- 各个进程需要分配他们自己独立的地址空间
- 进程间调用涉及的开销比线程间通信多
- 多个线程可共享相同的地址空间并且共同分享同一个进程
- 线程间的切换成本比进程间切换成本低
- 进程是重量级的,线程是轻量级的
17.2 线程
17.2.1 多线程优点
- 模拟同步动作:
线程同步
- 利用多处理器
- CPU的工作是基于
线程
的,如果同时有20个CPU,只有一个线程,那么CPU就浪费了 - 多线程允许程序在多个CPU上同时执行不同的线程,从而实现真正的
并行
处理。
- CPU的工作是基于
- 等待缓慢
IO操作
(堵塞操作)时完成其他任务堵塞操作
:调用了,但是堵在那里不动(不死但是也不反馈),就给该堵塞行为开个线程,从而不影响其他线程
- 与用户交互更佳
- 简化对象模型
17.2.2 JVM中的线程和进程
- JVM中的线程
- 当操作系统启动JVM(
javac
、java
、javaw
等命令)时就创建了一个进程 - JVM线程至少包含两种线程
- main方法
- 垃圾回收线程
main方法
和垃圾回收线程
称为主线程
,有时他们会启动其他线程,他们必须最后完成执行,执行各种关闭、释放动作
- 当操作系统启动JVM(
- JVM进程
17.2.3 创建线程两种方法
17.2.3.1 继承Thread类
- 声明一个
Thread类
的子类,并覆盖run()
方法 - 该类继承了
Thread类
,那么该类就是线程类
,重写线程类的run
方法,run方法是线程的核心方法,当开始线程时就会自动调用run方法
- 语法:
public class SampleThread extends Thread {public void run(){ }; }
- 举例:
public class MyThread extends Thread {public void run() {System.out.println("线程正在运行...");}public static void main(String[] args) {MyThread thread = new MyThread();thread.start();} }
17.2.3.2 实现Runnable接口
- 声明一个实现
Runnable接口
(共伴接口)的类,并实现run()
方法 - 该类实现Runnable接口,实现里面的
run
方法,但是该类不是线程类
,只是个普通类,且run方法不是线程里的核心方法,只是Runnable里的run方法,和线程没有关系 - new一个空的Thread,将
实现Runnable接口的类
作为参数给Thread,从而将空白线程Thread里的空的run方法按照传进来的实现Runnable接口类的run方法来runThread t1 = new Thread(new Text01());
- 语法:
public class SampleThread implements Runnable{public void run{}; }
- 举例:
public class Text01 implements Runnable {@Overridepublic void run() {System.out.println("这是一个实现了runnable的run方法.");} }public class Main {public static void main(String[] args) {Thread t1 = new Thread(new Text01());t1.start(); // 启动线程,会执行Text01类中实现的run方法} }
17.2.4 线程状态
- 新建态(
new
):线程new出来了,还没有运行 - 运行态(
runnable
):正在运行的状态(调用start
的状态) - 等代态
- 锁定态(
blocked
):受堵塞线程的线程状态 - 定时等待态(
timed waiting
):具有指定时间的某一等待状态的线程状态 - 不定时等待态(
waiting
):调用不带超时值的Object.wait()、Thread.join()之一而处于等待状态
- 锁定态(
- 终止态(
terminatrd
):线程已经运行完毕的状态
17.2.5 常用方法
-
Thread.
currentThread()
:获取当前正在运行线程Thread t = Thread.currentThread();
-
Thread.
sleep(long millis)
:线程睡眠- 参数:millis毫秒
Thread.currentThread().sleep(1000);
-
Thread.
start()
:线程启动,线程一开启就不管了,该咋运行就咋运行t1.start();
-
Thread.
getName()
:获取线程名称String name = Thread.currentThread().getName();
-
Thread.
isAlive()
:判断线程是否是运行状态boolean b = Thread.currentThread().isAlive();
-
Thread.
setName(String name)
:设置线程名称Thread.currentThread().setName("main线程");
-
Thread.
join()
:等待线程结束try {Thread.currentThread().join(); } catch (InterruptedException e) {e.printStackTrace(); }
-
Thread.
isDaemon()
:判断该线程是否是守护线程boolean b1 = Thread.currentThread().isDaemon();
-
Thread.
setDaemon(boolean b)
:设置该线程是否为守护线程Thread.currentThread().setDaemon(true);
-
Thread.
activeCount()
:返回正在运行的线程数int i = Thread.currentThread().activeCount();
-
Thread.
yield()
:使正在运行的线程暂停,并让其他线程执行Thread.currentThread().yield(); ```- Thread.currentThread():获取当前正在运行线程 ```java Thread t = Thread.currentThread();
17.2.6 线程优先级
- 线程优先级:线程抢占CPU资源(令牌)的能力
- 获得CPU的资源是一样的,只是优先级高的先获取的快
- java中的线程优先级是在
Thread
类中定义的常量
NOEM_PRIORTY
:5MAX_PRIORITY
:10MIN_PRIORITY
:1
- 常用方法
- Thread.
setPriority()
:修改线程的当前优先级 - Thread.
getPriority()
:返回线程的优先级
- Thread.
17.2.7 线程同步
- 同步:
独占,排除并发
- 线程同步:当多个线程同时访问同一资源时,就要使用
同步
- 线程同步的两种方法
-
同步方法
:将需要同步的方法前添加synchronized修饰词public synchronized void print(String name) {try {System.out.print("[");System.out.print(name);Thread.currentThread().sleep(1000);System.out.println("]");} catch (InterruptedException e) {e.printStackTrace();} }
-
同步块
:使用同步块,把要同步的代码写在同步块里synchronized(object){ //要同步的语句 }
object
指的是同步的资源,在代码块中调用的该资源的所有东西都是同步的
synchronized (this.pm) {this.pm.print(name); }
-
17.3 wait-notify机制
- wait-notify机制
wait-notify机制
(通信机制):两个线程之间等待和唤醒的关系- Java提供了一个精心设计的线程间通信机制,使用
wait()
、notify()
和notifyAll()
方法
- 三个方法(都是
Object
中的final
方法(不能重写的方法))wait()
:等待方法notify()
:唤醒方法,一次只唤醒一个离我最近的等待线程notifyAll()
:唤醒所有等待的线程- 唤醒的线程会抢占cpu资源,抢占到的资源运行,没有抢占到的继续等待,等待下次唤醒
- 这三个方法必须在
synchronized
方法(同步方法)中使用
18. 网络编程
18.1 网络编程
- 网络编程
- Java网络编程:套接字编程、socket编程、网络编程、net编程
- Java网络编程:用Java从网络上取东西
- 什么是网络?
- 网络:用某一种媒介(介质)把终端设备连接起来实现
资源共享和通信
- 网络:用某一种媒介(介质)把终端设备连接起来实现
18.2 ISO/OSI参考模型
18.2.1 ISO和OSI
ISO
(International Standard Organization ):国际标准组织(制定标准)OSI
(Open System Interconnection):开放系统连接
18.2.2 OSI参考模型
- 为了解决四个问题
- 怎么连
- 怎么找
- 怎么传
- 传什么
- 七层
- OSI参考模型图
18.2.3 TCP/IP参考模型
- TCP是传输层,IP是网络层
- TCP/IP参考模型图
18.3 IP、DNS、端口、协议
18.3.1 IP
- IP
IP
:计算机主机在网络上的唯一标识- 连接至网络的每台计算机的IP都是
唯一
的
- IP表示
- 32 位数字,四个用
点号
分隔的数字 - 范围:0~255
.
0~255.
0~255.
0~255186.156.23.23
- 32 位数字,四个用
- 网络类别:A、B、C、D
- A.B类一般都是一些独有的特有的在使用
- 我们平时使用的都是C、B类
- 一个主机(Host)只能有一个IP
18.3.2 DNS
- 域名:IP的映射(别名)
www.baidu.com
- DNS(Domain Name System):
域名系统
(目录(IP)名字(域名)服务),当输入域名时,DNS服务器就会将域名
解析成IP
- DNS服务器:做DNS服务的主机
18.3.3 端口
- 端口(Port):代表一个应用程序,用于实现程序间通信
- 端口是在
传输层
使用的 - 常用端口
- TeInet协议:23
- 简单邮件传输协议:25
- 文件传输协议:21
- 超文本传输协议:80
- 一个机子可以有成千上万个端口,自己一般设置5w以后的端口
18.3.4 协议
- 协议:网络中计算机之间通信的规则(约定)
- 常用协议
- 超文本传输协议(HTTP)
- 文件传输协议(FTP)
- 简单邮件传输协议(SMTP)
- 网络新闻传输协议(NNTP)
18.4 B/S模式和C/S模式
18.4.1 B/S模式
18.4.2 C/S模式
18.4.3 B/S模式的演变
- 基于web的0客户端
- 浏览器运行不了Java程序,只有服务器才能运行java程序,所以应用程序不能到浏览器中,都得在服务器上,从而每次操作都得请求服务器,导致速度太慢了
- 基于web的负客户端
- 浏览器虽然可以运行Java程序,但是写不了Java代码,只能写JavaScript代码,将程序从服务器运行到浏览器的技术实现是Ajax、JQurey
- 浏览器只能识别
html
(构建浏览器)、css
(美化浏览器)、js
(负责浏览器动作) - Ajax、JQurey:在浏览器上写应用,将重要的东西放在服务器上,将一些合理的东西就放在本地
- 基于web的前后端分离
- js框架:使用JavaScript框架(如React、Angular、Vue.js)构建前端
- Java框架:使用Java框架(如Spring MVC、Spring Boot)构建后端
18.4.4 完整开发的四个程序
18.5 套接字(socket)
18.5.1 套接字(socket)
- 套接字(socket):操作系统用于网络通讯的端点
- socket不是Java的东西,而是操作系统的东西
- socket与主机地址和端口地址相关联
- 客户端和服务器通过套接字建立连接和进行通信
18.5.2 socket图解
- 运行过程
- 操作系统A将程序A给socket,socket给驱动程序,驱动程序给操作系统外的网卡,网卡通过网线给另一个网卡,该网卡通过操作系统B中的驱动程序给与之对应的socket,该socket又给程序B
- 操作系统A将程序A给socket,socket给驱动程序,驱动程序给操作系统外的网卡,网卡通过网线给另一个网卡,该网卡通过操作系统B中的驱动程序给与之对应的socket,该socket又给程序B
19. Java.net包
19.1 InetAddress
19.1.1 InetAddress对象
- InetAddress对象:包装网络上的一台
主机
(Host)
19.1.2 获取方法
- InetAddress.
getByAddress(byte[] addr)
:通过地址
获取该主机byte[] bytes = {127, 0, 0, 1}; InetAddress address1 = InetAddress.getByAddress(bytes);
- InetAddress.
getByAddress(String host, byte[] addr)
:通过地址
获取该主机byte[] bytes = {127, 0, 0, 1}; InetAddress address2 = InetAddress.getByAddress("127.0.0.1", bytes);
- InetAddress.
getByName(String host)
:通过主机名(域名、IP)
获取该主机InetAddress address3 = InetAddress.getByName("www.baidu.com");
- InetAddress.
getLocalHost()
:通过本地主机名(域名、IP)
获取该主机- 127 . 0 . 0 . 1:永远代表本机IP
InetAddress address4 = InetAddress.getLocalHost();
- 参数
host
:用于指定主机名或者 IP 地址- 如果传入 IP 地址,会直接解析该地址
- 如果传入主机名(域名),则解析该主机名对应的 IP 地址
addr
:用于指定 IP 地址的字节表示形式- IPv4 地址通常是一个 4 字节的数组
- IPv6 地址通常是一个 16 字节的数组
19.1.3 常用方法
- InetAddress.
getHostAddress()
:获取InetAddress的IP
InetAddress address3 = InetAddress.getByName("www.baidu.com"); System.out.println(address3.getHostAddress());//36.155.132.76
- InetAddress.
getHostName()
- 如果该InetAddress是
getByAddress
获取的,则该方法是获取InetAddress的IP
byte[] bytes = {127, 0, 0, 1}; InetAddress address1 = InetAddress.getByAddress(bytes); System.out.println(address1.getHostName());//127.0.0.1
- 如果该InetAddress是
getByName
获取的,则该方法是获取InetAddress的域名
InetAddress address3 = InetAddress.getByName("www.baidu.com"); System.out.println(address3.getHostName());//www.baidu.com
- 如果该InetAddress是
getLocalHost
获取的,则该方法是获取InetAddress的电脑名
InetAddress address4 = InetAddress.getLocalHost(); System.out.println(address4.getHostName());//电脑名
- 如果该InetAddress是
19.2 TCP的网络通讯
19.2.1 TCP的网络通讯
- 在通讯时,是Socket与Socket进行通讯,
ServerSocket
负责侦听(当ServerSocket侦听到Socket1时会创建个Socket,与之通讯) - TCP的网络通讯是基于
数据流
的
19.2.2 对象
19.2.2.1 ServerSocket
- ServerSocket:服务器的TCP套接字对象
- 构造方法:
ServerSocket serverSocket = new ServerSocket(int port)
port
:端口号,判断当前程序用哪个端口进行通讯ServerSocket serverSocket = new ServerSocket(50088);
- 常用方法
- ServerSocket.
accept()
:返回值是Socket
侦听方法
,用于接受Socket的请求并返回一个已经连接了Socket请求的新Socket对象阻塞方法
,只要没有侦听到Socket,就一直卡在这,也不进行后面的事
Socket socket = serverSocket.accept();
- ServerSocket.
19.2.2.2 Socket
- Socket:客户端的
TCP套接字对象
- 构造方法
Socket socket = new Socket(InetAddress address,int port)
Socket socket = new Socket(String hostName,int port)
address
:包装主机(Host)的InetAddress对象hostName
:主机(域名、IP)port
:端口号
- 常用方法
- Socket.
getInputStream()
:获取输入字节流对象,返回值是InputStreamwriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
- Socket.
getOutputStream()
:获取输出字节流对象,返回值是OutputStream()writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
- Socket.
getPort()
:获取远程的端口号 - Socket.
getLocalPort()
:获取本地的端口号 - Socket.
getInetAddress()
:获取主机,返回值是InetAddress
- Socket.
19.2.3 SocketException异常
- SocketException:Connection reset(连接重置异常)
- 两边的Socket正在正常通讯,一边的Socket正在读取,另一边的Socket无辜掉线了,就会报该异常
- 解决方法:让两边的Socket都正常掉线
19.3 UDP的网络通讯
19.3.1 UDP的网络通讯
- 数据报:通信的一种报文类型,是
单向传输
的,可能会丢数据 - UDP的网络通讯是基于
数据报
的
19.3.2 对象
19.3.2.1 DatagramPacket
DatagramPacket:
数据容器
- 接收数据
- 接受数据的构造方法:
DatagramPacket rPacket = new DatagramPacket(byte[] buf,int offset,int length)
:创建个带缓冲区的用于接受数据的DatagramPacket对象rPacket = new DatagramPacket(bytes, 0, bytes.length);
- 常用方法
- packet.
getData()
:获取该数据容器里的数据,返回值是byte[]String str = new String(rPacket.getData());
- packet.
- 接受数据的构造方法:
- 发送数据
- 发送数据的构造方法
DatagramPacket sPacket = new DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)
:创建个带缓冲区的用于发送数据的DatagramPacket对象sPacket = new DatagramPacket(pool, 0, pool.length, InetAddress.getByName("127.0.0.1"), 50066);
- 常用方法
- packet.
setData(byte[] bytes)
:给该数据容器发送数据sPacket.setData("知道了".getBytes());
- packet.
- 发送数据的构造方法
19.3.2.2 DatagramSocket
- DatagramSocket:发送或接受DatagramPacket
- 构造方法
DatagramSocket socket = new DatagramSocket()
:系统会随机分配个端口号,然后根据这个端口号发送或接受数据DatagramSocket socket = new DatagramSocket(int port)
:指定端口号,然后根据这个端口号发送或接受数据
- 常用方法
- DatagramSocket.
receive(DatagramPacket)
:将接收到的数据放到DatagramPacket数据容器中- DatagramPacket指的是接受数据的数据容器
- 阻塞方法,只要没有DatagramPacket,就一直卡在这
socket.receive(rPacket);
- DatagramSocket.
send(DatagramPacket)
:将存放数据的DatagramPacket
数据容器发送出去- DatagramPacket指的是发送数据的数据容器
阻塞方法
,只要没有DatagramPacket,就一直卡在这
socket.send(sPacket);
- DatagramSocket.
close()
:释放资源socket.close();
- DatagramSocket.
20. 案例:类似于QQ的即时通信系统
20.1 两个项目包
20.1.1 Client包(客户端)
20.1.2 server包(服务端)
- 服务端:给每个客户端提供服务的,该程序永远不关机
20.1.3 分布式部署
- 分布式部署:有很多套代码,分别部署在不同的机子上,但是运行起来,我们从逻辑上认为是一套代码(一台机子)
20.2 通信原理
20.2.1 TCP通讯
- server端和client端之间的通讯是基于
TCP
进行通讯的 - 数据通信
- client端没有DB(没有完整的Model层),client端只有实体
- client端拿到数据后传给server端,server端进行处理(到数据库中进行验证),然后将处理的结果直接返回给client端
- 做事原理
- server端:操作数据库
- client端:收集数据打包上送,拿到结果拆包显示结果
- TCP通讯做的事:登录、注册、找好友、删好友等等
- severe端和client会有个持久的TCP通道
20.2.3 UDP通讯
- client端和client端之间的通讯是基于
UDP
进行通讯的 - UDP通讯
- client端第一次上线时会将自己的IP和ServerSocket的port发给server端,server端将其存放在数据库中
- 同时,server端也会将该client端所有好友的IP和ServerSocket的port发给该client端
- UDP通讯做的事
- client端与client端之间发消息
- 广播信息:server端对所有客户端点对点的群发消息
20.3 TCP通讯层次
20.3.1 Box布局管理器
- Box布局管理器:既能做布局管理器,也能做容器
- 获取方法
- 静态方法获取Box对象
- Box布局
- Box.
createHorizontalBox()
:横着布局(水平),水平方向一直放,返回值是Box - Box.
createVerticalBox()
:竖着布局(竖直),竖直方向一直放,返回值是Box
- Box.
- 常用方法
- Box.
add(组件/容器)
:向Box布局管理器中添加组件 - Box.
createVerticalBox(int height)
:设置竖直方向的空白区域 - Box.
createHorizontalBox(int width)
:设置水平方向的空白区域
- Box.
20.3.2 数据库连接
- 配置文件
- 将数据库的驱动信息、数据库、用户名、密码放在配置文件中,利用IO读取数据文件
- 将数据库的驱动信息、数据库、用户名、密码放在配置文件中,利用IO读取数据文件
- Java配置对象
- Java配置文件对象:
Properties对象
,用于加载配置文件(后缀名必须是.properties
) - 写法
- 将配置文件对象new出来:
Properties properties = new Properties();
- 加载配置文件
properties.load(DBUtil1.class.getResourceAsStream("/db.properties"));
DBUtil1.class.getResourceAsStream():加载该类所在的src路径
- 调用
Properties.getProperty(键)
:从而获取与键对应的值
- 将配置文件对象new出来:
- Java配置文件对象:
20.3.3 报文
- 报文:只要通讯双方涉及的业务是多种的就要用到报文(例如注册、登录两个不同的业务)
- 报文用处:识别业务、传输数据
- 报文形式
- 报文头:识别业务
- 报文体:要操作的资源