Java

Java

innocentzero

2026-06-16

#programming #programminglanguages | Status: Complete

Java basics from effective Java

Java basics

// comments are written like cpp
/* or like C */
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}
// Code from filename: Main.java
// abstract class
abstract class Main {
  public String fname = "John";
  public int age = 24;
  public abstract void study(); // abstract method
}

// Subclass (inherit from Main)
class Student extends Main {
  public int graduationYear = 2018;
  public void study() { // the body of the abstract method is provided here
    System.out.println("Studying all day long");
  }
}
// End code from filename: Main.java

// Code from filename: Second.java
class Second {
  public static void main(String[] args) {
    // create an object of the Student class (which inherits attributes and methods from Main)
    Student myObj = new Student();

    System.out.println("Name: " + myObj.fname);
    System.out.println("Age: " + myObj.age);
    System.out.println("Graduation Year: " + myObj.graduationYear);
    myObj.study(); // call abstract method
  }
}

Some special labels for methods/attributes:

Java packaging

file tree structure

└── root
  └── mypack
    └── MyPackageClass.java

Here's how it's done in code

package mypack;
class MyPackageClass {
  public static void main(String[] args) {
    System.out.println("This is my package!");
  }
}

Execution happens with java mypack.MyPackageClass

Inheritance/Class nesting

class OuterClass {
  int x = 10;

  class InnerClass {
    int y = 5;
  }
}

public class Main {
  public static void main(String[] args) {
    OuterClass myOuter = new OuterClass();
    OuterClass.InnerClass myInner = myOuter.new InnerClass();
    System.out.println(myInner.y + myOuter.x);
  }
}

// Outputs 15 (5 + 10)

Interfaces

// Interface
interface Animal {
  public void animalSound(); // interface method (does not have a body)
  public void sleep(); // interface method (does not have a body)
}

// Pig "implements" the Animal interface
class Pig implements Animal {
  public void animalSound() {
    // The body of animalSound() is provided here
    System.out.println("The pig says: wee wee");
  }
  public void sleep() {
    // The body of sleep() is provided here
    System.out.println("Zzz");
  }
}

class Main {
  public static void main(String[] args) {
    Pig myPig = new Pig();  // Create a Pig object
    myPig.animalSound();
    myPig.sleep();
  }
}

Object creation best practices

Static factory methods

Builder pattern

Eg:

public abstract class Pizza {
    public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
    final Set<Topping> toppings;
    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }
        abstract Pizza build();
        // Subclasses must override this method to return "this"
        protected abstract T self();
    }
    Pizza(Builder<?> builder) {
        toppings = builder.toppings.clone(); // See Item 50
    }
}

public class NyPizza extends Pizza {
    public enum Size { SMALL, MEDIUM, LARGE }
    private final Size size;
    public static class Builder extends Pizza.Builder<Builder> {
        private final Size size;
        public Builder(Size size) {
            this.size = Objects.requireNonNull(size);
        }
        @Override public NyPizza build() {
            return new NyPizza(this);
        }
        @Override protected Builder self() { return this; }
    }
    private NyPizza(Builder builder) {
        super(builder);
        size = builder.size;
    }
}

Enforce Singleton property with private constructors

Enforce non-instantiability with private constructors

Prefer dependency injection

Best practices for writing classes

If you can manage the memory, you should

Avoid Finalizers and Cleaners

try-with

static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

Overriding equals

High quality equals check:

HashCode/ToString

Override clone judiciously

Implement Comparable

Encapsulate

Favour composition over Inheritance

If you are letting a class be inherited by another:

Favour Interfaces over Abstract classes

Favour static member classes over nonstatic

Single toplevel class in a single source file

Generics

Use generics to constrain types wherever possible

Valid use of raw type

if (o instanceof Set) {
    Set<?> s = (Set<?>) o;
}

Favor generic methods over raw

public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
    Set<E> result = new HashSet<>(s1);
    result.addAll(s2);
    return result;
}

Recursive type bounds

// Using a recursive type bound to express mutual comparability
public static <E extends Comparable<E>> E max(Collection<E> c);

This indicates that E implements Comparable<E>, i.e., it can be compared to itself.

Bounded generics

// Wildcard type for a parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
        push(e);
}
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
    while (!isEmpty())
        dst.add(pop());
}

Wildcards should be invisible

The user of the API shouldn't be bothered with the presence of a wildcard. To him, it should be transparent.

Careful with varargs and generics

// Mixing generics and varargs can violate type safety!
static void dangerous(List<String>... stringLists) {
    List<Integer> intList = List.of(42);
    Object[] objects = stringLists;
    objects[0] = intList;
    // Heap pollution
    String s = stringLists[0].get(0); // ClassCastException
}

C programming to Java

Replace structs with Classes

Replace unions with class heirarchies

Replace enums with Java enums

Use EnumSet instead of bitfields

Replace enums with typesafe enums

class Foo {
    private final String name;
    private Foo() {}

    private Foo(String s) {
        name = s;
    }

    public String toString() {
        return name;
    }

    public static final Suit CLUBS = new Suit("clubs");
    public static final Suit DIAMONDS = new Suit("diamonds");
    public static final Suit HEARTS = new Suit("hearts");
    public static final Suit SPADES = new Suit("spades");
}
public abstract class Operation {
    private final String name;
    Operation(String name) { this.name = name; }
    public String toString() { return this.name; }
    // Perform arithmetic operation represented by this constant
    abstract double eval(double x, double y);
    public static final Operation PLUS = new Operation("+") {
            double eval(double x, double y) { return x + y; }
        };
    public static final Operation MINUS = new Operation("-") {
            double eval(double x, double y) { return x - y; }
        };
    public static final Operation TIMES = new Operation("*") {
            double eval(double x, double y) { return x * y; }
        };
    public static final Operation DIV = new Operation("/") {
            double eval(double x, double y) { return x / y; }
        };
}

Functional Programming in Java

Prefer Lambdas to anonymous classes

Use standard functional interfaces > purpose built one

Use streams judiciously

try returning collections

Don't parallelize streams indiscriminately

Methods

Document the restrictions that are placed on parameters

Make defensive copies

Return ZLAs, not null

Be careful about varargs

Use Optional instead of throws and null

Gen best practices

Reduce number of parameters

Reduce local variables

Minimize scope of local variables

Use loop idioms to minimize compute costs

for (int i = 0, n = list.size(); i < n; i++) {
    do_something(i);
}

Use libs instead of your own funky code

Avoid floats and doubles if precision is the key

String concatenation is quadratic in nature

Use interfaces to describe objects

Use interfaces over reflection

Exceptions as a divergent mechanism

Use exceptions for what they're meant for

Types of exceptions

Use standard exceptions

Throw exceptions for what they mean

Document the ones that are thrown by each method

Failure atomicity

In case of failure, leave the object in the state it was before the failure.

Threads

Synchronize access to shared mutable data

Keep synchronized space small

Don't call alien methods in synchronized

Call wait always in a loop

Don't depend on the thread scheduler

Document thread safety of the object

Use executors, tasks, and streams

Be careful about implementing serializable

Don't accept the default

Find alternatives