类
表示方法及其处理的数据对象的集合的结构就是类, 类是比方法更大一级的控件,是面向对象编程中最基本的技术
一、数据操作
操作两个人的银行账户数据的程序:
class Accounts {
public static void main(String[] args) {
String adachiAccountName = "A"; // A的账户名
String adachiAccountNo = "123456"; // A的账号
long adachiAccountBalance = 1000; // A的可用余额
String nakataAccountName = "B"; // B的账户名
String nakataAccountNo = "654321"; // B的账号
long nakataAccountBalance = 200; // B的可用余额
adachiAccountBalance -= 200; // A取了200元
nakataAccountBalance += 100; // B存了100元
System.out.println("■A的账户");
System.out.println(" 账户名:" + adachiAccountName);
System.out.println(" 账号:" + adachiAccountNo);
System.out.println(" 可用余额:" + adachiAccountBalance);
System.out.println("■B的账户");
System.out.println(" 账户名:" + nakataAccountName);
System.out.println(" 账号:" + nakataAccountNo);
System.out.println(" 可用余额:" + nakataAccountBalance);
}
}
输出:用6个变量来表示两个人的银行账户数据:如adachiAccountName是账户名/adachiAccountNo是账号/adachiAccountBalance是可用余额
通过变量名和注释可以推测出名称以adachi开头的变量是A的银行账户相关的数据;但这并不是说A的账户名不可以用nakataAccountNo、 B的账号不可以用adachiAccountName来表示;
问题在于通过变量名只能推测但不能确定变量之间的关系;分散声明的账户名、账号、 可用余额等变量都是与一个银行账户相关的数据这种关系在程序上表现不出来。
1)在创建程序时会将现实中的对象(物体)和概念映射到程序中的对象(变量)中
上述程序中一个账户相关的账户名、账号、可用余额等数据分别被映射到了各个变量中;我们将目光放在账户的多个方面, 而不是一个方面,如图所示,可以将账户名、账号、可用余额映射为一个汇总的对象,这样的映射就是类的基本思想。
虽然会受到程序所处理的问题种类和范围的影响,但从现实世界向程序世界映射时,应遵循如下两点:
• 应该汇总的进行汇总
• 已经汇总的保持原状
应用上述原则进行改造:
class Account {
String name; // 账户名
String no; // 账号
long balance; // 可用余额
}
// 用于测试银行账户类的类
class AccountTester {
public static void main(String[] args) {
Account adachi = new Account(); // A的账户
Account nakata = new Account(); // B的账户
adachi.name = "A"; // A的账户名
adachi.no = "123456"; // A的账号
adachi.balance = 1000; // A的可用余额
nakata.name = "B"; // B的账户名
nakata.no = "654321"; // B的账号
nakata.balance = 200; // B的可用余额
adachi.balance -= 200; // A取了200日元
nakata.balance += 100; // B存了100日元
System.out.println("■A的账户");
System.out.println(" 账户名:" + adachi.name);
System.out.println(" 账号:" + adachi.no);
System.out.println(" 可用余额:" + adachi.balance);
System.out.println("■B的账户");
System.out.println(" 账户名:" + nakata.name);
System.out.println(" 账号:" + nakata.no);
System.out.println(" 可用余额:" + nakata.balance);
}
}
输出:程序由两个类构成,这两个类的概述如下:Account【银行账户类】、AccountTester· 测试Account类的类
之前的程序的类结构都以main方法为中心,而在本程序中.类AccountTester就相当于main方法,程序启动时会执行AccountTester中的main方法,源程序的文件名也是类名加扩展名.java (是AccountTester.java, 而不是Account. java) 。
二、类声明
这是表示类Account是"账户名、账号和可用余额的组合” 的声明开头的 "class Account {是声明的开始 . 到.. }声明结束 像这样的声明称为类声明
class Account {
String name; // 账户名
String no; // 账号
long balance; // 可用余额
}
大括号{}中是构成类的数据, 即字段的声明, 类Account由三个字段构成:
• 表示账户名的String型的name
• 表示账号的String型的no
• 表示可用余额的long型的balance
三、类和对象
类声明是类型的声明,而不是变量的声明类Account类型的变量的声明如下所示:
Account adachi = new Account(); // A的账户
Account nakata = new Account(); // B的账户
但是使用该声明创建的adachi和nakata并不是银行账户类的主体, 而是用来引用主体的类类型变量
类就像是做小丸子的模具,小丸子本身就像是数组一样,需要另行创建;类本身的“主体” 是使用new运算符, 通过下述形式的表达式创建的--new 类名()
上述程序中是newAccount (),类类型变量adachi和nakata的声明以及“主体”的创建情形如图所示
使用new运算符创建的类类型的“主体” 称为实例,创建实例的操作称为实例化;
类类型变员和实例必须关联起来, 实现这种关联的是如下所示的赋值操作:
adachi = new Account();
nakata = new Account();
这样, adachi和nakata就会引用创建的实例
四、实例变量和字段访问
类Account类型的实例是账户名、账号、可用余额的“集合” 变量, 访问其中某个字段时使用的是如下所示的成员访问运算符,该运符符通常被称为点运算符
例如访问A账户的各个字段的表达式如下所示:
adachi.name = "A"; // A的账户名
adachi.no = "123456"; // A的账号
adachi.balance = 1000; // A的可用余额
实例中的字段是针对各个实例分别创建的变量,因此被称为实例变量【adachi.name和nakata.balance都是实例变量】
实例中的各个字段, 即实例变量, 可以使用成员访问运算符, 通过“类类型变量 . 字段名“ 进行访问
上述代码存在问题:
1)无法保证确实进行了初始化
账户实例中的各个字段并没有显式进行初始化,只是在创建实例后进行赋值;
由于将是否设定值委托给了程序,因此当忘记初始化时,就可能会发生意想不到的危险【示例中账户名和账号都变为了null】。
2)无法保证数据受保护
A的可用余额adachi.balance的值可以通过程序自由读写,如果放在现实世界中,则意味着即使不是A(没有存折和签字),
也可以从A的账户中取出钱来。
一般来说、在现实世界中,就算公开了账号, 他人也无法操作可用余额。为了解决上述问题,可以定义并使用类下面我们就来改进一下程序。
// 银行账户类【第2版】和对其进行测试的类
class Account {
private String name; // 账户名
private String no; // 账号
private long balance; // 可用余额
//--- 构造函数 ---//
Account(String n, String num, long z) {
name = n; // 账户名
no = num; // 账号
balance = z; // 可用余额
}
//--- 确认账户名 ---//
String getName() {
return name;
}
//--- 确认账号 ---//
String getNo() {
return no;
}
//--- 确认可用余额 ---//
long getBalance() {
return balance;
}
//--- 存入k元 ---//
void deposit(long k) {
balance += k;
}
//--- 取出k元 ---//
void withdraw(long k) {
balance -= k;
}
}
// 用于测试银行账户类【第2版】的类
class AccountTester {
public static void main(String[] args) {
// A的账户
Account adachi = new Account("A", "123456", 1000);
// B的账户
Account nakata = new Account("B", "654321", 200);
adachi.withdraw(200); // A取了200元
nakata.deposit(100); // B存了100元
System.out.println("■A的账户");
System.out.println(" 账户名:" + adachi.getName());
System.out.println(" 账号:" + adachi.getNo());
System.out.println(" 可用余额:" + adachi.getBalance());
System.out.println("■B的账户");
System.out.println(" 账户名:" + nakata.getName());
System.out.println(" 账号:" + nakata.getNo());
System.out.println(" 可用余额:" + nakata.getBalance());
}
}
输出:
五、数据隐藏
新的类Account的结构如图所示,类声明的内部由三大部分构成:
按” 字段一构造函数一万法” 的顺序进行排列,但每个部分无需汇总在—起,顺序也可以任意排列。
a)字段
类Account中包含3个字段name、no、balance,在宇段声明中加上了关键字private,字段的访问属性变成了私有访问,私有访问的字段对类的外部是隐藏的。因此,类Account外部的类AccountTester中的main方法无法访问私有字段name 、no、balance。
将数据对外隐藏起来, 防止非法访问的操作称为数据隐藏
银行卡的密码都是私密的,如果将字段设为私有,进行数据隐藏, 除了数据的保护性、隐蔽性之外,程序的维护性也会提高,为了实现数据隐藏,提升程序的品质,原则上要将类中的字段都设为私有。另外,未指定为private的字段都是默认访问,默认访问是public。
b)构造函数
构造函数形式上与方法类似,用来初始化类的实例但又有如下不同之处:
• 名称与类相同
• 没有返回类型
构造函数是在创建实例时被调用的,在程序流程通过下面的声明语句对表达式进行求值时, 构造函数就会被调用并执行
// A的账户
Account adachi = new Account("A", "123456", 1000);
// B的账户
Account nakata = new Account("B", "654321", 200);
如图所示,调用的构造函数会将形参n、num 、z中接收的值赋给字段name、no、balance 。
赋值目标并不是adachi.name和nakata.name, 而是单纯的name;1处调用的构造函数中的name表示adachi.name;2处调用的构造函数中的name表示nakata.name。之所以能像这样只使用字段名来表示 ,是因为构造函数知道自身的实例是哪—个 ,每个实例都有其专门的构造函数
构造函数可以防止初始化不完整或者不正确,构造函数的作用就是正确地初始化实例;
当声明类类型时, 一定要提供构造函数, 以便切实并且正确地初始化实例。
如果类中未定义构造函数, 会自动创建一个不接收参数、主体为空的默认构造函数
六、方法
代码中方法如下:
//--- 确认账户名 ---//
String getName() {
return name;
}
//--- 确认账号 ---//
String getNo() {
return no;
}
//--- 确认可用余额 ---//
long getBalance() {
return balance;
}
//--- 存入k元 ---//
void deposit(long k) {
balance += k;
}
//--- 取出k元 ---//
void withdraw(long k) {
balance -= k;
}
1) getName:用于确认账户名,返回String型的字段name的值。
2) getNo:用于确认账号,返回String型的字段no的值。
3)getBalance:用于确认可用余额,返回long型的字段balance的值。
4)deposit :用于存钱,可用余额只增加K元。
5)withdraw:用于取钱,可用余额只减少K元。
类Account中的所有方法的声明都未加上static,声明中未加static的方法是在该类的各个实例中分别创建的,也就是说,adachi和nakata都有自己专用的方法getName、getNo、getBalance等,由于未加static的方法是属于各个实例的,因此称为实例方法。
在实例方法中,不使用adachi.name和nakata.name, 只使用单纯的name来访问其自身所属的实例的账户名字段(构造函数也是如此);此外,由于实例方法是类Account内部的,因此也可以访问私有字段(构造函数也是如此)。
调用方法实例:
adachi.getbalabce ; //确认A的可用余额
adachi.withdraw(200); // A取了200元
nakata.deposit(100); // B存了100元
与访问字段一样,上述中也使用了成员访问运算符,确认并显示A的可用余额的情形如图所示,调用的方法 getBalance 会直接返回字段 balance 的值;从类的外部无法直接访问的账号和可用余额等数据也都可以通过方法间接进行访问。
另外,由于构造函数并不是方法,因此无法对创建的实例使用与方法相同的方式来调用构造函数
adachi.Account("A", "123456", 1000);