Home Java Tips & Tricks (1/2)
Post
Cancel

Java Tips & Tricks (1/2)

Today I cover a bunch of features that exist in Java that most intermediate Java developers don’t know about. In this tutorial, I cover the various types of constructors a class can have, the power of using code blocks to control scope and how to use labels to manage nested loops.

Overview & Aims

In this tutorial, we will cover:

  • The various types of constructors:
    • The default constructor
    • The static constructor
    • Constructors with different access modifiers
  • Code blocks and how they can be used to control scope of variables
  • Labels and how to use them effectively

Requirements & Expected Knowledge

In order to follow this tutorial, I’ll assume that:

  • You know how to write basic code in Java
    • for loops, while loops, switch statements
  • You know how to use constructors and create classes

Constructors: Class initializers

Constructors are the main entry point for Java objects. When Java objects are created, the class constructors are called to basically “set up” your class. When encountering Java for the first time, most beginner developers are taught about how to create basic constructors. These are public constructors which are normally of the following form:

1
2
3
4
5
6
7
public class SomeClass {
    
    public SomeClass() {
        //Code goes here
    }
    
}

However, this is just one of many types of constructors that a class can have.

The default constructor

The default constructor is a constructor that is called before any regular constructors are called. The syntax looks bizarre and abnormal since it’s basically a pair of braces in a class with no name, type or access modifier:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SomeClass {
    
    //Default constructor
    {
        System.out.println("Default constructor");
    }
    
    //Regular constructor
    public SomeClass() {
        System.out.println("Regular constructor");
    }
    
}

The default constructor is called before any regular constructor when the class is initialized. In the example above, initializing the class by using new SomeClass() will produce the following output:

1
2
Default constructor
Regular constructor

Default constructor uses

The default constructor allows you to share the initialization of the class across multiple different constructors. Since it’s called before all other constructors, it allows you to initialize variables that other constructors may use for their own initialization.

The static constructor

In addition to the regular constructor and the default constructor, there is also a special constructor called the static constructor. The static constructor is only ever invoked once when an object is instantiated, even if you instantiate multiple instances of an object. Say we have the following class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SomeClass {
    
    //Static constructor
    static {
        System.out.println("Static constructor");
    }
    
    //Default constructor
    {
        System.out.println("Default constructor");
    }
    
    //Regular constructor
    public SomeClass() {
        System.out.println("Regular constructor");
    }
    
}

We can then create multiple instances of that class by using the new keyword:

1
2
new SomeClass();
new SomeClass();

Upon running this code, the following output is printed to the console:

1
2
3
4
5
Static constructor
Default constructor
Regular constructor
Default constructor
Regular constructor

This is because the static constructor is only ever invoked once for the object because it is shared across all instances of that object. It’s also worth noting that static constructors are called before all other types of constructors.

Accessing static members

The static constructor is even more specialized than the other constructors that are covered in this tutorial in the sense that they are also called when you access any static members of a class. Take the following example:

1
2
3
4
5
6
7
8
9
10
11
12
public class SomeClass {

    public static float pi = 3.14159;

    static {
        System.out.println("Static constructor");
    }
    
    public static void printHello() {
        System.out.println("hello");
    }
}

Accessing either the function printHello() or even the field pi will cause the static constructor to run for the first time that it is accessed. Say we have the following program:

1
2
3
public static void main(String[] args) {
    System.out.println(SomeClass.pi);
}

Since we’re referring to the static member pi from the class SomeClass, and we’re referring to the class SomeClass for the first time, the static constructor is called and the output is therefore:

1
2
Static constructor
3.14159

Multiple default and static constructors

As an aside, since we’re talking about Java ‘tips and tricks’, it’s probably worth mentioning that multiple default and static constructors can be created in a class. Take the following example:

1
2
3
4
5
6
7
8
9
10
11
public class SomeClass {
    
    {
        System.out.println("Default constructor 1");
    }
    
    {
        System.out.println("Default constructor 2");
    }
    
}

In this class, we’ve declared two separate default constructors, which print to the console. When declaring multiple default and static constructors, they are called in the order that they are declared, before other constructors of lower priority. In the code snippet above, creating an instance of SomeClass will produce the following output (every time):

1
2
Default constructor 1
Default constructor 2

As far as I am aware, there is no reason to split a constructor like this into multiple different constructors, as their execution is in order and the constructors can be combined into one constructor of the same type (i.e. one single default constructor or one single static constructor).

Private, package-private and protected constructors

The last type of constructor which we will cover are constructors that have different access modifiers. Normally, constructors use the public access modifier, however there’s nothing to stop a constructor from having the private, package-private (no keyword) and protected access modifiers. The code below shows an example of a private constructor:

1
2
3
4
5
6
7
public class SomeClass {
    
    private SomeClass() {
        //Code goes here
    }
    
}

It’s important to not confuse the difference between constructors which have package-private access and default constructors. The following shows a constructor which has package-private access:

1
public class SomeClass { SomeClass() { //Code goes here } }

Compared to a default constructor:

1
public class SomeClass { { //Code goes here } }

This is particularly useful if you want to prevent other developers from creating an instance of a class. This is because the constructor is not visible outside of the declaration of the class and thus an instance cannot be constructed with the new keyword.

In the case of restricted access modifiers, this can be used along side class builders or static methods to create instances of the class. Below shows a basic example of how you can use a static method to create an instance of a class with a private constructor:

1
2
3
4
5
6
7
8
9
10
11
public class SomeClass {

    private SomeClass() {
        //Code goes here
    }

    public static SomeClass createSomeClass() {
        return new SomeClass();
    }

}

Extending classes with private constructors

In addition to preventing other developers from creating instances of a class, classes which have private constructors that other developers cannot access cannot be extended. This is because by default, all subclasses should invoke the constructor of their superclass, however private constructors are not in scope and cannot be called.

To illustrate this, say we have the following class:

1
2
3
4
5
6
7
public class ParentClass {

    private ParentClass() {
        //Code goes here
    }

}

This class ParentClass has a private constructor, therefore it cannot be extended:

1
2
3
public class ChildClass extends ParentClass { 

}

This code would cause a compilation error because the super constructor ParentClass() is not visible (since it has private access). This can be mitigated by changing the access modifier to the constructor ParentClass() to a suitable access modifier for the situation (for example, package-private if they are in the same package).


Code Blocks: Controlling scope

In Java, a code block is simply a section of code. Pretty much any time when you open a pair of braces and write code (for example, in a method, in a for loop, in a while loop or even a switch statement) what you’re doing is opening a new code block. For example, take the syntax for a while loop:

1
2
3
while(condition) {
    //Code goes here
}

In this example, the while loop contains a condition and a code block which contains the body of code to run. However, code blocks are not limited to loops and switch statements - they can be used anywhere in your code, as shown in the example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void someFunction() {
    
    int someNumber = 2;

    {
        int sum = 0;
        for(int i = 0; i < 20; i++) {
            sum += i;
        }
        System.out.println(sum);
    }
    
    System.out.println(someNumber);
    
}

The reason this is useful is due to local variable scope. Let’s show how scope is handled throughout the example shown above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void someFunction() {

    int someNumber = 2;
    
    {
        int sum = 0;
        for(int i = 0; i < 20; i++) {
            sum += i;
        }
        //Variable i no longer exists here
        System.out.println(sum);
    }
    //Variable sum no longer exists here
    System.out.println(someNumber);
    
}
//Variable someNumber no longer exists here

After each closing brace, you can see how variables drop out of scope. After the for loop, the variable i is no longer in scope within the code. Similarly, after the next closing brace, the variable sum is no longer in scope. Using code blocks allows us to control whether certain variables are in scope. This can be used for two reasons:

  • Ensuring that a variable is not used past a certain point. In the example code above, we can rest assured that the variable sum cannot be accidentally used past the code block from which it no longer exists.
  • Re-using the same variable name in an outer code block. For example, if we had to use the same variable name in a code block, we can place it in multiple inner code blocks. This is best used in switch statements.

Code blocks in switch statements

Code blocks can be used to manage scope, as we have seen above. This can be used very well with switch statements to allow duplicate local variable names. Take the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int i = /* Some value */;
switch(i) {
    case 0: 
        String str = "hello";
        //Do something with str
        break;
    case 1: 
        String str = "bye";
        //Do something with str
        break;
    case 2: 
        String str = "hello and bye";
        //Do something with str
        break;
}

If you try to compile this, you’ll get an error Duplicate local variable str. This is because in the code block declared by the switch statement, all of the variables str are in the same scope. Opening a new case statement does not open a new scope. However, we now know that we can open a new scope anywhere by using code blocks in our code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int i = /* Some value */;
switch(i) {
    case 0: {
        String str = "hello";
        //Do something with str
        break;
    }
    case 1: {
        String str = "bye";
        //Do something with str
        break;
    }
    case 2: {
        String str = "hello and bye";
        //Do something with str
        break;
    }
}

By opening a new scope at each case statement, we’ve now ensured that the variables str do not conflict as they are in their own individual code block.


Labels: Naming code blocks

To extend on the power of code blocks, it is also possible to name code blocks in Java and use the keywords break and continue to ‘break out’ of a code block or ‘go to the end’ of a code block. This is often used in for loops, while loops and switch statements. Let’s look at an example where we use the ability to ‘name’ code blocks to break out of nested loops.

Example: Breaking out of nested loops with labels

1
2
3
4
5
6
7
for(int i = 0; i < 10; i++) {
    for(int j = 0; j < 10; j++) {
        if(j == 5) {
            //Break out of 'i' for loop
        }
    }
}

In this example, we have two nested for looping between 0 and 9. In our nested loop, we want to break out of the outermost loop when the value is 5. Normally, to break out of a for loop, we would use the break keyword, however this merely breaks execution of the innermost loop. Another approach is to use a boolean value which we can change to end execution, however there is a better solution:

1
2
3
4
5
6
7
iLoop: for(int i = 0; i < 10; i++) {
    for(int j = 0; j < 10; j++) {
        if(j == 5) {
            break iLoop;
        }
    }
}

We can use labels to ‘name’ code blocks and lets you refer to them using break and continue. In the example above, we denote the outermost for loop as the iLoop and we can specifically break out of this loop by using break iLoop.

Breaking out of any code block

These labels are not limited to for loops, they can be used for any code block, as shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void someFunction() {
    
    //Code block
    someCode: {
        //Code goes here
        if(/* Some condition */) {
            break someCode;
        }
    }
    
    //While loops
    whileLoop: while(/* Condition */) {
        while(/* Condition */) {
           //Code goes here
            break whileLoop;
        }
    }
    
    //Switch statements
    switchStatements: switch(/* Value */) {
        case 0:
            switch(/* Value */) {
                case 0:
                    break switchStatements;
                ...
            }
        ...
    }
    
}

These can be used to help execution of nested statements, or write cleaner code. Take the first example above:

1
2
3
4
5
6
7
8
9
public void someFunction() {
    someCode: {
        //Code goes here
        if(/* Some condition */) {
            break someCode;
        }
        //Code goes here
    }
}

Normally, without using labels we would have to write something of the form:

1
2
3
4
5
6
7
public void someFunction() {

    //Code goes here
    if(! /* Some condition */) {
        //Code goes here
    }
}

It may be preferable to use the latter since it’s more concise and less ‘jumpy’, but nonetheless, both methods are valid and the flow of a program can be easily interpreted.


Summary

To summarize, we’ve covered the various types of constructors that exist, and various use cases for them. In particular, we looked at:

  • The default constructor which can be used to share code across constructors
  • The static constructor which can be used for initialization of static members and for all instances of a class
  • Private/protected/package-private constructors which can be used to control access to constructors and prevent subclassing

We also looked at code blocks which can be used to manage scope, in particular for switch statements to resolve the compilation error of duplicate local variables within a switch statement. Lastly, we covered labels which can be used to handle control flow through multiple code blocks by naming them.

This post is licensed under CC BY 4.0 by the author.

Making the Elm-Brainfuck-IDE

Java Tips & Tricks (2/2)