java封装继承_Java编程基础:封装与继承

由David发表在天码营

封装

接下来我们进一步来学习一些面向对象编程的技术。先来了解封装的概念。

封装是一种隐藏信息的技术,是将一个系统中的结构和行为通过类来划分的过程。即通过定义一组类,将特定的数据组合到某一个类中,形成一个整体,将该隐藏的数据进行保护,只对外暴露这些数据的访问的方法。

封装代码有两个好处:代码使用者无需考虑实现细节就能直接使用它,同时不用担心不可预料的副作用,别人不能随便修改内部结构

在外部接口保持不变的情况下,自己可以修改内部实现

封装甚至被一些面向对象的开发人员视为第一原则。

如何隐藏类的具体实现和数据?其实之前的代码中我们已经用到了,现在我们来系统地了解一下。

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,这样做的目的是阻止它的内容被修改。这意味着在声明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关键字

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修饰的类是不能类是不能继承的,因此,如果设计类的时候不想让该类被继承,就在该类的前面加上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类。

更多文章请访问天码营网站

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值