一个计算矩形面积的程序,怎么会出错?

wn10000neng 2008-05-18 11:53:52
我定义了一个名为Rectange 的矩形类,包括的属性有矩形的左下角和右上角2个点的坐标,要求计算并打印宽(width),高(height),面积(area);

我的的程序如下,但是打印出的结果跟我算的不一样,换了几组数据都是出错,不知道为什么?

class Rectangle
{
int x1,y1,x2,y2;
int width,height;


Rectangle(int x1,int y1,int x2,int y2)//构造函数
{
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
}




int getWidth()//计算宽width
{
width=x2-x1;
return width;
}


int getHeight()//计算高heigth
{
height=y2=y1;
return height;
}


int getArea()//计算面积
{

return (width*height);
}


public static void main(String []args)
{
Rectangle db1=new Rectangle (20,50,100,80);
System.out.println("width="+db1.getWidth());
System.out.println("height="+db1.getHeight());
System.out.println("area="+db1.getArea());
}
}
...全文
347 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
aipb2008 2008-05-21
  • 打赏
  • 举报
回复
呵呵,错误还是比较明显的!
apples_009 2008-05-21
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 KK3K2005 的回复:]
int getHeight()//计算高heigth
{
height=y2=y1; //请解释下 这句的用意
return height;
}
[/Quote]
...
condor888 2008-05-21
  • 打赏
  • 举报
回复
呵呵,楼主要细心点啊!
ldxfsh 2008-05-21
  • 打赏
  • 举报
回复
你要先写2个set方法给width,height赋值.
get方法是取值的,没有用set方法给width,height这2个属性设置值,你的getArea方法当然不能计算出值了.
ZX_ARES 2008-05-21
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 wn10000neng 的回复:]
引用 2 楼 KK3K2005 的回复:
int getHeight()//计算高heigth
{
height=y2=y1; //请解释下 这句的用意
return height;
}


晕,-写成=号,谢谢!
[/Quote]
我倒!
看来键盘上“-”和“=”离的太近了...
lanseqiuyu 2008-05-21
  • 打赏
  • 举报
回复
details is the key to success!
wxinb 2008-05-21
  • 打赏
  • 举报
回复
int getHeight()//计算高heigth
{
height=y2=y1; // 问题!!!
return height;
}
kingaking 2008-05-21
  • 打赏
  • 举报
回复
计算面积的算法也有问题的说,变量应该用坐标点X1、X2、Y1、Y2,否则就有可能出现结果为0的BUG
tomiehu 2008-05-21
  • 打赏
  • 举报
回复
height=y2=y1;没有这种写法
kch198725 2008-05-21
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 wxinb 的回复:]
int getHeight()//计算高heigth
{
height=y2=y1; // 问题!!!
return height;
}
[/Quote]
key_hua 2008-05-21
  • 打赏
  • 举报
回复
看来又是一个细节决定成败的问题
apples_009 2008-05-21
  • 打赏
  • 举报
回复

import java.util.Scanner;
import java.io.*;
public class Bidu{

int x1,x2,y1,y2;
int width,heigh,area;
public void setBidu(int x1,int x2,int y1,int y2){
this.x1=x1;
this.x2=x2;
this.y1=y1;
this.y2=y2;
}
public int getWidth(){
width=x2-x1;
return width;
}
public int getHeigh(){
heigh=y2-y1;
return heigh;
}

public int getArea(){
area=width*heigh;
return area;
}

public static void main(String args []){

Bidu bidu=new Bidu();
//String input="12 23 45 56";
int count=0;

outer:while(true){
count++;
System.out.println("--------------------------------------");
System.out.print("第"+count+"次"+"请输入四个坐标如(x1 x2 x3 x4)示:");
Scanner s=new Scanner(System.in);//.useDelimiter("\\s* \\s*");//使用正则表达式来取数;
int a1=s.nextInt();
int a2=s.nextInt();
int a3=s.nextInt();
int a4=s.nextInt();
if(a1<0||a2<0||a3<0||a4<0){
System.out.println("你输入的数非法,请重新输入");
count=0;
continue outer;
}
else if(a1>a2){
int temp=a1;
a1=a2;
a2=temp;
}
if(a3>a4){
int temp=a3;
a3=a4;
a4=temp;
}
bidu.setBidu(a1,a2,a3,a4);

System.out.println("矩形宽为"+bidu.getWidth());
System.out.println("矩形长为"+bidu.getHeigh());
System.out.println("矩形的面积为"+bidu.getArea());
if(count==4)break;
}
}
}
wn10000neng 2008-05-20
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 sp272530 的回复:]
我定义了一个名为Rectange 的矩形类,包括的属性有矩形的左下角和右上角2个点的坐标,要求计算并打印宽(width),高(height),面积(area);

class Rectange {

private int x,y,x1,y1;
public Rectange(int x,int y,int x1,int y1) {
this.x=x;
this.y=y;
this.x1=x1;
this.y1=y1;
}

public void showWidth() {
Syste…
[/Quote]

没必要去绝对值吧。左下角和右上角的坐标,哪个大哪个小,谁减去谁,应该是确定的
IvanStrong 2008-05-19
  • 打赏
  • 举报
回复
int getHeight()//计算高heigth 
{
height=y2=y1;
return height;
}

呵呵,太不相信电脑了
qusic 2008-05-19
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 KK3K2005 的回复:]
int getHeight()//计算高heigth
{
height=y2=y1;//请解释下 这句的用意
return height;
}
[/Quote]
恩,这一句错了.
KK3K2005 2008-05-19
  • 打赏
  • 举报
回复
int getHeight()//计算高heigth
{
height=y2=y1; //请解释下 这句的用意
return height;
}


老紫竹 2008-05-19
  • 打赏
  • 举报
回复
呵呵!太粗心了!
wn10000neng 2008-05-19
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 KK3K2005 的回复:]
int getHeight()//计算高heigth
{
height=y2=y1; //请解释下 这句的用意
return height;
}
[/Quote]

晕,-写成=号,谢谢!
AwL_1124 2008-05-19
  • 打赏
  • 举报
回复
up```
[Quote=引用 7 楼 sp272530 的回复:]
我定义了一个名为Rectange 的矩形类,包括的属性有矩形的左下角和右上角2个点的坐标,要求计算并打印宽(width),高(height),面积(area);

class Rectange {

private int x,y,x1,y1;
public Rectange(int x,int y,int x1,int y1) {
this.x=x;
this.y=y;
this.x1=x1;
this.y1=y1;
}

public void showWidth() {
Syste…
[/Quote]
sp272530 2008-05-19
  • 打赏
  • 举报
回复
我定义了一个名为Rectange 的矩形类,包括的属性有矩形的左下角和右上角2个点的坐标,要求计算并打印宽(width),高(height),面积(area);

class Rectange {

private int x,y,x1,y1;
public Rectange(int x,int y,int x1,int y1) {
this.x=x;
this.y=y;
this.x1=x1;
this.y1=y1;
}

public void showWidth() {
System.out.println("矩形的宽是 : "+Math.abs((x1-x)));
}
public void showHeidht() {
System.out.println("巨型的高是 : "+Math.abs((y1-y)));
}
public void showArea() {
System.out.println("矩形的面积是 : "+Math.abs((x1-x)*(y1-y)));
}
}
加载更多回复(1)
《C语言程序设计》 实验指导书 山东水利职业学院 目 录 实验一 C语言程序初步 2 实验二 数据类型、运算符和表达式 3 实验四 循环结构(1) 6 实验五 循环结构(2) 9 实验六 函数(1) 12 实验七 函数(2) 14 实验八 数组(1) 16 实验九 数组(2) 19 实验十 指 针 (1) 22 实验十一 指针(二) 24 实验十二 有关文件的程序设计 27 实验十三 结构体和共用体 30 实验十四 位运算 32 实验十五 综合应用举例 33 实验一 C语言程序初步 [实验目的和要求] 1、 熟悉C语言运行环境。 2、 掌握C语言程序的书写格式和C语言程序的结构。 3、 掌握C语言上机步骤,了解运行一个C程序的方法。 4、 本实验可在学习完教材第一章后进行。 [实验内容和步骤] 1、输入一个简单的C语言程序:输入矩形的两条边,求矩形面积。 [分析与讨论] 1、记下在调试过程中所发现的错误、系统给出的出错信息和对策。分析讨论对策成功或失败的原因。 2、总结C程序的结构和书写规则。 实验二 数据类型、运算符和表达式 [实验目的] 1.熟练表达式的表示方法 2.了解表达式的运行结果 3.了解C语言中数据类型的意义 [实验内容和步骤] 1. 用printf函数来输入表达式运行结果 2. 程序试图计算由键盘输入的任意两个整数的平均值: [问题讨论] 1.“=”和“==”有什么区别? 2.“&”和“&&”、“|”和“||”有什么区别? 3. 如何正确地选用数据类型? 实验三 选择结构 [实验目的] 1、了解条件与程序流程的关系 2、了解用不同的数据使程序的流程覆盖不同的语句、分支和路径。 [实验内容和步骤] 1、题目 有如下程序段: {…… if (a>1&&b= =0) x=x/a; if(a==2||x>1) x=x+1; } 为了更容易明白程序的逻辑结构,要求用流程图来加以描述。 要求增加一些输入语句和输出语句,以便使上述程序能在不同的a,b和x值下运行,并且能观察程序流程经过(覆盖)了哪些语句、哪些分支及哪些路径。 [分析讨论] (1) 用哪一组数据就可使程序中的每个处理语句都执行一次?为了找出程序中各条处理语句中的错误,应该使用什么样的数据对程序进行测试?请上机验证自己的结论。 (2) 用哪两组数据就可以使程序段中的每个分支都运行一次?这种情形与上面的讨论有何不同?如果为了找出程序中积压分支中的错误,应该使用什么样的数据对程序进行测试?请上机验证自己的结论。 (3)用哪两组数据就可以把判定框中的每个条件运算都进行一次?如果为了测试出判定条件中的错误,应使用哪些数据对程序进行测试?请上机验证自己的结论。 (4)用哪四组数据才可以把各种条件的组合都检测一遍? 如果为了测试各种条件的组合的情形,应该使用什么样的测试数据?请上机验证自己的结论。 (5)用哪四组数据才可以把起始到终止的各条路径都覆盖一次?如果为了测试出程序在不同路径下的错误,应该使用什么样的测试数据?请上机验证自己的结论。 实验四 循环结构(1) [目的和要求] 1、掌握在程序设计条件型循环结构时,如何正确地设定循环条件,以及如何控制循环的次数。 2、了解条件型循环结构的基本测试方法。 [实验内容与步骤] 1、下面是一个计算e的近似值(使误差小于给定的δ)的程序。 main() {double e=1.0,x=1.0,y,detax; int i=1; printf(“\n please input enter a error:”); scanf(“%lf”,&detax); y=1/x; while(y>=detax) { x=x*I; y=1/x; e=e+y; ++i; } printf(“%12.10lf”,e); } [分析讨论] (1)、阅读上面的程序,写出程序所依据的计算公式。 (2)、当输入的detax各是什么值时,能分别使程序按下面的要求运行: .不进入循环; .只循环一次; .只循环两次; .进入死循环(程序将永远循环下去)。 为了能知道程序循环了多少次,应该在程序中增加一条什么样的语句? (3)、原程序中while语句中的y>=detax,分别换成y>detax,y=detax,y程序运行将有什么变化。 假如不知道机器内的程序中的各语句实际上是什么,分别输入什么样的detax来测试出while语句的循环条件写错了。 (4)、把原程序中while语句之前的y=1/x语句去掉,观察程序的运行将发生什么样的变化。 假如不知道机器内的程序实际上是怎么写的,输入什么样的detax就能测试出少了上述这条语句。 (5)、若把原程序中的++i换成i++,观察程序的运行发生了什么变化? 假如不知道这条语句到底是怎么写的,输入什么样的detax就能测试出这条语句写错了. (6)、把原程序中的while结构改写成do—while结构,再分别按上述的(2)、(3)两步进行实验。 实验五 循环结构(2) [ 目的和要求] 1、 掌握如何正确地控制计数型循环结构的次数 2、 了解对计数型循环结构进行测试的基本方法 3、 了解在嵌套循环结构中,提高程序效率的方法 4、 本实验应在学习了教材第3.3.4进行 [实验内容与步骤] 实验题目:下面是一个循环结构 的C程序。 main() { int i ,j; long sum=0; for(i=1,i<=20,i++) for(j=1;j<=3;j++) sum=sum+(i+1)*(i+1)*(j+2) printf(“sum is :%d\n”,sum); } [ 分析讨论] (1) 将上述程序中的和分别做什么样修改,能使程序按如下要求运行: .不循环; .外循环1次,不进入内循环; .外循环,内循环各1次; .外循环1次,内循环2次; .外循环 2次, 内循环1次; .外循环19次,内循环3次; .外循环20次,内循环2次; .外循环21次,内循环3次。 (2) 试修改程序,以便能知道控制表达式 i=1;j<=20;i++ 和 j=1;j<3;j+= 各执行了多少次? (3)内循环与外循环的控制表达式进行对换,观察程序的执行结果有无变化,并且观 察对换后的外循环与内循环控制表达式执行的总次数有无变化? (4)将(3)中程序写为 main() { int i,j,k; long sum=0; for (j=1.j<=3.j++) { k=j+2; for(i=1;i<=20;i++) sum=sum+(i+1)*(i+1)**k; } printf(“sum is :%d\n”,sum); } 观察程序的执行结果有无变化? (5)将(3)中程序改写为 main() { int I,j,m,n; long sum=0; for(j=1;j<=3;j++) { m=j+2; for(I=1;j<=20;I++) { n=I+1; sum=sum+m*m*k; } } printf(“sum is :%d\n”,sum); } 观察程序的执行结果有无变化? (1) 实验六 函数(1) [实验目的] 1.理解和掌握多模块的程序设计与调试的方法。 2.掌握函数的定义和调用的方法。 3.学使用递归方法进行程序设计。 [实验内容和步骤] 1. 编写一个函数,判断一个数是不是素数。在主函数中输入一个整数,输出是否是素数的信息。 实验步骤与要求: (1) 编写一个函数prime(n),返回给定整数n是否为素数。 (2) 编写一个主函数,输入一个整数,调用(1)中的函数,判断此整数是否为素数,并输出结果。 (3) 对于属于多函数程序,可以采用每个函数分别进行编辑、编译的方法,然后再连接、运行。如果编译有错时,可分别修改,这样便于调试。实验2和实验3也可以使用这样的调试方法。 2. 用梯形法求函数的定积分。 实验步骤与要求: (1) 编制一个函数sab(a,b,n),其功能是求函数f(x)在[a,b]上的定积分,其中n为区间[a,b]的等分数。要求该函数在一个独立的文件中。 (2) 编制一个主函数以及计算被积函数值的函数f(x),在主函数中调用(1)中的函数计算并输出下列积分值。要求主函数与函数f(x)在同一个文件中。 (3) 编制另一个主函数以及计算被积函数值的函数f(x),在主函数中调用(1)中的函数计算并输出下列积分值。要求主函数与函数f(x)在同一个文件中。 说明: 用梯形法求定积分,梯形公式为 s=h[f(a)+f(b)]/2+hf(a+kh)其中,h=(b-a)/n 3. 用递归方法编写程序,求n阶勒让德多项式的值,递归公式为 1 (n=0) pn(x)=x (n=1) ((2n-1)xpn-1(x)-(n-1)pn-2(x))/n (n>1) , [ 分析讨论] 1. 小结函数的定义及调用方法。 2. 小结函数中形参和实参的结合规则。 实验七 函数(2) [目的与要求] 1、 掌握C语言函数定义及调用的规则。 2、 理解参数传递的过程。 [ 实验内容与步骤] 1、 上机调试下面的程序,记录系统给出的出错信息,并指出出错原因。 main() { int,y; printf(“%d\n”,sum(x+y)); int sum(a,b) { int a,b; return(a+b); } } 2、 编写并调试一个求(n为整数)的递归函数,希望能在程序运行过程中动态地显示递归函数被调用的轨迹。 [分析讨论] 1、 针对以上实验内容写出相应的参数传递过程并分析结果。 2、 讨论参数的传递的几种形式。 实验八 数组(1) [实验目的] 掌握有关数组和字符串的程序设计方法。 [实验内容和步骤] 1 已知一组数据如下: 6,3,42,23,35,71,98,67,56,38 编写程序,把它们按从小到大的次序排列起来。 程序如下: /*EX3-1*/ # include # define N 10 main() { int a[N]={6, 3, 42, 23, 35, 71, 98, 67, 56, 38}; int i, j, t; printf(“The array before sorted:”); for(i=0; ia[j]) { t=a[i]; a[i]=a[j]; a[j]=t; } } } for(i=0; i程序,测试下列数组的定义方式是否正确 (1) main() { int n; scanf(“%d”,&n); int a[n]; …… } (2) main() { const int n=10; int a[n]; } (3) #include #define M 10 main() { int a[M]; } (4) main() { int a[2+2*4]; } (5) #include #define M 2 #define N 8 main() { int a[M+N]; } 通过这一实验,可以说明什么问题? 2、运行下面的C程序,根据运行结果,可以说明什么? main( ) { int num[5]={1,2,3,4,5}; inti i; for(i=0;i<=5;i++) printf(“%d”,num[i]); } 3、操作符&用以求一个变量的地址,这在函数scanf中已经使用过了。现在要你设计一个程序,返回一个3 * 5 的二维数组各元素的地址,并由此说明二维数组中各元素是按什么顺序存诸的。 4、为一个起泡排序程序设计测试用例,并测试之。 [分析讨论] 通过实验,分析定义与引用数组的区别。 实验十 指 针 (1) [目的和要求] 1. 熟悉指针的正确用法。 [实验内容和步骤] 1、指出下面程序错误的原因。 main( ) { int x=10,y=5,*px,*py; px=py; px=&x; py=&y; printf(“*px=%d,*py=%d,*px,*py); } 2.下面的程序能获得上述运行结果吗? main() { char *S=”COMPUTER”; char c; printf(“which style you want to \n”); printf(“capital (c) or uncapital(u);”); c=getchar(); if(c=’c’) put(s); else { s=”computer”; puts(s); } } 分析出错原因。 3.设计一个C程序,用以测试下列各表达式的运算。 (1) y=*px++ (2) y=*++py (3) y=(*py)++ (4) y=--*ppx++ [分析讨论] 数组与指针的联系。 实验十一 指针(二) [目的和要求] 1、了解指针参数的特殊性。 2、掌握函数、指针、数组的用法。 [实验内容与步骤] 1、想使指针变量pt1指向a 和b 中的大者,pt2指向小者,以下程序能否实现此目的? swap(int *p1,int *p2) { int *p; p=p1;p1=p2;p2=p; } main() { int a,b; scanf(“%d,%d”,&a,&b); pt1=&a;pt2=&b; if(a程序。如果不能实现题目要求,指出原因,并修改之。 2、 下面的程序。注意其中的复杂声明语句; int (*function[4])(); 和各标记符的含义,并记录执行结果。 main() { int fun1(); int fun2(); int fun3(); int fun4(); int (*function[4])(); int a=10,b=5,i; function[0]=fun1; function[1]=fun2; function[2]=fun3; function[3]=fun4; for(I=0;I<4;i++) printf(“fun no.%d->%d\n”,i+1,execute(a,b,function[i])); } execute(int x,int y,int *fun()) { return(*fun)(x,y);} fun1(int x, int y) { return(x+y);} fun2(int x,int y) { return(x-y);} fun3(int x,int y) { return(x*y);} fun4(int x,int y) { return(x/y);} [分析讨论] 指针、数组、函数的联系。 实验十二 有关文件的程序设计 [实验目的] 1. 掌握文件建立的方法。 2. 掌握包含文件操作的程序设计和调试方法。 [实验内容和步骤 ] 1 建立一个磁盘文件,其内容是0~90°之间每隔5°的正弦值。 程序如下: //EX8-1 #include #include #include #define PI 3.14159 main() { float S[19]; int i, a; ofstream out(“fsin.bny”); if(!out) { cout<<”Cannot open file.”<”的应用。 4、共用体的概念和应用。 [实验内容和步骤] 1、编写程序:有4名学生,每个学生的数据包括学号、姓名、成绩,要求找出成绩最高者的姓名和成绩,上机运行程序。 2、建立一个链表每个结点包括的成员为:职工号、工资。用malloc打印出来。用一个creat 来建立链表,用list来输出数据。5个职工号为101,103,105,107,109。 3、在上题基础上,新增加一个职工的数据,按职工号的顺序插入链表,新插入的职工号为106。写一函数insert来插入新结点。 4、在上面的基础上,写一函数delete,用来删除一个结点。要求删除职工号为103的结点。打印出删除后的链表. 5、你有无办法测试构造链表时,内存空间的动态分配情形? [分析讨论] 怎样引用结构体变量成员? 实验十四 位运算 [目的和要求] 1. 掌握位运算的概念和方法。 2. 掌握位运算(&,|,^,~)的使用方法。 3. 掌握有关位运算的用法。 [实验内容和步骤] 1. 按习题8.2的要求,编一个程序,将一个整数的高字节和低字节分别输出(用位运算方法)。上机运行。 2. 按习题8.6的要求,编一个程序,使一个整数的低4位翻转。上机运行,用十六进制数输入和输出。 3. 按习题8.10的要求,将一个整数i的各位循环左移4位,设i的值为十六进制数fe13。 4. 按习题8.13的要求,设计一个函数,当给定一个整数后,能得到该数的补码(应考虑该整数是正数或负数)。 [分析讨论] 位运算时应注意什么问题? 实验十五 综合应用举例 [目的与要求] 1、熟悉C语言的端口及硬件控制的方法与原理。 2、了解用C语言实现发声程序所要访问的端口。 [实验内容和步骤] 1、调试课本中例10.8程序,注意调出不同的效果。 2、调试并总结程序中所用的知识点,写出乐谱文件播放的是什么乐谱? 乐谱实例: 6000 h5 1 h3 2 h2 0.5 h3 0.5 h1 4 h2 1 m7 2 m6 0.5 h1 0.5 m5 4 m3 2 m5 1.5 m6 0.5 h1 1.5 h2 0.5 m6 0.5 h1 0.5 m5 1 h5 1.5 h1 0.5 h6 0.5 h5 0.5 h3 0.5 h5 0.5 h2 4 h2 1.5 h3 0.5 m7 1 m6 1 m5 1.5 m6 0.5 h1 1 h2 1 m3 1 h1 1 m6 0 m5 0.5 m6 0.5 h1 0.5 m5 4 h3 1.5 h5 0.5 m7 1 h2 1 m6 0.5 h1 0.5 m5 3 m3 0. m5 0.5 m3 0.5 m5 0.5 m5 0.5 m6 0.5 m7 0.5 h2 0.5 m6 3 m5 0.5 m6 0.5 h1 1.5 h2 0.5 h5 1 h3 1 h2 1 h3 0.5 h2 0.5 h1 1 m6 0.5 m5 0.5 m3 2 h1 2 m6 0.5 h1 0.5 m6 0.5 m5 0.5 m3 0.5 m5 0.5 m6 0.5 h1 0.5 m5 3 h3 0.5 h5 0.5 h2 0.5 h3 0.5 h2 0.5 h1 0.5 m7 1 m6 1 h5 4## void play_music(char *filename) /*filename为音乐文件名格式见上*/ { FILE *fp; int rate; char sound_high[3]; float sound_long; register int i=0,j; int sign=0; float str[1000][2]; if ((fp=fopen(filename,"r"))==NULL) { printf("Open file music.doc Errors!\n"); exit(1); } fscanf(fp,"%d",&rate); while(!feof(fp)&&!sign) { fscanf(fp,"%s%f",&sound_high,&sound_long); str[i][1]=rate*sound_long; switch(sound_high[0]) { case 'h': switch(sound_high[1]) { case '1': str[i++][0]=1046.5; break; case '2': str[i++][0]=1174.7; break; case '3': str[i++][0]=1318.5; break; case '4': str[i++][0]=1396.9; break; case '5': str[i++][0]=1568; break; case '6': str[i++][0]=1760; break; case '7': str[i++][0]=1975.5; break; default: printf("\n Error Music.doc\n"); break; } break; case 'm': switch(sound_high[1]) { case '1': str[i++][0]=523.3; break; case '2': str[i++][0]=587.3; break; case '3': str[i++][0]=659.3; break; case '4': str[i++][0]=698.5; break; case '5': str[i++][0]=784.0; break; case '6': str[i++][0]=880; break; case '7': str[i++][0]=987.8; break; default: printf("\n Error music.doc.\n"); break; } break; case 'l': switch(sound_high[1]) { case '1': str[i++][0]=262; break; case '2': str[i++][0]=296; break; case '3': str[i++][0]=329.6; break; case '4': str[i++][0]=349.2; break; case '5': str[i++][0]=392; break; case '6': str[i++][0]=440; break; case '7': str[i++][0]=493.9; break; default: printf("\n Error music.doc.\n"); break; } break; case '*': switch(sound_high[1]) { case '1': str[i++][0]=131; break; case '2': str[i++][0]=147; break; case '3': str[i++][0]=165; break; case '4': str[i++][0]=176; break; case '5': str[i++][0]=196; break; case '6': str[i++][0]=220; break; case '7': str[i++][0]=247; break; default: printf("\n Error music.doc\n"); break; } break; case '#': if (sound_high[1]=='#') sign=1; break; case 'x': if (sound_high[1]=='0') str[i++][0]=20; break; default:printf("\n ERRor music.doc\n"); exit(1); } } for(j=0;j<=i-1;j++) { sound(str[j][0]); delay(str[j][1]); } nosound(); } [分析讨论] 1.c语言怎样对数据库进行管理? 2.分析程序设计过程中怎样对基础知识进行灵活运用。
1.面向对象的概念 面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程 架构,OOP 的一条基本原则是计算程序是由单个能够起到子程序作用的单元或对象组合而成,OOP 达到了软件工程的三个目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收 信息、处理数据和向其它对象发送信息。面向对象一直是软件开发领域内比较热门的话题,首先, 面向对象符合人类看待事物的一般规律。其次,采用面向对象方法可以使系统各部分各司其职、各 尽所能。为编程人员敞开了一扇大门,使其编程的代码更简洁、更易于维护,并且具有更强的可重 用性。有人说PHP 不是一个真正的面向对象的语言,这是事实。PHP 是一个混合型语言,你可以使 用OOP,也可以使用传统的过程化编程。然而,对于大型项目,你可能需要在PHP 中使用纯的OOP 去声明类,而且在你的项目里只用对象和类。这个概念我先不多说了,因为有很多朋友远离面向对 象编程的主要原因就是一接触面向对象概念的时候就理解不上去, 所以就不想去学下去了。等读 者看完整篇内容后再去把概念搞明白吧。 2.什么是类,什么是对象,类和对象之间的关系 类的概念:类是具有相同属性和服务的一组对象的集合。它为属于该类的所有对象提供了统一 的抽象描述,其内部包括属性和服务两个主要部分。在面向对象的编程语言中,类是一个独立的程 序单位,它应该有一个类名并包括属性说明和服务说明两个主要部分。 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》66/104 对象的概念:对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一 个对象由一组属性和对这组属性进行操作的一组服务组成。从更抽象的角度来说,对象是问题域或 实现域中某些事物的一个抽象,它反映该事物在系统中需要保存的信息和发挥的作用;它是一组属 性和有权对这些属性进行操作的一组服务的封装体。客观世界是由对象和对象之间的联系组成的。 类与对象的关系就如模具和铸件的关系,类的实例化结果就是对象,而对一类对象的抽象就是 类。类描述了一组有相同特性(属性)和相同行为(方法)的对象。 上面大概就是它们的定义吧,也许你是刚接触面向对象的朋友, 不要被概念的东西搞晕了,给 你举个例子吧,如果你去中关村想买几台组装的PC 机,到了那里你第一步要干什么,是不是装机 的工程师和你坐在一起,按你提供的信息和你一起完成一个装机的配置单呀,这个配置单就可以想 象成是类,它就是一张纸,但是它上面记录了你要买的PC 机的信息,如果用这个配置单买10 台机 器,那么这10 台机子,都是按这个配置单组成的,所以说这10 台机子是一个类型的,也可以说是 一类的。那么什么是对象呢,类的实例化结果就是对象,用这个配置单配置出来(实例化出来)的 机子就是对象,是我们可以操作的实体,10 台机子,10 个对象。每台机子都是独立的,只能说明 他们是同一类的,对其中一个机做任何动作都不影响其它9 台机器,但是我对类修改,也就是在 这个配置单上加一个或少一个配件,那么装出来的9 个机子都改变了,这是类和对象的关系(类的 实例化结果就是对象)。 3.什么是面向对象编程呢? 就不说他的概念,如果你想建立一个电脑教室,首先要有一个房间, 房间里面要有N 台电脑, 有N 张桌子, N 把椅子, 白板, 投影机等等,这些是什么,刚才咱们说了,这就是对象,能看 到的一个个的实体,可以说这个电脑教室的单位就是这一个个的实体对象, 它们共同组成了这个 电脑教室,那么我们是做程序,这和面向对象有什么关系呢?开发一个系统程序和建一个电脑教室 类似,你把每个独立的功能模块抽象成类,形成对象,由多个对象组成这个系统,这些对象之间都 能够接收信息、处理数据和向其它对象发送信息等等相互作用。就构成了面向对象的程序。 4.如何抽象出一个类? 上面已经介绍过了,面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,所以 我们首先要做的就是如何来声明类,做出来一个类很容易,只要掌握基本的程序语法定义规则就可 以做的出来,那么难点在那里呢?一个项目要用到多少个类,用多少个对象,在那要定义类,定义 一个什么样的类,这个类实例化出多少个对象,类里面有多少个属性,有多少个方法等等,这就需 要读者通过在实际的开发中就实际问题分析设计和总结了。 类的定义: class 类名{ } 使用一个关键字class 和后面加上一个你想要的类名以及加上一对大括号, 这样一个类的结构 就定义出来了,只要在里面写代码就可以了,但是里面写什么?能写什么?怎样写才是一个完整的 类呢?上面讲过来,使用类是为了让它实例出对象来给我们用,这就要知道你想要的是什么样的对 象了,像上面我们讲的一个装机配置单上写什么,你装出来的机子就有什么。比如说,一个人就是 一个对象,你怎么把一个你看好的人推荐给你们领导呢?当然是越详细越好了: 首先,你介绍这个人姓名、性别、年龄、身高、体重、电话、家庭住址等等。 然后,你要介绍这个人能做什么,可以开车,说英语,可以使用电脑等等。 只要你介绍多一点,别人对这个人就多一点了解,这就是我们对一个人的描述, 现在我们总结 一下,所有的对象我们用类去描述都是类似的,从上面人的描述可以看到, 做出一个类来,从定 义的角度分两部分,第一是从静态上描述,第二是从动态上描述, 静态上的描述就是我们所说的 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》67/104 属性,像上面我们看到的,人的姓名、性别、年龄、身高、体重、电话、家庭住址等等。动态上也 就是人的这个对象的功能,比如这个人可以开车,说英语,可以使用电脑等等,抽象成程序时, 我们把动态的写成函数或者说是方法,函数和方法是一样的。所以,所有类都是从属性和方法这两 方面去写, 属性又叫做这个类的成员属性,方法叫做这个类的成员方法。 class 人{ 成员属性:姓名、性别、年龄、身高、体重、电话、家庭住址 成员方法:可以开车, 说英语, 可以使用电脑 } 属性: 通过在类定义中使用关键字" var "来声明变量,即创建了类的属性,虽然在声明成员属性 的时候可以给定初始值, 但是在声明类的时候给成员属性初始值是没有必要的,比如说要 是把人的姓名赋上“张三”,那么用这个类实例出几十个人,这几十个人都叫张三了,所以 没有必要, 我们在实例出对象后给成员属性初始值就可以了。 如: var $somevar; 方法(成员函数): 通过在类定义中声明函数,即创建了类的方法。 如: function somefun(参数列表) { ... ... } 上面就是一个类的声明,从属性和方法上声明出来的一个类,但是成员属性最好在声明的时候 不要给初始的值,因为我们做的人这个类是一个描述信息,将来用它实例化对象,比如实例化出来 10 个人对象,那么这10 个人, 每一个人的名字、性别、年龄都是不一样的,所以最好不要在这个 地方给成员属性赋初值,而是对每个对象分别赋值的。 用同样的办法可以做出你想要的类了,只要你能用属性和方法能描述出来的实体都可以定义成 类,去实例化对象。 为了加强你对类的理解,我们再做一个类,做一个形状的类,形状的范围广了点, 我们就做个 矩形吧,先分析一下,想一想从两方面分析,矩形的属性都有什么?矩形的功能都有什么? class 矩形 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》68/104 { //矩形的属性 矩形的长; 矩形的宽; //矩形的方法 矩形的周长; 矩形面积; } 计算矩形的周长; }f unction mianJi() { 计算矩形面积; } } ?> 如果用这个类来创建出多个矩形对象,每个矩形对象都有自己的长和宽, 都可以求出自己的周 长和面积了。 类的声明我们就到这里吧!! 5.如何实例化对象 我们上面说过面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,既然我们类 声明了,下一步就是实例化对象了。 当定义好类后,我们使用new 关键字来生成一个对象。 $对象名称= new 类名称(); $p1=new Person(); 这条代码就是通过类产生实例对象的过程,$p1 就是我们实例出来的对象名称,同理,$p2, $p3 也是我们实例出来的对象名称,一个类可以实例出多个对象,每个对象都是独立的,上面的代码相 当于实例出来3 个人来,每个人之间是没有联系的,只能说明他们都是人类,每个人都有自己的姓 名,性别和年龄的属性,每个人都有说话和走路的方法,只要是类里面体现出来的成员属性和成员 方法,实例化出来的对象里面就包含了这些属性和方法。 对像在PHP 里面和整型、浮点型一样,也是一种数据类,都是存储不同类型数据用的,在运行 的时候都要加载到内存中去用, 那么对象在内存里面是怎么体现的呢?内存从逻辑上说大体上是 分为4 段,栈空间段、堆空间段、代码段、初始化静态段,程序里面不同的声明放在不同的内存段 里面,栈空间段是存储占用相同空间长度并且占用空间小的数据类型的地方,比如说整型1,10, 100,1000,10000,100000 等等,在内存里面占用空间是等长的,都是64 位4 个字节。那么数据 长度不定长,而且占有空间很大的数据类型的数据放在那内存的那个段里面呢?这样的数据是放在 堆内存里面的。栈内存是可以直接存取的,而堆内存是不可以直接存取的内存。对于我们的对象来 数就是一种大的数据类型而且是占用空间不定长的类型,所以说对象是放在堆里面的,但对象名称 是放在栈里面的,这样通过对象名称就可以使用对象了。 $p1=new Person(); 对于这个条代码, $p1 是对象名称在栈内存里面,new Person()是真正的对象是在堆内存里面 的,具体的请看下图: 从上图可以看出$p1=new Person();等号右边是真正的对象实例,在堆内存里面的实体,上图一 共有3 次new Person(),所以在堆里面开辟3 个空间,产生3 个实例对象,每个对象之间都是相 互独立的,使用自己的空间,在PHP 里面,只要有一个new 这个关键字出现就实例化出来一个对 象,在堆里面开辟一块自己的空间。 每个在堆里面的实例对象是存储属性的,比如说,现在堆里面的实例对象里面都存有姓名、性 别和年龄。每个属性又都有一个地址。 $p1=new Person();等号的右边$p1 是一个引用变量,通过赋值运算符“=”把对象的首地址赋 给“$p1”这个引用变量,所以$p1 是存储对象首地址的变量,$p1 放在栈内存里边,$p1 相当于一 个指针指向堆里面的对象,所以我们可以通过$p1 这个引用变量来操作对象,通常我们也称对象引用 为对象。 6.如何去使用对象中的成员 上面看到PHP 对象中的成员有两种一种是成员属性,一种是成员方法。对象我们以经可以声明 了,$p1=new Person();怎么去使用对象的成员呢?要想访问对象中的成员就要使用一个特殊的操 作符“->”来完成对象成员的访问: LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》70/104 对象->属性$p1->name; $p2->age; $p3->sex; 对象->方法$p1->say(); $p2->run(); 如下面实例: name=”张三”; $p1->sex=”男”; $p1->age=20; //下面三行是访问$p1对象的属性 echo “p1对象的名字是:”.$p1->name.”
”; echo “p1对象的性别是:”.$p1->sex.”
”; echo “p1对象的年龄是:”.$p1->age.”
”; //下面两行访问$p1对象中的方法 $p1->say(); $p1->run(); //下面三行是给$p2对象属性赋值 $p2->name=”李四”; $p2->sex=”女”; $p2->age=30; //下面三行是访问$p2对象的属性 echo “p2对象的名字是:”.$p2->name.”
”; echo “p2对象的性别是:”.$p2->sex.”
”; echo “p2对象的年龄是:”.$p2->age.”
”; //下面两行访问$p2对象中的方法 $p2->say(); $p2->run(); //下面三行是给$p3对象属性赋值 $p3->name=”王五”; $p3->sex=”男”; $p3->age=40; //下面三行是访问$p3对象的属性 echo “p3对象的名字是:”.$p3->name.”
”; echo “p3对象的性别是:”.$p3->sex.”
”; LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》71/104 echo “p3对象的年龄是:”.$p3->age.”
”; //下面两行访问$p3对象中的方法 $p3->say(); $p3->run(); ?> 从上例中可以看出只是对象里面的成员就要使用对象->属性、对象->方法形式访问,再没有第 二种方法来访问对象中的成员了。 7.特殊的引用“$this”的使用 现在我们知道了如何访问对象中的成员,是通过“对象->成员”的方式访问的,这是在对象的外 部去访问对象中成员的形式,那么如果我想在对象的内部,让对象里的方法访问本对象的属性,或 是对象中的方法去调用本对象的其它方法这时我们怎么办?因为对象里面的所有的成员都要用对 象来调用,包括对象的内部成员之间的调用,所以在PHP 里面给我提供了一个本对象的引用$this, 每个对象里面都有一个对象的引用$this 来代表这个对象,完成对象内部成员的调用, this 的本意就是 “这个”的意思,上面的实例里面,我们实例化三个实例对象$P1、$P2、$P3,这三个对象里面各 自存在一个$this 分别代表对象$p1、$p2、$p3 。 通过上图我们可以看到,$this 就是对象内部代表这个对象的引用,在对象内部和调用本对象的 成员和对象外部调用对象的成员所使用的方式是一样的。 $this->属性$this->name; $this->age; $this->sex; $this->方法$this->say(); $this->run(); 修改一下上面的实例,让每个人都说出自己的名字,性别和年龄: name." 性别:".$this->sex." 我的年龄是: ".$this->age."
"; }f unction run() //这个人可以走路的方法 { echo "这个人在走路"; } } $p1=new Person(); //创建实例对象$p1 $p2=new Person(); //创建实例对象$p2 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》72/104 $p3=new Person(); //创建实例对象$p3 //下面三行是给$p1对象属性赋值 $p1->name="张三"; $p1->sex="男"; $p1->age=20; //下面访问$p1对象中的说话方法 $p1->say(); //下面三行是给$p2对象属性赋值 $p2->name="李四"; $p2->sex="女"; $p2->age=30; //下面访问$p2对象中的说话方法 $p2->say(); //下面三行是给$p3对象属性赋值 $p3->name="王五"; $p3->sex="男"; $p3->age=40; //下面访问$p3对象中的说话方法 $p3->say(); ?> 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 分析一下这个方法: function say() //这个人可以说话的方法 { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."
"; } 在$p1、$p2 和$p3 这三个对象中都有say()这个方法,$this 分别代表这三个对象, 调用相应 的属性,打印出属性的值,这就是在对象内部访问对象属性的方式, 如果相在say()这个方法里调 用run()这个方法也是可以的,在say()这个方法中使用$this->run()的方式来完成调用。 8.构造方法与析构方法 大多数类都有一种称为构造函数的特殊方法。当创建一个对象时,它将自动调用构造函数,也 就是使用new 这个关键字来实例化对象的时候自动调用构造方法。 构造函数的声明与其它操作的声明一样,只是其名称必须是__construct( )。这是PHP5 中的变化, 以前的版本中,构造函数的名称必须与类名相同,这种在PHP5 中仍然可以用,但现在以经很少有 人用了,这样做的好处是可以使构造函数独立于类名,当类名发生改变时不需要改相应的构造函数 名称了。为了向下兼容,如果一个类中没有名为__construct( )的方法,PHP 将搜索一个php4 中的写 法,与类名相同名的构造方法。 格式:function __construct ( [参数] ) { ... ... } 在一个类中只能声明一个构造方法,而是只有在每次创建对象的时候都去调用一次构造方法, 不能主动的调用这个方法,所以通常用它执行一些有用的初始化任务。比如对成属性在创建对象的 时候赋初值。 一个人类 class Person { LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》73/104 //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //定义一个构造方法参数为姓名$name、性别$sex和年龄$age function __construct($name, $sex, $age) { //通过构造方法传进来的$name给成员属性$this->name赋初使值 $this->name=$name; //通过构造方法传进来的$sex给成员属性$this->sex赋初使值 $this->sex=$sex; //通过构造方法传进来的$age给成员属性$this->age赋初使值 $this->age=$age; }/ /这个人的说话方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."
"; } } //通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄 $p1=new Person(“张三”,”男”, 20); $p2=new Person(“李四”,”女”, 30); $p3=new Person(“王五”,”男”, 40); //下面访问$p1对象中的说话方法 $p1->say(); //下面访问$p2对象中的说话方法 $p2->say(); //下面访问$p3对象中的说话方法 $p3->say(); ?> 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 如图: LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》74/104 析构函数: 与构造函数相对的就是析构函数。析构函数是PHP5 新添加的内容,在PHP4 中没有析构函数。 析构函数允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件,释放结果集等, 析构函数在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,也就是对象在内存中 被销毁前调用析构函数。与构造函数的名称类似,一个类的析构函数名称必须是__destruct( )。析构 函数不能带有任何参数。 格式:function __destruct ( ) { ... ... } 一个人类 class Person { //下面是人的成员属性 var $name; //人的名字 var $sex; //人的性别 var $age; //人的年龄 //定义一个构造方法参数为姓名$name、性别$sex和年龄$age function __construct($name, $sex, $age) { //通过构造方法传进来的$name给成员属性$this->name赋初使值 $this->name=$name; //通过构造方法传进来的$sex给成员属性$this->sex赋初使值 $this->sex=$sex; //通过构造方法传进来的$age给成员属性$this->age赋初使值 $this->age=$age; } //这个人的说话方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."
"; } //这是一个析构函数,在对象销毁前调用 function __destruct() { echo “再见”.$this->name.”
”; } //通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄 $p1=new Person(“张三”,”男”, 20); $p2=new Person(“李四”,”女”, 30); $p3=new Person(“王五”,”男”, 40); //下面访问$p1对象中的说话方法 $p1->say(); //下面访问$p2对象中的说话方法 $p2->say(); //下面访问$p3对象中的说话方法 $p3->say(); ?> 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》75/104 再见张三 再见李四 再见王五 9.封装性 封装性是面向对象编程中的三大特性之一,封装性就是把对象的属性和服务结合成一个独立的 相同单位,并尽可能隐蔽对象的内部细节,包含两个含义:1.把对象的全部属性和全部服务结合在一 起,形成一个不可分割的独立单位(即对象)。2.信息隐蔽,即尽可能隐蔽对象的内部细节,对外形 成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系。 封装的原则在软件上的反映是:要求使对象以外的部分不能随意存取对象的内部数据(属性), 从而有效的避免了外部错误对它的"交叉感染",使软件错误能够局部化,大大减少查错和排错的难 度。 用个实例来说明吧,假如某个人的对象中有年龄和工资等属性,像这样个人隐私的属性是不想 让其它人随意就能获得到的,如果你不使用封装,那么别人想知道就能得到,但是如果你封装上之 后别人就没有办法获得封装的属性,除非你自己把它说出去,否则别人没有办法得到。 再比如说,个人电脑都有一个密码,不想让其它人随意的登陆,在你的电脑里面拷贝和粘贴。 还有就是像人这个对象,身高和年龄的属性,只能是自己来增长,不可以让别人随意的赋值等等。 使用private 这个关键字来对属性和方法进行封装: 原来的成员: var $name; //声明人的姓名 var $sex; //声明人的性别 var $age; //声明人的年龄 function run(){… … .} 改成封装的形式: private $name; //把人的姓名使用private 关键字进行封装 private $sex; //把人的性别使用private 关键字进行封装 private $age; //把人的年龄使用private 关键字进行封装 private function run(){… … } //把人的走路方法使用private 关键字进行封装 注意:只要是成员属性前面有其它的关键字就要去掉原有的关键字“var”。 通过private 就可以把人的成员(成员属性和成员方法)封装上了。封装上的成员就不能被类外 面直接访问了,只有对象内部自己可以访问;下面的代码产生错误: class Person { //下面是人的成员属性 private $name; //人的名字,被private封装上了 private $sex; //人的性别, 被private封装上了 private $age; //人的年龄, 被private封装上了 //这个人可以说话的方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."
"; }/ /这个人可以走路的方法, 被private封装上了 private function run() { LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》76/104 echo "这个人在走路"; } } //实例化一个人的实例对象 $p1=new Person(); //试图去给私有的属性赋值, 结果发生错误 $p1->name="张三"; $p1->sex="男"; $p1->age=20; //试图去打印私有的属性, 结果发生错误 echo $p1->name.”
”; echo $p1->sex.”
”; echo $p1->age.”
” //试图去打印私有的成员方法, 结果发生错误 $p1->run(); 输出结果为: Fatal error: Cannot access private property Person::$name Fatal error: Cannot access private property Person::$sex Fatal error: Cannot access private property Person::$age Fatal error: Cannot access private property Person::$name Fatal error: Call to private method Person::run() from context '' 从上面的实例可以看到,私有的成员是不能被外部访问的,因为私有成员只能在本对象内部自 己访问,比如,$p1 这个对象自己想把他的私有属性说出去,在say()这个方法里面访问了私有属性, 这样是可以。(没有加任何访问控制,默认的是public 的,任何地方都可以访问) //这个人可以说话的方法, 说出自己的私有属性,在这里也可以访问私有方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."
"; //在这里也可以访问私有方法 //$this->run(); } 因为成员方法say()是公有的, 所以我们在类的外部调用say()方法是可以的,改变上面的代码; class Person { //下面是人的成员属性 private $name; //人的名字,被private封装上了 private $sex; //人的性别, 被private封装上了 private $age; //人的年龄, 被private封装上了 //定义一个构造方法参数为私有的属性姓名$name、性别$sex和年龄$age进行赋值 function __construct($name, $sex, $age) { //通过构造方法传进来的$name给私有成员属性$this->name赋初使值 $this->name=$name; //通过构造方法传进来的$sex给私有成员属性$this->sex赋初使值 $this->sex=$sex; //通过构造方法传进来的$age给私有成员属性$this->age赋初使值 $this->age=$age; }/ /这个人可以说话的方法, 说出自己的私有属性,在这里也可以访问私有方法 function say() { echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是: ".$this->age."
"; LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》77/104 } } //通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄 $p1=new Person(“张三”,”男”, 20); $p2=new Person(“李四”,”女”, 30); $p3=new Person(“王五”,”男”, 40); //下面访问$p1对象中的说话方法 $p1->say(); //下面访问$p2对象中的说话方法 $p2->say(); //下面访问$p3对象中的说话方法 $p3->say(); 输出结果为: 我的名字叫:张三性别:男我的年龄是:20 我的名字叫:李四性别:女我的年龄是:30 我的名字叫:王五性别:男我的年龄是:40 因为构造方法是默认的公有方法(构造方法不要设置成私有的),所以在类的外面可以访问到, 这样就可以使用构造方法创建对象, 另外构造方法也是类里面的函数,所以可以用构造方法给私 有的属性赋初值。Say()的方法是默认公有的, 所以在外面也可以访问的到, 说出他自己的私有属 性。 从上面的例子中我们可以看到,私有的成员只能在类的内部使用,不能被类外部直接来存取, 但是在类的内部是有权限访问的,所以有时候我们需要在类的外面给私有属性赋值和读取出来,也 就是给类的外部提供一些可以存取的接口,上例中构造方法就是一种赋值的形式,但是构造方法只 是在创建对象的时候赋值,如果我们已经有一个存在的对象了,想对这个存在的对象赋值,这个时 候,如果你还使用构造方法传值的形式传值,那么就创建了一个新的对象,并不是这个已存在的对 象了。所以我们要对私有的属性做一些可以被外部存取的接口,目的就是可以在对象存在的情况下, 改变和存取属性的值,但要注意,只有需要让外部改变的属性才这样做,不想让外面访问的属性是 不做这样的接口的,这样就能达到封装的目的,所有的功能都是对象自己来完成,给外面提供尽量 少的操作。 如果给类外部提供接口,可以为私有属性在类外部提供设置方法和获取方法,来操作私有属性. 例如: prvate $age; //私有的属性年龄 function setAge($age) //为外部提供一个公有设置年龄的方法 { if($age<0 || $age>130) //在给属性赋值的时候,为了避免非法值设置给属性 return; $this->age=$age; }f unction getAge() //为外部提供一个公有获取年龄的方法 { return($this->age); } 上面的方法是为一个成员属性设置和获取值, 当然你也可以为每个属性用同样的方法对其进行 赋值和取值的操作,完成在类外部的存取工作。 10.__set() __get() __isset() __unset()四个方法的应用 一般来说,总是把类的属性定义为private,这更符合现实的逻辑。但是,对属性的读取和赋值 操作是非常频繁的,因此在PHP5 中,预定义了两个函数“__get()”和“__set()”来获取和赋值其 属性,以及检查属性的“__isset()”和删除属性的方法“__unset()”。 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》78/104 上一节中,我们为每个属性做了设置和获取的方法,在PHP5 中给我们提供了专门为属性设置 值和获取值的方法,“__set()”和“__get()”这两个方法,这两个方法不是默认存在的,而是我们手 工添加到类里面去的,像构造方法(__construct())一样, 类里面添加了才存在,可以按下面的方式 来添加这两个方法,当然也可以按个人的风格来添加: //__get()方法用来获取私有属性 private function __get($property_name) { if(isset($this->$property_name)) { return($this->$property_name); }else { return(NULL); } }/ /__set()方法用来设置私有属性 private function __set($property_name, $value) { $this->$property_name = $value; } __get()方法:这个方法用来获取私有成员属性值的,有一个参数,参数传入你要获取的成员属性 的名称,返回获取的属性值,这个方法不用我们手工的去调用,因为我们也可以把这个方法做成私 有的方法,是在直接获取私有属性的时候对象自动调用的。因为私有属性已经被封装上了,是不能 直接获取值的(比如:“echo $p1->name”这样直接获取是错误的),但是如果你在类里面加上了这 个方法,在使用“echo $p1->name”这样的语句直接获取值的时候就自动调用__get($property_name) 方法,将属性name 传给参数$property_name,通过这个方法的内部执行,返回我们传入的私有属性 的值。如果成员属性不封装成私有的,对象本身就不去自动调用这个方法。 __set()方法:这个方法用来为私有成员属性设置值的,有两个参数,第一个参数为你要为设置 值的属性名,第二个参数是要给属性设置的值,没有返回值。这个方法同样不用我们手工去调用, 它也可以做成私有的,是在直接设置私有属性值的时候自动调用的,同样属性私有的已经被封装上 了, 如果没有__set()这个方法,是不允许的, 比如:$this->name=‘zhangsan’, 这样出错,但 是如果你在类里面加上了__set($property_name, $value)这个方法,在直接给私有属性赋值的时候, 就自动调用它,把属性比如name 传给$property_name, 把要赋的值“zhangsan”传给$value,通过 这个方法的执行,达到赋值的目的。如果成员属性不封装成私有的,对象本身就不去自动调用这 个方法。为了不传入非法的值, 还可以在这个方法给做一下判断。代码如下: "; if(isset($this->$property_name)) { return($this->$property_name); }e lse { LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》79/104 return(NULL); } }/ /__set()方法用来设置私有属性 private function __set($property_name, $value) { echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值
"; $this->$property_name = $value; } } $p1=new Person(); //直接为私有属性赋值的操作, 自动调用__set()方法进行赋值 $p1->name="张三"; $p1->sex="男"; $p1->age=20; //直接获取私有属性的值, 自动调用__get()方法,返回成员属性的值 echo "姓名:".$p1->name."
"; echo "性别:".$p1->sex."
"; echo "年龄:".$p1->age."
"; ?> 程序执行结果: 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接获取私有属性值的时候,自动调用了这个__get()方法 姓名:张三 在直接获取私有属性值的时候,自动调用了这个__get()方法 性别:男 在直接获取私有属性值的时候,自动调用了这个__get()方法 年龄:20 以上代码如果不加上__get()和__set()方法,程序出错,因为不能在类的外部操作私有成员, 而上面的代码是通过自动调用__get()和__set()方法来帮助我们直接存取封装的私有成员的。 __isset() 方法:在看这个方法之前我们看一下“isset()”函数的应用,isset()是测定变量是否设 定用的函数,传入一个变量作为参数,如果传入的变量存在则传回true,否则传回false。那么如果 在一个对象外面使用“isset()”这个函数去测定对象里面的成员是否被设定可不可以用它呢?分两种 情况,如果对象里面成员是公有的,我们就可以使用这个函数来测定成员属性,如果是私有的成员 属性,这个函数就不起作用了,原因就是因为私有的被封装了,在外部不可见。那么我们就不可以 在对象的外部使用“isset()”函数来测定私有成员属性是否被设定了呢?可以,你只要在类里面加上 一个“__isset()”方法就可以了,当在类外部使用”isset()”函数来测定对象里面的私有成员是否被设 定时, 就自动调用类里面的“__isset()”方法了帮我们完成这样的操作,“__isset()”方法也可以 做成私有的。你可以在类里面加上下面这样的代码就可以了: private function __isset($nm) { echo "当在类外部使用isset()函数测定私有成员$nm时,自动调用
"; return isset($this->$nm); } __unset()方法:看这个方法之前呢,我们也先来看一下“unset()”这个函数,“unset()”这个函 数的作用是删除指定的变量且传回true,参数为要删除的变量。那么如果在一个对象外部去删除对 LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》80/104 象内部的成员属性用“unset()”函数可不可以呢,也是分两种情况,如果一个对象里面的成员属性 是公有的,就可以使用这个函数在对象外面删除对象的公有属性,如果对象的成员属性是私有的, 我使用这个函数就没有权限去删除,但同样如果你在一个对象里面加上“__unset()”这个方法,就 可以在对象的外部去删除对象的私有成员属性了。在对象里面加上了“__unset()”这个方法之后, 在对象外部使用“unset()”函数删除对象内部的私有成员属性时,自动调用“__unset()”函数来帮 我们删除对象内部的私有成员属性,这个方法也可以在类的内部定义成私有的。在对象里面加上下 面的代码就可以了: private function __unset($nm) { echo "当在类外部使用unset()函数来删除私有成员时自动调用的
"; unset($this->$nm); } 我们来看一个完整的实例: $property_name)) { return($this->$property_name); }else { return(NULL); } }/ /__set()方法用来设置私有属性 private function __set($property_name, $value) { $this->$property_name = $value; }/ /__isset()方法 private function __isset($nm) { echo "isset()函数测定私有成员时,自动调用
"; return isset($this->$nm); }/ /__unset()方法 private function __unset($nm) { echo "当在类外部使用unset()函数来删除私有成员时自动调用的
"; unset($this->$nm); } } $p1=new Person(); $p1->name="this is a person name"; //在使用isset()函数测定私有成员时,自动调用__isset()方法帮我们完成,返回结果为true echo var_dump(isset($p1->name))."
"; LAMP 大讲堂PHP 面向对象技术(全面讲解) 网站:http://www.phpchina.com 投稿:phper@phpchina.com 《PHPer》81/104 echo $p1->name."
"; //在使用unset()函数删除私有成员时,自动调用__unset()方法帮我们完成,删除name私有属性 unset($p1->name); //已经被删除了, 所这行不有输出 echo $p1->name; ?> 输出结果为: isset()函数测定私有成员时,自动调用 bool(true) this is a person name 当在类外部使用unset()函数来删除私有成员时自动调用的 __set()、__get()、__isset()、__unset() 这四个方法都是我们添加到对象里面的,在需要时自动调 用的,来完成在对象外部对对象内部私有属性的操作。
(1) 分数的输入如果直接输入“1/5”,系统将其变为“1月5日”,解决办法是:先输入“0”,然后输入空格,再输入分数“1/5”。(2) 序列“001”的输入如果直接输入“001”,系统自动判断001为数据1,解决办法是:首先输入“'”(西文单引号),然后输入“001”。(3) 日期的输入如果要输入“4月5日”,直接输入“4/5”,再敲回车就行了。如果要输入当前日期,按一下“Ctrl+;”键。(4) 填充条纹如果想在工作簿中加入漂亮的横条纹,可以利用对齐方式中的填充功能。先在一单元格内填入“*”或“~”等符号,然后单击此单元格,向右拖动鼠标,选中横向若干单元格,单击“格式”菜单,选中“单元格”命令,在弹出的“单元格格式”菜单中,选择“对齐”选项卡,在水平对齐下拉列表中选择“填充”,单击“确定”按钮(如图1)。图1(5) 多张工作表中输入相同的内容几个工作表中同一位置填入同一数据时,可以选中一张工作表,然后按住Ctrl键,再单击窗口左下角的Sheet1、Sheet2......来直接选择需要输入相同内容的多个工作表,接着在其中的任意一个工作表中输入这些相同的数据,此时这些数据自动出现在选中的其它工作表之中。输入完毕之后,再次按下键盘上的Ctrl键,然后使用鼠标左键单击所选择的多个工作表,解除这些工作表的联系,否则在一张表单中输入的数据接着出现在选中的其它工作表内。(6) 不连续单元格填充同一数据选中一个单元格,按住Ctrl键,用鼠标单击其他单元格,就将这些单元格全部都选中了。在编辑区中输入数据,然后按住Ctrl键,同时敲一下回车,在所有选中的单元格中都出现了这一数据。(7) 在单元格中显示公式如果工作表中的数据多数是由公式生成的,想要快速知道每个单元格中的公式形式,以便编辑修改,可以这样做:用鼠标左键单击“工具”菜单,选取“选项”命令,出现“选项”对话框,单击“视图”选项卡,接着设置“窗口选项”栏下的“公式”项有效,单击“确定”按钮(如图2)。这时每个单元格中的分工就显示出来了。如果想恢复公式计算结果的显示,就再设置“窗口选项”栏下的“公式”项失效即可。图2(8) 利用Ctrl+*选取文本如果一个工作表中有很多数据表格时,可以通过选定表格中某个单元格,然后按下Ctrl+*键可选定整个表格。Ctrl+*选定的区域为:根据选定单元格向四周辐射所涉及到的有数据单元格的最大区域。这样我们可以方便准确地选取数据表格,并能有效避免使用拖动鼠标方法选取较大单元格区域时屏幕的乱滚现象。(9) 快速清除单元格的内容如果要删除内容的单元格中的内容和它的格式和批注,就不能简单地应用选定该单元格,然后按Delete键的方法了。要彻底清除单元格,可用以下方法:选定想要清除的单元格或单元格范围;单击“编辑”菜单中“清除”项中的“全部”命令,这些单元格就恢复了本来面目。2、单元格内容的合并根据需要,有时想把B列与C列的内容进行合并,如果行数较少,可以直接用“剪切”和“粘贴”来完成操作,但如果有几万行,就不能这样办了。解决办法是:在C行后插入一个空列(如果D列没有内容,就直接在D列操作),在D1中输入“=B1&C1;”,D1列的内容就是B、C两列的和了。选中D1单元格,用鼠标指向单元格右下角的小方块“■”,当光标变成"+"后,按住鼠标拖动光标向下拖到要合并的结尾行处,就完成了B列和C列的合并。这时先不要忙着把B列和C列删除,先要把D列的结果复制一下,再用“选择性粘贴”命令,将数据粘贴到一个空列上。这时再删掉B、C、D列的数据。下面是一个“&”实际应用的例子。用AutoCAD绘图时,有人喜欢在EXCEL中存储坐标点,在绘制曲线时调用这些参数。存放数据格式为“x,y”的形式,首先在Excel中输入坐标值,将x坐标值放入A列,y坐标值放入到B列,然后利用“&”将A列和B列合并成C列,在C1中输入:=A1&","&B1;,此时C1中的数据形式就符合要求了,再用鼠标向下拖动C1单元格,完成对A列和B列的所有内容的合并(如图3-4)。合并不同单元格的内容,还有一种方法是利用CONCATENATE函数,此函数的作用是将若干文字串合并到一个字串中,具体操作为“=CONCATENATE(B1,C1)”。比如,假设在某一河流生态调查工作表中,B2包含“物种”、B3包含“河鳟鱼”,B7包含总数45,那么: 输入“=CONCATENATE("本次河流生态调查结果:",B2,"",B3,"为",B7,"条/公里。")” 计算结果为:本次河流生态调查结果:河鳟鱼物种为45条/公里。图3图43、条件显示我们知道,利用If函数,可以实现按照条件显示。一个常用的例子,就是教师在统计学生成绩时,希望输入60以下的分数时,能显示为“不及格”;输入60以上的分数时,显示为“及格"。这样的效果,利用IF函数可以很方便地实现。 假设成绩在A2单元格中,判断结果在A3单元格中。那么在A3单元格中输入公式: =if(A2<60,“不及格”,“及格”) 同时,在IF函数中还可以嵌套IF函数或其它函数。例如,如果输入: =if(A2<60,“不及格”,if(A2<=90,“及格”,“优秀")) 就把成绩分成了三个等级。如果输入=if(A2<60,“差",if(A2<=70,“中”,if(A2<90,“良”,“优”))) 就把成绩分为了四个等级。再比如,公式: =if(SUM(A1:A5>0,SUM(A1:A5),0) 此式就利用了嵌套函数,意思是,当A1至A5的和大于0时,返回这个值,如果小于0,那么就返回0。 还有一点要提醒你注意:以上的符号均为半角,而且IF与括号之间也不能有空格。4、自定义格式Excel中预设了很多有用的数据格式,基本能够满足使用的要求,但对一些特殊的要求,如强调显示某些重要数据或信息、设置显示条件等,就要使用自定义格式功能来完成。 Excel的自定义格式使用下面的通用模型:正数格式,负数格式,零格式,文本格式,在这个通用模型中,包含三个数字段和一个文本段:大于零的数据使用正数格式;小于零的数据使用负数格式;等于零的数据使用零格式;输入单元格的正文使用文本格式。 我们还可以通过使用条件测试,添加描述文本和使用颜色来扩展自定义格式通用模型的应用。(1)使用颜色 要在自定义格式的某个段中设置颜色,只需在该段中增加用方括号括住的颜色名或颜色编号。Excel识别的颜色名为:[黑色]、[红色]、[白色]、[蓝色]、[绿色]、[青色]和[洋红]。Excel也识别按[颜色X]指定的颜色,其中X是1至56之间的数字,代表56种颜色(如图5)。图5(2)添加描述文本 要在输入数字数据之后自动添加文本,使用自定义格式为:"文本内容"@;要在输入数字数据之前自动添加文本,使用自定义格式为:@"文本内容"。@符号的位置决定了Excel输入的数字数据相对于添加文本的位置。(3)创建条件格式 可以使用六种逻辑符号来设计一个条件格式:>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、=(等于)、<>(不等于),如果你觉得这些符号不好记,就干脆使用“>”或“>=”号来表示。由于自定义格式中最多只有3个数字段,Excel规定最多只能在前两个数字段中包括2个条件测试,满足某个测试条件的数字使用相应段中指定的格式,其余数字使用第3段格式。如果仅包含一个条件测试,则要根据不同的情况来具体分析。自定义格式的通用模型相当于下式:[>;0]正数格式;[<;0]负数格式;零格式;文本格式。下面给出一个例子:选中一列,然后单击“格式”菜单中的“单元格”命令,在弹出的对话框中选择“数字”选项卡,在“分类”列表中选择“自定义”,然后在“类型”文本框中输入“"正数:"($#,##0.00);"负数:"($ #,##0.00);"零";"文本:"@”,单击“确定”按钮,完成格式设置。这时如果我们输入“12”,就在单元格中显示“正数:($12.00)”,如果输入“-0.3”,就在单元格中显示“负数:($0.30)”,如果输入“0”,就在单元格中显示“零”,如果输入文本“this is a book”,就在单元格中显示“文本:this is a book”。 如果改变自定义格式的内容,“[红色]"正数:"($#,##0.00);[蓝色]"负数:"($ #,##0.00);[黄色]"零";"文本:"@”,那么正数、负数、零将显示为不同的颜色。如果输入“; ;[Yellow];[Green]”,那么正数、负数、零和文本将分别显示上面的颜色(如图6)。图6 再举一个例子,假设正在进行帐目的结算,想要用蓝色显示结余超过$50,000的帐目,负数值用红色显示在括号中,其余的值用缺省颜色显示,可以创建如下的格式: “[蓝色][>50000] $#,##0.00_);[红色][<0]( $#,##0.00); $#,##0.00_)” 使用条件运算符也可以作为缩放数值的强有力的辅助方式,例如,如果所在单位生产几种产品,每个产品中只要几克某化合物,而一天生产几千个此产品,那么在编制使用预算时,需要从克转为千克、吨,这时可以定义下面的格式: “[>999999]#,##0,,_m"吨"";[>999]##,_k_m"千克";#_k"克"” 可以看到,使用条件格式,千分符和均匀间隔指示符的组合,不用增加公式的数目就可以改进工作表的可读性和效率。另外,我们还可以运用自定义格式来达到隐藏输入数据的目的,比如格式";##;0"只显示负数和零,输入的正数则不显示;格式“;;;”则隐藏所有的输入值。 自定义格式只改变数据的显示外观,并不改变数据的值,也就是说不影响数据的计算。灵活运用好自定义格式功能,将给实际工作带来很大的方便。5、绘制函数图象做教学工作的朋友们一定遇到画函数曲线的问题吧!如果想快速准确地绘制一条函数曲线,可以借助EXCEL的图表功能,它能使你画的曲线既标准又漂亮。你一定问,是不是很难学呀?其实这一点儿也不难,可以说非常简便,不信你就跟我试一试。以绘制y=|lg(6+x^3)|的曲线为例,其方法如下: 在某张空白的工作表中,先输入函数的自变量:在A列的A1格输入"X=",表明这是自变量,再在A列的A2及以后的格内逐次从小到大输入自变量的各个值;实际输入的时候,通常应用等差数列输入法,先输入前二个值,定出自变量中数与数之间的步长,然后选中A2和A3两个单元格,使这二项变成一个带黑色边框的矩形,再用鼠标指向这黑色矩形的右下角的小方块“■”,当光标变成"+"后,按住鼠标拖动光标到适当的位置,就完成自变量的输入。输入函数式:在B列的B1格输入函数式的一般书面表达形式,y=|lg(6+x^3)|;在B2格输入“=ABS(LOG10(6+A2^3))”,B2格内马上得出了计算的结果。这时,再选中B2格,让光标指向B2矩形右下角的“■”,当光标变成"+"时按住光标沿B列拖动到适当的位置即完成函数值的计算。图7绘制曲线:点击工具栏上的“图表向导”按钮,选择“X,Y散点图”(如图7),然后在出现的“X,Y散点图”类型中选择“无数据点平滑线散点图”;此时可察看即将绘制的函数图像,发现并不是我们所要的函数曲线,单击“下一步”按钮,选中“数据产生在列”项,给出数据区域,这时曲线就在我们面前了(如图8)。图8需要注意:如何确定自变量的初始值,数据点之间的步长是多少,这是要根据函数的具体特点来判断,这也是对使用者能力的检验。如果想很快查到函数的极值或看出其发展趋势,给出的数据点也不一定非得是等差的,可以根据需要任意给定。从简单的三角函数到复杂的对数、指数函数,都可以用EXCEL画出曲线。如果用得到,你还可以利用EXCEL来完成行列式、矩阵的各种计算,进行简单的积分运算,利用迭代求函数值(如x^2=x^7+4,可用迭代方法求x值),等等,凡是涉及计算方面的事,找EXCEL来帮忙,它一定给你一个满意的答案。6、自定义函数虽然Excel中已有大量的内置函数,但有时可能还碰到一些计算无函数可用的情况。假如某公司采用一个特殊的数学公式计算产品购买者的折扣,如果有一个函数来计算岂不更方便?下面就说一下如何创建这样的自定义函数。自定义函数,也叫用户定义函数,是Excel最富有创意和吸引力的功能之一,下面我们在Visual Basic模块中创建一个函数。 在下面的例子中,我们要给每个人的金额乘一个系数,如果是上班时的工作餐,就打六折;如果是加班时的工作餐,就打五折;如果是休息日来就餐,就打九折。首先打开“工具”菜单,单击“宏”命令中的“Visual Basic编辑器”,进入Visual Basic编辑环境,在“工程-VBAobject”栏中的当前表上单击鼠标右键,选择“插入”-“模块”,在右边栏创建下面的函数rrr,代码如下: Function rrr(tatol, rr) If rr = "上班" Then rrr = 0.6 * tatol ElseIf rr = "加班" Then rrr = 0.5 * tatol ElseIf rr = "休息日" Then rrr = 0.9 * tatol End If End Function (如图9)。图9这时关闭编辑器,只要我们在相应的列中输入rrr(F2,B2),那么打完折后的金额就算出来了(如图10)。图107、矩阵计算Excel的强大计算功能,不但能够进行简单的四则运算,也可以进行数组、矩阵的计算。(1)数组和矩阵的定义矩阵不是一个数,而是一个数组。在Excel里,数组占用一片单元域,单元域用大括号表示,例如{A1:C3},以便和普通单元域A1:C3相区别。设置时先选定单元域,同时按Shift+Ctrl+Enter键,大括弧即自动产生,数组域得以确认。一个单元格就是一个变量,一片单元域也可以视为一组变量。为了计算上的方便,一组变量最好给一个数组名。例如A={A1:C3}、B={E1:G3}等。数组名的设置步骤是:选定数组域,单击“插入”菜单,选择“名称”项中的“定义”命令,输入数组名,单击“确定”按钮即可。更简单的命名办法为:选择数组域,单击名称框,直接输入名称就行了。矩阵函数是Excel进行矩阵计算的专用模块。用“插入”-“函数”命令打开“粘贴函数”对话框(如图11),选中函数分类栏中的“数学与三角函数”,在右边栏常用的矩阵函数有: MDETERM——计算一个矩阵的行列式; MINVERSE——计算一个矩阵的逆矩阵; MMULT——计算两个矩阵的乘积; SUMPRODUCT——计算所有矩阵对应元素乘积之和。图11(2)矩阵的基本计算数组计算和矩阵计算有很大的区别,比如下面这个例子中,A和B都是定义好的数组,因为这两个数组都是3×3的,输出结果也是3×3个单元格。计算时先选定矩阵计算结果的输出域,为3×3的单元格区域,然后输入公式。如果输入“=A+B”或“=A-B”,计算结果是数组对应项相加或相减,输入“=A*B”表示数组A和B相乘,输入“=A/B”表示数组A除数组B。如果要进行矩阵计算,就要用到相应的矩阵函数。矩阵相加、相减与数组的加减表达形式是一样的,也是“=A+B”和“=A-B”,表示矩阵相乘可以输入“=MMULT(A,B)”,而矩阵相除是矩阵A乘B的逆矩阵,所以计算公式是“=MMULT(A,MINVERSE(B))”。公式输入后,同时按Shift+Ctrl+Enter键得到计算结果。 对于更复杂的矩阵计算,可以采用分步计算。8、自动切换输入法在一张工作表中,往往是既有数据,又有文字,这样在输入时就需要来回在中英文之间反复切换输入法,非常麻烦。 如果你要输入的东西很有规律性,比如这一列全是单词,下一列全是汉语解释,你可以用以下方法实现自动切换。方法是:(1)选中要输入英文的列,单击“数据”菜单,选择“有效性...”命令,在弹出的“数据有效性”对话框中,选中“输入法模式”选项卡,在“模式”框中选择“关闭(英文模式)”命令,单击“确定”按钮(如图12)。图12(2)选中要输入汉字的列,在“有效数据”对话框中,单击“IME模式”选项卡,在“模式”框中选择“打开”命令,单击“确定”按钮。这样,当光标在前一列时,可以输入英文,在下一列时,直接可以输入中文,从而实现了中英文输入方式之间的自动切换。9、批量删除空行有时我们需要删除Excel工作薄中的空行,一般做法是将空行一一找出,然后删除。如果工作表的行数很多,这样做就非常不方便。我们可以利用“自动筛选”功能,把空行全部找到,然后一次性删除。 做法:先在表中插入新的一个空行,然后按下Ctrl+A键,选择整个工作表,用鼠标单击“数据”菜单,选择“筛选”项中的“自动筛选”命令。这时在每一列的顶部,都出现一个下拉列表框,在典型列的下拉列表框中选择“空白”,直到页面内已看不到数据为止(如图13)。图13在所有数据都被选中的情况下,单击“编辑”菜单,选择“删除行”命令,然后按“确定”按钮。这时所有的空行都已被删去,再单击“数据”菜单,选取“筛选”项中的“自动筛选”命令,工作表中的数据就全恢复了。插入一个空行是为了避免删除第一行数据。如果想只删除某一列中的空白单元格,而其它列的数据和空白单元格都不受影响,可以先复制 此列,把它粘贴到空白工作表上,按上面的方法将空行全部删掉,然后再将此列复制,粘贴到原工作表的相应位置上。10、如何避免错误信息在Excel中输入公式后,有时不能正确地计算出结果,并在单元格内显示一个错误信息,这些错误的产生,有的是因公式本身产生的,有的不是。下面就介绍一下几种常见的错误信息,并提出避免出错的办法。1)错误值:####含义:输入到单元格中的数据太长或单元格公式所产生的结果太大,使结果在单元格中显示不下。或是日期和时间格式的单元格做减法,出现了负值。解决办法:增加列的宽度,使结果能够完全显示。如果是由日期或时间相减产生了负值引起的,可以改变单元格的格式,比如改为文本格式,结果为负的时间量。2)错误值:#DIV/0!含义:试图除以0。这个错误的产生通常有下面几种情况:除数为0、在公式中除数使用了空单元格或是包含零值单元格的单元格引用。解决办法:修改单元格引用,或者在用作除数的单元格中输入不为零的值。3)错误值:#VALUE!含义:输入引用文本项的数学公式。如果使用了不正确的参数或运算符,或者当执行自动更正公式功能时不能更正公式,都将产生错误信息#VALUE!。解决办法:这时应确认公式或函数所需的运算符或参数正确,并且公式引用的单元格中包含有效的数值。例如,单元格C4中有一个数字或逻辑值,而单元格D4包含文本,则在计算公式=C4+D4时,系统不能将文本转换为正确的数据类型,因而返回错误值#VALUE!。4)错误值:#REF!含义:删除了被公式引用的单元格范围。解决办法:恢复被引用的单元格范围,或是重新设定引用范围。5)错误值:#N/A含义:无信息可用于所要执行的计算。在建立模型时,用户可以在单元格中输入#N/A,以表明正在等待数据。任何引用含有#N/A值的单元格都将返回#N/A。解决办法:在等待数据的单元格内填充上数据。6)错误值:#NAME?含义:在公式中使用了Excel所不能识别的文本,比如可能是输错了名称,或是输入了一个已删除的名称,如果没有将文字串括在双引号中,也产生此错误值解决办法:如果是使用了不存在的名称而产生这类错误,应确认使用的名称确实存在;如果是名称,函数名拼写错误应就改正过来;将文字串括在双引号中;确认公式中使用的所有区域引用都使用了冒号(:)。例如:SUM(C1:C10)。 注意将公式中的文本括在双引号中。7)错误值:#NUM! 含义:提供了无效的参数给工作表函数,或是公式的结果太大或太小而无法在工作表中表示。解决办法:确认函数中使用的参数类型正确。如果是公式结果太大或太小,就要修改公式,使其结果在-1×10307和1×10307之间。8)错误值:#NULL! 含义:在公式中的两个范围之间插入一个空格以表示交叉点,但这两个范围没有公共单元格。比如输入:“=SUM(A1:A10 C1:C10)”,就产生这种情况。解决办法: 取消两个范围之间的空格。上式可改为“=SUM(A1:A10 ,C1:C10)”11、宏的应用宏是一个指令集,用来告诉EXCEL来完成用户指定的动作。宏类似于计算程序,但是它是完全运行于EXCEL之中的,我们可以使用宏来完成枯燥的、频繁的重复性工作。 宏完成动作的速度比用户自己做要快得多。例如,我们可以创建一个宏,用来在工作表的每一行上输入一组日期,并在每一单元格内居中对齐日期,然后对此行应用边框格式。我们还可以创建一个宏,在“页面设置”对话框中指定打印设置并打印文档。由于宏病毒的影响和对编程的畏惧心理,使很多人不敢用“宏”,或是不知道什么时候可以找宏来帮忙。其实你尽管放心大胆地去用,如果只是用“录制宏”的方法,根本就没有什么难的,只是把一些操作象用录音机一样录下来,到用的时候,只要执行这个宏,系统就把那操作再执行一遍。下面给出了宏的应用场合,只要用“录制宏”就可以帮你完成任务,而不需要编程。如果想对所录制的宏再进行编辑,就要有一定的VBA知识了。* 设定一个每个工作表中都需要的固定形式的表头;* 将单元格设置成一种有自己风格的形式;* 每次打印都固定的页面设置;* 频繁地或是重复地输入某些固定的内容,比如排好格式的公司地址、人员名单等;* 创建格式化表格;* 插入工作表或工作薄等。需要指出的是,EXCEL中的宏与WORD中的宏有些不同之处,对于录制的操作,它记住单元格的坐标(即所有的引用都是绝对的),所以在涉及到与位置有关的操作时,要格外注意。如果相用相对引用,可以借助于Offset方法,比如下面的语句: ActiveCell.Offset(1,0). range("A1").select 宏的应用是很广的,上面提到的只是一点点,如果真的用起来,你发现它有更丰富的内容和更灵活的应用方法。12、图表的应用技巧Excel提供了14种标准的图表类型,每一种都具有多种组合和变换。在众多的图表类型中,选用那一种图表更好呢? 根据数据的不同和使用要求的不同,可以选择不同类型的图表。图表的选择主要同数据的形式有关,其次才考虑感觉效果和美观性。 下面给出了一些常见的规则。面积图:显示一段时间内变动的幅值。当有几个部分正在变动,而你对那些部分总和感兴趣时,他们特别有用。面积图使你看见单独各部分的变动,同时也看到总体的变化。条形图:由一系列水平条组成。使得对于时间轴上的某一点,两个或多个项目的相对尺寸具有可比性。比如:它可以比较每个季度、三种产品中任意一种的销售数量。条形图中的每一条在工作表上是一个单独的数据点或数。因为它与柱形图的行和列刚好是调过来了,所以有时可以互换使用。柱形图:由一系列垂直条组成,通常用来比较一段时间中两个或多个项目的相对尺寸。例如:不同产品季度或年销售量对比、在几个项目中不同部门的经费分配情况、每年各类资料的数目等。条形图是应用较广的图表类型,很多人用图表都是从它开始的。折线图:被用来显示一段时间内的趋势。比如:数据在一段时间内是呈增长趋势的,另一段时间内处于下降趋势,我们可以通过折线图,对将来作出预测。例如:速度-时间曲线、推力-耗油量曲线、升力系数-马赫数曲线、 压力-温度曲线、疲劳强度-转数曲线、转输功率代价-传输距离曲线等,都可以利用折线图来表示,一般在工程上应用较多,若是其中一个数据有几种情况,折线图里就有几条不同的线,比如五名运动员在万米过程中的速度变化,就有五条折线,可以互相对比,也可以对添加趋势线对速度进行预测。股价图:是具有三个数据序列的折线图,被用来显示一段给定时间内一种股标的最高价、最低价和收盘价。通过在最高、最低数据点之间画线形成垂直线条,而轴上的小刻度代表收盘价。股价图多用于金融、商贸等行业,用来描述商品价格、货币兑换率和温度、压力测量等,当然对股价进行描述是最拿手的了。饼形图:在用于对比几个数据在其形成的总和中所占百分比值时最有用。整个饼代表总和,每一个数用一个楔形或薄片代表。比如:表示不同产品的销售量占总销售量的百分比,各单位的经费占总经费的比例、收集的藏书中每一类占多少等。饼形图虽然只能表达一个数据列的情况,但因为表达得清楚明了,又易学好用,所以在实际工作中用得比较多。如果想多个系列的数据时,可以用环形图。雷达图:显示数据如何按中心点或其他数据变动。每个类别的坐标值从中心点辐射。来源于同一序列的数据同线条相连。你可以采用雷达图来绘制几个内部关联的序列,很容易地做出可视的对比。比如:你有三台具有五个相同部件的机器,在雷达图上就可以绘制出每一台机器上每一部件的磨损量。XY散点图:展示成对的数和它们所代表的趋势之间的关系。对于每一数对,一个数被绘制在X轴上,而另一个被绘制在Y轴上。过两点作轴垂线,相交处在图表上有一个标记。当大量的这种数对被绘制后,出现一个图形。散点图的重要作用是可以用来绘制函数曲线,从简单的三角函数、指数函数、对数函数到更复杂的混合型函数,都可以利用它快速准确地绘制出曲线,所以在教学、科学计算经常用到。还有其他一些类型的图表,比如圆柱图、圆锥图、棱锥图,只是条形图和柱形图变化而来的,没有突出的特点,而且用得相对较少,这里就不一一赘述。这里要说明的是:以上只是图表的一般应用情况,有时一组数据,可以用多种图表来表现,那时就要根据具体情况加以选择。 对有些图表,如果一个数据序列绘制成柱形,而另一个则绘制成折线图或面积图,则该图表看上去更好些。在EXCE中L允许这创建这样的组合图表,但如果想创建一种EXCEL不允许的组合图表类型,比如不可能将一个二维图同一个三维图表混在一起,这种组合是不可能的,系统就显示错误信息。怎么建立混合图表呢?单击“图表向导”按钮,选择“自定义类型”选项卡,这里还躲着二十种图表类型(如图14),也有各种组合形式。如果你对这些内部定义的形式还不满意,就单击“自定义”选项钮,按照向导自己定义全新的图表类型。

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧