Javaのクラスにfinalがついていた場合、これ以上拡張、継承は出来ません。
public final class Sample {
となっていた場合、
public class Sample2 extends Sample {
とは出来ません。
また、メソッドにfinalがついていた場合は、
そのサブクラスでoverride出来ません。
変数にfinalがついていた場合は、
初期値から変更できません。
final String str = "abc"; ----①
str = "ABC"; ----②
このようにすると②でエラーが起こります。
①が
final String str; ----③
となっていた場合は、②ではエラーになりません。
しかし、②の後に
str = "DEF";
とするとエラーになってしまいます。
何も入っていないときは、一度だけ値を代入できます。
配列だった場合は、
public final String[] s1 = new String[]{"a","b","c"};
s1[0] = "3"; ----④
String[] s2 = new String[4];
s1 = s2; ----⑤
このようになっていた場合は、
④ではエラーにならず、⑤でエラーになります。
一見④で配列に入っている値を変更しているので、
エラーになるように見えますが、
finalによってロックされるのは参照するアドレスです。
⑤のように参照するアドレスを変更しようとすると
エラーになります。
では、なぜ④がエラーにならないかというと。
配列はオブジェクト型なので、直接値を保持しているのではなく、
アドレスを参照して値を取りにいきます。
④で変更したのは、配列が参照しているアドレスの先にある値です。
そしてfinalによってロックされるのは配列へのアドレスです。
アドレス1を配列へのアドレス、
アドレス2を値へのアドレスとします。
finalによってロックされるのは『アドレス1』です。
なので、『アドレス2』の先にある値は変更することが出来るわけです。