Comprehensive Guide to Java Programming: Part III Unveiled
Written on
Chapter 1: Introduction to Java Programming
This series of blogs is designed to simplify your journey in learning Java programming and its core concepts. Whether you're completely new to Java or an experienced programmer looking to refresh your knowledge, this guide is tailored for you.
Threads in Java
A thread represents a lightweight process, and a single process can house multiple threads. You can utilize threads in Java by either extending the Thread class or implementing the Runnable interface.
For example:
public class Test extends Thread {
public void run() {
// logic}
public static void main(String[] args) {
Thread t1 = new Test();
// Thread t1 = new Thread(new Test()); FOR RUNNABLE
t1.start();
}
}
It's worth noting that implementing Runnable is often preferred, as it facilitates multiple inheritance. The start() method invokes the run() method; however, calling run() directly does not initiate a new thread. Instead, it executes in the current thread, behaving like any other method call.
Thread Control Mechanisms
- Object.wait(): Suspends the current thread until notify is invoked.
- Object.notify(): Awakens a single thread waiting on this object's monitor, placing it in runnable mode.
- Object.notifyAll(): Awakens all threads waiting on this object's monitor.
- Thread.yield(): Places the current thread in a runnable state and allows the next thread to execute.
- Thread.sleep(): Pauses the thread for a specified duration.
Daemon Threads
Daemon threads operate in the background at a lower priority, such as garbage collection threads.
Avoiding Deadlocks
To prevent deadlocks, refrain from synchronizing code that includes blocking calls.
Synchronization Tools: Mutex and Semaphore
- Semaphore: A flexible locking mechanism with a set maximum count, used for managing concurrent resource access.
public class Semaphore {
private int value;
public Semaphore(int init) {
value = Math.max(0, init);}
public synchronized void down() {
while (value == 0) {
try {
wait();} catch (InterruptedException e) {
Thread.currentThread().interrupt();}
}
value--;
}
public synchronized void up() {
value++;
notify();
}
}
- Mutex: A specialized lock that can be held by only one thread at a time. Only the thread that acquired the mutex can release it.
Error Handling in Java
Errors typically arise from dynamic linking failures or hardware issues, which are uncommon. Unchecked exceptions are runtime exceptions that occur during JVM execution, such as NullPointerException.
Custom Exception Example:
public class MyException extends Exception {
private String errorCode = "Unknown_Exception";
public MyException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}
public String getErrorCode() {
return this.errorCode;}
}
Utilize the throw keyword to raise exceptions during runtime. If an exception is thrown from a method without handling it, use the throws keyword in the method signature.
Observable and Observer Patterns
The Observer design pattern is beneficial for notifying multiple objects (Observers) when an event occurs in one instance (Observable). This pattern is commonly utilized in AJAX to inform several objects of an event.
Steps to Implement:
- Create an Observable class.
- Define Observer classes.
- Register Observers to the Observable object using addObserver.
- Invoke notifyObservers to alert all observers.
JDBC: Connecting to Databases
JDBC (Java Database Connectivity) is an API in Java that facilitates connecting to databases and executing queries using JDBC drivers.
Types of JDBC Statements:
- Statement: Compiles the query each time it is executed.
- PreparedStatement: Compiles the query once, enhancing performance.
- CallableStatement: Utilized for executing stored procedures and functions.
File Reading and Writing
- Byte Streams: For handling binary data.
- Character Streams: Designed for processing characters instead of bytes.
Reading from a File Example:
public static void readFromFile(String fileName) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(fileName));
String line;
while ((line = in.readLine()) != null) {
// Process each line}
in.close();
}
Writing to a File Example:
// Using FileOutputStream
FileOutputStream fos = new FileOutputStream("file_location");
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(fos));
out.write("something");
out.close();
// Using FileWriter
FileWriter fstream = new FileWriter("file_location");
BufferedWriter outWriter = new BufferedWriter(fstream);
outWriter.write("something");
outWriter.close();
Note: FileOutputStream is used for writing raw bytes, whereas FileWriter is meant for character streams.
Design Patterns in Java
- Singleton Pattern: Guarantees a class has only one instance, providing a global access point.
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;}
}
- Factory Pattern: Creates objects without revealing the instantiation logic to the client.
interface Dog {
void speak();
}
class Poodle implements Dog {
public void speak() {
System.out.println("The poodle says "arf"");}
}
class Rottweiler implements Dog {
public void speak() {
System.out.println("The Rottweiler says "WOOF!"");}
}
class DogFactory {
public static Dog getDog(String criteria) {
if (criteria.equals("small"))
return new Poodle();else if (criteria.equals("big"))
return new Rottweiler();return null;
}
}
public class JavaFactoryPatternExample {
public static void main(String[] args) {
Dog dog = DogFactory.getDog("small");
dog.speak();
dog = DogFactory.getDog("big");
dog.speak();
}
}
- Adapter Pattern: Facilitates interaction between two incompatible interfaces.
- Builder Pattern: Enhances the Factory pattern to construct complex objects in a stepwise manner.
Miscellaneous Concepts
- Synchronization: Manages access to shared resources among multiple threads.
- Thread Dumps: Provide a comprehensive list of active threads.
- Thread Leaks: Occur when an application fails to release references to thread objects.
- Thread Pools: A collection of threads where tasks can be scheduled.
- Cluster: A group of computers that can independently run software.
- Load Balancing: Distributes workloads across multiple machines or clusters.
- Failover: The process of switching to a backup machine upon failure.
- Pass-by-Value: Java passes object references by value.
- Arrays.sort(): Implements merge sort or a tuned quicksort.
Common Java Questions
- Why do we have the main() function?: It serves as the starting point for program execution.
- Why is the main method public?: To ensure accessibility to the JVM, which initiates execution from outside the class.
- Why is the main method static?: It allows execution without instantiating an object.
- What is the purpose of String args[]?: These are command-line arguments, a design decision made by Java's creators.
- What is the function of out in System.out.println()?: out is an object of the PrintStream class, a static member of the System class.
- How is Random implemented in Java?: The java.util.Random class employs a linear congruential generator (LCG).
- What is regex in Java?: A regular expression defines a pattern for searching strings.
The first video, "Guide and Tips for Java," offers strategies and insights for mastering Java programming, making it ideal for beginners and seasoned developers alike.
The second video, "Introduction To Java for Beginners Part 3: Packages and Imports," provides an essential overview of Java packages and import statements, crucial for structuring your Java applications effectively.