Java封装概念不清楚

25

这是一个基础问题,但我仍然不理解封装的概念。我不明白如何从其他类更改类的属性,因为当我们尝试设置类的公共实例值时,我们必须创建该类的对象,然后设置该值。而每个对象引用不同的内存,所以即使我们更改了实例值,这也不会影响任何其他对象。

即使我尝试使用静态公共实例值进行更改,我也无法更改类的属性值。

以下示例说明

// Employee class
public class Employee {
    public static int empid;
    public static String empname;

    public static void main(String[] args) {
        System.out.println("print employe details:"+empid+" "+empname);
    }

    // EmployeeTest  class
    public class EmployeeTest {

        public static void main(String[] args) {
            Employee e = new Employee();
            e.empid=20;
            e.empname="jerry";
            Employee.empid=10;
            Employee.empname="tom";
        }

    }
}

每次运行Employee类时,我都得到相同的值。

print employe details:0 null

尽管我没有遵循封装的概念,也无法更改员工类的公共实例值。请帮助我理解我错在哪里的概念。

12个回答

40

是的,有时这可能有点令人困惑。让我们一步一步来:首先,你需要理解

  • 什么是封装以及它为什么被使用?

封装是面向对象编程四个基本概念之一。封装是将类中的字段设为私有的,并通过公共方法提供对字段的访问的技术。如果一个字段被声明为私有的,那么在类外部就无法访问该字段,从而隐藏了类内部的字段。因此,封装也被称为数据隐藏。

封装可以描述为一种保护屏障,防止其他代码随意访问定义在类外部的代码和数据。对数据和代码的访问由接口严格控制。

封装的主要好处是能够修改我们实现的代码而不会破坏使用我们代码的其他人的代码。通过此特性,封装为我们的代码提供可维护性、灵活性和可扩展性。

举个小例子:

public class EncapTest{

   private String name;
   private String idNum;
   private int age;

   public int getAge(){
      return age;
   }

   public String getName(){
      return name;
   }

   public String getIdNum(){
      return idNum;
   }

   public void setAge( int newAge){
      age = newAge;
   }

   public void setName(String newName){
      name = newName;
   }

   public void setIdNum( String newId){
      idNum = newId;
   }
}

上述方法称为访问器(也称为getter和setter)。现在你可能会问,

  • 为什么应该使用访问器..? 实际上有许多很好的理由考虑使用访问器而不是直接暴露类的字段。Getter和Setter使API更加稳定。

例如,考虑一个类中公共字段被其他类访问。稍后,您想要添加任何额外的逻辑来获取和设置变量。这将影响使用API的现有客户端。因此,对这个公共字段的任何更改都需要更改引用它的每个类。相反,通过访问器方法,可以轻松地添加一些逻辑,例如缓存一些数据,稍后再进行惰性初始化。此外,如果新值与先前值不同,可以触发属性更改事件。所有这些对使用访问器方法获取值的类来说都是无缝的。

有很多教程和解释如何以及它们是什么。Google一下。

至于您当前的问题:

  1. 您有两个不同的类,每个类都有一个主要功能。那是错的。他们将有不同的属性。
  2. @Subhrajyoti Majumder建议的代码更改是正确的。检查答案以解决问题。

同时,阅读以下内容

更好地了解这些概念。希望对你有所帮助。 :)


谢谢你的回复,非常有帮助。能否请您举个例子,说明没有实现封装会产生哪些影响? - user2693404
解释已经由@Zeeshan给出。他的解释非常清晰。 :) - user2339071

6

看起来你正在分别运行两个不同的类,并假设在运行EmployeeTest时对属性所做的更改将反映在Employee的运行中。请注意,更改将反映在同一JRE实例中。如果我误解了你的问题,请原谅。

编辑:根据用户输入。以下是您如何访问和更新静态成员值的代码:

class Employee {
    public static int empid;
    public static String empname;

    public static void main(String[] args) {
        System.out.println("print employe details:" + empid + " " + empname);
    }
}

// EmployeeTest class
public class EmployeeTest {

    public static void main(String[] args) {
        Employee e = new Employee();
        e.empid = 20;
        e.empname = "jerry";
        Employee.empid = 10;
        Employee.empname = "tom";
        Employee.main(null);
    }

}

感谢您的回复。您能否举个例子,说明如果我们不实现封装概念,实例值如何被更改。在我上面的Employee类示例中,如果我们不实现封装,如何更改实例值。如果我们不实现封装,它将如何影响Employee类,在这两种情况下,它的工作方式都是相同的,无论我们是否实现封装。 - user2693404
@JunedAhsan,您能否告诉我在您上面的代码中Employee.main(null);是做什么的? - Amit Upadhyay

4

public static字段与类关联而不是对象,它违反了对象的封装规则。

Employee类有两个封装字段empid和empname

public class Employee {
    private int empid;
    private String empname;

    public int getEmpid(){
        return this.empid;
    } 
    public void setEmpid(int empid){
        this.empid = empid;
    }
    ...
}

public class EmployeeTest {
      public static void main(String[] args) {
            Employee e = new Employee();
            e.setempId(1);
            Employee e1 = new Employee();
            e1.setempId(2);
      }
}

点击此处,查看有关封装的更好理解文档


2
“static” 如何破坏封装性?成员变量的声明为 “public” 违反了封装性,但通常情况下为什么会 “static” 破坏它呢? - Viktor Seifert
1
static破坏了封装规则,因为可以直接访问它而不需要使用setter或getter。如果你像这样声明一个变量: public static 或者 static,在从另一个类调用它时,你可以写成:object.variable = value; - Josef

2
封装可以被描述为一种保护性屏障,防止代码和数据被定义在类外的其他代码随意访问。通过接口严格控制对数据和代码的访问。
Java中的封装是一种技术,即将类中的字段设为私有,并通过公共方法提供对这些字段的访问。
如果一个字段被声明为私有的,就不能被类外的任何人访问,从而将字段隐藏在类内部。因此,封装也被称为数据隐藏。
现实生活中的例子:汽车和车主。所有汽车的功能都与车主封装在一起,因此没有其他人可以访问它。
以下是此示例的代码。
public class Main {

  public static void main(String[] args) {
    Owner o1=new Car("SONY","Google Maps");
    o1.activate_Sunroof();
    o1.getNavigationSystem();
    o1.getRadioSytem();
 }
}  
//Interface designed for exposing car functionalities that an owner can use.
public interface Owner {
     void getNavigationSystem();
     void getRadioSytem();
     void activate_Sunroof();
}
/*
Car class protects the code and data access from outside world access by implementing Owner interface(i.e, exposing Cars functionalities) and restricting data access via private access modifier.
*/
public class Car implements Owner {
                private String radioSystem;
                private String gps;

                public Car(String radioSystem, String gps) {
                    super();
                    this.radioSystem = radioSystem;
                    this.gps = gps;
                }

                public String getRadioSystem() {
                    return radioSystem;
                }

                public void setRadioSystem(String radioSystem) {
                    this.radioSystem = radioSystem;
                }

                public String getGps() {
                    return gps;
                }

                public void setGps(String gps) {
                    this.gps = gps;
                }

                @Override
                public void getNavigationSystem() {
                    System.out.println("GPS system " + getGps() + " is in use...");
                }

                @Override
                public void getRadioSytem() {
                    System.out.println("Radio system " + getRadioSystem() + " activated");
                }

                @Override
                public void activate_Sunroof() {
                    System.out.println("Sunroof activated");
                }
}

通过提供公共的setter,你不是将实体细节暴露给外部世界了吗?最终会打破封装的整个目的。我们如何使其受到严格控制? - hardik9850

1
当然,更改一个对象不会影响另一个对象。假设你有一个名为“student”的类,你的学校里的所有孩子都是它的对象。如果有一个离开学校,这并不意味着每个其他学生(student类的对象)也应该离开学校。
封装是将类变量设置为私有的概念,以便外部世界无法直接访问你的数据成员。但你可以提供公共方法,让外部世界按照你想要的方式访问你的数据成员。Subhrajyoti Majumder提供了封装的良好编码示例。
(静态成员对于类的所有对象都是相同的。例如:静态计数变量,用于计算student类对象的数量(在学校中的学生人数)。)
编辑如您所请求的内容:
示例:
public class student{
    public String name;
    public student() {}
 }

在你的主函数中,外部世界可以通过以下方式访问你的类属性:

student s = new student();
s.name = "xyz";

假设您不希望外部世界更改对象的名称属性。那么,您应该将名称“name”设置为私有,并提供一个公共方法仅查看名称(get)。
示例:
public class student{
    private String name;
    public student() {}
    public String getName(){
      return this.name;
      }
 }

现在在你的主方法中,你只能获取名称对象,不能像第一个示例中那样将其设置为新值。

student s = new student();
String sname = s.getName();

如果你尝试:

s.name = "newname";

编译器不允许您这样做。因为您没有访问私有成员的权限。

谢谢大家,你们能否给出一个带有封装和不带封装的示例类,并说明两种情况下结果的不同之处。我的意思是我想看到两种实现方式的输出差异。 - user2693404
@user2693404,我已经编辑了答案并提供了示例。现在有帮助了吗?你理解这个概念了吗?还有其他问题吗? - Zeeshan
学生 s = new 学生(); s.name = "xyz"; 这个改变只是针对对象 's',是不会影响到类级别的,也不会对其他对象产生影响。那么封装对我们有什么用处呢?这个 s.name = "xyz"; 只会影响到对象 s,不会影响其他任何对象。 - user2693404
@user2693404请阅读我的回答的第二段。封装并不是关于它是否会影响仅限于“s”对象还是每个创建的对象。封装是一种技术,我在我的回答的第二段中提到了它,并且在结尾处给出了示例。至于更改的影响,我也在结尾处提到,只有静态类型的变量才会对student类的每个创建的对象保持相同。想象一下这种情况,您想要计算创建的学生对象数量。还有其他问题吗? - Zeeshan

1
封装的概念是与信息隐藏有关的一种设计技术。其基本原则是通过一个良好设计的接口,为类属性提供受保护的访问。封装的目的是强制执行类的不变量。
继续考虑您提供的示例,考虑该类的接口:
class Employee

  private final String firstName;
  private final String lastName;    

  public Employee(final firstName, final lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public String getName() {
    return firstName + " " + lastName;
  }
}

请注意,通过将属性声明为私有,该类限制了客户端直接访问员工对象实例的状态。客户端访问它们的唯一方式是通过getName()方法。这意味着这些属性被该类封装起来。还要注意,通过在构造函数中将属性声明为final并初始化它们,我们创建了一个有效的不可变类,即其状态在构造后不能被修改。
另一种实现方法如下:
class Employee

  private String firstName;
  private String lastName;    

  public Employee(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public String getName() {
    return firstName + " " + lastName;
  }

  public String setName(String firstName, String lastName) {

    if (firstName == null) {
      throw new IllegalArgumentException("First name cannot be null");
    }

    if (lastName == null) {
      throw new IllegalArgumentException("Last name cannot be null");
    }

    this.firstName = firstName;
    this.lastName = lastName;
  }
}

在这个例子中,对象并不是不可变的,但它的状态被封装了起来,因为只能通过访问器和修改器进行访问。请注意,封装可以帮助您保护对象状态的不变量。通过通过方法限制修改,您可以更好地控制对象状态的修改方式,并添加验证以确保任何修改与类的规范一致。

1
Encapsulation means combining data and code together(class). The main purpose of encapsulation is you would have full control on data by using the code.

class Encap{

private int amount;

public void setAmount(int amount)
{
this.amount = amount;
}

Here, you can set the amount using the setAmount method, but value should be more than 100. So, i have the control on it.

public void setAmount(int amount)
{
if(amount>100)
this.amount = amount;
}

1
封装=变量(使用私有修饰符a、b、c)+方法(setA&getA、setB&getB…)。我们可以通过使用私有修饰符来进行封装。假设你在类中创建了一个公共变量和一个私有变量……如果你必须将这些变量提供给另一个类进行只读访问(只能看到和使用,无法修改),则使用公共变量或方法是不可能的,但是我们可以通过提供get方法在私有变量中实现这一点。因此,你的类的私有变量或方法在你的控制之下,而在公共中却没有机会……我想你可以理解。

0

您的意思是empid的值应该是10,empname应该是tom,而不是0和null,如果是这样的话-:

1)变量的内存在运行时分配,并在程序终止后释放。

2)因此,如果您认为一旦将10赋给empid,它应该始终是10,那么并非如此,因为empid只是一个引用,指向存储“10”的内存。

3)所以,通过解除分配,我指的是empid不再指向存储10的内存区域,在程序终止后。

4)每当您执行新程序时,empid现在指向其他内存区域,并且根据相应的数据类型分配默认值,在静态变量的情况下始终为0和null。


0

封装是将方法和变量作为单个单元一起包装的机制。例如胶囊,即多种药物的混合物。

类的变量将被隐藏在其他类中,因为它将被声明为私有,并且只能通过其当前类的方法访问

要在Java中实现封装- * 将类的变量声明为私有。 * 提供公共的setter和getter方法来修改和查看变量值。 * Java Bean类是完全封装类的示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接