文件上傳

文件上傳是 Web 開發常見需求,上傳文件需要用到文件輸入框,如果給文件輸入框添加一個 multiple 屬性則可以一次選擇多個文件(不支持的瀏覽器會自動忽略這個屬性)

<input multiple type="file">

點擊這個輸入框就可以打開瀏覽文件對話框選擇文件了,一般一個輸入框上傳一個文件就行,要上傳多個文件也可以用多個輸入框來處理,這樣做是為了兼容那些不支持 multiple 屬性的瀏覽器,同時用戶一般也不會選擇多個文件

基本上傳方式

當把文件輸入框放入表單中,提交表單的時候即可將選中的文件一起提交上傳到服務器,需要注意的是由於提交的表單中包含文件,因此要修改一下錶單元素的 enctype 屬性為 multipart/form-data

<form action="#" enctype="multipart/form-data" method="post">
  <input name="file" type="file">
  <button type="submit">Upload</button>
</form>

這樣上傳方式是傳統的同步上傳,上傳的文件如果很大,往往需要等待很久,上傳完成后頁面還會重新加載,並且必須等待上傳完成后才能繼續操作

早期的瀏覽器並不支持異步上傳,不過可以使用 iframe 來模擬,在頁面中隱藏一個 <iframe> 元素,指定一個 name 值,同時將 <form> 元素的 target 屬性值指定為 <iframe> 元素的 name 屬性的值,將兩者關聯起來

<form action="#" enctype="multipart/form-data" method="post" target="upload-frame">
  <input name="file" type="file">
  <button type="submit">Upload</button>
</form>
<iframe id="upload-frame" name="upload-frame" src="about:blank" style="display: none;"></iframe>

這樣在提交表單上傳的時候,頁面就不會重新加載了,取而代之的是 iframe 重新加載了,不過 iframe 原本就是隱藏的,即使重新加載也不會感知到

訪問文件

File API 提供了訪問文件的能力,通過輸入框的 files 屬性訪問,這會得到一個 FileList,這是一個集合,如果只選擇了一個文件,那麼集合中的第一個元素就是這個文件

var input = document.querySelector('input[type="file"]')
var file = input.files[0]

console.log(file.name) // 文件名稱
console.log(file.size) // 文件大小
console.log(file.type) // 文件類型

支持 File API 的瀏覽器可以參考

Ajax 上傳

由於可以通過 File API 直接訪問文件內容,再結合 XMLHttpRequest 對象直接將文件上傳,將其作為參數傳給 XMLHttpRequest 對象的 send 方法即可

var xhr = new XMLHttpRequest()
xhr.open('POST', '/upload/url', true)
xhr.send(file)

不過一些原因不建議直接這樣傳遞文件,而是使用 FormData 對象來包裝需要上傳的文件,FormData 是一個構造函數,使用的時候先 new 一個實例,然後通過實例的 append 方法向其中添加數據,直接把需要上傳的文件添加進去

var formData = new FormData()
formData.append('file', file, file.name) // 第 3 個參數是文件名稱
formData.append('username', 'Mary') // 還可以添加額外的參數

甚至也可以直接把表單元素作為實例化參數,這樣整個表單中的數據就全部包含進去了

var formData = new FormData(document.querySelector('form'))

數據準備好后,就是上傳了,同樣是作為參數傳給 XMLHttpRequest 對象的 send 方法

var xhr = new XMLHttpRequest()
xhr.open('POST', '/upload/url', true)
xhr.send(formData)

監測上傳進度

XMLHttpRequest 對象還提供了一個 progress 事件,基於這個事件可以知道上傳進度如何

var xhr = new XMLHttpRequest()
xhr.open('POST', '/upload/url', true)
xhr.upload.onprogress = progressHandler // 這個函數接下來定義

上傳的 progress 事件由 xhr.upload 對象觸發,在事件處理程序中使用這個事件對象的 loaded(已上傳字節數) 和 total(總數) 屬性來計算上傳的進度

function progressHandler(e) {
  var percent = Math.round((e.loaded / e.total) * 100)
}

上面的計算會得到一個表示完成百分比的数字,不過這兩個值也不一定總會有,保險一點先判斷一下事件對象的 lengthComputable 屬性

function progressHandler(e) {
  if (e.lengthComputable) {
    var percent = Math.round((e.loaded / e.total) * 100)
  }
}

支持 Ajax 上傳的瀏覽器可以參考

分割上傳

使用文件對象的 slice 方法可以分割文件,給該方法傳遞兩個參數,一個起始位置和一個結束位置,這會返回一個新的 Blob 對象,包含原文件從起始位置到結束位置的那一部分(文件 File 對象其實也是 Blob 對象,這可以通過 file instanceof Blob 確定,Blob 是 File 的父類)

var blob = file.slice(0, 1024) // 文件從字節位置 0 到字節位置 1024 那 1KB

將文件分割成幾個 Blob 對象分別上傳就能實現將大文件分割上傳

function upload(file) {
  let formData = new FormData()
  formData.append('file', file)
  let xhr = new XMLHttpRequest()
  xhr.open('POST', '/upload/url', true)
  xhr.send(formData)
}

var blob = file.slice(0, 1024)
upload(blob) // 上傳第一部分

var blob2 = file.slice(1024, 2048)
upload(blob2) // 上傳第二部分

// 上傳剩餘部分

通常用一個循環來處理更方便

var pos = 0 // 起始位置
var size = 1024 // 塊的大小

while (pos < file.size) {
  let blob = file.slice(pos, pos + size) // 結束位置 = 起始位置 + 塊大小

  upload(blob)
  pos += size // 下次從結束位置開始繼續分割
}

服務器接收到分塊文件進行重新組裝的代碼就不在這裏展示了

使用這種方式上傳文件會一次性發送多個 HTTP 請求,那麼如何處理這種多個請求同時發送的情況呢?方法有很多,可以用 Promise 來處理,讓每次上傳都返回一個 promise 對象,然後用 Promise.all 方法來合併處理,Promise.all 方法接受一個數組作為參數,因此將每次上傳返回的 promise 對象放在一個數組中

var promises = []

while (pos < file.size) {
  let blob = file.slice(pos, pos + size)

  promises.push(upload(blob)) // upload 應該返回一個 promise
  pos += size
}

同時改造一下 upload 函數使其返回一個 promise

function upload(file) {
  return new Promise((resolve, reject) => {
    let formData = new FormData()
    formData.append('file', file)
    let xhr = new XMLHttpRequest()
    xhr.open('POST', '/upload/url', true)
    xhr.onload = () => resolve(xhr.responseText)
    xhr.onerror = () => reject(xhr.statusText)
    xhr.send(formData)
  })
}

當一切完成后

Promise.all(promises).then((response) => {
  console.log('Upload success!')
}).catch((err) => {
  console.log(err)
})

支持文件分割的瀏覽器可以參考

判斷一下文件對象是否有該方法就能知道瀏覽器是否支持該方法,對於早期的部分版本瀏覽器需要加上對應的瀏覽器廠商前綴

var slice = file.slice || file.webkitSlice || file.mozSlice

if (slice) {
  let blob = slice.call(file, 0, 1024) // call
  upload(blob)
} else {
  upload(file) // 不支持分割就只能直接上傳整個文件了,或者提示文件過大
}

拖拽上傳

通過拖拽 API 可以實現拖拽文件上傳,默認情況下,拖拽一個文件到瀏覽器中,瀏覽器會嘗試打開這個文件,要使用拖拽功能需要阻止這個默認行為

document.addEventListener('dragover', function(e) {
  e.preventDefault()
  e.stopPropagation()
})

任意指定一個元素來作為釋放拖拽的區域,給一個元素綁定 drop 事件

var element = document.querySelector('label')
element.addEventListener('drop', function(e) {
  e.preventDefault()
  e.stopPropagation()

  // ...
})

通過該事件對象的 dataTransfer 屬性獲取文件,然後上傳即可

var file = e.dataTransfer.files[0]
upload(file) // upload 函數前面已經定義

選擇類型

給文件輸入框添加 accept 屬性即可指定選擇文件的類型,比如要選擇 png 格式的圖片,則指定其值為 image/png,如果要允許選擇所有類型的圖片,就是 image/*

<input accept="image/*" type="file">

添加 capture 屬性可以調用設備機能,比如 capture="camera" 可以調用相機拍照,不過這並不是一個標準屬性,不同設備實現方式也不一樣,需要注意

<input accept="image/*" capture="camera" type="file">

經測 iOS 設備添加該屬性后只能拍照而不能從相冊選擇文件了,所以判斷一下

if (iOS) { // iOS 用 navigator.userAgent 判斷
  input.removeAttribute('capture')
}

不支持的瀏覽器會自動忽略這些屬性

自定義樣式

文件輸入框在各個瀏覽器中呈現的樣子都不大相同,而且給 input 定義樣式也不是那麼方便,如果有需要應用自定義樣式,有一個技巧,可以用一個 label 關聯到這個文件輸入框,當點擊這個 label 元素的時候就會觸發文件輸入框的點擊,打開瀏覽文件的對話框,相當於點擊了文件輸入框一樣的效果

<label for="file-input"></label>
<input id="file-input" style="clip: rect(0,0,0,0); position: absolute;" type="file">

這時就可以將原本的文件輸入框隱藏了,然後給 label 元素任意地應用樣式,畢竟要給 label 元素應用樣式比 input 方便得多

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

Spark(一)—— 大數據處理入門

一、Spark介紹

Apache Spark is a fast and general-purpose cluster computing system. It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs. It also supports a rich set of higher-level tools including Spark SQL for SQL and structured data processing, MLlib for machine learning, GraphX for graph processing, and Spark Streaming.

Spark是一個快速且多功能的集群計算系統。它為多種不同語言提供高級API,和支持一般執行圖的優化引擎。它也有豐富的高級工具集,Spark SQL進行結構化數據的處理,MLib處理機器學習,GraphX進行圖處理,以及Spark Streaming流計算。

組成

它的主要組件有:

  • SparkCore
    • 將分佈式數據抽象為彈性分佈式數據集(RDD),實現了應用任務調度、RPC、序列化和壓縮,併為運行在其上的上層組件提供API。
  • SparkSQL
    • Spark Sql 是Spark來操作結構化數據的程序包,可以讓我使用SQL語句的方式來查詢數據,Spark支持 多種數據源,包含Hive表,parquest以及JSON等內容。
  • SparkStreaming
    • 是Spark提供的實時數據進行流式計算的組件。
  • MLlib
    • 提供常用機器學習算法的實現庫。
  • GraphX
    • 提供一個分佈式圖計算框架,能高效進行圖計算。
  • BlinkDB
    • 用於在海量數據上進行交互式SQL的近似查詢引擎。
  • Tachyon
    • 以內存為中心高容錯的的分佈式文件系統。

返回一個包含數據集前n個元素的數組

二、WordCount程序講解

編寫代碼

scala程序編寫

object WordCountDemo {

  def main(args: Array[String]): Unit = {
    //創建Spark配置對象
    val conf = new SparkConf().setMaster("local").setAppName("MyApp")
    //通過conf創建sc
    val sc = new SparkContext(conf)
    //讀取文件
    val rdd1 = sc.textFile("/Users/README.md")
    //計算
    val rdd2 = rdd1.flatMap(line => line.split(" ")).map(word => (word,1)).reduceByKey(_+_)
    //打印
    rdd2.take(10).foreach(println)

  }

}

java程序編寫


public class WordCountJavaDemo {

    public static void main(String[] args) {
        
        SparkConf conf = new SparkConf();
        conf.setAppName("myapp").setMaster("local");
        JavaSparkContext sc = new JavaSparkContext(conf);
        JavaRDD<String> rdd1 = sc.textFile("/Users/README.md");
        JavaRDD<String> rdd2 = rdd1.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String s) throws Exception {
                List<String> list = new ArrayList<>();
                String[] arr = s.split(" ");
                for (String ss : arr) {
                    list.add(ss);
                }
                return list.iterator();
            }
        });

        JavaPairRDD<String, Integer> rdd3 = rdd2.mapToPair(new PairFunction<String, String, Integer>() {

            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<String, Integer>(s, 1);
            }

        });

        JavaPairRDD<String, Integer> rdd4 = rdd3.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });

        List<Tuple2<String, Integer>> list = rdd4.collect();
        for (Tuple2<String, Integer> t : list) {
            System.out.println(t._1() + " " + t._2());
        }
    }

}

三、原理介紹

RDD

  • 由一系列Partition組成
  • RDD之間有一系列依賴關係
  • RDD每個算子實際上是作用在每個Partition上
  • RDD會提供一系列最佳位置
  • 分區器是作用在KV格式的RDD上

RDD會在多個節點上存儲,就和hdfs的分佈式道理是一樣的。hdfs文件被切分為多個block存儲在各個節點上,而RDD是被切分為多個partition。不同的partition可能在不同的節點上。

Spark執行流程

1、Driver
分發task,在分發之前,會調用RDD的方法,獲取partition的位置。
將task的計算結果,拉回到Driver端
Driver是一個JVM進程

2、Worker

寬依賴、窄依賴

圖中stage2的并行度是4,也就是有4個task。

寬依賴

父RDD與子RDD,partition的關係是一對多,就是寬依賴。寬依賴於shuffle對應。

窄依賴

父RDD與子RDD,partition的關係是一對一或多對一,就是窄依賴。

四、Spark常用算子

Transformation算子

特點:懶執行

(1)map

map的輸入變換函數應用於RDD中所有元素

(2)flatMap

flatMap與map區別在於map為“映射”,而flatMap“先映射,后扁平化”,map對每一次(func)都產生一個元素,返回一個對象,而flatMap多一步就是將所有對象合併為一個對象。

(3)flatMapValues

每個元素的Value被輸入函數映射為一系列的值,然後這些值再與原RDD中的Key組成一系列新的KV對。

代碼

x = sc.parallelize([("a", ["x", "y", "z"]), ("b", ["p", "r"])])
def f(x): return x
x.flatMapValues(f).collect()

打印結果

 [('a', 'x'), ('a', 'y'), ('a', 'z'), ('b', 'p'), ('b', 'r')]

filter

過濾操作,滿足filter內function函數為true的RDD內所有元素組成一個新的數據集。

(4)groupByKey

主要作用是將相同的所有的鍵值對分組到一個集合序列當中,其順序是不確定的。

(5)reduceByKey

與groupByKey類似,卻有不同。如(a,1), (a,2), (b,1), (b,2)。groupByKey產生中間結果為( (a,1), (a,2) ), ( (b,1), (b,2) )。而reduceByKey為(a,3), (b,3)。

reduceByKey主要作用是聚合,groupByKey主要作用是分組。

(6)take

Action算子

特點:立即觸發執行

五、SparkSQL

介紹

Spark SQL is a Spark module for structured data processing. Unlike the basic Spark RDD API, the interfaces provided by Spark SQL provide Spark with more information about the structure of both the data and the computation being performed. Internally, Spark SQL uses this extra information to perform extra optimizations. There are several ways to interact with Spark SQL including SQL and the Dataset API. When computing a result the same execution engine is used, independent of which API/language you are using to express the computation. This unification means that developers can easily switch back and forth between different APIs based on which provides the most natural way to express a given transformation.

SparkSQL是Spark的一個用來處理結構化數據的模塊。使用類似SQL的方式訪問Hadoop,實現MR計算。

Datasets的概念

A Dataset is a distributed collection of data. Dataset is a new interface added in Spark 1.6 that provides the benefits of RDDs (strong typing, ability to use powerful lambda functions) with the benefits of Spark SQL’s optimized execution engine. A Dataset can be constructed from JVM objects and then manipulated using functional transformations (map, flatMap, filter, etc.). The Dataset API is available in Scala and Java. Python does not have the support for the Dataset API. But due to Python’s dynamic nature, many of the benefits of the Dataset API are already available (i.e. you can access the field of a row by name naturally row.columnName). The case for R is similar.

Dataset是分佈式數據集合。

DataFrames概念

A DataFrame is a Dataset organized into named columns. It is conceptually equivalent to a table in a relational database or a data frame in R/Python, but with richer optimizations under the hood. DataFrames can be constructed from a wide array of sources such as: structured data files, tables in Hive, external databases, or existing RDDs. The DataFrame API is available in Scala, Java, Python, and R. In Scala and Java, a DataFrame is represented by a Dataset of Rows. In the Scala API, DataFrame is simply a type alias of Dataset[Row]. While, in Java API, users need to use Dataset to represent a DataFrame.

基本使用

(1)創建DataFrames

數據

{"id":"1","name":"zhangsan","age":"12"}
{"id":"2","name":"lisi","age":"12"}
{"id":"3","name":"wangwu","age":"12"}

代碼


object SparkSqlDemo {

  def main(args: Array[String]): Unit = {

    //創建Spark配置對象
    val conf = new SparkConf().setMaster("local[4]").setAppName("MyApp");

    val spark = SparkSession
      .builder()
      .appName("Spark SQL basic example")
      .config(conf)
      .getOrCreate()

    val df = spark.read.json("/Users/opensource/dev-problem/source/people_sample_json.json");
    df.show()

  }

}

(2)查詢

val df = spark.read.json("/Users/fangzhijie/opensource/dev-problem/source/people_sample_json.json");
df.createOrReplaceTempView("people")
val sqlDF = spark.sql("SELECT * FROM people WHERE name = 'zhangsan'")
sqlDF.show()

六、SparkStreaming

介紹

Spark Streaming is an extension of the core Spark API that enables scalable, high-throughput, fault-tolerant stream processing of live data streams. Data can be ingested from many sources like Kafka, Flume, Kinesis, or TCP sockets, and can be processed using complex algorithms expressed with high-level functions like map, reduce, join and window. Finally, processed data can be pushed out to filesystems, databases, and live dashboards. In fact, you can apply Spark’s machine learning and graph processing algorithms on data streams.

基本使用

(1)簡單使用


object SparkStreamingDemo {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
    //創建Spark流上下文
    val ssc = new StreamingContext(conf, Seconds(1))
    //創建Socket文本流
    val lines = ssc.socketTextStream("localhost", 9999)
    val words = lines.flatMap(_.split(" "))

    val pairs = words.map(word => (word, 1))
    val wordCounts = pairs.reduceByKey(_ + _)

    // Print the first ten elements of each RDD generated in this DStream to the console
    wordCounts.print()
    //啟動
    ssc.start()
    //等待結束
    ssc.awaitTermination()  // Wait for the computation to terminate


  }

}

使用shell命令監聽端口,輸入待計算內容

$ nc -lk 9999

原理

SparkStreaming的編程抽象是離散化流(DStream),它是一個RDD序列,每個RDD代表數據流中一個時間片內的數據。

參考文檔

《Spark快速大數據分析》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

武漢肺炎衝擊 IEA:10餘年來石油需求將首度萎縮

摘錄自2020年3月9日中央社報導

國際能源總署(IEA)今(9日)表示,由於2019年冠狀病毒疾病(武漢肺炎)疫情拖累全球經濟活動,2020年全球石油需求料將出現10餘年來首度萎縮。

路透社報導,總部位於法國巴黎的國際能源總署表示,預期2020年石油需求為每日9990萬桶,除較先前預測值減少約100萬桶,也比去年需求少了9萬桶。石油需求將出現2009年以來首度萎縮。國際能源總署執行董事比羅爾(Fatih Birol)發布聲明表示:「這場冠狀病毒危機廣泛影響能源市場,包括煤炭、天然氣與再生能源等。但它對石油市場的衝擊尤其嚴重,因為它阻礙人員與商品流通。」

在國際能源總署大幅下修石油需求預測之際,沙烏地阿拉伯已點燃國際原油價格戰,油價下滑逾1/4,預料還將出現29年來單日最大跌幅。

能源議題
國際新聞
IEA
武漢肺炎
探勘石油

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

桃園綠能績效佳,電動車補助高、寺廟積極認購綠電

桃園市近兩年來大力推行各項綠能政策,已小有所成。目前,全市有2.7萬輛電動機車,穩坐台灣六都第一;全台今年綠電認購大戶前兩名,也都是位於桃園的寺廟。

桃園市環保局統計,桃園市目前有2.7萬輛電動機車,相當於每年減排7,452公噸的二氧化碳以及4,023公斤的PM2.5,同時種植65萬棵樹木。目前全桃園市有超過110萬輛機車,電動機車比例僅有2.42%,桃園市今年繼續提供新購電動機車新台幣1.5萬元,加上經濟部工業局的1萬元補助,最高補助金達2.5萬,希望能繼續推動民眾替換機車,進一步提高減碳量。

同時,騎乘電動機車滿三年者,環保局推出更換鋰電池最高新台幣4,000元補助,而合法立案場所設置充電站最高也可享有1萬元補助。環保局還開發手機APP,結合GPS定位系統,幫助使用者快速找到充電站,加強民眾改用電動車的意願。

神明也愛綠電,成全台認購最大手

今年台灣綠電認購量最高的單位,是位於桃園的慈護宮,認購30萬度電;第二名則是桃園中壢仁海宮,認購10萬度電。慈護宮主委劉新發表示,廟宇是台灣民眾生活中心,加上有24小時全年不滅的「光明燈」,用電需求量大,因此除了改用更省電的LED燈之外,也積極參與認購綠電,為環保盡一份心力。

目前台電提出的綠電認購附加費率為每度電新台幣1.06元,最低綠電認購門檻為100度;只要認購最低單位的綠電,就等同於多種五棵樹。

除了寺廟之外,桃園地區也有長榮航空、東龍興業、萬海航運、科德寶遠東等企業加入認購綠電,總認購度數超過百萬度。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

麥格納攜手格特拉克將亮相第六屆中國國際新能源汽車論壇2016

由希邁商務諮詢(上海)有限公司主辦的2016年第六屆中國國際新能源汽車論壇即將於4月20日-4月22日在上海隆重舉行。此次論壇獲得了亞太電動車學會、中國國家新能源機動車產品品質監督檢驗中心、上海交大密西根學院、賽迪顧問及中國綠色能源產業技術創新戰略聯盟的大力支持。截止3月14日,論壇已經確認40位演講嘉賓出席本次論壇並做高品質學術演講。演講嘉賓分別來自菲律賓電動車協會、國家新能源機動車產品品質監督檢驗中心、美國國家能源局、中國工程院等在內的政府單位與研究機構,以及包括寶馬、通用汽車、特斯拉、長安汽車、宇通客車、奇瑞、比亞迪戴姆勒、上汽、北汽、觀致等在內的多個知名整車商,將在論壇上共同研討新能源汽車行業政策趨勢、技術路線及難點、基礎設施建設、商業模式,延續以往的豐碩成果,繼續為新能源汽車行業作出貢獻。

作為全球首屈一指的汽車零部件供應商,麥格納旗下動力總成事業部此次將攜手格特拉克成為本屆論壇的鉑金贊助商,不僅會分享電子驅動和混合驅動控制及新能源汽車熱管理研究的技術報告,還將同期展示新能源汽車領域的前沿產品。麥格納動力總成是全球領先的汽車動力總成系統供應商,集傳動系統設計、研發、試驗和製造能力為一體,以世界級的製造水準不斷實現產品和工藝的創新,為國內外客戶傳遞卓越價值。

本屆論壇相比歷屆舉辦規模最大的第六屆新能源汽車論壇,涉及主論壇及三個分論壇、考察活動、頒獎典禮和交流晚宴。屆時將誠邀全球範圍內的整車製造商、電網電力公司、電池廠商、零部件供應商、核心技術提供商和政府官員近500位行業人士一起,對新能源汽車產業面臨的挑戰,機遇與對策各方面進行為期三天更深層次並具有建設和戰略性的探討。

如需更多詳情,請諮詢:
大會連絡人:Hill ZENG(曾先生)
聯繫電話:0086 21-6045 1760 或郵箱
我們期待與貴單位一起出席於2016年4月20-22日在上海舉辦的第六屆中國國際新能源汽車論壇2016,以利決策!

 

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

一個類GraphQL的ORM數據訪問框架發布

Zongsoft.Data 發布公告

很高興我們的 ORM 數據訪問框架()在歷經兩個 SaaS 產品的應用之後,今天正式宣布對外推廣!
這是一個類  風格的 ORM(Object/Relational Mapping) 數據訪問框架。

又一個輪子?

在很長時間里,.NET 陣營似乎一直缺乏一個被普遍使用的 ORM 數據訪問框架,從最早的原生 ADO.NET 到舶來品 iBatis.NETHibernate.NET,後來又經歷了 Linq for SQL 與 Entity Framework 的混戰,可能是因為 Entity Framework 早期版本的模糊定位和反覆變更的設計導致了它失之霸主之位,進而造就了一段百舸爭流、群雄共逐的戰國時代。在歷經漫長而反覆的期待、失望、糾結和痛苦之後,我終於決定動手造一個輪子。

設計理念

在開始動手之前,先確定以下基本設計原則:

  • 數據庫優先(Database First)
  • 嚴格的 POCO/POJO 支持
  • 映射模型與代碼完全隔離
  • 禁止業務層出現 SQL 和類 SQL 代碼

在一個業務系統中,數據結構及其關係毋庸置疑是最底層的基礎性結構,數據庫應由系統架構師或開發負責人進行仔細設計 No Schema/Weakly Schema 的思潮是塗抹了蜂蜜的毒藥),數據訪問映射以數據庫表結構關係為基石,在此之上業務層亦以概念映射模型為準繩,層級之間相互隔離。

領域模型實體避免通過註解 (標籤) 來進行元數據定義,應確保嚴格符合 POCO/POJO 範式。通過語義化的 Schema 來聲明訪問的數據結構關係,禁止應用層的 SQLLinq 式的類 SQL 代碼可降低業務層對數據層的依賴、提升代碼可維護性外,還具備更加統一可控的便利性,併為數據訪問引擎的實現提供了更大的優化空間和自由度。

範例說明

下面通過三個的例子 (注:例子均基於 項目) 來佐證上面的部分設計理念,更多示例和闡述請參考  項目的  文檔和  項目的代碼。

提示: 下面的範例均基於 開源項目,該項目是一個完整的論壇社區的後台程序。你可能需要預先閱讀一下該項目的文檔,以便更好的理解範例代碼的業務邏輯。

示例一

導航查詢及導航過濾

var forums = this.DataAccess.Select<Forum>(
    Condition.Equal("SiteId", this.User.SiteId) &
    Condition.In("Visibility", Visibility.Internal, Visibility.Public) |
    (
        Condition.Equal("Visibility", Visibility.Specified) &
        Condition.Exists("Users",
                  Condition.Equal("UserId", this.User.UserId) &
                  (
                      Condition.Equal("IsModerator", true) |
                      Condition.NotEqual("Permission", Permission.None)
                  )
        )
    ),
    "*, MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}}"
);

上述數據訪問的查詢方法大致生成如下SQL腳本:

SELECT
    t.*,
    t1.ThreadId AS 'MostRecentThread.ThreadId',
    t1.Title AS 'MostRecentThread.Title',
    t1.CreatorId AS 'MostRecentThread.CreatorId',
    t2.UserId AS 'MostRecentThread.Creator.UserId',
    t2.Name AS 'MostRecentThread.Creator.Name',
    t2.Nickname AS 'MostRecentThread.Creator.Nickname',
    t2.Avatar AS 'MostRecentThread.Creator.Avatar'
FROM Forum t
    LEFT JOIN Thread AS t1 ON
        t.MostRecentThreadId=t1.ThreadId
    LEFT JOIN UserProfile AS t2 ON
        t1.CreatorId=t2.UserId
WHERE
    t.SiteId = @p1 AND
    t.Visibility IN (@p2, @p3) OR
    (
        t.Visibility = @p4 AND
        EXISTS
        (
            SELECT u.SiteId, u.ForumId, u.UserId
            FROM ForumUser u
            WHERE u.SiteId = t.SiteId AND
                  u.ForumId = t.ForumId AND
                  u.UserId = @p5 AND
                  (
                      u.IsModerator = @p6 OR
                      u.Permission != @p7
                  )
        )
    );

上述示例通過 Select 查詢方法的 schema 參數 (即值為 *, MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}} 的參數) 從數據結構關係的層次指定了查詢數據的形狀,因而不再需要 SQL 或類 SQL 語法中 JOIN 這樣命令式的語法元素,它不光提供了更簡潔且語義化的 API 訪問方式,而且還給數據訪問引擎底層提供了更大的優化空間和自由度。

如果將 Select 查詢方法的 schema 參數值改為 *,Moderators{*},MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}} 后,數據訪問引擎會將查詢內部分解為一對多的兩條 SQL 語句進行迭代執行,而這些都不需要業務層進行分拆處理,因而提升了效率並降低了業務層的複雜度。

注:Schema 模式表達式通過 Web API 提供給前端應用,將大大減少後端開發的工作量,提升前後端的工作效率。

示例二

一對多的關聯新增

// 構建待新增的實體對象
var forum = new
{
    SiteId = this.User.SiteId,
    GroupId = 100,
    Name = "xxxx",

    // 一對多的導航屬性
    Users = new ForumUser[]
    {
      new ForumUser { UserId = 1001, IsModerator = true },
      new ForumUser { UserId = 1002, Permission = Permission.Read },
      new ForumUser { UserId = 1003, Permission = Permission.Write },
    }
}

// 執行數據新增操作
this.DataAccess.Insert<Forum>(forum, "*, Users{*}");

上述數據訪問的新增方法大致生成如下SQL腳本:

/* 主表插入語句,執行一次 */
INSERT INTO Forum (SiteId,ForumId,GroupId,Name,...) VALUES (@p1,@p2,@p3,@p4,...);

/* 子表插入語句,執行多次 */
INSERT INTO ForumUser (SiteId,ForumId,UserId,Permission,IsModerator) VALUES (@p1,@p2,@p3,@p4,@p5);

上述示例通過 Insert 新增方法的 schema 參數(即值為 *,User{*} 的參數)指定了新增數據的形狀,由數據訪問引擎根據映射定義自動處理底層的 SQL 執行方式,確保業務層代碼的簡潔和更高的執行效率。

示例三

一對一和一對多的關聯更新,對於“一對多”的導航屬性,還能確保該屬性值 (集合類型) 以 UPSERT 模式寫入。

public bool Approve(ulong threadId)
{
    //構建更新的條件
    var criteria =
        Condition.Equal(nameof(Thread.ThreadId), threadId) &
        Condition.Equal(nameof(Thread.Approved), false) &
        Condition.Equal(nameof(Thread.SiteId), this.User.SiteId) &
        Condition.Exists("Forum.Users",
            Condition.Equal(nameof(Forum.ForumUser.UserId), this.User.UserId) &
            Condition.Equal(nameof(Forum.ForumUser.IsModerator), true));

    //執行數據更新操作
    return this.DataAccess.Update<Thread>(new
    {
        Approved = true,
        ApprovedTime = DateTime.Now,
        Post = new
        {
            Approved = true,
        }
    }, criteria, "*,Post{Approved}") > 0;
}

上述數據訪問的更新方法大致生成如下SQL腳本:

/* 以下代碼為支持 OUTPUT/RETURNING 子句的數據庫(如:SQLServer,Oracle,PostgreSQL) */

/* 根據更新的關聯鍵創建臨時表 */
CREATE TABLE #TMP
(
    PostId bigint NOT NULL
);

/* 更新主表,並將更新的關聯鍵輸出到內存臨時表 */
UPDATE T SET
    T.[Approved]=@p1,
    T.[ApprovedTime]=@p2
OUTPUT DELETED.PostId INTO #TMP
FROM [Community_Thread] AS T
    LEFT JOIN [Community_Forum] AS T1 ON /* Forum */
        T1.[SiteId]=T.[SiteId] AND
        T1.[ForumId]=T.[ForumId]
WHERE
    T.[ThreadId]=@p3 AND
    T.[Approved]=@p4 AND
    T.[SiteId]=@p5 AND EXISTS (
        SELECT [SiteId],[ForumId]
        FROM [Community_ForumUser]
        WHERE [SiteId]=T1.[SiteId] AND
              [ForumId]=T1.[ForumId] AND
              [UserId]=@p6 AND
              [IsModerator]=@p7
    );

/* 更新關聯表 */
UPDATE T SET
    T.[Approved]=@p1
FROM [Community_Post] AS T
WHERE EXISTS (
    SELECT [PostId]
    FROM #TMP
    WHERE [PostId]=T.[PostId]);

上述示例通過 Update 更新方法的 schema 參數(即值為 *,Post{Approved} 的參數)指定了更新數據的形狀,數據訪問引擎將根據數據庫類型生成高效的 SQL 語句,對於業務層而言這一切都是無感的、透明的。

對於一對多的導航屬性,數據訪問引擎默認將以 UPSERT 模式處理子集的寫入,關於 UPSERT 更多信息請參考  項目文檔。

性能

我們希望提供最佳的綜合性價比,對於一個 ORM 數據訪問引擎來說,性能的關注點主要 (不限) 有這些要素:

  1. 生成簡潔高效的 SQL 腳本,並盡可能利用特定數據庫的最新 SQL 語法;
  2. 數據查詢結果的實體組裝(Populate)過程必須高效;
  3. 避免反射,有效的語法樹緩存。

實現層面我們採用 Emitting 動態編譯技術對實體組裝(Populate)、數據參數綁定等進行預熱處理,可查閱  等相關類的源碼深入了解。

其他

得益於 “以聲明方式來表達數據結構關係” 的語義化設計理念,相對於命令式設計而言,它使得程序意圖更加聚焦,天然地對底層數據的表達和優化更加寬容與自由。

更多詳細內容 (譬如:讀寫分離、繼承表、數據模式、映射文件、過濾器、驗證器、類型轉換、數據隔離) 請查閱相關文檔。

支持贊助

我們歡迎並期待任何形式的推廣支持!

如果你認同我們的設計理念請為這個項目點贊(Star),如果你認為該項目很有用,並且希望支持它未來的發展,請給予必要的資金來支持它:

  1. 關注 Zongsoft 微信公眾號,對我們的文章進行打賞;
  2. 加入 ,可以獲得在線問答和技術支持;
  3. 如果您的企業需要現場技術支持與輔導,又或者需要開發新功能、即刻的錯誤修復等請給我。

提醒: 本文可能會更新,請閱讀原文:,以避免因內容陳舊而導致的謬誤,同時亦有更好的閱讀體驗。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

花了幾個小時總結了一些容易出錯的 Java 知識點!

本文已經收錄自 (61k+Star!【Java學習+面試指南】 一份涵蓋大部分Java程序員所需要掌握的核心知識。歡迎 Star!歡迎騷擾!)
原文地址:https://javaguide.cn/2019/08/20/java/java%E5%9F%BA%E7%A1%80/Java%E7%96%91%E9%9A%BE%E7%82%B9/

1. 基礎

1.1. 正確使用 equals 方法

Object的equals方法容易拋空指針異常,應使用常量或確定有值的對象來調用 equals。

舉個例子:

// 不能使用一個值為null的引用類型變量來調用非靜態方法,否則會拋出異常
String str = null;
if (str.equals("SnailClimb")) {
  ...
} else {
  ..
}

運行上面的程序會拋出空指針異常,但是我們把第二行的條件判斷語句改為下面這樣的話,就不會拋出空指針異常,else 語句塊得到執行。:

"SnailClimb".equals(str);// false 

不過更推薦使用 java.util.Objects#equals(JDK7 引入的工具類)。

Objects.equals(null,"SnailClimb");// false

我們看一下java.util.Objects#equals的源碼就知道原因了。

public static boolean equals(Object a, Object b) {
        // 可以避免空指針異常。如果a==null的話此時a.equals(b)就不會得到執行,避免出現空指針異常。
        return (a == b) || (a != null && a.equals(b));
    }

注意:

Reference:

  • 每種原始類型都有默認值一樣,如int默認值為 0,boolean 的默認值為 false,null 是任何引用類型的默認值,不嚴格的說是所有 Object 類型的默認值。
  • 可以使用 == 或者 != 操作來比較null值,但是不能使用其他算法或者邏輯操作。在Java中null == null將返回true。
  • 不能使用一個值為null的引用類型變量來調用非靜態方法,否則會拋出異常

1.2. 整型包裝類值的比較

所有整型包裝類對象值的比較必須使用equals方法。

先看下面這個例子:

Integer x = 3;
Integer y = 3;
System.out.println(x == y);// true
Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);//false
System.out.println(a.equals(b));//true

當使用自動裝箱方式創建一個Integer對象時,當數值在-128 ~127時,會將創建的 Integer 對象緩存起來,當下次再出現該數值時,直接從緩存中取出對應的Integer對象。所以上述代碼中,x和y引用的是相同的Integer對象。

注意:如果你的IDE(IDEA/Eclipse)上安裝了阿里巴巴的p3c插件,這個插件如果檢測到你用 ==的話會報錯提示,推薦安裝一個這個插件,很不錯。

1.3. BigDecimal

1.3.1. BigDecimal 的用處

《阿里巴巴Java開發手冊》中提到:浮點數之間的等值判斷,基本數據類型不能用==來比較,包裝數據類型不能用 equals 來判斷。 具體原理和浮點數的編碼方式有關,這裏就不多提了,我們下面直接上實例:

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false

具有基本數學知識的我們很清楚的知道輸出並不是我們想要的結果(精度丟失),我們如何解決這個問題呢?一種很常用的方法是:使用使用 BigDecimal 來定義浮點數的值,再進行浮點數的運算操作。

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);// 0.1
BigDecimal y = b.subtract(c);// 0.1
System.out.println(x.equals(y));// true 

1.3.2. BigDecimal 的大小比較

a.compareTo(b) : 返回 -1 表示小於,0 表示 等於, 1表示 大於。

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.compareTo(b));// 1

1.3.3. BigDecimal 保留幾位小數

通過 setScale方法設置保留幾位小數以及保留規則。保留規則有挺多種,不需要記,IDEA會提示。

BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN);
System.out.println(n);// 1.255

1.3.4. BigDecimal 的使用注意事項

注意:我們在使用BigDecimal時,為了防止精度丟失,推薦使用它的 BigDecimal(String) 構造方法來創建對象。《阿里巴巴Java開發手冊》對這部分內容也有提到如下圖所示。

1.3.5. 總結

BigDecimal 主要用來操作(大)浮點數,BigInteger 主要用來操作大整數(超過 long 類型)。

BigDecimal 的實現利用到了 BigInteger, 所不同的是 BigDecimal 加入了小數位的概念

1.4. 基本數據類型與包裝數據類型的使用標準

Reference:《阿里巴巴Java開發手冊》

  • 【強制】所有的 POJO 類屬性必須使用包裝數據類型。
  • 【強制】RPC 方法的返回值和參數必須使用包裝數據類型。
  • 【推薦】所有的局部變量使用基本數據類型。

比如我們如果自定義了一個Student類,其中有一個屬性是成績score,如果用Integer而不用int定義,一次考試,學生可能沒考,值是null,也可能考了,但考了0分,值是0,這兩個表達的狀態明顯不一樣.

說明 :POJO 類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何 NPE 問題,或者入庫檢查,都由使用者來保證。

正例 : 數據庫的查詢結果可能是 null,因為自動拆箱,用基本數據類型接收有 NPE 風險。

反例 : 比如显示成交總額漲跌情況,即正負 x%,x 為基本數據類型,調用的 RPC 服務,調用不成功時,返回的是默認值,頁面显示為 0%,這是不合理的,應該显示成中劃線。所以包裝數據類型的 null 值,能夠表示額外的信息,如:遠程調用失敗,異常退出。

2. 集合

2.1. Arrays.asList()使用指南

最近使用Arrays.asList()遇到了一些坑,然後在網上看到這篇文章: 感覺挺不錯的,但是還不是特別全面。所以,自己對於這塊小知識點進行了簡單的總結。

2.1.1. 簡介

Arrays.asList()在平時開發中還是比較常見的,我們可以使用它將一個數組轉換為一個List集合。

String[] myArray = { "Apple", "Banana", "Orange" }; 
List<String> myList = Arrays.asList(myArray);
//上面兩個語句等價於下面一條語句
List<String> myList = Arrays.asList("Apple","Banana", "Orange");

JDK 源碼對於這個方法的說明:

/**
 *返回由指定數組支持的固定大小的列表。此方法作為基於數組和基於集合的API之間的橋樑,與           Collection.toArray()結合使用。返回的List是可序列化並實現RandomAccess接口。
 */ 
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

2.1.2. 《阿里巴巴Java 開發手冊》對其的描述

Arrays.asList()將數組轉換為集合后,底層其實還是數組,《阿里巴巴Java 開發手冊》對於這個方法有如下描述:

2.1.3. 使用時的注意事項總結

傳遞的數組必須是對象數組,而不是基本類型。

Arrays.asList()是泛型方法,傳入的對象必須是對象數組。

int[] myArray = { 1, 2, 3 };
List myList = Arrays.asList(myArray);
System.out.println(myList.size());//1
System.out.println(myList.get(0));//數組地址值
System.out.println(myList.get(1));//報錯:ArrayIndexOutOfBoundsException
int [] array=(int[]) myList.get(0);
System.out.println(array[0]);//1

當傳入一個原生數據類型數組時,Arrays.asList() 的真正得到的參數就不是數組中的元素,而是數組對象本身!此時List 的唯一元素就是這個數組,這也就解釋了上面的代碼。

我們使用包裝類型數組就可以解決這個問題。

Integer[] myArray = { 1, 2, 3 };

使用集合的修改方法:add()remove()clear()會拋出異常。

List myList = Arrays.asList(1, 2, 3);
myList.add(4);//運行時報錯:UnsupportedOperationException
myList.remove(1);//運行時報錯:UnsupportedOperationException
myList.clear();//運行時報錯:UnsupportedOperationException

Arrays.asList() 方法返回的並不是 java.util.ArrayList ,而是 java.util.Arrays 的一個內部類,這個內部類並沒有實現集合的修改方法或者說並沒有重寫這些方法。

List myList = Arrays.asList(1, 2, 3);
System.out.println(myList.getClass());//class java.util.Arrays$ArrayList

下圖是java.util.Arrays$ArrayList的簡易源碼,我們可以看到這個類重寫的方法有哪些。

  private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        ...

        @Override
        public E get(int index) {
          ...
        }

        @Override
        public E set(int index, E element) {
          ...
        }

        @Override
        public int indexOf(Object o) {
          ...
        }

        @Override
        public boolean contains(Object o) {
           ...
        }

        @Override
        public void forEach(Consumer<? super E> action) {
          ...
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
          ...
        }

        @Override
        public void sort(Comparator<? super E> c) {
          ...
        }
    }

我們再看一下java.util.AbstractListremove()方法,這樣我們就明白為啥會拋出UnsupportedOperationException

public E remove(int index) {
    throw new UnsupportedOperationException();
}

2.1.4. 如何正確的將數組轉換為ArrayList?

stackoverflow:https://dwz.cn/vcBkTiTW

1. 自己動手實現(教育目的)

//JDK1.5+
static <T> List<T> arrayToList(final T[] array) {
  final List<T> l = new ArrayList<T>(array.length);

  for (final T s : array) {
    l.add(s);
  }
  return (l);
}
Integer [] myArray = { 1, 2, 3 };
System.out.println(arrayToList(myArray).getClass());//class java.util.ArrayList

2. 最簡便的方法(推薦)

List list = new ArrayList<>(Arrays.asList("a", "b", "c"))

3. 使用 Java8 的Stream(推薦)

Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本類型也可以實現轉換(依賴boxed的裝箱操作)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());

4. 使用 Guava(推薦)

對於不可變集合,你可以使用類及其與工廠方法:(參數不能為空)

List<String> il = ImmutableList.of("string", "elements");  // from varargs
List<String> il = ImmutableList.copyOf(aStringArray);      // from array

對於可變集合,你可以使用類及其工廠方法:

List<String> l1 = Lists.newArrayList(anotherListOrCollection);    // from collection
List<String> l2 = Lists.newArrayList(aStringArray);               // from array
List<String> l3 = Lists.newArrayList("or", "string", "elements"); // from varargs

5. 使用 Apache Commons Collections

List<String> list = new ArrayList<String>();
CollectionUtils.addAll(list, str);

2.2. Collection.toArray()方法使用的坑&如何反轉數組

該方法是一個泛型方法:<T> T[] toArray(T[] a); 如果toArray方法中沒有傳遞任何參數的話返回的是Object類型數組。

String [] s= new String[]{
    "dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A"
};
List<String> list = Arrays.asList(s);
Collections.reverse(list);
s=list.toArray(new String[0]);//沒有指定類型的話會報錯

由於JVM優化,new String[0]作為Collection.toArray()方法的參數現在使用更好,new String[0]就是起一個模板的作用,指定了返回數組的類型,0是為了節省空間,因為它只是為了說明返回的類型。詳見:

2.3. 不要在 foreach 循環里進行元素的 remove/add 操作

如果要進行remove操作,可以調用迭代器的 remove方法而不是集合類的 remove 方法。因為如果列表在任何時間從結構上修改創建迭代器之後,以任何方式除非通過迭代器自身remove/add方法,迭代器都將拋出一個ConcurrentModificationException,這就是單線程狀態下產生的 fail-fast 機制

fail-fast 機制 :多個線程對 fail-fast 集合進行修改的時,可能會拋出ConcurrentModificationException,單線程下也會出現這種情況,上面已經提到過。

java.util包下面的所有的集合類都是fail-fast的,而java.util.concurrent包下面的所有的類都是fail-safe的。

開源項目推薦

作者的其他開源項目推薦:

  1. :【Java學習+面試指南】 一份涵蓋大部分Java程序員所需要掌握的核心知識。
  2. : 適合新手入門以及有經驗的開發人員查閱的 Spring Boot 教程(業餘時間維護中,歡迎一起維護)。
  3. : 我覺得技術人員應該有的一些好習慣!
  4. :從零入門 !Spring Security With JWT(含權限驗證)後端部分代碼。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

研究:極地冰蓋以1990年代六倍速融化 每年將有4億人受洪災影響

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務

2016年中國各類充電設施總投資300億元 將建私人充電樁86萬個

4月1日,國家能源局發佈《2016年能源工作指導意見》,意見明確,2016年計畫建設充電站2000多座、分散式公共充電樁10萬個,私人專用充電樁86萬個,各類充電設施總投資300億元。

意見指出,全面推進電動汽車充電設施建設。按照“樁站先行、適度超前”原則,用好財政支持政策,積極完善相關配套措施,保障工程建設順利進行。加強與建築、市政等公共設施的統籌銜接,研究編制充電設施工程技術標準規範。鼓勵大眾創業、萬眾創新,積極發展充電設施分享經濟。

另外,國家能源局制定的《電動汽車充電基礎設施建設規劃》提出,到2020年國內充換電站數量達到1.2萬個,充電樁達到450萬個。以充電樁均價2萬元/個,充電站300萬元/座計,未來六年國內新能源汽車充電樁(站)的直接市場規模有望達到1240億元。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

Head First設計模式——命令模式

前言:命令模式我們平常可能會經常使用,如果我們不了解命令模式的結構和定義那麼在使用的時候也不會將它對號入座。

舉個例子:在winform開發的時候我們常常要用同一個界面來進行文件的下載,但是並不是所有地方都用同一個下載邏輯處理文件,然後下載界面卻可以是同一個界面。

為了以後復用下載界面(下載显示,進度條等)我們常常將下載執行操作定義成一個接口,在具體使用的時候實現接口,將具體執行對象設置到下載界面。當下載按鈕被按下的時候,就調用設置的具體執行對象(接收者)來執行下載的處理。

那接下來我們就看下命令模式的具體細節和實現,再回頭想想我們平時什麼時候不經意就使用到了命令模式,這樣以後交流使用專業的術語不僅能裝還能用。

1、遙控器應用場景

HeadFirst設計模式一書中以遙控器為例實現命令模式,以餐館點餐講解命令模式的對象和結構。為了邏輯清晰我們不混合兩種講解方式,只以遙控器為例講解。

現在需求是有一個遙控器,遙控器上面有控制各種電器的開關,而開關的執行控制電器是由各個廠家開發的設備(對象)插入到對應開關位置的卡槽裏面,基於這些條件我們來實現遙控器系統。

簡單粗暴的解決方案可以對開關做一個標識,當某個開關被按下時根據開關類型進行if判斷。形如 if slot1==Light ,then light.on(), else if slot1==Tv then tv.on() 這種代碼將出現一堆,對於以後增加減少開關或者更換開關都是比較糟糕的。而對於設計遙控器類來說我們應該讓遙控器代碼盡量保持簡單,而不用去關心具體廠商類怎麼執行。所以我們應該將執行封裝在一個命令對象里中,那麼我們就試着一步步實現遙控器。

  首先我們為命令對象定義一個統一的接。

  接口只有一個簡單的execute執行命令方法。

    public interface Command
    {
        //執行命令的方法
        public void execute();
    }

  接下來我們實現一個打開電燈的命令

    public class Light
    {
        public void on() {
            Console.WriteLine("打開電燈");
        }

        public void off()
        {
            Console.WriteLine("關閉電燈");
        }
    }

    public class LightOnCommand : Command
    {
        Light light;

        public LightOnCommand(Light light)
        {
            this.light = light;
        }
        public void execute()
        {
            light.on();
        }
    }

  為了簡單我們假設遙控器只有一個開關,實現遙控器。

    public class SimpleRemoteControl
    {
        //卡槽
        Command slot;

        public void setCommand(Command command)
        {
            slot = command;
        }

        //按下開關
        public void ButtonWasPressed() {
            slot.execute();
        }

    }

  測試

     static void Main(string[] args)
        {
            SimpleRemoteControl remoteControl = new SimpleRemoteControl();
            //廠商提供的電燈類,命令的接收者
            Light light = new Light();

            //我們封裝的命令對象,設置接收者
            LightOnCommand lightOnCommand = new LightOnCommand(light);

            //設置遙控器開關對應的命令對象
            remoteControl.setCommand(lightOnCommand);
            remoteControl.ButtonWasPressed();
            Console.ReadKey();
        }

  

2、命令模式、類圖

通過上面的例子我們已經使用了命令模式來實現一個簡單的遙控器,再回顧【前言】我們說的界面下載文件按鈕操作是不是就是一個典型的可以使用命令模式的應用場景。

只是有一點我們可能不會有什麼其他廠商設計好的執行類,我們也許直接就在繼承接口的命令對象中實現execute的邏輯,而不用再調用其他接收者執行。

這就是“聰明”命令對象,上面我們實現的是“傻瓜”命令對象。這個稍後再說,我們先看命令模式定義和畫出類圖。

命令模式:將“請求”封裝成對象,以便使用不同的請求、隊列或日誌來參數化其他對象。命令模式也支持撤銷的操作。

3、完成多開關遙控器和撤銷操作

假設遙控器現在有五個開關。我們已經有簡單遙控器的經驗,那麼其他4個開關我們也將對應的命令對象設置上去就行了。定義兩個數組用來記錄開關對應的命令對象。

    public class RemoteControl
    {
        Command[] onCommands;
        Command[] offCommands;
        public RemoteControl()
        {
            onCommands = new Command[5];
            offCommands = new Command[5];
            Command noCommand = new NoCommand();
            for (int i = 0; i < 5; i++)
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        public void setCommand(int slot,Command commandOn, Command commandOff)
        {
            onCommands[slot] = commandOn;
            offCommands[slot] = commandOff;
        }

        //按下開關
        public void OnButtonWasPressed(int slot)
        {
            onCommands[slot].execute();
        }
        //關閉開關
        public void OffButtonWasPressed(int slot)
        {
            offCommands[slot].execute();
        }

        //打印出數組命令對象
        public override string ToString() {
            var sb = new StringBuilder("\n------------Remote Control-----------\n");
            for (int i = 0; i < onCommands.Length; i++)
            {
                sb.Append($"[slot{i}] {onCommands[i].GetType()}\t{offCommands[i].GetType()} \n");
            }
            return sb.ToString();
        }

    }

  在遙控器中我們定義了一個Nocommand類,是為了對遙控器對應的開關初始化命令對象,避免為空報錯或者消除開關調用命令對象時檢查對象是否為空的判斷。 

     public void OnButtonWasPressed(int slot)
        {
            if(onCommand[slot]!=null))
                onCommands[slot].execute();
        }

  在許多設計模式中我們都能看到這種初始值或者空對象的使用。甚至有時候,空對象本身也被視為一種設計模式。(感覺這樣代碼比較優雅O(∩_∩)O)

遙控器完成了,我們還有做一項工作,就是撤銷操作。

撤銷操作我們同樣在命令接口裡面定義一個undo 方法。

    public interface Command
    {
        //執行命令的方法
        public void execute();
        //撤銷命令方法
        public void undo();
    }

  然後我們讓LightOnCommand實現undo方法,添加LightOffCommand命令對象。

    public class LightOnCommand : Command
    {
        Light light;

        public LightOnCommand(Light light)
        {
            this.light = light;
        }
        public void execute()
        {
            light.on();
        }
        public void undo() {
            light.off();
        }
    }


    class LightOffCommand : Command
    {
        Light light;

        public LightOffCommand(Light light)
        {
            this.light = light;
        }
        public void execute()
        {
            light.off();
        }

        public void undo()
        {
            light.on();
        }
    }

 遙控器裏面添加撤銷按鈕操作UndoButtonWasPressed並用undoCommand屬性存儲上一次操作。

    public class RemoteControl
    {
        Command[] onCommands;
        Command[] offCommands;
        Command undoCommand;
        public RemoteControl()
        {
            onCommands = new Command[5];
            offCommands = new Command[5];
            Command noCommand = new NoCommand();
            for (int i = 0; i < 5; i++)
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        public void setCommand(int slot,Command commandOn, Command commandOff)
        {
            onCommands[slot] = commandOn;
            offCommands[slot] = commandOff;
        }

        //按下開關
        public void OnButtonWasPressed(int slot)
        {
            onCommands[slot].execute();
            undoCommand = onCommands[slot];
        }
        //關閉開關
        public void OffButtonWasPressed(int slot)
        {
            offCommands[slot].execute();
            undoCommand = offCommands[slot];
        }

        public void UndoButtonWasPressed() {
            undoCommand.undo();
        }
        //打印出數組命令對象
        public override string ToString() {
            var sb = new StringBuilder("\n------------Remote Control-----------\n");
            for (int i = 0; i < onCommands.Length; i++)
            {
                sb.Append($"[slot{i}] {onCommands[i].GetType()}\t{offCommands[i].GetType()} \n");
            }
            return sb.ToString();
        }

    }

測試:

4、補充總結

補充:

①命令模式的接收者不一定要存在,之前提到過“聰明”和“傻瓜”命令對象,如果以“聰明”命令對象設計,調用者和接收者之間解耦程度比不上“傻瓜”命令對象,但是我們在使用比較簡單的時候仍然可以使用“聰明”命令對象設計。

②撤銷例子我們只做了返回最後一次操作,如果要撤銷許多次我們可以對操作記錄進行保存到堆棧,不管什麼時候撤銷,我們都可以從堆棧中取出最上層命令對象執行撤銷操作。

命令模式常被用於隊列請求,日誌請求。當隊列按照順序取到存放的命令對象后調用執行方法就行了而不用去管具體執行什麼。

日誌請求在某些場合可以用來將所有動作記錄在日誌中,並能在系統死機后通過日誌記錄進行恢復到之前的狀態(撤銷)。對於更高級的的應用而言,這些技巧可以應用到事務(transaction)處理中。

 通過簡單到更進一步的實現講解了命令模式和一些靈活點和需要注意的點,有什麼理解不到位的歡迎指正。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?