Java History
Originally named oak
- First release 1995
- 1996 JDK 1.0
- 1997 JDK 1.1(Reflection,JDBC)
- 1998 J2SE1.2(JIT compiler)
- 2000 J2SE1.32
- 2002 J2SE1.4
- 2004 J2SE1.5(5.0) or called Java 5 -major change(generics,enumerations)
- 2006 Java 6
- 2010 - Oracle bought Sun
- 2011 - Java 7
- 2014 - Java 8
Java Architecture
- Compiles to byte code, instead of machine code
- Runs on protected JVM
- Call to native functions goes through Java Native Interface(JNI)
- Managed memory access
- Automatic garbage collection
- javac - a command line compiler
- java - a command line runtime
- jar - the packer(packages the files to .jar)
- javadoc - the documentation builder
Running from command line:
- go to dir
- javac filename.java or javac filename.java -verbose
- java class_name
- If we need to run a java file with a package, then javac -d destination_folder filename.java(with automatically create a folder structure same as the package name)
Java Basics
Everything is an object.
Assign it to null to make it explicitly make it eligible for garbage collection.
Garbage collector runs in its own thread.
Programmer cant force garbage collection can only request System.gc(), Runtime.gc() - no guarantee it will happen If out of memory JVM throws OutOfMemoryError.
Java is a strongly typed language.
Variables:
2 types: Primitive - Numerics(byte,short,int,long,float,double), Single characters, boolean. Note: All primitive numerics defaults to zero. Complex objects - Strings, Dates.
Strings:
Can be seen as an obj or array of characters. String name = “sailesh”/new String(“sailesh”)/new String({‘s’,’a’,’i’,’l’,’e’,’s’,’h’});
There are equivalent helper classes. Use BigDecimal class when you need precision, ie say working with currency values.
Type conversion:
Upward: double <- float <- long <- int <- short <- byte
Upward type conversion is automatic in java. Downward is not automatic, compiler will show compile-time error,so we have to explicitly mention the type cast.
Operators:
instanceof,&&,||
Comparing strings:
== wont work(Beacuse String is a complex obj and == checks if the two strings are same objects and not their values)
str1.equals(str2)
For primitive data types “some string” + var, implicit conversion of var happens to string, in case of comple objects like date, Java looks for a method called toString()
Switch:
Break will stop fall-through, its not compulsory. Can use it on ints,enums and in Java 7 can use it on Strings.
foreach:
for(String singlename : collectionname){
}
Methods:
private void hello(double...values){
} //means can accept any no of doubles as argument, ie it will treat values as an array
Can overload methods with same name but different no or kind of params.
Copy and reference:
In Java variables are always passed by copy.
Some Java programmers may say that passing primitives to functions is passed by copy but for complex objs its passed by reference, no its not.
Consider, a complex array,ie say an int[] array sample. When sample[0]=3 and passed to a function and say ++sample[0], now outside the function sample[0] will be 4.
How? Reference variable,ie complex objects points to a location in memory. When a varible is passed to a function, a new refernce is always created. But both references point to the same location internally, that is why changes made in arrays(complex objs) inside a function are reflected back in the main.
Strings:
Strings are comple objs, but in Java all strings are immutable. Once you create its value you cant change it. With strings again its always passed by copy to the functions, but the new reference obj created inside the function cant refer to the original obj internally, beacuse strings are immutable.
For all complex objs except strings the refernce of the new obj created inside the functions internally points to the same memory location. The behaviour is consistent, throughout Java.
Use .length() method on strings to find their length.
String, StringBuilder, StringBuffer:
//can easily convert a String to an arry of characters
char[] chars = str.toCharArray();
// because Strings are immutable internally a new String obj is actually created
s1 = s1 + "append"
//Better to use StringBuilder always while dealing with strings, as it takes less memory and resources, but only used for single threaded environments, use StringBuffer for synchronization between multiple threads
StringBuilder sb = new StringBuilder(str);
str.append("append this value");
Exceptions:
try{
}
catch(Exception e){
}
Throws:
public static void hello() throws ArrayIndexOutOfBoundsException{
// something
}
//while calling this method surround it with try-catch block
Debugging:
Set breakpoint and use the debugger to inspect variable values and find errors.
Arrays:
They must be of the same type. Also its always of fixed size, we cant change its size in runtime, if you want such behaviour then look at ArrayList.
int[] name = new int[3];
OR int name[] = new int[3];
OR int[] name = {3,6,9};
OR int name[] = {3,6,9};
Mutltidimensional array:
Same as array,they must be of the same type. Also its always of fixed size, we cant change its size in runtime.
int[][] //use for inside for
ArrayList:
<> operator called the diamond operator is used in Java for generics.
ArrayList<String> list = new ArrayList<string>();
java.util.ArrayList // add this import statement
list.add("sail");
list.add("raj");
list.add("dev");
list.remove("sail"); // OR list.remove(0);
list.get(1); //get element from list, its zero index based
list.indexOf("raj"); //returns the index
HashMap(Used to store unordered collection of data):
Its a part of java.util.ArrayList
HashMap<String, String> = new HashMap<String,String>();
//types can be custom objects also
map.put("raj","iOS");
map.put("sail","ruby");
map.get("sail"); //returns the str based on the key
map.remove("raj"); //removes based on key
Iterators to loop through ArrayList and Hashmap:
For an ArrayList the iterator is called ListIterator
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String value = listIterator.next();
System.out.println(value);
}
In HashMaps its two steps: We need to get the collection of Strings we need to loop through. Lets loop through the keys. Next we need to get the iterator.
Set<String> keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while(iterator.hasNext()){
String value = iterator.next();
System.out.println("The value of the key " + value + "is: " + map.get(value));
}
Overriding methods of super class:
@override
is not necessary but recommended. super() in constructor of sub class calls the constructor of the super class. super().method_name() calls the method of the super class, note only public and protected methods of the super class can be called.
Interfaces in Java:
- Lets us define the structure of the class
- A class implements an interface
- Methods should not have implementation and can only be public, cant be private
File handling:
Can use native code like InputStream,OutputStream and exception handling OR Use FileUtils of Apache commons.
How to add external jar to project:
- Download the binary file in zip format
- Unzip it
- Now open the folder and copy the jar
- Go to the project dir and create a folder called lib
- Paste the jar file here
- Open eclipse and refresh the project
- Right click on the jar and Build path > Add to build path
- Now import the package and use the required classes
Use Apache commons for common java tasks, provides common java tasks.
Adding an external jar to the project:
- Download the jar
- Create a folder named lib/ in the root of the project add the jar file here
- In eclipse refresh the project and right click the jar file and build path>add to build path
Create jar:
- jar file is a java archive file
- Go the file>export>java>jar file>select the resources to package(make sure you select the entire project, deselect .classpath and .project as they are not needed, they are eclipse config files)
- Click finish and the new jar file will be created
See the contents of a jar file:
- Double click the jar file and rename it to .zip file
- Now upzip the file to see the files inside it
Javadocs:
- We can generate it out of our source code
- We need to apply element comments
- Select the element, go to source>Generate Element comment(There is a keyboard shortcut for it)
- We can add html tags like
<b>
,etc in element comments - Go to file>export>java>javadocs>click finish
- Open index.html in the destimation folder to see the documentation
Java 7 features
The diamond operator’s data type only needs to be set once.
Old way: ArrayList<String> list = new ArrayList<String>();
New way: ArrayList<String> list = new ArrayList<>();
try-with-resourses eliminates a lot of code:
Old way: java FileStream s; try{ .... } catch(Exception e){ ..... } finally{ stream.close(); }
New way: java try(FileStream s = new FileStream(....)){ .... } catch(Exception e){ .... }
Numeric literals can now include underscores to make it easier to read:
Old way: int largeValue = 1000000000;
New way: int largeValue = 1_000_000_000;
Use String values in switch statements. New file system API with classes Path, Paths, Files, FileSystem and more. New support dynamic languages.
Java Class Structures
Static final fields:
public static final long BLACK=0X000000;
Static initializers:
public static ArrayList<Olive> olives;
//static initializer
static{
//any code that we type here will be executed the first time any static fields of thi class are referenced anywhere inside the application
}
Instance field initializers:
public static ArrayList<Olive> olives;
//just a code block without any modifier
{
olives = new ArrayList<>();
olives.add(new Olive("Hello",123));
}
What is the use of this? We can put the same thing inside a constructor. The answer is when we have multiple constructor methods, this code block gets executed no matter what constaructor is invoked.
Member classes:
public class Hello{
class Olive{
....}
}
We can now define classes that are only available within the containing class.
Inner classes:
Java lets us define classes within methods,ie within a code block. If the purpose of the class is only within the scope of a method then we can define local inner class.
2 ways to define them: 1. Local classes 2. Anonymous classes
Local inner classes:
public void reportOlives(){
//this class is visible only within the code block
class jarLid{
// the fields and methods in this class cant be static then have to be instance vars in this context
}
}
Anonymous Inner classes:
//Doesnt have a name. It is defined and used exactly once.
new name_of_class_from_which_will_be_extended(){
// the parenthesis above is the constructor
public void method_name(){
....
....
}
}.method_name();
Example:
new Object{
//so we are inheriting from the Object class which is the super-duper class of everything
public void hello(){
....
....
}.hello();
}
Generic programming:
<> - diamond operator
T[] datastore = (T[]) new Object[size];
Integer[] sample = new Integer[]{1,2,3,4,5,6}
For classes: public class Name<T>{ ....}
For methods: public static <T> void display(T[] arr){......}
Generic class with 2 generic type paramaters: public class Pair<T1,T2>{......}
Important interfaces - Collection, List, Set, Queue, Deque
Queue is FIFO Deque can be used as a queue(FIFO) or as a stack(LIFO)
ArrayList:
- Dynamic size
- Implements List interface
- Allows duplicate values to be stored
- Ordered collection and elements added can be accessed using index, get(index), remove(index)
HashSet:
- Dynamic size
- Implements Set interface
- Does not allow duplicate values
- HashSet is an unordered collection and doesn’t maintain any order.no get method, remove(element)
import java.util.Hashset;
HashSet<String> names = new HashSet<String>();
names.add("hello");
names.size() - gives the size of the set, useful for looping
name.remove("hello");
name.contains("hello") - returns a boolean
name.clear(); //claers out all the names in the set
TreeSet class:
TreeSet class differs from the HashSet class in the sense that when we store them we store them in natural sorted order, ie alphabetical order for Strings and ascending order for numbers
//mostly used to filter out unique values as Set class doesnt allow multiple duplicate values
import java.util.TreeSet;
TreeSet<String> names = new TreeSet<String>();
names.add("hello");
names.add("miss");
names.add("me");
names.size(); //.length is not avail in TreeSet
Names are stored in alphabetical order in TreeSet. Some methods of TreeSet class apart from contains,add,remove,etc:
names.lower("sailesh"); //gives name below sailesh in the set
names.higher("sailesh"); //gives name after sailesh in the set
names.first(); //returns the first element of the list
names.last(); //returns the last element of the list
PriorityQueue class:
Works similar to a queue but the data is stored inside in natural sorted order,ie alphabetic order for String and chars and ascensding order for numbers.
PriorityQueue<String> names = new PriorityQueue<String>();
names.add("hello");
names.add("miss");
names.add("me");
names.poll();//removes and returns the first element(head) in the queue
names.remove("Raymond");
names.peek();//return the head of the queue
Associative collections(Map interface):
TreeMap class:
import.util.TreeMap;
TreeMap<String,String> names = new TreeMap<String,String>();
names.put("David","3223");
names.put("Sailesh","8976");
names.put("Raj","1234");
names.get("Sailesh"); //specify the key in get method to get the corresponding value
TreeMap is an example of a SortedMap, and when iterating over the keys, you can expect that they will be in order unlike HashMap. Hashmap is time-efficient, TreeMap is space efficient.
Arrays.sort(array_name); //there is sort method avail in Arrays class
Stack(LIFO):
Stack doesnt take a type parameter unlike other collections,ie
Stack names = new Stack();
names.push("Sailesh"); //adds element to the top of the stack
names.peek();//returns the head or top element of the stack
names.empty(); //boolean value, empty or not
names.pop();// return the top element of the stack and removes it
LinkedList:
Lets do a queue implementation using LinkedList
import java.util.LinkedList;
LinkedList<String> names = new LinkedList<String>();
names.addLast("Sailesh");
names.removeFirst();
names.size();
names.empty(); //boolean value
Sorting
Insertion sort:
for(int i = 0; i < size; ++i)
for(int j = i; j > 0; --j)
if(data[j-1] > data[j])
//swap the values
Bubble Sort:
for(int pass = 1; pass < arr.length; pass++)
for (int i = 0; i < size-pass; i++)
if (arr[i] > arr[i+1])
//swap values
Both of the above sorting algorithms are efficient only for small set of data, say n<1000 elements, they become inefficient for large sets of data
Merge Sort:
public static void mergeSort(int[] arr, int low, int high) {
int size = arr.length;
if (low < high) {
int middle = (low + high) / 2;
mergeSort(arr, low, middle);
mergeSort(arr, middle+1, high);
merge(arr, low, middle, high);
}
}
public static void merge(int[] arr, int low, int middle, int high) {
int size = arr.length;
int[] temp = new int[size];
for(int i = low; i <= high; ++i) {
temp[i] = arr[i];
}
int i = low;
int j = middle + 1;
int k = low;
while (i <= middle && j <= high) {
if (temp[i] <= temp[j]) {
arr[k] = temp[i];
++i;
}
else {
arr[k] = temp[j];
++j;
}
++k;
}
while (i <= middle) {
arr[k] = temp[i];
++k;
++i;
}
}
Quick Sort:
public static void quickSort(int[] arr, int left, int right) {
int i = left;
int j = right;
int temp;
int pivot = arr[(left+right)/2];
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
}
Both the above sorting methods are very efficiently and works smoothly for large data sets.
Binary Search:
public static int binarySearch(int[] arr, int key) {
int first = 0;
int last = arr.length-1;
while (first <= last) {
int mid = (first+last) / 2;
if (key > arr[mid]) {
first = mid + 1;
}
else if (key < arr[mid]) {
last = mid - 1;
}
else {
return mid;
}
}
return -1;
}
Time taken to execute a code block:
long startTime = Sytem.nanoTime();
// code goes here
long stopTime = Sytem.nanoTime();
long duration = stopTime - startTime;
System.out.println("The time taken to execute the code block in nanoseconds is: " + duration);
Randomize:
int a = (int) (Math.random() * 100) //returns a no from 0 to 100
Testing and Exception Handling
- assert - not a method its a keyword
- We must add the command line parameter to the virtual machine to support assert
- Debug icon>debug configurations>Java Application>Arguments tab>Type -ea in the VM arguments panel
Assert condition
If the condition evaluates to true then no problem, the next line of code executes. If it evaluates to false, then it raises an exception
Defining and throwing custom exceptions:
public class NameOfException extends Exception{
public static final long serialVersionUID=42L;
//this is necessary and can be assigned to any long integer
@Override
public String getMessage(){
//this method overrides the default getMessage() func to fill in the message to be display if this exception is thrown
return "You chose the wrong file!";
}
}
//this is how you throw your own defined custom exception
throw(new NameOfException());