- 在本教程中,我们将介绍一些常用于Java开发项目的高级工具,其中包括:
- 继承与抽象
- 接口
- 嵌套类
- 正则表达式
- 集合
- 日期
- I / O
继承与抽象
Java代码中的类存在于层次结构中。层次结构中给定类之上的类是该类的超类。那个特定的类是每一个类的子类都更高。一个子类继承自其超类。这个课程Object
是每个班级层次的顶端。换句话说,每个类都是(并继承自)的子类Object
。
例如,假设我们有一个Adult
类似如下的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
我们的Adult
类隐含地继承Object
。这是任何类的假设,所以你不必键入extends Object
类定义。但是,我们的类继承自其超类是什么意思?它只是意味着可以Adult
访问其超类中的暴露的变量和方法。在这种情况下,这意味着Adult
可以从任何超类看到并使用以下内容(我们目前只有一个):
-
public
方法和变量 -
protected
方法和变量 - 包保护的方法和变量(即那些没有访问说明符的方法),如果超类在同一个包中
Adult
构造函数是特殊的。他们不是成熟的OO成员,所以他们不是继承的。
如果一个子类覆盖了超类中的一个方法或变量 - 如果子类实现了一个具有相同名称的成员,换句话说 - 隐藏超类的成员。为了准确,覆盖一个变量隐藏它,并且覆盖一个方法只是覆盖它,但效果是一样的:覆盖的成员基本上是隐藏的。您仍然可以使用super
关键字来获取超类的成员:
1 | |
在这种情况下Adult
,所有它继承的是在Object
(toString()
例如)上的方法。因此,以下代码段完全可以接受:
1 2 | |
该toString()
方法不是明确存在的Adult
,而是Adult
继承它。
这里有一些“陷阱”,你应该记住。首先,在一个子类中给变量和方法与该类的超类中的变量和方法相同的名称很容易,然后在不能调用继承的方法时变得困惑。记住,当一个方法与一个已经存在于一个超类中的子类中具有相同的名称时,你已经隐藏了它。其次,构造函数不是继承的,而是被调用。在您编写的任何子类构造函数中,对超类构造函数进行隐式调用,这是子类构造函数的第一件事。你必须这样生活 没有什么可以改变它。例如,我们的Adult
构造函数实际上在运行时看起来像这样,即使我们没有在body中输入任何东西:
1 2 3 | |
构造函数体中的这一行调用超类的无参数构造函数。在这种情况下,这是构造函数Object
。
定义类层次结构
假设我们有另一个类叫做Baby
。看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
我们Adult
和Baby
班级看起来很相似。事实上,它们几乎相同。这种代码重复使维护代码比它需要更痛苦。我们可以创建一个超类,将所有常见元素移动到该类,并删除代码重复。我们的超类可以被调用Person
,它可能是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
现在我们可以拥有Adult
和Baby
子类Person
,这使得这两个类目前很简单:
1 2 3 4 5 6 7 8 | |
我们有一个层次结构,我们可以将每个子类的实例引用为层次结构中任何一个超类的实例。例如:
1 2 3 4 5 | |
这个代码将给我们三个true
结果和一个false
结果。您还可以将对象投射到其层次结构中的任何类型,如下所示:
1 2 3 | |
这段代码将会编译没有问题。我们可以转换一个Adult
类型Person
,然后调用它的Person
方法。
因为我们有这个层次结构,我们的子类的代码更简单。但是你在这里看到一个问题吗?现在所有Adult
的人和所有人Baby
(借口不好的复数)会以同样的方式说话和移动。每个行为只有一个实现。这不是我们想要的,因为成年人不会像婴儿一样说话或移动。我们可以覆盖move()
和talk()
子类,但是我们在我们的超类中定义了基本上无用的“标准”行为。我们真正想要的是强制我们每个子类以自己的特定方式移动和谈话的一种方式。这就是抽象类。