Java-内部类

本文详细解析了Java中的内部类,包括概念、分类(成员内部类、局部内部类、静态内部类和匿名内部类),以及它们的目的、好处和特性。重点讨论了内部类如何解决多重继承问题,并通过实例展示了不同类型的内部类如何在实际编程中使用。

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

内部类

一、相关概念

1、概念

内部类是指在一个外部类的内部再定义一个类

内部类作为外部类的一个成员,并且依附于外部类而存在的

内部类可为静态,可用protected和private修饰。而外部类不可以,外部类只能使用public和default修饰

2、分类
  • 成员内部类
  • 局部内部类
  • 静态内部类
  • 匿名内部类
3、目的、好处

使用内部类可以使程序更加的简洁,便于命名规范和划分层次结构

使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

public interface Father {

}

public interface Mother {

}

public class Son implements Father, Mother {

}

public class Daughter implements Father{

    class Mother_ implements Mother{
        
    }
}

其实对于以上实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能使用内部类才能实现多重继承了。

在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

4、特性

使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):

1)内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。

2)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

3)创建内部类对象的时刻并不依赖于外围类对象的创建。

4)内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

5)内部类提供了更好的封装,除了该外围类,其他类都不能访问。

abstract class People {
  abstract void run() ;
}
interface Machine {
  void run() ;
}
//错误:此时run()不可直接实现
class Roboot extends People implements Machine {
  
}
//注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。
//用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。

二、内部类分类 - 实现

1、成员内部类

与成员变量、成员方法类似,作为某个类的成员定义的类,叫成员内部类

// 定义外部类 - 外部类修饰符只能为:public 或 default(缺省- 默认不写)
class Outer {
	//1.外部类成员
  	protected int age;
  
  	//2.成员内部类
  	// 修饰符可以为:与类成员一样,都可以使用四个修改符
  	[访问修饰符] class 内部类名 {
    	//3.内部类成员
  	}
  
  	[访问修饰符] 数据类型 方法名([参数列表]) {
    	内部类名 对象 = new 内部类名() ;
    	对象.成员...
  	}
}

说明

1)作为外部类的一个成员存在,与外部类的属性、方法并列

2)在内部类中,访问内部类的成员:this.成员

3)在内部类中,访问外部类的成员:外部类名.this.成员 或 直接访问成员

4)成员内部类中,不可以有静态成员(为什么?与成员方法中,不允许访问静态成员一样,成员内部类中不能存在任何static的变量和方法)

5)成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。语法如下:

// 方法二:
// 外部类 外部类对象 = new 外部类() ;
OuterClass oc = new OuterClass();

// 外部类名称.内部类名称 内部类对象 = 外部类对象.new 内部类名称() ;
oc.InnerClass ic = oc.new InnerClass();


// 方法二:
// 外部类名称.内部类名称 内部类对象 = new 外部类名称().new 内部类名称() ;
OuterClass.InnerClass ic = new OuterClass().new InnerClass();

注意:内部类的修饰符不能为私有的!!

优点:

1)内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类的成员声明为private,但是对于处于其内部的内部类还是可见的。)

2)用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后出现Outer.class和Outer$Inner.class两类。

/**
	内部类只能在内部使用
	第一:私有化内部类
	第二:在外部类中,编写公开方法,创建并使用私有的内部类
	第三:创建外部类对象,调用公开方法
*/
public class Outer {
	private int count = 100 ;

	//1、private修改内部类,则此类只能在类内部使用
	private class Inner {
		public void display() {
			System.out.println("count="+count);
		}
	}
	
	//2、在外部类中,编写公开方法,创建并使用私有的内部类
	public void test() {
		Inner inner = new Inner() ;
		inner.display();
	}
}

public class MainTest {

	public static void main(String[] args) {
		//3、创建外部类对象,通过调用公开方法来使用私有内部类
		Outer outer = new Outer() ;
		outer.test();
	}

}

内部类对象保存了一个对外部类对象的引用

例子:
/**
	内部类被外部使用
	第一:在外部类中,非私有化内部类
	第二:创建外部类对象
		外部类 外部类对象 = new 外部类() ;
	第三:创建内部类对象
		外部类名称.内部类名称 内部类对象 = 外部类对象.new 内部类名称() ;
*/

//外部类
public class OuterClass {
	private String name;
	private int age;
	private char sex ;

	//省略setter/getter方法
	//私有方法
	private void menu() {
		System.out.println("===用户信息===");
	}

	// 成员内部类:内部类作为外部类的成员,可以访问外部类的私有成员或属性
  /**
  	语法一:
  		外部名名称.this.属性 [=值];
  		外部名名称.this.方法([实参列表]); 
  		
  	语法二:
  		属性 [=值] ;
  		方法([实参列表]); 
  		
  	注意:语法二中,如果内部类定义了与外部内同名的成员,则默认调用内部类的成员。
  */
	public class InnerClass {
		//定义与外部类相同名称的属性
		int age = 20 ;
    
    //成员类中,不允许存在静态成员
		//static int staticA ;
		//public static void staticMethod() {}
		
		//内部类构造方法:访问外部内的私有成员
		public InnerClass() {
			OuterClass.this.name = "zing";
			//注意:直接通过属性访问,则默认访问内部类的age属性。
			//如果需要访问外部的属性必须通过外部类名.this.属性访问
			//age = 18 ;
			OuterClass.this.age = 18;
			sex = '女';
		}

		//内部类成员方法:访问外部类的成员方法
		public void display() {
			OuterClass.this.menu();
			System.out.println("姓名:" + getName());
			System.out.println("性别:" + getSex());
			System.out.println("年龄:" + getAge());
		}
	}
}


//测试类--主方法
public class MainTest {
  	public static void main(String[] args) {
      OuterClass outerClass = new OuterClass();
      //语法:外部类名称.内部类名称 内部类对象 = 外部类对象.new 内部类名称() ;
      OuterClass.InnerClass innerClass = outerClass.new InnerClass();
      innerClass.display();
    }
}

2、局部内部类

与局部变量类似,在方法内定义的类称为局部内部类;

语法:
//定义外部类
class Outer {
	//1.外部类成员
  
  //2.成员方法
  [访问修饰符] 数据类型 方法名([参数列表]) {
    //3.局部内部类 - 与局部变量一样,只能作用于当前方法- 不能使用修饰符!!
    class 内部类名 {
    	//4.内部类成员
  	}
    
    // 5.创建局部内部类对象:局部内部类名 对象 = new 局部内部类名() ;
    // 6.调用属性或方法
  
  }
}

说明

1)在方法中定义的内部类称为局部内部类;

2)与局部变量类似,在局部内部类前不允许加访问修饰符;外部类可以使用public 或 (default)修饰;成员内部类:可以使用public、protected、(default)、private;

3)局部内部类不仅可以访问外部类实例变量,还可以访问外部类的局部变量,但外部类的局部变量必须声明为final,JDK1.8+可以省略。原因:

  • new出来的对象存放在堆内存中;
  • 局部变量跟着方法走,存储在栈内存中;
  • 方法运行结束后,立即出栈,局部变量随之消失;
  • 而局部类new出来的对象在堆内存中持续存在,走到垃圾回收消失

4)在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。

例子:
public class Outer {
	//外部类成员变量
	int count = 100 ;
	
	public void test() {
		//注意:JDK1.8中,会自动进行检测,当局部内部类访问局部变量时,会隐式在该局部变量加final关键字,声明为常量
		int local1 = 200 ;
		final int local2 = 300 ;
		//local = 300 ;	//在此,再次给local变量赋值,会报错,常量不允许重新赋值
		//局部内部类
		class Inner {
			void display() {
				//访问外部类的成员变量
				System.out.println(count);
				//访问方法的final局部变量,JDK1.8会自动声明为final
				System.out.println(local1);
				System.out.println(local2);
			}
		}
		//实例化局部内部类对象,并调用方法
		Inner inner = new Inner() ;
		inner.display();
	}
}


public class MainTest {
	public static void main(String[] args) {
		Outer outer = new Outer() ;
		outer.test();
	}
}

3、匿名内部类

匿名内部类是一种 特殊 的局部内部类,它是通过匿名类实现接口。

语法:
new 外部类().方法(new 接口|抽象类|父类(){
  接口方法的实现();
  或
  重写抽象类或父类的方法() ;
}) ;


接口名称 对象名 = new 接口名称() {
  	// 注意:匿名内部类不能定义静态变量,可以定义成员变量
  
 	重写抽象方法; 
}

//具体步骤看以下例子

要点:

匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。

IA被定义为接口。IA I=new IA(){};

注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。因其为局部内部类,那么局部内部类的所有限制都对其生效。

匿名内部类是唯一一种无构造方法类。匿名内部类在编译的时候由系统自动起名Out$1.class。

如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。

因匿名内部类无构造方法,所以其使用范围非常的有限。

例子:
//第一:定义一个接口、抽象类或父类
public interface IA {
	 abstract void fun1();
}


//第二:定义外部类
public class Outer {
	
	public void test1() {
		//局部内部类
		class Inner implements IA {
			@Override
			public void fun1() {
				System.out.println("匿名类实现fun1--方式一");
			}
		}
		
    //第四:调用父类对象方法,传递接口实现对象或父类的子类对象(重写或实现方法)
		new Outer().callInner(new Inner());
	}
	
	//匿名内部类
	public void test2() {
		new Outer().callInner(new IA() {
			@Override
			public void fun1() {
				System.out.println("匿名类实现fun1--方式二");
			}
		});
	}
	
	//第三:定义一个方法,参数为父类对象引用或接口对象
	public void callInner(IA a) {
		a.fun1();
	}
}


public class MainTest {
	public static void main(String[] args) {
		Outer outer = new Outer() ;
		outer.test1();
		outer.test2();
	}
}

总结:接口的使用有以下两种用法,分别是:

  • 传统用法 - 必须预告定义好相关的实体类
// 接口用法一:传统的用法,需要定义实现类
// 接口 对象 = new 实现类();
  • 匿名内部类实现 - 直接 new 接口的同时,使用匿名内部类实现接口,只能使用一个,用完就没有了
    • 接口使用的位置有以下三种情况:
// 接口用法二:匿名内部类实现
// 特点:一般只用一次,用完就丢
接口 对象 = new 接口(){
  // 实现接口的方法
  1.成员变量
    
  2.成员方法
    
  // 注意:匿名内部类不允许定义静态的成员,但是可以定义常量  
  3.接口抽象方法的实现;
}

// 接口用法三:接口作为方法参数的使用
xxx.方法名(new 接口名称(){
  
};

// 接口用法四:接口作为方法返回值的使用
访问修饰符 接口名称 方法名称(){
  new return 接口名称(){
    // 实例接口的方法
  }
}

4、静态内部类

与静态变量、静态方法类似,作为某个类的静态成员定义的类,叫成员内部类

语法:
  • 调用(类名.静态内部类 && 对象.静态内部类)
//定义外部类
class Outer {
	//1.外部类成员
  
  //2.成员内部类
  [访问修饰符] static class 内部类名 {
    //3.内部类成员
  }
}

要点:

静态内部类定义在类中,任何方法外,用static定义

静态内部类只能访问外部类的静态成员

生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别

静态内部类的对象可以直接生成:Outer.Inner in=new Outer.Inner();而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。

静态内部类不可用private来进行定义。

例子:
//外部类
public class Outer {
	int count1 = 100 ;
	static int count2 = 200 ;
	
	
	void test() {
		Inner inner = new Inner() ;
		inner.display();
	}
	
  //静态内部类
	public static class Inner {
		void display() {
			//错误:静态内部类不允许访问外部类的非静态成员
			//System.out.println(count1);
			System.out.println(count2);
		}
	}
}


public class MainTest {
	public static void main(String[] args) {
    //内部类只能在内部使用(内部类声明为private)
		Outer outer = new Outer() ;
		outer.test() ;
		
    //内部类被外部使用,语法为:
    //外部类.内部类 内部类对象 = new 外部类.内部类() ;
		Outer.Inner inner = new Outer.Inner();
		inner.display();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WyuanY.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值