Sự khác biệt giữa các loại Class.forName () và Class Class.forName (). NewInstance (), là gì?


165

Sự khác biệt giữa Class.forName()và là Class.forName().newInstance()gì?

Tôi không hiểu sự khác biệt đáng kể (tôi đã đọc một cái gì đó về họ!). Liệu bạn có thể giúp mình không?

Câu trả lời:


247

Có thể một ví dụ minh họa cách cả hai phương pháp được sử dụng sẽ giúp bạn hiểu mọi thứ tốt hơn. Vì vậy, hãy xem xét các lớp sau:

package test;

public class Demo {

    public Demo() {
        System.out.println("Hi!");
    }

    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("test.Demo");
        Demo demo = (Demo) clazz.newInstance();
    }
}

Như đã giải thích trong javadoc của nó, việc gọi trả về đối tượng được liên kết với lớp hoặc giao diện với tên chuỗi đã cho tức là nó trả về bị ảnh hưởng đến biến kiểu .Class.forName(String) Classtest.Demo.classclazzClass

Sau đó, việc gọi tạo một thể hiện mới của lớp được đại diện bởi đối tượng này . Lớp được khởi tạo như thể bởi một biểu thức có danh sách đối số trống. Nói cách khác, đây thực sự là tương đương với a và trả về một thể hiện mới của .clazz.newInstance() Classnewnew Demo()Demo

Và chạy Demolớp này do đó in ra đầu ra sau:

Hi!

Sự khác biệt lớn với truyền thống newnewInstancecho phép khởi tạo một lớp mà bạn không biết cho đến khi chạy, làm cho mã của bạn năng động hơn.

Một ví dụ điển hình là API JDBC tải, trong thời gian chạy, trình điều khiển chính xác cần có để thực hiện công việc. Các thùng chứa EJB, các thùng chứa Servlet là những ví dụ điển hình khác: chúng sử dụng tải thời gian chạy động để tải và tạo các thành phần mà chúng không biết gì trước thời gian chạy.

Trên thực tế, nếu bạn muốn đi xa hơn, hãy xem bài viết của Ted Neward Hiểu về Class.forName () mà tôi đã diễn giải trong đoạn văn ở trên.

EDIT (trả lời một câu hỏi từ OP được đăng dưới dạng bình luận): Trường hợp trình điều khiển JDBC hơi đặc biệt. Như đã giải thích trong chương DriverManager về Bắt đầu với API JDBC :

(...) Một Driverlớp được tải và do đó tự động được đăng ký với DriverManager, theo một trong hai cách:

  1. bằng cách gọi phương thức Class.forName. Điều này rõ ràng tải lớp trình điều khiển. Vì nó không phụ thuộc vào bất kỳ thiết lập bên ngoài nào, nên cách tải trình điều khiển này là cách được khuyến nghị để sử dụng DriverManager khung. Đoạn mã sau tải lớp acme.db.Driver:

    Class.forName("acme.db.Driver");

    Nếu acme.db.Driverđã được viết để tải nó làm cho một cá thể được tạo và cũng gọi DriverManager.registerDrivervới cá thể đó là tham số (như nó nên làm), thì nó nằm trong DriverManagerdanh sách các trình điều khiển và có sẵn để tạo kết nối.

  2. (...)

Trong cả hai trường hợp này, trách nhiệm của lớp mới được tải Driverlà tự đăng ký bằng cách gọi DriverManager.registerDriver. Như đã đề cập, điều này nên được thực hiện tự động khi lớp được tải.

Để tự đăng ký trong quá trình khởi tạo, trình điều khiển JDBC thường sử dụng khối khởi tạo tĩnh như thế này:

package acme.db;

public class Driver {

    static {
        java.sql.DriverManager.registerDriver(new Driver());
    }

    ...
}

Việc gọi Class.forName("acme.db.Driver")gây ra việc khởi tạo acme.db.Driverlớp và do đó thực hiện khối khởi tạo tĩnh. Và Class.forName("acme.db.Driver")thực sự sẽ "tạo" một thể hiện nhưng đây chỉ là hệ quả của cách trình điều khiển JDBC (tốt) được triển khai.

Một lưu ý phụ, tôi muốn đề cập rằng tất cả những điều này không còn cần thiết nữa với JDBC 4.0 (được thêm dưới dạng gói mặc định kể từ Java 7) và tính năng tải tự động mới của trình điều khiển JDBC 4.0. Xem các cải tiến JDBC 4.0 trong Java SE 6 .



2
trong trang web trên có viết: "Gọi Class.forName sẽ tự động tạo một cá thể của trình điều khiển và đăng ký nó với DriverManager, vì vậy bạn không cần phải tạo một thể hiện của lớp. Nếu bạn tạo một cá thể của riêng bạn. , bạn sẽ tạo ra một bản sao không cần thiết, nhưng nó sẽ không gây hại gì. " điều đó có nghĩa là bởi Class.forName, bạn sẽ tạo một cá thể một cách kỳ lạ và nếu bạn muốn tạo một cá thể khác, nó sẽ tạo một cá thể không cần thiết. Vì vậy, cả Calss.forName () và Class.forName (). newInstance () sẽ tạo một thể hiện của người lái xe!!
Johanna

10
Các trình điều khiển JDBC là "đặc biệt", chúng được viết với một khối khởi tạo tĩnh trong đó một cá thể được tạo và được truyền dưới dạng tham số của DriverManager.registerDriver. Gọi Class.forNametrình điều khiển JDBC gây ra khởi tạo của nó và do đó thực thi khối tĩnh. Hãy xem java2s.com/Open-Source/Java-Document/Database-DBMS/iêu để lấy ví dụ. Vì vậy, đây thực sự là một trường hợp cụ thể do trình điều khiển bên trong.
Pascal Thivent

1
Tôi đã nhận thấy rằng trong một câu trả lời khác , sử dụng Class.newInstance () rất không được khuyến khích. Bạn nên sử dụng Class.getConstructor (), sau đó lần lượt là Constructor.newInstance (). Nó tránh che giấu các ngoại lệ có thể.
LS

"newInstance cho phép khởi tạo một lớp mà bạn không biết cho đến khi thời gian chạy" thực hiện ngày của tôi. Cảm ơn.
Mã nhiệt tình

37

Class.forName () cung cấp cho bạn đối tượng lớp, rất hữu ích cho việc phản chiếu. Các phương thức mà đối tượng này đã được xác định bởi Java, không phải bởi lập trình viên viết lớp. Chúng giống nhau cho mọi lớp học. Gọi newInstance () trên đó cung cấp cho bạn một thể hiện của lớp đó (tức là gọi Class.forName("ExampleClass").newInstance()nó tương đương với gọi new ExampleClass()), trên đó bạn có thể gọi các phương thức mà lớp định nghĩa, truy cập vào các trường hiển thị, v.v.


29

Trong thế giới JDBC, cách làm thông thường (theo API JDBC) là bạn sử dụng Class#forName()để tải trình điều khiển JDBC. Trình điều khiển JDBC nên đăng ký chính nó DriverManagertrong một khối tĩnh:

package com.dbvendor.jdbc;

import java.sql.Driver;
import java.sql.DriverManager;

public class MyDriver implements Driver {

    static {
        DriverManager.registerDriver(new MyDriver());
    }

    public MyDriver() {
        //
    }

}

Gọi Class#forName()sẽ thực hiện tất cả các khởi tạo tĩnh . Bằng cách này, DriverManagercó thể tìm thấy trình điều khiển được liên kết giữa các trình điều khiển đã đăng ký bằng URL kết nối trong getConnection()đó đại khái như sau:

public static Connection getConnection(String url) throws SQLException {
    for (Driver driver : registeredDrivers) {
        if (driver.acceptsURL(url)) {
            return driver.connect(url);
        }
    }
    throw new SQLException("No suitable driver");
}

Nhưng cũng có các trình điều khiển JDBC bị lỗi , bắt đầu với org.gjt.mm.mysql.Driverví dụ nổi tiếng, đăng ký không chính xác bên trong Trình xây dựng thay vì một khối tĩnh:

package com.dbvendor.jdbc;

import java.sql.Driver;
import java.sql.DriverManager;

public class BadDriver implements Driver {

    public BadDriver() {
        DriverManager.registerDriver(this);
    }

}

Cách duy nhất để làm cho nó hoạt động linh hoạt là gọi newInstance()sau đó! Nếu không, bạn sẽ phải đối mặt với cái nhìn đầu tiên không thể giải thích được "SQLException: không có trình điều khiển phù hợp". Một lần nữa, đây là một lỗi trong trình điều khiển JDBC, không phải trong mã của riêng bạn. Ngày nay, không một trình điều khiển JDBC nào có chứa lỗi này. Vì vậy, bạn có thể (và nên) rời newInstance()đi.


17

1: nếu bạn chỉ quan tâm đến khối tĩnh của lớp, việc tải lớp chỉ sẽ làm và sẽ thực thi các khối tĩnh thì tất cả những gì bạn cần là:

Class.forName("Somthing");

2: nếu bạn quan tâm đến việc tải lớp, thực thi các khối tĩnh của nó và cũng muốn truy cập vào phần không tĩnh của nó, thì bạn cần một thể hiện và sau đó bạn cần:

Class.forName("Somthing").newInstance();

Câu trả lời tuyệt vời! Rõ ràng và súc tích!
gaurav

6

Class.forName () nhận được tham chiếu đến Class, Class.forName (). NewInstance () cố gắng sử dụng hàm tạo no-arg cho Class để trả về một thể hiện mới.


3

"Class.forName ()" trả về Loại lớp cho tên đã cho. "newInstance ()" không trả về một thể hiện của lớp này.

Về kiểu bạn không thể gọi trực tiếp bất kỳ phương thức cá thể nào mà chỉ có thể sử dụng sự phản chiếu cho lớp. Nếu bạn muốn làm việc với một đối tượng của lớp, bạn phải tạo một thể hiện của nó (giống như gọi "new MyClass ()").

Ví dụ cho "Class.forName ()"

Class myClass = Class.forName("test.MyClass");
System.out.println("Number of public methods: " + myClass.getMethods().length);

Ví dụ cho "Class.forName (). NewInstance ()"

MyClass myClass = (MyClass) Class.forName("test.MyClass").newInstance();
System.out.println("String representation of MyClass instance: " + myClass.toString());

3

chỉ cần thêm vào các câu trả lời ở trên, khi chúng ta có một mã tĩnh (tức là khối mã là độc lập) cần phải có trong bộ nhớ, chúng ta có thể trả lại lớp vì vậy chúng ta sẽ sử dụng Class.forname ("someName") nếu chúng ta không có mã tĩnh, chúng ta có thể truy cập Class.forname (). newInstance ("someName") vì nó sẽ tải các khối mã cấp đối tượng (không tĩnh) vào bộ nhớ


1

Cho dù bạn gọi phương thức Class.forName () bao nhiêu lần, Chỉ khi khối tĩnh được thực thi không nhiều lần:

gói forNameMethodDemo;

lớp công khai MainClass {

    public static void main(String[] args) throws Exception {
        Class.forName("forNameMethodDemo.DemoClass");
        Class.forName("forNameMethodDemo.DemoClass");
        Class.forName("forNameMethodDemo.DemoClass");
        DemoClass demoClass = (DemoClass)Class.forName("forNameMethodDemo.DemoClass").newInstance();
    }

}

lớp học công cộng DemoClass {

static {
    System.out.println("in Static block");
}

{
    System.out.println("in Instance block");
}

}

đầu ra sẽ là:

in Static block in Instance block

Đây in Static blocktuyên bố được in một lần duy nhất chứ không phải ba lần.


0

Class.forName () -> forName () là phương thức tĩnh của lớp Class, nó trả về đối tượng lớp Class được sử dụng để phản ánh không phải đối tượng lớp người dùng, do đó bạn chỉ có thể gọi các phương thức lớp Class trên nó như getMethods (), getConstructor (), v.v.

Nếu bạn quan tâm đến việc chỉ chạy khối tĩnh của lớp (Thời gian chạy đã cho) và chỉ nhận thông tin về các phương thức, hàm tạo, Công cụ sửa đổi, v.v. của lớp bạn có thể thực hiện với đối tượng này mà bạn nhận được bằng Class.forName ()

Nhưng nếu bạn muốn truy cập hoặc gọi phương thức lớp của bạn (lớp mà bạn đã đưa ra trong thời gian chạy) thì bạn cần phải có đối tượng của nó để phương thức mới của lớp Class thực hiện điều đó cho bạn. Nó tạo ra thể hiện mới của lớp và trả lại cho bạn . Bạn chỉ cần gõ nó vào lớp của bạn.

ex-: giả sử Nhân viên là lớp của bạn thì

Lớp a = Class.forName (args [0]);

// args [0] = cmd dòng đối số để cung cấp cho lớp khi chạy.

Nhân viên ob1 = a.newInstance ();

a.newInstance () tương tự như tạo đối tượng bằng Employee () mới.

bây giờ bạn có thể truy cập tất cả các trường và phương thức hiển thị lớp của bạn.

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.