Java嵌套类

第一节嵌套类入门

• 嵌套类
– 重点是嵌套,一个类定义在别的类的内部

• 嵌套类: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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值