Object Oriented Programming in Java

Object Oriented Programming in Java

Part 2

Packages in Java:

  1. Definition:

    • Packages are containers for classes, used to keep the class namespace compartmentalized and organized.

    • They prevent naming collisions, allowing you to create classes with common names without conflicts.

    • No two same classes are allowed in a single package.

  2. Naming and Visibility:

    • Packages serve as both a naming and visibility control mechanism.

    • They allow you to organize classes hierarchically and control their visibility.

  3. Declaration:

    • Packages are declared using the package keyword followed by the package name.

    • Example: package MyPackage;

  4. File System Storage:

    • Java uses file system directories to store packages, where the directory structure reflects the package hierarchy.

    • The directory name must match the package name exactly, and case sensitivity must be observed.

    •     // For example that you had created a folder 'com'
          // Inside that folder you had created 'leetcode' folder
          // Inside the leetcode folder you are creating java files for solving each leetcode problems
          // So the package name will be like "com.leetcode"
          // this dot acts as a inside folder
      
          // com --> leetcode
          // Similarly create a codechef folder in com
          // So it will look like this "com.codechef"
          // this com.codechef is package
          // Representation: package com.codechef
      
  5. Specifying Package Location:

    • The Java runtime system looks for packages based on three factors:

      • By default, it uses the current working directory as the starting point.

      • You can set the CLASSPATH environmental variable to specify directory paths.

      •     # Linux/Mac
            export CLASSPATH=/path/to/directory:/path/to/jarfile.jar
        
            # Windows
            set CLASSPATH=C:\path\to\directory;C:\path\to\jarfile.jar
        
      • You can use the -classpath option with java and javac commands to specify class paths.

      •     # To compile Java source files with specific class paths
            javac -classpath /path/to/directory:/path/to/jarfile.jar YourJavaFile.java
        
            # To run Java program with specific class paths
            java -classpath /path/to/directory:/path/to/jarfile.jar YourMainClass
        
  6. Importing Packages:

    • When a package is imported, only those items declared as public within the package are available to non-subclasses in the importing code.

Import keyword:

  • The import keyword is used to import classes, interfaces, and other packages into the current compilation unit (file).

  • It allows you to use classes from other packages without fully qualifying their names.

  •     // Individual classes/interfaces: 
        import package_name.ClassName;
        // Entire package: 
        import package_name.*;
    
  • The import statement is typically placed at the beginning of a Java source file, before any class or interface definitions.

  •     import java.util.ArrayList;
        import java.util.*; // Importing all classes/interfaces from the java.util package
    
        // You can also import static members
        // import static package_name.ClassName.staticField;
        import static java.lang.Math.PI;
    

Understanding static:

When working with object-oriented programming languages like Java, we often encounter scenarios where certain attributes or behaviors of a class are shared among all instances of that class. However, traditional instance variables and methods are tied to individual objects, leading to redundancy and inefficiency when dealing with shared data.

Introducing the 'static' Keyword:

In Java, the static keyword provides a solution to this problem by allowing us to declare members (fields and methods) that belong to the class itself rather than to instances of the class. This means that static members are shared among all instances of the class and can be accessed without the need for an object reference.

Practical Example: Human Class

Let's consider a scenario where we have a Human class to represent individuals. Each Human object may have different characteristics such as age, name, salary, and marital status. However, there's one attribute that remains constant across all humans – the population count.

Implementation:

public class Human {
    int age;
    String name;
    int salary;
    boolean married;
    static long population; // static variable to track population

    // Constructor to initialize instance variables and update population
    public Human(int age, String name, int salary, boolean married){
        this.age = age;
        this.name = name;
        this.salary = salary;
        this.married = married;
        Human.population += 1; // Increment population count for each new Human object
    }

}

public static void main(String[] args) {
    Human abhay = new Human(21,"Abhay",30000,false);
    Human sandeep = new Human(21,"Sandeep",30000,false);
    // if we dont make static then the values will be same for each object
    // for example the count is 1 when you create an object and. Same when you create
   // a new object the values will be 1 only because it runs from the start for a
    // an new object but to static it does not depend upon object
    System.out.println(abhay.population); //2
    System.out.println(sandeep.population); //2
    System.out.println(Human.population); //2 // this is correct

    // By convention, we have to use ClassName 'Human' instead of this because
    // static variables belongs to class rather than Object
}

When a member is declared static, it can be accessed before any objects of its class are created, and without reference to any object. You can declare both methods and variables to be static.

The most common example of a static member is the main( ). main( ) is declared as static because it must be called before any objects exist. Static method in Java is a method that belongs to the class and not to the object.

public static void main(String[] args) {
    // main()
}

Static Member Inside Non-Static Context:

When a static member is defined inside a non-static context, such as within a non-static method or instance initializer block, it behaves differently compared to when it's defined at the class level. This scenario often arises when we need to access a static member within an instance-specific context.

Example:

public class Random {
    public static void main(String[] args) {
        greet();
    }

    // this is static method
    public static void greeting(){
        System.out.println("Hello AbhayGRT");
    }


    // this is non-static method
    public void greet(){
        // here we are calling a static method
        greeting();
        // it will run because static object does not require any object to run
        // Hence static method can be run inside non-static method
        System.out.println("Hello The great one");
    }

}

Non-Static Member Inside Static Context:

Context:

A non-static member, such as an instance variable or method, cannot be directly accessed from a static context like a static method or static initializer block. However, we can access non-static members from a static context by explicitly providing an object reference.

Example:

package SecondPart;

public class Random {
    public static void main(String[] args) {
        greeting();
    }

    // this is static method
    public static void greeting(){
        // Here we are calling a non-static method inside a static method
//        greet(); // it will give the error saying "Non-static method 'greet()' cannot be referenced from a static context"
        // Because non static member require an object to run but static does not require an object to run

        // But it can run if we provide the object of the given class here the class name is Random
        Random obj = new Random();
        obj.greet();
        System.out.println("Hello AbhayGRT");
    }


    // this is non-static method
    public void greet(){
        // here we are calling a static method
        greeting();
        // it will run because static object does not require any object to run
        // Hence static method can be run inside non-static method
        System.out.println("Hello The great one");
    }

}

Can You Call an Instance Variable Inside a Static Method?

Example:

javaCopy codepublic class Random {
    int age;
    String name;
    int salary;
    boolean married;
    static long population;

    // Static method attempting to access instance variable 'name'
    static void message() {
        System.out.println("Hello");
        System.out.println(this.name); // Compilation error: Cannot use 'this' in static context
    }

    public Random(int age, String name, int salary, boolean married) {
        this.age = age;
        this.name = name;
        this.salary = salary;
        this.married = married;
        Random.population += 1;
    }

    public static void main(String[] args) {
        Random abhay = new Random(21, "Abhay", 30000, false);
    }
}

Fix:

To resolve this issue, you can follow one of these approaches:

  1. Make the Static Method Non-Static:
void message() {
    System.out.println("Hello");
    System.out.println(this.name); // Accessing instance variable using 'this'
}
  1. Pass an Instance to the Static Method:
static void message(Random obj) {
    System.out.println("Hello");
    System.out.println(obj.name); // Accessing instance variable through the passed object
}

By implementing one of these solutions, you'll be able to access instance variables within a static method effectively.

Static Block:

Static blocks are primarily used for initialization tasks and are executed only once when the class is loaded into memory. Variables declared inside a static block are typically static variables, and their values can be changed after initialization like any other static variable.

public class StaticBlock {
    static int a = 5;
    static int b;

    static {
        System.out.println("I am static block");
        b =a * 4;
    }

    public static void main(String[] args) {
        StaticBlock a = new StaticBlock();
        System.out.println(StaticBlock.a + " " + StaticBlock.b);

        StaticBlock.b += 5;
        System.out.println(StaticBlock.a + " " + StaticBlock.b);

        StaticBlock b = new StaticBlock();
        System.out.println(StaticBlock.a + " " + StaticBlock.b);    }
}
// Output
I am static block
5 20
5 25
5 25

Inner Classes:

In Java, if there are two classes, and one class is nested inside the other, then only the inner class can have static members (variables or methods). The outer class cannot have static members.

//class Test {
//    static String name;
//    public Test(String name){
//        Test.name = name;
//    }
//
//}

//Outside classes are not static
public class InnerClasses {
    static class Test {
        String name;
        public Test(String name){
            this.name = name;
        }

    }

//    Static does not depend upon the object so the process that is class
//    making all are done in the compile time only i.e in Stack memory

    public static void main(String[] args) {
        Test A = new Test("Abhay");
        Test B = new Test("Sandeep");
        System.out.println(A.name + "  " + B.name);
    }
}

Singleton Class:

A Singleton class is a design pattern that restricts the instantiation of a class to one object. It ensures that only one instance of the class exists throughout the Java Virtual Machine (JVM) at any given time. This pattern is useful when exactly one object is needed to coordinate actions across the system.

package SecondPart;

public class Singleton {

    // Private static variable to hold the single instance of the class
    private static Singleton instance;

    // Private constructor to prevent instantiation from outside the class
    private Singleton() {}

    // Public static method to provide the global point of access to the instance
    public static Singleton getInstance() {
        // Lazy initialization: Create the instance only if it doesn't exist
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public static void main(String[] args) {
        Singleton single = Singleton.getInstance();
        System.out.println(single);

        Singleton single2 = Singleton.getInstance();
        System.out.println(single2);
    }
}

// Output
SecondPart.Singleton@6acbcfc0
SecondPart.Singleton@6acbcfc0
// Both have same hashcode (hashcode is simply a number it does not represent
// a memory address)