但是,请考虑以下两个例子:
1) 第一个例子:
Class Employee
{
public int age;
}
第二个例子:
Class Employee
{
private int age;
public int getAge(){return age;}
}
问题: 在上面指定的两个示例中,都没有数据隐藏,因为年龄要么被他人修改,要么被他人查看。数据隐藏在哪里?封装如何帮助上述示例?
Class Employee
{
public int age;
}
Class Employee
{
private int age;
public int getAge(){return age;}
}
存在数据隐藏。第二个例子隐藏了值的存储方式。此外,第二个例子使得类外代码只能读取值而无法进行修改。通过使用私有字段,并通过方法或属性来公开它们,您可以立即获得一些优点,其中一些包括:
请注意,封装不仅仅是关于访问控制。封装的主要用途是从调用代码中隐藏实现细节。当调用代码想要检索一个值时,它不应依赖于该值的来源。在内部,类可以在字段中存储该值或从某些外部资源(如文件或数据库)中检索该值。也许这个值根本没有被存储,而是动态计算的。对于调用代码而言,这些都不应该有所影响。
当您通过方法或属性公开值时,您就屏蔽了调用代码对这些细节的感知。这也为您提供了更改实现的可能性,而不会影响调用代码。
封装将“某物做什么”和“它如何实现”这两个概念分开。
封装是面向对象编程中非常重要的概念。它隐藏了应用程序中其他模块的数据。在您的示例2中,正如您所看到的,您只能获取年龄,并且只能使用getter方法设置私有成员的值,但是不能在类外部设置该值。这就是封装的优点。
考虑这个变体:
class Employee
{
private int _age;
public string Age
{
get { return _age.ToString(); }
}
}
封装
就是简单的数据隐藏
。private
变量。现在,即使另一个类创建一个对象并尝试像以前那样访问工资,它也不起作用。因为它是一个只能被Employee类访问的私有字段。现在我们已经隐藏了我们的数据。woked_hours, OT and leaves
(Emplyee类有三个变量),我们希望这个人输入这些详情,但我们不希望他知道工资计算
是如何完成的。它是根据Employee类内部的数学方程完成的。public int getSalary()
{
return salary;
}
public void setSalary(int newSalary)
{
salary=newSalary;
}
只读
权限。但是对于薪资,我们没有给他写入
权限。
所以,现在封装的作用就体现出来了。
同样的方式,他被允许设置
这些字段工作时间、加班和请假
。对于这些字段,我们只给了他写入权限。因此,我们故意没有编写getter方法
。如果你编写了getter
和setter
,你就允许他进行读/写
访问。
无论是设置还是获取,字段都是私有的,数据已经隐藏。但是通过getter
和setter
仍然可以访问。
假设我像这样使用你的代码:
Employee employee = new Employee();
int age = employee.getAge();
// Do something with age
在这里,除非我查看您的源代码,否则我完全不知道Employee
如何计算其年龄。 我所知道的是,getAge()
可能看起来像这样:
public int getAge() {
return new java.util.Random().nextInt( 100 );
}
或许还有更复杂的情况,我不确定。
所以在你的第二个例子中,getAge()
只返回一个私有成员变量的事实对我来说是完全隐藏的。
封装不仅仅是数据隐藏。封装允许您将与实体相关的所有数据和功能与该实体一起保留。这些数据的可见性级别是一个相关但不完全相同的概念。例如,Employee
对象携带所有员工数据和功能(方法)。请注意,这不是强制执行,而是由面向对象语言启用。
如果您出生在面向对象时代,这可能听起来很自然,但这是从函数式编程到支持此范例的语言中进行的重大飞跃,无论是在概念(设计)上还是在语言上。
引用维基百科article:
在编程语言中,封装用于指称两个相关但不同的概念之一,有时也指它们的组合:
- 一种限制访问对象某些组件的语言机制。
- 一种语言结构,便于将数据与操作该数据的方法(或其他函数)捆绑在一起。