This documentation aims to provide an overview of fundamental Java 8 , 11 , 17 and 21 Concepts necessary for working with Spring Framework.
-
The
JDK (Java Development Kit)is a superset of the JRE and contains everything that is in the JRE, plus tools such as the compiler, debugger, JavaDoc, keytool etc necessary for developing and running Java programs or applications.
-
The
JVM (Java Virtual Machine)is a very important component of Java programming language. When you run the Java program, the Java compiler first compiles your Java code to bytecode. Then, the JVM translates bytecode into native machine code (set of instructions that a computer's CPU executes directly). -
JVMis called virtual because it provides an interface that does not depend on the underlying operating system and machine hardware.
- The
Java Runtime Environment (JRE)provides the libraries, the Java Virtual Machine, and other components to run applets and applications written in the Java programming language. JRE doesn’t contain any development tools such as Java compiler, debugger, JShell, etc. - If you just want to execute a java program, you can install only JRE. You don’t need JDK because there is no development or compilation of java source code is required.
- Begin by writing your Java program using a text editor or an Integrated Development Environment (IDE) such as Eclipse, Netbeans, or IntelliJ IDEA. Save the Java source code in files with the .java extension.
- Compile your Java source code into bytecode using a Java compiler included in the
Java Development Kit (JDK). You can compile your code using thejavaccommand-line tool or built-in features of an IDE. Compilation converts your Java source code into.classfiles containing Java bytecode.
- To execute the Java bytecode, install the
Java Runtime Environment (JRE), which includes theJava Virtual Machine (JVM)and standard Java class libraries. The JRE provides an environment for executing Java bytecode.
- Once the JRE is installed, run your Java program using the
javacommand followed by the name of the class containing themain()method in your terminal or command prompt.
In Java, variables are containers that store data values. Each variable must be declared with a data type, which defines the type of value the variable can store.
A variable in Java has three key components:
- Data Type: Specifies the type of data the variable will hold (e.g.,
int,String). - Variable Name: The name of the variable (follows naming conventions).
- Value: The data or value assigned to the variable.
- Local Variables: Declared inside a method, constructor, or block and are only accessible within that scope.
- Instance Variables: Declared in a class, but outside a method, constructor, or block. They are non-static and each object has its own copy.
- Static Variables: Declared using the
statickeyword, and they are shared among all objects of the class.
public class Example {
// Instance variable
int instanceVar = 10;
// Static variable
static int staticVar = 20;
public void method() {
// Local variable
int localVar = 30;
System.out.println(localVar);
}
}Java has two main types of data types:
- Primitive Data Types: Predefined by the language and store simple values.
- Reference Data Types: Created using classes and store references (addresses) to objects.
Java provides 8 primitive data types:
| Data Type | Size | Default Value | Description |
|---|---|---|---|
byte |
1 byte | 0 | Stores small integers (-128 to 127). |
short |
2 bytes | 0 | Stores small integers (-32,768 to 32,767). |
int |
4 bytes | 0 | Stores whole numbers (-2^31 to 2^31-1). |
long |
8 bytes | 0L | Stores large integers (-2^63 to 2^63-1). |
float |
4 bytes | 0.0f | Stores fractional numbers with up to 7 decimal digits. |
double |
8 bytes | 0.0d | Stores fractional numbers with up to 16 decimal digits. |
char |
2 bytes | '\u0000' | Stores a single 16-bit Unicode character. |
boolean |
1 bit | false |
Stores a true or false value. |
public class DataTypesExample {
public static void main(String[] args) {
byte b = 100;
short s = 1000;
int i = 10000;
long l = 100000L;
float f = 10.5f;
double d = 20.99;
char c = 'A';
boolean bool = true;
System.out.println("Byte: " + b);
System.out.println("Short: " + s);
System.out.println("Int: " + i);
System.out.println("Long: " + l);
System.out.println("Float: " + f);
System.out.println("Double: " + d);
System.out.println("Char: " + c);
System.out.println("Boolean: " + bool);
}
}Reference data types include objects, arrays, and strings. These store memory addresses where data is located, rather than the actual value.
- Class Objects: Instances of user-defined classes.
- Arrays: Store multiple values of the same type.
- Strings: Special class in Java used to store sequences of characters.
Example:
public class ReferenceDataTypesExample {
public static void main(String[] args) {
// String (Reference Type)
String str = "Hello, Java!";
// Array (Reference Type)
int[] numbers = {1, 2, 3, 4, 5};
// Printing values
System.out.println("String: " + str);
System.out.println("First element in the array: " + numbers[0]);
}
}When assigning values of one type to another, Java provides type casting.
- Implicit Casting (Widening): Automatically converts smaller data types to larger ones.
Example:
inttolong - Explicit Casting (Narrowing): Manually converts larger data types to smaller ones.
Example:
doubletoint
Example:
public class TypeCastingExample {
public static void main(String[] args) {
int intVal = 100;
long longVal = intVal; // Implicit Casting (int to long)
double doubleVal = 10.99;
int intVal2 = (int) doubleVal; // Explicit Casting (double to int)
System.out.println("Long value: " + longVal);
System.out.println("Int value after casting: " + intVal2);
}
}In Java, operators are special symbols used to perform operations on variables and values. Java provides several types of operators to manipulate data, perform arithmetic calculations, compare values, and more.
- 1. Arithmetic Operators
- 2. Assignment Operators
- 3. Relational (Comparison) Operators
- 4. Logical Operators
- 5. Bitwise Operators
- 6. Unary Operators
- 7. Ternary Operator
Arithmetic operators are used to perform basic mathematical operations like addition, subtraction, multiplication, etc.
| Operator | Description | Example |
|---|---|---|
+ |
Addition | a + b |
- |
Subtraction | a - b |
* |
Multiplication | a * b |
/ |
Division | a / b |
% |
Modulus (remainder) | a % b |
Example:
public class ArithmeticExample {
public static void main(String[] args) {
int a = 10, b = 5;
System.out.println("Addition: " + (a + b)); // 15
System.out.println("Subtraction: " + (a - b)); // 5
System.out.println("Multiplication: " + (a * b)); // 50
System.out.println("Division: " + (a / b)); // 2
System.out.println("Modulus: " + (a % b)); // 0
}
}Assignment operators are used to assign values to variables.
| Operator | Description | Example |
|---|---|---|
= |
Assign value | a = 10 |
+= |
Add and assign | a += 5 |
-= |
Subtract and assign | a -= 5 |
*= |
Multiply and assign | a *= 5 |
/= |
Divide and assign | a /= 5 |
%= |
Modulus and assign | a %= 5 |
Example:
public class AssignmentExample {
public static void main(String[] args) {
int a = 10;
a += 5; // a = a + 5
System.out.println("a after += 5: " + a); // 15
a *= 2; // a = a * 2
System.out.println("a after *= 2: " + a); // 30
}
}Relational operators are used to compare two values. They return a boolean result (true or false).
| Operator | Description | Example |
|---|---|---|
== |
Equal to | a == b |
!= |
Not equal to | a != b |
> |
Greater than | a > b |
< |
Less than | a < b |
>= |
Greater than or equal to | a >= b |
<= |
Less than or equal to | a <= b |
Example:
public class ComparisonExample {
public static void main(String[] args) {
int a = 10, b = 5;
System.out.println("a == b: " + (a == b)); // false
System.out.println("a != b: " + (a != b)); // true
System.out.println("a > b: " + (a > b)); // true
System.out.println("a <= b: " + (a <= b)); // false
}
}Logical operators are used to combine two or more conditions. They work with boolean values (true or false).
| Operator | Description | Example |
|---|---|---|
&& |
Logical AND | a && b |
| ` | ` | |
! |
Logical NOT | !a |
public class LogicalExample {
public static void main(String[] args) {
boolean a = true, b = false;
System.out.println("a && b: " + (a && b)); // false
System.out.println("a || b: " + (a || b)); // true
System.out.println("!a: " + (!a)); // false
}
}Bitwise operators are used to perform operations on individual bits of numbers.
| Operator | Description | Example |
|---|---|---|
& |
Bitwise AND | a & b |
| ` | ` | Bitwise OR |
^ |
Bitwise XOR | a ^ b |
~ |
Bitwise Complement | ~a |
<< |
Left Shift | a << 2 |
>> |
Right Shift | a >> 2 |
public class BitwiseExample {
public static void main(String[] args) {
int a = 5, b = 3;
System.out.println("a & b: " + (a & b)); // 1 (Binary: 101 & 011 = 001)
System.out.println("a | b: " + (a | b)); // 7 (Binary: 101 | 011 = 111)
}
}Unary operators are used with a single operand to perform various operations like incrementing, decrementing, etc.
| Operator | Description | Example |
|---|---|---|
+ |
Unary plus (positive value) | +a |
- |
Unary minus (negative value) | -a |
++ |
Increment | a++ or ++a |
-- |
Decrement | a-- or --a |
public class UnaryExample {
public static void main(String[] args) {
int a = 10;
System.out.println("a++: " + (a++)); // 10 (then a becomes 11)
System.out.println("++a: " + (++a)); // 12
System.out.println("a--: " + (a--)); // 12 (then a becomes 11)
}
}The ternary operator is a shorthand for the if-else statement and works with three operands. It is also called the conditional operator.
| Operator | Description | Example |
|---|---|---|
? : |
Shorthand for if-else | condition ? value1 : value2 |
public class TernaryExample {
public static void main(String[] args) {
int a = 10, b = 20;
int max = (a > b) ? a : b;
System.out.println("Max value: " + max); // 20
}
}Control flow statements in Java are used to dictate the flow of execution of a program based on certain conditions. The two primary control flow statements you asked for are if-else and switch.
The if-else statement allows you to execute a block of code based on a condition. If the condition evaluates to true, the if block is executed. If the condition is false, the else block (if present) is executed.
Syntax:
if (condition) {
// Code to execute if condition is true
} else {
// Code to execute if condition is false
}Example:
int age = 18;
if (age >= 18) {
System.out.println("You are an adult.");
} else {
System.out.println("You are a minor.");
}- Condition: age >= 18 checks if the age is greater than or equal to 18.
- Output: Since the condition is true, it will print: "You are an adult."
Else-If Ladder: You can chain multiple if conditions using else-if to check multiple possibilities.
int score = 75;
if (score >= 90) {
System.out.println("Grade: A");
} else if (score >= 80) {
System.out.println("Grade: B");
} else if (score >= 70) {
System.out.println("Grade: C");
} else {
System.out.println("Grade: F");
}The switch statement provides an alternative to the if-else ladder when there are multiple conditions that depend on the value of a single variable. It is often used when you have many potential values for a variable and want to execute different blocks of code for each value.
Syntax:
switch (expression) {
case value1:
// Code to execute if expression matches value1
break;
case value2:
// Code to execute if expression matches value2
break;
// More cases...
default:
// Code to execute if none of the cases match
break;
}Example :
int day = 3;
switch (day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday");
break;
default:
System.out.println("Invalid day");
break;
}- Expression: day is evaluated in the switch statement.
- Output: Since day is 3, the output will be: "Wednesday".
- The break statement is used to exit the switch block after a case has been executed, preventing fall-through to the next case.
Switch with Strings (Java 7 and above):
String fruit = "apple";
switch (fruit) {
case "apple":
System.out.println("You selected an apple.");
break;
case "banana":
System.out.println("You selected a banana.");
break;
default:
System.out.println("Unknown fruit.");
break;
}-
Output: Since the string fruit is "apple", it will print: "You selected an apple."
-
Conditions: if-else checks conditions that return a boolean value, whereas switch compares an expression against a set of constant values.
-
Usage: switch is usually more efficient when you have many possible values for a single variable, while if-else is more flexible for complex conditions.
-
Types: In switch, only certain types are allowed (int, char, String, etc.), while if-else can handle any condition.
Loops are control structures that allow you to repeatedly execute a block of code as long as a specified condition is true. Java provides three primary types of loops: for, while, and do-while.
The for loop is used when you know in advance how many times you want to iterate. It consists of three parts: initialization, condition, and update.
Syntax:
for (initialization; condition; update) {
// Code to execute
}- Initialization: Initializes the loop control variable.
- Condition: Checks the condition before each iteration. If true, the loop runs; if false, the loop stops.
- Update: Updates the loop control variable after each iteration.
for (int i = 1; i <= 5; i++) {
System.out.println("Iteration: " + i);
}Explanation:
- The loop starts with i = 1, checks if i <= 5 (condition), and prints the current value of i.
- After each iteration, i is incremented by 1.
Output:
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Iteration: 5- Used to iterate over arrays or collections without using an index.
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num);
}The while loop checks the condition before executing the loop's body. It continues to iterate as long as the condition remains true.
Syntax:
while (condition) {
// Code to execute
}- Condition: The loop executes as long as this condition evaluates to true.
Example:
int i = 1;
while (i <= 5) {
System.out.println("Iteration: " + i);
i++;
}Explanation:
- The loop runs as long as i <= 5. After each iteration, i is incremented by 1.
Output:
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Iteration: 5The do-while loop is similar to the while loop, but it guarantees at least one iteration because the condition is checked after the loop body is executed.
do {
// Code to execute
} while (condition);Condition: The loop executes the block of code once, then checks the condition. If the condition is true, it continues to loop; otherwise, it stops.
Example:
int i = 1;
do {
System.out.println("Iteration: " + i);
i++;
} while (i <= 5);Explanation:
- The loop prints the value of i at least once, then checks if i <= 5. If true, it continues to iterate.
Output:
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Iteration: 5Key Difference from while loop:
In the do-while loop, the block of code executes at least once, even if the condition is initially false.
Java also provides loop control statements to alter the normal flow of loops:
-
break:Exits the loop entirely. -
continue:Skips the current iteration and proceeds to the next iteration.Example with break and continue:
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Skip the current iteration when i is 3
}
if (i == 5) {
break; // Exit the loop when i is 5
}
System.out.println("Iteration: " + i);
}Explanation:
- The continue statement skips the iteration where
i == 3, so it doesn't print "Iteration: 3". - The break statement exits the loop when
i == 5, so the loop stops before it reaches 5. Output:
Iteration: 1
Iteration: 2
Iteration: 4In Java, arrays are fundamental data structures that hold multiple values of the same type. Java provides various utility methods in the java.util.Arrays class to operate on arrays. These methods allow us to perform operations such as sorting, searching, filling, and comparing arrays with ease.
An array is declared by specifying the type of its elements and the array size. You can initialize it at the time of declaration or later.
int[] numbers = new int[5]; // Declares an array of 5 integersExample 1 : Array of Numbers
int[] numbers = {1, 2, 3, 4, 5}; // Initializes the array with valuesExample 2: Array of Strings
// Declare and initialize an array of strings
String[] fruits = {"Apple", "Banana", "Orange", "Mango", "Grapes"};
// Accessing elements in the array
System.out.println(fruits[0]); // Output: Apple
System.out.println(fruits[3]); // Output: Mango
// Looping through the array
for (String fruit : fruits) {
System.out.println(fruit);
}
// Output:
// Apple
// Banana
// Orange
// Mango
// GrapesExample 3: Array of Objects
// Define the Person class
class Person {
String name;
int age;
// Constructor
Person(String name, int age) {
this.name = name;
this.age = age;
}
// Method to display person details
void display() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
public class Main {
public static void main(String[] args) {
// Declare and initialize an array of Person objects
Person[] people = new Person[3];
// Initialize each element with a new Person object
people[0] = new Person("Alice", 30);
people[1] = new Person("Bob", 25);
people[2] = new Person("Charlie", 28);
// Access and display the details of each person in the array
for (Person person : people) {
person.display();
}
}
}1. Sorting an Array: The Arrays.sort() method is used to sort arrays in ascending order. It can sort primitive arrays (like int[]) and object arrays (like String[]).
import java.util.Arrays;
int[] numbers = {5, 2, 9, 1, 7};
Arrays.sort(numbers); // Sorts the array in ascending order
System.out.println(Arrays.toString(numbers)); // Output: [1, 2, 5, 7, 9]2. Binary Search: The Arrays.binarySearch() method is used to search for a specific element in a sorted array. It returns the index of the element if found; otherwise, it returns a negative value.
int[] numbers = {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(numbers, 3);
System.out.println(index); // Output: 2 (index of element 3)- Note: The array must be sorted before using binarySearch().
3. Filling an Array: The Arrays.fill() method fills all elements of an array with a specific value.
int[] numbers = new int[5];
Arrays.fill(numbers, 10); // Fills the array with the value 10
System.out.println(Arrays.toString(numbers)); // Output: [10, 10, 10, 10, 10]4. Copying Arrays: The Arrays.copyOf() method allows you to create a new array by copying the elements of an existing array.
int[] original = {1, 2, 3, 4, 5};
int[] copy = Arrays.copyOf(original, original.length);
System.out.println(Arrays.toString(copy)); // Output: [1, 2, 3, 4, 5]- You can specify the new array's length in
Arrays.copyOf(), which allows you to either truncate or extend the original array.
5. Comparing Arrays: The Arrays.equals() method checks if two arrays are equal (i.e., have the same length and elements in the same order).
int[] numbers1 = {1, 2, 3};
int[] numbers2 = {1, 2, 3};
boolean areEqual = Arrays.equals(numbers1, numbers2);
System.out.println(areEqual); // Output: true6. Converting Array to String: The Arrays.toString() method returns a string representation of the array.
int[] numbers = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(numbers)); // Output: [1, 2, 3, 4, 5]7. Converting Multi-dimensional Arrays to String: For multi-dimensional arrays, you can use Arrays.deepToString() to get a string representation.
int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
System.out.println(Arrays.deepToString(matrix)); // Output: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]8. Copying Part of an Array: The Arrays.copyOfRange() method allows copying a portion of an array.
int[] original = {1, 2, 3, 4, 5};
int[] partialCopy = Arrays.copyOfRange(original, 1, 4);
System.out.println(Arrays.toString(partialCopy)); // Output: [2, 3, 4]9. Parallel Sorting: The Arrays.parallelSort() method sorts large arrays more efficiently by using multiple threads in parallel.
int[] numbers = {9, 4, 6, 1, 7};
Arrays.parallelSort(numbers);
System.out.println(Arrays.toString(numbers)); // Output: [1, 4, 6, 7, 9]10. Checking if Two Multi-dimensional Arrays are Equal: For multi-dimensional arrays, Arrays.deepEquals() checks for deep equality.
int[][] matrix1 = {{1, 2}, {3, 4}};
int[][] matrix2 = {{1, 2}, {3, 4}};
boolean areEqual = Arrays.deepEquals(matrix1, matrix2);
System.out.println(areEqual); // Output: true| Function | Description | Example |
|---|---|---|
sort(array) |
Sorts the array in ascending order | Arrays.sort(array); |
binarySearch(array, key) |
Searches for key in a sorted array | Arrays.binarySearch(array, 3); |
fill(array, value) |
Fills the entire array with value | Arrays.fill(array, 0); |
copyOf(array, length) |
Copies the array into a new array | Arrays.copyOf(array, 5); |
equals(array1, array2) |
Checks if two arrays are equal | Arrays.equals(arr1, arr2); |
toString(array) |
Converts the array to a string | Arrays.toString(array); |
deepToString(array) |
Converts a multi-dimensional array to a string | Arrays.deepToString(matrix); |
copyOfRange(array, start, end) |
Copies a range of the array | Arrays.copyOfRange(arr, 0, 3); |
parallelSort(array) |
Sorts the array using multiple threads | Arrays.parallelSort(arr); |
deepEquals(array1, array2) |
Checks if two multi-dimensional arrays are equal | Arrays.deepEquals(matrix1, matrix2); |
In Java, a Table (often referring to two-dimensional arrays or hash tables) and an ArrayList have distinct characteristics and purposes. Here's a breakdown of their key differences:
| Feature | Table (2D Array or HashTable) | ArrayList |
|---|---|---|
| Type | Array (fixed-size) or HashTable (for key-value pairs). |
Dynamic array (part of Java's List interface, resizable). |
| Size | Fixed at the time of creation (for arrays). | Resizable, grows or shrinks dynamically. |
| Data Structure | 2D Array: Static array structure. HashTable: Key-value pair structure. |
Implements a dynamic array, can grow or shrink as needed. |
| Performance | Accessing elements by index is very fast (O(1) for arrays). | Accessing elements is also O(1), but resizing operations (like adding elements) take O(n). |
| Storage Type | Stores elements of the same data type (for arrays). | Can store objects of any data type (generic, ArrayList<Type>). |
| Memory Allocation | Memory is allocated at the time of creation and cannot be changed for arrays. | Memory is allocated dynamically and expands when needed. |
| Efficiency | Fixed-size, hence less memory overhead (arrays). | More flexible, but resizing may involve overhead (copying elements to a new larger array). |
| Type Safety | Arrays are strongly typed, meaning the data type of elements is fixed at compile time. | ArrayList is generic, meaning you can define the type (ArrayList<String>, ArrayList<Integer>, etc.). |
| Multi-dimensional | Arrays can be multi-dimensional (e.g., 2D or 3D arrays). | ArrayLists are single-dimensional, but can contain other lists to simulate multidimensional arrays. |
| Initialization | Arrays must be initialized with a fixed size when created (e.g., int[3][3] or new int[5]). |
ArrayList grows dynamically as elements are added (e.g., new ArrayList<Integer>()). |
| Methods and Utilities | Arrays have limited built-in methods. (Use Arrays class for utility methods like sorting). |
ArrayList has built-in methods such as add(), remove(), size(), clear(), etc. |
| Null Values | Can contain null values (for arrays and hash tables). |
Can also contain null values in ArrayList. |
-
2D Array Example:
-
A table in Java can refer to a two-dimensional array, where each "row" is an array, and each "column" contains elements of the same type.
-
Example:
int[][] table = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
-
-
HashTable Example:
-
A
HashTableis a collection of key-value pairs that maps keys to values. It is synchronized, but largely replaced byHashMapdue to better performance. -
Example:
Hashtable<String, Integer> hashtable = new Hashtable<>(); hashtable.put("Alice", 30); hashtable.put("Bob", 25);
-
-
An
ArrayListis a resizable array in Java, which allows dynamic memory allocation. It can store objects of any type using generics and has built-in methods for common operations (e.g., adding, removing elements). -
Example:
ArrayList<String> fruits = new ArrayList<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); System.out.println(fruits.get(0)); // Output: Apple
Strings in Java are objects that represent sequences of characters. Java provides a variety of built-in methods for manipulating strings. Here are some common operations for string manipulation:
Strings can be created using string literals or the String constructor:
String str1 = "Hello, World!"; // String literal
String str2 = new String("Hello, World!"); // String constructor| Method | Description | Example |
|---|---|---|
length() |
Returns the length of the string. | str1.length(); // Returns 13 |
charAt(int index) |
Returns the character at the specified index. | str1.charAt(0); // Returns 'H' |
substring(int beginIndex) |
Returns a substring starting from the specified index. | str1.substring(7); // Returns "World!" |
substring(int beginIndex, int endIndex) |
Returns a substring from the specified range. | str1.substring(0, 5); // Returns "Hello" |
indexOf(String str) |
Returns the index of the first occurrence of the specified substring. | str1.indexOf("World"); // Returns 7 |
lastIndexOf(String str) |
Returns the index of the last occurrence of the specified substring. | str1.lastIndexOf("o"); // Returns 8 |
toLowerCase() |
Converts all characters to lowercase. | str1.toLowerCase(); // Returns "hello, world!" |
toUpperCase() |
Converts all characters to uppercase. | str1.toUpperCase(); // Returns "HELLO, WORLD!" |
trim() |
Removes leading and trailing whitespace. | str1.trim(); // Removes spaces if present |
replace(char oldChar, char newChar) |
Replaces occurrences of a character with another. | str1.replace('o', 'a'); // Returns "Hella, Warld!" |
split(String regex) |
Splits the string into an array based on a regex. | str1.split(","); // Returns array ["Hello", " World!"] |
startsWith(String prefix) |
Checks if the string starts with the specified prefix. | "Java".startsWith("Ja"); // Returns true |
endsWith(String suffix) |
Checks if the string ends with the specified suffix. | "Java".endsWith("va"); // Returns true |
contains(CharSequence seq) |
Checks if the string contains the specified sequence. | "Java".contains("av"); // Returns true |
equalsIgnoreCase(String str) |
Compares two strings, ignoring case differences. | "java".equalsIgnoreCase("Java"); // Returns true |
isEmpty() |
Checks if the string is empty (length = 0). | "".isEmpty(); // Returns true |
join(CharSequence delimiter, CharSequence... elements) |
Joins multiple strings with a delimiter. | String.join("-", "2023", "10", "05"); // Returns "2023-10-05" |
matches(String regex) |
Checks if the string matches a regular expression. | "abc123".matches("[a-z]+[0-9]+"); // Returns true |
replaceAll(String regex, String replacement) |
Replaces all matches of a regex with a replacement. | "abc123".replaceAll("\\d", "#"); // Returns "abc###" |
replaceFirst(String regex, String replacement) |
Replaces the first match of a regex with a replacement. | "abc123".replaceFirst("\\d", "#"); // Returns "abc#23" |
repeat(int count) |
Repeats the string a specified number of times. | "Hello".repeat(3); // Returns "HelloHelloHello" |
strip() |
Removes leading and trailing whitespace (introduced in Java 11). | " Hello ".strip(); // Returns "Hello" |
stripLeading() |
Removes leading whitespace only. | " Hello ".stripLeading(); // Returns "Hello " |
stripTrailing() |
Removes trailing whitespace only. | " Hello ".stripTrailing(); // Returns " Hello" |
compareTo(String anotherString) |
Compares two strings lexicographically. | "abc".compareTo("def"); // Returns a negative number |
concat(String str) |
Concatenates the specified string to the end of the current string. | "Hello".concat(" World"); // Returns "Hello World" |
codePointAt(int index) |
Returns the Unicode code point at the specified index. | "A".codePointAt(0); // Returns 65 (Unicode for 'A') |
intern() |
Returns a canonical representation of the string. | "Hello".intern(); // Interns the string for memory efficiency |
Strings can be concatenated using the + operator or the concat() method:
String firstName = "John";
String lastName = "Doe";
String fullName = firstName + " " + lastName; // Using + operator
// or
String fullName2 = firstName.concat(" ").concat(lastName); // Using concat() methodFor more efficient string manipulation, especially in loops, it’s better to use StringBuilder or StringBuffer:
- StringBuilder: Not synchronized, faster for single-threaded operations.
- StringBuffer: Synchronized, thread-safe but slower than StringBuilder.
Example of StringBuilder:
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World!");
String result = sb.toString(); // Returns "Hello, World!"You can format strings using the String.format() method or System.out.printf() for console output:
String name = "Alice";
int age = 30;
String formatted = String.format("%s is %d years old.", name, age); // Returns "Alice is 30 years old."
System.out.println(formatted);Strings can be compared using the equals() method or equalsIgnoreCase() for case-insensitive comparisons:
String strA = "Hello";
String strB = "hello";
boolean isEqual = strA.equals(strB); // Returns false
boolean isEqualIgnoreCase = strA.equalsIgnoreCase(strB); // Returns trueThe substring() method in Java is used to extract a portion of a string based on specified indices. There are two common forms of the method:
substring(int beginIndex):Returns a substring starting from the given index up to the end of the string.substring(int beginIndex, int endIndex):Returns a substring starting from thebeginIndexand ending right before theendIndex(exclusive of endIndex).
Example:
public class SubstringExample {
public static void main(String[] args) {
String str = "Hello, World!";
// Example 1: Using substring(int beginIndex)
String result1 = str.substring(7);
System.out.println(result1); // Output: World!
// Example 2: Using substring(int beginIndex, int endIndex)
String result2 = str.substring(0, 5);
System.out.println(result2); // Output: Hello
// Example 3: Extracting a portion of the string
String result3 = str.substring(7, 12);
System.out.println(result3); // Output: World
}
}str.substring(7)extracts the substring starting from index7, which is"World!".str.substring(0, 5)extracts characters from index0to4(excluding5), which is"Hello".str.substring(7, 12)extracts characters from index7to11, resulting in"World".
Zero Parameters :
() -> System.out.println("No parameters!");One Parameter :
s -> System.out.println(s);Multiple Parameters :
(int x, int y) -> x + y;Single Expression :
a -> a * a // no need for the return keywordCode Block :
(a , b) -> { return a + b }; // Multiple statements inside braces- A functional interface is an interface that contains only
one abstract method. It can also containdefaultandstaticmethods, but it must have exactly one abstract method. - The
@FunctionalInterfacenotation is optional but recommended to clearly indicate to other developers that this interface is intended to be used as a functional interface. If you try to declare a second abstract method in an interface annotated with@FunctionalInterface, the compiler will generate an error.
@FunctionalInterface
interface MyFunctionalInterface {
void myAbstractMethod();
// This line would generate an error because it introduces a second abstract method
// void anotherAbstractMethod();
// This is a default method
default void myDefaultMethod() {
System.out.println("This is a default method.");
}
// This is a static method
static void myStaticMethod() {
System.out.println("This is a static method.");
}
} public class Main {
public static void main(String[] args) {
// Using a lambda expression to implement the abstract method of the functional interface
MyFunctionalInterface myInstance = () -> System.out.println("Implementation of myAbstractMethod.");
// Calling the abstract method
myInstance.myAbstractMethod(); // Output: "Implementation of myAbstractMethod."
// Calling the default method
myInstance.myDefaultMethod(); // Output: "This is a default method."
// Calling the static method
MyFunctionalInterface.myStaticMethod(); // Output: "This is a static method."
}
} import java.util.function.BinaryOperator;
public class Main {
public static void main(String[] args) {
// Lambda expression for multiplying two numbers
BinaryOperator<Integer> multiplication = (a, b) -> a * b;
int result1 = multiplication.apply(5, 3); // result1 will be 15
// Custom functional interface with an instance main method
Operation subtraction = (a, b) -> a - b;
int result3 = subtraction.operate(10, 3); // result3 will be 7
Operation division = (a, b) -> a / b;
int result4 = division.operate(15, 5); // result4 will be 3
System.out.println("Multiplication: " + result1);
System.out.println("Subtraction: " + result3);
System.out.println("Division: " + result4);
}
}
// Custom functional interface with an instance main method
@FunctionalInterface
interface Operation {
int operate(int a, int b);
}Method Reference:A method reference is a shorthand notation for invoking a method or passing it as a parameter. It provides a way to refer to a method without executing it directly, typically by using the::operator followed by the method name. Method references are often used in functional.
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
public class Main {
public static void main(String[] args) {
// Using a method reference for addition
BinaryOperator<Integer> addition = Main::add;
int result2 = addition.apply(10, 5); // result2 will be 15
System.out.println("Addition: " + result2);
// Using a method reference for conversion to upper case
UnaryOperator<String> toUpperCase = String::toUpperCase;
String upperCaseString = toUpperCase.apply("hello"); // upperCaseString will be "HELLO"
System.out.println("Uppercase String: " + upperCaseString);
}
// Static method for addition
static int add(int a, int b) {
return a + b;
}
}Predicate interfaceis a functional interface that represents a function that takes an argument and returns abooleanvalue, often used for testing a condition. This Predicate interface is widely used in the context of lambda expressions and methods of the Stream class to perform filtering or selecting elements.
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
// Using a pre-defined functional interface (Predicate) for checking even numbers
Predicate<Integer> isEven = num -> num % 2 == 0;
boolean result = isEven.test(10); // result will be true
System.out.println("Is 10 even? " + result);
// Using a Predicate for string comparison
Predicate<String> test = tes -> "toto".equals(tes);
boolean result = test.test("toto");
System.out.println("Is the string 'toto'? " + result); // result will be true
}
}-
Static methodwithin an interface is a method that can be called directly on the interface itself, without requiring an instance of a class that implements the interface. Static methods in interfaces were introduced in Java 8 along with default methods. -
Default methodis a method defined within an interface with a default implementation. Prior to the introduction of default methods in Java 8, interfaces could only contain method signatures, meaning methods without bodies. With default methods, it became possible to provide a default implementation for methods within interfaces.interface MyInterface { void abstractMethod(); // Abstract method default void defaultMethod() { // Default method System.out.println("This is a default method."); } static void staticMethod() { // Static method System.out.println("This is a static method."); } }
class MyClass implements MyInterface { public void abstractMethod() { System.out.println("Implementing the abstract method."); } }
public class Main { public static void main(String[] args) { MyClass obj = new MyClass(); obj.abstractMethod(); // Calling the abstract method obj.defaultMethod(); // Calling the default method MyInterface.staticMethod(); // Calling the static method directly on the interface } }
Collection Interfaces refers to the interfaces provided in the Java Collections Framework (JCF) that define various types of collections. These interfaces are part of the java.util package and provide a standardized way of working with collections of objects.
-
This is the root interface in the collection hierarchy. It defines basic operations applicable to all collections such as adding, removing, and querying elements. Use this when you need a general-purpose collection to store a group of objects without caring about the specific order or uniqueness of elements.
import java.util.ArrayList; import java.util.Collection; public class Main { public static void main(String[] args) { Collection<String> names = new ArrayList<>(); names.add("Alice"); names.add("Bob"); names.add("Charlie"); System.out.println("Collection : " + names); //Output : Collection : [Alice, Bob, Charlie] } }
-
An ordered collection (sometimes called a sequence). Lists allow duplicate elements and provide methods for accessing elements by their index. Common implementations of the List interface include
ArrayList,LinkedList, etc.import java.util.ArrayList; import java.util.List; public class ListExample { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(); numbers.add(10); numbers.add(20); numbers.add(30); System.out.println("List: " + numbers); //Output : List: [10, 20, 30] // Accessing an element by its index int secondElement = numbers.get(1); System.out.println("Second element: " + secondElement); //Output : Second element: 20 } }
- A map is a collection that associates unique keys with corresponding values. Each key is unique and corresponds to a single value. Common implementations of the Map interface include:
-
HashMap:This class uses a hash table to store key-value pairs, providing fast lookup and access. However, the order of elements is not guaranteed.import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("John", 25); hashMap.put("Alice", 30); hashMap.put("Bob", 20); for (Map.Entry<String, Integer> entry : hashMap.entrySet()) { System.out.print(entry.getKey() + " - " + entry.getValue()); System.out.print(" "); //Output : Bob - 20 Alice - 30 John - 25 } } }
-
TreeMap:This class implements the SortedMap interface and uses a red-black tree to store key-value pairs. Elements are sorted according to the natural order of keys or using a comparator provided when creating the TreeMap object.import java.util.TreeMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("John", 25); treeMap.put("Alice", 30); treeMap.put("Bob", 20); for (Map.Entry<String, Integer> entry : treeMap.entrySet()) { System.out.print(entry.getKey() + " - " + entry.getValue()); System.out.print(" "); //Output : Alice - 30 Bob - 20 John - 25 } } }
-
LinkedHashMap:This class maintains the insertion order of elements in addition to providing fast access to elements through a hash table. This means that elements are traversed in insertion order when iterating over the map.
-
-
A set is a collection that does not allow duplicate elements. This means that each element in a set is unique. Common implementations of the Set interface include HashSet, TreeSet, LinkedHashSet, etc.
HashSet:-
It is implemented using a hash table. It does not maintain any order of elements; instead, it uses the hash code of the objects to store elements.
-
If you try to add duplicates, they will be ignored.
-
Allows a single null element.
import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) { Set<String> hashSet = new HashSet<>(); hashSet.add("banana"); hashSet.add("apple"); hashSet.add("orange"); hashSet.add("banana"); System.out.print("Elements in HashSet:"); for (String fruit : hashSet) { System.out.print(fruit); //Output : Elements in HashSet: banana orange apple } } }
TreeSet:-
It is implemented using a red-black tree. It stores elements in sorted order (either natural ordering or specified by a comparator).
-
Also does not allow duplicate elements.
-
Does not allow null elements. If you try to add a null element, it will throw a NullPointerException.
import java.util.TreeSet; import java.util.Set; public class Main { public static void main(String[] args) { Set<String> treeSet = new TreeSet<>(); treeSet.add("banana"); treeSet.add("apple"); treeSet.add("orange"); treeSet.add("banana"); System.out.print("Elements in TreeSet:"); for (String fruit : treeSet) { System.out.print(fruit); //Output : Elements in TreeSet: apple banana orange } } }
-
LinkedHashSet:This class maintains the insertion order of elements in addition to providing fast access to elements through a hash table. This means that elements are traversed in insertion order when iterating over the map.
-
Stream API is a way to express and process collections of objects. Enable us to perform operations like filtering, mapping,reducing and sorting. The Stream API in the
java.util.streampackage in Java offers an simple approach to describe operations to be executed on a dataset.- Obtain a stream from an existing array:
private static Employee[] arrayOfEmps = { new Employee(1, "Jeff Bezos", 100000.0), new Employee(2, "Bill Gates", 200000.0), new Employee(3, "Mark Zuckerberg", 300000.0) }; Stream.of(arrayOfEmps);
- Obtain a stream from an existing list:
private static List<Employee> empList = Arrays.asList(arrayOfEmps); empList.stream();
- Create a stream from individual objects:
Stream.of(arrayOfEmps[0], arrayOfEmps[1], arrayOfEmps[2]);
- Create a stream using Stream.builder():
Stream.Builder<Employee> empStreamBuilder = Stream.builder(); empStreamBuilder.accept(arrayOfEmps[0]); empStreamBuilder.accept(arrayOfEmps[1]); empStreamBuilder.accept(arrayOfEmps[2]); Stream<Employee> empStream = empStreamBuilder.build();
Intermediate Operations:
-
filter():Returns a Stream containing only the elements that satisfy a specific condition.List<String> fruits = Arrays.asList("apple", "banana", "orange"); Stream<String> result = fruits.stream().filter(s -> s.startsWith("a")); System.out.println(result); // Output : apple
-
map():Transforms each element of the Stream into another element by applying a specified function.List<String> fruits = Arrays.asList("apple", "banana", "orange"); Stream<Integer> result = fruits.stream().map(String::length); System.out.println(result); // prints the length of each element of the fruits list
-
distinct():Returns a Stream with distinct elements (removes duplicates).List<String> numbers = Arrays.asList("1", "2", "2", "3", "4", "4"); Stream<String> result = numbers.stream().distinct(); result.forEach(System.out::println); // Output : 1 2 3 4
-
reduce():Reduces the elements of the Stream using a specific associative operation (e.g., sum, product, concatenation, etc.).List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> sum = numbers.stream() .reduce((a, b) -> a + b); sum.ifPresent(System.out::println); // Output: 15 (1 + 2 + 3 + 4 + 5)
-
sorted():Sorts the elements of the Stream.List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 2); Stream<Integer> result = numbers.stream().sorted(); result.forEach(System.out::println); // Output : 1 1 2 3 4
-
flatMap():Transforms each element of the Stream into a Stream of other elements, then flattens the resulting streams into a single Stream.List<List<Integer>> listOfLists = Arrays.asList( Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9) ); List<Integer> flattenedList = listOfLists.stream() .flatMap(List::stream) .collect(Collectors.toList()); System.out.println(flattenedList); // Output : [1, 2, 3, 4, 5, 6, 7, 8, 9]
-
skip():Skips the specified number of elements from the beginning of the Stream.List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List<Integer> skippedNumbers = numbers.stream() .skip(5) .collect(Collectors.toList()); System.out.println(skippedNumbers); // Output: [6, 7, 8, 9, 10]
-
limit():Limits the size of the Stream to the specified number of elements.List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List<Integer> skippedNumbers = numbers.stream() .skip(5) .collect(Collectors.toList()); System.out.println(skippedNumbers); // Output: [6, 7, 8, 9, 10]
Terminal Operations:
-
forEach():Performs an action for each element of the stream.List<String> fruits = Arrays.asList("apple", "banana", "orange"); fruits.stream().forEach(System.out::println);//prints each element of the list
-
collect():Collects the elements of the Stream into a specific data structure (e.g., a list, set, map, etc.).List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); List<String> modifiedNames = names.stream() .filter(name -> name.length() > 4) .map(String::toUpperCase) .collect(Collectors.toList());//collect the result into a list
-
-
isBlank():method was introduced in Java 11 in the java.lang.String class. It allows you to check if a string is empty or contains only whitespace characters.It returns true if the string is empty or contains only whitespace characters and false if the string contains at least one non-whitespace character.public class IsBlankExample { public static void main(String[] args) { String str1 = ""; // Empty string String str2 = " "; // String containing only whitespace characters String str3 = "Hello"; // String containing non-whitespace characters String str4 = " Hello "; // String containing whitespace characters along with text System.out.println("str1 is blank: " + str1.isBlank()); // true System.out.println("str2 is blank: " + str2.isBlank()); // true System.out.println("str3 is blank: " + str3.isBlank()); // false System.out.println("str4 is blank: " + str4.isBlank()); // false } }
-
strip():removes leading and trailing whitespace from a string.String trimmedStr = " Hello, world! ".strip(); System.out.println(trimmedStr); // Output : "Hello, world!"
-
repeat():repeats a string a certain number of times.String repeatedStr = "Java ".repeat(3); System.out.println(repeatedStr); // Output: "Java Java Java "
-
HttpClient:This API provides a modern alternative to the older HttpURLConnection class and offers richer features for making synchronous or asynchronous HTTP requests, handling cookies, headers, etc. It is built on the concept of reactive streams, making it more flexible and performant in scenarios where many requests need to be handled efficiently.import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class Main { public static void main(String[] args) throws IOException, InterruptedException { // Create an HttpClient HttpClient client = HttpClient.newHttpClient(); // Specify the URL for the GET request String url = "https://jsonplaceholder.typicode.com/posts/1"; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .GET() .build(); // Send the GET request and retrieve the response HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("Status code: " + response.statusCode()); System.out.println("Response body: " + response.body()); } }
Encapsulationis the mechanism of bundling the data(attributes)and the methods(functions)that operate on the data into a single unit called a class.- It allows data to be hidden from the outside world and accessed only through the methods defined in the class.
public class Car {
private String model;
private int year;
// Constructor
public Car(String model, int year) {
this.model = model;
this.year = year;
}
// Getter methods
public String getModel() {
return model;
}
public int getYear() {
return year;
}
// Setter methods
public void setModel(String model) {
this.model = model;
}
public void setYear(int year) {
this.year = year;
}
}Inheritanceis a mechanism in which a new class inherits properties and behavior(methods)from an existing class.- Promoting code reuse, class hierarchy, and object specialization.
// Parent class
class Vehicle {
String brand;
void drive() {
System.out.println("Driving a vehicle...");
}
}// Child class inheriting from Vehicle
class Car extends Vehicle {
int numberOfSeats;
void accelerate() {
System.out.println("Car is accelerating...");
}
}// Main class
public class Main {
public static void main(String[] args) {
// Creating an instance of Car
Car myCar = new Car();
myCar.brand = "Toyota";
myCar.numberOfSeats = 5;
// Accessing methods from both Vehicle and Car
myCar.drive(); // Inherited from Vehicle class
myCar.accelerate(); // Specific to Car class
}
}Abstractionis the process of hiding the implementation details and showing only the essential features of an object. It helps in reducing programming complexity and focusing on relevant aspectsAbstract classcannot be instantiated on their own and may contain abstract methods (methods without implementation)
// Abstract class
abstract class Shape {
abstract void draw(); // Abstract method
}// Concrete class implementing Shape
class Circle extends Shape {
void draw() {
System.out.println("Drawing a circle");
}
}Polymorphismis the ability of an object to take on different forms or behaviors depending on the context in which it is used. There are two main types of polymorphism in Java:compile-time polymorphism(also known asmethod overloading) andruntime polymorphism(also known asmethod overriding).
- This occurs when there are multiple methods with the same name within the same class, but with different parameter lists (signature).
- The Java compiler determines which method to call based on the number and types of arguments passed to the method.
- Method overloading is an example of compile-time polymorphism.
class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
} public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
System.out.println(calculator.add(1, 2)); // Calls the add(int, int) method
System.out.println(calculator.add(1.5, 2.5)); // Calls the add(double, double) method
}
}- This occurs when a method in a subclass has the same signature (name and parameter list) as a method in its superclass.
- When the method is called on an object of the subclass, the Java Virtual Machine (JVM) determines at runtime which version of the method to execute based on the actual type of the object.
- It is based on the actual type of object being referred to by the reference variable, not on the type of the reference variable itself.
- Method overriding is an example of runtime polymorphism.
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
} public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // Polymorphism
animal.makeSound(); // Calls makeSound from Dog class
}
}Aggregationrepresents a "has-a" relationship.- It occurs when an object contains another object, but the contained object can exist independently of the container object.
class Department {
private String name;
// Other attributes and methods
// Aggregation: Department has-a Professor
private Professor[] professors; // Array of professors
public Department(String name) {
this.name = name;
professors = new Professor[10]; // Example: department can have up to 10 professors
}
// Other methods to manage professors
}
class Professor {
private String name;
// Other attributes and methods
public Professor(String name) {
this.name = name;
}
}In this example, a Department has multiple Professor objects. However, the Professor objects can exist independently of theDepartment. If the Department object is destroyed, the Professor objects still exist.
-
Compositionrepresents a stronger form of aggregation and implies a "part-of" relationship. -
In composition, the lifetime of the contained object is dependent on the lifetime of the container object.
class Car { // Composition: Car has-a Engine private Engine engine; public Car() { this.engine = new Engine(); // Engine is created along with the Car } // Other attributes and methods } class Engine { // Attributes and methods of Engine }
In this example, a Car is composed of an Engine. The Engine object is created along with the Car object and cannot exist independently. If the Car object is destroyed, the Engine object is also destroyed.
Interfaceis a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types.- All methods declared in an interface are implicitly
publicandabstract. They cannot contain methods with implementation. - Classes that implement an interface must provide an
implementationfor all methods declared in that interface. - A class can implement
multiple interfaces, thus providing increased flexibility in program design. - Interfaces can be used to define types, enabling
polymorphism. - Extend one or more interfaces
// Define an interface
interface Animal {
void eat(); // method signature
void sleep();
}
// Implement the interface in a class
class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog eats");
}
@Override
public void sleep() {
System.out.println("Dog sleeps");
}
} public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.eat();
dog.sleep();
}
}- An abstract class is a class that cannot be
instantiateddirectly. - It can contain both
abstract methods(methods without implementation) andconcrete methods(methods with implementation). - An abstract class can have instance variables, constructors, and methods with or without implementation.
- Subclasses must extend an abstract class and implement all its abstract methods, unless they are also declared abstract.
- An abstract class can also provide default implementation for some methods, which subclasses may choose to override or not.
- Cannot be declared
final - Abstract methods should not be
private - An
abstract classcan partially implement aninterface
// Abstract class
abstract class Vehicle {
abstract void drive();
void stop() {
System.out.println("Vehicle stopped");
}
} // Class implementing an interface and extending an abstract class
class Car extends Vehicle implements Animal {
@Override
void drive() {
System.out.println("Car is being driven");
}
@Override
public void makeSound() {
System.out.println("Car honks");
}
} public class Main {
public static void main(String[] args) {
Car myCar = new Car();
myCar.drive();
myCar.makeSound();
myCar.stop();
}
}- Abstract Class: Can have both
abstractandconcretemethods. - Interface: Can only have
method signatures, but from Java 8 onwards, can havedefaultandstaticmethods.
- Abstract Class: Supports both single inheritance and implementation inheritance. A Java class can extend only
one abstract classdue toJava's single inheritancemodel. It's possible to inherit fields and methods from an abstract class, and subclassing allows for code reuse and extension of functionality. - Interface: Supports
multiple inheritancethrough implementation. A Java class canimplementmultiple interfaces. Interfaces enable a form of multiple inheritance by allowing a class to declare that it implements multiple behaviors.
- Abstract Class: Can have constructors with various access modifiers
(public, protected, or package-private). Abstract classes can also have fields with any access modifier. - Interface: All methods declared in an interface are by default
publicandabstract. Fields in interfaces are implicitlypublic,static, andfinal.
- Abstract Class: Can have constructors, which are called when a concrete subclass is instantiated.
- Interface: Cannot have constructors because interfaces cannot be instantiated. They are meant to be implemented by classes, which provide the necessary constructors.
- Abstract Class: Extended using the
extendskeyword. - Interface: Implemented using the
implementskeyword.
- Abstract Class: Used when you want to provide a common
base implementationfor a group of related classes. It's appropriate when you have a base class that contains some default behavior that subclasses can inherit and override. - Interface: Used when you want to define a
contractfor a group of classes, specifying aset of methodsthat implementing classes must provide. Interfaces are useful for achievingloose couplingand allowing different classes to adhere to a common interface without being tightly coupled to each other.
Static Variable:When a variable is declared as static within a class, it means that the variable belongs to the class itself rather than to any instance of the class. There will be only one copy of a static variable regardless of how many instances of the class are created.
public class MyClass {
public static int myStaticVariable = 10;
public static void main(String[] args) {
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
// Both objects share the same static variable
obj1.myStaticVariable = 20;
System.out.println("static variable: " + MyClass.myStaticVariable); // 20
System.out.println("obj1 static variable: " + obj1.myStaticVariable); // 20
System.out.println("obj2 static variable: " + obj2.myStaticVariable); // 20
}
}Static Method:static method is a method that belongs to the class rather than to any specific instance of the class. This means you can call a static method without creating an instance of the class.No Access to Instance Variables: Static methods cannot access instance variables directly because they don't belong to any instance. However, they can access static variables.- Cannot Use
thisKeyword Since static methods are not tied to any particular instance, they cannot use the this keyword to refer to the current instance. - Can Call other
staticmethods directly.
public class MyClass {
static void myStaticMethod() {
System.out.println("This is a static method.");
}
public static void main(String[] args) {
MyClass.myStaticMethod();
}
}public class MyClass {
private static int count = 0; // Static variable
public void incrementCount() {
count++; // Accessing and modifying the static variable
}
public int getCount() {
return count; // Accessing the static variable
}
} public class Main {
public static void main(String[] args) {
MyClass.incrementCount();
System.out.println(MyClass.getCount());
}
}Static Class:In Java, classes themselves cannot be declared as static. They are loaded into memory when the program starts, and each instance of the class has its own memory. However, nested static classes are allowed. These nested classes belong to the outer class and can be instantiated without requiring an instance of the outer class.
public class OuterClass {
static class NestedStaticClass {
void display() {
System.out.println("This is a static nested class.");
}
}
public static void main(String[] args) {
OuterClass.NestedStaticClass nested = new OuterClass.NestedStaticClass();
nested.display();
}
}-
Final Attribute (Variable):A final attribute cannot be reassigned once initialized.class MyClass { // Final attribute final int constant = 10; // Method attempting to change the value of constant public void changeValue() { // This would result in a compilation error // constant = 20; } } public class Main { public static void main(String[] args) { // Instantiate MyClass MyClass obj = new MyClass(); // Access constant attribute System.out.println("Constant value: " + obj.constant); } }
-
Final Method:A final method cannot be overridden in subclasses.class Parent { // Final method public final void finalMethod() { // Method implementation } } public class Child extends Parent { // Attempting to override finalMethod will result in a compilation error // public void finalMethod() { } public static void main(String[] args) { // Instantiate Child class Child child = new Child(); // Call finalMethod child.finalMethod(); } }
-
Final Class:When a class is declared as final, it means that it cannot be subclassed. This prevents other classes from inheriting from it.final class FinalClass { // Class members and methods } public class Main { public static void main(String[] args) { // Instantiate FinalClass FinalClass obj = new FinalClass(); // Access methods or members of FinalClass } }





