الملفات والمسارات ونظام الملفات
الملفات والمسارات ونظام الملفات
تمتلك Java جيلين من واجهات برمجة نظام الملفات. صنف java.io.File الأصلي موجود منذ الإصدار Java 1.0، أما حزمة java.nio.file الحديثة — التي يُشار إليها عادةً بـ NIO.2 — فقد جاءت في Java 7 وتُعوّض الأول تمامًا. فهم الجيلين معًا، وإدراك أوجه القصور في الأول، هو الأساس الذي يرتكز عليه كل ما سيأتي في هذا البرنامج التعليمي.
صنف java.io.File القديم
قبل NIO.2، كانت كل عمليات نظام الملفات تمر عبر java.io.File. كائن File في جوهره مجرد غلاف نصي حول مسار — لا يتحقق مما إذا كان المسار موجودًا فعلًا عند إنشائه.
تبدو الواجهة بسيطة، لكنها تحمل عدة مشكلات تصميمية جوهرية تعترض كل مطور يستخدمها بما يكفي:
- الإبلاغ عن الأخطاء بقيم منطقية. تُعيد معظم التوابع التي تُجري تعديلات (
mkdir()وdelete()وrenameTo()) قيمةfalseعند الفشل دون أي سبب. لا تعرف إن كانت العملية فشلت بسبب صلاحيات، أو مجلد أصل مفقود، أو حالة تسابق، أو أي شيء آخر. - فاصل المسار يعتمد على المنصة. استخدام
"/"أو"\\"مباشرة يُفسد التوافق. يساعدFile.separatorلكن يسهل نسيانه. - لا وعي بالروابط الرمزية. يتبع
Fileالروابط الرمزية بصمت بطرق قد تُفاجئك. - سرد المجلدات غير مكتمل. تُعيد
list()مصفوفةString[]أوFile[]— كافٍ للمجلدات الصغيرة، لكنه يُحمّل كل شيء في الذاكرة دفعة واحدة، مما يُشكّل مشكلة للمجلدات التي تحتوي ملايين المدخلات. - لا وصول للبيانات الوصفية. لا يمكنك قراءة صلاحيات الملف أو وقت إنشائه أو مالكه بطريقة متوافقة مع المنصات.
Files.move() في NIO.2 بدلًا منها.
واجهة NIO.2 الحديثة: Path و Files
تُقسّم NIO.2 المفهوم إلى تجريدين واضحين:
java.nio.file.Path— كائن قيمة خالص يمثل سلسلة مسار. يعرف بنية نظام الملفات (الفواصل، الجذور، النسبي مقابل المطلق) لكنه لا ينفذ I/O بنفسه.java.nio.file.Files— صنف مساعد من التوابع الساكنة التي تُنفّذ عمليات I/O الفعلية، وترمي استثناءIOExceptionالمتحقق (أو أنواعه الفرعية) عند الفشل حتى تعرف دومًا ما الذي حدث.
تحصل على Path من خلال التابع المصنع Path.of() (Java 11+) أو الأقدم Paths.get():
Path.of() في Java 11 كتابع مساعد مباشرة على الواجهة. في قواعد الكود الحديثة (Java 11+) فضّل Path.of() — فهو أوضح قراءةً ولا يتطلب استيراد صنف منفصل.
التنقل في المسارات والحل
من أبرز مزايا Path على السلاسل النصية الخام قدرتها المدمجة على حساب المسارات. يمكنك التنقل في شجرة نظام الملفات دون تسلسل نصي:
التحقق من الوجود وخصائص الملفات
يوفر صنف Files توابع شرطية تقابل توابع File القديمة، مع وصول أغنى للبيانات الوصفية:
Files.exists(). احتفظ بمعالجة IOException لحالات الفشل الاستثنائية الحقيقية — القرص ممتلئ، أخطاء الصلاحيات — لا للتحقق الروتيني من عدم وجود المسار.
التحويل بين File و Path
الكود القديم الذي يستخدم java.io.File منتشر في كل مكان — المكتبات القديمة، واجهات برمجة الطرف الثالث، وأغلفة Android قبل NIO. ستحتاج كثيرًا إلى الجسر بين العالمين:
FileSystem و FileSystems
خلف كل من Path وFiles يوجد java.nio.file.FileSystem — تجريد يتيح لك العمل مع أرشيفات ZIP وأنظمة ملفات في الذاكرة (للاختبار) وأنظمة ملفات بعيدة من خلال نفس الواجهة. يُحصل على نظام الملفات الافتراضي (قرص نظام التشغيل) عبر FileSystems.getDefault(). لن تحتاج هذا مباشرةً في معظم كود التطبيقات، لكنه يُوضّح سبب كون Path.of() اختصارًا لـ FileSystems.getDefault().getPath().
الخلاصة
صنف java.io.File القديم غلاف هش للمسارات مع أوضاع فشل صامتة، وبدون بيانات وصفية غنية أو تحكم في الروابط الرمزية. الثنائي الحديث في NIO.2 — Path (كائن قيمة للمسارات) وFiles (عمليات I/O مع استثناءات صحيحة) — يحل كل تلك المشكلات. في كل الكود الجديد استخدم Path.of() وFiles.*. عند صيانة كود قديم يعرض File، حوّل فورًا بـ file.toPath() وتعامل مع NIO.2 من تلك النقطة فصاعدًا.