天天看点

Java 核心学习——第四章对象与类

对象与类

1、  基本概念

1)  类是同一类事物的抽象描述,对象实这一类事物的某个实例的称谓。类是构造对象的模板,类构造对象的过程为创建对象实例。

2)  实例域:对象中的数据[C++ 称成员变量];

3)  方法:操作数据的过程[C++ 称成员函数];

4)  状态:每个特定的对象实例都有一组特定实例域值,这些值的集合描述了对象的当前特征信息, 这就是对象的状态;

5)  封装是对象隐藏实例域的关键,是面向对象编程的一个重要特点。

6)  对象的三个主要特性

A)      对象的行为(behavior)——可以对对象施加哪些行为;

B)      对象的状态(state)——当在对象上施加某一个/一些行为之后, 对象如何响应/变化;

C)      对象标识(identity)——如何区分具有相同行为与状态的不同对象;

7)  类之间的关系

A) 依赖(dependency)("uses -a”)A依赖B, 就表示B可以被A使用;尽量不要两个类之间相互依赖;

B) 聚合(aggregation)("has -a”)A 聚合B,就表示B是A构成的一部分;

C) 继承(inheritance)("is-a”)A继承自B,就表示A是B的一种特例;

2、  对象和对象变量

1)  一个对象变量并没有实际包含一个对象,而仅仅是一个对象的引用。Java中任何对象变量的值都是对存储在另外一个地方(堆内存区)的一个对象的引用[注:这与C++中的引用是不同的,C++中的任何真实类型都可以有引用,Java中对象的变量才是对象的引用, C++ 中引用类型变量必须赋值为一个地址,并且不能为空,Java中的对象引用可以为null ];

2)  new 操作返回值也是对象的一个引用;

3)  对象变量可以显示设置为null,表示这个对象变量没有引用任何对象;

4)  Java中所有对象都存储在堆中;

5)  当一个对象包含另一个对象变量时,这个变量依然包含着指向另外一个堆对象的指针;

3、  方法

1)  更改其方法(mutator method), 对实例域做出修改的方法;

2)  访问器方法(accessor method),仅访问实例域而不进行修改[注:C++ 中访问器方法后面增加const,java中访问器方法和更改器方法在语法上没有区别], 访问器方法通常以get开头;

4、  类

1)  类编写风格:首先构造器方法, 共有方法, 私有方法, 域(数据);

2)  一个源文件中,只能有一个公有类,并且文件名与公有类名完全相同, 但是可以包含多个非公有类;

3)  类中数据域通常用private修饰,不建议使用public修饰,不破坏封装原则;

5、  构造器

1)  构造器方法名必须与类名相同;

2)  构造器方法没有返回类型;

3)  构造器方法总是伴随new操作符的执行被调用(原因:Java对象都在堆中构造)[注:C++中可以构造堆对象通过new操作调用构造器,也可以定义栈中定义一个对象,自动调用构造器],而不能对一个已经存在的对象调用构造器方法重新设置对象的数据域,;

4)  每个类可以有多个构造器方法, 构造器方法可以有0个、1个或多个参数;没有参数的构造器称之为默认构造器,

5)  不要再构造器方法中定义与域数据重名的变量,否则在构造器方法中将隐藏了域数据变量;

6、  隐式参数与显示参数

1)  对象的方法调用的时候,第一个参数为隐式参数,其参数为调用方法前面的对象;

2)  位于方法的括号中的参数为显示参数;

3)  每个方法中this为隐式参数;

7、  方法定义

Java中方法都定义在类的内部,而C++ 中方法可以定义在类定义内部默认为inline,也可以定义外类外部;Java中方法是否为inline,不能由程序员设置,Java虚拟机会监视方法的调用情况、方法的简洁度和是否被重载决定是否进行优化(inline);

8、  封装的优点

1)  方法为接口,接口被用户调用,隐藏内部对数据域操作的实现,实现修改了,不影响调用者代码;

2)  更改器方法可以执行错误检查, 然而直接对数据域赋值就改变了对象的状态;

3)  不要设计返回对象引用的访问器方法,否则调用者可以通过对象的引用修改对象的状态,建议在访问器内克隆一个对象返回[Java中所有类都是Object的子类,Object 拥有clone方法];

9、  基于类的访问权限

1)  一个方法可以访问所属类的所有对象的私有数据;访问可以方法所属类的私有特性(数据|方法),而不仅仅限于隐式参数的私有特性;

2)  Final修饰实例域表示实例域在对象构造之后, 不会再被改变, final修饰的通常是基本数据类型或者不可变类(类中的每个方法都不可以改变对象,这样的类称为不可变类)的域;

3)  对象访问器如果返回final修饰的实例域可变类对象,但是调用者可以通过返回的对象调用对象的修改器, 这样导致对象被改变,这样导致破坏封装,如果要返回可变类,应该在访问器中构造新对象返回. [C++中常量对象只能调用对象的访问器,而不能调用修改器]

例如:

class Employee

{

               public Address getAddr()

               {

                  return addr;

               }

               private String name;

               private double salary;

      private final Address addr;

}

         class Address

         {

           public void setCode(String code)

           {

              this.code = code;

           }

           private String city;

           private String home;

           private String code;

         }

       Employee emp = new Employee(…);

       emp.getAddr().setCode(“adfad”);

10、  静态域和静态方法

1、  域被定义成静态的(static),表示所有对象都共用静态域,而非静态域则是每个对象都有。Java和C++ 中静态方法和静态域在功能上相同,访问方法不同,Java中通过[类名.方法/类名.域]访问,但是C++中用[类名::方法/类名::域]访问;

2、  Factory方法, NumberFormat类使用factory方法产生不同风格的对象;

11、  方法参数[C++中有传值和传引用]

1)  一个方法不能修改一个基本数据类型的参数;

2)  一个方法可以修改一个对象参数的状态;

3)  一个方法不能实现让对象引用一个新的对象;

12、  对象初始化

1)  默认构造器是指没有参数的构造器,如果编写一个类的时候,没有提供一个构造器,系统会提供一个默认构造器。 默认构造器将所有的实例域设置为默认值。数值型设置为0,布尔型设置为false, 所有对象变量将设置为null。如果类中提供了至少一个构造器,系统将不会提供默认构造器。

2)  显示初始化域, 在执行构造器之前对域进行赋值(声明的时候赋值)[C++中不能初始化实例域,但是有初始化列表,在执行构造器的时候, 在执行其方法实体前,Java没有子对象,只有子对象引用,因此java不需要初始化列表]。

3)  调用另一个构造器,对于同一个类中,一个构造器需要调用另一个构造器,用this(参数列表)[C++中,this指针不能作为构造器使用,也不能在一个构造器中调用另一个构造器],构造器中调用另一个构造器必须是该方法中的第一个语句。

4)  初始化块,用{} 在类中定义一个语句块,即不是方法定义,也不是数据定义;初始化块可以在对数据域进行改变, 它在构造器执行之后才执行的。

5)  对于静态域的初始化,不能在构造器中执行,可以在静态初始化块中执行,静态初始化块的定义如:static{};静态初始化块在类第一次加载的时候执行。

13、  析构

Java中没有析构器[ C++有析构函数, 在对象生命周期结束时调用],Java中有垃圾回收机制,因此不需要释放内存, 对于其他资源,建议在不在使用的时候, 立即释放。Java中可以为一个类添加finalize方法, 该方法将在垃圾回收器清除对象之前调用,但是Java中垃圾回收时机用户不能确定,因此不建议使用。

14、  包

1)        Java中使用包(packet)将类组织起来。借助于包可以方便的组织自己的代码, 并将自己的代码与别人提供的代码库分开管理。强调一下,这里说的是组织, 而不是简单的存放, 组织也表示了代码结构组织。

2)        使用包的另一个优点,确保类名唯一性。

3)        包命名规则, Sun建议使用公司internet域名的逆序作为包名,不同项目可以增加子包。

4)        一个类可以使用所属的包中的所有类,以及其他包中的公有类(public class)

5)        导入

A) 在每个类名称前面带完整的包名, 但是这样使得代码难看, 可以使用import导入某个包或者包中的类,import语句位于源文件的顶部(packet语句之后);

B) 导入是可以通过 import包名.*导入所有类,但是不建议这样使用,容易造成编译的时候类名称冲突导致编译失败(包的定位是编译器的工作), 但是Java字节码标示类是通过完整的包名引用的;

C) Java的import与C++ 的#inlucde不同, C++中#include将外部特性的声明加载进来,C++编译器无法查看任何文件的内部;Java编译器可以查看其它文件的内部,只要告诉编译器从哪里查找。C++中,命名空间(namespace)与Java中的包机制类似, package与import语句类似于C++中的namespace和using 指令。

D) 静态导入, Java SE5.0开始, import语句不仅可以导入类,还可以导入静态方法和静态域功能, 还可以导入特定的方法或域。

E)  Java.lang是默认导入的。

6)        包定义,要想将类加入某个包中,就必须将包的名字放在源文件的开头,包中定义类的代码之前。 如果没有在源文件中放置package语句,这个源文件中的类就被放置在一个默认包中。

7)        包作用域,public修饰的部分,可以被任意类使用;private修饰的部分只能被定义它们的类使用;没有指定public或private的,可被同一个包中的所有方法访问。对于类如果只在包中访问,可以不指定修饰符,但是数据域最好指定修饰符。

15、  类设计技巧

1)  一定将数据设计为私有, 不能破坏封装;

2)  一定要对数据初始化, Java不对局部变量进行初始化, 但是会对对象的实例域进行初始化。最好不要依赖系统的默认值。

3)  不要在类中使用过多的基本数据类型;

4)  不是所有的域都需要独立的访问器和更改器;

5)  使用标准格式进行类定义,并保持一致的风格;

A) 公有访问特性部分;

B) 包作用域访问特性部分;

C) 私有访问特性部分;

D) 实例方法;

E)  静态方法;

F)  实例域;

G) 静态实例域;

6)  将职责过多的类分解成不同的类;

7)  类名和方法名能够体现它们的职责;类名通常是一个名词,可以用动名词/形容词+ 名词