IO and Reflection


The Brenny | 20th Feb, 2015

So as you might know, I've been working on my JUMG Library (Git Blog) for the past 2 months or so now and recently I had been working on the FileIO and the FileInterpreter classes. This means I have been working on file reading and reflection, to instantiate classes that I have hardly an idea about. Because of this, I wanted to express my findings in hope to help the rest of the world. So here's my input...

So as you might know, I've been working on my JUMG Library (Git Blog) for the past 2 months or so now and recently I had been working on the FileIO and the FileInterpreter classes. This means I have been working on file input, file output and reflection, to instantiate classes that I have hardly an idea about. Because of this, I wanted to express my findings in hope to help the rest of the world. So here's my input...

Firstly, IO. When starting out with IO, it's important to know where to start. What classes are you going to use? Where are you going to read from? What will be the character encoding? Will the be compression? All these things need to be taken into account when writing your methods to read a file. To answer the above questions for this example:

  • Classes to use: BufferedReader and BufferedWriter.
  • Where we are reading to/from: "user.home"/fileIO/file.txt
  • Character encoding: Normal for your locale.
  • Compression: No compression.

    public class FileIO {
        public static void writeData(String file, String... lines) {}
        public static ArrayList<String> readData(String file) {return null;}
    }

Jot that skeleton down in your favourite IDE, and get ready to start writing the writeData(String, String...) method. The reason we need the first parameter is to know where to write the data to. Obviously, no file, no writing. The second parameter is actually an array with variable amounts of data that can be used. It's just a way of saying, the rest of the parameters are, in this case, strings. It saves the JVM from having to make an array of strings to pass through, thus saving memory.

    public static void writeData(String file, String... lines) {
        try {
            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(file))); //1
            for(String line : lines) { //2
                bw.write(line);
                bw.newLine();
            }
            bw.flush(); //3
            bw.close();
        } catch(IOException e) { //4
            e.printStackTrace();
        }
    }

1: First thing in our method will be the creation of the object we need: the BufferedWriter that we will be using to output the data to the file. this comes from creating a FileWriter the the file.

2: Next we need to enumerate over all the lines that we want to output to. This is the 'advanced' for-loop which takes the next item from the array and stores it in String line. We write the line to the file and then write out a return line after it so we aren't colliding lines together.

3: Finally we flush the data that the BufferedWriter has stored in itself, and then we close the connection to the file, making it available again to the user and/or other programs (hint: it's free so we can reuse it!).

4: This doesn't really count as a step in writing, but it's handy if something goes wrong. If something does go wrong, the terminal/command line that is attached to this JVM will output the error, pointing out what really went wrong. A common error is trying to open the file when it hasn't been closed properly, which is what I've mentioned in step three.

Now we know how to write data to a file, we need to find out how to read data from a file. It's essentially the same thing as writing data (as in the code is the same) but it has a few different words here and there.

    public static ArrayList<String> readData(String file) {
        try {
            BufferedReader br = new BufferedReader(new FileReader(new File(file))); //1
            String line = "";
            while((line = br.readLine()) != null) { //2
                data.add(line);
            }
            br.close(); //3
        } catch(IOException e) { //4
            e.printStackTrace();
        }
    }

1: This first bit is very similar to the first bit for File Writing. It's creating a connection to the file we specified so we can read from it, though, this time.

2: Because Java can handle assignment logic (assigning a variable while checking it at the same time) we can set line to the next line that the br and check if it's null or not. Generally a null line means you have reached the end of a file.

3: Finally, again like last time, we close the connection to the file, but we do not flush it. This is because the BufferedReader doesn't store data to be flushed, unlike the BufferedWriter.

4: Again, four doesn't need a huge mention. It just helps out with debugging.


Now we know how to read and write data, we need to learn reflection techniques so we can instantiate objects and call methods from classes based off the name and the parameters it needs. This simplifies everything when it comes down to creating your own language interpreter (just like what we are doing here).

But first, what is reflection? Well, when I called upon Google to answer this, StackOverflow was the top link, so the answer to this question on StackOverflow is probably the best way put. Here it is: "The name reflection is used to describe code which is able to inspect other code in the same system (or itself)."

When people start talking about reflection, they get often scared by the fact that it involves many lines of code, and think it's a very confusing topic. Really, reflection is quite the opposite. It's sort of like programming Java but calling a method to invoke the one you want. All the confusing stuff is all under the hood in Java's core libraries, kindly given to use by the good people at Oracle.

For this example, we will assume that we are instantiating classes that we don't know. This means that we need to find a class, method and parameters all based off user input. Let's start with a basic skeleton that we will use to build our reflection class on:

    public class FileReflector {
        public static Class<?> getClass(String clazz, boolean instantiate) throws ClassNotFoundException {return null;}
        public static Field getField(Class<?> c, String field) throws NoSuchFieldException, SecurityException {return null;}
        public static Method getMethod(Class<?> c, String method) {return null;}
    }

First we'll start by trying to find a class. This is fairly easy. All it is, is calling a static Class method called forName(String, boolean, ClassLoader). I'll go through in a moment about what all those parameters mean. But first, we need to create our methods!

    public static Class<?> getClass(String clazz, boolean instantiate) throws ClassNotFoundException { //1
        return Class.forName(clazz, instantiate, ClassLoader.getSystemClassLoader());
    }
    public static Field getField(Class<?> c, String field) throws NoSuchFieldException, SecurityException { //2
        return c.getField(field);
    }
    public static Method getMethod(Class<?> c, String method) { //3
        for(Method m : c.getMethods()) {
            if(m.getName().equals(method)) return m;
        }
        return null;
    }

1: This method calls the static method forName(String, boolean, ClassLoader). The parameters are "The class", "make a new instance", "class loader to load with". The third parameter is generally disused throught small programs, but I guess when making something large which needs to use reflection as it interacts with many other JVMs, I guess it's needed. Our method only needs the class name, and whether to make a new instance of the class or not. Making a new instance can come in handy for things like reflecting a new entity in a game. You can call methods and fields using reflection. However, instantiated or not, you can call .newInstance() on the returned class and attach it to an object of that type.

2: This method asks the class if it has a FIELD that goes by the name of what we ask for. There'll be problems if that field doesn't exist, or if who we are asking on behalf of doesn't have the right permissions to get to that field - that's mainly if the field is a private, or protected field.

3: This method asks the class to return the method we want. From this, we can call the invoke(Object, Object...) method to invoke it. The parameters are: 1) The first object is the underlying object that the method will invoke on, and 2) The object array that will be used as parameters for this method.


Putting two and two together with some glue, you'll end up getting something that can interpret a file of your own syntax and manipulate a java program to your needs. This can be very handy when making save game files, or even your own language if you're making a macro. For an example of a file interpreter that is in a usable state, see my FileInterpreter class in my JUMG Library which uses the JUMG syntax. Unfortunately, my FileInterpreter class had no place in JUMG, so I felt it was necessary to remove it, however, there are many other classes out there that can provide an example.

Oh oh...

Get in touch!


Got questions about programming, electronics, or life? Shoot an email!