Why Does Java Require Variables to Be Initialized? - java

In Java, why doesn't the compiler simply assume that an uninitialised variable should have 0 as its value, like C does? Is this just BetterPractice in general, or is there another reason that is particular to Java?

Because the code that uses a variable that has not been initialized, leading to unpredictable or unintended results.I guess java designers must have faced lot of problems while programming with C like stuck in loop, So probably when they were developing java they decided to get rid of this problem. And that is why we call java a robust language.
And also assigning variables initial values is not always fine. For example
int i; // uninitialized variable ,suppose it is initialize to 0 by compiler
int j=5/i; // run time error

For most situations using a variable before assigning it to anything is a mistake, and by having the compiler explicitly consider it an error it helps catching these bugs very early in the programming process.
Saying that uninitialized variables contain zero remove this capability. It is my guess that having the programmer type some more to explicitly assign variables to zero was found to be less important than finding some rather tricky bugs at runtime.

Related

Does java give a garbage data to a variable before you assign it? [duplicate]

Does it have a value?
I am trying to understand what is the state of a declared but not-initialized variable/object in Java.
I cannot actually test it, because I keep getting the "Not Initialized" compile-error and I cannot seem to be able to suppress it.
Though for example, I would guess that if the variable would be an integer it could be equal to 0.
But what if the variable would be a String, would be it be equal to null or the isEmpty() would return true?
Is the value the same for all non-initialized variables? or every declaration (meaning, int, string, double etc) has a different value when not explicitly initialized?
UPDATE
So as I see now, it makes a big difference if the variable is declared locally or in the Class, though I seem to be unable to understand why when declaring as static in the class it gives no error, but when declaring in the main it produces the "Not Initialized" error.
How exactly a JVM does this is entirely up to the JVM and shouldn't matter for a programmer, since the compiler ensures that you do not read uninitialized local variables.
Fields however are different. They need not be assigned before reading them (unless they are final) and the value of a field that has not been assigned is null for reference types or the 0 value of the appropriate primitive type, if the field has a primitive type.
Using s.isEmpty() for a field String s; that has not been assigned results in a NullPointerException.
So as I see now, it makes a big difference if the variable is declared locally or in the Class, though I seem to be unable to understand why when declaring in the class it gives no error, but when declaring in the main it produces the "Not Initialized" error.
In general it's undesirable to work with values that do not have a value. For this reason the language designers had 2 choices:
a) define a default value for variables not yet initialized
b) prevent the programmers from accessing the variable before writing to them.
b) is hard to achieve for fields and therefore option a) was chosen for fields. (There could be multiple methods reading/writing that could be valid or invalid depending on the order of calls, which could only be determined at runtime).
For local variables option b) is viable, since all possible paths of the execution of the method can be checked for assignment statements. This option was chosen during the language design for local variables, since it can help to find many easy mistakes.
Fabian already provided a very clear answer, I just try to add the specification from the official documentation for reference.
Fields in Class
It's not always necessary to assign a value when a field is declared. Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type. Relying on such default values, however, is generally considered bad programming style.
If not specified the default value, it only be treated as a bad style, while it's not the same case in local variables.
Local Variables
Local variables are slightly different; the compiler never assigns a default value to an uninitialized local variable. If you cannot initialize your local variable where it is declared, make sure to assign it a value before you attempt to use it. Accessing an uninitialized local variable will result in a compile-time error.
The default value will be based on the type of the data and place where you are using initialized variable . Please refer below for Primitive default.
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Why doesn't C++ support smart analysis of uninitialised variables? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
Here's a use case Java covers:
int x; // UNINITIALISED
if (condition){
x = 0; // init x;
}
else return;
use(x); // INITIALISED
Note:
There is at no point uninitialized memory (for x)
There is no non-deterministic state
You can not use an uninitialised variable
The variable is truly uninitialized, not null or 0 - however this doesn't leak into the run-time; I presume the compiler restructures the code, at compile time so x is initialised at deceleration only when necessary. x may be uninitialised in the code, however at runtime it's guaranteed to be initialised (which is why there's an error when you try to use it before initialisation).
From what I've seen/heard, C++ does not support this and will always run a default constructor - if available - even when it's unnecessary.
Interestingly, when a default constructor is unavailable, you actually get the same behaviour with uninitialized variables in Java. So, it's clearly possible for the C++ compiler to handle this behaviour (because it is with classes with no default constructor). So, why is it not supported fully?
I think syntax like int x = delete or [[no-default]] int x for this (as to not break any existing code) would be quite intuitive.
Please note, there's a difference between int x and int x = 0 - in Java.
Contrary to #akuzminykhs comment, it's not "initialised to 0" - I don't know why people are upvoting it
In the example, if you comment out the init x, you get an error - this will not happen in C++ - you will get undefined behaviour, because you're using uninitialised memory.
int x; // UNINITIALISED
if (Math.random() > 0.5) {
// x = 0; // init x;
} else return;
use(x); // ERROR: Variable 'x' might not have been initialized
I understand Java uses pointers, that is irrelevant to the essence of this question. This has nothing to do with the run-time, it's compile-time analysis.
Another scenario:
bool valid = false;
Type lastValid; // unnecessary default initialisation (only in C++)
for (auto object : objects)
try {
lastValid = process(object);
valid = true;
} catch (std::invalid_argument) {}
if (valid) use(lastValid);
Another Scenario: Forcing uninitialised declaration of member with a default constructor
More info: someone used this code in an example comparison between Java and C++
Example.java
Type x; // Reserve stack space for a pointer.
Example.cpp
Type x; // Reserve stack space for the object (and initialize it).
One mistake everyone seems to be making is like the comment above // Reserve stack space for a pointer, this is not what happens in Java.
The variable doesn't exist at that point during run-time. There's no allocation, no reservation, simply no unnecessary computation. The compiler is smart enough to do this. Again, this is not to do with the run-time; comparisons between the differences of java and c++ are misleading.
The reason you get an error when trying to use the uninitialised variable, is because the variable literally does not exist (at that point) during run-time (as it wasn't initialised). This is done using analysis of the code tree
Java is a very different language to C++.
In Java:
Variables without an explicit initialiser are initialised to a default value.
Variables of class types do not hold the memory of the object in-place. Rather, a variable is a pointer/reference to an object stored in the heap (called free store in C++). The closest thing comparable to Java object variable in C++ is std::shared_ptr. It's not an exact equivalent, since (besides the different lifetime management strategy) indirection through a Java variable is implicit (like C++ reference) while indirection through a C++ shared pointer is explicit through the use of indirection operator.
The indirection makes it possible for Java variable to be null. A null variable doesn't point to any object. The default value for an object variable is null.
In case of primitive types such as int, the variable is not a pointer. The default value for integer is 0.
In C++:
Variables are not implicitly references or pointers. A pointer to a class is a different type from that class.
A variable that is not a reference nor a pointer does not involve indirection. It doesn't point to an object elsewhere and because there is no pointer, there is no null. If you create such variable, then an object of that type is created.
When given no initialiser, the way a variable is initialised depends on the type as well as the storage class of the variable. For example, fundamental types such as pointers and integers, as well as trivially default constructible classes are left with an indeterminate value unless they have static storage class. Construction of such object requires no instructions to be executed by the CPU. This is fast, but difficult and potentially dangerous because there is no way to distinguish whether a value is indeterminate or well defined.
To answer the question "why": Because that is the way the language was designed.
Using indirection (which Java does) is slower than not using indirection. Using heap allocation (which Java does) is slower than allocating in-place. Always initialising value to something such as integer to 0 (which Java does) is (marginally) slower than not initialising when that value would later always be over-written. Runtime performance is often a reason behind design choices that make a language more complex or harder to use. Runtime performance is the main reason why most people use C++.
You can use a std::optional for this. Example:
std::optional<Type> x;
if (condition) {
x.emplace(); // default construct Type
}
else return;
use(x.value()); // will throw std::bad_optional_access if x is not actually initialized
For built-in types, C++ does allow uninitialized variables, so your code will compile fine in C++ too.
For class types, Java is using pointers, so can you:
#include <memory>
class B {
public:
B(int);
};
bool condition();
void use(B const&);
void foo() {
std::unique_ptr<B> b{nullptr};
if (condition())
b = std::make_unique<B>(42);
else
return;
use(*b);
}
But I think you want to know is why C++ doesn't let you leave you a variable uninitialized when it is possible statically to prove that the variable will not be used without being initialized. I can't clearly answer the "why", you should probably ask that from someone on isocpp committee. That's a feature newer languages like Kotlin enjoy but C++ hasn't caught up with yet. Herb Sutter has mentioned it in a talk I know of:
You can find that talk here:
... what if you could simply declare and then initialize before and make sure your first use is an initialization but they don't have to be in the same line. Well, we have the same tools; we can say what the definite first use is in every path and require initialization there before the variable's used ...
Then he goes on to talk about how we can introduce that into C++ type-system if memory serves me right, but considering this talk is from 2020, even in the best-case scenario, that feature will not be in C++ for the foreseeable future.
You can also have a look into "immediately invoked lambda expressions" as they could help here and they can also help you make your code more const-correct.
First of all, semantics in Java and C++ are quite different.
In Java each local variable must go through mandatory declaration -> first assignment sequence before being read where first assignment performs initialization. There is no special concept or syntax for initialization. Writing int x = 0; is equivalent to int x; x = 0;.
In C++ things are completely different. There is no special concept or syntax for declaring local variables. There is no way to postpone initialization. But there are many different special syntaxes for initialization. int x = 0; is copy initialization syntax, not just declaration with first assignment on the same line. There is also so called vacuous initialization which happens in case of int x; - even though initialization is there it does not actually initialize value with anything.
But what is more important is that language capabilities in Java and C++ are quite different as well.
In Java there is no support for pass-by-reference or pass-by-pointer. There is no way to share local variable in between threads. Changes to local variable are always observable by compiler and figuring out whether it is being read without prior assignment requires only relatively straightforward control flow analysis of current scope.
In C++ there is support for both pass-by-reference and pass-by-pointer. Local variables can be accessed from different threads. But there is even more than that, local variables can be modified by means completely unknown to compiler. That's what happens when one deals with DMA (Direct Memory Access) capable devices all the time. So there is no way for analysis to be smart enough to cover all these cases.

how to stop compiler from reading unitialized objects as errors

I have eclipse, and when I try to see if an uninitialized object equals null, it won't let me, it comes up with a "x might not have been intialized" error and I know it should work.
example of what I mean:
Object obj;
System.out.println(obj==null ? "no value":"has a value");
it would not compile and it would say 'obj might not have been initialized' How can I change my compiler settings in eclipse to fix this?
How can I change my compiler settings in eclipse to fix this?
You can't. The Java language specification requires any conformant Java compiler to treat this as a compilation error.
There is no Eclipse compiler setting to cause it to break this rule.
Even (hypothetically) if there was such a setting, I think that the bytecode file would fail verification when the JVM attempted to load it. (If you could somehow trick the JVM into using the value of an uninitialized local variable, that would undermine runtime type security, leading to JVM crashes ... and worse.)
If obj was an instance variable rather than a local variable, it would be default initialized to null and you wouldn't get a compilation error. But local variables are not default initialized.
You don't change the compiler settings in eclipse to fix this: you just initialize the variable obj.
Object obj = null;
System.out.println(obj==null ? "no value":"has a value");
From the Java Specification 4.12.5 - Initial Values of Variables:
A local variable (§14.4, §14.14) must be explicitly given a value before it is used, by either initialization (§14.4) or assignment (§15.26), in a way that can be verified using the rules for definite assignment (§16).
If you're really bent on not initializing obj you need to make it a member of a class instead of a local variable. Then it will have a default initial value: (again, refer to Java Specification 4.12.5 - Initial Values of Variables)
public class Example {
private static Object obj;
public static void main(String[] argv) throws Exception {
System.out.println(obj==null ? "no value":"has a value");
}
}
... but under the hood it's still getting initialized.
The compiler shows error because the rule is that all local variables must be initialized before they are first read. So first declare local variable without initializing it, initialize it later, and then use it:
Object obj = null;
System.out.println(obj==null ? "no value":"has a value");
That's a local variable, you need to initialise it with:
Object obj = null;
While certain fields of classes and objects, and arrays, may be implicitly initialised to useful defaults, that is not the case for local variables.
Section 16.2.4 (Local variable declaration statements) of JLS7 is the section to read if you want to understand this but it will take some time to understand, it's pretty obtuse :-)
You'd probably want to start at the top of section 16 (Definite Assignment) and work through from there. The first part of that has two paragraphs which are most pertinent here (my italics for emphasis):
For every access of a local variable or blank final field X, X must be definitely assigned before the access, or a compile-time error occurs.
Such an assignment is defined to occur if and only if either the simple name of the
variable (or, for a field, its simple name qualified by this) occurs on the left hand
side of an assignment operator.
How can I change my compiler settings in eclipse to fix this?
The solution is to fix your code. The purpose of this error is to detect and prevent bugs. Taking steps to allow broken code to compile is usually a very bad idea.

Why does javac complain about not initialized variable?

For this Java code:
String var;
clazz.doSomething(var);
Why does the compiler report this error:
Variable 'var' might not have been initialized
I thought all variables or references were initialized to null. Why do you need to do:
String var = null;
??
Instance and class variables are initialized to null (or 0), but local variables are not.
See §4.12.5 of the JLS for a very detailed explanation which says basically the same thing:
Every variable in a program must have a value before its value is used:
Each class variable, instance variable, or array component is initialized with a default value when it is created:
[snipped out list of all default values]
Each method parameter is initialized to the corresponding argument value provided by the invoker of the method.
Each constructor parameter is initialized to the corresponding argument value provided by a class instance creation expression or explicit constructor invocation.
An exception-handler parameter is initialized to the thrown object representing the exception.
A local variable must be explicitly given a value before it is used, by either initialization or assignment, in a way that can be verified by the compiler using the rules for definite assignment.
It's because Java is being very helpful (as much as possible).
It will use this same logic to catch some very interesting edge-cases that you might have missed. For instance:
int x;
if(cond2)
x=2;
else if(cond3)
x=3;
System.out.println("X was:"+x);
This will fail because there was an else case that wasn't specified. The fact is, an else case here should absolutely be specified, even if it's just an error (The same is true of a default: condition in a switch statement).
What you should take away from this, interestingly enough, is don't ever initialize your local variables until you figure out that you actually have to do so. If you are in the habit of always saying "int x=0;" you will prevent this fantastic "bad logic" detector from functioning. This error has saved me time more than once.
Ditto on Bill K. I add:
The Java compiler can protect you from hurting yourself by failing to set a variable before using it within a function. Thus it explicitly does NOT set a default value, as Bill K describes.
But when it comes to class variables, it would be very difficult for the compiler to do this for you. A class variable could be set by any function in the class. It would be very difficult for the compiler to determine all possible orders in which functions might be called. At the very least it would have to analyze all the classes in the system that call any function in this class. It might well have to examine the contents of any data files or database and somehow predict what inputs users will make. At best the task would be extremely complex, at worst impossible. So for class variables, it makes sense to provide a reliable default. That default is, basically, to fill the field with bits of zero, so you get null for references, zero for integers, false for booleans, etc.
As Bill says, you should definitely NOT get in the habit of automatically initializing variables when you declare them. Only initialize variables at declaration time if this really make sense in the context of your program. Like, if 99% of the time you want x to be 42, but inside some IF condition you might discover that this is a special case and x should be 666, then fine, start out with "int x=42;" and inside the IF override this. But in the more normal case, where you figure out the value based on whatever conditions, don't initialize to an arbitrary number. Just fill it with the calculated value. Then if you make a logic error and fail to set a value under some combination of conditions, the compiler can tell you that you screwed up rather than the user.
PS I've seen a lot of lame programs that say things like:
HashMap myMap=new HashMap();
myMap=getBunchOfData();
Why create an object to initialize the variable when you know you are promptly going to throw this object away a millisecond later? That's just a waste of time.
Edit
To take a trivial example, suppose you wrote this:
int foo;
if (bar<0)
foo=1;
else if (bar>0)
foo=2;
processSomething(foo);
This will throw an error at compile time, because the compiler will notice that when bar==0, you never set foo, but then you try to use it.
But if you initialize foo to a dummy value, like
int foo=0;
if (bar<0)
foo=1;
else if (bar>0)
foo=2;
processSomething(foo);
Then the compiler will see that no matter what the value of bar, foo gets set to something, so it will not produce an error. If what you really want is for foo to be 0 when bar is 0, then this is fine. But if what really happened is that you meant one of the tests to be <= or >= or you meant to include a final else for when bar==0, then you've tricked the compiler into failing to detect your error. And by the way, that's way I think such a construct is poor coding style: Not only can the compiler not be sure what you intended, but neither can a future maintenance programmer.
I like Bill K's point about letting the compiler work for you- I had fallen into initializing every automatic variable because it 'seemed like the Java thing to do'. I'd failed to understand that class variables (ie persistent things that constructors worry about) and automatic variables (some counter, etc) are different, even though EVERYTHING is a class in Java.
So I went back and removed the initialization I'd be using, for example
List <Thing> somethings = new List<Thing>();
somethings.add(somethingElse); // <--- this is completely unnecessary
Nice. I'd been getting a compiler warning for
List<Thing> somethings = new List();
and I'd thought the problem was lack of initialization. WRONG. The problem was I hadn't understood the rules and I needed the <Thing> identified in the "new", not any actual items of type <Thing> created.
(Next I need to learn how to put literal less-than and greater-than signs into HTML!)
I don't know the logic behind it, but local variables are not initialized to null. I guess to make your life easy. They could have done it with class variables if it were possible. It doesn't mean you have to have it initialized in the beginning. This is fine :
MyClass cls;
if (condition) {
cls = something;
else
cls = something_else;
Sure, if you've really got two lines on top of each other as you show- declare it, fill it, no need for a default constructor. But, for example, if you want to declare something once and use it several or many times, the default constructor or null declaration is relevant. Or is the pointer to an object so lightweight that its better to allocate it over and over inside a loop, because the allocation of the pointer is so much less than the instantiation of the object? (Presumably there's a valid reason for a new object at each step of the loop).
Bill IV

Why are local variables not initialized in Java?

Was there any reason why the designers of Java felt that local variables should not be given a default value? Seriously, if instance variables can be given a default value, then why can't we do the same for local variables?
And it also leads to problems as explained in this comment to a blog post:
Well this rule is most frustrating when trying to close a resource in a finally block. If I instantiate the resource inside a try, but try to close it within the finally, I get this error. If I move the instantiation outside the try, I get another error stating that a it must be within a try.
Very frustrating.
Local variables are declared mostly to do some calculation. So it's the programmer's decision to set the value of the variable and it should not take a default value.
If the programmer, by mistake, did not initialize a local variable and it takes a default value, then the output could be some unexpected value. So in case of local variables, the compiler will ask the programmer to initialize it with some value before they access the variable to avoid the usage of undefined values.
The "problem" you link to seems to be describing this situation:
SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}
The commenter's complaint is that the compiler balks at the line in the finally section, claiming that so might be uninitialized. The comment then mentions another way of writing the code, probably something like this:
// Do some work here ...
SomeObject so = new SomeObject();
try {
so.DoUsefulThings();
} finally {
so.CleanUp();
}
The commenter is unhappy with that solution because the compiler then says that the code "must be within a try." I guess that means some of the code may raise an exception that isn't handled anymore. I'm not sure. Neither version of my code handles any exceptions, so anything exception-related in the first version should work the same in the second.
Anyway, this second version of code is the correct way to write it. In the first version, the compiler's error message was correct. The so variable might be uninitialized. In particular, if the SomeObject constructor fails, so will not be initialized, and so it will be an error to attempt to call so.CleanUp. Always enter the try section after you have acquired the resource that the finally section finalizes.
The try-finally block after the so initialization is there only to protect the SomeObject instance, to make sure it gets cleaned up no matter what else happens. If there are other things that need to run, but they aren't related to whether the SomeObject instance was property allocated, then they should go in another try-finally block, probably one that wraps the one I've shown.
Requiring variables to be assigned manually before use does not lead to real problems. It only leads to minor hassles, but your code will be better for it. You'll have variables with more limited scope, and try-finally blocks that don't try to protect too much.
If local variables had default values, then so in the first example would have been null. That wouldn't really have solved anything. Instead of getting a compile-time error in the finally block, you'd have a NullPointerException lurking there that might hide whatever other exception could occur in the "Do some work here" section of the code. (Or do exceptions in finally sections automatically chain to the previous exception? I don't remember. Even so, you'd have an extra exception in the way of the real one.)
Moreover, in the example below, an exception may have been thrown inside the SomeObject construction, in which case the 'so' variable would be null and the call to CleanUp will throw a NullPointerException
SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}
What I tend to do is this:
SomeObject so = null;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
if (so != null) {
so.CleanUp(); // safe
}
}
The actual answer to your question is because method variables are instantiated by simply adding a number to the stack pointer. To zero them would be an extra step. For class variables they are put into initialized memory on the heap.
Why not take the extra step? Take a step back--Nobody mentioned that the "warning" in this case is a Very Good Thing.
You should never initialize your variable to zero or null on the first pass (when you are first coding it). Either assign it to the actual value or don't assign it at all because if you don't then Java can tell you when you really screw up. Take Electric Monk's answer as a great example. In the first case, it's actually amazingly useful that it's telling you that if the try() fails because SomeObject's constructor threw an exception, then you would end up with an NPE in the finally. If the constructor can't throw an exception, it shouldn't be in the try.
This warning is an awesome multi-path bad programmer checker that has saved me from doing stupid stuff since it checks every path and makes sure that if you used the variable in some path then you had to initialize it in every path that lead up to it. I now never explicitly initialize variables until I determine that it is the correct thing to do.
On top of that, isn't it better to explicitly say "int size=0" rather than "int size" and make the next programmer go figure out that you intend it to be zero?
On the flip side I can't come up with a single valid reason to have the compiler initialize all uninitialized variables to 0.
Notice that the final instance/member variables don't get initialized by default. Because those are final and can't be changed in the program afterwards. That's the reason that Java doesn't give any default value for them and force the programmer to initialize it.
On the other hand, non-final member variables can be changed later. Hence, the compiler doesn't let them remain uninitialised; precisely, because those can be changed later. Regarding local variables, the scope of local variables is much narrower; and compiler knows when it's getting used. Hence, forcing the programmer to initialize the variable, makes sense.
For me, the reason comes down to this this: The purpose of local variables is different than the purpose of instance variables. Local variables are there to be used as part of a calculation; instance variables are there to contain state. If you use a local variable without assigning it a value, that's almost certainly a logic error.
That said, I could totally get behind requiring that instance variables were always explicitly initialized; the error would occur on any constructor where the result allows an uninitialized instance variable (e.g., not initialized at declaration and not in the constructor). But that's not the decision Gosling, et. al., took in the early 90's, so here we are. (And I'm not saying they made the wrong call.)
I could not get behind defaulting local variables, though. Yes, we shouldn't rely on compilers to double-check our logic, and one doesn't, but it's still handy when the compiler catches one out. :-)
I think the primary purpose was to maintain similarity with C/C++. However the compiler detects and warns you about using uninitialized variables which will reduce the problem to a minimal point. From a performance perspective, it's a little faster to let you declare uninitialized variables since the compiler will not have to write an assignment statement, even if you overwrite the value of the variable in the next statement.
It is more efficient not to initialize variables, and in the case of local variables it is safe to do so, because initialization can be tracked by the compiler.
In cases where you need a variable to be initialized you can always do it yourself, so it is not a problem.
The idea behind local variables is they only exist inside the limited scope for which they are needed. As such, there should be little reason for uncertainty as to the value, or at least, where that value is coming from. I could imagine many errors arising from having a default value for local variables.
For example, consider the following simple code... (N.B. let us assume for demonstration purposes that local variables are assigned a default value, as specified, if not explicitly initialized)
System.out.println("Enter grade");
int grade = new Scanner(System.in).nextInt(); // I won't bother with exception handling here, to cut down on lines.
char letterGrade; // Let us assume the default value for a char is '\0'
if (grade >= 90)
letterGrade = 'A';
else if (grade >= 80)
letterGrade = 'B';
else if (grade >= 70)
letterGrade = 'C';
else if (grade >= 60)
letterGrade = 'D';
else
letterGrade = 'F';
System.out.println("Your grade is " + letterGrade);
When all is said and done, assuming the compiler assigned a default value of '\0' to letterGrade, this code as written would work properly. However, what if we forgot the else statement?
A test run of our code might result in the following
Enter grade
43
Your grade is
This outcome, while to be expected, surely was not the coder's intent. Indeed, probably in a vast majority of cases (or at least, a significant number, thereof), the default value wouldn't be the desired value, so in the vast majority of cases the default value would result in error. It makes more sense to force the coder to assign an initial value to a local variable before using it, since the debugging grief caused by forgetting the = 1 in for(int i = 1; i < 10; i++) far outweighs the convenience in not having to include the = 0 in for(int i; i < 10; i++).
It is true that try-catch-finally blocks could get a little messy (but it isn't actually a catch-22 as the quote seems to suggest), when for example an object throws a checked exception in its constructor, yet for one reason or another, something must be done to this object at the end of the block in finally. A perfect example of this is when dealing with resources, which must be closed.
One way to handle this in the past might be like so...
Scanner s = null; // Declared and initialized to null outside the block. This gives us the needed scope, and an initial value.
try {
s = new Scanner(new FileInputStream(new File("filename.txt")));
int someInt = s.nextInt();
} catch (InputMismatchException e) {
System.out.println("Some error message");
} catch (IOException e) {
System.out.println("different error message");
} finally {
if (s != null) // In case exception during initialization prevents assignment of new non-null value to s.
s.close();
}
However, as of Java 7, this finally block is no longer necessary using try-with-resources, like so.
try (Scanner s = new Scanner(new FileInputStream(new File("filename.txt")))) {
...
...
} catch(IOException e) {
System.out.println("different error message");
}
That said, (as the name suggests) this only works with resources.
And while the former example is a bit yucky, this perhaps speaks more to the way try-catch-finally or these classes are implemented than it speaks about local variables and how they are implemented.
It is true that fields are initialized to a default value, but this is a bit different. When you say, for example, int[] arr = new int[10];, as soon as you've initialized this array, the object exists in memory at a given location. Let's assume for a moment that there is no default values, but instead the initial value is whatever series of 1s and 0s happens to be in that memory location at the moment. This could lead to non-deterministic behavior in a number of cases.
Suppose we have...
int[] arr = new int[10];
if(arr[0] == 0)
System.out.println("Same.");
else
System.out.println("Not same.");
It would be perfectly possible that Same. might be displayed in one run and Not same. might be displayed in another. The problem could become even more grievous once you start talking reference variables.
String[] s = new String[5];
According to definition, each element of s should point to a String (or is null). However, if the initial value is whatever series of 0s and 1s happens to occur at this memory location, not only is there no guarantee you'll get the same results each time, but there's also no guarantee that the object s[0] points to (assuming it points to anything meaningful) even is a String (perhaps it's a Rabbit, :p)! This lack of concern for type would fly in the face of pretty much everything that makes Java Java. So while having default values for local variables could be seen as optional at best, having default values for instance variables is closer to a necessity.
Flip this around and ask: why are fields initialised to default values? If the Java compiler required you to initialise fields yourself instead of using their default values, that would be more efficient because there would be no need to zero out memory before you used it. So it would be a sensible language design if all variables were treated like local variables in this regard.
The reason is not because it's more difficult to check this for fields than for local variables. The Java compiler already knows how to check whether a field is definitely initialised by a constructor, because it has to check this for final fields. So it would be little extra work for the compiler to apply the same logic to other fields to ensure they are definitely assigned in the constructor.
The reason is that, even for final fields where the compiler proves that the field is definitely assigned in the constructor, its value before assignment can still be visible from other code:
class A {
final int x;
A() {
this.x = calculate();
}
int calculate() {
System.out.println(this.x);
return 1;
}
}
In this code, the constructor definitely assigns to this.x, but even so, the field's default initial value of 0 is visible in the calculate method at the point where this.x is printed. If the field wasn't zeroed out before the constructor was invoked, then the calculate method would be able to observe the contents of uninitialised memory, which would be non-deterministic behaviour and have potential security concerns.
The alternative would be to forbid the method call calculate() at this point in the code where the field isn't yet definitely assigned. But that would be inconvenient; it is useful to be able to call methods from the constructor like this. The convenience of being able to do that is worth more than the tiny performance cost of zeroing out the memory for the fields before invoking the constructor.
Note that this reasoning does not apply to local variables, because a method's uninitialised local variables are not visible from other methods; because they are local.
Eclipse even gives you warnings of uninitialized variables, so it becomes quite obvious anyway. Personally I think it's a good thing that this is the default behaviour, otherwise your application may use unexpected values, and instead of the compiler throwing an error it won't do anything (but perhaps give a warning) and then you'll be scratching your head as to why certain things don't quite behave the way they should.
Instance variable will have default values but the local variables could not have default values. Since local variables basically are in methods/behavior, its main aim is to do some operations or calculations. Therefore, it is not a good idea to set default values for local variables. Otherwise, it is very hard and time-consuming to check the reasons of unexpected answers.
The local variables are stored on a stack, but instance variables are stored on the heap, so there are some chances that a previous value on the stack will be read instead of a default value as happens in the heap.
For that reason the JVM doesn't allow to use a local variable without initializing it.
Memory stack for methods is created at execution time. The method stack order is decided at execution time.
There might be a function that may not be called at all. So to instantiate local variables at the time of object instantiation would be a complete wastage of memory. Also, Object variables remain in memory for a complete object lifecycle of a class whereas, local variables and their values become eligible for garbage collection the moment they are popped from the memory stack.
So, To give memory to the variables of methods that might not even be called or even if called, will not remain inside memory for the lifecycle of an object, would be a completely illogical and memory-waste-worthy
The answer is instance variables can be initialized in the class constructor or any class method. But in case of local variables, once you defined whatever in the method, that remains forever in the class.
I could think of the following two reasons
As most of the answers said, by putting the constraint of initialising the local variable, it is ensured that the local variable gets assigned a value as the programmer wants and ensures the expected results are computed.
Instance variables can be hidden by declaring local variables (same name) - to ensure the expected behaviour, local variables are forced to be initialised to a value (I would totally avoid this, though).

Categories

Resources