摘要:本文主要介紹了中的函數(shù)與原語(yǔ),由國(guó)內(nèi)管理平臺(tái)編譯呈現(xiàn)。原語(yǔ)與對(duì)象語(yǔ)言毫無(wú)關(guān)系。對(duì)象函數(shù)有個(gè)方法叫,返回?cái)?shù)字化原語(yǔ)的方法被稱為,或。你可以創(chuàng)建函數(shù)的特殊形式,使用原語(yǔ),而不是對(duì)象。
【編者按】本文作者為專注于自然語(yǔ)言處理多年的 Pierre-Yves Saumont,Pierre-Yves 著有30多本主講 Java 軟件開(kāi)發(fā)的書籍,自2008開(kāi)始供職于 Alcatel-Lucent 公司,擔(dān)任軟件研發(fā)工程師。
本文主要介紹了 Java 8 中的函數(shù)與原語(yǔ),由國(guó)內(nèi) ITOM 管理平臺(tái) OneAPM 編譯呈現(xiàn)。
Tony Hoare 把空引用的發(fā)明稱為“億萬(wàn)美元的錯(cuò)誤”。也許在 Java 中使用原語(yǔ)可以被稱為“百萬(wàn)美元的錯(cuò)誤”。創(chuàng)造原語(yǔ)的原因只有一個(gè):性能。原語(yǔ)與對(duì)象語(yǔ)言毫無(wú)關(guān)系。引入自動(dòng)裝箱和拆箱是件好事,不過(guò)還有很多有待發(fā)展。可能以后會(huì)實(shí)現(xiàn)(據(jù)說(shuō)已經(jīng)列入 Java 10的發(fā)展藍(lán)圖)。與此同時(shí),我們需要對(duì)付原語(yǔ),這可是個(gè)麻煩,尤其是在使用函數(shù)的時(shí)候。
Java 5/6/7的函數(shù)在 Java 8之前,使用者可以創(chuàng)建下面這樣的函數(shù):
public interface Function{ U apply(T t); } Function addTax = new Function () { @Override public Integer apply(Integer x) { return x / 100 * (100 + 10); } }; System.out.println(addTax.apply(100));
這些代碼會(huì)產(chǎn)生以下結(jié)果:
110
Java 8 帶來(lái)了 Function
FunctionaddTax = x -> x / 100 * (100 + 10); System.out.println(addTax.apply(100));
注意在第一個(gè)例子中,筆者用了一個(gè)匿名類文件來(lái)創(chuàng)建一個(gè)命名函數(shù)。在第二個(gè)例子中,使用 lambda 語(yǔ)法對(duì)結(jié)果并沒(méi)有任何影響。依然存在匿名類文件, 和一個(gè)命名函數(shù)。
一個(gè)有意思的問(wèn)題是:“x 是什么類型?”第一個(gè)例子中的類型很明顯。可以根據(jù)函數(shù)類型推斷出來(lái)。Java 知道函數(shù)參數(shù)類型是 Integer,因?yàn)楹瘮?shù)類型明顯是 Function
裝箱被自動(dòng)用于按照需要將 int 和 Integer 來(lái)回轉(zhuǎn)換。下文會(huì)詳談這一點(diǎn)。
可以使用匿名函數(shù)嗎?可以,不過(guò)類型就會(huì)有問(wèn)題。這樣行不通:
System.out.println((x -> x / 100 * (100 + 10)).apply(100));
這意味著我們無(wú)法用標(biāo)識(shí)符的值來(lái)替代標(biāo)識(shí)符 addTax 本身( addTax 函數(shù))。在本案例中,需要恢復(fù)現(xiàn)在缺失的類型信息,因?yàn)?Java 8 無(wú)法推斷類型。
最明顯缺乏類型的就是標(biāo)識(shí)符 x。可以做以下嘗試:
System.out.println((Integer x) -> x / 100 * 100 + 10).apply(100));
畢竟在第一個(gè)例子中,本可以這樣寫:
FunctionaddTax = (Integer x) -> x / 100 * 100 + 10;
這樣應(yīng)該足夠讓 Java 推測(cè)類型,但是卻沒(méi)有成功。需要做的是明確函數(shù)的類型。明確函數(shù)參數(shù)的類型并不夠,即使已經(jīng)明確了返回類型。這么做還有一個(gè)很嚴(yán)肅的原因:Java 8對(duì)函數(shù)一無(wú)所知。可以說(shuō)函數(shù)就是普通對(duì)象加上普通方法,僅此而已。因此需要像下面這樣明確類型:
System.out.println(((Function) x -> x / 100 * 100 + 10).apply(100));
否則,就會(huì)被解讀為:
System.out.println(((Whatever) x -> x / 100 * 100 + 10).whatever(100));
因此 lambda 只是在語(yǔ)法上起到簡(jiǎn)化匿名類在 Function(或 Whatever)接口執(zhí)行的作用。它實(shí)際上跟函數(shù)毫不相關(guān)。
假設(shè) Java 只有 apply 方法的 Function 接口,這就不是個(gè)大問(wèn)題。但是原語(yǔ)怎么辦呢?如果 Java 只是對(duì)象語(yǔ)言,Function 接口就沒(méi)關(guān)系。可是它不是。它只是模糊地面向?qū)ο蟮氖褂茫ㄒ虼吮环Q為面向?qū)ο螅ava 中最重要的類別是原語(yǔ),而原語(yǔ)與面向?qū)ο缶幊倘诤系貌⒉缓谩?/p>
Java 5 中引入了自動(dòng)裝箱,來(lái)協(xié)助解決這個(gè)問(wèn)題,但是自動(dòng)裝箱對(duì)性能產(chǎn)生了嚴(yán)重限制,這還關(guān)系到 Java 如何求值。Java 是一種嚴(yán)格的語(yǔ)言,遵循立即求值規(guī)則。結(jié)果就是每次有原語(yǔ)需要對(duì)象,都必須將原語(yǔ)裝箱。每次有對(duì)象需要原語(yǔ),都必須將對(duì)象拆箱。如果依賴自動(dòng)裝箱和拆箱,可能會(huì)產(chǎn)生多次裝箱和拆箱的大量開(kāi)銷。
其他語(yǔ)言解決這個(gè)問(wèn)題的方法有所不同,只允許對(duì)象,在后臺(tái)解決了轉(zhuǎn)化問(wèn)題。他們可能會(huì)有“值類”,也就是受到原語(yǔ)支持的對(duì)象。在這種功能下,程序員只使用對(duì)象,編譯器只使用原語(yǔ)(描述過(guò)于簡(jiǎn)化,不過(guò)反映了基本原則)。Java 允許程序員直接控制原語(yǔ),這就增大了問(wèn)題難度,帶來(lái)了更多安全隱患,因?yàn)槌绦騿T被鼓勵(lì)將原語(yǔ)用作業(yè)務(wù)類型,這在面向?qū)ο缶幊袒蚝瘮?shù)式程序設(shè)計(jì)中都沒(méi)有意義。(筆者將在另一篇文章中再談這個(gè)問(wèn)題。)
不客氣地說(shuō),我們不應(yīng)該擔(dān)心裝箱和拆箱的開(kāi)銷。如果帶有這種特性的 Java 程序運(yùn)行過(guò)慢,這種編程語(yǔ)言就應(yīng)該進(jìn)行修復(fù)。我們不應(yīng)該試圖用糟糕的編程技巧來(lái)解決語(yǔ)言本身的不足。使用原語(yǔ)會(huì)讓這種語(yǔ)言與我們作對(duì),而不是為我們所用。如果問(wèn)題不能通過(guò)修復(fù)語(yǔ)言來(lái)解決,那我們就應(yīng)該換一種編程語(yǔ)言。不過(guò)也許不能這樣做,原因有很多,其中最重要的一條是只有 Java 付錢讓我們編程,其他語(yǔ)言都沒(méi)有。結(jié)果就是我們不是在解決業(yè)務(wù)問(wèn)題,而是在解決 Java 的問(wèn)題。使用原語(yǔ)正是 Java 的問(wèn)題,而且問(wèn)題還不小。
現(xiàn)在不用對(duì)象,用原語(yǔ)來(lái)重寫例子。選取的函數(shù)采用類型 Integer 的參數(shù),返回 Integer。要取代這些,Java 有 IntUnaryOperator 類型。哇哦,這里不對(duì)勁兒!你猜怎么著,定義如下:
public interface IntUnaryOperator { int applyAsInt(int operand); ... }
這個(gè)問(wèn)題太簡(jiǎn)單,不值得調(diào)出方法 apply。
因此,使用原語(yǔ)重寫例子如下:
IntUnaryOperator addTax = x -> x / 100 * (100 + 10); System.out.println(addTax.applyAsInt(100));
或者采用匿名函數(shù):
System.out.println(((IntUnaryOperator) x -> x / 100 * (100 + 10)).applyAsInt(100));
如果只是為了 int 返回 int 的函數(shù),很容易實(shí)現(xiàn)。不過(guò)實(shí)際問(wèn)題要更加復(fù)雜。Java 8 的 java.util.function 包中有43種(功能)接口。實(shí)際上,它們不全都代表功能,可以分類如下:
21個(gè)帶有一個(gè)參數(shù)的函數(shù),其中2個(gè)為對(duì)象返回對(duì)象的函數(shù),19個(gè)為各種類型的對(duì)象到原語(yǔ)或原語(yǔ)到對(duì)象函數(shù)。2個(gè)對(duì)象到對(duì)象函數(shù)中的1個(gè)用于參數(shù)和返回值屬于相同類型的特殊情況。
9個(gè)帶有2個(gè)參數(shù)的函數(shù),其中2個(gè)為(對(duì)象,對(duì)象)到對(duì)象,7個(gè)為各種類型的(對(duì)象,對(duì)象)到原語(yǔ)或(原語(yǔ),原語(yǔ))到原語(yǔ)。
7個(gè)為效果,非函數(shù),因?yàn)樗鼈儾⒉环祷厝魏沃担抑槐挥糜讷@取副作用。(把這些稱為“功能接口”有些奇怪。)
5個(gè)為“供應(yīng)商”,意思就是這些函數(shù)不帶參數(shù),卻會(huì)返回值。這些可以是函數(shù)。在函數(shù)世界里,有些特殊函數(shù)被稱為無(wú)參函數(shù)(表明它們的元數(shù)或函數(shù)總量為0)。作為函數(shù),它們返回的值可能永遠(yuǎn)不變,因此它們?cè)试S將常量當(dāng)做函數(shù)。在
Java 8,它們的職責(zé)是根據(jù)可變語(yǔ)境來(lái)返回各種值。因此,它們不是函數(shù)。
真是太亂了!而且這些接口的方法有不同的名字。對(duì)象函數(shù)有個(gè)方法叫 apply,返回?cái)?shù)字化原語(yǔ)的方法被稱為 applyAsInt、applyAsLong,或 applyAsDouble。返回 boolean 的函數(shù)有個(gè)方法被稱為 test,供應(yīng)商的方法叫做 get 或 getAsInt、getAsLong、 getAsDouble,或 getAsBoolean。(他們沒(méi)敢把帶有 test 方法、不帶函數(shù)的 BooleanSupplier 稱為“謂語(yǔ)”。筆者真的很好奇為什么!)
值得注意的一點(diǎn),是并沒(méi)有對(duì)應(yīng) byte、 char、 short 和 float 的函數(shù),也沒(méi)有對(duì)應(yīng)兩個(gè)以上元數(shù)的函數(shù)。
不用說(shuō),這樣真是太荒謬了,然而我們又不得不堅(jiān)持下去。只要 Java 能推斷類型,我們就會(huì)覺(jué)得一切順利。然而,一旦試圖通過(guò)功能方式控制函數(shù),你將會(huì)很快面對(duì) Java 無(wú)法推斷類型的難題。最糟糕的是,有時(shí)候 Java 能夠推斷類型,卻會(huì)保持沉默,繼續(xù)使用另外一個(gè)類型,而不是我們想用的那一個(gè)。
如何發(fā)現(xiàn)正確類型假設(shè)筆者想使用三個(gè)參數(shù)的函數(shù)。由于 Java 8沒(méi)有現(xiàn)成可用的功能接口,筆者只有一個(gè)選擇:創(chuàng)建自己的功能接口,或者如前文(Java 8 怎么了之一)中所說(shuō),采取柯里化。創(chuàng)建三個(gè)對(duì)象參數(shù)、并返回對(duì)象的功能接口直截了當(dāng):
interface Function{ R apply(T, t, U, u, V, v); }
不過(guò),可能出現(xiàn)兩種問(wèn)題。第一種,可能需要處理原語(yǔ)。參數(shù)類型也幫不上忙。你可以創(chuàng)建函數(shù)的特殊形式,使用原語(yǔ),而不是對(duì)象。最后,算上8類原語(yǔ)、3個(gè)參數(shù)和1個(gè)返回值,只不過(guò)得到6561中該函數(shù)的不同版本。你以為甲骨文公司為什么沒(méi)有在 Java 8中包含 TriFunction?(準(zhǔn)確來(lái)說(shuō),他們只放了有限數(shù)量的 BiFunction,參數(shù)為 Object,返回類型為 int、long或double,或者參數(shù)和返回類型同為 int、long 或 Object,產(chǎn)生729種可能性中的9種結(jié)果。)
更好的解決辦法是使用拆箱。只需要使用 Integer、Long、Boolean 等等,接下來(lái)就讓 Java 去處理。任何其他行動(dòng)都會(huì)成為萬(wàn)惡之源,例如過(guò)早優(yōu)化(詳見(jiàn) http://c2.com/cgi/wiki?PrematureOptimization)。
另外一個(gè)辦法(除了創(chuàng)建三個(gè)參數(shù)的功能接口之外)就是采取柯里化。如果參數(shù)不在同一時(shí)間求值,就會(huì)強(qiáng)制柯里化。而且它還允許只用一種參數(shù)的函數(shù),將可能的函數(shù)數(shù)量限制在81之內(nèi)。如果只使用 boolean、int、long 和double,這個(gè)數(shù)字就會(huì)降到25(4個(gè)原語(yǔ)類型加上兩個(gè)位置的 Object 相當(dāng)于5 x 5)。
問(wèn)題在于在對(duì)返回原語(yǔ),或?qū)⒃Z(yǔ)作為參數(shù)的函數(shù)來(lái)說(shuō),使用柯里化可能有些困難。以下是前文(Java 8怎么了之一)中使用的同一例子,不過(guò)現(xiàn)在用了原語(yǔ):
IntFunction> intToIntCalculation = x -> y -> z -> x + y * z; private IntStream calculate(IntStream stream, int a) { return stream.map(intToIntCalculation.apply(b).apply(a)); } IntStream stream = IntStream.of(1, 2, 3, 4, 5); IntStream newStream = calculate(stream, 3);
注意結(jié)果不是“包含值5、8、11、14和17的流”,一開(kāi)始的流也不會(huì)包含值1、2、3、4和5。newStream 在這個(gè)階段并沒(méi)有求值,因此不包含值。(下篇文章將討論這個(gè)問(wèn)題)。
為了查看結(jié)果,就要對(duì)這個(gè)流求值,也許通過(guò)綁定一個(gè)終端操作來(lái)強(qiáng)制執(zhí)行。可以通過(guò)調(diào)用 collect 方法。不過(guò)在這個(gè)操作之前,筆者要利用 boxed 方法將結(jié)果與一個(gè)非終端函數(shù)綁定在一起。boxed 方法將流與一個(gè)能夠把原語(yǔ)轉(zhuǎn)為對(duì)應(yīng)對(duì)象的函數(shù)綁定在一起。這可以簡(jiǎn)化求值過(guò)程:
System.out.println(newStream.boxed().collect(toList()));
這顯示為:
[5,8, 11, 14, 17]
也可以使用匿名函數(shù)。不過(guò),Java 不能推斷類型,所以筆者必須提供協(xié)助:
private IntStream calculate(IntStream stream, int a) { return stream.map(((IntFunction>) x -> y -> z -> x + y * z).apply(b).apply(a)); } IntStream stream = IntStream.of(1, 2, 3, 4, 5); IntStream newStream = calculate(stream, 3);
柯里化本身很簡(jiǎn)單,只要?jiǎng)e忘了筆者在其他文章中提到過(guò)的一點(diǎn):
(x, y, z) -> w
解讀為:
x -> y -> z -> w
尋找正確類型稍微復(fù)雜一些。要記住,每次使用一個(gè)參數(shù),都會(huì)返回一個(gè)函數(shù),因此你需要一個(gè)從參數(shù)類型到對(duì)象類型的函數(shù)(因?yàn)楹瘮?shù)就是對(duì)象)。在本例中,每個(gè)參數(shù)類型都是 int,因此需要使用經(jīng)過(guò)返回函數(shù)類型參數(shù)化的 IntFunction。由于最終類型為 IntUnaryOperator(這是 IntStream 類的 map 方法的要求),結(jié)果如下:
IntFunction>>
筆者采用了三個(gè)參數(shù)中的兩種,所有參數(shù)類型都是 int ,因此類型如下:
IntFunction>
可以與使用自動(dòng)裝箱版本進(jìn)行比較:
Function>>
如果你無(wú)法決定正確類型,可以從使用自動(dòng)裝箱開(kāi)始,只要替換上你需要的最終類型(因?yàn)樗褪?map 參數(shù)的類型):
Function>
注意,你可能正好在你的程序中使用了這種類型:
private IntStream calculate(IntStream stream, int a) { return stream.map(((Function>) x -> y -> z -> x + y * z).apply(b).apply(a)); } IntStream stream = IntStream.of(1, 2, 3, 4, 5); IntStream newStream = calculate(stream, 3);
接下來(lái)可以用你使用的原語(yǔ)版本來(lái)替換每個(gè) Function
private IntStream calculate(IntStream stream, int a) { return stream.map(((Function>) x -> y -> z -> x + y * z).apply(b).apply(a)); }
然后是:
private IntStream calculate(IntStream stream, int a) { return stream.map(((IntFunction>) x -> y -> z -> x + y * z).apply(b).apply(a)); }
注意,三個(gè)版本都可編譯運(yùn)行,唯一的區(qū)別在于是否使用了自動(dòng)裝箱。
何時(shí)匿名
在以上例子中可見(jiàn),lambdas 很擅長(zhǎng)簡(jiǎn)化匿名類的創(chuàng)建,但是不給創(chuàng)建的范例命名實(shí)在沒(méi)有理由。命名函數(shù)的用處包括:
函數(shù)復(fù)用
函數(shù)測(cè)試
函數(shù)替換
程序維護(hù)
程序文檔管理
命名函數(shù)加上柯里化能夠讓函數(shù)完全獨(dú)立于環(huán)境(“引用透明性”),讓程序更安全、更模塊化。不過(guò)這也存在難度。使用原語(yǔ)增加了辨別柯里化函數(shù)類別的難度。更糟糕的是,原語(yǔ)并不是可使用的正確業(yè)務(wù)類型,因此編譯器也幫不上忙。具體原因請(qǐng)看以下例子:
double tax = 10.24; double limit = 500.0; double delivery = 35.50; DoubleStream stream3 = DoubleStream.of(234.23, 567.45, 344.12, 765.00); DoubleStream stream4 = stream3.map(x -> { double total = x / 100 * (100 + tax); if ( total > limit) { total = total + delivery; } return total; });
要用命名的柯里化函數(shù)來(lái)替代匿名“捕捉”函數(shù),確定正確類型并不難。有4個(gè)參數(shù),返回 DoubleUnaryOperator,那么類型應(yīng)該是 DoubleFunction
DoubleFunction>> computeTotal = x -> y -> z -> w -> { double total = w / 100 * (100 + x); if (total > y) { total = total + z; } return total; }; DoubleStream stream2 = stream.map(computeTotal.apply(tax).apply(limit).apply(delivery));
你怎么確定 x、y、z 和 w 是什么?實(shí)際上有個(gè)簡(jiǎn)單的規(guī)則:通過(guò)直接使用方法求值的參數(shù)在第一位,按照使用方法的順序,例如,tax、limit、delivery 對(duì)應(yīng)的就是 x、y 和 z。來(lái)自流的參數(shù)最后使用,因此它對(duì)應(yīng)的是 w。
不過(guò)還存在一個(gè)問(wèn)題:如果函數(shù)通過(guò)測(cè)試,我們知道它是正確的,但是沒(méi)有辦法確保它被正確使用。舉個(gè)例子,如果我們使用參數(shù)的順序不對(duì):
DoubleStream stream2 = stream.map(computeTotal.apply(limit).apply(tax).apply(delivery));
就會(huì)得到:
[1440.8799999999999, 3440.2000000000003, 2100.2200000000003, 4625.5]
而不是:
[258.215152, 661.05688, 379.357888, 878.836]
這就意味著不僅需要測(cè)試函數(shù),還要測(cè)試它的每次使用。如果能夠確保使用順序不對(duì)的參數(shù)不會(huì)被編譯,豈不是很好?
這就是使用正確類型體系的所有內(nèi)容。將原語(yǔ)用于業(yè)務(wù)類型并不好,從來(lái)就沒(méi)有好結(jié)果。但是現(xiàn)在有了函數(shù),就更多了一條不要這么做的理由。這個(gè)問(wèn)題將在其他文章中詳細(xì)討論。
敬請(qǐng)期待本文介紹了使用原語(yǔ)大概比使用對(duì)象更為復(fù)雜。在 Java 8中使用原語(yǔ)的函數(shù)一團(tuán)糟,不過(guò)還有更糟糕的。在下一篇文章中,筆者將談?wù)撛诹髦惺褂迷Z(yǔ)。
OneAPM 能為您提供端到端的 Java 應(yīng)用性能解決方案,我們支持所有常見(jiàn)的 Java 框架及應(yīng)用服務(wù)器,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸,定位異常根本原因。分鐘級(jí)部署,即刻體驗(yàn),Java 監(jiān)控從來(lái)沒(méi)有如此簡(jiǎn)單。想閱讀更多技術(shù)文章,請(qǐng)?jiān)L問(wèn) OneAPM 官方技術(shù)博客。
本文轉(zhuǎn)自 OneAPM 官方博客
原文地址: https://dzone.com/articles/whats-wrong-java-8-part-ii
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/65860.html
摘要:二叉樹(shù)是數(shù)據(jù)結(jié)構(gòu)中很重要的結(jié)構(gòu)類型,學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)也是深入學(xué)習(xí)編程的必由之路,這里我們簡(jiǎn)單介紹下我對(duì)于二叉樹(shù)的理解,水平有限,如有錯(cuò)誤還請(qǐng)不吝賜教。 二叉樹(shù)是數(shù)據(jù)結(jié)構(gòu)中很重要的結(jié)構(gòu)類型,學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)也是深入學(xué)習(xí)編程的必由之路,這里我們簡(jiǎn)單介紹下我對(duì)于二叉樹(shù)的理解,水平有限,如有錯(cuò)誤還請(qǐng)不吝賜教。 首先照例定義一個(gè)二叉樹(shù)的節(jié)點(diǎn)類 class Node { private int ...
摘要:順序一致性內(nèi)存模型有兩大特性一個(gè)線程中所有操作必須按照程序的順序執(zhí)行。這里的同步包括對(duì)常用同步原語(yǔ)的正確使用通過(guò)以下程序說(shuō)明與順序一致性兩種內(nèi)存模型的對(duì)比順序一致性模型中所有操作完全按程序的順序串行執(zhí)行。 java內(nèi)存模型 java內(nèi)存模型基礎(chǔ) happen-before模型 JSR-133使用happen-before的概念來(lái)闡述操作之間的內(nèi)存可見(jiàn)性。在JMM中,如果一個(gè)操作執(zhí)行的結(jié)...
摘要:今天開(kāi)始實(shí)戰(zhàn)虛擬機(jī)之二虛擬機(jī)的工作模式。總計(jì)有個(gè)系列實(shí)戰(zhàn)虛擬機(jī)之一堆溢出處理實(shí)戰(zhàn)虛擬機(jī)之二虛擬機(jī)的工作模式實(shí)戰(zhàn)虛擬機(jī)之三的新生代實(shí)戰(zhàn)虛擬機(jī)之四禁用實(shí)戰(zhàn)虛擬機(jī)之五開(kāi)啟編譯目前的虛擬機(jī)支持和兩種運(yùn)行模式。 今天開(kāi)始實(shí)戰(zhàn)Java虛擬機(jī)之二:虛擬機(jī)的工作模式。 總計(jì)有5個(gè)系列實(shí)戰(zhàn)Java虛擬機(jī)之一堆溢出處理實(shí)戰(zhàn)Java虛擬機(jī)之二虛擬機(jī)的工作模式實(shí)戰(zhàn)Java虛擬機(jī)之三G1的新生代GC實(shí)戰(zhàn)Jav...
摘要:如果是這樣,你一定要拿出個(gè)小時(shí)的時(shí)間,參加一次馬士兵老師的多線程與高并發(fā)訓(xùn)練營(yíng)。橫掃一切關(guān)于多線程的問(wèn)題,吊打所有敢于提問(wèn)并發(fā)問(wèn)題的面試官。 如果你平時(shí)只有CRUD的經(jīng)驗(yàn),從來(lái)不會(huì)了解多線程與高并發(fā),相信你一定一頭霧水。如果是這樣,你一定要拿出4個(gè)小時(shí)的時(shí)間,參加一次馬士兵老師的《多線程與高并發(fā)》訓(xùn)練營(yíng)。讓骨灰級(jí)掃地神僧馬...
摘要:大家好,小樂(lè)繼續(xù)接著上集樂(lè)字節(jié)反射之一反射概念與獲取反射源頭這次是之二實(shí)例化對(duì)象接口與父類修飾符和屬性一實(shí)例化對(duì)象之前我們講解過(guò)創(chuàng)建對(duì)象的方式有克隆反序列化,再加一種,根據(jù)對(duì)象,使用或者構(gòu)造器實(shí)例化對(duì)象。 大家好,小樂(lè)繼續(xù)接著上集:樂(lè)字節(jié)Java反射之一:反射概念與獲取反射源頭Class 這次是之二:實(shí)例化對(duì)象、接口與父類、修飾符和屬性 一:實(shí)例化對(duì)象 之前我們講解過(guò)創(chuàng)建對(duì)象的方式,有...
閱讀 3129·2021-11-15 18:14
閱讀 1781·2021-09-22 10:51
閱讀 3294·2021-09-09 09:34
閱讀 3511·2021-09-06 15:02
閱讀 1027·2021-09-01 11:40
閱讀 3194·2019-08-30 13:58
閱讀 2532·2019-08-30 11:04
閱讀 1088·2019-08-28 18:31