Apache httpclient okhttp(2)

學習鏈接

Apache httpclient & okhttp(1)
Apache httpclient & okhttp(2)

okhttp github

okhttp官方使用文檔

okhttp官方示例代碼

OkHttp使用介紹
OkHttp使用進階 譯自OkHttp Github官方教程

SpringBoot 整合okHttp okhttp3用法

Java中常用的HTTP客戶端庫:OkHttp和HttpClient(包含請求示例代碼)

深入淺出 OkHttp 源碼解析及應用實踐

文章目錄

    • 學習鏈接
  • okhttp
    • okhttp概述
      • 特點
    • 快速入門
      • pom.xml
      • get請求
      • Post請求
    • 示例代碼
      • 同步請求
      • 異步請求
      • 請求頭&響應頭
      • post + 請求體
      • 流式傳輸
        • 流式傳輸擴展示例
          • Test05StreamClient
          • StreamController
      • 文件傳輸
      • 表單提交
      • 文件上傳
      • 響應緩存
      • 取消調用
      • 超時
      • 每次調用配置
      • 處理鑒權
    • 攔截器
    • 事件監聽

okhttp

okhttp概述

HTTP是現代應用程序的網絡方式。這是我們交換數據和媒體的方式。高效地使用HTTP可以讓您的東西加載更快并節省帶寬。

OkHttp使用起來很方便。它的請求/響應API設計具有流式構建和不可變性。它支持同步阻塞調用帶有回調的異步調用

特點

OkHttp是一個高效的默認HTTP客戶端

  • HTTP/2支持允許對同一主機的所有請求共享一個套接字。
  • 連接池減少了請求延時(如果HTTP/2不可用)。
  • 透明GZIP縮小了下載大小。
  • 響應緩存完全避免了重復請求的網絡。

OkHttp遵循現代HTTP規范,例如

  • HTTP語義-RFC 9110
  • HTTP緩存-RFC 9111
  • HTTP/1.1-RFC 9112
  • HTTP/2-RFC 9113
  • Websocket-RFC 6455
  • SSE-服務器發送的事件

快速入門

pom.xml

注意:okhttp的3.9.0版本用的是java,okhttp4.12.0用的是kotlin。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.zzhua</groupId><artifactId>demo-okhttp</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!--<okhttp.version>3.9.0</okhttp.version>--><!--<okhttp.version>3.14.9</okhttp.version>--><!-- OkHttp從4.x版本開始轉向Kotlin, Kotlin 代碼可以被編譯成標準的 JVM 字節碼運行,這與 Java 代碼的最終執行形式完全兼容。 --><okhttp.version>4.12.0</okhttp.version></properties><dependencies><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>${okhttp.version}</version></dependency></dependencies></project>

get請求

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;import java.io.IOException;public class Test01 {public static void main(String[] args) {OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url("http://www.baidu.com").build();try (Response response = client.newCall(request).execute()) {System.out.println(response.body().string());} catch (IOException e) {throw new RuntimeException(e);}}}

Post請求

import okhttp3.*;
public class Test02 {public static void main(String[] args) {OkHttpClient client = new OkHttpClient();RequestBody body = RequestBody.create(MediaType.parse("application/json"), "{\"username\":\"zzhua\"}");Request request = new Request.Builder().url("http://localhost:8080/ok01").post(body).build();try (Response response = client.newCall(request).execute()) {System.out.println(response.body().string());} catch (Exception e) {throw new RuntimeException(e);}}}

示例代碼

這些示例代碼來自:https://square.github.io/okhttp/recipes,

官方也有對應的代碼:https://github.com/square/okhttp/blob/okhttp_3.14.x/samples
在這里插入圖片描述
在這里插入圖片描述

同步請求

下載文件,打印headers,并將其響應正文打印為字符串。

  • 響應正文上的string()方法對于小文檔來說既方便又高效。但是如果響應正文很大(大于1 MiB),避免string(),因為它會將整個文檔加載到內存中。在這種情況下,更推薦將正文作為流處理。
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;import java.io.IOException;public class TestSynchronous {private final static OkHttpClient client = new OkHttpClient();public static void main(String[] args) {Request request = new Request.Builder().url("https://publicobject.com/helloworld.txt").build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful())throw new IOException("Unexpected code " + response);// 獲取響應頭Headers responseHeaders = response.headers();for (int i = 0; i < responseHeaders.size(); i++) {System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));}// 獲取響應體System.out.println(response.body().string());} catch (IOException e) {throw new RuntimeException(e);}}}

異步請求

在工作線程中下載文件,并在響應可讀取時觸發回調。該回調會在響應頭準備就緒后觸發,但讀取響應體仍可能阻塞線程。當前OkHttp未提供異步API以分塊接收響應體內容。

@Slf4j
public class Test02Async {public static void main(String[] args) {log.info("main start");OkHttpClient okHttpClient = new OkHttpClient();Request requeset = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();Call call = okHttpClient.newCall(requeset);log.info("call.enqueue");// 執行回調的線程不是main線程call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {log.info("=========請求失敗=========: {}", e);}@Overridepublic void onResponse(Call call, Response response) throws IOException {log.info("=========獲得響應=========");try (ResponseBody responseBody = response.body()) {if (!response.isSuccessful())throw new IOException("Unexpected code " + response);Headers responseHeaders = response.headers();for (int i = 0, size = responseHeaders.size(); i < size; i++) {System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));}System.out.println(responseBody.string());}}});log.info("main end");}}

請求頭&響應頭

public class TestHeader {public static void main(String[] args) {OkHttpClient okHttpClient = new OkHttpClient();Request request = new Request.Builder().url("https://api.github.com/repos/square/okhttp/issues")// 單個header值.header("User-Agent", "OkHttp Headers.java")// 多個header值.addHeader("Accept", "application/json; q=0.5").addHeader("Accept", "application/vnd.github.v3+json").build();try (Response response = okHttpClient.newCall(request).execute()) {if (!response.isSuccessful())throw new IOException("Unexpected code " + response);System.out.println("Server: " + response.header("Server"));System.out.println("Date: " + response.header("Date"));// 多個響應頭使用headers()獲取System.out.println("Vary: " + response.headers("Vary"));} catch (IOException e) {throw new RuntimeException(e);}}}

post + 請求體

使用HTTP POST向服務發送請求正文。此示例將markdown文檔發布到將markdown渲染為html。由于整個請求正文同時在內存中,因此避免使用此API發布大型(大于1 MiB)文檔。

public class TestRequestBody {public static void main(String[] args) {MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");OkHttpClient client = new OkHttpClient();String postBody = ""+ "Releases\n"+ "--------\n"+ "\n"+ " * _1.0_ May 6, 2013\n"+ " * _1.1_ June 15, 2013\n"+ " * _1.2_ August 11, 2013\n";Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(RequestBody.create(mediaType, postBody)).build();try {Response response = client.newCall(request).execute();if (!response.isSuccessful())throw new IOException("Unexpected code " + response);System.out.println(response.body().string());} catch (Exception e) {throw new RuntimeException(e);}}}

流式傳輸

我們在這里將請求體以流的形式進行POST提交。該請求體的內容在寫入過程中動態生成。此示例直接將數據流式寫入Okio的緩沖池(Buffered Sink)。您的程序可能更傾向于使用OutputStream,您可以通過BufferedSink.outputStream()方法獲取它。

  • 流式傳輸(Streaming):區別于一次性加載完整數據到內存,流式傳輸允許邊生成數據邊發送,尤其適合:大文件傳輸(如視頻上傳)、實時生成數據(如傳感器數據流)、內存敏感場景(避免OOM)
public class Test05Stream {public static void main(String[] args) {MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");OkHttpClient client = new OkHttpClient();RequestBody requestBody = new RequestBody() {@Override public MediaType contentType() {return MEDIA_TYPE_MARKDOWN;}@Override public void writeTo(BufferedSink sink) throws IOException {sink.writeUtf8("Numbers\n");sink.writeUtf8("-------\n");for (int i = 2; i <= 997; i++) {sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));}}private String factor(int n) {for (int i = 2; i < n; i++) {int x = n / i;if (x * i == n) return factor(x) + " × " + i;}return Integer.toString(n);}};Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(requestBody).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());} catch (IOException e) {throw new RuntimeException(e);}}}
流式傳輸擴展示例

通過觀察客戶端和服務端的日志,可以看到客戶端發的同時,服務端也在收。

Test05StreamClient
@Slf4j
public class Test05StreamClient {public static void main(String[] args) {OkHttpClient client = new OkHttpClient.Builder().writeTimeout(30, TimeUnit.SECONDS) // 延長寫入超時.build();// 創建流式 RequestBodyRequestBody requestBody = new RequestBody() {@Overridepublic MediaType contentType() {return MediaType.parse("application/octet-stream");}@Overridepublic void writeTo(BufferedSink sink) throws IOException {try (java.io.OutputStream os = sink.outputStream()) {for (int i = 0; i < 50; i++) {// 模擬生成數據塊String chunk = "Chunk-" + i + "\n";log.info("發送數據塊: {}", chunk);os.write(chunk.getBytes());os.flush(); // 立即刷新緩沖區Thread.sleep(100); // 模擬延遲}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}};Request request = new Request.Builder().url("http://localhost:8080/stream-upload-raw").post(requestBody).header("Content-Type", "application/octet-stream") // 強制覆蓋.build();// 異步執行client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {log.info("[請求失敗]");}@Overridepublic void onResponse(Call call, Response response) throws IOException {log.info("[請求成功]");System.out.println("上傳成功: " + response.code());System.out.println("響應內容: " + response.body().string());response.close();}});}
}
StreamController
@Slf4j
@RestController
public class StreamController {// 通過 HttpServletRequest 獲取原始流@PostMapping("/stream-upload-raw")public String handleRawStream(HttpServletRequest request) {try (InputStream rawStream = request.getInputStream()) {byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = rawStream.read(buffer)) != -1) { // 按字節讀取String chunk = new String(buffer, 0, bytesRead);log.info("[字節流] {}, {}", chunk.trim(), bytesRead);}return "Raw stream processed";} catch (IOException e) {return "Error: " + e.getMessage();}}
}

文件傳輸

將文件作為請求體。

public class Test06File {public static void main(String[] args) {OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url("http://localhost:8080/okFile").post(RequestBody.create(null, new File("C:\\Users\\zzhua195\\Desktop\\test.png"))).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());} catch (IOException e) {throw new RuntimeException(e);}}}

后端代碼

@RequestMapping("okFile")
public Object okFile(HttpServletRequest request) throws Exception {ServletInputStream inputStream = request.getInputStream();org.springframework.util.FileCopyUtils.copy(request.getInputStream(), new FileOutputStream("D:\\Projects\\practice\\demo-boot\\src\\main\\resources\\test.png"));return "ok";
}

表單提交

使用FormBody.Builder構建一個像超文本標記語言<form>標簽一樣工作的請求正文。名稱和值將使用超文本標記語言兼容的表單URL編碼進行編碼。

public class Test07Form {public static void main(String[] args) {OkHttpClient client = new OkHttpClient();RequestBody formBody = new FormBody.Builder().add("username", "Jurassic Park").build();Request request = new Request.Builder().url("http://localhost:8080/okForm").post(formBody).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful())throw new IOException("Unexpected code " + response);System.out.println(response.body().string());} catch (IOException e) {throw new RuntimeException(e);}}}

對應的后端代碼

@RequestMapping("okForm")
public Object okForm(LoginForm loginForm) throws Exception {log.info("[okForm] {}", loginForm);return "ok";}

文件上傳

MultipartBody.Builder可以構建與超文本標記語言文件上傳表單兼容的復雜請求正文。多部分請求正文的每個部分本身就是一個請求正文,并且可以定義自己的標頭。如果存在,這些標頭應該描述部分正文,例如它的Content-Disposition。如果可用,Content-Length和Content-Type標頭會自動添加

public class Test08MultipartFile {public static void main(String[] args) {OkHttpClient client = new OkHttpClient();RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("comment", "Square Logo").addFormDataPart("bin", "logo-square.png", RequestBody.create(null, new File("C:\\Users\\zzhua195\\Desktop\\test.png"))).build();Request request = new Request.Builder().header("Authorization", "Client-ID").url("http://127.0.0.1:8080/multipart02").post(requestBody).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());} catch (IOException e) {throw new RuntimeException(e);}}}

對應的后端代碼

@RequestMapping("multipart01")public Object multipart(@RequestPart("bin") MultipartFile file,@RequestPart("comment") String comment) throws InterruptedException, IOException {System.out.println(file.getBytes().length);System.out.println(comment);return "ojbk";}@RequestMapping("multipart02")public Object multipart(MultipartDTO multipartDTO) throws InterruptedException, IOException {System.out.println(multipartDTO.getBin().getBytes().length);System.out.println(multipartDTO.getComment());return "ojbk";}

響應緩存

1、要緩存響應,您需要一個可以讀取和寫入的緩存目錄,以及對緩存大小的限制。緩存目錄應該是私有的,不受信任的應用程序應該無法讀取其內容!
2、讓多個緩存同時訪問同一個緩存目錄是錯誤的。大多數應用程序應該只調用一次new OkHttpClient(),用它們的緩存配置它,并在任何地方使用相同的實例。否則兩個緩存實例會互相踩踏,破壞響應緩存,并可能使您的程序崩潰。

3、響應緩存對所有配置都使用HTTP標頭。您可以添加請求標頭,如Cache-Control: max-stale=3600,OkHttp的緩存將尊重它們。您的網絡服務器使用自己的響應標頭配置緩存響應的時間,如Cache-Control: max-age=9600。有緩存標頭可以強制緩存響應、強制網絡響應或強制使用條件GET驗證網絡響應。
4、要防止響應使用緩存,請使用CacheControl.FORCE_NETWORK。要防止它使用網絡,請使用CacheControl.FORCE_CACHE。請注意:如果您使用FORCE_CACHE并且響應需要網絡,OkHttp將返回504 Unsatisfiable Request響應。

import okhttp3.Cache;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;import java.io.File;
import java.io.IOException;public class Test09CacheResponse {public static void main(String[] args) {try {Test09CacheResponse test = new Test09CacheResponse(new File("cache"));test.run();} catch (Exception e) {throw new RuntimeException(e);}}private final OkHttpClient client;public Test09CacheResponse(File cacheDirectory) throws Exception {int cacheSize = 10 * 1024 * 1024; // 10 MiBCache cache = new Cache(cacheDirectory, cacheSize);client = new OkHttpClient.Builder().cache(cache).build();}public void run() throws Exception {Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();String response1Body;try (Response response1 = client.newCall(request).execute()) {if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);response1Body = response1.body().string();System.out.println("Response 1 response:          " + response1);System.out.println("Response 1 cache response:    " + response1.cacheResponse());System.out.println("Response 1 network response:  " + response1.networkResponse());}String response2Body;try (Response response2 = client.newCall(request).execute()) {if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);response2Body = response2.body().string();System.out.println("Response 2 response:          " + response2);System.out.println("Response 2 cache response:    " + response2.cacheResponse());System.out.println("Response 2 network response:  " + response2.networkResponse());}System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));}}
/*
Response 1 response:          Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 1 cache response:    null
Response 1 network response:  Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 response:          Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 cache response:    Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 network response:  null
Response 2 equals Response 1? true
*/

取消調用

使用Call.cancel()立即停止正在進行的調用。如果線程當前正在寫入請求或讀取響應,它將收到IOException。當不再需要調用時,使用它來節省網絡;例如,當您的用戶導航離開應用程序時。同步和異步調用都可以取消。

public class Test09CancelCall {public static void main(String[] args) throws Exception {new Test09CancelCall().run();}private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay..build();final long startNanos = System.nanoTime();final Call call = client.newCall(request);// Schedule a job to cancel the call in 1 second.executor.schedule(new Runnable() {@Override public void run() {System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);call.cancel();System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);}}, 1, TimeUnit.SECONDS);System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);try (Response response = call.execute()) {System.out.printf("%.2f Call was expected to fail, but completed: %s%n",  (System.nanoTime() - startNanos) / 1e9f, response);} catch (IOException e) {System.out.printf("%.2f Call failed as expected: %s%n", (System.nanoTime() - startNanos) / 1e9f, e);}}}

超時

private final OkHttpClient client;public ConfigureTimeouts() throws Exception {client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
}public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay..build();try (Response response = client.newCall(request).execute()) {System.out.println("Response completed: " + response);}
}

每次調用配置

所有HTTP客戶端配置都存在OkHttpClient中,包括代理設置、超時和緩存。當您需要更改單個調用的配置時,調用OkHttpClient.newBuilder()。這將返回一個與原始客戶端共享相同連接池、調度程序和配置的構建器。在下面的示例中,我們發出一個超時500毫秒的請求,另一個超時3000毫秒。

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay..build();// 拷貝client的屬性,并作出自定義修改OkHttpClient client1 = client.newBuilder().readTimeout(500, TimeUnit.MILLISECONDS).build();try (Response response = client1.newCall(request).execute()) {System.out.println("Response 1 succeeded: " + response);} catch (IOException e) {System.out.println("Response 1 failed: " + e);}// Copy to customize OkHttp for this request.OkHttpClient client2 = client.newBuilder().readTimeout(3000, TimeUnit.MILLISECONDS).build();try (Response response = client2.newCall(request).execute()) {System.out.println("Response 2 succeeded: " + response);} catch (IOException e) {System.out.println("Response 2 failed: " + e);}
}

處理鑒權

OkHttp可以自動重試未經身份驗證的請求。當響應為401 Not Authorized時,會要求Authenticator提供憑據。實現應該構建一個包含缺失憑據的新請求。如果沒有可用的憑據,則返回null以跳過重試。

private final OkHttpClient client;public Authenticate() {client = new OkHttpClient.Builder().authenticator(new Authenticator() {@Override public Request authenticate(Route route, Response response) throws IOException {if (response.request().header("Authorization") != null) {return null; // Give up, we've already attempted to authenticate.}System.out.println("Authenticating for response: " + response);System.out.println("Challenges: " + response.challenges());String credential = Credentials.basic("jesse", "password1");return response.request().newBuilder().header("Authorization", credential).build();}}).build();
}public void run() throws Exception {Request request = new Request.Builder().url("http://publicobject.com/secrets/hellosecret.txt").build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}
}

為避免在鑒權不起作用時進行多次重試,您可以返回null以放棄。例如,當已經嘗試了這些確切的憑據時,您可能希望跳過重試:

if (credential.equals(response.request().header("Authorization"))) {return null; // If we already failed with these credentials, don't retry.
}

當您達到應用程序定義的嘗試限制時,您也可以跳過重試:

if (responseCount(response) >= 3) {return null; // If we've failed 3 times, give up.
}

攔截器

參考:https://square.github.io/okhttp/features/interceptors/
在這里插入圖片描述

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;import java.io.IOException;@Slf4j
public class TestInterceptor {public static void main(String[] args) throws IOException {OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor())// .addNetworkInterceptor(new LoggingInterceptor()).build();Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User-Agent", "OkHttp Example").build();Response response = client.newCall(request).execute();response.body().close();}static class LoggingInterceptor implements Interceptor {@Override public Response intercept(Interceptor.Chain chain) throws IOException {Request request = chain.request();long t1 = System.nanoTime();log.info(String.format("Sending request %s on %s%n%s",request.url(), chain.connection(), request.headers()));Response response = chain.proceed(request);long t2 = System.nanoTime();log.info(String.format("Received response for %s in %.1fms%n%s",response.request().url(), (t2 - t1) / 1e6d, response.headers()));return response;}}}

事件監聽

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74603.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74603.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74603.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【git項目管理】長話短說

目錄 主要分為三種使用情況 安裝git后第一次使用創建新倉庫并管理克隆倉庫并管理 初次使用git 首先確定電腦的用戶名是純英文&#xff0c;沒有中文和奇怪的符號&#xff0c;如果不滿足這個條件&#xff0c;參考這個 鏈接 修改用戶名 git config --global user.name "…

算法刷題記錄——LeetCode篇(3.2) [第211~212題](持續更新)

更新時間&#xff1a;2025-04-04 算法題解目錄匯總&#xff1a;算法刷題記錄——題解目錄匯總技術博客總目錄&#xff1a;計算機技術系列博客——目錄頁 優先整理熱門100及面試150&#xff0c;不定期持續更新&#xff0c;歡迎關注&#xff01; 215. 數組中的第K個最大元素 給…

【linux學習】linux系統調用編程

目錄 一、任務、進程和線程 1.1任務 1.2進程 1.3線程 1.4線程和進程的關系 1.5 在linux系統下進程操作 二、Linux虛擬內存管理與stm32的真實物理內存區別 2.1 Linux虛擬內存管理 2.2 STM32的真實物理內存映射 2.3區別 三、 Linux系統調用函數 fork()、wait()、exec(…

react redux的學習,多個reducer

redux系列文章目錄 第一章 簡單學習redux,單個reducer 前言 前面我們學習到的是單reducer的使用&#xff1b;要知道redux是個很強大的狀態存儲庫&#xff0c;可以支持多個reducer的使用。 combineReducers ?combineReducers?是Redux中的一個輔助函數&#xff0c;主要用于…

Oracle數據庫數據編程SQL<3.5 PL/SQL 存儲過程(Procedure)>

存儲過程(Stored Procedure)是 Oracle 數據庫中一組預編譯的 PL/SQL 語句集合,存儲在數據庫中并可通過名稱調用執行。它們是企業級數據庫應用開發的核心組件。 目錄 一、存儲過程基礎 1. 存儲過程特點 2. 創建基本語法 3. 存儲過程優點 4. 簡單示例 二、沒有參數的存儲…

手撕AVL樹

引入&#xff1a;為何要有AVL樹&#xff0c;二次搜索樹有什么不足&#xff1f; 二叉搜索樹有其自身的缺陷&#xff0c;假如往樹中插入的元素有序或者接近有序&#xff0c;二叉搜索樹就會退化成單支樹&#xff0c;時間復雜度會退化成O(N)&#xff0c;因此產生了AVL樹&#xff0c…

《 C語言中的變長數組:靈活而強大的特性》

&#x1f680;個人主頁&#xff1a;BabyZZの秘密日記 &#x1f4d6;收入專欄&#xff1a;C語言 &#x1f30d;文章目入 一、變長數組的定義二、變長數組的優勢三、變長數組的使用示例示例1&#xff1a;動態輸入數組大小示例2&#xff1a;變長數組在函數中的應用 四、變長數組的…

【微服務】基礎概念

1.什么是微服務 微服務其實就是一種架構風格&#xff0c;他提倡我們在開發的時候&#xff0c;一個應用應該是一組小型服務而組成的&#xff0c;每一個服務都運行在自己的進程中&#xff0c;每一個小服務都通過HTTP的方式進行互通。他更加強調服務的徹底拆分。他并不是僅局限于…

Linux make與makefile 項目自動化構建工具

本文章將對make與makefile進行一些基礎的講解。 假設我們要建造一座房子&#xff0c;建造過程涉及很多步驟&#xff0c;比如打地基、砌墻、安裝門窗、粉刷墻壁等。每個步驟都有先后順序&#xff0c;并且有些步驟可能依賴于其他步驟的完成。比如&#xff0c;你必須先打好地基才…

如何判斷多個點組成的3維面不是平的,如果不是平的,如何拆分成多個平面

判斷和拆分三維非平面為多個平面 要判斷多個三維點組成的面是否為平面&#xff0c;以及如何將非平面拆分為多個平面&#xff0c;可以按照以下步驟進行&#xff1a; 判斷是否為平面 平面方程法&#xff1a; 選擇三個不共線的點計算平面方程&#xff1a;Ax By Cz D 0檢查其…

多layout 布局適配

安卓多布局文件適配方案操作流程 以下為通過多套布局文件適配不同屏幕尺寸/密度的詳細步驟&#xff0c;結合主流適配策略及最佳實踐總結&#xff1a; 一、?創建多套布局資源目錄? ?按屏幕尺寸劃分? 在 res 目錄下創建以下文件夾&#xff08;根據設備特性自動匹配&#xff…

Java 大視界 -- Java 大數據在智能農業無人機植保作業路徑規劃與藥效評估中的應用(165)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

美關稅加征下,Odoo免費開源ERP如何助企業破局?

近期&#xff0c;美國特朗普政府推行的關稅政策對全球供應鏈和進出口企業造成巨大沖擊&#xff0c;尤其是依賴中美貿易的企業面臨成本激增、利潤壓縮和合規風險。在此背景下&#xff0c;如何通過數字化轉型優化管理效率、降低運營成本成為企業生存的關鍵。本文以免費開源ERP系統…

go游戲后端開發25:紅中麻將規則介紹

一、游戲基礎規則介紹 在開發紅中麻將游戲之前&#xff0c;我們需要先了解其基礎規則。紅中麻將的牌面由 a、b、c、d 四種花色組成&#xff0c;其中 a、b、c 分別代表萬、條、筒&#xff0c;每種花色都有 1 - 9 的九種牌&#xff0c;每種牌各有四張&#xff0c;總計 36 張 4 …

Unity:平滑輸入(Input.GetAxis)

目錄 1.為什么需要Input.GetAxis&#xff1f; 2. Input.GetAxis的基本功能 3. Input.GetAxis的工作原理 4. 常用參數和設置 5. 代碼示例&#xff1a;用GetAxis控制角色移動 6. 與Input.GetAxisRaw的區別 7.如何優化GetAxis&#xff1f; 1.為什么需要Input.GetAxis&…

OpenCV:計算機視覺的強大開源庫

文章目錄 引言一、什么是OpenCV&#xff1f;1.OpenCV的核心特點 二、OpenCV的主要功能模塊1. 核心功能&#xff08;Core Functionality&#xff09;2. 圖像處理&#xff08;Image Processing&#xff09;3. 特征檢測與描述&#xff08;Features2D&#xff09;4. 目標檢測&#…

AI浪潮下的IT職業轉型:醫藥流通行業傳統IT顧問的深度思考

AI浪潮下的IT職業轉型&#xff1a;醫藥流通行業傳統IT顧問的深度思考 一、AI重構IT行業的技術邏輯與實踐路徑 1.1 醫藥流通領域的智能辦公革命 在醫藥批發企業的日常運營中&#xff0c;傳統IT工具正經歷顛覆性變革。以訂單處理系統為例&#xff0c;某醫藥集團引入AI智能客服…

Qt進階開發:QFileSystemModel的使用

文章目錄 一、QFileSystemModel的基本介紹二、QFileSystemModel的基本使用2.1 在 QTreeView 中使用2.2 在 QListView 中使用2.3 在 QTableView 中使用 三、QFileSystemModel的常用API3.1 設置根目錄3.2 過濾文件3.2.1 僅顯示文件3.2.2 只顯示特定后綴的文件3.2.3 只顯示目錄 四…

KAPC的前世今生--(下)下RPCRT4!NMP_SyncSendRecv函數分析

第一部分&#xff1a;nt!KiDeliverApc函數調用nt!IopCompleteRequest函數后準備返回 1: kd> kv # ChildEBP RetAddr Args to Child 00 ba3eec18 80a3c83b 896e4e40 ba3eec64 ba3eec58 nt!IopCompleteRequest0x3a3 (FPO: [Non-Fpo]) (CONV: stdcall) [d:\srv…

深入理解C++引用:從基礎到現代編程實踐

一、引用的本質與基本特性 1.1 引用定義 引用是為現有變量創建的別名&#xff0c;通過&符號聲明。其核心特點&#xff1a; 必須初始化且不能重新綁定 與被引用變量共享內存地址 無獨立存儲空間&#xff08;編譯器實現&#xff09; 類型必須嚴格匹配 int value 42; in…