Java 实例变量与本地变量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1794141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java Instance Variables vs Local Variables
提问by user218251
I'm in my first programming class in high school. We're doing our end of the first semester project. This project only involves one class, but many methods. My question is best practice with instance variables and local variables. It seems that it would be much easier for me to code using almost only instance variables. But I'm not sure if this is how I should be doing it or if I should be using local variables more (I would just have to have methods take in the values of local variables a lot more).
我在高中的第一个编程课上。我们正在做我们第一学期末的项目。本项目只涉及一个类,方法很多。我的问题是实例变量和局部变量的最佳实践。对我来说,几乎只使用实例变量进行编码似乎要容易得多。但我不确定这是否是我应该做的,或者我是否应该更多地使用局部变量(我只需要让方法更多地接收局部变量的值)。
My reasoning for this is also because a lot of times I'll want to have a method return two or three values, but this is of course not possible. Thus it just seems easier to simply use instance variables and never having to worry since they are universal in the class.
我的理由也是因为很多时候我希望一个方法返回两个或三个值,但这当然是不可能的。因此,简单地使用实例变量似乎更容易,而不必担心,因为它们在类中是通用的。
回答by Marius Burz
Short story: if and only if a variable needs to be accessed by more than one method(or outside of the class) create it as an instance variables. If you need it only locally, in a single method, it has to be a local variable. Instance variables are more costly than local variables.
Keep in mind: instance variables are initialized to default values while local variables are not.
小故事:当且仅当一个变量需要被多个方法(或在类之外)访问时才将其创建为实例变量。如果您只在本地需要它,在单个方法中,它必须是一个局部变量。实例变量比局部变量成本更高。
请记住:实例变量被初始化为默认值,而局部变量则不是。
回答by Stephen
Use instance variables when it's a core concept of your class. If you're iterating, recursing or doing some processing, then use local variables.
当实例变量是类的核心概念时,请使用它。如果您正在迭代、递归或进行一些处理,请使用局部变量。
When you need to use two (or more) variables in the same places, it's time to create a new class with those attributes (and appropriate means to set them). This will make your code cleaner and help you think about problems (each class is a new term in your vocabulary).
当您需要在同一位置使用两个(或更多)变量时,是时候创建一个具有这些属性的新类(以及设置它们的适当方法)。这将使您的代码更清晰并帮助您思考问题(每个类都是您词汇表中的一个新术语)。
One variable may be made a class when it is a core concept. For example real-world identifiers: these could be represented as Strings, but often, if you encapsulate them into their own object they suddenly start "attracting" functionality (validation, association to other objects etc)
当一个变量是一个核心概念时,它可以成为一个类。例如现实世界的标识符:这些可以表示为字符串,但通常,如果您将它们封装到它们自己的对象中,它们会突然开始“吸引”功能(验证、与其他对象的关联等)
Also (not entirely related) is object consistency - an object is able to ensure that it's state makes sense. Setting one property may alter another. It also makes it far easier to alter your program to be thread-safe later (if required).
另外(不完全相关)是对象一致性——一个对象能够确保它的状态是有意义的。设置一个属性可能会改变另一个。它还可以更轻松地稍后将程序更改为线程安全(如果需要)。
回答by Andrés Bonilla
Local variables internal to methods are always prefered, since you want to keep each variable's scope as small as possible. But if more than one method needs to access a variable, then it's going to have to be an instance variable.
始终首选方法内部的局部变量,因为您希望使每个变量的范围尽可能小。但是如果多个方法需要访问一个变量,那么它就必须是一个实例变量。
Local variables are more like intermediate values used to reach a result or compute something on the fly. Instance variables are more like attributes of a class, like your age or name.
局部变量更像是用于达到结果或即时计算某些东西的中间值。实例变量更像是一个类的属性,比如你的年龄或名字。
回答by Kaleb Brasee
Try to think about your problem in terms of objects. Each class represents a different type of object. Instance variables are the pieces of data that a class needs to remember in order to work, either with itself or with other objects. Local variables should just be used intermediate calculations, data that you don't need to save once you leave the method.
试着从对象的角度考虑你的问题。每个类代表不同类型的对象。实例变量是类需要记住以便与自身或其他对象一起工作的数据片段。局部变量应该只用于中间计算,离开方法后不需要保存的数据。
回答by Doug Knesek
Declare variables to be scoped as narrowly as possible. Declare local variables first. If this isn't sufficient, use instance variables. If this isn't sufficient, use class (static) variables.
声明变量的范围尽可能窄。先声明局部变量。如果这还不够,请使用实例变量。如果这还不够,请使用类(静态)变量。
I you need to return more than one value return a composite structure, like an array or an object.
我需要返回多个值返回一个复合结构,如数组或对象。
回答by ccyu
The easy way: if the variable must be shared by more than one method, use instance variable, otherwise use local variable.
简单的方法:如果变量必须由多个方法共享,则使用实例变量,否则使用局部变量。
However, the good practice is to use as more local variables as possible. Why? For your simple project with only one class, there is no difference. For a project that includes a lot of classes, there is big difference. The instance variable indicates the state of your class. The more instance variables in your class, the more states this class can have and then, the more complex this class is, the hard the class is maintained or the more error prone your project might be. So the good practice is to use as more local variable as possible to keep the state of the class as simple as possible.
但是,好的做法是使用尽可能多的局部变量。为什么?对于只有一个类的简单项目,没有区别。对于一个包含很多类的项目,差别很大。实例变量指示类的状态。你的类中的实例变量越多,这个类可以拥有的状态就越多,然后这个类越复杂,这个类就越难维护,或者你的项目越容易出错。所以好的做法是使用尽可能多的局部变量来保持类的状态尽可能简单。
回答by Ravi Wallau
Try not to return more than one value from your methods in first place. If you can't, and in some cases you really can't, then I would recommend encapsulating that in a class. Just in last case I would recommend changing another variable inside your class (an instance variable). The problem with the instance variables approach is that it increases side effects - for example, you call method A in your program and it modifies some instance(s) variable(s). Over time, that leads to increased complexity in your code and maintenance becomes harder and harder.
首先尽量不要从您的方法中返回多个值。如果你不能,在某些情况下你真的不能,那么我建议将它封装在一个类中。在最后一种情况下,我建议更改类中的另一个变量(实例变量)。实例变量方法的问题在于它增加了副作用 - 例如,您在程序中调用方法 A 并且它修改了一些实例变量。随着时间的推移,这会导致代码的复杂性增加,并且维护变得越来越困难。
When I have to use instance variables, I try to make then final and initialize then in the class constructors, so side effects are minimized. This programming style (minimizing the state changes in your application) should lead to better code that is easier to maintain.
当我必须使用实例变量时,我尝试在类构造函数中使 then 成为 final 并初始化 then,因此副作用被最小化。这种编程风格(最小化应用程序中的状态更改)应该会产生更易于维护的更好的代码。
回答by chubbsondubs
I haven't seen anyone discuss this so I'll throw in more food for thought. The short answer/advice is don't use instance variables over local variables just because you think they are easier to return values. You are going to make working with your code very very hard if you don't use local variables and instance variables appropriately. You will produce some serious bugs that are really hard to track down. If you want to understand what I mean by serious bugs, and what that might look like read on.
我还没有看到任何人讨论这个,所以我会投入更多的思考。简短的回答/建议是不要仅仅因为您认为它们更容易返回值而在局部变量上使用实例变量。如果您没有适当地使用局部变量和实例变量,您将很难使用您的代码。你会产生一些很难追踪的严重错误。如果您想了解我所说的严重错误是什么意思,以及它可能是什么样子,请继续阅读。
Let's try and use only instance variables as you suggest to write to functions. I'll create a very simple class:
让我们尝试只使用您建议的实例变量来写入函数。我将创建一个非常简单的类:
public class BadIdea {
public Enum Color { GREEN, RED, BLUE, PURPLE };
public Color[] map = new Colors[] {
Color.GREEN,
Color.GREEN,
Color.RED,
Color.BLUE,
Color.PURPLE,
Color.RED,
Color.PURPLE };
List<Integer> indexes = new ArrayList<Integer>();
public int counter = 0;
public int index = 0;
public void findColor( Color value ) {
indexes.clear();
for( index = 0; index < map.length; index++ ) {
if( map[index] == value ) {
indexes.add( index );
counter++;
}
}
}
public void findOppositeColors( Color value ) {
indexes.clear();
for( index = 0; i < index < map.length; index++ ) {
if( map[index] != value ) {
indexes.add( index );
counter++;
}
}
}
}
This is a silly program I know, but we can use it to illustrate the concept that using instance variables for things like this is a tremendously bad idea. The biggest thing you'll find is that those methods use all of the instance variables we have. And it modifies indexes, counter, and index every time they are called. The first problem you'll find is that calling those methods one after the other can modify the answers from prior runs. So for example, if you wrote the following code:
我知道这是一个愚蠢的程序,但我们可以用它来说明这样一个概念,即对这样的事情使用实例变量是一个非常糟糕的主意。您会发现最重要的是这些方法使用了我们拥有的所有实例变量。并且每次调用时都会修改索引、计数器和索引。您会发现的第一个问题是,一个接一个地调用这些方法可以修改先前运行的答案。例如,如果您编写了以下代码:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
idea.findColor( Color.GREEN ); // whoops we just lost the results from finding all Color.RED
Since findColor uses instance variables to track returned values we can only return one result at a time. Let's try and save off a reference to those results before we call it again:
由于 findColor 使用实例变量来跟踪返回值,我们一次只能返回一个结果。在再次调用之前,让我们尝试保存对这些结果的引用:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes;
int redCount = idea.counter;
idea.findColor( Color.GREEN ); // this causes red positions to be lost! (i.e. idea.indexes.clear()
List<Integer> greenPositions = idea.indexes;
int greenCount = idea.counter;
In this second example we saved the red positions on the 3rd line, but same thing happened!?Why did we lose them?! Because idea.indexes was cleared instead of allocated so there can only be one answer used at a time. You have to completely finish using that result before calling it again. Once you call a method again the results are cleared and you lose everything. In order to fix this you'll have to allocate a new result each time so red and green answers are separate. So let's clone our answers to create new copies of things:
在第二个例子中,我们保存了第 3 行的红色位置,但同样的事情发生了!?为什么我们失去了它们?!因为idea.indexes 被清除而不是分配,所以一次只能使用一个答案。在再次调用它之前,您必须完全使用该结果。一旦你再次调用一个方法,结果就会被清除,你就会失去一切。为了解决这个问题,你每次都必须分配一个新的结果,这样红色和绿色的答案是分开的。因此,让我们克隆我们的答案以创建事物的新副本:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes.clone();
int redCount = idea.counter;
idea.findColor( Color.GREEN );
List<Integer> greenPositions = idea.indexes.clone();
int greenCount = idea.counter;
Ok finally we have two separate results. The results of red and green are now separate. But, we had to know a lot about how BadIdea operated internally before the program worked didn't we? We need to remember to clone the returns every time we called it to safely make sure our results didn't get clobbered. Why is the caller forced to remember these details? Wouldn't it be easier if we didn't have to do that?
好的,最后我们有两个单独的结果。红色和绿色的结果现在是分开的。但是,在程序运行之前,我们必须非常了解 BadIdea 的内部运作方式,不是吗?我们需要记住每次调用它时都要克隆返回值,以确保我们的结果不会被破坏。为什么来电者被迫记住这些细节?如果我们不必这样做,不是更容易吗?
Also notice that the caller has to use local variables to remember the results so while you didn't use local variables in the methods of BadIdea the caller has to use them to remember results. So what did you really accomplish? You really just moved the problem to the caller forcing them to do more. And the work you pushed onto the caller is not an easy rule to follow because there are some many exceptions to the rule.
另请注意,调用者必须使用局部变量来记住结果,因此虽然您没有在 BadIdea 的方法中使用局部变量,但调用者必须使用它们来记住结果。那么你真正完成了什么?你真的只是把问题转移到调用者身上,迫使他们做更多的事情。你推送给调用者的工作不是一个容易遵循的规则,因为规则有很多例外。
Now let's try doing that with two different methods. Notice how I've been "smart" and I reused those same instance variables to "save memory" and kept the code compact. ;-)
现在让我们尝试用两种不同的方法来做到这一点。请注意我是如何“聪明”的,我重用了那些相同的实例变量来“节省内存”并保持代码紧凑。;-)
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes;
int redCount = idea.counter;
idea.findOppositeColors( Color.RED ); // this causes red positions to be lost again!!
List<Integer> greenPositions = idea.indexes;
int greenCount = idea.counter;
Same thing happened! Damn but I was being so "smart" and saving memory and the code uses less resources!!! This is the real peril of using instance variables like this is calling methods is order dependent now. If I change the order of the method calls the results are different even though I haven't really changed the underlying state of BadIdea. I didn't change the contents of the map. Why does the program yield different results when I call the methods in different order?
同样的事情发生了!该死,但我太“聪明”了,节省了内存,代码使用的资源更少!!!这是使用实例变量的真正危险,因为现在调用方法依赖于顺序。如果我改变方法调用的顺序,即使我没有真正改变 BadIdea 的底层状态,结果也会有所不同。我没有改变地图的内容。当我以不同的顺序调用方法时,为什么程序会产生不同的结果?
idea.findColor( Color.RED )
idea.findOppositeColors( Color.RED )
Produces a different result than if I swapped those two methods:
产生与交换这两种方法不同的结果:
idea.findOppositeColors( Color.RED )
idea.findColor( Color.RED )
These types of errors are really hard to track down especially when those lines aren't right next to each other. You can completely break your program by just adding a new call in anywhere between those two lines and get wildly different results. Sure when we're dealing with small number of lines it's easy to spot errors. But, in a larger program you can waste days trying to reproduce them even though the data in the program hasn't changed.
这些类型的错误真的很难追踪,尤其是当这些线彼此不相邻时。你可以通过在这两行之间的任何地方添加一个新的调用来完全破坏你的程序,并得到截然不同的结果。当然,当我们处理少量行时,很容易发现错误。但是,在更大的程序中,即使程序中的数据没有改变,您也可能会浪费数天时间尝试重现它们。
And this only looks at single threaded problems. If BadIdea was being used in a multi-threaded situation the errors can get really bizarre. What happens if findColors() and findOppositeColors() is called at the same time? Crash, all your hair falls out, Death, space and time collapse into a singularity and the universe is swallows up? Probably at least two of those. Threads are probably above your head now, but hopefully we can steer you away from doing bad things now so when you do get to threads those bad practices don't cause you real heartache.
而这只关注单线程问题。如果在多线程情况下使用 BadIdea,错误可能会变得非常奇怪。如果同时调用 findColors() 和 findOppositeColors() 会发生什么?崩溃,你的头发都掉光了,死亡,时空坍缩成一个奇点,宇宙被吞没?可能至少有两个。线程现在可能在您的头顶之上,但希望我们现在可以引导您远离做坏事,这样当您进入线程时,这些不良做法不会让您真正心痛。
Did you notice how careful you had to be when calling the methods? They overwrote each other, they shared memory possibly randomly, you had to remember the details of how it worked on the inside to make it work on the outside, changing the order in which things were called produce very big changes in the next lines down, and it only could only work in a single thread situation. Doing things like this will produce really brittle code that seems to fall apart whenever you touch it. These practices I showed contributed directly to the code being brittle.
您是否注意到在调用方法时必须非常小心?他们互相覆盖,他们可能随机地共享内存,你必须记住它如何在内部工作以使其在外部工作的细节,改变事物被调用的顺序在下一行产生非常大的变化,并且只能在单线程情况下工作。做这样的事情会产生非常脆弱的代码,当你触摸它时似乎会崩溃。我展示的这些实践直接导致代码变得脆弱。
While this might look like encapsulation it is the exact opposite because the technical details of how you wrote it have to be known to the caller. The caller has to write their code in a very particular way to make their code work, and they can't do it without knowing about the technical details of your code. This is often called a Leaky Abstractionbecause the class is suppose to hide the technical details behind an abstraction/interface, but the technical details leak out forcing the caller to change their behavior. Every solution has some degree of leaky-ness, but using any of the above techniques like these guarantees no matter what problem you are trying to solve it will be terribly leaky if you apply them. So let's look at the GoodIdea now.
虽然这可能看起来像封装,但恰恰相反,因为调用者必须知道如何编写它的技术细节。调用者必须以非常特殊的方式编写他们的代码才能使他们的代码工作,如果不了解代码的技术细节,他们就无法做到。这通常被称为泄漏抽象,因为该类假设隐藏抽象/接口背后的技术细节,但技术细节泄漏迫使调用者改变他们的行为。每个解决方案都有一定程度的泄漏,但是使用上述任何一种技术,无论您尝试解决什么问题,都可以保证如果您应用它们,就会非常泄漏。现在让我们来看看GoodIdea。
Let's rewrite using local variables:
让我们使用局部变量重写:
public class GoodIdea {
...
public List<Integer> findColor( Color value ) {
List<Integer> results = new ArrayList<Integer>();
for( int i = 0; i < map.length; i++ ) {
if( map[index] == value ) {
results.add( i );
}
}
return results;
}
public List<Integer> findOppositeColors( Color value ) {
List<Integer> results = new ArrayList<Integer>();
for( int i = 0; i < map.length; i++ ) {
if( map[index] != value ) {
results.add( i );
}
}
return results;
}
}
This fixes every problem we discussed above. I know I'm not keeping track of counter or returning it, but if I did I can create a new class and return that instead of List. Sometimes I use the following object to return multiple results quickly:
这解决了我们上面讨论的每个问题。我知道我没有跟踪计数器或返回它,但是如果我这样做了,我可以创建一个新类并返回它而不是 List。有时我使用以下对象快速返回多个结果:
public class Pair<K,T> {
public K first;
public T second;
public Pair( K first, T second ) {
this.first = first;
this.second = second;
}
}
Long answer, but a very important topic.
答案很长,但一个非常重要的话题。
回答by developer.g
Generally variables should have minimal scope.
通常变量应该有最小的范围。
Unfortunately, in order to build classes with minimized variable scope, one often needs to do a lot of method parameter passing.
不幸的是,为了构建具有最小变量范围的类,通常需要进行大量的方法参数传递。
But if you follow that advice all the time, perfectly minimizing variable scope, you may end up with a lot of redundancy and method inflexibility with all the required objects passed in and out of methods.
但是如果你一直遵循这个建议,完美地最小化变量范围,你最终可能会因为所有必需的对象传入和传出方法而导致大量冗余和方法不灵活。
Picture a code base with thousands of methods like this:
想象一个包含数千种方法的代码库,如下所示:
private ClassThatHoldsReturnInfo foo(OneReallyBigClassThatHoldsCertainThings big,
AnotherClassThatDoesLittle little) {
LocalClassObjectJustUsedHere here;
...
}
private ClassThatHoldsReturnInfo bar(OneMediumSizedClassThatHoldsCertainThings medium,
AnotherClassThatDoesLittle little) {
...
}
And, on the other hand, imagine a code base with lots of instance variables like this:
并且,另一方面,想象一个包含大量实例变量的代码库,如下所示:
private OneReallyBigClassThatHoldsCertainThings big;
private OneMediumSizedClassThatHoldsCertainThings medium;
private AnotherClassThatDoesLittle little;
private ClassThatHoldsReturnInfo ret;
private void foo() {
LocalClassObjectJustUsedHere here;
....
}
private void bar() {
....
}
As code increases, the first way may minimize variable scope best, but can easily lead to a lot of method parameters being passed around. The code will usually be more verbose and this can lead to a complexity as one refactors all these methods.
随着代码的增加,第一种方式可能最好地最小化变量范围,但很容易导致大量方法参数被传递。代码通常会更加冗长,这会导致重构所有这些方法时的复杂性。
Using more instance variables can reduce the complexity of lots of method parameters being passed around and can give a flexibility to methods when you are frequently reorganizing methods for clarity. But it creates more object state that you have to maintain. Generally the advice is to do the former and refrain from the latter.
使用更多的实例变量可以降低传递的大量方法参数的复杂性,并且可以在您经常重新组织方法以保持清晰时为方法提供灵活性。但它会创建更多您必须维护的对象状态。一般来说,建议是做前者而不要做后者。
However, very often, and it may depend on the person, one can more easily manage state complexity compared with the thousands of extra object references of the first case. One may notice this when business logic within methods increases and organization needs to change to keep order and clarity.
然而,很多时候,这可能取决于个人,与第一种情况的数千个额外对象引用相比,人们可以更容易地管理状态复杂性。当方法中的业务逻辑增加并且组织需要改变以保持秩序和清晰时,人们可能会注意到这一点。
Not only that. When you reorganize your methods to keep clarity and make lots of method parameter changes in the process, you end up with lots of version control diffs which is not so good for stable production quality code. There is a balance. One way causes one kind of complexity. The other way causes another kind of complexity.
不仅。当您重新组织您的方法以保持清晰并在过程中进行大量方法参数更改时,您最终会得到许多版本控制差异,这对于稳定的生产质量代码来说并不是那么好。有一个平衡。一种方式导致一种复杂性。另一种方式导致另一种复杂性。
Use the way that works best for you. You will find that balance over time.
使用最适合您的方式。随着时间的推移,你会发现这种平衡。
I think this young programmer has some insightful first impressions for low maintenance code.
我认为这位年轻的程序员对低维护代码有一些深刻的第一印象。
回答by JavaDeveloper
Use instance variable when
使用实例变量时
- If 2 functions in the class need the same value then make it instance variable OR
- If the state is not expected to change make it instance variable. eg: immutable object, DTO, LinkedList, those with final variables OR
- If it is an underlying data on whom actions are performed. eg: final in arr[] in PriorityQueue.java source code OR
- Even if it is used only once && state is expected to change, make it instance if it is used only once by a function whose parameter list should be empty. eg: HTTPCookie.java Line: 860 hashcode() function uses 'path variable'.
- 如果类中的 2 个函数需要相同的值,则使其成为实例变量或
- 如果预期状态不会改变,则使其成为实例变量。例如:不可变对象、DTO、LinkedList、带有最终变量的对象或
- 如果它是执行操作的基础数据。例如:final in arr[] in PriorityQueue.java 源代码或
- 即使它只使用一次 && 状态预计会改变,如果它只被一个参数列表应该为空的函数使用一次,就让它成为实例。例如:HTTPCookie.java 行:860 hashcode() 函数使用“路径变量”。
Similarly Use local variable when none of these conditions match, specifically role of the variable would end after stack is popped off. eg: Comparator.compare(o1, o2);
类似地,当这些条件都不匹配时使用局部变量,特别是在堆栈弹出后变量的作用将结束。例如:Comparator.compare(o1, o2);