内部类
内部类共有四种:成员内部类、静态内部类、局部内部类、匿名内部类*
public class Outer {//外部类class Inner {//Outer的内部类}
}
1 成员内部类
在类中,可以定义成员方法、成员变量,还可以定义成员内部类
注:在成员内部类中,不能编写静态的属性和方法
//外部类
public class MenberOuterClass{//外部类的属性private String name;private static int age;//外部类的方法public void run(){}public static void go(){}//成员内部类public class MenberInnerClass{//成员内部类的属性private String name;private int age;//成员内部类的方法public void run(String name){}}
}
上述代码编译得到两个class文件:
MenberOuterClass.class
MenberOuterClass$MenberInnerClass.class
1.1 成员内部类和外部类的相互访问
package com.test.demo1;
public class MenberOuterClass{//外部类的属性private String name;private static int age;//外部类的方法public void run(){}public static void go(){}//外部类的方法,访问成员内部类的属性和方法public void test(){//需要创建内部类对象,然后才可以访问内部类的属性和方法MenberInnerClass inner = new MenberInnerClass();System.out.println(inner.name);System.out.println(inner.age);inner.show();inner.run("tom");}//成员内部类public class MenberInnerClass{//成员内部类的属性private String name;private int age;//成员内部类的方法public void show(){...}public void run(String name){//访问当前run方法的参数nameSystem.out.println(name);//访问内部类自己的属性nameSystem.out.println(this.name);//访问外部类的非静态属性System.out.println(MenberOuterClass.this.name);//访问外部类的静态属性System.out.println(MenberOuterClass.age);//访问外部类的非静态方法MenberOuterClass.this.run();//访问外部类的静态方法MenberOuterClass.go();}}
}
在其它类中访问这个内部类:
package com.test.demo1;
//1.首先导入内部类的包
import com.test.demo1.MenberOuterClass.MenberInnerClass;public class Test {public static void main(String[] args){MenberOuterClass outer = new MenberOuterClass();MenberInnerClass inner = outer.new MenberInnerClass();inner.run("tom");}
}
1.2 小结
1. 成员内部类访问外部的属性和方法:
(非静态)
- 外部类名.this.属性
- 外部类名.this.方法()
(静态)
- 外部类名.属性
- 外部类名.方法()
2. 外部类访问成员内部类的属性和方法:
(成员内部类不能有静态属性和静态方法)
先创建内部类对象再进行访问
3. 其它类中使用这个内部类:
内部类私有属性、方法无法访问
步骤1:导包
import …外部类.内部类;
步骤2:创建外部类对象
外部类 outer = new 外部类();
步骤3:创建内部类对象
内部类 inner = outer.new 内部类();
成员内部类注意事项:
注1:在外部类中可以访问内部类的私有属性,内部类中也可以访问外部类的私有属性
注2:如果成员内部类定义为私有的,外部类依旧可以调用该内部类的属性和方法并且能创建内部类对象,但是在其它类中不能创建该成员内部类的对象也不能调用它的方法和属性
注3:成员内部类不能定义静态属性、静态方法
2 静态内部类
静态内部类和成员内部类类似,只不过这个内部类被static关键字修饰了
注:静态内部类中,可以编写静态属性和静态方法,也只有静态内部类可以如此,其它三种内部类都不行
public class StaticOuterClass {public static class StaticInnerClass {//静态内部类}
}
上述代码编译得到两个class文件:
StaticOuterClass.class
StaticOuterClass$StaticInnerClass.class
2.1 静态内部类和外部类的相互访问
package com.test.demo2;
//外部类
public class StaticOuterClass {//外部类的属性private String name;private static int age;//外部类的方法public void run(){}public static void go(){}public void test(){//外部类中,访问静态类中的静态属性和方法System.out.println(StaticInnerClass.age);StaticInnerClass.show();//外部类中,访问静态内部类中的非静态属性和方法StaticInnerClass inner = new StaticInnerClass();System.out.println(inner.name);inner.run("tom");}//静态内部类public static class StaticInnerClass {//成员内部类的属性private String name;private static int age;//成员内部类的方法public static void show(){...}public void run(String name){//访问当前run方法中的参数nameSystem.out.println(name);//访问内部类自己的属性nameSystem.out.println(this.name);//访问内部类自己的静态属性ageSystem.out.println(age);//静态内部类中,无法访问外部类的非静态属性和方法//访问外部类的静态属性和方法System.out.println(StaticOuterClass.age);StaticOuterClass.go();}}
}
在其它类中使用这个内部类:
package com.test.demo2;
import com.test.demo2.StaticOuterClass.StaticInnerClass;
public class Test {public static void main(String[] args) {//写法一:StaticInnerClass inner = new StaticInnerClass();inner.run("tom");//写法二:StaticInnerClass sic = new StaticOuterClass.StaticInnerClass();sic.run("mimi");}
}
2.2 小结
1. 静态内部类访问外部的属性和方法:
(非静态)
- 静态内部类中无法直接访问外部类的非静态属性和方法
- 只能创建外部类对象去访问,不能通过 外部类.this.成员 去访问
(静态)
- 外部类名.属性
- 外部类名.方法()
2. 外部类访问静态内部类的属性和方法:
(非静态)先创建内部类对象在进行访问
(静态)
- 内部类名.属性
- 内部类名.方法()
3. 其它类中使用这个内部类:
只能访问非私有的哦
- 1 导包
import …外部类类名.内部类雷鸣
- 2 创建静态内部类对象
写法一:静态内部类类名 inner = new 静态内部类类名();
写法二:静态内部类类名 inner = new 外部类类名.静态内部类类名();
- 3 通过静态内部类对象引用静态内部类的方法、属性
inner.方法();
inner.属性;
引用静态内部类的静态方法和静态属性可以不创建对象直接用类名调用:
静态内部类类名.静态方法名();
静态内部类类名.静态属性;
或
外部类类名.静态内部类类名.静态方法名();
外部类类名.静态内部类类名.静态属性;
静态内部类注意事项:
注1:静态内部类中不能直接访问外部类的非静态属性和非静态方法
注2:外部类中可以访问静态内部类的静态属性、方法和非静态属性、方法,访问形式和成员内部类一致
注3:在创建对象的时候,直接使用这个静态内部类的名字即可,不再需要依赖外部类对象
注4:
外部类.this.成员—>意思是在内部类中调用外部类的成员
外部类.super.成员—>意思是在内部类中调用外部类的父类的成员
3 局部内部类(了解)
局部内部类,声明在外部类的方法中,相当于方法中的局部变量的位置,它的作用范围只在当前方法中
public class LocalOuterClass {public void sayHello(String name) {class LocalInnerClass {//局部内部类}}
}
上述代码编译得到两个class文件:
LocalOuterClass.class
LocalOuterClass$1LocalInnerClass.class
注意这里的内部类名前有一个数字1,此处是为了区分在同一个类中,不同方法内定义了相同类名的内部类
3.1 局部内部类和外部类的相互访问
public class LocalOuterClass {//外部类的属性private String name;private static int age;//外部类的方法public void run(){}public static void go(){}public void sayHello(String name) {final int num = 1;class LocalInnerClass {//局部内部类private String name;public void test(String name){//内部类中访问当前方法中的变量,这个变量必须是final修饰的System.out.println(num);//访问当前方法中的参数nameSystem.out.println(name);//访问内部类自己的属性nameSystem.out.println(this.name);//访问外部类的非静态属性System.out.println(LocalOuterClass.this.name);//访问外部类的非静态方法LocalOuterClass.this.run();//访问外部类的静态属性和静态方法System.out.println(LocalOuterClass.age);LocalOuterClass.go();}}//当前方法访问局部内部类的属性和方法LocalInnerClass inner = new LocalInnerClass();System.out.println(inner.name);inner.test();}
}
3.2 小结
1. 局部内部类访问外部的属性和方法
(非静态)
- 外部类类名.this.属性名
- 外部类类名.this.方法名()
(静态) - 外部类类名.静态属性
- 外部类类名.静态方法名()
注:局部内部类中访问外部属性时要注意以下两种情况:
- 访问外部类的成员变量—可以直接访问
- 访问外部类的局部变量(当前内部类所在的方法的变量)—该变量只能是一个常量,或者是看起来像一个常量(只赋值过一次的变量)
2. 当前方法访问局部内部类
创建内部类对象,通过对象调用
这里创建的内部类对象必须在内部类代码的下面
注1:局部内部类只能在当前声明的方法中进行使用,外部类无法使用
局部内部类不能定义静态的
注2:在其它类中无法访问局部内部类,它只能在方法中访问
4 匿名内部类(重点)
匿名内部类,是一种没有名字的内部类,它是内部类的一种简化写法。算是特殊的局部内部类
在普通的代码中,使用一个接口的步骤如下:
- 声明一个类,去实现这个接口
- 实现这个接口中的抽象方法(重写)
- 在其他代码中,创建该类的对象
- 调用类中实现(重写)后的方法
interface A {void method();
}public class Test implements A {public static void main(String[] args) {Test test = new Test();test.method();}public void method(){...}
}
使用匿名内部类则可以将上述整个过程简化。
4.1 匿名内部类格式
父类或者接口类型 变量名 = new 父类或者接口() {//方法重写public void method() {...}
}
//调用实现(重写)后的方法
变量名.method();
匿名内部类有两种形式
- 利用一个父类,进行声明并创建匿名内部类对象,这个匿名内部类默认就是这个父类的子类型
- 利用一个接口,进行声明并创建匿名内部类对象,这个匿名内部类默认就是这个接口的实现类
匿名内部类没有类名
所以:
- 匿名内部类必须依托于一个父类型或者一个接口
- 匿名内部类在声明的同时,就必须创建出对象,否则后面就没法创建了
- 匿名内部类中无法定义构造器
4.2 匿名内部类的声明&创建对象
- 利用父类型来声明并创建匿名内部类对象
public abstract class Animal {public abstract void run();
}
class Test {public static void main(String[] args) {Animal animal = new Animal() {public void run() {...}}animal.run();}
}
Test类会产生两个class文件:
Test.class
Test$1.class
注:如果利用父类型声明这个匿名内部类,name这个匿名内部类默认就是这个父类的子类
- 利用接口来声明并创建匿名内部类对象
public interface Action {void run();
}
class Test {public static void main(String[] args) {Action a = new Action {public void run() {...}}a.run();}
}
注:如果利用接口声明这个匿名内部类,那么这个匿名内部类就是这个接口的实现类
因为匿名内部类是一种特殊的局部内部类,所以匿名内部类里面调用外部的变量和局部内部类调用外部变量一致。
内部类的选择
假设现在已经确定了要使用内部类,那么一般情况下该如何选择:
1. 考虑这个内部类,如果需要反复的进行多次使用(必须要有名字)
- 在这个内部类中,如果需要定义静态的属性和方法,选择使用静态内部类
- 在这个内部类中,如果需要访问外部类的非静态属性和方法,选择使用成员内部类
2. 如果这个内部类只需要使用一次(可以没有名字)
- 选择使用匿名内部类
3. 局部内部类,基本不会使用