Java內部類使用總結
一、內部類的定義和作用
將一個類定義在另一個類裡面或者方法裡面的類成為內部類。
作用和優點:每個內部類都能獨立的繼承一個介面的實現,所以無論外部類是否繼承了某個介面的實現,對於內部類都沒有影響。內部類使得多繼承的解決方案變得完整。彌補了單繼承的缺點
二、內部類的用法
1.成員內部類
成員內部類即為外部類的成員,非靜態的。可以直接訪問外部類的所有成員和方法,當有和內部類重名時需要用外部類.this.屬性或方法。外部類訪問內部類需要建立一個內部類的物件才可以訪問。PS:成員內部類不能含有static的修飾符。因為內部類屬於非靜態的,先要建立外部類,才能建立它自己。成員內部類的修飾符可以是private,預設的,protected,public或者是靜態的。和外部類(public和default)不同
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.getInner();
inner.print("成員內部類第一次列印");
inner = outer.new Inner();
inner.print("這是一成員內部類");
}
public Inner getInner(){
return new Inner();
}
public static void printOuter(){
System.out.println("這是一個外部類");
}
public class Inner{
public void print(String str){
getInner();
Outer.this.printOuter();
System.out.println(str);
}
}
}
2.區域性內部類
區域性內部類定義在方法或作用域內部。
public class Part {
public Destination print(String str){
class a implements Destination{
private String label;
private a(String label){
this.label = label;
}
public String read() {
return label;
}
}
return new a(str);
}
public static void main(String[] args){
System.out.println(new Part().print("這是一區域性內部類的事例").read());
}
}
3.靜態內部類
靜態內部類可以讓外部類隨意訪問,無需建立內部類的物件。靜態內部類不能訪問外部類的非靜態成員,因為非靜態成員必須是物件存在的時候,所以有矛盾。
public class Singleton2 {
// 提供一私有建構函式
private Singleton2(){};
public Integer i;
public static Singleton2 getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder{
private static final Singleton2 instance = new Singleton2();
}
}
4.匿名內部類
匿名內部類也可以進行例項初始化,雖然沒有建構函式
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
System.out.println(inner.getProvince());
}
public Inner getInner(final String name, final String city){
// 匿名內部類中使用
return new Inner() {
private String namString = name;
private String province;
// 例項初始化
{
if(city.equals("gz")){
province = "gd";
}else {
province = "";
}
}
public String getName() {
return namString;
}
public String getProvince() {
return province;
}
};
}
interface Inner{
String getName();
String getProvince();
}
}
5.內部類的繼承
注意的是繼承內部類的類的建構函式預設傳遞外部類引數,並呼叫super方法,不然會有編譯錯誤。
class InheritInner extends Outer.Inner {
public InheritInner(Outer outer){
outer.super();
}
public static void main(String[] args){
new InheritInner(new Outer()).print("這是內部類繼承");
}
}
三、注意的問題
1.為什麼成員內部類可以無條件的訪問外部類成員:
首先要明確一點的是內部類和外部類是兩份位元組碼,即Outer.class, Outer$Inner.class.
通過反編譯位元組碼可以看出,內部類的構造器是無參建構函式,編譯器會預設新增一個引數,改引數的型別為指向外部類物件的一個引用,所以成員內部類中的Outter this&0 指標便指向了外部類物件,因此可以在成員內部類中隨意訪問外部類的成員。
2.為什麼匿名內部類和區域性內部類只能訪問區域性的final。
public class Test {
public static void main(String[] args) {
}
public void test(final int b) {
final int a = 10;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
}
}
如上面的程式碼,因為是當test方法執行完畢之後,變數a的生命週期就結束了,而此時Thread物件的生命週期很可能還沒有結束,那麼在Thread的run方法中繼續訪問變數a就變成不可能了,但是又要實現這樣的效果,怎麼辦呢?Java採用了 複製,也就說如果區域性變數的值在編譯期間就可以確定,則直接在匿名內部裡面建立一個拷貝。如果區域性變數的值無法在編譯期間確定,則通過構造器傳參的方式來對拷貝進行初始化賦值。 的手段來解決這個問題。複製的手段會帶來一個問題,會導致資料不一致,所以採用final為不可變