Everything has to be inside a class in .java source file. Filename must match the โentrypointโ classname (incl. case), and it ofcourse must have a main() method inside.
public and its name must match the filenameprivate or protected, it leads to compiler errorstatic, it leads to compiler error. Nested classes can be static though.public static final void main()
// only "final" is optional here
The argument has to be a String array. So all of the below works:
String args[]
String foobar[]
String[] args
String... args
static because it is called by JVM using class namestatic from it will not cause any compile-time exception but cause NoSuchMethodError error on runtimemain() can be overloaded just like any other method but only String[] one will be called by the JVMpublic static void main(String args[]){
args[0] // first argument (not program name) = 8
args[1] // second argument = "foobar"
}
/*
$ java Hello 8 foobar
*/
If we donโt pass required number of arguments: ArrayIndexOutOfBoundsException
package foo;
import java.util.*;
class Hello{
}
java.lang.* is always implicitly imported regardlesspackage declarations, then they need not import each other explicitly as itโs trivialimport static to import static methods and fields of a class (to access without instantiation)import java.nio.*) only imports classes from the current package and not from children pacakages toojava.util.Date and java.sql.* both are imported, then Date is fetched from util packageimport java.util.*;
import java.sql.*; // Date will lead to error
import java.util.Date;
import java.sql.Date; // ambiguous still
java.util.Date dateVar;
java.sql.Date dbDateVar; //removes ambiguity
Ordering Rules:
package declaration has to be first non-comment in a source fileimport always comes after package declarationNo nesting allowed.
// single line
/* block */
/**
* documentation
* style
* @author john doe
*/
data_type name = init_value;
$, etcโฆ_) isnโt a valid identifier name (unlike C/C++) (this changed in 2024 with Java 22)final variablesint x;
// no errors unless the variable is accessed without initialization
// all paths leading to access must initialize (Java is smart to detect this) or else error
final int a; // declaration
a = 5; // initialization
More on their initialization here
Variable Defaults:
Local -> no defaults (no garbage values), declaration only if value not assigned inline
non-final Instance -> default value assigned by compiler
non-final Class/static -> default value assigned by compiler
final Instance/Class -> no defaults, must be initialized before constructor finishes!
Do note that the defaults on non-final instance and class/static variables are not by constructor but by compiler itself. They are assigned default values even before any constructor finishes executing!
class A {
// inside some method
new B();
}
class B {
int x; // instance member
B(){
System.out.println(x); // prints 0; even before constructor has finished
}
}
var name = init_value; // type is implicitly inferred
var variables inline, and once type is inferred, we canโt change itnull but can be put in latervar isnโt a reserved keyword and can be used as an identifier (weird!). We canโt have types (classes, enum, interface) with name โvarโ.var is ONLY used for local variable type inference. Anywhere else it is used (method params, instance variables, constructors), it is errorvar canโt be used in multiple-variable assignmentsvar a = 3, b = 5; //invalid
var is known at compile-time and type cannot be changed ever after thatvar can be used in Lambda Expression variables thoughFoobar foobar = (var a, var b) -> a < b;
int short long byte char
float double
boolean
// there is no explicit "unsigned" specifier for primitives in Java
// char is unsigned; other integral types are not
int is 4 Bytesfloat is 4 Byteschar is unsigned Unicode (superset of ASCII), encoded as UTF-16 so one char can take one or two bytes. It is an integral type.boolean is not convertible to int and vice-versa, we cannot even use == with int and boolean values. Takes values true or false. Size is undefined and depends on JVM.while(1){ } // not allowed; expected boolean
55L // long -- l/L
0123 // octal
0xFBB // hex -- x/X (A to F case-insensitive)
0b1011 // binary -- b/B
0.33F // float -- f/F
1e4 // double
'a' 'โค๏ธ' // chars
"foobar" // string
long n = 9999999999;
// it is compiler error unlike in C++ since literal is int and is overflowed
long n = 9999999999L; // solves the above issue
// underscores are allowed in all numeric types to make them easier to read
int million1 = 1000000;
int million2 = 1_000_000;
int binary_num = 0b0001_0101;
// underscore can't be at beginnning or end of literal or on either sides of a decimal point (.)
float exp = 1.010_101;
// multiple underscores together is valid; useless though
int eleven = 1__________1;
Logical/Bitwise operators:
& | ^ // can be applied to both boolean and integer types; works the same way as conditionals with boolean type: && ||
~ // only for integer types
Logical operators are not short circuited whereas Conditional operators are.
boolean a = false;
boolean b = (true) | (a = true); // logical AND (&)
System.out.println(a); // true
boolean a = false;
boolean b = (true) || (a = true); // conditional AND (&&)
System.out.println(a); // false
double x = 99; // valid; smaller to larger type cast is implicit
long y = x; // invalid; larger to smaller cast can lead to loss of data
float z = 9.0; // invalid; must be 9.0f
double a = 9; // implicit cast to double from int
int b = (int) a; // explicit cast from double to int
int; this is to prevent numeric overflow error.short x = 5;
byte y = 5;
x + y; // int
Another example of Integral Promotion:
System.out.println('b' + 'i' + 't'); // prints 319; ASCII addition result
Wrap around can happen despite integral promotion if weโre not careful:
short i = 32767;
System.out.println( i + 1 ); // 32768, integral promotion happened on addition operator
System.out.println( ++i ); // -32768, no integral promotion happened so it wrapped around
~ operator is same as in C/C++. To find the bitwise complement of a number, multiply it by negative one and then subtract one.
System.out.println(~8); // -9; use -(n+1) formula
// it is supposed to perform 1's complement on the operand
Strings are immutable in Java
Advantages of immutability include:
All strings literals (anything within "") are cached in an area in Heap memory called โString Constant Poolโ or โString Intern Poolโ for performance optimization.
When we define literal without new, we either get old ref or a string pool entry is created and we get itโs new ref.
When we create using new String(), we force Java to create a new object regardless of any prior pool entry.
The sneaky thing is when we are allocating space using new, we end up using literal ("") in constructor call and a String literal ALWAYS gets placed in pool. So, its because of the String "" literal use that we get two objects created in memory and not by virtue of using new.
Reference: https://stackoverflow.com/a/66866401
// how many string literals does the following statement create internally?
String a = new String("A");
// 1 literal - if "A" exists in pool already
// 2 literal - if "A" doesn't exist in pool already
// compare that to simple literal
String b = "B";
// it can create 0 or 1 depending on existance in pool
// another example
String s = new String(new char[]{'a','b','c'});
// only 1 String literal is created in heap ("abc"), none in pool
String Representation: Since Java 9, a new representation is provided, called Compact Strings. This new format will choose the appropriate encoding between char[] and byte[] depending on the stored content.
The internal array representation has 65536 elements and String Pool can go OutOfMemory since it not eligible for Garbage Collection. The memory is freed only when JVM is restarted.
String a = "foo"; // stored in pool
String c = "foo"; // points to a only (a == c is true)
String b = new String("bar"); // stored in heap and b points to it, literal "bar" is also placed in pool
String d = b.concat("tender"); // d is in string pool (since "new" is not used)
// text block Strings
String d = """
foobar""";
/*
Rules:
1. Must have a new line after start """
2. \s puts two spaces
3. line splicing using \ is there
4. \" and \""" are available
5. escape characters work inside this
6. spaces at end of a line are ignored
*/
// Intern a string to copy it to Pool from String Object in heap if it doesn't exists there already and return it's reference, otherwise old ref from pool is returned
String internedStr = str.intern();
Once we have a String literal in pool or heap, we canโt โeditโ (modify) it in-place.
String a = new String("foobar");
a = a.substring(0, 3) + "d";
// Literals created in pool: "foobar", "food" (new literal)
String b = "foo";
b = b + "bar";
// Literals created in pool: "foo", "foobar" (new literal)
a.equals(b);
c.equalsIgnoreCase(d);
// Avoid using "==" for string comparison since it compares object references and not actual value
// Naturally, only interned strings can be compared with ==
For-Each loop doesnโt work on String object as it doesnโt implement the Iterable interface.
The below works in C++ but not in Java:
String str = "foobar";
for(char c : str){ // compiler-error; Foreach not applicable to type 'java.lang.String'
// logic
}
// fixed
String str = "foobar";
for(char c : str.toCharArray()){
// logic
}
import java.lang.*;
new String("foobar");
new StringBuffer("foobar");
new StringBuilder("foobar");
StringBuffer is slower but thread-safe since it is synchronized.StringBuilder is faster but not thread-safe.String can be casted to StringBuffer or StringBuilder using new.toString() can be performed on StringBuffer or StringBuilder to cast to immutable Stringimport java.util.*;
StringTokenizer();
StringJoiner();
Integer.parseInt(str) // returns int; takes Strings only; slower
Integer.valueOf(str) // returns Integer; takes both String and intgeral types; faster
String.valueOf(n) // n is primitive or any other type
x.toString() // where x is of type Integer
Injecting variable values into a string is called โString Interpolationโ.
// + operator
name = "Heisenberg"
age = 69
System.out.println("My name is " + name + " and I am " + age + "years old.");
// String.format() method
name = "Heisenberg"
age = 69
String.format("My name is %s and I am %d years old.", name, age);
// using StringBuilder
// MessageFormat.format() method
Other programming languages like Python and Javascript make it much easier to interpolate strings.
switch supports the following types:// all integer literal types:
int short long char byte enum
// and their wrapper classes: Short Integer Long Byte Char
String
var // if type is one of the supported types
// variables can be used but they must be "final"
switch labels and expressions and case labels can be String here too (unlike C/C++) and they have to be compile time constant//combining case values
case 1, 2:
case 1:
case 2: // old way
// switch expressions, notice semi-colon (;) at last
var result = switch(day) {
case 0 -> "Sunday";
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4 -> "Thursday";
case 5 -> "Friday";
case 6 -> "Saturday";
default -> "Invalid value";
};
// switch expressions must handle all possible input values so "default" case becomes mandatory all the time!
// yield keyword: used to supply value incase we want to execute logic in case output block
var result = switch(num) {
case 0 -> "Zero";
case 1 -> "One";
case 2 -> "Two";
default -> {
System.out.println("Number not allowed."); // executing logic
yield "Invalid"; // must be last statement of the block
// otherwise unreachable code compiler-error
}
};
System.exit(0) can be used to indicate successful termination, it even skips finally block execution
for-each loop
int [] arr = new int[]{1, 2, 3, 4, 5};
for(int i : arr){
System.out.printf("%d ", i);
}
// 1 2 3 4 5
break;
break LABEL;
continue;
continue LABEL;
import java.util.*;
Scanner in = new Scanner(System.in);
in.nextInt(); // int
in.nextFloat(); // float
in.next(); // string
in.nextLine(); // full line until '\n'
in.close();
Scanner is not thread-safe (no sync) wheareas BufferedReader is.Scanner buffer size is 1 KB wheareas BufferedReader buffer size is 8 KB. So BufferedReader is good for handling larger inputs.BufferedReader is a bit faster than Scanner.InputStreamReader r = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(r);
System.out.println("Enter your name");
String name = br.readLine();
System.out.println("Welcome " + name);
System.out.print()
System.out.println()
System.out.printf() // works mostly like C's printf()
String.format() // to store in another String, formatting works like printf
Available for all data types. Advantages include:
java.util methods take in wrapper objects as argumentsCollection framework deals with wrapper objectsBigInteger and BigDecimal provide huge ranges and utility methodsObjects of all primitive wrapper classes are immutable!
// 1. new
Integer n = new Integer(33); // deprecated way
// 2. valueOf()
Integer n = Integer.valueOf(33); // better way
// Points to old object if it already exists in constant pool
// 3. Autoboxing
Integer n = 5;
// unboxing
int num = n;
// Wrapper to primitive (explicitly; no unboxing)
int n = num.intValue();
// autoboxing and promotion can't happen in one go
Long l = 8; // compiler error
// promotion = int literal 8 to long
// autoboxing = long to Long
// autoboxing and unboxing null can lead to surprises
Character c = null; // valid since c is ref variable and it can store null ref
char ch = c; // NullPointerException; since we try and call method on c internally to get primitive out of it
// methods calls; same here
void foobar(Long x){ }
foobar(8L); // valid
foobar(9); // invalid
int[] arr;
Integer[] arrInt;
// both are different
Just like the String class is immutable, wrapper classes like Integer too are immutable and always Pass-by-Value (everything in Java is).
public static void main(String[] args) {
Integer n = 5;
increment(n);
System.out.println(n);
}
public static void increment(Integer n){
n = n + 6; // re-assignment of ref variable
}
// Output: 5
java.util.Object classCloneable and java.io.Serializable interfaces0 or false or nullint arr[]; // C-style
int[] arr; // Java-style
int arr[]; // uninitialized; declaration only (remember Java allows this with non-final variables)
int arr[] = new int[5]; // init list not allowed; sets default values
int arr[] = new int[]{1, 2, 3}; // anonymous array
int arr[] = {1, 2, 3}; // still allocated in heap
int arr[] = new int[3]{1, 2, 3}; // compiler error; both size and initilizer can't be specified
// variable sized arrays are allowed with "new" syntax, and size can be determined at runtime; can't change once set ofc
int n = new Scanner(System.in).nextInt();
int arr[] = new int[n]; // valid!
int arr[n]; // invalid!
// muti-dimensional arrays
int arr[][]; // 2D
int[][][] arr; // 3D
int[] arr[]; // 2D
// range checking is strict unlike C & C++ and often results in RUNTIME Exceptions:
ArrayIndexOutOfBoundsException
NegativeArraySizeException
// in method parameters
void foobar(int[] arr){ }
void foobar(int arr[]){ }
// length of an array
arr.length // and not arr.length()
// multiple-declarations
int [] a, b; // two array ref variables
int a[], b; // one array ref variable, one primitive int
// utility methods
Arrays.sort()
Arrays.compare()
Arrays.stream()
...
Math.min()
Math.max()
Math.round()
Math.ceil()
Math.floor()
Math.pow()
Math.random()
// etc...
import java.util.*;
Date d = new Date(); // 1st way
Calendar c = Calendar.getInstance(); // 2nd way
c.get(Calendar.DAY_OF_WEEK)
c.get(Calendar.DAY_OF_YEAR) // etc...
import java.text.SimpleDateFormat;
import java.util.Date;
SimpleDateFormat ft = new SimpleDateFormat("dd-MM-yyyy");
String str = ft.format(new Date()); // 07-05-2022
Frees up memory in heap used by dereferenced objects and objects which has gone out-of-scope. Run by System.gc() but nothing is guaranteed. When the garbage collection is triggered by the JVM isnโt guaranteed either.
Mark and Sweep Algorithm - identify and mark objects to clean, then sweep through and free up space.
Types of garbage collectors in Java:
Generational ZGC (Java 21) - based on generational hypothesis acc to which young objects are more likely to die first. Logically separates the heap into two generations: a young generation and an old generation. When an object is allocated, it is initially placed into the young generation, which is frequently scanned. If an object survives long enough, it will be promoted to the old generation.
Stopping everything is called stopping-the-world (STW) or pausing (GC Pause). It halts all the program threads. Stopping the world ensures that no one accesses or changes memory during a GC run.
References:
Called automatically when Garbage Collector is called by the JVM.
public void finalize() {
// we can do anything here
}