ความแตกต่างระหว่างClass.forName()
และClass.forName().newInstance()
คืออะไร?
ฉันไม่เข้าใจความแตกต่างที่สำคัญ (ฉันได้อ่านบางอย่างเกี่ยวกับพวกเขา!) คุณจะกรุณาช่วยฉันหน่อยได้ไหม?
ความแตกต่างระหว่างClass.forName()
และClass.forName().newInstance()
คืออะไร?
ฉันไม่เข้าใจความแตกต่างที่สำคัญ (ฉันได้อ่านบางอย่างเกี่ยวกับพวกเขา!) คุณจะกรุณาช่วยฉันหน่อยได้ไหม?
คำตอบ:
บางทีตัวอย่างที่แสดงให้เห็นว่าวิธีการใช้ทั้งสองวิธีจะช่วยให้คุณเข้าใจสิ่งต่าง ๆ ได้ดีขึ้น ดังนั้นให้พิจารณาคลาสต่อไปนี้:
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)
Class
test.Demo.class
clazz
Class
จากนั้นการเรียกจะสร้างอินสแตนซ์ใหม่ของคลาสที่แสดงโดย วัตถุนี้ ชั้นถูกยกตัวอย่างเช่นถ้าโดยการแสดงออกที่มีรายการอาร์กิวเมนต์ที่ว่างเปล่า ในคำอื่น ๆ นี้เป็นที่นี่จริงเทียบเท่ากับและผลตอบแทนตัวอย่างใหม่ของclazz.newInstance()
Class
new
new Demo()
Demo
และการรันDemo
คลาสนี้จึงพิมพ์ผลลัพธ์ต่อไปนี้:
Hi!
ความแตกต่างใหญ่กับแบบดั้งเดิมnew
คือnewInstance
อนุญาตให้ยกตัวอย่างคลาสที่คุณไม่รู้จนกระทั่งรันไทม์ทำให้โค้ดของคุณมีไดนามิกมากขึ้น
ตัวอย่างทั่วไปคือ JDBC API ซึ่งโหลด ณ เวลารันไทม์ไดรเวอร์ที่แน่นอนที่จำเป็นในการทำงาน คอนเทนเนอร์ EJBs คอนเทนเนอร์ Servlet เป็นตัวอย่างที่ดีอื่น ๆ : พวกเขาใช้การโหลดรันไทม์แบบไดนามิกในการโหลดและสร้างส่วนประกอบที่พวกเขาไม่เคยรู้มาก่อนรันไทม์
ที่จริงถ้าคุณต้องการที่จะไปต่อให้ดูที่กระดาษ Ted Neward เข้าใจ Class.forName ()ที่ฉันถอดความในวรรคข้างต้น
แก้ไข (ตอบคำถามจาก OP ที่โพสต์เป็นความคิดเห็น): กรณีของไดรเวอร์ JDBC นั้นค่อนข้างพิเศษ ตามที่อธิบายไว้ในบทDriverManagerของการเริ่มต้นกับ JDBC API :
(... )
Driver
โหลดคลาสแล้วจึงลงทะเบียนโดยอัตโนมัติด้วยDriverManager
หนึ่งในสองวิธี:
Class.forName
โดยการเรียกวิธีการ โหลดคลาสไดร์เวอร์นี้อย่างชัดเจน เนื่องจากมันไม่ได้ขึ้นอยู่กับการตั้งค่าภายนอกวิธีการโหลดไดรเวอร์นี้จึงเป็นวิธีที่แนะนำสำหรับการใช้DriverManager
เฟรมเวิร์ก รหัสต่อไปนี้โหลดคลาสacme.db.Driver
:Class.forName("acme.db.Driver");
หาก
acme.db.Driver
มีการเขียนเพื่อให้การโหลดทำให้อินสแตนซ์ถูกสร้างขึ้นและยังเรียกDriverManager.registerDriver
ใช้อินสแตนซ์นั้นเป็นพารามิเตอร์ (อย่างที่ควรทำ) แสดงว่าอยู่ในDriverManager
รายการไดรเวอร์และพร้อมสำหรับการสร้างการเชื่อมต่อ( ... )
ในทั้งสองกรณีนี้มันเป็นความรับผิดชอบของใหม่โหลดชั้นเรียนเพื่อลงทะเบียนตัวเองโดยการเรียก
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
DriverManager.registerDriver
ที่พวกเขาจะเขียนด้วยบล็อกเริ่มต้นคงที่อินสแตนซ์ถูกสร้างขึ้นและผ่านเป็นพารามิเตอร์ของ การเรียกClass.forName
ใช้ไดรเวอร์ JDBC ทำให้เกิดการเริ่มต้นและดำเนินการบล็อกแบบคงที่ ดูที่java2s.com/Open-Source/Java-Document/Database-DBMS/…สำหรับตัวอย่าง ดังนั้นนี่เป็นกรณีพิเศษเนื่องจากคนขับภายใน
Class.forName () ให้วัตถุคลาสซึ่งมีประโยชน์สำหรับการสะท้อนกลับ วิธีการที่วัตถุนี้มีการกำหนดโดย Java ไม่ใช่โดยโปรแกรมเมอร์ที่เขียนชั้นเรียน พวกเขาเหมือนกันสำหรับทุกชั้นเรียน การเรียก newInstance () ที่ให้อินสแตนซ์ของคลาสนั้น (เช่นการเรียกClass.forName("ExampleClass").newInstance()
ว่าเทียบเท่ากับการโทรnew ExampleClass()
) ซึ่งคุณสามารถเรียกเมธอดที่คลาสกำหนดให้เข้าถึงฟิลด์ที่มองเห็นเป็นต้น
ในโลก 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()
ไป
1: หากคุณสนใจเฉพาะในบล็อกแบบคงที่ของคลาสการโหลดคลาสจะทำและจะดำเนินการบล็อกแบบสแตติกสิ่งที่คุณต้องมีคือ:
Class.forName("Somthing");
2: ถ้าคุณมีความสนใจในการโหลดคลาสเรียกใช้บล็อกแบบคงที่และต้องการเข้าถึงส่วนที่ไม่คงที่แล้วคุณต้องมีอินสแตนซ์แล้วคุณต้องการ:
Class.forName("Somthing").newInstance();
Class.forName () ได้รับการอ้างอิงถึง Class, Class.forName (). newInstance () พยายามที่จะใช้คอนสตรัคเตอร์ no-arg สำหรับคลาสที่จะส่งคืนอินสแตนซ์ใหม่
"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());
เพียงเพิ่มคำตอบข้างต้นเมื่อเรามีรหัสคงที่ (เช่นบล็อกรหัสเป็นอิสระตัวอย่าง) ที่จะต้องมีอยู่ในหน่วยความจำเราสามารถคืนคลาสดังนั้นเราจะใช้ Class.forname ("someName") อื่นถ้าเรา dont มีรหัสคงที่เราสามารถไป Class.forname (). newInstance ("someName") เพราะมันจะโหลดบล็อกรหัสวัตถุระดับ (ไม่คงที่) ไปยังหน่วยความจำ
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
คำสั่งนี้ถูกพิมพ์เพียงครั้งเดียวไม่สามครั้ง
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 () ใหม่
ตอนนี้คุณสามารถเข้าถึงฟิลด์และวิธีการที่มองเห็นได้ในชั้นเรียนทั้งหมดของคุณ