CodeGym /وبلاگ جاوا /Random-FA /رابط خارجی در جاوا
John Squirrels
مرحله
San Francisco

رابط خارجی در جاوا

در گروه منتشر شد
سلام! امروز ما به آشنایی با سریال سازی و سریال زدایی اشیاء جاوا ادامه خواهیم داد. در درس آخر با رابط نشانگر Serializable آشنا شدیم ، نمونه هایی از کاربرد آن را مرور کردیم و همچنین یاد گرفتیم که چگونه می توانید از کلمه کلیدی گذرا برای کنترل فرآیند سریال سازی استفاده کنید. خوب، گفتن اینکه ما "فرایند را کنترل می کنیم" ممکن است اغراق آمیز باشد. ما یک کلمه کلیدی، یک شناسه نسخه داریم، و این در مورد آن است. بقیه مراحل در جاوا پنهان است و ما نمی توانیم به آن دسترسی پیدا کنیم. البته از نظر راحتی، این خوب است. اما یک برنامه نویس نباید تنها با راحتی خودش هدایت شود، درست است؟ :) عوامل دیگری نیز وجود دارد که باید در نظر بگیرید. به همین دلیل است که Serializable تنها مکانیزم برای سریال‌سازی – deserialization در جاوا نیست. امروز با رابط Externalizable آشنا می شویم . اما قبل از شروع مطالعه آن، ممکن است یک سوال منطقی داشته باشید: چرا به مکانیسم دیگری نیاز داریم؟ Serializableکار خود را انجام داد، و چه چیزی در مورد اجرای خودکار کل فرآیند را دوست ندارید؟ و نمونه هایی که ما به آنها نگاه کردیم نیز بدون عارضه بودند. پس مشکل چیست؟ چرا اساساً برای کارهای مشابه به رابط دیگری نیاز داریم؟ واقعیت این است که Serializableچند کاستی دارد. ما تعدادی از آنها را فهرست می کنیم:
  1. کارایی. رابط Serializableکاربری مزایای زیادی دارد، اما عملکرد بالا به وضوح یکی از آنها نیست.

    معرفی رابط Externalizable - 2

    اول، Serializable پیاده سازی داخلی 's مقدار زیادی از اطلاعات سرویس و انواع داده های موقت تولید می کند.

    دوم، Serializable به Reflection API متکی است (در حال حاضر لازم نیست در این مورد غوطه ور شوید؛ اگر علاقه مند هستید، می توانید در اوقات فراغت خود بیشتر بخوانید). این چیز به شما امکان می دهد کارهای به ظاهر غیرممکن را در جاوا انجام دهید: به عنوان مثال، مقادیر فیلدهای خصوصی را تغییر دهید. CodeGym یک مقاله عالی در مورد Reflection API دارد . شما می توانید در مورد آن در آنجا بخوانید.

  2. انعطاف پذیری. هنگامی که از رابط استفاده می کنیم، فرآیند سریال سازی-جداسازی را کنترل نمی کنیم Serializable.

    از یک طرف، بسیار راحت است، زیرا اگر ما به طور خاص نگران عملکرد نباشیم، به نظر خوب می رسد که مجبور نباشیم کد بنویسیم. اما اگر واقعاً نیاز داشته باشیم برخی از ویژگی های خودمان را (در زیر مثالی ارائه می دهیم) به منطق سریال سازی اضافه کنیم، چه؟

    اساساً، تنها چیزی که ما باید فرآیند را کنترل کنیم، transientکلمه کلیدی برای حذف برخی از داده ها است. خودشه. این تمام جعبه ابزار ماست :/

  3. امنیت. این مورد تا حدی از مورد قبلی مشتق شده است.

    ما قبلاً زمان زیادی را صرف فکر کردن به این موضوع نکرده‌ایم، اما اگر برخی از اطلاعات کلاس شما برای چشمان و گوش‌های کنجکاو دیگران در نظر گرفته نشده باشد، چه؟ یک مثال ساده یک رمز عبور یا سایر داده های شخصی کاربر است که در دنیای امروزی توسط مجموعه ای از قوانین اداره می شود.

    اگر استفاده کنیم Serializable، واقعاً نمی توانیم کاری در مورد آن انجام دهیم. ما همه چیز را همانطور که هست سریال می کنیم.

    اما اگر این کار را به روش درست انجام دهیم، باید این نوع داده ها را قبل از نوشتن روی یک فایل یا ارسال آن از طریق شبکه رمزگذاری کنیم. اما Serializableاین امکان را نمی دهد

معرفی رابط Externalizable - 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 حال بیایید از منطق deserialization خود استفاده کنیم.
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();

   }
}
خوب، هیچ چیز در اینجا پیچیده به نظر نمی رسد. باید کار کند! ما آن را اجرا می کنیم و می گیریم ... Exception در thread "main" java.io.InvalidClassException: UserInfo; سازنده معتبری وجود ندارد معرفی رابط Externalizable - 4 اوه! :( ظاهراً به این آسانی نیست! مکانیسم deserialization یک استثنا انداخت و از ما خواست که یک سازنده پیش فرض ایجاد کنیم. من تعجب می کنم که چرا. با Serializable, ما بدون یک ... :/ اینجا با یک تفاوت مهم دیگر روبرو شدیم. تفاوت بین Serializableو Externalizableنه تنها در دسترسی "گسترش" برنامه نویس و توانایی کنترل انعطاف پذیرتر فرآیند، بلکه در خود فرآیند نهفته است. مهمتر از همه، در مکانیسم deserialization . هنگام استفاده Serializable، حافظه به سادگی برای شی تخصیص داده می شود، و سپس مقادیر از جریان خوانده می شوند و برای تنظیم فیلدهای شی مورد استفاده قرار می گیرند. اگر استفاده کنیم، Serializableسازنده شی فراخوانی نمی شود Externalizable! مکانیسم deserialization متفاوت است.ابتدا سازنده پیش فرض فراخوانی می شود.فقط بعد از آن متد 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();
   }
}
خروجی کنسول: اطلاعات پاسپورت پل پایپر 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