本文共 5161 字,大约阅读时间需要 17 分钟。
由发表在
接下来我们进一步来学习一些面向对象编程的技术。先来了解封装的概念。
封装是一种隐藏信息的技术,是将一个系统中的结构和行为通过类来划分的过程。即通过定义一组类,将特定的数据组合到某一个类中,形成一个整体,将该隐藏的数据进行保护,只对外暴露这些数据的访问的方法。
封装代码有两个好处:
封装甚至被一些面向对象的开发人员视为第一原则。
如何隐藏类的具体实现和数据?其实之前的代码中我们已经用到了,现在我们来系统地了解一下。
Java是通过访问控制关键字来实现的信息隐藏的,一共有三个关键字:public
、protected
和private
。
关键字可用于修饰类,或者修饰类中的成员变量和成员方法。
用于修饰类
public
:其他任何类都可以访问该类private
:只能用于修饰内部静态类,一般不提倡用于修饰成员变量和成员方法:
public
:其他任何类都可以访问该成员protected
:只有继承自己的子类才能访问该成员private
:除自己外其他任何类都不能访问该成员 可见,如果要隐藏一个类的成员变量,只要在该成员变量的前面加上private
,这样外部就无法直接通过类的实例来访问这些成员变量了。
回头再看Post
类的代码,一开始我们就用public
修饰类本身,而用private
修饰了所有成员变量,现在你应该知道其中的含义了。
package com.tianmaying.domain;public class Post { private long id; private String title; private String content; private static int count = 0; ...}
想要让外部访问该成员变量的话,可以给这些私有成员变量添加public
的访问方法:
package com.tianmaying.domain;public class Post { ... public long getId() { return id; } public void setId(long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }}
这样的方法我们称之为setter
和getter
,在Eclipses中我们可以直接通过菜单栏的【Source】->【Generate Getters and Setters】来生成。
继承是一种类和类之间的关系,是面向对象系统的基石。继承表明为一个"是一种"(is-a)的关系,为在现实中有很多这样的例子:学生是一种人;树是一种植物,矩形是一种图案。
我们可以把共性的结构和行为放到父类中,子类可以通过继承复用父类中的代码,并且根据自己的需要进行扩展。
在Java中,使用extends
关键字表示继承关系,来看一个具体的例子:
class Graph { String name; public Graph(){} public Graph(String name) { this.name = name; } public void show() { System.out.println("I'm a graph"); }}class Rectangle extends Graph { int width; int height; public Rectangle(){ super(); } public Rectangle(String name) { super(name); } public Rectangle(int width, int height,String name) { this(name); System.out.println("My width is:" + width + "my height is :"+ height); } public void show() { super.show(); System.out.println("at the same time I'm a Rectangle"); }}
Java中的继承是单继承的,也就是说一个子类只能继承一个父类。子类会继承父类中的除构造函数以外的所有非private
成员方法,以及所有非private
成员变量。
具体到这个例子中来,由于Rectangle
继承了Graph
,它默认就具有了name
属性和show
方法,而无需自己声明。
生成子类对象或者实例时,Java默认地首先调用父类的不带参数的构造方法,接下来再调用子类的构造方法,生成子类对象。
this
表示对当前对象的引用,而super
表示对父类对象的引用。在子类的构造函数中,一般第一条语句是supre();
,表示调用父类构造函数。也可以调用父类有参数的构造函数,比如super(name);
。
如果一个类的构造函数的第一语句既不是this()
也不是super()
时,就会隐含的调用super()
。
如果子类中有和父类中非private的同名方法,且返回类型和参数表也完全相同,就会覆盖从父类继承来的方法。
当两个方法形成重写关系时,可以在子类中通过super
关键字调用父类被重写的方法。
比如Retangle
就覆盖了Graph
的show
方法,同时爱show
方法中通过super.show();
调用了父类的show
方法。
我们已经实现了PostRepository
,但是所有博客内容只保存在内存中,程序结束所有内容就会消失。假如我们希望将博客内容保存到TXT文件中,此时我们就可以定义一个FlatFilePostRepository
,通过继承PostRepository
就能自动拥有博客管理的功能,即add()
、remove()
等成员方法。然后我们只需在其中添加从文件加载博客loadData()
以及将博客写入文件saveData
两个方法即可。
package com.tianmaying.repository;public class FlatFilePostRepository extends PostRepository { public static void saveData() { // 从TXT文件加载所有博客信息 } public static void loadData() { // 将所有博客信息写入TXT文件 }}
如果没有继承,我们就要把PostRepository
中那些代码拷贝过来,这样的重复代码显然是难以维护的。我们从中可以看到继承对于代码复用的作用。
可能有人说可以直接在PostRepository
中添加saveData()
和loadData()
方法。那如果将来我们希望将博客信息保存到XML文件或者数据中时,怎么办呢?这里我们就能看到继承的第二个作用,建立这样的抽象层次,将来系统会更容易扩展。如果我们希望实现XML保存博客的功能,那么我们再实现一个XMLPostRepository
,让它也继承PostRepository
就行了。
至于这两个方法如何实现,我们后面讲解。
一个变量可以声明为final
,这样做的目的是阻止它的内容被修改。这意味着在声明final
变量的时候,必须初始化它(在这种用法上,final
类似于C/C++中的const
)。
通常情况下,我们会使用final
来定义一些常量,例如:
public class Post { private String title; private String content; private static int count = 0; public final static String DEAFUTL_Content = "这个家伙很懒,什么也没写"; public Post(){ count++; } public static int getCount(){ return count; } ...}
final变量的所有字符选择大写是一个普遍的编码约定,用final修饰的变量在实例中不占用内存,它实质上是一个常数。
这里我们定义了表示一篇博客的默认标题的静态变量DEAFUTL_Content
,我们用final
修饰该变量表明该对象在最初的赋值后是不可修改的,如果我们试图调用DEAFUTL_Content = "another title"
的话Java编译器会直接抛出错误。final
修饰符可以保证我们的变量值的安全性。
被final修饰的方法可以被子类继承,不能被子类的方法覆盖,因此,如果一个类不想让它的子类覆盖它的某个成员方法,就可以在该成员方法前面加上final
关键字
final不能修饰构造方法。由于父类中的private
成员方法是不能被子类覆盖的,所有有private
限制的成员方法默认也是final
的。
在之前的例子中,我们不希望getCount()
方法被其子类覆盖,这样可以保证getCount()
方法必然可以得到当前的Post数量而不被篡改,此时我们将Post
修改为:
public class Post { private String title; private String content; private static int count = 0; public final static String DEAFUTL_TITLE = "这是一篇天码营的博客"; public Post(){ count++; } public static final int getCount(){ return count; } ...}
使用final
修饰方法除了不想让子类覆盖之外,还有一个原因就是高效,Java编译器在遇到final
关键字修饰的方法时会使用内联机制,省去函数调用的开销,大大提高执行效率。
由final
修饰的类是不能类是不能继承的,因此,如果设计类的时候不想让该类被继承,就在该类的前面加上final
关键字。
public final class Post { private int id; private String title; private String content; private static int count = 0; public final static String DEAFUTL_TITLE = "这是一篇天码营的博客"; public Post(){ count++; } public static final int getCount(){ return count; } ...}
例如如果我们将Post
声明为final
,其他子类将不能再继承Post
类。
更多文章请访问