Java-アクセス権限を破る方法

4380 ワード

本論文は「Javaプログラミング思想」14.9節の読書ノートで、文章の内容は交流のみで使うことができます.
まずインターフェースを使う時のアクセス権限を見ます.
インタフェースのコードを使用してクラス間の結合を回避するために、インタフェースのコードを使用して定義されたインターフェースは主にコード間の分離を実現するために使用される.通常、あるインターフェースを実現したクラスの中で、自分の非をインターフェースから持つ方法は、インターフェースに変換する時、サブクラス自身の別の追加方法を呼び出すことができません.
これは完全に合理的ですが、このような制限を回避するためにタイプ情報を使用することができます.
まず、インターフェースAを定義します.
package com.henevalf.learn.typeinfo

public interface A {
    void f();
}
その後、BクラスでインターフェースAを実現します.後のInterfaceViolationでは、どのように制限を回避するかが見られます.
package com.henvealf.learn.typeinfo;

class B implements A {

    @Override
    public void f() {

    }

    public void g(){

    }
}

public class InterfaceViolation {
    private A a;

    public InterfaceViolation(A a) {
        this.a = a;
    }

    public void invoke() {
        a.f();
        //a.g();
        System.out.println(a.getClass().getName());
        //    !       ,      。。。。   ,    。
        if(a instanceof B) {
            B b = (B)a;
            b.g();
        }
    }

    public static void main(String[] args){
        InterfaceViolation inv = new InterfaceViolation(new B());
    }

}

はい、そうです.invoke()の方法では、強制的なタイプの変換です.インターフェースで定義されていない方法g()を使って、この例ではまずinstance ofでタイプの回転を検出してみました.このような方式を使ったことがあると思います.このようにするのはよくないです.正常に運行することができますが、彼は私達がインターフェースを使う意図に反して、種類のInterfaceViolationとクラスBは無意識の間に結合を増加します.
我慢できない強迫症があったら、あなたのクラスの他のプログラマを使いたくないです.二つの解決方法があります.
  • 彼の前で義正の言葉を彼に教えました.このように使ってはいけません.誰があなたを相手にしますか?
  • 自分でコードの中で制御します.
  • それをどう制御しますか?最も簡単な方法は、実装クラスに対するパケットアクセス権限です.実现クラスを一つのカバンに入れて、実现クラスを设定すると、カバンの中でしかアクセスできないということです.あなたのクラスのプログラマを使って、あなたの実现クラスの存在が见つからないと、変换ができなくなります.コードをご覧ください.
    package com.henvealf.learn.typeinfo.packageaccess;
    
    import com.henvealf.learn.typeinfo.A;
    
    /**
     * Created by Henvealf on 2016/9/10.
     */
    
    class C implements A {
    
        @Override
        public void f() {
            System.out.println("public C.f()");
        }
    
        public void g() {
            System.out.println("public C.g()");
        }
        void u() {
            System.out.println("package C.u()");
        }
        protected void v() {
            System.out.println("protected C.v()");
        }
    
        public void w() {
            System.out.println("private C.w()");
        }
    }
    public class HiddenC {
        public static A makeA(){
            return new C();
        }
    }
    
    
    パッケージ名に注意してください.現在Aの実装クラスCは独立したパッケージの中にあります.このパッケージの中で唯一公開されているpublicはHddenCであり、Cのオブジェクトに戻る静的な方法があります.これでは、パッケージの外部からA以外のいかなる方法でも呼び出すことができません.パッケージの外部にCタイプの情報を見つけることができないので、モデルチェンジができません.
    package com.henvealf.learn.typeinfo;
    
    import com.henvealf.learn.typeinfo.packageaccess.HiddenC;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     *
     * Created by Henvealf on 2016/9/10.
     */
    public class HiddenImplementation {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
            A a  = HiddenC.makeA();
            a.f();
            System.out.println(a.getClass().getName());
            /*if(a instanceof C) {     ,     C
                .....
                C c = (C)a;
                c.g();
            }*/
            
            
            //   ,             g()
            callHiddenMethod(a,"g");
            //          
            callHiddenMethod(a,"u");
            callHiddenMethod(a,"v");
            callHiddenMethod(a,"w");
        }
    
        static void callHiddenMethod(Object a, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            //        (Class  ),     Method  。
            Method g = a.getClass().getDeclaredMethod(methodName);
            //     ,          。
            g.setAccessible(true);
            //  
            g.invoke(a);
        }
    
    }
    
    
    パッケージの外にタイプCが見つけられず、対応する変換ができないことが分かります.これ以外にも、反射によってオブジェクトCを呼び出す方法が見られます.プライベートな方法でも、MethodオブジェクトにsetAccess ibleを呼び出し、その名の通りアクセス権限を設定しているからです.
    このような方法を使うには、クラスCの方法リストを取得しなければならないと思うかもしれませんが、もし私たちが入手したのはクラスCのコンパイルしたバイトコード(.classファイル)だけであれば、javapを使ってバイトコードを逆コンパイルしてからの方法のリストです.
    反射は、パケットアクセスの権限を突破するだけでなく、プライベートクラス、匿名クラスのすべての方法にアクセスすることができます.
    もちろん方法以外にもドメイン(フィールド/属性)に対しても同じですが、ドメインへのアクセスには特殊なフィールドがあります.つまり、finalフィールドは読み取られるだけで、反射によって再び割り当てられません.
    このように反射したらどうしようもないじゃないですか?何でも訪問できます.反射の原因は、プログラマに裏口を提供するために、プログラマが解決しにくい特定のタイプの問題を解決することができるからです.反射がないと、これらの問題は難しくなりますし、解決できないかもしれませんので、反射のメリットは間違いないです.
    End…