OOP: 限制对象属性的访问

原创 2004年07月06日 12:20:00

Friday, October 19 2001 7:15 PM

  study;sz=1x1;ord=249685187?

在"OOP入门:深入研究继承和多态性"这篇文章中,我们继续讨论了继承和多态性的好处.我们还学习了其它的东西:

  • 虽然Java只支持从一个父类继承,但它使用接口的方式支持多重继承.
  • 接口实现了多态,使得我们能够给与对象不同特性以满足不同的需要.
  • 你可以使用多态机制让完成相似功能的不同的方法拥有相同的名字但是拥有不同的参数列表.
  • 动态/运行时的绑定机制允许一个对象在运行时被强制转化成你所需要的对象类型,前提是这个对象实现了必需的接口或者括展了特定的父类.


下面我们将讨论通过限制对对象属性和方法的访问来强制实现对多重接口实现和父类拓展的正确使用的目的和实用性.


黑箱方法:封装

一个基本的面向对象的概念就是封装--将表示一个对象状态的数据与其它对象隔离开来.这一点是通过一个通常叫做作用域的概念来实现的.作用域指的是编程语言的一种能力,这种能力被用来实现一些限制对类或者结构体成员变量的访问的规则.大多数面向对象的语言支持作用域机制,这些机制通常是通过诸如public, protected, 和 private之类的特殊关键字来实现的.

Java提供了四种不同的作用范围:public, package, protected, 和 private.任何类,方法或者成员变量都能通过使用public, protected, 和 private关键字来显式的加以保护.任何类,方法,或者成员变量如果没有使用上面的关键字都将被隐式的给与package的作用范围.所有这些就构成了Java中命名空间的概念.


命名空间和软件包
一个命名空间可以被看成是在一个给定的上下文中一组相关的名字或是标识符.命名空间避免了拥有相同名字或标识符的实体存在于同一个上下文里.这里隐含的意思是只要实体是存在于不同的命名空间中,那么拥有相同名字或者标识符的实体就能够呆在一块儿.Java使用软件包的概念来实现命名空间和作用范围控制.

软件包是一个在统一的名字下的类和接口的集合.每一个类或者接口都必须存在于用package关键字构成的软件包申明语句定义的命名空间中.例如,下面的申明语句:

package com.mycompany.apps.HelloWorld;

它申明了一个存在于com.mycompany.apps软件包中的名叫HelloWorld的类或者接口.软件包申明总是放在包含了类或者接口定义的文件的顶部.

在java开发界,目前对软件包的命名有一个建议,就是使用公司或组织的域名(以相反的顺序),作为你的软件包的第一部分.因为域名是全球唯一的,所以使用你的域名来命名你的软件包也能使你软件包的名字全球唯一.

如果一个Java类或者接口没有包含一个软件包申明,那么它就属于"unamed package,"也就是没有名字的软件包.无名的软件包应该只用来测试程序或是代码原型等等.

请尽量使用封装机制
在任何程序风格中,尤其是在面向对象的编程中,将暴露的编程界面背后的实现细节隐藏起来是非常关键的.这使得低层的实现方法能够在不影响编程界面现有的客户端的前提下改变,而且能使对象完全自主的管理它们自己的状态.

分离界面和实现方法的第一步就是隐藏类的内部数据.要使一个成员变量或是方法对Java中所有潜在的客户不可见,可以将用private关键字将它声明为私有成员变量,如下所示:

private int customerCount;

要使一个成员变量或是方法除了其本身所属类的子类以外对Java中所有潜在的客户不可见可以使用protected关键字将它声明成保护类型的,如下所示:

protected int customerCount;

要使一个成员变量或是方法除了其本身所属的类以外对Java中所有潜在的客户不可见不使用任何关键字来声明它,如下所示:

int customerCount;

要将一个成员变量或是方法暴露给其所属类的所有客户,可以用public关键字将它声明为公共的成员变量,如下所示:

public int customerCount;


访问成员变量
不论一个对象的数据隐藏得多么好,客户仍然需要访问一些隐藏的数据.这是通过调用函数或方法来实现的.在Java中,使用特殊的被称做属性访问器的方法来访问隐藏的数据是可能的.在Java中属性访问器和通常的函数之间并没有本质的区别.将一个通常的方法转变成一个属性访问器唯一要做的事情就是参照一个命名规则来添加方法.

读数据的访问器的命名规则就是将方法命名为和数据域一样的名字,将首字母大写,然后在方法名字的前面添加get或是is."写"数据访问器的命名规则就是将方法命名为和数据域一样的名字,将首字母大写,然后在方法名字的前面添加set.下面的例子演示了写和读数据的数据访问器方法.

这是一个"读"数据访问器方法:

public int getCustomerCount()
{
    return(customerCount);
}


这是另一个"读"数据访问器方法

public int isCustomerActive()
{
    return(customerActive);
}

这是一个"写"数据访问器方法:

public void setCustomerCount(int newValue)
{
    customerCount = newValue;
}

使用访问器方法允许其它对象访问一个对象的隐藏数据而不直接涉及数据域.这就允许拥有隐含数据的对象在改变成员变量以前做正确性检查并控制成员变量是否应该被设置成新的值.

现在让我们修改例子程序来使用这些概念,如所示.

public class HelloWorld
{
    public static void main(String[] args)
    {
        Dog animal1 = new Dog();
        Cat animal2 = new Cat();
        Duck animal3 = new Duck();
        animal1.setMood(Animal.COMFORTED);
        System.out.println("A comforted dog says " +animal1.getHello());
        animal1.setMood(Animal.SCARED);
        System.out.println("A scared dog says " +animal1.getHello());
        System.out.println("Is a dog carnivorous? " +animal1.isCarnivorous());
        System.out.println("Is a dog a mammal? " +animal1.isCarnivorous());
        animal2.setMood(Animal.COMFORTED);
        System.out.println("A comforted cat says " +animal2.getHello());
        animal2.setMood(Animal.SCARED);
        System.out.println("A scared cat says " +animal2.getHello());
        System.out.println("Is a cat carnivorous? " +animal2.isCarnivorous());
        System.out.println("Is a cat a mammal? " +animal2.isCarnivorous());
        animal3.setMood(Animal.COMFORTED);
        System.out.println("A comforted duck says " +animal3.getHello());
        animal3.setMood(Animal.SCARED);
        System.out.println("A scared duck says " +animal3.getHello());
        System.out.println("Is a duck carnivorous? " +animal3.isCarnivorous());
        System.out.println("Is a duck a mammal? " +animal3.isCarnivorous());
    }
}

abstract class Animal
{
    // The two following fields are declared as public because they need to be
    // accessed by all clients
    public static final int SCARED = 1;
    public static final int COMFORTED = 2;
    // The following fields are declared as protected because they need to be
    // accessed only by descendant classes
    protected boolean mammal = false;
    protected boolean carnivorous = false;
    protected int mood = COMFORTED ;
    public boolean isMammal()
    {
        return(mammal);
    }

    public boolean isCarnivorous()
    {
        return(carnivorous);
    }

    abstract public String getHello();

    public void setMood(int newValue)
    {
        mood = newValue;
    }

    public int getMood()
    {
        return(mood);
    }
}


interface LandAnimal
{
    public int getNumberOfLegs();
    public boolean getTailFlag();
}

interface WaterAnimal
{
    public boolean getGillFlag();
    public boolean getLaysEggs();
}

class Dog extends Animal implements LandAnimal
{
    // The following fields are declared private because they do not need to be
    // access by any other classes besides this one.
    private int numberOfLegs = 4;
    private boolean tailFlag = true;
    // Default constructor to make sure our properties are set correctly
    public Dog()
    {
        mammal = true;
        carnivorous = true;
    }
    // methods that override superclass's implementation
    public String getHello()
    {
        switch (mood) {
            case SCARED:
                return("Growl");
            case COMFORTED:
                return("Bark");
        }
        return("Bark");
    }

    // Implementation of LandAnimal interface

    public int getNumberOfLegs()
    {
        return(numberOfLegs);
    }

    public boolean getTailFlag()
    {
        return(tailFlag);
    }
}

class Cat extends Animal implements LandAnimal
{
    // The following fields are declared private because they do not need to be
    // access by any other classes besides this one.
    private int numberOfLegs = 4;
    private boolean tailFlag = true;
    // Default constructor to make sure our properties are set correctly
    public Cat()
    {
        mammal = true;
        carnivorous = true;
    }
    // methods that override superclass's implementation
    public String getHello()
    {
        switch (mood) {
            case SCARED:
                return("Hiss");
            case COMFORTED:
                return("Purr");
        }
        return("Meow");
    }

    // Implementation of LandAnimal interface
    public int getNumberOfLegs()
    {
        return(numberOfLegs);
    }

    public boolean getTailFlag()
    {
        return(tailFlag);
    }
}

class Duck extends Animal implements LandAnimal, WaterAnimal
{
    // The following fields are declared private because they do not need to be
    // access by any other classes besides this one.
    private boolean gillFlag = false;
    private boolean laysEggs = true;
    private int numberOfLegs = 2;
    private boolean tailFlag = false;
    // Default constructor to make sure our properties are set correctly
    public Duck()
    {
        mammal = false;
        carnivorous = false;
    }
    // methods that override superclass's implementation
    public String getHello()
    {
        switch (mood) {
            case SCARED:
                return("Quack, Quack, Quack");
            case COMFORTED:
                return("Quack");
        }
        return("Quack");
    }

    // Implementation of WaterAnimal interface

    public boolean getGillFlag()
    {
        return(gillFlag);
    }

    public boolean getLaysEggs()
    {
        return(laysEggs);
    }

    // Implementation of LandAnimal interface

   
public int getNumberOfLegs()
    {
        return(numberOfLegs);
    }

    public boolean getTailFlag()
    {
        return(tailFlag);
    }
}



这个程序的输出结果应该如下:
A comforted dog says Bark
A scared dog says Growl
Is a dog carnivorous? true
Is a dog a mammal? true
A comforted cat says Purr
A scared cat says Hiss
Is a cat carnivorous? true
Is a cat a mammal? true
A comforted duck says Quack
A scared duck says Quack, Quack, Quack
Is a duck carnivorous? false
Is a duck a mammal? false

总结
使用数据隐藏/封装机制是一个控制对象数据和状态的强有力的方法.它允许一个对象决定是否要改变一个成员变量和如何改变一个成员变量.这使得一个对象的实现细节能够改变而暴露的对象界面得以维持.在我们的下一篇文章中,我们将进一步探讨Java提供的变量作用域规则并开始学习Java对象是如何构造和初始化的.

OOP: 限制对象属性的访问

on error resume next ShockMode = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.5")))在"OOP入门:...
  • PerseusYan
  • PerseusYan
  • 2005年04月11日 23:26
  • 860

JS 对象属性访问的2种方式和用途

问题起源: 在学习JS的过程中遇到一道题目“判断一个字符串中出现次数最多的字符,统计这个次数”,在网上查到一个用对象的方法解题,看完表示一脸懵逼。对代码中o[char]不能理解。 代码如下: ...
  • shuren1991
  • shuren1991
  • 2017年03月28日 17:15
  • 2387

OOP-----类与对象对象

很自然的,我们看到一个东西能很快的反映出它是什么,它可以做什么,这是我们所积累的认识,这种思维方式似乎是与生俱来的,那我们有没有想过它是怎么实现的呢?用个很简单的词语————类比。我们在脑袋里装着很多...
  • qq_32080545
  • qq_32080545
  • 2016年06月19日 13:53
  • 335

js访问对象属性两种方式

在javascript中我们通常使用.来访问对象的属性,那么也可以使用[ ]来访问对象属性。 object.propertyName; object['propertyName']; [ ]访问...
  • q646926099
  • q646926099
  • 2017年02月08日 17:13
  • 800

C#OOP基础之类,字段,属性和方法。

菜鸟开始接触OOP一脸懵逼。感觉确实需要记一下。一方面是检验自己是否记住已经学习的东西,另一方面是在写的时候理解并产生新的感悟。...
  • AlexiFeng
  • AlexiFeng
  • 2016年01月18日 12:47
  • 162

java-类与对象(访问对象的属性和行为)、

对象的定义方法: 1,对象声明:类名 对象名; 2,对象创建 对象名 =  new 类名(); new作用:分配内存空间。 也可以合写为:类名 对象名 = new 类名(); package asa;...
  • qq_32823673
  • qq_32823673
  • 2017年05月22日 17:51
  • 1341

C++ 如何突破private成员的访问限制

c++中类的private成员对外是不可见的,以下方法可以突破private成员的访问权限。 // accessPrivateMember.h class A { private:   ...
  • u010318270
  • u010318270
  • 2017年11月24日 09:46
  • 113

php面向对象(OOP)—调用成员和方法

class Person { //下面是人的成员属性 var $name; //人的名子 var $sex; //人的性别 var $age; //人的年龄 //下面是人的成员方法 functio...
  • guoshaoqing001
  • guoshaoqing001
  • 2015年06月03日 14:46
  • 1922

OOP的四个基本机制

 (1)、抽象   含义:对具体问题(对象)进行概括,抽出一类对象的公共属性并加以描述的过程。   如何实现抽象:对问题进行分析,提取其属性和行为。 人类 属性抽象--姓名、性别、年龄、眼睛等。 方法...
  • ziqin1984
  • ziqin1984
  • 2007年10月17日 17:41
  • 378

LabVIEW OOP基础

LabVIEW OOP基础本文介绍LabVIEW OOP编程的基础知识。 面向过程编程与面向对象编程的区别 属性和方法 类和对象 封装 继承 动态分配 丰富的快捷键 面向过程编程和面向对象编程的区别 ...
  • shmily0923
  • shmily0923
  • 2016年06月23日 14:36
  • 1616
收藏助手
不良信息举报
您举报文章:OOP: 限制对象属性的访问
举报原因:
原因补充:

(最多只允许输入30个字)