目录
1 概述
1、什么是内部类?
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
为什么要声明内部类呢?
总的来说,遵循高内聚低耦合的面向对象开发总原则。便于代码维护和扩展。
具体来说,当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,不在其他地方单独使用,那么整个内部的完整结构最好使用内部类。而且内部类因为在外部类的里面,因此可以直接访问外部类的私有成员。
3、内部类都有哪些形式?
根据内部类声明的位置(如同变量的分类),我们可以分为:
(1)成员内部类:
-
静态成员内部类
-
非静态成员内部类
(2)局部内部类
-
有名字的局部内部类
-
匿名的内部类
成员内部类
如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。
语法格式:
【修饰符】 class 外部类{
【其他修饰符】 【static】 class 内部类{
}
}
1、静态内部类
有static修饰的成员内部类叫做静态内部类。它的特点:
-
和其他类一样,它只是定义在外部类中的另一个完整的类结构
-
可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
-
可以在静态内部类中声明属性、方法、构造器等结构,包括静态成员
-
可以使用abstract修饰,因此它也可以被其他类继承
-
可以使用final修饰,表示不能被继承
-
编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。
-
-
和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
-
外部类只允许public或缺省的
-
-
只可以在静态内部类中使用外部类的静态成员
-
在静态内部类中不能使用外部类的非静态成员哦
-
如果在内部类中有变量与外部类的静态成员变量同名,可以使用“外部类名."进行区别
-
-
在外部类的外面不需要通过外部类的对象就可以创建静态内部类的对象(通常应该避免这样使用)
2、非静态成员内部类
没有static修饰的成员内部类叫做非静态内部类。非静态内部类的特点:
-
和其他类一样,它只是定义在外部类中的另一个完整的类结构
-
可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
-
可以在非静态内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,但是可以继承父类的静态成员,而且可以声明静态常量。
-
可以使用abstract修饰,因此它也可以被其他类继承
-
可以使用final修饰,表示不能被继承
-
编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。
-
-
和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
-
外部类只允许public或缺省的
-
-
还可以在非静态内部类中使用外部类的所有成员,哪怕是私有的
-
在外部类的静态成员中不可以使用非静态内部类哦
-
就如同静态方法中不能访问本类的非静态成员变量和非静态方法一样
-
-
在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象(通常应该避免这样使用)
-
如果要在外部类的外面使用非静态内部类的对象,通常在外部类中提供一个方法来返回这个非静态内部类的对象比较合适
-
因此在非静态内部类的方法中有两个this对象,一个是外部类的this对象,一个是内部类的this对象
-
public class TestMemberInnerClass {
public static void main(String[] args) {
Outer.outMethod();
System.out.println("-----------------------");
Outer out = new Outer();
out.outFun();
System.out.println("-----------------------------");
Outer.Inner.inMethod();
System.out.println("------------------------");
Outer.Inner inner = new Outer.Inner();
inner.inFun();
System.out.println("-----------------------------");
Outer outer = new Outer();
// Outer.Nei nei = outer.new Nei();
Outer.Nei nei = out.getNei();
nei.inFun();
}
}
class Outer{
private static String a = "外部类的静态a";
private static String b = "外部类的静态b";
private String c = "外部类对象的非静态c";
private String d = "外部类对象的非静态d";
static class Inner{
private static String a ="静态内部类的静态a";
private String c = "静态内部类对象的非静态c";
public static void inMethod(){
System.out.println("Inner.inMethod");
System.out.println("Outer.a = " + Outer.a);
System.out.println("Inner.a = " + a);
System.out.println("b = " + b);
// System.out.println("c = " + c);//不能访问外部类和自己的非静态成员
// System.out.println("d = " + d);//不能访问外部类的非静态成员
}
public void inFun(){
System.out.println("Inner.inFun");
System.out.println("Outer.a = " + Outer.a);
System.out.println("Inner.a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
// System.out.println("d = " + d);//不能访问外部类的非静态成员
}
}
class Nei{
private String a = "非静态内部类对象的非静态a";
private String c = "非静态内部类对象的非静态c";
public void inFun(){
System.out.println("Nei.inFun");
System.out.println("Outer.a = " + Outer.a);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("Outer.c = " + Outer.this.c);
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
public static void outMethod(){
System.out.println("Outer.outMethod");
System.out.println("a = " + a);
System.out.println("Inner.a = " + Inner.a);
System.out.println("b = " + b);
// System.out.println("c = " + c);
// System.out.println("d = " + d);
Inner in = new Inner();
System.out.println("in.c = " + in.c);
}
public void outFun(){
System.out.println("Outer.outFun");
System.out.println("a = " + a);
System.out.println("Inner.a = " + Inner.a);
System.out.println("b = " + b);
System.out.println("c = " + c);
System.out.println("d = " + d);
Inner in = new Inner();
System.out.println("in.c = " + in.c);
}
public Nei getNei(){
return new Nei();
}
}
静态内部类 | 非静态内部类 | ||
---|---|---|---|
类角色 | 字节码文件 | 外部类名$内部类名 | 相同 |
修饰符 | public,缺省,abstract,final | 相同 | |
父类或父接口 | 可以 | 相同 | |
可以包含的成员 | 所有成员 | 不允许有静态成员 | |
成员角色 | 修饰符 | public、protected、缺省、private,final,static | 没有static |
依赖于外部类 | 依赖 | 相同 | |
依赖于外部类的对象 | 不依赖 | 依赖 | |
使用 | 在外部类中使用内部类 | 没有限制 | 在外部类的静态方法等中不能使用非静态内部类 |
在内部类中使用外部类 | 静态内部类中不能使用外部类的非静态成员 | 没有限制 | |
在外部类的外面使用内部类的静态成员 | 外部类名.静态内部类名.静态成员 | 没有 | |
在外部类的外面使用内部类的非静态成员 | 见下面的框1 | 见下面的框2 | |
重名 | 外部类名.重名的成员名 | 外部类名.this.重名的成员 |
外部类名.静态内部类名 变量 = 外部类名.静态内部类名(); 变量.非静态成员();
外部类名 变量1 = new 外部类(); 外部类名.非静态内部类名 变量 = 变量1.new 非静态内部类名(); 变量.非静态成员();
7.6.4 局部内部类
1、局部内部类
语法格式:
【修饰符】 class 外部类{
【修饰符】 返回值类型 方法名(【形参列表】){
【final/abstract】 class 内部类{
}
}
}
局部内部类的特点:
-
和外部类一样,它只是定义在外部类的某个方法中的另一个完整的类结构
-
可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
-
可以在局部内部类中声明属性、方法、构造器等结构,但不包括静态成员,除非是从父类继承的或静态常量
-
可以使用abstract修饰,因此它也可以被同一个方法的在它后面的其他内部类继承
-
可以使用final修饰,表示不能被继承
-
编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
-
这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类
-
-
-
和成员内部类不同的是,它前面不能有权限修饰符等
-
局部内部类如同局部变量一样,有作用域
-
局部内部类中是否能访问外部类的非静态的成员,取决于所在的方法
-
局部内部类中还可以使用所在方法的局部常量,即用final声明的局部变量
-
JDK1.8之后,如果某个局部变量在局部内部类中被使用了,自动加final
-
为什么在局部内部类中使用外部类方法的局部变量要加final呢?考虑生命周期问题。
-
public class TestLocalInner {
public static void main(String[] args) {
Runner runner = Outer.getRunner();
runner.run();
System.out.println("-------------------");
Outer.outMethod();
System.out.println("-------------------");
Outer out = new Outer();
out.outTest();
}
}
class Outer{
private static String a = "外部类的静态变量a";
private String b = "外部类对象的非静态变量b";
public static void outMethod(){
System.out.println("Outer.outMethod");
final String c = "局部变量c";
class Inner{
public void inMethod(){
System.out.println("Inner.inMethod");
System.out.println("out.a = " + a);
// System.out.println("out.b = " + b);//错误的,因为outMethod是静态的
System.out.println("out.local.c = " + c);
}
}
Inner in = new Inner();
in.inMethod();
}
public void outTest(){
class Inner{
public void inMethod(){
System.out.println("out.a = " + a);
System.out.println("out.b = " + b);//可以,因为outTest是非静态的
}
}
Inner in = new Inner();
in.inMethod();
}
public static Runner getRunner(){
class LocalRunner implements Runner{
@Override
public void run() {
System.out.println("LocalRunner.run");
}
}
return new LocalRunner();
}
}
interface Runner{
void run();
}
2、匿名内部类
当我们在开发过程中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,而且逻辑代码也不复杂。那么我们原先怎么做的呢?
(1)编写类,继承这个父类或实现这个接口
(2)重写父类或父接口的方法
(3)创建这个子类或实现类的对象
这里,因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。
new 父类(【实参列表】){
重写方法...
}
//()中是否需要【实参列表】,看你想要让这个匿名内部类调用父类的哪个构造器,如果调用父类的无参构造,那么()中就不用写参数,如果调用父类的有参构造,那么()中需要传入实参
new 父接口(){
重写方法...
}
//()中没有参数,因为此时匿名内部类的父类是Object类,它只有一个无参构造
匿名内部类是没有名字的类,因此在声明类的同时就创建好了唯一的对象。
注意:
匿名内部类是一种特殊的局部内部类,只不过没有名称而已。所有局部内部类的限制都适用于匿名内部类。例如:
-
在匿名内部类中是否可以使用外部类的非静态成员变量,看所在方法是否静态
-
在匿名内部类中如果需要访问当前方法的局部变量,该局部变量需要加final
思考:这个对象能做什么呢?
(1)使用匿名内部类的对象直接调用方法
interface A{
void a();
}
public class Test{
public static void main(String[] args){
new A(){
@Override
public void a() {
System.out.println("aaaa");
}
}.a();
}
}
(2)通过父类或父接口的变量多态引用匿名内部类的对象
interface A{
void a();
}
public class Test{
public static void main(String[] args){
A obj = new A(){
@Override
public void a() {
System.out.println("aaaa");
}
};
obj.a();
}
}
(3)匿名内部类的对象作为实参
interface A{
void method();
}
public class Test{
public static void test(A a){
a.method();
}
public static void main(String[] args){
test(new A(){
@Override
public void method() {
System.out.println("aaaa");
}
});
}
}