Java 内部类 成员内部类 静态内部类 局部内部类 匿名内部类

前言

Java中,有一些类存在于其他类的内部,有一些类,内部存在其他类。所谓内部类就是定义在类中的类,而包含这个内部类的类就是外部类
与类中变量或是对象相似的是,内部类根据不同的位置和效用分为:成员内部类,局部内部类,匿名内部类和静态内部类
下面来总结一下:


成员内部类

作为类成员出现的内部类,它的作用范围和成员变量十分相似。
先写一个带有成员内部类的类:

class OuterClass
{
	private int num = 0;
	public void f()
	{
		System.out.print("Hello World!!");
	}
	
	class InnerClass
	{
		public void doSomething()
		{
			f();
			System.out.println(num);
		}
	}
}

内外类的方法和变量

在外部类中,两者的变量和方法是相互使用的,内部类可以访问外部类的成员和方法,外部类可以通过内部类对象访问内部类成员和方法。即使成员或是方法是被private修饰的。

在外部类外,通过内部类对象是无法访问到内部类的private的,这点同外部类。

那么既然可以相互调用,就必然会面临着命名冲突的问题,这里的命名冲突包含着内部类局部变量内部类成员变量外部类成员变量之间的冲突。为了解决这个问题,java采取的机制和解决类方法中局部变量与成员变量冲突的解决方案:使用this关键字和外部类调用。
具体的,在内部类方法中,若命名产生冲突(极短一些的情况,假设上述三种变量都存在冲突),我们可以:

  • 直接使用变量名,访问内部类局部变量
  • 使用this关键字,访问内部类成员变量
  • 使用 外部类名.this,访问外部类成员变量
    可参考以下代码:
class Outer
{
	private int x;
	class Inner
	{
		private int x;
		public void doSomething(int x)
		{
			x = 1;
			this.x = 2;
			Outer.this.x = 3;
		}
	}
}

实例化内部类

在外部类外面:

内部类的对象必须依靠外部类的对象!!
内部类的对象必须依靠外部类的对象!!
内部类的对象必须依靠外部类的对象!!

(重要的事情说三遍)

也就是说,如果没有已经存在的外部类对象,就不可能出现其成员内部类的对象。

实例化内部类对象语法为:

外部类.内部类 对象名 = 外部类对象.new 内部类();

写成代码:

OuterClass out = new OuterClass();
OuterClass.InnerClass in = out.new InnerClass();

先声明并实例化外部类对象,接着在外部类的对象中new关键字实例内部类对象。


在外部类中,可以直接创建外部类对象。
于是,也可以在外部类中提供一个创建内部类对象的方法,从而通过调用这个方法来获得内部类对象。
例如,在外部类中加入这个方法:

public InnerClass getInner()
{
	return new InnerClass();
}

然后通过调用这个方法来实例化内部类:

OuterClass oc = new OuterClass();
OuterClass.InnerClass ic = oc.getInner();

但是,需要加以注意的是,这样的定义方式,仍需要外部类的对象存在来调用该方法。

什么?你说把那个方法写成静态的不就可以通过类来调用啦?
回答这个问题,只需要注意到类内容装载时,尽管装载了这个静态方法,却是不装载成员内部类的内容的,所以在检查到这个方法的时候,解释器会提示不认识它而报错。不信你看:
在这里插入图片描述
报错信息:
在这里插入图片描述
什么?把内部类也改成静态的不就好了吗?emm,你说得对,但那就不是成员内部类了呀/doge。这个不是成员内部类的静态内部类将会在最后一部分详细阐述。

最后再来插一嘴,成员内部类和成员变量一样也可以指定作用域,它决定了什么地方可以访问到它。若是修饰为private,那可就只能在外部类内部用或是借助外部类的方法获得对象了,也应该注意到,private限制的内部类是不能在外部类外通过外部类名访问到类名的:类似于这样:
在这里插入图片描述
通过外部类访问内部类报错了。
在这里插入图片描述


局部内部类

有了成员内部列的铺垫,局部内部类就变得更易理解了。

首先,定义局部内部类为定义在外部类方法或任意的作用于中定义的内部类可以粗略的理解为,将内部类挪到了外部类的类方法中,这样,它的作用域就从外部类的成员变为了方法内的局部了,于是命名局部内部类。

在使用上,需要注意的就是像刚刚所说。这个类是方法的一部分,而非外部类的一部分
除此以外,还应注意,局部内部类仅能访问方法中的被final修饰的常量,且不能修改其值

如果写成这样:

class Outer
{
	public void doSomethingOut(final int x,int y)
	{
		class Inner
		{
			private int a = x;
			private int b = y;
		}
	}
}

访问y是会被报错的:
在这里插入图片描述


匿名内部类

接着来介绍一个比较特殊的类,这个类有些惨,他们总是被一次性使用,也因此没有自己的类名,成全了别的类,自己却不留功与名。
那么这个类从那里来的呢,还得从接口实例化对象说起。众所周知,接口是一种抽象类,而一个抽象类是无法实例化自身类的对象的,除非下转型为子类的对象。这个性质对于接口来讲,想要给一个接口类型的变量实例化对象,就必须实例化一个实现了该接口的类
有时候,实例化一个接口对象只是希望这个对象能够纯粹的实现接口中的方法,可以选择写一个类来实现这个接口并重写这些方法,但又不太需要一直保留这个类,毕竟它占着一个命名。
所以java提供了匿名内部类这种类,可以在实现接口时临时为接口编写一次性的实现接口的类,而由于是一次性的使用,这样的类没有命名,于是成为匿名内部类。

具体的语法如下:

接口名 变量 = new 接口(){
	匿名内部类内容
};

需要注意的有两点:

  • 接口变量引用的,是匿名内部类的对象而非接口的对象
  • 在实现内部类后,不要忘记写分号
    举个例子:
interface A
{
	public void doSomething();
}
public class Main {
	public static void main(String[] args)
	{
		A a = new A(){
			public void doSomething()
			{
				System.out.print("hello world");
			}
		};
	}
}

当然,也可以选择在继承了某个接口的类中实现一个通过匿名内部类获得接口对象的方法。
例如:

class aClass implements Comparator<Integer>
{
	public int compare(Integer o1, Integer o2) {
		return o1 - o2;
	}
	static public Comparator<Integer> getComparator()
	{
		return new Comparator<Integer>(){
			public int compare(Integer o1,Integer o2)
			{
				return o1 - o2;
			}
		};
	}
}
public class Main {
	public static void main(String[] args)
	{
		Comparator<Integer> judge = aClass.getComparator();
	}
}

静态内部类

最后,让我们来分析一下这个在一开始就提到的内部类。

在内部类前面添加修饰符static,我们就得到了一个静态内部类。

由于它是静态的,导致了它在表现出的性质上与其他内部类存在不同,不同之处有:

  • 静态内部类可以声明静态成员,而非静态内部类不可以
  • 静态内部类不可以访问外部类的非静态成员
  • 创建静态内部类对象不需要外部类对象
    可参考下图:
    在这里插入图片描述
发布了17 篇原创文章 · 获赞 2 · 访问量 459

猜你喜欢

转载自blog.csdn.net/wayne_lee_lwc/article/details/104626386