Reading a file in Java means opening it, pulling the text or data out, and storing it in your program so you can work with it
Java gives you several ways to read files, and which one you use depends on what kind of file you have and what you want to do with the data. The simplest approach for most beginners is to use the Files class, which handles the opening and closing automatically. If you need more control — like reading a file line by line without loading the whole thing into memory — you use a BufferedReader. For binary files like images or PDFs, you use different tools altogether.
Before you can read anything, your file has to exist on your computer and your program has to know where to find it. You give Java the file path (the location on your hard drive), Java opens it, reads what you ask for, and then closes it. If the file does not exist or Java cannot find it, your program will throw an error and stop — so you have to handle that possibility in your code.
Key Takeaways
- The Files.readAllBytes() method is the fastest way to read an entire file into memory if the file is small.
- Use BufferedReader when you need to read a large file line by line without loading everything at once.
- Always wrap file-reading code in a try-catch block to handle errors when a file does not exist or cannot be read.
- File paths can be absolute (the full location from your hard drive root) or relative (relative to where your program is running).
- Java automatically closes files opened with try-with-resources syntax, which prevents memory leaks.
Reading an entire file at once with Files.readAllBytes()
If your file is small and you want all of it in memory at once, Files.readAllBytes() is the simplest choice. You give it a file path, and it returns the entire contents as a byte array. This works well for configuration files, small text files, or JSON data.
Here is the basic structure:
import java.nio.file.Files; import java.nio.file.Paths; try { byte[] fileContent = Files.readAllBytes(Paths.get("myfile.txt")); String content = new String(fileContent); System.out.println(content); } catch (Exception e) { System.out.println("File not found or cannot be read"); }
The Paths.get() method takes your file name or path and turns it into something Java can work with. The try-catch block catches errors — if the file does not exist, the catch block runs instead of crashing your program. After you read the bytes, you convert them to a String so you can actually read the text.
This approach works fine for files up to a few megabytes. If your file is hundreds of megabytes or larger, loading it all at once will use too much memory and slow your program down. That is when you switch to reading line by line.
Reading a file line by line with BufferedReader
When you have a large file or you only need to process one line at a time, BufferedReader is the right tool. It reads from the file in chunks and gives you one line at a time, so your program uses much less memory.
Here is how it works:
import java.io.BufferedReader; import java.io.FileReader; try (BufferedReader reader = new BufferedReader(new FileReader("myfile.txt"))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (Exception e) { System.out.println("File not found or cannot be read"); }
The try (BufferedReader reader = ...) syntax is called try-with-resources. It automatically closes the file when you are done, even if an error happens. Inside the try block, reader.readLine() pulls one line from the file and returns it as a String. When there are no more lines, it returns null, so the while loop stops.
This method is memory-efficient because Java only keeps one line in memory at a time. You can process each line, store it somewhere, or throw it away before reading the next one. This is the standard way to read large text files, CSV files, or log files.
Understanding file paths and where Java looks for files
When you write Paths.get("myfile.txt"), Java looks for that file in the current working directory — usually the folder where your program is running from. If the file is in a different folder, you have to tell Java where it is.
A relative path starts from the current working directory. If your file is in a subfolder called data, you write Paths.get("data/myfile.txt"). If your file is in a parent folder, you write Paths.get("../myfile.txt") (the two dots mean "go up one level").
An absolute path starts from the root of your hard drive and includes the full location. On Windows, that looks like C:\\Users\\YourName\\Documents\\myfile.txt. On Mac or Linux, it looks like /Users/YourName/Documents/myfile.txt. Absolute paths are less portable — if you move your program to another computer, the path might not work anymore. Relative paths are usually better because they work as long as the file is in the right folder relative to your program.
Handling errors when a file cannot be read
Three common things go wrong when you try to read a file: the file does not exist, you do not have permission to read it, or the file path is wrong. Java throws an exception (an error) in all these cases, and if you do not catch it, your program stops.
The try-catch block is how you handle these errors. The try block contains the code that might fail. If something goes wrong, Java jumps to the catch block and runs whatever code you put there. You can print an error message, use a default value, or ask the user to provide a different file path.
A more specific approach catches different types of errors separately:
try { byte[] content = Files.readAllBytes(Paths.get("myfile.txt")); } catch (java.nio.file.NoSuchFileException e) { System.out.println("File does not exist"); } catch (java.nio.file.AccessDeniedException e) { System.out.println("You do not have permission to read this file"); } catch (Exception e) { System.out.println("Some other error happened"); }
This way, you can respond differently depending on what went wrong. If the file does not exist, maybe you create it. If you do not have permission, maybe you ask the user to move the file somewhere else. The last catch block (Exception) catches anything you did not expect.
Reading specific parts of a file
Sometimes you do not need the whole file — you just need a certain number of bytes, or you need to start reading from a specific position. Java lets you do this with RandomAccessFile, which lets you jump to any position in a file and read from there.
RandomAccessFile is more complex than the other methods, so you only use it when you really need to skip around in a file. For most tasks, reading the whole file or reading line by line covers what you need.
If you need to read only certain lines (like lines 5 through 10), the easiest approach is to read the whole file line by line and skip the lines you do not want. If your file is huge and you need to do this often, then RandomAccessFile becomes worth learning.
Choosing the right method for your situation
Use Files.readAllBytes() or Files.readAllLines() when your file is small (under a few megabytes) and you want all the data at once. This is the simplest code and works well for configuration files, small JSON files, or data you need to process all together.
Use BufferedReader when your file is large or you want to process it line by line. This is the standard choice for log files, CSV files, or any text file where you might want to stop reading partway through or process each line separately.
Use RandomAccessFile only when you need to jump around in a file or read from a specific byte position. This is rare in most programs.
For binary files like images or PDFs, the same methods work, but you usually do not convert the bytes to a String. Instead, you write the bytes to another file or pass them to a library that knows how to handle that file type.
Frequently Asked Questions
What is the difference between readAllBytes() and readAllLines()?
readAllBytes() gives you the entire file as one byte array, which you then convert to a String. readAllLines() gives you a List of Strings, where each String is one line. Use readAllLines() if you want to work with individual lines but still load the whole file at once.
Do I have to close the file myself?
No, not if you use try-with-resources syntax (the try statement with parentheses around the reader). Java closes the file automatically when the try block ends. If you create a reader without try-with-resources, you should call reader.close() yourself in a finally block to avoid memory leaks.
What happens if I try to read a file that does not exist?
Java throws a NoSuchFileException, which stops your program unless you catch it with a try-catch block. Always wrap file-reading code in try-catch so your program can handle the error gracefully instead of crashing.
Can I read a file while another program is writing to it?
Yes, but you might read incomplete data if the other program is still writing. On some systems, the file might be locked and you cannot read it at all. If you need to read files that are being written to, you should read a copy of the file or wait until the other program is done.
How do I read only part of a very large file?
Use BufferedReader and stop reading when you have what you need — just break out of the while loop. If you need to read from a specific byte position in the middle of the file, use RandomAccessFile and call seek() to jump to that position.