主頁 > 後端開發 > 用var宣告變數,在java中居然有這么多細節,你都了解么?

用var宣告變數,在java中居然有這么多細節,你都了解么?

2020-09-22 21:31:19 後端開發

簡介

Java SE 10引入了區域變數的型別推斷,早先,所有的區域變數宣告都要在左側宣告明確型別, 使用型別推斷,一些顯式型別可以替換為具有初始化值的區域變數保留型別var,這種作為區域變數型別 的var型別,是從初始化值的型別中推斷出來的,

關于此功能存在一定的爭議,有些人對它的簡潔性表示歡迎,其他人則擔心它剝奪了閱讀者看重的型別資訊 ,從而損害了代碼的可讀性,這兩邊觀點都是對的,它可以通過消除冗余資訊使代碼更具有可讀性,也可以 通過洗掉有用的資訊來降低代碼的可讀性,另外一個觀點是擔心它會被濫用,從而導致撰寫更糟糕的Java代碼, 這也是事實,但它也可能會導致撰寫更好的代碼,像所有功能一樣,使用它必須要判斷,何時該使用, 何時不該使用,沒有一系列規則,

區域變數宣告不是孤立存在的;周邊的代碼可以影響甚至壓倒使用var的影響,本檔案的目的是檢查周邊代碼 對var宣告的影響,解釋一些權衡,并提供有效使用var的指南,

使用原則

P1. 閱讀代碼比撰寫代碼更重要

代碼的讀取頻率遠高于撰寫代碼,此外,在撰寫代碼時,我們通常要有整體思路,并且要花費時間; 在閱讀代碼的時候,我們經常是背景關系瀏覽,而且可能更加匆忙,是否以及如何使用特定語言功能應該取決 于它對未來讀者的影響,而不是它的作者,較短的程式可能比較長的程式更可取,但是過多地簡寫程式會 省略對于理解程式有用的資訊,這里的核心問題是為程式找到合適的大小,以便最大化程式的可讀性,

我們在這里特別關注在輸入或撰寫程式時所需要的碼字量,雖然簡潔性可能會是對作者的一個很好的鼓勵,但 專注于它會忽略主要的目標,即提高程式的可讀性,

P2. 在經過區域變數型別推斷后,代碼應該變得清晰

讀者應該能夠查看var宣告,并且使用區域變數宣告的時候可以立刻了解代碼里正在發生了什么事情,理想 情況下,只通過代碼片段或背景關系就可以輕松理解代碼,如果讀懂一個var宣告需要讀者去查看代碼周邊的 若干位置代碼,此時使用var可能不是一個好的情況,而且,它還表明代碼本身可能存在問題,

P3. 代碼的可讀性不應該依賴于IDE

代碼通常在IDE中撰寫和讀取,所以很容易依賴IDE的代碼分析功能,對于型別宣告,在任何地方使用var,都可以通過IDE指向一個 變數來確定它的型別,但是為什么不這么做呢?

這里有兩個原因,代碼經常在IDE外部讀取,代碼出現在IDE設施不可用的許多地方,例如檔案中的片段,瀏覽互聯網上的倉庫或補丁檔案 ,為了理解這些代碼的作用,你必須將代碼匯入IDE,這樣做是適得其反的,

第二個原因是即使是在IDE中讀取代碼時也是如此,通常需要明確的操作來查詢IDE以獲取有關變數的更多資訊,例如,查詢使用var宣告 的變數型別,可能必須將滑鼠懸停在變數上并等待彈出資訊,這可能只需要片刻時間,但是它會擾亂閱讀流程,

代碼應該是自動呈現的,它的表面應該是可以理解的,并且無需工具的幫助,

P4. 顯式型別是一種權衡

Java歷來要求區域變數宣告里要明確包含顯式型別,顯然顯式型別可能非常有用,但它們有時候不是很重要,有時候還可以忽略,要求一個 明確的型別可能還會混亂一些有用的資訊,

省略顯式型別可以減少這種混亂,但只有在這種混亂不會損害其可理解性的情況下,這種型別不是向讀者傳達資訊的唯一方式,其他方法 包括變數的名稱和初始化運算式也可以傳達資訊,在確定是否可以將其中一個頻道靜音時,我們應該考慮所有可用的頻道,

指南
G1. 選擇能夠提供有用資訊的變數名稱
通常這是一個好習慣,不過在var的背景下它更為重要,在一個var的宣告中,可以使用變數的名稱來傳達有關變數含義和用法的資訊, 使用var替換顯式型別的同時也要改進變數的名稱,例如:

//原始寫法
List<Customer> x = dbconn.executeQuery(query);

//改進寫法
var custList = dbconn.executeQuery(query);

在這種情況下,無用的變數名x已被替換為一個能夠喚起變數型別的名稱custList,該名稱現在隱含在var的宣告中, 根據方法的邏輯結果對變數的型別進行編碼,得出了”匈牙利表示法”形式的變數名custList, 就像顯式型別一樣,這樣有時是有幫助的,有時候只是雜亂無章,在此示例中,名稱custList表示正在回傳List,這也可能不重要, 和顯式型別不同,變數的名稱有時可以更好地表達變數的角色或性質,比如”customers”:

//原始寫法    
try (Stream<Customer> result = dbconn.executeQuery(query)) {
     return result.map(...)
                  .filter(...)
                  .findAny();
 }
//改進寫法
try (var customers = dbconn.executeQuery(query)) {
    return customers.map(...)
                    .filter(...)
                    .findAny();
}

G2.最小化區域變數的使用范圍
最小化區域變數的范圍通常也是一個好的習慣,這種做法在 Effective Java (第三版),第57項 中有所描述, 如果你要使用var,它會是一個額外的助力,

在下面的例子中,add方法正確地將特殊項添加到list集合的最后一個元素,所以它按照預期最后處理,

var items = new ArrayList<Item>(...);
items.add(MUST_BE_PROCESSED_LAST);
for (var item : items) ...

現在假設為了洗掉重復的專案,程式員要修改此代碼以使用HashSet而不是ArrayList:

var items = new HashSet<Item>(...);
items.add(MUST_BE_PROCESSED_LAST);
for (var item : items) ...

這段代碼現在有個bug,因為Set沒有定義迭代順序,不過,程式員可能會立即修復這個bug,因為items變數的使用與其宣告相鄰,

現在假設這段代碼是一個大方法的一部分,相應地items變數具有很大的使用范圍:

var items = new HashSet<Item>(...);

// ... 100 lines of code ...

items.add(MUST_BE_PROCESSED_LAST);
for (var item : items) ...

將ArrayList更改為HashSet的影響不再明顯,因為使用items的代碼與宣告items的代碼離得很遠,這意味著上面所說的bug似乎可以存活 更長時間,

如果items已經明確宣告為List,還需要更改初始化程式將型別更改為Set,這可能會提示程式員檢查方法的其余部分 是否存在受此類更改影響的代碼,(問題來了,他也可能不會檢查),如果使用var會消除這類影響,不過也可能會增加在此類代碼中 引入錯誤的風險,

這似乎是反對使用var的論據,但實際上并非如此,使用var的初始化程式非常精簡,僅當變數的使用范圍很大時才會出現此問題, 你應該更改代碼來減少區域變數的使用范圍,然后用var宣告它們,而不是簡單地避免在這些情況下使用var,

G3. 當初始化程式為讀者提供足夠的資訊時,請考慮使用var
區域變數通常在建構式中進行初始化,正在創建的建構式名稱通常與左側顯式宣告的型別重復, 如果型別名稱很長,就可以使用var提升簡潔性而不會丟失資訊:

// 原始寫法:
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 改進寫法
var outputStream = new ByteArrayOutputStream();

在初始化程式是方法呼叫的情況下,使用var也是合理的,例如靜態工廠方法,并且其名稱包含足夠的型別資訊:

//原始寫法
BufferedReader reader = Files.newBufferedReader(...);
List<String> stringList = List.of("a", "b", "c");
// 改進寫法
var reader = Files.newBufferedReader(...);
var stringList = List.of("a", "b", "c"); 

在這些情況下,方法的名稱強烈暗示其特定的回傳型別,然后用于推斷變數的型別,

G4. 使用var區域變數分解鏈式或嵌套運算式
考慮使用字串集合并查找最常出現的字串的代碼,可能如下所示:

return strings.stream()
               .collect(groupingBy(s -> s, counting()))
               .entrySet()
               .stream()
               .max(Map.Entry.comparingByValue())
               .map(Map.Entry::getKey);

這段代碼是正確的,但它可能令人困惑,因為它看起來像是一個單一的流管道,事實上,它先是一個短暫的流,接著是第一個流的結果生成 的第二個流,然后是第二個流的可選結果映射后的流,表達此代碼的最易讀的方式是兩個或三個陳述句; 第一組物體放入一個Map,然后第二組過濾這個Map,第三組從Map結果中提取出Key,如下所示:

Map<String, Long> freqMap = strings.stream()
                                   .collect(groupingBy(s -> s, counting()));
Optional<Map.Entry<String, Long>> maxEntryOpt = freqMap.entrySet()
                                                       .stream()
                                                       .max(Map.Entry.comparingByValue());
return maxEntryOpt.map(Map.Entry::getKey);

但撰寫者可能會拒絕這樣做,因為撰寫中間變數的型別似乎太過于繁瑣,相反他們篡改了控制流程,使用var允許我們更自然地表達代碼 ,而無需付出顯式宣告中間變數型別的高代價:

var freqMap = strings.stream()
                     .collect(groupingBy(s -> s, counting()));
var maxEntryOpt = freqMap.entrySet()
                         .stream()
                         .max(Map.Entry.comparingByValue());
return maxEntryOpt.map(Map.Entry::getKey);

有些人可能更傾向于第一段代碼中單個長的鏈式呼叫,但是,在某些條件下,最好分解長的方法鏈,對這些情況使用var是一種可行的 方法,而在第二個段中使用中間變數的完整宣告會是一個不好的選擇, 和許多其他情況一樣,正確使用var可能會涉及到扔掉一些東西 (顯示型別)和加入一些東西(更好的變數名稱,更好的代碼結構),

G5. 不用過分擔心”使用介面編程” 中區域變數的使用問題

Java編程中常見的習慣用法是構造具體型別的實體,但要將其分配給介面型別的變數,這將代碼系結到抽象上而不是具體實作上,為 代碼以后的維護保留了靈活性,

//原始寫法, list型別為介面List型別
List<String> list = new ArrayList<>()

如果使用var,可以推斷出list具體的實作型別ArrayList而不是介面型別List

// 推斷出list的型別是 ArrayList<String>.
var list = new ArrayList<String>(); 

這里要再次重申一次,var只能用于區域變數,它不能用于推斷屬性型別,方法引數型別和方法回傳型別,”使用介面編程”的原則在這些 情況下仍然和以往一樣重要,

主要問題是使用該變數的代碼可以形成對具體實作的依賴性,如果變數的初始化程式以后要改變,這可能導致其推斷型別發生變化,在 使用該變數的后續代碼中產生例外或bug,

如果,如指南G2中所建議的那樣,區域變數的范圍很小,可能影響后續代碼的具體實作的”漏洞”是有限的,如果變數僅用于幾行之外的 代碼,應該很容易避免這些問題或者緩解這些出現的問題,

在這種特殊情況下,ArrayList只包含一些不在List上的方法,如ensureCapacity()和trimToSize(),這些方法不會影響到List,所以 呼叫他們不會影響程式的正確性,這進一步降低了推斷型別作為具體實作型別而不是介面型別的影響,

G6. 使用帶有<>和泛型方法的var時候要小心

var和<>功能允許您在可以從已存在的資訊派生時,省略具體的型別資訊,你能在同一個變數宣告中使用它們嗎?

考慮一下以下代碼:

PriorityQueue<Item> itemQueue = new PriorityQueue<Item>();

這段代碼可以使用var或<>重寫,并且不會丟失型別資訊:

// 正確:兩個變數都可以宣告為PriorityQueue<Item>型別
PriorityQueue<Item> itemQueue = new PriorityQueue<>();
var itemQueue = new PriorityQueue<Item>();   

同時使用var和<>是合法的,但推斷型別將會改變:

// 危險: 推斷型別變成了 PriorityQueue<Object>
var itemQueue = new PriorityQueue<>();

從上面的推斷結果來看,<>可以使用目標型別(通常在宣告的左側)或建構式作為<>里的引數型別,如果兩者都不存在,它會追溯到 最寬泛的適用型別,通常是Object,這通常不是我們預期的結果,

泛型方法早已經提供了型別推斷,使用泛型方法很少需要提供顯式型別引數,如果是沒有提供足夠型別資訊的實際方法引數,泛型方法 的推斷就會依賴于目標型別,在var宣告中沒有目標型別,所以也會出現類似的問題,例如:

// 危險: list推斷為 List<Object>
var list = List.of();

使用<>和泛型方法,可以通過建構式或方法的實際引數提供其他型別資訊,允許推斷出預期的型別,從而有:

// 正確: itemQueue 推斷為 PriorityQueue<String>
Comparator<String> comp = ... ;
var itemQueue = new PriorityQueue<>(comp);

// 正確: infers 推斷為 List<BigInteger>
var list = List.of(BigInteger.ZERO);

如果你想要將var與<>或泛型方法一起使用,你應該確保方法或建構式引數能夠提供足夠的型別資訊,以便推斷的型別與你想要的型別匹配, 否則,請避免在同一宣告中同時使用var和<>或泛型方法,

G7.使用基本型別的var要小心

基本型別可以使用var宣告進行初始化,在這些情況下使用var不太可能提供很多優勢,因為型別名稱通常很短,不過,var有時候也很有用 ,例如,可以使變數的名稱對齊,

boolean,character,long,string的基本型別使用var沒有問題,這些型別的推斷是精確的,因為var的含義是明確的

// 原始做法
boolean ready = true;
char ch = '\ufffd';
long sum = 0L;
String label = "wombat";

// 改進做法
var ready = true;
var ch    = '\ufffd';
var sum   = 0L;
var label = "wombat";

當初始值是數字時,應該特別小心,特別是int型別,如果左側有顯示型別,那么右側會通過向上或向下轉型將int數值默默轉為 左邊對應的型別,如果左邊使用var,那么右邊的值會被推斷為int型別,這可能是無意的,

// 原始做法
byte flags = 0;
short mask = 0x7fff;
long base = 17;

// 危險: 所有的變數型別都是int
var flags = 0;
var mask = 0x7fff;
var base = 17;  

如果初始值是浮點型,推斷的型別大多是明確的:

// 原始做法
float f = 1.0f;
double d = 2.0;

// 改進做法
var f = 1.0f;
var d = 2.0;

注意,float型別可以默默向上轉型為double型別,使用顯式的float變數(如3.0f)為double變數做初始化會有點遲鈍,不過, 如果是使用var對double變數用float變數做初始化,要注意:

// 原始做法
static final float INITIAL = 3.0f;
...
double temp = INITIAL;

// 危險: temp被推斷為float型別了
var temp = INITIAL;  

(實際上,這個例子違反了G3準則,因為初始化程式里沒有提供足夠的型別資訊能讓讀者明白其推斷型別,)

示例

本節包含一些示例,這些例子中使用var可以收到良好的效果,

下面這段代碼表示的是根據最多匹配數max從一個Map中移除匹配的物體,通配符(?)型別邊界可以提高方法的靈活性,但是長度會很長 ,不幸的是,這里Iterator的型別還被要求是一個嵌套的通配符型別,這樣使它的宣告更加的冗長,以至于for回圈標題長度在一行里 都放不下,也使代碼更加的難懂,

// 原始做法
void removeMatches(Map<? extends String, ? extends Number> map, int max) {
    for (Iterator<? extends Map.Entry<? extends String, ? extends Number>> iterator =
             map.entrySet().iterator(); iterator.hasNext();) {
        Map.Entry<? extends String, ? extends Number> entry = iterator.next();
        if (max > 0 &amp;&amp; matches(entry)) {
            iterator.remove();
            max--;
        }
    }
}  

在這里使用var就可以洗掉掉區域變數一些干擾的型別宣告,在這種回圈中顯式的型別Iterator,Map.Entry在很大程度上是沒有必要的,
使用var宣告就能夠讓for回圈標題放在同一行,代碼也更易懂,

// 改進做法
void removeMatches(Map<? extends String, ? extends Number> map, int max) {
    for (var iterator = map.entrySet().iterator(); iterator.hasNext();) {
        var entry = iterator.next();
        if (max > 0 &amp;&amp; matches(entry)) {
            iterator.remove();
            max--;
        }
    }
}

考慮使用try-with-resources陳述句從Socket讀取單行文本的代碼,網路和IO類一般都是裝飾類,在使用時,必須將每個中間物件宣告為 資源變數,以便在打開后續的裝飾類的時候能夠正確關閉這個資源,常規撰寫代碼要求在變數左右宣告重復的類名,這樣會導致很多混亂:

// 原始做法
try (InputStream is = socket.getInputStream();
     InputStreamReader isr = new InputStreamReader(is, charsetName);
     BufferedReader buf = new BufferedReader(isr)) {
    return buf.readLine();
}  

使用var宣告會減少很多這種混亂:

// 改進做法
try (var inputStream = socket.getInputStream();
     var reader = new InputStreamReader(inputStream, charsetName);
     var bufReader = new BufferedReader(reader)) {
    return bufReader.readLine();
}

結語

使用var宣告可以通過減少混亂來改善代碼,從而讓更重要的資訊脫穎而出,另一方面,不加選擇地使用var也會讓事情變得更糟,使用 得當,var有助于改善良好的代碼,使其更短更清晰,同時又不影響可理解性,

私信回復 “資料” 領取一線大廠Java面試題總結+阿里巴巴泰山手冊+各知識點學習思維導+一份300頁pdf檔案的Java核心知識點總結!

這些資料的內容都是面試時面試官必問的知識點,篇章包括了很多知識點,其中包括了有基礎知識、Java集合、JVM、多執行緒并發、spring原理、微服務、Netty 與RPC 、Kafka、日記、設計模式、Java演算法、資料庫、Zookeeper、分布式快取、資料結構等等,

file

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/107429.html

標籤:Java

上一篇:Maven核心概述

下一篇:IDEA中配置Maven

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more