Choosing when to instantiate classes

I recently wrote a class for an assignment in which I had to store names in an ArrayList (in java). I initialized ArrayList as an instance variable private ArrayList<String> names . Later, when I checked my work against the solution, I noticed that they initialized their ArrayList in the run() method.

I thought about it a bit, and I feel that it may be a matter of taste, but in general, how to choose in such situations? Does it take up less memory or something else?

PS I like instance variables in Ruby that start with the @ symbol: they are more attractive.

(meta-question: what would be the best heading for this question?)

+1
source share
6 answers

According to the great Knut, "Premature optimization is the root of all evil."

Just worry that your program is functioning correctly and that it has no errors. This is much more important than obscure optimization, which will be difficult to debug later.

But to answer your question - if you are initialized in a class member, memory will be allocated the first time you mention your class in the code (i.e., when you call a method from it). If you initialize a method, memory allocation occurs later when you call this particular method.

So, this is only a matter of initialization later ... this is called lazy initialization in the industry.

+3
source

Initialization

Typically, try to initialize variables when they are declared.

If the value of the variable is not changed, make it explicit using the final keyword. This will help you justify the correctness of your code, and although I do not know the compiler optimizers or JVMs that recognize the final keyword, they will certainly be possible.

Of course, there are exceptions to this rule. For example, a variable may be assigned in if if n else; else or switch. In this case, an โ€œemptyโ€ declaration (without initialization) is preferable to initialization, which is guaranteed to be overwritten before the dummy value is read.

 /* DON'T DO THIS! */ Color color = null; switch(colorCode) { case RED: color = new Color("crimson"); break; case GREEN: color = new Color("lime"); break; case BLUE: color = new Color("azure"); break; } color.fill(widget); 

You now have a NullPointerException if an unrecognized color code is presented. It is better not to assign meaningless null . The compiler will color.fill() error when calling color.fill() because it will detect that you could not initialize color .

To answer your question in this case, I will need to see this code. If the solution initialized it inside the run() method, it should have been used either as a temporary storage or as a way to "return" the results of the task.

If the collection is used as temporary storage and is not accessible outside the method, it should be declared as a local variable, not an instance variable, and most likely should be initialized where it is declared in the method.

Concurrency Problems

To start the programming course, your teacher probably did not try to confront you with the complexities of parallel programming, although if so, I'm not sure why you used Thread . But, given the current trends in CPU design, anyone learning to program should have a clear understanding of concurrency. I will try to go deeper here a little deeper.

The returned results from the thread run method are a bit more complicated. This method is a Runnable interface, and nothing stops multiple threads from executing the run method of a single instance. The resulting concurrency problems are part of the motivation for the Callable interface introduced in Java 5. This is very similar to Runnable , but can return a thread-safe way and throw an Exception if the task cannot be completed.

This is a bit of a digression, but if you're interested, consider the following example:

 class Oops extends Thread { /* Note that thread implements "Runnable" */ private int counter = 0; private Collection<Integer> state = ...; public void run() { state.add(counter); counter++; } public static void main(String... argv) throws Exception { Oops oops = new Oops(); oops.start(); Thread t2 = new Thread(oops); /* Now pass the same Runnable to a new Thread. */ t2.start(); /* Execute the "run" method of the same instance again. */ ... } } 

At the end of the main method, you hardly know what the โ€œstateโ€ of Collection . Two threads work on it at the same time, and we did not indicate whether the collection is safe for simultaneous use. If we initialize it inside the stream, at least we can say that ultimately state will contain one element, but we cannot say whether it will be 0 or 1.

+2
source

From wikibooks :

In Java, there are three main types of scope for variables:

  • a local variable declared in a class method valid for (and taking up only memory) the execution time of this method. Each time a method is called, a new copy of the variable is used.

  • instance variable declared inside the class, but outside of any method. It is valid and stores memory as long as the corresponding object is in memory; a program can instantiate several objects of a class, and each gets its own copy of all instance variables. This is a basic rule of the data structure of object-oriented programming; classes are defined to store data specific to a "class of objects" in a given system, and each instance contains its own data.

  • a static variable declared inside the class as static, outside of any method. There is only one copy of such a variable, regardless of how many objects are created from this class.

So yes, memory consumption is a problem, especially if the ArrayList inside run () is local.

0
source

I do not fully understand your complete problem.

But as I understand it now, the performance / memory advantage will be negligible. Therefore, I would definitely support the safety side.

So do what suits you. Optimize memory performance / optimization if necessary.

0
source

My personal rule for instance variables is to initialize them, at least with a default value:

  • during division, i.e.

    private ArrayList<String> myStrings = new ArrayList<String>();

  • in the constructor

If this is what is truly an instance variable and represents the state of the object, it is fully initialized by the time the constructor completes. Otherwise, you will discover the opportunity to try to access the variable before it gets the value. Of course, this does not apply to primitives, where you will automatically get the default value.

For static (class-level) variables, initialize them in a declaration or in a static initializer. I use a static initializer if I have calculations or other work to get the value. Initialize the declaration if you simply call new Foo() or set the variable to a known value.

0
source

You need to avoid lazy initialization. This leads to problems later. But if you need to do this because initialization is too heavy, you need to do it like this:

Static fields:

 // Lazy initialization holder class idiom for static fields private static class FieldHolder { static final FieldType field = computeFieldValue(); } static FieldType getField() { return FieldHolder.field; } 

Instance Fields:

 // Double-check idiom for lazy initialization of instance fields private volatile FieldType field; FieldType getField() { FieldType result = field; if (result == null) { // First check (no locking) synchronized(this) { result = field; if (result == null) // Second check (with locking) field = result = computeFieldValue(); } } return result; } 

According to Joshua Boch's book, Effective Java โ„ข Second Edition (ISBN-13: 978-0-321-35668-0):
"use lazy initialization wisely"

-1
source

Source: https://habr.com/ru/post/885829/


All Articles