目录
基本介绍
内部类的特点
内部类的基本语法
内部类的四种分类
定义在外部类局部位置上(比如方法内):
(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.局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
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修饰,因为局部变量也可以使用final
4.作用域:仅仅在定义它的方法或代码块中。
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);//这里输出的就是外部类的n1
System.out.println(n1);//因为默认遵守就近原则,所以访问到的就是内部类中的n1
System.out.println(Inner02.this.n1);//这样访问的也是内部类中的n1
}
}
//6.外部类---访问---->局部内部类的成员 ;
//访问方式:创建对象,再访问(注意:必须在作用域内) 因为他的作用域是在定义他的方法或者代码块中
//记住:
//(1)局部内部类定义在方法中/代码块
//(2)作用域在方法体或者代码块中
//(3)本质仍然是一个类
//注意此时这段代码的范围是在m1()方法中的,外部类也可以直接访问内部类的私有属性
Inner02 inner02 = new Inner02();
System.out.println(inner02.n1);
}
}
匿名内部类的使用(重要!!!!!!!)
(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 {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
//7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给 tiger
//8. 匿名内部类使用一次,就不能再使用
IA tiger = new IA() {
@Override
public 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{
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
}
*/
//4. 同时也直接返回了 匿名内部类 Outer04$2的对象
//5. 注意("jack") 参数列表会传递给 构造器
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2
father.test();
//基于抽象类的匿名内部类
Animal animal = new Animal() {
@Override
void 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;
@Override
public 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
// 运行类型 -> Person
Person p1 = new Person() {
};
p1.hi();//此时调用的方法就是Person中的hi方法因为,p1的运行类型是Person
//也可以直接调用,因为匿名内部类本身返回的就是一个对象
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类又一次的重写了hi方法");
}
}.hi();//可以直接调用
/*
可以这样理解
new Person(){
@Override
public 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() {
@Override
public 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 {
@Override
public 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() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
cellphone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell {
void ring();
}
class Cellphone {
public void alarmClock(Bell bell) {
//因为我们不同的匿名内部类传入进来,运行类型也会不同,会根据运行类型去找到,对应的方法去执行
bell.ring();//会有动态绑定
}
}
说明:
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);//访问的是外部类的n1
System.out.println(n1);//因为遵守就近原则,所以访问的是,Inner08中的n1
System.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);//可以直接访问成员内部类的私有属性
}
}
说明:
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);//因为遵守的是就近原则,所以访问的是内部类中的name
System.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();//5
System.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;
}
}