SamTebbs33
SamTebbs33

Reputation: 5647

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?

Upvotes: 2

Views: 624

Answers (3)

Philippe Aubertin
Philippe Aubertin

Reputation: 1063

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.

Upvotes: 2

Philipp
Philipp

Reputation: 69663

File I/O is provided by the operating system. Writing a file on Linux works differently than writing a file on Windows. Even low-level languages like C usually provide an abstraction layer in the standard library to make it look the same, but the generated binary code for either platform calls completely different operating system functions. That means any file I/O is in the end an operating-system specific task. OS abstraction in Java is provided by the Java Virtual Machine.

The implementation of all Java standard classes (like java.io.FileOutputStream) is shipped with your OS-specific Java Virtual Machine (the software you get when you click on "Free Java Download" on java.com). The JVM can implement the methods of these classes in Java, or can choose to implement a method native which looks like this.

This means that the method is implemented in the programming language the JVM is written in. When the class does something which must be provided by a call to the operating system (like file IO), it can only be implemented natively.

Upvotes: 2

fge
fge

Reputation: 121710

Like all classes, and since they are not interfaces, what will happen is that there will be an invokevirtual on the method, nothing else (there are 5 invocation opcodes in the JVM: invokevirtual, invokeinterface, invokespecial, invokestatic and invokedynamic).

Now, of course, the JRE will certainly have some native methods to deal with OS-level stuff (when dealing with File*Streams for instance); and for what it matters, the actual *Stream implementations may differ from OS to OS, and from JRE to JRE as well. All that matters is that the method does what its contract says it does.

After that, at runtime, the JIT may replace it with more efficient code (even native code). But InputStream and OutputStream are classes like any other; such details are of no relevance to the Java developer but, of course, of the utmost importance to the JDK developer ;)

Upvotes: 2

Related Questions