JavaRush /مدونة جافا /Random-AR /ما الذي يخفيه المعدل العابر في Java؟
Анзор Кармов
مستوى
Санкт-Петербург

ما الذي يخفيه المعدل العابر في Java؟

نشرت في المجموعة
مرحبًا! في مقال اليوم، سنلقي نظرة على المعدل العابر في Java. دعونا نتحدث عن سبب الحاجة إلى هذا المعدل وكيفية استخدامه بشكل صحيح. يذهب! ماذا يخفي المعدل العابر في Java - 1

دعونا نتذكر التسلسل

يتم استخدام المعدل transientفي عملية إجراء تسلسل وإلغاء تسلسل الكائنات. لذلك دعونا نتحدث باختصار عن هذا أولا. ماذا يخفي المعدل العابر في Java - 2لنفترض أن لدينا كائنًا ما، وله حقول، لكل منها قيمة معينة. كل هذا يسمى حالة الكائن. التسلسل هو تحويل حالة الكائن إلى سلسلة من البايتات. عادةً ما يتم تخزين هذه البايتات في بعض الملفات. إلغاء التسلسل هي العملية العكسية. لنتخيل أننا قمنا بتسلسل كائن إلى بايتات وقمنا بتخزين مجموعة البايتات هذه في ملف ما. عند إلغاء التسلسل يحتاج البرنامج إلى:
  1. قراءة مجموعة من البايتات من ملف.
  2. قم بإنشاء كائن أولي من مجموعة البايتات هذه وقم بتعيين كل حقل على القيمة التي كان للكائن وقت التسلسل.
متى قد يكون هذا مفيدا؟ على سبيل المثال، عندما نريد أن يحفظ البرنامج حالته عند إيقاف التشغيل واستعادتها في المرة التالية التي يتم فيها تشغيله. عند إيقاف تشغيل IntelliJ IDEA، سيكون لديك على الأرجح نفس علامات التبويب والفئات المفتوحة في المرة التالية التي تقوم فيها بتشغيله

دعونا نتذكر التسلسل في الممارسة العملية

حسنا، الآن دعونا نلقي نظرة على التسلسل في الممارسة العملية. إذا كنت تريد فهم الموضوع بشكل أفضل، نوصي بقراءة المادة التسلسل وإلغاء التسلسل في Java . حسنًا، في هذه المقالة سوف نتطرق إلى الأعلى وننتقل مباشرة إلى الأمثلة. لنفترض أن لدينا فئة Userتحتوي على مجموعة من الحقول والحروف والمحددات وطريقة toString:
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String firstName;
    private String lastName;
    private String email;
    private LocalDate birthDate;
    private String login;
    private String password;

    public User() {}

    public User(String firstName, String lastName, String email, LocalDate birthDate, String login, String password) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.birthDate = birthDate;
        this.login = login;
        this.password = password;
    }

    /*
        Геттеры, Сеттеры
     */

    @Override
    public String toString() {
        return "User{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", birthDate=" + birthDate +
                ", login='" + login + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
نريد إجراء تسلسل لكائنات هذه الفئة في المستقبل. لنكتب طريقة تأخذ كائنًا Userوسلسلة path- المسار إلى الملف الذي سنحفظ فيه البايتات:
static void serialize(User user, String path) throws IOException {
    FileOutputStream outputStream = null;
    ObjectOutputStream objectOutputStream = null;
    try {
        //create 2 threads to serialize the object and save it to a file
        outputStream = new FileOutputStream(path);
        objectOutputStream = new ObjectOutputStream(outputStream);

        // сохраняем an object в файл
        objectOutputStream.writeObject(user);
    } finally {
        // Закроем потоки в блоке finally
        if (objectOutputStream != null) {
            objectOutputStream.close();
        }
        if (outputStream != null) {
            outputStream.close();
        }
    }
}
سنكتب أيضًا طريقة لإلغاء التسلسل. تأخذ الطريقة سلسلة نصية path(المسار إلى الملف الذي سيتم "تحميل الكائن منه") وترجع كائنًا من النوع User:
static User deserialize(String path) throws IOException, ClassNotFoundException {
    FileInputStream fileInputStream = null;
    ObjectInputStream objectInputStream = null;

    try {

        //создаем 2 потока для десериализации an object из file
        fileInputStream = new FileInputStream(path);
        objectInputStream = new ObjectInputStream(fileInputStream);

        //загружаем an object из file
        return  (User) objectInputStream.readObject();
    } finally {
        if (fileInputStream != null) {
            fileInputStream.close();
        }
        if (objectInputStream != null) {
            objectInputStream.close();
        }
    }
}
جميع الأدوات جاهزة للاستخدام. لقد حان الوقت لتقسيم البايتات إلى ذرات . لنكتب طريقة mainنقوم من خلالها بإنشاء كائن فئة Userوإجراء تسلسل له. ثم سنقوم بتحميله ومقارنته بما كان في الأصل:
public static void main(String[] args) throws IOException, ClassNotFoundException {
    // вставьте свой путь до file
    final String path = "/home/zor/user.ser";

    // create our object
    User user = new User();
    user.setFirstName("Stefan");
    user.setLastName("Smith");
    user.setEmail("ssmith@email.com");
    user.setBirthDate(LocalDate.of(1991, 7, 16));
    user.setLogin("ssmith");
    user.setPassword("gemma_arterton_4ever_in_my_heart91");

    System.out.println("Initial user: " + user + "\r\n");


    serialize(user, path);
    User loadedUser = deserialize(path);
    System.out.println("Loaded user from file: " + loadedUser + "\r\n");
}
إذا قمنا بتشغيل الطريقة، فسنرى الإخراج التالي:
Initial user: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}

Loaded user from file: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}
كما ترون من الإخراج، الكائنات متطابقة. ولكن هناك شيء صغير ولكن... وهذا هو بالضبط المكان الذي يلعب فيه العار الإسباني دوره transient .

المعدل (أخيرًا)transient

هل ارتبك أحد أننا حفظنا كلمة مرور المستخدم؟ خاصة كلمة المرور هذه... نعم، نعم، لقد توصلنا إليها بأنفسنا، ولكن لا يزال... في بعض الأحيان توجد مواقف لا يمكن فيها إجراء تسلسل لبعض الحقول، أو من الأفضل عدم القيام بذلك. في المثال أعلاه، أود حفظ كافة الحقول باستثناء كلمة المرور. كيفية تحقيق ذلك؟ الجواب: استخدم المعدل transient. transientهو معدِّل يتم وضعه قبل حقل فئة (على غرار المعدلات الأخرى مثل public، finalوما إلى ذلك) للإشارة إلى أنه لا ينبغي إجراء تسلسل للحقل. transientلا يتم إجراء تسلسل للحقول المميزة بالكلمة الأساسية . الآن دعونا نعدل المثال مع المستخدم الخاص بنا لتصحيح الالتباس البسيط وعدم حفظ كلمة مرور المستخدم. للقيام بذلك، ضع علامة على الحقل المقابل في الفصل بالكلمة الأساسية transient:
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String firstName;
    private String lastName;
    private String email;
    private LocalDate birthDate;
    private String login;
    private transient String password;

    /*
        Конструкторы, геттеры, сеттеры, toString...
     */
}
إذا قمنا بتشغيل الطريقة من المثال أعلاه مرة أخرى main، فسنرى أن كلمة المرور لم يتم حفظها:
Initial user: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}

Loaded user from file: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='null'}
عظيم، لقد حققنا هدفنا ولا نقوم بتخزين معلومات سرية. وخاصة هذا النوع من المعلومات... (آسف)

متى تستخدم عابرة؟

كانت هناك حاجة إلى مثال مع المستخدم للتعمق في سياق التسلسل. الآن دعونا نتحدث بشكل أكثر تحديدًا عن وقت استخدام المُعدِّل transient.

  • الحقول التي يتم حسابها برمجيا

تحتوي بعض الفئات في بعض الأحيان على حقول يتم حسابها بناءً على حقول أخرى أو معلومات أخرى. يتم حسابها، إذا جاز التعبير، على الطاير. ولإعطاء مثال على هذا المجال، دعونا نتخيل طلبًا في متجر عبر الإنترنت أو إحدى خدمات توصيل الطعام. يتكون كل طلب، من بين معلومات أخرى، من قائمة السلع والتكلفة الإجمالية. وهي بدورها تتكون من التكلفة الإجمالية لكل منتج. اتضح أن التكلفة النهائية لا ينبغي تحديدها "يدويًا": يجب حسابها برمجيًا، وتلخيص تكلفة جميع السلع. الحقول مثل هذه التي يجب حسابها برمجيًا لا تحتاج إلى إجراء تسلسل لها. ولذلك، فإننا نضع علامة عليها مع المعدل transient.
class Order implements Serializable {

    private List items;
    private transient BigDecimal totalAmount; //вычисляется на ходу

}

  • الحقول التي تحتوي على معلومات خاصة

هناك أيضًا بعض الفئات التي تقوم بتخزين المعلومات الخاصة. لقد نظرنا إلى مثال لمثل هذه الفئة في بداية المقال. يجب ألا تسمح بتسرب مثل هذه المعلومات خارج JVM. لذلك، يجب وضع علامة على الحقول التي تحتوي على هذه البيانات باستخدام مُعدِّل transientإذا كنت ستقوم بإجراء تسلسل لمثل هذه الفئة.

  • الحقول التي لا تنفذ الواجهةSerializable

في بعض الأحيان تحتوي الفئة على حقول - كائنات من فئات أخرى لا تقوم بتنفيذ الواجهة Serializable. ومن أمثلة هذه الحقول المسجلات، وتدفقات الإدخال/الإخراج، والكائنات التي تخزن اتصالات قاعدة البيانات وفئات الأدوات المساعدة الأخرى. إذا حاولت إجراء تسلسل لكائن يحتوي على حقول غير قابلة للتسلسل، فسوف تتلقى خطأ java.io.NotSerializableException. لتجنب ذلك، Serializableيجب وضع علامة على كافة الحقول التي لا تنفذ الواجهة باستخدام المُعدِّل transient.
public class FileReader implements Serializable {
    // Первые 2 поля не реализуют Serializable
    // Помечаем их How transient поля
    private transient InputStream is;
    private transient BufferedReader buf;
    private String fileName;

    // Constructors, Getters, Setters

    public String readFile() throws IOException {
        try {
            is = new FileInputStream(fileName);
            buf = new BufferedReader(new InputStreamReader(is));
            String line = buf.readLine();
            StringBuilder sb = new StringBuilder();
            while (line != null) {
                sb.append(line).append("\n");
                line = buf.readLine();
            }
            return sb.toString();
        } finally {
            if (buf != null) {
                buf.close();
            }
            if (is != null) {
                is.close();
            }
        }
    }
}

  • الحقول التي تحتوي على معلومات حول حالة الكائن

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

transientوfinal

نتائج

هذا كل شئ. تحدثنا اليوم عن المعدل transient:
  1. تذكرنا التسلسل في النظرية والتطبيق.
  2. لقد أدركنا أنه من أجل عدم إجراء تسلسل لبعض حقول الفصل، يجب وضع علامة عليها باستخدام مُعدِّل transient.
  3. ناقشنا المواقف التي يجب استخدام هذا المعدل فيها. كانت هناك أربع حالات من هذا القبيل:
    1. الحقول التي يتم حسابها برمجيا؛
    2. الحقول التي تحتوي على معلومات سرية؛
    3. الحقول التي لا تنفذ الواجهة Serializable;
    4. الحقول التي لا تشكل جزءًا من حالة الكائن.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION