This time around we’ll look at files
But I also intend to make more use of VS Code
Make a new directory for the code for this session. I called min e_IX - Files_ and, in VS Code opened that directory
Create a new file
And set its LANGUAGE to Java by clicking on the blue hyperlinked Select a Language text
and select Java from the list
Save this as Files.java
and now the “project” should appear with that item in the list
Now we can start writing out code
Let suppose we want a simple app that can read a file, and echo its contents to a terminal window.
The first thing we will do is write out “application class”
Since the NAME of a file in Java also has to match the name of the principal class IN that file our Application class name is “Files”
So our declaration for this file should look like
class Files {
}
This class declaration is perfectly fine.
IF you try to RUN this java file by right clicking the file in the project browser and selecting Run Java or Debug Java from the contextual menu you will get an error.
By default a java class, when you try to RUN it, needs to have a main function. Xojo does as well just the IDE hides this from you so you dont have to think about it.
You then need a main method for the Java runtime to start the app.
If you create it as
public void main(String[] args ) {
}
it is SYNTACTICALLY valid - but again you will get an error. The reason is that this main method SHOULD ALWAYS be defined as “public static void” so the runtime can call it without having to create an instance of our principal class. In fact in Java the MAIN method should always be public, static, void because :
- In any Java program, the main() method is where the runtime starts execution.
- If the main() is allowed to be non-static, then to call the main() method the runtime has to create an instance of the class
- To create an instance the constructor has to be called. It will be ambiguous if the constructor of that class takes an argument.
- A static method of a class can be called by using the class name only without creating an instance. ( Note this is like Xojo’s SHARED methods in a class )
- The main() method in Java must be declared public, static and void. If any of these are missing, the Java program will compile but a runtime error will be thrown when you try to RUN that class as the main entry point
Alter the declaration for MAIN to :
public static void main(String[] args ) {
}
- public means the method is usable by any code OUTSIDE this class
- static means this method can be called without needing an instance
- void means there is NO return value from a call to this method
So far our code doesnt do much except execute. And do nothing.
Lets make it so this little app takes one, or more, file names as arguments and echos each file in turn to the terminal window its run in.
Our initial declaration already has the “arguments” parameter that is passed to the main method - thats String[] args which says that there is a String array passed to the main method that can be referred to using the local variable name args
So we can check that there are 1 or more arguments passed.
if (args.
aside IF VS code is NOT autocompleting for you open Preferences > Text Editor > Suggestions and click on the “edit in settings.json” under Quick Suggestions (personally I find this horrid UI but …)
In my case this json looks like
{
"editor.suggestSelection": "first",
"vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
"files.exclude": {
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true
},
"editor.quickSuggestions": true
}
Make sure editor.quickSuggestions has a true value. Save your work and the changes to the settings.json and restart VS Code an d you should now have autocompletion
back
So we can check that there are 1 or more arguments passed. If none are passed then we just print a suitable message and exit
if ( args.length < 1 ) {
System.out.println("nothing to do") ;
}
If there ARE arguments then we’ll open each file, and print the contents. The easiest way to iterate every string in args is with a Java foreach
if ( args.length < 1 ) {
System.out.println("nothing to do") ;
}
for (String filepath : args) { // in xojo this would be for each filepath as string in args
System.out.println("filepath = [" + filepath + "]") ;
}
Now if you run you will still see that the app, so far, still has no arguments passed to it.
In VS code there are a couple ways to configure your app for debugging.
For THIS testing I find the easiest is to use a Terminal window or to switch the lower panel in VS code to use a terminal. Click on the java process console and switch to zsh and we’ll run from the command line
In my case switching to the command line means I need to compile the code
javac Files.java
Then run it
java Files
and now to add arguments I can just drag files into this terminal window and I get properly quoted arguments
and when do run we see
npalardy@server IX- FIles % java Files '/Users/npalardy/Desktop/INN Beginning Java/IX- FIles/Files.java'
filepath = [/Users/npalardy/Desktop/INN Beginning Java/IX- FIles/Files.java]
npalardy@server IX- FIles %
1 argument and one output
If we run with multiple arguments we see one per argument
npalardy@server IX- FIles % java Files '/Users/npalardy/Desktop/INN Beginning Java/IX- FIles/Files.java' '/Users/npalardy/Desktop/INN Beginning Java/IX- FIles/Files.class'
filepath = [/Users/npalardy/Desktop/INN Beginning Java/IX- FIles/Files.java]
filepath = [/Users/npalardy/Desktop/INN Beginning Java/IX- FIles/Files.class]
npalardy@server IX- FIles %
So far so good. Now to take that file oath and open the file & print the contents out !
In Xojo taking that argument and turning it into a file we can list the contents of means you need to
- take the file path and get a folderitem for it
- open either a text input stream or binary stream so you can read the file
The process is the same in Java
However, in java you MUST tell the compiler which classes each file is going to use. It does not include much by default as the possible number of java libraries that you might need is huge and including all of them could make your Java application VERY bloated. Xojo by default includes the entire framework and then may strip out portions you have not used.
In order to use many filesystem related API’s we need to IMPORT java.io
Javas import system is very flexible and you can import one single class, a whole hierarchy or anything in between
Make the first line in our app
import java.io.* ;
and this will make the file system related input output mechanisms we’ll need be included so we can just use them.
In java the File object is more or less the equivalent of the Xojo Folderitem
To get a reference to a File we need to create one using the path to a file
That would be something like
File file = new File(filepath);
In Java just like in Xojo a fpath may refer to a file that does not exist
So we need to handle that
if (file.exists() == false) {
System.out.println("filepath = [" + filepath + "] does not exist") ;
continue ;
}
Now that we have a valid reference to a file that exists we can proceed to actually open the file & print its contents
FileInputStream fis = new FileInputStream(file);
int r=0;
while((r=fis.read())!=-1)
{
System.out.print((char)r); //prints the content of the file
}
FileInputStream is MOST analogous to Xojo’s BinaryStream (although it is READ ONLY by default)
So this code opens the stream and then reads byte by byte until it gets to the end of the file and prints a character for every byte as it goes
However, the while loop demonstrates something that is simple to do in Java that is also quite odd to read
The inner most statement
(r=fis.read())
reads 1 byte and assigns it to r
((r=fis.read())!=-1)
In java an assignment, like (r=fis.read()), has a value so it can be compared to other values
And thats what happens
File InputStreams return -1 when there is no more data
(FileInputStream (Java Platform SE 7 ))
System.out.print(char(r)) prints the character value to the standard output stream
AT this point the code looks like
import java.io.* ;
class Files {
public static void main(String[] args ) {
if ( args.length < 1 ) {
System.out.println("nothing to do") ;
}
for (String filepath : args) {
System.out.println("filepath = [" + filepath + "]") ;
File file = new File(filepath);
if (file.exists() == false) {
System.out.println("filepath = [" + filepath + "] does not exist") ;
continue ;
}
FileInputStream fis = new FileInputStream(file);
int r=0;
while( (r=fis.read())!=-1)
{
System.out.print((char)r); //prints the content of the file
}
}
}
}
there is however one issue
If you compile this code you get an error
npalardy@server IX- FIles % javac Files.java
Files.java:22: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
FileInputStream fis = new FileInputStream(file);
^
Files.java:25: error: unreported exception IOException; must be caught or declared to be thrown
while( (r=fis.read())!=-1)
^
2 errors
Remember that Java, unlike Xojo, REQUIRES you to either explicitly handle possible exceptions OR declare that your method is going to throw them.
And Java also documents ALL possible exceptions that a method might throw so its easy to know what you might have to handle.
So we’ll adjust the code to explicitly catch that possible exception
import java.io.* ;
class Files {
public static void main(String[] args ) {
if ( args.length < 1 ) {
System.out.println("nothing to do") ;
}
for (String filepath : args) {
System.out.println("filepath = [" + filepath + "]") ;
File file = new File(filepath);
if (file.exists() == false) {
System.out.println("filepath = [" + filepath + "] does not exist") ;
continue ;
}
try {
FileInputStream fis = new FileInputStream(file);
int r=0;
while( (r=fis.read())!=-1)
{
System.out.print((char)r); //prints the content of the file
}
}
catch (FileNotFoundException; fnf) {
// we're not going to do anything
}
}
}
}
If you try to compile now there STILL is one exception to catch
npalardy@server IX- FIles % javac Files.java
Files.java:26: error: unreported exception IOException; must be caught or declared to be thrown
while( (r=fis.read())!=-1)
^
1 error
npalardy@server IX- FIles %
When you read from the file something could go wrong
It could have gone missing between opening and trying to read the first time.
So if we catch the exception outside the while lop we’ll quit the while loop and just move on to the next file
The final code looks like
import java.io.* ;
class Files {
public static void main(String[] args ) {
if ( args.length < 1 ) {
System.out.println("nothing to do") ;
}
for (String filepath : args) {
System.out.println("filepath = [" + filepath + "]") ;
File file = new File(filepath);
if (file.exists() == false) {
System.out.println("filepath = [" + filepath + "] does not exist") ;
continue ;
}
try {
FileInputStream fis = new FileInputStream(file);
int r=0;
while( (r=fis.read())!=-1)
{
System.out.print((char)r); //prints the content of the file
}
}
catch (FileNotFoundException fnf) {
// we're not going to do anything
}
catch ( IOException iox ) {
// we're not going to do anything
}
}
}
}
Run it and play around with it and get a good feel for the things we’ve done
References
File - File (Java Platform SE 8 )
FileInputStream - FileInputStream (Java Platform SE 7 )
Oracle Getting Started - Lesson: A Closer Look at the "Hello World!" Application (The Java™ Tutorials > Getting Started)