Thứ Ba, 11 tháng 6, 2019

Serialization trong java

 Trước h mình đã gặp khái niệm này rất nhiều lần rồi, từ đồng nghiệp cho tới code, Serialization xuất hiện cực kì nhiều, mà mình thì lại không tìm hiểu kĩ về khái niệm này lắm. Cho tới dạo vừa rồi, mình có làm 1 task liên quan đến logging management, và khi in ra dữ liệu thì mình thấy một dãy dài các mã hexa. Điều này làm mình tò mò và tìm hiểu sâu, cuối cùng mình nhận ra cái mã hexa đó là từ thằng cha serialization mà ra. Vậy Serialization là gì, và trong java nó hoạt động ra làm sao? Hôm nay mình sẽ vén bức tấm áo của anh chàng này ra cùng các bạn nhá.

Serialization là gì? Deserialization là gì?


Serialization là chuyển một đối tượng object sang dạng bytes. Còn deserialization là chuyển dạng bytes thành một đối tượng object.
Thực sự khái niệm này rất đơn giản, có thể hiểu rằng đây giống như là quá trình mã hóa và giải mã mà thôi. Nhưng tại sao lại phải cần serialization và deserialization? Điều này chúng ta cần phải nhìn vào thực tế, hiện nay các ứng dụng lớn thường gồm nhiều module được kết nối với nhau, khi các module này muốn trao đổi dữ liệu thì bắt buộc chúng phải trao đổi qua 1 phương thức nào đó. Mà để phương thức trao đổi này hoạt động ấy, thì nó ít nhất phải biểu diễn được thông tin mà các module đang muốn trao đổi. Mà tính ra khi lập trình, các module có cả trăm loại object, vậy thì làm sao mà những phương thức trao đổi có thể biểu diễn được hết? Vậy nên người ta mới nghĩ ra cái vụ serialization và deserialization.
 Giả sử module A muốn trao đổi thông tin với module B dựa trên object x đi. Chỉ cần A serialize x thành bytes, sau đó B sẽ deserialize bytes thành x. Thế là được rồi chứ gì nữa. Và trong java, việc này được hỗ trợ khá tốt, người code chỉ cần thêm vài kí tự vào là có thể làm được.

Serialization và Deserialization trong java


Để 1 object trong java có thể được serialized hay deserialized, thì chỉ cần để object đó là implements ông nội java.io.Serializable interface. Mà cái interface này cũng khỏe cái nữa là nó chỉ là 1 marker interface, nó chả có phương thức nào hết nên mình cũng không cần bận tâm phải hiện thực gì hết.
Ví dụ ta có 1 object Student như dưới đây:
   public class Student {
  public String name = "cuong";
  public Integer age = 24;
 }
Muốn object này có thể được serialized và deserialized thì chỉ cần cho nó implements Serializable như sau:
   public class Student implements Serializable{
  public String name = "cuong";
  public Integer age = 24;
 }
Thế là xong, ta thử viết vài dòng code nữa xem thử nó có hoạt động không nhá. Đầu tiên là ta serialize nó thử nè:
   public static void main(String args[]) throws IOException {
  FileOutputStream fos = new FileOutputStream("temp.out");
  ObjectOutputStream oos = new ObjectOutputStream(fos);
  Student ts = new Student();
  oos.writeObject(ts);
  oos.flush();
  oos.close();
 }
Ở đây, java cung cấp cho chúng ta hàm writeObject(). Hàm này có khả năng serialize, nên ta chỉ việc bóc ra mà xài thôi. Đoạn code trên có nhiệm vụ serialize class student của chúng ta vào 1 file tên là temp.out. Sau khi mở file temp.out ra bằng hex editor, chúng ta có thể thấy mớ dữ liệu sau:
Đây chính xác là biến ts đã được serialize. Bây giờ, hãy thử viết code để thực hiện deserialize xem nào:

   public static void main(String[] args) throws Exception {
  FileInputStream fis = new FileInputStream("temp.out");
  ObjectInputStream oin = new ObjectInputStream(fis);
  Student ts = (Student) oin.readObject();
  System.out.println("name =" +ts.name + ",age = " + ts.age);
 }
Kết quả sẽ trả về cho bạn name=cuong, age = 24. Để kết thúc bài viết ngắn này thì mình cũng có chút lưu ý về serialization và deserialization trong java:
- các thuộc tính static và transient sẽ không được serialize. Ví dụ bạn đổi code thành: public transient Integer age = 24; , thì age sẽ field này sẽ không được serialize.
- Lớp cha implements Serializable rồi thì lớp con khỏi phải implements
- Một đối tượng được Serialized khi tất cả các đối tượng con trong nó phải được implements Serializable. Với ví dụ trên thì bạn thấy ta có 2 đối tượng con là name (String) và age(Integer). Sở dĩ 2 đối tượng này serialized được là nhờ chúng đã implements Serializable.
Share:

Thứ Năm, 25 tháng 4, 2019

Lambda expression trong Java 8

Nếu là 1 java developer, và đã xài qua java 8, chắc chắn bạn sẽ nghe nói tới lambda expression. Đây là 1 phần mới đc thêm vào trong java 8, và nó thực sự rất hữu ích nếu chúng ta biết tận dụng để viết code. Nhưng trước khi đi vào tìm hiểu lambda expression, bạn phải tìm hiểu một khái niệm khác gọi là : Functional interface.

Functional interface là gì?

Functional interface là một interface chỉ có chứa 1 abstract method duy nhất.
Ví dụ, interface dưới đây là functional interface.
public interface MyInterface{
    double getValue();
}
Còn interface dưới đây không phải là functional interface, vì có đến 2 abstract method.
public interface MyInterface{
    double getValue();
    double getUnit();
}
Trước khi có lambda expression, để sử dụng MyInterface chúng ta phải code như sau:
MyInterface myInterface = new MyInterface() {
   @Override
   public double getValue() {
    return 1;
   }
  };
Đoạn code trên chúng ta đã hiện thực hàm getValue để trả về 1. Nhưng vấn đề nằm ở chỗ, MyInterface của chúng ta chỉ có duy nhất 1 method nên việc ta phải viết lại cả tên method khi hiện thực là thừa thải. Đó là lí do ông nội lambda expression ra đời.

Lambda expression

Lambda expression trong java là dấu "->", nó giúp ta định nghĩa 1 method bằng dòng code siêu ngắn gọn vì ta không cần phải viết lại tên method đang hiện thực nữa. Ví dụ ta có hàm getValue() khi viết bình thường sẽ như sau:
double getValue(){ return 1;}
Nhưng với lambda expression ta sẽ viết được như sau:
() -> 1
Bạn thấy đấy, code của chúng ta ngắn hơn 1 cách đáng kinh ngạc. Minh sẽ đưa ra một số ví dụ cho những trường hợp khác khi ta dùng lambda expression. Ví dụ ta có 1 method void như sau:
void sayHello(){
    System.out.println("Hello");
}
Method trên có chức năng viết ra "hello". Với lambda expression, ta có thể viết như sau:
() -> System.out.println("Hello");
Cũng với method trên, nếu ta muốn in ra hai dòng là "hello" và "world" thì ta có:
void sayHello(){
    System.out.println("Hello");
    System.out.println("World");
}
Với lambda expression:
() -> {
    System.out.println("Hello");
    System.out.println("World");
}
Với method có parameter như method sau:
void sayHelloToPerson(String name){
    System.out.println("Hello" + name);
}
Ta cũng viết lại được dưới dạng lambda expression:
(name) -> System.out.println("Hello" + name);
Vậy ta dùng lambda expression như thế nào? Lamdba expression ko phải cứ muốn tùy tiện viết là viết đâu nhé. Nó phải viết dựa trên 1 functional interface đã có từ trước. Ví dụ với "MyInterface" của chúng ta, bây giờ ta có thể sử dụng nó bằng cách:
MyInterface myInterface = () -> 1;
myInterface.getValue(); //trả về 1
Với lamdba expression, mọi thứ đều trở nên ngắn hơn, vậy nên hãy tin dùng lambda expression.!!! Ta đi tiếp 1 ví dụ nữa nhé! Giả sử ta có 1 functional interface thứ 2.
public interface MyInterface2{
    double sum(double a, double b);
}
Ta có thể sử dụng interface này với lambda expression như sau:
MyInterface2 myInterface2 = (a,b) -> a+b;
System.out.println(myInterface2.sum(2, 3)); //in ra 5.0
Chúng ta cũng có thể tạo ra 1 interface generic hơn như sau:
public interface MyInterface {
 public T func(T t);
}
Từ đó ta có thể định nghĩa interface đó với đầu vào là String
MyInterface myInterface1 = (str) -> str + " is my friend";
System.out.println(myInterface1.func("cuong")); //cuong is my friend
Hoặc có thể định nghĩa parameter là Double
MyInterface myInterface2 = (number) -> number + 1;
System.out.println(myInterface2.func(1.0)); //2.0

Lambda expression kết hợp Stream trong java 8

Java 8 không chỉ show hàng mỗi ông nội lambda, mà còn cho chúng ta thêm 1 thứ nữa gọi là stream. 2 cái mới này nếu ta biết sử dụng nó thì đúng là code ngắn 1 cách kinh khủng khiếp. Ví dụ ta có đối tượng:
public static class Name {
  public Name(String first, String last){
   this.firstName = first;
   this.lastName = last;
  }
  private String firstName;
  private String lastName;
  public String getFirstName(){
   return firstName;
  }
  public String getLasttName(){
   return lastName;
  }
 }
Và ta có danh sách 3 người như sau:
List names = Arrays.asList(new Name("le","cuong"),new Name("nguyen","hung"),new Name("le","nam"));
Nhiệm vụ bây h là ta sẽ lọc ra những người họ "le", và sau đó in ra tên (lastname) của người đó. Nếu như không sử dụng stream và lambda expression, thực sự lượng code của bạn phải viết ra khó có thể viết được trong 1 hay 2 dòng. Nhưng với stream + lambda expession, code của bạn siêu đẹp và siêu ngắn như sau:
System.out.println(names.stream().filter(name -> name.getFirstName().equals("le")).map(name -> name.getLasttName()).collect(Collectors.toList())); //in ra [cuong, nam]
Quá ngắn đúng không? Và đó là lambda expression. Bài viết được viết dựa trên bài viết của programiz. Cám ơn mọi người đã ghé đọc.
Share:
Được tạo bởi Blogger.