第一节嵌套类入门
• 嵌套类
– 重点是嵌套,一个类定义在别的类的内部
• 嵌套类:Nested classes(分两种)
– 1.静态嵌套类:Static nested classes,即类前面有static修饰符
– 2.非静态嵌套类:Non-static nested classes, 又名内部类,Inner classes(分三种)
• 普通内部类(亦翻译为:成员内部类)
• 局部内部类(Local classes)
• 匿名内部类(Anonymous classes)
– https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
四种
1.静态嵌套类
class Outer{
//静态嵌套类
static class Inner{
void method1(){
System.out.println (“静态嵌套类中的普通方法");
}
static void method2(){
System.out.println (“静态嵌套类中的静态方法");
}
}
}
2.普通内部类/成员内部类
class Student{
……
class Birthday{
int year;
int month;
int day;
……
}
……
3.局部内部类 : 放在局部方法里面或者放在某一个局部的代码块
class Student
……
void printInfo(){
System.out.println ("姓名:"+name);
System.out.println ("性别:"+sex);
birth.printBirth();
class CalcAge{
int calc(int thisYear){
return thisYear-birth.year;
}
}
CalcAge ca=new CalcAge();
System.out.println ("年龄:"+ca.calc(2010));
}
……
}
4.匿名内部类
public static void main (String[] args) {
Person sp=new Person("李红",21) {
//重写抽象父类Person类的抽象方法
public void work(){
System.out.println ("个人信息:");
this.printInfo();
System.out.println ("我是一个java程序开发人员");
}
};
sp.work();
}
看不懂解释一下:
new子类(父类是抽象类,重写work方法)
子类没有名字了
注:1,2都需要成员类
• 为什么需要嵌套类
– 不同的访问权限要求,更细粒度的访问控制
– 简洁,避免过多的类定义
– 语言设计过于复杂,较难学习和使用
第二节 匿名内部类和局部内部类
匿名内部类
• 匿名内部类: Anonymous classes
– 没有类名的内部类,(必须继承一个父类/实现一个父接口)*
– 在实例化以后,迅速转型为父类/父接口
– 这种类型的对象,只能new一个对象,之后以对象名字操作
– 可在普通语句和成员变量赋值时使用内部类
– 没有正式类名的内部类
• 编译器产生内部名字:类名+$+数字编号
– 没有类名,没有构造函数,能用父类/父接口的构造函数(可带参数)
– 可以继承、改写、补充父类/父接口的方法
– 内部不可以新定义静态成员(变量+方法)*,常量除外
• final static int a = 5;
– 可以访问外部包围类的成员变量和方法(包括private)
– 如果定义在静态方法中,也只能访问外部包围类的静态成员
– 没有类名,外部包围类和其他类也无法访问到匿名内部类
package comparison;
public class Outer1 {
private String name = "abc";
public static int no = 5;
//匿名内部类可在成员变量声明
Runnable r1 = new Runnable() {
final static int a = 5;
public void run(){
System.out.println("hello " + name + no);
}
};
public void f1()
{
//匿名内部类可在成员方法内声明
Runnable r2 = new Runnable() {
//static int a = 5;
public void run(){
System.out.println("hello ");
}
};
}
}
局部内部类
• 局部内部类:Local classes
– 定义在代码块中的非静态的类,如方法,for循环,if语句等
– 定义后,即可创建对象使用
– 只能活在这个代码块中,代码块结束后,外界无法使用该类
• 局部内部类:Local classes
– 编译后名称:外部类名+$+序号+内部类名
– 可以继承其他类,或者实现其他接口
– 非静态的类,不能包含静态成员(变量和方法),除了常量
– 可以访问外部包围类的成员
– 如果定义在静态方法中,只能访问包围类的静态成员
– 局部内部类不能是一个接口,即接口不能定义在代码块中
如果访问外围的变量(必须是final)*
总结
• 了解匿名内部类和局部内部类
– 两者几乎相似
– 局部内部类可以重用,匿名内部类不能重用
– 匿名内部类更简洁
第三节 普通内部类和静态嵌套类
普通内部类
• 普通内部类
– 非static的类,定义在某个类的成员变量位置
– 定义后,在类里面均可以使用
•普通内部类
–编译后名称:外部类名+$+内部类名
–可以继承其他类,或者实现其他接口
–可以用private/package private(不写)/protected/public控制外界访问
–(非静态的类,不能包含静态变量/方法,除了常量)*
–和外部包围类的实例相关,一个普通内部类实例肯定是在一个外部包围类的实例中,且可以访问外部包围类的所有成员
–在第三方类中,需要先创建外部包围类实例,才能创建普通内部类的实例,不允许单独的普通内部类对象存在!!!
注意:
局部内部类和匿名内部类,外围访问不到,普通内部类通过包围对象的实例点new来访问。
private外部无法访问普通内部类的,哪怕用对象.new
创建的普通内部类对象两种方式
方式一:
外部类 外部类对象名 = new 外部类();
外部类.成员内部类 对象名 = 外部类对象名.new成员内部类 ();
如:
Student s2=new Student();
Student.Birthday birth2=s2.new Birthday(1988,8,17);
方式二:
外部类.成员内部类 对象名 = new 外部类().new成员内部类 ();
如:Student.Birthday birth1=new Student().new Birthday(1990,10,12);
静态嵌套类
• 静态嵌套类
– 层级和包围类(enclosing class)的成员变量/方法一样
– 第三方需要通过外部包围类才可以访问到静态嵌套类**(创建对象方式)**
• Outer1.Inner1 obj = new Outer1.Inner1();
外部包围类的名字.就可以了
静态嵌套类的static不再表示唯一的,修饰类的时候不表示唯一,修饰变量的时候表示唯一
• 静态嵌套类的语法总结
– 需要加修饰符static
– 可以定义静态成员和非静态成员
– 不能直接访问包围类的非静态成员,可直接访问包围类的静态成员
• 可通过包围类的对象进行访问非静态成员
– 外界可以通过静态嵌套类名访问其静态成员,通过对象访问其非静态成员
– 外界需要通过包围类才可以访问到静态嵌套类,并创建其对象,不需要外部包围类的实例
• 静态嵌套类的语法总结(续)
–官方教程:A static nested class interacts with the instance
members of its outer class (and other classes) just like any other
top-level class. In effect, a static nested class is behaviorally a top-level class
that has been nested in another top-level class for packaging convenience.
翻译:
静态嵌套类与实例交互它的外部类(和其他类)的成员和其他类一样
顶级课程。实际上,静态嵌套类在行为上是顶级类
为了便于打包,已经嵌套在另一个顶级类中。
– 可以用private/package private(不写)/protected/public控制外界访问
总结:
• 了解普通内部类和静态嵌套类
– 两者都定义在外围类中的成员级别
– 静态嵌套类不依赖于外围类对象,但是只能访问外围类的静态成员
– 普通内部类必须依赖于外围类对象,不能单独存在,但是可以访问外围类的所有成员
第四节 嵌套类对比
• Oracle官方文档比较
– 匿名内部类:Use it if you need to declare fields or additional methods.
应用它,如果需要定义额外的变量和方法
– 局部内部类:Use it if you need to create more than one instance of a class, access its constructor, or introduce a new, named type (because, for example, you need to invoke additional methods later).
在一个方法内,需要创建一个新的类型,并重复使用
• Oracle官方文档比较
– 普通内部类:Use it if your requirements are similar to those of a local class, you want to make the type more widely available, and you don’t require access to local variables or method parameters.
和局部内部类相似,在一个类中定义,可重复使用,可以访问外部类的成员,但不需要访问外部类方法的形参和内部变量
– 静态嵌套类:Use a static nested class if you don’t require access an enclosing instance’s non-public fields and methods
在一个类中定义,可重复使用,并需要访问外部类的静态成员
外部访问规则
• 外部访问和修饰符关系
– 普通内部类和静态嵌套类可以被外部访问
– 外部访问普通内部类和静态嵌套类,和普通类之间访问规则一样
变量遮蔽:Shadowing
– 嵌套类变量和外部包围类的变量重名
• 以离得近作为优先原则
• 优先级高的变量会遮蔽优先级低的变量
• 外部包围类.this.变量名,可以访问到外部包围类的成员变量
• 静态嵌套类不能访问非静态变量
• Java 7及以前,匿名内部类和局部内部类只能访问外部包围类的final成员变量
• Java 8及以后,匿名内部类和局部内部类可访问外部包围类的final成员变量和事实意义上的final变量(effectively final, 一个变量定值后,再也没有改过值)
ShadowTest2.java
public class ShadowTest2 {
public int x = 0;
public void f1()
{
int x = 20; //局部内部类无法访问得到!
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
System.out.println("x = " + x); //第12行
System.out.println("this.x = " + this.x); // 第10行
System.out.println("ShadowTest.this.x = " + ShadowTest2.this.x); //第4行
}
}
FirstLevel obj = new FirstLevel();
obj.methodInFirstLevel(10);
}
public static void main(String... args) {
ShadowTest2 st = new ShadowTest2();
st.f1();;
}
}
x = 10
this.x = 1
ShadowTest.this.x = 0
ShadowTest3.java
public class ShadowTest3 {
public int x = 0;
public void f1()
{
int x = 20; //可以访问得到,没有被遮蔽!
class FirstLevel {
void methodInFirstLevel() {
System.out.println("x = " + x); //第8行
System.out.println("ShadowTest.this.x = " + ShadowTest3.this.x); //第4行
}
}
//x=30;
FirstLevel obj = new FirstLevel();
obj.methodInFirstLevel();
}
public static void main(String... args) {
ShadowTest3 st = new ShadowTest3();
st.f1();;
}
}
x = 20
ShadowTest.this.x = 0
对比ShadowTest2.java和ShadowTest3.java会发现加上this导致20被屏蔽
第五节 嵌套类应用
• 匿名内部类
– 无需类名,用过即焚,使用广泛
– 该类的对象只要一个,且方法只有一个,代码短
• Android中常用匿名内部类
• 局部内部类
– 定义在方法体内,只能在当前方法内使用,代码短
– 使用较少
– 介于匿名内部类和普通内部类之间
• 只用一次,就用匿名内部类,简便
• 使用多次,那就上升到普通内部类,整个类都可以使用
• 局部内部类
– 继承某一个类或接口,重新定义方法,并当作返回值在外部使用
– 如java.util.regex.Pattern的splitAsStream方法的局部内部类
• 普通内部类
– 广泛使用在具有母子结构的类,内部类对象和外围类保持联系
– 如Map和Map.Entry,ZipFile和ZipFile.ZipEntryIterator等
• 静态嵌套类
– 和普通类一致,只是“碰巧”声明在一个外围类的内部
– 和外围类没有太多的联系,可以脱离外围类对象存在,也可以访问外围类的静态成员
– 如果不需要访问外围类的非静态成员,尽量将普通内部类变更为静态嵌套类
• 节省普通内部类和外围类的联系开销
• 使得外围类对象更容易被垃圾回收器回收
• Java 8中的rt.jar所包含的嵌套类数据
• 所有类:19730个
• 嵌套类:7115
– 匿名内部类:2295个
– 局部内部类:46个
– 普通内部类:3618个
– 静态嵌套类:1156个
•注:此数据是基于Oracle JDK 1.8.0_45 64位Windows版本统计得出的。对于不同版本的JDK,可能略有差别。
• 嵌套类的作用(官方文档)
– https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
– 逻辑上,是一种对类分组的好办法,限定某类只能在一个地方使用。
It is a way of logically grouping classes that are only used in one place.
– 增加了封装。 It increases encapsulation.
– 使得代码更加容易阅读和维护。 It can lead to more readable and maintainable code.