项目未来可能需要使用kotlin开发,所以特此记录一下学习笔记,仅供参考,方便后期查询。已同步到GitHub上:KotlinTest

Kotlin 简介

kotlin 的目标是成为一门全栈语言,主要有以下的特点:

  • 已经成为Android的官方推荐语言
  • 百分百的和java兼容,两者可以相互转换
  • JSJVMNative多平台开发

数据类型

1. 基本类型

Boolean true/false
Double 64
Float  32
Long   64
Int    32
Short  32
Byte   8val aChar = '0'
val bChar = '我'
val cChar = '\u000f'
复制代码

Char类型的转义字符

\t          制表符
\b          光标后退一个字符
\n          回车
\r          光标回到行首
\'          单引号
\"          双引号
\\          反斜杠
\$          美元符号,Kotlin 支持美元符号开头的字符串模板
复制代码

2. 基本类型的转换

不可隐式转换

val anInt: Int = 5
val aLong: Long = anInt.toLong()
复制代码

必须得通过.to类型的方式进行数据的转换

字符串

  • 一串Char

  • 用双引号""引起来

    val aString: String = "Hello World!"

  • 字符串比较

    a == b 表示比较内容 类似 Java 中的 equals a === b 表示比较对象是否相同

  • 字符串模板

    println("hello, $name") -> "hello, 小明"

3. Koltin 中的类和对象初始化

类的定义

  • 类,一个抽象的概念
  • 具有某些特征的事物的概括
  • 不特定指代任何一个具体的事物

一般写法:

/**
* 其中类参数如果加上 var 修饰,那么他便是成员变量,反之则是普通的参数
*/
class Student(var name: String, var age: Int){init {// ... 相当于构造函数中的代码}
}
复制代码

对象

  • 是一个具体的概念,与类相对

  • 描述某一个类的具体个体

  • 举例:

    某些人、领导的车等等

类和对象的关系

  • 一个类通常可以有很多歌具体的对象
  • 一个对象本质上只能从属一个类
  • 某一个人,他是工程师,但本质上还是属于人这一类

一般写法:

val student: Student = Student("xiaweizi", 23)
复制代码

类的继承

  • 提取多个类的共性得到一个更为抽象的类,即父类
  • 子类拥有父类的一切特征
  • 子类也可以定义自己的特征
  • 所有的类最终继承自Any,类似于java中的Object

4. 空类型和智能转换

空类型

// 定义
val notNull: String = null // 错误,不可能为空
val nullanle: String? = null // 正确,可以为空
// 使用
notNull.length // 正确,不可能为空所以可以直接使用
nullable.length // 有可能为空,不能直接获取长度
// 要想获取长度,可以通过以下两者方式
nullable!!.length // 正确,强制认定 nullable 不可能为空,如果为空则会抛出空指针异常
nullable?.length // 正确,若 nullable 为空,则返回 null
复制代码

智能类型转换

val child: Child = parent as Child // 类似于 Java 的类型转换,失败则抛出异常
val child: Child = parent as? Child // 如果转换失败,返回 null
复制代码

编译器智能识别转换:

val parent: Parent = Child()
if (parent is Child) {// parent 直接调用子类方法,不需要再进行强制转换
}val string: String = null
if (string != null) {// string.length 可以直接调用length 方法
}
复制代码

5. 区间

一个数学上的概念,表示范围, ClosedRange的子类,IntRange最常用

基本用法:

0..100 --> [0, 100]
0 until 100 --> [0, 100)
i in 0..100 表示 i 是否在区间[0, 100]中
复制代码

6. 数组

基本写法:

val ints: IntArray = IntArrayOf(1,2,3,5)
var charArray: CharArray = charArrayOf('a', 'b', 'c', 'd', 'e')
var stringArray: Array<String> = arrayOf("aa", "bb", "cc", "dd", "e")
复制代码

基本操作:

print(charArray[index])
ints[0] = 2
ints.length
cahrArray.joinToString("") // 讲 char 数组转换成字符串
stringArray.slice(1..4) // 取出区间里的值
复制代码

程序结构

1. 常亮和变量

常量

val a = 2
类似 Java 中的 final
不可被重复赋值
运行时常量:val x = getX()
编译期常量:const val x = 2
复制代码

变量

var a = 2
a = 3 // 可以被再次赋值
复制代码

类型推导

val string = "Hello" // 推导出 String 类型
val int = 5 // 推导出 Int 类型
var x = getString() + 5 // String 类型
复制代码

2. 函数 Function

以特定功能组织起来的代码块

// 最简单的打印信息,无返回的方法
fun printMessage(message: String):Unit{println("$message")
}
// 拥有返回值得方法
fun sum(first: Int, second: Int):Int {return first + second
}
// 可以简化成:
fun sum(first: Int, second: Int) = first + second
// 或者更简单的匿名函数
val result = fun(first: Int, second: Int) = first + second
复制代码

3. Lambda 表达式

其实又是匿名函数

一般形式

{传入参数 -> 函数体,最后一行是返回值}
// 例如
val sum = {first: Int, second: Int -> first + second}
val printMessage = {message: String -> println(message)}
复制代码

类型标识

() -> Unit // 无参,返回值为 null
(Int) -> Int // 传入整型,返回一个整型
(String, (String) -> String) -> Boolean // 传入字符串、Lambda 表达式,返回Boolean
复制代码

Lambda 表达式的简化

  • 函数参数调用时最后一个Lambda可以移出去
  • 函数参数只有一个Lambda,调用时小括号可以省略
  • Lambda只有一个参数可默认为it
  • 入参、返回值与形参一致的函数可以用函数引用方式作为实参传入

4. 成员变量和成员方法

成员变量的声明

// 第一种是在构造函数中声明
class Student(var age: Int, name: String){// age 是成员变量 name 是局部变量
}
// 第二种是在函数体内声明
var a = 0get() {field += 1return field}set(value) {println("set)field = value + 1}
// 可以进行对 get 和 set 方法的重新定义// 属性的初始化尽量在构造方法中完成
// var 用 lateinit 延迟初始化, val 用 lazylateinit var sex: String
val person: Person by lazy {Person()
}
复制代码

成员方法

在类中直接声明方法可以直接调用,包括lambda表达式

// 方法的声明
fun sum(a: Int, b: Int) = a + b
val sum1 = {a: Int, b: Int -> a + b}
// 方法的调用
println(person.sum(1,2))
println(person.sum1(3,5))
复制代码

5. 运算符

java中运算符是不能重新定义重载的,只能按照原先的逻辑进行计算

Kotlin则可以重新定义运算符,使用operator关键字,举了例子:

// 定义一个用于计算复数的类
class Complex(var real: Double, var imaginary: Double) {operator fun plus(other: Complex): Complex{return Complex(real+other.real, imaginary+other.imaginary)}// 重新 toString 方法overrride fun toString(): String {return "$real + ${imaginary}i"}
}
// 使用
val complex1 = Complex(1, 2)
val complex2 = Complex(2, 3)
println(complex1 + complex2)
// 输出结果为
"3 + 5i"
复制代码

关键就是这个方法,方法名必须是plus或者其他官方定义的运算符,参数有且仅有一个,类型自定义,返回值意识可以自定义的.

operator fun plus(other: Complex): Complex{return Complex(real+other.real, imaginary+other.imaginary)
}
复制代码

6. 表达式

中缀表达式

通过infix关键字修复方法,那么就可以不用通过 对象.方法() 的方式调用,而是直接 对象 方法名 参数的方式调用。举了例子

class Student(var age: Int){infix fun big(student: Student): Boolean {return age > student.age}
}
// 如果没有 infix 的调用方式:
println(Student(23).big(Student)(12))
// 如果使用 infix 修饰的调用方式:
println(Student(23) big Student(12))
复制代码

if表达式

直接来个例子

val a = 20
val b = 30
val flag: Int = if(a > b) a else b
复制代码

When 表达式

加强版的 switch,支持任意类型, 支持纯粹表达式条件分支(类似if),举个栗子:

val a = 5
when(a) {is Int -> println("$a is Int")in 1..6 -> println("$a is in 1..6")!in 1..4 -> println("$a is not in 1..4")else -> {println("null")}
}
复制代码

for循环

基本写法

for (element in elements)
复制代码

while循环

基本写法

while() {
}
do {
} while()
复制代码

跳过和终止循环

跳过当前循环用 continue
终止循环用 break
复制代码

6. 异常捕获

同样也是表达式,可以用来赋值,举个例子

return try{x/y}catch(e: Exception) {0} finally {//...}
复制代码

如果没有异常则返回x/y,否则返回0,finally中的代码无论如何还是要执行的。

7. 具名参数、变长参数和默认参数

**具名参数:**给函数的实参附上形参

fun sum(first: Int, second: Int) = first + second
sum(second = 2, first = 1)
复制代码

**变长参数:**用varary修饰,使用起来是和数组一样,某个参数可以接收多个值,可以不作为最后一个参数,如果传参时有歧义,需要使用具名参数。

fun hello(vararg ints: Int, string: String) = ints.forEach(println(it))
hello(1,3,4,5,string = "hello")
// 如果最后一个参数也是 Int
fun hello(varary ints: Int, anInt: Int)
// 创建数组
val arrayInt: IntArray = intArrayOf(1, 2, 3, 4)
hello(ints = *arrayInt, anInt = 2)
复制代码

**默认参数:**就是给参数传入一个默认的值

fun hello(anInt: Int = 1, string: String)
hello(string = "aaa")
复制代码

面向对象

1. 继承

继承语法要点:

  • 父类需要open才可以被继承
  • 父类方法、属性需要open才可以被覆写
  • 接口、接口方法、抽象类默认为open
  • 覆写父类(接口)成员需要override关键字

语法要点:

  • class A: B(), C, D
  • 继承类时实际上调用了父类的构造方法
  • 类只能单继承,接口可以多实现

接口代理:

一个类可以直接将自己的任务委托给接口的方法实现,举个例子:

interface Drive{fun drive()
}interface Sing{fun sing()
}class CarDrive: Drive{override fun drive() {println("我会开车呦")}
}class LoveSing: Sing{override fun sing() {println("我会唱歌呦")}
}class Manager(drive: Drive, sing: Sing): Drive by drive, Sing by singfun main(args: Array<String>) {val carDrive = CarDrive()val loveSing = LoveSing()val manager = Manager(carDrive, loveSing)manager.drive()manager.sing()
}
复制代码

这样,manager不用做任何事情,完全交付给接口实现.

接口方法冲突:

接口方法可以有默认实现,通过super<父类名>.方法名

interface A{fun a() = 0
}interface B{fun a() = 1
}interface C{fun a() = 2
}class D(var aInt: Int): A,B,C{override fun a(): Int {return when(aInt){in 1..10 ->{super<A>.a()}in 11..100 ->{super<B>.a()}else -> {println("dd")super<C>.a()}}}
}
复制代码

2. 类及成员的可见性

java类似,private、protected、public,其中internal代表的是模块内可见

3. Object

相当于Java中的单例模式,有以下特点

  • 只有一个实例的类

  • 不能自定义构造方法

  • 可以实现接口、继承父类

  • 本质上就是单例模式最基本的实现

      interface getDataSuccess{fun success()}abstract class getDataField{abstract fun failed()}object NetUtil: getDataField(), getDataSuccess{override fun success() {println("success")}override fun failed() {println("failed")}val state: Int = 0fun getData(): String = "请求成功"}
    复制代码

3. 伴生对象和静态成员

相当于java中的静态方法

  • 每个类可以对应一个伴生对象

  • 伴生对象的成员全局独一份

  • 如果java中想直接调用kotlin中的静态方法或者静态变量,可以考虑使用JvmField JvmStatic.

      open class Util private constructor(var anInt: Int) {companion object {@JvmStaticfun plus(first: Int, second: Int) = first + secondfun copy(util: Util) = Util(util.anInt)@JvmFieldval tag = "tag"}}
    复制代码

4. 方法的重载

通过给方法的参数配置默认值,即可实现方法的重载,按理说,一切可以拥有默认值的方法重载才是合理的方法重载。

名称形同、参数不同,跟返回值没有关系

class OverLoadTest {@JvmOverLoadsfun a(anInt: Int = 0, string: String="") = 1
}val test = OverLoadTest()
test.a(1, "")
test.a()
test.a(anInt = 2)
test.a(string = "")
复制代码

使用JvmOverLoads是为了方便Java中调用方法的重载.

5. 扩展方法

kotlin中的扩展方法,我认为相当于java中的代理模式,拿到被代理的对象,然后进行一系列的操作。

fun String.add(anInt: Int): String {var sb = StringBuilder()for (i in 0 until anInt) {sb.append(this)}return sb.toString()
}operator fun String.times(anInt: Int): String {var sb = StringBuilder()for (i in 0 until anInt) {sb.append(this)}return sb.toString()
}// 使用
var string = "xiaweizi"
println(string.add(5))
println(string * (3))
复制代码

6. 属性代理

类似之前说的var anInt: Int by lazy{2},懒赋值就是使用的属性代理,来看个例子:

fun main(args: Array<String>) {val a: Int by DelegatesTest()println(a)var b: Int by DelegatesTest()b = 3println(b)
}class DelegatesTest {private var anInt: Int? = nulloperator fun getValue(thisRef: Any?, property: KProperty<*>): Int {println("getValue")return anInt?:0}operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int): Unit {println("setValue")this.anInt = value}
}
复制代码

val 对应 getValuevar对应getValue和setValue方法,这个时候声明的属性就全权交付给DelegatesTest类中的anInt代理,当anInt为空的时候返回0,否则返回anInt.

7. JavaBean

使用data修饰类,类似java中的javaBean,默认实现了set get toString等方法,并拥有componentN方法.

不过有个缺点就是,无法被继承,没有无参构造函数,可以通过安装allOpennoArg插件解决这个问题.

data class UserBean(var name: String, var age: Int)val userBean: UserBean = UserBean("小芳", 23)
println(userBean.name)
println(userBean.toString())println(userBean.component1())
println(userBean.component2())val (name, age) = userBean
println("name: $name")
println("age: $age")
复制代码

至于这种写法val (name, age) = userBean,是因为定义了component1的运算符

class Complex{operator fun component1() = "你好呀"operator fun component2() = 2operator fun component3() = 'a'
}val complex = Complex()
val (a, b, c) = complex
println(a + b + c)
复制代码

使用起来也是很简单的

8. 内部类

  • 定义在类内部的类
  • 与类成员有相似的访问控制
  • 默认是静态内部类,非静态用 inner 关键字
  • this@Outter this@Inner 的用法
  • 匿名内部类
    • 没有定义名字的内部类
    • 类名编译时生成,类似Outter$1.class
    • 可继承父类,实现多个接口,与Java注意区别

举个例子:

class Outer{var string: String = "outer"class Inner1{var string: String = "inner1"fun sum(first: Int, second: Int) = first + second}inner class Inner2{var string: String = "inner2"fun cha(first: Int, second: Int) = first - secondfun getInnerField() = this.stringfun getOuterField() = this@Outer.string}
}fun main(args: Array<String>) {val inner1 = Outer.Inner1()val inner2 = Outer().Inner2()println(inner1.sum(1, 2))println(inner2.cha(2, 1))println(inner2.getInnerField())println(inner2.getOuterField())
}
复制代码

匿名内部类:

val listener: onClickListener = object : Father(), Mother, onClickListener{override fun sing() {println("mother sing")}override fun teach() {println("father teach")}override fun onClick() {println("匿名内部类")}
}
复制代码

使用Object实现匿名内部类

9. 枚举和密封类

枚举是对象可数,每个状态相当于每个对象,是可以传构造参数的

密封类时子类可数,在kotlin大于1.1子类只需要与密封类在同一个文件加,保护子类的位置

sealed class SealedClassTest{class sum(first: Int, seocnd: Int): SealedClassTest()class cha(first: Int, seocnd: Int): SealedClassTest()object Bean: SealedClassTest()
}enum class HttpStatus(val anInt: Int){SUCCESS(0), FAILED(1), LOADING(2)
}fun main(args: Array<String>) {val class1 = SealedClassTest.cha(1, 2)println(HttpStatus.SUCCESS)
}
复制代码

高阶函数

1. 基本概念

  • 传入或者返回函数的函数
  • 函数引用 ::println
  • 带有Receiver的引用 pdfPrinter::println

有三种显示

// 1. 包级函数
intArray.forEach(::print)// 2. 类.方法
intArray.forEach(Int::addOne)
fun Int.addOne(): Unit {println("addOne:$this")
}// 3. 对象.方法
intArray.forEach(AddTwo()::addTwo)
class AddTwo {fun addTwo(anInt: Int): Unit {println("addTwo:$anInt")}
}
复制代码

2. 常用的高阶函数

常用的高阶函数还是有很多的,会简单的使用例子即可:

// 遍历
fun forEachTest() {val strings: Array<String> = arrayOf("aa", "ee", "bb", "ll")strings.forEach { println(it) } // 遍历每一个值strings.forEachIndexed { index, s -> println("index:$index,String:$s") } // 遍历 下标和值一一对应}// 重新拷贝一个值
fun mapTest() {val strings: Array<String> = arrayOf("aa", "ee", "bb", "ll")var map = strings.map { "$it-test" }map.forEach { print("$it\t") }
}// 将集合合体
fun flatMapTest() {val lists = listOf(1..10,2..11,3..12)var flatMap = lists.flatMap {it.map {"No.$it"}}flatMap.forEach(::println)
}fun reduceTest() {val ints = listOf(2, 3, 4, 5)println(ints.reduce { acc, i ->acc + i})
}// 字符串连接
fun foldTest(){val ints = listOf(2, 3, 4, 5)println(ints.fold(StringBuffer(), { acc, i -> acc.append("$i,") }))println(ints.joinToString(","))
}fun filterTest() {val ints = listOf(1, 2, 3, 4, 5, 6)println(ints.filter { element -> element % 2 == 0 })
}// 当值不是奇数就去,遇到偶数就停止了
fun takeWhileTest() {val ints = listOf(1, 3, 3, 4, 5, 6)println(ints.takeWhile { it % 2 != 0 })
}fun letTest() {findPerson()?.let { (name, age) -> println("name:$name, age:$age") }findPerson()?.apply { println("name:$name, age:$age") }with(findPerson()!!) { println("name:$name, age:$age") }
}data class Person(val name: String, val age: Int)fun findPerson(): Person? {return Person("aa", 23)
}
复制代码

3. 复合函数

有点类似数据中的f(g(x))

fun main(args: Array<String>) {val add1 = {int: Int ->println("add1")int + 1}val add2 = {int : Int ->println("add2")int + 2}var add3 = add1 addThen (add2)println(add3(4))
}infix fun <P1, P2, R> Function1<P1, P2>.addThen(function: Function1<P2, R>): Function1<P1, R> {return fun(p: P1): R{return function.invoke(this.invoke(p))}
}
复制代码

4. Currying

简单来说就是多元函数变换成一元函数调用链式,举个简单的例子,这是优化之前:

fun log(tag: String, out: OutputStream, message: String){out.write("[$tag], $message".toByteArray())
}
复制代码

优化之后

fun log(tag: String)= fun(out: OutputStream)= fun(message: String)= out.write("[$tag], $message".toByteArray())
复制代码

5. 计算文件字符串个数的小例子

首先将字符串转换成字符串数组:

val map: HashMap<Char, Int> = HashMap()
var toCharArray = File("build.gradle").readText().toCharArray()
复制代码

通过分组的方式,统计每个字符串的个数,并打印:

toCharArray.groupBy { it }.map { it.key to  it.value.size }.forEach { println(it) }
复制代码

kotlinjava的混合开发

1. 基本的交互操作

属性读写

  • Kotlin自动识别 Java Getter/Setter
  • Java操作Kotlin属性通过Getter/Setter

空安全类型

  • Kotlin空安全类型的原理
  • 平台类型Platform Type
  • Java可以通过@Nullable、@NotNull

几类函数的调用

  • 包级函数:静态方法
  • 扩展方法:带Receiver的静态方法
  • 运算符重载:带Receiver的对应名称的静态方法

几个常用的注解

  • @JvmField:将属性编译为Java变量
  • @JvmStatic:将对象的方法编译成功Java静态方法
  • @JvmOverloads:默认参数生成重载方法
  • @JvmName:制定Kotlin文件编译后的类名

NoArg 和 AllOpen

  • NoArg为被标注的类生成无参构造
  • AllOpen为被标注的类去掉final,允许被继承

正则表达式

  • Raw字符串定义正则表达式
  • JavaPattern
  • KotlinRegex

举个例子:

val source = "Hello This my phone number: 010-12345678."
val pattern = """.*(\d{3}-\d{8}).*"""Regex(pattern).findAll(source).toList().flatMap(MatchResult::groupValues).forEach(::print)复制代码

Kotlin 自学笔记相关推荐

  1. 字节跳动大佬的Python自学笔记.pdf

    1. 字节跳动大佬的Python自学笔记 这是我的一个朋友自学资料包,通过这个资料包自学拿到了字节跳动的Offer, 下面是他之前入门学习Python时候的学习资料,非常全面,从Python基础.到w ...

  2. Kotlin 学习笔记(八)—— Kotlin类与对象之接口

    Kotlin 学习笔记(八)-- Kotlin类与对象之接口 Kotlin学习笔记系列教程 Kotlin 学习笔记(一)-- 概述.学习曲线.开发工具.参考资料 Kotlin 学习笔记(二)-- 基础 ...

  3. JAVA自学笔记07

    JAVA自学笔记07 1.构造方法 1) 例如:Student s = new Student();//构造方法 System.out.println(s);// Student@e5bbd6 2)功 ...

  4. MySQL自学笔记2--select的5个子句

    MySQL自学笔记 使用的MySQL自带命令客户端,其中具体的操作是在自建的数据库下room303表中进行的,表中的列有:id.name.age.email.tel.salary.riqi.class ...

  5. JAVA自学笔记24

    JAVA自学笔记24 1.能使用同步代码块就使用同步代码块,除非锁对象是this,就可以考虑使用同步方法.静态方法的锁是类的字节码对象. 2.JDK5新特性 1)接口Lock void Lock()/ ...

  6. 怎么用vc采集ni卡数据_SystemLink自学笔记(6):SystemLink架构和数据服务

    1. SystemLink架构和数据服务 1.1. 架构和特点 现在在对SystemLink的功能有了一个大概的了解后,可以进一步从它的整体架构学习这门新技术了.NI官网给出了白皮书,原文是英文资料, ...

  7. JAVA自学笔记22

    JAVA自学笔记22 1.操作基本数据类型的流 DataInputStream DataOutputStream 数据输出流允许应用程序以适当方式将基本的Java数据类型写入输出流中.然后,应用程序可 ...

  8. Kotlin学习笔记(3)- 语法

    系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...

  9. Kotlin学习笔记(2)- 空安全

    系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...

最新文章

  1. (C++)从字符串中取出整形、浮点型和字符串
  2. centos7.3网络实验环境(金个人)
  3. Rolling element bearing diagnostics using the Case Western Reserve University data-学习笔记
  4. Web框架——Flask系列之设置和读取cookie(十五)
  5. 动环监控系统接线图_机房动环监控系统报价
  6. 如何实现HashMap顺序存储
  7. ASP脚本语言之vbscript用法
  8. win2008r2 or centos6 硬盘挂载
  9. ES19-Java API全文搜索
  10. 办公室海王小姐姐悄悄问我如何在PC端登录多个微信小号?
  11. m_Orchestrate learning system---三十一、模板和需求的关系
  12. opecv-putText
  13. SpringBoot整合shiro的一个例子
  14. linux向苹果手机传文件,堪比 AirDrop,iPhone 与 Windows 电脑互传文件的三种方式
  15. 大使馆大师傅活动分工和非都市规划
  16. 100G QSFP28 AOC有源光缆的分类及应用
  17. nginx 区分手机浏览器和pc浏览器
  18. vue -【nextTick】-【过度与动画】-【插槽】-【配置代理服务器】-【vuex】-【路由】
  19. Java实现咖啡馆选餐系统
  20. JavaApi:IO基础

热门文章

  1. 微信小程序:添加到手机通讯录
  2. Build.VERSION_CODES.HONEYCOMB
  3. 充电计划 -- http协议
  4. 爬虫实践---今日头条街拍图片
  5. 机器学习笔记之指数族分布——最大熵原理与softmax激活函数的关系
  6. hadoop可能会遇到的问题
  7. VMware虚拟机黑群晖添多网卡教程
  8. MySQL在脚本中执行传参数_ZZW_shell脚本中的调用MYSQL传参及注意的问题
  9. 利用HOOKAPI拦截文件操作
  10. python用户登录a_Python入门案例-用户注册和登录