java 内部类理解

一、简介

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。

二、成员内部类

成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:

java 复制代码
package com.tfq.innerclass.member;

/**
 * @author: fqtang
 * @date: 2024/05/07/10:11
 * @description: 成员内部类--定义四边形类
 * 
 */
public class Quadrilateral {
	private int length = 0;
	private int width = 0;
	private Draw draw = null;

	public Quadrilateral(int length, int width) {
		this.length = length;
		this.width = width;
		//必须先创建成员内部类的对象,再进行访问
		//getDrawInstance().drawShape();
	}

	/**
	 * 虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。
	 * 在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
	 * @return 新创建的内部类Draw
	 */
	public Draw getDrawInstance(){
		if(draw == null)
			draw = new Draw();
		return draw;
	}

	/**
	 * 这样看起来,类Draw像是Quadrilateral类的一个成员,Quadrilateral称为外部类。
	 * 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
	 */
	class Draw {
		public void drawShape() {
			System.out.println("drawShape =" + (length == width?"正方形":"长方形"));
		}
	}
}

测试类

java 复制代码
package com.tfq.innerclass.member;



/**
 * @author: fqtang
 * @date: 2024/05/07/10:25
 * @description: 描述
 */
public class TestInnerClass {

	public static void main(String[] args) {
		// 成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,
		// 前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
		//第一种方式:
		Quadrilateral quadrilateral = new Quadrilateral(2, 2);
		Quadrilateral.Draw draw = quadrilateral.new Draw();
		draw.drawShape();

		//第二种方式:
		Quadrilateral.Draw draw1 = new Quadrilateral(2, 3).getDrawInstance();
		draw1.drawShape();

	}
}

三、局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

java 复制代码
package com.tfq.innerclass.local;

/**
 * @author: fqtang
 * @date: 2024/05/07/10:36
 * @description: 局部内部类讲解
 */
public class People {
	private String name;

	public People() {

	}

}

class Man{
	public Man(){

	}

	public People getWoman(){
		/**
		 * 注意,局部内部类就像是方法里面的一个局部变量一样,
		 * 是不能有public、protected、private以及static修饰符的。
		 */
		class Woman extends People{   //局部内部类
			int age =0;
		}
		return new Woman();
	}
}

四、匿名内部类

匿名内部类也就是没有名字的内部类

正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写

但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

实例1:不使用匿名内部类来实现抽象方法

java 复制代码
abstract class Person {
    public abstract void eat();
}
 
class Child extends Person {
    public void eat() {
        System.out.println("eat something");
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Child();
        p.eat();
    }
}

**运行结果:**eat something

可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用

但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?

这个时候就引入了匿名内部类

实例2:匿名内部类的基本实现

java 复制代码
package com.tfq.innerclass.anonymous.basedemo;

/**
 * @author: fqtang
 * @date: 2024/05/07/11:35
 * @description: 匿名内部类的基本实现
 */
abstract class Person {

	public abstract void eat();
}


package com.tfq.innerclass.anonymous.basedemo;

/**
 * @author: fqtang
 * @date: 2024/05/07/11:36
 * @description: 描述
 */
public class AbstractDemo {

	public static void main(String[] args) {

		Person p = new Person() {
			/**
			 * 可以看到,我们直接将抽象类Person中的方法在大括号中实现了
			 * 这样便可以省略一个类的书写
			 *
			 * 并且,匿名内部类还能用于接口上
			 */
			public void eat() {
				System.out.println("eat something");
			}
		};
		p.eat();
	}
}

**运行结果:**eat something

可以看到,我们直接将抽象类Person中的方法在大括号中实现了

这样便可以省略一个类的书写

并且,匿名内部类还能用于接口上

实例3:在接口上使用匿名内部类

java 复制代码
package com.tfq.innerclass.anonymous.interfacedemo;

/**
 * @author: fqtang
 * @date: 2024/05/07/11:38
 * @description: 在接口上使用匿名内部类
 */
public interface Person {

	void eat();
}

package com.tfq.innerclass.anonymous.interfacedemo;

/**
 * @author: fqtang
 * @date: 2024/05/07/11:39
 * @description: 描述
 */
public class InterfaceDemo {

	public static void main(String[] args) {
		Person p = new Person() {
			/**
			 * 由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
			 *
			 * 最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口
			 */
			public void eat() {
				System.out.println("eat something");
			}
		};
		p.eat();
	}
}

**运行结果:**eat something

由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现

最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口。

实例4:Thread类的匿名内部类实现

java 复制代码
package com.tfq.innerclass.anonymous.basedemo;

/**
 * @author: fqtang
 * @date: 2024/05/07/11:44
 * @description: 描述
 */
public class RunnableDemo {

	public static void main(String[] args) {
		Runnable r = new Runnable() {
			public void run() {
				for(int i=0;i<2;i++){
					System.out.println(i);
				}
			}
		};
		Thread thread = new Thread(r);
		thread.start();
	}
}



package com.tfq.innerclass.anonymous.basedemo;

/**
 * @author: fqtang
 * @date: 2024/05/07/11:41
 * @description: 描述
 */
public class ThreadDemo {

	public static void main(String[] args) {
		Thread t = new Thread(){
			public void run(){
				for(int i=0;i<10;i++){
					System.out.println(i);
				}
			}
		};

		t.start();
	}
}

运行结果:01 2 3 4 5 6 7 8 9

在使用匿名内部类的过程中,我们需要注意如下几点:

**a、**使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

**b、**匿名内部类中是不能定义构造函数的。

c**、**匿名内部类中不能存在任何的静态成员变量和静态方法。

**d、**匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

e**、**匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

f、匿名内部类访问局部变量,jdk 8之前必须加final 关键字。

五、静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

java 复制代码
public class Test {
    public static void main(String[] args)  {
        Outter.Inner inner = new Outter.Inner();
    }
}
 
class Outter {
    public Outter() {
         
    }
     
    static class Inner {
        public Inner() {
             
        }
    }
}

六、内部类的使用场景和好处

总结一下主要有以下四点:

1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,

2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

3.方便编写事件驱动程序

4.方便编写线程代码

参考链接地址:

https://www.cnblogs.com/dolphin0520/p/3811445.html

https://www.cnblogs.com/nerxious/archive/2013/01/25/2876489.html

https://www.cnblogs.com/chenssy/p/3390871.html

java 详解匿名内部类与函数式接口_函数式接口和匿名内部类-CSDN博客

相关推荐
尚学教辅学习资料几秒前
基于SpringBoot的医药管理系统+LW示例参考
java·spring boot·后端·java毕业设计·医药管理
明月看潮生13 分钟前
青少年编程与数学 02-003 Go语言网络编程 15课题、Go语言URL编程
开发语言·网络·青少年编程·golang·编程与数学
雷神乐乐17 分钟前
File.separator与File.separatorChar的区别
java·路径分隔符
小刘|21 分钟前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法
南宫理的日知录24 分钟前
99、Python并发编程:多线程的问题、临界资源以及同步机制
开发语言·python·学习·编程学习
逊嘘40 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
Half-up43 分钟前
C语言心型代码解析
c语言·开发语言
morris1311 小时前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
Source.Liu1 小时前
【用Rust写CAD】第二章 第四节 函数
开发语言·rust
monkey_meng1 小时前
【Rust中的迭代器】
开发语言·后端·rust