我试图理解很多次,但我无法理解这一点。
封装是使类中的字段成为私有并通过公共方法提供对字段的访问的技术。如果一个字段被声明为私有,则类外的任何人都无法访问它,从而隐藏了类内的字段。
我们如何通过 setter 方法更改字段的值?我们如何防止直接访问这些字段?封装的真正用途是什么?
我试图理解很多次,但我无法理解这一点。
封装是使类中的字段成为私有并通过公共方法提供对字段的访问的技术。如果一个字段被声明为私有,则类外的任何人都无法访问它,从而隐藏了类内的字段。
我们如何通过 setter 方法更改字段的值?我们如何防止直接访问这些字段?封装的真正用途是什么?
假设你有一个age
财产。
用户可以输入 的值-10
,虽然它是一个有效数字,但它是一个无效的年龄。setter 方法可能具有允许您捕获此类事物的逻辑。
另一种情况是拥有该age
字段,但将其隐藏。你也可以有一个出生日期字段,在它的设置器中你会有这样的东西:
...
private int age
private Date dob
...
public void setDateOfBirth(Date dob)
{
this.dob = dob;
age = ... //some logic to calculate the age from the Date of Birth.
}
我也和你一样困惑了很长时间,直到我读了《面向对象编程语言中的封装和继承》这本书和一个解释封装重要性的网站。我实际上是从网站上被引导到这本书的。
人们总是说封装是“隐藏信息”,因此,也许将封装重点放在安全上作为主要用途。是的,您实际上是在隐藏信息,但这不应该是定义,因为它可能会使人们感到困惑。
封装只是“通过定义严格的外部接口来最小化单独编写的模块之间的相互依赖性”(引自书中)。也就是说,当我构建一个模块时,我希望我的客户和我之间就他们如何访问我的模块签订严格的合同。原因是,我可以在不影响我的客户、生活、应用程序或他们使用我的模块的任何东西的情况下改进内部工作。因为他们的“模块”并不完全依赖于我模块的内部工作,而是依赖于“外部接口”,所以我向他们提供了。
所以,如果我不为我的客户提供一个设置器并让他们直接访问一个变量,并且我意识到我需要在我的客户使用它之前对变量设置一些限制,我改变它,可能是我,以巨额费用改变我客户的生活,或我的客户的应用程序。但是,如果我通过创建“严格的外部接口”(即设置器)来提供“严格的合同”,那么我可以轻松地改变我的内部运作方式,而我的客户只需很少或没有任何费用。
在 setter 情况下(使用封装),如果发生这种情况,当您设置变量时,我返回一条消息,通知您它已被分配,现在我可以通过我的“接口”发送一条消息,通知我的客户新的我的模块必须与之交互的方式,即“您不能分配负数”,即如果我的客户尝试分配负数。但是,如果我不使用封装,而是让我的客户直接访问变量并且我进行更改,则可能会导致系统崩溃。因为如果我实施的限制是,您无法保存底片并且我的客户始终能够存储底片,那么我的客户将拥有一个崩溃的系统(如果那个“崩溃的系统”是一个银行系统,想象一下可能发生)。
因此,封装更多是关于减少模块之间的依赖关系,并且可以“安静地”进行改进,而与它交互的其他模块几乎没有或没有成本,而不是安全性。因为交互的模块依赖于“严格的外部接口或严格的契约”。
我希望这能正确解释。如果没有,您可以转到下面的链接并自己阅读。
封装的真正用途还在于您可以对设置值的方式进行额外的检查/处理。
您并没有完全阻止对字段的访问——您正在控制其他人如何访问某些字段。例如,您可以为您的 setter 方法添加验证,或者您还可以在调用字段的 setter 方法时更新一些其他依赖字段。
您可以阻止对该字段的写入或读取访问(例如,仅通过分别提供一个 getter 或 setter)——但使用属性封装可以让您做的不仅仅是这些。
任何我如何能够通过 setter 方法更改字段的值。
仅当 setter 方法允许您这样做时。
我们如何防止访问字段?
setter 和 getter 可以控制是否以及如何访问这些字段。
setter 可以检查该值是否有效。它可能会询问 SecurityManager 是否应该允许您这样做。它可以在数据类型之间进行转换。等等。
如果您有私有字段,则无法在类外部访问它们,这意味着基本上这些字段对外界不存在,是的,您可以通过 setter 方法更改它们的值,但是使用 setter 方法,您可以更灵活/控制谁可以更改字段以及可以将它们更改为什么值...基本上通过封装,您可以限制更改字段的方式和人员。例如,您有:私人双薪,您的 setter 方法可以限制只有 hr 员工可以更改薪水字段,它可以写成:
void setSalary(Person p,double newSalary)
{
//only HR objects have access to change salary field.
If(p instanceof HR && newSalary>=0)
//change salary.
else
S.o.p("access denied");
}
想象一下,如果工资是公开的并且可以直接访问,任何人都可以随时更改它,这基本上就是封装的意义
封装背后的主要思想是数据隐藏。我们在面向对象编程中使用封装有几个原因。我们封装的一些已确定原因如下(封装的真正用途)。
更好的可维护性:当所有的属性都是私有的并且被封装时,我们很容易通过改变方法来维护程序。
使调试变得容易:这符合上述观点。我们知道对象只能通过方法来操作。因此,这使得调试和捕获错误变得容易。
拥有受控环境:让用户通过对象以受控方式使用给定对象。
隐藏复杂性:隐藏与用户无关的复杂性。有时,某些属性和方法仅供内部使用,用户不必知道这些。这使得用户使用对象变得简单。
所以,要回答这个问题,“当我能够使用 setter 方法更改属性值时,封装有什么用? ”,上面给出了我们使用封装的一些主要原因。为了理解为什么,getter 和 setter 是有用的,下面给出了从这篇文章中获得的一些要点。
您可以限制可以存储在字段中的值(即性别必须是 F 或 M)。
您可以在修改字段时执行操作(触发事件、验证等)。
您可以通过同步方法来提供线程安全。
您可以切换到新的数据表示(即计算字段,不同的数据类型)
假设您使用以下 setter / getter 创建了一个自定义 Date 类:
getDay()
getMonth()
getYear()
setDay()
setMonth()
setYear()
在内部,您可以使用以下方式存储日期:
private int day;
private int month;
private int year;
或者您可以使用 java.lang.Date-object 存储日期:
private Date date;
封装不会暴露你的类是如何在内部工作的。它使您可以更自由地更改班级的工作方式。它使您可以选择控制对班级的访问。您可以检查用户输入的内容是否有效(您不希望用户输入值为 32 的日期)。
它的目的只是保护任何容易改变的东西。你在网上有很多例子,所以我给你一些它的优点:
immutable class
Java编写通过方法访问字段会有所不同,因为它使它成为 OOP。例如,您可以扩展您的课程并更改您无法直接访问的行为。如果你有 getter / setter,你可以为你的类做一个代理,做一些 AOP 或做一个 1.4 的动态代理。您可以在课堂上进行模拟并进行单元测试...
Encapsultaion 用于隐藏成员变量,通过将成员设为私有并通过 getter 和 setter 方法访问该成员变量。
例子
类封装{
private int value ;
Encapsulation() {
System.out.println("constructor calling ");
}
void setValue(int value){
this.value = value;
}
int getValue() {
return value;
}
} 类封装主要 {
public static void main(String args[]) {
Encapsulation obj = new Encapsulation();
obj.setValue(4);
//System.out.print("value is "+obj.value);
//obj.value = 55;
//System.out.print("obj changing the value"+obj.value);
System.out.print("calling the value through the getterMethod"+obj.getValue());
}
}
您无法访问类外的私有值。
好吧,封装不仅仅是隐藏数据。这一切都是为了控制存储在字段中的内容。使用封装,我们可以根据需要将字段设置为只读或只写。用户也不知道数据是如何存储在字段中的。我们可以在 setter 方法中使用一些特殊的加密,并将其存储在字段中。例如,人是一个对象。我们只要求用户读取人的姓名字段,但不能修改。然后我们只在 name 字段上定义 get 方法。这就是封装的用处。
如果你有类,它的所有属性都是私有的——这意味着它们不能从类外部访问——与类属性交互的唯一方法是通过它的公共方法。
您正在通过让公众访问这些方法(设置器)来更改这些值。
使用封装可以制作类的字段read-only
或write-only.
而不是让每个人都直接访问变量:
public Object object;
最好使用 SET 和 GET 方法,或者例如只使用 GET 方法(有时您不希望没有人为该变量设置其他值)。
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
通过使用封装,您可以将您的类与外部世界(其他类)分开,并且外部世界可以通过访问修饰符访问和修改您的类实例变量,这提供了几个好处:
- 你可以在你的 getter/setter 方法中做一些记录。
-您可以验证/规范化(例如修剪空格,删除特殊字符,...)您在 setter 方法中的输入。
而且你也可以对外界隐藏你的实现,例如你的类中有一个像数组列表这样的集合,你可以像这样编写你的getter方法
public List<t> get collection(){
return new ArrayList<t>(this.arrayList);
}
因此,在这种情况下,将来如果您决定将集合的实现从数组列表更改为链表之类的其他东西,您可以自由地这样做,因为外界对您的实现一无所知。