Java is a high-level, class-based, object-oriented programming language designed to have as few implementation dependencies as possible. It is a general-purpose programming language intended to let programmers write once, run anywhere (WORA), meaning that compiled Java code can run on all platforms that support Java without the need for recompilation.
| Data Type | Size | Range/Description | Storage Units |
|---|---|---|---|
| byte | 8 bits | -128 to 127 | 1 Byte |
| short | 16 bits | -32,768 to 32,767 | 2 Bytes |
| int | 32 bits | -231 to 231-1 | 4 Bytes |
| long | 64 bits | -263 to 263-1 | 8 Bytes |
| float | 32 bits | Single-precision floating point | 4 Bytes |
| double | 64 bits | Double-precision floating point | 8 Bytes |
| char | 16 bits | Single Unicode character ('\u0000' to '\uffff') | 2 Bytes |
| boolean | 1 bit | true or false | 1 bit (typically 1 Byte in memory) |
| Unit | Size | Equivalent |
|---|---|---|
| Bit (b) | 1 binary digit | 0 or 1 |
| Byte (B) | 8 bits | 1 character |
| Kilobyte (KB) | 1024 Bytes | ≈1,000 Bytes |
| Megabyte (MB) | 1024 KB | ≈1 million Bytes |
| Gigabyte (GB) | 1024 MB | ≈1 billion Bytes |
| Terabyte (TB) | 1024 GB | ≈1 trillion Bytes |
| Petabyte (PB) | 1024 TB | ≈1 quadrillion Bytes |
| Exabyte (EB) | 1024 PB | ≈1 quintillion Bytes |
| Zettabyte (ZB) | 1024 EB | ≈1 sextillion Bytes |
| Yottabyte (YB) | 1024 ZB | ≈1 septillion Bytes |
A Token is the smallest unit of code that has meaning to the java compiler,such as keyword,identifire,literals,operators,or symbols.
These are reserved words that have special meaning.
Examples: class, public, static, void, if, else
Whatever the name we are using in java program is called as identifire . Names given to classes, methods, variables etc.
Examples: MyClass, main, age
A literal is a constant value written directly in the source code ,such as a number,character,string, or boolean..
Examples: 100, 3.14, 'A', "Hello", true
Operators are used to perform operations Between variables and values.
Examples: +, -, *, /, %, =, ==
Symbols used to separate code elements.
Examples: ; (semicolon), {} (curly braces), [] (square brackets)
Comments can be used to explain Java code, and to make it more readable. It can also be used to prevent execution when testing alternative code.Single-line Comments
Examples: // single-line, /* multi-line */
Single-line comments start with two forward slashes (//).
System.out.println("Hello World"); // This is a comment
Multi-line comments start with /* and ends with */.
/* The code below will print the words Hello World
to the screen, and it is amazing */
System.out.println("Hello World");
Definition: Cookies are small pieces of data stored on the client's browser. They are sent with each request to the server.
// Creating a cookie in Servlet
Cookie userCookie = new Cookie("user", "JohnDoe");
userCookie.setMaxAge(60*60*24*30); // 30 days
response.addCookie(userCookie);
// Reading cookies
Cookie[] cookies = request.getCookies();
if(cookies != null) {
for(Cookie cookie : cookies) {
if(cookie.getName().equals("user")) {
String value = cookie.getValue();
}
}
}
Definition: Session is a server-side state management technique that stores user-specific data on the server.
// Storing data in session (Servlet)
HttpSession session = request.getSession();
session.setAttribute("username", "JohnDoe");
session.setAttribute("cartItems", cartItems);
// Retrieving data from session
String username = (String) session.getAttribute("username");
List<CartItem> items = (List<CartItem>) session.getAttribute("cartItems");
// Invalidating session (logging out)
session.invalidate();
Java is designed to be easy to learn with clear syntax (similar to C/C++) but without complex features like pointers.
Java follows Object-Oriented principles like Encapsulation, Inheritance, Polymorphism, and Abstraction.
Java code is compiled to bytecode that runs on JVM, making it platform independent ("Write Once Run Anywhere").
Java provides features like no explicit pointers, bytecode verification, and security manager for safe execution.
Java supports multithreaded programming for developing concurrent applications.
Variables are used to store different type data but it depends upon data types.whenever an object of class is created memory is allocated for non-static data members of class.
There are 3 types of variables in Java:
If Variables are define with in the methods, constructors or blocks is called as local variables. They must be initialized before use.
Instance variables are non-static variables and are declared in a class but outside any method, constructor or block.
Whenever an object is created space or memory is allocated for non-static variables in heap area every time.
It data member of a class is declared as static keyword then one memory address of that member will be created method area.
A data type specifies the kind of value a variable can store and how much memory will be allocated for that variable.
Primitive data types are the basic data types that are directly handled by the processor. Their size and range are predefined, and they are the most commonly used data types in java.
Java has 8 primitive data types that are predefined by the language:
| Data Type | Size (Bits) | Range/Description |
|---|---|---|
| byte | 8 | -128 to 127 |
| short | 16 | -32,768 to 32,767 |
| int | 32 | -231 to 231-1 |
| long | 64 | -263 to 263-1 |
| float | 32 | Single-precision floating point |
| double | 64 | Double-precision floating point |
| char | 16 | Single Unicode character ('\u0000' to '\uffff') |
| boolean | 1 (bit) | true or false |
int age = 30; // Integer double price = 19.99; // Floating point char grade = 'A'; // Character boolean isJavaFun = true; // Boolean
Non-primitive data types are reference types created by the programmer, such as classes, arrays, strings, and interfaces. They store references to memory and can hold multiple or complex values.
var is a feature introduced in Java 10 that allows the compiler to infer the type of a local variable from the initializer expression.
var variableName = expression;
var with String
var greeting = "Hello, Java!";
System.out.println(greeting.toUpperCase()); // HELLO, JAVA!
var with List
var names = new ArrayList<String>();
names.add("Alice");
names.add("Bob");
System.out.println(names.get(0)); // Alice
var in For-Each Loop
var numbers = List.of(10, 20, 30);
for (var num : numbers) {
System.out.println(num);
}
var a; ❌ Not allowed — variable must be initializedvar b = null; ❌ Not allowed — type cannot be inferred from nullvoid method(var x); ❌ Not allowed — var not valid in method parameterspublic var getName() {...} ❌ Not allowed — return types cannot use varvar introduced?Java 10, released in March 2018, introduced var as part of JEP 286 - Local-Variable Type Inference.
Access modifiers are used to define the visibility/accessibility level members of a class . Basically security point of view we are using this concept.
public member can be access with in the class as well as outside the class and out side the package.
private member can be access with in class only ,not out side the class.
Accessible within the same package and subclasses.
protected member can be access with in the class as well as out side the class as well as out side package also but that class should be child class of it.
Accessible only within the same package.
public class MyClass {
public int publicVar;
private int privateVar;
protected int protectedVar;
int defaultVar; // default access
public void publicMethod() { }
private void privateMethod() { }
protected void protectedMethod() { }
void defaultMethod() { } // default access
}
Type casting is when you assign a value of one primitive data type to another type.
In Java, there are two types of casting:
Widening casting is done automatically when passing a smaller size type to a larger size type
byte -> short -> char -> int -> long -> float -> double
public class Main {
public static void main(String[] args) {
int myInt = 9;
double myDouble = myInt; // Automatic casting: int to double
System.out.println(myInt); // Outputs 9
System.out.println(myDouble); // Outputs 9.0
}
}
Narrowing casting must be done manually by placing the type in parentheses () in front of the value double -> float -> long -> int -> char -> short -> byte
public class Main {
public static void main(String[] args) {
double myDouble = 9.78d;
int myInt = (int) myDouble; // Manual casting: double to int
System.out.println(myDouble); // Outputs 9.78
System.out.println(myInt); // Outputs 9
}
}
Operators are symbols that perform operations on variables and values.
Used for mathematical operations:
int a = 10, b = 3; int sum = a + b; // 13 int remainder = a % b; // 1 a++; // a becomes 11
Used for comparisons (return boolean):
int x = 5, y = 10; boolean result = x < y; // true
Used for logical operations (return boolean):
boolean a = true, b = false; boolean andResult = a && b; // false boolean orResult = a || b; // true boolean notResult = !a; // false
Used to assign values to variables:
int x = 5; x += 3; // equivalent to x = x + 3; x becomes 8
Used to perform operations on individual bits:
int a = 5; // 0101 in binary int b = 3; // 0011 in binary int result = a & b; // 0001 (1 in decimal)
Ternary operator is a short form if-else statement. Its syntax is: condition ? value_if_true : value_if_false
int a = 10, b = 5; int max = (a > b) ? a : b; // max = 10
control flow statements are used to control program execution. There are 3 types:
Use the if statement to specify a block of Java code to be executed if a condition is true.
if (20 > 18) {
System.out.println("20 is greater than 18");
}
Use the else statement to specify a block of code to be executed if the condition is false.
int time = 20;
if (time < 18) {
System.out.println("Good day.");
} else {
System.out.println("Good evening.");
}
// Outputs "Good evening."
Use the else if statement to specify a new condition if the first condition is false.
int time = 22;
if (time < 10) {
System.out.println("Good morning.");
} else if (time < 18) {
System.out.println("Good day.");
} else {
System.out.println("Good evening.");
}
// Outputs "Good evening."
if (condition1) {
// code if condition1 is true
} else if (condition2) {
// code if condition2 is true
} else {
// code if all conditions are false
}
There is also a short-hand if else, which is known as the ternary operator because it consists of three operands.
int time = 20;
String result = (time < 18) ? "Good day." : "Good evening.";
System.out.println(result);
A switch statement allows you to execute different blocks of code based on the value of a variable. It compares the variable with multiple possible values (cases) and runs the code corresponding to the first match. If no match is found, the default block is executed.
switch (expression) {
case value1:
// code
break;
case value2:
// code
break;
default:
// default code
}
Loop execute a block of code repeatedly while a condition is true.
A for loop is a control flow statement used when you know exactly how many times you want to repeat a block of code.
for (initialization; condition; update) {
// code to repeat
}
while loop executes as long as condition is true. It checks condition first.
while (condition) {
// code to repeat
}
do-while loop is similar to while loop, but condition is checked at the end, so loop body executes at least once.
do {
// code to repeat
} while (condition);
| Feature | Traditional for-loop | forEach |
|---|---|---|
| Syntax | Verbose | Concise |
| Control | Supports break/continue | No control statements |
| Readability | Explicit iteration | Declarative style |
break statement is used to immediately exit a loop or switch statement.
continue statement is used to skip current iteration and jump to next iteration.
Purpose:
A method is a block of code that performs a specific task and can be called when needed. Methods help in organizing code, improving reusability, and making the code more modular.
access_modifier return_type method_name(parameter_list) {
// method body
return value; // if return_type is not void
}
public int addNumbers(int a, int b) {
int sum = a + b;
return sum;
}
1. Reusability: Avoid repeating the same code. Call a method whenever needed.
2. Modularity: Break down complex tasks into smaller, manageable pieces.
3. Maintainability: Easily update and maintain code in one place.
4. Abstraction: Hide complex details, making the code simpler to use.
5. Organization: Keep code structured and clean.
6. Testing: Easier to test individual parts of the code.
7. Reusability Across Programs: Methods can be reused in different programs.
Methods help keep code clean, efficient, and manageable.
Defining multiple method in a class having same name is called as method overloading but Number of arguments or types of arguments must be different. The correct method is called based on the number and type of arguments passed.
1. To perform similar actions in different ways.
2. Improves readability and code reuse.
3. Reduce the complexity .
4. Developer can easily remember that overloaded method by passing different number or different types of arguments.
class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
}
In Java, variables are only accessible inside the region they are created. This is called scope.
Variables declared directly inside a method are available anywhere in the method following the line of code in which they were declared
public class Main {
public static void main(String[] args) {
// Code here CANNOT use x
int x = 100;
// Code here can use x
System.out.println(x);
}
}
A block of code refers to all of the code between curly braces {}.
Variables declared inside blocks of code are only accessible by the code between the curly braces, which follows the line in which the variable was declared
public class Main {
public static void main(String[] args) {
// Code here CANNOT use x
{ // This is a block
// Code here CANNOT use x
int x = 100;
// Code here CAN use x
System.out.println(x);
} // The block ends here
// Code here CANNOT use x
}
}
Recursion is a method calling itself to solve a problem. It helps break down complex problems into smaller sub-problems. A recursive method typically has a base case to stop the recursion and prevent an infinite loop.
class MathOperations {
public int Factorial(int n) {
if (n < 0) {
throw new ArgumentException("Negative numbers not allowed.");
}
if (n == 0)
return 1; // Base case
else
return n * Factorial(n - 1); // Recursive call
}
}
// Usage:
MathOperations math = new MathOperations();
int result = math.Factorial(5); // Returns 120
new keyword allocates memory in the heap and invokes the constructor to initialize the object.
The this keyword refers to the current object of the class — it is used to access the current instance’s fields, properties, or methods.
public class Student {
String name; // Instance variable
public void setName(String name) {
this.name = name; // 'this.name' refers to the instance variable
}
}
1. A class is a blueprint of an objects.
2. Class is a collection of similar type of object means we can create many number of object any class.
3. Class is a user define datatype.
// Class definition
class Car {
// Fields (instance variables)
String color;
int maxSpeed;
// Method
void displayInfo() {
System.out.println("Color: " + color + ", Max Speed: " + maxSpeed);
}
}
public class Main {
public static void main(String[] args) {
// Creating objects
Car car1 = new Car();
car1.color = "Red";
car1.maxSpeed = 200;
Car car2 = new Car();
car2.color = "Blue";
car2.maxSpeed = 180;
// Calling methods
car1.displayInfo();
car2.displayInfo();
}
}
In Java, a static class can only be a nested class — that is, a class defined inside another class.
It is called a static nested class.
Unlike non-static nested classes (inner classes), a static nested class does not have access to the instance members of the outer class. It can only access static members of the outer class.
class OuterClass {
static int outerValue = 100;
// Static nested class
static class NestedClass {
void display() {
System.out.println("Outer value: " + outerValue);
}
}
}
public class Main {
public static void main(String[] args) {
OuterClass.NestedClass obj = new OuterClass.NestedClass();
obj.display(); // Output: Outer value: 100
}
}
static keyword inside another class.Java introduced sealed classes in Java 15 as a preview feature and finalized it in Java 17. A sealed class restricts which classes can extend or implement it using the permits clause.
public sealed class Vehicle permits Car, Bike { }
public final class Car extends Vehicle { }
public final class Bike extends Vehicle { }
If you want a subclass to allow further inheritance, use non-sealed:
public sealed class Animal permits Dog, Cat { }
public non-sealed class Dog extends Animal {
// other classes can still extend Dog
}
public final class Cat extends Animal { }
All subclasses listed in permits must be in the same package or module and must be declared as either sealed, non-sealed, or final.
Sealed classes were introduced in Java 15 (as a preview) and finalized in Java 17. They are used to control inheritance by restricting which classes can extend or implement them.
new keyword allocates memory in the heap and invokes the constructor to initialize the object.
A constructor is a special method that is automatically called when an object is created.
Default constructor is a constructor that does not take any parameters. There are 2 types
When a user creates a zero-parameter constructor manually in a program, it is called a user-defined default constructor.
When your java class does not have any constructor then compiler internaly adds a default constructor in your class at the time of compilation.and such type of constructor is called as system define default constuctor. There are two types of Dedault constructor.
class MyClass {
// Default constructor (created by compiler if not defined)
MyClass() {
}
}
Constructor with parameters.
class Student {
String name;
int age;
// Parameterized constructor
Student(String n, int a) {
name = n;
age = a;
}
}
Copy Constructor is used to copy the data of an existing objet to a newly created object.
class Circle {
int r;
// Copy constructor
Circle(int x) // normal parametrized
{
r = x;
}
Circle(Circle k) // copy constructor
{
this.r=k.r;
}
void area()
{
double a=3.14*r*r;
System.out.println("area of circle is ="+a)
}
public static void main(String[] args) // normal parametrized
{
Circle r1=new Circle(5);
Circle r2=new Circle(10);
Circle r3=new Circle(r1);
//both area is same r1.area(),r2.area();
r1.area();
r2.area();
}
}
If a block is defined as does not have any name ,return type ,arguments list is called as instace block. instance block is automatically executed whenever an object of its class is created but before constructor calling. and execution flow is top to bottom.
Instance Initialization Block (IIB) is used to initialize instance variables. It runs every time an object of the class is created, before the constructor.
class MyClass {
// Instance Initialization Block
{
System.out.println("Instance Initialization Block executed");
}
MyClass() {
System.out.println("Constructor executed");
}
public static void main(String[] args) {
MyClass obj = new MyClass();
}
}
Instance Initialization Block executed Constructor executed
If a block is defined by using statick keyword is called as static block.
public class Demo {
static int x;
// Static block
static {
System.out.println("Static block executed");
x = 10;
}
public static void main(String[] args) {
System.out.println("Main method executed");
System.out.println("Value of x = " + x);
}
}
Static block executed Main method executed Value of x = 10
The final keyword in Java is used to restrict the user. It can be applied to variables, methods, and classes.
If a variable declared as a final than its become as a constant means its value can not change.
class FinalExample {
final int MAX_VALUE = 100; // final variable
void changeValue() {
// MAX_VALUE = 200; // Compile-time error
}
}
If member function of class is declared as final then it cannot be overridden in chield class .
class Parent {
final void display() {
System.out.println("This is a final method");
}
}
class Child extends Parent {
// void display() { } // Compile-time error - cannot override
}
If class is declare as a final then it cannot be extends mens we can not create chield class from it .it is jut opposite to an abstract class . it is bottom class in inheritance herirchy.
final class FinalClass {
// class content
}
// class ChildClass extends FinalClass { } // Compile-time error
A package in Java is used to group related classes. Think of it as a folder in a file directory. We use packages to avoid name conflicts, and to write a better maintainable code. Packages are divided into two categories:
1. Built-in Packages (packages from the Java API)
2. User-defined Packages (create your own packages)
Built-in Packages vo package hote hai jo java me pahle se bane hai .
import java.util.Scanner;
class MyClass {
public static void main(String[] args) {
Scanner myObj = new Scanner(System.in);
System.out.println("Enter username");
String userName = myObj.nextLine();
System.out.println("Username is: " + userName);
}
}
To create your own package, you need to understand that Java uses a file system directory to store them. Just like folders on your computer
package mypack;
class MyPackageClass {
public static void main(String[] args) {
System.out.println("This is my package!");
}
}
Save the file as MyPackageClass.java, and compile it:
C:\Users\Your Name>javac MyPackageClass.java
Then compile the package:
C:\Users\Your Name>javac -d . MyPackageClass.java
Java is an object-oriented language that supports four main OOP concepts:
Encapsulation is tha process of Binding data member (variables) and Member Function (methods) together in a single unit and restricting direct access to some components is called as Encapsulation.
class BankAccount {
private double balance; // private variable
// public methods to access balance
public void deposit(double amount) {
if(amount > 0) balance += amount;
}
public double getBalance() {
return balance;
}
}
Parent class properties and method inherited by child class is called as inheritance.it is technique of creating new classes from existing one the main purpose of inheritance is reusing code .
class Vehicle { // Superclass
void run() {
System.out.println("Vehicle is running");
}
}
class Car extends Vehicle { // Subclass
void accelerate() {
System.out.println("Car is accelerating");
}
}
Polymorphism is a concept by which we can perform a single task in different ways overloading and overriding helps to achive polymorphism. Polymorphism allows methods to do different things based on the object calling them. There are two types of polymorphism:
Defining multiple method in a class having same name is called as method overloading but Number of arguments or types of arguments must be. The correct method is called based on the number and type of arguments passed.
class Calculator {
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("add(2, 3) = " + calc.add(2, 3));
System.out.println("add(1, 2, 3) = " + calc.add(1, 2, 3));
System.out.println("add(2.5, 3.5) = " + calc.add(2.5, 3.5));
}
}
Redefining base class method in derived class having same name ,same return type, same number of or of arguments is called as method overriding.
class Animal {
void sound() {
System.out.println("Animal makes sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows");
}
}
class Main {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.sound(); // Output: Dog barks
a2.sound(); // Output: Cat meows
}
}
@Override annotation to ensure proper overriding.
| Feature | Method Overloading | Method Overriding |
|---|---|---|
| Definition | Same method name with different parameters | Same method name and parameters in parent and child class |
| Polymorphism Type | Compile-time | Runtime |
| Class Relation | Same class | Subclass and superclass |
| Return Type | Can be same or different | Must be same or covariant |
| Inheritance Required | No | Yes |
1. Super keyword is used to represent parent class member , it can be data members,member functions and constructors.
2. Super keyword always use in child class.
3. Super keyword always used in inheritance concept.
4. Super keyword is used always chield class.
super keyword:
class Animal {
Animal() {
System.out.println("Animal constructor called");
}
}
class Dog extends Animal {
Dog() {
super(); // Calls parent class constructor
System.out.println("Dog constructor called");
}
public static void main(String[] args) {
Dog d = new Dog();
}
}
Animal constructor called Dog constructor called
class Animal {
String type = "Animal";
void show() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
String type = "Dog";
void display() {
System.out.println("Type: " + super.type); // Access parent variable
super.show(); // Call parent method
}
public static void main(String[] args) {
Dog d = new Dog();
d.display();
}
}
Type: Animal Animal sound
class Parent {
Parent() {
System.out.println("Parent constructor called");
}
}
class Child extends Parent {
Child()
{
super(); // ye enternaly laga hota hai jiki vjah se parent class ka default constructor execute hojata hai.
System.out.println("Child constructor called");
}
public static void main(String[] args) {
Child c = new Child();
}
}
Parent constructor called Child constructor called
Abstraction is the process of hiding internal implementation but providing services is called as abstraction.
abstract class Shape {
abstract void draw(); // abstract method
void display() {
System.out.println("Displaying shape");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing circle");
}
}
An abstract class in Java is a class that:
abstract keyword
abstract class Animal {
// Abstract method (no implementation)
public abstract void makeSound();
// Concrete method
public void eat() {
System.out.println("Animal is eating");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark bark");
}
}
Contrast with interfaces: "Abstract classes are used when we need to provide common base functionality, while interfaces are better for defining contracts that can be implemented by unrelated classes."
If a method just declare note a define is called as abstract method.
A method which does not have any body (implementation part ) is called as abstract method and class containing such type of method is called as abstract class
Interfaces are just like a abstract class but in abstract class can contain abstract method(without body) as well as concreat methods (with body) both, but in interface we just declare only abstract methods not concreat methods before java 8 .
java 8 se pahle interface ke under concreate method aur statick method nahi bana sakte hai lekin java 8 kebad concreate method aur static method bana sakte hai . tatha java 9 se private method bhi interface ke under banane ka concept agaya hai.
| Feature | Abstract Class | Interface |
|---|---|---|
| Keyword | abstract |
interface |
| Methods | Can have both abstract and concrete methods | Can have abstract methods (Java 7), default and static methods (Java 8+) |
| Fields | Can have instance variables | Fields are public static final only |
| Inheritance | Supports single inheritance | Supports multiple inheritance |
| Constructor | Can have a constructor | Cannot have a constructor |
| When to Use | When classes share common behavior | When unrelated classes need to follow a contract |
// Abstract Class Example
abstract class Animal {
abstract void sound();
void eat() {
System.out.println("Eating...");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Barks");
}
}
// Interface Example
interface Flyable {
void fly();
}
class Bird implements Flyable {
public void fly() {
System.out.println("Flying...");
}
}
A marker interface is an interface with no methods or fields. It provides metadata to the JVM or other code. Common marker interfaces include:
Serializable – Marks a class to allow its objects to be serialized.Cloneable – Allows the object to be cloned using Object.clone().Remote – Used for RMI (Remote Method Invocation).
import java.io.Serializable;
class Employee implements Serializable {
int id;
String name;
}
An enum is a special "class" that represents a group of constants (unchangeable variables, like final variables).
To create an enum, use the enum keyword (instead of class or interface), and separate the constants with a comma. Note that they should be in uppercase letters
enum Level {
LOW,
MEDIUM,
HIGH
}
You can access enum constants with the dot syntax:
Level myVar = Level.MEDIUM;
Enum is short for "enumerations", which means "specifically listed".
An enum can, just like a class, have attributes and methods. The only difference is that enum constants are public, static and final (unchangeable - cannot be overridden).
An enum cannot be used to create objects, and it cannot extend other classes (but it can implement interfaces).
Why And When To Use Enums?
Use enums when you have values that you know aren't going to change, like month days, days, colors, deck of cards, etc.
public class Main {
enum Level {
LOW,
MEDIUM,
HIGH
}
public static void main(String[] args) {
Level myVar = Level.MEDIUM;
System.out.println(myVar);
}
}
The Scanner class is used to get user input, and it is found in the java.util package.
To use the Scanner class, create an object of the class and use any of the available methods found in the Scanner class documentation. In our example, we will use the nextLine() method, which is used to read Strings
import java.util.Scanner; // Import the Scanner class
class Main {
public static void main(String[] args) {
Scanner myObj = new Scanner(System.in); // Create a Scanner object
System.out.println("Enter username");
String userName = myObj.nextLine(); // Read user input
System.out.println("Username is: " + userName); // Output user input
}
}
In the example above, we used the nextLine() method, which is used to read Strings. To read other types, look at the table below
| Method | Description |
|---|---|
| nextBoolean() | Reads a boolean value from the user |
| nextByte() | Reads a byte value from the user |
| nextDouble() | Reads a double value from the user |
| nextFloat() | Reads a float value from the user |
| nextInt() | Reads a int value from the user |
| nextLine() | Reads a String value from the user |
| nextLong() | Reads a long value from the user |
| nextShort() | Reads a short value from the user |
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner myObj = new Scanner(System.in);
System.out.println("Enter name, age and salary:");
// String input
String name = myObj.nextLine();
// Numerical input
int age = myObj.nextInt();
double salary = myObj.nextDouble();
// Output input by user
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Salary: " + salary);
}
}
Java does not have a built-in Date class, but we can import the java.time package to work with the date and time API. The package includes many date and time classes. For example
| class | Description |
|---|---|
| LocalDate | Represents a date (year, month, day (yyyy-MM-dd)) |
| LocalTime | Represents a time (hour, minute, second and nanoseconds (HH-mm-ss-ns)) |
| LocalDateTime | Represents both a date and a time (yyyy-MM-dd-HH-mm-ss-ns) |
| DateTimeFormatter | Formatter for displaying and parsing date-time objects |
Display Current Date To display the current date, import the java.time.LocalDate class, and use its now() method:
import java.time.LocalDate; // import the LocalDate class
public class Main {
public static void main(String[] args) {
LocalDate myObj = LocalDate.now(); // Create a date object
System.out.println(myObj); // Display the current date
}
}
Display Current Time To display the current time (hour, minute, second, and nanoseconds), import the java.time.LocalTime class, and use its now() method:
import java.time.LocalTime; // import the LocalTime class
public class Main {
public static void main(String[] args) {
LocalTime myObj = LocalTime.now();
System.out.println(myObj);
}
}
Display Current Date and Time To display the current date and time, import the java.time.LocalDateTime class, and use its now() method:
import java.time.LocalDateTime; // import the LocalDateTime class
public class Main {
public static void main(String[] args) {
LocalDateTime myObj = LocalDateTime.now();
System.out.println(myObj);
}
}
Debugging is the process of identifying and fixing errors or bugs in your code.
A regular expression is a sequence of characters that forms a search pattern. When you search for data in a text, you can use this search pattern to describe what you are searching for.
A regular expression can be a single character, or a more complicated pattern.
Regular expressions can be used to perform all types of text search and text replace operations.
Java does not have a built-in Regular Expression class, but we can import the java.util.regex package to work with regular expressions. The package includes the following classes:
Pattern Class - Defines a pattern (to be used in a search)
Matcher Class - Used to search for the pattern
PatternSyntaxException Class - Indicates syntax error in a regular expression pattern
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("w3schools", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher("Visit W3Schools!");
boolean matchFound = matcher.find();
if(matchFound) {
System.out.println("Match found");
} else {
System.out.println("Match not found");
}
}
}
// Outputs Match found
Both composition and aggregation represent "has-a" relationships in OOP but differ in ownership and lifecycle.
| Aspect | Composition | Aggregation |
|---|---|---|
| Relationship | Strong | Weak |
| Object Lifecycle | Child object cannot exist without parent | Child object can exist independently |
| Example | House has-a Room (Room doesn't exist without House) | Department has-a Teacher (Teacher exists without Department) |
class Engine {
void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine = new Engine(); // composition
void drive() {
engine.start();
System.out.println("Car is moving");
}
}
class Teacher {
String name;
Teacher(String name) {
this.name = name;
}
}
class Department {
Teacher teacher; // aggregation
Department(Teacher teacher) {
this.teacher = teacher;
}
}
Exception is an event that disrupts the normal flow of the program.
public class ExceptionExample {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
int result = a / b; // This will throw ArithmeticException
} catch (ArithmeticException ex) {
System.out.println("Cannot divide by zero");
System.out.println(ex.getMessage());
} catch (Exception ex) {
System.out.println("An error occurred");
System.out.println(ex.getMessage());
} finally {
System.out.println("This block always executes");
}
}
}
Lambda expressions were introduced in Java 8. They provide a clear and concise way to represent an anonymous function (a function without a name).
(parameters) -> { expression or block of code }
Streams, Collections, and functional interfaces
Lambda expressions can only be used with functional interfaces (interfaces with only one abstract method), like:
Runnable, Comparator, ActionListener, or custom interfaces.
Runnable r = () -> System.out.println("Running via Lambda");
new Thread(r).start();
@FunctionalInterface
interface MyFunction {
void sayHello();
}
public class Main {
public static void main(String[] args) {
MyFunction mf = () -> System.out.println("Hello from Lambda!");
mf.sayHello();
}
}
A String is an immutable group of characters represented in double quotes.
string class is available in java.lang package .it is final class means we can not extends this class.
concat(), replace() returns a new String.
String s1 = "Hello";
String s2 = s1.concat(" World");
System.out.println(s1); // Hello
System.out.println(s2); // Hello World
StringBuilder is a mutable class used to modify strings without creating new objects. It is not synchronized, so it is not thread-safe but faster.
append(), insert(), delete(), etc.
StringBuilder sb = new StringBuilder("Hello");
sb.append(" Java");
System.out.println(sb); // Hello Java
StringBuffer is similar to StringBuilder, but it is synchronized. It is thread-safe and used in multi-threaded environments where string content is frequently modified.
append() and insert() are synchronized.
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" Java");
System.out.println(sbf); // Hello Java
| Feature | String | StringBuffer |
|---|---|---|
| Mutability | Immutable | Mutable |
| Thread Safety | Thread-safe by immutability | Thread-safe (synchronized) |
| Performance | Slower (for changes) | Faster than String (for changes) |
| Memory Usage | More (creates new objects) | Less (same object is modified) |
| Use Case | Fixed or constant text | Text modified by multiple threads |
| Feature | String | StringBuffer |
|---|---|---|
| Mutability | Immutable (value can't be changed once created) | Mutable (value can be changed) |
| Thread Safety | Thread-safe due to immutability | Thread-safe (methods are synchronized) |
| Performance | Slower for modifications (creates new object) | Faster than String for modifications |
| Memory Usage | Consumes more memory (new object for every change) | Efficient memory usage (modifies existing object) |
| Use Case | When text doesn't change often | When string needs to be modified by multiple threads |
| Method Support | Uses + operator or concat() method | Provides append(), insert(), delete() etc. |
| Feature | StringBuilder | StringBuffer |
|---|---|---|
| Mutability | Mutable | Mutable |
| Thread Safety | Not thread-safe | Thread-safe (synchronized methods) |
| Performance | Faster (no overhead of synchronization) | Slower than StringBuilder (due to synchronization) |
| Usage | Used in single-threaded environments | Used in multi-threaded environments |
| Introduced in | Java 1.5 | Java 1.0 |
| Package | java.lang | java.lang |
The Java Math class has many methods that allows you to perform mathematical tasks on numbers.
The Math.max(x,y) method can be used to find the highest value of x and y
Math.max(5, 10);
The Math.min(x,y) method can be used to find the lowest value of x and y
Math.min(5, 10);
The Math.sqrt(x) method returns the square root of x:
Math.sqrt(64);
The Math.abs(x) method returns the absolute (positive) value of x
Math.abs(-4.7);
Math.random() returns a random number between 0.0 (inclusive), and 1.0 (exclusive)
Math.random();
Very often, in programming, you will need a data type that can only have one of two values, like:
1. YES / NO
2. ON / OFF
3. TRUE / FALSE
For this, Java has a boolean data type, which can store true or false values.
A boolean type is declared with the boolean keyword and can only take the values true or false
boolean isJavaFun = true;
boolean isFishTasty = false;
System.out.println(isJavaFun); // Outputs true
System.out.println(isFishTasty); // Outputs false
If multiple Threads are running simultaneously in the same program, it is called Multi Threading.
A thread is a class that present in java.lang package . it is predefine class in java .
The Thread.sleep() method:
InterruptedException (checked exception)
public class SleepExample {
public static void main(String[] args) {
System.out.println("Starting countdown...");
try {
for (int i = 5; i > 0; i--) {
System.out.println(i);
// Pause for 1 second (1000 milliseconds)
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.err.println("Thread was interrupted");
}
System.out.println("Liftoff!");
}
}
Starting countdown... 5 4 3 2 1 Liftoff!
sleep() doesn't release locks it holdsInterruptedException properlyjava.util.concurrent classes
// Using TimeUnit (more readable)
TimeUnit.SECONDS.sleep(1);
// Using ScheduledExecutorService (better for production)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(() -> {
System.out.println("Delayed task");
}, 2, TimeUnit.SECONDS);
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
File handling in Java is done using classes from the java.io and java.nio.file packages. It allows you to create, read, write, and delete files and directories.
File – For file/directory operations (create, delete, check)FileReader – For reading characters from a fileBufferedReader – For efficient reading of textFileWriter – For writing characters to a fileBufferedWriter – For efficient writingScanner – For reading file line by lineFiles – Utility class in java.nio.file for advanced operations
import java.io.File;
import java.io.IOException;
public class CreateFileExample {
public static void main(String[] args) {
try {
File file = new File("example.txt");
if (file.createNewFile()) {
System.out.println("File created: " + file.getName());
} else {
System.out.println("File already exists.");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.FileWriter;
import java.io.IOException;
public class WriteFileExample {
public static void main(String[] args) {
try {
FileWriter writer = new FileWriter("example.txt");
writer.write("Hello, Java File Handling!");
writer.close();
System.out.println("Successfully written to the file.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFileExample {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader("example.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.File;
public class DeleteFileExample {
public static void main(String[] args) {
File file = new File("example.txt");
if (file.delete()) {
System.out.println("Deleted the file: " + file.getName());
} else {
System.out.println("Failed to delete the file.");
}
}
}
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ScannerReadFile {
public static void main(String[] args) {
try {
File file = new File("example.txt");
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
String data = scanner.nextLine();
System.out.println(data);
}
scanner.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class NioReadExample {
public static void main(String[] args) {
try {
List<String> lines = Files.readAllLines(Paths.get("example.txt"));
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
An array is a linear data structure that stores elements of the same data type in contiguous memory locations under a single variable name, with each element accessible via an index.
// Syntax dataType[] arrayName = new dataType[size];
A two-dimensional array is an array of arrays that represents a table/matrix with rows and columns.
// Declaration
int[][] matrix = new int[3][4]; // 3 rows, 4 columns
// Literal initialization
int[][] chessBoard = {
{1, 0, 1},
{0, 1, 0},
{1, 0, 1}
};
// Accessing elements int value = matrix[1][2]; // 2nd row, 3rd column // Traversing for(int i=0; i
Arrays with more than two dimensions (3D, 4D etc.) used for complex data structures like tensors or spatial data.
// 3D Array
int[][][] cube = new int[3][3][3]; // 3x3x3 cube
// Initialization
int[][][] rgbImage = {
{{255,0,0}, {0,255,0}, {0,0,255}}, // Red, Green, Blue
{{255,255,0}, {255,0,255}, {0,255,255}}, // Yellow, Magenta, Cyan
{{255,255,255}, {0,0,0}, {128,128,128}} // White, Black, Gray
};
// Accessing 3D array int pixel = rgbImage[0][1][2]; // First layer, second row, third column // Traversing 3D array for(int i=0; i
Wrapper classes are object representations of primitive data types in Java. They "wrap" primitive values into objects.
| Primitive Type | Wrapper Class |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
Automatic conversion of primitive types to their corresponding wrapper class objects.
// Manual boxing (pre-Java 5) Integer num = Integer.valueOf(10); // Automatic boxing (Java 5+) Integer num = 10; // primitive int automatically converted to Integer object
Automatic conversion of wrapper class objects back to primitive types.
// Manual unboxing (pre-Java 5) int n = num.intValue(); // Automatic unboxing (Java 5+) int n = num; // Integer object automatically converted to primitive int
| Operation | Direction | Example |
|---|---|---|
| Boxing | Primitive → Wrapper | Integer x = 5; |
| Unboxing | Wrapper → Primitive | int y = x; |
ArrayList<Integer>)
import java.util.ArrayList;
public class WrapperExample {
public static void main(String[] args) {
// Boxing
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10); // Autoboxing
// Unboxing
int first = numbers.get(0); // Autounboxing
// Using wrapper class methods
String binary = Integer.toBinaryString(10);
System.out.println("Binary: " + binary);
}
}
Java provides a comprehensive collections framework in the java.util package for storing and manipulating groups of objects.
| Type | Size | Range | Wrapper Class | Example |
|---|---|---|---|---|
| byte | 1 byte | -128 to 127 | Byte | byte b = 100; |
| short | 2 bytes | -32,768 to 32,767 | Short | short s = 5000; |
| int | 4 bytes | -2³¹ to 2³¹-1 | Integer | int i = 100000; |
| long | 8 bytes | -2⁶³ to 2⁶³-1 | Long | long l = 15000000000L; |
| float | 4 bytes | ±1.4E-45 to ±3.4E+38 | Float | float f = 5.75f; |
| double | 8 bytes | ±4.9E-324 to ±1.7E+308 | Double | double d = 19.99d; |
| boolean | 1 bit | true/false | Boolean | boolean flag = true; |
| char | 2 bytes | '\u0000' to '\uffff' | Character | char c = 'A'; |
Fixed-size sequential collection of elements of the same type
// Declaration and initialization
int[] numbers = new int[5];
int[] numbers = {1, 2, 3, 4, 5};
// Accessing elements
int first = numbers[0]; // 1
numbers[1] = 10; // Modify second element
// Length property
int size = numbers.length; // 5
// 2D array
int[][] matrix = new int[3][3];
int[][] matrix = {{1,2,3}, {4,5,6}, {7,8,9}};
// Accessing elements
int value = matrix[1][2]; // 6
Resizable array implementation of the List interface
import java.util.ArrayList; ArrayListfruits = new ArrayList<>(); // Adding elements fruits.add("Apple"); fruits.add("Banana"); fruits.add(1, "Orange"); // Insert at index 1 // Accessing elements String first = fruits.get(0); // "Apple" // Removing elements fruits.remove(2); // Remove by index fruits.remove("Banana"); // Remove by value // Size int count = fruits.size(); // Iteration for (String fruit : fruits) { System.out.println(fruit); }
Doubly-linked list implementation of List and Deque interfaces
import java.util.LinkedList; LinkedListnumbers = new LinkedList<>(); // Adding elements numbers.add(10); numbers.addFirst(5); // Add to beginning numbers.addLast(20); // Add to end // Accessing elements int first = numbers.getFirst(); // 5 int last = numbers.getLast(); // 20 // Removing elements numbers.removeFirst(); // Remove first numbers.removeLast(); // Remove last // Size int size = numbers.size(); // As Queue numbers.offer(30); // Add to end int head = numbers.poll(); // Remove from head
LIFO (Last-In-First-Out) structure extending Vector
import java.util.Stack; Stackstack = new Stack<>(); // Pushing elements stack.push(10); stack.push(20); stack.push(30); // Popping elements int top = stack.pop(); // 30 // Peeking int next = stack.peek(); // 20 // Checking empty boolean isEmpty = stack.empty(); // Searching int position = stack.search(10); // 1-based position
FIFO (First-In-First-Out) collection
import java.util.Queue; import java.util.LinkedList; Queuequeue = new LinkedList<>(); // Adding elements queue.add("First"); queue.offer("Second"); // Alternative to add // Removing elements String head = queue.remove(); // "First" String next = queue.poll(); // "Second" // Inspecting String peek = queue.peek(); // null if empty
Hash table based implementation of Map interface
import java.util.HashMap; HashMapageMap = new HashMap<>(); // Adding entries ageMap.put("Alice", 25); ageMap.put("Bob", 30); ageMap.put(null, 0); // Null key // Accessing values int aliceAge = ageMap.get("Alice"); // 25 int unknownAge = ageMap.getOrDefault("Charlie", -1); // -1 // Checking existence boolean hasAlice = ageMap.containsKey("Alice"); // true // Removing entries ageMap.remove("Bob"); // Iterating for (Map.Entry entry : ageMap.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } // Size int size = ageMap.size();
Red-Black tree based NavigableMap implementation
import java.util.TreeMap; TreeMapsortedMap = new TreeMap<>(); // Adding entries sortedMap.put("Orange", 2); sortedMap.put("Apple", 5); sortedMap.put("Banana", 3); // Entries are stored in sorted order System.out.println(sortedMap); // {Apple=5, Banana=3, Orange=2} // First and last entries String firstKey = sortedMap.firstKey(); // "Apple" String lastKey = sortedMap.lastKey(); // "Orange" // Range views SortedMap subMap = sortedMap.subMap("Apple", "Orange");
Hash table based Set implementation
import java.util.HashSet; SetuniqueNames = new HashSet<>(); // Adding elements uniqueNames.add("Alice"); uniqueNames.add("Bob"); uniqueNames.add("Alice"); // Duplicate ignored // Checking membership boolean hasAlice = uniqueNames.contains("Alice"); // true // Size int size = uniqueNames.size(); // 2 // Iterating for (String name : uniqueNames) { System.out.println(name); } // Removing elements uniqueNames.remove("Bob");
Red-Black tree based NavigableSet implementation
import java.util.TreeSet; SetsortedNumbers = new TreeSet<>(); // Adding elements sortedNumbers.add(5); sortedNumbers.add(2); sortedNumbers.add(8); // Elements are stored sorted System.out.println(sortedNumbers); // [2, 5, 8] // First and last elements int first = ((TreeSet ) sortedNumbers).first(); // 2 int last = ((TreeSet ) sortedNumbers).last(); // 8 // Range views Set subset = ((TreeSet ) sortedNumbers).headSet(5);
Heap-based priority queue
import java.util.PriorityQueue; PriorityQueueminHeap = new PriorityQueue<>(); // Adding elements minHeap.add(10); minHeap.add(5); minHeap.add(20); // Removing elements (always removes smallest) int min = minHeap.poll(); // 5 int next = minHeap.peek(); // 10 // Custom ordering PriorityQueue maxHeap = new PriorityQueue<>(Collections.reverseOrder()); maxHeap.add(10); maxHeap.add(5); maxHeap.add(20); int max = maxHeap.poll(); // 20
Synchronized dynamic array
import java.util.Vector; Vectorvec = new Vector<>(); // Adding elements vec.add("Element1"); vec.addElement("Element2"); // Legacy method // Accessing elements String first = vec.get(0); String last = vec.lastElement(); // Size int size = vec.size(); int capacity = vec.capacity(); // Current capacity
Synchronized hash table
import java.util.Hashtable; Hashtabletable = new Hashtable<>(); // Adding entries table.put("One", 1); table.put("Two", 2); // Accessing values int value = table.get("One"); // Enumeration (legacy iteration) Enumeration keys = table.keys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); System.out.println(key + ": " + table.get(key)); }
Thread-safe hash table with high concurrency
import java.util.concurrent.ConcurrentHashMap; ConcurrentHashMapconcurrentMap = new ConcurrentHashMap<>(); // Atomic operations concurrentMap.putIfAbsent("Key", 1); concurrentMap.computeIfPresent("Key", (k, v) -> v + 1); // Safe iteration for (Map.Entry entry : concurrentMap.entrySet()) { // Iteration is thread-safe }
Thread-safe variant of ArrayList
import java.util.concurrent.CopyOnWriteArrayList; CopyOnWriteArrayListsafeList = new CopyOnWriteArrayList<>(); // Thread-safe operations safeList.add("Item1"); safeList.addIfAbsent("Item2"); // Thread-safe iteration for (String item : safeList) { // No ConcurrentModificationException }
| Data Structure | Access | Search | Insertion | Deletion | Notes |
|---|---|---|---|---|---|
| Array | O(1) | O(n) | O(n) | O(n) | Fixed size |
| ArrayList | O(1) | O(n) | O(1) amortized (end) O(n) (middle) |
O(n) | Dynamic resizing |
| LinkedList | O(n) | O(n) | O(1) | O(1) | Good for frequent insertions/deletions |
| HashSet/HashMap | O(1) | O(1) | O(1) | O(1) | Average case, depends on hash function |
| TreeSet/TreeMap | O(log n) | O(log n) | O(log n) | O(log n) | Elements stored in sorted order |
| PriorityQueue | O(1) peek | O(n) | O(log n) | O(log n) | Heap implementation |
Iterable (Interface)
Collection (Interface)
List (Interface)
ArrayList (Class)
LinkedList (Class)
Vector (Class) [Legacy]
Stack (Class) [Legacy]
Set (Interface)
HashSet (Class)
LinkedHashSet (Class)
TreeSet (Class)
Queue (Interface)
PriorityQueue (Class)
LinkedList (Class)
ArrayDeque (Class)
Map (Interface)
HashMap (Class)
LinkedHashMap (Class)
TreeMap (Class)
Hashtable (Class) [Legacy]
// Sorting Collections.sort(list); Collections.sort(list, comparator); // Searching int index = Collections.binarySearch(sortedList, key); // Synchronization ListsyncList = Collections.synchronizedList(list); // Immutable views List unmodifiable = Collections.unmodifiableList(list); // Empty collections List empty = Collections.emptyList(); // Singleton Set single = Collections.singleton("Item"); // Frequency int count = Collections.frequency(list, "Item");
// Sorting
Arrays.sort(array);
Arrays.sort(array, comparator);
// Searching
int index = Arrays.binarySearch(sortedArray, key);
// Filling
Arrays.fill(array, value);
// Copying
String[] copy = Arrays.copyOf(original, newLength);
// Comparison
boolean equal = Arrays.equals(array1, array2);
// String representation
String str = Arrays.toString(array);
Simple username/password verification:
Traditional web login forms:
Token-based authentication for APIs:
| Practice | Implementation |
|---|---|
| Password Hashing | Use BCrypt with strength 10-12 |
| Session Management | Use STATELESS for APIs, secure cookies for web |
| CSRF Protection | Enable for stateful apps, disable for APIs |
| CORS Configuration | Restrict origins, headers, and methods |
| Security Headers | Add CSP, XSS protection, HSTS |
Filters execute in the following order:
javax.servlet.annotation.ServletFilter interface
and use the @ServletFilter annotation's filterName
and ordering attributes.
| Method | Description | When Called |
|---|---|---|
| init() | Initialization method | When the filter is first loaded |
| doFilter() | Processes requests/responses | For each request |
| destroy() | Cleanup method | When the filter is unloaded |
Dependency Injection is a design pattern where objects receive their dependencies from an external source rather than creating them internally.
Imagine a car (your class) that needs an engine (dependency). Instead of building its own engine, it receives a ready-to-use engine from a factory.
public class Car {
private Engine engine;
// Dependency injected via constructor
public Car(Engine engine) {
this.engine = engine;
}
}
public class Car {
private Engine engine;
// Dependency injected via setter
public void setEngine(Engine engine) {
this.engine = engine;
}
}
public class Car {
@Inject // Annotation-based (Framework specific)
private Engine engine;
}
Frameworks that manage dependency injection:
@Autowired@Inject
// Service interface
public interface MessageService {
String getMessage();
}
// Implementation
@Service
public class EmailService implements MessageService {
public String getMessage() {
return "Email message";
}
}
// Client class
@Controller
public class NotificationController {
private final MessageService service;
// Constructor injection
@Autowired
public NotificationController(MessageService service) {
this.service = service;
}
public void sendNotification() {
String message = service.getMessage();
// Send notification
}
}
| Dependency Injection | Traditional |
|---|---|
| Dependencies provided externally | Dependencies created internally |
| Easier to test (can inject mocks) | Harder to test (real dependencies) |
| More flexible configuration | Rigid implementation |
The Streams API was introduced in Java 8. It provides a functional-style way to process collections of data without modifying the original data. Streams allow operations like filtering, mapping, and reducing in a clean, declarative manner.
filter() – Filters elements based on a conditionmap() – Transforms each elementcollect() – Collects stream results into a list, set, or mapforEach() – Performs action on each elementsorted() – Sorts elementscount() – Returns the count of elementsdistinct() – Removes duplicates
List<Integer> numbers = Arrays.asList(10, 15, 20, 25, 30);
List<Integer> filtered = numbers.stream()
.filter(n -> n > 20)
.collect(Collectors.toList());
System.out.println(filtered); // Output: [25, 30]
List<String> names = Arrays.asList("john", "jane", "jack");
names.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
// Output:
// JOHN
// JANE
// JACK
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream()
.forEach(n -> System.out.println(n));
Generics in Java allow you to define classes, interfaces, and methods with a placeholder for the data type. This enables code reusability and type safety by ensuring that you work with the intended data types at compile time.
class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.set("Hello Generics");
System.out.println(stringBox.get());
Box<Integer> intBox = new Box<>();
intBox.set(123);
System.out.println(intBox.get());
}
}
public class Main {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
public static void main(String[] args) {
Integer[] numbers = {1, 2, 3};
String[] names = {"Java", "Python"};
printArray(numbers);
printArray(names);
}
}
interface Printer<T> {
void print(T value);
}
class StringPrinter implements Printer<String> {
public void print(String value) {
System.out.println("Printing String: " + value);
}
}
You can restrict the types that can be used with generics using extends.
class Calculator<T extends Number> {
public double square(T number) {
return number.doubleValue() * number.doubleValue();
}
}
? extends T – Accepts T or its subclass? super T – Accepts T or its superclass? – Unknown type
public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
API stands for Application Programming Interface. In Java, an API is a collection of predefined classes, interfaces, and methods that provide functionality to perform various programming tasks.
String, Math, ObjectArrayList, HashMap, CollectionsSocket, URL
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("API");
list.add("Example");
for (String item : list) {
System.out.println(item);
}
}
}
You can create a RESTful API in Java using the Spring Boot framework. It allows clients to make HTTP requests and receive data (like JSON).
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello() {
return "Hello from Java API!";
}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ApiApp {
public static void main(String[] args) {
SpringApplication.run(ApiApp.class, args);
}
}
http://localhost:8080/helloHello from Java API!Java Annotations are metadata that provide additional information to the compiler or runtime environment. They do not change the actual logic of the program but can be used to instruct the compiler, generate code, or configure frameworks like Spring and Hibernate.
@Override: Indicates that a method overrides a method in its superclass@Deprecated: Marks a method or class as deprecated (not recommended for use)@SuppressWarnings: Suppresses compiler warnings@FunctionalInterface: Ensures an interface has only one abstract method@SafeVarargs: Suppresses unsafe operation warnings on varargs methods
class Animal {
void sound() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Bark");
}
}
You can also create your own annotations using the @interface keyword.
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {
String value();
}
public class Test {
@MyAnnotation(value = "Hello Annotation")
public void show() {
System.out.println("Using custom annotation");
}
}
@Retention: Defines how long annotations are retained (SOURCE, CLASS, RUNTIME)@Target: Specifies where the annotation can be applied (method, class, field, etc.)@Inherited: Indicates that a subclass inherits the annotation@Documented: Indicates that the annotation should be included in JavadocJava Annotations help add behavior and configuration to your code without cluttering logic. They are essential for modern Java frameworks and clean application development.
Java Memory Management is the process of allocating and deallocating memory automatically. It helps in efficiently using memory and avoiding memory leaks or overuse. Java uses an automatic garbage collector to manage memory.
new keyword.Java uses automatic garbage collection to reclaim memory. When an object is no longer reachable, the Garbage Collector frees its memory.
public class MemoryDemo {
public static void main(String[] args) {
MemoryDemo obj = new MemoryDemo(); // allocated in heap
obj = null; // eligible for garbage collection
System.gc(); // request GC
}
@Override
protected void finalize() {
System.out.println("Object is garbage collected");
}
}
StringBuilder for string concatenation in loopsnull when no longer neededJava Memory Management automates the process of memory allocation and garbage collection. Understanding how memory works helps in writing optimized and efficient Java applications.
// Client Socket Example
Socket clientSocket = new Socket("localhost", 8080);
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
out.println("Hello Server");
clientSocket.close();
// ServerSocket Example
ServerSocket serverSocket = new ServerSocket(8080);
Socket client = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String message = in.readLine();
System.out.println("Received: " + message);
serverSocket.close();
URL url = new URL("https://api.example.com/data");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
}
conn.disconnect();
// Server Example
Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress("localhost", 8080));
serverSocket.configureBlocking(false);
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
SocketChannel client = serverSocket.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
client.read(buffer);
String result = new String(buffer.array()).trim();
System.out.println("Message: " + result);
client.close();
}
iter.remove();
}
}
// MySQL connection example
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, username, password)) {
System.out.println("Connected to database!");
} catch (SQLException e) {
e.printStackTrace();
}
// Statement
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
// PreparedStatement
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO users VALUES (?, ?)");
pstmt.setInt(1, 101);
pstmt.setString(2, "John");
pstmt.executeUpdate();
// CallableStatement (for stored procedures)
CallableStatement cstmt = conn.prepareCall("{call get_user_details(?)}");
cstmt.setInt(1, 101);
ResultSet rs = cstmt.executeQuery();
// HikariCP example
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(10);
try (HikariDataSource ds = new HikariDataSource(config);
Connection conn = ds.getConnection()) {
// Use connection
}
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("default");
optional.ifPresent(n -> System.out.println("Name: " + n));
String value = optional.orElseThrow(() -> new RuntimeException("Value not present"));
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// Long running task
return "Result";
});
future.thenAccept(result -> System.out.println("Got: " + result))
.exceptionally(ex -> {
System.out.println("Error: " + ex.getMessage());
return null;
});
// Chaining
CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> 10)
.thenApply(x -> x * 2)
.thenApply(x -> x + 5);
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalTime now = LocalTime.now();
LocalTime later = now.plusHours(2);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
String formatted = zdt.format(formatter);
public record Person(String name, int age) {}
Person p = new Person("John", 30);
System.out.println(p.name()); // Accessor
System.out.println(p); // toString()
// Pattern matching for instanceof
if (obj instanceof String s) {
System.out.println(s.length());
}
// Switch pattern matching
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.length() * r.width();
default -> 0;
};
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
interface Shape {
void draw();
}
class Circle implements Shape {
public void draw() { System.out.println("Drawing circle"); }
}
class ShapeFactory {
public Shape getShape(String type) {
if (type.equalsIgnoreCase("CIRCLE")) {
return new Circle();
}
return null;
}
}
interface Observer {
void update(String message);
}
class ConcreteObserver implements Observer {
public void update(String message) {
System.out.println("Received: " + message);
}
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer o) {
observers.add(o);
}
public void notifyObservers(String message) {
for (Observer o : observers) {
o.update(message);
}
}
}
public class User {
private final String firstName;
private final String lastName;
private User(Builder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
}
public static class Builder {
private String firstName;
private String lastName;
public Builder firstName(String firstName) {
this.firstName = firstName;
return this;
}
public Builder lastName(String lastName) {
this.lastName = lastName;
return this;
}
public User build() {
return new User(this);
}
}
}
// Usage:
User user = new User.Builder()
.firstName("John")
.lastName("Doe")
.build();
interface PaymentStrategy {
void pay(int amount);
}
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via credit card");
}
}
class ShoppingCart {
private PaymentStrategy strategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void checkout(int amount) {
strategy.pay(amount);
}
}
Class<?> clazz = Class.forName("java.lang.String");
System.out.println("Class name: " + clazz.getName());
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m.getName());
}
Class<?> clazz = MyClass.class;
Field privateField = clazz.getDeclaredField("secret");
privateField.setAccessible(true);
MyClass obj = new MyClass();
String value = (String) privateField.get(obj);
System.out.println("Private value: " + value);
Class<?> clazz = Class.forName("com.example.MyClass");
Constructor<?> constructor = clazz.getConstructor(String.class);
Object instance = constructor.newInstance("test");
Method method = clazz.getMethod("doSomething");
method.invoke(instance);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new FilePermission("/tmp/test.txt", "read"));
}
// Hashing with MessageDigest
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest("password".getBytes());
// Encryption with Cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey key = new SecretKeySpec("0123456789ABCDEF".getBytes(), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal("secret".getBytes());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(kp.getPrivate());
sig.update("data".getBytes());
byte[] signature = sig.sign();
// Java code
public class NativeDemo {
public native void sayHello();
static {
System.loadLibrary("NativeDemo");
}
public static void main(String[] args) {
new NativeDemo().sayHello();
}
}
// C implementation
#include <jni.h>
#include <stdio.h>
#include "NativeDemo.h"
JNIEXPORT void JNICALL Java_NativeDemo_sayHello(JNIEnv *env, jobject obj) {
printf("Hello from C!\n");
}
module com.example.myapp {
requires java.base;
requires java.sql;
requires transitive com.example.mylib;
exports com.example.myapp.api;
opens com.example.myapp.internal to com.example.test;
}
// Compile with modules
javac --module-path lib -d mods/com.example.myapp \
src/com.example.myapp/module-info.java \
src/com.example.myapp/com/example/myapp/*.java
// Create modular JAR
jar --create --file=mlib/com.example.myapp.jar \
--main-class=com.example.myapp.Main \
-C mods/com.example.myapp .
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {
@Benchmark
public void testMethod(Blackhole bh) {
int result = doCalculation();
bh.consume(result);
}
private int doCalculation() {
// Complex calculation
return 42;
}
}
// Javassist example
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.example.MyClass");
CtMethod m = cc.getDeclaredMethod("doSomething");
m.insertBefore("{ System.out.println(\"Before method\"); }");
cc.writeFile();
// ASM example
ClassReader cr = new ClassReader("com/example/MyClass");
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new MyClassVisitor(cw);
cr.accept(cv, 0);
byte[] modifiedClass = cw.toByteArray();
// Servlet example
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
resp.getWriter().println("Hello World");
}
}
// JSP example
<%@ page contentType="text/html" %>
<html>
<body>
<% String name = request.getParameter("name"); %>
<h1>Hello <%= name %>!</h1>
</body>
</html>
// Managed Bean
@ManagedBean
@RequestScoped
public class UserBean {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String submit() {
return "result";
}
}
// Facelet page
<h:form>
<h:inputText value="#{userBean.name}"/>
<h:commandButton value="Submit" action="#{userBean.submit}"/>
</h:form>
// Spring Boot Controller
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/hello")
public String sayHello() {
return "Hello from Spring!";
}
}
// Spring Configuration
@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource("jdbc:h2:mem:test");
}
}
@Test
void standardAssertions() {
assertEquals(2, calculator.add(1, 1));
assertTrue('a' < 'b', "Assertion message");
}
@ParameterizedTest
@ValueSource(ints = {1, 3, 5})
void isOdd(int number) {
assertTrue(number % 2 != 0);
}
@Test
void testWithMock() {
// Create mock
List<String> mockedList = mock(List.class);
// Set up mock behavior
when(mockedList.get(0)).thenReturn("first");
// Use mock
assertEquals("first", mockedList.get(0));
// Verify interactions
verify(mockedList).get(0);
}
@Test
public void testAdd() {
Assert.assertEquals(calculator.add(1, 1), 2);
}
@Test(dependsOnMethods = {"testAdd"})
public void testMultiply() {
Assert.assertEquals(calculator.multiply(2, 3), 6);
}
@DataProvider(name = "testData")
public Object[][] createData() {
return new Object[][]{{1, 1, 2}, {2, 3, 5}};
}
@Test(dataProvider = "testData")
public void testAddWithData(int a, int b, int expected) {
Assert.assertEquals(calculator.add(a, b), expected);
}
class FibonacciTask extends RecursiveTask<Integer> {
final int n;
FibonacciTask(int n) { this.n = n; }
protected Integer compute() {
if (n <= 1) return n;
FibonacciTask f1 = new FibonacciTask(n - 1);
f1.fork();
FibonacciTask f2 = new FibonacciTask(n - 2);
return f2.compute() + f1.join();
}
}
// Usage
ForkJoinPool pool = new ForkJoinPool();
int result = pool.invoke(new FibonacciTask(10));
Phaser phaser = new Phaser(3); // 3 parties
new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // Phase 1
System.out.println("Thread 1 phase 1 done");
phaser.arriveAndDeregister(); // Done
}).start();
new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // Phase 1
System.out.println("Thread 2 phase 1 done");
phaser.arriveAndDeregister(); // Done
}).start();
phaser.arriveAndAwaitAdvance(); // Main thread waits
System.out.println("All threads completed phase 1");
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) {
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
JFrame frame = new JFrame("My App");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Click Me");
button.addActionListener(e -> {
JOptionPane.showMessageDialog(frame, "Button clicked!");
});
frame.getContentPane().add(button);
frame.pack();
frame.setVisible(true);
public class MyApp extends Application {
@Override
public void start(Stage stage) {
Button btn = new Button("Click Me");
btn.setOnAction(e -> {
System.out.println("Button clicked!");
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
stage.setTitle("My JavaFX App");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
public class WordCount extends Configured implements Tool {
public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable> {
public void map(Object key, Text value, Context context) {
// Map logic
}
}
public static class IntSumReducer
extends Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text key, Iterable<IntWritable> values, Context context) {
// Reduce logic
}
}
public int run(String[] args) throws Exception {
Job job = Job.getInstance(getConf(), "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setReducerClass(IntSumReducer.class);
// Other job configuration
return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new WordCount(), args);
System.exit(res);
}
}
SparkConf conf = new SparkConf().setAppName("WordCount").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> textFile = sc.textFile("hdfs://...");
JavaRDD<String> words = textFile.flatMap(line -> Arrays.asList(line.split(" ")).iterator());
JavaPairRDD<String, Integer> pairs = words.mapToPair(word -> new Tuple2<>(word, 1));
JavaPairRDD<String, Integer> counts = pairs.reduceByKey((a, b) -> a + b);
counts.saveAsTextFile("hdfs://...");
// Create S3 client
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.withRegion(Regions.US_EAST_1)
.build();
// List buckets
for (Bucket bucket : s3.listBuckets()) {
System.out.println(bucket.getName());
}
// Upload file
s3.putObject("my-bucket", "key", new File("file.txt"));
// Eureka Client
@SpringBootApplication
@EnableDiscoveryClient
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
// Feign Client
@FeignClient(name = "service-name")
public interface MyFeignClient {
@GetMapping("/api/resource")
String getResource();
}
// Config Client
@RestController
@RefreshScope
public class ConfigController {
@Value("${my.property}")
private String myProperty;
@GetMapping("/property")
public String getProperty() {
return myProperty;
}
}
| Feature | Java | C# |
|---|---|---|
| Platform | Write Once, Run Anywhere (JVM) | Primarily Windows (.NET), now cross-platform with .NET Core |
| Checked Exceptions | Supported (must be caught or declared) | Not supported |
| Properties | Need to write getter/setter methods | Built-in property syntax |
| Delegates/Events | Uses interfaces or functional interfaces | Has delegates and events |
| LINQ | Uses Stream API (Java 8+) | Has LINQ (Language Integrated Query) |
| Multiple Inheritance | Through interfaces only | Through interfaces only |