I wrote these while preparing for OCAJP exam. Most of it is in form of code with output right next to it as a comment.
One java file can have multiple classes, maximum only one class can be public.
// Three.java
class One {}
class Two {}
public class Three {} // only one public class, same name as file
class Four {}
also
// Numbers.java
// No public class
class One {}
class Two {}
class Three {}
class Four {}
Java file must be same name and same case as the public class in that file.
Compile javac myclass.java
run java myclass
.
Signature of main method is
public static void main(String[] args)
public static void main(String args[])
public static void main(String... args)
Arguments start from index 0. On java myclass test
first argument args[0]
contains test
and args[1]
gives java.lang.ArrayIndexOutOfBoundsException
.
Packages solve naming conflicts, e.g. have same class name in different packages. i.e. java.util.Data
and java.sql.Date
classes and interfaces without package are part of default package. default package does not have a name and automatically imported in packageless classes defined in same directory. Classes from default package cannot be used in a named package even if they are in the same directory.
// file a.java
class Foo {
}
// file b.java
class Bar {
Foo f; // Foo can be accessed here
}
import java.lang.*
is done automatically without explicitly writing it. Therefore importing java.lang.*
explicitly is considered redundant.
Explicit import of same class from different packages gives compiler error.
import java.util.Date; import java.sql.Date;
error: The import java.sql.Date collides with another import statement
Import of two packages containing same class gives ambiguous type error
import java.util.*; import java.sql.*;
error The type Date is ambiguous
Some packages to remember.
- java.lang.System
- java.util.ArrayList
Fields and instance initializer blocks run in the order they appear in a file.
Static initializer >>> (new instance()
) >>> Instance Initializer >>> Constructor
public class Chick {
{//8.
System.out.println("6: instance init block 1 ");
name = "foo1"; // forward reference is write only
// System.out.println("setting field " + name); // compile error, illegal forward reference 'name'
}
private String name = "Fluffy"; //9.
{//10.
System.out.println("7: instance init block 2");
System.out.println("8: setting field " + name);
name = "foo2";
}
static {//1.
System.out.println("1: static block 1");
// System.out.println("static block 1" + age); // illegal forward reference 'age'
age = 10;
color = "black";
}
static String color; //2.
static int age = 20; //3.
static { //4.
System.out.println("2: static block 2: " + age); // age = 20
age = 10;
color = "red";
}
public Chick() {//8.
name = "Tiny";
System.out.println("9: setting constructor");
}
public static void main(String[] args) {
System.out.println("4: in main"); //6.
Chick chick;
System.out.println("5: creating instance");
chick = new Chick(); //7.
System.out.println(chick.name); //11.
}
static { //5.
System.out.println("3: static block 3: " + age + color);
}
}
Local variables must be initialized explicitly. Class variables are automatically given default values when declared.
Local variables hide class variables.
primitive types
type | wrapper | size | default | start | end | comments |
---|---|---|---|---|---|---|
boolean |
Boolean | false | true | false | ||
byte |
Byte | 8-bit integral | 0 | -128 | 127 | |
short |
Short | 16-bit integral | 0 | -32,768 | 32,767 | |
char |
Character | 16-bit integral | '\u0000' | 0 | 65,535 | |
int |
Integer | 32-bit integral | 0 | -2,147,483,648 | 2,147,483,647 | Integer.MIN_VALUE, Integer.MAX_VALUE |
long |
Long | 64-bit integral | 0 | 0L | ||
float |
Float | 32-bit floating-point | 0.0f | requires f at the end. |
||
double |
Double | 64-bit floating-point | 0.0 | 0.0D |
A constant value representing data of a specific type in code is called a literal
.
literal | type |
---|---|
'a', '0' | char |
"baba" | String |
9793, 1001, -0 | int |
297.45, 0.15d | double |
493.252f, 370F | float |
66930L, 55l | long |
0b1001L | long |
010 == 8, 07 == 7, 01 == 1 | int octal |
0XA == 10, 0x1F == 31 | int hexadecimal |
0b10 == 2 | int binary |
0b10_1, 0x1_F, 3_3.5, 123_456___7 | all valid numbers |
Primitive types are non null-able i.e. can not be assigned null
.
-0
is a valid literal.
When applying operators, smaller type is promoted to larger data type. e.g. 5 + 1.5 == 5.5
byte
, short
and char
are always promoted to int
in a binary operation.
short x = 10;
short y = 3;
short z = x * y; // error, lossy conversion int to short
short z = (short)x * (short)y; // error, lossy conversion int to short
short z = (short)(x * y); // CORRECT
Here it looks like the result should be a floating point number, but because all terms are integer, the result is also an integer.
int c = 5/10; // c = 0 // result will be an integer, not a float or double
double c = 5/10; // c = 0, NOT 0.5
Small primitives can assigned to large primitives
int c = 'a';
long l1 = 5;
short s = (byte)25;
Larger datatype CANNOT be assigned to smaller datatype without casting
long x = 10;
int y = 5;
y = y * x; // DOES NOT COMPILE, lossy conversion long to int
1L == 1.0f // true
1 == 1L // true
Smaller primitive datatype CANNOT be directly assigned to a bigger Boxed type
long l1 = 10;
long l2 = 20l;
Long l3 = 10; // Compile error, int cannot be converted to Long
// similarly
Long l4 = 0b100; // Compile error, int cannot be converted to Long
Integer i1 = 0b1;
Integer i2 = 'a'; // Compile error, char cannot be converted to Integer
Integer i3 = (Character)'c';
Smaller boxed types CANNOT be assigned to bigger boxed types
// <Bigger_Boxed_type> x = <Smaller_Boxed_Type>; // error
Long l1 = (Integer)(10); // compiler error
But, smaller boxed type can be assigned to bigger primitive type
Short s = 1;
long l1 = s;
long l2 = (Byte)(byte)2;
// parse returns primitive
// valueOf returns wrapper
int num = parseInt("23");
Integer num = valueOf("23");
int $$$, ___, _a, x, $b, _1123; // all valid
String 5xyz, 0_, -a, *_; // invalid, can not start from number
long int, class, try, const, goto; // invalid, reserved words
public class Outer {
protected int a;
public class Inner {
protected int a;
public int foo(){
return Outer.this.a;
}
public Outer getOuter(){
return Outer.this;
}
}
}
System.gc()
hints JVM to run garbage collection. An object is collected only when it has no references left. An object becomes eligible for collection as soon as all reference to it are lost.
void finalize()
this method, when overridden in a class, runs when an object is garbage collected. It may NEVER run i.e. program exits before garbage collection.
precedence,
top to down then left to right
operator | symbol | types |
---|---|---|
post unary, Arithmetic | x++, x-- | primitive, except boolean |
pre unary, Arithmetic | ++x, --x | primitive, except boolean |
unary | + - ! |
|
Arithmetic | * / % |
primitive, except boolean |
Arithmetic | + - |
primitive, except boolean |
shift | << >> >>> |
|
comparison | < > <= >= intanceof |
|
== != |
||
Logical | & ^ ` |
` |
Short-circuit logical | && ` |
|
Ternary | expression ? expression1 : expression2 | |
Assignment | = += -= *= /= %= &= ^= != <<= >>= >>>= |
expressions are evaluated from left to right.
int x = 2;
int y = 0;
// IMPORTANT
// here y is incemented to 1 but returns/evalutes old value
y = y++; // y = 0
y = y++ + y; // y = 0 + 1 --> y = 1
y = x++ * ++x; // 2*4=8
y = ++x * x++; // 3*3=9
y = x++ * x++; // 2*3=6
y = ++x * ++x; // 3*4=12
y = x++*++x; // 2*4=8
y = x+++x; // (x++)+x = 2+3
y = ++x-+x; // (++x)-(+x) = 3-3=0
y = x+++x++; // (x++)+(x++) = 2+3
y = ++x+++x; // (++(x++))+x = (++(2))+x => error
if(<boolean expression>)
it only accepts expressions which evaluate to boolean.
<boolean>?<expression value>:<expression value>
ternary
XOR => X^Y => True when only one of them is true, not both. Red pill OR Blue pill.
Switch
Java < 5 = switch(<byte, short, char, int>)
Java 5 = switch(<byte, short, char, int, enum>)
Java 7 = switch(<byte, short, char, int, enum, String, Byte, Short, Char, Integer>)
Switch only supports [byte
--> Integer
] type variables, String
and Enum
.
long
, float
, double
and boolean
are NOT supported.
switch(<enum, String, byte, short, char, int, Byte, Short, Char, Integer>)
{
default: // only if none of the cases match
break;
case <compile time constant, literal, enum, final; assignable to switch variable without a cast>:
break;
}
final int x = 20; // compile time constant
final int y = getInt(); // NOT a compile time constant
final int z; z = 30; // NOT a compile time constant
int a = 5;
switch(a) {
case x: //
case y: // compile error, not a constant
case z: // compile error, not a constant
}
Empty switch, or switch with only default
case or any case is valid
int x = 5
switch(x) {
}
switch(x) {
default:
}
switch(x) {
case 5:
}
If numeric values are of different types, values are automatically promoted.
5 == 5.00 // true
for
for(int x=0, int y=0; x == y; x++) { // does not compile, invalid syntax, type can only be defined once
}
for-each
String[] names = new String[4];
for(String name: names){
System.out.print(name + " "); // prints null 4 times
}
break label;
continue label;
break
breaks the execution of given loop right there.
continue
immediately starts the next iteration of that loop.
LOOP_A: for(int i=0; i<10; i++) {
System.out.println("loop a start");
LOOP_B: for(int j=0; j<10; j++) {
System.out.println("loop b start");
LOOP_C: while(j>5) {
System.out.println("loop c start");
// break == break LOOP_C;
break LOOP_B; // breaks loop b and goes to line 13
}
System.out.println("loop b end");
continue LOOP_A; // continues execution of loop a, goes to line 1
}
System.out.println("loop a end"); // line: 13
}
// labels can also be applied to following constructs
label1: do{
continue label1;
break label1;
} while(true);
label2: if(true){
// NO break,
// NO continue
}
label3: switch(x){
break label3;
// NO continue
}
String s = 0; // error
String s = "1";
String s = "1" + 2; // 12
String s = 0 + "1"; // 01
String s = 1 + 2 + "3" + 4 + 5; // 3345
String s = 1 + 2 + "3" + (4 + 5); // 339
Print string
System.out.println('a'); // a
System.out.println('a' + 'b'); // 195 // because characters will be promoted to int with + operator
Strings are immutable. There is a string pool in JVM also known as intern pool.
"name" // literal, goes in string pool
myobj.toString(); // not a literal, does not go in string pool, garbage collected
String functions
String s = "animals";
// 0123456
s.length(); // 7
s.charAt(1); // n
s.chartAt(7); // exception
s.indexOf('a'); // 0
s.indexOf('a', 1); // 4, start looking from index 1
s.indexOf('x'); // -1
s.indexOf("al"); // 4
s.substring(3); // "mals"
s.substring(string.indexOf('m')); // "mals
s.substring(inclusive, exclusive);
s.substring(3, 4); // "m"
s.substring(3, 7); // "mals"
s.substring(4, 4); // empty
s.substring(4, 3); // exception
s.substring(4, 8); // exception
s.startsWith("a"); // true
s.endsWith("S"); // false
s.contains("l"); // true
s.replace("a", "A"); // AnimAls
s.replace('a','A'); // AnimAls
"\t a b c\n".trim(); // "a b c"
StringBuilder alpha = new StringBuilder();
for(char current = 'a'; current <= 'z'; current++){
alpha.append(current);
}
System.out.println(alpha);
// three ways to create StringBuilder
StringBuilder sb1 = new StringBuilder(); // default capacity 16
StringBuilder sb2 = new StringBuilder("animals"); // string, capcity == str.length() + 16
StringBuilder sb3 = new StringBuilder(10); // init with given capacity 10
// ***** length vs capacity ******//
StringBuilder sb = new StringBuilder();
sb.length(); // 0
sb.capacity(); // 16
// capacity is recalculated if appended string makes length more than capacity
// newCap = oldCap*2+2 >= length ? oldCap*2+2 : length
StringBuilder sb = new StringBuilder(1);
sb.length(); // 0
sb.capacity(); // 1
sb.append("1");
sb.length(); // 1
sb.capacity(); // 1
sb.append("2");
sb.length(); // 2
sb.capacity(); // 4 = 1*2 + 2
// if three chars are added, new cap will become 10 to fit them
// but 9 more chars will overflow capacity therefore cap will be same as length
sb.append("345678901"); // new length is even more than new capacity
sb.length(); // 11
sb.capacity(); // 11 = (10 >= 11 ? 10 : 11)
// insert(offset, ...), insert before given index
StringBuilder sb = new StringBuilder("animals");
sb.insert(7, "-"); // sb = animals-
sb.insert(0, "-"); // sb = -animals-
sb.insert(9, "-"); // sb = -animals--
//
StringBuilder sb = new StringBuilder("animals");
sb.delete(inclusive, exclusive);
sb.delete(1, 3); // sb = amals
sb.deleteCharAt(3); // sb = amas
sb.deleteCharAt(5); // throws an exception
//
sb.reverse(); // sb = sama
int[] arr;
int[] arr = new int[3];
int arr[] = new int[3];
int arr[] = new int[] {1, 2, 3};
int arr[] = {1, 2, 3};
int arr[] = new int[3] {1, 2, 3}; // compile err
int arr1[], num;
int[] arr1, arr2;
int []arr[];
int arr[][];
int[][] arr;
int[][] arr1, arr2; // both 2d arrays
int arr1[], arr2[][]; // 1d and a 2d array
int[] arr1, arr2[]; // 1d and a 2d array
int[][] differentSize = { {1, 4}, {3}, {9,8,7} };
int[][] = { {} }; // compile error
int[][] = { {1, 3}, {} }; // compile error
int[] = {}; // compile error
int arr[][] = new int[3][];
int arr[][] = new int[][3]; // compile err
int arr[][] = new int[][]; // compile err
// sorting
int[] nums = {5, 1, 8, 2};
int[] sorted = Arrays.sort(nums);
// strings sorting
// String sorts in alphabetic order, and 1 sorts before 9. (Numbers sort before
// letters and uppercase sorts before lowercase)
String sorted_strings[] = {"05", "1", "10", "2", "B", "C", "a", "b"}; // 0112BCab
// searching, works correctly only on sorted arrays
int numbers[] = {1,2,5,7,9};
Arrays.binarySearch(numbers, 2); // 1
Arrays.binarySearch(numbers, 6); // -4 = (3*-1 - 1)
ArrayList list1 = new ArrayList();
ArrayList list2 = new ArrayList(10 /*init size*/);
ArrayList list3 = new ArrayList(list2);
ArrayList<String> list4 = new ArrayList<String>();
ArrayList<String> list5 = new ArrayList<>();
List<String> list6 = new ArrayList<>();
ArrayList<String> list7 = new List<>(); // DOES NOT COMPILE
// it DOES NOT store primitives
ArrayList<int> li = new ArrayList<>(); // compile error
// also
ArrayList l = new ArrayList();
l.add(1); // valid
// Add
list.add(index, "item");
list.remove("item"); // true
list.remove("item"); // false
list.set(index, "item");
list.contains("item");
list.clear();
list.isEmpty();
list.size();
ArrayList copy = list.clone(); // shallow copy, points to original elements instead of copying, new elemnts will be added in its own memory
// Convert list to array
List<String> ls = new ArrayList<>();
ls.add("i1");
ls.add("i2");
Object[] arr = ls.toArray();
//
String[] targetArr = new String[2];
String[] strConverted = ls.toArray(targetArr); // if target array size can fit source list, it will be filled with items from list, otherwise new array will be created
System.out.println(strConverted == targetArr); // true
// Convert Array to List
// list is backed by the string array, change in one reflects in both, change in size is not allowed
String[] array = { "i1", "i2" };
List<String> ls = Arrays.asList(array);
ls.set(0, "test");
System.out.println(array[0]); // "test"
array[0] = "what";
System.out.println(ls.get(0)); // "what"
list.add("new"); // unsupported operation exception
// sorting
Collections.sort(list);
length
-- arrays (int[]
, double[]
, String[]
) -- immutable, to know the length of the arrays
length()
-- String related Object (String
, StringBuilder
, etc) -- to know the length of the String
size()
-- Collection Object (ArrayList
, Set
, etc) -- to know the size of the Collection
Now forget about length()
consider just length
and size()
.
length
is not a method, so it completely makes sense that it will not work on objects. It only works on arrays.
size()
its name describes it better and as it is a method, it will be used in the case of those objects who work with collection (collection frameworks) as I said up there.
Now come to length()
:
String is not a primitive array (so we can't use .length
) and also not a Collection (so we cant use .size()
) that's why we also need a different one which is length()
(keep the differences and serve the purpose).
LocalDate; // date only
LocalTime; // time only
LocalDateTime; // date and time
.now(); // current date/time
LocalDate d = LocalDate.of(2018, Month.December, 14);
LocalTime t = LocalTime.of(5, 30, 12/*optional*/, 300/*optional, nanos*/);
LocalDateTime dt = LocalDateTime.of(Y, M, D, h, m, s/*optional*/, ns/*optional*/);
.
.
.
// naming, same as variables
public void mth0_$();
public void $();
public void _();
public void 4(); // illegal
// optional exception
public void method () throws IllegalArgumentException, InterruptedException {}
// varargs
public void walk(int x, int... nums) { System.out.println(nums[0]); }
walk(1, null); // will throw exception because arg array is null
walk(2);
walk(3, new int[]{5, 7});
private < (default) < protected < public
private
Only accessible in same class
default access: Accessible to classes within same package only, C# => internal
protected
accessible within same package with reference (with dot .
) and in child classes only, in C# accessible only in child classes. C# => protected+internal
public
accessible everywhere
package one;
class Bird {
protected void fly(){}
}
package two;
class Swan extends one.Bird {
public void flap(){
fly();
}
public void testFlight(){
Bird b = new Bird();
b.fly(); // compile error
}
}
protected in C# is only accessible through child classes.
static method can be called from a null
instance, C# does not allow accessing static members from instance
class MyObj {
public static int count = 1;
}
MyObj obj = null;
System.out.println(obj.count); // prints 1
static final
must be only given value in definition or only initialized in a static block
static final int a;
static final int b = 2;
static final int c; // compile error
static {
a = 1;
b = 2; // compile error
}
Static methods and fields can be imported using import static
, NOT static import
. can only import static methods, not classes
import static java.util.Arrays.asList;
public class testStaticImport {
public static void main(String... args){
List<Integer> list1 = asList(1, 2, 3, 4, 5);
List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5); // compile error
// reason: missing import java.util.Arrays
}
}
public void run(int distance);
public boolean run(int distance); // compile error, can not overload on differnt return type only
public void run(double distance);
public void run(int distance, double from);
public boolean run(double from, int distance);
Most specific method is picked first
public void show(String str) { System.out.println("String"); }
public void show(Object obj) { System.out.println("Object"); }
show(""); // will print "String"
show(0); // will print "Object"
// order of calling for int literal
int > long > Integer > int...
primitive > larger primitive > autoboxed > vararg
- Initialize super class first
- Static variables and static initializers, in the order they appear
- Instance variables and instance initializers, in the order the appear
- Constructor
public class InitOrder {
private String name = "dog";
private static int c = 0;
{ System.out.println(name); }
static { System.out.println(c); }
static { c += 10; System.out.println(c); }
public InitOrder() {
System.out.println("constructor");
}
}
// output
/*
0
10
dog
constructor
*/
remove setters, input through constructor only
return value data, don't return reference to modifiable object
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html this article is a great explanation of what lambdas are for and how they are used.
Lambda is quick and short implementation of an interface (functional interface which has only one abstract function)
// assume a simple class
class Person {
private int age;
private String name;
private String email;
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public int getAge(){ return age; }
public String getName(){ return name; }
public String getEmail(){ return email; }
public void print(){ }
}
To search all persons matching a specific criteria, we can create an interface to match person and its various implementations for different kind of searches e.g.
public interface MatchPerson {
boolean match(Person p);
}
public class IsElderMale implements MatchPerson {
boolean match(Person p) {
return p.age > 50 && p.gender == Person.Sex.MALE;
}
}
public class IsKid implements MatchPerson {
boolean match(Person p) {
return p.age < 5;
}
}
e
// a -> a.canDo();
// {Animal a} -> { return a.canFly(); }
// {Animal a} -> return a.canFly(); // omit braces because single statement, like if
public interface CheckInterface {
boolean test(Animal a);
}
// old way
public CanFly implements CheckInterface {
public boolean test(Animal a){
return a.canFly();
}
}
public CanHop implements CheckInterface {
public boolean test(Animal a) {
return a.canHop();
}
}
public print(Animal a, CheckInterface c) {
if(c.test(a))
System.out.println("can do");
}
// using lambda
///////////////////
// java provides Predicate<T> for simple test interfaces like CheckInterface
/*
interface Predicate<T> {
boolean test(T t);
}
interface Consumer<T> {
void accept(T t);
}
interface Function<T, R> {
R apply(T t);
}
*/
// using above interfaces provided by java, we can redefine our methods like following
public printPredicate(Animal a, Predicate<Animal> p){
if(p.test(a))
System.out.println("can do");
}
// why just print items filterd with lambda, we can even make print operation generic
public processAnimal(Animal a, Predicate<Animal> p, Consumer<Animal> op) {
if(p.test(a))
op.accept(a);
}
// taking it even further, get result from a function and pass it to Consumer
public processFunction(Animal a, Predicate<Animal> p, Consumer<Integer> op, Function<Animal, Integer> func) {
if(p.test(a)) {
int age = f.apply(a);
op.accept(age);
}
}
// Now example of different print methods, including lambdas
public static void main(String ...args){
Animal a = new Animal();
a.setCanFly(true);
// old ways
// either provide the existing implementation in here
print(a, new CanFly());
// or for a different check, create new anonymous class
print(a, new CheckInterface(){ public boolean test(Animal a){ return a.age() > 5; }} );
// using lambda
print(a, {Animal a} -> { return a.age > 5; });
// short form
print(a, a -> a.age > 5);
// using lambda on predicate in same way
printPredicate(a, a -> a.age > 5);
// taking it one step further
processAnimal(a, a -> a.age > 5, a -> {System.out.println("can do");});
// include Function<T, R>
processFunction(a,
a -> a.age > 5,
age -> {System.out.println("can do"+age);},
a -> a.age
);
}
Either this()
or super()
can only be used in the first line in a constructor. First statement of every constructor is either this()
or super()
.
Default constructor exists only only when no constructor is provided.
In all child class constructors, a hidden super()
call to parent is present if not explicitly provided. This can cause compile error if parent doesn't have a no-argument constructor.
Hidden super()
call can be replaced with an explicit super(...)
or this(...)
call.
public class Mammal {
public Mammal(int age) {} // Constructor
}
public class Elephant extends Mammal {
public Elephant() { // COMPILE ERROR
// super(); // hidden call to default super constructor which does not exist
// can be fixed using super(10);
}
}
public class Dog extends Mammal {
// COMPILE ERROR
// because default constructor of this class has a super() call
}
public class Monkey {
public Monkey(){ this(10); }
public Monkey(int age) { // COMPILE ERROR
// super(); // hidden call to default super constructor which does not exist
// can be fixed using super(10);
}
}
Child class method must have same or more access than parent be it an abstract class or interface. Child method return type must be same or subclass of parent Child can only throw same or subclass of checked exception in parent, or no exception Child cannot throw checked exception that is new or broader than parent Child can re-declare private methods of parent. Child can only override static method with static method, and instance method with instance method. Overriding/hiding static with instance or vice versa is compile error. C# allows doing both ways. Child CAN NOT override/hide final methods
class Person {
Person walk(){ return new Person(); }
}
class Student extends Person {
Student walk(){ return new Student(); } // override with subtype, covariant return type
}
When parent method is called from child, which calls a static method (same in parent and child), an instance method (same in parent and child) and a variable (same in parent and child)
- static parent method is called
- instance method of child is called
- variable of parent is called
Static methods are hidden in child classes because don't belong to an instance. They belong to class itself.
// example
class Parent {
public String i = "parent instance var";
public static String s = "parent static var";
public static void staticmethod(){
System.out.println("parent static method " + s);
}
public void instancemethod(){
System.out.println("parent instance method " + i + " and " + s);
}
public void dad(){
System.out.println("Dad()");
staticmethod();
instancemethod();
}
}
public class Child extends Parent {
public String i = "child instance var";
public static String s = "child static var";
public static void staticmethod(){
System.out.println("child static method " + s);
}
public void instancemethod(){
System.out.println("child instance method " + i + " and " + s);
}
public void son(){
System.out.println("Son()");
staticmethod();
instancemethod();
}
public static void main (String ...args){
Child cc = new Child();
cc.son();
//Son()
//child static method child static var
//child instance method child instance var and child static var
cc.dad();
//Dad()
//parent static method parent static var
//child instance method child instance var and child static var
Parent pc = new Child();
//Dad()
//parent static method parent static var
//child instance method child instance var and child static var
Parent pp = new Parent();
System.out.println(cc.i); // child instance var
System.out.println(pc.i); // parent instance var
System.out.println(pp.i); // parent instance var
}
}
Overridden methods must have same or more access than parent.
interface walkable {
void walk();
}
class Person implements walkable {
walk() {} // Compile error, interface methods are public there this must also be public
}
private
methods are hidden from child class. If called from where private
is visible, it will be called.
// example
class Parent {
private void instancemethod(){
System.out.println("parent private instance method ");
}
public static void main (String ...args){
Parent p1 = new Child();
p1.instancemethod();
// output: parent private instance method
Child c1 = new Child();
c1.instancemethod();
// output: child instance method
// its because child can not see the parents private method
}
}
public class Child extends Parent {
public void instancemethod() {
System.out.println("child instance method ");
}
}
A class or method which is not completely defined is abstract. abstract class
and interface
are two abstract type of classes.
Abstract class = Concrete class + Abstract Methods
Can not be instantiated.
Can not be final
. e.g. abstract final class {}
is invalid.
Can be empty.
Can extend
only concrete or abstract class.
Can implement
interface.
May not provide implementation of inherited abstract
methods from abstract class
or interface
.
Abstract method = abstract <<type>> methodname();
Can not have a defition.
Can not be final
.
Can not be private
.
Only exist in abstract class
and interface
.
Child of abstract class must either provide implementation OR marked as abstract.
public abstract class Walker [extends (concrete|abstract)] {
public abstract void walk();
public abstract void jog();
public void useLegs(){ }
}
// keywords in <<...>> are always assumed if not written explicity
Interface can be empty.
Interface cannot be final.
abstract
keyword is assumed with interface and its methods.
Can contain only constants (final
) which are assumed public static
.
public <<abstract>> interface CanWalk {
<<public>> <<static>> <<final>> int LEGS = 4;
<<public>> <<abstract>> void walk();
<<public>> default void useLegs(){ }
<<public>> static int getlegs() { return 4; }
}
public interface CanRun extends CanWalk {
<<public>> void useLegs()<{}>;
}
// keywords in <<...>> are always assumed if not written explicity
interface extends interface
Interface cannot implement another interface.
[abstract] class implements interface
Class cannot extend interface.
static methods are not inherited and are only referenced using interface name.
Interfaces with same method signatures can not be applied on a class until that class redefines that method.
All non-final, non-static, non-private methods are virtual methods in Java because they can be overridden at runtime.
interface House{
public default String getAddress() {
return "101 Main Str";
}
}
interface Hotel {
public default String getAddress() {
return "Regent Str";
}
}
interface Bungalow extends House {
public default String getAddress() {
return "101 Smart Str";
}
}
class MyHouse implements Bungalow, House { } // No problem here because Bunglow is already a House
class BizzareBuilding implements Hotel, House { } // Compile error, unrelated method getAddress() is inherited from both House and Hotel
public class TestClass {
public static void main(String[] args) {
House ci = new MyHouse();
System.out.println(ci.getAddress());
}
}
input for graph-easy
[C: Throwable] -> [U: Error] -> [StackOverflowError]
[C: Throwable] -> [C: Exception] -> [U: RuntimeException] -> [NullPointerException]
[U: RuntimeException] -> [ClassCastException]
[U: RuntimeException] -> [ArithimeticException]
[U: RuntimeException] -> [IndexOutOfBoundsException] -> [ArrayIndexOutOfBoundsException]
[U: RuntimeException] -> [SecurityException]
[U: RuntimeException] -> [IllegalArgumentException] -> [NumberFormatException]
[U: RuntimeException] -> [IllegalStateException]
[U: Error] -> [AssertionError]
[U: Error] -> [NoClassDef]
[C: Exception] -> [C: IOException] -> [FileNotFoundException]
C = Checked
U = Unchecked
+----------------+ +-----------------------+ +---------------------------+ +--------------------------------+
| C: IOException | --> | FileNotFoundException | | IndexOutOfBoundsException | --> | ArrayIndexOutOfBoundsException |
+----------------+ +-----------------------+ +---------------------------+ +--------------------------------+
^ ^
| |
| |
+--------------+ +----------------+ +---------------------------------------------------------+ +--------------------------------+ +-----------------------+
| C: Throwable | --> | C: Exception | --> | | --> | IllegalArgumentException | --> | NumberFormatException |
+--------------+ +----------------+ | | +--------------------------------+ +-----------------------+
| | |
| | U: RuntimeException | ---------------------------------------------+
v | | v
+--------------------+ +--------------+ +----------------+ | | +--------------------------------+ +-----------------------+
| StackOverflowError | <-- | U: Error | --> | AssertionError | | | --> | ArithimeticException | | NullPointerException |
+--------------------+ +--------------+ +----------------+ +---------------------------------------------------------+ +--------------------------------+ +-----------------------+
| | | |
| | | |
v v | v
+--------------+ +-----------------------+ | +---------------------------+
| NoClassDef | | ClassCastException | | | IllegalStateException |
+--------------+ +-----------------------+ | +---------------------------+
+-----------------------+ |
| SecurityException | <+
+-----------------------+
Errors are thrown by JVM and should not be handled or declared.
Some known errors are ExceptionInInitializerError
, StackOverflowError
, NoClassDefFoundError
// correct order of try catch finally
try {}
catch() {}
finally {}
// catch is optional if finally is present
try {}
finally {}
Exceptions are handled by catch
. If not handled, they will be thrown.
public static void main(String ... arg) {
try {
System.out.println("try");
throw new IndexOutOfBoundsException();
}
finally {
System.out.println("finally");
}
}
// try
// finally
//
// Exception in thread "main" java.lang.IndexOutOfBoundsException at MyClass.main(MyClass.java:5)
Methods throwing checked exceptions must be written within try catch
block.
Methods throwing unchecked exception do not need it.
All exceptions under RuntimeException
and Error
are unchecked, all remaining exceptions including Throwable are checked exceptions
Most specific exceptions must be catched first. If parent exception is handling all further exceptions, other catch blocks are uncreachable.
try {
badcode();
} catch (Throwable e) {
// handle exception
} catch (Exception e) { // compile error, unreachable code
// handle exception
}
Doing it the other way is fine
public void badcode() throws RuntimeException{}
try {
badcode();
} catch (RuntimeException e) {
// handle exception
} catch (Exception e) {
// handle exception
} catch (Throwable e) {
// handle exception
}
If we try to catch a checked exception which is not in try block, catch block is unreachable
public void goodcode(){}
try {
goodcode(); // does not throw any exception
} catch (java.io.IOException e) { // compile error, unreachable code
// handle exception
}
Overriding method CANNOT throw checked exception that is new or broader than parent. It can throw less exceptions than parent.
Child constructors, on the other hand, must throw same or broader exceptions than parent.
Reason: there is a hidden super() call to the parent constructor.
import java.io.*;
class CanNotHopException extends Exception {}
class Hopper {
Hopper() throws CanNotHopException { } // parent constructor exception
public void hop() throws CanNotHopException { }
public void jump() throws CanNotHopException { }
}
class Bunny extends Hopper {
Bunny() throws Exception { } // broader exception in constructor is valid
Bunny(int x) throws Exception, CanNotHopException, IOException { } // new exceptions are valid
Bunny(int x, int y) { } // Compile error, CanNotHopException must be caught or declared to be thrown
public void hop() { } // valid
public void jump() throws CanNotHopException, IOException { } // compile error, overridden method hop() does not throw IOException
}
and
class CanNotHopException extends Exception {}
class Hopper {
public void hop() throws Exception { }
}
class Bunny extends Hopper {
public void hop() throws [CanNotHopException|Exception| ] { }
}
This is only for checked exceptions. Unchecked exceptions can be declared anywhere. No declaration is required.
class Hopper {
public void hop() { }
}
class Bunny extends Hopper {
public void hop() { throw new IllegalStateException(); }
}
inheritance
default constructor is created only if none is given in a class
there is a call to default super constructor if not called inside any child constructor
public class A {
// if no constructor defined
// then following is generated
// a() {}
}
public class B extends A {
B(){
// if no call to super constructor explicitly
// then contains following by default
// super();
}
}
public class C extends A {
// no constructor defined
// then following is generated
// C(){
// super();
// }
}
functional interface
@FunctionalInterface
public interface SimpleFuncInterface {
// only one abstract method
public void doWork();
// any number of default methds
default public void print(int i){System.out.println(i);}
// any number of static methods
static int pi(){return 3.17;}
}
interface
public interface A {
public /*static final*/ int a = 10;
// fields in interface are by default public static final
// but it is not usally explicitly written.
}
C# does not allow fields or constants in interface
overriding
class ShapeBuilder {
...
public Shape build(String name) { }
....
}****
class CircleBuilder extends ShapeBuilder{
...
@Override
public Circle build(String name) { } // Circle is subtype of Shape
....
}
overriding method can opt to not throw any exception if parent method throws exception, this is because no exception is valid subset of exceptions.
types
integral types: byte, short, char, int, long,
type | ||
---|---|---|
byte | -128 | 127 |
short | -32,768 | 32,767 |
char | 0 | 65,535 |
int | -2,147,483,648 | 2,147,483,647 |
casting
casting from type A to type B when A can already fit in B. e.g. byte, char or short to int, long or float does not need a cast.
operators
*, / and % have the same level of precedence.
bitwise operators &, ^, | have specified behaviour when used on boolean
When both operands of a
&
,^
, or|
operator are of typeboolean
orBoolean
, then the type of the bitwise operator expression isboolean
. In all cases, the operands are subject to unboxing conversion (§5.1.8) as necessary.
For
&
, the result value istrue
if both operand values aretrue
; otherwise, the result isfalse
.
For
|
, the result value isfalse
if both operand values arefalse
; otherwise, the result istrue
.
For
^
, the result value istrue
if the operand values are different; otherwise, the result isfalse
.
short-circuit
boolean a(){return true;}
boolean b(){return false;}
boolean c = b() && a(); // short-circuit, a() is not called
boolean d = b() & a(); // no short-circuit, both b() and a() are called
None > Widening > Autoboxing > Varargs
static void m(int l){System.out.println("nothing");}
static void m(long l){System.out.println("Widening Long");}
static void m(Integer i){System.out.println("Boxing Integer");}
static void m(Integer... i){System.out.println("Varargs Integer");}
public static void main(String args[]){
int a=30;
m(a);
}
public > protected > package (default) > private