Methods

Method Declaration

  • a single underscore (_) isnโ€™t a valid method name (it wasnโ€™t a valid identifier in Java till Java 22, but still canโ€™t be used as a method name)
public static final int foo() throws IOException, SQLException {
	// body
}

// return type must be written with method name followed by exception list; rest's order doesn't matter

Unreachable Code

public int foobar(){
	return 1;
	System.out.println("foobar");	// unreachable code; compiler error
}

public String getStr(){
		// compiler error if nothing is returned in body  
}

Access Modifiers

public	 	   --> everywhere

protected	   --> same class, other classes in the same package, and subclasses (even if subclass is in a diff package)

package access --> default; when we omit any access modifier; accessible only from inside the same package (class or subclass in the same package); if we try to access this from a subclass that's in a different package, there will be error (aka "package-private")

private		   --> accessible from within the same class only; not even its subclasses

Argument Passing

Primitives & references vars  -->	Pass-by-value
Objects  					  -->	Pass-by-reference

Since there are no explicit references in Java unlike C++. We say everything is pass-by-value in Java which means a new reference variable is created in called method and not actual object/array is created in memory again.

Passing mutable types like List<T> or Map<K, V>:

public void addElement(List<String> listStr){	// new ref variable "listStr"; points to same list of string in heap
	listStr.add("B");
}

public void bar(){
	List<String> listStr = new ArrayList<>();	// list of string in heap
	listStr.add("A");
	addElement(listStr);		// method call
	System.out.println(listStr.size());
}

// Output: 2

Since wrapper class objects like String and Integer are immutable, their reference canโ€™t be modified but can be re-assigned (re-pointed):

public static void foo() {
	String s = "test";
	change(s);
	System.out.println(s);
}

public static void change(String s){	// new variable created (pass-by-value); ref to old
	s = "eeee";		// re-assignment
}

// Output: test

------------------------------------------

public void foo(){
	Integer n = 5;
	increment(n);
	System.out.println(n);
}

public void increment(Integer n){		// new variable created (pass-by-value); ref to old
	n = n + 2;		// re-assignment
}

// Output: 5

Local and Instance Variables

class Hello{
	int a = 1;			// instance variable
	void foo(){
		int b = 2;		// local variable
	}
}
  • only one modifier can be applied to local variables - final
  • remember that final on a reference variable wonโ€™t stop anyone from accessing and changing the value inside; its to stop changing the ref variable itself
  • final instance variables must be assigned a value before constructor/constructor chain finishes

Variable Arguments

The vararg syntax ... only works in method declaration signature, compiler-error otherwise if used as a substitute for [] in class or method body.

Rules:

  1. Only 1 vararg can be present
  2. vararg must occur at the last of parameter list
void foobar(String... str){			// or foobar(String ...str)

	for(String s : str){
		// code
	}
}

// Call
String[] strArr = {"john", "doe"};
foobar(strArr)
foobar("foo", "bar")


// compiler-error
String... arr = {"a", "b", "c"};

static

  • static variables and methods are allocated memory once and remain in scope till program end
  • static variables and methods can be accessed/called without creating an instance of the class theyโ€™re part of
  • even if theyโ€™re called from an instance, they arenโ€™t actaully called using object!
class Hello{
	public static a = 5;
}

Hello obj = new Hello();
System.out.println(obj.a);		 // 5
obj = null;
System.out.println(obj.a);		// still prints 5, no NullPointerException
  • static fields can be updated from instance. Whenever instance updates, there is only one copy so it gets reflected
Hello obj2 = new Hello();
obj2.a = 9;
Hello obj3 = new Hello();
obj3.a = 7;
System.out.println(Hello.a);		// 7	
  • static methods canโ€™t be overriden, since they are resolved using static binding by the compiler at compile time, they can be โ€œhiddenโ€ though
  • static methods canโ€™t access instance methods and instance variables directly (from inside the class, without any object created with โ€œnewโ€). They must use reference to object. And static method canโ€™t use this keyword as there is no instance for โ€œthisโ€ to refer to.
  • instance initializer blocks can initialize static variables but not the other way round. We canโ€™t access non-static members from a static context without object reference.
class Foo{
	int a = 5;
	static void foobar(){
		a = 6;				// not allowed
		new Foo().a = 6;	// allowed now since we provided an object ref
	}
}

static methods are almost like instance methods - overloaded, inherited, hidden, can have generic type params (unlike static variables). Exception - static interface methods arenโ€™t inherited to another interface or goto its implementing class.

static Initializer Block

class Hello{
static final int bamboo;	// either initialize inline or use initializer block below
static { bamboo = 5;}		// since we can't have constructors for static fields
}

import static

  • only for a static method or field, and not class unlike normal import
import static java.util.Arrays.asList;

// can use without Arays.asList() now

static import java.util.Arrays.asList;		// invalid! order is wrong

Overloading

  • compile-time polymorphism
  • Same method names but different signatures (parameter types, order, arity). We can overload by any change in the parameter list
  • return type or staticness cannot be a distinguishing factor for overloading methods (compiler-error; same methods, so no overloading). This is because from callerโ€™s POV, return type or staticness doesnโ€™t matter, so they arenโ€™t part of overloading
  • so trivially, static methods can be overloaded by any other method since staticness doesnโ€™t matter in overloading
void foobar() {	}

int foobar() { return 0; }	// compiler-error; method already defined

----------------------------------------------------------------------

void foobar() {	}

static void foobar() {	}	// compiler-error; method already defined

Reference Types

  • The more specific class (subclass) is preferred over lesser one. (String is matched if available over Object)
void foobar(String s){ }	// 1

void foobar(Object o){ }	// 2


foobar("yo");		// calls 1
foobar(88);			// calls 2; autoboxes to Integer, then calls Object

Primitive Types

void foobar(int n){ }		// a
void foobar(Integer i)		// b
void foobar(long l){ }		// c

foobar(123);	// calls a; if a is commented, then it calls b (autoboxing); if b is also commented then it calls c (promotion)
fooabr(123L);	// calls c

Varargs Type

void foo(int[] arr){ }
void foo(int... arr){ }		// compiler error; duplicate method definition

foo(new int[]{1, 2, 3});	// can call either
foo(1, 2, 3);				// calls varargs one only