本章概览:
目录面向对象方法的特征
抽象:从同类型对象中抽象出共同属性
封装:把数据和处理数据的方法封到一个类中
继承:在已有的类的基础上开发新的类
多态:在由继承的环境下,超类(父类)和子类都能响应共同的消息,但是响应消息的具体实现办法可以不同
类与对象基础
类的声明
对象的创建
数据成员
方法成员
包
类的访问权限控制
类成员的访问权限控制
对象初始化和回收
构造方法(初始化)
内存回收
枚举类型
简单枚举类型
枚举类(功能更为强大)
应用举例
银行账号示例
抽象的思想:忽略问题中与当前目标无关的方面,只关注与当前目标有关的内容。
封装是一种信息隐蔽技术。利用抽象数据类型将数据和基于数据的操作封装在一起;用户只能看到对象的封装界面信息,对象的内部细节对用户是隐蔽的;封装的目的在于将对象的使用者和设计者分开,使用者不必知道行为实现的细节。
继承是一种基于已有类产生新类的机制。是指新的类可以获得已有类(成为超类、基类或父类)的属性和行为,称新类为已有类的子类(也成为派生类),在Java中一般用超类、子类的术语;在继承过程中子类继承了超类的特性,包括方法和实例变量;子类也可以修改继承的方法或增加新的方法;有助于解决软件的可重用性问题,使程序结构清晰,降低了编码和维护的工作量。
一个子类只有单一的直接超类。
一个子类可以有一个以上的直接超类。
在Java中仅支持单继承。
在有继承的情况下,超类和他的子类都可以响应同名的消息,但是这些对象对这些同名的消息的实现方式可以是不一样的。主要通过子类覆盖从超类继承过来的方法来实现多态。
类是对一类对象共同的属性和行为的一种抽象,是一种抽象出来的数据类型;
对象是类的具体的实例
/*完整语法结构*/ /*方括号里的关键字是可选项,可有可无*/ [public][abstract|final]class类名称//class关键字是必须的,表示后面定义的是一个类 [extends父类名称] [implements接口名称列表]//大括号中为类体 { 数据成员声明及初始化; 方法声明及方法体; }
class
表明其后声明的是一个类。extends
如果所声明的类是从某一父类派生而来,那么,父类的名字应该写在extends
之后。即,当我们要继承已有的类,形成新类时,要用extends
关键字implements
(用来实现接口)如果所声明的类要实现某些接口,那么,接口的名字应写在implements
之后。public
表明此类为公有类(后续章节介绍类的访问控制属性时会介绍public
)。abstract
是抽象的意思,有abstract
修饰的类是抽象类(后续章节会介绍)。final
表明这个类是终结类,表明这个类不可以被继承。类名 引用变量名;
例:
Clock
是已经声明的类名,声明引用变量aclock
,勇于存储该对象的引用。
Clock aclock;
此时,对象还没有生成,我们只是创建了一个引用,且为空引用。
意思是分配新的内存空间(在运行时分配),勇于存放一个Clock
类型的对象。此时没有进行初始化,如果希望初始化,则需要在圆括号中给出初始化参数(后续会介绍)
new <类名>()
例:
aclock = new Clock();
new
的作用是在内存中为Clock
类型的对象分配内存空间,同时返回对象的引用。
医用变量可以被赋以空值,如
aclock = null;
数据成员用来表示对象的状态,也可以存放在整个类所有对象之间要共享的数据;数据成员可以是任意的数据类型,如基本类型,另外一个类的对象,数组等。
方括号中为可选项,在需要的时候写,不需要的时候可以不写。
[public|protected|private] [static][final][transient][volatile] 数据类型 变量名1[=变量初值],变量名2[=变量初值],...;
public
protected
private
称为访问控制符,是用来控制对类成员的访问权限的。static
指明这是一个静态成员变量(类变量)(后面会介绍)。final
指明变量的值不可以被修改。transient
指明变量不需要序列化(后面介绍文件IO时会涉及到)。volatile
指明变量是共享变量。没有static
修饰的变量(数据成员)成为实例变量。
实例变量,也叫属于对象的属性(实例属性),是用来描述每个对象的属性的,不同对象的属性即实例变量的值往往是不同的,这些值用来区分此对象与彼对象。
访问实例变量要通过变量名访问,语法形式为<实例名>.<实例变量名>
。不是所有实例变量都可以这样访问,要注意属性或变量的访问控制权限。
/*圆类保存在文件Circle.java中,测试类保存在文件ShapeTester.java中,两文件放在相同的目录下*/ public class Circle{ int radius; } public class ShapeTester{ public static void main(String args[]){//定义了一个主方法 Circle x;//定义了一个圆类的引用 x = new Circle();//用new获得一个新的圆对象,并把引用赋给x System.out.println(x); System.out.println("radius = " + x.radius); } }
输出结果(在本机测试,与网课中不同)
Circle@379619aa radius = 0
对输出结果的说明:
所有的类中都有默认的toString()
方法,默认的toString
的返回:getClass().getName()+"@"+Integer.toHexString(hashCode())
,即,先取得类的类名并转成字符串,输出@
符号,然后调用hashCode()
方法,将对象的哈希码转成十六进制形式的字符串。
该默认的toString
不是很有意义,后续章节将会介绍如何自己写一个toString
覆盖已有的toString
。
/*矩形类保存在Recrangle.java中,测试类保存在ShapeTester.java中,两文件保存在相同目录下*/ public class Rectangle { double width = 10.128;//在类里面已经定义好初始值了 double height = 5.734;//在类里面已经定义好初始值了 } public class ShapeTester{ public static void main(String args[]){//定义了一个主方法 Circle x; Rectangle y; x = new Circle();//圆对象依然没初始化(和上方代码中相同) y = new Rectangle(); System.out.println(x + " " + y); } }
输出结果
Circle@cac736f hello.Rectangle@5e265ba4
static
修饰。<类名|实例名>.<类变量名>
,无需用对象名使用,但是用对象名使用也可。public class Circle{ static double PI = 3.14159265;//类变量(静态变量)圆里面所有对象都共享常量pi int radius; } //当我们生成Circle类的实例时,在每一个实例中并没有存储PI的值,PI的值储存在类中(只存一份)
对类变量进行测试
public class ClassVariableTester { public static void main(String[] args) { Circle x = new Circle();//构造圆对象,并把引用赋给x System.out.println(x.PI);//通过<实例名>.<类变量名>输出PI的值 System.out.println(Circle.PI);//通过<类名>.<类变量名>输出PI的值 Circle.PI = 3.14; System.out.println(x.PI); System.out.println(Circle.PI); } }
输出结果如下
3.14159265 3.14159265 3.14 3.14
由以上测试可以看出,对象名访问和类名访问静态成员的时候是一样的
此部分涉及的新名词较多,需要着重辨析不同名词所指代内容是否相同,及时予以总结。
类定义的方法分为两类,类的方法和实例的方法。
类的方法是用来表示类的一些共同的行为或功能的。
实例的方法是用来表示每一个实例的功能或者行为的。
在类中定义方法和C语言中定义函数很相像,只不过方法不是独立的,即不是全局的,是必须出现在类体里面的。
/*方括号中为可选内容*/ [public|protected|private] [static][final][abstract][native][synchronized] 返回类型 方法名([参数列表])[throws exceptionList]//返回类型类似C中返回值类型,方法名类似C中函数名,参数列表类似C中函数形参表 { 方法体;//类似C中函数体 }
public
protected
private
控制访问权限。static
指明这是一个类方法(静态方法)。final
指明这是一个终结方法。abstract
指明这是一个抽象方法(只有方法原型,没有方法体体现)。native
用来集成java
代码和其他语言的代码(本课程不涉及)。synchronized
用来控制多个并发线程对共享数据的访问(在Java语言程序设计进阶中涉及)。throw exceptionList
列出这个方法有可能抛出的异常,即异常抛出列表(在后续章节会介绍异常处理)实例方法属于每个对象,用来表示每个对象的功能或者行为。定义实例方法时不用static
关键字。
给对象发消息,使用对象的某个行为/功能时调用方法(因为方法即代表对象的行为或者功能)。
实例方法调用格式
<对象名>.<方法名>([参数列表])
<对象名>
为消息的接收者。
从内外来调用方法时通过对象名来调用;如果在类体里面方法之间互相调用,前面则不需要挂一个对象名,即在类体内部方法与方法之间可以直接互相调用,直接用方法名即可。
值传递:参数类型为基本数据类型时,用实参初始化形参,实际上是一次性的单向传递,然后实参和形参之间没有关系了。
引用传递:参数类型为对象类型或数组时,传对象作为参数,实际上传的是对象的引用,实参名和形参名两个不同的名字指向了同一个对象。
public class Circle{ static double PI = 3.14159265; int radius; public double circumference() {//求圆周长的方法 return 2 * PI * radius; } public void enlarge(int factor) {//将圆扩大若干倍的方法,参数是倍数 radius = radius * factor; } public boolean fitsInside(Rectangle r) {//参数是另一个类的对象,方法要计算是否可以将圆装入矩形并返回布尔值 return (2 * radius < r.width) && (2 * radius < r.height); } }
测试如下:
public class InsideTester { public static void main(String[] args) { Circle c1 = new Circle(); c1.radius = 8; Circle c2 = new Circle(); c2.radius = 15; Rectangle r = new Rectangle(); r.width = 20; r.height = 30; System.out.println("Circle 1 fits inside Rectangle:" + c1.fitsInside(r)); System.out.println("Circle 2 fits inside Rectangle:" + c2.fitsInside(r)); } }
运行结果如下:
Circle 1 fits inside Rectangle:true Circle 2 fits inside Rectangle:false
static
修饰。只需要方法,不需要对象。
public class Converter { public static int centigradeToFahrenheit(int cent) { return (cent * 9 / 5 + 32); } }
方法调用
Converter.contigradeToFahrenheit(10)
方法参数列表中可以定义可变长参数列表。
String...s
表示String[] s
。static double maxArea(Circle c,Rectangle...varRec){ Rectangle[] rec = varRec; for(Rectangle r:rec){//基于范围的for循环,定义一个Rectangle 的引用,冒号后面是 数组名,这个循环的作用是在循环每一次依次从数组中取出一个元素,赋给r,来访问这个元素。此方法也是访问可变长参数的常用手段 //...(没有具体实现方法体,只是做一个示意) } }
参数表中有一个圆对象的引用做参数,有若干个矩形对象的引用做参数,其中...
表示varRec
本质上是一个Rectangle
对象的引用数组,只是数组元素数不确定,具体调用形式如下。
public static void main(String[] args){ Circle c = new Circle(); Rectangle r1 = new Rectangle(); Rectangle r2 = new Rectangle(); System.out.println("max area of c,r1 and r2 is " + maxArea(c,r11,r2)); System.out.println("max area of c and r1 is " + maxArea(c,r1)); System.out.println("max area of c and r2 is " + maxArea(c,r2)); System.out.println("max area of only c is" + maxArea(c)); }
包是一组类的集合;一个包可以包含若干个类文件,还可以包含若干个包。
Import
(引入)包的声明,用于导入外部的类(使用其他包里面的类)③(自己定义的)类和接口的声明。public
类,该类名与文件名相同,编译单元中的其他类往往是public
类的辅助类,经过编译,每个类都会产生一个class
文件,文件名必须是相同的;辅助的类不叫public
类,叫缺省的default
类,在内部起辅助作用。例如:package Mypackage;
不含有包声明的编译单元是默认包的一部分。
引用包是为了使用包所提供的类,此时需要使用import
语句引入所需要的类。
Java编译器回味所有程序自动引入包java.lang
。
引入更多其它包时:import
语句的格式:
import package1[.package2...].(classname|*);
包名可以是多级,由上文介绍Java推荐包命名规则知域名反序形如.packagei
;如果要引入包的某个类名,就将类名写在这classname
,如果要引入包里面所有的类,则可以使用*
代替类名。
在内外使用一个类的静态成员,我们的引用方法是类名.静态方法名
来使用,如果大量使用某些静态方法,可以用静态引入简化之。
单一引入是指引入某一个指定的静态成员,例如import static java.lang.Math.PI;
引入了PI
常量。
全体引入是指引入类中所有的静态成员,例如import static java.lang.Math.*;
例如
import static java.lang.Math.PI; public class Circle{ int radius; public double circumference(){ return 2 * PI * radius;//此时可以直接使用PI而不用带着类名,方便。 } }
类型 | 无修饰(默认) | public |
---|---|---|
同一包中的类 | 是 | 是 |
不同包中的类 | 否 | 是 |
public
)可以被其他任何方法访问(前提是对类成员所属的类有访问权限)。
protected
)只可被同一类及其子类的方法访问。
private
)只可被同一类的方法访问。
default
)仅允许同一个包内的访问;又被称为包(package
)访问权限。
以下前提为该类可以被访问
类型 | private | 无修饰 | protected | public |
---|---|---|---|---|
同一类 | 是 | 是 | 是 | 是 |
同一包中的子类 | 否 | 是 | 是 | 是 |
同一包中的非子类 | 否 | 是 | 是 | 是 |
不同包中的子类 | 否 | 否 | 是 | 是 |
不同包中的非子类 | 否 | 否 | 否 | 是 |
即,在该类可以被访问的情况下,public
公有成员都可以被访问;protected
保护乘员主要在有继承关系的时候,可以被子类的其他方法访问,无论子类和超类是否在同一个包中;无修饰(默认)访问控制权限是包内的,即同一个类中的其他方法可以访问这种无修饰的数据成员和方法成员;private
最为严格,只可以被同一个类中的其他方法访问,其他类中不可以看到别的类中的private
成员,无论是否在同一个包中。
public class Circle{ static double PI = 3.14159265; private int radius;//将半径设为私有 public double circumference() { return 2 * PI * radius; } }
再编译CircumferenceTester.java
public class CircumferenceTester{ public static void main(String[] args){ Circle c1 = new Circle(); c1.radius = 50; Circle c2 = new Circle(); c2.radius = 10; double circum1 = c1.circumference(); double circum2 = c2.circumference(); System.out.println("Circle 1 has circumference " + circum1); System.out.println("Circle 2 has circumference " + circum2); } }
上述测试类会出现语法错误提示The field Circle.radius is not visible
,原因是类的private
私有成员在其它类中不能直接访问,后续会介绍如何通过公有接口访问私有成员(在类中提供公有接口,一般叫get
方法和set
方法,get
方法用于获取数据成员的值(属性),set
方法用于升格至数据成员的值(属性))。
get
方法(public
)功能是取得属性变量的值。
get方法名以get
开头,后面跟实例变量的名字。
例如:
public int getRadius(){ return radius; }
以上实例中getRadius
中的R
大写,为一般惯例(老师语)。笔者感觉像是驼峰命名法的应用(上学期一直这么用来着(逃
set
方法(public
)功能是修改属性变量的值。
set
方法名以set
开头,后面是实例变量的名字。
例如:
public void setRadius(int r){ radius = r; }
this
关键字如果方法内的局部变量(包括形参)名与实例变量名相同,则方法体内访问实例变量时需要this
关键字。
例如:
public void setRadius(int radius){ this.radius = radius; }
Tip:在set
方法中将形参名和要设置的变量名相同是一种常用的处理方式,可以使得set
方式的可读性很好。
当我们定义基本类型变量的时候往往会希望在定义变量的同时指定初始值,叫做变量的初始化;当我们构造对象的时候,也希望给对象指定一个初始状态。
对象的初始化不会由编译器自动完成,这有别于基本类型的变量初始化。
void
。public
),也存在一些特殊场合我们不希望对象被随意构造时,也可能不声明为公有的(初学者无需考虑)。例:一个银行账户类及测试代码
银行账户类
public class BankAccount { String ownerName; int accountNumber; float balance;//余额 }
测试
public class BankTester { public static void main(String[] args) { BankAccount myAccount = new BankAccount(); System.out.println("ownerName=" + myAccount.ownerName); System.out.println("accountNumber=" + myAccount.accountNumber); System.out.println("balabne=" + myAccount.balance); } }
输出结果
ownerName=null accountNumber=0 balabne=0.0
从输出结果可以看到,引用类型初始值为空引用,账户(数值)初始值为0,余额(数值)初始值为0.0。
例:为银行账户类声明构造方法
public class BankAccount { String ownerName; int accountNumber; float balance; /* 为BankAccount声明一个有三个参数的构造方法 */ public BankAccount(String initName, int initAccountNumber, float initBalance) { ownerName = initName; accountNumber = initAccountNumber; balance = initBalance; } /* 假设一个新账号的初始余额可以为0,则可以增加一个带有两个参数的构造方法 */ public BankAccount(String initName, int initAccountNumber) { ownerName = initName; accountNumber = initAccountNumber; balance = 0.0f; } /* 无参数的构造方法——自定义默认的初始化方法 */ public BankAccount() { ownerName = ""; accountNumber = 999999; balance = 0.0f; } }
以上构造方法中的逻辑本质相同,只是参数的个数不同,此时,可以采用如下方法减少冗余。
this
关键字this
关键字在一个构造方法中调用另外的构造方法。例:使用this
的重载构造方法
public BankAccount() { this("", 999999, 0.0f);//this代表本类的参数方法参数名,把参数作为实参 } public BankAccount(String initName, int initAccountNumber) { this(initName, initAccountNumber, 0.0f); } public BankAccount(String initName, int initAccountNumber, float initBalance) { ownerName = initName; accountNumber = initAccountNumber; balance = initBalance; }
final
变量的初始化如果我们希望某个属性一经初始化就不能再被改变,即为常量,则可以使用final
。
final
。final
实例变量可以在类中定义时给出初始值,或者在每个构造方法结束之前完成初始化(最晚的时刻)。final
变量的值就不能再被改变。final
类变量必须在声明的同时完成初始化:因为类变量属于整个类,在整个类中只有一份,不属于任何对象。同样地,声明完成后即不能再被改变。finalize()
方法(每个类中默认都有finalize()
方法,这个方法也可以被覆盖)。finalize()
方法java.lang.Object
中声明,因此Java中的每一个类都有该方法:protected void finalize() throws throwable
。java.lang.Object
是所有Java类的直接的或间接的超类,因此Java中每一个类都有该方法(从Object
继承而来)。finalize()
方法。finalize()
方法可能在任何时机以任何次序执行,所以如果要覆盖finalize()
方法,释放的操作不能有严格次序关系。[public]enum 枚举类型名称 [implements 接口名称列表]{ 枚举值; 变量成员声明及初始化; 方法声明及方法体; }
枚举类是一种类,也可以声明为public
,不声明则默认为包内的(见[2.2-5类的访问权限控制](##2.2-5 类的访问权限控制 ))
例:简单的枚举类型
enum Score { EXCELLENT, QUALIFIED, FAILED; }; public class ScoreTester { public static void main(String[] args) { giveScore(Score.EXCELLENT); } public static void giveScore(Score s) { switch (s) { case EXCELLENT: System.out.println("Excellent"); break; case QUALIFIED: System.out.println("Quqlified"); break; case FAILED: System.out.println("Failed"); break; } } }
java.lang.Enum
类,因此枚举类型不能再继承其他任何类(继承会在后续章节介绍)。values()
方法用于获得枚举类型的枚举值的数组,即values()
会返回一个数组包含所有枚举值。toString
方法返回枚举值的字符串描述,即,将枚举值转换成字符串类型。valueOf
方法将以字符串形式表示的枚举值转化为枚举类型的对象。Ordinal
方法获得对象在枚举类型中的位置索引。例:
public enum Planet {//Planet类代表太阳系中行星的枚举类型 MERCURY(3.303e+23, 2.4397e6), VENUS(4.869e+24, 6.0518e6), EARTH(5.976e+24, 6.37814e6), MARS(6.421e+23, 3.3972e6), JUPITER(1.9e+27, 7.1492e7), SATURN(5.688e+26, 6.0268e7), URANUS(8.686e+25, 2.5559e7), NEPTUNE(1.024e+26, 2.4746e7); private final double mass; // in kilograms private final double radius; // in meters Planet(double mass, double radius) {//定义了构造方法,用参数方法初始化mass和radius this.mass = mass; this.radius = radius; } private double mass() {//返回mass值,私有方法 return mass; } private double radius() {//返回radius值,私有方法 return radius; } // universal gravitational constant (m3 kg-1 s-2) public static final double G = 6.67300E-11;//静态final常量 double surfaceGravity() { return G * mass / (radius * radius);//处理枚举对象,说明枚举对象也可以有功能 } double surfaceWeight(double otherMass) { return otherMass * surfaceGravity();//处理枚举对象 } public static void main(String[] args) {//获得命令行参数 if (args.length != 1) { System.err.println("Usage: java Planet <earth_weight>"); System.exit(-1); } double earthWeight = Double.parseDouble(args[0]); double mass = earthWeight / EARTH.surfaceGravity(); for (Planet p : Planet.values())//专门处理数组/集合类型的增强型循环,p是对象,是在定义枚举类的时候自动生成的 System.out.printf("Your weight on %s is %f%n", p, p.surfaceWeight(mass)); } }
本章将会通过一个银行账户类的实例复习学过的语法。
public class BankAccount { private String ownerName; private int accountNumber; private float balance; public BankAccount() { this("", 0, 0); } public BankAccount(String initName, int initAccNum, float initBal) { ownerName = initName; accountNumber = initAccNum; balance = initBal; } } public String getOwnerName() { return ownerName; } public int getAccountNumber() { return accountNumber; } public float getBalance() { return balance; } public void setOwnerName(String newName) { ownerName = newName; } public void setAccountNumber(int newNum) { accountNumber = newNum; } public void setBalance(float newBalance) { balance = newBalance; }
测试类——AccountTester.java
public class AccountTester { public static void main(String[] args) { BankAccount anAccount; anAccount = new BankAccount("ZhangLi", 100023, 0); anAccount.setBalance(anAccount.getBalance() + 100); System.out.println("Here in the account: " + anAccount); System.out.println("Account name: " + anAccount.getOwnerName()); System.out.println("Account number:" + anAccount.getAccountNumber()); System.out.println("Balance:$" + anAccount.getBalance()); } }
输出结果
Here in the account: BankAccount@379619aa Account name: ZhangLi Account number:100023 Balance:$100.0
以下将对银行账户类进行修改并测试:
toString()
方法。Decimal Format
类。toString()
方法在每个类中默认都有一个toString()
方法,当我们在上下文需要一个字符串String
类型的时候,如果我们给了一个类的对象,会自动调用类的toString()
方法。即,System.out.println(anAccount);
和System.out.println(anAccount.toString());
等价。
由于自带的toString()
方法用处不大,我们如果需要特殊的转换功能,则需要自己覆盖toString()
方法,并需要遵循以下原则。
public
类型。String
。toString()
,且没有参数。System.out.println()
。为BankAccount
覆盖toString()
方法
public String toString() { return ("Account#" + accountNumber + " with balance $" + balance); }
重新编译BankAccount
类,并运行测试类BankAccountTester
,结果如下:
Here in the account: Account#100023 with balance $100.0 Account name: ZhangLi Account number:100023 Balance:$100.0
银行账户中的余额不应当是随意变动的,而是通过存取款操作来发生改变的。
给BankAccount
类增加存款及取款方法
//存钱 public float deposit(float anAmount) { balance += anAmount; return (balance); } //取钱 public float withdraw(float anAmount) { balance -= anAmount; return (balance); }
测试存取款——修改AccountTester.java
public class AccountTester { public static void main(String[] args) { BankAccount anAccount; anAccount = new BankAccount("ZhangLi", 100023, 0); anAccount.setBalance(anAccount.getBalance() + 100); System.out.println(anAccount); System.out.println(); anAccount = new BankAccount("WangFang", 100024, 0); System.out.println(anAccount); anAccount.deposit(225.67f); anAccount.deposit(300.00f); System.out.println(anAccount); anAccount.withdraw(400.17f); System.out.println(anAccount); } }
测试结果
Account#100023 with balance $100.0 Account#100024 with balance $0.0 Account#100024 with balance $525.67 Account#100024 with balance $125.49997
DecimalFormat
类(格式化)DecimalFormat
类在java.text
包中。toString()
方法中使用DecimalFormat
类的实例方法format
对数据进行格式化。进一步修改toString
方法,给输出金额设置格式
public String toString() { return ("Account#" + accountNumber + " with balance" + new java.text.DecimalFormat("$0.00").format(balance)); }
更多内容关于DecimalFormat
的内容可以查看Java的API文档,学会查看文档是一个程序员的必备技能。
例:使用静态方法(类方法)生成三个样例账户
public static BankAccount example1() { BankAccount ba = new BankAccount(); ba.setOwnerName(("LiHong")); ba.setAccountNumber(554000); ba.deposit(1000); return ba; } public static BankAccount example2() { BankAccount ba = new BankAccount(); ba.setOwnerName("ZhaoWei"); ba.setAccountNumber(554001); ba.deposit(1000); ba.deposit(2000); return ba; } public static BankAccount emptyAccountExample() { BankAccount ba = new BankAccount(); ba.setOwnerName("HeLi"); ba.setAccountNumber(554002); return ba; }
例:修改账号生成、余额变动方式
setAccountNumber
方法。LAST_ACCOUNT_NUMBER
初始值为0,当生成一个新的BankAccount
对象时,其账号(accountNumber
)自动设置为LAST_ACCOUNT_NUMBER
的值累加1。setBalance
方法,仅通过存取款操作改变余额。修改后完整的BankAccount2.java
package hello; package hello; public class BankAccount2 { private static int LAST_ACCOUNT_NUMBER = 0; private int accountNumber; private String ownerName; private float balance; public BankAccount2() { this("", 0); } public BankAccount2(String initName) { this(initName, 0); } public BankAccount2(String initName, float initBal) { ownerName = initName; accountNumber = ++LAST_ACCOUNT_NUMBER; balance = initBal; } public String getOwnerName() { return ownerName; } public int getAccountNumber() { return accountNumber; } public float getBalance() { return balance; } public void setOwnerName(String newName) { ownerName = newName; } public String toString() { return ("Account#" + new java.text.DecimalFormat("000000").format(accountNumber) + " with balance" + new java.text.DecimalFormat("$0.00").format(balance)); } public float deposit(float anAmount) { balance += anAmount; return (balance); } public float withdraw(float anAmount) { balance -= anAmount; return (balance); } public static BankAccount2 example1() { BankAccount2 ba = new BankAccount2(); ba.setOwnerName(("LiHong")); ba.deposit(1000); return ba; } public static BankAccount2 example2() { BankAccount2 ba = new BankAccount2(); ba.setOwnerName("ZhaoWei"); ba.deposit(1000); ba.deposit(2000); return ba; } public static BankAccount2 emptyAccountExample() { BankAccount2 ba = new BankAccount2(); ba.setOwnerName("HeLi"); return ba; } }
测试程序AccountTester2.java
public class AccountTester2 { public static void main(String[] args) { BankAccount2 bobsAccount, marysAccount, biffsAccount; bobsAccount = BankAccount2.example1(); marysAccount = BankAccount2.example1(); biffsAccount = BankAccount2.example2(); marysAccount.setOwnerName("Mary"); marysAccount.deposit(250); System.out.println(bobsAccount); System.out.println(marysAccount); System.out.println(biffsAccount); } }
样例输出:
Account#000001 with balance$1000.00 Account#000002 with balance$1250.00 Account#000003 with balance$3000.00