當前位置 博文首頁 > 信息技術智庫:23篇大數據系列(二)scala基礎知識全集(史上最

    信息技術智庫:23篇大數據系列(二)scala基礎知識全集(史上最

    作者:[db:作者] 時間:2021-09-13 19:07

    作者簡介:

    藍橋簽約作者、大數據&Python領域優質創作者。管理多個大數據技術群,幫助大學生就業和初級程序員解決工作難題。

    我的使命與愿景:持續穩定輸出,賦能中國技術社區蓬勃發展!

    大數據系列文章,從技術能力、業務基礎、分析思維三大板塊來呈現,你將收獲:

    ??提升自信心,自如應對面試,順利拿到實習崗位或offer;

    ??掌握大數據的基礎知識,與其他同事溝通無障礙;

    ??具備一定的項目實戰能力,對于大數據工作直接上手;

    評論、點贊、收藏是對我最大的支持!!!


    大數據工程師系列專欄:?面試真題、開發經驗、調優策略

    大數據工程師知識體系:

    大數據時代已經到來

    最近幾十年,高速發展的互聯網,滲透進了我們生活的方方面面,整個人類社會都已經被互聯網連接為一體。身處互聯網之中,我們無時無刻不在產生大量數據,如瀏覽商品的記錄、成交訂單記錄、觀看視頻的數據、瀏覽過的網頁、搜索過的關鍵詞、點擊過的廣告、朋友圈的自拍和狀態等。這些數據,既是我們行為留下的痕跡,同時也是描述我們自身最佳的證據。

    2014年3月,馬云曾經在北京的一次演講中說道:“人類正從IT時代走向DT時代”。7年過去了,正如馬云預想的那樣,大數據時代已經到來了。

    大數據工程師的工作內容是什么?

    而大數據時代,有一個關鍵性的崗位不得不提,那就是大數據工程師。想必大家也會好奇,大數據工程師,日常是做什么的呢??

    1.數據采集找出描述用戶或對業務發展有幫助的數據,并將定義相關的數據格式,交由業務開發部門負責收集對應的數據。
    2.ETL工程?對收集到的數據,進行各種清洗、處理、轉化等操作,完成格式轉換,便于后續分析,保證數據質量,以便得出可以信賴的結果。
    3.構建數倉將數據有效治理起來,構建統一的數據倉庫,讓數據與數據間建立連接,碰撞出更大的價值。
    4.數據建模基于已有的數據,梳理數據間的復雜關系,建立恰當的數據模型,便于分析出有價值的結論。
    5.統計分析對數據進行各種維度的統計分析,建立指標體系,系統性地描述業務發展的當前狀態,尋找業務中的問題,發現新的優化點與增長點。
    6.用戶畫像

    基于用戶的各方面數據,建立對用戶的全方位理解,構建每個特定用戶的畫像,以便針對每個個體完成精細化運營。

    大數據工程師必備技能

    那么,問題來了,如果想成為一名大數據工程師,勝任上述工作內容,需要具備什么樣的條件?擁有什么樣的知識呢?

    分類

    子分類

    技能

    描述

    編程基礎

    Java基礎

    大數據生態必備的java基礎

    Scala基礎

    Spark相關生態的必備技能

    SQL基礎

    數據分析師的通用語言

    SQL進階

    完成復雜分析的必備技能

    大數據框架

    HDFS&YARN

    大數據生態的底層基石

    Hive基礎

    大數據分析的常用工具

    Hive進階

    大數據分析師的高級裝備

    Spark基礎

    排查問題必備的底層運行原理

    Spark SQL

    應對復雜任務的利刃

    工具

    Hue&Zeppelin

    通用的探索分析工具

    Azkaban

    作業管理調度平臺

    Tableau

    數據可視化平臺

    業務基礎

    數據收集

    數據是如何收集到的?

    ETL工程

    怎么清洗、處理和轉化數據?

    數據倉庫基礎

    如何完成面向分析的數據建模?

    元數據中心

    如何做好數據治理?

    分析思維

    數據分析思維方法論

    怎么去分析一個具體問題?

    排查問題思維

    如何高效排查數據問題?

    指標體系

    怎么讓數據成體系化?

    Scala為什么會如此重要,作者覺得主要有以下三點原因:

    1、因為spark?

    大部分從事大數據的工程師是先了解Spark進而再去選擇學習Scala的,因為Spark是用Scala開發的,F在Spark是大數據領域的殺手級應用框架,只要搭建了大數據平臺,都會大量使用Spark來處理和分析數據,而要想學好Spark,Scala這一關必須是要過的。順便說一句,Kafka也是基于Scala開發的。

    2、無縫對接大數據生態組件?

    眾所周知,大數據生態的大部分組件都是java語言開發的。而Scala是一門基于JVM的語言,可以與java無縫混編,因此可以很好地融合到大數據生態圈。

    3、適合大數據處理與機器學習?

    Scala的語法簡潔而富有表達力,更容易掌握。Scala將面向對象與函數式編程相結合,功能強大且簡練,非常適合用于處理各種數據。因此,在大數據處理與機器學習中占有重要的地位。

    針對大數據分析師必須掌握的scala基礎知識,本文的講解思路如下:

    第1部分:scala特性。主要講解面向對象特性、函數式編程、靜態類型、擴展性和并發性。

    第2部分:表達式。在scala中一切皆為表達式,理解表達式是理解其語法的前提。

    第3部分:方法與函數。主要講兩者之間的區別和轉換。

    第4部分:模式匹配。講解常用的幾種模式,并舉例說明。

    5部分:scala?trait。講解特質的基本特性和示例。

    6部分:集合操作。主要針對常用集合和集合函數的講解和介紹。

    7部分:讀取數據源。只針對scala如何通過Source類讀取數據源進行簡單介紹。

    8部分:隱式轉換、隱式參數。主要講解Java和scala之間的類型轉換,以及通過一個實例介紹一下隱式參數的概念。

    9部分:正則匹配。主要講解如何寫正則相關的代碼。

    第10部分:異常處理。介紹scala和java的異常有何區別。

    第11部分:類型層級。主要介紹scala的類型層級體系。

    第12部分:基本數值類型轉換。講解scala與java基本數值類型轉換常遇到的問題。

    scala基礎知識

    一、Scala特性

    面向對象特性?

    Scala是一種純面向對象的語言,徹底貫徹萬物皆對象理念。對象的類型和行為是由類和特質來描述的。Scala引入特質(trait)來改進Java的對象模型,使得可以通過混入特質的方式,擴展類的功能。

    函數式編程?

    Scala也是一種函數式語言,函數也能當成值來傳遞。Scala提供了輕量級的語法用以定義匿名函數,支持高階函數,允許嵌套多層函數,并支持柯里化。Scala的case class及其內置的模式匹配相當于函數式編程語言中常用的代數類型。

    靜態類型?

    Scala擁有一個強大表達能力的類型系統,通過編譯時檢查,保證代碼的安全性和一致性。Scala具備類型推斷的特性,這使得開發者可以不去額外標明重復的類型信息,讓代碼看起來更加整潔易讀。

    ?擴展性?

    Scala的設計秉承一項事實,即在實踐中,某個領域特定的應用程序開發往往需要特定于該領域的語言擴展。Scala提供了許多獨特的語言機制,可以以庫的形式輕易無縫添加新的語言結構。

    二、表達式

    在scala中,一切皆為表達式。scala非常推崇表達式語法,因為表達式語法,對函數式編程是非常友好的。對開發者而言,表達式語法,使得代碼非常簡潔易讀。

    舉個例子,我們在定義方法時,會和聲明變量一樣,使用等號(=)連接,等號左側是函數名、參數列表和返回值類型(可以省略),而等號右邊便是一個由大括號({})包裹的多行表達式。

    表達式,是一定會有返回值的。在java中使用void來聲明無返回值的方法,而在scala里,這種情況也會有返回值,會返回一個Unit,這是一個特定的值,表示忽略方法的返回值。

    三、方法與函數

    初學scala時,往往會覺得方法和函數的概念有些模糊,在使用中可能會搞不清楚到底該使用方法還是函數。那怎么區分呢?關鍵是看這個函數是否在類中定義,在類中定義就是方法,所以Scala 方法是類的一部分。Scala 中的函數則是一個完整的對象,可以賦給一個變量。不過,在scala中,方法和函數是可以相互轉化的。下面我們重點說下,如何把方法轉為函數。

    方法轉函數

    上文中提到任何方法都是在聲明一個表達式,所以將方法轉為函數也就非常簡單了,相當于是把方法指向的表達式,又重新賦給了一個函數變量,這就是顯式轉化。還有另外一種寫法,是通過偏應用函數的方式,將方法轉化為一個新的函數,稱作隱式轉化。

    1)隱式轉化

    val f2 = f1 _

    2)顯式轉化

    val f2: (Int) => Int = f1

    四、模式匹配

    模式匹配是檢查某個值是否匹配某一個模式的機制。它是Java中的switch語句的升級版,同樣可以用于替代一系列的 if/else 語句,以下介紹幾種常用的模式匹配:常量模式、變量模式、通配符模式。

    常量模式

    常量模式匹配,就是在模式匹配中匹配常量。

    object ConstantPattern{  def main(args:Array[String]) :Unit = {    //模式匹配結果作為函數返回值    def patternShow(x : Any) = x match {          //常量模式      case 5 => "五"      case true => "真"      case "test" => "字符串"      case null => "null值"      case Nil => "空列表"          //變量模式          case x => "變量"          //通配符模式      case _ => "通配符"    }  }}
    變量模式和通配符模式,都可以匹配任意值,他們之間的區別是,變量模式匹配成功后,該變量中會存儲匹配成功的值,在后續的代碼中還可以引用,而通配符模式匹配成功后,不能再引用匹配到的值。另外要注意的是,由于模式匹配是按順序匹配的,因此變量模式和通配符模式要寫在表達式的最后面。
    

    類型匹配模式

    可以匹配輸入變量的類型。

    object TypePattern{  def main(args:Array[String]) :Unit = {  //類型匹配模式  def typePattern(t : Any) = t match {    case t : String => "String"    case t : Int => "Intger"    case t : Double => "Double"    case _ => "Other Type"    }  }}
    

    case class模式

    構造器模式指的是,直接在case語句后面接類構造器,匹配的內容放置在構造器參數中。

    object CaseClassPattern{  def main(args:Array[String]) :Unit = {  //定義一個Person實例  val p = new Person("nyz",27)   //case class 模式  def constructorPattern(p : Person) = p match {     case Person(name,age) => "name =" + name + ",age =" + age     case _ => "Other"    }  }}

    模式守衛

    為了讓匹配更加具體,可以使用模式守衛,也就是在模式后面加上if判斷語句。

    object ConstantPattern{  def main(args:Array[String]) :Unit = {    //模式匹配結果作為函數返回值    def patternShow(x : Any) = x match {          //模式守衛          case x if(x == 5) => "守衛"          //通配符模式      case _ => "通配符"    }  }}

    Option匹配

    在Scala中Option類型樣例類用來表示可能存在或也可能不存在的值(Option的子類有Some和None)。Some包裝了某個值,None表示沒有值。

    class OptionDemo {  val map = Map (("a",18),("b",81))??//get方法返回的類型就是Option[Int]  map.get("b") match {    case some(x) => println(x)    case None => println("不存在")  }}

    五、Scala Trait(特質)

    Scala Trait(特質) 相當于 Java 的接口,但實際上它比接口的功能強大。與接口不同的是,它還可以定義屬性和方法的實現。

    一般情況下Scala的類只能夠繼承單一父類,但可以使用with關鍵字混入多個 Trait(特質) 。不過,如果一個scala類沒有父類,那么它混入的第一個特質需要使用extends關鍵字,之后混入的特質使用with關鍵字。

    Trait(特質) 定義的方式與類相似,但它使用的關鍵字是 trait,如下所示:

    trait Equal {  def isEqual(x: Any): Boolean  def isNotEqual(x: Any): Boolean = !isEqual(x)}
    

    以上特質(Equal)由兩個方法組成:isEqual 和 isNotEqual。isEqual 方法沒有定義方法的實現,isNotEqual定義了方法的實現。子類繼承特質可以實現未被實現的方法。

    以下演示了特質的完整實例:

    trait Equal { def isEqual(x: Any): Boolean def isNotEqual(x: Any): Boolean = !isEqual(x)}
    
    class Point(xc: Int, yc: Int) extends Equal {  val x: Int = xc  val y: Int = yc  def isEqual(obj: Any) =    obj.isInstanceOf[Point] &&    obj.asInstanceOf[Point].x == x}
    object Test {   def main(args: Array[String]) {      val p1 = new Point(2, 3)      val p2 = new Point(2, 4)      val p3 = new Point(3, 3)
          println(p1.isNotEqual(p2))      println(p1.isNotEqual(p3))      println(p1.isNotEqual(2))   }}

    執行以上代碼,輸出結果為:

    $ scalac Test.scala $ scala -cp . Testfalsetruetrue
    

    六、集合操作

    常用集合

    通過下面的代碼,可以了解常用集合的創建方式

    // 定義整型 List,其元素以線性方式存儲,可以存放重復對象。val x = List(1,2,3,4)
    // 定義 Set,其對象不按特定的方式排序,并且沒有重復對象。val x = Set(1,3,5,7)
    // 定義 Map,把鍵對象和值對象映射的集合,它的每一個元素都包含一對鍵對象和值對象。val x = Map("one" -> 1, "two" -> 2, "three" -> 3)
    // 創建兩個不同類型元素的元組,元組是不同類型的值的集合val x = (10, "Bigdata")
    // 定義 Option,表示有可能包含值的容器,也可能不包含值。val x:Option[Int] = Some(5)
    

    集合函數

    工作中操作 Scala 集合時,一般會進行兩類操作:轉換操作(transformation )和行動操作(action)。第一種操作類型將集合轉換為另一個集合,第二種操作類型返回某些類型的值。

    1)最大值和最小值

    先從行動函數開始。在序列中查找最大或最小值是一個極常見的需求。

    先看一下簡單的例子。

    val numbers = Seq(11, 2, 5, 1, 6, 3, 9)  numbers.max //11 numbers.min //1

    對于這種簡單數據集合,Scala的函數式特性顯露無疑,如此簡單的取到了最大值和最小值。再來看一個數據集合復雜的例子。

    case class Book(title: String, pages: Int)  val books = Seq(  Book("Future of Scala developers", 85),  Book("Parallel algorithms", 240),  Book("Object Oriented Programming", 130),  Book("Mobile Development", 495))  //下面代碼返回Book(Mobile Development,495)books.maxBy(book => book.pages)  //下面代碼返回Book(Future of Scala developers,85)books.minBy(book?=>?book.pages)

    minBy & maxBy方法解決了復雜數據的問題。

    2)篩選-Filter

    對集合進行過濾,返回滿足條件的元素的新集合,比如過濾一組數據中的偶數。

    val numbers = Seq(1,2,3,4,5,6,7,8,9,10) numbers.filter(n => n % 2 == 0)//上面返回Seq(2,4,6,8,10)

    獲取頁數大于300頁的書。

    val books = Seq(  Book("Future of Scala developers", 85),  Book("Parallel algorithms", 240),  Book("Object Oriented Programming", 130),  Book("Mobile Development", 495))
    books.filter(book => book.pages >= 300)//上面返回Seq(Book("Mobile?Development",?495))

    還有一個與 filter類似的方法是 filterNot,也就是篩選出不滿足條件的對象。

    3)Flatten

    它的作用是將多個集合展開,組成一個新的集合,舉例說明。

    val abcd = Seq('a', 'b', 'c', 'd')val efgj = Seq('e', 'f', 'g', 'h')val ijkl = Seq('i', 'j', 'k', 'l')val mnop = Seq('m', 'n', 'o', 'p')val qrst = Seq('q', 'r', 's', 't')val uvwx = Seq('u', 'v', 'w', 'x')val yz   = Seq('y', 'z')  val alphabet = Seq(abcd, efgj, ijkl, mnop, qrst, uvwx, yz)
    alphabet.flatten

    執行后返回下面的集合:

    List('a',?'b',?'c',?'d',?'e',?'f',?'g',?'h',?'i',?'j',?'k',?'l',?'m',?'n',?'o',?'p',?'q',?'r',?'s',?'t',?'u',?'v',?'w',?'x',?'y',?'z')

    4)集合運算函數

    集合運算即差集、交集和并集操作。

    val num1 = Seq(1, 2, 3, 4, 5, 6)val num2 = Seq(4, 5, 6, 7, 8, 9)  //返回List(1, 2, 3)num1.diff(num2)  //返回List(4, 5, 6)num1.intersect(num2)  //返回List(1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9)num1.union(num2)
    //合并后再去重,返回List(1, 2, 3, 4, 5, 6, 7, 8, 9)num1.union(num2).distinct
    

    5)map函數

    map 函數的邏輯是遍歷集合并對每個元素調用傳入的函數進行處理。

    val numbers = Seq(1,2,3,4,5,6)  //返回List(2, 4, 6, 8, 10, 12)numbers.map(n => n * 2)  val chars = Seq('a', 'b', 'c', 'd')  //返回List(A, B, C, D)chars.map(ch?=>?ch.toUpper)
    

    6)flatMap

    它將map & flatten組合起來,請看下面的操作。

    val abcd = Seq('a', 'b', 'c', 'd')  //List(A, a, B, b, C, c, D, d)abcd.flatMap(ch?=>?List(ch.toUpper,?ch))

    從結果可以看出來是先做的map,然后做的flatten

    7)forall?& exists

    forall是對整個集合做判斷,當集合中的所有元素都滿足條件時,返回true。而exists則是只要有一個元素滿足條件就返回true。

    val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)  //返回turenumbers.forall(n => n < 10)  //返回falsenumbers.forall(n => n > 5)
    //返回truenumbers.exists(n?=>?n?>?5)

    七、讀取數據源

    讀取外部數據源是開發中很常見的需求,如在程序中讀取外部配置文件并解析,獲取相應的執行參數。這里只針對scala如何通過Source類讀取數據源進行簡單介紹。

    import scala.io.Source
    
    object ReadFile {  //讀取ClasPath下的配置文件  val file = Source.fromInputStream(this.getClass.getClassLoader.getResourceAsStream("app.conf"))    
      //一行一行讀取文件,getLines()表示讀取文件所有行  def readLine: Unit ={    for(line <- file.getLines()){      println(line)    }  }   //讀取網絡上的內容  def readNetwork: Unit ={    val file = Source.fromURL("http://www.baidu.com")    for(line <- file.getLines()){      println(line)    }  }
     //讀取給定的字符串-多用于調試 val source = Source.fromString("test") }

    八、隱式轉換

    隱式轉換是Scala中一種非常有特色的功能,是其他編程語言所不具有的,可以實現將某種類型的對象轉換為另一種類型的對象。數據分析工作中,最常使用到的就是java和scala集合之間的互相轉換,轉換以后就可以調用另一種類型的方法。scala提供了scala.collection.JavaConversions類,只要引入此類中相應的隱式轉化方法,在程序中就可以用相應的類型來代替要求的類型。

    如通過以下轉換,scala.collection.mutable.Buffer自動轉換成了java.util.List。

    import scala.collection.JavaConversions.bufferAsJavaListscala.collection.mutable.Buffer?=>?java.util.List

    同樣,java.util.List也可以轉換成scala.collection.mutable.Buffer。

    import scala.collection.JavaConversions.asScalaBufferjava.util.List?=>?scala.collection.mutable.Buffer

    所有可能的轉換匯總如下,雙向箭頭表示可互相轉換,單箭頭則表示只有左邊可轉換到右邊。

    import scala.collection.JavaConversions._
    scala.collection.Iterable <=> java.lang.Iterablescala.collection.Iterable <=> java.util.Collectionscala.collection.Iterator <=> java.util.{ Iterator, Enumeration }scala.collection.mutable.Buffer <=> java.util.Listscala.collection.mutable.Set <=> java.util.Setscala.collection.mutable.Map <=> java.util.{ Map, Dictionary }scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap
    scala.collection.Seq         => java.util.Listscala.collection.mutable.Seq => java.util.Listscala.collection.Set         => java.util.Setscala.collection.Map         => java.util.Mapjava.util.Properties???=>?scala.collection.mutable.Map[String,?String]
    

    隱式參數

    所謂隱式參數,指的是在函數或者方法中,定義使用implicit修飾的參數。當調用該函數或方法時,scala會嘗試在變量作用域中找到一個與指定類型相匹配的使用implicit修飾的對象,即隱式值,注入到函數參數中函數體使用。示例如下:

    class SayHello{  def write(content:String) = println(content)}implicit val sayHello=new SayHello
    def saySomething(name:String)(implicit sayHello:SayHello){ sayHello.write("Hello," + name)}
    saySomething("Scala")
    //打印 Hello,Scala

    值得注意的是,隱式參數是根據類型匹配的,因此作用域中不能同時出現兩個相同類型的隱式變量,否則編譯時會拋出隱式變量模糊的異常。

    九、正則匹配

    正則的概念、作用和規則都在上一篇《大數據分析工程師入門--1.Java基礎》中已經完整的講述了,這里將通過示例來講解下在scala中正則相關代碼怎么寫:

    定義

    val?TEST_REGEX?=?"home\\*(classification|foundation|my_tv)\\*[0-9-]{0,2}([a-z_]*)".r

    使用

    //path是用來匹配的字符串TEST_REGEX findFirstMatchIn path match {  case Some(p) => {    //獲取TEST_REGEX中的第一個括號里正則片段匹配到的內容    launcher_area_code = p.group(1)    //獲取TEST_REGEX中的第二個括號里正則片段匹配到的內容    launcher_location_code = p.group(2)    }}
    

    十、異常處理

    學習過Java的同學對異常一定并不陌生,異常通常是程序執行過程中遇到問題時,用來打斷程序執行的重要方式。關于異常處理的注意事項,在上一講《大數據分析工程師入門--1.Java基礎》里已經講過了,這里就不再贅述了。我們重點來講下scala和java在異常這個特性的設計上的不同。

    1. 捕獲異常的方式略有不同

    java中是通過多個catch子句來捕獲不同類型的異常,而在scala中是通過一個catch子句,加上模式匹配的類型匹配方式來捕獲不同類型的異常。如下圖所示:

    圖片

    2.scala沒有checked異常

    在java中,非運行時異常在編譯期是會被強制檢查的,要么寫try...catch...處理,要么使用throws關鍵字,將異常拋給調用者處理。而在scala中,更推崇通過使用函數式結構和強類型來減少對異常及其處理的依賴。因此scala不支持檢查型異常(checked exception)。

    當使用scala調用java類庫時,scala會把java代碼中聲明的異常,轉換為非檢查型異常。

    3.scala在throw異常時是有返回值的

    在scala的設計中,所有表達式都是有返回值的。那么,自然throw表達式也不例外,throw表達式的返回值為Nothing。由于Nothing類型是所有類型的子類型,因此throw表達式可以出現在任意位置,而不會影響到類型的推斷。

    十一、類型層級

    在scala中,所有的值都是有類型的,包括數值型值和函數,比java更加徹底地貫徹了萬物皆對象的理念。因此,scala有一套自己的類型層級,如下圖所示:

    圖片

    (圖片來自于網絡)

    如圖中所示,scala的頂級類是Any,下面包含兩個子類,AnyVal和AnyRef,其中AnyVal是所有值類型的父類,其中包含一個特殊的值Unit;而AnyRef是所有引用類型的父類,所有java類型和非值類型的scala類型都是它的子類。其中,有兩個比較特殊的底層子類型,一個是Null,它是所有引用類型的子類型,可以賦給任何引用類型變量;另一個是Nothing,它是所有類型的子類,因此既可以賦給引用類型變量,也可以賦給值類型變量。

    十二、基本數值類型轉換

    在scala中,通常會自動進行java和scala之間基本數值類型的轉換,并不需要單獨去處理。所以,在我們的感受中,通常java和scala的基本數據類型是可以無縫銜接的。但是,有一種情況是例外的,那就是當你引用第三方的java類庫,而在它的代碼中接收參數是Object類型,之后又對傳入對象的實際數值類型做判斷時,通常會失敗報錯。

    原因很簡單,第三方java類庫,使用java語言編寫,它只認得java的類型。當接收參數為Object類型時,scala默認不會轉換成java的數值類型,這樣當判斷對象的具體數值類型時,會出現不認識scala對象類型的異常。

    解決方案也很簡單,只需要在傳入第三方類庫方法前,手動包裝成java類型即可。以下是代碼示例,本例演示了DBUtils類庫傳入scala類型時的處理,只展示了部分代碼:

程序員cxuan的個人主頁:這篇 Java 基礎,我吹不動了 小小張自由―>張有博:軟件工程――編碼、測試、維護 小小張自由―>張有博:淺談面向對象方法學 小小張自由―>張有博:UML――概述(事物、關系、圖) 小小張自由―>張有博:UML――用例圖 小小張自由―>張有博:UML――活動圖和狀態圖 小小張自由―>張有博:UML――交互圖(順序圖與協作圖) 小小張自由―>張有博:UML――實現圖(構件圖與部署圖) 小小張自由―>張有博:C#編程基礎――C#與.NET的關系 小小張自由―>張有博:C#編程基礎――數據類型 小小張自由―>張有博:C#編程基礎――常量與變量 小小張自由―>張有博:C#編程基礎――運算符與表達式 小小張自由―>張有博:C#編程基礎――循環語句 小小張自由―>張有博:C#編程基礎――跳轉語句 小小張自由―>張有博:C#編程基礎――類 小小張自由―>張有博:C#編程基礎――方法 小小張自由―>張有博:初始三層架構(超超超詳細) 小小張自由―>張有博:C#連接數據庫之Connection、Command、D 小小張自由―>張有博:System.ArgumentOutOfRangeException: 小小張自由―>張有博:機房重構之單例模式的應用 小小張自由―>張有博:機房重構之備忘錄模式的應用 小小張自由―>張有博:機房重構之職責鏈模式的應用 小小張自由―>張有博:HTML基礎――標簽 小小張自由―>張有博:div+css的入門知識 小小張自由―>張有博:CSS核心內容:標準流、盒子模型、浮動 小小張自由―>張有博:asp.net生成驗證碼并提交驗證 小小張自由―>張有博:XML基礎 小小張自由―>張有博:各種計算機語言簡短簡介 小小張自由―>張有博:2020年10月自考總結 小小張自由―>張有博:vs2019利用gitee(碼云)協作開發 小小張自由―>張有博:1024程序員節 小小張自由―>張有博:IDEA2020.3詳細安裝教程 小小張自由―>張有博:JavaWeb之Request與Response詳解 小小張自由―>張有博:JavaWeb之Filter和Listener 小小張自由―>張有博:Vue插件報錯:Vue.js is detected on t 小小張自由―>張有博:在項目中使用Spring Cloud Alibaba Sen 小小張自由―>張有博:在項目中使用OpenFeign 小小張自由―>張有博:解決idea打開Vue項目報紅 小小張自由―>張有博:CentOS7詳細安裝教程--圖文介紹超詳細 zhtbs的博客:Springboot 入門培訓 5 Thymeleaf 與 MVC項目搭建 zhtbs的博客:(Framework7 移動webapp) Springboot 入門培訓 7 zhtbs的博客:HTML+CSS+JavaScript 迷宮生成算法 【建議收藏】 zhtbs的博客:(Framework7 移動webapp) Springboot 入門培訓 8 C zhtbs的博客:Springboot 入門培訓 9 Security(一) 登錄驗證 zhtbs的博客:Springboot 入門培訓 4 WEB+JSP MVC項目搭建 zhtbs的博客:Springboot 入門培訓 10 Security(二) 數據庫DB 貓耳山在天邊:《Linux命令行與shell腳本編程大全》(第三版)讀 英雄哪里出來:??13萬字《C語言動漫對話教程(入門篇)》??(建議收 qq1113673178的博客:[學習][筆記] qt5 從入門到入墳:<12>Grap qq1113673178的博客:[學習][筆記] qt5 從入門到入墳:<13>基于 .net平臺的rabbitmq使用封裝demo詳解 C++類的特種函數生成機制詳解 Python調用百度AI實現圖片上表格識別功能 node自定義安裝更改npm全局模塊默認安裝路徑的步驟 帶你用C語言實現strtok和字符串分割函數 靜態網頁和靜態網頁性能比較 網頁標題優化原則和描述優化原則 php 怎么設置cookie記住密碼 php設置時區無效怎么辦 php __autoload 失效怎么辦 有關PHP調試的小技巧,看看吧! 從0開始:教你微信小店怎么開! 成本5元竟然賣50元 微信朋友圈賣面膜真黑啊 HashMap原理及put方法與get方法的調用過程 基于IDEA 的遠程調試 Weblogic的操作過程 UTC時間、GMT時間、本地時間、Unix時間戳的具體使用 如何利用SwiftUI實現可縮放的圖片預覽器 網站怎么利用內容更新雙重境界快速提高網站權重? php顯示繁體亂碼怎么辦 php不能開啟php_curl怎么辦
A级免费视频