bingo 发布的文章

Git 识记

1.安装Git

安装git非常直接:

Linux – 打开控制台,然后通过包管理安装,在Ubuntu上命令是:
sudo apt-get install git-all

Windows – 推荐使用git for windows,它包括了图形工具以及命令行模拟器。

OS X – 最简单的方式是使用homebrew安装,命令行执行
brew install git

如果你是在是先用图形工具的话,那么推荐你使用Github desktop,Sourcetree。但我还是推荐你使用命令行,下面的内容就都是命令行的。

2.配置Git

安装完git,首要任务是配置我们的信息,最重要的是用户名及邮箱,打开终端,执行以下命令。

$git config --global user.name"My Name"
$git config --global user.emailmyEmail@example.com

配置好这两项,用户就能知道谁做了什么,并且一切都更有组织性了不是吗?

3.创建一个新仓库 – git init

git 会把所有文件以及历史记录保存在你的项目中,创建一个新的仓库,首先要去到项目路径,执行 git init。然后git会创建一个隐藏的文件夹.git,所有的信息都储存在其中。

在桌面创建一个联系文件夹 git_exercise, 打开终端:

$cdDesktop/git_exercise/
$git init

OK,现在项目还什么都没有,新建一个 hello.txt 文件试试~

4.检查状态 – git status

git status 是另一个非常重要的命令,它会告诉我们创库的当前状态:是否为最新代码,有什么更新等等执行git status:

$git status

On branch master

Initial commit

Untracked files:
(use"git add ..."toinclude inwhatwill be committed)

hello.txt

git 告诉我们,hello.txt尚未跟踪,这是因为这个文件是新的,git不知道是应该跟踪它的变动呢,还是直接忽略不管呢。为了跟踪我们的新文件,我们需要暂存它。

5.暂存 – git add

git 有个概念叫 暂存区,你可以把它看成一块空白帆布,包裹着所有你可能会提交的变动。它一开始为空,你可以通过 git add 命令添加内容,并使用 git commit 提交。

这个例子中只有一个文件:

$ git add hello.txt

如果需要提交目录下的所有内容,可以这样:

$ git add -A

再次使用git status查看:

$git status

On branch master

Initial commit

Changes tobe committed:
(use"git rm --cached ..."tounstage)

newfile: hello.txt

我们的文件已经提交了。状态信息还会告诉我们暂存区文件发生了什么变动,不过这里我们提交的是一个全新文件。

6.提交 – git commit

一次提交代表着我们的仓库到了一个交付状态,通常是完成了某一块小功能。它就像是一个快照,允许我们像使用时光机一样回到旧时光。

创建提交,需要我们提交东西到暂存区(git add),然后:

$ git commit -m "Initial commit."

这就创建了一次提交,-m “Initial commit.”表示对这次提交的描述,建议使用有意义的描述性信息。
远端仓库

到目前为止,我们的操作都是在本地的,它存在于.git文件中。为了能够协同开发,我们需要把代码发布到远端仓库上。

1.链接远端仓库 – git remote add

为了能够上传到远端仓库,我们需要先建立起链接,这篇教程中,远端仓库的地址为:https://github.com/tutorialzine/awesome-project,但你应该自己在Github, BitBucket上搭建仓库,自己一步一步尝试。 添加测试用的远端仓库

$ git remote add origin https://github.com/tutorialzine/awesome-project.git

一个项目可以同时拥有好几个远端仓库为了能够区分,通常会起不同的名字。通常主远端仓库被称为origin。

2.上传到服务器 – git push

每次我们要提交代码到服务器上时,都会使用到git push。

git push命令会有两个参数,远端仓库的名字,以及分支的名字:

$git push origin master

Counting objects: 3,done.
Writing objects: 100% (3/3),212bytes | 0bytes/s,done.
Total3(delta0),reused0(delta0)
Tohttps://github.com/tutorialzine/awesome-project.git

  • [newbranch] master -> master

取决于你使用的服务器,push过程你可能需要验证身份。如果没有出差错,现在使用浏览器去你的远端分支上看,hello.txt已经在那里等着你了。

3.克隆仓库 – git clone

放在Github上的开源项目,人们可以看到你的代码。可以使用 git clone进行下载到本地。

$git clone https://github.com/tutorialzine/awesome-project.git

本地也会创建一个新的仓库,并自动将github上的分支设为远端分支。

4.从服务器上拉取代码 – git pull

如果你更新了代码到仓库上,其他人可以通过git pull命令拉取你的变动:

$git pull origin master
From https://github.com/tutorialzine/awesome-project

  • branch master -> FETCH_HEAD
    Already up-to-date.

因为暂时没有其他人提交,所有没有任何变动

分支

当你在做一个新功能的时候,最好是在一个独立的区域上开发,通常称之为分支。分支之间相互独立,并且拥有自己的历史记录。这样做的原因是:

稳定版本的代码不会被破坏
不同的功能可以由不同开发者同时开发。
开发者可以专注于自己的分支,不用担心被其他人破坏了环境
在不确定之前,同一个特性可以拥有几个版本,便于比较
1.创建新分支 – git branch

每一个仓库的默认分支都叫master, 创建新分支可以这样:

$ git branch amazing_new_feature

创建了一个名为amazing_new_feature的新分支,它跟当前分支同一起点

2.切换分支 – git checkout

单独使用git branch,可以查看分支状态:

$git branch
amazing_new_feature

  • master
  • 号表示当前活跃分支为master,使用git checkout切换分支。

$ git checkout amazing_new_feature

3.合并分支 – git merge

我们的 amazing_new_feature 分支的任务是增加一个featuer.txt。我们来创建,添加到暂存区,提交。

$git add feature.txt
$git commit -m"New feature complete."

新分支任务完成了,回到master分支

$ git checkout master

现在去查看文件,你会发现,之前创建的feature.txt文件不见了,因为master分支上并没有feature.txt。使用git merge 把 amazing_new_feature 分支合并到master上。

$ git merge amazing_new_feature

ok! 然后再把amazing_new_feature 分支删掉吧。

$ git branch -d amazing_new_feature

高级

这篇文章的最后一节,我们来说些比较高级并且使用的技巧。

1.比对两个不同提交之间的差别

每次提交都有一个唯一id,查看所有提交和他们的id,可以使用 git log:

$git log

commit ba25c0ff30e1b2f0259157b42b9f8f5d174d80d7
Author: Tutorialzine
Date: Mon May3017:15:282016 +0300

Newfeature complete

commit b10cc1238e355c02a044ef9f9860811ff605c9b4
Author: Tutorialzine
Date: Mon May3016:30:042016 +0300

Added content tohello.txt

commit09bd8cc171d7084e78e4d118a2346b7487dca059
Author: Tutorialzine
Date: Sat May2817:52:142016 +0300

Initial commit

id 很长,但是你并不需要复制整个字符串,前一小部分就够了。

查看某一次提交更新了什么,使用 git show:

$git show b10cc123

commit b10cc1238e355c02a044ef9f9860811ff605c9b4
Author: Tutorialzine
Date: Mon May3016:30:042016 +0300

Added content tohello.txt

diff --gita/hello.txtb/hello.txt
index e69de29..b546a21100644
--- a/hello.txt
+++ b/hello.txt
-0,0 +1
+Niceweather today,isn'tit?

查看两次提交的不同,可以使用git diff [commit-from]..[commit-to] 语法:

$git diff09bd8cc..ba25c0ff

diff --gita/feature.txtb/feature.txt
newfilemode100644
index0000000..e69de29
diff --gita/hello.txtb/hello.txt
index e69de29..b546a21100644
--- a/hello.txt
+++ b/hello.txt
-0,0 +1
+Niceweather today,isn'tit?

比较首次提交和最后一次提交,我们可以看到所有的更改。当然使用git difftool命令更加方便。

2.回滚某个文件到之前的版本

git 允许我们将某个特定的文件回滚到特定的提交,使用的也是 git checkout。
下面的例子,我们将hello.txt回滚到最初的状态,需要指定回滚到哪个提交,以及文件的全路径。

$ git checkout 09bd8cc1 hello.txt

3.回滚提交

如果你发现最新的一次提交完了加某个文件,你可以通过 git commit —amend来修复,它会把最新的提交打回暂存区,并尝试重新提交。

如果是更复杂的情况,比如不是最新的提交了。那你可以使用git revert。

最新的一次提交别名也叫HEAD。

$ git revert HEAD

其他提交可以使用id:

$ git revert b10cc123

混滚提交时,发生冲突是非常频繁的。当文件被后面的提交修改了以后,git不能正确回滚。

4.解决合并冲突

冲突经常出现在合并分支或者是拉去别人的代码。有些时候git能自动处理冲突,但大部分需要我们手动处理。

比如John 和 Tim 分别在各自的分支上写了两部分代码。

John 喜欢 for:
// Use a for loop to console.log contents.
for(var i=0; i console.log(arr[i]);
}
Tim 喜欢 forEach:
// Use forEach to console.log contents.
arr.forEach(function(item) {
console.log(item);
});

假设John 现在去拉取 Tim的代码:

$git merge tim_branch

Auto-merging print_array.js
CONFLICT(content): Merge conflict inprint_array.js
Automatic merge failed;fix conflicts andthencommit the result.

这时候git并不知道如何解决冲突,因为他不知道John和Tim谁写得更好。

于是它就在代码中插入标记。

HEAD
// Useaforloop toconsole.logcontents.
for(vari=0;iarr.length;i++){

console.log(arr[i]);

}

// UseforEachtoconsole.logcontents.
arr.forEach(function(item){

console.log(item);

});

Timscommit.

==== 号上方是当前最新一次提交,下方是冲突的代码。我们需要解决这样的冲突,经过组委会成员讨论,一致认定,在座的各位都是垃圾!两个都不要。改成下面的代码。

// Notusing forloop orforEach.
// UseArray.toString()toconsole.logcontents.
console.log(arr.toString());

好了,再提交一下:

$git add -A
$git commit -m"Array printing conflict resolved."

如果在大型项目中,这个过程可能容易出问题。你可以使用GUI 工具来帮助你。使用 git mergetool。

5.配置 .gitignore

大部分项目中,会有写文件,文件夹是我们不想提交的。为了防止一不小心提交,我们需要gitignore文件:

在项目根目录创建.gitignore文件
在文件中列出不需要提交的文件名,文件夹名,每个一行
.gitignore文件需要提交,就像普通文件一样
通常会被ignore的文件有:

log文件
task runner builds
node_modules等文件夹
IDEs生成的文件
个人笔记
例如:

*.log
build/
node_modules/
.idea/
my_notes.txt

一些简单的排序法(已烂大街)

冒泡排序,快速排序,选择排序,二分法查找,快速查找

/**

  • 冒泡排序
  • 相邻2数比较,小的在前,大的在后
  • 数组有几个元素,就要比较几轮 $i
  • 每轮需要比较的次数为,数组元素个数-已比较的次数 $j
  • @param array $array 要操作的数组
  • @return array $array 返回的数组
    */

function bubbleSort($array)
{

    $cnt = count($array);
    for($i = 0; $i < $cnt ; $i++){
            for($j = 0 ; $j < ($cnt-$i-1) ; $j++){
                    if($array[$j] > $array[$j+1]){
                            $temp = $array[$j];
                            $array[$j] = $array[$j+1];
                            $array[$j+1] = $temp;
                    }
            }
    }
    return $array;

}

/**

  • 快速排序
  • 递归实现
  • 获取数组第一个数,循环使后面的数与其比较,
  • 比其小的放在一个数组中,比其大的放在一个数组中
  • 将2个数组递归调用,直到最终数组元素小于等于1时,没有可以比较的元素
  • 通过array_merge函数,将比较的数组按大小顺序合并然后一层一层的return出去,最终实现从小到大排序
  • @param array $array 要操作的数组
  • @return array $array 返回的数组
    */

function quickSort($array)
{

    if(count($array) <= 1 ) return $array;
    $key = $array[0];
    $left_arr = array();
    $right_arr = array();
    for ($i=1;$i<count($array);$i++){
            if($array[$i] <= $key){
                    $left_arr[] = $array[$i];
            }else{
                    $right_arr[] = $array[$i];
            }
    }

    $left_arr = quickSort($left_arr);
    $right_arr = quickSort($right_arr);
    return array_merge($left_arr,array($key),$right_arr);

}

/**

  • 选择排序
  • 2层循环
  • 第一层逐个获取数组的值 $array[$i]
  • 第二次遍历整个数组与$array[$i]比较($j=$i+1已经比较的,不再比较,减少比较次数)
  • 如果比$array[$i]小,就交换位置
  • 这样一轮下来就可以得到数组中最小值
  • 以此内推整个外层循环下来就数组从小到大排序了
  • @param array $array 要比较的数组
  • @return array $array 从小到大排序后的数组
    */

function selectSort($array){

    $cnt = count($array);
    for($i=0;$i<$cnt;$i++){
            for($j=($i+1);$j<$cnt;$j++){
                    if($array[$i]>$array[$j]){
                            $tmp = $array[$i];
                            $array[$i] = $array[$j];
                            $array[$j] = $tmp;
                    }
            }
    }
    return $array;

}

/**

  • 二分法查找一个值在数组中的位置
  • @param array $array 操作的数组
  • @param void $val 要查找的值
  • @return int $mid 返回要查找的值在数组中的索引,如果不存在返回-1
    */

function binarySearch($array,$val)
{

    $cnt = count($array);
    $low = 0;
    $high = $cnt - 1;
    while ($low <= $high){
            $mid = intval(($low + $high)/2);
            if($array[$mid] == $val){
                    return $mid;
            }

            if($array[$mid] < $val){
                    $low = $mid + 1;
            }

            if($array[$mid] > $val){
                    $high = $mid - 1;
            }
    }

    return -1;

}

/**

  • 顺序查找(最简单,效率低下)
  • 通过循环数组查找要的值
  • @param array $array 要操作的数组
  • @param void $val 要查找的值
  • @return int 如果存在,返回该值在数组中的索引,否则返回-1
    */

function seqSch($array,$val)
{

    for($i=0;$i<count($array);$i++){
            if($array[$i] == $val)
                    break;
    }

    if($i < count($array)){
            return $i;
    }else{
            return -1;
    }

}

进制之间的转换 - 载至其他

一、 二进制与十进制之间的转换
a.二进制转十进制(不分整数和小数从最后一位算起,每一位上的数乘以2的几次方,这个次数由这个数字所在的位置决定,从零位开始,然后相加)

01101011.001 转十进制:
第-3 1乘2的-3次方=0.125
第-2 0乘2的-2次方=0
第-1 0乘2的-1次方=0
第0位 1乘2的0次方=1
1 1乘2的1次方=2
2  0乘2的2次方=0
3  1乘2的3次方=8 
4 0乘2的4次方=0
5 1乘2的5次方=32
6  1乘2的6次方=64
7  0乘2的7次方=0
然后:1+2+0+8+0+32+64+0=107.125
01101011=107

b.十进制转二进制
(整数:除2取余法是一个连续除2的过程,直到商出现0时位置,余数反向排列;)
整数23 转二级制:
23除2商11余1
11除2商5余1
5除2商2余1
2除2商1余0
1除2商0余1
然后把余数反向排列 :23=10111

(小数:乘2取整法,即将小数部分乘以2,然后取整数部分,剩下的小数部分继续乘以2,然后取整数部分,剩下的小数部分又乘以2,一直取到小数部分
为零为止。如果永远不能为零,就同十进制数的四舍五入一样,按照要求保留多少位小数时,就根据后面一位是0还是1,取舍,如果是零,舍掉,如果是1,向入一位。换句话说就是0舍1入。读数要从前面的整数读到后面的整数)
0.125 转二进制
第一步,将0.125乘以2,得0.25,则整数部分为0,小数部分为0.25;
第二步, 将小数部分0.25乘以2,得0.5,则整数部分为0,小数部分为0.5;
第三步, 将小数部分0.5乘以2,得1.0,则整数部分为1,小数部分为0.0;
第四步,读数,从第一位读起,读到最后一位,即为0.001。
23.125 转二进制10111.001

0.45 转二进制(保留到小数点第四位)
第一步,将0.45乘以2,得0.9,则整数部分为0,小数部分为0.9;
第二步, 将小数部分0.9乘以2,得1.8,则整数部分为1,小数部分为0.8;
第三步, 将小数部分0.8乘以2,得1.6,则整数部分为1,小数部分为0.6;
第四步, 将小数部分0.6乘以2,得1.2,则整数部分为1,小数部分为0.2; 算到这一步就可以了,因为只需要保留四位小数
第五步, 将小数部分0.2乘以2,得0.4,则整数部分为0,小数部分为0.4;
第六步, 将小数部分0.4乘以2,得0.8,则整数部分为0,小数部分为0.8;后面会一直循环重复
第七步, 将小数部分0.8乘以2,得1.6,则整数部分为1,小数部分为0.6;
。。。。。。
读整数不分,从第一位读起,读到最后一位,即为0.0111。
23.45(保留到小数点第四位)10111.0111

二、 二进制与八进制之间的转换(基础还是二进制和十进制之间的转换)
(取三合一法,即从二进制的小数点为分界点,向左(向右)每三位取成一位,接着将这三位二进制按权相加,得到 的数就是一位八位二进制数,然后,按顺序进行排列,小数点的位置不变,得到的数字就是我们所求的八进制数。如果向左(向右)取三位后,取到最高(最低)位 时候,如果无法凑足三位,可以在小数点最左边(最右边),即整数的最高位(最低位)添0,凑足三位)这里的最高位,最低位和十进制的一样,前面的是最高位,后面的是最低位
三位二进制表示一位八进制, 因为三位二进制数最大(111)的十进制数也就是7,所以就保证每位数都是0-7之间的数
a.二进制转八进制
1100100 拆分成 : 001 100 100
001 1 这是由二级制转换成十进制得来的 02^2 + 02^1 + 1*2^0 = 1
100 4 12^2 + 02^1 + 0*2^0 = 4
100 4 12^2 + 02^1 + 0*2^0 = 4
依次读下来就是 144
1100100 =144
b.八进制转二级制(脑海中有个概念,那就是你要用十进制转二进制先算出0-7每个数的二级制数表达方式,还是一样不够三位补零)
000 0
001 1
010 2
011 3
100 4
101 5
110 6
111 7
有了上面这个表,随便报一个数653524,你要明白八进制上的一位就对应三位二进制,653524有6位,所以二进制数就有3*6=18位,6用110代替,5用101代替,依次下来就是
110 101 011 101 010 100

三、 二进制与十六进制之间的转换(基础还是二进制和十进制之间的转换)
四位二进制表示一位十六进制, 因为四位二进制数最大(1111)也就是十进制的表示法15即十六进制的表示法F,所以就保证每位数都是0-F之间的数
a.二进制转十六进制
同样还是上面这个数:1100100 拆分 0110 0100
0110 6
0100 4
1100100 = 64

b.十六进制转二进制(脑海中有个概念,那就是你要先算出0-F每个数的二级制数表达方式,还是一样不够四位补零)
1-0001
2-0010
3-0011
4-0100
5-0101
6-0110
7-0111
8-1000
9-1001
A-1010
B-1011
C-1100
D-1101
E-1110
F-1111

四、 十进制与十六进制之间的转换(其算法和二进制和十进制之间的算法一样只是,由2变成了16) 十进制与八进制之间的转换(其算法和二进制和十进制之间的算法一样只是,由2变成了8)
a.十进制转十六进制
123 转成十六进制
123除16商7余B
7除16商0余7
结果就是7B
b.十六进制转二进制
十六进数 2AF5
第0位: 5 * 16^0 = 5
第1位: F * 16^1 = 240
第2位: A * 16^2 = 2560
第3位: 2 * 16^3 = 8192
5 16^0 + F 16^1 + A 16^2 + 2 16^3 = 10997
2AF5 = 10997

HTML Meta标签

1、控制浏览器缓存

当我们访问网页,本地缓存可以让我们提高访问该页面的速度。有时候,你可能会发现,你再次访问的页面没有得到更新,这是因为浏览器显示缓存页面。如果,你想这种事情不再发生,通过使用Meta标签禁用浏览器缓存,可以解决。代码如下:

<meta http-equiv="Cache-Control" content="no-store" />

以上代码在主流浏览器上都可以使用。可以使用更多的值和规范禁用缓存:

<meta http-equiv="Cache-Control" content="no-store" />
<meta http-equiv="Pragma" content="no-store" />

你也可以设置一个截止日期,以确保浏览器将显示来自服务器的文件,而且是最新内容,而不是缓存中的。

<meta http-equiv="expires" content="Fri,18 Jul 2014 1:00:00 GMT" />

上面的代码意思是,指定的日期和时间文件过期时间。

2、设置cookie

和缓存相似,cookies你在浏览器访问过的网站的一小块数据存储信息。现在很多网站都会使用cookie,最典型的例子,就是,我们在很多网站上看到不断变换的广告。如果你在百度上搜索了某商品,那么他就会被存储下来,一般来说都会存储1-7天,也有可能更长。

设置cookie,你可以使用下面的代码:

<meta http-equiv="Set-Cookie" content="name=data;path=path;expires=Day,DD-MMM-YY HH:MM:SS ZOME" />

name=data 是cookie名称,由设置的值决定。反之,expired表示cookie到期后删除的时间。如果把过期时间设置为空,你关掉浏览器,cookie就会被清空。

举个例子,我想把cookie过期时间设置为2015年1月31日:

<meta http-equiv="Set-Cookie" content="name=data;path=path;expires=Thursday,01-Jan-2015 00:00:00 GMT" />

3、刷新网页

你可以设置一段时间后对页面进行刷新操作。meta http-equiv="refresh"可以指定浏览器延迟一段时间自动刷新页面。下面的meta-tag指定浏览器每5秒自动刷新一次。

<meta http-equiv="refresh" content='5' />

4、重定向

我们可以使用refresh meta标签对页面进行重定向。下面的例子将在5秒后访问example.com

<meta http-equiv="refresh" content="5;url=http:www.baidu.com"; />

抽象类和方法<转>

可以使用abstract来修饰一个类或者方法。

用abstract修饰的类表示这个类是一个抽象类,用abstract修饰的方法表示这个方法是一个抽象方法。

抽象类不能被实例化。

抽象方法是只有方法声明,而没有方法的实现内容。
abstract 抽象类

可以使用abstract来修饰一个类。

用abstract修饰的类表示这个类是一个抽象类。

抽象类不能被实例化。

这是一个简单抽象的方法,如果它被直接实例化,系统会报错。
[PHP] view plain copy

<?php  
//定义一个抽象类  
abstract class User  
{  
    public function __toString() {  
        return get_class($this);  
    }   
}  
//实例化这个类会出现错误  
echo new User();  
?>  

下面例子的 NormalUser 继承自 User类,就可以被实例化了。

[php] view plain copy

<?php  
//定义一个抽象类  
abstract class User  
{  
    public function __toString() {  
        return get_class($this);  
    }   
}  
//实例化这个类会出现错误  
echo new User();  
class NormalUser extends User  
{  
}  
$a = new NormalUser();  
echo "这个类" . $a . "的实例";  
?>  

单独设置一个抽象类是没有意义的,只有有了抽象方法,抽象类才有了血肉。下面介绍抽象方法。
abstract 抽象方法

用abstract修饰的类表示这个方法是一个抽象方法。

抽象方法,只有方法的声明部分,没有方法体。

抽象方法没有 {} ,而采用 ; 结束。

一个类中,只要有一个抽象方法,这个类必须被声明为抽象类。

抽象方法在子类中必须被重写。

下面是一个抽象类,其中有两个抽象方法,分别是 setSal() 和 getSal()。用来取回 $sal 员工的工资。

[php] view plain copy

<?php  
abstract class User  
{  
    protected $sal = 0;  
    //这里定义的抽象方法。  
    //注意抽象方法没有方法体,而且方法结束使用 ; 号。  
    abstract function getSal();  
    abstract function setSal();  
    //定义它的__tostring方法  
    public function __toString() {  
        return get_class($this);  
    }   
}   
?>  

既然User类不能被直接继承,我们写一个NormalUser类继承自User类。当我们写成如下代码时,系统会报错。 这个错误告诉我们,在 User类中有两个抽象方法,我们必须在子类中重写这两个方法。

[php] view plain copy

<?php  
abstract class User  
{  
    protected $sal = 0;  
    //这里定义的抽象方法。  
    //注意抽象方法没有方法体,而且方法结束使用 ; 号。  
    abstract function getSal();  
    abstract function setSal();  
    //定义它的__tostring方法  
    public function __toString() {  
        return get_class($this);  
    }   
}  
class NormalUser extends User  
{  
}  
?>   

下面例子,重写了这两个方法,虽然方法体里面 {} 的内容是空的,也算重写了这个方法。注意看重写方法的参数名称,这里只要参数数量一致就可以,不要求参数的名称必须一致。

[php] view plain copy

<?php  
abstract class User  
{  
    protected $sal = 0;  
    //这里定义的抽象方法。  
    //注意抽象方法没有方法体,而且方法结束使用;号。  
    abstract function getSal();  
    abstract function setSal();  
    //定义它的__tostring方法  
    public function __toString() {  
        return get_class($this);  
    }   
}  
class NormalUser extends User  
{  
    function getSal() {  
    }  
    function setSal($sal) {   
    }  
}  
    //这样就不会出错了。  
?>  

下面19-21行,三种写重写的方式都会报错。

19行,缺少参数。

20行,参数又多了。

21行,参数类型不对。(这种写法在以后章节介绍)

一个类中,如果有一个抽象方法,这个类必须被声明为抽象类。

下面这个类不是抽象类,其中定义了一个抽象方法,会报错。

[php] view plain copy

<?php  
class User  
{  
    protected $sal = 0;  
    //这里定义的抽象方法。  
    //注意抽象方法没有方法体,而且方法结束使用;号。  
    abstract function getSal();  
    abstract function setSal();  
    //定义它的__tostring方法  
    public function __toString() {  
        return get_class($this);  
    }   
}  
    //这个类中有两个抽象方法,如果这个类不是抽象的。会报错  
?>  

抽象类继承抽象类

抽象类继承另外一个抽象类时,不用重写其中的抽象方法。

抽象类中,不能重写抽象父类的抽象方法。

这样的用法,可以理解为对抽象类的扩展

下面的例子,演示了一个抽象类继承自另外一个抽象类时,不需要重写其中的抽象方法。

[php] view plain copy

<?php  
abstract class User  
{  
    protected $sal = 0;  
    abstract function getSal();  
    abstract function setSal($sal);   
}  
abstract class VipUser extends User  
{  
}  
?>  

抽象类在被继承后,其中的抽象方法不能被重写。

如果发生重写,系统会报错。

[php] view plain copy

<?php  
abstract class User  
{  
    protected $sal = 0;  
    abstract function getSal();  
    abstract function setSal($sal);   
}  
abstract class VipUser extends User  
{  
    abstract function setSal();   
}  
?>  

抽象类继承抽象类,目的对抽象类的扩展。

[php] view plain copy

<?php  
abstract class User  
{  
    protected $sal = 0;  
    abstract function getSal();  
    abstract function setSal($sal);   
}  
abstract class VipUser extends User  
{  
    protected $commision = 0;  
    abstract function getCommision();  
    abstract function setCommision();  
}  
?>  

在PHP5.1中,抽象类中支持静态抽象方法。下面这个例子,看到静态抽象方法可以声明。实现这个方法时,必须是静态的方法。
静态抽象方法

在PHP5.1中,抽象类中支持静态抽象方法。下面这个例子,看到静态抽象方法可以声明。实现这个方法时,必须是静态的方法。

[php] view plain copy

<?php  
abstract class User  
{  
    protected static  $sal = 0;  
    static abstract function getSal();  
    static abstract function setSal($sal);   
}  
class VipUser extends User  
{  
    static function getSal() {  
        return self::$sal;  
    }  
    static function setSal($sal) {  
        self::$sal = $sal;  
    }  
}  
VipUser::setSal(100);  
echo "you sal is " . VipUser::getSal();  
?>  
//这里的抽象方法好像没有问题