رفتن به محتوای اصلی

آموزش برنامه‌نویسی

آموزش Java با پروژهٔ فهرست کارها؛ کلاس و شیء را در عمل بفهم

یک برنامهٔ خط فرمان جاوا بساز: کلاس، سازنده، فیلد private، متد، ArrayList، ورودی، خطا و تست. با فایل کامل و تمرین توسعه.

آموزش Java با پروژهٔ فهرست کارها؛ کلاس و شیء را در عمل بفهم

تصویر اختصاصی مجلهٔ ساروی؛ ساخته‌شده با هوش مصنوعی

اگر کلاس را فقط «قالب ساخت شیء» حفظ کرده‌ای، هنوز شاید ندانی در پروژه کجا باید یک کلاس بسازی. در این درس، هر کار در فهرست ما عنوان و وضعیت دارد. همین دو داده و دو رفتار، یک مثال کوچک و روشن برای کلاس Task می‌سازند. برنامه را اجرا می‌کنیم، چند کار اضافه می‌کنیم و بعد می‌فهمیم هر بخش چه مسئولیتی دارد.

کلاس و شیء را با ساروی تمرین کن

۱. JDK و فرمان‌های اول

برای کامپایل به JDK نیاز داری؛ داشتن فقط محیط اجرای جاوا کافی نیست. javac نسخهٔ کامپایلر و java نسخهٔ محیط اجرا را نشان می‌دهد. این نمونه با Java 17 یا جدیدتر نوشته شده و به کتابخانهٔ بیرونی نیاز ندارد.

نام فایل Main.java با نام کلاس public Main باید هماهنگ باشد. پس از کامپایل، فایل class ساخته می‌شود. هنگام اجرای java Main پسوند .class را نمی‌نویسیم.

terminal.txt text
javac --version
java --version
javac Main.java
java Main --self-test
# Java tasks: all checks passed
java Main

۲. شیء Task چه چیزی می‌داند؟

هر Task عنوان و وضعیت done دارد. عنوان پس از ساخت ثابت است و با final مشخص شده. وضعیت می‌تواند از انجام‌نشده به انجام‌شده تغییر کند. این داده‌ها private هستند تا بقیهٔ برنامه مستقیماً آن‌ها را به مقدار دلخواه تغییر ندهند.

private به معنی رمزگذاری یا حفاظت از فایل داده نیست؛ یک قاعدهٔ دسترسی در ساختار برنامه است. هدف اینجا حفظ قرارداد شیء است. عنوان خالی نباید ساخته شود و تغییر وضعیت باید از رفتار مشخص finish عبور کند.

Task.java — مفهوم کلاس java
static final class Task {
    private final String title;
    private boolean done;

    Task(String title) {
        if (title == null || title.isBlank()) {
            throw new IllegalArgumentException("Title cannot be blank");
        }
        this.title = title.trim();
    }

    void finish() { done = true; }
    String summary() { return (done ? "[x] " : "[ ] ") + title; }
}
در فایل دانلودی، این کلاس داخل Main قرار دارد تا پروژه با یک فایل اجرا شود.

۳. سازنده با متد معمولی چه فرقی دارد؟

سازنده هنگام new Task("Read chapter 1") اجرا می‌شود و نامش همان نام کلاس است. نوع بازگشت ندارد. this.title به فیلد شیء اشاره می‌کند و title بدون this پارامتر سازنده است.

بررسی null قبل از isBlank مهم است؛ اگر روی null متد صدا بزنی خطا می‌گیری. بعد از تأیید، trim فضای ابتدا و انتهای عنوان را حذف می‌کند. شرط و ترتیبشان، قرارداد ساخت شیء را اجرا می‌کنند.

۴. متد، رفتار مربوط به همان داده است

finish وضعیت را به true می‌برد. اگر دوبار صدا زده شود، همچنان true می‌ماند؛ این رفتار در تست بررسی شده است. summary یک رشتهٔ قابل نمایش می‌سازد ولی در خود کلاس چاپ نمی‌کند.

جدا بودن ساخت متن از چاپ، آزمون را ساده‌تر می‌کند. می‌توانیم summary را با مقدار مورد انتظار مقایسه کنیم. اگر فردا رابط گرافیکی بسازی، لازم نیست منطق Task را به خروجی ترمینال گره زده باشی.

۵. چند شیء را در ArrayList نگه دار

List<Task> نوع رابط فهرست را نشان می‌دهد و new ArrayList<>() پیاده‌سازی انتخابی ماست. tasks.add شیء تازه را اضافه می‌کند. tasks.get(i) عضو اندیس i را برمی‌گرداند. تعداد اعضا را با size می‌گیریم.

شماره‌ای که به کاربر نشان می‌دهیم از ۱ شروع می‌شود، اما اندیس فهرست از صفر است. برای دستور done 2، عدد دو را می‌خوانیم و یک واحد کم می‌کنیم. قبل از get محدوده را بررسی می‌کنیم؛ اینجا یکی از رایج‌ترین خطاهای مبتدی رخ می‌دهد.

۶. ورودی را به چند فرمان محدود کن

فرمان‌ها عبارت‌اند از add TITLE، list، done NUMBER و exit. Scanner هر خط را می‌خواند. با startsWith تشخیص می‌دهیم ادامهٔ خط عنوان است یا شماره. ورودی ناشناخته پیام روشن می‌گیرد.

این یک مفسر سادهٔ فرمان است، نه تحلیل‌گر کامل زبان. برای پروژهٔ اول کافی است. اگر عنوان با فاصله آغاز شود یا فرمان تنها باشد، قرارداد باید مشخص باشد؛ فایل کامل عنوان خالی را رد می‌کند و خطای شماره را مدیریت می‌کند.

session.txt text
add Read chapter 1
add Solve Two Sum
list
# 1. [ ] Read chapter 1
# 2. [ ] Solve Two Sum
done 2
list
# 1. [ ] Read chapter 1
# 2. [x] Solve Two Sum
exit

۷. رشته‌ها را با equals مقایسه کن

برای مقایسهٔ محتوای String از equals استفاده کرده‌ایم. == روی referenceها می‌تواند پرسش متفاوتی جواب بدهد: آیا هر دو به همان شیء اشاره می‌کنند؟ بعضی رشته‌ها به خاطر اشتراک داخلی ممکن است با == هم برابر دیده شوند و این خطا را پنهان کنند.

Integer.parseInt متن شماره را به عدد تبدیل می‌کند و در ورودی خراب، NumberFormatException می‌دهد که زیرمجموعهٔ IllegalArgumentException است. بلوک catch پیام قابل فهم نشان می‌دهد و حلقه را ادامه می‌دهد؛ کل برنامه با یک فرمان اشتباه بسته نمی‌شود.

۸. ماندگاری داده هنوز اضافه نشده است

با خروج از برنامه، فهرست داخل حافظه از بین می‌رود. این رفتار در نسخهٔ اول مشخص و ساده است. اگر می‌خواهی کارها باقی بمانند، باید قالب فایل و زمان ذخیره را طراحی کنی.

تمرین پیشنهادی: هر کار را با عنوان و done در یک فایل متنی ذخیره کن. قبل از پیاده‌سازی، عنوان حاوی جداکننده، فایل خالی، فایل خراب و نوشتن نیمه‌کاره را در نظر بگیر. فقط اضافه کردن یک write، کل مسئلهٔ ذخیره‌سازی را حل نمی‌کند.

۹. آزمون رفتار کلاس

فایل با --self-test بررسی می‌کند عنوان trim شود، وضعیت اولیه false باشد، finish کار کند و عنوان خالی رد شود. از check صریح استفاده کرده‌ایم تا فعال بودن آزمون به گزینهٔ -ea وابسته نباشد.

تمرین دوم: متد reopen اضافه کن. اول یک تست بنویس که کار انجام‌شده را دوباره باز کند. بعد تصمیم بگیر دوبار reopen چه معنایی دارد. همین سؤال کوچک، طراحی رفتار را ملموس می‌کند.

۱۰. Java را با توضیح شیءها از ساروی یاد بگیر

کد را بده و بخواه برای دو Task متفاوت، مقدار فیلدها را قبل و بعد از finish نشان دهد. اگر توضیح فقط تعریف کلاس بود، یک مثال با دو شیء و دو عنوان بخواه.

برای مقایسهٔ سبک زبان‌ها، پایتون روی فایل و دیکشنری تمرکز دارد و C++ روی نوع و عمر داده. لازم نیست هر سه را هم‌زمان یاد بگیری؛ یکی را با پروژه تا پایان ببر و بعد تفاوت‌ها را مقایسه کن.

حالا نوبت توست

این کار را خودت انجام بده.

  • Main.java را کامپایل و self-test را اجرا کن.
  • دو Task بساز و فقط یکی را finish کن.
  • برای reopen اول رفتار و تست بنویس.

برای بررسی بیشتر

Java: کلاس‌ها و شیءهاJava: مبانی زبان

در انجام این کار به مشکل خوردی؟

از پشتیبانی کمک بگیر