Complete Java Fundamentals for Beginners
A comprehensive 12+ hour Java course covering core concepts from setup through advanced topics like threads, collections, and streams. Learn JDK installation, variables, data types, operators, OOP principles, exception handling, multithreading, collection APIs, and functional programming with streams.
Why Java Matters
Java's Dominance in Enterprise
Java consistently ranks in the top 5 programming languages because it supports multiple use cases: enterprise applications, web development, mobile development, and more. Most large companies prefer Java as their primary language.
JVM Enables Language Flexibility
Java's JVM (Java Virtual Machine) allows multiple languages—Kotlin, Scala, Groovy—to run on the same platform. Learning Java makes it easier to transition to other JVM-based languages.
Java's Readability and Maintainability
While Java requires more boilerplate code than Python or JavaScript at first, it is highly readable and maintainable. Once you build larger applications, the line count remains similar, but the code clarity makes Java preferred for long-term projects.
Java History and Versions
Java's Origin and Evolution
Java was created in 1995 by Sun Microsystems under James Gosling's leadership. Oracle later acquired Sun and now maintains Java. The language updates every six months with new features, though not all are major updates.
LTS Versions for Stability
Not every Java version receives long-term support (LTS). Java 17 is an LTS version, making it ideal for production use. Choosing LTS versions ensures stability and extended support.
Setting Up Java Development
JDK vs JRE vs JVM
JDK (Java Development Kit) is for developers and includes the compiler and runtime. JRE (Java Runtime Environment) is for end-users and includes JVM and libraries. JVM (Java Virtual Machine) executes the bytecode. Developers install JDK; users only need JRE.
Installation and Path Configuration
Download JDK 17 (LTS) and set the system PATH variable to the JDK bin folder. Verify installation by running 'java -version' and 'javac -version' in the command prompt.
IDE Choice: VS Code
VS Code is lightweight and suitable for beginners. Other options include Eclipse, IntelliJ IDEA (paid Ultimate version), and NetBeans. VS Code allows you to write, compile, and run Java code with optional extensions.
Java Compilation and Execution
Two-Step Process: Compile and Run
Java requires two steps: (1) Compile .java file to .class bytecode using 'javac filename.java', (2) Run bytecode on JVM using 'java classname'. The .class file is platform-independent bytecode that JVM interprets.
Platform Independence Through JVM
Java is write-once-run-anywhere because the bytecode runs on any JVM. The JVM itself is platform-dependent (Windows, Mac, Linux), but your compiled code works everywhere JVM is installed.
Main Method Requirement
Every Java application needs a main method with the exact signature: 'public static void main(String[] args)'. JVM looks for this method as the entry point to start execution.
Hello World and Basic Syntax
Class and File Structure
Java requires code to be inside a class. The class name should match the filename (e.g., Hello.java contains class Hello). Every Java file must have .java extension.
Printing Output
Use 'System.out.println()' to print text and move to a new line, or 'System.out.print()' to print without a newline. Always end statements with a semicolon.
JShell for Quick Testing
Java 9+ includes JShell, an interactive shell for testing code snippets without compilation. Type 'jshell' in terminal to experiment with Java syntax quickly.
Variables and Data Types
Variable Declaration and Assignment
Variables store data in boxes with a name, type, and value. Syntax: 'type variableName = value;'. Java is strongly typed—you must declare the type before use. The equals sign assigns the right-side value to the left-side variable.
Primitive Data Types Overview
Java has 8 primitive types: byte, short, int, long (integers); float, double (decimals); char (single character); boolean (true/false). Each has a specific size and range.
Integer Type Ranges
Byte ranges from -128 to 127 (1 byte). Short ranges from -32,768 to 32,767 (2 bytes). Int ranges from about -2.1 billion to 2.1 billion (4 bytes). Long supports larger values (8 bytes, append L to literal).
Float vs Double
Float uses 4 bytes with limited precision; double uses 8 bytes with higher precision. By default, decimal literals are treated as double. Append F to specify float (e.g., 5.6F).
Character and Boolean Types
Char stores a single character in single quotes (e.g., 'a') and uses 2 bytes (Unicode). Boolean stores only true or false—not 1 or 0. Both are primitive types.
Number Literals and Formats
Decimal numbers are base 10. Binary literals use 0b prefix (e.g., 0b101 = 5). Hexadecimal literals use 0x prefix (e.g., 0x7E = 126). Underscores in numbers improve readability (e.g., 1_000_000).
Type Conversion and Casting
Implicit Conversion (Widening)
When assigning a smaller type to a larger type, Java automatically converts. Example: byte to int. No data loss occurs because the larger type can hold all values of the smaller type.
Explicit Casting (Narrowing)
To assign a larger type to a smaller type, use explicit casting: '(byte) intValue'. This may lose data. Example: 257 cast to byte becomes 1 (using modulo with range 256).
Type Promotion in Operations
When performing operations on different types, Java promotes smaller types to larger ones. Example: byte * byte results in int. This prevents overflow and data loss.
Operators and Expressions
Arithmetic Operators
Addition (+), subtraction (-), multiplication (*), division (/), and modulus (%) perform basic math. Division of integers returns an integer (truncates decimals). Modulus returns the remainder.
Assignment Operators
Basic assignment (=) assigns right value to left variable. Compound operators (+=, -=, *=, /=) combine operation and assignment. Example: 'num += 2' is equivalent to 'num = num + 2'.
Increment and Decrement
++ increments by 1, -- decrements by 1. Pre-increment (++num) increments before use; post-increment (num++) increments after use. Both modify the variable.
Relational Operators
Compare values: == (equal), != (not equal), > (greater), < (less), >= (greater or equal), <= (less or equal). All return boolean (true/false).
Logical Operators
AND (&&) returns true if both conditions are true. OR (||) returns true if at least one condition is true. NOT (!) inverts the boolean value.
Ternary Operator
Shorthand for if-else: 'condition ? valueIfTrue : valueIfFalse'. Returns one of two values based on a condition.
Control Flow Statements
If-Else Conditionals
Execute code based on conditions. If condition is true, execute if block; otherwise, execute else block. Use else-if for multiple conditions.
Switch Statements
Select one of many code blocks to execute based on a value. Each case must end with break to prevent fall-through. Default case executes if no case matches.
For Loops
Repeat code a fixed number of times. Syntax: 'for(init; condition; increment)'. Initialize counter, check condition, execute body, then increment.
While and Do-While Loops
While loops repeat while condition is true. Do-while loops execute body first, then check condition (always runs at least once).
Enhanced For Loop
Simplified loop for arrays and collections: 'for(type variable : collection)'. Automatically iterates through each element without managing index.
Break and Continue
Break exits the loop immediately. Continue skips the current iteration and moves to the next. Both are useful for controlling loop flow.
Arrays
Array Declaration and Initialization
Arrays store multiple values of the same type in fixed size. Declare with 'type[] arrayName = new type[size]'. Access elements with index starting from 0.
Array Operations
Access elements using index notation (array[0]). Modify elements with assignment. Iterate using for loops or enhanced for loops. Arrays have fixed size—cannot expand.
Multi-dimensional Arrays
Arrays of arrays create matrices. Declare with 'type[][] arrayName'. Access with two indices: array[row][column].
Object-Oriented Programming Basics
Classes and Objects
A class is a blueprint; an object is an instance of a class. Classes define properties (variables) and methods (functions). Create objects with 'new ClassName()'.
Constructors
Constructors initialize objects when created. They have the same name as the class and no return type. Can have parameters to set initial values.
Methods
Methods are functions inside classes. Define with 'returnType methodName(parameters)'. Call with 'object.methodName(arguments)'.
Encapsulation with Access Modifiers
Public members are accessible everywhere. Private members are only accessible within the class. Protected members are accessible within package and subclasses. Use getters and setters to control access.
Inheritance
A class can inherit from another class using 'extends'. Child class inherits properties and methods from parent class. Use 'super' to call parent class methods.
Polymorphism
Objects can take multiple forms. Method overriding allows child classes to redefine parent methods. Method overloading allows multiple methods with the same name but different parameters.
Abstraction
Abstract classes and interfaces define contracts without implementation. Abstract methods must be implemented by subclasses. Interfaces define what a class should do, not how.
Exception Handling
Try-Catch Blocks
Wrap risky code in try block. If exception occurs, catch block handles it. Syntax: 'try { risky code } catch (ExceptionType e) { handle error }'.
Finally Block
Finally block executes regardless of whether exception occurred. Use for cleanup code (closing files, releasing resources). Executes after try or catch block.
Throw and Throws
Throw keyword manually throws an exception. Throws keyword in method signature declares that method may throw exceptions. Caller must handle them.
Common Exception Types
NullPointerException occurs when accessing null object. ArrayIndexOutOfBoundsException occurs with invalid array index. ArithmeticException occurs with division by zero. IOException occurs with file operations.
Multithreading
Thread Creation
Create threads by extending Thread class or implementing Runnable interface. Override run() method with code to execute. Call start() to begin execution, not run().
Thread States
New state: thread created. Runnable state: ready to run. Running state: executing on CPU. Waiting state: paused (sleep, wait). Dead state: execution complete.
Sleep and Join Methods
sleep() pauses thread for milliseconds. join() makes main thread wait for other threads to complete. Both throw InterruptedException.
Synchronization for Shared Resources
When multiple threads access shared variables, use synchronized keyword to ensure only one thread accesses at a time. Prevents data corruption and race conditions.
Race Conditions Example
When two threads increment same variable simultaneously, both may read old value before either writes new value. Result: lost increment. Synchronization prevents this.
Collections Framework
Collection API Overview
Collection API provides interfaces and classes for storing groups of objects. Includes List (ordered, allows duplicates), Set (unique values), Queue (FIFO), and Map (key-value pairs).
ArrayList Implementation
ArrayList is a dynamic array that grows automatically. Use 'List<Integer> list = new ArrayList<>()'. Add elements with add(), access with get(index), remove with remove().
Generics for Type Safety
Use angle brackets to specify element type: 'List<Integer> list'. Prevents ClassCastException at runtime. Compiler catches type mismatches at compile time.
HashSet for Unique Values
HashSet stores unique values only. Duplicates are ignored. Order is not guaranteed. Use when you need fast lookup and uniqueness.
TreeSet for Sorted Values
TreeSet stores unique values in sorted order. Slower than HashSet but maintains order. Use when you need sorted unique values.
HashMap for Key-Value Storage
HashMap stores key-value pairs. Keys must be unique. Use put(key, value) to add, get(key) to retrieve. Order not guaranteed.
Iterator for Collection Traversal
Iterator provides hasNext() and next() methods to traverse collections. Use in while loop: 'while(iterator.hasNext()) { element = iterator.next(); }'.
Sorting and Comparators
Collections.sort() Method
Sort collections using Collections.sort(list). Sorts in natural order (ascending for numbers). Works with any comparable type.
Comparator for Custom Sorting
Implement Comparator interface to define custom sort logic. compare() method returns negative (less), zero (equal), or positive (greater). Pass to sort() method.
Comparable Interface
Implement Comparable in class to define natural sort order. compareTo() method compares this object with another. Returns negative, zero, or positive.
Lambda Expressions for Comparators
Use lambda syntax for concise comparators: '(a, b) -> a.getAge() - b.getAge()'. Functional interface allows single-line implementation.
Stream API
Stream Basics
Stream is a sequence of elements supporting functional operations. Create with collection.stream(). Streams are one-time use—cannot reuse after terminal operation.
Filter Operation
Filter removes elements not matching condition. Syntax: 'stream.filter(predicate)'. Predicate returns boolean. Returns new stream with matching elements.
Map Operation
Map transforms each element. Syntax: 'stream.map(function)'. Function takes element, returns transformed value. Returns new stream with transformed elements.
Reduce Operation
Reduce combines all elements into single value. Syntax: 'stream.reduce(initialValue, (accumulator, element) -> accumulator + element)'. Returns final result.
ForEach Terminal Operation
ForEach executes action for each element. Syntax: 'stream.forEach(action)'. Terminal operation—ends stream pipeline. No return value.
Sorted Operation
Sort stream elements. Syntax: 'stream.sorted()' for natural order or 'stream.sorted(comparator)' for custom order. Returns new sorted stream.
Chaining Stream Operations
Combine multiple operations: 'stream.filter(...).map(...).sorted(...).forEach(...)'. Each intermediate operation returns new stream. Terminal operation ends chain.
Functional Interfaces
Predicate: tests condition, returns boolean. Function: transforms value. Consumer: accepts value, no return. BinaryOperator: combines two values. All support lambda expressions.
Notable quotes
Java is difficult just to start with, but once you make a big application the number of lines will remain almost same. — Naveen Reddy
Java is one of the most readable languages. If you are looking at someone else's code you can read the Java code line by line. — Naveen Reddy
It's not difficult, it's just unfamiliar. — Venkat Subramaniam (referenced by Naveen)
Action items
- Download and install JDK 17 (LTS version) from Oracle website
- Set system PATH variable to JDK bin folder and verify with 'java -version' and 'javac -version'
- Install VS Code and configure it for Java development
- Write and compile your first Hello World program
- Practice creating variables with different primitive data types
- Experiment with type casting and conversion
- Create classes with constructors and methods
- Practice control flow statements (if-else, loops, switch)
- Create and manipulate arrays
- Implement exception handling with try-catch-finally blocks
- Create and run multiple threads with synchronization
- Work with Collections: ArrayList, HashSet, HashMap
- Implement custom sorting with Comparator and Comparable
- Use Stream API for functional operations on collections
- Build a small project combining multiple concepts learned