前言
內部類,講完前面的特性,今天就講下內部類這個用的比較多,出現頻率挺高的知識點。
what is that?
首先,顧名思義,內部類就是在類的內部,也就是類的類,嵌套在里面的。直接代碼介紹,現一般分為成員內部類和局部內部類,還有一種匿名類。內部類擁有對外圍對象的引用。大部分使用的都是成員內部類。成員內部類是一種與Field、方法、構造器和初始化塊相似的類成員;局部內部類和匿名內部類則不是類成員。
成員內部類
定義在類里面的成員變量域的類,就是成員內部類。此時的內部類作為其外部類的成員,所以可以使用任意訪問控制符如private、protected和public等修飾。
class A {
//成員內部類,這種包含類的結構
class b{}
}
成員內部類還可以分為靜態內部類和非靜態內部類。注意:根據靜態成員不能訪問非靜態成員的規則,外部類的靜態方法、靜態代碼塊不能訪問非靜態內部類,包括不能使用非靜態內部類定義變量、創建實例等。
- 靜態內部類(帶static)
static關鍵字的作用是把類的成員變成類相關,而不是實例相關,即static修飾的成員屬於整個類,而不屬於單個對象。靜態內部類只可以訪問靜態的外部類的方法和對象。但是靜態內部類可以有非靜態成員。
/**
* 這一篇是用來測試靜態內部類的使用的
* @author yhy
* @date 1/14
*/
public class StaticInner {
private int num = 7;
/**
* 定義靜態常量,這個才可以被靜態內部類調用
*/
private static int num2 = 6;
static class Innerclass{
private static int num3;
public void innerfangfa(){
// System.out.println("調用外部類的成員變量:"+num);,這個會報錯,因為不能調用非靜態的
System.out.println("調用外部類的成員變量:"+num2);
}
}
}
報錯信息

外部類使用靜態類的代碼。
public class StaticInner {
private int num = 7;
/**
* 定義靜態常量,這個才可以被靜態內部類調用
*/
private static int num2 = 6;
/**
* 定義一個靜態內部類
*/
static class Innerclass{
private static int num3;
private int num4 = 4;
public void innerfangfa(){
// System.out.println("調用外部類的成員變量:"+num);,這個會報錯,因為不能調用非靜態的
System.out.println("調用外部類的成員變量:"+num2);
}
}
/**
* 這是外部類的方法,用來驗證訪問內部類的方法
*/
public static void inneracess(){
// 通過類名訪問靜態內部類的類成員
System.out.println("訪問靜態內部類:"+Innerclass.num3);
// 通過實例訪問靜態內部類的實例成員
System.out.println("訪問靜態內部類:"+new Innerclass().num4);
}
}
- 非靜態內部類(不帶static)
內部類可以直接訪問外部類的變量、方法等,而外部類只能通過顯式創建類才可以。包括可以訪問外部類的private。同時,在非靜態內部類里不能有靜態方法、靜態field、靜態初始化塊。
/**
* 這一篇是用來講內部類的一些定義的
* @author yhy
* @date 1/14
*/
public class InnerClass {
private int outernum = 9;
/**
* 創建內部類,同時還定義一個常量,為private
*/
class Inner{
private int innernum = 8;
// 定義內部類的方法,去調用外部類的成員常量
public void outAcess(){
System.out.println("外部類的值:"+outernum);
}
}
/**
* 這個是外部類的成員方法
* 這里直接調用內部類會報錯,不可以直接innernum使用
* 要通過顯式創建內部類對象才可以實現
*/
public void innerAcess(){
System.out.println("內部類的值:"+new Inner().innernum);
}
public static void main(String[] args) {
// 創建外部類對象,沒創建內部類對象
InnerClass i = new InnerClass();
i.innerAcess();
}
}
//非靜態內部類對象必須寄存在外部類對象里,而外部類對象則不必一定有非靜態內部類對象寄存其中
非靜態內部類的方法內訪問某個變量時,系統優先在該方法內查找(如果存在就使用)——if not,則到該方法所在的內部類中(存在則使用)——if not,則到該內部類所在的外部類(如果存在則使用)——if not,系統將出現編譯錯誤。
局部內部類
局部內部類是定義在類的內部方法或者其他作用域里面的內部類。
class A {
//局部內部類
public void B(){
class B{}
}
}
局部內部類的使用
package music.daima.ebook;
/**
* * 這一篇是用來觀察局部內部類的定義使用等,thinking in java的練習九
* * @author yhy
* * @date 1/14
* */
interface Ex9Interface{
/**
* @param s string類型在下面給內部類調用
* 定義一個接口,同時包含一個say方法,給下面使用
*/
void say(String s);
}
public class JuBuInner {
/**
* @return Inner()返回該類對此接口的引用
* // 這是一個方法,下面在里面定義一個內部類
*/
Ex9Interface f() {
class Inner implements Ex9Interface {
@Override
public void say(String s) {
System.out.println(s);
}
}
return new Inner();
}
public static void main(String[] args) {
JuBuInner x = new JuBuInner();
// 調用局部內部類的say方法
x.f().say("hi");
}
}
- 局部內部類不可以為public 和 private來修飾。而且因為它的上一級是方法,所以用static來修飾它是沒有意義的,所以也不能用static來修飾
匿名內部類
這個知識點就直接看代碼,是內部類的簡化寫版 ,本質不是類而是匿名對象(繼承該類或者實現了改接口的)。
前提
得提前存在一個類或者是接口給它用,可以是具體類也可以是抽象類。
格式
new 類名or接口名() {重寫方法};
package music.daima.ebook;
/**
* thinking in java
* @author yhy
* 匿名內部類
*/
public class AnonymousInner {
public static test1 getTest(int i){
// 匿名類的結構如下,這里的i會傳給基類的構造器
return new test1(i) {
{
System.out.println(22);
}
// 重寫f方法
@Override
public void f() {
System.out.println("在匿名類內");
}
};//這里要注意有分號
}
public static void main(String[] args) {
test1 abc = getTest(55);
abc.f();
}
}
/**
*創建一個抽象類
*/
abstract class test1{
// 基類的構造器
public test1(int i){
System.out.println("i is :"+i);
}
public abstract void f();
}
《Java的編程思想》中的練習題答案——使用匿名內部類
interface Inner12 {
void modifyOuter();
}
public class Outer12 {
private int oi = 1;
private void hi() { System.out.println("Outer hi"); }
public Inner12 inner() {
return new Inner12() {
public void modifyOuter() {
oi *= 2;
hi();
}
};
}
public void showOi() { System.out.println(oi); }
public static void main(String[] args) {
Outer12 out = new Outer12();
out.showOi();
out.inner().modifyOuter();
out.showOi();
}
}
下面的pd.method()方法調用結果是一樣的,下面用的就是匿名內部類,相對簡單一點,但是不好理解。記住它是一個對象而不是類。

why use it?
內部類方法可以訪問該類定義所在的作用域中的數據,包括私有的數據
內部類可以對同一個包內的其他類隱藏起來
當想要定義一個回調函數且不想編寫大量代碼,可以考慮使用匿名內部類比較便捷
——《Java核心技術卷一》
個人理解:
- 內部類可以將其封裝起來,很好的隱藏,可以用protected 和private權限來修飾。
- 擁有訪問外部類的所有元素的權限,普通的內部類對象隱式地保存了一個引用,指向創建它的外圍類對象。
- 比如可以很好處理類的辦法同名重疊情況,更加有效地去引用。
- 可以間接實現了多重繼承,內部類繼承其它類,然后實例化外部類可以實現這種間接效果
how to use?
1.在外部類以外使用靜態內部類
(因為靜態內部類是外部類類相關的,因此創建內部類對象時無須創建外部類對象。)
/**這是用來測試在外部類以外使用靜態內部類
* @author yhy
*/
public class innerOut {
public static void main(String[] args) {
Out1.In1 abc = new Out1.In1();
}
}
/**
* 定義一個外部類
* @author yhy
*/
class Out1{
/**
* 定義一個靜態內部類,靜態內部類是直接與外部類相關的
*/
static class In1{
void FangFa(){
System.out.println("這是靜態內部類");
}
}
}
//output:這是靜態內部類
2.在外部類以外使用非靜態內部類
/**
* 使用教程——外部類以外使用非靜態內部類
* @author yhy
*/
public class InnerTest {
public static void main(String[] args) {
// 格式,先外部再內部 = new 外部.new 內部
Out.In test = new Out().new In();
test.inner();
}
}
/**
* 定義一個外部類
*/
class Out{
/**
* 定義一個非靜態內部類
*/
class In{
void inner(){
System.out.println("打印出內部類的信息");
}
}
}
//output:打印出內部類的信息
3.在外部類內部使用內部類
總結
靜態內部類和非靜態內部類區別只是在創建內部類對象時,靜態的只需使用外部類即可調用構造器,而非靜態則一定使用外部類對象來調用里面的方法。