Java Basics

Mihai PopescuMihai Popescu
8 min read

This is the debut article from a series that I created for myself. Maybe this will help somebody even if some information that I felt I already know by heart are not present. Good luck at your interview experiences.

JDK (Java Development Kit)

  • compile, archive (jar, war...)

  • debug

JRE (Java Runtime Environment)

  • run java apps

JVM (Java Virtual Machine)

  • just in time compiler (JIT)

ClassLoader

  • System -> CLASSPATH (jar, war etc..)

  • Extension -> /ext folder

  • Bootstrap -> java core files (base classes)

Wrapper Classes (Integer, Boolean, Long, Short, etc..)

  • accept null value (as opposed to primitives)

  • can be used in Collections

  • creation of wrapper classes objects

ex:

Integer i = new Integer(10);

or

Integer val = Integer.valueOf(10);

  • autoboxing, static, uses cached values so it saves space

Autoboxing (when java converts automatically primitives to wrapper class objects)

Casting

  • Implicit Autoboxing (Compiler does the conversion automatically):

int primitiveInt = 42;

Integer wrappedInt = primitiveInt;

  • Explicit Autoboxing (Programmer explicitly performs the conversion):

int primitiveInt = 100;

Integer wrappedInt = Integer.valueOf(primitiveInt);

Strings

  • immutable

  • stored in the "String constant pool" in the Heap memory

  • StringBuffer - mutable, better performance and synchronized

  • StringBuilder - mutable, better performance not thread safe

more here: https://javainterviewprep.hashnode.dev/java-strings

OOP

Class (is a template)

  • state: class values at a particular time

  • behavior: methods

Object - instance of a class

Object class - the class that is inherited by all Java classes

  • \== (compares references but not the content of a class)

  • equals() - compares references and contents

  • hashCode() - object with the same hashcode are, by definition, equal

  • object that are not equal can have the same hashcode (collision)

Methods

Overloading - the same method but with different signature

ex:

Object(int n){}

and

Object(String s){}

Overriding - creating the same method with same signature but different implementation in a subclass

Inheritance

ex:

class Actor{} / class Hero extends Actor{}

Actor a1 = new Actor();

Actor a2 = new Hero(); ← can only execute the methods present in Actor

Interfaces

  • Implemented

  • Contract for a class to implement methods

  • All defined methods must be implemented

  • Can contain default implementation

  • Public and abstract by default

  • Variables are always public, static, and final

  • Methods are public by default

  • Can be extended by another interface

  • A class can implement multiple interfaces

Abstract classes

https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

An abstract class is a class that is declared abstract, it may or may not include abstract methods. Abstract classes cannot be instantiated, but they can be subclassed.

An abstract method is a method that is declared without an implementation (without braces, and followed by a semicolon), like this:

  • Extended

  • Can be fully or partially implemented

  • Extended by classes with common functionality

  • A concrete class can extend only one abstract class

  • A child class can define abstract methods but with less or equal visibility

@MappedSuperclass
public abstract class AbstractBaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    public AbstractBaseEntity() {
    }

    public AbstractBaseEntity(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

Constructors

  • default: public Class(){}

  • any argument constructor overrides the default constructor

  • this()

ex:

public Animal(){ this("Some Name"));}

calls

public Animal(String name){}

  • calling a child class constructor automatically calls constructors from the inheritance chain

ex: Animal <<< Dog extends Animal <<< Labrador extends Dog

so Labrador calls Dog that calls Animal

Polymorphism (same code, different behavior)

The power of polymorphism lies in the ability to treat different objects with a common interface (Shape in this case) in a unified manner, making the code more flexible and maintainable.

ex:

interface Shape {
    double calculateArea();
}

class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle implements Shape {
    private double length;
    private double width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    @Override
    public double calculateArea() {
        return length * width;
    }
}

class Triangle implements Shape {
    private double base;
    private double height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    @Override
    public double calculateArea() {
        return 0.5 * base * height;
    }
}

import java.util.ArrayList;
import java.util.List;

public class AreaCalculator {
    public static void main(String[] args) {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(5));
        shapes.add(new Rectangle(4, 6));
        shapes.add(new Triangle(3, 7));

        double totalArea = calculateTotalArea(shapes);
        System.out.println("Total Area: " + totalArea);
    }

    public static double calculateTotalArea(List<Shape> shapes) {
        double totalArea = 0;
        for (Shape shape : shapes) {
            totalArea += shape.calculateArea();
        }
        return totalArea;
    }
}

instanceOf()

  • works with classes and interfaces

Coupling

  • defines how much a class is dependent of other classes

  • the objective is loose coupling

Cohesion

  • defines how related the responsibilities of a class are

ex:

class InternetDownloader {
    public void downloadFromInternet() {}
}

class DataParser {
    public void parseData() {}
}

class StoreIntoDB {
    public void storeInDB() {}
}
  • in the example above the responsibilities are separated

Encapsulation

  • Hide the implementation of a class

  • Internal modification of a class should not require changes to other code

  • Use getters and setters, and create methods that limit the setting of private variables

Inner Class

  • created inside another class or inside methods

ex:

OuterClass ex = new OuterClass();

OuterClass.InnerClass in = ex.new InnerClass();

  • for Static Nested Classes there is no need to instantiate the OuterClass to access the Inner

  • static cannot access values of OuterClass

ex:

OuterClass.StaticInnerClass = new OuterClass.StaticInnerClass();

Anonymous classes have no name and help you create a class and it's implementation inside another class

  • used for single usage classes and methods

ex: creating a Comparator and it's implementation inside another class

Collections.sort(people, new Comparator<Person>() {
    @Override
    public int compare(Person person1, Person person2) {
        return Integer.compare(person1.getAge(), person2.getAge());
    }
});

Modifiers

  • default

    • Package access

    • Visibility only inside the package

  • private

    • Only available in the class they are declared in

    • Not available in subclasses

  • protected

    • Visibility inside the package

    • Available to subclasses

  • public

    • Access from anywhere

Final Modifier

  • Class: A final class cannot be extended and becomes immutable.

  • Method: A final method cannot be overridden.

  • Variable: A constant variable cannot change (e.g., pi).

Static Modifier

  • Variables and methods become class-level members.

  • Static variables are shared by all instances of the class and can be accessed without instantiating.

  • Static methods cannot access instance variables or this().

  • Non-static methods and variables can access static methods and variables defined in the class.

Conditions and Loops

if (n > 10)  
    n = 1; // Without the {} only the first line is considered part of the if

n++; // This is executed outside of the if

// int n = 2;


switch(n) { // switch works only for String, char, byte, short, int, and ENUM
    case 1: 
        System.out.println(1);
        break;
    case 2: 
        System.out.println(2);
        break;
    case 3: 
        System.out.println(3);
        break;
    default: 
        System.out.println("default"); 
        // without a break, the output will be 2 3 default
}
// default can be anywhere in the switch but it will act the same

for (int n : numbers) { // numbers is an array[]
    System.out.println(n);
}

Exception handling

  • Chain of responsibility

ex:

main() >calls> method1() >calls> method2()

if method2() has no try catch it sends the responsibility to method1()

if method1() has no try catch it sends the responsibility to main()

if main also does not have a try catch it will throw the full stack trace

  • finally keyword -> good for closing connections or context

it will always execute except if it throws exception or a JVM crash

  • accepted forms:

try catch

try finally

try catch finally

Exception hierarchy

  • All exceptions inherit Throwable.

  • Error extends Throwable: Represents issues a programmer cannot handle (e.g., out of memory, stack overflow).

  • Exception extends Throwable: Can and should be handled by the programmer.

  • CheckedException extends Exception: Must be handled by the code calling this method.

  • UncheckedException extends RuntimeException: Thrown when the calling code is not expected or able to handle the exception.

  • When a method throws a CheckedException, it should handle it or declare that it throws an exception (e.g., throws Exception) so the calling method can handle it or pass it up the chain of responsibility.

Try with resources:

ex:

try(BufferedReader b = new BufferedReader()){code}

it should close automatically the BufferedReader as long as it extends AutoClosable

Arrays

  • default values:

byte, short, int, long: 0

float, double: 0.0

boolean: false

object: null

  • array

.toString() -> for 1D arrays

.deepToString -> for 2D, 3D... arrays

. equals()

Enum

  • works in switch statements

enum Seasons { WINTER(1), SPRING(2), SUMMER(3), FALL(4); private int code; public int getCode() { return code; } Seasons(int code) { this.code = code; } }

Variable arguments

ex:

public int sum(int...numbers){

for(int number: numbers){code...}}

Initializers

Static - runs when a class is loaded (ex: static {...} from a class)

Instance - runs when a new object is created

ex: class X {

int n;

{...}

}

Serialization

ex: Class implements serializable

  • converting an object in its current state to some internal representation (to File etc...)

  • deserialization is the reverse process

  • transient keyword for element we do not want to serialize

0
Subscribe to my newsletter

Read articles from Mihai Popescu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mihai Popescu
Mihai Popescu