📘 Advanced Programming (Java) – Week 1
🎓 Lecture 1: Building GUI Through Drag and Drop, Use
of Event Listeners
🔹 PART 1: Building GUI Through Drag and Drop
(IDE-based GUI Building)
🔷 What is GUI (Graphical User Interface)?
A GUI (Graphical User Interface) is a user-friendly way of interacting with a computer program
through graphical elements like:
● Buttons
● Menus
● Text fields
● Checkboxes
● Tables
● Sliders
Instead of using commands typed in a terminal (CLI), users interact through windows, forms,
and mouse clicks.
🔷 Java GUI Frameworks
Framework Description Thread Safety
AWT (Abstract Window First GUI library in Java. Platform-dependent (uses Partially
Toolkit) native OS components).
Swing Lightweight components written in Java. Fully Not thread-safe by
customizable and portable. default
JavaFX Modern GUI framework with better graphics and Mostly thread-safe
media capabilities.
🔷 Drag and Drop GUI Building (with IDEs)
Instead of writing the entire GUI code manually, you can use visual tools within IDEs to design
GUI.
🔹 Key IDEs and Tools:
IDE Tool Description
NetBeans GUI Builder (Matisse) Drag and drop Swing components
IntelliJ IDEA UI Designer Form editor for Swing GUIs
Eclipse WindowBuilder Plugin Visual design of AWT/Swing/JavaFX
🔹 Process of Drag-and-Drop GUI Building:
1. Open a new JFrame Form or JavaFX Form.
2. Drag UI components from the palette (like JButton, JTextField).
3. Position components using built-in layout managers.
4. Configure properties (text, font, color, variable name) in the properties pane.
5. Double-click components to auto-generate event handler methods.
✅ Code Example (Generated by IDE):
private void btnSubmitActionPerformed(java.awt.event.ActionEvent evt)
{
// This is the auto-generated event handling method
JOptionPane.showMessageDialog(this, "Submit button clicked!");
}
📝 Annotation:
● btnSubmitActionPerformed is automatically named based on the button name
and event type.
● evt is an instance of ActionEvent, holding event metadata.
● this refers to the enclosing JFrame instance.
✅ Advantages of Drag-and-Drop GUI Design:
● 🚀 Rapid Development: GUI can be created visually without writing every line manually.
● 📐 Layout Consistency: IDE manages layout rules and resizing behavior.
● 🧠 Reduced Learning Curve: Beginners can focus on logic, not GUI details.
● 🔄 Automatic Code Generation: Reduces chances of syntax errors.
🔹 PART 2: Use of Event Listeners in Java
🔷 Event-Driven Programming
Java uses the Event Delegation Model, where an object (source) generates an event, and
another object (listener) handles it.
🧠 Key Concepts:
● Event Source: GUI component (e.g., JButton) that generates events.
● Event Object: Encapsulates info about the event (e.g., ActionEvent).
● Listener Interface: Defines method(s) to respond to the event (e.g.,
actionPerformed()).
● Event Handler: The code that is run when the event occurs.
🔷 Common Event Listener Interfaces
Listener Interface Description Event Type
ActionListener Listens for button clicks, menu item selections ActionEvent
MouseListener Detects mouse clicks, enters, exits, etc. MouseEvent
KeyListener Detects key presses, releases, and types KeyEvent
WindowListener Detects window events (open, close, focus) WindowEvent
✅ Code Walkthrough: Adding ActionListener to a Button
JButton btn = new JButton("Click Me");
// Registering listener using anonymous class
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked!");
}
});
📝 Annotation:
● addActionListener(...) registers the listener.
● ActionEvent e gives details like source component, timestamp.
● Anonymous class simplifies defining the listener inline.
✅ Lambda Expression (Java 8+)
btn.addActionListener(e -> System.out.println("Button clicked using
lambda!"));
🧠 Great for reducing boilerplate and increasing readability.
🔄 Event Flow Diagram:
[User clicks button]
↓
[Button (source) creates ActionEvent]
↓
[Event sent to registered ActionListener]
↓
[actionPerformed() method executes]
🎓 Lecture 2: Fully Functional GUIs, Layout Managers,
Wrapper Classes, Strings
🔹 PART 3: Functional GUIs With Layout Managers and
Event Listeners
🔷 What is a Layout Manager?
A Layout Manager automatically arranges GUI components inside a container (like JPanel or
JFrame) according to rules.
🔹 Why use them?
● Makes GUI scalable and portable.
● Handles dynamic resizing.
● Reduces use of absolute positioning (setBounds).
🔷 Important Layout Managers in Swing
Layout Description Example
FlowLayout Left-to-right flow (like words in a new FlowLayout()
sentence)
BorderLayout Divides into 5 regions: N, S, E, W, new BorderLayout()
Center
GridLayout Uniform grid of rows × columns new GridLayout(2, 2)
BoxLayout Aligns components vertically or new BoxLayout(panel,
horizontally BoxLayout.Y_AXIS)
GridBagLayout Most flexible, allows fine-grained control Complex, but powerful
✅ Code Example: Using BorderLayout
JFrame frame = new JFrame("BorderLayout Demo");
frame.setLayout(new BorderLayout());
frame.add(new JButton("North"), BorderLayout.NORTH);
frame.add(new JButton("South"), BorderLayout.SOUTH);
frame.add(new JButton("Center"), BorderLayout.CENTER);
frame.setSize(300, 200);
frame.setVisible(true);
🧠 Each region resizes dynamically.
🔹 PART 4: Java Wrapper Classes
🔷 Why Do We Need Wrapper Classes?
● Primitive types (int, float) are not objects.
● Collections (like ArrayList) store only objects.
● Wrapper classes provide object-oriented representation of primitives.
🔷 List of Wrapper Classes
Primitive Type Wrapper Class
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
🔷 Autoboxing and Unboxing
● Autoboxing: int → Integer
Integer num = 10; // Automatically converted from int
● Unboxing: Integer → int
int x = num; // Automatically converted from Integer
✅ Wrapper Methods
int x = Integer.parseInt("123");
boolean b = Boolean.parseBoolean("true");
char ch = Character.toUpperCase('a');
🧠 These methods are often used in input processing.
🔹 PART 5: Java String, StringBuffer, StringBuilder
🔷 String Class (Immutable)
● Once created, cannot be changed.
● "hello".concat(" world") → creates a new string.
✅ Example:
String s1 = "Hello";
String s2 = s1.concat(" Java");
System.out.println(s1); // Hello
System.out.println(s2); // Hello Java
🔷 StringBuffer vs StringBuilder
Feature StringBuffer StringBuilder
Mutability Mutable Mutable
Thread-Safe Yes No
Performance Slower (synchronized) Faster (no sync)
✅ Example:
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // Changes original object
🔹 PART 6: Important String Methods
🔷 Common Methods with Breakdown:
Method Description Example
length() Returns length "abc".length() → 3
charAt(i) Char at position "abc".charAt(1) → 'b'
substring(i, j) From index i to j-1 "hello".substring(1, 4) → "ell"
indexOf("s") Index of substring "miss".indexOf("s") → 2
equals() Content equality "abc".equals("abc") → true
compareTo() Lexicographical diff "a".compareTo("b") → -1
toLowerCase() To lowercase "JAVA".toLowerCase() → "java"
replace("a", "@") Replace chars "banana".replace("a", "@") → "b@n@n@"
split(",") Regex-based split "a,b,c".split(",") → [a, b, c]
🧠 Potential Exam Questions
● Write a Swing application with two buttons. Use ActionListener to change label text.
● What is the purpose of layout managers? Explain with diagrams and code.
● Compare String, StringBuffer, and StringBuilder in terms of memory and
thread safety.
● Demonstrate how wrapper classes allow primitives to be used in ArrayList.
● Write a Java program that reads a string input and displays each word on a new line
using split().
📘 Advanced Programming (Java) – Week 2
🎓 Lecture 1: JDBC, Relational Databases, ODBC, SQL Statements
🔷 Introduction to Relational Databases
Relational databases are the backbone of most modern data storage systems. They organize
data into structured tables (relations), which consist of rows and columns. Each table typically
represents a real-world entity—for example, a "Student" table may contain rows representing
individual students, and columns for attributes like student ID, name, and marks.
Each table must have a primary key, which uniquely identifies each row. Relationships among
tables (such as one-to-many or many-to-many) are established using foreign keys, which
reference primary keys in other tables.
Some common relational databases include:
● MySQL
● PostgreSQL
● Oracle
● Microsoft SQL Server
Relational databases use SQL (Structured Query Language) to manage and manipulate data.
SQL enables users to create tables, insert data, query data, and update or delete existing
records.
🔷 What is JDBC?
JDBC (Java Database Connectivity) is an API (Application Programming Interface) provided
by Java to allow applications to interact with relational databases in a standardized way. Using
JDBC, a Java application can:
● Connect to a database
● Execute SQL queries
● Retrieve and process results
● Perform updates, insertions, and deletions
● Handle transactions and manage connections
JDBC provides an abstraction so that your Java code does not need to be changed even if the
underlying database changes (as long as you update the driver).
✅ Core JDBC Classes and Interfaces
Component Purpose
DriverManager Manages database drivers and establishes connections.
Connection Represents a session with a database.
Statement Used for executing static SQL queries (no input parameters).
PreparedStatement A subclass of Statement that allows parameterized SQL queries.
ResultSet Holds the result returned from a query.
🔷 Steps in a JDBC Program
Load the Driver Class: The driver must be loaded so that the DriverManager can find it.
Class.forName("com.mysql.cj.jdbc.Driver");
Establish the Connection:
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/studentdb", "root", "password");
Create a Statement or PreparedStatement:
Statement stmt = con.createStatement();
Execute SQL Query:
ResultSet rs = stmt.executeQuery("SELECT * FROM students");
Process the Results:
while(rs.next()) {
System.out.println(rs.getInt("id") + " " + rs.getString("name"));
}
Close Resources:
rs.close();
stmt.close();
con.close();
🔷 What is ODBC?
ODBC (Open Database Connectivity) is a similar concept to JDBC but is designed by Microsoft
to be platform-independent. It allows any application to access any data source for which an
ODBC driver is available.
While ODBC is language-neutral, JDBC is specific to Java. JDBC can actually use an ODBC
bridge, allowing Java programs to communicate with databases through ODBC drivers, but this
approach is outdated and generally discouraged due to performance and compatibility issues.
🔷 SQL Statements Overview
SQL (Structured Query Language) is used to communicate with a database. There are
different types of SQL commands:
1. DDL (Data Definition Language):
Used to define the structure of the database.
● CREATE: Creates a new table or database.
● ALTER: Modifies an existing table.
● DROP: Deletes a table or database.
2. DML (Data Manipulation Language):
Used to manipulate data in tables.
● INSERT: Adds new data.
● UPDATE: Modifies existing data.
● DELETE: Removes data.
3. DQL (Data Query Language):
● SELECT: Retrieves data from tables.
4. DCL (Data Control Language):
● GRANT, REVOKE: Manage access permissions.
5. TCL (Transaction Control Language):
● COMMIT, ROLLBACK, SAVEPOINT: Manage database transactions.
Understanding SQL syntax is crucial for working with databases through JDBC, as your Java
code will execute these commands to interact with the data layer.
🎓 Lecture 2: Inserting and Updating Data via JDBC
🔷 Inserting Data using JDBC
When inserting data from a Java application into a database, it's important to use either a
Statement or preferably a PreparedStatement to safely and efficiently execute SQL
commands.
✅ Example using Statement
Statement stmt = con.createStatement();
stmt.executeUpdate("INSERT INTO students VALUES (1, 'Alice')");
While this works, it's not recommended for real-world applications, especially when input
values come from users. This method is vulnerable to SQL injection.
🔷 Using PreparedStatement for Safe Inserts
The PreparedStatement interface helps avoid SQL injection and improves performance by
precompiling the query.
String query = "INSERT INTO students (id, name) VALUES (?, ?)";
PreparedStatement pst = con.prepareStatement(query);
pst.setInt(1, 2);
pst.setString(2, "Bob");
pst.executeUpdate();
This version is much safer and scalable, especially when working with user input.
🔍 Why Use PreparedStatement?
● Automatically escapes input
● Prevents SQL injection
● Precompiled SQL improves performance
● Easier to manage dynamic input
🔷 Updating Records in a Database
Updating data is another frequent operation. It follows a similar process as insertion.
String query = "UPDATE students SET name = ? WHERE id = ?";
PreparedStatement pst = con.prepareStatement(query);
pst.setString(1, "Charlie");
pst.setInt(2, 2);
pst.executeUpdate();
This SQL statement updates the name of the student whose id is 2.
✅ Best Practices for Updates
● Always include a WHERE clause to avoid updating all records by mistake.
● Use transactions (con.setAutoCommit(false)) when multiple updates are involved.
● Use try-with-resources to automatically close connections and statements.
🔷 Error Handling and Exceptions
JDBC operations often involve database connections, which can fail due to a variety of
reasons—such as network issues, incorrect SQL, or permission problems. Always use proper
exception handling:
try {
// JDBC logic
} catch (SQLException e) {
System.out.println("Database error: " + e.getMessage());
} finally {
// close resources
}
This ensures that even if an error occurs, your application won't crash and can clean up
resources appropriately.
🔷 Summary of Key Concepts
● JDBC is Java's API to interact with relational databases.
● Use PreparedStatement for inserts and updates to avoid SQL injection.
● Understand the basic SQL commands: INSERT, UPDATE, SELECT.
● Handle exceptions and close database connections properly.
● Relational databases store data in rows and columns using schemas, keys, and
constraints.
🧠 Potential Exam Questions
1. Explain the steps involved in connecting a Java application to a database using
JDBC.
2. Differentiate between Statement and PreparedStatement with examples.
3. Write a Java program that inserts a new record into a database table using JDBC.
4. What is SQL injection, and how does PreparedStatement help prevent it?
5. Describe the purpose and syntax of the UPDATE SQL command with a JDBC
example.
6. Compare JDBC and ODBC in terms of platform support and language
dependence.
📘 Advanced Programming (Java) – Week 3
🎓 Lecture 1: Deleting Records, JDBC Metadata
🔷 Part 1: Deleting Records Using JDBC
🔍 Concept Overview
In any CRUD (Create, Read, Update, Delete) application, deletion is a vital operation. In Java,
we perform deletion of records from a database using JDBC (Java Database Connectivity).
The process involves writing a DELETE SQL command, preparing it using
PreparedStatement, and then executing it via JDBC methods.
Deleting records must be handled very cautiously. Without proper filtering (i.e., WHERE clause),
a delete operation may remove all records, causing data loss.
🧱 SQL Syntax for Deletion
DELETE FROM table_name WHERE condition;
● table_name — The table from which you want to delete the records.
● condition — Specifies which records to delete.
● ⚠️ Omitting WHERE clause deletes all rows in the table.
✅ Step-by-Step Java Example (Annotated)
Suppose we want to delete a student with ID = 3 from the students table.
String sql = "DELETE FROM students WHERE id = ?";
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 3); // Replaces '?' with ID 3
int affectedRows = pst.executeUpdate();
System.out.println(affectedRows + " row(s) deleted.");
🔍 Annotations:
● PreparedStatement pst = con.prepareStatement(sql);
➤ Precompiles the SQL statement with a parameter placeholder (?).
➤ Helps prevent SQL injection.
● pst.setInt(1, 3);
➤ Binds the value 3 to the first parameter (?) in the SQL query.
● executeUpdate()
➤ Executes the deletion. Returns the number of affected rows.
✅ Real-World Best Practices
● Check Before You Delete: Always prompt the user for confirmation.
● Use Transactions if you're deleting multiple records and want rollback capability.
● Log Deletions for recovery and audit purposes.
● Avoid Hard-Coded Values: Use variables or UI input to specify deletion conditions.
🔷 Part 2: Working with JDBC Metadata
🔍 Concept Overview
Metadata is "data about data." In JDBC, metadata allows a Java program to dynamically
explore:
● Database capabilities
● Tables, columns, and their data types
● ResultSet structure (columns, types, labels)
This is essential when writing programs that work across multiple databases or where schema
may change.
JDBC exposes metadata through two key interfaces:
1. DatabaseMetaData – For information about the entire database.
2. ResultSetMetaData – For information about the result set columns from a query.
✅ Using DatabaseMetaData
Used when you want to explore the structure or capabilities of the whole database (e.g., list all
tables, supported features, etc.).
🔍 Example:
DatabaseMetaData dbmd = con.getMetaData();
System.out.println("DB Name: " + dbmd.getDatabaseProductName());
System.out.println("Driver: " + dbmd.getDriverName());
System.out.println("User: " + dbmd.getUserName());
Listing All Tables:
ResultSet rs = dbmd.getTables(null, null, "%", new String[]{"TABLE"});
while (rs.next()) {
System.out.println("Table Name: " + rs.getString("TABLE_NAME"));
}
📌 Real Use:
● Schema explorers
● Admin dashboards
● ORM tools like Hibernate
✅ Using ResultSetMetaData
Used when you want to dynamically understand the structure of the data returned from a query.
🔍 Example:
PreparedStatement pst = con.prepareStatement("SELECT * FROM
students");
ResultSet rs = pst.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
System.out.println("Column " + i + ": " + rsmd.getColumnName(i) +
" (Type: " + rsmd.getColumnTypeName(i) + ")");
}
🔍 Annotations:
● getColumnCount() returns number of columns.
● getColumnName(i) returns name of the i-th column.
● getColumnTypeName(i) returns SQL type name (e.g., VARCHAR, INT).
💡 Summary of Metadata Use Cases
Interface Purpose
DatabaseMetaData Access database structure, tables, version, driver info
ResultSetMetaData Access query result structure (columns, types, labels)
🎓 Lecture 2: JDBC Full Revision
🔁 JDBC Overview: Complete Lifecycle
Java Database Connectivity (JDBC) provides a standard API to interact with relational
databases using SQL. The typical steps are:
✅ Step 1: Load and Register JDBC Driver
Class.forName("com.mysql.cj.jdbc.Driver");
● Registers the driver class to the DriverManager.
● Some newer JDBC versions auto-load the driver, but this is still good practice for
compatibility.
✅ Step 2: Establish Database Connection
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/studentdb", "root", "password");
● Connects to the database using the URL, username, and password.
JDBC URL format:
jdbc:<database_type>://<host>:<port>/<database_name>
✅ Step 3: Create Statement / PreparedStatement
Using Statement (Unsafe for user input):
Statement stmt = con.createStatement();
Using PreparedStatement (Safe and Preferred):
PreparedStatement pst = con.prepareStatement("SELECT * FROM students
WHERE name = ?");
pst.setString(1, "Ali");
✅ Step 4: Execute SQL Command
For SELECT queries:
ResultSet rs = pst.executeQuery();
For INSERT/UPDATE/DELETE:
int rows = pst.executeUpdate();
✅ Step 5: Process the ResultSet
while (rs.next()) {
System.out.println("ID: " + rs.getInt("id"));
System.out.println("Name: " + rs.getString("name"));
}
● rs.next() moves cursor to next row.
● Column values are fetched by name or index.
✅ Step 6: Close the Resources
rs.close();
pst.close();
con.close();
● Always close database resources to avoid memory leaks and locked connections.
⚔️ Statement vs PreparedStatement – Detailed
Comparison
Feature Statement PreparedStatement
Compilation Compiled every time Precompiled once
Security Vulnerable to SQL Injection Prevents SQL Injection
Syntax Static SQL Parameterized SQL
Efficiency Slower on repeated calls Faster on repeated use
🔐 Exception Handling in JDBC
All JDBC operations throw SQLException. It’s important to catch and log these errors.
Traditional try-catch:
try {
// JDBC code
} catch (SQLException e) {
e.printStackTrace();
}
Modern Java (try-with-resources):
try (Connection con = DriverManager.getConnection(...);
PreparedStatement pst = con.prepareStatement(...)) {
// JDBC code here
}
This automatically closes all resources even if an exception occurs.
✅ Complete CRUD Summary in JDBC
Operation SQL Command JDBC Method
Create INSERT INTO... executeUpdate()
Read SELECT... executeQuery()
Update UPDATE... executeUpdate()
Delete DELETE FROM... executeUpdate()
🧠 Potential Exam Questions
1. Write a Java program to delete a record from a database using JDBC.
2. Differentiate between DatabaseMetaData and ResultSetMetaData. Give
examples.
3. Explain the full JDBC workflow with code and annotations.
4. What are the risks of using Statement over PreparedStatement?
5. Demonstrate how ResultSetMetaData is used to fetch dynamic column names.
6. What is the purpose of using metadata in JDBC and where is it used in real
applications?
7. Explain try-with-resources in JDBC with example. Why is it preferred?
📘 Advanced Programming (Java) – Week 4
🎓 Lecture 1: Using Lambda Expressions
🔷 Introduction to Lambda Expressions
Lambda expressions were introduced in Java 8 as a new feature to facilitate functional
programming and enable the use of anonymous functions (functions without names). They
allow developers to write cleaner, more concise, and more readable code, especially when
working with functional interfaces and collections.
At its core, a lambda expression provides a clear and concise way to represent a method
interface using an expression, instead of writing a separate class or anonymous class.
🔹 Basic Syntax:
(parameters) -> expression
Or for multi-line:
(parameters) -> {
// multiple statements
}
🔹 Example:
(a, b) -> a + b
This represents a function that takes two parameters and returns their sum.
🔷 Key Concepts Underlying Lambda Expressions
✅ Functional Interface
A lambda expression can only be used where there is a functional interface. A functional
interface is an interface that contains exactly one abstract method.
💡 Example:
The Runnable interface is a functional interface because it has just one method: run().
@FunctionalInterface
interface MyFunction {
void apply();
}
🔸 Java provides many built-in functional interfaces in the java.util.function package,
such as:
● Function<T, R>
● Consumer<T>
● Supplier<T>
● Predicate<T>
🔷 Benefits of Lambda Expressions
Lambda expressions offer the following advantages:
● Reduced Boilerplate Code: Removes the need for anonymous class declarations.
● Improved Readability: Logic is encapsulated in a single expression.
● Encourages Functional Style: Brings Java closer to functional programming
paradigms.
● Easier Collection Processing: Works well with Stream API for data manipulation.
✅ Comparison: Without Lambda vs. With Lambda
🔹 Without Lambda Expression
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Thread running");
}
});
t.start();
🔹 With Lambda Expression
Thread t = new Thread(() -> System.out.println("Thread running"));
t.start();
📝 Annotation:
● The lambda eliminates the need for creating a new class or anonymous implementation
of Runnable.
● () -> implies that the method has no parameters.
● The code within the arrow body {} is the implementation of run().
🔷 Rules and Characteristics of Lambda Expressions
● They can access final or effectively final variables from the enclosing scope.
● They do not define a new scope as inner classes do.
● They cannot access instance variables directly unless they belong to the same class.
● Lambda expressions are not objects but get converted to instances of functional
interfaces during compilation.
● You cannot overload lambda expressions directly, as they are just implementations of
interfaces.
🎓 Lecture 2: Example of Lambda Expression
🔷 Lambda in Action: Real Use Cases
Lambda expressions become extremely powerful when used with functional interfaces and
collections. Let's go through a few real-world-style examples.
✅ Example 1: Using Lambda with Comparator
Suppose you have a list of strings and want to sort them based on their lengths.
🔹 Without Lambda:
Collections.sort(list, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
🔹 With Lambda:
Collections.sort(list, (s1, s2) -> s1.length() - s2.length());
📝 Annotation:
● (s1, s2) are parameters.
● -> separates the parameter list from the method body.
● The expression s1.length() - s2.length() is returned implicitly.
✅ Example 2: Iterating Collections with forEach
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
📝 Annotation:
● forEach is a method from the Iterable interface.
● name -> System.out.println(name) prints each element.
✅ Example 3: Filtering with Predicate
List<String> names = Arrays.asList("John", "Jane", "Steve", "Sara");
names.stream()
.filter(name -> name.startsWith("S"))
.forEach(System.out::println);
📝 Annotation:
● name -> name.startsWith("S") is a Predicate.
● The lambda is passed to filter() to select only names starting with 'S'.
🔷 Behind the Scenes: How Lambdas Work Internally
When you write a lambda in Java, the compiler:
1. Checks that it matches a functional interface.
2. Internally, it creates a synthetic class or invokedynamic call site (since Java 8) to
represent the lambda.
3. It passes the lambda as an instance of that interface during execution.
This means:
● Lambda expressions do not have their own type. Their type is inferred from the
context.
● They can be stored in variables of functional interface types.
🔷 Lambda and Method References
In some cases, if the lambda is simply calling a method, Java allows a method reference as a
shorthand:
names.forEach(System.out::println);
This is a method reference equivalent to:
names.forEach(name -> System.out.println(name));
📝 Annotation:
● System.out::println refers to the instance method println of System.out.
● It improves readability by removing redundancy.
🧠 Summary of Key Takeaways
● Lambda expressions are anonymous functions introduced in Java 8.
● They require functional interfaces to work.
● The syntax (parameters) -> { body } replaces verbose anonymous class syntax.
● Lambdas improve readability, reduce boilerplate, and support functional programming.
● They're ideal for use in Streams, Collections, and Event Handling.
● Method references (ClassName::methodName) are concise alternatives when
applicable.
📝 Potential Exam Questions
1. Explain lambda expressions in Java. How do they differ from anonymous classes?
2. Write a Java program using lambda to sort a list of strings by length.
3. What is a functional interface? Give examples of standard functional interfaces.
4. Compare lambda expressions and method references with examples.
5. Demonstrate how lambda expressions can simplify collection iteration and
filtering.
📘 Advanced Programming (Java) – Week 5
🎓 Lecture 1: Concurrency and Threading, Single Thread
🔹 Understanding Concurrency
Concurrency in Java refers to the ability of a program to execute multiple tasks simultaneously.
However, it doesn't always mean tasks are executed in parallel. Rather, it allows a system to
manage multiple tasks that make progress without blocking each other completely.
Concurrency is critical for applications that need to remain responsive, like GUI applications, or
applications that need to perform background tasks such as file I/O, data processing, or network
communication.
✅ Example: In a GUI-based application, the interface remains responsive while
downloading a file in the background.
🔹 What is a Thread?
A thread is the smallest unit of execution within a program. In Java, every application has at
least one thread — the main thread — which begins when the main() method is invoked.
Each thread runs independently but shares the same memory space (heap) with other threads
in the same process, allowing communication and data sharing.
Key Terminologies:
● Process: A running instance of a program. Each process has its own memory.
● Thread: A unit within a process. Threads share memory but execute independently.
● Main Thread: The default thread created when the Java program starts.
🧠 Annotation: Java threads are managed by the Java Virtual Machine (JVM),
which delegates the actual threading to the operating system’s native thread
management.
🔹 Creating a Thread (Single-Threaded Context)
In a single-threaded Java application, everything runs sequentially on the main thread. Let’s
look at how you can create a thread, even if the program has only one at a time.
There are two primary ways to create a thread in Java:
1. Extending the Thread class
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running.");
}
}
public class Demo {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // starts the new thread and calls run()
}
}
🔍 Annotation:
● run() contains the code that will be executed by the thread.
● start() initiates the new thread and eventually calls run() internally.
● Directly calling run() will not start a new thread; it will execute in the
current thread.
2. Implementing the Runnable interface
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable thread is running.");
}
}
public class Demo {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
🔍 Annotation:
This approach is preferred when you want to inherit from another class, since Java
allows only single inheritance.
🔹 Thread Life Cycle
A thread in Java undergoes several states throughout its lifetime. These states are defined in
the Thread.State enum:
1. New: Thread object is created but not started.
2. Runnable: Thread is ready to run and is waiting for CPU time.
3. Running: Thread is actively executing.
4. Blocked/Waiting: Thread is paused, waiting for a resource or another thread.
5. Timed Waiting: Waiting for a specific amount of time.
6. Terminated (Dead): Thread completes execution or is stopped.
🧠 Use methods like sleep(), join(), and yield() to influence thread
behavior.
🎓 Lecture 2: Multi-threading
🔹 What is Multi-threading?
Multi-threading is the concurrent execution of two or more threads. It allows tasks to be
performed in parallel, improving performance, especially on multi-core processors.
Multi-threading enhances responsiveness and resource utilization.
Java provides built-in support for multi-threading via the java.lang.Thread class and the
java.util.concurrent package.
🔹 Benefits of Multi-threading
● Responsiveness: GUI applications stay interactive while performing background
operations.
● Resource Sharing: Threads within the same process can share memory and resources.
● Efficiency: Less memory overhead than creating new processes.
● Improved CPU Utilization: Multiple threads can run on multiple cores simultaneously.
🔹 Creating Multiple Threads
Let’s build on the earlier examples and see multiple threads running simultaneously.
class Task1 extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task1 - Count: " + i);
}
}
}
class Task2 extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Task2 - Count: " + i);
}
}
}
public class Demo {
public static void main(String[] args) {
Task1 t1 = new Task1();
Task2 t2 = new Task2();
t1.start(); // starts Task1
t2.start(); // starts Task2
}
}
🔍 Annotation:
● The output may vary with every execution due to the non-deterministic
scheduling of threads.
● Both threads share the CPU time, which results in an interleaved output.
🔹 Managing Threads
Java provides several methods to control and manage threads:
● start(): Initiates thread execution.
● run(): Contains the logic executed by the thread.
● sleep(ms): Pauses thread for specified milliseconds.
● join(): Waits for one thread to die before continuing.
● isAlive(): Checks if the thread is still running.
● yield(): Allows other threads of the same priority to execute.
public class Demo {
public static void main(String[] args) throws InterruptedException
{
Thread t1 = new Thread(() -> {
System.out.println("Thread1 sleeping...");
try { Thread.sleep(1000); } catch (InterruptedException e)
{}
System.out.println("Thread1 finished.");
});
t1.start();
t1.join(); // waits for t1 to complete
System.out.println("Main thread ends.");
}
}
🔍 Annotation:
● join() is used when one thread must complete before another starts.
● sleep() is often used in simulation, polling, or controlling thread flow.
🔹 Thread Synchronization
When multiple threads access shared resources, synchronization ensures that only one thread
accesses the resource at a time, avoiding race conditions.
Synchronized Methods
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getValue() {
return count;
}
}
🔍 Annotation:
● The synchronized keyword locks the object, preventing other threads
from executing synchronized methods on the same object simultaneously.
🔹 Daemon Threads
A daemon thread runs in the background and supports non-daemon threads. They are
terminated once all user threads finish execution.
Thread t = new Thread(() -> {
while (true) {
System.out.println("Daemon is running...");
}
});
t.setDaemon(true);
t.start();
🔍 Annotation:
Daemon threads are useful for tasks like garbage collection, monitoring, etc. They
should not be used for tasks that require completion.
🔹 Thread Priorities
Each thread in Java has a priority value from 1 (MIN_PRIORITY) to 10 (MAX_PRIORITY), with
the default being 5 (NORM_PRIORITY).
Thread t1 = new Thread(...);
t1.setPriority(Thread.MAX_PRIORITY);
⚠️ Note: Thread priorities are hints to the JVM scheduler; they do not guarantee
execution order.
🔹 Thread Pools (Introductory)
To manage a large number of threads efficiently, Java provides the Executor Framework via
the java.util.concurrent package.
ExecutorService service = Executors.newFixedThreadPool(5);
service.execute(() -> System.out.println("Thread pool task"));
service.shutdown();
🔍 Thread pools help in reducing overhead of thread creation and destruction,
especially in high-load applications like web servers.
🧠 Potential Exam Questions
1. Explain the thread life cycle in Java with suitable diagrams and state transitions.
2. Differentiate between implementing Runnable and extending Thread. When would
you use each?
3. Write a multi-threaded Java program to simulate two tasks executing concurrently.
4. What is synchronization in Java? Explain with a program demonstrating race
condition.
5. Define and explain daemon threads. Give an example.
6. Describe how sleep(), join(), and yield() control thread execution.
📘 Advanced Programming (Java) – Week 6
🎓 Lecture 1: Network Programming – TCP (Transmission Control Protocol)
🔷 Introduction to Network Programming in Java
Network programming allows communication between computers over a network using
predefined protocols. Java provides powerful libraries in the java.net package that enable
developers to build both client-server and peer-to-peer communication systems.
TCP (Transmission Control Protocol) is one of the core protocols used for such communication.
Java supports TCP-based communication through the use of Socket and ServerSocket
classes.
🔷 What is TCP?
TCP (Transmission Control Protocol) is a connection-oriented, reliable, and stream-based
communication protocol. It is part of the TCP/IP suite and ensures that data sent from one
computer to another arrives intact, in order, and without duplication.
Key characteristics of TCP:
● Connection-oriented: A persistent connection is established before any data is
transferred.
● Reliable: It guarantees delivery of packets using acknowledgment mechanisms.
● Stream-based: Data is read and written as a continuous stream of bytes.
● Ordered delivery: Packets are reassembled in the correct order.
Use Cases of TCP:
● Web browsing (HTTP/HTTPS)
● File transfers (FTP)
● Emails (SMTP, POP3, IMAP)
● Chat applications
🔷 TCP Communication Model: Client-Server Architecture
TCP communication typically follows the Client-Server Model. The server runs continuously
and listens on a specific port. A client connects to the server, establishes a socket, and
communicates using input and output streams.
🔁 Overview of Working:
[ServerSocket] ---accept()---> [Socket (Server side)] <---> [Socket
(Client side)] ---connect()---> [Client]
🔹 Core Classes in Java for TCP
1. ServerSocket (java.net.ServerSocket)
Used on the server side to listen for incoming connections on a specified port.
2. Socket (java.net.Socket)
Represents the actual connection between client and server. Used on both client and
server sides to send and receive data.
3. InputStream & OutputStream
For reading and writing data through the socket connection.
✅ Code Example: TCP Server
import java.io.*;
import java.net.*;
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(5000); // Bind to port
5000
System.out.println("Server is waiting for client...");
Socket socket = server.accept(); // Block until client
connects
System.out.println("Client connected!");
BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(),
true);
String clientMessage = in.readLine();
System.out.println("Received: " + clientMessage);
out.println("Hello from Server");
socket.close();
server.close();
}
}
✅ Code Example: TCP Client
import java.io.*;
import java.net.*;
public class TCPClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 5000); // Connect to
server on port 5000
BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(),
true);
out.println("Hello Server!");
String reply = in.readLine();
System.out.println("Server says: " + reply);
socket.close();
}
}
📝 Annotations:
● ServerSocket server = new ServerSocket(5000);
Initializes a TCP server listening on port 5000.
● Socket socket = server.accept();
Waits (blocking) until a client connects, then returns a Socket object.
● BufferedReader and PrintWriter
These are wrappers around the socket’s input/output streams to facilitate easy text
communication.
🔷 Advantages of TCP Communication:
● Reliable delivery: Ensures message integrity.
● Ordered data: Maintains the correct order of sent data.
● Flow control and congestion handling: Efficient use of network resources.
● Bi-directional communication: Both client and server can send/receive data.
🔷 Limitations of TCP:
● Higher overhead due to connection setup and reliability features.
● Slower than UDP for real-time applications.
🎓 Lecture 2: Network Programming – UDP (User Datagram Protocol)
🔷 What is UDP?
UDP (User Datagram Protocol) is a connectionless, unreliable, and message-based protocol.
It is also a part of the TCP/IP suite, but unlike TCP, it does not guarantee delivery, ordering, or
duplicate protection.
UDP is best suited for applications where speed is more critical than reliability, such as:
● Video streaming
● Online gaming
● Voice over IP (VoIP)
● DNS (Domain Name System)
🔷 Characteristics of UDP:
● Connectionless: No setup or teardown of connections.
● Unreliable: Packets may be lost or arrive out of order.
● Lightweight and Fast: No overhead from reliability mechanisms.
● Message-based: Sends data in discrete chunks (datagrams).
🔹 Core Classes in Java for UDP
1. DatagramSocket (java.net.DatagramSocket)
Used to send and receive datagram packets.
2. DatagramPacket (java.net.DatagramPacket)
Represents a datagram to be sent or received. Holds data and the address of the
receiver.
✅ Code Example: UDP Server
import java.net.*;
public class UDPServer {
public static void main(String[] args) throws Exception {
DatagramSocket serverSocket = new DatagramSocket(6000);
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
System.out.println("Server is waiting...");
serverSocket.receive(receivePacket); // Block until a packet
arrives
String message = new String(receivePacket.getData(), 0,
receivePacket.getLength());
System.out.println("Client says: " + message);
serverSocket.close();
}
}
✅ Code Example: UDP Client
import java.net.*;
public class UDPClient {
public static void main(String[] args) throws Exception {
DatagramSocket clientSocket = new DatagramSocket();
String message = "Hello UDP Server";
byte[] sendData = message.getBytes();
InetAddress serverAddress =
InetAddress.getByName("localhost");
DatagramPacket sendPacket = new DatagramPacket(sendData,
sendData.length, serverAddress, 6000);
clientSocket.send(sendPacket);
clientSocket.close();
}
}
📝 Annotations:
● DatagramSocket
Used for both sending and receiving datagrams.
● DatagramPacket
Contains the message to send and the destination address or holds the received data.
● receivePacket.getData()
Extracts the byte array from the received packet.
🔷 Advantages of UDP:
● Low latency: Suitable for time-sensitive applications.
● No connection setup: Faster communication initiation.
● Supports broadcast/multicast: Can send to multiple destinations.
🔷 Limitations of UDP:
● No guarantee of delivery or order.
● Packets may be dropped or duplicated.
● Requires manual handling for reliability (if needed).
📌 TCP vs UDP Summary Table:
Feature TCP UDP
Protocol Type Connection-oriented Connectionless
Reliability Reliable (acknowledged) Unreliable (no ack)
Speed Slower Faster
Message Ordering Guaranteed Not guaranteed
Use Cases HTTP, FTP, SMTP VoIP, DNS, Video Streaming
Java Classes Socket, ServerSocket DatagramSocket, DatagramPacket
🧠 Potential Exam Questions:
● Write a Java program for TCP client-server communication with full annotations.
● Explain the working of UDP protocol with examples. Why is it suitable for real-time
applications?
● Compare TCP and UDP in terms of reliability and use cases.
● What are the core classes used in Java for network programming?
📘 Advanced Programming (Java) – Week 7
🎓 Lecture 1: Distributed Applications using RMI
🔷 What is a Distributed Application?
A distributed application is a software system where components located on different networked
computers communicate and coordinate their actions by passing messages. These systems are
designed to share resources and computational tasks across multiple machines to improve
scalability, fault tolerance, and performance. In Java, this is often achieved using technologies
like RMI (Remote Method Invocation), CORBA, Web Services, or sockets, but RMI stands
out for providing a seamless Java-to-Java remote communication mechanism.
🔷 Purpose and Advantages of Distributed Applications
The primary goal of a distributed application is to divide and delegate processing over
multiple systems. Some key purposes and advantages include:
● Load Distribution: Allows different parts of a program to execute on different machines,
distributing the processing load.
● Resource Sharing: Enables systems to share data, memory, storage, and hardware
resources.
● Fault Tolerance: If one system fails, others can continue running, improving overall
reliability.
● Scalability: Systems can be scaled by adding more nodes or servers.
● Concurrent Processing: Enables multiple clients to access the same service
concurrently.
🔷 What is RMI (Remote Method Invocation)?
Java RMI allows a Java program on one machine (client) to invoke methods on a Java object
located on another machine (server), just as if the object were local. This is a fundamental
technology for building distributed Java applications. RMI abstracts the underlying network
communication, serialization, and method invocation details, enabling developers to focus on
application logic.
🔷 RMI Architecture and Components
RMI works on a client-server model and involves the following key components:
1. Remote Interface
○ Declares the methods that can be invoked remotely.
○ Must extend java.rmi.Remote.
2. Remote Object (Server Implementation Class)
○ Implements the Remote Interface.
○ Must extend UnicastRemoteObject and handle remote exceptions.
3. RMI Registry
○ A simple name service that allows clients to look up remote objects by name.
○ Runs on the server machine and listens on a specific port (default: 1099).
4. Stub and Skeleton (Pre-Java 1.5) / Dynamic Proxy (Post-Java 1.5)
○ Stub: Resides on the client and acts as a proxy to the remote object.
○ Skeleton (legacy): On the server, handled request dispatching.
○ Newer RMI uses dynamic proxies and does not require skeleton classes.
5. Client
○ Looks up the remote object from the registry and invokes its methods as if they
were local.
✅ Example: Basic RMI Program (Server and Client Overview)
1. Define the Remote Interface
import java.rmi.*;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
2. Implement the Remote Interface (Server)
java
CopyEdit
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
public class HelloImpl extends UnicastRemoteObject implements Hello {
protected HelloImpl() throws RemoteException {
super();
}
public String sayHello() throws RemoteException {
return "Hello from the server!";
}
}
3. Create the Server Class
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Server {
public static void main(String args[]) {
try {
HelloImpl obj = new HelloImpl();
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("HelloService", obj);
System.out.println("Server ready...");
} catch (Exception e) {
System.out.println("Server exception: " + e);
}
}
}
4. Create the Client Class
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
public static void main(String args[]) {
try {
Registry registry =
LocateRegistry.getRegistry("localhost");
Hello stub = (Hello) registry.lookup("HelloService");
System.out.println(stub.sayHello());
} catch (Exception e) {
System.out.println("Client exception: " + e);
}
}
}
📝 Annotations and Key Concepts
● RemoteException: Mandatory to handle network failures or lookup issues.
● UnicastRemoteObject: Ensures that objects can accept incoming calls from clients.
● LocateRegistry: Used to create or obtain reference to the RMI registry.
● rebind: Registers the remote object with the specified name in the registry.
🔄 RMI Workflow Summary
1. Server implements and exports a remote object.
2. Server registers the object with the RMI registry.
3. Client looks up the object in the registry using its name.
4. Client invokes methods on the remote object through the stub.
🎓 Lecture 2: Documenting and Packaging Java
Applications
🔷 Purpose of Documentation in Java
Good documentation is essential for the usability, maintainability, and extensibility of Java
applications. Java provides a built-in tool called javadoc that generates API documentation in
HTML format from Java source files.
🔷 javadoc and How It Works
● javadoc processes special comment blocks starting with /** and ending with */.
● These are used to describe classes, methods, constructors, parameters, return types,
and exceptions.
● The generated output includes class summaries, method signatures, and inline
documentation.
✅ Example: Javadoc Comments
/**
* This class performs basic arithmetic operations.
* @author John
* @version 1.0
*/
public class Calculator {
/**
* Adds two integers.
* @param a first integer
* @param b second integer
* @return sum of a and b
*/
public int add(int a, int b) {
return a + b;
}
}
📘 Common Javadoc Tags
● @param – Describes a method parameter.
● @return – Describes the return value.
● @throws – Describes exceptions thrown.
● @author
● @version
● @see – Links to other documentation.
🔷 Creating Javadoc Output
To generate HTML documentation:
javadoc Calculator.java
This will produce a set of .html files that document the class and its members.
🔷 Packaging Java Applications
Packaging refers to organizing and bundling Java classes, resources, and metadata into
deployable units.
🔷 Java Archive (JAR) Files
A JAR (Java ARchive) file is the standard format for packaging Java applications. It bundles
.class files, images, libraries, and META-INF directory into a single compressed file.
✅ Creating a JAR File
jar cf MyApp.jar *.class
● c → Create a new archive.
● f → Specify archive filename.
✅ Executable JAR with Main Class
jar cfm MyApp.jar manifest.txt *.class
Where manifest.txt contains:
Main-Class: MainClassName
Then run using:
java -jar MyApp.jar
🔷 Packaging Best Practices
● Use meaningful package names (e.g., com.mycompany.project.module).
● Organize classes logically.
● Include a manifest file specifying main class and versioning info.
● Document public APIs and classes using javadoc.
🧠 Exam Preparation Tips
● Explain the RMI process flow and roles of stub, registry, remote interface.
● Write a basic RMI-based program with a remote method and client call.
● Explain the significance of javadoc and provide sample documentation syntax.
● Describe the structure of a JAR file and how to create and run it.