新人エンジニアが最初に知っておきたい変数とデータ型の使い方
なぜ、変数とデータ型が重要なのか、その理由。
この記事では、弊社の新人エンジニア研修の参考にJavaを解説します。
前回はJava言語の特徴を解説しました。
今回は、変数とデータ型について解説します。
変数はデータに名前(ニックネーム)を付けたものだと思ってください。変数を使うことでプログラミングではいろいろなことができるようになります。
プログラミングに限った話ではありませんが、"名前"というものはすごい発明ですね。もしも、名前というものがなければ世の中どれだけ不便になることでしょうか?
たとえば、「クリスマスイブの日に橋の下に捨てられていた白い犬」がいたとして、その犬を呼ぶときに毎回 「クリスマスイブの日に…」 と呼ぶことを私達はふつうしません。“シロ”のように名前をつけて呼ぶと思います。名前を使い回すことで同じ対象を何度でも指し示すことができるのですね。
個人的には適切な名前をつけることができた機能については、その設計の8割が完成したと考えても言い過ぎでないことが多いように思います。
(Ruby開発者:まつもとゆきひろ - プログラマが知るべき97のこと)
コンピュータの本質は計算機です。計算機の役割は、データを処理することです。その際、データを一時的に保管する場所が必要なのです。その場所に名前を付けたものを変数(へんすう)といいます。
実際には、メインメモリ上にデータの保管場所が用意されるのですが、その場所を指定するのにメモリのアドレスを使用するとしたら大変です。なぜなら、人間にとってアドレス表記は分かりやすいとはいえないからです。そこで、分かりやすい変数名を付けて扱うのです。
例えるなら変数とは、引っ越しの荷物を入れるダンボール箱のようなものです。
引っ越しのダンボールにはラベルを張っておかないと何だか分からなくて困りますね。
そのラベルが、変数名です。
また、段ボール箱は何を入れるかをあらかじめ決めておきますね。食器と電化製品を一緒に入れたりはしないと思います。同様に変数には何を入れるかあらかじめ決めておく必要があります。
これがデータ型です。
データ型により、データを入れるときや、後でデータを取り出すときに間違いがなくなるのです。
簡単な例で言えば「2+3」は5ですが、「"Hello " + "World"」は”Hello World”という文字列になります。同じ+という操作でもデータ型によって結果は変わります。「2-3」 には意味がありますが 「"Hello " - "World"」 は無意味です。
Javaでは整数値や文字列だけでなく様々なデータ型を扱います。我々自身が作るデータ型もあります。データ型が変われば処理が変わるという点はJavaの本質を知る上で大変重要です。
変数というのは変わる数と書きます。その名の通り、中身(データ)は変わっていく可能性があります。それに対して変わらない数(データ)を定数(ていすう)といいます。ここでは、変数、定数の順で学びます。
1.変数とは
以下のExample05は型の異なる4つの変数を宣言と同時に初期化してそれぞれの値をコンソールに出力します。
ポイントは、文字型はシングルクォーテーション ’ で囲うところです。後で見る文字列がダブルクオーテーション " で囲うのと区別してください。どちらも何かで囲わないと変数名と区別がつかないから囲うのですね。
public class Example05 {
public static void main(String[] args) {
int data1 = 1;
double data2 = 1.0;
boolean flag = false;
char c = 'C';
System.out.println(data1);
System.out.println(data2);
System.out.println(flag);
System.out.println(c);
}
}
<結果>
1 1.0 false C |
プログラムは原則として上から下に実行されていきます。これを順次といいます。ですので、まずは上記プログラムを上から順にみていきましょう。
変数を作成することを「変数の宣言」といいます。
例えば、以下のようにデータ型を明示して宣言します。
int data3;
変数に値を入れることを「値の代入」といいます。イコール記号を使います。算数の「等しい」ではないので注意しましょう。
data3 = 10;
なお、変数の宣言と値の代入は以下のように1文に書くこともできます。
int data3 = 10;
変数(ローカル)は初期化してから使うのが原則です。
変数に入っている値を見ることを「値の参照」といいます。
System.out.println(data3);
このとき初学者に多い間違いはSystem.out.println("data1")
のように変数名ではなくダブルクオーテーションのついた文字列としてしまうことです。気をつけましょう。
- もし、変数名ではなく文字列を渡すと、どんな実行結果になりますか?
あなたの答え: |
変数には、他の変数の値を代入することができます。
int data4 = data3;
また、変数には、演算の結果を代入することもできます。
data4 = data3+ 1;
さらに、変数に値を代入すると今までの値は上書きされます。
data3 = data3+ 1;
今回ご紹介した変数はローカル変数といいます。
ローカル変数は、使用できるスコープ(範囲)【scope】が一番狭い変数で、{ }(ブロック)の中だけで有効です。
ちなみに、英語のscopeには望遠鏡などから覗いた範囲という意味があります。このあと、インスタンス変数、static変数という別種の変数が出てきますから混同しないようにしましょう。
- 以下のようなプログラムを書くと実行結果はどうなりますか?
int data1;
System.out.println(data1);
あなたの答え: |
ローカル変数は初期化してから使わないといけません。
初期化とは、そろばんで言えば「ご破算」のようなもので、変数の値をクリアしてから使うために必要な処理です。
ちなみに、直接記述される定数値のことを「リテラル」といいますので、あわせて覚えてください。上記プログラムの1、1.0、false、Cはいずれもリテラルです。Javaには整数、浮動少数点数、真偽、文字の4つのリテラルがあります。【Literary】「文字通り」という英語表現を聞いたことのある新人エンジニアの方もいらっしゃるのではないでしょうか?
ローカル変数が格納されるスタックメモリのイメージを以下の図を使って講師が説明します。
2.変数の宣言
変数の宣言では、変数に入れる値の型(type)をはじめに指定する必要があります。これが静的型付け言語のJavaの特徴です。
<構文>
型名 変数名;
Javaでは変数やメソッド、クラスなどの名前のことを識別子と呼びます。識別子は数字から始めてはいけないことは覚えておきましょう。また、使える記号はアンダースコア(_)とドル記号($)のみです。
※余談ですが、Webアプリケーションの世界で存在感を誇っているPHPという言語
良い名前付けは本当に大切です。「codic: プログラマーのためのネーミング辞書」では、日本語に適切な英語の変数名等をつけてくれますので是非活用してみてください。
また、英語の名詞には単数形と複数形がありますのでこれもうまく変数名に利用して下さい。numとnumsでは意味するものが違うことをしっかり読み取るようにして下さい。さらに、名前付けはユーザーが業務で実際に使用している名前を使うと確実です。
以下の予約語とリテラルは特別な意味のために予約されており、識別子には使えません。これはつまり、intという名前の変数を作りたくてもできないということです。
もしも、
int int = 0;
のような書き方ができるとすると、以後、プログラム中に出てくる「int」という文字列がint型の宣言なのか、intという変数名なのか、分からなくなってしまうのですね。
下表は、Javaの予約語とリテラルの一覧です。今は覚えなくてよいですが、研修終了時には8割がた目にしたことがある状態になっているはずです。
abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while |
Javaのリテラル一覧
true false null |
なお、予約語やリテラルはIDE上で特別な色で表示されますので、それと知ることができます。
3.Javaで使用できるプリミティブ型
プリミティブ型【Primitive data type】とは、Javaで使用できる基本的なデータ型です。後で学ぶ参照型以外のデータ型をいいます。英語のプリミティブには「原始的な」といった意味があります。
プリミティブ型を下表2.3にまとめました。(プリミティブ型を基本データ型ともいいます)
データ型 | 値 |
boolean | true or false |
byte | 8ビット整数 -128~127 |
char | 16ビットUnicode文字 ¥u0000~¥uFFFF |
short | 16ビット整数 -32,768~32,767 |
int | 32ビット整数 -2,147,483,648~2,147,483,647 |
long | 64ビット整数 -9,223,372,036,854,775,808~9,223,372,036,854,775,807 |
float | 32ビット単精度浮動小数点数 -1.4E-45〜約340澗 |
double | 64ビット倍精度浮動小数点数 -4.9E-324〜約1.7澗 |
覚え方としては以下の通りです。
booleanはブール代数から来ています。ブール代数についてはリンクをご覧ください。
byteは8ビットなので名前通りです。
charは文字を意味するcharacterから。文字コードを表現しますからマイナスの範囲はありません。世界中の文字を一つの文字コード体系(Unicode)で表現しようとしているため、16ビットです。
intは整数を意味するintegerから来ています。数値リテラルはデフォルトでint型の値として解釈されるので整数を表すときには最もよく使われます。このintを基準にビット数を半分にしたのがshort、倍にしたのがlongです。2進数で考えると名前の意味がよくわかりますね。
floatは浮動小数点数を意味するfloating point numberから。
そして、floatの2倍のビット数があるからdoubleです。一般的に実数(の近似)を表現するときにはdoubleを使います。
データ型はとても大切な考え方です。なぜなら、その変数に入る値の範囲がデータ型で決まるからです。例えば、byte型の変数に128は入れられないのです。また、人間にとっては整数の1も実数の1.0も同じですが、すべてを2進数で表現するコンピュータでは下表2.4のような違いがあります。
10進数表現 | 2進数表現(32ビット) |
整数の1 | 0000 0000 0000 0000 0000 0000 0000 0001 |
実数の1.0f ※ | 0011 1111 1000 0000 0000 0000 0000 0000 |
※floatとdoubleでも2進数表現は違います。したがってfloatの場合には数値の後ろにfをつける必要があります。doubleの場合には数値の後ろにdを付けることができますが、省略することが一般的です。
※2進数の知識がない人は「コンピュータと仲良くなるためにデータ表現を知る」という記事を参照ください。
このように全くの別物です。このことは、後で型変換(キャスト)という考えを学ぶときに必要になりますからここで理解しておきましょう。ゆくゆくは全ての型を覚えたいですが、まずは、真偽値のboolean、整数値のint、浮動小数点数のdoubleの3つは最低限、覚えましょう。
4.なぜ、変数を使うのか?
変数はなぜ必要なのでしょうか?
その答えを一言で言えば、変数を使うとデータに意味を与えることができ、変数のデータを使いまわすことができ、さらにはそのデータを変更することもできるからです。
以下のExample06は、現在22歳のあなたの将来の年齢を表示するプログラムです。
public class Example06 {
public static void main(String[] args) {
int yourAge = 22;
System.out.println("あなたは今" + yourAge + "才です");
System.out.println("あなたは10年後" + (yourAge + 10) + "才です");
System.out.println("あなたは20年後" + (yourAge + 20) + "才です");
System.out.println("あなたは30年後" + (yourAge + 30) + "才です");
System.out.println("あなたは40年後" + (yourAge + 40) + "才です");
System.out.println("あなたは50年後" + (yourAge + 50)+ "才です");
}
}
<結果>
あなたは今22才です あなたは10年後32才です あなたは20年後42才です あなたは30年後52才です あなたは40年後62才です あなたは50年後72才です |
yourAgeが年齢であるというのはその名前から明らかです。これが「22」という数値ですと、何なのか分かりませんね。このように名前のついていないリテラルはマジックナンバー(magic number:魔法の数値)と呼ばれてその使用を戒めています。
変数yourAgeが使いまわされていますね。もし、一つ年齢を重ねたとしたらどうでしょうか?
変数yourAgeを23で初期化すれば良いですね。
このように、変数を使うとデータに意味を与えることができ、使い回すこともできるのです。
なお、今回の"yourAge"のような変数名の書き方をラクダに見立ててキャメルケースといいます。
次のスネークケースとの対比で覚えましょう。
長い変数名でもIDEを使っていれば入力補完(Ctrl + スペースキー)が効きますので入力は楽なはずです。
5.定数を使うとき
変わりうるデータに付けた名前、それが変数でした。
一方、定数はその名の通り宣言時に決めた値をコードの中で後から変更することができないデータです。つまり、宣言と同時に初期化する必要があります。変わらない(変わりにくい)データに付けた名前です。
したがって次のExample07のようなことはできません。
public class Example07 {
public static void main(String[] args) {
final int ADULT_AGE = 20;
ADULT_AGE = 18;
}
}
それ以外は変数と同じと思っていただいて結構です。
例えば、次のExample08は何をしているプログラムでしょうか?
public class Example08 {
public static void main(String[] args) {
final int ADULT_AGE = 18;
System.out.println("成人年齢は" + ADULT_AGE + "才です");
System.out.println("今年の成人は10年後" + (ADULT_AGE + 10) + "才です");
System.out.println("今年の成人は20年後" + (ADULT_AGE + 20) + "才です");
System.out.println("今年の成人は30年後" + (ADULT_AGE + 30) + "才です");
System.out.println("今年の成人は40年後" + (ADULT_AGE + 40) + "才です");
System.out.println("今年の成人は50年後" + (ADULT_AGE + 50) + "才です");
}
}
<結果>
成人年齢は18才です 今年の成人は10年後28才です 今年の成人は20年後38才です 今年の成人は30年後48才です 今年の成人は40年後58才です 今年の成人は50年後68才です |
定数にはfinal:「最後の」という修飾子を付けます。そしてすべて大文字にして単語と単語の間はアンダーバーでつなぐのがJavaプログラマの慣習です。アンダーバーでつなぐ定数名の書き方をスネークケースといいます。
6.char型は整数であるということ
次のExample09は何をしているプログラムでしょうか?
public class Example09 {
public static void main(String[] args) {
char a = 'A';
a += 32;
System.out.println(a);
}
}
<結果>
a |
char型のA(ラージA)に対して32を足すとa(スモールa)になりました。char型が整数(文字コード)だからできることです。
プログラムでは、大文字と小文字を相互に変換したり、英数記号でランダムなパスワードを生成したり、平文(ひらぶん)を暗号化して読めなくしてまた戻したり、といった処理をすることがありますが、それができるのもchar型が整数だからなのですね.
ぜひ、リンク先の「バイトとは(アスキーコード)」で'A'と'a'には32の差があることを確認して下さい。
7.クラスはパッケージに入れて管理する
今まで作成してきたJavaファイルがどこにあるかを確認します。
そうすると(デフォルトパッケージ)(別名:無名パッケージ)というところにあります。実は、これはあまり良い保管場所とは言えません。
Javaファイルもファイルですから、一般的なコンピュータファイルと同様、何らかの分類基準を持ったフォルダで管理するのが良いのです。そのフォルダのことをJavaではパッケージ(package)といいます。
また、パッケージごとに区切ることで別パッケージであれば、同じ名前のクラスを複数作ることができます。この考え方を名前空間といいます。みなさんも、拡張子まで含めると全く同じ名前のファイルを作れないことはご存知だと思います。今はあまり実感が無いかもしれませんが、クラス名が重複してしまうということは良くあることです。
さらに、パッケージ管理することで、別のクラスからは見られないクラスやフィールド、メソッドを作ることができるのです。(この点は後述します)
前回オブジェクト指向では、モノの世界と同じようにプログラムの部品を組み合わせるというお話をしました。部品Aと部品Bがつながるとまずい、ということもあるわけです。たとえば車でも発火プラグとガソリンタンクが直接つながると大爆発ですね。
さらにパッケージ管理することで分かりやすいクラス体系になります。
つまり、パッケージ管理の目的を3つ挙げると以下のようになります。
①わかりやすい分類整理
②名前空間の提供
③不必要なものを公開しないことができる
③に関しては後ほどカプセル化のところでまとめていますのでそこでお話しますが、ここでも少し触れておきます。
Javaはセキュリティに強いとよく言われます。その理由は、アプリケーションとOSとの直接のやり取りを禁じて、JVM経由でやり取りさせる点が一つ、そして、この③不必要なものを公開しないことができる、こともJavaのセキュリティ強化に役立っているのです。この強力なパッケージ管理の仕組みがJavaが企業の大規模システム開発で採用されている理由の一つです。
こんな風に。Example01
ということでここからはクラスをパッケージに入れることにしたいと思います。
package chap02;
public class Example01 {
public static void main(String[] args) {
char a = 'A';
a += 32;
System.out.println(a);
}
}
パッケージを分けたため、過去に使用したExample01というクラス名が再び使用できています。
1行目の「package chap02; 」がこのクラスの所属するパッケージを示しています。パッケージの名前は同じものがあってはいけません。全く同じ名前のパッケージがあると区別ができないからです。そのため、例えば会社で作成するクラスの場合は自社のドメイン名を逆向きにした名前を使うのが一般的です。
例えば、当社の場合ですとpackage jp.co.saycon.chap2;
のようになります。
しかし、みなさんは、新人エンジニア研修中ですからそこまでの配慮は結構です。パッケージ名は講師の指示に従ってください。
これ以降は、章ごとのパッケージにクラスファイルを作成するようにします。
8.別パッケージのクラスを呼び出して使用する
では、別のパッケージにあるクラスを使用してみましょう。
java.timeパッケージのLocalDateTimeクラスを使って今の日時を表示するのがExample02です。
package chap02;
public class Example02 {
public static void main(String[] args) {
System.out.println(java.time.LocalDateTime.now());
}
}
<結果>
2024-01-07T16:45:55.276 |
※Tは日付と時間の区切り記号です。
OSから取得した現在のシステム時刻が表示されました。javaパッケージの中のtimeパッケージの中のLocalDateTimeクラスのnow()メソッドを実行しています。
LocalDateTimeクラスは文字通り、あなたのOSが使っているローカルな日時のクラスです。このようなクラスの指定方式を完全修飾クラス名(fully qualified class name:FQCN)といいます。例えるなら、名字と名前を呼んで個人を特定するようなものです。
しかし、毎回、完全修飾クラス名を使うのは冗長に感じますね。
そこでこんな書き方ができます。
package chap02;
import java.time.LocalDateTime;
public class Example03 {
public static void main(String[] args) {
System.out.println(LocalDateTime.now());
}
}
3行目にimportと書いて完全修飾クラス名が書いてありますね。これで、クラス内ではクラス名だけを書けば良くなります。クラス内では友達の下の名前だけを呼びたいものです。
新人エンジニアによくある勘違いとして、importの語感から「ここでクラスを読み込んで使えるようにしている」という誤解があります。そうではなく、完全修飾クラス名を使わなくて済むためのimport記述です。
今後は、基本的にimport文を書いていくことにします。
また、IDEを使っているとクラスを簡単にインポートすることができますので、やり方を講師から聞いてください。
なお、import java.time.*;
とアスタリスクを使うことでtimeパッケージのすべてのクラスをインポートすることができます。ただし、インポートされるのはクラスだけでパッケージはインポートされません。つまり、import java.time.*;
と書いても、例えばjava.time.chrono.JapaneseDate
(和暦表示のためのクラス)などはインポートされませんので、ご注意ください。
import java.time.*;
import java.time.chrono.*;
と書くことで両方のパッケージをインポートすることができます。
※ただし、省略表記は非推奨です。クラス名まで特定してインポートすべきです。
※「import java.lang.*;」という記述は省略できます。いままでSystemクラスをインポートせずに使えたのもSystemクラスがjava.langパッケージに含まれるからです。java.langパッケージに含まれる主なクラスとしては他に文字列のString、便利な計算道具があるMathなどがあります。Javaの標準APIのクラスが多すぎてどこから手を付けていけばいいか分からないという人は、このjava.langパッケージかjava.utilパッケージあたりから学ぶと良いでしょう。ちなみに"util"はutility「便利な道具」といった意味です。
まとめができたら、アウトプットとして演習問題にチャレンジしましょう。
以上、今回は「変数でデータを再利用できるようになる」方法について見てきました。
次回は、「演算子でプログラムに計算させる」です。