go语言八股文(三)
1.java和go的区别
1. 语言设计目标
- Java:
-
- 通用性:设计为一种通用的、面向对象的编程语言,适用于多种应用场景,如桌面应用、服务器端应用、移动应用等。
- 跨平台性:通过“一次编写,到处运行”(Write Once, Run Anywhere, WORA)的理念,通过 Java 虚拟机(JVM)实现跨平台运行。
- 安全性:提供了强大的内存管理和垃圾回收机制,避免了指针操作带来的安全隐患。
- Go:
-
- 系统编程:最初设计用于系统编程、网络编程和并发编程,特别适合构建高性能的服务器和分布式系统。
- 简洁性:语法简洁,注重代码的可读性和开发效率,减少不必要的复杂性。
- 并发支持:内置了强大的并发支持(如 goroutines 和 channels),使得并发编程变得简单高效。
2. 语法和语言特性
- Java:
-
- 面向对象:完全基于类和对象,支持继承、多态和接口等面向对象特性。
- 类型系统:静态类型语言,支持泛型编程。
- 异常处理:通过
try-catch-finally
机制处理异常。 - 代码结构:代码通常组织在类和包中,需要显式定义类和方法。
- Go:
-
- 结构体和接口:支持结构体(struct)和接口(interface),但不支持传统意义上的类继承。接口是隐式的,只要类型实现了接口中的所有方法,就自动满足该接口。
- 类型系统:静态类型语言,但语法更简洁,支持类型推断。
- 错误处理:通过返回错误值(
error
类型)来处理错误,而不是抛出异常。 - 代码结构:代码通常组织在包(package)中,语法简洁,减少了样板代码。
3. 并发模型
- Java:
-
- 线程模型:使用线程(
Thread
)和同步机制(如synchronized
、Lock
等)实现并发。 - 复杂性:并发编程相对复杂,需要手动管理线程的创建、同步和销毁。
- 工具库:提供了丰富的并发工具库,如
java.util.concurrent
包。
- 线程模型:使用线程(
- Go:
-
- Goroutines:内置的轻量级线程,启动成本极低,适合高并发场景。
- Channels:用于在 Goroutines 之间安全地传递数据,简化了并发编程。
- 简洁性:并发模型简单易用,通过
go
关键字启动 Goroutines,通过channel
实现通信。
4. 性能
- Java:
-
- 运行时性能:通过即时编译(JIT)优化,运行时性能接近原生代码,但启动时间可能较长。
- 内存占用:由于 JVM 的存在,内存占用相对较高。
- Go:
-
- 编译型语言:编译成原生代码,启动速度快,运行时性能接近 C/C++。
- 内存占用:内存占用相对较低,适合资源受限的环境。
5. 工具和生态系统
- Java:
-
- 工具丰富:拥有强大的开发工具(如 IntelliJ IDEA、Eclipse)和构建工具(如 Maven、Gradle)。
- 生态系统庞大:有大量的开源库和框架,如 Spring、Hibernate 等。
- Go:
-
- 工具简单高效:官方提供了
go
工具链,支持代码管理、构建、测试等功能。 - 生态系统增长迅速:虽然起步较晚,但近年来发展迅速,特别是在云原生、微服务等领域。
- 工具简单高效:官方提供了
6. 垃圾回收
- Java:
-
- 垃圾回收机制:通过垃圾回收器(GC)自动管理内存,用户无需手动释放内存。
- 复杂性:垃圾回收机制相对复杂,可能会影响程序的性能。
- Go:
-
- 垃圾回收机制:也有垃圾回收机制,但设计目标是尽量减少停顿时间。
- 性能优化:垃圾回收机制在设计上更注重低延迟和高吞吐量。
7. 社区和应用场景
- Java:
-
- 社区庞大:拥有庞大的开发者社区,适用于各种应用场景,如企业级应用、Android 开发等。
- Go:
-
- 社区活跃:社区相对年轻但非常活跃,特别适合构建高性能的服务器、微服务和云原生应用。
总结
- Java 适合需要跨平台、面向对象和复杂业务逻辑的应用场景,如企业级应用、Android 开发等。
- Go 适合需要高性能、并发支持和简洁语法的应用场景,如服务器端编程、微服务、云原生应用等。
2.go语言中defer的变量快照在什么情况下会生效
1. 变量在 defer 被注册时的值被捕获
当 defer 被注册时,它会捕获变量在那一刻的值。如果变量是值类型(如基本类型、结构体等),defer 会捕获该值的副本;如果变量是指针类型,defer 会捕获指针本身,而不是指针指向的值。
示例 1:值类型变量
func main() {x := 5defer fmt.Println(x) // 捕获 x 的当前值 5x = 10fmt.Println("x is now", x) // 输出 x is now 10
} // defer 的 fmt.Println(x) 在这里执行,输出 5
●在 defer 被注册时,x 的值是 5,defer 捕获了这个值。
●后续对 x 的修改(x = 10)不会影响 defer 捕获的值。
●最终,defer 调用 fmt.Println(x) 时输出的是 5,而不是 10。
示例 2:指针类型变量
Go运行代码
func main() {x := 5p := &xdefer fmt.Println(*p) // 捕获指针 p,而不是 p 指向的值x = 10fmt.Println("x is now", x) // 输出 x is now 10
} // defer 的 fmt.Println(*p) 在这里执行,输出 10
●在 defer 被注册时,捕获的是指针 p,而不是 p 指向的值。
●后续对 x 的修改(x = 10)会改变 p 指向的值。
●最终,defer 调用 fmt.Println(*p) 时输出的是 10,而不是 5。
2. 闭包中的变量捕获
如果 defer 调用的是一个闭包(匿名函数),闭包会捕获变量的当前值。捕获的值取决于闭包的定义方式。
示例 3:闭包捕获值类型变量
Go运行代码
func main() {x := 5defer func() {fmt.Println(x) // 捕获 x 的当前值 5}()x = 10fmt.Println("x is now", x) // 输出 x is now 10
} // defer 的闭包在这里执行,输出 5
●闭包捕获了变量 x 在 defer 被注册时的值 5。
●后续对 x 的修改不会影响闭包捕获的值。
●最终,闭包输出的是 5,而不是 10。
示例 4:闭包捕获指针类型变量
Go运行代码
func main() {x := 5p := &xdefer func() {fmt.Println(*p) // 捕获指针 p,而不是 p 指向的值}()x = 10fmt.Println("x is now", x) // 输出 x is now 10
} // defer 的闭包在这里执行,输出 10
●闭包捕获的是指针 p,而不是 p 指向的值。
●后续对 x 的修改会改变 p 指向的值。
●最终,闭包输出的是 10,而不是 5。
总结
defer 的变量快照在以下情况下生效:
1值类型变量:defer 捕获变量在 defer 被注册时的值的副本。后续对变量的修改不会影响 defer 捕获的值。
2指针类型变量:defer 捕获指针本身,而不是指针指向的值。后续对指针指向的值的修改会影响 defer 的输出。
3闭包中的变量捕获:闭包会捕获变量在 defer 被注册时的值,捕获的值取决于变量的类型(值类型或指针类型)。
自学go语言笔记,希望我们可以一起学习!