Java Notes.pdf
Document Details
Uploaded by GaloreEcoArt
Full Transcript
Introduction to Java Java is a high-level, object-oriented programming language developed by Sun Microsystems (now owned by Oracle Corporation) in the mid-1990s. It is designed to be platform-independent, meaning that code written in Java can run on any device that has a Java Virtual Machine (JVM)...
Introduction to Java Java is a high-level, object-oriented programming language developed by Sun Microsystems (now owned by Oracle Corporation) in the mid-1990s. It is designed to be platform-independent, meaning that code written in Java can run on any device that has a Java Virtual Machine (JVM) installed. Key Features of Java: Object-Oriented: Java follows the OOP paradigm, promoting code reusability and modularity. Platform-Independent: Java programs are compiled into bytecode, which can be executed on any platform with a JVM. Strongly Typed: Java enforces strict data type rules, enhancing code reliability and reducing runtime errors. Automatic Memory Management: Java has a built-in garbage collector that automatically manages memory allocation and deallocation. Rich Standard Library: Java provides a vast standard library (Java API) that includes classes for data structures, networking, and GUI development. Multithreading: Java supports multithreading, allowing concurrent execution of tasks, which is essential for modern applications. What is Object-Oriented Programming (OOP)? Object-Oriented Programming (OOP) is a programming paradigm that uses "objects" to represent data and methods to manipulate that data. The main concepts of OOP include: 1. Class: A blueprint for creating objects that defines properties (attributes) and methods (functions) that the objects created from the class can have. 2. Object: An instance of a class. It represents a specific item or entity in the application with its own unique state and behavior. 3. Encapsulation: The bundling of data (attributes) and methods (functions) that operate on the data into a single unit, known as a class. This restricts direct access to some of the object's components, which can prevent unintended interference and misuse. 4. Inheritance: The mechanism by which one class (subclass or derived class) can inherit attributes and methods from another class (superclass or base class). This promotes code reusability. 5. Polymorphism: The ability to perform a single action in different forms. In Java, this can be achieved through method overloading (same method name with different parameters) and method overriding (subclass providing a specific implementation of a method that is already defined in its superclass). Classes and Objects in Java Class A class in Java is defined using the class keyword. It serves as a template for creating objects. A class can contain: Attributes: Variables that define the properties of the class. Methods: Functions that define the behavior of the class. Example of a Class: java Copy code public class Car { // Attributes String color; String model; int year; // Method void displayInfo() { System.out.println("Car Model: " + model); System.out.println("Car Color: " + color); System.out.println("Year of Manufacture: " + year); } } Object An object is created from a class using the new keyword. It represents a specific instance of the class. Example of Creating an Object: java Copy code public class Main { public static void main(String[] args) { // Creating an object of the Car class Car myCar = new Car(); // Assigning values to attributes myCar.color = "Red"; myCar.model = "Toyota"; myCar.year = 2020; // Calling a method on the object myCar.displayInfo(); } } Constructors in Java A constructor is a special method used to initialize objects. It has the same name as the class and does not have a return type. Constructors can be classified into two types: 1. Default Constructor: A constructor that takes no parameters. If no constructor is defined in a class, the Java compiler automatically provides a default constructor. 2. Parameterized Constructor: A constructor that takes parameters to initialize an object with specific values. Example of Constructors: java Copy code public class Car { String color; String model; int year; // Default Constructor public Car() { color = "Unknown"; model = "Unknown"; year = 0; } // Parameterized Constructor public Car(String color, String model, int year) { this.color = color; this.model = model; this.year = year; } void displayInfo() { System.out.println("Car Model: " + model); System.out.println("Car Color: " + color); System.out.println("Year of Manufacture: " + year); } } public class Main { public static void main(String[] args) { // Creating an object using the default constructor Car car1 = new Car(); car1.displayInfo(); // Creating an object using the parameterized constructor Car car2 = new Car("Blue", "Honda", 2022); car2.displayInfo(); } } Inheritance in Object-Oriented Programming (OOP) Inheritance is one of the core concepts of Object-Oriented Programming (OOP) and allows a class to inherit properties and behaviors (methods) from another class. This promotes code reusability and establishes a natural hierarchy between classes. Key Concepts of Inheritance 1. Superclass (Base Class): The class whose properties and methods are inherited by another class. It is the parent class in the inheritance relationship. 2. Subclass (Derived Class): The class that inherits from the superclass. It can have additional properties and methods in addition to those inherited from the superclass. The subclass is often referred to as the child class. 3. IS-A Relationship: Inheritance establishes an "is-a" relationship between the subclass and the superclass. For example, if you have a class Animal and a subclass Dog, you can say that a Dog is an Animal. Types of Inheritance in Java Java supports several types of inheritance: 1. Single Inheritance: A class inherits from one superclass only. 2. Multilevel Inheritance: A class can inherit from a superclass, and then another class can inherit from that subclass, forming a chain. 3. Hierarchical Inheritance: Multiple subclasses inherit from a single superclass. 4. Multiple Inheritance (through Interfaces): Java does not support multiple inheritance with classes to avoid ambiguity (the "Diamond Problem"). However, a class can implement multiple interfaces. Example of Inheritance in Java Here's a simple example demonstrating inheritance in Java: Superclass java Copy code // Superclass class Animal { void eat() { System.out.println("This animal eats food."); } } Subclass java Copy code // Subclass inheriting from Animal class Dog extends Animal { void bark() { System.out.println("The dog barks."); } } Using Inheritance java Copy code public class Main { public static void main(String[] args) { // Creating an object of the subclass Dog myDog = new Dog(); // Calling methods from the subclass and superclass myDog.eat(); // Inherited method myDog.bark(); // Subclass method } } Output Copy code This animal eats food. The dog barks. Benefits of Inheritance 1. Code Reusability: Inheritance allows for the reuse of existing code, reducing redundancy and maintenance efforts. 2. Method Overriding: Subclasses can provide specific implementations of methods defined in the superclass, allowing for dynamic behavior. 3. Establishing Hierarchies: Inheritance creates a natural class hierarchy, making it easier to understand and manage complex systems. 4. Polymorphism: Inheritance supports polymorphism, allowing methods to be invoked on objects of different classes that share a common superclass. Method Overriding In Java, a subclass can override a method defined in its superclass to provide its specific implementation. This is a key aspect of polymorphism. Example of Method Overriding: java Copy code // Superclass class Animal { void sound() { System.out.println("Animal makes a sound."); } } // Subclass class Dog extends Animal { @Override void sound() { System.out.println("Dog barks."); } } public class Main { public static void main(String[] args) { Animal myAnimal = new Dog(); myAnimal.sound(); // Calls the overridden method in Dog class } } Output Copy code Dog barks. What is Polymorphism? Polymorphism is one of the core principles of Object-Oriented Programming (OOP). The term "polymorphism" is derived from the Greek words "poly" (many) and "morphs" (forms). In simple terms, polymorphism allows objects to be treated as instances of their parent class, even if they are actually instances of a derived class. This concept enables a single method, operator, or object to behave differently in different contexts. Polymorphism in Java can be classified into two types: 1. Compile-time Polymorphism (Static Polymorphism) 2. Run-time Polymorphism (Dynamic Polymorphism) 1. Compile-time Polymorphism (Static Polymorphism) This type of polymorphism is resolved during the compilation of the program. Compile-time polymorphism is achieved through method overloading and operator overloading (though Java doesn’t support operator overloading except for + for string concatenation). Method Overloading: Method overloading occurs when a class has more than one method with the same name but different parameter lists (either in number or type of parameters). The correct method is determined at compile time based on the arguments passed. Example: java Copy code class Calculator { // Method to add two integers public int add(int a, int b) { return a + b; } // Overloaded method to add three integers public int add(int a, int b, int c) { return a + b + c; } // Overloaded method to add two doubles public double add(double a, double b) { return a + b; } } public class Main { public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println(calc.add(10, 20)); // Output: 30 System.out.println(calc.add(10, 20, 30)); // Output: 60 System.out.println(calc.add(10.5, 20.5)); // Output: 31.0 } } In the above example, the add method is overloaded with different parameter lists. The correct method is chosen at compile-time based on the number and type of arguments passed. Key Characteristics of Compile-time Polymorphism: It is determined at compile-time. Achieved through method overloading. Operators (like +) can also be overloaded in languages like C++, but Java only allows operator overloading with the + operator for string concatenation. Static binding: The method to be invoked is decided during compile time. 2. Run-time Polymorphism (Dynamic Polymorphism) Run-time polymorphism occurs when a method call to an overridden method is resolved at runtime. This is achieved through method overriding, where a subclass provides a specific implementation of a method that is already defined in its parent class. In this type of polymorphism, the method that gets executed is determined at runtime based on the object type (not the reference type). Method Overriding: Method overriding occurs when a subclass defines a method that has the same name, return type, and parameters as a method in its superclass. The subclass's method is called when an object of the subclass is created, even if the object is referred to by a reference variable of the superclass. Example: java Copy code class Animal { void sound() { System.out.println("Animal makes a sound"); } } class Dog extends Animal { @Override void sound() { System.out.println("Dog barks"); } } class Cat extends Animal { @Override void sound() { System.out.println("Cat meows"); } } public class Main { public static void main(String[] args) { // Reference of superclass, object of subclass Animal myAnimal = new Animal(); // Animal reference and object Animal myDog = new Dog(); // Animal reference but Dog object Animal myCat = new Cat(); // Animal reference but Cat object myAnimal.sound(); // Output: Animal makes a sound myDog.sound(); // Output: Dog barks myCat.sound(); // Output: Cat meows } } In this example, even though the reference variable type is Animal, the actual method that gets called is determined at runtime based on the actual object type (Dog or Cat). Key Characteristics of Run-time Polymorphism: It is determined at runtime. Achieved through method overriding. Dynamic binding: The method to be invoked is decided during runtime based on the actual object type (not the reference type). Upcasting: Run-time polymorphism can occur when a reference variable of the parent class refers to an object of the child class. Differences Between Compile-time and Run-time Polymorphism Aspect Compile-time Polymorphism Run-time Polymorphism Binding Static binding (at compile time) Dynamic binding (at runtime) Achieved by Method overloading Method overriding Determinatio Method to be invoked is determined at Method to be invoked is n compile time determined at runtime Flexibility Less flexible More flexible Performance Better performance since it’s Slightly slower due to runtime determined at compile time determination Example Overloading methods with different Overriding methods in subclasses parameter types Java Abstraction in Object-Oriented Programming (OOP) Abstraction is one of the four key principles of Object-Oriented Programming (OOP), along with Encapsulation, Inheritance, and Polymorphism. In Java, abstraction is the process of hiding the implementation details from the user and only providing the essential features or functionality. This helps in reducing complexity and improving code maintainability. Key Points of Abstraction: 1. Purpose: To hide unnecessary details from the user and show only relevant information. 2. Implementation: Achieved using abstract classes and interfaces. 3. Benefit: Helps in managing complexity and allows for a clearer, high-level understanding of the code. 1. Abstract Class in Java An abstract class is a class that cannot be instantiated directly. It may or may not contain abstract methods (methods without a body). If a class contains at least one abstract method, it must be declared as an abstract class. Characteristics: It can have both abstract and non-abstract methods. It can have constructors and static methods. It can have final methods, which cannot be overridden. It cannot be instantiated directly (cannot create an object of an abstract class). Syntax: abstract class Vehicle { abstract void startEngine(); // Abstract method, no implementation void stopEngine() { // Non-abstract method with implementation System.out.println("Engine stopped."); } } Example: abstract class Vehicle { abstract void startEngine(); void stopEngine() { System.out.println("Engine stopped."); } } class Car extends Vehicle { void startEngine() { System.out.println("Car engine started."); } } public class Main { public static void main(String[] args) { Vehicle myCar = new Car(); myCar.startEngine(); // Output: Car engine started. myCar.stopEngine(); // Output: Engine stopped. } } 2. Abstract Methods An abstract method is a method without a body (no implementation). The subclass that extends the abstract class must provide an implementation for all the abstract methods unless the subclass is also declared as abstract. Syntax: abstract void methodName(); In the example above, startEngine() is an abstract method. The subclass Car provides the implementation for startEngine(). Why Use Abstraction? 1. Code Reusability: By defining common functionality in an abstract class or interface, we can avoid code duplication across different classes. 2. Implementation Hiding: The user only interacts with the interface or abstract class without knowing how it’s implemented, making the code easier to use. 3. Flexibility: Abstract classes and interfaces provide flexibility to implement details later in different ways. 4. Security: By hiding the implementation details, abstraction also increases the security of the application by preventing unauthorized access to certain parts of the code. Real-World Example of Abstraction Consider a Payment system where we want to process payments through different methods (credit card, PayPal, etc.). abstract class Payment { abstract void makePayment(); } class CreditCardPayment extends Payment { void makePayment() { System.out.println("Payment made using Credit Card."); } } class PayPalPayment extends Payment { void makePayment() { System.out.println("Payment made using PayPal."); } } public class PaymentApp { public static void main(String[] args) { Payment payment = new CreditCardPayment(); payment.makePayment(); // Output: Payment made using Credit Card. payment = new PayPalPayment(); payment.makePayment(); // Output: Payment made using PayPal. } } In this example, the Payment class is abstract, and different types of payments provide their specific implementations. The main application does not need to know how each payment works; it just calls makePayment(). Code without abstract: class Vehicle { void startEngine() { System.out.println("Vehicle engine started."); } void stopEngine() { System.out.println("Engine stopped."); } } class Car extends Vehicle { @Override void startEngine() { System.out.println("Car engine started."); } } public class Main { public static void main(String[] args) { Vehicle myCar = new Car(); myCar.startEngine(); // Output: Car engine started. myCar.stopEngine(); // Output: Engine stopped. } } Key Differences: 1. No Abstract Class: ○ In the updated version, the Vehicle class is no longer abstract. ○ This means that Vehicle can be instantiated directly, though we are still creating a Car object here. 2. Concrete Methods: ○ The startEngine() method now has a default implementation in the Vehicle class instead of being abstract. ○ Subclasses like Car can still override this method, but they are no longer forced to do so by the abstract keyword. 3. Implementation Visibility: ○ There is no hidden information in the non-abstract version. ○ Both startEngine() and stopEngine() are fully implemented in the Vehicle class. ○ Subclasses (like Car) can choose to override startEngine() or leave it as is. Detailed Explanation: 1. Information Hiding (Abstraction vs. Concrete Classes) With abstraction: When you declare a method as abstract, you are hiding how that method works from any class that uses the abstract class. For example, in your original code, the Vehicle class defines that every vehicle must have a startEngine() method, but it does not specify how the engine starts. The Car class (or any subclass) is responsible for implementing that behavior. ○ The implementation detail is hidden until the subclass provides the actual code for startEngine(). ○ The programmer knows that startEngine() exists, but they do not know how it works until the concrete class (Car) implements it. Without abstraction: In the revised code, there is no abstraction. The Vehicle class itself provides a concrete implementation for startEngine(). Therefore, no implementation is hidden. ○ All classes that use Vehicle now either use the default implementation or override it voluntarily (without being forced by the abstract keyword). ○ Subclasses like Car are still free to override the method, but the fact that there is no abstraction means the base Vehicle class already has its own implementation. 2. Enforcement of Implementation With abstraction: By using an abstract method, we enforce that any subclass (like Car) must provide an implementation for startEngine(). This is crucial when you want to ensure that all derived classes define their own version of a certain behavior (in this case, starting the engine). Without abstraction: In the version without abstract methods, there is no enforcement. The startEngine() method already has a default implementation in the Vehicle class, so subclasses do not have to override it if they don’t want to. This could potentially lead to scenarios where a subclass forgets to override the method, and the base class's implementation (which may not be appropriate) is used. 3. Flexibility With abstraction: Abstract classes provide more flexibility because they allow you to define a common structure without providing specific implementations. This is useful when different subclasses will have different implementations for certain methods. It allows you to design systems where the exact behavior of methods can vary between subclasses, without needing to know those details upfront. ○ Example: A Vehicle abstract class could have a startEngine() method that different types of vehicles (like Car, Truck, Motorcycle) implement differently. Without abstraction: Without abstract classes, the base class (Vehicle) provides a specific implementation of startEngine(). This limits flexibility since every subclass will either inherit this specific implementation or override it explicitly. There’s no forced variation between subclasses. 4. Use Case When to use abstraction: ○ When different subclasses have different implementations for certain methods. For example, different types of vehicles (Car, Bike, Truck) may have very different ways to start their engines. Abstraction ensures that each subclass provides its own unique behavior. ○ When you want to hide details and enforce a common interface/structure. When not to use abstraction: ○ When the base class provides a reasonable default implementation, and you don’t necessarily need to force subclasses to implement the method. ○ When there's no need to hide implementation details, and all relevant details can be shared directly.