Как правильно из строки, которая записана в файл.txt, создать объект и закинуть его в список?
Начал изучать java и программирование в целом совсем недавно. Так, что не знаю элементарных вещей.
Вот строка в в файле worker.txt
1 Ivan Sidorov 12.03.1993 12.03.2019
Вот класс работника:
Представим, что в классе есть геттеры-сеттеры и конструкторы.
public class Worker {
private Long id;
private String firstName;
private String lastName;
private Date dateOfBirth;
private Date dateOfEmployment; //время устройства на работу
}
Вот другой класс с реализацией:
public class App {
public static void main(String[] args) {
starter();
}
public static void starter() {
File file = new File("src/main/resources/worker.txt");
try (Scanner scanner = new Scanner(file)) {
System.out.println(parseFileToObjectList(scanner));
} catch (FileNotFoundException e) {
System.out.println("Файл не найден");
}
}
public static List<Worker> parseFileToObjectList(Scanner scanner) {
List<Worker> workers = new ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] worker = line.split(" ");
Worker currentWorker = new Worker(Long.parseLong(worker[0]), worker[1], worker[2], (worker[3]), worker[4]);
workers.add(currentWorker);
}
return workers;
}
}
Я не могу понять как парсить дату рождения и дату устройства на работу из строки в нужный тип данных (как я запарсил Long -> Long.parseLong(worker[0]). И вообще правильно ли я делаю?
Ответы (2 шт):
Мне кажется, вам лучше использовать сериализацию и десериализацию, а не записывать и читать данные из файла, как это делаете вы на данный момент .
Сериализация - побитовая запись объектов в файл. А десериализация - наоборот: чтение объекта из файла. Вы как раз пытаетесь считать данные из файла и поместить их в объект.
Правильней будет записать в файл сам объект побитово (от слова "бит", 1/8 байта) и потом его считать, а не проводить махинации со Scanner'ом. Ничего сложного в сериализации нет, стоит только хорошенько разобраться =).
Как сериализовать и десериализовать объект?
- Класс, объекты которого нужно сериализовать, должен реализовывать интерфейс
Serializableиз пакетаjava.io. В интерфейсеSerializableнет ни одного метода, но это такой "маркер", что объекты этого класса можно сериализовывать.
import java.io.Serializable;
public class Worker implements Serializable{
private Long id;
private String firstName;
private String lastName;
private Date dateOfBirth;
private Date dateOfEmployment; //время устройства на работу
}
- Теперь можно записать объект файл. Вот как это сделать:
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
public class App {
public static void writeObject(){
//В конструктор передайте значения полей для этого объекта
Worker worker1 = new Worker ();
try{
//В конструктор подаём путь к файлу.
FileOutputStream fileOutputStream = new FileOutputStream("src/main/resources/worker.txt");
//В конструктор подаём объект класса FileOutputStream
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//Метод writeObject() записывает в файл объект.
//В параметрах нужно указать тот объект, который нужно записать в файл
objectOutputStream.writeObject(worker1);
objectOutputStream.close(); //ObjectOutputStream нужно обязательно закрыть!
System.out.println("Сериализация объектов прошла успешно!");
} catch(IOException e){
System.err.println("Ошибка! Не удалось сериализовать объекты!");
}
}
}
Теперь объект записан в файл. Можно его оттуда считать. Делается это похожим образом. Обратите внимание: для чтения файла создаются объекты других классов! То есть не FileOutputStream и ObjectOutputStream, а FileInputStream и ObjectInputStream!
public static void readObject(){
try{
FileInputStream fileInputStream = new FileInputStream(path);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
/*
Метод readObject() читает объект из файла. Важно! Этот метод возвращает
объект класса java.lang.Object,
поэтому необходимо совершить нисходящее преобразование.
*/
Worker worker1 = (Worker) objectInputStream.readObject();
System.out.println("Десериализация объектов прошла успешно!");
objectInputStream.close(); //Обязательно закрываем поток!
} catch(IOException e){
System.err.println("Ошибка! Не удалось десерилизовать объекты!");
} catch(ClassNotFoundException e){
//Это исключение произойдёт, если в проекте не будет класса Worker
System.err.println("Ошибка! Класс Worker был удалён или перемещён!");
}
}
P.S Лучше использовать try-with-resources, а не закрывать потоки ввода/вывода в конце блока try.
Для начала неплохо бы отказаться от класса Date. Начиная с java 8, появился пакет java.time, настоятельно рекомендуется использовать именно его. Кроме того, если уж вы говорите о преобразовании массива стрингов в объект, то лучше это делать не в методе, который парсит данные, а в конструкторе объекта. Посему ваш класс будет выглядеть так:
class Worker {
private final static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private Long id;
private String firstName;
private String lastName;
private LocalDate dateOfBirth;
private LocalDate dateOfEmployment; //дата трудоустройства
public Worker(String[] args) {
this.id = Long.valueOf(args[0]);
this.firstName = args[1];
this.lastName = args[2];
this.dateOfBirth = LocalDate.parse(args[3], FORMATTER);
this.dateOfEmployment = LocalDate.parse(args[4], FORMATTER);
}
}
В этом случае вашему парсеру больше не нужно знать ничего о полях класса, объекты которого он создает, ведь ваш класс принимает просто массив строк и сам знает как корректно преобразовать их в объект. Посему парсер можно сделать более универсальным:
public class App {
public static void main(String[] args) throws Exception {
List<Worker> result = parseFileToObjectList("src/main/resources/worker.txt", " ", Worker.class);
System.out.println(result);
}
private static <T> List<T> parseFileToObjectList(String fileName, String separator, Class<T> modelClass) throws Exception {
try (Scanner scanner = new Scanner(new File(fileName))) {
List<T> cities = new ArrayList<>();
while (scanner.hasNextLine()) {
String[] splitLine = scanner.nextLine().split(separator);
Constructor<T> constructor = modelClass.getConstructor(String[].class);
cities.add(constructor.newInstance(new Object[] {splitLine}));
}
return cities;
}
}
}
Теперь с помощью этого кода вы можете парсить данные из текстового файла с любыми разделителями в любой кастомный класс, сождержащий конструктор, который принимает массив строк.
Если есть вопросы, не стесняйтесь их задавать