SE高阶(19):内部类的使用方式、应用场景和注意点

本文详细介绍了Java中的内部类,包括成员内部类、静态内部类、匿名内部类和局部内部类的特点与应用场景。通过实例展示了如何使用这些内部类,并讨论了它们之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说起内部类,大多数人都知道但却不怎么用,常规使用中,最常用到的也就是匿名内部类,所以下面会理一理各种内部类的相关知识及用法。


 内部类的定义 

     Java中,类通常作为一个独立的程序单元。但在某些情况下,把将一个类定义在另一个类中,这就叫做内部类,而包含了内部类的类就叫做外部类。


 内部类的主要作用与注意点 

  1. 内部类隐藏在外部类中,别的类无法轻易访问,提供了更好的封装。
  2. 内部类属于外部类成员,可以直接访问外部类的成员属性和方法(包括私有数据),但外部类却不能直接访问内部类成员。
  3. 内部类可以使用private、protected来控制访问权限;使用static修饰。反之,外部类不能使用这些修饰符。
  4. 有时候某些方法只用一次,没必要单独创建类,这时就可以采用匿名内部类。
  5. 如果非静态内部类,是不能定义静态成员的,就好像在普通代码块中无法定义静态一样。
     上面只是简要说明,但内部类又分为成员内部类、静态内部类、匿名内部类和方法内部类(局部内部类)。各自的应用场景、使用方法和访问权限皆有不同之处,在下面,将会对四个内部类的要点进行分析,附带实例。


  成员内部类                                                                                            

public class Demo01 {
	private int a = 20; 
	public static int b = 20; //静态变量
	private void testInstance() {System.out.println("外部类实例方法");}
	private static void testStatic() {System.out.println("外部类静态方法");}
	//打印a b 值
	void printAttribute() {
		System.out.println("外部类:" + "a:" + a + " , b:" + b);
	}
	//成员内部类,权限默认,可被同类、同包访问
	class InnerClass{
		private int a = 10;	
//		public static void test() {}//error
		//访问外部类成员
		public void changeOutside() {
			a = a - 10;
			b = b - 10;
			//能访问外部类静态方法和实例
			testInstance();
			testStatic();
			System.out.println("内部类:" + "a:" + a + " , b:" + b);
		}
	}
	//返回一个内部类对象
		public InnerClass getInnerClass(){
			return new InnerClass();
		}
}
//测试
public class Test {
	public static void main(String[] args) {
		Demo01 d = new Demo01();
		//创建内部类对象
		Demo01.InnerClass ic = d.new InnerClass();
		ic.changeOutside();
		d.printAttribute();
	}
}
//输出结果:
外部类实例方法
外部类静态方法
内部类:a:0 , b:10
外部类:a:20 , b:10

成员内部类实例解析 

  • 从实例来看,创建内部类对象时需要依托于外部类对象的存在,因为成员内部类在类中等同于一个成员,所以需要外部类对象才能对其访问。
  • 在上面的changeOutside()方法中,因为成员内部类等同于成员,所以能访问所有外部类成员,包括静态。但是不能创建静态成员和方法。
  • 如果成员内部类和外部类的属性名相同,那么默认使用内部类的属性,如同上面a、b值一样。如果要访问外部类同名属性,用this来指定。
		//创建内部类对象
		Demo01.InnerClass ic = d.new InnerClass();
		System.out.println(ic.getClass());
		//输出结果:
		class com.InnerClass.test.Demo01$InnerClass
根据上面代码,可以看到内部类的class文件就是上面所打印的结构,从名字的命名也能看出内部类和外部类的关系。



  静态内部类                                                                                            

public class outClass {
	private int insVar = 555;
	private String a = "外部A"; //同名变量,用于测验
	private static int staticVar = 333;
		
	//静态内部类
	static class InnerClass{
		//能创建静态变量和方法
		private static String a = "内部A";
		public static void test() {
			//访问同名变量
			System.out.println("a: " + a);
			System.out.println("外部类 a: " + new outClass().a); //访问外部类同名实例变量,this不能使用
			//访问外部静态变量
			System.out.println("外部类 staticVar : " + staticVar);
		}
	}
}

//测试
public static void main(String[] args) {
		InnerClass inn = new InnerClass();//可以直接创建
		inn.test();
		//输出结果:
		a: 内部A
		外部类 a: 外部A
		外部类 staticVar : 333
	}

静态内部类实例解析 

  • 静态内部类能够直接创建对象,不像成员内部类依附于对象而存在,这说明静态内部类依附于类本身,而非对象。
  • 静态内部类不能有非静态的成员/方法,如果要引用外部类实例变量/方法,可以采用外部对象.变量/方法的形式来引用。
  • 遇到同名变量和方法时,不能像成员内部类一样用this来区分,依旧需要使用外部对象来调用。
  • 为什么不允许访问实例变量/方法呢?从类加载的过程来看,静态内部类依附于类,而使用实例变量/方法需要对象,所以直接引用外部实例会引发错误。


  匿名内部类                                                                                            

  • 匿名内部类就是没有名字的内部类,相当于简写的内部类。既然没名字,自然也就找不到,访问修饰符就无意义。
  • 创建匿名内部类时,会立即创建一个该类实例,然后该类就被销毁了。
  • 匿名内部类必须是继承一个抽象类或者实现接口,然后匿名内部类就可以进行重写。
  • 匿名内部类只能用一次,不能重复调用。
  • 创建匿名内部类和对象差不多,但实现内容是放在里面,可以理解成带内容的对象。
实例1
public class outClass {
	public static void main(String[] args) {
		//常规执行方法形式
		new Test().start();
	}	
	
}
//用一个线程来重复打印i
class Test extends Thread {
	@Override
	public void run() {
		for(int i = 0; i < 50; i++) {
			System.out.println(Thread.currentThread().getName() + " , i:" + i);
		}
	}
}
上面实例使用常规方式:定义类->定义方法->调用方法,但如果这方法只用一次,用完就不管了,那么去创建类来定义方法不仅显得很麻烦,而且还占空间,所以匿名内部类就是为了解决该类问题而用。改用匿名内部类如下:
//使用匿名内部类,实现同样的功能,但是代码量大大降低。
		new Thread() {
			@Override
			public void run() {
				for(int i = 0; i < 50; i++) {
					System.out.println(Thread.currentThread().getName() + " , i:" + i);
				}
			}
		}.start();

实例2
interface Command {
	void processArr(int[] arr);
}

public class outClass {
	public static void main(String[] args) {
		int[] arr  = {24,5,12,3,34};
		
		dealArr(arr, new addCommand());
		dealArr(arr, new getMaxCommand());
	}	
	
	//使用命令模式,根据相应命令来处理数组
	public static void dealArr(int[] arr, Command cmd) {
		cmd.processArr(arr);
	}
}
//累计数组之和
class addCommand implements Command {
	@Override
	public void processArr(int[] arr) {
		int sum = 0;
		for(int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		System.out.println("数组总和:" + sum);
	} 
}
//得到数组最大值
class getMaxCommand implements Command {
	@Override
	public void processArr(int[] arr) {
		int sum = arr[0];
		for(int i = 1; i < arr.length; i++) {
			if(arr[i] > sum) {
				sum = arr[i];
			}
		}
		System.out.println("数组最大值:" + sum);
	} 
}
实例2使用了简单的命令模式来处理数组,但从上面可以看出,代码量很多。因为只使用一次,所以改用匿名内部类,如下:
	//为了简化,所以省略一些不必要的代码,只保留关键代码
	dealArr(arr, new Command() {		
			@Override
			public void processArr(int[] arr) {
				//累计数组和的代码
			}
		});
		
		dealArr(arr, new Command() {
			@Override
			public void processArr(int[] arr) {
				//得到数组最大值的代码		
			}
		});
同上面比较,减少了创建两个类的代码量,代码显得简洁多了。但也有缺点,那就是方法过多时就没必要使用匿名内部类了。就像使用匿名内部类最频繁的绑定事件,方法最多2、3个,也是这个道理。



  局部内部类                                                                                            

       方法内部类也叫作局部内部类,可以定义在方法和作用域中,例如普通代码块、for循环、while循环中,局部内部类的访问仅限于方法内或者该作用域内,这意味着不能被public、protected、private和static修饰。
由于方法内部类很少使用,所以实在没什么好例子,下面就以一个动态代理的例子来演示一下:
interface USB{
	void connect(); //连接
}
//实现类
class Kingson implements USB{
	@Override
	public void connect() {
		System.out.println("金士顿USB开始连接");
	}
	
}
class methodClass {
	private Object target;
	//对指定对象进行代理,并返回代理对象
	public Object getProxy(Object target){
		this.target = target;
		Object proObj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),							   
							   new InvocationHandler() { //看起来像是在方法中使用匿名内部类....		
									@Override
									public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
										System.out.println("打开电脑USB接口");
										method.invoke(target, args);
										System.out.println("记录USB接口连接信息");
										return null;
									}
							  });
		return proObj; //返回生成的代理对象
	}
}
//测试类
public class Test {
	public static void main(String[] args) {
		Kingson ks = new Kingson();
		USB u = (USB)new methodClass().getProxy(ks);
		u.connect();
		//输出结果:
		打开电脑USB接口
		金士顿USB开始连接
		记录USB接口连接信息
	}
}
上面这个实例估计看不出局部内部类有啥特点...我也没办法,因为局部内部类实在是基本用不到,上面这个勉强体现了把类放在方法中,尽管使用的匿名内部类。

  关于内部类的个人感想  

        从上面对四种内部类使用来看,内部类本质上就是对类的一种简化,所以内部类能做的事,直接创建类也一定能做。所以无需纠结什么地方该用内部类,什么地方不用,再极端无外乎就是多创建一个类,如果不够就两个。当然,能简便就简便,但没必要过于追求极端化,最终都是为了解决问题,所以怎么顺手怎么来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值