嵌套类的定义
- 重点是嵌套,一个类定义在别的类内部;
//文件名为 B.java
public class B {
//A类在B类中定义,嵌套
class A {
}
}
//文件名为D.java
//类C与D是并列的,无从属关系
class C {
}
public class D {
}
- 嵌套类:Nested classes
----静态嵌套类:Static nested classes,即 类前面有static 修饰符;
----非静态嵌套类:Non-static nested classes,又名 内部类,Inner classes:
- 普通内部类(成员内部类);
- 局部内部类;
- 匿名内部类;
静态嵌套类
public class Outer1 {
String name;
//静态嵌套类
static class Inner1 {
}
}
成员内部类
public class Outer2 {
String name;
//普通内部类/成员内部类
public class Inner2 {
}
}
局部内部类
public class Outer3 {
String name;
//局部内部类
public void f1() {
class Inner3 {
String name;
}
}
}
匿名内部类
public class Outer4 {
String name;
//匿名内部类
public void f1() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello!");
}
}).start();
}
}
这个匿名内部类没有名字,它实现了Runnable接口,只调用一次;
为什么需要嵌套类
- 不同的访问权限要求,更细粒度的访问控制;
- 简洁,避免过多的类定义;
- 缺点:设计过于复杂,较难学习和使用;
静态嵌套类详解
- 层级和包围类的成员变量/方法一样;
public class Outer1 {
String name;
//静态嵌套类
static class Inner1{
String name;
}
}
---测试类
public class MyTest1 {
public static void main(String[] args) {
Outer1.Inner1 obj = new Outer1.Inner1();
//第三方需要通过外部包围类才可以访问到静态嵌套类
}
}
- 静态内部类分析:
----测试类
public class Outer2Test {
public static void main(String[] args) {
//第三方类访问静态嵌套类
Outer2.Inner1 obj1 = new Outer2.Inner1();
obj1.getInnFlied1();
Outer2.Inner1 obj2 = new Outer2.Inner1();
System.out.println(obj1 == obj2);
System.out.println("===================");
Outer2 obj3 = new Outer2();
obj3.outFun2();
/*false
===================
false
inner 333
inner hello
inner static 444*/
}
}
----静态内部类
public class Outer2 {
private static String outStaticFiled2 = "outer static 222";
private String outFiled = "outer 111";
public void outFun2() {
Inner1 obj1 = new Inner1();
Inner1 obj2 = new Inner1();
System.out.println(obj1 == obj2);//false
System.out.println(obj1.getInnFlied1());//inner 333
System.out.println(Inner1.getInnerStaticFiled2());//inner static 444
}
public String getOutFiled() {
return this.outFiled;
}
//静态嵌套类
// private static class Inner1
// static class Inner1
// protected static class Inner1
public static class Inner1 {
static String innerStaticFiled2 = "inner static 444";
//静态嵌套类可以定义静态的、非静态的成员
private String innFlied1 = "inner 333";
public static String getInnerStaticFiled2() {
hello();
//Outer2.hello();
return innerStaticFiled2;
}
private static void hello() {
System.out.println("inner hello");
}
public String getInnFlied1() {
return innFlied1;
}
public void innFun1() {
//静态嵌套类可以直接访问包围类的静态成员
System.out.println(outStaticFiled2);
//System.out.println(outFiled);--error
//静态嵌套类可以通过对象访问包围类的非静态成员
Outer2 obj = new Outer2();
System.out.println(obj.getOutFiled());
}
}
}
- 需要加修饰符static;
- 可以定义静态成员和非静态成员;
- 不能直接访问包围类的非静态成员,可直接访问包围类的静态成员;
- 可以通过包围类的对象进行访问非静态成员;
- 外界可以通过静态嵌套类名访问其静态成员,通过对象访问其非静态成员;
- 外界需要通过包围类才可以访问到静态嵌套类,并创建其对象,不需要外部包围类的实例;
- 和正常的类没有什么区别,纯粹只是为了打包的便利性存在;
成员内部类详解
- 非static的类,定义在某个类的成员变量的位置;
- 定义后,在类里面均可以使用;
public class Outer3 {
public Bird obj = new Bird();
String name = "aaaaaa";
public Bird getBird(){
return this.obj;
}
public void f1() {
obj.fly();
System.out.println(obj.getClass().getName());
this.name = "bbbbbbb";
obj.fly();
}
public class Bird extends Animal implements Flyable {
//常量OK
public static final int a = 3;
public void eat() {
System.out.println("I can eat.");
}
public void fly() {
System.out.println("I can fly." + name);
}
}
}
----测试类:
public class Outer3Test {
public static void main(String[] args) {
Outer3 foo1 = new Outer3();
foo1.f1();
Outer3.Bird foo2 = foo1.new Bird();
foo2.fly();
System.out.println(foo2 == foo1.getBird());
/*I can fly.aaaaaa
org.westos.demo1.Outer3$Bird
I can fly.bbbbbbb
I can fly.bbbbbbb
false*/
}
}
如果在测试类里面直接写上
Outer3.Bird foo3 = new Outer3.Bird();
会报错,不允许没有关联的单独的普通内部类对象,也就是说,你必须通过外围类的实例才能new出普通内部类的对象;
- 编译后名称:外部类名+$+内部类名;
- 可以继承其他类,也可以实现其它接口;
- 可以用private/package private(不写)/protected/public 控制外界访问;
- 非静态的类,不能包含静态变量/方法,除了常量;
- 和外部包围类的实例相关,一个普通内部类的实例肯定是在一个外部包围类的实例中,且可以访问外部包围类的所有成员;
- 在第三方类中,需要首先创建外部包围类的实例,才能创建普通内部类的实例,不允许单独的普通内部类对象存在!
局部内部类详解
- 定义在代码块中的非静态的类,如方法、for循环、if语句等;
- 定义后,即可创建对象使用;
- 只能存活在这个代码块中,代码快结束,外界无法使用该类;
public class MyTest1 {
String name = "abc";
//局部内部类
public static void f2() {
final String name = "def";
class Inner2 {
public void f2() {
//只能访问常量
System.out.println(name);
//System.out.println(MyTest1.this.name);
//静态方法只能访问静态变量
}
}
Inner2 obj1 = new Inner2();
obj1.f2();
System.out.println(obj1.getClass().getName());
}
//局部内部类
public void f1() {
String name = "def";
class Inner2 {
final static int a = 1;
String name = "ghi";
//static int b=2;
//不允许定义静态变量
public void f2() {
System.out.println(name);
System.out.println(MyTest1.this.name);
}
//不允许定义静态方法
// public static void f3(){
//
// }
}
Inner2 obj1 = new Inner2();
obj1.f2();
System.out.println(obj1.getClass().getName());
}
}
- 编译后名称:外部类名+$+序号+内部类名;
- 可以继承其他类或者实现其它接口;
- 非静态的类,不能包含静态变量(成员+方法),除了常量;
- 可以访问外部包围类的成员;
- 如果定义在静态方法中,只能访问包围类的静态成员;
- 局部内部类不能是一个接口,即接口不能定义在代码块中;
- 可以重用;
匿名内部类详解
- 没有类名的内部类,必须继承一个父类/实现一个父接口;
- 在实例化以后,迅速转型为父类/父接口;
- 这种类型的对象,只能new一个对象,之后以对象名操作;
- 可以在普通语句和成员变量赋值时使用内部类;
public class MyTest {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
};
new Thread(r).start();
}
}
public class Outer1 {
private String name = "abc";
//public static void f1()
public void f1() {
String name = "def";
Runnable r = new Runnable() {
//匿名内部类不能定义静态变量,除非是常量
public final static int a = 5;
//public static int b = 3;
String name = "ghi";
@Override
public void run() {
System.out.println("hello" + name);
//拼接外部方法的临时变量
}
//静态方法不能在匿名内部类里面定义
/*public static void f2(){
}*/
};
new Thread(r).start();
System.out.println(r.getClass().getName());
Runnable r2 = new Runnable() {
@Override
public void run() {
//访问到最外层的变量
System.out.println("hello" + Outer1.this.name);
}
};
new Thread(r2).start();
System.out.println(r2.getClass().getName());
}
}
两个匿名内部类不是同一个类,编译结果为:
- 可以继承、改写、补充父类/父接口的方法;
- 内部不可以新定义静态成员(变量+方法),常量除外;
final static int a = 5;
- 没有正式类名的内部类:
编译器产生内部名字:类名+$+数字编号
- 可以访问外部包围类的成员变量和方法(包括private);
- 如果定义在静态方法中,也只能访问外部包围类的静态成员;
- 没有类名,外部包围类和其他类也无法访问到匿名内部类;
- 匿名内部类很简洁;
嵌套类的对比
- Oracle官方文档这样描述对比:
匿名内部类:
- 应用它,如果需要定义额外的变量和方法;
局部内部类:- 在一个方法内,需要创建一个新的类型,并重复使用;
普通内部类:- he局部内部类相似,在一个类中定义,可重复使用,可以访问外部类的成员,但不需要访问外部类方法的形参和内部变量;
静态内部类:- 在一个类中定义,可重复使用,并需要访问外部类的静态成员;
- 外部访问和修饰符的关系:
- 普通内部类和静态嵌套类可以被外部访问;
- 外部访问普通内部类和静态嵌套类,和普通类之间访问规则一样;
变量遮蔽
嵌套类变量和外部包围类的变量重名:
- 就近优先原则;
- 优先级高的变量会遮蔽优先级低的变量;
- 外部包围类.this.变量名,可以访问到外部包围类的成员变量;
- 静态嵌套类不能访问非静态变量;
- Java 7及以前,匿名内部类和局部内部类只能访问外部包围类的final成员变量;
- Java 8之后,匿名内部类可以访问外部包围类的final成员变量和事实意义上的final变量(一个变量定值后,再也没有改过值);
public class shadowTest {
private int x = 0;
public static void main(String[] args) {
shadowTest st = new shadowTest();
FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(20);
/*x=20
this.x=1
ShadowText.this.x=0*/
}
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
System.out.println("x=" + x);
System.out.println("this.x=" + this.x);
System.out.println("ShadowText.this.x=" + shadowTest.this.x);
}
}
}
public class shadowTest2 {
private int x = 0;
public void f1() {
int x = 20;
//局部内部类无法访问!
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
System.out.println("x=" + x);
System.out.println("this.x=" + this.x);
System.out.println("ShadowTest2.this.x=" + shadowTest2.this.x);
}
}
FirstLevel obj = new FirstLevel();
obj.methodInFirstLevel(10);
}
}
public class shadowTest3 {
private int x = 0;
public void f1() {
int x = 20;
//可以访问
class FirstLevel {
public int x = 20;
void methodInFirstLevel() {
System.out.println("x=" + x);
System.out.println("ShadowTest3.this.x=" + shadowTest3.this.x);
}
}
FirstLevel obj = new FirstLevel();
obj.methodInFirstLevel();//x=20
}
public static void main(String[] args) {
shadowTest3 st = new shadowTest3();
st.f1();
/*x=20
ShadowTest3.this.x=0*/
}
}
嵌套类的应用
- 匿名内部类:
无需类名,用过即焚,使用广泛,且方法只有一个,代码短;
Android中常用的匿名内部类:
- 局部内部类:
定义在方法体内,只能在当前方法内使用,代码短,使用较少;
介于匿名内部类和普通内部类之间;
继承某一个类或接口,重新定义方法,并当做返回值在外部使用;
- 普通内部类
广泛使用在具有母子结构的类,内部类对象和外围类保持联系;
如Map和Map.Entry,ZipFile和ZipFile.ZipEntryIterator等;
- 静态嵌套类
—和普通类一致,只是"碰巧"声明在一个外围类的内部;
—和外围类没有太多的联系,可以脱离外围类对象存在,也可以访问外围类静态成员;
—如果不需要访问外围类的非静态成员,尽量将普通内部类变更为静态嵌套类;
- 节省了内部类和外围类的联系开销,使得外围类更容易被垃圾回收器回收;