三十分钟入门基础Go(Java小子版)

news/2024/11/15 0:33:09/文章来源:https://www.cnblogs.com/Jcloud/p/18354965

前言

Go语言定义

Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态、强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC,结构形态及 CSP-style 并发计算

适用范围

本篇文章适用于学习过其他面向对象语言(Java、Php),但没有学过Go语言的初学者。文章主要从Go与Java功能上的对比来阐述Go语言的基础语法、面向对象编程、并发与错误四个方面。

一、基础语法

Go语言的基础语法与常规的编程语言基本类似,所不同的有声明变量的方式,数组、切片、字典的概念及功能与Java不太相同,不过Java中这些数据结构都可以通过类比功能的方式在Go中使用。

1.1 变量、常量、nil与零值、方法、包、可见性、指针

1.1.1 变量声明

Go语言中有两种方式

1.使用var关键字声明,且需要注意的是,与大多数强类型语言不同,Go语言的声明变量类型位于变量名称的后面。Go语句结束不需要分号。

var num int

var result string = "this is result"

2.使用:=赋值。

num := 3 等同于 var num int = 3

其中变量的类型会根据右侧的值进行匹配,例如"3"会匹配为int,"3.0"会匹配为float64,"result"会匹配为string。

1.1.2 常量声明

使用const来声明一个常量,一个常量在声明后不可改变。

const laugh string = "go"

1.1.3 nil与零值

只声明未赋值的变量,其值为nil。类似于java中的“null”

没有明确初始值的变量声明会被赋予它们的 零值

零值是:

  • 数值类型为 0

  • 布尔类型为 false

  • 字符串为 ""(空字符串)。

1.1.4 方法、包

Go中方法的定义

使用func关键字来定义一个方法,后面跟方法名,然后是参数,返回值(如果有的话,没有返回值则不写)。

func MethodName(p1 Parm, p2 Parm) int{}

//学习一个语言应该从Hello World开始!
package mainimport "fmt"func main() {fmt.Println("Hello World!")// Hello World!fmt.Println(add(3, 5)) //8var sum = add(3, 5)
}func add(a int, b int) int{return a+b;
}
多个返回值

Go 函数与其他编程语言一大不同之处在于支持多返回值,这在处理程序出错的时候非常有用。例如,如果上述 add 函数只支持非负整数相加,传入负数则会报错。

//返回值只定义了类型 没有定义返回参数
func add(a, b int) (int, error) {if a < 0 || b < 0 {err := errors.New("只支持非负整数相加")return 0, err}a *= 2b *= 3return a + b, nil
}//返回值还定义了参数 这样可以直接return 并且定义的参数可以直接使用 return时只会返回这两个参数
func add1(a, b int) (z int, err error) {if a < 0 || b < 0 {err := errors.New("只支持非负整数相加")return   //实际返回0 err 因为z只定义没有赋值 则nil值为0}a *= 2b *= 3z = a + breturn //返回 z err
}func main()  {x, y := -1, 2z, err := add(x, y)if err != nil {fmt.Println(err.Error())return}fmt.Printf("add(%d, %d) = %d\n", x, y, z)
}
变长参数
func myfunc(numbers ...int) {for _, number := range numbers {fmt.Println(number)}
}slice := []int{1, 2, 3, 4, 5}
//使用...将slice打碎传入
myfunc(slice...)
包与可见性

在 Go 语言中,无论是变量、函数还是类属性和成员方法,它们的可见性都是以包为维度的,而不是类似传统面向编程那样,类属性和成员方法的可见性封装在所属的类中,然后通过 privateprotectedpublic 这些关键字来修饰其可见性。

Go 语言没有提供这些关键字,不管是变量、函数,还是自定义类的属性和成员方法,它们的可见性都是根据其首字母的大小写来决定的,如果变量名、属性名、函数名或方法名首字母大写,就可以在包外直接访问这些变量、属性、函数和方法,否则只能在包内访问,因此 Go 语言类属性和成员方法的可见性都是包一级的,而不是类一级的。

假如说一个名为domain的文件夹下有3个.go文件,则三个文件中的package都应为domain,其中程序的入口main方法所在的文件,包为main

//定义了此文件属于 main 包
package main//通过import导入标注库中包
import "fmt"func main() {fmt.Println("Hello World!")// Hello World!fmt.Println(add(3, 5)) //8var sum = add(3, 5)
}func add(a int, b int) int{return a+b;
}

1.1.5 指针

对于学过C语言来说,指针还是比较熟悉的,我所理解的指针,其实就是一个在内存中实际的16进制的地址值,引用变量的值通过此地址去内存中取出对应的真实值。

func main() {i := 0//使用&来传入地址fmt.Println(&i) //0xc00000c054var a, b int = 3 ,4//传入 0xc00000a089 0xc00000a090fmt.Println(add(&a, &b)) 
}//使用*来声明一个指针类型的参数与使用指针
func add(a *int, b *int)int{//接收到 0xc00000a089 0xc00000a090//前往 0xc00000a089位置查找具体数据 并取赋给xx := *a//前往 0xc00000a090位置查找具体数据 并取赋给yy := *breturn x+y
}

1.2 条件、循环、分支

1.2.1 条件

与Java语言的if基本相同

// if
if condition { // do something 
}// if...else...
if condition { // do something 
} else {// do something 
}// if...else if...else...
if condition1 { // do something 
} else if condition2 {// do something else 
} else {// catch-all or default 
}

1.2.2 循环

sum := 0 //普通for循环
for i := 1; i <= 100; i++ { sum += i 
}//无限循环
for{sum++if sum = 100{break;}
}//带条件的循环
for res := sum+1; sum < 15{sum++res++
}//使用kv循环一个map或一个数组  k为索引或键值 v为值 k、v不需要时可以用_带替
for k, v := range a {fmt.Println(k, v)
}

1.2.3 分支

score := 100
switch score {
case 90, 100:fmt.Println("Grade: A")
case 80:fmt.Println("Grade: B")
case 70:fmt.Println("Grade: C")
case 65:fmt.Println("Grade: D")
default:fmt.Println("Grade: F")
}

1.3 数组、切片、字典

1.3.1 数组

数组功能与Java语言类似,都是长度不可变,并且可以使用多维数组,也可以通过arrays[i]来存储或获取值。

//声明
var nums [3]int 
//声明并初始化
var nums = [3]int{1,2,3} <==> nums:=[3]int{1,2,3}//使用
for sum := 0, i := 0;i<10{sum += nums[i]i++
}
//修改值
num[0] = -1

数组使用较为简单,但是存在着难以解决的问题:长度固定

例如当我们在程序中需要一个数据结构来存储获取到的所有用户,因为用户数量是会随着时间变化的,但是数组其长度却不可改变,所以数组并不适合存储长度会发生改变的数据。因此在Go语言中通过使用切片来解决以上问题。

1.3.2 切片

切片相比于Java来说是一种全新的概念。在Java中,对于不定长的数据存储结构,可以使用List接口来完成操作,例如有ArrayList与LinkList,这些接口可以实现数据的随时添加与获取,并没有对长度进行限制。但是在Go中不存在这样的接口,而是通过切片(Slice)来完成不定长的数据长度存储

切片与数组最大的不同就是切片不用声明长度。但是切片与数组并非毫无关系,数组可以看作是切片的底层数组,而切片则可以看作是数组某个连续片段的引用。切片可以只使用数组的一部分元素或者整个数组来创建,甚至可以创建一个比所基于的数组还要大的切片:

长度、容量

切片的长度就是它所包含的元素个数。

切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。

切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。

切片的长度从功能上类比与Java中List的size(),即通过len(slice)来感知切片的长度,即可对len(slice)进行循环,来动态控制切片内的具体内容。切片的容量在实际开发中运用不多,了解其概念即可。

创建切片
//声明一个数组
var nums =[3]int{1, 2, 3}
//0.直接声明
var slice =[]int{0, 1, 2}//1.从数组中引用切片 其中a:b是指包括a但不包括b
var slice1 = nums[0:2] //{1,2}
//如果不写的则默认为0(左边)或最大值(右边)
var slice2 = slice1[:2] <==> var slice2 = slice1[0:] <==>var slice2 = slice1[:]//2.使用make创建Slice 其中int为切片类型,4为其长度,5为容量
slice3 := make([]int, 5)
slice4 := make([]int, 4, 5)
动态操作切片
//使用append向切片中动态的添加元素
func append(s []T, vs ...T) []Tslice5 := make([]int, 4, 5) //{0, 0, 0, 0}
slice5 = append(slice5, 1) //{0,0,0,0,1}//删除第一个0
sliece5 = slice5[1:]
切片的常用场景

模拟上述提到的问题使用切片解决方案

//声明切片
var userIds = []int{}
//模拟获取所有用户ID
for i := 0; i< 100{userIds = append(userIdS, i);i++;
}
//对用户信息进行处理
for k,v := range userIds{userIds[k] = v++
}

1.3.3 字典

字典也可称为 ‘键值对’ 或 ‘key-value’,是一种常用的数据结构,Java中有各种Map接口,常用的有HashMap等。在Go中通过使用字典来实现键值对的存储,字典是无序的,所以不会根据添加顺序来保证数据的顺序。

字典的声明与初始化
//string为键类型,int为值类型
maps := map[string]int{"java" : 1,"go" : 2,"python" : 3,
}//还可以通过make来创建字典 100为其初始容量 超出可扩容
maps = make(map[string]int, 100)
字典的使用场景
//直接使用
fmt.Println(maps["java"]) //1//赋值
maps["go"] = 4//取值 同时判断map中是否存在该键 ok为bool型
value, ok := maps["one"] 
if ok { // 找到了// 处理找到的value 
}//删除
delete(testMap, "four")

二、面向对象编程

2.1 Go语言中的类

众所周知,在面向对象的语言中,一个类应该具有属性、构造方法、成员方法三种结构,Go语言也不例外。

2.1.1 类的声明与初始化

Go语言中并没有明确的类的概念,只有struct关键字可以从功能上类比为 面向对象语言中的“类” 。比如要定义一个学生类,可以这么做:

type Student struct {id intname stringmale boolscore float64
}//定义了一个学生类,属性有id name等,每个属性的类型都在其后面//定义学生类的构造方法
func NewStudent(id uint, name string, male bool, score float64) *Student {return &Student{id, name, male, score}
}//实例化一个类对象
student := NewStudent(1, "学院君", 100)
fmt.Println(student)

2.1.2 成员方法

Go中的成员方法声明与其他语言不大相同。以Student类为例,

//在方法名前,添加对应的类,即可认为改方法为该类的成员方法。
func (s Student) GetName() string  {return s.name
}//注意这里的Student是带了*的 这是因为在方法传值过程中 存在着值传递与引用传递 即指针的概念 当使用值传递时 编译器会为该参数创建一个副本传入 因此如果对副本进行修改其实是不生效的 因为在执行完此方法后该副本会被销毁 所以此处应该是用*Student 将要修改的对象指针传入 修改值才能起作用
func (s *Student) SetName(name string) {//这里其实是应该使用(*s).name = name,因为对于一个地址来说 其属性是没意义的 不过这样使用也是可以的 因为编译器会帮我们自动转换s.name = name
}

2.2 接口

接口在 Go 语言中有着至关重要的地位,如果说 goroutine 和 channel 是支撑起 Go 语言并发模型的基石,那么接口就是 Go 语言整个类型系统的基石。Go 语言的接口不单单只是接口,下面就让我们一步步来探索 Go 语言的接口特性。

2.2.1 传统侵入式接口实现

和类的实现相似,Go 语言的接口和其他语言中提供的接口概念完全不同。以 Java、PHP 为例,接口主要作为不同类之间的契约(Contract)存在,对契约的实现是强制的,体现在具体的细节上就是如果一个类实现了某个接口,就必须实现该接口声明的所有方法,这个叫「履行契约」:

// 声明一个'iTemplate'接口
interface iTemplate
{public function setVariable($name, $var);public function getHtml($template);
}// 实现接口
// 下面的写法是正确的
class Template implements iTemplate
{private $vars = array();public function setVariable($name, $var){$this->vars[$name] = $var;}public function getHtml($template){foreach($this->vars as $name => $value) {$template = str_replace('{' . $name . '}', $value, $template);}return $template;}
}

这个时候,如果有另外有一个接口 iTemplate2 声明了与 iTemplate 完全一样的接口方法,甚至名字也叫 iTemplate,只不过位于不同的命名空间下,编译器也会认为上面的类 Template 只实现了 iTemplate 而没有实现 iTemplate2 接口。

这在我们之前的认知中是理所当然的,无论是类与类之间的继承,还是类与接口之间的实现,在 Java、PHP 这种单继承语言中,存在着严格的层级关系,一个类只能直接继承自一个父类,一个类也只能实现指定的接口,如果没有显式声明继承自某个父类或者实现某个接口,那么这个类就与该父类或者该接口没有任何关系。

我们把这种接口称为侵入式接口,所谓「侵入式」指的是实现类必须明确声明自己实现了某个接口。这种实现方式虽然足够明确和简单明了,但也存在一些问题,尤其是在设计标准库的时候,因为标准库必然涉及到接口设计,接口的需求方是业务实现类,只有具体编写业务实现类的时候才知道需要定义哪些方法,而在此之前,标准库的接口就已经设计好了,我们要么按照约定好的接口进行实现,如果没有合适的接口需要自己去设计,这里的问题就是接口的设计和业务的实现是分离的,接口的设计者并不能总是预判到业务方要实现哪些功能,这就造成了设计与实现的脱节。

接口的过分设计会导致某些声明的方法实现类完全不需要,如果设计的太简单又会导致无法满足业务的需求,这确实是一个问题,而且脱离了用户使用场景讨论这些并没有意义,以 PHP 自带的 SessionHandlerInterface 接口为例,该接口声明的接口方法如下:

SessionHandlerInterface {/* 方法 */abstract public close ( void ) : boolabstract public destroy ( string $session_id ) : boolabstract public gc ( int $maxlifetime ) : intabstract public open ( string $save_path , string $session_name ) : boolabstract public read ( string $session_id ) : stringabstract public write ( string $session_id , string $session_data ) : bool
}

用户自定义的 Session 管理器需要实现该接口,也就是要实现该接口声明的所有方法,但是实际在做业务开发的时候,某些方法其实并不需要实现,比如如果我们基于 Redis 或 Memcached 作为 Session 存储器的话,它们自身就包含了过期回收机制,所以 gc 方法根本不需要实现,又比如 close 方法对于大部分驱动来说,也是没有什么意义的。

正是因为这种不合理的设计,所以在编写 PHP 类库中的每个接口时都需要纠结以下两个问题(Java 也类似):

  1. 一个接口需要声明哪些接口方法?

  2. 如果多个类实现了相同的接口方法,应该如何设计接口?比如上面这个 SessionHandlerInterface,有没有必要拆分成多个更细分的接口,以适应不同实现类的需要?

接下我们来看看 Go 语言的接口是如何避免这些问题的。

2.2.2 Go 语言的接口实现

在 Go 语言中,类对接口的实现和子类对父类的继承一样,并没有提供类似 implement 这种关键字显式声明该类实现了哪个接口,一个类只要实现了某个接口要求的所有方法,我们就说这个类实现了该接口

例如,我们定义了一个 File 类,并实现了 Read()Write()Seek()Close() 四个方法:

type File struct { // ...
}func (f *File) Read(buf []byte) (n int, err error) 
func (f *File) Write(buf []byte) (n int, err error) 
func (f *File) Seek(off int64, whence int) (pos int64, err error) 
func (f *File) Close() error

假设我们有如下接口(Go 语言通过关键字 interface 来声明接口,以示和结构体类型的区别,花括号内包含的是待实现的方法集合):

type IFile interface { Read(buf []byte) (n int, err error) Write(buf []byte) (n int, err error) Seek(off int64, whence int) (pos int64, err error) Close() error 
}type IReader interface { Read(buf []byte) (n int, err error) 
}type IWriter interface { Write(buf []byte) (n int, err error) 
}type ICloser interface { Close() error 
}

尽管 File 类并没有显式实现这些接口,甚至根本不知道这些接口的存在,但是我们说 File 类实现了这些接口,因为 File 类实现了上述所有接口声明的方法。当一个类的成员方法集合包含了某个接口声明的所有方法,换句话说,如果一个接口的方法集合是某个类成员方法集合的子集,我们就认为该类实现了这个接口。

与 Java、PHP 相对,我们把 Go 语言的这种接口称作非侵入式接口,因为类与接口的实现关系不是通过显式声明,而是系统根据两者的方法集合进行判断。这样做有两个好处:

  • 其一,Go 语言的标准库不需要绘制类库的继承/实现树图,在 Go 语言中,类的继承树并无意义,你只需要知道这个类实现了哪些方法,每个方法是干什么的就足够了。

  • 其二,定义接口的时候,只需要关心自己应该提供哪些方法即可,不用再纠结接口需要拆得多细才合理,也不需要为了实现某个接口而引入接口所在的包,接口由使用方按需定义,不用事先设计,也不用考虑之前是否有其他模块定义过类似接口。

这样一来,就完美地避免了传统面向对象编程中的接口设计问题。

三、并发与多线程

3.1 Goroutine

对于任何一个优秀的语言来说,并发处理的能力都是决定其优劣的关键。在Go语言中,通过Goroutine来实现并发的处理。

func say(s string) {fmt.Println(s)
}func main() {//通过 go 关键字新开一个协程go say("world")say("hello")
}

Go语言中没有像Java那么多的锁来限制资源同时访问,只提供了Mutex来进行同步操作。

//给类SafeCounter添加锁
type SafeCounter struct {v   map[string]intmux sync.Mutex
}// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {//给该对象上锁c.mux.Lock()// Lock 之后同一时刻只有一个 goroutine 能访问 c.vc.v[key]++//解锁c.mux.Unlock()
}

3.2 Channel

多协程之间通过Channel进行通信,从功能上可以类比为Java的volatile关键字。

ch := make(chan int) 声明一个int型的Channel,两个协程之间可以通过ch进行int数据通信。

通过Channel进行数据传输。

ch <- v    // 将 v 发送至信道 ch。
v := <-ch  // 从 ch 接收值并赋予 v。
package mainimport "fmt"func sum(s []int, c chan int) {sum := 0for _, v := range s {sum += v}c <- sum // 将和送入 c
}//对于main方法来说 相当于就是开启了一个协程
func main() {s := []int{7, 2, 8, -9, 4, 0}c := make(chan int)//通过go关键字开启两个协程 将chaneel当做参数传入go sum(s[:len(s)/2], c)go sum(s[len(s)/2:], c)//通过箭头方向获取或传入信息x, y := <-c, <-c // 从 c 中接收fmt.Println(x, y, x+y)
}

四、错误处理

4.1 error

Go 语言错误处理机制非常简单明了,不需要学习了解复杂的概念、函数和类型,Go 语言为错误处理定义了一个标准模式,即 error 接口,该接口的定义非常简单:

type error interface { Error() string 
}

其中只声明了一个 Error() 方法,用于返回字符串类型的错误消息。对于大多数函数或类方法,如果要返回错误,基本都可以定义成如下模式 —— 将错误类型作为第二个参数返回:

func Foo(param int) (n int, err error) { // ...
}

然后在调用返回错误信息的函数/方法时,按照如下「卫述语句」模板编写处理代码即可:

n, err := Foo(0)if err != nil { // 错误处理 
} else{// 使用返回值 n 
}

非常简洁优雅。

4.2 defer

defer用于确保一个方法执行完成之后,无论执行结果是否成功,都要执行defer中的语句。类似于Java中的try..catch..finally用法。例如在文件处理中,无论结果是否成功,都要关闭文件流。

func ReadFile(filename string) ([]byte, error) {f, err := os.Open(filename)if err != nil {return nil, err}//无论结果如何 都要关闭文件流defer f.Close()var n int64 = bytes.MinReadif fi, err := f.Stat(); err == nil {if size := fi.Size() + bytes.MinRead; size > n {n = size}}return readAll(f, n)
}

4.3 panic

Go语言中没有太多的异常类,不像Java一样有Error、Exception等错误类型,当然也没有try..catch语句。

Panic(恐慌),意味在程序运行中出现了错误,如果该错误未被捕获的话,就会造成系统崩溃退出。例如一个简单的panic:a := 1/0

就会引发panic: integer divide by zero。

image.png

其中第一行表示出问题的协程,第二行是问题代码所在的包和函数,第三行是问题代码的具体位置,最后一行则是程序的退出状态,通过这些信息,可以帮助你快速定位问题并予以解决。

4.4 recover

当有可以预见的错误时,又不希望程序崩溃退出,可以使用recover()语句来捕获未处理的panic。recover应当放在defer语句中,且该语句应该在方法中前部,避免未能执行到defer语句时就引发了系统异常退出。

package mainimport ("fmt"
)func divide() {//通过defer,确保该方法只要执行完毕都要执行该匿名方法defer func() {//进行异常捕获if err := recover(); err != nil {fmt.Printf("Runtime panic caught: %v\n", err)}}()var i = 1var j = 0k := i / jfmt.Printf("%d / %d = %d\n", i, j, k)
}func main() {divide()fmt.Println("divide 方法调用完毕,回到 main 函数")
}

image.png

可以看到,虽然会出现异常,但我们使用recover()捕获之后,就不会出现系统崩溃退出的情形,而只是将该方法结束。其中fmt.Printf("%d / %d = %d\n", i, j, k)语句并没有执行到,因为代码执行到他的上一步已经出现异常导致该方法提前结束。
4 recover

当有可以预见的错误时,又不希望程序崩溃退出,可以使用recover()语句来捕获未处理的panic。recover应当放在defer语句中,且该语句应该在方法中前部,避免未能执行到defer语句时就引发了系统异常退出。

package mainimport ("fmt"
)func divide() {//通过defer,确保该方法只要执行完毕都要执行该匿名方法defer func() {//进行异常捕获if err := recover(); err != nil {fmt.Printf("Runtime panic caught: %v\n", err)}}()var i = 1var j = 0k := i / jfmt.Printf("%d / %d = %d\n", i, j, k)
}func main() {divide()fmt.Println("divide 方法调用完毕,回到 main 函数")
}

可以看到,虽然会出现异常,但我们使用recover()捕获之后,就不会出现系统崩溃退出的情形,而只是将该方法结束。其中fmt.Printf("%d / %d = %d\n", i, j, k)语句并没有执行到,因为代码执行到他的上一步已经出现异常导致该方法提前结束。

五、总结

通过以上的学习,大家可以以使用为目的的初步了解到go的基础语法,但是仅凭本文想要学明白go是完全不够的。例如go的最大优势之一“协程”,由于文章目的就没有特别详细展开,有兴趣的同学可以继续学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/781772.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(二) 树莓派CM4调试

1. 参考资料资料汇总页面https://shumeipai.nxez.com/raspberry-pi-datasheets《bcm2711-peripherals.pdf》,下载地址https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf《cm4io-datasheet.pdf》,下载地址https://datasheets.raspberrypi.com/cm4/cm4-data…

中国式报表有这么多种类型,你都知道吗?

中国式报表是一种在中国企业中使用的会计报告格式,但你真的了解它吗?你知道它有多少种类型吗?今天我们就一起来聊聊,中国式报表都包含哪些类型的报表吧!按样式来划分,中国式报表通常分为以下几类:1. 行式报表 行式报表也就是我们常说的清单式明细表,是最常见也是最简单…

JDBC加载MySQL驱动【底层实现】

JDBC4.0如何加载引入依赖<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version></dependency>上代码import java.sql.Connection;import java.sql.DriverManager;imp…

tensorboard_logger库无法导入的问题解决

一、问题描述最近在学习深度学习时,从大神们那里copy的代码中有用到tensorboard_logger这个库的东西,所以很自然地就用conda install或者pip去安装它,但是结果是:python开源库里面没有这东西。 这就让我很苦恼,所以只能自己动手,丰衣足食了。 二、解决方法首先找到tenso…

sqlserver 2000 数据库文件*。mdf附加到sql2008 报错 提示使用dbcc checkcatalog检查

sqlserver 2000 数据库文件*。mdf附加到sql2008 报错 提示使用dbcc checkcatalog检查检查提示有两个存储过程对象 在 SYSOBJECTS 与 SYSCOMMENTS 之间不匹配。drop procedure TS_G_ArApIniModiy drop procedure TS_T_CRMContactionQry 删除这两个存储过程之后可以直接在sqlser…

广东盈致MES系统——注塑和冲压行业的智能化管理

广东注塑冲压行业MES制造执行系统是一种专门为注塑和冲压行业设计的生产管理系统,可以帮助企业实现生产过程的智能化管理和优化。盈致MES系统是一种常见的MES系统,具有以下特点和功能: 生产计划和调度:MES系统可以帮助企业进行生产计划和任务调度,根据订单需求和资源情况进…

电子取证

内存取证、磁盘取证、服务器取证电子取证tool DiskGenius、FTK、Rstudio,各种都挂一手文件位置 windows ntds.dit: C:\Windows\NTDS\NTDS.dit system: C:\Windows\System32\config\SYSTEM sam: C:\Windows\System32\config\SAMpowershell命令记录:\AppData\Roaming\Mi…

Python deepface:让你的代码轻松地实现人脸识别功能

楔子 在 GitHub 上面发现了一个非常有趣的库,叫 deepface,简直是人间宝藏。这个库主要是做人脸识别和面部属性分析的,它集合了目前全球最顶尖的开源人脸识别算法,使用卷积神经网络(CNN)对图像进行特征提取,以实现高精度的人脸识别。实验表明,人类在面部识别任务上的准确…

翔云PHP身份证识别接口集成示例-护照识别-港澳台通行证识别

证件识别接口简介: 证件识别接口一般是指针对各类证件进行识别,其中包含但不限于身份证识别、护照识别、港澳台通行证识别、户口页识别、驾驶证识别、行驶证识别、台湾健保卡等,其​多应用于需要进行实名认证与证件信息登记的场景。 证件身份证识别接口返回结果示例如下:证…

雅思考试官方指南(第2版)配套资源 All In One

雅思考试官方指南(第2版)配套资源 All In One 听力 (音频) / 口语 (视频) International English Language Testing System (IELTS)雅思考试官方指南(第2版)配套资源 All In One听力 (音频) / 口语 (视频)https://mp.pxwhqr.cn/book.htm?id=236649&sign=1a6d5b&au…

Vue + ElementUI表格内实现图片点击放大效果的两种方式

方式一:使用el-popover弹出框 trigger属性用于设置何时触发Popover(弹出框)属性值有:hover、click、focus 和 manual style="cursor:pointer":当鼠标放上去时让img标签出现小手状态<el-table-column label="物品图片" header-align="center&quo…

2024纷享销客逆势再获7500万美元投资

8月12日,纷享销客宣布完成新一轮7500万美元的战略投资,由钟鼎资本领投,启明创投与建发新兴投资跟投,这是继2023年7月在IPO资本协助下获贵阳创投3000万美元投资后,纷享销客在一年间再获投资,这也是其连续第四年获得新的投资。 本轮融资后,纷享销客将一如既往地持续完善产…