javaのStringとcharと byteとunicode

javaのString::substring(int,int)で部分文字列を取り出そうとして自分の無知に気づく。
1byte目から5byte目のひとつ手前までを文字列として取得しようとして
String a = "aあいうえb";
String b = a.substring(1,5); // => b = "あいうえ"
と、書いて気づいた。
C言語風に(無意識に)日本語は2byteで、アルファベットや半角スペースは1byteと計算されると、思い込んでいた。つまり、
b => "あい"
あ = 2byte -> char二つ分のメモリを消費
い = 2byte -> char二つ分のメモリを消費
あい = 4byte -> char四つ分のメモリを消費
と、誤解していた。
これって、日常的にjavaを触ってる人にとっては常識なんだろうけれど、
javaでは文字(char)を2byte(UNICODE)として扱う。
だから、"abc"はchar三つ分を消費し、"あいう"はchar三つ分を消費する。
String::substringでは、純粋に指定したbyte位置の文字列を抜くのは難しい。


c/c++では文字(char)を1byteといて扱う。windowsならShift_jisで扱う。
javaでもShift_jisを扱うことは可能だが、"Shift_JIS"と指定してはいけない。
"Windows-31J"として指定する必要がある(MS漢字コード+Shift_JIS
マルチバイト文字を意識しているのは嬉しいことのはずだが、ここに混乱が生じる原因にもなっている(気がする)。
C++で送信した文字列をjavaで受け取るために、javaでは、byteという1byte単位でデータを扱うための型が別に用意されている。
ここから、文字コードを意識してchar[] や Stringへ変換する必要がある。
以下はそのサンプル


// byte[] -> String
byte[] bytes;
String str = new String(bytes,"Windows-31J");
// String -> byte[]
String str = "いろは";
byte[] bytes = str.getBytes("Windows-31J");
java.io APIで使える文字コードは他にもShift_JIS,ISO-8859-1,UTF8,UTF-16などが指定できる。
J2SE1.4以後のjava.nio APIではこの部分がさらに拡張されている。