一,java语言概述
1.常用Dos命令
dir:列出当前目录下的文件以及文件夹
md:创建目录
rd:删除目录(空目录)
cd:进入指定目录
cd..:退回上一级目录
cd/:退回根目录
del:删除文件
echo 1 >java.txt:创建文件
exit:退出
2,java语言运行机制及运行过程
Java语言特点:跨平台性
Java两种核心机制:
Java虚拟机(jvm):Java程序运行环境
垃圾回收机制(gc)
3.jdk,jre,jvm的关系
jdk:Java开发工具包。(Java开发工具包和jre)
jre:Java运行环境。(包括jvm)
4 环境变量的配置
为什么要添加环境变量?
让Java工具包在任何路径下都可以使用。
高级系统设置-环境变量:
系统变量-path(window系统执行命令时需要搜索的路径)
加上 jdk的安装路径\bin
开发时的配置(推荐):
在系统变量里面新建变量JAVA_HOME 值为jdk安装路径。
然后再classpath变量中加入:%JAVA_HOME%\bin
5.HelloWorld
如何显示记事本后缀:
我的电脑-查看-文件拓展名
Java代码写在.java结尾的文件中。(源文件)
通过javac命令对该Java文件进行编译。(字节码文件)
通过Java命令对。class文件进行运行。
6 注释
1.单行注释//
2.多行注释/**/
3.文档注释/** */
注释内容可以被jdk提供的工具javadoc解析,生成一套以网页文件形式体现的该程序的说明文档。
二,java基本语法
1.关键字和保留字
关键字:在Java语言里有特殊含义的字符串。
保留字:目前没用到,以后的版本可能会用到。
2.标识符
标识符:自己起的名字。包括:包名,类名。。。
0-9 _ $
数字不可以开头
不能使用关键字和保留字,可以包含
严格区分大小写
不能包含空格
3.变量
变量
变量:内存中的一个存储区域。
可以在同一类型范围内变化。
包含:类型,名,存储的值。
作用:内存中保存数据。
先声明,后使用。
同一个作用域内,不能定义重复的变量名。
变量分类:按照数据类型分类
基本数据类型:数值(整数,浮点数),字符,布尔
引用数据类型:类(包括字符串),接口,数组
变量分类:按照声明位置不同分类
成员变量:(类内,方法体外)
包括:实例变量(不以
static修饰),类变量(以
static修饰)
局部变量:(方法体内)
包括:形参,方法局部变量,代码块局部变量
1.整型:
byte,short,int,long 字节
(1.2.4.8)1字节
=8bit
*long型变量的声明必须以l或L结尾
2.浮点型:
float(
4),
double(
8)
float:单精度浮点数,精确
7位
*Java的浮点型常量默认为
double类型,声明
float常量,后加f或F。
3.字符类型
char(
2)
表示方式:
1.声明一个字符,
2.定义一个转义符。
char c
='\n';
4.布尔类型
boolean:true,false
双引号里面如果想要使用双引号,前面需要加\
char+int=int
基本数据类型之间的运算
1.自动类型提升
byte+int=int
Java支持自动向上转型。
当
byte,
char,
short三种变量做运算时,结果为
int。
2.强制类型转换
向下转型
强制类型转换符(
int),可能会损失精度。
*long赋值的数后面不加l可能会导致编译失败,过大的整数。
整型常量默认类型为
int,浮点型默认常量为
double。
String类型变量的使用
String属于引用类型。
String可以和所有类型做运算。
4.运算符
算术运算符
a=2;b=++a;=>a=3,b=3;
a=2;b=a++;=>a=3,b=2;
赋值运算符
比较运算符
逻辑运算符
&和&&的异同:
1)都表示且
2)&&短路且
位运算符
三元运算符
三元运算符的嵌套:
int sum=(a>b):a?((a==b)?"a==b":b)
另一个三目运算符作为表达式出现
5.流程控制
顺序结构
分支结构
if-else
switch-case
switch(表达式
){
case 常量
1:
执行语句;
break;
default:
执行语句;
}
switch结构中的表达式,只能是如下
6种数据类型:
byte,
short,
char,
int,String,枚举。
例题:年份累加
循环结构:
for
while
do{
}while();
键盘输入:
import java
.util
.Scanner
;
Scanner sc
=new Scanner(System
.in
);
int a
=sc
.nextInt();
String b
=sc
.next();
三,数组
1.一维数组
1)数组默认值
数组元素的默认初始化值:
整型数组元素默认值为0
浮点型数组元素默认值0.0
布尔型数组元素默认值false
字符型数组元素默认值Ascii值位0的元素
引用数据类型数组元素默认值null
2)一位数组内存解析
2.二维数组
1)二维数组的使用
int[][] arr
= new int[][] { { 1, 2, 3 }, { 4, 5 }, { 6, 8, 7 } };
int[][] arr1
= new int[3][2];
int[][] arr2
= new int[3][];
arr2
[1] = new int[3];
for (int i
= 0; i
< arr2
[1].length
; i
++) {
System
.out
.println(arr2
[1][i
]);
}
System
.out
.println(arr
.length
);
System
.out
.println(arr
[0].length
);
for (int i
= 0; i
< arr
.length
; i
++) {
for (int j
= 0; j
< arr
[i
].length
; j
++) {
System
.out
.println(arr
[i
][j
]);
}
}
2)二维数组默认初始值
int a
[][]=new int[3][3];
System
.out
.println(a
[0]);
System
.out
.println(a
[0][0]);
System
.out
.println(a
[2][3]);
3)二维数组的内存解析
3.数组中设计的常见算法:
1)反转
2)线性查找
3)二分查找
4)冒泡排序
4.Arrays工具类的使用
Arrays工具类的使用
int a
[] = new int[] { 43, 26, 25, 65, 89, 75, 45, 13, 23, 15, 65 };
int b
[] = new int[] { 43, 26, 25, 65, 89, 75, 45, 13, 23, 15, 65 };
System
.out
.println(Arrays
.equals(a
, b
));
Arrays
.sort(a
);
int key
=89;
System
.out
.println(Arrays
.binarySearch(a
, key
));
System
.out
.println(Arrays
.toString(a
));
5.数组中的常见异常
1.下标越界异常
2.空指针异常
String arr
[]=new String [] {"aa","bb","cc"};
arr
[0]=null
;
System
.out
.println(arr
[0].toString());
四,面向对象
1.成员变量(属性)与局部变量的区别
不同点:
1.类中的声明位置不同
1)类内,方法体外
2)方法的形参列表,方法体内,构造器形参,构造器内部变量
2.权限修饰符的不同
1)可以在声明属性时指明其权限,使用权限修饰符
2)不能使用权限修饰符
3.默认初始化值
1)根据其类型都有默认的初始化值
2)没有默认初始化值(调用之前显示赋值),形参在调用时赋值。
4.内存中加载的位置
1)堆中(非
static)
2)栈中
2.return关键字
1适用范围:使用在方法体中
2.作用:
1)结束方法
2)针对有返回值类型的方法,返回值。
3.return 后面不可以声明执行语句
方法中使用的注意点:
可以调用当前类的属性和方法
方法中不可以定义方法
3.对象数组
内存解析
4.匿名对象与方法重载
匿名对象
new Phone().price
=1999;
new Phone().showPrice
;
每次
new的都是堆空间的一个新对象。
方法重载
1.定义:在同一个类中,允许存在一个以上同名方法,
只要他们的参数个数/顺序,参数类型不同即可。
2.特点:与返回值无关,与权限修饰符无关。
常见的:构造器重载
5.可变个数形参与变量赋值
可变个数形参
Object...args
如果有多个参数,必须放到最后。
变量赋值
如果变量是基本数据类型:此时赋值的是变量所保存的数据值。
如果变量时引用数据类型:此时赋值的是变量所保存数据的地址值。
此时改变变量的值,相当于改变地址对应的值。
6.值传递机制与递归方法
值传递机制
形参:方法定义时,小括号内的参数。
实参:方法调用时,实际传递给形参的值。
如果参数是基本数据类型,此时赋值的是变量所保存的数据值。
如果参数是引用数据类型,此时赋给形参的值,是变量所指向地址的值。
递归方法
一个方法体内,自己调用自己。
public static void main(String
[] args
) {
int n
= 100;
int sum
= getSum(n
);
System
.out
.println(sum
);
}
public static int getSum(int sum
) {
if (sum
== 1) {
return 1;
} else {
return sum
+ getSum(sum
- 1);
}
}
public static int getSum(int sum
) {
if (sum
== 0) {
return 1;
} else if (sum
== 1) {
return 4;
} else {
return 2 * getSum(sum
- 1) + getSum(sum
- 2);
}
}
public static void main(String
[] args
) {
int arg
=10;
int avg
=getArgs(arg
);
System
.out
.println(avg
);
}
public static int getArgs(int args
){
if(args
<=2){
return 1;
}
else{
return getArgs(args
-1)+getArgs(args
-2);
}
}
7.封装和隐藏
封装和隐藏:
特点:
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅仅对外暴漏少量方法用于使用。
体现:私有化属性,设置公有的方法来获取和设置属性。需要权限修饰符来配合。
权限修饰符:
1.private:类内部
2.default:同一包下的类
3.protected:不同包的子类
4.public:同一项目下
用来修饰类:public和default,和类的内部结构:属性,方法,构造器,内部类。
构造器:construct
作用:
1)创建对象
2)初始化对象
构造器重载
属性赋值的先后顺序:
1)默认初始化
2)显示初始化
3)构造器初始化
4)set方法
javabean
类是公有的
有一个无参的公共构造器
有属性,且有对应的get,set方法
关键字this的使用
1)
this可以用来修饰属性和方法
this代表当前对象
2)
this可以用来修饰和调用构造器
调用构造器:
public Student(Integer number
, Integer state
) {
this.number
= number
;
this.state
= state
;
}
public Student(Integer number
, Integer state
, Double score
) {
this(number
,state
);
this.score
= score
;
}
MVC设计模式:
视图模型层view
控制器层controller:service,base,activity
模型层model:bean,dao,db
继承性:extends
减少了代码的量,提高了代码复用性
便于功能的拓展
为多态的实现提供了前提
一旦子类A继承父类B以后,A就获取了B中声明的所有属性和方法。
规定:
一个类可以被多个子类继承
一个类只能由一个父类
允许多层继承
8.Eclipse中的Debug
Debug:
1.设置断点:
2.按键:
F5进入方法,F6一行一行执行,F7从方法中出来
resume:终止此处断点,进入下一处。
Terminate:强行终止
debug中step into功能失灵问题:更换eclipsejre为jdk的。
9.方法重写与权限修饰符
方法重写
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。
2.应用:重写以后,当子类执行该方法,实际上执行的是子类重写父类的方法。
3.规定:
方法名形参列表必须相同,
子类重写方法的权限修饰符不小于父类被重写的方法,
*子类不能重写父类的私有方法,
返回值类型:void--》void,其他的小于等于父类的返回值类型。
子类抛出的异常类型不能大于父类。
非static(因为静态方法不能被覆盖,随着类的加载而加载)
权限修饰符
四种权限修饰符
在不同包的子类中,能调用order类中声明为protected和public的属性,方法。
不同包下的不同类(非子类)只能调用order类中的public的属性和方法。
10.super关键字
super调用属性和方法
1.我们可以在子类的方法或构造器中,通过使用
"super."属性
/方法的方式,显示的调用
父类中声明的属性或方法,通常省略。
2.特殊情况下,当子类和父类定义了同名的属性时,用
super来调用父类的方法。
3.当子类重写了父类的方法,在子类的方法中调用父类中被重写的方法,必须使用
super。
4.super调用构造器:
public Student(int id
,String name
,String school
){
super(id
,name
);
this.school
=school
;
}
在子类构造器中用
super(形参列表)调用父类中声明的指定的构造器。
super声明在子类构造器首行。
11.子类对象实例化全过程:
1.从结果上看,
子类继承父类以后,就获取了父类中声明的属性和方法;
创建子类对象,在堆空间中,就会加载所有父类中声明的属性。
2.从过程上看,
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接地调用其父类构造器,
进而调用父类的父类的构造器,知道调用了Object的无参构造器为止。正因为加载过所
有的父类结构,所以才可以看到内存中有父类的结构,子类对象才可以进行调用。
12.向下转型
向下转型:使用强制类型转换符
Person p
=new Student();
Student stu
=(Student
)p
;
13.instanceof关键字:
stu instanceof p:
判断对象stu是否是类p的实例,如果是,返回true,如果不是,返回false。
使用情景:为了避免向下转型时出现类型转换异常,我们在向下转型之前,先用
instanceof进行判断,如果返回true在进行转型。
14.java.lang.Object
1.Object类时左右Java类的父类
2.如果在声明类时没有指明类的直接父类,默认类的父类为Object。
3.Object类中的功能(属性,方法)具有通用性。
属性:无。
方法:equals()/toString()/getClass()/hashCode()/clone()克隆/finalize()垃圾回收
4.Object类只声明了一个空参构造器
15.==和equals()的区别
1.==可以使用在基本数据类型和引用数据类型变量中
2.==如果比较基本数据类型变量,比较的是数据是否相等(不一定类型要相同)。
如果比较引用类型变量,比较的是地址值是否相等,即两个对象是否指向同一个对象实体。
1.equals()是一个方法,不是运算符。
2.只适用引用数据类型。
3.Object类中equals的定义:
public boolean equals(Object obj) {
return (this == obj);
}
String类中定义的equals方法:
STring对equals方法进行了重写,如果地址相同,返回true,
否则比较字符串的值是否相同。
16.equals()方法的重写:
public boolean equals(Object o
) {
if (this == o
) return true;
if (o
== null
|| getClass() != o
.getClass()) return false;
Customer customer
= (Customer
) o
;
return age
== customer
.age
&&
Objects
.equals(name
, customer
.name
);
}
17.Object类中toString()的使用:
1.当我们输出一个对象的引用,实际上就是调用当前对象的toString()
2.Object类中对toString()的定义:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
3.向String,Date,File。包装类实际上都重写了Object的toString方法。
使得在调用对象的toString方法时,返回实体对象信息。
4.自定义类重写toString方法
18.包装类
八种数据类型都有对应的包装类 char–>Character
public static void main(String
[] args
) {
Integer a
=10;
int b
=a
;
int c
=a
.intValue();
int num1
=10;
Integer num2
=num1
;
Integer num3
=new Integer(num1
);
int num
=1;
String s1
=num
+"";
String s2
=String
.valueOf(num
);
Integer number
=10;
String s3
=String
.valueOf(number
);
int aa
=Integer
.parseInt(s3
);
Integer bb
= Integer
.parseInt(s3
);
}
Object o1
=true?new Integer(1):new Double(2.0);
System
.out
.println(o1
);
Integer内部存在一个Integer[]数组,范围-128–+127,如果我们是用自动装箱的方式, 给integer赋值的范围在此范围内直接从数组中取。
19.static关键字
1.可以用来修饰属性,方法,代码块,内部类
2.修饰属性:
静态属性(类变量)和非静态属性(实例变量)
实例变量:每个对象都有自己的实例变量。
静态变量:所有对象共享静态变量。通过一个对象修改,别的对象调用的也是修改过的。
public static void setCountry(String country) {
Chinese.country = country;
}
静态变量随着类的加载而加载。
静态变量的加载早于对象的创建。
由于类只会加载一次,则静态变量在内存中也只存在一份,在方法区的静态域中。
3.修饰方法:
静态方法_随着类的加载而加载。
静态方法中只能调用静态的方法或属性。
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。
4.开发中如何确定一个属性要声明为static?
所有对象都相同的属性。
开发中如何确定一个方法要声明为static?
操作静态属性的方法
工具类中的方法
static实现id自增
private static int idadd=1001;
类变量与实例变量的内存解析
20.单例设计模式:
1.采取一定的方法,保证在整个软件系统中,对某个类只能存在一个对象实例。
2.饿汉式vs懒汉式实现:
public class Test1 {
private Test1(){
}
private static Test1 test
=new Test1();
public static Test1
getTest1(){
return test
;
}
}
public class Test1 {
private Test1(){
}
private static Test1 test
=null
;
public static Test1
getTest1(){
if(test
==null
){
test
=new Test1();
}
return test
;
}
}
3.懒汉式和饿汉式的对比:
饿汉式占用内存,线程安全的。
懒汉式好处:延迟对象的创建,线程不安全。
4.应用场景:
网站计数器,应用程序的日志应用,数据库连接池,Application。
main()方法的使用说明
也是一个普通的静态方法
可以做输入
21.代码块:和属性赋值顺序完结篇
作用:用来初始化对象。
只能用static来修饰
分类:静态代码块,非静态代码块
非静态能调用静态的属性方法,静态的不能掉用非静态的属性方法。
1.静态代码块
随着类的加载而执行
一共只执行一次,因此可以初始化类的信息。
2.非静态代码块
随着对象的创建而执行
每次创建对象都会执行一次,因此可以创建对象时初始化对象。
属性赋值顺序完结篇
1)默认初始化
2)显示初始化/在代码块中赋值
3)构造器初始化
4)set方法
22.final关键字
1.final修饰类和方法
用final修饰的类不能被继承,称为最终类。
eg:String,System,StringBuffer
用final修饰的方法不能被重写。
2.final修饰变量
此时的变量就称为常量。
1)修饰属性
显式初始化,代码块中初始化。
2)修饰局部变量
该局部变量的值不能被再次修改
23.抽象类与抽象方法
abstract关键字的使用
1.abstract抽象的
2.可以用来修饰的结构:类和方法
3.abstract修饰类:抽象类
1)此类不能实例化
2)抽象类中一定有构造器,便于子类实例化时调用。
3)开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作。
4.abstract修饰方法:抽象方法
public abstract void test();
包含抽象方法的类一定是抽象类,抽象类不一定包含抽象方法。
如果继承了抽象类,必须继承抽象类的抽象方法。
5.注意
1)
abstract不能用来修饰:属性,构造器。
2)
abstract不能用来修饰私有方法,静态方法,
final方法,
final的类。
创建抽象类的匿名子类对象
Person p
=new Person(){
public void eat(){
}
public void walk(){
}
};
24.接口:interface
1.接口的使用用interface来定义
2.Java中,接口和类是并列的两个结构。
3.如何定义接口,定义接口的成员
1)jdk7以前,只能定义全局常量和抽象方法
全局常量:public static final的,但是书写时,可以省略不写
抽象方法:public abstract的,但是书写时,可以省略不写。
2)jdk8:除了定义全局常量和抽象方法外,还可以定义静态方法,默认方法。
4.接口中不能定义构造器,接口不能实例化。
5.Java中,通过类去实现接口。implements
6.Java中允许实现多个接口。
7.接口与接口之间可以多继承
8.接口的实现体现了多态性。
面试题:接口与抽象类的比较:
1.抽象类:通过extends来继承,只能继承一个抽象类,
抽象类中一定有构造方法,创建子类对象时被调用。
抽象类中不光有抽象方法,还可以有其他方法。
2.接口:接口通过implements来实现,允许实现多个接口,
接口中不存在构造方法,接口中只能声明全局常量和抽象方法,
jdk'8.0以后,还可以定义静态方法和默认方法。
3.不能实例化,都可以被继承。
接口的应用:
代理模式
class TEST{
public static void main(String
[] args
) {
Zhuli zhuli
=new Zhuli(new Star());
zhuli
.sing();
zhuli
.buyThing();
zhuli
.getMoney();
}
}
public class Star implements Proxy {
@Override
public void sing() {
System
.out
.println("明星唱歌");
}
@Override
public void getMoney() {
}
@Override
public void buyThing() {
}
}
interface Proxy {
public void sing();
public void getMoney();
public void buyThing();
}
class Zhuli implements Proxy{
private Star star
;
public Zhuli(Star star
){
this.star
=star
;
}
@Override
public void sing() {
star
.sing();
}
@Override
public void getMoney() {
System
.out
.println("助理替歌手收钱");
}
@Override
public void buyThing() {
System
.out
.println("助理替歌手买东西");
}
}
工厂模式
实现创建者与调用者的分离,将创建对象的具体过程隔离起来。
public interface Java8 {
public static void test1(){
System
.out
.println("***");
};
public default void test2(){
System
.out
.println("***");
}
}
25.内部类的使用:
1.Java中允许将一个类声明在另一个类的内部 2.内部类的分类:成员内部类(静态,非静态)vs局部内部类(方法,代码块,构造器内) 3.成员内部类:
class Demo{
public static void main(String
[] args
) {
Person
.Dog dog
=new Person.Dog();
Person p
=new Person();
Person
.Bird bird
=p
.new Bird();
}
}
class Person {
private String name
;
public void eat(){
System
.out
.println("吃饭");
}
static class Dog{
}
class Bird{
public Bird(){
}
public void sing(){
eat();
name
="bird";
}
}
}
面试题:创建静态内部类对象和非静态内部类对象:
Person
.Dog dog
=new Person.Dog();
Person p
=new Person();
Person
.Bird bird
=new p.bird();
五,异常处理
1.异常的体系结构
异常:在Java中,程序执行发生的不正常情况称为异常。
java.long.Throwable
1.java.long.Error,:一般不编写针对性代码进行处理
2.java.long,Exception:可以进行异常的处理
1)编译时异常:IOException,FileNotFindException,ClassNotFindException
2)运行时异常:NullPointException,ArrayIndexOutOfBoundsException,ClassCastException
,NumberFormatException,InputMismatchException,ArithmeticException
2.异常处理机制一:
try-catch-finally
一旦抛出异常,其后的代码就不再执行。
try {
//可能会出现异常的代码
} catch (异常处理类型1 变量名1) {
e.printStackTrace
} catch (异常处理类型2 变量名2) {
e.printStackTrace
} finally {
//最终一定要执行的代码
}
编译时异常和运行时异常的不同处理
开发时运行时异常不需要try-catch
编译时异常用try-catch来解决
3.异常处理机制二:Throws +异常类型
“throws+异常类型”写在方法声明处,指明方法执行时,可能会抛出的异常。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,
此对象满足throws的异常类型时就会抛出,异常代码后续的代码,就不要执行。
try-catch真正的将异常处理掉了,而throws只是将异常抛给了方法的调用者,并没有真正的处理掉。
重写方法异常抛出的规则
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
开发中如何选择try-catch还是throws
1.如果父类被重写的方法没有抛出异常,则子类重写的方法中的异常只能用try-catch
2.执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,
我们建议这几个方法使用throws的方法进行处理,而方法a可以用try-catch进行处理。
4.手动生成一个异常对象并抛出:throw
public class MyException {
public static void main(String
[] args
) {
person p
=new person();
try {
p
.setAge(-1001);
} catch (Exception e
) {
System
.out
.println(e
.getMessage());
}
}
}
class person {
private int age
;
public person(){
}
public void setAge(int age
) throws Exception
{
if (age
>0){
this.age
=age
;
}else{
throw new Exception("年龄不能小于0!");
}
}
public int getAge(){
return age
;
}
}
5.用户自定义异常:
public class TestException extends IOException {
static final long serialVersionUID
=-7034897190745766939L
;
public TestException(){
}
public TestException(String msg
){
super(msg
);
}
}
六,多线程
1.概述
1)概念
程序:为完成特定任务,用某种语言编写的一组指令的集合。
一段静态的代码,静态对象。
进程:程序的一次执行过程,或是正在运行的一个程序。
是一个动态的过程,有他本身的生命周期。
独立的方法区和堆空间
线程:一个程序内部的执行路径。
独立的计数器和栈
单核cpu和多核cpu
假的多线程,多个线程交替进行。
并行和并发
并行:多个cpu同时执行多个任务。
并发:一个cpu执行多个任务。
并行:传输的数据8位一送出去
串行:传输的数据1位1送出去
2)优点
1.提高应用程序的响应,增强图形化界面用户的体验。
2.提高cpu利用率
3.改善程序结构,将复杂的程序分为多个线程。
3)何时需要
1.程序需要同时执行多个任务。
2.程序需要实现一些需要等待的任务时,用户输入,文件读写,网络操作,搜索。
3.需要一些后台运行的程序。
2.创建线程的方式一:继承Thread类
public class MyThread extends Thread {
public void run(){
for (int i
= 0; i
<100 ; i
++) {
System
.out
.println(this.currentThread().getName()+" "+i
);
}
}
}
class Test1{
public static void main(String
[] args
) {
MyThread m1
=new MyThread();
m1
.start();
}
}
1)线程的常用方法:
*start():启动当前线程:调用当前线程的run方法
* run();通常需要重写Thread类的run方法,将创建线程要执行的操作声明在此方法
* currentThread():静态方法,返回执行当前代码的线程。
* getName():获取当前线程名
* setName():设置当前线程的名字
* yield():释放当前cpu执行权
* join():在线程a中调用线程b的join方法,a进入阻塞状态,直到b执行完以后,a才结束阻塞状态。优先权
* sleep():挂起一会儿,单位ms
* stop():强制终止,死亡
2)线程优先级:
1.
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
2.如何获取和设置当前线程优先级:
m1.setPriority();
m1.getPriority();
优先级高并不一定代表一定先执行,只是概率大一点。
3)案例:多窗口卖票
public class ThreadTest1 {
public static void main(String
[] args
) {
Window w1
=new Window();
Window w2
=new Window();
w1
.start();
w2
.start();
}
}
class Window extends Thread{
private static int piao
=100;
public void run(){
while (true){
if (piao
>0){
System
.out
.println(currentThread().getName()+" "+piao
);
piao
--;
}else{
break;
}
}
}
}
3.创建多线程的方式二:实现runnerable接口的方式
public class RunnableTest2 {
public static void main(String
[] args
) {
myRunnable m1
=new myRunnable();
Thread t1
=new Thread(m1
);
t1
.start();
}
}
class myRunnable implements Runnable{
public void run(){
for (int i
= 0; i
<10 ; i
++) {
System
.out
.println(Thread
.currentThread().getName()+" "+i
);
}
}
}
1)案例:多窗口卖票
**
* runnable方式实现多窗口卖票,存在线程安全问题
*/
public class RunnableTest {
public static void main(String
[] args
) {
MyRunnable m1
=new MyRunnable();
Thread t1
=new Thread(m1
);
Thread t2
=new Thread(m1
);
Thread t3
=new Thread(m1
);
t1
.start();
t2
.start();
t3
.start();
}
}
class MyRunnable implements Runnable{
private int piao
;
public void run(){
while(true){
if (piao
>0){
System
.out
.println(Thread
.currentThread().getName()+" :"+piao
);
piao
--;
}else{
break;
}
}
}
}
2)线程的生命周期
线程的生命周期
Thread.state();
1.新建 new
2.就绪 start
3.运行 run
4.阻塞 join,sleep,等待同步锁,wait,(过时的挂起)
5.死亡 stop
4.线程的同步
通过继承Thread类实现多窗口卖票
public class TreadTest1 {
public static void main(String
[] args
) {
window1 w1
=new window1();
window1 w2
=new window1();
w1
.setName("窗口一");
w2
.setName("窗口二");
w1
.start();
w2
.start();
}
}
class window1 extends Thread {
private static int ticket
=100;
@Override
public void run() {
while (true){
synchronized (window1
.class){
if (ticket
>0){
try {
sleep(10);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
System
.out
.println(getName()+"卖出了一张票:"+ticket
);
ticket
--;
}else{
break;
}
}
}
}
}
继承Runnable方式实现多窗口卖票
public class RunnableTest1 {
public static void main(String
[] args
) {
window2 w1
=new window2();
Thread t1
=new Thread(w1
);
Thread t2
=new Thread(w1
);
t1
.setName("窗口一:");
t1
.setName("窗口二:");
t1
.start();
t2
.start();
}
}
class window2 implements Runnable{
private int ticket
=100;
@Override
public void run() {
while (true){
synchronized (this){
if (ticket
>0){
System
.out
.println(Thread
.currentThread().getName()+"卖出了票:"+ticket
);
ticket
--;
}else{
break;
}
}
}
}
}
通过继承Thread类来创建多线程
public class ThreadTest2 {
public static void main(String
[] args
) {
window3 t1
=new window3();
window3 t2
=new window3();
t1
.setName("窗口一:");
t2
.setName("窗口二:");
t1
.start();
t2
.start();
}
}
class window3 extends Thread{
private static int ticket
=100;
@Override
public void run() {
while (true){
show();
}
}
public static synchronized void show(){
if (ticket
>0){
System
.out
.println(Thread
.currentThread().getName()+"卖出了票:"+ticket
);
ticket
--;
}
}
}
通过继承runnable方式来创建多线程
public class RunnableTest2 {
public static void main(String
[] args
) {
window4 w
=new window4();
Thread t1
=new Thread(w
);
Thread t2
=new Thread(w
);
t1
.setName("窗口一:");
t2
.setName("窗口二:");
t1
.start();
t2
.start();
}
}
class window4 implements Runnable{
private int ticket
=100;
@Override
public void run() {
while (true){
show();
}
}
public synchronized void show(){
if (ticket
>0){
System
.out
.println(Thread
.currentThread().getName()+"卖出了:"+ticket
);
ticket
--;
}
}
}
5.线程安全的单例模式之懒汉式
1)通过同步代码块解决懒汉式单例设计模式的线程安全问题
public class Thread1 {
private static Thread1 instance
=null
;
public Thread1
getInstance(){
if (instance
==null
){
synchronized (Thread1
.class){
if (instance
==null
){
instance
=new Thread1();
}
}
}
return instance
;
}
}
2)通过同步方法解决懒汉式单例设计模式的线程安全问题
public class Thread2 {
private static Thread2 instance
= null
;
public synchronized Thread2
getInstance() {
if (instance
== null
) {
instance
= new Thread2();
}
return instance
;
}
}
6.死锁的问题:
public class DeadTest {
public static void main(String
[] args
) {
StringBuffer s1
=new StringBuffer();
StringBuffer s2
=new StringBuffer();
new Thread(){
public void run(){
synchronized (s1
){
s1
.append("123");
s2
.append("666");
synchronized (s2
){
s1
.append("456");
s2
.append("888");
}
}
}
}.start();
new Thread(){
public void run(){
synchronized (s2
){
s1
.append("123");
s2
.append("666");
synchronized (s1
){
s1
.append("456");
s2
.append("888");
}
}
}
}.start();
}
}
7.Lock锁方式解决线程安全问题:
public class LockTest {
public static void main(String
[] args
) {
testlock t
=new testlock();
Thread t1
=new Thread(t
);
Thread t2
=new Thread(t
);
t1
.setName("窗口一!");
t2
.setName("窗口二:");
t1
.start();
t2
.start();
}
}
class testlock implements Runnable {
private int ticket
= 100;
private ReentrantLock lock
= new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock
.lock();
if (ticket
> 0) {
System
.out
.println(Thread
.currentThread().getName() + "卖出了票:" + ticket
);
ticket
--;
} else {
break;
}
} finally {
lock
.unlock();
}
}
}
}
练习题:
public class Bank {
public static void main(String
[] args
) {
Account account
=new Account();
Customer c1
=new Customer(account
);
Customer c2
=new Customer(account
);
c1
.setName("尹会东");
c2
.setName("张贝贝");
c1
.start();
c2
.start();
}
}
class Account {
private double balance
=0;
public Account() {
}
public Account(double balance
) {
this.balance
= balance
;
}
public synchronized void save(double money
) {
try {
Thread
.sleep(1000);
balance
+=money
;
} catch (InterruptedException e
) {
e
.printStackTrace();
}
System
.out
.println(Thread
.currentThread().getName()+"存钱成功"+balance
);
}
}
class Customer extends Thread{
private Account account
;
public Customer(Account account
){
this.account
=account
;
}
public void run(){
for (int i
= 0; i
<3 ; i
++) {
account
.save(1000);
}
}
}
8.线程的通信:
public class Number implements Runnable {
private int num
=100;
@Override
public synchronized void run() {
while (true){
notify();
if (num
>0){
System
.out
.println(Thread
.currentThread().getName()+"打印了:"+num
);
num
--;
try {
wait();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}else{
break;
}
}
}
}
class test{
public static void main(String
[] args
) {
Number n
=new Number();
Thread t1
=new Thread(n
);
Thread t2
=new Thread(n
);
t1
.setName("线程一");
t2
.setName("线程二");
t1
.start();
t2
.start();
}
}
经典例题:生产者和消费者问题:
public class ProductTest {
public static void main(String
[] args
) {
Shop shop
=new Shop();
Producer p
=new Producer(shop
);
Customers c
=new Customers(shop
);
Thread t1
=new Thread(p
);
Thread t2
=new Thread(c
);
Thread t3
=new Thread(c
);
t1
.setName("生产者");
t2
.setName("消费者一");
t3
.setName("消费者二");
t1
.start();
t2
.start();
t3
.start();
}
}
class Producer implements Runnable{
private Shop shop
;
public Producer(Shop shop
){
this.shop
=shop
;
}
@Override
public void run() {
System
.out
.println("生产者开始生产东西");
while (true){
try {
sleep(50);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
shop
.in();
}
}
}
class Customers implements Runnable{
private Shop shop
;
public Customers(Shop shop
){
this.shop
=shop
;
}
@Override
public void run() {
System
.out
.println("消费者开始购买东西");
while (true){
try {
sleep(100);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
shop
.out();
}
}
}
class Shop{
private int thing
=0;
public synchronized void in() {
if (thing
<20){
thing
++;
System
.out
.println(Thread
.currentThread().getName()+"生产了东西"+thing
);
notify();
}else{
try {
wait();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
}
public synchronized void out(){
if (thing
>0) {
System
.out
.println(Thread
.currentThread().getName()+"购买了东西"+thing
);
thing
--;
notify();
}else{
try {
wait();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
}
}
9.创建多线程的方式三:实现Callable接口
public class Demo1 {
public static void main(String
[] args
) {
Share share
= new Share();
new Thread(new FutureTask(()->{
for (int i
= 0; i
< 100; i
++) share
.print(); return null
;}),"AA").start();
new Thread(new FutureTask(()->{
for (int i
= 0; i
< 100; i
++) share
.print(); return null
;}),"BB").start();
}
}
class Share {
private Integer num
=0;
private ReentrantLock lock
=new ReentrantLock();
private Condition cd
=lock
.newCondition();
public void print(){
try {
lock
.lock();
while (num
<100){
cd
.signal();
System
.out
.println(Thread
.currentThread().getName()+"打印了一张票:"+ ++num
+"还剩"+
(100-num
) +"张票。");
cd
.await();
}
} catch (Exception e
) {
e
.printStackTrace();
} finally {
lock
.unlock();
}
}
}
10.创建多线程的方式四:使用线程池
class num implements Runnable {
private int num
= 100;
@Override
public void run() {
while (true) {
synchronized (com
.qtguigu
.fuxi
.num
.class) {
if (num
> 0) {
System
.out
.println(Thread
.currentThread().getName() + num
);
num
--;
} else {
break;
}
}
}
}
}
public class PoolTest {
public static void main(String
[] args
) {
ExecutorService service
= Executors
.newFixedThreadPool(10);
num n1
= new num();
service
.execute(n1
);
service
.shutdown();
}
}
七,常用类
1.String
1)String内存解析
2)String分析
String s1
="abc";
String s2
="abc";
System
.out
.println(s1
==s2
);
System
.out
.println("**********************************");
String s3
="abc";
s3
+="def";
System
.out
.println(s1
==s3
);
System
.out
.println("**********************************");
String s4
="abc";
String s5
=s4
.replace('a','m');
System
.out
.println(s4
+" "+s5
);
3)String实例化
String s1
="java";
String s2
="java";
String s3
=new String("java");
String s4
=new String("java");
System
.out
.println(s1
==s2
);
System
.out
.println(s1
==s3
);
System
.out
.println(s3
==s4
);
4)图解两种创建字符串方式的区别
5)图解字符串的存储
6)图解字符串对象的存储
7)String不同拼接操作的对比:
String s1
="java";
String s2
="hadoop";
String s3
="javahadoop";
String s4
="java"+"hadoop";
String s5
=s1
+"hadoop";
String s6
="java"+s2
;
String s7
=s1
+s2
;
String s8
=s5
.intern();
System
.out
.println(s3
==s4
);
System
.out
.println(s4
==s5
);
System
.out
.println(s3
==s5
);
System
.out
.println(s5
==s6
);
System
.out
.println(s3
==s7
);
System
.out
.println(s5
==s7
);
System
.out
.println(s6
==s7
);
System
.out
.println(s8
==s3
);
面试题
public class StringTest4 {
String str
=new String("good");
char []ch
={'t','e','s','t'};
public static void main(String
[] args
) {
StringTest4 ex
=new StringTest4();
ex
.change(ex
.str
,ex
.ch
);
System
.out
.println(ex
.str
+" "+ex
.ch
);
}
private void change(String str
, char[] ch
) {
str
="test ok";
ch
[0]='b';
}
}
8)String的常用方法:
public static void main(String
[] args
) {
String s1
="hello";
String s2
="world";
System
.out
.println(s1
.length());
System
.out
.println(s1
.charAt(3));
System
.out
.println(s1
.isEmpty());
System
.out
.println(s1
.toLowerCase());
System
.out
.println(s1
.toUpperCase());
System
.out
.println(s1
.trim());
System
.out
.println(s1
.equals(s2
));
System
.out
.println(s1
.equalsIgnoreCase(s2
));
System
.out
.println(s1
.compareTo(s2
));
System
.out
.println(s1
.substring(2));
System
.out
.println(s1
.substring(2,4));
}
public static void main(String
[] args
) {
String s1
="helloworld";
String s2
="HelloWorld";
System
.out
.println(s1
.endsWith("ld"));
System
.out
.println(s1
.startsWith("he"));
System
.out
.println(s1
.startsWith("wo",5));
System
.out
.println(s1
.contains("owo"));
System
.out
.println(s1
.indexOf("lo"));
System
.out
.println(s1
.indexOf("lo",5));
System
.out
.println(s1
.lastIndexOf("lo"));
System
.out
.println(s1
.replace('l','y'));
System
.out
.println(s1
.replace("hello","666"));
9)String和其他类型之间的转换
public class StringTest7 {
public static void main(String
[] args
) {
String str1
="123";
int num
=Integer
.parseInt(str1
);
String str2
=String
.valueOf(num
);
String str3
="123456";
char[] array
= str3
.toCharArray();
String str4
= new String(array
);
String str5
="123456";
byte [] bytes
= str5
.getBytes();
String str6
= new String(bytes
);
}
}
10)String的四道面试题:
public static void main(String
[] args
) {
String str1
= " 123456 ";
char[] array
= str1
.toCharArray();
char[] array2
= new char[str1
.length()];
int num
= 0;
for (int i
= 0; i
< array
.length
; i
++) {
if (array
[i
] == ' ') {
continue;
} else {
array2
[num
] = array
[i
];
num
++;
}
}
String string
= new String(array2
);
System
.out
.println(string
.substring(0, num
));
}
public static void main(String
[] args
) {
int start
=2;
int end
=5;
String str1
="0123456789";
char[] array
= str1
.toCharArray();
for (int i
= start
; i
<(start
+end
)/2+1 ; i
++) {
char a
=array
[i
];
array
[i
]=array
[end
+start
-i
];
array
[end
+start
-i
]=a
;
}
String s
= new String(array
);
System
.out
.println(s
);
}
public static void main(String
[] args
) {
String str1
="我爱中国,中国共产党万岁!";
String str2
="中国";
int sum
=0;
while (str1
.contains(str2
)){
sum
++;
int num
=str1
.indexOf(str2
);
str1
=str1
.substring(num
+str2
.length());
}
System
.out
.println(sum
);
}
public static void main(String
[] args
) {
String str1
="9876543210";
char[] array
= str1
.toCharArray();
Arrays
.sort(array
);
String str2
= new String(array
);
System
.out
.println(str2
);
}
11)StringBuffer和StringBuilder
public class StringBufferTest {
public static void main(String
[] args
) {
StringBuffer sb1
= new StringBuffer("abcdef");
sb1
.append("A");
sb1
.delete(1, 4);
sb1
.replace(2,3,"hello");
sb1
.insert(2,"HELLO");
sb1
.reverse();
sb1
.charAt(1);
sb1
.indexOf("A");
System
.out
.println(sb1
);
}
}
面试题
public static void main(String
[] args
) {
String s1
=null
;
StringBuffer sb1
=new StringBuffer();
sb1
.append(s1
);
System
.out
.println(sb1
.length());
System
.out
.println(sb1
);
StringBuffer sb2
=new StringBuffer(s1
);
System
.out
.println(sb2
);
}
String,StringBuffer,StringBuilder的区别
2.时间日期类
1)jdk8之前的时间日期API
①时间戳
@Test
public void test(){
long millis
= System
.currentTimeMillis();
System
.out
.println(millis
);
}
②java.util.Date
-->java.sql.Date
两个构造器的使用
两个方法的使用
toString()显示当前的年月日时分秒
date.getTime()时间戳
@Test
public void test2(){
Date date
=new Date();
System
.out
.println(date
.toString());
System
.out
.println(date
.getTime());
}
@Test
public void test3(){
Date date
= new Date(System
.currentTimeMillis());
System
.out
.println(date
.toString());
}
③java.sql.Date
对应着数据库中的日期类型变量 如何实例化 sql.Date–>util.Date直接赋值 util.Date–>sql.Date
@Test
public void test4(){
java
.sql
.Date date
=new java.sql.Date(System
.currentTimeMillis());
System
.out
.println(date
.toString());
Date date1
=new Date();
long time
= date1
.getTime();
java
.sql
.Date date2
=new java.sql.Date(time
);
System
.out
.println(date2
.toString());
}
④SimpleDateFormat类
实例化格式化:日期–>文本(字符串)解析:文本(字符串)–>日期
@Test
public void test6() throws ParseException
{
SimpleDateFormat format
= new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date
=new Date();
String s
= format
.format(date
);
System
.out
.println(s
);
Date date1
= format
.parse(s
);
System
.out
.println(date1
);
}
⑤Calendar日历类的使用(抽象类)
1.实例化:①创建子类的对象(不建议)②调用其静态方法
@Test
public void test7() {
Calendar calendar
= Calendar
.getInstance();
int days
= calendar
.get(Calendar
.DAY_OF_MONTH
);
System
.out
.println(days
);
calendar
.set(Calendar
.DAY_OF_MONTH
, 20);
System
.out
.println(calendar
.get(Calendar
.DAY_OF_MONTH
));
calendar
.add(Calendar
.DAY_OF_MONTH
, 20);
System
.out
.println(calendar
.get(Calendar
.DAY_OF_MONTH
));
Date time
= calendar
.getTime();
System
.out
.println(time
);
Date date
= new Date();
calendar
.setTime(date
);
System
.out
.println(calendar
.getTime());
}
⑥练习
字符串2020-09-08转换为java.sql.Date
@Test
public void test8() throws ParseException
{
String str
="2020-09-08";
SimpleDateFormat format
= new SimpleDateFormat("yyyy-MM-dd");
Date date
= format
.parse(str
);
long time
= date
.getTime();
java
.sql
.Date sqlDate
=new java.sql.Date(time
);
System
.out
.println(sqlDate
.toString());
}
渔夫三天打鱼,两天晒网。1990-01-01
* 问:渔夫在打鱼还是在晒网?
* 2020-09-08
* 总天数%5==1,2,3打鱼;0,4晒网
* 总天数?
@Test
public void test9() throws ParseException
{
String start
="1990-01-01";
String end
="2020-09-08";
SimpleDateFormat format
= new SimpleDateFormat("yyyy-MM-dd");
Date date
= format
.parse(start
);
Date date1
= format
.parse(end
);
long time
= date
.getTime();
long time1
= date1
.getTime();
long day
=(time1
-time
)/(1000*60*60*24)+1;
if (day
%5==0||day
%5==4){
System
.out
.println("今天筛网");
}else{
System
.out
.println("今天打🐟");
}
}
2)jdk8时间日期API
①LocalDate,LocalTime,LocalDateTime的使用
@Test
public void test() {
LocalDate now
= LocalDate
.now();
LocalTime now1
= LocalTime
.now();
LocalDateTime now2
= LocalDateTime
.now();
System
.out
.println(now
);
System
.out
.println(now1
);
System
.out
.println(now2
);
}
@Test
public void test2() {
LocalDate date
= LocalDate
.of(2020, 2, 2);
System
.out
.println(date
);
}
@Test
public void test3() {
LocalDateTime time
= LocalDateTime
.of(2012, 2, 2, 12, 53, 23);
System
.out
.println(time
.getDayOfMonth());
System
.out
.println(time
.getDayOfWeek());
System
.out
.println(time
.getDayOfYear());
System
.out
.println(time
.getHour());
System
.out
.println(time
.getMonthValue());
}
@Test
public void test4() {
LocalDateTime time
= LocalDateTime
.of(2012, 2, 2, 12, 53, 23);
LocalDateTime localDateTime
= time
.withDayOfMonth(2);
System
.out
.println(localDateTime
);
LocalDateTime plusDays
= time
.plusDays(108);
System
.out
.println(plusDays
);
LocalDateTime minusDays
= time
.minusDays(20);
System
.out
.println(minusDays
);
}
②Instant类
@Test
public void test(){
Instant now
= Instant
.now();
System
.out
.println(now
);
OffsetDateTime offsetDateTime
= now
.atOffset(ZoneOffset
.ofHours(8));
System
.out
.println(offsetDateTime
);
long second
= offsetDateTime
.toEpochSecond();
System
.out
.println(second
);
Instant instant
= Instant
.ofEpochMilli(1580365450L
*1000);
System
.out
.println(instant
);
}
③格式化或者解析时间日期
@Test
public void test(){
DateTimeFormatter formatter
=DateTimeFormatter
.ofPattern("yyyy-MM-dd hh:mm:ss");
String str
=formatter
.format(LocalDateTime
.now());
System
.out
.println(str
);
TemporalAccessor accessor
=formatter
.parse("2020-02-18 03:52:09");
System
.out
.println(accessor
);
}
3.比较器与其他类
1)比较器
Java中的对象,只能使用==或者!=进行比较,不能使用<或>进行比较,但是在实际开发中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。如何实现?使用两个接口,Comparable或Comparator。
liang者使用对比:
1)Comparable:让类去继承接口,对象具有比较大小的属性
2)Comparator:临时new一个匿名对象重写方法,传入对象进行比较,对象不具有比较大小的属性
* Comparable接口的使用:自然排序
* 1.String或者包装类实现了Comparable接口重写了ComepareTo方法,给出了比较两个对象大小的方法。
* 2.重写CompareTo()方法的规则:
* 如果当前对象this大于形参对象obj,则返回正整数,
* 如果当前对象this小于形参对象obj,则返回负整数,
* 否则返回0;
* 3.对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写CompareTo方法。
* 在方法中指明如何排序。
public static void main(String
[] args
) {
Goods
[]arr
=new Goods[4];
arr
[0]=new Goods("lenovo",50.0);
arr
[1]=new Goods("honor",50.0);
arr
[2]=new Goods("iphone",888.8);
arr
[3]=new Goods("zte",66.6);
Arrays
.sort(arr
);
System
.out
.println(Arrays
.toString(arr
));
}
}
class Goods implements Comparable{
private String name
;
private double price
;
public Goods() {
}
public Goods(String name
, double price
) {
this.name
= name
;
this.price
= price
;
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public double getPrice() {
return price
;
}
@Override
public String
toString() {
return "Goods{" +
"name='" + name
+ '\'' +
", price=" + price
+
'}';
}
public void setPrice(double price
) {
this.price
= price
;
}
@Override
public int compareTo(Object o
) {
if (o
instanceof Goods){
Goods goods
= (Goods
) o
;
return this.price
>goods
.price
?1:(this.price
<goods
.price
?-1:(this.name
.compareTo(goods
.name
)));
}
throw new RuntimeException("传入的数据类型不一致!");
}
}
* Comparator接口:定制排序
* 1.当元素的北京没有实现java.long.Comparable接口而又不方便修改代码
* 2.实现了java.lang.Comparable接口的排序规则不适合当前的操作
* 3.抽象方法:compare(Object obj1,Object obj2)
public static void main(String
[] args
) {
Dog
[]arr
=new Dog[4];
arr
[0]=new Dog("lenovo",50);
arr
[1]=new Dog("honor",50);
arr
[2]=new Dog("iphone",40);
arr
[3]=new Dog("zte",60);
Arrays
.sort(arr
, new Comparator<Dog>() {
@Override
public int compare(Dog o1
, Dog o2
) {
if (o1
.getName().equals(o2
.getName())){
return -Double
.compare(o1
.getAge(),o2
.getAge());
}else{
return o1
.getName().compareTo(o2
.getName());
}
}
});
System
.out
.println(Arrays
.toString(arr
));
}
}
class Dog {
private String name
;
private int age
;
public Dog() {
}
public Dog(String name
, int age
) {
this.name
= name
;
this.age
= age
;
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public int getAge() {
return age
;
}
public void setAge(int age
) {
this.age
= age
;
}
@Override
public String
toString() {
return "Dog{" +
"name='" + name
+ '\'' +
", age=" + age
+
'}';
}
}
2)Math类
3)BigInteger和BigDecimal
高精度整数运算器和高精度浮点数运算器
八,枚举类和注解
1.枚举类
1)枚举类的使用
1.枚举的理解:类的对象只有有限个,确定的。我们称此类为枚举类。2.当我们定义一组常量时,建议使用枚举类。3.如果枚举类的对象只有一个,可以看作时单例设计模式。
2)如何定义枚举类
方式一:jdk5.0之前自定义枚举类
public class Season {
private final String name
;
private final String desc
;
private Season(String name
,String desc
){
this.name
=name
;
this.desc
=desc
;
}
public static final Season SPRING
=new Season("春天","春暖花开");
public static final Season SUMMER
=new Season("夏天","夏日炎炎");
public static final Season AUTUMO
=new Season("秋天","秋高气爽");
public static final Season WINTER
=new Season("冬天","雪花飘飘");
public String
getName() {
return name
;
}
public String
getDesc() {
return desc
;
}
@Override
public String
toString() {
return name
;
}
}
class Test1{
public static void main(String
[] args
) {
Season season
=Season
.SPRING
;
}
}
jdk5.0使用enum关键字定义枚举类
interface Info{
void show();
}
public enum Status
implements Info{
FREE
{
public void show(){
System
.out
.println("空闲");
}
},
BUSY
{
public void show(){
System
.out
.println("忙碌");
}
},
WORK
{
public void show(){
System
.out
.println("工作");
}
};
private Status(){
}
}
class Test2{
public static void main(String
[] args
) {
Status status
= FREE
;
status
.show();
Status
[] values
= Status
.values();
for (int i
=0;i
<values
.length
;i
++){
System
.out
.println(values
[i
]);
}
System
.out
.println(FREE
.toString());
System
.out
.println(Status
.valueOf("FREE"));
}
}
3)Enum类中常用方法
value()
valueof()
toString()
State
[] states
= State
.values();
for (int i
= 0; i
<states
.length
; i
++) {
System
.out
.print(states
[i
]+" ");
}
System
.out
.println(free
.toString());
System
.out
.println(State
.valueOf("FREE"));
4)使用enum关键字定义的枚举类实现接口的情况
*情况一:实现接口,在enum的枚举类中重写方法
情况二:实现接口,在enum的枚举类中声明的每个对象下都重写方法
FREE
{
@Override
public void show() {
System
.out
.println("空闲!");
}
},
2.注解
1.Annotation使用示例
/**
* @author yinhuidong
* @createTime 2020-04-08-20:56
* 1.Annotation使用示例
* 1)文档注释中的注解
* @return
* @Exception
* @param
* @see
* 2)jdk三个内置的注解
* 1.@Override 子类重写父类方法,编译期间校验
* 2.@Deprecated 过时的或危险的(可能造成线程死锁)
* 3.@SuppressWarnings() 未使用提醒
* 3)组件框架,跟踪代码依赖性,代替配置文件
* @Autowrited
*/
2.自定义注解与元注解
/**
* @author yinhuidong
* @createTime 2020-04-08-21:02
* 自定义注解:参照@SuppressWarnings()
* 如果自定义的注解没有成员,那就代表一个标识
* 如果自定义注解有成员,需要在使用时指定成员的值
* 自定义注解必须配合反射
* 如果想食用反射操作注解,那么注解必须声明为RUNTIME
*
* 元注解:可以修饰其他注解的注解
* 1. Retention:指明修饰的注解的生命周期:SOURCE\CLASS(默认行为)\RUNTIME
* *只有声明为runtime的注解,才能通过反射获取
* 2.Target:用于指定被修饰的结构有哪些
* 3.Documented:被他修饰的注解可以被文档注释读取,保留下来
* 4.Inherited:具有继承性,父类被此注解修饰,子类自动继承父类的注解
*/
public @interface MyAnnotation {
String value() default "hello";
//String类型的属性,默认值为hello
}
1.Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
继续查看RetentionPolicy,这是一个枚举类
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
SOURCE:编译时起作用
CLASS:字节码文件
RUNTIME:运行时
2.Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
继续点击进入ElementType
又是一个枚举类:指定可以修饰的类型
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,//类上
/** Field declaration (includes enum constants) */
FIELD,//属性
/** Method declaration */
METHOD,//方法
/** Formal parameter declaration */
PARAMETER,//成员变量
/** Constructor declaration */
CONSTRUCTOR,//构造器
/** Local variable declaration */
LOCAL_VARIABLE,//局部变量
/** Annotation type declaration */
ANNOTATION_TYPE,//注解类型
/** Package declaration */
PACKAGE,//包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,//泛型
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE //可重复注解
}
3.Documented
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
指示默认情况下iavadoc和类似工具将记录具有类型的注释。此类型应用于对类型声明进行注释,这些类型的注释会影响其客户端对带注释的元素的使用。如果类型声明是用文档注释的,那么它的注释将成为公共API的一部分注释元素的。
4.Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
表示注释类型是自动继承的。如果继承的元注释存在于注释类型上声明,用户查询类声明上的注释类型,而类声明没有针对这种类型的注释,然后类的超类将自动查询注释类型。此过程将重复进行,直到找到此类型的注释,或找到类层次结构的顶部(对象)是达到了。如果没有该类的超类,那么查询将表明所涉及的类没有这样的注释。<p>注意,这个元注释类型没有效果,如果注释的类型是用来注释类以外的任何东西。还要注意,这个元注释只会导致从超类继承注释;对实现接口的注释没有效果
3.jdk8新特性
1.可重复注解
一个类上写两个一样的注解
@MyAnnotation("hi")
@MyAnnotation
public class AnnotationTest {
}
@Retention(RetentionPolicy.RUNTIME)//运行时
//指定可以修饰哪些结构
@Target(value = {ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE,
ElementType.PARAMETER, ElementType.TYPE, ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE})
@Documented//文档注释保留识别
@Inherited//被子类继承
@Repeatable(MyAnnotations.class)//标识该注解可以实现重复注解
public @interface MyAnnotation {
String value() default "hello";
//String类型的属性,默认值为hello
}
@Retention(RetentionPolicy.RUNTIME)//运行时
//指定可以修饰哪些结构
@Target(value = {ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE,
ElementType.PARAMETER, ElementType.TYPE, ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE})
@Documented//文档注释保留识别
@Inherited//被子类继承
public @interface MyAnnotations {
//声明一个MyAnnotation类型的数组
MyAnnotation []value();
}
2.类型注解
class Test<@MyAnnotation T> {
}
//此时需要指明@Target(ElementType.TYPE_PARAMETER)
class Test2{
public static void main(String[] args) {
Class<Test> clazz = Test.class;
Annotation[] annotations = clazz.getAnnotations();
for (int i = 0; i <annotations.length ; i++) {
System.out.println(annotations[i]);
}
}
}
九,集合框架
1.collection接口
1)Java集合框架的概述
1.集合和数组都是对多个数据进行存储操作的结构,简称Java容器。
说明:此时的存储时内存方面的存储,不涉及持久化存储
2.1数组在存储多个数据方面的特点:
1)一旦初始化以后,长度就确定了。
2)元素类型一旦指定,就不能改变,我们就只能操作指定类型的数据。
2.2数组在存取数据方面的缺点:
1)初始化以后,长度不可修改。
2)数组中提供的方法非常有限,对于删除插入数据非常不方便,效率也不高。
3)获取数组中实际元素的个数,数组并没有提供现成的方法。
4)数组存储数据的特点:有序,可重复。
集合框架
Collection接口:单列集合,用来存储一个一个对象
list接口:有序的可重复的数据。“动态数组”
Set接口:无序的不可重复的数据。
Map接口:双列集合,用来存储一对一对的数据。(key,value)
Collection接口中的方法的使用
public static void main(String
[] args
) {
Collection collection
=new ArrayList();
collection
.add("aa");
collection
.add("bb");
collection
.add("cc");
System
.out
.println(collection
.size());
Collection collection2
=new ArrayList();
collection2
.add("bbb");
collection2
.add("ccc");
collection
.addAll(collection2
);
System
.out
.println(collection
.size());
System
.out
.println(collection
);
collection2
.clear();
System
.out
.println(collection
.isEmpty());
}
public static void main(String
[] args
) {
Collection collection
=new ArrayList();
collection
.add(123);
collection
.add("tom");
collection
.add("aa");
Person p1
=new Person("dong",23);
collection
.add(p1
);
System
.out
.println(collection
.contains(p1
));
collection
.containsAll(collection
);
collection
.remove(p1
);
collection
.removeAll(collection
);
collection
.retainAll(collection
);
collection
.equals(collection
);
}
public static void main(String
[] args
) {
Collection collection
=new ArrayList();
collection
.add("123");
collection
.add("aaa");
collection
.add("bbbb");
collection
.add("dddd");
System
.out
.println(collection
.hashCode());
collection
.toArray();
}
使用Iterator遍历Colllection集合
Collection集合实现了Iterator接口,重写了接口的hasNext()和next()方法。内部定义了remove()方法,可以在便利的时候,删除集合中的元素, 此方法不同于集合直接调用remove().
public static void main(String
[] args
) {
Collection co
=new ArrayList();
co
.add("123113212");
co
.add("456487874");
co
.add("56646665454654");
Iterator
<Collection>iterator
=co
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
}
增强for循环遍历集合
内部仍然调用了迭代器
把集合中的每个值一次一次赋值给Object类型的变量然后输出
public static void main(String
[] args
) {
Collection co
=new ArrayList();
co
.add("123113212");
co
.add("456487874");
co
.add("56646665454654");
for (Object c
:co
){
System
.out
.println(c
);
}
}
Collection集合接口是支持泛型的,并且继承了Iterable接口,可以使用Iterator iterator()进行遍历
* @see Set
* @see List
* @see Map
* @see SortedSet
* @see SortedMap
* @see HashSet
* @see TreeSet
* @see ArrayList
* @see LinkedList
* @see Vector
* @see Collections
* @see Arrays
* @see AbstractCollection
* @since 1.2
*/
public interface Collection<E> extends Iterable<E> {
public interface Iterable<T> {
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
Iterator<T> iterator();
2)list
比较ArrayList,LinkedList,Vector
* 同:三个类都实现了list接口,存储数据的特点相同,有序,可重复。
* ArrayList:作为list接口的主要实现类,jdk1.2,线程不安全的,效率高,底层使用Object []elementData存储
* LinkedList:jdk1.2:底层使用双向链表存储,对于频繁的插入删除,他的效率高。
* Vector:古老的实现类jdk1.0,线程安全的。
List接口常用方法测试:
public static void main(String
[] args
) {
ArrayList
<Object> list
= new ArrayList<>();
list
.add("123");
list
.add("456");
list
.add("789");
System
.out
.println(list
);
list
.add(0,"000");
System
.out
.println(list
);
List
<Integer> list1
= Arrays
.asList(1, 2, 3, 4, 5, 6);
list
.addAll(list1
);
System
.out
.println(list
.size());
Object o
= list
.get(0);
System
.out
.println(o
);
int index
= list
.indexOf("456");
System
.out
.println(index
);
Object o1
= list
.remove(1);
System
.out
.println(o1
+" "+list
);
list
.set(1,"cc");
List
<Object> list2
= list
.subList(1, 5);
System
.out
.println(list2
);
}
List遍历,及方法总结
public static void main(String
[] args
) {
ArrayList
<Object> list
= new ArrayList<>();
list
.add("123");
list
.add("456");
list
.add("789");
Iterator
<Object> iterator
= list
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
for (Object obj
:list
){
System
.out
.println(obj
);
}
for (int i
=0;i
<list
.size();i
++){
System
.out
.println(list
.get(i
));
}
}
List的一道面试题
public static void main(String
[] args
) {
List list
=new ArrayList<>();
list
.add(1);
list
.add(2);
list
.add(3);
updatelist(list
);
System
.out
.println(list
);
}
private static void updatelist(List list
) {
list
.remove(2);
}
jdk 1.2的接口
继承了Collection接口
sort方法需要传入一个比较器
定制排序
实际上使用了Arrays的sort方法
public interface List<E> extends Collection<E> {
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
ArrayList源码分析:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
//默认初始容量为10
private static final int DEFAULT_CAPACITY = 10;
//空的ElementData数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于默认大小的空实例。我们从空的ELEMENTDATA中解出这个问题,以了解在添加第一个元素时应该增加多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储ArrayList的元素的数组缓冲区。arraylisis的容量是这个数组缓冲区的长度。当添加第一个元素时,任何带有elementData DBEAULTCAPACITY empty ELEMENTDATE的空ArrayList都将被扩展为默认容量(10)
transient Object[] elementData;
//带有指定长度的数组构造器
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//传入一个Collection类型的构造器
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
//将这个<tt>ArrayList</tt>实例的容量调整为列表的当前大小。集合创建的时候会流出来预留的空间,使用这个方法可以去掉预留空间,节省内存。
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
//设置数组的最大长度为integer的最大值-8,防止造成内存溢出异常
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//数组的扩容问题:>>1意思就是/2
private void grow(int minCapacity) {
// overflow-conscious code
//扩容前数组的长度
int oldCapacity = elementData.length;
//新数组扩容为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果扩容前比扩容后小
if (newCapacity - minCapacity < 0)
//还是原来的数组
newCapacity = minCapacity;
//如果扩容后超出数组的最大值
if (newCapacity - MAX_ARRAY_SIZE > 0)
//数组长度变为数组的最大长度
newCapacity = hugeCapacity(minCapacity);
//将原来的数组在复制进新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
//在列表中指定的位置插入指定的元素。将当前位于该位置的元素(如果有)和任何后续元素右移一位(将一个元素添加到它们的索引中)
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//底层重写了sort方法
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
//记录集合的修改次数
final int expectedModCount = modCount;
//排序
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {//应该是多线程考虑
throw new ConcurrentModificationException();
}
modCount++;
}
LinkedList源码分析
private static class Node<E> {
E item
;
Node
<E> next
;
Node
<E> prev
;
Node(Node
<E> prev
, E element
, Node
<E> next
) {
this.item
= element
;
this.next
= next
;
this.prev
= prev
;
}
}
jdk1.2
实现了List接口,Deque,Cloneable, java.io.Serializable
Deque
线性集合,支持两端插入和移除元素。 名称deque是“双端队列”的缩写
Cloneable
cloneable其实就是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功,如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。
java.io.Serializable
支持序列化
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//指向第一个节点的指针
transient Node<E> first;
//指向最后一个节点的指针
transient Node<E> last;
//LinkedList的数据结构就是双向链表
private static class Node<E> {
E item;//数据元素
Node<E> next;//后继节点
Node<E> prev;//前驱节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
//构造器
transient int size = 0;//数据个数
transient Node<E> first;//表示链表的第一个节点
transient Node<E> last;//表示链表的最后一个节点
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {//用于整合Collection类型的数据
this();
addAll(c);
}
//add:
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {//采用的是尾插法
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);//新节点的前驱指向last的地址,后继为null,
//所以说这是一个双向链表,但不是循环的,循环的话,后继指向头节点
last = newNode;//让last指向新节点,也就说这个新节点是链表的最后一个元素
if (l == null)//当第一次添加时,first,last都是null,如果last是null,表明这是一个空链表
first = newNode;//就让新节点指向first,现在first和last都是同一个节点
else
l.next = newNode;//当在添加数据时,就让老链表的最后一个节点的后继指向新节点(那个节点本来是null的)
size++; //长度加1
modCount++;
/**
总结:
新建一个节点,让新节点的前驱指向老链表的最后一个节点
让老链表的最后一个节点的后继指向新节点
让新节点变成链表的最后一个节点
长度加1
第一个节点前驱为null,最后一个节点后继为null
*/
}
//get
public E get(int index) {
checkElementIndex(index);//检查一下索引是否在0到size的范围内
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {//看看索引的位置是在链表的前半部分还是后半部分,决定正着搜索或倒着搜索,找到后返回就行啦
Node<E> x = first;
for (int i = 0; i < index; i++)//在这里看到链表是从0开始的
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
//remove
public E remove(int index) {
checkElementIndex(index);//先检查一下索引
return unlink(node(index));
}
//先拿着索引找到这个节点
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;//节点的元素
final Node<E> next = x.next;//节点的后继
final Node<E> prev = x.prev;//节点的前驱
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
Vector源码分析
先创建初始长度为十的数组,扩容默认为原来的二倍,线程安全的。
Vector 是矢量队列,底层是数组。它是JDK1.0版本添加的类。继承于AbstractList,实现了List, RandomAccess, Cloneable
Vector 继承了AbstractList,实现了List;所以,它是一个队列,支持相关的添加、删除、修改、遍历等功能。 Vector 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在Vector中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。 Vector 实现了Cloneable接口,即实现clone()函数。它能被克隆。 Vector中的操作是线程安全的。因为Vector的方法前加了synchronized 关键字,所以效率不高。
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//向量的分量所在的数组缓冲区存储。向量的容量是这个数组缓冲区的长度,并且至少大到可以包含向量的所有元素。
protected Object[] elementData;
//这个对象有效组件的数量
protected int elementCount;
//当向量的大小大于其容量时,该向量的容量自动增加的量。如果容量增量小于或等于0,则每次需要增长时,向量的容量将增加一倍。
protected int capacityIncrement;
//用指定的初始容量和容量增量构造一个空向量。@paraminitialCapacity向量的初始容量@paramcapacitylncrement容量所占的量当向量溢出@抛出illeqalarqumentexceptionifspecifiedinitialcapacity时增加是负的
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//默认增量为0
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//默认长度为10
public Vector() {
this(10);
}
//扩容方法
//@param minCapacity the desired minimum capacity 所需要的最低容量
如果最低容量>0,记录集合又被修改一次
调用ensureCapacityHelper(minCapacity)方法
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
ensureCapacityHelper(minCapacity);
}
}
//接下来,进入ensureCapacityHelper(minCapacity)方法
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
如果指定的扩容后长度比现在的容量大,说明扩容是合法的
调用grow(minCapacity)方法
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//继续点击,进入grow(minCapacity)
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//用来记录原长度
//新的长度的计算:如果增长的长度大于0就扩容为原来的长度+新增的长度,否则扩容为原来的2倍
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
//如果闲的容量比所需要的最低容量小,新的长度就等于所需的最低容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//如果超出最大临界值,就让新数组长度等于最大临界值
newCapacity = hugeCapacity(minCapacity);
//将原来的元素复制进来
elementData = Arrays.copyOf(elementData, newCapacity);
}
3)set
public interface Spliterator<T> {
Spliterator(splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比最早Java提供的顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历
public interface Set<E> extends Collection<E> {
//证明了set集合不可重复
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.DISTINCT);
}
面试题
public static void main(String
[] args
) {
ArrayList
<String> list
= new ArrayList<>();
list
.add("123");
list
.add("456");
list
.add("789");
list
.add("456");
List list2
=quChong(list
);
Iterator
<String> iterator
= list2
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next
());
}
}
public static List
<String> quChong(List list
){
HashSet
<String> set
= new HashSet<>();
set
.addAll(list
);
return new ArrayList(set
);
}
HashSet
继承了AbstractSet
实现了Set, Cloneable, java.io.Serializable
AbstractSet
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {
里面写了equals hashcode removeAll方法
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
// 底层使用HashMap来保存HashSet中所有元素。
private transient HashMap<E,Object> map;
// 定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。
private static final Object PRESENT = new Object();
//关于为什么不用null而是用一个Object类型的对象,因为map的key本身是可以为null的,二set存储元素成功与否
是需要返回一个true或者false,如果使用null来充当value,你就不知道到底存储成功没
/**
* 默认的无参构造器,构造一个空的HashSet。
*
* 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
*/
public HashSet() {
map = new HashMap<E,Object>();
}
/**
* 构造一个包含指定collection中的元素的新set。
*
* 实际底层使用默认的加载因子0.75和足以包含指定
* collection中所有元素的初始容量来创建一个HashMap。
* @param c 其中的元素将存放在此set中的collection。
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 以指定的initialCapacity和loadFactor构造一个空的HashSet。
*
* 实际底层以相应的参数构造一个空的HashMap。
* @param initialCapacity 初始容量。
* @param loadFactor 加载因子。
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
/**
* 以指定的initialCapacity构造一个空的HashSet。
*
* 实际底层以相应的参数及加载因子loadFactor为0.75构造一个空的HashMap。
* @param initialCapacity 初始容量。
*/
public HashSet(int initialCapacity) {
map = new HashMap<E,Object>(initialCapacity);
}
/**
* 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。
* 此构造函数为包访问权限,不对外公开,实际只是是对LinkedHashSet的支持。
*
* 实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。
* @param initialCapacity 初始容量。
* @param loadFactor 加载因子。
* @param dummy 标记。
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}
/**
* 返回对此set中元素进行迭代的迭代器。返回元素的顺序并不是特定的。
*
* 底层实际调用底层HashMap的keySet来返回所有的key。
* 可见HashSet中的元素,只是存放在了底层HashMap的key上,
* value使用一个static final的Object对象标识。
* @return 对此set中元素进行迭代的Iterator。
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
/**
* 返回此set中的元素的数量(set的容量)。
*
* 底层实际调用HashMap的size()方法返回Entry的数量,就得到该Set中元素的个数。
* @return 此set中的元素的数量(set的容量)。
*/
public int size() {
return map.size();
}
/**
* 如果此set不包含任何元素,则返回true。
*
* 底层实际调用HashMap的isEmpty()判断该HashSet是否为空。
* @return 如果此set不包含任何元素,则返回true。
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* 如果此set包含指定元素,则返回true。
* 更确切地讲,当且仅当此set包含一个满足(o==null ? e==null : o.equals(e))
* 的e元素时,返回true。
*
* 底层实际调用HashMap的containsKey判断是否包含指定key。
* @param o 在此set中的存在已得到测试的元素。
* @return 如果此set包含指定元素,则返回true。
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
/**
* 如果此set中尚未包含指定元素,则添加指定元素。
* 更确切地讲,如果此 set 没有包含满足(e==null ? e2==null : e.equals(e2))
* 的元素e2,则向此set 添加指定的元素e。
* 如果此set已包含该元素,则该调用不更改set并返回false。
*
* 底层实际将将该元素作为key放入HashMap。
* 由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key
* 与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true),
* 新添加的Entry的value会将覆盖原来Entry的value,但key不会有任何改变,
* 因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中,
* 原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。
* @param e 将添加到此set中的元素。
* @return 如果此set尚未包含指定元素,则返回true。
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* 如果指定元素存在于此set中,则将其移除。
* 更确切地讲,如果此set包含一个满足(o==null ? e==null : o.equals(e))的元素e,
* 则将其移除。如果此set已包含该元素,则返回true
* (或者:如果此set因调用而发生更改,则返回true)。(一旦调用返回,则此set不再包含该元素)。
*
* 底层实际调用HashMap的remove方法删除指定Entry。
* @param o 如果存在于此set中则需要将其移除的对象。
* @return 如果set包含指定元素,则返回true。
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
/**
* 从此set中移除所有元素。此调用返回后,该set将为空。
*
* 底层实际调用HashMap的clear方法清空Entry中所有元素。
*/
public void clear() {
map.clear();
}
/**
* 返回此HashSet实例的浅表副本:并没有复制这些元素本身。
*
* 底层实际调用HashMap的clone()方法,获取HashMap的浅表副本,并设置到HashSet中。
*/
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
}
LinkedHashSet
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable {
LinkHashSet底层只是单纯的继承了HashSet并没啥太大改变
TreeSet的使用
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
/**
* 可以排序的map.
*/
private transient NavigableMap<E,Object> m;
// 用来存入map的value的
private static final Object PRESENT = new Object();
/**
* 构造一个指定map集合的TreeSet
*/
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
//自然排序
构造一个新的空树集,根据其元素的自然顺序排序。所有插入到集合中的元素必须实现{@link comparable}接口。此外,所有这些元素都必须<我>相互可比< / ix: f@code e1.compareTo (e2)}不能抛出一个f@code ClassCastException} {@code el}对任何元素集和f@code e2}。如果用户试图添加一个字符串进入Integer类型的Set{@code添加}调用将抛出一个
public TreeSet() {
this(new TreeMap<E,Object>());
}
/**
定制排序
* Constructs a new, empty tree set, sorted according to the specified
* comparator. All elements inserted into the set must be <i>mutually
* comparable</i> by the specified comparator: {@code comparator.compare(e1,
* e2)} must not throw a {@code ClassCastException} for any elements
* {@code e1} and {@code e2} in the set. If the user attempts to add
* an element to the set that violates this constraint, the
* {@code add} call will throw a {@code ClassCastException}.
*
* @param comparator the comparator that will be used to order this set.
* If {@code null}, the {@linkplain Comparable natural
* ordering} of the elements will be used.
*/
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
/**
* Constructs a new tree set containing the elements in the specified
* collection, sorted according to the <i>natural ordering</i> of its
* elements. All elements inserted into the set must implement the
* {@link Comparable} interface. Furthermore, all such elements must be
* <i>mutually comparable</i>: {@code e1.compareTo(e2)} must not throw a
* {@code ClassCastException} for any elements {@code e1} and
* {@code e2} in the set.
*
* @param c collection whose elements will comprise the new set
* @throws ClassCastException if the elements in {@code c} are
* not {@link Comparable}, or are not mutually comparable
* @throws NullPointerException if the specified collection is null
*/
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* Constructs a new tree set containing the same elements and
* using the same ordering as the specified sorted set.
*
* @param s sorted set whose elements will comprise the new set
* @throws NullPointerException if the specified sorted set is null
*/
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
/**
* Returns an iterator over the elements in this set in ascending order.
*以升序返回此集合中元素的迭代器。
* @return an iterator over the elements in this set in ascending order
*/
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
/**
* Returns an iterator over the elements in this set in descending order.
*按降序返回该集合中元素的迭代器。
* @return an iterator over the elements in this set in descending order
* @since 1.6
*/
public Iterator<E> descendingIterator() {
return m.descendingKeySet().iterator();
}
/**
* @since 1.6
*/
public NavigableSet<E> descendingSet() {
return new TreeSet<>(m.descendingMap());
}
public int size() {
return m.size();
}
public boolean isEmpty() {
return m.isEmpty();
}
public boolean contains(Object o) {
return m.containsKey(o);
}
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element {@code e} to this set if
* the set contains no element {@code e2} such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns {@code false}.
*大概意思就是先根据hash值比交,不相同直接添加成功,
hashcode相同在判断是不是null然后调用equals方法进行比较
equals方法返回true,则添加失败,否则添加成功。
* @param e element to be added to this set
* @return {@code true} if this set did not already contain the specified
* element
* @throws ClassCastException if the specified object cannot be compared
* with the elements currently in this set
* @throws NullPointerException if the specified element is null
* and this set uses natural ordering, or its comparator
* does not permit null elements
*/
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
如果指定的元素存在,则从该集合中移除它。更正式的说法是,删除元素f@code e}< (ttx - o = null ? e = -null  ;o.equals (e)) / tt >、<如果这个集合包含这样一个元素。如果该集合包含元素,则返回{@code true}(或者,如果该集合由于调用而改变,则返回相等的结果)。(一旦调用返回,这个集合将不包含元素。)
* @param o object to be removed from this set, if present
* @return {@code true} if this set contained the specified element
* @throws ClassCastException if the specified object cannot be compared
* with the elements currently in this set
* @throws NullPointerException if the specified element is null
* and this set uses natural ordering, or its comparator
* does not permit null elements
*/
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
/**
* Removes all of the elements from this set.
* The set will be empty after this call returns.
*/
public void clear() {
m.clear();
}
/**
* Adds all of the elements in the specified collection to this set.
*
* @param c collection containing elements to be added to this set
* @return {@code true} if this set changed as a result of the call
* @throws ClassCastException if the elements provided cannot be compared
* with the elements currently in the set
* @throws NullPointerException if the specified collection is null or
* if any element is null and this set uses natural ordering, or
* its comparator does not permit null elements
*/
public boolean addAll(Collection<? extends E> c) {
// Use linear-time version if applicable
if (m.size()==0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap) {
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<?> cc = set.comparator();
Comparator<? super E> mc = map.comparator();
if (cc==mc || (cc != null && cc.equals(mc))) {
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
return super.addAll(c);
}
①向TreeSet中添加的数据,要求是同一个类的对象,不能添加不同类的对象。②两种排序方式:自然排序和定制排序③自然排序中,比较两个对象是否相同的标准:compareTo()返回0,不再是equals();
public static void main(String
[] args
) {
TreeSet
<Object> treeSet
= new TreeSet<>();
treeSet
.add(123);
treeSet
.add(456);
treeSet
.add(789);
treeSet
.add(456);
System
.out
.println(treeSet
);
System
.out
.println("*****************************************");
TreeSet
<Object> set
= new TreeSet<>();
set
.add(new User("Tom",22));
set
.add(new User("Jerry",24));
set
.add(new User("BeiBei",21));
set
.add(new User("DongDong",20));
set
.add(new User("MM",18));
Iterator
<Object> iterator
= set
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
}
}
class User implements Comparable{
private String name
;
private int age
;
public User(String name
, int age
) {
this.name
= name
;
this.age
= age
;
}
public User() {
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public int getAge() {
return age
;
}
public void setAge(int age
) {
this.age
= age
;
}
@Override
public String
toString() {
return "User{" +
"name='" + name
+ '\'' +
", age=" + age
+
'}';
}
@Override
public int compareTo(Object o
) {
if (o
instanceof User){
User user
= (User
) o
;
int num
= this.name
.compareTo(user
.name
);
if (num
!=0){
return -num
;
}else{
return Integer
.compare(this.age
,user
.age
);
}
}else{
throw new RuntimeException("类型不一致!");
}
}
}
TreeSet定制排序
①new一个Comparator对象,重写compare方法②将Comparator对象传入TreeSet的构造器③添加对象时就会按照compare方法进行比较
public static void main(String
[] args
) {
Comparator comparator
= new Comparator(){
@Override
public int compare(Object o1
, Object o2
) {
if (o1
instanceof Dog&& o2
instanceof Dog){
Dog d1
= (Dog
) o1
;
Dog d2
= (Dog
) o2
;
return Integer
.compare(d1
.getAge(),d2
.getAge());
}else{
throw new RuntimeException("类型不一致!");
}
}
};
TreeSet
<Object> set
= new TreeSet<>(comparator
);
set
.add(new Dog("Tom",22));
set
.add(new Dog("Jerry",22));
set
.add(new Dog("BeiBei",21));
set
.add(new Dog("DongDong",20));
set
.add(new Dog("MM",18));
set
.add(123);
Iterator
<Object> iterator
= set
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
}
}
class Dog{
private String name
;
private int age
;
public Dog() {
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public int getAge() {
return age
;
}
public void setAge(int age
) {
this.age
= age
;
}
public Dog(String name
, int age
) {
this.name
= name
;
this.age
= age
;
}
@Override
public String
toString() {
return "Dog{" +
"name='" + name
+ '\'' +
", age=" + age
+
'}';
}
}
2.map接口
map底层源码分析
支持自然排序和定制排序
public interface Map<K,V> {
//里面定义了一个Entry接口
interface Entry<K,V> {
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
}
HashMap
底层源码分析
/**
* 以jdk7为例说明:
* HashMap<Object, Object> map = new HashMap<>();//实例化以后,底层创建了长度为16的一维数组Entry[]table。
* ....已经执行过多次put操作。。。。
* map.put(1,666);//首先,计算key1的hash值,此hash值经过某种算法计算,得到在entry数组的存放位置。
* 如果此位置的数据为空,此时的key1添加成功(成功一);如果此位置的数据不为空(意味着此位置存在一个或者多个数据),
* 比较key1和已经存在的一个或多个数据的哈希值,如果key1的哈希值与已经存在的都不相同,此时添加成功(成功二)。
* 如果如果key1的哈希值与已经存在的某个数据(key2-value2)的哈希值相同,继续比较,
* 调用key1所在类的equals()方法,比较:
* 如果equals()返回false:添加成功(成功三);如果返回true:使用value1替换value2.
* 关于成功二和成功三:
* 此时key1value1和原来的数据一链表的方式存储。
* 在不断的添加过程中,涉及到扩容问题,当超出临界值,且要存放的位置非空时,默认的扩容方式,扩容为原来容量的2倍,并将原有的数据复制过来。
* 在jdk8中的底层实现:
* jdk8相比于底层实现方面的不同:
* 1.new HashMap();底层没有创建一个长度为16的数组。
* 2.jdk8底层是Node【】,不再是Entry【】。
* 3.首次调用put方法时,底层创建长度为16的数组。
* 4.原来jdk7底层结构只有数组加链表,jdk8又加入了红黑树,当数组某一个索引位置上的元素以链表形式存在的
* 数据个数>8且当前数组长度>64时,此时此索引位置上的所有数据改为使用红黑树存储。
*/
HashMap基于Map接口实现,元素以键值对的方式存储,并且允许使用null 建和null 值, 因为key不允许重复,因此只能有一个键为null,另外HashMap不能保证放入元素的顺序,它是无序的,和放入的顺序并不能相同。HashMap是线程不安全的。
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默认初始化大小 16
static final float DEFAULT_LOAD_FACTOR = 0.75f; //负载因子0.75
static final Entry<?,?>[] EMPTY_TABLE = {}; //初始化的默认数组
transient int size; //HashMap中元素的数量
int threshold; //判断是否需要调整HashMap的容量
//当数组总长度>64,且单个节点的元素大于8个时,该节点的元素使用红黑树存储
当删除该节点元素时,当该节点的元素小于6个,从二叉树变为指针
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
//底层使用指针
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
//HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸。
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1);
//在该方法中,添加键值对时,首先进行table是否初始化的判断,如果没有进行初始化(分配空间,Entry[]数组的长度)。然后进行key是否为null的判断,如果key==null ,放置在Entry[]的0号位置。计算在Entry[]数组的存储位置,判断该位置上是否已有元素,如果已经有元素存在,则遍历该Entry[]数组位置上的单链表。判断key是否存在,如果key已经存在,则用新的value值,替换点旧的value值,并将旧的value值返回。如果key不存在于HashMap中,程序继续向下执行。将key-vlaue, 生成Entry实体,添加到HashMap中的Entry[]数组中。
public V put(K key, V value) {
if (table == EMPTY_TABLE) { //是否初始化
inflateTable(threshold);
}
if (key == null) //放置在0号位置
return putForNullKey(value);
int hash = hash(key); //计算hash值
int i = indexFor(hash, table.length); //计算在Entry[]中的存储位置
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i); //添加到Map中
return null;
}
添加到方法的具体操作,在添加之前先进行容量的判断,如果当前容量达到了阈值,并且需要存储到Entry[]数组中,先进性扩容操作,空充的容量为table长度的2倍。重新计算hash值,和数组存储的位置,扩容后的链表顺序与扩容前的链表顺序相反。然后将新添加的Entry实体存放到当前Entry[]位置链表的头部。在1.8之前,新插入的元素都是放在了链表的头部位置,但是这种操作在高并发的环境下容易导致死锁,所以1.8之后,新插入的元素都放在了链表的尾部。
/*
* hash hash值
* key 键值
* value value值
* bucketIndex Entry[]数组中的存储索引
* /
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length); //扩容操作,将数据元素重新计算位置后放入newTable中,链表的顺序与之前的顺序相反
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
HashMap里面实现一个静态内部类Entry,其重要的属性有 hash,key,value,next。
HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。也就是说数组中存储的是最后插入的元素。
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //参数e, 是Entry.next
//如果size超过threshold,则扩充table大小。再散列
if (size++ >= threshold)
resize(2 * table.length);
}
//添加方法精讲
public V put(K key, V value) {
//调用putVal()方法完成
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//判断table是否初始化,否则初始化操作
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//计算存储的索引位置,如果没有元素,直接赋值
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//节点若已经存在,执行赋值操作
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//判断链表是否是红黑树
else if (p instanceof TreeNode)
//红黑树对象操作
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//为链表,
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//链表长度8,将链表转化为红黑树存储
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//key存在,直接覆盖
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
//记录修改次数
++modCount;
//判断是否需要扩容
if (++size > threshold)
resize();
//空操作
afterNodeInsertion(evict);
return null;
}
LinkedHashMap
在LinkedHashMap中,是通过双联表的结构来维护节点的顺序的。每个节点都进行了双向的连接,维持插入的顺序(默认)。head指向第一个插入的节点,tail指向最后一个节点。
LinkedHashMap是HashMap的亲儿子,直接继承HashMap类。LinkedHashMap中的节点元素为Entry,直接继承HashMap.Node。
在HashMap类的put方法中,新建节点是使用的newNode方法。而在LinkedHashMap没有重写父类的put方法,而是重写了newNode方法来构建自己的节点对象。
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
LinkedHashMap相对于HashMap,增加了双链表的结果(即节点中增加了前后指针),其他处理逻辑与HashMap一致,同样也没有锁保护,多线程使用存在风险。
Map接口中定义的方法1
public static void main(String
[] args
) {
HashMap
<Object, Object> map
= new HashMap<>();
map
.put(1, 666);
map
.putAll(map
);
map
.remove(1);
map
.clear();
map
.size();
map
.get(1);
map
.containsKey(1);
map
.containsValue(666);
map
.isEmpty();
map
.equals(map
);
Map中的常用方法二
public static void main(String
[] args
) {
HashMap
<Object, Object> map
= new HashMap<>();
map
.put(1,666);
map
.put(2,888);
map
.put(3,555);
Set
<Object> set
= map
.keySet();
Iterator
<Object> iterator
= set
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
Collection
<Object> collection
= map
.values();
Iterator
<Object> iterator1
= collection
.iterator();
while (iterator1
.hasNext()){
System
.out
.println(iterator1
.next());
}
Set
<Map
.Entry
<Object, Object>> entries
= map
.entrySet();
Iterator
<Map
.Entry
<Object, Object>> iterator2
= entries
.iterator();
while (iterator2
.hasNext()){
System
.out
.println(iterator2
.next());
}
}
HashTable
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
属性:
table:为一个Entry[]数组类型,Entry代表了“拉链”的节点,每一个Entry代表了一个键值对,哈希表的"key-value键值对"都是存储在Entry数组中的。
count:HashTable的大小,注意这个大小并不是HashTable的容器大小,而是他所包含Entry键值对的数量。
threshold:Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。
loadFactor:加载因子。
modCount:用来实现“fail-fast”机制的(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,并且立即抛出ConcurrentModificationException异常,而不是等到迭代完成之后才告诉你(你已经出错了)
构造方法:
1.默认构造函数,容量为11,加载因子为0.75:
public Hashtable() {
this(11, 0.75f);
}
2.
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
3.
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
重要方法
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;//计算出索引
@SuppressWarnings("unchecked")
//遍历该数组
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
//扩容
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;2倍+1
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
TreeMap
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
public Comparator<? super K> comparator() {
return comparator;
}
TreeMap两种添加方式的使用:
向treemap中添加数据,要求key必须是同一个类创建的对象,因为要按照类进行排序。
①自然排序
public static void main(String
[] args
) {
TreeMap
<Object, Object> map
= new TreeMap<>();
map
.put(new User("尹会东",23),"6666");
map
.put(new User("张贝贝",25),"6666");
map
.put(new User("刘淼",23),"6666");
Set
<Map
.Entry
<Object, Object>> set
= map
.entrySet();
Iterator
<Map
.Entry
<Object, Object>> iterator
= set
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
}
}
class User implements Comparable{
private String name
;
private int age
;
public User(String name
, int age
) {
this.name
= name
;
this.age
= age
;
}
public User() {
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public int getAge() {
return age
;
}
public void setAge(int age
) {
this.age
= age
;
}
@Override
public String
toString() {
return "User{" +
"name='" + name
+ '\'' +
", age=" + age
+
'}';
}
@Override
public int compareTo(Object o
) {
if (o
instanceof User){
User user
= (User
) o
;
int num
= this.name
.compareTo(user
.name
);
if (num
!=0){
return -num
;
}else{
return Integer
.compare(this.age
,user
.age
);
}
}else{
throw new RuntimeException("类型不一致!");
}
}
}
②定制排序
public static void main(String
[] args
) {
Comparator comparator
= new Comparator() {
@Override
public int compare(Object o1
, Object o2
) {
if (o1
instanceof Dog && o2
instanceof Dog){
Dog d1
= (Dog
) o1
;
Dog d2
= (Dog
) o2
;
return Integer
.compare(d1
.getAge(),d2
.getAge());
}
throw new RuntimeException("类型不一致!");
}
};
TreeMap
<Object, Object> map
= new TreeMap<>(comparator
);
map
.put(new Dog("尹会东",23),"6666");
map
.put(new Dog("张贝贝",25),"6666");
map
.put(new Dog("45646",56),"888");
Set
<Map
.Entry
<Object, Object>> set
= map
.entrySet();
Iterator
<Map
.Entry
<Object, Object>> iterator
= set
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
}
}
class Dog{
private String name
;
private int age
;
public Dog() {
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public int getAge() {
return age
;
}
public void setAge(int age
) {
this.age
= age
;
}
public Dog(String name
, int age
) {
this.name
= name
;
this.age
= age
;
}
@Override
public String
toString() {
return "Dog{" +
"name='" + name
+ '\'' +
", age=" + age
+
'}';
}
}
Properties
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
public synchronized void load(Reader reader) throws IOException {
load0(new LineReader(reader));
}
private void load0 (LineReader lr) throws IOException {
char[] convtBuf = new char[1024];
int limit;
int keyLen;
int valueStart;
char c;
boolean hasSep;
boolean precedingBackslash;
while ((limit = lr.readLine()) >= 0) {
c = 0;
keyLen = 0;
valueStart = limit;
hasSep = false;
//System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
precedingBackslash = false;
while (keyLen < limit) {
c = lr.lineBuf[keyLen];
//need check if escaped.
if ((c == '=' || c == ':') && !precedingBackslash) {
valueStart = keyLen + 1;
hasSep = true;
break;
} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
keyLen++;
}
while (valueStart < limit) {
c = lr.lineBuf[valueStart];
if (c != ' ' && c != '\t' && c != '\f') {
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
valueStart++;
}
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
put(key, value);
}
}
Properties处理配置文件
public static void main(String
[] args
) {
Properties prop
=new Properties();
FileInputStream fileInputStream
=null
;
try {
fileInputStream
= new FileInputStream("jdbc.properties");
prop
.load(fileInputStream
);
String name
=prop
.getProperty("user");
System
.out
.println(name
);
} catch (IOException e
) {
e
.printStackTrace();
}finally {
try {
if (fileInputStream
!=null
){
fileInputStream
.close();
}
} catch (IOException e
) {
e
.printStackTrace();
}
}
}
3.collections工具类
Collections工具类:操作set,map,list的工具类
面试题:Collection和Collections的区别:
public static void main(String
[] args
) {
ArrayList
<Object> list
= new ArrayList<>();
list
.add(new Integer(1));
list
.add(new Integer(2));
list
.add(new Integer(3));
Collections
.reverse(list
);
Collections
.shuffle(list
);
Collections
.swap(list
,1,2);
Collections
.frequency(list
,1);
List
<Object> list1
= Collections
.synchronizedList(list
);
}
十,泛型
jdk1.5新特性泛型 把元素的类型设计成一个参数,这个参数类型叫做泛型。 为什么要使用泛型? 1.类型无限制,类型不安全。 2.类型强制转换时,容易出现异常。 ClassCastException
集合中使用泛型
集合接口或类在jdk5.0都修改为带泛型的结构。
在实例化集合类时,可以指明泛型的类型。
指明完以后,在集合类或接口中,凡是定义接口或类时,内部结构使用到类的泛型位置,都指定为实例化的泛型。
泛型的类型必须是一个类。,使用基本数据类型时,需要转换为包装类。
如果实例化时未指明泛型,默认为Object类型。
public static void main(String
[] args
) {
ArrayList
<Integer> list
= new ArrayList<>();
list
.add(99);
list
.add(100);
list
.add(88);
for (Integer integer
:list
){
System
.out
.print(integer
+" ");
}
System
.out
.println();
System
.out
.println("-------------------------------------------------------");
Iterator
<Integer> iterator
= list
.iterator();
while (iterator
.hasNext()){
System
.out
.print(iterator
.next()+" ");
}
System
.out
.println("-------------------------------------------------------");
HashMap
<String, Integer> map
= new HashMap<>();
map
.put("第一个",123);
map
.put("第二个",456);
map
.put("第个",789);
Set
<Map
.Entry
<String, Integer>> set
= map
.entrySet();
Iterator
<Map
.Entry
<String, Integer>> iterator1
= set
.iterator();
while (iterator1
.hasNext()){
System
.out
.print(iterator1
.next()+" ");
}
System
.out
.println("-------------------------------------------------------------");
}
自定义泛型结构:泛型类,接口,方法
public class Test1 {
@Test
public void test1() {
Student
<String> student
= new Student<>("张三", "优秀");
Student
<Double> student1
= new Student<>("李四", 90.5);
Student
<Character> student2
= new Student<>("王五", 'A');
}
@Test
public void test2() {
Student2 student
= new Student2("zhangsan", "优秀");
}
@Test
public void test3(){
Integer
[] arr
= new Integer[4];
arr
[0]=1;
arr
[1]=2;
arr
[2]=3;
arr
[3]=4;
Student2 student
= new Student2();
List
<Integer> list
= student
.copy(arr
);
for (Integer i
:list
){
System
.out
.println(i
);
}
}
}
class Student<T> {
private String name
;
private T score
;
public Student() {
}
public Student(String name
, T score
) {
this.name
= name
;
this.score
= score
;
}
public String
getName() {
return name
;
}
public void setName(String name
) {
this.name
= name
;
}
public T
getScore() {
return score
;
}
public void setScore(T score
) {
this.score
= score
;
}
@Override
public String
toString() {
return "Student{" +
"name='" + name
+ '\'' +
", score=" + score
+
'}';
}
}
class Student2 extends Student<String> {
public Student2() {
}
public Student2(String name
, String score
) {
super(name
, score
);
}
public <E> List
<E> copy(E
[] arr
) {
ArrayList
<E> list
= new ArrayList<>();
for (E e
: arr
) {
list
.add(e
);
}
return list
;
}
}
通配符的使用
类A是类B的父类,G和G是没有关系的,二者共有的父类是G<?>。
public class Test2 {
@Test
public void test1(){
List
<Object> list1
=null
;
List
<Integer> list2
=null
;
}
public void show(List
<?> list
){
list
.add(null
);
Iterator
<?> iterator
= list
.iterator();
while (iterator
.hasNext()){
System
.out
.println(iterator
.next());
}
Object o
= list
.get(0);
}
@Test
public void test2(){
ArrayList
<Integer> list
= new ArrayList<>();
list
.add(1);
list
.add(2);
list
.add(3);
show(list
);
}
}