Bit Fields in C

 Bit Fields in C


Bit fields allow you to define structures with members of specific widths in terms of bits. This is useful when you want to optimize memory usage or work with packed data structures.


#include <stdio.h>


struct Status {

    unsigned int flag1 : 1;  // 1-bit field

    unsigned int flag2 : 2;  // 2-bit field

    unsigned int flag3 : 3;  // 3-bit field

};


int main() {

    struct Status s;

    s.flag1 = 1;

    s.flag2 = 2;

    s.flag3 = 5;


    printf("Flag 1: %u\n", s.flag1);

    printf("Flag 2: %u\n", s.flag2);

    printf("Flag 3: %u\n", s.flag3);


    printf("Size of struct Status: %zu bytes\n", sizeof(struct Status));


    return 0;

}

In this example, the struct Status contains bit fields that define the widths of individual members. This allows for efficient memory usage, as each member is allocated the specified number of bits.

Function Pointers and Callbacks in C

 Function Pointers and Callbacks in C


Function pointers allow you to store the address of a function and invoke it later. They are particularly useful when combined with callback mechanisms, where functions can be passed as arguments to other functions.


#include <stdio.h>


int add(int a, int b) {

    return a + b;

}


int subtract(int a, int b) {

    return a - b;

}


int calculator(int (*operation)(int, int), int a, int b) {

    return operation(a, b);

}


int main() {

    int result1 = calculator(add, 5, 3);

    int result2 = calculator(subtract, 5, 3);


    printf("Result 1: %d\n", result1);

    printf("Result 2: %d\n", result2);


    return 0;

}

In this example, the calculator function takes a function pointer as an argument and invokes it with two integer arguments. This allows for dynamic function dispatching and flexibility in the operations performed.

Variadic Functions in C

 Variadic Functions in C


Variadic functions allow you to define functions that can accept a variable number of arguments. This is useful when you want to create functions with a flexible number of parameters.


#include <stdio.h>

#include <stdarg.h>


double average(int count, ...) {

    double sum = 0;


    va_list args;

    va_start(args, count);


    for (int i = 0; i < count; i++) {

        sum += va_arg(args, int);

    }


    va_end(args);


    return sum / count;

}


int main() {

    double avg1 = average(3, 2, 4, 6);

    double avg2 = average(5, 1, 3, 5, 7, 9);


    printf("Average 1: %.2f\n", avg1);

    printf("Average 2: %.2f\n", avg2);


    return 0;

}

In this example, the average function takes a variable number of integer arguments and calculates their average. The stdarg.h header provides the necessary functions and macros to work with variadic arguments.


Error Handling with errno and perror in C

 Error Handling with errno and perror in C


The errno variable is set by system calls and library functions to indicate errors. The perror function can be used to print a descriptive error message based on the value of errno.


#include <stdio.h>

#include <stdlib.h>

#include <errno.h>


int main() {

    FILE* file = fopen("nonexistent.txt", "r");


    if (file == NULL) {

        perror("Error");

        printf("Error code: %d\n", errno);

        return 1;

    }


    // Code to work with the file


    fclose(file);


    return 0;

}

Memory Management: malloc, calloc, realloc, and free in C

 Memory Management: malloc, calloc, realloc, and free in C


C provides functions for dynamic memory allocation and deallocation. malloc is used to allocate a block of memory, calloc initializes the allocated memory to zero, realloc changes the size of an allocated block, and free deallocates the memory.


#include <stdio.h>

#include <stdlib.h>


int main() {

    int* nums = (int*)malloc(5 * sizeof(int));


    if (nums == NULL) {

        printf("Memory allocation failed.\n");

        return 1;

    }


    for (int i = 0; i < 5; i++) {

        nums[i] = i + 1;

    }


    for (int i = 0; i < 5; i++) {

        printf("%d ", nums[i]);

    }

    printf("\n");


    // Resizing the allocated memory

    int* resizedNums = (int*)realloc(nums, 10 * sizeof(int));


    if (resizedNums == NULL) {

        printf("Memory reallocation failed.\n");

        free(nums); // Free original memory if reallocation fails

        return 1;

    }


    for (int i = 5; i < 10; i++) {

        resizedNums[i] = i + 1;

    }


    for (int i = 0; i < 10; i++) {

        printf("%d ", resizedNums[i]);

    }

    printf("\n");


    free(resizedNums); // Deallocate the memory


    return 0;

}

This example demonstrates allocating memory for an array of integers, resizing the memory, and freeing the allocated memory.

Command-Line Arguments with argc and argv in C

 Command-Line Arguments with argc and argv in C


The argc and argv parameters of the main function allow you to handle command-line arguments passed to a C program. argc represents the number of arguments, and argv is an array of strings containing the actual arguments.


#include <stdio.h>


int main(int argc, char* argv[]) {

    printf("Number of arguments: %d\n", argc);


    printf("Arguments:\n");

    for (int i = 0; i < argc; i++) {

        printf("%d: %s\n", i, argv[i]);

    }


    return 0;

}

When running the program from the command line, you can pass arguments after the program name:


./program arg1 arg2 arg3

The program will print the number of arguments and list them with their respective indices.

Bitwise Operations in C

 Bitwise Operations in C


Bitwise operations allow you to manipulate individual bits of data. They are useful for tasks such as setting/clearing specific bits, checking bit flags, or performing bitwise logical operations.



#include <stdio.h>


int main() {

    unsigned int flags = 0b1010;  // Binary representation: 1010


    // Setting bit 2 (third bit) to 1

    flags |= (1 << 2);


    // Clearing bit 0 (first bit)

    flags &= ~(1 << 0);


    // Checking if bit 3 (fourth bit) is set

    if (flags & (1 << 3)) {

        printf("Bit 3 is set.\n");

    }


    // Toggling bit 1 (second bit)

    flags ^= (1 << 1);


    // Printing the resulting flags value

    printf("Flags: %u\n", flags);


    return 0;

}

Enumerations in C

 Enumerations in C


Enumerations allow you to define a set of named constants with associated values. They provide a convenient way to represent a finite set of values and improve code readability.



#include <stdio.h>


enum Color {

    RED,

    GREEN,

    BLUE

};


int main() {

    enum Color c = GREEN;


    switch (c) {

        case RED:

            printf("Color is red.\n");

            break;

        case GREEN:

            printf("Color is green.\n");

            break;

        case BLUE:

            printf("Color is blue.\n");

            break;

        default:

            printf("Unknown color.\n");

            break;

    }


    return 0;

}

File Handling with Standard I/O in C

 File Handling with Standard I/O in C


In addition to low-level file I/O operations, C provides high-level file handling functions based on standard I/O. These functions allow you to perform file read/write operations using the familiar printf and scanf format specifiers.



#include <stdio.h>


int main() {

    FILE* file = fopen("data.txt", "w");


    if (file == NULL) {

        printf("File could not be opened.\n");

        return 1;

    }


    fprintf(file, "Hello, World!\n");

    fputs("This is a line of text.", file);


    fclose(file);


    file = fopen("data.txt", "r");


    if (file == NULL) {

        printf("File could not be opened.\n");

        return 1;

    }


    char buffer[100];

    while (fgets(buffer, sizeof(buffer), file) != NULL) {

        printf("%s", buffer);

    }


    fclose(file);


    return 0;

}

Pointers to Pointers and Multilevel Indirection in C

 Pointers to Pointers and Multilevel Indirection in C


Pointers to pointers allow you to create multilevel indirection and handle complex data structures that involve multiple levels of memory references.



#include <stdio.h>


int main() {

    int num = 42;

    int* ptr = &num;

    int** ptrToPtr = &ptr;

    int*** ptrToPtrToPtr = &ptrToPtr;


    printf("Value: %d\n", ***ptrToPtrToPtr);

    printf("Address: %p\n", ***ptrToPtrToPtr);


    return 0;

}

Preprocessor Directives and Macros in C

 Preprocessor Directives and Macros in C


The C preprocessor provides directives and macros for code preprocessing before compilation. Macros allow you to define reusable code snippets, while directives control compilation and inclusion of code from other files.



#include <stdio.h>


#define MAX(a, b) ((a) > (b) ? (a) : (b))

#define PI 3.14159


#ifdef DEBUG

#define DEBUG_PRINT(...) printf(__VA_ARGS__)

#else

#define DEBUG_PRINT(...)

#endif


int main() {

    int num1 = 10;

    int num2 = 20;

    int max = MAX(num1, num2);

    printf("Maximum: %d\n", max);


    double circleArea = PI * 2.5 * 2.5;

    printf("Circle area: %.2f\n", circleArea);


    DEBUG_PRINT("Debug mode is enabled\n");


    return 0;

}

Function Pointers with Structures in C

 Function Pointers with Structures in C


Function pointers can be combined with structures to create powerful and flexible data structures that encapsulate both data and behavior.



#include <stdio.h>


struct Operation {

    int (*perform)(int, int);

};


int add(int a, int b) {

    return a + b;

}


int subtract(int a, int b) {

    return a - b;

}


int main() {

    struct Operation op1 = {add};

    struct Operation op2 = {subtract};


    int result1 = op1.perform(5, 3);

    int result2 = op2.perform(5, 3);


    printf("Result 1: %d\n", result1);

    printf("Result 2: %d\n", result2);


    return 0;

}

Structures and Unions in C

 Structures and Unions in C


Structures and unions allow you to define custom data types by grouping together multiple variables of different types. Structures are used to group related data elements, while unions allow different types of data to share the same memory space.



#include <stdio.h>


struct Point {

    int x;

    int y;

};


union Data {

    int intValue;

    float floatValue;

    char stringValue[20];

};


int main() {

    struct Point p;

    p.x = 3;

    p.y = 5;

    printf("Point coordinates: (%d, %d)\n", p.x, p.y);


    union Data data;

    data.intValue = 42;

    printf("Integer value: %d\n", data.intValue);

    data.floatValue = 3.14;

    printf("Float value: %.2f\n", data.floatValue);

    strcpy(data.stringValue, "Hello");

    printf("String value: %s\n", data.stringValue);


    return 0;

}

Dynamic Memory Allocation for Strings in C

 Dynamic Memory Allocation for Strings in C


Dynamic memory allocation is often used for handling strings of variable lengths. Functions like malloc, realloc, and free can be used to allocate and manipulate memory for strings.



#include <stdio.h>

#include <stdlib.h>

#include <string.h>


int main() {

    char* message = (char*)malloc(20 * sizeof(char));

    strcpy(message, "Hello, ");

    message = (char*)realloc(message, 30 * sizeof(char));

    strcat(message, "world!");

    printf("%s\n", message);

    free(message);


    return 0;

}

Command-Line Arguments with getopt in C

 Command-Line Arguments with getopt in C


The getopt function allows you to handle command-line arguments and options in a more structured manner. It simplifies the parsing of command-line arguments and provides support for both short and long options.



#include <stdio.h>

#include <unistd.h>


int main(int argc, char* argv[]) {

    int opt;

    while ((opt = getopt(argc, argv, "af:")) != -1) {

        switch (opt) {

            case 'a':

                printf("Option -a is specified\n");

                break;

            case 'f':

                printf("Option -f is specified with value: %s\n", optarg);

                break;

            default:

                printf("Unknown option: %c\n", opt);

                break;

        }

    }


    for (int i = optind; i < argc; i++) {

        printf("Non-option argument: %s\n", argv[i]);

    }


    return 0;

}

Pointers to Functions in C

Pointers to Functions in C


Pointers to functions allow you to store and pass around the addresses of functions. This provides flexibility in function invocation and enables dynamic function dispatch.



#include <stdio.h>


int add(int a, int b) {

    return a + b;

}


int subtract(int a, int b) {

    return a - b;

}


int multiply(int a, int b) {

    return a * b;

}


int main() {

    int (*operation)(int, int);  // Pointer to function declaration

    operation = add;             // Assign address of add function

    printf("Addition: %d\n", operation(3, 2));


    operation = subtract;        // Assign address of subtract function

    printf("Subtraction: %d\n", operation(3, 2));


    operation = multiply;        // Assign address of multiply function

    printf("Multiplication: %d\n", operation(3, 2));


    return 0;

}

File I/O with Binary Data in C

 File I/O with Binary Data in C


C provides file I/O operations for reading and writing binary data. This is useful when working with files that store structured data or when you need to perform low-level data manipulation.



#include <stdio.h>


struct Person {

    char name[50];

    int age;

    float height;

};


int main() {

    struct Person person = {"John", 25, 175.5};


    FILE* file = fopen("data.bin", "wb");

    if (file == NULL) {

        printf("File could not be opened.\n");

        return 1;

    }


    fwrite(&person, sizeof(struct Person), 1, file);

    fclose(file);


    file = fopen("data.bin", "rb");

    if (file == NULL) {

        printf("File could not be opened.\n");

        return 1;

    }


    struct Person readPerson;

    fread(&readPerson, sizeof(struct Person), 1, file);

    printf("Name: %s\n", readPerson.name);

    printf("Age: %d\n", readPerson.age);

    printf("Height: %.2f\n", readPerson.height);


    fclose

Dynamic Data Structures in C

 Dynamic Data Structures


In addition to arrays, C allows you to create dynamic data structures such as linked lists, stacks, queues, and trees. Implementing these data structures enhances your ability to manage complex data efficiently.



#include <stdio.h>

#include <stdlib.h>


struct Node {

    int data;

    struct Node* next;

};


struct Stack {

    struct Node* top;

};


void push(struct Stack* stack, int data) {

    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));

    newNode->data = data;

    newNode->next = stack->top;

    stack->top = newNode;

}


int pop(struct Stack* stack) {

    if (stack->top == NULL) {

        printf("Stack underflow\n");

        return -1;

    }


    struct Node* temp = stack->top;

    int data = temp->data;

    stack->top = stack->top->next;

    free(temp);


    return data;

}


int main() {

    struct Stack stack;

    stack.top = NULL;


    push(&stack, 3);

    push(&stack, 2);

    push(&stack, 1);


    printf("Popped element: %d\n", pop(&stack));

    printf("Popped element: %d\n", pop(&stack));

    printf("Popped element: %d\n", pop(&stack));

    printf("Popped element: %d\n", pop(&stack));


    return 0;

}

Function Pointers and Callbacks in C

 Function Pointers and Callbacks


Function pointers allow you to create variables that can store the address of functions. This enables dynamic function invocation and can be used for implementing callback mechanisms.



#include <stdio.h>


void greet() {

    printf("Hello!\n");

}


void farewell() {

    printf("Goodbye!\n");

}


void invoke(void (*function)()) {

    function();

}


int main() {

    void (*callback)();  // Function pointer declaration

    callback = greet;    // Assign address of greet function

    invoke(callback);    // Call greet function through function pointer


    callback = farewell; // Assign address of farewell function

    invoke(callback);    // Call farewell function through function pointer


    return 0;

}

Linked Lists in C

 Linked Lists


Linked lists are dynamic data structures that allow efficient insertion and deletion of elements. They consist of nodes containing data and a pointer to the next node. Implementing linked lists enhances your understanding of data structures and memory management.



#include <stdio.h>

#include <stdlib.h>


struct Node {

    int data;

    struct Node* next;

};


void insert(struct Node** head, int data) {

    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));

    newNode->data = data;

    newNode->next = *head;

    *head = newNode;

}


void display(struct Node* head) {

    struct Node* current = head;

    while (current != NULL) {

        printf("%d ", current->data);

        current = current->next;

    }

    printf("\n");

}


int main() {

    struct Node* head = NULL;

    insert(&head, 3);

    insert(&head, 2);

    insert(&head, 1);

    display(head);


    return 0;

}

Error Handling in C

 Error Handling


Error handling is essential to handle unexpected situations and ensure robust programs. C provides mechanisms like return values, error codes, and errno variable for error handling. Additionally, you can use perror and strerror functions to print error messages.



#include <stdio.h>

#include <errno.h>


int main() {

    FILE* file = fopen("nonexistent_file.txt", "r");


    if (file == NULL) {

        printf("Error opening file: %s\n", strerror(errno));

        perror("fopen");

        return 1;

    }


    // File processing...


    fclose(file);


    return 0;

}

Memory Management

 Memory Management


Memory management is crucial for efficient resource utilization. C provides manual memory management through functions like malloc, calloc, realloc, and free. Understanding memory management allows you to allocate and deallocate memory dynamically.



#include <stdio.h>

#include <stdlib.h>


int main() {

    int* numbers = (int*)malloc(5 * sizeof(int));


    if (numbers == NULL) {

        printf("Memory allocation failed.\n");

        return 1;

    }


    for (int i = 0; i < 5; i++) {

        numbers[i] = i + 1;

    }


    for (int i = 0; i < 5; i++) {

        printf("%d ", numbers[i]);

    }


    free(numbers);


    return 0;

}

Command-Line Arguments

 Command-Line Arguments


Command-line arguments allow you to pass arguments to a C program when it is executed from the command line. They provide flexibility and enable program behavior customization.



#include <stdio.h>


int main(int argc, char* argv[]) {

    printf("Number of command-line arguments: %d\n", argc);


    printf("Arguments:\n");

    for (int i = 0; i < argc; i++) {

        printf("%d: %s\n", i, argv[i]);

    }


    return 0;

}

Multi-dimensional Arrays

 Multi-dimensional Arrays


Multi-dimensional arrays are arrays with more than one dimension. They allow you to store data in a tabular form or create complex data structures like matrices.



#include <stdio.h>


int main() {

    int matrix[3][3] = {{1, 2, 3},

                        {4, 5, 6},

                        {7, 8, 9}};


    printf("Matrix:\n");

    for (int i = 0; i < 3; i++) {

        for (int j = 0; j < 3; j++) {

            printf("%d ", matrix[i][j]);

        }

        printf("\n");

    }


    return 0;

}

Recursion in C

 Recursion


Recursion is a programming technique where a function calls itself to solve a problem. It is particularly useful for solving problems that can be divided into smaller subproblems. However, it's important to handle base cases to prevent infinite recursion.



#include <stdio.h>


int factorial(int n) {

    if (n == 0) {

        return 1; // Base case: factorial of 0 is 1

    } else {

        return n * factorial(n - 1); // Recursive call

    }

}


int main() {

    int n = 5;

    int result = factorial(n);

    printf("Factorial of %d: %d\n", n, result);


    return 0;

}

Bit Manipulation

 Bit Manipulation


Bit manipulation involves performing operations on individual bits within a data structure. It allows for compact and efficient storage of data and is commonly used in low-level programming and embedded systems.



#include <stdio.h>


#define SET_BIT(x, pos) (x |= (1 << pos))

#define CLEAR_BIT(x, pos) (x &= ~(1 << pos))

#define TOGGLE_BIT(x, pos) (x ^= (1 << pos))

#define CHECK_BIT(x, pos) (x & (1 << pos))


int main() {

    unsigned char flags = 0x00;


    SET_BIT(flags, 1); // Set bit at position 1

    printf("Bit at position 1: %d\n", CHECK_BIT(flags, 1));


    TOGGLE_BIT(flags, 2); // Toggle bit at position 2

    printf("Bit at position 2: %d\n", CHECK_BIT(flags, 2));


    CLEAR_BIT(flags, 0); // Clear bit at position 0

    printf("Bit at position 0: %d\n", CHECK_BIT(flags, 0));


    return 0;

}

Enumerations (Enums)

 Enumerations (Enums)


Enums allow you to define a set of named constants, representing a group of related values. They provide a convenient way to work with a fixed set of values and improve code readability.



#include <stdio.h>


enum Day {

    MON,

    TUE,

    WED,

    THU,

    FRI,

    SAT,

    SUN

};


int main() {

    enum Day today = WED;


    if (today == WED || today == THU) {

        printf("It's a weekday.\n");

    } else {

        printf("It's a weekend.\n");

    }


    return 0;

}

Preprocessor Directives

 Preprocessor Directives


Preprocessor directives are special instructions to the compiler that are executed before the actual compilation process. They are used to perform various tasks like including header files, defining constants, conditional compilation, and more.



#include <stdio.h>


#define MAX(x, y) ((x) > (y) ? (x) : (y))


#ifdef DEBUG

#define DEBUG_PRINT(x) printf("Debug: %s\n", x)

#else

#define DEBUG_PRINT(x)

#endif


int main() {

    int a = 10;

    int b = 20;

    int max = MAX(a, b);

    printf("Maximum value: %d\n", max);


    DEBUG_PRINT("This is a debug message");


    return 0;

}

Structures and Unions

 Structures and Unions


Structures and unions are used to create user-defined data types that can hold different types of data. Structures group related data together, while unions allow multiple variables to share the same memory location.



#include <stdio.h>


struct Person {

    char name[50];

    int age;

    float height;

};


union Data {

    int intValue;

    float floatValue;

    char stringValue[20];

};


int main() {

    struct Person person;

    strcpy(person.name, "John");

    person.age = 25;

    person.height = 175.5;


    printf("Name: %s\n", person.name);

    printf("Age: %d\n", person.age);

    printf("Height: %.2f\n", person.height);


    union Data data;

    data.intValue = 10;

    printf("Integer Value: %d\n", data.intValue);


    data.floatValue = 3.14;

    printf("Float Value: %.2f\n", data.floatValue);


    strcpy(data.stringValue, "Hello");

    printf("String Value: %s\n", data.stringValue);


    return 0;

}

File Handling

 File Handling


File handling in C allows you to read from and write to files on the disk. You can perform operations like opening files, reading data, writing data, and closing files.



#include <stdio.h>


int main() {

    FILE* file = fopen("data.txt", "w"); // Open the file in write mode


    if (file == NULL) {

        printf("File could not be opened.\n");

        return 1;

    }


    fprintf(file, "Hello, World!\n"); // Write data to the file

    fclose(file); // Close the file


    file = fopen("data.txt", "r"); // Open the file in read mode


    if (file == NULL) {

        printf("File could not be opened.\n");

        return 1;

    }


    char buffer[100];

    fgets(buffer, sizeof(buffer), file); // Read data from the file

    printf("Data: %s", buffer);


    fclose(file); // Close the file


    return 0;

}

Dynamic Memory Allocation

 Dynamic Memory Allocation


Dynamic memory allocation allows you to allocate memory at runtime using functions like malloc, calloc, and realloc. This is useful when you need to work with variable-sized data structures or manage memory efficiently.



#include <stdio.h>

#include <stdlib.h>


int main() {

    int* numbers = (int*)malloc(5 * sizeof(int)); // Allocate memory for 5 integers


    if (numbers == NULL) {

        printf("Memory allocation failed.\n");

        return 1;

    }


    for (int i = 0; i < 5; i++) {

        numbers[i] = i + 1;

    }


    for (int i = 0; i < 5; i++) {

        printf("%d ", numbers[i]);

    }


    free(numbers); // Release the allocated memory


    return 0;

}

Pointers to Functions

 Pointers to Functions


Pointers to functions allow you to store and manipulate function addresses. They can be used to create callbacks and implement advanced programming techniques like function pointers arrays, function pointers as arguments, etc.



#include <stdio.h>


int add(int a, int b) {

    return a + b;

}


int subtract(int a, int b) {

    return a - b;

}


int main() {

    int (*operation)(int, int); // Declare a pointer to a function

    operation = add; // Assign the address of the 'add' function


    int result = operation(5, 3); // Call the function using the pointer

    printf("Result: %d\n", result);


    operation = subtract; // Assign the address of the 'subtract' function

    result = operation(5, 3); // Call the 'subtract' function using the pointer

    printf("Result: %d\n", result);


    return 0;

}

Sets and Maps

 Sets and Maps


Sets and maps are built-in data structures in JavaScript that provide efficient ways to store unique values and key-value pairs, respectively.


// Set

const set = new Set();

set.add(1);

set.add(2);

set.add(3);

set.add(2); // Ignored, already exists


console.log(set.size); // Output: 3

console.log(set.has(2)); // Output: true


// Map

const map = new Map();

map.set('name', 'John');

map.set('age', 30);


console.log(map.size); // Output: 2

console.log(map.get('name')); // Output: John

console.log(map.has('age')); // Output: true

In the example above, a set is created using the Set constructor, and values are added using the add method. Sets automatically eliminate duplicate values. A map is created using the Map constructor, and key-value pairs are added using the set method. Maps provide efficient retrieval of values based on keys using the get method.

Optional Function Arguments

 Optional Function Arguments


JavaScript allows you to define functions with optional arguments by assigning default values to the parameters.


function greet(name = 'Anonymous') {

  console.log(`Hello, ${name}!`);

}


greet(); // Output: Hello, Anonymous!

greet('John'); // Output: Hello, John!

In the example above, the greet function has an optional name parameter that defaults to 'Anonymous' if no value is provided. This allows you to call the function without specifying an argument.

Rest and Spread Operators

 Rest and Spread Operators


The rest and spread operators provide convenient ways to work with arrays and objects.


// Rest Operator

function sum(...numbers) {

  return numbers.reduce((acc, num) => acc + num, 0);

}


console.log(sum(1, 2, 3, 4, 5)); // Output: 15


// Spread Operator

const arr1 = [1, 2, 3];

const arr2 = [4, 5, 6];

const combined = [...arr1, ...arr2];


console.log(combined); // Output: [1, 2, 3, 4, 5, 6]

In the example above, the rest operator (...) is used in the sum function to collect multiple arguments into an array. The spread operator (...) is used to spread the elements of arr1 and arr2 into the combined array.

Optional Chaining Operator

 Optional Chaining Operator


The optional chaining operator (?.) allows you to access nested properties of an object without worrying about potential null or undefined values.


const person = {

  name: 'John',

  address: {

    street: '123 Main St',

    city: 'New York'

  }

};


const street = person.address?.street;

console.log(street);

// Output: 123 Main St


const zipCode = person.address?.zipCode;

console.log(zipCode);

// Output: undefined

In the example above, the optional chaining operator is used to access the street property of the address object. If any intermediate property in the chain is null or undefined, the result will be undefined.

Object Property Shorthand

 Object Property Shorthand


Object property shorthand allows you to create objects more concisely by using variable names as property names.



const name = 'John';

const age = 30;


const person = { name, age };

console.log(person);

// Output: { name: 'John', age: 30 }

In the example above, object property shorthand is used to create the person object. Instead of explicitly specifying name: name and age: age, the shorthand syntax allows you to directly use the variable names as property names.

Class Fields and Methods

 Class Fields and Methods


Class fields and methods provide a more convenient way to define instance variables and methods directly within the class declaration.



class Circle {

  radius = 0;


  constructor(radius) {

    this.radius = radius;

  }


  get area() {

    return Math.PI * this.radius ** 2;

  }


  static circumference(radius) {

    return 2 * Math.PI * radius;

  }

}


const circle = new Circle(5);

console.log(circle.area); // Output: 78.53981633974483

console.log(Circle.circumference(5)); // Output: 31.41592653589793

In the example above, the Circle class has a class field radius and a getter method area that calculates the area of the circle. The circumference method is declared as a static method, which can be invoked directly on the class itself.

Array Methods (map, filter, reduce)

 Array Methods (map, filter, reduce)


JavaScript provides powerful array methods that allow you to manipulate and transform arrays in a concise and functional way.



const numbers = [1, 2, 3, 4, 5];


// map: transforms each element of the array

const multiplied = numbers.map(num => num * 2);

console.log(multiplied); // Output: [2, 4, 6, 8, 10]


// filter: filters elements based on a condition

const evenNumbers = numbers.filter(num => num % 2 === 0);

console.log(evenNumbers); // Output: [2, 4]


// reduce: reduces the array to a single value

const sum = numbers.reduce((acc, num) => acc + num, 0);

console.log(sum); // Output: 15

In the example above, the map method is used to multiply each element of the numbers array by 2. The filter method is used to filter out even numbers. The reduce method is used to calculate the sum of all numbers in the array.

SetTimeout and setInterval

 SetTimeout and setInterval


The setTimeout and setInterval functions are used to execute code after a certain delay or at fixed intervals, respectively.



setTimeout(() => {

  console.log('Delayed execution');

}, 2000);

// Output (after 2 seconds): Delayed execution


let counter = 0;

const intervalId = setInterval(() => {

  counter++;

  console.log(counter);

  if (counter === 5) {

    clearInterval(intervalId);

  }

}, 1000);

// Output (every 1 second): 1, 2, 3, 4, 5

In the example above, setTimeout is used to execute a function after a delay of 2000 milliseconds. setInterval is used to repeatedly execute a function every 1000 milliseconds until the condition counter === 5 is met. The interval is cleared using clearInterval when the condition is satisfied.

Generators and Iterators

 Generators and Iterators


Generators and iterators can work together to create custom iterable objects.



function* generateSequence(start, end) {

  for (let i = start; i <= end; i++) {

    yield i;

  }

}


const sequence = generateSequence(1, 5);


for (const value of sequence) {

  console.log(value);

}

// Output: 1, 2, 3, 4, 5

In the example above, the generateSequence function is a generator function that produces a sequence of numbers between start and end. The yield keyword is used to pause and resume the execution of the generator, allowing iteration over the generated values.

Web APIs

 Web APIs


JavaScript has access to various Web APIs that provide functionality for interacting with web browser features and APIs, such as DOM manipulation, AJAX requests, and more.


// Fetch API for making HTTP requests

fetch('https://api.example.com/data')

  .then(response => response.json())

  .then(data => console.log(data))

  .catch(error => console.error(error));


// DOM manipulation using Document API

const element = document.querySelector('#myElement');

element.textContent = 'Hello, World!';

In the above example, the fetch function is used to make an HTTP request to an API endpoint and retrieve data. The querySelector method from the Document API is used to select an element in the DOM and update its content.

Iterator and Iterable

 Iterator and Iterable


The iterator and iterable protocols provide a way to define and iterate over collections or sequences of data in JavaScript.



const myIterable = {

  [Symbol.iterator]() {

    let count = 0;

    return {

      next() {

        count++;

        if (count <= 5) {

          return { value: count, done: false };

        }

        return { done: true };

      }

    };

  }

};


for (const value of myIterable) {

  console.log(value);

}

// Output: 1, 2, 3, 4, 5

In the example above, the myIterable object implements the iterable protocol by defining a method with the key [Symbol.iterator]. The method returns an iterator object with the next method, which provides the next value in the sequence.

JavaScript Modules (CommonJS)

 JavaScript Modules (CommonJS)


CommonJS is a module system for JavaScript that allows you to organize code into reusable modules using the require and module.exports syntax.



// math.js

exports.add = function(a, b) {

  return a + b;

};


exports.subtract = function(a, b) {

  return a - b;

};


// main.js

const math = require('./math');


console.log(math.add(5, 3)); // Output: 8

console.log(math.subtract(10, 4)); // Output: 6

In the example above, the math.js file exports two functions (add and subtract) using the exports object. In the main.js file, the require function is used to import the math module, and you can access its exported functions.

Object Destructuring Assignment

 Object Destructuring Assignment


Object destructuring assignment allows you to extract values from objects and assign them to variables with a concise syntax.



const person = {

  name: 'John Doe',

  age: 30,

  address: {

    street: '123 Main St',

    city: 'New York'

  }

};


const { name, age, address: { city } } = person;


console.log(name, age, city);

// Output: John Doe 30 New York

In the example above, object destructuring is used to extract the name, age, and city properties from the person object. The address property is further destructured to extract the city property from the nested object.

Error Handling with Try-Catch:

 Error Handling with Try-Catch:


Error handling is an essential aspect of programming. JavaScript provides the try-catch statement to handle and manage exceptions.



function divide(a, b) {

  try {

    if (b === 0) {

      throw new Error('Division by zero is not allowed.');

    }

    return a / b;

  } catch (error) {

    console.error(error.message);

  }

}


console.log(divide(10, 0)); // Output: Division by zero is not allowed.

In the example above, the divide function attempts to perform a division operation. If the divisor (b) is zero, an Error object is thrown using the throw statement. The thrown error is then caught by the catch block, and the error message is logged to the console.

Object-oriented Programming (OOP) Concepts

 Object-oriented Programming (OOP) Concepts


JavaScript supports object-oriented programming concepts like classes, inheritance, and encapsulation.



class Person {

  constructor(name) {

    this.name = name;

  }


  sayHello() {

    console.log(`Hello, my name is ${this.name}.`);

  }

}


class Student extends Person {

  constructor(name, grade) {

    super(name);

    this.grade = grade;

  }


  sayHello() {

    super.sayHello();

    console.log(`I am in grade ${this.grade}.`);

  }

}


const john = new Student('John Doe', 8);

john.sayHello();

// Output:

// Hello, my name is John Doe.

// I am in grade 8.

In the example above, Person and Student are classes representing a person and a student, respectively. The Student class extends the Person class, inheriting its properties and methods. The sayHello() method is overridden in the Student class using the super keyword to call the parent class's implementation.

BigInt

 BigInt


BigInt is a numeric data type introduced in JavaScript to represent integers of arbitrary precision. It allows you to work with numbers beyond the limits of the regular Number type.



const largeNumber = BigInt(9007199254740991);

const largerNumber = largeNumber + BigInt(1);


console.log(largeNumber.toString()); // Output: 9007199254740991

console.log(largerNumber.toString()); // Output: 9007199254740992

In the example above, BigInt is used to create a large number. Operations can be performed on BigInt values using regular arithmetic operators. However, note that you need to use the toString() method to convert the BigInt value to a string for printing.

Spread Operator

 Spread Operator


The spread operator allows you to expand an iterable (such as an array or a string) into individual elements. It is denoted by three dots (...).



const numbers = [1, 2, 3];

const newArray = [...numbers, 4, 5];


console.log(newArray); // Output: [1, 2, 3, 4, 5]


const str = 'Hello';

const charArray = [...str];


console.log(charArray); // Output: ['H', 'e', 'l', 'l', 'o']

In the example above, the spread operator is used to expand the numbers array and the str string into individual elements. These elements are then used to create a new array (newArray) and a new array of characters (charArray), respectively.

Proxy

 Proxy


Proxies enable you to intercept and customize operations performed on objects, such as accessing properties, invoking methods, etc.



const person = {

  name: 'John Doe',

  age: 30

};


const personProxy = new Proxy(person, {

  get(target, property) {

    console.log(`Getting ${property}`);

    return target[property];

  },

  set(target, property, value) {

    console.log(`Setting ${property} to ${value}`);

    target[property] = value;

  }

});


console.log(personProxy.name); // Output: Getting name, John Doe

personProxy.age = 35; // Output: Setting age to 35

In the example above, a Proxy is created for the person object. The get and set traps intercept property access and assignment operations and allow you to add custom behavior or perform additional actions.

Sets and Maps

 Sets and Maps


Sets and Maps are built-in data structures introduced in JavaScript that provide efficient ways to store unique values and key-value pairs, respectively.



// Set example

const mySet = new Set();

mySet.add(1);

mySet.add(2);

mySet.add(3);


console.log(mySet.has(2)); // Output: true

console.log(mySet.size); // Output: 3


// Map example

const myMap = new Map();

myMap.set('name', 'John');

myMap.set('age', 30);


console.log(myMap.get('name')); // Output: John

console.log(myMap.size); // Output: 2

Sets store unique values, allowing you to add, check for existence, and retrieve elements efficiently. Maps store key-value pairs and provide methods to set, get, and delete values based on the keys.

Template Literals

 Template Literals


Template literals provide an improved way to work with strings in JavaScript. They allow you to embed expressions and multiline strings easily.


const name = 'John';

const age = 30;


console.log(`My name is ${name} and I am ${age} years old.`);

// Output: My name is John and I am 30 years old.


const multilineString = `

This is a multiline string.

It can span across multiple lines.

`;

console.log(multilineString);

/* Output:

This is a multiline string.

It can span across multiple lines.

*/

In the example above, the ${} syntax is used within the template literal to embed expressions (variables, calculations, etc.) within the string. Additionally, template literals can span multiple lines without needing to concatenate strings.

Optional Chaining

 Optional Chaining


Optional chaining allows you to access properties of an object without causing an error if intermediate properties are nullish or undefined.


const person = {

  name: 'John Doe',

  address: {

    street: '123 Main St',

    city: 'New York'

  }

};


console.log(person.address?.street); // Output: 123 Main St

console.log(person.address?.zipCode?.toString()); // Output: undefined

In the above example, the optional chaining operator ?. is used to access the street property of the address object. If the address object or the street property is undefined or null, it gracefully returns undefined without causing an error.

Generators

 Generators


Generators are functions that can be paused and resumed. They allow you to generate a series of values over time, which is useful for dealing with asynchronous operations or iterating over large datasets.



function* generateNumbers() {

  let i = 0;

  while (true) {

    yield i++;

  }

}


const numberGenerator = generateNumbers();

console.log(numberGenerator.next().value); // Output: 0

console.log(numberGenerator.next().value); // Output: 1

console.log(numberGenerator.next().value); // Output: 2

The generateNumbers generator function uses the yield keyword to produce a series of numbers. Each time you call numberGenerator.next(), it returns an object with the value property containing the next generated value.

Modules (ES Modules)

 Modules (ES Modules)


Modules allow you to organize your code into reusable and separate files. ES modules provide a standardized way of working with modules in JavaScript.



// math.js

export function add(a, b) {

  return a + b;

}


export function subtract(a, b) {

  return a - b;

}


// main.js

import { add, subtract } from './math.js';


console.log(add(5, 3)); // Output: 8

console.log(subtract(10, 4)); // Output: 6

In the example above, the math.js file exports two functions (add and subtract) using the export keyword. In the main.js file, you can import these functions using the import keyword and use them in your code.

Destructuring

 Destructuring


Destructuring allows you to extract values from objects or arrays into distinct variables.



const person = {

  name: 'John Doe',

  age: 30,

  city: 'New York'

};


const { name, age } = person;

console.log(name, age); // Output: John Doe 30


const numbers = [1, 2, 3, 4, 5];

const [first, second, ...rest] = numbers;

console.log(first, second, rest); // Output: 1 2 [3, 4, 5]

In the example above, object destructuring is used to extract the name and age properties from the person object. Array destructuring is used to assign the first two elements of the numbers array to first and second, while the rest of the elements are captured in the rest array.

Arrow Functions

 Arrow Functions


Arrow functions provide a concise syntax for writing function expressions in JavaScript.



const multiply = (a, b) => a * b;

console.log(multiply(5, 3)); // Output: 15


const greet = name => {

  console.log(`Hello, ${name}!`);

};

greet('John'); // Output: Hello, John!

The arrow function syntax (parameters) => { ... } allows for a more compact function declaration. If the function body consists of a single expression, you can omit the curly braces and the return statement.

Promises in JS

 Promises in JS


Promises are objects used to handle asynchronous operations in JavaScript. They represent a value that may be available now, in the future, or never.



function fetchData() {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

      resolve('Data fetched successfully!');

    }, 2000);

  });

}


fetchData()

  .then(result => {

    console.log(result);

  })

  .catch(error => {

    console.error(error);

  });

In the above example, fetchData returns a promise that resolves after 2 seconds. You can use the .then() method to handle the resolved value and the .catch() method to handle errors.

Asynchronous JavaScript (Async/Await)

 Asynchronous JavaScript (Async/Await)


Async/await is a way to write asynchronous code in a more synchronous manner, making it easier to handle promises. It allows you to write code that appears to be synchronous, but actually runs asynchronously.



function fetchData() {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

      resolve('Data fetched successfully!');

    }, 2000);

  });

}


async function getData() {

  try {

    const result = await fetchData();

    console.log(result);

  } catch (error) {

    console.error(error);

  }

}


getData();

In the example above, fetchData simulates an asynchronous operation that resolves after 2 seconds. The getData function uses the await keyword to pause execution until the promise returned by fetchData resolves.

Records with Local Enumerated Types (Java 17)

 Records with Local Enumerated Types (Java 17)


Starting from Java 17, records can include local enumerated types, providing more flexibility and convenience when working with records. This allows you to define an enumerated type directly within a record's body. Here's an example:


record Person(String name, int age, Gender gender) {

    enum Gender {

        MALE, FEMALE, OTHER

    }

}

In this example, the Person record includes a local enumerated type Gender within its body. The Gender enum defines three constants: MALE, FEMALE, and OTHER. The Person record can then use the Gender enum as the type for the gender property.


Using local enumerated types within records helps encapsulate and organize related types within the scope of the record itself. It provides a convenient way to define and use enums that are specific to the record's purpose.


Exploring records with local enumerated types can improve the organization and readability of your code, especially when working with records that require specialized enumerated types.

Sealed Classes and Sealed Interfaces Enhancements (Java 17)

 Sealed Classes and Sealed Interfaces Enhancements (Java 17)


Java 17 introduced enhancements to sealed classes and sealed interfaces, providing more control and flexibility in defining the inheritance hierarchy. These enhancements include the ability to:

Declare a permitted subclass or subinterface as non-sealed, allowing it to be further extended by other classes or interfaces.

Declare a permitted subclass or subinterface as final, preventing further subclassing or subinterface extension.

Declare a sealed class or sealed interface in a different package than its permitted subclasses or subinterfaces.

These enhancements allow for more fine-grained control over the inheritance hierarchy, promoting better encapsulation and extensibility.


Here's an example:



// In package com.example

public sealed class Shape permits Circle, Rectangle {

    // Common shape properties and methods

}


public final class Circle extends Shape {

    // Circle-specific properties and methods

}


// In package com.example.shapes

public non-sealed class Square extends Shape {

    // Square-specific properties and methods

}


// In package com.example.shapes.triangle

public final class EquilateralTriangle extends Shape {

    // EquilateralTriangle-specific properties and methods

}

In this example, the Shape class is sealed and permits subclasses Circle and Rectangle. The Square class, located in a different package, is declared as non-sealed, allowing further subclasses. The EquilateralTriangle class is a final subclass of Shape, demonstrating that a permitted subclass can be declared as final.

Pattern Matching for Null (Preview Feature in Java 14-17, Standard Feature in Java 18)

 Pattern Matching for Null (Preview Feature in Java 14-17, Standard Feature in Java 18)


Pattern matching for null allows for more concise and expressive code when checking for null values. It simplifies null checks and can help reduce NullPointerExceptions. Here's an example:


String text = null;


// Prior to Java 14

if (text == null) {

    System.out.println("Text is null");

} else {

    System.out.println("Text is not null: " + text);

}


// Java 14 and later

if (text instanceof String str) {

    System.out.println("Text is not null: " + str);

} else {

    System.out.println("Text is null");

}

In the Java 14 and later version, the pattern instanceof String str checks if text is an instance of String and simultaneously assigns it to the variable str. This allows you to directly use the variable str within the conditional block, avoiding the need for a separate null check.


It's important to note that pattern matching for null is a preview feature in Java 14-17. Starting from Java 18, it has become a standard feature.


Exploring this feature can lead to cleaner and more readable code, particularly when dealing with nullable values.

JEPs (Java Enhancement Proposals)

 JEPs (Java Enhancement Proposals)


JEPs are proposals for new features and enhancements to the Java platform. They cover a wide range of topics and can introduce significant changes. You can explore the list of JEPs to discover additional new Java programming topics. Here's an example of a JEP related to records:


record Point(int x, int y) {}


Point p = new Point(10, 20);

System.out.println(p.x()); // 10

System.out.println(p.y()); // 20

Vector API Enhancements (introduced in Java 17)

 Vector API Enhancements (introduced in Java 17)


Java 17 introduced enhancements to the Vector API, enabling improved performance for vectorized computations. It includes additional methods and optimizations. Here's an example:


import java.util.Arrays;

import java.util.stream.*;


float[] array1 = new float[]{1.0f, 2.0f, 3.0f};

float[] array2 = new float[]{4.0f, 5.0f, 6.0f};


VectorFloat vector1 = VectorFloat.fromArray(array1, 0);

VectorFloat vector2 = VectorFloat.fromArray(array2, 0);

VectorFloat sum = vector1.add(vector2);

float[] result = sum.intoArray();


System.out.println(Arrays.toString(result)); // [5.0, 7.0, 9.0]

Java Time API (introduced in Java 8)

 Java Time API (introduced in Java 8)


The Java Time API provides a comprehensive set of classes for date and time manipulation. It offers improved flexibility, thread-safety, and functionality compared to the older java.util.Date and java.util.Calendar classes. Here's an example:


LocalDate today = LocalDate.now();

LocalDate tomorrow = today.plusDays(1);

LocalDateTime currentDateTime = LocalDateTime.now();


System.out.println(today); // 2023-05-22

System.out.println(tomorrow); // 2023-05-23

System.out.println(currentDateTime); // 2023-05-22T14:30:00

These are a few more new Java programming topics that you can explore. Each topic introduces powerful features and improvements to enhance your Java programming experience.

Sealed Interfaces (introduced in Java 17)

 Sealed Interfaces (introduced in Java 17)


Sealed interfaces extend the concept of sealed classes to interfaces. They provide control over which classes can implement the interface, promoting better encapsulation and API design. Here's an example:


public sealed interface Shape permits Circle, Rectangle {

    void draw();

}


public final class Circle implements Shape {

    public void draw() {

        System.out.println("Drawing a circle.");

    }

}


public final class Rectangle implements Shape {

    public void draw() {

        System.out.println("Drawing a rectangle.");

    }

}

Pattern Matching for Switch (introduced in Java 14)

 Pattern Matching for Switch (introduced in Java 14)


Pattern matching for switch simplifies the common use case of performing different actions based on the value of a variable. It eliminates the need for repetitive casting and enhances code readability. Here's an example:


String day = "Sunday";


switch (day) {

    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" ->

        System.out.println("It's a weekday.");

    case "Saturday", "Sunday" ->

        System.out.println("It's a weekend day.");

    default ->

        System.out.println("Invalid day.");

}

Records with Local Variable Type Inference (introduced in Java 16)

 Records with Local Variable Type Inference (introduced in Java 16)


Java 16 introduced the combination of records and local variable type inference, allowing you to declare record instances with inferred types. Here's an example:


record Person(String name, int age) {}


var person = new Person("John Doe", 30);

System.out.println(person.name()); // John Doe

System.out.println(person.age()); // 30

Foreign Function & Memory API (introduced in Java 16)

 Foreign Function & Memory API (introduced in Java 16)


The Foreign Function & Memory API provides a way to interact with native code and manage off-heap memory. It enables Java programs to call functions from native libraries and work with direct memory allocations. Here's a simplified example:


import jdk.incubator.foreign.*;


try (MemorySegment segment = MemorySegment.allocateNative(1024)) {

    segment.asByteBuffer().putInt(42); // Writing to native memory


    LibraryLookup lookup = LibraryLookup.ofDefault();

    FunctionDescriptor descriptor = FunctionDescriptor.of(CLinker.C_INT, CLinker.C_POINTER);

    MethodHandle function = lookup.lookup("myFunction", descriptor);

    int result = (int) function.invokeExact(segment.address()); // Calling native function

}


Vector API (introduced in Java 16)

 Vector API (introduced in Java 16)


The Vector API provides a set of classes and methods for efficient vectorized computations on arrays of numeric data. It enables better utilization of SIMD (Single Instruction, Multiple Data) instructions for improved performance. Here's a simple example:


import java.util.Arrays;

import java.util.stream.*;


float[] array1 = new float[]{1.0f, 2.0f, 3.0f};

float[] array2 = new float[]{4.0f, 5.0f, 6.0f};


VectorFloat vector1 = VectorFloat.fromArray(array1, 0);

VectorFloat vector2 = VectorFloat.fromArray(array2, 0);

VectorFloat sum = vector1.add(vector2);

float[] result = sum.intoArray();


System.out.println(Arrays.toString(result)); // [5.0, 7.0, 9.0]

Text Blocks (introduced in Java 15)

 Text Blocks (introduced in Java 15)


Text blocks simplify the creation and formatting of multiline strings. They allow you to write strings with line breaks and indentation without the need for escape sequences or concatenation. Here's an example:


String html = """

    <html>

        <body>

            <h1>Welcome to my website!</h1>

            <p>This is a paragraph of text.</p>

        </body>

    </html>

    """;

Sealed Classes (introduced in Java 15)

 Sealed Classes (introduced in Java 15)


Sealed classes restrict the inheritance hierarchy of a class, allowing you to specify which classes can extend it. This promotes better control and helps prevent unintended subclassing. Here's an example:


public sealed class Shape permits Circle, Rectangle {

    // Common shape properties and methods

}


public final class Circle extends Shape {

    // Circle-specific properties and methods

}


public final class Rectangle extends Shape {

    // Rectangle-specific properties and methods

}

Reactive Programming with Java Streams

 Reactive Programming with Java Streams


Java Streams provide a powerful way to process collections of data. Reactive programming takes it a step further by allowing you to build asynchronous and non-blocking applications. Here's an example using the Reactor library:


Flux<Integer> numbers = Flux.just(1, 2, 3, 4, 5);

numbers

    .map(n -> n * 2)

    .filter(n -> n > 5)

    .subscribe(System.out::println);

Records (introduced in Java 14)

 Records (introduced in Java 14)


Records are a compact way to declare classes that are primarily used to store data. They provide automatic implementations of equals(), hashCode(), toString(), and more. Here's an example:


record Person(String name, int age) {

    // Implicitly generated constructors, accessors, equals, hashCode, toString

}


Person person = new Person("John Doe", 30);

System.out.println(person.name()); // John Doe

System.out.println(person.age()); // 30

Pattern Matching for instanceof (introduced in Java 16)

 Pattern Matching for instanceof (introduced in Java 16)


Pattern matching simplifies code that checks types and performs casts. It allows you to combine type checking and type casting into a single operation. Here's an example:


if (obj instanceof String str) {

    System.out.println(str.length()); // Safe to access str as a String

} else {

    System.out.println("Not a string.");

}

Local Variable Type Inference (introduced in Java 10)

 Local Variable Type Inference (introduced in Java 10)


Local variable type inference enables you to declare variables without explicitly specifying their types. Instead, the compiler infers the type based on the assigned value. Here's an example:


var message = "Hello, World!"; // inferred as String

var numbers = List.of(1, 2, 3, 4, 5); // inferred as List<Integer>

Java Modules (introduced in Java 9)

 Java Modules (introduced in Java 9)


Java Modules allow you to define explicit boundaries and dependencies between different parts of your codebase. They promote modularity and encapsulation, making it easier to manage large-scale applications. Here's an example:


// module-info.java

module com.example.myapp {

    requires javafx.controls;

    exports com.example.myapp.ui;

}

CSS Background Gradients

 CSS Background Gradients


CSS gradients allow you to create smooth transitions between two or more colors as backgrounds. Here's an example of a linear gradient:


.box {

  background: linear-gradient(to right, #ff0000, #00ff00);

}

CSS Transforms

 CSS Transforms


CSS transforms allow you to manipulate the position, size, and orientation of elements. Here are some commonly used transform properties:

translate(): Moves an element along the X and Y axes. For example:



.box {

  transform: translate(50px, 20px);

}

scale(): Changes the size of an element. For example:



.box {

  transform: scale(1.2);

}

rotate(): Rotates an element clockwise or counterclockwise. For example:



.box {

  transform: rotate(45deg);

}

CSS Responsive Design

 CSS Responsive Design


CSS provides techniques to create responsive layouts that adapt to different screen sizes. Here's an example using media queries:


@media screen and (max-width: 768px) {

  .container {

    flex-direction: column;

  }

}


@media screen and (max-width: 480px) {

  .item {

    width: 100%;

  }

}

CSS Animations

 CSS Animations


CSS animations allow you to add dynamic and interactive effects to elements. Here's an example of a simple animation:


@keyframes slide-in {

  0% {

    transform: translateX(-100%);

  }

  100% {

    transform: translateX(0);

  }

}


.box {

  animation: slide-in 1s forwards;

}

CSS Selectors

 CSS Selectors


CSS selectors allow you to target specific elements to apply styles. Here are some commonly used selectors:

Class Selector: Targets elements with a specific class. For example:



.my-class {

  color: red;

}

ID Selector: Targets a specific element with a unique ID. For example:



#my-id {

  font-weight: bold;

}

Descendant Selector: Targets elements that are descendants of another element. For example:



.parent-element .child-element {

  background-color: blue;

}

CSS Background Images

 CSS Background Images


CSS allows you to set background images for elements. Here's an example:


.container {

  background-image: url('image.jpg');

  background-size: cover;

  background-position: center;

}

CSS Media Queries

 CSS Media Queries


CSS media queries allow you to apply different styles based on the characteristics of the device or browser. Here's an example of a media query for mobile devices:


@media screen and (max-width: 480px) {

  .container {

    flex-direction: column;

  }

}

CSS Multiple Columns

 CSS Multiple Columns


CSS provides a way to create multiple columns of content within an element. Here's an example:


.container {

  columns: 2;

  column-gap: 20px;

}

CSS Grid Areas

 CSS Grid Areas


CSS Grid allows you to create grid-based layouts by defining areas within the grid. Here's an example:


.container {

  display: grid;

  grid-template-areas:

    "header header"

    "sidebar content"

    "footer footer";

  grid-template-rows: auto 1fr auto;

  grid-template-columns: 200px 1fr;

  grid-gap: 10px;

}


.header {

  grid-area: header;

}


.sidebar {

  grid-area: sidebar;

}


.content {

  grid-area: content;

}


.footer {

  grid-area: footer;

}

CSS Columns

 CSS Columns


CSS columns allow you to create multi-column layouts for text content. Here's an example:


.container {

  columns: 2;

  column-gap: 20px;

}

CSS Transitions with Multiple Properties

 CSS Transitions with Multiple Properties


CSS transitions allow you to smoothly animate changes to CSS properties over a specified duration. You can apply transitions to multiple properties simultaneously. Here's an example:


.box {

  width: 100px;

  height: 100px;

  background-color: red;

  transition: width 0.3s ease, height 0.3s ease, background-color 0.3s ease;

}


.box:hover {

  width: 200px;

  height: 200px;

  background-color: blue;

}


CSS Box Model

 CSS Box Model


The CSS box model describes the layout and sizing of elements on a web page. It consists of the content area, padding, border, and margin. You can control these properties individually. Here's an example:


.box {

  width: 200px;

  height: 100px;

  padding: 20px;

  border: 1px solid black;

  margin: 10px;

}

CSS Flexbox Ordering

 CSS Flexbox Ordering


CSS Flexbox allows you to control the order of flex items. By default, flex items are displayed in the order they appear in the HTML source. However, you can change their order using the order property. Here's an example:


.item:nth-child(2) {

  order: 1;

}

In this example, the second flex item will be displayed first.

CSS Gradients

 CSS Gradients


CSS gradients allow you to create smooth transitions between two or more colors. Here's an example of using a linear gradient:


.box {

  background: linear-gradient(to right, #ff0000, #00ff00);

}

You can also use radial gradients to create circular gradients.

CSS Flexbox Alignment

 CSS Flexbox Alignment


CSS Flexbox provides properties to control the alignment of flex items within a container. Some commonly used alignment properties include:

align-items: Specifies how flex items are aligned vertically within the flex container. For example:



.container {

  display: flex;

  align-items: center;

}

justify-content: Defines how flex items are aligned horizontally within the flex container. For example:



.container {

  display: flex;

  justify-content: space-between;

}

align-self: Overrides the alignment of an individual flex item. For example:



.item {

  align-self: flex-end;

}

CSS Custom Animations with keyframes

 CSS Custom Animations with keyframes


CSS keyframes allow you to create custom animations with full control over each step. Here's an example:


@keyframes pulse {

  0% {

    transform: scale(1);

  }

  50% {

    transform: scale(1.2);

  }

  100% {

    transform: scale(1);

  }

}


.box {

  animation: pulse 2s infinite;

}

CSS Filters

 CSS Filters


CSS filters allow you to apply visual effects to elements. Some commonly used filters include:

Grayscale: Converts an element to grayscale. For example:



.image {

  filter: grayscale(100%);

}

Blur: Applies a blur effect to an element. For example:



.background {

  filter: blur(5px);

}

Brightness: Adjusts the brightness of an element. For example:



.video {

  filter: brightness(150%);

}

CSS Flexibility

 CSS Flexibility


CSS provides properties to control the flexibility of elements within a flex container. Some commonly used properties include:

Flex-grow: Specifies the ability of an element to grow to fill available space. For example:



.item {

  flex-grow: 1;

}

Flex-shrink: Specifies the ability of an element to shrink if necessary. For example:



.item {

  flex-shrink: 0;

}

Flex-basis: Sets the initial size of an element before any available space is distributed. For example:



.item {

  flex-basis: 200px;

}

CSS Positioning

 CSS Positioning


CSS positioning allows you to precisely control the placement of elements on a web page. Some commonly used positioning techniques include:

Relative Positioning: Positions an element relative to its normal position. For example:



.box {

  position: relative;

  top: 20px;

  left: 50px;

}

Absolute Positioning: Positions an element relative to its closest positioned ancestor. For example:



.box {

  position: absolute;

  top: 0;

  right: 0;

}

Fixed Positioning: Positions an element relative to the viewport. For example:



.box {

  position: fixed;

  top: 20px;

  right: 20px;

}

CSS Transforms and Transitions

 CSS Transforms and Transitions


CSS transforms and transitions allow you to apply smooth and animated transformations to elements. Here's an example:



.box {

  width: 100px;

  height: 100px;

  background-color: blue;

  transition: transform 0.3s ease;

}


.box:hover {

  transform: rotate(45deg);

}

CSS Variables

 CSS Variables


CSS variables allow you to define reusable values that can be used throughout your CSS code. Here's an example of using CSS variables:



:root {

  --primary-color: #007bff;

  --secondary-color: #6c757d;

}


.header {

  background-color: var(--primary-color);

  color: var(--secondary-color);

}

CSS Pseudo-classes and Pseudo-elements

 CSS Pseudo-classes and Pseudo-elements


CSS pseudo-classes and pseudo-elements allow you to select and style specific parts of elements. Some commonly used ones include:


:hover: Styles an element when the mouse is over it. For example:



a:hover {

  color: red;

}

:nth-child(): Selects elements based on their position within a parent. For example:



li:nth-child(even) {

  background-color: #f2f2f2;

}

::before: Inserts content before the content of an element. For example:



.tooltip::before {

  content: "Tooltip";

}

CSS Grid

 CSS Grid


CSS Grid allows for a two-dimensional grid-based layout system. Some commonly used properties include:


Grid-template-columns: Defines the number and size of columns in the grid. For example:



.container {

  display: grid;

  grid-template-columns: 1fr 1fr 1fr;

}

Grid-template-rows: Defines the number and size of rows in the grid. For example:



.container {

  display: grid;

  grid-template-rows: 100px 200px;

}

Grid-gap: Sets the gap between grid cells. For example:



.container {

  display: grid;

  grid-gap: 10px;

}

CSS Flexbox

 CSS Flexbox


Flexbox is a powerful layout module that provides a flexible way to arrange elements in a container. Some commonly used properties include:


Flex-direction: Specifies the direction of the flex container. For example:



.container {

  display: flex;

  flex-direction: row;

}

Justify-content: Defines how flex items are aligned along the main axis. For example:



.container {

  display: flex;

  justify-content: center;

}

Align-items: Specifies how flex items are aligned along the cross axis. For example:



.container {

  display: flex;

  align-items: center;

}

CSS Backgrounds

 CSS Backgrounds


CSS provides properties to control the background of elements. Some commonly used properties include:


Background-color: Sets the background color of an element. For example:



body {

  background-color: #f2f2f2;

}

Background-image: Specifies an image to be used as the background. For example:



.header {

  background-image: url("header-bg.jpg");

  background-repeat: no-repeat;

  background-size: cover;

}

CSS Typography

 CSS Typography


CSS provides various properties to style text and control its appearance. Some commonly used properties include:


Font-family: Specifies the font to be used for text. For example:



body {

  font-family: Arial, sans-serif;

}

Font-size: Sets the size of the font. For example:



h1 {

  font-size: 24px;

}

Color: Defines the color of the text. For example:



p {

  color: #333333;

}

CSS Selectors

 CSS Selectors


CSS selectors determine which elements on a web page should be styled. Some common selectors include:


Element Selector: Targets elements by their tag name. For example:



p {

  color: blue;

}

Class Selector: Targets elements with a specific class attribute. For example:



.highlight {

  background-color: yellow;

}

ID Selector: Targets a specific element with a unique ID attribute. For example:



#logo {

  width: 200px;

}

HTML5 WebSockets

 HTML5 WebSockets


WebSockets enable bidirectional communication between a client and a server over a single, long-lived connection. It allows real-time data exchange. Here's an example:


<script>

  const socket = new WebSocket("ws://example.com/socket");


  socket.onopen = function() {

    console.log("WebSocket connection established.");

    socket.send("Hello, server!");

  };


  socket.onmessage = function(event) {

    console.log("Received message from server:", event.data);

  };


  socket.onclose = function() {

    console.log("WebSocket connection closed.");

  };

</script>

HTML5 Offline Web Applications

 HTML5 Offline Web Applications


HTML5 provides a feature called offline web applications, which allows web pages to be accessed and used even when the user is offline. This is achieved using the Application Cache API. Here's an example:


<!DOCTYPE html>

<html manifest="myapp.appcache">

<head>

  <title>Offline Web Application</title>

</head>

<body>

  <h1>Welcome to my offline app!</h1>

  <p>This page will be accessible even when you are offline.</p>

</body>

</html>

Create a file named myapp.appcache and include the following content:



CACHE MANIFEST

# Version 1.0


CACHE:

index.html

styles.css

script.js

image.jpg


NETWORK:

*


FALLBACK:

/ fallback.html

HTML Semantic Elements

 HTML Semantic Elements


HTML5 introduced semantic elements that provide a clearer structure and meaning to the content of a web page. Here are some commonly used semantic elements:


<header>

  <h1>Website Title</h1>

  <nav>

    <ul>

      <li><a href="#">Home</a></li>

      <li><a href="#">About</a></li>

      <li><a href="#">Services</a></li>

      <li><a href="#">Contact</a></li>

    </ul>

  </nav>

</header>


<main>

  <article>

    <h2>Article Title</h2>

    <p>Article content goes here...</p>

  </article>

</main>


<footer>

  <p>&copy; 2023 My Website. All rights reserved.</p>

</footer>

HTML Accessibility

 HTML Accessibility


HTML provides various features and attributes to improve web accessibility for users with disabilities. Here are a few accessibility-related attributes:


<button id="myButton" disabled>Disabled Button</button>


<input type="text" id="myInput" aria-label="Username" required>


<img src="image.jpg" alt="Description of the image" tabindex="0">

HTML Forms

 HTML Forms


Forms allow users to input data and submit it to a server for processing. HTML provides various form elements and attributes for creating interactive forms. Here's an example:


<form>

  <label for="name">Name:</label>

  <input type="text" id="name" name="name" required>


  <label for="email">Email:</label>

  <input type="email" id="email" name="email" required>


  <label for="message">Message:</label>

  <textarea id="message" name="message" required></textarea>


  <input type="submit" value="Submit">

</form>

HTML Drag and Drop with Data Transfer

 HTML Drag and Drop with Data Transfer


The HTML5 Drag and Drop API also allows you to transfer custom data between draggable and drop zones using the dataTransfer object. Here's an example:


<div id="dragElement" draggable="true" ondragstart="dragStart(event)">Drag me</div>

<div id="dropZone" ondrop="drop(event)" ondragover="allowDrop(event)">Drop here</div>


<script>

  function dragStart(event) {

    event.dataTransfer.setData("text/plain", event.target.id);

    event.dataTransfer.effectAllowed = "move";

  }


  function allowDrop(event) {

    event.preventDefault();

  }


  function drop(event) {

    event.preventDefault();

    const data = event.dataTransfer.getData("text/plain");

    const draggedElement = document.getElementById(data);

    event.target.appendChild(draggedElement);

  }

</script>

HTML Audio and Video APIs

 HTML Audio and Video APIs


HTML provides JavaScript APIs to control and manipulate audio and video elements. You can use these APIs to dynamically control playback, adjust volume, and respond to various events. Here's an example:


<audio id="myAudio" src="audio.mp3"></audio>


<button onclick="playAudio()">Play</button>

<button onclick="pauseAudio()">Pause</button>

<button onclick="stopAudio()">Stop</button>


<script>

  const audio = document.getElementById("myAudio");


  function playAudio() {

    audio.play();

  }


HTML Templates

 HTML Templates


HTML templates allow you to define reusable sections of HTML that can be dynamically inserted into the document. Here's an example:


<template id="myTemplate">

  <h2>Template Content</h2>

  <p>This is some content inside the template.</p>

</template>


<button onclick="insertTemplate()">Insert Template</button>


<script>

  function insertTemplate() {

    const template = document.getElementById("myTemplate");

    const content = template.content.cloneNode(true);

    document.body.appendChild(content);

  }

</script>

HTML5 Geolocation API

 HTML5 Geolocation API


The Geolocation API allows you to retrieve the user's current geographical location using JavaScript. The user's permission is required to access this information. Here's an example:


<button onclick="getLocation()">Get Location</button>


<script>

  function getLocation() {

    if (navigator.geolocation) {

      navigator.geolocation.getCurrentPosition(showPosition);

    } else {

      alert("Geolocation is not supported by this browser.");

    }

  }


  function showPosition(position) {

    const latitude = position.coords.latitude;

    const longitude = position.coords.longitude;

    alert(`Latitude: ${latitude}, Longitude: ${longitude}`);

  }

</script>

Custom Data Attributes

 Custom Data Attributes


HTML allows you to define custom data attributes on HTML elements using the data- prefix. These attributes can store additional information that can be accessed using JavaScript. Here's an example:


<div id="myElement" data-color="blue" data-size="large"></div>


<script>

  const element = document.getElementById("myElement");

  const color = element.dataset.color;

  const size = element.dataset.size;


  console.log(color); // Output: blue

  console.log(size); // Output: large

</script>

HTML Tables

 HTML Tables


Tables are used to display tabular data in HTML. You can define tables using the <table> element and its related tags such as <tr> for table rows, <th> for table headers, and <td> for table cells. Here's an example:


<table>

  <tr>

    <th>Name</th>

    <th>Age</th>

    <th>Country</th>

  </tr>

  <tr>

    <td>John</td>

    <td>25</td>

    <td>USA</td>

  </tr>

  <tr>

    <td>Jane</td>

    <td>30</td>

    <td>UK</td>

  </tr>

</table>

HTML5 Web Storage

 HTML5 Web Storage


Web storage allows you to store data in the browser's storage for persistence between sessions. There are two types of web storage available: localStorage and sessionStorage. Here's an example of using localStorage:


<script>

  // Storing data in localStorage

  localStorage.setItem("username", "John");


  // Retrieving data from localStorage

  const username = localStorage.getItem("username");

  console.log(username); // Output: John


  // Removing data from localStorage

  localStorage.removeItem("username");

</script>

The Importance of Cybersecurity in the Digital Age

 The Importance of Cybersecurity in the Digital Age Introduction: In today's digital age, where technology is deeply intertwined with ev...