我有以下功能:
@SuppressWarnings("unchecked")
public static <T> T createInstance(String className, Object... args) {
try {
Class<?> clazz = Class.forName(className);
Class<?>[] parameterTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
return (T) clazz.getDeclaredConstructor(parameterTypes).newInstance(args);
} catch (Exception e) {
throw new RuntimeException("Error", e);
}
}
該函數應該獲得一個類名(如String
),加載它,如果找到了與Object... args
匹配的構造函數,則創建此類對象的新實例。
這段代碼可以工作,但它可能會在@HotSpotIntrinsicCandidate
方法上被調用。例如,如果調用者這樣做:
String str = createInstance("java.lang.String", "Some value");
... 然后函數將調用類java.lang.String
的構造函數:
@HotSpotIntrinsicCandidate
public String(String original)
我對@HotSpotIntrinsicCandidate
構造函數的理解是,JVM將對這些構造函數進行特殊處理,例如,這就是為什么我可以做String str = "some string";
,但我不能做MyObject myObj = "some string"
(假設MyObject
類有一個在參數中采用String
的構造函數)。
同時,我有點理解這些構造函數是“不可靠的”,因為它們嚴格依賴于JVM人員的決定。例如,該代碼也適用于裝箱原語,例如Integer
:
Integer myInt = createInstance("java.lang.Integer", "3");
... 但是自從Java 9以來,這些構造函數就被棄用了(這些類的新@HotSpotIntrinsicCandidate
是valueOf(...)
方法,它們不再是構造函數)。
我的感覺是,我應該拒絕對@Deprecated
或@HotSpotIntrinsicCandidate
構造函數的任何請求,以避免調用方在某個可能有一天被棄用的東西上構建積木。
- 有人能證實(或否認)我的假設嗎?
- 假設我的直覺是正確的,我可以輕松地
getAnnotation(Deprecated.class)
在我檢索的Constructor
上(因為這個注釋是公共的),但我不能getAnnotation(HotSpotIntrinsicCandidate.class)
,因為這個注釋是package-private到jdk.internal
。如果是這樣的話,我怎樣才能檢測出我應該拒絕構建的所有類?
Completely wrong.
HotSpot指的是JVM將以極其愚蠢/緩慢的速度運行所有java代碼,甚至比您想象的還要慢,因為它還做了大量看似毫無意義的簿記工作(這種“如果”分支的頻率是以一種方式還是以另一種方式——讓我們無緣無故地計算一下)。
... 因為有了所有這些簿記,JVM可以很容易地識別出消耗99%資源的1%代碼,然后將花費相當多的時間(并使用它所做的所有簿記)來生成一些經過微調的機器代碼,專門用于您運行的確切硬件,并針對迄今為止觀察到的情況(即簿記)進行優化以最快地運行。現在你有了極快的代碼。考慮到這1%的代碼實際上占用了99%的資源,這就是JVM速度非常快的原因。“重寫”的過程是熱點。這純粹是一件Java-The-Virtual-Machine的事情。Java-The-Language沒有熱點,不知道它是什么,你說的是一種語言功能,因此-不,這根本不是@hospotintrisccandidate的意思。
之所以可以編寫
String x = "hello"
,是因為java語言規范規定了這一點。就這樣。這是唯一的原因。因為它在規范中是硬編碼的(這和為什么你可以寫Integer i = 5;
是一樣的,因為'5'在JLS中被寫為一個概念,如果代碼沒有編譯,但是將一個原語轉換成匹配的包裝類型會使它工作,那么假設程序員打算這么做,并編譯它,就像它說Integer.valueOf(5)
取而代之的是,這被稱為“自動裝箱”,也寫在JLS中,這使其起作用。同樣,@HotspotIntrinsicCandidate
與此無關)。@HIC指的是一個JVM實現可以自由地擁有一個"pre-written“經過微調的機器代碼版本,可以直接在JVM實現本身中使用,JVM運行人員應該尋找,然后使用這樣的東西,如果它可用的話。這并不能保證(但大多數JVM實現都是這樣工作的;記錄“頻率”并沒有多大意義)
j.l.String
的hashCode()
方法已被調用——只要你在hashmaps中插入字符串,就意味著它將被大量調用。不妨“預編譯”它的機器代碼,并將其直接注入java.exe
。為什么要等,對嗎?考慮到這純粹是一個優化動作,它的存在與否絕對不會對公開該構造函數是否“是個好主意”產生任何影響。
貶低是一種怪獸。它被用于許多不同的事情。不使用這樣的構造函數沒有什么特別的意義——當然,它們將來可能會消失,但不推薦使用標記還有其他原因(有時只是“這種方法是個壞主意,因為大多數使用它的人都感到困惑”。例如,
j.u.Date
的getYear()
方法被不推薦使用。我很樂意以1000比1的幾率打賭,它在10年后仍然存在。它不會有任何用途。使用它只是個壞主意。這讓我們。。。
這是個壞主意,句號。
這不管用。
Trivial example:
上述方法不起作用!!
那是因為在5上調用
.getClass()
會得到java.lang.Integer.class
。這與Number(它是它的一個子類)完全“兼容”——new Example(5)
工作正常。然而,getConstructor()
會精確地搜索您所要求的內容,這是一個接受1整數的構造函數。那是不存在的。你有一個構造函數,它接受整數的超類型。解決這個問題的唯一方法是使用
.getDeclaredConstructors()
,遍歷所有參數,并在每個參數上使用.isSubclass
和友元,如果愿意,還可以添加代碼來處理varargs調用,然后使用它。這速度太慢了。
更一般地說,這種方法意味著您要考慮laissez-fairecompiler-less語言(通常稱為“腳本語言”)最差的方面,以及java最差的方面,同時忽略最好的部分。這有什么意義?
我打賭你正在尋找以下幾種選擇:
構建和編譯infra比您想象的要好得多
有很多工具非常聰明,可以知道需要編譯哪些java代碼(即只編譯您更改的內容),并且只編譯這些內容。例如,eclipse(編輯器)將自動地、幾乎即時地持續編譯您的所有內容。如果這是為了避免compile-build循環,那么沒有必要這樣做。
您甚至可以運行
java foo.java
,JVM將自動編譯并運行它,用于普通的one-file項目。有java-esque種腳本語言
如果你想的話,你可以在java中運行javascript。GraalVM項目有一個只運行javascript的java庫。為什么不用呢?如果您希望語法特別是java-like,可以使用beanshell和groovy。
有模塊系統和動態裝載機
您可以在plain jane java(例如,在javadocs或OSGi項目中查找ClassLoader類)中設置一個系統,該系統可以在運行的JVM中運行'live-reload類。如果目標是啟動JVM,然后在它運行時手動“在其中編程”,那么就可以使用它們。
調試器很棒
調試器可以插入運行的JVM,即使在Internet連接上,如果需要的話,只需在斷點thread中間運行按需鍵入的代碼即可。例如,在eclipse中,只要在任何地方設置一個斷點,運行這個東西(單擊在調試模式下運行它的“bug”按鈕),然后打開調試shell并鍵入任何需要的內容。你甚至可以訪問局部變量,當斷點被擊中時,它們會有任何值。如果愿意,可以對服務器上運行的“實時代碼”執行此操作。
There's JSP
我強烈反對這種做法,但JSP是一種可以在
.jsp
文件中的HTML中插入java代碼,并讓web服務器運行它的東西。它會處理所有的事情——檢測它的變化,(重新)編譯它,并運行它。您可以簡單地編輯JSP文件,然后重新加載一個頁面,現在就可以看到新的內容了。有些要求你告訴它:是的,請檢查更改和re-compile。