Reading and writing text files
Package by feature, not layer
.class
files can also contain any other kind of file as well.
If a text file or a binary file is only used by a single package, then the most natural home for
it is often the very same directory/package that uses it.
This will tend to increase modularization and coherence.
(See the package-by-feature topic as well.)
Such files might be used for:
At runtime, the most convenient way of accessing such files is with these methods:
Class.getResoureAsStream
Class.getResource
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Objects; import java.util.Scanner; /** Read a text file that's in the same directory as this (.class) file. */ public final class ReadNeighbouringTextFile { public static void main(String... args) throws IOException { log("Classpath: " + System.getProperty("java.class.path")); log("user.dir: " + System.getProperty("user.dir")); ReadNeighbouringTextFile read = new ReadNeighbouringTextFile(); read.readFileViaStream(); read.readFileasViaUrl(); //read.readFileViaPath(); } void readFileViaStream() throws IOException { log("Via stream..."); try ( //uses the class loader search mechanism: InputStream input = this.getClass().getResourceAsStream("test.txt"); InputStreamReader isr = new InputStreamReader(input, ENCODING); BufferedReader reader = new BufferedReader(isr); ){ String line = null; while ((line = reader.readLine()) != null) { //process the line in some way log(line); } } } void readFileasViaUrl() throws IOException{ log("Via URL..."); //this doesn't work in a Java Web Start context //the URL points back to the server; it's not local //uses the class loader search mechanism: URL url = this.getClass().getResource("test.txt"); URI uri = null; try { uri = url.toURI(); } catch(URISyntaxException ex){ //in practice this will be very rare ex.printStackTrace(); } Path path = Paths.get(uri); //now that you have the path, it's just regular text file processing //this gets the whole content at once: List<String> lines = Files.readAllLines(path, ENCODING); log("Number of lines in the file: " + lines.size()); //OR, use this style, to process each line one at a time try (Scanner scanner = new Scanner(path, ENCODING.name())){ while (scanner.hasNextLine()){ //process each line in some way log(scanner.nextLine()); } } } /** Here, relative Path objects don't know about the file system in the same way that a classloader does. It only knows about the 'user.dir' directory, the base directory of the runtime; this style is much less flexible, and is not recommended. */ void readFileViaPath() throws IOException{ log("Via path (not recommended)..."); //Succeeds: absolute reference //Path path = Paths.get("C:\\myproj\\test-api\\bin\\test.txt"); /* * Relative reference. * * Fails when the file is beside the .class file. * Succeeds only when the test.txt file is in the 'user.dir' directory. * * This means that relative paths don't use the classpath; they * only use the 'user.dir' System property. */ Path path = Paths.get("test.txt"); log("Path: " + path); log("Absolute: " + path.isAbsolute()); List<String> lines = Files.readAllLines(path, ENCODING); log("Number of lines in the file: " + lines.size()); } private final static Charset ENCODING = StandardCharsets.UTF_8; private static void log(Object msg){ System.out.println(Objects.toString(msg)); } }