مرحبًا! في مقال اليوم، سنلقي نظرة على المعدل العابر في Java. دعونا نتحدث عن سبب الحاجة إلى هذا المعدل وكيفية استخدامه بشكل صحيح. يذهب!
ذرات . لنكتب طريقة العار الإسباني دوره المعدل (أخيرًا)
هل ارتبك أحد أننا حفظنا كلمة مرور المستخدم؟ خاصة كلمة المرور هذه... نعم، نعم، لقد توصلنا إليها بأنفسنا، ولكن لا يزال... في بعض الأحيان توجد مواقف لا يمكن فيها إجراء تسلسل لبعض الحقول، أو من الأفضل عدم القيام بذلك. في المثال أعلاه، أود حفظ كافة الحقول باستثناء كلمة المرور. كيفية تحقيق ذلك؟ الجواب: استخدم المعدل
تحتوي بعض الفئات في بعض الأحيان على حقول يتم حسابها بناءً على حقول أخرى أو معلومات أخرى. يتم حسابها، إذا جاز التعبير، على الطاير. ولإعطاء مثال على هذا المجال، دعونا نتخيل طلبًا في متجر عبر الإنترنت أو إحدى خدمات توصيل الطعام. يتكون كل طلب، من بين معلومات أخرى، من قائمة السلع والتكلفة الإجمالية. وهي بدورها تتكون من التكلفة الإجمالية لكل منتج. اتضح أن التكلفة النهائية لا ينبغي تحديدها "يدويًا": يجب حسابها برمجيًا، وتلخيص تكلفة جميع السلع. الحقول مثل هذه التي يجب حسابها برمجيًا لا تحتاج إلى إجراء تسلسل لها. ولذلك، فإننا نضع علامة عليها مع المعدل
هناك أيضًا بعض الفئات التي تقوم بتخزين المعلومات الخاصة. لقد نظرنا إلى مثال لمثل هذه الفئة في بداية المقال. يجب ألا تسمح بتسرب مثل هذه المعلومات خارج JVM. لذلك، يجب وضع علامة على الحقول التي تحتوي على هذه البيانات باستخدام مُعدِّل
في بعض الأحيان تحتوي الفئة على حقول - كائنات من فئات أخرى لا تقوم بتنفيذ الواجهة
حسنا، شيء أخير. ليست هناك حاجة لإجراء تسلسل للحقول التي لا تشكل جزءًا من معلومات حالة الكائن. الأمثلة المذكورة أعلاه تندرج تحت هذه القاعدة. ولكن يمكنك أيضًا تضمين جميع الحقول الأخرى المضافة هنا لتصحيح الأخطاء أو لأداء نوع من وظائف الخدمة التي لا تحمل معلومات حول حالة الكائن.
دعونا نتذكر التسلسل
يتم استخدام المعدلtransient
في عملية إجراء تسلسل وإلغاء تسلسل الكائنات. لذلك دعونا نتحدث باختصار عن هذا أولا. لنفترض أن لدينا كائنًا ما، وله حقول، لكل منها قيمة معينة. كل هذا يسمى حالة الكائن. التسلسل هو تحويل حالة الكائن إلى سلسلة من البايتات. عادةً ما يتم تخزين هذه البايتات في بعض الملفات. إلغاء التسلسل هي العملية العكسية. لنتخيل أننا قمنا بتسلسل كائن إلى بايتات وقمنا بتخزين مجموعة البايتات هذه في ملف ما. عند إلغاء التسلسل يحتاج البرنامج إلى:
- قراءة مجموعة من البايتات من ملف.
- قم بإنشاء كائن أولي من مجموعة البايتات هذه وقم بتعيين كل حقل على القيمة التي كان للكائن وقت التسلسل.
دعونا نتذكر التسلسل في الممارسة العملية
حسنا، الآن دعونا نلقي نظرة على التسلسل في الممارسة العملية. إذا كنت تريد فهم الموضوع بشكل أفضل، نوصي بقراءة المادة التسلسل وإلغاء التسلسل في 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
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
:
- تذكرنا التسلسل في النظرية والتطبيق.
- لقد أدركنا أنه من أجل عدم إجراء تسلسل لبعض حقول الفصل، يجب وضع علامة عليها باستخدام مُعدِّل
transient
. - ناقشنا المواقف التي يجب استخدام هذا المعدل فيها. كانت هناك أربع حالات من هذا القبيل:
- الحقول التي يتم حسابها برمجيا؛
- الحقول التي تحتوي على معلومات سرية؛
- الحقول التي لا تنفذ الواجهة
Serializable
; - الحقول التي لا تشكل جزءًا من حالة الكائن.
GO TO FULL VERSION