javaSE基础

javaSe 整体大纲

  1. java基础 (循环,数组)
  2. OOP
  3. API

基础

  • 编程的本质=数据结构+算法
  • 什么是编程:就是让计算机按自己的意图去工作

编程语言的发展史(了解)

  1. 机器语言:0101
  2. 汇编语言: 助记符
  3. 高级语言:java,c,c++,python,basic

为什么是java

流行的语言:

  • java,python,c,C++,javascript,go…..
  • 语言没有好坏之分,只有适不适用?

厨房中的刀,哪把刀更好:砍刀,切菜刀,西瓜刀,水果刀?

  • 每个语言都有适用的场景
  • 数据分析:python,
  • 3D游戏:C++
  • 前端页面:javascript
  • 应用程序的后台:java

回顾

1.学习方法:编码-》巩固-》检索 3F:focus->feedback->fix
2.课程 se:1.基础 2.oop,3API
3.编程的基础: 数据结构+算法=程序
4.编程语言的发展史

  1. 机器语言
  2. 汇编语言
  3. 高级语言

5.java

  • 企业后台
  • 安卓
  • 大数据
  • 桌面

6.java语言的发展史

java语言的发展

高斯林(高司令):嵌入式程序

99年,三个版本:javase,javame,javaee

  • LTS
  • JDK8
  • JDK11
  • JDK17

java 语言的特点

  • 面向对象
  • 简单
  • 跨平台

java语言的三个版本

  • A:javaSE:标准版本:基础,桌面级应用
  • B:javaEE:企业版:服务器端的大网络,分布式
  • C:javaME:微型版本:嵌入式设备,小设备

环境安装

JDK

java development kid:java开发包

作用:开发,运行java程序

JRE:java runtime environmentjava 运行环境

作用:运行java程序

JDK包含了JRE

JVM:java virtual machine:java虚拟机

作用:用来运行java程序 不包含类库和命令

JDK中包含了JRE,JRE中包含了JVM

安装JDK

建议:安装 11,8,

安装注意事项

  1. 傻瓜式安装,按步骤来就行
  2. 安装路径不要有空格,不要使用中文路径
  3. 安装后会有两个目录
    • bin:包含了java命令:javac(编译),java(运行),javadoc(生成文档)
    • lib:包含了java类库

配置:

配置环境变量?

DOS命令

DOS:disk operation system:命令行

命令:

  • cd:改变目录

  • dir:查看当前目录下的内容

  • 盘符:切换到其它盘,例如 D:

  • d:切换到D盘

  • cd ..:切换到上层目录

  • cd bin:进入取bin子目录

  • dir:显示当前目录中的内容

    问题?
    java.exe,javac.exe在 d:/java/jdk-11.0.8/bin下,如果想使用java,javac命令,必须先进入到d:/java/jdk-11.0.8/bin下才可以使用,如果希望在任何一个目录下都使用此命令,则需要配置环境变量才可以

    环境变量中有一个叫path的
    path的作用是,当运行一个命令时,先从当前路径找,如找不到,再去path中找

    如果在path中设置一个值 d:/java/jdk-11.0.8/bin
    当我们在 e:盘下,运行 java

  • 1.先在 e:/下找java

  • 2.如找不到,再到path下找

环境变量
path:路径
classpath:类路径 .d:/java/jdk-11.0.8/lib

window: 图形界面

android:图形

linux:有图形

第一个java程序

1
2
3
4
5
public class HelloWorld{	
public static void main(String[] args){
System.out.println("这是我的第一个java程序,我好高兴呀,今天晚上给自己加一个鸡腿吧");
}
}
  1. HelloWorld:是类名,类名必须要与文件名相同 (HelloWorld.java
  2. System.out.println(“”):代表输出 “”中可以写任何字符串
  3. 所有的标点符号都是英文半角
  4. java是区分大小写的

HelloWorld详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Hello{
public static void main(String[] args){

System.out.println();
}
}
//Hello是类名
//public static void main(String[] args):是主函数,程序的入口,如果想直接运行此类,必须有此方法
//System.out.println();输出内容后换行
//System.out.print();输出内容后不换行
//无论是print还是println都只能输出一个内容,内容可以是字符串,数字,布尔等等

//9+9
//"hello"+"hello"+9

java命令

javac:编译
java:运行
javadoc:生成文档

javac语法

javac 文件名.java
javac HelloWorld.java
作用:将.java的源代码编译成.class文件,如果代码有错误,将无法完成编译

java 语法

java 类名
java HelloWorld
注意:千万不要写.class
作用:运行java类,要求此类必须有主函数

javadoc:生成文档
javadoc 类名.java

注释

作用:为了让程序员更容易理解代码的含义,可以使用注释加以说明 注释不会影响代码的运行

注释有三种

//:单行
/* /:块注释
/
* */:文档注释

java编程基础

关键字

  • 什么是关键字:在java语言有,有特殊含义的一些单词就是关键字

  • 关键字的特点:全部都有小写

标识符

  • 什么标识符:用来标识某种东西的符号就是标识符

比如:类,方法名,变量名,参数名,

定义标识符的规则

  • 标识符中可包含字母,数字,_,$,中文也可以(不建议)

  • 不能以数字开头

  • 不能是关键字

  • 长度无限制,最好做到“见名知义”

变量

变量:就是内存中的一个区域,为了便于使用,需要给变量起个名字

变量用法:

  1. 变量一定要先定义,再使用

  2. 定义变量的格式是

    数据类型 变量名=初始值

  3. 变量的值是可以改变的

  4. 在一个作用域中,变量名是不能重复的

数据类型

java的数据类型分为两大类,分别是

  • 基本数据类型
  • 引用数据类型
    • 类,接口,数组

基本数据类型

  • 整数

    • byte :1
    • short:1
    • int :2
    • long:2
  • 浮点数

    • float:45.6f
    • double:444.4
  • 字符

    • char ‘a’
  • 布尔

    • boolean :true,false

整数:

  • byte:1个字节

    • 位(bit)

      01:bit,每个一个bit只能存储两个值

      一个字节等于8位二进制

      10101011

      byte = -128~127;

  • short:2个字节

  • int:4个字节

  • long:8个字节

关于容量的换算关系

字节 B byte

1KB->1024B

1MB->1024KB

1GB->1024MB

1TB->1024GB

1PB->1024TB

进制的转换

1.10进制-》二进制
除2取余法

2.二进制-》十进制
乘权相加法
110101
1+4+16+32=53
3.二进制-》十六进制
1011 1010
B A
100011111
4.十六进制-》二进制
6E-》

字面值(literal)

字面值也叫直接量

1.整数的字面值的类型默认为是int
2.十六进制 0x开头
3.八进制以0开头,例如 int a=011
4.如果想表示一个long值,可以使用 L结尾,例如 long a=12312312399L

浮点型

字节 范围
float 4 -3.403E38 ~ 3.403E38
double 8 -1.798E308 ~ 1.798E308

总结
1.浮点数只能表示出一个近似数,而不能表示出一个准确的数
2.浮点数的字值的类型是double,如果想表示一个float,应该使用 F结尾

字符型

用 一对‘’引起的单个字符就是字符,字符可以以数字,英文字母,标点符号,还可以表示任何语言的单字符 例如,中文,法文,俄文,日文

每个字符存储时,都会转换成与之对应的数字
字符与数字之间的对应关系就是一种字符集 UTF-8 GBK,GB2312
char有三种表示方式
1.‘’
2.‘\转义字符’ ,例如 ‘\n’:换行
3.unicode码 ‘\uXXXX’

boolean

boolean叫布尔
只有两个值,分别是true,false
通常来说,可以使用boolean来存储一个状态

是否打开音乐
boolean isMusic=false

String类型

String代表字符串,即用双引号引起的多个字符序列
String不是基本数据类型,而是一种引用类型

String name=”刘备”

java代码三级跳

1.表达式
类似于语文中的汉字,词组,例如 a+b,c*d
2.语句
类似于语文中的句子。以 号结束 一个语句中会包含多个表达式,

​ int a=5;
​ int b=a+b;
​ c=sum+a;
3.代码段
​ 相当于语文的作文,一个代码段中包含多个语句

数据类型的转换

两种转换
1.自动转换:低精度向高精度可以自动转换,安全
2.强制转换:高精度向低精度转换时可以采用强制转换,强转可能会造成精度丢失

数值类的的精度
byte->short->int->long->float->double
char

自动类型转换发生在两种情况

  • 赋值
  • 运算

强制类型转换

  • 高精度向低精度转换时,可使用强转
  • 强转的语法是 (数据类型) 例如 long b=6677; int a=(int)b;

Scanner

//有交互的程序
交互的方式主要用两种
1.命令行
2.图形
Scanner叫扫描器,他可以接收用户输入的数据 例如数字,字符串,布尔值等

使用Scanner的步骤
1.导包 import java.util.*
2.创建Scanner对象
Scanner s=new Scanner(System.in)
3.接收数据
int a=s.nextInt()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.*;  //1.导包

public class Circle
{
public static void main(String[] args)
{
//2.创建一个扫描器
Scanner scanner=new Scanner(System.in);

System.out.println("请输入半径");
//3.接收一个半径
double r=scanner.nextDouble();

double area=r*r*3.14;

System.out.println("圆的面积是:"+area);
}
}

运算符

运算符可以有两种分在方式

  1. 功能
    • 算术运算符
    • 赋值运算符
    • 比较运算符
    • 位运算符
    • 逻辑运算符
    • 条件(三元)运算符
  2. 操作数的个数
    • 一元操作符: ++b,
    • 二元操作符: a+b
    • 三元操作符 a>b?a:b

算术运算符

1
2
3
4
5
6
7
8
9
10
11
++,--
可以放在操作数之前,也可以放在操作数之后

a++操作符有作用
1.对a做一个加操作
2.a++也是一个表达式,即将表达式的结果赋给一个变量
b=a++;
b=++a;

a++;
++a;

赋值运算符

1
2
3
4
5
=
例如: a=9+8;
特点:
1.操作符的结合性是从右到左
2.操作符左侧一定是一个变量

比较运算符

1
2
3
4
5
6
7
8
9
10
11
12
==  
!=
<
=
<=
instanceof

所有比较运算符的计算结果的类型都是boolean; true,false;

注意,
=是赋值
==是比较

逻辑运算符

1
2
3
4
5
6
与: & ,&&
或: | ||
非: !
异或: ^

与的语法
1
2
表达式1  & 表达式2  
表达式1 && 表达式2
1
2
3
4
5
6
7
8
9
10
11
12
13
 要求,两个表达式都是布尔表达式  
即,两个表达式的结果同时为true时,与后的结果才true;

或: | ||
作用:两个操作数的结果,只要有一个为true,那么或的结果就为true;
非:
取反
!true;=false;
!false:=true;

异或:
作用:两个操作数不同时为true,否则为false;

1
2
3
4
5
6
7
8
9
&,&&的区别?
&,&&都是逻辑与,计算的结果是相同的 但运算的过程是有区别的?
例如
a&&b;
假如,a的结果是false;那么是否需要计算b,才知道整个表达式的结果

区别:
&:无论第一个操作数结果是什么,都会计算所有表达式
&&:如果第一个操作数的结果是false,那么将不会计算第二个表达式

位运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
操作对象:整数(byte,short,int,long
作用:将整数转换成二进制后,按位进行运算 常用的操作有,位移,按位与,或,异或
位运算的优点:效率高

:按位右移
<<:按位左移

:无符号右移
&:按位与
|:按位或
^:按位异或

895;

条件运算符

1
2
3
语法
布尔表达式?表达式1:表达式2
int c=a>b?a:b;

Java进阶

流程控制

三种
顺序结构
分支(选择)结构:branch
循环结构:loop

顺序:
程序从上到下,依次执行

分支语句

分支语句分为if,switch

if语句

单分支

语法

1
2
3
4
if(布尔表达式){
语句块
}
。。

适用场景:
为程序增加一个可能执行的代码,此代码有可能执行,有可能不执行

双分支

1
2
3
4
5
if(布尔表达式){
语句块1
}else{
语句块2
}

多分支

语法

1
2
3
4
5
6
7
8
9
if(布尔表达式){

}else if(布尔表达式2){

}else if(布尔表达式3.。。。){

}else{

}

关于if的小结
1.完整的if语句中,包含 if-else if-else
2.if语法中,if是必须存在的, else if可有0到n个,可以有0到1个else
3.如果存在else,else一定放在最后
4.多分支语句中,最多只能执行一个分支,执行此分支后,分支语句结束

if的嵌套

在一个分支语句块中包含另一个分支语句
适用场景:适合解决较为复杂的逻辑问题

例如:
登录
1.成功 用户名,密码,验证码
2.失败
1.验证码?
2.用户名,或密码错误,但少于3次
3.用户名,或密码错误,但大于等于3次 锁定(1个小时)

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(条件){
System.out.println();
i++;
if(){

}
}else if(条件){
if(){

}else{

}
}else{

}

if语句的简化

1
2
3
4
5
//如果if语句块中只包含一行代码,那么{}可以省略,(但不建议省略)
if(true)
System.out.println("ok");
else
System.out.println("else");

switch 语句

1
2
3
4
5
6
7
8
9
10
11
//语法
switch(表达式){
case1
语句;
break;
case2
语句;
break;
default:
语句3
}
1
2
//输入一个成绩   5,4,3,2,1,0  

关于break;
在switch语句中,如果case 没有break,那么将会穿透

解决问题
输入月份,判断此月有多少天?
2:28,29
1,3,5,7,8,10,12:31
4,6,9,11:30

case:
2,
4,
6,
9,11

default:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.*;
public class Switch2
{
public static void main(String[] args)
{
Scanner s=new Scanner(System.in);

System.out.println("输入月份");

int month=s.nextInt();

switch(month){
case 2:
System.out.println("28或29天");
break;
case 4:
case 6:
case 9:
case 11:
System.out.println("30天");
break;
default:
System.out.println("31天");
}
}
}

使用switch的注意事项
1.switch只能使用等值比较,而且对数据类型有要求(byte,short,int,char,String)
2.注意 case穿透 (break)
3.switch的结果清晰,但没有if应用的广泛

循环

语法很简单,练习是关键

循环语句的三个部分

1.初始化部分(1次)
2.条件部分(多次)
3.循环体部分 (多次)

java中,循环语句的分类

1.while
2.do-while
3.for

while语句

1
2
3
4
5
6
//语法
[初始化语句]
while(表达式){
循环体语句;
}
...

1+2+3+4…..+100;

int sum=0;//定义一个变量 用来存储累加的和

int i=1;
sum+=i;(1.2,3,4,5,5.6,7)

盈盈为了考验令狐冲夺冠的决心,要他说一百遍“我能行!”;
1+…100;

死循环

死循环:无法结束的循环被称为死循环

变量的作用域

内层可以使用外层的变量,外层不能使用内层的变量

do-while

1
2
3
4
5
6
7
//do-while循环
[初使化变量]
do{
循环体
}while(布尔表达式);

特点:至少进入一次循环体
1
2
3
4
5
6
7
8
9
10
//猜数游戏
Random r=new Random();
int n=r.nextInt(10);//产生一个随机数,(整数)
int i=0;//代表用户猜的那个数值
do{
//猜数
}while(n!=i)

//计算一下,用户用了几次机会

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.*;

public class GuessNumber
{
public static void main(String[] args)
{
Random r=new Random();//创建一个随机数生成器
Scanner s=new Scanner(System.in);//创建一个扫描器,用来接收用户猜的数字

int n=r.nextInt(100)+1;//获得一个随机数

int i=0;//i代表用户每次猜的那个数,如果i==n,说明猜对了

do{
//每次猜数
System.out.println("说出一个数字");
i=s.nextInt();

if(i>n){
System.out.println("高了");
}else if(i<n){
System.out.println("低了");
}
}while(i!=n);

System.out.println("你猜对了,这个数就是:"+n);
}
}

for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//语法

for(初始化变量;条件表达式;循环体的最后一个表达式 ){
循环体
}

//特点:非常适用于已知循环次数的程序

int sum=0;
for(int i=0;i<100;i++){
sum+=i;
}

//1.int i=0;
//2( i<100; sum+=i; i++)

break 与 continue

break与continue都是关键字,可以在循环体中使用 (for,while,do-while)

break:结束当前循环(退出循环)
continue:结束当次循环,进入下一次循环判断

嵌套循环

语法结构:
在一个循环体中,包含另外一个循环语句,这种结构就是嵌套循环

循环中的算法

暴力(穷举):数字,字母,特殊字符

顺推:已知条件推结果

逆推:已知结果推条件

数组

数组的定义

1.数组是一种引用数据类型,它可以存储多个相同类型的数据

2.数组的长度一旦定义,不能改变

3.数组中的元素可以通过下标来访问,下标的取值范围是0~长度-1

数组

数组元素的初始化

动态:创建数组后,为元素分别赋值

静态:创建数组的同时,为元素赋值

for each

增强的for循环

作用:遍历集合中的数据

语法:

for(数据类型 变量:数组名){

}

将数组中的元素转成字符串

Arrays.toString(数组)

内存模型

java虚拟机将内容主要分为两个区域,分别是栈和堆

基本数据类型,存在栈中

int i=9;

基本数据类型变量存储的是数据本身

引用类型是存储在堆中的

引用类型有数组,类,接口

int[] a=new int[5];

排序及二维数组

算法

什么是算法:

其实就是解决问题?

如何衡量算法优劣?

时间

空间

时间复杂度:大O表示法

O(1):固定次数,常数

O(logn):二分

O(n):一层循环

O(n方):二层循环

O(n的立方):三层循环

O(2的n次幂):

O(n!):

排序

排序是最常的算法

直接选择排序?

冒泡排序?

未排序,已排序

相邻比较并交换。整个的过程就像冒水泡一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.*;
//冒泡排序
public class BubbleSort
{
public static void main(String[] args)
{
int[] nums={12,45,78,5,75,33,23,47};
int temp=0;
//循环n-1次
for(int i=0;i<nums.length-1;i++){
//通过冒泡,每次从未排序堆中选一个最大的,放在已排序堆中
for(int j=0;j<nums.length-1-i;j++){
//交换
if(nums[j]>nums[j+1]){
temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
System.out.println(Arrays.toString(nums));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.*;
//直接选择排序
public class Sort3
{
public static void main(String[] args)
{
int[] nums={30,78,90,10,4,67};
//将最大的元素与第一个元素进行交换
//1.定义一个变量,用来存储最大元素的下标
for(int i=0;i<nums.length-1;i++){
int max=i;
//1.向未排序堆中找出最大的元素的下标
for(int j=i+1;j<nums.length;j++){
if(nums[max]<nums[j]){
max=j;
}
}
//2.交换
if(max!=i){
int temp=nums[max];
nums[max]=nums[i];
nums[i]=temp;
}
}
System.out.println(Arrays.toString(nums));
}
}

二分法

二分法,也称为折半查找法 前提是,数据一定是已排序的。

时间复杂度为O(logn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.*;
public class BinarySearch
{
public static void main(String[] args)
{
int[] nums={1,3,6,7,9,11,23,34,56,67,88,99,100};
Scanner s=new Scanner(System.in);
System.out.println("请输入一个数");
int n=s.nextInt();
int i=0;
int j=nums.length-1;
int m=0;
//1.编写代码,完成查询,如果找到了,输出位置,否则,输出“查无此数”
//2.分别使用O(n) O(logn)来解决?
while(i<=j){
//1.计算出中间位置
m=(i+j)>>1;
if(n==nums[m]){
System.out.println("找到了,位置是:"+m);
break;
}else if(nums[m]>n){
j=m-1;
}else{
i=m+1;
}
}
if(i>j){
System.out.println("查无此数");
}
}
}

二位数组

二维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java中其实没有二维数组,所谓的二维数组其实就是一个数组,只不过数组中的元素还是一个数组   

int[] nums={3,5,6,7};

int nums=new int3;

二维数组的初使化

静态初使化

int nums={{3,4,5,7},{6,6,6},{8,9,4,3}};

动态初使化

int nums=new int3;

int nums=new int3;

nums[0]=new int[]{3,4,5,6,7,8};

nums[1]=new int[]{333,56};

nums[2]=new int[]{4,4,6,8};

String names={{"刘备","张飞","关羽"},{"曹操"},{"孙权","周瑜"}};

面向对象OOP

面向过程与面向对象

面向对象,强调由谁来做
面向过程,强调怎么做

面向过程:把解决问题的步骤列出来,再逐步实现

面向对象:将问题域中的对象找出来,再确定它们之间的关系

面向对象有什么优点

1.更容易理解
2.解决复杂的问题,更有优势

面向对象的基础知识

什么是对象

所有具体的事物都是对象(object)

类是一组具有相同属性和行为的一组对象的抽象表示
类和对象的关系:
类是对象的抽象表示
对象是类的一个具体实例
类是抽象的,对象是具体的

class :

定义类的语法

回顾

1.面向过程,面向对象的区别?

2.面向对象的两个重要的概念

对象,类

3.如何定义一个类

class 类名{

属性

方法

}

类中都应该有什么

1.定义一个学生类型

1
2
3
4
5
6
public class student{
int stuno; //定义类的属性
String name;
String sex;
int age;
}

方法

大家用过方法吗?

用过哪呢?

Random r=new Random();

int i=r.nextInt(10);

方法中都包括什么?

方法名:

参数:多个,每个参数都需要有类型

返回值类型:

定义一个方法,判断是否为闰年

定义方法的语法?

1
2
3
4
5
6
7
8
9
返回值类型 方法名(参数列表 ){
方法体

int sum(int a){
int s=(1+a)*a/2;
return s;
}
int s=num(100);
s=num(50);

形参和实参的区别?

形参:定义方法时使用的参数,也叫形式参数

实参:调用方法,传递的参数,也叫实际参数

传参的过程就是赋值 即把实参赋给形参

return 用法

1.return语句 可以放在方法体中,代表返回的意思

2.如果一个方法中有return 语句,return 一定作为最后一条语句来存在

3.return 后的表达式的类型一定要与方法的返回值类型一致

4.如果方法返回值类型为void ,那么可以省略return ,或者直接写return

内存结构

方法的传参

pass by value:按值传递

pass by ref:按引用传递 (引用类型)

重载(overload)

方法重载(overload):同一类中,方法名相同,参数列表不同的一组方法就叫重载方法

输出

​ System.out.printlnEmpty();

​ System.out.printlnString(“hello”);

​ System.out.printlnInt(8);

参数列表的不同主要体现在三方面

1.数量不同

2.类型不同

3.顺序不同

四件套

1.理解题意,考虑边界问题

2.找出所有解

3.编写代码

4.测试

leetcode

构造方法及继承

构造方法

构造方法是一种特殊的方法
构造方法会给属性赋初值
构造方法会自动调用

构造方法与类同名

默认的构造方法

1.每个类都至少有一个构造方法,如果不显式的定义,java编译器会自动为类创建一个无参的构造方法 即默

认的构造方法

2.默认的构造方法没有参数,没有方法体

3.如果开发者显式的定义了构造方法,那么java编译器将不会再生成默认构造方法

构造方法不返回值

1
2
3
4
5
6
7
Student(int no,String name,int age){

}
//构造方法前面不能加void (添加后会变成一个普通方法)
(void)Student(){

}

默认的构造方法

就近原则

就近原则:当访问不同作用域同名变量时 寻找最近的同名变量

this关键字

1
2
3
4
5
6
7
8
this代表当前对象的属性引用   
this 用法:
this.属性
this.普通方法
this().构造方法
只有在其他的构造方法中才能使用this()来调用;
this()必须位于构造方法第一条语句;

Person p=new Person 一共做了几件事

1,开辟的新的空间

2,调用构造方法

3,返回引用赋值给p

匿名块

作用:将每个构造方法中相同的代码可以写在匿名块中 匿名块中的代码一定是在构造方法之前调用。

1
2
3
4
5
语法:
{
//代码
}

关于构造方法的小结

1
2
3
4
Person p=new Person(); //一共做了三件事
1.在堆中开辟内存空间
2.调用构造方法,为属性初使化
3.将引用返回给p;

包管理

1
2
3
4
5
6
7
8
照片   
d:/照片/男朋友/男朋友1
/
语法格式
package com.huayu.oop;
导包
import com.huayu.oop.*;
import com.huayu.oop.Person;

继承

继承是面向对象编程的最重要的特性之王一,它可以简化类的设计,利用的原有的类来创建新的类

1
2
3
class Person{};
class Student extends Person{
}

关于继承的总结

1.java中,只支持继承

2.子类继承父类的属性和方法,并且可以增加新的属性和方法

3.使用extends关键字来继承

4.是否可以使用继承 ,可以通过Instanceof 来判断

方法重写

方法重写(override,overwrite):在继承关系下,子类覆盖父类的方法覆盖时,要求方法名,参数,返回值都相同

关于继承的小结

1.父类有的,子类一定有

2.父类没有的,子类可以扩展

3.父类有的,子类可以更改

super关键字

super代表父类对象的引用
通过super关键字,再子类中可以

多态和封装

面向对象的高级特性

封装

继承 :复用

多态

多态

多态:同一类事物,可以拥有不同的形态

动物:吃,喝,行走

狗:

猫:

蛇:

1
2
3
4
5
//子类的对象可以替代父类的对象进行使用
Student s=new Student();
Person p=s;
父类 s=new 子类

编译时类型

1
2
3
4
5
6
7
//当我们使用多态时,定义变量时使用的类型称为编译时类型   我们在使用方法和属性时,只能使用编译时类型
//提供的属性和方法
Person p=new Student();//此时,编译时类型是Person
Animal a=new Dog(); //编译时类型是Animal
a=net Cat();
a=new Fish();
a=new Bird();

运行时类型

1
2
3
4
5
6
//当我们调用多态的方法时,执行期间,调用的是运行时类型的方法   
Animal a=new Dog(); //编译时类型是Animal,运行时类型是Dog
a=net Cat(); //编译时类型是Animal,运行时类型是Cat
a=new Fish(); //编译时类型是Animal,运行时类型是Fish
a=new Bird(); //编译时类型是Animal,运行时类型是Bird

类型转换

downcast:向下转型

upcast:向上转型

向上转换:直接转换无风险
向下转换:有风险

例如将,Animal转成Dog就是向下转型,向下转型有风险,建议转型前,使用instanceof来判断 否则可能

会产生ClassCastException(类型转换异常) 异常

封装

java中,处处皆封装 主要表现在三方面?

1.类就是一个封装体,类中封装的属性,方法

2.方法也是一种封装 (方法中封装的是代码)

Random r=new Random();

r.nextInt(10);

3.通过访问修饰符可以改变访问权限

访问修饰符

类内部 同包 不同包的子类 不同包的非子类
private
default
protected
public

修饰符

final

final修饰属性,类,方法
它可以修饰,属性,变量,参数,只能赋值一次 不能被修改
修饰类:不能被继承
修饰方法:不能被重写

static

static修饰属性,说明当前属性是的类属性,只存在一份整个类共享
并且可以直接通过类访问。
属性:类属性 类名,属性名
方法:类方法

示例

定义一个学生类,为学生自动生成学号,即创建的第一个学生,学号是1,第二个学生,学号是2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Student {
int stuno;
String name;
int age;

public Student(String name, int age) {
}

public void showInfo() {
System.out.println("学号:" + stuno + ",姓名:" + name + ",age:" + age);
}

public static void main(String[] args) {
Student s = new Student("王千金", 18);
Student s1 = new Student("王万两", 18);
Student s2 = new Student("王大锤", 19);
}
}

方法:

1.被static修饰的方法被称为类方法,调用类方法时无须创建对象,可以通过 类名.方法名()来调用

2.在static方法中,只能实现静态成员,不能使用非静态成员

3.在static方法中,也不能使用this,super关键字

static 返回值类型 方法名(){

//方法体

}

静态块

static块

语法:

static{

//代码

}

什么时执行:当类加载,初使化时,会自动调用静态块中的代码,因为每个类只加载一次,因此static{}只

被调用一次

加载顺序
静态成员和static块
👇
普通成员和非static块
👇
构造方法

加载顺寻
父类静态成员和static块
👇
子类静态成员和static块
👇
父类普通成员和非static块
👇
父类构造方法
👇
子类普通成员和非static块
👇
子类构造方法

单例模式

设计模式:Design partten

解决某些设计问题所采用的套路,有点类似于三十六计

目前设计模式一共有23种

一共分为三类

1.创建型

2.结构型

3.行为型

单例模式:Singleton

解决的问题:某个类,只允许产生唯一的实例

解决的方法

1.将构造方法定义成私有

2.定义一个同类型的静态的属性

3.定义一个public的静态方法,用来返回唯一的实例

单例模式 的两种类型

1.饿汉式

2.懒汉式 (lazy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static Singleton s=new Singleton();
/*String name;
String time;*/
private Singleton(){
/*System.out.println("创建单例模式: ");
System.out.println("进程分别有");
System.out.println("1");
System.out.println("2");
System.out.println("3");
System.out.println("4");*/
}
public static Singleton getinstance() {

return s;
}
/*public void close() {
System.out.println("关闭程序");
} */

抽象类与接口

abstract

abstract是一个修饰符

可以修饰方法和类
类:抽象类,不能被实例化
方法:抽象方法,让子类重写 如果子类不重写,子类方法也是抽象方法
抽象方法没有具体的实现

抽象方法只有定义,没有方法的实现
如果一个类有抽象方法,那这个类一定是抽象类
如果子类不重写方法,那这个子类还是抽象类

接口

1.java中不支持继承,但通过接口,可以实现多继承的效果

2.接口是一个特殊的抽象类 接口中只能有抽象方法和public static final属性

3.接口使用 interface来定义

接口与类

接口通常代表一种能力,抽象类通常代表一个概念

接口:able:

会飞的

发光的

吃肉的

抽象类:

动物

交通工具

图形

找你妹

发光

会飞的

带尖的

交通工具

飞机

会飞的 a=new Plan();

=new 天使();

=new 蝙蝠();

=new 萤火虫();

接口与抽象类的区别

1.接口是一个特殊的抽象类,只有public static final 属性及抽象方法

2.一个类只能有一个父类,但一个类可以同时实现多个接口

3.类和类之间是单继承 ,但接口和接口之间可以多继承

interface C extends A,B

4.抽象类通常表示一个概念,而接口则表示一种能力

类和类之间的关系

主要有三种

1.继承

2.关联

3.依赖

关联又可以细分为

1.聚合

2.组合

继承

子类拥有父类的属性和方法

判断两个类是否是继承关系 is 即 子类 is 父类

代码: class 子类 extends 父类{}

实现

一个类可以实现多个接口

代码: class 类 implements 接口

依赖

两个类之间关系是临时发生的,即做某一件事时才产生联系,这种关系就是依赖

1
2
3
4
5
6
7
8
9
10
判断方法 : use;
代码:
两种形式
class Person{
public void eat(){
Spoon s=new Spoon();
}
public void attack(Weapon w){
}
}

关联

关联:一种强依赖,比依赖的关系更强,是一种稳定的关系

判断: has

代码:作为别外一个类的属性来存在

聚合

聚合是关联的一种特例,体现整体和局部的关系 整体和局部之间没有共同的生命周期

组合

聚合是关联的一种特例,体现整体和局部的关系 整体和局部之间有共同的生命周期

设计思想

高内聚,低耦合

内部类

可以将类的定义放在其它的类中,这种类叫内部类

内部类一共有四种,分别是

  1. 静态内部类

  2. 成员内部类

  3. 匿名内部类

  4. 方法内部类

静态内部类

静态内部类可以使用外部类的静态成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class A {
static int a=9;
String name="人";
//静态内部类
static class B{
public void getB() {System.out.println(a);
}
}
}
//test
public class TestA {
public static void main(String[] args) {
//创建一个内部类的对象
A.B b=new A.B();
b.getB();
}
}

成员内部类

在一个类的内部定义另外一个类,内部类可以使用外部类的非静态成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Outer {
String name="outer";
class Inner{
public void getInner() {
System.out.println(name);
}
}
}
//test
public class TestA {
public static void main(String[] args) {
//创建一个外部类的对象
Outer o=new Outer();
//创建o的内部类对象
Outer.Inner i=o.new Inner();
i.getInner();
}
}

方法内部类

匿名内部类

有时,我们定义了一个内部类,使用这个内部类时,只实例一个对象 此时,就可以将定义类和实例化

对象,两个操作合到一起,这个就是匿名内部类

使用匿名类的条件是,此类必须实现一个接口或继承一个抽象类

1
2
3
4
5
1.定义一个类
class A(){
}
2.只实体化一次
A a=new A();
1
2
3
4
5
6
7
8
Usb u=new Usb() {
@Override
public String getData() {
// TODO Auto-generated method stub
return "这就是匿名内部类,看着有点不舒服。";
}
};
c.connect(u);

常用类

lang包

lang是java中使用最广泛的包,全称是java.lang 此包不需要手动导入,即可使用此包中的所有类

此包中常用的类有

System

Object

包装类

Math

字符串

Thread等

System类

1
2
3
4
5
6
7
8
9
//System 类包含一些有用的类字段和方法。它不能被实例化。 final修饰的类不能被继承
//常用方法:
System.out.println();
System.err.println();
System.in;
System.exit(0);
System.arraycopy()
System.gc()
System.currentTimeMillis()

包装类

java,为每个基本数据类型都定义了与之对对就的类,这些类就叫包装类

Boolean

Byte,Short,Integer,Long

Float,Double

Character

包装类与基本数据类型是可以相互转换的

基本数据类型-》包装类 :装包

包装-》基本数据类型: 拆包

Math

Math类:包含了所有用于几何和三角的浮点运算方法。Math类是final的,其中所有方法都是static的。

object类

Object类是所有类的类的父类 此类中下定义所有类都拥有的方法

1
2
3
4
5
6
7
8
9
equals():比较
toString():转成字符串
hashcode():哈希码
getClass():获得当前对象的类
clone():克隆
finalize():垃圾回收前执行的方法
notify()
notifyAll();
wait();

hashcode方法

hashcode是一个int值

即可将任何长度的对象转换成固定长度的数字

hashcode的协定

1.如一个对象不改变,那么多次调用hashcode()方法,返回的结果应该相同

2.如果两个对象相等(equals()),那么两个对象的hashcode()一定相等

3.如果两对象不相等,那么hashcode也有可能相等,设计时,尽量不相等

equals方法

boolean equals(Object obj);

四个特性

1.自反性:a.equals(a); true

2.对称性:a.equals(b)==b.equals(a);

3.传递性:a.equals(b)=true,b.equals(c)=true,那么 a.equals(c)=true

4.一致性:a.equasl(b)的值,多次调用时,应该相同 (前提是,a和b都不改变)

另外 a.equals(null),永远为false

toString方法

将对象转成String

注意:

1.如果不重写,那么将会调用父类的方法,内容是 类名@hashcode()的十六进制

2.当调用System.out.println(s)方法,会自动调用s.toString()方法

**getClass()**方法

获得对象的类 Class 即,所用同类型的对象返回的是同一个类

String 类

字符串 Java是常用的类
字符串的特点
1.字符串是常量,不能改变
2.为了提升使用率,java对字符串进行了优化处理,即字符串池(String Pool)

常用的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
int length():返回字符串的长度
int indexOf(int):返回某个字符位于串中的位置(下标)
int lastIndexOf(String):查找最后一个指定子串的位置
boolean isEmpty():判断某个字符串是否为空
boolean startsWith(String):是否以某个字符串开头
boolean endsWith(String):是否以某个字符串结尾
String toUpperCase():转成大写
String toLowerCase():转成小写
String trim():返回一个去掉前后空格的新串
String[] split(String);拆分字符串
char charAt(int):根据索引找字符
String replaceAll(String a,String b):将字符串中的a都替换成b,并返回一个新的字符串
boolean contains(String):是否包含另外一个子串

字符串类

字符串比较方法

1
2
3
4
5
boolean equals(String) 比较
boolean equalsIgnoreCase(String) 比较(忽略大小写(英文))
int compareto(String) 与另一个字符串比较大小
int comparetoignoreCase()

字符串池

因为String是java最常用的类,同时此类是常量(一旦定义,不能改变),因此,为了提高效率(节省内存),java中使用字符串池

1
2
3
4
String s="hello"; 
String s1="hello";
String s2="hello";
String s3="hello";

字符串转换

不同的类型之间是否能转换?

1 基本类型 (可转)c
2 不同类之间 需要有继承关系才能转
3 如果两个类型之间没有继承关系,有时也可以通过一些方法转换
integer parseInt()

1
2
3
4
5
6
String s="123";
//将String,s转成int类型
int i=Integer.parseInt(s);
System.out.println(i);
//将int 类型的i转换成字符串
String s2=String.valueOf(i);

StringBuffer

1.StringBuffer是字符串缓冲区,与String不同,内容是可以修改的

2.StringBuffer支持链式编程

3.与StringBuilder不同,它是线程安全的

4.StringBuffer与String是可以转换的

1
2
StringBuffer->String; sb.toString();
String->StringBuffer: StringBuffer sb=new StringBuffer(String);

常用方法

1
2
3
4
5
StringBuffer append():追加
StringBuffer insert():插入
StringBuffer delete(start,end):删除指定的子串
StringBuffer repalace(start,end,str):将指定位置的字符串替换成str
StringBuffer reverse():翻转字符串

StringBuilder

与StringBuffer基本相同,只是线程不安全

异常

异常概念

错误主要发生有两种
1 编译时发生错误
2 运行时错误 叫做异常

java的异常分为两类
1 error jvm内部错误、资源耗尽
2 exception 其他编程错误或偶然的外在因素

异常的体系结构

Throwable
Error:错误
Exception:异常
RuntimeException:运行时异常
NullPointerException:空指针异常
ClassCastException:类型转换异常
ArrayIndexOutOfBoundsException:数组下标越界
ArithmeticException:数字异常
受检查异常

异常处理

java中处理异常的机制是:抓抛模型

1
2
3
4
5
6
7
8
9
10
11
12
try-catch-finally
try{
//代码1;
//代码2 出现exceptin2;
//代码3:出现exception3;
}catch(Exception1 e){
//处理代码1
}catch(Exception2 e2){
//处理代码2
}finally{
//finally
}

异常分类

1 运行时异常 runtimeException类和其子类
2 受检查异常 非runtimException

区别:
运行时异常编译时可以不处理
受检查异常必须得处理

throws

当我们定义一个方法时,如果正确调用,可以返回一个结果,如果错误的调用,则应该抛出一个异常,这时,

可以使用throws来抛出某种异常的类型

1
2
3
4
5
public double getCircleArea(double r) thrwos Exception{
}
//MyArray; 10,11
public int get(int index) throws ArrayIndexOutOfBoundsException{
}

throw关键字

throw代表:手动抛出一个异常对象 格式是

throw new Excpetion();

throw主要用在方法中 throw有点类似return

throw和throws的区别

throws与throw的区别

1.throws放在方法后,说明此方法可能抛出某种类型的异常

2.throws后,放的是异常类型

2.throw 放在方法体中,throw后面放置的是异常对象,说明要手动抛出一个异常

工具类

java.util包,被称为工具包,也是java最常用的一个包

此包中主要有集合框架,日期,编码解码,国际化等

random类

Random类是随机数生成器,提供了很多用于生成各种类型的随机数的方法

Date 类

Date类主用来存储时间,精确到毫秒,内部是通过一个long值来进行存储的

Calendar

日历类:主要用来处理时间,例如,获取时间的某个部分,改变时间的某个部分 对时间进行加减操作等

1
2
3
4
5
6
7
8
9
1.实例化   
Calendar c=Calendar.getInstance();
2.Date与Calendar的转换
Calendar->Date c.getTime();
date->Calendar c.setTime(date);
3.操作时间
获得某个部分
int get(int )
Calendar.YEAR=1;

SimpleDateFormat

SimpleDateFormat类是日期格式化的类,可以用来将指定格式的之符串转成Date,也可以将Date转换成指

定格式的字符串

用法

1.先实例化一个对象

​ SimpleDateFormat sdf=new SimpleDateFormat(“yyyy-MM-dd”);

2.使用 parse方法将字符串转成日期

​ Date d=sdf.parse(str);

3.使用format方法将日期转成字符串

​ String str=sdf.format(data);

集合框架

集合框架

集合:集合也被称为容器,也是一个对象,此对象中可以管理其它的对象(element)元素 例如数组就是一

个集合

集合框架:java中给我提供了很多集合类,接口和算法类,作为一个整体,被称为集合框架

集合框架有两个顶层接口,所有的集合类都是这两个接口的实现

Collection接口

1.是集合框架的顶层接口之一,没有直接的实现类

2.Collection有三个子接口,分别是List,Set,Queue;

3.三个子接口的特点如下

List:有序(有索引),可重复

Set:无序,不可重复

Queue:先进先出

Collection接口应该有什么方法?

CURD

boolean add(E):增加一个元素

boolean addAll(Collection):增加多个元素

void clear():清空

boolean isEmpty():是否为空

int size():返回元素的个数

boolean remove(E):删除一个元素

boolean removeAll(Collection):删除多个元素

boolean retainAll(Collection):保留多个元素

boolean contains(E):是否包含某个元素

boolean containsAll(Collection):是否包含某些元素

Object[] toArray():将集合中的元素转成数组返回

所有集合类中存储的不是对象本身,而是一个引用而已

泛型

泛型指的是数据类型的参数化

泛型可以解决两个问题

1.类型安全检查

2.消除强制类型转换

java中,集合框架中的集合类都支持泛型

如何使用

1
List<Person> list=new ArrayList<Person>();

算法类

集合框架中除了提供接口和实现类之外,还提供用于处理集合的算法类,算法类主要有

Collections:用于处理集合的算法类

Arrays:用于处理数组的算法类

Collections常用的方法

1
2
3
4
5
6
7
8
static void sort(List):对List中的元素按自然顺序进行排序   
static void reverse(List):翻转集合中元素
static void shuffle(List):洗牌 (打乱顺序)
static E max(Collection):获取最大的元素
static E min(Collection):获取最小的元素
static void addAll(Collection coll,T...t):向coll中增加多个元素
static int binarySearch(List):二分查找
static void copy(List desc,List src):复制

Arrays常用的方法

1
2
3
4
5
static List asList(T...t):创建一个List,将添加多个元素
static T binarySearch():二分查找
static T[] copyRange(int[],start,end);
static void sort();
static String toString();

Comparable

Comparable是自然排序的接口,实现此接口的类,就拥有了比较大小的能力

此接口中的方法只有一个

public int comparareTo(T t){

}

返回值说明

正数:当前对象与比t大

0:当前对象与t相等

负数:当前对象比t小

Comparator

自定义的比较器

Comparator接口中包含一个方法

Comparator与Comparable的区别

1.Comparable为可排序的,也被称为自然排序或内部比较器,实现该接口的类的对象自动拥有可排序功能。

2.Comparator为比较器,也被称为外部比较器,实现该接口可以定义一个针对某个类的排序方式。

3.Comparator与Comparable同时存在的情况下,前者优先级高

ArrayList源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
transient Object[]elementData; //定义一个Object数组,用来存储数组的元素,此数组可以扩

//2.构造方法
public ArrayList(){
this.elementData=DEFAULTCAPACITY_EMPTY_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);
}
}
//3.add方法
public boolean add(E e){modCount++;
add(e,elementData,size);
return true;
}
private void add(E e,Object[]elementData,int s){
if(s==elementData.length)
elementData=grow();
elementData[s]=e;
size=s+1;
}

数组总结

数组的特点:元素之间的地址是连续的

get(index):时间复杂度是O(1)

set(index,E):O(1)

add(index,E):时间复杂度是O(n)

remove(index):O(n)

链表

1
2
3
4
5
6
7
8
9
10
11
//内部结节
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;
}
}

Stack

Stack是栈 特点是先进后出

​ LIFO:last in first out

方法:

​ push:压栈

​ pop:弹出

​ peek:获得栈顶的元素

栈的用途:

悔棋

撤销

方法的调用

Queue

Queue是一个队列,它的特点是先进先出(FIFO)

双端队列

Deque =dubole ended queue

两端都可以插入和删除元素

方法如下

​ addFirst

​ addLast

​ removeFist

​ removeLast

​ offerFirst

​ offerLast

​ pollFirst

​ pollLast

集合框架2

Set

Set:是Collection的子接口,无序,不可重复

三个主要的实现类

HashSet:hash

LinkedHashList:有顺序

TreeSet:排序的,内部是树

Iterator

迭代器 用来从所有的Collection集合中遍历数组 提供三个方法

boolean hasNext():是否有下一个未遍历到的元素

E next():取出一个元素

void remove():删除正在取出的元素

1
2
3
4
5
//代码模版如下   
Iteartor ite=collection.iterator();
while(ite.hasNext()){
E e=ite.next();
}

LinkedHashSet

LinkedHashSet是对HashSet的扩展,内部也是一个HashSet,但是按存入的顺序进行排列的

LinkdeHashSet中的元素也是不可重复的

SortedSet

SortedSet:是Set的子接口,具备排序的能力

TreeSet:是SortedSet的实现类,内部是一个树结构

Map

1.Map是集合框架中的另一个顶层接口 Map结构的特点是 kev-value

2.集合中,key 是不可以重复的

3.key 和 value的类型都是引用类型

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
put(k,v):增加一对元素
putAll(Map):将map中的所有元素增加到集合中
v get(k):根据key,来查找value
remove(k):根据key,来删除元素(key-value)
int size():获取集合中元素的个数
boolean isEmpty():是否为空
clear():清空
boolean containsKey(k):是否包含某个key
boolean containsValue(v):是否包含某个value
#遍历
Set keySet():得到所有的key的集合
Collection values():获得所有value的集合
entrySet():获得所有的元素的集合(Entry(key,value))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    //遍历的示例
Map<Integer, String> map = new HashMap<Integer, String>();
//增加
map.put(1,"james");
map.put(2,"jack");
map.put(3,"james");
map.put(4,"owen");
map.put(3,"scott");
//1.如何遍历集合 keySet
Set<Integer> set = map.keySet();
System.out.println(set);
for(
Integer k:set)

{
System.out.println(k + ":" + map.get(k));
}

//2.values
Collection<String> coll = map.values();
for(
String s:coll)

{
System.out.println(s);
}
System.out.println("----------------");
//3.key-value
Set<Entry<Integer, String>> es = map.entrySet();
for(
Entry<Integer, String> e:es)

{
System.out.println(e.getKey() + ":" + e.getValue());
}

HashMap的内部结构

1.key-value在内部被封装成了一个内部类 Node

2.HashMap中定义了一个Node类型的数组用来保存元素

3.Node还是一个单向链表 当链表中的元素超过8时,则转换成红黑树

1
2
3
4
5
6
7
8
9
10
11
12
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key; //key
V value; //value
Node<K,V> next; //下一个节点,Node是封装了key和value,同时也是一个单向链表
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
1
transient Node<K,V>[] table; //将所有的key-value对,即node,都存在table中,table是一个数组
1
2
3
4
//put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K, V>[] tab; //table的引用
Node<K, V> p; //元素中的首个Node节点
int n; //table的长度
int i; //元素的下标 通过hash后,模运行(实际是 (n - 1) & hash])
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //重新分配长度,算出tal的长度
//如果元素中无值,则将Node放在索引为i的下标下
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K, V> e; //如果有相同的key,e存储的是重复节点的引用
K k;
//如果元素中的首Node与新节点相同 则
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 {//遍历链表,如果有重复的,将重复的节点赋值给e,并break,否则,将新节点加到
链表尾部
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;
}
//如果有重复的,则不再比较
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)//根据onlyIfAbsend决定是否替

e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

Hashtable

Hashtable 是一个古老的 Map 实现类,不建议使用

Hashtable 是一个线程安全的 Map 实现,但 HashMap 是线程不安全的。

Hashtable 不允许使用 null 作为 key 和 value,而 HashMap 可以

TreeMap

TreeMap 存储 Key-Value 对时,需要根据 Key 对 key-value 对进行排序。TreeMap 内部是同红黑

树来实现的。

TreeMap 的 Key 的排序:

自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对

象,否则将会抛出 ClasssCastException

定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进

行排序。此时不需要 Map 的 Key 实现 Comparable 接口