JavaRush /Blog Java /Random-VI /Tự động đóng hộp và mở hộp trong Java
Viacheslav
Mức độ

Tự động đóng hộp và mở hộp trong Java

Xuất bản trong nhóm
<h2>Giới thiệu</h2>Một ngôn ngữ lập trình, giống như ngôn ngữ con người nói, tồn tại và thay đổi, những hiện tượng mới xuất hiện trong đó giúp ngôn ngữ trở nên thuận tiện hơn khi sử dụng. Và như chúng ta biết, ngôn ngữ nên thể hiện suy nghĩ của chúng ta một cách thuận tiện.
Autoboxing và unboxing trong Java - 1
Vì vậy, trong Java SE 5, cơ chế đóng hộp/mở hộp đã được giới thiệu. Và một hướng dẫn riêng của Oracle được dành cho các tính năng của phương tiện thể hiện suy nghĩ này: Autoboxing và Unboxing . <h2>Đấm bốc tự động</h2>Hãy xem một ví dụ về Đấm bốc tự động đóng gói. Đầu tiên, hãy xem nó hoạt động như thế nào. Hãy sử dụng trang web biên dịchjava.net và tạo một lớp:
public class App {
    public static void main(String[] args) {
        Integer portNumber = 8080;
        if (args.length != 0) {
            portNumber = Integer.valueOf(args[0]);
        }
        System.out.println("Port number is: " + portNumber);
    }
}
Mã đơn giản. Chúng ta có thể chỉ định tham số đầu vào và thay đổi giá trị cổng. Như chúng ta thấy, bởi vì chúng tôi đọc giá trị cổng từ Stringcác tham số, chúng tôi nhận được Integergiá trị đó bằng cách chuyển nó qua Integer.valueOf. Do đó, chúng tôi buộc phải chỉ định nó không phải là kiểu nguyên thủy mà là kiểu đối tượng Integer. Và ở đây một mặt chúng ta có một biến đối tượng và giá trị mặc định là nguyên thủy. Và nó hoạt động. Nhưng chúng ta không tin vào phép thuật, phải không? Chúng ta hãy xem xét “dưới mui xe” như họ nói. Tải xuống mã nguồn từ biên dịchjava.net bằng cách nhấp vào “Tải xuống ZIP”. Sau đó, giải nén kho lưu trữ đã tải xuống vào một thư mục và truy cập nó. Bây giờ hãy làm: javap -c -p App.classtrong đó App.class là tệp lớp được biên dịch cho lớp của bạn. Chúng ta sẽ thấy nội dung như thế này:
Autoboxing và unboxing trong Java - 2
Đây chính là “mã byte” khét tiếng tương tự. Nhưng điều quan trọng đối với chúng tôi bây giờ là những gì chúng tôi thấy. Đầu tiên, nguyên hàm 8080 được đặt trên ngăn thực thi phương thức và sau đó Integer.valueOf được thực thi . Đây chính là “sự kỳ diệu” của quyền anh. Và bên trong phép thuật trông như thế này:
Autoboxing và unboxing trong Java - 3
Tức là, về bản chất, một cái mới sẽ được lấy Integerhoặc sẽ được lấy Integertừ bộ đệm (bộ đệm không gì khác hơn chỉ là một mảng các Số nguyên) tùy thuộc vào giá trị của số. Đương nhiên, Integerkhông chỉ có một người may mắn như vậy. Có toàn bộ danh sách các kiểu nguyên thủy có liên quan và các lớp bao bọc của chúng (các lớp đại diện cho các kiểu nguyên thủy trong thế giới OOP). Danh sách này được đưa ra ở cuối Hướng dẫn của Oracle: “ Autoboxing and Unboxing ”. Điều đáng lưu ý ngay là các mảng được tạo từ nguyên thủy không có “trình bao bọc” mà không kết nối bất kỳ thư viện bên thứ ba nào. Những thứ kia. Arrays.asListsẽ không làm cho int[]chúng tôi Listtừ Integer's. <h2>Mở hộp</h2>Quy trình ngược lại với quy trình đóng hộp được gọi là mở hộp, mở hộp. Hãy xem một ví dụ giải nén:
public class App {

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Please, enter params");
            return;
        }
      	int value = Math.abs(Integer.valueOf(args[0]));
        System.out.println("Absolute value is: " + value);
    }

}
Math.abschỉ chấp nhận nguyên thủy. Phải làm gì? Lớp trình bao bọc có một phương thức đặc biệt cho trường hợp này trả về một giá trị nguyên thủy. Ví dụ: đây là Integerphương thức intValue . Nếu chúng ta nhìn vào bytecode, nó sẽ như thế này:
Autoboxing và unboxing trong Java - 4
Rõ ràng là không có phép thuật nào cả. Mọi thứ đều có trong Java. Nó chỉ hoạt động “tự nó”. Để thuận tiện cho chúng tôi. <h2>Cào</h2>
Autoboxing và unboxing trong Java - 5
Bất kỳ công cụ nào nếu sử dụng không đúng cách sẽ trở thành vũ khí đáng gờm để chống lại chính nó. Và cơ chế đóng hộp/mở hộp tự động trong Java cũng không ngoại lệ. Sự so sánh rõ ràng đầu tiên là thông qua ==. Tôi nghĩ điều này đã rõ ràng, nhưng chúng ta hãy xem lại:
public static void main(String[] args) {
    Integer inCacheValue = 127;
    Integer inCacheValue2 = 127;
    Integer notInCache = 128; // new Integer(129)
    Integer notInCache2 = 128; // new Integer(129)
    System.out.println(inCacheValue == inCacheValue2); //true
    System.out.println(notInCache == notInCache2); //false
}
Trong trường hợp đầu tiên, giá trị được lấy từ Integerbộ đệm giá trị (xem phần giải thích về Quyền anh ở trên) và trong trường hợp thứ hai, một đối tượng mới sẽ được tạo mỗi lần. Nhưng ở đây nó đáng để đặt trước. Hành vi này phụ thuộc vào giới hạn cao của bộ đệm ( java.lang.Integer.IntegerCache.high ). Ngoài ra, giới hạn này có thể thay đổi do các cài đặt khác. Bạn có thể đọc cuộc thảo luận về chủ đề này trên stackoverflow: Bộ đệm Integer lớn đến mức nào? Đương nhiên, các đối tượng cần được so sánh bằng cách sử dụng giá trị bằng: System.out.println(notInCache.equals(notInCache2)); Vấn đề thứ hai liên quan đến cùng một cơ chế là hiệu suất. Bất kỳ quyền anh nào trong Java đều tương đương với việc tạo một đối tượng mới. Nếu số này không được bao gồm trong các giá trị bộ đệm (tức là -128 đến 127), thì một đối tượng mới sẽ được tạo mỗi lần. Nếu đột nhiên việc đóng gói (tức là đấm bốc) được thực hiện theo vòng lặp, điều này sẽ gây ra sự gia tăng lớn về các vật thể không cần thiết và tiêu tốn tài nguyên cho công việc của người thu gom rác. Vì vậy, đừng quá liều lĩnh về nó. Cú cào thứ ba không kém phần đau đớn cũng bắt nguồn từ cơ chế tương tự:
public static void check(Integer value) {
    if (value <= 0) {
        throw new IllegalStateException("Value is too small");
    }
}
Trong mã này, người đó rõ ràng đang cố gắng không vượt qua lỗi. Nhưng không có kiểm tra cho null. Nếu nói đến đầu vào null, thì thay vì một lỗi có thể hiểu được, chúng ta sẽ gặp một lỗi không thể hiểu được NullPointerException. Bởi vì để so sánh, Java sẽ cố gắng thực thi value.intValuevà gặp sự cố, bởi vì... valuesẽ null. <h2>Kết luận</h2>Cơ chế đóng hộp/mở hộp cho phép lập trình viên viết ít mã hơn và đôi khi thậm chí không nghĩ đến việc chuyển đổi từ nguyên thủy sang đối tượng và ngược lại. Nhưng điều đó không có nghĩa là bạn nên quên cách thức hoạt động của nó. Nếu không, bạn có thể mắc lỗi không xuất hiện ngay lập tức. Chúng ta không nên dựa vào các phần của hệ thống không hoàn toàn nằm trong tầm kiểm soát của chúng ta (chẳng hạn như ranh giới số nguyên). Nhưng đừng quên tất cả ưu điểm của các lớp bao bọc (như Integer). Thông thường các lớp trình bao bọc này có một tập hợp các phương thức tĩnh bổ sung sẽ giúp cuộc sống của bạn tốt hơn và mã của bạn mang tính biểu cảm hơn. Đây là một ví dụ bắt kịp:
public static void main(String[] args) {
    int first = 1;
    int second = 5;
    System.out.println(Integer.max(first, second));
    System.out.println(Character.toLowerCase('S'));
}
Kết luận chính xác từ mọi thứ là không có phép thuật nào cả, chỉ có một loại nhận thức nào đó. Và không phải mọi thứ sẽ luôn như chúng ta mong đợi. Ví dụ: không có bao bì: System.out.println("The number is " + 8); Ví dụ trên sẽ được trình biên dịch tối ưu hóa thành một dòng. Tức là giống như bạn viết “Số là 8”. Và trong ví dụ dưới đây cũng sẽ không có bao bì:
public static void main(String[] args) {
    System.out.println("The number is " + Math.abs(-2));
}
Làm sao có thể xảy ra khi chúng ta printlnlấy một đối tượng làm đầu vào và cần bằng cách nào đó kết nối các đường dây. Dây chuyền... vâng, đó là lý do tại sao không có bao bì như vậy. Có Integercác phương thức tĩnh, nhưng một số trong số đó là package. Nghĩa là, chúng ta không thể sử dụng chúng, nhưng trong bản thân Java, chúng có thể được sử dụng tích cực. Đây chính xác là trường hợp ở đây. Phương thức getChars sẽ được gọi, tạo ra một mảng các ký tự từ số. Một lần nữa, không có phép thuật nào cả, chỉ có Java). Vì vậy, trong bất kỳ tình huống không rõ ràng nào, bạn chỉ cần xem xét việc thực hiện và ít nhất điều gì đó sẽ xảy ra. #Viacheslav
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION