Method that reads an input file of a particular format and creates objects corresponding to the file This is essentially a best practices question about reading a file and making sure it follows a specific format. My current implementation works, but it's a while loop with a bunch of if/else statements. I think it wouldn't be too readable for other developers.

/**
 * Request the server to add the theatre details found in the file indicated by the path.
 * The details include the theatre ID (unique across all theatres), the seating dimension, and
 * the floor area (square metres). All theatres have as many seats in a row as they do rows, and the number of
 * rows is indicated by the seating dimension.
 * &lt;p&gt;
 * &lt;p&gt;The file format is, each line consists of
 * "THEATRE" "\t" theatre ID; "\t" seating dimension; "\t" floor area;
 * &lt;p&gt;
 * &lt;p&gt;If there is no file associated with the path, or the file format is incorrect, then
 * the request fails.
 *
 * @param path The path indicating the file to use for the initialisation.
 * @return An empty string if the initialisation is successful, otherwise a
 * message explaining what went wrong beginning with ERROR (e.g. "ERROR incorrect format").
 */
public String initialise(String path)
{
    //IO
    File theatreFile = new File(path);
    Scanner fileScanner = null;

    try
    {
        fileScanner = new Scanner(theatreFile);
    }
    catch (FileNotFoundException e)
    {
        return ResponseMessages.FILE_NOT_FOUND_ERR_MSG.getDescription();
    }

    String currentID = null;
    int numRows;
    int floorSpace;

    /*Loop through input file tokens, check if format is correct and if so, create a theatre object
    that corresponds with the given data. */
    while(fileScanner.hasNext())
    {
        if(fileScanner.hasNext(THEATRE_NAME_MARKER))
        {
            fileScanner.next();

            if(fileScanner.hasNext(THEATRE_CODE_MARKER))
            {
                currentID = fileScanner.next();

                if (getTheatreIDs().contains(currentID))
                {
                    return ResponseMessages.DUPLICATE_CODE_ERR_MSG.getDescription();
                }

                if (fileScanner.hasNextInt())
                {
                    numRows = fileScanner.nextInt();

                    if (fileScanner.hasNextInt())
                    {
                        floorSpace = fileScanner.nextInt();
                    }
                    else
                    {
                        return ResponseMessages.FILE_FORMAT_ERR_MSG.getDescription();
                    }
                }
                else
                {
                    return ResponseMessages.FILE_FORMAT_ERR_MSG.getDescription();
                }
            }
            else
            {
                return ResponseMessages.FILE_FORMAT_ERR_MSG.getDescription();
            }
        }
        else
        {
            return ResponseMessages.FILE_FORMAT_ERR_MSG.getDescription();
        }

        Theatre newTheatre = new Theatre(currentID, numRows, floorSpace);
        theatres.add(newTheatre);
    }

    fileScanner.close();
    return ResponseMessages.FILE_FOUND_SUCCESS_MSG.getDescription();
}


Additionally, here's an example of what an input file may look like:

THEATRE T2  10  400
THEATRE T1  7   200
THEATRE T3  12  600
THEATRE 215 21  1200
As you are asking for best practices: methods and exceptions.

First of all, your code does everything in one method, where you could (and should) clearly divide the parts of


looping over the file
parsing a single line
checking uniqueness of the theatres


As you return with a failure anyway if a single line fails, you might as well throw an exception and stop the complete process. If you want to stick to the interface that returns an error/success condition as a result value, you may well catch and return in the outermost method.

Rough sketch:

private static class MyFileParsingException extends RuntimeException {
    ... add a constructor with a message here
}

public String initialise(String pathString) {
    try {
        Path filePath = Paths.get(pathString);
        List&lt;Theater&gt; theatres = Files.lines(filePath)
            .map(this::parseLineToTheater)
            .collect(Collectors.toList());
        checkUniqueness(theatres );
        this.theatres = theatres; // only set if successful
        return ... // success
    }
    catch(IOException e) {
        return ResponseMessages.FILE_NOT_FOUND_ERR_MSG.getDescription();
    }
    catch(MyFileParsingException e) {
        return e.getMessage();
    }
}

private Theater parseLineToTheater(String inputLine) throws MyFileParsingException {
    // parse single line, throw MyFileParsingException with the
    // appropriate message, return the theater
}

private void checkUniqueness(List&lt;Theater&gt; allReadTheaters) throws MyFileParsingException {
    // check, whether the ids are unique, throw MyFileParsingException
    // with DUPLICATE_CODE_ERR_MSG if you find a duplicate
}


Here, you have a trivial outer loop (or in fact a stream), which uses the nio.Files methods (which have been around since java 8, i.e. more than 4 years now - time to start using them), and two more methods which can trivially be unit-tested without even creating a file.

A little twist is extending RuntimeException instead of Exception for the hommade business exception, so that it can be used in a lambda expression. As it extends RuntimeException, the throws declaration is technically not really necessary on the methods, but I like to have them there so that the reader immediately sees that this exception type is to be expected.