Professional Java Practices
Organizing Java Applications with Packages
You are viewing a free preview lesson.
Lesson Overview
ছোট Java program অনেক সময় এক বা দুইটি class দিয়েই শুরু হয়।
Example:
Main.java
Course.java
কিন্তু application বড় হলে dozens বা hundreds of classes তৈরি হতে পারে।
তখন প্রশ্ন আসে:
কোন class কোথায় থাকবে?
কোন class কোন feature-এর অংশ?
কোন class অন্য class access করতে পারবে?
একই নামের class থাকলে কী হবে?
Project structure কীভাবে maintainable রাখা যায়?
Java এই organization-এর জন্য provide করে:
package
Packages শুধু folder structure নয়।
এগুলো help করে:
- Related classes group করতে
- Naming conflicts avoid করতে
- Access boundaries তৈরি করতে
- Codebase navigate করতে
- Application architecture communicate করতে
এই lesson-এ আমরা শিখব:
- Package কী
- Package declaration
- Package naming conventions
- Directory structure
import- Fully qualified class names
- Same-package access
public- Package-private access
protectedসম্পর্কে প্রয়োজনীয় context- Feature-based package organization
- Layer-based organization
- Avoiding giant packages
- Avoiding unnecessary package fragmentation
- Designing clear package boundaries
Learning Objectives
এই lesson শেষে আপনি পারবেন:
- Java package-এর purpose explain করতে
- Correct package declaration লিখতে
- Package এবং directory structure align করতে
importব্যবহার করতে- Fully qualified class name বুঝতে
- Package-private access explain করতে
- Related classes logically group করতে
- Reasonable Java project package structure design করতে
- Over-organization এবং under-organization দুইটাই avoid করতে
What Is a Package?
A package is a namespace used to organize Java types.
A type হতে পারে:
class
interface
enum
record
annotation
Example:
package io.liveklass.course;
এর অর্থ:
এই type io.liveklass.course package-এর অংশ
Basic Example
package io.liveklass.course;
public class Course {
}
The fully qualified name of this class is:
io.liveklass.course.Course
Why Packages Exist
Suppose two libraries both have:
User
Without namespaces:
Which User?
Packages make them distinct:
io.liveklass.user.User
com.example.security.User
Both classes can exist in the same application.
Package as a Namespace
Consider:
io.liveklass.course.Course
and:
io.liveklass.catalog.Course
Java considers them different types because their fully qualified names are different.
Package is part of type identity.
Package Declaration
The package declaration normally appears at the top of the file:
package io.liveklass.course;
public class Course {
}
Imports come after package declaration:
package io.liveklass.course;
import java.util.List;
public class Course {
}
Typical order:
package
imports
type declaration
Package and Directory Structure
For:
package io.liveklass.course;
the source file is normally stored under:
src/main/java/io/liveklass/course/
Example:
src/
└── main/
└── java/
└── io/
└── liveklass/
└── course/
└── Course.java
This convention is followed by standard Java build tools and IDEs.
Package Naming Convention
Java package names are normally lowercase.
Good:
io.liveklass.course
io.liveklass.enrollment
io.liveklass.shared
Avoid:
io.LiveKlass.Course
IO.LIVEKLASS.COURSE
Reverse Domain Naming
A common convention starts with a reversed domain name.
For:
liveklass.io
package root can be:
io.liveklass
Then features:
io.liveklass.course
io.liveklass.lesson
io.liveklass.enrollment
This reduces naming collisions between organizations.
Do Not Overthink the Domain Prefix
The important part is consistency.
For a learning project:
io.liveklass
is enough.
You do not need unnecessarily deep roots such as:
io.liveklass.application.backend.platform.course.management.internal.core
Long package names do not automatically create better architecture.
Package Names Should Communicate Meaning
Weak:
io.liveklass.misc
io.liveklass.stuff
io.liveklass.common2
Better:
io.liveklass.course
io.liveklass.lesson
io.liveklass.enrollment
A developer should be able to guess what kinds of classes exist in a package.
The Default Package
Java technically allows a class with no package declaration:
public class Main {
}
This is called the:
default package
Avoid it for real applications.
Why?
- Poor organization
- Difficult reuse across packages
- Build/tooling limitations
- No meaningful namespace
Use explicit packages.
What Is import?
Suppose we have:
java.util.List
Without an import:
java.util.List<String> names;
With:
import java.util.List;
you can write:
List<String> names;
Basic Import Example
package io.liveklass.course;
import java.util.ArrayList;
import java.util.List;
public class Course {
private final List<String> lessons =
new ArrayList<>();
}
Import Does Not Copy Code
Important misconception:
import java.util.List;
does not:
Load List into your class
Copy List source code
Create a dependency at runtime by itself
It mainly allows you to reference a type using its short name:
List
instead of:
java.util.List
java.lang Is Automatically Available
Types in:
java.lang
do not need explicit imports.
Examples:
String
Object
Integer
Long
RuntimeException
System
You normally do not write:
import java.lang.String;
Same-Package Types Do Not Need Import
Suppose:
package io.liveklass.course;
public class Course {
}
and:
package io.liveklass.course;
public class CourseService {
private Course course;
}
No:
import io.liveklass.course.Course;
is required because both classes belong to the same package.
Import from Another Package
Suppose:
package io.liveklass.repository;
public interface CourseRepository {
}
Then:
package io.liveklass.course;
import io.liveklass.repository.CourseRepository;
public class CourseService {
private final CourseRepository repository;
public CourseService(
CourseRepository repository
) {
this.repository =
repository;
}
}
Now the import is needed because the interface is in another package.
Fully Qualified Class Name
A fully qualified class name includes the complete package:
java.util.List
io.liveklass.course.Course
You can use it directly:
java.util.List<String> names =
new java.util.ArrayList<>();
This is valid but verbose.
When Fully Qualified Names Are Useful
Mostly when two imported types have the same short name.
Suppose:
java.util.Date
java.sql.Date
Both are named:
Date
You cannot import both and use only Date unambiguously.
You may write:
java.util.Date createdAt =
new java.util.Date();
java.sql.Date databaseDate =
...;
Wildcard Imports
Java allows:
import java.util.*;
This means types under:
java.util
can be referenced without individual imports.
But wildcard import does not recursively import subpackages.
For example:
java.util.*
does not import:
java.util.concurrent.*
Individual Imports vs Wildcards
Many teams prefer:
import java.util.List;
import java.util.Map;
import java.util.Set;
because dependencies are explicit.
Modern IDEs can manage imports automatically.
Follow project conventions.
Static Imports
Java also supports:
import static java.lang.Math.max;
Then:
int result =
max(
10,
20
);
instead of:
Math.max(
10,
20
);
Use Static Imports Carefully
They can improve readability in some contexts:
Tests
Constants
Mathematical utilities
But excessive static imports can hide where methods come from.
Weak:
save();
load();
create();
update();
if nobody can tell which class provides them.
Clarity matters more than saving a few characters.
Packages Also Affect Access
Packages are not only organizational namespaces.
They also participate in Java access control.
We already know:
public
private
protected
Java also has:
package-private
which has no explicit keyword.
Package-Private Access
Example:
package io.liveklass.course;
final class CourseValidator {
}
Notice there is no:
public
before class.
This class is package-private.
Only code in:
io.liveklass.course
can directly access it.
Package-Private Method
public final class Course {
void markInternalReviewComplete() {
}
}
Because the method has no access modifier, it is package-private.
Classes in the same package can call it.
Classes in another package cannot.
Why Package-Private Is Useful
Suppose:
CourseValidator
CourseMapper
CourseFileName
are implementation details used only inside the course package.
Making all of them:
public
exposes unnecessary API surface.
Better:
final class CourseValidator {
}
Package-private access communicates:
Internal to this package
Public API Surface
A package may contain many classes but expose only a few public ones.
Example:
io.liveklass.course
├── Course.java public
├── CourseService.java public
├── CourseValidator.java package-private
├── CourseTitleNormalizer.java package-private
└── CourseRules.java package-private
External packages depend on:
Course
CourseService
Implementation details remain internal.
Why Smaller Public Surface Is Good
Every public type creates a potential dependency.
If many unrelated classes use:
CourseTitleNormalizer
directly, later refactoring becomes harder.
Keeping internal implementation package-private reduces accidental coupling.
private
private is narrower than package-private.
Example:
public final class Course {
private String normalizeTitle(
String title
) {
return title.strip();
}
}
Only the containing class can call this method.
public
public means the type or member can generally be accessed from other packages, assuming the enclosing type is also accessible.
Example:
public final class CourseService {
public Course findCourse(...) {
}
}
protected
protected access involves:
- Same package
- Subclasses under specific inheritance rules
It is most relevant when designing inheritance.
Do not use protected simply as:
Almost public
If a class should expose behavior publicly, use public.
If it is internal to a package, package-private may be more appropriate.
Access Comparison
Simplified:
| Modifier | Same Class | Same Package | Subclass Outside Package | Other Package |
|---|---|---|---|---|
private | Yes | No | No | No |
| package-private | Yes | Yes | No | No |
protected | Yes | Yes | Yes* | No |
public | Yes | Yes | Yes | Yes |
protected has additional inheritance rules, so this table is intentionally simplified.
Packages as Design Boundaries
A package can communicate:
These types belong together
Example:
io.liveklass.course
may contain:
Course
CourseCode
CourseStatus
CourseService
This forms a meaningful feature boundary.
Organizing by Technical Layer
A common beginner project structure:
io.liveklass
├── model
├── service
├── repository
├── exception
└── util
Example:
model/
Course.java
Lesson.java
Enrollment.java
service/
CourseService.java
LessonService.java
EnrollmentService.java
repository/
CourseRepository.java
LessonRepository.java
This is called:
layer-based organization
Problem with Large Layer-Based Packages
As application grows:
model/
may contain:
Course
Lesson
Enrollment
Payment
Organization
Subscription
User
Address
...
Everything gets grouped by technical category rather than business meaning.
Finding all course-related code now requires jumping across multiple packages.
Feature-Based Organization
Another approach:
io.liveklass
├── course
├── lesson
├── enrollment
└── payment
Each feature owns related types.
Example:
course/
Course.java
CourseCode.java
CourseStatus.java
CourseRepository.java
CourseService.java
This groups code by domain capability.
Feature-Based Organization Is Often Easier to Navigate
If you are working on:
Course
most related code is near:
io.liveklass.course
Instead of jumping through:
model
service
repository
exception
validator
But Do Not Turn Every Class into Its Own Package
Over-fragmented:
io.liveklass.course.model
io.liveklass.course.model.value
io.liveklass.course.service
io.liveklass.course.service.internal
io.liveklass.course.repository
io.liveklass.course.repository.file
io.liveklass.course.validation
io.liveklass.course.validation.internal
for only six classes is unnecessary.
Start simple.
Split when the package becomes meaningfully crowded.
A Reasonable Small Application Structure
io.liveklass
├── Main.java
├── course/
│ ├── Course.java
│ ├── CourseCode.java
│ ├── CourseStatus.java
│ ├── CourseRepository.java
│ └── CourseService.java
└── enrollment/
├── Enrollment.java
├── EnrollmentRepository.java
└── EnrollmentService.java
This is enough for many small applications.
Splitting a Growing Feature
If course grows to dozens of classes, you might create:
io.liveklass.course
├── Course.java
├── CourseCode.java
├── CourseStatus.java
├── CourseService.java
├── repository/
└── storage/
Only split when it makes navigation clearer.
Package by Responsibility Inside a Feature
A larger feature may eventually look like:
io.liveklass.course
├── Course.java
├── CourseCode.java
├── CourseStatus.java
├── CourseService.java
├── CourseRepository.java
└── storage/
└── FileCourseRepository.java
This communicates:
CourseRepository = feature contract
FileCourseRepository = storage implementation
Package Dependency Direction
Package organization should also help clarify dependency direction.
Example:
course
↓
course.storage
But be careful.
If:
CourseService
directly depends on:
FileCourseRepository
then business logic becomes coupled to a particular storage implementation.
Better:
CourseService
↓
CourseRepository
↑
FileCourseRepository
Example Repository Contract in Feature Package
package io.liveklass.course;
public interface CourseRepository {
Course findByCode(
CourseCode courseCode
);
void save(
Course course
);
}
Implementation:
package io.liveklass.course.storage;
import io.liveklass.course.Course;
import io.liveklass.course.CourseCode;
import io.liveklass.course.CourseRepository;
public final class FileCourseRepository
implements CourseRepository {
}
This can make the dependency direction clearer.
Avoid util as a Dumping Ground
A package named:
util
often becomes:
Everything we did not know where to put
Example:
DateUtil
StringUtil
CourseUtil
FileUtil
ValidationUtil
PriceUtil
This can become a design smell.
Prefer Meaningful Ownership
Instead of:
util/CourseFileUtil.java
consider:
course/storage/CourseContentStorage.java
Instead of:
util/PriceUtil.java
maybe behavior belongs in:
Money
or:
CoursePrice
Not every reusable method needs a util package.
Avoid common Becoming Another Dumping Ground
Packages such as:
common
shared
core
can be legitimate.
But they should not mean:
Anything used by more than one feature
Shared code should have a clear reason for being shared.
A Better Question
Before moving a class to:
shared
ask:
Does this concept genuinely belong to multiple features?
not:
Can multiple features technically call it?
Example: Validation Helper
Suppose:
CourseTitleValidator
is only meaningful for courses.
It belongs in:
course
not:
shared.validation
just because it performs validation.
Keep Related Changes Together
Good package organization helps developers make changes locally.
Suppose a requirement says:
Course code validation changes
Ideally relevant types are near each other:
CourseCode
Course
CourseRepository
This lowers cognitive load.
Package Name Should Not Describe Implementation Forever
Example:
course.file
may be fine if it specifically contains file implementation.
But:
course.temporary
course.new
course.v2
usually indicates unstable naming.
Names should describe durable concepts.
Avoid Version Packages Unless Necessary
Weak:
io.liveklass.course.v1
io.liveklass.course.v2
for internal implementation changes.
Versioning belongs in package names only when versions are truly independent public APIs or long-lived contracts.
Circular Package Dependencies
Imagine:
course
depends on enrollment
enrollment
depends on course
Some domain relationships naturally reference each other, but excessive circular package dependencies often indicate unclear boundaries.
Example:
CourseService
calls:
EnrollmentService
and:
EnrollmentService
calls:
CourseService
This can become difficult to reason about.
Reduce Circular Dependencies with Smaller Contracts
Instead of:
EnrollmentService
depending on the entire:
CourseService
it might depend on a smaller contract:
CourseLookup
Example:
public interface CourseLookup {
boolean exists(
CourseCode courseCode
);
}
Only introduce this when there is a genuine dependency problem.
Do not create interfaces preemptively for every class.
Package Structure Should Follow the Application
There is no universal perfect package structure.
A good package structure should optimize for:
- Discoverability
- Cohesion
- Clear ownership
- Controlled dependencies
- Reasonable visibility
Not:
Maximum number of folders
Cohesion
Cohesion means related responsibilities stay together.
High cohesion:
course/
Course
CourseCode
CourseStatus
CourseService
Low cohesion:
misc/
CourseCode
CsvExporter
DateParser
PaymentValidator
Coupling
Coupling means how strongly one part depends on another.
Packages should avoid unnecessary coupling.
Example:
course
should not depend on:
file storage
HTTP controller
console printing
unless those concerns are intentionally part of the package.
Package Design and Testing
Good packages make tests easier to organize.
Typical structure:
src/
├── main/
│ └── java/
│ └── io/liveklass/course/
│ └── Course.java
└── test/
└── java/
└── io/liveklass/course/
└── CourseTest.java
The test package often mirrors production package structure.
Package-Private and Tests
If a test is declared in the same package:
package io.liveklass.course;
it can access package-private members.
This can occasionally be useful.
But do not expose internals purely for testing if behavior can be tested through the public API.
Example: Poorly Organized Application
io.liveklass
├── Main.java
├── Course.java
├── CourseService.java
├── FileHelper.java
├── Lesson.java
├── Enrollment.java
├── EnrollmentService.java
├── StringHelper.java
├── CourseRepository.java
└── FileCourseRepository.java
As the project grows, the root package becomes crowded.
Improved Structure
io.liveklass
├── Main.java
├── course/
│ ├── Course.java
│ ├── CourseCode.java
│ ├── CourseRepository.java
│ ├── CourseService.java
│ └── storage/
│ └── FileCourseRepository.java
├── lesson/
│ └── Lesson.java
└── enrollment/
├── Enrollment.java
└── EnrollmentService.java
Now feature ownership is easier to see.
Example Package Declaration
Course.java
package io.liveklass.course;
public final class Course {
}
CourseRepository.java
package io.liveklass.course;
public interface CourseRepository {
}
FileCourseRepository.java
package io.liveklass.course.storage;
import io.liveklass.course.CourseRepository;
public final class FileCourseRepository
implements CourseRepository {
}
Package-Private Helper Example
package io.liveklass.course;
final class CourseTitleNormalizer {
String normalize(
String title
) {
if (title == null) {
throw new IllegalArgumentException(
"Title is required."
);
}
return title.strip();
}
}
External code cannot directly depend on it.
Public Service Using an Internal Helper
package io.liveklass.course;
public final class CourseService {
private final CourseTitleNormalizer normalizer;
public CourseService() {
this.normalizer =
new CourseTitleNormalizer();
}
public String normalizeTitle(
String title
) {
return normalizer.normalize(
title
);
}
}
Only:
CourseService
needs to be part of the public package API.
Don't Make Everything Public
Beginner code often uses:
public class ...
public method ...
public field ...
for everything.
That creates a wide API surface.
Ask:
Does another package actually need this?
If not, package-private or private may be better.
But Don't Hide Things Arbitrarily
Making everything package-private can also hurt design.
Public application capabilities should remain public.
Example:
public interface CourseRepository
may need to be implemented by another package.
So visibility should reflect intended usage.
Naming Classes Inside Packages
If package already provides context:
io.liveklass.course
you usually do not need:
CourseCourseService
CourseCourseValidator
Use:
CourseService
CourseValidator
Avoid repetitive naming.
Avoid Generic Names Without Context
Within:
io.liveklass.course
a class named:
Manager
is still vague.
Prefer:
CourseService
CoursePublisher
CourseCatalog
Names should explain responsibility.
When Should You Create a New Package?
Useful signals:
- Package has many unrelated responsibilities
- A clear sub-capability has emerged
- Several classes are always changed together
- Internal implementation should be hidden from public feature API
- Navigation is becoming difficult
Not a good reason:
There are three classes
alone.
When Should You Merge Packages?
Consider merging if:
- Each package contains one trivial class
- Names differ but responsibilities are tightly coupled
- Developers constantly jump between packages
- Package boundaries provide no meaningful access or dependency benefit
Package Structure Evolves
A small project may start:
io.liveklass.course
Later:
io.liveklass.course.storage
io.liveklass.course.importing
This is normal.
You do not need to predict the final architecture on day one.
A Practical Starting Rule
For a small application:
Organize primarily by feature.
Keep related domain types together.
Split implementation details only when complexity justifies it.
This provides a strong default without overengineering.
Complete Example
Project:
src/main/java/
└── io/liveklass/
├── Main.java
└── course/
├── Course.java
├── CourseCode.java
├── CourseRepository.java
├── CourseService.java
└── storage/
└── InMemoryCourseRepository.java
CourseCode.java
package io.liveklass.course;
import java.util.Objects;
public final class CourseCode {
private final String value;
public CourseCode(
String value
) {
if (
value == null
|| value.isBlank()
) {
throw new IllegalArgumentException(
"Course code is required."
);
}
this.value =
value.strip()
.toUpperCase();
}
public String getValue() {
return value;
}
@Override
public boolean equals(
Object other
) {
if (this == other) {
return true;
}
if (
!(other
instanceof CourseCode courseCode)
) {
return false;
}
return value.equals(
courseCode.value
);
}
@Override
public int hashCode() {
return Objects.hash(
value
);
}
}
Course.java
package io.liveklass.course;
public final class Course {
private final CourseCode code;
private final String title;
public Course(
CourseCode code,
String title
) {
if (code == null) {
throw new IllegalArgumentException(
"Course code is required."
);
}
if (
title == null
|| title.isBlank()
) {
throw new IllegalArgumentException(
"Course title is required."
);
}
this.code = code;
this.title = title.strip();
}
public CourseCode getCode() {
return code;
}
public String getTitle() {
return title;
}
}
CourseRepository.java
package io.liveklass.course;
import java.util.List;
public interface CourseRepository {
void save(
Course course
);
Course findByCode(
CourseCode courseCode
);
List<Course> findAll();
}
CourseService.java
package io.liveklass.course;
import java.util.List;
public final class CourseService {
private final CourseRepository repository;
public CourseService(
CourseRepository repository
) {
if (repository == null) {
throw new IllegalArgumentException(
"Course repository is required."
);
}
this.repository =
repository;
}
public void createCourse(
CourseCode code,
String title
) {
Course existing =
repository.findByCode(
code
);
if (existing != null) {
throw new IllegalStateException(
"Course already exists."
);
}
repository.save(
new Course(
code,
title
)
);
}
public List<Course> findAll() {
return repository.findAll();
}
}
InMemoryCourseRepository.java
package io.liveklass.course.storage;
import io.liveklass.course.Course;
import io.liveklass.course.CourseCode;
import io.liveklass.course.CourseRepository;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public final class InMemoryCourseRepository
implements CourseRepository {
private final Map<CourseCode, Course> courses =
new LinkedHashMap<>();
@Override
public void save(
Course course
) {
courses.put(
course.getCode(),
course
);
}
@Override
public Course findByCode(
CourseCode courseCode
) {
return courses.get(
courseCode
);
}
@Override
public List<Course> findAll() {
return new ArrayList<>(
courses.values()
);
}
}
Main.java
package io.liveklass;
import io.liveklass.course.CourseCode;
import io.liveklass.course.CourseRepository;
import io.liveklass.course.CourseService;
import io.liveklass.course.storage.InMemoryCourseRepository;
public class Main {
public static void main(
String[] args
) {
CourseRepository repository =
new InMemoryCourseRepository();
CourseService service =
new CourseService(
repository
);
service.createCourse(
new CourseCode(
"JAVA-OOP"
),
"Java and OOP Foundation"
);
service.findAll()
.forEach(
course ->
System.out.println(
course.getTitle()
)
);
}
}
What This Structure Communicates
io.liveklass.course
owns:
Course domain concepts
Repository contract
Course application behavior
io.liveklass.course.storage
contains a persistence implementation.
io.liveklass
contains application entry point.
This is already enough structure for a small application.
Common Mistakes
Putting Everything in the Root Package
Becomes difficult to navigate as the codebase grows.
Creating Too Many Packages Too Early
Adds navigation overhead without improving design.
Making Every Class public
Creates unnecessary dependencies between packages.
Using util as a Dumping Ground
Often hides unclear ownership.
Grouping Only by Technical Layer Forever
Can scatter feature-related code across the codebase.
Creating Deep Package Hierarchies for Prestige
More folders do not mean better architecture.
Hardcoding Package Names Based on Temporary Implementation
Package names should represent durable concepts.
Ignoring Dependency Direction
A clean folder tree does not help if business code directly depends on infrastructure details.
Using the Default Package
Fine for tiny experiments, poor for structured applications.
Practice Exercises
Exercise 1: Create Package Structure
Organize these classes:
Course
CourseCode
CourseService
CourseRepository
FileCourseRepository
Enrollment
EnrollmentService
Main
Create a reasonable package structure.
Exercise 2: Identify Public Types
For package:
io.liveklass.course
decide whether these should be public or package-private:
Course
CourseService
CourseTitleNormalizer
CourseRepository
Explain your reasoning.
Exercise 3: Fix a Dumping-Ground Package
Given:
io.liveklass.util
├── CourseValidator
├── PaymentCalculator
├── EnrollmentFormatter
└── FileCourseRepository
Move each class to a more meaningful package.
Exercise 4: Import Practice
Given:
io.liveklass.course.Course
io.liveklass.course.CourseService
io.liveklass.repository.CourseRepository
Write the imports needed inside a class in:
io.liveklass.course
that uses all three types.
Exercise 5: Same Class Name
Your application needs:
java.util.Date
java.sql.Date
Explain how fully qualified class names can resolve the naming conflict.
Exercise 6: Package-Private Helper
Create:
CourseTitleNormalizer
that should only be used inside:
io.liveklass.course
Do not make the class public.
Predict the Result
Question 1
package io.liveklass.course;
class CourseValidator {
}
Can:
io.liveklass.payment.PaymentService
directly instantiate CourseValidator?
Answer
No.
CourseValidator is package-private.
Question 2
Two classes are both in:
io.liveklass.course
Does one need to import the other?
Answer
No.
Same-package types are directly available by short name.
Question 3
Does:
import java.util.*;
also import:
java.util.concurrent.ExecutorService
?
Answer
No.
Wildcard imports do not recursively import subpackages.
Question 4
What is the fully qualified name of:
package io.liveklass.course;
public class Course {
}
?
Answer
io.liveklass.course.Course
Question 5
Does making a class public improve its design automatically?
Answer
No.
Visibility should reflect intended usage.
Unnecessary public access increases coupling.
Knowledge Check
Question 1
What is a Java package?
Question 2
Why are packages useful?
Question 3
Where is the package declaration placed?
Question 4
Why are package names usually lowercase?
Question 5
What does an import do?
Question 6
Do classes in the same package require imports?
Question 7
What is a fully qualified class name?
Question 8
What is package-private access?
Question 9
Why avoid making every type public?
Question 10
What is the difference between feature-based and layer-based organization?
Question 11
Why can large util packages be problematic?
Question 12
What does package cohesion mean?
Question 13
Why should package boundaries influence dependency direction?
Question 14
Should every three classes get their own subpackage?
Question 15
What is a good starting rule for small Java applications?
Knowledge Check Answers
Answer 1
A namespace used to organize related Java types and participate in access control.
Answer 2
They organize code, prevent naming collisions, create visibility boundaries, and improve navigation.
Answer 3
At the top of the Java source file before imports.
Answer 4
It is the established Java naming convention and keeps package names consistent.
Answer 5
It allows a type to be referenced by its short name instead of its fully qualified name.
Answer 6
No.
Answer 7
The package name plus type name, such as:
io.liveklass.course.Course
Answer 8
Access available to types in the same package when no explicit access modifier is used.
Answer 9
Every public type creates a larger API surface and allows more code to depend on implementation details.
Answer 10
Layer-based organization groups by technical role; feature-based organization groups code by domain capability or feature.
Answer 11
They often become dumping grounds for unrelated responsibilities and hide ownership.
Answer 12
Related responsibilities are kept near each other.
Answer 13
Folder organization is useful only if dependencies also respect the intended boundaries.
Answer 14
No. Split packages only when doing so improves cohesion, visibility, or navigation.
Answer 15
Organize primarily by feature, keep closely related domain types together, and split further only when complexity justifies it.
Lesson Summary
এই lesson-এ আমরা শিখেছি:
- Packages Java typesকে namespaces-এর মধ্যে organize করে
- Package name type identity-এর অংশ
- Reverse-domain naming common convention
- Package structure সাধারণত directory structure-এর সঙ্গে align করে
importshort type names ব্যবহার করতে সাহায্য করেjava.langautomatically available- Same-package types import প্রয়োজন করে না
- Fully qualified names naming conflicts resolve করতে পারে
- Wildcard imports subpackages recursively import করে না
- Package-private access explicit keyword ছাড়াই তৈরি হয়
- Package-private types internal implementation hide করতে useful
- Everything public করা unnecessary coupling তৈরি করে
- Packages meaningful design boundaries হিসেবেও কাজ করতে পারে
- Layer-based organization simple হলেও large applications-এ feature code scatter করতে পারে
- Feature-based organization related code কাছাকাছি রাখে
- Over-fragmented package trees navigation harder করতে পারে
util,common, এবংshareddumping grounds হওয়া উচিত নয়- Package cohesion এবং dependency direction গুরুত্বপূর্ণ
- Small applications-এর জন্য simple feature-oriented structure একটি strong default
- Package organization evolve করতে পারে; final architecture day one-এ predict করার দরকার নেই
Next Lesson
পরবর্তী lesson:
Immutability and Defensive Programming
আমরা শিখব:
- Mutable vs immutable objects
- Why immutability reduces bugs
finalfields- Constructor validation
- Defensive copying
- Protecting collections
- Returning snapshots
- Immutable value objects
- Mutation leaks
- Designing classes that protect their own invariants