e攻城狮

  • 首页

  • 随笔

  • 分类

  • 瞎折腾

  • 搜索

字符编码基础

发表于 2017-01-08

ACSII规定:一个字符由一个字节存储。范围:前32位(0x00 — 0x20)为控制码。第32位至127位(0x20 — 0x7f)为空格、标点符号、数字、字母。127位以后不用。

扩展字符集

原因: 由于其它国家的字符太多,ASCII标准已经不够用,所以他们启用了127位以后的全部空位。

GB2312

原因:由于其它国家制定的扩展字符集并不适用于中文,所以中国人重新制定了符合本国国情的编码方式:GB2312(GB2312是对ASCII码的中文扩展)。规定:小于127的字符意义同ASCII标准一样,但两个大于127的字符连在一起时,就表示一个汉字。范围:前一个字节(高字节)使用范围为:0xa1 — 0xf7后一个字节(低字节)使用范围为:0xa1 — oxfe

GBK

原因:后来由于汉字实在太多,GB2312已经不够使用了,于是又制定了新的编码:GBK,取消了GB2312中对汉字低字节的限制。规定: 小于127的字符意义同ASCII标准一样,如果遇到大于127位的字节则无论其后的一个字节是否是大于127都将这个两个字节视为一个汉字(GB2312要求两个字节都要大于127,但GBK取消了对低字节的要求)。范围:前一个字节(高字节)使用范围为:0x81 — 0xfe后一个字节(低字节)使用范围为:0x40 — 0xfe

GB18030

原因: 由于GBK并没有包含少数民族的文字字符,所以又再次对GBK进行扩展以包含少数名族字符,由此又制定了新的编码GB18030,从此该编码方式可以完全表示中华民族的所有字符了。

小结:人们为了统一称呼这些【汉字】编码方式,于是把它们都叫做DBCS(Double Byte Charecter Set,双字节字符集),它们最大的特点就是两个字节长的汉字字符和一个字节长的英文字符并存在一套编码系统里。因此在处理这些编码时要时刻注意每一个字节的值是否是大于127,如果小于则取按照ASCII标准取值,如过大于则表示一个汉字开始了,这时就要和它和后面一个字节一起组合起来识别为一个汉字字符。

Unicode

原因: 由于每个国家都制定了自己的一套编码体系,造成各个国家之间的字符编码互不兼容,于是ISO组织统一制定了能包含全球所有文化、字符、符号的编码标准:Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “UNICODE”。规定: Unicode规定所有字符都统一使用两个字节编码,即使用16位来统一表示所有字符,对于ASCII标准的字符保持其值不变,但字节长度由一个字节变成两个字节,所以英文字符的Unicode编码的高字节永远都为0,因此也造成了存储空间的浪费。

UTF-8

定义: UTF即UCS Transfer Format(Unicode传输格式),utf-8是在网络上传输Unicode字符的一项标准,它是Unicode的主要实现方式之一。它每次传输8位数据,但它并不是直接对应Unicode的字节的值,而是为了传输的可靠性制定了一套从Unicode到utf-8的转换规则。转换规则:

Unicode UTF-8
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 10xxxxxx
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx

举例: ‘汉’字的Unicode编码是:6C49,通过上表可以得出,它在0x0800到0xffff之间,所以它需要使用3个字节来传输,将6C49转位二进制: 0110 1100 0100 1001,按照utf-8的格式转换,第一个字节为:[1110]0110,第二个字节为:[10]110001,第三个字节为:[10]001001,所以将‘汉’字的unicode转换为utf-8之后的二进制是:11100110 10110001 10001001 ,即16进制E6 B1 89

单字节: 由一个字节构成一个字符的编码。如ASCII

多字节: 由一个或多个字节构成一个字符的编码。如GB2312、GBK宽字节: 始终由两个字节构成一个字符的编码。如Unicode

渗透测试报告标准

发表于 2016-12-25 分类于 安全研究

优秀的报告标准

漏洞标题

标题描述:标题可简明扼要说明问题,描述语言规范化。如“testasp.vulnweb.com站showforum.asp请求存在SQL注入漏洞”“testasp.vulnweb.com站查看订单处存在越权漏洞”

基本信息

漏洞类型:填写信息准确无误

漏洞等级:填写信息准确无误

厂商信息:填写信息准确无误

漏洞描述

漏洞简述:包含漏洞概述,漏洞危害

漏洞正文

漏洞复现过程:复现过程完整,无需二次沟通或补充数据

分步骤图文描述:有详细的漏洞复现步骤、测试步骤,并每个步骤配有图文描述。平台、厂商可根据描述一次性完成漏洞复测

漏洞危害证明:漏洞危害证明完整,无误

URL及重要参数:URL及重要参数完整,无误

格式排版、专业术语:格式排版规范;无病句错别字;描述用语专业化,规范化

修复建议

漏洞修复建议:修复建议对开发有较大实用性,如修复思路,修复代码样式,伪代码等

良好的报告标准

漏洞标题

标题描述:标题可基本概括漏洞情况,描述语言缺乏规范性。如“一处注入漏洞”“另外一处注入”“网站某处存在越权”

基本信息

漏洞类型:填写信息准确无误

漏洞等级:填写信息准确无误

厂商信息:填写信息准确无误

漏洞描述

漏洞简述:包含漏洞概述

漏洞正文

漏洞复现过程:复现过程基本完整,个别地方描述不清或缺少数据,对漏洞评估、复现有一定影响

分步骤图文描述:关键测试步骤完整,但复现步骤缺失,对漏洞复现有一定影响。如直接粘贴出漏洞利用请求包,但缺乏测试步骤如何获取此请求包,需要二次沟通方才可复现

漏洞危害证明:漏洞危害证明基本完整

URL及重要参数:URL及重要参数基本完整

格式排版、专业术语:格式排版不影响正常阅读;个别病句错别字;描述用语较为规范化

修复建议

漏洞修复建议:修复建议基本无误,但过于简单,无实际参考意义。如“控制权限”,“过滤参数”,“校验身份”

较差的报告标准

漏洞标题

标题描述:标题描述不清;与漏洞详情不符;夸大漏洞级别及危害

基本信息

漏洞类型:填写信息准确无误

漏洞等级:填写信息准确无误

厂商信息:填写信息准确无误

漏洞描述

漏洞简述:无效的漏洞描述,如“RT”,“请看详情”

漏洞正文

漏洞复现过程:复现过程关键步骤缺失,影响正常的漏洞评估、复现

分步骤图文描述:无测试步骤;无文字描述,直接粘贴截图。对漏洞评估、复现、修复工作产生较大影响。

漏洞危害证明:无有效漏洞危害证明或主管臆断危害。

URL及重要参数:缺少关键URL,核心参数,影响复现

格式排版、专业术语:格式排版混乱,影响正常阅读;出现较多的病句,错别字;过于口语化、网络化用语,如“你懂的”

修复建议

漏洞修复建议:无意义或错误的修复建议。如“不知道”,“你懂的”,“问开发”,“修复”

前端Yslow的23个优化原则

发表于 2016-12-22 分类于 前端开发

最常遇见的前端优化问题。

Yslow是雅虎开发的基于网页性能分析浏览器插件,可以检测出网页的具体性能值,并且有著名的Yslow 23条优化规则:

减少HTTP请求次数

尽量合并图片、CSS、JS。比如加载一个页面,如果有5个css文件的话,那么会发出5次http请求,这样会让用户第一次访问你的页面的时候会长时间等待。而如果把这个5个文件合成一个的话,就只需要发出一次http请求,节省网络请求时间,加快页面的加载。

使用CDN

网站上静态资源即css、js全都使用cdn分发,图片亦然。

避免空的src和href

当link标签的href属性为空、script标签的src属性为空的时候,浏览器渲染的时候会把当前页面的URL作为它们的属性值,从而把页面的内容加载进来作为它们的值。所以要避免犯这样的疏忽。

为文件头指定Expires

Exipres是用来设置文件的过期时间的,一般对css、js、图片资源有效。 他可以使内容具有缓存性,这样下回再访问同样的资源时就通过浏览器缓存区读取,不需要再发出http请求。如下例子:

使用gzip压缩内容

gzip能够压缩任何一个文本类型的响应,包括html,xml,json。大大缩小请求返回的数据量。

把CSS放到顶部

网页上的资源加载时从上网下顺序加载的,所以css放在页面的顶部能够优先渲染页面,让用户感觉页面加载很快。

把JS放到底部

加载js时会对后续的资源造成阻塞,必须得等js加载完才去加载后续的文件 ,所以就把js放在页面底部最后加载。

避免使用CSS表达式

举个css表达式的例子

font-color: expression( (new Date()).getHours()%3 ? “#FFFFFF” : “#AAAAAA” );

这个表达式会持续的在页面上计算样式,影响页面的性能。并且css表达式只被IE支持。

将CSS和JS放到外部文件中

目的是缓存文件,可以参考原则4。 但有时候为了减少请求,也会直接写到页面里,需根据PV和IP的比例权衡。

权衡DNS查找次数

减少主机名可以节省响应时间。但同时,需要注意,减少主机会减少页面中并行下载的数量。

IE浏览器在同一时刻只能从同一域名下载两个文件。当在一个页面显示多张图片时,IE 用户的图片下载速度就会受到影响。所以新浪会搞N个二级域名来放图片。

下面是新浪微博的图片域名,我们可以看到他有多个域名,这样可以保证这些不同域名能够同时去下载图片,而不用排队。不过如果当使用的域名过多时,响应时间就会慢,因为不用响应域名时间不一致。

精简CSS和JS

这里就涉及到css和js的压缩了。比如下面的新浪的一个css文件,把空格回车全部去掉,减少文件的大小。现在的压缩工具有很多,基本主流的前端构建工具都能进行css和js文件的压缩,如grunt,glup等。

避免跳转

有种现象会比较坑爹,看起来没什么差别,其实多次了一次页面跳转。比如当URL本该有斜杠(/)却被忽略掉时。

例如,当我们要访问https://baidu.com时,实际上返回的是一个包含301代码的跳转,它指向的是http://baidu.com/(注意末尾的斜杠)。在nginx服务器可以使用rewrite;Apache服务器中可以使用Alias 或者 mod_rewrite或者the DirectorySlash来避免。另一种是不用域名之间的跳转, 比如访问http://baidu.com/bbs跳转到
http://bbs.baidu.com/。那么可以通过使用Alias或者mod_rewirte建立CNAME(保存一个域名和另外一个域名之间关系的DNS记录)来替代。

删除重复的JS和CSS

重复调用脚本,除了增加额外的HTTP请求外,多次运算也会浪费时间。在IE和Firefox中不管脚本是否可缓存,它们都存在重复运算JavaScript的问题。

配置ETags

它用来判断浏览器缓存里的元素是否和原来服务器上的一致。比last-modified date更具有弹性,例如某个文件在1秒内修改了10次,Etag可以综合Inode(文件的索引节点(inode)数),MTime(修改时间)和Size来精准的进行判断,避开UNIX记录MTime只能精确到秒的问题。 服务器集群使用,可取后两个参数。使用ETags减少Web应用带宽和负载

可缓存的AJAX

异步请求同样的造成用户等待,所以使用ajax请求时,要主动告诉浏览器如果该请求有缓存就去请求缓存内容。如下代码片段, cache:true 就是显式的要求如果当前请求有缓存的话,直接使用缓存

1
2
3
4
5
6
7
$.ajax({
url : 'url',
dataType : "json",
cache: true,
success : function(son, status){

})

使用GET来完成AJAX请求

当使用XMLHttpRequest时,浏览器中的POST方法是一个“两步走”的过程:首先发送文件头,然后才发送数据。因此使用GET获取数据时更加有意义。

减少DOM元素数量

这是一门大学问,这里可以引申出一堆优化的细节。想要具体研究的可以看后面推荐书籍。总之大原则减少DOM数量,就会减少浏览器的解析负担。

避免404

比如外链的css、js文件出现问题返回404时,会破坏浏览器的并行加载。

减少Cookie的大小

Cookie里面别塞那么多东西,因为每个请求都得带着他跑。

使用无cookie的域

比如CSS、js、图片等,客户端请求静态文件的时候,减少了 Cookie 的反复传输对主域名的影响。

不要使用滤镜

IE独有属性AlphaImageLoader用于修正7.0以下版本中显示PNG图片的半透明效果。这个滤镜的问题在于浏览器加载图片时它会终止内容的呈现并且冻结浏览器。在每一个元素(不仅仅是图片)它都会运算一次,增加了内存开支,因此它的问题是多方面的。

完全避免使用AlphaImageLoader的最好方法就是使用PNG8格式来代替,这种格式能在IE中很好地工作。如果你确实需要使用AlphaImageLoader,请使用下划线_filter又使之对IE7以上版本的用户无效。

不要在HTML中缩放图片

比如你需要的图片尺寸是50* 50

那就不用用一张500*500的大尺寸图片,影响加载

缩小favicon.ico并缓存

以上是Yslow的23个优化原则

setTimeout(call,0)作用

发表于 2016-11-26 分类于 前端开发

经常看到setTimeout延时0ms的javascript代码,感到很迷惑,难道延时0ms和不延时不是一个道理吗?后来通过查资料以及实验得出以下两个作用,可能还有作用我还不知道,希望得知的朋友在后面评论上不吝指出。

1. 实现javascript的异步

正常情况下javascript都是按照顺序执行的。但是我们可能让该语句后面的语句执行完再执行本身,这时就可以用到setTimeout延时0ms来实现了。 如:

1
2
3
4
5
6

alert(1);

setTimeout("alert(2)", 0);

alert(3);

虽然延时了0ms,但是执行顺序为:1,3,2

这样就保证setTimeout里面的语句在某一代码段中最后执行。

2. 在事件中,setTimeout 会在其完成当前任何延宕事件的事件处理器的执行,以及完成文档当前状态更新后,告诉浏览器去启用 setTimeout 内注册的函数。

举个例子来说这句话的意思,假如当某个事件在页面上建立一个文本框,并给文本框赋值(完成文档当前状态更新),然后将焦点定到文本框,并且选中文本框的内容(后面部分就需要用到setTimeout 延迟0ms实现,否则不好实现)。

先看个例子:

<html lang="en">
<head>
  <title>setTimeout(call,0) demo</title>
</head>
<script type="text/javascript" >

(function(){

 function get(id){

    return document.getElementById(id);
 }

 window.onload = function(){

    get('makeinput').onmousedown = function(){

    var input = document.createElement('input');

    input.setAttribute('type', 'text');

    input.setAttribute('value', 'test1');

    get('inpwrapper').appendChild(input);

    input.focus();

    input.select();

    get('makeinput2').onmousedown = function(){


    var input = document.createElement('input');

    input.setAttribute('type', 'text');

    input.setAttribute('value', 'test1');

    get('inpwrapper2').appendChild(input);

    setTimeout(function(){
        input.focus();
        input.select();

    }, 0);

    get('input1').onkeypress = function(){


    get('preview1').innerHTML = this.value;

    get('input2').onkeypress = function(){
        setTimeout(function(){
            get('preview2').innerHTML = get('input2').value;
        },0 );
    } 
 } 
})();
</script>

<body>
    <h1><code>DEMO1</code></h1>
    <h2>1、未使用 <code>setTimeout</code>(未选中文本框内容)</h2>
    <button id="makeinput">生成 input</button>
    <p id="inpwrapper"></p>

    <h2>2、使用 <code>setTimeout</code>(立即选中文本框内容)</h2>

    <button id="makeinput2">生成 input</button></h2>

    <p id="inpwrapper2"></p>

    --------------------------------------------------------------------------

    <h1><code>DEMO2</code></h1>

    <h2>1、未使用 <code>setTimeout</code>(只有输入第二个字符时,前一个字符才显示出来)</h2>

    <input type="text" id="input1" value=""/><div id="preview1"></div>

    <h2>2、使用 <code>setTimeout</code>(输入时,字符同时显示出来)</h2>

    <input type="text" id="input2" value=""/><div id="preview2"></div>
</body>
</html>

3. 总结

现有的 JavaScript 引擎是单线程处理任务的。它把任务放到队列中,不会同步去执行,必须在完成一个任务后才开始另外一个任务。其实,这是一个把需要执行的任务从队列中跳脱的技巧。在DEMO1中,JavaScript 引擎在执行 onmousedown时,由于没有多线程的同步执行,不可能同时去处理刚创建元素的 focus 和 select 方法,由于这两个方法都不在队列中,在完成 onmousedown 后,JavaScript 引擎已经丢弃了这两个任务,正如第一种情况。而在第二种情况中,由于setTimeout可以把任务从某个队列中跳脱成为新队列,因而能够得到期望的结果。

PHP实现四种基本排序算法

发表于 2016-11-24 分类于 PHP

Q:分别用冒泡排序法,快速排序法,选择排序法,插入排序法将下面数组中的值按照从小到大的顺序进行排序。

1
$arr = array(1,43,54,62,21,66,32,78,36,76,39);

冒泡排序

思路分析:在要排序的一组数中,对当前还未排好的序列,从前往后对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即,每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function bubbleSort($arr)
{
$len = count($arr);
// 该层循环控制 需要冒泡的轮数
for ($i = 1; $i < $len; $i++) {
// 该层循环用来控制每轮 冒出一个数 需要比较的次数
for ($k = 0; $k < $len - $i; $k++) {
if ($arr[$k] > $arr[$k + 1]) {
$tmp = $arr[$k + 1];
$arr[$k + 1] = $arr[$k];
$arr[$k] = $tmp;
}
}
}
return $arr;
}

选择排序

思路分析:在要排序的一组数中,选出最小的一个数与第一个位置的数交换。然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function selectSort($arr)
{
//双重循环完成,外层控制轮数,内层控制比较次数
$len = count($arr);
for ($i = 0; $i < $len - 1; $i++) {
//先假设最小的值的位置
$p = $i;
for ($j = $i + 1; $j < $len; $j++) {
//$arr[$p] 是当前已知的最小值
if ($arr[$p] > $arr[$j]) {
//比较,发现更小的,记录下最小值的位置;并且在下次比较时采用已知的最小值进行比较。
$p = $j;
}
}
//已经确定了当前的最小值的位置,保存到$p中。如果发现最小值的位置与当前假设的位置$i不同,则位置互换即可。
if ($p != $i) {
$tmp = $arr[$p];
$arr[$p] = $arr[$i];
$arr[$i] = $tmp;
}
}
//返回最终结果
return $arr;
}

插入排序

思路分析:在要排序的一组数中,假设前面的数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function insertSort($arr)
{
$len = count($arr);
for ($i=1; $i<$len; $i++) {
$tmp = $arr[$i];
//内层循环控制,比较并插入
for ($j = $i - 1; $j >= 0; $j--) {
if ($tmp < $arr[$j]) {
//发现插入的元素要小,交换位置,将后边的元素与前面的元素互换
$arr[$j + 1] = $arr[$j];
$arr[$j] = $tmp;
} else {
//如果碰到不需要移动的元素,由于是已经排序好是数组,则前面的就不需要再次比较了。
break;
}
}
}
return $arr;
}

快速排序

思路分析:选择一个基准元素,通常选择第一个元素或者最后一个元素。通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素。此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function quickSort($arr)
{
//先判断是否需要继续进行
$length = count($arr);
if ($length <= 1) {
return $arr;
}

//选择第一个元素作为基准
$base_num = $arr[0];

//遍历除了标尺外的所有元素,按照大小关系放入两个数组内

//初始化两个数组
$left_array = array(); //小于基准的
$right_array = array(); //大于基准的

for ($i = 1; $i < $length; $i++) {
if ($base_num > $arr[$i]) {
//放入左边数组
$left_array[] = $arr[$i];
} else {
//放入右边
$right_array[] = $arr[$i];
}
}

//再分别对左边和右边的数组进行相同的排序处理方式递归调用这个函数
$left_array = quickSort($left_array);
$right_array = quickSort($right_array);

//合并
return array_merge($left_array, array($base_num), $right_array);
}

什么是OOP

发表于 2016-11-24

面向对象是相对于面向过程而言的。面向过程语言是一种基于功能分析的、以算法为中心的程序设计方法;而面向对象是一种基于结构分析的、以数据为中心的程序设计思想。早在面向过程语言时代,有一句话说:程序=算法+数据结构。而现在在面向对象语言时代,这句话变为:程序= 对象+消息。对象:万物皆对象; 消息:指对象之间的相互通信。在面向对象语言中有一个有很重要东西,叫做类。从面向过程的角度看,类就是一个特殊的数据结构,它就好像是我们C语言中的结构体;从面向对象的角度看,类就是具有相同属性和方法的对象的集合。

面向对象有三大特性:封装、继承、多态。

封装

所谓封装,就是指隐藏对象的实现细节,给外界提供公共的方法来访问。

这一点,我个人认为和面向过程语言有本质的区别。在C语言中,我们必须在乎每一个实现细节,去关注每一个过程; 而自从从在面向对象语言中提出了封装这个概念后,我们就可以不必要去关心每一个对象的实现细节, 我们只要关注我们所要实现的功能就行,然后根据给我们提供好的接口,我们去面向接口编程就行了。

面向对象的封装思想,我认为应用的最好、最成功的地方,就是在微软的.NET技术上.微软把很多经常用到的功能都封装在一个控件里,作为我们用户不必去在意到底这个控件是用什么实现的,它内部到底是怎么样的?我们只需要关心我们需要实现的功能就行, 然后根据控件给我们提供的属性和方法去操作这些控件,实现我们想要的功能就行了。

继承

面向对象的继承和生物学的继承很相似。子类可以继承父类的公共属性和方法,子类永远没法继承到父类的私有属性和方法。这一点还区别于生物学的继承,生物学中子类可以同时继承父亲和母亲。但是在 java|C#|C++ 等面向对象语言中,是不允许多重继承的,但可以多层继承。

为了弥补不能多重继承这点,在java和c#语言中都提出了接口这一概念。接口就是一种规范。它同样不会有实现细节,而只是给那些要实现这个接口的类一个规范和约束,约束那些实现这些接口的类,要实现我提供的功能,就必须实现我的所有方法, 要不你就声明为抽象类。

多态

多态,就是同一个实现接口,对不同的实例而执行不同的操作。这一点,可以理解为遗传学的变异。 同一个物种的后代由于基因突变或自然环境等影响,而造成不同的个体差异。而我们这里的多态也一样,同属一个基类的不同派生类也可以有自己不同于其他类的属性和方法。

除了这封装、继承、多态这三点基本特征外,面向对象还有一个很重要的概念,叫抽象。抽象就是把提取事物的本质东西,而忽视非本质的东西。对应于抽象这一概念,java和c#中都有一个类叫做抽象类。抽象类中可以给出方法的实现细节,同接口一样如果你要实现我这个抽象类就必须实现我的所有方法,要不你就声明你为抽象类。如果不允许抽象类中有方法的实现细节,这就变成了接口。

总之,面向对象就是万物皆对象,把客观事物当成一个对象来处理的程序设计思想。是一种区别于POP、SOA、面向组件等其他程序设计思想,是一种基于结构分析的,以数据为中心的程序设计思想。

OOP几大设计模式遵循的一般原则:

1.开-闭原则(Open-Closed Principle, OCP)

一个软件实体应当对扩展开发,对修改关闭.说的是,再设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展.换言之,应当可以在不必修改源代码的情况下改变这个模块的行为,在保持系统一定稳定性的基础上,对系统进行扩展。这是面向对象设计(OOD)的基石,也是最重要的原则。

2.里氏代换原则(Liskov Substitution Principle,常缩写为.LSP)

  • 由Barbar Liskov(芭芭拉.里氏)提出,是继承复用的基石。

  • 严格表达:如果每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换称o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型.

换言之,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别.只有衍生类可以替换基类,软件单位的功能才能不受影响,基类才能真正被复用,而衍生类也能够在基类的基础上增加新功能。

  • 反过来的代换不成立

  • <墨子.小取>中说:”白马,马也; 乘白马,乘马也.骊马(黑马),马也;乘骊马,乘马也.”

  • 该类西方著名的例程为:正方形是否是长方形的子类(答案是”否”)。类似的还有椭圆和圆的关系。

  • 应当尽量从抽象类继承,而不从具体类继承,一般而言,如果有两个具体类A,B有继承关系,那么一个最简单的修改方案是建立一个抽象类C,然后让类A和B成为抽象类C的子类.即如果有一个由继承关系形成的登记结构的话,那么在等级结构的树形图上面所有的树叶节点都应当是具体类;而所有的树枝节点都应当是抽象类或者接口.

  • “基于契约设计(Design By Constract),简称DBC”这项技术对LISKOV代换原则提供了支持.该项技术Bertrand Meyer伯特兰做过详细的介绍: 使用DBC,类的编写者显式地规定针对该类的契约.客户代码的编写者可以通过该契约获悉可以依赖的行为方式.契约是通过每个方法声明的前置条件(preconditions)和后置条件(postconditions)来指定的.要使一个方法得以执行,前置条件必须为真.执行完毕后,该方法要保证后置条件为真.就是说,在重新声明派生类中的例程(routine)时,只能使用相等或者更弱的前置条件来替换原始的前置条件,只能使用相等或者更强的后置条件来替换原始的后置条件.

3.依赖倒置原则(Dependence Inversion Principle)

要求客户端依赖于抽象耦合.

  • 表述: 抽象不应当依赖于细节,细节应当依赖于抽象.(Program to an interface, not an implementaction)

  • 表述二: 针对接口编程的意思是说,应当使用接口和抽象类进行变量的类型声明,参量的类型声明,方法的返还类型声明,以及数据类型的转换等.不要针对实现编程的意思就是说,不应当使用具体类进行变量的类型声明,参量类型声明,方法的返还类型声明,以及数据类型的转换等.要保证做到这一点,一个具体的类应等只实现接口和抽象类中声明过的方法,而不应当给出多余的方法.只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参量的类型声明,方法返还类型的声明,属性变量的类型声明等.

  • 接口与抽象的区别就在于抽象类可以提供某些方法的部分实现,而接口则不可以,这也大概是抽象类唯一的优点.如果向一个抽象类加入一个新的具体方法,那么所有的子类型一下子就都得到得到了这个新的具体方法,而接口做不到这一点.如果向一个接口加入了一个新的方法的话,所有实现这个接口的类就全部不能通过编译了,因为它们都没有实现这个新声明的方法.这显然是接口的一个缺点.

  • 一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的登记结构中,而由于一般语言都限制一个类只能从最多一个超类继承,因此将抽象作为类型定义工具的效能大打折扣.
    反过来,看接口,就会发现任何一个实现了一个接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个接口.

  • 从代码重构的角度上讲,将一个单独的具体类重构成一个接口的实现是很容易的,只需要声明一个接口,并将重要的方法添加到接口声明中,然后在具体类定义语句中加上保留字以继承于该接口就行了.而作为一个已有的具体类添加一个抽象类作为抽象类型不那么容易,因为这个具体类有可能已经有一个超类.这样一来,这个新定义的抽象类只好继续向上移动,变成这个超类的超类,如此循环,最后这个新的抽象类必定处于整个类型等级结构的最上端,从而使登记结构中的所有成员都会受到影响.

  • 接口是定义混合类型的理想工具,所为混合类型,就是在一个类的主类型之外的次要类型.一个混合类型表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为.

  • 联合使用接口和抽象类:由于抽象类具有提供缺省实现的优点,而接口具有其他所有优点,所以联合使用两者就是一个很好的选择. 首先,声明类型的工作仍然接口承担的,但是同时给出的还有一个抽象类,为这个接口给出一个缺省实现.其他同属于这个抽象类型的具体类可以选择实现这个接口,也可以选择继承自这个抽象类.如果一个具体类直接实现这个接口的话,它就必须自行实现所有的接口;相反,如果它继承自抽象类的话,它可以省去一些不必要的的方法,因为它可以从抽象类中自动得到这些方法的缺省实现;如果需要向接口加入一个新的方法的话,那么只要同时向这个抽象类加入这个方法的一个具体实现就可以了,因为所有继承自这个抽象类的子类都会从这个抽象类得到这个具体方法.这其实就是缺省适配器模式(Defaule Adapter).

  • 什么是高层策略呢?它是应用背后的抽象,是那些不随具体细节的改变而改变的真理. 它是系统内部的隐喻.

4.接口隔离原则

(Interface Segregation Principle, ISP)

  • 一个类对另外一个类的依赖是建立在最小的接口上。

  • 使用多个专门的接口比使用单一的总接口要好.根据客户需要的不同,而为不同的客户端提供不同的服务是一种应当得到鼓励的做法.就像”看人下菜碟”一样,要看客人是谁,再提供不同档次的饭菜.

  • 胖接口会导致他们的客户程序之间产生不正常的并且有害的耦合关系.当一个客户程序要求该胖接口进行一个改动时,会影响到所有其他的客户程序.因此客户程序应该仅仅依赖他们实际需要调用的方法.

5.合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)

在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过这些向对象的委派达到复用已有功能的目的.这个设计原则有另一个简短的表述:要尽量使用合成/聚合,尽量不要使用继承.

6.迪米特法则(Law of Demeter LoD)又叫做最少知识原则(Least Knowledge Principle,LKP)

就是说,一个对象应当对其他对象有尽可能少的了了解.

迪米特法则最初是用来作为面向对象的系统设计风格的一种法则,与1987年秋天由Ian Holland在美国东北大学为一个叫做迪米特(Demeter)的项目设计提出的,因此叫做迪米特法则[LIEB89][LIEB86].这条法则实际上是很多著名系统,比如火星登陆软件系统,木星的欧罗巴卫星轨道飞船的软件系统的指导设计原则.

没有任何一个其他的OO设计原则象迪米特法则这样有如此之多的表述方式,如下几种:

  • 只与你直接的朋友们通信(Only talk to your immediate friends)

  • 不要跟”陌生人”说话(Don’t talk to strangers)

  • 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些本单位密切相关的软件单位.

就是说,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

7.单一职责原则(Simple responsibility pinciple SRP)

就一个类而言,应该仅有一个引起它变化的原因,如果你能想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责.应该把多于的指责分离出去,分别再创建一些类来完成每一个职责.

常说的OO五大原则就是指其中的 :

1、单一职责原则;

2、开放闭合原则;

3、里氏替换原则;

4、依赖倒置原则;

5、接口隔离原则。

HTTP的请求头标签 If-Modified-Since

发表于 2016-11-22 分类于 PHP

大家都知道客户端浏览器是有缓存的,里面存放之前访问过的一些网页文件。例如IE,会把缓存文件存到“C:\Documents and Settings\用户名\Local Settings\Temporary Internet Files”这样类似的目录里。其实缓存里存储的不只是网页文件,还有服务器发过来的该文件的最后服务器修改时间。

If-Modified-Since是标准的HTTP请求头标签,在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较:

  • 如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端接到之后,就直接把本地缓存文件显示到浏览器中。

  • 如果时间不一致,就返回HTTP状态码200和新的文件内容,客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中。

下面用一个简单的小例子说明一下。

由于演示例子需要截取HTTP Request和Response的信息,我在这里使用的工具是Fiddler(点击下载)。

1.首先在服务器创建一个简单的HTML文件,用浏览器访问一下,成功表示HTML页面。Fiddler就会产生下面的捕获信息。

需要留意的是

(1)因为是第一次访问该页面,客户端发请求时,请求头中没有If-Modified-Since标签。

(2)服务器返回的HTTP状态码是200,并发送页面的全部内容。

(3)服务器返回的HTTP头标签中有Last-Modified,告诉客户端页面的最后修改时间。

2.在浏览器中刷新一下页面,Fiddler就会产生下面的捕获信息。

需要注意的是

(1)客户端发HTTP请求时,使用If-Modified-Since标签,把上次服务器告诉它的文件最后修改时间返回到服务器端了。

(2)因为文件没有改动过,所以服务器返回的HTTP状态码是304,没有发送页面的内容。

3.用文本编辑器稍微改动一下页面文件,保存。再用浏览器访问一下,Fiddler就会产生下面的捕获信息。

需要留意的是

(1)客户端发HTTP请求时,使用If-Modified-Since标签,把上次服务器告诉它的文件最后修改时间返回到服务器端了。

(2)因为文件被改动过,两边时间不一致,所以服务器返回的HTTP状态码是200,并发送新页面的全部内容。

(3)服务器返回的HTTP头标签中有Last-Modified,告诉客户端页面的新的最后修改时间。

HTTP的If-Modified-Since头标签与客户端缓存相互配合,大大节约了网络流量。

mysql优化

发表于 2016-11-21 分类于 数据库

创建索引

对于查询占主要的应用来说,索引显得尤为重要。很多时候性能问题很简单的就是因为我们忘了添加索引而造成的,或者说没有添加更为有效的索引导致。如果不加索引的话,那么查找任何哪怕只是一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降。但是也不是什么情况都非得建索引不可,比如性别可能就只有两个值,建索引不仅没什么优势,还会影响到更新速度,这被称为过度索引。

复合索引

比如有一条语句是这样的:select * from users where area='beijing' and age=22

如果我们是在area和age上分别创建单个索引的话,由于mysql查询每次只能使用一个索引,所以虽然这样已经相对不做索引时全表扫描提高了很多效率,但是如果在area、age两列上创建复合索引的话将带来更高的效率。如果我们创建了(area, age, salary)的复合索引,那么其实相当于创建了(area,age,salary)、(area,age)、(area)三个索引,这被称为最佳左前缀特性。因此我们在创建复合索引时应该将最常用 作限制条件的列放在最左边,依次递减。

索引不会包含有NULL值的列

只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

使用短索引

对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的 列,如果在前10 个或20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

排序的索引问题

mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

like语句操作

一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。

不要在列上进行运算

select * from users where YEAR(adddate)<2007

将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成

select * from users where adddate < '2007-01-01'

8、不使用NOT IN和<>操作

NOT IN和<>操作都不会使用索引将进行全表扫描。NOT IN可以NOT EXISTS代替,id<>3则可使用id>3 or id<3来代替。

Ajax使用详解

发表于 2016-11-21 分类于 前端开发

jQuery使用详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$.ajax({

type: "POST", // 请求方式

contentType: "application/x-www-form-urlencoded", // Body编码

dataType: "html",// 预期服务器返回的数据类型

async: true,// 异步请求,同步的话改成false

url: "http://www.*****.com", // 请求地址

timeout:30000, // 超时

success: (data) => { // 请求成功
alert(data);
},

error: (XMLHttpRequest, textStatus, errorThrown) => { // 请求失败
alert(errorThrown);
}

complete: (XMLHttpRequest, ts) => { // 请求完成(不管成功还是失败)
alert(ts);
}
});

参数

options

类型:Object

可选。AJAX 请求设置。所有选项都是可选的。

async

类型:Boolean

默认值: true。默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。

注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。

beforeSend(XHR)

类型:Function

发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。

XMLHttpRequest 对象是唯一的参数。

这是一个 Ajax 事件。如果返回 false 可以取消本次 ajax 请求。

cache

类型:Boolean

默认值: true,dataType 为 script 和 jsonp 时默认为 false。设置为 false 将不缓存此页面。

jQuery 1.2 新功能。

complete(XHR, TS)

类型:Function

请求完成后回调函数 (请求成功或失败之后均调用)。

参数: XMLHttpRequest 对象和一个描述请求类型的字符串。

这是一个 Ajax 事件。

contentType

类型:String

默认值: “application/x-www-form-urlencoded”。发送信息至服务器时内容编码类型。

默认值适合大多数情况。如果你明确地传递了一个 content-type 给 $.ajax() 那么它必定会发送给服务器(即使没有数据要发送)。

context

类型:Object

这个对象用于设置 Ajax 相关回调函数的上下文。也就是说,让回调函数内 this 指向这个对象(如果不设定这个参数,那么 this 就指向调用本次 AJAX 请求时传递的 options 参数)。比如指定一个 DOM 元素作为 context 参数,这样就设置了 success 回调函数的上下文为这个 DOM 元素。

就像这样:

1
2
3
$.ajax({ url: "test.html", context: document.body, success: function(){
$(this).addClass("done");
}});

data

类型:String

发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:[“bar1”, “bar2”]} 转换为 ‘&foo=bar1&foo=bar2’。

dataFilter

类型:Function

给 Ajax 返回的原始数据的进行预处理的函数。提供 data 和 type 两个参数:data 是 Ajax 返回的原始数据,type 是调用 jQuery.ajax 时提供的 dataType 参数。函数返回的值将由 jQuery 进一步处理。

dataType

类型:String

预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。在 1.4 中,JSON 就会生成一个 JavaScript 对象,而 script 则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。可用值:

  • xml: 返回 XML 文档,可用 jQuery 处理。

  • html: 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。

  • script: 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 “cache” 参数。注意:在远程请求时(不在同一个域下),所有POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script标签来加载)

  • json: 返回 JSON 数据 。

  • jsonp: JSONP 格式。使用 JSONP 形式调用函数时,如 “myurl?callback=?” jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。

  • text: 返回纯文本字符串

error

类型:Function

默认值: 自动判断 (xml 或 html)。请求失败时调用此函数。

有以下三个参数:XMLHttpRequest 对象、错误信息、(可选)捕获的异常对象。

如果发生了错误,错误信息(第二个参数)除了得到 null 之外,还可能是 “timeout”, “error”, “notmodified” 和 “parsererror”。这是一个 Ajax 事件。

global

类型:Boolean

是否触发全局 AJAX 事件。默认值: true。设置为 false 将不会触发全局 AJAX 事件,如 ajaxStart 或 ajaxStop 可用于控制不同的 Ajax 事件。

ifModified

类型:Boolean

仅在服务器数据改变时获取新数据。默认值: false。使用 HTTP 包 Last-Modified 头信息判断。在 jQuery 1.4 中,它也会检查服务器指定的 ‘etag’ 来确定数据没有被修改过。

jsonp

类型:String

在一个 jsonp 请求中重写回调函数的名字。这个值用来替代在 “callback=?” 这种 GET 或 POST 请求中 URL 参数里的 “callback” 部分,比如 {jsonp:’onJsonPLoad’} 会导致将 “onJsonPLoad=?” 传给服务器。

jsonpCallback

类型:String

为 jsonp 请求指定一个回调函数名。这个值将用来取代 jQuery 自动生成的随机函数名。这主要用来让 jQuery 生成度独特的函数名,这样管理请求更容易,也能方便地提供回调函数和错误处理。你也可以在想让浏览器缓存 GET 请求的时候,指定这个回调函数名。

password

类型:String

用于响应 HTTP 访问认证请求的密码

processData

类型:Boolean

默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 “application/x-www-form-urlencoded”。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。

scriptCharset

类型:String

只有当请求时 dataType 为 “jsonp” 或 “script”,并且 type 是 “GET” 才会用于强制修改 charset。通常只在本地和远程的内容编码不同时使用。

success

类型:Function

请求成功后的回调函数。

参数:由服务器返回,并根据 dataType 参数进行处理后的数据;描述状态的字符串。

这是一个 Ajax 事件。

traditional

类型:Boolean

如果你想要用传统的方式来序列化数据,那么就设置为 true。请参考工具分类下面的 jQuery.param 方法。

timeout

类型:Number

设置请求超时时间(毫秒)。此设置将覆盖全局设置。

type

类型:String

默认值: GET。请求方式 (“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。

url

类型:String

默认值: 当前页地址。发送请求的地址。

username

类型:String

用于响应 HTTP 访问认证请求的用户名。

xhr

类型:Function

需要返回一个 XMLHttpRequest 对象。默认在 IE 下是 ActiveXObject 而其他情况下是 XMLHttpRequest 。用于重写或者提供一个增强的 XMLHttpRequest 对象。这个参数在 jQuery 1.3 以前不可用。

MYSQL中竖表和横表之间的相互转换

发表于 2016-11-18 分类于 数据库

横表转为竖表

表tb的结构为

字段名 类型 允许为空 是否主键
id int(11) no yes
姓名 int(11) yes no
语文 decimal(3,1) yes no
数学 decimal(3,1) yes no
英语 decimal(3,1) yes no

表中的数据为

id 姓名 语文 数学 英语
1 小张 89 80 89
2 小丽 69 78 98

现在要求查询到如下结果

name subject score
小丽 语文 69
小丽 数学 78
小丽 英语 79
小张 语文 89
小张 数学 80
小张 英语 89

使用的SQL查询语句应该如下:

1
2
3
4
      select 姓名 as name, '语文' as subject, 语文 as score from tb
union select 姓名 as name, '数学' as subject, 数学 as score from tb
union select 姓名 as name, '英语' as subject, 英语 as score from tb
order by name

或者

1
2
3
4
5
6
7
8
select * from(
select 姓名 as name, '语文' as subject `语文` as score from tb
union
select 姓名 as name, '数学' as subject `数学` as score from tb
union
select 姓名 as name, '英语' as subject `英语` as score from tb
) as tb2
order by name

竖表转为横表

tb2表的结构如下

字段名 类型 允许为空 是否主键
id int(11) no yes
name varchar(255) yes no
subject varchar(255) yes no
score decimal(3,1) yes no

tb2的数据如下

id name subject score
2 小丽 英语 98
3 小丽 数学 78
6 小张 语文 89
5 小张 数学 80
4 小张 英语 89
1 小丽 语文 69

现在想把tb2的数据变为

姓名 语文 英语 数学
小丽 68 98 78
小张 89 89 80

查询的SQL语句如下:

1
2
3
4
5
6
select name as '姓名',
max(case subject when '语文' then score else 0 end) 语文,
max(case subject when '英语' then score else 0 end) 英语,
max(case subject when '数学' then score else 0 end) 数学
from tb2
group by name

现在要求得到如下查询结果

姓名 语文 数学 英语 总分 平均分
小丽 69 78 98 245 81.6666667
小张 89 80 89 258 86

SQL查询语句应该为:

1
2
3
4
5
6
7
8
select `name` as 姓名,
max(case `subject` when '语文' then `score` else 0 end) as 语文,
max(case `subject` when '数学' then `score` else 0 end) as 数学,
max(case `subject` when '英语' then `score` else 0 end) as 英语,
sum(`score`) as 总分,
sum(`score`) as 平均分
from `tb2`
group by `name`
1…141516
Mr.Gou

Mr.Gou

155 日志
11 分类
38 标签
RSS
GitHub E-Mail Weibo
Links
  • 阮一峰的网络日志
  • 离别歌 - 代码审计漏洞挖掘pythonc++
  • 一只猿 - 前端攻城尸
  • 雨了个雨's blog
  • nMask's Blog - 风陵渡口
  • 区块链技术导航
© 2023 蜀ICP备2022014529号