Javaプログラミング:finalのキーワードを深く理解する


javaにおけるfinalキーワードの意味は最終的であり、可変ではないという意味で、finalキーワードはクラス、方法、変数(属性、局部変数、変形)を修飾するために用いられます。以下、詳細に説明します。もし不正があれば、ご指摘ください。
修飾類
finalで修飾されたクラスは引き継がれません。すなわち自分のサブクラスを持つことができません。例えばjava.lang.String、sun.misc.Unisafeなどは全部finalの修飾の種類です。
public final class Animal {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
final修飾のクラスを継承しようとすると、コンパイル中にエラーが発生します。
public class User extends Animal {
    private String sex;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}
エラーメッセージ:Cannot inherit from final "org.learn.finalkey.Animal"
修飾の方法
final修飾の方法は書き換えられません。final修飾の方法のアクセス権限はprvateに設定するべきです。
final修飾の方法はなぜ書き換えられないですか?
親の方法がfinalによって修正された場合、この方法のアクセス権限はprvateに設定されるべきであり、このような方法は継承できないので、書き換えの可能性はない。サブクラスでは、親の名前と同じパラメータの方法を定義することができます。この方法は、サブクラス定義の新しい方法であり、親には関係なく書き換えに関係ありません。
例1:親の方法(getName)はfinalで修飾されていません。
public class Animal {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
子類は正常に親類を継承する方法で、この方法を書き直します。
public class User extends Animal {

    public String getName() {
        System.out.println("before getting");
        return super.getName();
    }

    public void setName(String name) {
        System.out.println("before setting");
        super.setName(name);
        System.out.println("after setting");
    }
}
例二:親の方法はpublic finalによって修正されます。
public class Animal {
    private String name;

    public final String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
子類が正常に親類を継承する方法。
public class User extends Animal {

    public String getName() {
        System.out.println("before getting");
        return super.getName();
    }

    public void setName(String name) {
        System.out.println("before setting");
        super.setName(name);
        System.out.println("after setting");
    }
}
この時、コンパイラはエラーを報告します。エラーメッセージ:'getName()' cannot be override 'getName()' in 'org.learn.finalkey.Animal': overridden method is final
例3:親の方法(getName)がprvate finalによって修正される。
public class Animal {
    private String name;

    private final String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
子類が正常に親類を継承する方法。
public class User extends Animal {

    public String getName() {
        System.out.println("before getting");
        return "name";
    }

    public void setName(String name) {
        System.out.println("before setting");
        super.setName(name);
        System.out.println("after setting");
    }
}
サブクラスのgetNameメソッドは、サブクラスだけで定義された新しい方法であり、親には関係なく書き換えに関係ない。従って,final法はクラス内でのみ使用されると推測できる。
修飾変数
final修飾の変数値は、最初の初期化後に変更されてはいけない。Java文法の規定:final修飾の属性、変数は表示初期化を行わなければなりません。
初期化の方式
  • 定義の時初期化する。
  • 構築関数から初期化する。
  • スタティックブロックに初期化する。
  • 修飾されたデータタイプ
  • 基本データタイプ:finalは基本データタイプを修飾し、この変数の値は変更できない。
  • 参照データタイプ:final修正参照データタイプ、参照変数が指すアドレスは変更できません。このアドレスが指すオブジェクトは変更可能です。
  • 内部クラスの方法では、外部クラスの変数にアクセスしたい場合、この変数はfinalによって修飾されます。(クリックして原因を確認します)
    マクロ変数の置換
    マクロ変数とは、コンパイル段階で特定の変数が直接に自身の値に置き換えられる変数です。Java言語にはマクロ変数のキーワードが定義されていません。場合によってはfinalで修飾された変数は、コンパイル段階で同様のマクロ変数に置き換えられます。例1:final変数を使ってname、sexを修飾していません。
    public class Macro {
    
        public static void main(String[] args) {
    
            String name = "zhibo";
            String sex = "male";
    
            String userInfo1 = name + sex;
            String userInfo2 = "zhibo" + "male";
    
            System.out.println("args1 = [" + userInfo1 + "]");
            System.out.println("args2 = [" + userInfo2 + "]");
    
        }
    }
    コンパイラでコンパイルしたコードは、逆コンパイルツールで復元されます。
    public class Macro {
        public Macro() {
        }
    
        public static void main(String[] args) {
            String name = "zhibo";
            String sex = "male";
            String userInfo1 = name + sex;
            String userInfo2 = "zhibomale";
            System.out.println("args1 = [" + userInfo1 + "]");
            System.out.println("args2 = [" + userInfo2 + "]");
        }
    }
    
    userInfo1          ,     name、sex       ,          name、sex   ,         。
      userInfo2         ,  "zhibo""male"         ,             。
    例2:final変数を使ってname、sexを修飾します。
    public class Macro {
    
        public static void main(String[] args) {
    
            final String name = "zhibo";
            final String sex = "male";
    
            String userInfo1 = name + sex;
            String userInfo2 = "zhibo" + "male";
    
            System.out.println("args1 = [" + userInfo1 + "]");
            System.out.println("args2 = [" + userInfo2 + "]");
    
        }
    }
    コンパイラでコンパイルしたコードは、逆コンパイルツールで復元されます。
    public class Macro {
        public Macro() {
        }
    
        public static void main(String[] args) {
            String name = "zhibo";
            String sex = "male";
            String userInfo1 = "zhibomale";
            String userInfo2 = "zhibomale";
            System.out.println("args1 = [" + userInfo1 + "]");
            System.out.println("args2 = [" + userInfo2 + "]");
        }
    }
    
    userInfo 1は、finalキーを使用してname、sex変数を修飾し、コンパイル段階でname、sexの具体的な値を決定することができるので、マクロの置換を行います。
    まとめ:finalキーワードの修飾変数は、コンパイル段階で直接その値を決定できます。この値は可変ではありません。コンパイル中にその変数を直接自分の値に置き換えることができます。