Java Tutorial for Python Programmers

This tutorial is designed to help people who have had a semester of Python programming learn to do the same in Java. It is not designed for people who have no coding experience.

Java Files and Classes

Unlike Python, Java can't have a file with just a single print statement. Instead, each file must contain a class with the same name as the file. For example, the following could be the code inside CodeToRun.java:

public class CodeToRun {
    
    //code inside the class. (This is a comment.)
    
}
        

Java class names should always be given in PascalCase, just like in Python. Variable names, however, should be in camelCase instead of snake_case. The difference between camelCase and PascalCase is only that the first letter is always lower case in camelCase.

Running Java Code

Compiling and Running

Java is a compiled language instead of interpreted. That means that running Java code is a two-step process: first the code is compiled, which creates .class files. Then, those .class files are executed, running the actual code. To do this in the command line, type these two commands:

$ javac *.java
$ java CodeToRun
$ 

The first line compiles all the code in files that have the .java suffix. If there are any syntax errors, they will be reported and the compilation will fail. The second line executes the file CodeToRun.class. (The .class designation is left off when running Java.) Everytime you change the source code (don't forget to save it!) you need to recompile it to get the new executable .class files.

Let's introduce an error to see what a syntax error looks like: (I misspelled the word class in the first line)

public clas CodeToRun {
    
    //code inside the class. (This is a comment.)
    
}
        
$ javac *.java
CodeToRun.java:1: error: class, interface, or enum expected
public clas CodeToRun {
       ^
CodeToRun.java:5: error: class, interface, or enum expected
    }
    ^
2 errors
$ 

Java reported two errors. Important:Only worry about the first one! In many cases, fixing the first error will solve many of the later ones. In this case, Java found an syntax error on the first line, so it ignored that line. That causes the last line to also have a problem, because Java detects what looks like an extra close-squiggly brace. Notice that the compiler points out the first character of the symbol (clas in the first error) where it notices each error.

Add the second 's' back, recompile and run the code to make sure everything is working again before continuing.

Java main Methods

Nothing is output here because there's nothing to run inside CodeToRun.java. When a .class file is executed, Java automatically calls the main method inside that class. Without the main method, nothing happens. Let's add a main method now:

public class CodeToRun {
    
    public static void main(String[] args) {
    
        //code will go in here (this is a comment)
        
    }
}
        

Compile and run this code again. Nothing still happens, since there's nothing to execute inside the main method. Next we'll add some code that will actually execute!

Java Printing

In Java, a print statement looks like the following:

public class CodeToRun {
    
    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        
    }
}
        

This tells Java to print a string to the standard output: System.out. Let's see what happens when we run this in the terminal:

$ javac *.java
$ java CodeToRun
Hello, World!

Change the print statement to concatenate two strings:

        System.out.println("Hello, World!" + "  I like bananas!");
        

Run it again:

$ javac *.java
$ java CodeToRun
Hello, World!  I like bananas!

Java Statements

Java statements (non-compound statements, anyways) all end in a semicolon. Python uses whitespace (line breaks) as a delimiter between statements, but Java doesn't, so semicolons are necessary. If I wanted to assign the variable simian to the string value "monkey", that statement looks exactly the same as it does in Python, except with the semicolon added:

        simian = "monkey";
        

Comments

Unlike Python, Java has two different types of comments: inline (similar to Python comments) and block comments.

Inline Comments

Java uses // to indicate the start of an inline comment, which works exactly the same as # in Python. Everything after the two slashes is ignored by Java:

        age = 5; //age of the gorilla in years
        

Block Comments

Sometimes it's convenient to have a comment that spans more than one line. Instead of adding // to the beginning of each line, add /* before the first line and */ after the end of the last line:

        /* The next five lines sort all the apes, then either:
           - removes the biggest ape if there are more than seven, or
           - adds two apes if there are enough bananas. */
        

Block comments can be very useful to temporarily "comment out" code that you are considering deleting. They can also be used to temporarily comment out a piece of a line of code:

        age = 2 * otherAge /* + 1 */; //TODO: need to offset by 1?
        

Java Types and Variables

Unlike Python, Java variables must have their type declared before an assignment can be made. Try changing your main method to add the line from above: (I also removed the part about bananas.)

public class CodeToRun {
    
    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        simian = "monkey";
        
    }
}
        

Now try compiling and running your code. You should see a message something like the following:

$ javac *.java
CodeToRun.java:6: error: cannot find symbol
        simian = "monkey";
        ^
    symbol:   variable simian
    location: class CodeToRun
1 error

In order to turn this into working Java code, we have to tell the program what type the variable simian will have, called declaring the variable. The following is a modified version of the body of the main method, with a declaration statement added:

        System.out.println("Hello, World!");
        String simian;  //declaration for simian
        simian = "monkey";  //assignment to simian
        

This code will compile and run happily:

$ javac *.java
$ java CodeToRun
Hello, World! 

We can also combine those two into a single statement, known as an initialization:

        System.out.println("Hello, World!");
        String simian = "monkey";  //initialization for simian
        

Initializations are more common than sole declarations, but there are some times where you might want to declare a variable without giving it a value.

We can use the variable as part of a print statement:

        System.out.println("Hello, World!");
        String simian = "monkey";  //initialization for simian
        System.out.println("simian: " + simian);
        
$ javac *.java
$ java CodeToRun
Hello, World!
simian: monkey

Other common types are int, boolean, float, and double. These last two are similar to Python's float type, except that double keeps track of twice as many significant digits. These are all primitive types in Java, meaning that they are not stored as objects and thus have no associated methods. (Primitive types are all specified in camelCase, while object types are all in PascalCase.) We can use all types in print statements:

        System.out.println("Hello, World!");
        String simian = "monkey";  //initialization for simian
        System.out.println("simian: " + simian);
        int maxCompiles = 200;
        System.out.println("I've compiled this code less than " + maxCompiles + " times.");
        double confidence = 92.3;
        System.out.println("I'm " + confidence + "% confident that I'll enjoy programming in Java.");
        
$ javac *.java
$ java CodeToRun
Hello, World!
simian: monkey
I've compiled this code less than 200 times.
I'm 92.3% confident that I'll enjoy programming in Java.
        

In Python, we'd have to use the str() method to concatenate strings with other types. Java automatically converts non-string values to strings when they are needed, for example in concatenation expressions or as the argument to System.out.println. When the value is non-primitive and not a string, Java calls the toString method.

It's important to know that a variable can't be assigned to values with non-matching types. Try adding this line to the end of your main method's body:

        maxCompiles = "1000";
        

Now try to compile your code:

$ javac *.java
CodeToRun.java:12: error: incompatible types
        maxCompiles = "1000";
                        ^
    required: int
    found:    String
1 error

It won't even work if you try to redeclare the variable's type. Once a variable has been declared to have a certain type, that type cannot be changed:

        String maxCompiles = "1000";
        
$ javac *.java
CodeToRun.java:12: error: variable maxCompiles 
is already defined in method main(String[])
        String maxCompiles = "1000";
                ^
1 error

This happens because the Java compiler reserves different amount of memory for different types, and wants to know ahead of time what that will look like. Most compilers for modern languages make optimization decisions based on this kind of info. None of this is to say that the variable can't be reused! Reassignment to another int works fine:

        maxCompiles = 250;
        

But if a non-integer value is needed, another variable will have to be used.

Conditionals

if, else, and elif become if, else, and else if, respectively. The most basic of these is if. Let's write some code to test the parity of an integer: (The percent sign is the modulus operator, just as in Python.)

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int x = 5;
        if (x % 2 == 0) {
            System.out.println("x is even.");
        } else {
            System.out.println("x is odd.");
        }
        
    }
        

Try running this, then change x to an even number and run it again. Notice that squiggly braces are used to delimit the different branches of the conditional, instead of relying on indentation levels. Notice also that the condition must be in parentheses, unlike Python. If you don't include those parentheses, you'll get some syntax errors:

$ javac *.java
CodeToRun.java:7: error: '(' expected
        if x % 2 == 0 {
          ^
CodeToRun.java:7: error: ')' expected
        if x % 2 == 0 {
                     ^
2 errors

Let's do another conditional, except that we'll test the sign of the integer:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int x = 5;
        if (x < 0) {
            System.out.println("x is negative.");
        } else {
            System.out.println("x is positive.");
        }
        
    }
        

If you try running this with different values, you might notice that you'll be lied to! Especially if you set x to zero. (Zero is neither positive nor negative.) Let's change the conditional to reflect this change by chaining the branches:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int x = 5;
        if (x < 0) {
            System.out.println("x is negative.");
        } else if (x > 0) {
            System.out.println("x is positive.");
        } else {
            System.out.println("x is zero.");
        }
        
    }
        

Java does not bother to shorten else if into elif. Other than that, the functionality of chained conditionals works the same. We can have as many branches as we want, and it's not necessary to have a final else branch.

Static Java Methods

Java doesn't actually have plain functions: all functions must belong to a class, and thus must be methods. Java does this in an effort to enforce object-oriented programming practice. Luckily, this doesn't mean that all methods must have a subject. Methods specified as static can be called on the class itself instead of instances of the class.

Types are important in Java methods as well as variables. When you create a new method, you have to declare the return type. Here's an example of a static method that returns an integer:

    public static int getTwenty() {
        return 20;
    }
        

The parts of the method signature are important:

Notice that the body of the method is indented with respect to the method header, just like in Python. After the body, the close-squiggly brace (indented back with the header line) ends the method. The compiler doesn't actually care whether the body is indented, but other programmers will, so keep up the good indenting practice!

Usually the main method is the last method in a class, so let's add the above method to our code like the following:

public class CodeToRun {

    public static int getTwenty() {
        return 20;
    }
    
    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        
    }
}
        

That code should compile happily. Let's change the main method to invoke that method:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        System.out.println("Should be 30: " + (10 + getTwenty()) );
        
    }
        

Let's write a method that has a parameter. Just like all variables, parameters need to be typed. This method adds five to an integer:

    public static int addFiveTo(int x) {
        return x + 5;
    }
        

addFive can be invoked just like you'd expect:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        System.out.println("Should be 30: " + (10 + getTwenty()) );
        int thirteen = addFiveTo(8);
        System.out.println("Should be 13: " + thirteen);
        
    }
        

Part of the reason Java is a stickler about types is that it prevents some semantic errors and exceptions by enforcing that all the types work out at compile time. Try changing your code by adding another line to our main method:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        System.out.println("Should be 30: " + (10 + getTwenty()) );
        int thirteen = addFiveTo(8);
        System.out.println("Should be 13: " + thirteen);
        int broken = addFiveTo("A monkey did it!");
        System.out.println("broken: " + broken);
        
    }
        

Attempting to compile this gives us an error:

$ javac *.java
CodeToRun.java:18: error: method addFiveTo in class CodeToRun cannot be applied to given types;
        int broken = addFiveTo("A monkey did it!");
                     ^
  required: int
  found: String
  reason: actual argument String cannot be converted to int by method invocation conversion
1 error

Notice that the compilation error tells us exactly what's going on: the method requires an int, but a String is given there instead.

If we really wanted to define a version of addFiveTo that takes a string, we can! We can have two different versions of the method with the same name, so long as the signatures are different some other way. Here, we'll give them different parameter types (and return types). Add this method to your code:

    public static String addFiveTo(String string) {
        return string + " five";
    }
        

Compile and run your method again. You should see:

$ javac *.java
$ java CodeToRun
Hello, World!
Should be 30: 30
Should be 13: 13
broken: A monkey did it! five
        

Having methods with the same name with different types of parameters is known as method overloading. Since Python is a "weakly typed" language (as opposed to Java, "strongly typed") it doesn't support overloading.

At compile time, Java chooses the method with the appropriate signature. (This does not prevent dynamic method invocation between subclasses. We'll show examples of that later.)

Let's write a method that takes two integer parameters:

    public static int addInts(int x, int y) {
        return x + y;
    }
        

Python allows users to multiply integers with strings, like this:

            x = 3 * "monkey"
        

Let's write a method to do it instead! This method takes two parameters of different types (and is also an example of recursion):

    public static String repeatString(String string, int amount) {
        if (amount <=0) {
            return "";
        } else {
            return string + repeatString(string, amount - 1);
        }
    }
        

We can test it out in our main method:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        System.out.println("Three monkeys: " + repeatString("monkey", 3));
        
        System.out.println("Should be 30: " + (10 + getTwenty()) );
        int thirteen = addFiveTo(8);
        System.out.println("Should be 13: " + thirteen);
        int broken = addFiveTo("A monkey did it!");
        System.out.println("broken: " + broken);
    }
        

Compile and run that to make sure everything works.

Java Strings

Python has lots of notation that can be used directly on strings, that Java does via methods. The exception to this is concatenation, which we've already seen (and which Java does better by automatically applying toString to non-strings). I'll cover the other basics here. For a complete reference for Java string methods, check out the Java 7 String API.

String.length

len("monkey") becomes "monkey".length(). There is no magical len function that returns the length of any iterable object. Instead, we use the length method for strings. Here's an example in an updated main method: (the \" is the escaped double-quotes character)

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String simian = "monkey";
        System.out.println("\"monkey\" has " + simian.length() + 
                           " characters.");
    }
        

Compile and run this to make sure you've got it working.

String.charAt

"monkey"[3] becomes "monkey".charAt(3). We can use square brackets with Java arrays, but not strings. (Sorry!) To get a specific character, use the charAt method. Here's an example:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String simian = "monkey";
        System.out.println("The foureth character of \"monkey\" is " + 
                           simian.charAt(4) + ".");
    }
        

Compile and run this code.

String.substring

"monkeybingo"[3:8] becomes "monkeybingo".substring(3, 8). Slices don't work at all in Java. For strings, we use the substring method instead. As always, here's an example of the method in action:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String food = "pearstrawberry";
        System.out.println("There are " + food.substring(1, 5) + 
                           " in my " + food + "!");
    }
        

If you compile and run this, it should say: There are ears in my pearstrawberry!

No negative indices.

Java's indices run from 0 to the length of the string (or list) minus 1. Try running the following code to see what happens if the index is out of these bounds:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String food = "pearstrawberry";
        System.out.println(food.charAt(-3));
    }
        

Running this leads to the following:

$ javac *.java
$ java CodeToRun
Hello, World!
Exception in thread "main" java.lang.StringIndexOutOfBoundsException:
String index out of range: -3
	at java.lang.String.charAt(String.java:646)
	at CodeToRun.main(CodeToRun.java:16)
        

String.indexOf

"monkey".find("n") becomes "monkey".indexOf("n"). The Java method is overloaded and works both with strings and characters as parameters. Check out the following code as an example:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String band = "bananarama";
        System.out.println("Should be 4: " + band.find("nar"));
        System.out.println("Should be 6: " + band.find('r'));
        System.out.println("Should be -1: " + band.find("x") + 
                           "    -1 means it doesn't contain the parameter.");
    }
        

String.contains

"key" in "monkey" becomes "monkey".contains("key"). This returns a boolean exactly as you would expect. Try out this code:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String band = "bananarama";
        System.out.println("Has \"banana\": " + band.contains("banana"));
        System.out.println("Has \"apple\": " + band.contains("apple"));
    }
        

String.equals

"monkey" == animal becomes "monkey".equals(animal). In Java, a String is a type of object. You can still use ==, but it won't do what you expect. Instead of comparing the values in the expression x == y, Java just tests whether the two object variables point to the same memory address. While this will always work for primitive values (ints, booleans, floats, doubles) it won't work for two objects that have the same value but aren't stored in the same place. Compile and run this code:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String band = "bananarama";
        String sameBand = band; //band and sameBand are stored in the same place.
        System.out.println("These two are equal; the test works:" + (band == sameBand));
        System.out.println("These two are equal; better test:" + (band.equals(sameBand)));
        String sameBandAgain = new String("bananarama"); //stored in a new place
        System.out.println("These two are equal; test fails:" + (band == sameBandAgain));
        System.out.println("These two are equal; test works:" + (band.equals(sameBandAgain)));
    }
        

It's very common for beginner Java programmers to use == to test the equality of non-primitives. This isn't an issue in Python, because == automatically calls the special __cmp__ method that should test for equality. If something is going wrong, take a look at whether you're using the equals method less often than you should be.

Traversal with Loops

You are probably familiar with two types of Python loops: while and for. Java has both, but two types of for loops, for and for-each.

while loops

Just like conditionals, methods, and all other compound statements, Java while loops use squiggly braces. Otherwise, they look very similar to Python while loops:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String band = "garbage";
        int index = 0;
        while (index < band.length()) {
            System.out.println(band.charAt(index));
            index++;
        }
    }
        
$ javac *.java
$ java CodeToRun
Hello, World!
g
a
r
b
a
g
e
        

for loops

We can simplify the above code using a for loop. This looks less like a Python for loop, and more like a modified while loop. Basically, instead of just putting the condition inside the parentheses, there are three full statements inside. Here's an example of the above code rewritten with for:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        String band = "garbage";
        for (int index = 0; index < band.length(); index++) {
            System.out.println(band.charAt(index));
        }
    }
        

Note the three parts inside the parentheses, separated by semicolons. The first part is the instantiation and is executed only once as the very first part of the loop. The second part is the condition we're familiar with: after each iteration, the loop only continues if the condition is true. The third part is the update; this line gets executed after each iteration of the loop finishes. If you ever have a while loop that looks like this: (A, B, X, Z, and C are some statements or expressions.)

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        A;
        while (B) {
            X;
            ...
            Z;
            C;
        }
    }
        

...then you can rewrite the loop as:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        for (A; B; C) {
            X;
            ...
            Z;
        }
    }
        

In Python, you can use a for loop to traverse anything that's iterable, such as strings and lists. The Java version of this is a for-each loop, except that some things in Java aren't iterable. (Strings are an example of this!) We'll see examples of Java for-each loops after we talk about arrays.

Arrays

Arrays are Java's version of lists, and unfortunately they are a bit more difficult to deal with. First, the Java compiler likes to do a bunch of space optimization, so it needs to know how big the value in each variable is going to be during its lifetime. Thus, when you create an array, you have to specify both the type elements are going have as well as the number of cells. The most common way to do this is without specifying the values of the elements:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int[] odds = new int[5];
    }
        

odds now has type int[], meaning it will be an array of int values. The array has five cells, so it will be able to hold five values. We can initialize those values by adding some assignment statements. These look almost exactly the same as in Python:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int[] odds = new int[5];
        odds[0] = 1;
        odds[1] = 3;
        odds[2] = 5;
        odds[3] = 7;
        odds[4] = 9;
        System.out.println("odds: " + odds);
    }
        

Note that just like Python, Java indexes starting from 0. If we wanted to do this all in one line, we can! To do this, we still have to use the keyword new, but drop the size argument and add the elements in squiggly braces:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int[] odds = new int[] {1, 3, 5, 7, 9};
        System.out.println("odds: " + odds);
    }
        

Let's try running that code:

$ javac *.java
$ java CodeToRun
Hello, World!
odds: [I@7852e922
        

What? That doesn't look like what we want because there is no toString method for arrays. (Arrays are some sort of not-quite a class in Java. Like Python, they have fields. Unlike Python, there are no array methods!) The weird things printed out correspond to the address the array is stored at. We can access single elements easily, though:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int[] odds = new int[] {1, 3, 5, 7, 9};
        System.out.println("odds: " + odds);
        System.out.println("odds[2] (should be 5): " + odds[2]);
    }
        

In order to nicely print out the entire array, we need to write a loop. Our for loop to traverse the array will look a lot like the one to traverse a string, except that we can't use the length() method. Instead, we have to use the length field:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int[] odds = new int[] {1, 3, 5, 7, 9};
        System.out.println("[");
        for (int i = 0; i < odds.length; i++) {
            System.out.print(odds[i] + ", ");
        }
        System.out.println("]");
    }
        

Running this gives us:

$ javac *.java
$ java CodeToRun
Hello, World!
odds: [1, 3, 5, 7, 9, ]
        

Removing that extra comma can be attained by adding a conditional inside the loop:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int[] odds = new int[] {1, 3, 5, 7, 9};
        System.out.println("[");
        for (int i = 0; i < odds.length; i++) {
            System.out.print(odds[i]);
            if (i < odds.length - 1) {
                System.out.print(", ");
            }
        }
        System.out.println("]");
    }
        

Unfortunately, there is no way to change the size of an array once it has been created. In order to do something like Python's odds.append(11), you have to create a whole new array:

    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        int[] odds = new int[] {1, 3, 5, 7, 9};
        
        //temporary variable for the new array
        int[] moreOdds = new int[odds.length + 1];
        
        //copy the old elements over to moreOdds
        for (int i = 0; i < odds.length; i ++) {
            moreOdds[i] = odds[i];
        }
        //add the new element to the end of moreOdds
        moreOdds[moreOdds.length - 1] = 11;
        
        //reassign the odds variable to the new array
        odds = moreOdds;
    }
        

ArrayLists (More like Python lists)

Working with arrays can be a bit frustrating after the freedom of Python lists, but it's important to be aware of how they work.  If you want all the functionality of Python lists, you instead want to use an ArrayList in Java.  Here's an example of an ArrayList in action:
    public static void main(String[] args) {
        
        System.out.println("Hello, World!");
        ArrayList list = new ArrayList();
        list.add(5);
        list.add("Hello there!");
        list.add(6 == 4);
        System.out.println(list);
                    
    }
        

Compile this code. You'll see a new warning message:

$ javac *.java
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$
        

This is not an error: the code has compiled successfully. The compiler, however, is extremely concerned with type safety, and it's letting you know that some of the operations you've written could cause an exception due to the types of values added to list. There is a way to eliminate this error, which we'll see soon, though it will also restrict the types of values we can add to the list.

For now, let's enjoy the benefits of ArrayLists:

Let's go more closely over the different operations. At some point you might want to look over the ArrayList API.

The ArrayList constructor

        ArrayList fishMonkey = new ArrayList();
        

That's it. When you put the keyword new before a classname followed by parentheses (and possibly arguments), then you're calling a constructor, which creates a new instance of that class. This is just like Python, except that you need the new keyword. Python allows you to be a bit more flexible in creating lists in one line, so this may take some getting used to.

ArrayList.add

Python's append method becomes the add method. We saw an example of this already. Let's keep moving!

ArrayList.set

some_list[5] = "beluga whale" becomes someList.set(5, "beluga whale");

ArrayList.get

mighty_mouse = some_list[3] becomes Object mightyMouse = someList.get(3); Here's some code extending on the previous example that uses get and set:

        ArrayList list = new ArrayList();
        list.add(5);
        list.add("Hello there!");
        list.add(6 == 4);
        System.out.println("list: " + list);
        Object number = list.get(0);
        list.set(0, number + 3);
        System.out.println("list: " + list); 
        

Unfortunately, this doesn't compile successfully:

$ javac *.java
CodeToRun.java:13: error: bad operand types for binary operator '+'
        list.set(0, number + 3);
                           ^
  first type:  Object
  second type: int
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
$
        

Java doesn't know how to use the + operator between an object and an integer. There are really two things at play here:

What can we do about this? Well, we can create a new variable with the desired type and type cast the value:

        ArrayList list = new ArrayList();
        list.add(5);
        list.add("Hello there!");
        list.add(6 == 4);
        System.out.println("list: " + list);
        Object number = list.get(0);
        Integer x = (Integer) number;
        list.set(0, x + 3);
        System.out.println("list: " + list); 
        

Try to compile and run this:

$ javac *.java
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
cspc05:web kgb1013$ java CodeToRun
Hello, World!
list: [5, Hello there!, false]
list: [8, Hello there!, false]
$
        

It works! The expression (Integer) number performs the type cast. This does not change the type of the variable number! number is still has type Object. We can forego creating the new x variable and do the typecast inside the argument expression for set:

        ArrayList list = new ArrayList();
        list.add(5);
        list.add("Hello there!");
        list.add(6 == 4);
        System.out.println("list: " + list);
        Object number = list.get(0);
        list.set(0, ((Integer) number) + 3);
        System.out.println("list: " + list); 
        

Notice that the parentheses are needed around the new type.

What do you think will happen if we change the zeroeth element to be a string?

        ArrayList list = new ArrayList();
        list.add("bonanza");
        list.add("Hello there!");
        list.add(6 == 4);
        System.out.println("list: " + list);
        Object number = list.get(0);
        list.set(0, ((Integer) number) + 3);
        System.out.println("list: " + list); 
        

We get a ClassCastException:

$ java CodeToRun
Hello, World!
list: [bonanza, Hello there!, false]
Exception in thread "main" java.lang.ClassCastException: 
  java.lang.String cannot be cast to java.lang.Integer
	at CodeToRun.main(CodeToRun.java:13)
$
	    

"bonanza" couldn't be cast into an Integer because Java doesn't know how to do that conversion. Generally you want to do as little typecasting as possible, in order to avoid these errors. Soon we'll see how to use get without needing to any casting.

ArrayList.size

len(some_list) becomes someList.size(). We can use this in a for loop to print out the elements of a list on different lines.

        ArrayList list = new ArrayList();
        list.add("bonanza");
        list.add("Hello there!");
        list.add(6 == 4);
        System.out.println("list: " + list);
        for (int i = 0; i < list.size(); i++) {
            System.out.println("list[" + i + "]: " + list.get(i));
        }
        

Other ArrayList operations

There are lots of other ArrayList methods. Go check out the ArrayList API.

Objects and Classes

Let's create our own classes now. Python lets you get away with a lot of things here that you can't do in Java, but it's not terribly different. Some important things to keep in mind:

Creating a New Class

Let's create a new class to represent monkeys. Our class will be named Monkey, so we need to create a new text file, Monkey.java. Create that file, and add the following code to it:

public class Monkey {
        
} //end of class Monkey        
        

We can compile this to make sure it works:

$ javac *.java
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$
	    

Fields

Right now our object has no fields, so it's not representing much. Let's add some fields for basic stats about each Monkey object.

public class Monkey {

    //age of this monkey, in years
    public int age;
    
    //height of the monkey, from feet to head, in meters
    public double height;
    
    //the monkey's name
    public String name;
    
} //end of class Monkey
        

Constructors

In Python, the __init__ method is the constructor, which initializes all the fields. In Java, the syntax for this is quite a bit different. It still looks like a method, except the return type isn't specified and the "name" is replaced by the class name (in PascalCase). Here's the class with the constructor added:

public class Monkey {

    //age of this monkey, in years
    public int age;
    
    //height of the monkey, from feet to head, in meters
    public double height;
    
    //the monkey's name
    public String name;
    
    //constructor
    public Monkey(int age, double height, String name) {
        this.age = age;
        this.height = height;
        this.name = name;
    }
    
} //end of class Monkey
        

Notice that this is the reference to the subject instead of Python's self. Since Java has only methods and no non-method functions, this identifier isn't listed as the first parameter.

Create an instance of this class in the main method in CodeToRun.java:

    public static void main(String[] args) {
        Monkey monkey = new Monkey(3, .65, "Banano");
        System.out.println("Banano's age: " + monkey.age);
        System.out.println("Banano's height: " + monkey.height);
        System.out.println("Banano's name: " + monkey.name);
    }
        

toString

__repr__ and __str__ become toString. Java uses toString as the default method for displaying objects, so you definitely want to write that next. This method should always take no parameters and return a string. Here I've added toString to Monkey.java:

    //constructor
    public Monkey(int age, double height, String name) {
        this.age = age;
        this.height = height;
        this.name = name;
    }
    
    //toString
    public String toString() {
        String string = "";
        string += "Monkey named " + this.name + ":\n";
        string += "  age: " + this.age + " years\n";
        string += "  height: " + this.height + " m";
    }
        

Now we can test this by adding a line to our main method in CodeToRun:

    public static void main(String[] args) {
        Monkey monkey = new Monkey(3, .65, "Banano");
        System.out.println("Banano's age: " + monkey.age);
        System.out.println("Banano's height: " + monkey.height);
        System.out.println("Banano's name: " + monkey.name);
        System.out.println("monkey: \n" + monkey.toString());
    }
        

Now run that code:

$ javac *.java
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ java CodeToRun
Banano's age: 3
Banano's height: .65
Banano's name: Banano
monkey: 
Monkey named Banano:
  age: 3 years
  height: .65 m
$
        

Recall that Java automatically calls toString in most cases where it's looking for a string, so we can remove the .toString() in the last line:

    public static void main(String[] args) {
        Monkey monkey = new Monkey(3, .65, "Banano");
        System.out.println("Banano's age: " + monkey.age);
        System.out.println("Banano's height: " + monkey.height);
        System.out.println("Banano's name: " + monkey.name);
        System.out.println("monkey: \n" + monkey);
        return string;
    }
        

The output will be the same as before.

Access Modifiers

Unfortunately, we can do the following thing:

    public static void main(String[] args) {
        Monkey monkey = new Monkey(3, .65, "Banano");
        System.out.println("Banano's age: " + monkey.age);
        System.out.println("Banano's height: " + monkey.height);
        System.out.println("Banano's name: " + monkey.name);
        monkey.age = -17;
        System.out.println("monkey: \n" + monkey);
    }
        

-17 doesn't make a lot of sense for the age. How can we fix this? We can change the constructor so that negative ages don't work:

    //constructor
    public Monkey(int age, double height, String name) {
        this.age = age;
        if (this.age < 0) {
            //an illegal age was given, so set the age to zero.
            // (should be improved by throwing an exception instead)
            this.age = 0; 
        }
        this.height = height;
        this.name = name;
    }
        

This won't solve the entire problem, though. We want to make it so that other code doesn't have direct access to the fields of any Monkey object. We can do this by changing the declarations to specify that these fields are private instead of public. Modify your Monkey class to:

public class Monkey {

    //age of this monkey, in years
    private int age;
    
    //height of the monkey, from feet to head, in meters
    private double height;
    
    //the monkey's name
    private String name;
        

Try compiling your code again. You'll see these errors:

$ javac *.java
CodeToRun.java:6: error: age has private access in Monkey
        System.out.println("Banano's age: " + monkey.age);
                                                    ^
CodeToRun.java:7: error: height has private access in Monkey
        System.out.println("Banano's height: " + monkey.height);
                                                       ^
CodeToRun.java:8: error: name has private access in Monkey
        System.out.println("Banano's name: " + monkey.name);
                                                     ^
3 errors
$
        

Any member (field or a method) of a class has an access modifier, which can be set to one of four things:

There are some general rules to follow when choosing access modifiers, in order to best protect your code:

Getters

We might still want some access to the fields. For example, it seems reasonable to get a hold of the age of a monkey. We can do that by adding another method to Monkey.java, a "getter":

    public int getAge() {
        return this.age;
    }
        

Then, in CodeToRun.java, we can use this in the main method:

    public static void main(String[] args) {
        Monkey monkey = new Monkey(3, .65, "Banano");
        int yearsOld = monkey.getAge();
        System.out.println("Banano's age: " + yearsOld);
        yearsOld = 15;
        System.out.println("yearsOld: " + yearsOld);
        System.out.println("Banano's age: " + monkey.getAge());
    }
        

Running this, we see:

$ javac *.java
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ java CodeToRun
Banano's age: 3
yearsOld: 15
Banano's age: 3
$
        

Notice that we get access to the value of the age, but can't change the actual value in the object.

Setters

Sometimes we still want to be able to change the value, though maybe to a limited extent. We can do this by adding a "setter":

    public void setAge(int age) {
        if (age >= 0) {
            //change the age if positive
            this.age = age;
        }
    }
        

Notice that this allows us to change the value, but still protects against rogue code trying to assign it a negative number.

Subclasses and Inheritance

Declaring a subclass

Inheritance is a useful tool in Java just as in Python. It's very easy to create a subclass. First create the following Mammal.java class:

public class Mammal {

    //age in years
    private int age;
    
    public Mammal(int age) {
        this.age = age;
        if (this.age < 0) {
            //avoid negative ages!
            this.age = 0;
        }
    }
    
    public String toString() {
        return "A " + age + " year old mammal with a four-chamber heart.";
    }
    
} //end of Mammal.java
        

Now, create a new file, Mouse.java. We can declare this as a subclass by using the following class signature:

public class Mouse extends Mammal {

} //end of Mouse.java
        

Inheritance

Since the Mouse class extends Mammal, it inherits all of Mammal's members. That means it already has the age field (even though it's private). Let's add a constructor to Mouse.java and set the age field in there. We want a constructor with signature

    public Mouse(int age) {
        //body of the constructor goes here
    }
        

There are three common ways to do this, from worst to best:

  1. Change the age field in Mammal to be protected:

        protected int age;
                    

    Now the Monkey constructor can be written just like the one in Mammal.

  2. Leave the age private, and add a setAge method to Mammal.java (not Monkey.java) similar to the one we did for Monkey:

        public void setAge(int age) {
            if (age >= 0) {
                //change the age if positive
                this.age = age;
            }
        }
                    

    Now the Monkey constructor can invoke the setAge method:

        public Mouse(int age) {
            this.setAge(age);
        }
                    
  3. Invoke the superclass's constructor. It is common for constructors of subclasses to call the constructor in the super class. This must be done in the first line:

        public Mouse(int age) {
            super(age);
        }
                    

    This last version is considered the best because it avoids adding extra code to Mammal.java for Monkey.java's sake. Sometimes you only have access the the .class file for the superclass, not the source code (the .java), thus you can't modify the superclass implementation.

For all three solutions, the code can be tested by adding the following to CodeToRun.java.

    public static void main(String[] args) {
        Mouse mouse = new Mouse(2);
        System.out.println("mouse: " + mouse);
    }
        

Since Mammal has implemented toString, that will be invoked, giving us the following output when tested:

$ javac *.java
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ java CodeToRun
mouse: A 2 year old mammal with a four-chamber heart.
$
        

Overriding Methods

Just like in Python, we might decide that a superclass's method is inappropriate for the subclass. We can solve this problem by overriding the method, implementing the desired method body inside the subclass. Add a toString method to Mouse.java:

    
    public String toString() {
        return "A tiny mouse that is " + age + " years old.";
    }
        

Test this code out and notice that the subclass's method is invoked.

Declared Types

We can declare variables to have types of any supertype. Add the following to CodeToRun.java:

    public static void main(String[] args) {
        Mouse mouse = new Mouse(2);
        Mammal mammal = new Mouse(3);
        Object object = new Mouse(4);
    }
        

Object is automatically a superclass of all other classes. The above code compiles happily because the values stored in the three variables all have the correct types: any value is of it's own type and all supertypes. Thus, each Mouse is also a Mammal and an Object.

The reverse is not true, however. The following code does not compile:

    public static void main(String[] args) {
        Mouse mouse = new Mammal(5);
    }
        

Polymorphism

All object-oriented languages must implement polymorphism: where objects act as their own type, not their declared type. Let's go back to the main code with three mice of different declared types, then add three print statements that will invoke toString methods:

    public static void main(String[] args) {
        Mouse mouse = new Mouse(2);
        Mammal mammal = new Mouse(3);
        Object object = new Mouse(4);
        System.out.println("mouse: " + mouse);
        System.out.println("mammal: " + mammal);
        System.out.println("object: " + object);
    }
        

Which version of toString will be invoked in each line? If the declared types' (Mouse, Mammal, then Object) versions are called, then we'll see three different types of messages. Is that what happens? Try it out!

$ javac *.java
Note: CodeToRun.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ java CodeToRun
mouse: A tiny mouse that is 2 years old.
mammal: A tiny mouse that is 3 years old.
object: A tiny mouse that is 4 years old.
$
        

The compiler only knows that mammal is a Mammal, not that it's a Mouse. The same is true for object. At run-time, though, Java takes the time to determine the actual type of the value in the variable to choose which method to invoke. (This is called dynamic method invocation.) Choosing the most specific method at the time of invocation is vital to polymorphism.

Abstract classes and Interfaces

Abstract classes and interfaces are just slightly beyond the scope of this tutorial. I'll add a link you can check out later.

Generic Types

Type-casting is both annoying and dangerous, but it has to be done when pulling elements out of an ArrayList. Or does it? In version 1.5, Java introduced type placeholders, known as generics.

Using Generic Types

Instead of defining an ArrayList the normal way, ...

    public static void main(String[] args) {
        ArrayList names = new ArrayList();
        names.add("Bahram");
        names.add("Anika");
        names.add("Hugo");
        String firstName = (String) names.get(0);
        System.out.println("Is the first name Bahram? " + firstName.equals("Bahrom"));
    }
        

... we can specify a generic type for the ArrayList:

    public static void main(String[] args) {
        ArrayList names = new ArrayList();
        names.add("Bahram");
        names.add("Anika");
        names.add("Hugo");
        String firstName = names.get(0);
        System.out.println("Is the first name Bahrom? " + firstName.equals("Bahrom"));
    }
        

Here's what changed:

Implementing Classes to use Generics

On the other hand, you might want to create a class that uses generics. Although we can't see the source code for the ArrayList class, here's what the signature probably looks like:

public class ArrayList {
        

ElementType is the name of the placeholder that will be used throughout the code. If we wanted to only allow certain types of objects, then we could change the type the placeholder extends. (Notice the name of the placeholder is different as well as the super type in this example.)

public class ZooExhibit {
        

Back to ArrayList! In the method definitions, the placeholder is used to refer to the type. This is what I expect the signature for ArrayList.get looks like:

    public ElementType get(int index) {
        

Javadoc

In Python, auto-generating documentation can be created from docstrings embedded in the code. Java has a similar system using something called Javadoc. Instead of strings, Javadoc is added using specially-formatted block comments. These comments are sometimes called Javadoc headers and can replace normal comment headers placed above of public member definitions.

Class documentation

Classes are documented by adding a block comment above the class signature. The comment must begin with /** and each line should begin with an *. Here's an example for Monkey.java:

/**
 * Represents a monkey simian.
 *
 * @author Leif Reddington
 */
public class Monkey extends Mammal {
        

The @author part is called a tag. Each tag takes different "arguments". This one only takes one, but if more are used, they are separated only by spaces.

Generating the Documentation

Like with Pydoc, Javadoc can generate documentation webpages via the command line. Let's try it out!

$ javadoc Monkey.java
Loading source file Monkey.java...
Constructing Javadoc information...
Standard Doclet version 1.7.0_79
Building tree for all the packages and classes...
Generating /Monkey.html...
Generating /package-frame.html...
Generating /package-summary.html...
Generating /package-tree.html...
Generating /constant-values.html...
Building index for all the packages and classes...
Generating /overview-tree.html...
Generating /index-all.html...
Generating /deprecated-list.html...
Building index for all classes...
Generating /allclasses-frame.html...
Generating /allclasses-noframe.html...
Generating /index.html...
Generating /help-doc.html...
$ ls *.html
Monkey.html
$
        

Check out the .html file in a web browser and you'll see a nice API page with your description for the class.

Constant Documentation

Public constants should also enjoy a Javadoc header comment:

    /**
     * The number of ounces in one pound.
     */
    public static final int OUNCES_PER_POUND = 16;
        

Method Documentation

Only public members should be Javadoc'ed. Methods each deserve a Javadoc comment with tags for each parameter and a tag for the return value (if it's fruitful). Here's what the Javadoc for Mammal's setAge method might look like:

    /**
     * Changes the age of this Mammal.
     *
     * @param age  The new age of this mammal.  If this is less than zero, then the age will be set to zero instead.
     */
    public void setAge(int age) {
        

Notice that the name of the parameter is included as an argment to the tag. If you don't specify parameter name correctly, Javadoc will notice and will report an error. Here's an example with a return value:

    /**
     * Returns the age of this mammal.
     *
     * @return  The new age of this mammal, in years.
     */
    public int getAge() {
        

Public constructors need Javadoc too:

    /**
     * Creates a new Monkey.
     *
     * @param age  The age of the monkey, in years.  If a negative argument is provided, this is set to zero instead.
     * @param height  The height of the monkey, in meters.
     * @param name  The name of the monkey.
     */
    public Monkey(int age, double height, String name) {
        

Add more getters and setters to Monkey.java, then document them and compile the Javadoc to see your awesome documentation pages!

Keep Learning More Java!

Now you're ready to learn more advanced programming in Java. If you haven't already learned about abstract classes and interfaces, I recommend learning those next. You can also learn about anonymous and/or inner classes. Or if you haven't learned about data structures, maybe it's time to take that course next! Congratulations! Enjoy exploring Java!

Acknowledgements

No one has mentioned any issues with this yet, but when they do, I'll mention them here when I fix them. If you notice a problem, please email me about it!