第11回 Javaのクラスの理解を深める
平井玄
2003/11/26
|
今回の内容
|
String型はJavaの言語仕様そのものには含まれませんが、Javaのプログラミングに欠かせない最も基本的なクラスの1つです。これまでの連載でも、文字列を格納するための変数の型として何度となく登場しました。
前回「Javaのクラスをグループ化するパッケージ」も少しふれましたが、String型は文字列を扱うためのクラスとして、そのソースコードがJavaに付属しています。今回はString型クラスのソースを読みながら、これまで紹介していなかったJavaの言語仕様についてまとめて説明します。
その前にまず、String型について確認しておきましょう。String型の役割は、リードオンリーの文字列を扱うこと(後で書き換えたい場合はStringBuffer型を使う)です。Javaのちょっと面白い特徴として、単なる文字列もString型のオブジェクトとして扱われることがあります。したがって、C/C++を経験したプログラマには違和感のある以下のようなコードを書くことができます。
| 文字列の長さを表示する |
public class ShowStringLength { |
C:\>javac ShowStringLength.java |
このコードでは、「"文字列"」の部分がString型のオブジェクトとして扱われるため、変数ではないにも関わらずString型のメソッドlength()が使えるのです。
ここで、String型のソースがどのようになっているのか、想像してみましょう。Javaの文字列がString型のオブジェクトとして扱われるといっても、コンピュータの中ではあるメモリ領域に格納されるはずです。メモリを直接扱うのはプリミティブ型ですから、String型のソースには文字列を実際に格納するための変数が定義されているはずです。もちろん、メモリをどう扱うのかをプログラマがあまり意識しなくてもよいのがJavaの長所ですので、こうした変数はクラス内部の作業用としてprivateに指定されているはずです。また、String型のメンバであるlength()や第8回のHTMLパーサを実現するために使ったcharAt()などのメソッドは、privateに指定されたプリミティブ型の変数に直接アクセスしているはずです。では、String型のソースがどうなっているのか、実際に見ていきましょう。
| 配列 |
前回も少しだけふれましたが、String型で実際に文字列を格納しているのはvalueという名前の変数です。
| String.javaの93行目〜 |
/** The value is used for character storage. */ |
予想通りvalue はprivateに指定されていますので、プログラマが直接には操作できません。ただ、最後に付いている「[ ]」という記号は何を意味するのでしょう。
「[ ]」は配列を表す記号です。配列とは、ある型の変数がメモリ上で連続していることです。配列型の変数valueは、連続したメモリの先頭アドレスを参照するために使います。ただし、配列型はクラス型と同様に参照型の変数ですので、配列型の変数そのものはメモリの先頭アドレスを格納するために使うものですので、配列は別に生成しなければなりません。
char value[] = new char[3]; |
この場合、char型の変数を3個格納するのに必要なメモリ領域が確保され、その先頭アドレスが変数valueに格納されます。配列の要素にアクセスするには以下のように添え字を使います。
char value[] = new char[3]; |
1行目では、char型3個分のメモリ領域が確保され、その先頭アドレスがvalueに格納されます。2行目では、確保した1番目の部分に「文」という文字を書き込んでいます。配列の添え字は1番目が0、2番目は1というように0から始まることに注意してください。
0から始まることに違和感を覚える方は、次のように考えるとよいでしょう。まず、char型のサイズは2バイトですので、「new char[3]」という文で2バイト×3個=6バイトのメモリ領域が確保される*ことになります。同じ参照型であるクラスの場合、クラスのメンバ変数は同じ型とは限りません。しかし配列は同じ型の変数が連続したものですので、先頭アドレスと何番目なのかが分かれば、目的の要素にアクセスできます。したがってn番目の要素のアドレスは、配列の先頭アドレス+(n×配列の型のサイズ)です。この例の場合、「文」というデータが格納されているのは「配列の先頭アドレス+(0×2バイト)」、つまり先頭アドレス+0バイトです。同様に「字」は先頭アドレス+2バイト、「列」は先頭アドレス+4バイトです。つまり、クラスのメンバ変数と同様、配列の要素も先頭アドレスからの相対値でアクセスしているわけです。
冒頭でふれたchatAt()も、配列に添え字を使ってアクセスして値を取り出しています。
| String.javaの444行目〜 |
public char charAt(int index) { |
注:実際に確保されるメモリ領域のサイズはJavaの仮想マシンを実行するCPUによって異なる。
なお、lengthという配列型のメンバ変数を使うと、配列型のオブジェクトが保持している要素の数が分かります。
| 文字列の長さを表示する |
public class ShowStringLength2 { |
C:\>javac ShowStringLength2.java |
ちなみに、配列型の変数の宣言方法は2つあります。1つはすでに説明したように、
型名 変数名[];(例:char value[]) |
というスタイルです。もう1つは
|
|
というスタイルです。後者の方がJavaの正式の書き方なのですが、C/C++プログラマには前者の方がなじみがあるでしょう。String.javaでは後者のJavaの正式の記法ではなく、C/C++流の書き方が使われています。しかし、以下のような文もあります。
| String.javaの545行目 |
char[] val = value; /* avoid getfield opcode */
|
本来、少なくとも同じソースファイルの中では書き方を統一すべきなのですが、String.javaではなぜか両方の書き方が混在しています。
| thisとsuper |
String型にはいくつかのコンストラクタが用意されています。配列がどのように生成されているのか、そのうちの1つを見てみましょう。
| String.javaの142行目〜 |
public String(String original) { |
このコンストラクタは、String型のオブジェクトに新しい文字列を代入するときに呼び出されます。すでに文字列が割り当てられている場合は、新しい文字列に置き換えられます。
最初のif文は新旧の文字列の長さを比較するものです。新旧の文字列の長さが違う場合は、配列を新たに生成しています。ただ、「this」という見慣れないキーワードがあります。
this.count = original.count;
|
thisは、オブジェクト自身を表す特殊な変数です。たとえば、String型のオブジェクトを以下のように生成したとしましょう。
String s = "文字列"; |
このとき、String型オブジェクト内のthisは、変数sと同じく「"文字列"」というString型オブジェクトの先頭アドレスを表しています。つまりthisという変数は、クラスがオブジェクトとして生成されたときに、オブジェクト内部から自分自身の先頭アドレスにアクセスするために使うのです。したがって、「original.value.length
> this.count」は、新しい文字列を格納している配列の長さと、現在の文字列の長さを比較している条件式です。
新旧の文字列の長さが異なる場合は、新しい文字列の長さに合わせてchar型の配列を生成しています。一方、新旧の文字列の長さが同じ場合、新しい文字列を格納しているメモリの先頭アドレスを代入することで、無駄を省いています。
} else { |
valueはprivateに指定されていますが、String型同士なのでアクセスできます。
なお、thisの仲間にsuperという変数があります。thisが自分自身を表すのに対して、superは自身のスーパークラスを表します。
thisとsuperは、サブクラスからスーパークラスの同名メソッドを呼び出すときにも使えます。
| thisとsuper |
class ClassA { |
C:\>javac SampleForThisAndSuper.java |
上記の例では、ClassAで定義されたメソッドWhoAreYou()と同名のメソッドを、ClassAを継承したClassBで再定義(=オーバーライド)しています。ClassBのWhoAreYou()内でClassAのWhoAreYou()を呼び出すために、変数superを使っています。
| finalとstatic |
クラスを使うことのメリットとして、あるクラスを継承して別のクラスを作れることがあります。元のクラスと異なる部分だけを定義すればよく、同じ機能をゼロから作るよりも作業の手間が省けるとされています。しかし、プログラマはString型を継承して、新たなクラスを作ることはできません。なぜなら、String型にはfinalという修飾子が付いているからです。
| String.javaの90行目〜 |
|
クラスの宣言にfinalを付けると、そのクラスは他のクラスのスーパークラスになれなくなります。つまり、継承できなくなるのです。なお、finalは変数の宣言に付けることもできます。
final double pi = 3.1415926; |
クラスにfinalを付けると継承して機能を変更できなくなりますが、変数の宣言にfinalを付けると後から値を変更できなくなります。したがってfinal付きの変数は宣言時に値を代入して初期化しておきます。final付きの変数は一種の定数になるのです。
finalはクラスか変数に付く修飾子ですが、変数とメソッドに付くstaticという修飾子もあります。
| String.javaの2234行目〜 |
public static String valueOf(boolean b) { |
staticとして宣言されたメソッドは、インスタンスを生成しなくても利用できます。クラスはそれ自体がオブジェクトでもあるので、staticの付いたメソッドはクラスそのものに所属するメソッドです。もちろん、それぞれのインスタンスからも利用できます。
| staticメソッド |
public class SampleForStaticMethod { |
C:\>javac SampleForStaticMethod.java |
上記の例では、String型のオブジェクトを生成せずにvalueOf()というString型のメソッドを使っています。valueOf()がstaticメソッドなので、オブジェクトを生成しなくても使えるのです。
一方、メンバ変数にstaticを付けると、その変数はクラスに所属する変数になり、やはりオブジェクトを生成しなくても使えます。別の言い方をすれば、あるクラスのインスタンスで共通して使える変数が実現できるのです。
| static変数 |
class ClassWithStaticVariable { |
C:\>javac SampleForStaticVariable.java |
上記の例では、ClassWithStaticVariable内のstatic変数countを別々のやり方で加算していますが、すべて同じ変数にアクセスしているので計算結果は111になります。
String型のソースは全体で2000行以上もある膨大なものですが、プログラムの内容はそれほど複雑ではありません。これまで説明した変数の型や制御構造についての知識と、今回紹介した配列やthisとsuper、finalとstaticについての知識があれば、何をしているのかおおよそは読み取れるはずです。しかし、いくつかの文はまだ説明していないJavaの機能を使っています。次回は「例外」という機能について紹介します。
いまから始めるJava バックナンバー
- 第1回 Java2 SDKで学習の準備
- 第2回 Javaの変数の本質を知る
- 第3回 クラスを簡単に理解しよう
- 第4回 クラスの継承の本質を知る
- 第5回 メソッドとコンストラクタはなぜ必要?
- 第6回 Javaプログラムの制御構造を理解する
- 第7回 Javaの制御文を使いこなそう
- 第8回 メソッドの挙動を変えるオーバーライド
- 第9回 クラスのメンバに利用制限を付与するアクセス制御
- 第10回 Javaのクラスをグループ化するパッケージ
- 第11回 Javaのクラスの理解を深める
- 第12回 Javaプログラミングにおけるエラー対処
- 第13回 Javaの例外をスマートに記述する
- 最終回 Javaの抽象クラスとインターフェイス
| 「いまから始めるJava」記事一覧 |
ホワイトペーパー(TechTargetジャパン)
- Android NDKでJNIを使用してアプリを高速化するには (2010/3/17)
C/C++やOpenGL ESといったネイティブコードを使うためのNDKとJNIを紹介し、その使い方や注意点を徹底解説します - 調査の難しい「OutOfMemoryError」事例、5選 (2010/3/11)
Java開発者が避けて通れないメモリ不足エラーの基本的な問題切り分け方法と発生につながる事例、対処法を解説 - 究極の問題解析ツール、逆コンパイラJD-Eclipseとは (2010/3/8)
ライブラリ内で例外が発生! そのクラスのソースコードを調べたい!! 自動で逆コンパイルしてくれる無料Eclipseプラグインがあります - いまさら聞けない「Webサービス」の常識 (2010/2/26)
昨今では企業システムでも使われる「Webサービス」の概念やJava標準のJAX-WSを紹介しJBoss WSでサンプルを作成
|
|
スキルアップ/キャリアアップ(JOB@IT)
スポンサーからのお知らせ
- - PR -
| 「いつかは壊れるサーバ」そんな故障に 迅速で安価に手軽に対応する方法とは? New! |
| 「特権ユーザー」の事件を防げ! 万能権限を持つユーザーの管理方法とは? New! |
| 仮想環境の構築とデータ保護の特効薬?! 実績と信頼性の高いパッケージで安心運用 |
| 仮想環境のバックアップもこれまでどおり 「まるごと取ってまるごと戻す」簡単運用 |
| おばかアプリ選手権、第4弾開催中!! ムダにカッコよくてくだらない作品求ム! |
| 社内ファイルサーバを“クラウド”に統合 VPN直結「クラウド型ストレージ」を紹介 |
| その数、なんと400台以上! グループ内 サーバの「統合管理」によるメリットは? |
| 美人!? まあまあ? 気になる いやし系!! PV急増で「美人時計」がとった手段とは? |
| 進化を続ける富士通ストレージETERNUS DX 製品開発者の自信を裏付けるものとは何か |
| 運用管理の課題を“2つの観点”から分析 ユーザー満足度の高い「仮想環境」とは? |
- - PR -
お勧め求人情報

**先週の人気講座ランキング**
〜CCNA編〜
| ◆ | TomcatやJBossなどAPサーバ環境に関する 情報を集約! “業務”用APサーバ大百科 New! |
| ◆ | 一気に解説! 最新のクラスタストレージ 「RAIDを超えたストレージ基準」……など New! |
| ◆ | クラウド的ユーザー体験の変化は脅威か? 仮想化技術を使いこなす運用管理術を紹介 New! |

| ◆ | 上司や部下、部署内メンバーとの情報共有 を“ガラッ”と変えるコラボツールとは? New! |
| ◆ | おばかアプリ選手権、第4弾開催中!! ムダにカッコよくてくだらない作品求ム! |
| ◆ | 社内ファイルサーバを“クラウド”に統合 VPN直結「クラウド型ストレージ」を紹介 |

| ◆ | Twitterのアカウントはなぜ突破された? メールによる新手の攻撃手法とその対策 |
| ◆ | もう仮想化のお試しフェイズは終わりだ! Hyper-V 2.0が基幹システムも仮想化 |
| ◆ | 美人!? まあまあ? 気になる いやし系!! PV急増で「美人時計」がとった手段とは? |

| ◆ | クライアント企業から求められる人材 ⇒IT技術と経営戦略を併せ持つ「戦略家」 |
| ◆ | .NET編集長が実践する「技術情報検索術」 サンプル・コードを簡単に探す“技”は? |
| ◆ | 業務効率と情報セキュリティ対策を両立! 手間なく確実に機密情報を守る方法とは? |

| ◆ | 進化を続ける富士通ストレージETERNUS DX 製品開発者の自信を裏付けるものとは何か |
| ◆ | 運用管理の課題を“2つの観点”から分析 ユーザー満足度の高い「仮想環境」とは? |

| ◆ | 【CTC事例】約30の基幹システムを統合! 膨大なバッジジョブを制御した方法は? |
| ◆ | 仮想化すればコストは削減できるか? 仮想化に必要な「3つの視点」を解説する |
| ◆ | その数、なんと400台以上! グループ内 サーバの「統合管理」によるメリットは? |






