大数据学习之 scala基础(补充)
scala基础:
hello world:
- 写scala可运行文件的注意事项
- 1、如果一个scala文件要运行,class要改成object
- 2、如果是class,就仅单纯代表一个类,如果是object代表的是单例对象
- 3、scala语法中,一句话结束不需要加分号
- 4、scala文件中,可以无缝使用java中的类和方法
object HelloWorld {def main(args: Array[String]): Unit = {// 输出一句hello worldprintln("hello world")// java语言的输出一句话System.out.println("hello world")}
}
变量、常量
/*** 变量:在程序的运行过程中,其值可以发生改变的量* 在scala中定义一个变量,需要使用一个关键词:var*常量: 定义一个常数,使用关键字:var* 注意:* 1、变量一旦定义,它的类型就确定,可以不用手动指定类型,根据赋的值自动推断出类型* 2、也可以手动的指定变量的数据类型,完整的写法:var 变量名:数据类型 = 值** scala中的数据类型和java的数据类型对应关系(Byte->Double 按所占字节数的大小,从小到大进行排序):* java: scala:* byte Byte* short Short* int Int* long Long* float Float* double Double* boolean Boolean* char Char*/
// var定义一个变量var a1 = 100println(a1)//获取变量的类型println(a1.getClass)// 更改a1的值a1 = 200println(a1)var a2: Int = 10println(a2.getClass)val a3: Int = 100println(a3)// val 定义常量,若是修改其值会报错
// a3 = 200// * : 为一个函数,底层通过StringBuilder来实现字符的链接println("=" * 50)
字符串
/*** scala中字符串、及其函数的使用* 字符串:由若该字符串组成的序列*/
// 可以使用双引号构建字符串
var s1: String = "这是一个字符串"
println(s1)// 使用""" """" 构建一个长字符串
var sql: String ="""|这是一个长字符串|真的很长|注意了!!!|""".stripMargin
println(sql)// String类和Java是一个共同的字符串类,String类中的功能在scala中正常使用var s3 = "hello,world,java,hadoop,scala"
val arr1: Array[String] = s3.split(",")
// scala中的数组下标也是从0开始的,不过取的时候要使用arr1(0)
println(arr1(0))
println(arr1(1))
println(arr1(2))/*** scala中字符串的拼接:* 1、使用 + 进行拼接,不过这种方式很消耗性能* 2、使用StringBuilder* 3、使用scala的特有函数mkString,前提条件是:有一组可拼接的序列* 4、使用scala特有的字符串传递方式 s"{变量}” (类似于python语言) 底层就是使用StringBuilder方式拼接的*/
var q1: String = "hello"
var q2: String = "hello"
var q3: String = "hello"
var res1: String = q1 + "," + q2 + "," + q3
println(res1)var res2: StringBuilder = new StringBuilder()
res2.append(q1).append(",").append(q2).append(",").append(q3)
println(res2)var res3: String = arr1.mkString(",")
println(res3)// 使用s"${}", 功能强大可以在括号中调用函数
var res4: String = s"${q1.toUpperCase},${q2},${q3}"
println(res4)
运算符
/*** 运算符*/
var x: Int = 3
var y: Int = 4println(x + y)
println(x - y)
println(x * y)
//此处的 / 为整除,若想让其取小数,可以让两个数中的其中一个变成一个浮点数
println(x / y)
println(x * 1.0 / y)
println(x % y)
循环语句
/*** 循环语句:* 注:* 1、在scala语言中,没有++或--, 如 i++ 、 i--* 2、在scala语言中,不存在和java一样的普通for循环* 3、scala中的循环的写法不太一样*/var i: Int = 0
while (i < arr2.length){println(arr2(i))
}// 1 to 10 : 相当于闭区间的1到10
for (e <- 1to 10){println(e)
}for(e <- 1 until 10){println(e)
}
控制语句
/*** 控制语句* 注:在scala中没有break、continue关键字* 要想使用break得导包:import scala.util.control.Breaks.break*///TODO 在break后,程序的执行结束,如若想继续执行,那么需要再加上一个breakablebreakable{for (e <- 1 to 10) {if (e == 5) {//TODO:底层为一个异常抛出, def break(): Nothing = { throw breakException }break;}println(e)}}println("太牛了!")
}
IO流
def main(args: Array[String]): Unit = {//读取一个文件内容//使用java的方式读取文件, 使用带缓冲区的字符输入流val br: BufferedReader = new BufferedReader(new FileReader("scala/data/words.txt"))var line:String = br.readLine()while (line!=null){println(line)line = br.readLine()}//scala中的读取文件的方式//Source.fromFil 底层是使用了字节输入流读取数据FileInputStreamval bs: BufferedSource = Source.fromFile("scala/data/words.txt")// getLines();返回的是一个迭代器对象, 使用迭代器的hasNext()、next() 方法进行数据的输出val lineIterator: Iterator[String] = bs.getLines()while (lineIterator.hasNext){val s: String = lineIterator.next()println(s)}// 既然返回的是一个迭代器,那么就可以使用for循环来进行输出for (e <- bs.getLines()) {println(e)}//java写文件/*** FileWriter对象被用作参数来创建一个BufferedWriter对象。* 这样,就可以通过BufferedWriter来写入字符,而实际的写入操作(包括可能的缓冲)将由BufferedWriter处理。*/val bw = new BufferedWriter(new FileWriter("scala/data/words2.txt"))bw.write("写入数据!")// newLine()方法用于写入一个行分隔符bw.newLine()bw.write("太棒了!")//flush()方法用于将缓冲区中的数据强制写入到底层输出流(如FileWriter)中,并清空缓冲区。bw.flush()//TODO 纯scala中没有写文件的方式!!}
异常抛出(与java中很像)
手动抛出异常:
val sc = new Scanner(System.in)
print("输入除数:")
val cs: Int = sc.nextInt()
if(cs!=0){println(10/cs)
}else{throw new ArithmeticException("您输入的除数是0")
}
使用try、catch捕获异常
def main(args: Array[String]): Unit = {/*** scala中的异常和java的很像*/try {println(10/2)val arr1: Array[Int] = Array(1, 2, 3, 4, 5)println(arr1(2))val br: BufferedReader = new BufferedReader(new FileReader("scala/data/words888.txt"))val sc = new Scanner(System.in)print("输入除数:")val cs: Int = sc.nextInt()println(10/cs)// 异常被捕获后,后续代码都可以运行}catch{//类似于sql语句中case when// 使用case来选择抛出的异常case e:ArithmeticException=>println("除0异常")e.printStackTrace()case e:ArrayIndexOutOfBoundsException=>println("数组越界异常")// TODO _ : 表示所有的异常都可以抛出,相当于Exceptioncase _ =>println("出现异常")}finally {//TODO 用于确保无论是否发生异常,都会执行一段代码。// 今后finally中的处理大部分情况下都与释放资源有关println("这是finally代码块")}println("hello world")
}
scala中的函数
/*** def: 定义函数或者方法的关键字* main: 是函数或者方法的名字,符合标识符的命名规则* args: 函数形参的名字* Array[String]: 参数的数据类型是一个元素为字符串的数组* =: 后面跟着函数体(与Java中不同之处)* Unit: 等同于java中的void 表示无返回值的意思**形式:* def main(args: Array[String]): Unit = {** }** 在不同的地方定义,称呼不一样* 函数:在object中定义的叫做函数* 方法:在class中定义的叫做方法*/object Demo5Function {def main(args: Array[String]): Unit = {//调用函数val res1: Int = add(3, 4)println(res1)// scala中的函数可以嵌套定义,函数中可以再定义函数def plus(x: Int, y: Int): Int = {return x + y}//调用必须在定义之后val res2: Int = plus(10, 20)println(res2)// 函数无法成功调用
// val res3: Int = add2(11, 22)
// println(res3)val d1: Demo1 = new Demo1()val res4: Int = d1.add2(11, 22)println(res4)//调用形式1:object中的函数可以使用类名调用,类似于静态一样val res5: Int = Demo5Function.add(100, 200)println(res5)//调用形式2:object中的函数调用时,可以省略类名val res6: Int = add(200, 300)println(res6)val res7: Int = fun1("1000")println(res7)//TODO 如果方法调用的函数只有一个参数的时候,可以将.和小括号用空格代替调用val res9: Int = Demo5Function.fun1("1000")val res8: Int = Demo5Function fun1 "1000" // "=" * 50 -> "=".*(50)println(res8)//TODO 如果定义的时候,没有小括号,调用的时候,就不需要加小括号(无需传入参数)show}//定义格式1:如果函数有返回值,且最后一句话作为返回值的话,return关键字可以不写def add3(a1: Int, b1: Int): Int = {a1 + b1}//定义格式2:如果函数体中只有一句实现,那么大括号也可以不写def add4(a1: Int, b1: Int): Int = a1 + b1//定义格式3:如果函数没有参数的时候,小括号省略不写def show= println("好好学习,天天向上!")//需求1:定义一个求两个数之和的函数,返回结果def add(a1: Int, b1: Int): Int = {return a1 + b1}def fun1(s:String): Int = {return s.toInt}}//TODO 函数或者方法必须定义在class或者object中,否则将会报错,无法进行编译
//def add2(a1: Int, b1: Int): Int = {
// return a1 + b1
//}class Demo1{//这里叫方法,将来调用时需要创建该类的对象才可以调用def add2(a1: Int, b1: Int): Int = {return a1 + b1}
}
递归调用
/*** scala中的函数也可以递归* 方法定义时,自身调用自身的现象** 条件:要有出口(停止递归调用条件),不然就是死递归*/
object Demo6Function {def main(args: Array[String]): Unit = {//求阶乘 5!val res1: Int = factorial(5)println(s"5的阶乘是$res1")println(s"5的阶乘是${Demo6Function factorial 5}")}def factorial(number: Int): Int = {if (number == 1) {1} else {number * factorial(number - 1)}}}
scala中定义class类
object Demo7Class {def main(args: Array[String]): Unit = {// val s1: Student = new Student()// val s1: Student = new Student("张三",18)val s2: Student = new Student("张三", 18, "男")println(s2)//如果调用的是一个类的无参构造方法,new的时候小括号可以不用写val s3: Student2 = new Student2s3.fun1()//也可以使用多态的方式创建对象val s4:Object = new Student("张三111", 19, "男")
// s4.fun1()println(s4.toString)}
}/*** 可以在scala程序定义类* 类:构造方法 成员方法 成员变量** 构造方法:* 1、在scala中构造方法的编写和在java中不太一样,类所拥有的大括号中都是构造代码块的内容* 2、默认情况下,每一个类都应该提供一个无参的构造方法* 3、构造方法可以有许多*/
class Student(name: String, age: Int) {/*** 定义成员变量*/val _name: String = nameval _age: Int = age// _: 这个下划线,就表示将来不传值时,会赋予其默认值。String的默认值是一个特殊的值,即nullvar _gender: String = _/*** 构造方法也可以写多个*/// TODO def this () :为重载的构造器,有着不同的参数列表,// 在创建类的对象时,若传递三个参数,则会使用该构造方法进行初始化对象def this(name: String, age: Int, gender: String) {/*** this():* 用于在辅助构造器中调用主构造器或其他辅助构造器,* 以确保对象被正确初始化。需要注意的是,this(...)调用必须是构造器体中的第一条语句。*/this(name: String, age: Int)_gender = gender}// println("好好学习,天天向上!")/*** 也可以重写方法* 此处定义的类的父类都是Object,重写继承自父类的toString方法*/override def toString: String = {// 使用s"${}"的形式会报错"姓名:" + _name + ", 年龄:" + _age + ", 性别:" + _gender}// override def toString: String = super.toString
}class Student2{def fun1()={println("666")}
}
样例类
/*** scala提供了一个非常好用的功能:样例类* 较少用户创建类所编写代码量,只需要定义成员变量即可,自动扩充成员变量,构造方法,重写toString方法*/
object Demo8CaseClass {def main(args: Array[String]): Unit = {val t1 = new Teacher("小虎", 16, "学习")println(t1)println(t1.name)println(t1.age)println(t1.like)t1.like = "敲代码"println(t1)}
}/*** 样例类中的成员变量,编译后默认是被jvm添加了final关键字,用户是改变不了的* 对于scala来说,默认是被val修饰的* 如果将来想要被改变,定义的时候需要使用var进行修饰*/
case class Teacher(name:String,age:Int,var like:String)
伴生对象(apply方法)
object Demo9Apply {def main(args: Array[String]): Unit = {val b: Book1 = new Book1()b.apply() // 定义在class中是一个普通的方法// TODO: 若定义在object中,那么可以直接用Book("中华上下五千年", 999)的形式来调用这个方法val b1: Book = Book("中华上下五千年", 999)println(b1)}
}class Book1 {def apply(): Unit = {println("哈哈哈")}
}// TODO object Book 为 class Book的伴生对象
object Book {def apply(name:String,price:Int): Book = {new Book(name,price)}
}class Book(name: String, price: Int) {val _name: String = nameval _price: Int = priceoverride def toString: String = "书名:" + _name + ", 价格:" + _price
}
scala面向函数式编程
/*** scala中的函数式编程** 面向对象编程:将对象当作参数一样传来传去* 1、对象可以当作方法参数传递* 2、对象也可以当作方法的返回值返回* 当看到类,抽象类,接口的时候,今后无论是参数类型还是返回值类型,都需要提供对应的实现类对象** 面向函数式编程:将函数当作参数一样传来传去* 1、函数A当作函数B的参数进行传递* 2、函数A当作函数B的返回值返回** 在scala中,将函数也当作一个对象,对象就有类型* 函数在scala也有类型的说法* 函数的类型的形式为:* 参数类型=>返回值类型**/
将函数当作对象,赋值给类型是函数类型的变量
//是一个参数为字符串类型,返回值是整数类型的函数def fun1(s: String): Int = {s.toInt + 1000}val res1: Int = fun1("1000")println(res1)//定义变量的方式,定义一个函数//将函数当作对象,赋值给类型是函数类型的变量,将来可以直接通过变量调用函数val fun2: String => Int = fun1val res2: Int = fun2("2000")println(res2)/*** 函数A作为函数B的参数定义** 本质上是将函数A的处理逻辑主体传给了函数B,在函数B中使用这个处理逻辑*/
// show1 show2 相当于函数A
// fun1 相当于函数B//定义def fun1(f: String => Int): Int = {val a1: Int = f("1000")a1 + 3000}def show1(s:String): Int = {s.toInt}//调用val res1: Int = fun1(show1)println(res1)def show2(s: String): Int = {s.toInt+11111}val res2: Int = fun1(show2)println(res2)//定义一个函数fun1, 函数的参数列表中,既有正常的类型参数,也有函数类型的参数def fun1(s: String, f: String => Int): Int = {val a1: Int = f(s)a1 + 1000}def show1(s: String): Int = {s.toInt}def show2(s: String): Int = {s.toInt + 1111}//.....val res1: Int = fun1("2000", show2)println(res1)//使用lambda表达式改写函数作为参数传递的调用形式:(s: String) => s.toIntfun1("2000", (s: String) => s.toInt)fun1("2000", (s: String) => s.toInt+1000)//在scala中,数据类型可以自动类型推断fun1("2000", s => s.toInt+1000)//如果当作参数的函数的参数只在函数主体使用了一次,那么可以使用_代替fun1("2000", _.toInt+1000)val res2: Int = fun1("2000", _.toInt+1000)println(res2)
函数当作参数传递的应用
object Demo11Fun {def main(args: Array[String]): Unit = {val arr1: Array[Int] = Array(11, 22, 33, 44, 55)// for循环输出数组for (e <- arr1) {println(e)}// 定义一个函数def fun1(i: Int): Unit = {println(i*2)}//def foreach[U](f: A => U): Unit//foreach函数需要一个参数,它和数组元素一样的类型,返回值是Unit的函数//foreach函数的主要作用是将调用该方法的序列中的元素,依次取出并传递给传入的函数进行处理arr1.foreach(fun1)// scala自带的一个函数def println(x: Any) = Console.println(x)// Any可以接收任意的数据类型元素arr1.foreach(println)}
}
函数当作返回值返回
//定义返回值是函数的函数方式1:def fun1(s1: String): String => Int = {def show(s: String): Int = {s.toInt + s1.toInt}show}val resFun1: String => Int = fun1("1")val res1: Int = resFun1("1000")println(res1)
//定义方式2(是方式1的简化写法):/*** 方式2这种将参数分开定义,今后调用时可以分开传递,这种做法,在scala中叫做函数柯里化** 面试题:什么是函数柯里化?* 1、本身是一个数学界的一个名词,本意是原来一次传递多个参数,现在被改成了可以分开传递的形式,这种做法叫做柯里化* 2、在scala中体现柯里化,指的是函数的返回值也是一个函数,将来调用时参数可以分开传递。* 3、提高了程序的灵活性和代码复用性* 4、在scala中也可以通过偏函数实现参数分开传递的功能*/
def fun1(s1: String)(s: String): Int = {s.toInt + s1.toInt
}//调用函数的返回值是函数的方式1:
val resFun1: String => Int = fun1("1")
val r1: Int = resFun1("11")
println(r1)
val r2: Int = resFun1("12")
println(r2)
val r3: Int = resFun1("13")
println(r3)//调用方式2:val res2: Int = fun1("1")("1000")println(res2)def function1(s1: String, s2: String): Int = {s1.toInt + s2.toInt
}val res1: Int = function1("1", "1000")println(res1)
/*** 偏函数*/
//TODO 将第二个参数用 _ 代替,则会返回一个函数(由底层代码进行操作)
val f1: String => Int = function1("1", _)
val res1: Int = f1("1000")
val res2: Int = f1("2000")
val res3: Int = f1("3000")
println(s"res1:$res1,res2:$res2,res3:$res3")
集合
ArrayList
package com.shujia.jichuimport java.utilobject Demo13ArrayList {def main(args: Array[String]): Unit = {val list1: util.ArrayList[Int] = new util.ArrayList[Int]()list1.add(11)list1.add(123)list1.add(22)list1.add(31)list1.add(17)println(list1)println("=" * 50)//scala中的for循环,只能遍历scala中的序列,无法遍历java的序列// for (e <- list1) {//// }var i = 0while (i < list1.size()) {println(list1.get(i))i += 1}}
}
- scala中的集合:
- List: 元素有序,且可以发生重复,长度固定的
- Set: 元素无序,且唯一,长度固定的
- Map: 元素是键值对的形式,键是唯一的
- Tuple: 元组,长度是固定的,每个元素的数据类型可以不一样
List
//创建一个scala中的List集合
//创建了一个空集合
val list1: List[Nothing] = List()
val list2: List[Int] = List(34, 11, 22, 11, 33, 44, 55, 22, 75, 987, 1, 12, 34, 66, 77)println(list2)
//获取List集合的长度
println(list2.size)
println(list2.length)
println("=" * 50)
//可以直接通过索引下标获取元素
println(list2(0))
println(list2(1))
println(list2(2))
println("=" * 50)
//scala推荐获取第一个元素的方式是调用head函数(scala更希望使用这种方式来获取第一个元素的值)
println(list2.head)
println(list2.last)
//根据指定的分隔符拼接元素
println(list2.mkString("|"))
// 34|11|22|11|33|44|55|22|75|987|1|12|34|66|77
println("=" * 50)
val resList1: List[Int] = list2.reverse //返回一个新的所有元素反转的集合
println(s"list2:$list2")
println(s"resList1:$resList1")
println("=" * 50)
val resList2: List[Int] = list2.distinct //返回一个新的没有重复元素的集合
println(s"list2:$list2")
println(s"resList2:$resList2")
println("=" * 50)
val resList3: List[Int] = list2.tail // 除去第一个,其余的元素返回一个新的集合
println(s"list2:$list2")
println(s"resList3:$resList3")
println("=" * 50)
val resList4: List[Int] = list2.take(5) // 从左向右取指定数量的元素
println(s"list2:$list2")
println(s"resList4:$resList4")
println("=" * 50)
val resList5: List[Int] = list2.takeRight(5) //取右边的几个(取的顺序为从左向右),组成新的集合
println(s"list2:$list2")
println(s"resList5:$resList5")
println("=" * 50)
//从第一个判断取数据,直到不符合条件停止
val resList10: List[Int] = list2.takeWhile((e: Int) => e % 2 == 0)
println(s"list2:$list2")
println(s"resList10:$resList10")
// resList10:List(34)
println("*********************" * 5)
val res1: Int = list2.sum // 元素必须是数值,sum求和
println(s"集合中的元素和为:$res1")
println("=" * 50)
val res2: Int = list2.max
println(s"集合中的元素最大值为:$res2")
println("=" * 50)//集合的遍历
for (e <- list2) {println(e)
}
println("=" * 50)
高阶函数
- 高阶函数:
- foreach: 依次取出元素,进行后面函数逻辑,没有返回值
- map: 依次取出元素,进行后面函数逻辑,有返回值,返回新的集合
- filter: 所有数据中取出符合条件的元素
- sortBy/sortWith: 排序
- flatMap: 扁平化
- groupBy: 分组,结果是一个map集合
foreach
- foreach: 将集合中的元素依次取出传入到后面的函数中
- 注意:没有返回值的,要么就输出,要么就其他方式处理掉了
//def foreach[U](f: A => U)
// list2.foreach((e: Int) => println(e))
// list2.foreach(println)
//需求1:使用foreach求出集合中偶数的和
var ouSum = 0
var jiSum = 0
list2.foreach((e: Int) => {if (e % 2 == 0) {ouSum += e} else {jiSum += e}
})
println(s"集合中偶数之和为:$ouSum")
println(s"集合中奇数之和为:$jiSum")
println("=" * 50)
map
- 高阶函数:
- map: 依次处理每一个元素,得到一个新的结果,返回到一个新的集合中
val list3: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
//需求2:将集合中的每一个元素*2
val resList6: List[Int] = list3.map((e: Int) => e * 2)
println(s"list3:$list3")
println(s"resList6:$resList6")
filter
保留符合条件的元素
println("=" * 50)
val list4: List[Int] = List(4, 7, 9, 10, 12, 11, 14, 9, 7)
val resList7: List[Int] = list4.filter((e: Int) => e % 2 == 0)
println(s"list4:$list4")
println(s"resList7:$resList7")
sortBy、sortWith
- sortBy: 排序
- sortWith: 两个数之间的关系排序
println("=" * 50)
// -e : 表示为降序排序
val resList8: List[Int] = list4.sortBy((e: Int) => -e)
println(s"list4:$list4")
println(s"resList8:$resList8")
// 相邻元素之间两两比较,递减排序
val resList9: List[Int] = list4.sortWith((x: Int, y: Int) => x > y)
println(s"list4:$list4")
println(s"resList9:$resList9")
flatMap
- flatMap: 扁平化
println("=" * 50)
val list5: List[String] = List("hello|world|java", "hello|hadoop|flink", "scala|spark|hadoop")
val resTmp1: List[String] = list5.flatMap((e: String) => e.split("\\|"))
resTmp1.foreach(println)
/*** hello* world* java* hello* hadoop* flink* scala* spark* hadoop*/
groupBy
- groupBy: 分组
val list6: List[String] = List("hello", "world", "java", "hadoop", "flink", "java", "hadoop", "flink", "flink", "java", "hadoop", "flink", "java", "hadoop", "hello", "world", "java", "hadoop", "hello", "world", "java", "hadoop")
val map: Map[String, List[String]] = list6.groupBy((e: String) => e)
for (e <- map) {println(e)
}
set集合
def main(args: Array[String]): Unit = {// val set1: Set[Int] = Set(11, 22, 33, 44)/*** set集合:scala中的Set集合也是不可变的,除了排序相关的函数以外,List集合有的高阶函数,Set集合也有*/val set1: Set[Int] = Set(1, 4, 3, 6, 5)val set2: Set[Int] = Set(3, 6, 5, 7, 8)println(s"set1: ${set1}")println(s"set2: ${set2}")println("=" * 50)//求交集
// val resSet1: Set[Int] = set1.&(set2)
// val resSet1: Set[Int] = set1 & set2val resSet1: Set[Int] = set1.intersect(set2)println(s"set1: ${set1}")println(s"set2: ${set2}")println(s"交集: ${resSet1}")println("=" * 50)//求并集// val resSet2: Set[Int] = set1.|(set2)val resSet2: Set[Int] = set1 | set2println(s"set1: ${set1}")println(s"set2: ${set2}")println(s"并集: ${resSet2}")println("=" * 50)//求差集// val resSet3: Set[Int] = set1.&~(set2)val resSet3: Set[Int] = set1 &~ set2println(s"set1: ${set1}")println(s"set2: ${set2}")println(s"差集: ${resSet3}")println("=" * 50)/*** Set集合和List集合能不能互相转换?* 可以的*/val list1: List[Int] = List(11, 22, 33, 44, 55, 11, 22, 44, 88, 33, 44, 99, 11, 22, 55)//List->Setval resSet4: Set[Int] = list1.toSetprintln(s"list1:${list1}")println(s"resSet4:${resSet4}")println("=" * 50)//Set->Listval list2: List[Int] = resSet4.toList.sortBy((e:Int)=>e)println(s"list1:${list1}")println(s"resSet4:${resSet4}")println(s"list2:${list2}")}
Mutable下的可变的集合
import scala.collection.mutable
import scala.collection.mutable.ListBufferobject Demo16Mutable {def main(args: Array[String]): Unit = {/*** 通过观察api发现,不可变的集合是属于scala.collection.immutable包下的* 如果将来想要使用可变的集合,就要去scala.collection.mutable包下寻找*///创建一个可变的List集合val listBuffer1: ListBuffer[Int] = new ListBuffer[Int]println(listBuffer1)listBuffer1.+=(11)listBuffer1.+=(22)listBuffer1.+=(33)listBuffer1.+=(11)listBuffer1.+=(55)listBuffer1.+=(22)listBuffer1.+=(33)listBuffer1.+=(66)listBuffer1.+=(33)println(listBuffer1)println("=" * 50)//获取元素println(listBuffer1(2))println(listBuffer1.head)println(listBuffer1.last)/*** 这里的可变List集合,上午说的功能函数,这里都可以调用*/println("=" * 50)//删除元素//ListBuffer(11, 22, 33, 11, 55, 22, 33, 66, 33)listBuffer1.-=(33) //从左向右找元素,只会删除第一次找到的println(listBuffer1)println("=" * 50)//批量添加元素listBuffer1.+=(100,220,300,400)println(listBuffer1)println("=" * 50)val list1: List[Int] = List(99, 88, 77)listBuffer1.++=(list1)println(listBuffer1)/*** 可变的Set集合*/val hashSet1: mutable.HashSet[Int] = new mutable.HashSet[Int]()val set1: hashSet1.type = hashSet1.+=(1, 2, 3, 4, 5, 7, 1, 2, 3, 1, 6, 5)println(set1)}
}
元组
/*** 大小,值是固定的,根据创建的类来定,每个元素的数据类型可以是不一样,最高可以创建存储22个元素的元组*/
object Demo17Tuple {def main(args: Array[String]): Unit = {// 有几个数值就是几元组val t1: (Int, String, String, Int, String) = Tuple5(1001, "张三", "男", 17, "学习")println("=" * 50)val s2: Student1 = new Student1(1002, "李四", 18, "看剧")val t2: (Int, Student1) = Tuple2(1002, s2)println(t2._2.name)}
}case class Student1(id: Int, name: String, age: Int, like: String)
Map集合
object Demo18Map {def main(args: Array[String]): Unit = {//创建Map集合//键是唯一的,键一样的时候,值会被覆盖val map1: Map[Int, String] = Map((1001, "张三"), (1002, "李四"), (1003, "王五"), (1001, "赵六"), 1005 -> "易政")println(map1)println("=" * 50)//可以根据键获取值// println(map1(1006)) // 小括号获取值,键不存在报错// println(map1.get(1006)) // get函数获取,键不存在,返回Noneprintln(map1.getOrElse(1006, 0)) //根据键获取值,若键不存在,返回提供的默认值,默认值的类型可以是任意数据类型println("=" * 50)val keys: Iterable[Int] = map1.keys // 获取所有的键,组成一个迭代器for (e <- keys) {println(e)}println("=" * 50)val values: Iterable[String] = map1.values // 获取所有的值,组成一个迭代器for (e <- values) {println(e)}println("=" * 50)//遍历Map集合第一种方式,先获取所有的键,根据键获取每个值val keys2: Iterable[Int] = map1.keys // 获取所有的键,组成一个迭代器for (e <- keys2) {val v: Any = map1.getOrElse(e, 0)println(s"键:${e}, 值:${v}")}println("=" * 50)//遍历Map集合第二种方式,先获取所有的键,根据键获取每个值for (kv <- map1) { // 直接遍历map集合,得到每一个键值对组成的元组println(s"键:${kv._1}, 值:${kv._2}")}println("=" * 50)//遍历Map集合第三种方式,先获取所有的键,根据键获取每个值map1.foreach((kv: (Int, String)) => println(s"键:${kv._1}, 值:${kv._2}"))}
}
wordcount案例
import scala.io.{BufferedSource, Source}object Demo19WordCount {def main(args: Array[String]): Unit = {//1、读取数据文件,将每一行数据封装成集合的元素val lineList: List[String] = Source.fromFile("scala/data/words.txt").getLines().toListprintln(lineList)//2、将每一行数据按照|切分,并且进行扁平化val wordsList: List[String] = lineList.flatMap((line: String) => line.split("\\|"))println(wordsList)//3、根据元素进行分组val wordKV: Map[String, List[String]] = wordsList.groupBy((e: String) => e)println(wordKV)/*** List((world,8), (java,11),...)*/val wordCount: Map[String, Int] = wordKV.map((kv: (String, List[String])) => {val word: String = kv._1val count: Int = kv._2.size(word, count)})println("="*50)val resultList: List[(String, Int)] = wordCount.toListresultList.foreach(println)println("="*50)/*** 使用链式调用的方式简写*/Source.fromFile("scala/data/words.txt").getLines().toList.flatMap((line:String)=>line.split("\\|")).groupBy((e:String)=>e).map((kv: (String, List[String])) => {val word: String = kv._1val count: Int = kv._2.size(word, count)}).toList.foreach(println)println("=" * 50)/*** 使用链式调用的方式简写*/Source.fromFile("scala/data/words.txt").getLines().toList.flatMap(_.split("\\|")).groupBy((e:String)=>e).map((kv: (String, List[String])) => (kv._1, kv._2.size)).toList.foreach(println)}
}
JDBC
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}/*** jdbc的链接步骤* 1、注册驱动* 2、创建数据库链接对象* 3、创建数据操作对象* 4、执行sql语句* 5、如果第4步是查询的话,分析查询结果* 6、释放资源*/object Demo20JDBC {def main(args: Array[String]): Unit = {//1、注册驱动(若是8及其以后的版本需要mysql.cg.jdbc)Class.forName("com.mysql.jdbc.Driver")//2、创建数据库链接对象//jdbc:数据库名://host:port/数据库?xxx=xxx&xxx=xxxval conn: Connection = DriverManager.getConnection("jdbc:mysql://192.168.128.100:3306/studentdb?useUnicode=true&characterEncoding=UTF-8&useSSL=false", "root", "123456")//3、创建数据操作对象val preparedStatement: PreparedStatement = conn.prepareStatement("select student_id,cource_id,score from score where score > ?")//4、执行sql语句
// preparedStatement.setInt(1,23)// 传入参数防止sql注入preparedStatement.setInt(1, 60)val resultSet: ResultSet = preparedStatement.executeQuery()//5、如果第4步是查询的话,分析查询结果while (resultSet.next()){val student_id: String = resultSet.getString("student_id")val cource_id: String = resultSet.getString("cource_id")val score: Int = resultSet.getInt("score")println(s"学号:$student_id, 课程号:$cource_id, 分数:$score")}//6、释放资源conn.close()}
}
Json
import com.alibaba.fastjson.{JSON, JSONArray, JSONObject}import scala.io.Sourceobject Demo21Json {def main(args: Array[String]): Unit = {val lineList: List[String] = Source.fromFile("scala/data/stu.json").getLines().toListval jsonStr: String = lineList.mkString("\n")println(jsonStr)//使用fastjson包中的JSON类,将一个字符串转成json对象//转成json对象之后,可以通过键获取值
// parseObject 将整体转成一个json格式数据val jsonObj1: JSONObject = JSON.parseObject(jsonStr)println(jsonObj1)val s1: String = jsonObj1.getString("student_list")println(s1)//parseArray将一个"[{},{}]"变成一个元素是json对象的数组val jSONArray: JSONArray = JSON.parseArray(s1)var i = 0while (i < jSONArray.size()) {// getJSONObject(i): 获取数组中的第 i 个json对象val obj1: JSONObject = jSONArray.getJSONObject(i)val name: String = obj1.getString("name")val like: String = obj1.getString("like")println(s"${name}的爱好是${like}")i += 1}}}
Java与scala中的集合的相互转换
import java.utilobject Demo22Scala2Java {def main(args: Array[String]): Unit = {//创建一个java中的集合val array1: util.ArrayList[Int] = new util.ArrayList[Int]()array1.add(11)array1.add(22)array1.add(33)array1.add(66)array1.add(55)array1.add(44)println(array1)/*** 将java中的集合转成scala中的集合** java中的集合本来是没有转换scala的功能,需要导入隐式转换* scala中的导包,可以在任意地方**/import scala.collection.JavaConverters._val list1: List[Int] = array1.asScala.toListprintln(list1)/*** scala中的集合转java的集合*/val list2: util.List[Int] = list1.asJavaprintln(list2)}
}
Match
import java.util.Scanner
import scala.io.Sourceobject Demo23Match {def main(args: Array[String]): Unit = {/*** 模式匹配,就可以帮助我们开发的时候,减少代码量,让逻辑看起来更加清晰,以及可以避免一些异常* 语法:* 表达式 match {* case 值|[变量名:类型]|元组|数组|对象=>* 匹配成功执行的语句* case xxx=>* xxx* _ xxx=>* xxx* }** 模式匹配中,如果没有对应的匹配,那么就报错!!!*//*** 可以匹配变量值*/var i: Int = 100i match {case 20 => println("该值是20")case 50 => println("该值是50")// case 100=>println("该值是100")case _ => println("其他值")}/*** 匹配数据类型*/var flag1: Any = trueflag1 match {case _: Int => println("是Int类型")case _: Boolean => println("是boolean类型")}/*** 匹配元组* 元素的数量与类型都得一一对应*/val t1: (Int, String, Int) = Tuple3(1001, "张三", 18)t1 match {case (a1: Int, b1: String, c1: Int) =>println(s"学号:$a1, 姓名:$b1, 年龄:$c1")}/*** 匹配数组* 可以用来做数据封装*/val array: Array[Any] = Array(1001, "李四", "男", 18, "理科一班")array match {case Array(id: Int, name: String, gender: String, age: Int, clazz: String) =>println(s"学号:$id, 姓名:$name, 性别:$gender, 年龄:$age, 班级:$clazz")}/*** 模式匹配的应用1:避免异常**/val map1: Map[Int, String] = Map((1001, "张三"), (1002, "李四"))val res1: Option[String] = map1.get(1001) // Some("张三")println(res1.get) // 返回的数值为Option[String]类型
// val res1: Option[String] = map1.get(1003)
// println(res1.get)val sc: Scanner = new Scanner(System.in)println("请输入要查询的键:")val key: Int = sc.nextInt()map1.get(key) match {case Some(a: Any) => println(s"${key}键对应的值为$a")case None => println(s"${key}键不存在!")}println("=" * 50)/*** 模式匹配的应用2:简化代码**/val stuList: List[String] = Source.fromFile("scala/data/students.txt").getLines().toListval stuArrayList: List[Array[String]] = stuList.map((line: String) => line.split(","))stuArrayList.map((e:Array[String])=>{val id: String = e(0)val name: String = e(1)val age: String = e(2)val gender: String = e(3)val clazz: String = e(4)(id, name, gender, age, clazz)}).foreach(println)stuArrayList.map{case Array(id: String, name: String, gender: String, age: String, clazz: String)=>(id, name, gender, age, clazz)}.foreach(println)}
}
隐式转换
1、隐式转换函数
import scala.io.{BufferedSource, Source}
import scala.language.implicitConversionsobject Demo24implicit {def main(args: Array[String]): Unit = {/*** 隐式转换* 1、隐式转换函数* 2、隐式转换类* 3、隐式转换变量* 一个A类型将来会自动地转换成另一个B类型,类型可以是基本数据类型,也可以是引用数据类型** 显式转换*/
// var i:String = "100"
// //显式转换
// val res1: Int = i.toInt//定义一个函数def fun1(s: Int): Int = {return s + 1000}//调用函数println(fun1(100))println(fun1(200))
// println(fun1("300".toInt)) // 若已设置隐式转换函数,则会报错,因为不知道要不要交给隐式转换函数处理//需求:调用fun1函数,就只传字符串,不会报错//定义隐式转换函数//在需要返回值类型的功能的时候,自动地根据已有隐式转换函数将参数的类型转成返回值的类型implicit def implicitFun1(s: String): Int = {return Integer.parseInt(s)}// implicit def implicitFun2(s: String): Int = {
// return Integer.parseInt(s) + 2000
// }// def fun1(s: Int): Int = {
// return s + 1000
// }//调用函数println(fun1(100))println(fun1(200))
// println(fun1("300"))// 封装在一个Object类中进行导入import com.shujia.jichu.Demo11._
// val stuList: List[String] = "scala/data/students.txt".getLines().toList
// val scoreList: List[String] = "scala/data/scores.txt".getLines().toListprintln("1000" + 500) // 1000500 // 使用字符串自身的+拼接功能,做字符串拼接println("1000" - 500) // 500 // 字符串中没有-减法功能,自动使用隐式转换中的函数,将字符串转成数字做减法println("2000" - 500) // 1500 // 字符串中没有-减法功能,自动使用隐式转换中的函数,将字符串转成数字做减法}
}object Demo11 {implicit def implicitFun3(s: String): BufferedSource = Source.fromFile(s)implicit def implicitFun1(s: String): Int = Integer.parseInt(s)
}
2、隐式转换类
import scala.io.Source/*** 隐式转换类*/
object Demo25implicit {def main(args: Array[String]): Unit = {// val demo1 = new Demo12("scala/data/students.txt")
// val stuList: List[String] = demo1.show1()val stuList: List[String] = "scala/data/students.txt".show1()val scoreList: List[String] = "scala/data/score.txt".show1()//TODO 使用隐式转换类时不见其类名"张三".f()}//`implicit' modifier cannot be used for top-level objects//implicit class Demo12(path: String) {//implicit使用的地方,不能超过object作用域(将其放在object Demo25implicit中)// path: String :使用对应的上其参数类型的一个量即可调用类中的方法implicit class Demo12(path: String) {def show1(): List[String] = {Source.fromFile(path).getLines().toList}def f()={println(s"好好学习,天天向上!$path")}}}
3、隐式转换变量
import scala.io.{Codec, Source}/*** 隐式转换变量*/
object Demo26implicit {def main(args: Array[String]): Unit = {Source.fromFile("scala/data/students.txt")(Codec("GBK")).getLines().toList// def fun1(a1: Int)(a2: Int): Int = a1 + a2// val res1: Int=>Int = fun1(100)//定义一个隐式转换参数def fun1(a1: Int)(implicit a2: Int): Int = a1 + a2//定义一个隐式转换变量,若上述函数在被调用时没有传入第二个隐式转换参数时,则会使用下面设定的默认值implicit var i1: Int = 1000// 只传入一个参数值后调用fun1返回的为一个数值。若是没有定义隐式转换变量val res1: Int = fun1(100)println(res1)}
}
相关文章:
大数据学习之 scala基础(补充)
scala基础: hello world: 写scala可运行文件的注意事项1、如果一个scala文件要运行,class要改成object2、如果是class,就仅单纯代表一个类,如果是object代表的是单例对象3、scala语法中,一句话结束不需要加分号4、sca…...
正向传播和反向传播
正向传播(Forward Propagation) 正向传播是指将输入数据通过神经网络,计算出预测值的过程。具体步骤如下: 输入层:接受输入数据。隐藏层:每个隐藏层中的神经元接收上一层的输出,进行加权求和&…...
前端文件下载的方式
方式一:a标签直接下载 <a href"链接" >下载</a>一个文件链接(一般是服务器上的某个文件),这个链接一般地址栏输入是预览,不是附件下载 如果想改成附件下载,以下两种方式任选一个均…...
视图库对接系列(GA-T 1400)十六、视图库对接系列(本级)通知(订阅回调)
说明 之前我们实现了订阅接口,其中有一个receiveAddr参数, 这个就是对应的回调的地址。一般情况下对应的是同一个服务。 我们推荐使用http://xxx:xxx/VIID/SubscribeNotifications接口文档 SubscribeNotificationList对象对象如下: 文档中是xml,但实际上目前使用的都是jso…...
Python | Leetcode Python题解之第230题二叉搜索树中第K小的元素
题目: 题解: class AVL:"""平衡二叉搜索树(AVL树):允许重复值"""class Node:"""平衡二叉搜索树结点"""__slots__ ("val", "parent&quo…...
Python酷库之旅-第三方库Pandas(018)
目录 一、用法精讲 44、pandas.crosstab函数 44-1、语法 44-2、参数 44-3、功能 44-4、返回值 44-5、说明 44-6、用法 44-6-1、数据准备 44-6-2、代码示例 44-6-3、结果输出 45、pandas.cut函数 45-1、语法 45-2、参数 45-3、功能 45-4、返回值 45-5、说明 4…...
九科bit-Worker RPA 内容学习
入门阶段, 花时间学习和记忆细枝末节,可能会反而分散新手去理解核心逻辑的精力,并且不常用的知识也很容易被遗忘。 简介: 什么是RPA? RPA(Robotic Process Automation,机器人流程自动化&#x…...
vscode编译环境配置-golang
1. 支持跳转 如果单测函数上方不显示run test | debug test,需要安装Code Debugger(因为以前的go Test Explorer不再被维护了) 2. 单测 指定单个用例测试 go test -v run TestXXXdlv 调试 需要安装匹配的go版本和delve版本(如…...
【JavaEE】网络编程——UDP
🤡🤡🤡个人主页🤡🤡🤡 🤡🤡🤡JavaEE专栏🤡🤡🤡 文章目录 1.数据报套接字(UDP)1.1特点1.2编码1.2.1DatagramSocket1.2.2DatagramPacket…...
JAVA毕业设计147—基于Java+Springboot的手机维修管理系统(源代码+数据库)
基于JavaSpringboot的手机维修管理系统(源代码数据库)147 一、系统介绍 本项目分为用户、管理员、维修员三种角色 1、用户: 注册、登录、新闻公告、售后申请、申请列表、意见反馈、个人信息、密码修改 2、管理员: 用户管理、用户管理、栏目管理、网…...
力扣第228题“汇总区间”
在本篇文章中,我们将详细解读力扣第228题“汇总区间”。通过学习本篇文章,读者将掌握如何遍历和汇总区间,并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释,以便于理解。 问题描述 力扣第228题“汇总区间”描…...
部署大语言模型并对话
在阿里云的https://developer.aliyun.com/adc/scenario/b105013328814fe995c0f091d708d67d 选择函数计算 设置服务器配置 复制公网地址 这个地址不能直接 在返回应用,创建应用LLM 对话页面 Open WebUI 点击下面的创建应用 部署完成后访问域名 打开访问地址...
WebSocket、socket.io-client
WebSocket WebSocket 是一种网络通信协议,它提供了一个在单个长期持久的 TCP 连接上进行全双工(full-duplex)通信的通道。 WebSocket 允许客户端和服务器之间进行双向的数据交换,这意味着服务器可以主动向客户端推送数据&#x…...
Maven 仓库
在 Maven 世界中,任何一个依赖、插件或者项目构建的输出,都可以称为 构件 。 坐标和依赖是构件在 Maven 世界中的逻辑表示方式,构件的物理表示方式是文件,Maven 通过仓库来统一管理这些文件。 任何一个构件都有一组坐标唯一标识。…...
给后台写了一个优雅的自定义风格的数据日志上报页面
highlight: atelier-cave-dark 查看后台数据日志是非常常见的场景,经常看到后台的小伙伴从服务器日志复制一段json数据字符串,然后找一个JSON工具网页打开,在线JSON格式化校验。有的时候,一些业务需要展示mqtt或者socket的实时信息展示,如果不做任何修改直接展示一串字符…...
【React Native优质开源项目】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...
Android 自动更新时间的数字时钟 TextClock
TextClock 继承 TextView ,使用方法和 TextView 一样。 它专门用于显示数字时钟,可以自定义显示格式。 只要在布局文件里添加,它会自动更新时间,不需要添加刷新逻辑。 布局文件, <?xml version"1.0"…...
【Linux Git入门】Git的介绍
文章目录 前言git简介git是什么git的作用为什么要学习git安装git总结前言 在现代软件开发中,版本控制系统已经成为了不可或缺的工具。其中,Git是最受欢迎的版本控制系统之一。Git是由Linux的创造者Linus Torvalds在2005年创建的,用于管理Linux内核的开发。Git是一个分布式版…...
kafka面试题(基础-进阶-高阶)
目录 Kafka 基础篇 1.Kafka 的用途有哪些?使用场景如何? 2.Kafka 中的ISR、AR 又代表什么?ISR 的伸缩又指什么 3.Kafka 中的 HW、LEO、LSO、LW 等分别代表什么? 4.Kafka 中是怎么体现消息顺序性的? 5.Kafka 中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序…...
《系统架构设计师教程(第2版)》第11章-未来信息综合技术-07-大数据技术概述
文章目录 1. 大数据的定义2. 大数据的研究内容2.1 面临的问题2.2 面临的挑战2.3 分析步骤2.3.1 数据获取和记录2.3.2 信息抽取和清洗2.3.3 数据集成、聚集和表示2.3.4 查询处理、数据建模和分析2.3.5 解释 3.大数据的应用领域3.1 制造业的应用3.2 服务业的应用3.3 交通行业的应…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
