読み込み中...

Javaのsubstringメソッドが初心者でもわかる10選の実用例

Javaのsubstringメソッドを解説するイラスト Java
この記事は約35分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Javaで文字列の一部を切り出すなら、Stringクラスのsubstringメソッドを使います。開始位置を表すbeginIndexは含まれ、終了位置のendIndexは含まれないため、初心者がつまずきやすい境界の読み方を早い段階で押さえると、サンプルコードの意味を追いやすくなります。

これらの文字列操作は、ファイル名、日付文字列、商品コード、ログ行、URLの一部などを扱う実用例で頻繁に登場するのが基本です。そのため、使い方だけでなく、範囲外アクセス、負のインデックス、Unicode文字の注意点、カスタマイズの考え方までまとめて確認すると、応用に移るときの判断がしやすくなります。

動作確認環境
  • Java SE 21 / OpenJDK 21
  • Apache Commons Lang 3.14.0
📖 この記事で学べること
  • substringメソッドの基本構文とインデックスの考え方
  • indexOftoUpperCaseと組み合わせる使い方
  • 日付、価格、リスト処理に使える実用例とサンプルコード
  • StringIndexOutOfBoundsExceptionを避ける注意点
  • 外部ライブラリや独自メソッドによるカスタマイズ

Javaとsubstringメソッドとは

substringメソッドの結論は、文字列から「開始位置以上、終了位置未満」の範囲を取り出す処理です。JavaのStringは0始まりのインデックスで文字位置を扱うため、substring(0, 5)なら0番目から4番目までが対象になります。

// substringメソッドの基本的な使い方
public class Main {
    public static void main(String[] args) {
        // 元の文字列
        String original = "Hello, World!";

        // substringメソッドを使って部分文字列を取得
        String part = original.substring(0, 5);

        // 部分文字列を出力
        System.out.println(part);
    }
}

結果: 期待される出力はHelloです。

この例では、Hello, World!の先頭から5文字分だけを切り出しています。ただし、endIndexにあたる5番目の文字は含まれないため、範囲指定は「0から5まで」ではなく「0以上5未満」と理解すると安定します。

この半開区間は、文字数を数えるときにも役立ちますし、ここがポイントです。たとえば5文字を得たいなら、開始位置に5を足した値を終了位置に置くと整理でき、startからstart + 5未満までという考え方になります。

そのため、仕様書や設計メモには「何文字目まで」ではなく「どの位置から何文字分」と書くほうが誤解を減らせます。endIndexを直接考えるより、開始位置と取得したい長さから終了位置を計算する形にすると、境界値の間違いを見つけやすくなるのが目安です。

Java言語の概要

Javaは、サーバーサイド開発、Androidアプリ、業務システム、バッチ処理などで使われる汎用言語です。一般に、標準ライブラリの互換性を重視した設計が採られており、StringArrayListPatternなどのクラスを組み合わせて多くの処理を構成します。

その標準ライブラリの中で、文字列を表すStringは特に利用頻度が高い型です。公式ドキュメントの詳細はOracle Java SE 21 String APIで確認でき、substringindexOflengthcharAtなどの仕様がまとまっています。

一般に、文字列は画面表示だけでなく、設定値、ログ、通信データ、ファイルパスなどにも含まれますが、これは押さえたい点です。そのため、Javaで文字列の一部だけを扱えることは、入力チェックやデータ変換の前処理にもつながります。

substringメソッドの基本

substring(int beginIndex)は開始位置から末尾まで、substring(int beginIndex, int endIndex)は開始位置から終了位置の直前までを返します。一方、範囲が不正な場合はStringIndexOutOfBoundsExceptionが発生するため、初心者はlength()との併用を習慣にするとよいでしょう。

項目書き方意味注意点実用例
末尾まで取得substring(4)4番目から最後まで開始位置は0以上接頭辞の除去
範囲取得substring(0, 5)0以上5未満終了位置は含まないIDの一部抽出
位置検索と併用indexOf後に使用文字の場所を基準に切り出す-1の確認が必要区切り文字の前後取得
長さ確認length()文字列長を調べる範囲外の予防に使う入力値チェック
文字取得charAt1文字だけ取り出すサロゲートペアに注意空白判定
大文字化toUpperCase()切り出し後に変換ロケール差に注意表示名の整形
空白除去trim()両端の空白を削る内部の空白は残るフォーム入力の整形
分割split()区切り文字で配列化正規表現として解釈されるCSV風文字列の処理
正規表現Pattern/Matcherパターン一致を扱う一致しない場合の分岐が必要価格や番号の抽出
外部ライブラリStringUtils.substring範囲処理を補助依存関係を管理する堅牢なカスタマイズ
💡 Tips: beginIndexは含まれ、endIndexは含まれません。この半開区間の考え方は、配列、リスト、ループ条件でもよく使われます。

これと近い考え方は、Java List型完全ガイドで扱うListの位置指定にも通じます。文字列でも配列でも、0始まりの位置を意識できると、応用コードの読み取りが速くなるのがポイントです。

substringメソッドの使い方

substringメソッドの使い方は、開始位置だけを渡す形と、開始位置と終了位置を渡す形に分けられます。具体的には、固定長の文字列なら数値を直接書けますが、入力値が変わる場面ではindexOflengthで位置を求めてから切り出す構成が扱いやすくなります。

初心者が最初に混乱しやすいのは、終了位置の直前で切れる点です。そのため、サンプルコードを読むときは、text.substring(4, 7)が「4番目、5番目、6番目」を返すと声に出せるくらいまで確認するとよいでしょう。

具体的には、切り出したい文字列の左端をbeginIndex、右端の次の位置をendIndexに置きますし、これが一つの目安です。この読み方に慣れると、1文字だけ取得するsubstring(i, i + 1)や、末尾だけ除くsubstring(0, text.length() - 1)も同じ規則で理解できます。

これを変数で表すなら、startendcountの関係を分けて考えます。count文字ほしい場合はend = start + countになり、この式を使うと、文字数と終了位置を混同しにくくなるのが一般的です。

サンプルコード1:基本的なsubstringの使い方

この最小例では、固定された文字列から中央の一部だけを切り出します。ただし、元記事の説明にあった取得範囲と出力文字列にはずれがあるため、Javaの仕様に沿ってsubstring(4, 8)Progを得る形に整えます。

public class Main {
    public static void main(String[] args) {
        String text = "JavaProgramming";
        String result = text.substring(4, 8);
        System.out.println(result);
    }
}

結果: 期待される出力はProgです。

このとき、JavaProgrammingの0から3番目まではJavaで、4番目からPが始まります。一方、終了位置の8は含まれないので、4番目から7番目までのProgだけが返ります。

ただし、固定の数値をそのまま書く使い方は、文字列の形式が完全に決まっている場合に向いているのが現実的です。入力値が外部から来る処理では、length()で長さを確認し、短い文字列なら別の戻り値にする設計が必要になります。

サンプルコード2:文字列の途中から取得する方法

そのまま末尾まで取り出す場合は、引数を1個だけ渡します。使い方としては、固定の接頭辞を取り除いて残りを得たいときに合いると整理できます。

public class Main {
    public static void main(String[] args) {
        String text = "JavaProgramming";
        String result = text.substring(4);
        System.out.println(result);
    }
}

結果: 期待される出力はProgrammingです。

これにより、先頭のJavaを除いた残りの文字列を取得できます。ただし、開始位置が文字列長と同じ場合は空文字""になり、文字列長を超えると例外が発生します。

そのため、接頭辞を取り除く処理では、対象文字列が本当にその接頭辞で始まるかをstartsWithで確かめると扱いやすくなると理解できます。条件を満たさない入力まで同じ切り出しを行うと、意味の異なる文字列を返してしまう可能性があります。

サンプルコード3:特定の位置からの取得

位置が固定できない文字列では、indexOfで対象語の開始位置を調べてからsubstringを呼び出します。この組み合わせは、区切り文字やキーワードを基準に値を抜き出す実用例でよく使われますが、覚えておくと役立つでしょう。

public class Main {
    public static void main(String[] args) {
        String text = "JavaProgramming";
        int index = text.indexOf("Pro");
        String result = text.substring(index, index + 3);
        System.out.println(result);
    }
}

結果: 期待される出力はProです。

このコードでは、indexOf("Pro")4を返す想定です。一方、検索語が見つからない場合は-1になるため、実用コードではif (index >= 0)のような分岐を加えると安全性が上がります。

この分岐は、エラーメッセージを返す処理にも、元の文字列をそのまま返す処理にも使えます。どちらを選ぶかは、対象データが必ず含まれる前提なのか、含まれない入力も正常に扱う前提なのかで変わりますし、ここを基本と考えるとよいでしょう。

その判断を曖昧にしたまま共通処理へ入れると、呼び出し元ごとに解釈が分かれます。たとえば検索語がない場合に空文字を返すなら、空文字が本当にデータとして存在した場合との区別も考える必要があります。

同様に、条件分岐や注釈を組み合わせる設計ではJavaアノテーションの12選で扱うメタ情報の考え方も参考になると覚えるとよいでしょう。文字列の切り出し自体は単純でも、周辺の設計によって読みやすさが変わります。

substringメソッドの応用例

基本構文がつかめたら、実用例では他のメソッドと組み合わせる形が中心になります。indexOfで場所を探し、toUpperCaseで表記を変え、splitArrayListで複数データとして扱う流れを知ると、応用の幅が広がりますし、ここがポイントです。

ただし、文字列フォーマットが変わる可能性があるなら、決め打ちの数値だけに頼らないほうが保守しやすくなります。たとえば日付の処理ではLocalDateも候補になり、仕様はOracle Java SE 21 LocalDate APIで確認できます。

実際、応用で差が出るのは、切り出しそのものよりも前後の条件分岐です。検索対象がない、桁数が足りない、余分な空白がある、区切り文字が複数あるといった入力を想定すると、サンプルコードを実務寄りに調整できると考えられます。

こうした入力の揺れは、画面入力、外部ファイル、APIレスポンスなど、データの入口が増えるほど起こりやすくなります。固定値の例で動きを理解した後は、空文字、短い文字列、区切り文字なしの値を想定して、処理方針を明文化すると保守しやすくなります。

サンプルコード4:特定の文字を基に部分文字列を取得

ハイフンやスラッシュなどの区切り文字がある場合、区切り文字の位置までを切り出す処理が使えると言えるでしょう。このサンプルコードでは、最初の-より前にある単語だけを得ます。

public class Main {
    public static void main(String[] args) {
        String text = "Java-is-awesome";
        int index = text.indexOf("-");
        String result = text.substring(0, index);
        System.out.println(result); // 期待される出力は "Java"
    }
}

結果: 期待される出力はJavaです。

この処理では、indexOf("-")が最初のハイフン位置を返し、substring(0, index)がその直前までを取得します。ただし、区切り文字が存在しない文字列ではindex-1になり、範囲エラーにつながります。

そのため、区切り文字より前だけが欲しい処理では、index >= 0の場合だけ切り出し、見つからない場合は元の文字列を返すなどの方針を決めますが、これは押さえたい点です。ログ解析や商品コード処理では、この小さな分岐が障害時の原因追跡を楽にします。

この種の処理では、区切り文字が複数ある場合に最初を使うのか、最後を使うのかも決めます。最初の位置ならindexOf、最後の位置ならlastIndexOfを使い、期待するデータ形式に合わせて選びますし、これが一つの目安です。

たとえばcategory-item-001のような値では、最初のハイフンより前をカテゴリ名として扱う場合と、最後のハイフンより後を番号として扱う場合があります。同じ文字列でも、どの区切りを基準にするかでsubstringの範囲は変わります。

サンプルコード5:文字列の変換と組み合わせて使用

表記をそろえてから切り出すと、比較や表示の揺れを抑えられますが、覚えておくと役立つでしょう。この例では、Javaの文字列を大文字に変換してから、必要な範囲だけを取り出します。

public class Main {
    public static void main(String[] args) {
        String text = "JavaIsAwesome";
        String upperText = text.toUpperCase();
        String result = upperText.substring(4, 6);
        System.out.println(result); // 期待される出力は "IS"
    }
}

結果: 期待される出力はISです。

この場合、toUpperCase()JavaIsAwesome全体が大文字になり、その後にsubstring(4, 6)ISを返します。一方、言語依存の大文字小文字変換が問題になる場面では、Localeを指定する設計も検討対象になります。

逆に、切り出してから変換する構成も選べますし、ここを基本と考えるとよいでしょう。対象範囲が小さい場合は、必要な部分だけにtoUpperCase()を適用できるため、読み手に「どの範囲を加工しているか」が伝わりやすくなります。

サンプルコード6:複数の部分文字列を取得

固定形式の文字列では、複数の範囲を分けて取り出す使い方ができます。日付文字列yyyy-MM-ddのように桁数が決まっている場合、年、月、日を別々の変数に分けやすい構造になるのが基本です。

public class Main {
    public static void main(String[] args) {
        String text = "2022-09-24";
        String year = text.substring(0, 4);
        String month = text.substring(5, 7);
        String day = text.substring(8, 10);

        System.out.println("年: " + year);  // 期待される出力は "年: 2022"
        System.out.println("月: " + month); // 期待される出力は "月: 09"
        System.out.println("日: " + day);   // 期待される出力は "日: 24"
    }
}

結果: 期待される出力は年: 2022月: 09日: 24です。

このサンプルコードでは、年をsubstring(0, 4)、月をsubstring(5, 7)、日をsubstring(8, 10)で取得します。ただし、日付として計算したい場合は文字列のまま扱わず、LocalDate.parseに渡すほうが適する場面もあります。

具体的には、表示用の分割だけならsubstringで十分な場合があるのが目安です。一方、月末日、うるう年、日付の大小比較まで扱うなら、文字列ではなく日付型へ変換したほうが誤判定を避けやすくなります。

これと似た分岐処理は、Javaでうるう年を判定する解説でも扱う日付ロジックと関係します。文字列から値を抜き出した後に、暦として妥当かどうかを判定する流れまで考えると、実用例としての完成度が上がりますし、ここがポイントです。

サンプルコード7:正規表現との組み合わせ

文字列内のどこに対象データがあるかわからない場合、正規表現で候補を抽出してからsubstringを使う方法があります。この応用では、PatternMatcherで価格の数値部分を取り出し、小数点より前だけを得ます。

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        String text = "Price: $25.50";
        Pattern pattern = Pattern.compile("\d+\.?\d*");
        Matcher matcher = pattern.matcher(text);

        if (matcher.find()) {
            String price = matcher.group();
            String dollarPart = price.substring(0, price.indexOf("."));
            System.out.println("ドル部分: " + dollarPart); // 期待される出力は "ドル部分: 25"
        }
    }
}

結果: 期待される出力はドル部分: 25です。

このコードでは、matcher.find()が真になった場合だけmatcher.group()を読み取ります。一方、小数点がない価格文字列ではprice.indexOf(".")-1になるため、整数価格も扱うなら分岐を追加する必要があります。

これらの処理では、正規表現で「候補を探す役割」と、substringで「候補の一部を切り出す役割」が分かれているのがポイントです。役割を分けておくと、価格表記が変わったときに正規表現だけを直すのか、切り出し範囲も変えるのかを判断しやすくなります。

⚠️ 注意: indexOfの戻り値が-1のままsubstringに渡ると例外になります。区切り文字を基準にする実用例では、見つからない場合の処理を先に決めておきますが、これは押さえたい点です。

サンプルコード8:大文字・小文字の変換との組み合わせ

単語単位で表記を変えたい場合も、範囲を切り分けてから変換できます。この例ではhello worldを2つの単語に分け、それぞれを大文字化してから連結します。

public class Main {
    public static void main(String[] args) {
        String sentence = "hello world";
        String firstWord = sentence.substring(0, 5).toUpperCase();
        String secondWord = sentence.substring(6, 11).toUpperCase();

        String result = firstWord + " " + secondWord;
        System.out.println(result);  // 期待される出力は "HELLO WORLD"
    }
}

結果: 期待される出力はHELLO WORLDです。

この処理では、sentence.substring(0, 5)hellosentence.substring(6, 11)worldを返します。ただし、空白位置が変わる入力には弱いため、入力が固定でないならsplit(" ")や正規表現を使う設計も候補になります。

基本的に、固定位置で切り出す方法は、教材や固定フォーマットのデータに向いているのが一般的です。ユーザー入力のように単語数や空白数が変わる文字列では、位置を探してから切り出すか、分割処理に任せるほうが自然です。

サンプルコード9:trimメソッドとの連携

フォーム入力やCSV由来の文字列では、前後に不要な空白が混ざることがあります。その空白を残したまま位置を数えると期待とずれるため、trimで両端を整えてからsubstringを使います。

public class Main {
    public static void main(String[] args) {
        String text = "  Java Programming  ";
        String trimmed = text.trim();
        String target = trimmed.substring(5, 14);

        System.out.println("取得した部分文字列: " + target);  // 期待される出力は "取得した部分文字列: Programmi"
    }
}

結果: 期待される出力は取得した部分文字列: Programmiです。

そのため、trimmedにはJava Programmingが入り、5番目から13番目までが切り出されます。一方、trim()は文字列の両端だけを対象にするため、単語間の空白やタブを整える用途では別の処理が必要です。

この注意点を見落とすと、見た目には同じ文字列でもインデックスがずれます。入力値を受け取った直後にtrim()strip()を使うか、空白も意味を持つデータとして残すかを決めてから切り出すと、後続処理の意図が明確になるのが現実的です。

一方、パスワード、コード、固定長データのように空白そのものが意味を持つ入力もあります。その場合は安易に空白を削らず、空白を含めたまま長さを確認してからsubstringを使うほうが、データの意味を壊しにくくなります。

サンプルコード10:配列やリストとの連携

配列やリストと連携すると、文字列の各要素を同じルールで加工できると整理できます。たとえば、カンマ区切りの文字列をsplitで配列化し、各単語の先頭だけを大文字にする処理は、一覧データの整形に近い実用例です。

public class Main {
    public static void main(String[] args) {
        String str = "apple,banana,cherry";
        String[] fruits = str.split(",");

        for (int i = 0; i < fruits.length; i++) {
            // 各要素の先頭文字だけを大文字にする
            String firstLetter = fruits[i].substring(0, 1).toUpperCase();
            String restLetters = fruits[i].substring(1);
            fruits[i] = firstLetter + restLetters;
        }

        for (String fruit : fruits) {
            System.out.println(fruit);
        }
    }
}

結果: 期待される出力はAppleBananaCherryです。

このサンプルコードでは、substring(0, 1)で先頭文字を取り出し、substring(1)で残りの文字列を得ています。ただし、空文字が配列に含まれるとsubstring(0, 1)で例外になるため、isEmpty()の確認を入れると入力の揺れに対応しやすくなります。

一方、リストに単語をためる場合はArrayListを使えると理解できます。Javaのオーバーライド解説のように、クラス設計を広げる場面でも、データを扱いやすい単位へ分解する発想が土台になります。

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        String sentence = "I love Java";
        ArrayList<String> words = new ArrayList<>();

        int start = 0;
        for (int end = 0; end < sentence.length(); end++) {
            if (sentence.charAt(end) == ' ') {
                words.add(sentence.substring(start, end));
                start = end + 1;
            }
        }
        words.add(sentence.substring(start));  // 最後の単語を追加

        System.out.println("ArrayListの内容: " + words.toString());
    }
}

結果: 期待される出力はArrayListの内容: [I, love, Java]です。

このコードでは、空白文字を見つけた位置をendとして、直前のstartからend未満までを単語として追加します。その後、ループ終了後に残った最後の単語をwords.addで格納し、配列ではなく可変長のリストとして扱います。

ただし、この例は空白1文字だけを区切りとして扱いると覚えるとよいでしょう。連続する空白、タブ、改行を含む入力では、split("\s+")Scannerを使う方法もあり、対象データの形式に合わせて選ぶ必要があります。

substringメソッドの注意点と対処法

substringメソッドの注意点は、範囲指定の誤りがすぐ例外につながる点です。初心者が安全に使うには、beginIndexendIndexlength()の関係を確認し、検索結果の-1や空文字をそのまま渡さない設計が必要になります。

公式ドキュメントによれば、範囲外のインデックス指定ではIndexOutOfBoundsException系の例外が発生すると考えられます。そのため、例外で止める設計にするのか、事前チェックで避ける設計にするのかを用途ごとに決めると、実用例として扱いやすくなります。

一般に、ライブラリ内部やバリデーション層では不正入力を早めに検出し、画面表示やログ整形では代替文字列を返すなど、層ごとに扱いを変えます。注意点を例外名だけで覚えるのではなく、どの層で吸収するかまで考えると設計に落とし込みやすくなると言えるでしょう。

この考え方は、単体の文字列処理だけでなく、メソッドの戻り値設計にも関わります。不正な範囲を受け取ったときにnullを返すのか、空文字を返すのか、例外を投げるのかを統一しておくと、呼び出し側の分岐も読みやすくなります。

文字列の範囲外へのアクセス

範囲外アクセスは、終了位置が文字列長を超えた場合や、開始位置が終了位置より大きい場合に起こりますし、これが一つの目安です。このサンプルコードでは、5文字のhelloに対して10番目までを求めているため、例外をcatchで処理します。

public class Main {
    public static void main(String[] args) {
        String str = "hello";
        try {
            String sub = str.substring(3, 10);
        } catch (StringIndexOutOfBoundsException e) {
            System.out.println("文字列の範囲外にアクセスしました");
        }
    }
}

結果: 期待される出力は文字列の範囲外にアクセスしましたです。

これにより、例外発生時もプログラム全体の異常終了を避けられます。ただし、正常系として頻繁に起こる入力不備なら、tryに頼るよりif (end <= str.length())で事前に分岐するほうが読みやすい場合があります。

このとき、catch内で単にメッセージを出すだけでなく、呼び出し元へ失敗を伝える戻り値も検討するのが基本です。たとえばOptional<String>を返す、空文字を返す、例外を再送出するなど、呼び出し側が誤って正常値として扱わない形が望まれます。

そのため、学習用のサンプルコードではtrycatchで例外の種類を知り、実用コードでは事前条件を満たすかどうかを先に判定する流れが扱いやすくなります。例外処理は失敗時の保険であり、入力チェックとは役割が異なるのが目安です。

負の開始位置や終了位置の取り扱い

負の値はJavaの文字列インデックスとして扱えません。配列やリストと同じく、文字位置は0から始まるため、-1を開始位置にすると例外になります。

public class Main {
    public static void main(String[] args) {
        String str = "Java";
        try {
            String sub = str.substring(-1, 2);
        } catch (StringIndexOutOfBoundsException e) {
            System.out.println("負の位置を指定しました");
        }
    }
}

結果: 期待される出力は負の位置を指定しましたです。

この注意点は、indexOfの戻り値を使うコードでも関係します。検索対象が見つからないと-1になるため、if (index == -1)でエラーメッセージを返すか、既定値を返すかを決めてからsubstringを呼び出します。

逆に、末尾から数えたいという理由で負の値を使いたくなる場面もあるのがポイントです。Javaの標準substringはその書き方を受け付けないため、末尾基準の位置はtext.length() - nのように正のインデックスへ変換してから渡します。

ℹ️ 補足: 範囲チェックは0 <= startstart <= endend <= text.length()の関係で整理できます。条件をメソッド化すると、複数箇所で同じ注意点を扱いやすくなるのが一般的です。

特殊文字や全角文字の取扱い

Stringのインデックスは、内部的なUTF-16コード単位を基準にします。ひらがなや一般的な全角文字なら直感どおり扱えるケースが多い一方、絵文字や一部の記号では見た目の1文字とcharの単位が一致しない場合があります。

public class Main {
    public static void main(String[] args) {
        String str = "こんにちは";
        String sub = str.substring(0, 2);
        System.out.println("部分文字列: " + sub);
    }
}

結果: 期待される出力は部分文字列: こんです。

この例では、ひらがなの先頭2文字が自然に切り出されますが、覚えておくと役立つでしょう。ただし、ユーザー名やメッセージ本文のように絵文字を含む可能性がある文字列では、codePointAtoffsetByCodePointsNormalizerなどを検討すると安全に近づきます。

そのため、多言語入力を扱う画面では、見た目の文字数制限とString.length()の値が一致するとは限らない点を意識します。表示幅、文字数、保存可能なバイト数は別の基準なので、substringだけで入力制限を完結させないほうがよい場合があるのが現実的です。

同様に、エスケープ文字やバックスラッシュを含む文字列では、表示上の文字とソースコード上の表記がずれる場合があります。Javaエスケープ処理の解説と合わせて読むと、文字列リテラルの扱いを整理できます。

カスタマイズ方法

標準のsubstringだけで足りない場合、外部ライブラリを使う方法と、独自メソッドでルールを包む方法があると整理できます。カスタマイズでは、呼び出し側に毎回範囲チェックを書かせるのではなく、切り出しの方針を1か所に集めると保守しやすくなります。

ただし、標準APIで十分な処理に外部依存を増やすと、ビルド設定や脆弱性管理の対象も増えます。そのため、用途が単純なら独自の小さなメソッド、複数の文字列処理を広く扱うならライブラリという使い分けが現実的です。

具体的には、同じ範囲チェックが複数クラスに散らばり始めたら、カスタマイズ用のユーティリティを用意する候補になると理解できます。逆に、1か所だけの処理なら、標準メソッドと短い条件分岐で済ませたほうが読みやすいこともあります。

これらのユーティリティを作る場合は、名前に意図を含めると扱いやすくなります。たとえばsafeSubstringなら範囲外を吸収する印象になり、requiredSubstringなら不正な入力を拒否する印象になるため、名前と実際の挙動をそろえますし、ここを基本と考えるとよいでしょう。

外部ライブラリを使った拡張

Apache Commons LangのStringUtilsには、文字列処理を補助するメソッドが用意されています。公式情報はApache Commons Lang StringUtils APIで確認できます。

import org.apache.commons.lang3.StringUtils;

public class CustomSubstring {
    public static void main(String[] args) {
        // 元の文字列
        String original = "abcdefg";

        // 部分文字列の取得
        String part = StringUtils.substring(original, 2, 5);

        // 結果の表示
        System.out.println("取得した部分文字列: " + part);
    }
}

結果: 期待される出力は取得した部分文字列: cdeです。

このサンプルコードでは、StringUtils.substring(original, 2, 5)によって2番目から4番目までの文字が返ります。一方、外部ライブラリを使うにはMavenpom.xmlGradlebuild.gradleに依存関係を追加する必要があります。

この方法の利点は、文字列操作の細かな補助処理を共通のAPIとして扱える点にあると覚えるとよいでしょう。ただし、チームやプロジェクトで依存ライブラリを制限している場合もあるため、標準APIで書く版とライブラリ版の差を説明できる状態にしておくと導入判断がしやすくなります。

一方、外部ライブラリの挙動は標準APIと完全に同じとは限りません。特にnull入力、負の位置、終了位置が長さを超える場合の扱いは、公式APIの説明を確認してから使う必要があります。

⚠️ 注意: ライブラリの導入は、バージョン管理、ライセンス確認、セキュリティ更新の対象を増やすると考えられます。小さなカスタマイズなら、標準APIで完結する設計も比較します。

substringメソッドをオーバーライドする方法

厳密には、JavaのStringクラスはfinalであり、標準のsubstringメソッドをオーバーライドすることはできません。見出しの意図としては、独自のcustomSubstringメソッドを作り、標準処理の前後にルールを追加する方法と理解するのが正確です。

public class CustomSubstring {

    // substringメソッドのカスタマイズ版
    public static String customSubstring(String original, int start, int end, String filler) {
        if (end > original.length()) {
            int shortage = end - original.length();
            StringBuilder builder = new StringBuilder(original);
            for (int i = 0; i < shortage; i++) {
                builder.append(filler);
            }
            original = builder.toString();
        }
        return original.substring(start, end);
    }

    public static void main(String[] args) {
        // 元の文字列
        String str = "Javaプログラミング";

        // カスタマイズ版substringメソッドの使用
        String result = customSubstring(str, 2, 9, "*");

        // 結果の表示
        System.out.println("取得した部分文字列: " + result);
    }
}

結果: 期待される出力は取得した部分文字列: vaプログラです。

このコードでは、endが元の文字列長を超えた場合にStringBuilderで不足分を埋める構成に直しています。元記事のままだとendを文字列長に縮めてからループするため、不足分を追加できない論理になりやすい点が注意点です。

そのため、独自メソッドを作るときは、引数の意味を先に固定します。startendが元文字列基準なのか、補正後の文字列基準なのかを曖昧にすると、呼び出し側が期待する結果とずれますし、ここがポイントです。

これらのカスタマイズは、入力値の不備を許容して補正するのか、不正として拒否するのかで設計が変わります。一般に、業務データを扱う処理では、補正内容が後から追えるように、戻り値だけでなくエラー理由やログ方針も合わせて決めると運用しやすくなります。

カスタマイズしたメソッドを共有する場合は、引数の境界値を単体テストで固定しておくと安心です。startが0、endが文字列長と同じ、空文字、短すぎる文字列、補完文字を含む入力を確認対象にすると、仕様変更時の影響を見つけやすくなると言えるでしょう。

まとめ

Javaのsubstringメソッドは、文字列から必要な範囲だけを取り出すための標準的な手段です。使い方の中心は、beginIndexを含め、endIndexを含めないという半開区間の理解にあります。

その理解ができると、固定長の文字列、区切り文字つきのデータ、日付、価格、配列、ArrayListなどの実用例へ応用できます。ただし、indexOf-1、負の値、文字列長を超える範囲、絵文字を含むUnicode文字列には注意点があるのが基本です。

そのため、サンプルコードをそのまま覚えるより、length()で範囲を確認し、StringIndexOutOfBoundsExceptionを避ける分岐を組み合わせる考え方が役立ちます。カスタマイズが必要な場合も、標準API、Apache Commons Lang、独自メソッドのどれが適するかを入力条件から選ぶと、Javaの文字列処理を安定して書けます。

これで、初心者が迷いやすいインデックスの境界、メソッド連携、応用時の例外処理、カスタマイズの判断材料までを一通り整理できるのが目安です。substringを使う場面では、切り出したい内容だけでなく、見つからない場合や短すぎる入力の扱いまで同時に考えると、実用コードに近づきます。

特に押さえたいのは、切り出し範囲を決める根拠をコード上に残すことです。固定位置ならコメントや変数名で意味を示し、検索位置ならindexOfの失敗時の分岐を置くことで、後から読む人が処理の意図を追いやすくなります。

一般に、文字列処理は小さな処理に見えて、入力の揺れが集まりやすい領域です。短いサンプルコードで構文を覚えた後は、範囲外、空文字、区切り文字なし、全角文字を含む入力を確認しながら、自分の用途に合わせて使い方を調整するとよいでしょう。

その確認を習慣化すると、短い文字列操作でも仕様の抜けを早く見つけられますが、これは押さえたい点です。substringは単体で覚えるより、検索、検証、変換、例外処理と合わせて読むほうが、実際の保守で役立つ知識になります。

ただし、すべての文字列処理をsubstringで解決する必要はありません。区切り文字で分けるならsplit、一致判定ならmatches、置換ならreplacereplaceAllが向くため、目的に合うメソッドを選ぶ視点も欠かせません。

使い方を選ぶ基準は、位置が決まっているか、目印となる文字があるか、文字列全体の形式を検証したいかで変わります。位置が決まっているならsubstring、目印があるならindexOfとの組み合わせ、形式全体を見るなら正規表現という整理が実践的です。

その整理を持っておくと、短い処理でも過剰なカスタマイズを避けられますし、これが一つの目安です。目的に合う標準メソッドを選び、足りない部分だけを補う姿勢が、読みやすい文字列処理につながります。

関連記事

著者: Japanシーモア編集部

Japanシーモアは、Web/IoT/APP/SYS 分野のプログラミング情報を体系的に提供するメディアです。本記事は編集部による執筆とAI支援を組み合わせて制作し、公開前に編集部が校正しています。誤りや改善案がございましたらお問い合わせよりご連絡ください。

※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。