- JAVA
Java charの使い方|int変換とCharacter判定メソッド一覧
charとは、1文字を表す Java の基本型で、中身は 0〜65535 の数値(UTF-16 の文字コード)です。 'A' のようにシングルクォートで囲んで書きます。「文字に見えるが実体は数値」という性質が、'5' - '0' で数値に変換できる便利さと、'1' + 1 が 50 になるつまずきの両方を生んでいます。
この記事では、すべてのコードを OpenJDK 21.0.10 で実行し、出力をそのまま掲載しています(JDK 25.0.4.1 でも同じコードを実行し、出力が一致することを確認しました)。char⇔int⇔String の変換パターン、char の計算で起きる意外な結果、Character クラスの判定メソッドに半角・全角・ひらがな・漢字・記号を入れた実測表、絵文字で length() が 2 になる再現まで、1ページで確認できます。
char変換の早見表
「数字の文字を数値にする」のか「文字コードを知りたい」のかで書き方が変わります。 ここがいちばん間違えやすい箇所です。
| やりたいこと | 書き方 | 例の結果 |
|---|---|---|
数字の char → その数値('5' → 5) |
c - '0' |
'5' - '0' → 5 |
| 数字の char → その数値(全角にも対応) | Character.getNumericValue(c) |
'5' → 5 |
| 数字の char → 数値(基数を指定、数字以外は -1) | Character.digit(c, 10) |
'a' → -1 |
| char → 文字コード | (int) c |
(int) '5' → 53、(int) 'a' → 97 |
| 数値(0〜9)→ 数字の char | (char) (n + '0') |
7 → '7' |
| 数値(0〜9)→ 数字の char(メソッド) | Character.forDigit(n, 10) |
7 → '7' |
| 文字コード → char | (char) 97 |
'a' |
| char → String | String.valueOf(c) |
"x" |
| char → String(別の書き方) | Character.toString(c) / "" + c |
"x" |
| String → char(n 文字目) | s.charAt(n) |
"hello".charAt(1) → 'e' |
| String → char[] | s.toCharArray() |
[a, b, c] |
| char[] → String | new String(arr) / String.valueOf(arr) |
"ab" |
| char → Character(ラッパー) | 代入するだけ(オートボクシング) | Character boxed = 'q'; |
| 大文字 / 小文字にする | Character.toUpperCase(c) / toLowerCase(c) |
'a' → 'A' |
| 数字か判定する | Character.isDigit(c) |
全角数字も true |
| 半角数字だけ判定する | c >= '0' && c <= '9' |
全角数字は false |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java charの使い方|int変換とCharacter判定メソッド一覧」
charの基本(宣言・範囲・初期値)
public class CharBasic {
public static void main(String[] args) {
char c = 'A'; // 文字で書く
char d = 65; // 文字コードで書く('A' と同じ)
char e = 'あ'; // 日本語も1文字なら入る
char tab = '\t'; // エスケープシーケンス(タブ)
System.out.println(c + " " + d + " " + e + " [" + tab + "]");
System.out.println((int) c + " " + (int) e);
System.out.println((int) Character.MIN_VALUE + " " + (int) Character.MAX_VALUE
+ " " + Character.BYTES + " " + Character.SIZE);
char[] def = new char[1]; // 配列やフィールドの初期値
System.out.println((int) def[0]);
System.out.println(c == d); // char 同士は == で比べる
System.out.println(c == 65); // 数値とも比べられる
}
}
実行結果(java CharBasic.java、OpenJDK 21.0.10):
A A あ [ ]
65 12354
0 65535 2 16
0
true
true
| 項目 | 内容 |
|---|---|
| サイズ | 2バイト(16ビット) |
| 範囲 | 0〜65535(符号なし)。負の値は持たない |
| 書き方 | シングルクォート 'A'。ダブルクォート "A" は String |
| 初期値(フィールド・配列) | 文字コード 0 の文字(空文字ではない) |
| 比較 | 基本型なので == で比べる(String の equals とは異なる) |
| ラッパークラス | Character |
'A' と "A" は別の型です。char c = "A"; はコンパイルエラー(incompatible types: String cannot be converted to char)になります。また、char に「空」はありません。'' と書くとコンパイルエラーになるので、「文字がない」状態を表したいときは String の空文字 "" か、Character 型の null を使います。
char⇔int⇔Stringの変換を実行して確認
import java.util.Arrays;
public class CharConvert {
public static void main(String[] args) {
char five = '5';
System.out.println("-- 数字の char を数値にする");
System.out.println(five - '0');
System.out.println(Character.getNumericValue(five));
System.out.println(Character.digit(five, 10));
System.out.println(Integer.parseInt(String.valueOf(five)));
System.out.println((int) five); // これは文字コード(53)
System.out.println("-- 数値を char にする");
int n = 7;
System.out.println((char) (n + '0'));
System.out.println(Character.forDigit(n, 10));
System.out.println((char) n == '7'); // (char) 7 は制御文字で、'7' とは別
System.out.println((char) 97);
System.out.println("-- char と String");
System.out.println(String.valueOf('x') + " " + Character.toString('x') + " " + ("" + 'x'));
System.out.println("hello".charAt(1));
System.out.println(Arrays.toString("abc".toCharArray()));
System.out.println(new String(new char[]{'a', 'b'}) + " " + String.valueOf(new char[]{'c', 'd'}));
System.out.println("-- getNumericValue と引き算の違い");
System.out.println(Character.getNumericValue('a') + " " + Character.getNumericValue('Z')
+ " " + Character.getNumericValue('5') + " " + Character.getNumericValue('三')
+ " " + Character.getNumericValue('Ⅻ') + " " + Character.getNumericValue('+'));
System.out.println(('a' - '0') + " " + ('5' - '0') + " " + ('5' - '0'));
System.out.println(Character.digit('f', 16) + " " + Character.digit('g', 16)
+ " " + Character.digit('5', 10) + " " + Character.digit('a', 10));
System.out.println("-- 範囲外の charAt");
try {
"abc".charAt(3);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
実行結果:
-- 数字の char を数値にする
5
5
5
5
53
-- 数値を char にする
7
7
false
a
-- char と String
x x x
e
[a, b, c]
ab cd
-- getNumericValue と引き算の違い
10 35 5 -1 12 -1
49 65253 5
15 -1 5 -1
-- 範囲外の charAt
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
数字のcharを数値にする3つの方法の違い
| 入力 | c - '0' |
Character.getNumericValue(c) |
Character.digit(c, 10) |
|---|---|---|---|
'5'(半角数字) |
5 | 5 | 5 |
'5'(全角数字) |
65253 | 5 | 5 |
'a'(英字) |
49 | 10 | -1 |
'三'(漢数字) |
19929 | -1 | -1 |
'+'(記号) |
-5 | -1 | -1 |
c - '0'は最も短く速い書き方です。'0'〜'9'の文字コードが 48〜57 と連続していることを利用しています。半角数字だとわかっている場合に使います。Character.getNumericValue(c)は全角数字やローマ数字('Ⅻ'→ 12)にも対応しますが、英字 a〜z / A〜Z に 10〜35 を返す点に注意が必要です(16進数や36進数の桁として扱うための仕様で、公式 API ドキュメントにも記載があります)。数字でない文字は -1 です。Character.digit(c, 10)は「10進数の数字でなければ -1」と明確なので、入力を検証しながら変換したい場合に向いています。
(int) '5' は 5 ではなく 53(文字コード)です。逆に (char) 7 は文字 '7' ではなく文字コード 7 の制御文字になります。「数字の見た目」と「文字コード」を変換するときは、必ず '0' を足すか引く、と覚えておくと混乱しません。String 全体を数値にする Integer.parseInt については「Java int⇔String変換」の記事で扱っています。
'1'+1が50になる:charの計算で起きる意外な結果
char を + で計算すると、結果は int になります。 これが char でいちばん多いつまずきです。
import java.util.ArrayList;
import java.util.List;
public class CharPitfalls {
public static void main(String[] args) {
System.out.println('1' + 1); // 文字コード 49 + 1
System.out.println((char) ('1' + 1)); // char に戻すと '2'
System.out.println('a' + 'b'); // 97 + 98
System.out.println("" + 'a' + 'b'); // 先に文字列があれば連結
System.out.println('a' + 'b' + ""); // 先に足し算されてから文字列に
System.out.println('a' + 1 + "x");
char x = 'a';
x += 1; // 複合代入は自動でキャストされる
System.out.println(x);
x++;
System.out.println(x);
char y = (char) (x + 1); // x + 1 は int なのでキャストが必要
System.out.println(y);
StringBuilder sb = new StringBuilder();
sb.append('a').append('b');
System.out.println(sb);
StringBuilder sb2 = new StringBuilder();
sb2.append('a' + 'b'); // int の 195 が追加される
System.out.println(sb2);
StringBuilder sb3 = new StringBuilder('a'); // 初期容量 97 の指定になる
sb3.append("b");
System.out.println(sb3 + " capacity=" + sb3.capacity());
char[] arr = {'a', 'b'};
System.out.println(arr); // char[] は中身が表示される
System.out.println("arr=" + String.valueOf(arr));
List<Character> list = new ArrayList<>(List.of('a', 'b', 'c'));
list.remove(Character.valueOf('b')); // 値で削除
System.out.println(list);
try {
list.remove('a'); // 'a' が int の 97 として扱われる
} catch (Exception ex) {
System.out.println(ex);
}
}
}
実行結果:
50
2
195
ab
195
98x
b
c
d
ab
195
b capacity=97
ab
arr=ab
[a, c]
java.lang.IndexOutOfBoundsException: Index 97 out of bounds for length 2
| 書いたコード | 結果 | 理由 | 直し方 |
|---|---|---|---|
'1' + 1 |
50 | char は int に変換されて計算される(49 + 1) | (char) ('1' + 1) で '2' |
'a' + 'b' |
195 | char 同士の + も int の足し算 |
"" + 'a' + 'b' か String.valueOf |
'a' + 'b' + "" |
"195" | 左から順に評価され、先に足し算される | 文字列を先頭に置く |
char y = x + 1; |
コンパイルエラー | x + 1 は int |
(char) (x + 1) か x += 1 |
sb.append('a' + 'b') |
"195" が追加 | 引数が int になる | sb.append('a').append('b') |
new StringBuilder('a') |
空(容量97) | char が int の容量指定として解釈される | new StringBuilder("a") |
list.remove('a') |
IndexOutOfBoundsException | remove(int index) が選ばれる |
list.remove(Character.valueOf('a')) |
char y = x + 1; のコンパイルエラーは incompatible types: possible lossy conversion from int to char です(javac 21 で確認)。一方で x += 1 や x++ はコンパイルが通ります。 複合代入演算子には暗黙のキャストが含まれているためです。
文字を連結したいときは、式の先頭に文字列を置くか、StringBuilder の append を1文字ずつ呼ぶのが確実です。
Characterの判定メソッド一覧と実測結果
Character クラスには、文字の種類を判定する static メソッドがそろっています。日本語の文字や全角文字を入れたときの結果は、名前から想像するものと違うことがあるので、20種類の文字で実測しました。
public class CharJudge {
public static void main(String[] args) {
String[] names = {"A", "z", "5", "A", "5", "あ", "ア", "ア", "漢", "々", "ー",
"半角スペース", "全角スペース", "改行", "_", "$", "!", "、", "①", "é"};
char[] cs = {'A', 'z', '5', 'A', '5', 'あ', 'ア', 'ア', '漢', '々', 'ー',
' ', ' ', '\n', '_', '$', '!', '、', '①', 'é'};
for (int i = 0; i < cs.length; i++) {
char ch = cs[i];
System.out.printf("| %s | U+%04X | %s | %s | %s | %s | %s | %s | %s |%n", names[i], (int) ch,
Character.isDigit(ch), Character.isLetter(ch), Character.isLetterOrDigit(ch),
Character.isAlphabetic(ch), Character.isUpperCase(ch), Character.isLowerCase(ch),
Character.isWhitespace(ch));
}
}
}
実行結果を表にしたものです(各行はプログラムの出力そのままです)。
| 文字 | コード | isDigit | isLetter | isLetterOrDigit | isAlphabetic | isUpperCase | isLowerCase | isWhitespace |
|---|---|---|---|---|---|---|---|---|
| A | U+0041 | false | true | true | true | true | false | false |
| z | U+007A | false | true | true | true | false | true | false |
| 5 | U+0035 | true | false | true | false | false | false | false |
| A | U+FF21 | false | true | true | true | true | false | false |
| 5 | U+FF15 | true | false | true | false | false | false | false |
| あ | U+3042 | false | true | true | true | false | false | false |
| ア | U+30A2 | false | true | true | true | false | false | false |
| ア | U+FF71 | false | true | true | true | false | false | false |
| 漢 | U+6F22 | false | true | true | true | false | false | false |
| 々 | U+3005 | false | true | true | true | false | false | false |
| ー | U+30FC | false | true | true | true | false | false | false |
| 半角スペース | U+0020 | false | false | false | false | false | false | true |
| 全角スペース | U+3000 | false | false | false | false | false | false | true |
| 改行 | U+000A | false | false | false | false | false | false | true |
| _ | U+005F | false | false | false | false | false | false | false |
| $ | U+0024 | false | false | false | false | false | false | false |
| ! | U+0021 | false | false | false | false | false | false | false |
| 、 | U+3001 | false | false | false | false | false | false | false |
| ① | U+2460 | false | false | false | false | false | false | false |
| é | U+00E9 | false | true | true | true | false | true | false |
この表から読み取れるポイントは次のとおりです。
isDigitは全角数字5でも true です。半角数字だけを通したいときはc >= '0' && c <= '9'と書きます。丸数字①は false でした。isLetterはひらがな・カタカナ・漢字・長音記号ー・繰り返し記号々でも true です。「英字だけ」の判定には使えません。英字だけなら(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')と書きます。isUpperCase/isLowerCaseは全角英字Aにも反応し、ひらがなや漢字は両方 false です。大文字・小文字の区別がない文字だからです。isWhitespaceは全角スペースや改行でも true です。空白の除去に使えます。_と$は判定メソッドでは文字扱いされませんが、Java の識別子には使えます(Character.isJavaIdentifierStartは両方 true でした)。
判定メソッドの一覧
| メソッド | true になる文字 | 補足 |
|---|---|---|
isDigit(c) |
数字(半角・全角など) | Unicode の「10進数字」 |
isLetter(c) |
文字(英字・かな・漢字など) | 記号・数字は false |
isLetterOrDigit(c) |
文字または数字 | 記号や空白を除く判定に |
isAlphabetic(c) |
アルファベット的な文字 | isLetter より少し広い。引数は int(コードポイント) |
isUpperCase(c) / isLowerCase(c) |
大文字 / 小文字 | 区別のない文字は両方 false |
isWhitespace(c) |
空白・タブ・改行・全角スペース | ノーブレークスペースは false |
isSpaceChar(c) |
Unicode の空白文字 | 改行は false、全角スペースは true |
isISOControl(c) |
制御文字 | 改行は true |
isIdeographic(c) |
漢字(表意文字) | '漢' は true、'々' は false(引数は int) |
isSurrogate(c) |
サロゲート(絵文字などの片割れ) | 後述 |
isEmoji(cp) |
絵文字 | Java 21 以降。引数は int |
漢字かどうか・ひらがなかどうかを判定したいときは、Character.UnicodeScript.of(c) や Character.UnicodeBlock.of(c) が使えます。実行すると 'あ' は HIRAGANA、'ア' と 'ア' は KATAKANA、'漢' と '々' は HAN(UnicodeScript の場合)でした。
文字列がすべて数字かを判定するコピペ
public class CharAllDigits {
public static void main(String[] args) {
System.out.println("12345".chars().allMatch(Character::isDigit));
System.out.println("12a45".chars().allMatch(Character::isDigit));
System.out.println("".chars().allMatch(Character::isDigit)); // 空文字は true になる
System.out.println("123".chars().allMatch(Character::isDigit)); // 全角も true
System.out.println("123".chars().allMatch(c -> c >= '0' && c <= '9')); // 半角だけ
System.out.println(Integer.parseInt("123")); // parseInt は全角も変換する
}
}
実行結果:
true
false
true
true
false
123
空文字に対する allMatch は true になります。空を許さないなら !s.isEmpty() && を前に付けます。また Integer.parseInt が全角数字も受け付ける点は、入力チェックを書くときに知っておくと役立ちます。
大文字・小文字の変換
public class CharCase {
public static void main(String[] args) {
System.out.println(Character.toUpperCase('a') + " " + Character.toLowerCase('Q')
+ " " + Character.toUpperCase('a') + " " + Character.toUpperCase('5')
+ " " + Character.toUpperCase('あ'));
System.out.println((char) ('a' - 32) + " " + (char) ('A' + 32)); // 文字コードの差は 32
// 先頭だけ大文字にする
String w = "hello";
System.out.println(Character.toUpperCase(w.charAt(0)) + w.substring(1));
// 大文字と小文字を入れ替える
StringBuilder sb = new StringBuilder();
for (char ch : "Hello World".toCharArray()) {
sb.append(Character.isUpperCase(ch) ? Character.toLowerCase(ch) : Character.toUpperCase(ch));
}
System.out.println(sb);
// 大文字小文字を区別せずに比べる
System.out.println(Character.toLowerCase('A') == Character.toLowerCase('a'));
}
}
実行結果:
A q A 5 あ
A a
Hello
hELLO wORLD
true
Character.toUpperCase は、変換先がない文字(数字・ひらがななど)はそのまま返します。 全角英字 a も A になります。'a' - 32 のような文字コードの計算でも変換できますが、英字以外に使うと別の文字になるので、Character.toUpperCase を使うほうが読みやすく確実です。
文字コード(Unicode)と比較・ソート
char の大小比較は、文字コードの大小比較です。 そのため < や > でそのまま比べられます。
import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Collectors;
public class CharSort {
public static void main(String[] args) {
for (char ch : new char[]{'0', '9', 'A', 'Z', 'a', 'z', 'あ', 'ア', '亜', 'A'}) {
System.out.print(ch + "=" + (int) ch + " ");
}
System.out.println();
System.out.println(('a' < 'b') + " " + ('Z' < 'a') + " " + ('9' < 'A') + " " + ('あ' < 'ア'));
System.out.println(Character.compare('a', 'b') + " " + Character.compare('b', 'a')
+ " " + Character.compare('a', 'a') + " " + Character.valueOf('a').compareTo('d'));
// 文字列の中の文字をソートする
char[] sc = "banana Apple 3".toCharArray();
Arrays.sort(sc);
System.out.println("[" + new String(sc) + "]");
// 降順にソートする(Character に変換してから)
System.out.println("banana".chars().mapToObj(ch -> (char) ch)
.sorted(Comparator.reverseOrder()).map(String::valueOf).collect(Collectors.joining()));
// 連続した文字コードを使う
for (char ch = 'a'; ch <= 'e'; ch++) System.out.print(ch);
System.out.println();
System.out.println((char) ('a' + 25) + " " + ('c' - 'a') + " " + (char) ('A' + 2));
// 文字の出現回数を数える('a' を 0 番目とする配列)
int[] count = new int[26];
for (char ch : "banana".toCharArray()) count[ch - 'a']++;
System.out.println(count[0] + " " + count[1] + " " + count['n' - 'a']);
// 文字コードを 16 進数で表示する
System.out.println(String.format("%c %d %04x U+%04X", 'あ', (int) 'あ', (int) 'あ', (int) 'あ'));
System.out.println((char) 0x3042 + " " + (char) Integer.parseInt("3042", 16));
// chars() は IntStream を返す
System.out.println("ab".chars().boxed().toList() + " -> " + "ab".chars().mapToObj(ch -> (char) ch).toList());
}
}
実行結果:
0=48 9=57 A=65 Z=90 a=97 z=122 あ=12354 ア=12450 亜=20124 A=65313
true true true true
-1 1 0 -3
[ 3Aaaabelnnpp]
nnbaaa
abcde
z 2 C
3 1 2
あ 12354 3042 U+3042
あ あ
[97, 98] -> [a, b]
| 文字の種類 | 文字コードの範囲 | 並び順 |
|---|---|---|
| 半角スペース | 32 | 最も小さい |
半角数字 0〜9 |
48〜57 | 英字より前 |
半角大文字 A〜Z |
65〜90 | 小文字より前 |
半角小文字 a〜z |
97〜122 | 大文字より後 |
| ひらがな | 12353〜('あ' は 12354) |
カタカナより前 |
| カタカナ | 12449〜('ア' は 12450) |
漢字より前 |
| 漢字 | 19968〜('亜' は 20124) |
読みの順ではない |
| 全角英数字 | 65296〜('A' は 65313) |
漢字より後 |
- ソートすると「空白 → 数字 → 大文字 → 小文字」の順になります。
Arrays.sortの結果[ 3Aaaabelnnpp]で、大文字のAが小文字より前に来ているのが確認できます。 Character.compare(a, b)は -1 / 0 / 1 を返し、compareToは文字コードの差(-3 など)を返します。 符号だけを見て判定するのが確実です。"abc".chars()はStream<Character>ではなく IntStream を返します。そのまま表示すると[97, 98]と数値になるので、mapToObj(ch -> (char) ch)で char に戻します。- 漢字は文字コード順であり、読みの五十音順にはなりません。日本語を辞書順に並べたいときは
java.text.Collatorを使います。
絵文字でlength()が2になる理由とcodePoints()
char は16ビットなので、65536 種類までしか表せません。絵文字や一部の漢字は char 2つ(サロゲートペア)で1文字を表すため、length() が見た目の文字数と合わなくなります。
public class CharEmoji {
public static void main(String[] args) {
String s = "a😀b";
System.out.println(s.length()); // char の数
System.out.println(s.codePointCount(0, s.length())); // コードポイント(文字)の数
System.out.println(Integer.toHexString(s.charAt(1)) + " " + Integer.toHexString(s.charAt(2))
+ " " + Character.isHighSurrogate(s.charAt(1)) + " " + Character.isLowSurrogate(s.charAt(2)));
System.out.println(Integer.toHexString(s.codePointAt(1)) + " " + Character.charCount(s.codePointAt(1)));
System.out.println(Character.isLetter(s.charAt(1)) + " " + Character.isEmoji(s.codePointAt(1)));
// 1文字ずつ処理する:codePoints() を使う
System.out.print("codePoints : ");
s.codePoints().forEach(cp -> System.out.print("[" + Character.toString(cp) + "]"));
System.out.println();
// 逆順:StringBuilder.reverse はサロゲートペアを保つ
System.out.println(new StringBuilder(s).reverse());
// 漢字にも char 2つの文字がある
String kanji = "𠮷野家";
System.out.println(kanji.length() + " " + kanji.codePointCount(0, kanji.length())
+ " " + kanji.codePoints().mapToObj(Character::toString).toList());
// 複数の絵文字を結合した文字は、コードポイントでも1にならない
String family = "👨👩👧";
System.out.println(family.length() + " " + family.codePointCount(0, family.length())
+ " " + family.split("\\b{g}").length);
}
}
実行結果:
4
3
d83d de00 true true
1f600 2
false true
codePoints : [a][😀][b]
b😀a
4 3 [𠮷, 野, 家]
8 5 1
| 数え方 | 書き方 | "a😀b" |
家族の絵文字(結合) |
|---|---|---|---|
| char の数 | s.length() |
4 | 8 |
| コードポイントの数 | s.codePointCount(0, s.length()) |
3 | 5 |
| 見た目の文字数(書記素) | s.split("\\b{g}").length |
3 | 1 |
charAt(1) で取れるのは絵文字の前半分(d83d)だけです。これを単独で表示したり、substring(0, 2) で切ったりすると、表示できない文字になります。絵文字や人名の漢字を含む可能性がある文字列を1文字ずつ処理するときは、charAt の代わりに codePoints() を使います。
| やりたいこと | char 単位(絵文字で崩れる) | コードポイント単位 |
|---|---|---|
| 文字数 | s.length() |
s.codePointCount(0, s.length()) |
| 1文字ずつ処理 | for (char c : s.toCharArray()) |
s.codePoints().forEach(...) |
| n 番目の文字 | s.charAt(i) |
s.codePointAt(i)(i は char の位置) |
| int → String | String.valueOf((char) n) |
Character.toString(cp) |
| 判定メソッド | Character.isLetter(char) |
Character.isLetter(int) |
Character の判定メソッドの多くには、char を受け取る版と int(コードポイント)を受け取る版の両方があります。絵文字かどうかを調べる Character.isEmoji(int) は Java 21 で加わったメソッドです。
switchでcharを使う
char は switch の条件にそのまま使えます。Java 14 以降の switch 式(->)なら、複数の文字をカンマで並べられ、break も不要です。
public class CharSwitch {
public static void main(String[] args) {
for (char ch : new char[]{'a', 'E', '7', '+', 'あ'}) {
String kind = switch (ch) {
case 'a', 'e', 'i', 'o', 'u' -> "vowel";
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> "digit";
case '+', '-', '*', '/' -> "operator";
default -> Character.isLetter(ch) ? "letter" : "other";
};
System.out.println(ch + " -> " + kind);
}
// 従来の switch 文(break が必要)
char op = '*';
int a = 6, b = 7, r;
switch (op) {
case '+': r = a + b; break;
case '*': r = a * b; break;
default: throw new IllegalArgumentException("unknown op: " + op);
}
System.out.println(r);
// Java 21:Character 型にパターンと条件(when)を書ける
Character boxed = '+';
String msg = switch (boxed) {
case Character c when Character.isDigit(c) -> "digit";
case '+' -> "plus";
default -> "other";
};
System.out.println(msg);
}
}
実行結果:
a -> vowel
E -> letter
7 -> digit
+ -> operator
あ -> letter
42
plus
| ポイント | 内容 |
|---|---|
| case に書けるもの | 'a' のような char の定数。"a"(String)は型が合わずコンパイルエラー |
| 大文字と小文字 | 別の文字として扱われる('E' は 'e' の case に入らない)。先に Character.toLowerCase(ch) してから switch するとまとめられる |
| 範囲(a〜z など) | case に範囲は書けない。default の中で Character.isLetter などを使うか、if 文で ch >= 'a' && ch <= 'z' と書く |
Character(ラッパー)が null |
switch に渡すと NullPointerException(実行して確認)。null の可能性があるなら先に確認する |
switch 式やパターンマッチングの詳しい書き方は「Java switchパターンマッチング」の記事で扱っています。
つまずきやすい点と対処のまとめ
| 起きること | 原因 | 対処 |
|---|---|---|
'1' + 1 が 50 になる |
char が int として計算される | (char) ('1' + 1)、数値にしたいなら '1' - '0' + 1 |
'a' + 'b' が 195 になる |
char 同士の + は足し算 |
"" + 'a' + 'b' |
(int) '5' が 53 になる |
キャストは文字コードを返す | '5' - '0' |
incompatible types: possible lossy conversion from int to char |
char c = x + 1; |
(char) (x + 1) |
全角数字が isDigit を通る |
Unicode の数字すべてが対象 | c >= '0' && c <= '9' |
ひらがなが isLetter を通る |
Unicode の文字すべてが対象 | 英字の範囲を自分で書く |
getNumericValue('a') が 10 |
英字は 10〜35 を返す仕様 | Character.digit(c, 10) |
絵文字で length() が合わない |
サロゲートペア(char 2つ) | codePointCount / codePoints() |
list.remove('a') で例外 |
remove(int) が選ばれる |
Character.valueOf('a') を渡す |
StringIndexOutOfBoundsException |
charAt の位置が範囲外 |
s.length() を確認。空文字の charAt(0) も同じ |
Java 25との差分
同じコードを JDK 25.0.4.1 でも実行し、この記事の出力はすべて Java 21.0.10 と一致しました。char 型と Character の基本的な動きは版による違いが少ない部分です。変わりうるのは対応する Unicode の版で、公式 API ドキュメントの Character クラスの表では Java SE 24 以降が Unicode 16.0 と記載されています。新しく追加された文字や絵文字の判定結果は、JDK の版によって変わる可能性があります。
| 機能 | 使える Java |
|---|---|
switch 式(->、複数ラベル) |
14 以降 |
Character.toString(int) |
11 以降 |
正規表現 \b{g}(書記素の境界) |
9 以降(結合絵文字を1文字として扱う動きは Java 21 と 25 で確認) |
Character.isEmoji(int) |
21 以降 |
switch のパターンと when |
21 以降 |
よくある質問
Q. char と String はどう使い分けますか? A. 1文字を数値のように扱いたい(比較、計算、switch、配列の添字)ときは char、文字の並びを扱うときは String です。メソッドの引数や戻り値として渡すだけなら、絵文字なども1文字として持てる String のほうが扱いやすい場面が多いです。
Q. '5' を 5 にするには、どれを使えばよいですか?
A. 半角数字だとわかっているなら c - '0' が最も短く書けます。数字以外が来る可能性があるなら Character.digit(c, 10) を使い、-1 が返ったら数字ではないと判定します。(int) c は文字コード(53)になるので、この用途には合いません。
Q. char の比較に equals は使えますか?
A. char は基本型なので == で比べます。Character(ラッパー型)同士を比べるときは equals を使います。なお Character.valueOf('a').equals("a") は false です(実行して確認)。char と String は型が違うので、s.charAt(0) == 'a' や s.equals("a") のように型をそろえて比べます。
Q. 文字列の1文字ずつを処理する書き方はどれがよいですか?
A. 英数字だけなら for (char c : s.toCharArray()) が簡潔です。絵文字や 𠮷 のような文字を含む可能性があるなら s.codePoints() を使うと、サロゲートペアを分割せずに処理できます。
まとめ
- char は 0〜65535 の数値(UTF-16 の文字コード)。シングルクォートで書き、
==で比べる - 数字の char を数値にするのは
c - '0'。(int) cは文字コードになる。検証も兼ねるならCharacter.digit(c, 10) - char の
+は int の計算。'1' + 1は 50、'a' + 'b'は 195。連結は文字列を先頭に置く isDigitは全角数字、isLetterはひらがな・漢字でも true。半角だけに絞るなら範囲を自分で書く- 大小比較は文字コード順(数字 → 大文字 → 小文字 → ひらがな → カタカナ → 漢字)
- 絵文字は char 2つ。
length()ではなくcodePointCount、charAtではなくcodePoints()を使う - switch では
case 'a', 'e' ->と複数の文字を並べられる。大文字小文字は別扱い
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Java charの使い方|int変換とCharacter判定メソッド一覧」
株式会社RJCでは、Javaの基本型と文字の扱い方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。