JavaLearn Coding

Java Programming

Table of Contents

Beginner (Basics) — Foundations of Java

Java is one of the most popular programming languages in the world. Its platform independence, strong object-oriented structure, and vast ecosystem make it a valuable language for anyone aiming to build robust, scalable applications. This guide will help you build a strong foundation in Java, especially if you’re just starting out.


Introduction to Java

What is Java?

Java is a high-level, object-oriented, and general-purpose programming language developed by Sun Microsystems (now owned by Oracle). It is designed to be simple, secure, and platform-independent. One of Java’s biggest strengths is its philosophy: “Write Once, Run Anywhere.” This means Java programs can run on any device equipped with a Java Virtual Machine (JVM), regardless of the underlying hardware or operating system.

History and Features

Java was introduced in 1995 and has since become integral to enterprise systems, Android development, and web applications. Key features include:

  • Platform independence via the JVM

  • Automatic memory management through garbage collection

  • Robust and secure execution model

  • Rich standard library for networking, file I/O, collections, etc.

Java Virtual Machine (JVM), JRE, and JDK

  • JVM (Java Virtual Machine): Executes Java bytecode and enables platform independence.

  • JRE (Java Runtime Environment): Contains JVM and libraries required to run Java applications.

  • JDK (Java Development Kit): Includes the JRE plus compilers and tools needed for development.

Installing and Setting Up an IDE

To begin coding in Java, download the JDK from Oracle’s official site. Then choose an IDE such as Eclipse, IntelliJ IDEA, or VS Code.

Setting up in IntelliJ IDEA:

  1. Install IntelliJ and open it.

  2. Create a new project.

  3. Add a new Java class.

  4. Write your first Java program.


Java Syntax and Structure

Main Method Structure: public static void main(String[] args)

Every Java application starts with the main() method:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
    }
}
  • public: Accessible from anywhere

  • static: Doesn’t need an object to run

  • void: No return value

  • String[] args: Accepts command-line arguments

Printing Output with System.out.println

Use System.out.println() to print messages to the console:

System.out.println("This prints with a new line.");
System.out.print("This prints on the same line.");

Comments

Comments make code easier to understand:

// This is a single-line comment

/* 
This is a 
multi-line comment
*/

File and Class Naming Conventions

  • Java file names should match the class name exactly.

  • Class names should begin with an uppercase letter.

  • Use CamelCase for class names (e.g., StudentRecord).


Variables and Data Types

Primitive Types

Java provides several built-in primitive types:

Type Size Example
int 4 bytes 100
float 4 bytes 3.14f
double 8 bytes 3.14159
boolean 1 bit true / false
char 2 bytes ‘A’
int age = 25;
float price = 9.99f;
boolean isFun = true;
char grade = 'A';

Reference Types

Reference types refer to objects and include classes, arrays, and interfaces.

String name = "Alice";
int[] scores = {90, 80, 70};

Type Casting and Conversion

You can convert between compatible data types:

Implicit casting (widening):

int a = 5;
double b = a;  // Automatically converts int to double

Explicit casting (narrowing):

double x = 10.5;
int y = (int)x;  // Must be explicitly casted

Constants with final Keyword

Use the final keyword to declare constants:

final int MAX_USERS = 100;

Once assigned, a final variable cannot be changed.


Operators in Java

Arithmetic, Logical, Relational, Assignment, Bitwise

Arithmetic Operators:

int sum = 10 + 5;      // Addition
int product = 10 * 5;  // Multiplication

Relational Operators:

int x = 10, y = 20;
System.out.println(x < y);  // true

Logical Operators:

boolean isAdult = true;
boolean hasTicket = false;
System.out.println(isAdult && hasTicket);  // false

Assignment Operators:

int z = 10;
z += 5;  // z = 15

Bitwise Operators:

int a = 5;    // 0101
int b = 3;    // 0011
System.out.println(a & b);  // 1 (0001)

Precedence and Associativity

Operator precedence determines the order of execution. Multiplication has higher precedence than addition:

int result = 5 + 2 * 3;  // 11, not 21

Associativity defines the direction — most operators are left-to-right, except assignment which is right-to-left.


Control Flow Statements

Conditional Statements: if, else if, switch

If-Else Example:

int score = 85;
if (score >= 90) {
    System.out.println("Grade A");
} else if (score >= 75) {
    System.out.println("Grade B");
} else {
    System.out.println("Grade C");
}

Switch Statement:

int day = 3;
switch (day) {
    case 1: System.out.println("Monday"); break;
    case 2: System.out.println("Tuesday"); break;
    default: System.out.println("Another day");
}

Looping: for, while, do-while

For Loop:

for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

While Loop:

int i = 0;
while (i < 5) {
    System.out.println(i);
    i++;
}

Do-While Loop:

int j = 0;
do {
    System.out.println(j);
    j++;
} while (j < 5);

Jump Statements: break, continue, return

  • break: Exits the loop early.

  • continue: Skips to the next iteration.

  • return: Exits the current method.

for (int i = 1; i <= 5; i++) {
    if (i == 3) continue;
    System.out.println(i);
}

Input and Output

Reading Input Using Scanner

Java uses the Scanner class to read user input:

import java.util.Scanner;

public class UserInput {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Enter name: ");
        String name = sc.nextLine();
        System.out.println("Hello, " + name);
    }
}
  • nextLine(): Reads entire line

  • nextInt(): Reads an integer

  • nextDouble(): Reads a decimal

Formatting Output with System.out.printf

Use printf for formatted output:

double price = 45.6789;
System.out.printf("Price: %.2f", price);  // Output: Price: 45.68

This is especially useful in financial applications or reports where precision matters.


Intermediate (Core Java) — Object-Oriented and Collections

Now that you’ve mastered the Java basics, it’s time to explore the heart of the language: Object-Oriented Programming (OOP) and collections. These features make Java powerful, scalable, and capable of handling complex applications. Let’s break down these intermediate concepts in depth.


Object-Oriented Programming (OOP)

Classes and Objects

Java is built on the object-oriented paradigm. At its core, this means modeling real-world entities using classes (blueprints) and objects (instances).

public class Car {
    String color;
    void drive() {
        System.out.println("Car is driving.");
    }
}

Creating and using an object:

Car myCar = new Car();
myCar.color = "Red";
myCar.drive();

Each object holds its own state and behavior, defined by variables and methods in the class.

Methods and Constructors

Methods define actions an object can perform, while constructors initialize new objects.

public class Student {
    String name;

    // Constructor
    public Student(String name) {
        this.name = name;
    }

    void display() {
        System.out.println("Name: " + name);
    }
}
Student s1 = new Student("Alice");
s1.display();

provides a default constructor if none is defined. However, defining your own allows better control during object creation.

The this Keyword

this is a reference to the current object. It’s commonly used when parameter names conflict with instance variables.

public class Employee {
    String name;

    Employee(String name) {
        this.name = name;
    }
}

Using this eliminates ambiguity and improves readability.

Access Modifiers

Java provides four levels of access control:

  • private: Accessible only within the class.

  • default: Accessible within the package.

  • protected: Accessible within the package and subclasses.

  • public: Accessible from anywhere.

public class Bank {
    private double balance;

    public void deposit(double amount) {
        balance += amount;
    }
}

Always use the most restrictive access level needed. This promotes data encapsulation and security.

Encapsulation, Inheritance, Polymorphism, Abstraction

These four pillars define OOP in Java:

  • Encapsulation: Wrapping data and methods into a single unit.

    public class Account {
        private double balance;
    
        public void deposit(double amount) {
            balance += amount;
        }
    
        public double getBalance() {
            return balance;
        }
    }
    
  • Inheritance: One class inherits properties of another.

    class Animal {
        void speak() {
            System.out.println("Animal speaks");
        }
    }
    
    class Dog extends Animal {
        void bark() {
            System.out.println("Dog barks");
        }
    }
    
  • Polymorphism: A single interface with multiple implementations.

    Animal a = new Dog();
    a.speak();  // Calls overridden method in Dog
    
  • Abstraction: Hiding internal details using abstract classes or interfaces.

    abstract class Shape {
        abstract void draw();
    }
    
    class Circle extends Shape {
        void draw() {
            System.out.println("Drawing Circle");
        }
    }
    

OOP makes code reusable, extendable, and easier to maintain.


Arrays and Strings

Single and Multidimensional Arrays

Arrays store fixed-size sequences of values of the same type.

int[] nums = {10, 20, 30};
System.out.println(nums[1]);  // 20

Multidimensional Arrays:

int[][] matrix = {
    {1, 2},
    {3, 4}
};
System.out.println(matrix[0][1]);  // 2

Arrays are great for static data, but collections are better for dynamic storage.

The String Class and Its Methods

Java strings are immutable and offer many useful methods:

String name = "Java Programming";
System.out.println(name.length());          // 16
System.out.println(name.charAt(0));         // J
System.out.println(name.substring(5));      // Programming
System.out.println(name.toUpperCase());     // JAVA PROGRAMMING

Because strings are objects, they support method chaining and comparison (equals(), compareTo()).

StringBuilder and StringBuffer

Unlike String, these classes are mutable and ideal for string manipulation.

StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb);  // Hello World

StringBuffer is thread-safe but slightly slower than StringBuilder. Choose according to your need.


Exception Handling

try, catch, finally, throw, throws

Java handles runtime errors through exceptions. This keeps the program from crashing unexpectedly.

try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Cannot divide by zero.");
} finally {
    System.out.println("This always runs.");
}
  • try: Wraps the risky code

  • catch: Handles specific exceptions

  • finally: Executes regardless of exception

  • throw: Manually throw an exception

  • throws: Declare an exception in method signature

Checked vs. Unchecked Exceptions

  • Checked exceptions: Checked at compile time (e.g., IOException).

  • Unchecked exceptions: Occur at runtime (e.g., NullPointerException).

public void readFile() throws IOException {
    throw new IOException("File not found");
}

Use try-catch or throws to handle checked exceptions.

Custom Exceptions

Create your own exceptions for business logic:

class InvalidAgeException extends Exception {
    InvalidAgeException(String message) {
        super(message);
    }
}
if (age < 18) {
    throw new InvalidAgeException("Underage not allowed.");
}

This approach enhances code readability and enforces specific rules.


Java Collections Framework

List, Set, Map, Queue

Java Collections handle dynamic data structures. Interfaces like List, Set, Map, and Queue define behavior, and concrete classes implement them.

  • List: Ordered, allows duplicates

  • Set: Unordered, no duplicates

  • Map: Key-value pairs

  • Queue: FIFO structure

ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap

Each implementation has its strengths:

List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");

Set<Integer> set = new HashSet<>();
set.add(1); set.add(2); set.add(1);  // Duplicate ignored

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.put("B", 20);
  • ArrayList: Fast random access, slower insertions

  • LinkedList: Efficient insertions/deletions

  • HashSet: Fast operations, no order

  • TreeSet: Sorted set

  • HashMap: Unordered key-value storage

  • TreeMap: Sorted key-value pairs

Iterators and for-each Loop

Looping through collections:

for (String lang : list) {
    System.out.println(lang);
}

Using Iterator:

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

Iterators offer more control and allow safe removal of elements during traversal.


Static and Final Keywords

Static Methods and Static Blocks

In Java, static members belong to the class itself rather than any individual object.
This means they can be accessed without creating an instance of the class.
Additionally, static blocks are used to initialize static variables and are executed only once when the class is loaded.

public class MathUtils {
    static int add(int a, int b) {
        return a + b;
    }

    static {
        System.out.println("Static block runs first.");
    }
}

Call static methods without creating an object:

int sum = MathUtils.add(5, 3);

Static blocks initialize static data before any constructor.

Final Variables, Methods, and Classes

  • Final variables: Cannot be reassigned.

  • Final methods: Cannot be overridden.

  • Final classes: Cannot be extended.

final int MAX = 100;

final class Constants {
    // Cannot be subclassed
}

Use final for safety, consistency, and optimization.


Wrapper Classes and Autoboxing

Integer, Double, Boolean, etc.

Java provides object equivalents for primitives:

  • intInteger

  • doubleDouble

  • booleanBoolean

Integer num = Integer.valueOf(10);

Autoboxing and Unboxing

Autoboxing automatically converts primitives to wrapper objects:

Integer x = 10;  // Autoboxing
int y = x;       // Unboxing

This is often used in collections:

List<Integer> nums = new ArrayList<>();
nums.add(5);  // Autoboxing

Understanding this is key to writing clean, type-safe code.


Inner Classes

Regular Inner Classes

Defined inside another class:

class Outer {
    class Inner {
        void show() {
            System.out.println("Inside Inner");
        }
    }
}

Static Nested Classes

Behaves like a static member:

class Outer {
    static class Nested {
        void display() {
            System.out.println("Static Nested Class");
        }
    }
}

Anonymous Classes

Defined and instantiated on the fly, often used in event handling:

Runnable r = new Runnable() {
    public void run() {
        System.out.println("Anonymous Class Running");
    }
};
r.run();

Local Inner Classes

Defined inside methods and limited to their scope:

void method() {
    class Local {
        void msg() {
            System.out.println("Local Inner Class");
        }
    }
    Local l = new Local();
    l.msg();
}

Each type of inner class serves a unique purpose, especially in GUI development and encapsulating helper logic.


Advanced Java — Tools, Frameworks, and Concepts

After grasping core Java and object-oriented programming, you’re now ready to explore more advanced features. This section covers essential tools and frameworks that allow Java to power enterprise applications, desktop tools, APIs, and databases. You’ll also dive into Java’s powerful language enhancements introduced from Java 8 onwards.


File Handling and I/O

File Class

The File class in Java is part of java.io and is used for file and directory path representations.

import java.io.File;

public class FileExample {
    public static void main(String[] args) {
        File file = new File("example.txt");

        if (file.exists()) {
            System.out.println("File Name: " + file.getName());
            System.out.println("Path: " + file.getAbsolutePath());
        } else {
            System.out.println("File does not exist.");
        }
    }
}

The class can also be used to create, delete, and check permissions of files and directories.

Reading and Writing Files

Using FileReader and BufferedReader:

import java.io.*;

public class ReadFile {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader("example.txt"));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        reader.close();
    }
}

Writing with FileWriter:

FileWriter writer = new FileWriter("output.txt");
writer.write("Hello, Java File Handling!");
writer.close();

Always remember to close file streams to avoid memory leaks.

Java NIO Package

Introduced in Java 7, the java.nio.file package offers a more efficient, non-blocking approach.

import java.nio.file.*;

public class NIOExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("data.txt");
        Files.write(path, "Welcome to NIO".getBytes());
        List<String> lines = Files.readAllLines(path);
        lines.forEach(System.out::println);
    }
}

NIO is preferred for high-performance and scalable file operations.


Threads and Concurrency

Creating Threads

Using the Thread class:

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread running");
    }
}

new MyThread().start();

 

Using the Runnable interface:

Runnable task = () -> System.out.println("Runnable thread");
new Thread(task).start();

Thread Lifecycle

A thread in Java passes through multiple states:

  1. New

  2. Runnable

  3. Running

  4. Waiting/Blocked

  5. Terminated

These states are managed by the JVM based on scheduling.

Synchronization

Synchronization ensures thread-safe operations, especially when multiple threads access shared resources.

public synchronized void increment() {
    count++;
}

Alternatively, use synchronized blocks for finer control:

synchronized(this) {
    // thread-safe code
}

wait(), notify(), notifyAll()

These methods allow threads to communicate and coordinate.

synchronized(obj) {
    while (condition) {
        obj.wait();
    }
    obj.notify();
}

Use these methods with caution, as improper usage can lead to deadlocks or missed signals.


Lambda Expressions and Functional Interfaces

What Are Lambdas?

Introduced in Java 8, lambda expressions provide a concise way to implement interfaces with a single abstract method (functional interfaces).

Runnable r = () -> System.out.println("Lambda Thread");
r.run();

Built-in Functional Interfaces

Located in java.util.function, these interfaces simplify functional-style programming.

  • Predicate: Evaluates a condition

Predicate<String> checkLength = s -> s.length() > 5;
  • Consumer: Takes a value and returns nothing

Consumer<String> greet = name -> System.out.println("Hi " + name);
  • Function: Takes an input and returns an output

Function<Integer, String> intToString = i -> "Number: " + i;

Streams API (Java 8+)

The Streams API simplifies data processing with operations like map(), filter(), and reduce().

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

names.stream()
    .filter(n -> n.startsWith("C"))
    .map(String::toUpperCase)
    .forEach(System.out::println);

Instead of processing each item manually, streams offer a declarative approach.
They are lazy, meaning operations are only executed when needed.
Moreover, streams can be parallelized, making them efficient for large data sets.


Generics

Generic Classes and Methods

Generics provide type safety and code reusability without casting.

class Box<T> {
    private T value;
    void set(T val) { value = val; }
    T get() { return value; }
}

Generic Method Example:

public <T> void printArray(T[] arr) {
    for (T element : arr) {
        System.out.println(element);
    }
}

Bounded Types

Bound types restrict the types that can be used with generics.

class Stats<T extends Number> {
    T[] nums;
}

This ensures only numerical types are used, preventing runtime errors.

Wildcards

  • <?>: Unknown type

  • ? extends T: Accepts T or its subclasses

  • ? super T: Accepts T or its superclasses

List<? extends Number> list = new ArrayList<Integer>();

Wildcards improve flexibility while preserving type safety.


Annotations and Reflection

Built-in Annotations

It provides several annotations that add metadata to code:

  • @Override: Ensures the method overrides a superclass method

  • @Deprecated: Marks a method as outdated

  • @FunctionalInterface: Ensures only one abstract method

@FunctionalInterface
interface Greetable {
    void greet();
}

Creating Custom Annotations

You can define your own annotations for frameworks or validations.

@interface MyAnnotation {
    String value();
}

Using Reflection

Reflection allows inspection of classes, methods, and fields at runtime.

Class<?> cls = Class.forName("java.util.ArrayList");
Method[] methods = cls.getDeclaredMethods();

for (Method m : methods) {
    System.out.println(m.getName());
}

Reflection is often used in frameworks, but misuse can lead to security issues and performance degradation.


JDBC (Java Database Connectivity)

Connecting to Databases

JDBC enables Java to interact with relational databases.

Connection conn = DriverManager.getConnection(
    "jdbc:mysql://localhost:3306/mydb", "root", "password");

Executing Queries

Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

while (rs.next()) {
    System.out.println(rs.getString("username"));
}

PreparedStatement and ResultSet

Use PreparedStatement to prevent SQL injection:

PreparedStatement ps = conn.prepareStatement(
    "SELECT * FROM users WHERE id = ?");
ps.setInt(1, 10);
ResultSet rs = ps.executeQuery();

Transactions and Exception Handling

try {
    conn.setAutoCommit(false);
    // multiple operations
    conn.commit();
} catch (SQLException e) {
    conn.rollback();
}

Transaction management ensures consistency, especially in financial systems.


Packages and Modularization

Creating and Using Packages

Packages help organize code and avoid naming conflicts.

package com.example.utils;

public class Helper {
    public static void print() {
        System.out.println("Helper class");
    }
}

Use the import keyword to access it:

import com.example.utils.Helper;

Java Modules (Java 9+)

Modules introduced in Java 9 enhance encapsulation and dependency management.

module-info.java:

module com.myapp {
    requires java.sql;
    exports com.myapp.api;
}

Modules allow fine-grained control over what is exposed and what is hidden.


Thanks for visiting! Explore the categories below for more exciting and useful content.


Leave a Reply

Your email address will not be published. Required fields are marked *