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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?