Java概述

Java语言的主要特点

  • 简单性
  • 面向对象
  • 跨平台
  • 安全性
  • 多线程
  • 动态性

初始Java程序

Java源文件以 “java”结尾,此文件中最多只能有一个类被声明为public,保存时源文件名需与public类名相同,如果文件中不存在public类,源文件名无要求。

一个源文件包含几个类就可以编译出几个.class文件。

基本程序设计

常量

符号常量:final修饰的变量,一旦赋值后不可更改。

类型转换

picture 0

位运算

| 运算符 | 用法 | 说明 |
| —— | ——— | ––––– | —— |
| ~ | ~a | 按位取反 |
| & | a & b | 按位与 |
| | | a | b | 按位或 |
| ^ | a ^ b | 按位异或 |
| << | a << b | 左移 |
| >> | a >> b | 右移 |
| >>> | a >>> b | 无符号右移 |

流程控制

标准输入与Scanner类

Scanner in = new Scanner(System.in);
\\ ...
in.close();
返回值 方法 说明
boolean hasNext() 判断是否还有输入
String next() 读取一个字符串
T nextT() 读取一个T类型的值
String nextLine() 读取一行字符串
boolean hasNextT() 判断下一个输入是否为T类型
boolean hasNextT(int radix) 判断下一个输入是否为T类型,radix为进制

标准输出

System.out.println("Hello, World!");

printf方法

标志 说明
- 左对齐
+ 显示正负号
0 数字前补0
# 显示八进制或十六进制前缀
空格 正数前加空格
, 数字以逗号分隔
( 负数用括号括起来

格式化占位符

格式 说明
%d 整数
%f 浮点数
%e 科学计数法
%g 通用浮点数
%h 散列码
%s 字符串
%c 字符
%b 布尔值
%t 日期时间
%n 换行
%x 十六进制整数
%o 八进制整数
%a 十六进制浮点数

面向对象

面向对象编程的特性

封装

将对象的属性和行封装起来构成新的类型,并隐藏内部实现细节,只向用户提供对象的外部可调用操作

封装最大的好处是降低了软件系统的耦合程度。实现了代码的可重用性和可维护性。

继承

继承性是类与类之间的一种关系,通过继承,可以在无需重新编写原有类的情况下,实现代码的扩展和重用。

  • 具有父类的全部属性和行为
  • 能对继承的属性和行为进行修改和扩充。
  • 极大提升了可重用性和可维护性

多态

多态性是指在父类中定义的属性和行为被子类继承之后,可以具有不同的数据类型或表现形式。

  • 单个类:多态性在单个类中表现为方法重载:一个类可以有多个名字相同、形参列表不同的方法,在使用时由传递给它们的实参来决定使用哪个方法。
  • 在多个类中主要表现为继承结构中的方法覆盖:父类和子类中具有相同的方法头,不同的代码实现,运行时再决定调用哪个方法。

对象与构造方法

要比较两个对象的内容是否相等,即对象实体值,必须在类里实现专门的方法,所有类的父类Object中有一个方法equals()。

因对象数组中每个元素都是对象,所以每个元素都需单独实例化(还需用new实例化每个元素)

方法重载

定义方法时使用相同的方法名,不同的形参列表,叫方法重载(overloading)方法重载是实现“多态”的一种方法。

  • 形参列表不同是指参数个数不同,或者对应位置上参数类型不同。
  • 重载方法返回类型、修饰符可以相同,也可不同,它不决定是否是重载方法

定位重载函数的顺序

原则:

  • 查找同名方法,没有则报错
  • 比较形参和实参的数目是否相等,如果多个方法符合条件,这些方法进入候选集
  • 候选集中
    • 如果对应位置上的每个参数类型完全匹配,为最佳方法,
    • 如果无匹配的可以通过扩展转换找出最佳匹配方法,选择原则为:源类型与目标类型的距离越近越好。

this关键字

this表示当前对象

当通过一个对象引用调用它的成员方法时,系统会将当前对象的别名this传递到被调方法中,所以,this只能在成员方法中可见。

使用this访问对象成员

特别是局部变量和成员变量重名时,利用this可以限定某个变量是成员变量。

构造方法中,用this调用本类的另一构造方法

在一个构造方法中,调用另一个重载的构造方法:形式为:this([实参])这条语句必须是构造方法的第一条语句,且只能出现一次。

返回当前对象

在方法中,利用return this可以返回当前对象,从而可以继续调用该类或其子类的成员。

参数传递

调用方法时,先将实参赋给形参,然后再执行操作。JAVA传参数总是采用按值传递的方式,所谓值传递就是将实参值的副本传递给被调方法的形参。

基本数据类型

基本数据类型的传递是将实参的值传递给形参,形参的改变不会影响实参。

引用数据类型

  • 如果形参一直指向同一个对象,那么通过形参改变对象的内容,会影响实参。
  • 如果形参指向了另一个对象,那么通过形参改变对象的内容,不会影响实参。

static修饰符

  • 与类相关的静态成员称为类变量或类方法,
  • 与实例相关的普通成员称为实例变量或实例方法。
  • static关键字可以修饰字段、方法、语句块和类(只能修饰内部类)

static修饰字段

static字段也称类/静态数据,被类的所有对象共享。

当系统第一次准备使用该类时,系统会为该类的static字段分配内存空间,存储在方法区中。此时类变量开始生效,直到类被卸载。该类所占有的内存才垃圾回收机制回收。

static变量被该类的所有对象所共享,只能是类一级的成员,不能声
明为方法的局部变量。而实例变量则是属于一个对象实例。

static变量可以被同一类的其他方法直接访问。
其他类可以通过此static成员所属类的类名访问它,而无需先创建对象。

static修饰方法

静态方法是属于类的,而不是属于对象的。静态方法只能访问静态域和静态方法,不能访问实例域和实例方法。

静态方法属于定义它的类,而且无需创建对象就

  • 可直接通过类名访问它。
  • 通过对象引用(无论是否为null)调用,但实例方法必须通过非null的对象引用调用。

静态方法不能使用this和super关键字,因为静态方法在对象创建之前就已经存在。

static修饰代码块

静态语句块不属于任何一个方法,当类被加载时,虚拟机会执行静态块中的语句,且在类型的生命周期中只执行一次。
所以,可以利用静态块在类的加载阶段做一些初始化操作,如初始化静态数据。

访问控制

| 修饰符 | 权限 | 修饰对象 ||| 可见性 ||||

^^ ^^ 类 字段 方法 同类 同包 不同包子类 不同包非子类
public 公有 ✔️ ✔️ ✔️ * * * *
protected 保护 ✔️ ✔️ * * *
默认 包访问 ✔️ ✔️ ✔️ * *
private 私有 ✔️ ✔️ *

继承

  • Java只支持单继承,默认父类是Object
  • 父类中声明为private的字段和方法,子类不可见。
  • 子类可以添加字段和方法,可以通过定义重名的属性隐藏父类属性(比较少用),也可以通过定义重名方法覆盖父类方法。
  • 子类不会自动获得父类的构造方法,子类在构造方法中如果要调用父类的构造方法,可以使用super调用,而且该调用必须是子类构造方法的第一条语句。

final修饰符

final常量

final修饰的常量只能赋值一次,之后不能再修改。它可以是局部常量也可以是成员常量。

  • 局部常量必须在读取之前被赋值,

  • 成员常量必须在声明时或者在构造方法中被初始化。

  • 基本数据类型的常量值不能改变

  • 引用类型不能指向其他对象,但可以改变对象的内容

final常量的几个常见用法

  • blank finals (空final)
    当由同一个类生成的不同对象希望可以有不同的final字段值时,可以在定义该字段时只声明不赋值,通过构造方法对每个对象的final字段进行赋值。
  • final 参数
    final修饰形参,如果修饰的是基本数据类型,表示形参在传入后值不变,如果修饰的是引用类型,表示形参的引用值被赋值后,就不会指向新的对象。
  • static final 静态常量
    是类一级的全局常量,只用于修饰字段而不能用于局部变量,它需要在类型被加载时就完成初始化操作。因此,一定要在定义时或者在static块中就给定初始值。

final类

final修饰的类不能被继承

final方法

final修饰的方法不能被子类重写

类的进阶设计

对象类型转换

向上转型

使用向上转型的对象引用可以调用父类中的方法,但不能调用子类中扩展的方法。

使用向上转型的对象引用可以调用父类类型中的成员,但不能调用子类类型中扩展的成员。这是因为Java对象有两种类型:

  • 编译时类型:是对象引用的类型,其能调用的方法集由引用类型决定。
  • 运行时类型:是对象本身的类型,由存放在对象结构中的类型指针所决定,它决定了运行时运行哪个类型的方法。

向下转型

向下转型是将父类对象转换为子类对象,需要强制类型转换,但是在转换之前需要使用instanceof判断是否可以转换。

Father f = new Son();
if (f instanceof Son) {
  Son s = (Son) f;
}

// jdk 16
if (f instanceof Son s) {
  s.method();
}

方法重写

方法重写是指子类重新定义父类的方法,方法名、参数列表、返回类型必须与父类中被重写的方法一致。

  • 访问权限:子类方法的访问权限不能小于父类方法的访问权限。
  • 返回类型:子类方法的返回类型必须与父类方法的返回类型一致,或者是父类方法返回类型的子类。
  • 异常:子类方法不能抛出比父类方法更多的异常,但可以抛出更少的异常,或者不抛出异常。
  • final方法不能被重写;不能一个是static方法,一个是实例方法。

动态绑定

Java绑定规则如下:

  • 被final、static、private修饰的方法执行静态绑定,与编译时类型的方法体进行绑定。

  • 其余实例方法执行动态绑定,与对象的运行时类型的方法体进行绑定。

  • 成员变量(包括静态变量和实例变量)执行静态绑定,与引用类型的成员变量绑定。

  • 静态绑定:是指在程序执行前由编译器或连接程序绑定

  • 动态绑定:是指在程序执行时由运行时JVM通过对象的类型指针找到对象的实际类型,然后调用相应的方法。

抽象方法

抽象方法是没有方法体的方法,只有声明,没有实现。

  • 访问权限:抽象方法不能是private,因为private方法不能被继承。
  • 不能被final和static修饰。
public abstract void foo();

抽象类

抽象类是包含抽象方法的类,抽象类不能被实例化,只能被继承。

  • 抽象类可以包含零个到多个成员变量、普通方法,也可以含零个到多个抽象方法。
  • 抽象类不能被final修饰。
  • 一个类只要有一个方法是抽象方法,这个类就要定义成抽象类。

接口

接口是一种特殊的抽象类,接口中的方法都是抽象方法,接口中的成员变量都是public static final类型的常量。

public interface InterfaceName {
  // 常量
  public static final int MAX = 100;
  // 抽象方法
  public abstract void foo();
}
  • 所有的方法都是公开的抽象方法,所以,方法可以省略public和abstract关键字。
  • 所有的字段都是公开的静态常量,所以,字段可省略public static final
    关键字。需要定义时赋初值。
  • 类似抽象类,接口不能被实例化,只能被实现。
  • 接口可以继承其他接口(不能是类),而且支持多继承,多个接口用,分割。这一点与类不同。

重写接口方法时,访问权限只能是public,因为接口中的方法默认是public的。

接口与抽象类的区别

抽象类与接口是进行抽象的两种机制,有相似性,也有区别。

  • 语法上:抽象类用关键字abstract class定义,可定义成员变量和非抽象方法;接口用interface定义,接口内只有公开的静态常量、成员方法都是公开的,且只能是抽象方法、默认方法或静态方法。
  • 使用上:抽象类是用来被单继承的,而一个类可以实现多个接口。
  • 设计上:抽象类作为父类,与子类之间存在“is-a”关系,即父子类本质上是一种类型。接口只能表示类支持接口的行,具有接口的功能。因此接口和实现类之间表示的是“like-a”关系。因此,在设计上,如果父子类型本质上是一种类型,那父类可设计成抽象类,如果子类型只是想额外具有一些特性,则可以将父类型设计成接口,而且这些接口不易过大,应该设计成多个专题的小接口。这也是面向对象设计的一个重要原则—接口隔离原则。

内部类

  • 内部类仍是一个独立的类,在编译外部类时,内部类也会被编译成独立的class文件,文件名前面冠以外部类的类型和$符号,如OuterClass$InnerClass。
  • 内部类是外部类的成员,因此,内部类可以访问外部类的成员,无论是否为private。如果内部类声明成static,相应的,只能访问外部类的静态成员。
  • 内部类可作为外部类的成员,也可作为方法的成员(局部内部类)。如果作为外部类的成员,则可以使用4种访问权限修饰符,如果作为方法成员则没有访问权限修饰符。

实例内部类

  • 实例内部类是最常见的内部类,它是外部类的一个成员,可以访问外部类的所有成员。

  • 在外部类的静态方法和外部类以外的地方创建内部类的实例,需要先创建外部类的实例,再通过外部类的实例创建内部类的实例。

    OuterClass outer = new OuterClass();
    OuterClass.InnerClass inner = outer.new InnerClass();
  • 在外部类中不能直接访问内部类的实例成员,需要通过内部类的实例访问。

  • 在内部类中可以直接访问外部类的实例成员,如果内部类和外部类有同名成员,可以通过OuterClass.this.member访问外部类的成员。

  • 内部类不能与外部类重名

静态内部类

静态内部类是static修饰的内部类,它是外部类的一个静态成员,它与所属的外部类而不是外部对象相关联。

  • 通过外部类名直接访问静态内部类的静态成员

    OuterClass.InnerClass inner = new OuterClass.InnerClass();
  • 静态内部类中可定义静态成员和实例成员。外部类以外的其他类可通过类名访问静态内部类中的静态成员,如果要访问静态内部类中的实例成员,则必须通过静态内部类的实例。

  • 类似于类的静态方法,静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。

  • 接口中可以定义内部类,且默认是static内部类,这种类可以被某个接口的所有不同实现所共用。

局部内部类

  • 局部内部类是指定义在方法内的内部类。其有效范围只在定义它的方法内。
  • 局部内部类遵循如下规则:
    • 局部内部类与局部变量一样,不能使用访问修饰符和static修饰符。
    • 在局部内部类中可以访问外部类的所有成员。
    • 在局部内部类中只可以读取方法常量,或初始化后不再改变的变量。
    • 如果方法中的成员与外部类成员同名,则可用OuterClass.this访问外部类中的实例成员 OuterClass.this.MemberName或用OuterClass.MemberName访问外部类的静态成员。

匿名内部类

  • 匿名类是指没有类名只有类体的内部类,如果程序定义某个类却只需要创建一个对象,这时可以考虑使用匿名内部类。
  • 由于匿名类没有类名,所以创建匿名类的对象时需要用到该匿名类的父类或接口,而且匿名类的定义和对象创建是同时进行的,因此,在定义的同时,使用new语句来声明对象。

匿名内部类的特点:

  • 匿名类和局部内部类一样,可以访问外部类的所有成员。其他局部内部类的特性也适用于匿名内部类。
  • 匿名类没有名字,所以不能定义构造方法,但可定义非静态字段,重写父类型方法。
  • 匿名内部类编译后对应的字节码文件名为:外部类$数字序号(序号从1开始)。
  • 匿名类常用方式是向方法传参,当匿名内部类重写的父类(接口)只有一个方法时,建议使用Lambda表达式。

异常处理

异常类

  • 运行时异常(Runtime Exception):是一种不检查异常,是程序员编程时的错误,如数组越界、空指针等。
  • 受检异常(Checked Exception):是一种检查异常,是程序员无法控制的异常,如文件不存在、网络中断等。

常用异常类

异常类 描述
ArithmeticException 算术异常
ArrayIndexOutOfBoundsException 数组下标越界异常
NullPointerException 空指针异常
FileNotFoundException 文件未找到异常
IOException 输入输出异常

异常处理机制

  • 声明异常
    不捕获异常,而只是声明方法有可能抛出的异常,从而让该方法的上层调用方法捕获异常。
  • 捕获处理异常
    Java运行时系统捕获异常并找到相应异常的捕获处理代码并运行,这一过程称为捕获处理异常。如果系统找不到捕获异常代码,程序将终止。

捕获处理异常

说明:

  • try语句:发生异常时,将跳过try块中异常点后面的语句,且异常处理需要更多的时间和资源。因此,应当仔细分析代码,尽量缩减try代码块。
  • catch语句可以有零个或多个,finally语句可以有零个或一个。
  • catch语句:设计捕获异常代码要注意其顺序,按照从“特殊到一般”的顺序来设计。将子类的catch块放在前面,父类的catch块放在后面。
  • 从java7开始,多个异常可以写在一个catch中,它们之间用竖线隔开,但需要注意,用|操作符合并的异常不要出现互为父子的关系。
  • finally语句是可选项。如果有该语句,无论是否捕获或处理异常,即使try或者catch块中包含break或return语句,finally块里的语句也会被执行。finally语句一般用来在最后做一些资源回收工作,比如在try语句中打开了文件流,可以在finally中确保文件被有效关闭。
方法 描述
getMessage()->String 返回异常的详细信息字符串
toString()->String 返回异常类全名+异常信息
printStackTrace() 打印异常的堆栈信息
getStackTrace()->StackTraceElement[] 返回和异常对象相关的堆栈跟踪元素数组

带资源的try语句

可以被自动关闭的资源有一个前提,这个资源的类已经实现了java.lang.AutoCloseable接口,这个接口有一个方法:void close()。

throw抛出异常和throws声明异常

抛出异常

检测到错误的程序可以创建一个合适的异常对象并抛出它,这就称为抛出一个异常。
Java中有两种方法抛出异常。

  • Java运行时环境自动抛出异常:系统定义的RuntimeException类及其子类和Error都可以由系统自动抛出。
  • 语句throw抛出异常:用户程序想在一定条件下显式抛出异常,这必须借助于throw语句抛出。Java用throw语句抛出异常。

声明异常

  • 定义方法时,如果方法可能出现异常,但该方法不想或不能自己捕获处理这种异常,那就必须在声明方法时用throws声明可能发生的异常。
  • 对于不受查异常(Runtime Exception和Error),Java不要求在方法头中显示声明,但是,其它异常就一定要在方法头中显示声明。

常用类

Object类

  • Object类是所有类的父类,所以所有类都可以调用Object类的方法。
  • Object类中的clone()方法是浅拷贝,即只拷贝对象的引用,而不拷贝对象的内容。
  • Object类中的equals()方法是比较两个对象的内容是否相等,如果没有重写equals()方法,那么比较的是两个对象的引用是否相等。
  • Object类中的hashCode()方法是返回对象的哈希码,哈希码是对象的内存地址经过哈希算法得到的一个整数,用于快速查找对象。
  • Object类中的toString()方法是返回对象的字符串表示,如果没有重写toString()方法,那么返回的是对象的哈希码。

String类

  • String类是不可变的,一旦创建,它的值就不能被改变。

创建字符串

  • 直接赋值:String str = "Hello, World!";
  • 通过构造方法:String str = new String("Hello, World!");
  • 通过字符数组:char[] ch = {'H', 'e', 'l', 'l', 'o'}; String str = new String(ch);
  • 通过字节数组:byte[] b = {72, 101, 108, 108, 111}; String str = new String(b);
String str = "Hello, World!";
String str1 = "Hello, World!";
String str2 = new String("Hello, World!");
String str3 = new String("Hello, World!");
System.out.println(str == str1); // true
System.out.println(str == str2); // false
System.out.println(str2 == str3); // false

字符串常用方法

方法 描述
length()->int 返回字符串的长度
charAt(int index)->char 返回指定索引处的字符
indexOf(String str)->int 返回指定子字符串在此字符串中第一次出现的索引
lastIndexOf(String str)->int 返回指定子字符串在此字符串中最右边出现的索引
substring(int beginIndex, int endIndex)->String 返回一个新的字符串,它是此字符串的一个子字符串
concat(String str)->String 将指定字符串连接到此字符串的结尾
replace(char oldChar, char newChar)->String 返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的
split(String regex)->String[] 根据给定正则表达式的匹配拆分此字符串
trim()->String 返回字符串的副本,忽略前导空白和尾部空白
endsWith(String suffix)->boolean 测试此字符串是否以指定的后缀结束
startsWith(String prefix)->boolean 测试此字符串是否以指定的前缀开始
format(String format, Object... args)->String 使用指定的格式字符串和参数返回格式化的字符串
toLowerCase()->String 使用默认语言环境的规则将此String中的所有字符都转换为小写
toUpperCase()->String 使用默认语言环境的规则将此String中的所有字符都转换为大写
equals(Object obj)->boolean 将此字符串与指定对象进行比较

正则表达式

字符类
字符 描述
\d 匹配一个数字字符
\D 匹配一个非数字字符
\w 匹配一个字母、数字或下划线字符,不包括$
\W 匹配一个非字母、数字或下划线字符
\s 匹配一个空白字符
\S 匹配一个非空白字符
. 匹配除换行符\n之外的任何字符
\p{Lower} 匹配一个小写字母
\p{Upper} 匹配一个大写字母
\p{ASCII} 匹配一个ASCII字符
\p{Alpha} 匹配一个字母字符
\p{Digit} 匹配一个数字字符
\p{Alnum} 匹配一个字母或数字字符
\p{Punct} 匹配一个标点字符
\p{Graph} 匹配一个可打印字符,不包括空格
\p{Print} 匹配一个可打印字符,包括空格
\p{Blank} 匹配一个空格或制表符
\p{Cntrl} 匹配一个控制字符
量词
字符 描述
X? 匹配X零次或一次
X* 匹配X零次或多次
X+ 匹配X一次或多次
X{n} 匹配X恰好n次
X{n,} 匹配X至少n次
X{n,m} 匹配X至少n次,但不超过m次
运算符
字符 描述
[] 表示[]中的任意一个字符
| 表示|两边的任意一个字符
^ 1. 表示输入字符串的开始位置\n 2. 在[]使用表示不接受[]中的字符
$ 表示输入字符串的结束位置
() 表示一个子表达式,可以获取供以后使用,用$1、$2等表示
&& 表示交集

泛型集合

泛式数据结构

  • Collection接口
    • List接口
      • ArrayList类
      • LinkedList类
    • Set接口
      • HashSet类
      • TreeSet类
    • Queue接口
      • PriorityQueue类
  • Map接口
    • HashMap类
    • TreeMap类

集合遍历

List<String> list = new ArrayList<>();

// 1. 使用迭代器遍历
Iterator<String> it = list.iterator();
while (it.hasNext()) {
  System.out.println(it.next());
}

// 2. 使用增强for循环遍历
for (String str : list) {
  System.out.println(str);
}

// 3. 使用forEach方法遍历
list.forEach(str -> System.out.println(str));

// 4. 使用for循环遍历
for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}

Map<String, String> map = new HashMap<>();

// 键值对遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
  System.out.println(entry.getKey() + " : " + entry.getValue());
}

// 键遍历
for (String key : map.keySet()) {
  System.out.println(key);
}

// 值遍历
for (String value : map.values()) {
  System.out.println(value);
}

Arrays类(了解)

  • Arrays类是java.util包中的一个工具类,提供了一系列静态方法用于操作数组。
方法 描述
asList(T... a)->List<T> 将数组转换为List
binarySearch(T[] a, T key)->int 对数组进行二分查找,需要先排序
copyOf(T[] original, int newLength)->T[] 复制指定的数组,截取或用null填充(如果需要)
sort(T[] a)->void 对数组进行排序
fill(T[] a, T val)->void 用指定的值填充数组
equals(T[] a, T[] a2)->boolean 比较两个数组是否相等
compare(T[] a, T[] a2)->int 比较两个数组的大小
stream(T[] array)->Stream<T> 返回数组的流

Collections类(了解)

  • Collections类是java.util包中的一个工具类,提供了一系列静态方法用于操作集合。
方法 描述
sort(List<T> list)->void 对列表进行排序
reverse(List<T> list)->void 反转列表中的元素
shuffle(List<T> list)->void 随机排列列表中的元素
swap(List<T> list, int i, int j)->void 交换列表中指定位置的元素
fill(List<? super T> list, T obj)->void 用指定元素替换列表中的所有元素
copy(List<? super T> dest, List<? extends T> src)->void 将所有元素从一个列表复制到另一个列表
max(Collection<? extends T> coll)->T 返回集合中的最大元素
min(Collection<? extends T> coll)->T 返回集合中的最小元素
binarySearch(List<? extends T> list, T key)->int 对列表进行二分查找,需要先排序
disjoint(Collection<?> c1, Collection<?> c2)->boolean 如果两个指定的集合没有相同的元素,则返回true

IO流

File类

  • File类是java.io包中的一个类,用于表示文件或目录的路径名。

文件流

创建FileInputStream的两种方式

File file = new File("test.txt");
FileInputStream fis = new FileInputStream(file);
// ********** or **********
FileInputStream fis2 = new FileInputStream("test.txt");
  • 创建FilelnputStream实例对象时,指定的文件应当是存在和可读的。
  • 创建FileOutputStream实例对象时,如果指定的文件己经存在,这个文件中原来内容将被覆盖清除,可以指定还不存在的文件名

文件拷贝

FileInputStream和FileOutputStream
public static void copyFile(String src, String dest) {
  try (FileInputStream fis = new FileInputStream(src);
       FileOutputStream fos = new FileOutputStream(dest)) {
    byte[] buffer = new byte[fis.available()];
    fis.read(buffer);
    fos.write(buffer);
  } catch (IOException e) {
    e.printStackTrace();
  }
}
FileReader和FileWriter
public static void copyFile(String src, String dest) {
  try (FileReader fr = new FileReader(src);
       FileWriter fw = new FileWriter(dest)) {
    char[] buffer = new char[1024];
    int len;
    while ((len = fr.read(buffer)) != -1) {
      fw.write(buffer, 0, len);
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
}

缓冲流

  • 缓冲流是对文件流的包装,提供了缓冲功能,可以提高读写文件的效率。

BufferedInputStream和BufferedOutputStream

FileInputStream fis = new FileInputStream("test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);

FileOutputStream fos = new FileOutputStream("test.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);

BufferedReader和BufferedWriter

public static void copyFile(String src, String dest) {
  try (BufferedReader br = new BufferedReader(new FileReader(src));
       BufferedWriter bw = new BufferedWriter(new FileWriter(dest))) {
    String line;
    while ((line = br.readLine()) != null && !line.isEmpty()) {
      bw.write(line);
      bw.newLine();
    }
    bw.flush();
  } catch (IOException e) {
    e.printStackTrace();
  }
}

字节字符转换流

  • 字节流和字符流之间的转换,可以使用InputStreamReader和OutputStreamWriter。

InputStreamReader和OutputStreamWriter

public static void main(String[] args) {
  try (InputStreamReader isr = new InputStreamReader(new FileInputStream("test.txt"), StandardCharsets.UTF_8);
     OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test2.bak"), StandardCharsets.UTF_8)) {
    char[] buffer = new char[1024];
    int len;
    while ((len = isr.read(buffer)) != -1) {
      osw.write(buffer, 0, len);
    }
  } catch (IOException e) {
    throw new RuntimeException(e);
  }
}

线程

线程概念

程序

程序是一段静态代码,是指令与数据的集合。通常是外存上保存的可执行的二进制文件。

进程 (Process)

进程是程序的一次运行活动。它对应从代码加载、执行到结束的一个过程,进程可以申请和拥有系统一整套资源,是系统进行资源分配和调度的基本单位。

线程 (Thread)

线程是进程中能够独立执行的执行序列。一个进程可以产生多个线程,线程也有创建、存活到消亡的生命周期,每个线程都有独立的运行栈和程序计数器,是CPU调度的最小单位。

进程与线程关系

一个进程中的所有线程共享相同的地址空间和这个进程所拥有的操作系统资源。

线程的创建

继承Thread类

public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println("MyThread is running...");
  }
}

public class Main {
  public static void main(String[] args) {
    MyThread myThread = new MyThread();
    myThread.start();
  }
}

实现Runnable接口

public class MyRunnable implements Runnable {
  @Override
  public void run() {
    System.out.println("MyRunnable is running...");
  }
}

public class Main {
  public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    thread.start();
  }
}

实现Callable接口和FutureTask类

public class MyCallable implements Callable<String> {
  @Override
  public String call() throws Exception {
    return "MyCallable is running...";
  }
}

public class Main {
  public static void main(String[] args) {
    MyCallable myCallable = new MyCallable();
    FutureTask<String> futureTask = new FutureTask<>(myCallable);
    new Thread(futureTask).start();
    try {
      System.out.println(futureTask.get());
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }
}

图形用户界面

JFrame类

  • JFrame是Swing中的顶层容器,是一个窗口,可以包含其他GUI组件。
  • 每个GUI组件只能被添加到一个容器中。
  • 创建JFrame对象后,默认情况下布局管理器是BorderLayout
  • Swing中事件处理和绘画代码都在一个单独的线程中执行。该线程确保了事件处理器都能串行的执行,并且绘画过程不会被事件打断。因此,在main方法或其他启动界面的线程,尽量使用SwingUtilities.invokeLater(Runnable doRun) 启动线程来操作界面。

JFrame的常用方法

方法 描述
getContentPanel()->Container 返回JFrame的内容面板
setVisible(boolean b)->void 设置窗口是否可见
setTitle(String title)->void 设置窗口标题
setBounds(int x, int y, int width, int height)->void 设置窗口的位置和大小
resize(int width, int height)->void 设置窗口的大小
setLocation(int x, int y)->void 设置窗口的位置
add(Component comp)->void 添加组件到窗口中心
add(Component comp, Object constraints)->void 添加组件到窗口中心,并设置布局约束
pack()->void 调整窗口大小,以适应其子组件的首选大小
hide()->void 隐藏窗口
setDefaultCloseOperation(int operation)->void 设置窗口关闭操作

布局管理器

  • java.awt包中的布局管理器
  • 容器中的组件在容器中的大小和位置是由容器的布局管理器(layoutmanager)来布置的,这样能根据不同的屏幕自动进行排版。
  • 每个容器中都有默认的布局管理器,缺省的布局管理器为:
    • 所有窗口:BorderLayout(文件对话框除外)
    • 所有面板(包括Applet):FlowLayout
      但容器组件可以通过setLayout方法设置或取消容器的布局管理器,其语法格式为:
void setLayout(LayoutManager mgr)

FlowLayout

  • FlowLayout是java.awt包中的布局管理器,将容器中的组件按照添加的顺序从左到右排列,当一行排满时,自动换行。
  • 构造方法:FlowLayout()、FlowLayout(int align)、FlowLayout(int align, int hgap, int vgap)
  • align:对齐方式,有FlowLayout.LEFT、FlowLayout.CENTER、FlowLayout.RIGHT
  • hgap:水平间距,vgap:垂直间距

BorderLayout

  • BorderLayout是java.awt包中的布局管理器,是JFrame的默认布局管理器。
  • BorderLayout将容器分为5个区域:NORTH、SOUTH、WEST、EAST、CENTER。
  • BorderLayout的构造方法:BorderLayout(int hgap, int vgap),hgap和vgap分别是水平和垂直间距。
  • add(Component comp, Object constraints)方法可以设置组件的位置,constraints是一个字符串,可以是NORTH、SOUTH、WEST、EAST、CENTER

GridLayout

  • GridLayout是java.awt包中的布局管理器,将容器分为行和列,每个单元格中放置一个组件。
  • GridLayout的构造方法:GridLayout(int rows, int cols, int hgap, int vgap),rows和cols分别是行数和列数,hgap和vgap分别是水平和垂直间距。

CardLayout

  • CardLayout是java.awt包中的布局管理器,可以在容器中放置多个组件,但只能显示一个组件。
  • CardLayout的构造方法:CardLayout(int hgap, int vgap),hgap和vgap分别是水平和垂直间距。
  • CardLayout添加组件的方法:addLayoutComponent(Component comp, Object constraints),constraints是一个字符串,用于标识组件。
  • CardLayout显示组件的方法:show(Container parent, String name),parent是容器,name是组件的标识。

GridBagLayout

  • GridBagLayout是java.awt包中的布局管理器。

事件处理

事件处理机制

picture 2

应用委托模型编写事件处理程序一般包含以下几个步骤:

  • 确定事件源应关注的事件。
  • 设计处理各事件的事件监听器类。
  • 创建事件监听器对象。
  • 调用事件源的addXxxListener方法事件源注册事件监听器
事件源

能够产生事件的组件都可以成为事件源,例如按钮、菜单、文本框等。

  • 事件源通常提供注册和注销事件监听器(Event Listener)的方法,注册/注销监听器的方法:
    • 事件源对象.addXXXListener(监听器对象)
    • 事件源对象.removeXXXListener(监听器对象)
事件对象

事件对象通常由用户操作触发,由Java虚拟机产生并传播的对象。
例如:点击按钮产生的事件(ActionEvent),按下某个键产生的事件(KeyEvent),关闭窗口产生的事件(WindowEvent)

事件监听器

事件监听器(Event Listener)用于接收和处理事件的对象。

Java中采用委托模型的方式处理事件。即事件产生以后,不是由事件源处理事件,而是将事件委托给第三方对象——事件监听器来处理。

事件监听器能够工作必须满足两个要求:

  • 事件监听器实现了处理某种事件的接口方法;
  • 需要将事件监听器注册到事件源中,从而与事件源建立关联。

根据事件源产生事件的类型和需要,可以为事件源注册一个或多个监视器,又称为注册监视器。注册监视器的方法:事件源对象.addXXXListener(监视器)(XXX为对应的事件类型)。

事件处理方法

事件处理方法是事件监听器接口中的方法,用于处理事件。

事件和事件分类

  • 低级事件:低级事件是指基于组件和容器的事件,当一个组件上发生事件,如鼠标的进入、点击、拖放等,或组件的窗口开关等时,触发了组件事件。如ComponentEvent(组件事件:组件尺寸的变化、移动)、ContainerEvent(容器事件:组件增加、移动)、FocusEvent(焦点事件:焦点的获得和丟失)、KeyEvent(键盘事件:键按下、释放)、MouseEvent(鼠标事件:鼠标单击、移动)、WindowEvent(窗口事件:关闭窗口、窗口闭合、图标化)等。
  • 高级事件(也称语意事件):高级事件是基于语义的事件,它可以不和特定的动作相关联。是用来描述用户操作所产生的结果,低级事件是高级事件的基础。如ActionEvent(动作事件:按钮按下,TextField中按Enter键)、AdjustmentEvent(调节事件:在滚动条上移动滑块以调节数值)、ItemEvent(项目事件:选择项目,不选择“项目改变”)、TextEvent(文本事件:文本对象改变)等。

事件监听器

事件监听器的实现,有两种方法:

  • 实现监听器接口:implements XXXListener
    • 在事件源和事件监听器对象中进行约定的接口。
    • 事件监听器接口的名称与事件类的名称是相对的,例如:KeyEvent事件类的监听器接口名为KeyListener
  • 扩展监听适配类:extends XXXAdapter,JDK中也提供了大多数事件监听器接口的最简单的实现类,称之为事件适配器(Adapter) 类.
事件类型 监听接口 接口中的方法 适配器类
ActionEvent ActionListener actionPerformed(ActionEvent) 无
AdjustmentEvent AdjustmentListener adjustmentValueChanged(AdjustmentEvent) 无
ItemEvent ItemListener itemStateChanged(ItemEvent) 无
TextEvent TextListener textValueChanged(TextEvent) 无
MouseEvent MouseListener mouseClicked(MouseEvent) MouseAdapter
^^ ^^ mouseEntered(MouseEvent) ^^
^^ ^^ mouseExited(MouseEvent) ^^
^^ ^^ mousePressed(MouseEvent) ^^
^^ ^^ mouseReleased(MouseEvent) ^^
MouseEvent MouseMotionListener mouseDragged(MouseEvent) MouseMotionAdapter
^^ ^^ mouseMoved(MouseEvent) ^^
KeyEvent KeyListener keyPressed(KeyEvent) KeyAdapter
^^ ^^ keyReleased(KeyEvent) ^^
^^ ^^ keyTyped(KeyEvent) ^^
FocusEvent FocusListener focusGained(FocusEvent) FocusAdapter
^^ ^^ focusLost(FocusEvent) ^^
WindowEvent WindowListener windowActivated(WindowEvent) WindowAdapter
^^ ^^ windowClosed(WindowEvent) ^^
^^ ^^ windowClosing(WindowEvent) ^^
^^ ^^ windowDeactivated(WindowEvent) ^^
^^ ^^ windowDeiconified(WindowEvent) ^^
^^ ^^ windowIconified(WindowEvent) ^^
^^ ^^ windowOpened(WindowEvent) ^^
DocumentEvent DocumentListener changedUpdate(DocumentEvent) 无
^^ ^^ removeUpdate(DocumentEvent) ^^
^^ ^^ insertUpdate(DocumentEvent) ^^

实现接口需要实现接口中的所有方法,而适配器类中的方法都是空方法,可以选择性的实现。

回调与事件监听器的实现

  • 回调是一种编程模式,是指在一个函数中调用另一个函数。
  • 在委托事件处理模型中,类A相当于事件源,类B相当于事件监视器,实现上述目的的设计方式有下面两类。
    • 用闭包(closure)类实现事件监视器。可以用事件源的内部类的方式创建事件监视器;也可以把事件源和事件监视器类合而为一;如果监视器只有一个函数,还可以用Lambda表达式简写监视器。
    • 用外部类实现事件监视器。这时监视器B为了调用事件源A的回调方法,需要给监视器B传入事件源A的引用。
事件源和事件监听器合二为一
public class MyFrame extends JFrame implements ActionListener {
  private JButton button;

  public MyFrame() {
    button = new JButton("Click me");
    button.addActionListener(this);
    add(button);
    pack();
    setVisible(true);
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    System.out.println("Button clicked");
  }

  public static void main(String[] args) {
    new MyFrame();
  }
}
匿名内部类实现事件监听器
public class MyFrame extends JFrame {
  private JButton button;

  public MyFrame() {
    button = new JButton("Click me");
    button.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        System.out.println("Button clicked");
      }
    });
    add(button);
    pack();
    setVisible(true);
  }

  public static void main(String[] args) {
    new MyFrame();
  }
}
Lambda表达式实现事件监听器
public class MyFrame extends JFrame {
  private JButton button;

  public MyFrame() {
    button = new JButton("Click me");
    button.addActionListener(e -> System.out.println("Button clicked"));
    add(button);
    pack();
    setVisible(true);
  }

  public static void main(String[] args) {
    new MyFrame();
  }
}
外部类实现事件监听器
public class MyFrame extends JFrame {
  private JButton button;

  public MyFrame() {
    button = new JButton("Click me");
    button.addActionListener(new MyActionListener());
    add(button);
    pack();
    setVisible(true);
  }

  public static void main(String[] args) {
    new MyFrame();
  }

}

class MyActionListener implements ActionListener {
  @Override
  public void actionPerformed(ActionEvent e) {
    System.out.println("Button clicked");
  }
}

网络编程

TCP/IP通信

创建Socket对象

方法 描述
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定IP地址的指定端口号
Socket() 创建一个未连接的套接字
getInputStream()->InputStream 返回此套接字的输入流
getOutputStream()->OutputStream 返回此套接字的输出流
close()->void 关闭此套接字
connect(SocketAddress endpoint)->void 连接到服务器
getInetAddress()->InetAddress 返回此套接字连接的远程主机的地址
getPort()->int 返回此套接字连接的远程端口号
getLocalAddress()->InetAddress 返回此套接字绑定到的本地地址
getLocalPort()->int 返回此套接字绑定到的本地端口号
getRemoteSocketAddress()->SocketAddress 返回此套接字连接的远程地址

打开IO流

ServerSocket类的构造方法和方法

构造方法 描述
ServerSocket(int port) 创建绑定到指定端口的服务器套接字
ServerSocket(int port, int backlog) 创建绑定到指定端口的服务器套接字,并指定最大连接数
ServerSocket(int port, int backlog, InetAddress bindAddr) 创建绑定到指定端口的服务器套接字,并指定最大连接数和本地地址
accept()->Socket 侦听并接受到此套接字的连接
close()->void 关闭此套接字
bind(SocketAddress endpoint)->void 将服务器套接字绑定到指定的端口
ServerSocket server = null;
try {
  server = new ServerSocket(8888);

} catch (IOException e) {
  e.printStackTrace();
}
Socket socket = null;
try {
  // accept()方法会阻塞,直到有客户端连接,
  // 返回一个Socket对象同客户端进行交互
  socket = server.accept();
} catch (IOException e) {
  e.printStackTrace();
}

C/S模式一对一TCP通信

  • 服务器端
public class Server {
  public static void main(String[] args) {
    ServerSocket server = null;
    Socket socket = null;
    BufferedReader is = null;
    PrintWriter os = null;
    Scanner sin = null;
    try {
      String line;
      server = new ServerSocket(8888);
      socket = server.accept();
      is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      os = new PrintWriter(socket.getOutputStream());
      sin = new Scanner(System.in);
      System.out.println("Client:" + is.readLine());
      line = sin.nextLine();
      while (!line.trim().equals("bye")) {
        os.println("Server:" + line);
        os.flush();
        System.out.println("Client:" + is.readLine());
        line = sin.nextLine();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        if (server != null) {
          server.close();
        }
        if (socket != null) {
          socket.close();
        }
        if (is != null) {
          is.close();
        }
        if (os != null) {
          os.close();
        }
        if (sin != null) {
          sin.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  • 客户端
public class Client {
  public static void main(String[] args) {
    Socket socket = null;
    Scanner sin = null;
    PrintWriter os = null;
    BufferedReader is = null;
    try {
      socket = new Socket("127.0.0.1", 8888);
      sin = new Scanner(System.in);
      os = new PrintWriter(socket.getOutputStream());
      is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      String line;
      line = sin.nextLine();
      while(!line.trim().equals("bye")) {
        os.println(line);
        os.flush();
        System.out.println("Server:" + is.readLine());
        line = sin.nextLine();
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        if (socket != null) {
          socket.close();
        }
        if (sin != null) {
          sin.close();
        }
        if (os != null) {
          os.close();
        }
        if (is != null) {
          is.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

C/S模式多对一TCP通信

  • 服务器端
public class Server {
  static int clientNum = 0;
  static ArrayList<Socket> clients = new ArrayList<>();
  public static void main(String args[]) throws IOException {
    ServerSocket server = null;
    boolean listening = true;
    try {
      server = new ServerSocket(8888);
    } catch (IOException e) {
      System.err.println("Could not listen on port: 8888.");
      System.exit(-1);
    }
    while (listening) {
      Socket socket = server.accept();
      clients.add(socket);
      new ServerThread(socket, clientNum).start();
      clientNum++;
    }
    server.close();
  }
}
  • 服务器线程
class ServerThread extends Thread {
  private Socket socket = null;
  private int clientNum = 0;
  public ServerThread(Socket socket, int clientNum) {
    super("ServerThread");
    this.socket = socket;
    this.clientNum = clientNum + 1;
  }
  public void run() {
    BufferedReader is = null;
    PrintWriter os = null;
    Scanner sin = null;
    try {
      String line;
      is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      os = new PrintWriter(socket.getOutputStream());
      sin = new Scanner(System.in);
      String outStr = is.readLine();
      System.out.println("Client " + clientNum + ":" + outStr);
      line = sin.nextLine();
      while(!(outStr == null)) {
        os.println(line);
        os.flush();
        outStr = is.readLine();
        System.out.println("Client " + clientNum + ":" + outStr);
        line = sin.nextLine();
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        if (socket != null) {
          socket.close();
        }
        if (is != null) {
          is.close();
        }
        if (os != null) {
          os.close();
        }
        if (sin != null) {
          sin.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}