Java에서는 일반적으로 동일한 패키지의 클래스에 대해 서로 다른 클래스를 만듭니다. 그러나 실제로는 또 다른 상황이 있습니다. 일부 클래스는 다른 클래스 내부에 정의될 수 있습니다. 클래스 내부에 정의된 클래스는 내부 클래스(InnerClass) 또는 중첩 클래스라고 하며, 외부에 정의된 클래스는 외부 클래스(OutClass) 또는 호스트 클래스라고 합니다. 즉, 멤버 변수와 메서드는 다른 클래스뿐만 아니라 클래스 내부에서도 정의할 수 있습니다. 내부 클래스를 정의하는 일반적인 형식은 다음과 같습니다.
class Outer {//外部类 class Inner {//内部类 //方法和属性 } }
위 코드에서 Outer는 일반적인 외부 클래스이고 Inner는 내부 클래스입니다. 일반 외부 클래스와의 가장 큰 차이점은 인스턴스 객체가 단독으로 존재할 수 없으며 외부 클래스의 인스턴스 객체에 연결되어야 한다는 것입니다.
내부 클래스는 잘 숨겨질 수 있습니다. 일반적으로 내부 클래스가 아닌 클래스는 개인 및 보호 권한을 가질 수 없지만 내부 클래스는 가능하며 내부 클래스도 외부 클래스의 모든 요소에 대한 액세스 권한을 갖습니다. 즉, 내부 클래스에 대한 많은 액세스 규칙은 변수와 메서드를 참조할 수 있습니다.
하지만 프로그램 구조를 더 간결하게 만들기 위해 내부 클래스를 사용하더라도 객체 지향 아이디어가 어느 정도 파괴된다는 점에 유의하세요.
내부 클래스의 존재에는 다음과 같은 장점이 있습니다.
내부 클래스는 다중 상속 솔루션을 더욱 완벽하게 만듭니다. 각 내부 클래스는 외부 클래스가 있는지 여부에 관계없이 독립적으로 인터페이스를 구현할 수 있습니다. 인터페이스가 구현되었거나 상위 클래스가 상속된 경우 내부 클래스에는 아무런 영향이 없습니다.
특정 논리적 관계를 가진 클래스를 편리하게 구성할 수 있을 뿐만 아니라 외부 세계에서 숨길 수도 있습니다.
멤버 내부 클래스는 다음과 같은 특징을 가집니다.
초기 jdk 버전에서는 최종 속성과 메서드를 모두 수정하지 않는 한 비정적 속성과 메서드만 멤버 내부 클래스에서 정의할 수 있습니다. static ;
外部类的实例与内部类实例是一对多的关系,即一个内部类实例只对应一个外部类实例,但一个外部类实例则可以对应多个内部类实例。
如果是在外部类中,创建成员内部类对象的基本语法格式如下:
内部类 对象名 = new 内部类();
如果是在外部的其他类中,或者是在外部类的静态方法中,创建成员内部类对象的基本语法格式如下:
内部类 对象名 = new 外部类().new 内部类();
4.1 定义成员内部类
/** * 成员内部类 */ public class OuterClass { // 外部类的非静态成员 String name = "一一哥"; private String hobby = "撸码"; static int age = 30; // 非静态方法 public void show() { //这里的this是指OuterClass对象 System.out.println("show方法...name="+this.name); //如果是在外部类里面创建内部类的对象,就不需要创建外部类实例,可以直接new 内部类() //InnerClass inner = new InnerClass(); } // 定义一个成员内部类 public class InnerClass { // 也可以定义私有属性 private int a = 10; //在早期的JDK中,成员内部类中不能定义静态变量;但在新版JDK中,成员内部类中可以定义静态变量 static int b = 20; // 非静态方法 public void m1() { // 这里的this对象是InnerClass内部类对象 System.out.println("成员内部类的成员变量:" + this.a); //外部类.this.属性或方法,这个this是外部类对象 System.out.println("外部类的成员变量:" + OuterClass.this.name); //内部类中可以访问外部类的私有成员和静态成员 System.out.println("外部类的私有成员变量:" + hobby); System.out.println("外部类的静态变量:" + age); } //在早期的JDK中,成员内部类中不能定义静态方法;但在新版JDK中,成员内部类中可以定义静态方法 public static void m2() { System.out.println("调用成员内部类的静态变量:" + b); System.out.println("调用外部类的静态变量:" + age); //在静态方法中创建内部类对象,也要通过内部类 对象名 = new 外部类().new 内部类();的格式 //InnerClass innerClass = new OuterClass().new InnerClass(); } } }
我们要注意,在早期的JDK中,成员内部类中不能定义静态属性和方法;但在新版JDK中,成员内部类中可以定义静态的属性和方法。并且我们要搞清楚在不同的位置上,创建内部类对象的方式,以及this的具体含义。
4.2 定义测试类
我们在外部的其他类中,要想创建出一个成员内部类的对象,需要通过如下形式:
内部类 对象名 = new 外部类().new 内部类();
public class InnerClassTest { public static void main(String[] args) { //在外部的其他类中,不能直接创建内部类对象,否则: //No enclosing instance of type OuterClass is accessible. //Must qualify the allocation with an enclosing instance of type OuterClass //(e.g. x.new A() where x is an instance of OuterClass). //InnerClass inner=new InnerClass(); //在外部的其他类中创建内部类对象,需要通过如下格式: //内部类 对象名 = new 外部类().new 内部类(); //InnerClass inner=new OuterClass().new InnerClass(); //也可以拆分成如下格式: OuterClass outer=new OuterClass(); InnerClass inner=outer.new InnerClass(); inner.m1(); InnerClass.m2(); } }
学习到这里,你可能会被内部类与外部类之间的调用访问关系整蒙圈,所以给大家梳理了一下访问方式:
成员内部类 访问 外部类的成员(属性、方法),可以【直接访问使用】;
外部类 访问 成员内部类,需要【直接创建内部类对象后再访问】,即 new InnerClass();
外部的其他类 访问 成员内部类,需要【创建外部类对象,再创建内部类对象后访问】,即 InnerClass inner=new OuterClass().new InnerClass();
在之前给大家讲过this的作用和用法,但在内部类中,关于this,我们需要注意以下两点:
如果同时存在外部类和内部类,那么this在哪个类中使用,this就代表哪个类的对象;
如果内部类想要通过this来调用外部类的属性和方法,需要使用外部类名.this.属性或者方法名。
局部内部类是指在方法中定义的内部类。
局部内部类具有如下特点:
局部内部类只能在方法中定义和创建对象,也只在当前方法中有效;
局部内部类中可以访问外部类的所有成员;
局部内部类与局部变量一样,不能使用访问控制修饰符(public、private和protected)和static修饰符;
在jdk 7版本中,如果局部变量是在局部内部类中使用,必须显式地加上final关键字;在jdk 8版本中,会默认添加final关键字;
局部内部类只能访问当前方法中final类型的参数与变量。如果方法中的成员与外部类的成员同名,可以使用 .this. 的形式访问外部类成员;
局部内部类中还可以包含内部类,但这些内部类也不能使用访问控制修饰符(public、private 和 protected) 和 static修饰符;
局部变量在方法执行结束后会被销毁,而局部内部类的对象会等到内存回收机制进行销毁。如果是局部内部类里的常量,该常量会被存放在常量池中。
创建局部内部类对象的基本语法格式如下:
public class PartClass { public void method() { //在方法中定义的内部类,就是局部内部类 class Inner { //属性 //方法 } } }
4.1 定义局部内部类
我们来定义一个局部内部类的案例代码。
/** * * 局部内部类---定义在方法中的内部类 */ public class PartOuterClass { //类的成员变量 String name="一一哥"; private int age=30; static String hobby="java"; public void show() { //局部变量 //JDK 7之前,匿名内部类和局部内部类中访问外部的局部变量时,该变量需要明确地带有final修饰符 //final int num = 10; //Effectively final特性 int num = 10; //局部内部类,类似于是方法中的局部对象 class PartInnerClass{ //内部可以正常定义方法 public void m1() { //访问外部类的非静态成员,可以使用OuterClass.this.成员的格式,也可以直接访问 //System.out.println("外部类的成员变量"+name); System.out.println("外部类的成员变量"+PartOuterClass.this.name); System.out.println("外部类私有的成员变量"+age); System.out.println("外部类的静态变量"+hobby); //局部内部类,可以直接访问方法中的局部变量 System.out.println("访问局部变量"+num); } //在新版的jdk中,也可以定义静态的属性和方法,老版的jdk则不行 static int b=10; public static void m2() { System.out.println("外部类的静态变量,hobby="+hobby+",b="+b); } } //创建局部内部类对象 PartInnerClass inner = new PartInnerClass(); inner.m1(); //在当前类中,局部内部类可以直接访问静态成员 PartInnerClass.m2(); } }
在JDK 7之前,匿名内部类和局部内部类中访问外部的局部变量时,该变量需要明确地带有final修饰符。但从JDK 8之后,我们可以不带final修饰符,而是由系统默认添加了。
4.2 定义测试类
接下来我们对上面的案例进行测试。
public class PartInnerClassTest { public static void main(String[] args) { //创建外部类对象,调用方法,执行局部内部类 PartOuterClass outer=new PartOuterClass(); outer.show(); } }
4.3 Effectively final特性
一般情况下,Java中的局部内部类和匿名内部类访问局部变量时,该变量必须由 final修饰,以保证内部类和外部类的数据一致性。但从 Java 8开始,我们可以不加 final修饰符,而是由系统默认添加,当然这在 Java 8以前是不允许的。Java将这个新的特性称为 Effectively(有效的、实际的) final 功能。
另外在 Lambda表达式中,使用局部变量时也要求该变量必须是 final 修饰的,所以 effectively final特性在 Lambda表达式的上下文中非常有用。
其实effectively final特性,只是让我们不用显式地把变量声明为final修饰的,它给我们自动添加了final修饰词,但并没有取消final,主要是减少了一点不必要的操作,给开发节省了点时间。
匿名内部类就是指没有类名的内部类,必须在创建时使用 new 语句来声明。匿名内部类不能在Outer Class外部类中定义,而是要在某个方法的内部,通过匿名类(Anonymous Class)的形式来定义。 匿名内部类本身就是一个对象。
通常情况下,如果一个方法的参数是接口类型,且该接口只需要实现一次,那么我们就可以通过匿名内部类的形式来进行定义。另外如果该接口的实现每次都不同,也可以使用匿名内部类的形式进行定义。我们也可以把这种定义形式叫做 “接口回调” 。匿名内部类的代码格式使得代码更加简洁、紧凑,模块化程度也更高。
匿名内部类具有如下特点:
匿名内部类本身就是一个对象;
一般在匿名内部类中不会定义属性和方法,因为没有意义;
匿名内部类的父类一般都是抽象类或者是接口;
匿名内部类和局部内部类一样,可以访问外部类的所有成员;
如果匿名内部类位于方法中,则该类只能访问方法中 final 类型的局部变量和参数;
匿名内部类中允许使用非静态代码块对成员进行初始化操作;
匿名内部类的非静态代码块会在父类的构造方法之后被执行。
通常匿名内部类有两种实现方式:
继承一个类,重写其方法;
实现一个或多个接口,并实现其方法。
创建匿名内部类对象的基本语法格式如下:
new 68a4a8383a2c08fd13f910298afd9572 (){
重写类或接口的方法
}
为了给大家演示匿名内部类的用法,接下来壹哥设计一个用于模拟按钮点击事件的案例。当我们进行安卓等设备开发时,面板上有个按钮,点击该按钮,如何监听点击事件?在Android系统中提供了各种对应的按钮点击监听事件。所以这里壹哥就通过实现接口的形式来定义匿名内部类,模拟一个单击事件。
4.1 定义接口
首先我们需要定义一个接口,表示单击监听,内部有个点击事件。
/** * 点击监听事件 */ public interface OnClickListener { //点击事件 void onClick(); }
4.2 定义Button按钮类
然后定义一个Button按钮类,给Button按钮安排一个点击监听方法。
/** * * 局部内部类---定义在方法中的内部类 */ public class Button { //处理案例点击的监听事件 public void setOnClickListener(OnClickListener listener) { listener.onClick(); } }
4.3 定义测试类
接下来我们就测试运行上面的代码。
/** * 匿名内部类测试 */ public class AnonyInnerClassTest { public static void main(String[] args) { //外部变量 int num=20; //测试匿名内部类 Button btn=new Button(); //模拟处理按钮的点击事件 btn.setOnClickListener(new OnClickListener() {//这里就是一个匿名内部类 //在匿名内部类中,可以允许使用非静态代码块进行成员初始化操作。 int i; { // 非静态代码块,在构造方法之后执行 i = 100; //成员初始化 } @Override public void onClick() { System.out.println("按钮被点击啦...i="+i+",num="+num); } }); } }
根据上面的案例可知:
在匿名内部类中,可以允许使用非静态代码块进行成员初始化操作;
匿名内部类的非静态代码块,会在构造方法之后执行;
匿名内部类也可以直接使用外部类的成员。
静态内部类和成员内部类的定义类似,但要使用static修饰,所以称为静态内部类(Static Nested Class)。
静态内部类和成员内部类有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this的方式调用。但它可以访问Outer类的private静态字段和静态方法,如果我们把静态内部类移到Outer类之外,就失去了访问private的权限。
静态内部类中可以定义非静态的属性和方法,也可以定义静态的属性和方法;
静态内部类中只能访问静态外部类的静态属性和方法。
创建静态内部类对象的基本语法格式如下:
内部类 对象名 = new 外部类.内部类();
4.1 定义静态内部类
这里我们先简单定义一个静态内部类,后面我们在学习内部类时再专门讲解。在这个静态内部类中,定义了一个方法,来访问外部类中的普通属性和静态属性。我们要记住以下几点:
静态内部类访问外部类的成员变量时,需要先创建外部类对象;
非静态内部类可以直接访问使用外部类的成员变量,如同使用本类中的变量;
所有的内部类访问外部类的静态变量时,可以直接通过"外部类.静态变量"的形式访问。
/** * 外部类和内部类 */ public class OuterClass { //普通属性,属于外部类 static int outerNum=10; //定义一个静态的内部类,如果不带static,就是一个普通的内部类。 //内部类的使用,和普通类一样,里面可以正常定义属性、方法、构造方法等。 //static前面可以带public等任意访问修饰符,也可以不带! static class InnerClass{ //私有属性无法在类的外部直接访问 //private int innerNum=20; int innerNum=20; public void printNum() { //定义外部类对象 OuterClass outer=new OuterClass(); //这里的this是指InnerClass内部类对象! System.out.println("innerNum="+this.innerNum+",outerAge="+outer.outerAge+",outerNum="+OuterClass.outerNum); } } }
对于静态内部类而言,static前面可以带public等任意访问修饰符,也可以不带!
4.2 定义测试类
我们再定义一个测试类,看看内部类对象是怎么调用的。
/** * 测试访问内部类 */ public class InnerClassTest { public static void main(String[] args) { //创建内部类对象,格式为“外部类.内部类 对象名 = new 外部类.内部类的构造方法” OuterClass.InnerClass inner = new OuterClass.InnerClass(); //调用内部类的方法 inner.printNum(); //访问外部类属性 System.out.println("outerNum="+OuterClass.outerNum); //访问内部类属性 System.out.println("innerNum="+inner.innerNum); } }
对于静态内部类的访问要求,给大家总结如下:
静态内部类中可以直接访问外部类的所有静态方法,包含私有的,但不能直接访问非静态成员;
静态内部类可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员;
如果静态内部类 访问 外部类 的静态属性、静态方法等,访问方式是【直接访问】;
如果外部类或外部的其他类来 访问 静态内部类,访问方式是【外部类.内部类 对象名 = new 外部类.内部类的构造方法】,创建出内部类对象后再访问。
위 내용은 Java 내부 클래스를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!