《Go语言圣经》学习笔记 第五章函数

《Go语言圣经》学习笔记 第五章 函数


目录

  1. 函数声明
  2. 递归
  3. 多返回值
  4. 匿名函数
  5. 可变参数
  6. Deferred函数
  7. Panic异常
  8. Recover捕获异常

注:学习《Go语言圣经》笔记,PDF点击下载,建议看书。
Go语言小白学习笔记,书上的内容照搬,大佬看了勿喷,以后熟悉了会总结成自己的读书笔记。


  1. 函数可以让我们将一个语句序列打包为一个单元, 然后可以从程序中其它地方多次调用。 函数的机制可以让我们将一个大的工作分解为小的任务, 这样的小任务可以让不同程序员在不同时间、 不同地方独立完成。 一个函数同时对用户隐藏了其实现细节。 由于这些因素, 对于任何编程语言来说, 函数都是一个至关重要的部分。
  2. 我们已经见过许多函数了。 现在, 让我们多花一点时间来彻底地讨论函数特性。 本章的运行示例是一个网络蜘蛛, 也就是web搜索引擎中负责抓取网页部分的组件, 它们根据抓取网页中的链接继续抓取链接指向的页面。 一个网络蜘蛛的例子给我们足够的机会去探索递归函数、匿名函数、 错误处理和函数其它的很多特性。

1. 函数声明

  1. 函数声明包括函数名、 形式参数列表、 返回值列表( 可省略) 以及函数体。
    在这里插入图片描述
  2. 形式参数列表描述了函数的参数名以及参数类型。 这些参数作为局部变量, 其值由参数调用者提供。 返回值列表描述了函数返回值的变量名以及类型。 如果函数返回一个无名变量或者没有返回值, 返回值列表的括号是可以省略的。 如果一个函数声明不包括返回值列表, 那么函数体执行完毕后, 不会返回任何值。 在hypot函数中
    《Go语言圣经》学习笔记 第五章函数_第1张图片
  3. x和y是形参名,3和4是调用时的传入的实数, 函数返回了一个float64类型的值。 返回值也可以像形式参数一样被命名。 在这种情况下, 每个返回值被声明成一个局部变量, 并根据该返回值的类型, 将其初始化为0。 如果一个函数在声明时, 包含返回值列表, 该函数必须以 return语句结尾, 除非函数明显无法运行到结尾处。 例如函数在结尾时调用了panic异常或函数中存在无限循环。
  4. 正如hypot一样, 如果一组形参或返回值有相同的类型, 我们不必为每个形参都写出参数类型。 下面2个声明是等价的:
    在这里插入图片描述
  5. 下面, 我们给出4种方法声明拥有2个int型参数和1个int型返回值的函数.blank identifier(译者注: 即下文的_符号)可以强调某个参数未被使用。
    《Go语言圣经》学习笔记 第五章函数_第2张图片
  6. 函数的类型被称为函数的标识符。 如果两个函数形式参数列表和返回值列表中的变量类型一一对应, 那么这两个函数被认为有相同的类型和标识符。 形参和返回值的变量名不影响函数标识符也不影响它们是否可以以省略参数类型的形式表示。
  7. 每一次函数调用都必须按照声明顺序为所有参数提供实参( 参数值) 。 在函数调用时, Go语言没有默认参数值, 也没有任何方法可以通过参数名指定形参, 因此形参和返回值的变量名对于函数调用者而言没有意义。
  8. 在函数体中, 函数的形参作为局部变量, 被初始化为调用者提供的值。 函数的形参和有名返回值作为函数最外层的局部变量, 被存储在相同的词法块中。
  9. 实参通过值的方式传递, 因此函数的形参是实参的拷贝。 对形参进行修改不会影响实参。 但是, 如果实参包括引用类型, 如指针, slice(切片)、 map、 function、 channel等类型, 实参可能会由于函数的简介引用被修改。
  10. 你可能会偶尔遇到没有函数体的函数声明, 这表示该函数不是以Go实现的。 这样的声明定义了函数标识符
    在这里插入图片描述

2. 递归

  1. 函数可以是递归的, 这意味着函数可以直接或间接的调用自身。 对许多问题而言, 递归是一种强有力的技术, 例如处理递归的数据结构。 在4.4节, 我们通过遍历二叉树来实现简单的插入排序, 在本章节, 我们再次使用它来处理HTML文件。

  2. 下文的示例代码使用了非标准包 golang.org/x/net/html , 解析HTML。 golang.org/x/… 目录下存储了一些由Go团队设计、 维护, 对网络编程、 国际化文件处理、 移动平台、 图像处理、 加密解密、 开发者工具提供支持的扩展包。 未将这些扩展包加入到标准库原因有二, 一是部分包仍在开发中, 二是对大多数Go语言的开发者而言, 扩展包提供的功能很少被使用。

  3. 例子中调用golang.org/x/net/html的部分api如下所示。 html.Parse函数读入一组bytes.解析后, 返回html.node类型的HTML页面树状结构根节点。 HTML拥有很多类型的结点如text( 文本) ,commnets( 注释) 类型, 在下面的例子中, 我们 只关注< name key=‘value’ >形式的结点。

  4. golang.org/x/net/html
    《Go语言圣经》学习笔记 第五章函数_第3张图片

  5. main函数解析HTML标准输入, 通过递归函数visit获得links( 链接) , 并打印出这些links:
    《Go语言圣经》学习笔记 第五章函数_第4张图片

  6. visit函数遍历HTML的节点树, 从每一个anchor元素的href属性获得link,将这些links存入字符串数组中, 并返回这个字符串数组。
    《Go语言圣经》学习笔记 第五章函数_第5张图片

  7. 为了遍历结点n的所有后代结点, 每次遇到n的孩子结点时, visit递归的调用自身。 这些孩子结点存放在FirstChild链表中。

  8. 让我们以Go的主页( golang.org) 作为目标, 运行findlinks。 我们以fetch( 1.5章) 的输出作为findlinks的输入。 下面的输出做了简化处理。
    《Go语言圣经》学习笔记 第五章函数_第6张图片

  9. 注意在页面中出现的链接格式, 在之后我们会介绍如何将这些链接, 根据根路径(https://golang.org ) 生成可以直接访问的url。

  10. 在函数outline中, 我们通过递归的方式遍历整个HTML结点树, 并输出树的结构。 在outline内部, 每遇到一个HTML元素标签, 就将其入栈, 并输出。

  11. gopl.io/ch5/outline
    《Go语言圣经》学习笔记 第五章函数_第7张图片

  12. 有一点值得注意: outline有入栈操作, 但没有相对应的出栈操作。 当outline调用自身时, 被调用者接收的是stack的拷贝。 被调用者的入栈操作, 修改的是stack的拷贝, 而不是调用者的stack,因对当函数返回时,调用者的stack并未被修改。

  13. 下面是 https://golang.org 页面的简要结构:
    《Go语言圣经》学习笔记 第五章函数_第8张图片

  14. 正如你在上面实验中所见, 大部分HTML页面只需几层递归就能被处理, 但仍然有些页面需要深层次的递归。

  15. 大部分编程语言使用固定大小的函数调用栈, 常见的大小从64KB到2MB不等。 固定大小栈会限制递归的深度, 当你用递归处理大量数据时, 需要避免栈溢出; 除此之外, 还会导致安全性问题。 与相反,Go语言使用可变栈, 栈的大小按需增加(初始时很小)。 这使得我们使用递归时不必考虑溢出和安全问题。


3. 多返回值

  1. 在Go中, 一个函数可以返回多个值。 我们已经在之前例子中看到, 许多标准库中的函数返回2个值, 一个是期望得到的返回值, 另一个是函数出错时的错误信息。 下面的例子会展示如何编写多返回值的函数。

  2. 下面的程序是findlinks的改进版本。 修改后的findlinks可以自己发起HTTP请求, 这样我们就不必再运行fetch。 因为HTTP请求和解析操作可能会失败, 因此findlinks声明了2个返回值: 链接列表和错误信息。 一般而言, HTML的解析器可以处理HTML页面的错误结点, 构造出HTML页面结构, 所以解析HTML很少失败。 这意味着如果findlinks函数失败了, 很可能是由于I/O的错误导致的。

  3. gopl.io/ch5/findlinks2

    package main
    
    import (
    	"fmt"
    	"html"
    	"net/http"
    	"os"
    )
    
    func main() {
    	for _, url := range os.Args[1:] {
    		links, err := findLinks(url)
    		if err != nil {
    			fmt.Fprintf(os.Stderr, "findlinks2: %v\n", err)
    			continue
    		}
    		for _, link := range links {
    			fmt.Println(link)
    		}
    	}
    }
    
    // findLinks performs an HTTP GET request for url, parses the
    // response as HTML, and extracts and returns the links.
    
    func findLinks(url string) ([]string, error) {
    	resp, err := http.Get(url)
    	if err != nil {
    		return nil, err
    	}
    	if resp.StatusCode != http.StatusOK {
    		resp.Body.Close()
    		return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
    	}
    	doc, err := html.Parse(resp.Body)
    	resp.Body.Close()
    	if err != nil {
    		return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
    	}
    	return visit(nil, doc), nil
    }
    
    
  4. 在findlinks中, 有4处return语句, 每一处return都返回了一组值。 前三处return, 将http和html包中的错误信息传递给findlinks的调用者。 第一处return直接返回错误信息, 其他两处通过fmt.Errorf( §7.8) 输出详细的错误信息。 如果findlinks成功结束, 最后的return语句将一组解析获得的连接返回给用户。

  5. 在finallinks中, 我们必须确保resp.Body被关闭, 释放网络资源。 虽然Go的垃圾回收机制会回收不被使用的内存, 但是这不包括操作系统层面的资源, 比如打开的文件、 网络连接。 因此我们必须显式的释放这些资源。

  6. 调用多返回值函数时, 返回给调用者的是一组值, 调用者必须显式的将这些值分配给变量:
    在这里插入图片描述

  7. 如果某个值不被使用, 可以将其分配给blank identifier:
    在这里插入图片描述

  8. 一个函数内部可以将另一个有多返回值的函数作为返回值, 下面的例子展示了与findLinks有相同功能的函数, 两者的区别在于下面的例子先输出参数:
    《Go语言圣经》学习笔记 第五章函数_第9张图片

  9. 当你调用接受多参数的函数时, 可以将一个返回多参数的函数作为该函数的参数。 虽然这很少出现在实际生产代码中, 但这个特性在debug时很方便, 我们只需要一条语句就可以输出所有的返回值。 下面的代码是等价的:
    在这里插入图片描述

  10. 准确的变量名可以传达函数返回值的含义。 尤其在返回值的类型都相同时, 就像下面这样:
    《Go语言圣经》学习笔记 第五章函数_第10张图片

  11. 虽然良好的命名很重要, 但你也不必为每一个返回值都取一个适当的名字。 比如, 按照惯例, 函数的最后一个bool类型的返回值表示函数是否运行成功, error类型的返回值代表函数的错误信息, 对于这些类似的惯例, 我们不必思考合适的命名, 它们都无需解释。

  12. 如果一个函数将所有的返回值都显示的变量名, 那么该函数的return语句可以省略操作数。 这称之为bare return。
    《Go语言圣经》学习笔记 第五章函数_第11张图片

  13. 按照返回值列表的次序, 返回所有的返回值, 在上面的例子中, 每一个return语句等价于:
    在这里插入图片描述

  14. 当一个函数有多处return语句以及许多返回值时, bare return 可以减少代码的重复, 但是使得代码难以被理解。 举个例子, 如果你没有仔细的审查代码, 很难发现前2处return等价于return 0,0,err( Go会将返回值 words和images在函数体的开始处, 根据它们的类型, 将其初始化为0) , 最后一处return等价于 return words, image, nil。 基于以上原因, 不宜过度使用bare return。


4. 错误

  1. 在Go中有一部分函数总是能成功的运行。 比如strings.Contains和strconv.FormatBool函数,对各种可能的输入都做了良好的处理, 使得运行时几乎不会失败, 除非遇到灾难性的、 不可预料的情况, 比如运行时的内存溢出。 导致这种错误的原因很复杂, 难以处理, 从错误中恢复的可能性也很低。
  2. 还有一部分函数只要输入的参数满足一定条件, 也能保证运行成功。 比如time.Date函数, 该函数将年月日等参数构造成time.Time对象, 除非最后一个参数( 时区) 是nil。 这种情况下会引发panic异常。 panic是来自被调函数的信号, 表示发生了某个已知的bug。 一个良好的程序永远不应该发生panic异常。
  3. 对于大部分函数而言, 永远无法确保能否成功运行。 这是因为错误的原因超出了程序员的控制。 举个例子, 任何进行I/O操作的函数都会面临出现错误的可能, 只有没有经验的程序员才会相信读写操作不会失败, 即时是简单的读写。 因此, 当本该可信的操作出乎意料的失败后, 我们必须弄清楚导致失败的原因。
  4. 在Go的错误处理中, 错误是软件包API和应用程序用户界面的一个重要组成部分, 程序运行失败仅被认为是几个预期的结果之一。
  5. 对于那些将运行失败看作是预期结果的函数, 它们会返回一个额外的返回值, 通常是最后一个, 来传递错误信息。 如果导致失败的原因只有一个, 额外的返回值可以是一个布尔值, 通常被命名为ok。 比如, cache.Lookup失败的唯一原因是key不存在, 那么代码可以按照下面的方式组织:
    《Go语言圣经》学习笔记 第五章函数_第12张图片
  6. 通常, 导致失败的原因不止一种, 尤其是对I/O操作而言, 用户需要了解更多的错误信息。 因此, 额外的返回值不再是简单的布尔类型, 而是error类型。
  7. 内置的error是接口类型。 我们将在第七章了解接口类型的含义, 以及它对错误处理的影响。现在我们只需要明白error类型可能是nil或者non-nil。 nil意味着函数运行成功, non-nil表示失败。 对于non-nil的error类型,我们可以通过调用error的Error函数或者输出函数获得字符串类型的错误信息。
    在这里插入图片描述
  8. 通常, 当函数返回non-nil的error时, 其他的返回值是未定义的(undefined),这些未定义的返回值应该被忽略。 然而, 有少部分函数在发生错误时, 仍然会返回一些有用的返回值。 比如,当读取文件发生错误时, Read函数会返回可以读取的字节数以及错误信息。 对于这种情况,正确的处理方式应该是先处理这些不完整的数据, 再处理错误。 因此对函数的返回值要有清晰的说明, 以便于其他人使用。
  9. 在Go中, 函数运行失败时会返回错误信息, 这些错误信息被认为是一种预期的值而非异常( exception) , 这使得Go有别于那些将函数运行失败看作是异常的语言。 虽然Go有各种异常机制, 但这些机制仅被使用在处理那些未被预料到的错误, 即bug, 而不是那些在健壮程序中应该被避免的程序错误。 对于Go的异常机制我们将在5.9介绍。
  10. Go这样设计的原因是由于对于某个应该在控制流程中处理的错误而言, 将这个错误以异常的形式抛出会混乱对错误的描述, 这通常会导致一些糟糕的后果。 当某个程序错误被当作异常处理后, 这个错误会将堆栈根据信息返回给终端用户, 这些信息复杂且无用, 无法帮助定位错误。
  11. 正因此, Go使用控制流机制( 如if和return) 处理异常, 这使得编码人员能更多的关注错误处理。

1. 错误处理策略

  1. 当一次函数调用返回错误时, 调用者有应该选择何时的方式处理错误。 根据情况的不同, 有很多处理方式, 让我们来看看常用的五种方式。
  2. 首先, 也是最常用的方式是传播错误。 这意味着函数中某个子程序的失败, 会变成该函数的失败。 下面, 我们以5.3节的findLinks函数作为例子。 如果findLinks对http.Get的调用失败,findLinks会直接将这个HTTP错误返回给调用者:
    《Go语言圣经》学习笔记 第五章函数_第13张图片
  3. 当对html.Parse的调用失败时, findLinks不会直接返回html.Parse的错误, 因为缺少两条重要信息: 1、 错误发生在解析器; 2、 url已经被解析。 这些信息有助于错误的处理, findLinks会构造新的错误信息返回给调用者:
    《Go语言圣经》学习笔记 第五章函数_第14张图片
  4. fmt.Errorf函数使用fmt.Sprintf格式化错误信息并返回。 我们使用该函数前缀添加额外的上下文信息到原始错误信息。 当错误最终由main函数处理时, 错误信息应提供清晰的从原因到后果的因果链。
  5. 由于错误信息经常是以链式组合在一起的, 所以错误信息中应避免大写和换行符。 最终的错误信息可能很长, 我们可以通过类似grep的工具处理错误信息( 译者注: grep是一种文本搜索工具) 。
  6. 编写错误信息时, 我们要确保错误信息对问题细节的描述是详尽的。 尤其是要注意错误信息表达的一致性, 即相同的函数或同包内的同一组函数返回的错误在构成和处理方式上是相似的。
  7. 以OS包为例, OS包确保文件操作( 如os.Open、 Read、 Write、 Close) 返回的每个错误的描述不仅仅包含错误的原因( 如无权限, 文件目录不存在) 也包含文件名, 这样调用者在构造新的错误信息时无需再添加这些信息。
  8. 一般而言, 被调函数f(x)会将调用信息和参数信息作为发生错误时的上下文放在错误信息中并返回给调用者, 调用者需要添加一些错误信息中不包含的信息, 比如添加url到html.Parse返回的错误中。
  9. 让我们来看看处理错误的第二种策略。 如果错误的发生是偶然性的, 或由不可预知的问题导致的。 一个明智的选择是重新尝试失败的操作。 在重试时, 我们需要限制重试的时间间隔或重试的次数, 防止无限制的重试。
  10. gopl.io/ch5/wait
    《Go语言圣经》学习笔记 第五章函数_第15张图片
  11. 如果错误发生后, 程序无法继续运行, 我们就可以采用第三种策略: 输出错误信息并结束程序。 需要注意的是, 这种策略只应在main中执行。 对库函数而言, 应仅向上传播错误, 除非该错误意味着程序内部包含不一致性, 即遇到了bug, 才能在库函数中结束程序。
    《Go语言圣经》学习笔记 第五章函数_第16张图片
  12. 调用log.Fatalf可以更简洁的代码达到与上文相同的效果。 log中的所有函数, 都默认会在错误信息之前输出时间信息。
    在这里插入图片描述
  13. 长时间运行的服务器常采用默认的时间格式, 而交互式工具很少采用包含如此多信息的格式。
    在这里插入图片描述
  14. 我们可以设置log的前缀信息屏蔽时间信息, 一般而言, 前缀信息会被设置成命令名。
    在这里插入图片描述
  15. 第四种策略: 有时, 我们只需要输出错误信息就足够了, 不需要中断程序的运行。 我们可以通过log包提供函数
    《Go语言圣经》学习笔记 第五章函数_第17张图片
  16. 或者标准错误流输出错误信息。
    在这里插入图片描述
  17. log包中的所有函数会为没有换行符的字符串增加换行符。
  18. 第五种, 也是最后一种策略: 我们可以直接忽略掉错误.
    《Go语言圣经》学习笔记 第五章函数_第18张图片
  19. 尽管os.RemoveAll会失败, 但上面的例子并没有做错误处理。 这是因为操作系统会定期的清理临时目录。 正因如此, 虽然程序没有处理错误, 但程序的逻辑不会因此受到影响。 我们应该在每次函数调用后, 都养成考虑错误处理的习惯, 当你决定忽略某个错误时, 你应该在清晰的记录下你的意图。
  20. 在Go中, 错误处理有一套独特的编码风格。 检查某个子函数是否失败后, 我们通常将处理失败的逻辑代码放在处理成功的代码之前。 如果某个错误会导致函数返回, 那么成功时的逻辑代码不应放在else语句块中, 而应直接放在函数体中。 Go中大部分函数的代码结构几乎相同, 首先是一系列的初始检查, 防止错误发生, 之后是函数的实际逻辑。

2. 文件结尾错误( EOF)

  1. 函数经常会返回多种错误, 这对终端用户来说可能会很有趣, 但对程序而言, 这使得情况变得复杂。 很多时候, 程序必须根据错误类型, 作出不同的响应。 让我们考虑这样一个例子:从文件中读取n个字节。 如果n等于文件的长度, 读取过程的任何错误都表示失败。 如果n小于文件的长度, 调用者会重复的读取固定大小的数据直到文件结束。 这会导致调用者必须分别处理由文件结束引起的各种错误。 基于这样的原因, io包保证任何由文件结束引起的读取失败都返回同一个错误——io.EOF, 该错误在io包中定义:
    《Go语言圣经》学习笔记 第五章函数_第19张图片
  2. 调用者只需通过简单的比较, 就可以检测出这个错误。 下面的例子展示了如何从标准输入中读取字符, 以及判断文件结束。 ( 4.3的chartcount程序展示了更加复杂的代码)
    《Go语言圣经》学习笔记 第五章函数_第20张图片
  3. 因为文件结束这种错误不需要更多的描述, 所以io.EOF有固定的错误信息——“EOF”。 对于其他错误, 我们可能需要在错误信息中描述错误的类型和数量, 这使得我们不能像io.EOF一样采用固定的错误信息。 在7.11节中, 我们会提出更系统的方法区分某些固定的错误值。

5. 函数值

  1. 在Go中, 函数被看作第一类值( first-class values) : 函数像其他值一样, 拥有类型, 可以被赋值给其他变量, 传递给函数, 从函数返回。 对函数值( function value) 的调用类似函数调用。 例子如下:
    《Go语言圣经》学习笔记 第五章函数_第21张图片
  2. 函数类型的零值是nil。 调用值为nil的函数值会引起panic错误:
    在这里插入图片描述
  3. 函数值可以与nil比较:
    《Go语言圣经》学习笔记 第五章函数_第22张图片
  4. 但是函数值之间是不可比较的, 也不能用函数值作为map的key。
  5. 函数值使得我们不仅仅可以通过数据来参数化函数, 亦可通过行为。 标准库中包含许多这样的例子。 下面的代码展示了如何使用这个技巧。 strings.Map对字符串中的每个字符调用add1函数, 并将每个add1函数的返回值组成一个新的字符串返回给调用者。
    《Go语言圣经》学习笔记 第五章函数_第23张图片
  6. 5.2节的findLinks函数使用了辅助函数visit,遍历和操作了HTML页面的所有结点。 使用函数值, 我们可以将遍历结点的逻辑和操作结点的逻辑分离, 使得我们可以复用遍历的逻辑, 从而对结点进行不同的操作。
  7. gopl.io/ch5/outline2
    《Go语言圣经》学习笔记 第五章函数_第24张图片
  8. 该函数接收2个函数作为参数, 分别在结点的孩子被访问前和访问后调用。 这样的设计给调用者更大的灵活性。 举个例子, 现在我们有startElemen和endElement两个函数用于输出HTML元素的开始标签和结束标签 ...
    《Go语言圣经》学习笔记 第五章函数_第25张图片
  9. 上面的代码利用fmt.Printf的一个小技巧控制输出的缩进。 %s 中的 * 会在字符串之前填充一些空格。 在例子中,每次输出会先填充 depth2 数量的空格, 再输出"", 最后再输出HTML标签。
  10. 如果我们像下面这样调用forEachNode:
    在这里插入图片描述
  11. 与之前的outline程序相比, 我们得到了更加详细的页面结构:
    《Go语言圣经》学习笔记 第五章函数_第26张图片

6. 匿名函数

  1. 拥有函数名的函数只能在包级语法块中被声明, 通过函数字面量( function literal) , 我们可绕过这一限制, 在任何表达式中表示一个函数值。 函数字面量的语法和函数声明相似, 区别在于func关键字后没有函数名。 函数值字面量是一种表达式, 它的值被称为匿名函数( anonymous function) 。

  2. 函数字面量允许我们在使用函数时, 再定义它。 通过这种技巧, 我们可以改写之前对strings.Map的调用:
    在这里插入图片描述

  3. 更为重要的是, 通过这种方式定义的函数可以访问完整的词法环境( lexical environment) ,这意味着在函数中定义的内部函数可以引用该函数的变量, 如下例所示:

  4. gopl.io/ch5/squares
    《Go语言圣经》学习笔记 第五章函数_第27张图片

  5. 函数squares返回另一个类型为 func() int 的函数。 对squares的一次调用会生成一个局部变量x并返回一个匿名函数。 每次调用时匿名函数时, 该函数都会先使x的值加1, 再返回x的平方。 第二次调用squares时, 会生成第二个x变量, 并返回一个新的匿名函数。 新匿名函数操作的是第二个x变量。

  6. squares的例子证明, 函数值不仅仅是一串代码, 还记录了状态。 在squares中定义的匿名内部函数可以访问和更新squares中的局部变量, 这意味着匿名函数和squares中, 存在变量引用。 这就是函数值属于引用类型和函数值不可比较的原因。 Go使用闭包( closures) 技术实现函数值, Go程序员也把函数值叫做闭包。

  7. 通过这个例子, 我们看到变量的生命周期不由它的作用域决定: squares返回后, 变量x仍然隐式的存在于f中。

  8. 接下来, 我们讨论一个有点学术性的例子, 考虑这样一个问题: 给定一些计算机课程, 每个课程都有前置课程, 只有完成了前置课程才可以开始当前课程的学习; 我们的目标是选择出一组课程, 这组课程必须确保按顺序学习时, 能全部被完成。 每个课程的前置课程如下:

  9. gopl.io/ch5/toposort
    《Go语言圣经》学习笔记 第五章函数_第28张图片

  10. 这类问题被称作拓扑排序。 从概念上说, 前置条件可以构成有向图。 图中的顶点表示课程,边表示课程间的依赖关系。 显然, 图中应该无环, 这也就是说从某点出发的边, 最终不会回到该点。 下面的代码用深度优先搜索了整张图, 获得了符合要求的课程序列。
    《Go语言圣经》学习笔记 第五章函数_第29张图片

  11. 当匿名函数需要被递归调用时, 我们必须首先声明一个变量( 在上面的例子中, 我们首先声明了 visitAll) , 再将匿名函数赋值给这个变量。 如果不分成两部, 函数字面量无法与visitAll绑定, 我们也无法递归调用该匿名函数。
    《Go语言圣经》学习笔记 第五章函数_第30张图片

  12. 在topsort中, 首先对prereqs中的key排序, 再调用visitAll。 因为prereqs映射的是切片而不是更复杂的map, 所以数据的遍历次序是固定的, 这意味着你每次运行topsort得到的输出都是一样的。 topsort的输出结果如下:
    《Go语言圣经》学习笔记 第五章函数_第31张图片

  13. 让我们回到findLinks这个例子。 我们将代码移动到了links包下, 将函数重命名为Extract, 在第八章我们会再次用到这个函数。 新的匿名函数被引入, 用于替换原来的visit函数。 该匿名函数负责将新连接添加到切片中。 在Extract中, 使用forEachNode遍历HTML页面, 由于Extract只需要在遍历结点前操作结点, 所以forEachNode的post参数被传入nil。

  14. gopl.io/ch5/links

    // Package links provides a link-extraction function.
    package links
    
    import (
    	"fmt"
    	"net/http"
    
    	"golang.org/x/net/html"
    )
    
    // Extract makes an HTTP GET request to the specified URL, parses
    // the response as HTML, and returns the links in the HTML document.
    func Extract(url string) ([]string, error) {
    	resp, err := http.Get(url)
    	if err != nil {
    		return nil, err
    	}
    	if resp.StatusCode != http.StatusOK {
    		resp.Body.Close()
    		return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
    	}
    
    	doc, err := html.Parse(resp.Body)
    	resp.Body.Close()
    	if err != nil {
    		return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
    	}
    
    	var links []string
    	visitNode := func(n *html.Node) {
    		if n.Type == html.ElementNode && n.Data == "a" {
    			for _, a := range n.Attr {
    				if a.Key != "href" {
    					continue
    				}
    				link, err := resp.Request.URL.Parse(a.Val)
    				if err != nil {
    					continue // ignore bad URLs
    				}
    				links = append(links, link.String())
    			}
    		}
    	}
    	forEachNode(doc, visitNode, nil)
    	return links, nil
    }
    
  15. 上面的代码对之前的版本做了改进, 现在links中存储的不是href属性的原始值, 而是通过resp.Request.URL解析后的值。 解析后, 这些连接以绝对路径的形式存在, 可以直接被http.Get访问。

  16. 网页抓取的核心问题就是如何遍历图。 在topoSort的例子中, 已经展示了深度优先遍历, 在网页抓取中, 我们会展示如何用广度优先遍历图。 在第8章, 我们会介绍如何将深度优先和广度优先结合使用。

  17. 下面的函数实现了广度优先算法。 调用者需要输入一个初始的待访问列表和一个函数f。 待访问列表中的每个元素被定义为string类型。 广度优先算法会为每个元素调用一次f。 每次f执行完毕后, 会返回一组待访问元素。 这些元素会被加入到待访问列表中。 当待访问列表中的所有元素都被访问后, breadthFirst函数运行结束。 为了避免同一个元素被访问两次, 代码中维护了一个map。

  18. gopl.io/ch5/findlinks3
    《Go语言圣经》学习笔记 第五章函数_第32张图片

  19. 就像我们在章节3解释的那样, append的参数“f(item)…”, 会将f返回的一组元素一个个添加到worklist中。

  20. 在我们网页抓取器中, 元素的类型是url。 crawl函数会将URL输出, 提取其中的新链接, 并将这些新链接返回。 我们会将crawl作为参数传递给breadthFirst。
    《Go语言圣经》学习笔记 第五章函数_第33张图片

  21. 为了使抓取器开始运行, 我们用命令行输入的参数作为初始的待访问url。
    《Go语言圣经》学习笔记 第五章函数_第34张图片

  22. 让我们从 https://golang.org 开始, 下面是程序的输出结果:
    《Go语言圣经》学习笔记 第五章函数_第35张图片

  23. 当所有发现的链接都已经被访问或电脑的内存耗尽时, 程序运行结束。

1. 警告: 捕获迭代变量

  1. 本节, 将介绍Go词法作用域的一个陷阱。 请务必仔细的阅读, 弄清楚发生问题的原因。 即使是经验丰富的程序员也会在这个问题上犯错误。
  2. 考虑这个样一个问题: 你被要求首先创建一些目录, 再将目录删除。 在下面的例子中我们用函数值来完成删除操作。 下面的示例代码需要引入os包。 为了使代码简单, 我们忽略了所有的异常处理。
    《Go语言圣经》学习笔记 第五章函数_第36张图片
  3. 你可能会感到困惑, 为什么要在循环体中用循环变量d赋值一个新的局部变量, 而不是像下面的代码一样直接使用循环变量dir。 需要注意, 下面的代码是错误的。
    《Go语言圣经》学习笔记 第五章函数_第37张图片
  4. 问题的原因在于循环变量的作用域。 在上面的程序中, for循环语句引入了新的词法块, 循环变量dir在这个词法块中被声明。 在该循环中生成的所有函数值都共享相同的循环变量。 需要注意, 函数值中记录的是循环变量的内存地址, 而不是循环变量某一时刻的值。 以dir为例,后续的迭代会不断更新dir的值, 当删除操作执行时, for循环已完成, dir中存储的值等于最后一次迭代的值。 这意味着, 每次对os.RemoveAll的调用删除的都是相同的目录。
  5. 通常, 为了解决这个问题, 我们会引入一个与循环变量同名的局部变量, 作为循环变量的副本。 比如下面的变量dir, 虽然这看起来很奇怪, 但却很有用。
    《Go语言圣经》学习笔记 第五章函数_第38张图片
  6. 这个问题不仅存在基于range的循环, 在下面的例子中, 对循环变量i的使用也存在同样的问题:
    《Go语言圣经》学习笔记 第五章函数_第39张图片
  7. 如果你使用go语句( 第八章) 或者defer语句( 5.8节) 会经常遇到此类问题。 这不是go或defer本身导致的, 而是因为它们都会等待循环结束后, 再执行函数值。

7. 可变参数

  1. 参数数量可变的函数称为为可变参数函数。 典型的例子就是fmt.Printf和类似函数。 Printf首先接收一个的必备参数, 之后接收任意个数的后续参数。
  2. 在声明可变参数函数时, 需要在参数列表的最后一个参数类型之前加上省略符号“…”, 这表示该函数会接收任意数量的该类型参数。
  3. gopl.io/ch5/sum
    《Go语言圣经》学习笔记 第五章函数_第40张图片
  4. sum函数返回任意个int型参数的和。 在函数体中,vals被看作是类型为[] int的切片。 sum可以接收任意数量的int型参数:
    在这里插入图片描述
  5. 在上面的代码中, 调用者隐式的创建一个数组, 并将原始参数复制到数组中, 再把数组的一个切片作为参数传给被调函数。 如果原始参数已经是切片类型, 我们该如何传递给sum? 只需在最后一个参数后加上省略符。 下面的代码功能与上个例子中最后一条语句相同。
    6.
  6. 虽然在可变参数函数内部, …int 型参数的行为看起来很像切片类型, 但实际上, 可变参数函数和以切片作为参数的函数是不同的。
    《Go语言圣经》学习笔记 第五章函数_第41张图片
  7. 可变参数函数经常被用于格式化字符串。 下面的errorf函数构造了一个以行号开头的, 经过格式化的错误信息。 函数名的后缀f是一种通用的命名规范, 代表该可变参数函数可以接收Printf风格的格式化字符串。
    《Go语言圣经》学习笔记 第五章函数_第42张图片
  8. interfac{}表示函数的最后一个参数可以接收任意类型, 我们会在第7章详细介绍。

8. Deferred函数

  1. 在findLinks的例子中, 我们用http.Get的输出作为html.Parse的输入。 只有url的内容的确是HTML格式的, html.Parse才可以正常工作, 但实际上, url指向的内容很丰富, 可能是图片,纯文本或是其他。 将这些格式的内容传递给html.parse, 会产生不良后果。
  2. 下面的例子获取HTML页面并输出页面的标题。 title函数会检查服务器返回的Content-Type字段, 如果发现页面不是HTML, 将终止函数运行, 返回错误。
  3. gopl.io/ch5/title1
    《Go语言圣经》学习笔记 第五章函数_第43张图片
  4. 下面展示了运行效果:
    《Go语言圣经》学习笔记 第五章函数_第44张图片
  5. resp.Body.close调用了多次, 这是为了确保title在所有执行路径下( 即使函数运行失败) 都关闭了网络连接。 随着函数变得复杂, 需要处理的错误也变多, 维护清理逻辑变得越来越困难。 而Go语言独有的defer机制可以让事情变得简单。
  6. 你只需要在调用普通函数或方法前加上关键字defer, 就完成了defer所需要的语法。 当defer语句被执行时, 跟在defer后面的函数会被延迟执行。 直到包含该defer语句的函数执行完毕时,defer后的函数才会被执行, 不论包含defer语句的函数是通过return正常结束, 还是由于panic导致的异常结束。 你可以在一个函数中执行多条defer语句, 它们的执行顺序与声明顺序相反。
  7. defer语句经常被用于处理成对的操作, 如打开、 关闭、 连接、 断开连接、 加锁、 释放锁。 通过defer机制, 不论函数逻辑多复杂, 都能保证在任何执行路径下, 资源被释放。 释放资源的defer应该直接跟在请求资源的语句后。 在下面的代码中, 一条defer语句替代了之前的所有resp.Body.Close
  8. gopl.io/ch5/title2
    《Go语言圣经》学习笔记 第五章函数_第45张图片
  9. 在处理其他资源时, 也可以采用defer机制, 比如对文件的操作:
  10. io/ioutil
    《Go语言圣经》学习笔记 第五章函数_第46张图片
  11. 或是处理互斥锁( 9.2章)
    《Go语言圣经》学习笔记 第五章函数_第47张图片
  12. 调试复杂程序时, defer机制也常被用于记录何时进入和退出函数。 下例中的bigSlowOperation函数, 直接调用trace记录函数的被调情况。 bigSlowOperation被调时,trace会返回一个函数值, 该函数值会在bigSlowOperation退出时被调用。 通过这种方式, 我们可以只通过一条语句控制函数的入口和所有的出口, 甚至可以记录函数的运行时间, 如例子中的start。 需要注意一点: 不要忘记defer语句后的圆括号, 否则本该在进入时执行的操作会在退出时执行, 而本该在退出时执行的, 永远不会被执行。
  13. gopl.io/ch5/trace
    《Go语言圣经》学习笔记 第五章函数_第48张图片
  14. 每一次bigSlowOperation被调用, 程序都会记录函数的进入, 退出, 持续时间。 ( 我们用time.Sleep模拟一个耗时的操作)
    《Go语言圣经》学习笔记 第五章函数_第49张图片
  15. 我们知道, defer语句中的函数会在return语句更新返回值变量后再执行, 又因为在函数中定义的匿名函数可以访问该函数包括返回值变量在内的所有变量, 所以, 对匿名函数采用defer机制, 可以使其观察函数的返回值。
  16. 以double函数为例:
    在这里插入图片描述
  17. 我们只需要首先命名double的返回值, 再增加defer语句, 我们就可以在double每次被调用时, 输出参数以及返回值。
    《Go语言圣经》学习笔记 第五章函数_第50张图片
  18. 可能doulbe函数过于简单, 看不出这个小技巧的作用, 但对于有许多return语句的函数而言,这个技巧很有用。
  19. 被延迟执行的匿名函数甚至可以修改函数返回给调用者的返回值:
    《Go语言圣经》学习笔记 第五章函数_第51张图片
  20. 在循环体中的defer语句需要特别注意, 因为只有在函数执行完毕后, 这些被延迟的函数才会执行。 下面的代码会导致系统的文件描述符耗尽, 因为在所有文件都被处理之前, 没有文件会被关闭。
    《Go语言圣经》学习笔记 第五章函数_第52张图片
  21. 一种解决方法是将循环体中的defer语句移至另外一个函数。 在每次循环时, 调用这个函数。
    《Go语言圣经》学习笔记 第五章函数_第53张图片
  22. 下面的代码是fetch( 1.5节) 的改进版, 我们将http响应信息写入本地文件而不是从标准输出流输出。 我们通过path.Base提出url路径的最后一段作为文件名。
  23. gopl.io/ch5/fetch
    《Go语言圣经》学习笔记 第五章函数_第54张图片
  24. 对resp.Body.Close延迟调用我们已经见过了, 在此不做解释。 上例中, 通过os.Create打开文件进行写入, 在关闭文件时, 我们没有对f.close采用defer机制, 因为这会产生一些微妙的错误。 许多文件系统, 尤其是NFS, 写入文件时发生的错误会被延迟到文件关闭时反馈。 如果没有检查文件关闭时的反馈信息, 可能会导致数据丢失, 而我们还误以为写入操作成功。 如果io.Copy和f.close都失败了, 我们倾向于将io.Copy的错误信息反馈给调用者, 因为它先于f.close发生, 更有可能接近问题的本质。

9. Panic异常

  1. Go的类型系统会在编译时捕获很多错误, 但有些错误只能在运行时检查, 如数组访问越界、空指针引用等。 这些运行时错误会引起painc异常。
  2. 一般而言, 当panic异常发生时, 程序会中断运行, 并立即执行在该goroutine( 可以先理解成线程, 在第8章会详细介绍) 中被延迟的函数( defer 机制) 。 随后, 程序崩溃并输出日志信息。 日志信息包括panic value和函数调用的堆栈跟踪信息。 panic value通常是某种错误信息。 对于每个goroutine, 日志信息中都会有与之相对的, 发生panic时的函数调用堆栈跟踪信息。 通常, 我们不需要再次运行程序去定位问题, 日志信息已经提供了足够的诊断依据。 因此, 在我们填写问题报告时, 一般会将panic异常和日志信息一并记录。
  3. 不是所有的panic异常都来自运行时, 直接调用内置的panic函数也会引发panic异常; panic函数接受任何值作为参数。 当某些不应该发生的场景发生时, 我们就应该调用panic。 比如, 当程序到达了某条逻辑上不可能到达的路径:
    《Go语言圣经》学习笔记 第五章函数_第55张图片
  4. 断言函数必须满足的前置条件是明智的做法, 但这很容易被滥用。 除非你能提供更多的错误信息, 或者能更快速的发现错误, 否则不需要使用断言, 编译器在运行时会帮你检查代码。
    《Go语言圣经》学习笔记 第五章函数_第56张图片
  5. 虽然Go的panic机制类似于其他语言的异常, 但panic的适用场景有一些不同。 由于panic会引起程序的崩溃, 因此panic一般用于严重错误, 如程序内部的逻辑不一致。 勤奋的程序员认为任何崩溃都表明代码中存在漏洞, 所以对于大部分漏洞, 我们应该使用Go提供的错误机制,而不是panic, 尽量避免程序的崩溃。 在健壮的程序中, 任何可以预料到的错误, 如不正确的输入、 错误的配置或是失败的I/O操作都应该被优雅的处理, 最好的处理方式, 就是使用Go的错误机制。
  6. 考虑regexp.Compile函数, 该函数将正则表达式编译成有效的可匹配格式。 当输入的正则表达式不合法时, 该函数会返回一个错误。 当调用者明确的知道正确的输入不会引起函数错误时, 要求调用者检查这个错误是不必要和累赘的。 我们应该假设函数的输入一直合法, 就如前面的断言一样: 当调用者输入了不应该出现的输入时, 触发panic异常。
  7. 在程序源码中, 大多数正则表达式是字符串字面值( string literals) , 因此regexp包提供了包装函数regexp.MustCompile检查输入的合法性。
    《Go语言圣经》学习笔记 第五章函数_第57张图片
  8. 包装函数使得调用者可以便捷的用一个编译后的正则表达式为包级别的变量赋值:
    在这里插入图片描述
  9. 显然, MustCompile不能接收不合法的输入。 函数名中的Must前缀是一种针对此类函数的命名约定, 比如template.Must( 4.6节)
    《Go语言圣经》学习笔记 第五章函数_第58张图片
  10. 当f(0)被调用时, 发生panic异常, 之前被延迟执行的的3个fmt.Printf被调用。 程序中断执行后, panic信息和堆栈信息会被输出( 下面是简化的输出) :
    《Go语言圣经》学习笔记 第五章函数_第59张图片
  11. 我们在下一节将看到, 如何使程序从panic异常中恢复, 阻止程序的崩溃。
  12. 为了方便诊断问题, runtime包允许程序员输出堆栈信息。 在下面的例子中, 我们通过在main函数中延迟调用printStack输出堆栈信息。
    《Go语言圣经》学习笔记 第五章函数_第60张图片
  13. printStack的简化输出如下( 下面只是printStack的输出, 不包括panic的日志信息) :
    《Go语言圣经》学习笔记 第五章函数_第61张图片
  14. 将panic机制类比其他语言异常机制的读者可能会惊讶, runtime.Stack为何能输出已经被释放函数的信息? 在Go的panic机制中, 延迟函数的调用在释放堆栈信息之前。

10. Recover捕获异常

  1. 通常来说, 不应该对panic异常做任何处理, 但有时, 也许我们可以从异常中恢复, 至少我们可以在程序崩溃前, 做一些操作。 举个例子, 当web服务器遇到不可预料的严重问题时, 在崩溃前应该将所有的连接关闭; 如果不做任何处理, 会使得客户端一直处于等待状态。 如果web服务器还在开发阶段, 服务器甚至可以将异常信息反馈到客户端, 帮助调试。
  2. 如果在deferred函数中调用了内置函数recover, 并且定义该defer语句的函数发生了panic异常, recover会使程序从panic中恢复, 并返回panic value。 导致panic异常的函数不会继续运行, 但能正常返回。 在未发生panic时调用recover, recover会返回nil。
  3. 让我们以语言解析器为例, 说明recover的使用场景。 考虑到语言解析器的复杂性, 即使某个语言解析器目前工作正常, 也无法肯定它没有漏洞。 因此, 当某个异常出现时, 我们不会选择让解析器崩溃, 而是会将panic异常当作普通的解析错误, 并附加额外信息提醒用户报告此错误。
    《Go语言圣经》学习笔记 第五章函数_第62张图片
  4. deferred函数帮助Parse从panic中恢复。 在deferred函数内部, panic value被附加到错误信息中; 并用err变量接收错误信息, 返回给调用者。 我们也可以通过调用runtime.Stack往错误信息中添加完整的堆栈调用信息。
  5. 不加区分的恢复所有的panic异常, 不是可取的做法; 因为在panic之后, 无法保证包级变量的状态仍然和我们预期一致。 比如, 对数据结构的一次重要更新没有被完整完成、 文件或者网络连接没有被关闭、 获得的锁没有被释放。 此外, 如果写日志时产生的panic被不加区分的恢复, 可能会导致漏洞被忽略。
  6. 虽然把对panic的处理都集中在一个包下, 有助于简化对复杂和不可以预料问题的处理, 但作为被广泛遵守的规范, 你不应该试图去恢复其他包引起的panic。 公有的API应该将函数的运行失败作为error返回, 而不是panic。 同样的, 你也不应该恢复一个由他人开发的函数引起的panic, 比如说调用者传入的回调函数, 因为你无法确保这样做是安全的。
  7. 有时我们很难完全遵循规范, 举个例子, net/http包中提供了一个web服务器, 将收到的请求分发给用户提供的处理函数。 很显然, 我们不能因为某个处理函数引发的panic异常, 杀掉整个进程; web服务器遇到处理函数导致的panic时会调用recover, 输出堆栈信息, 继续运行。
  8. 这样的做法在实践中很便捷, 但也会引起资源泄漏, 或是因为recover操作, 导致其他问题。
  9. 基于以上原因, 安全的做法是有选择性的recover。 换句话说, 只恢复应该被恢复的panic异常, 此外, 这些异常所占的比例应该尽可能的低。 为了标识某个panic是否应该被恢复, 我们可以将panic value设置成特殊类型。 在recover时对panic value进行检查, 如果发现panicvalue是特殊类型, 就将这个panic作为errror处理, 如果不是, 则按照正常的panic进行处理( 在下面的例子中, 我们会看到这种方式) 。
  10. 下面的例子是title函数的变形, 如果HTML页面包含多个</code>, 该函数会给调用者返回一个错误( error) 。 在soleTitle内部处理时, 如果检测到有多个<code><title></code>, 会调用panic, 阻止函数继续递归, 并将特殊类型bailout作为panic的参数。<br> <a href="http://img.e-com-net.com/image/info8/0c102993640a4fcd83e6e581a88c274b.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/0c102993640a4fcd83e6e581a88c274b.jpg" alt="《Go语言圣经》学习笔记 第五章函数_第63张图片" width="650" height="665" style="border:1px solid black;"></a></li> <li>在上例中, deferred函数调用recover, 并检查panic value。 当panic value是bailout{}类型时,deferred函数生成一个error返回给调用者。 当panic value是其他non-nil值时, 表示发生了未知的panic异常, deferred函数将调用panic函数并将当前的panic value作为参数传入; 此时, 等同于recover没有做任何操作。 ( 请注意: 在例子中, 对可预期的错误采用了panic, 这违反了之前的建议, 我们在此只是想向读者演示这种机制。 )</li> <li>有些情况下, 我们无法恢复。 某些致命错误会导致Go在运行时终止程序, 如内存不足。</li> </ol> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1277314277471961088"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(Go)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1898669053208227840.htm" title="Google Java 编程风格及编码标准指南" target="_blank">Google Java 编程风格及编码标准指南</a> <span class="text-muted">yaoxin521123</span> <a class="tag" taget="_blank" href="/search/%E3%80%90Java/1.htm">【Java</a><a class="tag" taget="_blank" href="/search/%E4%BD%BF%E7%94%A8%E6%96%B9%E6%A1%88%E3%80%91/1.htm">使用方案】</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E8%A7%84%E8%8C%83/1.htm">编程规范</a> <div>文章目录GoogleJava编程风格及编码标准指南前言1简介1.1术语说明1.2指南说明2源文件基础2.1文件名2.2文件编码:`UTF-8`2.3特殊字符2.3.1空白字符2.3.2特殊转义序列2.3.3非ASCII字符3源文件结构3.1许可证/版权信息3.2`package`语句3.3`import`语句3.3.1禁止通配符导入3.3.2禁止换行3.3.3排序与间距3.3.4静态嵌套类导入3.</div> </li> <li><a href="/article/1898663755957465088.htm" title="设计模式-行为型模式-中介者模式" target="_blank">设计模式-行为型模式-中介者模式</a> <span class="text-muted">繁星璀璨G</span> <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F/1.htm">行为型模式</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E4%BB%8B%E8%80%85%E6%A8%A1%E5%BC%8F/1.htm">中介者模式</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a> <div>工程源码:c++设计模式-行为型模式-中介者模式https://download.csdn.net/download/qq_40788199/85763979码云:设计模式-行为型模式-中介者模式https://gitee.com/gongguixing/c-design-mode.git1、模式的定义与特点中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对</div> </li> <li><a href="/article/1898656454336901120.htm" title="mysql数据库报连接超时" target="_blank">mysql数据库报连接超时</a> <span class="text-muted"></span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>Thelastpacketsuccessfullyreceivedfromtheserverwas10,069millisecondsago.Thelastpacketsentsuccessfullytotheserverwas10,082millisecondsago。解决办法:在数据库连接的url后面加上:autoReconnect=true&socketTimeout=60000&conne</div> </li> <li><a href="/article/1898651411164491776.htm" title="《Operating System Concepts》阅读笔记:p208-p227" target="_blank">《Operating System Concepts》阅读笔记:p208-p227</a> <span class="text-muted"></span> <a class="tag" taget="_blank" href="/search/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/1.htm">操作系统</a> <div>《OperatingSystemConcepts》学习第24天,p208-p227总结,总计20页。一、技术总结1.vmstatLinux系统上vmstat命令的作用是“Reportvirtualmemorystatistics”。2.schedulingalgorithms(1)FCFS(first-comefirst-serve)(2)SJF(shortest-job-first)准确的叫法应</div> </li> <li><a href="/article/1898646347465158656.htm" title="Ubuntu下搭建本地的gogs(git管理工具)" target="_blank">Ubuntu下搭建本地的gogs(git管理工具)</a> <span class="text-muted">太极__</span> <a class="tag" taget="_blank" href="/search/php%E6%8A%80%E6%9C%AF/1.htm">php技术</a><a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E5%BF%83%E5%BE%97/1.htm">项目心得</a><a class="tag" taget="_blank" href="/search/gogs%28git%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7%29/1.htm">gogs(git管理工具)</a> <div>Ubuntu下搭建本地的gogs(git管理工具)1.首先安装docker容器:sudoapt-getinstalldockerdocer-cedocker-engine2.docker配置镜像加速:镜像加速鉴于国内网络问题,后续拉取Docker镜像十分缓慢,我们可以需要配置加速器来解决,我使用的是网易的镜像地址:http://hub-mirror.c.163.com。新版的Docker使用/et</div> </li> <li><a href="/article/1898619368145743872.htm" title="算法每日一练 (9)" target="_blank">算法每日一练 (9)</a> <span class="text-muted">张胤尘</span> <a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95%E6%AF%8F%E6%97%A5%E4%B8%80%E7%BB%83/1.htm">算法每日一练</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a> <div>欢迎来到张胤尘的技术站技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌文章目录算法每日一练(9)最小路径和题目描述解题思路解题代码`c/c++``golang``lua`官方站点:力扣Leetcode算法每日一练(9)最小路径和题目地址:最小路径和题目描述给定一个包含非负整数的mxn网格grid,请找出一条从左上角到右下角的路径,使得路径上的数字总和为</div> </li> <li><a href="/article/1898610924432977920.htm" title="vue管理布局左侧菜单栏NavMenu" target="_blank">vue管理布局左侧菜单栏NavMenu</a> <span class="text-muted">liuyang___</span> <a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>我们接着上回的写,我们写一下左侧的菜单logo系统首页系统首页系统首页信息管理系统首页系统首页这就是左侧的代码,我加了一点样式,因为正常的都是上面有一个logo,下面再写菜单栏,我们写一下这个el-menu首先写一个el-menu,这个显然就是一级菜单,然后在里面写el-menu-item写出几项就可以了,然后我们再继续写二级菜单,就是el-submenu,这个东西吧,你可以在里面直接谢日el-m</div> </li> <li><a href="/article/1898598568667705344.htm" title="排序算法动画网站" target="_blank">排序算法动画网站</a> <span class="text-muted">齊 天 大 聖</span> <a class="tag" taget="_blank" href="/search/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/1.htm">排序算法</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div>排序算法动画网站(1)https://visualgo.net/zh(2)http://tools.jb51.net/aideddesign/paixu_ys(3)https://www.toptal.com/developers/sorting-algorithms(4)https://www.webhek.com/post/comparison-sort/(<-简单明了)</div> </li> <li><a href="/article/1898592502374526976.htm" title="H5播放webrtc视频" target="_blank">H5播放webrtc视频</a> <span class="text-muted"></span> <a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91%E5%A4%84%E7%90%86html5/1.htm">视频处理html5</a> <div>一、简介WebRTC概念WebRTC是由Google主导的,由一组标准、协议和JavaScriptAPI组成,用于实现浏览器之间(端到端之间)的音频、视频及数据共享。WebRTC不需要安装任何插件,通过简单的JavaScriptAPI就可以使得实时通信变成一种标准功能。为什么使用webrtc现在各大浏览器以及终已经逐渐加大对WebRTC技术的支持。下图是webrtc官网给出的现在已经提供支持了的浏</div> </li> <li><a href="/article/1898577008598773760.htm" title="10.1go常用包json" target="_blank">10.1go常用包json</a> <span class="text-muted">chxii</span> <a class="tag" taget="_blank" href="/search/go%E8%AF%AD%E8%A8%80%E5%9F%BA%E7%A1%80/1.htm">go语言基础</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>序列化(Marshal):将Go的数据结构转换为JSON字符串。反序列化(Unmarshal):将JSON字符串转换为Go的数据结构。使用json.Marshal进行序列化json.Marshal函数可以将Go的数据结构转换为JSON格式的字节切片([]byte)packagemainimport("encoding/json""fmt""log")//定义一个结构体typePersonstruc</div> </li> <li><a href="/article/1898558847992262656.htm" title="【uniapp】图片添加canvas水印" target="_blank">【uniapp】图片添加canvas水印</a> <span class="text-muted">sailven</span> <a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a><a class="tag" taget="_blank" href="/search/%E6%8B%8D%E7%85%A7/1.htm">拍照</a><a class="tag" taget="_blank" href="/search/%E5%9B%BE%E7%89%87%E6%B0%B4%E5%8D%B0/1.htm">图片水印</a><a class="tag" taget="_blank" href="/search/canvas/1.htm">canvas</a> <div>目录需求&背景实现地理位置添加水印ios补充需求&背景需求:拍照后给图片添加水印,水印包含经纬度、用户信息、公司logo等信息。效果图:方案:使用canvas添加水印。具体实现:上传图片组件是项目里现有的,主要还是使用uni.chooseImage,这里不做赘述。在上传图片组件中增加一个参数判断是否添加水印,在获取到图片、上传到后端之前对图片进行加工。实现在模板中添加canvas。template</div> </li> <li><a href="/article/1898556323511988224.htm" title="初识Django(四):工程文件功能说明" target="_blank">初识Django(四):工程文件功能说明</a> <span class="text-muted">公众号:重生之成为赛博女保安</span> <a class="tag" taget="_blank" href="/search/django/1.htm">django</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>目录工程文件总览工程文件夹djangoexample文件解析工程子文件夹djangoexample文件解析应用子文件夹helloworld文件解析工程文件总览打开我们在教程一里创建的带app“helloworld”的Django工程“djangoexample”:djangoexample│manage.py│├─djangoexample││asgi.py││settings.py││urls.</div> </li> <li><a href="/article/1898551403152994304.htm" title="Python项目-基于Django的在线教育平台开发" target="_blank">Python项目-基于Django的在线教育平台开发</a> <span class="text-muted">天天进步2015</span> <a class="tag" taget="_blank" href="/search/Python%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/1.htm">Python项目实战</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>1.项目概述在线教育平台已成为现代教育的重要组成部分,特别是在后疫情时代,远程学习的需求显著增加。本文将详细介绍如何使用Python的Django框架开发一个功能完善的在线教育平台,包括系统设计、核心功能实现以及部署上线等关键环节。本项目旨在创建一个集课程管理、视频播放、在线测验、学习进度跟踪和社区互动于一体的综合性教育平台,为教育机构和个人讲师提供一站式在线教学解决方案。2.技术栈选择2.1后端</div> </li> <li><a href="/article/1898543825392037888.htm" title="最新(四)Django学习——模板标签定义及语法:for循环" target="_blank">最新(四)Django学习——模板标签定义及语法:for循环</a> <span class="text-muted">2301_82243493</span> <a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">程序员</a><a class="tag" taget="_blank" href="/search/django/1.htm">django</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F/1.htm">状态模式</a> <div>fromdjango.contribimportadminfromdjango.urlsimportpathfrom.importviewsurlpatterns=[#子路由path(‘test01/’,views.test01),path(‘sing/’,views.sing,{“name”:“小明”,“age”:18},name=“bb”),]②对应的前端html模板文件test02.html</div> </li> <li><a href="/article/1898543698044579840.htm" title="Django学习实战篇一(适合略有基础的新手小白学习)(从0开发项目)" target="_blank">Django学习实战篇一(适合略有基础的新手小白学习)(从0开发项目)</a> <span class="text-muted">不染_是非</span> <a class="tag" taget="_blank" href="/search/Django/1.htm">Django</a><a class="tag" taget="_blank" href="/search/django/1.htm">django</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>前言:  本系列博客将带大家从0开始做一个简单的博客管理系统。完整代码在github上。本项目将用django4.2版本和python3.11版本带大家实现完整开发过程。  在学习django过程中,绝大部分的教学和讲解采用的都是老版本的django(1.x,2.x,3.2)和python(3.6),目前最新django版本为5.1,python版本也到了3.12了。对于django版本而言,1.</div> </li> <li><a href="/article/1898542178712481792.htm" title="Django系列教程(5)——Django模型详解" target="_blank">Django系列教程(5)——Django模型详解</a> <span class="text-muted">l软件定制开发工作室</span> <a class="tag" taget="_blank" href="/search/Django%E6%95%99%E7%A8%8B/1.htm">Django教程</a><a class="tag" taget="_blank" href="/search/django/1.htm">django</a> <div>目录模型定义小案例模型的组成模型的字段基础字段关系字段on_delete删除选项related_name选项模型的META选项模型的方法标准方法示例一:自定义方法示例二:自定义Manager方法完美的高级Django模型示例小结Model(模型)简而言之即数据模型,是一个Django应用的核心。模型不是数据本身(比如数据表里的数据),而是抽象的描述数据的构成和逻辑关系。每个Django的模型(mo</div> </li> <li><a href="/article/1898540664447102976.htm" title="搭建测试用的redis集群 访问失败小记" target="_blank">搭建测试用的redis集群 访问失败小记</a> <span class="text-muted">yinhezhanshen</span> <a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a> <div>redis运行在virtualbox虚拟机的ubuntu中。golang编写的redis客户端代码运行在windows下。首先在ubuntu下启动redis集群下载redis源码,编译成功后,进入utils/create-cluster目录,运行./create-clusterstart./create-clustercreate在windows下编写访问redis代码packagemainimp</div> </li> <li><a href="/article/1898536871277948928.htm" title="golang中实现LRU-K算法(附带单元测试)" target="_blank">golang中实现LRU-K算法(附带单元测试)</a> <span class="text-muted">我的鱼干呢w</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/lru/1.htm">lru</a><a class="tag" taget="_blank" href="/search/lru-k/1.htm">lru-k</a> <div>LRU-K中的K代表最近使用的次数,因此LRU可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。LR</div> </li> <li><a href="/article/1898533185105489920.htm" title="在Go语言中,判断变量是否为“空”(零值或未初始化状态)的方法总结" target="_blank">在Go语言中,判断变量是否为“空”(零值或未初始化状态)的方法总结</a> <span class="text-muted">半桶水专家</span> <a class="tag" taget="_blank" href="/search/golang%E5%85%A5%E9%97%A8/1.htm">golang入门</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>在Go语言中,判断变量是否为“空”(零值或未初始化状态)的方法因数据类型而异。以下是各类型变量的判断方法总结:1.基本类型整数(int)判断是否等于零值0。variintifi==0{//空}字符串(string)判断是否等于空字符串""。varsstringifs==""{//空}布尔(bool)零值为false,直接判断是否为false。varbboolif!b{//空}2.引用类型指针(*T</div> </li> <li><a href="/article/1898532679553445888.htm" title="golang 从零单排 (一) 安装环境" target="_blank">golang 从零单排 (一) 安装环境</a> <span class="text-muted">hamburgerDaddy1</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E4%BB%8E%E9%9B%B6%E5%8D%95%E6%8E%92/1.htm">从零单排</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>1.下载安装打开网址TheGoProgrammingLanguage直接点击下载go1.24.1.windows-amd64.msi下载完成直接双击下一步下一步安装完成环境变量自动设置不必配置2.验证win+r输入cmd打开命令行输入goversion</div> </li> <li><a href="/article/1898528767131840512.htm" title="golang深度学习-性能分析" target="_blank">golang深度学习-性能分析</a> <span class="text-muted">老狼伙计</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/1.htm">编程语言</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E5%8E%9F%E7%94%9F%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">云原生学习笔记</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>性能分析Go语言原生支持在运行时保留重要的特征指标和状态,有许多工具可以分析甚至可视化程序运行的状态和过程。pprof工具:常用于分析资源的使用情况,可以采集程序运行时的多种不同类型的数据(例如CPU占用、内存消耗和协程数量等),并对数据进行分析聚合生成的报告。trace工具:则关注程序运行时的事件(例如协程状态切换,GC的开始和结束、系统调用等等),常用于分析延迟、阻塞和调度等问题。pprof工</div> </li> <li><a href="/article/1898521581097906176.htm" title="常见开源许可证" target="_blank">常见开源许可证</a> <span class="text-muted">WwwwwH_PLUS</span> <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Software/1.htm">Software</a><a class="tag" taget="_blank" href="/search/Engineering/1.htm">Engineering</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90/1.htm">开源</a> <div>常见开源许可证许可证允许商业使用是否必须开源适用场景MIT✅是❌否最宽松,适合开源库(如OCCT相关项目)GPL✅是✅必须强制开源,适合Linux、GNU项目LGPL✅是☑️仅限修改核心部分适用于动态库(如Qt、FFmpeg)Apache2.0✅是❌否允许专利权,适合企业(如Google)BSD3-Clause✅是❌否类似MIT,但限制广告使用</div> </li> <li><a href="/article/1898519805523193856.htm" title="从零开始实现一个完整的购物网站 Django vs Flask 究竟哪个更适合初学者?" target="_blank">从零开始实现一个完整的购物网站 Django vs Flask 究竟哪个更适合初学者?</a> <span class="text-muted">AI天才研究院</span> <a class="tag" taget="_blank" href="/search/Python%E5%AE%9E%E6%88%98/1.htm">Python实战</a><a class="tag" taget="_blank" href="/search/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/1.htm">自然语言处理</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/1.htm">语言模型</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E5%AE%9E%E8%B7%B5/1.htm">编程实践</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/1.htm">架构设计</a> <div>作者:禅与计算机程序设计艺术1.简介在Web开发领域,Django和Flask都是最流行的Pythonweb框架,这两款框架各有千秋,但是两者之间到底选哪个才是适合初学者入门的呢?为了让大家能够有一个更直观的认识,本文将对Django和Flask进行详细比较并结合自己的实际经验给出建议,帮助读者更好地理解两者之间的区别、优劣势,并且告诉读者如何选择合适自己的框架,进行快速开发、部署、维护等。本文根</div> </li> <li><a href="/article/1898518155089735680.htm" title="Godot4.3 显示像素风格图片模糊如何设置?" target="_blank">Godot4.3 显示像素风格图片模糊如何设置?</a> <span class="text-muted">松树戈</span> <a class="tag" taget="_blank" href="/search/Godot/1.htm">Godot</a><a class="tag" taget="_blank" href="/search/godot/1.htm">godot</a> <div>Godot4.3显示像素风格图片模糊如何设置?使用Godot4.3显示低分辨率像素风图片时比较模糊在ProjectSettings>Rendering>Textures>CanvasTextures设置`Nearest`(或`NearestMipmap`)解决效果使用Godot4.3显示低分辨率像素风图片时比较模糊在ProjectSettings>Rendering>Textures>Canvas</div> </li> <li><a href="/article/1898503379047477248.htm" title="linux git 搭建 debian,如何在Debian服务器上安装公共Git存储库" target="_blank">linux git 搭建 debian,如何在Debian服务器上安装公共Git存储库</a> <span class="text-muted">高江Takae</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a><a class="tag" taget="_blank" href="/search/%E6%90%AD%E5%BB%BA/1.htm">搭建</a><a class="tag" taget="_blank" href="/search/debian/1.htm">debian</a> <div>如何在Debian服务器上安装公共Git存储库Git是一个免费的分布式版本控制,最初由LinusTorvalds为Linux内核开发创建。它主要在Linux上开发,但可用于其他Unix操作系统,包括BSD,Solaris和Darwin。基于POSIX的系统,如Linux,Git非常快。它与svn不同,并在远程服务器上创建一个私有存储库。一些流行的项目使用Git:*YUI*Merb*DragonFl</div> </li> <li><a href="/article/1898491875250204672.htm" title="golang游戏开发学习笔记-开发一个简单的2D游戏(基础篇)" target="_blank">golang游戏开发学习笔记-开发一个简单的2D游戏(基础篇)</a> <span class="text-muted">2401_86638887</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a> <div>go-glexamplego-gl的示例代码二.基础概念这里涉及到的概念在之前的文章里基本上都有过介绍,不再赘述。不过大家有兴趣可以去看一看碰撞检测的一些算法实现三.依赖没有新增任何依赖四.资源准备我们创建的游戏世界里有两个地方需要用到纹理资源(贴图),一是组成世界的方块、二是游戏主角。由于方块是静态的,不需要动画效果,所以只需要一张贴图就可以了。而游戏主角则需要多张纹理图像来组成运动时的动画。要</div> </li> <li><a href="/article/1898489352472817664.htm" title="golang坐标转换 gomap3d库" target="_blank">golang坐标转换 gomap3d库</a> <span class="text-muted">研志必有功</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%9D%90%E6%A0%87%E8%BD%AC%E6%8D%A2/1.htm">坐标转换</a><a class="tag" taget="_blank" href="/search/%E5%A4%A9%E6%96%87/1.htm">天文</a> <div>gomap3dGo语言实现的多坐标系转换库,支持天文学/航天领域常用坐标系转换gomap3d特性支持7种坐标系互转:站心坐标系(AER)东北天坐标系(ENU)地心地固坐标系(ECEF)地心惯性坐标系(ECI)大地坐标系(WGS84)支持多种参考椭球体:WGS-84CGCS2000月球火星精确天文计算:儒略日计算格林威治恒星时ECI/ECEF时变转换安装gogetgithub.com/PingPon</div> </li> <li><a href="/article/1898485816993312768.htm" title="基于视场角,高度重叠率,选装角度的无人机区域巡检航线规划" target="_blank">基于视场角,高度重叠率,选装角度的无人机区域巡检航线规划</a> <span class="text-muted">1028_左傲杰</span> <a class="tag" taget="_blank" href="/search/%E6%97%A0%E4%BA%BA%E6%9C%BA/1.htm">无人机</a> <div>importmathfromshapely.geometryimportPolygon,LineStringdefcalculate_route_with_angle(area_coords,fov_angle,altitude,overlap,line_angle):defline_intersection(p1,p2,p3,p4):"""计算两条线段的交点"""line1=LineString</div> </li> <li><a href="/article/1898484914978877440.htm" title="无人机怎么设定航线_航拍测绘必学教程|无人机航线设计流程" target="_blank">无人机怎么设定航线_航拍测绘必学教程|无人机航线设计流程</a> <span class="text-muted">weixin_39568706</span> <a class="tag" taget="_blank" href="/search/%E6%97%A0%E4%BA%BA%E6%9C%BA%E6%80%8E%E4%B9%88%E8%AE%BE%E5%AE%9A%E8%88%AA%E7%BA%BF/1.htm">无人机怎么设定航线</a> <div>航线设计是制作高质量影像图的关键,是航拍测绘必学教程之一。航线需要根据测区的地形地貌来进行设计,必须为内业正射影像图的制作提供足够的重叠率,因此,无人机航线设计需要综合考虑各方面因素,以保障飞行安全和获取影像满足要求。1明确航测范围航线规划软件(地面站)的参考底图数据大多来源于谷歌地图。规划航线之前,有必要在GoogleEarth中确定项目航飞范围,了解测区地貌,并进行合理的飞行架次划分,优化航飞</div> </li> <li><a href="/article/1898482878166134784.htm" title="matlab 设置网格线为虚线" target="_blank">matlab 设置网格线为虚线</a> <span class="text-muted">phymat.nico</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%90%86%E6%96%B9%E6%B3%95/1.htm">数理方法</a> <div>gridon;set(gca,'GridLineStyle',':','GridColor','k','GridAlpha',1);https://blog.csdn.net/cursethegod/article/details/80359738</div> </li> <li><a href="/article/77.htm" title="算法 单链的创建与删除" target="_blank">算法 单链的创建与删除</a> <span class="text-muted">换个号韩国红果果</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div> 先创建结构体 struct student { int data; //int tag;//标记这是第几个 struct student *next; }; // addone 用于将一个数插入已从小到大排好序的链中 struct student *addone(struct student *h,int x){ if(h==NULL) //?????? </div> </li> <li><a href="/article/204.htm" title="《大型网站系统与Java中间件实践》第2章读后感" target="_blank">《大型网站系统与Java中间件实践》第2章读后感</a> <span class="text-muted">白糖_</span> <a class="tag" taget="_blank" href="/search/java%E4%B8%AD%E9%97%B4%E4%BB%B6/1.htm">java中间件</a> <div>       断断续续花了两天时间试读了《大型网站系统与Java中间件实践》的第2章,这章总述了从一个小型单机构建的网站发展到大型网站的演化过程---整个过程会遇到很多困难,但每一个屏障都会有解决方案,最终就是依靠这些个解决方案汇聚到一起组成了一个健壮稳定高效的大型系统。          看完整章内容,</div> </li> <li><a href="/article/331.htm" title="zeus持久层spring事务单元测试" target="_blank">zeus持久层spring事务单元测试</a> <span class="text-muted">deng520159</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/DAO/1.htm">DAO</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/jdbc/1.htm">jdbc</a> <div>今天把zeus事务单元测试放出来,让大家指出他的毛病, 1.ZeusTransactionTest.java 单元测试   package com.dengliang.zeus.webdemo.test; import java.util.ArrayList; import java.util.List; import org.junit.Test; import </div> </li> <li><a href="/article/458.htm" title="Rss 订阅 开发" target="_blank">Rss 订阅 开发</a> <span class="text-muted">周凡杨</span> <a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a><a class="tag" taget="_blank" href="/search/%E8%AE%A2%E9%98%85/1.htm">订阅</a><a class="tag" taget="_blank" href="/search/rss/1.htm">rss</a><a class="tag" taget="_blank" href="/search/%E8%A7%84%E8%8C%83/1.htm">规范</a> <div>                RSS是 Really Simple Syndication的缩写(对rss2.0而言,是这三个词的缩写,对rss1.0而言则是RDF Site Summary的缩写,1.0与2.0走的是两个体系)。   RSS</div> </li> <li><a href="/article/585.htm" title="分页查询实现" target="_blank">分页查询实现</a> <span class="text-muted">g21121</span> <a class="tag" taget="_blank" href="/search/%E5%88%86%E9%A1%B5%E6%9F%A5%E8%AF%A2/1.htm">分页查询</a> <div>在查询列表时我们常常会用到分页,分页的好处就是减少数据交换,每次查询一定数量减少数据库压力等等。 按实现形式分前台分页和服务器分页: 前台分页就是一次查询出所有记录,在页面中用js进行虚拟分页,这种形式在数据量较小时优势比较明显,一次加载就不必再访问服务器了,但当数据量较大时会对页面造成压力,传输速度也会大幅下降。 服务器分页就是每次请求相同数量记录,按一定规则排序,每次取一定序号直接的数据</div> </li> <li><a href="/article/712.htm" title="spring jms异步消息处理" target="_blank">spring jms异步消息处理</a> <span class="text-muted">510888780</span> <a class="tag" taget="_blank" href="/search/jms/1.htm">jms</a> <div>spring JMS对于异步消息处理基本上只需配置下就能进行高效的处理。其核心就是消息侦听器容器,常用的类就是DefaultMessageListenerContainer。该容器可配置侦听器的并发数量,以及配合MessageListenerAdapter使用消息驱动POJO进行消息处理。且消息驱动POJO是放入TaskExecutor中进行处理,进一步提高性能,减少侦听器的阻塞。具体配置如下: </div> </li> <li><a href="/article/839.htm" title="highCharts柱状图" target="_blank">highCharts柱状图</a> <span class="text-muted">布衣凌宇</span> <a class="tag" taget="_blank" href="/search/hightCharts/1.htm">hightCharts</a><a class="tag" taget="_blank" href="/search/%E6%9F%B1%E5%9B%BE/1.htm">柱图</a> <div>第一步:导入 exporting.js,grid.js,highcharts.js;第二步:写controller   @Controller@RequestMapping(value="${adminPath}/statistick")public class StatistickController {  private UserServi</div> </li> <li><a href="/article/966.htm" title="我的spring学习笔记2-IoC(反向控制 依赖注入)" target="_blank">我的spring学习笔记2-IoC(反向控制 依赖注入)</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/mvc/1.htm">mvc</a><a class="tag" taget="_blank" href="/search/Spring+%E6%95%99%E7%A8%8B/1.htm">Spring 教程</a><a class="tag" taget="_blank" href="/search/spring3+%E6%95%99%E7%A8%8B/1.htm">spring3 教程</a><a class="tag" taget="_blank" href="/search/Spring+%E5%85%A5%E9%97%A8/1.htm">Spring 入门</a> <div>IoC(反向控制 依赖注入)这是Spring提出来了,这也是Spring一大特色。这里我不用多说,我们看Spring教程就可以了解。当然我们不用Spring也可以用IoC,下面我将介绍不用Spring的IoC。 IoC不是框架,她是java的技术,如今大多数轻量级的容器都会用到IoC技术。这里我就用一个例子来说明: 如:程序中有 Mysql.calss 、Oracle.class 、SqlSe</div> </li> <li><a href="/article/1093.htm" title="TLS java简单实现" target="_blank">TLS java简单实现</a> <span class="text-muted">antlove</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/ssl/1.htm">ssl</a><a class="tag" taget="_blank" href="/search/keystore/1.htm">keystore</a><a class="tag" taget="_blank" href="/search/tls/1.htm">tls</a><a class="tag" taget="_blank" href="/search/secure/1.htm">secure</a> <div>  1. SSLServer.java package ssl; import java.io.FileInputStream; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import java.security.KeyStore; import </div> </li> <li><a href="/article/1220.htm" title="Zip解压压缩文件" target="_blank">Zip解压压缩文件</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/Zip%E6%A0%BC%E5%BC%8F%E8%A7%A3%E5%8E%8B/1.htm">Zip格式解压</a><a class="tag" taget="_blank" href="/search/Zip%E6%B5%81%E7%9A%84%E4%BD%BF%E7%94%A8/1.htm">Zip流的使用</a><a class="tag" taget="_blank" href="/search/%E6%96%87%E4%BB%B6%E8%A7%A3%E5%8E%8B/1.htm">文件解压</a> <div>   ZIP文件的解压缩实质上就是从输入流中读取数据。Java.util.zip包提供了类ZipInputStream来读取ZIP文件,下面的代码段创建了一个输入流来读取ZIP格式的文件; ZipInputStream in = new ZipInputStream(new FileInputStream(zipFileName));     &n</div> </li> <li><a href="/article/1347.htm" title="underscore.js 学习(一)" target="_blank">underscore.js 学习(一)</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/underscore/1.htm">underscore</a> <div>        工作中需要用到underscore.js,发现这是一个包括了很多基本功能函数的js库,里面有很多实用的函数。而且它没有扩展 javascript的原生对象。主要涉及对Collection、Object、Array、Function的操作。       学</div> </li> <li><a href="/article/1474.htm" title="java jvm常用命令工具——jstatd命令(Java Statistics Monitoring Daemon)" target="_blank">java jvm常用命令工具——jstatd命令(Java Statistics Monitoring Daemon)</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/jstatd/1.htm">jstatd</a> <div>1.介绍         jstatd是一个基于RMI(Remove Method Invocation)的服务程序,它用于监控基于HotSpot的JVM中资源的创建及销毁,并且提供了一个远程接口允许远程的监控工具连接到本地的JVM执行命令。         jstatd是基于RMI的,所以在运行jstatd的服务</div> </li> <li><a href="/article/1601.htm" title="【Spring框架三】Spring常用注解之Transactional" target="_blank">【Spring框架三】Spring常用注解之Transactional</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/transactional/1.htm">transactional</a> <div>Spring可以通过注解@Transactional来为业务逻辑层的方法(调用DAO完成持久化动作)添加事务能力,如下是@Transactional注解的定义:   /* * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version </div> </li> <li><a href="/article/1728.htm" title="我(程序员)的前进方向" target="_blank">我(程序员)的前进方向</a> <span class="text-muted">bitray</span> <a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">程序员</a> <div>作为一个普通的程序员,我一直游走在java语言中,java也确实让我有了很多的体会.不过随着学习的深入,java语言的新技术产生的越来越多,从最初期的javase,我逐渐开始转变到ssh,ssi,这种主流的码农,.过了几天为了解决新问题,webservice的大旗也被我祭出来了,又过了些日子jms架构的activemq也开始必须学习了.再后来开始了一系列技术学习,osgi,restful.....</div> </li> <li><a href="/article/1855.htm" title="nginx lua开发经验总结" target="_blank">nginx lua开发经验总结</a> <span class="text-muted">ronin47</span> <div>使用nginx lua已经两三个月了,项目接开发完毕了,这几天准备上线并且跟高德地图对接。回顾下来lua在项目中占得必中还是比较大的,跟PHP的占比差不多持平了,因此在开发中遇到一些问题备忘一下 1:content_by_lua中代码容量有限制,一般不要写太多代码,正常编写代码一般在100行左右(具体容量没有细心测哈哈,在4kb左右),如果超出了则重启nginx的时候会报 too long pa</div> </li> <li><a href="/article/1982.htm" title="java-66-用递归颠倒一个栈。例如输入栈{1,2,3,4,5},1在栈顶。颠倒之后的栈为{5,4,3,2,1},5处在栈顶" target="_blank">java-66-用递归颠倒一个栈。例如输入栈{1,2,3,4,5},1在栈顶。颠倒之后的栈为{5,4,3,2,1},5处在栈顶</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> import java.util.Stack; public class ReverseStackRecursive { /** * Q 66.颠倒栈。 * 题目:用递归颠倒一个栈。例如输入栈{1,2,3,4,5},1在栈顶。 * 颠倒之后的栈为{5,4,3,2,1},5处在栈顶。 *1. Pop the top element *2. Revers</div> </li> <li><a href="/article/2109.htm" title="正确理解Linux内存占用过高的问题" target="_blank">正确理解Linux内存占用过高的问题</a> <span class="text-muted">cfyme</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>Linux开机后,使用top命令查看,4G物理内存发现已使用的多大3.2G,占用率高达80%以上: Mem:   3889836k total,  3341868k used,   547968k free,   286044k buffers Swap:  6127608k total,&nb</div> </li> <li><a href="/article/2236.htm" title="[JWFD开源工作流]当前流程引擎设计的一个急需解决的问题" target="_blank">[JWFD开源工作流]当前流程引擎设计的一个急需解决的问题</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C%E6%B5%81/1.htm">工作流</a> <div>      当我们的流程引擎进入IRC阶段的时候,当循环反馈模型出现之后,每次循环都会导致一大堆节点内存数据残留在系统内存中,循环的次数越多,这些残留数据将导致系统内存溢出,并使得引擎崩溃。。。。。。       而解决办法就是利用汇编语言或者其它系统编程语言,在引擎运行时,把这些残留数据清除掉。</div> </li> <li><a href="/article/2363.htm" title="自定义类的equals函数" target="_blank">自定义类的equals函数</a> <span class="text-muted">dai_lm</span> <a class="tag" taget="_blank" href="/search/equals/1.htm">equals</a> <div>仅作笔记使用 public class VectorQueue { private final Vector<VectorItem> queue; private class VectorItem { private final Object item; private final int quantity; public VectorI</div> </li> <li><a href="/article/2490.htm" title="Linux下安装R语言" target="_blank">Linux下安装R语言</a> <span class="text-muted">datageek</span> <a class="tag" taget="_blank" href="/search/R%E8%AF%AD%E8%A8%80+linux/1.htm">R语言 linux</a> <div>命令如下:sudo gedit  /etc/apt/sources.list1、deb http://mirrors.ustc.edu.cn/CRAN/bin/linux/ubuntu/ precise/ 2、deb http://dk.archive.ubuntu.com/ubuntu hardy universesudo apt-key adv --keyserver ke</div> </li> <li><a href="/article/2617.htm" title="如何修改mysql 并发数(连接数)最大值" target="_blank">如何修改mysql 并发数(连接数)最大值</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>MySQL的连接数最大值跟MySQL没关系,主要看系统和业务逻辑了   方法一:进入MYSQL安装目录 打开MYSQL配置文件 my.ini 或 my.cnf查找 max_connections=100 修改为 max_connections=1000 服务里重起MYSQL即可   方法二:MySQL的最大连接数默认是100客户端登录:mysql -uusername -ppass</div> </li> <li><a href="/article/2744.htm" title="单一功能原则" target="_blank">单一功能原则</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/1.htm">面向对象的程序设计</a><a class="tag" taget="_blank" href="/search/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1/1.htm">软件设计</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E5%8E%9F%E5%88%99/1.htm">编程原则</a> <div>单一功能原则[ 编辑]     SOLID 原则 单一功能原则 开闭原则 Liskov代换原则 接口隔离原则 依赖反转原则 查   论   编 在面向对象编程领域中,单一功能原则(Single responsibility principle)规定每个类都应该有</div> </li> <li><a href="/article/2871.htm" title="POJO、VO和JavaBean区别和联系" target="_blank">POJO、VO和JavaBean区别和联系</a> <span class="text-muted">fanmingxing</span> <a class="tag" taget="_blank" href="/search/VO/1.htm">VO</a><a class="tag" taget="_blank" href="/search/POJO/1.htm">POJO</a><a class="tag" taget="_blank" href="/search/javabean/1.htm">javabean</a> <div>POJO和JavaBean是我们常见的两个关键字,一般容易混淆,POJO全称是Plain Ordinary Java Object / Plain Old Java Object,中文可以翻译成:普通Java类,具有一部分getter/setter方法的那种类就可以称作POJO,但是JavaBean则比POJO复杂很多,JavaBean是一种组件技术,就好像你做了一个扳子,而这个扳子会在很多地方被</div> </li> <li><a href="/article/2998.htm" title="SpringSecurity3.X--LDAP:AD配置" target="_blank">SpringSecurity3.X--LDAP:AD配置</a> <span class="text-muted">hanqunfeng</span> <a class="tag" taget="_blank" href="/search/SpringSecurity/1.htm">SpringSecurity</a> <div>前面介绍过基于本地数据库验证的方式,参考http://hanqunfeng.iteye.com/blog/1155226,这里说一下如何修改为使用AD进行身份验证【只对用户名和密码进行验证,权限依旧存储在本地数据库中】。   将配置文件中的如下部分删除: <!-- 认证管理器,使用自定义的UserDetailsService,并对密码采用md5加密--> </div> </li> <li><a href="/article/3125.htm" title="mac mysql 修改密码" target="_blank">mac mysql 修改密码</a> <span class="text-muted">IXHONG</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>$ sudo /usr/local/mysql/bin/mysqld_safe –user=root & //启动MySQL(也可以通过偏好设置面板来启动)$ sudo /usr/local/mysql/bin/mysqladmin -uroot password yourpassword //设置MySQL密码(注意,这是第一次MySQL密码为空的时候的设置命令,如果是修改密码,还需在-</div> </li> <li><a href="/article/3252.htm" title="设计模式--抽象工厂模式" target="_blank">设计模式--抽象工厂模式</a> <span class="text-muted">kerryg</span> <a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a> <div>抽象工厂模式:     工厂模式有一个问题就是,类的创建依赖于工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。我们采用抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。     总结:这个模式的好处就是,如果想增加一个功能,就需要做一个实现类,</div> </li> <li><a href="/article/3379.htm" title="评"高中女生军训期跳楼”" target="_blank">评"高中女生军训期跳楼”</a> <span class="text-muted">nannan408</span> <div>   首先,先抛出我的观点,各位看官少点砖头。那就是,中国的差异化教育必须做起来。    孔圣人有云:有教无类。不同类型的人,都应该有对应的教育方法。目前中国的一体化教育,不知道已经扼杀了多少创造性人才。我们出不了爱迪生,出不了爱因斯坦,很大原因,是我们的培养思路错了,我们是第一要“顺从”。如果不顺从,我们的学校,就会用各种方法,罚站,罚写作业,各种罚。军</div> </li> <li><a href="/article/3506.htm" title="scala如何读取和写入文件内容?" target="_blank">scala如何读取和写入文件内容?</a> <span class="text-muted">qindongliang1922</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/scala/1.htm">scala</a> <div>直接看如下代码: package file import java.io.RandomAccessFile import java.nio.charset.Charset import scala.io.Source import scala.reflect.io.{File, Path} /** * Created by qindongliang on 2015/</div> </li> <li><a href="/article/3633.htm" title="C语言算法之百元买百鸡" target="_blank">C语言算法之百元买百鸡</a> <span class="text-muted">qiufeihu</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div>中国古代数学家张丘建在他的《算经》中提出了一个著名的“百钱买百鸡问题”,鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁,母,雏各几何? 代码如下: #include <stdio.h> int main() { int cock,hen,chick; /*定义变量为基本整型*/ for(coc</div> </li> <li><a href="/article/3760.htm" title="Hadoop集群安全性:Hadoop中Namenode单点故障的解决方案及详细介绍AvatarNode" target="_blank">Hadoop集群安全性:Hadoop中Namenode单点故障的解决方案及详细介绍AvatarNode</a> <span class="text-muted">wyz2009107220</span> <a class="tag" taget="_blank" href="/search/NameNode/1.htm">NameNode</a> <div>正如大家所知,NameNode在Hadoop系统中存在单点故障问题,这个对于标榜高可用性的Hadoop来说一直是个软肋。本文讨论一下为了解决这个问题而存在的几个solution。 1. Secondary NameNode 原理:Secondary NN会定期的从NN中读取editlog,与自己存储的Image进行合并形成新的metadata image 优点:Hadoop较早的版本都自带,</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>