¡Hola! En el artículo de hoy, veremos el modificador transitorio en Java. Hablemos de por qué es necesario este modificador y cómo usarlo correctamente. ¡Ir!
átomos . Escribamos un método la vergüenza española Modificador (finalmente)
¿Alguien quedó confundido porque guardamos la contraseña del usuario? Especialmente una contraseña así... Sí, sí, se nos ocurrió a nosotros mismos, pero aún así... A veces hay situaciones en las que algunos campos no se pueden serializar, o es mejor no hacerlo. En el ejemplo anterior, me gustaría guardar todos los campos excepto la contraseña. ¿Cómo lograr esto? Respuesta: usa el modificador
Algunas clases a veces tienen campos que se calculan en función de otros campos u otra información. Se calculan, por así decirlo, sobre la marcha. Para dar un ejemplo de este campo, imaginemos un pedido en una tienda online o algún servicio de entrega de comida a domicilio. Cada pedido, entre otra información, consta de una lista de bienes y un costo total. Este, a su vez, consiste en el costo total de cada producto. Resulta que el costo final no debe fijarse "a mano": debe calcularse mediante programación, sumando el costo de todos los bienes. Campos como estos que deben calcularse mediante programación no necesitan serializarse. Por eso, los marcamos con un modificador
También hay algunas clases que almacenan información privada. Vimos un ejemplo de dicha clase al principio del artículo. No debe permitir que dicha información se filtre fuera de la JVM. Por lo tanto, los campos con dichos datos deben marcarse con un modificador
A veces una clase contiene campos-objetos de otras clases que no implementan la interfaz
Bueno, una última cosa. No es necesario serializar campos que no forman parte de la información de estado del objeto. Los ejemplos anteriores caen bajo esta regla. Pero también puede incluir aquí todos los demás campos agregados para depurar o realizar algún tipo de función de servicio que no contengan información sobre el estado del objeto.
Recordemos la serialización
El modificadortransient
se utiliza en el proceso de serializar y deserializar objetos. Así que primero hablemos brevemente sobre esto. Supongamos que tenemos algún objeto y tiene campos, cada uno de los cuales tiene algún valor. Todo esto se llama estado del objeto. La serialización es la conversión del estado de un objeto en una secuencia de bytes. Estos bytes suelen almacenarse en algún archivo. La deserialización es el proceso inverso. Imaginemos que serializamos un objeto en bytes y almacenamos este conjunto de bytes en algún archivo. Al deserializar, el programa necesita:
- Leer un conjunto de bytes de un archivo.
- Construya un objeto inicial a partir de este conjunto de bytes y establezca cada campo en el valor que tenía el objeto en el momento de la serialización.
Recordemos la serialización en la práctica.
Bueno, ahora veamos la serialización en la práctica. Si desea comprender mejor el tema, le recomendamos leer el material Serialización y deserialización en Java . Bueno, en este artículo repasaremos la parte superior e iremos directamente a los ejemplos. Digamos que tenemos una claseUser
con un conjunto de algunos campos, captadores y definidores, y un método 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 + '\'' +
'}';
}
}
Queremos serializar objetos de esta clase en el futuro. Escribamos un método que tome un objeto User
y una cadena path
: la ruta al archivo en el que guardaremos los bytes:
static void serialize(User user, String path) throws IOException {
FileOutputStream outputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
//crear 2 subprocesos para serializar el objeto y guardarlo en un archivo
outputStream = new FileOutputStream(path);
objectOutputStream = new ObjectOutputStream(outputStream);
// сохраняем un objeto в файл
objectOutputStream.writeObject(user);
} finally {
// Закроем потоки в блоке finally
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
También escribiremos un método para la deserialización. El método toma una cadena path
(la ruta al archivo desde el cual se “cargará” el objeto) y devuelve un objeto de tipo User
:
static User deserialize(String path) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try {
//создаем 2 потока для десериализации un objetoа из archivo
fileInputStream = new FileInputStream(path);
objectInputStream = new ObjectInputStream(fileInputStream);
//загружаем un objeto из archivo
return (User) objectInputStream.readObject();
} finally {
if (fileInputStream != null) {
fileInputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
}
}
Todas las herramientas están listas para usar. Es hora de dividir los bytes en main
en el que creamos un objeto de clase User
y lo serializamos. Luego lo cargaremos y lo compararemos con lo que era originalmente:
public static void main(String[] args) throws IOException, ClassNotFoundException {
// вставьте свой путь до archivo
final String path = "/home/zor/user.ser";
// creamos nuestro objeto
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");
}
Si ejecutamos el método, veremos el siguiente resultado:
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'}
Como puede ver en el resultado, los objetos son idénticos. Pero hay un pequeño pero... Y es precisamente aquí donde entra en juego transient
.
Modificador (finalmente)transient
¿Alguien quedó confundido porque guardamos la contraseña del usuario? Especialmente una contraseña así... Sí, sí, se nos ocurrió a nosotros mismos, pero aún así... A veces hay situaciones en las que algunos campos no se pueden serializar, o es mejor no hacerlo. En el ejemplo anterior, me gustaría guardar todos los campos excepto la contraseña. ¿Cómo lograr esto? Respuesta: usa el modificador transient
. transient
es un modificador colocado antes de un campo de clase (similar a otros modificadores como public
, final
etc.) para indicar que el campo no debe serializarse. Los campos marcados con la palabra clave transient
no están serializados. Ahora editemos el ejemplo con nuestro usuario para corregir una pequeña confusión y no guardar la contraseña del usuario. Para hacer esto, marque el campo correspondiente en la clase con la palabra clave 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...
*/
}
Si volvemos a ejecutar el método del ejemplo anterior main
, veremos que la contraseña no se guarda:
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'}
Genial, logramos nuestro objetivo y no almacenamos información confidencial. Especialmente este tipo de información... (lo siento)
¿Cuándo utilizar transitorio?
Se necesitaba un ejemplo con un usuario para poder profundizar en el contexto de la serialización. Ahora hablemos más específicamente sobre cuándo usar el modificadortransient
.
- Campos que se calculan mediante programación
Algunas clases a veces tienen campos que se calculan en función de otros campos u otra información. Se calculan, por así decirlo, sobre la marcha. Para dar un ejemplo de este campo, imaginemos un pedido en una tienda online o algún servicio de entrega de comida a domicilio. Cada pedido, entre otra información, consta de una lista de bienes y un costo total. Este, a su vez, consiste en el costo total de cada producto. Resulta que el costo final no debe fijarse "a mano": debe calcularse mediante programación, sumando el costo de todos los bienes. Campos como estos que deben calcularse mediante programación no necesitan serializarse. Por eso, los marcamos con un modificador transient
.
class Order implements Serializable {
private List- items;
private transient BigDecimal totalAmount; //вычисляется на ходу
}
- Campos con información privada
También hay algunas clases que almacenan información privada. Vimos un ejemplo de dicha clase al principio del artículo. No debe permitir que dicha información se filtre fuera de la JVM. Por lo tanto, los campos con dichos datos deben marcarse con un modificador transient
si va a serializar dicha clase.
- Campos que no implementan la interfaz.
Serializable
A veces una clase contiene campos-objetos de otras clases que no implementan la interfaz Serializable
Serializable
. Ejemplos de dichos campos son registradores, flujos de E/S, objetos que almacenan conexiones de bases de datos y otras clases de utilidades. Si intenta serializar un objeto que contiene campos no serializables, recibirá un error java.io.NotSerializableException
. Para evitar esto, todos los campos que no implementan la interfaz Serializable
deben marcarse con un modificador transient
.
public class FileReader implements Serializable {
// Первые 2 поля не реализуют Serializable
// Помечаем их Cómo 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();
}
}
}
}
- Campos con información sobre el estado del objeto.
Bueno, una última cosa. No es necesario serializar campos que no forman parte de la información de estado del objeto. Los ejemplos anteriores caen bajo esta regla. Pero también puede incluir aquí todos los demás campos agregados para depurar o realizar algún tipo de función de servicio que no contengan información sobre el estado del objeto.
transient
Yfinal
Resultados
Eso es todo. Hoy hablamos del modificadortransient
:
- Recordamos la serialización en teoría y práctica.
- Nos dimos cuenta de que para no serializar algunos campos de la clase, es necesario marcarlos con un modificador
transient
. - Discutimos en qué situaciones se debe utilizar este modificador. Hubo cuatro situaciones de este tipo:
- campos que se calculan mediante programación;
- campos que contienen información secreta;
- campos que no implementan la interfaz
Serializable
; - campos que no forman parte del estado del objeto.
GO TO FULL VERSION