Search code examples
javafile-iobytecode

How does java source code ultimately interact with files?


I have been wondering about how the java compiler/interpreter manages to provide an interaction between bytecode/source code and file input and output.

I understand that InputStream and OutputStream are the super-classes of all file i/o classes, but after reading through them, they provide no implementation for the basic file interaction methods (read() and write(byte b)). My initial idea was that perhaps the compiler would translate these methods into certain bytecode operations (that return a byte from a file or write a byte to a file) that only occur in this instance, but this may not be correct.

If Java were compiled to assembly, then I understand that a certain instruction would be translated into platform-specific file i/o code (e.g. cout << ... in C), but this is obviously (afaik) not the case with Java since it's compiled to bytecode.

Can anyone enlighten me?


Solution

  • The InputStream and OutputStream classes do not provide any implementation themselves. They both are abstract classes that require derived classes to provide an implementation. For example, the read() method of the InputStream class is an abstract method:

    public abstract int read() throws IOException
    

    Now, a derived class can provide an implementation in any way it sees fit, but you are correct that low-level operations such as operations which require access to system calls ultimately need some bootstrapping code which cannot be implemented in pure Java alone.

    For example, if you have a look at the source code for FileInputStream, you will see its read() method is declared native:

    public
    class  FileInputStream extends InputStream
    {
        /* (...) */
    
        public native int read() throws IOException;
    
        /* (...) */
    }
    

    Since FileInputStream is a core Java class, the JVM may be taking some shortcuts, but the typical way to implement native methods is through the Java Native Interface (JNI). The JNI makes it possible to provide an implementation of a Java method written in a different language such as C.

    How does this all fit together? When you are compiling a class which uses the read() method of FileInputStream, as far as the compiler is concerned, you are just using some method of some class, and the bytecode will be the same as for any other method and class. At runtime, when your call is executed, the JVM, having previously loaded the FileInputStream class, knows the method you are calling is a native method, and calls its native implementation.