当前位置: 首页 > news >正文

Go语言精修(尚硅谷笔记)第十一章

十一、文件操作

image-20210118151400557

  • os .File封装所有文件相关操作,File是一个结构体

image-20210118151855390

11.1 文件打开与关闭

  • 使用的函数和方法

    func Open

    func Open(name string) (file *File, err error)
    

    Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。

    func (*File) Close

    func (f *File) Close() error
    

    Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。

  • 案例演示

package main
import (
	"fmt"
	"os" 
)
func main() {
	//打开文件
	//概念说明: file 的叫法
	//1. file 叫 file对象
	//2. file 叫 file指针
	//3. file 叫 file 文件句柄
	file , err := os.Open("d:/test.txt")
	if err != nil {
		fmt.Println("open file err=", err)
	}
	//输出下文件,看看文件是什么, 看出file 就是一个指针 *File
	fmt.Printf("file=%v", file)
	//关闭文件
	err = file.Close()
	if err != nil {
		fmt.Println("close file err=", err)
	}
}

11.2 读文件操作

1 ) 读取文件的内容并显示在终端(带缓冲区的方式),使用os.Open,file.Close,bufio.NewReader(), reader.ReadString 函数和方法.

package main
import (
	"fmt"
	"os"
	"bufio"
	"io" 
)
func main() {
	//打开文件
	//概念说明: file 的叫法
	//1. file 叫 file对象
	//2. file 叫 file指针
	//3. file 叫 file 文件句柄
	file , err := os.Open("d:/test.txt")
	if err != nil {
		fmt.Println("open file err=", err)
	}

	//当函数退出时,要及时的关闭file
	defer file.Close() //要及时关闭file句柄,否则会有内存泄漏.

	// 创建一个 *Reader  ,是带缓冲的
	/*
	const (
	defaultBufSize = 4096 //默认的缓冲区为4096
	)
	*/
	reader := bufio.NewReader(file)
	//循环的读取文件的内容
	for {
		str, err := reader.ReadString('\n') // 读到一个换行就结束
		if err == io.EOF { // io.EOF表示文件的末尾
			break
		}
		//输出内容
		fmt.Printf(str)
	}

	fmt.Println("文件读取结束...")
}

2 ) 一次性读取文件的内容并显示在终端(使用ioutil一次将整个文件读入到内存中),这种方式适用于文件 不大的情况。相关方法和函数(ioutil.ReadFile)

package main
import (
	"fmt"
	"io/ioutil" 
)
func main() {

	//使用ioutil.ReadFile一次性将文件读取到位
	file := "d:/test.txt"
	content, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Printf("read file err=%v", err)
	}
	//把读取到的内容显示到终端
	//fmt.Printf("%v", content) // []byte
	fmt.Printf("%v", string(content)) // []byte
	
	//我们没有显式的Open文件,因此也不需要显式的Close文件
	//因为,文件的Open和Close被封装到 ReadFile 函数内部
}

11.3 写文件操作

image-20210118152541107

1 ) 创建一个新文件,写入内容 5 句 “hello,Gardon”

package main
import (
	"fmt"
	"bufio"
	"os" 
)
func main() {
	//创建一个新文件,写入内容 5句 "hello, Gardon"
	//1 .打开文件 d:/abc.txt
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return 
	}
	//及时关闭file句柄
	defer file.Close()
	//准备写入5句 "hello, Gardon"
	str := "hello,Gardon\r\n" // \r\n 表示换行
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}
	//因为writer是带缓存,因此在调用WriterString方法时,其实
	//内容是先写入到缓存的,所以需要调用Flush方法,将缓冲的数据
	//真正写入到文件中, 否则文件中会没有数据!!!
	writer.Flush()
}

2 ) 打开一个存在的文件中,将原来的内容覆盖成新的内容 10 句 “你好,Golang Roadmap!”

package main
import (
	"fmt"
	"bufio"
	"os" 
)

func main() {
	//打开一个存在的文件中,将原来的内容覆盖成新的内容10句 "你好,Golang Roadmap!"

	//创建一个新文件,写入内容 5句 "hello, Gardon"
	//1 .打开文件已经存在文件, d:/abc.txt
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_TRUNC, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return 
	}
	//及时关闭file句柄
	defer file.Close()
	//准备写入5句 "你好,Golang Roadmap!"
	str := "你好,Golang Roadmap!\r\n" // \r\n 表示换行
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString(str)
	}
	//因为writer是带缓存,因此在调用WriterString方法时,其实
	//内容是先写入到缓存的,所以需要调用Flush方法,将缓冲的数据
	//真正写入到文件中, 否则文件中会没有数据!!!
	writer.Flush()
}

3 ) 打开一个存在的文件,在原来的内容追加内容 ‘ABC!ENGLISH!’

package main
import (
	"fmt"
	"bufio"
	"os" 
)

func main() {
	

	//打开一个存在的文件,在原来的内容追加内容 'ABC! ENGLISH!'
	//1 .打开文件已经存在文件, d:/abc.txt
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_APPEND, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return 
	}
	//及时关闭file句柄
	defer file.Close()
	//准备写入5句 "你好,Golang Roadmap!"
	str := "ABC,ENGLISH!\r\n" // \r\n 表示换行
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString(str)
	}
	//因为writer是带缓存,因此在调用WriterString方法时,其实
	//内容是先写入到缓存的,所以需要调用Flush方法,将缓冲的数据
	//真正写入到文件中, 否则文件中会没有数据!!!
	writer.Flush()

}

4 ) 打开一个存在的文件,将原来的内容读出显示在终端,并且追加 5 句"hello,北京!" 代码实现:

package main
import (
	"fmt"
	"bufio"
	"os"
	"io" 
)

func main() {
	
	//打开一个存在的文件,将原来的内容读出显示在终端,并且追加5句"hello,北京!"
	//1 .打开文件已经存在文件, d:/abc.txt
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return 
	}
	//及时关闭file句柄
	defer file.Close()

	//先读取原来文件的内容,并显示在终端.
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF { //如果读取到文件的末尾
			break
		}
		//显示到终端
		fmt.Print(str)
	}


	//准备写入5句 "你好,Golang Roadmap!"
	str := "hello,北京!\r\n" // \r\n 表示换行
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}
	//因为writer是带缓存,因此在调用WriterString方法时,其实
	//内容是先写入到缓存的,所以需要调用Flush方法,将缓冲的数据
	//真正写入到文件中, 否则文件中会没有数据!!!
	writer.Flush()
}

5)编程一个程序,将一个文件的内容,写入到另外一个文件。注:这两个文件已经存在了

说明:使用ioutil.ReadFile/ioutil.WriteFile 完成写文件的任务

package main
import (
	"fmt"
	"io/ioutil" 
)
func main() {
	//将d:/abc.txt 文件内容导入到  e:/kkk.txt
	//1. 首先将  d:/abc.txt 内容读取到内存
	//2. 将读取到的内容写入 e:/kkk.txt
	file1Path := "d:/abc.txt" 
	file2Path := "e:/kkk.txt" 
	data, err := ioutil.ReadFile(file1Path)
	if err != nil {
		//说明读取文件有错误
		fmt.Printf("read file err=%v\n", err)
		return
	}
	err = ioutil.WriteFile(file2Path, data, 0666)
	if err != nil {
		fmt.Printf("write file error=%v\n", err)
	}
}

11.4 判断文件是否存在

golang判断文件或文件夹是否存在的方法为使用os.Stat()函数返回的错误值进行判断:

  1. 如果返回的错误为nil,说明文件或文件夹存在
  2. 如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
  3. 如果返回的错误为其他类型,则不确定是否存在
func PathExists(path string)(bool,error){
	_, err := os.Stat(path)
	if err == nil{//文件或目录存在
		return true,nil
	}
	if os.IsNotExit(err){
		return false,nil
	}
	return false,err
}

11.5 文件复制

将一张图片/电影/mp 3 拷贝到另外一个文件 e:/abc.jpg io包

funcCopy(dstWriter,srcReader)(writtenint 64 ,errerror)

注意;Copy 函数是 io包提供的.

package main
import (
	"fmt"
	"os"
	"io"
	"bufio" 
)

//自己编写一个函数,接收两个文件路径 srcFileName dstFileName
func CopyFile(dstFileName string, srcFileName string) (written int64, err error) {

	srcFile, err := os.Open(srcFileName)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
	}
	defer srcFile.Close()
	//通过srcfile ,获取到 Reader
	reader := bufio.NewReader(srcFile)

	//打开dstFileName
	dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY | os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return 
	}

	//通过dstFile, 获取到 Writer
	writer := bufio.NewWriter(dstFile)
	defer dstFile.Close()

	return io.Copy(writer, reader)


}

func main() {

	//将d:/flower.jpg 文件拷贝到 e:/abc.jpg

	//调用CopyFile 完成文件拷贝
	srcFile := "d:/flower.jpg"
	dstFile := "e:/abc.jpg"
	_, err := CopyFile(dstFile, srcFile)
	if err == nil {
		fmt.Printf("拷贝完成\n")
	} else {
		fmt.Printf("拷贝错误 err=%v\n", err)
	}
	
}

11.6 命令行参数

我们希望能够获取到命令行输入的各种参数

  • os.Args 是一个string的切片,用来存储所有的命令行参数

    package main
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    	fmt.Println("命令行的参数有", len(os.Args))
    	//遍历os.Args切片,就可以得到所有的命令行输入参数值
    	for i, v := range os.Args {
    		fmt.Printf("args[%v]=%v\n", i, v)
    	}
    }
    

执行结果

image-20210118154106511

  • flag包用来解析命令行参数

前面的方式是比较原生的方式,对解析参数不是特别的方便,特别是带有指定参数形式的命

令行。比如:cmd>main.exe -f c:/aaa.txt -p 200 - u root

这样的形式命令行,go设计者给我们提供了 flag包,可以方便的解析命令行参数,而且参数顺序可以随意

package main
import (
	"fmt"
	"flag"
)

func main() {

	//定义几个变量,用于接收命令行的参数值
	var user string
	var pwd string
	var host string
	var port int

	//&user 就是接收用户命令行中输入的 -u 后面的参数值
	//"u" ,就是 -u 指定参数
	//"" , 默认值
	//"用户名,默认为空" 说明
	flag.StringVar(&user, "u", "", "用户名,默认为空")
	flag.StringVar(&pwd, "pwd", "", "密码,默认为空")
	flag.StringVar(&host, "h", "localhost", "主机名,默认为localhost")
	flag.IntVar(&port, "port", 3306, "端口号,默认为3306")
	//这里有一个非常重要的操作,转换, 必须调用该方法
	flag.Parse()

	//输出结果
	fmt.Printf("user=%v pwd=%v host=%v port=%v", 
		user, pwd, host, port)

}

执行结果

image-20210118154317890

11.7 JSON序列化

  • 介绍

    json序列化是指,将有 key-valu e结构的数据类型(比如结构体、 map 、切片)序列化成json字符串的操作。

package main
import (
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string `json:"monster_name"` //反射机制
	Age int `json:"monster_age"`
	Birthday string //....
	Sal float64
	Skill string
}

func testStruct() {
	//演示
	monster := Monster{
		Name :"牛魔王",
		Age : 500 ,
		Birthday : "2011-11-11",
		Sal : 8000.0,
		Skill : "牛魔拳",
	}

	//将monster 序列化
	data, err := json.Marshal(&monster) //..
	if err != nil {
		fmt.Printf("序列号错误 err=%v\n", err)
	}
	//输出序列化后的结果
	fmt.Printf("monster序列化后=%v\n", string(data))

}

//将map进行序列化
func testMap() {
	//定义一个map
	var a map[string]interface{}
	//使用map,需要make
	a = make(map[string]interface{})
	a["name"] = "红孩儿"
	a["age"] = 30
	a["address"] = "洪崖洞"

	//将a这个map进行序列化
	//将monster 序列化
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
	}
	//输出序列化后的结果
	fmt.Printf("a map 序列化后=%v\n", string(data))

}

//演示对切片进行序列化, 我们这个切片 []map[string]interface{}
func testSlice() {
	var slice []map[string]interface{}
	var m1 map[string]interface{}
	//使用map前,需要先make
	m1 = make(map[string]interface{})
	m1["name"] = "jack"
	m1["age"] = "7"
	m1["address"] = "北京"
	slice = append(slice, m1)

	var m2 map[string]interface{}
	//使用map前,需要先make
	m2 = make(map[string]interface{})
	m2["name"] = "tom"
	m2["age"] = "20"
	m2["address"] = [2]string{"墨西哥","夏威夷"}
	slice = append(slice, m2)

	//将切片进行序列化操作
	data, err := json.Marshal(slice)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
	}
	//输出序列化后的结果
	fmt.Printf("slice 序列化后=%v\n", string(data))
	
}

//对基本数据类型序列化,对基本数据类型进行序列化意义不大
func testFloat64() {
	var num1 float64 = 2345.67

	//对num1进行序列化
	data, err := json.Marshal(num1)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
	}
	//输出序列化后的结果
	fmt.Printf("num1 序列化后=%v\n", string(data))
}

func main() {
	//演示将结构体, map , 切片进行序列号
	testStruct()
	testMap()
	testSlice()//演示对切片的序列化
	testFloat64()//演示对基本数据类型的序列化
}
  • 注意事项

    package main
    import (
    	"fmt"
    	"encoding/json"
    )
    
    //定义一个结构体
    type Monster struct {
    	Name string `json:"monster_name"` //反射机制
    	Age int `json:"monster_age"`
    	Birthday string //....
    	Sal float64
    	Skill string
    }
    
    func testStruct() {
    	//演示
    	monster := Monster{
    		Name :"牛魔王",
    		Age : 500 ,
    		Birthday : "2011-11-11",
    		Sal : 8000.0,
    		Skill : "牛魔拳",
    	}
    
    	//将monster 序列化
    	data, err := json.Marshal(&monster) //..
    	if err != nil {
    		fmt.Printf("序列号错误 err=%v\n", err)
    	}
    	//输出序列化后的结果
    	fmt.Printf("monster序列化后=%v\n", string(data))
    
    }
    
    //将map进行序列化
    func testMap() {
    	//定义一个map
    	var a map[string]interface{}
    	//使用map,需要make
    	a = make(map[string]interface{})
    	a["name"] = "红孩儿"
    	a["age"] = 30
    	a["address"] = "洪崖洞"
    
    	//将a这个map进行序列化
    	//将monster 序列化
    	data, err := json.Marshal(a)
    	if err != nil {
    		fmt.Printf("序列化错误 err=%v\n", err)
    	}
    	//输出序列化后的结果
    	fmt.Printf("a map 序列化后=%v\n", string(data))
    
    }
    
    //演示对切片进行序列化, 我们这个切片 []map[string]interface{}
    func testSlice() {
    	var slice []map[string]interface{}
    	var m1 map[string]interface{}
    	//使用map前,需要先make
    	m1 = make(map[string]interface{})
    	m1["name"] = "jack"
    	m1["age"] = "7"
    	m1["address"] = "北京"
    	slice = append(slice, m1)
    
    	var m2 map[string]interface{}
    	//使用map前,需要先make
    	m2 = make(map[string]interface{})
    	m2["name"] = "tom"
    	m2["age"] = "20"
    	m2["address"] = [2]string{"墨西哥","夏威夷"}
    	slice = append(slice, m2)
    
    	//将切片进行序列化操作
    	data, err := json.Marshal(slice)
    	if err != nil {
    		fmt.Printf("序列化错误 err=%v\n", err)
    	}
    	//输出序列化后的结果
    	fmt.Printf("slice 序列化后=%v\n", string(data))
    	
    }
    
    //对基本数据类型序列化,对基本数据类型进行序列化意义不大
    func testFloat64() {
    	var num1 float64 = 2345.67
    
    	//对num1进行序列化
    	data, err := json.Marshal(num1)
    	if err != nil {
    		fmt.Printf("序列化错误 err=%v\n", err)
    	}
    	//输出序列化后的结果
    	fmt.Printf("num1 序列化后=%v\n", string(data))
    }
    
    func main() {
    	//演示将结构体, map , 切片进行序列号
    	testStruct()
    	testMap()
    	testSlice()//演示对切片的序列化
    	testFloat64()//演示对基本数据类型的序列化
    }
    

11.8 JSON反序列化

json反序列化是指,将json字符串反序列化成对应的数据类型(比如结构体、map、切片)的操作

  • 这里我们介绍一下将json字符串反序列化成结构体、map和切片
  • 反序列化,不需要make,因为make操作被封装到 Unmarshal函数
package main
import (
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string  
	Age int 
	Birthday string //....
	Sal float64
	Skill string
}


//演示将json字符串,反序列化成struct
func unmarshalStruct() {
	//说明str 在项目开发中,是通过网络传输获取到.. 或者是读取文件获取到
	str := "{\"Name\":\"牛魔王~~~\",\"Age\":500,\"Birthday\":\"2011-11-11\",\"Sal\":8000,\"Skill\":\"牛魔拳\"}"

	//定义一个Monster实例
	var monster Monster

	err := json.Unmarshal([]byte(str), &monster)
	if err != nil {
		fmt.Printf("unmarshal err=%v\n", err)
	}
	fmt.Printf("反序列化后 monster=%v monster.Name=%v \n", monster, monster.Name)

}
//将map进行序列化
func testMap() string {
	//定义一个map
	var a map[string]interface{}
	//使用map,需要make
	a = make(map[string]interface{})
	a["name"] = "红孩儿~~~~~~"
	a["age"] = 30
	a["address"] = "洪崖洞"

	//将a这个map进行序列化
	//将monster 序列化
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
	}
	//输出序列化后的结果
	//fmt.Printf("a map 序列化后=%v\n", string(data))
	return string(data)

}

//演示将json字符串,反序列化成map
func unmarshalMap() {
	//str := "{\"address\":\"洪崖洞\",\"age\":30,\"name\":\"红孩儿\"}"
	str := testMap()
	//定义一个map
	var a map[string]interface{} 

	//反序列化
	//注意:反序列化map,不需要make,因为make操作被封装到 Unmarshal函数
	err := json.Unmarshal([]byte(str), &a)
	if err != nil {
		fmt.Printf("unmarshal err=%v\n", err)
	}
	fmt.Printf("反序列化后 a=%v\n", a)

}

//演示将json字符串,反序列化成切片
func unmarshalSlice() {
	str := "[{\"address\":\"北京\",\"age\":\"7\",\"name\":\"jack\"}," + 
		"{\"address\":[\"墨西哥\",\"夏威夷\"],\"age\":\"20\",\"name\":\"tom\"}]"
	
	//定义一个slice
	var slice []map[string]interface{}
	//反序列化,不需要make,因为make操作被封装到 Unmarshal函数
	err := json.Unmarshal([]byte(str), &slice)
	if err != nil {
		fmt.Printf("unmarshal err=%v\n", err)
	}
	fmt.Printf("反序列化后 slice=%v\n", slice)
}

func main() {

	unmarshalStruct()
	unmarshalMap()
	unmarshalSlice()
}
  • 1 ) 在反序列化一个json字符串时,要确保反序列化后的数据类型原来序列化前的数据类型一致。

    2 ) 如果json字符串是通过程序获取到的,则不需要再对 “ 转义处理。

image-20210118155633534

相关文章:

  • 【EHub_tx1_tx2_E100】不止科技NVISTAR ROC 300激光雷达Ubuntu18.04+ROS1ROS2 评测
  • WebRTC API
  • 大数据之Spark开发环境准备
  • 什么是Java运算?Java运算好学吗?
  • abaqus子程序vumat安装使用
  • 【Java项目】Minio的安装部署以及SpringBoot整合Minio
  • 架构设计第一讲:架构设计相关面试题汇总
  • LeetCode笔记:Biweekly Contest 101
  • 【python实操】马上毕业了,你还不懂什么是守护线程、线程、进程?(附12306抢票程序-源代码)
  • Springboot整合rabbitmq并实现消息可靠性和持久性
  • ChatGPT可以作为一个翻译器吗?
  • 一文学会 Spring MVC 表单标签
  • 【联邦学习(Federated Learning)】- 横向联邦学习与联邦平均FedAvg
  • 免费一键生成原创文章-原创文章批量生成
  • 众人围剿,GPT-5招惹了谁
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • Android 控件背景颜色处理
  • avalon2.2的VM生成过程
  • chrome扩展demo1-小时钟
  • css选择器
  • exif信息对照
  • extjs4学习之配置
  • golang中接口赋值与方法集
  • JDK9: 集成 Jshell 和 Maven 项目.
  • MySQL主从复制读写分离及奇怪的问题
  • ReactNative开发常用的三方模块
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 包装类对象
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 近期前端发展计划
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 深入浅出webpack学习(1)--核心概念
  • 小程序 setData 学问多
  • 国内开源镜像站点
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • #include<初见C语言之指针(5)>
  • #includecmath
  • $.each()与$(selector).each()
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (NSDate) 时间 (time )比较
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (ZT)一个美国文科博士的YardLife
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (理论篇)httpmoudle和httphandler一览
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)一些感悟
  • **CI中自动类加载的用法总结
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution