JavaSE
1. Java 基础
1. JDK、JRE
JRE | JDK | |
---|---|---|
含义 | java 运行环境(java) | java 开发工具(javac) |
内容 | java 核心类库 + JVM | JRE + 开发工具 |
位置 | 客户端装的 | 开发者(程序员)装的 |
java -version
:可以查看 JDK 的版本
配置环境变量
path
:文件路径
classpath
:类文件路径,规定 java JVM
在哪里去查找 class 文件
,如果没有配置就在当前目录下
查找,配置后就在配置后的目录下
查找
配置临时环境变量:
set path # 查看环境变量
set path=值 # 设置环境变量
set path=空格 # 删除环境变量
set path=新值;%path% # 在已有的环境变量,再加新值
set classpath=路径 # 在指定路径下查找
set classpath=路径; # 先在配置目录(指定路径)下查找,如果没有查找到,就在当前目录下查找
set classpath=.;路径 # 在当前路径和指定路径下查找
2. 标识符
概念:java 所有的名字(变量名,数组名,方法名,类名,接口名…)
名字定义规则:
- 组成 = 字母 + 数字 + 下划线(_) + 美元符号($)
- 不能以数字开头
- 严重区分大小写
- 不能是关键字:java 中具有特殊含义的单词(48 个)
- 不能是保留字:
goto
、const
3. 数据类型
3.1 基本数据类型(八大基本数据类型)
-
整型:所有没有小数点的数
数据类型 关键字 所占字节数 取值范围 字节型 byte 1 -128(2 的 7 次方)到 127(减-1) 短整型 short 2 -2 的 15 次到 2 的 15 次方减 1 整型 int 4 -2 的 31 次到 2 的 31 次方减 1 长整形 long 8 -2 的 63 次到 2 的 63 次方减 1 注意:
long 值
超过了int 范围
(2147483647)必须加 l 或者 L -
浮点型:所有有小数点的数
数据类型 关键字 所占字节数 注意 单精度 float 4 值后面必须加 f 或者 F 双精度 double 8 值后面加 d 或者 D -
字符型:所有的字符,只可以存储单个字符
数据类型 关键字 所占字节数 注意 字符型 char 2 值必须放在单引号(’’)里面 -
布尔型:所有产生两个相反的结果的数据
数据类型 关键字 所占字节数 注意 布尔类型 boolean 1 值只有两个 true 和 false
3.2 引用数据类型
- 数组
- 类(String 字符串类)
- 接口
3.3 数据类型的转换
精度排序(低到高):byte short char int long float double
自动转换(低转高):低精度类型 变量名 1=值; 高精度类型 变量 2=变量 1;
强制转换(高转低):高精度类型 变量名 1=值;低精度类型 变量 2=(低精度类型)
变量名 1;
byte+byte=int
short+short=int
4. 运算符
运算符 | |
---|---|
算术运算符 | + - * / %(求余) ++ – |
赋值运算符 | += -= *= /= %= |
比较运算符 | > >= < <= == != |
逻辑运算符 | & (与) 、| (或)、 ! (非)、 ^ (异或)、 && (双与,效率更高) 、||(双或) |
条件运算符(三目(三元)运算符) | boolean 条件 ? 条件为 true 时执行的语句 : 条件为 false 时执行的语句 |
位运算符 | <<(左移)、>>(右移)、>>>(无符号右移)、&(与) 、|(或) 、^(异或)、~(反码) |
位运算符
<<:左移几位其实就是该数据乘以 2 的几次方。可以完成 2 的次幂运算
如:3<<2=12 ==>3*2*2=12
>>:右移几位其实就是该数据除以 2 的几次方。对于高位出现的空位,原来高位是什么就用什么补这个空位
如:3>>1=1 ==>3/2=1
>>>:无符号右移,数据进行右移时,高位出现的空位,无论原高位是什么,空位都用 0 补
如:3>>>1=1 ==>3/2=1
^:一个数异或同一个数两次,结果还是这个数。如 6^3^3=6
5. java 语句
-
顺序结构:默认的执行流程
-
选择结构(分支结构)
-
if
-
单分支
if( boolean 条件 ){ } // boolean 条件为 true 是执行的语句
-
双分支
if( boolean 条件 ){ // boolean 条件为 true 是执行的语句 }else{ } // boolean 条件为 false 是执行的语句
-
多分支
if( boolean 条件 1 ){ // boolean 条件 1 为 true 是执行的语句 }else if( boolean 条件 2 ){ // boolean 条件 2 为 true 是执行的语句 }else if....{ // boolean 条件 n 为 true 是执行的语句 }else{ } // 以上条件均不满足时执行的语句
-
-
switch
// 注意:switch 表达式应为:byte、short、int、char、String switch(表达式){ case 常量 1: //如果表达式==常量 1 就执行的语句 [break;] case 常量 2: //如果表达式==常量 2 就执行的语句 [break;] case 常量 n: //如果表达式==常量 n 就执行的语句 [break;] default: //如果表达式和所有 case 都不相等执行的语句 }
-
-
循环结构
-
while
while(boolean 条件){ //循环执行的代码 }
-
do while
do{ //循环体 }while(boolean 条件);
do-while 和 while 的区别?
-
for
for(初始化表达式;boolean 条件;迭代){ //boolean 条件为 true 时循环执行的代码 }
-
-
局部代码块:可以定义局部变量的生命周期
{ // 局部代码块 // 代码 }
关键字 break 和 continue
break:跳出
语句(循环、switch)执行,应用于选择和循环结构
continue:结束本次
循环,继续
下一次循环,应用于循环结构
注意
:
-
这两个语句离开应用范围,存在是没有意义的
-
这两个语句
单独存在
,后面都不可以有语句,因为执行不到
6. 函数
-
函数结构:
// 修饰符 返回值类型 函数名(参数类型 参数名 1,参数类型 参数名 2,....) public static void main(String[] args) { // 执行语句(方法体) return 返回值; }
-
函数的重载(overload)
重载:在同类中,函数名相同,参数列表不同(顺序、个数,类型不同)即可,与返回值类型无关
7. 数组
数组的特点
- 是一种数据类型,是一种引用数据类型
- 数据类型申明的变量存储的是一个数组对象
- 数组对象是一个可以存储多数据的容器
- 数组对象可以是任何类型,但每个数据类型必须保持一致
- 每个数据称为一个元素
- 数组一旦创建,其长度不可变
数组的声明
-
先定义,后赋值
数据类型[] 数组名 = new 数据类型[长度]; // 定义数组 数组名[下标] = 值; // 给对应下标元素赋值
-
定义并赋值
数据类型 [] 数组名 = new 数据类型[长度]; 数据类型 [] 数组名 = new 数据类型[]{元素 1,元素 2,...,元素 n}; 数据类型 [] 数组名 = {元素 1,元素 2,...,元素 n}; //不规范,但常用
-
获得数组的长度
数组名.length
-
遍历(for/foreach)
-
元素有默认值
整型:0 float:0.0f double:0.0 char:空白字符
内存剖析
栈内存:存储的是局部变量
(既是方法中或局部代码块中的变量),而且变量所属的作用域一旦结束,该变量就自动释放
堆内存:存储的是数组和对象
(其实数组就是对象),凡是 new 建立的储存在堆
中

数组的最值、遍历、排序、查找
-
最值
-
方法1:定义变量记录较大的
值
public static int getMax(int[] arr){ int maxElement = arr[0]; // 初始化为数组中的任意一个元素。 for(int x=1; x<arr.length; x++) { if(arr[x]>maxElement) maxElement = arr[x]; } return maxElement; }
-
方法2:定义变量记录较大的
下标
public static int getMax(int[] arr){ int maxIndex = 0; //初始化为数组中任意一个角标。 for(int x=1; x<arr.length; x++){ if(arr[x]>arr[maxIndex]) maxIndex = x; } return arr[maxIndex]; }
-
-
遍历
public static void printArray(int[] arr){ System.out.print("["); for(int x=0; x<arr.length; x++){ if(x!=arr.length-1) System.out.print(arr[x]+", "); else System.out.println(arr[x]+"]"); } }
-
排序:其实
真正开发的时候
,一般用 Aarrys.sort(数组工具类
的排序方法)-
顺序排序
-
方法1
int a[]={5,2,3,1,4}; for(int i=0;i<a.length-1;i++){ // 控制比较的轮数 for(int j=i+1;j<a.length;j++){ // 控制本轮的比较次数 if(a[i]>a[j]){ // 如果前一个数大于后一个数 int temp=a[i]; // 将后一个数与前一个数交换位置 a[i]=a[j]; a[j]=temp; } } }
-
方法2:
减少换位的操作,此方法效率更高
public static void selectSort_2(int[] arr){ for(int x=0; x<arr.length-1; x++){ int num = arr[x]; int index = x; for(int y=x+1; y<arr.length; y++){ if(num>arr[y]){ num = arr[y]; index = y; } } if(index != x){ // 如果不等交换位置 int temp = arr[x]; arr[x] = arr[index]; arr[index] = temp; } } }
-
-
冒泡排序
int a[]={5,2,3,1,4}; for(int i=0;i<a.length-1;i++){ // 控制比较的轮数 for(int j=0;j<a.length-i-1;j++){ // 控制本轮的比较次数 if(a[j]>a[j+1]){ // 如果前一个数大于后一个数 int temp=a[j]; // 将后一个数与前一个数交换位置 a[j]=a[j+1]; a[j+1]=temp; } } }
-
-
查找
-
无序数组的查找
public static int getIndex(int[] arr,int key){ for(int x=0; x<arr.length; x++){ if(arr[x]==key) // 查到就返回 值 return x; } return -1; // 没查到返回 -1 }
-
有序数组的查找:折半/二分查找
真实开发
时,使用 Arrays.binarySearch 方法(数组工具类
的二分查找
)public static void main(String[] args) { int[] arr = {13,15,19,28,33,45,78,106}; // 有序数组 int index = halfSearch(arr,15); // 折半查找 int index1 = Arrays.binarySearch(arr,15);// 二分查找 System.out.println("index="+index+"index1="+index1); //存在返回的具体的角标位置,不存在返回-1 }
-
折半查找
-
方法1
public static int halfSearch(int[] arr,int key){ int min = 0; int max = arr.length-1; int mid = (max+min)/2; while(arr[mid]!=key){ if(key>arr[mid]) min = mid + 1; else if(key<arr[mid]) max = mid - 1; if(max<min) return -1; // 不存在返回-1 mid = (max+min)/2; // 重新赋中间值 } return mid; // 存在返回的具体的角标位置 }
-
方法2
public static int halfSearch_2(int[] arr,int key){ int mid; int min = 0; int max = arr.length-1; while(min<=max){ mid = (max+min)>>1; // 相当于 mid = (max+min)/2 if(key>arr[mid]) min = mid + 1; else if(key<arr[mid]) max = mid - 1; else return mid; } return -min-1; }
-
二分查找 :使用 Arrays.binarySearch 方法
-
-
多维数组
多维数组:如果一个数组的元素是数组,该数组是多维数组
二维数组:如果如果一个数组的元素是一维数组,该数组是二维数组
-
定义二维数组与赋值
数据类型 [][] 数组名 = new 数据类型[长度][长度]; // 定义 数组名[下标][下标]=值; // 赋值 数据类型 [][] 数组名 = {{数据 1,数据 2,数据 n},{数据 1,数据 2,数据 n}}; // 定义时就赋值
-
遍历(与一维数组遍历方法类似)
2. 面向对象(OOP)
1. 面向对象概述
类:由一群相同共性的对象组成的群体
对象:一切客观存在,可以相互区别的个体
对象和类之间的关联:对象是类的具体化,类是对象的抽象化(模板)
匿名对象:没有名字的对象 。其实就是对象的简写格式
- 当对象的
方法仅调用一次
的时候,就可以简化成匿名对象
- 匿名对象可以作为
实际参数
进行传递
1.1 类
类的格式:
修饰符 class 类名{
// 1.属性(成员变量、全局变量):描述类的信息
[修饰符] 数据类型 变量名[=初始值];
// 2.构造器 构造函数 构造方法 constructor 作用是:创建对象
[修饰符] 类名([参数列表]){//方法名必须和类名一致
//初始化代码
}
// 3.方法 行为 函数:让对象具有某种特定的功能
[修饰符] 返回值(void)方法名([参数列表]){
//行为代码
}
}
-
全局(成员)变量
和局部变量
的区别区别 成员变量 局部变量 存在位置 成员变量定义在类中 局部变量定义在方法中 默认值 成员变量有默认值 局部变量若使用必须赋初始值 修饰符 成员变量可以用 所有修饰符
修饰局部只能用 final 修饰符修饰 作用范围 成员变量能作用于整个类 局部变量只能作用于方法,语句,局部代码块中 存储位置 成员变量储存在堆内存的对象中 局部变量储存于栈内存的方法中 生命周期 随着 对象的创建
而存在,消失而消失随着 所属区域的执行
而存在,结束而释放 -
成员变量
和静态变量(类变量)
的区别区别 成员变量 静态变量 生命周期 随着对象的创建而存在,消失而消失 随着类的加载而存在,随着类的消失而消失 调用方式 成员变量只能被对象调用 静态变量可以被对象调用,还可被类名调用 存储位置 成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据 静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据 别名 成员变量也称为 实例变量
静态变量称为 类变量
-
普通方法
和构造器方法
的区别区别 构造方法 普通方法 格式 构造方法没有返回值 普通方法必须有返回值 作用 构造方法的作用是创建对象 普通方法的作用是让对象具有某种特定的功能 调用时机 创建对象时调用构造方法 普通方法必须在创建对象之后,用对象名来多次调用 默认值 一个类中没有构造器,系统会默认提供一个
无参构造器,当类中有构造器时,系统将不再提供 -
参数
- 概念:参数本质就是一个
特殊的局部变量
- 作用:调用方法时
动态的传入数据
- 传参:调用方法时,实参传递给形参
- 概念:参数本质就是一个
-
值传递和引用传递
- 值传递:对象被值传递了,意味着传递了对象的一个副本,该副本被改变不会影响源对象的值
- 引用传递:对象被引用传递了,意味着传递的引用(堆的地址),外部对象被改变,会影响源对象的值
-
返回值
-
无返回值:void
-
有返回值
[修饰符] 返回数据类型 方法名([参数列表]){ // 行为代码 return 值; // 结束方法并返回一条数据 }
-
1.2 几个关键字和修饰符
-
overload(重载):在同类中,方法名相同,参数列表不同(顺序、个数,类型不同)即可
-
override(重写 ):在子类中,方法名相同,返回值相同,参数列表相同(个数、类型、顺序)
当父类的方法不能满足子类的需求,
子类
对该方法重新定义,称为重写
注意:- 子类的访问修饰符必须 >= 父类
- 静态只能覆盖静态,或被静态覆盖(此处有错误:静态方法不能被重写,可以被继承)
overload
和override
的区别
-
this(当前对象)
- 调用成员变量:当成员变量名和局部变量名相同时,局部变量把成员变量隐藏起来,如果想调用成员变量,必须加
this. 成员变量名
- 调用构造器:如果当前的构造器无法完成初始化,需借助其他构造器帮助完成初始化,使用
this(参数)
,该句必须放在第一行
,该调用不会创建对象,只是借助方法来完成初始化
- 调用成员变量:当成员变量名和局部变量名相同时,局部变量把成员变量隐藏起来,如果想调用成员变量,必须加
-
super:就是 this 的父类对象
-
调属性:子类属性和父类属性相同时,想使用父类属性,使用
super.属性名
-
调方法:子类重写父类的方法,当子类中想调用该父类的方法,使用
super.方法()
-
调构造器:
super([参数])
,如果子类构造器没有调用父类构造器,默认调用父类无参构造器,调用构造器必须放在方法的第一行,this([参数])和 super([参数])不能共存注意:
- 每一个
子类的构造方法
在没有显示调用 super()系统都会提供一个默认的 super()
,super() 书写在第一行 - 可以在
子类构造方法中
显示调用 super([参数列表]),完成对特定父类构造方法的调用 - 当成员变量和局部变量重名的时候用 this 区分。
当父类和子类的成员变量重名
时用 super 区分。 - this 指代一个本类对象的引用。
super 代表一个父类空间。super 指向父类。
- 每一个
优先级:static > 父类 > 属性 > 构造器
-
-
static 修饰符:优先被加载,且执行一次
-
修饰变量:称为
类变量
,因为该变量是所有对象共享的变量调用:可以用
对象名
来调用,也可以用类名
调用注意:
静态变量
和实例变量(对象变量)
的区别? -
修饰方法:称为类方法,静态方法只能调用静态变量和静态方法
调用:可以用
对象名
来调用,也可以用类名
调用注意:
- .static 优先于对象存在,因为 static 的成员随着类的加载就已经存在了
静态方法只能访问静态成员
。(非静态既可以访问静态,又可以访问非静态)- 类方法不能使用 this 或 super,因为没有 this 实例可供使用
- static 修饰的
方法
不能被重写,可以被继承
,static和 abstract 不能共存
静态方法
和实例方法(对象方法)
的区别及静态变量
和实例变量(成员变量)
的区别
-
静态代码块:
static{ //代码语句:此代码仅执行一次 }
-
-
final 修饰符
-
final 修饰
变量
:变量就变成常量,只能赋值一次,一旦被赋值不能再次赋值
final 修饰的变量是一个常量,对所有的对象都一样,一般在 final 之前加上 static
常量所有字母都大写,多个单词中间用_连接
-
修饰
成员变量
:成员属性定义成常量必须赋初始
值,或在构造器中赋值
-
修饰
局部变量
:可以不赋初始值,但在方法结束之前
赋值(冗余代码) -
final 修饰
方法
:能继承但不能被重写 -
final 修饰
类
:不能有子类,即不可以被继承
-
1.3 代码块
-
静态代码块
-
随着
类的加载
而执行,而且只执行一次
-
作用:用于给类进行初始化(构造方法是对象进行初始化)
-
格式
static{ //代码语句: 给类进行初始化,此代码仅执行一次 }
-
-
构造代码块
-
类中
的独立代码块 -
特点:随着对象的创建而创建,只要
new
一个对象,就执行一次而
静态代码块
,随着类的加载
而加载,只执行一次 -
注意:
- 构造代码块:可以给所有对象进行
初始化
- 构造函数:是给对应的对象进行
初始化
- 构造代码块:可以给所有对象进行
-
格式:
class Person{ private String name; public Person(){ // 构造函数 System.out.println("给对应对象进行初始化") } { // 构造代码块 System.out.println("给所有对象进行初始化") } }
-
-
局部代码块
-
可以定义局部变量的生命周期
-
格式:
public void jubuFunction(){ { // 局部代码块 // 代码 } }
-
1.4 访问修饰符
作用域 | public | protected | default | private |
---|---|---|---|---|
同一类中 | ok | ok | ok | ok |
同一包中 | ok | ok | ok | |
子类中 | ok | ok | ||
不同包中 | ok |
2. 面向对象的三大特征
2.1 封装
- 概念:将类中的成员隐藏起来,只让可靠的对象访问
- 分类:属性封装、方法封装
- 规范:一般在编程时只封装属性
- 具体步骤:把属性设为 private,提供 public 的 getter 和 setter 方法来间接访问属性
- 封装的好处:将变化隔离,便于使用,提高了重用性和安全性
注意:
-
子类可以直接访问父类中的
私有成员
吗???不能,可以通过创建公共的访问方法来访问
-
私有化仅是
封装的一种体现
而已
2.2 继承
-
概念:现有的类派生出新的类,现有的类被称为父类,派生出来的类称为子类(派生类),子类能吸收父类的数据属性和行为,并有拓展自己新的能力
-
优缺点:
- 好处:提高了代码的复用性,让
类与类
之间产生了关系,给第三个特征多态提供了前提 - 缺点:
打破了封装性
- 好处:提高了代码的复用性,让
-
语法
[访问修饰符] class 子类类名 extends 父类类名{ //类的实体 }
-
继承范围
- 同包下,子类可以继承父类除私有的属性和方法
- 不同包下,子类可以继承父类 public 和 protected 的属性和方法
- 子类无法继承父类的构造器
- 子类最多只能有一个父类(单继承,多实现)
-
子类的创建过程(
new 子类()
)具体执行步骤:
- 系统会获取相应的父类构造器,创建一个父类对象
- 再使用子类构造器创建子类对象
- 最后将两个对象关联起来
提问:为什么
子类实例化
的时候要访问父类中的构造函数
呢?那是因为
子类继承了父类
,获取到了父类中内容(属性)
,所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。所以子类在构造对象时,必须访问父类中的构造函数,就在子类的构造函数中加入了 **super()**语句。- 如果父类中没有定义空参数构造函数,那么子类的构造函数必须用 super 明确要调用父类中哪个构造函数。
- 如果
子类构造函数中
如果使用this
调用了本类构造函数时,那么super 就没有
了,因为 super 和 this 都只能定义第一行且只能有一个。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。
2.3 多态
-
概念:一个事物多种形态,简单说,就是一个对象对应着不同类型
多态在
代码中
的体现:父类或者接口
的引用指向其子类的对象
-
优缺点:
- 优点:提高了代码的扩展性和重用性,前期定义的代码可以使用后期的内容
- 缺点:前期定义的内容不能使用(调用)后期
子类的特有内容
-
分类
方法
的多态:overload 和 override变量
的多态:使用父类声明的对象变量,即可以存储父类类型对象,也可以存储子类类型对象
-
变量多态的前提
-
继承 或 实现
-
方法重写
-
转换类型
- 向上转型:把一个子类类型的对象转化为父类类型,此操作会使变量拥有
只有父类没有被重写的方法及自己重写的方法
,会失去自己独有的属性和方法
- 向下转型:子类类型的对象由原来父类类型强制转化为原来的子类类型
多态的转型:类型判断-instance of(只能用于引用数据类型判断)
- 向上转型:把一个子类类型的对象转化为父类类型,此操作会使变量拥有
-
-
多态时,类中成员的变化特点
-
成员变量
编译时
:参考引用型变量所属的类
中的是否有调用的成员变量
:有,编译通过;没有,编译失败运行时
:参考引用型变量所属的类
中的是否有调用的成员变量
,并运行该所属类中的成员变量
简单说:编译和运行都看等号的左边
-
成员函数(非静态)
编译时
:参考引用型变量所属的类
中的是否有调用的函数
。有,编译通过,没有,编译失败运行时
:参考的是对象所属的类中是否有调用的函数
简单说:编译看左边,运行看右边。因为成员函数存在覆盖特性
-
静态函数
编译时
:参考引用型变量所属的类
中的是否有调用的静态方法
运行时
:参考引用型变量所属的类
中的是否有调用的静态方法
简单说,编译和运行都看等号的左边。其实对于静态方法,是不需要对象的。直接用类名调用即可
-
-
多态的理解
多态允许将子类的对象当做父类对象使用,某父类类型的引用指向其子类类型的对象,调用的方法是该子类的方法(前提是重写
),这里的引用和调用方法的代码
在编译前就已经决定了,而引用指向的对象
可以在运行期间进行多态
3. 抽象类
当一个类在描述事物的时候,如果没有足够的信息
来描述这个事物,这个类就声明为抽象类
- 抽象类:使用 abstract 修饰的类,就称为抽象类
- 抽象方法:使用 abstract 修饰的方法称为抽象方法,该方法没有方法体
- 抽象类的作用:约定和模板
- 什么时候定义抽象类?当无法确定该行为时,把该方法定义为抽象方法
注意:
-
一个类中如果有抽象方法,那么这个类一定为抽象类
-
有抽象方法的类一定是抽象类,抽象类不一定有抽象方法
-
普通的子类必须重写父类及父类继承的抽象方法,否则,这个子类还是抽象类
-
抽象类
不能实例化
,因为调用抽象方法没意义 -
抽象类中有构造函数,用于给子类对象进行初始化
-
抽象类一
定是个父类
,因为需要子类覆盖其方法后才可以对子类
实例化 -
抽象类可以不定义抽象方法,但是很少见,目的就是不让该类创建对象
通常这个类中的方法有方法体,但是却没有内容。AWT 的适配器对象就是这种类
abstract class Demo{ void show1(){} void show2(){} }
-
抽象关键字 abstract 不能和那些关键字共存
private
:抽象方法是要被子类重写,private 是私有化,不让重写static
:抽象方法是要被子类重写,没有方法体,而 static 方法不让重写final
:因为 final 的作用是不让子类重写
抽象类和一般类的异同点
相同点:抽象类和一般类都是用来描述事物的,都可在内部定了成
员。
不同:
区别 | 一般类 | 抽象类 |
---|---|---|
信息是否充足 | 有足够的信息描述事物 | 描述事物的信息有可能不足 |
能否有抽象 | 不能定义抽象方法,只能定非抽象方法 | 可定义抽象方法,也可以定义非抽象方法 |
能否被实例 | 可以被实例化 | 不可以被实例化 |
4. 接口
-
本质:比抽象类更抽象
-
定义语法
接口中成员的修饰符是固定的
[访问修饰符] interface 接口名{ //静态常量属性 public static final //抽象方法 public abstract //没有构造器 }
-
实现接口(单继承多实现 )
[访问修饰符] class 类名 implements 接口 1,接口 2,......{ //类的主体(必须实现接口的抽象方法) }
-
接口之间可以多继承
[访问修饰符] interface 接口名 extends 接口 1,接口 2,......{}
-
.注意
-
一个普通的类实现一个接口,
必须实现
接口及其父类的所有抽象方法 -
一个抽象类实现一个接口,可以不用重写接口的方法(抽象方法可以有抽象方法)
-
接口中的成员都是
公共的权限 public
-
接口成员修饰符是固定的,通常不写,系统会默认给加上,但是不能写成其它的修饰符
-
接口只能定义静态常量
-
类与类之间是继承关系,类与接口直接是实现关系
接口与接口之间是继承关系,而且接口可以多继承
-
-
接口作用:和抽象类一样,起到
约定和模板
的作用,达到了对类的扩展,实现多继承
的特点
接口与抽象类的区别?
区别 | 接口 | 抽象类 |
---|---|---|
结构 | 接口需要被实现,而且可以多实现 | 抽象类需要被继承,而且只能单继承 |
方法类型 | 接口中只能定义抽象方法 ,必须由子类去实现,接口中的成员必须由固定的修饰符 | 抽象类中可以定义抽象方法 和非抽象方法 ,子类继承后,可以直接使用非抽象 方法 |
关系 | 接口的实现是 like a 关系,在定义体系额外功能 | 抽象类的继承是 is a 关系,在定义体系基本共性内容 |
5. 内部类
5.1 内部类
内部类:先实例化外部类,再用外部类实例化内部类
-
分析事物时,发现该事物描述中还有
事物
,而且这个事物还在访问被描述事物的内容
,这时就把还有的事物
定义成内部类。一般用于类的设计。 -
内部类编译的结果有两个字节码文件:
Outer.class
,Outer$Inner.class
class Outer{ // 外部类 class Inner{ // 内部类 } }
-
内部类访问特点
-
内部类可以
直接访问外部类中的成员
,包括私有成员
-
外部类要访问内部类,必须
先建立
内部类的对象为什么内部类能直接访问外部类中成员呢? 那是因为
内部类
持有了外部类的引用
(外部类名.this)class Outer{ // 外部类 private int num =31; class Inner{ // 内部类 void show(){ System.out.println("外部类的变量 num ="+ num); //可以访问外部类的成员 } } public void method(){ Inner in = new Inner(); // 外部类要访问内部类,必须先创建内部类的对象 in.show(); } }
-
-
注意
- 如果内部类是静态的,相当于一个外部类
- 如果
内部类
中定义了静态成员,该内部类也必须是静态的
-
内部类的声明(实例)
-
直接访问外部类中的内部类中的成员(这种格式不多见,因为一般内部类定义为私有的)
public static void main(){ //先 new 外部类,再用外部类 new 内部类,相当于是对象.属性 Outer.Inner in = new Outer().new Inner(); in.show(); }
-
如果内部类是静态的,相当于一个外部类
//相当于类名.属性,因为是静态的,对象一加载就存在,可正常 new Outer.Inner in = new Outer.Inner(); in.show();
-
如果内部类是静态的,
成员是静态的
Outer.Inner.function();
-
5.2 局部内部类
局部内部类:内部类可以存放在局部位置
上
内部类在局部位置
上只能访问局部中被 final 修饰的局部变量,因为 final 定义的局部变量相当于一个常量,延长了其生命周期
,使得方法在消亡时,其内部类仍然可以访问该变量
注意:方法中
的局部内部类
不允许有访问修饰符,和局部变量
相同
5.3 匿名内部类
匿名内部类:内部类的简写格式,其实就是一个匿名子类对象
必须有前提:内部类必须继承外部类或者实现一个外部接口(在 new 的时候去重写
或者实现
类的方法)
格式:new 父类 or 接口(){子类内容}
父类 变量名 = new 父类(){子类内容};
接口 变量名 = new 接口(){子类内容};
abstract class Demo{
abstract void show();
}
class Outer{
int num = ;
class Inner extends Demo{
void show(){
System.out.println("外部类的变量 num ="+ num);
}
}
public void method(){
// new Inner().show();
new Demo(){ //匿名内部类
void show(){
System.out.println("外部类的变量 num ="+ num);
}
}.show();
}
}
public class InnerClassDemo{
public static void method(String[] args){
new Outer().method);
}
}
通常的使用场景:
- 当函数参数是
接口类型
时,而且接口中的方法不超过三个
- 可以用
匿名内部类
作为实际参数进行传递
5.4 静态内部类
使用方法,跟外部类一样
6. 单例设计模式
解决的问题:保证一个类
在内存中的对象唯一性
对于多个程序
使用同一个配置信息对象时,就需要保证该对象的唯一性
注意:在单线程下,单例设计模式是安全的,而在多线程下,单例设计模式是不安全的
如何保证对象唯一性?
- 不允许其他程序用 new 创建该类对象(私有化该类构造函数)
- 在该类创建一个本类实例(在本类中 new 一个本类对象)
- 对外提供一个方法让其他程序可以获取该对象(定义一个
公有的方法
,返回创建的对象)
6.1 饿汉式
开发时常用
特点:类一加载,对象就已经存在了
class Single{
private static Single s = new Single(); // 通过 new 在本类中创建一个本类对象
private Single(){} // 私有化该类构造函数
public static Single getInstance(){ // 定义一个公有的方法,将创建的对象返回
return s;
}
}
6.2 懒汉式
面试时常用
特点:类加载时,没有对象,只有调用了 getInstance
方法时,才会创建对象,是延迟加载形式。
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s==null)
s = new Single();
return s;
}
}
3. 异常
异常:程序在编译
或者运行
时发生的意外
1. 异常的体系
Throwable:无论是错误(Error),还是异常(Exception),问题发生就应该可以抛出,让调用者知道并处理。
该体系的特点:就在于 Throwable
及其所有的子类
都具有可抛性。
throws、throw:凡是可以被这两个关键字所操作的类和对象
都具备可抛性
区别 | throws | throw |
---|---|---|
位置 | 使用在函数上 | 使用在函数内 |
抛出内容 | 抛出的是异常类 ,可以抛出多个,用逗号隔开 | 抛出的是异常对象 |
(Throwable)不正常情况分成了两大类
-
一般不可处理的:Error
特点:是由
JVM 抛出的严重性
的问题,这种问题发生一般不针对性处理,直接修改程序
,如:内存溢出 -
可以处理的:Exception
-
编译期异常(非运行时异常/检查异常)-----Exception 以及其子类(除
RuntimeException
)在
编译时
就进行检测,让这种问题有对应的处理方式,这样的问题都可以针对性的处理
。常见:
ParseException
解析异常ClassNotFoundException
找不到类
-
运行时异常(未检查异常)-----RuntimeException
异常发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。这种异常
一般不处理
,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。常见:
NullPointerException
空指针ArrayIndexOutOfBoundsException
数组下标越界StringIndexOutOfBoundsException
字符串下标越界ClassCastException
类型转换异常ArithmeticException
算术异常
注意:
- 编译期异常:不能避免,必须处理,否则编译不通过
- 运行时异常:可以不用处理
- 如果程序产生
异常没有得到处理
,那么程序将会不能通过编
译或者终止运行
-
2. 异常处理方式
-
抛:throws
[修饰符] 返回值 方法名(参数列表) throws 异常类
-
捕捉:try_catch
try{ //可能出现异常的语句 }catch(异常对象 1 对象名 1){ //处理方式 }catch(异常对象 2 对象名 2){ //处理方式 }finally{ //始终会执行 }
-
声明:throw
throw new 异常类型();
异常处理的原则
- 如果抛出需要编译时异常,那么
函数上
必须要声明
,否则必须在函数内
用 try_catch 捕捉,否则编译失败 - 如果调用到了
声明
异常的函数,要么 try_catch 要么 throws,否则编译失败 - 功能内容
可以解决,用 catch
;解决不了,就 throws 告诉调用者
,由调用者解决 - 一个功能如果
抛出了多个异常
,那么调用时,必须有对应多个 catch
进行针对性的处理 - 内部
有几个需要检测的异常,就抛几个异常
,抛出几个,就 catch 几个
3. 自定义异常
- 继承于 Exception
- 继承于 RuntimeException
注意:
子类
重写父类
异常方法,则子类抛出的异常<=
父类异常,即是子类不能抛出比父类大的异常- 父类抛出多个异常,子类只能抛出父类异常的子集
- 如果父类的方法
没有抛出
异常,那么子类覆盖时绝对不能抛
,就只能try
4. 线程
**java.lang.Thread **
1. 基本概念
进程:正在进行中的程序,是操作系统的调度单位
,是一种系统资源
,有独立的地址空间
线程:线程是进程中的一个独立调度执行的任务单元
多线程:一个进程中多条执行路径
多线程的优缺点:
- 优点:解决了多个线程
同时运行问题
,充分利用 CPU 的资源 - 缺点:
线程太多
会导致效率降低
注意:
- 一个程序运行至少有一个进程,一个进程至少有一个线程
- 线程比进程更容
易调度
- 线程的
创建
比进程的开销小
- 线程之间的
切换
也比进程开销小
每个线程都有自己的堆栈
,程序计数器和自己局部变量,多线程间能共享数据
- 多线程提高了
并发性
,有效的利用闲置的 cpu 的资源 - JVM 启动时就启动至少有两个线程:主线程 main,垃圾回收线程 GC
2. Thread 类
-
属性
MAX_PRIORITY // 最高优先级 10 MIN_PRIORITY // 最低优先级 1 NORM_PRIORITY // 默认优先级 5
-
构造器
Thread(); Thread(String name); Thread(Runnable runab); Thread(Runnable runab,String name);
-
方法
Thread currentThread(); // 返回当前运行的线程 String getName(); // 获得线程的名字 void run(); // 存放具体操作的代码 void start(); // 使线程开始启动,Java 虚拟机去调用 run 方法 void sleep(long miliss); // 休眠 void setDaemon(); // 设置为守护线程(即是后台线程) void setPriority(); // 设置线程优先等级 void join(); // 让某线程先执行 void yield(); // 暂停线程 void interrupt(); // 让某线程中断执行,若是冻结状态的,可使冻结转换为运行
3. 实现线程的方式
-
继承 Thread(不常用)
- 定义一个类
继承 Thread
类 - 覆盖 Thread 类中的
run
方法 - 直接创建 Thread 的
子类对象
创建线程 调用 start 方法
开启线程并调用线程的任务 run 方法执行
- 定义一个类
-
实现 Runnable (常用)
- 定义一个类
实现 Runnable
接口 - 覆盖接口中的
run
方法,将线程的任务代码封装到 run 方法中 - 通过 Thread 类创建线程对象,并将 Runnable 接口的子类对象作为 Thread 类的构造函数的参数进行传递
- 调用线程对象的
start
方法开启线程
实现 Runnable 接口的好处:
- 将
线程任务
从线程的子类中分离出来
,进行了单独的封装,按照面向对象的思想将任务的封装成对象
- 避免了 java
单继承局限性
。所以较为常用
- 定义一个类
4. 线程状态
- 初始化状态:创建线程对象的时候
- 可运行状态:对象调用 start()方法时,进入 cpu 队列中进行排队
- 运行状态:cpu 运行线程时
- 阻塞状态:线程放弃运行,不在队列中
sleep
方法:当前线程阻塞,休眠,这时候该线程不在队列中,并且释放 cpu 的资源给其他线程,时间结束之后重新开始排队join
方法:当前线程调用其他线程,当前被阻塞,直到另一个线程运行完毕当前线程才开始重新排队wait
方法:当前线程阻塞,并放弃该对象的锁,直到被唤醒才重新排队
- 终止状态:线程运行完毕
wait 和 sleep 的区别
不同点 | wait | sleep |
---|---|---|
所属类 | java.lang.Object | java.lang.Thread |
是否释放锁 | 释放 | 不释放 |
是否执行权 | 释放执行权 | 释放执行权 |
唤醒时机 | 等待被 notify 唤醒 | 到一定的时间自动唤醒 |
使用范围 | 同步方法 或者 同步块 | 所有地方 |
5. 线程并发
当运行一个程序,启动了多个线程,这些线程就进入 CPU 队列
中进行排队,争夺 cpu 的执行权
,但 cpu 不会一直执行某个线程,只是在某个时间点上运行一个线程,cup 会计算队列里面的线程的优先级
,选取线程优先级最高的线程运行,cpu 在运行时某个线程会分配时间片,
时间片到期就结束,该线程就暂停运行,该线程又进行重新排队,在某个时间,多个线程在 cpu 上交替运行
,这就叫线程并发
并发:当前多个线程并发执行,可能会出现争夺同一个资源,导致数据不一致。
-
解决思路
:就是将多条操作共享数据
的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算 -
解决办法
:-
同步 synchronized
同步的前提:同步中必须有
多个线程
并使用同一个锁
-
同步方法(锁:是固定的 this)
public synchronized void method(){ //要同步的代码 }
-
静态同步函数(锁:是
该函数
所属字节码文件对象,可用getClass
获取,也可用当前类名.class
表示)public static synchronized void method(){ //要同步的代码 }
-
同步代码块(锁:是任意的对象)
public void method(){ synchronized(锁对象[只能是引用数据类型]){ //代码块 //要同步的代码 } }
同步的优缺点:
- 优点:解决了
线程的并发安全
问题 - 缺点:相对
降低了效率
,因为同步的线程的都会判断同步锁
-
-
Lock 锁
所在包:java.util.concurrent.locks
jdk1.5 以后将同步 和 锁封装成了对象,并将操作锁的
隐式
方式定义到了该对象中,将隐式动作变成了显式动作两个重要的接口
-
Lock 接口:出现替代了
同步代码块
或者同步函数
,将同步的隐式锁
操作变成显式锁
操作,更为灵活,可以在锁上加上多组监视器。 -
Condition 接口:出现替代了 Object 中的
wait
、notify
、notifyAll
方法,将这些方法进行了封装,变成 Condition 监视器对象,锁可以进行任意组合,一个锁可以挂多个监视器await() <==> wait() signal() <==> notify() signalAll() <==> notifyAll()
Lock 简单应用
// 只是一个简单格式,代码不完整 public class Resource{ Lock lock = new ReentrantLock(); // 创建一个锁对象 Condition Condition = lock.newCondition();// 通过锁对象获取该锁上的监视器对象 public void set(String name){ lock.lock();// 加锁 try{ while(true){ Condition.await();//等待 //中间执行代码 Condition.signalAll();// 唤醒所有 } }finally{ lock.unlock();//解锁 } } }
-
-
死锁:自己掌握对方等待的资源,而对方有掌握了自己等待的资源
6. 线程间通信
线程间通讯:多个线程在处理同一资源
,但是任务却不同
通信机制:等待/唤醒机制
-
使用方法有
wait() // 让线程处于冻结状态,被 wait 的线程会被存储到线程池中。 notify() // 唤醒线程池中任意一个线程。 notifyAll() // 唤醒线程池中的所有线程
这些方法都必须定义在同步中,因为是用于操作线程状态的方法,必须要明确操作的是
哪个锁上的线程
为什么操作线程的方法
wait
、notify
、notifyAll
定义在了 Object 类中?因为这些方法是监视器的方法。监视器其实就是锁。锁可以是
任意的对象
,任意的对象调用的方式一定定义在 Object 类中 -
生产者/消费者问题
- 要使用 while 来判断标记
- 使用 notifyAll 来会唤醒对方线程
-
停止线程的方法
-
定义停止标签(常用)
任务中都会有
循环结构
,只要控制住循环就可以结束任务,控制循环通常就用定义标记
来完成 -
Interrupt(不建议使用)
如果线程处于了
冻结状态
,无法读取标记
。可以使用 interrupt() 方法让 wait() 的线程从冻结状态
强制恢复到运行状态
中来,让线程具备 cpu 的执行资格。但是强制动作会发生了 InterruptedException
,记得要处理
-
5. Java 常用类库
类库:是 java 语言提供的,编写好的一些类,每个都具有一定特殊的功能性的类
java.lang 下类都不需要导包
类库学什么?
- 有哪些类
- 这个类有什么属性 ,什么构造器 ,什么方法
- 这些属性有什么作用,表示什么意思
- 这些方法的参数是什么意思,返回值又什么意思,有什么作用
1. Object 类
java.lang.Object
-
构造器:Object();
-
方法
equals(Object o); // 比较两个对象是否相等,比较的是对象的地址 getClass(); // 返回当前运行的类,能够在运行时知道当前对象所在的类 hashCode(); // 返回对象的哈希码值 toString(); // 把一个对象作为一个字符串返回 , // 该字符串=getClass().getName()+@+十六进制的 hashCode()
- equals:比较的是
对象的地址
。在实际应用中,可能不需要比较地址,可能需要比较两个对象的某些属性是否相等,所以说在自定义的类中通常覆盖 equals 方法 - toString:将当前对象的信息用字符串描述,但默认返回的是对象名加十六进制的哈希地址,没有实际意义,因此大多数情况下,在子类中,要覆盖 toString 方法
- equals:比较的是
2. String 类
java.lang.String
String 类的特点:字符串对象一旦被初始化
就不会被改变
-
构造器
String(); String(String str); // 把一个字符串常量转化为一个字符串对象 String(char[] c); // 把一个字符数组转化为字符串对象 String(byte[] b); // 把一个字节数组转化为字符串对象
-
方法
-
获取
int length(); // 获取字符串中字符的个数(长度) char charAt(int index) // 给一个下标,返回该下标出的字符 int indexOf(int ch) int indexOf(int ch,int fromIndex) int indexOf(String str); // 查找 str 首次出现的下标,没有返回-1 int indexOf(String str,int fromIndex) int lastIndexOf(int ch) int lastIndexOf(int ch,int fromIndex) int lastIndexOf(String str); // 查找 str 最后一次出现的下标,没有返回-1 int lastIndexOf(String str,int fromIndex); int codePointAt(int index) // 返回指定下标处的字符 UNicode 编码值 int codePointBefore(int index) // 返回指定索引之前的字符(Unicode 代码值) String substring(int index); // 字符串从 index 开始截取到最后 String substring(int start,int end); // 字符串从 index 开始截取到 end
-
转换
String[] split(String regex); // 用str拆分字符串,涉及到正则表达式 // 如果str作为开始,则str之前的空白也作为一个元素 // 如果str作为结尾,则str之后的空白不作为一个元素 String replace(String oldstr,String newstr); // 替换 String replaceFirst(String oldstr,String newstr); // 替换第一个 String toLowerCase(); // 大转小 String toUpperCase(); // 小转大 String concat(String str) // 将指定字符串连接到此字符串的结尾。 String trim(); // 去掉首尾空白 byte[] getBytes(); // 把一个都字符串对象转化为字节数组 char[] toCharArray(); // 把字符以字符数组返回 String valueOf(任何类型); // 把任何类型的数据转化为一个字符串对象
-
判断
boolean equals(Object obj); // 两个字符串内容是否相同 boolean equalsIgnoreCase(string str);// 忽略大写比较字符串内容 boolean contains(String str); // 是否包含 str 指定的字符串 boolean endsWith(String str); // 是否以什么结束 boolean startsWith(String str); // 是否以什么开始 boolean isEmpty(); // 字符串对象的 length 是否为 0
-
比较
int compareTo(String anotherString) // 按字典顺比较字符串的大小 int compareToIgnoreCase(String str) // 按字典顺序比较两个字符串,不考虑大小写 string intern() // 对字符串池进行操作的
-
3. StringBuffer/StringBuilder
java.lang.StringBuffer/StringBuilder
StringBuffer
和 StringBuilder
:前者比后者多一个 synchronized 线程同步,故线程安全,后者不安全
注意:String
和 StringBuffer
及 StringBuilder
的区别?
StringBuffer(线程安全)
StringBuffer:字符串缓冲区,用于储存数据的容器
特点:
长度的可变
的- 可以存储不同类型数据
- 最终要转成
字符串
进行使用 - 可以对
字符串进行修改
jdk1.5 以后出现了功能和 StringBuffer
一模一样的对象,就是 StringBuilder
不同的是:
- StringBuffer 是线程
同步
的,通常用于多线程 - StringBuilder 是线程
不同步
的,通常用于单线程,它的出现提高效率
StringBuffer 的使用
-
构造器
StringBuffer(); // 构造一个不带字符的缓冲区,默认初始容量为 16 个字符 StringBuffer(int i); // 构造一个不带字符,但具有指定初始容量的字符串缓冲区。 StringBuffer(String str); // 构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容
-
方法:大多数与 String 类一致,不一致的有
// 增: StringBuffer append(boolean | char | int ....); // 拼接在末尾 StringBuffer insert(int index,boolean|char|int....) // 在指定的下标处插入字符 // 删: StringBuffer delete(int start,int end); // 删除[start,end)区间的字符 StringBuffer deleteCharAt(int index); // 删除指定下标处的字符 // 改: StringBuffer replace(int start,int end,String srt); // 替换 StringBuffer reverse(); // 反转 void setCharAt(int index,char ch); // 修改指定下标除的字符 // 查: char charAt(index); // 查找下标为 index 的字符 int indexOf(string); // 查找 string 的第一次出现的下标 int lastIndexOf(string); // 查找 string 的最后一次出现的下标
4. 包装类
java.lang.Number
4.1 Number 类
-
构造器
Number();
-
方法
byte byteValue(); // 将 Number 类型转化为 byte 类型 short shortValue(); // 将 Number 类型转化为 short 类型 //其他的数字类型的Value()方法为抽象方法,在其子类中实现的
4.2 包装类
包装类:是 Number 类的子类
为了方便操作基本数据类型值
,将其封装成了对象,在对象中定义了属性和行为丰富了该数据的操作
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
char | Character |
拆箱:将包装类型
转化成基本类型
装箱:将基本类型
转换成包装类型
Integer
-
属性
static MAX_VALUE int // 类型的最大值 static MIN_VALUE int // 类型的最小值
-
构造器
Integer(int value); // 把一个 int 的基本数据类型转化为 Integer 对象 Integer(String number); // 把一个 String 字符串类型转化为 Integer 对象
-
基本类型—>字符串
-
基本类型
数值+"" -
用 String 类中的静态方法
valueOf
static String valueOf(基本类型数值); // 将基本类型数值转为 String
-
-
方法(延伸至所有包装类)
-
将
字符串类型或基本类型
===>包装类型
static Integer valueOf(int i); // 把一个 int 的基本数据类型转化为 Integer对象 static Integer valueOf(String i); // 把一个 String 字符串类型转化为 Integer对象
-
将
包装类型或字符串类型
===>基本类型
只有
Character
没有parse 方法
int intValue(); // 将包装类型 Integer 转换为基本类型 int byte byteValue(); // 将包装类型 Byte 转换为基本类型 byte static int parseInt("intstring"); // 使用 Integer 类的 parseInt static long parseLong("longstring"); // 使用 Long 类的 parseLong static boolean parseBoolean("booleanstring"); // 使用 Boolean 类的 parseBoolean
-
将
基本类型
===>字符串类型
-
基本类型
数值+"" -
用 String 类中的静态方法
valueOf
static String valueOf(基本类型数值); // 将基本类型数值转为 String
-
-
整数具备不同的进制体现
-
十进制–>其他进制
static String toBinaryString(int i); // 转换为二进制 static String toOctalString(int i); // 转换为八进制 static String toHexString(int i); // 转换为十六进制
-
其他进制–>十进制
static int parseInt("string",radix); // 将字符串转化为radix的进制的类型 static long parseLong("string",radix); // 将字符串转化为radix的进制的类型
-
-
Character 常用方法
static boolean isDigit(char ch); // ch 是否为数字
static boolean isLetter(char ch); // ch 是否为字母
static boolean isUpperCase(char ch); // ch 是否为大写字母
static boolean isLowerCase(char ch); // ch 是否为小写字母
5. Math 类
java.lang.Math Math 类
中的方法是静态的
-
属性
E // 自然底数 PI // 圆周率
-
方法 static
double abs(double a); // 绝对值 double ceil(double a); // 向上取整 double floor(double a); // 向下取整 double rint(double a); // 四舍五入,点五取整 double round(double a); // 四舍五入 long round(double a); // 返回最接近参数的 long 值 int round(float a); // 返回最接近参数的 int 值 double random(); // [0,1) double pow(double a, double b);//返回第一个参数的第二个参数次幂的值
6. System 类
java.lang.System System 类
中的方法和属性都是静态的
-
属性
err // 错误打印输出流 out // 输出流 java.io.PrintStream in // 输入流 java.io.InputStream
-
方法
void exit(int status); // 终止 Java 虚拟机 ,停止运行,参数非零非正常终止 long currentTimeMillis(); // 获取基准时间到现在的毫秒值 void setProperty(String key, String value); // 设置系统属性信息,信息是全局,其他程序都可用 String getProperty(“属性”); // 获取系统属性信息 Properties getProperties(); // 获取系统的属性信息,并存储到了 Properties 集合
Properties 集合的使用方法
Properties prop = System.getProperties(); // 获取系统的属性信息,并存到 Properties Set<String> nameSet = prop.stringPropertyNames();// 得到所有键名 for(String name : nameSet){ String value = prop.getProperty(name); // 根据键得到值 System.out.println(name+"::"+value); }
7. Date 类
java.util.Date
-
构造器
Date(); // 返回本机的时间 Date(long L); // 把毫秒数 L 转化为 date 对象 (基准时间+毫秒值)
-
方法
boolean after(Date d); // 是否比指定时间晚 boolean before(Date d); // 是否比指定时间早 int compareTo(Date d); // 比较两个时间顺序 (值为 1 0 -1) long getTime(); // 时间毫秒值 void setTime(long L); // 设置毫秒值
-
日期对象和毫秒值之间的转换
-
毫秒值
===>日期对象
- 通过 Date 对象的构造方法:
new Date(timeMillis);
- 通过 Date 对象的 setTime 方法来设置:
void setTime(long L);
- 通过 Date 对象的构造方法:
-
日期对象
===>毫秒值
通过 Date 对象的 getTime 方法来获得
-
-
日期对象和字符串之间的转换
-
将
日期对象
===>日期格式的字符串
,对日期对象进行 format 编码DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG); dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG); dateFormat = new SimpleDateFormat("yyyy--MM--dd"); String str_date = dateFormat.format(date);
-
将日期格式的
字符串
===>日期对象
,对日期对象进行 parse 解码DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG); dateFormat = new SimpleDateFormat("yyyy---MM---dd"); Date date = dateFormat.parse(str_date);
-
-
8. Calender 类
java.util.Calendar
-
属性
YEAR // 年 MONTH // 月 DATE // 日 HOUR 12 // 小时制 HOUR_OF_DAY // 24 小时制 MINUTE // 分 SECOND // 秒 DAY_OF_MONTH DAY_OF_WEEK DAY_OF_YEAR WEEK_OF_MONTH WEEK_OF_YEAR
-
方法
Calendar getInstance(); // 获得一个日历对象 int get(属性) // 获得属性的值 void set(属性,int value) // 设置属性的值 Date getTime(); // 把一个日历对象转化为日期对象 void setTime(Date date); // 把一个日期转化为一个日历对象 long getTimeInMillis(); // 获得毫秒值 void setTimeInMillis(long L); // 设置毫秒值 void set(int year,int month,int date) void set(int year,int month,int date,int hour,int minute,int second); void add(属性,int value); // 给指定的字段添加值,value 可以为负
9. 格式化转换器
java.text.DateFormat/NumberFormat
-
DateFormat:日期格式器
SimpleDateFormat:把一个日期格式化
-
构造器
SimpleDateFormat() ; SimpleDateFormat(String datestr);// 指定格式
-
特殊字符(格式)
y // 年 M // 月 d // 日 H // 0-23 h // 1-12 m // 分 s // 秒 E // 周
-
方法
String format(Date date); // 把日期按指定格式化解析成一个字符串 Date parse(String str); // 把一个字符串按指定格式解析成一个日期对象
-
-
NumberFormat:数字格式器
DecimalFormat
-
构造器
DecimalFormat(); DecimalFormat(String str);
-
特殊字符
0 // 数字 是 阿拉伯数字 # // 数字 是 阿拉伯数字,如果不存在则显示为 0 . // 数字 是 小数分隔符或货币小数分隔符 - // 数字 是 减号 % // 前缀或后缀 是 乘以 100 并显示为百分数
-
方法
String format(Number number); // 把数字按指定格式化解析成一个字符串 Number parse(String str) // 把一个字符串按指定格式解析成一个数字
-
6. 集合
-
为什么会出现集合?
对象是为了
封装特有的数据
,对象多了需要存储,如果对象的个数不确定
,就用集合来存 -
数组和集合同是容器,他们有什么区别?
容器最基本的功能:添加,删除
- 数组可以存储对象和基本数据类型,而集合只能存储对象
- 数组的长度是固定的,集合的长度是可变的
集合的特点:
- 用于存储对象的容器
- 集合的长度是可变的
- 集合中不可以存储基本数据类型
1. Collection(接口)
java.util .Collection
常用方法:
-
添加
boolean add(Object obj) // 添加一个对象 boolean addAll(Collection coll) // 添加一堆对象
-
删除
boolean remove(Object obj) // 移除一个对象 boolean removeAll(Collection coll) // 移除一堆对象 void clear() // 清空集合
-
判断
boolean contains(Object obj) // 是否包含这个对象 boolean containsAll(Collection coll) // 是否包含一堆对象 boolean isEmpty() // 判断集合是否为空
-
获取
int size() // 获取集合的长度 Iterator iterator() // 返回在此 collection 的元素上进行迭代的迭代器
-
其他方法
boolean retainAll(Collection coll) // 取两个集合的交集 Object[] toArray(); // 将集合转成数组
Iterator 接口:是对所有的 Collection 集合
进行元素取出
的公共接口,只要是 Collection 容器
,就可以用 Iterator 接口
去取
- 该对象必须依赖于
具体容器
,因为每一个容器的数据结构都不同
,所以该迭代器对象是在容器中进行内部
实现的。对于使用容器而言,具体的实现不重要,只要通过容器获取该实现的迭代器的对象即可。
2. List(接口)
java.util .List
特点:List 有序,可重复(取出和存入的顺序一致,元素都有索引(角标)
,元素可以重复)
常用方法:
-
添加
boolean add(Object obj); void add(int index, Object obj) // 在列表的指定位置插入指定元素(可选操作)
-
删除
Object remove(int index) // 移除列表中指定位置的元素。 boolean remove(Object o) // 从此列表中移除第一次出现的指定元素(如果存在)
-
修改
Object set(int index, Object obj) // 用指定元素替换列表中指定位置的元素(可选操作)
-
获取
Object get(int index) // 返回列表中指定位置的元素。 int indexOf(Object obj) // 返回此列表中第一次出现的指定元素的索引;如果不存在,则返回 -1 int lastIndexOf(Object obj) // 返回此列表中最后出现的指定元素的索引;如不存在,则返回 -1 List subList(from,to); ListIterator listIterator();//获取列表迭代器对象,它可以实现在迭代过程中完成对元素的增删改查
注意:只有 list 集合具备 ListIterator 迭代功能
List 接口的实现类
-
Vector:数组实现,是
同步
的,线程安全
的,查询很慢 -
ArrayList:
数组
实现,是不同步
的,线程不安全
的,查询的速度快,替代了 Vector -
LinkedList:
链表
实现,是不同步
的,线程不安全
的,增删元素的速度很快
ArrayList 和 LinkedList 比较:
不同点 | ArrayList | LinkedList |
---|---|---|
内部结构 | 数组 | 链表 |
是否可存null | 可以存 null | 可以存 null |
是否同步 | 不同步 | 不同步 |
特点 | 适合遍历和查找,不适合添加和删除 | 不适合遍历和查找,适合添加和删除 |
适合场景 | 查询速度较快,增删速度比较慢 | 查询速度比较慢,增删速度较快 |
LinkedList
特有的常用方法(jdk 1.6 之前):
- addFirst(); addLast();
- getFirst(); getLast(); // 获取但不移除,如果链表为空,抛出 NoSuchElementException
- removeFirst(); removeLast(); // 获取并移除,如果链表为空,抛出 NoSuchElementException
LinkedList
特有的常用方法(jdk 1.6 之后):
- offerFirst(); offetLast();
- peekFirst(); peekLast(); // 获取但不移除,如果链表为空,返回 null
- pollFirst(); pollLast(); // 获取并移除,如果链表为空,返回 null
3. Set(接口)
java.util .Set
特点:Set 无序,不可重复
**常用方法:**和 collection
接口的方法一样
boolean add(E e); // 添加元素
boolean addAll(Collection<? extends E> c); // 添加多个元素
boolean remove(Object o) // 移除某个元素
void clear(); // 移除所有元素
int size(); // 集合长度
boolean equals(Object o) // 比较指定的对象与列表是否相等。
boolean isEmpty(); // size==0
boolean contains(Object o); // 是否包含
boolean containsAll(Collection<?> c); // 是否包含所有元素,则返回 true。
Iterator<E> iterator() // 返回在此 collection 的元素上进行迭代的迭代器。
Object[] toArray() // 将 set 集合转换成 Object 数组
子类
HashSet、TreeSet、LinkedHashSet 三者的比较:
不同点 | HashSet | TreeSet | LinkedHashSet |
---|---|---|---|
内部结构 | 哈希表 | 二叉树 | 链表 |
是否有序 | 输入输出的顺序可能不同 | 输出顺序为输入排序后结果 | 输入输出顺序相同 |
是否同步 | 不同步 | 不同步 | 不同步 |
3.1 HashSet
HashSet:内部数据结构是哈希表,是不同步的
如何保证该集合的元素唯一性呢?
是通过对象的 hashCode 和 equals 方法来完成对象唯一性的
- 如果对象的
hashCode
值不同,那么不用判断equals
方法,就直接存储到哈希表中 - 如果对象的
hashCode
值相同,那么要再次判断对象的equals
方法是否为 true- 如果为 true,视为相同元素,不存
- 如果为 false,那么视为不同元素,就进行存储
记住:如果元素要存储到 HashSet 集合中,必须覆盖 hashCode 方法和 equals 方法
哈希表确定元素是否相同
- 判断的是两个元素的哈希值是否相同。如果相同,再判断两个对象的内容是否相同。
- 判断
哈希值相同
,其实判断的是对象的 hashCode 的方法。判断内容相同
,用的是 equals 方法 - 如果
哈希值不同
,是不需要判断equals
3.2 TreeSet
TreeSet:可以对 Set 集合中的元素进行排序,是不同步的
判断元素唯一性的方式:就是根据比较方法
的返回结果是否是 0,是 0,就是相同元素,不存
TreeSet 对元素进行排序的方式:
-
让元素自身具备
比较功能
,就需要实现 Comparable 接口,覆盖compareTo
方法 -
让集合自身具备
比较功能
,定义一个类实现 Comparator 接口,覆盖compare
方法将
该对象
作为参数传
递给 TreeSet 集合的构造函数
4. Map(接口)
java.util .Map
Map:一次添加一对元素(键和值)。Map 集合称为双列集合
Collection:一次添加一个元素。Collection 集合称为单列集合
Map 集合存储的是键值对,必须保证键的唯一性
。
Map 常用的实现类
-
Hashtable :内部结构是
哈希表
,同步的,不允许 null 作为键和值Properties
:用于操作
以键值对形式存在的配置文件
,可以和 IO 技术相结合-
Properties 集合特点
- 该集合中的键和值都是
字符串类型
- 集合中的数据可以
保存到流
中,或者从流获取
- 该集合中的键和值都是
-
常用方法
void setProperty(String k, String v); // 储存 String getProperty(String k); // 取出 Set<String> stringPropertyNames(); // 取出所有键 Enumeration<?> propertyNames() // 取出所有键的枚举 void list(PrintStream out); // 取出 properties 集合中的元素通过字节流输出 void list(PrintWriter out); void store(OutputStream out, String comments); // 将集合中的数据存入到文件中 void store(Writer writer, String comments); void load(InputStream inStream); // 读取一个文件中的配置信息 void load(Reader reader);
读取
Properties prop = System.getProperties(); // 获取系统的属性信息 Set<String> nameSet = prop.stringPropertyNames(); // 得到所有键名 for(String name : nameSet){ String value = prop.getProperty(name); // 根据键得到值 System.out.println(name+"::"+value); }
-
-
HashMap : 内部结构是
哈希表
,不同步的,允许 null 作为键和值 -
TreeMap : 内部结构是
二叉树
,不同步的,可以对 Map 集合中的键进行排序
HashMap 和HashTable 的比较:
不同点 | HashMap | HashTable |
---|---|---|
内部结构 | 哈希表 | 哈希表 |
是否可存null | 允许 null 作为键和值 | 不允许 null 作为键和值 |
是否同步 | 不同步 | 同步 |
取出元素迭代方式 | Iterator | Enumeration |
哈希值的使用不同 | 会重新计算 hash 值 | 直接使用对象的 hashCode |
HashTable 有一个 contains()方法,功能和 containsValue()功能一样
HashMap
-
构造器
HashMap();
-
方法(一一对应 映射)
V put(K key,V value); // 添加键值对。如果没有返回 null,存相同键,值会被覆盖 V get(Object key); // 给键获得值 V remove(Object key); // 给键删除指定的键值对 void clear(); // 清空 map 集合 boolean containsKey(Object key); // 判断集合是否包含该键 boolean containsValue(Object value); // 判断集合是否包含该值 boolean isEmpty(); // 如果次映射中未包含键值映射关系,则返回 true int size(); // 集合长度 Set entrySet(); // 返回键值对视图关系 Set keySet(); // 获得所有的键 Collection values(); // 获得所有的值
Set entrySet():将
键和值的映射关系
作为对象存储到了Set 集合
中,而这映射关系类型就是 Map.Entry 类型,成功解决了 map 集合没有迭代器的问题//Map.Entry<Integer, String>内部类的用法 Set<Map.Entry<Integer, String>> entrySet = map.entrySet(); Iterator<Map.Entry<Integer, String>> it = entrySet.iterator(); while(it.hasNext()){ Map.Entry<Integer, String> me = it.next(); Integer key = me.getKey(); String value = me.getValue(); System.out.println(key+"::::"+value); }
Map
和 Collection
的区别?
Collection
和 Collections
的区别?
5. Collections 集合工具类
java.util.Collections
**常用方法:**方法都是静态的
void sort(List list); // 对 list 集合进行指定顺序的排序,升序
void shuffle(List list); // 随机
void reverse(List list); // 颠倒,逆序
T max(List list); // 最值
int binarySearch(List list); // 二分查找
boolean replaceAll(); // 替换所有
6. Arrays 数组工具类
java.util.Arrays
**常用方法:**方法都是静态的
List asList(元素 1,元素 2,元素 n,....); // 将任意个元素转为集合
void sort(任何类型数组); // 对数组进行指定顺序的排序,升序
源数组类型 copyOf(源数组,新数组的长度); // 复制指定长度的数组,得到新数组
7. 泛型
泛型:广泛的引用数据类型。出现于 JDK1.5
开始出现的安全机制,泛型解决了对象的向上转型和向下转型
好处:将运行时期
的问题 ClassCastException 转到了编译时期
,避免了强制转换的麻烦
说明:它代表不确定的类型,当在访问时,如果没有特殊指定类型,它默认 Object
泛型的特点:类型参数化
,更灵活,类型更安全
什么时候用?
当操作的引用数据类型不确定
的时候,就使用**<>,将要操作的引用数据类型传入**即可。
其实<>:就是一个用于接收具体引用数据类型的参数范围
在程序中,只要用到了带有<>的类或者接口
,就要明确传入
的具体引用数据类型
泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。
运行时,会将泛型去掉,生成的 class 文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?因为为了兼容运行的类加载
器
泛型
泛型类 泛型方法 泛型接口
泛型通配符:? (表示未知类型)
泛型的限定:
-
泛型上限:? Extends E (是 E 类型或者 E 的子类,都可以)
一般
存储对象
的时候用。这样取出都是按照上限类型来运算的,不会出现类型安全隐患比如添加元素:
addAll
-
泛型下限:? super E (是 E 类型或者 E 的父类,都可以)
一般取
出对象
的时候用。比如:比较器
7. 文件以及 IO 流
1. File 文件类
java.io.File
File:硬盘上的一个文件
或者一个目录
的抽象表现形式
-
构造器
File(String pathName);
-
方法:如果文件存在,就不创建,不存在就创建
// 获取 getName(); // 获得文件名称 getPath(); // 获得文件相对路径 getAbsolutePath(); // 获得文件绝对路径 length(); // 文件大小 lastModified(); // 获得最后一次修改的时间 getFreeSpace(); // 剩余空间 getTotalSpace(); // 总空间 getUsableSpace(); // 可用空间 getParent(); // 获得父级目录 getParentFile(); // 获得父级文件 list(); // 获得当前目录下所有目录及文件 string[] listFiles(); // 获得当前目录下所有目录及文件 file[] listRoots(); // 获取目录文件 // 判断 canExecute(); // 是否可执行 canRead(); // 是否可读 canWrite(); // 是否可写 isHidden(); // 是否是隐藏文件 isFile(); // 是否是文件 isDirectory(); // 是否是目录 exists(); // 是否存在 // 创建 delete(); // 删除文件或文件夹(空文件夹) createNewFile(); // 创建新文件 mkdir(); // 创建单个目录 mkdirs(); // 创建多个目录 renameTo(File file);// 重命名
1.1 过滤器 filter 应用
-
FileNameFilter 接口:过滤文件名
public class FileNameFilterDemo{ public static void main(String[] args){ File dir = new File("c:\\"); String[] names = dir.list(new FilterByJava()); // 查找指定文件的文件名 } } class FilterByJava implements FileNameFilter{ @Override public boolean accept(File dir,String name){ return name.endsWith(".java"); // 返回所有以.java结尾的文件名 } }
-
FileFilter 接口:过滤文件
public class FileFilterDemo{ public static void main(String[] args){ File dir = new File("c:\\"); File[] files = dir.listFile(new FilterByHidden()); // 查找指定的文件 } } class FilterByHidden implements FileFilter{ @Override public boolean accept(File pathname){ return pathname.isHidden(); // 返回所有隐藏的文件 } }
分隔符:
- Windows://
- Uniux:\
2. IO 流
流:是一组有序有起点有终点
的字节集合
,是计算机的数据传输
的一种抽象表现形式
IO 流:用来处理设备之间的数据传输,java 对数据的操作
是通过流的方式,用于操作流的对象
都在 io 包中
输入流和输出流都是相对于内存而言。
- 输入流:将外部设备中的数据读取到内存中
- 输出流:将内存中的数据写入到外部设备中
编码:把看得懂的东西变成看不懂的东西
解码:把看不懂的东西变成看的懂得东西
OutputStreamWriter 是字符流
通向字节流
的桥梁,可使用指定的 charset 将要写入流
中的字符
编码成字节
简单说: 字符流=字节流+编码表
字节流
:基本操作与字符流相同,但是它不仅可以操作字符
,还可以操作其他媒体文件
如果要操作文字数据,建议优先考虑字符流
如果要操作流数据,建议优先考虑字节流
流分类:
字节流
的两个顶层父类:1.InputStream 2.OutputStream
字符流
的两个顶层父类:1.Reader 2.Writer
类型 | 1 | 2 |
---|---|---|
按方向分 | 输出流 OutputStream | 输入流 InputStream |
按处理数据类型分 | 字节流 Stream | 字符流 Reader/Writer |
按功能分 | 节点流 | 缓冲流(过滤流) |
2.1 文件流
-
文件字节流
-
文件字节输入流:FileInputStream
在
创建读取流对象
时,必须要明确被读取的文件,一定要确定该文件是存在的-
构造器
FileInputStream(String path); FileInputStream(File file);
-
方法:如果文件存在,就覆盖,不存在就自动创建
读取方式:
单字符
读取、多字符
读取(自定义缓冲区)int read(); // 读取一个字节 int read(byte [] b); // 读取缓冲的字符数组,把数据保留在数组中,返回读取的长度,没有返回-1 close(); // 关闭,应该放在 finally 中且被判断流是否为 null
-
-
文件字节输出流:FileOutputStream
-
构造器
FileOutputStream(String path); FileOutputStream(File file); FileOutputStream(String path,boolean b); // 是否续写 FileOutputStream(File file,boolean b);
-
续写、换行(
系统换行符
:LINE_SEPARATOR 由System.getProperty("line.separator")
得到)
-
-
-
文件字符流
FileReader 字符输入流
FileWriter 字符输出流
2.2 转换流
转换流 = 字节流 + 编码集
-
字符流
转换为字节流
:OutputStreamWriter-
构造器
OutputStreamWriter(OutputStream);
-
方法
writer(int c); // 写入单个字符 writer(String str); // 写入一个字符串 flush() // 刷新该流的缓冲。 close()
-
-
字节流
转换为字符流
:InputStreamReader-
构造器
InputStreamReader(InputStream);
-
什么时候使用转换流呢?
源或目的
对应的设备是字节流,但操作的却是文本数据,可以使用转换作为桥梁,便捷对文本操作- 一旦操作文本且涉及到具体的指定编码表时,必须使用
转换流
2.3 缓冲流
-
输入缓冲流:BufferedReader,BufferedInputStream
-
方法
String readLine(); //读取整行
-
-
输出缓冲流:BufferedWriter、BufferedOutputStream
-
构造器
BufferedInputStream(InputStream);
-
方法
newLine(); //换行
-
1. 装饰设计模式
装饰设计模式:对一组对象的功能进行增强
时,就可以使用该模式进行问题的解决
特点:装饰类和被装饰类都必须所属同一个接口或者父类
-
装饰和继承都能实现一样的特点:进行
功能的扩展增强
。 有什么区别呢?装饰比继承灵活public class PersonDemo{ public static void main(String[] args){ Person p = new Person(); NewPerson p1 = new Newperson(p); // 装饰方式 p1.chifan(); NewPerson2 p2 = new Newperson2(); // 继承方式 p2.chifan(); } } class Person{ void chifan(){ System.out.println("吃饭"); } } // 此类就是为了增强Person而出现的 class NewPerson{ private Person p; public NewPerson(Person p){ this.p = p; } public void chifan(){ System.out.println("开胃酒"); p.chifan(); System.out.println("甜点"); } } class NewPerson2 extends Person{ public void chifan(){ System.out.println("开胃酒"); p.chifan(); System.out.println("甜点"); } }
-
LineNumberReader:可以查询出行号。装饰类 BufferedReader 的子类
public class LineNumberReaderDemo{ public static void main(String[] args) throws IOException{ FileReader fr = new FileReader("文件.txt"); LineNumberReader lnr = new LineNumberReader(fr); String line = null; while((line =lnr.readLine()) != null){ System.out.println(lnr.getLineNumber()+":"+line); } lnr.close(); } }
2.4 对象流
序列化:把对象
转换成字节码
,对象流中的对象必须实现 Serializable 接口
注意:
- transient(非静态数据不想被序列化可以使用这个关键字修饰)
- static(静态数据不会被序列化)
对象流:把对象
存入文件
中,或者将文件中的对象数据
读出存入对象
-
对象输入流:ObjectInputStream EOFException
-
构造器
ObjectInputStream(InputStream);
-
方法
Object readObject();
-
-
对象输出流:ObjectOutputStream NotSerializableException 未实现序列化
-
构造器
ObjectOutputStream (OutputStream);
-
方 法
writeObject(Object o);
-
2.5 IO 包中常见流
1. 打印流
打印流:PrintWriter
、printStream
,可以直接操作输入流
和文件
- PrintWriter:字符打印流, 在需要
写入字符
而不是写入字节的情况下,应该使用 PrintWriter 类- 构造函数参数:字符串路径、File 对象、字节输出流、字符输出流
- 重要方法:append、flush、format、print、println、write
- PrintStream:字节打印流,提供了打印方法可以对多种
数据类型值
进行打印
,并保持数据的表示形式
、不抛 IOException,打印的所有字符都使用平台的默认字符编码
转换为字节
。- 构造函数参数:字符串路径、File 对象、字节输出流
- 重要方法:append、flush、format、print、println、write
2. 序列流
序列流:SequenceInputStream
对多个流进行合并,文件分割和合并
3. 管道流
管道流:PipeInputStream
、PipeOutputStream
:输入输出可以直接进行连接,通过结合线程使用
4. 随机访问文件
RandomAccessFile:自身具备读写
的方法。通过 skipBytes(int x)
,seek(int x)
来达到随机访问
特点:
-
该对象即能读,又能写
该对象内部维护了一个 byte 数组,并通过指针可以操作数组中的元素,可以通过
getFilePointer
方法获取指针的位置
和通过seek
方法设置指针的位置
-
其实该对象就是将
字节输入流
和字节输出流
进行了封装
-
该对象的源或者目的只能是文件,通过构造函数就可以看出
5. 其他流
- 操作基本数据类型:DateInputStream、DateOutputStream
- 操作字节数组:ByteArrayInputStream、ByteArrayOutputStream
- 操作字符数组:CharArrayReader、CharArrayWriter
- 操作字符串:StringReader、StringWriter
2.6 编码表
常见码表:
ASCII
:美国标准信息交换码ISO8859-1
:拉丁码表、欧洲码表- GB2312:中国的中文编码表
GBK
:中国的中文编码表的升级Unicode
:国际标准码- UTF-8:最多用三个字节来表示一个字符
8. 网络编程
1. 网络模型
位置 | 作用 |
---|---|
应用层 | 应用程序间的交换和数据交换 ,主要是一些 终端的应用 ,比加说 FTP(各种文件下载),WEB(IE 浏览),QQ 之类的(可以把它理解成我们在电脑屏幕上可以看到的东西,就是终端应用) |
表示层 | 进行对接收的数据 进行解释 、加密与解密 ,压缩与解压缩 等,也就是把计算机能够识别的东西转换成人能够能识别的东西 |
会话层 | 通过传输层(端口号:传输端口与换收端口)建立数据传输通路 |
传输层 | 定义了一些传输数据的协议(如:TCP/UDP )和端口号(如:WWW 80 ),将从下层接收的数据进行分段和传输,到达目的地址后再进行重组,常把这一层数据叫做 段 |
网络层 | 从下层接收到的数据进行 IP 地址的封装与解封装, 这一层工作的设备是路由器,常把这一层的数据叫做 数据包 |
数据连接层 | 将从物理层接收的数据进行 MAC (网卡)地址, 在这一层工作的设备是交换机,数据通过交换机来传输,常把这一层的数据叫做 帧 |
物理层 | 主要定义物理设备(网线/光纤)标准,用于传输比特流,这一层的数据叫做比特 |
2. 网络通讯要素
网络通讯要素:IP 地址、端口号、传输协议(TCP/UDP)
2.1 IP 地址
InetAddress 类:表示互联网协议(IP)地址,对应于网络层
本地环回地址:127.0.0.1
对应主机名:localhost
2.2 端口号
端口:是用来标示应用程序
的
有效端口:0 - 65535
,其中 0 - 1024 系统使用或者是保留端口,尽量不要用,因为系统要使用
2.3 传输协议
传输协议:其实就是通讯规则
,对应于传输层
Socket(套接字):就是为网络服务提供的一种机制,网络通信其实就是 Socket 间的通信
- Socket 可以理解为
通信的两端
,数据就是在 Socket 之间进行传输。 - 通信的两端都有 Socket,数据在两个 Socket 间通过
IO 传输
常见协议:TCP,UDP(都是传输层
的协议)
- TCP:面向连接,可靠传输协议
- UDP:不面向连接,不可靠
TCP/UDP 协议比较:
区别 | UDP | TCP |
---|---|---|
是否连接 | 将数据及源和目的封装成数据包,不需要建立连接 | 建立连接,形成传输数据的通道 |
传输数据量 | 每个数据报的大小限制在64k内 | 在连接中进行大数据量传输 |
是否可靠 | 因无连接,是不可靠协议 | 通过三次握手完成连接,是可靠协议 |
传输速度 | 不需要建立连接,速度快 | 必须建立连接,效率会稍低点 |
3. 网络通信应用
我们编写网络通信程序,其实是在应用层
3.1 UDP 通信
-
发送端
创建 UDP 发送端思路:
- 建立 udp 的 socket 服务
- 将要发送的数据封装到数据包中
- 通过 udp 的 socket 服务将数据包发送出去
- 关闭 socket 服务
public class UDPSendDemo { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(8888); // 1.udpsocket 服务 String str = "udp 传输演示:哥们来了!"; byte[] buf = str.getBytes(); // 2.使用 DatagramPacket 将数据封装到的该对象包中。 DatagramPacket dp = new DatagramPacket(buf, // 数据 buf.length, // 长度 InetAddress.getByName("127.0.0.1"),//地址 10000); // 端口号 ds.send(dp); // 3.通过 udp 的 socket 服务将数据包发送出去。使用 send 方法。 ds.close(); // 4.关闭资源。 } }
-
接收端
建立 UDP 接收端思路:
- 建立 udp socket 服务,因为是要接收数据,必须要明确一个端口号
- 创建数据包,用于存储接收到的数据,方便用数据包对象的方法解析这些数据
- 使用 socket 服务的 receive 方法将接收的数据存储到数据包中
- 通过数据包的方法解析数据包中的数据
- 关闭资源
public class UDPReceDemo { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(10000); // 1.建立 udp socket 服务 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); // 2.创建数据包 ds.receive(dp); // 3.使用接收方法将数据存储到数据包中,阻塞式的。 // 4.通过数据包对象的方法,解析其中的数据,比如:地址,端口,数据内容。 String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); String text = new String(dp.getData(),0,dp.getLength()); System.out.println(ip+":"+port+":"+text); ds.close(); // 5.关闭资源。 } }
3.2 TCP 通信
-
客户端
TCP 客户端建立思路:
-
创建 tcp 客户端 socket 服务。建议该对象一创建就明确目的地,要连接的主机
-
如果连接建立成功,说明数据传输通道已建立
该通道就是
socket 流
,是底层建立好的。 既然是流,说明这里既有输入,又有输出。想要输入或者输出流对象,可以找 Socket 来获取,可以通过
getOutputStream()
和getInputStream()
来获取两个字节流。 -
使用输出流,将数据写出
-
关闭资源
public class ClientDemo { public static void main(String[] args) throws Exception { Socket socket = new Socket("127.0.0.1",10002);// 1.创建客户端 socket 服务 OutputStream out = socket.getOutputStream(); // 2.获取 socket 流中的输出流 out.write("tcp 演示:哥们又来了!".getBytes()); // 3.使用输出流将指定的数据写出 socket.close();//关闭资源。 } }
-
-
服务端
TCP 服务端建立思路:
- 创建服务端 socket 服务,通过 ServerSocket 对象
- 服务端必须对外提供一个端口,否则客户端无法连接
- 获取连接过来的客户端对象
- 通过客户端对象获取 socket 流读取客户端发来的数据,并打印在控制台上
- 关闭资源:关客户端,关服务端
public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10002); // 1.创建服务端对象 Socket s = ss.accept(); // 2.获取连接过来的客户端对象。阻塞式 InputStream in = s.getInputStream(); // 3.获取输入流,要读取客户端发来的数据 byte[] buf = new byte[1024]; int len = in.read(buf); String text = new String(buf,0,len); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+":"+text); s.close(); ss.close(); } }
3.3 网络架构
网络架构
-
C/S(client/server)
特点
:该结构的软件,客户端和服务端都需要编写,开发成本较高,维护较为麻烦好处
:客户端在本地可以分担一部分运算 -
B/S(browser/server)
特点
:该结构的软件,只开发服务器端,不开发客户端,因为客户端直接由浏览器取代,开发成本相对低,维护更为简单
缺点
:所有运算都要在服务端完成
客户端、服务端
最常见的客户端:浏览器:IE
最常见的服务端:服务器:Tomcat
客户端 IE 如何给服务端 Tomcat发请求?
-
客户端 IE
发送的请求:请求行+消息头+请求体// 请求行: 请求方式(GET) 请求的资源路径(/myweb/1.html) http协议版本(HTTP/1.1) GET / HTTP/1.1 // 请求消息头: 属性名:属性值 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash,application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept: */* Accept-Language: zh-cn,zu;q=0.5 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.2) Host: 192.168.1.100:9090 Connection: Keep-Alive // 空行 // 请求体
-
服务端 Tomcat
返回应答消息:应答行+消息属性信息+应答体// 应答行:http协议版本(HTTP/1.1) 应答状态码(200) 应答状态描述信息(OK) HTTP/1.1 200 OK // 应答消息属性信息:属性名:属性值 Server: Apache-Coyote/1.1 ETag: W/"199-1323480176984" Last-Modified: Sat, 10 Dec 2011 01:22:56 GMT Content-Type: text/html Content-Length: 199 Date: Fri, 11 May 2012 07:51:39 GMT Connection: close // 空行 // 应答体 <html> <head> <title>这是我的网页</title> </head> <body> <h1>欢迎光临</h1> <font size='5' color="red">这是一个 tomcat 服务器中的资源,是一个 html 网页</font> </body> </html>
GET 提交和 POST 提交的区别
-
在客服端的区别
区别 GET POST 是否显示 提交的信息都 显示
在地址栏中提交的信息 不显示
地址栏中封装位置 将信息封装到了请求消息的请求行中 将信息封装到了请求体中 安全性 对于敏感的数据信息 不安全
对于敏感信息 安全
数据体积 对于 大数据不行
,因为地址栏存储体积有限可以提交 大体积数据
-
在服务端的区别
如果提交中文到 tomcat 服务器会出现乱码,服务器默认会用 iso8859-1 进行解码,解决方法:
-
对
get
提交和post
提交都有效- 通过 iso8859-1 进行编码,再用
指定的中文码表
解码即可 - 用 URLDecoder.decode(user,“utf-8”)
- 通过 iso8859-1 进行编码,再用
-
对于
post
提交,还有另一种解决办法,就是直接使用服务端一个 request 对象request
对象的 setCharacterEncoding 方法直接设置指定的中文码表就可以将中文数据解析出来。这个方法只对请求体中的数据进行解码
-
9. XML
1. 概述
XML:可扩展的标记语言,由 W3C 发布,版本 1.0
XML作用:
- 用于软件配置
- 描述数据之间的关系
- 数据传输
2. XML 使用
-
定义方法
<?xml version="1.0"?> <?xml version="1.0" encoding="GBK"?>
-
元素标记命名规则
- 区分大小写
- 不以数字开头,不能有空格和冒号(😃
- 一个良好的 xml 有且只有一个根目录
- 不能以 XML(xml 或者 Xml 等)开头
-
属性:一个标记可以有多个属性,每个属性都有自己名称值
-
CDATA 区:xml 解析程序不会处理 CDATA 区中的内容,而是直接输出
-
注释:
<!-- 注释 -->
-
特殊字符:
& &
3. XML 解析
-
dom W3C 官方推荐
特点:解析器读入
整个文档
,然后构建一个内存树形结构
,然后就可以使用 Document 接口来操作这树形结构,消耗计算内存
-
SAX 不是官方标准 ,是 xml 社区上标准
特点:不必加载所有文档,就可以操作文档对象
4. DOM4J 编程
-
体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kX928ZNU-1648561039749)(link-picture\image-20220111233522129.png)]
-
dom 解析方式
加 dom4j 的 jar:dom4j-1.6.1.jar
拷贝 jar 包,然后新建文件夹,把 jar 放入,点击右键 build path,然后 add build path
-
dom 解析常用方法
SAXReader read = new SAXReader(); // 创建一个 XML 解析流 Document doc = read.read(new File("xml 的路径")); // 获得 Document Element root = doc.getRootElement(); // 获得根元素 // 操作 Element 的方法 String getName(); // 获得标记名 String getText(); // 获得文本值 String attitudteValue(String name); // 用属性名获得属性值 List elements(); // 获得所有的子元素 Element getParent(); // 获得该元素的父级元素 Iterator elementIterator(); // 获得所有的子元素的迭代器
5. XPath
Xpath:是在 XML 文档中查找信息的语言,它简化了 DOM4j 查找节点
(Node)的过程,通过元素和属性
进行查找
需要导入 jar 包:jaxen-1.1-beta-6.jar
-
语法
/Students/Student // 绝对路径下获得根目录下所有直接 Student 子元素 /Students//Student // 相对路径下获得根目录下所有的 Student 子元素 Student//name // 获得所有属于 Student 的子元素的 name 元素 //@id // 获得所有带 id 属性的元素 //Student[@id] // 获得所有带 id 属性的 Student 元素 //Student[@id='110'] // 获得所有带 id 属性值为 110 的 Student 元素 //Student[@age>=18] // 获得所有带 age 属性值大于等于 18 的 Student 元素 //Student[@sex='女' and @age>20] //Student[@sex='女' or @age>20]
-
方法
Node selectSingleNode(String xpath); // 返回是第一个满足 xpath 条件的 Node 对象 List<Node> selectNodes(String xpath);// 返回所有满足 xpath 条件的 Node 对象的 List 集合
6. DOM 操作(增、删、改、查)
DOM 操作方法:
DocumentHelper.createDocument(); // 创建一个 Document
DocumentHelper.createElement("name"); // 创建一个根目录
DocumentHelper.createAttribute(元素, "属性", "属性值");// 创建属性
add(属性/元素); // 添加到 Document
addAttribute("属性", "属性值"); // 添加属性
属性.setValue("属性值"); // 修改属性
remove(子元素); // 删除子元素
10. 反射机制
在运行状态
中,对于任意一个类(class 文件)
,都能够知道这个类的所有属性和方法
;对于任意一个对象
,都能够调用它的任意一个方法和属性
,这种动态获取类中的信息
以及动态调用对象的方法
的功能称为 java 语言的反射机制。
-
获取 Class 对象(
字节码文件对象
)获取 Class 对象后,再用
newInStance()
创建对象getClass(); // 方式1 静态的属性.class/对象.class // 方式2 Class.forName(“带包名的类名”) // 方式3
-
获取
Class 对象
的构造方法getConstructor();
-
获取
Class 对象
的字段getField(“字段名称”); // 只能获得公共的属性 getDeclareField(“字段名称”); // 既获得公共的属性,也可获得私有的属性 field.setAccessiable(ture); // 对私有字段的访问取消权限检查(即是暴力访问):
-
获取
Class 对象
的方法getMethods(); // 获取的都是公共的方法 getDeclareMethod(); // 可以获得本类中的私有方法
11. 正则表达式
正则表达式:用于操作字符串数据
,通过一些特定的符号
来体现的。虽然简化了,但是阅读性差
1. 常见的符号
-
字符
[abc] # 包含 a、b 或 c [^abc] # 包含任何字符,除了 a、b 或 c [a-zA-Z] # 包含 a 到 z 或 A 到 Z [a-d[m-p]] # 包含 a 到 d 或 m 到 p [a-z&&[def]] # 包含 d、e 或 f [a-z&&[^bc]] # 包含 a 到 z,除了 b 和 c [a-z&&[^m-p]] # 包含 a 到 z,除了 m 到 p
-
预定义字符
. # 任何字符 \d # 数字:[0-9] \D # 非数字: [^0-9] \s # 空白字符:[ \t\n\x0B\f\r] \S # 非空白字符:[^\s] \w # 单词字符:[a-zA-Z_0-9] \W # 非单词字符:[^\w
-
边界
^ # 行的开头 $ # 行的结尾
-
量词
X? # X 最多一次 X* # X 任意次 X+ # X 至少一次 X{n} # X,恰好 n 次 X{n,} # X,至少 n 次 X{n,m} # X,至少 n 次,但是不超过 m 次
-
运算符
(X) # X,作为捕获组
2. 常见操作
正则类将正则规则
进行对象的封装
。
-
常用两个类
- Pattern 类:java.util.regex.Pattern
- Matcher 类:java.util.regex.Matcher
-
正则表达式对字符串的
操作原理
- 匹配:其实使用的就是 String 类中的 matches 方法
- 切割:其实使用的就是 String 类中的 split 方法
- 替换:其实使用的就是 String 类中的 replaceAll()方法
-
正则表达式对字符串的
操作
Pattern p = Pattern.compile("regex"); // 将正则表达式封装成对象 Matcher m = p.matcher("str"); // 获取要对字符串操作的匹配器对象 Matcher boolean b = m.matches(); // 通过 Matcher 匹配器对象的方法对字符串进行操作
例如:
String str = "da jia hao,ming tian bu fang jia!"; String regex = "\\b[a-z]{3}\\b"; // 正则表达式 Pattern p = Pattern.compile(regex); // 1.将正则封装成对象 Matcher m = p.matcher(str); // 2.通过正则对象获取匹配器对象 //使用 Matcher 对象的方法对字符串进行操作。 //既然要获取三个字母组成的单词,使用 Matcher 对象的查找方法:find(); System.out.println(str); while(m.find()){ System.out.println(m.group()); //获取匹配的子序列 System.out.println(m.start()+":"+m.end()); }
素
//Student[@id] // 获得所有带 id 属性的 Student 元素
//Student[@id=‘110’] // 获得所有带 id 属性值为 110 的 Student 元素
//Student[@age>=18] // 获得所有带 age 属性值大于等于 18 的 Student 元素
//Student[@sex=‘女’ and @age>20]
//Student[@sex=‘女’ or @age>20]
2. 方法
~~~java
Node selectSingleNode(String xpath); // 返回是第一个满足 xpath 条件的 Node 对象
List<Node> selectNodes(String xpath);// 返回所有满足 xpath 条件的 Node 对象的 List 集合
6. DOM 操作(增、删、改、查)
DOM 操作方法:
DocumentHelper.createDocument(); // 创建一个 Document
DocumentHelper.createElement("name"); // 创建一个根目录
DocumentHelper.createAttribute(元素, "属性", "属性值");// 创建属性
add(属性/元素); // 添加到 Document
addAttribute("属性", "属性值"); // 添加属性
属性.setValue("属性值"); // 修改属性
remove(子元素); // 删除子元素
10. 反射机制
在运行状态
中,对于任意一个类(class 文件)
,都能够知道这个类的所有属性和方法
;对于任意一个对象
,都能够调用它的任意一个方法和属性
,这种动态获取类中的信息
以及动态调用对象的方法
的功能称为 java 语言的反射机制。
-
获取 Class 对象(
字节码文件对象
)获取 Class 对象后,再用
newInStance()
创建对象getClass(); // 方式1 静态的属性.class/对象.class // 方式2 Class.forName(“带包名的类名”) // 方式3
-
获取
Class 对象
的构造方法getConstructor();
-
获取
Class 对象
的字段getField(“字段名称”); // 只能获得公共的属性 getDeclareField(“字段名称”); // 既获得公共的属性,也可获得私有的属性 field.setAccessiable(ture); // 对私有字段的访问取消权限检查(即是暴力访问):
-
获取
Class 对象
的方法getMethods(); // 获取的都是公共的方法 getDeclareMethod(); // 可以获得本类中的私有方法
11. 正则表达式
正则表达式:用于操作字符串数据
,通过一些特定的符号
来体现的。虽然简化了,但是阅读性差
1. 常见的符号
-
字符
[abc] # 包含 a、b 或 c [^abc] # 包含任何字符,除了 a、b 或 c [a-zA-Z] # 包含 a 到 z 或 A 到 Z [a-d[m-p]] # 包含 a 到 d 或 m 到 p [a-z&&[def]] # 包含 d、e 或 f [a-z&&[^bc]] # 包含 a 到 z,除了 b 和 c [a-z&&[^m-p]] # 包含 a 到 z,除了 m 到 p
-
预定义字符
. # 任何字符 \d # 数字:[0-9] \D # 非数字: [^0-9] \s # 空白字符:[ \t\n\x0B\f\r] \S # 非空白字符:[^\s] \w # 单词字符:[a-zA-Z_0-9] \W # 非单词字符:[^\w
-
边界
^ # 行的开头 $ # 行的结尾
-
量词
X? # X 最多一次 X* # X 任意次 X+ # X 至少一次 X{n} # X,恰好 n 次 X{n,} # X,至少 n 次 X{n,m} # X,至少 n 次,但是不超过 m 次
-
运算符
(X) # X,作为捕获组
2. 常见操作
正则类将正则规则
进行对象的封装
。
-
常用两个类
- Pattern 类:java.util.regex.Pattern
- Matcher 类:java.util.regex.Matcher
-
正则表达式对字符串的
操作原理
- 匹配:其实使用的就是 String 类中的 matches 方法
- 切割:其实使用的就是 String 类中的 split 方法
- 替换:其实使用的就是 String 类中的 replaceAll()方法
-
正则表达式对字符串的
操作
Pattern p = Pattern.compile("regex"); // 将正则表达式封装成对象 Matcher m = p.matcher("str"); // 获取要对字符串操作的匹配器对象 Matcher boolean b = m.matches(); // 通过 Matcher 匹配器对象的方法对字符串进行操作
例如:
String str = "da jia hao,ming tian bu fang jia!"; String regex = "\\b[a-z]{3}\\b"; // 正则表达式 Pattern p = Pattern.compile(regex); // 1.将正则封装成对象 Matcher m = p.matcher(str); // 2.通过正则对象获取匹配器对象 //使用 Matcher 对象的方法对字符串进行操作。 //既然要获取三个字母组成的单词,使用 Matcher 对象的查找方法:find(); System.out.println(str); while(m.find()){ System.out.println(m.group()); //获取匹配的子序列 System.out.println(m.start()+":"+m.end()); }