Java基础之(十五)隐藏和封装

说明
在前面的程序中经常出现通过某个对象直接访问其Field(成员变量)的情形,这可能引起一些潜在的问题。比如我们给某个Person类中的age成员变量赋值为1000,这在语法上来说是合法的,但是违背了现实。因此,Java程序推荐将类和对象的Field进行封装。

理解封装

封装是面向对象三大特征之一(其他两个是继承多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。

对一个类或对象实现良好的封装,可以实现以下目的:

  • 隐藏类的实现细节。
  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入逻辑控制,限制对Field的不合理访问。
  • 可进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码可维护性。

为了实现良好的封装,需要从两个方面考虑:

  • 将对象的Field和实现细节隐藏起来,不允许外部直接访问
  • 把方法暴露出来,让方法来控制对这些Field来进行安全的访问和操作

这两个方面都需要访问控制符来实现。

访问控制符

说明
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限。

  1. 默认的,也称为 default,在同一包内可见,不使用任何修饰符。
  2. 私有的,以 private 修饰符指定,在同一类内可见。
  3. 共有的,以 public 修饰符指定,对所有类可见。
  4. 受保护的,以 protected 修饰符指定,对同一包内的类和所有子类可见。

默认访问修饰符-不使用任何关键字

如果你声明一个字段,方法,构造函数,但你不写明访问修饰符,这意味着这将使用默认(default)的访问修饰符。
默认(default)的访问修饰符的访问范围是在包里面,即在同一个包,你可以访问成员的访问修饰符是默认(default),而且,不能访问包之外的东西,即使它们是一个子类。

如下例所示,变量和方法的声明可以不使用任何修饰符。

String version = "1.5.1";
boolean processOrder() {
   return true;
}

私有访问修饰符-private

私有访问修饰符是最严格的访问级别,所以被声明为private的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为private。

声明为私有访问类型的变量只能通过类中公共的getter方法被外部类访问。

Private访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。

下面的类使用了私有访问修饰符:

public class Logger {
   private String format;
   public String getFormat() {
      return this.format;
   }
   public void setFormat(String format) {
      this.format = format;
   }
}

公有访问修饰符-public

被声明为public的类、方法、构造方法和接口能够被任何其他类访问。

如果几个相互访问的public类分布在不同的包中,则需要导入相应public类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

以下函数使用了公有访问控制:

public static void main(String[] args) {
   // ...
}

Java程序的main() 方法必须设置成公有的,否则,Java解释器将不能运行该类。

受保护的访问修饰符-protected

被声明为protected的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。

Protected访问修饰符不能修饰类和接口,方法和成员变量能够声明为protected,但是接口的成员变量和成员方法不能声明为protected。

子类能访问Protected修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。

下面的父类使用了protected访问修饰符,子类重载了父类的openSpeaker()方法。

class AudioPlayer {
   protected boolean openSpeaker(Speaker sp) {
      // 实现细节
   }
}

class StreamingAudioPlayer {
   boolean openSpeaker(Speaker sp) {
      // 实现细节
   }
}

访问控制符的几条基本原则

  • 类里的绝大部分Field都应当使用private修饰,只有一些static修饰的、类似全局变量的Field,才可能考虑public修饰。除此之外,有些方法只是用于辅助实现该类的其他方法,这些方法被称之为工具方法,工具方法也应该用private修饰。

  • 如果某个类主要用于其他类的父类,该父类里包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应当使用protected修饰。

  • 希望暴露出来给其他类自由调用的方法应该使用public修饰,因此大部分外部类都是用publci修饰。

访问控制和继承

请注意以下方法继承的规则:

  • 父类中声明为public的方法在子类中也必须为public。

  • 父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。

  • 父类中声明为private的方法,不能够被继承。

实现封装

掌握了访问修饰符的用法之后,下面通过合理的访问控制符来定义一个Person类,这个Person类实现了良好的封装。

public class Person
{
    private String name;
    private int age;

    public Person()
    {
    }

    public Person(String name , int age)
    {
        this.name = name;
        this.age = age;
    }

    public void setName(String name)
    {
        //执行合理性校验,要求用户名必须在2~6位之间
        if (name.length() > 6 || name.length() < 2)
        {
            System.out.println("您设置的人名不符合要求");
            return;
        }
        else
        {
            this.name = name;
        }
    }
    public String getName()
    {
         return this.name;
    }

    public void setAge(int age)
    {
        //执行合理性校验,要求用户年龄必须在0~100之间
        if (age > 100 || age < 0)
        {
            System.out.println("您设置的年龄不合法");
            return;
        }
        else
        {
            this.age = age;
        }
    }
    public int getAge()
    {
         return this.age;
    }
}

定义了Person类之后,该类的age和name两个Field只有在Person类内才可以操作和访问,在Person类之外只能通过各自对应的setter和getter方法来操作和访问它们。

通俗地讲,get方法称为读取器,而set方法称为设置器。
get方法的语法格式为:

public returnType getPropertyName()

如果返回值类型是boolean型,习惯上如下定义get方法:

public boolean isPropertyName()

set方法的语法格式为:

public void setPropertyName(dateType propertyValue)

下面程序在main方法中创建一个Person对象,并尝试操作和访问该对象的age和name两个Field。

public class TestPerson
{
    public static void main(String[] args) 
    {
        Person p = new Person();
        //因为age属性已被隐藏,所以下面语句将出现编译错误。
        //p.age = 1000;
        //下面语句编译不会出现错误,但运行时将提示出入的age属性不合法
        //程序不会修改p的age属性
        p.setAge(1000);
        //访问p的age属性也必须通过其对应的getter方法
        //因为上面从未成功设置p的age属性,故此处输出0
        System.out.println("未能设置age属性时:" + p.getAge());
        //成功修改p的age属性
        p.setAge(30);
        //因为上面成功设置了p的age属性,故此处输出30
        System.out.println("成功设置age属性后:" + p.getAge());
        //不能直接操作p的name属性,只能通过其对应的setter方法
        //因为"李刚"字符串长度满足2~6,所以可以成功设置
        p.setName("李刚");
        System.out.println("成功设置name属性后:" + p.getName());
    }
}

正如上面程序中的注释所言,PersonTest的main方法不可直接修改Person对象的age和name,只能通过各自对应的setter方法来操作这两个Field的值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值