Как правильно из строки, которая записана в файл.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'ом. Ничего сложного в сериализации нет, стоит только хорошенько разобраться =).

Как сериализовать и десериализовать объект?

  1. Класс, объекты которого нужно сериализовать, должен реализовывать интерфейс 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; //время устройства на работу
}
  1. Теперь можно записать объект файл. Вот как это сделать:
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;
        }
    }
    
}

Теперь с помощью этого кода вы можете парсить данные из текстового файла с любыми разделителями в любой кастомный класс, сождержащий конструктор, который принимает массив строк.

Если есть вопросы, не стесняйтесь их задавать

→ Ссылка