软件工程概述

2013-02-20 Management

一、软件工程概述

软件工程是一门研究用工程化方法构建和维护有效的、实用的和高质量的软件的学科。

最主要的目的是为了解决软件危机。

 

二、软件危机

表现:

1.软件开发成本、进度的估计很不准确

软件开发机构制定的项目计划跟实际情况有很大差距,使得开发经费一再突破。由于对工

作量和开发难度估计不足,进度计划无法按时完成,开发时间一再拖延,这种现象严重降低了软件开发机构的信誉。

2.软件产品常常与用户的要求不一致

在开发过程中,软件开发人员和用户之间缺乏信息交流。开发人员常常是在对用户要求只

有模糊了解的情况下就仓促上阵,匆忙着手编写程序。由于这些问题的存在,导致开发出来的软件不能满足用户的实际应用需要。

3.软件产品质量可靠性差

软件开发过程中,没有建立起确实有效的质量保证体系。一些软件项目为了赶进度或降低

软件开发成本,甚至不惜降低软件质量标准、偷工减料。

4.软件文档不完整、不一致

计算机软件不仅仅是程序,在软件开发过程中还应该产生出一系列的文档资料。实际上,

软件开发非常依赖这些文档资料。在软件开发过程中,软件开发机构的管理人员需要使用这些文档资料来管理软件项目;技术人员则需要利用文档资料进行信息交流;用户也需要通过这些文档资料来认识软件,对软件进行验收,熟悉软件的安装、操作等。但是,由于软件项目管理工作的欠缺,软件文档往往不完整,对软件的描述经常不一致,很难通过文档去跟踪软件开发过程中软件产品规格的变更。

5.软件产品可维护性差

软件中的错误非常难改正,软件很难适应新的硬件环境,很难根据用户的需要在原有软件

中增加一些新的功能。这样的软件是不便于重用的,以前开发的软件,一旦过时就不得不完全丢弃。

6.软件生产率低

软件生产率跟不上硬件的发展速度,不能适应计算机应用的迅速普及,以致现代计算机硬

件提供的巨大潜力不能被充分利用。

 

三、危机产生的原因

看点历史:

观察软件的发展,可以发现软件生产有三个发展时代,即程序设计时代、程序系统时代和

软件工程时代。

1.程序设计时代(20 世纪50 年代)

这个时期的程序大多是自用,程序的编写者往往也就是使用者,软件还没有形成为产品。

由于早期程序大多是为某个具体应用而专门编写的,程序任务单一,因此,对程序的设计也就仅仅体现在单个程序的算法上。早期程序还往往只能在某台专门的计算机上工作,很难将程序由一台设备移植到另一台设备。

 

2.程序系统时代(20 世纪60 年代)

这个时期的软件开发更多地依赖于个人创作。由于软件开发的主要内容仍然是程序编写,

软件开发的核心问题仍是技术问题;于是,用户的意图被忽视了,除了程序之外的其他文档、

技术标准、软件在今后运行过程中的维护等问题,也往往被忽视了。

软件已经开始成为产品,但软件的生产方式则是跟产品并不适宜的作坊创作方式。于是,

随着软件规模的不断扩大,软件危机现象在这个时期最终爆发出来。

 

3.软件工程时代(20 世纪70 年代起)

1968 年在联邦德国召开的计算机国际会议上,专门针对软件危机问题进行了讨论,在这次

会议上正式提出并使用了“软件工程”术语。于是,软件工程作为一门新兴的工程学科诞生了。

在软件开发上,自20 世纪70 年代以来的30 年里,结构化的工程方法获得了广泛应用,

并已成为了一种成熟的软件工程方法学;而自20 世纪90 年代起,基于面向对象的工程方法,也已被应用于软件开发之中。应该说,采用工程的原理、技术和方法实施软件产品开发,以适应软件产业化发展的需要,成为了这个时期诸多软件企业的追求目标。

 

 

真正的原因:

1.软件的不可见特性

软件不同于硬件,它是计算机系统中的逻辑部件,缺乏“可见性”。硬件错误往往可以通

过它的物理现象直接反映出来,例如,出现不正常的发热、噪音现象等;但软件错误没有这些直观表现,例如,软件中存在的程序行错误,就必须等到这行程序执行时才有可能被发现。因此,软件错误比起硬件错误来更难发现。软件的不可见特性也使得对软件项目的量化管理更难实施,对软件质量的量化评价更难操作。

2.软件系统规模庞大

软件成为产品以后已不同于早期程序,随着它的功能的增多,其规模、复杂程度越来越大。

例如,1968 年美国航空公司订票系统达到30 万条指令;IBM360OS 第16 版达到100 万条指令;1973 年美国阿波罗计划达到1 000 万条指令。这些庞大规模的软件系统,其复杂程度已超过了人所能接受的程度;但是,面对不断复杂的软件系统,其开发手段却仍然需要依靠开发人员的个人创造与手工操作。

3.软件生产工程化管理程度低

软件生产的工程化管理是软件作为产品所必须的,这意味着软件也需要像硬件一样,在软

件分析、设计完成之后,才能考虑软件的实现。应该说,工程化管理能够降低解决问题的代价。但是,许多软件的开发则往往是在分析、设计没有完成的情况下,就已经进入编码实现阶段。由于前期准备工作不充分,致使软件项目管理纷乱,严重影响软件项目成本、开发进度。

4.对用户需求关心程度不够

软件开发机构不熟悉用户业务领域。软件技术人员所关注的仅仅是计算机技术,它们不太

愿意和用户沟通,轻视对用户的需求调查,也缺乏有效的用户调查策略、手段。由于这些问题的存在,使得用户的需求意愿不能充分反映,或被错误理解。

实际上,软件是为用户开发的,只有用户才能真正了解他们自己的需要。由于没有对用户

做大量深入细致的调查研究,以致软件需求规格定义不准确,并最终使得完成后的软件不能适应用户的应用需要。

5.对软件维护重视程度不够

软件开发缺乏统一的规范。在软件产品开发过程中,开发者很少考虑到这个软件今后还需

要提供维护。但是,软件的使用周期漫长,软件错误具有隐蔽性,许多年之后软件仍可能需要改错。另外,软件的工作环境也可能会在几年后发生改变;用户也可能在软件运行几年以后,要求对它增加新的功能。这些都是属于软件维护问题。实际上,软件的可维护性是衡量软件质量的一项重要指标,软件可维护性程度高,软件就便于修正、改版和升级,由此可以使软件具有更长的使用寿命。

6.软件开发工具自动化程度低

尽管软件开发工具比30 年前已经有了很大的进步,但直到今天,软件开发仍然离不开工

程人员的个人创造与手工操作,软件生产仍不可能像硬件设备的生产那样,达到高度的自

动化。

 

四、探讨如何解决危机

  1. 软件工程的主要环节有:人员管理、项目管理、可行性与需求分析、系统设计、程序设计、测试、维护

 

 

  1. 常见的软件工程模型有:线性模型(瀑布模型),渐增式模型,螺旋模型,快速原型模型,增量模型,喷泉模型等

最早出现的软件工程模型是线性模型(又称瀑布模型)。

瀑布模型核心思想是按工序将问题化简,将功能的实现与设计分开,便于分工协作,即采用瀑布模型用结构化的分析与设计方法将逻辑实现与物理实现分开。将软件生命周期划分为制定计划、需求分析、软件设计、程序编写、软件测试和运行维护等六个基本活动,并且规定了它们自上而下、相互衔接的固定次序,如同瀑布流水,逐级下落。

002

优点

1)为项目提供了按阶段划分的检查点。

2)当前一阶段完成后,您只需要去关注后续阶段。

缺点

1)在项目各个阶段之间极少有反馈。

2)只有在项目生命周期的后期才能看到结果。

3)通过过多的强制完成日期和里程碑来跟踪各个项目阶段。

4)瀑布模型的突出缺点是不适应用户需求的变化.

 

线性模型太理想化,太单纯,已不再适合现代的软件开发模式,几乎被业界抛弃。但是复杂的“非线性”问题时,总是千方百计地将其分解或转化为一系列简单的线性问题。

 

 

螺旋模型:

基本做法是在“瀑布模型”的每一个开发阶段前引入一个非常严格的风险识别、风险分析和风险控制,它把软件项目分解成一个个小项目。每个小项目都标识一个或多个主要风险,直到所有的主要风险因素都被确定。

003

优点

1)设计上的灵活性,可以在项目的各个阶段进行变更。

2)以小的分段来构建大型系统,使成本计算变得简单容易。

3)客户始终参与每个阶段的开发,保证了项目不偏离正确方向以及项目的可控性。

4)随着项目推进,客户始终掌握项目的最新信息 , 从而他或她能够和管理层有效地交互。

5)客户认可这种公司内部的开发方式带来的良好的沟通和高质量的产品。

缺点

很难让用户确信这种演化方法的结果是可以控制的。建设周期长,而软件技术发展比较快,所以经常出现软件开发完毕后,和当前的技术水平有了较大的差距,无法满足当前用户需求。

螺旋模型的项目适用:

对于新近开发,需求不明确的情况下,适合用螺旋模型进行开发,便于风险控制和需求变更!

 

快速原型模型


Linux(Ubuntu)下设置golang环境变量

2013-01-31 Golang

1).源代码安装go语言

Go的工具链采用C语言编写,要构建它,你需要安装一个C编译器,本例采用gcc工具; 进入linux 终端,输入命令行

sudo apt-get install build-essential

Go使用Mercurial进行版本管理,首先你必须安装了Mercurial,对于 Ubuntu/Debian 系统先安装easy_install;

sudo apt-get install mercurial

出现Processing triggers for python-support…需先安装python环境 执行

sudo apt-get install python-setuptools python-dev

sudo easy_install mercurial

如果 还不行,那直接下载Mercurial安装

获取GO代码

hg clone -r release https://go.googlecode.com/hg/ $GOROOT

安装Go:

cd $GOROOT/src

./all.bash

如果 还不行,那直接下载编译好的代码安装 解压 go包放用户根目录下

sudo tar zxvf ~/Downloads/go1.0.3.linux-386.tar.gz -C /opt

2).设置环境变量,在/etc/profile文件同添加以下内容

sudo gedit /etc/profile

export GOROOT=$HOME/go
export GOBIN=$GOROOT/bin
export GOARCH=386
export GOOS=linux
export GOPATH=$HOME/workspacego
export PATH=.:$PATH:$GOBIN

GOARCH、GOOS为可选配置,交叉编译时有用 GOPATH设置工作空间,允许多个目录,当有多个目录时,用分隔符分隔,当有多个GOPATH时,默认会将go get的内容放在第一个目录下;

$GOPATH 目录约定有三个子目录:

src 存放源代码(比如:.go .c .h .s等)

pkg 编译后生成的文件(比如:.a)

bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中)

应用刚刚配置的环境变量:source /etc/profile

上面的配置是针对系统级别的,建议采用用户级别的配置如下:

如果用户根目录下不存在.bash_profile,则

cat > .bash_profile

export GOROOT=$HOME/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=$HOME/workspacego

CTRL_D(这是按 CTRL - D两个键同时)

每次启动系统应用

. ./.bash_profile

官方建议把配置记录在用户在~/.bashrc或者 ~/.profile中,这样 登陆就可以使用go 而不用,每次登陆系统都要执行. ./.bash_profile

配置文件的区别,请参阅后面的附录

3).查看go是否是release版

hg identify

更新go到新版本

cd $GOROOT

hg pull

hg update release

./all.bash

4).浏览本地doc

在命令行执行

godoc -http=:8080

在浏览器地址输入:http://127.0.0.1:8080,即可进入doc界面

5).搭建基于浏览器的交互式Go编程指南-gotour

5.1).安装Go语言英文教程:

sudo go get code.google.com/p/go-tour/gotour

5.2).安装Go语言中文教程:

sudo go get bitbucket.org/mikespook/go-tour-zh/gotour

在命令行执行gotour,在浏览器地址输入:http://127.0.0.1:3999,即可进入教程的学习界面。

附录

参考:http://blog.csdn.net/wumingxing0228/article/details/6050175

环境变量是和Shell紧密相关的,用户登录系统后就启动了一个Shell。对于Linux来说一般是bash,但也可以重新设定或切换到其它的 Shell。对于UNIX,可能是CShelll。环境变量是通过Shell命令来设置的,设置好的环境变量又可以被所有当前用户所运行的程序所使用。对于bash这个Shell程序来说,可以通过变量名来访问相应的环境变量,通过export来设置环境变量。下面通过几个实例来说明。

1)etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. 并从/etc/profile.d目录的配置文件中搜集shell的设置. 注:在这里我们设定是为所有用户可使用的全局变量。

2)/etc/bashrc:为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取.

3)~/.bashprofile:每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的.bashrc文件. 注:~在LINUX下面是代表HOME这个变量的。 另外在不同的LINUX操作系统下,这个文件可能是不同的,可能是~/.bashprofile; ~/.bash_login或 ~/.profile其中的一种或几种,如果存在几种的话,那么执行的顺序便是:~/.bashprofile、 ~/.bashlogin、 ~/.profile。比如我用的是Ubuntu,我的用户文件夹下默认的就只有~/.profile文件。

4)~/.bashrc:该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该文件被读取. (注:这个文件是 .开头的,所以在文件夹中被隐藏了) 那么我们如何添加自己定义的环境变量呢? 用记事本打开这个文件,然后在里面最后写上: xiaokang=kangkang 然后保存,这样每次打开一个新的terminal的时候,我们这个变量就生效了。记住,如果你已经打开一个terminal,然后你修改了这个文件,那么在这个terminal下是不会生效的。一般情况用户最好在这里进行修改,但是有时候会覆盖父级的变量,比如PATH是ROOT设定的,但是如果你在这个文件里面写了PATH=xx,那么将来所有的PATH都成了xx了,所以我们应该在这个文件中写为: PATH=$PATH:xx 这样就把原来的和你自己的一起加上了。而且注意在LINUX系统下用:分割表示并列,而不是windo的; 3和4都是在用户目录下的,他们唯一的不同是: .bash_profile只能在登录的时候启动一次。在我的Ubuntu里面这个3文件似乎没有。

5)~/.bash_logout:当每次退出系统(退出bash shell)时,执行该文件. 另外,/etc/profile中设定的变量(全局)的可以作用于任何用户,而~/.bashrc等中设定的变量(局部)只能继承/etc/profile中的变量,他们是/”父子/”关系.

~/.bash_profile 是交互式、login 方式进入 bash 运行的 ~/.bashrc 是交互式 non-login 方式进入 bash 运行的 通常二者设置大致相同,所以通常前者会调用后者。

好的,总结一下他们的执行方式: 当你登录并且登录shell是bash时,bash首先执行/etc/profile文件中的命令(如果该文件存在),然后它顺序寻找~ /.bashprofile,~/.bashlogin或~/.profile文件,并执行找到的第一个可读文件中的命令.当登录bash退出时,它 将执行~/.bashlogout文件中的命令. 当启动一个交互的bash时,它将执行~/.bashrc文件中的命令(如果该文件存在并且可读).当非交互地启动以运行一个shell脚本时,bash将查找bashenv环境变量,确定执行文件的名称.



Go For Java Programmers(面向Java开发者的GO编程)

2013-01-13 Golang Java

本文旨在帮助JAVA开发者迅速掌握 Go语言.

开始用一个很容易能被所有的Java程序员认出的例子来突出特色,然后对GO的框架给出了详细的的描述,最后用一个例子来说明GO结构中没有与 Java直接对应处。

##Hello stack (一个栈的例子) 为了吊起你的胃口,我们用一个麻雀虽小,五脏俱全和符合习惯的例子对应这个Stack.java 程序


// 包collection实现了生成栈.
package collection

// 零值栈是一个空栈,准备被使用.
type Stack struct {
    data []interface{}
}

// Push函数将x添加到栈顶.
func (s *Stack) Push(x interface{}) {
    s.data = append(s.data, x)
}

// Pop函数是将栈顶元素移除并返回.
// 在Pop函数执行在空栈时,会被一个运行时的error警示.
func (s *Stack) Pop() interface{} {
    i := len(s.data) - 1
    res := s.data[i]
    s.data[i] = nil  // 避免内存泄露
    s.data = s.data[:i]
    return res
}

// Size函数返回栈中元素的个数
func (s *Stack) Size() int {
    return len(s.data)
}

stack.go

  • 顶级声明出现之前,直接的评论是文档注释。他们是纯文字。.
  • 对于声明,你把名字写在类型后面.
  • struct 对应Java中的类, 但struct组成不是方法而只能是变量.
  • Tinterface{}类型对应Java的 Object. 在GO中它被所有的类型所实现,而不仅仅是引用类型.
  • 代码段 (s *Stack) 声明了一个方法,接收者 s 对应Java中的 this.
  • 操作符:=声明并初始化了一个变量. 它的类型可以从初始化表达式中推导出.

这里是一个的Hello world程序,演示了如何使用collection.Stack的抽象数据类型.


package collection_test

import (
    collection "."
    "fmt"
)

func Example() {
    var s collection.Stack
    s.Push("world")
    s.Push("hello, ")
    for s.Size() > 0 {
        fmt.Print(s.Pop())
    }
    fmt.Println()
    //输出: hello, world
}

example_test.go

##概念上的差异

  • Go的构造器没有类。Go 用 structs 和 interfaces来替代实例化方法,类的继承机制,动态方法查找.也可用于Java使用泛型接口
  • Go提供所有类型的指针的值,而不只是对象和数组。对于任何类型 T,有一个相应的指针类型*T表示指针指向类型 T的值。 offers pointers to values of all types, not just objects and arrays.
  • Go允许任何类型都有方法而没有装箱的限制 allows methods on any type; no boxing is required. 方法receiver,在Java中对应this可以是直接值或者是指针.
  • 数组在Go就是值. 当一个数组被当做函数的参数时,这个函数接收到的是数组的拷贝而不是它的指针. 然而在实践中,函数经常使用 slices作为参数; slices引用了基础数组.
  • 该语言提供了字符串,一个字符串行为就像一个字节片,但是是不可改变的。
  • 该语言中的哈希表被称作maps.
  • 该语言提供了独立运行的线程goroutines 和他们之间的通信渠道channels.
  • 某些类型(maps, slices, 和 channels)是按引用传递,而不是值。也就是说,传递一个map到函数并而不是拷贝map,如果函数修改了map,将被调用者看到变化。在Java术语来说,可以认为这是一个map的引用.
  • Go提供了两种访问级别对应Java的public和包的private.如果它的命名是大写字母开头就是最高级别public,反之就是包的private.
  • 作为替换Java中的异常机制, Go采用了类型 error值来表示事件,如文件结尾,和运行时的panics来表示运行时的错误,如数组越界等.
  • Go不支持隐式类型转换。混合使用不同类型的操作需要显式转换.
  • Go不支持函数重载。在同一范围内的函数和方法必须具有唯一的名称.
  • Go使用nil表示无效的指针,类似于Java使用null.

##句法 ###声明

声明是跟Java是相反的。你在类型后面再写名称,类型声明从左往右更容易读

Go 与Java相对应的

Go 与Java相对应的
var v1 int int v1;
var v2 *int Integer v2;
var v3 string String v3 = "";
var v4 [10]int int[] v4 = new int[10]; // v4 在Go中是一个值.
var v5 []int int[] v5;
var v6 *struct { a int } C v6; // Given: class C { int a; }
var v7 map[string]int HashMap<String,Integer> v7;
var v8 func(a int) int F v8; // Given: interface F { int f(int a); }

声明的一般形式是一个关键字后面跟着被声明对象的名字.这个关键字是const, type, var, 或者func. 您也可以使用一个关键字,后面的括号中跟着一系列声明.

var (
    n int
    x float64
)

当声明一个函数,你必须提供每个参数的名称,或者不提供任何参数的名称,你不能提供了一些而忽略了另外一些名字。您可以组合几个相同类型的名称:

func f(i, j, k int, s, t string)

一个变量可以在声明时初始化。当这样做时,指定的变量的类型是允许的,但不是必需的。当未指定类型,默认的是初始化表达式的类型.

var v9 = *v2 如果一个变量没有立即初始化,必须要制定类型。那样的情况下,它它会被隐式初始化该类型的零值zero value(0, nil, "", 等.). Go不存在未初始化的变量.

###短声明

在函数中,一个短的声明句法是 := 表示.

v10 := v1 这等效于

var v10 = v1

###函数类型

在Go中,函数都是一等公民。Go的函数类型表示一组所有具有相同的参数和返回类型的函数.

type binOp func(int, int) int

var op binOp
add := func(i, j int) int { return i + j }

op = add
n = op(100, 200)  // n = 100 + 200

###多重分配

GO允许多重分配。在右边的表达式会在评估后,再分配到任何的左操作数。

i, j = j, i  //交换i和j. 函数可以具有多个返回值,表示由括号中的列表。返回的值可以存储分配给一个变??量列表。

func f() (i int, pj *int) { ... }
v1, v2 = f()

###空白标识符

空白标识符提供了一种忽略多值表达式返回值的方式,用下划线字符表示: The blank identifier, represented by the underscore character, provides a way to ignore values returned by a multi-valued expression:

v1, _ = f()  // 忽略f()返回的第二个值.

###分号和格式

为了消除对分号和格式不必要的担忧,你可能会用gofmt程序来写GO风格的标准代码,虽然这种风格看起来很古怪,但熟悉了之后最终会像其他语言风格一样变得舒服

Go的代码在实际中很多出现分号。严格来说,Go所有的声明都用分号结束。但是Go毫无疑问会在每个非空白行的结尾插入一个分号,除非它还没有完. 这样做的后果是,在某些情况下,Go不允许断行。举例,你可能会像下面这样写:

func g()
{            //无效的;“{”应该是在前面那一行。
} 在g()后面会被插入一个分号,这样就使他像是一个函数声明而不是函数定义了类似的,你不能这样写:

if n == 0 {
}
else {       // 无效的; "else {" 应该是在前面那一行。
} 在} 后和else前面会插入一个分号,导致句法错误.

###条件语句

Go在条件语句中并不使用括号,像 if条件语句 , for 条件语句的表达式, switch 条件语句的值. 另一方面,它并不需要在 if或 for条件语句中加花括号

if a < b { f() }
if (a < b) { f() }           // 括号是不必要的.
if (a < b) f()               // 无效的
for i = 0; i < 10; i++ {}
for (i = 0; i < 10; i++) {}  // 无效的

此外, if和switch 接收一个可选的初始化的状态,那么惯用做法是建一个局部变量

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

###For 语句

Go没有while和do-while语句. 当for语句的条件比较单一时,他的作用就像是while语句. 完全省略条件则产生一个死循环。

for语句可能包含range遍历 strings, arrays, slices, maps, 或 channels。除了写成下面这样

for i := 0; i < len(a); i++ { ... } 去遍历a的元素,也可以写成

for i, v := range a { ... } 这里的i指索引, v代表的array, slice, 或者string的连续元素。对于字符串,i是一个字节的索引,v指向rune类型(rune类型是int32)的一个别名)。maps迭代产生键 - 值对,而channels只产生一个迭代值。

###Break和continue

像Java一样,GO许可break和continue指定一个标签,但标签上必须指的for, switch, 或者 select 语句.

###Switch 语句

在 switch 语句,case 标签默认情况下不通过,但你可以让他们 fallthrough语句结束的情况下通过了。

switch n {
case 0:  // empty case body
case 1:
    f()  // f is not called when n == 0.
} 但是一个case可以包含过个值

switch n {
case 0, 1:
    f()  // f is called if n == 0 || n == 1.
} case的值可以支持任何类型的相等比较操作符,如字符串或指针。一个丢失表达式的switch语句 等价于表达式为 true。

switch {
case n < 0:
    f1()
case n == 0:
    f2()
default:
    f3()
} ### ++ 和 -- 语句

++和–只能作为后缀操作符,和仅在语句中,而不是在表达式中。例如,你不可以写n = i++。

defer语句

defer语句调用一个函数的执行被推迟到函数返回那一刻。defer语句执行时,被递延的函数的参数被计算,并保存,以备将来使用

f, err := os.Open("filename")
defer f.Close()  // f will be closed when this function returns.

##Constants(常量) GO中的常数可能是untyped的。这适用于无类型的常量表达式的数字文本,和使用const声明的无类型的常量表达式。当它被用在需要一个带类型的值的背景下,一个无类型的常量的可以被转变成有类型的值。这样常量的使用相对自由,即使Go没有隐式类型转换

var a uint
f(a + 1)    // The untyped numeric constant 1 becomes typed as uint.
f(a + 1e3)  // 1e3 is also typed as uint. 语言对非类型化的数字常量不限制大小。限制仅适用于使用一个常量时,其中一种类型是必需的。

const huge = 1 << 100
var n int = huge >> 98 如果是不存在的变量声明的类型和相应的表达式的计算结果为一个非类型化的数字常数,这个常数是被转换为 rune, int, float64, 或者complex128 类型,取决于该值是否一个字符,整数,浮点,或复杂的常数。

c := '?'      // rune (alias for int32)
n := 1 + 2    // int
x := 2.7      // float64
z := 1 + 2i   // complex128 GO 不存在枚举类型。相反,你可以使用特殊的名字iota在单一的const声明中从而得到一系列累加值。当初始化表达式省略为一个 const,它重用了前面的表达式。

const (
    red = iota  // red == 0
    blue        // blue == 1
    green       // green == 2
)

Structs(结构体)

结构体对应于Java中的类,但一个结构的成员不能是方法,而是变量。结构体的指针是类似Java的的引用变量。与Java类不同的是,结构也可以被定义为直接值。在这两种情况下使用 .来访问结构体的成员。

type MyStruct struct {
    s string
    n int64
}

var x MyStruct      // x is initialized to MyStruct{"", 0}.
var px *MyStruct    // px is initialized to nil.
px = new(MyStruct)  // px points to the new struct MyStruct{"", 0}.

x.s = "Foo"
px.s = "Bar" 在Go中,方法可以与任何命名的类型关联,而不仅仅是与结构体。详情见方法和接口的讨论。

##Pointers(指针)

如果你有一个int或struct或者array需要分配对象的内容复制。 想达到Java的引用变量的效果,Go使用指针。对于任何类型e T,有一个相应的指针类型*T,表示指针类型 T的值

给指针变量分配存储空间,使用内置函数new,传入一个类型,并返回一个指针,指向分配的存储空间。分配的空间将被零初始化的类型。例如, new(int) 分配存储为一个新的int,初始化它的值为e 0,并返回它的地址,类型 *int。

Java代码T p = new T(),其中 T是一个两个int型实例变量a和b的类,对应于

type T struct { a, b int }
var p *T = new(T) 或者习惯性这样干

p := new(T) var v T代表声明,声明了一个变量包含一个值类型 T,这在Java中是没有的。也可使用复合方式创建并初始化值。

v := T{1, 2} 等同于

var v T
v.a = 1
v.b = 2 对于类型T的操作数x,地址运算符 &x提供值类型为*T的x的地址,

##Slices(切片) slice是概念上一个结构包含三个域:一个数组的指针、长度和容量。切片支持[]操作符来访问底层数组的元素。内置的len函数返回的切片长度。内置的的cap函数返回切片的能力。

给定一个数组,或另一个切片,通过a[i:j]来创建一个新的切片。这个新创建的切片指向a,从索引i开始,并结束索引j之前。它的长度是j - i。如果i 被省略,切片从0开始。如果j 被省略,切片在 len(a)结束。新切片跟 a一样指向相同的数组。即,改变后组成的新的切片的元素在a都能见到。新切片的容量就是简单的a减去i。数组的容量就是数组的长度。

var s []int
var a [10]int

s = a[:]  // short for s = a[0:len(a)] 如果你创建一个值类型为[100]byte(100个字节,也许是一个缓冲区的数组),你想不复制它,而将它传递给函数,那么函数的参数声明类型[]byte,并传入数组的切片。切片也可以用 make的函数创建(如下文所述)。

切片组合采用内置的append函数,Java的ArrayList提供相同的功能。

s0 := []int{1, 2}
s1 := append(s0, 3)      // append a single element
s2 := append(s1, 4, 5)   // append multiple elements
s3 := append(s2, s0...)  // append a slice 切片语法,也可以使用在字符串上。它返回一个新字符串,其值是原始的字符串的子串

##make函数 Map and channel values must be allocated using the built-in function make. For example, calling map和channel值必须使用内置的函数make。例如,调用

make(map[string]int) map[string]int返回一个新分配的值类型。相对于new, make 返回的是实际的对象,而不是一个地址。这是一致的事实,map和channel是引用类型。

对于map,make函数将容量作为一个可选的第二个参数的提示。对于channel,有一个可选的第二个参数来设置channel的缓冲能力,默认为0(无缓冲)。

make函数也可以用来分配一个切片。在这种情况下,它分配内存给基本数组并返回一个引用他的切片。该切片中的元素数是一个必需的参数。第二个可选的参数是切片的容量。

m := make([]int, 10, 20)  // Same as new([20]int)[:10]

##方法和接口 ###方法

方法看起来像一个普通的函数定义,但它有一个receiver(接收者)。receiver是类似Java实例方法中的this引用。

type MyType struct { i int }

func (p *MyType) Get() int {
    return p.i
}

var pm = new(MyType)
var n = pm.Get() 这声明了一个方法Get与MyType关联的。receiver被命名为p 在函数体内。

命名的类型来定义方法。如果您转换不同类型的值,新的值将有新的类型,而不是那些旧的类型。

你可以定义一个内置类型的方法,用新的命名类型声明。新的类型和内置的类型是不同的。

type MyInt int

func (p MyInt) Get() int {
    return int(p)  // The conversion is required.
}

func f(i int) {}
var v MyInt

v = v * v          // The operators of the underlying type still apply.
f(int(v))          // int(v) has no defined methods.
f(v)               // INVALID

###接口

Go接口类似于Java接口,但可被视为一个实现该接口提供任何类型的在Go接口命名的方法。明确的声明是不必要的。

接口像这样:

type MyInterface interface {
    Get() int
    Set(i int)
} 自从 MyType 已经有了Get 方法, 我们可以让 MyType满足接口通过添加

func (p *MyType) Set(i int) {
    p.i = i
} 现在任何只要将MyInterface当做参数就可以接收类型是*MyType的变量

func GetAndSet(x MyInterface) {}

func f1() {
    var p MyType
    GetAndSet(&p)
} 在Java术语,给MyType 定义 Set和Get 使MyType自动实现了MyInterface接口。这种类型型可满足多个接口。这是一种形式的鸭子类型。

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” James Whitcomb Riley

###匿名域

匿名域可以用于实现很像一个Java子类的东西。

type MySubType struct {
    MyType
    j int
}

func (p *MySubType) Get() int {
    p.j++
    return p.MyType.Get()
}
MySubType有效实现的像是MyType的子类型.

func f2() {
    var p MySubType
    GetAndSet(&p)
} Set方法是继承自MyType的,因为关联了匿名域的方法的变为了封闭类型的方法。在这种情况下,因为 MySubType有一个匿名与域 MyType类型,所以为 MyTypee的方法也成为MySubType的方法。Get方法被重写,Set方法被继承。

这是与Java中的子类不完全相同。当一个匿名域的方法被调用时,它的 receiver就是这个匿名域,而不是周围的结构体。换句话说,匿名域上的方法的不会动态调度。当你想要实现相当于Java的动态方法查找,请使用接口。

func f3() {
    var v MyInterface

    v = new(MyType)
    v.Get()  // Call the Get method for *MyType.

    v = new(MySubType)
    v.Get()  // Call the Get method for *MySubType.
}

###类型断言

使用一个类型断言可以使具有一个接口类型的变量转换成具有不同的接口类型。这是在运行时动态执行。与Java不同,并不需要任何声明两个接口之间的关系。

type Printer interface {
    Print()
}

func f4(x MyInterface) {
    x.(Printer).Print()  // type assertion to Printer
} 转换为Printer 完全是动态的。只要x(x中存储的值的实际类型)的 动态类型 定义了一个Print方法。

##Errors(错误机制) Go的多值的返回可以很容易地沿着正常的返回值返回详细的错误消息。按照惯例,这样的error类型的消息,就像下面的简单的内置接口

type error interface {
    Error() string
} 例如,os.Open函数当它无法打开文件时返回一个非零error值。

func Open(name string) (file *File, err error) 以下代码使用 os.Open打开一个文件。如果 error发生,它调用 log.Fatal打印错误信息并停止。

f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}
// do something with the open *File f error的接口只需要一个 Error的方法,但具体的error的实现往往有额外的方法,允许调用者进行检查错误的详细信息

##Panic and recover(恐惧和恢复) panic(恐慌)是一个运行时错误,展开goroutine的堆栈,一路运行任何递延的功能,然后停止程序。恐慌与Java异常相似,但只适用于运行时的错误,如一个nil 指针或试图索引数组越界。 Go程序使用内置的error类型 (详见上文)为了表示如文件结束等事件。

可以使用内置的recover (恢复),重新获得控制权的恐慌的goroutine和恢复正常运行。呼叫recover停止展开,并返回传入的参数panic。因为只有运行的未展开代码,内部含有延迟函数,recover只在内递延的函数有效。如果的goroutine是没有恐慌,recover返回nil。

##Go的线程机制和管道 ###Goroutines(Go的线程机制)

Go允许用go开启一个新的执行线程–goroutine。它运行在不同的,新创建的的goroutine中。在一个程序中的所有goroutine共享相同的地址空间。

Goroutines是轻量级的,只占用比堆栈分配多一点的空间。堆栈开始小和成长的分配和释放堆(heap)的要求。内部goroutines像进行了复用多个操作系统线程的协程。您不必担心这些细节。

go list.Sort()  // Run list.Sort in parallel; don’t wait for it. Go处理文字的函数,可以作为结束,在处理go时很强大

func Publish(text string, delay time.Duration) {
    go func() {
        time.Sleep(delay)
        fmt.Println(text)
    }()  // Note the parentheses. We must call the function.
} 变量text和delay在周围函数和函数文字之间共享;只要它们都可以访问,它们就存在。

###Channels(管道)

管道通过指定的元素类型的值来提供两个goroutine同步执行和沟通的机制。 <- 操作符指定通道的方向,发送或接收。如无任何指示方向时,通道是双向的。

chan T          // can be used to send and receive values of type T
chan<- float64  // can only be used to send float64s
<-chan int      // can only be used to receive ints 管道是一个引用类型,用make分配。

ic := make(chan int)        // unbuffered channel of ints
wc := make(chan *Work, 10)  // buffered channel of pointers to Work

To receive a value on a channel, use it as a unary operator. 使用<- 作为一个二元操作符来在管道上发送值。当在管道上接收一个值时,把它作为一元运算符。

ic <- 3       // Send 3 on the channel.
work := <-wc  // Receive a pointer to Work from the channel. 如果管道是无缓冲,那么发送者阻塞,直到接收器接收到值。如果管道有一个缓冲区,发送者阻塞,直到该值已被复制到缓冲区。如果缓冲区已满,这意味着等待,直到一些接收器中检索到值。接收器被阻塞,直到有数据接收。

##并发 (示例) 最后我们用一个例子来说明如何散落的内容拼起来。这是一个服务器通过管道来接受的Work请求的例子。每个请求都在一个单独的goroutine运行。 Work 结构本身包含了一个管道,用于返回一个结果。

package server

import "log"

// New creates a new server that accepts Work requests
// through the req channel.
func New() (req chan<- *Work) {
    wc := make(chan *Work)
    go serve(wc)
    return wc
}

type Work struct {
    Op    func(int, int) int
    A, B  int
    Reply chan int  // Server sends result on this channel.
}

func serve(wc <-chan *Work) {
    for w := range wc {
        go safelyDo(w)
    }
}

func safelyDo(w *Work) {
    // Regain control of panicking goroutine to avoid
    // killing the other executing goroutines.
    defer func() {
        if err := recover(); err != nil {
            log.Println("work failed:", err)
        }
    }()
    do(w)
}

func do(w *Work) {
    w.Reply <- w.Op(w.A, w.B)
} [server.go](http://www.nada.kth.se/~snilsson/go_for_java_programmers/src/server/server.go) 下面展示如何使用:

package server_test

import (
    server "."
    "fmt"
)

func main() {
    s := server.New()

    divideByZero := &server.Work{
        Op:    func(a, b int) int { return a / b },
        A:     100,
        B:     0,
        Reply: make(chan int),
    }
    s <- divideByZero

    add := &server.Work{
        Op:    func(a, b int) int { return a + b },
        A:     100,
        B:     200,
        Reply: make(chan int),
    }
    s <- add

    fmt.Println(<-add.Reply)
    // Output: 300
} [example_test.go](http://www.nada.kth.se/~snilsson/go_for_java_programmers/src/server/example_test.go) 并发编程是一个大主题,JavaGo的方法是完全不同的。要想充分体验到并发编程的乐趣,看这篇[Share Memory by Communicating(《通过沟通共享内存》)](http://golang.org/doc/codewalk/sharemem/)

Stefan Nilsson

该文基于相似的文章 《面向C++的GO编程》

参考: http://www.nada.kth.se/~snilsson/goforjava_programmers


Flex 4.6 自定义Datagrid添加checkbox\图片等功能

2012-03-28 Flex

flex 4.6 中的 spark 的Datagrid相比于之前的 mx 有很大不同

实现自定义<s:itemRenderer>详见代码~

<s:DataGrid id="dg" left="14" right="10" top="35" bottom="9" dataProvider="{datagrid_data}">
<s:columns>
<s:ArrayList>
<s:GridColumn headerText="程序名称"
dataField="name">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<s:HGroup verticalAlign="middle">
<s:CheckBox id="cbx"/>
<s:BitmapImage width="24" height="24" source="{data.pic}"/>
<s:Label text="{data.name}"/>
</s:HGroup>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
<s:GridColumn dataField="date" headerText="更新时间"/>
<s:GridColumn dataField="version" headerText="版本号"/>
<s:GridColumn dataField="moduleUrl" headerText="moduleUrl"/>
<s:GridColumn />
</s:ArrayList>
</s:columns>
</s:DataGrid>

flex4 ColumnChart外包裹NavigatorContent进行TabNavigator分页出错,柱子移位

2012-03-28 Flex

之前单页面的ColumnChart显示图标正常。 后来用TabNavigator进行分2页,每个页面用NavigatorContent, 在NavigatorContent中 再添加ColumnChart 出现了柱子移位的现象。

在ColumnChart 外包裹一层BorderContainer ,问题解决。~

<mx:TabNavigator id="tabnavigator1" width="100%" height="100%" >
<s:NavigatorContent id="nc_Id_1" label="调峰报告"
icon="@Embed('assets/icon/list/48.png')" width="100%" height="100%">
<ElectroLoadShifting:Usefuldays width="100%" height="100%" id="usefuldays_id"/>
</s:NavigatorContent>

<s:NavigatorContent id="nc_Id_2" label="调峰执行"
icon="@Embed('assets/icon/list/48.png')" width="100%" height="100%" >

<s:HGroup width="100%" height="100%" paddingLeft="1" paddingRight="10"
verticalAlign="middle" includeIn="loadingState">
<s:BorderContainer width="100%" height="100%" borderVisible="false" >
<s:HGroup width="100%" height="100%" >
<s:Label id="unit_id" width="18" height="100%"
text="{'用电量︵'+_model.loadingShitServerModel.unit+'︶'}"
verticalAlign="middle"/>
<s:VGroup width="100%" height="100%">
<s:Label text="近30天用电量"/>
<mx:ColumnChart id="column" height="100%" color="0x323232" width="100%"
showDataTips="true" dataProvider="{_model.loadingShitServerModel.datas}"
dataTipRenderer="common.skin.ColumnChart_DatatipSkin" creationComplete="init()"
>
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="time"/>
</mx:horizontalAxis>

<mx:series>
<mx:ColumnSeries xField="time" yField="data" displayName="日用电量" id="columSeries_id" />
</mx:series>
</mx:ColumnChart>
</s:VGroup>

</s:HGroup>
</s:BorderContainer>

</s:HGroup>

<s:HGroup width="100%" height="100%" includeIn="coarseTurningState">
<ElectroLoadShifting:CoarseTurning id="coarseTurning_id" width="100%" height="100%"/>
</s:HGroup>
<s:HGroup width="100%" height="100%" includeIn="fineTurningState">
<ElectroLoadShifting:FineTurning id="fineTurning_id" width="100%" height="100%"/>
</s:HGroup>
<s:HGroup width="100%" height="100%" includeIn="powerSchemeState">
<ElectroLoadShifting:PowerScheme id="powerScheme_id" width="100%" height="100%"/>
</s:HGroup>

</s:NavigatorContent>

</mx:TabNavigator>

Q.yuhen推介的一些书籍

2012-01-28 Uncategorized

Q.yuhen:比较喜欢的一些图书,推荐给大家。

Linux 管理:

《Linux 系统管理技术手册》 案头必备的工具书。

《鸟哥的 Linux 私房菜》不错的入门书。

《Linux 101 Hacks》常用命令手册

《UNIX Shell Scripting》写脚本的参考书

《The Linux Command Line》更详细的命令手册

Linux 编程:

《Linux 系统编程》对常用 API 讲述最详细的一本书

《UNIX 环境高级编程》经典

《The Linux Programming Interface》与上本书配套

《程序员的自我修养》别被名字误导,极好的一本深度基础书。

《深入理解 Linux 内核》可以翻翻,对提升细节理解有好处。

《UNIX 网络编程》经典

《TCP/IP 高级编程》好书

C/C++:

《C 程序设计语言》入门书

《Lnux C 编程一站式学习》Linux 下开发的入门书

《C 语言核心技术》参考手册

《彻底搞定 C 指针》最好的指针入门书

《C++ 编程思想》经典

《高质量程序设计指南——C/C++语言》经典

《C 专家编程》

《C 和指针》

《C 陷阱与缺陷》

Golang:

《Learing Go》简单

《The Go Programming Language》比较详细

《The way to Go》提升

Javascript:

《Javascript, A Beginner’s Guide》

《Object-Oriented Javascript》

Python:

《Python Pocket Reference》适合经常翻翻

《Expert Python Programming》某些地方很有启发

其他:

《深入理解计算机系统》经典,必读

《计算机组成与设计》可以翻翻

《汇编语言》王爽 最好的汇编入门书

《数据结构》C 语言版 经典

《Java 数据结构和算法》更易阅读

《Debug Hacks 中文版》GDB 入门书

《设计模式——可复用面向对象软件的基础》经典

《MongoDB, The Definitive Guide》


Flex4 发布后访问,初始化极其缓慢的问题 解决方案

2011-07-14 Flex

  • Flex项目初始化太慢!Flex4为了加快加载速度使用了RSL,即把可以共享的框架、类库单独生成文件,并在请求时缓存到 Flash cache中,这样只要是同域的Flex项目就都可以使用而不必重新加载从而加快了加载时间。而问题也恰恰处在这个默认设置上,这些共享文件的优先位置是 从Adobe的服务端上下载,正是这个请求下载过程造成了加载奇慢。解决的办法就是将这些文件的位置优先指向自己的服务器(本地),Flash builder的Release 发布也可以生成这些文件,只要将这些.swz文件上传至服务器即可。

  • 如何设置RSL路径。网上找了半天,大多数是flex-config.xml,但这个是Flex3的,Flex4 默认是不使用这个文件了。在Adobe的帮助文档上找到了Customize the deployment location of the framework RSLs(http://help.adobe.com/en_US/flex/using/WS2db454920 e96a9e51e63e3d11c0bf69084-7add.html)设置方法如图,打开项目–属性–flex构建路径–库路径标签,点开 Flex4.1 选择framework.swc的链接类型–编辑–弹出库路径选项框。 可以看到默认的adobe的server路径在第一个,第二个是本地路径(要求与swf在同目录),将第二个调到第一位置(图中已调整)。当然也可以自定义路径,可以是相对的或绝对地址。点击确定保存设置。 这样依次设置 framework_4.6.0.23201.swz textLayout_2.0.0.232.swz 等等

  • 最后点击项目–导出发行版,在bin-releas下可以看到生成的.swz文件,第一次要把所有的文件上传到网站,以后只要是同域的其他Flex项目指定好RSL路径,就不必再上传.swz文件了,这样大大减小了swf文件的大小,加快了加载速度。

到此解决了开始的问题,同时使得RSL的优点也得到体现。

查考: http://forums.adobe.com/message/2428795#2428795 http://blog.163.com/[email protected]/blog/static/170958034201282222046364/


Way Lau

Software Engineer and Full Stack Developer, now work and live in Shenzhen, China. Detail

Donate

See the list of Donors.