Java入門内検索
Javaトップ
0. はじめに
1. Javaの基礎
1. コンパイル方法・実行方法
2. 予約語
3. 構成
4. データの型と宣言
5. 定数と変数
6. 簡単な演算
7. ディスプレイへの表示
8. 文字列
2. 制御文
1. 順次構造
2. 単一分岐
3. 多重分岐
4. ケース構造
5. 反復構造(while)
6. 反復構造(do)
7. 反復構造(for)
8. 補助制御文
9. 練習問題
10.練習問題
11.多重ループの抜け方
3. 配列
1. 配列とは
2. 配列の宣言と初期化
3. 配列の使用法
4. 練習問題
5. 2次配列と多次元配列
6. 練習問題
4. クラスの基礎
1. クラスとは
2. クラスの作成
3. コンストラクタ
4. 例題
5. 演算子==とequalsメソッド
5. 演算子
1. 演算子の種類
2. インクリメント演算子とデクリメント演算子
3. 複合代入演算子
4. ビット演算子
5. シフト演算子
6. キャスト演算子
7. 順次演算子
8. 条件演算子(三項演算子)
9. instanceof演算子
10.演算子の優先順位
11.式と値
12.条件式と値
13.練習問題
6. オブジェクト指向とは
1. オブジェクト指向とクラス
2. 継承
3. カプセル化
4. ポリモーフィズム
7. クラス
1. thisキーワード
2. public, protected, private
3. 静的変数・静的メソッド・静的ブロック
4. ローカル変数
5. メソッドの引数
6. null
7. 練習問題
8. インナークラス(内部クラス)
9. 無名インナークラス(無名内部クラス)
10. クラスの包含
11. 練習問題
8. 継承
1. 継承とは
2. スーパークラスのコンストラクタ
3. 継承とキャスト
4. スコープ
5. クラスの作成
6. abstract
7. 例題)お絵かきソフト
8. 継承と包含
9. インターフェース
1. インターフェースとは
2. インターフェースの宣言
3. インターフェースの参照
4. インターフェースの修飾子
5. インターフェースの継承
6. インターフェースとinstanceof演算子
10. パッケージ
1. パッケージとは
2. パッケージの宣言
3. クラスパス
4. import
11. 例外
1. 例外とは
2. 例外が起きたら
3. 例外の種類
4. catchブロックの検索
5. throw
6. throws
7. エラーと例外
8. 独自の例外
12. マルチスレッド
1. スレッドとは
2. スレッドのライフサイクル
3. スレッドの作成(Threadクラス)
4. Threadクラスのメソッド
5. スレッドの作成(Runnableインターフェース)
6. 同期
7. デッドロック
8. スレッド間の通信
13. ガーベージコレクター
1. ガーベージコレクターとは
2. ガーベージコレクターの実行
3. finalize
14. 有用な(?)クラス群
1. Math
2. Random
3. System
4. Vector
5. Stack
6. Hashtable
7. Enumeration
8. String
9. StringTokenizer
10. Object
11. Number
12. Byte
13. Short
14. Integer
15. Long
16. Float
17. Double
18. BigInteger
19. BigDecimal
20. Character
21. Date
22. Calendar
15. 入出力
1. ファイルとディレクトリ
2. ストリームの種類
3. 文字ストリーム
4. Reader
5. CharArrayReader
6. StringReader
7. InputStreamReader
8. FileReader
9. BufferedReader
10. Writer
11. CharArrayWriter
12. StringWriter
13. OutputStreamWriter
14. FileWriter
15. PrintWriter
16. BufferedWriter
17. バイトストリーム
18. InputStream
19. ByteArrayInputStream
20. FileInputStream
21. FilterInputStream
22. BufferedInputStream
23. DataInputStream
24. OutputStream
25. ByteArrayOutputStream
26. FileOutputStream
27. FilterOutputStream
28. BufferedOutputStream
29. PrintStream
30. DataOutputStream
31. ランダムアクセスファイル
32. スレッド間通信 PipedInputStream PipedOutputStream
33. クラスの保存 ObjectInputStream ObjectOutputStream Serializable
34. StreamTokenizer
35. 標準入出力 キーボードからの入力(標準入力) 標準出力 標準エラー出力
36. 練習問題
16. ネットワーク
1. インターネットアドレス
2. TCP/IP通信
3. UDP通信
4. URL
5. 練習問題
6. マルチキャスト
7. RMI
17. 中間試験
1. 問題:Webサーバーを作りなさい
2. HTTPプロトコル
3. クラス設計
4. 解答例
18. AWT
1. AWTとは
2. Component
3. Container
4. Frame
5. Button
6. レイアウト
7. BorderLayout
8. FlowLayout
9. GridLayout
10. CardLayout
11. nullレイアウト
12. イベントとイベントリスナー
13. ActionListener
14. Panel
15. Label
16. CheckBoxとItemListener
17. CheckBoxGroup
18. Choice
19. List
20. TextFieldとTextListener
21. TextArea
22. ScrollbarとAdjastmentListener
23. Canvas
24. 練習問題(計算機)
25. Point
26. Dimension
27. Color
28. Font
29. Graphics
30. 練習問題
31. 練習問題
32. ダブルバッファ
33. イベント その2 ActionEvent ItemEvent AdjustmentEvent
34. KeyListenerとKeyEvent InputEvent
35. MouseListenerとMouseEvent
36. MouseMotionListener
37. FocusListenerとFosusEvent
38. WindowListenerとWindowEvent
39. ComponentListenerとComponentEvent
40. ContainerListenerとContainerEvent
41. アダプタクラスとインナークラス
42. 無名インナークラス
43. 練習問題 お絵かき
44. WindowとDialog、FileDialog
45. メニュー
46. ポップアップメニュー
47. ショートカット
48. カーソル
19. Java Applet
1. Java Appletとは
2. 最初のJava Applet
3. Appletクラス
4. Appletクラスのライフサイクル
5. AppletContext
6. イメージ
7. パラメーター
8. 練習問題 スライドショー
9. Jarファイル
10. 練習問題 時計
20. その他のチップス
1. Javaから別のプログラムを実行させる
2. StringBuffer
3. 環境変数の取得
4. 基数表記
5. インスタンスのコピー
6. 値渡しと参照渡し
7. クラスオブジェクトの比較
21. 修了試験 ↓これより下は工事中です。
1. 考え中
2. 考え中
3.
4.
22. そしてその後は・・・
1. ヒューマンアカデミー Javaプログラマー本科
2. Appli-Style.com iアプリ作成に挑戦
3. SJC-A 受験チケット申込
このページの先頭に戻る
トップページに戻る
このページの先頭に戻る
トップページに戻る
|
前ページへ :
トップへ :
次ページへ
- 5. インスタンスのコピー
-
Java入門も終盤ですが、オブジェクトのコピーってどうやりますか?って聞かれたらどう答えますか?
こう答えた人は、申し訳ないですが4章くらいから読み直してください。=演算子はオブジェクトの中身をコピーする演算子ではなく、その参照先をコピーするだけです。obj1の中身は例えばメモリーアドレス2000番地にあるとします。obj1は2000番地というアドレスが入っています。上の式を実行するとobj2に2000番地というのがコピーされるだけで、中身自体は同じ物をさしており、コピーしていることにはなりません。obj1の中身が書き換われば、obj2の中身も書き換わってしまいます。
class MyPoint{
private int x;
private int y;
MyPoint(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "("+x+", "+y+")";
}
void setPoint(int x, int y){
this.x = x;
this.y = y;
}
}
public class Test{
public static void main(String[] args){
MyPoint obj1, obj2;
obj1 = new MyPoint(100, 200);
obj2 = obj1;
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
obj1.setPoint(50, 22);
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
}
}
|
C:\java>java Test
obj1:(100, 200)
obj2:(100, 200)
obj1:(50, 22)
obj2:(50, 22)
C:\java>
|
このようにobj1を書き換えても、obj2も書き換わっています。つまりobj1もobj2も同じオブジェクトを見ているのです。
Objectクラスにはcloneというオブジェクトをコピーするメソッドがあることは説明済みです。これを使うのが正解です。でも、MyPointクラスでcloneメソッドをオーバーライドしていないのにうまく行くのでしょうか?とりあえずやってみます。
public class Test{
public static void main(String[] args){
MyPoint obj1, obj2;
obj1 = new MyPoint(100, 200);
obj2 = (MyPoint)obj1.clone();
}
}
|
clone()メソッドの戻りの型はObject型なので代入するときはキャストを行います。しかしこれはコンパイルするときはエラーが出てしまいます。cloneメソッドがprotectedメソッドであるため、外部からは呼べないのです。
ではMyPointクラスにcloneメソッドをpublicとしてオーバーライドしましょう。
class MyPoint{
private int x;
private int y;
MyPoint(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "("+x+", "+y+")";
}
public Object clone(){
try{
return super.clone();
}catch(CloneNotSupportedException e){
throw new InternalError(e.toString());
}
}
void setPoint(int x, int y){
this.x = x;
this.y = y;
}
}
public class Test{
;
省略
;
}
|
cloneメソッドはCloneNotSupportedExceptionが投げられるためcatchする必要があります。上記のプログラムはコンパイルは可能ですが実行するときにエラーが出ます。
これはMyPointクラスがcloneメソッドをサポートしているかJavaにはわからないからです。そこでClobeableインターフェースを実装します。
class MyPoint implements Cloneable{
private int x;
private int y;
MyPoint(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "("+x+", "+y+")";
}
public Object clone(){
try{
return super.clone();
}catch(CloneNotSupportedException e){
throw new InternalError(e.toString());
}
}
void setPoint(int x, int y){
this.x = x;
this.y = y;
}
}
public class Test{
public static void main(String[] args){
MyPoint obj1, obj2;
obj1 = new MyPoint(100, 200);
obj2 = (MyPoint)obj1.clone();
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
obj1.setPoint(50, 22);
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
}
}
|
C:\java>java Test
obj1:(100, 200)
obj2:(100, 200)
obj1:(50, 22)
obj2:(100, 200)
C:\java>
|
cloneメソッド内では基底クラスのcloneメソッドを呼んでいるだけなのに、きちんと変数をコピーしてくれています。
自分でcloneの中身をすべての変数きちんとコピーするようなメソッドを定義しなくても基底クラスを呼んだだけできちんとコピーしてくれるなら、こんな面倒なことさせなくても、最初からcloneメソッドを定義しておいてくれよ〜。という感じがしませんか?実は上記の例のようにクラスの中身がプリミティブな変数だけならば良いんですけど、下の例のようにクラスオブジェクトがフィールド(メンバー変数)としてもっているとうまく行かない場合があるんです。
class MyPoint implements Cloneable{
public int x;
public int y;
MyPoint(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "("+x+", "+y+")";
}
public Object clone(){
try{
return super.clone();
}catch(CloneNotSupportedException e){
throw new InternalError(e.toString());
}
}
}
class MyLine implements Cloneable{
public MyPoint pnt1;
public MyPoint pnt2;
MyLine(MyPoint p1, MyPoint p2){
pnt1 = p1;
pnt2 = p2;
}
public Object clone(){
try{
return super.clone();
}catch(CloneNotSupportedException e){
throw new InternalError(e.toString());
}
}
public String toString(){
return pnt1.toString() + "-" + pnt2.toString();
}
public void setLine(MyPoint p1, MyPoint p2){
pnt1 = p1;
pnt2 = p2;
}
}
public class Test{
public static void main(String[] args){
MyLine obj1, obj2;
obj1 = new MyLine(new MyPoint(0,0), new MyPoint(100,100));
obj2 = (MyLine)obj1.clone();
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
obj1.pnt1.x = -50;
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
}
}
|
C:\java>java Test
obj1:(0, 0)-(100, 100)
obj2:(0, 0)-(100, 100)
obj1:(-50, 0)-(100, 100)
obj2:(-50, 0)-(100, 100)
C:\java>
|
MyLineがコピーされるとき、MyPointの参照をコピーしているだけで、MyPointのオブジェクトその物をコピーしているのではないんです。このようにネストされたクラスをきちんとコピーできるかはプログラマしかわからないのでプログラマが明示的にcloneメソッドがきちんと動作するかを示さなければいけないのです。
上記の例の場合はきちんとcloneメソッドが動作するように基底クラスを呼び出すだけでなく、中身を実装する必要があります。
class MyPoint implements Cloneable{
public int x;
public int y;
MyPoint(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "("+x+", "+y+")";
}
public Object clone(){
try{
return super.clone();
}catch(CloneNotSupportedException e){
throw new InternalError(e.toString());
}
}
}
class MyLine implements Cloneable{
public MyPoint pnt1;
public MyPoint pnt2;
MyLine(MyPoint p1, MyPoint p2){
pnt1 = p1;
pnt2 = p2;
}
public Object clone(){
try{
return new MyLine((MyPoint)pnt1.clone(), (MyPoint)pnt2.clone());
}catch(Exception e){
throw new InternalError(e.toString());
}
}
public String toString(){
return pnt1.toString() + "-" + pnt2.toString();
}
public void setLine(MyPoint p1, MyPoint p2){
pnt1 = p1;
pnt2 = p2;
}
}
public class Test{
public static void main(String[] args){
MyLine obj1, obj2;
obj1 = new MyLine(new MyPoint(0,0), new MyPoint(100,100));
obj2 = (MyLine)obj1.clone();
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
obj1.pnt1.x = -50;
System.out.println("obj1:" + obj1);
System.out.println("obj2:" + obj2);
}
}
|
C:\java>java Test
obj1:(0, 0)-(100, 100)
obj2:(0, 0)-(100, 100)
obj1:(-50, 0)-(100, 100)
obj2:(0, 0)-(100, 100)
C:\java>
|
このようにクラスのフィールドがプリミティブ型でない場合は、基底クラスのcloneメソッドを呼び出すだけでなく、プログラマがきちんと明示する必要があります。
今回のMyPointクラスのようにそれが自分で作ったクラスの場合は良いのですが、Javaが標準で用意しているクラスの場合は、そのクラスがcloneメソッドをサポートしているかを調べる必要があります。
また、配列やVectorクラスのような場合は、その中身が何なのかを考え、参照がコピーされるだけでよいのか、その中身もきちんとコピーする必要があるのかを見極める必要があります。
cloneメソッドの実装をするクラスの場合、cloneメソッドの中身で様々なコピーをするのではなく、自分と同じ方の引数をとってその中身をコピーするコピーコンストラクタを作っておくと便利です。
なお、配列のコピーはcloneではなく、Systemクラスのarraycopyメソッドも使用できます。
前ページへ :
トップへ :
次ページへ
|