Java期末复习
Java期末复习
一个简单的小复习,后面要详细再细细学习,祝Java考试大捷😎!
PPT-1
问题:
为什么Java编程语言被认为是平台独立的?
答案:
编译后的代码可以在多个平台上运行,几乎无需修改。
解释:
Java被认为是平台独立的,因为它采用了一次编写,到处运行的理念。当Java代码被编译时,它被转换成字节码(而非本机机器码),并保存在.class
文件中。然后,这些字节码可以在任何安装了Java虚拟机(JVM)的平台上运行,无论底层的硬件和操作系统是什么。只要平台上有JVM,编译后的Java代码就能不做修改地运行。这就是Java平台独立性的来源。
以下是其他选项为何不正确的解释: - 不允许使用指针来操作内存:虽然Java不允许指针操作(以避免错误并增强安全性),但这并不是Java平台独立性的直接原因。 - 编译后的Java程序格式是CPU特定的代码:这是错误的。Java编译后的代码是字节码,而不是CPU特定的机器码。JVM在运行时将字节码转换为平台特定的机器码。 - 它是多线程的:虽然Java支持多线程,但这与其平台独立性没有直接关系。多线程主要涉及并发执行,而不是Java代码在多个平台上的运行能力。
问题:
The Java technology product group that is designed for developing applications for consumer devices is _______.
选项:
- Java SE JDK
- Java ES SDK
- Java EE SDK
- Java ME SDK
答案:
- Java ME SDK
解释:
Java ME (Micro Edition) 是为嵌入式和移动设备(例如手机、消费类电子产品等)设计的Java技术平台。Java ME SDK提供了开发、测试和部署Java应用程序的工具,专门用于低功耗设备和资源受限的环境,因此它适用于消费设备。
其他选项解释:
- Java SE JDK (Standard Edition):这是用于开发桌面、服务器和嵌入式系统应用程序的Java开发工具包,但它不是专门为消费类设备设计的。
- Java ES SDK (Enterprise System):这是为企业应用开发设计的Java技术平台,通常用于构建大型企业级应用系统。
- Java EE SDK (Enterprise Edition):这是面向企业级应用开发的Java平台,主要用于构建Web应用、企业应用等,和消费设备开发无关。
Problem 1: Write a program to display "Good Morning".
Java Program:
1 | public class GoodMorning { |
Explanation: This Java program creates a class
GoodMorning
with a main
method, which is the
entry point of the program. The System.out.println
statement is used to display the text "Good Morning" on the console.
Problem 2: What command is used to compile a Java program?
Answer: To compile a Java program, you use the
javac
command.
Command:
1 | javac ProgramName.java |
Explanation:
- The
javac
command is used to compile Java source code (.java
file) into bytecode (.class
file). - Example:
javac GoodMorning.java
compiles theGoodMorning.java
source file and creates aGoodMorning.class
file (bytecode).
Problem 3: What command is used to run a Java program?
Answer: To run a Java program, you use the
java
command.
Command:
1 | java ProgramName |
Explanation:
- The
java
command is used to run the bytecode (.class
file) generated after compilation. - Example:
java GoodMorning
runs theGoodMorning.class
file and prints "Good Morning" to the console.
PPT-2
计算未来投资价值的 Java 程序
问题描述:
编写一个程序,输入投资金额、年利率和年数,使用以下公式计算未来的投资价值:
[ = (1 + )^{ } ]
例如,如果输入的金额为 1000,年利率为 3.25%,年数为 1年,则未来投资价值为 1032.98。
提示: 可以使用 Math.pow(a, b)
方法计算
a 的 b 次方。
样例运行:
1 | 样例 1: |
程序代码:
1 | import java.util.Scanner; |
代码解释:
- 输入部分:
- 使用
Scanner
获取用户输入的投资金额、年利率和年数。
- 使用
- 计算月利率:
monthlyInterestRate = annualInterestRate / 1200
,因为年利率是百分比,所以要除以 1200 将年利率转换为月利率。
- 计算未来价值:
- 使用公式
futureValue = investmentAmount * Math.pow(1 + monthlyInterestRate, numOfYears * 12)
计算未来价值。Math.pow(a, b)
方法计算a
的b
次方。
- 使用公式
- 输出未来价值:
System.out.print("未来投资价值是:" + (int)(futureValue * 100) / 100.0);
通过乘以 100 转换为整数,再除以 100 保留两位小数。
示例输入输出:
- 输入:
- 投资金额:1000
- 年利率:4.25
- 年数:1
- 输出:
- 未来投资价值是:1043.34
注意事项:
- 程序中的
Math.pow(a, b)
用于计算(1 + 月利率) 的年数乘以12次方
。 - 输出保留两位小数,使用
(int)(futureValue * 100) / 100.0
实现。
PPT-3
无
PPT-4
寻找最大数及其出现次数
问题描述:
编写一个程序,读取一组整数,找出其中的最大数,并统计其出现的次数。假设输入以数字 0 结束。
例如,输入为 3 5 2 5 5 5 0
,程序会输出:
1
2最大数是 5
最大数的出现次数是 4
提示: - 使用两个变量 max
和
count
: - max
存储当前的最大值。 -
count
存储最大值的出现次数。 - 初始时,将第一个数字赋值给
max
,count
设为 1。 - 比较每个后续数字: -
如果数字大于 max
,将其赋值给 max
,并重置
count
为 1。 - 如果数字等于 max
,则将
count
增加 1。
程序设计思路:
- 初始化:
- 输入的第一个数字赋值给
max
。 - 初始化
count
为 1,表示当前最大值第一次出现。
- 输入的第一个数字赋值给
- 处理后续数字:
- 遍历每个输入的数字:
- 如果数字大于当前的
max
,则更新max
并重置count
为 1。 - 如果数字等于
max
,则增加count
。
- 如果数字大于当前的
- 遍历每个输入的数字:
- 结束条件:
- 输入结束时,遇到数字 0,停止输入并输出结果。
程序代码:
1 | import java.util.Scanner; |
代码解释:
- 输入与初始化:
int max = input.nextInt();
读取第一个输入的数字并赋值给max
。int count = 1;
初始化count
为 1,表示第一个数字出现一次。
- 循环输入:
while (true)
循环持续输入直到遇到 0 为止。- 每次输入一个数字,将其赋值给
num
。 - 如果
num == 0
,则结束输入循环。
- 比较并更新:
if (num > max)
:如果当前输入的数字大于max
,则更新max
,并重置count
为 1,表示当前最大值出现一次。else if (num == max)
:如果当前数字与max
相等,则将count
加 1。
- 输出结果:
- 最后输出
max
和count
,分别表示最大数和它的出现次数。
- 最后输出
示例输入与输出:
输入:
1
3 5 2 5 5 5 0
输出:
1
2最大数是 5
最大数的出现次数是 4
边界情况:
- 没有输入任何数字:
- 由于程序设计要求输入以
0
结束,如果没有输入其他数字,程序会直接输出:1
2最大数是 0
最大数的出现次数是 1
- 由于程序设计要求输入以
- 只有一个数字:
- 如果输入的只有一个数字并且是非零的,那么该数字就是最大值,出现次数为 1。
- 所有输入数字相同:
- 如果所有输入的数字相同,则程序会统计这个数字的出现次数。
PPT-5
无
PPT-6
计算整数各位数字和的程序
问题描述:
编写一个方法
sumDigits(long n)
,计算一个整数的各位数字之和。
示例: - 输入:234
-
输出:9
(即:2 + 3 + 4)
提示:
- 使用
%
运算符来提取数字的最后一位。- 例如:
234 % 10
提取出 4。
- 例如:
- 使用
/
运算符来移除数字的最后一位。- 例如:
234 / 10
变成 23。
- 例如:
通过不断提取和移除数字,直到所有数字被处理完。
程序设计思路:
- sumDigits 方法:
- 输入:一个长整型数字
n
。 - 处理:
- 取
n
的绝对值,因为负数的每一位数字的和与正数相同。 - 使用
%
和/
运算符提取数字,并累加到sum
变量中。 - 使用
while
循环不断处理数字,直到所有位数被处理完。
- 取
- 输出:数字各位数之和。
- 输入:一个长整型数字
- Test 类:
- 输入:提示用户输入一个整数。
- 输出:调用
sumDigits
方法,计算并显示数字的各位和。
程序代码:
1 | import java.util.Scanner; |
程序流程:
- 输入:
- 程序提示用户输入一个整数
value
。
- 程序提示用户输入一个整数
- sumDigits 方法:
- 将输入的数字
n
取绝对值,处理负数情况。 - 初始化
temp
为数字的绝对值,并初始化sum
为 0。 - 使用
while
循环:- 每次通过
% 10
获取当前数字的最后一位。 - 使用
/ 10
去除当前数字的最后一位。 - 将提取的数字累加到
sum
中。
- 每次通过
- 循环直到所有数字处理完。
- 返回
sum
。
- 将输入的数字
- 输出:
- 程序输出用户输入数字的各位数字之和。
示例运行:
输入:
1
Enter a number: 234
输出:
1
The sum of digits for 234 is 9
程序解析:
sumDigits
方法:- 首先,方法将输入的数字取绝对值,以便处理负数的情况。
- 接着,进入
while
循环,每次提取数字的最后一位,并加到sum
中。 - 每次通过除以 10 移除数字的最后一位,直到
temp
为 0,表示所有位数处理完。
main
方法:- 通过
Scanner
类读取用户输入的整数。 - 调用
sumDigits
方法,输出结果。
- 通过
边界情况:
- 输入为负数:
- 例如输入
-234
,程序会自动计算其绝对值,输出 9。
- 例如输入
- 输入为 0:
- 当输入为
0
时,输出结果应为 0。
- 当输入为
- 输入为单个数字:
- 如果输入为单个数字,如
7
,则输出结果为该数字本身。
- 如果输入为单个数字,如
PPT-7
1. 声明数组变量的语句:
声明一个名为
x
的数组,类型为double
:1
double[] x;
double[]
表示数组的类型为double
数组。x
是数组的名称。
2.
创建一个大小为 40 的 int
类型数组的表达式:
1 | int[] arr = new int[40]; |
int[]
表示该数组是存储int
类型数据的数组。new int[40]
表示创建一个长度为 40 的int
数组。
3.
java.util.Arrays.binarySearch
的用法:
binarySearch
方法是用于在已排序的数组中进行二分查找。该方法返回目标值的索引,如果目标值不存在,返回负值(其负值加
1 表示插入位置)。
java.util.Arrays.binarySearch(new int[]{1, 2, 3, 7, 14}, 7)
:- 数组
new int[]{1, 2, 3, 7, 14}
是一个已排序的数组。 - 目标值为
7
。 - 结果:
binarySearch
会返回目标值7
在数组中的索引,索引为3
(从 0 开始计数)。
输出:
1
3
- 数组
java.util.Arrays.binarySearch(new int[]{1, 2, 3, 7, 14}, 5)
:- 数组
new int[]{1, 2, 3, 7, 14}
中没有值为5
的元素。 - 由于
5
不在数组中,binarySearch
方法返回的是插入位置的负值加 1,即-4
。 - 这是因为
5
应该插入到索引3
(7
前面)的位置,所以返回的负数是-(3 + 1)
,即-4
。
输出:
1
-4
- 数组
4. 代码的输出分析:
1 | public class Test { |
解释:
number
变量:- 在
main
方法中,number
被初始化为0
。 - 在调用
m(number, numbers)
时,number
被按值传递给方法m
,因此在方法内修改x
对number
没有任何影响,number
的值保持为0
。
- 在
numbers
数组:numbers
数组被初始化为大小为 1 的数组,且初始值为0
。- 在
m
方法中,numbers[0]
被设置为3
,因此这个数组的值被修改为3
。 - 注意:数组是按引用传递的,所以在方法内修改
numbers[0]
会影响到main
方法中的numbers
数组。
程序输出:
1 | number is 0 and numbers[0] is 3 |
number
的值未改变,仍为0
。numbers[0]
被修改为3
,所以输出为3
。
5. 精华所在:
- 声明和初始化数组: 可以通过
double[] x
或int[] arr
声明数组,并通过new
关键字初始化数组。 - 二分查找:
Arrays.binarySearch
用于在已排序的数组中查找元素。返回值为目标值的索引,若不存在该值则返回负值。 - 值传递与引用传递:
- 值传递: 基本数据类型(如
int
)是按值传递的,修改方法内的变量不会影响外部变量。 - 引用传递: 对象(如数组)是按引用传递的,方法内对对象的修改会影响外部对象的状态。
- 值传递: 基本数据类型(如
PPT-9
1. 声明 Circle
对象的变量:
- 声明一个
Circle
类型的变量x
:
1 | Circle x; |
- 这里
Circle
是对象类型,x
是对象变量,用于引用一个Circle
类的对象。
2.
使用无参构造函数创建 Circle
对象:
1 | Circle circle = new Circle(); |
Circle()
是无参数构造函数,它会创建一个默认的Circle
对象。
3. 声明
Circle
类型的变量并初始化:
1 | Circle x = new Circle(5.5); |
- 这里
Circle(5.5)
假设构造函数Circle(double radius)
已存在,用来初始化一个半径为5.5
的Circle
对象。
4. 调用
getArea()
方法返回 Circle
对象的面积:
1 | double area = c.getArea(); |
- 假设
c
是一个已经创建的Circle
对象,并且getArea()
方法存在,用来计算并返回圆的面积。
5. 创建一个
Date
对象,表示当前时间:
1 | Date currentDate = new Date(); |
Date
类表示一个具体的时间点。通过new Date()
创建一个表示当前时间的Date
对象。
6. 代码输出分析:
1 | public class Test { |
解释:
- 类
A
的成员变量:i
是实例变量,属于每个A
对象。每次创建A
类的实例时,i
都会初始化为1
,并且在构造函数中i
会增加1
,所以每个对象的i
值会是2
。j
是静态变量,属于类A
,而不是某个实例。静态变量在类的所有实例之间共享,所以j
只有一个值,所有对象都共享这个值。
- 构造函数:
- 每次创建
A
类的实例时,构造函数会被调用,i
和j
都会被加1
。由于i
是实例变量,因此每个实例的i
都会独立增加。而j
是静态变量,因此所有实例共享j
,它只会增加一次。
- 每次创建
- 代码执行:
- 创建
a1
对象时,i
被初始化为1
,然后在构造函数中增加1
,所以i
变为2
。同时,静态变量j
也被加1
,所以j
变为2
。 - 创建
a2
对象时,i
也被初始化为1
,然后增加1
,所以i
变为2
。但是静态变量j
在两个对象之间共享,已经增加过一次,所以j
变为3
。
- 创建
输出:
1 | 2 3 |
- 第一个输出是
a1.j
,由于j
被加过一次,a1.j
的值为2
。 - 第二个输出是
a2.j
,j
被加过两次,所以a2.j
的值为3
。
7. 归纳:
- 实例变量与静态变量:
- 实例变量(如
i
)是每个对象独有的,每次创建一个对象时,都会为实例变量分配一个新值。 - 静态变量(如
j
)是类级别的变量,所有对象共享一个静态变量,因此它的变化会影响到所有对象。
- 实例变量(如
- 构造函数:
- 在每个对象创建时,构造函数都会被调用,实例变量
i
会根据对象初始化并修改,而静态变量j
是类级别的,因此它的值在所有对象间共享。
- 在每个对象创建时,构造函数都会被调用,实例变量
PPT-10
String、StringBuffer与StringBuilder的性能比较
1. 程序示例与性能测试
通过以下程序,我们比较了
String
、StringBuffer
和
StringBuilder
在进行字符串拼接时的性能:
1 | public class StringMore { |
该程序通过测试不同的字符串拼接方式来测量执行时间。分别测试了使用
String
、StringBuffer
和
StringBuilder
对同一个字符串进行拼接的性能。
2. 性能比较
String
:每次拼接字符串时都会创建一个新的字符串对象,性能较差。因为String
是不可变的,每次修改都会创建新的对象,因此在频繁拼接时效率较低。StringBuffer
:线程安全的字符串缓冲区。每次拼接时,StringBuffer
会扩展它的内部数组并追加字符,因此在多次拼接时效率较高。StringBuffer
使用了synchronized
关键字来确保线程安全,导致它的性能较StringBuilder
略逊一筹。StringBuilder
:与StringBuffer
类似,但是它没有synchronized
关键字,因此它比StringBuffer
更快,适用于单线程环境。
执行效率: - StringBuilder
>
StringBuffer
> String
3. 优化与建议
String拼接的优化:当直接用
+
操作符拼接字符串时,Java 编译器会对这种情况进行优化,将多个字符串拼接转换为一个StringBuilder
或StringBuffer
对象的调用。例如:这样优化的效率高于手动创建1
String str = "my" + "hello" + "world"; // 编译器优化为 "my" + "hello" + "world"
StringBuilder
对象并调用append
方法:1
StringBuilder st = new StringBuilder("my").append("hello").append("world");
选择合适的类:
String
:适合拼接次数较少,或者只有少量字符串拼接的场合。StringBuilder
:适合在单线程环境下,进行大量字符串拼接的场合。StringBuffer
:适合在多线程环境下进行字符串拼接的场合,虽然它的性能稍逊于StringBuilder
。
4. 成员方法对比
- String:不可变,每次拼接都会创建新的对象。
- StringBuffer 和
StringBuilder:都实现了可变的字符串操作,具有
append
、insert
等方法,可以高效地进行字符串拼接。
StringBuffer
:是线程安全的,使用了synchronized
关键字。StringBuilder
:不是线程安全的,性能较StringBuffer
更优。
5. 精华
- 当需要频繁修改或拼接字符串时,推荐使用
StringBuilder
(单线程)。 - 在多线程环境中,使用
StringBuffer
(它是线程安全的)。 - 如果拼接次数较少,使用
String
即可,因为它的实现简单且直观。
PPT-11
1. Superclasses and Subclasses (父类与子类)
在面向对象编程中,类继承是构建类关系的一种方式。类与类之间可以形成父类(superclass)与子类(subclass)的关系。继承机制使得子类可以继承父类的属性和方法,从而避免重复代码。
- 父类(Superclass):是一个被其他类继承的类,通常包含一些通用的属性和方法。
- 子类(Subclass):继承父类的类。子类可以继承父类的所有非私有属性和方法,并可以根据需要重写或扩展这些属性和方法。
extends
关键字:用于表示继承关系,子类通过extends
关键字继承父类。
示例:
1 | class Animal { // 父类 |
关键点:
- 子类通过继承父类,可以访问父类的公有属性和方法。
- 子类可以通过方法重写(Override)父类的方法。
- 子类可以使用父类的构造方法,但父类的构造方法必须通过
super()
显式调用。
2. The Object Class and Its Methods (Object类及其方法)
Object
类是 Java
中的根类,所有的类(包括数组)都直接或间接继承自 Object
类。Object
类中定义了许多常用的方法,这些方法可以被所有类所使用。
常用的 Object
类方法:
toString()
:返回对象的字符串表示。
- 默认返回对象的类名和内存地址,可以被子类重写以提供更有意义的输出。
1
2
3
4
public String toString() {
return "Dog";
}equals(Object obj)
:比较两个对象的相等性。
- 默认通过内存地址比较两个对象,但通常会重写该方法来比较对象的内容。
hashCode()
:返回对象的哈希码。
- 通常与
equals()
方法一起重写,确保当两个对象相等时,它们的哈希码也相等。
- 通常与
getClass()
:返回对象的类类型。
- 可以用来获取对象的实际类。
示例:
1 | public class Animal { |
3. Polymorphism, Dynamic Binding and Generic Programming (多态、动态绑定和泛型编程)
3.1 Polymorphism (多态)
多态是面向对象编程的核心概念之一,它允许使用统一的接口来操作不同类型的对象。多态使得程序具有更强的灵活性和可扩展性。
- 编译时多态(方法重载):方法名相同,但参数列表不同。
- 运行时多态(方法重写):子类重写父类的方法。
3.2 Dynamic Binding (动态绑定)
动态绑定是指程序在运行时决定方法调用的具体类型(方法的实现)。例如,当子类重写父类的方法时,Java 会根据对象的实际类型来决定调用哪个方法。
- 动态绑定通常发生在多态的情况下,即方法重写。
- 方法重载是编译时绑定。
3.3 Generic Programming (泛型编程)
泛型是 Java 提供的一种支持类型参数化的机制。通过泛型,可以在编译时检查类型错误,而不是运行时。
- 泛型使得代码更加通用和可重用。
- 泛型使用尖括号
<>
来指定类型。
示例:
1 | // 泛型方法 |
4. The ArrayList Class (ArrayList类)
ArrayList
是 Java
中的一个可动态扩展的数组实现,它是 List
接口的实现类之一。与普通数组不同,ArrayList
可以自动调整大小。
主要特点:
- 动态扩展:当添加元素超过容量时,
ArrayList
会自动扩展。 - 顺序存储:它保持元素的插入顺序,并且允许重复的元素。
- 元素访问:通过索引访问元素,时间复杂度为 O(1)。
常用方法:
add(E e)
:添加元素。get(int index)
:获取指定索引的元素。size()
:返回元素的数量。remove(int index)
:删除指定索引的元素。
示例:
1 | import java.util.ArrayList; |
5. The protected / final Modifier (protected / final 修饰符)
5.1 protected 修饰符
protected
允许类中的成员被当前类、同一包中的其他类以及所有继承该类的子类访问。protected
主要用于继承机制中的属性和方法共享。
示例:
1 | class Animal { |
5.2 final 修饰符
final
用于变量、方法和类:
final
变量:值不可更改,一旦赋值就不能修改。final
方法:不能被子类重写。final
类:不能被继承。
示例:
1 | final class FinalClass { |
精华:
- Superclasses and Subclasses:通过继承机制,子类能够继承父类的属性和方法,并可以重写和扩展。
- The Object Class:
Object
类是所有 Java 类的根类,提供了如toString()
、equals()
等常用方法。 - Polymorphism and Dynamic Binding:通过多态性,可以让不同类型的对象调用相同的方法,且方法调用的实际实现通过动态绑定决定。
- The ArrayList Class:
ArrayList
是一个动态扩展的数组实现,适用于需要频繁增加和删除元素的场景。 - The protected/final
Modifier:
protected
提供了访问权限控制,而final
用于限制变量、方法和类的修改和继承。
PPT-12
1. Exception (异常)
异常(Exception)
是程序运行过程中发生的意外事件,它通常导致程序的正常执行流中断。Java
使用异常处理机制来处理这些错误和异常情况。异常可以是运行时错误或逻辑错误,并且可以通过异常处理机制(如
try-catch
)来捕获和处理。
主要特点:
- 异常是对象,可以使用
throw
语句抛出。 - 异常通常会导致程序终止,但可以通过适当的异常处理机制恢复程序的执行。
2. Exception Advantages (异常的优势)
异常处理机制提供了以下几个主要优势:
- 提高程序的可维护性:通过异常处理,程序能够明确处理不同的错误情况,而不会让错误影响到其他部分的逻辑。
- 减少错误:通过集中处理错误,程序能够避免出现未处理的错误。
- 增强代码的健壮性:通过捕获和处理异常,程序能够在出现问题时优雅地失败或恢复,而不是直接崩溃。
- 代码清晰:异常处理机制使得错误处理与正常逻辑分开,增强了代码的可读性。
3. Exception Types (异常类型)
在 Java 中,异常类型可以分为以下几类:
Checked Exception
(已检查异常):- 这些异常在编译时就能被发现,必须被显式捕获或声明。
- 常见的
Checked Exception
包括IOException
、SQLException
等。 - 这些异常通常是由外部因素引起的,如文件不存在、数据库连接失败等。
Unchecked Exception
(未检查异常):- 这些异常是程序错误引起的,通常在运行时发生。
- 常见的
Unchecked Exception
包括NullPointerException
、ArrayIndexOutOfBoundsException
、ArithmeticException
等。 - 未检查异常不需要显式地处理或声明。
Error
(错误):- 错误通常是由系统级问题引起的,例如
OutOfMemoryError
或StackOverflowError
,这些通常无法通过代码处理。
- 错误通常是由系统级问题引起的,例如
4. Declaring, Throwing, and Catching Exceptions (声明、抛出和捕获异常)
4.1 声明异常(Declaring Exceptions)
如果方法中可能会抛出
Checked Exception
,那么必须在方法签名中使用
throws
关键字声明该异常。
1 | public void readFile() throws IOException { |
4.2 抛出异常(Throwing Exceptions)
通过 throw
语句可以在程序中显式抛出异常。
1 | throw new ArithmeticException("Cannot divide by zero"); |
4.3 捕获异常(Catching Exceptions)
通过 try-catch
语句来捕获和处理异常。如果发生异常,程序会跳转到对应的
catch
块中。
1 | try { |
4.4 多重捕获(Multiple Catch Blocks)
可以有多个 catch
块来捕获不同类型的异常。
1 | try { |
4.5 finally
块
finally
块用于执行清理代码(如释放资源),无论是否发生异常,它始终都会被执行。
1 | try { |
5. Text I/O (文本输入输出)
Java 提供了多种方式来处理文本的输入输出。常用的类包括
FileReader
、BufferedReader
和
PrintWriter
。
5.1
FileReader
和
BufferedReader
FileReader
用于读取字符文件,但它的效率较低,因此通常配合
BufferedReader
使用来提高效率。
1 | FileReader fr = new FileReader("file.txt"); |
5.2 PrintWriter
PrintWriter
是一种便捷的类,可以用于输出文本数据,支持自动刷新和格式化输出。
1 | PrintWriter writer = new PrintWriter("output.txt"); |
6. File (文件处理)
在 Java 中,文件处理可以通过 File
类来实现,File
类提供了文件和目录的创建、删除、重命名等操作。
1 | File file = new File("example.txt"); |
常用方法:
createNewFile()
:创建新文件。delete()
:删除文件。exists()
:检查文件或目录是否存在。getAbsolutePath()
:返回文件的绝对路径。
7. PrintWriter (PrintWriter类)
PrintWriter
是 Java
中用于写入字符数据到文件或控制台的类。它具有更高效的输出能力,并且支持自动刷新功能。
1 | PrintWriter writer = new PrintWriter(new FileWriter("output.txt")); |
flush()
:刷新流,确保数据写入。close()
:关闭流,释放资源。
8. Scanner (Scanner类)
Scanner
类是用于从控制台或文件中读取输入的工具。它支持读取各种类型的数据,包括整数、浮点数、字符串等。
示例:
1 | Scanner sc = new Scanner(System.in); |
常用方法:
nextLine()
:读取一行文本。nextInt()
:读取一个整数。nextDouble()
:读取一个浮点数。close()
:关闭Scanner
对象。
精华:
- 异常是 Java 中重要的概念,通过
try-catch
机制来捕获和处理运行时错误。 - 异常的种类包括
Checked Exception
和Unchecked Exception
,每种类型的异常有不同的处理方式。 - Text I/O 涉及读取和写入文本文件,常用的类有
FileReader
、BufferedReader
、PrintWriter
等。 - 文件操作可以通过
File
类来进行文件的创建、删除、检查等。 PrintWriter
提供了便捷的文本输出功能,Scanner
用于读取输入。
PPT-13
深拷贝 (Deep Copy) 和浅拷贝 (Shallow Copy)
在对象复制的过程中,常常遇到浅拷贝和深拷贝的问题。它们之间的区别在于是否对对象内部的引用类型字段进行了复制。
浅拷贝 (Shallow Copy):只复制对象的引用,而不复制对象内部的引用类型字段。即复制后的对象与原对象共享相同的引用数据。
深拷贝 (Deep Copy):不仅复制对象本身,还会复制对象内部引用的数据,使得复制后的对象与原对象完全独立,所有字段都有自己的副本。
问题描述:在
Course1
类中实现 clone()
方法并对
students
字段进行深拷贝
Course1
类中有一个 students
字段,表示课程中的学生数组。需要实现 clone()
方法,确保在克隆 Course1
对象时,students
数组进行深拷贝。
Course1
类设计
假设 Course1
类的设计如下:
1 | public class Course1 implements Cloneable { |
代码详解
Course1
类字段:courseName
:课程名称,类型为String
。students
:一个长度为 100 的String
数组,表示学生的姓名。numberOfStudents
:记录学生的数量。
- 构造方法
(
Course1(String courseName)
):- 用于初始化课程名称和学生数量。
addStudent(String student)
方法:- 用于添加学生到
students
数组中,numberOfStudents
会自增。
- 用于添加学生到
getStudents()
方法:- 返回学生数组。
getNumberOfStudents()
方法:- 返回当前学生的数量。
getCourseName()
方法:- 返回课程名称。
dropStudent(String student)
方法:- 用于从学生数组中删除指定学生。删除后数组会左移,
numberOfStudents
减 1。
- 用于从学生数组中删除指定学生。删除后数组会左移,
clone()
方法:clone()
方法用于克隆对象。在 Java 中,Object
类提供了一个clone()
方法,但它是浅拷贝。为了实现深拷贝,我们在clone()
方法中手动拷贝students
数组。- 首先,调用
super.clone()
完成浅拷贝,复制Course1
对象的基本属性(如courseName
和numberOfStudents
)。 - 然后,使用
System.arraycopy()
或者循环手动拷贝students
数组,以确保新的Course1
对象拥有一个独立的students
数组副本。
深拷贝的实现方式
在 clone()
方法中实现深拷贝有两种常见方式:
- 使用
System.arraycopy()
:System.arraycopy(this.students, 0, c.students, 0, 100);
直接将原数组的内容拷贝到新数组。这样可以有效地复制students
数组,但两者依然是独立的数组副本。
- 使用循环手动拷贝:
- 使用
for
循环遍历students
数组,将每个学生的引用逐一复制到新数组中。
1
2
3for (int i = 0; i < numberOfStudents; i++) {
c.students[i] = this.students[i];
} - 使用
为什么要使用
clone()
方法?
clone()
方法提供了一种创建对象副本的简便方式。通过实现深拷贝,我们确保了两个
Course1
对象之间不会互相影响。特别是在涉及到
students
数组这样的引用类型字段时,深拷贝非常重要。
- 浅拷贝:如果不进行深拷贝,克隆后的
Course1
对象和原对象会共享students
数组中的数据。对其中一个对象的修改可能会影响到另一个对象的状态。 - 深拷贝:通过深拷贝,我们确保了原对象和克隆对象的
students
数组是完全独立的,互不干扰。
注意事项
Cloneable
接口:要使用clone()
方法,类必须实现Cloneable
接口,否则会抛出CloneNotSupportedException
异常。super.clone()
:super.clone()
会调用Object
类的clone()
方法,这会返回一个浅拷贝的副本,之后可以在此基础上进行深拷贝操作。
精华
在 Course1
类中,通过实现 clone()
方法并对
students
数组进行深拷贝,确保了克隆的 Course1
对象拥有独立的学生数组,从而避免了原对象和克隆对象之间的相互影响。这种深拷贝的方式是对对象内部引用字段进行复制的标准做法,适用于具有复杂属性的类。
PPT-14
JavaFX vs Swing and AWT
1. Swing: - Swing 是 Java 中的一个 GUI(图形用户界面)工具包,它基于 AWT(抽象窗口工具包)进行扩展。 - Swing 提供了丰富的控件,如按钮、标签、文本框等,使用 Java 编写而不是依赖于本地操作系统的窗口组件,因此界面更加一致。 - Swing 中的组件是“轻量级”的,意味着它们不依赖于操作系统的窗口,而是由 Java 绘制的。
2. AWT (Abstract Window Toolkit): - AWT 是 Java 的基础 GUI 库,用于构建图形界面。 - 与 Swing 不同,AWT 组件通常是“重量级”的,即它们依赖于操作系统提供的本地组件,因此它们的外观和行为可能因平台而异。 - AWT 是 Java GUI 的最初实现,但功能较为简单,不支持高级图形处理。
3. JavaFX: - JavaFX 是 Java 8 引入的新 GUI 库,用于开发丰富的桌面应用程序。 - 相比于 Swing 和 AWT,JavaFX 提供了更强大的功能,如硬件加速的图形处理、动画、3D 图形、样式表支持(CSS)、更现代的布局管理和易于使用的事件处理。 - JavaFX 基于 Scene Graph(场景图)模型,它比 Swing 和 AWT 更灵活、强大,支持多种控件和图形元素。
4. 比较总结: - Swing vs JavaFX:Swing 的设计比较旧,适合传统的桌面应用,而 JavaFX 适合构建现代的、具有多媒体功能的桌面应用。 - AWT vs JavaFX:AWT 提供基础功能,JavaFX 提供更现代和丰富的功能,支持硬件加速和复杂的界面设计。
SceneBuilder
SceneBuilder 是一种 JavaFX 可视化布局工具,用于创建 JavaFX 界面的 FXML 文件。FXML 是一种类似于 HTML 的标记语言,用于描述 JavaFX 应用的用户界面。SceneBuilder 提供了一个图形化的界面,用户可以通过拖放组件来设计 JavaFX 界面,然后生成相应的 FXML 文件。
特点: - 图形化界面:通过拖放界面控件,快速构建界面。 - 生成 FXML 文件:自动生成 FXML 文件,用户可以进一步在 Java 代码中引用。 - 与代码分离:JavaFX 与 FXML 分离,代码更加清晰,易于维护。
优势: - 提高开发效率:无需手动编写 UI 布局代码,便于快速构建应用。 - 支持预览:可以实时预览界面设计效果。 - 可与 Java 代码无缝集成:FXML 与 Java 代码结合,支持动态控制界面元素。
Basic Structure of JavaFX
JavaFX 的基本结构包括:
- Application 类:
- 所有 JavaFX 程序都必须继承
javafx.application.Application
类,并重写start()
方法。 start()
方法接受一个Stage
参数,表示应用的主窗口。
- 所有 JavaFX 程序都必须继承
- Stage(舞台):
Stage
是应用的顶层容器,它表示一个窗口。- 每个 JavaFX 程序至少有一个
Stage
对象,应用程序的主界面是Stage
上的Scene
。
- Scene(场景):
Scene
表示一个容器,包含所有界面元素(控件、布局等)。- 场景是添加到舞台上的,舞台可以持有多个场景。
- Node(节点):
Node
是 JavaFX 中所有可视化元素的基类,包括图形、控件、容器等。每个节点都可以是Stage
或Scene
的一部分。- 常见的节点有
Button
、TextField
、ImageView
、Shape
等。
Stage/Scene/Pane/Node
- Stage:
- JavaFX 的
Stage
类是舞台,它表示一个窗口。 - 每个 JavaFX 应用程序必须至少有一个
Stage
,它是界面的容器。
- JavaFX 的
- Scene:
Scene
是舞台中的内容,包含所有的 UI 元素,和事件处理。Scene
可以包含多个控件和布局元素。
- Pane:
Pane
是 JavaFX 中的容器类,主要用于布局和组织其他节点。- 常见的
Pane
类型包括StackPane
、GridPane
、FlowPane
等,它们有不同的布局策略。
- Node:
Node
是所有 JavaFX 可视元素的基类,可以是 UI 控件(如按钮、标签)或图形元素(如矩形、圆形)。
Binding Property (属性绑定)
属性绑定是 JavaFX 中的强大功能,它允许控件或对象的属性相互关联,并自动更新。当一个属性的值发生变化时,绑定到该属性的其他属性也会自动更新。
常见的属性绑定方式: - 双向绑定:两个属性互相绑定,互相更新。 - 单向绑定:一个属性绑定到另一个属性,源属性变化时目标属性自动更新。
示例:
1 | TextField textField = new TextField(); |
在此示例中,label
的文本会始终与 textField
的文本内容同步。
Common Properties and Methods for Nodes
常见的 JavaFX 节点属性和方法包括:
- 属性:
x
和y
:定义节点的位置。width
和height
:定义节点的尺寸。opacity
:设置节点的透明度。style
:设置节点的 CSS 样式。visible
:设置节点是否可见。rotate
:设置节点的旋转角度。
- 方法:
setLayoutX()
和setLayoutY()
:设置节点的位置。setWidth()
和setHeight()
:设置节点的宽度和高度。setStyle()
:设置节点的 CSS 样式。
Style / Rotate
Style:
- JavaFX 使用 CSS 来控制界面元素的样式。通过
setStyle()
方法可以动态改变节点的样式。 - 可以设置属性,如
background-color
、font-size
、color
等。
示例:
1
button.setStyle("-fx-background-color: blue;");
- JavaFX 使用 CSS 来控制界面元素的样式。通过
Rotate:
rotate
属性用于旋转节点,单位是度(°)。- 旋转是相对于节点的原点进行的,可以通过
setRotate()
方法设置旋转角度。
示例:
1
rectangle.setRotate(45); // 旋转 45 度
Color / Font / Layout Panes / Image / Shape
Color:
- JavaFX 提供了
Color
类用于设置颜色。可以使用预定义的颜色常量,或通过 RGB 值自定义颜色。
示例:
1
2Color color = Color.RED; // 使用预定义颜色
Color customColor = Color.rgb(255, 0, 0); // 使用 RGB 值- JavaFX 提供了
Font:
Font
类用于设置文本的字体、大小等属性。
示例:
1
Font font = Font.font("Arial", 20); // 设置 Arial 字体,大小为 20
Layout Panes:
- JavaFX 提供了多种布局容器类(
Pane
),如VBox
、HBox
、GridPane
、FlowPane
,用于自动排列子节点。
示例:
1
2VBox vbox = new VBox();
vbox.getChildren().add(button); // 将按钮添加到垂直布局- JavaFX 提供了多种布局容器类(
Image:
Image
类用于处理图像。可以加载本地或远程图片并显示。
示例:
1
2Image image = new Image("file:picture.jpg");
ImageView imageView = new ImageView(image);Shape:
- JavaFX 提供了多种形状类,如
Rectangle
、Circle
、Ellipse
、Line
,可以用来绘制图形。
示例:
1
Rectangle rect = new Rectangle(100, 100, 200, 150); // 创建一个矩形
- JavaFX 提供了多种形状类,如
PPT-15
Java Event Delegation Model
Java 中的 事件委托模型(Event Delegation Model)是事件处理的核心。它是处理 GUI 事件(如按钮点击、鼠标移动等)的机制,并基于分离关注的设计理念。事件的发生与事件的响应(处理)是分离的,事件源和事件处理器(监听器)是解耦的。
基本概念:
- 事件源(Event Source):事件源是触发事件的对象。在 Java 中,通常是 GUI 组件(如按钮、文本框等)。
- 事件(Event):事件表示用户与应用程序的交互行为(如点击、按键、鼠标移动等)。Java
中的事件通常继承自
java.util.EventObject
类。 - 事件监听器(Event Listener):事件监听器是对特定事件做出响应的对象。它会在事件发生时被触发,并执行相应的处理逻辑。
事件处理的过程:
- 用户在界面上与某个组件交互(如点击按钮)。
- 该组件会生成一个事件(例如
ActionEvent
)。 - 事件通过事件源传播到注册的监听器(事件处理器)。
- 监听器接收事件并根据事件的类型执行对应的处理方法。
事件委托模型的优势:
- 松散耦合:事件源和监听器是解耦的,事件源不需要知道监听器的具体实现,监听器通过接口与事件源进行交互。
- 灵活性:多个监听器可以监听同一个事件源,允许事件的灵活处理。
Event / Event Source / Event Handler (Listener)
- Event(事件):
- 事件是用户与 GUI 组件交互时产生的一个对象,表示某种动作或状态变化。
- Java 提供了多种不同的事件类,如
ActionEvent
(按钮点击事件),KeyEvent
(键盘事件),MouseEvent
(鼠标事件)等。
- Event Source(事件源):
- 事件源是产生事件的对象。在 Java 中,常见的事件源是 GUI 组件,如
Button
、TextField
、Label
等。 - 事件源通过
addXXXListener()
方法将事件监听器注册到自身。当事件发生时,事件源会通知所有已注册的监听器。
- 事件源是产生事件的对象。在 Java 中,常见的事件源是 GUI 组件,如
- Event Handler(事件处理器,Listener):
- 事件监听器是实现特定事件处理逻辑的对象。监听器实现特定的事件监听接口,并定义响应事件的方法。
- 每个事件类型都有对应的监听接口,例如:
ActionListener
(用于按钮点击),MouseListener
(用于鼠标事件),KeyListener
(用于键盘事件)等。
Inner Class(内部类)
内部类是定义在另一个类中的类。Java 中有四种内部类类型:
- 成员内部类:定义在外部类的成员位置,且可以访问外部类的成员。
- 静态内部类:定义为
static
的内部类,它不依赖于外部类的实例,因此不能访问外部类的实例成员。 - 局部内部类:定义在方法中,作用范围限定在方法内。
- 匿名内部类:没有名字的类,它是对某个类的快速实现,通常用于简化代码,特别是在事件处理时。
内部类的应用:
- 内部类可以访问外部类的成员和方法,因此它可以直接访问外部类的非静态变量和方法。
- 内部类能有效实现封装和逻辑分离,特别适合事件处理和回调等场景。
示例:
1 | class OuterClass { |
Anonymous Inner Class (匿名内部类) / Lambda Expression
1. Anonymous Inner Class (匿名内部类):
- 匿名内部类是没有名字的内部类,它通常用来简化事件监听器的实现。匿名内部类可以在创建的同时初始化并实现接口或继承类。
- 常用在事件监听中,如按钮的点击事件监听。
示例:
1 | button.addActionListener(new ActionListener() { |
匿名内部类无需单独定义一个实现类,而是直接在创建监听器时实现事件处理方法。
2. Lambda Expression (Lambda 表达式):
- 从 Java 8 开始,Java 引入了 Lambda 表达式,它是一种简洁的方式来表示函数式接口的实现,特别是对于事件监听器的处理更加简洁和清晰。
- Lambda 表达式为事件监听提供了简化的语法,避免了冗长的匿名内部类定义。
示例:
1 | button.addActionListener(e -> System.out.println("Button clicked!")); |
Lambda 表达式简化了事件处理代码,使得事件处理变得更加直观和易于维护。
Listeners for Observable Objects
在 Java 中,某些对象支持监听其状态的变化,这些对象称为
可观察对象(Observable Objects)。Java 提供了
Observable
类和 Observer
接口来实现对象状态变化的监听。
- Observable 类:
Observable
是 Java 中一个抽象类,表示可被观察的对象。它维护了一些观察者(Observer
)的列表,当其状态变化时,通知所有注册的观察者。 - Observer 接口:
Observer
接口表示观察者,它定义了一个update()
方法,用于接收被观察对象的状态变化通知。
示例:
1 | import java.util.*; |
精华
- Java 事件委托模型:通过事件源、事件和事件监听器解耦了事件的发生和处理,使得代码更加灵活和易于维护。
- 事件源与监听器:事件源产生事件,监听器接收并处理事件。通过
addXXXListener()
方法将监听器注册到事件源。 - 内部类:内部类能够访问外部类的成员,常用于事件处理。匿名内部类简化了事件处理器的定义。
- Lambda 表达式:从 Java 8 开始,Lambda 表达式简化了函数式接口(如事件监听器)的实现。
- 可观察对象:
Observable
类和Observer
接口为对象的状态变化提供了通知机制,允许多个观察者监听对象的状态变化。
PPT-16
无
PPT-17
Text I/O 与 Binary I/O
在 Java 中,输入和输出操作分为两种主要类型:文本输入/输出(Text I/O) 和 二进制输入/输出(Binary I/O)。两者主要区别在于数据的处理方式,文本 I/O 处理的是字符数据,而二进制 I/O 处理的是原始字节数据。
1. Text I/O 与 Binary I/O 的区别
- Text I/O(文本输入/输出):
- 处理的是字符数据。文本文件通常是由字符(例如 ASCII 或 UTF-8 编码的字符)组成的。
- Java 提供了字符流类,例如
FileReader
、FileWriter
、BufferedReader
和BufferedWriter
等,主要用于字符的读取与写入。 - 文本流会自动进行字符编码与解码(例如从字节转换为字符)。
- Binary I/O(二进制输入/输出):
- 处理的是字节数据。二进制文件可以包含任何类型的数据(如图片、音频、视频、程序文件等)。
- Java 提供了字节流类,如
InputStream
、OutputStream
、FileInputStream
、FileOutputStream
、DataInputStream
、DataOutputStream
等,用于处理字节数据。 - 二进制流不会进行编码或解码操作,直接读取或写入字节数据。
2. InputStream 和 OutputStream
- InputStream:
InputStream
是所有字节输入流的父类,用于表示字节输入流的基类。它定义了从输入流中读取字节的基本方法。- 常用方法:
read()
:读取一个字节并返回(返回 -1 表示流结束)。read(byte[] b)
:读取一定数量的字节并存储到数组中。close()
:关闭流。
- OutputStream:
OutputStream
是所有字节输出流的父类,用于表示字节输出流的基类。它定义了向输出流写入字节的基本方法。- 常用方法:
write(int b)
:写入一个字节。write(byte[] b)
:写入字节数组。flush()
:刷新输出流,确保所有数据都被写入。close()
:关闭流。
3. FileInputStream 和 FileOutputStream
FileInputStream:
- 用于从文件中读取字节数据,继承自
InputStream
类。 - 读取文件的字节流,并将内容传递给程序。
示例:
1
2
3
4
5
6FileInputStream fis = new FileInputStream("file.txt");
int byteData;
while ((byteData = fis.read()) != -1) {
System.out.print((char) byteData); // 将字节转换为字符
}
fis.close();- 用于从文件中读取字节数据,继承自
FileOutputStream:
- 用于向文件中写入字节数据,继承自
OutputStream
类。 - 向指定的文件中写入字节数据。
示例:
1
2
3
4FileOutputStream fos = new FileOutputStream("file.txt");
String content = "Hello, Java!";
fos.write(content.getBytes()); // 将字符串转换为字节并写入文件
fos.close();- 用于向文件中写入字节数据,继承自
4. DataInputStream 和 DataOutputStream
DataInputStream:
DataInputStream
是InputStream
的子类,用于以机器无关的方式读取原始数据类型(如int
、float
、double
、long
、boolean
)以及 UTF-8 编码的字符串。- 它提供了许多方法,可以直接读取 Java 中的基本数据类型。
常用方法:
readInt()
:读取一个int
。readFloat()
:读取一个float
。readUTF()
:读取一个 UTF-8 编码的字符串。
示例:
1
2
3
4DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"));
int number = dis.readInt();
String name = dis.readUTF();
dis.close();DataOutputStream:
DataOutputStream
是OutputStream
的子类,用于以机器无关的方式写入原始数据类型。- 它提供了写入 Java 中基本数据类型的方法。
常用方法:
writeInt(int v)
:写入一个int
。writeFloat(float v)
:写入一个float
。writeUTF(String str)
:写入一个 UTF-8 编码的字符串。
示例:
1
2
3
4DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"));
dos.writeInt(100);
dos.writeUTF("Hello");
dos.close();
5. BufferedInputStream 和 BufferedOutputStream
BufferedInputStream:
BufferedInputStream
是InputStream
的子类,使用一个缓冲区来提高读取数据的效率。它通过减少磁盘操作来提升性能。- 一般在需要频繁读取数据时使用
BufferedInputStream
。
示例:
1
2
3
4
5
6BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"));
int byteData;
while ((byteData = bis.read()) != -1) {
System.out.print((char) byteData); // 将字节转换为字符
}
bis.close();BufferedOutputStream:
BufferedOutputStream
是OutputStream
的子类,使用缓冲区来提高写入数据的效率。- 它将数据先写入缓冲区,再一次性写入磁盘,从而减少磁盘操作,提高性能。
示例:
1
2
3
4BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("file.txt"));
String content = "Buffered Output!";
bos.write(content.getBytes());
bos.close();
6. RandomAccessFile
- RandomAccessFile:
RandomAccessFile
类允许随机访问文件内容,可以在文件的任意位置读写数据。它既可以用于读取数据,也可以用于写入数据。RandomAccessFile
与其他流类的不同之处在于,它支持读写文件中的任意位置,而不必按顺序读取或写入。
seek(long pos)
:设置文件指针的位置,可以随机访问文件中的任意位置。read()
:读取一个字节。write(byte[] b)
:写入字节数组。
1
2
3
4RandomAccessFile raf = new RandomAccessFile("file.txt", "rw");
raf.seek(5); // 设置文件指针到第5个字节位置
raf.write("Hello".getBytes()); // 写入数据
raf.close();
精华:
- Text I/O 和 Binary I/O 主要的区别在于数据的处理方式:Text I/O 处理字符数据,Binary I/O 处理字节数据。
- 字节流(
InputStream
和OutputStream
)适用于读取和写入所有类型的数据,包括文本和二进制数据。 - 字符流(如
FileReader
和FileWriter
)专门用于处理文本文件。 - DataInputStream 和 DataOutputStream 提供了读取和写入 Java 原始数据类型的方法。
- BufferedInputStream 和 BufferedOutputStream 提供了带缓冲的字节流,提升了 I/O 操作的效率。
- RandomAccessFile 允许随机访问文件中的任意位置,是一种灵活的文件操作方式。
PPT-18
无
PPT-19
无
PPT-20
无
PPT-21
无