CompletableFuture創(chuàng)建及功能使用全面詳解
引言
FutureTask對(duì)于get()方法容易造成阻塞,所以在其基礎(chǔ)上誕生了CompletableFuture。他們的關(guān)系就像i和i++的關(guān)系,FutureTask能做的,CompletableFuture也能做,并且更加高效,功能更加擴(kuò)展。
創(chuàng)建CompletableFuture
在CompletableFuture源碼注釋中,作者并不希望開發(fā)人員直接使用實(shí)例化去創(chuàng)建CompletableFuture,而是使用四大靜態(tài)方法。
實(shí)例化創(chuàng)建示例:
CompletableFuture completableFuture = new CompletableFuture();
CompletableFuture的四大靜態(tài)方法
supplyAsync(Supplier<U> supplier) ----有返回值
創(chuàng)建帶有返回值的異步任務(wù),類似方法ExecutorService
的 submit(Callable<T> task)
方法
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + " come in....."); int result = ThreadLocalRandom.current().nextInt(10); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return result; });
supplyAsync(Supplier<U> supplier,Executor executor)
ExecutorService threadPool = Executors.newFixedThreadPool(3); // 如果自己沒有創(chuàng)建線程池,則使用默認(rèn)的ForkJoinPool線程池 CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + " come in....."); int result = ThreadLocalRandom.current().nextInt(10); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return result; }, threadPool);
runAsync(Runnable runnable)----沒有返回值
創(chuàng)建沒有返回值的異步任務(wù),類似ExecutorService
submit(Runnable task)
方法
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> { System.out.println(Thread.currentThread().getName() + " come in....."); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(runAsync.get());
runAsync(Runnable runnable,Executor executor)
ExecutorService threadPool = Executors.newFixedThreadPool(3); // 如果沒有指定線程池,則使用默認(rèn)的ForkJoinPool線程池 CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> { System.out.println(Thread.currentThread().getName() + " come in....."); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } }, threadPool); System.out.println(runAsync.get());
CompletableFuture獲取值
get()
最直接的獲值方法,但會(huì)拋出異常。源碼如下
/** * Waits if necessary for this future to complete, and then * returns its result. * 等待線程計(jì)算完成以后獲取結(jié)果 * @return the result value * @throws CancellationException if this future was cancelled * @throws ExecutionException if this future completed exceptionally * @throws InterruptedException if the current thread was interrupted * while waiting */ public T get() throws InterruptedException, ExecutionException { Object r; return reportGet((r = result) == null ? waitingGet(true) : r); }
get(long timeout, TimeUnit unit)
如上,但可以加入等待時(shí)間,超過等待時(shí)間,拋出Timeout異常。源碼如下
/** * Waits if necessary for at most the given time for this future * to complete, and then returns its result, if available. * 最多等待給定的時(shí)間以完成此計(jì)算,然后返回其結(jié)果(如果可用) * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @return the result value * @throws CancellationException if this future was cancelled * @throws ExecutionException if this future completed exceptionally * @throws InterruptedException if the current thread was interrupted * while waiting * @throws TimeoutException if the wait timed out */ public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { Object r; long nanos = unit.toNanos(timeout); return reportGet((r = result) == null ? timedGet(nanos) : r); }
join()
直接獲取值,類似于get(),但與get()不同的是,join()不需要手動(dòng)拋出異常。代碼如下
/** * Returns the result value when complete, or throws an * (unchecked) exception if completed exceptionally. To better * conform with the use of common functional forms, if a * computation involved in the completion of this * CompletableFuture threw an exception, this method throws an * (unchecked) {@link CompletionException} with the underlying * exception as its cause. * 完成時(shí)返回結(jié)果值,如果異常完成則拋出(未選中)異常。 * 為了更好地符合通用函數(shù)形式的使用,如果完成此CompletableFuture所涉及的計(jì)算引發(fā)異常, * 則此方法將引發(fā)(未選中){@link CompletionException},其原因是基礎(chǔ)異常。 * * @return the result value * @throws CancellationException if the computation was cancelled * @throws CompletionException if this future completed * exceptionally or a completion computation threw an exception */ public T join() { Object r; return reportJoin((r = result) == null ? waitingGet(false) : r); }
getNow(T valueIfAbsent)
和join()相同,不需要手動(dòng)拋出異常,在執(zhí)行過程中會(huì)返回遇見的異常。不同的是可以給定默認(rèn)值,如果執(zhí)行錯(cuò)誤或未完成,返回給定的默認(rèn)值。源碼如下
/** * Returns the result value (or throws any encountered exception) * if completed, else returns the given valueIfAbsent. * 如果計(jì)算完成,則返回結(jié)果值(或引發(fā)任何遇到的異常),否則返回給定的默認(rèn)值 * @param valueIfAbsent the value to return if not completed * @return the result value, if completed, else the given valueIfAbsent * @throws CancellationException if the computation was cancelled * @throws CompletionException if this future completed * exceptionally or a completion computation threw an exception */ public T getNow(T valueIfAbsent) { Object r; return ((r = result) == null) ? valueIfAbsent : reportJoin(r); }
complete
通俗來說,在complete之前設(shè)定一個(gè)等待時(shí)間,如果在等待時(shí)間內(nèi)沒有計(jì)算出結(jié)果,則返回true,并返回complete給定的默認(rèn)值。反之為false,返回原定計(jì)算的值。源碼如下
/** * If not already completed, sets the value returned by {@link * #get()} and related methods to the given value. * * @param value the result value * @return {@code true} if this invocation caused this CompletableFuture * to transition to a completed state, else {@code false} */ public boolean complete(T value) { boolean triggered = completeValue(value); postComplete(); return triggered; }
示例:
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } return "123"; }); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } System.out.println(completableFuture.complete("給定的默認(rèn)值") + "\t" + completableFuture.join());
返回結(jié)果:
true 給定的默認(rèn)值
CompletableFuture的回調(diào)
其中,帶Async
后綴的函數(shù)表示需要連接的后置任務(wù)會(huì)被單獨(dú)提交到線程池中
,從而相對(duì)前置任務(wù)來說是異步運(yùn)行
的。除此之外,兩者沒有其他區(qū)別。
thenApply / thenAccept / thenRun互相依賴
thenApply()-----有入?yún)⒂蟹祷?/h2>
表示獲取上一個(gè)任務(wù)的執(zhí)行結(jié)果作為新任務(wù)的執(zhí)行參數(shù),有返回值。但遇見錯(cuò)誤時(shí)會(huì)終止后面所有的線程。
通俗來說,任務(wù)A執(zhí)行完執(zhí)行B,B需要A的結(jié)果,并且B有返回值
thenApply
也是有三個(gè)方法重載
// 后一個(gè)任務(wù)與前一個(gè)任務(wù)在同一線程執(zhí)行 public <U> CompletableFuture<U> thenApply( Function<? super T,? extends U> fn) { return uniApplyStage(null, fn); } // 后一個(gè)任務(wù)與前一個(gè)任務(wù)在不同線程中執(zhí)行 public <U> CompletableFuture<U> thenApplyAsync( Function<? super T,? extends U> fn) { return uniApplyStage(defaultExecutor(), fn); } //后一個(gè)任務(wù)使用自定義線程池執(zhí)行 public <U> CompletableFuture<U> thenApplyAsync( Function<? super T,? extends U> fn, Executor executor) { return uniApplyStage(screenExecutor(executor), fn); }
ps:示例
ExecutorService threadPool = Executors.newFixedThreadPool(3); CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + " come in....."); int result = ThreadLocalRandom.current().nextInt(10); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return result; }, threadPool); // 承接上一個(gè)多線程,有返回值 CompletableFuture<Integer> thenApply = supplyAsync.thenApply((result) -> { System.out.println("上一線程結(jié)果" + result); return ThreadLocalRandom.current().nextInt(10); }); System.out.println(thenApply.get()); threadPool.shutdown();
如果沒用使用thenApplyAsync()指定自己的線程池,線程池依舊使用的是默認(rèn)的ForkJoinPool線程池。
thenAccept() ----有入?yún)o返回
消費(fèi)型回調(diào)。接受上一個(gè)任務(wù)的結(jié)果作為參數(shù),但是沒有返回值。
通俗來說,任務(wù)A執(zhí)行完執(zhí)行B,B需要A的結(jié)果,B沒有返回值
// 后一個(gè)任務(wù)與前一個(gè)任務(wù)在同一線程執(zhí)行 public CompletableFuture<Void> thenAccept(Consumer<? super T> action) { return uniAcceptStage(null, action); } // 后一個(gè)任務(wù)與前一個(gè)任務(wù)在不同線程中執(zhí)行 public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) { return uniAcceptStage(defaultExecutor(), action); } //后一個(gè)任務(wù)使用自定義線程池執(zhí)行 public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor) { return uniAcceptStage(screenExecutor(executor), action); }
示例:
public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(2); CompletableFuture<Void> async = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } System.out.println("111111"); return 1; }, threadPool).thenApplyAsync((f) -> { // 手動(dòng)創(chuàng)建異常 System.out.println("222222"); f += 2; return f; }, threadPool).thenAcceptAsync(f -> { f += 3; System.out.println("最終的值:" + f); }, threadPool); System.out.println(Thread.currentThread().getName() + " 先去做其他事情了"); async.join(); threadPool.shutdown(); }
ps:只消費(fèi)
thenRun()----無入?yún)o返回
thenRun
的方法沒有入?yún)?,也沒有返回值。
通俗來說,任務(wù)A執(zhí)行完執(zhí)行B,并且B不需要A的結(jié)果,B沒有返回值
示例:
public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(2); CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } return "123"; },threadPool); completableFuture.thenRunAsync(() -> { System.out.println("thenRun 無入?yún)?,無返回值"); },threadPool); completableFuture.join(); threadPool.shutdown(); }
小結(jié)
thenApply()、thenAccept()、thenRun()的區(qū)別,示例代碼如下:
System.out.println(CompletableFuture.supplyAsync(()->"resultA").thenRun(()->{}).join()); System.out.println("《-----------------------------》"); System.out.println(CompletableFuture.supplyAsync(()->"resultA").thenAccept(System.out::println).join()); System.out.println("《-----------------------------》"); System.out.println(CompletableFuture.supplyAsync(()->"resultA").thenApply((f)-> "resultB").join());
結(jié)果:
null
《------------------------------------------》
resultA
null
《-------------------------------------------》
resultB
exceptionally()
如果執(zhí)行任務(wù)出現(xiàn)異常,則執(zhí)行 exceptionally 中的代碼塊,并且需要一個(gè)返回值。
示例:
public static void main(String[] args) throws Exception { CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + " come in....."); int a = 10 / 0; return 1; }); //whenComplete:參數(shù)列表 (T t, U u),如果執(zhí)行過程正常完成,則執(zhí)行該分支 System.out.println(completableFuture.whenComplete((t, u) -> { if (u==null) { System.out.println(t); } //exceptionally :如果執(zhí)行過程出現(xiàn)異常,則走該分支 }).exceptionally((f) -> { System.out.println("異常詳細(xì)信息:" + f.getMessage()); return 500; }).get()); }
whenComplete()
whenComplete
算是 exceptionally
和thenApply
的結(jié)合,將任務(wù)執(zhí)行的結(jié)果和異常作為回到方法的參數(shù),如果沒有發(fā)生異常則異常參數(shù)為null。源碼如下:
public CompletableFuture<T> whenComplete( BiConsumer<? super T, ? super Throwable> action) { return uniWhenCompleteStage(null, action); } public CompletableFuture<T> whenCompleteAsync( BiConsumer<? super T, ? super Throwable> action) { return uniWhenCompleteStage(asyncPool, action); } public CompletableFuture<T> whenCompleteAsync( BiConsumer<? super T, ? super Throwable> action, Executor executor) { return uniWhenCompleteStage(screenExecutor(executor), action); }
示例:
ExecutorService threadPool = Executors.newFixedThreadPool(3); // 自定義線程池,可以防止主線程立刻結(jié)束,導(dǎo)致守護(hù)線程forkjoin的問題 try { CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() ->{ System.out.println(Thread.currentThread().getName() + " come in....."); int result = ThreadLocalRandom.current().nextInt(10); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return result; }, threadPool).whenComplete((v,e) -> { if (e==null) { System.out.println(Thread.currentThread().getName() + " come in....."); System.out.println("上一個(gè)線程執(zhí)行的結(jié)果:" + v); } }).exceptionally(e -> { e.printStackTrace(); System.out.println("上一個(gè)線程執(zhí)行的異常" + e); return null; }); System.out.println(Thread.currentThread().getName() + " 忙其他的去了....."); } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); }
執(zhí)行結(jié)果:
handle()
表示獲取上一個(gè)任務(wù)的執(zhí)行結(jié)果作為新任務(wù)的執(zhí)行參數(shù),有返回值。相對(duì)于thenApply(),handle()可以將異常帶入下一個(gè)線程處理。源碼如下
public <U> CompletableFuture<U> handle( BiFunction<? super T, Throwable, ? extends U> fn) { return uniHandleStage(null, fn); }
示例:
public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(2); CompletableFuture<Integer> handle = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } System.out.println("111111"); return 1; }, threadPool).handle((f, n) -> { // 手動(dòng)創(chuàng)建異常,跳過此線程后面的動(dòng)作 int i = 10/0; System.out.println("222222"); f += 2; return f; }).handle((f, n) -> { // 如果檢測(cè)到上一線程有異常,可以處理 if (n!=null) { f=10; } System.out.println("333333"); f += 3; return f; }).whenComplete((f,e) -> { if (e==null) { System.out.println("計(jì)算結(jié)果為:" + f); } }).exceptionally(e -> { e.printStackTrace(); System.out.println(e.getMessage()); return null; }); System.out.println(Thread.currentThread().getName() + " 先去做其他事情了"); handle.join(); threadPool.shutdown(); }
運(yùn)行結(jié)果:
main 先去做其他事情了
111111
333333
計(jì)算結(jié)果為:13
CompletableFuture對(duì)計(jì)算速度的選用
使用applyToEither
可對(duì)兩個(gè)線程執(zhí)行速度進(jìn)行比較,獲取速度最快的執(zhí)行結(jié)果。源碼如下
public <U> CompletableFuture<U> applyToEither( CompletionStage<? extends T> other, Function<? super T, U> fn) { return orApplyStage(null, other, fn); } public <U> CompletableFuture<U> applyToEitherAsync( CompletionStage<? extends T> other, Function<? super T, U> fn) { return orApplyStage(asyncPool, other, fn); } public <U> CompletableFuture<U> applyToEitherAsync( CompletionStage<? extends T> other, Function<? super T, U> fn, Executor executor) { return orApplyStage(screenExecutor(executor), other, fn); }
示例:
CompletableFuture<String> supplyAsyncA = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } return "playA"; }); CompletableFuture<String> supplyAsyncB = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } return "playB"; }); CompletableFuture<String> applyToEither = supplyAsyncA.applyToEither(supplyAsyncB, f -> { return f + " is winner!"; }); System.out.println(Thread.currentThread().getName() + "----" + applyToEither.join());
applyToEither
、acceptEither
、runAfterEither
的區(qū)別與前面回調(diào)函數(shù)的區(qū)別一致,在于是否有返回值
thenAcceptBoth
當(dāng)兩個(gè)CompletionStage都執(zhí)行完成后,把結(jié)果一塊交給thenAcceptBoth來進(jìn)行消耗
applyToEither
兩個(gè)CompletionStage,誰(shuí)執(zhí)行返回的結(jié)果快,我就用那個(gè)CompletionStage的結(jié)果進(jìn)行下一步的轉(zhuǎn)化操作。
acceptEither
兩個(gè)CompletionStage,誰(shuí)執(zhí)行返回的結(jié)果快,我就用那個(gè)CompletionStage的結(jié)果進(jìn)行下一步的消耗操作。
runAfterEither
兩個(gè)CompletionStage,任何一個(gè)完成了都會(huì)執(zhí)行下一步的操作(Runnable)
runAfterBoth
兩個(gè)CompletionStage,都完成了計(jì)算才會(huì)執(zhí)行下一步的操作(Runnable)
CompletableFuture多任務(wù)合并
thenCombine
thenCompose 方法允許你對(duì)兩個(gè) CompletionStage 進(jìn)行流水線操作,第一個(gè)操作完成時(shí),將其結(jié)果作為參數(shù)傳遞給第二個(gè)操作。,源碼如下
public <U,V> CompletableFuture<V> thenCombine( CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) { return biApplyStage(null, other, fn); } public <U,V> CompletableFuture<V> thenCombineAsync( CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) { return biApplyStage(asyncPool, other, fn); } public <U,V> CompletableFuture<V> thenCombineAsync( CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor) { return biApplyStage(screenExecutor(executor), other, fn); }
示例:
CompletableFuture<Integer> integerCompletableFuture1 = CompletableFuture.supplyAsync(() -> { System.out.println("線程一。。。。。。啟動(dòng)"); try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } return 10; }); CompletableFuture<Integer> integerCompletableFuture2 = CompletableFuture.supplyAsync(() -> { System.out.println("線程二。。。。。。啟動(dòng)"); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } return 20; }); CompletableFuture<Integer> result = integerCompletableFuture1.thenCombine(integerCompletableFuture2, (x, y) -> { System.out.println("開始合并。。。。。。。。"); return x + y; }); System.out.println(result.join());
結(jié)果:
線程一。。。。。。啟動(dòng)
線程二。。。。。。啟動(dòng)
開始合并。。。。。。。。
30
allof
等待所有任務(wù)完成。源碼如下:
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) { return andTree(cfs, 0, cfs.length - 1); }
示例:
ExecutorService threadPool = Executors.newFixedThreadPool(2); CompletableFuture<String> integerCompletableFuture1 = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); System.out.println("線程一。。。。。。啟動(dòng)"); } catch (Exception e) { e.printStackTrace(); } return "華為"; },threadPool); CompletableFuture<String> integerCompletableFuture2 = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(1); System.out.println("線程二。。。。。。啟動(dòng)"); } catch (Exception e) { e.printStackTrace(); } return "小米"; },threadPool); CompletableFuture<Void> allOf = CompletableFuture.allOf(integerCompletableFuture1, integerCompletableFuture2); System.out.println(integerCompletableFuture1.join()); System.out.println(integerCompletableFuture2.join()); System.out.println("main..........end........."); threadPool.shutdown();
因?yàn)?code>allof需要等待所有線程執(zhí)行完畢,所以會(huì)先打印線程二。并等待線程一執(zhí)行完畢。
結(jié)果:
線程二。。。。。。啟動(dòng)
線程一。。。。。。啟動(dòng)
華為
小米
main..........end.........
anyof
等待其中一個(gè)任務(wù)完成。源碼如下:
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) { return orTree(cfs, 0, cfs.length - 1); }
示例(在任務(wù)合并時(shí)不同,將allof改為anyof):
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(integerCompletableFuture1, integerCompletableFuture2); System.out.println(anyOf.join()); System.out.println("main..........end........."); threadPool.shutdown();
因?yàn)榫€程二的執(zhí)行比線程一快,所以直接打印線程二。anyof返回的CompletableFuture,存儲(chǔ)的時(shí)先完成線程的返回結(jié)果。
結(jié)果:
線程二。。。。。。啟動(dòng)
小米
main..........end.........
線程一。。。。。。啟動(dòng)
以上就是CompletableFuture創(chuàng)建及功能使用全面詳解的詳細(xì)內(nèi)容,更多關(guān)于CompletableFuture功能創(chuàng)建的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
idea?intellij快速修復(fù)if語(yǔ)句缺少大括號(hào)的問題
這篇文章主要介紹了idea?intellij快速修復(fù)if語(yǔ)句缺少大括號(hào)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04springboot集成開發(fā)實(shí)現(xiàn)商場(chǎng)秒殺功能
這篇文章主要介紹了springboot集成實(shí)現(xiàn)商品秒殺功能,秒殺系統(tǒng)業(yè)務(wù)流程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12Spring實(shí)現(xiàn)IoC的多種方式小結(jié)
本篇文章主要介紹了Spring實(shí)現(xiàn)IoC的多種方式小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02SpringBoot熔斷機(jī)制之CircuitBreaker詳解
這篇文章主要介紹了SpringBoot熔斷機(jī)制之CircuitBreaker詳解,SpringBoot的熔斷機(jī)制在微服務(wù)架構(gòu)中扮演著重要角色,其中CircuitBreaker是其核心機(jī)制之一,用于防止服務(wù)的異常狀態(tài)影響到整個(gè)系統(tǒng)的運(yùn)作,需要的朋友可以參考下2023-10-10Java報(bào)錯(cuò)java.awt.AWTException: AWT的解決方法
在Java圖形用戶界面(GUI)編程中,java.awt.AWTException是一個(gè)常見的異常,它通常與AWT(Abstract Window Toolkit)組件相關(guān),這個(gè)異常可能在嘗試進(jìn)行與窗口、圖形環(huán)境或系統(tǒng)剪貼板等操作時(shí)拋出,本文將詳細(xì)探討AWTException的成因,并提供多種解決方案2024-12-12Maven安裝本地的jar包和創(chuàng)建帶模板的自定義項(xiàng)目的操作過程
這篇文章主要介紹了Maven安裝本地的jar包和創(chuàng)建帶模板的自定義項(xiàng)目,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-03-03