OOP principles:
What is OOP?
short answer:
Object-oriented programming is a programming paradigm that involves organising data and functions into a single unit of code called an object. It has 4 pillars: Abstraction, Polymorphism, Inheritance and Encapsulation (A PIE).
long answer:
Object-oriented programming is a programming paradigm, or classification, that organises a group of data attributes with functions or methods into a unit, known as an object.
OOP is a programming paradigm that organises software design around data, or objects, rather than functions and logic. Objects are instances of classes, which define their structure and behaviour. Classes are templates that define the properties and methods of objects, and they can be extended and inherited to create more specialised classes.
OOP has 4 main pillars: Abstraction, Polymorphism, Inheritance and Encapsulation.
Abstraction
What is an Abstraction in Java?
short:
Abstraction in Java is the process of hiding implementation details and displaying only essential information to the user.
short answer:
Abstraction is a fundamental concept in object-oriented programming, and it allows programmers to create complex systems by breaking them down into smaller, more manageable parts.
In Java, abstraction is implemented using abstract classes and interfaces, which provide a way to define a set of common behaviors without implementation details.
Abstract classes are classes that cannot be instantiated, and they are typically used as base classes for other, more specific classes that inherit their properties and methods.
Interfaces, on the other hand, are a way to define a set of behaviors that a class must implement, but they provide no implementation details themselves.
By using abstraction, programmers can create code that is more modular and easier to maintain, since changes to one part of the system do not affect other parts.
Abstraction also allows programmers to write code that is more flexible and adaptable to different situations, since it can be customized by implementing different concrete classes or interfaces.
One of the main benefits is that abstraction simplifies the process of designing and developing software, since it helps to break down complex systems into smaller, more manageable components.
Another benefit of abstraction is that it promotes code reusability, since abstract classes and interfaces can be used as templates for creating new classes that share common behaviors.
Abstraction is a key component of the Java programming language, and it is used extensively in the development of many types of software.
How to achieve and maintain abstraction in java?
To achieve and maintain abstraction in Java, you should use interfaces and/or abstract classes (but remember about composition over inheritance), encapsulate implementation details, use dependency injections, and use single responsibility principle.
Use interfaces: Interfaces are a powerful tool for achieving abstraction in Java. By defining interfaces, you can create a contract that specifies the methods that must be implemented by a class. This allows you to create abstract types that can be used in a variety of contexts.
Encapsulate implementation details: Abstraction is all about hiding implementation details from the user. To achieve this, you should encapsulate implementation details within your classes. This means that the user should only be able to interact with your class through a well-defined interface, rather than being able to directly access the internal state of the class.
Use abstract classes: Abstract classes are similar to interfaces, but they can also contain concrete methods. This can be useful if you have some common functionality that you want to share between multiple classes.
Follow the Single Responsibility Principle: The Single Responsibility Principle (SRP) states that a class should have only one reason to change. By following this principle, you can ensure that your classes are focused on a single responsibility and are less likely to change in unexpected ways. This can help you maintain abstraction over time.
Use dependency injection: Dependency injection is a technique for achieving loose coupling between classes. By injecting dependencies into a class rather than creating them directly, you can make your code more flexible and easier to maintain. This can help you achieve abstraction by allowing you to swap out implementations of a dependency without affecting the rest of your code.
Use composition over inheritance: Inheritance can be a powerful tool for achieving abstraction, but it can also lead to tight coupling between classes. By using composition instead of inheritance, you can achieve the same level of abstraction while maintaining more flexibility and reducing the risk of unexpected behavior.
Use meaningful abstractions: Finally, it’s important to make sure that your abstractions are meaningful and aligned with the problem you’re trying to solve. If your abstractions are too complex or don’t align with the problem domain, they may be difficult to understand and maintain over time.
What is an abstract class in Java?
An abstract class in Java is a class that cannot be instantiated, but can be used as a template for creating concrete subclasses.
An abstract class can contain both abstract and non-abstract methods, and it can also contain instance variables and constructors. However, any methods that are declared as abstract in the abstract class must be implemented by any concrete subclasses.
Abstract classes can be extended by other abstract classes, creating a hierarchy of abstract classes that can be used to provide increasingly specific implementations of a set of behaviors.
Instance variables and constructors in an abstract class can help to provide a common set of properties and behaviors that can be shared by multiple subclasses, while also providing flexibility to customize those properties and behaviors as needed.
Abstract classes can have constructors, but they cannot be directly instantiated, so the constructors are usually used to initialize the instance variables of the abstract class or to provide a way for subclasses to pass arguments to the constructor of the abstract class.
What is an interface in Java?
An interface is a contract that defines a set of methods that a class must implement.
In Java, an interface is a collection of method signatures that define a set of actions that a class can perform. An interface provides a contract or a set of rules that classes must follow when they implement the interface.
Interfaces in Java are similar to abstract classes, but they cannot contain any implementation details or instance variables.
Interfaces are defined using the interface keyword and can be implemented by any class using the implements keyword.
A class can implement multiple interfaces, but can only extend one class.
Interfaces can be used to define common functionality across multiple classes, providing a consistent set of methods that can be used by other classes.
Interfaces can also be used to create generic code that is not tied to any specific class or implementation.
Interfaces can also define default methods and static methods, which provide default implementation or behavior for specific methods.
Interfaces can be extended by other interfaces, allowing for more specific and specialized contracts to be defined.
Interfaces are a key part of many Java APIs, including the Java Collections framework, where interfaces such as List, Set, and Map define a common set of methods for working with lists, sets, and maps, respectively.
Polymorphism
How to describe Description the idea of polymorphism?
short answer:
Polymorphism is a fundamental concept in object-oriented programming where objects of different classes can be treated as if they were objects of the same class through the use of inheritance and interfaces.
long answer:
Polymorphism enables dynamic binding, which means that the method to be invoked is determined at runtime based on the actual object being referenced, rather than at compile-time based on the declared type of the reference variable.
Polymorphism is achieved through the use of inheritance and interfaces, which allow classes to share common methods and properties.
Polymorphism enables method overloading, which allows multiple methods with the same name to be defined in a class, as long as they have different parameter lists.
Polymorphism also enables method overriding, where a subclass can provide its own implementation of a method inherited from its superclass.
Polymorphic interfaces define a set of methods that can be implemented by multiple classes, which enables code to be written in a more modular and reusable way.
Polymorphism can also be achieved through the use of abstract classes, which define a set of abstract methods that must be implemented by any subclass.
Polymorphism is a key concept in design patterns, such as the Factory Method pattern and the Strategy pattern, which rely on polymorphic interfaces and dynamic binding to achieve their goals.
Java supports both static and dynamic polymorphism. Static polymorphism is achieved through method overloading, while dynamic polymorphism is achieved through method overriding and dynamic binding.
Polymorphism can be used to write more concise and readable code, by allowing multiple objects to be treated in a uniform way, without the need for repetitive code.
Polymorphism can be used to write more testable code, by allowing different implementations to be substituted and tested independently of each other.
What types of polymorphism are there?
In Java, there are two types of polymorphism: static polymorphism (compile-time polymorphism) and dynamic polymorphism (runtime polymorphism).
Static polymorphism (compile-time polymorphism), is achieved through method overloading. In method overloading, two or more methods in a class have the same name, but different parameters. The compiler determines which method to call based on the number and types of arguments passed at compile time. The method overloading allows you to create methods that perform similar operations, but with different input parameters.
Dynamic polymorphism (runtime polymorphism) is achieved through method overriding and dynamic binding. Method overriding is when a subclass provides its own implementation of a method that is already defined in its parent class. Dynamic binding is the process of determining which method to call at runtime, based on the actual object type that the method is called on. In dynamic binding, the decision of which method to call is delayed until runtime, rather than determined at compile time.
Dynamic polymorphism allows for more flexible and modular code, as different objects can be treated in a uniform way, even if they have different underlying implementations. This can make it easier to add new functionality to an existing program, without needing to modify existing code.
How does Java Implement and support polymorphism?
Java’s support for polymorphism is implemented through (1) inheritance, (2) interfaces, (3) method overriding, (4) method overloading, and (5) dynamic binding.
How does Polymorphism affect data members in Java?
short answer:
Data members (aka instance variables) are not directly affected by polymorphism but they can be accessed and modified through the use of polymorphic references and dynamic binding (if inherited from superclass) in combination with public/protected methods.
long answer:
Data members are variables that are declared within a class and hold the state or data of an object. They are also called instance variables, as each object of the class has its own copy of these variables. Data members are typically declared with a visibility modifier (such as private, public, or protected) and a data type (such as int, double, or String).
Polymorphism in Java is not directly related to data members, as they are not overridden by subclasses in the same way that methods are. However, data members can still be accessed and modified through polymorphic references to objects.
In Java, when a subclass inherits from a superclass, it also inherits all the data members defined in the superclass. These data members are usually marked as private or protected, which means that they cannot be accessed directly from outside the class. However, they can be accessed and modified using public methods defined in the class.
Polymorphism in Java allows objects of different classes to be treated as if they were objects of the same type, which is known as the “is-a” relationship. This means that you can use a reference to a superclass or interface type to refer to objects of any subclass or implementation class that extends or implements the superclass or interface. When you call a method on an object using a polymorphic reference, Java uses dynamic binding to determine which implementation of the method to use, based on the actual object type that the method is called on.
While data members are not overridden in the same way that methods are, they can still be accessed and modified through polymorphic references to objects. For example, if a subclass defines a data member with the same name as a data member in its superclass, you can still access the superclass data member using the “super” keyword. You can also use the “instanceof” operator to check whether an object is an instance of a particular class, and cast the object to the appropriate type to access its data members directly.
What is MEthod Overloading and method overriding?
Method overloading is the process of creating multiple methods with the same name but with different parameters. The method that is called depends on the type and number of arguments passed to it (and is decided at runtime).
This allows the programmer to have multiple methods with the same name, but with different behaviors based on the type and number of arguments passed in.
This means that an object can take on different forms depending on the parameters that are passed to the method.
Method overriding is the process of creating a new implementation for a method that is already defined in a parent class. This is done by creating a new method with the same name, return type, and parameters as the parent class method.
It allows a subclass to provide a different implementation of a method that is already defined in the superclass. This means that an object can have a different behavior depending on its actual type at runtime. This enables the programmer to create more specialized behavior for subclasses while still retaining the functionality of the superclass.
What is runtime polymorphism and compile-time polymorphism?
Compile-time polymorphism (aka static polymorphism) is achieved by the use of method overloading and is resolved at compile-time based on the number, types, and order of arguments passed to the method.
Runtime polymorphism (aka dynamic polymorphism) is achieved with method overriding, as well as dynamic binding, and it is resolved at runtime based on the actual object that is calling the method.
What are Best practices regarding polymorphism in Java?
- Use abstract classes and interfaces to define common behavior and enforce contracts.
- Implement the Liskov substitution principle, ensuring that subclasses can be used interchangeably with their parent classes.
- Use the @Override annotation to indicate that a method overrides a superclass or interface method.
- Favor composition over inheritance to achieve greater flexibility and maintainability.
- Use the instanceof operator and typecasting judiciously to avoid potential runtime errors.
- Avoid method overloading that leads to ambiguous or confusing code (f.e. overloading with the parameter int and Integer);
- Simplify complex branching logic and reduce code duplication with polymorphism.
- Write clear and concise documentation that explains how polymorphic behaviour works and how to use it effectively.
Can you override or overload static methods in Java? How?
In Java, it is possible to override and overload static methods, but there are limitations you need to consider.
When you override a static method, you’re defining a new method with the same name and parameters as the original method, but it only works if you call it on the subclass directly. You can use the @Override annotation to catch errors (no true polymorphic behaviour).
Overloading a static method means you’re creating a new method with the same name as the original method, but with different parameters. It provides different implementations of the same functionality based on the arguments passed to the method. When you overload a static method in Java, the method resolution happens at compile-time rather than at runtime (no dynamic method dispatch).
Overriding or overloading static methods can have performance implications, especially if the methods are frequently called or if they perform complex operations.
What is the difference between “is a” vs “has a” relationship in Java?
“Is a” relationships are established through inheritance, where a subclass is a type of its superclass. For example, a Dog class could extend an Animal class, since a dog is a type of animal.
- Inheritance allows for the reuse of code and the creation of hierarchies of related classes.
- It can simplify code by allowing for the use of polymorphism.
- It can improve code readability and organization by establishing clear relationships between classes.
- “Is a” relationships (inheritance) are most appropriate when you are dealing with a true “is a” relationship, where one class truly is a type of another class.
“Has a” relationships are established through composition, where a class has an instance of another class as a member variable. For example, a Car class could have an Engine instance, since a car has an engine.
- Composition can lead to more flexible and modular code.
- It can make code easier to maintain and modify over time.
- It can improve encapsulation and information hiding. “Has a” relationship is best when you’re building complex systems that should be composed at runtime.
Inheritance
What is inheritance?
short answer:
Inheritance in Java is a mechanism that allows a subclass (or subinterface) to inherit properties and behaviours from a superclass or interface, enabling code reuse and polymorphic behaviour.
Long answer:
Inheritance is a fundamental concept in object-oriented programming and a key feature of Java. It enables code reuse and helps to create complex class hierarchies.
All classes except for the Object class implicitly inherit from the Object class, which is the root of the class hierarchy.
When a class inherits from another, the child class can access all of the parent’s properties and methods and can override them to provide its own implementation.
Java supports single inheritance, which means that a class can only inherit from one superclass. However, Java also supports multiple inheritance of interfaces, which allows a class to inherit from multiple interfaces and implement their methods.
Multiple inheritance by interface occurs if a class implements multiple interfaces or also if an interface itself extends multiple interfaces.
Inheritance can be used to create polymorphic behaviour, where a variable of a superclass type can hold an object of a subclass type. This allows for more flexible and extensible code, as the behaviour of the variable can vary depending on the type of object it holds.
What are the types of Inheritance in java?
Java supports two types of inheritance based on the mechanism of inheritance:
- Class inheritance (aka single inheritance): it allows a subclass to inherit properties and methods from a single superclass. It is achieved by using the “extends” keyword in a class definition.
- Interface inheritance: A class can inherit properties and method signatures from one or more interfaces. It is achieved by using the “implements” keyword in a class definition It allows the class to inherit method signatures and default implementations from one or more interfaces.
Based on the structure of inheritance, there are five types of inheritance:
- Single-level inheritance: Single-level inheritance is the simplest form of inheritance, where a subclass is derived from a single superclass. The subclass inherits all the methods and variables of the superclass and can also add new methods and variables.
- Multi-level Inheritance: In multi-level inheritance, a subclass is derived from a superclass, which is also a subclass of another superclass. In other words, it involves chaining of inheritance from one class to another. Multi-level inheritance can result in deep class hierarchies, making the code more difficult to maintain.
- Hierarchical Inheritance: Hierarchical inheritance is where multiple subclasses inherit from a single superclass. This means that there is one superclass and multiple subclasses that extend it. It allows for multiple classes to share a common set of properties or methods. It can lead to code duplication if the common properties or methods are not abstracted into a separate class.
- Multiple Inheritance: Multiple inheritance is where a subclass inherits from two or more superclasses. In Java, multiple inheritance is achieved through the use of interfaces, as Java does not support multiple inheritance with classes. It promotes a more natural representation of the problem domain, as some classes may naturally inherit properties and behaviors from multiple sources. It can lead to method or attribute name conflicts.
- Hybrid Inheritance: Hybrid inheritance is a combination of two or more types of inheritance. For example, it can be a combination of hierarchical and multiple inheritance or multi-level and multiple inheritance.
What are the Ways to use inheritance in java?
- Implementing inheritance hierarchies: Inheritance allows you to create hierarchies of classes, where subclasses inherit properties and methods from their superclasses. This allows for creating more specialized classes that share common functionality with their parent classes, while also adding their own unique features.
- Polymorphism: Polymorphism is one of the key benefits of inheritance in Java. It allows for using a subclass in place of a superclass, which can help reduce code duplication and make your code more flexible.
- Code reuse: Inheritance allows for reusing code from existing classes, which can save time and reduce errors. By inheriting properties and methods from a superclass, you can avoid duplicating code in your subclasses.
- Abstract classes: Abstract classes are classes that cannot be instantiated directly, but are instead used as a basis for other classes. They allow you to define a common set of properties and methods that will be shared by all subclasses, while also allowing you to specify abstract methods that must be implemented by each subclass.
- Interfaces: Interfaces are similar to abstract classes, but they define a contract for what methods a class must implement, without specifying any implementation details. By using interfaces, you can create more flexible code that can work with different classes that implement the same interface.
- Overriding methods: Inheritance allows for overriding methods from a superclass in a subclass, which can be useful for customising the behaviour of a class without having to rewrite the entire class from scratch.
Difference between inheritance and abstraction.
short answer
Inheritance and abstraction serve different purposes and are implemented differently. Inheritance lets you reuse code and create hierarchies of related classes, and abstraction lets you focus on the essential features of an object or system and ignore the non-essential ones by hiding complex implementation details.
long answer:
Inheritance and abstraction are both important concepts in object-oriented programming, but they serve different purposes and are implemented differently in Java.
Inheritance is a mechanism where one class inherits the properties and behavior of another class. It is done to reuse code and to create new classes that are similar to existing ones but with additional or modified functionality. It allows for the creation of a hierarchy of related classes, with more specific classes inheriting from more general ones.
On the other hand, abstraction is a way to focus on the essential features of an object or system and ignore the non-essential ones. It is the process of hiding complex implementation details while showing only the necessary functionality to the users.
In summary, inheritance is a way to reuse code and create hierarchies of related classes, while abstraction is a way to focus on the essential features of an object or system and ignore the non-essential ones by hiding complex implementation details.
What’s the difference between Polymorphism and inheritance?
Polymorphism is the ability of an object to take on many forms, or the ability of a method to be used with objects of different classes.
Inheritance is a mechanism where one class inherits the properties and behavior of another class.
- Polymorphism is focused on the behaviour of objects and methods, while inheritance is focused on the properties and behaviour of classes.
- Polymorphism can be achieved through method overriding or method overloading, while inheritance is achieved through subclassing.
- Both polymorphism and inheritance are mechanisms for achieving more flexibility and reusability in code.
What’s the difference between Composition and inheritance?
Composition is a mechanism where a class contains an instance of another class as a member variable by declaring an instance of another class as a member variable within a class (“has-a” relationship). It allows for creating complex objects by combining simpler ones.
Composition allows for greater flexibility in designing class relationships, as a class can be composed of different objects at runtime.
Composition allows for better encapsulation and information hiding in a codebase.
Inheritance is a mechanism where one class inherits the properties and behavior of another class (“is-a” relationship). It allows for the creation of a hierarchy of related classes, with more specific classes inheriting from more general ones.
Inheritance allows for the reuse of code and the creation of hierarchies of related classes.
- Composition is focused on the relationship between objects, while inheritance is focused on the relationship between classes.
- In composition, a class contains an instance of another class as a member variable, while in inheritance, a subclass inherits from a superclass.
- Composition can allow for greater flexibility and easier maintenance, as objects can be swapped out or changed more easily than class hierarchies. Inheritance can lead to tighter coupling between classes and can make code harder to change over time.
Diamond problem with multiple inheritance of interfaces
The diamond problem in Java occurs when a class implements two or more interfaces that have default methods with the same name and signature. In this case, the implementing class inherits conflicting default implementations from multiple interfaces, causing ambiguity and leading to a compilation error.
What is static method hiding and how it’s connected to inheritance?
Static methods are not inherited in the same way as instance methods. While instance methods can be overridden in subclasses, static methods cannot be overridden. Static methods are associated with the class itself, not with instances of the class.
When a subclass defines a static method with the same signature as a static method in its superclass, it’s called method hiding, not method overriding. The subclass is merely providing a new implementation of the method that’s specific to the subclass, but it does not affect the original implementation in the superclass.
When calling a static method, it is always called on the class where it is defined, even if you use a reference of the subclass. The correct method to call is determined at compile time based on the reference type, not the object type.
example:
SuperClass superClass = new SuperClass();
SuperClass subClassRef = new SubClass(); // Reference type is SuperClass
SubClass subClass = new SubClass();
superClass.foo(); // Output: Static method in SuperClass
subClassRef.foo(); // Output: Static method in SuperClass
subClass.foo(); // Output: Static method in SubClass
What is tight coupling and how it’s connected to class inheritance
Class inheritance allows us to define common functionality in a superclass and extend it in subclasses. Tight coupling means two or more classes are dependent on each other in such a way that a change in one class requires changes in others. This can be problematic because it can lead to a domino effect of changes that ripple throughout the codebase, making it difficult to maintain and evolve the system.
Tight coupling can also make it harder to reuse code, as it becomes difficult to extract and reuse individual classes or modules without also bringing along their dependencies.
To reduce tight coupling, it’s important to design classes and modules that have a clear separation of concerns (Single Responsibility Principle), with each class responsible for a specific task or set of tasks.
The use of interfaces and abstractions can help to reduce tight coupling by providing a way to communicate between classes without knowing their specific implementation details.
The use of dependency injection can also help to reduce tight coupling (composition) by allowing dependencies to be injected into a class at runtime, rather than being hard-coded into the class itself.
Using certain design patterns, such as the Observer pattern, helps to reduce tight coupling by defining a one-to-many relationship.
Pros and cons of inheritance in Java
Pros of inheritance:
- Reusability: Inheritance allows you to reuse code from a parent class in a child class, reducing code duplication and promoting code reuse.
- Polymorphism: Inheritance enables you to use objects of a child class in place of objects of a parent class, allowing for polymorphic behavior.
- Code organization: Inheritance helps organize code into a hierarchical structure, making it easier to understand and maintain.
- Flexibility: Inheritance allows for dynamic binding, which means that the method implementation to be used is decided at runtime based on the type of object.
- Easier debugging: Inheritance simplifies debugging by allowing you to trace issues to the base class instead of examining the entire code.
- Saves time: Inheritance can save you time by providing you with a well-designed set of classes and functionality, so that you can focus on implementing new features rather than re-inventing the wheel.
- Code extensibility: Inheritance can be used to extend the functionality of an existing class by adding new methods and attributes in a child class.
- Enhances code readability: By following the inheritance hierarchy, code becomes more readable, and it becomes easier to understand the relationships between classes.
- Encourages abstraction: Inheritance encourages abstraction, which helps to separate concerns and improve the design of the software.
- Promotes consistent behavior: Inheritance allows you to define common behavior in a parent class, which ensures that all child classes behave consistently.
Cons of inheritance:
- Tight coupling: Inheritance can lead to tight coupling between classes, which can make the code less flexible and harder to maintain.
- Inheritance hierarchy complexity: Inheritance can result in a complex inheritance hierarchy, which can be difficult to understand and manage.
- Code bloat: Inheritance can lead to code bloat, where classes become too large and unwieldy, making it harder to maintain and modify.
- Overriding behavior: Inheritance can cause issues when a method is overridden in a child class, potentially leading to unexpected behavior.
- Dependency on base class implementation: Child classes are dependent on the implementation of the base class, which can cause issues when changes are made to the base class.
- Initialization order issues: Inheritance can cause initialization order issues, where variables and methods are not initialized in the expected order.
- Security issues: Inheritance can lead to security issues if access modifiers are not used correctly, potentially exposing sensitive data or functionality.
Encapsulation
What is encapsulation?
short answer:
Encapsulation is a process of bundling data and methods within a single unit (i.e. class) and restricting direct access to the data from outside the class. In Java, encapsulation is typically implemented using access modifiers, as well as the use of interfaces, abstract classes and packages to prevent unauthorised access.
long answer:
Encapsulation is a fundamental concept in object-oriented programming that’s all about keeping data and methods private and hidden from the outside world. The idea is to restrict access to an object’s internal state and behavior, and only expose a public interface that other objects can interact with.
Encapsulation helps to ensure that data is protected from unauthorised access or modification, and it promotes data integrity and consistency. It also allows for the implementation of complex behaviour behind a simple interface, making code easier to use and maintain.
In Java, encapsulation is typically achieved using access modifiers like “private”, “protected”, and “public” to control the visibility of class members.
By encapsulating data and behaviour, we can create more robust, flexible, and secure software. We can also change the implementation of a class without affecting the code that uses it, since the public interface remains the same.
What are the features of encapsulation in Java?
Encapsulation:
- is a mechanism for hiding the implementation details of a class from the outside world.
- allows you to protect the internal state of an object from outside interference.
- is achieved through access modifiers like private, public, and protected.
- helps to prevent unauthorised access to the internal data of a class.
- promotes code reusability and helps to reduce code complexity.
- improves the maintainability of code by making it easier to modify and debug.
- can also help to promote good design principles like abstraction and information hiding.
Advantages and disadvantages of encapsulation.
Advantages:
- promotes code reusability and reduces code complexity, making it easier to maintain and modify your code over time.
- improves program stability and security by protecting the internal state of objects from outside interference.
- helps prevent naming conflicts and reduces the risk of errors caused by naming collisions.
- promotes good design principles like abstraction and information hiding, allowing you to create modular and maintainable code.
Disadvantages:
- can make it sometimes more difficult to debug code, since the internal state of objects is hidden from outside code.
- can sometimes result in code duplication, since objects must be initialised separately from the class that defines them.
- can sometimes make it more difficult to achieve performance optimisations (f.e. inlining, dead code elimination, and field access optimisation) since access restrictions may prevent certain optimisations from being applied.
- can sometimes result in higher memory usage, since objects must be initialised separately from the class that defines them.
How does Java support Encapsulation and what are its benefits?
Java supports encapsulation through the use of access modifiers like private, public, and protected, which control the visibility of class members. This allows you to hide the implementation details of a class from the outside world and protect the internal state of objects from outside interference, improving program stability and security. Encapsulation also promotes code reusability and reduces code complexity, making it easier to maintain and modify your code over time.
Difference between inheritance and encapsulation.
Inheritance is about creating new classes based on existing classes, while encapsulation is about hiding the internal details of a class from the outside world.
About data members: Inheritance allows subclasses to inherit properties and methods from their parent classes, while encapsulation restricts access to a class’s internal data and provides methods for accessing and modifying it.
About class coupling: Inheritance can result in a complex and tightly coupled class hierarchy that can be difficult to maintain and modify over time, while encapsulation can promote loose coupling and modularity, making it easier to change the implementation details of a class without affecting other parts of the program.About class relationship: Inheritance is used to model a “is-a” relationship between classes, while encapsulation is used to model a “has-a” relationship between classes.
