On appendReplacement/Tail
You'd have to use appendReplacement and appendTail explicitly. Unfortunately you have to use StringBuffer to do this. Here's a snippet (see also in ideone.com):
    String content="aaaaaaaaaa";
    Pattern pattern = Pattern.compile("a");
    Matcher m = pattern.matcher(content);
    StringBuffer sb = new StringBuffer();
    final int N = 3;
    for (int i = 0; i < N; i++) {
      if (m.find()) {
         m.appendReplacement(sb, "b");
      } else {
         break;
      }
    }
    m.appendTail(sb);
    System.out.println(sb); // bbbaaaaaaa
See also
Another example: N times uppercase replacement
Here's another example that shows how appendReplacement/Tail can give you more control over replacement than replaceFirst/replaceAll:
// replaces up to N times with uppercase of matched text
static String replaceUppercase(int N, Matcher m) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < N; i++) {
        if (m.find()) {
            m.appendReplacement(
                sb,
                Matcher.quoteReplacement(m.group().toUpperCase())
            );
        } else {
            break;
        }
    }
    m.appendTail(sb);
    return sb.toString();
}
Then we can have (see also on ideone.com):
    Pattern p = Pattern.compile("<[^>]*>");
    Matcher m = p.matcher("<a> b c <ddd> e <ff> g <$$$> i <jjj>");
    System.out.println(replaceUppercase(4, m));
    // <A> b c <DDD> e <FF> g <$$$> i <jjj>
    //  1        2       3      4
The pattern <[^>]*> is just a simple example pattern that matches "<tags like this>".
Note that Matcher.quoteReplacement is necessary in this particular case, or else appending "<$$$>" as replacement would trigger IllegalArgumentException about an illegal group reference (because $ unescaped in replacement string is a backreference sigil).
On replaceFirst and replaceAll
Attached is the java.util.regex.Matcher code for replaceFirst and replaceAll (version 1.64 06/04/07). Note that it's done using essentially the same appendReplacement/Tail logic:
// Excerpt from  @(#)Matcher.java   1.64 06/04/07
public String replaceFirst(String replacement) {
    if (replacement == null)
        throw new NullPointerException("replacement");
    StringBuffer sb = new StringBuffer();
    reset(); // !!!!
    if (find())
        appendReplacement(sb, replacement);
    appendTail(sb);
    return sb.toString();
}
public String replaceAll(String replacement) {
    reset(); // !!!!
    boolean result = find();
    if (result) {
        StringBuffer sb = new StringBuffer();
        do {
            appendReplacement(sb, replacement);
            result = find();
        } while (result);
        appendTail(sb);
        return sb.toString();
    }
    return text.toString();
}
Note that the Matcher is reset() prior to any replaceFirst/All. Thus, simply calling replaceFirst 3 times would always get you the same result (see also on ideone.com):
        String content="aaaaaaaaaa";
        Pattern pattern = Pattern.compile("a");
        Matcher m = pattern.matcher(content);
        String result;
        result = m.replaceFirst("b"); // once!
        result = m.replaceFirst("b"); // twice!
        result = m.replaceFirst("b"); // one more for "good" measure!
        System.out.println(result);
        // baaaaaaaaa
        // i.e. THIS DOES NOT WORK!!!
See also