2019/02/06
sponsored link
Stringクラス??
初めはStringって何なのかよく分からないまま、ただ単に文字列を保持する変数だと思って使う人が多いと思います。かく言う私も、Stringってすんごい基本的な変数なのに、なぜ基本データ型(プリミティブ型)に含まれていないのかな?なんて思ったクチでした。
そもそもStringと、基本データ型(boolean,byte,char,short,int,long,float,double)には見た目の決定的な違いがありますよね。何かというと基本データ型は全部小文字だけど、Stringは頭文字が大文字だと言う事。なぜStringは頭文字が大文字なのかと言えばStringはクラス型変数だからです。
基本データ型というのはJavaのまさに基本データとして元々存在している型です。一方、クラス型というのはクラスでそのオブジェクトについて定義することで使えるようになる型です。
つまり、StringクラスというのがJavaのAPIで定義されているわけです。ちなみにStringクラスは「java.lang」パッケージの中にあります。
あなたが書いているクラスから他のパッケージの中にあるクラスを使おうと思ったら、普通は、パッケージ名からフルネームで「java.lang.String」と書くか、あるいはそのパッケージなりクラスなりをimportしてしまう必要があるのですが、Stringクラスに関してはimportせずとも「String」だけで通じるようになっています。なぜなら「java.lang」パッケージはJavaプログラミングにおける必須的なクラスが詰まっているので、初めからパッケージごとimportされている状態らしいです。だから「String」だけで普通に使えるわけです。
文字列リテラルはオブジェクト?
String型の変数を宣言して、文字列を代入するのは簡単なので誰でもやったことあると思います。
String s = "あいうえお";
こんな風に簡単にできますよね。けど、これって実は裏があるんですね。
上記でも言いましたが、Stringは「java.lang」パッケージの中にあるStringクラスで定義されているクラス型変数です。クラス型の変数というのは言い方を変えるとオブジェクトです。
オブジェクトを作る時は普通はnew演算子を使ってクラスをインスタンス化しなければなりません。が、Stringに関しては見ての通り、文字列を代入しているだけですよね。
本来なら、文字列”あいうえお”を保持するStringオブジェクトを作るには、
String s = new String("あいうえお");
こうやってnew演算子を使って、Stringクラスのコンストラクタを呼ばないとオブジェクトは作れません。文字列をコンストラクタの引数に渡すことで、その文字列を保持したStringオブジェクトが作られるわけです。
しかし、この形は実はすごい二度手間なことをしているんです。
プログラムソースの中で、ダブルクォーテーションで囲われた文字列を「文字列リテラル」と言いますが、この文字列リテラルを、コンパイラは実は既にStringオブジェクトとして処理しているんです。つまり、プログラムソースの中で、”あいうえお”と書くだけで、”あいうえお”という文字列を保持したString型オブジェクトがメモリ上に勝手に作られているわけです。
それを踏まえると、
String s = new String("あいうえお");
この形は、引数に渡した”あいうえお”自体がStringオブジェクトなので、Stringオブジェクトを作るために引数にStringオブジェクトを渡していることになりますよね。二度手間とはまさにこのことです。
しかも全く同じ文字列を保持したオブジェクトをもう一つ作ってしまっていることになり、引数に渡した”あいうえお”オブジェクトは結果的にほったらかし状態になってしまいます。メモリの無駄使いです。
なので、String型のオブジェクトを作る際は、
String s = "あいうえお";
これで正解なんです。
これは一体何をしているのかと言うと、String型の変数sに、Stringオブジェクトである”あいうえお”の参照値を代入しているわけです。”あいうえお”っていうのは文字列リテラルなのですが、その実体はコンパイラが勝手に作った”あいうえお”オブジェクトの場所を示す参照値なんです。
その参照値をString型変数sに代入しているわけです。これなら余計なオブジェクトは作らずにコンパイラが文字列リテラルから勝手に作ってくれたStringオブジェクトをそのまま使えますね。
Stringクラスが文字列を保持できるわけ
Stringクラスの中身を見たことありますでしょうか?面白いので暇な人は覗いてみたらいいと思います。
Stringクラスのインスタンスフィールドの一つに、
private final char value[];
というのがあります。これは何かというとchar型変数の配列です。ちょっと余談ですが、僕は配列の宣言の表記の仕方は、
char[] value;
こっちの方が分かりやすくて好きです。余談でした。。
charというのはunicodeで表される文字1文字を保持する基本データ型の変数型です(参考:基本データ型変数について)。そのcharの配列がStringクラスのフィールドにあるんです。ということはStringクラスは、文字列を文字(char)の配列として、そのフィールドに保持しているわけです。
なるほど!と唸りますよね。charなんて一体どんな時に使うねん!一文字でもStringを使えばいいやん!と思っていましたが、Stringというのはchar型の配列をフィールドに持っているからこそ、文字列を保持できるんです。charあってのStringなんですね。charに謝りたい気持ちでいっぱいです。
そしてもう一つ、重要なフィールド(パラメータ)を紹介します。
private final int count;
このint型変数countは、上記のchar[ ] valueの要素数を保持するint型変数なんです。つまり、このStringインスタンスが保持する文字列の文字数を表します。
Stringの文字数と言えば、このメソッドは使ったことあると思います。↓
public int length() {
return count;
}
出ました。length()です。このインスタンスメソッドは、「return count」しています。「this」は省略されていますが、省略せずに書くと、
public int length() {
return this.count;
}
ということです。
つまり、length()は、このインスタンスが保持している文字列の文字数を戻り値として返すメソッドなんです。だから、
String s = "あいうえお";
System.out.print(s + "の文字数は" + s.length() + "文字です");
これを実行すると、言うまでもないですが、
あいうえおの文字数は5文字です
となるわけです。
length( )とlength
ちょっと関係ない話になりますが、「length( )」とよく似たのに「length」があります。( )があるかないかの違いです。
「length( )」は上記のようにStringクラスのインスタンスメソッドなのですが、「length」はStringとは全く関係ありません。これは配列の要素数を保持しているパラメータみたいなものです。配列というのはクラスで定義されているものではないようなので、パラメータという言葉が正しいのかどうか私の知識では限界を超えているので解りかねるのですが、配列をオブジェクトと考えた時にそのパラメータである「length」を呼び出す形が、
int[] a = new int[]{1,2,3};
System.out.print(a.length);
この「a.length」の形です。これで「3」と出力されます(配列変数aの要素数)。
「length( )」はStringクラスのインスタンスメソッド、「length」は、配列オブジェクトのパラメータ??みたいなもんです。なんだか曖昧で申し訳ないのですが、片やメソッド、片やフィールドだと考えれば、( )の有る無しにも納得がいきますね。(真相をご存じの方いらっしゃいましたら、ぜひ教えてもらいたいところです。。)
その他、Stringクラスのメソッド
Stringクラスのメンバメソッドは、length( )だけではなく、いろんなメソッドが定義されています。String (Java Platform SE 8 )に詳しく書いていますが、いくつかよく使いそうなのを紹介します。
まずは、個人的に非常によく使うstaticなメソッドを一つ。
public static String valueOf(int i)
このvalueOfメソッドは、引数に受け取ったint型の値をStringに変換して戻り値として返します。
int i = 5; String s = String.valueOf(i)
staticなメソッドなので、このように
クラス名.メソッド( )
という形で使います。
Androidアプリのプログラミングでよく、TextViewにsetText( )して文字を表示するのですが、このsetText( )メソッドはStringしか受け取ってくれません。だからもし数字を表示したい時は、intやdoubleのままではsetText( )の引数に渡せません。
そんな時に、このStringクラスのvalueOf( )メソッドが役立ちます。ちなみにこのメソッドはint型の他にも、基本データ型全種及びオブジェクトも引数に受け取ることができるようにオーバーロードされていて、渡せば何でもStringに変換してくれます。(参考:メソッドのオーバーロードって何?)
もう一つ、今度は便利なインスタンスメソッドをご紹介。
public String substring(int beginIndex, int endIndex)
このsubstringメソッドは、そのインスタンスが保持する文字列から、一部分を切り取って新たな文字列にして戻り値として返します。
String s = "あいうえお"
String t = s.substring(1,4);
これで、sの保持する文字列(あいうえお)の1番目の文字から3番目の文字までを切り取った文字列を返して、tに代入してくれます。引数は1と4ですが、切り取られるのは1番目の文字から3番目の文字までです。第一引数beginIndexはその値を含みますが、第二引数endIndexはその値を含まないんです。ちょっとややこしいですね。さらに、この何番目というのがちょっとやっかいなのですが、0番から始まります。つまり0番目が”あ”、1番目が”い”、・・、4番目が”お”になります。
結果、tには”いうえ”が入ることになります。引数の1,4を見ると、直感的には”あいうえ”になるように思えてしまいますが、そうではないのでご注意を。
valueOfメソッドと、substringメソッドを使うちょっと強引?な例を挙げます。例えば、生年月日を表す19770101というint型の値があって、それを”1977年01月01日”に変換するとしましょう。
int birthday = 19770101; String strBirthday = String.valueOf(birthday); String year = strBirthday.substring(0,4); String month = strBirthday.substring(4,6); String day = strBirthday.substring(6,8); String strBirthday2 = year + "年" + month + "月" + day + "日";
これでstrBirthday2には、”1977年01月01日”という文字列が入ります。”01″を”1″に変えたい場合は正規表現を使ってちょちょいのちょいとできますが、それはまたの機会に。。やろうと思えばif文を使って簡単にできますよね。もしyearの1文字目が0ならば・・、ってな具合に。やりたい人はやってみて下さい。
とりあえず2つのメソッドを紹介しましたが、Stringクラスには便利なメソッドがたくさん定義されています。上にも書きましたが、String (Java Platform SE 8 )で、private以外のメンバは全て解説してくれています。初心者にはちょっと文章が難解だと思いますが、頑張って読んでみて下さい。
書き換えられないString
ちょっと初心者がつまづきやすいポイントだと思うのですが、こちらをご覧下さい。
String s = "あいうえお"
s.substring(1,4);
この時点で、sは何になっているでしょうか?
”いうえ”だと思った方は、残念ながら素人確定です。。
substringメソッドはインスタンスメソッドなので、どうしてもそのインスタンス自体をいじるようなイメージがあるのですが、それは誤解です。このメソッドはインスタンスメソッドのくせに、全く別のインスタンスを新たに作って、戻り値として返すようになっています。
つまり、s自体にはなんら手を加えることなく、sの1番目から3番目までの文字列を保持する新たなStringインスタンスを作って、戻り値として返すのが、このsubstringメソッドなんです。
なので、
String s = "あいうえお"
s.substring(1,4);
これだと、sには何も手を加えていないので、sは”あいうえお”のまんまです。二行目はただ単に”いうえ”を保持するStringインスタンスを別で作っただけで、それをどこに代入するわけでもなく放置しているだけです。。(こんなわけの分からないコードは決して書かないようにしましょう。)
インスタンスメソッドで、別のインスタンスを戻り値として返すというのはなんか変な感じですが、なぜこんなことするかと言うと、そもそもStringというのは、そのインスタンスが保持する文字列を書き換える事ができないからです。つまり、インスタンスメソッドで自分自身のフィールドである文字列を操作することはできないんです。
上述しましたが、Stringクラスは文字列を保持する為に、char配列を使っています。
private final char value[];
これが、Stringクラスの文字列を保持する為のフィールドなんですが、見ての通り、「final」という修飾子がついています。これは、上書き禁止を表す修飾子です。「final」が付いているということは、このchar配列は一度初期化したら書き換えることはできないわけですね。つまりStringは文字列を一度保持したら、二度と書き換えることができないということです。
なので、substringメソッドだけでなく、Stringクラスで定義されている、文字列を操作して戻り値として返しているようなメソッドは全て、新たなインスタンスを作ってそれを戻り値として返しているというわけです。
こんなところで。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
[…] string型 […]
by 今週もjavaとクレジットカード決済で苦しんだ② – コピペプログラマー雑記 2018/01/12 23:05