社区
C++ 语言
帖子详情
设计一个日期类Date,包括年、月、日三个私有数据成员。要求利用运算符重载实现:1日期对象“-”,求得两个日期间相差的天数2日期对象+常量,求得该天的日期
fushuiya
2009-06-02 09:10:12
这个应该要考虑闰年等等,貌似代码很长
求教啊
感激不尽!
...全文
1634
2
打赏
收藏
设计一个日期类Date,包括年、月、日三个私有数据成员。要求利用运算符重载实现:1日期对象“-”,求得两个日期间相差的天数2日期对象+常量,求得该天的日期
这个应该要考虑闰年等等,貌似代码很长 求教啊 感激不尽!
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
2 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
zuojiayoujian
2012-05-11
打赏
举报
回复
//解题思路:较小日期年份还剩天数+中间相隔年份天数+较大日期所在年份已过去天数,当然也不止这一种思路
#include<iostream.h>
class Date
{
public:
int Year;
int Month;
int Day;
Date()
{
Year=0;
Month=0;
Day=0;
}
Date(int(x),int(y),int(z))
{
Year=x;
Month=y;
Day=z;
}
friend void operator-(Date &da1,Date &da2);
};
bool fun1(int d)//判断闰年
{
if ((d%100==0&&d%400==0)||(d%100!=0&&d%4==0))return 1;
else return 0;
}
int fun2(Date &d1)//较小日期所在年份还有几天
{
int M[12]={31,28,31,30,31,30,31,31,30,31,30,31};
int sumd=0;
if(fun1(d1.Year)) M[1]=29;
for(int i=0;i<12-d1.Month;i++)
{
sumd=sumd+M[d1.Month+i];
}
return sumd+M[d1.Month-1]-d1.Day;
}
int fun3(Date &d1)//较大日期所在年份已过去了几天
{
int M[12]={31,28,31,30,31,30,31,31,30,31,30,31};
int sumd=0;
if(fun1(d1.Year)) M[1]=29;
for(int i=0;i<d1.Month;i++)
{
sumd=sumd+M[i];
}
return sumd-M[d1.Month-1]+d1.Day;
}
void operator-(Date &da1,Date &da2)
{
Date tmp;
if(da1.Year>da2.Year)
{
tmp=da1;
da1=da2;
da2=tmp;
}
if(da1.Year==da2.Year)
{
if(da1.Month>da2.Month)
{
tmp=da1;
da1=da2;
da2=tmp;
}
}
int a=fun2(da1);
int b=fun3(da2);
int y=da2.Year-da1.Year;
if(y==0)
{
if(fun1(da1.Year))cout<<"相差天数:"<<a+b-366<<endl;
else cout<<"相差天数:"<<a+b-365<<endl;
}
else if(y==1)
{
cout<<"相差天数:"<<a+b<<endl;
}
else
{
int n=0;
for(int j=da1.Year;j<da2.Year;j++)
{
if(fun1(j))n++;
}
cout<<"相差天数:"<<a+b+(y-1)*365+n<<endl;
}
}
void main()
{
int year[2],month[2],day[2];
for(int i=0;i<2;i++)
{
cout<<"输入日期:";
loop: cin>>year[i]>>month[i]>>day[i];
if(month[i]<=0||day[i]<=0)
{
cout<<"输入日期出错!! 请重新输入";
goto loop;
}
}
Date ds1(year[0],month[0],day[0]),ds2(year[1],month[1],day[1]);
ds2-ds1;
}
w29468
2009-06-02
打赏
举报
回复
找找MFC的COleDateTimeSpan的源码
继承多态定义
一个
类Employee(雇员)
3.定义
一个
类Employee(雇员)
三个
属性,雇员姓名(name,String),薪水(salary,double),开始雇佣时间(hireDay,
Date
)
一个
构造方法,带参数,初始化各个属性,特别
要求
带有
年
月
日
的参数,并
利用
GregorianCalendar
日
历类生成
日
期
并转化成对应的
Date
类的
日
期
对于每个属性添加
一个
set和get方法
一个
实现
增长薪水的的方法raiseSalary,传递
一个
double类型的薪水增长率
一个
Employee类的数组,生成
三个
雇员类的
对象
填充到数组中
新手学习C++入门资料
主体:(一) 一、C++概述 (一) 发展历史 1980
年
,Bjarne Stroustrup博士开始着手创建一种模拟语言,能够具有面向
对象
的程序
设计
特色。在当时,面向
对象
编程还是
一个
比较新的理念,Stroustrup博士并不是从头开始
设计
新语言,而是在C语言的基础上进行创建。这就是C++语言。 1985
年
,C++开始在外面慢慢流行。经过多
年
的发展,C++已经有了多个版本。为次,ANSI和ISO的联合委员会于1989
年
着手为C++制定标准。1994
年
2
月
,该委员会出版了第一份非正式草案,1998
年
正式推出了C++的国际标准。 (二) C和C++ C++是C的超集,也可以说C是C++的子集,因为C先出现。按常理说,C++编译器能够编译任何C程序,但是C和C++还是有一些小差别。 例如C++增加了C不具有的关键字。这些关键字能作为函数和变量的标识符在C程序中使用,尽管C++包含了所有的C,但显然没有任何C++编译器能编译这样的C程序。 C程序员可以省略函数原型,而C++不可以,
一个
不带参数的C函数原型必须把void写出来。而C++可以使用空参数列表。 C++中new和delete是对内存分配的运算符,取代了C中的malloc和free。 标准C++中的字符串类取代了C标准C函数库头文件中的字符数组处理函数。 C++中用来做控制态输入输出的iostream类库替代了标准C中的stdio函数库。 C++中的try/catch/throw异常处理机制取代了标准C中的setjmp()和longjmp()函数。 二、关键字和变量 C++相对与C增加了一些关键字,如下: typename bool dynamic_cast mutable namespace static_cast using catch explicit new virtual operator false private template volatile const protected this wchar_t const_cast public throw friend true reinterpret_cast try bitor xor_e and_eq compl or_eq not_eq bitand 在C++中还增加了bool型变量和wchar_t型变量: 布尔型变量是有两种逻辑状态的变量,它包含
两个
值:真和假。如果在表达式中使用了布尔型变量,那么将根据变量值的真假而赋予整型值1或0。要把
一个
整型变量转换成布尔型变量,如果整型值为0,则其布尔型值为假;反之如果整型值为非0,则其布尔型值为真。布儿型变量在运行时通常用做标志,比如进行逻辑测试以改变程序流程。 #include iostream.h int main() { bool flag; flag=true; if(flag) cout<
包括wchar_t
数据
类型,wchar_t也是字符类型,但是是那些宽度超过8位的
数据
类型。许多外文字符集所含的数目超过256个,char字符类型无法完全囊括。wchar_t
数据
类型一般为16位。 标准C++的iostream类库中
包括
了可以支持宽字符的类和
对象
。用wout替代cout即可。 #include iostream.h int main() { wchar_t wc; wc='b'; wout<
数据类型)。 三、强制类型转换 有时候,根据表达式的需要,某个
数据
需要被当成另外的
数据
类型来处理,这时,就需要强制编译器把变量或常数由声明时的类型转换成需要的类型。为此,就要使用强制类型转换说明,格式如下: int* iptr=(int*) &table; 表达式的前缀(int*)就是传统C风格的强制类型转换说明(typecast),又可称为强制转换说明(cast)。强制转换说明告诉编译器把表达式转换成指定的类型。有些情况下强制转换是禁用的,例如不能把
一个
结构类型转换成其他任何类型。数字类型和数字类型、指针和指针之间可以相互转换。当然,数字类型和指针类型也可以相互转换,但通常认为这样做是不安全而且也是没必要的。强制类型转换可以避免编译器的警告。 long int el=123; short i=(int) el; float m=34.56; int i=(int) m; 上面
两个
都是C风格的强制类型转换,C++还增加了一种转换方式,比较一下上面和下面这个书写方式的不同: long int el=123; short i=int (el); float m=34.56; int i=int (m); 使用强制类型转换的最大好处就是:禁止编译器对你故意去做的事发出警告。但是,
利用
强制类型转换说明使得编译器的类型检查机制失效,这不是明智的选择。通常,是不提倡进行强制类型转换的。除非不可避免,如要调用malloc()函数时要用的void型指针转换成指定类型指针。 四、标准输入输出流 在C语言中,输入输出是使用语句scanf()和printf()来
实现
的,而C++中是使用类来
实现
的。 #include iostream.h main() //C++中main()函数默认为int型,而C语言中默认为void型。 { int a; cout<>a; /*输入
一个
数值*/ cout<
对象,他们本身并不是C++语言的组成部分。虽然他们已经是ANSI标准C++中被定义,但是他们不是语言的内在组成部分。在C++中不提供内在的输入输出运算符,这与其他语言是不同的。输入和输出是通过C++类来
实现
的,cin和cout是这些类的实例,他们是在C++语言的外部
实现
。 在C++语言中,有了一种新的注释方法,就是‘//’,在该行//后的所有说明都被编译器认为是注释,这种注释不能换行。C++中仍然保留了传统C语言的注释风格/*……*/。 C++也可采用格式化输出的方法: #include iostream.h int main() { int a; cout<>a; cout<
一个或多个用不到的形式参数。这种情况多出现在用
一个
通用的函数指针调用多个函数的场合,其中有些函数不需要函数指针声明中的所有参数。看下面的例子: int fun(int x,int y) { return x*2; } 尽管这样的用法是正确的,但大多数C和C++的编译器都会给出
一个
警告,说参数y在程序中没有被用到。为了避免这样的警告,C++允许声明
一个
无名形参,以告诉编译器存在该参数,且调用者需要为其传递
一个
实际参数,但是函数不会用到这个参数。下面给出使用了无名参数的C++函数代码: int fun(int x,int) //注意不同点 { return x*2; } (二) 函数的默认参数 C++函数的原型中可以声明
一个
或多个带有默认值的参数。如果调用函数时,省略了相应的实际参数,那么编译器就会把默认值作为实际参数。可以这样来声明具有默认参数的C++函数原型: #include iostream.h void show(int=1,float=2.3,long=6); int main() { show(); show(2); show(4,5.6); show(8,12.34,50L); return 0; } void show(int first,float second,long third) { cout<
一个参数,而让编译器提供剩下的
两个
,第三次调用则提供了前面
两个
参数,编译器只需提供最后
一个
,最后
一个
调用则给出了所有
三个
参数,没有用到默认参数。 六、函数重载 在C++中,允许有相同的函数名,不过它们的参数类型不能完全相同,这样这些函数就可以相互区别开来。而这在C语言中是不允许的。 1.参数个数不同 #include iostream.h void a(int,int); void a(int); int main() { a(5); a(6,7); return 0; } void a(int i) { cout<
a; for(int i=1;i<=10;i++) //C语言中,不允许在这里定义变量 { static int a=0; //C语言中,同一函数块,不允许有同名变量 a+=i; cout<<::a<< <
size; int *array=new int[size]; for(int i=0;i
一个经常使用的概念。引用型变量是其他变量的
一个
别名,我们可以认为他们只是名字不相同,其他都是相同的。 1.引用是
一个
别名 C++中的引用是其他变量的别名。声明
一个
引用型变量,需要给他
一个
初始化值,在变量的生存周期内,该值不会改变。& 运算符定义了
一个
引用型变量: int a; int& b=a; 先声明
一个
名为a的变量,它还有
一个
别名b。我们可以认为是
一个
人,有
一个
真名,
一个
外号,以后不管是喊他a还是b,都是叫他这个人。同样,作为变量,以后对这
两个
标识符操作都会产生相同的效果。 #include iostream.h int main() { int a=123; int& b=a; cout<
两个函数没有明显区别,不过他们所花的时间却有很大差异,func2()函数所用的时间开销会比func2()函数少很多。它们还有
一个
差别,如果程序递归func1(),随着递归的深入,会因为栈的耗尽而崩溃,但func2()没有这样的担忧。 4.以引用方式调用 当函数把引用作为参数传递给另
一个
函数时,被调用函数将直接对参数在调用者中的拷贝进行操作,而不是产生
一个
局部的拷贝(传递变量本身是这样的)。这就称为以引用方式调用。把参数的值传递到被调用函数内部的拷贝中则称为以传值方式调用。 #include iostream.h void display(const
Date
&,const char*); void swapper(
Date
&,
Date
&); struct
Date
{ int month,day,year; }; int main() { static
Date
now={2,23,90}; static
Date
then={9,10,60}; display(now,Now: ); display(then,Then: ); swapper(now,then); display(now,Now: ); display(then,Then: ); return 0; } void swapper(
Date
& dt1,
Date
& dt2) {
Date
save; save=dt1; dt1=dt2; dt2=save; } void display(const
Date
& dt,const char *s) { cout<
Date { int month,day,year; };
Date
birthdays[]= { {12,12,60}; {10,25,85}; {5,20,73}; }; const
Date
& get
date
(int n) { return birthdays[n-1]; } int main() { int dt=1; while(dt!=0) { cout<
date # (1-3,0 to quit)<>dt; if(dt>0 && dt<4) { const
Date
& bd=get
date
(dt); cout<
设计,构造函数和析构函数 类是编程人员表达自定义
数据
类型的C++机制。它和C语言中的结构类似,C++类支持
数据
抽象和面向
对象
的程序
设计
,从某种意义上说,也就是
数据
类型的
设计
和
实现
。 一、类的
设计
1.类的声明 class 类名 { private: //
私有
... public: //公有 ... }; 2.类的
成员
一般在C++类中,所有定义的变量和函数都是类的
成员
。如果是变量,我们就叫它
数据
成员
如果是函数,我们就叫它
成员
函数。 3.类
成员
的可见性 private和public访问控制符决定了
成员
的可见性。由
一个
访问控制符设定的可访问状态将一直持续到下
一个
访问控制符出现,或者类声明的结束。
私有
成员
仅能被同
一个
类中的
成员
函数访问,公有
成员
既可以被同一类中的
成员
函数访问,也可以被其他已经实例化的类中函数访问。当然,这也有例外的情况,这是以后要讨论的友元函数。 类中默认的
数据
类型是private,结构中的默认类型是public。一般情况下,变量都作为
私有
成员
出现,函数都作为公有
成员
出现。 类中还有一种访问控制符protected,叫保护
成员
,以后再说明。 4.初始化 在声明
一个
类的
对象
时,可以用圆括号()包含
一个
初始化表。 看下面
一个
例子: #include iostream.h class Box { private: int height,width,depth; //3个
私有
数据
成员
public: Box(int,int,int); ~Box(); int volume(); //
成员
函数 }; Box::Box(int ht,int wd,int dp) { height=ht; width=wd; depth=dp; } Box::~Box() { //nothing } int Box::volume() { return height*width*depth; } int main() { Box thisbox(3,4,5); //声明
一个
类
对象
并初始化 cout<
一个类中没有private
成员
和protected
成员
时,也没有虚函数,并且不是从其他类中派生出来的,可以用{}来初始化。(以后再讲解) 5.内联函数 内联函数和普通函数的区别是:内联函数是在编译过程中展开的。通常内联函数必须简短。定义类的内联函数有两种方法:一种和C语言一样,在定义函数时使用关键字inline。如: inline int Box::volume() { return height*width*depth; } 还有一种方法就是直接在类声明的内部定义函数体,而不是仅仅给出
一个
函数原型。我们把上面的函数简化一下: #include iostream.h class Box { private: int height,width,depth; public: Box(int ht,int wd,int dp) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5); //声明
一个
类
对象
并初始化 cout<
两个函数都默认为内联函数了。 二、构造函数 什么是构造函数?通俗的讲,在类中,函数名和类名相同的函数称为构造函数。上面的Box()函数就是构造函数。C++允许同名函数,也就允许在
一个
类中有多个构造函数。如果
一个
都没有,编译器将为该类产生
一个
默认的构造函数,这个构造函数可能会完成一些工作,也可能什么都不做。 绝对不能指定构造函数的类型,即使是void型都不可以。实际上构造函数默认为void型。 当
一个
类的
对象
进入作用域时,系统会为其
数据
成员
分配足够的内存,但是系统不一定将其初始化。和内部
数据
类型
对象
一样,外部
对象
的
数据
成员
总是初始化为0。局部
对象
不会被初始化。构造函数就是被用来进行初始化工作的。当自动类型的类
对象
离开其作用域时,所站用的内存将释放回系统。 看上面的例子,构造函数Box()函数接受
三个
整型擦黑素,并把他们赋值给立方体
对象
的
数据
成员
。 如果构造函数没有参数,那么声明
对象
时也不需要括号。 1.使用默认参数的构造函数 当在声明类
对象
时,如果没有指定参数,则使用默认参数来初始化
对象
。 #include iostream.h class Box { private: int height,width,depth; public: Box(int ht=2,int wd=3,int dp=4) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5); //初始化 Box defaulbox; //使用默认参数 cout<
一个公共的默认构造函数,这个构造函数什么都不做。如果至少提供
一个
构造函数,则编译器就不会产生默认构造函数。 3.重载构造函数
一个
类中可以有多个构造函数。这些构造函数必须具有不同的参数表。在
一个
类中需要接受不同初始化值时,就需要编写多个构造函数,但有时候只需要
一个
不带初始值的空的Box
对象
。 #include iostream.h class Box { private: int height,width,depth; public: Box() { //nothing } Box(int ht=2,int wd=3,int dp=4) { height=ht; width=wd; depth=dp; } ~Box(); int volume() { return height*width*depth; } }; int main() { Box thisbox(3,4,5); //初始化 Box otherbox; otherbox=thisbox; cout<
两个构造函数
一个
没有初始化值,
一个
有。当没有初始化值时,程序使用默认值,即2,3,4。 但是这样的程序是不好的。它允许使用初始化过的和没有初始化过的Box
对象
,但它没有考虑当thisbox给otherbox赋值失败后,volume()该返回什么。较好的方法是,没有参数表的构造函数也把默认值赋值给
对象
。 class Box { int height,width,depth; public: Box() { height=0;width=0;depth=0; } Box(int ht,int wd,int dp) { height=ht;width=wd;depth=dp; } int volume() { return height*width*depth; } }; 这还不是最好的方法,更好的方法是使用默认参数,根本不需要不带参数的构造函数。 class Box { int height,width,depth; public: Box(int ht=0,int wd=0,int dp=0) { height=ht;width=wd;depth=dp; } int volume() { return height*width*depth; } }; 三、析构函数 当
一个
类的
对象
离开作用域时,析构函数将被调用(系统自动调用)。析构函数的名字和类名一样,不过要在前面加上 ~ 。对
一个
类来说,只能允许
一个
析构函数,析构函数不能有参数,并且也没有返回值。析构函数的作用是完成
一个
清理工作,如释放从堆中分配的内存。 我们也可以只给出析构函数的形式,而不给出起具体函数体,其效果是一样的,如上面的例子。但在有些情况下,析构函数又是必需的。如在类中从堆中分配了内存,则必须在析构函数中释放 主体:(三)类的转换 C++的内部
数据
类型遵循隐式类型转换规则。假设某个表达市中使用了
一个
短整型变量,而编译器根据上下文认为这儿需要是的长整型,则编译器就会根据类型转换规则自动把它转换成长整型,这种隐式转换出现在赋值、参数传递、返回值、初始化和表达式中。我们也可以为类提供相应的转换规则。 对
一个
类建立隐式转换规则需要构造
一个
转换函数,该函数作为类的
成员
,可以把该类的
对象
和其他
数据
类型的
对象
进行相互转换。声明了转换函数,就告诉了编译器,当根据句法判定需要类型转换时,就调用函数。 有两种转换函数。一种是转换构造函数;另一种是
成员
转换函数。需要采用哪种转换函数取决于转换的方向。 一、转换构造函数 当
一个
构造函数仅有
一个
参数,且该参数是不同于该类的
一个
数据
类型,这样的构造函数就叫转换构造函数。转换构造函数把别的
数据
类型的
对象
转换为该类的
一个
对象
。和其他构造函数一样,如果声明类的
对象
的初始化表同转换构造函数的参数表相匹配,该函数就会被调用。当在需要使用该类的地方使用了别的
数据
类型,便宜器就会调用转换构造函数进行转换。 #include iostream.h #include time.h #include stdio.h class
Date
{ int mo, da, yr; public:
Date
(time_t); void display(); }; void
Date
::display() { char year[5]; if(yr<10) sprintf(year,0%d,yr); else sprintf(year,%d,yr); cout<
tm_mon+1; yr=tim->tm_year; if(yr>=100) yr-=100; } int main() { time_t now=time(0);
Date
dt(now); dt.display(); return 0; } 本程序先调用time()函数来获取当前时间,并把它赋给time_t
对象
;然后程序通过调用
Date
类的转换构造函数来创建
一个
Date
对象
,该
对象
由time_t
对象
转换而来。time_t
对象
先传递给localtime()函数,然后返回
一个
指向tm结构(time.h文件中声明)的指针,然后构造函数把结构中的
日
月
年
的数值拷贝给
Date
对象
的
数据
成员
,这就完成了从time_t
对象
到
Date
对象
的转换。 二、
成员
转换函数
成员
转换函数把该类的
对象
转换为其他
数据
类型的
对象
。在
成员
转换函数的声明中要用到关键字operator。这样声明
一个
成员
转换函数: operator aaa(); 在这个例子中,aaa就是要转换成的
数据
类型的说明符。这里的类型说明符可以是任何合法的C++类型,
包括
其他的类。如下来定义
成员
转换函数; Classname::operator aaa() 类名标识符是声明了该函数的类的类型说明符。上面定义的
Date
类并不能把该类的
对象
转换回time_t型变量,但可以把它转换成
一个
长整型值,计算从2000
年
1
月
1
日
到现在的
天数
。 #include iostream.h class
Date
{ int mo,da,yr; public:
Date
(int m,int d,int y) {mo=m; da=d; yr=y;} operator int(); //声明 };
Date
::operator int() //定义 { static int dys[]={31,28,31,30,31,30,31,31,30,31,30,31}; int days=yr-2000; days*=365; days+=(yr-2000)/4; for(int i=0;i
Date now(12,24,2003); int since=now; cout<
两个例子都是C++类
对象
和内部
数据
对象
之间的相互转换。也可以定义转换函数来
实现
两个
类
对象
之间的相互转换。 #include iostream.h class Custom
Date
{ public: int da, yr; Custom
Date
(int d=0,int y=0) {da=d; yr=y;} void display() { cout<
Date { int mo, da, yr; public:
Date
(int m=0,int d=0,int y=0) {mo=m; da=d; yr=y;}
Date
(const Custom
Date
&); //转换构造函数 operator Custom
Date
(); //
成员
转换函数 void display() { cout<
Date::
Date
(const Custom
Date
& jd) { yr=jd.yr; da=jd.da; for(mo=0;modys[mo]) da-=dys[mo]; else break; mo++; }
Date
::operator Custom
Date
() { Custom
Date
cd(0,yr); for(int i=0;i
Date dt(12,24,3); Custom
Date
cd; cd = dt; //调用
成员
转换函数 cd.display(); dt = cd; //调用转换构造函数 dt.display(); return 0; } 这个例子中有
两个
类Custom
Date
和
Date
,Custom
Date
型
日
期
包含
年
份和
天数
。 这个例子没有考虑闰
年
情况。但是在实际构造
一个
类时,应该考虑到所有问题的可能性。 在
Date
里中具有两种转换函数,这样,当需要从
Date
型变为Custom
Date
型十,可以调用
成员
转换函数;反之可以调用转换构造函数。 不能既在
Date
类中定义
成员
转换函数,又在Custom
Date
类里定义转换构造函数。那样编译器在进行转换时就不知道该调用哪
一个
函数,从而出错。 四、转换函数的调用 C++里调用转换函数有三种形式:第一种是隐式转换,例如编译器需要
一个
Date
对象
,而程序提供的是Custom
Date
对象
,编译器会自动调用合适的转换函数。另外两种都是需要在程序代码中明确给出的显式转换。C++强制类型转换是一种,还有一种是显式调用转换构造函数和
成员
转换函数。下面的程序给出了三中转换形式: #include iostream.h class Custom
Date
{ public: int da, yr; Custom
Date
(int d=0,int y=0) {da=d; yr=y;} void display() { cout<
Date { int mo, da, yr; public:
Date
(int m,int d,int y) { mo=m; da=d; yr=y; } operator Custom
Date
(); };
Date
::operator Custom
Date
() { static int dys[]={31,28,31,30,31,30,31,31,30,31,30,31}; Custom
Date
cd(0,yr); for(int i=0;i
Date dt(11,17,89); Custom
Date
cd; cd = dt; cd.display(); cd = (Custom
Date
) dt; cd.display(); cd = Custom
Date
(dt); cd.display(); return 0; } 五、转换发生的情形 上面的几个例子都是通过不能类型
对象
之间的相互赋值来调用转换函数,还有几种调用的可能: 参数传递 初始化 返回值 表达式语句 这些情况下,都有可能调用转换函数。 下面的程序不难理解,就不分析了。 #include iostream.h class Custom
Date
{ public: int da, yr; Custom
Date
() {} Custom
Date
(int d,int y) { da=d; yr=y;} void display() { cout<
Date { int mo, da, yr; public:
Date
(int m,int d,int y) { mo=m; da=d; yr=y; } operator Custom
Date
(); };
Date
::operator Custom
Date
() { static int dys[]={31,28,31,30,31,30,31,31,30,31,30,31}; Custom
Date
cd(0,yr); for (int i=0;i
Date cd; public: explicit Tester(Custom
Date
c) { cd=c; } void display() { cd.display(); } }; void disp
date
(Custom
Date
cd) { cd.display(); } Custom
Date
rtn
date
() {
Date
dt(9,11,1); return dt; } int main() {
Date
dt(12,24,3); Custom
Date
cd; cd = dt; cd.display(); disp
date
(dt); Tester ts(dt); ts.display(); cd = rtn
date
(); cd.display(); return 0; } 六、显式构造函数 注意上面Tester类的构造函数前面有
一个
explicit修饰符。如果不加上这个关键字,那么在需要把Custom
Date
对象
转换成Tester
对象
时,编译器会把该函数当作转换构造函数来调用。但是有时候,并不想把这种只有
一个
参数的构造函数用于转换目的,而仅仅希望用它来显式地初始化
对象
,此时,就需要在构造函数前加explicit。如果在声明了Tester
对象
以后使用了下面的语句将导致
一个
错误: ts=jd; //error 这个错误说明,虽然Tester类中有
一个
以
Date
型变量为参数的构造函数,编译器却不会把它看作是从
Date
到Tester的转换构造函数,因为它的声明中包含了explicit修饰符。 七、表达式内部的转换 在表达式内部,如果发现某个类型和需要的不一致,就会发生错误。数字类型的转换是很简单,这里就不举例了。下面的程序是把
Date
对象
转换成长整型值。 #include iostream.h class
Date
{ int mo, da, yr; public:
Date
(int m,int d,int y) { mo=m; da=d; yr=y; } operator long(); };
Date
::operator long() { static int dys[]={31,28,31,30,31,30,31,31,30,31,30,31}; long days=yr; days*=365; days+=(yr-1900)/4; //从1900
年
1
月
1
日
开始计算 for(int i=0;i
Date today(12,24,2003); const long ott=123; long sum=ott+today; cout<
对象可以转换成某个数字类型,或者表达式调用了作用于某个类的重载运算符时,就会发生隐式转换。
运算符重载
以后再学习。 主体:(四)
私有
数据
成员
和友元 一、
私有
数据
成员
的使用 1.取值和赋值
成员
函数 面向
对象
的约定就是保证所有
数据
成员
的
私有
性。一般我们都是通过公有
成员
函数来作为公共接口来读取
私有
数据
成员
的。某些时候,我们称这样的函数为取值和赋值函数。 取值函数的返回值和传递给赋值函数的参数不必一一匹配所有
数据
成员
的类型。 #include iostream.h class
Date
{ int mo, da, yr; public:
Date
(int m,int d,int y) { mo=m; da=d; yr=y; } int getyear() const { return yr; } void setyear(int y) { yr = y; } }; int main() {
Date
dt(4,1,89); cout<
成员函数来访问和改变类中的
数据
。这样有利于软件的
设计
和维护。比如,改变
Date
类内部
数据
的形式,但仍然用修改过的getyear()和setyear()来提供访问接口,那么使用该类就不必修改他们的代码,仅需要重新编译程序即可。 2.
常量
成员
函数 注意上面的程序中getyear()被声明为
常量
型,这样可以保证该
成员
函数不会修改调用他的
对象
。通过加上const修饰符,可以使访问
对象
数据
的
成员
函数仅仅完成不会引起
数据
变动的那些操作。 如果程序声明某个
Date
对象
为
常量
的话,那么该
对象
不得调用任何非
常量
型
成员
函数,不论这些函数是否真的试图修改
对象
的
数据
。只有把那些不会引起
数据
改变的函数都声明为
常量
型,才可以让
常量
对象
来调用。 3.改进的
成员
转换函数 下面的程序改进了从
Date
对象
到Custom
Date
对象
的
成员
转换函数,用取值和赋值函数取代了使用公有
数据
成员
的做法。(以前的程序代码在上一帖中) #include iostream.h class Custom
Date
{ int da,yr; public: Custom
Date
() {} Custom
Date
(int d,int y) { da=d; yr=y; } void display() const {cout<
Date { int mo,da,yr; public:
Date
(int m,int d,int y) { mo=m; da=d; yr=y; } operator Custom
Date
() const; };
Date
::operator Custom
Date
() const { static int dys[] = {31,28,31,30,31,30,31,31,30,31,30,31}; Custom
Date
cd(0,yr); int day=da; for(int i=0;i
Date dt(11,17,89); Custom
Date
cd; cd=dt; cd.display(); return 0; } 注意上面的程序中
Date
::operator Custom
Date
()声明为
常量
型,因为这个函数没有改变调用它
对象
的
数据
,尽管它修改了
一个
临时Custom
Date
对象
并将其作为函数返回值。 二、友元 前面已经说过了,
私有
数据
成员
不能被类外的其他函数读取,但是有时候类会允许一些特殊的函数直接读写其
私有
数据
成员
。 关键字friend可以让特定的函数或者别的类的所有
成员
函数对
私有
数据
成员
进行读写。这既可以维护
数据
的
私有
性,有可以保证让特定的类或函数能够直接访问
私有
数据
。 1.友元类
一个
类可以声明另
一个
类为其友元,这个友元的所有
成员
函数都可以读写它的
私有
数据
。 #include iostream.h class
Date
; class Custom
Date
{ int da,yr; public: Custom
Date
(int d=0,int y=0) { da=d; yr=y; } void display() const {cout<
Date; //这儿 }; class
Date
{ int mo,da,yr; public:
Date
(int m,int d,int y) { mo=m; da=d; yr=y; } operator Custom
Date
(); };
Date
::operator Custom
Date
() { static int dys[] = {31,28,31,30,31,30,31,31,30,31,30,31}; Custom
Date
cd(0, yr); for (int i=0;i
Date dt(11,17,89); Custom
Date
cd(dt); cd.display(); return 0; } 在上面的程序中,有这样一句 friend
Date
; 该语句告诉编译器,
Date
类的所有
成员
函数有权访问Custom
Date
类的
私有
成员
。因为
Date
类的转换函数需要知道Custom
Date
类的每个
数据
成员
,所以真个
Date
类都被声明为Custom
Date
类的友元。 2.隐式构造函数 上面程序对Custom
Date
的构造函数的调用
私有
显示该类需要如下的
一个
转换构造函数: Custom
Date
(
Date
& dt); 但是唯一的
一个
构造函数是:Custom
Date
(int d=0;int y=0); 这就出现了问题,编译器要从
Date
对象
构造
一个
Custom
Date
对象
,但是Custom
Date
类中并没有定义这样的转换构造函数。不过
Date
类中定义了
一个
成员
转换函数,它可以把
Date
对象
转换成Custom
Date
对象
。于是编译器开始搜索Custom
Date
类,看其是否有
一个
构造函数,能从
一个
已存在的Custom
Date
的
对象
创建新的Custom
Date
对象
。这种构造函数叫拷贝构造函数。拷贝构造函数也只有
一个
参数,该参数是它所属的类的
一个
对象
,由于Custom
Date
类中没有拷贝构造函数,于是编译器就会产生
一个
默认的拷贝构造函数,该函数简单地把已存在的
对象
的每个
成员
拷贝给新
对象
。现在我们已经知道,编译器可以把
Date
对象
转换成Custom
Date
对象
,也可以从已存在的Custom
Date
对象
生成
一个
新的Custom
Date
对象
。那么上面提出的问题,编译器就是这样做的:它首先调用转换函数,从
Date
对象
创建
一个
隐藏的、临时的、匿名的Custom
Date
对象
,然后用该临时
对象
作为参数调用默认拷贝构造函数,这就生成了
一个
新的Custom
Date
对象
。 3.预引用 上面的例子中还有这样一句 class
Date
; 这个语句叫做预引用。它告诉编译器,类
Date
将在后面定义。编译器必须知道这个信号,因为Custom
Date
类中引用了
Date
类,而
Date
里也引用了Custom
Date
类,必须首先声明其中之一。 使用了预引用后,就可以声明未定义的类的友元、指针和引用。但是不可以使用那些需要知道预引用的类的定义细节的语句,如声明该类的
一个
实例或者任何对该类
成员
的引用。 4.显式友元预引用 也可以不使用预引用,这只要在声明友元的时候加上关键自class就行了。 #include iostream.h class Custom
Date
{ int da,yr; public: Custom
Date
(int d=0,int y=0) { da=d; yr=y; } void display() const {cout<
Date; //这儿,去掉前面的预引用 }; class
Date
{ ... ... };
Date
::operator Custom
Date
() { ... ... } int main() { ... ... } 5.友元函数 通常,除非真的需要,否则并不需要把整个类都设为另
一个
类的友元,只需挑出需要访问当前类
私有
数据
成员
的
成员
函数,将它们设置为该类的友元即可。这样的函数称为友元函数。 下面的程序限制了Custom
Date
类
数据
成员
的访问,
Date
类中只有需要这些
数据
的
成员
函数才有权读写它们。 #include iostream.h class Custom
Date
; class
Date
{ int mo,da,yr; public:
Date
(const Custom
Date
&); void display() const {cout<
Date { int da,yr; public: Custom
Date
(int d=0,int y=0) { da=d; yr=y; } friend
Date
::
Date
(const Custom
Date
&); };
Date
::
Date
(const Custom
Date
& cd) { static int dys[] = {31,28,31,30,31,30,31,31,30,31,30,31}; yr=cd.yr; da=cd.da; for(mo=0;modys[mo]) da-=dys[mo]; else break; mo++; } int main() {
Date
dt(Custom
Date
(123, 89)); dt.display(); return 0; } 6.匿名
对象
上面main()函数中
Date
对象
调用Custom
Date
类的构造函数创建了
一个
匿名Custom
Date
对象
,然后用该
对象
创建了
一个
Date
对象
。这种用法在C++中是经常出现的。 7.非类
成员
的友元函数 有时候友元函数未必是某个类的
成员
。这样的函数拥有类
对象
私有
数据
成员
的读写权,但它并不是任何类的
成员
函数。这个特性在重载运算符时特别有用。 非类
成员
的友元函数通常被用来做为类之间的纽带。
一个
函数如果被
两个
类同时声明为友元,它就可以访问这
两个
类的
私有
成员
。下面的程序说明了
一个
可以访问
两个
类
私有
数据
成员
的友元函数是如何将在
两个
类之间架起桥梁的。 #include iostream.h class Time; class
Date
{ int mo,da,yr; public:
Date
(int m,int d,int y) { mo=m; da=d; yr=y;} friend void display(const
Date
&, const Time&); }; class Time { int hr,min,sec; public: Time(int h,int m,int s) { hr=h; min=m; sec=s;} friend void display(const
Date
&, const Time&); }; void display(const
Date
& dt, const Time& tm) { cout << dt.mo << '/' << dt.da << '/' << dt.yr; cout << ' '; cout << tm.hr << ':' << tm.min << ':' << tm.sec; } int main() {
Date
dt(2,16,97); Time tm(10,55,0); display(dt, tm); return 0; } 主体:(五)析构函数和this指针 一、析构函数 前面的一些例子都没有说明析构函数,这是因为所用到的类在结束时不需要做特别的清理工作。下面的程序给出了一新的
Date
类,其中
包括
一个
字符串指针,用来表示
月
份。 #include iostream.h #include string.h class
Date
{ int mo,da,yr; char *month; public:
Date
(int m=0, int d=0, int y=0); ~
Date
(); void display() const; };
Date
::
Date
(int m,int d,int y) { static char *mos[] = { January,February,March,April,May,June, July,August,September,October,November,December }; mo=m; da=d; yr=y; if(m!=0) { month=new char[strlen(mos[m-1])+1]; strcpy(month, mos[m-1]); } else month = 0; }
Date
::~
Date
() { delete [] month; } void
Date
::display() const { if(month!=0) cout<
Date birthday(8,11,1979); birthday.display(); return 0; } 在
Date
对象
的构造函数中,首先用new运算符为字符串month动态分配了内存,然后从内部数组中把
月
份的名字拷贝给字符串指针month。 析构函数在删除month指针时,可能会出现一些问题。当然从这个程序本身来看,没什么麻烦;但是从
设计
一个
类的角度来看,当
Date
类用于赋值时,就会出现问题。假设上面的main()修改为“ int main() {
Date
birthday(8,11,1979);
Date
today; today=birthday; birthday.display(); return 0; } 这会生成
一个
名为today的空的
Date
型变量,并且把birthday值赋给它。如果不特别通知编译器,它会简单的认为类的赋值就是
成员
对
成员
的拷贝。在上面的程序中,变量birthday有
一个
字符型指针month,并且在构造函数里用new运算符初始化过了。当birthday离开其作用域时,析构函数会调用delete运算符来释放内存。但同时,当today离开它的作用域时,析构函数同样会对它进行释放操作,而today里的month指针是birthday里的month指针的
一个
拷贝。析构函数对同一指针进行了两次删除操作,这会带来不可预知的后果。 如果假设today是
一个
外部变量,而birthday是
一个
自变量。当birthday离开其作用域时,就已经把
对象
today里的month指针删除了。显然这也是不正确的。 再假设有
两个
初始化的
Date
变量,把其中
一个
的值赋值给另
一个
:
Date
birthday(8,11,1979);
Date
today(12,29,2003); today=birthday; 问题就更复杂了,当这
两个
变量离开作用域时,birthday中的month的值已经通过赋值传递给了today。而today中构造函数用new运算符给month的值却因为赋值被覆盖了。这样,birthday中的month被删除了两次,而today中month却没有被删除掉。 二、重载赋值运算符 为了解决上面的问题,我们应该写
一个
特殊的赋值运算符函数来处理这类问题。当需要为同
一个
类的
两个
对象
相互赋值时,就可以重载运算符函数。这个方法可以解决类的赋值和指针的释放。 下面的程序中,类中的赋值函数用new运算符从堆中分配了
一个
不同的指针,该指针获取赋值
对象
中相应的值,然后拷贝给接受赋值的
对象
。 在类中重载赋值运算符的格式如下: void operator = (const
Date
&) 后面我们回加以改进。目前,重载的运算符函数的返回类型为void。它是类总的
成员
函数,在本程序红,是
Date
类的
成员
函数。它的函数名始终是operator =,参数也始终是同
一个
类的
对象
的引用。参数表示的是源
对象
,即赋值
数据
的提供者。重载函数的运算符作为目标
对象
的
成员
函数来使用。 #include iostream.h #include string.h class
Date
{ int mo,da,yr; char *month; public:
Date
(int m=0, int d=0, int y=0); ~
Date
(); void operator=(const
Date
&); void display() const; };
Date
::
Date
(int m, int d, int y) { static char *mos[] = { January,February,March,April,May,June, July,August,September,October,November,December }; mo = m; da = d; yr = y; if (m != 0) { month = new char[strlen(mos[m-1])+1]; strcpy(month, mos[m-1]); } else month = 0; }
Date
::~
Date
() { delete [] month; } void
Date
::display() const { if (month!=0) cout<
Date::operator=(const
Date
& dt) { if (this != &dt) { mo = dt.mo; da = dt.da; yr = dt.yr; delete [] month; if (dt.month != 0) { month = new char [std::strlen(dt.month)+1]; std::strcpy(month, dt.month); } else month = 0; } } int main() {
Date
birthday(8,11,1979); birthday.display();
Date
newday(12,29,2003); newday.display(); newday = birthday; newday.display(); return 0; } 除了为
Date
类加入了
一个
重载运算符函数,这个程序和上面的
一个
程序是相同的。赋值运算符函数首先取得所需的
数据
,然后用delete把原来的month指针所占用的内存返还给堆。接着,如果源
对象
的month指针已经初始化过,就用new运算符为
对象
重新分配内存,并把源
对象
的month字符串拷贝给接受方。 重载的
Date
类赋值运算符函数的第
一个
语句比较了源
对象
的地址和this指针。这个操作取保
对象
不会自己给自己赋值。 三、this指针 this指针是
一个
特殊的指针,当类的某个非静态的
成员
函数在执行时,就会存在this指针。它指向类的
一个
对象
,且这个
对象
的某个
成员
函数正在被调用。 this指针的名字始终是this,而且总是作为隐含参数传递给每
一个
被声明的
成员
函数,例如: void
Date
::myFunc(
Date
* this); 实际编程时函数的声明不需要包含这个参数。 当程序中调用某个
对象
的
成员
函数时,编译器会把该
对象
的地址加入到参数列表中,感觉上就好象函数采用了上面所示的声明,并且是用如下方式来调用的: dt.myFunc(& dt); 静态
成员
函数不存在this指针。 当调用某个
对象
的
成员
函数时,编译器把
对象
的地址传递给this指针,然后再调用该函数。因此,
成员
函数你对任何
成员
的调用实际上都隐式地使用了this指针。 1.以this指针作为返回值 使用this指针可以允许
成员
函数返回调用
对象
给调用者。前面的程序中重载赋值运算符没有返回值,因此不能用如下的形式对字符串进行赋值: a=b=c; 为了使重载的类赋值机制也能这样方便,必须让赋值函数返回赋值的结果,在这里就是目标
对象
。当赋值函数执行时,其返回值也恰好是this指针所指的内容。 下面的程序对前面那个程序进行了修改,让重载赋值运算符返回了
一个
Date
对象
的引用。 #include iostream.h #include string.h class
Date
{ int mo,da,yr; char *month; public:
Date
(int m=0, int d=0, int y=0); ~
Date
(); void operator=(const
Date
&); void display() const; };
Date
::
Date
(int m, int d, int y) { static char *mos[] = { January,February,March,April,May,June, July,August,September,October,November,December }; mo = m; da = d; yr = y; if (m != 0) { month = new char[strlen(mos[m-1])+1]; strcpy(month, mos[m-1]); } else month = 0; }
Date
::~
Date
() { delete [] month; } void
Date
::display() const { if (month!=0) cout<
Date::operator=(const
Date
& dt) { if (this != &dt) { mo = dt.mo; da = dt.da; yr = dt.yr; delete [] month; if (dt.month != 0) { month = new char [std::strlen(dt.month)+1]; std::strcpy(month, dt.month); } else month = 0; } return *this; } int main() {
Date
birthday(8,11,1979);
Date
oldday,newday; oldday=newday=birthday; birthday.display(); oldday.display(); newday.display(); return 0; } 2.在链表中使用this指针 在应用程序中,如果
数据
结构里有指向自身类型的
成员
,那么使用this指针会提供更多的方便。下面的程序中建立了
一个
类ListEntry的链表。 #include iostream.h #include string.h class ListEntry { char* listvalue; ListEntry* preventry; public: ListEntry(char*); ~ListEntry() { delete [] listvalue; } ListEntry* PrevEntry() const { return preventry; }; void display() const { cout<
name; if (strncmp(name, end, 3) == 0) break; ListEntry* list = new ListEntry(name); if (prev != 0) prev->AddEntry(*list); prev = list; } while (prev != 0) { prev->display(); ListEntry* hold = prev; prev = prev->PrevEntry(); delete hold; } return 0; } 程序运行时,会提示输入一串姓名,当输入完毕后,键入end,然后程序会逆序显示刚才输入的所有姓名。 程序中ListEntry类含有
一个
字符串和
一个
指向前
一个
表项的指针。构造函数从对中获取内存分配给字符串,并把字符串的内容拷贝到内存,然后置链接指针为NULL。析构函数将释放字符串所占用的内存。
成员
函数PrevEntry()返回指向链表前
一个
表项的指针。另
一个
成员
函数显示当前的表项内容。
成员
函数AddEntry(),它把this指针拷贝给参数的preventry指针,即把当前表项的地址赋值给下
一个
表项的链接指针,从而构造了
一个
链表。它并没有改变调用它的listEntry
对象
的内容,只是把该
对象
的地址赋给函数的参数所引用的那个ListEntry
对象
的preventry指针,尽管该函数不会修改
对象
的
数据
,但它并不是
常量
型。这是因为,它拷贝
对象
的地址this指针的内容给
一个
非长
常量
对象
,而编译器回认为这个非
常量
对象
就有可能通过拷贝得到的地址去修改当前
对象
的
数据
,因此AddEntry()函数在声明时不需要用const。 主体:(六)类
对象
数组和静态
成员
一、类
对象
数组 类的
对象
和C++其他
数据
类型一样,也可以为其建立数组,数组的表示方法和结构一样。 #include iostream.h class
Date
{ int mo,da,yr; public:
Date
(int m=0,int d=0, int y=0) { mo=m; da=d; yr=y;} void display() const { cout<
Date
date
s[2];
Date
today(12,31,2003);
date
s[0]=today;
date
s[0].display();
date
s[1].display(); return 0; } 1.类
对象
数组和默认构造函数 在前面已经说过,不带参数或者所有参数都有默认值的构造函数叫做默认构造函数。如果类中没有构造函数,编译器会自动提供
一个
什么都不做的公共默认构造函数 。如果类当中至少有
一个
构造函数,编译器就不会提供默认构造函数。 如果类当中不含默认构造函数,则无法实例化其
对象
数组。因为实例花类
对象
数组的格式不允许用初始化值来匹配某个构造函数的参数表。 上面的程序中,main()函数声明了
一个
长度为2的
Date
对象
数组,还有
一个
包含初始化值的单个
Date
对象
。接着把这个初始化的
Date
对象
赋值给数组中第
一个
对象
,然后显示
两个
数组元素中包含的
日
期
。从输出中可以看到,第
一个
日
期
是有效
日
期
,而第二个显示的都是0。 当声明了某个类的
对象
数组时,编译器会为每个元素都调用默认构造函数。 下面的程序去掉了构造函数的默认参数值,并且增加了
一个
默认构造函数。 #include class
Date
{ int mo, da, yr; public:
Date
();
Date
(int m,int d,int y) { mo=m; da=d; yr=y;} void display() const { cout <
Date::
Date
() { cout <<
Date
constructor running<
Date
date
s[2];
Date
today(12,31,2003);
date
s[0]=today;
date
s[0].display();
date
s[1].display(); return 0; } 运行程序,输出为:
Date
constructor running
Date
constructor running 12/31/2003 0/0/0 从输出中可以看出,
Date
()这个默认构造函数被调用了两次。 2.类
对象
数组和析构函数 当类
对象
离开作用域时,编译器会为每个
对象
数组元素调用析构函数。 #include iostream.h class
Date
{ int mo,da,yr; public:
Date
(int m=0,int d=0,int y=0) { mo=m; da=d; yr=y;} ~
Date
() {cout<<
Date
destructor running<
Date
date
s[2];
Date
today(12,31,2003);
date
s[0]=today;
date
s[0].display();
date
s[1].display(); return 0; } 运行程序,输出为: 12/31/2003 0/0/0
Date
destructor running
Date
destructor running
Date
destructor running 表明析构函数被调用了三次,也就是
date
s[0],
date
s[1],today这
三个
对象
离开作用域时调用的。 二、静态
成员
可以把类的
成员
声明为静态的。静态
成员
只能存在唯一的实例。所有的
成员
函数都可以访问这个静态
成员
。即使没有声明类的任何实例,静态
成员
也已经是存在的。不过类当中声明静态
成员
时并不能自动定义这个变量,必须在类定义之外来定义该
成员
。 1.静态
数据
成员
静态
数据
成员
相当于
一个
全局变量,类的所有实例都可以使用它。
成员
函数能访问并且修改这个值。如果这个静态
成员
是公有的,那么类的作用域之内的所有代码(不论是在类的内部还是外部)都可以访问这个
成员
。下面的程序通过静态
数据
成员
来记录链表首项和末项的地址。 #include iostream.h #include string.h class ListEntry { public: static ListEntry* firstentry; private: static ListEntry* lastentry; char* listvalue; ListEntry* nextentry; public: ListEntry(char*); ~ListEntry() { delete [] listvalue;} ListEntry* NextEntry() const { return nextentry; }; void display() const { cout<
name
零基础Java入门课程-类和
对象
该课程由付强老师主讲,系统的、全面的、具体的讲解了java入门的知识。可以使初级的学员入门。Java入门Java的历史、Java的版本、Java的优势、软件行业前景Java开发环境搭建、编写Java入门练习虚拟机的运行机制、Java的平台无关性、虚拟机垃圾回收机制基础语法关键字、标识符、Java
数据
类型分类、基础
数据
类型、取值范围变量、
常量
、三种注释、生成doc文档、运算符、表达式if语句、switch语句、嵌套判断for语句、while语句、do-while语句、嵌套循环类和
对象
面向过程和面向
对象
的区别类的构成、访问修饰符、
对象
的内存分配this关键字、按值传递与按引用传递、
对象
的初始化顺序高级类特性类的继承、父子类的初始化顺序、单继承性方法的覆盖、重载、super关键字、多态、instanceof关键字、强制类型转换、static关键字、final关键字数组和枚举一维数组的应用及内存分配多维数组的应用及内存分配数组的复制、数组的按引用传递排序算法:冒泡、直接选择、插入选择、希尔、快速、归并、堆排序Arrays工具类的应用枚举类型的应用常见类的使用Object类的底层及应用、Objects类String类的底层及应用、正则表达式StringBuffer和StringBuilder的底层及应用Math类的应用、包装类的应用
日
期
类
的应用:
Date
、
Date
Format、Calendar、Local
Date
Time、Instant、Local
Date
、MonthDay、Zoned
Date
Time、YearMonth、新旧
日
期
转换BigInteger和BingDecimal、DecimalFormatSystem类、Scanner类抽象类和接口抽象类的规则及应用接口的规则及应用默认方法、静态方法、函数式接口、Lambda表达式异常异常的定义异常的处理:抓(try-catch-finally)、抛(throws)异常的分类、自定义异常的应用、throw关键字集合框架集合框架结构:接口、
实现
类Collection接口的方法、Set接口的方法、List接口的方法、Map接口的方法Array、Linked、Hash、Tree底层
实现
原理泛型的作用、Collections工具类、历史集合类I/O流Path类的原理及应用、Files类的原理及应用文件字节流FileInputStream的原理及应用
对象
类、缓冲流、
数据
流的原理及应用字符流的原理及应用多线程进程与线程的概念、查看线程
对象
Java内存模型线程的创建与启动:扩展Thread类、
实现
Runnable接口、
实现
Callable接口、线程池线程状态的转换:新建、就绪、运行、阻塞、死亡线程的调度:sleep、yield、join、interrupt、wait后台线程、定时任务线程的并发与同步、同步锁、同步块、线程安全的类Lock接口、CAS、volatile修饰符内部类
成员
内部类、本地内部类的应用匿名内部类的原理及引用、Lambda表达式
设计
模式基础
设计
模式概念、框架概念7大
设计
原则:开闭原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特原则、里氏替换原则、合成复用原则单例模式、工厂模式、模板模式、代理模式、装饰模式、适配器模式、外观模式、策略模式、观察者模式、命令模式、备忘录模式、观察者模式反射反射包Class类的使用反射获取类属性、方法、构造器通过反射创建类
对象
/通过反射调用方法反射的应用
Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册
面向
对象
编程的基础 要了解面向
对象
编程(OOP)的基本概念,需要理解 OOP 的
三个
主要概念,它们撑起 了整个 OOP 的框架。这
三个
概念是:封装、继承性和多态性。除此以外,还需了解
对象
、 类、消息、接口、及抽象等概念。 2.2.1
对象
现实世界中的
对象
具
两个
特征:状态和行为。例如:自行车有状态(传动装置、步度、
两个
车轮和齿轮的数目等)和行为(刹车、加速、减速和换档等)。 其次,我们再来看看软件
对象
。软件
对象
是现实世界
对象
的模式化产物,他们也有状态 和行为。软件
对象
把状态用
数据
表示并存放在变量里,而行为则用方法
实现
。实际上,软件
对象
还
包括
了
数据
结构和使用这些
数据
结构的代码。因此也可以说:软件
对象
是现实世界客 观事务的软件化模拟,是变量(
数据
和
数据
结构)和相关方法(对
数据
操作和
对象
管理的程 序)的软件组合。 在面向
对象
的程序
设计
中,你可以用软件
对象
表示现实世界的
对象
,而这些软件
对象
和 现实世界
对象
是相对应的。例如:如果你正在建立
一个
帐户管理系统,那么你的
对象
就是帐 户、欠款、信用卡、
月
收入、贷款、交易等等。如果你
设计
一个
电子实习交通工具系统,那 么你的
对象
就是汽车、摩托车、自行车等等。就自行车的软件
对象
而言,表示该
对象
的状态 和行为应为与变量和方法相对应。自行车的状态:数度是 10mp(每小时 10 米),步度是 90rpm (每分钟 90 转),当前传动装置是第 5 个齿轮。再面向
对象
的程序
设计
中,这些
数据
应放在 变量中。自行车的行为:刹车,改变步度和换档。在面向
对象
的程序
设计
中,这些行为用方 法
实现
。 在 OOP 技术中,
对象
充当了
一个
很重要的角色。
对象
的
数据
是组成
对象
的核心,而方法 则环绕这个核心并隐藏在
对象
之中。 2.2.2 封装 "封装"是 OOP 语言的优点之一。把
一个
对象
的
数据
加以包装并置于其方法的保护之下 称为封装。所谓封装就是对
数据
的隐藏。封装
实现
了把
数据
和操作这些
数据
的代码包装成为
一个
对象
(即离散的部件),而
数据
和操作细节(方法)隐藏起来。如果增加某些限制,使 得对
数据
的访问可按照统一的方式进行,那些能比较容易地产生更为强壮的代码。 OOP 语言提出一种(或称为协议),以保证对
数据
进行统一的操作。通常的做法是:程 序和
对象
数据
的交互作用通过
一个
公开的接口进行,而不直接进行操作。由于把
数据
封装在
对象
中,所以,访问
对象
中的
数据
只有一种途径,那就是
利用
一个
公开的接口。 实际上,封装在程序和
数据
之间设置了一道栅栏,它可以阻止一部分的
设计
错误,不至 于涉足应用程序其他部分的
数据
。 2.2.3 消息
一个
单独的
对象
一般不十分有用,而作为一员出现在包含有许多其他
对象
的大程序或应 用程序之中,通过这些
对象
的相互作用,程序员可
实现
高层次的操作和更负责的功能。某此
对象
通过向其他
对象
发送消息与其他
对象
进行交互作用和通信。 消息是以参数的形式传递给某方法的。
一个
消息通常由三部分组成: 1. 消息传送到
对象
的名称。 2. 要执行的方法的名称。 3. 方法需要的任意参数。 2.2.4 类 类是
一个
蓝图或样板,定义了某种类型的所有
对象
的变量和方法。 在 java 语言中,Java 程序的基本单位是类,也就是说:
一个
Java 程序是由多个类组成 的。定义
一个
类与定义
一个
数据
类型是有区别的。在程序
设计
语言中,把定义
数据
类型的能 力作为一种很重要的能力来对待。在面向
对象
的语言中,类的功能更强大,这是因为类不仅 含有定义
数据
类型的功能,而且还包含了对方法的定义。
对象
实际是类中的
一个
实例。生成实例的过程叫做把"
一个
对象
实例化"。
一个
实例化 的
对象
实际上是由若干个实例变量和实例方法组成的。当你创建出
一个
类的实例时,系统将 为实例变量指定内存,然后你就可以
利用
实例方法去做某些事情。 2.2.5 继承 继承是指建立子类的能力。子类继承了父亲的特征和功能。类的层次结构类似于一棵数 的结构,也像
一个
家庭谱系。它显示了根和它的导出类之间的关系。 子类从它先辈类那里继承了代码和
数据
,这样,它就可以执行先辈类的功能和访问先辈 类的
数据
。
一个
纯面向
对象
程序
设计
的语言将具有严格的继承性。 通过
对象
、类,我们
实现
了封装,通过子类我们可以
实现
继承。例如,公共汽车、出租 车、货车等都是汽车,但它们是不同的汽车,除了具有汽车的共性外,它们还具有自己的特 点(如不同的操作方法,不同的用途等)。这时我们可以把它们作为汽车的子类来
实现
,它们 继承父类(汽车)的所有状态和行为,同时增加自己的状态和行为。通过父类和子类,我们实 现了类的的层次,可以从最一般的类开始,逐步特殊化,定义一系列的子类。同时,通过继 承也
实现
了代码的复用,使程序的复杂性线性地增长,而不是呈几何级数增长。 2.2.6 抽象 面向
对象
的程序
设计
系统鼓励充分
利用
"抽象"。在现实世界中,人们正是通过抽象来 理解复杂的事务。例如:人们并没有把汽车当作成百上千的零件组成来认识,而是把它当作 具有自己特定行为的
对象
。人们可以忽略发动机、液压传输、刹车系统等如何工作的细节, 而习惯于把汽车当作
一个
整体来认识。 包含通用
对象
类的库叫作类库。 2.2.7 多态型 面向
对象
程序的最后
一个
概念是多态性。凭借多态性,你可以创建
一个
新的
对象
,它具 有与基
对象
相同的功能,但是这些功能中的
一个
或多个是通过不同的方式完成的。例如:在 Java 中你可以凭借多态性,通过
一个
画圆的
对象
,来创建
一个
画椭圆或矩形的
对象
。不管是 画圆,画椭圆还是画矩形的方法,它们都有
一个
相同的方法名,但以不同的方式完成他们的 画圆的功能。 1.8 类和
对象
1.8.1 类 类是组成 Java 程序的基本要素。它封装了一类
对象
的状态和方法,是这一类
对象
的 原型。定义
一个
类,实际上就是指定该类所包含的
数据
和对
数据
进行操作的代码。 类通过关键字 class 来定义,一般格式为: 【类说明修饰符】class 类名【extends 子句】【implements 子句】 type instance-varable1; type instance-varable2; type instance-varable3; the methodname1(parameter-list){method-body;} the methodname2(parameter-list){method-body;} the methodnameN (parameter-list){method-body;} 下面将类定义格式的项目说明如下: (1) class 是类说明关键字。 (2) 类名是由程序员自己定义的 Java 标识符,每个类说明必须有 class 和类名。 (3) 类说明修饰符
包括
: abstract 说明
一个
类为抽象类,抽象类是指不能直接实例化
对象
的类。 final 说明
一个
类为最终类,即改类不能再有子类。 public 说明类为公共类,该类可以被当前包以外的类和
对象
使用。 private 说明类为
私有
类。 (4) extends 子句用于说明类的直接超类。 (5) implements 子句用于说明类中将
实现
哪些接口,接口是 Java 的一种引用类 型。 (6) 类体包含了变量和方法。在类体中定义的
数据
、变量和方法称为类的
成员
, 或称为实例变量和实例方法。 (7) 例如: 下例定义了
一个
Point 类 ,并且声明了它的
两个
变量 x、y 坐标 ,同时
实现
init()方法 对 x、y 赋初值 。 class Ponit { int x,y; void init(int ix, int iy){ x=ix; y=iy; } } 类中所定义的变量和方法都是类的
成员
。对类的
成员
可以设定访问权限 ,来限定 其它
对象
对它的访问,访问权限所以有以下几种:private, protected, public, friendly。 1.8.2
对象
把类实例化,我们可以生成多个
对象
,这些
对象
通过消息传递来进行交互(消息 传递即激活指定的某个
对象
的方法以改变其状态或让它产生一定的行为),最终完 成复杂的任务。
一个
对象
的生命期
包括
三个
阶段:创建
对象
、
对象
的引用和释放对 象 。 1.8.3 创建
对象
创建
对象
包括
声明、实例化和初始化三方面的内容。通常的格式为 : 1. 声明
对象
对象
声明实际上是给
对象
命名,也称定义
一个
实例变量。
对象
声明的一般格式为: type name 其中,type 是
一个
类的类名,用它声明的
对象
将属于改类;name 是
对象
名。 例如:
Date
today; Rectangle myRectangle; 第一条语句说明了
对象
today 属于
Date
类,第二条语句说明了
对象
myRectangle 属于 Rectangle 类。
对象
说明并没有体现
一个
具体的
对象
,只有通过实例化后的对 象才能被使用。 2. 实例化
对象
实例化
对象
就是创建
一个
对象
。实例化
对象
意味着给
对象
分配必要的存储空间,用 来保存
对象
的
数据
和代码。实例化后的每个
对象
均占有自己的一块内存区域,实例 化时,每个
对象
分配有
一个
"引用"(reference)保存到
一个
实例变量中。"引用" 实际上是
一个
指针,此指针指向
对象
所占有的内存区域。 因此,
对象
名(变量)实际上存放的是
一个
被实例化之后的
对象
所占有的内存区域 的指针。 例如: type objectName = new type ( [paramlist] ); 运算符 new 为
对象
分配内存空间 ,实例化
一个
对象
。new 调用
对象
的构造方法,返 回对该
对象
的
一个
引用(即该
对象
所在的内存地址)。用 new 可以为
一个
类实例化, 多个不同的
对象
。这些
对象
分别占用不同的内存空间,因此改变其中
一个
对象
的状 态不会影响其它
对象
的状态 。 3.初始化
对象
生成
对象
的最后一步是执行构造方法,进行初始化。由于对构造方法可以进行重写 ,所以通过给出不同个数或类型的参数会分别调用不同的构造方法。 例子:以类 Rectangle 为例,我们生成类 Rectangle 的
对象
: Rectangle p1=new Rectangle (); Rectangle p2=new Rectangle (30,40); 这里,我们为类 Rectangle 生成了
两个
对象
p1、p2,它们分别调用不同的构造方法, p1 调用缺省的构造方法(即没有参数),p2 则调用带参数的构造方法。p1、p2 分别对 应于不同的内存空间,它们的值是不同的,可以完全独立地分别对它们进行操作。虽 然 new 运算符返回对
一个
对象
的引用,但与 C、C++中的指针不同,
对象
的引用是指 向
一个
中间的
数据
结构,它存储有关
数据
类型的信息以及当前
对象
所在的堆的地址, 而对于
对象
所在的实际的内存地址是不可操作的,这就保证了安全性。 1.8.4
对象
的引用
对象
的使用
包括
引用
对象
的
成员
变量和方法,通过运算符·可以
实现
对变量的访问和方法的调 用,变量和方法可以通过设定一定的访问权限(见下面的例子)来允许或禁止其它
对象
对它的 访问。 我们先定义
一个
类 Point。 例子: class Point{ int x,y; String name = "a point"; Point(){ x = 0; y = 0; } Point( int x, int y, String name ){ this.x = x; this.y = y; this.name = name; } int getX(){ return x; } int getY(){ return y; } void move( int newX, int newY ){ x = newX; y = newY; } Point newPoint( String name ){ Point newP = new Point( -x, -y, name ); return newP; } boolean equal( int x, int y ){ if( this.x==x && this.y==y ) return true; else return false; } void print(){ System.out.println(name+" : x = "+x+" y = "+y); } } public class UsingObject{ public static void main( String args[] ){ Point p = new Point(); p.print(); //call method of an object p.move( 50, 50 ); System.out.println("** after moving **"); System.out.println("Get x and y directly"); System.out.println("x = "+p.x+" y = "+p.y); //access variabl es of an object System.out.println("or Get x and y by calling method"); System.out.println("x = "+p.getY()+" y = "+p.getY()); if( p.equal(50,50) ) System.out.println("I like this point!!!! "); else System.out.println("I hate it!!!!! "); p.newPoint( "a new point" ).print(); new Point( 10, 15, "another new point" ).print(); } } 运行结果为: C:\java UsingObject a point : x = 0 y = 0 **** after moving ***** Get x and y directly x = 50 y = 50 or Get x and y by calling method x = 50 y = 50 I like this point!!!! a new point : x = -50 y = -50 another new point : x = 10 y = 15 1.引用
对象
的变量 要访问
对象
的某个变量,其格式为: objectReference.variable 其中 objectReference 是
对象
的
一个
引用,它可以是
一个
已生成的
对象
,也可以是能够生成对 象引用的表达式。 例如:我们用 Point p=newPoint();生成了类 Point 的
对象
p 后,可以用 p.x,p.y 来访问该点的 x、y 坐标,如 p.x = 10; p.y = 20; 或者用 new 生成
对象
的引用,然后直接访问,如: tx=new point().x; 2.调用
对象
的方法 要调用
对象
的某个方法,其格式为: objectReference.methodName ( [paramlist] ); 例 如我们要移动类 Point 的
对象
p,可以用 p.move(30,20); 虽然我们可以直接访问
对象
的变量 p.x、p.y 来改变点 p 的坐标,但是通过方法调用的方 式来
实现
能更好地体现面向
对象
的特点,建议在可能的情况下尽可能使用方法调用。 同样,也可以用 new 生成
对象
的引用,然后直接调用它的方法,如 new point(). move (30,20); 前面已经讲过,在
对象
的方法执行完后,通常会返回指定类型的值,我们可以合法地使 用这个值,如:例子中类 Point 的方法 equal 返回布尔值,我们可以用它来作为判断条件分别执 行不同的分支。如: if (p.equal (20,30)){ ...... //statements when equal }else { ...... //statements when unequal } 另外,类 Point 的方法 newPoint 返回该点关于原点的对称点,返回值也是
一个
Point 类型,我们 可以访问它的变量或调用它的方法,如: px = p.newPoint().x 或 px = p.newPoint(). getX(); 1.8.5
成员
变量
对象
具有状态和行为,而
对象
的状态则是用变量或
数据
来描述的。在
一个
类中,
对象
的 状态是以变量或
数据
的形式定义的。 例如: "盒子"的体积的状态主要是宽度、高度、和深度。因此在类定义"盒子"
对象
时,只 将这
三个
属性作为其主要的状态,并用变量的形式来描述,这些变量称为
成员
变量。而在对 象实例化后,这些变量称为实例变量。 1.8.6
成员
变量定义格式
成员
变量定义的一般格式为: 【Modifer】type variablelist; 其中, type 指定变量的类型,它可以时 Java 的任意一种类型。 variablelist 是一组逗号隔开的变量名(变量列表),每个变量都可带有自己的初始化的表达 式。 例如: xint ,z; aint ,b=2,c=3; Modifer 是定义变量的修饰符,它说明了变量的访问权限和某些使用规则。变量修饰符可以 是关键字 public、protected、private、final、static、transient 和 volatile 的组合。 1.8.7
成员
变量的初始化 当
成员
变量含有自己的初始化表达式时,可以创建实例的方式使
成员
变量实例化。 例如: class Box{ double width = 10; double height= 15; double depth= } 变量 width、height、depth 是
成员
变量。在执行 Box myBox1 = new Box()语句之后, new 运算符就创建了
一个
实例,并将变量分别赋初值为 10、15、20。在此时的变量 width、 height、depth 称为实例变量。 注意:在初始化表达式中,不能包含
成员
变量本身或同类的其他
成员
变量。例如,下面 的用法式错误的: class Test{ int int t =j; int } 错误有
两个
:
一个
式变量 k 的初始化涉及对 k 自身的访问;二式对 t 进行初始化时含有 对 j 的访问,而 j 的说明在其后。 1.8.8
成员
变量的访问权限
成员
变量或方法的访问权限是用访问权限修饰符来指定的。Java 的访问权限修饰符
包括
四种 显示方式修饰符和一种隐含方式修饰符,即: 1. 公用变量 用 public 说明的变量是公有变量。 访问权限:允许任何包中的任何类的变量访问。 例如:下面的代码中,在类 Alpha 中说明了
一个
公用变量 i_public,而在另
一个
类 Beta 中可以访问该变量。 class Alpha{ public int i_public ; } class Beta{ void accessmethod() { Alphaa= newAlpha(); a.i_public=10; } } 2.
私有
变量 //说明公用变量 i_public //访问公用变量 i_public 用 private 说明的变量是
私有
变量。 访问权限:只能被定义它的类的变量访问。 例如:下面的代码中,在类 Alpha 中说明了
一个
私有
变量 i_private,其他类不允 许访问。 正确的访问格式: class Alpha{ 3. 保护变量 } public int i_private ; void accessmethod() { Alphaa= newAlpha(); a.i_private=10; } //说明
私有
变量 i_private //访问
私有
变量 i_private 用 protected 说明的变量是保护变量。 访问权限:允许类自身、子类以及在同
一个
包中的所有类的变量访问。 例如:假定某个包 Geek 中含有
两个
成员
类 Alpha 和 Beta,若在类 Alpha 中说明 了
一个
保护变量 i_protected,则在另外
一个
类 Beta 中可以访问该变量。 class Alpha{ public int i_protected; void accessmethod() } class Beta { void accessmethod() { Alpha a= new Alpha(); a.i_protected=10; } } 4.
私有
保护变量 //说明保护变量 i_protected //访问保护变量 i_protected 用 private protected 说明的变量是
私有
保护变量。 访问权限:允许类自身以及它的子类变量访问。 例如:下面的两种访问方式是可行的。 (1) 在类中访问
私有
保护变量 例如: class Alpha{ private protected int i_pri_prot ; void accessmethod() { Alphaa= newAlpha(); } } a. i_pri_prot =10; //访问
私有
保护变量 i_pri_prot (2) 在子类中访问
私有
保护变量 例如: class Alpha{ private protected int i_pri_prot=10 ; } class Beta extends Alpha { void accessmethod() { Alphaa= newAlpha(); } } a. i_pri_prot =30; //访问
私有
保护变量 i_pri_prot 在程序执行时,变量 i_pri_prot 的值是 30,而不是 10; 5. 友好变量 如果
一个
变量没有显示地设置访问权限,则该变量为友好变量。 访问权限:允许类自身以及在同
一个
包中地所有类地变量访问。 例如:下面的类中定义了
一个
友好变量: class Alpha{ int i_friendly ; void accessmethod() { Alphaa= newAlpha(); } } a. i_friendly=10; //访问友好变量 i_friendly 在了解了
成员
变量的访问权限之后,那么在说明每
一个
成员
变量时,都可以按访问权限给变 量提供适当的保护措施,这样就加强了变量的安全性。 名称 公用
私有
保护
私有
保护 访问权限修饰 public private protected private protected 类 √ √ √ √ 子类 √ √ √ 包 √ * 所有类 √ √ 友好 friendly √ √ 注:表中√的为可选,打*的说明有特殊限制。*号是针对子类访问保护变量而言,即
一个
子类只有与超类 在同
一个
包中,才可以访问超类
对象
的保护变量。 1.8.9 静态变量 用 static 说明的变量是静态变量。静态变量与其他
成员
变量有区别:其他
成员
变量必须通过 类的
对象
来访问,每个
对象
都有这些变量的备份;而静态变量独立于改类中的任何
对象
,它 在类的实例中只有
一个
备份,可以直接使用,而不必通过类的
对象
去访问,它一直属于定义 它的类,因此也称为类变量。类的所有
对象
都共享 static 变量。static 变量通常也称为全局变 量。 例如: 静态变量的定义和引用。首先在类 MyDemo 中定义了 static 变量 x,y 然后在类 MyStaticDemo 中输入变量 x 和 y 的值。 import java.awt.Graphics; class MyDemo { static int x=80; static int y=120; } class MyStaticDemo extends java.applet.Applet { public void paint(Graphics g) { g.drawString("x="+MyDemo.x+"y="+MyDemo.y,25,50); } } 程序运行结果: x=80 y=120 在上面的程序中,在访问的静态变量 x 和 y 时,是通过类名 MyDemo 直接访问的。 static 也可以说明方法。用 static 说明的方法是静态方法或类方法,在实例中只有一 个备份。该方法具有以下约束: a) b) c) 它们仅可以调用其他 static 方法。 它们仅可以访问 static 变量。 它们不能参考 this 或 super。 如果类的
成员
被定义为 static,则可以通过下面形式引用: 类名,
成员
名 这里,类名是定义 static
成员
所属的类。Java 通过这样的方式,
实现
了全局方法和变量。 1.8.10 final 变量 用 final 说明的变量可以当作
一个
常量
使用,不得对其进行任何修改。若某此变量为了 防止被修改,则定义变量时必须初始化
一个
final 变量。在这一点上,final 与 C\C++的 const 相似。比如: final int MONDAY=1; final int TUSDAY=2; final int WEDNESDAY=3; ...... 以后程序可以把上述变量当作
常量
来使用,而不用担心其被修改。 final 变量用大写字母来表示,这是一种习惯约定。final 变量不占内存空间,实际上也 就是
一个
常数。 1.9 方法 1.9.1 方法的定义 方法也是类的
一个
成员
,定义方法时在定义类的同时进行的。其一般格式为: type name(parameter -list) { //方法体 } 格式说明: (1) type 指定方法的返回类型,简称方法的类型,它可以是任何有效的类型,
包括
类类型。方法的返回或带值返回都由 return 语句
实现
,当
一个
方法没 有返回值时,其 type 必须为 void,且 return 语句可以省略。 (2) name 指定方法名,方法名可以是合适的 Java 标识符。 (3) parameter-list 指定方法的参数列表,参数
包括
参数的类型和参数名,每个 参数用逗号隔开。在定义方法时,其参数将作为形参;在调用方法时,其 参数被称为实参。调用时是把实参的值传递给形参。入过方法没有参数, 参数列表为空,但括号"()"不能省略。 (4) 方法体包含了一组代码,它用于对
数据
处理。方法体用以对大括号"{}"括 起来。 例如:Box 类封装"盒子"的状态和行为,即
数据
变量和方法,用方法 volume 计 算 Box
对象
的体积。 import java.awt.Graphics; class Box { double width; double height; double depth; void setDemo(double w,double h,double d) { width=w; height=h; depth=d; } } double volume() { return width*height*depth; } class BoxDemo extends java.applet.Applet { public void paint(Graphics g) { double d_volume; Box myBox = new Box(); myBox.setDemo(10,20,30); //调用方法 setDemo 给变量赋值 d_volumn = myBox.volume(); //计算体积 g.drawString("myBox 的体积是:"+ d_volumn,25,50); } } 程序运行的结果如下: myBox 的体积是:6000 1.9.2 方法的访问权限 方法同
成员
变量一样,可以在定义时说明方法的访问权限,其说明格式和访问机制与成 员变量完全一样。 例如:
私有
方法、
私有
保护方法只能被同
一个
类中的方法调用,公用方法可以被其他类 的方法调用。 import java.awt.Graphics; class Alpah1 { pivateint i_private() { int x=10; int y=20; return x+y; } public int i_public1() { return i_private(); } } class Alpah2 { public public int x,y; private protected int i_protected(int a,int h) { x=a; y=b; return x+y; } public void i_public2(int i,int j) { k=i_protected(i,j); } } class Test extends java.applet.Applet { public void paint(Graphics g) { int p1; Alpah1 ap1= newAlpah1(); Alpah2 ap2= newAlpah2(); p1=ap1.i_public1(); ap2. i_public2(50,50); g.drawString("i_public1()的返回值是:"+p1,25,50); g.drawString("i_public2()的返回值是:"+ap2.k,25,50); } } 程序运行的结果如下: 方法 i_public1()的返回值是:30 方法 i_public2()的返回值是:100 程序说明: (1)在类 Alpah1 中,方法 i_private()是
私有
的,它只能被同类的方法 i_public1()调用。 而方法 i_public1()是公有的,它可以被另一类 Demo 中的方法 paint()调用。 (2)在类 Alpah2 中,方法 i_protected()是
私有
保护性的,它不能被其他类的方法调用, 而只能被同类(或子类)的方法 i_public2()调用。同样,方法 i_public2()也是公有的,它可 以被另一类 Test 中的方法 paint()调用。 (3)在方法 i_public1()中,语句 return i_private()执行的顺序为:先调用,后返回。其 返回值是 x+y 的和。 (4)在方法 i_public2(int i,int j)中,将形参 i 和 j 作为调用方法中的实参。 如:k=i_protected(i,j); (5) 在类 Alpha2 中,定义了
成员
变量 k、x 和 y。其中 k 是公有的,它可被类 Test 中 的方法引用,其格式为:ap2.k;而变量 x 和 y 是
私有
的。它只能被同一类的方法引用。 (6)在类 Alpah1 方法 i_private()中,定义的变量 x 和 y 是局部变量。 局部变量作用域是:只能在定义它的方法中有效,即使同一类中的方法也不能引用。 例如:方法 i_public()是不能访问局部变量 x 和 y 的。 1.9.3
对象
作为参数 例:给定
两个
数,按大小顺序输出。 import java.awt.Graphics; class Test1 { public int a,b; void mov(int i,int j) { a=i; b=j; } } class Test2 { void max(Test1 test1) { int c; if(test1.a
对象的
成员
变量 a。 test1.a = test.b; test1.b=c; } } } class MaxTest extends java.awt.Applet { public void paint(Graphics g) { Test1myTest1=new Test1(); Test2 myTest2 = new Test2(); myTest1.mov(45,55); //调用 Test2 类的方法 max,并将
对象
myTest1 作为实参。 myTest2.max(myTest1); g.drawString("大小顺序为:"+ myTest1.a+","+myTest1.b,25,50); } } 1.9.4
对象
作为返回值 下面的例子是把
对象
作为返回值的编程方法。 例如:
利用
方法返回
对象
的特性,
实现
对实例变量的值的递增。 import java.awt.Graphics; class Test { public int a,b; void mov( int i,int j) { a=i; b=j; } Test max() //方法 max()的返回值类型是 Test 类型 { Test myTest = new Test(); myTest.mov(a+10,b+10);使实例变量的值增 10。 return myTest; } } class OutMyDemo extends java.applet.Applet; { public void paint(Graphics g) { Test p1 = new Test(); Test p2; p1.mov(15,55); g.drawString("p1 的实例变量值:a="+pa1.a+",b="+pa1.b,25,75); p2=p2.amx(); g.drawString("p2(p2 加 10)的值:a="+p2.a+",b="+p2.b,25,100); } } 程序运行的结果如下: p1 的实例变量值:a=15,b=55; p2(p1 加 10)的值:a=15,b=65; p2(p2 加 10)的值:a=35,b=75; 1.10 构造方法 1.10.1构造方法概述 构造方法是
一个
在创建
对象
初始化的方法,它具有与类相同的名字。事实上,除了构造 方法,在类中不能再有别的方法与类同名。一旦定义了构造方法,在执行 new 操作
期间
, 首先创建
对象
,然后自动调用构造方法将
对象
初始化。 与其他方法不同,构造方法必须在 new 运算符中引用,而不能按引用一般方法的格式 去引用构造方法。 在同
一个
类中,允许定义多个构造方法。这些构造方法是以参数的个数来区分的。在创 建
对象
时,程序员应当根据参数的个数和类型来选择合适的构造方法。 如果
一个
类中没有包含构造的说明,将提供隐含的构造方法,隐含的构造方法没有参数, 它将调用超类中不带参数的构造方法,如果超类中没有不带参数的构造方法,将产生编译错 误。 构造方法有些特殊,它没有返回类型甚至 void。因为构造方法的缺省返回类型是类类型 本身。构造方法的另
一个
特殊性是它不能继承超类。 例子: import java.awt.Graphics; class Test { double width; double height; double depth; Test(double w,double h,double d) //定义构造方法 Box() { width = w; height = h; depth=d; } double volume() { return width*height*depth; } } class Testing extends java.applet.applet { public void paint(Graphics g) { double d_volume; Test test = new Test()//new 运算符创建
对象
test 后,随即对 test 初始化。 } } d_volume = test.volume();//计算体积 g.drawString("test 的体积是:"+d_volume,25,50); 程序运行的结果如下: test 的体积是:6000 1.10.2构造方法的访问权限 在定义构造方法时可以对他们附加访问修饰符,它们时: pulic(公有型的构造方法) private (
私有
型的构造访问) proected (保护型的构造方法) private protected (
私有
保护型的构造方法) 这四种方法访问修饰符的权限范围和方法的访问权限的范围一样。构咱方法的访问修饰符不 能是 abstract、static、final、native 或 synchronized; 1.11 方法重载 在 java 中,可以在同
一个
类中定义多个同名的方法,只要它们的参数列表不同,这叫做方 法的重载(Overload)。方法重载是 Java 最具有吸引力的有利特征。 当
一个
重载方法被激起时,Java 便根据参数的类型和数目确定被调用的方法。这样,重载方 法之间一定具有不同的参数列表,
包括
参数的类型和数目。 例:给定
两个
或
三个
数,将它们由大到小按顺序输出。 import java.awt.Graphics; class Test { double max1,max2,nax3; Test() { max1=-1; maxe2=-1; max3=-1; } void sort(double i,double j) { double s; max1=i; max2=j; if( max1
两个数的大小顺序为:"+ t1.max1+","+t1.max2,25,10); t1.sort(100.9f,200.9f,300.9f); g.drawString("
三个
数的大小顺序为:"+ t1.max1+","+t1.max2+","+t1.max3,25,30); } } 程序运行的结果如下:
两个
数的大小顺序为:300,200
三个
数的大小顺序为:300.9,200.9,100.9 在上面的代码中,类 Test 中定义了
三个
方法: Test()是
一个
无参的构造方法,它在创建
对象
之后紧接着对变量进行初始化。如果没有该 方法,在创建
对象
时将产生编译错误。因为类 Test 是直接超类。
两个
sort()是排序方法,它们是用参数的数目来区分的,这
两个
sort()方法能对任 何数字类型的
数据
进行排序。当然,对多个
数据
进行排序最好用数组来
实现
。 1.12 this 1.12.1用 this 引用
成员
变量 例子:在构造方法中使用 this,this 代表当前
对象
的引用参考,并将参数值赋值到
成员
变量 max1、max2 和 max3 中。 import java.awt.Graphics; class Test { double max1; double max2; double max3; Test(double i,double j) { this.max1=i; this.max2=j; this.max3=k; } } class Test1 extends java.awt.Applet { public void paint(Graphics g) { Test t = new Test(10,20,30); g.drawString(t.max1+","+t.max2+","+t.max3,25,30); } } 程序运行的结果如下: 10,20,30 1.12.2在构造方法中用 this 调用一般方法 例子:在构造方法中使用 this 调用 sort()方法。 import java.awt.Graphics; class Test { double max1,max2,max3; Test(double i,double j) { max1=i; max2=j; this.sort(i,j); //调用 sort 方法,将
两个
数排大小顺序。 } Test(double i,double j,double k) { max1=i; max2=j; max3=k; this.sort(i,j,k) //调用 sort 方法,将
三个
数排大小顺序。 } void sort(int i,double j) { double s; if(max1
两个数的大小顺序为:"+t1.max1+","t1.max2,25,10); Test t2 = new Test(100,10,1000); g.drawString("
三个
数的大小顺序为:"+t2.max1+","t2.max2+","+t2.max3,25,30); } } 程序运行的结果如下:
两个
数的大小顺序为:100,10
三个
数的大小顺序为:1000,100,10 1.12.3在方法中用 this 调用另外
一个
方法 例子:在上面的例子上加
一个
方法 mov(),在 mov()中引用 this 再调用 sort()方法。 import java.awt.Graphics; class Test { double max1,max2,max3; Test(double i,double j) { max1=i; max2=j; this.sort(i,j); //调用 sort 方法,将
两个
数排大小顺序。 } void sort(int i,double j) { double s; if(max1
两个数的大小顺序为:"+t1.max1+","t1.max2,25,10); } } 程序运行的结果如下:
两个
数的大小顺序为:200,20 注意:再初始化之后,变量 max1 和 max2 的值分别为 10 和 100,当调用 mov()之后, 其值为 20 和 200。 1.13 类的继承 继承是面向
对象
程序
设计
的重要概念之一。在 java 中,被继承类称为超类,继承称为 子类。通过继承可使子类继承超类的特征和功能。其基本规则如下: (1) 子类可继承其超类的代码和
数据
,即在子类中可以直接执行超类的方法和访问超类 的
数据
。 (2) 在子类中可以增加超类中没有的变量和方法。 (3) 在子类中可以重新定义超类中已有的变量(
包括
实例变量和类变量)。变量的重新 定义被称为变量的隐藏,也就是说,在子类中允许定义与超类同名的变量。 (4) 在子类中可以重载超类中已有的方法(
包括
构造方法、实例方法和类方法)。 (5) 在子类的构造方法中,可以通过 super()方法将超类的变量初始化。 (6) 当超类为抽象类时,子类可继承超类中的抽象方法,并在子类中去
实现
该方法的细 节。 1.13.1 创建子类
实现
继承要用 extends 关键词,其格式如下: 子类名 extends 超类名 (1) 继承超类的方法和变量 例子:给定
三个
数,将它们按大到小的顺序输出。在下面的程序中,子类 Test1 继承超类 Test 的全部代码和变量。在子类的方法中,调用超类的方法 sort()和更 新
数据
。 import java.awt.Graphics; class Test { double max1,max2,max3; Test() { max1=-1 max2=-1; max3=-1; } void sort() { double s; if(max1
三个数的大小顺序为:"+t1.max1+","t1.max2+","+t1.max3,25,30); } } 程序运行的结果如下:
三个
数的大小顺序为:300,200,100 (2) 隐藏实例变量和类变量 在子类中,若定义了与超类同名的变量,则只有子类中的变量有效,而超类中 的变量无效。 例:
数据
求和。 import java.awt.Graphics; class Test { double sum,num1,num2; static int num3; Test() { num1=0; num2=0; num3=0; sum=0; } } class Test1 extendsTest { int sum,num1,num2; //隐藏超类 Test 中的实例变量 static int num3; //隐藏超类 Test 中的类变量 } void sum(int i,int j,int k) { num1=i; num2=j; num3=k; sum=num1+num2+num3; } class SumTest extends java.awt.Applet { public void paint(Graphics g) { Test1t1 =new Test1(); t1.sum(100,200,300); g.drawString("sum="+ t1.num1+"+"+t1.num2+"+"+t1.num+"="+t1.sum,25,30); } } 程序运行的结果如下: sum=100+200+300+=600 (3)访问权限 尽管子类可以继承超类的所有
成员
,但子类不能继承超类中的
私有
变量和方法,而只能 继承如下变量和方法。 .超类中定义为公有的、
私有
保护的或保护的
成员
变量和方法可被子类访问。 .同
一个
包中的超类的友好变量和方法可被子类访问。 (4)用继承的方法来扩充超类的功能 如果子类只继承超类的代码和
数据
是没有多大意义的,对继承来说,其真正的目的是想 通过子类来扩充超来的功能,只有这样,才能有效发挥继承的作用。 例:从超类 Box 中只计算"盒子"的体积,但在子类 SubBox 中不仅只计算"盒子"的 体积,而且还能
求得
它重量。 import java.awt.Graphics; class Box { double width; double height; double depth; Box(double w,double h,double d) { width=w; height=h; } Box() { depth=d; } width=-1; height=-1; depth=-1; } double volume() { return width*height*depth; } class SubBox extends Box { double weight; SubBox(double w,double h,double d,double m) { width=w; height=h; depth=d; weight=m; } } class Test extends java.awt.Applet { public void paint(Graphics g) { double d_volume; Box box1=new Box(10,20,30); d_volume=box1.volume(); //计算 box1 的体积 } } g.drawString("box1 的体积是:"+d_volume,25,50); SubBox subBox = new SubBox(15,15,20,40); d_volume= subBox.volume(); g.drawString("subBox 的体积是:"+d_volume,25,75); g.drawString("subBox 的体积是:"+subBox.weight,25,90); 程序运行的结果如下: box1 的体积是:6000; subBox 的体积是:4500 subBox 的重量是:40 1.13.2 重载超类的方法 要使多态性在程序中
设计
运用成功,不仅
要求
超类给子类提供可直接使用的元素,更重要的 是在类层次结构中具有一致的接口,使子类在超类的基础上扩充超类的功能。 例:超类 Test 中方法 sort()
实现
对 n 个整个升序排列,子类 Test1 中的 sort()
实现
对 n 个整数 按降序排列。 import java.awt.Graphics; class Test { int Test() { i=j=k=swap=0; } void sort(int t1,int t2[ ]) //用选择法按升序排列 { for(int i=0;i
数据为:",20,10); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,30); } Test t1=new Test(); t1.sort(a.lenght,a); //调用 Test 类的方法 g.drawString("按升序排列的
数据
为:",20,50); for(int i=0;i<10;i++) { } g.drawString(""+a[i],20+i*30,70); } } Test1 t2=new Test2(); t2.sort(a.lenght,a); g.drawString("按降序排列的
数据
为:",20,90); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,110); } 程序运行的结果如下: 排序前的
数据
为: 34 12 8 67 88 23 98 101 119 56 按升序排列的
数据
为: 8 12 23 34 56 67 88 98 101 119 按降序排列的
数据
为: 119 101 98 67 56 34 23 12 8 1.13.3 super super 是
一个
方法,其格式为: super(parameter-list) 其中,parameter-list 是参数列表。super()的作用是调用超类的构造方法。降超类中的变量初 始化。super()总是用于子类的构造方法中,而且在子类的构造方法中最先执行 super().因此, 只有 parameter-list 与超类的构造方法中的参数相匹配,才能有效调用超类的构造方法去
实现
对超类的变量初始化。同时,在子类中也减少了初始化编码的重复工作。 super()的用法请见下面的例子: 例:在下面的例子里,将子类的构造方法中的赋初值改用 super()方法来完成 import java.awt.Graphics; class Box { double width; double height; double depth; Box(double w,double h,double d) { width=w; height=h; depth=d; } Box() { } width=-1; height=-1; depth=-1; } double volume() { return width* height* depth; } class SubBox extends Box { double weight; SubBox(double w,double h,double d,double m) { super(w,h,d); weight=m; } } class BoxTest extends java.awt.Applet { public void paint(Graphics g) { double d_volume; Box box1=new Box(10,20,30); d_volume=box1.volume(); //计算 box1 的体积 } } g.drawString("box1 的体积是:"+d_volume,25,50); SubBox subBox = new SubBox(15,15,20,40); d_volume= subBox.volume(); //计算 subBox 的体积 g.drawString("subBox 的体积是:"+d_volume,25,75); g.drawString("subBox 的体积是:"+subBox.weight,25,90); 程序运行的结果如下: box1 的体积是:6000; subBox 的体积是:4500 subBox 的重量是:40 但要注意的是: (1) super(w,h,d)用于子类的构造方法 SubBox()中。 (2) super(w,h,d)中的参数应与超类的构造方法参数相匹配,即参数个数及类型 一样。 1.13.4 abstract 当
要求
将
一个
超类定义为抽象类时,前面加关键字 abstact。其格式如下: abstract 类型 类名{} abstract 的作用是说明该类是一种抽象结构。抽象结构的类包含了一些抽象的方法,而这些 抽象方法只有方法的形式,即方法体是空的,方法体的细节由子类去
实现
。抽象方法的定义 也由关键字 abstract 来说明,其格式为: abstract 类型 方法名{parameter-list}; { 其中,parameter-list 是参数列表。因此,整个抽象类的结构如下形式:
成员
变量 1; 。。。。。 构造方法 。。。。。。 abstract 类型 方法名(parameter-list); } 抽象类的定义也是多态的一种体现。因为多态性具有子类重载超类中的方法的特性,而在超 类中只限定子类重载规定的方法,但这些方法的细节必须由子类来完成。所有,常把这样的 类作为抽象类。 抽象类不能直接用 new 运算符实例化
一个
对象
。抽象方法只能是实例化方法,它不
包括
子 类
对象
。 例:在抽象类 Test 中,定义
一个
抽象方法 sort(),并在子类 Test1,Test2 中,分别去
实现
超类中 的抽象方法 sort()的细节,从而分别完成对 n 个整数的降序、升序的排列。 import java.awt.Graphics; abstract class Test { int i,j,k,swap; Test() { i=j=k=swap=0; } abstract void sort(int t1,int[] t2); //定义
一个
抽象的方法。 } class Test1 extends Test { void sort(int t1,int t2[ ]) //重载超类的方法 sort(),用选择法按降序排列 { for(int i=0;i
对象。 Test1 k1=new Test1(); k1.sort(a.length,a); g.drawString("按降序排列的
数据
为",20,10); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,30); } Test2 t2=new Test2(); t2.sort(a.lenght,a); g.drawString("按升序排列的
数据
为:",20,50); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,70); } 程序运行的结果如下: 按降序排列的
数据
为: 119 101 98 67 56 34 23 12 8 按升序排列的
数据
为: 8 12 23 34 56 67 88 98 101 119 通过本例可以发现抽象类的好处:从
一个
抽象类可以派生出多个子类,而且在各个子类中允 许对同
一个
抽象方法产生不同的方法体。 1.13.5 final final 有
三个
用途:其一,可以用于创建与
常量
一样的变量,其二,另外 2 个用途就是防止 类被继承或方法被重载。 1. 最终类 Java 引入了最终类的概念,所谓最终类既是那些不能再有子类的类。说明最终类时可以 在最前面加上修饰符 final。 例:final class A { //.... } class B extends A //错误!不能从 A 类派生 { //....... } 注意:不能将
一个
抽象类说明为 final 类,因为抽象类必须要被派生类来
实现
它的抽象 方法。当
一个
类被说明为 final 类后,也相当于隐式的说明了它的所有方法为 final。 1.14 java 包,接口和异常 包是类的容器,用于保证类名空间的一致性。包以层次结构组织并可被明确地引入到一 个新类定义。 接口是方法的显示说明,
利用
接口可以完成没有具体
实现
的类。接口虽然与抽象类相似, 但它具有多继承能力。
一个
类可以有无数个接口,但是只能从
一个
父类派生。 异常是代码运行时出现地非正常状态。Java 的异常是
一个
出现在代码中描述异常状态的
对象
。每当
一个
异常情况出现,系统就创建
一个
异常
对象
,并转入到引进异常的方法中,方 法就根据不同的类型捕捉异常。为防止由于异常而引起的退出,在方法退出前应执行特定的 代码段。 1.14.1 包 1.14.1.1 package 语句 包是一种命名和可视控制的机制,用户可以把某些类定义在
一个
包 中,也可以对定义在这个包中的类施加访问权限,以限定包外或包 内的程序对其中的某些类进行访问。 定义包的格式: package pkg; Java 的文件系统将存储和管理这个包。例如:属于 pkg 包中的.class 文件将存储到目录 pkg。注意:目录与包的名字完全一样。 package 语句可以创建包的层次结构,通过使用逗号,把每个包分开。 多级包层次结构的一般格式为: package pkg1[.pkg2[.pkg3]] 包层次结构必须反映 Java 开发系统的文件系统。比如:pacakge java.awt.Image; 1.14.1.2 import 语句 import 语句的功能是引入包或包中的类。它们必须位于 package 语 句与这个文件的类或接口之间。 格式:import pkg1[.pkg2.(classname|*)] 这里,pkg1 是顶层包名,pkg2 是下一层包名,其间用点隔开。除了 对文件系统的限制外,对包的层次深度没有限制。最后以
一个
显示 方式指定
一个
类名 classname 或*号,星号*指示 Java 编译器应该引入 整个包。 例子: import java.awt.Image; import java.lang.*; 1.14.2 接口 1.14.2.1 接口的概念 接口与类存在着本质的差别,类有它的
成员
变量和方法,而接口只有
常量
和方法协议,从概念来讲,接口是一组方法协议和
常量
的集合。 接口在方法协议与方法体实体之间只起到一种称之为界面的作用,这 种界面限定了方法实体中的参数类型一定要与方法协议中所规定的 参数类型保持一致,除此以外,这种界面还限定了方法名、参数个数 及方法返回类型的一致性。因此,在使用接口时,类与接口之间并不 存在子类与父类的那种继承关系,在
实现
接口所规定的某些操作时只 存在类中的方法与接口之间保持一致的关系,而且
一个
类可以和多个 接口之间保持这种关系,即
一个
类可以
实现
多个接口。 1.14.2.2 接口的定义 格式: access interface name { return method-name(parameter-list); type final-varname=value; } 其中,access 是访问修饰符。它只有 public 和未指定访问修饰符 两种情况。当未指定访问修饰符时,该接口只能为同
一个
包中的 其他
成员
所用;当 public 修饰符说明时,该接口可以被其他任何 代码使用。name 是接口的名字,可以是任何有效的标识符。接 口体
包括
方法协议和
常量
,它们都用分号结束。在接口中定义的
常量
必须用变量名标识。他们隐式为 final 和 static,不能被
实现
类所改变。 例:interface Back { void c_back(int param); int SOON=4; } 该接口定义了
一个
方法协议,其参数为整数类型;还定义了一整 数
常量
,其变量名为 SOON。该接口只能为同
一个
包中其他
成员
所用。 1.14.2.3 接口的
实现
一旦接口被定义,类就可以
实现
它。在类的定义中可以使用关键 字 implements
实现
接口,然后在类中创建接口中所定义的方法, implements 关键字的类的格式: access class classname[extends superclass] [implements interfacename[,interface...]] { } ..... 例子:定义
两个
接口,其方法协议分别完成
两个
数的加法和减法 操作,然后分别
实现
这
两个
接口的方法。 import java.awt.Graphics; interface Demo_Add { public int add(int x,int y); } interface Demo_Sub { public int sub(int x,int y); } class Demo_Add_Sub implements Demo_Add, Demo_Sub { public int add(int x,int y) { return i+y; } public int sub(int x,int y) { return x-y; } } public class Add_Sub extends java.awt.Applet { public void paint(Graphics g) { Demo_Add_Sub k=new Demo_Add_Sub(); g.drawString("x+y="+k.add(30,10),30,30); g.drawString("x-y=" + k.sub(30,10),30,50); } } 程序运行结果如下: x+y=40; x-y=20; 1.14.2.4 接口的继承
一个
接口可以继承其他接口,这可通过关键字 extends 来
实现
,其语 法与类的继承相同。当
一个
类
实现
一个
派生接口时,必须
实现
所有 接口及其派生接口中所定义的全部方法协议。 例:定义 5 个接口,其中
两个
超接口。 import java.awt.Graphics; //定义接口 Test_C interface Test_C { public int add(int x,int y); } interface Test_B extends Test_C { public int sub(int x,int y); } interface Test_A extends Test_B { public int mul(int x,int y); } //定义接口 Test_Y interface Test_Y { public int div (int x,int y); } interface Test_X extends Test_Y { public int mod (int x,int y); } //定义类 Test class Test implements Test_A ,Test_X { public int add(int x,int y) { return x+y; } public int sub(int x,int y) { } return x-y; } public int mul(int x,int y) { return x*y; } public int div (int x,int y) { return x/y; } public int mod (int x,int y) { return x%y; } public class Add_Mod extends java.awt.Applet { public void paint(Graphics g) { Test k =new Test(); g.drawString("x+y="+k.add(66,10),30,10); g.drawString("x-y="+k.sub(66,10),30,10); g.drawString("x*y="+k.mul(66,10),30,10); g.drawString("x/y="+k.div(66,10),30,10); g.drawString("x%y="+k.mod(66,10),30,10); } } 程序运行结果如下; x+y=76 x-y=56 x*y=660 x/y=6 x%y=6 注意:在类中
实现
一个
接口时,任何类都必须
实现
该接口或接口树的所有方法。 1.14.3 异常处理 所谓异常就是代码在运行时发生的非正常状态。Java 的异常是
一个
出现在代码中描 述异常状态的
对象
。每当出现
一个
异常情况,就创建
一个
异常
对象
,并向导致错误 的方法中抛出该
对象
。该方法捕捉到异常
对象
之后,可以选择用来处理该异常或不 管它。异常通常由 Java 运行系统产生,或者由用户的程序代码产生。Java 抛出的异 常大多是用户违背了 Java 语言的基本规则,或者超出了 Java 执行环境的约束。 1.14.4 异常处理机制 Java 通过 5 个关键字 try、catch、throw、throws 和 finally 管理异常处理。 try 用来监视它所在的那个程序块是否发生异常,如果发生异常就抛出它。对于系统 产生的异常或程序块中未用 try 监视所产生的异常,将一律被 Java 运行系统自动抛 出。 catch 用来捕捉 try 程序所抛出的异常,将一律被 Java 运行系统自动抛出。 throw 可以用以人工地抛出
一个
异常 throws 用于从
一个
方法中抛出
一个
异常。 finally 用于调用缺省异常处理。 下面是异常处理地一般形式: try { //有可能会出错地代码块,被 try 监视。 } catch(ExceptionType1 ex) { //关于 ExceptionType1 的异常处理 } catch(ExceptionType2 ex) { //关于 ExceptionType2 的异常处理 } //...... finally { //在 try 块结束前被执行的代码块。 } 1.2 常用类 System 类 全称:java.lang.System 扩展:Object 描述:公有最终类。此类与 Runtime 一起可以访问许多有用的系统功能。有一些方法在
两个
类中都出现。exit()、gc()、load()和 loadLibrary()可以通过调用 Runtime 中的同名方 法来响应。特别有用的是类变量 err、in 和 out,它们可以访问基本控制台 I/O。System 类完全由类域组成,这些类域可以通过"System.变量名"和"System.方法()"的方法 进行访问。此类不能被实例化。 实例变量: public static PrintStream err; public static InputStream in; public static PrintStream out; 每个变量对整个程序而言都是唯一的
对象
变量。这些
对象
可以访问系统的输入、输出和 错误输出。 常用类方法: void exit(int status) 导致 Java 程序退出,向系统传递指定的状态代码。 void gc() 请求立即激活垃圾收集器开始清除不再使用的
对象
。 String getProperty(String key) String getProperty(String key,String def) 在系统属性表中查找的
一个
属性。如果未发现属性,而且在 getProperty()方法中指定了 参数 def,那么返回 def。 SwingUtil.ities 类 全称:javax.swing.SwingUtilities 扩展:Object 描述:公用类。此类中包含了一组在 Swing 工具包中使用的帮助器方法 常用类方法: void paintComponent(Graphics g,Component c,Container p,int x,int y,int w,int h); void paintComponent(Graphics g,Component c,Container p,Rectangle r); void invokeLate(Runnable doRun); boolean isEventDispatchThread(); Component getRoot(Component c); JRootPane getRootPane(Component c);
3:
运算符重载
1
设计
一个
日
期
类
Date
,
包括
年
、
月
、
日
等
私有
数据
成员
。
要求
实现
日
期
的基本运算,如一
日
期
加上
天数
、一
日
期
减去
天数
、两
日
期
相差
的
天数
等。
描述
设计
一个
日
期
类
Date
,
包括
年
、
月
、
日
等
私有
数据
成员
。
要求
实现
日
期
的基本运算,如一
日
期
加上
天数
、一
日
期
减去
天数
、两
日
期
相差
的
天数
等。 提示: 在
Date
类中
设计
如下重载运算符函数:
Date
operator+(int days); 返回一
日
期
加一
天数
得到的
日
期
Date
operator-(int days); 返回一
日
期
减去
天数
得到的
日
期
int operator-(
Date
&b); 返回两
日
期
相差
的
天数
在
实现
这些重载运算符函数调用以下
私有
成员
函数: leap(int); 判断指定的
年
C++ 语言
64,637
社区成员
250,559
社区内容
发帖
与我相关
我的任务
C++ 语言
C++ 语言相关问题讨论,技术干货分享,前沿动态等
复制链接
扫一扫
分享
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++
技术论坛(原bbs)
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
请不要发布与C++技术无关的贴子
请不要发布与技术无关的招聘、广告的帖子
请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下
试试用AI创作助手写篇文章吧
+ 用AI写文章