1. Modules: Basic Concept
Java:
- A module is typically a single
.javafile containing one or more classes. - Classes are organized within packages.
// File: Animal.java
public class Animal {
public void eat() {
System.out.println("Eating");
}
}
Python:
- A module is a single
.pyfile containing functions, classes, or variables. - Modules are imported using the
importstatement.
# File: animal.py
class Animal:
def eat(self):
print("Eating")
def make_sound():
print("Sound")
2. Importing Modules
Java:
// Import specific class
import com.company.animals.Animal;
// Import all classes from package
import com.company.animals.*;
// Using the class
Animal dog = new Animal();
dog.eat();
Python:
# Import entire module
import animal
# Import specific class
from animal import Animal
# Import specific function
from animal import make_sound
# Import with alias
import animal as anim
from animal import Animal as AnimalClass
# Import all (not recommended)
from animal import *
# Using
a = Animal()
a.eat()
make_sound()
Key Differences:
- Java uses package hierarchy with dots (
com.company.animals). - Python uses module names directly or file paths.
3. Packages: Directory Structure
Java Package Structure
Directory Layout:
src/
├── com/
│ └── company/
│ ├── animals/
│ │ ├── Animal.java
│ │ └── Dog.java
│ └── utils/
│ └── Logger.java
Animal.java:
package com.company.animals;
public class Animal {
public void eat() {
System.out.println("Eating");
}
}
Dog.java:
package com.company.animals;
public class Dog extends Animal {
public void bark() {
System.out.println("Bark");
}
}
Main.java:
import com.company.animals.Animal;
import com.company.animals.Dog;
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
dog.bark();
}
}
Python Package Structure
Directory Layout:
my_project/
├── company/
│ ├── __init__.py
│ ├── animals/
│ │ ├── __init__.py
│ │ ├── animal.py
│ │ └── dog.py
│ └── utils/
│ ├── __init__.py
│ └── logger.py
├── main.py
└── requirements.txt
company/init.py:
# Makes directory a package (can be empty)
company/animals/init.py:
# Makes directory a package
from .animal import Animal
from .dog import Dog
company/animals/animal.py:
class Animal:
def eat(self):
print("Eating")
company/animals/dog.py:
from .animal import Animal
class Dog(Animal):
def bark(self):
print("Bark")
company/utils/logger.py:
def log_message(msg):
print(f"[LOG] {msg}")
main.py:
from company.animals import Dog, Animal
from company.utils.logger import log_message
if __name__ == "__main__":
dog = Dog()
dog.eat()
dog.bark()
log_message("Dog created successfully")
Key Differences:
- Java uses
/for package hierarchy, Python uses/for directories. - Python requires
__init__.pyfiles to mark directories as packages (Python 3.3+ supports namespace packages without it). - Java's package naming uses reverse domain convention (
com.company). - Python uses relative (
from .module import) and absolute (from company.animals import) imports.
4. Module/Package Naming Conventions
Java:
package com.company.projectname.modulename; // All lowercase, dot-separated
Python:
# Module: lowercase_with_underscores.py
# Package: lowercase_with_underscores/
# Class: PascalCase
# Function: lowercase_with_underscores
# Constant: UPPERCASE_WITH_UNDERSCORES
5. Relative vs Absolute Imports
Python Only:
Absolute Import:
# In company/animals/dog.py
from company.animals.animal import Animal # Full path
Relative Import:
# In company/animals/dog.py
from .animal import Animal # Import from same package
from ..utils.logger import log_message # Import from parent package
Java uses absolute imports only:
import com.company.animals.Animal; // Always absolute
6. init.py and Package Initialization
Python:
# company/__init__.py
VERSION = "1.0.0"
def init_company():
print("Initializing company")
# Executed when package is imported
print("Loading company package")
Usage:
import company
print(company.VERSION) # "1.0.0"
company.init_company()
Java: No direct equivalent; static initializers in classes serve similar purposes.
public class CompanyConstants {
public static final String VERSION = "1.0.0";
static { // Static initializer block
System.out.println("Loading company constants");
}
}
7. Distributing Code: requirements.txt vs Maven/Gradle
Python: requirements.txt
requirements.txt:
numpy==1.24.0
pandas>=1.5.0,<2.0.0
requests
Installation:
pip install -r requirements.txt
Java: Maven
pom.xml:
<project>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
</project>
8. Complete Application Structure Example
Python Application
Directory Structure:
ecommerce_app/
├── ecommerce/
│ ├── __init__.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── product.py
│ │ └── order.py
│ ├── services/
│ │ ├── __init__.py
│ │ └── order_service.py
│ ├── utils/
│ │ ├── __init__.py
│ │ └── validators.py
│ └── config.py
├── main.py
├── requirements.txt
└── README.md
ecommerce/models/product.py:
class Product:
def __init__(self, id, name, price):
self.id = id
self.name = name
self.price = price
def __str__(self):
return f"{self.name}: ${self.price}"
ecommerce/models/order.py:
class Order:
def __init__(self, order_id):
self.order_id = order_id
self.products = []
def add_product(self, product):
self.products.append(product)
def total_price(self):
return sum(p.price for p in self.products)
ecommerce/services/order_service.py:
from ..models.order import Order
from ..models.product import Product
class OrderService:
def __init__(self):
self.orders = []
def create_order(self, order_id):
order = Order(order_id)
self.orders.append(order)
return order
def get_order_total(self, order_id):
order = next((o for o in self.orders if o.order_id == order_id), None)
return order.total_price() if order else 0
ecommerce/utils/validators.py:
def is_valid_email(email):
return "@" in email
def is_valid_price(price):
return price > 0
ecommerce/config.py:
# Configuration constants
DATABASE_URL = "sqlite:///ecommerce.db"
DEBUG = True
LOG_LEVEL = "INFO"
main.py:
from ecommerce.models.product import Product
from ecommerce.services.order_service import OrderService
from ecommerce.utils.validators import is_valid_price
if __name__ == "__main__":
# Create service
service = OrderService()
# Create products
p1 = Product(1, "Laptop", 999)
p2 = Product(2, "Mouse", 25)
# Validate prices
if is_valid_price(p1.price) and is_valid_price(p2.price):
# Create order
order = service.create_order("ORD001")
order.add_product(p1)
order.add_product(p2)
print(f"Order Total: ${order.total_price()}")
from ecommerce import config
print(f"Debug Mode: {config.DEBUG}")
Java Application
Directory Structure:
ecommerce-app/
├── src/main/java/com/company/ecommerce/
│ ├── models/
│ │ ├── Product.java
│ │ └── Order.java
│ ├── services/
│ │ └── OrderService.java
│ ├── utils/
│ │ └── Validators.java
│ ├── config/
│ │ └── Config.java
│ └── Main.java
├── pom.xml
└── README.md
com/company/ecommerce/models/Product.java:
package com.company.ecommerce.models;
public class Product {
private int id;
private String name;
private double price;
public Product(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return name + ": $" + price;
}
}
com/company/ecommerce/models/Order.java:
package com.company.ecommerce.models;
import java.util.ArrayList;
import java.util.List;
public class Order {
private String orderId;
private List<Product> products;
public Order(String orderId) {
this.orderId = orderId;
this.products = new ArrayList<>();
}
public void addProduct(Product product) {
products.add(product);
}
public double getTotalPrice() {
return products.stream().mapToDouble(Product::getPrice).sum();
}
}
com/company/ecommerce/services/OrderService.java:
package com.company.ecommerce.services;
import java.util.ArrayList;
import java.util.List;
import com.company.ecommerce.models.Order;
public class OrderService {
private List<Order> orders = new ArrayList<>();
public Order createOrder(String orderId) {
Order order = new Order(orderId);
orders.add(order);
return order;
}
public double getOrderTotal(String orderId) {
return orders.stream()
.filter(o -> o.getOrderId().equals(orderId))
.map(Order::getTotalPrice)
.findFirst()
.orElse(0.0);
}
}
com/company/ecommerce/Main.java:
package com.company.ecommerce;
import com.company.ecommerce.models.Product;
import com.company.ecommerce.services.OrderService;
public class Main {
public static void main(String[] args) {
OrderService service = new OrderService();
Product p1 = new Product(1, "Laptop", 999);
Product p2 = new Product(2, "Mouse", 25);
Order order = service.createOrder("ORD001");
order.addProduct(p1);
order.addProduct(p2);
System.out.println("Order Total: $" + order.getTotalPrice());
}
}
9. Namespace/Package Comparison Table
-
Basic Unit
- Java: Class in Package
- Python: Function/Class in Module
-
Organization
- Java:
package com.company.module; - Python:
company/module/directory
- Java:
-
File/Class
- Java: One public class per file
- Python: Multiple classes/functions per file
-
Import Style
- Java:
import package.Class; - Python:
from module import Class
- Java:
-
Relative Imports
- Java: Not supported
- Python: Supported with
.and..
-
Package Init
- Java: Static initializer block
- Python:
__init__.pyfile
-
Visibility
- Java:
public,private,protected - Python: Convention-based (
_,__)
- Java:
-
Directory Structure
- Java: Mirrors package hierarchy
- Python: Same as file path structure
10. Key Differences Summary
- Namespace Convention: Java uses reverse domain naming (
com.company); Python uses lowercase with underscores. - Flexibility: Python allows multiple classes per file; Java typically one public class per file.
- Imports: Python supports relative imports; Java only absolute.
- Package Init: Python uses
__init__.pyfiles; Java uses static blocks or configuration classes. - Directory Mapping: Both map directory structure to package hierarchy, but Python is more flexible.
- Type Safety: Java enforces package/module structure at compile time; Python is runtime-based.