Summary OOP
Summary OOP
1. incompatible
2. ";" expected
3. cannot find symbols
Runtime errors
1. ClassCastException
2. IllegalArgumentException
3. ArrayIndexOutOfBoundsException
non-static inner-classes
// Example 1
public class OuterClass {
private static String outerField = "Outer Field";
// Example 2
public class OuterClass {
private static String staticOuterField = "Static Outer Field";
private String instanceOuterField = "Instance Outer Field";
// Example 1
public class OuterClass {
private static String outerField = "Outer Field";
// Example 2
public class OuterClass {
private static String staticOuterField = "Static Outer Field";
private String instanceOuterField = "Instance Outer Field";
Note: Modularity and code reuse: OOP promotes modularity and code reuse through encapsulation,
inheritance, and polymorphism, making it easier to maintain and extend large and complex programs.
Polymorphism: When you define a supertype for a group of classes, any subclass of that supertype can be
substituted where the supertype is expected.
class Animal {
public void makeSound() {
System.out.println("Some generic animal sound");
}
}
//Compile Time: At compile time, the compiler only knows that animal is of type Animal. It does not kn
//Runtime: At runtime, when the line Animal animal = new Dog(); is executed, an object of type Dog is
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // Upcasting: Dog object, Animal reference
//
animal.makeSound(); // Outputs "Bark" because the actual object is a Dog
Structured Programming: Structured programming, exemplified by languages like C, emphasizes a logical structure
in the code. It uses functions or procedures to perform tasks. The focus is on improving clarity, quality, and
development time by using blocks of code (like functions and control structures such as loops and if-else
statements) that can execute sequentially, conditionally, or repeatedly.
In Java, three things can prevent a class from being subclassed (inheritance):
Access Control: A non-public class (default access) can only be subclassed by classes within the same
package.
Final Modifier: A class declared as final cannot be subclassed.
Private Constructors: A class with only private constructors cannot be subclassed.
2. Code Organization
OOP: Code in OOP is organized around objects and classes. Classes define the attributes and behaviors of
objects, encapsulating data and functions together. This leads to a modular approach where related data and
behaviors are grouped together, making the codebase more scalable and manageable.
Structured Programming: In structured programming, code is organized into procedures or functions. This
approach promotes a top-down design model, starting with a broad problem and breaking it down into smaller and
more manageable functions. There’s less emphasis on how data is encapsulated or related to functions.
3. Data Handling
OOP: Data is almost always hidden from the outside world (private instance variables) and can only be accessed
or modified through methods (functions within classes), which is a part of encapsulation. This can prevent data
from being accidentally modified in unintended ways.
Setter methods in object-oriented programming let you change how values are set without affecting other parts
of your code that use the class. While using public variables directly might look more efficient, the gain in
performance is very small. The main benefit of setters is that they keep your code flexible and prevent issues
when changes are made internally.
Structured Programming: Data is generally more exposed and can be accessed directly from anywhere in the
program. This can lead to higher risk of bugs or data corruption due to unintended modifications.
1. Control Over Data: This approach allows you to modify the implementation without affecting any existing code that
uses the getBalance() or setBalance(double) methods, demonstrating flexibility and future-proofing.
2. Encapsulation: Imagine a class UserProfile that should not allow the user's ID to be changed once it's set, but other
profile information can be updated. In this case, the absence of a setter for the userId protects its value from
external changes, ensuring the user ID remains consistent throughout the object's lifecycle.
3. Additional Logic in Access/Mutation: Let's consider a Rectangle class where area needs to be computed based on
length and width, but it should not be stored directly. The getArea method computes the area dynamically from
length and width, ensuring that the area is always correct relative to its dimensions, without the need for storing it
directly.
4. Debugging and Logging: In this case, the setter logs each change to the temperature.
OOP: OOP languages support inheritance, allowing new classes to derive attributes and behaviors from existing
ones, facilitating code reusability and the creation of a hierarchical organization of classes.
Structured Programming: Structured languages like C do not support inheritance. Code reuse is typically achieved
through functions and, sometimes, through copy-pasting code with modifications, which can be less efficient and
more error-prone.
Final Keyword
Final Variables: A final variable can only be initialized once. Once a final variable has been assigned, it cannot be
reassigned.
Final Methods: A final method cannot be overridden by subclasses. This ensures that the method's implementation
remains unchanged.
class Parent {
public final void display() {
System.out.println("This is a final method.");
}
}
Final Classes: A final class cannot be subclassed. This is useful when you want to prevent inheritance of a class.
Final Parameters: A final parameter cannot be changed within the method. It ensures that the parameter value remains
constant during method execution.
Insertion - End
O(1) O(1) (if position is known)
(without resizing)
For variables that are members of a class (fields), if you don’t explicitly initialize them, they are given a default
value (null for objects, 0 or 0.0 for numeric types, false for booleans) by the Java Virtual Machine (JVM). Thus, you
can use them even if you haven't assigned any initial value in your code.
Switch vs if-else
Switch is ideal for simple, direct comparisons, while if-else is necessary for more complex logical evaluations.
Usage: Use switch for straightforward equality checks against multiple constants or enums of a single variable. Use
if-else for more complex conditions involving ranges, multiple variables, or logical operations.
Readability: switch is cleaner and easier to manage with many cases. if-else is more flexible but can become
unwieldy with numerous conditions.
Performance: switch can be faster for many cases due to compiler optimizations like jump tables. if-else checks
conditions sequentially, which may be slower for large chains.
String: The String class in Java creates immutable objects. Any operation that seems to modify a String actually
creates a new String object.
Wrapper Classes: All the primitive wrapper classes (Integer, Long, Double, Float, Short, Byte, Character, Boolean)
are immutable.
BigDecimal and BigInteger: These classes are used for precise arithmetic operations, especially where standard
floating-point types like double and float cannot maintain precision. They are immutable.
LocalDate, LocalTime, and LocalDateTime: These classes from the Java 8 Date-Time API are immutable and
thread-safe, designed to replace the older mutable classes like java.util.Date and Calendar.
UUID: The universally unique identifier class is immutable.
Path: Represents a system-independent path, part of the NIO.2 upgrade to Java's file I/O capabilities, is immutable.
String s = "initial";
s = s.concat(" more"); // 's' now points to a new string object, original string is unchanged.
Key Points:
Note:
Reference Assignment: When you assign one reference variable to another, both references point to the
same object.
Object State: Changes made through one reference to the object’s state are visible through the other
reference because they both point to the same object.
No Pointer Arithmetic: While you can change what object a reference points to, you cannot perform
arithmetic on the reference itself as you would with pointers in C/C++.
Like, can you refer to a Dog and then five minutes later refer to a Car? Of course not. Once I’m declared,
that’s it. If I’m a Dog remote control then I’ll never be able to refer to anything but a Dog. But I can be
referring to one Dog, and then five minutes later I can refer to some other Dog. As long as it’s a Dog, I can
be redirected. if I’m marked as final, then once I am assigned a Dog, I can never be reprogrammed to
anything else but that one and only Dog.
Primitives Cannot Be Null: Primitive types always have a value and cannot be assigned null.
Garbage Collection: Only applies to objects (reference types) on the heap. When an object becomes unreachable
(no references pointing to it), it becomes eligible for garbage collection.
What Happens When a Primitive is "Null"? Primitives are stored on the stack (if they are local variables) or within
the object's memory space on the heap (if they are instance variables). Since primitives cannot be null, the concept
of garbage collection does not apply to them directly. Local variables (including primitives) are automatically
managed by the stack frame. When a method call completes, its stack frame is popped from the stack, and all local
variables within that frame are discarded, effectively freeing that memory.
Stack vs ArrayDeque
Deque is like Stack & Queue combined Deque implements Queue interface
import java.util.ArrayDeque;
import java.util.Deque;
// Stack operations
deque.push(1); // Stack: 1
deque.push(2); // Stack: 2, 1
// Queue operation
deque.offer(3); // Queue/Deque: 2, 1, 3
// Queue operations
System.out.println("Polled from queue: " + deque.poll()); // Outputs 1, remaining: 3
Protected: Visible to all classes in the same package and to subclasses even if they are in different packages.
Package-Private (Default): Visible only to classes within the same package. Not visible to classes outside the
package, regardless of subclassing.
package package1;
public class A {
int defaultMember = 1; // package-private
protected int protectedMember = 2;
private int privateMember = 3;
public int publicMember = 4;
}
package package2;
import package1.A;
The concept that Java is "pass by value" refers to the way Java handles method arguments. In Java, when you pass a
variable to a method, you are actually passing a copy of the variable's value, not the actual variable itself. This is true for
both primitive data types and objects. Let's break down what this means for each type
Passing Primitives: The actual value is copied, and modifications to this value do not affect the original
variable.
Passing Objects: The reference value is copied (not the object itself), and modifications through this copied
reference can affect the original object. However, reassigning the reference to a new object does not change
where the original reference points.
Autoboxing
int i = 5;
Integer integerObject = i; // Autoboxing from int to Integer
In Java, explicit casting is necessary when you want to convert a value from a broader or higher precision type to a
narrower or lower precision type, or from a superclass to a subclass.
float f = 3.14f;
int x = (int) f; // x will equal 3
byte h = calcArea(4, 20); //This will not compile. The int value cannot be implicitly cast to byte wit
byte h = (byte) calcArea(4, 20); //This will compile. Since implicit conversion is provided for the re
Local variables, on the other hand, do not receive a default value. If you attempt to use a local variable without
initializing it, the Java compiler will issue a compilation error stating that the variable might not have been
initialized.
Method parameters are virtually the same as local variables—they’re declared inside the method. Parameters are
ALWAYS initialized, because the compiler guarantees that methods are always called with arguments that match
the parameters declared for the method, and the arguments are assigned (automatically) to the parameters. But
method parameters will never be uninitialized, so you’ll never get a compiler error telling you that a parameter
variable might not have been initialized.
int x = 0;
int z = x++; //produces: x is 1,z is 0
Useful library functions
ArrayList
interface B {
void display(); // Abstract method by default
}
class C implements A, B {
@Override
public void display() {
System.out.println("Display from C");
}
}
public class Main {
public static void main(String[] args) {
C obj = new C();
obj.display(); // No ambiguity here, calls display() from C
}
}
interface B {
default void display() {
System.out.println("Display from B");
}
}
class C implements A, B {
// The compiler requires this method to resolve the conflict
@Override
public void display() {
// Optionally, you can call a specific default method from an interface
A.super.display(); // or B.super.display();
// Or provide a completely new implementation
System.out.println("Display from C");
}
}
Object lifespan
A local variable lives only within the method that declared the variable.
An instance variable lives as long as the object does. If the object is still alive, so are its instance variables.
Life: A local variable is alive as long as its Stack frame is on the Stack. In other words, until the method
completes.
Scope: A local variable is in scope only within the method in which the variable was declared. When its own
method calls another, the variable is alive, but not in scope until its method resumes. You can use a variable
only when it is in scope.
public class Life {
int size;
// Variable ‘s’ (this time a method parameter) is in scope only within the setSize() method.
// But instance variable size is scoped to the life of the object as opposed to the life of the method
Static variables
instance variables: 1 per instance; static variables: 1 per class
Without an instance reference, static methods cannot directly access instance variables or instance methods
because those require an instance of the class to determine which object's variables or methods to use.
Duck.java:6: non-static variable size cannot be referenced from a static context
Duck.java:6: non-static method getSize() cannot be referenced from a static context
Instance Variables: Each instance (object) of the class has its own separate copy of these variables. They are
defined without the static keyword.
Class Variables (Static Variables): These are shared among all instances of the class. They are defined with
the static keyword.
In Java, if you have a local variable, method parameter, or field with the same name as a class-level variable
(static or instance), the local scope will shadow the class-level variable. This means that within the method, the
local variable or parameter takes precedence over the class-level variable.
class Example {
int instanceVariable = 10; // Instance variable
static int staticVariable = 20; // Static variable
// Instance method
void instanceMethod() {
System.out.println("Instance method");
}
// Static method
static void staticMethod() {
// Can access static variable
System.out.println("Static variable: " + staticVariable);
class Example {
int instanceVariable = 10; // Instance variable
static int staticVariable = 20; // Static variable
// Instance method
void instanceMethod() {
System.out.println("Instance method");
}
// Static method
static void staticMethod() {
// Create an instance of Example
Example example = new Example();
// Constructor
public CombinedExample() {
this.instanceVariable = Math.random();
System.out.println("Constructor executed.");
}
Exception
Throwable
├── Error
│ ├── LinkageError
│ ├── VirtualMachineError
│ │ ├── OutOfMemoryError
│ │ ├── StackOverflowError
│ ├── AssertionError
├── Exception
│ ├── RuntimeException
│ │ ├── ArithmeticException
│ │ │ ├── NullPointerException
│ │ │ ├── IndexOutOfBoundsException
│ │ │ ├── IllegalArgumentException
│ │ │ ├── IllegalStateException
│ │ │ ├── ClassCastException
│ │ │ ├── ArrayStoreException
│ │ │ ├── UnsupportedOperationException
│ ├── IOException
│ │ ├── FileNotFoundException
│ │ ├── EOFException
│ ├── SQLException
│ ├── ClassNotFoundException
│ ├── InterruptedException
│ ├── NoSuchMethodException
│ ├── IllegalAccessException
│ ├── InstantiationException
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
bufferedReader.close();
}
// A method with a try block and a catch block can optionally declare the exception.
// Theoretically yes, but practical makes no sense, because declaring an exception that is fully handl
// If a method handles an exception internally, it does not need to declare it in its signature; the t
SOLID
Single Responsibility Principle (SRP)
class Book {
private String title;
private String author;
// the Shape class is open for extension (new shapes can be added by extending Shape) but closed for m
// Software entities (classes, modules, functions, etc.) should be open for extension but closed for m
abstract class Shape {
abstract void draw();
}
class ShapeDrawer {
public void drawShape(Shape shape) {
shape.draw();
}
}
// Violating OCP
class ShapeDrawer {
public void drawShape(Object shape) {
if (shape instanceof Circle) {
System.out.println("Drawing Circle");
} else if (shape instanceof Rectangle) {
System.out.println("Drawing Rectangle");
}
// Every time a new shape is added, this method needs to be modified.
}
}
// Objects of a superclass should be replaceable with objects of a subclass without affecting the func
// A better design would involve separating the flying behavior into its own interface.
class Bird {
public void fly() {
System.out.println("Flying");
}
}
interface MultiFunctionDevice {
void print(Document document);
void scan(Document document);
void fax(Document document);
}
class SimplePrinter implements MultiFunctionDevice {
@Override
public void print(Document document) {
System.out.println("Printing document");
}
@Override
public void scan(Document document) {
// Not needed by SimplePrinter but must be implemented
}
@Override
public void fax(Document document) {
// Not needed by SimplePrinter but must be implemented
}
}
class Notification {
private MessageService messageService;
// Constructor Injection
public Notification(MessageService messageService) {
this.messageService = messageService;
}
// In this violating example, the Notification class directly depends on the EmailService concrete cla
// making it hard to switch to a different messaging service without modifying the Notification class
class Notification {
private EmailService emailService = new EmailService(); // Direct dependency on concrete class
class EmailService {
public void sendMessage(String message, String receiver) {
System.out.println("Email sent to " + receiver + " with message: " + message);
}
}
CRUD / ACID
CRUD is an acronym for the four basic types of operations you can perform on data stored in a database: Create,
Read, Update, and Delete.
ACID is an acronym for the set of properties that guarantee database transactions are processed reliably. ACID
stands for Atomicity, Consistency, Isolation, and Durability.
Atomicity: Ensures that each transaction is treated as a single "unit", which either completely succeeds or
completely fails. If any part of the transaction fails, the entire transaction is rolled back (When transferring
money between bank accounts, the operation must ensure that money is debited from one account and
credited to another, or neither action happens at all).
Consistency: Ensures that a transaction brings the database from one valid state to another, maintaining
database invariants (In a banking system, ensuring that the total amount of money remains constant after a
transaction).
Isolation: Ensures that concurrent transactions do not affect each other. Intermediate state of a transaction is
invisible to others until it's complete (Two transactions that update different rows in the same table will not
interfere with each other). By adhering to CRUD operations and ACID principles, database systems ensure
reliable, consistent, and safe handling of data.
Durability: Ensures that once a transaction is committed, it remains so, even in the case of a system failure
(Once a transaction to add money to a bank account is committed, the new balance will be stored
permanently).
By adhering to CRUD operations and ACID principles, database systems ensure reliable, consistent, and safe
handling of data.
Deadlock in a database occurs when two or more transactions are waiting for each other to release locks on
resources, creating a cycle of dependencies that prevents any of them from proceeding.
SQL
SELECT department, SUM(salary) AS total_salary
FROM Employee
WHERE department <> 'Finance'
GROUP BY department
HAVING SUM(salary) > 10000
ORDER BY total_salary DESC, department ASC;
Coupling / Cohesion
Low coupling means that modules are largely independent of each other, which enhances modularity and makes
the system easier to maintain and extend.
@Override
public String getName() {
return name;
}
@Override
public double getPrice() {
return price;
}
@Override
public String getCategory() {
return category;
}
}
public Invoice() {
products = new ArrayList<>();
}
Cohesion refers to how closely related the responsibilities and functionalities of a single module or class are. High
cohesion means that the methods and attributes of a class are highly related and focused on a single task or group
of related tasks. Low cohesion means that a class is trying to do too many unrelated things, making it harder to
understand and maintain.
public Invoice() {
items = new ArrayList<>();
totalAmount = 0.0;
}