Sự khác biệt giữa a.getClass () và A.class trong Java là gì?


101

Trong Java, những ưu / nhược điểm nào tồn tại xung quanh sự lựa chọn sử dụng a.getClass()hoặc A.class? Hoặc có thể được sử dụng ở bất cứ nơi nào Class<?>được mong đợi, nhưng tôi tưởng tượng rằng sẽ có hiệu suất hoặc những lợi ích tinh tế khác khi sử dụng cả hai trong các trường hợp khác nhau (giống như có với Class.forName()ClassLoader.loadClass().

Câu trả lời:


163

Tôi sẽ không so sánh chúng về ưu / nhược điểm vì chúng có các mục đích khác nhau và hiếm khi có "sự lựa chọn" để đưa ra giữa hai điều này.

  • a.getClass()trả về kiểu thời gian chạy của a. Tức là, nếu bạn có A a = new B();thì a.getClass()sẽ trả lại Blớp học.

  • A.classđánh giá Alớp một cách tĩnh , và được sử dụng cho các mục đích khác thường liên quan đến phản xạ.

Về hiệu suất, có thể có một sự khác biệt có thể đo lường được, nhưng tôi sẽ không nói bất cứ điều gì về nó vì cuối cùng thì nó phụ thuộc vào JVM và / hoặc trình biên dịch.


Bài đăng này đã được viết lại thành một bài báo ở đây .


2
Làm thế nào về A.class.getClass()?
user1870400

5
Điều đó sẽ cung cấp cho bạn Classđối tượng của lớp đại diện cho A.classđối tượng lại là một thể hiện của java.lang.Class.
aioobe

Làm thế nào về A.getClass().class?
Shikhar Mainalee

@ShikharMainalee, điều đó không thực sự có ý nghĩa nếu Alà một lớp học. Không có getClassphương thức tĩnh trên các lớp.
aioobe

điều này liên quan như thế nào đến gợi ý ở đây rằng chúng cũng trỏ đến các bộ tải lớp khác nhau. Đây cũng có thể là một sự khác biệt đáng kể giữa hai?
Jim

32

Chúng thực sự khác nhau về nơi bạn có thể sử dụng chúng. A.classhoạt động tại thời điểm biên dịch trong khi a.getClass()yêu cầu một thể hiện của kiểu Avà hoạt động trong thời gian chạy.

Có thể có sự khác biệt về hiệu suất. Trong khi A.classcó thể được giải quyết bởi trình biên dịch vì nó biết loại thực tế của A, a.getClass()là một cuộc gọi phương thức ảo xảy ra trong thời gian chạy.

Để tham khảo, mã bytecode nhắm mục tiêu của trình biên dịch thường phát ra các hướng dẫn sau cho Integer.getClass():

aload_1
invokevirtual   #3; //Method java/lang/Object.getClass:()Ljava/lang/Class;

và những điều sau đây cho Integer.class:

//const #3 = class  #16;    //  java/lang/Integer

ldc_w   #3; //class java/lang/Integer

Trước đây thường liên quan đến việc gửi phương thức ảo và do đó có lẽ mất nhiều thời gian hơn để thực thi. Tuy nhiên, điều đó cuối cùng phụ thuộc vào JVM.


1
[1] về mặt kỹ thuật các ngôn ngữ Java Specification không đề cập đến bất kỳ hồ bơi thường xuyên ở tất cả ...
aioobe

@aioobe: Tôi đoán bạn nói đúng, đó là lý do tại sao tôi đã kiểm tra mã bytecode được tạo bởi Sun JDK.
Tomasz Nurkiewicz

1
... mà nói về mặt kỹ thuật cũng không cung cấp một câu trả lời có thẩm quyền, vì Đặc tả ngôn ngữ Java thậm chí không đề cập đến mã bytecode khi nói đến ngữ nghĩa.
aioobe

@aioobe: Tôi hiểu ý bạn. Tôi chưa bao giờ đề cập đến JLS, chỉ kiểm tra thực nghiệm cách nó hoạt động, vì tôi không chắc chắn. OP hỏi về hiệu suất và kiểm tra bytecode có vẻ là một ý kiến ​​hay. Cảm thấy tự do để chỉnh sửa bài viết của tôi hoặc gỡ bỏ tuyên bố mơ hồ
Tomasz Nurkiewicz

1
cuộn trở lại nếu bạn không thích nó ;-)
aioobe

8

hãy xem các ví dụ dưới đây

a.getClass()!= A.class, tức là a không phải là một thể hiện của A mà là một lớp con ẩn danh của A

a.getClass() yêu cầu một phiên bản của loại A


4

Sử dụng a.getClasskhi bạn có một thể hiện của lớp / kiểu và bạn muốn nhận chính xác kiểu của nó. while a.classđược sử dụng khi bạn có typesẵn và bạn muốn tạo phiên bản của nó.
Đồng thời getClass()trả về kiểu thời gian chạy của trường hợp trong khi .classđược đánh giá tại thời điểm biên dịch.
Xem xét hiệu suất của getClass().class, .classcó hiệu suất tốt hơn getClass() .
Thí dụ :

public class PerfomanceClass {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        long time=System.nanoTime();
        Class class1="String".getClass();
        class1="String".getClass();
        class1="String".getClass();
        class1="String".getClass();

        System.out.println("time (getClass()) :"+(System.nanoTime()-time)+" ns");     


        long time2=System.nanoTime();
        Class class2=String.class;
        class2=String.class;
        class2=String.class;
        class2=String.class;

        System.out.println("time (.class):"+(System.nanoTime()-time2)+" ns");
    }

}

Đầu ra:

time (getClass()) : 79410 ns
time (.class)     : 8032 ns

1

Có một sự khác biệt tôi muốn bổ sung. Giả sử bạn có một phương thức khởi tạo của một lớp như được hiển thị bên dưới với một siêu lớp nhận một đối tượng Lớp. Bạn muốn rằng bất cứ khi nào một đối tượng lớp con được tạo thì đối tượng lớp của lớp con phải được chuyển cho lớp siêu. Mã bên dưới sẽ không biên dịch vì bạn không thể gọi một phương thức thể hiện trong một phương thức khởi tạo. Trong trường hợp đó nếu bạn thay thế myObject.getClass()bằng MyClass.class. Nó sẽ chạy hoàn hảo.

Class MyClass
{
    private MyClass myObject = new MyClass();
    public MyClass()
    {
        super(myObject.getClass()); //error line compile time error
    }
}

2
Có thể hiện của cùng một lớp với các biến thể hiện của cùng một lớp .... Bạn sẽ sử dụng hết không gian heap khi tạo các đối tượng một cách đệ quy.
Aniket Thakur

1

Điều thú vị là sự khác biệt về hiệu suất được đề cập trong ví dụ trên, dường như có liên quan đến các lý do khác. Sử dụng 3 Class khác nhau, về trung bình, hiệu suất sẽ gần giống nhau:

import java.util.LinkedHashMap;
public class PerfomanceClass {

public static void main(String[] args) {

    long time = System.nanoTime();
    Class class1 = "String".getClass();
    Class class11 = "Integer".getClass();
    Class class111 = "LinkedHashMap".getClass();

    System.out.println("time (getClass()) :" + (System.nanoTime() - time) + " ns");

    long time2 = System.nanoTime();
    Class class2 = String.class;
    Class class22 = Integer.class;
    Class class222 = LinkedHashMap.class;

    System.out.println("time (.class):" + (System.nanoTime() - time2) + " ns");
} }

Đầu ra sẽ giống như sau:

time (getClass()) :23506 ns 
time (.class):23838 ns

Và việc chuyển đổi thứ tự các cuộc gọi thậm chí sẽ getClass()nhanh hơn.

import java.util.LinkedHashMap;

public class PerfomanceClass {

public static void main(String[] args) {
    long time2 = System.nanoTime();
    Class class2 = LinkedHashMap.class;

    System.out.println("time (.class):" + (System.nanoTime() - time2) + " ns");

    long time = System.nanoTime();
    Class class1 = "LinkedHashMap".getClass();

    System.out.println("time (getClass()) :" + (System.nanoTime() - time) + " ns");
}}

Đầu ra:

time (.class):33108 ns
time (getClass()) :6622 ns

"Sử dụng 3 Class khác nhau". Nhưng trong phần getClass (), bạn không sử dụng 3 lớp khác nhau. Tất cả đều là Chuỗi và do đó getClass sẽ trả về java.lang.String cho tất cả các trường hợp.
Kilian

1

p.getClass(), trong đó plà một thể hiện của một đối tượng, trả về lớp thời gian chạy của đối tượng này p. pkhông thể là một kiểu sẽ gây ra lỗi thời gian biên dịch, nó phải là một thể hiện của một đối tượng.

// B extends A
A a = new B();
System.out.println(a.getClass());
//output: class B

p.classlà một biểu hiện. Các .classđược gọi là cú pháp lớp. plà một loại. Nó có thể là tên của một lớp, giao diện hoặc mảng và thậm chí là một kiểu nguyên thủy. a.getClass() == B.class.

Nếu một kiểu có sẵn và có một thể hiện thì có thể sử dụng getClassphương thức để lấy tên của kiểu đó. Nếu không, hãy sử dụng .classcú pháp

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.