1
0
mirror of https://github.com/adambard/learnxinyminutes-docs.git synced 2025-09-25 05:51:31 +02:00

Merge pull request #147 from lichenbo/master-cn

Chinese translated version of c and elisp tutorial
This commit is contained in:
Adam Bard
2013-07-31 22:03:42 -07:00
4 changed files with 1773 additions and 0 deletions

394
zh-cn/c-cn.html.markdown Executable file
View File

@@ -0,0 +1,394 @@
---
language: c
filename: learnc.c
contributors:
- ["Adam Bard", "http://adambard.com/"]
translators:
- ["Chenbo Li", "http://binarythink.net/"]
---
C语言在今天仍然是高性能计算的主要选择。
C大概是大多数程序员用到的最接近底层的语言了但是C语言本身不仅可以用来提升程序运行的速度
注意看看C语言的文档你就会知道C语言在内存管理方面的强大也是其他语言无法比拟的。
```c
// 用“//”来实现单行注释
/*
多行注释是这个样子的
*/
// 用#include来导入头文件
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// 函数的标签(signature)应该放在.h文件中并且引入到程序顶部
// 也可以直接放到你的.c文件的最上面
void function_1();
void function_2();
// c程序的入口是一个返回值为int型的函数名字叫做main
int main() {
// 用printf来实现标准输出这种输出也可以用格式来控制
// %d 代表一个整数, \n 代表一个新行
printf("%d\n", 0); // => 输出 0
// 所有的语句都要以分号结束
///////////////////////////////////////
// 类型
///////////////////////////////////////
// 在使用变量之前我们必须先声明它们。
// 变量在声明时需要指明其类型,而类型能够告诉系统这个变量所占用的空间
// int型整型变量一般占用4个字节
int x_int = 0;
// short型短整型变量一般占用2个字节
short x_short = 0;
// char型字符型变量会占用1个字节
char x_char = 0;
char y_char = 'y'; // 字符变量的字面值需要用单引号包住
// long型长整型一般需要4个字节到8个字节; 而long long型则至少需要8个字节64位
long x_long = 0;
long long x_long_long = 0;
// float一般是用32位表示的浮点数字
float x_float = 0.0;
// double一般是用64位表示的浮点数字
double x_double = 0.0;
// 整数类型也可以有无符号的类型表示。这样这些变量就无法表示负数
// 但是无符号整数所能表示的范围就可以比原来的整数大一些
unsigned char ux_char;
unsigned short ux_short;
unsigned int ux_int;
unsigned long long ux_long_long;
// char类型一定会占用1个字节但是其他的类型却会因具体机器的不同而各异
// sizeof(T) 可以返回T类型在运行的机器上占用多少个字节
// 这样你的代码就可以在各处正确运行了
// 比如
printf("%lu\n", sizeof(int)); // => 4 (字长为4的机器上)
// 数组必须要在开始被初始化为特定的长度
char my_char_array[20]; // 这个数组占据 1 * 20 = 20 个字节
int my_int_array[20]; // 这个数组占据 4 * 20 = 80 个字节
// (这里我们假设字长为4)
// 可以用下面的方法把数组初始化为0:
char my_array[20] = {0};
// 对数组任意存取就像其他语言的方式 -- 其实是其他的语言像C
my_array[0]; // => 0
// 数组是可变的,其实就是内存的映射!
my_array[1] = 2;
printf("%d\n", my_array[1]); // => 2
// 字符串就是以 NUL (0x00) 这个字符结尾的字符数组,
// 这个字符可以用'\0'来表示.
// (在字符串字面值中我们不必输入这个字符,编译器会自动添加的)
char a_string[20] = "This is a string";
printf("%s\n", a_string); // %s 可以对字符串进行格式化
/*
也许你会注意到 a_string 实际上只有16个字节长.
第17个字节是一个空字符(NUL)
而第18, 19 和 20 个字符的值是不确定的。
*/
printf("%d\n", a_string[16]); // => 0
///////////////////////////////////////
// 操作符
///////////////////////////////////////
int i1 = 1, i2 = 2; // 多个变量声明的简写
float f1 = 1.0, f2 = 2.0;
// 算数运算
i1 + i2; // => 3
i2 - i1; // => 1
i2 * i1; // => 2
i1 / i2; // => 0 (0.5 会被化整为 0)
f1 / f2; // => 0.5, 也许会有很小的误差
// 取余运算
11 % 3; // => 2
// 比较操作符我们也很熟悉, 但是有一点C中没有布尔类型
// 而是用整形替代
// 0 就是 false, 其他的就是 true. (比较操作符的返回值则仅有0和1)
3 == 2; // => 0 (false)
3 != 2; // => 1 (true)
3 > 2; // => 1
3 < 2; // => 0
2 <= 2; // => 1
2 >= 2; // => 1
// 逻辑运算符需要作用于整数
!3; // => 0 (非)
!0; // => 1
1 && 1; // => 1 (且)
0 && 1; // => 0
0 || 1; // => 1 (或)
0 || 0; // => 0
// 位运算
~0x0F; // => 0xF0 (取反)
0x0F & 0xF0; // => 0x00 (和)
0x0F | 0xF0; // => 0xFF (或)
0x04 ^ 0x0F; // => 0x0B (异或)
0x01 << 1; // => 0x02 (左移1位)
0x02 >> 1; // => 0x01 (右移1位)
///////////////////////////////////////
// 控制结构
///////////////////////////////////////
if (0) {
printf("I am never run\n");
} else if (0) {
printf("I am also never run\n");
} else {
printf("I print\n");
}
// While循环
int ii = 0;
while (ii < 10) {
printf("%d, ", ii++); // ii++ 在取值过后自增
} // => 输出 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n");
int kk = 0;
do {
printf("%d, ", kk);
} while (++kk < 10); // ++kk 先自增,在被取值
// => 输出 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n");
// For 循环
int jj;
for (jj=0; jj < 10; jj++) {
printf("%d, ", jj);
} // => 输出 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n");
///////////////////////////////////////
// 类型转换
///////////////////////////////////////
// 在C中每个变量都有类型你可以将变量的类型进行转换
int x_hex = 0x01; // 可以用16进制赋值
// 在类型转换时,数字本身的值会被保留下来
printf("%d\n", x_hex); // => 输出 1
printf("%d\n", (short) x_hex); // => 输出 1
printf("%d\n", (char) x_hex); // => 输出 1
// 类型转换时可能会造成溢出,而且不会抛出警告
printf("%d\n", (char) 257); // => 1 (char的最大值为255)
// 整数型和浮点型可以互相转换
printf("%f\n", (float)100); // %f 表示单精度浮点
printf("%lf\n", (double)100); // %lf 表示双精度浮点
printf("%d\n", (char)100.0);
///////////////////////////////////////
// 指针
///////////////////////////////////////
// 指针变量是用来储存内存地址的变量
// 指针变量的定义也会告诉你指向的地址的变量的类型
// 你也可以得到某个变量的地址,并对它们进行操作
int x = 0;
printf("%p\n", &x); // 用 & 来获取变量的地址
// (%p 表示一个指针)
// => 输出某个内存地址
// 指针类型在定义是需要以*结束
int* px; // px是一个指向int型的指针
px = &x; // 把x的地址保存到px中
printf("%p\n", px); // => 输出内存中的某个地址
// 要得到某个指针指向的内容的值,可以在指针前加一个*来取得(去引用)
printf("%d\n", *px); // => 输出 0, 即x的值
// 你也可以改变指针所指向的值
// 此时你需要在*运算符后添加一个括号,因为++比*的优先级更高
(*px)++; // 把px所指向的值增加2
printf("%d\n", *px); // => 输出 1
printf("%d\n", x); // => 输出 1
int x_array[20]; // 数组是分配一系列连续空间的常用方式
int xx;
for (xx=0; xx<20; xx++) {
x_array[xx] = 20 - xx;
} // 初始化 x_array 为 20, 19, 18,... 2, 1
// 生命一个变量为指向整型的指针类型并初始化为指向x_array
int* x_ptr = x_array;
// x_ptr现在指向了数组的第一个元素(即整数20).
// 事实上数组本身就是指向它的第一个元素的指针
printf("%d\n", *(x_ptr)); // => 输出 20
printf("%d\n", x_array[0]); // => 输出 20
// 指针的增减多少是依据它本身的类型而定的
printf("%d\n", *(x_ptr + 1)); // => 输出 19
printf("%d\n", x_array[1]); // => 输出 19
// 你也可以通过标准库函数malloc来实现动态分配
// 这个函数接受一个代表容量的参数
// 系统会从堆区分配指定容量字节大小的空间
int* my_ptr = (int*) malloc(sizeof(int) * 20);
for (xx=0; xx<20; xx++) {
*(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx 也可以
} // 初始化内存为 20, 19, 18, 17... 2, 1 (as ints)
// 如果对一些未分配的内存取值则会得到未知的结果
printf("%d\n", *(my_ptr + 21)); // => 谁知道会输出什么
// 当你通过malloc得到一块区域后你需要释放它
// 否则没人能够再次使用这块内存,直到程序结束为止
free(my_ptr);
// 字符串通常是字符数组,但是经常用字符指针表示
// 指针:
char* my_str = "This is my very own string";
printf("%c\n", *my_str); // => 'T'
function_1();
} // main函数结束
///////////////////////////////////////
// 函数
///////////////////////////////////////
// 函数声明语法:
// <返回值类型> <函数名称>(<参数>)
int add_two_ints(int x1, int x2){
return x1 + x2; // 用return来返回一个值
}
/*
函数是按值传递的, 但是你可以通过传递参数来传递引用,这样函数就可以更改值
例子:字符串本身翻转
*/
// 类型为void的函数没有返回值
void str_reverse(char* str_in){
char tmp;
int ii=0, len = strlen(str_in); // Strlen 是C标准库函数
for(ii=0; ii<len/2; ii++){
tmp = str_in[ii];
str_in[ii] = str_in[len - ii - 1]; // 从倒数第ii个开始
str_in[len - ii - 1] = tmp;
}
}
/*
char c[] = "This is a test.";
str_reverse(c);
printf("%s\n", c); // => ".tset a si sihT"
*/
///////////////////////////////////////
// 用户自定义类型和结构
///////////////////////////////////////
// Typedefs可以创建类型别名
typedef int my_type;
my_type my_type_var = 0;
// 结构是一系列数据的集合
struct rectangle {
int width;
int height;
};
void function_1(){
struct rectangle my_rec;
// 通过 . 来访问结构中的数据
my_rec.width = 10;
my_rec.height = 20;
// 你也可以声明指向结构体的指针
struct rectangle* my_rec_ptr = &my_rec;
// 通过取值来改变结构体的成员...
(*my_rec_ptr).width = 30;
// ... 或者用 -> 操作符作为简写
my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10;
}
// 你也可以用typedef来给一个结构体起一个别名
typedef struct rectangle rect;
int area(rect r){
return r.width * r.height;
}
///////////////////////////////////////
// 函数指针
///////////////////////////////////////
/*
在运行时,函数本身也被存放到某块内存区域当中
函数指针就像其他指针一样, 但却可以被用来直接调用函数,
并且可以被四处传递(就像回调函数那样)
但是,定义的语法有可能在一开始会有些误解
例子通过指针调用str_reverse
*/
void str_reverse_through_pointer(char * str_in) {
// 定义一个函数指针 f.
void (*f)(char *); // 签名一定要与目标函数相同
f = &str_reverse; // 将函数的地址在运行时赋给指针
(*f)(str_in); // 通过指针调用函数
// f(str_in); // 等价于这种调用方式
}
/*
只要函数签名是正确的,任何时候都能将正确的函数赋给某个函数指针
为了可读性和简洁性函数指针经常和typedef搭配使用
*/
typedef void (*my_fnp_type)(char *);
// 实际声明函数指针会这么用:
// ...
// my_fnp_type f;
```
## 更多阅读
最好找一本 [K&R, aka "The C Programming Language", “C程序设计语言”](https://en.wikipedia.org/wiki/The_C_Programming_Language)
其他一些比较好的资源 [Learn C the hard way](http://c.learncodethehardway.org/book/)
除了这些多多Google吧

340
zh-cn/elisp-cn.html.markdown Executable file
View File

@@ -0,0 +1,340 @@
---
language: elisp
contributors:
- ["Bastien Guerry", "http://bzg.fr"]
translators:
- ["Chenbo Li", "http://binarythink.net"]
filename: learn-emacs-lisp.el
---
```scheme
;; 15分钟学会Emacs Lisp (v0.2a) 作者bzghttps://github.com/bzg 译者lichenbohttp://douban.com/people/lichenbo
;;
;; 请先阅读Peter Norvig的一篇好文:
;; http://norvig.com/21-days.html
;; 译者注中文版请见http://blog.youxu.info/21-days/
;;
;; 之后安装GNU Emacs 24.3:
;;
;; Debian: apt-get install emacs (视具体发行版而定)
;; MacOSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg
;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip
;;
;; 更多信息可以在这里找到:
;; http://www.gnu.org/software/emacs/#Obtaining
;; 很重要的警告:
;;
;; 按照这个教程来学习并不会对你的电脑有任何损坏
;; 除非你自己在学习的过程中愤怒地把它砸了
;; 如果出现了这种情况,我不会承担任何责任
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 打开emacs
;;
;; 按'q'消除欢迎界面
;;
;; 现在请注意窗口底部的那一个灰色长条
;;
;; "*scratch*" 是你现在编辑界面的名字。
;; 这个编辑界面叫做一个"buffer"。
;;
;; 每当你打开Emacs时都会默认打开这个scratch buffer
;; 此时你并没有在编辑任何文件而是在编辑一个buffer
;; 之后你可以将这个buffer保存到一个文件中。
;;
;; 之后的"Lisp interaction" 则是表明我们可以用的某组命令
;;
;; Emacs在每个buffer中都有一组内置的命令
;; 而当你激活某种特定的模式时,就可以使用相应的命令
;; 这里我们使用`lisp-interaction-mode'
;; 这样我们就可以使用内置的Emacs Lisp以下简称Elisp命令了。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 分号是注释开始的标志
;;
;; Elisp 是由符号表达式构成的 (即"s-表达式"或"s式"):
(+ 2 2)
;; 这个s式的意思是 "对2进行加2操作".
;; s式周围有括号而且也可以嵌套:
(+ 2 (+ 1 1))
;; 一个s式可以包含原子符号或者其他s式
;; 在上面的例子中1和2是原子符号
;; (+ 2 (+ 1 1)) 和 (+ 1 1) 是s式.
;; 在 `lisp-interaction-mode' 中你可以计算s式.
;; 把光标移到闭括号后之后按下ctrl+j以后简写为'C-j'
(+ 3 (+ 1 2))
;; ^ 光标放到这里
;; 按下`C-j' 就会输出 6
;; `C-j' 会在buffer中插入当前运算的结果
;; 而`C-xC-e' 则会在emacs最底部显示结果也就是被称作"minibuffer"的区域
;; 为了避免把我们的buffer填满无用的结果我们以后会一直用`C-xC-e'
;; `setq' 可以将一个值赋给一个变量
(setq my-name "Bastien")
;; `C-xC-e' 输出 "Bastien" (在 mini-buffer 中显示)
;; `insert' 会在光标处插入字符串:
(insert "Hello!")
;; `C-xC-e' 输出 "Hello!"
;; 在这里我们只传给了insert一个参数"Hello!", 但是
;; 我们也可以传给它更多的参数比如2个
(insert "Hello" " world!")
;; `C-xC-e' 输出 "Hello world!"
;; 你也可以用变量名来代替字符串
(insert "Hello, I am " my-name)
;; `C-xC-e' 输出 "Hello, I am Bastien"
;; 你可以把s式嵌入函数中
(defun hello () (insert "Hello, I am " my-name))
;; `C-xC-e' 输出 hello
;; 现在执行这个函数
(hello)
;; `C-xC-e' 输出 Hello, I am Bastien
;; 函数中空括号的意思是我们不需要接受任何参数
;; 但是我们不能一直总是用my-name这个变量
;; 所以我们现在使我们的函数接受一个叫做"name"的参数
(defun hello (name) (insert "Hello " name))
;; `C-xC-e' 输出 hello
;; 现在我们调用这个函数,并且将"you"作为参数传递
(hello "you")
;; `C-xC-e' 输出 "Hello you"
;; 成功!
;; 现在我们可以休息一下
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 下面我们在新的窗口中新建一个名为 "*test*" 的buffer:
(switch-to-buffer-other-window "*test*")
;; `C-xC-e' 这时屏幕上会显示两个窗口,而光标此时位于*test* buffer内
;; 用鼠标单击上面的buffer就会使光标移回。
;; 或者你可以使用 `C-xo' 是的光标跳到另一个窗口中
;; 你可以用 `progn'命令将s式结合起来:
(progn
(switch-to-buffer-other-window "*test*")
(hello "you"))
;; `C-xC-e' 此时屏幕分为两个窗口,并且在*test* buffer中显示"Hello you"
;; 现在为了简洁我们需要在每个s式后面都使用`C-xC-e'来执行,后面就不再说明了
;; 记得可以用过鼠标或者`C-xo'回到*scratch*这个buffer。
;; 清除当前buffer也是常用操作之一
(progn
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello "there"))
;; 也可以回到其他的窗口中
(progn
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello "you")
(other-window 1))
;; 你可以用 `let' 将一个值和一个局部变量绑定:
(let ((local-name "you"))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello local-name)
(other-window 1))
;; 这里我们就不需要使用 `progn' 了, 因为 `let' 也可以将很多s式组合起来。
;; 格式化字符串的方法:
(format "Hello %s!\n" "visitor")
;; %s 是字符串占位符,这里被"visitor"替代.
;; \n 是换行符。
;; 现在我们用格式化的方法再重写一下我们的函数:
(defun hello (name)
(insert (format "Hello %s!\n" name)))
(hello "you")
;; 我们再用`let'新建另一个函数:
(defun greeting (name)
(let ((your-name "Bastien"))
(insert (format "Hello %s!\n\nI am %s."
name ; the argument of the function
your-name ; the let-bound variable "Bastien"
))))
;; 之后执行:
(greeting "you")
;; 有些函数可以和用户交互:
(read-from-minibuffer "Enter your name: ")
;; 这个函数会返回在执行时用户输入的信息
;; 现在我们让`greeting'函数显示你的名字:
(defun greeting (from-name)
(let ((your-name (read-from-minibuffer "Enter your name: ")))
(insert (format "Hello!\n\nI am %s and you are %s."
from-name ; the argument of the function
your-name ; the let-bound var, entered at prompt
))))
(greeting "Bastien")
;; 我们让结果在另一个窗口中显示:
(defun greeting (from-name)
(let ((your-name (read-from-minibuffer "Enter your name: ")))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(insert (format "Hello %s!\n\nI am %s." your-name from-name))
(other-window 1)))
;; 测试一下:
(greeting "Bastien")
;; 第二节结束,休息一下吧。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 我们将一些名字存到列表中;
(setq list-of-names '("Sarah" "Chloe" "Mathilde"))
;; 用 `car'来取得第一个名字:
(car list-of-names)
;; 用 `cdr'取得剩下的名字:
(cdr list-of-names)
;; 用 `push'把名字添加到列表的开头:
(push "Stephanie" list-of-names)
;; 注意: `car' 和 `cdr' 并不修改列表本身, 但是 `push' 却会对列表本身进行操作.
;; 这个区别是很重要的: 有些函数没有任何副作用(比如`car'
;; 但还有一些却是有的 (比如 `push').
;; 我们来对`list-of-names'列表中的每一个元素都使用hello函数:
(mapcar 'hello list-of-names)
;; 将 `greeting' 改进,使的我们能够对`list-of-names'中的所有名字执行:
(defun greeting ()
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(mapcar 'hello list-of-names)
(other-window 1))
(greeting)
;; 记得我们之前定义的 `hello' 函数吗? 这个函数接受一个参数,名字。
;; `mapcar' 调用 `hello', 并将`list-of-names'作为参数先后传给`hello'
;; 现在我们对显示的buffer中的内容进行一些更改
(defun replace-hello-by-bonjour ()
(switch-to-buffer-other-window "*test*")
(goto-char (point-min))
(while (search-forward "Hello")
(replace-match "Bonjour"))
(other-window 1))
;; (goto-char (point-min)) 将光标移到buffer的开始
;; (search-forward "Hello") 查找字符串"Hello"
;; (while x y) 当x返回某个值时执行y这个s式
;; 当x返回`nil' (空), 退出循环
(replace-hello-by-bonjour)
;; 你会看到所有在*test* buffer中出现的"Hello"字样都被换成了"Bonjour"
;; 你也会得到以下错误提示: "Search failed: Hello".
;;
;; 如果要避免这个错误, 你需要告诉 `search-forward' 这个命令是否在
;; buffer的某个地方停止查找, 并且在什么都没找到时是否应该不给出错误提示
;; (search-forward "Hello" nil t) 可以达到这个要求:
;; `nil' 参数的意思是 : 查找并不限于某个范围内
;; `t' 参数的意思是: 当什么都没找到时,不给出错误提示
;; 在下面的函数中我们用到了s式并且不给出任何错误提示:
(defun hello-to-bonjour ()
(switch-to-buffer-other-window "*test*")
(erase-buffer)
;; 为`list-of-names'中的每个名字调用hello
(mapcar 'hello list-of-names)
(goto-char (point-min))
;; 将"Hello" 替换为"Bonjour"
(while (search-forward "Hello" nil t)
(replace-match "Bonjour"))
(other-window 1))
(hello-to-bonjour)
;; 给这些名字上个色:
(defun boldify-names ()
(switch-to-buffer-other-window "*test*")
(goto-char (point-min))
(while (re-search-forward "Bonjour \\(.+\\)!" nil t)
(add-text-properties (match-beginning 1)
(match-end 1)
(list 'face 'bold)))
(other-window 1))
;; 这个函数使用了 `re-search-forward': 和查找一个字符串不同,你用这个命令可以查找一个模式,即正则表达式
;; 正则表达式 "Bonjour \\(.+\\)!" 的意思是:
;; 字符串 "Bonjour ", 之后跟着
;; 一组 | \\( ... \\) 结构
;; 任意字符 | . 的含义
;; 有可能重复的 | + 的含义
;; 之后跟着 "!" 这个字符串
;; 准备好了?试试看。
(boldify-names)
;; `add-text-properties' 可以添加文字属性, 比如文字样式
;; 好的,我们成功了!
;; 如果你想对一个变量或者函数有更多的了解:
;;
;; C-h v 变量 回车
;; C-h f 函数 回车
;;
;; 阅读Emacs Lisp官方文档:
;;
;; C-h i m elisp 回车
;;
;; 在线阅读Emacs Lisp文档:
;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html
;; 感谢以下同学的建议和反馈:
;; - Wes Hardaker
;; - notbob
;; - Kevin Montuori
;; - Arne Babenhauserheide
;; - Alan Schmitt
```

404
zh-cn/java-cn.html.markdown Executable file
View File

@@ -0,0 +1,404 @@
---
language: java
contributors:
- ["Jake Prather", "http://github.com/JakeHP"]
translators:
- ["Chenbo Li", "http://binarythink.net"]
filename: LearnJava.java
---
Java是一个通用的程序语言, 包含并发, 基于类的面向对象等特性
[阅读更多](http://docs.oracle.com/javase/tutorial/java/index.html)
```java
// 单行注释
/*
多行注释
*/
/**
JavaDocJava文档注释是这样的。可以用来描述类和类的属性。
*/
// 导入 java.util中的 ArrayList 类
import java.util.ArrayList;
// 导入 java.security 包中的所有类
import java.security.*;
// 每个 .java 文件都包含一个public类这个类的名字必须和这个文件名一致。
public class LearnJava {
// 每个程序都需要有一个main函数作为入口
public static void main (String[] args) {
// 使用 System.out.println 来输出到标准输出
System.out.println("Hello World!");
System.out.println(
"Integer: " + 10 +
" Double: " + 3.14 +
" Boolean: " + true);
// 如果要在输出后不自动换行可以使用System.out.print方法。
System.out.print("Hello ");
System.out.print("World");
///////////////////////////////////////
// 类型与变量
///////////////////////////////////////
// 用 <type> <name> 来声明变量
// 字节类型 - 8位补码表示
// (-128 <= 字节 <= 127)
byte fooByte = 100;
// 短整型 - 16位补码表示
// (-32,768 <= 短整型 <= 32,767)
short fooShort = 10000;
// 整型 - 32位补码表示
// (-2,147,483,648 <= 整型 <= 2,147,483,647)
int fooInt = 1;
// 长整型 - 64位补码表示
// (-9,223,372,036,854,775,808 <= 长整型 <= 9,223,372,036,854,775,807)
long fooLong = 100000L;
// L可以用来表示一个数字是长整型的。
// 其他的数字都默认为整型。
// 注意Java中没有无符号类型
// 浮点型 - 即 IEEE 754 规定的32位单精度浮点类型
float fooFloat = 234.5f;
// f用来表示一个数字是浮点型的。
// 否则会被默认当做是双精度浮点型。
// 双精度浮点型 - 即 IEEE 754 规定的64位双精度浮点类型
double fooDouble = 123.4;
// 布尔类型 - true 与 false
boolean fooBoolean = true;
boolean barBoolean = false;
// 字符类型 - 16位 Unicode编码字符
char fooChar = 'A';
// 用 final 可以使一个常量不可更改
final int HOURS_I_WORK_PER_WEEK = 9001;
// 字符串
String fooString = "My String Is Here!";
// \n 代表一个新的换行
String barString = "Printing on a new line?\nNo Problem!";
// \t 代表一个新的制表符
String bazString = "Do you want to add a tab?\tNo Problem!";
System.out.println(fooString);
System.out.println(barString);
System.out.println(bazString);
// 数组
// 数组在声明时大小必须已经确定
// 数组的声明格式:
//<数据类型> [] <变量名> = new <数据类型>[<数组大小>];
int [] intArray = new int[10];
String [] stringArray = new String[1];
boolean [] booleanArray = new boolean[100];
// 声明并初始化数组也可以这样:
int [] y = {9000, 1000, 1337};
// 随机访问数组中的元素
System.out.println("intArray @ 0: " + intArray[0]);
// 数组下标从0开始并且可以被更改
intArray[1] = 1;
System.out.println("intArray @ 1: " + intArray[1]); // => 1
// 其他数据类型
// ArrayLists - 类似于数组,但是功能更多,并且大小也可以改变
// LinkedLists
// Maps
// HashMaps
///////////////////////////////////////
// 操作符
///////////////////////////////////////
System.out.println("\n->Operators");
int i1 = 1, i2 = 2; // 多重声明可以简化
// 算数运算
System.out.println("1+2 = " + (i1 + i2)); // => 3
System.out.println("2-1 = " + (i2 - i1)); // => 1
System.out.println("2*1 = " + (i2 * i1)); // => 2
System.out.println("1/2 = " + (i1 / i2)); // => 0 (0.5 truncated down)
// 取余
System.out.println("11%3 = "+(11 % 3)); // => 2
// 比较操作符
System.out.println("3 == 2? " + (3 == 2)); // => false
System.out.println("3 != 2? " + (3 != 2)); // => true
System.out.println("3 > 2? " + (3 > 2)); // => true
System.out.println("3 < 2? " + (3 < 2)); // => false
System.out.println("2 <= 2? " + (2 <= 2)); // => true
System.out.println("2 >= 2? " + (2 >= 2)); // => true
// 位运算操作符
/*
~ 补
<< 带符号左移
>> 带符号右移
>>> 无符号右移
& 和
^ 异或
| 相容或
*/
// 自增
int i = 0;
System.out.println("\n->Inc/Dec-rementation");
System.out.println(i++); //i = 1 后自增
System.out.println(++i); //i = 2 前自增
System.out.println(i--); //i = 1 后自减
System.out.println(--i); //i = 0 前自减
///////////////////////////////////////
// 控制结构
///////////////////////////////////////
System.out.println("\n->Control Structures");
// If语句和C的类似
int j = 10;
if (j == 10){
System.out.println("I get printed");
} else if (j > 10) {
System.out.println("I don't");
} else {
System.out.println("I also don't");
}
// While循环
int fooWhile = 0;
while(fooWhile < 100)
{
//System.out.println(fooWhile);
//增加计数器
//遍历99次 fooWhile 0->99
fooWhile++;
}
System.out.println("fooWhile Value: " + fooWhile);
// Do While循环
int fooDoWhile = 0;
do
{
//System.out.println(fooDoWhile);
//增加计数器
//遍历99次, fooDoWhile 0->99
fooDoWhile++;
}while(fooDoWhile < 100);
System.out.println("fooDoWhile Value: " + fooDoWhile);
// For 循环
int fooFor;
//for 循环结构 => for(<起始语句>; <循环进行的条件>; <步长>)
for(fooFor=0; fooFor<10; fooFor++){
//System.out.println(fooFor);
//遍历 10 次, fooFor 0->9
}
System.out.println("fooFor Value: " + fooFor);
// Switch Case 语句
// switch可以用来处理 byte, short, char, 和 int 数据类型
// 也可以用来处理枚举类型,字符串类,和原始数据类型的包装类:
// Character, Byte, Short, 和 Integer
int month = 3;
String monthString;
switch (month){
case 1:
monthString = "January";
break;
case 2:
monthString = "February";
break;
case 3:
monthString = "March";
break;
default:
monthString = "Some other month";
break;
}
System.out.println("Switch Case Result: " + monthString);
///////////////////////////////////////
// 类型转换
///////////////////////////////////////
// 数据转换
// 将字符串转换为整型
Integer.parseInt("123");//返回整数123
// 将整型转换为字符串
Integer.toString(123);//返回字符串"123"
// 其他的数据也可以进行互相转换:
// Double
// Long
// String
// 类型转换
// 你也可以对java对象进行类型转换, 但其中会牵扯到很多概念
// 在这里可以查看更详细的信息:
// http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
///////////////////////////////////////
// 类与函数
///////////////////////////////////////
System.out.println("\n->Classes & Functions");
// (Bicycle类定义如下)
// 用new来实例化一个类
Bicycle trek = new Bicycle();
// 调用对象的方法
trek.speedUp(3); // 需用getter和setter方法
trek.setCadence(100);
// toString 可以把对象转换为字符串
System.out.println("trek info: " + trek.toString());
} // main 方法结束
} // LearnJava 类结束
// 你也可以把其他的非public类放入到.java文件中
// 类定义的语法:
// <public/private/protected> class <类名>{
// //成员变量, 构造函数, 函数
// //Java中函数被称作方法
// }
class Bicycle {
// Bicycle 类的成员变量和方法
public int cadence; // Public: 任意位置均可访问
private int speed; // Private: 只在同类中可以访问
protected int gear; // Protected: 可以在同类与子类中可以访问
String name; // default: 可以在包内中可以访问
// 构造函数是初始化一个对象的方式
// 以下是一个默认构造函数
public Bi450635425cycle() {
gear = 1;
cadence = 50;
speed = 5;
name = "Bontrager";
}
// 一下是一个含有参数的构造函数
public Bicycle(int startCadence, int startSpeed, int startGear, String name) {
this.gear = startGear;
this.cadence = startCadence;
this.speed = startSpeed;
this.name = name;
}
// 函数语法:
// <public/private/protected> <返回值类型> <函数名称>(<参数列表>)
// Java类中经常会用getter和setter来对成员变量进行操作
// 方法声明的语法:
// <作用域> <返回值类型> <方法名>(<参数列表>)
public int getCadence() {
retur450635425n cadence;
}
// void返450635425回值函数没有返回值
public void setCadence(int newValue) {
cadence = newValue;
}
public void setGear(int newValue) {
gear = newValue;
}
public void speedUp(int increment) {
speed += increment;
}
public void slowDown(int decrement) {
speed -= decrement;
}
public void setName(String newName) {
name = newName;
}
public String getName() {
return name;
}
//返回对象属性的方法
@Override
public String toString() {
return "gear: " + gear +
" cadence: " + cadence +
" speed: " + speed +
" name: " + name;
}
} // Bicycle 类结束
// PennyFarthing 是 Bicycle 的子类
class PennyFarthing extends Bicycle {
// (Penny Farthings 是前轮很大的 Bicycle 并且没有齿轮)
public PennyFarthing(int startCadence, int startSpeed){
// 通过super调用父类的构造函数
super(startCadence, startSpeed, 0, "PennyFarthing");
}
// 你可以用@注释来表示需要重载的方法
// 了解更多的注释使用方法,可以访问下面的地址:
// http://docs.oracle.com/javase/tutorial/java/annotations/
@Override
public void setGear(int gear) {
gear = 0;
}
}
```
## 更多阅读
下面的链接只是为了便于大家理解这些主题而给出的对于具体的例子请大家自行Google
其他主题:
* [Java 官方教程](http://docs.oracle.com/javase/tutorial/index.html)
* [Java 访问修饰符](http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html)
* [面向对象程序设计概念](http://docs.oracle.com/javase/tutorial/java/concepts/index.html):
* [继承](http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html)
* [多态](http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html)
* [抽象](http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html)
* [异常](http://docs.oracle.com/javase/tutorial/essential/exceptions/index.html)
* [接口](http://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html)
* [泛型](http://docs.oracle.com/javase/tutorial/java/generics/index.html)
* [Java代码规范](http://www.oracle.com/technetwork/java/codeconv-138413.html)

635
zh-cn/php-cn.html.markdown Executable file
View File

@@ -0,0 +1,635 @@
---
language: php
contributors:
- ["Malcolm Fell", "http://emarref.net/"]
- ["Trismegiste", "https://github.com/Trismegiste"]
translators:
- ["Chenbo Li", "http://binarythink.net"]
filename: learnphp.php
---
这份教程所使用的版本是 PHP 5+.
```php
<?php // PHP必须被包围于 <?php ? > 之中
// 如果你的文件中只有php代码那么最好省略结束括号标记
// 这是单行注释的标志
# 井号也可以,但是//更常见
/*
这是多行注释
*/
// 使用 "echo" 或者 "print" 来输出信息到标准输出
print('Hello '); // 输出 "Hello " 并且没有换行符
// () 对于echo和print是可选的
echo "World\n"; // 输出 "World" 并且换行
// (每个语句必须以分号结尾)
// 在 <?php 标签之外的语句都被自动输出到标准输出
?>Hello World Again!
<?php
/************************************
* 类型与变量
*/
// 变量以$开始
// 变量可以以字母或者下划线开头,后面可以跟着数字、字母和下划线
// 布尔值是大小写无关的
$boolean = true; // 或 TRUE 或 True
$boolean = false; // 或 FALSE 或 False
// 整型
$int1 = 12; // => 12
$int2 = -12; // => -12
$int3 = 012; // => 10 (0开头代表八进制数)
$int4 = 0x0F; // => 15 (0x开头代表十六进制数)
// 浮点型 (即双精度浮点型)
$float = 1.234;
$float = 1.2e3;
$float = 7E-10;
// 算数运算
$sum = 1 + 1; // 2
$difference = 2 - 1; // 1
$product = 2 * 2; // 4
$quotient = 2 / 1; // 2
// 算数运算的简写
$number = 0;
$number += 1; // $number 自增1
echo $number++; // 输出1 (运算后自增)
echo ++$number; // 输出3 (自增后运算)
$number /= $float; // 先除后赋值给 $number
// 字符串需要被包含在单引号之中
$sgl_quotes = '$String'; // => '$String'
// 如果需要在字符串中引用变量,就需要使用双引号
$dbl_quotes = "This is a $sgl_quotes."; // => 'This is a $String.'
// 特殊字符只有在双引号中有用
$escaped = "This contains a \t tab character.";
$unescaped = 'This just contains a slash and a t: \t';
// 可以把变量包含在一对大括号中
$money = "I have $${number} in the bank.";
// 自 PHP 5.3 开始, nowdocs 可以被用作多行非计算型字符串
$nowdoc = <<<'END'
Multi line
string
END;
// 而Heredocs则可以用作多行计算型字符串
$heredoc = <<<END
Multi line
$sgl_quotes
END;
// 字符串需要用 . 来连接
echo 'This string ' . 'is concatenated';
/********************************
* 数组
*/
// PHP 中的数组都是关联型数组,也就是某些语言中的哈希表或字典
// 在所有PHP版本中均适用
$associative = array('One' => 1, 'Two' => 2, 'Three' => 3);
// PHP 5.4 中引入了新的语法
$associative = ['One' => 1, 'Two' => 2, 'Three' => 3];
echo $associative['One']; // 输出 1
// 声明为列表实际上是给每个值都分配了一个整数键key
$array = ['One', 'Two', 'Three'];
echo $array[0]; // => "One"
/********************************
* 输出
*/
echo('Hello World!');
// 输出到标准输出
// 此时标准输出就是浏览器中的网页
print('Hello World!'); // 和echo相同
// echo和print实际上也属于这个语言本身所以我们省略括号
echo 'Hello World!';
print 'Hello World!';
$paragraph = 'paragraph';
echo 100; // 直接输出标量
echo $paragraph; // 或者输出变量
// 如果你配置了短标签或者使用5.4.0及以上的版本
// 你就可以使用简写的echo语法
?>
<p><?= $paragraph ?></p>
<?php
$x = 1;
$y = 2;
$x = $y; // $x 现在和 $y 的值相同
$z = &$y;
// $z 现在持有 $y 的引用. 现在更改 $z 的值也会更改 $y 的值,反之亦然
// 但是改变 $y 的值不会改变 $x 的值
echo $x; // => 2
echo $z; // => 2
$y = 0;
echo $x; // => 2
echo $z; // => 0
/********************************
* 逻辑
*/
$a = 0;
$b = '0';
$c = '1';
$d = '1';
// 如果assert的参数为假就会抛出警告
// 下面的比较都为真,不管它们的类型是否匹配
assert($a == $b); // 相等
assert($c != $a); // 不等
assert($c <> $a); // 另一种不等的表示
assert($a < $c);
assert($c > $b);
assert($a <= $b);
assert($c >= $d);
// 下面的比较只有在类型相同、值相同的情况下才为真
assert($c === $d);
assert($a !== $d);
assert(1 == '1');
assert(1 !== '1');
// 变量可以根据其使用来进行类型转换
$integer = 1;
echo $integer + $integer; // => 2
$string = '1';
echo $string + $string; // => 2 (字符串在此时被转化为整数)
$string = 'one';
echo $string + $string; // => 0
// 输出0因为'one'这个字符串无法被转换为整数
// 类型转换可以将一个类型视作另一种类型
$boolean = (boolean) 1; // => true
$zero = 0;
$boolean = (boolean) $zero; // => false
// 还有一些专用的函数来进行类型转换
$integer = 5;
$string = strval($integer);
$var = null; // 空值
/********************************
* 控制结构
*/
if (true) {
print 'I get printed';
}
if (false) {
print 'I don\'t';
} else {
print 'I get printed';
}
if (false) {
print 'Does not get printed';
} elseif(true) {
print 'Does';
}
// 三目运算符
print (false ? 'Does not get printed' : 'Does');
$x = 0;
if ($x === '0') {
print 'Does not print';
} elseif($x == '1') {
print 'Does not print';
} else {
print 'Does print';
}
// 下面的语法常用语模板中:
?>
<?php if ($x): ?>
This is displayed if the test is truthy.
<?php else: ?>
This is displayed otherwise.
<?php endif; ?>
<?php
// 用switch来实现相同的逻辑
switch ($x) {
case '0':
print 'Switch does type coercion';
break; // 在case中必须使用一个break语句
// 否则在执行完这个语句后会直接执行后面的语句
case 'two':
case 'three':
// 如果$variable是 'two' 或 'three',执行这里的语句
break;
default:
// 其他情况
}
// While, do...while 和 for 循环
$i = 0;
while ($i < 5) {
echo $i++;
}; // 输出 "01234"
echo "\n";
$i = 0;
do {
echo $i++;
} while ($i < 5); // 输出 "01234"
echo "\n";
for ($x = 0; $x < 10; $x++) {
echo $x;
} // 输出 "0123456789"
echo "\n";
$wheels = ['bicycle' => 2, 'car' => 4];
// Foreach 循环可以遍历数组
foreach ($wheels as $wheel_count) {
echo $wheel_count;
} // 输出 "24"
echo "\n";
// 也可以同时遍历键和值
foreach ($wheels as $vehicle => $wheel_count) {
echo "A $vehicle has $wheel_count wheels";
}
echo "\n";
$i = 0;
while ($i < 5) {
if ($i === 3) {
break; // 退出循环
}
echo $i++;
} // 输出 "012"
for ($i = 0; $i < 5; $i++) {
if ($i === 3) {
continue; // 跳过此次遍历
}
echo $i;
} // 输出 "0124"
/********************************
* 函数
*/
// 通过"function"定义函数:
function my_function () {
return 'Hello';
}
echo my_function(); // => "Hello"
// 函数名需要以字母或者下划线开头,
// 后面可以跟着任意的字幕、下划线、数字.
function add ($x, $y = 1) { // $y 是可选参数,默认值为 1
$result = $x + $y;
return $result;
}
echo add(4); // => 5
echo add(4, 2); // => 6
// $result 在函数外部不可访问
// print $result; // 抛出警告
// 从 PHP 5.3 起我们可以定义匿名函数
$inc = function ($x) {
return $x + 1;
};
echo $inc(2); // => 3
function foo ($x, $y, $z) {
echo "$x - $y - $z";
}
// 函数也可以返回一个函数
function bar ($x, $y) {
// 用 'use' 将外部的参数引入到里面
return function ($z) use ($x, $y) {
foo($x, $y, $z);
};
}
$bar = bar('A', 'B');
$bar('C'); // 输出 "A - B - C"
// 你也可以通过字符串调用函数
$function_name = 'add';
echo $function_name(1, 2); // => 3
// 在通过程序来决定调用哪个函数时很有用
// 或者,使用 call_user_func(callable $callback [, $parameter [, ... ]]);
/********************************
* 导入
*/
<?php
// 被导入的php文件也必须以php开标签开始
include 'my-file.php';
// 现在my-file.php就在当前作用域中可见了
// 如果这个文件无法被导入(比如文件不存在),会抛出警告
include_once 'my-file.php';
// my-file.php中的代码在其他地方被导入了那么就不会被再次导入
// 这会避免类的多重定义错误
require 'my-file.php';
require_once 'my-file.php';
// 和include功能相同只不过如果不能被导入时会抛出错误
// my-include.php的内容:
<?php
return 'Anything you like.';
// 文件结束
// Include和Require函数也有返回值
$value = include 'my-include.php';
// 被引入的文件是根据文件路径或者include_path配置来查找到的
// 如果文件最终没有被找到,那么就会查找当前文件夹。之后才会报错
/* */
/********************************
* 类
*/
// 类是由class关键字定义的
class MyClass
{
const MY_CONST = 'value'; // 常量
static $staticVar = 'static';
// 属性必须声明其作用域
public $property = 'public';
public $instanceProp;
protected $prot = 'protected'; // 当前类和子类可访问
private $priv = 'private'; // 仅当前类可访问
// 通过 __construct 来定义构造函数
public function __construct($instanceProp) {
// 通过 $this 访问当前对象
$this->instanceProp = $instanceProp;
}
// 方法就是类中定义的函数
public function myMethod()
{
print 'MyClass';
}
final function youCannotOverrideMe()
{
}
public static function myStaticMethod()
{
print 'I am static';
}
}
echo MyClass::MY_CONST; // 输出 'value';
echo MyClass::$staticVar; // 输出 'static';
MyClass::myStaticMethod(); // 输出 'I am static';
// 通过new来新建实例
$my_class = new MyClass('An instance property');
// 如果不传递参数,那么括号可以省略
// 用 -> 来访问成员
echo $my_class->property; // => "public"
echo $my_class->instanceProp; // => "An instance property"
$my_class->myMethod(); // => "MyClass"
// 使用extends来生成子类
class MyOtherClass extends MyClass
{
function printProtectedProperty()
{
echo $this->prot;
}
// 方法覆盖
function myMethod()
{
parent::myMethod();
print ' > MyOtherClass';
}
}
$my_other_class = new MyOtherClass('Instance prop');
$my_other_class->printProtectedProperty(); // => 输出 "protected"
$my_other_class->myMethod(); // 输出 "MyClass > MyOtherClass"
final class YouCannotExtendMe
{
}
// 你可以使用“魔法方法”来生成getter和setter方法
class MyMapClass
{
private $property;
public function __get($key)
{
return $this->$key;
}
public function __set($key, $value)
{
$this->$key = $value;
}
}
$x = new MyMapClass();
echo $x->property; // 会使用 __get() 方法
$x->property = 'Something'; // 会使用 __set() 方法
// 类可以是被定义成抽象类 (使用 abstract 关键字) 或者
// 去实现接口 (使用 implements 关键字).
// 接口需要通过interface关键字来定义
interface InterfaceOne
{
public function doSomething();
}
interface InterfaceTwo
{
public function doSomethingElse();
}
// 接口可以被扩展
interface InterfaceThree extends InterfaceTwo
{
public function doAnotherContract();
}
abstract class MyAbstractClass implements InterfaceOne
{
public $x = 'doSomething';
}
class MyConcreteClass extends MyAbstractClass implements InterfaceTwo
{
public function doSomething()
{
echo $x;
}
public function doSomethingElse()
{
echo 'doSomethingElse';
}
}
// 一个类可以实现多个接口
class SomeOtherClass implements InterfaceOne, InterfaceTwo
{
public function doSomething()
{
echo 'doSomething';
}
public function doSomethingElse()
{
echo 'doSomethingElse';
}
}
/********************************
* 特征
*/
// 特征 从 PHP 5.4.0 开始包括,需要用 "trait" 这个关键字声明
trait MyTrait
{
public function myTraitMethod()
{
print 'I have MyTrait';
}
}
class MyTraitfulClass
{
use MyTrait;
}
$cls = new MyTraitfulClass();
$cls->myTraitMethod(); // 输出 "I have MyTrait"
/********************************
* 命名空间
*/
// 这部分是独立于这个文件的
// 因为命名空间必须在一个文件的开始处。
<?php
// 类会被默认的放在全局命名空间中,可以被一个\来显式调用
$cls = new \MyClass();
// 为一个文件设置一个命名空间
namespace My\Namespace;
class MyClass
{
}
// (或者从其他文件中)
$cls = new My\Namespace\MyClass;
//或者从其他命名空间中
namespace My\Other\Namespace;
use My\Namespace\MyClass;
$cls = new MyClass();
// 你也可以为命名空间起一个别名
namespace My\Other\Namespace;
use My\Namespace as SomeOtherNamespace;
$cls = new SomeOtherNamespace\MyClass();
*/
```
## 更多阅读
访问 [PHP 官方文档](http://www.php.net/manual/)
如果你对最佳实践感兴趣(实时更新) [PHP The Right Way](http://www.phptherightway.com/).
如果你很熟悉善于包管理的语言 [Composer](http://getcomposer.org/).
如要了解通用标准请访问PHP Framework Interoperability Group's [PSR standards](https://github.com/php-fig/fig-standards).