“ Class.forName ()” และ“ Class.forName (). newInstance ()” แตกต่างกันอย่างไร?


165

ความแตกต่างระหว่างClass.forName()และClass.forName().newInstance()คืออะไร?

ฉันไม่เข้าใจความแตกต่างที่สำคัญ (ฉันได้อ่านบางอย่างเกี่ยวกับพวกเขา!) คุณจะกรุณาช่วยฉันหน่อยได้ไหม?

คำตอบ:


247

บางทีตัวอย่างที่แสดงให้เห็นว่าวิธีการใช้ทั้งสองวิธีจะช่วยให้คุณเข้าใจสิ่งต่าง ๆ ได้ดีขึ้น ดังนั้นให้พิจารณาคลาสต่อไปนี้:

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();
    }
}

ตามที่อธิบายไว้ใน Javadoc ของการเรียกผลตอบแทนวัตถุที่เกี่ยวข้องกับการเรียนหรืออินเตอร์เฟซที่มีชื่อสตริงที่กำหนดคือผลตอบแทนมันซึ่งเป็นผลกระทบกับตัวแปรของชนิดClass.forName(String) Classtest.Demo.classclazzClass

จากนั้นการเรียกจะสร้างอินสแตนซ์ใหม่ของคลาสที่แสดงโดย วัตถุนี้ ชั้นถูกยกตัวอย่างเช่นถ้าโดยการแสดงออกที่มีรายการอาร์กิวเมนต์ที่ว่างเปล่า ในคำอื่น ๆ นี้เป็นที่นี่จริงเทียบเท่ากับและผลตอบแทนตัวอย่างใหม่ของclazz.newInstance() Classnewnew Demo()Demo

และการรันDemoคลาสนี้จึงพิมพ์ผลลัพธ์ต่อไปนี้:

Hi!

ความแตกต่างใหญ่กับแบบดั้งเดิมnewคือnewInstanceอนุญาตให้ยกตัวอย่างคลาสที่คุณไม่รู้จนกระทั่งรันไทม์ทำให้โค้ดของคุณมีไดนามิกมากขึ้น

ตัวอย่างทั่วไปคือ JDBC API ซึ่งโหลด ณ เวลารันไทม์ไดรเวอร์ที่แน่นอนที่จำเป็นในการทำงาน คอนเทนเนอร์ EJBs คอนเทนเนอร์ Servlet เป็นตัวอย่างที่ดีอื่น ๆ : พวกเขาใช้การโหลดรันไทม์แบบไดนามิกในการโหลดและสร้างส่วนประกอบที่พวกเขาไม่เคยรู้มาก่อนรันไทม์

ที่จริงถ้าคุณต้องการที่จะไปต่อให้ดูที่กระดาษ Ted Neward เข้าใจ Class.forName ()ที่ฉันถอดความในวรรคข้างต้น

แก้ไข (ตอบคำถามจาก OP ที่โพสต์เป็นความคิดเห็น): กรณีของไดรเวอร์ JDBC นั้นค่อนข้างพิเศษ ตามที่อธิบายไว้ในบทDriverManagerของการเริ่มต้นกับ JDBC API :

(... ) Driverโหลดคลาสแล้วจึงลงทะเบียนโดยอัตโนมัติด้วยDriverManagerหนึ่งในสองวิธี:

  1. Class.forNameโดยการเรียกวิธีการ โหลดคลาสไดร์เวอร์นี้อย่างชัดเจน เนื่องจากมันไม่ได้ขึ้นอยู่กับการตั้งค่าภายนอกวิธีการโหลดไดรเวอร์นี้จึงเป็นวิธีที่แนะนำสำหรับการใช้DriverManager เฟรมเวิร์ก รหัสต่อไปนี้โหลดคลาสacme.db.Driver:

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

    หากacme.db.Driverมีการเขียนเพื่อให้การโหลดทำให้อินสแตนซ์ถูกสร้างขึ้นและยังเรียก DriverManager.registerDriverใช้อินสแตนซ์นั้นเป็นพารามิเตอร์ (อย่างที่ควรทำ) แสดงว่าอยู่ใน DriverManagerรายการไดรเวอร์และพร้อมสำหรับการสร้างการเชื่อมต่อ

  2. ( ... )

ในทั้งสองกรณีนี้มันเป็นความรับผิดชอบของใหม่โหลดชั้นเรียนเพื่อลงทะเบียนตัวเองโดยการเรียกDriver DriverManager.registerDriverตามที่กล่าวไว้สิ่งนี้ควรทำโดยอัตโนมัติเมื่อโหลดคลาส

หากต้องการลงทะเบียนตัวเองในระหว่างการเริ่มต้นไดรเวอร์ JDBC มักจะใช้บล็อกการเริ่มต้นแบบคงที่เช่นนี้:

package acme.db;

public class Driver {

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

    ...
}

การโทรClass.forName("acme.db.Driver")ทำให้เกิดการเริ่มต้นของacme.db.Driverคลาสและการดำเนินการบล็อกเริ่มต้นแบบคงที่ และ Class.forName("acme.db.Driver")จะ "สร้าง" อินสแตนซ์ แต่นี่เป็นเพียงผลที่ตามมาของวิธีการใช้ไดร์เวอร์ JDBC (ดี)

เป็นหมายเหตุด้านข้างฉันจะพูดถึงว่าทั้งหมดนี้ไม่จำเป็นต้องใช้อีกต่อไปกับ JDBC 4.0 (เพิ่มเป็นแพ็คเกจเริ่มต้นตั้งแต่ Java 7) และคุณสมบัติการโหลดอัตโนมัติใหม่ของไดรเวอร์ JDBC 4.0 ดูJDBC 4.0 การปรับปรุงใน Java SE 6


แต่ยังอยู่ในไซต์นี้: java.sun.com/docs/books/tutorial/jdbc/basics/connecting.html
Johanna

2
ในเว็บไซต์ด้านบนเขียนว่า: "การเรียก Class.forName จะสร้างอินสแตนซ์ของไดรเวอร์โดยอัตโนมัติและลงทะเบียนกับ DriverManager ดังนั้นคุณไม่จำเป็นต้องสร้างอินสแตนซ์ของคลาสถ้าคุณต้องสร้างอินสแตนซ์ของคุณเอง คุณจะสร้างสำเนาที่ไม่จำเป็นโดยไม่จำเป็น แต่จะไม่มีอันตรายใด ๆ " มันหมายความว่าโดย Class.forName คุณจะสร้างอินสแตนซ์ outomatically และถ้าคุณต้องการที่จะสร้างอื่น ๆ มันจะทำให้อินสแตนซ์ที่ไม่จำเป็นดังนั้นทั้ง Calss.forName () และ Class.forName () newInstance () จะสร้างอินสแตนซ์ของ คนขับรถ !!
Johanna

10
ไดรเวอร์ JDBC เป็น "พิเศษ" DriverManager.registerDriverที่พวกเขาจะเขียนด้วยบล็อกเริ่มต้นคงที่อินสแตนซ์ถูกสร้างขึ้นและผ่านเป็นพารามิเตอร์ของ การเรียกClass.forNameใช้ไดรเวอร์ JDBC ทำให้เกิดการเริ่มต้นและดำเนินการบล็อกแบบคงที่ ดูที่java2s.com/Open-Source/Java-Document/Database-DBMS/…สำหรับตัวอย่าง ดังนั้นนี่เป็นกรณีพิเศษเนื่องจากคนขับภายใน
Pascal Thivent

1
ฉันสังเกตเห็นว่าในคำตอบอื่นการใช้ Class.newInstance () นั้นเป็นสิ่งที่ท้อแท้อย่างยิ่ง ขอแนะนำให้ใช้ Class.getConstructor () ตามด้วย Constructor.newInstance () มันหลีกเลี่ยงการปิดบังข้อยกเว้นที่เป็นไปได้
LS

"newInstance อนุญาตให้ยกตัวอย่างคลาสที่คุณไม่รู้จักจนกระทั่งรันไทม์" ทำวันของฉัน ขอบคุณ
รหัสกระตือรือร้น

37

Class.forName () ให้วัตถุคลาสซึ่งมีประโยชน์สำหรับการสะท้อนกลับ วิธีการที่วัตถุนี้มีการกำหนดโดย Java ไม่ใช่โดยโปรแกรมเมอร์ที่เขียนชั้นเรียน พวกเขาเหมือนกันสำหรับทุกชั้นเรียน การเรียก newInstance () ที่ให้อินสแตนซ์ของคลาสนั้น (เช่นการเรียกClass.forName("ExampleClass").newInstance()ว่าเทียบเท่ากับการโทรnew ExampleClass()) ซึ่งคุณสามารถเรียกเมธอดที่คลาสกำหนดให้เข้าถึงฟิลด์ที่มองเห็นเป็นต้น


29

ในโลก JDBC แนวปฏิบัติปกติ (ตาม JDBC API) คือคุณใช้Class#forName()ในการโหลดไดรเวอร์ JDBC ไดรเวอร์ JDBC ควรลงทะเบียนตัวเองในDriverManagerบล็อกคงที่:

package com.dbvendor.jdbc;

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

public class MyDriver implements Driver {

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

    public MyDriver() {
        //
    }

}

อัญเชิญClass#forName()จะดำเนินการทุกinitializers คงที่ วิธีนี้DriverManagerสามารถค้นหาไดรเวอร์ที่เกี่ยวข้องระหว่างไดรเวอร์ที่ลงทะเบียนโดย URL การเชื่อมต่อgetConnection()ซึ่งมีลักษณะดังนี้:

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");
}

แต่ยังมีรถไดรเวอร์ JDBC เริ่มต้นด้วยorg.gjt.mm.mysql.Driverตัวอย่างเช่นเป็นที่รู้จักกันดีซึ่งไม่ถูกต้องลงทะเบียนตัวเองภายในสร้างแทนการบล็อกแบบคงที่:

package com.dbvendor.jdbc;

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

public class BadDriver implements Driver {

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

}

วิธีเดียวที่จะทำให้การทำงานเป็นแบบไดนามิกคือโทรหาnewInstance()หลังจากนั้น! ไม่เช่นนั้นคุณจะพบตั้งแต่แรกพบ "SQLException: ไม่มีไดรเวอร์ที่เหมาะสม" นี่เป็นข้อผิดพลาดในไดรเวอร์ JDBC ไม่ใช่ในรหัสของคุณเอง ทุกวันนี้ไม่มีไดรเวอร์ JDBC หนึ่งตัวที่ควรมีข้อบกพร่องนี้ ดังนั้นคุณสามารถ (และควร) ออกnewInstance()ไป


17

1: หากคุณสนใจเฉพาะในบล็อกแบบคงที่ของคลาสการโหลดคลาสจะทำและจะดำเนินการบล็อกแบบสแตติกสิ่งที่คุณต้องมีคือ:

Class.forName("Somthing");

2: ถ้าคุณมีความสนใจในการโหลดคลาสเรียกใช้บล็อกแบบคงที่และต้องการเข้าถึงส่วนที่ไม่คงที่แล้วคุณต้องมีอินสแตนซ์แล้วคุณต้องการ:

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

คำตอบที่ยอดเยี่ยม! ชัดเจนและรัดกุม!
gaurav

6

Class.forName () ได้รับการอ้างอิงถึง Class, Class.forName (). newInstance () พยายามที่จะใช้คอนสตรัคเตอร์ no-arg สำหรับคลาสที่จะส่งคืนอินสแตนซ์ใหม่


3

"Class.forName ()" ส่งคืน Class-Type สำหรับชื่อที่กำหนด "newInstance ()" จะส่งคืนอินสแตนซ์ของคลาสนี้

ในประเภทที่คุณไม่สามารถเรียกวิธีการอินสแตนซ์ใด ๆ โดยตรง แต่สามารถใช้การสะท้อนกลับสำหรับคลาส ถ้าคุณต้องการทำงานกับวัตถุของคลาสคุณต้องสร้างอินสแตนซ์ของมัน (เหมือนกับการเรียก "new MyClass ()")

ตัวอย่างสำหรับ "Class.forName ()"

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

ตัวอย่างสำหรับ "Class.forName (). newInstance ()"

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

3

เพียงเพิ่มคำตอบข้างต้นเมื่อเรามีรหัสคงที่ (เช่นบล็อกรหัสเป็นอิสระตัวอย่าง) ที่จะต้องมีอยู่ในหน่วยความจำเราสามารถคืนคลาสดังนั้นเราจะใช้ Class.forname ("someName") อื่นถ้าเรา dont มีรหัสคงที่เราสามารถไป Class.forname (). newInstance ("someName") เพราะมันจะโหลดบล็อกรหัสวัตถุระดับ (ไม่คงที่) ไปยังหน่วยความจำ


1

Class.forName () ไม่ว่ากี่ครั้งที่คุณเรียกใช้เมธอด Class.forName () เพียงครั้งเดียวที่บล็อกแบบสแตติกได้รับการดำเนินการไม่ได้หลายครั้ง:

แพคเกจ forNameMethodDemo;

คลาสสาธารณะ 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();
    }

}

คลาสสาธารณะ DemoClass {

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

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

}

ผลลัพธ์จะเป็น:

in Static block in Instance block

in Static blockคำสั่งนี้ถูกพิมพ์เพียงครั้งเดียวไม่สามครั้ง


0

Class.forName () -> forName () เป็นวิธีการคงที่ของระดับชั้นมันส่งกลับวัตถุชั้นเรียนที่ใช้ในการสะท้อนไม่ใช่วัตถุระดับผู้ใช้ดังนั้นคุณสามารถเรียกวิธีการเรียนในชั้นเรียนเช่น getMethods (), getConstructors () เป็นต้น

หากคุณสนใจเพียงแค่เรียกใช้บล็อกแบบคงที่ของคลาส (Runtime ที่กำหนดไว้) ของคุณและรับเฉพาะข้อมูลของเมธอด, ตัวสร้าง, ตัวดัดแปลงและคลาสของคุณคุณสามารถทำได้กับวัตถุนี้ซึ่งคุณได้รับโดยใช้ Class.forName ()

แต่ถ้าคุณต้องการที่จะเข้าถึงหรือเรียกวิธีการเรียนของคุณ (คลาสที่คุณได้ให้ไว้ตอนรันไทม์) คุณต้องมีวัตถุเพื่อให้วิธี newInstance ของคลาส Class ทำเพื่อคุณมันสร้างอินสแตนซ์ใหม่ของคลาสและส่งกลับมาให้คุณ คุณเพียงแค่พิมพ์มันลงในชั้นเรียนของคุณ

ตัวอย่าง: สมมติว่าพนักงานคือชั้นเรียนของคุณแล้ว

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

// args [0] = อาร์กิวเมนต์บรรทัด cmd เพื่อให้คลาสที่ runtime

พนักงาน ob1 = a.newInstance ();

a.newInstance () คล้ายกับการสร้างวัตถุโดยใช้ Employee () ใหม่

ตอนนี้คุณสามารถเข้าถึงฟิลด์และวิธีการที่มองเห็นได้ในชั้นเรียนทั้งหมดของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.