Helpex - Trao đổi & giúp đỡ Đăng nhập

Tại sao Java là một ngôn ngữ hướng đối tượng thuần túy ... Hoặc tại sao không

Vài năm trước khi tôi bắt đầu học Java, tôi đã biết rằng Java tuân theo  mô hình Lập trình hướng đối tượng và mọi thứ trong Java đều là một đối tượng — có thể là Chuỗi (là mảng char trong C) hoặc chính một mảng.

Nhưng sau đó, tôi thấy mọi người nói trên Internet rằng Java thực sự không hoàn toàn là Hướng đối tượng, vì mọi thứ trong Java không phải là một đối tượng; ví dụ:

  1. Tất cả các kiểu nguyên thủy (char, boolean, byte, short, int, long, float, double) đều không phải là đối tượng vì chúng ta không thể thực hiện bất kỳ hoạt động giống đối tượng nào (sử dụng "." Và các phương thức gọi) trên chúng.
  2. Tôi cũng đã tìm thấy một số người nói rằng tất cả nội dung tĩnh (biến và phương thức) không thuộc về bất kỳ đối tượng nào nên chúng là những thứ không phải đối tượng.

Do ít kiến ​​thức và ít kinh nghiệm, tôi dễ dàng chấp nhận những lý do này và bắt đầu tin rằng Java không phải là một ngôn ngữ lập trình hướng đối tượng thuần túy.

Nhưng sau này, tôi thấy rằng đối với mỗi đối tượng, JVM tạo ra hai đối tượng:

  1. Đối tượng chính nó.
  2. Và một đối tượng cấp Lớp (được gọi bằng cú pháp ClassName.class) chỉ được tạo một lần trong khi Bộ nạp lớp tải lớp vào bộ nhớ. Tất cả nội dung tĩnh của lớp đó thuộc về đối tượng Lớp này và tất cả các đối tượng khác của lớp đó tham chiếu đến đối tượng cấp lớp này cho tất cả nội dung tĩnh.

Ví dụ, trong câu lệnh sau, sẽ có hai đối tượng được tạo:

Employee emp =  new Employee();

Một cái là chính nó, và một cái khác là đối tượng cấp lớp của lớp nhân viên, mà chúng ta có thể tham khảo bởi Employee.class. Và đối tượng mức lớp này chứa tất cả nội dung tĩnh của lớp Employee hoặc nó là một biến hoặc phương thức. Nếu chúng ta đang truy cập bất kỳ nội dung tĩnh nào thông qua đối tượng emp, nó sẽ trỏ đến đối tượng Employee.class để truy cập nội dung đó.

Đó là lý do tại sao một biến static được thay đổi cho mọi đối tượng ngay cả khi chúng ta thay đổi nó cho một đối tượng emp vì tất cả các đối tượng emp đều trỏ cùng một bản sao của biến đó từ đối tượng Employee.class.

Bây giờ điểm thứ 2 bị hủy bỏ vì nội dung tĩnh thuộc về một đối tượng. Nhưng điểm đầu tiên vẫn còn đó, và chúng ta vẫn có các kiểu dữ liệu nguyên thủy trong Java, và chúng không phải là đối tượng.

Như đã đề cập trước đó, các kiểu nguyên thủy không phải là đối tượng vì chúng ta không thể thực hiện bất kỳ chức năng nào liên quan đến đối tượng trên chúng. Để khắc phục vấn đề này, Java đã giới thiệu các lớp Wrapper cho mọi kiểu nguyên thủy (ví dụ: Integer for int, Long for long, Character for char). Bây giờ chúng ta có thể tạo các đối tượng cho các kiểu nguyên thủy và thực hiện tất cả các thao tác liên quan đến đối tượng trên chúng.

Và do tính năng autoboxing (tự động unboxing-boxing, boxing-unboxing), chúng ta có thể chỉ định trực tiếp một chữ nguyên thủy cho tham chiếu lớp Wrapper của nó. Nhưng chúng ta vẫn không thể thực hiện các thao tác này trên các biến nguyên thủy — chúng ta luôn cần tạo các đối tượng của lớp Wrapper tương ứng.

Ví dụ:

Integer obj = new Integer(5); // here we can do i.toString();

int i = 5; // but we can't do i.toString() here

Cho đến nay rõ ràng là các kiểu nguyên thủy không phải là đối tượng, nhưng đó thực sự là quan điểm của người dùng cuối (các nhà phát triển Java là người dùng cuối của Java vì chúng ta đang sử dụng nó chứ không phải tạo ra nó).

JVM nội bộ coi tất cả các kiểu nguyên thủy là đối tượng và bằng chứng về điều này có thể được tìm thấy trong mã nguồn hoặc Javadoc của lớp Class . Theo mã nguồn của lớp Class :

Các thể hiện của lớp Lớp đại diện cho các lớp và giao diện trong một ứng dụng Java đang chạy. Enum là một loại lớp và chú thích là một loại giao diện. Mọi mảng cũng thuộc về một lớp được phản ánh như một đối tượng Lớp được chia sẻ bởi tất cả các mảng có cùng kiểu phần tử và số thứ nguyên. Các kiểu Java nguyên thủy (boolean, byte, char, short, int, long, float và double) và từ khóa void cũng được biểu diễn dưới dạng các đối tượng Lớp.

Và mã Javadoc của phương thức Class.isPrimitive () :

public boolean isPrimitive ()
Xác định xem đối tượng Class được chỉ định có đại diện cho kiểu nguyên thủy hay không.
Có chín đối tượng Class được xác định trước để đại diện cho tám kiểu nguyên thủy và void. Chúng được tạo bởi Máy ảo Java và có cùng tên với các kiểu nguyên thủy mà chúng đại diện, đó là boolean, byte, char, short, int, long, float và double.
Các đối tượng này chỉ có thể được truy cập thông qua các biến cuối cùng tĩnh công khai sau đây và là các đối tượng Lớp duy nhất mà phương thức này trả về true.
Trả về:
true nếu và chỉ khi lớp này đại diện cho kiểu nguyên thủy
Kể từ:
JDK1.1
Xem thêm:
Boolean.TYPE , Character.TYPE , Byte.TYPE, Short.TYPE , Integer.TYPE , Long.TYPE , Float.TYPE , Double.TYPE , Void.TYPE

Nếu bạn mở Javadoc của lớp Class và nhấn Ctrl + F cho từ “nguyên thủy”, bạn sẽ tìm thấy nhiều lý do để tin rằng JVM coi tất cả các kiểu nguyên thủy là đối tượng bên trong.

Hãy mở  mục nhập Integer.TYPE từ phía trên. Trong phần này, bạn sẽ tìm thấy:

public static final Class < Integer > TYPE
Cá thể Class đại diện cho kiểu nguyên thủy int.

Và nếu bạn viết dòng sau trong chương trình của mình bằng Eclipse:

Integer.TYPE i = 5;

Bạn sẽ gặp lỗi biên dịch cho biết không thể giải quyết Integer.TYPE thành một kiểu có gợi ý từ Eclipse để thay đổi nó thành int.

Tại sao chúng ta nên sử dụng các loại nguyên thủy





   

Vì vậy, nếu JVM tạo các đối tượng cho tất cả các kiểu nguyên thủy, tại sao chúng ta cần sử dụng các kiểu nguyên thủy thay vì tạo các đối tượng với các lớp Wrapper tương ứng? Đó là bởi vì JVM tạo các đối tượng gốc này cho các kiểu nguyên thủy bên trong và các đối tượng đó rất nhẹ và được tối ưu hóa hơn các đối tượng lớp bao bọc tương ứng của chúng; vì điều này, chúng có ít chức năng hơn (ví dụ: chúng tôi không thể gọi các phương thức trên chúng vì chúng không có bất kỳ).

Chúng ta nên sử dụng các kiểu nguyên thủy:

  1. Bởi vì chúng nhanh (ví dụ: chương trình sau mất 9 giây để chạy trên máy của tôi, trong khi mất 0 giây nếu tôi chuyển đổi Tổng dài thành tổng dài ... nếu đó là bất kỳ dấu hiệu nào tại sao chúng ta sử dụng nguyên thủy).
public static void main(String[] args) {

    long millis = System.currentTimeMillis();
    Long sum = 0L; // uses Long, not long

    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }

    System.out.println(sum);
    System.out.println((System.currentTimeMillis() - millis) / 1000);
}
  1. Chúng cho phép chúng tôi sử dụng toán tử bình đẳng gốc "==":
new Integer(3) == new Integer(3); // false

new Integer(100) == new Integer(100); // false

Integer.valueOf(5) == Integer.valueOf(5); //true

Integer.valueOf(200) == Integer.valueOf(200); //false

Câu lệnh thứ 4 ở đây cho kết quả là false vì 256 số nguyên gần nhất với 0 [-128; 127] được lưu trong bộ nhớ cache của JVM, vì vậy chúng trả về cùng một đối tượng cho những. Tuy nhiên, ngoài phạm vi đó, chúng không được lưu vào bộ nhớ cache, vì vậy một đối tượng mới sẽ được tạo.

Vì vậy, chúng ta có thể nói JVM xử lý tất cả các kiểu nguyên thủy như các đối tượng bên trong, nhưng chúng ta không thể sử dụng chúng theo cách đó; thay vào đó, chúng tôi có các lớp Wrapper cho điều đó.

Đây là lý do tại sao Java thực sự là một ngôn ngữ hướng đối tượng thuần túy. Vui lòng cho tôi biết bạn nghĩ gì trong phần bình luận — Java có phải là một ngôn ngữ hướng đối tượng thuần túy hay không?

11 hữu ích 0 bình luận 29k xem chia sẻ

Có thể bạn quan tâm

loading