l 2.1.简介
l 软件工程是艰巨的,难以驾御的学科。在过去的半个世纪里,计算机科学家、软件工程师、系统结构师们反复思考一个问题:如何通过代码重用使开发软件系统变得容易?最初的程序设计是用机器语言进行的,为了解决机器语言的复杂性,人们设计了“高级语言”。设计了系统过程调用,完成常用的相同的操作(如文件的打开、读、写)。
l 与此同时,另外一些开发者将公用的函数、过程收集组织到库中。例如:计算工程结构负荷的NASTRAN,在网络上的计算机之间传输字节流的TCP/IP,通过索引访问数据的ISAM(有序文件系统),在点阵监视器上生成窗口、图形、用户接口的X-Windows和OpenLook。这些函数库中的大部分采用“开放式记录数据结构”管理数据。采用“开放式记录数据结构”会带来这样的问题:函数库的设计者无法将数据从实现过程中隐藏起来。这个问题使得很难在不影响客户端代码的情况下修改实现过程,因为实现过程是与数据结构的细节紧密关联的。
l 上世纪八十年代晚期,面向对象的程序设计(OOP)在C++中流行起来。OOP技术的最大优点就是能够将数据隐藏起来。库的升级,不会对库的使用这造成影响。另一个优点是处理过程与数据结构相关连,将数据当成一个不可分割的整体。这个整体叫做类(class)。
l 上世纪九十年代,原来的函数库改称类库或工具包。新的类库提供的类完成的操作与旧库中的函数相同,而且通过使用子类,客户端程序员可以容易地扩展类库的功能。体系结构定义的APIs可以由许多不同的制造商实现,用户程序员拥有更大的选择余地。
l Java技术是一个持续扩展的平台,新的APIs和体系结构不断补充进Java技术平台,例如:Swing、JavaBeans、JDBC等。
2.1.2对象
l 现实社会中一切皆对象。比如:人,树,石头,猫,鸟,汽车,地球等等。任何一种对象都具有静态的属性,但不一定具有动态的行为。比如:石头。一般情况下,对象既有静态的属性,也有动态的行为。对象本身的属性和行为之间可以相互影响,比如:一个人饿了(属性),就会去吃饭(行为)。相反,这个人吃饭(行为)后,就饱了(属性),体重(属性)也增加了。不同的对象之间也可以相互作用。比如:人看到汽车开过来了,就会沿着路边走。如果这个人站在路中间不动(他不怕死),那么汽车就会停下来。那么怎么用JAVA语言来实现上述功能呢??后面有例子的实现。
l
2.1.3类
l 如同建筑设计师设计建筑图(建筑的蓝图),可以用该图来盖出许许多多这种风格的房子一样。类是软件的蓝图,你可以用类,来实例化许许多多个别的对象。在类中定义了一套数据元素(属性)和一套行为(方法)。行为用来操作对象,以及完成相关对象之间的交互。属性和方法都叫做成员。例如杯子装水的时候:最大盛水量和当前盛水量。盛水的方法要始终跟踪这两个属性。
2.2声明类
l 在Java技术中采用下列方法声明类:
l <modifier> class <name>{
l <attribute_declaration>
l <constructor_declaration>
l <method_declaration>
l }
l 说明:
l <name>:任何合法的标识符。它代表所声明类的名称。
l <modifier>:暂时只用“public”,含义为:可以被所有其它类访问。
l <attribute_declaration>:声明属性。也就是说用变量表示事物的状态。
l <constructor_declaration>:声明构造函数。
l <method_declaration>:声明方法。
l 注意:属性,方法,构造函数在类中的顺序没有固定的约束。一般习惯性地先声明属性,后声明方法(构造函数也叫构造方法,习惯性地把构造方法写普通方法的前面)
2.2.1声明属性
l 句法:
l <modifier> <type> <name> [ = <default_value> ];
l
l 说明:
l <name>:任何合法的标识符。它代表所声明属性的名称。
l <modifier>:暂时只用“private”,含义为:仅能被所属类中的方法访问。
l <type>:可以是任何原始类型或其它类。
l [=<default_value>]是给属性初始化为给定的值。如果没有的话初始化为默认的值。(基本类型的初始化相应的值:比如:int,short,byte,long,char(Unicode码值)初始化为0,float,double初始化为0.0,boolean初始化为false,所有的引用类型都初始化为null)。
l 注意:JAVA语言与其它语言不同,在JAVA中声明属性和初始化属性值必须一句完成。不能分开写:先声明,再另起一行初始化;
l 例如: int a ;
l a=5; 是错误的 。
l 在类里面除了声明语句之外,是不能直接写其它执行语句的。 a=5 是赋值语句。执行语句放在块(方法块,初始化块等)里执行。
2.2.2声明方法
l 句法:
l <modifier> <return_type> <name> ( <parameter> ){
l <statement>
l }
l
l 说明:
l <name>:任何合法的标识符(已经被使用的除外)。
l <modifier>:可以是public,指明可以被任何其它代码访问。
l protected:指明可以被同一个包中的其它代码访问。
l private:指明仅能被同一个类中的其它代码访问。
l < return_type>:指明方法返回值的类型。假如方法不返回值,应被声明为void。
l <parameter>:向方法传递参数。传递多个参数时,参数之间用逗号分开。每个参数由参数类型和标识符组成。
l 注意:方法必须有返回类型(构造方法除外),返回类型为void和没有返回类型是不一样的。除了返回类型void外,有其它返回类型的方法块里必须有return语句。
l
l 例如:
l public class Person //声明类
l {
l private int weight; //声明属性,初始化为默认值0。
l
l public int getWeight() //声明方法
l {
l return weight; //由于返回类型不是void,所以必须有return语句。
l }
l
l public void setWeight(int new_w)
l {
l weight = new_w; //该方法的返回类型是void的,所以方法块里可以没有return语句。
l }
l }
l
l 说明:
l 类Person有一个实例变量weight 。方法getWeight返回weight这个数据属性,方法getWeight没有参数。Return语句反回这个值。方法setWeight用参数new_w修改weight的值,它不返回任何值。
l 下面说明如何使用这个方法:
l public class TestPerson{
l public static void main(String[ ] args){
l Person person = new Person( );
l
l person.setWeight(56);
l System.out.println(“person.weight is “ + person.getWeight() );
l }
l }
l 输出结果为:
l person.weight is 56
2.3访问对象的成员
l 在前面的例子中有下面的代码:
l person.setWeight(56);
l 在方法的使用中,这行代码告诉对象person执行它的setWeight方法。这叫做点符号。它允许程序访问类的非私(non-private )有成员(属性和方法)。
l 在方法的定义中,不需要使用点符号访问本地成员。例如Person类中的setWeight方法没有用点符号访问weight属性。
2.4信息隐藏(封装)
l 信息的封装其实就是在类里面把属性定义成私有,也就是把属性的访问权限定义成private。也就是说你不能直接在这个类的外面来操作这个属性。比如人这个对象,你只能通过吃饭来增加体重,或者做运动来减少体重,而不能直接修改体重(比如:不能把一斤猪肉粘在你的背上就是你的肉了,同样为了减肥,也不能直接从身上割块肉下来。)
l 如下事例:
l public class Person
l {
l private int weight;//人的体重
l public void eat(int w)//吃饭的方法
l {
l weight=weight+w;//体重增加
l }
l }
l 在使用中:
l public class TestPerson
l {
l public static void main(String[] args)
l {
l Person p=new Person();
l p.weight+=2 ;//错误,不能在类的外面访问类的私有成员(封装的好处)
l p.eat(2);//正确,通过方法来改变对象的状态
l }
l }
2.5声明构造函数
l 我们经常要用类来实例化新的对象,一个新对象的初始化的最终步骤是去调用一个叫做构造函数的方法。构造函数是一套指令,用来初始化类的实例。可以向构造函数传递参数,其格式与向方法传递参数相同。构造函数用下列语法声明:
l [<modifier>] <class_name> (<parameter>){
l <statement>
l }
l 声明构造函数的时候应该注意以下两点:
n 构造函数的名称必须与类名相同。
n 构造函数没有返回类型。
l <modifier>:可以是public, protected, private。
l <parameter>:与方法的声明相同。
l 例如:
l public class Person
l {
l private int weight;//人的体重
l
l public Person() //不带参数的构造函数,也就是默认的构造器
l {}
l
l public Person(int w) //带参数的构造器,初始化属性weight 的值为 w
l {
l weight=w;
l }
l
l public void eat(int w)//吃饭的方法
l {
l weight=weight+w;//体重增加
l }
l
l public int getWeight()
l {
l return weight;
l }
l }
l
l 构造函数的调用方法如下:
l public class TestPerson
l {
l public static void main(String[] args)
l {
l Person p1=new Person(); //调用不带参数的构造函数
l Person p2=new Person(120); //调用带参数的构造函数
l p1.eat(2);
l p2.eat(2);
l System.out.println(“person1’s weight is ”+p1.getWeight());
l System.out.println(“person2’s weight is”+p2.getWeight());
l }
l }
l 输出结果为:
l person1’s weight is 2
l person2’s weight is 122
2.6默认的构造函数
l 每个类至少要有一个构造函数,如果你没有编写构造函数,Java编程语言会为你提供一个默认的构造函数。这个构造函数不带参数,函数体是空的。
2.7 源文件布局
l Java技术源文件采用下面的布局:
l [<package_declaration>]
l
l <import_declaration>
l
l <class_declaration>
l 以上条目的顺序不能改变。声明包的语句必须写在源文件的第一行。
l 源文件的名字必须与文件中声明的公有类的名字相同。一个源文件中可以包含多个类,但是最多只能包含一个公有类。如果源文件中不含公有类,源文件的名字不受限制。
l 例如,源文件TestDate.java的内容如下:
l package shenzhen.luohu;
l
l import java.util.Date;
l import java.io.*;
l
l public class TestDate{
l
l public static void main(String[] args)
l {
l System.out.println(new Date());
l }
l }
2.8软件包
l 多数软件系统是庞大的。为了方便管理,通常要将类组织成包。在包中可以存放类,也可以存放子包,从而形成具有层次结构的包。包可以根据需要任意组织,通常,要按照类的用途、含义来组织包。
l
l
l 图2-2
l
l 如图2-2所示,深圳市的软件系统可以组织如下:
l nanshan:保存南山区的所有公司类。
l futian :保存福田区的所有公司类。
l luohu:保存罗湖区的所有公司类。
2.8.1声明包
l Java技术提供了包的机制,以次来组织相关的类。声明包的句法如下:
l
l package <top_pkg_name> [.<sub_pkg_name>].*;
l
l 你可以使用package命令指明源文件中的类属于某个特定的包。例如:
l package shenzhen.luohu;
l public class YuanBiao{
l …
l }
l
l package命令必须放在源文件的最前面。一个源文件最多只能有一条package命令。一条package命令对源文件中的所有类起作用。如果你的源文件中没有package命令,你的类将会保存在当前目录。
2.8.2导入包
l 当你想要使用包中的类的时候,可以用import命令告诉编译器类在哪里。import命令的句法如下:
l import <pkg_name>[.<sub_pkg_name>].<class_name | *>;
l 例如:
l import shenzhen.nanshan.*;
l import shenzhen.futian.*;
l
l import java.util.List;
l import java.io.*;
l 当你使用import指令时,你并没有将那个包或那个包中的类拷贝到当前文件或当前包中。你仅仅是将你在import指令中选择的类加入到你的当前名字空间。无论你是否导入当前包,当前包都是你的名字空间的一部分。
l import命令指明你要访问的类。例如,你需要访问Writer类,你需要下面的命令:
l import java.io.Writer;
l 如果你需要访问一个包中的所有类,你需要下面的命令:
l import java.io.*;
2.9包与目录布局
l 2.9.1 包保存在目录结构中,包的名字就是目录的名字。例如,shenzhen.luohu包中的Company.class文件应该在 path/shenzhen/luohu目录中。
l *运行的时候进入到path目录下:
l path>java shenzhen.luohu.Company 或者
l path>java Shenzhen/luohu/Company
l 注意:
l 不能把 shenzhen.luohu.Company或者shenzhen/luohu/Company写开运行!例如:你不能进入到shenzhen目录下运行:path/shenzhen>java luohu.Company或者path/shenzhen>java luohu/Company
l 也不能进入到上一及目录运行。假如你把shenzhen 放在daima文件夹里,你也不能进入到daima 那个文件所在目录下运行:path>java daima/shenzhen/luohu/Company 或者path>java daima.shenzhen.luohu.Company
l 上面是运行时的目录结构。在编译的时候用下面两种方式把包的结构生成出来。
1. 手动建立文件夹。把包的结构创建出来
l 例如:上面的两个包结构,可以在path目录下先建立个文件夹叫shenzhen ,打开shenzhen 文件夹,在该文件夹下再建立个文件夹叫luohu 然后进入luohu文件夹,编译Company.java
l path/shenzhen/luohu>javac Company.java
2. 自动把包的目录结构生成出来。
l 例如进入到path目录下编译:path>javac –d . Company.java 就自动把包的结构生成出来了。(.是代表当前目录也可以改成其他目录)
l 运行的步骤如*所述。
2.9.2 classpath环境变量的作用。
classpath 顾名思义,是class的path。是用来找类的。比如我把上面的shenzhen 目录指定一下。指定到d:/path/ 下 ,就可以用下面代码来描述 :任意目录>set classpath=%classpath%;d:/path
那么就可以在任意目录下运行上面代码了。例如:在e:/test目录下运行
e:/test>java shenzhen/luohu/Company
也可以动态的指定到某一目录下:
例如:任意目录>java –classpath %classpath%;d:/path shenzhen.luohu.Company
或者:任意目录>javaw –classpath %classpath%;d:/path shenzhen.luohu.Company
javaw表示运行完主线程退出程序,JBuilder工具就用此操作运行。
l
2.10使用Java API文档
Java API是扩展的Java类库。它为程序员提供了几千个类,包括基本的数学函数、数组和字符串、窗口,图形用户界面,输入/输出,联网等任何你需要的内容。
类库被组织成许多包,每个包都包含多个类。下面列举了一些重要的包:
· java.lang:包含一些形成语言核心的类,如String、Math、Integer和Thread。
· java.awt:包含了构成抽象窗口工具包(AWT)的类,这个包被用来构建和管理应用程序的图形用户界面。
· java.applet:包含了可执行applet特殊行为的类。
· java.net:包含执行与网络相关的操作的类和处理接口及统一资源定位器(URLs)的类。
· java.io:包含处理I/O文件的类。
· java.util:包含为任务设置的实用程序类,如随机数发生、定义系统特性和使用与日期日历相关的函数。
Java API文档详细说明了Java API的使用方法。Java API文档是一组等级制布局的HTML文件,因而主页列出所有的包为超链接。如果选中了一个特殊包的热链接,作为那个包成员的类将被列出。从一个包页选中一个类的热链接将提交一页有关那个类的信息。
下载Java2 SE Version 1.4的文档并解压缩,打开 /api/Index.html 文件。
l 一个类文档的主要部分包括:
- 类层次
- 类和类的一般目的描述
- 成员变量列表
- 构造函数列表
- 方法列表
- 变量详细列表及目的和用途的描述
- 构造函数详细列表及描述
- 方法详细列表及描述
2.11事例代码
例1.PersonApp.java
class Person
{
private int weight; //人的体重
private boolean hungry; //饥饿的属性
public Person(int w) //带参数的构造器,初始化人的体重
{
weight=w;
}
public void setHungry() //用来设置饥饿这个属性,如果原来为true
{ //就改为false,否则改为true
if(hungry)
hungry=false;
else
hungry=true;
}
public boolean getHungry()
{
return hungry;
}
public int getWeight()
{
return weight;
}
public void eat(int i)
{
if(i>=2) //东西多的时候,最多只能吃两斤。
{
weight=weight+2; //体重增加2斤
hungry=true ; //表示吃饱了
}
else
if(i>0&&i<2) //当有东西吃的时候,并且少于2斤
{
weight=weight+i; //吃多少,体重就增加多少,但是返回false
hungry=false; //但是返回false,表示没吃饱
}
else
hungry=false ; //直接没东西吃,返回false
}
}
public class PersonApp
{
public static void main(String[] args)
{
Person zhangsan=new Person(120);
zhangsan.setHungry();
if(zhangsan.getHungry()) //属性影响行为
zhangsan.eat(2); //行为改变属性
System.out.println(zhangsan.getWeight());
}
};
例2.CarPersonApp.java
public class CarPersonTest
{
public static void main(String[] args)
{
Person p=new Person();
Car c=new Car();
c.move(true); //车在行驶
p.walk(c.getMoving());//测试人是否能行走,也就是说车的行为,影响了人的行为
}
}
class Car
{
private boolean moving;
public boolean getMoving()
{
return moving;
}
public void move(boolean side)
{
if(side)
{
System.out.println("车继续行驶");
moving=true;
}
else
{
System.out.println("车停下来");
moving=false;
}
}
};
class Person
{
boolean side; //表示人是否在路边
public boolean walk(boolean car)
{
if(car) //如果有车的话,人往路边走
{
System.out.println("人往路边走");
side=true;
return side;
}
else
{
System.out.println("人直着走");
side=false;
return side;
}
}
};