> 文章列表 > 四种内部类的详解

四种内部类的详解

四种内部类的详解

目录

基本介绍

部类的特点

内部类的基本语法

内部类的四种分类

定义在外部类局部位置上(比如方法内):

(1)局部内部类

局部内部类的使用

(2)匿名内部类(没有类名,重点!!!!

匿名内部类的细节

匿名内部类习题

第一题

第二题

定义在外部类的成员位置上:

1)成员内部类(没用static修饰)

成员内部类的使用

2)静态内部类(使用static修饰)

静态内部类的使用 

内部类的习题


基本介绍

一个类的内部又完整的嵌套了另一个类结构被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员

类的五大成员是哪些?

分别是 属性 方法 构造器 代码块 内部类

内部类的特点

内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系 外部

内部类的基本语法

class Outerf { //外部类

        class Innert { //内部类

        }

}

class Othert{//外部其他类

}

代码演示:

package idea.chapter10.innerclass;/*** 演示内部类的基本使用*/
public class InnerClass01 {public static void main(String[] args) {/*一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员类的五大成员是哪些分别是 属性 方法 构造器 代码块 内部类*/}
}class outer {//外部类//私有属性private int n1 = 100;//属性//构造器public outer(int n1) {//构造器this.n1 = n1;}//方法public void m1() {//方法System.out.println("m1()");}{//代码块System.out.println("代码块");}//内部类在outer类的内部class Inner {public void hi() {//内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,方法和属性都可以使用System.out.println(n1);m1();}}
}

内部类的四种分类

内部类的分类有四种分别是,局部内部类,匿名内部类,成员内部类,静态内部类。其中匿名内部类是最重要也是最难的。

接下我们先来看局部内部类

定义在外部类局部位置上(比如方法内):

(1)局部内部类

局部内部类的使用

说明:

1.局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。

2.可以直接访问外部类的所有成员,包含私有的

3.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final

4.作用域:仅仅在定义它的方法或代码块中。

5.局部内部类---访问---->外部类的成员[访问方式:直接访问]

6.外部类---访问---->局部内部类的成员 ;

访问方式:创建对象,再访问(注意:必须在作用域内)

记住:

(1)局部内部类定义在方法中/代码块

(2)作用域在方法体或者代码块中

(3)本质仍然是一个类

7.外部其他类---不能访问----->局部内部类(因为 局部内部类地位是一个局部变量)

8.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

System.out.println("外部类的n2=”+外部类名.this.n2);

代码演示:

package idea.chapter10.innerclass;/*** 演示局部内部类的使用*/
public class LocalInnerClass {//外部其他类public static void main(String[] args) {/*结论局部内部类的使用说明:1.局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。2.可以直接访问外部类的所有成员,包含私有的3.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final4.作用域:仅仅在定义它的方法或代码块中。5.局部内部类---访问---->外部类的成员[访问方式:直接访问]6.外部类---访问---->局部内部类的成员 ;访问方式:创建对象,再访问(注意:必须在作用域内)记住:(1)局部内部类定义在方法中/代码块(2)作用域在方法体或者代码块中(3)本质仍然是一个类7.外部其他类---不能访问----->局部内部类(因为 局部内部类地位是一个局部变量)8.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问System.out.println("外部类的n2=”+外部类名.this.n2);*///7.外部其他类---不能访问----->局部内部类(因为 局部内部类地位是一个局部变量)//我们的LocalInnerClass就是一个外部其他类,外部其他类来访问局部内部类,是不能访问的outer02 outer02 = new outer02();outer02.m1();}
}class outer02 {//私有属性private int n1 = 100;private int a = 0;//私有方法private void m2() {System.out.println("Outer02 m2()");}public void m1() {//1.局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。//  这就是一个局部内部类,定义在方法中,也可以定义在代码块中 类名是Inner02,定义在方法中//3.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final//注意不可以使用访问修饰符去修饰局部类,只能使用final 注意使用final修饰后的类还是可以被实例化的,如果是abstract那么就是一个抽象类就不能被实例化不要搞混了//4.作用域:仅仅在定义它的方法或代码块中。final class Inner02 {//局部内部类private int n1 = 800;public void hi() {//2.局部内部类可以直接访问外部类的所有成员,包含私有的//直接访问外部类的私有属性和私有方法System.out.println(n1);m2();//5.局部内部类---访问---->外部类的成员[访问方式:直接访问]System.out.println(a);//8.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问//此时我们的Inner02类(也就是局部内部类)中有一个私有的n1属性,在外部类中也有一个n1s属性,因为默认遵守就近原则,所以当我们直接去输出n1的时候,访问到的是内部类中的值//如果想访问外部类中的值,使用外部类名.this.属性名去访问//看演示System.out.println(outer02.this.n1);//这里输出的就是外部类的n1System.out.println(n1);//因为默认遵守就近原则,所以访问到的就是内部类中的n1System.out.println(Inner02.this.n1);//这样访问的也是内部类中的n1}}//6.外部类---访问---->局部内部类的成员 ;//访问方式:创建对象,再访问(注意:必须在作用域内) 因为他的作用域是在定义他的方法或者代码块中//记住://(1)局部内部类定义在方法中/代码块//(2)作用域在方法体或者代码块中//(3)本质仍然是一个类//注意此时这段代码的范围是在m1()方法中的,外部类也可以直接访问内部类的私有属性Inner02 inner02 = new Inner02();System.out.println(inner02.n1);}}

(2)匿名内部类(没有类名,重点!!!!

匿名内部类的使用(重要!!!!!!!) 

(1)本质是类

(2)内部类

(3)该类没有名字

(4)同时还是一个对象

说明:匿名内部类是定义在外部类 的局部位置,比如方法中,并且没有类名

1.匿名内部类的基本语法

new 类或接口(参数列表){

        类体

};

代码演示:

package idea.chapter10.innerclass;/*** 演示匿名内部类的使用*/
public class AnonymousInnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();outer04.method();}
}class Outer04 { //外部类private int n1 = 10;//属性public void method() {//方法//基于接口的匿名内部类//解读//1.现在我们有一需求就是需求: 想使用IA接口,并创建对象//2.传统方式,是写一个类,实现该接口,然后创建对象//3.需求是 Tiger/Dog 类只是使用一次,后面再不使用//4. 可以使用匿名内部类来简化开发//5. tiger的编译类型 -> IA//6. tiger的运行类型 -> 就是匿名内部类  Outer04$1/*在底层 会分配 类名 Outer04$1  (就是类名+$1)class Outer04$1 implements IA {@Overridepublic void cry() {System.out.println("老虎叫唤...");}}*///7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给 tiger//8. 匿名内部类使用一次,就不能再使用IA tiger = new IA() {@Overridepublic void cry() {System.out.println("老虎在叫");}};//getClass就是获取到对象的运行类型System.out.println("tiger的运行类型=" + tiger.getClass());//因为匿名内部类只会创建一次对象使用完后对象就销毁了,但是创建出来的对象实例,可以继续使用tiger.cry();tiger.cry();//        IA tiger = new Tiger();
//        tiger.cry();//演示基于类的匿名内部类//分析//1. father编译类型 Father//2. father运行类型 Outer04$2//3. 底层会创建匿名内部类/*class Outer04$2 extends Father{@Overridepublic void test() {System.out.println("匿名内部类重写了test方法");}}*///4. 同时也直接返回了 匿名内部类 Outer04$2的对象//5. 注意("jack") 参数列表会传递给 构造器Father father = new Father("jack") {@Overridepublic void test() {System.out.println("匿名内部类重写了test方法");}};System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2father.test();//基于抽象类的匿名内部类Animal animal = new Animal() {@Overridevoid eat() {System.out.println("狗吃骨头");}};animal.eat();}
}interface IA {//接口public void cry();
}
//class Tiger implements IA {
//
//    @Override
//    public void cry() {
//        System.out.println("老虎再叫");
//    }
//}
//class Dog implements  IA{
//    @Override
//    public void cry() {
//        System.out.println("小狗再叫");
//    }
//}class Father {//类public Father(String name) {//构造器System.out.println("接收到name=" + name);}public void test() {//方法}
}abstract class Animal { //抽象类abstract void eat();
}

匿名内部类的细节

2.匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。

3.可以直接访问外部类的所有成员,包含私有的

4.不能添加访问修饰符,因为它的地位就是一个局部变量。

5.作用域:仅仅在定义它的方法或代码块中。

6.匿名内部类---访问---->外部类成员[访问方式:直接访问]

7.外部其他类---不能访问----->匿名内部类(因为 匿名内部类地位是一个局部变量)

8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

代码演示:

package idea.chapter10.innerclass;/*** 演示匿名内部类的注意事项和使用细节*/
public class AnonymousInnerClassDetail {public static void main(String[] args) {/*2.匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。3.可以直接访问外部类的所有成员,包含私有的[案例演示]4.不能添加访问修饰符,因为它的地位就是一个局部变量。[过]5.作用域:仅仅在定义它的方法或代码块中。[过]6.匿名内部类---访问---->外部类成员[访问方式:直接访问]7.外部其他类---不能访问----->匿名内部类(因为 匿名内部类地位是一个局部变量)8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问*/Outer05 outer05 = new Outer05();outer05.m1();//7.外部其他类---不能访问----->匿名内部类(因为 匿名内部类地位是一个局部变量)}
}class Outer05 {private int a = 10;public void m1() {//创建一个基于类的匿名内部类//此时p的 编译类型 -> Person//       运行类型 -> Outer05&1//4.不能添加访问修饰符,因为它的地位就是一个局部变量//5.作用域:仅仅在定义它的方法或代码块中Person p = new Person() {private int a = 20;@Overridepublic void hi() {//3.可以直接访问外部类的所有成员,包含私有的//6.匿名内部类---访问---->外部类成员[访问方式:直接访问]//直接访问外部类的私有属性System.out.println(a);System.out.println("匿名内部类重写了hi方法");//8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问System.out.println(a);//因为默认遵守就近原则,所以访问到的是匿名内部类中的a//如何访问外部类中的属性System.out.println(Outer05.this.a);//这样访问到的就是外部类中的a}};p.hi();//会有动态绑定 , 因为运行类型是Outer05$1 所以会找到Outer05$1中的方法//如果没有重写hi方法//此时p1的 编译类型 -> Person//        运行类型 -> PersonPerson p1 = new Person() {};p1.hi();//此时调用的方法就是Person中的hi方法因为,p1的运行类型是Person//也可以直接调用,因为匿名内部类本身返回的就是一个对象new Person() {@Overridepublic void hi() {System.out.println("匿名内部类又一次的重写了hi方法");}}.hi();//可以直接调用/*可以这样理解new Person(){@Overridepublic void hi() {System.out.println("匿名内部类又一次的重写了hi方法");}}上面这一大块,返回的就是一个对象,在使用对象.方法,不就是我们传统的调用方法的形式吗,对象名.方法名*/}
}class Person {public void hi() {System.out.println("Person中的hi方法");}
}

匿名内部类习题

第一题

代码演示:

package idea.chapter10.innerclass;public class InnerClassExercise01 {public static void main(String[] args) {//当做实参直接传递,简洁高效f(new AA() {@Overridepublic void show() {System.out.println("AA接口中的show方法被调用");}});//传统方法/*因为我们的f方法的形参是AA 因为我们的Picture实现了AA接口,所以可以吧Picture传入到方法中,因为接口可以指向实现了该接口的对象实例*/f(new Picture());}//静态方法,形参是接口类型public static void f(AA a) {a.show();}
}interface AA {void show();
}//类->实现AA => 编程领域 (硬编码)
class Picture implements AA {@Overridepublic void show() {System.out.println("这是一副画");}
}

第二题

代码演示:

package idea.chapter10.innerclass;/*** 匿名内部类习题*/
public class InnerClassExercise02 {public static void main(String[] args) {/*1.有一个铃声接口Bell,里面有个ring方法。2.有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了4.再传入另一个匿名内部类(对象),打印:小伙伴上课了*/Cellphone cellphone = new Cellphone();cellphone.alarmClock(new Bell() {@Overridepublic void ring() {System.out.println("懒猪起床了");}});cellphone.alarmClock(new Bell() {@Overridepublic void ring() {System.out.println("小伙伴上课了");}});}
}interface Bell {void ring();
}class Cellphone {public void alarmClock(Bell bell) {//因为我们不同的匿名内部类传入进来,运行类型也会不同,会根据运行类型去找到,对应的方法去执行bell.ring();//会有动态绑定}
}

定义在外部类的成员位置上:

1)成员内部类(没用static修饰)

成员内部类的使用

说明:

1.成员内部类是定义在外部类的成员位置,并且没有static修饰。

2.可以直接访问外部类的所有成员,包含私有的

3.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。

4.作用域和外部类的其他成员一样,为整个类体 比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法.

5.成员内部类---访问---->外部类成员(比如:属性)访问方式:直接访问

6.外部类---访问------>成员内部类(说明)访问方式:创建对象,再访问

7.外部其他类---访问---->成员内部类

8.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如 果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

代码演示:

package idea.chapter10.innerclass;/*** 演示成员内部类的使用*/
public class MemberInnerClass01 {public static void main(String[] args) {outer08 outer08 = new outer08();/*说明:1.成员内部类是定义在外部类的成员位置,并且没有static修饰。2.可以直接访问外部类的所有成员,包含私有的3.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。4.作用域和外部类的其他成员一样,为整个类体 比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法.5.成员内部类---访问---->外部类成员(比如:属性)访问方式:直接访问6.外部类---访问------>成员内部类(说明)访问方式:创建对象,再访问7.外部其他类---访问---->成员内部类8.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如 果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问*///7.外部其他类---访问---->成员内部类// 第一种方式// outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员// 这就是一个语法,不要特别的纠结.outer08.Inner08 inner081 = outer08.new Inner08();inner081.m1();// 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象outer08.Inner08 inner082 = outer08.getInner08();inner082.m1();}
}class outer08 {//外部类//私有属性private int n1 = 10;public String name = "jack";//私有方法private void hi() {System.out.println("hello,word");}//1.成员内部类是定义在外部类的成员位置,并且没有static修饰。 此时我们的内部类就是定义在属性的位置上的//3.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。public class Inner08 {private int n1 = 66;public void m1() {//2.可以直接访问外部类的所有成员,包含私有的//直接访问外部类的私有属性和私有方法System.out.println(n1);hi();//5.成员内部类---访问---->外部类成员(比如:属性)访问方式:直接访问//访问外部类的私有属性System.out.println(name);//访问外部类的私有方法hi();//8.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如 果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问System.out.println(outer08.this.n1);//访问的是外部类的n1System.out.println(n1);//因为遵守就近原则,所以访问的是,Inner08中的n1System.out.println(Inner08.this.n1);//访问的也是Inner08中的n1}}//编写一个方法,返回一个Inner08的对象public Inner08 getInner08() {return new Inner08();}//4.作用域和外部类的其他成员一样,为整个类体 比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法.//6.外部类---访问------>成员内部类(说明)访问方式:创建对象,再访问public void ok() {//外部类使用内部类成员,先创建对象,然后使用相关属性或者方法Inner08 inner08 = new Inner08();inner08.m1();System.out.println(inner08.n1);//可以直接访问成员内部类的私有属性}
}

2)静态内部类(使用static修饰)

静态内部类的使用 

说明:

1.静态内部类是定义在外部类的成员位置,并且有static修饰

2.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员

3.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。

4.作用域:同其他的成员,为整个类体

5.静态内部类---访问---->外部类(比如:静态属性)[访问方式:直接访问所有静 态成员]

6.外部类---访问------>静态内部类 访问方式:创建对象,再访问

7.外部其他类---访问----->静态内部类

8.如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近 原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问。

代码演示:

package idea.chapter10.innerclass;/*** 演示静态内部类的使用*/
public class StaticInnerClass01 {public static void main(String[] args) {outer10 outer = new outer10();/*1.静态内部类是定义在外部类的成员位置,并且有static修饰2.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员3.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。4.作用域:同其他的成员,为整个类体5.静态内部类---访问---->外部类(比如:静态属性)[访问方式:直接访问所有静 态成员]6.外部类---访问------>静态内部类 访问方式:创建对象,再访问7.外部其他类---访问----->静态内部类8.如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近 原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问*///7.外部其他类---访问----->静态内部类//方式1//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)outer10.Inner10 inner10 = new outer10.Inner10();inner10.hi();//方式2//编写一个方法,可以返回静态内部类的对象实例.outer10.Inner10 inner101 = outer.getInner10();inner101.hi();}
}class outer10 {//私有属性private int n1 = 10;//私有的静态属性private static String name = "jack";//私有的静态方法private static void cry() {}//1.静态内部类是定义在外部类的成员位置,并且有static修饰//Inner10就是静态内部类//定义在类的局部位置上//并且是static修饰的//3.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。//4.作用域:同其他的成员,为整个类体static public class Inner10 {private static String name = "tom";//2.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员public void hi() {//直接使用外部类的静态私有属性System.out.println(name);//直接使用外部类的静态私有方法cry();//不能直接使用外部类的非静态属性,会报错//System.out.println(n1);//5.静态内部类---访问---->外部类(比如:静态属性)[访问方式:直接访问所有静 态成员]//访问外部类的静态私有属性System.out.println(name);//访问外部类的静态私有方法cry();//8.如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近 原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问System.out.println(name);//因为遵守的是就近原则,所以访问的是内部类中的nameSystem.out.println(outer10.name);//访问的就是外部类中的name属性System.out.println(Inner10.name);//访问的就是内部类中的name属性}}//编写方法返回Inner10对象public Inner10 getInner10() {return new Inner10();}public void m1() {//6.外部类---访问------>静态内部类 访问方式:创建对象,再访问Inner10 inner10 = new Inner10();System.out.println(inner10.name);inner10.hi();}
}

内部类的习题

思路分析:

1.在执行这句话的时候Test t = new Test(); 会导致类的加载,因此会去执行Test的构造器,在构造器中创建了一个Inner的对象实例s1 然后将s1.a改成了10紧接着就创建了一个Inner的对象实例s2,此时直接输出了s2.a,注意输出的值是5并不是10 导致构造器的部分就结束了
2.接下来就执行了这样一句话,  Test.Inner r = t.new Inner();  这句话的意思就是创建了一个内部类的对象,然后直接输出,因为a属性是public的,因此可以输出,
3.最后输出的结果就是两个5
package idea.chapter10.innerclass;/*** 静态内部类习题*/
public class InnerClassExercise {public static void main(String[] args) {Test t = new Test();Test.Inner r = t.new Inner();//5System.out.println(r.a);//5}
}
/*
思路分析:
1.在执行这句话的时候Test t = new Test(); 会导致类的加载,因此会去执行Test的构造器,在构造器中创建了一个Inner的对象实例s1 然后将s1.a改成了10紧接着就创建了一个Inner的对象实例s2,此时直接输出了s2.a,注意输出的值是5并不是10 导致构造器的部分就结束了
2.接下来就执行了这样一句话,  Test.Inner r = t.new Inner();  这句话的意思就是创建了一个内部类的对象,然后直接输出,因为a属性是public的,因此可以输出,
3.最后输出的结果就是两个5*/
class Test {//外部类public Test() {//构造器Inner s1 = new Inner();s1.a = 10;Inner s2 = new Inner();System.out.println(s2.a);}class Inner { //内部类,成员内部类public int a = 5;}
}