Creating Files in Java
Create new files and directories in Java with File.createNewFile, Files.createFile, and Files.createDirectories.
Creating Files in Java
"Make a new empty file" and "make this directory tree" are two of the smallest operations the file system offers, yet Java exposes four overlapping ways to do each. The differences matter — they're the difference between "fails if the file exists" and "overwrites silently," between "mkdir" and "mkdir -p," between a boolean-returning legacy method and an exception-throwing modern one.
This chapter covers the four creators:
File.createNewFile()— legacy file creation.File.mkdir()/File.mkdirs()— legacy directory creation.Files.createFile(path)— modern atomic "create or fail."Files.createDirectory(path)/Files.createDirectories(path)— modern directory creation.
Plus the temp-file helpers (Files.createTempFile, Files.createTempDirectory) and the OpenOption flags that let writers create files implicitly.
File.createNewFile() — legacy, returns a boolean
File f = new File("data/users.txt");
boolean created = f.createNewFile(); // true if it actually created the file
// false if it already existed
// throws IOException if the parent dir is missingThe contract is atomic check-and-create: the OS guarantees no other process can create the same path between the existence check and the creation. That makes createNewFile a primitive lock under some legacy "PID file" idioms — if (!f.createNewFile()) throw new IllegalStateException("already running");
What it does not do:
- It does not create missing parent directories.
new File("does/not/exist/file.txt").createNewFile()throwsIOException. - It returns
false(not throws) when the file already exists.
If you only need the file to exist by the end of the call, the false return value is fine. If you need the file to be brand-new (a lock semantics), false is your trigger to take a different path.
File.mkdir() and File.mkdirs()
new File("logs").mkdir(); // creates "logs" — fails if "." has no perms or parent missing
new File("a/b/c").mkdirs(); // creates "a", "a/b", and "a/b/c" — like `mkdir -p`Both return boolean, both lose information about why a failure happened. mkdir fails if a parent is missing; mkdirs doesn't. Both succeed (return true) only if the directory was newly created — if it already exists, they return false. Combined with the "no error info" problem, this is the kind of legacy API that gets wrapped in a helper:
File dir = new File("data");
if (!dir.exists() && !dir.mkdirs()) throw new IOException("cannot create " + dir);The modern Files.createDirectories(path) is the one-liner replacement.
Files.createFile(path) — modern, throws
Files.createFile is the java.nio.file peer of File.createNewFile() with one important difference: it throws instead of returning a boolean.
Path p = Path.of("data/users.txt");
Files.createFile(p); // creates an empty regular file
// throws FileAlreadyExistsException if it exists
// throws NoSuchFileException if the parent is missingFileAlreadyExistsException is what you catch if the existence outcome matters; NoSuchFileException is what you catch (or prevent with createDirectories) if the parent might not be there. The exception types are specific subclasses of IOException, so a blanket catch (IOException e) still works.
You can pass FileAttribute arguments to set POSIX permissions at creation time on Unix — the most common use is to ensure secret files (private keys, tokens) are created 0600:
import static java.nio.file.attribute.PosixFilePermissions.*;
var attr = asFileAttribute(fromString("rw-------"));
Files.createFile(Path.of("/tmp/secret"), attr); // born with 0600 permissions, atomically(That call throws UnsupportedOperationException on Windows, which has no POSIX permission model — guard with a platform check if you target both.)
Files.createDirectory versus Files.createDirectories
The same difference as mkdir versus mkdir -p, just exception-based:
Files.createDirectory(Path.of("logs")); // one level deep; parent must exist
Files.createDirectories(Path.of("a/b/c")); // creates every missing ancestorcreateDirectory throws FileAlreadyExistsException if the target already exists and is not a directory; if it's already a directory, it throws too (not what you usually want).
createDirectories is the friendlier choice: it does nothing if every directory is already there, and creates whatever's missing otherwise. It does not throw if the path already exists as a directory. That makes it idempotent — safe to call at startup without an exists() check.
Temp files and directories
For tests, scratch space, and "I need somewhere safe to put this for a few minutes," the JDK ships Files.createTempFile and Files.createTempDirectory:
Path scratch = Files.createTempFile("session-", ".log"); // /tmp/session-3829387.log
Path workdir = Files.createTempDirectory("export-"); // /tmp/export-1827392Both pick a unique name in the system temp directory, both return a Path to the new entry, and both create the entry with restrictive permissions on Unix. The prefix and suffix are hints the JDK appends a unique value to — you don't get to choose the exact name (that's the point: another caller can't predict it and clobber yours).
Temp files are not deleted automatically. You either:
- Call
Files.deleteIfExists(path)when you're done; or - Call
path.toFile().deleteOnExit()to schedule a delete on JVM shutdown (cleared by hard kills); or - Open the file with
StandardOpenOption.DELETE_ON_CLOSEif you only need it while a stream is open.
Writers create files implicitly
Most of the time you don't need a "create file" call at all — a writer creates one for you. Files.newBufferedWriter, Files.write, and Files.writeString all accept OpenOption... varargs that decide what happens when the file does or doesn't exist:
import static java.nio.file.StandardOpenOption.*;
Files.writeString(path, "hello\n", CREATE, WRITE, TRUNCATE_EXISTING);
Files.writeString(path, "more\n", CREATE, WRITE, APPEND);
Files.writeString(path, "new\n", CREATE_NEW); // fails if file existsCREATE— create if missing, otherwise open existing.CREATE_NEW— create, throwFileAlreadyExistsExceptionif it exists. The same semantics asFiles.createFile.TRUNCATE_EXISTING— clear the file's contents on open (the default forwriteStringwhen not appending).APPEND— write at the end without truncating.
The default for Files.writeString (no options) is CREATE, WRITE, TRUNCATE_EXISTING — i.e. "create or overwrite." Files.newBufferedWriter defaults the same way. If you want append semantics, you must say so.
A worked example: each creator side by side
The program below builds a small tree from scratch under the system temp directory using both APIs and several open options. Each step prints what changed; the last block shows what happens when you re-run an operation against a path that already exists.
What to take from the run:
- The first
legacy.createNewFile()returnedtrue(created); the second returnedfalse(already existed). Thebooleandoesn't tell you what happened — you have to remember the convention. deep.mkdirs()succeeded once and returnedfalsethe second time. Thatfalselooks identical to "permission denied" or "parent missing" — exactly the missing-error-info problemFilessolves.Files.createFileon an existing path threwFileAlreadyExistsException. The exception type is specific, so a real handler can distinguish "already there" from "permission denied" without parsing strings.Files.createDirectoriescalled twice in a row did nothing harmful the second time. That's the property that makes it the right choice in startup code: no guard, just call it.Files.writeString(log, "line 1\n")created the file becauseCREATEis in the default options. The second and third calls usedAPPENDexplicitly, and the file accumulated three lines. The fourth call usedCREATE_NEWand refused to overwrite. The defaults are designed for the "overwrite with new content" case; you opt into append.Files.createTempFile(root, "scratch-", ".tmp")produced a name likescratch-1827392.tmp— your prefix and suffix, plus a unique chunk the JVM picks so two concurrent calls never collide.- The cleanup walks
rootin reverse order so child files and directories disappear before their parents.Files.deleterefuses to delete a non-empty directory; that ordering is how a manualrm -rfis built.
What's next
You can create files; the next chapter, Reading Files in Java, reads them — first with the modern one-liners (Files.readString, Files.readAllLines, Files.lines), then with the classic FileReader / BufferedReader / Scanner decorator stack so the older code in the next chapters has a foundation.
Practice
You want a one-liner that creates a directory plus any missing parent directories, and does **nothing** if the directory already exists. Which call is it?