The most annoying Java related questions

Below is my personal list of most annoying Java related questions, answer for which I found difficult to memorize, or I perceived as strange or non-intuitive when I asked them myself.

I am making this list to save on googling time and to peek occasionally on the below examples hoping it will spark a deeper understanding of Java concepts.

1. How are things passed to functions?

In general thing are passed to function as a copy of their values. Let us see this in few examples:

  • in case of primitive types (boolean, byte , char , short , int , long , float and double) - you can do anything with them inside the function and you will not change the original variable since a function receives a copy of a value of original variable:

    public class TestClass {
        public void run() {
            int a = 0;
            System.out.println(a); //output = 0
            foo(a);
            System.out.println(a); //output = 0
    
        }
        private void foo(int a) {
            int b = 1;
            a = b; //value copy is modified, not the original value
            System.out.println(a); //output = 1
        }
    
    }
  • objects - a copy of value of a reference to the object is passed; so in a method there is an access to the memory where the object is stored; in case of:

    • mutable objects their content can be modified from within the function like below:
    public class TestClass {
        public void run() {
            //mutable Integer class
            AtomicInteger a = new AtomicInteger(0);
            System.out.println(a); //output = 0
            foo(a);
            System.out.println(a); //output = 1
    
        }
    
        private void foo(AtomicInteger a) {
            a.addAndGet(1); //object referenced by 'a' gets mutated
            System.out.println(a); //output = 1
        }
    }
    • immutable objects - their content is not modified, since they cannot be mutated; when doing a++ below, actually the new Integer object somewhere in memory is created and a reference to it is assigned to a. But this foo originating a is a separate copy of run originating a, so foo's a is changed, and run's a is untouched;
    public class TestClass {
    
        public void run() {
            //immutable integer class
            Integer a = 0;
            System.out.println(a); //output = 0
            foo(a);
            System.out.println(a); //output = 0
    
        }
    
        private void foo(Integer a) {
            a++; //new Integer object created behind the scenes
            System.out.println(a); //output = 1
        }
    }
    • dereferencing - if you want to dereference a at foo it is possible, but you will dereference only the foo's copy of a and not the a belonging to run:

      public class TestClass {
      
        public void run() {
            //mutable Integer class
            AtomicInteger a = new AtomicInteger(0);
            System.out.println(a); //output = 0
            foo(a); //this a still points at the same place in memory
            System.out.println(a); //output = 0
      
        }
      
        private void foo(AtomicInteger a) {
            AtomicInteger b = new AtomicInteger(1);
            a = b; //local reference a now points at b
            System.out.println(a); //output = 1
        }
      }

2. How to hack into String with reflection?

The way to mutate String in function through reflection is like below (the original example I have seen somewhere in the web but I cut some of the dead wood to leave the essence). Notice how string pool modification is changing "Immutable":

import java.lang.reflect.Field;

public class MutableString {

    public static void main(String[] args) {
        String s = "Immutable";
        String b = "Immutable";
        String c = new String(b.toCharArray(), 4, b.length() - 4);
        String t = "Notreally";

        mutate(s, t);
        System.out.println(t); //this will print Notreally
        System.out.println(b); //this will print Notreally!!!!
        System.out.println("Immutable"); //this will print Notreally!!!!
        System.out.println(c); //this will print table
    }

    // change the first min(|s|, |t|) characters of s to t
    public static void mutate(String s, String t) {
        try {
            Field val = String.class.getDeclaredField("value"); //characters storage ->  private final byte[] value;
            val.setAccessible(true);

            byte[] value = (byte[]) val.get(s);
            for (int i = 0; i < Math.min(s.length(), t.length()); i++) {
                value[i] = (byte) t.charAt(i); //here is the actual muatation of string "Immutable"
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3 Inner classes - terminology

  • nested classes
    • static nested classes
    • inner classes (i.e. non-static)
    • inner classes
    • method local inner classes
    • anonymous inner classes

4. Inner classes - access to variables

Below is an example of what an inner class in Java can access. Defining those two abstract classes:

protected class AbstractTestClass {
    protected int a = 10;
}
protected class AbstractInnerClass {
    protected int a = 20;
}

and having a TestClass and calling run method on it, the console should print what is in the comments

class TestClass extends AbstractTestClass {
    private static int a = 0;

    public void run() {
        int a = 1; // <-not accessible from inner due to shadowing by a = 2 and a = 3
        new InnerClass().run();
    }

    //public, private, protected -  just limit the access from outside as for fields and methods
    public class InnerClass extends AbstractInnerClass {
        int a = 2;
        private  void run() {
            int a = 3;
            System.out.println(a); //3
            System.out.println(this.a); //2
            System.out.println(TestClass.this.a); //0
            System.out.println(TestClass.super.a); //10
            System.out.println(super.a); //20
        }
    }
}

5. Diamond problem

Maybe that is obvious but I put it for the record - without overriding default def() method, TestClass cannot implement both interfaces A and B, as def() exists as default method at both interfaces. Still the access to 'def()' from both interfaces is not lost as seen in the example below:

public interface A {
    void foo();

    default void def(){
        System.out.println("Def from A");
    }
}

public interface B {
    void foo();

    default void def() {
        System.out.println("Def from B");
    }

}

class TestClass implements A, B {
    @Override
    public void foo() {
        System.out.println();

    }

    @Override
    public void def() {
        A.super.def(); //this calls default from A
        B.super.def(); //this calls default from B
    }
}

6. Closures in Java

6.1 Final and effectively final

This works although packing c into array b looks like cheating.

public class main {

    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(Arrays.asList(0,1,2,3,4,5,6,7,8,9));

        Integer c = 3;
        c++;

        Integer[] b = new Integer[1];
        b[0] = c;
        //below works and returns 0,4,8 despite c is not (effectively) final
        System.out.println(
            numbers.stream()
            .filter(a -> a % b[0].intValue()==0) //wrapping c with b looks like cheating
            .collect(toList())
            );
    }
}

6.2 The common example with twice and triple

Below is another closure example. twice and triple are accompanied by their lexical scopes.

public class Multiplier {

    public Integer factor;

    public Function<Integer, Integer> getMultiplier(Integer factor) {
        this.factor = factor;
        return ( x -> x*this.factor);
    }
}

public class main {

    public static void main(String[] args) {
        Function<Integer, Integer> twice = new Multiplier().getMultiplier(2);
        Function<Integer, Integer> triple = new Multiplier().getMultiplier(3);
        System.out.println(twice.apply(10)); //20
        System.out.println(triple.apply(10)); //30
        System.out.println(twice.andThen(triple).apply(10));//60 <- ugly? lovely?
    }
}