參考:
Java核心技術
Java編程思想
https://www.cnblogs.com/dolphin0520/p/3811445.html
https://www.cnblogs.com/chenssy/p/3388487.html
局部內部類:http://baijiahao.baidu.com/s?id=1600724548575261991&wfr=spider&for=pc
外部類和內部類數據訪問:https://blog.csdn.net/weixin_40707866/article/details/79652084
內部類
內部類的概念
將一個類的定義放在另一個類的定義內部,這就是內部類。雖然內部類存在於外部類定義之中,但是編譯之後還是會產生屬於自己的.class文件,依舊遵循着每個類都會產生一個.class文件。一般來說內部類包括四種:成員內部類、局部內部類、匿名內部類和嵌套類(也叫靜態內部類)。其中局部內部類定義在方法或作用域內,靜態內部類使用static修飾。
內部類與外部類之間的數據訪問
內部類就相當於一個外部類的成員變量,所以可以直接訪問外部類的成員,包括成員變量和成員函數。但是外部類不能直接訪問內部類成員,必須通過創建內部類實例的方法進行訪問。
爲什麼使用內部類
如果沒有內部類提供的,可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。使用內部類的最大好處就是它使得Java的多重繼承的解決方案更加完整,因爲內部類允許繼承多個非接口類型(即類和抽象類)。
當多重繼承應用於接口時,那麼看不出使用內部類的必要性。因爲使用多實現仍然可以實現功能,但是如果擁有的是抽象類或具體的類,那麼就只能使用內部類實現多重繼承。
package learninnerclass;
interface A{}
interface B{}
class X implements A,B{ }
class Y implements A{
B makeB(){
return new B(){};
}
}
public class MultiInterfaces {
static void takesA(A a){}
static void takesB(B b){}
public static void main(String[] args) {
X x = new X();
Y y = new Y();
takesA(x);
takesA(y);
takesB(x);
takesB(y.makeB());
}
}
只能使用內部類實現多重繼承的情況
package learninnerclass;
class D{}
abstract class E{}
class Z extends D{
E makeE(){ return new E(){};}
}
public class MultiImplementation {
static void takesD(D d){}
static void takesE(E e){}
public static void main(String[] args){
Z z = new Z();
takesD(z);
takesE(z.makeE());
}
}
.this和.new
內部類的對象有一個隱式引用(即.this),它引用了實例化該內部對象的外部類對象。通過這個引用,可以訪問外部類對象的全部狀態。這個引用在內部類的定義中是不可見的,是由編譯器自動生成的。具體使用如下:
外部類名.this; // 該表達式表示外部類的引用
外部類對象.new 內部類名(構造方法的參數); // 該表達式表示創建內部類對象
外部類名.內部類名; // 該表達表示在外部類的作用域之外,引用內部類
成員內部類
在成員內部類中要注意兩點,1) 成員內部類中不能存在任何static的變量和方法,static只能用在靜態常量的聲明上;2)成員內部類是依附於外部類的,所以只有先創建了外部類對象才能夠創建內部類對象。
在成員內部類的定義中可以對外部類所有元素進行訪問,即使該元素使用private修飾。其實這就相當於將成員內部類當做外部類的一個元素(成員)了。正因爲如此,所以可以使用private、包訪問權限、protected、public等權限修飾符來修飾成員內部類,而不像外部類只能使用public或包訪問權限來修飾。
在非靜態內部類中,不能定義靜態的成員變量和成員方法,包括成員內部類,局部內部類,匿名局部類,但可以在靜態內部中定義靜態的成員變量和方法。值得注意的是,可以在所有的內部類中定義靜態常量,即static final。
雖然成員內部類可以無條件地訪問外部類的成員,而外部類想訪問成員內部類的成員卻不是這麼隨心所欲了。在外部類定義中如果要訪問成員內部類的成員,必須先創建一個成員內部類的對象,再通過指向該內部類對象的引用來訪問內部類的域或者方法。
不過需要注意的是,當成員內部類擁有和外部類同名的成員變量或方法時,會發生隱藏現象。默認情況下,訪問的是成員內部類的成員。如果想要訪問外部類的同名成員,則需要使用下面的形式進行訪問。
示例
一般示例
package com.thinkinginjava.chapter10;
public class Parcel3 { // 外部類
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 static void main(String[] args) {
Parcel3 p = new Parcel3(); // 生成外部類對象
// 必須通過外部類對象才能創建內部類對象
Parcel3.Contents c = p.new Contents(); // 生成Contents成員內部類對象
System.out.println(c.value());
Parcel3.Destination d = p.new Destination("Suzhou");// 生成Destination成員內部類對象
System.out.println(d.readLabel());
}
}
訪問同名屬性
class MemberInnerClass{
private String str = "外部類同名屬性";// 與內部類數據有重名
class Contents{ // 成員內部類
private String str = "內部類同名屬性";
public String value() { return str; }// 只會訪問到內部類的同名屬性
// 要在內部類中訪問外部類同名屬性,需要藉助外部類對象
public String FatherValue() { return new MemberInnerClass().str; }
}
public void useInnerClass(){
System.out.println("在外部類中,str = "+str); //訪問同名變量(只會訪問到外部類的)
// 要想在外部類中訪問內部類同名屬性需要藉助內部類對象
Contents c = new Contents();
System.out.println("Contents中的域 :"+c.str);
System.out.println("Contents中的方法 value: "+ c.value());
}
}
public class MemberInnerClassTest {
public static void main(String[] args) {
MemberInnerClass mic = new MemberInnerClass();
MemberInnerClass.Contents c = mic.new Contents();
System.out.println("通過內部類的方法訪問內部類的屬性 str:"+c.value());
System.out.println("通過內部類的方法訪問外部類的屬性 str:"+c.FatherValue());
System.out.println("*****************");
mic.useInnerClass();
}
}
匿名內部類
匿名內部類的特點:1)沒有名字的局部內部類;2)匿名內部類是一種沒有構造器的類;3)匿名內部類沒有訪問修飾符和static修飾符;4)匿名內部類其實是隱式地繼承某一個父類或者實現某一個接口。這句話的意思就是如果聲明匿名內部類,前提是這個父類或者接口必須先存在。如下代碼所示,Contents是一個早已存在的接口,如果Contents接口不存在則無法創建匿名內部類。換句話說,匿名內部類是實現一個接口或者繼承一個父類並重寫的簡潔方式。
示例
package learninnerclass;
// 已存在的一個接口
interface Contents{
int value();
}
public class AnonymousInnerClassTest {
public Contents contents() {
return new Contents() { // 匿名內部類
private int i = 11;
public int value() { return i;}
};
}
public static void main(String[] args) {
AnonymousInnerClassTest p = new AnonymousInnerClassTest();
Contents c = p.contents();
System.out.println(c.value());
}
}
其實上面的代碼,如果不使用匿名內部類,則等價於下面的寫法。
public class Parcel7b {
// 繼承接口並實現
class MyContents implements Contents{
private int i =11;
public int value() { return i; }
}
public Contents contents() { return new MyContents(); }
public static void main() {
Parcel7b p = new Parcel7b();
Contents c = p.contents();
System.out.println(c.value());
}
}
局部內部類
局部內部類定義在方法或作用域(代碼塊)內,不能使用任何訪問修飾符。局部內部類的優勢在於,除包含該類的方法,其他代碼均不知道局部內部類的存在。同其他內部類比較,局部內部類不僅可以訪問包含它們的外圍類,而且還可以訪問局部變量。
1) 如果局部內部類定義在靜態方法中,它可以訪問外部類中所有靜態成員,包含私有。
2) 如果局部內部類定義在實例方法中,它可以訪問外部類中所有的成員,包含私有。
除此之外,局部內部類還可以有構造方法。如果局部內部類要訪問局部變量,那麼局部變量必須聲明爲final類型。在實踐中,局部內部類是所有內部類中最少使用的一種形式。
示例
package learninnerclass;
interface Counter{
int next();
}
public class LocalInnerClass {
private int count = 10;
private static double salary = 13.14;
public int getNum(){
return count;
}
public static double getSalary(){
return salary;
}
// 1.在實例方法中的局部內部類
Counter getCounter(final String name)
{
class LocalCounter implements Counter
{ //局部內部類中可以有構造方法
public LocalCounter(){
System.out.println("局部內部類的構造器...");
}
@Override
public int next() {
System.out.println("name = "+name);
// 1.1 實例方法中的局部內部類能訪問外部類的實例變量和實例方法
System.out.println("count = "+count);
System.out.println("通過方法獲取count = "+getNum());
// 1.2 實例方法中的局部內部類能訪問外部類的靜態變量和靜態方法
System.out.println("salary = "+salary);
System.out.println("通過方法獲取salary = "+getSalary());
return count++;
}
}
return new LocalCounter();
}
// 2.在靜態方法中的局部內部類
static Counter getCounter2(final String name)
{
class LocalCounter implements Counter
{ //局部內部類中可以有構造方法
public LocalCounter(){
System.out.println("局部內部類的構造器...");
}
@Override
public int next() {
System.out.println("name = "+name);
// 2.1 靜態方法中的局部內部類不能訪問外部類的實例變量和實例方法
// System.out.println("count = "+count);
// System.out.println("通過方法獲取count = "+getNum());
// 2.2 靜態方法中的局部內部類只能訪問外部類的靜態變量和靜態方法
System.out.println("salary = "+salary);
System.out.println("通過方法獲取salary = "+getSalary());
return 0;
}
}
return new LocalCounter();
}
public static void main(String[] args)
{
LocalInnerClass lic = new LocalInnerClass();
Counter c1 = lic.getCounter("實例方法中的局部內部類");
c1.next();
Counter c2 = lic.getCounter2("靜態方法中的局部內部類");
c2.next();
}
}
靜態內部類(嵌套類)
如果不需要內部類對象與其對應的外部類對象之間存在聯繫,那麼就可以將內部類聲明爲static的,以便消除產生的引用。換句話說,當內部類是靜態內部類的時候,它並不會隱式地保存this引用(該引用指向創建它的外部類對象,普通內部類訪問外部類對象的數據也是通過該引用才能夠實現。),因此靜態內部類對象和外部類對象之間也就不存在聯繫了。
如果創建了一個靜態內部類,那就意味着:
1) 要創建靜態內部類的對象,並不需要其外部類的對象;
2) 在靜態內部類中不能訪問外部類的非靜態(static)的成員變量和成員方法,只能訪問靜態的成員變量和靜態成員方法。
示例
package learninnerclass;
public class StaticInnerClassTest02 {
private int num =10;
private static double salary=100.4;
public int getNum(){
return num;
}
public static double getSalary(){
return salary;
}
public static class Inner{
// 1.訪問外圍類的成員變量
// 1.1 在靜態內部類中不能訪問外部類的非靜態屬性
// public void info1(){// 這段代碼是錯誤的
// System.out.println("num : "+num);
// }
// 1.2訪問外圍類的靜態成員變量
public void info2(){// 可以訪問外部類的靜態成員變量
System.out.println("salary : "+salary);
}
// 2.訪問外圍類的成員方法
// 2.1 在靜態內部類中不能訪問外部類的非靜態方法
// public void info3(){// 這段代碼是錯誤的
// System.out.println("num :"+ getNum());
// }
// 2.2 在靜態內部類中可以訪問外部類的靜態方法
public void info4(){// 可以訪問外部類的靜態成員方法
System.out.println("salary : "+getSalary());
}
}
public static void main(String[] args) {
// 創建靜態內部類對象(創建靜態內部類對象的時候並沒有通過外部類對象,即:外部類對象.new 內部類名() )
StaticInnerClassTest02.Inner in = new Inner();
in.info2();
in.info4();
}
}
另外,聲明在接口中的內部類也會自動成爲static和public。