CodeGym /مدونة جافا /Random-AR /واجهة خارجية في جافا
John Squirrels
مستوى
San Francisco

واجهة خارجية في جافا

نشرت في المجموعة
أهلاً! سنستمر اليوم في التعرف على التسلسل وإلغاء تسلسل كائنات Java. تعرفنا في الدرس الأخير على واجهة العلامة القابلة للتسلسل ، واستعرضنا أمثلة على استخدامها، وتعلمنا أيضًا كيف يمكنك استخدام الكلمة الأساسية العابرة للتحكم في عملية التسلسل. حسنًا، إن القول بأننا "نسيطر على العملية" قد يكون مبالغة في تقديرها. لدينا كلمة رئيسية واحدة، ومعرف إصدار واحد، وهذا كل ما في الأمر. بقية العملية مخفية داخل جافا، ولا يمكننا الوصول إليها. وبطبيعة الحال، من حيث الراحة، وهذا أمر جيد. ولكن لا ينبغي للمبرمج أن يسترشد فقط براحته الخاصة، أليس كذلك؟ :) هناك عوامل أخرى تحتاج إلى أخذها في الاعتبار. لهذا السبب فإن إمكانية التسلسل ليست هي الآلية الوحيدة للتسلسل وإلغاء التسلسل في Java. اليوم سوف نتعرف على الواجهة القابلة للتحويل . لكن قبل أن نبدأ بدراستها قد يكون لديك سؤال معقول: لماذا نحتاج إلى آلية أخرى؟ Serializableهل قامت بوظيفتها، وما الذي لا يعجبك في التنفيذ التلقائي للعملية برمتها؟ وكانت الأمثلة التي نظرنا إليها أيضًا غير معقدة. إذا ما هي المشكلة؟ لماذا نحتاج إلى واجهة أخرى لنفس المهام بشكل أساسي؟ والحقيقة هي أن Serializableلديه العديد من أوجه القصور. نذكر بعضًا منها:
  1. أداء. تتمتع الواجهة Serializableبالعديد من المزايا، ولكن من الواضح أن الأداء العالي ليس واحدًا منها.

    تقديم الواجهة الخارجية - 2

    أولاً، Serializable يُنشئ التنفيذ الداخلي لـ s كمية كبيرة من معلومات الخدمة وجميع أنواع البيانات المؤقتة.

    ثانيًا، Serializable يعتمد على Reflection API (ليس عليك التعمق في هذا الأمر الآن؛ يمكنك قراءة المزيد في وقت فراغك، إذا كنت مهتمًا). يتيح لك هذا الشيء القيام بالأشياء التي تبدو مستحيلة في Java: على سبيل المثال، تغيير قيم الحقول الخاصة. يحتوي CodeGym على مقالة ممتازة حول Reflection API . يمكنك أن تقرأ عنها هناك.

  2. المرونة. نحن لا نتحكم في عملية التسلسل وإلغاء التسلسل عندما نستخدم الواجهة Serializable.

    من ناحية، إنه مريح للغاية، لأنه إذا لم نكن مهتمين بشكل خاص بالأداء، فيبدو من الجيد ألا نضطر إلى كتابة التعليمات البرمجية. ولكن ماذا لو كنا بحاجة حقًا إلى إضافة بعض الميزات الخاصة بنا (سنقدم مثالاً أدناه) إلى منطق التسلسل؟

    في الأساس، كل ما علينا التحكم في العملية هو الكلمة transientالرئيسية لاستبعاد بعض البيانات. هذا كل شيء. هذا هو صندوق أدواتنا بأكمله :/

  3. حماية. هذا البند مشتق جزئيا من البند السابق.

    لم نقضي الكثير من الوقت في التفكير في هذا الأمر من قبل، ولكن ماذا لو كانت بعض المعلومات في صفك غير مخصصة لأعين وآذان الآخرين المتطفلين؟ مثال بسيط هو كلمة المرور أو بيانات المستخدم الشخصية الأخرى، والتي تخضع في عالم اليوم لمجموعة من القوانين.

    إذا استخدمنا Serializable، لا يمكننا فعل أي شيء حيال ذلك. نحن نتسلسل كل شيء كما هو.

    ولكن إذا قمنا بذلك بالطريقة الصحيحة، فيجب علينا تشفير هذا النوع من البيانات قبل كتابتها إلى ملف أو إرسالها عبر الشبكة. ولكن Serializableلا يجعل هذا ممكنا.

تقديم الواجهة الخارجية - 3حسنًا، دعونا أخيرًا نرى كيف سيبدو الفصل إذا استخدمنا الواجهة Externalizable.
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class UserInfo implements Externalizable {

   private String firstName;
   private String lastName;
   private String superSecretInformation;

private static final long SERIAL_VERSION_UID = 1L;

   // ...constructor, getters, setters, toString()...

   @Override
   public void writeExternal(ObjectOutput out) throws IOException {

   }

   @Override
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

   }
}
كما ترون، لدينا تغييرات كبيرة! السبب الرئيسي واضح: عند تنفيذ Externalizableالواجهة، يجب عليك تنفيذ طريقتين مطلوبتين: writeExternal()و readExternal(). وكما قلنا سابقًا، فإن مسؤولية التسلسل وإلغاء التسلسل تقع على عاتق المبرمج. ولكن الآن يمكنك حل مشكلة عدم السيطرة على العملية! تتم برمجة العملية برمتها مباشرة بواسطتك. وبطبيعة الحال، هذا يسمح بآلية أكثر مرونة. بالإضافة إلى ذلك، تم حل مشكلة الأمان. كما ترون، يحتوي فصلنا على حقل بيانات شخصية لا يمكن تخزينه بدون تشفير. الآن يمكننا بسهولة كتابة التعليمات البرمجية التي تلبي هذا القيد. على سبيل المثال، يمكننا أن نضيف إلى فصلنا طريقتين خاصتين بسيطتين لتشفير وفك تشفير البيانات الحساسة. سنقوم بكتابة البيانات إلى الملف وقراءتها من الملف في شكل مشفر. سيتم كتابة بقية البيانات وقراءتها كما هي :) ونتيجة لذلك، يبدو صفنا كما يلي:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Base64;

public class UserInfo implements Externalizable {

   private String firstName;
   private String lastName;
   private String superSecretInformation;

   private static final long serialVersionUID = 1L;

   public UserInfo() {
   }

   public UserInfo(String firstName, String lastName, String superSecretInformation) {
       this.firstName = firstName;
       this.lastName = lastName;
       this.superSecretInformation = superSecretInformation;
   }

   @Override
   public void writeExternal(ObjectOutput out) throws IOException {
       out.writeObject(this.getFirstName());
       out.writeObject(this.getLastName());
       out.writeObject(this.encryptString(this.getSuperSecretInformation()));
   }

   @Override
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
       firstName = (String) in.readObject();
       lastName = (String) in.readObject();
       superSecretInformation = this.decryptString((String) in.readObject());
   }

   private String encryptString(String data) {
       String encryptedData = Base64.getEncoder().encodeToString(data.getBytes());
       System.out.println(encryptedData);
       return encryptedData;
   }

   private String decryptString(String data) {
       String decrypted = new String(Base64.getDecoder().decode(data));
       System.out.println(decrypted);
       return decrypted;
   }

   public String getFirstName() {
       return firstName;
   }

   public String getLastName() {
       return lastName;
   }

   public String getSuperSecretInformation() {
       return superSecretInformation;
   }
}
لقد قمنا بتنفيذ طريقتين تستخدمان نفس ObjectOutputالمعلمات ObjectInputالتي التقينا بها بالفعل في الدرس حول Serializable. في اللحظة المناسبة، نقوم بتشفير أو فك تشفير البيانات المطلوبة، ونستخدم البيانات المشفرة لإجراء تسلسل لكائننا. دعونا نرى كيف يبدو هذا في الممارسة العملية:
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

       UserInfo userInfo = new UserInfo("Paul", "Piper", "Paul Piper's passport data");

       objectOutputStream.writeObject(userInfo);

       objectOutputStream.close();

   }
}
في encryptString()الأساليب decryptString()، أضفنا على وجه التحديد مخرجات وحدة التحكم للتحقق من النموذج الذي سيتم كتابة البيانات السرية وقراءتها. يعرض الكود أعلاه السطر التالي: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh نجح التشفير! تبدو المحتويات الكاملة للملف كما يلي: ¬н sr UserInfoГ!}͐џC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx الآن دعونا نحاول استخدام منطق إلغاء التسلسل الخاص بنا.
public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);


       UserInfo userInfo = (UserInfo) objectInputStream.readObject();
       System.out.println(userInfo);

       objectInputStream.close();

   }
}
حسنًا، لا شيء يبدو معقدًا هنا. يجب أن تعمل! نقوم بتشغيله ونحصل على... استثناء في مؤشر الترابط "الرئيسي" java.io.InvalidClassException: UserInfo؛ لا يوجد مُنشئ صالح تقديم الواجهة الخارجية - 4 عفوًا! :( على ما يبدو، الأمر ليس بهذه السهولة! ألقت آلية إلغاء التسلسل استثناءً وطلبت منا إنشاء مُنشئ افتراضي. أتساءل لماذا. مع ، Serializableمررنا بمنشئ واحد... :/ هنا واجهنا فارقًا بسيطًا مهمًا آخر. الفرق بين Serializableو Externalizableلا يكمن فقط في وصول المبرمج "الموسع" والقدرة على التحكم في العملية بشكل أكثر مرونة، ولكن أيضًا في العملية نفسها. قبل كل شيء، في آلية إلغاء التسلسل . عند الاستخدام Serializable، يتم تخصيص الذاكرة ببساطة للكائن، و ثم تتم قراءة القيم من المجرى واستخدامها لتعيين حقول الكائن. إذا استخدمنا Serializable، فلن يتم استدعاء مُنشئ الكائن! كل العمل يحدث من خلال الانعكاس (واجهة API للانعكاس، والتي ذكرناها باختصار في الدرس الأخير) Externalizable. "آلية إلغاء التسلسل مختلفة. يتم استدعاء المُنشئ الافتراضي أولاً. فقط بعد ذلك يتم استدعاء طريقة UserInfoالكائن الذي تم إنشاؤه readExternal(). وهو مسؤول عن تعيين حقول الكائن. ولهذا السبب يجب أن يكون لدى أي فئة تنفذ Externalizableالواجهة مُنشئ افتراضي . دعونا نضيف واحدًا إلى فصلنا UserInfoونعيد تشغيل الكود:
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);


       UserInfo userInfo = (UserInfo) objectInputStream.readObject();
       System.out.println(userInfo);

       objectInputStream.close();
   }
}
إخراج وحدة التحكم: بيانات جواز سفر Paul Piper UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'بيانات جواز سفر Paul Piper' } الآن هذا شيء مختلف تمامًا! أولاً، تم عرض السلسلة التي تم فك تشفيرها والتي تحتوي على معلومات سرية على وحدة التحكم. ثم تم عرض الكائن الذي استردناه من الملف كسلسلة! لقد نجحنا في حل جميع المشكلات :) يبدو موضوع التسلسل وإلغاء التسلسل بسيطًا، ولكن، كما ترون، كانت الدروس طويلة. وهناك الكثير الذي لم نغطيه! لا يزال هناك العديد من التفاصيل الدقيقة عند استخدام كل من هذه الواجهات. ولكن لتجنب انفجار عقلك من المعلومات الجديدة المفرطة، سأدرج بإيجاز بعض النقاط الأكثر أهمية وأعطيك روابط لقراءة إضافية. إذًا، ما الذي تحتاج إلى معرفته أيضًا؟ أولاً ، أثناء عملية التسلسل (بغض النظر عما إذا كنت تستخدم Serializableأو Externalizable)، انتبه إلى staticالمتغيرات. عند استخدام Serializable، لا يتم إجراء تسلسل لهذه الحقول على الإطلاق (وبالتالي، لا تتغير قيمها، لأن staticالحقول تنتمي إلى الفئة، وليس الكائن). ولكن عند استخدامك Externalizable، فإنك تتحكم في العملية بنفسك، لذا يمكنك من الناحية الفنية إجراء تسلسل لها. ولكننا لا ننصح بذلك، نظرًا لأنه من المحتمل أن يؤدي ذلك إلى حدوث الكثير من الأخطاء الدقيقة. ثانيًا ، يجب أيضًا الانتباه إلى المتغيرات التي تحتوي على finalالمُعدِّل. عند استخدامك Serializable، يتم إجراء تسلسل لها وإلغاء تسلسلها كالمعتاد، ولكن عند استخدامك Externalizable، فمن المستحيل إلغاء تسلسل finalمتغير ! السبب بسيط: finalتتم تهيئة كافة الحقول عند استدعاء المنشئ الافتراضي - وبعد ذلك، لا يمكن تغيير قيمتها. لذلك، لإجراء تسلسل للكائنات التي تحتوي على finalحقول، استخدم التسلسل القياسي الذي يوفره Serializable. ثالثًا ، عند استخدام الوراثة، Externalizableيجب أن تحتوي جميع الفئات التابعة التي ترث بعض الفئات أيضًا على مُنشئات افتراضية. هنا رابط لمقالة جيدة حول آليات التسلسل: حتى المرة القادمة! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION