一个类的内部又完整的嵌套了另外一个类的结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。就是我们类的第五大成员(属性、方法、构造器、代码块、内部类),内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间包含的关系
基本语法 :
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
package com.javase.innerclass;
public class InnerClass01 {//外部其他类
public static void main(String[] args) {
}
}
class Outer {//外部类
//属性
private int n1 = 10;
//构造器
public Outer(int n1) {
this.n1 = n1;
}
//代码块
{
System.out.println("代码块...");
}
//方法
public void hi() {}
//内部类
class Dog {}
}
局部内部类的使用细节
说明:局部内部类是定义在外部类的局部位置,比如方法或者代码块中,并且有类名
1、可以直接访问外部类的所有成员,包含私有的
class Outer01 {//外部类
private int n1 = 10;
private void h1() {//私有方法
System.out.println("外部类的h1()方法...");
}
public void m1() {//方法
//局部内部类是定义在外部类的局部位置,通常是在方法中
class Inner01 {
//可以直接访问外部类的所有成员,包括私有的
public void f1() {
System.out.println("n1=" + n1);
h1();
}
}
}
}
2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用 final 修饰,因为局部变量也可以使用 final
3、作用域仅仅在定义它的方法或者代码块中
4、局部内部类---访问--->外部类的成员,访问方式:直接访问
5、外部类---访问--->局部内部类的成员,访问方式:创建对象,再访问(必须在作用域内)
class Outer01 {//外部类
private int n1 = 10;
private void h1() {//私有方法
System.out.println("外部类的h1()方法...");
}
public void m1() {//方法
//局部内部类是定义在外部类的局部位置,通常是在方法中
//不能添加访问修饰符,但是可以用final修饰
//作用域仅仅在定义它的方法或者代码块中
class Inner01 {
//可以直接访问外部类的所有成员,包括私有的
public void f1() {
System.out.println("n1=" + n1);
h1();
}
}
//外部类在方法中,可以创建Inner01对象,然后调用方法
Inner01 inner01 = new Inner01();
inner01.f1();
}
}
6、外部其他类 ---不能访问--->局部内部类,因为局部内部类地位是一个局部变量
7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名 . this . 成员)去访问
class Outer01 {//外部类
private int n1 = 10;
private void h1() {//私有方法
System.out.println("外部类的h1()方法...");
}
public void m1() {//方法
class Inner01 {
//可以直接访问外部类的所有成员,包括私有的
private int n1 = 20;
public void f1() {
//如果有重名的成员,遵循就近访问
//访问外部类的成员,则可以使用(外部类名.this.成员)去访问
System.out.println("n1=" + n1 + " 外部类的n1=" + Outer01.this.n1);
h1();
}
}
}
}
小结:
1、局部内部类定义在方法或者代码块中
2、作用域在方法体或者代码块中
3、局部内部类本质仍然是一个类
匿名内部类的使用细节(重要!)
说明:匿名内部类是定义在外部类的局部位置,比如方法或者代码块中,并且没有类名。
本质是一个类,内部类,该类没有名字,同时还是一个对象
1、匿名内部类的基本语法
new 类或接口(参数列表) {
类体
};
演示基于接口的匿名内部类:
package com.javase.innerclass;
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.method();
}
}
class Outer02 {//外部类
private int n1 = 10;
public void method() {//方法
//基于接口的匿名内部类
//1.需求:想使用IA接口,并创建对象
//2.传统方法,是写一个类,实现该接口,并创建对象
//3.需求创建的类只使用一次,后面不再使用
//4.可以使用匿名内部类来简化开发
//5.tiger的编译类型 ---> IA
//6.tiger的运行类型 ---> 就是匿名内部类 Outer02$1
/*
看底层代码实现,系统会分配一个类名 Outer02$1
class Outer02$1 implements IA() {
@Override
public void cry() {
System.out.println("老虎叫...");
}
}
*/
//7.JDK底层在创建匿名内部类Outer02$1,马上就创建了Outer02$1实例对象,
// 并且把地址返回给tiger
//8.匿名内部类使用一次后就不能再使用
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫...");
}
};
System.out.println("tiger的运行类型=" + tiger.getClass());
tiger.cry();
}
}
interface IA {//接口
public void cry();
}
演示基于类的匿名内部类:
package com.javase.innerclass;
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.method();
}
}
class Outer02 {//外部类
private int n1 = 10;
public void method() {//方法
//演示基于类的匿名内部类
//1.father 编译类型 ---> Father
//2.father 运行类型 ---> Outer02$2
//3.底层会创建匿名内部类
/*
class Outer02$2 extends Father {
@Override
public void test() {
System.out.println("匿名内部类重写test方法");
}
}
*/
//4.同时也直接返回了 匿名内部类 Outer02$2的对象
Father father = new Father("张三") {
@Override
public void test() {
System.out.println("匿名内部类重写test方法");
}
};
System.out.println("father的运行类型-->" + father.getClass());
father.test();
//基于抽象类的匿名内部类
Animal animal = new Animal() {
@Override
void eat() {
System.out.println("小狗吃骨头...");
}
};
}
}
class Father {//普通类
public Father(String name) {
}
public void test() {
}
}
abstract class Animal {//抽象类
abstract void eat();
}
2、匿名内部类的语法比较奇怪,请大家注意,因为匿名内部类即是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
package com.javase.innerclass;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
new Outer03().f1();
}
}
class Outer03 {
private int n1 = 10;
public void f1() {
//基于类的匿名内部类
Person person = new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写 hi()方法...");
}
};
person.hi();//动态绑定 运行类型是Outer03$1
//也可以直接调用,匿名内部类本身也是返回对象
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写 hi()方法...直接调用");
}
}.hi();
}
}
class Person {//类
public void hi() {
System.out.println("Person hi()...");
}
}
3、可以直接访问外部类的所有成员,包含私有的
4、不能添加访问修饰符,因为它的地位就是一个局部变量
5、作用域:仅仅定义在它的方法或者代码块中
6、匿名内部类---访问--->外部类的成员,访问方式:直接访问
7、外部其他类 ---不能访问--->局部内部类,因为局部内部类地位是一个局部变量
8、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名 . this . 成员)去访问
匿名内部类的最佳实践:
1、当作实参直接传递,简洁高效
package com.javase.innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
//当作实参直接传递,简洁高效
f1(new IL() {
@Override
public void hi() {
System.out.println("简洁高效...");
}
});
//传统方法
f1(new Car());
}
//静态方法,形参是接口类型
public static void f1(IL il) {
il.hi();
}
}
//接口
interface IL {
void hi();
}
//类->实现IL 编程领域(硬编码)
class Car implements IL {
@Override
public void hi() {
System.out.println("传统方法...");
}
}
2、有一个铃声接口Bell,里面有个ring方法。有一个手机类Cellphone,具有脑子功能alarmclock,参数是Bell类型。测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了。再传入一个匿名内部类(对象),打印:小伙伴上课了
package com.javase.innerclass;
import com.javase.interface_.Phone;
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone phone = new CellPhone();
phone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了...");
}
});
phone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了...");
}
});
}
}
//接口
interface Bell{
void ring();
}
//类
class CellPhone {
public void alarmClock(Bell bell) {
bell.ring();
}
}
成员内部类的使用细节
说明:成员内部类是定义在外部类的成员位置,并且没有 static 修饰
1、可以直接访问外部类的所有成员,包含私有的
2、可以添加任意的访问修饰符(public protected 默认 private),因为它的地位是一个成员
3、作用域和外部类的其他成员一样,为整个类体,比如在外部类的成员方法中创建成员内部类对象,再调用方法
4、成员内部类---访问--->外部类的成员,访问方式:直接访问
5、外部类 ---访问--->内部类,访问方式:创建对象,再访问
6、外部其他类 ---访问--->成员内部类,访问方式:有二种方式
7、如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名 . this . 成员)去访问
package com.javase.innerclass;
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.h1();
//外部其他类,使用成员内部类的二种方式
//第一种方式
Outer04.Inner04 inner04 = outer04.new Inner04();
//第二种方式 在外部类中,编写一个方法,可以返回Inner04对象
Outer04.Inner04 inner041 = outer04.getInner08();
}
}
class Outer04 {//外部类
private int n1 = 10;
public String name = "张三";
public class Inner04 {//成员内部类
private int n1 = 66;
public void say() {
//可以直接访问外部类的所有成员,包含私有的
//如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则
//果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
System.out.println("n1=" + n1 + " 外部类的n1=" + Outer04.this.n1);
}
}
//写方法
public void h1() {
//创建成员内部类对象,再调用方法
new Inner04().say();
}
//可以返回Inner04对象实例
public Inner04 getInner08() {
return new Inner04();
}
}
静态内部类的使用细节
说明:成员内部类是定义在外部类的成员位置,并且使用 static 修饰
1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2、可以添加任意的访问修饰符(public protected 默认 private),因为它的地位是一个成员
3、作用域和外部类的其他成员一样,为整个类体
4、静态内部类---访问--->外部类(比如静态属性),访问方式:直接访问所有静态成员
5、外部类 ---访问--->静态内部类,访问方式:创建对象,再访问
6、外部其他类 ---访问--->静态内部类,访问方式:有二种方式
7、如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名 . 成员)去访问
package com.javase.innerclass;
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.m1();
//外部其他类访问静态内部类
//方式1 因为是静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer05.Inner05 inner05 = new Outer05.Inner05();
inner05.say();
//方式2 编写一个方法可以返回静态内部类对象实例
Outer05.Inner05 inner051 = new Outer05().getInner05();
inner051.say();
//静态方法返回静态内部类对象实例
Outer05.Inner05 inner052 = Outer05.getInner05_();
inner052.say();
}
}
class Outer05 {//外部类
private int n1 = 10;
private static int n2 = 20;
//放在外部类的成员位置,使用static修饰
//可以添加任意的访问修饰符,因为它的地位是一个成员
public static class Inner05 {//静态内部类
private static int n2 = 20;
public void say() {
//可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则
//如果想访问外部类的成员,则可以使用(外部类名 . 成员)去访问
System.out.println("n2=" + n2 + " 外部类n2=" + Outer05.n2);
}
}
public void m1() {
new Inner05().say();
}
public Inner05 getInner05() {
return new Inner05();
}
public static Inner05 getInner05_() {
return new Inner05();
}
}