Java复习

第一章 JAVA语言基础知识

1、Java有三个版本

J2SE java平台标准版

J2EE java平台企业版

J2ME java平台微型版

2、编译与运行

JAVA程序的运行通过编译和解释两个过程完成。JAVA源程序成为.java文件,经过编译后产生的是同名的.class字节码文件,再经过解释在JAVA虚拟机(JVM)上运行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3、JDK、JRE和JVM

JDK Java开发工具包

JRE Java运行时环境

JVM Java虚拟机

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4、标识符

标识符是一个名称,与内存中的某个位置(地址)相对应

标识符的第一个字符必须是下列字符之一:

大写字母 (A-Z)

小写字母 (a-z)

下划线(_)

美元符号 ($)

标识符的第二个字符及后继字符必须是:

上述列表中的任意字符

数字字符 (0-9)

5、数值型

类型说明
byte带符号微整数
short带符号短整数
int带符号整数
long带符号长整数
float单精度浮点数
double双精度浮点数

6、数值型文字量

数据类型文字量
byte,short,int十进制数,开头不为0;0X跟十六进制数,如0XF1C4; 0跟八进制数,如0726
long同上,但后面跟l或L,如:84l,0X1F39L
float数字后跟f或F,如1.23456F,1.893E2F
double后面可选d或D做后缀,如:1.23D
booleantrue或false

7、字符型

字符类型的文字量是单引号括起来的字符或者转义序列

某些特殊的字符型常量使用转义

转义字符表示含义
\’单引号字符
\”双引号字符
\反斜线字符
\r回车
\n回车并换行
\t水平制表符
\b退格

8、布尔类型

boolean

只有true和false两种值

9、字符串

使用多个字符组成,用" "括起来

字符可以转义

用String/StringBuffer类来存储

String类不可变,而StringBuffer类可变

10、运算符

算数运算符

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

赋值运算符

short s=10;

s+=2;

编译可以通过,不会改变数据类型(加上了强制类型转换,2默认为int类型)

比较运算符

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果均为boolean型

逻辑运算符

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

&&(短路与)与 & (逻辑与) 的区别

&&:如果第一个条件为false,则第二个条件不进行

&:两个条件都要执行

||(短路或)与 |(逻辑或)的区别

||: 如果第一个条件为true,则第二个条件不进行

|:两个条件都要执行

位运算符

"<<"左移:二进制右边补零

">>"右移:二进制左边如果原来为00,补00,原来为01,补01

">>>"无符号右移:二进制左边补00

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

三元表达式

(条件表达式) 表达式1?表达式2

结果需要为统一一个类型

运算符优先级

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

11、类型转换

扩展转换

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

只有整型向浮点型转换会造成精度丢失

窄化转换

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

每种转换都可能会造成精度损失

自动类型转换

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自动类型转换指的是容量小的数据类型可以自动转换为容量大的数据类型。实线表示无数据丢失的自动类型转换,而虚线表示在转换时可能会有精度的损失。

隐式转换(自动类型转换)

(1)赋值转换

将表达式类型转换为指定类型

(2)方法调用转换

调用指定的方法

(3)字符串转换

任何类型都可以转换成字符串

显示转换(强制类型转换)

在表达式前加上(指定类型)

e.g.

int a = (int)3.14

12、数组

每个数组的length成员变量,都是public final的

(1)一维数组

a.声明与创建

方式一:

Type[] arrayName = new Type();

方式二:

Type arrayName[] = new Type();

new后面也可以是子类、接口实现类

方式三:

Type arrayName[] = {初始化数值};

b.初始化

基本数据类型数据,默认的初始值为0

boolean类型数据,默认值为false

引用类型元素,默认值为null

c.访问

通过下标进行访问

数组下标的类型必须在int,short,byte,char类型中

最大下标为length-1

d.复制数组函数

public static void arraycopy(源数组,源数值起始坐标, 目标数组, 目标数组复制开始的位置, 从源数组复制长度)

调用:System.arraycopy(…)

(2)多维数组

a.声明和创建

方式1:

Type arrayName[][];

arrayName = new Type[num1][];

arrayName[0][] = new Type[num2];

方式2:

Type arrayName[][] = new [num1][num2];

方式3:

Type arrayName[][] = new {{}, {}, {}};

多维数组每维的长度可不同

b.访问

基本同一维


//一维数组的增强for
for(Type e: arrayName){
}
//多维数组的增强for
for(Type[] e1: arrayName){
	for(Type e2: e1){	
	}
}

13、流程控制

分支控制

if(判断条件){

}else if(判断条件2){

}else{

}

需要多少判断条件根据实际判断

switch(e){
	case value1: ; break;
	case value2: ; break;
	...
	default: break;
}
  • e必须是整型或字符型

  • 如果与case后的值相同,就从该case后执行,直到遇到break;

  • default可有可无

循环控制

for
for(初始化: 返回布尔值的条件表达式 :每轮的变化){
	//循环体
}
//增强for 一般用来遍历
for (Type name : 数组或集合类型对象) {
	//循环体
}
while
while(条件表达式){
	//循环体
}
do-while
do{
	//循环体
}while(条件表达式)

至少执行一次

跳转语句

break:

  • 跳出循环,不再执行剩余部分
  • 也可用在代码块中,用于跳出它所指定的块

continue:

  • 必须用于循环结构中

  • 停止本次迭代,回到循环起始处,开始下一次迭代过程

  • 有两种使用格式

    • 不带标号的continue语句

      终止当前这一轮的循环,跳出本轮循环剩余的语句,直接进入当前循环的下一轮

    • 带标号的continue语句

      使程序的流程直接转入标号标明的循环层次

return:

  • 返回调用此方法的语句

第二章 类与对象

1、概念

抽象

概念:忽略问题中与当前目标无关的方面,只关注与当前目标有关的方面。

封装

概念:一种信息隐蔽技术,利用抽象数据类型将数据和基于数据的操作封装在一起;用户只能看到对象的封装界面信息,对象的内部细节对用户是隐蔽的;封装的目的在于将用户的使用者和设计者分开,使用者不需要知道行为实现的细节。

继承

概念:基于已有类产生新的类,指新的类可以获得已有类(称为超类、基类或父类)的属性和行为,称新类为已有类的子类(也称为派生类);在继承过程中子类继承超类的特性,包括方法和实例变量;子类也可以修改继承的方法或增添新的方法;有助于解决软件的可重用性问题,使程序结构清晰,降低了编码和维护的工作量。

Java中只支持单继承,但有接口

多态

概念:超类及其不同子类的对象可以响应同名的消息,具体的实现方法却不同;主要通过子类对父类方法的覆盖来实现。

2、类与对象的关系

类是对一群具有相同性质的对象的描述

对象是类的具体实例

3、类的声明

[public] [abstract | final] class 类名称 [extends 父类名称] [implements 接口名称列表]
{  
	数据成员声明及初始化;
	方法声明及方法体;
}
  • class 声明一个类
  • public 表示该类是共有类,一个.java文件中只能有一个
  • abstract 表示该类是抽象类,其中可能有抽象方法,不能被实例化
  • final表示 该类是终结类,不可以被继承
  • extends 表明类是从某一个父类派生而来
  • implements 表明类实现某些接口

对象的引用声明和创建

声明:

类名 引用变量名

声明一个引用变量时并没有生成对象

创建:

new 类名()

new的作用是:

  • 在内存中为类名类型的对象分配内存空间(堆栈、方法池)

  • 返回对象的引用

引用变量可以被赋以空值

4、变量成员

[public | protected | private | 缺省] [static][final][transient][volatile] 数据类型 变量名1[=变量初值], 变量名2[=变量初值],...;
  • public/protected/private/缺省(即什么都不写) 为权限修饰符

  • static指明这是一个静态成员变量(类变量)。

  • final指明变量的值不能被修改。

  • transient指明变量是不需要序列化的。

  • volatile指明变量是一个共享变量。

变量的类型:

局部变量:在方法体内的,只能在方法体中使用

实例变量:没有static修饰的变量(数据成员)称为实例变量。

类变量:

  • 写在类的定义中,用static修饰
  • 在类中只有一个值,为该类的所有对象所共享
  • 类初始化的同时就被赋值
  • 引用格式:类名/实例对象名.类变量名

5、方法成员

[public | protected | private | 缺省] 
[static][final][abstract] [native] [synchronized]
返回类型 方法名([参数列表]) [throws exceptionList]
{
	方法体
}
  • public、protected、private、缺省 控制访问权限。

  • static指明这是一个类方法(静态方法)。

  • final指明这是一个终结方法。

  • abstract指明这是一个抽象方法。

  • native用来集成java代码和其它语言的代码(本课程不涉及)。

  • synchronized用来控制多个并发线程对共享数据的访问。

  • 返回类型:

    • 可以是任意的Java数据类型
    • 不需要返回值时,返回类型为void
  • 参数类型:

    • 简单数据类型
    • 引用类型(数组、类或接口)
    • 可以有多个参数,也可以没有参数,方法声明时的参数被称为形式参数
    • 可变长参数:
      • 可变长参数使用省略号表示,其实质是数组。
      • e.g. Object …args
      • 对于具有可变长参数的方法,传递给可变长参数的实际参数可以是零个到多个对象。
      • 一个方法中,可变长参数只能由一个,且必须是最后一个。
      • 其余需要指定的参数要放到可变长参数前。
  • 方法体

    • 存放方法的实现。
    • 包括局部变量的声明以及所有合法的Java语句。
    • 局部变量的作用域只在该方法内部。
  • throws exceptionList

    • 抛出异常列表

实例方法与类方法

实例方法
  • 特定对象的行为

  • 声明时前面不加static

  • 调用格式:

    对象名.方法名([参数列表])

  • 参数传递

    • 值传递:参数类型为基本数据类型时
    • 引用传递:参数类型为对象类型或数组时(形参的变化会影响实参)
类方法
  • 类中对象的共有行为,也称为静态方法

  • 声明时前面加static

  • 不能被声明为抽象的

  • 调用格式:

    类名/对象名.方法名([参数列表])

  • 静态方法只能调用静态成员,不能调用非静态成员;非静态方法里既可调用静态成员,也可调用非静态成员。

6、包

概念:一组类的集合

一个包可以包含若干个类文件,还可包含若干个包

作用:

  • 将相关的源代码文件组织在一起。
  • 类名的空间管理,利用包来划分名字空间可以避免类名冲突
  • 提供包一级的封装及存取权限

编译单元

  • 一个Java源代码文件称为一个编译单元,由三部分组成:
    • 所属包的声明(省略,则属于默认包);
    • Import (引入)包的声明,用于导入外部的类;
    • 类和接口的声明。

一个编译单元中只能有一个public类,该类名与文件名相同,编译单元中的其他类往往是public类的辅助类,经过编译,每个类都会产生一个class文件。

包的声明

  • 命名的包(Named Packages)

    • 例如: package Mypackage;
  • 默认包(未命名的包)

    • 不含有包声明的编译单元是默认包的一部分。

包名就是文件夹名,即目录名;

目录名并不一定是包名;

引入包

  • Java编译器为所有程序自动引入包java.lang

  • import语句的格式:

    • import package1[.package2…]. (classname |*);

    • package1[.package2…]表明包的层次,对应于文件目录;

    • classname指明所要引入的类名;

    • 如果要引入一个包中的所有类,可以使用星号(*)来代替类名

  • 静态引入

    • 单一引入是指引入某一个指定的静态成员:

      import static java.lang.Math.PI;

    • 全体引入是指引入类中所有的静态成员

      ▫import static java.lang.Math.*;

7、类的成员访问权限控制

类型private缺省protectedpublic
同一类
同一包中的子类
同一包中的非子类
不同包中的子类
不同包中的非子类

记忆口诀:

private: 私有资源,只能自己使用

default: 家族(包),我自己,我子孙可以用

protected: 我可以开放,但是只给自己家族的但是嫁到或者入赘其它家族的后代使用

public: 谁都可以用,水资源

protected详解:

  • 基类(父类)的protected成员(包括成员变量个成员方法)对本包内可见,并且对子类可见。

  • 若子类与基类(父类)不在同一包中,那么在子类中,只有本类实例可以访问其从基类继承而来的protected方法,而在子类中不能访问基类实例(对象)(所调用)的protected方法。

  • 不论是否在一个包内,父类中可以访问子类实例(对象)继承的父类protected修饰的方法。(子父类访问权限特点:父类访问域大于子类)。

  • 若子类与基类(父类)不在同一包中,子类只能在自己的类(域)中访问父类继承而来的protected成员,无法访问别的子类实例(即便相同父类的亲兄弟)所继承的protected修饰的方法。

  • 若子类与基类(父类)不在同一包中,父类中不可以使用子类实例调用(父类中没有)子类中特有的(自己的)protected修饰的成员。(毕竟没有满足同一包内和继承获得protected成员的关系)。

8、get/set方法

get方法

  • 功能是取得属性变量的值

  • get方法名以“get”开头,后面是实例变量的名字

  • public Type getName(){
       return ...;
    } 
    

set方法

  • 功能是修改属性变量的值

  • set方法名以“set”开头,后面是实例变量的名字

  • public Type setName(){
       
    } 
    

9、this关键字

如果方法内的局部变量(包括形参)名与实例变量名相同,则方法体内访问实例变量时需要this关键字。

e.g.

public void setName(Type Name){
	this.Name = Name;
}

this指的是当前对象(调用该方法)本身

this关键字不能用于static静态方法中

10、构造方法

  • 方法名与类名相同

  • 无返回值类型

  • 通常被声明为公有(public)

  • 可以有任意个参数

  • 完成初始化

  • 不能显示调用

  • new一个对象,会自动调用该类的构造方法为新对像初始化

  • 若没有显示声明的构造方法,编译器会隐含地生成默认的构造方法(如果父类的构造方法有参数,子类的构造方法必须显示声明)

默认构造方法

  • 没有参数的构造方法,方法体可以为空

  • 使用默认的构造方法初始化对象时,如果类声明中没有给实例变量赋初值,则对象的属性值为0/false/null

自定义构造方法

  • 主要用于给对象属性赋初值

  • 构造方法可以被重载

    • 一个类中可以有多个同名的方法,参数列表不相同,就称为方法重载
    • 在方法调用时,通过传递参数列表的不同来辨别,调用那个方法
  • 只要显示声明构造方法,编译器就不会生成默认构造方法

  • 也可以自己显示的声明无参构造方法,方法体中可以定义默认初始化方式

  • 在一个构造方法中想去调用其他构造方法,可以使用this(参数列表),一般由参数多的调用参数少的,必须位于方法体的第一句

final变量的初始化

  • 定义为final后,这个变量一旦被初始化便不可改变
  • 实例变量和类变量都可以被声明为final
  • final实例变量可以在类中定义时给出初始值,或在每个构造方法结束前完成初始化
  • final类变量必须在声明的同时初始化

11、对象自动回收与垃圾收集器

对象自动回收

当一个对象在程序中不再被使用时,就成为一个无用对象,系统将在必要时调用垃圾回收程序将其占用的内存回收。

  • 无用对象

    • 离开了作用域的对象
    • 无引用指向对象
  • Java运行时系统通过垃圾收集器周期性地释放无用对象所使用的内存,垃圾收集触发与执行由虚拟机(JVM)在后台自动控制

  • Java运行时系统会在对对象进行自动垃圾回收前,自动调用对象的finalize()方法(Java 9开始被弃用)

垃圾收集器
  • 自动扫描对象的动态内存区,对不再使用的对象做上标记以进行垃圾回收

  • 作为一个后台线程运行,通常在系统空闲时异步地执行

  • 垃圾回收机制并不能完全杜绝内存泄露,如创建大量对象不释放其引用,或对象创建频率超过垃圾收集处理能力时,仍可能内存耗尽

12、枚举类

对象的可取值为可列举的特定的值时,可以使用枚举类型

声明

[public] enum 枚举类型名称 [implement 接口名称列表]
{
	枚举值;
	变量成员声明及初始化;
	方法声明及方法体;
}

在枚举类中可以声明构造方法和其他用于操作枚举对象的方法

特点

  • 枚举类也是类
  • 所有枚举类都隐含继承(扩展)自java.lang.Enum,因此枚举类不能继承其他任何类
  • 枚举类型的类体中可以包含方法和变量
  • 枚举类型的构造方法必须是包内私有(default)或私有(private)的,默认private
  • 定义在枚举变量开头的常量会被自动创建,不能显示地调用枚举类的构造方法
  • 枚举值是public、static、final的常量,可以通过枚举类名来调用

默认方法

  • 静态的values()方法用于获得枚举类型的枚举值的数组

    for(Score value: Score.values()){
                System.out.println(value);
    }
    ------
    EXCELLENT
    QUALIFIED
    FAILED
    
  • toString方法返回枚举值的字符串描述

     System.out.println(Score.EXCELLENT.toString());
     ------
     EXCELLENT
    
  • valueOf方法将以字符串形式表示的枚举值转化为枚举类型的对象

    public class Main {
        public static void main(String[] args) {
            System.out.println(Score.valueOf("EXCELLENT").getClass());
        }
    }
    
    enum Score{
        EXCELLENT,
        QUALIFIED,
        FAILED
    }
    ------
    class gxuoj.Score
    
  • ordinal方法获得对象在枚举类型中的位置索引

    System.out.println(Score.EXCELLENT.ordinal());
    ------
    0
    

枚举类自定义属性方法

enum NumericScore {
    EXCELLENT(90, 100),
    QUALIFIED(60, 89),
    FAILED(0, 59);
    private final int min;
    private final int max;
    NumericScore(int min, int max) {
        this.min = min;
        this.max = max;
    }
    public boolean test(int val) {
        return (val >= min) && (val <= max);
    }
}

13、toString()方法

作用:将对象的内容转换为字符串

//下面两行等价
System.out.println(A); 
System.out.println(A.toString());

如果需要别的输出,可以自己覆盖toString()方法:

  • 必须被声明为public

  • 返回类型为String

  • 方法的名称必须为toString,且没有参数

  • 在方法体中不要使用输出方法System.out.println()

  • “@Override”是一个注解,表明覆盖或实现父类或接口中同名方法

14、DecimalFormat类

  • DecimalFormat类在java.text包中。

  • 在toString()方法中使用DecimalFormat类的实例方法format对数据进行格式化

java.text.DecimalFormat("$0.00").format(参数)

第三章 类的重用

1、类的继承

所有的类都直接或间接(包括没有继承任何父类,就隐含继承)的继承java.lang.Object

Java只支持类的单继承,每个子类只能有一个直接超类

  • 超类:
    • 也称父类/基类
    • 被直接或间接继承的类
  • 子类
    • 也称派生类
    • 继承其他类而得到的类
    • 继承所有祖先的状态或行为
    • 可以增加变量和方法
    • 可以覆盖继承的方法

子类继承超类,可以继承超类的全部属性和方法(除了超类构造方法),但不一定可以直接访问(例如父类的私有属性和方法)

继承语法:

[权限修饰符] class SubClassName extends SuperClassName{
	//变量成员
    //方法成员
}

属性隐藏

子类中声明了与超类中相同的成员变量名

  • 从超类继承的变量将被隐藏

  • 子类拥有两个相同名字的变量,一个继承自超类,另一个由自己声明

  • 当子类执行继承自超类的操作时,处理的是继承自超类的变量,而当子类执行它自己声明的方法时,所操作的就是它自己声明的变量

  • 本类中通过super.属性访问从超类继承的属性

方法覆盖

  • 子类不需使用从超类继承来的方法的功能,则可以声明自己的同名方法,称为方法覆盖
  • 覆盖方法的返回类型、方法名称、参数个数及类型必须和被覆盖方法一致
  • 只需要在方法名前使用不同的类名或不同类的对象名即可区分覆盖方法和被覆盖方法
  • 覆盖方法的访问权限可以比被覆盖方法更宽松,但是不能更严格

注意事项:

1、必须覆盖的方法:

派生类必须覆盖超类中的抽象方法,否则自身必须成为抽象类

2、不能覆盖的方法:

超类中声明为final的终结方法

超类中声明为static的静态方法

3、调用被覆盖的方法:

super.overriddenMethodName();

4、重载与覆盖的区别

重载:同一个类里面,有同名的方法,但参数不同

void f(int a,int b);int f(int a); 是重载

void f(int a,int b);void f(double a,double b);  是重载 

void f(int a,int b);void f(int x,int y);不是重载

覆盖:子类与父类有同名的方法,而且参数一模一样

2、子类构造方法

  • 子类不能从超类继承构造方法
  • 子类最好可以显示的调用超类的某个构造方法,调用语句必须出现在子类构造方法的第一行
  • 如果子类的构造方法中调用本类的其他构造方法,则不能调用超类的构造方法,即有super(参数列表)不能有this(参数列表),反之亦然
  • 如果子类中没有显示调用超类的构造方法,系统在执行子类的构造方法时会自动调用超类的默认构造方法(无参构造)
  • 如果超类没有无参构造方法,则子类必须显示调用超类的构造方法

父类变量可以引用子类对象,赋值号左边是父类对象,右边是可以是子类对象

例:要构造A类的一个实例,A类实现了接口B,下列正确的是()

A. B b=new B(); 错

B. B b=new A(); 对

C. A a=new B(); 错

D. A a=new Object(); 错

super使用

super.成员变量名;

super.成员方法(参数);

调用父类构造方法:super( ); 或 super(参数);

this使用

this.成员变量名;

this.方法名();

this(); 或 this(参数); 调用本类的构造方法

3、Object类

是所有类的直接或间接超类,处在类层次最高点

包含所有java类的公共属性

  • 主要方法
public final Class getClass()  
获取当前对象所属的类信息,返回Class对象。
public String toString() 
返回表示当前对象本身有关信息的字符串对象。
public boolean equals(Object obj)  
比较两个对象引用是否指向同一对象,是则返回true,否则返回falseprotected Object clone( )  
复制当前对象,并返回这个副本。
Public int hashCode()   
返回该对象的哈希代码值。
protected void finalize() throws Throwable 
在对象被回收时执行,通常完成的资源释放工作。
  • 相等和同一
    • 在Object类中声明的equals()方法功能是比较两个对象引用是否指向同一对象,而不是比较两个引用指向的对象是否相等
    • 两个对象具有相同的类型,及相同的属性值,则称二者相等(equal)
    • 如果两个引用变量指向的是同一个对象,则称这两个引用变量同一(identical)
    • 两个对象同一,则一定相等
    • 两个对象相等,不一定同一
    • 比较运算符 ”==“ 判断的是这两个对象是否同一
  • 覆盖equals

判断两个对象各个属性域的值是否相同,不能使用从Object类中继承来的equals方法,而需要在类声明中对equals方法进行覆盖

String覆盖的Object类的equals方法,可以判别两个字符串是否内容相等

方法原型必须与Object类中的equals方法完全相同,可以加上@Override覆写注释


不作为重点看

  • hashCode方法

    • hashCode是一个返回对象散列码的方法,该方法实现的一般规定是:
      • 在一个Java程序的一次执行过程中,如果对象“相等比较”所使用的信息没有被修改的话,同一对象执行hashCode方法每次都应返回同一个整数。在不同的执行中,对象的hashCode方法返回值不必一致。
      • 如果依照equals方法两个对象是相等的,则在这两个对象上调用hashCode方法应该返回同样的整数结果。
      • 如果依据equals方法两个对象不相等,并不要求在这两个对象上调用hashCode方法返回值不同。
    • 只要实现得合理,Object类定义的hashCode方法为不同对象返回不同的整数。一个典型的实现是,将对象的内部地址转换为整数返回,但是Java语言并不要求必须这样实现。
  • clone方法

    • 用于根据已存在的对象构造一个新的对象,也就是复制对象

    • 使用clone方法复制对象

      • 覆盖clone方法:在Object类中被定义为protected,所以需要覆盖为public

      • 实现Cloneable接口,赋予一个对象被克隆的能力(cloneability)

      • class MyObject implements Cloneable { 
        }
        
    • 注意:Object类中的clone方法是浅拷贝(值拷贝),如果被复制的对象成员中中有其他类的对象(类的组合),则默认复制引用,需要覆盖clone方法以实现深拷贝(内存重新分配)。

  • finalize方法

    • 在对象被垃圾回收器回收之前,系统自动调用对象的finalize方法。

    • 如果要覆盖finalize方法,覆盖方法的最后必须调用super.finalize。


  • getClass方法

    • final 方法,返回一个Class对象,用来代表对象所属的类

    • 通过Class对象,可以查询类的各种信息:比如名字、超类、实现接口的名字等

    • e.g.

      void PrintClassName(Object obj) {
        System.out.println("The Object's class is " +obj.getClass().getName());
      }
      

4、final类

  • 用final修饰的类
  • final类不能被继承
final class Father {}
class Child extends Father {}
编译器将提示编译错误
  • final方法不能被子类覆盖
class Father
{
   public Parent() {   } //构造方法
   final int getPI() { return Math.PI; } //终结方法
}
class Child extends Parent
{
   public Child() {}
   int getPI() { return 3.14; } //编译错误!不允许覆盖超类中的终结方法
}
  • final变量,即为常量,在定义时赋初值,以后不可被修改
final Type name;

5、抽象类

  • 代表一个抽象概念的类,用abstract修饰

  • 规定整个类家族必须具备的属性或方法

  • 可以有常规类的任何成员(变量成员、方法成员等),包括非抽象方法

  • 可以包含抽象方法:用abstract修饰,只有方法原型,没有方法实现

    public abstract <returnType> <methodName>([形式参数表])
    

    只有抽象类可以包含抽象方法,但抽象类中可以没有抽象方法

  • 没有具体实例对象的类,即不能用new方法进行实例化,只能用作超类

  • 子类只有实现了抽象超类中的所有抽象方法,子类才不是抽象类,才能产生实例;如果没有完全实现抽象超类中的抽象方法,子类也只能是抽象类

  • 声明

    abstract class className{}
    
  • 优点

    • 隐藏具体的实现细节,所有的子类使用的都是相同的方法原型,其中包含了调用该方法时需要了解的全部信息
    • 强迫子类完成指定的行为,规定所有子类的“标准”行为

6、泛型(不考)

  • 泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数

  • 有泛型类、泛型方法和泛型接口

  • 泛型的好处是在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率

泛型:就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。会在编译期检查类型是否错误。

泛型类

public class 类名 <泛型类型1,...> {}

e.g.

class GeneralType<Type> {
    Type2 object;

    public GeneralType(Type object) {
        this.object = object;
    }

    public Type getObj() {
        return object;
    }
}

public class GenericsTester {
    public static void main(String[] args) {
        GeneralType<Integer> i = new GeneralType2<>(2);
        GeneralType<Double> d = new GeneralType2<>(0.33);
        System.out.println("i.object=" + (Integer) i.getObj());

//      System.out.println("i.object=" + (Integer)d.getObj()); // 不能通过编译
    }
}

泛型方法

方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。当调用方法时,根据传入的实际对象,编译器就会判断出类型形参T所代表的实际类型。

格式:

public <泛型类型> 返回类型 方法名(泛型类型 变量名) {}

e.g.

class GeneralMethod {
    <Type> void printClassName(Type object) {
         System.out.println(object.getClass().getName());
    }
}
public class GenericsTester {
    public static void main(String[] args) {
        GeneralMethod gm = new GeneralMethod();
        gm.printClassName("hello");
        gm.printClassName(3);
        gm.printClassName(3.0f);
        gm.printClassName(3.0);
    }
}
----
java.lang.String
java.lang.Integer
java.lang.Float
java.lang.Double

通配符泛型

目标:用来解决泛型无法协变的问题的,协变指的就是如果 Student 是Person的子类,那么 List 也应该是 List 的子类,但是泛型是不支持这样的父子类关系的。

class GeneralType <Type> {
    Type4 object;
    public GeneralType4(Type object) {
        this.object = object;
    }
    public Type4 getObj() {
        return object;
    }
}
class ShowType {
   //无边界的通配符:主要作用就是让泛型能够接受未知类型的数据
    public static void showType(GeneralType<?> o) { 
        System.out.println(o.getObj().getClass().getName());
    }
}
public class GenericsTester4 {
    public static void main(String[] args){
        GeneralType<Integer> i = new GeneralType<>(2);
        GeneralType<String> s = new GeneralType<>("hello");
        ShowType.showType(i);
        ShowType.showType(s);
    }
}
程序的运行结果如下:
java.lang.Integer
java.lang.String

有限制的泛型

  • 虽然可以使用泛型使程序能够处理多种不同类型对象,并不是所有对象都能进行某些操作

  • 有时候需要将泛型中参数代表的类型做限制,此时就可以使用有限制的泛型

  • 在参数“Type”后面使用“extends”关键字并加上类名或接口名,表明参数所代表的类型必须是该类的子类或者实现了该接口

    • 对于实现了某接口的有限制泛型,也是使用extends关键字,而不是implements关键字

e.g.

class GeneralType <Type extends Number> {
    Type object;
    public GeneralType(Type object) {
        this.object = object;
    }
    public Type5 getObj() {
        return object;
    }
}
public class GenericsTester {
    public static void main(String[] args){
        GeneralType<Integer> i = new GeneralType<>(2);
        // 非法,Type只能是Number或Number的子类
       // GeneralType5<String> s = new GeneralType5<>("hello");    
    }
}

7、类的组合

就是类的变量成员中含有引用变量成员

构造方法的调用顺序:

父类静态代码块(静态变量 > 静态块) > 子类的静态代码块 > 父类构造代码块、构造方法> 子类的构造代码块、构造方法

静态代码块:

class demo{ static { //静态代码块...... } }

特点:

1、Java静态代码块中的代码会在类加载JVM时运行,且只被执行一次

2、静态块常用来执行类属性的初始化 ,和一些全局初始化的工作

3、静态块优先于各种代码块以及构造函数,如果一个类中有多个静态代码块,会按照书写顺序依次执行

4、静态代码块可以定义在类的任何地方中除了方法体中(这里的方法体是任何方法体)

5、静态代码块不能访问普通变量

第四章 接口与多态

1、接口

概念:

  • 接口可以看作是一个”纯“的抽象类,只提供一种形式,并不提供实现,可以理解为一种规范

  • 接口中可以规定方法原型:

    • 方法名
    • 参数列表
    • 返回类型
    • 不规定方法主体
  • 可以包含基本数据类型的数据成员,默认为static和final

作用:

  • 是面向对像的一个重要机制
  • 接口与实现类不是继承关系,是实现规则的关系
  • 实现多继承
  • 建立类和类之间的“协议”
    • 将类根据其实现的功能分组用接口代表,而不必考虑它所在的类继承层次;这样可以最大限度地利用动态绑定,隐藏实现细节
    • 实现不同类之间的常量共享

语法:

[接口修饰符] interface 接口名称 [extends 父接口名]{
	...//方法的原型声明或静态常量
}
  • 接口访问修饰符:public 或 缺省(default)

  • extends:接口可以多继承

  • 接口的成员变量的默认为public,static,final,由于final关键字,接口的成员变量一定要有初值,且此值将不能再更改,可以省略

  • 接口中的方法必须是“抽象方法”,不能有方法体,可以省略public及abstract关键字

  • Java 8级以上版本,允许接口中包含默认方法(default)和静态方法

    • 接口的默认方法可以用来定义一些默认的行为,接口的实现类可以直接继承默认方法也可以在必要的时候覆盖默认方法(需声明为public)
    • 接口的静态方法则是用来定义接口的默认行为或辅助方法不能被实现类覆盖,而且只能通过接口名加方法名来调用
    • 接口可以提供默认方法的实现,实现可重写也可以不重写
    • 静态方法从属于接口,通过接口名调用。实现中定义了同名静态方法,为完全不同的方法,从属于实现类
    • 默认方法可以调用静态方法,静态方法不能调用默认方法
  • 注意:

    接口不可以被实例化,即不能创建(new)接口对象,必须通过类来实现接口

实现:

[权限修饰符] class className implements interfaceName {
	//在类体中实现接口方法
	//本类声明的其他变量和方法
}

注意:

  1. 除非声明接口的类是抽象类,否则必须实现接口中的所有方法
  2. 来自接口的方法必须声明成public(即权限不能小于接口方法)

Lambda表达式和函数式接口不考


java不允许一个类有多个超类,但在java中,一个类可以实现多个接口,通过这种机制可实现对设计的多重继承

语法:

[权限修饰符] class className implements interface1, interface2, ...{
	//在类体中实现接口方法
	//本类声明的其他变量和方法
}

扩展:

  • 接口可通过扩展的技术派生出新的接口

    • 原来的接口称为超接口(super interface)
    • 派生出的接口称为子接口(sub interface)
  • 实现一个接口的类也必须实现其超接口

  • 语法:

    interface subInterfaceName extends superInterfaceName1,superInterfaceName2,...{
    }
    

私有方法

从Java 9开始,可以为接口声明私有方法

这与Java类的私有方法设计理念是一样的,可以将实现细节对子类和实现类隐藏起来,提高代码的安全性与可维护性

interface SoccerGame {
	// 获取格式化之后的比赛日期字符串
    static String getGameDate(Date date) {
        return formatDate(date);
    }
	// 对日期进行格式化
    private static String formatDate(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return simpleDateFormat.format(date);
    }
}

2、类型转换

又称为塑性

  • 转换方式
    • 隐式的类型转化
    • 显示的类型转换
  • 转换方向
    • 向上转型
    • 向下转型

类型转换规则

  • 基本类型之间的转换
    • 将值从一种类型转换成另一种类型
  • 引用变量的类型转换
    • 将引用转换为另一个类型的引用,并不改变对象本身的类型
    • 转换方向
      • 任何一个(直接或间接)超类的类型(向上转型)
      • 对象所属的类(或其超类)实现的一个接口(向上转型)
      • 转为引用指向的对象的类型(唯一的向下转型)
  • 当一个引用被转为其超类引用后,通过它能够访问的只有在超类中声明过的方法

e.g.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

隐式类型转换

基本数据类型

可以转换的类型之间,存储容量低的自动向存储容量高的类型转换

引用变量

  • 被转换为更普适的类
Employee emp;
emp = new Manager();//将Manager类型的对象直接赋给Employee类的引用变量,系统会自动将Manage对象塑型为Employee类
  • 被塑型为对象所属类实现的接口类型
Car car = new Car();
Insurable item = car;
显示类型转化

基本数据类型

(int)871.34354;     // 结果为 871 
(char)65;              // 结果为‘A’ 
(long)453;            // 结果为453L

引用变量

Employee  emp; 
Manager man;
emp = new Manager();
man = (Manager)emp; //将emp显式转换为它指向的对象的类型
应用场合
  • 赋值转换

    • 赋值运算符右边的表达式类型或对象转换为左边的类型
  • 方法调用转换

    • 实参的类型转换为形参的类型
  • 算术表达式转换

    • 算数混合运算时,不同类型的操作数转换为相同的类型再进行运算
  • 字符串转换

    • 字符串连接运算时,如果一个操作数为字符串,另一个操作数为其他类型,则会自动将其他类型转换为字符串

方法的查找

针对塑性前和塑性后的类中都提供了相同原型的方法,即子类重写了父类的方法

实例方法的查找

从对象创建时的类开始,沿类层次向上查找

image-20231029102451890
Manager   man = new Manager(); 
Employee  emp1 = new Employee(); 
Employee  emp2 = (Employee)man; 
emp1.computePay();  // 调用Employee类中的computePay()方法 
man.computePay();    // 调用Manager类中的computePay()方法  
emp2.computePay(); // 调用Manager类中的computePay()方法 

类方法的查找

总是在引用变量声明时所属的类中进行查找

image-20231029102602952
Manager  man = new Manager(); 
Employee emp1 = new Employee(); 
Employee emp2 = (Employee)man; 
man.expenseAllowance();        // 调用Manager类中的computePay()方法  
emp1.expenseAllowance();       // 调用Employee类中的computePay()方法 
emp2.expenseAllowance();       // 调用Employee类中的computePay()方法 

3、多态

  • 是指不同类型的对象可以响应相同的消息
  • 超类对象和从相同的超类派生出来的多个子类的对象,可被当作同一种类型的对象对待
  • 实现同一接口不同类型的对象,可被当作同一种类型的对象对待
  • 可向这些不同的类型对象发送同样的消息,由于多态性,这些不同类的对象响应同一消息时的行为可以有所差别

在java中要实现多态,必须要满足如下几个条件:

1.必须在继承体系下

2.子类必须要对父类方法进行重写

3.通过父类的引用调用重写方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法

绑定
  • 指将一个方法调用同一个方法主体(调用方法所在的类)连接到一起

  • 根据绑定时期可分为

    • 前期绑定(静态绑定、编译器绑定):在程序运行之前进行绑定
      • 由private、static、final任意一个关键字修饰,那么这个方法就是静态绑定的
      • 构造方法
    • 后期绑定(动态绑定、运行时绑定):基于对象的类别,在程序运行时绑定

动态绑定e.g.

public class Main{ 
    public static void main(String[] args) { 
       Shape[] s = new Shape[9]; 
       int n;
       for(int i = 0; i < s.length; i++) { 
            n = (int)(Math.random() * 3);
            switch(n) {  
                case 0: s[i] =  new Circle(); break;
                case 1: s[i] =  new Square(); break;
                case 2: s[i] =  new Triangle();
             } 
        }      
        for(Shape oneS: s)   oneS.draw(); 
      }
}
//Circle、Square、Triangle都覆盖了Shape的draw()方法
  • 在主方法的循环体中,每次随机生成一个Circle、Square或者Triangle对象;

  • 编译时无法知道s数组元素指向的实际对象类型,运行时才能确定类型,所以是动态绑定

多态应用举例见二次分发

构造方法与多态(同构造方法调用顺序)

调用顺序:

  1. 首先调用超类的构造方法,这个步骤会不断重复下去,首先被执行的是最远超类的构造方法
  2. 按声明顺序调用成员初始化模块
  3. 执行当前子类对象的构造方法体其他语句
class Meal {
    Meal() {
        System.out.println("Meal()");
    }
}
class Bread {
    Bread() {
        System.out.println("Bread()");
    }
}
class Cheese {
    Cheese() {
        System.out.println("Cheese()");
    }
}
class Lettuce {
    Lettuce() {
        System.out.println("Lettuce()");
    }
}
class Lunch extends Meal {
    Lunch() {
        System.out.println("Lunch()");
    }
}
class PortableLunch extends Lunch {
    PortableLunch() {
        System.out.println("PortableLunch()");
    }
}
public class Sandwich extends PortableLunch {
    Bread b = new Bread();
    Cheese c = new Cheese();
    Lettuce l = new Lettuce();
    Sandwich() {
          System.out.println("Sandwich()");
    }
    public static void main(String[] args) {
           new Sandwich();
    }
}
----
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
  • 在构造方法内调用准备构造的那个对象的动态绑定方法
    • 被调用方法要操纵的成员可能尚未得到正确的初始化
    • 可能造成一些难于发现的程序错误
abstract class Glyph {
    abstract void draw();
    Glyph() {
        System.out.println("Glyph() before draw()");
        draw(); 
        System.out.println("Glyph() after draw()");
     }
}
class RoundGlyph extends Glyph {
    int radius = 1;
    RoundGlyph(int r) {
         radius = r;
         System.out.println("RoundGlyph.RoundGlyph(),  radius = " + radius);
     }
     void draw() { 
         System.out.println("RoundGlyph.draw(), radius = " + radius);
      }
}
public class PolyConstructors {
      public static void main(String[] args) {
           new RoundGlyph(5);
      }
}
/*
运行结果
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
说明
在Glyph中,draw()方法是抽象方法,在子类RoundGlyph中对此方法进行了覆盖。Glyph的构造方法调用了这个方法;
从运行的结果可以看到:当Glyph的构造方法调用draw()时,radius的值甚至不是默认的初始值1,而是0。
*/

注意事项:

  1. 用尽可能少的动作把对象的状态设置好
  2. 如果可以避免,不要调用任何方法
  3. 在构造方法内唯一能够安全调用的是在超类中具有final属性的那些方法(也适用于private方法,它们自动具有final属性)。这些方法不能被覆盖,所以不会出现前述的潜在问题

第五章 异常处理与输入输出

1、异常处理

概念

又称为例外,是特殊的运行错误对象

  • Java中声明了很多异常类,每个异常类都代表了一种运行错误,类中包含了

    • 该运行错误的信息

    • 处理错误的方法

每当Java程序运行过程中发生一个可识别的运行错误时,即该错误有一个异常类与之相对应时,系统都会产生一个相应的该异常类的对象,即产生一个异常

错误的分类

根据错误的严重程度不同,可分为两类:

  • 错误
    • 致命性的,程序无法处理
    • Error类是所有错误类的父类
  • 异常
    • 非致命性的,可编制程序捕获和处理
    • Exception类是所有异常类的父类
    • 分类:
      • 非检查型异常(编译器不检查,运行时异常)
        • 不期望程序捕获的异常,在方法中不需要声明,编译器也不进行检查
        • 继承自RuntimeException
        • 不要求捕获和声明的原因
          • 引发RuntimeException的操作在Java应用程序中会频繁出现。例如,若每次使用对象时,都必须编写异常处理代码来检查null引用,则整个应用程序很快将变成一个庞大的try-catch块
          • 它们表示的问题不一定作为异常处理。如:可以在除法运算时检查0值,而不使用ArithmeticException。可以在使用引用前测试空值
      • 检查型异常
        • 其他类型的异常
        • 如果被调用的方法抛出一个类型为E的检查型异常,那么调用者必须捕获E或者也声明抛出E(或者E的一个父类),对此编译器要进行检查

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

处理

  • 声明抛出异常
    • 不在当前方法内处理异常,可以使用throws子句声明将异常抛出到调用方法中
    • 如果所有的方法都选择了抛出此异常,最后JVM将捕获它,输出相关的错误信息,并终止程序的运行
  • 捕获异常
    • 使用try{}catch(){}块,捕获到所发生的异常,并进行相应的处理

处理示意图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 抛出异常语法
try {
    statement(s)
} catch (exceptiontype name) {
    statement(s)
} finally {
    statement(s)
}
  • catch和finally可以只有一项,但不能都没有

  • try语句

    • 其后跟随可能产生异常的代码块
    • 一条语句产生异常时,就会跳过该条语句后面的代码
  • catch语句

    • 其后跟随异常处理语句,通常都要用到三个方法
      • getMessage() – 返回一个字符串,对发生的异常进行描述
      • printStackTrace() – 给出方法的调用序列,一直到异常的产生位置
      • toString() - 返回异常类型和异常描述
    • 如果并列有多个catch语句捕获多个异常,则一般的异常类型放在后面,特殊的放在前面
  • finally语句

    • 不论在try代码段是否产生异常,finally 后的程序代码段都会被执行
    • 通常在这里释放内存以外的其他资源
    • try后面可以没有catch直接跟finnally,该异常还会往上层抛,try…finnally 的用法主要是为了释放资源,不进行异常捕获,将异常交由上层调用者处理
    • 当程序执行try{}遇到return时,程序会先将return语句要做的一切事情都准备好,在将要返回、但并未返回的时候,执行流程转去执行finally块,当finally块执行完成后就直接返回刚才return语句已经准备好的结果

示意图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 抛出异常语法
[权限修饰符] class className throws exception1,exception2...{
    throw new exception1();
}

throws抛出异常,throw强制生成异常

注意:

throws写在方法头,表示异常若出现在本方法中不处理,抛出给上一级调用方法处理;

throw写在方法体中,执行到这一句表示强制生成异常。

异常对象生成

三种方式

  1. 由Java虚拟机生成
  2. 由Java类库中的某些类生成
  3. 在自己写的程序中生成和抛出异常对象

抛出异常对象都是通过throw语句实现,异常对象必须是Throwable或其子类的实例:

throw new ThrowableObject();
//
ArithmeticException  e = new ArithmeticException();
throw e;

自定义异常类

  • 自定义的所有异常类都必须是Exception的子类

  • 语法

    public class MyExceptionName extends SuperclassOfMyException { 
         public MyExceptionName() { 
              super("Some string explaining the exception"); 
         } 
         public MyExceptionName(String message) { 
              super(message);
          }
    } 
    

2、输入输出流

I/O流

在Java中将信息的输入与输出过程抽象为I/O流

  • 输入指数据流入程序

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 输出指数据从程序流出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 一个流就是一个从源流向目的地的数据序列

读写方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

预定义的I/O流分类

  • 从流的方向划分
    • 输入流
    • 输出流
  • 从流的分工划分
    • 节点流
    • 处理流
  • 从流的内容划分
    • 面向字符的流
    • 面向字节的流

java.io包的顶级层次结构

  • 面向字符的流:专门用于字符数据

  • 面向字节的流:用于一般目的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

面向字符的流
  • 针对字符数据的特点进行过优化,提供一些面向字符的有用特性

  • 源/目标通常是文本文件

  • 格式转换

    • 内部格式:16bit char数据类型
    • 外部格式:UTF/ASCII/非ASCII
抽象流类–Reader和Writer
  • java.io包中所有字符流的抽象超类
  • Reader提供了输入字符的API
  • Writer提供了输出字符的API
  • 子类分为两大类
    • 节点流:从数据源读入数据或往目的地写出数据
    • 处理流:对数据执行某种处理
image-20231030195343714

多数程序使用这两个抽象类的一系列子类来读入/写出文本信息

例如FileReader/FileWriter用来读/写文本文件

e.g.

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in))//字符流,处理流;
        String s;
        while ((s = in.readLine()).length() != 0) {
            System.out.println(s);
        }
    }
}

输入Hello 后回车,输出结果为:

Hello

Hello

  • System.in

    • 程序启动时由Java系统自动创建的流对象,它是原始的字节流,不能直接从中读取字符,需要对其进行进一步的处理
  • InputStreamReader(System.in)

    • 以System.in为参数创建一个InputStreamReader流对象,相当于字节流和字符流之间的一座桥梁,读取字节并将其转换为字符
  • BufferedReader in

    • 对InputStreamReader处理后的信息进行缓冲,以提高效率

e.g.

import java.io.*;
public class FinallyTester1 {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String s;
        try {
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
            try {while ((s = in.readLine()).length() != 0) { out.write(s); }
            } 
            catch (IOException ioe) { ioe.printStackTrace(); } 
            finally { out.close(); }
        } 
        catch (IOException ioe) { ioe.printStackTrace(); } 
        finally {
            in.close();
        }
    }
}
image-20231030200852493

通过finally关闭资源

try-with-resource自动关闭Closable接口资源

JDK7之后,新增了"try-with-resource”。它可以自动关闭实现了Closable接口的类,实现类需要实现close()方法。”try-with-resources 声明”,将 try-catch-finally简化为try-catch,这其实是一种语法糖,在编译时仍然会进行转化为try-catch-finally 语句

import java.io.*;
public class TryWithResourcesTester1 {
    public static void main(String[] args) {
        String s;
        try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
             BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
        ) {
            while ((s = in.readLine()).length() != 0) {
                out.write(s);
            }
        } catch (IOException ioe) { ioe.printStackTrace(); }
    }
}
面向字节的流
  • 数据源或目标中含有非字符数据,必须用字节流来输入/输出

  • 通常被用来读写诸如图片、声音之类的二进制数据

  • 绝大多数数据是被存储为二进制文件的,通常二进制文件要比含有相同数据量的文本文件小得多

抽象流类–InputStream和OutputStream
  • 用来处理字节流的抽象基类,程序使用这两个类的子类来读写字节信息
  • 分为两类
    • 节点流
    • 处理流
image-20231030195713985

输入输出

标准输入输出流对象(System)

System类的静态成员变量(字节流)

包括:

  • System.in:InputStream类型的,代表标准输入流,默认状态对应于键盘输入
  • System.out:PrintStream类型的,代表标准输出流,默认状态对应于显示器输出
  • System.err:PrintStream类型的,代表标准错误信息输出流,默认状态对应于显示器输出
按类型输入/输出数据
  • printf方法

    System.out.printf(%-12s is %2d long, name, l);
    System.out.printf(“value = %2.2F, value);
    //%n 是平台无关的换行标志
    
  • Scanner

    Scanner s = new Scanner(System.in);
    int n = s.nextInt();
    还有下列方法:nextByte(),nextDouble(),nextFloat,nextInt(),nextLine(),nextLong(),nextShort()
    
    image-20231030201406685
标准输入/输出重定向(复制文件)
import java.io.*;
public class Redirecting {
public static void main(String[] args)  throws IOException {
    BufferedInputStream in = new BufferedInputStream(new FileInputStream( "Redirecting.java"));
    PrintStream out = new PrintStream( new BufferedOutputStream( new FileOutputStream("test.out")));
    System.setIn(in);
    System.setOut(out);
    System.setErr(out);
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String s;
    while((s = br.readLine()) != null)   System.out.println(s);
    in.close();
    out.close();
    br.close()
  }
} 
  • setIn(InputStream): 设置标准输入流

  • setOut(PrintStream):设置标准输出流

  • setErr(PrintStream):设置标准错误输出流

写文本文件

FileWriter类是一个以字符方式写文件内容的Writer类的子类,最常用构造方法如下:

  • FileWriter(String filePath)
  • FileWriter(String filePath, boolean append)
  • FileWriter(File fileobj)
IO异常

多数IO方法在遇到错误时会抛出异常,因此调用这些方法时必须在如下操作中的一个:

  • 在方法头声明抛出IOException异常
  • 在try块中执行IO,然后在catch块中捕获IOException异常
创建文件并写入文件
import java.io.*;   
class FileWriterTester1 {
  public static void main ( String[] args ) throws IOException {  
     //main方法中声明抛出IO异常
     String fileName = "Hello.txt"; 
     FileWriter writer = new FileWriter( fileName );   
     writer.write( "Hello!\n"); 
     writer.write( "This is my first text file,\n"  );  
     writer.write( "You can see how this is done.\n" ); 
     writer.write("输入一行中文也可以\n");
     writer.close(); 
  }
}
import java.io.*;   
class FileWriterTester2 {
    public static void main ( String[] args ) {
          String fileName = “d:\\Hello.txt" ;
          try {  //将所有IO操作放入try块中
  	   	  FileWriter writer = new FileWriter( fileName ,true );    //追加
 	   	  writer.write( "Hello!\n");
       	  writer.write( "This is my first text file,\n"  );  
    	  writer.write( "You can see how this is done. \n" );
          writer.write("输入一行中文也可以\n");
     	   writer.close();
          }
          catch ( IOException iox) {  System.out.println("Problem writing" + fileName ); }
    }
}
  • 运行此程序,会发现在原文件内容后面又追加了重复的内容,这就是将构造方法的第二个参数设为true的效果
  • 如果将文件属性改为只读属性,再运行本程序,就会出现IO错误,程序将转入catch块中,给出错误信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

BufferedWriter类

多提供了一个newLine()方法用于换行

C盘根目录创建文本文件Hello.txt,并往里写入若干行文本,使用BufferedWriter完成
1. 创建写对象:out\ writer 
     FileWriter writer = new FileWriter( fileName );
     BufferedWriter out = new BufferedWriter( 
                                              new  FileWriter( fileName ) );
2. 写:调用对象的write方法,写入文件
    out.write( "Hello!\n"); 
3.关闭写对象
    out.close();

读文本文件

FileReader类
  • 从文本文件中读取字符
  • 最常用构造方法:
    • FileReader ( String filePath )
    • FileReader ( File fileobj)
BufferedReader
  • 读文本文件的缓冲器类。

  • 具有readLine()方法,可以对换行符进行鉴别,一行一行地读取输入流中的内容。

  • 继承自Reader。

Hello.txt中读取文本并显示在屏幕上
1、创建流文件:
      BufferedReader in = new BufferedReader(
                  new FileReader( fileName  ) );
2、读文件、显示:   
      line = in.readLine();
      system.out
3、关闭文件:    
      in.close(); 
4、考虑异常:  
      try catch

写二进制文件

抽象类OutPutStream
  • 派生类FileOutputStream

    • 用于一般目的的输出(非字符输出)
    • 用于成组字节输出
  • 派生类DataOutputStream

    • 具有写各种基本数据类型的方法
    • 将数据写到另一个输出流
    • 在所有的计算机平台上使用同样的数据格式
    • 其中size方法,可作为计数器,统计写入的字节数
  • BufferedOutputStream类

    • 写二进制文件的缓冲类

    • 对于大量数据的写入,可提高效率

    • DataOutputStream out = new DataOutputStream( 
                          new BufferedOutputStream( new FileOutputStream( fileName ) ) ); 
      
    • import java.io.*;
      public class ByteIOTester {
         public static void main(String[] args) throws Exception {
            DataOutputStream out=new DataOutputStream(
                                  new FileOutputStream("try.dat"));
      	  out.writeByte(-1);  out.close();
      	  DataInputStream in=new DataInputStream(
                                  new FileInputStream("try.dat"));
      	  int a=in.readByte();
      	  System.out.println(Integer.toHexString (a));
      	  System.out.println(a);
      	  in.skip (-1);  //往后一个位置,以便下面重新读出
      	  a=in.readUnsignedByte();
      	  System.out.println(Integer.toHexString (a));
      	  System.out.println(a);  in.close();		
         }
      }
      /*
      如果用readByte读入,其高24位都将补1,所以结果还是-1
      如果用readUnsignedByte读入,其高24位都将补0,结果就变成了255
      写的字节是连续的,中间没有分隔符,所以应该记住写的数据类型、个数等情况,以便
      

读二进制文件

  • FileInputStream

  • DataInputStream

  • BufferedInputSteam

DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream( fileName )));
读写字节
DataOutputStream的writeByte方法
 	public final void writeByte(int b) throws IOExceptionint的最不重要字节写入输出流
DataInputStream的readUnsignedByte方法
 	public final int readUnsignedByte() throws IOException
	从输入流中读取1字节存入int的最不重要字节

File类

作用:

  • 表示磁盘文件信息

  • 定义了一些与平台无关的方法来操纵文件

  • 创建、删除文件;

  • 重命名文件;

  • 判断文件的读写权限及是否存在;

  • 设置和查询文件的最近修改时间等;

  • 构造文件流可以使用File类的对象作为参数。

File f=new File("Hello.txt");
f.exists() 判断文件是否存在
f.delete() 删除文件
f.createNewFile() 创建新文件

第六章 Java集合框架

概念:

  • 为表示和操作集合而规定的一种统一的标准的体系结构

  • 对外的接口:一套具有层次结构的抽象数据类型,它将集合的操作与表示分开

  • 接口的实现:指实现集合接口的Java类,是可重用的数据结构

  • 对集合运算的算法:是指执行运算的方法

1、Collection接口

  • 声明了一组操作成批对象的抽象方法,包括查询和修改方法

  • 包含两个主要的子接口是集合(Set)与列表(List)

  • Collection接口声明时应该使用泛型,即Collection<E>

  • 实现它的类:AbstractCollection

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常用方法:

查询方法
int size() – 返回集合对象中包含的元素个数
boolean isEmpty() – 判断集合对象中是否还包含元素,如果没有任何元素,则返回true
boolean contains(Object obj) – 判断对象是否在集合中
boolean containsAll(Collection c) – 判断方法的接收者对象是否包含集合中的所有元素
修改方法
boolean add(Object obj) – 向集合中增加对象
boolean addAll(Collection<?> c) – 将参数集合中的所有元素增加到接收者集合中
boolean remove(Object obj) –从集合中删除对象
boolean removeAll(Collection c) -将参数集合中的所有元素从接收者集合中删除
boolean retainAll(Collection c) – 在接收者集合中保留参数集合中的所有元素,其它元素都删除
void clear() – 删除集合中的所有元素

2、Set接口

  • 禁止重复的元素,是数学中“集合”的抽象

  • 对equals和hashCode操作有了更强的约定,如果两个Set对象包含同样的元素,二者便是相等的

  • 哈希集合(HashSet)及树集合(TreeSet)

3、SortedSet接口

  • 一种特殊的Set

  • 其中的元素是升序排列的,还增加了与次序相关的操作

  • 通常用于存放词汇表这样的内容

  • 实现它的类: TreeSet

4、List接口

  • 可包含重复元素;

  • 元素是有顺序的,每个元素都有一个index值(从0开始)标明元素在列表中的位置。

Vector

ArrayList

一种类似数组的形式进行存储,因此它的随机访问速度极快

LinkedList

内部实现是链表,适合于在链表中间需要频繁进行插入和删除操作

Stack

5、Queue接口

除了Collection 的基本操作,队列接口另外还有插入、移除和查看操作

FIFO (先进先出,first-in-first-out)

LinkedList

PriorityQueue

按元素值排序的队列

6、Deque接口

继承自Queue接口,也可被称之为”双向队列“

它支持从队列的头、尾两端对元素进行插入、移除和查看等操作

Deque的双向特性使它能够作为一种后进先出(last-in-first-out,LIFO)的集合来使用

7、Map接口

  • 用于维护键/值对(key/value pairs)

  • 不能有重复的关键字,每个关键字最多能够映射到一个值

  • 声明时可以带有两个参数,即Map<K, V>,其中K表示关键字的类型,V表示值的类型

查询方法

int size() —— 返回Map中的元素个数 
boolean isEmpty() —— 返回Map中是否包含元素,如不包括任何元素,则返回true
boolean containsKey(Object key) —— 判断给定的参数是否是Map中的一个关键字(key) 
boolean containsValue(Object val) —— 判断给定的参数是否是Map中的一个值(value) 
Object get(Object key) —— 返回Map中与给定关键字相关联的值(value)
Collection values() —— 返回包含Map中所有值(value)Collection对象 
Set keySet() ——返回包含Map中所有关键字(key)Set对象
Set entrySet() —— 返回包含Map中所有项的Set对象 

修改方法

Object put(Object key, Object val) —— 将给定的关键字(key)/(value)对加入到Map对象中。其中关键字(key)必须唯一,否则,新加入的值会取代Map对象中已有的值
void putAll(Map m) —— 将给定的参数Map中的所有项加入到接收者Map对象中
Object remove(Object key) —— 将关键字为给定参数的项从Map对象中删除 
void clear() —— 从Map对象中删除所有的项

8、SortedMap接口

Map的子接口

一种特殊的Map,其中的关键字是升序排列的

9、常用类

HashMap

称为散列表,是用来存储群体对象的集合类结构

哈希表存储对象的方式

对象的位置和对象的关键属性k之间有一个特定的对应关系f,我们称之为哈希(Hash)函数。它使每个对象与一个唯一的存储位置相对应。因而在查找时,只要根据待查对象的关键属性k,计算f(k)的值即可知其存储位置

构造方法

HashMap() 
默认容量16,默认装填因子为0.75HashMap(int initialCapacity) 
容量initialCapacity ,默认装填因子为0.75HashMap(int initialCapacity, float loadFactor) 
容量initialCapacity ,装填因子为loadFactor。 
HashMap(Map<? extends K,? extends V> m) 
以参数m为初值构造新的HashMap. 

其他方法

void clear() 
Object clone() 
boolean containsKey(Object key) 
boolean containsValue(Object value) 
Set<Map.Entry<K,V>> entrySet() 
void forEach(BiConsumer<? super K,? super V> action) 
V get(Object key) 
V getOrDefault(Object key, V defaultValue) 
boolean isEmpty() 
Set<K> keySet() 
V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 
V put(K key, V value) 
void putAll(Map<? extends K,? extends V> m) 
V remove(Object key) 
boolean remove(Object key, Object value)
V replace(K key, V value) 
boolean replace(K key, V oldValue, V newValue) 
void replaceAll(BiFunction<? super K,? super V,? extends V> function) 
int size() 
Collection<V> values()  

Vector, ArrayList

  • 实现了Collection接口
  • 能够存储相同类型(或具有相同的父类或接口)的对象
  • 不能存储基本类型(primitive)的数据,要将基本类型数据包裹在包裹类中
  • 其容量能够根据空间需要自动扩充
  • 增加元素方法的效率较高,除非空间已满(在这种情况下,在增加之前需要先扩充容量)
  • Vector:集合框架中的遗留类,旧线程安全集合
  • ArrayList方法是非同步的,效率较高

Vector方法

size()
capacity()
addElement(对象)
firstElement()
lastElement()
contains(对象)

ArrayList构造方法

ArrayList() 
	构造一个空表,默认容量为10ArrayList(Collection<? extends E> c) 
	用参数集合元素为初始值构造一个表。
ArrayList(int initialCapacity) 
	构造一个空表,容量为initialCapacity 。

其他方法

boolean add(E e) 
void add(int index, E element) 
boolean addAll(Collection<? extends E> c) 
boolean addAll(int index, Collection<? extends E> c) 
void clear() 
Object clone() 
boolean contains(Object o) 
void ensureCapacity(int minCapacity) 
void forEach(Consumer<? super E> action) 
E get(int index)
int indexOf(Object o) 
boolean isEmpty() 
Iterator<E> iterator()
int lastIndexOf(Object o) 
ListIterator<E> listIterator() 
ListIterator<E> listIterator(int index) 
E remove(int index) 
boolean remove(Object o) 
boolean removeAll(Collection<?> c) 
boolean removeIf(Predicate<? super E> filter) 
protected void removeRange(int fromIndex, int toIndex) 
void replaceAll(UnaryOperator<E> operator) 
boolean retainAll(Collection<?> c) 
E set(int index, E element) 
int size()
void sort(Comparator<? super E> c) 
(
	Collections.sort(sites)
)    
Spliterator<E> spliterator() 
List<E> subList(int fromIndex, int toIndex) 
Object[] toArray() 
<T> T[] toArray(T[] a) 
void trimToSize()

LinkList

LinkedList通过双向链表(doubly-linked list)数据结构实现了List和Deque接口。由于同时具备List、Queue和Deque的特性,LinkedList的应用场景比较广泛

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

迭代器

Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法

Iterator能够从集合类对象中提取每一个元素,并提供了用于遍历元素的方法,还具有从正在遍历的集合中去除对象的能力

具有如下三个实例方法:

  • hasNext() —— 判断是否还有元素。

  • next() —— 取得下一个元素。

  • remove() —— 去除一个元素。注意是从集合中去除最后调用next()返回的元素,而不是从Iterator类中去除。

import java.util.Arrays;
import java.util.Iterator;
import java.util.Vector;
public class IteratorTester {
    public static void main(String[] args) {
        String[] num = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"};
        Vector<String> vector = new Vector<>(Arrays.asList(num));
        System.out.println("The initial Vector is: " + vector);
        Iterator<String> nums = vector.iterator();
        while (nums.hasNext()) {
            String aString = nums.next();
            System.out.println(aString);
            if (aString.length() > 4) {  nums.remove();    }
        }
        System.out.println("The Vector after iteration is: " + vector);
    }
}
------
The initial Vector is: [one, two, three, four, five, six, seven, eight, nine, ten]
one
two
three
four
five
six
seven
eight
nine
ten
The Vector after iteration is: [one, two, four, five, six, nine, ten]

第七章 线程

Thread类

  • 直接继承了Object类,并实现了Runnable接口,位于java.lang包中

  • 封装了线程对象需要的属性和方法

  • 继承Thread类——创建多线程的方法之一

    • 从Thread类派生一个子类,并创建子类对象
    • 子类应覆盖Thread类的run方法,写入需要在新线程中执行的语句段
    • 调用start方法来启动新线程,自动进入run方法
class Thread1 extends Thread {
	private int num;  
    public Thread1(int num ) {
    	this.num=num;
   	}
    public void run() {  
          int i=num; 
          int result=1;   
          System.out.println("new thread started" );        
          while(i>0) {
               result=result*i;   i=i-1; 
          } 
          System.out.println("The factorial of "+num+" is "+result);  
          System.out.println("new thread ends");  
    } 
} 
public class ThreadTester 
 {
   public static void main( String [] args ) 
   {     //main方法执行也是一条默认的线程
	     System.out.println("main thread starts");
	     Thread1 thread=new Thread1(10);
         thread.start();    //确定线程,将自动进入run()方法   
         System.out.println("main thread ends " );        
   }
} 
------
main thread starts
main thread ends
new thread started
The factorial of 10 is 3628800
new thread ends
说明:
main线程已经执行完后,新线程才执行完
main方法调用thread.start()方法启动新线程后并不等待其run方法返回就继续运行,thread.run方法在一边独自运行,不影响原来的main方法的运行

如果启动新线程后希望主线程多持续一会再结束,可在start语句后加上让当前线程(这里当然是main)休息1毫秒的语句:

try { 
	Thread.sleep(1); 

} 
catch(Exception e) {};

启动线程必须用start()方法,不是run()方法,直接调用run()方法不启动线程,只是一次简单的对象方法调用,还是单线程执行

Runnable接口

  • Thread类实现了Runnable接口

  • 只有一个run()方法

  • 更便于多个线程共享资源

  • Java不支持多继承,如果已经继承了某个超类,便需要实现Runnable接口来生成多线程

  • 以实现runnable的对象为参数建立新的线程

  • start方法启动线程就会运行run()方法

class Thread2 implements Runnable {  //任务类
   private int num;  
   public Thread2( int num ) {
      this.num=num;
   } 
   public void run()  {  
      int i=num; 
      int result=1;   
      while(i>0) {
          result=result*i;
          i=i-1;
      }
      System.out.println("The factorial of "+num+" is "+result);  
      System.out.println("new thread ends");  
    }     
} 
public class RunnableTester {
   public static void main( String [] args ) {
      System.out.println("main thread starts");
      Thread2 t=new Thread2(10);//实现了Runnable的
      new Thread(t).start();  //运行FactorialThread的run
      System.out.println("new thread started,main thread ends " );   
   }
} 

线程间的数据共享

  • 用同一个实现了Runnable接口的对象作为参数创建多个线程
  • 多个线程共享同一对象中的相同的数据
class TestThread3 implements Runnable {
       private int sleepTime;   
       public TestThread3()  {
            sleepTime = ( int ) ( Math.random() * 6000 );
       }       
       public void run() {      
           try {
                   System.out.println( 
                   Thread.currentThread().getName() + " going to sleep for " + sleepTime );            
                   Thread.sleep( sleepTime ); 
           } 
           catch ( InterruptedException exception ) {};
	      System.out.println( Thread.currentThread().getName() + "finished" );
       }     
}
public class ShareRunnableTester  {
   public static void main( String [] args )  {
   	 TestThread3 threadobj = new TestThread3();        
      System.out.println( "Starting threads" );
      
      new Thread(threadobj,"Thread1").start();  
      new Thread(threadobj,"Thread2").start();
      new Thread(threadobj,"Thread3").start();
        
      System.out.println( "Threads started, main ends\n" );        
   }     
}
------
Starting threads
Thread1 going to sleep for 966
Thread2 going to sleep for 966
Threads started, main ends
Thread3 going to sleep for 966
Thread1 finished
Thread2 finished
Thread3 finished
因为是用一个Runnable类型对象创建的3个新线程,这三个线程就共享了这个对象的私有成员sleepTime,在本次运行中,三个线程都休眠了966毫秒
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值