I have the following test program that is writing out a Person record's field values using java.io.DataOutputStream and it writes them out just fine. Then, after a pause that I put in to check the data file, it is supposed to read in the same three records using java.io.DataInputStream, but it reads in the first record, the Person.id of the second record and throws the EOFException, but is not at the end of file. Here's the code:
public class BinaryFileAccessTest {
private static File dataFile = new File(System.getProperty("user.home") + File.separator + "dataFile.dat");
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Dough", "John", new Date(70, 03, 17), 23500.00d));
people.add(new Person("Smith", "Sean", new Date(70, 05, 06), 53900.00d));
people.add(new Person("Carrick", "Rebecca", new Date(59, 06, 13), 20000.00d));
System.out.println("From List<Person> people:");
for (Person p : people) {
System.out.println("\tCreated: " + p.toString());
}
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(dataFile))) {
System.out.println("Writing Person to file:");
for (Person p : people) {
out.writeLong(p.getId());
out.writeUTF(p.getLastName());
out.writeUTF(p.getFirstName());
out.write(p.getBirthDate().toString().getBytes());
out.writeDouble(p.getSalary());
System.out.println("\tWrote: " + p.toString());
}
} catch (IOException ex) {
System.err.println("An error occurred writing to " + dataFile);
System.err.println(ex);
ex.printStackTrace(System.err);
}
Scanner keyboard = new Scanner(System.in);
boolean exit = false;
while (!exit) {
String input = keyboard.nextLine();
if (input != null) {
exit = true;
}
}
try (DataInputStream in = new DataInputStream(new FileInputStream(dataFile))) {
people.clear();
System.out.println("Reading Person from file:");
while (true) {
Person p = new Person();
p.setId(in.readLong());
p.setLastName(in.readUTF());
p.setFirstName(in.readUTF());
p.setBirthDate(new Date(in.read()));
p.setSalary(in.readDouble());
System.out.println("\tRead: " + p.toString());
people.add(p);
}
} catch (IOException ex) {
// if (!(ex instanceof EOFException)) {
System.err.println("An error occurred writing to " + dataFile);
System.err.println(ex);
ex.printStackTrace(System.err);
// } else {
// System.out.println("End of file reached.");
// }
}
System.out.println("From List<Person> people:");
for (Person p : people) {
System.out.println("\tContains: " + p.toString());
}
}
}
The output from this program is:
From List<Person> people:
Created: Dough, John [birth=Fri Apr 17 00:00:00 CST 1970; salary=23500.0
Created: Smith, Sean [birth=Sat Jun 06 00:00:00 CDT 1970; salary=53900.0
Created: Carrick, Rebecca [birth=Mon Jul 13 00:00:00 CDT 1959; salary=20000.0
Writing Person to file:
Wrote: Dough, John [birth=Fri Apr 17 00:00:00 CST 1970; salary=23500.0
Wrote: Smith, Sean [birth=Sat Jun 06 00:00:00 CDT 1970; salary=53900.0
Wrote: Carrick, Rebecca [birth=Mon Jul 13 00:00:00 CDT 1959; salary=20000.0
Reading Person from file:
Read: Dough, John [birth=Wed Dec 31 18:00:00 CST 1969; salary=1.3403241663660614E243
In the second try...catch block, the first record (for John Dough) is read in and stored in the people list just fine. On the second iteration of the while loop, it reads in the ID value for Sean Smith, then when reading in the last name, it throws the EOFException, even though it is not at the end of the file. When I look at the contents of the file during the program execution pause, it has three blocks of data, which are clearly visible:
𐀀ꑢ?ꣁ???甔? 珐????? ꗌ?쵐?
Of course, it looks different in the editor in which I look at it, but there are three clear blocks of data present. Yet, after reading the one field from the second block of data, it somehow hits the EOF.
Can anyone explain why this would be? It seems that if these classes are working correctly, when data is written out as I did, then read in by the inverse class, it would read in exactly as it was written out, so it should read in the same number of datapoints as were written.
I guess the better question is, where did I screw this up? ;)
Thank you for any help you can provide!
-SC
[EDIT]
I also just noticed that John Dough's salary is not being read back in as the same value as written, neither is his birthDate. Why would this be?
Thx!
[EDIT-2]
Here's the Person Java Bean:
public class Person {
private long id;
private String lastName;
private String firstName;
private Date birthDate;
private double salary;
public Person() {
}
public Person (String lastName, String firstName, Date birthDate, double salary) {
this.id = System.currentTimeMillis();
this.lastName = lastName;
this.firstName = firstName;
this.birthDate = birthDate;
this.salary = salary;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return getLastName() + ", " + getFirstName() + " [birth=" + getBirthDate() + "; salary=" + getSalary();
}
}
Personclass to the question to make it a minimal reproducible example and we can be sure that the problem is not within there.Date. That class is poorly designed and long outdated. And even if you insist on usingDate, stay far away from its 3-arg constructor. It’s been deprecated for 25 years because it works unreliably across time zones. For a birthdate useLocalDatefrom java.time, the modern Java date and time API.ObjectOutputStream? It would make things a lot easierPersonclass is just a standard Java Bean, but I'll edit and include it as well.Data*Streams a try...The frustrations are seeming to not be worth the effort.