Learn Java

In this free online tutorial you will learn step by step the basic features and capabilities of Java and how to use them.
Be sure you checked [Understand programming] and [Learn programming] sections.


Below we have a nice Editor that you can use to experiment as you learn. You can Copy&Paste the code, but never forget to tweak it and see what happens. This is one of the recipes to learn fast.
Also there is a link inside you can click to popup this Editor as a floating window.

Java editor : click to show up

[ Click to popup the console ]

Editing info

You can use [Ctrl+Enter] shortcut to run the code you’ve written.

Read the comments in the source code boxes, because they explain what is happening.

We will start with very basic even trivial examples, and then slowly towards more complex scenarios.

First we need to go trough a lot of dry stuff and mumbo jumbo before we start coding, but be patient that is the nature of Java, why make it easy if it can be hard 😉
Get used to it. You need to be persistent if you want to learn Java.

Java program

A Java program consist of Classes defined in files that use extension .java. Every Java :class can have a method called main() that will be executed whenever you run a compiled program (Understand programming). Yes in Java you use a compiler to compile the source code that you wrote in the .java file to a bytecode file, which can then be run on the Java virtual machine.

If you red Learn programming section which is non mandatory but nice to read ;), especially when we talk about Java, you will have an idea of what is a :class and a :method.

Hello World

I always start with the obligatory Hello world example. Here is how it looks in Java :

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

----
Output:

Hello World!

-----
//We can also compile it on the command line ..
> javac hello.java
//... and then run the compiled program with JVM..
> java hello
//, but you will most probably use IDE as Eclipse

There is a lot going on here. In what follows I will mention many concepts now but will explain them later in their respective sections.

The main thing happening is printing a String on the screen. A String is a sequence characters i.e. text surrounded by double quotes. In this case “Hello World!”

  • System: is a class defined in the java.lang package. (Packages are used to organize code)
  • out: is an instance of PrintStream, which is a public field of the System class.
  • println() is a :method that print its arguments plus new line at the end.

This code is surrounded by the method called main(), which is executed by default when you run the program.
And finally this method is surrounded by the class definition.

Now lets go in reverse.

Every .java file is a :class which contains :methods and :attributes.

  • Attributes are :variables, which contain values.
  • Methods are encapsulated piece of code that do stuff on behalf of the object

Let’s find out what those terms mean :

Values

Values aka Literals are the atoms of any programming language and includes things such as Numbers, Characters and :Strings.

Variables

Variables are called that because they can vary. In the same way a Box can hold objects a variable can hold different values at different times.

Java is a static :type language so the type of variables have to be explicitly defined. The types are divided to two categories Primitive and reference based.

  • Primitive type names are all lowercase and always must have value.
  • reference data type names start with upper case, are full blown :objects and can be assigned a null value.

Why would the designers of Java complicate things ?

The reason is that a primitive types are closer to the “machine” i.e.occupy less memory and are faster to use. It complicates matters for programmers but if used in the right places they can speed up a piece of code.
For the same reason Java is statically typed language. It sucks for you, but makes for a faster programs.

You can use the following primitive data types and their reference based equivalents :

Data TypeSizeDescriptionreference
byte1 bytecan represent integer numbers from -128 to 127Byte
short2 bytesinteger numbers from -32768 to 32767Short
int4 bytesinteger numbers from -2 147 483 648 to 2 147 483 647Integer
long8 bytesnumbers from -9 223 372 036 854 775 808 to 9 223 372 036 854 775 807Long
float4 bytesholds fractional numbers significant 6 to 7 decimal digitsFloat
double8 bytesholds fractional numbers significant to 15 decimal digitsDouble
boolean1 bittrue or false valuesBoolean
char2 bytessingle character/letter or ASCII codeCharacter
say() instead of System.out.println()

In most of the code snippets I will include the following definition :

public static <ARG> void say(ARG arg) { System.out.println(arg); }

which allows me to print anything by using say(), instead of System.out.println(). If you are curios I’m using here Templates which allow me to treat Types as arguments.

Lets see some examples :

Few things to say about those.

For now both kind of types seem the same.

The syntax for defining variables is as follows :

type variable = value;

We can reassign a variable, but the types must be compatible. F.e. we can not assign 300 to a byte variable because it is out of the range 0 … 255. Also we can’t copy/assign integer variable to a byte variable, because we can’t be sure that the current integer value will fit in the byte type range.

Below you can see some examples of assigning to variables of different types.

public class Variables {
public static void main(String[] args){
   
    //primitive types
    byte b = 25;
    int i = 2356;
    float f = 55.334f;
    double d = 55.334d;
    char c = 'c';

    //reference types
    Short sr = 5;
    Integer ir = 344;
    Float fr = 2.76f;
    Double dr = 5.89;
    Character cr = 'C';
    
    String str = "hello";
    System.out.println(str);

    int changed = 3;//define
    System.out.println(changed);
    changed = 5;// reassign
    System.out.println(changed);
    changed = i;//accepts int <=> int
    System.out.println(changed);
    changed = b;//accepts int <= byte
    System.out.println(changed);
    changed = ir;//accepts int <= Integer
    System.out.println(changed);        
    //doesn't accept
    // changed = f; // int != float    
}
-----
Output:

hello
3
5
2356
25
344

First we experiment with the primitive types.

Because float and double are both real numbers and compiler can’t distinguish you have to provide a hint by appending f|d at the end.

Then we do the same with reference types and String.

After that I show how you can reassign compatible types where the compiler can figure out how to convert between them w/o your assistance.

if you try to reassign incompatible types you will get an error. In those cases you have to use the reference types available conversion methods, which you can see in the next tab.

As you probably guessed already this separation of types causes a lot of confusion, so here we will try some Procedures to remedy this situation.

There are two ways to reassign or use incompatible types : conversion and casting.

As you can see below the reference types have methods available to convert from one type to another.

You can also use explicit :type casts between similar types as shown in the last example. Of course you lose precision this way and you can not cast unrelated types.
F.e. you can cast between Number types, but not between Number and String, you have to do conversion in that case as shown below.

Implicit automatic casting happens when you use lower to higher precision. Here is the “direction” in which the conversion happens :


byte => short => char => int => long => float => double

public class HelloWorld {
  public static void main(String[] args) {
    
    String num = "123";
    //convert String to Integer
    Integer inum = Integer.parseInt(num);
    //... and then back to String
    String str = inum.toString();

    //convert reference Double to int
    Double dbl = 123.45d;
    int ival = dbl.intValue();
    System.out.println("Double("+dbl+") => int : " + ival);

    //convert primitive double to int
    double dbl2 = 89.12d;
    int ival2 = new Double(dbl2).intValue();
    System.out.println("double("+dbl2+") => int : " + ival2);
    
    //casting
    int ival3 = (int) dbl2;
    System.out.println("cast:"+dbl2+" => int : " + ival3);
  }
}

-----

Double(123.45) => int : 123
double(89.12) => int : 89
cast:89.12 => int : 89

Strings are special type which are more akin to a data structure, but are so necessary and obvious that you need to get familiar with them, right away.

Below you can see some of the useful operations you can do with Strings :

public class Strings {
  
    //shortcut
    public static <ARG> void say(ARG arg) { System.out.println(arg); }
    
    public static void main(String[] args) {
        String str = "hello world";
        String exc = "!";

        say("substring: " + str.substring(6));
        say("substr 0-5: " + str.substring(0,5));
        say("char @pos 6: " + str.charAt(6));
        say("concat : " + str.concat(exc));
        say( "hello in str ? : " + str.contains("hello"));
        say("isEmpty() ? : " + str.isEmpty());
        say("startsWith(hello) ? : " + str.startsWith("hello"));
        say("indexOf(wo): " + str.indexOf("wo"));
        say("replace() : " + str.replace("hello","howdy"));
        say("toUpperCase() : " + str.toUpperCase());

    }
}

-------

substring: world
substr 0-5: hello
char @pos 6: w
concat : hello world!
hello in str ? : true
isEmpty() ? : false
startsWith(hello) ? : true
indexOf(wo): 6
replace() : howdy world
toUpperCase() : HELLO WORLD

Control statements

We will next look into the constructs that helps us control the flow of execution.

Conditionals

Because solving problems by writing programs requires taking different code paths based on conditions, programming languages have to provide capability to handle them. Java is no exception to this rule.

We thus have :

if (condition) {
….. code ….
} elif (condition2) {
…..
} else {
…..
}
elif block can repeat as many times as needed or none at all.
else is also optional.

First we will declare several variables.

Next is the interesting part where we will generate a random number. For this purpose we import the Math library and call Math.random() which returns a value in the 0 .. 1 range. That is why we multiply it by 10, to get number in the 0 … 10 range.

And now it gets even more interesting, we want to round the number to the closest integer. The weird part is that Math.round() has two forms (thanks to overloading, more on that in part2 of this tutorial).

  • If you pass double value Math.round() returns a long integer value
  • If you pass float value Math.round() returns a integer value

Math.random() returns double value and we want random integer value between 0 and 10. So to make it work we have to use the second form, but have to convert the double argument to float so we use the .floatValue() method. This example clearly illustrate the shortcomings of statically typed languages, very often you have to do those type of code gymnastics to get the correct type.

Now that this is out of the way lets look of how we work with conditions.

It is very easy we write the keyword if followed by a condition and if the condition is true then the statement that follows is executed.

In the first case we do pretty straightforward comparison, so the result is obvious “5 is smaller than 7” is printed on the screen. You may also notice that because this is a single statement we don’t need curly brackets around it.

import java.lang.Math;   

public class IfElse {
  //shortcut
  public static <ARG> void say(ARG arg) { System.out.println(arg); }
  
  public static void main(String[] args) {

    int five = 5;
    int two = 2;
    //lets mix them up
    Integer seven = 7;
    
    //multiply by 10 to change the range to 0 .. 10
    Double random_double = Math.random() * 10;
    //convert to match the types
    int random = Math.round(random_double.floatValue());
    //no conversion when the types match
    long random_long = Math.round(random_double);
    
    say("Generated random number : " + random);
    
    if (five < seven) say("5 is smaller than 7");
    if (five < random) {
      say("5 is smaller than " + random);
    } else {
      say("5 is bigger than " + random);
    } 
    
    if (random > two && random < seven) {
      say(random + " is between 2 and 7");
    } else {
      say(random + " is outside the range 2 .. 7");
    } 

  }
}

Next we use the random number we generated, so we have no idea if the condition will be true or false. As you see we first write the block that will be executed if the condition is true, the other block follows next preceded by the else keyword.

Finally the third variant use a compound condition with logical connectors. You can use : and:&&, or:||, not:!

Generated random number : 5
5 is smaller than 7
5 is bigger than 5
5 is between 2 and seven
5 is smaller than 7
5 is smaller than 7

Sometimes we need to be able to check multiple conditions with multiple outcomes. For this we need to use the else-if sub-construct as shown below.

After you know the basic if-else the code is pretty easy to understand. The order of conditions checks follows the order of declaration. If none of them match the else block is executed.

import java.lang.Math;   

public class IfElseIf {
  //shortcut
  public static <ARG> void say(ARG arg) { System.out.println(arg); }
  
  public static void main(String[] args) {

    int zero = 0;
    int three = 3;
    int five = 5;
    int seven = 7;
    int ten = 10;

    //multiply by 10 to change the range to 0 .. 13
    Double random_double = Math.random() * 13;
    //convert to match the types
    int random = Math.round(random_double.floatValue());

    say("Generated random number : " + random);
    
    if (random >= zero && random < three) {
      say(random + " is between 0 and 3");
    } else if (random >= three && random < five) {
      say(random + " is between 3 and 5");
    } else if (random >= five && random < seven) {
      say(random + " is between 5 and 7");
    } else if (random >= seven && random < ten) {
      say(random + " is between 7 and 10");
    } else {//default
      say(random + " is outside the range 0 .. 10");
    } 

  }
}

-----

Generated random number : 9
9 is between 7 and 10

Switch-case

Switch-case is another conditional control structure. Why another one ?

While if-else is binary true/false decision, while switch-case is multi choice selection. Something like a simplified if-elseif.

What we can have is an expression that returns a value and depending on this value different option or path of action is selected.

Like I said the switch() part contains an expression or a variable and then depending on the result/value a comparison is made with every case value. If there is a match the code behind this match is executed, until it reaches a break statement. If no match is successful then the default option is chosen.

In the example below I generate random number between 1 and 7, based on that number I print the day of the week. Not that hard, right ?

public class SwitchCase {
  
  //shortcut print
  public static <ARG> void say(ARG arg) 
                  { System.out.println(arg); }

  public static void main(String[] args) {
    
    //casting
    int random = (int) Math.round(Math.random() * 7);
    
    say("Generated number : " + random);
    
    switch (random) {
      case 1 : say("Monday"); break;
      case 2 : say("Tuesday"); break;
      case 3 : say("Wednesday"); break;
      case 4 : say("Thursday"); break;
      case 5 : say("Friday"); break;
      case 6 : say("Saturday"); break;
      case 7 : say("Sunday"); break;
      default : say("out of range");
    }
  }
}
-----
Output:

Generated number : 5
Friday

In the random generation we still have the nuisance with the types. This time I do the casting inline.

The interesting part is that it allowed me to cast long => int, it shouldn’t ;\

1


-----
Output:

Loops

There is always a need to do repetitive things. Instead of repeating the code mindlessly we will “surround” this block of code by a condition that will control how many times or how long the block will be executed.

Before we start I defined two helper Functions say(), which will print a text w/o printing new line afterwards. And then another separate function nl() to just print new line.

So now on the basic loops.

First the for-loop is used to repeat N-times a code block.To do that we use index variable which changes at every cycle. It is controlled by three knobs :

  • Initialization of the index variable : int i = 0
  • Condition which is checked at every cycle whether to continue or break out of it : i < 6
  • How the index variables change at every cycle : i++

This specific loop prints the numbers from 1 to 5.

public class Loops {
  
  //shortcut print w/o new line
  public static <ARG> void say(ARG arg) 
                   { System.out.print(arg); }
  // new line
  public static void nl() 
                  { System.out.println(""); }

  public static void main(String[] args) {
    
    //counting from 1 to 5
    for (int i = 1; i < 6 ; i++ ) 
                        say(i + ",");
    nl();
    
    int i = 1;//initialize
    while ( i < 6) {
      say(i + ",");
      i++;
    }
    nl();
    
    String[] nums = { "one", "two",
            "three", "four", "five" };
    //for each loop
    for (String num : nums) {
      say(num + ", "); 
    }
    nl();
  }
}
-----
Output:

1,2,3,4,5,
1,2,3,4,5,
one, two, three, four, five, 

The next type of loop is while-do loop. Here we can also “count” like a for-loop. The example below shows how. The difference is that we have to take care of handling the index variable separately.

But there is one other way a while loop differ : the condition do not have to use index variable.

while-do is “how long”, rather than “how many”‘ kind of loop.

In addition you can also put the condition at the end after the cycle body and make it a do-while loop.
This way you guarantee the code block will be executed at least once.

There is also a third kind of loop a for-each loop.

Java has a bunch of classes that support an Interface called Iterable i.e. things that can be iterated-over, those include arrays, lists, hashes and so on.

The for-each loop uses the capability of these classes to iterate over themselves to implement cycling behavior.

Because the iteration is controlled by the target class there are many looping schemes and behaviors, but the format stays the same … ONE item,object, entity at a time.

In the example above we use for-each loop to iterate over a String array elements.

1


-----
Output: