JavaSE回顾笔记Ⅱ
本笔记为JavaSE的第二部分(共三部分)。虽然本笔记属于较为详细版本,但也是个人根据个人情况调整和精简过的。更适合学完每个小结后回顾而不是当初次的学习资料,否则可能会有遗漏一些简单的知识。另外也会有少量后面的知识整合到前面的情况。持续更新中。。。。
1.打字速度,80-100字母/分钟。 最低标准,要求手法正确。
2.JavaSE为Java的基础。要求会用,暂时不必过于深究一些不懂的知识,可以等到后期再研究。
3.面试中会问一些原理和底层。但用到的并不多,不过考虑到深造,还是要了解。
4.技术更新很快,要学会如何学习,与时俱进。
5.最重要的还是能力。电脑内存至少8G.
6.英语不好的要注意积累专业相关的词汇(平时遇到的单词就行,积累下来。)
Day01【对象、继承、抽象类】
ctrl+o: 重写父类的方法
Alt+Insert 可以新建类、方法等任何东西。
Ctrl+Alt+T 将选中的代码使用if、while、try/catch等包装
ctrl+alt+M 方法抽取(将主方法的一段代码选中抽取为一个单独的方法)
ctrl+r 替换
1.1对象的内存图
1.2匿名对象
匿名对象:
创建对象时,只有创建对象的语句,没有把对象赋值给某个变量,这个对象叫匿名对象
特点:
只能使用一次,使用完毕会被JVM在空闲的时候进行垃圾回收
好处:
可以节约内存,可以提高程序的效率
作用:
一般可以作为方法的参数和返回值使用
1.3继承
继承的格式:
public class 子类 extends 父类{ }
继承的特点:
1.子类继承父类,子类就会自动拥有父类非私有的成员变量和成员方法
2.在子类中可以定义子类特有的成员
3.Java只支持单继承,不支持多继承。
4.一个类可以有多个子类。
5.可以多层继承。
6.顶层父类是Object类。所有的类默认继承Object,作为父类。
继承的好处:
1.可以提高代码的复用性(重复使用)
2.继承使类与类之间产生了关系,是多态的前提
继承的注意事项:
1.构造方法是不能继承的,构造方法是本类创建对象使用的
2.父类私有成员子类是不能继承的
继承后的特点—成员变量
1.子类有,使用子类自己的成员变量
2.子类没有,使用子类继承自父类的成员变量
3.子类和父类都没有,编译报错
继承后的特点—成员方法
1.子类有,使用子类自己的成员方法
2.子类没有,使用子类继承自父类的成员方法
3.子类和父类都没有,编译报错
继承后的特点—构造方法
1.在子类构造方法的第一行,有一个默认的super();
2.super();作用就是调用父类的空参数构造方法
3.子类继承父类,子类想要使用继承自父类的成员,就必须把父类加载到内存中,调用父类的构造方法创建父类对象
4.父类进入到内存中,子类才可以使用父类的成员
1.4方法重写
方法重写:发生在两个类之间,在子类中出现了和父类一模一样的方法,叫方法重写/覆写/覆盖(@Override)
一模一样:
方法名一样
参数列表一样
返回值类型一样
修饰符一样(子类的修饰符权限大于等于父类的修饰符)
注解:
@Override:检查方法是否为重写的方法
方法重载:发生在一个类中,在一个类中出现了方法名相同,但是参数列表不同的方法,叫方法重载(@Overload)
方法重写的注意事项:
1. 方法重写是发生在子父类之间的关系。
2. 子类方法重写父类方法,必须要保证权限大于等于父类权限。
java中四大权限修饰符
public:公共的
protected:受保护的
:默认的,不写就是默认的
private:私有的
3. 子类方法重写父类方法,返回值类型、方法名和参数列表都要和父类方法一模一样
4. 私有方法不能被重写(父类的私有方法子类是不能继承)
1.5子父类的内存图解
1.6this与super关键字
this关键字:
代表本类对象的引用==>根据类创建的对象
哪个对象调用的方法,方法中的this就是哪个对象
作用:
当局部变量和成员变量重名时,使用this关键字可以区分局部变量和成员变量
this.变量名==>成员变量
this关键字:本类对象的引用
this.成员变量:本类的成员变量
this.成员方法(参数):本类的成员方法
super关键字:父类对象的引用
super.成员变量:父类的成员变量
super.成员方法(参数):父类的成员方法
this关键字:可以调用本类其他的构造方法
格式:
this();调用空参数构造方法
this(参数);调用带参数构造方法
注意:
1.this();|this(参数);调用构造方法必须写在构造方法中的第一行,创建对象必须优先执行
2.构造方法不能相互调用(不能你调用我,我在调用你-->死循环)
super关键字:调用父类的构造方法
格式:
super();调用父类的空参数构造方法
super(参数);调用父类带参数构造方法
注意:
1.在子类的构造方法中没有写super();有一个默认的super();用于调用父类的空参数构造方法
2.super();|super(参数);必须写在子类构造方法有效代码的第一行,构造方法必须优先执行
3.在子类的构造方法中this和super不能同时出现,因为都必须写在有效代码第一行
1.7抽象类&抽象方法概述
1.抽象方法:没有方法体的方法。
2.抽象类:包含抽象方法的类。
3.抽象类体现的是模板思想。
4.抽象类中构造器的作用:让其子类通过调用构造器完成属于抽象类的初始化操作
1.8抽象类&抽象方法使用
定义父类
public abstract class 类名{}
定义成员变量
定义成员方法(可以有普通方法)
修饰符 abstract 返回值类型 方法名(参数);
注意:
1.抽象类是无法直接创建对象使用的
a.有一些类就是为了不让别人创建对象使用,可以定义为抽象类
b.抽象类中一般都包含抽象方法,抽象方法没有方法体,创建对象调用抽象方法没有意义
2.需要创建子类继承抽象父类,重写抽象类中的抽象方法,创建子类对象使用
3.抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
好处:
在抽象类中定义抽象方法,那么子类就必须重写这个抽象方法
子类继承抽象类有两种处理方式 alt+回车
1.子类也声明为抽象类(子类包含继承自父类的抽象方法)
2.子类重写抽象方法,添加方法体
Day02【final、static、接口】
2.1final关键字
1.final修饰的类:
1.是一个最终类,不能被继承;其他的使用方式不变(继承其他的类,创建对象使用...)
2.final修饰的方法:
1.是一个最终方法,可以被继承使用,但是不能被重写
3.final修饰的局部变量:是一个常量,值不能改变
1.final修饰符是局部变量唯一的修饰符
4.final修饰的成员变量:是一个常量,值不能改变
1.final修饰的成员变量必须在创建对象前赋值
2.成员变量的默认值不是final修饰的成员变量的值,必须赋一个具体值
3.赋值的方式:
a.直接赋值:定义变量,直接给变量赋值
b.使用构造方法赋值,构造方法是创建对象前执行
无论哪种赋值方式,只能赋值一次
4.常量的命名规则:一般都全部使用大写字母,单词间用_分隔,
2.2static关键字
1.static修饰成员(变量/方法)
被static修饰的成员属于类,不属于单个这个类的某个对象。
static修饰的成员被多个对象共享.
static修饰的成员属于类,但是会影响每一个对象。
被static修饰的成员又叫类成员,不叫对象的成员。
2.在下面两种情况下使用静态方法
1.一个方法不需要访问对象状态,其所需参数都是通过显式参数提供。
2.一个方法只需要访问类的静态域。
3.静态方法不使用对象,也就没有隐式参数,也就没有this,也不能访问实例域
4.静态方法调用的注意事项:
1.静态方法可以直接访问类变量和静态方法。
2.静态方法不能直接访问普通成员变量或成员方法。成员方法可以直接访问类变量或静态方法。
3.静态方法中,不能使用this关键字。
2.3static的内存图解
2.4接口概述
1.接口:是引用数据类型的一种,是功能的集合(接口中定义的都是方法)
接口中不能定义变量,可以定义常量(很少使用)
定义接口使用的也是.java文件;编译生成的也是.class文件
定义接口使用关键字interface
2.定义格式:
修饰符 interface 接口名{
抽象方法;(jdk7)重点
默认方法;(jdk8)
静态方法;(jdk8)
私有方法;(jdk9)
}
3.接口中其他成员的特点(使用)
1.接口中是无法定义成员变量的,但是可以定义常量,常量的值不能改变
2.默认使用的修饰符public static final,接口中的常量修饰符可以省略不写,默认默认也是public static final
3.常量的命名规则:所有的单词都要大写,多个单词之间使用下划线连接用于定义一些常用的常量
4.接口中,没有构造方法,不能创建对象
5.接口中,没有静态代码块
2.5接口的使用
1.定义使用含有抽象方法的接口(重点)
抽象方法:没有方法体,被abstract修饰的方法
定义格式:
public abstract 返回值类型 方法名(参数);
注意:
1.接口中的抽象方法修饰符是可以省略不写的,不写默认也是public abstract,建议写出,增强阅读性
2.接口的使用:
1.接口是不能创建对象使用
2.可以定义一个实现类,实现(继承)接口,重写接口中的抽象方法,创建实现类对象使用
-----------------------------
3.定义使用含有默认方法的接口(了解):很少自己定义,看java底层源码能看到
定义格式:
修饰符 default 返回值类型 方法名(参数){
方法体;
}
注意:
默认方法的修饰符default是不能省略
4.含有默认方法的接口的使用:定义实现类,实现接口,选择性的重写默认方法,创建实现类对象使用
重写了默认方法:使用实现类重写后的方法
没有重写默认方法:使用继承自接口中的默认方法
注意:
实现类重写接口中的默认方法,去掉default关键字
-----------------------------
5.定义使用含有静态方法的接口(了解):很少自己定义,看java底层源码能看到
定义格式:
修饰符 static 返回值类型 方法名(参数){
方法体;
}
注意:
定义静态方法不能省略static关键字
6.定义含有静态方法的接口的使用:
静态成员属于接口(类)本身,所以可以通过接口名.方法名(参数)直接使用
静态方法是不能重写的,属于类|接口本身,不能被子类|实现类重写
在是实现类定义了静态方法,属于实现类本身
2.6接口的多实现
1.接口的多实现:类可以同时实现多个接口
格式:
public class 实现类 implements 接口1,接口2,接口3....接口n{
重写所有接口中的抽象方法
}
注意:
1.接口中含有抽象方法,实现类需要重写所有接口的抽象方法
2.如果接口中有同名的抽象方法,实现类只重写一个就可以了,不会产生不确定性
3.抽象方法没有方法体
2.接口的多实现:类实现含有默认方法的多个接口
格式:
public class 实现类 implements 接口1,接口2,接口3....接口n{
}
注意:
1.如果接口中的默认方法不重复,实现类可以选择重写或者不重写默认方法
重写:使用实现类重写的
不重写:使用继承自接口的
2.如果多个接口中默认方法有重复的,实现类必须重写这个重复的默认方法
有不确定性,实现类不知道使用继承自哪个接口的默认方法,重写之后使用自己的
3.接口的多实现:实现类实现含有静态方法的多个接口
格式:
public class 实现类 implements 接口1,接口2,接口3....接口n{
}
注意:
1.实现类实现含有静态方法的接口,没有意义;静态方法实现类不能继承,也不能重写
2.接口中静态方法有重复的,不会冲突,静态方法属于每个接口本身
2.7类继承类的同时实现多个接口
一个类可以在继承父类的同时,实现多个接口
格式:
public class 子类 extends 父类 implements 接口1,接口2,....接口n{
}
注意:
1.父类|接口中有抽象方法,子类需要全部重写
2.父类中的普通方法和接口中的默认方法重复了,子类优先使用父类的
2.8接口的多继承
类与类之间:继承关系
public class 子类 extends 父类{ }
类与接口之间:实现关系
public class 实现类 implements 接口,接口...{ }
接口与接口之间:继承关系
public interface 子接口 extends 父接口1,父接口2,...{ }
注意:
子接口继承多个父接口,相当于求接口的并集
子接口包含所有父接口中的方法(抽象的,默认的)
父接口中的默认方法有重复的,子接口必须重写这个默认方法,为了区分使用的是哪个默认方法,重写之后使用自己的
2.9接口和抽象类的区别
Day03【多态、内部类】
3.1多态
多态:
父类的引用变量指向了子类的对象
格式:
父类类型 变量名 = new 子类对象();
接口类型 变量名 = new 实现类对象();
使用前提:
1.必须有子父类关系或者接口和实现类的实现关系
2.在子类|实现类中重写父类|接口中的方法,否则多态没有意义
特点:
1.多态调用的是子类重写后的方法
2.如果有多个子类,创建的是哪个子类对象,调用哪个子类重写后的方法
3.扩展性强,父类的变量可以赋值不同的子类对象,而调用每个子类重写后的方法
4.不能使用子类特有的功能
5.一般使用父类类型作为方法的参数的类型和返回值的类型
6.调用方法时,编译看左,执行看右。
3.2多态的转型
1.向上转型:多态本身就是向上转型,把子类对象赋值给父类的变量
格式:
父类类型 变量名 = new 子类对象():
2.向下转型:把父类类型的变量强制转换为子类类型(强转)
格式:
子类类型 变量名 = (子类类型)父类变量名;
好处:
强制之后,多态变量变成了子类类型,就可以使用子类特有的功能
注意:
a.向下转型前提,必须是多态
b.直接创建父类对象,不能向下转型
3.instanceof关键字:判断某个对象是否属于某种数据类型
格式:
boolean b = 对象 instanceof 数据类型;
对象属于对应的数据类型,返回true(该对象的创建时new的类及其各级父类)
对象不属于对应的数据类型,返回false
对象为null返回false
使用前提:
对象根据类创建的(对象所属的类(引用)和判断的数据类型之间必须有继承或者实现关系)
3.3内部类
内部类:定义在其他类内部的类
分类:
成员内部类:内部类定义在其他类的成员位置(类中方法外)
局部内部类:内部类定义在其他类的方法中
---------------------------------------------
成员内部类:
定义格式:
修饰符 class 外部类{
修饰符 class 成员内部类名{
内部类的成员变量
内部类的成员方法
}
}
使用格式:通过外部类来找到内部类
外部类名.内部类名 变量名 = new 外部类().new 内部类();
变量名.内部类的成员变量
变量名.内部类的成员方法();
注意:
1.在内部类中可以直接使用外部类的成员变量和成员方法
2.内部类只是定义在其他类的内部,其他的使用方式不变,也可以继承其他的类,也可以实现接口
3.内部类生成的class文件含有$
---------------------------------------------
局部内部类:
定义格式:
修饰符 class 外部类名{
修饰符 返回值类型 外部类方法名(参数){
(final)class 局部内部类名{
内部类的成员变量,
内部类的成员方法();
}
}
}
使用格式:局部内部类的使用范围就是在方法中有效
使用方式就是在方法中定义完局部内部类之后,直接创建局部内部类对象使用
注意:
1.如果希望访问所在方法的局部变量,那么这个局部变量必须是有效final,Java8开始,final可以省略,但是不能再次赋值。原因是生命周期不同。
2.局部内部类生产的class含有$(1,2,3指的是外部类中第1个,第2个...方法)
3.4匿名内部类
匿名内部类(重点):
匿名:创建内部类,没有具体的类名
内部类:是一个局部内部类(写在方法中)
作用:简化代码
把子类继承父类,重写父类的方法,创建子类对象合成一步完成
把实现类实现接口,重写接口中的方法,创建实现类对象合成一步完成
格式:
new 父类|接口(){
重写父类|接口中的方法;
};
注意:
匿名内部类生成的class文件,包含$(1,2,3指的是第几个匿名内部类)
3.5权限修饰符
public | protected | default(空的) | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用`private` ,隐藏细节。
- 构造方法使用`public` ,方便创建对象。
- 成员方法使用`public` ,方便调用方法。
--------------------------------------------------
可修饰对象:
1.外部类可以使用 default(默认的) 和 public 进行修饰
2.四种访问权限修饰符都可以修饰“成员属性”
3.四种访问权限修饰符都可以修饰构造方法
3.6代码块
1.代码块:被{ }包裹起来的代码叫代码块
1.局部代码块:写在方法中的代码块
2.构造代码块:写在成员位置(类中方法外)的代码块
3.静态代码块:写在成员位置(类中方法外)被static修饰的代码块
2.局部代码块:写在方法中的代码块
作用:修改变量的作用域,提高程序的效率
变量的作用域:在变量所在的{ }的范围内有效,出了作用域,就会被垃圾回收
3.构造代码块:写在成员位置(类中方法外)的代码块
特点:
优先于构造方法执行,每创建一次对象,都会执行一次
作用:
1.可以给成员变量赋初始化值
2.可以把每个构造方法中的共性内容提取出来,写在构造代码块中,可以提高代码复用性
4.静态代码块:写在成员位置(类中方法外)被static修饰的代码块
特点:
static修饰的成员属于类,不属于某一个对象,被所有的对象所共享
所以我们无论创建多少次对象,静态代码块只执行一次
静态优先于非静态加载到内存中,优先于构造代码块和构造方法执行
作用:
1.可以给静态的成员变量赋初始化值
2.在项目启动的时候,可以做一些初始化的设计(只执行一次 数据库)
day04【常用API壹】
4.1toString方法
1.java.lang.Object
2.类Object是类层次结构的根类.
3.每个类都使用Object作为超类。任何一个类都直接或者间接的继承了Object类
4.所有对象(包括数组)都实现这个Object类的方法。任何一个类都可以使用Object类中的方法
5.Java中只有基本类型不是对象
6.Object类中的常用方法:
String toString() 返回该对象的字符串表示。
Object类toString方法的底层源码:
public String toString() {
return getClass().getName() + "@" +Integer.toHexString(hashCode());
}
1.getClass().getName():使用反射技术获取类的全类名(包名+类名)
2."@" :字符串原样输出,分割的作用
3.hashCode():Object类中的方法,可以获取对象的哈希值,哈希值是一个系统随机给出的十进制的整数
4.Integer.toHexString(hashCode());把十进制的整数转换为十六进制 0-9 a-f
7.注意:
以后看一个类是否重写toString方法,打印名看看是否为地址值即可(默认调用toString方法)
是地址值:类没有重写toString方法
不是地址值:类重写了toString方法
4.2equals方法
Object类中的方法equals:
boolean equals(Object obj) 指示其他某个对象是否与此对象“相等”。
Object类中的equals方法的源码:
public boolean equals(Object obj) {
return (this == obj);
}
==:比较运算符
基本数据类型:比较的是值是否相等 10==20
引用数据类型(数组,集合,类Person):比较的是地址值是否相等 @1ac1==@223
2.快捷键 alt+insert 重写equals方法(可选择版本,不同版本代码不同)
@Override
public boolean equals(Object o) {
if (this == o) {//先判断地址值是否相同
return true;
}
getClass() != o.getClass() //使用反射技术判断对象的类型 相当于 obj instanceof Student
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;//向下转型
if (this.age != student.age) {
return false;
}
return name != null ? this.name.equals(student.name) : student.name == null;
}
4.3native方法
native修饰的方法:说明这个方法不是java语言编写的
调用的是其他语言编写的代码或者操作系统底层的代码,看不到具体的方法
Object:
public native int hashCode();
public final native Class<?> getClass();
4.4Objects对象的工具类
1.特点:
java.util.Objects类:操作对象的工具类,里边的方法都是静态的
Objects中的方法都是防止空指针异常的
2.注意:
工具类中的方法一般都是静态的,可以通过类名直接使用
3.Obejcts工具类中的equals方法
static boolean equals(Object a, Object b) :判断两个对象是否相等,相等返回true,不相等返回false
底层源码:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
requireNonNull方法:判断传递的对象是否为null
public static <T> T requireNonNull(T obj) {}
public static <T> T requireNonNull(T obj, String message) {}
如果传递的对象是null,方法内部就会抛出空指针异常(message可传入异常内容)
如果传递的对象不是null,则方法内部返回这个对象
4.5Date类
1.java.util.Date类:用于描述日期和时间的类,表示特定的瞬间,精确到毫秒。
2.时间原点:0毫秒的时间点
1970 年 1 月 1 日 00:00:00 英国格林威治时间(世界标准时间)
1970 年 1 月 1 日 08:00:00 中国:东八区 时间+8个小时的时差
3.java.uti.Date类
构造方法:
Date() new出来的对象为当前时间的Date值
Date(long date) 参数传递毫秒值后得到的时间的Date值
成员方法:
long getTime() 重点
返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
void setTime(long time)
设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 以后 time 毫秒的时间点。
4.6DateFormat
1.java.text.DateFormat类
public abstract class DateFormat extends Format
DateFormat是Format类的子类,DateFormat本身还是一个抽象类。
2.作用:
它以与语言无关的方式格式化并解析日期或时间。
格式化(也就是日期 -> 文本)
解析(文本-> 日期)
3.DateFormat类的成员方法:
String format(Date date) 传递指定的日期,把日期格式化为符合模式的字符串
Date parse(String source) 把符合模式的字符串解析为Date日期
-----------------------------------------------------------------
1.java.text.SimpleDateFormat类 extends DateFormat类
2.SimpleDateFormat类构造方法
SimpleDateFormat(String pattern)
用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
参数:
String pattern:传递日期和时间的模式
在模式中写y代表年
在模式中写M代表月
在模式中写d代表日
在模式中写H代表时
在模式中写m代表分
在模式中写s代表秒
在模式中写S代表毫秒
注意:
1.表示模式的字母不能改变(y,M...),字母的连接符号可以改变(-,/...)
2.表示模式的字母严格区分大小写
3.常用方法
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss");
String format(Date date); Date--->String
Date parse(String source); String--->Date
public Date parse(String source) throws ParseException
parse方法抛出了解析异常ParseException
当传递的字符串参数"2000-1-11"和构造方法中的模式"yyyy-MM-dd"不匹配的时候,就会抛出ParseException解析异常
调用了一个抛出异常的方法,有两种处理方式:
1.使用throws关键字继续把异常抛出给方法的调用者处理,最终抛出给JVM
2.使用try...catch自己手动的处理异常
4.7Calendar类
1.获取Calendar类的子类对象//
static Calendar getInstance() 使用默认时区和语言环境获得一个当前时间的日历。
2.Calendar类的常用方法
int get(int field) 获取给定日历字段的值。
void set(int field, int value) 将给定的日历字段设置为给定值。
void set(年,月,日)
void set(年,月,日,时,分)
void set(年,月,日,时,分,秒)
void add(int field, int amount) 把日历字段增加|减少指定的值
Date getTime() 把日历转换为日期对象
3.以上方法的参数(int field),让我们传递指定的日历字段,这些日历字段在Calendar类中被定义为了常量//不创建对象,直接调用的值
年:public final static int YEAR = 1;
月:public final static int MONTH = 2;
日:public final static int DATE = 5;
日:public final static int DAY_OF_MONTH = 5;
时:public final static int HOUR = 10;
分:public final static int MINUTE = 12;
秒:public final static int SECOND = 13;
毫秒:public final static int MILLISECOND = 14;
4.月份0-11对应1-12月;
星期0-6对应周一到周日;
4.8Math类
java.lang.Math类:数学工具类
Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
Math类中的方法都是静态方法,通过类名.方法名(参数)可以直接使用
Math类中私有了构造方法,不让我们直接创建对象使用 private Math() {}
成员方法:
public static int abs(int a) 获取参数a的绝对值:
public static double ceil(double a) 向上取整 数值大
public static double floor(double a) 向下取整 数值小
public static long round(double a) 四舍五入取整
public static double pow(double a, double b) 获取a的b次幂
4.9System类
java.lang.System类
System 类包含一些有用的类字段和方法。它不能被实例化(私有了构造方法)。
里边的方法都是静态的,通过类名.方法名(参数)可以直接使用
成员方法:
public static void exit(int status) 终止当前运行的 Java 虚拟机,非零表示异常终止
public static long currentTimeMillis() 返回当前时间(以毫秒为单位)
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 数组复制
Day5【常用API贰】
5.1BigInteger类
1.java.math.BigInteger 类,
不可变的任意精度的整数,不限制长度。
2.构造方法
BigInteger(String value);
3.成员方法
add(BigInteger value)
subtract(BigInteger value)
multiply(BigInteger value)
divide(BigInteger value)//除不尽的取整数部分
5.2BigDecimal类
1.java.math.BigDecimal类
用于浮点数(小数)的精确计算
以后想进行小数的精确计算,不要使用float和double,使用BigDecimal类
2.构造方法:
BigDecimal(String val) 将 BigDecimal 的字符串表示形式转换为 BigDecimal。
//可以但不要用基本类型创建,否则会损失精度
参数:
String val:传递一个字符串类型的小数
3.成员方法:
加法:BigDecimal add(BigDecimal augend)
减法:BigDecimal subtract(BigDecimal subtrahend)
乘法:BigDecimal multiply(BigDecimal multiplicand)
除法:BigDecimal divide(BigDecimal divisor) //除不尽抛出 ArithmeticException。
除法:BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
BigDecimal.ROUND_HALF_UP:四舍五入模式
5.3Arrays类
1.java.util.Arrays:操作数组的工具类
2.此类包含用来操作数组(比如排序和搜索)的各种方法。
3.Arrays类中的方法都是静态的,可以通过类名.方法名(参数)直接使用
4.Arrays类中的成员方法:
static String toString(Object[] a) 返回指定数组内容的字符串表示形式。
对数组进行遍历,把数组中的元素组合成一个字符串返回
static void sort(Object[] a)
根据元素的自然顺序对指定对象数组按升序进行排序。
static String deepToString(Object[] a)
返回指定数组的“深度内容”的字符串表示形式。(多维数组)
5.4包装类
1.包装类是为了能使基本类型数据能使用一些方法。
2.包装类中的构造方法:
Integer(int value) 传递一个整数
Integer(String s) 传递字符串类型的整数
3.包装类中的静态方法:
static Integer valueOf(int i) 传递一个整数
static Integer valueOf(String s) 传递字符串类型的整数
4.注意:
两个传递字符串的方法,必须传递整数类型的字符串,否则会抛出异常
5.装箱/拆箱:
装箱:基本类型-->包装类
拆箱:包装类-->基本类型
自动装箱和自动拆箱:在JDK1.5以后,装箱和拆箱可以自动进行
6.基本类型与字符串之间的转换 :
1.基本数据类型==>字符串
a.基本数据类型的值+"":工作中最常用
b.使用包装类Integer中的静态方法toString
static String toString(int i) 返回一个表示指定整数的 String 对象。
c.使用String类中的静态方法valueOf
static String valueOf(int i) 返回 int 参数的字符串表示形式。
2.字符串类型==>基本数据类型(非常重要)
在每个包装类中都有一个parseXXX方法,可以把字符串格式的基本类型数据的值,转换为基本数据类型
Integer类: static int parseInt(String s)
Double类: static double parseDouble(String s)
...
3.注意:
1.除了Character类之外,其他所有包装类都具有parseXxx静态方法
2.字符串必须传递基本数据类型的字符串,否则会抛出数字格式化异常,boolean类型除外,不是true都会返回flase
5.5String类
1.java.lang.String类
String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。
字符串底层就是一个数组(字节数组/字符数组JDK9),数组被final修饰,数组的地址值不能改变,所以字符串就是一个常量
private final char value[];
2.String类的构造方法:(IO流的时候用到) 扩展知识点,不作为强制掌握,了解即可
String(String original)
String(byte[] bytes) 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, int offset, int length)
String(char[] value)
String(char[] value, int offset, int count)
3. String类的常用成员方法:
1.String concat(String str) 将指定字符串连接到此字符串的结尾。
2.boolean contains(CharSequence s) 判断字符串中是否包含指定的字符串;
2.boolean contains(String str) 判断字符串中是否包含指定的字符串;
3.boolean endsWith(String suffix) 判断字符串是否以指定的字符串而结尾;
4.boolean startsWith(String prefix) 判断字符串是否以指定的字符串而开头;
5.int indexOf(String str) 从前往后在字符串中查找另外一个字符串,找到了返回字符串对应的索引,找不到返回-1
6.int lastIndexOf(String str) 从后往前在字符串中查找另外一个字符串,找到了返回字符串对应的索引,找不到返回-1
如果大的字符串有重复的被查找的字符串,找到了第一个就不在找了
7.String replace(CharSequence target, CharSequence replacement) 把字符串中所有的目标字符串,替换为新的字符串
7.String replace(String target, String replacement) 把字符串中所有的目标字符串,替换为新的字符串
8.String substring(int beginIndex) 从开始索引beginIndex开始截取字符串到字符串的末尾,截取一个新的字符串
9.String substring(int beginIndex, int endIndex) 从开始索引beginIndex到结束索引endIndex截取字符串;包含头,不包含尾
10.char[] toCharArray() 将此字符串转换为一个新的字符数组。
11.byte[] getBytes() 查询系统默认的编码表把字符串转换为字节数组
12.String toLowerCase() 把字符串中所有的英文字符转换为小写
13.String toUpperCase() 把字符串中所有的英文字符转换为大写
14.String trim() 去掉字符串两端的空格
15.String[] split(String regex) 根据指定的字符串对大的字符串进行切割,把大的字符串切割为多个小字符串,存储到一个数组中
方法的参数:
CharSequence s:是一个接口,String实现了CharSequence接口
所以只要方法的参数是CharSequence接口,就可以直接传递字符串
5.6引用类型使用小结
1.类名作为方法参数和返回值
2.抽象类作为方法参数和返回值
- 抽象类作为形参:表示可以接收任何此抽象类的"子类对象"作为实参;
- 抽象类作为返回值:表示"此方法可以返回此抽象类的任何子类对象";
3.接口作为方法参数和返回值(同抽象类)
4.类名作为成员变量
5.抽象类作为成员变量
-为此成员变量赋值时,可以是任何它的子类对象
6.接口作为成员变量(同抽象类)
7.总结:
抽象类|接口作为方法参数,返回值类型,成员变量==>扩展性强,可以传递任意的子类|实现类对象
Day06【综合案例】
6.1注意事项
创建工具类
注意:
工具类中变量和方法都是静态的==>方便使用
私有空参数构造方法:不让外界创建对象调用方法
引用类型作为参数:
Scanner sc:传递过来定义的Scanner对象,可以直接使用,不用再创建了
Day07【Collection、Iterator、泛型、数据结构】
7.1Collection集合
1.集合和数组的区别
数组:
1.是引用数据类型的一种
2.可以存储多个元素
3.数组的长度是固定的
4.数组即可以存储基本数据类型的数据,又可以存储引用数据类型的数据
集合:
1.是引用数据类型的一种
2.可以存储多个元素
3.集合的长度是可以变化的(添加元素,删除集合中的元素)
4.集合只能存储引用数据类型的数据
2.集合的嵌套:
集合中的元素仍然是一个集合
任何集合之间都可以相互嵌套
7.2集合常用类的继承体系
7.3Collection常用功能
1.java.util.Collection<E>
Collection 层次结构 中的根接口。
Collection 表示一组对象,这些对象也称为 collection 的元素。
一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。
2.Collection接口中定义了所有单列集合共性的成员方法,所有的实现类都可以使用
public boolean add(E e) : 往集合中添加元素
public boolean remove(E e) : 移除集合中指定的元素(只会移除第一个)
public boolean contains(Object obj) : 判断当前集合中是否包指定的元素。
public boolean isEmpty() : 判断当前集合是否为空。
public int size() : 返回集合中元素的个数。获取集合的长度
public Object[] toArray() : 把集合中的元素,存储到数组中
public <T> T[] toArray(T[] a) 返回一个包含此集合中所有元素的指定类型数组。
public void clear() :清空集合中所有的元素。
7.4Iterator迭代器
1.迭代器:是一种通用取出集合中元素的方式
2.java.util.Iterator<E>接口:对 collection 进行迭代的迭代器。
Iterator接口的常用方法:
boolean hasNext() 如果仍有元素可以迭代,则返回 true
E next() 返回迭代的下一个元素。 取出集合中的元素
3.Iterator<E> iterator() 返回在此 collection 的元素上进行迭代的迭代器。
Iterator<String> it = arrayList.iterator();
4.原理:
hasNext()判断指针的下一个位置是否有元素;
next()取出指针的下一个元素,并将指针向后移动一位
5.迭代的并发修改异常:
在使用迭代器遍历集合过程中,对集合长度进行了修改,迭代器就会抛出并发修改异常ConcurrentModificationException
解决方案:
1.遍历集合的同时,不修改集合的长度
2.Iterator接口中的remove()方法;
3..Iterator接口有一个子接口叫ListIterator
在ListIterator接口定义了往集合中添加元素的方法
public interface ListIterator<E>extends Iterator<E>
void add(E e) 迭代器中往集合添加元素的方法
void remove() 删除的是next方法取出的元素
注意:
ListIterator迭代器只能遍历List接口下的集合,不能遍历Set接口下的集合
6.迭代器的实现类是每个集合的成员内部类
7.5增强for循环
1.增强for循环:
是JDK1.5之后出现的新特性
使用for循环的方式,对迭代器进行了简化
增强for循环内部就是一个迭代器,对迭代器进行了封装
2.Collection接口有一个父接口叫Iterable
public interface Collection<E> extends Iterable<E>
java.lang.Iterable<T>接口
实现这个接口允许对象成为 "foreach" 语句的目标。
Collection接口继承了Iterable接口,所以可以使用增强for循环
Collection接口所有的实现类,都可以使用增强for循环
3.增强for循环的格式:
for(集合|数组中元素的类型 变量名 : 集合名|数组名){
sout(变量名);
}
7.6泛型的介绍
1.概念:
泛型是一个未知的数据类型,当我们不知道使用什么类型时,就可以使用泛型。
泛型也可以看成是一个变量,用来接收数据类型
2.java中的泛型:是一个伪泛型,在.java文件中有,但是.class文件中没有
3.使用泛型创建ArrayList集合对象(有<>)
好处:
1.使用什么泛型就只能存储什么类型的数据;避免向下转型抛出类型转换异常
2.写上泛型存储的是什么类型,取出的就是什么类型,不用向下转型,就可以使用特有的方法
弊端:
1.不能什么类型的数据都存储
4.不使用泛型创建ArrayList集合对象(没有<>)
好处:
不使用泛型,集合默认的数据类型就是Object类型,可以存储任意数据类型的元素
弊端:
1.不能使用元素特有的方法(多态)
2.在进行向下转型的时候,容易引发类型转换异常
5.注意:
1.泛型不存在继承关系 Collection<Object> list = new ArrayList<String>();这种是错误的。
7.7泛型的使用
1.定义和使用含有泛型的方法
定义格式:
修饰符 <泛型> 返回值类型 方法名(参数类型-->使用泛型){
方法体;
}
什么时候确定泛型的数据类型:
调用方法,传递的参数是什么类型,方法的泛型就是什么类型
--------------------------------------------------------
2.定义和使用含有泛型的接口
定义格式:
public interface GenericInterface<I> {
//定义抽象方法,使用接口上的泛型,作为参数的类型
public abstract void show(I i);
}
什么时候确定泛型的数据类型:
1.定义一个类,实现含有泛型的接口,在实现接口的同时,指定接口泛型的数据类型
格式:
public class GenericInterfaceImpl implements GenericInterface<String>{
重写接口中的方法,使用指定的类型String
public void show(String s) { }
}
2. 定义类实现含有泛型的接口,接口使用什么泛型,实现类就使用什么泛型,实现类跟着接口走,就和定义一个含有泛型的类是一样的
格式:
public class GenericInterfaceImpl2<I> implements GenericInterface<I>{
重写的方法使用接口上的泛型
public void show(I i) { }
}
注意:
创建对象的时候,确定泛型的数据类型;创建对象是什么数据类型,泛型就是什么数据类型
--------------------------------------------------------
3.定义和使用含有泛型的类
定义格式:
修饰符 class 类名<代表泛型的变量> {}
什么时候确定泛型的数据类型:
在创建对象的时候确定泛型
--------------------------------------------------------
3.泛型的通配符
1.?:代表可以接收任意的数据类型
2.?已经由java定义好了,我们可以直接使用
3.泛型的通配符只能作为方法参数的数据类型使用,不能创建对象作为数据类型使用
4.泛型通配符的高级使用
泛型的通配符: ? 代表可以接收任意数据类型的数据
泛型的上限限定: ? extends E==>传递的未知类型?只能使用E的子类或者是E本身
泛型的下限限定: ? super E==>传递的未知类型?只能使用E的父类或者是E本身
5.区别:
如果希望只取出,不插入,就使用? extends Hero
如果希望只插入,不取出,就使用? super Hero
如果希望,又能插入,又能取出,就不要用通配符?
4.受限泛型
泛型的上限:
格式: 类型名称 <? extends 类 >
对象名称意义: 只能接收该类型及其子类
泛型的下限:
格式: 类型名称 <? super 类 >
对象名称意义: 只能接收该类型及其父类型
7.8数据结构
1.栈
stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
先进后出
2.队列
queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行取出并删除。
先进先出
3.数组
Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。
查询快,增删慢
4.链表
linked list,由一系列结点node组成,结点可以在运行时动态生成。
查询慢,增删快
单向链表 不能保证顺序(HashSet)
双向链表 能保证顺序(LinkedList,LinkedHashSet)
5.树
1.树的基本结构
节点的度:节点拥有的子树的个数
叶子节点:度为0的节点,也称之为终端结点
高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高
层:根节点在第一层,以此类推
2.二叉查找树的特点:
左子树上所有的节点的值均小于等于他的根节点的值
右子树上所有的节点值均大于或者等于他的根节点的值
每一个子节点最多有两个子树
3.平衡二叉树
它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
4.旋转
左旋:左旋就是将节点的右支往左拉,右子节点变成父节点,并把晋升之后多余的左子节点出让给降级节点的右子节点;
右旋:将节点的左支往右拉,左子节点变成了父节点,并把晋升之后多余的右子节点出让给降级节点的左子节点
左左:对节点进行一次右旋即可调整平衡,
左右:将左右进行第一次左旋,将左右先调整成左左,然后再对左左进行调整,从而使得二叉树平衡。
右左:将左右进行第一次右旋,将左右先调整成右右,然后再对右右进行调整,从而使得二叉树平衡。
右右:对节点进行一次左旋即可调整平衡,
5.红黑树:
红黑树是一种自平衡的二叉查找树,不是高度平衡的,它的平衡是通过"红黑树的特性"进行实现的;
红黑树的特性:
1.每一个节点或是红色的,或者是黑色的。
2.根节点必须是黑色
3.每个叶节点(Nil)是黑色的;(如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点)
4.如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
5.对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
6.在进行元素插入的时候,和之前一样; 每一次插入完毕以后,使用黑色规则进行校验,如果不满足红黑规则,就需要通过变色,左旋和右旋来调整树,使其满足红黑规则;
Day08【List、Collection、Set】
8.1List接口
java.uil.List<E>接口 extends Collection<E>接口
List接口的特点:
1.是一个有序的集合:存储元素和取出元素的顺序是一致的
2.允许存储重复的元素
3.包含一些带索引的特有方法
void add(int index, E element) 在指定索引处,添加一个新的元素
E get(int index) 获取指定索引处的元素
E remove(int index) 移除并返回指定索引处的元素,返回的就是被移除的元素
E set(int index, E element) 替换并返回指定索引处的元素,返回的是被替换的元素
<T> T[] toArray(T[] a) 如果a的长度小于List将新建一个数组将List中的元素转化到数组中,并返回。否则,直接赋值给a
8.2实现了List的集合
1.ArrayList集合(数组)
java.util.ArrayList<E>集合 implements List<E>接口
List 接口的大小可变数组的实现。
ArrayLis集合底层采用的就是数组结构:查询快,增删慢
add方法底层:创建一个新的数组,长度是源数组长度+1,把源数组中的元素使用System类中的arraycopy方法复制到新的数组中
工作中:查询多的时候使用,不对集合的长度进行修改(添加,删除)
2.LinkedList集合(双向链表)
java.util.LinkedList<E> implements List<E>接口
List 接口的链接列表实现。
LinkedList集合底层是一个双向链表:查询慢,增删快
双向:是一个有序的集合
LinkedList集合中含有一些操作链表首尾元素的方法:
public void addFirst(E e) :将指定元素插入此列表的开头。
public void push(E e) :将元素推入此列表所表示的堆栈。
public void addLast(E e) :将指定元素添加到此列表的结尾。
public E getFirst() :返回此列表的第一个元素。
public E getLast() :返回此列表的最后一个元素。
public boolean isEmpty() :如果列表不包含元素,则返回true
public E removeFirst() :移除并返回此列表的第一个元素。
public E pop() :从此列表所表示的堆栈处弹出一个元素。
public E removeLast() :移除并返回此列表的最后一个元素。
3.Vector集合(面试)
java.util.Vector<E> implements List<E>(jdk1.2之后)
Vector是JDK1.0时期存在的单列集合,Collection下边的其他集合(ArrayList,LinkedList...)是JDK1.2之后出现的
Vector 类可以实现可增长的对象数组。Vector集合底层和ArrayList集合是一样的,也是一个数组结构(查询快,增删慢)
Vector集合1.0时期有一些特有的方法:
void addElement(E obj) 往集合中添加元素
Enumeration<E> elements() 返回此向量的组件的枚举。
Enumeration<E>接口:向量枚举,是1.0时期的迭代器
boolean hasMoreElements() 判断集合中还有没有元素==>hasNext
E nextElement() 取出集合的元素==>next
与新 collection 实现不同,Vector 是同步的。
同步技术:可以保证多线程的安全
同步技术:集合存储数据效率低
所以Vector集合被效率更高的ArrayList集合取代了
8.3Collections类
1.java.util.Collections:操作集合的工具类,里边的方法都是静态的
通过类名.方法名(参数)可以直接使用
常用的方法:
static void sort(List<T> list) 根据元素的自然顺序 对指定集合按升序进行排序。
static <T> void sort(List<T> list, Comparator<? super T> c) 根据指定比较器产生的顺序对指定列表进行排序。
static void shuffle(List<?> list) 随机打乱集合中元素的顺序
注意:
以上3个方法,参数都是List,只能传递List接口下的实现类对象(ArrayList,LinkedList),不能使用Set接口下的集合
static int binarySearch(int[] a, int key) 如果它包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
2.Comparator比较器
Comparator<? super T> c:对集合进行排序的比较器
java.util.Comparator<T>接口
强行对某个对象 collection 进行整体排序 的比较函数。
Comparator接口中抽象方法:
int compare(T o1, T o2) 比较用来排序的两个参数。
参数:
T o1, T o2:依次比较的集合中的元素
比较的规则:
升序: o1-o2
降序: o2-o1
例子:(匿名内部类的方式)
Collections.sort(list01, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//降序: o2-o1
return o2-o1;
}
});
3.addAll方法
static <T> boolean addAll(Collection<T> c, T... elements) 将所有指定元素添加到指定 collection 中。
8.4可变参数
可变参数
是JDK1.5之后出现的新特性
作用:
当我们定义一个方法的时候,方法的参数类型已经确定了,但是参数的个数不确定,就可以使用可变参数
格式:
修饰符 返回值类型 方法名(数据类型...变量名){
方法体;
}
数据类型...变量名:叫可变参数,代表形式参数可以接收任意个数据
调用参数是可变参数的方法,参数的个数可以传递任意个(不传递,1,2,3,4,5,6....n...)
原理:
可变参数底层就是一个数组,传递不同个数个参数,就会创建不同长度的数组,来接收这些参数
注意:
1.一个方法的参数只能写一个可变参数
2.方法的参数列表有多个参数,可变参数必须写在末尾
8.5Set接口
java.util.Set<E>接口 extends Collection<E>接口
Set接口的特点:
1.不允许存储重复的元素
2.不包含带索引的方法,里边的方法和Collection接口是一样
8.6HashSet集合
1.java.util.HashSet<E>集合 implements Set<E>接口
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。
它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
HashSet集合的特点:
1.不允许存储重复的元素
2.不包含带索引的方法(不能使用普通的for循环遍历Set集合)
3.是一个无序的集合(存储的元素和取出的元素顺序有可能不一致)
4.底层是一个哈希表
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表|数组+红黑树(可以提高查询的效率)
5.可以向哈希表中添加null,默认保存在哈希表中数组的0下标中;
2.使用HashSet集合存储String不重复的原理
1.add方法会调用元素的hashCode方法和equals方法
2.先比较hashCode是否相同
3.如果相同再判断equals方法返回值是否为true;
3.HashSet存储自定义类型元素(重点中的重点)
1.重写hashCode方法
计算哈希值,将对象内的元素的哈希值相加,并尽量降低重复概率(*31)
2.重写equals方法
8.7哈希值
1.哈希值:
是一个十进制的整数,由操作系统随机给出,我们打印对象的地址值,使用的就是哈希值(逻辑地址)
对象在内存中实际存储的地址(物理地址),并不是哈希值的地址
2.Object类获取对象哈希值的方法:
int hashCode() 返回该对象的哈希码值。
hashCode方法的底层源码:
public native int hashCode();
native:调用的是操作系统底层的方法,不是由java语言编写的
3.String类的哈希值
String类重写了Object类的hashCode方法
规则:
相同的字符串返回的哈希值是一样的
不同的字符串,计算出的哈希值也有可能是一样
3.注意:
Equals与hashCode的定义必须一致。即前者返回true,则两个对象的哈希值就必须相等
8.8LinkedHashSet集合
1.java.util.LinkedHashSet<E> extends HashSet<E>
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。
2.LinkedHashSet特点:
1.不允许存储重复元素
2.没有带索引的方法
3.底层是哈希表+单向链表
JDK1.8之前:数组+单向链表+单向链表
JDK1.8之后:数组+单向链表+单向链表|数组+红黑树+单向链表(可以提高查询的效率)
结构就是一个双向链表,可以保证迭代的顺序,是一个有序的集合
8.9TreeSet集合
java.util.TreeSet<E>集合 implements Set<E>接口
基于Set接口的红黑树的实现
使用元素的自然顺序对元素进行排序(内部会使用Comparator比较器对元素进行默认的升序排序)
或者根据创建TreeSet集合时提供的 Comparator 进行排序,具体取决于使用的构造方法。
构造方法:
TreeSet() 构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
TreeSet集合特点:
1.不允许存储重复元素
2.没有带索引的方法
3.底层是一个红黑树结构(元素是有序的)
4.可以根据比较器产生的规则对元素进行排序
Day09【Map、排序、二分查找】
9.1Map集合的概述
1.Collection中的集合称为单列集合, Map中的集合称为双列集合。
2.java.util.Map<k,v>:接口 key:键 value:值
特点:
1.Map集合是一个双列集合,每个元素包含两个值,一个key一个value
2.Map集合中的key是不允许重复的,value可以重复
3.Map集合中一个key只能对应一个value
4.Map集合中key和value数据类型可以是相同的,也可以是不同
9.2Map集合常用的子类
1.HashMap
java.util.HashMap<K,V>集合 implements Map<K,V>接口
基于哈希表的 Map 接口的实现。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
a.HashMap集合底层是一个哈希表结构和HashSet是一样
b.是一个无序集合
--------------------------------------------------
2.LinkedHashMap
java.util.LinkedHashMap<K,V>集合 extends HashMap<K,V>集合
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。
a.LinkedHashMap集合底层是哈希表+单向链表和LinkedHashSet是一样
b.是一个有序的集合
--------------------------------------------------
3.TreeMap
java.util.TreeMap<K,V>集合 implements Map<K,V>接口
基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
a.TreeMap底层是一个红黑树结构和TreeSet是一样
b.TreeMap集合自带了一个比较器,里边存储的key是有序的:默认是升序或者可以根据比较器自定义排序规
构造方法:
TreeMap() 使用键的自然顺序构造一个新的、空的树映射。
TreeMap(Comparator<? super K> comparator) 构造一个新的、空的树映射,该映射根据给定比较器进行排序。
--------------------------------------------------
4.Hashtable集合(了解-->面试)
java.util.Hashtable<K,V>集合 implements Map<K,V>接口
此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
HashMap集合的特点:
1.HashMap集合底层是一个哈希表
2.HashMap集合是jdk1.2版本之后出现的
3.HashMap集合运行存储null值和null键
4.HashMap集合是不同步的,效率高(多线程不安全)
Hashtable集合的特点:
1.Hashtable集合底层是一个哈希表
2.Hashtable集合是jdk1.0时期出现的双列集合(最早期)
3.Hashtable集合不允许存储null值和null键
4.Hashtable集合是同步的,效率低(多线程安全)
-----------------------------------------------------
Hashtable集合效率没有HashMap高,所以已经被淘汰了
但是Hashtable集合的子类Properties集合依然活跃在历史的舞台
9.3Map的常用方法
V put(K key, V value) 把指定的键与值添加到Map集合中
返回值:V(值)
添加元素的时候,key不重复,返回值V就是null
添加元素的时候,key重复,会使用新的value,替换之前的value,返回的值V就是被替换的value
V remove(Object key) 根据指定的键,删除键值对,返回被删除的值
V get(Object key) 根据key获取value值
boolean containsKey(Object key) 判断集合中是否包含指定的key
default V getOrDefault(Object key, V defaultValue)(JDK1.8版本之后)
返回指定键映射到的值,如果此映射不包含该键的映射,则返回 defaultValue 。
参数:
Object key:map集合中的key
V defaultValue:给对应key设置一个默认的value值
返回值:
V:如果集合中key存在,根据key获取value值返回
如果集合中key不存在,返回设置的默认值
JDK9之后List Set Map 接口特有的批量添加元素的静态方法of();
static <E> List<E> of(E... elements) 返回包含任意数量元素的不可变列表。
9.4Map集合的遍历
1.Map集合的遍历_键找值方式
使用到的方法:
Set<K> keySet() 把所有的key取出来,存储到一个Set集合中
V get(Object key) 根据key获取value值
实现步骤:
1.使用Map集合中的方法keySet,把所有的key取出来,存储到一个Set集合中
2.遍历Set集合,获取Map集合中的每一个key
3.使用Map集合中的方法get,根据key获取value值
2.Map集合的遍历_键值对方式
1.在Map接口中,有一个内部接口叫Entry:当我们创建Map集合对象的时候,也会创建出Entry对象,会自动记录键与值的映射关系(记录每一对键值对)
2.使用Map集合中的方法:
//成员内部类:外部类名.内部类名 Map.Entry
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
3.Entry接口中的方法:
K getKey() 返回与此项对应的键。
V getValue() 返回与此项对应的值。
4.实现步骤:
1.使用Map集合中的方法entrySet,获取Map集合中所有的Entry对象,存储到一个Set集合中
2.遍历Set集合,获取每一个Entry对象
3.使用Entry对象中的方法getKey和getValue获取键与值
9.5排序
1.冒泡排序:使用数组中的每个元素和相邻的元素依次比较,每次选出最大的元素,把大的元素往后放
2.选择排序:使用数组中的元素依次和其他的元素进行比较,每次选出最小的元素,把小的元素往前放
3.效率相同,自行选择。
9.6二分查找
1.前提:
数组|集合中的元素必须是有序的(升序)
2.原理
1.定义三个指针,min,mid,max
2.mid=(min+max)/2
3.mid和所查元素比较大小
4.min=mid+1或max=mid-1或返回mid
5.循环条件为min<=max
Day10【异常】
10.1异常的概念
异常:
指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理(停止java虚拟机JVM)。
用法:
在工作中一般用于对传递到方法中参数进行一些合法性判断
10.2异常的体系
异常的体系:
1.java.lang.Throwable:异常和错误的最顶层的服务器
Throwable 类是 Java 语言中所有错误或异常的超类。
2.Exception:
java.lang.Exception extends Throwable:编译期异常。
java.lang.RuntimeException extends Exception:运行期异常
3.Error:
java.lang.Error extends Throwable :错误
10.3异常的产生过程解析
10.4throw和throws关键字
1.throw的作用:
可以在方法中抛出指定的异常对象
2.throws的作用:
关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
3.格式:
修饰符 返回值类型 方法名(参数列表) throws xxxException,...,yyyException{
throw new xxxException("异常的信息");
...
throw new yyyException("异常的信息");
}
4.注意:
1.throw关键字必须写在方法中使用,必须写在方法声明处
2.throw关键字后边创建的异常对象,必须使用Exception或者RuntimeException的子类对象.
3.在方法中使用throw关键字抛出了编译异常,我们就必须处理这个编译异常
在方法中使用throw关键字抛出了运行期异常,我们可以不用处理这个异常,最终交给JVM处理(中断)
4.一般在方法内部抛出了什么异常对象,就使用throws关键字在方法上声明抛出什么异常对象
a.在方法内部抛出了多个异常对象,就需要使用throws关键字在方法上声明多个异常对象
b.在方法内部抛出的多个异常对象,如果有子父类关系,在方法上声明父类异常即可
5.调用一个使用throws关键字声明异常的方法,那么我们就必须处理这个异常对象
a.可以使用throws关键字继续声明抛出这个异常对象,最终会抛出给JVM处理
b.可以使用try...catch自己手动的处理异常
6.可以抛出Throwable,并且在调用的时候必须进行catch处理
10.5try…catch关键字
1.作用:
方法内部抛出了指定的异常对象,我们就需要处理这个异常对象
调用的方法上声明抛出了异常对象,我们需要处理这个异常对象
可以使用try...catch关键字捕获处理异常,自己定义异常的处理方式
2.格式:
try{
可能产生异常的代码
}catch(XXXException e){
异常的处理逻辑
}
注意:
1.try中可能产生什么异常对象,catch中就要定义什么异常变量来接收这个异常对象
2.如果try中产生了异常对象,就会执行catch中异常的处理逻辑,执行完catch中的代码,继续执行try...catch后边的代码
3.如果try中没有产生异常对象,就不会执行catch中异常的处理逻辑,正常执行完try中的代码,继续执行try...catch后边的代码
4.如果try中产生了多个异常对象,就需要定义多个catch来捕获处理这些异常对象
3.多异常捕捉:
除了用多个catch分别捕捉不同异常,也可以放在一个catch里统一捕捉,异常类型用|分隔,变量只有一个,缺点是无法分清具体是哪个异常,但可以用instanceof关键字判断区分。
10.6finally关键字
1.Throwable类中定义的异常处理逻辑
Throwable类中定义的方法:
String getMessage() 返回此 throwable 的简短描述。
String toString() 返回此 throwable 的详细消息字符串。重写Object类的toStirng方法
void printStackTrace() JVM把异常信息打印在控制台,默认调用就是此方法
Exception类继承了Throwable,所以Exception所有的子类都可以使用这个三个方法
2.finally关键字(重点)
作用:
finally关键字里边定义的代码,无论程序是否有异常,都会执行
工作中一般用于资源释放(IO)
格式:
try{
throw new Exception("提示内容");
}catch(Exception e){
System.out.println(e.getMessage());//"提示内容"
System.out.println(e.toString());//java.lang.Exception:"提示内容"
e.printStackTrace();/*java.lang.Exception:"提示内容"
at com.包名.类名.方法名(文件名.java:行号)*/
}
}finally{
无论是否异常,都会执行的代码
}
注意:
1.finally关键字不能单独使用,必须和try一起使用
2.如果try中出现了异常,会先执行catch中异常的处理逻辑,再执行finally中定义的代码,执行完毕,继续执行try...catch...finally之后的代码
3.如果try中没有出现异常,执行完try中的代码,再执行finally中定义的代码,执行完毕,继续执行try...catch...finally之后的代码
10.7注意事项
1.运行时(期)异常被抛出可以不处理。
即不捕获(不使用try...catch来捕获处理异常)
也不声明抛出(不使用throws关键声明抛出异常)。
运行时异常,处理没有意义,处理了也是为了执行后续代码
2.子父类异常的处理
a.如果父类的方法抛出了多个异常,
子类重写父类方法时,可以抛出相同的异常
子类重写父类方法时,可以抛出父类异常的子类
子类重写父类方法时,可以不抛出异常
b.父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
c.父类异常什么样,子类重写父类方法就和父类一样就可以了,无需考虑异常问题
3.多个异常使用捕获处理呢
1. 多个异常分别处理。
每一个异常都会执行
2. 多个异常一次捕获,多次处理。
第一个异常出现后,就不再执行try...catch中后面的语句
catch中定义的异常变量不能是下边定义异常变量的父类
3. 多个异常一次捕获一次处理。
无论try中有多少种异常产生,只能在catch中写一种异常的处理逻辑
10.8自定义异常
自定义异常:
1.一般都是以Exception结尾的,或者这个类是一个异常相关的类(见名知意)
2.可以继承Exception:自定义异常就是一个编译期异常
3.可以继承RuntimeException:自定义异常就是一个运行时异常
格式:
public class xxxException extends Exception|RuntimeException{
//定义一个空参数构造方法
public xxxException(){
super(); //调用父类的空参数构造方法
}
//定义一个带异常信息的构造方法
public xxxException(String message){
super(message); //调用父类带异常信息的构造方法
}
}
Day11【多线程】
11.1并发、并行、进程、线程
1.并行:
指两个或多个事件在同一时刻发生(同时执行);
2.并发:
指两个或多个事件在同一时间段内发生(交替进行)
3.进程:
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
进程也是程序的一次执行过程,是系统运行程序的基本单位;
系统运行一个程序即是一个进程从创建、运行到消亡的过程。
有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
4.线程:是进程的一部分
堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
1.一个进程中只有一个线程,叫单线程程序
2.一个进程中有多个线程,叫多线程程序
3.线程的调度:
分时调度:所有的线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间
抢占式调度:优先让优先级高的线程使用cpu,如果线程的优先级相同的,那么会随机选择一个线程执行(线程的随机性),java使用的就是抢占式调度
4.主线程:
执行主方法的线程,叫主线程
main方法是程序执行的入口,JVM从main方法开始执行,自上到下逐行执行代码
5.Java 程序的进程里面至少包含两个线程,main方法线程和垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
6.由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。
11.2Thread类
1.java.lang.Thread类:是一个描述线程的类
构造方法:
public Thread():分配一个新的线程对象。
public Thread(String name):分配一个指定名字的新的线程对象。
public Thread(Runnable target):分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定
常用方法:
public String getName():获取当前线程名称。
public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run():此线程要执行的任务在此处定义代码。
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停
public static Thread currentThread():返回对当前正在执行的线程对象的引用。
1.创建多线程的第一种方法:
步骤:
1.创建一个类继承Thread类
2.在Thread类的子类中,重写Thread类中run方法,设置线程任务
3.创建Thread类的子类对象
4.调用继承自Thread类中的start方法,开启新的线程执行run方法
5.调用start方法执行,Java 虚拟机调用该线程的 run 方法。结果是两个线程并发地运行;
注意:
1.多次启动一个线程是非法的(一个线程对象只能调用一次start方法)。特别是当线程已经结束执行后,不能再重新启动。
2.多线程会导致程序运行顺序随机
3.执行start()方法时,就会开辟新的栈内存运行run方法
2.获取线程名称的方法:
1.使用Thread类中的方法getName
2.先获取到当前正在执行的线程Thread,再使用Thread类中的方法getName获取线程名称
11.3Runnable接口
1.创建多线程的第二种方法:
java.lang.Runnable接口:
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
Thread类就是实现了Runnable接口的类。
----------------------------------------------
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("这是一个方法");
}
}).start();
11.4两种方式的特点
1.使用实现Runnable接口的方式创建多线程程序,可以避免单继承的局限性
a.类继承了Thread,就不能继承别的类了
b.类实现了Runnable接口,还可以继承其他的类
2.使用实现Runnable接口的方式创建多线程程序,把设置线程任务和开启线程进行了解耦(解除了耦合性,增强了扩展性)
a.类继承了Thread,在run方法中设置什么任务,创建子类对象就执行什么任务(耦合性强)
b.类实现Runnable接口的目的:重写run方法设置线程任务
创建Thread类对象目的:传递不同的Runnable接口的实现类(传递不同的任务),执行不同的任务
3.匿名内部类的方式实现多线程程序
11.5Java内存模型JMM
1.JMM的概念
JMM(Java Memory Model)Java内存模型,是java虚拟机规范中所定义的一种内存模型。描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
2.访问规则
所有的共享变量都存储于主内存。这里所说的变量指的是实例变量(成员变量)和类变量(静态成员变量)。不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。
每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。线程对变量的所有的操作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接访问
对工作内存中的变量,线程间变量的值的传递需要通过主内存完成。
11.6高并发及线程安全
1.高并发:
在某个时间点上,有多个线程同时访问某一个资源。
2.线程安全性问题:
1.当多个线程无序的访问同一个资源(例如:同一个变量、同一数据库、同一个文件……),而且访问同一资源的代码不具有“原子性”,这时对这一资源的方法就会产生安全性问题——导致此资源最终的结果是错误。
2.主要表现(安全问题会破坏的特性):
1).可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
产生的原因:定义一个变量。一个线程修改,另一个线程由于访问频率太快,导致一直使用本线程区内的变量副本,而没有实时的到主内存中获取变量的新值。
2).有序性:即程序执行的顺序按照代码的先后顺序执行。
多行代码的编写(.java)顺序和编译(.class)顺序。有些时候,编译器在编译代码时,为了提高效率,会对代码“重排”
3).原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行
11.7volatile关键字
只能修饰"成员变量"
1.解决可见性
变量a被volatile关键字修饰,当我们改变变量a的值,volatile关键字会让所有a的变量副本立即失效,每个线程的工作内存中想要使用变量a的值,需要在静态区(主内存中)重新获取。
2.解决有序性
变量添加了volatile关键字,就不会在进行重排了
3.不能解决原子性
11.8原子类(乐观锁)
1.概述:
java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。
1). java.util.concurrent.atomic.AtomicInteger:对int变量进行原子操作的类。
2). java.util.concurrent.atomic.AtomicLong:对long变量进行原子操作的类。
3). java.util.concurrent.atomic.AtomicBoolean:对boolean变量进行原子操作的类。
2.作用:
这些类可以保证对“某种类型的变量”原子操作,多线程、高并发的环境下,就可以保证对变量访问的有序性,从而保证最终的结果是正确的。
3.AtomicInteger
1.构造方法:
public AtomicInteger():初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer
2.成员方法:
int get(): 获取值
int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值。 i++
int incrementAndGet():以原子方式将当前值加1,注意,这里返回的是自增后的值。 ++i
int addAndGet(int data):以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
int getAndSet(int value):以原子方式设置为newValue的值,并返回旧值。
4.CAS机制(乐观锁)
5.AtomicIntegerArray可以保证数组的原子性
1.构造方法:
AtomicIntegerArray(int length) 创建指定长度的给定长度的新AtomicIntegerArray。
AtomicIntegerArray(int[] array) 创建与给定数组具有相同长度的新AtomicIntegerArray,并从给定数组复制其所有元素。
2.成员方法:
int addAndGet(int i, int delta) 以原子方式将给定值与索引 i 的元素相加。
int get(int i) 获取指定索引处元素的值
Day12【线程安全、并发包】
线程安全问题的产生原因:
线程安全问题都是由全局变量及静态变量引起的。而每个线程操作这个变量都需要很多步骤:获取变量的值、打印变量的值、更改变量的值,而一个线程在执行某一步骤时都可能被暂停,而另一个线程会执行,这同样会导致多个线程访问同一个变量,最终导致这个变量的值不准确。
12.1同步代码块(悲观锁)
格式:
synchronized(锁对象){
访问了共享数据的代码(产生了线程安全问题的代码)
}
注意:
1.锁对象可以是任意的对象;"aaa"==>也是对象,相同字符串变为同一个对象,因为地址相同。
2.必须保证所有的线程使用的都是同一个锁对象
原理:
1.当一个线程运行到run()方法,会先判断是否有锁对象
2.如果有则将锁对象带入到代码块中,执行完后再归还给代码块。
如果没有,就一直等待其他线程归还代码块
特点:
同时只有一个线程在执行且需要频繁判断获取和归还代码块,会导致效率低下
12.2同步方法(悲观锁)
原理:
把访问了共享数据的代码,提取出来放到一个方法中
在方法上添加一个同步关键字synchronized
底层也是使用一个锁对象,把方法锁住,只让一个线程获取锁对象进入到方法中执行,保证安全
格式:
权限修饰符 synchronized 返回值类型 方法名(参数){
访问了共享数据的代码(产生了线程安全问题的代码)
}
注意:
快捷键:抽取方法: ctrl+alt+m
同步方法的锁对象是根据本类创建的对象。
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
12.3Lock锁(悲观锁)
1.概念
java.util.concurrent.locks.Lock接口
DK1.5之后出现的
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
2.接口中的方法:
void lock() 获取锁。
void unlock() 释放锁。
java.util.concurrent.locks.ReentrantLock类 implements Lock接口
3.实现步骤:
1.在成员位置创建ReentrantLock对象
2.在可能出现线程安全问题的代码前,使用lock方法获取锁对象
3.在可能出现线程安全问题的代码后,使用unlock方法释放锁对象
4.原理:
使用lock方法和unlock方法把一段代码包裹住,只让一个线程进入到两个方法中间执行
12.4CAS与Synchronized
1.AtomicInteger:只能解决一个变量的原子性
synchronized:可以解决一段代码的原子性
2.Synchronized是从悲观的角度出发:
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。因此Synchronized我们也将其称之为悲观锁。jdk中的ReentrantLock也是一种悲观锁。
3.CAS是从乐观的角度出发:
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
12.5并发包
在JDK的并发包java.util.concurrent里提供了几个非常有用的并发容器和并发工具类。供我们在多线程开发中进行使用。这些集合和工具类都可以保证高并发的线程安全问题.
---------------------------------------------------------------
1.并发List集合_CopyOnWriteArrayList
里边的方法使用方式和ArrayList集合是一样的,底层使用CAS机制:乐观锁 效率高
java.util.Vector<E>:是一个高并发线程安全的集合,底层使用的是悲观锁synchronized 效率低
2.并发Set集合_CopyOnWriteArraySet
3.并发Map集合_ConcurrentHashMap
java.util.concurrent.ConcurrentHashMap<K,V>:多线程安全的双列集合底层采用的是乐观锁:CSA 效率高
java.util.Hashtable<K,V>:多线程安全的双列集合,底层采用的是悲观锁:synchronized效率低
原因:
Hashtable锁定整个哈希表,一个操作正在进行时,其他操作也同时锁定
concurrentHashMap局部锁定,指锁定桶,当前元素锁定时,其他元素不锁定
12.6多线程协作
1.CountDownLatch
1.概念:
CountDownLatch允许一个或多个线程等待其他线程完成操作。
2.构造方法:
public CountDownLatch(int count)// 初始化一个指定计数器的CountDownLatch对象
3.成员方法:
public void await() throws InterruptedException// 让当前线程等待
public void countDown() // 计数器进行减1
2.CyclicBarrier
1.概念:
CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
2.构造方法:
public CyclicBarrier(int parties, Runnable barrierAction)// 用于在线程到达屏障后,再优先执行barrierAction,方便处理更复杂的业务场景
3.成员方法:
public int await()// 每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞
12.7并发数量控制
1.Semaphore:
Semaphore的主要作用是控制线程的并发数量。
synchronized可以起到"锁"的作用,但某个时间段内,只能有一个线程允许执行。
Semaphore可以设置同时允许几个线程执行。
Semaphore字面意思是信号量的意思,它的作用是控制访问特定资源的线程数目。
2.构造方法:
public Semaphore(int permits)//permits 表示许可线程的数量
public Semaphore(int permits, boolean fair) //fair 表示公平性,如果这个设为 true 的话,下次执行的线程会是等待最久的线程
3.成员方法:
public void acquire() 表示获取许可 lock
public void release() 表示释放许可 unlock
12.8线程信息交互
1.概述:
Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。
两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
2.构造方法:
public Exchanger()
3.成员方法:
public V exchange(V x) 参数传递给对方的数据,返回值接收对方返回的数据
4.应用:
数据校对工作
Day13【线程池、死锁、线程状态、定时器】
13.1线程池
1.线程池:
本质是容器(数组、集合),常用LinkedList<Thread> list = new ...,
利用list.removeFirst()和list.addLast获取和归还线程
JDK1.5后,Java内置了线程池技术。
2.Executors:
java.util.concurrent.Executors:是一个创建线程池的工具类,专门用来生产线程池,里边的方法都是静态的
1.静态方法:
static ExecutorService newFixedThreadPool(int nThreads)
参数:
int nThreads:创建的线程池,包含的线程数量
返回值:
ExecutorService:就是一个线程池,ExecutorService是一个接口,返回的是ExecutorService接口的实现类对象
3.ExecutorService:
java.util.concurrent.ExecutorService:描述线程池的接口
Future<?> submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
<T> Future<T> submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
java.util.concurrent.Future<V>接口
Future 表示异步计算的结果。用来接收call方法的返回值
Future接口中的方法:
V get() 如有必要,等待计算完成,然后获取其结果。
参数:
Runnable task:传递Runnable接口的实现类对象(线程任务)==>重写run方法,设置线程任务==>run方法没有返回值
Callable<T> task:传递Callable接口的实现类对象(线程任务)==>重写call方法,设置线程任务==>call方法有返回值
返回值:
Future:用来接收线程任务的返回值==>用来接收call方法的返回值
4.Callable:
java.util.concurrent.Callable<V>接口:返回结果并且可能抛出异常的任务。
方法:
V call() 计算结果,如果无法计算结果,则抛出一个异常。
注意:
Callable 不能通过Tread创建线程。因为Tread中没有对应的构造方法。
13.2死锁
1.概念:
线程获取不到锁对象,从而进不去同步中执行
2.前提:
1.必须出现同步代码块的嵌套
2.必须有两个线程
3.必须有两个锁对象
13.3线程状态
线程由生到死的完整过程,在java.lang.Thread.State这个枚举中给出了六种线程状态:
1.NEW(新建) 线程刚被创建,但还并未启动。
2.Runable(可运行) 线程可以在java虚拟机中运行的状态。
3.Blocked(锁阻塞) 当一个线程试图获取一个锁对象,而该对象锁被其他的线程持有,则进入Blocked状态
4.Waiting(无限等待) 一个线程在等待另一个线程执行一个唤醒动作时
5.Timed Waiting(计时等待) 同waiting状态,但是有超时参数,这一状态会保持到超时期被唤醒
6.Terminated(被终止) 因为run方法正常推出或没有捕获的异常而死亡。
13.4等待与唤醒
1.java.lang.Object
2.方法:
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void notify()
唤醒在此对象监视器(同步锁,对象锁)上等待的单个线程。 最开始的那个
void notifyAll()
唤醒在此对象监视器(同步锁,对象锁)上等待的所有线程。
注意:
1.wait方法和notify方法一般都是使用在同步代码块中==>有锁对象==>对象监视器
wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。
因为所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。
wait()的意思是: 让占用了这个同步对象的线程,临时释放当前的占用,并且等待。 所以调用wait是有前提条件的,一定是在synchronized块里,否则就会出错。
2.一般都是使用锁对象调用wait和notify方法(多个线程使用的是同一个锁对象)
锁对象(Thread-0)-->wait方法-->Thread-0线程-->等待
锁对象(Thread-1)-->notify方法-->唤醒在锁对象上等待的线程-->唤醒Thread-0线程
3.在同步中的线程调用wait方法,进入到等待,会释放锁对象的
在同步中线程调用sleep方法,进入睡眠,不会释放锁对象的
3.使用方法:
锁对象、唤醒和等待方法、状态控制变量 三者之间相互搭配控制两个线程交替运行。
13.5定时器
1.概述:
可以设置线程在某个时间执行某件事情,或者某个时间开始,每间隔指定的时间反复的做某件事情
2.java.util.Timer类:描述定时器的类
一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
构造方法:
Timer() 创建一个新计时器。
成员方法:
void cancel() 终止此计时器,丢弃所有当前已安排的任务。
void schedule(TimerTask task, long delay) 在指定毫秒值之后,执行指定的任务,只会执行一次
void schedule(TimerTask task, long delay, long period) 在指定毫秒值之后,执行指定的任务,之后每隔固定的毫秒数重复的执行定时任务
void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务,只会执行一次
void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。
3.java.util.TimerTask:类 implements Runnable接口
由 Timer 安排为一次执行或重复执行的任务。
void run() 此计时器任务要执行的操作。重写run方法,设置线程任务
程获取不到锁对象,从而进不去同步中执行
2.前提:
1.必须出现同步代码块的嵌套
2.必须有两个线程
3.必须有两个锁对象
13.3线程状态
线程由生到死的完整过程,在java.lang.Thread.State这个枚举中给出了六种线程状态:
1.NEW(新建) 线程刚被创建,但还并未启动。
2.Runable(可运行) 线程可以在java虚拟机中运行的状态。
3.Blocked(锁阻塞) 当一个线程试图获取一个锁对象,而该对象锁被其他的线程持有,则进入Blocked状态
4.Waiting(无限等待) 一个线程在等待另一个线程执行一个唤醒动作时
5.Timed Waiting(计时等待) 同waiting状态,但是有超时参数,这一状态会保持到超时期被唤醒
6.Terminated(被终止) 因为run方法正常推出或没有捕获的异常而死亡。
13.4等待与唤醒
1.java.lang.Object
2.方法:
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void notify()
唤醒在此对象监视器(同步锁,对象锁)上等待的单个线程。 最开始的那个
void notifyAll()
唤醒在此对象监视器(同步锁,对象锁)上等待的所有线程。
注意:
1.wait方法和notify方法一般都是使用在同步代码块中==>有锁对象==>对象监视器
wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。
因为所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。
wait()的意思是: 让占用了这个同步对象的线程,临时释放当前的占用,并且等待。 所以调用wait是有前提条件的,一定是在synchronized块里,否则就会出错。
2.一般都是使用锁对象调用wait和notify方法(多个线程使用的是同一个锁对象)
锁对象(Thread-0)-->wait方法-->Thread-0线程-->等待
锁对象(Thread-1)-->notify方法-->唤醒在锁对象上等待的线程-->唤醒Thread-0线程
3.在同步中的线程调用wait方法,进入到等待,会释放锁对象的
在同步中线程调用sleep方法,进入睡眠,不会释放锁对象的
3.使用方法:
锁对象、唤醒和等待方法、状态控制变量 三者之间相互搭配控制两个线程交替运行。
13.5定时器
1.概述:
可以设置线程在某个时间执行某件事情,或者某个时间开始,每间隔指定的时间反复的做某件事情
2.java.util.Timer类:描述定时器的类
一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
构造方法:
Timer() 创建一个新计时器。
成员方法:
void cancel() 终止此计时器,丢弃所有当前已安排的任务。
void schedule(TimerTask task, long delay) 在指定毫秒值之后,执行指定的任务,只会执行一次
void schedule(TimerTask task, long delay, long period) 在指定毫秒值之后,执行指定的任务,之后每隔固定的毫秒数重复的执行定时任务
void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务,只会执行一次
void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。
3.java.util.TimerTask:类 implements Runnable接口
由 Timer 安排为一次执行或重复执行的任务。
void run() 此计时器任务要执行的操作。重写run方法,设置线程任务