Java内部クラスについて

5479 ワード

一.Java値の伝達と参照の伝達
「Javaではパラメータ転送はすべて値転送です」つまり、値転送は転送された値のコピーであり、参照転送は参照されたアドレス値であるため、総称して値転送と呼ばれます.
簡単に言えば、基本データ型は値によって伝達され、方法の実パラメータは元の値の複本である.クラスオブジェクトがオブジェクトの参照アドレス(メモリアドレス)によってアドレスの値を渡す場合、メソッド内でこのオブジェクトを変更すると元のオブジェクトに直接反応します(または、この2つの参照は同じメモリアドレスを指します).Stringも値伝達であることに注意してください.
public static void main(String[] args) { 
    String x = new String("now"); 
    User user = new User("now");
    change(x); 
    System.out.println("1:"+x);
    change(user);
    System.out.println("2:"+user.name);
     User user1 = new User("now");
     System.out.println("3:"+user1 .name);
}
public static void change(String x) { 
    x = "even";
}
public static void change(User x) { 
    x.name = "even";
}
public static void change(User x) { 
    x = new User("even");
}

結果
1:even
2:even
3:now

二.本題に戻り、Javaの内部クラスについて話します.
Javaは1つのクラスで別のクラスを定義することを許可します.クラスの中のクラスは内部クラスで、ネストクラスとも呼ばれます.
簡単な内部クラスは次のとおりです.
class OuterClass { 
    class InnerClass{ 
    }
}

よく使用される内部クラス:
public class PrivateToOuter { 
    Runnable mRunnable = new Runnable(){ 
        private int x=10; 
        @Override 
        public void run() { 
            System.out.println(x); 
        } 
    }; 
}


以上のコードmRunnableは内部クラスとは思えません.InnerClassのようなイメージではありませんが、実は以下のハンドルはRunableを継承するクラス、つまりカスタムクラスを実現しています.それは明らかに内部クラスです.実はそれは内部クラスの一種に属しています:匿名の内部クラスAnonymous Inner Class
{ 
        private int x=10; 
        @Override 
        public void run() { 
            System.out.println(x); 
        } 
}

内部クラスでは、次の点に注意してください.
(1)内部クラスの取得
内部クラスは、プライベートメソッドを含む外部クラスをパッケージするすべてのメソッド、メソッド、およびプロパティにアクセスできます.しかし、他のクラスにとっては、同じパッケージの下の他のクラスにとっても、内部クラスは隠されています.つまり、内部クラスにアクセスするには、まず彼の外部パッケージクラスを取得し、そのハンドルを取得してからnew内部クラスを取得する必要があります.次のように、彼の方法を呼び出すことができます.
OuterClass outer = new OuterClass();
InnerClass innerClass = outer.new InnerClass();

したがって,内部クラスは単一継承の問題をうまく解決できる.privateがこの論理でなぜ失効したのかを調べると、Java:「失効」のprivate修飾子を参照できます.
(2)静的内部クラス
staticで内部を静的にすると、内部クラスは外部クラスの静的メンバー変数にのみアクセスでき、限界があります.
class Out {
    private static int age = 12;
    private int time = 14;
    static class In {
        public void print() {
            System.out.println(age);
        }
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Out.In in = new Out.In();
        in.print();
    }
}

つまりinはtimeではなくageにしかアクセスできません
(3)プライベート内部クラス
内部クラスが外部クラスのメソッドでのみ操作されることを望む場合はprivateを使用して内部クラスを宣言できます.
class Out {
    private int age = 12;
     
    private class In {
        public void print() {
            System.out.println(age);
        }
    }
    public void outPrint() {
        new In().print();
    }
}
 
public class Demo {
    public static void main(String[] args) {
        //     
        /*
        Out.In in = new Out().new In();
        in.print();
        */
        Out out = new Out();
        out.outPrint();
    }
}

特に皆さんに伝えたいポイントは、
内部クラスが構築されると、外部クラスの参照が渡され、内部クラスの属性として使用されるため、内部クラスは外部クラスの参照を保持します.
Javaでは、非静的な内部クラスと匿名の内部クラスが外部クラスの参照を暗黙的に保持します.静的な内部クラスは、外部クラスの参照を保持しません.したがってpoint 3の内部クラスは外部クラスのメンバー変数とメソッドを取得できない.
androidに戻る:
public class SampleActivity extends Activity { 
    private final Handler handler= new Handler() { 
        @Override 
        public void handleMessage(Message msg) { 
            // ...  
        } 
    }
}

これはhandlerインスタンスを取得するためによく行われる操作ですが、pointに戻ると、handlerの匿名の内部クラスにSampleActivityの参照があることがわかります.そのため、OOMのリスクを引き起こす可能性があります.例えば、次のコードです.
public class SampleActivity extends Activity { 
    private final Handler mLeakyHandler = new Handler() { 
        @Override 
        public void handleMessage(Message msg) { // ... } 
    } 
    @Override 
    protected void onCreate(Bundle savedInstanceState) {         
        super.onCreate(savedInstanceState); 
        mLeakyHandler.postDelayed(new Runnable() { 
            @Override 
            public void run() { /* ... */ } 
        }, 1000 * 60 * 10); 
        finish(); 
    }
}

SampleActivityオブジェクトはfinishされていますが、mLeakyHandlerのメッセージは10分後に実行されます.Handlerは匿名の内部クラスのインスタンスです.外部のSampleActivityの参照を持っている(かつRunableも匿名の内部クラスであり、その内部もSampleActivityの参照を持っている)ため、SampleActivityが回収できず、SampleActivityが持っている多くのリソースが回収できないことを行っている.これが私たちがよく言うメモリリークである.解決方法は以下の通りである.
public class SampleActivity extends Activity { 
    private static class MyHandler extends Handler { 
        private final WeakReference mActivity; 
        public MyHandler(SampleActivity activity) { 
            mActivity = new WeakReference(activity); 
        } 
        @Override 
        public void handleMessage(Message msg) { 
            SampleActivity activity = mActivity.get(); 
            if (activity != null) { // ... } 
        } 
    } 
    private final MyHandler mHandler = new MyHandler(this); 
    private static final Runnable sRunnable = new Runnable() { 
        @Override 
        public void run() { /* ... */ } 
    };
    @Override 
    protected void onCreate(Bundle savedInstanceState) {     
        super.onCreate(savedInstanceState); 
        mHandler.postDelayed(sRunnable, 1000 * 60 * 10); 
        finish(); 
    }
}