前言
學習Java已經有一段日子了,最近的日子筆者在重新對java進行再學習。不過這個階段的學習
筆者不會再著眼於具體的語法哪些細枝末節的東西了,這個階段的學習中筆者將會對以前學習的
模糊的,遺漏的知識概念做一些相關性的總結。今天,恰好看到內部類這塊了,記得以前對內部類
的使用就有一些模糊,因此專門就內部類做一些總結。
內部類概念
所謂內部類就是指在一個外部類中再定義一個類(內部類),這樣內部類就作為一個成員依附於
外部類而存在。不過在使用內部類的時候需要注意的是內部類可以static,protect,private,但是
外部類只能使用public和缺省的包訪問權限.
若非這兩種編譯出錯:
Illegal modifier for the class Outer; only public, abstract & final are permitted
[java]
package com.kiritor;
public class Outer {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
class Inner {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}
package com.kiritor;
public class Outer {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
class Inner {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}
內部類的意義
簡單看來內部類好像就是一種代碼隱藏機制:將類至於其他類的內部.不過內部類遠不止這樣
它了解外部類,能夠和外部類進行必要的通信。
1、封裝一些別人不想知道的操作.
2、內部類可以訪問創建它的外部類對象的內容,甚至包括了private的變量.
內部類同樣可以實現接口,繼承基類,這使得java中的多繼承變得完整.。我們可以使用內部類
的方式模擬出多繼承的效果。
通過內部類繼承基類,外部類創建內部類對象,並使用內部類提供的方法,這樣就變相的實現了
多繼承的效果.
[java]
public class Graduate{
private Graduate_stu gaduate_stu= new Graduate_stu();
private Graduate_emp graduate_emp = new Graduate_emp();
private class Graduate_stu extends Student{
public void getName() {
....
}
}
private class Graduate_emp extends Employee{
public double getMoney()
{
return 0.0;
}
}
public void getName() {
gaduate_stu.getName();
}
public double getMoney() {
graduate_emp .getMoney();
}
}
public class Graduate{
private Graduate_stu gaduate_stu= new Graduate_stu();
private Graduate_emp graduate_emp = new Graduate_emp();
private class Graduate_stu extends Student{
public void getName() {
....
}
}
private class Graduate_emp extends Employee{
public double getMoney()
{
return 0.0;
}
}
public void getName() {
gaduate_stu.getName();
}
public double getMoney() {
graduate_emp .getMoney();
}
} 只是用代碼簡單的模擬了一下!
內部類還有一個頗具吸引力的特點,那就是內部類和外部類對於接口的繼承是"分離"的
相互不存在影響的,基於現實的情況,有時候我們實現一個接口,但是接口的方法在此類
中已經有定義了,對於這種情況,我們就可以使用內部類實現該接口,實現其方法,因為內
部類對於外部類的成員是可訪問的,因此使用內部類的方法就解決可該問題。
內部類的分類
我們現在知道內部類是放在外部類中的,根據內部類不同的"位置"和特性,內部類可以
分為以下幾類:
● 成員內部類
● 局部內部類
● 靜態內部類(嵌套類)
● 匿名內部類
對於其具體的特性和用法下面介紹
成員內部類
內部類作為外部類的一個成員存在,與外部類的方法,屬性並列.
[java]
package com.kiritor;
public class Outer {
private String type="Outer Class";
private static int flag = 1;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
System.out.println(""+getType()+":"+this.flag);
Inner inner = new Inner();
inner.toString();//外部類的非靜態方法訪問內部類的方法
//外部類的靜態方法訪問與其是一樣的,不做演示了.
return super.toString();
}
class Inner {
private String type="Inner Class";
private int flag=2;//這裡成員內部類中不允許定義靜態成員
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String toString() {
System.out.println(""+getType()+":"+this.flag);
System.out.println(""+Outer.this.getType()+Outer.this.flag);//若有變量重名,通過此種方式訪問
return super.toString();
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.toString();
Outer.Inner inner = outer.new Inner();//通過此種方式new inner
inner.toString();
}
}
package com.kiritor;
public class Outer {
private String type="Outer Class";
private static int flag = 1;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
System.out.println(""+getType()+":"+this.flag);
Inner inner = new Inner();
inner.toString();//外部類的非靜態方法訪問內部類的方法
//外部類的靜態方法訪問與其是一樣的,不做演示了.
return super.toString();
}
class Inner {
private String type="Inner Class";
private int flag=2;//這裡成員內部類中不允許定義靜態成員
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String toString() {
System.out.println(""+getType()+":"+this.flag);
System.out.println(""+Outer.this.getType()+Outer.this.flag);//若有變量重名,通過此種方式訪問
return super.toString();
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.toString();
Outer.Inner inner = outer.new Inner();//通過此種方式new inner
inner.toString();
}
}
Tips:在創建一個內部類的時候除非你已經有了一個外部類對象,否則不可能生成
方法內部類對象,因為內部類對象會悄悄的鏈接到創建他的外部類的對象,沒有外部類對象
自然也就不可能生成內部類對象了,不過還需注意的是內部類是一個在編譯時的概念,一旦編譯
通過,就會成為完全不同的兩個類,也就是會出現Outer,class和Outer$Inner,class兩個字節碼
文件。
局部內部類
在方法中定義的內部類稱為局部內部類.它與局部變量類似,因此局部內部類是不能有訪問
修飾符的,因為它不是外部類成員,但是他可以訪問當前方法中的代碼塊的常量,和外部類的所有
成員.
[java]
package com.kiritor;
public class Outer {
private String type = "Outer Class";
private static int flag = 1;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
System.out.println("" + getType() + ":" + this.flag);
return super.toString();
}
public void innerInfo() {
final String innerFinal = "可以訪問方法體內的常量";
class Inner {
private String type = "Inner Class";
private int flag = 2;// 這裡成員內部類中不允許定義靜態成員
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String toString() {
System.out.println("" + getType() + ":" + this.flag
+ innerFinal);
System.out.println("" + Outer.this.getType() + Outer.this.flag);// 若有變量重名,通過此種方式訪問
return super.toString();
}
}
new Inner().toString();//注意是通過這種方式調用內部類的方法的!
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.toString();
outer.innerInfo();
}
}
package com.kiritor;
public class Outer {
private String type = "Outer Class";
private static int flag = 1;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
System.out.println("" + getType() + ":" + this.flag);
return super.toString();
}
public void innerInfo() {
final String innerFinal = "可以訪問方法體內的常量";
class Inner {
private String type = "Inner Class";
private int flag = 2;// 這裡成員內部類中不允許定義靜態成員
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String toString() {
System.out.println("" + getType() + ":" + this.flag
+ innerFinal);
System.out.println("" + Outer.this.getType() + Outer.this.flag);// 若有變量重名,通過此種方式訪問
return super.toString();
}
}
new Inner().toString();//注意是通過這種方式調用內部類的方法的!
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.toString();
outer.innerInfo();
}
}
靜態內部類(嵌套類)
前面兩種內部類和變量類似,這裡的變量就是成員變量,和局部變量,可以參照進行對比
如果你不需要內部類對象與其外部類對象之間有聯系,那你可以將內部類聲明為static的,這就
是靜態內部類.我們需要明白的是:普通的內部類對象隱含的保存了一個外部類對象的引用。
但是當內部類為static的時候這種“特性”也就沒有了,這意味著:
1、創建靜態內部類對象的時候並不需要外部類對象
2、不能通過靜態內部類對象訪問非靜態的外部類對象了。
看例子:
[java]
package com.kiritor;
public class Outer {
private String type = "Outer Class";
private static int flag = 1;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
System.out.println("" + getType() + ":" + this.flag);
Inner.info();//外部類訪問內部類的靜態成員:內部類.靜態成員(靜態方法)
Inner inner = new Inner();//這裡生成一個內部類對象不再需要通過外部類對象了
inner.toString();//外部類訪問靜態內部類的非靜態成員或方法必須new一個對象
System.out.println(inner.type);
return super.toString();
}
static class Inner {
private String type = "Inner Class";
private int flag = 2;
private static String info="Inner Class 2";
//靜態內部類中可以有非靜態方法、屬性
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public static void info()
{
System.out.println(info);
}
public String toString() {
System.out.println("" + getType() + ":" + this.flag
);
//System.out.println("" + Outer.getType() + Outer.this.flag);// 若有變量重名,通過此種方式訪問
return super.toString();
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.toString();
}
}
package com.kiritor;
public class Outer {
private String type = "Outer Class";
private static int flag = 1;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
System.out.println("" + getType() + ":" + this.flag);
Inner.info();//外部類訪問內部類的靜態成員:內部類.靜態成員(靜態方法)
Inner inner = new Inner();//這裡生成一個內部類對象不再需要通過外部類對象了
inner.toString();//外部類訪問靜態內部類的非靜態成員或方法必須new一個對象
System.out.println(inner.type);
return super.toString();
}
static class Inner {
private String type = "Inner Class";
private int flag = 2;
private static String info="Inner Class 2";
//靜態內部類中可以有非靜態方法、屬性
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public static void info()
{
System.out.println(info);
}
public String toString() {
System.out.println("" + getType() + ":" + this.flag
);
//System.out.println("" + Outer.getType() + Outer.this.flag);// 若有變量重名,通過此種方式訪問
return super.toString();
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.toString();
}
}
可以看出的是:生成一個內部類對象不再需要通過一個外部類對象了,這也是靜態內部類和
成員內部類的區別:Outer.Inner in = new Outer.Inner();
匿名內部類
簡單的說匿名內部類就是沒有名字的類了,這在GUI編程裡面是較為常見的,給個例子:
[java]
package com.kiritor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyFrame extends JFrame{
private JButton button = null;
public MyFrame() {
this.setSize(200, 200);
this.setVisible(true);
button = new JButton("匿名內部類");
button.addMouseListener(new MouseListener() {//一個匿名的類
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
});
this.add(button);
}
}
package com.kiritor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyFrame extends JFrame{
private JButton button = null;
public MyFrame() {
this.setSize(200, 200);
this.setVisible(true);
button = new JButton("匿名內部類");
button.addMouseListener(new MouseListener() {//一個匿名的類
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
});
this.add(button);
}
}
對於匿名內部類,筆者現就不做總結了,之後會找個時間理解一下.
內部類的相關問題
下面討論的是內部類中的一些有趣的問題!
內部類能否被"重載"、“繼承”?
內部類重載問題
假設如果你創建了一個外部類,並定義了一個內部類,之後繼承外部類並重新定義內部類
的時候會發生什麼呢?
[java]
package com.kiritor;
class Outer {
public Outer() {
System.out.print("Outer:");
new Inner();
}
class Inner {
public Inner() {
System.out.println("Inner");
}
}
}
public class Outer2 extends Outer {
class Inner {
public Inner() {
System.out.println("outer2:Inner");
}
}
public static void main(String[] args) {
new Outer2();
}
}
package com.kiritor;
class Outer {
public Outer() {
System.out.print("Outer:");
new Inner();
}
class Inner {
public Inner() {
System.out.println("Inner");
}
}
}
public class Outer2 extends Outer {
class Inner {
public Inner() {
System.out.println("outer2:Inner");
}
}
public static void main(String[] args) {
new Outer2();
}
}
看一看輸出情況:Outer:Inner
缺省的構造器Outer2()是編譯器自動生成的,他會先調用父類的構造器,通過結果可以看出
雖然創建的是子類對象,但是 內部類並不是使用的"重載"過的.這說明檔你繼承了某個外部類
的時候,內部類並未發生特別變化,當然明確的繼承某個內部類的方式除外!
[java]
package com.kiritor;
class Outer {
public Outer() {
System.out.print("Outer:");
new Inner();
}
class Inner {
public Inner() {
System.out.println("Inner");
}
}
}
public class Outer2 extends Outer {
class Inner extends com.kiritor.Outer.Inner{
public Inner() {
System.out.println("outer2:Inner");
}
}
public Outer2() {
new Inner();
}
public static void main(String[] args) {
new Outer2();
}
}
package com.kiritor;
class Outer {
public Outer() {
System.out.print("Outer:");
new Inner();
}
class Inner {
public Inner() {
System.out.println("Inner");
}
}
}
public class Outer2 extends Outer {
class Inner extends com.kiritor.Outer.Inner{
public Inner() {
System.out.println("outer2:Inner");
}
}
public Outer2() {
new Inner();
}
public static void main(String[] args) {
new Outer2();
}
}
明確繼承之後的輸出結果為:
內部類的繼承問題
有時候我們只是需要繼承內部類,但是內部類的構造器又必須用到外部對象的引用
, 因此在繼承一個內部類的時候就有點特別了,主要的問題在於外部類對象的引用必須
初始化,而在被繼承類中並不存在,也就是單一繼承內部類的時候,沒有內部類與其外部類
的一種關聯.
可以使用一下方式解決:
[java]
package com.kiritor;
import com.kiritor.Outer.Inner;
class Outer {
public Outer() {
System.out.print("Outer:");
new Inner();
}
class Inner {
public Inner() {
System.out.println("Inner");
}
}
}
public class Inner2 extends Outer.Inner {
Inner2(Outer outer)
{
outer.super();
//構造器只能是這種方式的
System.out.println("只能為此種構造器");
}
public static void main(String[] args) {
new Inner2(new Outer());
}
}
package com.kiritor;
import com.kiritor.Outer.Inner;
class Outer {
public Outer() {
System.out.print("Outer:");
new Inner();
}
class Inner {
public Inner() {
System.out.println("Inner");
}
}
}
public class Inner2 extends Outer.Inner {
Inner2(Outer outer)
{
outer.super();
//構造器只能是這種方式的
System.out.println("只能為此種構造器");
}
public static void main(String[] args) {
new Inner2(new Outer());
}
}
輸出結果為:
Outer:Inner
Inner
只能為此種構造器
可以看出的是,Inner2只是集成了內部類,但是其缺省的構造器並不能用,而且僅僅傳遞一個
外部類的引用還不夠,還必須首先調用外部類的構造方法.這樣才提供了內部類與外部類對象
的引用關聯,才能夠通過編譯的.