詳見:https://blog.csdn.net/zai_xia/article/details/80026325
擴展:java反射機制與動態加載類 https://www.cnblogs.com/wzk-0000/p/9322866.html
在講解動態加載類之前呢,我們先弄清楚為什么要動態加載類,靜態加載不行嗎?我們可以看下面的實例:
我在文件夾里寫了Office.java 類和 Word.java類,如下:
Office.java
class Office{ public static void main(String[] args){ if(args[0].equals("Word")){ Word w = new Word(); w.start(); } if(args[0].equals("Excel")){ Excel e = new Excel(); e.start(); } }
Word.java
class Word{ public void start(){ System.out.println("Word Start"); }
編譯會報錯,這當然沒問題,因為確實沒有寫Excel.java類。
但是這個Excel類我們一定會用到嗎?如果這個Excel類需要很久才能寫出來,在此我們也不能使用其他功能嗎?后面如果一個類出問題了,這個系統是不是就癱瘓了?
所以這就體現出動態加載類的好處了,動態加載類是按需加載的,你需要什么類,就加載什么類,一個類的狀態,不會影響到另一個類的使用。
所以我們可以將Office類改造如下:
class Office{ public static void main(String[] args){ try{ Class c = Class.forName(args[0]); Word w = (Word)c.newInstance(); w.start(); } catch(Exception e){ e.printStackTrace(); } }
雖然我們還是沒有寫Excel類,但此時我們再編譯Office.java文件,編譯通過;
成功按照我們預想的結果運行,這樣Word類就可以單獨運行。
但是這樣還是不夠規范,因為現在只能加載Word類類型的類文件,為了使程序有更好的擴展性,我們添加一個接口,只有讓所有的功能類實現該接口即可。完善如下:
OfficeAble.java
interface OfficeAble{ public void start(); }
Office.java
class Office{ public static void main(String[] args){ try{ Class c = Class.forName(args[0]); OfficeAble oa = (OfficeAble)c.newInstance(); oa.start(); } catch(Exception e){ e.printStackTrace(); } }
Word.java
class Word implements OfficeAble{ public void start(){ System.out.println("Word Start"); } }
這樣改造之后,我后面要添加一個Excel類,乃至添加其他的類,只需要實現OfficeAble接口就可以了,不需要改動Office這個類和其他的功能類,擴展性很強,這就是動態加載的優勢。
擴展:java的反射機制與動態加載
什么是java反射機制?
1、當程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言。我們認為java並不是動態語言,但是它卻有一個非常突出的動態相關機制,俗稱:反射。
IT行業里這么說,沒有反射也就沒有框架,現有的框架都是以反射為基礎。在實際項目開發中,用的最多的是框架,填的最多的是類,反射這一概念就是將框架和類揉在一起的調和劑。所以,反射才是接觸項目開發的敲門磚。
2、java中的new方法是靜態加載,因為new方法是在編譯階段就會檢查,而不是在運行階段。反射是可以在運行時創建對象,調用對象的方法、變量等。
3、Java反射機制主要提供了以下功能:在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具有的成員變量和方法;在運行時調用任意一個對象的方法;生成動態代理。
例如:
package service.impl; public class Test { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { F f = new F(); // 第一種表達方式 Class c1 = F.class;// 這種表達方式同時也告訴了我們任何一個類都有一個隱含的靜態成員變量class // 第二種表達方式 Class c2 = f.getClass();// 這種表達方式在已知了該類的對象的情況下通過getClass方法獲取 // 第三種表達方式 Class c3 = Class.forName("service.impl.F");// 類的全稱 System.out.println(c1.getName()); System.out.println(c2.getName()); System.out.println(c3.getName()); F temp = (F) c1.newInstance();//這里可以是c1/c2/c3 System.out.println(temp.f); temp.save(); } } class F { public String f = "test"; public void save() { System.out.println("save..."); } }
運行結果
service.impl.F
service.impl.F
service.impl.F
test
save...
動態加載 。。。。
如果沒有不帶參數的構造函數,則不能通過c1.newInstance() 構造對象,需要先獲取構造函數,再構造對象
Class c = Class.forName("com.my.app.Book"); Constructor con = c.getConstructor(String.class); Book book = (Book) con.newInstance("語文"); System.out.println(book);
4.獲取方法,運行方法:
package com.my.app; public class Book { private String name ="金1瓶1梅"; public Book() { super(); } public Book(String name) { super(); this.name = name; } public void outDate(){ System.out.println("1111111 "); } public void outDate(String string){ System.out.println("1111111 " +string); } public void outDate(String string,int num){ System.out.println("1111111 " +string +" " +num); } }
Class c = Class.forName("com.my.app.Book"); Book book = (Book) c.newInstance(); book.outDate(); Method[] methods = c.getMethods(); Method method = c.getMethod("outDate", String.class,int.class); method.invoke(book, "6465465",55);
Method method = c.getMethod("outDate", String.class);
method.invoke(book, "6465465");
獲取變量
Class c = Class.forName("com.my.app.Book"); Book book = (Book) c.newInstance(); Field field1 = c.getField("price");//根據變量名,返回一個具體的具有public屬性的成員變量 Field field2 = c.getDeclaredField("price");//根據變量名,返回一個成員變量(不分public和非public屬性) System.out.println(field1.getDouble(book)); System.out.println(field2.getName());//獲取變量名 System.out.println(field2.getType());//獲取變量類型 System.out.println(field2.get(book));//獲取變量值 System.out.println(field2.getDouble(book));//獲取變量值 //獲取私有字段 模擬get set方法 Field nameField = c.getDeclaredField("name"); //取消訪問私有字段的合法性檢查 nameField.setAccessible(true); System.out.println(nameField.get(book)); nameField.set(book, "BBBB"); System.out.println(book);