(Thinking in Java)內部類的簡單使用
1. 成員內部類
1. 最基本使用
public class Demo {
class Contents{
private int i=11;
public int value(){
return i;
}
}
class Destination{
private String label;
Destination(String whereTo){
label=whereTo;
}
String readLabel(){
return label;
}
}
public void ship(String dest){
Contents c=new Contents();
Destination d=new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Demo d=new Demo();
d.ship("Tasmania");
}
}
2.內部類可以訪問外部類的成員
內部類可以訪問外部類的成員變數。如下:
public class Demo {
private Object[] items;
private int next = 0;
public Demo(int size) {
items = new Object[size];
}
public void add(Object x) {
if (next < items.length) {
items[next++] = x;
}
}
private class SequenceSelector implements Selector {
private int i = 0;
public boolean end() {
return i == items.length;
}
public Object current() {
return items[i];
}
public void next() {
if (i < items.length) {
i++;
}
}
}
public Selector selector() {
return new SequenceSelector();
}
public static void main(String[] args) {
Demo d = new Demo(10);
for (int i = 0; i < 10; i++) {
d.add(Integer.toString(i));
}
Selector selector = d.selector();
while (!selector.end()) {
System.out.print(selector.current() + " ");
selector.next();
}
}
}
interface Selector {
boolean end();
Object current();
void next();
}
因為在建立內部類物件的時候,內部類物件會捕獲一個指向外部類物件的引用。訪問外部類成員的時候,就是用這個引用來獲取外部類成員的。內部類中也可以取得這個外部類物件引用。舉例如下:
public class DotThis{
void f(){
System.out.println("DotThis.f()");
}
class Inner{
public DotThis outer(){
return DotThis.this;
//A plain "this" would be Inner's this
}
}
public Inner inner(){
return new Inner();
}
public static void main(String[] args) {
DotThis dt=new DotThis();
DotThis.Inner dti=dt.inner();
dti.outer().f();
}
}
當要在其他類中建立一個內部類物件的時候,可以使用.new語法。
public class DotNew{
public class Inner{
}
public static void main(String[] args){
Inner dni=new DotNew().new Inner();
}
}
當創造內部類物件的時候,如果這個內部類不是巢狀類(靜態內部類),那麼就必須要通過外部類物件,才能建立這個內部類物件,為什麼呢,之前說過,因為這個內部類物件要獲取外部類的引用啊。
並且在存在多個內部類的時候,多個內部類之間可以互相建立物件。例子就不舉了。
小結:現在所說的都是成員內部類,其實內部類沒那麼複雜,既然叫做成員內部類了,他就只是類的成員罷了。他也可以帶修飾符,他的修飾符和其他普通的成員變數的修飾符的意義也沒有什麼不同。
3. 內部類許可權修飾符
當內部類被private修飾的時候,該類只能被外部類內的方法使用,其他類不能獲取該內部類的引用,因為是private的,所以其他類根本不知道存在一個這樣的類。當然也可以作為一個成員變數使用,但是如果作為成員變數,則其他類並不能直接建立內部類的引用,需要用其他手段獲取該引用,如下:
class Outer{
Inner in;
private class Inner implements a_interface{
void show(){
System.out.println("123");
}
}
}
interface a_interface{
void show();
}
class test{
//Inner in=new Outer().in;這是錯誤的,因為test並不知道Outer類有一個Inner內部類,因為是私有的
a_interface in=new Outer().in;//可以運用向上轉型的方法獲取private修飾的內部類。
}
小結:其實這也很好記,無論是public還是private,修飾到內部類上的時候,和他們修飾普通的成員變數(如string,int之類)的時候沒什麼不同,規則都一樣,public就都能使用,private就類內可以用。所以規則就記住三條就好:1.先考慮外部類的許可權,是否可以獲取一個外部類物件。2.建立成員內部類物件的時候需要外部類物件。3.考慮內部類的許可權,是否可以獲取這樣的一個內部類物件(或者說,在外部知不知道有這樣一個內部類)。
2. 方法和作用域內的內部類
當我們需要解決一個複雜的問題,想建立一個類來輔助解決問題,但是不希望這個類是公共可用的,甚至不希望在外部類之內的其他地方可以訪問到這個輔助類。我們可以運用方法內的內部類
public class Outer {
public InterfaceDemo get_InterfaceDemo(String s) {
class InterfaceDemoTool implements InterfaceDemo {
private String label;
private InterfaceDemoTool(String label) {
this.label = label;
}
public String readLabel() {
return label;
}
}
return new InterfaceDemoTool(s);
}
public static void main(String[] args) {
Outer o = new Outer();
InterfaceDemo i = o.get_InterfaceDemo("123");
}
}
interface InterfaceDemo {
String readLabel();
}
當然在方法中還可以定義多個內部類,並且這些內部類之間的關係和普通一個java檔案中多個類之間的關係好像沒什麼不同。也可以相互繼承和建立物件。另外在方法中的內部類不能加private等許可權修飾符,只能加abstract和final修飾符。
另外也可以在某個作用域內建立內部類物件
if(a==b){
class inner{
}
new inner();
}
上面的例子中,在if外就不能知道有這麼個inner類了,他的作用域只在{…}之中,同理,在方法內定義的內部類,在方法外也不能知道存在這麼個類,因為這個內部類的作用域只在這個方法內。
3.匿名內部類
下面這塊程式碼中get_inner()的意思是,建立一個繼承自InnerFather的匿名類物件,並且自動向上轉型為InnerFather後返回。
public class Outer {
public InnerFather get_inner() {
return new InnerFather() {
void print(){
System.out.println("Inner_Override");
}
};
}
class InnerFather {
InnerFather() {
}
void print(){
System.out.println("InnerFather");
}
}
public static void main(String[] args) {
Outer o = new Outer();
InnerFather i = o.get_inner();
i.print();
}
}
當然這只是有無參建構函式,當父類只有一個含參建構函式的時候,我們可以這樣向匿名內部類傳入一個建構函式引數。
public class Outer {
public InnerFather get_inner(int i) {
return new InnerFather(i) {
void print(){
System.out.println("Inner_Override");
}
};
}
class InnerFather {
InnerFather(int i) {
}
void print(){
System.out.println("InnerFather");
}
}
public static void main(String[] args) {
Outer o = new Outer();
InnerFather i = o.get_inner(10);
i.print();
}
}
可以通過構造程式碼塊來實現匿名內部類的自定義的建構函式
abstract class Base {
public Base(int i) {
System.out.println("Base constructor");
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i){
return new Base(i){
{System.out.println("AnonymousConstructor constructor");}
public void f(){
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
}
}
(書上說,如果傳入了新的物件,就比如下面例子中的s_in,這個s_in就必須是final的,但是我實驗了一下發現並不用啊,我也沒太搞懂。)
abstract class Base {
public Base(int i) {
System.out.println("Base constructor");
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i,String s_in){
return new Base(i){
{System.out.println("AnonymousConstructor constructor");}
String s=s_in;
public void f(){
}
};
}
public static void main(String[] args) {
Base base = getBase(47,"hello");
}
}
4.巢狀類
巢狀類指的是被static修飾的內部類。這意味著:1.建立巢狀類物件不需要外部類物件。2.不能再巢狀類物件之中訪問非靜態的外圍類物件。普通內部類的成員和方法只能放在類的外部層次上(這句話我沒搞懂= =),所以普通內部類不能有static的成員和方法。但是巢狀類可以有。
public class Outer {
static class Inner{
static int i=5;
}
public static void main(String[] args) {
Inner i=new Outer.Inner();
}
}
可以從上面的例子看到,在建立這個巢狀類物件的時候,並沒有像最開始那樣,用一個外部類物件來建立這個內部類物件。其實這和靜態方法差不多。
可以在介面內部定義內部類,而且他們即使沒有static修飾,也會自動變成public static的。
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface{
public void howdy(){
System.out.println("howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
書上有句話說的很好,在開發的時候建議在每個類中都寫一個main方法測試,但是這又必須帶著那些已經編譯過的額外程式碼,所以我們可以用巢狀類放置測試程式碼。
public class Outer {
public void f(){
System.out.println("I need to be tested");
}
public static class Tester{
public static void main(String[] args) {
Outer o=new Outer();
o.f();
}
}
}
當然以上兩段程式碼如果是在eclipse上執行的話,需要設定一下執行的main函式在哪,否則會報錯
5.多層內部類巢狀
紙老虎,愛有幾層有幾層,反正只要是外部類的東西,不管哪層外部類,都能訪問到。
public class Outer {
void f(){
System.out.println("hello");
}
class Inner1{
void g(){
System.out.println("java");
}
class Inner2{
void h(){
f();
g();
}
}
}
public static void main(String[] args) {
Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
in2.h();
}
}
下面是個好玩的
public class Outer {
void f(){
System.out.println("hello");
}
class Inner1{
void f(){
System.out.println("java");
}
class Inner2{
void h(){
f();
}
}
}
public static void main(String[] args) {
Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
in2.h();
}
}
最後列印結果是java。
大概就這麼多吧,以後如果還有新東西再補。