超详细深入浅出Java内部类(通俗易懂+代码示例)

可能有一些小伙伴会问我,不是说详细吗?连个目录都没,你咋这么懒呢 …嗯 其实呢,不是我懒,确实我看到许多博客文章有列个目录,挺好的,但是呢,也有一些博客文章是没列目录,说实话,我更喜欢没列目录的,不是说更好,或者懒的借口啊,只能说每个人都有各自喜好的写作风格,而我是那种喜欢直接进入主题开始的,能把我带进去的文章,所以我也希望用这种方式,让正在看我文章的你能直接进入主题,把最核心的怼给你。

什么是内部类:

一个类内部除了有属性和方法,还可以有其他类,内部类也可以在方法或代码块中。 (往下会代码示例)

可以简单的理解为 花括号里 { } 里面再定义一个类,那么这个类就称为:内部类。 因为理论上内部类可以在类任意位置,所以代码块也好,普通方法也可以。

内部类的作用:

1.增强封装,把内部类放在外部类当中,不允许其它类访问这个内部类。

2.增加了代码一个维护性,不涉及到其他类。

3.内部类可以直接访问外部类当中的成员。

内部类分为四种:

  • 实例内部类:直接定义在类当中的一个类,在类前面没有任何一个修饰符。
  • 静态内部类:在内部类前面加上一个static。
  • 局部内部类:定义在方法当中的内部类。
  • 匿名内部类:属于局部的一种特殊情况。

实例内部类

//外部类
//外部类
 class Outer {
	 String str = "一颗剽悍的种子";
	//内部类
	class Inner{
		void fun() {
			System.out.println(str);
		}
	}
}
public class Test2{
	public static void main(String[] args) {
		Outer out = new Outer();
		//实例内部类
		out.new Inner().fun();
	}
}

运行结果
在这里插入图片描述
上面代码示例有个细节:就是我们提到过的内部类的作用, 内部类可以访问外部类的成员。

实例化内部类的格式:

new 外部类().new 内部类 ();

代码本身很简单,只要理解类的语法和对象实例化方式就可以了,了解它的底层才有趣,创建内部类的时候发生了什么? 在创建内部类的时候,会有一个外部类的引用。

我们可以用代码编译后的class文件通过我们的反编译工具来看。

class Outer$Inner
{
	final Outer this$0;

	void fun()
	{
		System.out.println(str);
	}

	Outer$Inner()
	{
		this$0 = Outer.this;
		super();
	}
}

从上面的代码可以看到,内部类当中除了有自己的引用,还有外部类的引用。在堆中相当于外部类有一个地址,内部类有两个地址,一个是自己内部类的,一个是指向外部类的地址。

静态内部类

有接触过Java 静态这个知识点的小伙伴应该都知道,谈到静态无非就是对static关键字的理解和运用,这么说你可能会好懂一些。

说得更直接点就是内部类前面加上一个 static 就是静态内部类。

//外部类
 class Outer {
	static String str = "一颗剽悍的种子";
	//静态内部类
	static class Inner{
		void fun() {
			System.out.println(str);
		}
	}
}
public class Test2{
	public static void main(String[] args) {
				new Outer.Inner().fun();;
	}
}

运行结果
在这里插入图片描述
注意点:可以看到上面成员也是用static声明静态,是因为声明static无法直接调用非static变量或方法。

为什么?

我们都知道static是最先执行,在加载字节码就会自动调用,而且在主方法main之前,比构造方法早,此时非static属性和方法还没有完成初始化呢。

还有与上面非 static 定义内部类有两点不同:

1.静态内部类的创建格式不需要实例化,具体格式如下:

new 外部类(). 内部类 ();

2.静态内部类当中没有外部类的引用。

为什么?

因为加上static 静态内部类是属于外部类的,上面所说也可以看出它没有用new 而是通过 .类的方式。

还是通过我们的反编译查看class文件,有码有真相。

static class Outer$Inner
{
	void fun()
	{
		System.out.println(Outer.str);
	}
	Outer$Inner()
	{
	}
}

局部内部类

开头说什么是内部类当中,有说到内部类不仅可以定义在外部类当中,也可以定义在方法当中,而定义在方法当中的类称为局部内部类

//外部类
class Outer {
	void fun_1() {
	String  name = "一颗剽悍的种子";
		 class Inner{
			void fun_2() {
				System.out.print(name);
			}
		}
		 Inner in = new Inner();
		 in.fun_2();
	}
}
public class Test2{
	public static void main(String[] args) {
		new Outer().fun_1();
	}
}

运行结果
在这里插入图片描述

局部内部类有什么局限性呢?

1.局部内部类只能在定义方法中使用。 可以看到代码示例,在方法中创建内部类并且调用内部类里的方法,后续实例化只要调用外部类里的 fun_1 方法就可以。

2.不能使用 public、protected、private 修饰 。 这一条是对应第一条的,你想想,你已经在方法中创建了,只在方法中使用,你还用这些权限干嘛 ???

3.局部类当中不能使用static变量。 上面静态内部类时说过,static是属于外部类的,所以在方法里的内部类定义static变量会报错。

除了局部内部类的局限性,还可以看到在代码实例中,内部类是可以直接使用方法中的变量的。

(在jdk1.7之前内部类想访问方法中的变量是需要加上final的,我们知道final是用来声明常量的,fianl需要设置好值而且不能修改。)

jdk1.8版本之后不需要,正好我的jdk1.8 所以就不用加final了,但是真实情况下,通过反编译工具看class文件会发现编译时已经帮我们自动加上了。

(你会发现随着语言的发展,让你更易于开发的同时,你离本质也更加的遥远)

class Outer$1Inner
{

	final Outer this$0;
	private final String val$name;

	void fun_2()
	{
		System.out.print(val$name);
	}

	Outer$1Inner()
	{
		this$0 = final_outer;
		val$name = String.this;
		super();
	}
}

到这里可能有小伙伴会问,为啥内部声明变量需要加上final,这里就不展开了,因为涉及到 final 在方法区以及代码运行后的堆栈关系,下次有机会再写一篇。

匿名内部类

根据字面意思就知道没有命名的内部类,那么匿名内部类就是没有构造器的,因为我们都知道类的构造器需要跟类同样的命名。

new 父类 或 接口

上面围绕四种内部类展开,实例内部类,静态内部类、局部内部类、匿名内部类,因为匿名内部类的应用是比较少,所以简单的谈谈就没有代码示例,等有用到,而且好的例子,可以再单独写一篇滴。

最后:

我是一颗剽悍的种子 把我会的,认真的分享 是我写博客一直不变的信条。如果你能看到这篇,说明咱们还是很有缘的,希望能带给你一些许帮助,创作的不易, 把我文章的知识带走,你的三连留下,点赞,评论,关注,是我最大的动力。

猜你喜欢

转载自blog.csdn.net/A_hxy/article/details/106985170