[???] /
[Java FAQ] / [S012]
S012: 変数/引数
[S012 Q-01]
変数の初期状態はどのようになっていますか?
変数を定義したときに、デフォルトではどういう値が入っているのですか?
[S012 A-01]
変数は型に応じてデフォルト値に初期化されます。また、初期値を指定すると、
その値に初期化されます。
型とデフォルト値との対応は次の表のようになります。
型 デフォルト値
byte (byte) 0
short (short) 0
int 0
long 0L
float 0.0f
double 0.0d
char '\u0000'
boolean false
reference type null
なお、ローカル変数の場合は、参照する前に初期化、あるいは、代入しなければ
なりません。
参考記事 [JavaHouse-Brewers:3293]
[S012 Q-02]
ローカル変数を初期化しているのに、コンパイルエラーがでますがどうしたら良
いのですか?
[S012 A-02]
あらゆるケースで必ず初期化されるようにプログラムを修正しましょう。
ローカル変数は使用する前に必ず初期化されていなければなりません。
実行時の条件分岐や例外発生によってローカル変数が初期化されない場合があるときには、
コンパイルエラーになります。
たとえば、次のようなプログラムがあったとします。
public void foo(boolean flag) {
int a;
if (flag) {
a = 1;
}
System.out.println(a);
}
これはコンパイルエラーになります。
System.out.println(a); を実行する前に if 文の中を通らないケースもあり、
そのとき a が初期化されないためです。次のように a が必ず初期化されるように
変更すればコンパイルエラーになりません。
public void foo(boolean flag) {
int a;
if (flag) {
a = 1;
} else {
a = 0;
}
System.out.println(a);
}
参考記事 [JavaHouse-Brewers:18741]
[S012 Q-03]
スーパークラスのもつ変数と同名の変数をサブクラスで宣言するとどうなりますか?
[S012 A-03]
サブクラスにも同名の別の変数が作られます。
サブクラス内でその名前を修飾なしに使うと、サブクラス内で
宣言された変数にアクセスすることになります。これを隠蔽(hide)といいます。
サブクラス内でスーパークラスの
変数にアクセスしたい場合は、((スーパークラス名)this)で限定 (qualified) し
ます。また、相対的に一段上のクラスからアクセスできる変数を参照する場合は、
キーワード super を使用することもできます。
次のようなサンプルプログラムを動かして確認をしてみましょう。
class Parent {
String data = "parent's data";
}
class Child extends Parent {
String data = "child's data";
}
class Grandchild extends Child {
String data = "Grandchild's data";
void print1() { System.out.println(data); }
void print2() { System.out.println(((Child)this).data); }
void print3() { System.out.println(((Parent)this).data); }
void print4() { System.out.println(super.data); }
}
public class Test {
public static void main(String[] args) {
Grandchild g = new Grandchild();
g.print1();
g.print2();
g.print3();
g.print4();
}
}
出力結果は以下のようになります。
Grandchild's data
child's data
parent's data
child's data
参考記事 [JavaHouse-Brewers:11403]
[S012 Q-04]
null とはいったい何ですか?
[S012 A-04]
無を意味する特別な値です。
任意の参照型の変数に代入できる値で、「どのオブジェクトも参照しない」
ことを示します。無は 0 を意味するものではありません。
参考記事 [JavaHouse-Brewers:8707]
[S012 Q-05]
クラス変数を変更してクラスを再コンパイルしましたが、他のクラスから参照しても
その修正が反映されていません。
[S012 A-05]
static final で変数が定義されていませんか?
このような場合、使用しているクラスがコンパイルされたときに、変数の値が
埋め込まれています。そのため、定義しているクラスだけを再コンパイルしても、
使用しているクラスには反映されません。
下記のような場合、Bar クラスの変数 bar への代入の箇所にはFoo.CONSTANT への
参照ではなくその値 "foo" が埋め込まれています。そのため、Foo.CONSTANT の値のみ
変更して再コンパイルすると、その変更が Bar クラスに反映されないということが
起ります。
class Foo {
public static final String CONSTANT = "foo";
}
class Bar {
private String bar = Foo.CONSTANT;
}
これを防ぐには、Foo を変更したときに Bar も一緒にコンパイルし直す
必要があります。
参考記事 [JavaHouse-Brewers:16097]
[S012 Q-06]
final な変数に代入したオブジェクトの内容が変更できてしまうのですが?
[S012 A-06]
変数が参照型の場合、finalで宣言されていても、
変数が示すオブジェクトの内容は変更することができます。
参考記事 [JavaHouse-Brewers:8056]
[S012 Q-07]
final で修飾されている変数を宣言時に初期化しなくてもいいのですか?
[S012 A-07]
final変数は代入が1回しか行われないことが保証されていれば、初期化する必要はありません。
代入を行う箇所は以下のようになります。
1. static メンバの場合、変数を宣言した場所または静的初期化子
2. インスタンスメンバの場合、変数を宣言した場所または各コンストラクタ
3. ローカル変数の場合、スコープ内の任意の場所
変数を宣言した場所で代入を行わない場合の final を blank final といいます。
参考記事 [JavaHouse-Brewers:9544]
[S012 Q-08]
どのような場合に static 変数を使用すれば良いのでしょうか?
[S012 A-08]
設計によって異なります。
static変数、すなわちクラス変数は、インスタンスではなく、クラスにおける
値を保持するために使われます。static変数は、その性質に適合する場面で
使用することになるでしょう。
例えば、次のような場面で使われます。
1. 定数
final static String CONSTANT = "foo";
2. ただ1つのインスタンスのみを生成する場合
public class Singleton {
private static Singleton theSingleton;
private Singleton() {}
public static Singleton() {
if (theSingleton == null) {
theSingleton = new Singleton();
}
return theSingleton;
}
}
3. データの共有
class Sleepy {
static boolean state;
}
class CareManager {
void setState(boolean b) {
Sleepy.state = b;
}
}
class Watcher {
boolean isState() {
return Sleepy.state;
}
}
参考記事 [JavaHouse-Brewers:9034] [JavaHouse-Brewers:9016]
[S012 Q-09]
Javaには「可変長引数」は、ありますか?
[S012 A-09]
ありません。
大抵のケースは設計を見直すことで可変長引数を使わない形に変更できるはずです。
どうしても可変長引数が必要かどうかもう一度考えてみましょう。
参考記事 [JavaHouse-Brewers:16188]
[S012 Q-10]
ローカル変数や、引数の数は制限されているのですか?
[S012 A-10]
Java Virtual Machine Specification により、制限されています。
具体的には次のように定められています。
ローカル変数は、 1メソッド内で 65535個が上限です。
ただし、double と long はそれぞれ 2個分として計算します。
メソッド引数は 1メソッド内で 255個が上限です。
double と long の引数はそれぞれ 2個分として計算します。
それ以外の型の引数は 1個として計算します。
実装上の制限については、Java 仮想マシン仕様を参照してください。
URL http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html
参考記事 [JavaHouse-Brewers:14117]
[S012 Q-11]
コンストラクタ等で「this.foo = foo」と書くとき引数名を間違え
るとどうなりますか?
[S012 A-11]
例えば、
public class Foo {
private String bar;
public Foo(String bar) {
this.bar = bar;
}
}
というクラスにおいて、上記のようにすべきところを、3行目を
public Foo(String baz) {
のように書き間違えた場合、4行目の "this.bar" と "bar" はともに
Foo クラスのメンバ(フィールド)の "private String bar" を
指し示すことになります。
つまり "this.bar = this.bar" と記述したときと同じ動きになります。
そのためコンパイルエラーにはなりませんが、bar が null のままになるため
どこか別の場所で NullPointerException が発生する可能性が高くなります。
参考記事 [JavaHouse-Brewers:19444]
[S012 Q-12]
変数とは何でしょうか?
[S012 A-12]
Java において、変数とは次のどちらかです。
1.(primitive 型) primitive 型の値を保持するもの。
2.(参照型) オブジェクトを指し示すか、または null であるもの。
primitive 型は、Java では boolean, char, byte, short, int, long, float,
double, void の 9種類あります。このうち void はメソッドにおいて返り値が
ないことを示す特殊な型で、この型の値を変数に代入することはできません。
参照型の場合、ある変数がどのオブジェクトを指し示すかは、
その変数へ代入を行うことにより変更できます。
null を代入するとその変数は「どのオブジェクトをも指していない」
状態となります。
あるオブジェクトがプログラム中のどの変数からも指し示されなくなった場合、
そのオブジェクトは 2度とプログラムから使用することはできず、
いずれそのオブジェクトは Java VM によりガベージコレクションされます。
S012-01、S012-4も参照してください。
参考記事 [JavaHouse-Brewers:8729]
[S012 Q-13]
ローカルブロックでの変数宣言はその外側の変数を隠さないのでしょうか?
[S012 A-13]
より内側のスコープに変数宣言を行った場合、
・クラスのフィールドである変数は隠す
・ローカル変数は隠さない(エラーとなる)
となります。
具体的には、以下のようになります。
class Foo {
private int i;
void bar1() {
int i = 1; // クラスメンバの"private int i"を隠す
}
void bar2() {
{
int i = 2; // クラスメンバの"private int i"を隠す
}
int i = 3; // クラスメンバの"private int i"を隠す
// 上記の "int i = 2" は外側のスコープにならないので ok
}
void bar3() {
int i = 4;
{
int i = 5; // エラー "int i = 4" が外側のスコープにある
}
}
void bar4(int i) {
int i = 6; // エラー メソッドの引数の"int i" が外側のスコープにある
}
}
参考記事 [JavaHouse-Brewers:5108]
[S012 Q-14]
変数はオーバーライドされないのでしょうか?
[S012 A-14]
Java では変数のオーバーライド機能はありません。サブクラスで同名の変数を
定義した場合、サブクラスの変数はスーパークラスの同名の変数を隠蔽(hide)し
ます。
class Parent {
String data = "parent's data";
}
class Child extends Parent {
String data = "child's data";
}
となっている場合、Child クラスのインスタンスには Parent クラスの data と
Child クラスの data の両方のインスタンスがメンバとして存在することになり
ます。
Parent クラス内から Child の data にアクセスすることはできませんし、
Child クラス内から Parent の data へも(Child に同名の変数があるため)
直接アクセスすることはできません。
参考記事 [JavaHouse-Brewers:5108]
[S012 Q-15]
変数はオーバーライドされないのでしょうか?
[S012 A-15]
S012-03を参照してください。
[S012 Q-16]
JDK 1.1 で引数を final にできるようになったのは何のためですか?
[S012 A-16]
メソッド引数やローカル変数で final を使用すると、
その変数をメソッド内に記述された inner class(匿名クラス)内でも
使用することができます。
Button button = new Button("How are you?");
final TextField textfield = new TextField();
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
textfield.setText("I'm fine!");
}
});
ここで textfield を final とする理由は、
inner class(ここでは ActionListener を継承した匿名クラス)
が受け取った textfield の参照先と outer class の textfield の
参照先が一致しなくなってしまう可能性があるためです。
参考記事 [JavaHouse-Brewers:9568]
[S012 Q-17]
JDK 1.1で導入された「blank final」とはなんですか?
[S012 A-17]
S012-07を参照してください。
[S012 Q-18]
blank final(代入のないfinal変数宣言)がJDK 1.1で必要になった理由はなんですか?
[S012 A-18]
JDK 1.1 で inner class(内部クラス)が導入されました。
inner classでは外側のクラス(outer class)のインスタンスへの参照を
final で持たなければいけないのですが、
このインスタンスはコンストラクタで渡されてくるため、
blank final の構文を使用しないとできません。
参考記事 [JavaHouse-Brewers:9551]
[S012 Q-19]
static 変数を不用意に用いたアプレットを複数個同じページに張付けると大変
なことになると聞きましたが?
[S012 A-19]
ブラウザで複数のアプレットを走らせた場合、それらは同一の VM 内で走るため、
static 変数を不用意に使用すると、複数のアプレット間で同じクラスを
使用している場合に変数が共有されてしまい、予期しない動作をすることがあり
ます。
参考記事 [JavaHouse-Brewers:8988]
[S012 Q-20]
引数で変数渡しをしたいのですが?
[S012 A-20]
Java では primitive 型以外の変数はオブジェクトへの参照を表すので、
その中身を書換えることができます。たとえば、
void move(Point point) {
point.x = point.x + 10;
point.y = point.y + 10;
}
というメソッドを記述し、
Point p = new Point(100, 100);
move(p);
を実行すれば、実行後の p は、
p.x = 110
p.y = 110
となります。
参考記事 [JavaHouse-Brewers:5202]
[S012 Q-21]
引数は参照渡しではなく値渡しですか?
[S012 A-21]
厳密に言えば、参照渡しとは言えません。
S012-20 の例において、
move(p) を実行したとき、 move(p) の "p" と、move メソッドの引数
"point" は同一の Point インスタンスを参照しますが、
"point" が "p" という変数を参照して、
その参照先の中身(である Point インスタンス)を書換えるわけではないからです。
Java では変数への参照はなく、上記例では p という変数の参照先を示す
"値" が move メソッドの point 引数にコピーされると考えられます。
なお、primitive 型の場合は常に値渡しが行われます。
参考記事 [JavaHouse-Brewers:15208]
[S012 Q-22]
swap(int, int) を作りたいのですが?
[S012 A-22]
S012-21にもあるように、Java ではメソッドの引数が呼出し元の
パラメータを参照することはできません。そのため、C++ であるような、
swap(int& n, int &m) {
int tmp = n;
n = m;
m = tmp;
}
といったことはそのままでは実現できません。
次のいずれかの方法を取ります。
1. ラッパクラスを作成する
class IntegerWrapper {
int value;
}
としておいて、
void swap(IntegerWrapper n, IntegerWrapper m) {
int tmp = n.value;
n.value = m.value;
m.value = tmp;
}
する。
2. 一時的な配列を使用する。
void swap(int[] n, int[] m) {
int tmp = n[0];
n[0] = m[0];
m[0] = tmp;
}
3. メソッドにしない
そもそもこのような簡単な機能をわざわざメソッドにする必要はなく、
int tmp = n;
n = m;
m = tmp;
で済んでしまいます。
設計しているオブジェクトやメソッドに本来どのような機能が必要なのか
よく考えた方がいいでしょう。
参考記事 [JavaHouse-Brewers:3705]
contributor: markn
コメントの送り先:Java FAQ BBS