Proč jsou lokální proměnné není inicializována v Javě?

hlasů
89

Byl tam nějaký důvod, proč návrháři Javy pocit, že lokální proměnné by neměl být podáván výchozí hodnotu? Vážně, pokud instance proměnné lze uvést výchozí hodnotu, tak proč nemůžeme udělat totéž pro lokální proměnné?

A to také vede k problémům, jak je vysvětleno v tomto komentáři na blogu :

No toto pravidlo je nejvíce frustrující, když se snaží uzavřít zdroj v bloku finally. Kdybych instanci zdroje uvnitř pokus, ale snaží se ji uzavřít v rámci konečně dostanu tuto chybu. Kdybych přesunout instance mimo pokus, mám jiné chybě tvrzení, že to musí být v rámci pokusu.

Velmi frustrující.

Položena 06/01/2009 v 08:03
zdroj uživatelem
V jiných jazycích...                            


14 odpovědí

hlasů
3

Myslím, že hlavním cílem bylo zachovat podobnost s C / C ++. Nicméně kompilátor rozpozná a upozorní vás o používání neinicializované proměnné, které sníží problém minimální bodu. Z hlediska výkonu, je to trochu rychlejší nechat prohlásit neinicializované proměnné, protože kompilátor nebude muset psát příkazu přiřazení, a to i v případě, přepsat hodnotu proměnné v dalším prohlášení.

Odpovězeno 06/01/2009 v 08:12
zdroj uživatelem

hlasů
54

Lokální proměnné jsou deklarovány především udělat nějaké výpočty. Takže rozhodnutí svého programátora nastavit hodnotu proměnné a nemělo by mít výchozí hodnotu. V případě, že programátor, omylem, neinicializoval lokální proměnné a trvat výchozí hodnotu, pak výstup by mohl být nějaký neočekávané hodnoty. Takže v případě lokálních proměnných, kompilátor vyzve programátora inicializovat s nějakou hodnotu před tím, než přístup k proměnné, aby se zabránilo používání nedefinovaných hodnot.

Odpovězeno 06/01/2009 v 08:18
zdroj uživatelem

hlasů
10

Všimněte si, že konečné proměnné instance / členské nenechte se inicializuje ve výchozím nastavení. Protože ty jsou konečné a nemůže být změněn v programu později. To je důvod, že Java neposkytuje žádnou výchozí hodnotu pro ně a nutí programátor k inicializaci.

Na druhé straně, non-konečné proměnné členů mohou být později změněny. Proto je překladač nedovolí jim zůstane i zovaných, přesně, protože ty mohou být později změněny. Pokud jde o lokálních proměnných, rozsah lokálních proměnných je mnohem užší. Překladač neví, kdy jeho čas zvykat. Z tohoto důvodu, nutí programátor inicializovat proměnné dává smysl.

Odpovězeno 06/01/2009 v 08:31
zdroj uživatelem

hlasů
22

„Problém“ propojíte se zdá být popisující tuto situaci:

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

Stížnost komentátor je, že kompilátor trámů na trati v finallyúseku s tvrzením, že soby mohl být neinicializované. Potom komentář zmiňuje jiný způsob psaní kódu, pravděpodobně něco takového:

// Do some work here ...
SomeObject so = new SomeObject();
try {
  so.DoUsefulThings();
} finally {
  so.CleanUp();
}

Komentátor je spokojen s tímto roztokem, protože kompilátor pak říká, že kód „musí být v rámci pokusu.“ Myslím, že to znamená, že některé z kódu, může vyvolat výjimku, která není ještě zpracována. Nejsem si jistý. Ani verzi mého kódu zpracovává žádné výjimky, takže cokoliv výjimka související u první verze by měla fungovat stejně i ve druhém.

Mimochodem, tato druhá verze kódu je správný způsob, jak psát. V první verzi, chybová zpráva kompilátoru byl správný. soProměnná může být inicializována. Zejména v případě, že SomeObjectkonstruktor selže, sonedojde k inicializaci, a tak to bude chyba, aby se pokusili volat so.CleanUp. Vždy zadat tryčást poté, co jste získali zdroje, že finallyčást dokončí.

try- finallyblok po soinicializaci je tu jen k ochraně SomeObjectinstance, aby se ujistil, že dostane vyčistit bez ohledu na to, co jiného se stane. Pokud existují jiné věci, které potřebují ke spuštění, ale nejsou souvisí s zda SomeObjectinstance byl z vlastnictví přidělený, pak by měl jít do jiného try - finallybloku, pravděpodobně ten, který obtéká ten jsem ukázal.

Vyžadující proměnných, které mají být přiřazena ručně před použitím nevede ke skutečné problémy. To vede jen k drobné nepříjemnosti, ale váš kód bude lepší. Budete mít proměnné s více omezeném rozsahu, a try- finallybloky, které se nesnaží chránit příliš mnoho.

Pokud lokální proměnné měli výchozí hodnoty, pak sov prvním příkladu by to bylo null. To by opravdu vyřešili cokoliv. Místo toho, aby chyby v době kompilace v finallybloku, měli byste mít NullPointerExceptiončíhá tam, který by mohl skrývat jakákoli jiná výjimka může dojít v části „dělat nějakou práci tady“ kódu. (Nebo výjimky v finallysekcích automaticky řetěz na předchozí výjimky? Nevzpomínám si. Dokonce tak, že máte navíc výjimku v cestě ten skutečný.)

Odpovězeno 06/01/2009 v 09:00
zdroj uživatelem

hlasů
0

Eclipse dokonce dává vám varování neinicializované proměnné, takže se stane zcela zřejmé, tak jako tak. Osobně si myslím, že je to dobrá věc, že ​​se jedná o výchozí chování, jinak vaše aplikace mohou používat neočekávané hodnoty, a namísto kompilátoru hází chybu, že nebude dělat nic (ale třeba dát varování) a pak budete poškrábání hlavu, proč některé věci nemají úplně chovat tak, jak by měly.

Odpovězeno 06/01/2009 v 09:10
zdroj uživatelem

hlasů
2

Je efektivnější není inicializovat proměnné, a v případě lokálních proměnných je to bezpečné, protože inicializace lze sledovat kompilátorem.

V případech, kdy potřebujete proměnnou inicializovat vždy můžete udělat sami, takže to není problém.

Odpovězeno 06/01/2009 v 10:48
zdroj uživatelem

hlasů
12

Kromě toho, v níže uvedeném příkladu, výjimka může být vyvolána uvnitř someObject konstrukce, přičemž v tomto případě ‚takže‘ proměnná bude mít hodnotu null a volání vyčistění bude hodit NullPointerException

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

Co mám tendenci udělat, je toto:

SomeObject so = null;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  if (so != null) {
     so.CleanUp(); // safe
  }
}
Odpovězeno 06/01/2009 v 10:58
zdroj uživatelem

hlasů
0

Tyto lokální proměnné jsou uloženy na hromadu, ale instance proměnné jsou uloženy na haldě, takže tam jsou některé šance, že předchozí hodnota na zásobníku budou číst namísto výchozí hodnoty, jak je tomu v haldě. Z tohoto důvodu JVM neumožňuje používat lokální proměnné bez inicializovat.

Odpovězeno 28/01/2009 v 17:50
zdroj uživatelem

hlasů
4

(Může se to zdát divné, psát novou odpověď po otázce tak dlouho, ale duplikát přišel.)

Pro mě je důvod, přijde na to toto: Účelem lokálních proměnných je jiný než účel proměnných instance. Lokální proměnné jsou tam mají být použity jako součást výpočtu; instance proměnné jsou tam obsahovat stav. Používáte-li lokální proměnné bez přiřazení to hodnota, která je téměř jistě logická chyba.

To znamená, že bych mohl úplně dostat za vyžadující instanci proměnné byly vždy explicitně inicializovat; chyba by došlo k jakékoliv konstruktoru, kde důsledkem umožňuje neinicializované proměnné instance (např není inicializována při prohlášení, a nikoli v konstruktoru). Ale to není rozhodnutí Gosling, et. a kol., se na počátku 90. let, tak jsme tady. (A já neříkám, že se špatně volání.)

Nemohl jsem ani získat za prodlení lokální proměnné, ačkoli. Ano, měli bychom se neměli spoléhat na kompilátory překontrolovat, zda naši logiku, a jeden ne, ale je to stále po ruce, když kompilátor chytí jeden ven. :-)

Odpovězeno 25/03/2010 v 09:24
zdroj uživatelem

hlasů
9

Skutečná odpověď na vaši otázku je, protože metoda proměnné jsou instance pouhým přidáním čísla na ukazateli zásobníku. Nule nich by byl další krok. Pro třídy proměnných, které jsou vloženy do inicializována paměti na haldě.

Proč ne vzít další krok? Udělat krok zpět - Nikdo se zmínil, že „varování“ je v tomto případě velmi dobrá věc.

Nikdy byste neměli inicializaci proměnnou na nulu nebo null v prvním průchodu (pokud ho nejprve kódování). Buď přiřadit k aktuální hodnotě nebo ji nepřiřadí vůbec, protože pokud nemáte pak java může říct, když opravdu zkazit. Vezměte Electric mnišská odpověď jako skvělý příklad. V prvním případě je to vlastně neuvěřitelně užitečné, že to říkám, že v případě, že pokus () nezdaří, protože someObject v konstruktoru došlo k výjimce, pak byste měli skončit s NPE v hotově. V případě, že konstruktor nelze házet výjimku, nemělo by být v pokusu.

Toto upozornění je úžasné multi-path špatná kontrola programátor, který mě zachránil od dělá blbosti, protože kontroluje každou cestu a zajišťuje, že pokud jste použili proměnnou v nějaké cestě a pak byste měli k inicializaci v každé cestě, která vede až k ní , I teď se nikdy explicitně inicializovat proměnné, dokud jsem zjistit, že je to správná věc.

Na vrcholu se, že není lepší výslovně říkat „velikost int = 0“ spíše než „velikosti int“ a provést další programátor jít přijít na to, že jste to v úmyslu být nula?

Na druhou stranu nemohu přijít s jediným pádný důvod mít kompilátor inicializovat všechny neinicializované proměnné na 0 ° C.

Odpovězeno 21/05/2011 v 00:11
zdroj uživatelem

hlasů
-1

Odpovědí je instance proměnné lze inicializovat v konstruktoru třídy nebo jakoukoli metodu třídy, ale v případě lokálních proměnných, jakmile jste definovali, co v metodě, která zůstane navždy ve třídě.

Odpovězeno 04/03/2013 v 11:53
zdroj uživatelem

hlasů
-2

Mohl bych si, že z těchto důvodů 2

  1. Protože většina z odpovědí, že tím, že je omezovači inicializaci lokální proměnné, je zajištěno, že lokální proměnné dostane přiřazena hodnota jako programátor chce a zajišťuje očekávané výsledky jsou vypočteny.
  2. Proměnné instance mohou být skryty podle deklarování lokálních proměnných (stejného jména) - s cílem zajistit očekávané chování, lokální proměnné jsou nuceni být initailised hodnotu. (By zcela vyhnout tomuto ačkoli)
Odpovězeno 30/12/2014 v 21:20
zdroj uživatelem

hlasů
0

Instance proměnná bude mít výchozí hodnoty, ale lokální proměnné nemůže mít výchozí hodnoty. Vzhledem k tomu, lokální proměnné v podstatě jsou v metodách / chování, jeho hlavním cílem je udělat nějaké operace nebo výpočty. Z tohoto důvodu, to není dobrý nápad nastavit výchozí hodnoty pro lokální proměnné. V opačném případě je velmi obtížné a časově náročné zjistit příčiny nečekané odpovědi.

Odpovězeno 30/08/2017 v 05:45
zdroj uživatelem

hlasů
1

Myšlenka lokálních proměnných je, že existují pouze v omezeném rozsahu, pro které jsou potřebné. Jako takový by měl být málo důvodů k nejistotě, pokud jde o hodnotu, nebo alespoň, kde tato hodnota pochází. Dokázal jsem si představit mnoho chyb vyplývající z nutnosti výchozí hodnotu pro lokální proměnné.

Zvažte například následující jednoduchý kód ... ( NB předpokládejme pro demonstrační účely, že lokální proměnné jsou přiřazeny výchozí hodnoty, jak je uvedeno, pokud není explicitně inicializovat )

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);

Když je vše řečeno a uděláno, za předpokladu, že kompilátor přiřazena výchozí hodnotu ‚\ 0‘ až letterGrade tento kód jako psaný bude fungovat správně. Avšak to, co kdybychom zapomněli else?

Testovací běh našeho kódu může vést k následujícím

Enter grade
43
Your grade is

Tento výsledek, přičemž lze očekávat, rozhodně nebyl kodéru záměr. Ve skutečnosti, pravděpodobně v drtivé většině případů (nebo alespoň významné množství, z nich), výchozí hodnota by neměla být požadovaná hodnota, tak se v naprosté většině případů je výchozí hodnota bude mít za následek chyby. To dává větší smysl nutit kodér přiřadit počáteční hodnotu lokální proměnné před použitím, protože ladění smutek způsobený zapomenutí = 1v for(int i = 1; i < 10; i++)daleko převáží pohodlí v ne muset zahrnout = 0do hotelu for(int i; i < 10; i++).

Je pravda, že try-catch nakonec bloky mohl dostat trochu chaotický (ale není to vlastně catch-22 jako citát nasvědčuje), když například objekt vyvolá zkontrolovat výjimku v jeho konstruktor, ale po dobu jednoho důvodem či onak, co je třeba udělat, aby tento objekt na konci bloku nakonec. Dokonalým příkladem je případ, kdy se zabývá prostředky, které musí být uzavřeny.

Jeden způsob, jak řešit tento problém v minulosti, může být tak jako ...

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();
}

Nicméně, jak Java 7 je konečně blok již není nutné používat try-s-zdrojů, jako tak.

try (Scanner s = new Scanner(new FileInputStream(new File("filename.txt")))) {
...
...
} catch(IOException e) {
    System.out.println("different error message");
}

To znamená, že (jak název napovídá), to funguje pouze se zdroji.

A zatímco bývalý příkladem je trochu odporný, to snad mluví spíše cestou pokusit-catch nakonec ani tyto třídy jsou implementovány, než se mluví o lokálních proměnných a jak jsou implementovány.

Je pravda, že pole jsou nastaveny na výchozí hodnoty, ale je to trochu jinak. Když říkáte, například int[] arr = new int[10];, jakmile jste se inicializuje toto pole, objekt existuje v paměti v daném místě. Předpokládejme na okamžik, že neexistuje žádné výchozí hodnoty, ale místo toho je počáteční hodnota je taková, jakou sérii 1s a 0s je shodou okolností v tomto paměťovém místě v tomto okamžiku. To by mohlo vést k non-deterministické chování v celé řadě případů.

Předpokládejme, že máme ...

int[] arr = new int[10];
if(arr[0] == 0)
    System.out.println("Same.");
else
    System.out.println("Not same.");

To by bylo docela dobře možné, že Same.by mohly být zobrazeny v jednom běhu a Not same.mohou být zobrazeny v jiném. Problém by mohl být ještě těžké, jakmile začnete mluvit referenční proměnné.

String[] s = new String[5];

Podle definice, každý prvek s směřovat řetězec (nebo je na nule). Nicméně, pokud je počáteční hodnota je taková, jakou série 0 a 1 stane se vyskytují v tomto umístění v paměti, není jen tam žádná záruka, budete získat stejné výsledky pokaždé, ale je tu také žádná záruka, že objekt s [0] bodů k (za předpokladu, že upozorňuje na cokoliv smysluplného) dokonce je String (možná je to králík, p )! Tento nedostatek zájmu o typu by mouše v obličeji skoro všeho, co dělá Java Java. Takže i když má výchozí hodnoty pro místní proměnné lze považovat za volitelná přinejlepším s výchozí hodnoty proměnných instance je blíže k nezbytnosti .

Odpovězeno 12/09/2018 v 02:49
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more