热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

黑马程序员——>第三天<语句、函数、数组简介>

-------android培训、java培训、期待与您交流-------01语句while循环结构代表语句:whiledowhileforwhile(条件表达式

-------android培训java培训、期待与您交流-------

01语句while
循环结构
代表语句:while   do while               for
while(条件表达式)
{
 执行语句;
}

do
{
   执行语句;
}while(条件表达式);

02语句do while
while:先判断条件,只有条件满足才执行循环体
do while:先执行循环体,再判断条件,条件满足,再执行循环体
简单一句话:do while特点是条件无论是否满足,循环体至少被执行一次。
语句结束有两种方式:要么后面跟大括号,要么跟分号

03语句for
格式
for(初始化表达式;循环条件表达式;循环后的操作表达式) for(int x=0;x<3;x++)
{
    执行语句;
}
注:

a.for里面的两个表达式运行的顺序,初始化表达式只读一次,判断循环条件为真就执行循环体,然后再执行循环后的操作表达式,接着继续判断循环条件,重复这个过程,直到条件不满足为止
b.while与for可以互换,区别在于for为了循环而定义的变量再for循环结束就是再内存中释放,而while循环使用的变量在循环结束后还可以继续使用
c.最简单无限循环格式:while(true),for(;;)无限循环存在的原因是并不知道循环多少次,而是根据某些条件来控制循环

1.初始化表达式只执行一次
2.只有条件不满足循环就结束

04while和for的区别
1.变量有自己的作用域,对于for来讲:如果将用于控制循环的增量定义再for语句中,那么该变量只在for语句内有效,for语句执行完毕,该变量再内存中被释放
2.for和while可以进行互换,如果需要定义循环增量,用for更合适
3.当要对某些语句执行很多次时,就使用循环结构

05循环语句的其他特点
1.强调循环条件表达式必须知道真假,条件既不是真也不是假,编译就是失败的
2.初始化表达式只要是一个执行语句,它就可以作为一个合法表达式存在
3.语法格式:当多表达式出现时,用逗号分隔开
4.初始化表达式可以不写,循环后的操作表达式也可以不写,循环条件表达式不写,默认为true

06for语句练习——累加计数器
1.获取1~10的和,并打印


int sum = 0;                //1.定义变量用于存储不断变化的和
int x = 1 ; //2.定义变量,记录住不断变化的被加的数
while(x<=10) //3.定义循环,重复加法的过程
{
sum = sum +x; // 循环注意:一定要明确哪些语句需要参与循环,哪些不需要
x++;
}
System.out.println("sum="+sum);

int sum = 0;
for(int x=0; x<=10; x++) //用for语句写更合适(因为循环中的变量x运行完就不需要在内存中存在了)
{
sum += x;
}
System.out.println("for sum = "+sum);



其实这就是累加思想(一个数不断的变大或者一个数不断地变小以记录住不断变化的结果;字符串的不断延长); 

记住代码只能解决一个问题,记住思想,能够解决一类问题
原理:通过变量记录住每次变化的结果(循环操作后的结果)   通过循环的形式,进行累加动作
 
一说累加,不多说,搞一变量,搞一循环(要有循环,没有循环就不叫累加了)


2.1~100之间7的倍数的个数,并打印
思路:
1,先对1~100进行循环(遍历)通过循环的形式。
2,在遍历的过程中,定义条件,只对7的倍数进行操作
3,因为7的倍数不确定,只要符合条件,就通过一个变量来记录住这个变化的次数


步骤:
1,定义循环语句,选择for语句;
2,在循环中定义判断,只要是7的倍数即可,使用if语句。条件:7的倍数x%7==0;
3,定义变量,该变量随着7的倍数的出现而自增

int count = 0;
for(int x=1; x<=100; x++)
{
if(x%7==0) // 这个数据的状态变化用这个记录器(count)
// System.out.println("x="+x); // 来记录 这个记录器不一定是自增的
count++; // 也可以是自减(手机的使用寿命)
}
System.out.println("count="+count);


 

计数器思想:
通过一个变量记录住数据的状态变化
也需要通过循环完成

如果再有一样需求说想获取某一个里面符合条件的有多少个时,要立刻想计数器  计数器中的条件根据需求来定

07for嵌套
//语句嵌套形式,其实就是语句中还有语句
//循环嵌套

for(int x=0; x<3; x++)
{
for(int y=0; y<4; y++)
{
System.out.print("*")
}
System.out.println();//只有一个功能就是换行
}



****
****
****   对于打印长方形:外循环控制行数,内循环控制的是每一行的列数,也就是一行中元素的个数

练习

*****      
****
***
**
*

分析:发现图形有很多行,每一个行有很多列
      要使用嵌套循环。原理:形象说法:大圈套小圈(毕老师独家)
for(int x=0;x<5;x++)
{
     for(int y=x;y<5;y++)         
    {
         System.out.print("*")
    }
     System.out.println();

 


08for嵌套练习


*
**
***
****
*****

for(int x=0; x<5; x++)
{
for(int y=0; y<=x; y++) // 若写成y {
System.out.print("*");
}
System.out.println();
}


 

不是规律的规律:尖朝上,可以改变条件,让条件随着外循环变化;
                尖朝下,可以改变初始化值,让初始化随着外循环变化

 

1
12
123
1234
12345

for(int x=1; x<=5; x++)
{
for(int y=1; y<=x; y++) //思考:第一列都是1,每一行的列的变化在自增
{
System.out.print(y);
}
System.out.println();
}


09for嵌套  九九乘法表
九九乘法表
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9


尖朝上,改变循环条件       y被乘数每一行的第一列都是1,   y=1
乘数随着行数的递增而递增 x=1;x<=3;x++

for(int x=1; x<=3; x++)
{
for(int y=1; y<=x; y++)
{
System.out.print(y+"*"+x+"="+y*x+"\t"); // 这个表达式里边分清楚谁是变化的,谁是不变的
} // \t制表符,
System.out.println();
}

10-语句(break-continue)
其他流程控制语句
break(跳出)(跳出当前循环)    

 continue(继续)


break语句:应用范围:选择结构和循环结构
continue语句:应用与循环结构

a.这两个语句离开应用范围,存在是没有意义的
b.这两个语句单独存在 下面都不可以有语句,因为执行不到
c.continue语句是结束本次循环继续下次循环
d.标号的出现,可以让这两个语句作用于指定的范围  只能用于循环上,给循环起名字的一种方式
//break

w:for(int x=0; x<3; x++)
{
q:for(int y=0; y<4; y++)
{
System.out.println("x="+x);
break q; //不写标号,默认跳出内循环
}

}



//continue:只能作用于虚幻结构,继续循环   特点:结束本次循环继续下次循环


 

w:for(int x=0; x<3; x++)
{
q:for(int y=0; y<4; y++)
{
System.out.println("x="+x);
continue w;
} // 记住: 1.break和continue语句作用的范围
// 2.break和continue单独存在时,下面不可以有任何语句,因为都执行不到
}

11-语句(练习)
----*
---* *
--* * *
-* * * *
* * * * *

for(int x=0; x<5; x++)
{
for(int y=x+1; y<5; y++) // y=x+1 消除前面的空格,缩短距离
{
System.out.print("-")
}
for(int z=0;z<=x;z++)
{
System.out.print("* ")
}
System.out.println();
}


 

* * * * *
-* * * *
--* * *
---* *
----*

for(int x=0; x<5; x++)
{
for(int y=0; y{
System.out.print(" ");
}
for(int z=x; z<5;z++)
{
System.out.print("* ");
}
System.out.println();
}

12-函数概述

函数的定义
 函数就是定义在类中的具有特定功能的一段独立小程序。
 函数也称为方法;
格式
修饰符 返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,....)
{
     执行语句;
     return 返回值;
}
返回值类型:函数运行后的结果的数据类型
参数类型:是形式参数的数据类型        (就是定义一个变量,用来接收你给的那个要运算的值)
形式参数:是一个变量,用于存储调用函数时传递给函数的实际参数
实际参数:传递给形式参数的具体数值
return:用于结束函数
返回值:该值会返回给调用者


 发现以上的运算,因为获取不同数据的运算结果,代码出现了重复
 为了提高代码的复用性,对代码进行抽取
 将这个部分定义成一个独立的功能,方便日后使用
 java中对功能的定义是通过函数的形式来体现的
//需要定义功能,完成一个整数的*3+5的运算
1.先明确函数定义的格式
修饰符 返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,....)
{
     执行语句;
     return 返回值;
}

public static int getResult(int num)
{
return num*3+5; //实际上是把一部分代码封装起来,实现了复用性
}


 

怎么使用
getResult(7);   可以被重复使用
函数的特点
 定义函数可以将功能代码进行封装
 便于对该功能进行复用
 函数只有被调用才会被执行
 函数的出现提高了代码的复用性
 对于函数没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写
//当函数运算后,没有具体的返回值时,这时返回值类型用一个特殊的关键字来标识,该关键字就是void,void代表的是函数没有具体返回值的情况;当函数的返回值类型是void时,函数中的return语句可以省略不写
注意 
  函数中只能调用函数,不可以在函数内部定义函数
  定义函数时,函数的结果应该返回给调用者,交由调用者处理


13-函数(应用)
函数的应用
  我们在编写程序实际上就是在不断的实现功能,而java当中,最小的功能单元就是函数。所以日后再写代码时,你只要在处理或者定义功能,都把它定义到一个独立的函数中去,而不要再把代码乱七八糟都塞在主函数里。这时,主函数的功能就剩一个:调用函数(用主函数调用写好的函数,运行一下来测试函数运行的结果是否正确)。这样方便后期的复用。

必须要掌握的一个思想(贯穿于其他语言)
  如何定义一个函数呢?
    1.既然函数是一个独立的功能,那么该功能的运算结果是什么先明确
        因为这是在明确函数的返回值类型
    2.在明确在定义该功能的过程中是否需要未知的内容参与运算(在编写功能内部细节时,这个细节是我完全独立能编写完,还是依靠调用者给我一些数据才能完成细节的编写 )
       因为是在明确函数的参数列表(参数的类型和参数的个数)
 

   练习
需求:定义一个功能,完成3+4的运算,并将结果返回给调用者
1.明确功能的结果:是一个整数的和
2.在实现该功能的过程中是否哟未知内容参与运算/没有
其实这两个功能就是在明确函数的定义
1.是在明确函数的返回值类型
2.明确函数的参数列表(参数的类型和参数的个数)

public static int getSum()
{
return 3+4;
}


 

以上这个函数的功能,结果是固定的,毫无扩展性而言
为了方便用户需求,由用户来指定加数和被加数,这样,功能才有意义

思路:
1.功能结果是一个和,返回值类型是int
2.有未知内容参与运算,有两个,这两个未知内容的类型都是int

public static int getSum(int x,int y)
{
return x+y;
}


 


需求:判断两个数是否相同
思路:
1.明确功能的结果:结果是:boolean
2.功能是否有未知内容参与运算。有,两个整数

public static boolean compare(int a,int b)
{
if(a==b)
return true;
else // 能否省略:如果a和b不相等,直接读false这句话
return false;
}

上面的if语句可以简写成一下格式:

return (a==b)?true:false;  //三元运算符

return a==b;  //a==b,==是比较运算符,运算完直接有个真假值出现,往回一返就ok(最简单)


这就是代码基本的简化过程

 

需求:定义功能,对两个数进行比较,获取较大的数


 

public static int getMax(int a,int b)
{
if(a>b)
return a;
else
return b;
}

//下面为简化格式
{
return (a>b)?a:b;
}

 

小问题:定义函数时,你只要完成相对应的功能就可以了,不要做过多的功能!
  这个功能定义思想有问题,为什么呢?因为只为完成加法运算,至于是否要对和进行打印操作,那是调用者的事,不要在该功能中完成

public static int getMax(int a,int b)
{
System.out.println(a+b);
return;
}


14函数(练习)
1.定义一个功能,用于打印矩形。
2.定义一个打印99乘法表功能的函数。


定义一个功能,用于打印矩形。
思路:
1.确定结果:没有,因为直接打印,所以返回值类型是void
2.有未知内容吗?有,两个,因为矩形的行和列不确定

public static void draw(int row,int col)
{
for(int x=0; x {
for(int y=0; y {
System.out.print("*");
}
System.out.println();
}
}


 

定义一个打印99乘法表功能的函数。

qublic static void print99()
{
for(int x=1; x<=9; x++)
{
for(int y=1; y<=x;y++)
{
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.println();
}
}



总结:将代码搞一个地方存起来(函数是用于存放代码的),存完后,给这个地方起一个名字,想什么时候用,到这个地方照这个名字就可以了;一找到这个名字,虚拟机自动就找符合这个名称的函数
提高复用性,函数的最基本体现。


15函数(重载)overload
函数的重载
重载的概念:在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可
重载的特点:与返回值类型无关,只看参数列表
重载的好处:方便与阅读,优化了程序设计

//定义一个加法运算,获取两个整数的和

public static int add(int x,int y)  // 函数名称(add)的作用:用于标识函数;方便与调用;用与通过这个名称,体现这个函数的功能
{
return x+y; // 定义名称要有意义,这个意义就是为了阅读性而体现的
}  //  定义函数名的重点是 实现函数的功能

 

//定义一个加法运算,获取三个整数的和

public static int add(int x,int y,int z)     //   因为功能一致,所以用的功能名称也一致
{
return x+y+z;
}


 

函数就相当于              这样就又一次提高代码的复用性

 public static int add(int x,int y,int z)     
{
return add(x,y)+z;
}



函数重载:保证名称一致就可以了

什么时候用重载?
  当定义的功能相同,但参与运算的未知内容不同,那么,这时候就定义一个函数名称以表示该功能,方便阅读,而通过参数列表的不同来区分多个同名函数。

public static void print99(int num)
{
for(int x=1; x<=num; x++)
{
for(int y=1; y<=x;y++)
{
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.println();
}
}


 

      功能一致,参数不同  一个有参数一个没有参数

//打印99乘法表

qublic static void print99()
{
for(int x=1; x<=9; x++)
{
for(int y=1; y<=x;y++)
{
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.println();
}
}



    函数就相当于

qublic static void print99()
{
print99(9);调用上面的函数
}




16函数(重载练习)

void show(int a,char b,double c){}  
a
void show(int x,char y,double z){}  没有,因为和原函数一样
b
int show(int a,double c,char b){}   重载,因为参数类型不同,注意:重载和返回值类型没关系
c
void show(int a,double c,char b){}  重载,因为参数类型不同,注意:重载和返回值类型没关系
d
boolean show(int c,char b){}        重载了,因为参数个数不同
e
void show(double c){}               重载了,因为参数个数不同
f
double show(int x,char y,double z){}没有,这个函数不可以和给定函数同时存在与一个类中
(都叫show函数,参数也一致,调用show函数的时候,它到底是没有返回值还是返回double不确定)
重载的特点:与返回值类型无关,只看参数列表


重载的好处:方便与阅读,优化了程序设计


17数组(概述——内存结构)
数组的定义
概念:同一种类型数据的集合。其实数组就是一个容器
数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些元素
格式1:
    元素类型[] 数组名 = new 元素类型[元素个数或数组长度];

示例:int[] arr = new int[5];  5是元素的个数  右边部分定义了一个真实存在的数组,存储5个元素
                                这个数组为了方便使用,就定义了一个名称arr
任何一个变量它都得有自己的数据类型,因为得确定变量中要存什么东西,前面[]前的int代表数组中存的元素的类型;而变量arr不是int类型;是数组类型(数组是一种单独的数据类型)
格式2
     元素类型[] 数组名 = new 元素类型[]{元素,元素,.....};

  int[] arr = new int[]{3,5,1,7};
  int[] arr = {3,5,1,7};
其中 关键字new表示:用来在内存中产生一个容器实体

    任何一个应用程序在运行时都需要在内存中开辟一个空间,


内存结构
  java程序在运行时,需要在内存中分配空间。为了提高运算效率,又对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据的方式和内存管理方式
栈内存:用于存储局部变量,当数据使用完,所占空间会自动释放
堆内存:
 数组和对象,通过new建立的实例都存放在堆内存中
 每一个实体都有内存地址值
 实体中的变量都有默认初始化值
 实体不再被使用,会在不确定的时间内被垃圾回收器回收
方法区
本地方法区
寄存器

在主函数中定义一个int x = 3;其实这个3就在主函数的内存区域当中:主函数运行时会在栈内存中开辟一个空间,这个空间里就会有一个变量叫做x,值是3;这个区域就是主函数区域,

int[] x = new int[3];
x[0] = 59;在数组中赋值
x = null; x不再指向这个数组而是指向空


这句话在内存中是什么样的呢?
   在栈内存中定义x,(凡是局部变量(定义在方法中、方法中参数上的变量、for循环当中)都在栈内存中)这个局部变量会在栈内存当中开辟一个空间,凡是new出来的实体都在堆内存中(堆里面放的就是实体 包括数组和对象);右边这部分一new就在堆内存中产生了一个空间,因为要放三个元素,所以分配成三个格子,而每一个格子都有一个编号分别为数组中的[0](0角标位)[1][2]。这个堆怎么和x联系起来呢?
堆内存的特点:
1.

对内存中的每一个实体都有一个存放位置(内存中的地址值),数组在内存中存放时,总有一个起始位置,这个起始位置如果用十六进制(因为表示起来更短一些)来表示0x0078,那么用它来标识数组位置的话,我们只需要将这个地址值赋给x。 ( 这是一个赋值动作:赋的不是数组本身,是数组在内存中的地址) x就有值了,x就指向了这个数组或者叫x引用了这个数组【为什么叫引用数据类型(数组没有真正的存放到x变量中去,而是只存放数组的地址,在引用这个数组而已)】
//打印数组中角标为0的元素的值:格式:System.out.println(x[0]);  数组一被定义,里边的元素都有值 
2.堆内存中的实体是用于封装数据的,对内存中的实体化的值都有默认初始化值  默认值根据你数组中元素的类型而定  如果是int型数组 默认为0    double  0.0   fload  0.0f  boolean  false

3.垃圾回收机制
在堆内存当中没有任何引用所使用它的话,我们视它为垃圾,这个垃圾不会立刻被内存清除掉,而是在不定时间内,启动垃圾回收机制,将这个数组实体在堆内存中清除
 

引用数据类型不同之处
两个引用指向同一个对象或者叫做同一个数组  两个引用在同时操作同一个数组

 -------android培训java培训、期待与您交流-------


推荐阅读
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
author-avatar
kaxiaoliog_334
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有