Javaについて徹底解説!

byteと基本データ型・文字列の変換を詳細に! Javaのbyteを基本から

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

Javaのbyteはプリミティブ型の一つで、8ビットの範囲で-128127までの整数を表現するものです。

byteは主にバイナリファイルやバイナリデータを扱う時に使います。人によってはbyteは使ったことがないかもしれませんが、Java 9以降のStringは内部的にbyteで文字列を管理していますし、実はbyteは結構身近にあるのです。

ですから、byteの扱い方や考え方を知っておくと、いざプログラムでバイナリデータを扱わなければならなくなった時に慌てずに済みます。そして、バイナリデータの扱いが分かるということは、コンピュータの動き方の理解に一歩近づくことでもあります。

この記事ではbyteについて、byteとはどういうものか、どうやって使うのか、気を付けたい所などを初心者向けにお伝えします。2進数にまつわる話がどうしても多いですが、そこも分かりやすくお伝えします。

※この記事のサンプルは、Java 11の環境で動作確認しています


1.byteは1バイトを表すデータ型

byteは整数のデータ型で、プリミティブ型(primitive、基本データ型)と呼ばれるものの一つです。一つのbyteのサイズは8ビット(1バイト)で、Javaではもっともビットのサイズが小さいプリミティブ型です。

byteを他のプリミティブ型と簡単に比較すると、以下のとおりです。

データ型値の種類ビット数表現できる値の範囲接尾語備考
boolean真偽値1ビットtrue/falseのどちらか
byte整数8ビット(1バイト)-128~127
short整数16ビット(2バイト)-32,768~32,767
char文字16ビット(2バイト)0~65,535Unicode文字、\u0000\uffff
int整数32ビット(4バイト)-2,147,483,648~
2,147,483,647
±2147百万、Unicodeコードポイント
long整数64ビット(8バイト)-9,223,372,036,854,775,808~
9,223,372,036,854,775,807
Lまたはl±922
float浮動小数点32ビット(4バイト)±3.40282347E+38~ 1.40239846E-45Fまたはf単精度
±3.4×1038乗~約±1.4×10-45
double浮動小数点64ビット(8バイト)±1.79769313486231570E+308~±4.94065645841246544E-324Dまたはd

倍精度
±1.8×10308乗~約±4.9×10-324

1-1.Javaのbyte-128127の整数

Javaのbyteでは、-128127の整数を表現できます。整数ですので、数値は1ずつ変化し、小数はありません。

そして、byte8ビットなので28乗とおり、すなわち256とおりを表現できます。その半分をマイナスの整数として使っています。

byteの8ビットのビットパターンと、実際の数値は以下のように対応します。つまり、byte+1し続けると-128127の範囲をぐるぐる回るのです。

5~8ビット目1~4ビット目10進数表現備考
000000000すべてのビットが0
000000011
000000102
01111110126
01111111127Byte.MAX_VALUE
10000000-128Byte.MIN_VALUE
10000001-127
11111110-2
11111111-1すべてのビットが1+1すると0に戻る

byteは0から始まって1ずつ大きくなり、8ビット目が0かつ17ビット目が全て1になると127です。そこから、8ビット目が1かつ07ビット目が全て0になると-128で、そこから1ずつ大きくなり、全てのビットが1になると-1です。

1-2.byteの使いどころはデータをバイナリで扱う時

byteはバイナリファイルやバイナリデータを「そのまま」扱う時に使います。もちろん、byteは数値型なのでbyteを使った計算もできますが、プログラム上からの扱いはデータの格納容器であることが多いでしょう。

ですから、byteの変数を1つだけ使うよりも、byteを配列にして、バイナリデータの集まりを表すのによく使われます。ファイルや文字列も、裏ではbyteの集まりです。このように、プログラムで扱えるものは全てbyteで表せるのです。

byte配列を使ったファイルの読み書きについては、この記事を読み終わった後に、以下の記事も読んでみてください。

関連記事 関連記事

2.byteの基本的な使い方

この章では、byteの基本的な使い方について、サンプルを交えながらお伝えします。

2-1.byteの宣言の仕方、初期値、リテラル

2-1-1.byteの変数・配列の宣言、初期値の指定

byteは型の一つですから、以下のように変数や配列変数の型として使えます。もちろんメソッドの引数や、戻り値としても使えます。配列の宣言や値の代入や初期化も、他のプリミティブと同じように行えます。

2-1-2.byteのリテラルの書き方

byteをリテラルで表現する時は、接頭語をつけなければ10進数です。Java 11の時点では10進数の他に以下の書き方ができます。さらに、“_”で桁区切りが出来るようにもなっています。

  • 2進数(“0b”で始まる、Java 7から)
  • 8進数(“0”で始まる)
  • 10進数(接頭語なし)
  • 16進数(“0x”で始まる)

なお、1バイトの範囲は符号なしの0255と覚えているのが普通でしょう。ですから、Javabyteの変数へ、例えば2550xff0b1111_1111が代入できると思いがちです。でも、それはJavaではコンパイルエラーになります。

なぜなら、2550xffJavabyteで表現できる-128127の範囲に無い数値だからです。ただ、明示的なbyteへのキャストをすれば代入ができます。この仕組みにはintbyteの変換の仕様が関係しますが、詳しくは後述します。

ただ、0255の範囲で数値を扱って、それをbyteとの間で自由に変換できれば、それはそれで分かりやすいこともありますし、便利です。そのための方法もありますので、こちらも後述します。

2-1-3.byteのフィールド、配列の初期値は0

フィールドとしてbyte型の変数を使ったり、byte型の配列を使う場合は、未初期化だと0になります。他の数値型プリミティブと同じ動きです。

2-2.byteを使った四則演算

byteを使った四則演算(加減乗除)には、四則演算用の算術演算子、つまり+-*/を使います。余りを求めるなら%です。

ただし、計算結果で小数点が出た場合、byteの変数へ代入すると、小数点以下の値は切り捨てされます。これは整数型のプリミティブに共通する動きです。

なお、Javaではbyte/shortへの演算はintに自動的に変換して行うのが仕様で、計算結果もintになります。ですので、計算結果をbyteにするなら、例のようにbyteへの明示的なキャストが必要です。

そして、byte以外のプリミティブ型からbyteにキャストする際は注意が必要です。どういう扱いがされるかは後述します。

2-2-1.byteの計算では、符号の変化に要注意!!

byteを扱う時は、符号の変化に注意しましょう。

byteに数値を加減算して、プラス側の最大値を超えるとマイナス側の最大値になり、マイナス側の最小値よりも小さくなるとプラス側の最大値になります。つまり、加算していくとプラスマイナスがひっくり返るタイミングがあり、オーバーフローと呼ばれます。

これは、二進数で言うと 0111 1111 1000 0000 の間でプラスマイナスが変わるということです。前者がByte.MAX_VALUE、後者がByte.MIN_VALUEです。これを細かく言えば、Javaでは2の補数でマイナスの数値を表現しているから、ということです。

byteは表現できる範囲が8ビットと小さいので、特に加算や乗算をするとすぐに範囲を超えてしまいます。ですから、計算結果を格納する変数は、byteよりもビット数が大きな型のshortintなどにしておいてもいいでしょう。

2-3.byteでのビット演算

byteはビット演算にも使えます。業務向けのプログラムではビット演算はあまり使いませんが、科学技術計算など処理速度が必要なプログラムでは、ビット演算で計算することもあります。特に、掛け算や割り算をシフトで代用するのが代表的です。

なお、C言語などの名残があるプログラムだと、処理のオプションをビットパターンで指定したりします。また、プログラムの仕様で特定のビットが1か調べることもありますので、ビット演算のやり方は覚えておいても損はありません。

以下では、Javabyteが対象のビット演算の例を簡単に示しておきます。ただ、Javaのビット演算はshortbyteではintに変換後に実行され結果もintなので、特に右シフトで論理シフト(>>>)をするなら事前に0xff&しておく必要があるなど、工夫が必要です。


3.byteと他のプリミティブ型との変換

Javaでは数値型のプリミティブ型として、byteとビット数が違う整数(short/char/int/long)と、小数点が扱えるもの(float/double)があります。

byteをビット数がより大きいプリミティブへ変換する際は数値が維持されるのですが、逆に、ビット数が大きなプリミティブからbyteへ変換する際には、知っておくべきことがいくつかあります。この章ではそれをお伝えします。

ちなみに、前者はプリミティブ型のワイドニング変換(Widening Primitive Conversion)、後者はナローイング変換(Narrowing Primitive Conversion)とも呼ばれます。

3-1.short/char/int/long→byteへのキャスト

このパターンは、ビット数が大きな方の整数から、小さい整数(この記事ではbyte)のビット数分のデータだけを、ビットパターンをそのままに取り出すイメージです。言葉だと分かりづらいので、具体的な例で説明してみます。

16ビットであるshort254は、ビットパターンでは 0000 0000 1111 1110 です。これを8ビットのbyteにキャストすると、後ろの8ビット分だけが取り出されます。ですので、byteのビットパターンは 1111 1110 となって、これを先述した表で見てみると-2ですよね。

同じように、short255のビットパターンは 0000 0000 1111 1111 で、byteにキャストすると 1111 1111、すなわち-1です。さらにshort256 0000 0001 0000 0000 で、byteにキャストすると 0000 0000、すなわち0です。

3-2.byte→short/char/int/longへのキャスト

このパターンでは、ビット数が大きな方の整数に、小さい整数(この記事ではbyte)で表現されている数値がそのまま入ります。こちらも具体的な例で説明してみます。

8ビットであるbyte-2は、ビットパターンでは 1111 1110 です。これを16ビットのshortにキャストしても、-2という数値はそのままです。short-2のビットパターンは 1111 1111 1111 1110 です。つまり、同じ数値なのですが、型が違うのでビットパターンも違うのです。

同じように、byte-1 1111 1111shortにキャストすると数値は-1のままで 1111 1111 1111 1110 です。さらにbyte0 0000 0000 で、shortにキャストすると数値は0、すなわち 0000 0000 0000 0000 です。

3-3.float/double→byteへのキャスト

このパターンでは、float/doubleで表現されている数値が一旦32ビット整数のintに変換され、その後にビットの大きい整数小さい整数のパターンで変換されます。float/doubleの浮動小数点型とbyte/intなどの整数型ではビットパターンに互換性がありませんので、一旦intを経由するのですね。

floatが254.5の時の場合だけ解説します。まずは254.532ビットのintへ変換されます。この際、小数点以下は切り捨てられ、254になります。32ビットのint254のビットパターンは 0000 0000 0000 0000 0000 0000 1111 1110 です。この後ろ8ビット分がbyteになるので 1111 1110 となり、数値としては-2です。

なお、float/doubleからのキャスト先がlongの場合だけ、途中に出てくる整数型はlongになります。それ以外の整数型の場合はintです。この変換の詳細は、整数整数の場合も含め、Java言語仕様の「Narrowing Primitive Conversion」に記述されています。

3-4.byte→float/doubleへのキャスト

このパターンでは、byteで表現されている値がfloat/doubleにそのまま入ります。そもそもfloat/doublebyteの範囲の数字を全部表現できますので、数値そのものでやりとりできるのです。

3-5.【応用】byte0255で扱いたい場合は、Integer.toUnsignedIntかビット演算を使おう

byteで扱える-128127の範囲の数値を、0255の範囲で扱えるとプログラムが分かりやすくなることがあります。その際にはbyteintなどに変換するのですが、この章でお伝えしたとおり、byteからintへの単純なキャストでは意図したとおりに変換できません。

そこで、Java 8以降ではByte.toUnsignedIntを使うと、いい感じに変換ができるのでお勧めです。

Java 7以前の環境なら、0xffとビット演算の&(論理積、AND)をした結果でいいでしょう。こうすると、byteで表現している数値ではなく、byte8ビット分のビットパターンをそのままintにした数値を得られます。ビット演算は、数値ではなくビットパターンで処理するからですね。

ちなみに、Byte.toUnsignedIntでやっているのも同じことです。ただ、ビット演算を使えば、変換先はshortでもOKです。

そして、0255までの範囲のintを、-128127byteに再度変換するには、単にbyteへキャストすればOKです。この仕組みもこの章でお伝えしましたよね。


4.byteとStringとの変換

StringもJavaでは重要なクラスです。Stringbyteにすること、またbyteStringにできればいろいろと便利です。ここではその方法をお伝えします。

4-1.String→byteの変換

ファイルから読み込んだ文字列や、引数で受け取った文字列からbyteを作りたい時があります。そういう時はByte.parseByteを使いましょう。文字列をbyteに変換してくれます。byteに解釈できない文字列へはNumberFormatExceptionthrowされますのでご注意を。

基数(何進数かを表す数値)を指定するならByte.parseByte(String, int)を使います。二番目の引数には基数を指定します。例えば、2進数なら2ですし、16進数なら16です。

4-2.byte→Stringの変換

逆に、byteStringにしたい時があります。簡単にやるなら、Byte.toString(byte)か、String.valueOf(byte)を使いましょう。byteが持つ数値が10進数のStringになります。ちなみに、どちらのメソッドでも結果は同じです。

また、byte2進数や16進数表記にしたいこともあります。16進数にするならString.formatがお手軽ですが、2進数にするにはもう少し工夫が必要です。以下の例で0xff&しているところは、Byte.toUnsignedIntでも同じです。

4-3.byte配列→Stringの変換

実務でよくやるのは、デバッグ用にbyteの配列をStringにしてログに出力することです。

4-3-1.java.util.Arraysを使う

byte配列のString変換を簡単に行うなら、java.util.Arrays.toString(byte[])を使います。byteの多次元配列なら、java.util.Arrays.deepToString(byte[])で良いでしょう。

4-3-2.自分で変換用のクラス・メソッドを作る

ただ、Arrays.toStringdeepToString10進数で表示されます。2進数や16進数の場合が分かりやすい場合も多いでしょう。その場合は以下の様なクラス・メソッドを作ります。書式はお好みで変えてください。

なお、今ではApache Commonsなどの外部ライブラリにも変換用のクラスがありますし、Java 10までならJava SEの環境でDatatypeConverterというクラスが使えます。

ですが、外部ライブラリはプロジェクトで自由に使えないこともありますし、DatatypeConverterJava 11以降のSE環境では標準で使えなくなりました。ですので、どのように変換するのかを知っておくのは悪いことではありません。


5.byteとByte

Javaでは8ビットの整数を表現するために、byteByteの二つの方法があります。Javaプログラミングの初心者は、なぜ同じ整数の表し方が二つあるのか混乱すると思います。

この章ではその理由と、byteByteの使い分けの方針などをお伝えします。

5-1.二種類の表現方法は性能確保のため

Javaではプリミティブ型のbyteと、クラスのByteは別物です。C#などではこういう区別がないのに、なぜJavaではあるのか。これは、Javaが生まれた当時にプログラムの実行速度を確保するためでした。

Javaは1995年に登場したプログラミング言語です。当時のCPUのクロック周波数は今とは桁が違い、一般向けのCPUでようやく100MHzを超えたくらい。メモリの量も全体で数MB~数10MBと非常に乏しかったものです。

byteは8ビットの数字そのものなので、楽に速く扱えます。しかし、byteをクラスとすると、一つのbyteの数値を表すのに8ビットよりもずっと多くのメモリを使います。簡単な計算をするにも、処理上では余分なオーバーヘッドが発生します。

5-2.クラスのByteならnullを表現できる

JavaでByteを使うのは、Byteが持つメソッドを使いたい時と、値がない場合すなわちnullを表現したい時です。例えば、SQLでは値の有り無しをNULLかどうかで表現できますが、それをJavabyteでは上手に表現できません。

ですから、プログラム上では0-1などの値に特別な意味を持たせたり、Byte.MAX_VALUEMIN_VALUEを使うのですが、確実さには欠けます。そういう値のチェックを忘れるなどのミスもしがちです。

そういう時に参照型であるByteを使えば、値がないことをnullとして表現できるのです。Byteをどういう時に使うか分からない方は、その変数でnullを表現する必要があるかを一つの指針にしてみてください。

5-3.オートボクシングでbyteByteを自動変換する

Java 1.5でオートボクシング(auto boxing)という仕組みが導入されました。オートボクシングで、byteByteをプログラム上でほぼ同じものとして扱えます。

プログラム上でbyteを使う所ではByteを使えますし、Byteを使う所ではbyteが使えます。本当のプログラム上は相変わらずbyteByteは別物なのですが、その違いをJavaが裏で自動的に変換をしてくれるのです。

これでJavaの面倒な部分がある程度解消されました。ですが、前述のとおりBytenullを表せますが、byteは必ず何かの整数なので、nullに相当するものがありません。

ですので、以下のように予期せぬところでNullPobyteerExceptionが発生したりします。これは2019年のJava 11の時点でも変わっていません。プログラマが注意するか、Optionalを使う必要があります。

5-4.【応用】符号なしbyteとしての扱い方

この記事ではずっと、byteはプラスマイナスがある8ビットの整数だとお伝えしてきました。それは今も変わりませんが、Java 8byteを符号なし整数として扱えるメソッドがByteに追加されました。それらのメソッドを使うと、byte8ビットをフルに使った計算ができます。

例えば以下のようにです。比較しているのは同じbyteでビットパターンも変わりませんが、compareUnsignedでは8ビット目も含めた整数として比較しているのが分かるでしょうか。

Byte.~Unsigned系のメソッドは以下のものです。それぞれの詳細は、Javadocを参照してみてください。

Byte.compareUnsigned:符号なし整数として大小比較する

Byte.toUnsignedInt:符号なし整数としてintに変換する

Byte.toUnsignedLong:符号なし整数としてlongに変換する


6.まとめ

この記事では、Javabyteをお伝えしてきました。Javabyte1バイト(8ビット)の整数で、-128127までの値を表現できます。byteJavaでバイナリデータをバイト単位で使う時の標準的なデータ型です。

Javaでは、1バイト分の整数を表現するにはプリミティブ型のbyteと、クラス(参照型)としてのByteの二種類があり、それらは違うものであることには注意しましょう。ただ、オートボクシングにより違いが見えにくくはなっています。

さて、byteはプログラミングやコンピュータの基本となるデータ単位です。ですので、Javabyteの使い方を学んでおけば、他のプログラミング言語でもそう大きな違いなく使えるでしょう。

特にビットパターンと数値の対応を覚えておけば、いざという時に役に立ちます。トラブル対応などで詳細なデータを見る時は16進数などの形式で見ることが多いので、そういう知識があるなしでは作業効率が大きく違いますよ。

私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。もし、今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。

コメント