这两天在研究C/C++的函数指针,找到一篇讲解比较详细的博客,内容有点多,但是讲解得比较详细,适合初学者。特转之:

1.     无处不见的函数指针

使用函数指针可以设计出更优雅的程序,比如设计一个集群的通信框架的底层通信系统:首先将要每个消息的对应处理函数的指针保存映射表中(使用STL的map,键是消息的标志,值是对应的函数指针),然后启动一个线程在结点上的某个端口侦听,收到消息后,根据消息的编号,从映射表中找到对应的函数入口,将消息体数据作为参数传给相应的函数。我曾看过lam-mpi在启动集群中每个结点的进程时的实现,该模块的最上层就是一个结构体,这个结构体中仅是由函数指针构成,每个函数指针都指向一个子模块,这样做的好处就是在运行时期间可以自由的切换子模块。比如某个子模块不适合某个体系结构,只需要改动函数指针,指向另外一个模块就可。

在平时的程序设计中,经常遇到函数指针。如EnumWindows这个函数的参数,C语言库函数qsort的参数,定义新的线程时,这些地方函数指针都是作为回调函数来应用的。

还有就是unix的库函数signal(sys/signal.h)(这个函数我们将多次用到)的声明形式为:

void (*signal)(int signo,void (*func)(int)))(int);

这个形式是相当复杂的,因为它不仅使用函数指针作为参数,而且返回类型还是函数指针(虽然这个函数在POSIX中不被推荐使用了)。

还有些底层实现实际上也用到了函数指针,可能你已经猜到了。嗯,就是C++中的多态。这是一个典型的迟绑定(late-binding)的例子,因为在编译时是无法确定到底绑定到哪个函数上执行,只有在运行时的时候才能确定。这个可以通过下面这个例子来帮助理解:

Shape *pSh;

scanf(“%d”,&choice);

if(choice)

{

pSh= new Rectangle();

}

else

{

pSh= new Square();
}

pSh->display();

对于上面这段代码,做以下几个假设:

(1)    Square继承自Rectange

(2)    Rectangle继承自Shape

(3)    display为虚函数,在每个Shape的子类链中都必须实现

正是因为在编译期间无法确定choice的值,所以在编译到最后一行的时候无法确定应该绑定到那个一个函数上,只能在运行期间根据choice的值,来确定要绑定的函数的地址。

总之,使用指针可以让我们写出更加优雅,高效,灵活的程序。另外,和普通指针相比,函数指针还有一个好处就是你不用担心内存释放问题。

但是,函数指针确实很难学的,我认为难学的东西主要有两个原因:(1)语法过于复杂。(2)语义过于复杂。从哲学上讲,可以对应为(1)形式过于复杂。(2)内容过于复杂。

比如,如果我们要描述“美女”这种动物(老婆不要生气啊~),如果在原始时代,我们可能需要通过以下这种方式:

_____                 &&&&_) )

\/,---<                &&&&&&\ \

( )c~c~~@~@            )- - &&\ \

C   >/                \<   |&/

\_O/ - 哇塞          _`*-'_/ /

,- >o<-.              / ____ _/

/   \/   \            / /\  _)_)

/ /|  | |\ \          / /  )   |

\ \|  | |/ /          \ \ /    |

\_\  | |_/            \ \_    |

/_/`___|_\            /_/\____|

|  | |                  \  \|

|  | |                   `. )

|  | |                   / /

|__|_|_                 /_/|

(____)_)                |\_\_

而现在我们只需要用语言来抽象就行,即用两个汉字“美女”或者英文“beauty”就行了。这就是形式上的简化,也就方便了我们的交流。另外一种就是内容上的复杂度过高,一个高度抽象的表达式后面蕴含着巨大的复杂度对于我们理解问题也是很难的,例如:

P=NP?

由于接触过的书上所讲的关于函数指针方面的都是蜻蜓点水一样,让我很不满足。我认为C/C++语言函数指针难学的主要原因是由于其形式上的定义过于复杂,但是在内容上我们一定要搞清楚函数的本质。函数的本质就是表达式的抽象,它在内存中对应的数据结构为堆栈帧,它表示一段连续指令序列,这段连续指令序列在内存中有一个确定的起始地址,它执行时一般需要传入参数,执行结束后会返回一个参数。和函数相关的,应该大致就是这些内容吧。

2 函数指针简单介绍

2.1 什么是函数指针

函数指针是一个指向函数的指针(呃,貌似是废话),函数指针表示一个函数的入口地址。使用函数指针的好处就是在处理“在运行时根据数据的具体状态来选择相应的处理方式”这种需求时更加灵活。

2.2 一个简单的例子

下面是一个简单的使用函数指针取代switch-case语句的例子,为了能够比较出二者效率差异,所以在循环中进行了大量的计算。

 /* 

 *Author:Choas Lee 

 *Date:2012-02-28 

 */ 

 #include<stdio.h>
#define UNIXEVN #if defined(UNIXENV) #include<sys/time.h> #endif #define N 1000000 #define COE 1000000 float add(float a,float b){return a+b;} float minus(float a,float b){return a-b;} float multiply(float a,float b){return a*b;} float divide(float a,float b){return a/b;} typedef float (*pf)(float,float); void switch_impl(float a,float b,char op) { float result=0.0; switch(op) { case '+': result=add(a,b); break; case '-': result=minus(a,b); break; case '*': result=multiply(a,b); break; case '/': result=divide(a,b); break; } } void switch_fp_impl(float a,float b,pf p) { float result=0.0; result=p(a,b); } int conversion(struct timeval tmp_time) { return tmp_time.tv_sec*COE+tmp_time.tv_usec; } int main() { int i=; #if defined(UNIXENV) struct timeval start_point,end_point; gettimeofday(&start_point,NULL); #endif for(i=;i<N;i++) { switch_impl(12.32,54.14,'-'); } #if defined(UNIXENV) gettimeofday(&end_point,NULL); printf("check point 1:%d\n",conversion(end_point)-conversion(start_point)); gettimeofday(&start_point,NULL); #endif for(i=;i<N;i++) { switch_fp_impl(12.32,54.14,minus); } #if defined(UNIXENV) gettimeofday(&end_point,NULL); printf("check point 2:%d\n",conversion(end_point)-conversion(start_point)); #endif return ; }

下面是执行结果:

 [lichao@sg01 replaceswitch]$ ./replaceswitch 

 check point : 

 check point : 

 [lichao@sg01 replaceswitch]$ ./replaceswitch 

 check point : 

 check point : 

 [lichao@sg01 replaceswitch]$ ./replaceswitch 

 check point : 

 check point : 

 [lichao@sg01 replaceswitch]$ ./replaceswitch 

 check point : 

 check point : 

 [lichao@sg01 replaceswitch]$ ./replaceswitch 

 check point : 

 check point : 

 [lichao@sg01 replaceswitch]$ ./replaceswitch 

 check point : 

 check point : 

 [lichao@sg01 replaceswitch]$ ./replaceswitch 

 check point : 

 check point : 

从上面可以看出,使用函数指针:(一)在某种程度上简化程序的设计(二)可以提高效率。在这个例子中,使用函数指针可以提高10%的效率。

注意:以上代码在unix环境下实现的,如果要在windows下运行,可以稍微改下,把“#define UNIXENV”行删掉即可

3 C/C++函数指针的语法

从语法上讲,有两种不兼容的函数指针形式:

(1)    指向C语言函数和C++静态成员函数的函数指针

(2)    指向C++非静态成员函数的函数指针

不兼容的原因是因为在使用C++非静态成员函数的函数指针时,需要一个指向类的实例的this指针,而前一类不需要。

3.1 定义一个函数指针

指针是变量,所以函数指针也是变量,因此可以使用变量定义的方式来定义函数指针,对于普通的指针,可以这么定义:

int a=10;

int *pa=&a;

这里,pa是一个指向整型的指针,定义这个指针的形式为:

int * pa;

区别于定义非指针的普通变量的“形式”就是在类型中间和指针名称中间加了一个“*”,所以能够表达不同的“内容”。这种形式对于表达的内容是完备的,因为它说明了两点:(1)这是一个指针(2)这是一个指向整型变量的指针

以下给出三个函数指针定义的形式,第一个是C语言的函数指针,第二个和第三个是C++的函数指针的定义形式(都是指向非静态函数成员的函数指针):

int (*pFunction)(float,char,char)=NULL;

int (MyClass::*pMemberFunction)(float,char,char)=NULL;

int (MyClass::*pConstMemberFunction)(float,char,char) const=NULL;

我们先不管函数指针的定义形式,如果让我们自己来设计指向函数的函数指针的定义形式的话,我们会怎么设计?

首先,要记住一点的就是形式一定要具备完备性,能表达出我们所要表达的内容,即指向函数这个事实。我们知道普通变量指针可以指向对应类型的任何变量,同样函数指针也应该能够指向对应类型的任何变量。对应的函数类型靠什么来确定?这个我们可以想一下C++的函数重载靠什么来区分不同的函数?这里,函数类型是靠这几个方面来确定的:(1)函数的参数个数(2)函数的参数类型(3)函数的返回值类型。所以我们要设计一种形式,这种形式定义的函数指针能够准确的指向这种函数类型的任何函数。

在C语言中这种形式为:

返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);

嗯,定义变量的形式显然不是我们通常见到的这种形式:

类型名称 变量名称;

但是,这也是为了表达函数这种相对复杂的语义而不得已采用的非一致表示形式的方法。因为定义的这个函数指针变量,能够明确的表达出它指向什么类型的函数,这个函数都有哪些类型的参数这些信息,确切的说,它是完备的。你可能会问为什么要加括号?形式上讲能不能更简洁点?不能,因为不加括号就会产生二义性:

返回类型 *函数指针名称(参数类型,参数类型,参数类型,…);

这样的定义形式定义了一个“返回类型为‘返回类型*’参数为(参数类型,参数类型,参数类型,…)的函数而不是函数指针了。

接下来,对于C++来说,下面这样的定义形式也就不难理解了(加上类名称是为了区分不同类中定义的相同名称的成员函数):

返回类型 (类名称::*函数成员名称)(参数类型,参数类型,参数类型,….)

3.2 函数的调用规则

一般来说,不用太关注这个问题。调用规则主要是指函数被调用的方式,常见的有_stdcall,_fastcall,_pascal,_cdecl等规则。不同的规则在参数压入堆栈的顺序是不同的,同时在有调用者清理压入堆栈的参数还是由被调用者清理压入堆栈的参数上也是不同的。一般来说,如果你没有显式的说明调用规则的话,编译器会统一按照_cdecl来处理。

3.3 给函数指针赋值和调用

给函数指针赋值,就是为函数指针指定一个函数名称。这个过程很简单,下面是两个例子:

int func1(float f,int a,int b){return f*a/b;}

int func2(float f,int a,int b){return f*a*b}

然后我们给函数指针pFunction赋值:

pFunction=func1;

pFunction=&func2;

上面这段代码说明了两个问题:(1)一个函数指针可以多次赋值(想想C++中的引用)(2)取地址符号是可选的,却是推荐使用的。

我们可以思考一下为什么取地址符号是可选的,在普通的指针变量赋值时,如上面所示,需要加取地址符号,而这里却是可选的?这是由于要同时考虑到两个因素(1)避免二义性(2)形式一致性。在普通指针赋值,需要加取地址符号是为了区别于将地址还是将内容赋给指针。而在函数赋值时没有这种考虑,因为这里的语义是清晰的,加上&符号是为了和普通指针变量一致---“因为一致的时候就不容易出错”。

最后我们来使用这个函数

pFunction(10.0,’a’,’b’);

(*pFunction)(10.0,’a’,’b’);

上面这两种使用函数指针调用函数的方式都是可以的,原因和上面一样。

下面来说明C++中的函数指针赋值和调用,这里说明非静态函数成员的情况,C++中规则要求的严格的多了。让我感觉C++就像函数指针的后爸一样,对函数指针要求特别死,或许是因为他有一个函数对象这个亲儿子。

在C++中,对于赋值,你必须要加“&”,而且你还必须再次之前已经定义好了一个类实例,取地址符号要操作于这个类实例的对应的函数成员上。在使用成员函数的指针调用成员函数时,你必须要加类实例的名称,然后再使用.*或者->*来使用成员函数指针。举例如下:

MyClass

{

public:

int func1(float f,char a,char b)

{

return f*a*b;

}

int func2(float f,char a,char b) const

{

return f*a/b;
      }

}

首先来赋值:

MyClass mc;

pMemberFunction= &mc.func1;  //必须要加取地址符号

pConstMemberFunction = &mc.func2;

接下来,调用函数:

(mc.*pMemberFunction)(10.0,’a’,’b’);

(mc.*pConstMemberFunction)(10.0,’a’,’b’);

我感觉,C++简直在虐待函数指针啊。

下面是一个完整的例子:

 /* 

 *Author:Choas Lee 

 *Date:2012-02-28 

 */ 

 #include<stdio.h> 

 float func1(float f,char a,char b) 

 { 

       printf("func1\n"); 

       return f*a/b; 

 } 

 float  func2(float f,char a,char b) 

 { 

       printf("func2\n"); 

       return f*a*b; 

 } 

 class MyClass 

 { 

 public: 

       MyClass(float f) 

       { 

              factor=f; 

       } 

       float func1(float f,char a,char b) 

       { 

              printf("MyClass::func1\n"); 

              return f*a/b*factor;      

       } 

       float func2(float f,char a,char b) const 

       { 

              printf("MyClass::func2\n"); 

              return f*a*b*factor; 

       } 

 private: 

       float factor; 

 }; 

 int main(int argc,char *argv[]) 

 { 

       float (*pFunction)(float,char,char)=NULL; 

       float (MyClass::*pMemberFunction)(float,char,char)=NULL; 

       float (MyClass::*pConstMemberFunction)(float,char,char)const=NULL; 

       float f=10.0; 

       char a='a',b='b'; 

       float result; 

       pFunction=func1; 

       printf("pointer pFunction's address is:%x\n",pFunction); 

       result=(*pFunction)(f,a,b); 

         printf("result=%f\n",result); 

       pFunction=&func2; 

       printf("pointer pFunction's address is:%x\n",pFunction); 

       result=pFunction(f,a,b); 

         printf("result=%f\n",result); 

       if(func1!=pFunction) 

              printf("not equal.\n"); 

       pMemberFunction=&MyClass::func1; 

       MyClass mc1(0.2); 

       printf("pointer pMemberFunction's address is:%x\n",pMemberFunction); 

       result=(mc1.*pMemberFunction)(f,a,b); 

         printf("result=%f\n",result); 

       pConstMemberFunction=&MyClass::func2; 

       MyClass mc2(); 

       printf("pointer pConstMemberFunction's address is:%x\n",pConstMemberFunction); 

       result=(mc2.*pConstMemberFunction)(f,a,b); 

         printf("result=%f\n",result); 

       return ; 

 } 

运行结果为:

 pointer pFunction's address is:400882 

 func1 

 result=9.897959 

 pointer pFunction's address is:400830 

 func2 

 result=95060.000000 

 not equal. 

 pointer pMemberFunction's address is:400952 

 MyClass::func1 

 result=1.979592 

 pointer pConstMemberFunction's address is:4008f2 

 MyClass::func2 

 result=190120.000000 

注意:上面的代码还说明了一点就是函数指针的一些基本操作,函数指针没有普通变量指针的算术操作,但是可以进行比较操作。如上面代码所示。

使用类的静态函数成员的函数指针和使用C语言的函数很类似,这里仅仅给出一个例子和其执行结果:

程序代码为:

 /* 

 *Author:Chaos Lee 

 *Date:2012-02-28 

 */ 

 #include<iostream> 

 class MyClass 

 { 

 public: 

       static float plus(float a,float b) 

       { 

              return a+b; 

       }     

 }; 

 int main() 

 { 

       float result,a=10.0,b=10.0; 

       float (*p)(float,float); 

       p=&MyClass::plus; 

       result=p(a,b); 

       printf("result=%f\n",result); 

       return ; 

 }
//执行结果为: //result=20.000000

3.4 函数指针作为参数

如果你已经明白了函数的参数机制,而且完全理解并实践了3.3节的内容,这一节其实是很简单的。只需要在函数的参数列表中,声明一个函数指针类型的参数即可,然后再调用的时候传给它一个实参就可以了。你可以这么想象,就是把函数指针的赋值语句的等号换成了形参和实参结合的模式就行。

下面给一个简单的例子:

      /* 

 *Author:Choas Lee 

 *Date:2012-02-28 

 */ 

 #include<stdio.h> 

 float add(float a,float b){return a+b;} 

 float minus(float a,float b){return a-b;} 

 float multiply(float a,float b){return a*b;} 

 float divide(float a,float b){return a/b;} 

 int pass_func_pointer(float (*pFunction)(float a,float b)) 

 { 

       float result=pFunction(10.0,12.0); 

       printf("result=%f\n",result); 

 } 

 int main() 

 { 

       pass_func_pointer(add); 

       pass_func_pointer(minus); 

       pass_func_pointer(multiply); 

       pass_func_pointer(divide); 

       return ; 

 } 

输出结果为:

 result=22.000000 

 result=-2.000000 

 result=120.000000 

 result=0.833333 

3.5 使用函数指针作为返回值

函数指针可以作为返回值。我们先类比的思考一下,如果说整型可以作为返回值,你会怎么声明函数?嗯,应该是下面这个样子的:

int func(){}

整数对应的类型为int。同样再类比以下,如果说整型指针可以作为返回值,你会怎么声明?嗯,这个貌似难度也不大:

int * func(){}

好吧,现在说函数指针如果可以作为返回值,你该怎么声明?首先要保证的一点就是返回的函数指针的类型必须是能够明显的表达在这个函数的声明或者定义形式中的,也就是说在这个形式中,要能够包含函数指针所对应的能够确定函数类型的信息:这个函数类型的返回值类型,这个函数类型的参数个数,这个函数类型的参数类型。,

现在我们在类比一次,如果要返回浮点型指针,那么返回类型应该表达为:

float *

如果要函数指针对应的函数是返回值为浮点型,带有两个参数,两个参数都是浮点型,那么返回类型应该表达为下面的表达形式:
       float (*)(float ,float )

嗯,没办法,函数的语义比较复杂,对应的表现就是形式的复杂性了。对于返回为浮点型指针的情况,定义的函数的名称放在“float *”的后面,而对于返回为上面类型的函数指针的话,定义的函数就要放在“(*)”这个括号中的*的后面了。

所以对于以下形式:

float (* func(char op) ) (float ,float)

其具体含义就是,声明了这样一个函数:

l  其名称为func,其参数的个数为1个;

l  其各个参数的类型为:op—char;

l  其返回变量(函数指针)类型为:float(*)(float,float)

再次强调:函数指针时变量哦。

到了这里之后,我们再来分析一下unix的系统调用函数signal的定义形式:

void (*signal)(int signo,void (*func)(int)))(int);

其具体含义为就是,声明了这样一个函数:

l  其函数名称为:signal

l  其参数个数为:2

l  其各个参数的类型为:signo--int, func— void (*)(int)

l  其返回的变量(函数指针)的类型为:void(*)(int)

上面这个函数比较经典,有一个参数类型为函数指针,返回值还是函数指针。

哦,我的天,如果你一步一步看到这里了,就快大功告成啦。嘿嘿,接下来看一个例子:

 /* 

 *Author:Choas Lee 

 *Date:2012-02-28 

 */ 

 #include<stdio.h> 

 #include<stdlib.h> 

 #include<string.h> 

 float add(float a,float b){return a+b;} 

 float minus(float a,float b){return a-b;} 

 float multiply(float a,float b){return a*b;} 

 float divide(float a,float b){return a/b;} 

 float(* FunctionMap(char op) )(float,float) 

 { 

       switch(op) 

       { 

              case '+': 

                     return add; 

                     break; 

              case '-': 

                     return minus; 

                     break; 

              case '*': 

                     return multiply; 

                     break; 

              case '\\': 

                     return divide; 

                     break; 

              default: 

                     exit(); 

       } 

 } 

 int main() 

 { 

       float a=,b=; 

       char ops[]={'+','-','*','\\'}; 

       int len=strlen(ops); 

       int i=; 

       float (*returned_function_pointer)(float,float); 

       for(i=;i<len;i++) 

       { 

              returned_function_pointer=FunctionMap(ops[i]); 

              printf("the result caculated by the operator %c is %f\n",ops[i],returned_function_pointer(a,b)); 

       } 

       return ; 

 } 

计算的结果为:

 the result caculated by the operator + is 15.000000 

 the result caculated by the operator - is 5.000000 

 the result caculated by the operator * is 50.000000 

 the result caculated by the operator \ is 2.000000 

转自http://hipercomer.blog.51cto.com/4415661/792300

C/C++函数指针详解(转)的更多相关文章

  1. C语言函数名与函数指针详解

    一.通常的函数调用 一个通常的函数调用的例子: /* 自行包含头文件 */ void MyFun(int x); /* 此处的声明也可写成:void MyFun(int) */ int main(in ...

  2. C 函数指针详解

    一 通常的函数调用    一个通常的函数调用的例子://自行包含头文件 void MyFun(int x); //此处的申明也可写成:void MyFun( int ); int main(int a ...

  3. 7--OC中NSLog函数输出格式详解

    OC中NSLog函数输出格式详解 • %@ 对象 • %d, %i 整数 • %u 无符整形 • %f 浮点/双字 • %x, %X 二进制整数 • %o 八进制整数 • %zu size_t • % ...

  4. 【C++】智能指针详解(一):智能指针的引入

    智能指针是C++中一种利用RAII机制(后面解释),通过对象来管理指针的一种方式. 在C++中,动态开辟的内存需要我们自己去维护,在出函数作用域或程序异常退出之前,我们必须手动释放掉它,否则的话就会引 ...

  5. Go 延迟函数 defer 详解

    Go 延迟函数 defer 详解 Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在实际应用中,很多 gopher 并没有真正搞明白 defer.re ...

  6. php中的PDO函数库详解

    PHP中的PDO函数库详解 PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力:与ADODB和MDB2相比,P ...

  7. C语言对文件的操作函数用法详解2

    fopen(打开文件) 相关函数 open,fclose 表头文件 #include<stdio.h> 定义函数 FILE * fopen(const char * path,const  ...

  8. C语言对文件的操作函数用法详解1

    在ANSIC中,对文件的操作分为两种方式,即: 流式文件操作 I/O文件操作 一.流式文件操作 这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下: typedef str ...

  9. c/c++指针详解(一)

    一:相关概念 1.指针数组:int *p[6]               是数组,是一个存放指针的数组,也就是里面存放的是地址. 2.数组指针:int (*p)[6]                 ...

随机推荐

  1. 如何修改hosts文件并生效

    hosts文件位置C:\Windows\System32\drivers\etc(可以建立一个.bat 的文件把(start "" C:\Windows\System32\driv ...

  2. 洛谷 P1589 泥泞路

    题目描述 暴雨过后,FJ的农场到镇上的公路上有一些泥泞路,他有若干块长度为L的木板可以铺在这些泥泞路上,问他至少需要多少块木板,才能把所有的泥泞路覆盖住. 输入输出格式 输入格式: 第一行为正整数n( ...

  3. ssh密钥分发之二:使用sshpass配合ssh-kopy-id编写脚本批量分发密钥:

    使用sshpass配合ssh-kopy-id编写脚本批量分发密钥: 首先sshpass是一个ssh连接时的免交互工具,首先要安装一下: yum install sshpass -y 接下来我们就可以使 ...

  4. 数据库sql 使用 lag 和OVER 函数和 like 使用 小技巧

    1. sample 1: Lag()就是取当前顺序的上一行记录.结合over就是分组统计数据的.Lag()函数,就是去上N行的字段的数据. SQL> select * from x; A---- ...

  5. 三色灯渐变DIY制作

    小编前几天查资料,怎么使用12864屏幕的用法,突然发觉微博是个好东西,随着自己的成长,学习了很多的知识,没有做笔记的习惯,只是习惯把用到的硬件,传感器,资料写到程序的备注内,但感觉,用到时不是那么方 ...

  6. Linux常用命令awk

    awk能够处理类似csv这种按行格式的数据,对每一行record按照-F指定的分隔符切割,然后处理.默认支持空格和\t分隔符 1.统计文件里某一列数据等于某个值的个数 -0_djt10.txt 2.拼 ...

  7. Web API性能优化(一)压缩

    简单的应用场景:分页获取日志JSON信息. 很简单的实现,简单的记录一下 未压缩时候 使用PostMan请求http://localhost:34390/api/gpm/syslog/page?pag ...

  8. Java提供的序列化和反序列化

    序列化:是指将Java对象转换为二进制数据. 反序列化:将二进制数据转换为Java对象. 与序列化功能相关的类有: java.io.Serializable; java.io.ObjectOutput ...

  9. AJPFX总结java InputStream读取数据问题

    1. 关于InputStream.read()     在从数据流里读取数据时,为图简单,经常用InputStream.read()方法.这个方法是从流里每次只读取读取一个字节,效率会非常低.     ...

  10. AJPFX总结Collection集合(上)

    出现集合类的原因 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一个方式. 数组和集合都是容器有何不同? 数组虽也可存储对象,但长度 ...