JavaSE
2020-09-27 10:34:01 5 举报
AI智能生成
Java基础吐血整理
作者其他创作
大纲/内容
概述环境搭建
小知识点
应用方面
ctrl+c 强制停止正在运行的程序
Math.pow(m,n);m的n次方
Math.random随机小数
Random random = new Random;
int a = random.nextInt(x);随机获得一个小于x的数
int a = random.nextInt(x);随机获得一个小于x的数
"".equals 用于引用数据类型
== . equals 与 !的应用解释
== 比较的是地址,不是内容。所以当比较字符串时,==不够准确。
比较字符串时,应使用 s1.equals(s2) ,可准确的比较字符串内容。
当比较两个字符串不相同时,可使用 !s1.equals(s2)。 !(逻辑运算符:非)代表“不是”
关于类和对象
A a = new A();
A a1 = a;
a1等同于a的地址
A a1 = a;
a1等同于a的地址
可以在类里建其他类的对象
class B{
A a = new A();
C c ;
}
public B(){
c = new C();
}
class B{
A a = new A();
C c ;
}
public B(){
c = new C();
}
循环结束
在循环前加个“常量名词”可以在想要结束循环时加 ”break + 常量名词“
不用加“return”结束整个函数
不用加“return”结束整个函数
Eclipse
工具的基本使用:
修改编辑字体:windows -> Preferences -> General -> Appearance -> Colors And Fonts -> Basic -> Text Font
修改输出字体:windows -> Preferences -> General -> Appearance -> Colors And Fonts -> Debug ->
重置窗口布局:windows -> Perspective -> Reset Perspective
修改输出字体:windows -> Preferences -> General -> Appearance -> Colors And Fonts -> Debug ->
重置窗口布局:windows -> Perspective -> Reset Perspective
Eclipse导入外部项目
File -> Import -> General -> Existing Projects into Workspace -> Browse -> 项目的根文件夹 -> Finish
格式化代码
Ctrl + Shift + F
构造方法快捷键
source
Generate Constructor using Fields
封装快捷键
source
Generate Getters and Setters
语言基础
变量
计算机内存中的一块存储空间,是存储数据的基本单位
组成部分
数据类型
变量名
值
声明方式
先声明,再赋值
数据类型 变量名;
变量名 = 值;
变量名 = 值;
声明并赋值
多个同类型变量的声明与赋值
数据类型
基本数据类型
整数
byte
1字节
-2^7~2^7-1
short
2字节
-2^15~2^15-1
int
4字节
-2^31~2^31-1
long
8字节
-2^63~2^63-1
小数
float
double
布尔
boolean
字符
char
转义字符
子主题
\'
\"
\\
\t
\n
引用数据类型
字符串
String
数组
对象
类型的转换
自动类型转换
两种类型互相兼容,目标类型大于源类型
强制类型转换
两种类型相互兼容,目标类型小于源类型
转换规则
整数长度足够,数据完整
整数长度不够,数据截断
小数强转整数,数据截断
字符整数互转,数据完整
保证正整数转换成字符,可以正常显示
boolean不可与其他类型转换
运算符
算数运算符
+ - * / % ++ --
赋值运算符
= += -= *= /= %=
关系运算符
> < >= <= == !=
逻辑运算符
&& || !
三元运算符
布尔表达式 ? 结果1:结果2
选择结构与分支结构
概念:根据已知条件进行落尽判断,满意条件后执行相应的操作
选择结构
基本if选择结构
if else选择结构
多重if选择结构
相互排斥,有一个为true,其他选择不在执行,适用于区间判断
嵌套if选择结构
可以在一个选择结构当中,语法正确、格式正确的情况下,可以任意组合
分支结构
switch
可判断的byte 、short 、int、char、String(JDK7+)
switch中的多个case取值不可相同
switch有自动向下贯穿的特性,如需要在某个选项执行后中止,需要追加break关键字
局部变量
概念:声明在函数内部的变量,必须先赋值在使用
作用范围:从定义行开始到所在的代码块结束
注意:多个变量在重合的作用范围内,不可出现重名
循环结构
概念:通过某个条件,重复的执行一段逻辑代码
循环结构
while
int i= 0;while(i <= ?){ i++;}
首次既有入口条件,先判断,在执行,适用于循环次数明确的情况
do while
do{ }while(条件判断);
首次没有入口条件,先执行,在判断,适用于循环次数不明确的情况
for
for(int i = ? ;i <=?; i++){ }
首次既有入口条件,先判断,在执行,适用于循环次数明确的情况
流程控制语句
break
中止,跳出:switch分支结构,循环结构
continue
结束本次,进入下一次循环
嵌套循环的概念
在一个完整的循环结构中,嵌套另一个循环结构
外层控制循环次数,内层控制单次循环操作
函数
概念:实现特定功能的一段代码,可反复使用。
定义
定义: public static void 函数名称(){
//函数的主体(功能代码)}
//函数的主体(功能代码)}
位置
定义在类的内部,与main函数并列。多数情况下,定义在main函数的下面
函数调用
函数名称();
public static void 函数名称 ( 无参 ) { //局部变量的声明 }
public static void 函数名称 ( 无参 ) { //局部变量的声明 }
函数名称(实参);
public static void 函数名称 ( 形参 ) { //局部变量的声明 }
public static void 函数名称 ( 形参 ) { //局部变量的声明 }
返回值
定义返回值类型
基本数据类型、引用数据类型、void
return value;
函数可以返回一个结果,类型必须与函数定义的返回值类型一致。
一个函数只能有一个返回值,如果函数中包含分支,需要保证所有的分支都有正确的返回值。
return的两种用法:
return value; //表示结束当前函数,并伴有返回值,返回到函数调用处。(有具体的返回值类型)
return; //表示结束当前函数,直接会返回到函数调用处。(返回值类型是void)
递归
实际再发中,递归可以解决一些具有既定规律的问题。
当需要解决的问题可以拆分成若干个小问题,大问题、小问题的解决方法相同,有特定的规律,函数自己调用自己。
设置有效的出口条件,避免无穷递归。
总结
注意
一个类中可以定义多个函数,函数之间属于并列关系,不可嵌套。
经验
一个函数只做一件事。
好处
子主题减少代码冗余。提高复用性。提高可读性。提高可维护性。方便分工合作。
数组
概念
一组连续的存储空间,存储多个相同数据类型的值。
数组的创建:
数据类型[] 数组名 = new 数据类型[长度]; // int [] a = new int[5];
数组的组成概述:
数组中的每个“格子”称为“数组的元素”。
对元素的访问分为:“赋值”、“取值”。
访问元素时,需要通过“下标”(从0开始,依次+1,自动生成)
访问的语法:数组名[下标]; //存:a[0] = 10; 取:a[0]
下标的范围:0 ~ 数组的长度-1 ,如果不在有效下标内,产生java.util.ArrayIndexOutOfBoundsException:错误下标
数组的遍历
从头至尾,逐一对数组的每个元素进行访问
数组名.length
可动态获取数组长度
在没有为数组元素赋值的情况下,依旧可以正确访问
int
0
double
0.0
字符
\u0000
布尔
false
其他
null
数组的”扩容“
创建数组时,必须显示指定长度,并在创建之后不可更改长度。
思路
创建长度大于原数组的新数组。
将原数组中的数据依次复制到新数组当中。
复制
循环将原数组中的所有元素数据逐一赋值给新数组。
System.arraycopy(原数组 , 原数组起始位置 , 新数组 , 新数组起始位置 , 长度);
数组类型的变量 = java.util.Arrays.copyOf(原数组 , 新长度); //根据长度创建新数组,并将原数组中的元素赋值到新数组中
地址的替换
图片解析
基本数据类型的变量存储的是值 也就是栈
引用数据类型的变量存储的是地址 地址也就是堆
nums = newNums; //将新数组的地址,赋值给nums变量,进而在后续操作nums时,则操作长度更大的新数组。
可变长参数
概念
可接收多个同类型实参,个数不限,使用方式与数组相同。
语法
数据类型... 形参名 //必须定义在形参列表的最后,且只能有一个,支持0~N个参数。
int... = int[] String... = String[] char... = char[] double... = double[]
method(xxx , xxx[] , xxx... ); //可变长参数,是对数组的一种便利写法的补充(80%的场景用数组,20%的场景用可变长参数)
排序
冒泡排序:
1). 相邻的两个值比较大小,互换位置。
2). 记忆:外层 length - 1 ;内层 length -1 - i
选择排序
固定值与其他值比较大小,互换位置。
记忆:外层 length - 1 ;同时外层i作为固定值,内层的j = i+1作为其他值的起始
- java.util.Array.sort(数组名) 只能用于升序
补充:嵌套循环约定俗称的变量命名,一重循环 i;二重循环j;三重循环k;
二维数组
概念
一维数组中的一维数组,数组中的元素,还是数组。
语法
先声明再分配空间
数组类型[][] 数组名
数组名 = new 数据类型[高维长度][低维长度]
数组名 = new 数据类型[高维长度][低维长度]
声明并分配空间
数组类型[][] 数组名= new 数据类型[高维长度][低维长度]
声明并赋值(繁)
数组类型[][] 数组名 = new 数据类型[高维长度][] // 不规则数组自行new低数组 数组名[高维数组] = new[低维数组]
声明并赋值(简)
数组类型[][] 数组名 = {{v1,v2,v3},{v8,v9,v7},{v4,v5,v6}}
注意
高维数组中的每一个元素,保存了低维数组的地址。访问array[0]等价于在访问0x0000A111("二维数组的内存分配图")
面向对象
程序的概念:
程序是为了模拟现实世界、解决现实问题而使用计算机语言编写的指令集合。
概念
什么是对象?
一切客观存在的事物都是对象,万物皆对象。
初期:看得见、摸得着、并真实存在,都是对象。
对象有什么?
子主题任何对象,一定有自己的特征和行为。
特征和行为:
特征:称为属性,一般为名词,代表对象都有什么。
行为:称为方法,一般为动词,代表对象能做什么。
程序中的对象
程序如何模拟现实世界?
(1). 现实世界中,都是对象,程序中也应有对象。
(2). 程序当中必须具有和现实中相同的对象,用以模拟。
(3). 使用程序中的对象,代表现实中的对象,并执行操作,解决现实问题。
(2). 程序当中必须具有和现实中相同的对象,用以模拟。
(3). 使用程序中的对象,代表现实中的对象,并执行操作,解决现实问题。
解析
现实与程序:
(1). 现实中的对象,来自于模板,通过模板造出来的实体,即是现实的对象。
(2). 程序中的对象,来自于模板(“类”),通过类造出来的实体,及时程序中的对象。
(2). 程序中的对象,来自于模板(“类”),通过类造出来的实体,及时程序中的对象。
类与对象的关系:
类:定义了对象应具有的特征和行为,类是对象的模板。
对象:拥有多个特征和行为的实体,对象是类的实例。
类的抽取
在一组相同或类似的对象中,抽取出共性的特征和行为,保留所关注的部分
实例变量与局部变量的区别
局部变量定义在方法或方法的结构当中,无默认值,使用范围是从定义行到包含其结构结束,不允许重名
实例变量定义在类的内部,方法的外部,默认值为字面值(与数组相同),仅在本类中有效,可与局部变量重名,局部变量优先
方法重载(Overload)
概念
在一个类中定义多个相同名称的方法。
要求
方法名称相同
参数列表不同
与访问修饰符、返回值无关
好处
屏蔽用户的使用差异,方便。
构造方法(Constractor)
概念:类中的特殊方法,主要用于创建对象。
特点
名称与类名完全相同(包括大小写)。
没有返回值类型。
创建对象时(new对象时),触发构造方法的调用,不可通过句点的形式手工调用。
注意:如果没有在类中显示定义过构造方法,则编译器默认提供无参构造方法。
如果已经手动添加过有参构造方法,则无参构造方法不再默认提供,必须手动添加无参构造方法)
如果已经手动添加过有参构造方法,则无参构造方法不再默认提供,必须手动添加无参构造方法)
this关键字
this代表“当前实例”,即是模板中的当前对象,模板服务与哪个对象,this就指向哪个对象。
this第一种用法:调用本类中的实例属性、实例方法。例如:this.name、this.run()。
this第二种用法:调用本类中的其他构造方法。例如:this()、this(实参)。注意:必须在构造方法的首行。
默认来讲,实例属性和方法前的this.隐式存在
三大特性
封装
概念:尽可能隐藏对象的内部实现细节,控制对象的修改及访问的权限。
private的访问修饰符,修饰属性,达到本类可见的效果。
get/set方法是外界访问私有属性的唯一通道,方法内部可对数据进行过滤。(可在set方法中添加过滤条件)
提供公共访问方法,以保证数据可以正常录入。
private构造方法达到私有类的效果,别人无法看见
银行案例总结
继承
概念
程序中的继承,是类与类之间特征和行为的一种赠与或获得
类与类之间必须满足“is a”的关系
父类的选择:功能越精细,重合点越多的,越接近直接父类
父类的抽象:根据程序需要使用到的多个具体类,进行共性的提取,进而定义父类
在一组相同或类似的类中,抽取出共性的特征和行为,定义在父类中,实现重用
产生继承关系之后,子类可以使用父类中的属性和方法,也可定义子类独有的属性和方法
完整的子类 = 父类共性 + 子类独有
好处:既提高代码的复用性,又提高代码的可扩展性
Java为单继承,一个类只能有一个直接父类,但可以多级继承,属性和方法逐级叠加
不可继承:
父类的构造方法,子类不可继承。
父类中由private修饰的成员,不可继承(不可见)。
父类中由default修饰的成员,子类不在同包时,不可继承(不可见)。
访问修饰符
方法的覆盖/重写(Override)
当父类提供的方法无法满足子类需求时,可以在子类中定义和父类相同的方法进行覆盖。
要求
方法名、参数表、返回值,必须与父类完全相同。
访问修饰符应与父类相同或比父类更宽泛。
执行机制:子类覆盖父类方法后,优先执行子类覆盖后的方法版本。
super关键字
第一种用法:在子类中,可以通过" super. "的形式访问父类的属性和方法,可解决一定的属性遮蔽、方法覆盖后的父类成员调用问题。
第二种用法:super() 表示在子类构造方法的首行,调用父类的无参构造方法。
继承关系下的对象创建:
继承关系下,构建子类对象时,会先构建父类对象。
由“父类共性”+ “子类独有”组合成一个完整的子类对象。
继承关系下的对象创建流程:
构建父类对象
初始化自身属性
执行自身构造方法中的逻辑代码
注意
如果子类构造方法中,没有显示定义super()或super(实参),则默认提供super()。
同一个子类构造方法中,super()、this()不可同时存在。
多态
概念:父类引用指向子类对象,从而产生多种形态。
构成多态的前提,二者之间必须具有直接或间接的继承关系,父类引用可指向子类对象,进而形成多态。
父类引用仅可调用父类中所声明的属性和方法,不可调用子类独有的属性和方法。
多态两种应用场景:
场景一:使用父类作为方法形参,实现多态
调用方法时,可传递的实参类型包括:本类型对象+其所有的子类对象。
调用方法时,可传递的实参类型包括:本类型对象+其所有的子类对象。
场景二:使用父类作为方法返回值,实现多态。
调用方法后,可得到的结果类型包括:本类型对象+其所有的子类对象。
调用方法后,可得到的结果类型包括:本类型对象+其所有的子类对象。
多态的作用:
屏蔽子类间的差异。
灵活、耦合度低。
时间与空间的平衡、效率与安全的平衡
三大修饰符
abstract
父类:不够完整、不够具体、不该独立存在,如何解决?通过abstract修饰类,意为抽象类,不能new对象
abstract的意思:抽象的,似是而非的,像,却又不是,具备某种对象的特征,但不完整。
abstract修饰类,意为“不够完整、不够具体,不该独立存在”
即为抽象类,不能独立new对象。
可被子类继承,对子类提供共性的属性和方法。
可声明引用,更纯粹的使用多态。
抽象类的构造方法的作用:构建子类对象时,先构建父类对象,由父类共性+子类独有组成完整的子类对象。
总结
abstract修饰类:不能new对象,但可以声明引用。
abstract修饰方法:只有方法声明,没有方法实现。(需包含在抽象类中)
抽象类中不一定由抽象方法,但由抽象方法的类一定是抽象类。
子类继承抽象类之后,必须覆盖父类当中所有的抽象方法,否则子类还是抽象类。
static
静态与实例的区别:
实例属性是每个对象各自持有的独立空间(多份),对象单方面修改,不会影响其他对象。
方法区
静态属性是整个类共同持有的共享空间(一份),任何对象修改,都会影响其他对象。
静态的概念:
static可以修饰属性和方法,即为静态属性(类属性)、静态方法(类方法)
静态成员是全类所有对象共享的,全类只有一份,不因创建多个对象而产生多份。
不必创建对象,也可通过类名,直接访问静态成员。
经验:访问静态属性和方法时,可直接通过“类名.静态属性名”以及“类名.静态方法名”(推荐)
静态的特点:
静态方法允许直接访问静态成员。
静态方法不能直接访问非静态成员。
静态方法中不允许使用this或super关键字。
静态方法可以继承,不能覆盖,没有多态。
类加载:
JVM首次使用某个类时,将该类的.class文件加载到内存中,进行保存。
II.加载时机:
创建对象
创建子类对象
调用静态属性和方法
Class.forName(“全限定名”); //主动的加载一个类
静态代码块:
类加载时,触发静态代码块的执行(仅一次)。
执行地位:静态属性初始化之后。
作用:可为静态属性赋值,或必要的初始行为。
总结:
static修饰的成员为静态成员,无需创建对象,可直接通过类名访问。
静态方法不能直接访问非静态成员。
静态方法中不能使用this或super。
静态方法可以继承、不能重写、没有多态。
静态代码块在类加载时被执行,且只执行一次。
动态代码块和静态代码块执行初始工作的思路
动态代码块
静态代码块
final
修饰类:此类不能被继承
修饰方法:此方法不能被覆盖
修饰变量:此变量值不可改变--常量(无初始值、只允许赋值一次)
1). 局部常量:显示初始化
2). 实例常量的赋值:显示初始化、动态代码块、构造方法。
a).实例常量赋值deadline:在构造方法完成之前,为实例常量赋值即可。
b).如果在构造方法中为实例常量赋值,必须保证所有的构造方法都可正确赋值。
3). 静态常量的赋值:显示初始化、静态代码块。
a). 静态常量赋值deadline:在类加载完成之前(通过类名调用之前),为静态常量赋值即可。
不同常量类型的特点:
1). 基本数据类型常量:值不可变。
2). 引用数据类型常量:地址不可变。
接口
接口的概念:
接口是种能力和约定
接口的定义:能力
方法的定义:约定
经验:Java为单继承,当父类的方法种类无法满足子类需求时,可实现接口扩充子类能力。
经验:接口支持多实现,可为类扩充多种能力。
接口的语法:
相当于特殊的抽象类,定义方式、组成部分,与抽象类类似。
接口中只能定义公开静态常量(变量)
接口中只能定义公开抽象方法(方法)
接口不是类
接口与抽象类的异同:
相同:
1). 可以编译成字节码文件
2). 不能创建对象。(接口不是类,不是模板的概念,也没有构造方法)
3). 可以声明引用。
4). 具备Object定义的方法。
不同:
1). 接口中的属性只能是公开静态常量(隐式使用public static final修饰)
2). 接口中的方法只能是公开抽象方法(隐式使用public abstract修饰)
3). 没有构造方法、没有动态代码块、没有静态代码块
接口的规范:
任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类为抽象类。
实现接口中的抽象方法时,访问修饰符必须是public。
接口引用:
同父类一样,接口也可声明为引用,并指向实现类对象。
注意:
1). 仅可调用接口中所声明的方法,而不可调用实现类中独有的方法。
2). 可强转回实现类的本身类型,进行独有的属性和方法的调用。(强转前通过instanceof判断)
接口的多态:
不再关注具体的类型,而是关注行为
常见关系
类与类:单继承,extends父类名称
类与接口:多实现,implements 接口名称1,接口名称2,接口名称3
接口与接口:多继承,extends 父接口名称1,父接口名称2
接口:
接口是一种标准。
耦合度:模块与模块之间的关联程度,关联的越密切,耦合越高,关联的越松散,耦合越低。
常量接口:
将多个常用于表示状态和固定值的变量,以形态常量的形式定义在接口中统一管理,提高代码的可读性。
图片解析
接口回调思路
接口的好处
程序的耦合度降低
更自然的使用多态
设计与实现完全分离
更容易搭建程序框架
更容易更换具体实现
常用类
内部类
成员:
1). 在类的内部定义,与实例变量、实例方法同级别的类。
2). 属于外部类的一个实例部分,创建内部类对象,必须依赖外部类对象。
3). Outer out = new Outer();
Outer.Inner in = out.new Inner();
Outer.Inner in = out.new Inner();
4). 当外部类、内部类存在重名属性时,有限访问内部类属性,通过外部类类名.this.外部类实例属性
5). 成员内部类不能定义静态成员。
静态:
1). 不依赖外部类对象,可直接创建或通过类名访问,也可声明静态成员。
2). Outer.Inner.静态成员
Outer.Inner in = new Outer.Inner();
Outer.Inner in = new Outer.Inner();
局部:
1). 定义在外部类的方法中,作用范围和创建对象的范围仅限当前方法中。
2). 局部内部类访问外部类局部变量时,因无法保障变量的生命周期与自身相同,所以修饰为final。
jvm垃圾清理周期图
3). 隐藏类的信息、限制类的使用范围。
匿名:
没有类名的局部内部类,正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
2). 必须继承一个父类或实现一个接口。
3). 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象。
定义格式:new 父类类名(){ 方法区};
4). 优:减少代码量,书写的思路流畅。
5). 劣:可读性较差。
以后可能作为接口回调的方法
Object
概念
超类、基类,所有类的直接或间接父类,位于继承树的最顶端
任何类,如没有书写extends显示继承某个类,都默认直接继承Object类,否则为间接继承
Object类中所定义的方法,是所有对象都具备的方法
Object类型所定义的方法,是所有对象都具备的方法
任何参数,可接受任何对象
作为返回值,可返回任何对象
getClass()方法
public final Class<?> getClass(){}
返回引用中存储的实际对象类型
应用: 通常用于判断两个引用中实际存储对象类型是否一致
hashCode()方法
public int hashCode(){}
返回该对象的十进制的哈希码值
哈希算法根据对象的地址或字符串或数字计算出来的int类型的数值。
哈希码并不唯一,可保证相同对象返回相同哈希码,尽量保证不同对象返回不同哈希码
toString()方法 Object stu = new Student();
public String toString(){}
返回该对象的字符串表示(表现形式)
可以根据程序需求覆盖该方法,如:展示对象各个属性值
Object stu = new Student();
System.out.println(stu.toString());==
Sysotem.out.println(stu.getClass() + "@" + Integer.toHexstring(stu.hashCode()));
equals()方法
public boolean equals(Object obf){}
默认实现为(this == obj),比较两个对象地址是否相同
可进行覆盖,比较两个对象的内容是否相同
方法equals()方法覆盖步骤
比较两个引用是否指向同一个对象
判断obj是否为null
判断两个引用指向的实际对象类型是否一致
强制类型转换
依次比较各个属性值是否相同
finalize()方法
当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列
垃圾对象:没有有效引用指向此对象,为垃圾对象(两个对象相互引用也为无效引用)
垃圾回收:由GC销毁垃圾对象,释放数据存储空间
自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象
手动回收机制:使用System.gc();通知JVM执行垃圾回收(执行有间隔时间)如果在执行过程中,被别的有效引用指向此对象,那么垃圾回收将不再回收此对象,容易造成内存泄漏
不建议使用子类覆盖Object中的finalize()方法去手动执行垃圾回收
包装类
概念:
基本数据类型所对应的引用数据类型
Object可统一所有数据,包装类的默认值是null
包装类中实际上就是持有了一个基本类型的属性,作为数据的存储空间(Byte中有一个byte属性),还提供了常用的转型方法,以及常量,
既可以存储值,又具备了一系列的转型方法和常用常量,比直接使用基本类型的功能更强大
既可以存储值,又具备了一系列的转型方法和常用常量,比直接使用基本类型的功能更强大
类型转换与装箱、拆箱
8种包装类提供不同类型间的转换方式
Number父类中提供的6个共性方法
byte b1 = b.byteValue();
short s1 = 对象名.shortValue();
int i1 = 对象名.intValue();
long l1 = 对象名.longValue();
double d1 = 对象名.doubleValue
float f1 = 对象名.floatValue;
- parseXXX()静态方法(将字符串转化成基本数据类型)
byte b1 = Byte.parseByte("123");
byte b2 = new Byte("123").byteValue(); 先通过构造方法将字符串转化为对象,然后通过byteValue转化为基本数据类型
valueOf()静态方法(将字符串转化为包装类)
Byte b3 = Byte.valueOf((byte)123);整数默认为int 所以要强转为byte
Byte b4 = new Byte("123");通过构造方法在创建对象时转化为Byte类型
注意:需保证类型兼容,否则抛出NumberFormatException异常
JDK5.0之后,自动装箱、拆箱。基本数据类型和包装类自动转换
整数缓存区
java预先创建了256个常用的整数包装类型对象(-128-127)赋值在此范围内的都会指向同一个地址
存入数组的过程
Short s=new Short("129");
byte a=s.byteValue();
System.out.println(a);
byte a=s.byteValue();
System.out.println(a);
结果为-127(因为byte的范围为-128到127所以就变成了-128+1)
在实际应用对象中,对以创建的对象进行复用
String
概念
字符串是常量,创建之后不可改变
字符串字面值存储在字符池中,可以共享
String s = “Hello”;产生一个对象,字符串池中存储
子主题
String s = new String("Hello");产生两个对象,堆,池各存储一个
方法
String s = "Hello";
public char charAt(int index):根据下标获取字符
s.charAt(1);结果为“e”
s.charAt(1);结果为“e”
public boolean contains(Strng str);判断当前字符串中是否包含str
s.comtains("Hel");结果为true s.comtains("Ho");结果为flase
s.comtains("Hel");结果为true s.comtains("Ho");结果为flase
public char[] tocharArray();将字符串转化为字符数组并返回
char[] chs = s.tocharArray();
char[] chs = s.tocharArray();
public int indexOf(String str);查找str首次出现的下标,存在,则返回下标;不存在,则返回-1;
indexOf(int ch) s.indexOf('H')结果为0
indexOf(int ch,int fromIndex) s.indexOf('l',3)结果为1
indexOf(Strnig str, s.indexOf("el")结果为1
indexOf(Strnig str,int fromIndex) s.index("lo", 0)结果为3
indexOf(int ch) s.indexOf('H')结果为0
indexOf(int ch,int fromIndex) s.indexOf('l',3)结果为1
indexOf(Strnig str, s.indexOf("el")结果为1
indexOf(Strnig str,int fromIndex) s.index("lo", 0)结果为3
public int lostIndexOf(String str)查找字符串最后一次出现的下标索引(比较👆indexOf())
public int length()返回字符串的长度
public String trim()去掉字符串前后的空格
public String toUpper(asel);将小写转化为大写
public String toLower(asel);将大写转化为小写
对象名.equalsIgnorelase(另一对象)不论大小写,两者进行比较
public boolean endWith(String str)判断字符串是否以str结尾
public boolean startWith(String str)判断字符串是否以str为前缀
public String replace(char oldChar , char newChar)将旧字符串替换成新字符串返回
s.replace('e' , '123')
s.replace('e' , "222")
s.replace('e' , '123')
s.replace('e' , "222")
public String[] split(String str)根据str将字符拆分成数组,元素多少根据拆分的数量,拆分后str将消失
public String subString(int beginIndex)根据输入的值作为截取返回新字符串(从输入的值开始)
public boolean isEmpty()字符串数组长度为零时返回true,表示为空串
getBytes()
byte[] getBytes() String编码成一个序列使用平台的默认字符集字节,结果存放到一个新的字节数组。
byte[] getBytes(Charset charset)String编码为一个字节序列使用给定的 charset,结果存放到一个新的字节数组。
byte[] getBytes(String charsetName)String编码成一个序列使用指定的字符的字节,并将结果存储到一个新的字节数组。
StringBuffer
可变长字符串,JDK1.0提供,运行效率慢、线路安全
StringBuilder
可变长字符串,JDK5.0提供,运行效率快、线路不安全
保存在池中的条件:1.直接声明,2.首次出现
String s1= "abc"; //(保存在池中)
String s2 = s1 + "def";
//会自动运行StringBuilder会在堆中建一个新的stringBuilder对象,进行拼接,最后通过toString()方法返回一个new String () 对象给s2(保存在堆中)
s2.inter(); //过s2.inter();在池中创建一个为“abcdef”的字符串(池中没有创建过“abcdef”)
String s3 = "abcdef"; //复用s2保存在池中的地址
s2 == s3 true
String s2 = s1 + "def";
//会自动运行StringBuilder会在堆中建一个新的stringBuilder对象,进行拼接,最后通过toString()方法返回一个new String () 对象给s2(保存在堆中)
s2.inter(); //过s2.inter();在池中创建一个为“abcdef”的字符串(池中没有创建过“abcdef”)
String s3 = "abcdef"; //复用s2保存在池中的地址
s2 == s3 true
inter()方法的使用----------------------------------------------------------------------------
String s1= "abc"; //(保存在池中)
String s2 = s1 + "def";
//会自动运行StringBuilder会在堆中建一个新的stringBuilder对象,进行拼接,最后通过toString()方法返回一个new String () 对象给s2(保存在堆中)
String s3 = "abcdef"; //在池中创建一个新的“abcdef”;
/*s2.inter(); //池中已经有一个“abcdef”的地址,所以不能创建,但是会返回一个名为“abcdef”的地址,需要赋值给新的变量。
s2 == s3 flase*/
String s4 = s2.inter();
s3 == s4; true
String s2 = s1 + "def";
//会自动运行StringBuilder会在堆中建一个新的stringBuilder对象,进行拼接,最后通过toString()方法返回一个new String () 对象给s2(保存在堆中)
String s3 = "abcdef"; //在池中创建一个新的“abcdef”;
/*s2.inter(); //池中已经有一个“abcdef”的地址,所以不能创建,但是会返回一个名为“abcdef”的地址,需要赋值给新的变量。
s2 == s3 flase*/
String s4 = s2.inter();
s3 == s4; true
BigDecimal
位置:java.math包中
作用:精确计算浮点数
创建方式:BigDecimal bd = new BigDecimal(“1.0”);
子主题
加:BigDecimal add(BigDecimal bd )
减:BigDecimal subtract(BigDecimal bd )
乘:BigDecimal multiply(BigDecimal bd )
除;BigDecimal divide(BigDecimal bd )
除;BigDecimal divide(BigDecimal bd ,int scal, RoundingMode mode)
参数scal:指定精确到小数点后几位
参数mode:
指定小数部分得取舍模式,通常采用四舍五入得模式
取值为BigDecimal.ROUND_UP
集合
什么是集合
概念:对象的容器,存储对象的对象,可代替数组
特点:容器的工具类,定义了对多个对象进行操作的常用方法
位置:java.util.*;
Collection体系集合
特点:代表一组任意类型的对象,无序、无下标
Collections工具类:
概念:集合工具类,定义了除了存取以外的集合常用方法。
I. public static <T extends Comparable<? super T>> void sort(List<T> list) //排序,要求:必须实现Comparable,必须可与自身类型比,以及父类型比
II. public static void reverse(List<?> list) //反转、倒置元素
III. public static void shuffle(List<?> list) //随机重置顺序
Collection子接口
List
特点:有序、有下标、元素可以重复
List实现类
使用接口引用执向实现类对象的定义方法
List list = new 实现类();
作用:更容易更换具体实现
List list = new 实现类();
作用:更容易更换具体实现
ArrayList
数据结构实现,查询快、增删慢
JDK1.2版本,运行效率快、线程不安全。
ArrayList只能存储对象类型
通过Array.asList();方法可以快速创建一个长度固定的集合,但不可改变
对象构造方法 new ArrayList(); JDK7及之前,无参构造方法直接创建长度为10的Object数组,用还是不用,数组就在那里,(占内存)
new ArrayList(); JDK7及之前,无参构造方法直接创建长度为10的Object数组,用还是不用,数组就在那里,(占内存)
JDK8之后,无参构造方法直接创建长度为0的数组,如果不用,不占空间,只有当真正插入元素时,再分配数组空间(10个空间)
new ArrayList(Collection<? extends E> c);构造一个包含指定集合的元素的列表,它们在集合的迭代器返回的顺序中返回。
new ArrayList(int initialCapacity);用指定的初始容量构造一个空列表。
I. JDK8的ArrayList,实际初始长度是0
II. 首次添加元素时,需要实际分配数组空间,执行数组扩容操作
III. 真正向数组中插入数据,(Lazy懒)用的时候再创建,或再加载,有效的降低无用内存的占用
II. 首次添加元素时,需要实际分配数组空间,执行数组扩容操作
III. 真正向数组中插入数据,(Lazy懒)用的时候再创建,或再加载,有效的降低无用内存的占用
Vector
I. 数组结构存储,查询快,增删慢。
II. JDK 1.0发布,执行效率慢,线程安全。
LinkedList
I. 链表(链接列表)结构存储,查询慢、增删快。
II. 了解:Queue接口:队列、双端队列
III. 了解:栈结构Last In First Out(后进先出)
IV. 了解:队列结构First In First Out(先进先出)
LinkedList.first = Node对象(自身的值、null、下一个节点)
LinkedList.last = Node对象(自身的值、上一个节点、null)
LinkedList.last = Node对象(自身的值、上一个节点、null)
Collection子接口
Set foreach循环:
无序、无下标,元素不可重复
方法:全部继承自Collection中的方法
Set实现类
HashSet
通过对元素的hashCode进行判断,若哈希码相同,再去判断是否为同一对象(地址),如果
判断的对象地址不同,内容相同,则需要覆盖equals()方法去判断,内容相同返回true;
判断的对象地址不同,内容相同,则需要覆盖equals()方法去判断,内容相同返回true;
先判断hashCode是否一致,==比较地址,equals比较内容
//return 123;//固定值,所有对象都要比较
(2)覆盖equals()方法进行判断,如果相同对象输出ture,不同对象,内容相同输入true拒绝插入
public boolean equals(Object obj) {
(1)覆盖hashCode()方法大范围的保证相同的内容输出相同的哈希码,进行比较
public int hashCode() {//6个对象比较15次,原因是6个对象哈希码都相同
//return 123;//固定值,所有对象都要比较
//return this.age;//相对优化:所有对象返回相同哈希码,达到了可以去掉重复的目的,但是,效率又很低(该比的比,不该比的不比)
return this.name.hashCode() + this.age.hashCode() + this.sex.hashCode() + this.score.hashCode();
}(2)覆盖equals()方法进行判断,如果相同对象输出ture,不同对象,内容相同输入true拒绝插入
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null) return false;
if(this.getClass() != obj.getClass()) return false;
Student s =(Student)obj;
if(this.name.equals(s.name) && this.age.equals(s.age) &&
this.sex.equals(s.sex) && this.score.equals(s.score)) return true;
return false;
}
HashSet的底层使用的HashMap类,即是将所有需要存入HashSet的值,直接保存在HashMap中
HashSet如何去掉重复?
LinkedHashSet
1). 底层使用LinkedHashMap(链表结构)存储,节点形式单独存储数据,并可以指向下一个节点,通过顺序访问节点,可保留元素插入顺序
其他与HashSet工具相同,只是保留元素的插入顺序
Set子接口
SortedSet接口
TreeSet
实现了SortedSet接口,要求必须可以对元素排序。
所有插入元素,必须实现Comparable接口,覆盖compareTo方法。
根据compareTo方法返回0作为去重的依据,(意味重复) public int compareTo(Student o) {
在实现类中重新定义comparable()方法比较内容选择升序降序,当返回值为零时表示相同对象,不存入
public int compareTo(Student o) {
System.out.println("compareTo被TreeSet的add方法回调");
//升序//主要排序列
if(this.score < o.score) {//this小,this就靠前
return -1;
}else if(this.score > o.score) {//this大,this就靠后
return 1;
}else {//次要排序列//成绩相同,比另一个属性
if(this.age < o.age) {
return -1;
}else if(this.age > o.age) {
return 1;
}else{//次次要排序列//成绩和年龄相同,比姓名首字母
if(this.name.compareTo(o.name) < 0){
return -1;
}
else if(this.name.compareTo(o.name) > 0){
return 1;
}
else{//最后排序列//成绩和年龄和姓名相同,比性别
return this.sex.compareTo(o.sex);
}
}
}
foreach循环:
for(数据类型 变量名 : 容器名称){ //可遍历集合或数组(常用在无序集合上)
}
Iterator(用与Collection)
1.获得一个迭代器 Itreator<数据类型> ir = 集合名.iterator();
2.判断,进行下一项:hasNext();是否有下一个元素可以遍历,有:true 没有:flash
next();返回下一个元素
next();返回下一个元素
3.循环遍历 while(ir.hasNext()){
System.out.println(ir.next());
}
System.out.println(ir.next());
}
泛型集合
基础
概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
特点:
编译时即可检查,而非运行时抛出异常。
访问时,不必类型转换(拆箱)。
不同泛型之间引用不能相互赋值,泛型不存在多态。
高级
高级类别的知识,熟练应用,需要时间、经验的积累(常用名称:E = Element / T = Type / K = Key / V = Value)
概念:约束-规范类型
泛型的场景:
A). 实例泛型:
类:创建对象时,为类所定义的泛型,进行参数化赋值 MyClass<Integer> mc1 = new MyClass<Integer>();
MyClass<Integer> mc1 = new MyClass<Integer>();
MyClass<Double> mc2 = new MyClass<Double>();
MyClass<String> mc3 = new MyClass<String>();
class MyClass<E>{ //E代表一种通配,可以是任意类型,未指明类型前,为Object
- public void m1(E e) {//泛型可以动态
- }
public void m2(Object o) {//固定写死,不能变
}
}
---------------------------------------------------------------------------------------------------------
接口:实现接口时,为接口所定义的泛型,进行参数化赋值 interface Comparable<E>{//可比较,可排序
interface Comparable<E>{//可比较,可排序
public int compareTo(E obj);//Object参数的通用,还是在具体业务场景中稍显麻烦
}
class MyStudent implements Comparable<MyStudent>{
int age;
public int compareTo(MyStudent obj) {
if(this.age > obj.age) {
return -1;
}
return 0;
}
}
B). 静态泛型: //?代表任意泛型
形参中为T时,可以把返回值定义为T,用传进来的类型,
//<T extends Comparable> 只要List集合中的元素,实现了Comparable接口就行
静态(声明条件)
形参中为T时,可以把返回值定义为T,用传进来的类型,
List集合的元素实现(升级版)
形参中为T时,可以把返回值定义为T,用传进来的类型,
/**
静态集合(形参条件)
//?代表任意泛型
//可接受的是所有的动物集合
/**
* ? extends Animal 泛型类型必须是Animal的子类
* ? extends Comparable 泛型类型必须是Comparable的实现类
* ? super Dog 泛型类型必须是Dog或Dog父类
*/
public static void m1(List<? super Dog> list) {
}
静态集合(声明条件)
形参中为T时,可以把返回值定义为T,用传进来的类型,
返回一个同类型的对象或类型
//<T extends Comparable> 只要List集合中的元素,实现了Comparable接口就行
//<T extends Comparable<Integer>> List集合中的元素,实现的Comparable接口,必须是Integer泛型的
//目标:集合中的所有对象,必须具备本类型的两个元素进行比较的方法
public static <T extends Comparable<T>> void m(List<T> list) {
//当LIst<T>被传入实参后,要求T所代表的类型,必须实现Comparable接口,同时,接口泛型必须是T类型
}
静态(声明条件)
形参中为T时,可以把返回值定义为T,用传进来的类型,
返回一个同类型的对象或类型
传入的实参必须是继承了number然后是Serializable 和 Comparable的实现类(才可以通过实参传入)
public static <T extends Number & Serializable & Comparable> void m1(T t) {//宽泛(特别宽泛,不存在约束)
}
List集合的元素实现(升级版)
形参中为T时,可以把返回值定义为T,用传进来的类型,
返回一个同类型的对象或类型
/**
* 原案例:当LIst<T>被传入实参后,要求T所代表的类型,必须实现Comparable接口,同时,接口泛型必须是T类型(合理,但是过于严格)
* 1.如果要求Comparable<T>必须是自身类型,则导致,无法对一组父类引用的对象进行排序
* 2.故而加入<? super T> 实现Comparable接口时,无论是父类还是子类,都可以成为泛型参数
* 参数化类型,类型安全
*/
public static <T extends Comparable<? super T>> void m(List<T> list) { //Arrays.sort( new Student[5] );
for(int i = 0 ; i < list.size() - 1 ; i++) {
Comparable c = (Comparable)list.get(i);// ClassCastException connot be convert Student to Comparable
int result = c.compareTo(list.get(i+1)); // 1 -1 0
}
}
Map体系集合
父接口的特点:存储一堆数据(Key - Value),无序、无下标,键不可重复,值可重复
实现类
HashMap
java1.2版本提供,线程不安全,运行效率快
允许null作为key或Value
HashMap底层是链表数组
HashMap算法:拿到任何一个对象好,通过hash(key)做运算,key>>>16(除以16),只可能得到0~15之间的一个数组,作为插入数组的下标
子主题
HashMap存入过程
HashSet存入过程
Hashtable
JDK1.0版本提供,线程安全,运行效率慢
不允许用null作为Key或Value
Properties //将集合加载到文件中去
//把properties文件里的内容通过字符流加载到properties集合里
Key和Value都是String类型
setProperty();
getProperty();
主要用于存储key和value都是字符串的情况,常在读取配置文件之后,保存文件中的键值对。反射、JDBC
父类的put()方法里面的类型是Object,而Properties的put()中的类型是String所以基本数用于配置文件的读取
//将集合加载到文件中去
userPros.store(new FileWriter("Files\\userPros.properties"),"用户信息");
//把properties文件里的内容通过字符流加载到properties集合里
userPros.load(new FileReader(file));
SortedMap
TreeMap
自动对key做排序
必须实现Comparable,根据compareTo的返回值去重
注意:HashMap和TreeMap 中的 Key 被HashSet和TreeSet重用 所以HashSet和TreeSet中的对象和值不可重复不可以重复
工具方法
V put(K key, V value)插入键和值
V get(Object key)返回一个key键指定的值
Set<k> keySet() 返回整个key键
Collection<V> values()返回整个Key键的值
boolean containsKey(Object key)如果其中有值的话返回true
default V replace(K key, V value)替换键中的值,返回
default boolean replace(K key, V oldValue, V newValue)替换键中指定的值
V remove(Object key)删除其中键的值,返回
default boolean remove(Object key, Object value)删除键中的指定值,返回
boolean containsKey(Object key) 返回 true表示Map中有Key键中的值
boolean containsValue(Object value) 返回 true则表示这个值有一个或多个键。
Set<Map.Entry<K,V>> entrySet() 返回一个 Set视图的映射包含在这个Map。
三种遍历方法
Map<String> mp = new HashMap<String>();
for(String k : keys) {
System.out.println(k);
System.out.println(maps.get(k));
}
Map<String> mp = new HashMap<String>();
遍历方式一:KeySet();键-无序、无下标、不可重复
Set<String> keys = mp.keySet();for(String k : keys) {
System.out.println(k);
System.out.println(maps.get(k));
}
遍历方式二: values(); 值 可以重复
Collection<String> cs = mp.values();
for(String value : cs) {
System.out.println(value);
}
Collection<String> cs = mp.values();
for(String value : cs) {
System.out.println(value);
}
遍历方式3 entrySet(); 键值对 getKey(); getValue();(便捷,常用)
Set集合里存放的都是node对象(Entry)
Set<Map.Entry<String,String>> entry = maps.entrySet();
for(Map.Entry<String, String> e :entry) {
System.out.println(e.getKey() + ":"+e.getValue()); 分别获取键 和 值
System.out.println(e);//通过toString方法获得
}
Set集合里存放的都是node对象(Entry)
Set<Map.Entry<String,String>> entry = maps.entrySet();
for(Map.Entry<String, String> e :entry) {
System.out.println(e.getKey() + ":"+e.getValue()); 分别获取键 和 值
System.out.println(e);//通过toString方法获得
}
HashMap中的内部类Node(java1.7及之前叫Entry)实现类Map.Entry这个接口,返回了一个
Map.Entry<String, String>
里面的方法就是getKey();和getValue();
覆盖了toString()方法
Map.Entry<String, String>
里面的方法就是getKey();和getValue();
覆盖了toString()方法
集合总结
1. 如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
2. 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
3. 在除需要排序时使用TreeSet,TreeMap外,都应使用HashSet,HashMap,因为他们 的效率更高。
4. 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
5. 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。一旦将对象置入容器内,便损失了该对象的型别信息。
6. 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
2. 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
3. 在除需要排序时使用TreeSet,TreeMap外,都应使用HashSet,HashMap,因为他们 的效率更高。
4. 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
5. 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。一旦将对象置入容器内,便损失了该对象的型别信息。
6. 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
注意
1、Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。
2、Set和Collection拥有一模一样的接口。
3、List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)...。(add/get)
4、一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。
5、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。
HashMap会利用对象的hashCode来快速找到key。
6、Map中元素,可以将key序列、value序列单独抽取出来。
使用keySet()抽取key序列,将map中的所有keys生成一个Set。
使用values()抽取value序列,将map中的所有values生成一个Collection。
为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。
2、Set和Collection拥有一模一样的接口。
3、List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)...。(add/get)
4、一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。
5、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。
HashMap会利用对象的hashCode来快速找到key。
6、Map中元素,可以将key序列、value序列单独抽取出来。
使用keySet()抽取key序列,将map中的所有keys生成一个Set。
使用values()抽取value序列,将map中的所有values生成一个Collection。
为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。
异常
位于java.lang包中 Error:
Exception:
概念:程序在运行过程中出现的特殊情况
异常处理的必须性:任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失
Throwable:
可抛出的,一切错误或异常的父类位于java.lang包中
Error:
JVM、硬件、执行逻辑错误,不能手动处理
Exception:
程序在运行和配置中产生的问题,可处理
CheckedException:受查异常,必须处理
RuntimeException:
运行时异常,可处理,可不处理(仅有此一个类)
异常的产生
自动抛出异常:当程序在运行时遇到不合规范的代码结果时,会产生异常
手动抛出异常:语法:throw new 异常类型("实际参数");
产生结果异常:相当于遇到return语句,导致程序因异常而终止
异常的传递
异常的传递:按照方法的调用链反向传递,如始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息)
受查异常:throws声明异常,修饰在方法参数列表后端
运行时异常:因可处理可不处理,无需声明异常
异常的处理
throws 异常类型
throws Exception 位于参数后,表示提供异常,一旦提供,不在此处解决异常,向上报告,在调用除解决异常或JVM打印堆栈跟踪信息
try{} catch(){}
try{
可能出现异常的代码
}catch(Exception e){
异常处理的相关代码
}finally{
无论是否出现异常,都需执行的代码结构,常用于释放资源
}
可能出现异常的代码
}catch(Exception e){
异常处理的相关代码
e.printStackTrace()打印到标准错误流这个异常和回溯。 (打印堆栈跟踪信息)
System.out.println(e.getMessage())输出错误的原因(构造方法提供private Message的赋值,没有setMessage()方法)}finally{
无论是否出现异常,都需执行的代码结构,常用于释放资源
}
常见异常处理结构
try{} catch{}
try{} catch{} catch{} (父类要在子类的下面,子类顺序无所谓,Exception的同级别的throwable的子类(满足需求才能太添加的)位置在父类前面即可)
try{} catch{} finally{}
try{} catch{} catch{} finally{}
try{} finally{}
多重catch,遵循从子(小)到父(大)的顺序,父类异常在最后。
finally的用法 //核心方法(每个用户都需要使用)5万
//核心方法(每个用户都需要使用)5万
try{
//使用资源对象-->发生了异常
//发生了异常
}catch(Exception e){
e.printStackTrace();
}finally{
//使用完毕,关闭资源
}
自定义异常
场景:根据自身的需求去创建异常
要求:需要继承Exception 或 RuntimeException,常用于RuntimeException
class AgeInputException extends RuntimeException{
public AgeInputException() {}//支持创建无异常原因信息的异常对象
public AgeInputException(String message) {//提供有参构造方法,支持编写异常原因信息
super(message);//调用父类的有参构造方法,为message属性赋值。
}
}
public AgeInputException() {}//支持创建无异常原因信息的异常对象
public AgeInputException(String message) {//提供有参构造方法,支持编写异常原因信息
super(message);//调用父类的有参构造方法,为message属性赋值。
}
}
方法覆盖
带有异常声明的方法覆盖
方法名、参数列表、返回值必须和父类相同
子类的访问修饰符符合父类相同或是比父类更宽
子类中的方法,不能抛出比父类更宽泛的异常
//带有异常的方法覆盖(补充)
//1.父类中方法声明了异常。子类重写后可声明也可不声明
//2.父类中方法没有声明异常,则子类也不可以声明异常。
//3.父类中方法声明了异常,子类可以声明的异常与其相等或是其子类
//4.子类可以声明比父类更多的异常。必须小于其父类声明的异常
//1.父类中方法声明了异常。子类重写后可声明也可不声明
//2.父类中方法没有声明异常,则子类也不可以声明异常。
//3.父类中方法声明了异常,子类可以声明的异常与其相等或是其子类
//4.子类可以声明比父类更多的异常。必须小于其父类声明的异常
字节码文件运行过程
宏观
微观
多线程
初级多线程
进程:运行时的程序,单核cpu在任何时间点上,只能运行一个进程,宏观并行、微观串行
进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
线程
线程,又称轻量级进程(Light Weight Process)程序中的一个顺序控制流程,同时也是CPU的基本调度单位
进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程
进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程
java虚拟机是一个进程,当中默认包含主线程(Main),可通过代码创建多个独立线程,与Main并发执行
线程的组成
CPU时间片:操作系统(OS)会为每个线程分配执行时间
运行数据:
堆空间共享,栈空间独立
堆空间共享,栈空间独立
堆空间:存储线程需使用的多个对象,多个线程可以共享堆空间的对象
栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈
线程的逻辑代码
创建线程
第一种
1.继承Thread类
2.覆盖run()方法
3.创建子类对象
4.调用start()方法
1.继承Thread类
2.覆盖run()方法
3.创建子类对象
4.调用start()方法
第二种
1.实现Runnable接口
2.覆盖run()方法
3.创建实现类对象
4.创建线程对象,将实现类对象付给线程对象
5.调用start()方法
1.实现Runnable接口
2.覆盖run()方法
3.创建实现类对象
4.创建线程对象,将实现类对象付给线程对象
5.调用start()方法
线程的状态
初级
高级
常见的方法
当前线程主动休眠millis秒
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
允许其他线程加入到当前线程中(等加入的线程执行完,此线程才继续执行)
休眠
public static void sleep(long millis)当前线程主动休眠millis秒
放弃
public static void yield()当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
结合
public final void join()允许其他线程加入到当前线程中(等加入的线程执行完,此线程才继续执行)
线程的 sleep()方法和 yield()方法有什么区别?
(1) sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
(2) 线程执行 sleep()方法后转入阻塞(blocked)状态,而执行 yield()方法后转入就绪(ready)状态;
(3)sleep()方法声明抛出 InterruptedException,而 yield()方法没有声明任何异常;
(4)sleep()方法比 yield()方法(跟操作系统 CPU 调度相关)具有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。
(1) sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
(2) 线程执行 sleep()方法后转入阻塞(blocked)状态,而执行 yield()方法后转入就绪(ready)状态;
(3)sleep()方法声明抛出 InterruptedException,而 yield()方法没有声明任何异常;
(4)sleep()方法比 yield()方法(跟操作系统 CPU 调度相关)具有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。
线程的安全问题
当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省
同步方法
第一种同步代码块:
synchronized(临界资源对象){//对临界资源对象加锁
//代码(原子操作)
}
synchronized(临界资源对象){//对临界资源对象加锁
//代码(原子操作)
}
注:
每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记
每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记
第二种同步方法:
synchronized 返回值类型 方法名称(参数列表){
//原子操作
}
synchronized 返回值类型 方法名称(参数列表){
//原子操作
}
同步规则
只有在调用包含同步代码块的方法或者是同步方法时,才需要对象的锁标记
如果调用的是不包含同步代码块的方法或是普通方法时,则不需要锁标记,直接调用
已知线程安全的内容:StringBuffer、Vector、Hashtable
死锁、生产者与消费者
死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
形成死锁的四个必要条件是什么
互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞
请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞
线程通信
等待
对象.wait();
必须在对obj(对象)加锁的同步代码块(或同步方法)中,在一个线程执行期间,调用了obj.wait(),
该线程会释放所拥有的锁标记。同时,进入到obj的等待队列中,等待唤醒
该线程会释放所拥有的锁标记。同时,进入到obj的等待队列中,等待唤醒
通知(唤醒)
对象.notify();、notifyAll();
必须在对obj加锁的同步代码块(或同步方法)中,从obj的Waiting(等待队列)中随机释放一个
或全部线程,对自身线程无影响。
或全部线程,对自身线程无影响。
如何停止一个正在运行的线程?
在java中有以下3种方法可以终止正在运行的线程:
在java中有以下3种方法可以终止正在运行的线程:
使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
使用interrupt方法中断线程。
使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
使用interrupt方法中断线程。
Java 中 interrupted 和 isInterrupted 方法的区别?
interrupt:用于中断线程。调用该方法的线程的状态为将被置为”中断”状态。
注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException 的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。
注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException 的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。
interrupted:是静态方法,查看当前中断信号是true还是false并且清除中断信号。如果一个线程被中断了,第一次调用 interrupted 则返回 true,第二次和后面的就返回 false 了。
isInterrupted:查看当前中断信号是true还是false
isInterrupted:查看当前中断信号是true还是false
高级多线程
Executor
线程池概念
现有问题
线程是宝贵的内存资源、单个线程约占1MB的空间,过多分配易造成内存溢出
频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降
线程池
线程容器,可设定线程分配的数量上限
将预先创建的线程对象存入池中,并重用线程池中的线程对象
避免频繁的创建和销毁
获取线程池
位置:java.util.concurrent
Executor:线程池的顶级接口
ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码
Executors工厂类:通过此类可以获得一个线程池
ExecutorService es = Executors.newFixedThreadPool(int nThreads)
获得一个固定数量的线程池
获得一个固定数量的线程池
ExecutorService es = Executors.newCachedThreadPool()
获得一个动态数量的线程池,如不够则创建新的,没有上限
获得一个动态数量的线程池,如不够则创建新的,没有上限
Callable接口
public interface Callable<V>{
public void call()throws Exception;
}
public void call()throws Exception;
}
JDK5加入,与Runnable接口类似,实现之后代表一个线程任务。
Callable具有泛型返回值、可以声明异常
Callable具有泛型返回值、可以声明异常
Future接口
概念:异步接收ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值
方法:Future f = ExecutorService.submit(task);V get()以阻塞形式等待Future中的异步处理结果(call()的返回值)
<V> 数据名称 = f.get();
<V> 数据名称 = f.get();
Callable中泛型的类型是什么就会返回什么类型的返回值,而Future就会接收什么类型的值,就只能建立这种类型的变量
同步与异步
同步:当一个同步调用发出去后,调用者要一直等待调用结果的返回后,才能进行后续的操作
异步;当一个异步调用发出去后,调用者不用管被调用方法是否完成,
都会继续执行后面的代码。 异步调用,要想获得结果,一般有两种方式:
都会继续执行后面的代码。 异步调用,要想获得结果,一般有两种方式:
主动轮询异步调用的结果;
被调用方通过callback来通知调用方调用结果;
Lock接口
JDK5加入,与synchronized比较,显示定义,结构更灵活
提供更多实用性方法,功能更强大、性能更优越
提供更多实用性方法,功能更强大、性能更优越
Lock l = New ReentrantLook();
方法:
void lock()获取锁,如锁被占用,则等待
void unlock()释放锁
boolean tryLock()尝试获取锁(成功返回true,失败返回false,不阻塞)
读写锁
ReentrantReadWtiteLock;
ReentrantReadWtiteLock rrwl = new ReentrantReadWtiteLock();
ReadLock read = rrwl.readLock();//读锁
WriteLock write = rrwl.writeLock();//写锁
ReadLock read = rrwl.readLock();//读锁
WriteLock write = rrwl.writeLock();//写锁
一种支持一些多读的同步锁,读写分离,可别分配读锁、写锁。
支持多次分配读锁,使多个读操作可以并发执行
互斥规则
写-写:互斥,阻塞。
读-写:互斥,读阻塞写、写阻塞读
读-读:不互斥、不阻塞
在读操作远远高于写操作的环境中,可在保证线程安全的情况下,提高运行效率
在读中加Read锁进入的时候只会获得一次锁,大大降低使用时间,增加效率
线程安全的集合
Collections中的工具方法(获得线程安全集合)
public static <T> Collection<T> synchronizedCollection(Collection<T> c)(获得一个Collection安全集合)
public static <T> List<T> synchronizedList(List<T> list)(获得一个List安全集合) 创建方式
返回一个新的List类型的集合对象
子类构造方法
父类构造方法
this.c = Objects.requireNonNull(c);//非空判断
用Final Object mutex作为同步代码块的对象,进行加锁,保证线程安全
创建方式
List<String> li = new ArrayList<String>();
List<String> list = Collections.synchronizedList(li);
返回一个新的List类型的集合对象
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
子类构造方法
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
父类构造方法
SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);//非空判断
mutex = this;
}
final Object mutex
final Collection<E> c
用Final Object mutex作为同步代码块的对象,进行加锁,保证线程安全
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public static <T> Set<T> synchronizedSet(Set<T> s)(获得一个Set安全集合)
public static <K , V> Map<K , V> synchronizedMap(Map<K , V> m)(获得一个Map安全集合)
public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
public static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m)
JDK1.2提供,接口统一、维护性高,但性能没有提升,均以synchronized实现
CopyOnWtiteArrayList
线程安全的ArrayList,加强版读写分离
写有锁,读无锁,读写之间不阻塞,优于读写锁
写入时,先copy一个容器副本、在添加新元素,最后替换引用
使用方式与ArrayList无异
List<String> list = new CopyOnWriteArrayList<String>(); 写有锁
都将底层数组做了一次复制,写的是新数组,完成赋值后,再将新数组替换掉旧数组!
public boolean add(E e) {
final ReentrantLock lock = this.lock;//将原来建好的锁赋值给变量,
lock.lock(); //获取锁 相当于建了一个新锁
try {
Object[] elements = getArray();//获得一个数组
int len = elements.length;//数组的长度,初始为0
Object[] newElements = Arrays.copyOf(elements, len + 1);//扩容操作
newElements[len] = e;//将新数组的下标0存入传过来的e
setArray(newElements);//将新创建的数组调用setArray
return true;//成功返回true
} finally {
lock.unlock();//释放锁
}
}
private transient volatile Object[] array; 读无锁
读的是写操作完成之前的旧数组!写完之后,才能读到的新数组的新值
public E get(int index) {
return get(getArray(), index);//调用两个方法查询下标的值
}
List<String> list = new CopyOnWriteArrayList<String>();
写有锁
都将底层数组做了一次复制,写的是新数组,完成赋值后,再将新数组替换掉旧数组!
每调用一次,底层方法扩容一次!
public boolean add(E e) {final ReentrantLock lock = this.lock;//将原来建好的锁赋值给变量,
lock.lock(); //获取锁 相当于建了一个新锁
try {
Object[] elements = getArray();//获得一个数组
int len = elements.length;//数组的长度,初始为0
Object[] newElements = Arrays.copyOf(elements, len + 1);//扩容操作
newElements[len] = e;//将新数组的下标0存入传过来的e
setArray(newElements);//将新创建的数组调用setArray
return true;//成功返回true
} finally {
lock.unlock();//释放锁
}
}
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
返回一个存好array数组
return array;
}
返回一个存好array数组
final void setArray(Object[] a) {
array = a;
}
将传过来的数组存入array
array = a;
}
将传过来的数组存入array
读无锁
读的是写操作完成之前的旧数组!写完之后,才能读到的新数组的新值public E get(int index) {
return get(getArray(), index);//调用两个方法查询下标的值
}
final Object[] getArray() {
return array;
}
返回一个array数组
return array;
}
返回一个array数组
private E get(Object[] a, int index) {
return (E) a[index];
}
返回一个数组index下标的值
return (E) a[index];
}
返回一个数组index下标的值
CopyOnWtiteArraySet
线程安全的Set,底层使用CopyOnWriteArrayList实现
唯一不同在于,使用addIfAbsent()添加元素,会遍历数组 public boolean add(E e) {
public boolean addIfAbsent(E e) {
//判断过程
public boolean add(E e) {
return al.addIfAbsent(e);
}//调用CopyOnWriteArrayList下的addIfAbsent()方法
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();//e插入的值,snapshot数组
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);//第四步的操作
}//调用indexOf()方法如果存在返回false,如果不存在返回新建的数组。
//判断过程
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
if (o == null) {
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
} else {
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;//表示没有相同元素
}
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
Object[] current = getArray();
Object[] newElements = Arrays.copyOf(current, len + 1);
} finally {
}
final ReentrantLock lock = this.lock;
lock.lock();//获得锁
try { Object[] current = getArray();
int len = current.length;
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
if (indexOf(e, current, common, len) >= 0)
return false;
}
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);//和CopyOnWroteArrayList相同
return true;
} finally {lock.unlock();//释放锁
}}
如存在元素,则不添加(扔掉副本)
Set<String> set = new CopyOnWriteArraySet<String>();
ConcurrentHashMap
Map<String,String> map = new ConcurrentHashMap<String,String>()
1.7版本
初始容量默认为16段(Segment),使用分段锁设计
不对整个Map加锁,而是为每个Segment加锁
当多个对象存入同一个Segment时,才需要互斥
最理想的状态为16个对象分别存入16个Segment,并行数量16
使用方式与HashMap无异
只有16把锁,后续扩容之后也只有16把锁,一个锁锁多个链表数组(与1.8比较)
子主题
子主题
子主题
Map<String,String> map = new ConcurrentHashMap<String,String>();
1.8版本 同步锁锁的是表头对象,拿到锁的对象要先做节点遍历。查看有没有相同的key,
每个链表的第一个都是表头,都有锁,创建新的也是会有(和1.7不同之处)
CAS(ComparableAndSwap)交换算法和同步锁
CAS(ComparableAndSwap)交换算法
V:要更新的值
E:预期的值
N:新值
当V == E时,V == N
V:要更新的值
E:预期的值
N:新值
当V == E时,V == N
如果在修改过程中,V已经发生变化,V != E ,则取消当前赋值操作,做下一次赋值操作
多线程访问,一个线程在修改过程中,时间片没了,下一个线程也去修改完成,上一个线程分到时间片发现V已经发生变化
多线程访问,一个线程在修改过程中,时间片没了,下一个线程也去修改完成,上一个线程分到时间片发现V已经发生变化
同步锁锁的是表头对象,拿到锁的对象要先做节点遍历。查看有没有相同的key,
相同覆盖,不同,则挂在最后一个节点的next上
每个链表的第一个都是表头,都有锁,创建新的也是会有(和1.7不同之处) public V put(K key, V value) {
return putVal(key, value, false);//用putVal()来判断传入的值
}
return putVal(key, value, false);//用putVal()来判断传入的值
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;//f为Node中的第一个数组中的对象,作为锁头的对象
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//用Cab方法去判断如果插入的为null表明里面没有值,
if (casTabAt(tab, i, null, 插入成功,为第一个,上锁,成为锁头
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;//f为Node中的第一个数组中的对象,作为锁头的对象
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//用Cab方法去判断如果插入的为null表明里面没有值,
if (casTabAt(tab, i, null, 插入成功,为第一个,上锁,成为锁头
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
synchronized (f) {//对f上锁,插入到这个链表数组中的元素都要经过锁头f获得锁,遍历对比
if (tabAt(tab, i) == f) { 如有相同键的对象,覆盖,没有,插入到最后一个
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {//如果key相等
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;//值覆盖
break;
}
Node<K,V> pred = e;//找到最后一个
if ((e = e.next) == null) {//如果它的下一个是null
pred.next = new Node<K,V>(hash, key,//将自己的地址存到它的next中
value, null);
break;
}
}
}
if (tabAt(tab, i) == f) { 如有相同键的对象,覆盖,没有,插入到最后一个
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {//如果key相等
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;//值覆盖
break;
}
Node<K,V> pred = e;//找到最后一个
if ((e = e.next) == null) {//如果它的下一个是null
pred.next = new Node<K,V>(hash, key,//将自己的地址存到它的next中
value, null);
break;
}
}
}
Queue接口(队列)
Collection的子接口,表示队列FIFO(First In First Out)
常用方法
会抛出异常
boolean add(E e)//顺序添加一个元素(到达上限后,在添加则会抛出异常)
E remove() //获得第一个元素并移除(如果队列没有元素时,则抛异常)
E element() //获得一个元素但不移除(如果队列中没有元素时,则抛异常)
返回特殊值:推荐使用
boolean offer(E e)//顺序添加一个元素(到达上限后,在添加则会返回false)
E pool() //获得第一个元素并移除(如果队列没有元素时,则返回null)
E peek() //获得一个元素但不移除(如果队列中没有元素时,则返回null)
子接口ConcurrentLinkedQueue(安全版本Linked队列)
线程安全、可高效读写的队列,高并发下性能最好的队列
linkedList的三种形式
列表(尾部添加,指定下标)
add()
链表(头尾相加)
addfirst
addlost
队列(FIFO)
offer
peek
poll
无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
V:要更新的变量、E:预期值、N:新值
只有当V == E时,V = N;否则表示已被更新过,则取消当前操作
Queue<String> queue = new ConcurrentLinkedQueue<String>(); public boolean offer(E e) {
public boolean offer(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
if (p.casNext(null, newNode)) {
if (p != t) // hop two nodes at a time
casTail(t, newNode);
return true;
}
}
else if (p == q)
p = (t != (t = tail)) ? t : head;
else
p = (p != t && t != (t = tail)) ? t : q;
}
}
Queue<String> qu = new Linked<String>();可以只使用队列的方法,但是不安全
子接口BlockingQueue接口(阻塞队列)
Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法
方法
void put(E e)//将指定元素插入此队列中,如果没有可用空间,则等待
E take()//获取并移除此队列头部元素,如果没有可用元素,则等待
用于解决生产者、消费者问题
实现类
ArrayBlockingQueue:数组结构实现,有界队列(手工固定上限)
BlockingQueue<String> abq = new ArrayBlockingQueue<String>(10)LinkedBlockingQueue:链表结构实现,无界队列。(默认上限Integer.MAX_VALUE)
BlockingQueue<String> lbq = new LinkedBlockingQueue<String>(); 补充知识
用户 (User) 线程:运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
守护 (Daemon) 线程:运行在后台,为其他前台线程服务。也可以说守护线程是 JVM 中非守护线程的 “佣人”。一旦所有用户线程都结束运行,守护线程会随 JVM 一起结束工作
守护 (Daemon) 线程:运行在后台,为其他前台线程服务。也可以说守护线程是 JVM 中非守护线程的 “佣人”。一旦所有用户线程都结束运行,守护线程会随 JVM 一起结束工作
IO框架
概念:内存与存储设备之间传输数据的通道
流的分类
按方向
输入流:将<存储设备>中的内容读入到<内存>中
输出流:将<内存>中的内容写入到<存储设备>中
按单位
字节流:以字节为单位,可以读写所有数据
字符流:以字符为单位,只能读写文本数据
按功能
节点流:具有实际传输数据的读写功能
过滤流:在节点流的基础之上增强功能
字节流
字节流的父类(抽象类)
InputStream:字节输入流
public int read(){}
public int read(byte[] b){}
public int read(byte[] b,int off , int len){}
OutoutStream:字节输出流
public void write(){}
public void write(byte[] b){}
public void write(byte[] b,int off , int len){}
字节节点流
FileInputStream
public int read(byte[] b){}
从流中读取多个字节,将读到内容存到b数组,返回实际读到的字节数;如过达到文件的尾部,则返回-1
//将指定图片,上传一份到项目里来!
//文件是在存储设备中的---》读到程序中来---->写入到存储设备中
//输入流
FileInputStream fis = new FileInputStream("C:\\Users\\Lenovo\\Desktop\\1570252842407.jpg");
//输出流
FileOutputStream fos = new FileOutputStream("messi.jpg");
//缓冲空间过大过小都容易有小问题
int len = 0;//代表每次读到的字节
while((len = fis.read())!=-1) {//只要不读到-1
fos.write(len);//读多少 写多少
}
//释放资源!
fis.close();
fos.close();
}
}
//文件是在存储设备中的---》读到程序中来---->写入到存储设备中
//输入流
FileInputStream fis = new FileInputStream("C:\\Users\\Lenovo\\Desktop\\1570252842407.jpg");
//输出流
FileOutputStream fos = new FileOutputStream("messi.jpg");
//缓冲空间过大过小都容易有小问题
int len = 0;//代表每次读到的字节
while((len = fis.read())!=-1) {//只要不读到-1
fos.write(len);//读多少 写多少
}
//释放资源!
fis.close();
fos.close();
}
}
FileOutputStream
public void write(byte[] b){}
一次写多个字节,将b数组中所有字节,写入输出流
字节过滤流
缓冲流:
分类
BufferedInputStream
BufferedOutoutStream(主要用于BufferedOutoutStream)
FileOutputStream fos = new FileOutputStream("文件名");
BufferedOutoutStream bos = new BufferedOutoutStream(fos);
BufferedOutoutStream bos = new BufferedOutoutStream(fos);
作用
提高IO效率,减少访问磁盘的次数
数据存储在缓存区中,flush是将缓存区的内容写入文件中,也可以直接close
对象流
分类
ObjectOutputStream
ObjectInputStream
功能
增强的缓冲区功能
增强了读写8种基本数据类型和字符串功能
增强了读写对象的功能
readObject()从流中读取一个对象
writeObject(object obj)向流中写入一个对象
使用流传输的对象的过程称为序列化、反序列化
再读取对象的过程中,因为不知道最后一个对象是否在哪一个,如果访问到不在的对象会报错,所以采用try{}catch{}解决 while(true){
while(true){
try {
Object obj = ois.readObject();
System.out.println(obj);
} catch (Exception e) {
break;//当遇到查询不到对象报
异常时捕获结束循环
}
}
对象序列化
必须实现Serializable接口
必须保证其所有属性均可序列化(所有属性实现Serializable接口)
transient修饰为临时属性,不参与序列化
读取到文件尾部的标志:java.io.EOFException
字符流
字符编码
UTF-8
针对Unicode的可变长度字符编码
GB2312
简体中文
GBK
简体中文、扩充
BIG5
台湾、繁体中文
ISO-8859-1
收录除ASCII外,还包括西欧、希腊语、泰语、阿拉伯语、稀薄来语对应得文字符号
当编码方式和解码方式不一致时,会出现乱码
父类
Reader:字符输入流
public int read(){}
public int read(byte[] b){}
public int read(byte[] b,int off , int len){}
Writer:字符输入流
public void write(){}
public void write(byte[] b){}
public void write(byte[] b,int off , int len){}
字符节点流
FileWriter
public void writr(String str)
FileReader
public int read(char[] c)
FileWriter
public void writr(String str)
一次写多个字符
public void writr(String str)
FileReader
public int read(char[] c)
从流中读取多个字符,返回实际读到的字符数,如过达到文件的尾部,则返回-1
用char数组来接收,遍历循环数组,如果结果为-1时break;
用char数组来接收,遍历循环数组,如果结果为-1时break;
public int read(char[] c)
字符过滤流
缓冲流:
BufferedWriter
支持换行符:newLine()
BufferedReader
PrintWriter
封装了print()/println()方法,支持写入后换行
缓冲流:
BufferedWriter
支持换行符:newLine()
BufferedReader
BufferedWriter
支持换行符:newLine()
不足:每次换行都要书写一遍,代码冗余
支持换行符:newLine()
BufferedReader
每读一次读一行:readLine();
while(true) {
String s = br.readLine();
if(s == null) {
break;
}
System.out.println(s);
}
String s = br.readLine();
if(s == null) {
break;
}
System.out.println(s);
}
PrintWriter
封装了print()/println()方法,支持写入后换行
当使用print()不会换行
封装了print()/println()方法,支持写入后换行
字符节点流
桥转换流
InputStreamReader/OutputStreamWriter
可将字节流转化为字符流
可设置字符的编码方式
书写方式
//1.字节输出流
OutputStream os = new FileOutputStream("Files\\convert.txt");//字节输出流
//编码 2.转换为字符输出流
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");//指定输出的数据 编码格式
//3.包装字符过滤流
PrintWriter pw = new PrintWriter(osw);
//4.关闭流
OutputStream os = new FileOutputStream("Files\\convert.txt");//字节输出流
//编码 2.转换为字符输出流
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");//指定输出的数据 编码格式
//3.包装字符过滤流
PrintWriter pw = new PrintWriter(osw);
//4.关闭流
//1.字节输入流
InputStream is = new FileInputStream("Files\\convert.txt");
//解码 2.转换为字符输入流
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
//3.包装字符过滤流
BufferedReader br = new BufferedReader(isr);
//4.关闭流
InputStream is = new FileInputStream("Files\\convert.txt");
//解码 2.转换为字符输入流
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
//3.包装字符过滤流
BufferedReader br = new BufferedReader(isr);
//4.关闭流
读入方式
while(true) {
String s = br.readLine();
if(s == null) {
break;
}
System.out.println(s);
}
br.close();
while(true) {
String s = br.readLine();
if(s == null) {
break;
}
System.out.println(s);
}
br.close();
File类
概念:代表物理盘符中的一个文件或则文件夹
File file = new File("文件名");
方法
能不能
boolean file.canExecute()所有可以打开的文件或文件夹,都是可执行的!
boolean file.canWrite() 能不能修改文件(如果文件改为只读,返回false)
boolean file.canRead()能不能执行文件(文件改为隐藏也返回true)
创建和删除
file.mkdirs()如果文件夹不存在会建立一个新的文件夹(只会建文件夹)
file.createNewFile()如果不存在就创建一个文件,如果存在,就返回false
file.delete()如果文件存在,则删除,返回true
file.deleteOnExit();//JVM终止时,执行删除文件
获得
file.getAbsolutePath()获得绝对路径
file.getPath()获得相对路径
file.getName()获得文件名 名字.后缀
file.getFreeSpace() / 1024 / 1024/ 1024//获取硬盘的空闲空间
file.getTotalSpace()/1024 / 1024 / 1024//获取硬盘总空间
file.getParent()获取指定文件的上一级目录
(System.currentTimeMillis() - file.lastModified()) / 1000 / 60获取文件最后一次修改的时间
file.length()获得文件内容的字节
判断
file.isDirectory()判断是否为文件夹
file.isFile()判断是否为文件
file.isHidden()判断文件是否为隐藏
file.exists()判断文件是否存在
FileFilter接口
public interface FileFilter{
boolean accept(File pathname)
}
boolean accept(File pathname)
}
当调用File类中的listFile()方法时,支持传入FileFilter接口接口实现类,
对获取文件进行过滤,只有满足条件的文件才可以出现在listFiles()的返回值中
对获取文件进行过滤,只有满足条件的文件才可以出现在listFiles()的返回值中
class MyFilter implements FileFilter{
public boolean accept(File pathname) {//true就保存,false就过滤
if(pathname.isFile()) {
if(pathname.getName().endsWith(".doc")) {
return true;//保存
}
}
return false;//过滤
}
}
public boolean accept(File pathname) {//true就保存,false就过滤
if(pathname.isFile()) {
if(pathname.getName().endsWith(".doc")) {
return true;//保存
}
}
return false;//过滤
}
}
网络编程
计算机网络
为实现资源共享和信息传递,通过通信线路连接起来的若干主机(Host)
互联网:(Internet)点与点相连
万维网:(WWW - World wide web)端与端相连
物联网:(IoT - Internet of things)物与物相连
网络编程:让计算机与计算机之间建立连接、进行通信
网络模型
OSI(Oper System Ineterconnection)开放式系统互联
第一层:物理层为设备之间的数据通信提供传输信号和物理介质。(双绞线、 光导纤维)
第二层:链路层在物理层上,通过规程或协议(差错控制)来控制传输数据的正确性。(MAC)
第三层:网络层负责定义了能够标识所有网络节点的逻辑地址。(IP地址)
第四层:传输层负责是否选择差错恢复协议、数据流重用、错误顺序重排。(TCP、 UDP)
第五层:会话层负责使应用建立和维持会话,使通信在失效时继续恢复通信。(断点续传)
第六层:表示层负责定义转换数据格式及加密,允许选择以二进制或ASCII格式传输。
第七层:应用层负责文件访问和管理、可靠运输服务、远程操作服务。(HTTP、FTP、SMTP)
TCP/IP模型
一组用于实现网络互连的通讯协议,
将协议分为4个层次
将协议分为4个层次
第一层按口层负责建文电路连接,是整个同格的物用基础, 典型的协议包括以太网、ADSI等等。
第二层:网络层负责分配地址和传送进制数据, 主要协议是IP协议。
第三层:传输层负责传送文本数据,主要协议是TCP、 UDP协议。
第四层:应用层负责传送各种最终形态的数据,是直接与用户打交道的层,典型协议是HTTP、FTP等。
TCP/UCD
TCP协议: Transmission Control Protocol传输控制协议
是一种面向连接的、可靠的、基于字节流的传输层通信协议。数据大小无限制。建立连接的过程需要三次握手,断开连接的过程需要四次挥手。
子主题
UDP协议: User Datagram Protocol用户数据报协议
是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,每个包的大小64KB。
子主题
IP
IP协议: Internet Protocol Address互联网协议地址/网际协议地址●分配给互联网设备的数字标签(唯标识)
IP地址分为两种:
IPV4: 4字节32位整数,并分成4段8位的二进制数,每8位之间用圆点隔开,每8位整数可以转换为一一个0~255的十进制整数。
格式: D.D.D.D例如: 255.255.255.255
格式: D.D.D.D例如: 255.255.255.255
●A类:政府机构,1.0.0.1 ~ 126.255.255.254
●B类:中型企业,128.0.0.1 ~ 191.255.255.254
●C类:个人用户,192.0.0.1 ~ 223.255.255.254
●D类:用于组播,224.0.0.1 ~ 239.255.255.254
●E类:用于实验,240.0.0.1 ~ 255.255.255.254
.回环地址: 127.0.0.1, 指本机,- -般用于测试使用。查看IP命令: ipconfig
●测试IP命令: ping D.D.D.D
●IPV6: 16字节128位整数,并分成8段十六进制数,每16位之间用圆点隔开,每16位整数可以转换为一个0~65535的十进制数。
格式: X.X.X.X.X.X.X.X例如: FFFF.FFFF.FFFF.FFFF.FFFF.FFF.FFFF.FFFF
格式: X.X.X.X.X.X.X.X例如: FFFF.FFFF.FFFF.FFFF.FFFF.FFF.FFFF.FFFF
Port
●端口号:在通信实体上进行网络通讯的程序的唯一 标识。
●端口分类
公认端口: 0~1023
●注册端口: 1024~49151
●动态或私有端口: 49152~65535
.常用端口:
●MySql: 3306
●. Oracle: 1521
●Tomcat: 8080
●SMTP: 25
Web服务器: 80
FTP服务器: 21
InetAddress类
概念: 表示互联网协议(P)地址对象,封装了与该P地址相关的所有信息,并提供获取信息的常用方法。
方法
public static InetAddress getLocalHost()获得本地主机地址对象
public static InetAddress getByName(String host)根据主机名称获得地址对象
public static InetAddress[ ]getAllByName(String host)获得所有相关地址对象
public String getHostAddress()获取IP地址字符串
public String getHostName()获得IP地址主机名
cmd获得本机ip ipconfig
基于Tcp的网络编程
Socket编程
客户端Socket
服务器ServerSocket
通信要求:IP地址+端口号
Socket(套接字)是网络中的一个通信节点
开发步骤
客户端 Socket client = new Socket("192.168.1.104", 1111);
Socket client = new Socket("192.168.1.104", 1111);
//创建客服端对象参数为IP和端口号
InputStream is = client.getInputStream();//用client创建一个字节输入流
InputStreamReader isr = new InputStreamReader(is ,"UTF-8");
BufferedReader br = new BufferedReader(isr);
//用桥转换流和缓冲流转换成一个可以设置编码方式的可一次读一行的字符输入过滤流
OutputStream os = client.getOutputStream();//用client创建一个字节输出流
OutputStreamWriter osw = new OutputStreamWriter(os , "UTF-8");
PrintWriter pw = new PrintWriter(osw);
服务器 ServerSocket server = new ServerSocket(5555);//创建ServerSocket,指定端口号
ServerSocket server = new ServerSocket(5555);//创建ServerSocket,指定端口号
Socket client = server.accept();//调用accept等待客户端接入
InputStream is = client.getInputStream();//用client创建一个字节输入流
InputStreamReader isr = new InputStreamReader(is ,"UTF-8");
BufferedReader br = new BufferedReader(isr);
OutputStream os = client.getOutputStream();//用client创建一个字节输出流
OutputStreamWriter osw = new OutputStreamWriter(os , "UTF-8");
PrintWriter pw = new PrintWriter(osw);
//用桥转换流和缓冲流转换成一个可以设置编码方式的可写完自动换行的字符输出过滤流
Properties集合添加 //1将集合加载到文件中去
//把properties文件里的内容通过字符流加载到properties集合里
Properties userPros = new Properties();
//1将集合加载到文件中去
userPros.store(new FileWriter("Files\\userPros.properties"),"用户信息");
//把properties文件里的内容通过字符流加载到properties集合里
userPros.load(new FileReader(file));
userPros = new Properties();
//指向信息配置文件
File file = new File("Files\\userPros.properties");
if(file.exists()) {//文件存在
try {
//把properties文件里的内容通过字符流加载到properties集合里
userPros.load(new FileReader(file));
} catch (Exception e) {
e.printStackTrace();
}
}
//指向信息配置文件
File file = new File("Files\\userPros.properties");
if(file.exists()) {//文件存在
try {
//把properties文件里的内容通过字符流加载到properties集合里
userPros.load(new FileReader(file));
} catch (Exception e) {
e.printStackTrace();
}
}
反射
类对象
区别
类的对象:基于某个类new出来的对象,也称为实例对象
类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)
获取类对象
通过类的对象,获取类对象
Student s = new Student();
Class c = s.getClass();
Student s = new Student();
Class c = s.getClass();
通过类名获取类对象
Class c = 类名.class;
Class c = 类名.class;
通过静态方法获取类对象
Class c = Class.forName("包名.类名");
Class c = Class.forName("包名.类名");
常用方法 //获得构造方法的形参
//获得指定类对象的包
Package pack = c.getPackage();
Package pack = c.getPackage();
//获得父类的Class对象
Class superClass = c.getSuperclass();
Class superClass = c.getSuperclass();
//获得接口的Class对象
Class[] interfaces = c.getInterfaces();
Class[] interfaces = c.getInterfaces();
//获取属性(自身+父类的公开属性)
Field[] fields = c.getFields();
Field[] fields = c.getFields();
//获取方法(自身+父类的所有公开方法)
Method[] methods = c.getMethods();
Method[] methods = c.getMethods();
//获得Class对象的自身所有属性
Field[] fields2 = c.getDeclaredFields();
Field[] fields2 = c.getDeclaredFields();
//获得自身的全部方法
Method[] methods2 = c.getDeclaredMethods();
Method[] methods2 = c.getDeclaredMethods();
//获得构造方法的形参
Constructor[] cs = c.getConstructors();//先获得全部构造方法
for(Constructor ct : cs) {
System.out.print(ct.getName()+":");
Class[] param = ct.getParameterTypes();//通过构造方法获得参数
for(Class p : param) {
System.out.println(p.getName());
}
}
工厂设计模式 //通过输入流获取全限定名
//创建对象!
开发中有一个非常重要的原则“开闭原则”,对拓展开放、对修改关闭。
工厂模式主要负责对象创建的问题
可通过反射进行工厂模式的设计,完成动态的对象创建。
创建对象的两种方式(以Student为例)
Student s = new Student();
Class c = Student.class;
Object o = c.newInstance();
Student s2 = (Student)o;
Object o = c.newInstance();
Student s2 = (Student)o;
//通过输入流获取全限定名
FileReader fr = new FileReader("Files\\properties.txt");
BufferedReader br = new BufferedReader(fr);
String str = br.readLine();
Object oo = createObject(str);
//创建对象!
//工厂:创建对象工厂
public static Object createObject(String str){
try {
Class c = Class.forName(str);
Object o = c.newInstance();
return o;//通过一系列的转换返回一个Object对象
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
invoke //反射 类的对象
m4.setAccessible(true);//使用完此方法后可以访问私有方法
Object o = TestInvoke.createObject("com.qf.day35.t0.invoke.Student");//获取对象
//通用编程(调用任何一个方法)
一:通过Class类对象去调用对象的方法
//反射 类的对象
Object o = createObject("com.qf.day35.t1.reflects.Student");
//类对象
Class c =o.getClass();
//name->方法名 ,parameterTypes ->参数列表类型
Method m = c.getMethod("study",null);
//通过invoke方法,执行某个实例方法,参数:Object->所需对象,args->调用的方法所需的实参
m.invoke(o, null);
//注意:反射是一种Java底层技术,可以取消语言检查。 突破封装
m4.setAccessible(true);//使用完此方法后可以访问私有方法
m4.invoke(o,null);
二:通过方法输入对象,形参类型和实参
Object o = TestInvoke.createObject("com.qf.day35.t0.invoke.Student");//获取对象
invokeAny(o,"exam",new Class[]{int.class,double.class,String.class},1,90,"赵公民");
invokeAny(o, "study",null,null);//调用invokeAny()方法执行其中的方法
//通用编程(调用任何一个方法)
public static void invokeAny(Object obj,String methodName,Class[] types,Object... args) throws Exception {
//使用反射技术执行任何方法
//类对象
Class c = obj.getClass();
//获得方法的对象Method
Method m = c.getDeclaredMethod(methodName, types);
//执行方法
m.invoke(obj, args);
}
单例模式 饿汉式:天生线程安全(无锁)、
class Singleton{
class Singleton{
class Singleton{
只允许创建一个该类的对象
饿汉式:天生线程安全(无锁)、
类加载时创建(不用的时候,也被迫创建了,占用资源)
class Singleton{
private static final Singleton instance = new Singleton();//私有静态常量
private Singleton() {}//私有构造方法
public static Singleton getInstance() {//通过调用静态方法创建对象
return instance;
}
}
懒汉式:使用时创建、天生线程不安全(加同步锁); 效率低
class Singleton{
private static Singleton instance = null;//私有静态最初为null
private Singleton() {}//私有构造方法
public synchronized static Singleton getInstance() {
if(instance == null) {//第一次创建时
instance = new Singleton();
}
return instance;//返回对象
}
}
懒汉式(加强版):使用时创建、天生线程安全
class Singleton{
private Singleton() {}
private static class Holder{//私有内部类
static final Singleton instance= new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;//调用私有内部类
}
}
JDK8
Lambda表达式
概念:允许把函数作为一个方法的参数(函数作为参数传递到方法中)
函数式接口 变量名 = (参数1,参数2)->{
//方法体
}
//方法体
}
注意事项
形参列表的数据类型会自动判断
如果形参列表为空,只需保留()
如果形参只有一个,()可以省略,只要参数名字即可
如果执行语句只有一句,且无返回值,{}可以省略
若有返回值,仍想省略{},return也省略。保证执行语句只有一句
lambda表达式不会生成单独的内部类文件
lambda访问局部变量时,变量要修饰final,如果没加,会自动添加。
用lamada表达式启动线程
//第一种方式建立内部类
new Thread(){
public void run() {
System.out.println("子线程启动");
}
}.start();
new Thread(){
public void run() {
System.out.println("子线程启动");
}
}.start();
//第二种方式,用lambda表达式
Runnable r = ()->{System.out.println("第二个子线程启动");};
new Thread(r).start();
Runnable r = ()->{System.out.println("第二个子线程启动");};
new Thread(r).start();
//第三种,lambda表达式简写
new Thread( ()->System.out.println("第三个线程") ).start();
new Thread( ()->System.out.println("第三个线程") ).start();
函数式接口
如果一个接口只有一个抽象方法,则该接口称为函数式接口
为了保证接口达到要求,可以添加@FunctionalInterface注解
四个核心函数式接口
Consumer<T>消费型接口 void accept(T t)
接收一个参数,不返回
第一种:通过自己写表达式,自己调自己
Consumer<String> cs = (e)->System.out.println("今天天气:" + e);
cs.accept("晴");
Consumer<String> cs = (e)->System.out.println("今天天气:" + e);
cs.accept("晴");
第二种:通过方法形参中写函数型接口,调用时写Lambda表达式
方法调用:m(1000.0, (s)-> {System.out.println("今天挣了" + s +"块");});
public static void m(Double b,Consumer<Double> cb){
cb.accept(b);//方法执行
}
方法调用:m(1000.0, (s)-> {System.out.println("今天挣了" + s +"块");});
public static void m(Double b,Consumer<Double> cb){
cb.accept(b);//方法执行
}
Supplier<T> 供给型接口 T get();
不接收参数,返回
第一种:自己写,自己调
Supplier<Integer> sp = ()->new Random().nextInt(200);
int nums = sp.get();
System.out.println(nums);
Supplier<Integer> sp = ()->new Random().nextInt(200);
int nums = sp.get();
System.out.println(nums);
第二种:调方法()
方法调用:int add = m(50, ()->{
int nums = 0;
for(int i = 1;i <= 50;i++){
nums += i;
}return nums;});
public static int m(int a ,Supplier<Integer> sp){
return sp.get(); //方法执行
}
方法调用:int add = m(50, ()->{
int nums = 0;
for(int i = 1;i <= 50;i++){
nums += i;
}return nums;});
public static int m(int a ,Supplier<Integer> sp){
return sp.get(); //方法执行
}
Function <T,R>函数型接口 R apply(T t);
接收一个参数,返回
第一种:自己写,自己调:
Function<String,String> fc = (e)->e.toLowerCase();
String message = fc.apply("HELLO");
Function<String,String> fc = (e)->e.toLowerCase();
String message = fc.apply("HELLO");
第二种:调方法;
调用方法:String message = m("hello", (e)->e.toUpperCase());
public static String m(String str ,Function<String, String> fc){
return fc.apply(str);//方法执行
}
调用方法:String message = m("hello", (e)->e.toUpperCase());
public static String m(String str ,Function<String, String> fc){
return fc.apply(str);//方法执行
}
Predicate<T> 断言型接口 boolean test(T t);
接收一个参数,返回boolean类型的判断
List li = new ArrayList();
li.add("光芒");
li.add("公民");
li.add("阿杰");
li.add("阿辉");
li.add("光芒");
li.add("公民");
li.add("阿杰");
li.add("阿辉");
第一种:自己写自己调
Predicate<String> pd = (e)->e.startsWith("阿");
List<String> newli = new ArrayList<String>();
for (String str : li) {
if(pd.test(str)){
newli.add(str);
}
}
Predicate<String> pd = (e)->e.startsWith("阿");
List<String> newli = new ArrayList<String>();
for (String str : li) {
if(pd.test(str)){
newli.add(str);
}
}
第二种:传一个集合,调方法
调用方法:List<String> f = m(li, (e)-> e.startsWith("阿"));
public static List<String> m(List<String> li,Predicate<String> pc){
List<String> newLi = new ArrayList<String>();
for (String string : li) {
if(pc.test(string)){//调用Predicate接口中的方法,返回内容作为条件判断
newLi.add(string);
} }
return newLi;}
调用方法:List<String> f = m(li, (e)-> e.startsWith("阿"));
public static List<String> m(List<String> li,Predicate<String> pc){
List<String> newLi = new ArrayList<String>();
for (String string : li) {
if(pc.test(string)){//调用Predicate接口中的方法,返回内容作为条件判断
newLi.add(string);
} }
return newLi;}
方法引用
方法引用是Lambda表达式的一种简写形式,如果Lambda表达式方法体中只是调用一个特定的已存在的方法,
则可以使用方法引用
则可以使用方法引用
使用::操作符将对象或类的方法名的名字分割开来
对象::实例方法
Consumer c = (e)->System.out.println(e);
c.accept("nihao");//Lambda表达式
Consumer c2 = System.out::println;
c2.accept("你好啊");//方法引用
c.accept("nihao");//Lambda表达式
Consumer c2 = System.out::println;
c2.accept("你好啊");//方法引用
Employee emp = new Employee("阿杰" , 21,3000);
Supplier<String> s = ()-> emp.getName();//Lambda表达式
Supplier<String> s2 = emp::getName;//方法引用
Supplier<String> s = ()-> emp.getName();//Lambda表达式
Supplier<String> s2 = emp::getName;//方法引用
类::静态方法(不常用)
int n = com.compare(1, 2);
TreeSet<Integer> ts = new TreeSet<Integer>(com2);
Lambda表达式
Comparator<Integer> com = (x,y)->Integer.compare(x, y);int n = com.compare(1, 2);
方法引用:
Comparator<Integer> com2 = Integer::compare;TreeSet<Integer> ts = new TreeSet<Integer>(com2);
类::实例方法
String str2 = ft.apply(emp);
String str3 = ft2.apply(emp);
Lambda表达式
Function<Employee, String> ft = (e)->e.getName();String str2 = ft.apply(emp);
方法引用:
Function<Employee, String> ft2 = Employee::getName;String str3 = ft2.apply(emp);
类::new
Employee emps = supp.get();
Employee emps2 = supp2.get();
Lambda表达式
Supplier<Employee> supp = ()->new Employee();Employee emps = supp.get();
方法引用:
Supplier<Employee> supp2 = Employee::new;Employee emps2 = supp2.get();
注意:调用的方法参数列表与返回值类型,要与函数型接口中的方法参数列表与返回值类型一致
Stream API
Stream是Java8中处理数组、集合的抽象概念
可以执行非常复杂的查找、过滤、映射等操作
常用方法
中间操作
System.out.println("------------filter-----------------------");过滤
li.stream().filter((e)->e.getName().startsWith("阿")).forEach(System.out::println);
li.stream().filter((e)->e.getName().startsWith("阿")).forEach(System.out::println);
System.out.println("------------limit-----------------------");截断,不超过给定数量
li.stream().limit(3).forEach(System.out::println);
li.stream().limit(3).forEach(System.out::println);
System.out.println("------------distinct-----------------------");筛选,利用hashCode和equals
li.stream().distinct().forEach(System.out::println);
li.stream().distinct().forEach(System.out::println);
System.out.println("------------map-----------------------");//将一个类型转换一个类型
li.stream().map((e)->e.getName()).forEach(System.out::println);
li.stream().map((e)->e.getAge()).forEach(System.out::println);
List<String> newLi = Arrays.asList("aaa","bbb","ccc","ddd");
//newLi.stream().map((e)->e.toUpperCase()).forEach(System.out::println);
newLi.stream().map(String::toUpperCase).forEach(System.out::println);
li.stream().map((e)->e.getName()).forEach(System.out::println);
li.stream().map((e)->e.getAge()).forEach(System.out::println);
List<String> newLi = Arrays.asList("aaa","bbb","ccc","ddd");
//newLi.stream().map((e)->e.toUpperCase()).forEach(System.out::println);
newLi.stream().map(String::toUpperCase).forEach(System.out::println);
System.out.println("------------sorted-----------------------");自然排序
newLi.stream().sorted().forEach(System.out::println);
li.stream().sorted().forEach(System.out::println);
newLi.stream().sorted().forEach(System.out::println);
li.stream().sorted().forEach(System.out::println);
要求传入的参数是实现了Comparable接口
System.out.println("--------sorted(Comparator)--------");定制排序
li.stream().sorted((x,y)->Double.compare(x.getScore(),y.getScore())).forEach(System.out::println);
li.stream().sorted((x,y)->Double.compare(x.getScore(),y.getScore())).forEach(System.out::println);
终止操作
System.out.println("---------------------count-------------------");返回流中元素的总个数
long l = li.stream().count();
long l = li.stream().count();
System.out.println("---------------------anyMatch---------------------------");是否有匹配的
boolean boo = li.stream().filter((e)->e.getName().length()>= 3).anyMatch((e)->e.getName().startsWith("阿"));
boolean boo = li.stream().filter((e)->e.getName().length()>= 3).anyMatch((e)->e.getName().startsWith("阿"));
System.out.println("---------------------allMatch---------------------------");是否全部都匹配
boolean boo2 = li.stream().filter((e)->e.getName().length()>=3).allMatch((e)->e.getName().startsWith("阿"));
boolean boo2 = li.stream().filter((e)->e.getName().length()>=3).allMatch((e)->e.getName().startsWith("阿"));
System.out.println("---------------------noneMatch---------------------------");是否没有匹配的
boolean boo3 = li.stream().filter((e)->e.getName().length()>=3).noneMatch((e)->e.getAge()>= 30);
boolean boo3 = li.stream().filter((e)->e.getName().length()>=3).noneMatch((e)->e.getAge()>= 30);
System.out.println("---------------------findFirst---------------------------");返回第一个
Optional<Employee> o = li.stream().findFirst();
System.out.println(o.get());
Optional<Employee> o = li.stream().findFirst();
System.out.println(o.get());
System.out.println("---------------------findAny---------------------------");返回任意
Optional<Employee> o2 = li.stream().filter((e)->e.getName().length()>=3).findAny();
System.out.println(o2.get());
Optional<Employee> o2 = li.stream().filter((e)->e.getName().length()>=3).findAny();
System.out.println(o2.get());
System.out.println("---------------------Max---------------------------");返回最大
Optional<Employee> o3 = li.stream().max((x,y)->x.getAge()-y.getAge());
System.out.println(o3.get());
Optional<Employee> o3 = li.stream().max((x,y)->x.getAge()-y.getAge());
System.out.println(o3.get());
System.out.println("---------------------Min---------------------------");返回最小
Optional<Employee> o4 = li.stream().min((x,y)->x.getAge()-y.getAge());
System.out.println(o4.get());
Optional<Employee> o4 = li.stream().min((x,y)->x.getAge()-y.getAge());
System.out.println(o4.get());
stream()
//串行-->一条执行路径 单线程
parallelStream()
//并行-->多条执行路径, 多线程
0 条评论
下一页