I just observed this behavior;
Pattern p1 = Pattern.compile(\"^$\");
Matcher m1 = p1.matcher(\"\");
System.out.println(m1.matches()); /* true */
Pattern p2
Let's look a bit closer at your second example:
Pattern p2 = Pattern.compile("^$", Pattern.MULTILINE);
Matcher m2 = p2.matcher("");
System.out.println(m2.matches()); /* false */
So you have a line in m2, that is empty OR contains only character of endline and no other characters. Therefore you pattern, in order to correspond to the given line, should be only "$" i.e.:
// Your example
Pattern p2 = Pattern.compile("^$", Pattern.MULTILINE);
Matcher m2 = p2.matcher("");
System.out.println(m2.matches()); /* false */
// Let's check if it is start of the line
p2 = Pattern.compile("^", Pattern.MULTILINE);
m2 = p2.matcher("");
System.out.println(m2.matches()); /* false */
// Let's check if it is end of the line
p2 = Pattern.compile("$", Pattern.MULTILINE);
m2 = p2.matcher("");
System.out.println(m2.matches()); /* true */