方法对实例变量的使用:对象行为方式
对象行为与状态之间的互动(状态影响行为,行为亦影响状态)。我们已然知道对象有着 状态 与 行为,分别由 实例变量 和 方法 所表示。但直到现在,我们仍对二者之间是如何关联起来的一无所知。我们已经知道某个类的各个实例(也就是某个特定类型的各个对象),对于其实例变量,都可以有独特的值。Dog
A 的名字可以是 Fido
,重量是 70
磅,Dog
B 的名字是 Killer
,重量是 9
磅。如果 Dog
类有一个 makeNoise()
方法,那么 70 磅的狗子发出的声音肯定要比 9 磅的狗子低沉一些。幸运的是,这就是对象的应有之义 -- 其行为取决于其状态。换句话说,就是 方法使用到实例变量的值。比如”如果狗子重量大于14磅,那么就会发出嚎叫声,相反则会发出呜呜声“,或者“重量每增加5磅,发出的声音就低一些”。
类描述了对象知道什么,和做些什么(A class describes what an object knows and what it does)
类是对象的蓝图(A class is the blueprint for an object)。 在编写某个类的时候,实际上实在告诉JVM,怎样去构造那个类型的对象。我们已经知道某个类型的各个对象,可以有着不同的实例变量值,那么在方法上呢?
某个类型的各个对象,可以有着不同的方法行为吗?
答案是:...当然可以。
特定类的各个实例,有着同样的方法,但根据各自实例变量的值,这些方法可以行事各异。
-
可以向方法传递值。方法使用参数,调用者传递参数(A method use parameters. A caller passes arguments)。
-
又可以从某个方法,得到返回值。
-
Java 是值传递的,也就是说传递的是拷贝(Java is pass-by-value. That means pass-by-copy)。
-
在往方法传递对象引用变量(某个类的类型变量),而不是原生类型变量时,情况就比较复杂了。但是仍然是值传递。Java中的参数传递,全都是值传递,传递的是对象地址(指针)的一个拷贝。
-
方法只能声明一个返回值。对于同一类型的几个值,可以返回一个数组。对于要返回不同类型的几个值,情况则稍微复杂。后续章节中讲到
ArrayList
时,会讲到这个问题。 -
可以返回任何的可 隐式 提升到所声明的返回值类型的值(Anything could be implicitly prometed to the type declared, can be returned as the return value)。因此就可以把一个
byte
类型的值,作为int
类型,返回给调用者。函数调用者不会 care 这个,因为byte
类型恰好与int
是兼容的,调用者就会把其作为返回赋值。在所声明的类型比返回值的空间要大的时候,就要 显式 的进行截取了。 -
可以不理会被调用方法的返回值。不必接收返回值。
-
Getters 和 Setters,访问器与修改器,从对象中获取或设置实例变量(对象属性,attributes)。访问器的唯一目的,就是取得某个实例变量的值。
-
封装(encapsulation),特指类的封装。定义/编写类/类型(class/type)的时候,将实例变量标记为
private
,将访问器和修改器标记为public
。避免外部直接接触到对象的实例变量(instance variables, 属性,attributes)。 -
封装给对象的实例变量装上了金钟罩,从此就没有人能把 不恰当(inappropriate) 的值设置给他们了。
-
大多数实例变量,都硬编码了其值的范围确定的具体条件。比如说,如果允许负数出现,那么所有事情都将崩溃。淋浴房的数量、飞机的航速、生日、杠铃重量、手机号码、微波炉功率等等....
-
通过强制其他代码通过修改器(
setter
)检查,来为实例变量设置边界。修改器可以对参数进行检查,从而确定是否可行。也许修改器会拒绝而什么也不干,或者将抛出一个例外(Exception
,比如对于某个信用卡应用来说无效的社保编号),或者修改器会把传递进来的参数,按照最接近的可接受值进行保留取值。关键是,可以在修改器方法中,做任何想要的操作。相反如果实例变量是public
的话,就什么也做不了了。 -
修改器的要点(同时访问器也是),就是 可以在不破坏外部代码的情况下,于后期改变主意!封装的价值就在于,你可以改变主意,没有人会受伤害。直接去使用实例变量所能得到的性能优势,相对使用封装所带来的好处,简直不值一提。
实例变量总是会获得一个默认值,在没有显示地给某个实例变量赋值,也没有调用修改器的时候,实例变量仍然有着一个值!
类型 | 默认值 |
---|---|
整数 | 0 |
浮点数 | 0.0 |
逻辑值 | false |
引用(references) | null |
实例变量与本地变量的不同之处
- 实例变量是在类中、方法之外声明的。
- 本地变量是在方法中所声明的。
- 本地变量在使用前必须被初始化!
本地变量不会获取到一个默认值!在变量尚未初始化之前就使用加以使用,编译器就会报出错误。
变量的比较(包括原生变量与引用变量)
使用 ==
来比较两个原生变量
==
可用于比较任何类型的原生变量,只是简单的对数据位(bits)进行比较。if (a==b){...}
就是去查看a
和b
中的数据位,如果二者的位模式一致,就返回 true
(他不会关心变量的尺寸,因此变量左侧的那些额外的0
就没有关系)。
int a = 3;
byte b = 3;
if (a==b) {// true}
要检查两个引用变量是否同样(就是说他们是否是内存堆上同一对象的引用),也是使用 ==
。
对于原生变量和引用变量来说,==
运算符都是只关心变量中的数据位模式。因此在两个引用变量都是指向同一个对象时,将返回 true
!就算我们不知道引用变量的位模式(bit pattern)具体是怎样的(因为这取决于 JVM,对我们是隐藏的),但我们是知道他看起来是怎样的,两个指向同一对象的引用,将是相同的。
Foo a = new Foo ();
Foo b = new Foo ();
Foo c = a;
if (a == b) { // false }
if (a == c) { // true }
if (b == c) { // false }
问题 | 答案 |
---|---|
类可以有着任意数量的。 | 实例变量,访问器,修改器,方法 |
方法只能有一个的。 | return |
可以隐式提升的(This can be implicitly promoted)。 | return , argument |
优先使用私有的实例变量(Prefer instance variables private)。 | 封装 |
就是要“生成一份拷贝”(It really means 'make a copy')。 | 值传递 |
只能由修改器来更新。 | 实例变量 |
方法可以后多个的。 | 参数 |
根据定义返回一些东西。 | 访问器(getter) |
不应和实例便利一起使用的。 | public |
可以有多个参数 | 方法 |
有助于建立良好封装的。 | 访问器,修改器,public , private |
总是独行的(I always fly solo)。 | return |