这是有关Java语言基础的,偶这学期专业课开始学习java,希望通过此贴把偶的课堂笔记与大家分享,与初学者共同从零学起,还请各位高手随时指点,引导大家把基础打好,谢谢
. 现在每周两节课,偶会随时更新的,因为板书很多,有些精彩的描述都是口述的,所以下面的笔记并不是很规范.
-------------------------------------------------------------------------------
1楼: 第一节课: Java 入门起步
2楼: 第二节课: 标识符 关键字及数据类型
3楼: 第三节课: 类和对象
4楼: 第四节课: static的用法和包
5楼 第五节课: 继承与访问权限
6楼 第六节课: 多态与抽象类
7楼 第七节课: 接口
8楼 第八节课: 一些重要的类(内部类, 匿名类, 异常类, Class类, 基本数据类型类等)
9楼 第九节课: 字符串
第一节课:
1. java 语言的诞生和特点(一般的教材都会有很详细的叙述,略)
2. java 学习的起步
(1)Sun公司要实现“编写一次,到处运行”(Write once,run anywhere)的目标,必须提供相应的Java运行平台,目前Java运行平台主要分为3个版本:J2SE(标准平台),J2EE(企业平台),J2ME(移动平台)
(2)学习Java必须从J2SE开始,当然得从了解在标准平台(J2SE)上是如何开发程序开始.
a.首先从源文件(以.java结尾的文件)开始----经过java编译器(javac.exe)编译----生成字节码文件----根据内文件的本质再可以再产生两种文件:java应用 程序(application) 和 java小应用程序(applet);应用程序是由于java解释器(java.exe)执行,小应用程序是由浏览器执行(如IE)
b.提到的编译器或解释器是哪来的呢?应该知道无论哪种语言的程序执行都有自身的运行环境,java语言是由SDK(software development kit也可叫jdk)平台支持,其提供了java虚拟机JVE(java virtual environment),包含运行环境和类加载器;还提供java编译器(javac.exe)和java解释器(java.exe)
c.目前Sun公司已发布了SDK的1.5
下载地址: http://www.javaresearch.org/members/jross/jdk/jdk-1_5_0-windows-i586.exe
如果你使用的windows NT(2000,xp等)操作系统,安装完SDK后还应该正确配置系统的环境变量
如果安装路径是c:/java/里,可如下配置,右键单击我的 电脑->属性->高级->环境变量,然后选择系统变量里的"新建" 设置变量名为path 变量值为
c:/java/jdk1.5/bin; 确定,再选择"新建"设置变量名为classpath 变量值为c:/java/jdk1.5/jre/lib/rt.jar; 确定则配置完毕(一个是配置JVM运行
环境,一个是配置类路径)
(3)java开发的过程和运行环境都明白了之后可以先简单的编写一个程序来运行
a.应用程序:
(a)打开记事本输入
程序代码:
public class Hello
{
public static void main (String args[ ])
{
System.out.println("你好,很高兴学习Java");
}
}
保存为Hello.java(注意保存的名字必须是和主类的名字一样(包括大小写),所谓主类就是包含main方法的类)
(b).按住"窗口+R"组合键打开运行窗口输入cmd进入dos界面转换盘符到刚刚java文件保存路径(例如保存在D盘,输入 D: 回车 )然有执行命令
javac Hello.java 回车.这时会发现在和Hello.java同一个文件夹下生成了Hello.class 字节码文件,因为上面是一个应用程序的格式所以可以通过
java解释器来生成应用程序.继续输入java Hello 回车.屏幕会显示"你好,很高兴学习Java"
b.小应用程序
(a)打开记事本输入
程序代码:
import java.applet.*;
import java.awt.*;
public class Boy extends Applet //entends代表Boy继承了小应用程序Applet类
{
public void paint(Graphics g)
{ g.setColor(Color.red);
g.drawString("我一边喝着咖啡,一边学Java呢",5,30);
g.setColor(Color.blue);
g.drawString("我学得很认真",10,50);
}
}
保存为Boy.java
(b)操作同理到生成Boy.class文件,应为程序的可是可以看出是属于小应用程序的,所以必须在浏览器中执行,众所周知浏览器只能加载网页文件(如html,xml,asp,php等 )所以要把boy.class下载网页文件中,新打开一个记事本,输入
程序代码:
<html>
<applet code=Boy.class width=100, height=100></applet>
</html>
保存为以.html结尾的文件(注意上述的.java或.class或.html文件都是在一个目录下),然后打开此网页文件即可预览
总结:第一节课主要简单的介绍了java的产生背景及其特点,并且从运行原理上引导大家来如何编写并运行java程序
第二节课
标识符 关键字及数据类型
1.标识符: 用来表示类名,变量名,方法名,类型名,数组名,文件名的有效字符序列.
特点:java语言规定标识符由字母,下划线,美元符号和数字组成,并且第一个字符不能是数字字符.
java语言使用unicode标准字符集其包含65536个字符,比C和C++所使用的ASCII码多很多.
2.关键字:编程语言中已经被赋予特定意义的一些单词,不能把这类词作为名字来用,具体的关键字书上列举的很全,略.
3.数据类型
逻辑类型: boolean 用ture(真)和false(假)来赋值,没有赋值默认情况时表示false
整数类型:
(1) int型变量,内存分配4个字节的空间,即32位的空间,所以可以表示-2^31到2^31-1的整数,例如int x=10;
(2) byte型变量,内存分配1个字节的空间,范围从-2^7到2^7-1,例如byte x=127
(3) short型变量,内存分配2个字节的空间,范围从-2^15到2^15-1,例如short x=1000
(4) long型变量,内存分配8个字节的空间,范围从-2^63到2^63-1,例如long x=700000;
字符类型:
(1)常量:java使用unicode字符集,所以字符表中的每个字符都是一个字符常量,用单引号括上,例如'A','你','あ'
注意有些字符不能通过键盘直接输入到字符串中或程序中,这时就需要转意字符常量,例如:'/n'(换行),'/b'(退格),'/''(单引号)等等.
(2)变量:通过关键字char来定义字符变量
char型变量,内存分配2个字节的空间,因为是字符不存在负数的概念,所以范围从0到2^16-1(即65535),例如char name='刘',grade='A'
如果要查一个字符在unicode字符集里的顺序位置,必须使用int类型转换,如(int)'A',但不可以用short型转换(范围只有一半);同样道理如果想得到一个在0到65535之间的数字在unicode字符集中所对应的字符必须使用char型转换,如(char)30000.具体含义理解如下面代码
程序代码:
public class Sign_Num
{ public static void main (String args[ ])
{ char chinaWord='你',japanWord='ぁ';
int p1=36328,p2=38358;
System.out.println("汉字/'你/'在unicode表中的顺序位置:"+(int)chinaWord);
System.out.println("日语/'ぁ/'在unicode表中的顺序位置:"+(int)japanWord);
System.out.println("unicode表中第20328位置上的字符是:"+(char)p1);
System.out.println("unicode表中第12358位置上的字符是:"+(char)p2);
}
}
除了char以外还要稍微介绍一下String,在C或C++中,String是一个以空为终结的字符数组,而在java中它是java.lang包中的String类来创建一个字符串变量,因此字符串变量是一个对象.String的具体介绍在后面会学习到.
浮点类型:
(1)float型 内存分配4个字节,占32位,范围从10^-38到10^38 和 -10^38到-10^-38
例float x=123.456f,y=2e20f; 注意float型定义的数据末尾必须有"f",为了和double区别
(2)double型 内存分配8个字节,范围从10^-308到10^308 和 -10^-308到-10^-308
例double x=1234567.98,y=8980.09d; 末尾可以有"d"也可以不写
基本数据类型转换:
类型按精度从低到高:byte short int long float double
(1)当把级别低的变量赋值给级别高的变量时,系统自动完成数据类型的转换,例
int x=100;
float y;
y=x;
结果y的值应该是100.0
(2)当把级别高的变量赋值给级别低的变量时,必须使用显示类型转换运算,格式为:(类型名)要转换的值;例
int x=(int)23.89f;
结果x的值为23
所以强制转换运算可能导致精度的损失
(3)当把一个整数赋值给一个byte,short,int或long型变量时,不可以超出这些变量的取值范围,否则必须进行类型转换运算,例
byte a=(byte)128;
byte b=(byte)-129;//超出范围
结果a值为128,b值为127
用下面的代码来复习下:
程序代码:
public class Example2_2
{ public static void main (String args[ ])
{ int c=2200;
long d=8000;
float f;
double g=123456789.123456789;
c=(int)d;
f=(float)g; //导致精度的损失
System.out.print("c= "+c);
System.out.println(" d= "+d);
System.out.println("f= "+f);
System.out.println("g= "+g);
}
}
java中的数组
格式: 数组元素类型 数组名字[ ] 或
数组元素类型 [ ]数组名字 例
float boy[];
创建数组: 数组名字=new 数组元素的类型[数组元素的个数]; 例
boy=new float[2]; //意思是boy获得了两个用来存放float型数据的空间
注意:和C和C++不同的是java允许使用int型变量来指定数组大小,例
int size=30;
double number=new double[size];
程序代码复习数组:
程序代码:
public class Example2_3
{ public static void main(String args[])
{ int a[]={100,200,300};
int b[]={10,11,12,13,14,15,16};
b=a;
b[0]=123456;
System.out.println("数组a:"+a[0]+","+a[1]+","+a[2]);
System.out.println("数组b:"+b[0]+","+b[1]+","+b[2]);
System.out.println("数组b的长度:"+b.length);
}
}
总结:本节课把编程语言中的基础-关键字,数据类型等概念复习一遍,尤其是在java中的用法,为以后在使用变量的学习中做基础.
|
|
第三节课
运算符 表达式和语句
因为大家肯定都学过C或C++的,所以这部分的内容自己可以回忆,都是一样的,略.
类和对象
1.类:类是组成java程序的基本要素.类封装了一类对象的状态和方法.类是用来定义对象的模板.
类的实现包括两个部分:类声明和类体.基本格式为:
程序代码:
class 类名
{
类体的内容
}
其中class是关键字,用来类定义."class 类名"是类的声明部分,类名必须是合法的java标识符,{}之间的内容是类
体,由变量的定义和方法(函数)的定义组成.
所谓变量定义,如int x或 intx=10;
但不能出现除声明以外的变量操作语句,如 int x; x=10;//是错误的
(1)变量:成员变量和局部变量
a.成员变量在整个类内都有效,局部变量只在定义它的方体体内有效.例:
程序代码:
class Sun
{ int distance;
int find()
{int a=12;
distance=a;//合法,distance是成员变量在整个类内有效
return distance;
}
void g()
{inty;
y=a; //非法,a是局部变量,不在find()中就会失效
}
}
注意:成员变量在它在类中书写的先后位置无关.但不提倡把成员变量的定义分散的写在方法之间或类体的后面.
b.成员变量又分为实例成员变量(实例变量)和类成员变量(类变量)
类变量是指变量类型前加关键字 static 的成员变量,也称为静态变量.例:
程序代码:
class Sun
{float x; //实例变量
static int y; //类变量
}
c.当局部变量与成员变量名字相同时,则成员变量被隐藏,即这个成员变量在这个方法内暂时失效,例:
程序代码:
class Tom
{int x=90,y;
void f()
{int x=3;
y=x; //y得到的值应是3,因为成员变量x=90与y在此方法体里失效
}
}
但如果想在方法体内使用成员变量则通过关键字 this 来实现,例:
程序代码:
class 三角形
{float sideA,sideB,sideC,lengthSum;
void setside(float sideA,float sideB,float sideC)
{this.sideA=sideA; //this.sideA表示成员变量sideA
this.sideB=sideB;
this.sideC=sideC;
}
}
(2)方法
方法即是函数,包括声明和定义,在类体中是函数的定义,与C或C++中函数的声明或定义格式是一样的.
a.类方法和实例方法
实例方法即在方法返回值类型前加上关键字static,例:
程序代码:
class A
{int a;
float max(float a,float b)//实例方法
{//...}
static float min()//类方法
{//...}
}
[code]
b.方法的重载
方法的重载定义为多个函数定义时函数名必须相同,参数的类型和个数必须不同.例:
[code]
class Area
{float getArea(float r)
{return 3.14*r*r;}
double getArea(float x,int y)
{return x*y;}
float getArea(float x,float y)
{return x+y;}
double getArea(float x,float y,float z)
{return x*y*z*0.2;}
}
注意:在函数的重载中方法的返回值类型和参数的名字不参加比较.
c.构造方法
构在方法是一种特殊方法,它的名字必须与类名完全相同,而且没有类型,构造方法也可以重载的.
用法:一般用类创建对象时,使用构在方法,后面会有详细说明.
程序代码:
class 梯形
{float 上底,下底,高;
梯形()
{上底=60;
下底=100;
高=20}
梯形(float x,float y,float z)
{上底=x;
下底=y;
高=z;
}
}
2.对象
类是创建对象的模板.当使用一个类创建了一个对象时,也就是说给出了这个类的一个实例.
(1)对象的创建:包括对象的声明和为对象分配内存空间
声明格式: 类的名字 对象的名字;
分配内存空间格式; 类的名字 对象的名字=new 类的名字(); 例:
程序代码:
class XiyoujiRenwu
{ float height,weight;
String head, ear,hand,foot, mouth;
void speak(String s)
{ System.out.println(s);
}
}
class A
{ public static void main(String args[])
{ XiyoujiRenwu zhubajie; //声明对象
zhubajie=new XiyoujiRenwu(); //为对象分配内存,使用new 运算符和默认的构造方法
}
}
程序代码:
class Point
{ int x,y;
Point(int a,int b)
{ x=a;
y=b;
}
}
public class A
{ public static void main(String args[])
{ Point p1,p2,p3; //声明对象p1和p2
p1=new Point(10,10); //为对象分配内存,使用 new 和类中的构造方法
p2=new Point(23,35); //为对象分配内存,使用 new 和类中的构造方法
// p3=new Point(); 错误语句!当类中定义了一个或多个构造方法,那么java不提供默认的构造方法
}
}
(2)对象的内存模型
声明对象时,内存不分配空间,只存在一个空对象;当用new 分配内存空间时,类中的成员变量就会被分配到相应的空
间中,并通过构造方法(如果有)初始化,函数的入口地址也会被载入内存.但此时局部变量不会被分配内存空间,只有
当对象调用类中的函数时,函数中的局部变量才被分配空间,但函数执行完后,局部变量释放内存,所以如果在函数中
没有对局部变量进行初始化,就没有默认值,因此在使用局部变量之前,要事先为其赋值.
创建多个对象之间没有相互状态影响,个用个自的内存空间.
(3)对象的使用
对象不仅可以改变自己变量的状态,而且还拥有了使用创建它的那个类中的方法的能力,对象通过使用这些方法可以产生一定的行为.通过使用运算符" . "对象可以实现对自己的变量的访问和方法的调用.具体使用如程序例子:
程序代码:
class XiyoujiRenwu
{ float height,weight;
String head, ear,hand,foot,mouth;
void speak(String s)
{ head="歪着头";
System.out.println(s);
}
}
class Example4_3
{ public static void main(String args[])
{ XiyoujiRenwu zhubajie,sunwukong;//声明对象
zhubajie=new XiyoujiRenwu(); //为对象分配内存
sunwukong=new XiyoujiRenwu();
zhubajie.height=1.80f; //对象给自己的变量赋值
zhubajie.head="大头";
zhubajie.ear="一双大耳朵";
sunwukong.height=1.62f; //对象给自己的变量赋值
sunwukong.weight=1000f;
sunwukong.head="绣发飘飘";
System.out.println("zhubajie的身高:"+zhubajie.height);
System.out.println("zhubajie的头:"+zhubajie.head);
System.out.println("sunwukong的重量:"+sunwukong.weight);
System.out.println("sunwukong的头:"+sunwukong.head);
zhubajie.speak("俺老猪我想娶媳妇"); //对象调用方法
System.out.println("zhubajie现在的头:"+zhubajie.head);
sunwukong.speak("老孙我重1000斤,我想骗八戒背我"); //对象调用方法
System.out.println("sunwukong现在的头:"+sunwukong.head);
}
}
程序代码:
class 梯形
{ float 上底,下底,高,面积;
梯形(float x,float y,float h)
{ 上底=x;
下底=y;
高=h;
}
float 计算面积()
{ 面积=(上底+下底)*高/2.0f;
return 面积;
}
void 修改高(float height)
{ 高=height;
}
float 获取高()
{ return 高;
}
}
public class Example4_4
{ public static void main(String args[])
{ 梯形 laderOne=new 梯形(12.0f,3.5f,50),laderTwo=new 梯形(2.67f,3.0f,10);
System.out.println("laderOne的高是:"+laderOne.获取高());
System.out.println("laderTwo的高是:"+laderTwo.获取高());
System.out.println("laderOne的面积是:"+laderOne.计算面积());
System.out.println("laderTwo的面积是:"+laderTwo.计算面积());
laderOne.修改高(10);
float h=laderOne.获取高();
laderTwo.修改高(h*2);
System.out.println("laderOne现在的高是:"+laderOne.获取高());
System.out.println("laderTwo现在的高是:"+laderTwo.获取高());
System.out.println("laderOne现在的面积是:"+laderOne.计算面积());
System.out.println("laderTwo现在的面积是:"+laderTwo.计算面积());
}
}
总结:介绍了类和对象及通过类体中的变量和方法来实现对对象的应用
第四节课
static的用法和包
上次课提到了static关键字,下面介绍一下具体的用法
1.实例变量和类变量
我们已经知道一个类可以使用new关键字可以创建多个不同的对象,这些对象被分配不同的内存空间.具体说的话,不同的对象的实例变量将被分配不同的内存空间,如果类中的成员变量有类变量,那么所有对象的这个类变量都分配分配给相同的一处内存,改变其中一个对象的这个类变量会影响其他对象的这个类变量,也就是说对象共享类变量.如
程序代码:
class Takecare
{
static int x;
int y;
}
class A
{
Takecare A1=now Takecare();
Takecare A2=now Takecare();
//在此代码中如果给x赋值那么,A1.x 和A2.x指的是同一个内存空间,无论那个对象修改都是对一个x进行操作
}
当java程序执行时,类的字节码文件被加载到内存,如果该类没有创建对象,类的实例成员变量不会被分配内存.但是类中的类变量被加载到内存时,会被分配相应的内存空间.简单的说就是,没创建对象当类加载到内存时候,只有类变量(static)会被分配内存空间,只有创建对象时候实例变量才会被分配空间的.
像上面的例子里,在没有创建Takecare的对象时候,Takecare::x是有空间的即此时可以访问的,当且仅当Takecare A1=now Takecare()时A1.y才会有内存空间.
通过下面的例子体会一下,不同的对象会共享一个底
程序代码:
class 梯形
{ float 上底,高;
static float 下底;
梯形(float x,float y,float h)
{ 上底=x; 下底=y; 高=h;
}
float 获取下底()
{ return 下底;
}
void 修改下底(float b)
{ 下底=b;
}
}
class Example4_7
{ public static void main(String args[])
{ 梯形 laderOne=new 梯形(3.0f,10.0f,20),laderTwo=new 梯形(2.0f,3.0f,10);
梯形.下底=200; //通过类名操作类变量
System.out.println("laderOne的下底:"+laderOne.获取下底());
System.out.println("laderTwo的下底:"+laderTwo.获取下底());
laderTwo.修改下底(60); //通过对象操作类变量
System.out.println("laderOne的下底:"+laderOne.获取下底());
System.out.println("laderTwo的下底:"+laderTwo.获取下底());
}
}
2.实例方法和类方法
和实例变量和类变量的描述时是一样,当类的字节码加载到内存是类方法的入口地址就会被加载到相应的内存空间,即此时可以访问;而当且只有创建对象之后,实例方法的入口地址才会被加载到内存中,这是才可以调用.
既然知道了实例方法和类方法开始生效的时间,可以知道他们可以调用何种类的成员变量:
1)类方法因为是在创建对象之前就已经生效了,这时候实例变量还没有被分配内存空间,只有类变量被分配了内存空间,所以类方法只能调用被分配内存空间的变量即类变量;
2)实例方法是在创建对象之后才开始生效的,这是无论是类变量还是实例变量都已经被分配了内存空间,所以实例方法生效后可以调用类变量和实例变量.
程序代码:
class Fibi
{ public static long fibinacii(int n)
{ long c=0;
if(n==1||n==2)
c=1;
else
c=fibinacii(n-1)+fibinacii(n-2);
return c;
}
}
public class Example4_8
{ public static void main(String args[])
{ System.out.println(Fibi.fibinacii(7));// 可见还没哟创建Fibi的对象时就可以调用fibinacii()方法,因为属于static修饰的类变量
}
}
包
包是java中有效的管理类的一个机制,包有类似于目录结构的层次结构.通过import关键字可以在程序中使用包
语法格式:
import<包名>.*; //包含包中的所有类
import<包名><类名>//包含包中的指定类
例如:
import java.awt.*; //包含java.awt包中的所有类
import java.awt.JButton;//包含java.awt包中的JButton类
既然知道了怎么使用,那如何制作包呢,可以通过关键字package声明包语句.package语句作为java源文件的一个条语句,指明该源文件定义的类的所在包.
语法格式:
package 包名;
如果源文件中省略了package语句,那么源文件中定义命名的类将被隐含的认为是无名包的一部分,即源文件中定义命名的类在同一个包中,但该报没有名字而已.
包还可以是一个合法的表示符,也可以是若干个标识符加"."分割而成,如
package sunrise;
package sunrise.com.cn
程序如果使用了包的语句,如
package desney.tom;
那么目录必须包含有如下结构,如
C:/cartoon/desney/tom
并且要将源文件保存在目录C:/cartoon/desney/tom中,才能使用这个包,然后编译源文件:
C:/cartoon/desney/tom/javac 源文件.java
或
jacac C:/cartoon/desney/tom/源文件.java
从一个例子体会此过程:
程序代码:
package desney.tom;
public class PrimNumber
{ public void getPrimnumber(int n)
{ int sum=0,i,j;
for(i=1;i<=n;i++)
{ for(j=2;j<=i/2;j++)
{ if(i%j==0)
break;
}
if(j>i/2)
System.out.print(" "+i);
}
}
public static void main(String args[])
{ PrimNumber p=new PrimNumber();
p.getPrimnumber(20);
}
}
保存上述源文件到C:/cartoon/desney/tom下,然后编译
C:/cartoon/desney/tom/javac PrimNumber.java
运行程序时必须到desney/tom的上一层目录cartoon中来运行,如
C:/cartoon/java desney.tom.PrimNumber
因为起了报名,类PrimNumber的全名应该为desney.tom.PrimNumber
导入这个包时也要注意名称
程序代码:
import desney.tom.PrimNumber;
所以用package可以把认可类都打包然后在其他程序中使用,于c或c++中的头文件有着相似的效果,差别就是java中的包里只能是类
注意:使用import可以导入包中的类.在编写源文件时,除了自己编写的类外,经常需要使用java提供的去多类,这些类可能不在一个包中.在学习java时,使用已经存在的类,避免一切从头做起,这是面向对象编程的一个重要方面.
总结:具体的把static修饰的变量和方法的使用方法做了介绍;对java中包有了初步认识
第五节课
继承与访问权限
继承的概念:
继承是一种由己有的类创建新类的机制.利用继承,可以先创建一个共有属性的一般类,根据该一般类再创建具有特殊属性的新类,新类继承一般类的状态和行为,并根据需要增加它自己的新的状态和行为.由继承而得到的类称为子类,被继承的类称为父亲.java不支持多重继承,即子类只能有一个父亲(有别于C++的语法).
子类:
在类的声明中,通过使用关键字extends来声明一个类的子类,格式为:
class 子类名 extends 父类名
{
//...
}
注:如果一个类声明中没有使用extends关键字,这个类被系统默认为是Object的直接子类,Object是java.lang包中的类
子类的继承性
所谓子类的继承父类的成员变量作为自己的一个成员变量,就好像它们是在子类中直接声明一样,可以被子类中自己声明的任何实例方法操作,也就是说,一个子类继承的成员应当是这个类的完全意义的成员,如果子类中声明的实例方法不能操作父类的某个成员变量,该成员变量就没有被子类继承;所谓子类继承父类的方法作为子类中的一个方法,就象它们在子类中直接声明一样,可以被子类中自己声明的任何实例方法调用.
1) 子类和父类在同一包中的继承性
如果子类和父类在同一包中,那么子类自然地继承了其父类中不是private的成员变量作为自己的成员变量,并自然地继承了其父类中不是private的成员方法作为自己的成员方法,继承的成员的访问权限保持不变. 例如:
程序代码:
class Father
{ private int money;
float weight,height;
String head;
void speak(String s)
{ System.out.println(s);
}
}
class Son extends Father
{ String hand,foot;
}
public class Example4_15
{ public static void main(String args[])
{ Son boy;
boy=new Son();
boy.weight=1.80f;
boy.height=120f;
boy.head="一个头";
boy.hand="两只手 ";
boy.foot="两只脚";
boy.speak("我是儿子");
System.out.println(boy.hand+boy.foot+boy.head+boy.weight+boy.height);
}
}
2)子类和父类不在同一个包中的继承性
如果子类和父类不在同一个包中,那么,子类继承了父亲的protected,public成员变量和成员方法作为子类的成员变量和方法,继承的成员或方法的访问权限保持不变.如果子类和父类和父类不在同一包中,子类不能继承父类的友好变量和友好方法.
上面提到的private,protected,public以及友好(friendly)等字眼就是访问权限,下面来讲解一下他们具体的意义
当一个类创建了一个对象之后,该对象可以通过"."运算符操作自己的变量,使用类中的方法,但对象操作自己变量和使用类中的方法是有一定限制的.所谓访问权限是指对象是否可以通过"."运算符作自己的变量或使用类中的方法.访问权限的修饰符有private,protected和public,都些都是java中的关键字,还有就是友好(friendly)但它不是关键字只是种默认时的权限.访问权限由高到低:public->protected->friendly->private
具体分析如下:
用关键字private修饰的成员变量和方法称为私有变量和方法.如
class room
{private float weight;
private float f(float a,float b){//....}
}
当在另外一个类中用类Tom创建一个对象后,该对象不能访问自己的私有变量和私有方法.如
class jerry
{void g()
{Tom cat=new Tom;
cat.weight=23f;//错误,因为weight被私有修饰,不可一被对象访问
cat.f(3f,4f);//错误,f()被private修饰,不能被使用
}
}
对于私有成员变量或方法,只有在本类中创建该类的对象时,这个对象才能访问自己的私有成员变量和类中的私有方法.如
程序代码:
class Example4_14//所有代码都包含在一个类中
{ private int money;
Example4_14()
{ money=2000;
}
private int getMoney()
{ return money;
}
public static void main(String args[])
{ Example4_14 exa=new Example4_14();
exa.money=3000;
int m=exa.getMoney();
System.out.println("money="+m);
}
}
当然如果类中只有变量是私有的,而能操作此变量的函数是共有的,就可以间接对此私有变量进行操作,如
程序代码:
class room
{private float weight;
float f(float weight){this.weight=weight;}//都在一个类中方法可以访问变量
}
class jerry
{void g()
{Tom cat=new Tom;
cat.f(23f);//正确,f()没有被private修饰可以访问,这样就间接的操作了weight,相当于cat.weight=23f;
}
}
公有关键字public顾名思义,是访问权限最大的了,只要被它修饰无论在哪里都可以访问到
class room
{public float weight;
public float f(float a,float b){//....}
}
class jerry
{void g()
{Tom cat=new Tom;
cat.weight=23f;//正确
cat.f(3f,4f);//正确
}
}
友好(friendly)的概念,即不被private,protected,public修饰的成员变量和方法被称为友好变量和友好方法,如.
class room
{float weight;//友好变量
float f(float a,float b){//....}//友好方法
}
当在另一个类中用类Tom创建了一个对象后,如果这个类与Tom类在同一个包中,那么该对象能访问自己的友好变量和友好方法.在任何一个与Tom同一个包中的类,也可以通过Tom类的类名访问Tom类的类友好成员变量和类友好方法.
假如jerry和Tom是同一个包中的类,那么下述jerry类中的cat.weight.cat.f(3,4)都是合法的
class jerry
{void g()
{Tom cat=new Tom;
cat.weight=23f;//正确
cat.f(3f,4f);//正确
}
}
受保护关键字protected的用法只有在继承中才能体现出来,就是为什么先把继承引出来的原因,可以看看上面继承时说的子类和父类不在同一包中时,只有public和protected修饰的变量和方法才可以被继承使用.除了在继承这点的区别,在同一包中时,用法和作用和友好是一摸一样的,注意体会一下.
来全面总结一下protected的用法:
一个类A中的protected成员变量和方法可以被它的直接子类和间接子类继承,比如B是A的子类,C是B的子类,D又是C的子类,那么B,C,D类都继承了A的protected的成员变量和方法.如果用D类在D本身创建了一个对象,那么该对象总是可以通过"."运算符访问继承的或者自己定义的protected变量和方法,但是,如果在另一个类中,比如Other类,用D类创建了一个对象object,该对象通过"."运算符访问protected变量和方法的权限如下所述:
1)子类D中亲自声明的protected成员变量和方法,不可能是从别的类继承来的,object访问这些非继承的protected成员变量和方法时,只要other类和D类在同一包中就可以了.
2)如果子类D的对象的protected成员变量或方法是从父类继承来的,那么就要一直追追述到该protected成员变量或方法的"祖先"类,即最先头的父类A,如果Other类和A类在同一包中,object对象能访问继承的protected变量或protected方法.
访问权限不只能修饰变量和成员还是可以修饰类的,但protected和private不可以
类声明时,如果关键字class前面加上public关键字,就称这样的类是一个public类,如
public class A
{//...}
可以在任何另外一个类中,使用public类创建对象.如果一个类不加public修饰,如
class A
{//...}
这样的类称为友好类,那么另外一个类中使用友好类创建对象时,要保证它们是在同一个包中
特别注意:不能用protected和private修饰类
总结:简单的引入了继承和访问权限的概念,重在体会,为下次课学习多态,抽象类以及接口打下基础
第六节课
多态与抽象类
多态是java中一个比较重要的概念,在认识和理解它之前先熟悉一下下面的几个概念.
1.成员变量的隐藏和方法的重写
成员变量的隐藏:子类可以隐藏继承的成员变量,对于子类可以从父类继承成员变量,只要子类中定义的成员变量和父类中的成员变量
同名时,子类就隐藏了继承的成员变量.但不提倡这种做法,子类总是自己定义变量而隐藏父类的变量,这样会浪费很多空间.
方法的重写:子类可以隐藏已继承的方法,子类通过方法重写来隐藏继承的方法.具体是指,子类中定义一个方法,并且这个方法的名字
,返回值类型,参数个数和类型与父类继承的方法完全相同.
注意:方法的重写与方法的重载要分清楚,回顾一下方法的重载的定义为方法名必须相同,参数的类型和个数必须不同.
用下面的程序好好体会一下方法的重写(还有重载)
程序代码:
class Chengji
{ float f(float x,float y)
{ return x*y;
}
}
class Xiangjia extends Chengji
{
float f(float x,float y) //方法的重写,方法的名字,返回值类型,参数个数和类型与父类完全相同
{ return x+y ;
}
//float f(float,x,int y) {return x-y;}错误的语句,在方法名相同的情况下既不属于"重写"也不属于"重载"
//float f(int x,int y,int z){z=x-y; return z;}方法的重写,琢磨一下定义就知道了
}
public class Example
{ public static void main(String args[])
{ Xiangjia sum;
sum=new Xiangjia();
float c=sum.f(4,6);//调用的是子类里的f(),因为子类重写了父类的f()
//c=sum.f(1,2,3); 这个则是调用了重载的函数;当然具体调用那个要根据实参的个数和类型来判断喽
System.out.println(c);
}
}
如果没有重写父类的方法,那使用父类中的方法时就正常使用,看看下面这个混合使用的例子
程序代码:
class Area
{ float f(float r )
{ return 3.14159f*r*r;
}
float g(float x,float y)
{ return x+y;
}
}
class Circle extends Area
{ float f(float r)
{ return 3.14159f*2.0f*r;//重写
}
}
public class Example4_17
{ public static void main(String args[])
{ Circle yuan;
yuan=new Circle();
float length=yuan.f(5.0f);
float sum=yuan.g(232.645f,418.567f); //父类中的方法正常使用
System.out.println(length);
System.out.println(sum);
}
}
注意:重写父类的方法时,不可以降低方法的访问权限.下面的例子中,子类重写父类的方法f,该方法在父类中的访问权限实protected
级别,子类重写时不允许级别低于protected级别.
程序代码:
class Chengji
{
protected float f(float x,float y)
{return x*y;}
}
class Xiangjia extends Chengji
{
float f(float x,float y)//错误,friendly的权限低于protected
{return x+y;}
}
class Xiangjian extends Chengji
{
public float f(float x,float y)//正确,public的权限高于protected
{return x-y;}
}
子类重写了父类中的方法后也有办法访问被隐藏的父类的方法的,这就要引入super关键字了
1).使用关键字super调用父类的构造方法
子类不继承父类的构造方法,因此,子类如果想使用父类的构造方法,必须在子类的构造方法中使用,并且必须使用关键字super来表示
,而且super必须是子类构造方法中的头一条语句.例如:
程序代码:
class Student
{ int number;String name;
Student()
{
}
Student(int number,String name)//父类的构造方法
{ this.number=number;
this.name=name;
System.out.println("I am "+name+ "my number is "+number);
}
}
class Univer_Student extends Student
{ boolean 婚否;
Univer_Student(int number,String name,boolean b) //子类的构造方法
{ super(number,name);//子类构造方法中的第一条语句
婚否=b;
System.out.println("婚否="+婚否);
}
}
public class Example4_23
{ public static void main(String args[])
{ Univer_Student zhang=new Univer_Student(9901,"和晓林",false);
}
}
注意:如果在子类的构造方法中,没有使用关键字super调用父类的某个构造方法,那么默认有super();语句,即调用父类不带参数的构
造方法.
如果类里定义一个或多个构造方法,那么java不提供默认的构造方法(不带参数的构造方法),因此,当在父类中定义多个构造方法时,
应当包括一个不带参数的构造方法,以防子类省略super时出现错误.
2).使用关键字super操作被隐藏的成员变量和方法
如果在子类中想使用被子类隐藏了的父类的成员变量或方法就可以使用关键字super.比如super.x,super.play(),就是被子类隐藏的
父类的成员变量x和方法play().例如
程序代码:
class Sum
{ int n;
float f()
{ float sum=0;
for(int i=1;i<=n;i++)
sum=sum+i;
return sum;
}
}
class Average extends Sum
{ int n;
float f()
{ float c;
super.n=n;//调用被隐藏的父类中的变量n
c=super.f();//调用父类中被隐藏的方法f()
return c/n;
}
float g()
{ float c;
c=super.f();//调用父类中被隐藏的方法f()
return c/2;
}
}
public class Example
{ public static void main(String args[])
{ Average aver=new Average();
aver.n=100;
float result_1=aver.f();
float result_2=aver.g();
System.out.println("result_1="+result_1);
System.out.println("result_2="+result_2);
}
}
2.对象的上转型对象(具体用途在抽象类中体现出来)
我们经常说"老虎是哺乳动物","狗是哺乳动物"等.若哺乳类是老虎类的父类,这样说当然正确,但当说老虎是哺乳动物时,老虎讲失掉老虎独有的属性和功能.下面就介绍对象的上转型对象.
假设B类是A类的子类或间接子类,当用子类B创建一个对象,并把这个对象的引用放到A类的对象中时,如
A a;
a=new B();
或
A a;
B b=new B();
a=b;
那么,称这个A类对象a是子类对象b的上转型对象(好比说:"老虎是哺乳动物").对象的上转型型对象的实体是子类负责创建的,但上转型对象会失去原来的一些功能.
上转型对象具有如下特点:
1)上转型对象不能操作子类新增的成员变量(失掉了这部分属性),不能使用子类新增的方法(失掉了一些功能).
2)上转型对象可以操作子类继承或隐藏的成员变量,也可以使用子类继承的或重写的方法.
3)上转型对象操作子类继承或重写的方法时,就时通知对应的子类对象去调用这些方法.因此,如果子类重写了父类的某个方法后,对象的上转型对象调用这个方法时,一定是调用了这个重写的方法.
4)可以讲对象的上转型对象再强制转换到一个子类的对象,这时,该子类对象又具备了子类的所有属性和功能.
注意:a.不要将父类创建的对象和子类对象的上转型对象混淆;b.不可以将父类创建的对象的引用赋值给子类声明的对象(不能说:"哺乳动物是老虎")
体会下面的例子来掌握上转型对象的概念
程序代码:
class 类人猿
{ private int n=100;
void crySpeak(String s)
{ System.out.println(s);
}
}
class People extends 类人猿
{ void computer(int a,int b)
{ int c=a*b;
System.out.println(c);
}
void crySpeak(String s)
{ System.out.println("**"+s+"**");
}
}
class Example
{ public static void main(String args[])
{ 类人猿 monkey=new People(); //把子类创建的对象赋给父类创建的对象monkey,则monkey现在就是一个上转型对象
monkey.crySpeak("I love this game");//monkey可以调用子类中继承或重写的方法
People people=(People)monkey; //把上转型对象强制转换为子类的对象,赋值子类创建的对象
people.computer(10,10);
}
}
3.final 关键字
final可以修饰类,成员变量,方法中的参数.如变量被其修饰后就相当于C++中常量的概念,不可以再对其更改
final类不可以被继承,即不能有子类,如
A就是一个final类.有时候出于安全性的考虑,将一些类修饰为final类.例如:java提供的String类,它对于编辑器和解释器的正常运行有很重要的作用,对它不能轻易改变.因此它被修饰为final类.
如果一个方法被修饰为final方法,则这个方法不可以被重写;
如果一个成员变量被修饰为final的,就是常量,常量必须赋初始值,而且不能再发生变化;
如果方法的参数被修饰为final的,该参数的值不能被改变.
体会下面的例子:
程序代码:
class A
{
final double PI=3.1415926;//变量PI为常量不可再被更改
public double getArea(final double r)//参数r被初始化后不可以被更改
{ //r=20;错误,r不可以被修改
return PI*r*r;
}
}
public class
{ public static void main(String args[])
{ A a=new A();
System.out.println("面积:"+a.getArea(100));
}
}
4.准备工作都做完了,下面让我们开始正式学习多态性
我们经常说:"哺乳动物有很多叫声",比如,"汪汪","喵喵","嚎","吼"等,这就是叫声的多态.
当一个类有很多子类时,并且这些子类都重写了父类中的某个方法.那么当把子类创建的对象的引用放到一个父类的对象中时,就得到
了该对象的一个上转型对象,那么这个上转的对象在调用这个方法时就可能具有多种形态,因为不同的子类在重写父类的方法时可能
产生不同的行为,比如,狗类的上转型对象调用"叫声"方法时产生的行为是"汪汪",而猫类的上转型对象调用"叫声"的方法时,产生的行为是"喵喵",等等.
多态性就是指父类的某个方法被其子类重写时,可以各自产生自己的功能行为,下面的例子展示了多态性.
程序代码:
class 动物
{ void cry()
{
}
}
class 狗 extends 动物
{ void cry()//方法的重写
{ System.out.println("汪汪.....");
}
}
class 猫 extends 动物
{ void cry()//方法的重写
{ System.out.println("喵喵.....");
}
}
class Example4_20
{ public static void main(String args[])
{ 动物 dongwu;
dongwu=new 狗();
dongwu.cry();
dongwu=new 猫();
dongwu.cry();
}
}
5.抽象(abstract)类
用关键字abstract修饰的类称为抽象(abstract)类.如
abstract class A
{//...}
abstract类的几个特点:
1)abstract类中可以有abstract方法
与普通的类相比,abstract类可以有abstract方法.对于abstract方法,只允许声明,不允许实现,而且不允许使用final修饰abstract方法.下面的A类中的min()方法就是abstract方法
abstract class A
{ abstract int min(int x,int y);
int max(int x,int y)
{ return x>y?x:y;
}
}
注意:abstract类中也可以没有abstract方法
2)对于abstract类,不能使用new运算符创建该类的对象,需产生其子类,由子类创建对象,如果一个类是abstract类的子类,它必须具体实现abstract方法,这就是为什么不允许使用final修饰abstract方法的原因.体会一下下面的例子:
程序代码:
abstract class A
{ abstract int min(int x,int y);
int max(int x,int y)
{ return x>y?x:y;
}
}
class B extends A
{ int min(int x,int y) //子类中具体实现了min()的功能
{ return x<y?x:y;
}
}
public class Example
{ public static void main(String args[])
{ A a;
B b=new B();
int max=b.max(12,34);
int min=b.min(12,34);
System.out.println("max="+max+" min="+min);
a=b;
max=a.max(12,34);
System.out.println("max="+max);
}
}
一个abstract类只关心它的子类是否具有某种功能,并不关心功能的具体行为,功能的具体行为由子类负责实现,抽象类中的抽象方法可以强制子类必须给出这些方法的具体实现.理解下面的例子来充分学习抽象类.
程序代码:
abstract class 图形
{ public abstract double 求面积();//相当于给子类们发出了求面积的具体表现命令
}
class 梯形 extends 图形
{ double a,b,h;
梯形(double a,double b,double h)
{ this.a=a;
this.b=b;
this.h=h;
}
public double 求面积()//子类梯形类实现了求面积功能
{ return((1/2.0)*(a+b)*h);
}
}
class 圆形 extends 图形
{ double r;
圆形(double r)
{ this.r=r;
}
public double 求面积()//子类圆形类实现了求面积功能
{ return(3.14*r*r);
}
}
class 堆
{ 图形 底;//只是声明
double 高;
堆(图形 底,double 高)//调用时候就可以用上转型对象来解释为什么了
{ this.底=底;
this.高=高;
}
void 换底(图形 底)
{ this.底=底;
}
public double 求体积()
{ return (底.求面积()*高)/3.0;
}
}
public class Example
{ public static void main(String args[])
{ 堆 zui;
图形 tuxing;
tuxing=new 梯形(2.0,7.0,10.7);
System.out.println("梯形的面积"+tuxing.求面积());
zui=new 堆(tuxing,30);
System.out.println("梯形底的堆的体积"+zui.求体积());
tuxing=new 圆形(10);
System.out.println("半径是10的圆的面积"+tuxing.求面积());
zui.换底(tuxing);
System.out.println("圆形底的堆的体积"+zui.求体积());
}
}
总结:初步了解了多态性和抽象类及与其相关的概念.
第七节课
接 口
接口在java中尤其是面向对象语言中是一个很重要的概念.
java中使用接口概念的原因:java不支持多继承性,即一个类只能有一个父类.单继承性使得java简单,易于管理程序.为了克服单继
承的缺点,java使用了接口.
一个类可以实现多个接口.使用关键字interface来定义一个接口.接口的定义和类的定义很相似,分为接口的声明和接口体.
1.接口声明及接口体
前面曾使用class关键字来声明类,接口通过使用关键字interface来声明.格式
interface 接口的名字
接口体中包含常量的定义和方法定义两部分.接口体中只进行方法的声明,不允许提供方法的实现,所以,方法的定义没有方法体,且
用";"结尾.如
程序代码:
interface Printable
{
final int max=100;
void add();
float sum(float x,float y);
}
2.接口的使用
一个类通过使用关键字implements声明自己使用一个或多个接口.如果使用多个接口,用逗号隔开接口名.如:
class A implements Printable,Addable
类A使用接口Printable 和接口Addable
子类使用接口格式也一样,如:
class B extends A implements Printable,Addable
子类B使用接口Printable 和接口Addable
如果一个类使用了某个接口,那么这个类必须实现该接口的所有方法,即为这些方法提供方法体.需要注意的是:在类中实现接口的
方法时,方法的名字,返回值类型,参数个数及类型,必须与接口中的完全一致.特别要注意的是:接口中的方法默认是public
abstract方法,所以类在实现接口方法时必须给出方法体,并且一定要用public来修饰,而且接口中的常量默认是public static常
量.
由于接口体中只有常量定义和public的abstract方法定义,程序在编写接口时,允许省略常量前面的public,final和static修饰,也
允许省略方法前的public和abstract修饰.另外,如果接口方法的返回类型不是void型,那么在类中实现接口方法时,方法体至少要
有一个return语句;如果是void型,类体除了两个大括号外,也可以没有任何语句.
java提供的接口都在相应的包中,通过引入包可以使用java提供的接口.也可以自己定义接口,一个java源文件就是由类和接口组成的.
虽然"使用接口"的描述的很罗嗦,看看下面的例子就容易掌握了
程序代码:
interface Computable//定义接口
{ int MAX=100;
int f(int x);//声明方法(是public abstrac型的,只是省略了关键字)
}
class China implements Computable
{ int number;
public int f(int x) //不要忘记public关键字
{ int sum=0;
for(int i=1;i<=x;i++)
{ sum=sum+i;
}
return sum;
}
}
class Japan implements Computable
{ int number;
public int f(int x)//不要忘记public关键字
{ return 44+x;
}
}
public class E
{ public static void main(String args[])
{ China zhang;
Japan henlu;
zhang=new China();
henlu=new Japan();
zhang.number=991898+Computable.MAX;
henlu.number=941448+Computable.MAX;
System.out.println("number:"+zhang.number+"求和"+zhang.f(100));
System.out.println("number:"+henlu.number+"求和"+henlu.f(100));
}
}
还要注意的就是如果一个类声明实现一个接口,但没有实现接口中的所有方法,那么这个类必须是abstract类,如
程序代码:
interface Computable
{final int max=100;
void speak(String s);
int f(int x);
abstract class A implements Computable//类A使用的接口但类体中并没有实现接口中speak()的方法,所以此类必须是抽象类
{public int f(int x)//只是实现了接口中的一个方法
{int sum=0;
for(int i=1;i<=x;i++)
sum+=i;
return
}
}
类实现的接口方法以及接口中的常量可以被类的对象调用,而且常量也可以用类名或接口名直接调用.这理解不难,因为前面讲过
final修饰的常量,在内存程序加载字节码时,内存就给常量分配了一个固定空间,不可以改变(直到程序结束),所以这个地址是固定
了,并且这个常量是来自一个接口,所以此接口名或者使用这个接口的类的对象可以直接调用这个常量喽.方法也是一样的.
接口声明时,如果关键字interface前面加public关键字,就称这样的接口是一个public接口.public接口可以被任何一个类使用.如
果一个接口不加public修饰,就称为友好接口类,友好接口可以被同一包中的类使用.
如果父类使用了某个接口,那么子类也就自然使用了该接口,子类不必再使用关键字implements声明自己使用这个接口了.
接口也可以被继承,即可以通过关键字extends声明一个接口是另一个接口的子接口.由于接口中的方法和常量都是public的,子接
口将继承父接口中的全部方法和常量.
3.理解接口
讲了半天,你已经知道如何定义和使用接口了,但你会疑问,使用接口的好处在哪里?其实接口的思想就在于它可以增加很多类都需
要实现的功能,使用相同的接口类不一定由继承的关系,就是象各种各样的商品,他们可能隶属不同的公司,工商部门要求不管哪里
的商品必须具有显示商标的功能(即实现同一接口),但商标的具体制作由各个公司自己去实现,这就是接口的灵活所在.再比如,你
是一个项目的主管,你需要管理许多部门,这些部门要开发一些 软件所需要的类,你可能要求某个类实现一个接口,也就是说你对一
些类是否具有这个功能非常关心,但不关心功能的具体实现.比如,这个功能是speaklove,但你不关心是用汉语实现功能还是用英语
实现speaklove.还是用个例子来理解一下接口,如:
程序代码:
interface 收费 //定义接口收费功能
{ public void 收取费用();
}
interface 调节温度 //定义接口调节温度功能
{ public void controlTemperature();
}
class 公共汽车 implements 收费 //一般的公共汽车只具有收费功能, 空调车少
{ public void 收取费用()
{ System.out.println("公共汽车:一元/张,不计算公里数");
}
}
class 出租车 implements 收费, 调节温度//出租车比较高级当然除了收费功能还有空调喽
{ public void 收取费用()
{ System.out.println("出租车:1.60元/公里,起价3公里");
}
public void controlTemperature()
{ System.out.println("安装了Hair空调");
}
}
class 电影院 implements 收费,调节温度//电影院有收费和调节温度功能
{ public void 收取费用()
{ System.out.println("电影院:门票,十元/张");
}
public void controlTemperature()
{ System.out.println("安装了中央空调");
}
}
class E
{ public static void main(String args[])
{ 公共汽车 七路=new 公共汽车();
出租车 天宇=new 出租车();
电影院 红星=new 电影院();
七路.收取费用();
天宇.收取费用();
红星.收取费用();
天宇.controlTemperature();
红星.controlTemperature();
}
}
从这个例子可以看出来定义了多个接口,不同类根据自己的需要可以灵活使用想要的接口.建议接口里实现的方法越少越好,可以定
义多个接口来实现多种功能.
4.接口回调
接口回调是指:可以把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量中.那么该接口变量就可以调用被类实现的
接口中的方法.实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法.通过例子掌握接口回调
的意义,如
程序代码:
interface ShowMessage//接口
{ void 显示商标(String s);
}
class TV implements ShowMessage//使用接口
{ public void 显示商标(String s)
{ System.out.println(s);
}
}
class PC implements ShowMessage//使用接口
{ public void 显示商标(String s)
{ System.out.println(s);
}
}
public class E
{ public static void main(String args[])
{ ShowMessage sm;//声明了一个接口变量
sm=new TV();//把使用该接口的类的对象赋给接口变量
sm.显示商标("长城牌电视机");//此时接口变量就可以使用被该类实现的接口中的方法了
sm=new PC(); //另一个对象赋给接口变量
sm.显示商标("联想奔月5008PC机");
}
}
体会一下,如果你理解上节课讲的上转型对象,就可以很轻松理解接口回调的过程
回顾下上节课中将抽象类时给的例子,计算面积的那个,下面用接口l来实现,比较不同点
程序代码:
interface Computerable
{ public double 求面积();
}
class 梯形 implements Computerable
{ double a,b,h;
梯形(double a,double b,double h)
{ this.a=a;
this.b=b;
this.h=h;
}
public double 求面积()
{ return((1/2.0)*(a+b)*h);
}
}
class 圆形 implements Computerable
{ double r;
圆形(double r)
{ this.r=r;
}
public double 求面积()
{ return(3.14*r*r);
}
}
class 堆
{ Computerable 底;
double 高;
堆(Computerable 底,double 高)
{ this.底=底;
this.高=高;
}
void 换底(Computerable 底)
{ this.底=底;
}
public double 求体积()
{ return (底.求面积()*高)/3.0;
}
}
public class E
{ public static void main(String args[])
{ 堆 zui;
Computerable bottom;//声明接口变量
bottom=new 梯形(2.0,7.0,10.7); //回调
System.out.println("梯形的面积"+bottom.求面积());
zui=new 堆(bottom,30);
System.out.println("梯形底的堆的体积"+zui.求体积());
bottom=new 圆形(10);
System.out.println("半径是10的圆的面积"+bottom.求面积());
zui.换底(bottom);
System.out.println("圆形底的堆的体积"+zui.求体积());
}
}
上面的例子明白后或者理解了什么时接口回调,再讨论下"接口做参数"的概念
如果准备给一个方法的参数传递一个值,可能希望该方法的参数的类型时都不了类型,这样就可以想该参数传递byte int long float double类型的数据.
如果一个方法的参数时接口类型,就可以将任何实现改接口类的实例传递给该接口参数,那么接口参数就可以回调类实现的接口方法.
比如上面的例子中堆那个类,
程序代码:
class 堆
{ Computerable 底;//声明接口类型变量
double 高;
堆(Computerable 底,double 高) //方法的参数类型是接口
{ this.底=底;
this.高=高;
}
void 换底(Computerable 底)
{ this.底=底;
}
public double 求体积()
{ return (底.求面积()*高)/3.0;//"底"是接口类型的变量怎么能使用求面积的方法呢,说明了肯定是接口回调,即传进来的参数是一个被对象赋过值的接口变量,才使得此接口变量可以使用被类实现的方法
}
}
如果还是不太明白过程,把下面的程序理解就行了
程序代码:
interface SpeakHello //接口
{ void speakHello();
}
class Chinese implements SpeakHello//使用接口
{ public void speakHello()
{ System.out.println("中国人习惯问候语:你好,吃饭了吗? ");
}
}
class English implements SpeakHello//使用接口
{ public void speakHello()
{ System.out.println("英国人习惯问候语:你好,天气不错 ");
}
}
class KindHello
{ public void lookHello(SpeakHello hello)//接口做参数
{ hello.speakHello();//被赋值了的接口(回调后的接口)就可以使用被类实现了的方法speakHello();
}
}
public class E
{ public static void main(String args[])
{ KindHello kindHello=new KindHello();
kindHello.lookHello(new Chinese());//可以把它分解为SpeakHello hello; 和hello=new Chinese();
kindHello.lookHello(new English());
}
}
总结:花了一个篇幅讲接口,就证明它的重要性,灵活使用接口对以后编写java程序再性能上会有质的提高的.
第八节课
一些比较重要的类
1.内部类
类可以有两种重要的成员:成员变量和方法,类还可以有一种成员:内部类.
java支持在一个类中声明另一个类,这样的类称为内部类,而包含内部类的类称为内部类的外部类.声明内部类如同在类中声明方法
和成员变量一样,一个类把内部类看作是自己的成员.内部类的外嵌类的成员变量在内部类中仍然有效,内部类中的方法也可以调用
外嵌类中的方法.内部类的类体中不可以声明类变量和方法.外嵌类的类体中可以用内部类声明对象,作为外嵌类的成员.举例体会:
程序代码:
class China
{ final String nationalAnthem="义勇军进行曲";
Beijing beijing; //Beijing 是个内部类,其声明了一个对象作为外嵌类的成员
China()
{ beijing=new Beijing();
}
String getSong()
{ return nationalAnthem;
}
class Beijing //内部类的声明
{ String name="北京";
void speak()
{ System.out.println("我们是"+name+" 我们的国歌是:"+getSong());
}
}
}
public class E
{ public static void main(String args[])
{ China china=new China();
china.beijing.speak();
}
}
2.匿名类
a) 和类有关的匿名类
当使用类创建对象时,程序允许把类体与对象的创建组合在一起,也就是说,类创建对象时,除了构造方法还有类体,此类体被认为是
该类的一个子类去掉类声明后的类体,称作匿名类.匿名类就是一个子类,由于无名可用,所以不可能用匿名类声明对象,但却可以直
接用匿名类创建对象.假设People是类,那么下列代码就是用People的一个子类(匿名类)创建对象:
new People()
{
匿名类的类体
}
因此,匿名类可以继承父类的方法也可以重写父类的方法.使用匿名类时,必然是在某个类中直接用匿名类创建对象,因此匿名类一定
是内部类,匿名类可以访问外嵌类中的成员变量和方法,匿名类的类体中不可以声明static成员变量和static方法.
尽管匿名类创建的对象没有经历类声明的步骤,但匿名对象的引用必须传递一个匹配的参数,匿名类的主要途径就是向方法的参数传
值.
假设f(B x)是一个方法
void f(B x){
x调用B类中的方法
}
其中参数是B类的对象,那么在调用方法f时可以向其参数x传递一个匿名对象,例如:
f(new B()
{匿名类的类体}
)
如果匿名类继承了类的方法,x就调用继承的方法,如果匿名类重写了父类的方法,x就调用重写的方法.
看看下面的例子,用匿名类创建一个对象,并向一个方法的参数传递一个匿名类的对象.
程序代码:
class Cubic
{ double getCubic(int n)
{ return 0;
}
}
abstract class Sqrt
{ public abstract double getSqrt(int x);
}
class A
{ void f(Cubic cubic)
{ double result=cubic.getCubic(3);//执行匿名类体中重写的getCubic()
System.out.println(result);
}
}
public class E
{ public static void main(String args[])
{ A a=new A();
a.f(new Cubic() //使用匿名类创建对象,将该对象传递给方法f的参数cubic
{ double getCubic(int n)//类体
{ return n*n*n;
}
}
);
Sqrt ss=new Sqrt() //使用匿名类创建对象,ss是该对象的上转型对象
{ public double getSqrt(int x) //匿名类是abstract类Sqrt的一个子类,所以必须实现getSqrt()方法
{ return Math.sqrt(x);
}
};
double m=ss.getSqrt(5); //上转型对象调用子类重写的方法
System.out.println(m);
}
}
b) 和接口相关的匿名类
假设Computable是一个接口,那么,java允许直接调用接口名和一个类体创建一个匿名对象,此类体被认为是实现Computable接口的
类去掉类声明后的类体,称为匿名类.下面这个例子就是用实现了Computable接口的类(匿名类)创建对象:
程序代码:
interface Cubic
{ double getCubic(int n);
}
interface Sqrt
{ public double getSqrt(int x);
}
class A
{ void f(Cubic cubic)
{ double result=cubic.getCubic(3); //执行匿名类体中实现getCubic()方法
System.out.println(result);
}
}
public class E
{ public static void main(String args[])
{ A a=new A();
a.f(new Cubic() //使用匿名类创建对象,将该对象传递给方法f的参数cubic
{ public double getCubic(int n)
{ return n*n*n;
}
}
);
Sqrt ss=new Sqrt() //使用匿名类创建对象,接口ss存放该对象的引用
{ public double getSqrt(int x)//匿名类是实现Sqrt接口的方法,所以必须要实现getSqrt方法
&
|