面向对象与类(对象,类,包,OOP,重载,继承...)

面向对象概述

面向对象程序设计(简称 OOP,Object Oriented Programming) 是当今主流的程序设计范型

Java 是完全面向对象的,必须熟悉 OOP才能够编写 Java 程序。

**类**( class) 是构造对象的模板或蓝图。由类构造(construct) 对象的过程称为创建类的实例 (instance )。

**封装**( encapsulation , 有时称为数据隐藏) 是与对象有关的一个重要概念。

从形式上看, 封装不过是将数据和行为组合在一个包中, 并对对象的使用者隐藏了数据的实现方式。

对象 中的数据称为实例域( instance field ), 操纵数据的过程称为方法( method 。) 

对于每个特定的 类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态( state )。

无论何时,只要向对象发送一个消息,它的状态就有可能发生改变。

实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域。

OOP 的另一个原则会让用户自定义 Java 类变得轻而易举,这就是:可以通过扩展一个 类来建立另外一个新的类。

事实上, 在 Java 中, 所有的类都源自于一个“ 神通广大的超类”, 它就是 Object。

继承(inheritance)    

在扩展一个已有的类时, 这个扩展后的新类具有所扩展的类的全部属性和方法。在新类 中,只需提供适用于这个新类的新方法和数据域就可以了。通过扩展一个类来建立另外一个 类的过程称为继承(inheritance,)

对象

要想使用 OOP, —定要清楚对象的三个主要特性:

对象的行为(behavior) ———可以对对象施加哪些操作,或可以对对象施加哪些方法?

对象的状态(state ) ———当施加那些方法时,对象如何响应?

对象标识(identity ) ———如何辨别具有相同行为与状态的不同对象?

同一个类的所有对象实例, 由于支持相同的行为而具有家族式的相似性。对象的行为是 用可调用的方法定义的。

此外,每个对象都保存着描述当前特征的信息。这就是对象的状态。对象的状态可能会 随着时间而发生改变,但这种改变不会是自发的。对象状态的改变必须通过调用方法实现 (如果不经过方法调用就可以改变对象状态,只能说明封装性遭到了破坏。

但是,对象的状态并不能完全描述一个对象。每个对象都有一个唯一的身份( identity。)

类和类之间的关系

在类之间,常见的关系有:**依赖(“ uses-a”),聚合(“ has-a”),继承(“ is-a”)**。

依赖( dependence )

即“ uses-a” 关系, 是一种最明显的、 最常见的关系。例如,Order 类使用 Account 类是因为 Order 对象需要访问 Account 对象查看信用状态。但是 Item 类不依 赖于 Account 类, 这是因为 Item 对象与客户账户无关。因此, 如果一个类的方法操纵另一个 类的对象,我们就说一个类依赖于另一个类。应该尽可能地将相互依赖的类减至最少,用软件工程的术语来说,就是 让类之间的耦合度最小。

聚合(aggregation )

即“ has-a ” 关系, 是一种具体且易于理解的关系。一个 Order 对象包含一些 Item 对象。聚合关系意味着类 A 的对象包含类 B 的对象。

聚合也可以被称为关联。

 对象与对象变量

要想使用对象,就必须首先构造对象, 并指定其初始状态。然后,对对象应用方法。 

在 Java 程序设计语言中, 使用构造器(constructor ) 构造新实例。构造器是一种特殊的方法, 用来构造并初始化对象。

构造器的名字应该与类名相同。因此 Date 类的构造器名为 Date。要想构造一个 Date 对 象, 需要在构造器前面加上 new 操作符,如:new Date()

这个表达式构造了一个新对象。这个对象被初始化为当前的日期和时间。也可以将该类传递给一个方法,如:System.out.println(new Date());

一定要认识到: 一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。

在 Java 中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。new 操作符的返回值也是一个引用。

局部变量不会自动地初始化为 null,而必须通过调用 new 或将它们设置为 null 进行初始化。

用户自定义类

之前学的都是通过main方法来加载执行程序的,接下俩我们需要学习自己编写的自己的类来运行程序,这些类被称为主力类(workhorse class),通常, 这些类没有 main 方法, 却有自己的实例域和实例方法。 要想创建一个完整的程序, 应该将 若干类组合在一起, 其中只有一个类有 main 方法。

构造器

•构造器与类同名

•每个类可以有一个以上的构造器

•构造器可以有 0 个、1 个或多个参数

•构造器没有返回值

•构造器总是伴随着 new 操作一起调用

构造器与其他的方法有一个重要的不同。构造器总是伴随着 new 操作符的执行被调用, 而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。

构造器与类同名。

隐式参数与显示参数

第一个参数称为隐式 ( implicit ) 参数, 是出现在方法名前的 Employee 类对象。第二个参数位于方法名后面括号中的数值,这是一个显式 ( explicit) 参 数 。

显式参数是明显地列在方法声明中的。隐式参数没有 出现在方法声明中。

在每一个方法中, 关键字 this 表示隐式参数。

封装

LocalDate 类没有更改器方法, 与之不同, Date 类有一个更改器方法 setTime, 可以 在这里设置毫秒数。

final

final 修饰符大都应用于基本 (primitive ) 类型域,或不可变(immutable) 类的域(如果类 中的每个方法都不会改变其对象, 这种类就是不可变的类。

静态域

如果将域定义为 static, 每个类中只有一个这样的域。它属于类,而不属于任何独立的对象。

静态常量

静态变量使用得比较少,但静态常量却使用得比较多。静态常量:Math.PI

静态方法

可以认为静态方法是没有 this 参数的方法(在一个非静态的方法中,this 参数表示这个方法的隐式参数。)

可以使用对象调用静态方法。

•一 方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如:Math.pow) 。

•一个方法只需要访问类的静态域(例如:Employee.getNextld)。

工厂方法

静态方法还有另外一种常见的用途。类似 LocalDate 和 NumberFormat 的类使用静态工 厂方法 (factory methocO 来构造对象。

工厂方法 LocalDate.now 和 LocalDate.of。

为什么 NumberFormat 类不利用构造器完成这些操作呢? 这主要有两个原因:

•无法命名构造器。构造器的名字必须与类名相同。但是, 这里希望将得到的货币实例 和百分比实例采用不用的名字。

•当使用构造器时,无法改变所构造的对象类型。而 Factory 方法将返回一个 DecimalFormat 类对象,这是 NumberFormat 的子类。

main方法也是一个静态方法。

main 方法不对任何对象进行操作。事实上,在启动程序时还没有任何一个对象。静态的 main 方法将执行并创建程序所需要的对象。

方法参数

Java 程序设计语言对对象采用的不是引用调用。

•一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。

•一个方法可以改变一个对象参数的状态。

•一个方法不能让对象参数引用一个新的对象。

重载

如果多个方法(比如, StringBuilder 构造器方法)有 相同的名字、 不同的参数,便产生了重载。编译器必须挑选出具体执行哪个方法,它通过用 各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。如 果编译器找不到匹配的参数, 就会产生编译时错误,因为根本不存在匹配, 或者没有一个比 其他的更好。这种特征叫做重载( overloading。)这种特征叫做重载( overloading。)

Java 允许重载任何方法, 而不只是构造器方法。

无参数的构造器

如果在编写一个类时没有编写构造器, 那么系统就会提供一个无参数构造器。

如果类中提供了至少一个构造器, 但是没有提供无参数的构造器, 则在构造对象时如果 没有提供参数就会被视为不合法。

finalize方法:

可以为任何一个类添加 finalize 方法。finalize 方法将在垃圾回收器清除对象之前调用。

由于 Java 有自动的垃圾回收器,不需要人工回收内存, 所以 Java 不支持析构器。

Java 允许使用包( package > 将类组织起来。借助于包可以方便地组织自己的代码,并将 自己的代码与别人提供的代码库分开管理。

从编译器的角度来看, 嵌套的包之间没有任何关系。例如,java.utU 包与java.util.jar 包 毫无关系。每一个都拥有独立的类集合。

**静态导入**

import 语句不仅可以导人类,还增加了导人静态方法和静态域的功能。

**包作用域**

前面已经接触过访问修饰符 public 和 private。标记为 public 的部分可以被任意的类使 用;标记为 private 的部分只能被定义它们的类使用。如果没有指定 public 或 private , 这 个 部 分(类、方法或变量)可以被同一个包中的所有方法访问。

**通用注释**

•@author 姓名 这个标记将产生一个 ** author" (作者)条目。可以使用多个 @aUthor 标记,每个 @ author 标记对应一个作者 

·@version 这个标记将产生一个“ version”(版本)条目。这里的文本可以是对当前版本的任何描 述。 下面的标记可以用于所有的文档注释中。 

•@si nee 文本 这个标记将产生一个“ since” (始于)条目。这里的 text 可以是对引人特性的版本描 述。例如, ©since version 1.7.10

•@deprecated 这个标记将对类、 方法或变量添加一个不再使用的注释。文本中给出了取代的建议。 例如, @deprecated Use ` setVIsible(true)` instead 通过 @see 和@link标记,可以使用超级链接, 链接到 javadoc 文档的相关部分或外 部文档。 

•@see 引用 这个标记将在“ see also” 部分增加一个超级链接。它可以用于类中,也可以用于方 法中。这里的引用可以选择下列情形之一: package,class feature label

**类设计技巧**

OOP准则

1.一定要保证数据私有

这是最重要的;绝对不要破坏封装性。有时候, 需要编写一个访问器方法或更改器方法, 但是最好还是保持实例域的私有性。很多惨痛的经验告诉我们, 数据的表示形式很可能会改 变, 但它们的使用方式却不会经常发生变化。当数据保持私有时, 它们的表示形式的变化不 会对类的使用者产生影响, 即使出现 bug 也易于检测。

2.一定要对数据初始化

 Java 不对局部变量进行初始化, 但是会对对象的实例域进行初始化。最好不要依赖于系 统的默认值, 而是应该显式地初始化所有的数据, 具体的初始化方式可以是提供默认值, 也 可以是在所有构造器中设置默认值。

3.不要在类中使用过多的基本类型

 就是说,用其他的类代替多个相关的基本类型的使用。这样会使类更加易于理解且易于 修改。例如, 用一个称为 Address 的新的类替换一个 Customer 类中以下的实例域: private String street; private String city; private String state; private int zip; 这样, 可以很容易处理地址的变化, 例如, 需要增加对国际地址的处理。 

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

或许, 需要获得或设置雇员的薪金。 而一旦构造了雇员对象, 就应该禁止更改雇用日 期,并且在对象中,常常包含一些不希望别人获得或设置的实例域, 例如, 在 Address 类中, 存放州缩写的数组。

5.将职责过多的类进行分解

这样说似乎有点含糊不清, 究竟多少算是“ 过多” ? 每个人的看法不同。但是,如果明 显地可以将一个复杂的类分解成两个更为简单的类,就应该将其分解(但另一方面,也不要 走极端。设计 10 个类,每个类只有一个方法,显然有些矫枉过正了)

6.类名和方法名要能够体现它们的职责

 与变量应该有一个能够反映其含义的名字一样, 类也应该如此

7.优先使用不可变的类

LocalDate 类以及 java.time 包中的其他类是不可变的—没有方法能修改对象的状态。 类似 plusDays 的方法并不是更改对象,而是返回状态已修改的新对象。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值