What is Base64 Encoding?
Base64 encoding is a process of converting binary data (like images, files, or text) into a string of
printable ASCII characters. This ensures safe transmission of data over protocols that only support text
(like HTTP, SMTP, or JSON APIs).
Earlier Approach:
Java originally offered Base64 support via the internal class:
[Link].BASE64Encoder
However, it was non-public and undocumented, limiting its usability.
Java 8 Solution:
With Java 8, a public and standard utility was introduced:
In Java 8 and above, [Link].Base64 provides built-in methods to encode and decode data using
Base64 schemes.
Base64 Encoders and Decoders in Java
1. Basic Encoding and Decoding
Based on RFC 4648 and RFC 2045
Suitable for encoding small chunks of data.
No line breaks are inserted in the encoded output.
Characters outside Base64 are rejected by the decoder.
2. URL and Filename Encoding and Decoding
Also based on RFC 4648.
Designed for encoding data that will be used in URLs or filenames.
Line breaks and unsafe characters are excluded.
Safe for web transmission.
Key Benefits
Feature Benefit
Built-in Support Available directly from Java 8+, no need for third-party libraries.
Platform Independent Encoded data is ASCII text, usable across platforms and devices.
Transmission-Friendly Ideal for APIs, web, email where binary data can't be sent directly.
Multiple Encoders Choose based on use-case: Basic, URL-safe, or MIME (for emails).
Secure and Consistent Rejects invalid characters, minimizing corruption or misuse.
Used for Backward compatibility
Use Case Example:
import [Link].Base64;
public class Base64Example {
public static void main(String[] args) {
String input = "Hello Java!";
String encoded = [Link]().encodeToString([Link]());
String decoded = new String([Link]().decode(encoded));
[Link]("Encoded: " + encoded);
[Link]("Decoded: " + decoded);
}
}
MIME Encoding and an example of encoding/decoding an image file and password using
Base64 in Java.
1. MIME Encoding and Decoding in Java
MIME (Multipurpose Internet Mail Extensions) encoding is used when the encoded output
needs to be split across lines—such as in email attachments or when sending large content via
MIME-based protocols.
Usage:
[Link] mimeEncoder = [Link]();
[Link] mimeDecoder = [Link]();
Example:
import [Link].Base64;
public class MimeBase64Example {
public static void main(String[] args) {
String input = "This is a sample message that needs to be encoded
using MIME Base64 encoding for secure email transmission.";
[Link] mimeEncoder = [Link](20, "\
n".getBytes());
String encoded = [Link]([Link]());
[Link]("MIME Encoded:\n" + encoded);
[Link] mimeDecoder = [Link]();
String decoded = new String([Link](encoded));
[Link]("\nDecoded:\n" + decoded);
}
}
2. Encoding and Decoding an Image File using Base64
Use Case:
Sending images as Base64 in JSON APIs, HTML, or email.
Example:
import [Link].*;
import [Link].Base64;
import [Link];
import [Link];
public class ImageBase64 {
public static void main(String[] args) throws IOException {
String filePath = "[Link]";
// Encode image to Base64
byte[] imageBytes = [Link]([Link](filePath));
String encodedImage = [Link]().encodeToString(imageBytes);
[Link]("Encoded Image (first 100 chars): " +
[Link](0, 100) + "...");
// Decode back to image
byte[] decodedBytes = [Link]().decode(encodedImage);
[Link]([Link]("decoded_image.png"), decodedBytes);
[Link]("Image decoded and saved as decoded_image.png");
}
}
3. Password Encryption using Base64 (Not Secure for Real
Encryption)
Base64 is not encryption—it's just encoding. It’s useful for obfuscation, but not for security.
Example:
import [Link].Base64;
public class PasswordEncoding {
public static void main(String[] args) {
String password = "MySecurePassword@123";
String encodedPassword =
[Link]().encodeToString([Link]());
[Link]("Encoded Password: " + encodedPassword);
String decodedPassword = new
String([Link]().decode(encodedPassword));
[Link]("Decoded Password: " + decodedPassword);
}
}
Note: For secure password storage, use hashing algorithms like BCrypt, not Base64.
Encoding vs. Encryption are both methods used to transform data, but they serve very different
purposes. Here's a simple and clear comparison:
Aspect Encoding Encryption
To convert data into a readable To protect data confidentiality from
Purpose
format for compatibility unauthorized access
Reversible transformation
Process using publicly known Reversible transformation using secret keys
algorithms
Security Not secure; easily reversible Secure if the encryption key is kept secret
Examples Base64, ASCII, URL Encoding AES, RSA, DES
Key/Password
No Yes (Public/Private or Symmetric Key)
Needed?
Data storage, transmission Secure communication, file encryption, data
Use Case
(e.g., email attachments, URLs) privacy
Human
Yes, mostly No, produces gibberish unless decrypted
Readable?
Primary Concern Data integrity and compatibility Data confidentiality and privacy
In Short:
Encoding is for understanding and interoperability (e.g., Base64).
Encryption is for security and privacy (e.g., AES, RSA).
forEach() Method in Java
The forEach() method in Java is used to iterate over each element of a collection such as a List, Set, or
Map. It was introduced in Java 8 as part of the [Link] interface and supports lambda
expressions and method references for cleaner and more functional-style code.
Syntax:
[Link](action);
Where:
collection is any class implementing Iterable (like ArrayList, HashSet, etc.)
action is a lambda expression or method reference.
Example
public class ForEachArrayExample1 {
public static void main(String[] args) {
String[] colors = {"Red", "Green", "Blue"};
for (String color : colors) {
[Link]("Color: " + color);
}
} }
Example using List:
import [Link].*;
public class ForEachExample {
public static void main(String[] args) {
List<String> names = [Link]("Aman", "Ravi", "Sneha");
// Using lambda
[Link](name -> [Link]("Name: " + name));
// Using method reference
[Link]([Link]::println);
}
}
Example using Map:
import [Link].*;
public class ForEachMapExample {
public static void main(String[] args) {
Map<Integer, String> students = new HashMap<>();
[Link](1, "Asha");
[Link](2, "Rahul");
[Link](3, "Neha");
// Using lambda
[Link]((id, name) -> [Link]("ID: " + id + ",
Name: " + name));
}
}
⭐ Benefits of forEach()
Clean and concise code.
Functional programming style.
Avoids boilerplate code of traditional for or while loops.
Easily supports parallel streams for concurrent processing (with .parallelStream()).
Note:
forEach() is best suited for read-only or simple processing. If you need to modify the list
while iterating, use a regular for loop or Iterator instead.
What is try-with-resources in Java?
try-with-resources is a special version of the try block in Java that automatically closes
resources like files, sockets, or database connections when they are no longer needed.
Introduced in Java 7, it helps us avoid resource leaks—like forgetting to close a file or database
connection manually.
Why is it useful?
Traditionally, we had to write:
FileReader fr = null;
try {
fr = new FileReader("[Link]");
// Read the file
} catch (IOException e) {
[Link]();
} finally {
try {
if (fr != null) [Link]();
} catch (IOException e) {
[Link]();
}
}
This is long and error-prone.
try-with-resources simplifies this:
try (FileReader fr = new FileReader("[Link]")) {
int ch;
while ((ch = [Link]()) != -1) {
[Link]((char) ch);
}
} catch (IOException e) {
[Link]();
}
What happens here?
fr (the FileReader) is declared inside the parentheses after try.
Java automatically closes it when the try block finishes—even if there's an exception!
Important Condition:
The resource must implement the AutoCloseable interface.
Most Java I/O classes like FileReader, BufferedReader, Scanner, FileInputStream, etc.,
already implement AutoCloseable.
Another Example: Reading from a file using BufferedReader
import [Link];
import [Link];
import [Link];
public class TryWithResourcesExample {
public static void main(String[] args) {
String file = "[Link]";
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = [Link]()) != null) {
[Link](line);
}
} catch (IOException e) {
[Link]("Error reading file: " + [Link]());
}
}
}
What if you need multiple resources?
try (
BufferedReader reader = new BufferedReader(new FileReader("[Link]"));
PrintWriter writer = new PrintWriter("[Link]")
) {
String line;
while ((line = [Link]()) != null) {
[Link]([Link]());
}
} catch (IOException e) {
[Link]();
}
Both resources (reader and writer) are closed automatically in reverse order of their
declaration.
Summary
Traditional try-
Feature try-with-resources
finally
Manual closing needed ✅ Yes ❌ No
Cleaner & shorter code ❌ No ✅ Yes
Auto-close on exception ❌ No ✅ Yes
Annotation in Java
What is Type Annotation in Java?
Java annotations are a kind of metadata—extra information we attach to Java code (like classes,
methods, variables, etc.) to give hints to the compiler or tools. They do not directly affect how
the program runs.
Example of a regular annotation:
@Override
public String toString() {
return "Example";
}
What Are Type Annotations?
While regular annotations go on declarations, type annotations go on the actual data types
used in code.
Think of it like this:
Regular annotation = "Label for a method or variable"
Type annotation = "Label for the type used in a variable or method"
Type annotations are a feature introduced in Java 8 that allow us to apply annotations not just
to declarations, but also directly to types. This gives more control to tools (like compilers or
static analyzers) to check for correctness, null-safety, immutability, and more.
Regular Annotation vs Type Annotation – Comparison
Feature Regular Annotation Type Annotation
Introduced in JDK 5 JDK 8
Type usage (like @NonNull
Placement Class, method, field declaration
String)
Target [Link], FIELD, etc. ElementType.TYPE_USE
Usage Example @Deprecated on a method List<@NonNull String>
Purpose Code hinting, instructions for compiler Detailed type-checking, validation
Enabling Type Annotations
To use a type annotation, your annotation must be defined with:
@Target(ElementType.TYPE_USE)
You may also include TYPE_PARAMETER if needed.
Basic Example
import [Link].*;
@Target(ElementType.TYPE_USE)
@Retention([Link])
@interface NonNull {}
public class Example {
public void print(@NonNull String message) {
[Link]([Link]());
}
}
Here, @NonNull is a type annotation applied to the String parameter message. Tools like
checker framework or IDEs can use this to give compile-time warnings if you try to pass null.
Why Use Type Annotations?
To detect null pointer bugs early
To write safer, cleaner code
To allow tools like the Checker Framework or IDE plugins to enforce rules
Visual Placement Diagram (Text Format)
Here's a basic representation of where type annotations can go:
@MyAnnotation String name; // On declaration
String @MyAnnotation [] arr; // On array component type
List<@MyAnnotation String> list; // On generic type
(@MyAnnotation String) obj; // On type cast
1. Marker Annotations
Definition: A marker annotation is an annotation without any elements/parameters. It
works like a flag to give some instruction to the compiler or tools.
Think of it as:
"Mark this method or class as special, no need to pass any values."
Built-in Example: @Override
class Animal {
void sound() {
[Link]("Animal sound");
}
}
class Dog extends Animal {
@Override // This tells the compiler that you're overriding a superclass
method
void sound() {
[Link]("Bark");
}
}
Custom Example:
@interface Important {} // Marker annotation
@Important
class Task { }
➡ The @Important annotation marks Task as something significant (a tool or framework can
look for this).
2. Single-Value Annotations
Definition: These annotations allow only one value, usually through a field named value().
Custom Example:
@interface Author {
String value();
}
@Author("John Doe")
class Book {
}
How It Works:
You only need to mention the value.
Java assumes it’s assigned to the method value() internally.
If you want to be explicit:
@Author(value = "John Doe") // also valid
3. Full Annotations (Multi-Value Annotations)
Definition: Full annotations contain multiple fields (key-value pairs) inside.
Custom Example:
@interface Info {
String author();
String version();
String date();
}
@Info(author = "Alice", version = "2.0", date = "2025-05-25")
public class Project {
}
Why Use:
Add detailed metadata
Used in frameworks like Spring, JUnit, Hibernate, etc.
Real Framework Example (JUnit 5):
@Test(timeout = 1000)
@DisplayName("Check login with valid user")
4. Type Annotations
Definition: Type Annotations let us annotate types directly — such as String, int, generics,
arrays, casts, etc.
Requires:
@Target(ElementType.TYPE_USE)
@Retention([Link])
Example:
@Target(ElementType.TYPE_USE)
@interface NonNull {}
public void greet(@NonNull String name) {
[Link]("Hello, " + name);
}
Tools like Checker Framework, FindBugs, or IDE plugins can warn if a method violates this
constraint (e.g., null passed to @NonNull).
More Type Annotation Use Cases:
List<@NonNull String> names;
String @NonNull [] data;
Object obj = (@NonNull String) rawObject;
5. Repeating Annotations
Problem It Solves:
Before Java 8, you could not use the same annotation multiple times on one element. From
Java 8 onward, you can do this with @Repeatable.
Step-by-Step:
1. Create main annotation:
@Repeatable([Link])
@interface Role {
String value();
}
2. Create the container annotation:
@interface Roles {
Role[] value();
}
3. Use in your code:
@Role("Admin")
@Role("User")
class Account { }
Java internally wraps this as:
@Roles({@Role("Admin"), @Role("User")})
➡ Useful in security, access roles, or categorization.
How Java Internally Handles Annotations
All annotations are:
Compiled into .class files
Retained based on @Retention policy (SOURCE, CLASS, or RUNTIME)
Read at compile-time, class loading, or runtime
Summary Table
Parameters Introduced
Type Common Use
Allowed? In
Marker Signaling (e.g., @Override,
❌ No JDK 5
Annotations @Deprecated)
Single-Value ✅ One (default
Simple metadata (@Author("A")) JDK 5
Annotations value())
Full Annotations ✅ Multiple Detailed metadata (@Info(...)) JDK 5
Type Annotations ✅ On types Advanced checking (e.g., null-safety) JDK 8
Repeating ✅ Multiple same
Role-based, event tagging JDK 8
Annotations annotations
1. Built-in Java Annotations Used in Java Code
These are annotations we commonly use while writing everyday Java programs:
@Override
Purpose:
Indicates that the method overrides a method from its superclass.
Why it's important:
Helps catch errors at compile time
Prevents mistakes like misnaming or wrong method signatures
Example:
class Parent {
void greet() { }
}
class Child extends Parent {
@Override
void greet() {
[Link]("Hello!");
}
}
If you wrote greets() instead of greet(), the compiler would give an error.
@SuppressWarnings
Purpose:
Tells the compiler to ignore certain warnings.
Common Warnings: "unchecked", "deprecation", "unused"
Example:
@SuppressWarnings("unchecked")
public void showList() {
List list = new ArrayList(); // unchecked warning suppressed
}
Why use it?
When you're sure the warning is not an issue, and you want to keep your code clean.
@Deprecated
Purpose:
Marks that a class, method, or field is outdated and should not be used.
Why it matters:
It helps developers avoid using old or risky code.
IDEs will often strike through deprecated code.
Example:
@Deprecated
void oldMethod() {
[Link]("Use newMethod() instead!");
}
Modern frameworks like Spring mark old methods as deprecated to guide users toward newer
APIs.
2. Built-in Annotations Used in Other Annotations
These are meta-annotations, meaning they are used to define how other annotations behave.
@Target
Purpose:
Defines where an annotation can be applied.
Values (from ElementType) include:
METHOD, FIELD, CONSTRUCTOR, TYPE, PARAMETER, TYPE_USE, etc.
Example:
@Target([Link])
@interface MyAnnotation { }
Now @MyAnnotation can only be used on methods.
@Retention
Purpose:
Specifies how long an annotation is kept.
Retention Policies:
SOURCE: Removed at compile-time
CLASS: Kept in .class file but not available at runtime
RUNTIME: Available at runtime via reflection
Example:
@Retention([Link])
@interface Trackable { }
Needed when you want to read annotations at runtime (e.g., using reflection).
@Inherited
Purpose:
Allows child classes to inherit an annotation from the parent class.
Example:
@Inherited
@interface Audited {}
@Audited
class BaseClass { }
class SubClass extends BaseClass { }
Now SubClass will also be considered as annotated with @Audited.
Limitation:
Works only on classes, not methods or fields.
@Documented
Purpose:
Ensures that the annotation appears in the JavaDocs.
Example:
@Documented
@interface Important { }
If you use @Important on a method or class, it will show up in the generated JavaDoc files.
Summary Table
Annotation Purpose Common Use Area
@Override Validates method override Methods
@SuppressWarnings Hides unwanted compiler warnings Methods, Classes
@Deprecated Marks a method/class as outdated Methods, Classes
In annotation
@Target Limits where an annotation can be applied
definitions
In annotation
@Retention Defines how long the annotation is kept
definitions
@Inherited Allows annotation inheritance for subclasses Classes
@Documented Includes annotation in Javadoc Classes, Methods
1. What is a Module System in Java?
Starting from Java 9, a module is a self-contained unit of Java code that groups together:
Packages
Classes
Interfaces
Configuration files
Resources
This concept was introduced to better organize, secure, and optimize Java applications.
Simple Definition:
A module is a named, reusable, and declarative collection of related Java code (packages), with
clear dependencies and boundaries.
2. Why Was the Module System Needed?
Before Java 9:
Java applications were heavy (e.g., [Link] alone ≈ 64MB).
Everything in the JDK loaded together—even unused parts.
No strict control on which classes could access others.
It was difficult to scale apps to smaller devices (like IoT).
Solution in Java 9:
Java introduced a modular JDK and JVM structure, allowing developers to:
Load only necessary modules
Prevent circular dependencies
Improve performance, security, and maintainability
3. Key Tools and Enhancements in Java 9 Module System
Tools:
javac – Compile Java code and modules.
java – Run modular applications.
jlink – Create custom runtimes by linking only required modules.
jdeps – Analyze dependencies between modules.
New File Types:
Modular JAR – A JAR that includes a [Link] file.
JMOD – Similar to JAR, but includes native code and config files.
4. JDK 9 Directory Restructure
JDK 8 JDK 9
Has jre, [Link], lib Has jmods instead
Monolithic and heavy Modular and lightweight
jmods folder:
Contains compiled modules like:
[Link]
[Link]
[Link]
[Link]
5. Module Dependency Graph
[Link] is the foundation. Every module depends on it.
The structure is a Directed Acyclic Graph (DAG):
o This means no circular dependencies are allowed.
6. Core Concepts in [Link]
Every module must have a module descriptor file called:
[Link]
It declares:
module name – Like a package name ([Link])
requires – Which modules it depends on
exports – Which packages it makes public
(Optional) opens, uses, provides for advanced configurations
Example:
module [Link] {
exports [Link];
requires [Link];
}
7. Steps to Create a Java Module
Step-by-step:
1. Create directory structure (reverse domain style):
arduino
src/
└── com/javamodule/
├── [Link]
└── [Link]
2. Write module descriptor ([Link]):
module [Link] {
}
3. Write Java class ([Link]):
package [Link];
public class Greeting {
public static void main(String[] args) {
[Link]("Hello Java World!!");
}
}
4. Compile module:
javac -d mods --module-source-path . --module [Link]
5. Run module:
java --module-path mods --module [Link]/[Link]
Summary Table
Feature Description
Introduced in Java 9
Key file [Link]
Core module [Link] (required by all)
Benefits Better security, reduced size, organized dependencies
Key tools javac, java, jlink, jdeps
Packaging formats Modular JAR, JMOD
Execution flow Write module ➝ Compile ➝ Run with --module-path