JavaRush /Java Blog /Random-ID /Apa yang disembunyikan pengubah sementara di Java?
Анзор Кармов
Level 31
Санкт-Петербург

Apa yang disembunyikan pengubah sementara di Java?

Dipublikasikan di grup Random-ID
Halo! Pada artikel hari ini, kita akan melihat pengubah sementara di Java. Mari kita bahas mengapa pengubah ini diperlukan dan bagaimana menggunakannya dengan benar. Pergi! Apa yang disembunyikan pengubah sementara di Java - 1

Mari kita ingat serialisasi

Pengubah transientdigunakan dalam proses serialisasi dan deserialisasi objek. Jadi mari kita bahas secara singkat tentang hal ini terlebih dahulu. Apa yang disembunyikan pengubah sementara di Java - 2Misalkan kita memiliki suatu objek, dan objek tersebut memiliki bidang, yang masing-masing memiliki nilai tertentu. Semua ini disebut keadaan obyek. Serialisasi adalah konversi status objek menjadi urutan byte. Byte ini biasanya disimpan dalam beberapa file. Deserialisasi adalah proses sebaliknya. Bayangkan kita membuat serial suatu objek menjadi byte dan menyimpan kumpulan byte ini dalam beberapa file. Saat melakukan deserialisasi, program memerlukan:
  1. Membaca satu set byte dari sebuah file.
  2. Buatlah objek awal dari kumpulan byte ini dan atur setiap bidang ke nilai yang dimiliki objek tersebut pada saat serialisasi.
Kapan hal ini berguna? Misalnya, ketika kita ingin program menyimpan statusnya saat dimatikan dan memulihkannya saat dihidupkan lagi. Saat Anda mematikan IntelliJ IDEA, kemungkinan besar Anda akan memiliki tab dan kelas yang sama yang terbuka saat Anda menyalakannya lagi.

Mari kita ingat serialisasi dalam praktiknya

Nah, sekarang mari kita lihat serialisasi dalam praktiknya. Jika Anda ingin memahami topik ini dengan lebih baik, kami sarankan membaca materi Serialisasi dan deserialisasi di Java . Nah, pada artikel kali ini kita akan membahasnya dan langsung ke contohnya. Katakanlah kita memiliki kelas Userdengan sekumpulan beberapa bidang, pengambil dan penyetel, dan sebuah metode 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 + '\'' +
                '}';
    }
}
Kami ingin membuat serialisasi objek kelas ini di masa mendatang. Mari kita tulis metode yang mengambil objek Userdan string path- jalur ke file tempat kita akan menyimpan byte:
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();
        }
    }
}
Kami juga akan menulis metode deserialisasi. Metode ini mengambil string path(jalur ke file tempat objek akan "dimuat") dan mengembalikan objek bertipe 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();
        }
    }
}
Semua alat siap digunakan. Saatnya membagi byte menjadi atom . Mari kita menulis sebuah metode maindi mana kita membuat objek kelas Userdan membuat cerita bersambung. Kemudian kita akan memuatnya dan membandingkannya dengan aslinya:
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");
}
Jika kita menjalankan metode ini, kita akan melihat output berikut:
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'}
Seperti yang Anda lihat dari keluarannya, objek-objeknya identik. Tapi ada sedikit tapi... Dan di sinilah rasa malu orang Spanyol transient ikut berperan .

Pengubah (akhirnya)transient

Adakah yang bingung karena kami menyimpan kata sandi pengguna? Terutama kata sandi seperti itu... Ya, ya, kami membuatnya sendiri, tapi tetap saja... Terkadang ada situasi ketika beberapa bidang tidak dapat diserialkan, atau lebih baik tidak melakukan ini. Pada contoh di atas, saya ingin menyimpan semua kolom kecuali kata sandi. Bagaimana cara mencapainya? Jawaban: gunakan pengubah transient. transientadalah pengubah yang ditempatkan sebelum bidang kelas (mirip dengan pengubah lain seperti public, finaldll.) untuk menunjukkan bahwa bidang tersebut tidak boleh diserialkan. Bidang yang ditandai dengan kata kunci transienttidak berseri. Sekarang mari kita edit contoh dengan pengguna kita untuk memperbaiki kebingungan kecil dan tidak menyimpan kata sandi pengguna. Untuk melakukan ini, tandai bidang yang sesuai di kelas dengan kata kunci 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...
     */
}
Jika kita menjalankan kembali metode dari contoh di atas main, kita akan melihat bahwa kata sandi tidak disimpan:
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'}
Hebat, kami mencapai tujuan kami dan tidak menyimpan informasi rahasia. Terutama informasi semacam ini... (maaf)

Kapan menggunakan sementara?

Contoh dengan pengguna diperlukan untuk menyelami konteks serialisasi. Sekarang mari kita bicara lebih spesifik tentang kapan harus menggunakan modifier transient.

  • Bidang yang dihitung secara terprogram

Beberapa kelas terkadang memiliki bidang yang dihitung berdasarkan bidang lain atau informasi lainnya. Bisa dikatakan, mereka dihitung dengan cepat. Sebagai contoh bidang tersebut, mari kita bayangkan pemesanan di toko online atau layanan pesan-antar makanan. Setiap pesanan antara lain informasi terdiri dari daftar barang dan total biaya. Ini, pada gilirannya, terdiri dari total biaya setiap produk. Ternyata biaya akhir tidak boleh ditetapkan “dengan tangan”: biaya tersebut harus dihitung secara terprogram, menjumlahkan harga semua barang. Bidang seperti ini yang harus dihitung secara terprogram tidak perlu diserialkan. Oleh karena itu, kami menandainya dengan pengubah transient.
class Order implements Serializable {

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

}

  • Bidang dengan informasi pribadi

Ada juga beberapa kelas yang menyimpan informasi pribadi. Kami melihat contoh kelas seperti itu di awal artikel. Anda tidak boleh membiarkan informasi tersebut bocor di luar JVM. Oleh karena itu, bidang dengan data tersebut harus ditandai dengan pengubah transientjika Anda ingin membuat serialisasi kelas tersebut.

  • Bidang yang tidak mengimplementasikan antarmukaSerializable

Terkadang suatu kelas berisi field – objek dari kelas lain yang tidak mengimplementasikan antarmuka Serializable. Contoh bidang tersebut adalah logger, aliran I/O, objek yang menyimpan koneksi database, dan kelas utilitas lainnya. Jika Anda mencoba membuat serialisasi objek yang berisi bidang yang tidak dapat diserialkan, Anda akan menerima kesalahan java.io.NotSerializableException. Untuk menghindari hal ini, semua bidang yang tidak mengimplementasikan antarmuka Serializableharus ditandai dengan pengubah 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();
            }
        }
    }
}

  • Bidang dengan informasi tentang status objek

Nah, satu hal terakhir. Tidak perlu membuat serial bidang yang bukan bagian dari informasi status objek. Contoh di atas termasuk dalam aturan ini. Tapi Anda juga bisa memasukkan di sini semua bidang lain yang ditambahkan untuk debugging atau untuk melakukan beberapa jenis fungsi layanan yang tidak membawa informasi tentang keadaan objek.

transientDanfinal

Hasil

Itu saja. Hari ini kita berbicara tentang pengubah transient:
  1. Kami ingat serialisasi dalam teori dan praktik.
  2. Kami menyadari bahwa agar tidak membuat serialisasi beberapa bidang kelas, bidang tersebut perlu ditandai dengan pengubah transient.
  3. Kami membahas dalam situasi apa pengubah ini harus digunakan. Ada empat situasi seperti itu:
    1. bidang yang dihitung secara terprogram;
    2. bidang yang berisi informasi rahasia;
    3. bidang yang tidak mengimplementasikan antarmuka Serializable;
    4. bidang yang bukan bagian dari status objek.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION