Java编程中的对象类与文件输入输出
1. Object类的探索
在Java编程里,理解面向对象编程的实现方式至关重要,而Object类在其中扮演着关键角色。下面我们通过一个简单的程序来开启对Object类的学习。
package theobjectclass;
public class TheObjectClass {
public static void main(String[] args) {
MyClass object1 = new MyClass("abcdefg");
MyClass object2 = new MyClass("abcdefg");
object1.MyMethod();
object2.MyMethod();
System.out.println("The objects are the same: " +
(object1 == object2));
System.out.println("The objects are the same: " +
object1.equals(object2));
}
}
这个程序创建了 MyClass 类的两个实例 object1 和 object2 ,接着调用了每个对象的 MyMethod 方法,该方法会打印出对象所包含的值。之后,程序对这两个对象进行了比较。
- 首先使用比较运算符
==,它用于检查两个对象是否为同一个对象。显然,这里的object1和object2是两个独立的实例,所以object1 == object2的结果为false。 - 然后使用
equals()方法进行比较。运行程序后会发现,即使object1和object2在创建时使用了相同的参数,并且从创建到当前时刻经历了相同的操作,equals()方法仍然判定它们不相等。
那么, equals() 方法从何而来呢?查看 MyClass 类的定义,并没有发现 equals() 方法。实际上,所有的Java类都继承自一个超类,即位于类继承树顶端的Object类,尽管在代码中没有显式声明。
在Java文档( docs.oracle.com/javase/7/docs/api/java/lang/Object.html )中可以找到Object类的定义:“Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.” 这表明所有Java对象都继承了Object类的方法,其中就包括我们正在探讨的 equals() 方法。
Object类中 equals() 方法的定义比较模糊,它只是表示“Indicates whether some other object is equal to this one.” 这就需要我们程序员根据具体的类来确定“相等”的具体含义。
2. 重写 equals() 方法
重写Object类的方法并不比重写其他超类的方法困难。我们只需声明一个相同的方法,在处理 MyClass 对象时,这个特定的方法就会被调用。需要注意的是, equals() 方法的输入参数是任意对象,而不是 MyClass 对象。因此,在比较输入对象的值与当前 MyClass 对象的值之前,需要确保输入的对象确实是 MyClass 对象。
以下是一些需要直接返回 false 的情况:
1. 如果输入的对象未被实例化,即 null 指针,由于已实例化的 MyClass 对象与 null 不相等,所以直接返回 false 。
2. 检查输入的对象是否为 MyClass 的实例。使用 instanceof 关键字,如果输入对象不是 MyClass 的实例,那么比较就没有意义,直接返回 false 。
以下是重写 equals() 方法后的 MyClass 类代码:
package theobjectclass;
public class MyClass {
public String value;
public MyClass(String value)
{
this.value = value;
System.out.println
("A MyClass object was created with value:" + value);
}
public void MyMethod()
{
System.out.println
("MyMethod was called on a MyClass object with value: " +
value);
}
@Override
public boolean equals(Object obj)
{
if(obj == null)
return false;
if(!(obj instanceof MyClass))
return false;
return value.equals(((MyClass)obj).value);
}
}
运行这个程序,会发现 object1 和 object2 被判定为相等。
3. Object类的其他方法
Object类还声明了许多其他方法,除了 equals() ,还有 hashCode() 和 toString() 等重要方法。
-
hashCode():该方法应返回一个描述特定对象的整数值。如果两个对象通过equals()方法判定为相等,那么它们的hashCode()方法应返回相同的整数值;如果不相等,则应返回不同的值。由于实现hashCode()方法需要一些复杂的数学运算,这里暂不实现,但建议查看文档了解其工作原理。 -
toString():这也是Object类的一个方法,任何对象都可以调用该方法。但在自定义对象中,在重写toString()方法之前,它可能不会返回有意义的、人类可读的信息。
在学习Java时,建议在编写的小测试类中实现 equals() 和 toString() 方法,这有助于养成良好的编程习惯,并且能让我们像Java一样思考面向对象编程。
4. 基本类
在Java中,字符串比较特殊,虽然可以用双引号直接表示,但主要还是通过 String 类来操作,因为Java并没有提供字符串基本类型。
对于标准的Java基本类型,通常通过基本类型的操作方法来与之交互。每个基本类型都有对应的包装类,如 Integer 、 Character 和 Float 等。一般来说,创建这些包装类的实例并调用其方法的实际用途不大,除非是为了创建自己的类而进行重写。
以下是一个简单的示例:
package the.primitiveclasses;
public class ThePrimitiveClasses {
public static void main(String[] args) {
String s = "string";
Character c = 'c';
}
}
Character 类的实例提供的方法主要是转换方法,例如 compareTo() 方法,当比较的字符相等时返回 0 ,否则根据字符在整数转换表中的位置返回小于或大于 0 的值。
此外,还可以使用基本类的静态方法来操作基本类型的实例。例如,要判断一个字符是否为小写字母,可以使用 Character 类的静态方法 isLowerCase() :
package the.primitiveclasses;
public class ThePrimitiveClasses {
public static void main(String[] args) {
String s = "string";
Character c = 'c';
System.out.println(Character.isLowerCase(c));
}
}
当不需要基本类的功能时,建议使用基本类型,如使用 char 而不是 Character 。基本类型的语法高亮和跨语言的通用性使其更便于程序员使用。
5. 文件输入输出概述
文件输入输出(I/O)是现代编程中非常强大的工具,它能让代码中逻辑上分离的实体之间的信息传输变得更加容易。在接下来的内容中,将学习如何使用 FileWriter 、 BufferedWriter 、 FileReader 和 BufferedReader 类来读写数据文件,还会了解 close() 方法和 Scanner 类的使用,以及异常处理和 Serializable 类。
具体将涵盖以下主题:
- 写入数据到文件
- 从文件读取数据
- Serializable 类
6. 写入数据到文件
首先,我们来看看如何使用Java写入文件。下面的程序会生成一个斐波那契数列的前50个数字,并将它们打印到控制台:
package writingtofiles;
public class WritingToFiles {
public static void main(String[] args) {
for(long number : FibonacciNumbers())
{
System.out.println(number);
}
}
private static long[] FibonacciNumbers()
{
long[] fibNumbers = new long[50];
fibNumbers[0] = 0;
fibNumbers[1] = 1;
for(int i = 2; i < 50; i++)
{
fibNumbers[i] = fibNumbers[i - 1] + fibNumbers[i - 2];
}
return fibNumbers;
}
}
然而,当关闭控制台后,这些数字就会丢失。为了解决这个问题,我们将使用 java.io 库中的 FileWriter 类。
以下是使用 FileWriter 类的步骤:
1. 声明一个 FileWriter 对象,并初始化为 null :
public class WritingToFiles {
public static void main(String[] args) {
FileWriter out = null;
- 实例化
FileWriter对象。要写入文件,需要知道两件重要的事情:一是要写入的内容,二是要写入的文件名。
public class WritingToFiles {
public static void main(String[] args) {
FileWriter out = null;
out = new FileWriter("out.txt");
此时,NetBeans会提示编译器错误,因为 FileWriter 构造函数可能会抛出 IOException 异常,而代码中没有显式处理该异常。
- 使用
try...catch块来捕获IOException异常:
try {
out = new FileWriter("out.txt");
for(long number : FibonacciNumbers())
{
// System.out.println(number);
out.write(String.valueOf(number));
}
} catch(IOException e)
{
System.err.println("File IO Failed.");
}
- 由于
FileWriter的write方法不能直接接受long类型的参数,所以使用String.valueOf()方法将long类型的数字转换为字符串:
for(long number : FibonacciNumbers())
{
out.write(String.valueOf(number));
// System.out.println(number);
}
现在,程序已经具备了将斐波那契数列的前50个数字写入文件的功能。但问题是, out.txt 文件会创建在哪里呢?由于没有提供完整的系统路径,只给出了文件名, FileWriter 类会在运行程序的工作目录下创建文件。要确定工作目录,可以在NetBeans中打开控制台窗口并构建程序,NetBeans会显示文件的创建位置。通常,JAR文件会被编译到 dist 文件夹中,这就是程序的工作目录, out.txt 文件也会创建在这个目录下。
综上所述,通过以上步骤,我们可以将数据成功写入文件,并且了解了如何处理可能出现的异常。在后续的学习中,还将继续探索从文件读取数据以及 Serializable 类的相关内容。
7. 从文件读取数据
在学会了如何将数据写入文件后,接下来我们要学习如何从文件中读取数据。为了实现这个功能,我们将使用 FileReader 和 BufferedReader 类。
以下是一个简单的示例程序,展示了如何从文件中读取数据:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadingFromFiles {
public static void main(String[] args) {
BufferedReader in = null;
try {
// 创建一个FileReader对象,关联要读取的文件
FileReader fileReader = new FileReader("out.txt");
// 使用BufferedReader包装FileReader,提高读取效率
in = new BufferedReader(fileReader);
String line;
// 逐行读取文件内容
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("File reading failed.");
} finally {
try {
if (in != null) {
// 关闭BufferedReader
in.close();
}
} catch (IOException e) {
System.err.println("Error closing the reader.");
}
}
}
}
上述代码的执行步骤如下:
1. 声明 BufferedReader 对象 :并初始化为 null ,用于后续读取文件内容。
2. 创建 FileReader 对象 :将其与要读取的文件(这里是 out.txt )关联起来。
3. 使用 BufferedReader 包装 FileReader :这样可以提高读取效率,因为 BufferedReader 会在内存中创建一个缓冲区。
4. 逐行读取文件内容 :使用 readLine() 方法,该方法会返回文件中的一行内容,如果到达文件末尾则返回 null 。
5. 异常处理 :使用 try...catch 块捕获可能出现的 IOException 异常,并在发生异常时输出错误信息。
6. 关闭 BufferedReader :在 finally 块中关闭 BufferedReader ,确保无论是否发生异常,资源都能被正确释放。
下面是这个过程的流程图:
graph TD
A[开始] --> B[声明BufferedReader并初始化为null]
B --> C[创建FileReader关联文件]
C --> D[使用BufferedReader包装FileReader]
D --> E{是否读取到文件末尾}
E -- 否 --> F[读取一行内容并输出]
F --> E
E -- 是 --> G[结束读取]
G --> H[关闭BufferedReader]
H --> I[结束]
C --> J{是否发生IOException}
J -- 是 --> K[输出错误信息]
K --> H
J -- 否 --> E
8. Serializable类
Serializable 类是Java中用于实现对象序列化和反序列化的接口。对象序列化是指将对象的状态转换为字节流,以便可以将其保存到文件、数据库或通过网络传输;反序列化则是将字节流恢复为对象的过程。
以下是一个简单的示例,展示了如何实现对象的序列化和反序列化:
import java.io.*;
// 实现Serializable接口
class MySerializableClass implements Serializable {
private String data;
public MySerializableClass(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
public class SerializationExample {
public static void main(String[] args) {
// 创建一个可序列化的对象
MySerializableClass obj = new MySerializableClass("Hello, Serializable!");
// 序列化对象
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serialized.obj"))) {
out.writeObject(obj);
System.out.println("Object serialized successfully.");
} catch (IOException e) {
System.err.println("Serialization failed: " + e.getMessage());
}
// 反序列化对象
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("serialized.obj"))) {
MySerializableClass deserializedObj = (MySerializableClass) in.readObject();
System.out.println("Object deserialized successfully: " + deserializedObj.getData());
} catch (IOException | ClassNotFoundException e) {
System.err.println("Deserialization failed: " + e.getMessage());
}
}
}
上述代码的执行步骤如下:
1. 定义可序列化的类 : MySerializableClass 实现了 Serializable 接口,这样该类的对象就可以被序列化。
2. 序列化对象 :使用 ObjectOutputStream 将对象写入文件( serialized.obj )。
3. 反序列化对象 :使用 ObjectInputStream 从文件中读取对象,并将其转换为 MySerializableClass 类型。
4. 异常处理 :在序列化和反序列化过程中,使用 try...catch 块捕获可能出现的 IOException 和 ClassNotFoundException 异常,并输出错误信息。
下面是对象序列化和反序列化的流程图:
graph TD
A[创建可序列化对象] --> B[序列化对象]
B --> C[将对象写入文件]
C --> D[反序列化对象]
D --> E[从文件读取对象]
E --> F[恢复为对象并使用]
B --> G{是否发生IOException}
G -- 是 --> H[输出序列化错误信息]
G -- 否 --> C
D --> I{是否发生IOException或ClassNotFoundException}
I -- 是 --> J[输出反序列化错误信息]
I -- 否 --> E
总结
通过前面的学习,我们对Java编程中的对象类和文件输入输出有了较为全面的了解。以下是一个总结表格:
| 主题 | 主要内容 |
| ---- | ---- |
| Object类 | 所有Java类的超类,包含 equals() 、 hashCode() 和 toString() 等重要方法,可通过重写这些方法实现自定义功能。 |
| 基本类 | 每个基本类型都有对应的包装类,可使用包装类的静态方法操作基本类型实例,不需要包装类功能时建议使用基本类型。 |
| 文件输入输出 | 可使用 FileWriter 和 BufferedWriter 将数据写入文件,使用 FileReader 和 BufferedReader 从文件读取数据,同时要注意异常处理。 |
| Serializable类 | 用于实现对象的序列化和反序列化,可将对象状态转换为字节流进行保存或传输。 |
在实际的Java编程中,这些知识非常实用。例如,在处理文件数据时,我们可以灵活地进行读写操作;在处理对象时,可通过序列化和反序列化实现数据的持久化和传输。希望大家能够熟练掌握这些知识,并在实际项目中灵活运用。
超级会员免费看

被折叠的 条评论
为什么被折叠?



