1. JAVA简述

Java平台Java虚拟机(Java Virtual Machine)和Java 应用编程接口(Application Programming Interface、简称API)构成。Java 应用编程接口为Java应用提供了一个独立于操作系统的标准接口,可分为基本部分和扩展部分。在硬件或操作系统平台上安装一个Java平台之后,Java应用程序就可运行。Java平台已经嵌入了几乎所有的操作系统。这样Java程序可以只编译一次,就可以在各种系统中运行。Java应用编程接口已经从1.1x版发展到1.2版。常用的Java平台基于Java1.8。

  • JRE:只需要运行JAVA程序

  • JDK:用于开发,我用的版本:JDK8

  • SE主要用于桌面开发,其中一部分核心的是EE也要学的共同

    的基础部分,所以我们叫SE为基础版,是企业级开发的基础

  • JAVA语言的特性:跨平台性

JDK8安装

  1. oracle下载jdk8u311

  2. 下一步下一步安装即可(路径不要有中文)

  3. 安装完成后只有进入到这个文件夹才能执行javac.exe等,所以要配置环境变量(在任何目录都可以执行)

  4. 配置环境变量

    • 新建一个JAVA_HOME变量,值是bin所在文件夹

    • 在path里建一个bin里文件的路径

      可以下载多个版本jdk,环境变量配置谁就用哪个版本的jdk

2. Windows常用cmd命令

  1. cmd打开命令行窗口

  2. d:,回车进入d盘(可以不加cd )

    • dir 显示当前

    • md 创建文件目录

      1
      2
      echo str>1.txt
      创建文件1.txt 并将str写入该文件
    • cd filename 进入目录

  3. cd.. 返回上一级目录

    cd filename 返回指定目录

    cd\ 返回到根目录

  4. del 1.txt 删除1.txt这个文件

    del *.txt 删除所有txt后缀的文件

    del 文件目录名字(删除该目录下所有文件,目录不删)

    rd 文件目录名 (删除目录,目录需为空才能删除)

  5. 执行完上一条命令后,按↑即可快速再次执行该命令

3. HelloWorld

    • 新建一个.java文件(用记事本打开)
    • java代码编写.java文件;javac命令编译java文件成为字节码.class文件;java命令运行字节码文件
  1. 文档注释

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    bbbbb
    */
    /**
    解释说明此function
    */
    funion
    注释内容可以被JDK提供的工具javadoc解析,生成一套网页文件的说明文档
    1
    2
    javadoc -d 文件名 -author -version filename.java
    //完成后可以在文件名的文件夹中找到index.html文档

4. JAVA基本语法

4.1 关键字和保留字

保留字:go to const,以后可能会用,但现在不用。不要用作标识符

4.2 标识符

命名规则:

  1. 由26个英文字母大小写,0-9,_或$组成
  2. 数字不可以开头
  3. 不能使用关键字和保留字,但能包含关键字和保留字
  4. 不能包含空格
  5. Java中严格区分大小写,长度无限制

命名规范:

  1. 包名:多单词组成时所有字母都小写xxxyyyzzz
  2. 类名,接口名:多单词组成时,所有单词首字母大写XxxYyyZzz
  3. 变量名,方法名:第一个单词字母小写,第二个开始首字母大写xxxYyyZzz
  4. 常量名:所有字母都大写,多单词用下划线连接XXX_YYY_ZZZ

4.3 数据类型和运算符

image-20230707130929294

4.3.1 整型

1
2
3
4
5
6
7
8
9
10
11
class VariableTest
{
public static void main(String[] args)
{
byte b1 = 12;
byte b2 = -128;
byte b3 = 128;//超出范围,编译不通过
long l1 = 17371731379L//long型要以l或者L结尾(打印不会打印出来)
long l2 = 121;//把一个int型121赋值给long型变量l2(有一个自动类型转换的过程)
}
}
  • java默认整型常量为int,声明long型常量须后加‘l’或者‘L’

  • byte:1字节

    short:2字节

    int:4字节

    long:8字节

    Byte或者B是字节,bit是二进制位

4.3.2 浮点型

image-20230707131530748

1
2
float f1 = 12.3f;
double d1 = 12.1212;

4.3.3 字符类型

1
2
3
4
5
6
7
8
9
//char 字符 = 两个字节2byte = 16bit
char c1 = 'a';
int i1 = c1 + 1;//i1=98 + 有字符串就是连接,没有就是运算
char c2 = '\n'//转义字符
char c3 = '\u0043'//Unicode编码集0043对应的字符

计算机底层是2进制文件存储。
会采用不同的编码方式Unicode(具体有UTF-8等),gbk等
使用不同的解码方式就会乱码。
1
2
3
"\n"  转义换行
"\\n" 直接就是字符\n
"\"zrh\"" 转义让编译器不要把我们的双引号误以为是字符串的结束的标志

4.3.4 基本数据类型运算的规则

  • 自动类型提升
1
2
3
4
byte b1 = 2;
int i1 = 129;
int i2 = b1 + i1;//自动提升为容量大的类型
float f1 = b1 + b2;//自动类型转换(提升类型)

byte char short–>int–>long–>float–>double

前三者运算应用int型接收,即使是byte+byte

整型常量默认为int,浮点类型默认为float

1
2
3
short s1 = 10;
s1 = s1 + 2;//编译失败,short+int应该为int
S1 += 2;//编译成功,这种写法不会改变数据本身的类型

类似 += *= 这种是不会改变变量类型的(推荐使用)

  • 强制类型转换
1
2
double d1 = 12.9;
int i1 = (int)d1;//i1=12,直接阶段,损失精度
  • //除法运算
    int num1 = 12;
    int num2 = 5;
    int result = num1/num2//result = 2
     
    double result1 = num1/num2//result1 = 2.0
    double result2 = num1 / (num2 + 0.0)//result2 = 2.4
    double result3 = (double)num1 / num2;//result3 = 2.4
    
    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

    - 逻辑运算符

    ![image-20230707134724813](https://nnu-zrh.oss-cn-hangzhou.aliyuncs.com/202307071347919.png)

    短路与的判断方式是:

    从左到右依次判断,直到出现false为止将不再判断,直接得到结果为false(短路遇false就停)

    逻辑或和短路或的区别 逻辑或的判断方式是:

    从左到右依次判断,直到结尾

    ### 4.3.5 String

    ~~~java
    class StringTest{
    public static void main(String[] args){
    String s1 = "Hello World";
    String s2 = "";//空串

    int number = 1001;
    String s3 = s1 + number;//字符串和其他类型的+均为连接,结果任然是字符串
    System.out.printl(s1);
    }
    }

    两种创建String的方法
    //一是new型:String s = new String("abc");
    //另一种是双引号型:String s = "abc";(相同内容的会共享内存空间)
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
//String的常见操作

//获取字符串长度
int length = str.length();
//获取字符串中的第i个字符方法
char ch = str.charAt(i);
//获取指定位置的字符
cha[] array = new char[80]; //先要创建以一个容量足够大的char型数组,数组名为array,1、indexBegin:需要复制的字符串的开始索引2、 indexEnd:需要复制的字符串的结束索引3、array:前面定义的char型数组的数组名4、arrayBegin:数组array开始存储的位置索引号
str.getChars(indexBegin,indexEnd,array,arrayBegin);

//不忽略字符串大小写情况下字符串的大小比较
int result = str1.compareTo(str2);
//不忽略字符串大小写情况下判别字符串相等
boolean result = str1.equals(str2);

//查找字符(或者字符串)首次出现的位置
str.indexOf(ch);
str.indexOf(ch,fromIndex);
//最后一次出现
str.lastIndexOf(ch);
str.lastIndexOf(ch,fromIndex);

//截取
String result = str.substring(beginIndex,EndIndex);
//以参数为界拆分
String strArray[] = str.split(正则表达式);// 拆分的结果保存到字符串数组中

//替换
/***实现字符串的替换,原字符串内容不变***/
String s = “abcat”;
String s1 = s.replace(‘a’,‘1’);//替换所有字符
String s1 = s.replaceAll(“ba”,“12”);//替换所有字符串
String s1 = s. replaceFirst(“ba”,“12”);//替换第一个出现的指定字符串时

4.3.6 进制

1
2
3
4
int num1 = 0b110;//二进制
int num2 = 110;//十进制
int num3 = 0110//八进制
int num4 = 0x110//十六进制
  • 正数三码合一

  • 负数image-20230707141050609

补码再求一个补码就得到源码

计算机底层都以补码的形式存储数据,运算的时候,都是以补码进行运算的。

4.3.7 位运算符

image-20230707141121388

  • 位运算符操作的都是整型的数据

  • 在一定范围内:<< 每向左移一位,相当于*2,>>每向右移一位,相当于/2

  • 左移a

    image-20230707141205451

  • 右移

    image-20230707141254393

  • 无符号右移

    image-20230707141334676

4.3.8 三元运算符

1
(m > n)?m : n //真执行m,假执行n

4.4 程序流程控制

4.4.1 if-else

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
if()
{
//true执行
}

//else就近原则配对
//二者选一执行
if()
{

}else
{

}

//多者选一执行
if()
{

}else if()
{

}else
{

}

4.4.2 switch-case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//表达式只能是6种类型之一:byte short char int String 枚举
switch(表达式)
{
case 常量1:
语句1;
break;
....
default:
语句;
break
}

//如果多个case条件是相同的,那么可以合并
case 0:
case 1:
case 2:
System.out.println("Good")

break在switch中是可选的,第一次根据case进来,一直往下执行,直到遇到break或者末尾才跳出switch语句结构

4.4.3 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//break;跳出循环结构
//continue;结束当前循环,进入下一次循环

for(初始条件,执行语句;判断语句;迭代条件,执行语句)
{

}

while(true)
{
setence;
}

//至少有一次循环
do
{

}while(true)

带标签的循环

1
2
3
4
5
6
7
8
9
10
11
12
label:for(int i=1;i<=4;++i)
{
for(int j=1;j<=10;j++)
{
if(j%4==0)
{
//break默认跳出最近的循环,也可跳出默认指定标签的循环
break label;//直接跳出外层循环
continue label;//结束当前。继续外层大循环
}
}
}

foreach循环(增强for循环)内部是迭代器实现只能用于遍历集合或者数组

1
2
3
4
5
int[] arr = new int[]{1,2,3,4}
//循环过程中不能对item做修改,否则会出错
for (int item : arr){
system.out.print(item);
}

5. 数组

5.1 数组的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//静态初始化
int[] ids;
ids = new int[]{1,2,3,4};

int[] ids = new int[]{1,2,3,4};

//动态初始化
String[] names = new String[5];
//初始化值: 1、整型初始化值为0
2、浮点型初始化值为0.0
3char0(ASCII码为0):格式化输出是一个空格
4、(引用类型)Sting类型为 null
//方括号可以写到数组名后

其他写法均是错误的

5.2 元素索引

1
//索引角标从0开始,到-1结束

5.3 获取数组长度

1
2
//数组有一个length的属性
names.length

5.4 数组内存解析

new的东西都在堆区,数组名是在栈区,指向了堆区的地址。当堆区的东西没有索引指向它的时候,内存就会被释放掉。

5.5 多维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
int[][] arr = new int[4][];//动态初始化二维数组,且每行长度是不固定
int[][] arr = new int[4][];
for(int i = 0,j=1;i<arr.length;++i){
arr[i] = new int[j++];
}
//创建的是一个如下的二维数组
0
0 0
0 0 0
0 0 0 0

arr.length//有多少行
arr[0].length//该行有多少列

5.6 Arrays工具类

1
2
3
4
5
6
7
8
9
10
Arrays.fill()//填值
Arrays.toString(int[] arr);//数组转字符串 [1,2,3]带方括号的

Arrays.asList(Integer[] arr) //数组转List集合(注意这里要是包装类型的,才会把arr里的所有元素转换为list,如果是int[] arr 则会把整个数组arr当成一个元素放入list)

Arrays.sort(int[] arr);// 数组排序(默认降序)(要升序,只有把int[] arr转换为Integer[] arr,再传入comparator)

Arrays.copyOf(boolean[] original, int newLength);// 复制数组(以布尔型数组举例),没有填满新数组,默认填充
Arrays.copyOfRange(boolean[] original, int from, int to);//指定数组索引开始和结束的范围复制数组(以布尔型数组举例)
Arrays.equals(arr1,arr2); //两个数组值进行比较是否相等
1
2
3
4
5
6
7
8
int[] arr = new int[]{1, 2, 3};
//Arrays.toString返回的String带有[]
StringBuffer strb = new StringBuffer();
for(int s:arr){
strb.append(s);
}
String s = new String(strb);
System.out.println(s);

6. 面向对象

6.1 内存分配

image-20230707153627122

6.2 类成员

成员变量:类{}里面的,可以先不赋初值。

局部变量:函数方法里面的,必须有初值。

6.3 匿名对象

1
new phone().sendEmail();//创建一个对象,但没有显示的赋给一个对象名,调用完方法就无法使用了。

6.4 方法

6.4.1 方法的重载

重载:一些重名的方法

​ 同一个类,相同的方法名;

​ 参数列表不同,参数个数不同,参数类型不同;

6.4.2 可变个数的形参

1
2
3
4
5
public void show(int num,String...strs){//传入任意个数的String类型参数(可以为0个),可变类型形参只能有一个,且只能在参数列表末尾。
for(int i=0;i<strs.length;++i){
strs[i];//遍历所有参数(strs就相当于一个变长数组)
}
}

6.5 变量赋值

基本数据类型:值传递

引用数据类型:地址传递

6.6 封装性

类属性私有化(private),提供公共的属性(public)来获取或者设置该属性。

image-20230707153832792

类只能用缺省和public修饰

6.7 构造器

​ 一个类中多个构造器构成重载;

​ 构造方法没有返回类型,但是有返回值,返回的就是一个对象,可以有访问权限修饰符,但是不能有static final之类的修饰符

  • 空参构造器
    1. 子类继承时,默认调用super()空参
    2. 便于反射时创建运行时类对象

6.8 UML类图

image-20230707154016760

6.9 this

this即是当前对象,可以调用当前类的属性和方法。

6.10 package和import

  • package

    image-20230707155422719

    image-20230707155449936

  • import

    image-20230707155510691

6.11 继承

  • extends
  • 一个类可以被多个类继承,一个类只能有一个父类(单继承性)
  • 光标放到类名,CTRL+T 可以看到继承结构
1
2
3
4
5
6
7
8
class A extends B{
public A(parameter){//构造器
//如果子类构造方法的声明没有明确调用父类的构造方法,则系统在执行子类构造方法时会自动调用父类的默认构造方法(即无参数构造方法)。

   //如果想调用一个带参数的父类构造方法,就必须用关键字super显示地编写构造方法语句。
super(parameter);
}
}

6.11.1 方法的重写

子类继承父类,对同名同参的方法,进行覆盖操作;

不能重写父类的private方法

1
2
3
4
@Override//表明以下方法是重写
public void function(){

}

6.11.2 super

显式的调用父类的方法

super.属性

super.方法

1
2
3
//在构造器中,以下两种只能二选一(因为他们都必须在构造器的首行)
this(形参列表);
super(形参列表);

6.12 多态

  1. 对象的多态性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //父类的引用指向子类的对象
    Person p = new Man();

    //也叫虚拟方法调用
    p.method();//调用的是子类的方法(有重写的情况下)
    p.参数;//只能调用Person类的属性

    //虽然参数类型是Person,但可以传入其子类对象Man,调用的当然也是子类的方法,
    public void func(Person p){
    p.method();
    }
    • 多态和重载的区别

    image-20230707155909774

    多态是运行时行为

  2. instanceof

    1
    2
    3
    a instanceof A//判断实列对象a是否是类A的一个实列,是返回true,不是返回false

    A可以是父类,也可以是间接父类,都会返回true

6.13 Object类

6.13.1 equal和==

image-20230707160204682

比如说比较String,就要用equals(),==比较的是地址,不同的String肯定不会相等,而equals是重写过的,比较的是字符串的每一个字符是否都相等。

6.13.2 toString()

1
System.out.println(object);

执行过程中调用对象内部的toString方法

6.14 单元测试

就相当于写了一个Public void无参函数,可以单独执行这个函数,从而测试这个函数体类的相关代码。

image-20230707183620733

6.15 包装类(Wrapper)

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character
  • 基本数据类型、包装类、和string之间的转换

    image-20230707184026001

  • 新特性,自动装箱与自动拆箱(只适用于基本数据类型和包装类)

6.16 static

1
2
3
4
5
6
7
//静态变量(也叫类变量)随着类的加载而加载
//通过某一对象修改,其他对象也会改变
//可以通过类调用
static int num;

//静态方法只能调用静态属性和方法
public static void fun()

6.17 代码块

1
2
3
4
5
6
7
8
9
10
class mmm{
//静态代码块
static{

}
//非静态代码块
{

}
}
  1. 静态代码块
  • 内部可以有输出语句

  • 随着类的加载而执行,而且只执行一次

  • 作用:初始化类的信息

  • 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行

  • 静态代码块的执行要优先于非静态代码块的执行

  • 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构

  1. 非静态代码块
  • 内部可以有输出语句

  • 随着对象的创建而执行

  • 每创建一个对象,就执行一次非静态代码块

  • 作用:可以在创建对象时,对对象的属性等选行初始化

  • 如果一个类中定义了多个非静态代码块,则按昭声明的先后顺序执行

  • 非楼态代码块內可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法

要理解清楚静态的只有类,还没有对象

6.18 final

  1. final可以用来修饰的结构:类、方法、变量
  2. final 用来修饰一个类:此类不能被其他类所继承
    比如: String、System类、StringBuffer类
  3. final 用来修饰方法,表明此方法不可以被重写
    比如Object类中getClass()
  4. final 用来修饰变量,此时的“变量“就称为是一个常量
    1. final修饰属性,可以考虑赋值的位置有,显式初始化、代码块中初始化、构造器中初始化
    2. final修饰局部变量
      尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋值一个实参,就只能在方法体内使用此形参,但不能对其重新赋值

6.19 匿名类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class Person{
//一个抽象类Peron
}

//匿名子类对象
Person p = new Person{
@Override
//重写所有抽象方法
};

//匿名子类匿名对象
method(new Person{
@Override
//重写所有抽象方法
});

6.20 抽象类

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能共享特征。有时将一个父类设计得非常抽象,以至手它没有具体的实例,这样的类叫做抽象类。

1
2
3
4
5
abstract class name{
//用abstract修饰的类就不能被实例化了
//抽象方法,没有方法实体,只有抽象类才能创建抽象方法
abstract public void fun();
}

abstract只能修饰类和方法

  1. abstract修饰类:抽象类
  • 此类不能实例化

  • 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)

  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

  1. abstract俊饰方法:抽象方法
  • 抽象方法只有方法的声明,没有方法体

  • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。

  • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化

  • 若子类没有重写父类中的所有的抽象方法,则子类也是一个抽象类,需要使用abstract修饰

6.21 接口

  1. 接口使用interface来定义

  2. java中,接口和类是并列的两个结构

  3. 如何定义接口:定义接口中的成员

    1. JDK7及以前:只能定义全局常量和抽象方法
      • 全局常量:public static final修饰的。但是书写时,可以省略不写
      • 抽象方法:public abstract修饰
  4. JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)

  5. 接口中不能定义构造器的!意味着接口不可以实例化

  6. java开发中,接口通过让类去实现(implements)的方式来使用,如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化,如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类

  7. java类可以实現多个接口 —>弥补了java单继承性的局限性

    class AA extends BB implements CC, DD,EE

    接口与接口之间可以继承,而且可以多继承,接口的县体使用,体现多态性

JDK8新特性:除了定义全局常量和抽象方法外,还可以定义静态方法默认方法

  1. 接口中定义的静态方法,只能通过接口名来调用,其实现类的对象不能调用
  2. 而实现类的对象可以调用接口中的默认方法
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface ClothFactory {
//全局常量
int MM=10;
//抽象方法
int getValue();
//静态方法
static void show(){
}
//默认方法
default void fun(){
}
}

6.22 内部类

  1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类

  2. 内部类的分类,成员内部类(静忘,非静态) VS 局部内部类(方法内,代码块内、构造器内)

  • 成员内部类(作为类的属性成员)

image-20220404212517336

image-20220404213012685

image-20220404213130679

  • 局部内部类(方法内或者代码块内)
1
2
3
4
5
6
7
public class Test {
public void method() {
class Inner {
// 局部内部类
}
}
}

7. 设计模式

image-20220328173414182

7.1 单例设计模式

image-20220328173434845

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
//饿汉式
class Bank{
//私有化构造器
private Bank(){

}
//内部创建类的对象
//要求此对象也必须声明为静态
private static Bank instance = new Bank();
//提供公共的静态的方法,返回对象
public static Bank getInstance(){
return instance;
}
}
//外部创建对象,即使再创建一个也是同一个对象
Bank bank1 = Bank.getInstance();


//懒汉式
class Bank{
private Bank(){

}
private static Bank instance = null;
//提供公共的静态的方法,返回对象
public static synchronized Bank getInstance(){
if(instance==null){
instance = new Bank();
}
return instance;
}
}

饿汉式:1. 对象加载时间过长

​ 2. 线程安全的

懒汉式:1. 延迟对象的创建

​ 2.可能存在线程不安全的情况

7.2 模板方法的设计模式

抽象类的应用:模板方法的设计模式

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
abstract class BankTemplateMethod{
//具体方法
public void takeNumber(){
//
}
public abstract void transact();//办理具体的业务,钩子方法
public void evaluate(){
//
}
//模板方法,把基本操作组合到一起,子类一般不能重写
public final void process(){
this.takeNumber();
this.transact();//像个钩子,具体执行时,挂哪个子类就执行哪
this.evaluate();
}
}

class DrawMoney extends BankTemplateMethod{
@Override
public void transact(){
//取款
}
}

class ManageMoney extends BankTemplateMethod{
@Override
public void transact(){
//理财
}
}

main:
//根据创建对象的不同执行不同的功能
BankTemplateMethod btm = new DrawMoney();
btm.process();

BankTemplateMethod btm = new ManageMoney();
btm.process();

7.3 代理模式

接口的应用:代理模式

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
//实现网络访问的接口
interface NetWork{
public void browse();
}

//被代理类
class Server implements NetWork{
@Override
public void browse(){
//真实实现访问网络
}
}

//代理类(调用被代理类的一个过程)
class ProxyServer implements NetWork{
private NetWork work;
ProxyServer(NetWork work){
this.work = work;
}
//其他自己的工作
public void check(){

}
@Override
public void browse(){
chech();
work.browse();
}
}
  • 静态代理
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
public class StaticProxyTest {
public static void main(String[] args) {
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(new NikeClothFactory());
proxyClothFactory.produceCloth();
}
}

//代理类
class ProxyClothFactory implements ClothFactory {
private ClothFactory factory = null;

//传入的是实现了ClothFactory接口的对象
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}

@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作");
factory.produceCloth();
System.out.println("代理工厂做一些收尾工作");
}
}

//被代理类
class NikeClothFactory implements ClothFactory {
@Override
public void produceCloth() {
System.out.println("NIKE工厂生产一批运动服");
}
}
//工厂接口
interface ClothFactory {
void produceCloth();
}

7.4 工厂模式

7.4.1 简单工厂

  • 设计

image-20220403113642103

  • 使用

image-20220403113751335

7.4.2 工厂方法

7.4.3 抽象工厂

8. 异常处理

8.1 概述

Throwable异常分类:

image-20220405101530020

Exception:编译时异常&&运行时异常(RuntimeException)

  • image-20220603152251863

8.2 异常处理机制

  1. try-catch-finally 注意:try-catch-finally 以下的代码会照样执行而try中发生异常之后的代码不执行
1
2
3
4
5
6
7
8
9
try{
//可能会出现异常的代码
}catch(异常类型名1 变量名1){
//处理异常的代码
}catch(异常类型名2 变量名2){
//处理异常的代码
}finally{
//一定会执行的代码,可以不写
}

image-20220729153107515

image-20220405172603402

1
2
e.getMessage();//返回一个错误信息的字符串
e.printStackTrace();//打印错误相关信息,红的红的
  1. throws
1
2
3
4
//该方法可能会抛两个异常
public void fun() throws Excp1,Excp2{

}
  • image-20220405180203815

  • 子类重写父类方法时,抛出的异常应该比父类的异常同级或者是其子类,这样在某些多态条件下才能catch住这个给异常

8.3 生成异常

1
2
//生成一个异常,同样可以用catch和throw方式进行处理
throw new Exception("message");

8.4 自定义异常类

一般继承两个异常 Exception(编译时就要考虑的异常)和RuntimeException(运行时异常)

image-20220410160948920

9. 多线程

1
Java.lang.Thread //多线程类

9.1 多线程的创建

  • Tread类方法

image-20220422143429958

  • 设置优先级

    1
    myThread.setPriority(Thread.MAX_PRIORITY)
  • 线程创建方法一:

    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
    public class MyThread extends Thread {
    public MyThread(){}
    //带参构造器,可以设置线程名字
    public MyThread(String name){
    super(name);
    }
    @Override
    //run()里写的是我们这个线程需要执行的语句
    public void run() {
    super.run();
    for (int i = 0; i < 100; i++) {
    System.out.println(i);
    if(i%20==0){
    Thread.yield();//释放当前进程的执行权(当然下一刻又可能重新分配给他)
    try {
    sleep(1000);//当前进程阻塞1000ms
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }



    class Start {
    //主线程
    public static void main(String[] args) {
    MyThread myThread = new MyThread(); myThread.setPriority(Thread.MAX_PRIORITY);//线程开始之前可以设置优先级
    myThread.start();//启动myThread线程;并调用当前线程的run方法;一个线程只能start一次
    myThread.run();//不是多线程,只是执行这个方法
    //如果这里再写其他输出语句,此时是主线程,就会和上面的myThread交替输出(并行)
    }
    }
  • 创建线程方法二:

    image-20220422155855127

    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
    public class ImplementRunnable {
    public static void main(String[] args) {
    //多个线程可以共用这个对象,适合多个线程有共享数据的情况
    ImThread imThread = new ImThread();
    Thread thread_1 = new Thread(imThread);
    thread_1.setName("thread_1");
    thread_1.start();
    Thread thread_2 = new Thread(imThread);
    thread_2.setName("thread_2");
    thread_2.start();
    }
    }

    class ImThread implements Runnable {
    //一般来说只造一个对象,所以声明一个private类型的属性就行
    private int data;//共享数据
    @Override
    public void run() {
    for (int i = 0; i < 100; i++) {
    //方便查看线程交错执行的情况,每次该线程阻塞100ms
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + " 输出 " + i);
    }
    }
    }
  • 两者不同:

    image-20220422160842113

  • 创建线程方法三:==可以抛异常,且有返回值,支持泛型==

    实现Callable接口

    image-20220423173131184

    image-20220423173335621

  • 创建线程方法四:线程池

    image-20220711155526895

    image-20220423172958847

9.2 生命周期

image-20220422180747065

9.3 线程同步

  • 方法一:同步代码块

    image-20220422181706722

    继承Thread类的实现中,同步监视器可用 ==类名.class==

    实现Runnable接口,同步监视器可用==this== 因为一般只new一个对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class ImThread 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;
    }
    }
    }
    }
    }
  • 方法二:同步方法

    如果操作共享数据的代码完整的声明在一个方法中,不妨将此方法声明为同步方法

    ==注意:==run()方法不能用synchronized修饰

    image-20220708230917309

    • 实现runnable的使用,用同步方法

      1
      2
      3
      4
      //把操作临界区的方法设置为synchronized的,监视器默认为this(所以对于实现Thread的方式来说是不安全的,他一般会造几个对象,this就不唯一了)
      public void synchronized fun(){

      }
    • 实现Thread的方式

      1
      2
      3
      4
      //方法设置为静态,监视器为当前类(类名.class)
      public static void synchronized fun(){

      }
  • 方法三:同步锁

    JDK5.0开始,更强大的线程同步机制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ReentrantLock lock = new ReentrantLock();
    try{
    lock.lock();
    /**
    临界区
    */
    }finally{
    lock.unlock();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Window implements Runnable {
    private int ticket = 100;
    //设置一个同步锁,true表示先来的进程会优先得到临界资源
    private ReentrantLock lock = new ReentrantLock(true);

    @Override
    public void run() {
    while (true) {
    //临界代码用try/finally包围
    try {
    lock.lock();
    if (ticket > 0) {
    System.out.println(Thread.currentThread().getName() + "买票,票号为: " + ticket);
    ticket--;
    } else {
    break;
    }
    } finally {
    lock.unlock();
    }
    }
    }
    }

例题 一个账户两个储户:两个储户分别向这个账户存3000,每次存1000,存3次。

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
53
54
55
public class Test {
public static void main(String[] args) {
Customer customer_1 = new Customer("储户一");
Customer customer_2 = new Customer("储户二");
customer_1.start();
customer_2.start();
}
}

class Account {
private int balance = 0;

public int getBalance() {
return balance;
}

public Account() {
}

public Account(int balance) {
this.balance = balance;
}

public void deposit(int money) {
balance += money;
}
}

//因为每个顾客都有自己的属性balance,所以用继承Thread的方式创建3个顾客进程
class Customer extends Thread {
private static Account act = new Account();
private int balance = 3000;
//注意同步锁的唯一性,这里创建了2个对象,所以加static
private static ReentrantLock lock = new ReentrantLock(true);

public Customer(String name) {
super(name);
}

@Override
public void run() {
while (true) {
try {
lock.lock();
if (balance > 0) {
act.deposit(1000);
balance -= 1000;
System.out.println(Thread.currentThread().getName() + " 存钱:1000 " + "账户余额为:" + act.getBalance());
} else break;
} finally {
lock.unlock();
}
}
}
}

9.4 死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

9.5 线程通信

1
2
wait();//执行该代码的线程进入阻塞状态  并释放锁
notifyAll();//唤醒所有阻塞进程

image-20220423160411420

==只能用在同步代码块和同步方法(lock不能用这个进行通信),因为这三个方法的调用对象只能是同步监视器对象,lock没有同步监视器==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ImRunnable implements Runnable {
private int num = 1;

@Override
public void run() {
while (true) {
synchronized (this) {
//唤醒另一个优先级最高的进程,当前进程还握有锁,其他进程也不能进入
this.notify();
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + " " + num);
num++;
//当前进程进入阻塞状态,并释放同步锁(注意:Sleep是不会释放锁的)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else break;
}
}
}
}

10. String类

10.0 编码

  • 最早的美国ASCII编码一个字节就能表示数字,字母等。首位没用,就用了后7位来表示

  • 后来计算机技术传入其它国家,有新的编码方式GBK等;它是向下兼容的

    仍用一个字节表示原来的ASCII字符,其它的如,汉字就用两个字节表示,字节首个二进制0代表一个字节表示,1代表2个字节表示

  • ANSI:平台默认编码。英文操作系统中是ISO-8859-1,中文系统是GBK

  • Unicode只是定义了一个庞大的,全球通用的字符集,并为每个字符确定了唯一确定的编号,具体存为什么样的字符流,取决于字符编码方案,推荐的Unicode编码为UTF-8,UTF-16(即每次传输8,16个二进制位)

    image-20220727164400881

    如上图,同样用首位来确定几位编码,汉字用了3个字节

10.1 String

image-20220507095851038

字符串的不可变性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//字面量的定义方式,此时字符串值声明在字符串常量池中
//字符串常量池不会存储相同的内容的字符串
//此时s1 == s2
String s1 = "abc";
String s2 = "abc";

//new的方式开辟的在内存的堆区
//s1 != s2
String s1 = new String("www");
String s2 = new String("www");

//涉及到变量的拼接那么就会在堆区开辟内存
//当然如果s1是final修饰的,那还是在常量池中
String s3 = s1 + "eee"

image-20220505175338063

image-20220506215212473

1
2
3
4
5
6
7
8
9
10
11
12
13
//字符串转换为字符数组
char[] charArray = str.toCharArray();
//字符数组转换为字符串
String str = new String(charArray);

//转换为字节数组
byte[] byt = str.getBytes(charsetName:"gbk");//转换为字节数组并指定编码方式
//解码
String str = new String(byt,charsetName:"gbk");

昝荣华 //UTF_8汉字是3个字节
[-26, -104, -99, -24, -115, -93, -27, -115, -114]

10.2 StringBuffer

默认容量是初始容量+16,超过之后会在底层重新new一个字符数组

1
2
//指定容量的buffer
StringBuffer sb2 = new StringBuffer(10);

image-20220507111442413

10.3 StringBuilder

同StringBuffer,效率更高,但不是线程安全的;

10.4 Java比较器

  • comparable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
compareable接口;
//自然排序
//自定义类要实现排序,需要继承compareable接口,重写compareTo接口

// 类内部实现Comparable接口
public class Student implements Comparable<Student>{
private String ID;
private String name;
private int age;
private Double score;
@Override
public int compareTo(Student arg0) {
System.out.println("======================");
// 此种写法为升序,若改为arg0.ID.compareTo(this.ID)则为降序
return this.ID.compareTo(arg0.ID);
//return this.ID - arg0.ID;
}
}
  • comparator

image-20220510171556772

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定制排序
//实现comparator接口,重写compare方法
Arrays.sort(arr,new Comparator(){
@Override
public int compare(object o1,object o2){

}
})


// 按姓名进行升序排序的外部类,用Comparator接口
class ComparatorWithNameUP implements Comparator<Student> {

@Override
public int compare(Student arg0, Student arg1) {
return arg0.getName().compareTo(arg1.getName());
}
}

10.4 正则表达式

  • 主要类及其使用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
String s = "砸金花1998和时间256莎莎看哈萨克156874啥叫和.";
//正则表达式
String reg = "(\\d\\d)(\\d\\d)";//括号进行分组
//创建模式
Pattern pattern = Pattern.compile(reg);
//字符串s中去匹配正则
Matcher matcher = pattern.matcher(s);
//每次匹配一个,如果能匹配到,matcher.find()返回为True。匹配到的内容在group中,
while (matcher.find()){
String s1 = matcher.group(0);//所有
String s2 = matcher.group(1);//第一组
String s3 = matcher.group(2);//第二组
System.out.println(s1+" "+s2+" "+s3);
}
1
2
3
4
5
6
7
8
//调用Pattern类的静态方法判断字符串s是否整体匹配成功
boolean isMatch = Pattern.matches(reg,s);


//Matcher类
matcher.start();//每次匹配到的开始索引
matcher.end();//每次匹配到的结束索引,参数里可以指定分组
String nStr = matcher.replaceAll("newString");//替换所有
  • 正则转义符

    1
    String reg = "\\.";//匹配一个字符'.' ,两个\\表示转义
  • 字符匹配(大写表示取反)

    image-20220604231838045

    👆都是要加[]的

    image-20220604231940991

    \\s匹配任何空白字符(空格,制表符)

    \\S匹配任何非空白字符

  • 大小写问题

    1
    2
    3
    4
    5
    6
    (?i)abc //abc不区分大小写
    a(?i)bc //bc不区分大小写
    a((?i)b)c //b不区分大小写

    //创建模式时参数Pattern.CASE_INSENSITIVE不区分大小写
    Pattern pattern = Pattern.compile(reg,Pattern.CASE_INSENSITIVE);
  • 选择匹配符

    image-20220605105226540

  • 限定符

    image-20220605140306271

    image-20220605140902595

    1
    String regStr = "a{2,3}" //表示匹配aa 或者aaa 贪婪匹配,优先匹配多的即aaa
  • 定位符

    指定在原字符串的那个位置进行匹配

    image-20220605143251482

  • 分组

    1
    2
    String reg = "(\\d\\d)(\\d\\d)"; //分组0,1,2
    String reg = "(?<g1>\\d\\d)(?<g2>\\d\\d)"; //分组命名
  • 非捕获分组

    就是加了括号,但表示的不是分组,不能用group(1)这样去获取

    image-20220605144440907

  • 非贪婪匹配

    默认贪婪匹配。后面加个?表示非贪婪匹配,即尽量匹配少的

  • 反向引用

    image-20220605154700126

    内部反向引用:正则表达式内使用的。

    例:image-20220605155216957

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //反向引用案例:结巴去重

    String s = "我我我...要要..学学学....java";
    //反向引用,能匹配到 我我我 ... 要要 .. 学学学 ....
    String reg = "(.)\\1+";
    Pattern pattern = Pattern.compile(reg);
    Matcher matcher = pattern.matcher(s);
    //在外面把匹配到的替换成 反向引用的那个(外部反向引用)
    String s1 = matcher.replaceAll("$1");
    //去除.
    System.out.println(s1.replaceAll("\\.", ""));

11. 枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//自定义枚举类
自己写的普通类,是继承于Object类的
pass;

//enum关键字定义枚举类
//使用enum关键字默认继承于java.lang.Enum
enum Season {
//通过构造器构造的枚举类对象,只不过省略了前面的public staic final .. = new()
SPRING("春天","春天好啊"),
SUMMER("夏天","夏天好啊"),
AUTUMN("秋天","秋天好啊"),
WINTER("冬天","冬天好啊");
//枚举类的属性
public final String seasonName;
public final String seasonDesc;
Season(String seasonName,String seasonDesc) {
this.seasonName=seasonName;
this.seasonDesc=seasonDesc;
}
//一般不重写toString方法,默认是SPRING..
}


Season SPRING = Season.SPRING;

image-20220508212226576

12 注解

Annotation

框架 = 注解 + 反射+ 设计模式

  • JDK内置的三个注解
1
2
3
4
5
6
7
8
9
10
11
@Override
//方法重写,编译时会进行校验

@Deprecated
//此方法已过时,不建议使用

@SuppressWarnings
//抑制变量未使用警告
@SuppressWarnings("unused")
//抑制变量未使用警告and未加泛型警告
@SuppressWarnings({"unused","rawtypes"})
  • 自定义注解

    框架的时候再学(根据反射来实现对应注解的功能)

    1
    2
    3
    public @interface 注解名{
    //定义内容
    }
    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
    public class AnnotationTest {
    //注解可以显示赋值,如果没有默认值,则必须赋值
    @MyAnnotation1(age = 25)
    public void test1(){

    }
    @MyAnnotation2("value")//只有一个参数可以省略参数名
    public void test2(){

    }
    @Test//没有参数
    public void test3(){

    }
    }

    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation1{
    //注解的参数:参数类型+参数名+()
    String name() default "";
    int age();
    int id() default -1;//-1代表不存在
    String[] schools() default {"武汉理工大学","南京师范大学"};
    }

    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation2{
    String value();//只有一个参数名字约定用value
    }
  • 元注解

    即修饰注解的注解

    1
    2
    3
    @Target  用于描述注解的使用范围,修饰的是类,方法,还是其它;
    @Rentenion 用于描述注解的生命周期,即什么时候有效;
    SOURCE<CLASS<RUNTIME 源代码时<编译成类时<运行时

13. 集合

  • 无论是Collection还是Map的对象一定要重写equals()方法 {无论是否可重复,删除的时候一定会调用equals方法}

  • //向上转型,Collection list只能使用自己的方法,不能使用ArrayList的方法
    Collection list = new 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
      
    - ![image-20220509103112231](https://nnu-zrh.oss-cn-hangzhou.aliyuncs.com/202307101725981.png)

    ## 13.1 Collection

    ![Collection](https://nnu-zrh.oss-cn-hangzhou.aliyuncs.com/202307101724602.png)

    ~~~java
    //Collection常用方法

    Collection list = new ArrayList();
    ++++++++++++++++++++
    list.add(Object);//添加元素

    //判断是否包含元素 contains调用的是equal方法
    boolean contains = list.contains(1);//True

    list.containsAll(list1);//判断list1中的所有元素是不是都被包含

    list.remove(1);//移除

    list.removeAll(list1);//移除所有元素

    list.retainAll(list1);//返回两个集合交集中的部分

    Object[] arr = list.toArray();//集合转换为数组
  • 集合的遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//集合的遍历
System.out.println(list);//[object1,object2,...]

//Iterator的三个方法
hasNext();
next();
remove();

Iterator iterator = list.iterator();//获取迭代器对象
//每次调用list.iterator()都会获得指向开头的一个指针
while (iterator.hasNext()){
System.out.println(iterator.next());//从头开始获取下一个元素
iterator.remove();//在遍历的时候删除元素
}

//foreach 内部也是iterator
for(Object o:list){
System.out.println(o);
}

//内部迭代
list1.forEach(System.out::println);

13.1.1 List

有序,支持索引

  • List方法(有序,可重复)

    1
    2
    3
    4
    //arr类型不能为基本数据类型
    //数组转List集合(这样构建的list是定长的)
    Arrays.asList(Integer[] arr)
    Arrays.asList(1,2,3)

    image-20220509231640934

13.1.1.1 ArrayList

  • 线程不安全,效率高

  • 扩容机制

    ArraysList底层是数组实现的。jdk7 new ArrayList()底层创建了长度是10的数组,jdk8初始化长度为0,首次add时才创建了长度为10的数组。容量不够时都是扩容为原来的1.5倍。

    1
    2
    //指定初始数组的容量,避免频繁扩容
    ArrayList new ArrayList(int capacity);
  • 可以使用Collections工具类转变为线程安全的

  • 底层使用双向链表进行存储

  • 实现了Queue接口

13.1.1.3 Vector

  • 线程安全

  • Vector();底层创建长度为10的数组,默认扩容为原来数组长度的两倍

  • 一般用ArrayList代替了

13.1.2 Set

  • 重写equals()和hashCode()

  • 无序性体现在底层是按hash值存储,所以无序

  • image-20220510174229930

  • 要重写hashCode用它自动生成那个就行,就保证了一致性

  • 操作就是Collection的操作

13.1.2.1 HashSet

image-20220510155208802

13.1.2.2 LinkHashSet

image-20220510173557166

13.1.2.3 TreeSet

重写比较器

会把添加的元素按大小排列

image-20220510161332261

1
TreeSet set = new TreeSet(com);//TreeSet传入我们定制的比较器com

13.1.3 Queue

13.1.3.1Queue

只能在队头进行操作,队尾进队

1
Queue<Integer> q1 = new LinkedList<>();

可以存入null元素

image-20220602221916572

13.1.3.2 Deque

1
Deque<Integer> q3 = new LinkedList<>();

双端队列,在两头进行操作,尽量不要添加null元素

image-20220602224417454

可以当作堆栈来使用,不用stack了

13.1.3.2 PriorityQueue

1
PriorityQueue<Integer> q2 = new PriorityQueue<>(Comparator);

优先级队列种的元素根据自然顺序进行排序,或者通过在队列构建时提供的Comparator进行排序,当然这取决于使用哪种构造函数。优先级队列不允许空(null)元素

PriorityQueue是一个无界优先级队列是基于优先级堆的

优先级队列的队头元素是最小的元素,如果有多个元素并列最小,那么队头是它们其中之一

13.1.3.3 BlockingQueue

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:

  1. 在队列为空时,获取元素的线程会等待队列变为非空。
  2. 当队列满时,存储元素的线程会等待队列可用。

13.2 Map

Map

image-20220511150125858

image-20220511175439181

1
2
3
//返回的泛型是一个map的Entry,这个Entry也是泛型
Set<Map.Entry<Integer, String>> entries = map.entrySet();
sout: [1=a, 2=b, 3=c, 4=d]

13.2.1 HashMap

image-20220511172409047

image-20220511172438407

  • linkhashmap

image-20220511175048624

HashMap和双向链表合二为一即是LinkedHashMap。所谓LinkedHashMap,其落脚点在HashMap,因此更准确地说,它是一个将所有Entry节点链入一个双向链表的HashMap。由于LinkedHashMap是HashMap的子类,所以LinkedHashMap自然会拥有HashMap的所有特性。比如,LinkedHashMap的元素存取过程基本与HashMap基本类似,只是在细节实现上稍有不同。当然,这是由LinkedHashMap本身的特性所决定的,因为它额外维护了一个双向链表用于保持迭代顺序。此外,LinkedHashMap可以很好的支持LRU算法。

image-20230329212332504

13.2.2 TreeMap

  • 按照key进行排序,所以key必须是同一类型的对象

image-20220511180011005

  • //返回大于等于指定key的key或Entry
    Integer key = map.ceilingKey(2);
    Map.Entry<Integer, String> entry = map.ceilingEntry(2);
    
    //返回小于等于指定key的key或Entry
    Integer key1 = map.floorKey(5);
    Map.Entry<Integer, String> entry1 = map.floorEntry(5);
    
    //返回逆序的set或map
    NavigableSet<Integer> set = map.descendingKeySet();
    NavigableMap<Integer, String> map1 = map.descendingMap();
    
    //返回集合中含有最小的Key的元素或Entry,最大是last
    Integer key2 = map.firstKey();
    Map.Entry<Integer, String> entry2 = map.firstEntry();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23



    ### 13.2.3 HashTable

    和HashMap差不多,一般不用。线程安全,效率低

    properties:常用来处理配置文件

    ![image-20220812144917531](https://nnu-zrh.oss-cn-hangzhou.aliyuncs.com/202307102309084.png) 配置文件

    ![image-20220812145013415](https://nnu-zrh.oss-cn-hangzhou.aliyuncs.com/202307102309843.png)

    ~~~Java
    public static void main(String[] args) throws Exception {
    Properties properties = new Properties();
    FileInputStream fileInputStream = new FileInputStream("./src/properties.properties");
    properties.load(fileInputStream);
    String user = properties.getProperty("user");
    String mm = properties.getProperty("密码");
    System.out.println(user);
    System.out.println(mm);
    }

13.3 Collections工具类

用来操作List Set Map

image-20220511182636921

13.4 相互转换

  1. 数组转集合

    • 遍历数组,将元素保存到集合中
    1
    2
    3
    4
    5
    6
    int[] arr = {1,2,3};
    List<Integer> list = new ArrayList<Integer>();
    for(int n:arr){
    list.add(n);
    }
    System.out.println(list);
    • 使用asList()方法

      注意:1)如果传入的参数是一个数组,那么这个数组一定要是引用类型才能将其转换为List集合,当传入基本数据类型数组时则会将这个数组对象当成一个引用类型对象存进List集合。

    1
    2
    3
    Integer[] arr = {1,2,3};
    List list = Arrays.asList(arr);
    System.out.println(list);

    注意:2)使用这种转换的方法,直接添加元素会报错,需要重新new一个list

    1
    2
    3
    4
    5
    6
    7
    Integer[] arr = {1,2,3};
    List<Integer> list = Arrays.asList(arr);
    //使用这种转换的方法,直接添加元素会报错,需要重新new一个list
    list.add(4);
    ArrayList<Integer> list1 = new ArrayList<>(list);
    list1.add(4);
    list1.forEach(System.out::println);
    • 采用流的方式
    1
    2
    3
    int[] arr = {1,2,3};
    List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
    System.out.println(list);
  2. 集合转数组

    • 传入一个类型和大小完全相同的数组进行转换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    List<Integer> list = new ArrayList<>();
    list.add(10);
    list.add(20);
    list.add(30);
    Integer[] arr = new Integer[list.size()];
    list.toArray(arr);
    for(int i:arr){
    System.out.println(i);
    }

14. 泛型

https://blog.csdn.net/weixin_45395059/article/details/126006369

  1. 静态方法不能用泛型

  2. 异常类不能用泛型

  3. //T[] arr = new T[10];编译是不通过的
    T[] arr =(T[]) new Object[10];
    
    1
    2
    3
    4
    5
    6
    7



    ![image-20220513152638559](https://nnu-zrh.oss-cn-hangzhou.aliyuncs.com/202307111249521.png)

    ~~~java
    Collection<String> list = new ArrayList<String>();//java8后面的<String可以省略>

14.1 自定义泛型结构

  • 泛型类
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
class A<T>{
T parameter;

public A(T parameter) {
this.parameter = parameter;
}
}
//继承时指明泛型类型,B就不再是一个泛型类了
class B extends A<Integer>{
public B(Integer parameter) {
super(parameter);
}
}
//继承时保留泛型
class C<T> extends A<T>{
public C(T parameter) {
super(parameter);
}
}



class D<T,E>{
public D() {
}
}
//保留部分泛型
class ED<E> extends D<Integer,E>{

}
  • 泛型方法

    不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法

    泛型方法可以声明为静态的

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
class A<T>
{
public void printInfo(T t){
System.out.println(t);
}
}
class B //B后面的<T>可加可不加
{
//泛型方法()只需要将泛型列表放置在返回类型与访问修饰符之间即可
public <T> void printInfo(T t){ //类中其它方法不能使用当前方法声明的泛型,即该(T t)只作用于该泛型方法
System.out.println(t);
//return t;
}
//泛型方法重载
public <T, T1> T printInfo(T t, T1 t1){
System.out.println(t);
System.out.println(t1);

return t;
}


}

public class Test {
public static void main(String[] args) {
A<String> a = new A<String>();
a.printInfo("kobe");

B b = new B();
b.printInfo('c');
b.printInfo("zhangkun", 100);
System.out.println(b.printInfo("zhangkun", 100));
}

}

14.2 通配符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
Object[] arr1 = null;
String[] arr2 = new String[]{"shja","sh"};
arr1 = arr2;//String是Object的子类,可以赋值,是多态
System.out.println(arr1[0]);
List<Object> list1 = null;
List<String> list2 = null;
//list1 = list2;他们是并列的关系,不能直接赋值
new FanXin().print(list1);
}
//使用通配符的方式,这样就可以传入多种类型的List了
public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}

有限制条件的通配符

1
2
<? extends P>//?是P类及其子类   上界通配符
<? super P>//?是P类及其父类 下界通配符

15.网络编程

1. IP地址

  • 本地回环地址

本地回环地址127.0.0.1,通常被称为本地回环地址(Loopback Address),不属于任何一个有类别地址类。它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口。”

1
2
//获取本地IP地址对象
InetAddress localHost = InetAddress.getLocalHost();
1
2
//通过域名或者IP获取一个Ip地址的对象
InetAddress address =InetAddress.getByName("www.baidu.com");

2. 端口号

image-20220802154235231

3. TCP

TCP或UDP的网络编程都叫socket编程

  • 客户端向服务器端发送文件

    流程

    1.打开服务器端,会阻塞在read方法里(read()和readLine()都会读取对端发送过来的数据,如果无数据可读,就会阻塞直到有数据可读。或者到达流的末尾,这个时候分别返回-1和null。)

    2.打开客户端,把文件写道socket里去,写完往下执行流会关闭

    3.那么服务端的read操作也会完成

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
@Test
public void client() throws IOException {
//1.创建socket对象,指明服务器段的IP和端口号
Socket socket = new Socket("127.0.0.1", 8899);
//2.获取一个输出流(只有字节的),用于输出数据(把数据写到服务器上)
OutputStream os = socket.getOutputStream();
//3.具体写数据的操作
InputStream inputStream = new FileInputStream("./src/text/hello1.txt");
byte[] buffer = new byte[5];
int len;
while ((len = inputStream.read(buffer)) != -1){
//把数据写道os流里面去
os.write(buffer,0,len);
}
//关4.闭流资源
os.close();
inputStream.close();
socket.close();
}


@Test
public void server() throws IOException {
//1.创建服务器端的serverSocket,指明自己的端口号(当运行起来时,自己在哪个服务器运行IP就是多少,不需指明)
ServerSocket serverSocket = new ServerSocket(8899);
//2.调用accept接收来自客户端的socket
Socket socket = serverSocket.accept();
//3.获取输入流
InputStream is = socket.getInputStream();
//4.读取输入流中的数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while ((len = is.read(buffer)) != -1) {
//读到的数据写道baos这个流里面,方便打印输出
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
//5.资源的关闭
baos.close();
is.close();
socket.close();
}
  • 客户端服务端进行交互

    1.打开服务端,一直read等待数据

    2.写数据,写完关闭 socket.shutdownOutput();对面read就会停止

    3.此时客户端阻塞在read,服务端就行写数据,写完向下执行流关闭

    4.客户端read就会停止,向下执行,关闭流

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
53
54
55
56
@Test
public void client() throws IOException {
//1.创建socket对象,指明服务器段的IP和端口号
Socket socket = new Socket("127.0.0.1", 8899);
//2.获取一个输出流(只有字节的),用于输出数据(把数据写道服务器上)
OutputStream os = socket.getOutputStream();
//3.具体写数据的操作
InputStream inputStream = new FileInputStream("./src/text/hello1.txt");
byte[] buffer = new byte[5];
int len;
while ((len = inputStream.read(buffer)) != -1){
//把数据写道os流里面去
os.write(buffer,0,len);
}
socket.shutdownOutput();

//接收服务端的数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((len = socket.getInputStream().read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());

//关4.闭流资源
os.close();
inputStream.close();
socket.close();
baos.close();
}

@Test
public void server() throws IOException {
//1.创建服务器端的serverSocket,指明自己的端口号(当运行起来时,自己在哪个服务器运行IP就是多少,不需指明)
ServerSocket serverSocket = new ServerSocket(8899);
//2.调用accept接收来自客户端的socket
Socket socket = serverSocket.accept();
//3.获取输入流
InputStream is = socket.getInputStream();
//4.读取输入流中的数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while ((len = is.read(buffer)) != -1) {
//读到的数据写道baos这个流里面
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());

//向客户端发送数据
socket.getOutputStream().write("data received".getBytes());

//5.资源的关闭
baos.close();
is.close();
socket.close();
}

4. UDP

代码略,这里稍微理解就行。

package(数据包(报))的形式发送,不用建立连接,不确保接收,速度更快。

5. URL编程

统一资源定位符

1
URL url = new URL("url");

16. 反射

1. 概述

反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能操作任何对象的内部属性及方法

反射被视为动态语言的关键

2. Class

获取Class实例的方法(获取运行时类)

  • Class<Person> clazz = Person.class;
    
    1
    2
    3
    4

    - ~~~Java
    Person person = new Person();
    Class<? extends Person> aClass = person.getClass();
  • //注意要写全类名
    Class<?> aClass1 = Class.forName("com.atzrh.java.Person");
    
    1
    2
    3
    4

    - ~~~Java
    ClassLoader classLoader = 类名.class.getClassLoader();
    Class<?> aClass2 = classLoader.loadClass("com.atzrh.java.Person");
    clazz == aClass == aClass1 == aClass2

哪些类型可以有class对象:

image-20220812133126280

3. 类加载器

Class Loader

image-20220812141122382

image-20220812141351036

加载配置文件时通过类加载器获取输入流

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) throws Exception {
Properties properties = new Properties();

ClassLoader classLoader = PropertiesTest.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("properties.properties");

properties.load(inputStream);
String user = properties.getProperty("user");
String mm = properties.getProperty("密码");
System.out.println(user);
System.out.println(mm);
}

4. 创建运行时类对象

1
2
3
4
5
//获取Class实例
Class<?> aClass = Class.forName("com.atzrh.java.Person");
//运行时类要求提供一个空参构造器,并且访问权限得够,通常设置为public
Person person = (Person) aClass.newInstance();
//反射的动态性:程序执行时才知道具体构造的是哪个对象

5. 获取运行时类内部结构

  1. 获取类的属性,以及属性的权限修饰符、类型、变量名
  2. 获取类的方法,以及方法的权限修饰符、注解、返回值类型、方法名(参数信息)、异常
  3. 获取运行时类的构造器
  4. 获取运行时类的父类
  5. 获取运行时类实现的接口、所在包、注解

6. 调用运行时类的结构

  1. 调用指定的属性并可以设定值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //获取运行时类
    Class<?> aClass = Class.forName("com.atzrh.java.Person");
    //创建运行时类对象
    Person person = (Person) aClass.newInstance();
    //获取指定的属性
    Filed name = aClass.getDeclaredField("name");

    //保证当前属性可访问,才能有如下操作
    name.setAccessible(true);
    //Set方法
    name.set(person,"Tom");
    //Get方法
    name.get(person);
  2. 调用方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //获取运行时类
    Class<?> aClass = Class.forName("com.atzrh.java.Person");
    //创建运行时类对象
    Person person = (Person) aClass.newInstance();
    //通过方法名和形参列表获取某个指定方法
    Method show = aClass.getDeclaredMethod("show",String.class);
    //保证方法可访问
    show.setAccessible(true);
    //传入通过对象和参数调用方法,没有返回值返回null
    Object returnValue = show.invoke(person,"CHN");
    //静态方法调用不用传对象
  3. 调用指定构造器

    1
    2
    3
    4
    5
    6
    //获取对应参数列表的构造器
    Constructor constructor = aClass.getDeclaredConstructor(String.class);

    constructor.setAccessible(true);
    //创建运行时类对象
    Person person = (Person) constructor.newInstance("Tom");

7. 反射应用:动态代理

代理模式和接口是密不可分的

代理设计模式:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理

前面第7章的代理是静态代理,代理类和目标对象在编译期间就确定了下来

动态代理举例:

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
public class ProxyTest {
public static void main(String[] args) {
//被代理类对象
SuperMan superMan = new SuperMan();
//代理类的对象
Human instance = (Human) ProxyFactory.getProxyInstance(superMan);
//通过代理类对象,调用被代理类的同盟==同名方法
instance.eat("hotpot");
System.out.println(instance.getBelief());

//静态代理的那个Nike工厂也可用动态代理
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ClothFactory o =(ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
o.produceCloth();
}
}
//要实现的接口
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String getBelief() {
return "I believe I can fly";
}

@Override
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
//生产代理类的一个工厂
class ProxyFactory{
//得到一个代理类对象
public static Object getProxyInstance(Object object){
//object是被代理类的对象,根据这个动态的造一个代理类对象
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
//通过代理类的对象调用方法a时,会自动调用如下的方法:invoke..
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//被代理类要执行的方法就声明在invoke中
return method.invoke(object, args);
}
});
}
}

17. IO

1. Scanner

1
2
3
4
5
6
7
8
9
import java.util.Scanner;
Scanner scan = new Scanner(System.in);//创建一个Scanner对象用于接收
String name = scan.next();//接收String
scan.nextInt();
scan.nextFloat();
scan.nextBoolean();
//Scanner没有获取char型的方法
String genderStr = scan.next();
char gender = genderStr.charAt(0);//获取字符串的索引变为

2. 格式化输出

1
2
3
4
5
6
7
8
9
10
11
12
13
String str="Java";
double pi=3.14;
int i=100;
//"%"表示进行格式化输出,其后是格式的定义
System.out.printf("%f\n",pi);//"f"表示格式化输出浮点数
System.out.printf("%d\n",i);//"d"表示格式化输出十进制整数
System.out.printf("%o\n",i);//"o"表示格式化输出八进制整数
System.out.printf("%x\n",i);//"x"表示格式化输出十六进制整数
System.out.printf("%s\n",str);//"s"表示格式化输出字符串
System.out.printf("一个字符串:%s,一个浮点数:%f,一个整数:%d",str,pi,i);//可以一次输出多个变量,注意顺序即可

//二进制
String s = Integer.toBinaryString(i)

3. File类

  1. Test单元测试里当前目录指的是当前Module下的目录
  2. main方法里当前目录指的是当前工程
  • File类对象构造方法
1
2
3
4
5
6
7
@Test
public void test(){
//根据传入的参数得到一个文件或者文件目录
File file1 = new File("./test.txt");
//得到在file1(也可以传字符串)目录下的 的文件或目录xx
File file2 = new File(file1, "xx");
}
  • 以下全是File类对象的方法

image-20220514161429836

  1. 先获得一个File对象,然后去创建它

    1
    2
    3
    4
    5
    6
    File file = new File("./test.txt");
    if (!file.exists()){
    file.createNewFile();
    }else {
    file.delete();
    }
  2. image-20220514165209302

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
boolean delete();//删除文件或者空的文件目录


//递归的去删除一个非空的文件目录
public static void deleteFile(File file){
if(file.isFile()){
file.delete();
}else{
File[] files = file.listFiles();
file.delete();
for (int i = 0; i <files.length ; i++) {
deleteFile(files[i]);
}
}
file.delete();
}

4. IO流

1.IO

  • 字节流 字符流

​ 输入流 输出流(都是相对于内存来说的,读:把文件读到内存 写:从内存写到文件)

Reader:读文件 Writer:写文件

Input:读文件 Output:写文件

image-20220722180101240

​ 节点(文件)流 处理流

  • (下图 上面是抽象基类,访问文件这一行属于节点流,可直接作用于文件,下面的都属于处理流,要作用于节点流才能处理文件)

image-20220515152311767

  1. FileReader&&FileWriter

    • 读数据完整流程

      读数据(读到内存),文件要存在,不然会报错

    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
    //把fr定义在{}里的话,下面finally{}就没有了
    FileReader fr = null;
    try {
    //1.实例化File类对象,指明要操作的文件
    File file1 = new File("./IO/hello.txt");
    //2.提供具体的流
    fr = new FileReader(file1);
    //3.读入数据,read()返回的是每次读入字符的字符码,文件末尾就是-1
    int data = fr.read();
    while (data != -1) {
    System.out.println((char) data);
    data = fr.read();
    }
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    try {
    //4.关闭流
    if (fr!=null) {
    fr.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    • read(char[]),每次可读入多个
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //创建一个5的char型数组,相当于每次可最多读取5个字符
    char[] cbuf = new char[5];
    int len;
    //read返回的是每次读入字符的数量(最后一次可能不为5),读完返-1
    while((len = fr.read(cbuf)!=-1){
    //因为可能数组存不满,所以要用len,不能用cbuf.length
    for(int i = 0;i<len;++i){
    System.out.print(cbuf[i]);
    }
    }
    • 写数据完整流程

      写到文件,File指向的文件不存在则会创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    FileWriter fw = null;
    try {
    //1. 提供file类的对象,指明写出到的文件
    File file1 = new File("./IO/hello1.txt");
    //2. 提供FileWriter的对象,用于数据的写出(可指定方式:覆盖or追加)
    fw = new FileWriter(file1,true);
    //3. 写出数据(若file1对应文件不存在则自动创建)
    fw.write("\nhello1");
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    //4. 关闭资源流
    try {
    if (fw != null) {
    fw.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    为了使代码逻辑清晰,以下代码均throws异常(实际开发中应该如上)

    • 使用输入输出流进行文件复制操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //1. 构造File类对象,指明要操作的文件
    File file1 = new File("./src/text/hello1.txt");
    File file2 = new File("./src/text/hello2.txt");
    //2. 创建输入输出流
    FileReader fr = new FileReader(file1);
    FileWriter fw = new FileWriter(file2);
    //3. 文件读取,写入
    char[] cbuf = new char[5];
    int len;
    while ((len = fr.read(cbuf)) != -1) {
    //每次写入cbuf的0-len长度的字符
    fw.write(cbuf, 0, len);
    }
    //4.流资源关闭
    fr.close();
    fw.close();
  2. FileInputStream&&FileOutputStream

    1
    2
    3
    4
    5
    //操作基本都一样
    //2. 创建输入输出流,使用字节流
    FileReader fr = new FileInputStream(file1);
    //3. 文件读取的时候使用字节数组
    byte[] cbuf = new byte[5];

    当使用字节流去处理文字时,使用字节数组

    1
    byte[] cbuf = new byte[5];

    而在UTF-8里,英文数字都是一个byte存储,汉字是3个byte

    而你是每5个byte的去读,当你每读5个就通过控制台输出的话,汉字的3个byte可能就不在每批的那5个里,所以极可能乱码

    • 字符流处理:文本文件(.txt .java .c .cpp)

      字节流处理:非文本文件(.jpg .doc…….)

  3. 缓冲流

    处理流的一种,要作用于节点流

    实际工程中,通常使用缓冲流,因为它的速度更快

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //1. 构造File类对象,指明要操作的文件
    File file1 = new File("./src/text/hello1.txt");
    File file2 = new File("./src/text/hello2.txt");
    //2.1. 创建输入输出流(字节流)
    FileReader fr = new FileReader(file1);
    FileWriter fw = new FileWriter(file2);
    //2.2. 创建缓冲流(对应的字节缓冲流)
    BufferedReader bufferedReader = new BufferedReader(fr);
    BufferedWriter bufferedWriter = new BufferedWriter(fw);
    //3. 文件传输的细节
    char[] cbuf = new char[5];
    int len;
    while ((len = bufferedReader.read(cbuf)) != -1) {
    //每次写入cbuf的0-len长度的字符
    //在缓冲流的输出流中,有一个缓冲。输出达到缓冲区域上限时,就会输出出去(bufferedWriter.flush()可主动刷新缓冲)
    bufferedWriter.write(cbuf, 0, len);
    }
    //4.流资源关闭,外层处理流关闭内层节点流会自动关闭
    bufferedReader.close();
    bufferedWriter.close();
    1
    2
    3
    4
    5
    6
    7
    //可以不造File对象,直接把文件路径传入节点流
    BufferedReader bufferedReader = new BufferedReader(new FileReader("./src/text/hello1.txt"));
    //缓冲流每次可以读一行
    String data;
    while ((data=bufferedReader.readLine())!=null){
    System.out.println(data);
    }
  4. 转换流

    实现字节流—->字符流

    • InputStreamReader

      字节的读流—>字符的读流

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //1.字节输入(读)流
    FileInputStream fileInputStream = new FileInputStream("./text/hello1.txt");
    //2. 转换为字符输入(读)流
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
    //3.按照字符流的方式读数据
    char[] cbuf = new char[10];
    int len;
    while ((len=inputStreamReader.read(cbuf))!=-1){
    //
    }
    • OutputStreamWriter

      字节的写流—>字符的写流

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //常用来更改编码格式

    //1.字节输入输出流
    FileInputStream fileInputStream = new FileInputStream("./src/text/hello1.txt");
    FileOutputStream fileOutputStream = new FileOutputStream("./src/text/hello2.txt");
    //2. 转换为字符输入输出流
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "gbk");
    //3.按照字符流的方式读写数据
    char[] cbuf = new char[10];
    int len;
    while ((len=inputStreamReader.read(cbuf))!=-1){
    outputStreamWriter.write(cbuf, 0, len);
    }
    inputStreamReader.close();
    outputStreamWriter.close();
  5. 标准输入输出流

    标准:键盘输入,控制台输出

    可调用System类的输入输出方法修改输入输出位置

    1
    2
    System.in//返回的是一个字节输入流(从键盘读入)
    System.out//返回的是一个字节输出流(写出到控制台)
  6. 打印流

    两个都是输出流

    PrintStream

    PrintWriter

  7. 数据流

    方便保存Java的基本数据类型和String

    • DataOutputStream
    1
    2
    3
    4
    5
    6
    DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("./src/text/hello1.txt"));
    String time = new Date().toString();
    //写入String用writeUTF
    dataOutputStream.writeUTF(time);
    dataOutputStream.flush();//刷新,立马写入文件(默认达到缓冲区上限才写,其它流同理)
    dataOutputStream.close();
    • DataInputStream
    1
    2
    3
    4
    DataInputStream dataInputStream = new DataInputStream(new FileInputStream("./src/text/hello1.txt"));
    //如果写入多个数据,读的时候顺序要和写入顺序一样
    System.out.println(dataInputStream.readUTF());
    dataInputStream.close();
  8. 对象流

    ObjectInputStream :读取 反序列化

    ObjectOutputStream:保存 序列化

    弥补7,除了基本数据类型还可以操作对象

    • 自定义类Person满足以下要求才能序列化

    image-20220728224840468

    关于上图补充:被以上两个关键字修饰的成员变量,其实可以序列化,但是值保存不了,即反序列化的时候读取的是默认值(String为null,int为0等等)

    1
    2
    3
    //标识接口,没有任何需要实现的抽象方法
    public interface Serializable {
    }
  • RandomAccessFile

    不同于上述都是继承于4个抽象基类,他直接继承于Object类,同时实现了DataInput和DataOutput接口。它根据构造器的参数去指定这个流是实现输入还是输出的功能

    image-20220728231254953

    代码逻辑一样:

    image-20220728231724879

    作为输出流(写文件)时:

    通过seek()调整写入时的指针位置

    image-20220728232930069

    image-20220728234646400

    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
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    public class BreakPTrans {
    public static boolean copy(String src, String des) {
    boolean copySuccess = false;
    RandomAccessFile r = null;
    RandomAccessFile w = null;
    File seekFile = new File("./BreakPointDL/src/file/seek.txt");
    FileReader FileReader = null;
    FileWriter FileWriter = null;
    //记录每次读的位置
    long seek = 0;
    try {
    //如果记录seek的文件存在,则要更新seek
    if (seekFile.exists()) {
    FileReader = new FileReader(seekFile);
    StringBuilder stringBuilder = new StringBuilder();
    int data;
    while ((data = FileReader.read()) != -1) {
    stringBuilder.append((char) data);
    }
    seek = Long.parseLong(new String(stringBuilder));
    }
    FileWriter = new FileWriter(seekFile);
    r = new RandomAccessFile(src, "r");
    r.seek(seek);
    w = new RandomAccessFile(des, "rw");
    //指针指向末尾,实现追加操作
    w.seek(w.length());
    int data;
    while ((data = r.read()) != -1) {
    w.write((char) data);
    System.out.println("one byte has been written");
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    copySuccess = true;
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    try {
    if (FileWriter != null && r != null) {
    //把退出时指针位置记录到文件
    FileWriter.write(String.valueOf(r.getFilePointer()));
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    try {
    if (r != null) {
    r.close();
    }
    if (w != null) {
    w.close();
    }
    if (FileReader != null) {
    FileReader.close();
    }
    if (FileWriter != null) {
    FileWriter.close();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    return copySuccess;
    }
    }

2.NIO

pass

18. 时间

随机数

1
double value = Math.random();//随机生成一个[0.0,1.0)的随机数,double类型

19.2.1 System类

  • 此方法适合于计算时间差
1
2
//获取当前时间(距离时间戳1970年..)的毫秒数
long time = System.currentTimeMillis()

19.2.2 Date类

  • Java.util.Date
1
2
3
4
5
Date date1 = new Date();//创建对应当前时间的Date对象
date1.toString();//显示当前时区的年月日时分秒
date1.getTime();//显示时间戳(获取当前时间(距离时间戳1970年..)的毫秒数)

Date date2 = new Date(41524512L);//创建自定义时间戳的Date对象
  • Java.sql.Date

    是Java.util.Date的子类

1
Date date2 = new Date(41524512L);//只有时间戳的这个构造方法

可以通过获取时间戳的方式进行两种Date的转换

19.2.3 SimpleDateFormat类

  • java.text.SimpleDateFormat

    Date类的API不利于国际化,大部分已被废弃

    SimpleDateFormat可以对Date类进行格式化解析

1
2
3
4
5
//默认构造器pattern = "22-7-12 下午5:01"
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
//可以格式化和解析这个pattern类型的字符串
//一般常用pattern = "yyyy-MM-dd hh:mm:ss"
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
  1. 格式化:日期->字符串

    1
    2
    3
    4
    Date date = new Date();
    long ms = System.currentTimeMillis();
    //可以传Date,时间戳
    String s = simpleDateFormat.format(ms);
  2. 解析:字符串->日期

    1
    2
    3
    String str = "22-7-12 下午5:01";
    Date date1 = simpleDateFormat.parse(str);
    System.out.println(date1);

19.2.4 Calendar类

Calendar是一个抽象基类,主用于完成日期字段之间相互操作的功能

  • 创建实例

    1
    2
    //抽象类,调用静态方法得到实例
    Calendar calendar = Calendar.getInstance();
  • get set 方法

    1
    2
    3
    4
    //获取当前时间是本月的第几天
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    //直接设置calendar为本月的第20天
    calendar.set(Calendar.DAY_OF_MONTH, 20);
  • add方法

    1
    2
    //本月第几天加3天,也可以加负数,表示减
    calendar.add(Calendar.DAY_OF_MONTH, 3);
  • 和Date的相互转换

    1
    2
    3
    4
    5
    6
    7
    //getTime()
    //Calendar->Date
    Date date = calendar.getTime();

    //setTime()
    //Date->Calendar
    calendar.setTime(new Date());

19.2.4 JDK8的时间API

  • LocalDateTime
  1. 构造方法

    1
    2
    3
    4
    //当前时间.now()
    LocalDate date = LocalDate.now();//2022-07-14
    LocalTime time = LocalTime.now();//14:15:24.020
    LocalDateTime dateTime = LocalDateTime.now();//2022-07-14T14:15:24.020
    1
    2
    //自定义时间.of()有多种构造器供使用
    LocalDateTime dateTime1 = LocalDateTime.of(2020,7,14,14,54);
  2. get操作

    1
    2
    int dayOfMonth = dateTime.getDayOfMonth();
    DayOfWeek dayOfWeek = dateTime.getDayOfWeek();
  3. with操作,设置日期

    1
    LocalDateTime dateTime2 = dateTime.withDayOfMonth(25);
  4. 日期加减

    1
    2
    LocalDateTime dateTime3 = dateTime.plusDays(3);//加
    LocalDateTime dateTime4 = dateTime.minusDays(3);//减
  • Instant

    image-20220714154136806

  • DateTimeFormatter

  1. 创建对象

    1
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
  2. //将String转换为LocalDateTime的对象
    String dateTimeStr= "2016-10-25 12:00:00";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    //把Str按照formatter进行转换
    LocalDateTime localDateTime=LocalDateTime.parse(dateTimeStr,formatter);
    
    //将LocalDateTime的对象转换为String
    String format = localDateTime.format(formatter);
    
    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
    53
    54
    55



    # 19. Java8

    ## 1. 函数式接口

    ### 1. Lambda

    本质:作为接口的实例(它是一个对象)。依赖函数式接口(该接口只能有一个抽象方法)

    匿名实现类的对象可以替换为Lambda表达式

    - ->左边:Lambda形参列表(就是接口中抽象方法的形参列表)
    1. 形参的参数类型可以省略(类型推断)
    2. 若只有一个参数,小括号也可以省略



    - ->右边:Lambda体(就是重写的抽象方法的方法体)
    1. Lambda体应用{}包裹
    2. 如果该方法只有一条执行语句,可省略{},如果该条是return语句,return也可以省略





    ### 2. Functional

    函数式接口(只有一个抽象方法的接口)

    可通过注解 @FunctionalInterface 修饰

    可通过Lambda表达式来创建函数式接口的对象

    ![image-20220815104432429](https://nnu-zrh.oss-cn-hangzhou.aliyuncs.com/202307131522484.png)



    ### 3. 方法引用

    Method Reference

    本质上就是Lambda表达式

    - 对象::实例方法

    ~~~Java
    Consumer<String> con1 = str-> System.out.println(str);
    con1.accept("北京");

    //Consumer中的void accept(T t)
    //System.out对象中的void println(T t)
    Consumer<String> con2 = System.out::println;
    con2.accept("南京");
  • 类::静态方法

    1
    2
    3
    4
    5
    Comparator<Integer> com1 = (o1,o2)->o1.compareTo(o2);

    //Comparator中的int compare(T t1,T t2)
    //Integer中的int compare(T t1,T t2)
    Comparator<Integer> com2 = Integer::compareTo;
  • 类::实例·方法

    1
    2
    3
    4
    5
    BiPredicate<String,String> pre1 = (s1,s2)->s1.equals(s2);

    //BiPredicate中的Boolean test(T t1,T t2)
    //String中的Boolean t1.equals()
    BiPredicate<String,String> pre2 = String::equals;

4. 构造器引用

1
2
3
4
5
6
7
8
9
10
11
//原始写法
Supplier<Person> p1 = new Supplier<Person>() {
@Override
public Person get() {
return new Person();
}
};
//Lambda表达式
Supplier<Person> p2 = ()->new Person();
//构造器引用
Supplier<Person> p3 = Person::new;

2. StreamAPI

使用StreamAPI对集合数据进行操作,就类似于SQL进行的数据库查询

1. 创建Stream

  • 通过集合

    1
    2
    3
    4
    5
    List<Integer> list;
    //返回一个顺序流
    Stream<Integer> stream1 = list.stream();
    //返回一个并行流
    Stream<Integer> stream2 = list.parallelStream();
  • 通过数组

    1
    2
    3
    4
    int[] arr = new int[]{1,2,3};
    IntStream stream1 = Arrays.stream(arr);

    Stream<Person> stream2 = Arrays.stream(Person数组);
  • Stream.of()

    1
    Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
  • 无限流

    1
    2
    //从0开始,每次加2,截取前10个,对每个元素进行输出操作
    Stream.iterate(0, t->t+2).limit(10).forEach(System.out::println);

2. 中间操作

流就和iterator一样,进行了中间操作,然后进行了结尾操作(例如:forEach(System.out::println))。就不能再操作这个流了,它已经结束了

经过 中间操作得到的还是一个Stream

  • 筛选与切片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int[] arr = {1,2,3,4,4,6,7,8,9};
    //过滤流:筛选输出arr中的偶数
    IntStream stream = Arrays.stream(arr);
    stream.filter(e->e%2==0).forEach(e-> System.out.print(e+" "));
    System.out.println("------------------");
    //截断流:只要前两个元素
    Arrays.stream(arr).limit(2).forEach(System.out::println);
    System.out.println("------------------");
    //跳过前2个元素
    Arrays.stream(arr).skip(2).forEach(System.out::println);
    System.out.println("------------------");
    //筛选:去除重复元素:依据hashcode和equals方法
    Arrays.stream(arr).distinct().forEach(System.out::println);
    System.out.println("------------------");
  • 映射

    1
    2
    3
    4
    5
    6
    7
    8
    List<String> list = Arrays.asList("aa", "bbc", "cc", "dd11");
    //将流中的每一个元素都映射成另一个元素(大写)
    list.stream().map(String::toUpperCase).forEach(System.out::println);


    //.flatMap
    //如果map把每个元素映射成了一个Stream,那么list.stream().map(映射成Stream)返回
    //的就是stream构成的stream,而.flatMap会把各个stream拆包还是返回像上面的由元素构成的stream
  • 排序

    1
    2
    3
    4
    List<Person> list = Arrays.asList(new Person(52,"Tom"), new Person(18,"Jack"), new Person(24,"Jerry"), new Person(30,"Smith"));

    //按年龄进行排序
    list.stream().sorted((p1,p2)->-(p1.getAge()-p2.getAge())).forEach(System.out::println);

3. 终止操作

  • 匹配与查找

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //检查是否匹配所有元素
    //是否所有人年龄都大于18
    boolean allMatch = list.stream().allMatch(p -> p.getAge() > 18);

    //检查是否至少匹配一个
    //是否至少一个人年龄大于18
    boolean anyMatch = list.stream().anyMatch(p -> p.getAge() > 18);

    //findFirst 返回流中第一个元素,放在Optional容器
    Optional<Person> first = list.stream().findFirst();
    Person person = first.get();

    //count 流中元素个数

    //max

    //min

    //forEach 内部迭代
  • 规约

    1
    2
    //映射成age流,再对流中元素求和
    Optional<Integer> sum = list.stream().map(Person::getAge).reduce(Integer::sum);
  • 收集

    1
    2
    //映射成为大写后收集成一个list或者set等
    List<String> list1 = list.stream().map(String::toUpperCase).collect(Collectors.toList());

    .collect之后可以用Collectors工具类进行一系列操作

    1
    2
    3
    4
    5
    6
    7
    8
    1.转换为集合;
    List<String> custListResult = list.stream().collect(Collectors.toCollection(LinkedList::new));//转换为特定的集合LinkList

    2.转换为map;
    Map<String, Integer> mapResult = list.stream().collect(Collectors.toMap(Function.identity(), String::length));//Function.identity()就是x->x即流中元素本身

    3.连接元素;
    String joinResult = list.stream().collect(Collectors.joining());

3. Option类

避免空指针问题

20. Java9以后

1. java9

  • 模块化系统

    1
    //创建一个module.info文件来导入需要的模块
  • REPL工具

    像python一样在命令行就能交互式的编程环境

    命令行输入jshell打开该交互工具

  • 接口

    新增可以定义私有方法

  • 砖石操作符

    在8中,使用匿名对象时,后面不能省略

    image-20220817111653245

  • try结构

    java8之前,括号里不能写

    java8:

    image-20220817114018319

    java9:

    image-20220817114158873

  • String

    底层时char型数组存储,每个字符两个字节

    java9:底层为字节型数组存储,拉丁一个字节,汉字两个字节

  • 集合工厂

    快速创建只读集合

    1
    2
    Set.of();
    List.of();
  • 流转换

    image-20220817160832992

  • StreamAPI

    增强版

    image-20220817161313019

    image-20220817161611954

  • optional

    optional容器也可以转换为流

  • Nashorn引擎

    可以在JVM上运行JavaScript程序

2. Java10

  • 局部变量类型推断

    1
    2
    3
    4
    5
    var num = 10;
    var list = new ArrayList<Integer>();
    for(var num : list){

    }
  • image-20220817213314639

3. java11

  • Epsilon

  • ZGC

  • 字符串新方法

    image-20220817214852547

A. Eclipse

  • 创建一个工程:

    1. 在Package Explorer里新建一个Java project
    2. 在src目录下新建一个包(包名:com.at作者名.包名)
    3. 在包里新建一个class
  • Perspective(透视图,选择Java EE或者Java SE等)image-20220125195915662

    Quick Access(快速搜索一些工具)

  • window->preferences->General->workspace->默认编码改成UTF-8

  • 写个main,alt+/ 一下会出来选择

    syso + 回车 标准输出

  • 导入Javaproject

    1. 比如给你发了一个文件过来,你是不能直接改名的,导入到工作空间后修改才有效

    2. file->import->image-20220125204506783

      把勾打上,就在此空间复制了一份,原来的就可以删除了,不然链接的还是原来的文件

      image-20220125204556096

  • 关联Java源文件后,按住CTRL就可以查看Java的比如String的源代码,左边outline可以看到他的结构

  • 作者信息 package和method设置后 /**回车 即可显示

  • 快捷键:Ctrl+shift+/ 多行注释选中代码

    ​ Ctrl+shift+\ 取消多行注释

    ​ Ctrl+/注释取消注释

    ​ Ctrl+d 删除光标所在行代码

    ​ alt+up/down 移动光标所在行代码

    ​ ctrl+alt+down 向下复制所选区域的代码

    ​ alt+shift+s 选择generate getters and setters(生成获得和设置类属性的方法)以及重写,设置构造器等,不用手写。

  • 代码自动补全:window->preference->java->editor->content Assist

    把Auto activation trigger for Java 后面的改为.qwer….键盘上的所有键

  • debug模式:

    双击行左侧设置断点;

    image-20220320151552339

    (右上角切换Javaee透视图和debug透视图)

    image-20220320152202828

    (左边两个:直接到下一个断点;中止debug

    右边四个:进入方法内部;进入下一语句;跳出方法内部;返回方法首行)

B. 易错易混

Test

1
2
3
4
5
//创建一个数组大小为n,数组的元素是存放double[]型数组的List
List<double[]>[] graph = new LinkList[n]
for (int i = 1; i <= n; i++) {
graph[i] = new LinkedList<>();
}