سلام! در مقاله امروز ما به اصلاح کننده گذرا در جاوا خواهیم پرداخت. بیایید در مورد چرایی نیاز به این اصلاح کننده و نحوه استفاده صحیح از آن صحبت کنیم. برو!
اتم ها تقسیم کنیم . بیایید متدی بنویسیم شرم اسپانیایی اصلاح کننده (در نهایت)
آیا کسی گیج شده بود که رمز عبور کاربر را ذخیره کرده ایم؟ مخصوصا همچین پسوردی... بله، بله، خودمان به آن رسیدیم، اما باز هم... گاهی اوقات شرایطی پیش می آید که برخی از فیلدها را نمی توان سریال کرد یا بهتر است این کار را انجام ندهید. در مثال بالا، من می خواهم تمام فیلدها به جز رمز عبور را ذخیره کنم. چگونه می توان به این امر دست یافت؟ پاسخ: از اصلاح کننده استفاده کنید
برخی از کلاس ها گاهی دارای فیلدهایی هستند که بر اساس فیلدهای دیگر یا اطلاعات دیگر محاسبه می شوند. آنها به اصطلاح، در پرواز محاسبه می شوند. برای مثالی از چنین زمینه ای، بیایید سفارشی را در یک فروشگاه آنلاین یا برخی از خدمات تحویل غذا تصور کنیم. هر سفارش، در میان اطلاعات دیگر، شامل لیستی از کالاها و هزینه کل است. این به نوبه خود شامل هزینه کل هر محصول است. به نظر می رسد که هزینه نهایی نباید "با دست" تنظیم شود: باید به صورت برنامه ریزی شده محاسبه شود و هزینه تمام کالاها را خلاصه کند. فیلدهایی مانند اینها که باید به صورت برنامه نویسی محاسبه شوند نیازی به سریال سازی ندارند. بنابراین، آنها را با یک اصلاح کننده علامت گذاری می کنیم
همچنین کلاس هایی وجود دارند که اطلاعات خصوصی را ذخیره می کنند. نمونه ای از چنین کلاسی را در ابتدای مقاله بررسی کردیم. شما نباید اجازه دهید چنین اطلاعاتی در خارج از JVM درز کند.
گاهی اوقات یک کلاس حاوی فیلدها - اشیاء کلاس های دیگر است که رابط را پیاده سازی نمی کنند
خوب، یک چیز آخر. نیازی به سریال سازی فیلدهایی که بخشی از اطلاعات وضعیت شی نیستند وجود ندارد. مثال های بالا مشمول این قاعده می شوند. اما میتوانید تمام فیلدهای دیگری را که برای اشکالزدایی یا انجام نوعی عملکرد سرویس اضافه شدهاند را در اینجا اضافه کنید که حاوی اطلاعاتی درباره وضعیت شی نیست.
سریال سازی را به یاد بیاوریم
اصلاح کنندهtransient
در فرآیند سریال سازی و سریال زدایی اشیاء استفاده می شود. بنابراین اجازه دهید ابتدا به طور خلاصه در این مورد صحبت کنیم. فرض کنید یک شی داریم، و آن فیلدهایی دارد که هر کدام مقداری ارزش دارند. همه اینها حالت جسم نامیده می شود. سریال سازی تبدیل حالت یک شی به دنباله ای از بایت است. این بایت ها معمولا در برخی از فایل ها ذخیره می شوند. سریال زدایی فرآیند معکوس است. بیایید تصور کنیم که یک شی را به صورت بایت سریال کرده و این مجموعه از بایت ها را در یک فایل ذخیره می کنیم. هنگام deserialization، برنامه به موارد زیر نیاز دارد:
- مجموعه ای از بایت ها را از یک فایل بخوانید.
- یک شی اولیه از این مجموعه بایت بسازید و هر فیلد را به مقداری که شی در زمان سریال سازی داشت تنظیم کنید.
سریال سازی را در عمل به یاد بیاوریم
خب حالا بیایید سریال سازی را در عمل بررسی کنیم. اگر می خواهید موضوع را بهتر درک کنید، توصیه می کنیم مطالب Serialization and deserialization در جاوا را مطالعه کنید . خوب، در این مقاله به بالا می پردازیم و مستقیماً به سراغ مثال ها می رویم. فرض کنید یک کلاس داریم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();
}
}
}
ما همچنین یک روش برای deserialization خواهیم نوشت. متد یک رشته می گیرد 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