JavaRush /Blog Java /Random-MS /Apakah yang disembunyikan pengubah sementara di Jawa?
Анзор Кармов
Tahap
Санкт-Петербург

Apakah yang disembunyikan pengubah sementara di Jawa?

Diterbitkan dalam kumpulan
hello! Dalam artikel hari ini, kita akan melihat pengubah sementara di Java. Mari kita bincangkan mengapa pengubah suai ini diperlukan dan cara menggunakannya dengan betul. Pergi! Apakah yang disembunyikan oleh pengubah sementara dalam Java - 1

Mari kita ingat bersiri

Pengubah suai transientdigunakan dalam proses mensiri dan menyahsiri objek. Jadi mari kita bercakap secara ringkas tentang perkara ini terlebih dahulu. Apakah yang disembunyikan pengubah sementara dalam Java - 2Katakan kita mempunyai beberapa objek, dan ia mempunyai medan, setiap satunya mempunyai beberapa nilai. Semua ini dipanggil keadaan objek. Serialisasi ialah penukaran keadaan objek kepada urutan bait. Bait ini biasanya disimpan dalam beberapa fail. Deserialisasi adalah proses sebaliknya. Mari kita bayangkan bahawa kita menyusun objek menjadi bait dan menyimpan set bait ini dalam beberapa fail. Apabila desirialisasi, program memerlukan:
  1. Baca satu set bait daripada fail.
  2. Bina objek awal daripada set bait ini dan tetapkan setiap medan kepada nilai yang dimiliki objek pada masa penyirian.
Bilakah ini berguna? Sebagai contoh, apabila kita mahu atur cara menyimpan keadaannya apabila menutup dan memulihkannya apabila ia dihidupkan seterusnya. Apabila anda menutup IntelliJ IDEA, anda kemungkinan besar akan mempunyai tab dan kelas yang sama dibuka apabila anda menghidupkannya seterusnya

Mari kita ingat bersiri dalam amalan

Nah, sekarang mari kita lihat siri dalam amalan. Jika anda ingin memahami topik ini dengan lebih baik, kami mengesyorkan membaca bahan Pensirian dan penyahserikatan dalam Java . Nah, dalam artikel ini kita akan pergi ke atas dan terus ke contoh. Katakan kita mempunyai kelas Userdengan set beberapa medan, getter dan setter, dan kaedah 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 mahu mensirikan objek kelas ini pada masa hadapan. Mari tulis kaedah yang mengambil objek Userdan rentetan path- laluan ke fail di mana kita akan menyimpan bait:
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 kaedah untuk penyahserialisasian. Kaedah mengambil rentetan path(laluan ke fail dari mana objek akan "dimuatkan") dan mengembalikan objek jenis 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 alatan sedia untuk digunakan. Sudah tiba masanya untuk membahagikan bait kepada atom . Mari kita tulis kaedah maindi mana kita mencipta objek kelas Userdan mensirikannya. Kemudian kami akan memuatkannya dan membandingkannya dengan yang asalnya:
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 kaedah, 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 output, objek adalah sama. Tetapi ada yang kecil tetapi... Dan di sinilah rasa malu Sepanyol transient mula bermain .

Pengubah suai (akhirnya)transient

Adakah sesiapa yang keliru bahawa kami menyimpan kata laluan pengguna? Terutama kata laluan sedemikian... Ya, ya, kami sendiri yang menciptanya, tetapi masih... Kadang-kadang terdapat situasi apabila sesetengah medan tidak boleh disiri, atau lebih baik tidak melakukan ini. Dalam contoh di atas, saya ingin menyimpan semua medan kecuali kata laluan. Bagaimana untuk mencapai ini? Jawapan: gunakan pengubah suai transient. transientialah pengubah suai yang ditentukan sebelum medan kelas (serupa dengan pengubah suai lain seperti public, finaldsb.) untuk menunjukkan bahawa medan itu tidak perlu bersiri. Medan yang ditanda dengan kata kunci transienttidak bersiri. Sekarang mari edit contoh dengan pengguna kami untuk membetulkan kekeliruan kecil dan tidak menyimpan kata laluan pengguna. Untuk melakukan ini, tandakan medan yang sepadan dalam 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 kami menjalankan kaedah daripada contoh di atas sekali lagi main, kami akan melihat bahawa kata laluan 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 matlamat kami dan tidak menyimpan maklumat sulit. Terutamanya maklumat sebegini... (maaf)

Bila hendak menggunakan sementara?

Contoh dengan pengguna diperlukan untuk menyelami konteks penyirian. Sekarang mari kita bincangkan dengan lebih khusus tentang masa untuk menggunakan pengubah suai transient.

  • Medan yang dikira secara pemrograman

Sesetengah kelas kadangkala mempunyai medan yang dikira berdasarkan medan lain atau maklumat lain. Mereka dikira, boleh dikatakan, dengan cepat. Untuk memberikan contoh bidang sedemikian, mari bayangkan pesanan di kedai dalam talian atau beberapa perkhidmatan penghantaran makanan. Setiap pesanan, antara maklumat lain, terdiri daripada senarai barang dan jumlah kos. Ia pula terdiri daripada jumlah kos setiap produk. Ternyata kos akhir tidak boleh ditetapkan "dengan tangan": ia mesti dikira secara pemrograman, menjumlahkan kos semua barang. Medan seperti ini yang harus dikira secara pemrograman tidak perlu bersiri. Oleh itu, kami menandakannya dengan pengubah suai transient.
class Order implements Serializable {

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

}

  • Medan dengan maklumat peribadi

Terdapat juga beberapa kelas yang menyimpan maklumat peribadi. Kami melihat contoh kelas sedemikian pada permulaan artikel. Anda tidak seharusnya membenarkan maklumat sedemikian bocor di luar JVM. Oleh itu, medan dengan data sedemikian mesti ditandakan dengan pengubah suai transientjika anda akan membuat siri kelas sedemikian.

  • Medan yang tidak melaksanakan antara mukaSerializable

Kadangkala kelas mengandungi medan - objek kelas lain yang tidak melaksanakan antara muka Serializable. Contoh medan tersebut ialah pembalak, aliran I/O, objek yang menyimpan sambungan pangkalan data dan kelas utiliti lain. Jika anda cuba mensirikan objek yang mengandungi medan tidak boleh disiri, anda akan menerima ralat java.io.NotSerializableException. Untuk mengelakkan ini, semua medan yang tidak melaksanakan antara muka Serializablemesti ditandakan dengan pengubah suai 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();
            }
        }
    }
}

  • Medan dengan maklumat tentang keadaan objek

Nah, satu perkara terakhir. Tidak perlu menyiri medan yang bukan sebahagian daripada maklumat keadaan objek. Contoh di atas terletak di bawah peraturan ini. Tetapi anda juga boleh memasukkan di sini semua medan lain yang ditambahkan untuk penyahpepijatan atau untuk melaksanakan beberapa jenis fungsi perkhidmatan yang tidak membawa maklumat tentang keadaan objek.

transientDanfinal

Keputusan

Itu sahaja. Hari ini kita bercakap tentang pengubah suai transient:
  1. Kami ingat bersiri dalam teori dan amalan.
  2. Kami menyedari bahawa untuk tidak menyerikan beberapa medan kelas, mereka perlu ditandakan dengan pengubah suai transient.
  3. Kami membincangkan dalam situasi apa pengubah suai ini harus digunakan. Terdapat empat situasi sedemikian:
    1. medan yang dikira secara pemrograman;
    2. medan yang mengandungi maklumat rahsia;
    3. medan yang tidak melaksanakan antara muka Serializable;
    4. medan yang bukan sebahagian daripada keadaan objek.
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION