为什么使用getters和setter?

使用getter和setter有什么好处 – 只能得到和设置 – 而不是简单地使用这些variables的公共字段?

如果吸气人员和安装人员做的不仅仅是简单的get / set,我可以很快地把这个问题弄清楚,但是我不是100%清楚的:

public String foo; 

比任何更糟的是:

 private String foo; public void setFoo(String foo) { this.foo = foo; } public String getFoo() { return foo; } 

而前者则less了许多样板代码。

从Java Web开发人员的观点来看,这里列出了我最喜欢的获奖者名单:

  1. 当你意识到你需要做的不仅仅是设置和获取价值,你不必改变代码库中的每个文件。
  2. 你可以在这里执行validation。
  3. 您可以更改正在设置的值。
  4. 您可以隐藏内部表示。 getAddress()实际上可以为你获得几个字段。
  5. 您已将公共界面与表单下的更改隔离开来。
  6. 有些图书馆期待这一点 reflection,序列化,模拟对象。
  7. inheritance这个类,你可以覆盖默认的function。
  8. 你可以对getter和setter有不同的访问级别。
  9. 延迟加载。
  10. 人们可以很容易地告诉你没有使用Python。

实际上有许多好的理由要考虑使用访问器,而不是直接暴露一个类的领域 – 除了封装的参数和使未来的变化更容易。

以下是我所知道的一些原因:

  • 封装与获取或设置属性相关的行为 – 这允许稍后添加更多function(如validation)。
  • 隐藏属性的内部表示,同时使用替代表示forms显示属性。
  • 将公共接口从变化中隔离出来 – 允许公共接口在执行过程中保持不变,而不会影响现有的消费者。
  • 控制属性的生命周期和内存pipe理(处置)语义 – 在非托pipe内存环境(如C ++或Objective-C)中尤为重要。
  • 在运行时为属性更改提供一个debugging拦截点 – 在某些语言中debugging何时何地属性更改为特定值可能非常困难。
  • 提高了与devise用于针对属性获取器/设置器运行的库的互操作性 – 让我们想起了模拟,序列化和WPF。
  • 允许inheritance者通过重写getter / setter方法来改变属性行为的语义。
  • 允许getter / setter作为lambdaexpression式而不是值传递。
  • 吸气者和吸引者可以允许不同的访问级别 – 例如获取可能是公开的,但是该组可以被保护。

因为从现在起2个星期(几个月,几年),当你意识到你的制定者需要做的不仅仅是设置价值,你还会意识到这个属性已经被其他238个类直接使用了:-)

公共领域并不比一个getter / setter对更糟糕,除了返回字段并赋值给它。 首先,很显然(在大多数语言中)没有function差异。 任何差异必须在其他因素,如可维护性或可读性。

getter / setter对的一个优点是,不是。 有这个声明,你可以改变实现,你的客户端不必重新编译。 据说,setter可以让你稍后添加validation等function,你的客户甚至不需要知道它。 然而,对二传手的join是对前提条件的改变, 违反了前一个合同 ,很简单,“你可以把任何东西放在这里,而且你可以从吸气者那里得到同样的东西”。

所以,现在你违约了,改变代码库中的每个文件都是你应该做的事情,而不是避免。 如果你避开它,你就假设所有的代码都假定这些方法的合同是不同的。

如果这不应该是合同,那么接口就是让客户把对象置于无效状态。 这与封装完全相反如果这个领域从一开始就不能真正被设置为什么,为什么不从一开始就进行validation呢?

这个相同的论点适用于这些传递getter / setter对的其他优点:如果您以后决定更改设置的值,那么您违反了合同。 如果您在派生类中重写默认function,那么您可以通过超出一些无害修改(如日志logging或其他非可观察行为)的方式,来打破基类的合同。 这违反了Liskov可替代性原则,这被视为面向对象的原则之一。

如果一个阶级在每一个领域都有这些愚蠢的获得者和制定者,那么这个阶级没有任何不变的东西, 没有合同 。 那真的是面向对象的devise吗? 如果所有class级都是那些获得者和设置者,那么这只是一个愚蠢的数据持有者,而哑数据持有者应该看起来像是哑数据持有者:

 class Foo { public: int DaysLeft; int ContestantNumber; }; 

将传递的getter / setter对添加到这样的类不会增加任何值。 其他类应该提供有意义的操作,而不仅仅是字段已经提供的操作。 这就是你如何定义和维护有用的不variables。

客户 :“我能用这个class级的对象做什么?”
devise师 :“你可以读写几个variables。”
客户 :“哦,很酷,我猜?”

有一些使用getter和setter的理由,但是如果这些原因不存在,那么以伪封装神为名的getter / setter对就不是好事。 有效的理由使得吸气剂或者吸附剂包括经常被提到的事情,如后面可能做出的改变,比如validation或者不同的内部performance。 或者,也许值应该是客户端可读,但不可写(例如,阅读字典的大小),所以一个简单的getter是一个不错的select。 但是当你做出select的时候,这些理由应该在那里,而不仅仅是你以后可能想要的。 这是YAGNI的一个例子( 你不会需要它 )。

很多人谈论获得者和制定者的优点,但是我想扮演魔鬼的拥护者。 现在我正在debugging一个非常大的程序,程序员决定让所有的getter和setter成为可能。 这看起来不错,但它是一个逆向工程的噩梦。

假设您正在浏览数百行代码,并且遇到以下情况:

 person.name = "Joe"; 

这是一个非常简单的代码,直到你意识到它是一个二传手。 现在,你遵循这个setter,发现它也设置person.firstName,person.lastName,person.isHuman,person.hasReallyCommonFirstName,并调用person.update(),它将查询发送到数据库,等等。你的内存泄漏发生在哪里。

乍一看对本地代码的理解是一个重要的属性,它具有良好的可读性,即getter和setter往往会打破。 这就是为什么我尽量避免他们,并尽量减less他们做什么,当我使用它们。

有很多原因。 我最喜欢的是当你需要改变行为或规定你可以设置一个variables。 例如,假设你有一个setSpeed(int speed)方法。 但是你希望你只能设置一个100的最大速度。你会这样做:

 public void setSpeed(int speed) { if ( speed > 100 ) { this.speed = 100; } else { this.speed = speed; } } 

现在如果你在代码中的任何地方使用公共领域,然后你意识到你需要上述要求呢? 玩得开心,追求公共领域的每一次使用,而不仅仅是修改你的setter。

我2美分:)

访问器和增变器的一个优点是可以执行validation。

例如,如果foo是公开的,我可以很容易地将其设置为null ,然后其他人可以尝试调用对象的方法。 但它不在了! 用setFoo方法,我可以确保foo从来没有被设置为null

访问器和增变器也允许封装 – 如果你不应该看到它的值(可能是在构造函数中设置,然后被方法使用,但从来没有被改变),那么任何人都不会看到这个值。 但是,如果您可以允许其他类查看或更改它,则可以提供正确的存取器和/或增变器。

在一个纯粹的面向对象的世界里,获取者和制定者是一个可怕的反模式 。 阅读这篇文章: Getters / Setters。 邪恶。 期间 。 简而言之,他们鼓励程序员将对象视为数据结构,而这种types的思维是纯粹的程序化的(就像在COBOL或C中一样)。 在面向对象的语言中没有数据结构,只有暴露行为的对象(不是属性/属性!)

您可以在Elegant Objects的第3.5节(我的关于面向对象编程的书)中find更多关于它们的内容。

取决于你的语言。 您已经标记了这个“面向对象”而不是“Java”,所以我想指出ChssPly76的答案是依赖于语言的。 例如在Python中,没有理由使用getter和setter。 如果您需要更改行为,可以使用一个属性,该属性包含基本属性访问权限的getter和setter。 像这样的东西:

 class Simple(object): def _get_value(self): return self._value -1 def _set_value(self, new_value): self._value = new_value + 1 def _del_value(self): self.old_values.append(self._value) del self._value value = property(_get_value, _set_value, _del_value) 

那么我只想补充一点,即使有时它们对于你的variables/对象的封装和安全是必要的,如果我们想要编写一个真正的面向对象的程序,那么我们需要停止使用ACCESSORS ,因为有时候我们会依赖很多在他们什么时候不是真的有必要,这就和把variables公开一样。

谢谢,这真的澄清了我的想法。 现在这里(几乎)有10个(几乎)不是使用getter和setter的好理由:

  1. 当你意识到你需要做的不仅仅是设置和获得价值,你可以让这个领域是私人的,它会立即告诉你你直接访问它的地方。
  2. 你在那里执行的任何validation只能是上下文无关的,这种validation很less在实践中进行。
  3. 您可以更改设置的值 – 这是一个绝对的噩梦,当来电者传递给您一个价值,他们[震惊恐怖]要你存储的原样。
  4. 你可以隐藏内部表示 – 太棒了,所以你要确保所有这些操作是对称的吗?
  5. 你已经将你的公共接口与表单之下的变化隔离开来了 – 如果你正在devise一个接口,并且不确定是否可以直接访问某些东西,那么你应该保持devise。
  6. 一些图书馆期望这一点,但不是很多 – reflection,序列化,模拟对象都可以在公共领域工作得很好。
  7. inheritance这个类,你可以重写默认的function – 换句话说,你可以通过不仅隐藏实现而且使其不一致而真正地混淆调用者。

最后三个我刚刚离开(N / A或D / C)…

它可以用于延迟加载。 说有问题的对象存储在一个数据库中,除非你需要它,否则你不想去得到它。 如果对象被getter检索到,那么内部对象可以是null,直到有人请求它,那么你可以在第一次调用getter的时候得到它。

我在一个项目中有一个基础页面类,这个项目是从几个不同的Web服务调用中加载一些数据的,但这些Web服务调用中的数据并不总是在所有子页面中使用。 Web服务为所有的好处开创了“慢”的新定义,所以如果你不需要的话,你不想做Web服务调用。

我从公共领域移动到getter,现在getters检查caching,如果不是那里调用web服务。 所以有一点点包装,很多networking服务调用被阻止。

所以吸气剂使我无法在每个子页面上找出我需要的东西。 如果我需要的话,我打电话给吸气剂,如果我还没有吸气剂的话,就去找我。

  protected YourType _yourName = null; public YourType YourName{ get { if (_yourName == null) { _yourName = new YourType(); return _yourName; } } } 

我知道这有点迟,但我认为有一些人对表演感兴趣:)

我做了一点性能testing。 我写了一个“NumberHolder”类,它持有一个Integer。 您可以使用getter方法anInstance.getNumber()读取该Integer,也可以使用anInstance.getNumber()直接访问该数字。 我的程序通过两种方式读取数十亿次。 该过程重复五次,打印时间。 我有以下结果:

 Time 1: 953ms, Time 2: 741ms Time 1: 655ms, Time 2: 743ms Time 1: 656ms, Time 2: 634ms Time 1: 637ms, Time 2: 629ms Time 1: 633ms, Time 2: 625ms 

(时间1是直接的方式,时间2是获取者)

你看,吸气剂(几乎)总是快一点。 然后我尝试了不同数量的周期。 我用了1000万和10万,而不是100万。 结果:

1000万个周期:

 Time 1: 6382ms, Time 2: 6351ms Time 1: 6363ms, Time 2: 6351ms Time 1: 6350ms, Time 2: 6363ms Time 1: 6353ms, Time 2: 6357ms Time 1: 6348ms, Time 2: 6354ms 

一千万个周期的时间几乎是一样的。 这里有10万(10万)个周期:

 Time 1: 77ms, Time 2: 73ms Time 1: 94ms, Time 2: 65ms Time 1: 67ms, Time 2: 63ms Time 1: 65ms, Time 2: 65ms Time 1: 66ms, Time 2: 63ms 

也有不同的周期数量,吸气剂比常规方式快一点。 我希望这对你有帮助! 🙂

顺便说一下,我是一个德语七年级,使用我自己的知识和谷歌翻译。 所以不要对我的英语严格;)

我花了很长一段时间思考Java的情况,我相信真正的原因是:

  1. 代码到接口,而不是执行
  2. 接口只是指定方法,而不是字段

换句话说,唯一可以在接口中指定字段的方法是提供一个写入新值的方法和一个读取当前值的方法。

这些方法是臭名昭着的getter和setter ….

不要使用getter setter,除非你当前的交付需要,也就是说,不要过多地考虑将来会发生什么,如果在大多数生产应用系统中有什么东西要改变它的变更请求的话。

想想简单,容易,需要时增加复杂性。

我不会利用对技术深厚的企业主的无知,因为我认为这是正确的,或者我喜欢这种方法。

我有大量的系统编写没有getter setter只有访问修饰符和一些方法来validationn执行biz逻辑。 如果你绝对需要的话。 使用任何东西

我们使用getter和setter:

  • 重用性
  • 在编程的后期阶段执行validation

Getter和setter方法是访问私有类成员的公共接口。


封装口头禅

封装的口头禅是使领域私密和方法公开。

Getter方法: 我们可以访问私有variables。

Setter方法: 我们可以修改私有字段。

即使getter和setter方法没有添加新的function,但是我们可以改变我们的想法,稍后再来做出这个方法

  • 更好;
  • 更安全; 和
  • 更快。

在任何可以使用的值中,都可以添加返回该值的方法。 代替:

 int x = 1000 - 500 

使用

 int x = 1000 - class_name.getValue(); 

用外行的话来说

“人”类的表示

假设我们需要存储这个Person的细节。 This Person has the fields name , age and sex . Doing this involves creating methods for name , age and sex . Now if we need create another person, it becomes necessary to create the methods for name , age , sex all over again.

Instead of doing this, we can create a bean class(Person) with getter and setter methods. So tomorrow we can just create objects of this Bean class(Person class) whenever we need to add a new person (see the figure). Thus we are reusing the fields and methods of bean class, which is much better.

One aspect I missed in the answers so far, the access specification:

  • for members you have only one access specification for both setting and getting
  • for setters and getters you can fine tune it and define it separately

EDIT: I answered this question because there are a bunch of people learning programming asking this, and most of the answers are very technically competent, but they're not as easy to understand if you're a newbie. We were all newbies, so I thought I'd try my hand at a more newbie friendly answer.

The two main ones are polymorphism, and validation. Even if it's just a stupid data structure.

Let's say we have this simple class:

 public class Bottle { public int amountOfWaterMl; public int capacityMl; } 

A very simple class that holds how much liquid is in it, and what its capacity is (in milliliters).

What happens when I do:

 Bottle bot = new Bottle(); bot.amountOfWaterMl = 1500; bot.capacity = 1000; 

Well, you wouldn't expect that to work, right? You want there to be some kind of sanity check. And worse, what if I never specified the maximum capacity? Oh dear, we have a problem.

But there's another problem too. What if bottles were just one type of container? What if we had several containers, all with capacities and amounts of liquid filled? If we could just make an interface, we could let the rest of our program accept that interface, and bottles, jerrycans and all sorts of stuff would just work interchangably. Wouldn't that be better? Since interfaces demand methods, this is also a good thing.

We'd end up with something like:

 public interface LiquidContainer { public int getAmountMl(); public void setAmountMl(int amountMl); public int getCapacityMl(); public void setCapcityMl(int capacityMl); } 

大! And now we just change Bottle to this:

 public class Bottle extends LiquidContainer { private int capacityMl; private int amountFilledMl; public Bottle(int capacityMl, int amountFilledMl) { this.capacityMl = capacityMl; this.amountFilledMl = amountFilledMl; checkNotOverFlow(); } public int getAmountMl() { return amountFilledMl; } public void setAmountMl(int amountMl) { this.amountFilled = amountMl; checkNotOverFlow(); } public int getCapacityMl() { return capacityMl; public void setCapcityMl(int capacityMl) { this.capacityMl = capacityMl; checkNotOverFlow(); } private void checkNotOverFlow() { if(amountOfWaterMl > capacityMl) { throw new BottleOverflowException(); } } 

I'll leave the definition ofthe BottleOverflowException as an exercise to the reader.

Now notice how much more robust this is. We can deal with any type of container in our code now by accepting LiquidContainer instead of Bottle. And how these bottles deal with this sort of stuff can all differ. You can have bottles that writer their state to disk when it changes, or bottles that save on SQL databases or GNU knows what else.

And all these can have different ways to handle various whoopsies. The Bottle just checks and if it's overflowing it throws a RuntimeException. But that might be the wrong thing to do. (There is a useful discussion to be had about error handling, but I'm keeping it very simple here on purpose. People in comments will likely point out the flaws of this simplistic approach. 😉 )

And yes, it seems like we go from a very simple idea to getting much better answers quickly.

There's also the third thing that not everyone has addressed: Getters and setters use method calls. That means that they look like normal methods everywhere else does. Instead of having weird specific syntax for DTOs and stuff, you have the same thing everywhere.

One of the basic principals of OO design: Encapsulation!

It gives you many benefits, one of which being that you can change the implementation of the getter/setter behind the scenes but any consumer of that value will continue to work as long as the data type remains the same.

In languages which don't support "properties" (C++, Java) or require recompilation of clients when changing fields to properties (C#), using get/set methods is easier to modify. For example, adding validation logic to a setFoo method will not require changing the public interface of a class.

In languages which support "real" properties (Python, Ruby, maybe Smalltalk?) there is no point to get/set methods.

From a object orientation design standpoint both alternatives can be damaging to the maintenance of the code by weakening the encapsulation of the classes. For a discussion you can look into this excellent article: http://typicalprogrammer.com/?p=23

Getter and setter methods are accessor methods, meaning that they are generally a public interface to change private class members. You use getter and setter methods to define a property. You access getter and setter methods as properties outside the class, even though you define them within the class as methods. Those properties outside the class can have a different name from the property name in the class.

There are some advantages to using getter and setter methods, such as the ability to let you create members with sophisticated functionality that you can access like properties. They also let you create read-only and write-only properties.

Even though getter and setter methods are useful, you should be careful not to overuse them because, among other issues, they can make code maintenance more difficult in certain situations. Also, they provide access to your class implementation, like public members. OOP practice discourages direct access to properties within a class.

When you write classes, you are always encouraged to make as many as possible of your instance variables private and add getter and setter methods accordingly. This is because there are several times when you may not want to let users change certain variables within your classes. For example, if you have a private static method that tracks the number of instances created for a specific class, you don't want a user to modify that counter using code. Only the constructor statement should increment that variable whenever it's called. In this situation, you might create a private instance variable and allow a getter method only for the counter variable, which means users are able to retrieve the current value only by using the getter method, and they won't be able to set new values using the setter method. Creating a getter without a setter is a simple way of making certain variables in your class read-only.

Code evolves . private is great for when you need data member protection . Eventually all classes should be sort of "miniprograms" that have a well-defined interface that you can't just screw with the internals of .

That said, software development isn't about setting down that final version of the class as if you're pressing some cast iron statue on the first try. While you're working with it, code is more like clay. It evolves as you develop it and learn more about the problem domain you are solving. During development classes may interact with each other than they should (dependency you plan to factor out), merge together, or split apart. So I think the debate boils down to people not wanting to religiously write

 int getVar() const { return var ; } 

所以你有:

 doSomething( obj->getVar() ) ; 

代替

 doSomething( obj->var ) ; 

Not only is getVar() visually noisy, it gives this illusion that gettingVar() is somehow a more complex process than it really is. How you (as the class writer) regard the sanctity of var is particularly confusing to a user of your class if it has a passthru setter — then it looks like you're putting up these gates to "protect" something you insist is valuable, (the sanctity of var ) but yet even you concede var 's protection isn't worth much by the ability for anyone to just come in and set var to whatever value they want, without you even peeking at what they are doing.

So I program as follows (assuming an "agile" type approach — ie when I write code not knowing exactly what it will be doing/don't have time or experience to plan an elaborate waterfall style interface set):

1) Start with all public members for basic objects with data and behavior. This is why in all my C++ "example" code you'll notice me using struct instead of class everywhere.

2) When an object's internal behavior for a data member becomes complex enough, (for example, it likes to keep an internal std::list in some kind of order), accessor type functions are written. Because I'm programming by myself, I don't always set the member private right away, but somewhere down the evolution of the class the member will be "promoted" to either protected or private .

3) Classes that are fully fleshed out and have strict rules about their internals (ie they know exactly what they are doing, and you are not to "fuck" (technical term) with its internals) are given the class designation, default private members, and only a select few members are allowed to be public .

I find this approach allows me to avoid sitting there and religiously writing getter/setters when a lot of data members get migrated out, shifted around, etc. during the early stages of a class's evolution.

There is a good reason to consider using accessors is there is no property inheritance. See next example:

 public class TestPropertyOverride { public static class A { public int i = 0; public void add() { i++; } public int getI() { return i; } } public static class B extends A { public int i = 2; @Override public void add() { i = i + 2; } @Override public int getI() { return i; } } public static void main(String[] args) { A a = new B(); System.out.println(ai); a.add(); System.out.println(ai); System.out.println(a.getI()); } } 

输出:

 0 0 4 

You should use getters and setters when:

  • You're dealing with something that is conceptually an attribute, but:
    • Your language doesn't have properties (or some similar mechanism, like Tcl's variable traces), or
    • Your language's property support isn't sufficient for this use case, or
    • Your language's (or sometimes your framework's) idiomatic conventions encourage getters or setters for this use case.

So this is very rarely a general OO question; it's a language-specific question, with different answers for different languages (and different use cases).


From an OO theory point of view, getters and setters are useless. The interface of your class is what it does, not what its state is. (If not, you've written the wrong class.) In very simple cases, where what a class does is just, eg, represent a point in rectangular coordinates,* the attributes are part of the interface; getters and setters just cloud that. But in anything but very simple cases, neither the attributes nor getters and setters are part of the interface.

Put another way: If you believe that consumers of your class shouldn't even know that you have a spam attribute, much less be able to change it willy-nilly, then giving them a set_spam method is the last thing you want to do.

* Even for that simple class, you may not necessarily want to allow setting the x and y values. If this is really a class, shouldn't it have methods like translate , rotate , etc.? If it's only a class because your language doesn't have records/structs/named tuples, then this isn't really a question of OO…


But nobody is ever doing general OO design. They're doing design, and implementation, in a specific language. And in some languages, getters and setters are far from useless.

If your language doesn't have properties, then the only way to represent something that's conceptually an attribute, but is actually computed, or validated, etc., is through getters and setters.

Even if your language does have properties, there may be cases where they're insufficient or inappropriate. For example, if you want to allow subclasses to control the semantics of an attribute, in languages without dynamic access, a subclass can't substitute a computed property for an attribute.

As for the "what if I want to change my implementation later?" question (which is repeated multiple times in different wording in both the OP's question and the accepted answer): If it really is a pure implementation change, and you started with an attribute, you can change it to a property without affecting the interface. Unless, of course, your language doesn't support that. So this is really just the same case again.

Also, it's important to follow the idioms of the language (or framework) you're using. If you write beautiful Ruby-style code in C#, any experienced C# developer other than you is going to have trouble reading it, and that's bad. Some languages have stronger cultures around their conventions than others.—and it may not be a coincidence that Java and Python, which are on opposite ends of the spectrum for how idiomatic getters are, happen to have two of the strongest cultures.

Beyond human readers, there will be libraries and tools that expect you to follow the conventions, and make your life harder if you don't. Hooking Interface Builder widgets to anything but ObjC properties, or using certain Java mocking libraries without getters, is just making your life more difficult. If the tools are important to you, don't fight them.

One other use (in languages that support properties) is that setters and getters can imply that an operation is non-trivial. Typically, you want to avoid doing anything that's computationally expensive in a property.

Getters and setters are used to implement two of the fundamental aspects of Object Oriented Programming which are:

  1. 抽象化
  2. Encapsulation

Suppose we have an Employee class:

 package com.highmark.productConfig.types; public class Employee { private String firstName; private String middleName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getMiddleName() { return middleName; } public void setMiddleName(String middleName) { this.middleName = middleName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getFullName(){ return this.getFirstName() + this.getMiddleName() + this.getLastName(); } } 

Here the implementation details of Full Name is hidden from the user and is not accessible directly to the user, unlike a public attribute.

Additionally, this is to "future-proof" your class. In particular, changing from a field to a property is an ABI break, so if you do later decide that you need more logic than just "set/get the field", then you need to break ABI, which of course creates problems for anything else already compiled against your class.

I would just like to throw the idea of annotation : @getter and @setter. With @getter, you should be able to obj = class.field but not class.field = obj. With @setter, vice versa. With @getter and @setter you should be able to do both. This would preserve encapsulation and reduce the time by not calling trivial methods at runtime.

I can think of one reason why you wouldn't just want everything public.

For instance, variable you never intended to use outside of the class could be accessed, even irdirectly via chain variable access (ie object.item.origin.x ).

By having mostly everything private, and only the stuff you want to extend and possibly refer to in subclasses as protected, and generally only having static final objects as public, then you can control what other programmers and programs can use in the API and what it can access and what it can't by using setters and getters to access the stuff you want the program, or indeed possibly other programmers who just happen to use your code, can modify in your program.

One relatively modern advantage of getters/setters is that is makes it easier to browse code in tagged (indexed) code editors. Eg If you want to see who sets a member, you can open the call hierarchy of the setter.

On the other hand, if the member is public, the tools don't make it possible to filter read/write access to the member. So you have to trudge though all uses of the member.