内部类介绍
内部类定义在外部类的局部位置(方法中/代码块)
(1) 局部内部类(有类名) (2) 匿名内部类(无类名)
内部类定义在外部类的成员位置 (和成员变量/方法同一位置)
(1) 成员内部类(无static修饰符) (2) 静态内部类(有static修饰符)
一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类
类的五大成员:属性、方法、构造器、代码块、内部类
基本语法
class Outer{ //外部类
class Inner{} //内部类
}
class Other{} //外部其他类
类的五大成员
package com.lian.pojo.f.demo16;
//外部其他类
public class InnerClass01 {
public static void main(String[] args) {
}
}
//外部类
class Outer {
//属性
private int n1 = 100;
//构造器
public Outer(int n1) {
this.n1 = n1;
}
//方法
public void m1(){
System.out.println("m1()");
}
//代码块
{
System.out.println("code piece");
}
//内部类
class Inner{
}
}
一、局部内部类
说明:局部内部类是定义在外部类的局部位置,比如方法或代码块中,并且有类名
1、可以直接访问外部类的包含私有访问权限的所有成员
2、不能添加访问修饰符,因为他的地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final修饰
3、作用域:仅仅在定义它的方法或代码块中
4、局部内部类访问外部类的成员,直接可访问
5、外部类访问局部内部类的成员,必须在作用域内(方法或代码块),创建对象再访问
6、外部其他类不能访问局部内部类,因为局部内部类的地位和局部变量一样
7、如果外部类和局部内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问
System.out.println("外部类的n= "+ 外部类名.this.成员)
package com.lian.pojo.f.demo16;
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
System.out.println("outer的hashcode= " + outer);
}
}
//外部类
class Outer {
private int n1 = 100;
//私有方法
private void m2() {
System.out.println("Outer m2()");
}
public void m1() { //方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用final 修饰,final代表不可以被其他类继承
//4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner { //局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的,类似于外部类的灵魂,共享身体所有权利
private int n1 = 800;
public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
// 使用 外部类名.this.成员)去访问
// 解读 Outer.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer.this就是哪个对象
System.out.println("n1= " + n1 + " 外部类的n1= " + Outer.this.n1);
System.out.println("Outer.this hashcode=" + Outer.this);
m2(); //内部类可直接调用外部类的属性和方法
}
}
//6. 外部类在方法中,可以创建Inner对象,然后调用方法即可
Inner inner = new Inner();
inner.f1();
}
}
打印结果
n1=800 外部类的n1=100
Outer.this hashcode=com.lian.pojo.f.demo16.Outer@7cef4e59
Outer m2()
outer的hashcode= com.lian.pojo.f.demo16.Outer@7cef4e59
二、匿名内部类
1、语法
new 类/接口/抽象类(参数列表){
类体
}
2、匿名内部类既是一个类的定义,同时本身也是一个对象
Person p = new Person(); //对象
Person p = new Person(){}; //匿名内部类
3、可以访问外部类包括私有访问权限的成员
4、不能添加访问修饰符,因为它的地位和局部变量一样
5、作用域:仅仅再定义它的方法或代码块中
6、匿名内部类访问外部类成员,可直接访问
7、外部其他类不能访问匿名内部类
8、如果外部类和局部内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问
案例1
package com.lian.pojo.f.demo16;
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
}
}
class Outer { //外部类
private int n1 = 10;//属性
public void method() {//方法
//基于接口的匿名内部类
//解读
//1.需求: 想使用IA接口,并创建对象
//2.传统方式,是写一个类,实现该接口,并创建对象
//3.需求是 Tiger/Dog 类只是使用一次,后面再不使用
//4. 可以使用匿名内部类来简化开发
//5. tiger的编译类型 ? IA
//6. tiger的运行类型 ? 就是匿名内部类 Outer$1,系统自动分配
/*
我们看底层 会分配 类名 Outer$1
class Outer$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
//7. jdk底层在创建匿名内部类 Outer$1,立即马上就创建了 Outer$1实例,并且把地址返回给 tiger
//8. 匿名内部类使用一次,就不能再使用
/**
* 接口、类、抽象类 都可以作为 匿名内部类
* 传统方法:新建子类继承 接口、抽象类 重新父类的方法,缺点 增加了多余的鸡肋的类
* 匿名内部类方法: 直接语法就是 new class/interface/abstractClass(){}
* 匿名内部类 系统其实内部给隐式的创建了 子类继承 class/interface/abstractClass
* 例如 父类编译类型是 IA,子类运行类型是 外部类Outer$n
*/
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger的运行类型=" + tiger.getClass()); //子类是系统隐式创建 Outer$1
tiger.cry();
tiger.cry();
tiger.cry();
//传统编码方式
// IA tiger = new Tiger();
// tiger.cry();
//演示基于类的匿名内部类
//分析
//1. father编译类型 Father
//2. father运行类型 Outer$2
//3. 底层会创建匿名内部类
/*
class Outer$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
}
*/
//4. 同时也直接返回了 匿名内部类 Outer$2的对象
//5. 注意("jack") 参数列表会传递给 构造器
Father father = new Father("jack"){
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father对象的运行类型=" + father.getClass());//系统隐式创建继承父类的子类名 Outer$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
package com.lian.pojo.f.demo16;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer outer = new Outer();
outer.f1();
//外部其他类---不能访问----->匿名内部类,因为匿名内部类的作用域只局限于外部类的局部方法或代码块中
System.out.println("main outer hashcode=" + outer);
}
}
class Outer {
private int n1 = 99;
public void f1() {
//创建一个基于类的匿名内部类
//不能添加访问修饰符,因为它的地位就是一个局部变量
//作用域 : 仅仅在定义它的方法或代码块中
Person p = new Person(){
private int n1 = 88;
@Override
public void hi() {
//可以直接访问外部类的所有成员,包含私有的
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
System.out.println("匿名内部类重写了 hi方法 n1=" + n1 + " 外部内的n1=" + Outer.this.n1 );
//inner n1= 88 outer n1= 99
System.out.println("inner n1= " + n1 + " outer n1= " + Outer.this.n1 );
//Outer0.this 就是调用 f1的 对象
System.out.println("Outer.this hashcode=" + Outer.this);
}
};
p.hi();//动态绑定, 运行类型是 Outer0$1
//也可以直接调用, 匿名内部类本身也是返回对象
// class 匿名内部类 extends Person {}
// new Person(){
// @Override
// public void hi() {
// System.out.println("匿名内部类重写了 hi方法,哈哈...");
// }
// @Override
// public void ok(String str) {
// super.ok(str);
// }
// }.ok("jack");
}
}
class Person {//类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
//抽象类/接口 和以上类案例同理...
匿名内部类做参数使用
package com.lian.pojo.f.demo16;
public class InnerClassExercise {
public static void main(String[] args) {
//方法2: 接口直接当做实参传递,简洁高效
f(new A() {
@Override
public void show() {
System.out.println("interface : this is a good picture");
}
});
//方法1:传统方法,类实现接口,类当作参数传递
f(new Picture());
}
//静态方法,形参是接口类型
public static void f(A a) {
// a.show(); = new A().show();
a.show();
}
}
//接口
interface A {
void show();
}
//类->实现IL => 编程领域 (硬编码)
class Picture implements A {
@Override
public void show() {
System.out.println("class : this is a good picture");
}
}
练习
需求:
1、有一个铃声接口Bell,里面有个ring方法
2、有一个手机类CellPhone,具有闹钟功能 alarmClock,参数是Bell类型
3、测试手机类的闹钟功能,通过匿名内部类对象作为参数
4、再传入另一个匿名内部类对象
package com.lian.pojo.f.demo16;
public class InnerClassExercise {
public static void main(String[] args) {
/*
1.有一个铃声接口Bell,里面有个ring方法
2.有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型
3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
*/
CellPhone cellPhone = new CellPhone();
//解读
//1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise$1
//2. 重写了 ring
// 编译类型:父类Bell , 运行类型:系统隐式自动生成 子类 InnerClassExercise$1
// 可以理解为:Bell bell = new InnerClassExercise$1();
// 子类InnerClassExercise$1继承父类的ring()方法,遵循 动态绑定机制
// 动态绑定机制:bell可访问父类所有属性和方法(需遵守访问控制规则),可访问子类继承父类的方法
//3. Bell bell = new Bell() {
// @Override
// public void ring() {
// System.out.println("懒猪起床了");
// }
// }
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("lazy pig get up");
}
});
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("honey begin class");
}
});
}
}
interface Bell{ //接口
void ring();//方法
}
class CellPhone{ //类
public void alarmClock(Bell bell){ //形参是Bell接口类型
System.out.println(bell.getClass()); //运行类型,系统自动为接口隐式生成,名称:外部类$n
// new Bell().ring()
bell.ring();//动态绑定
}
}
三、成员内部类
1、成员内部类是定义在外部类的成员位置,并且没有static修饰
2、可以直接访问外部类包括私有访问权限的所有成员
class Outer{ //外部类
private int n = 10;
public String name = "dan";
class Inner{
public void say(){
System.out.println("outer n"+n+"outer name"+name);
}
}
3、作用域和外部类的其他成员一样是整个类
4、成员内部类访问外部类成员,直接访问
5、外部类访问成员内部类,创建对象再访问
6、外部其他类访问成员内部类
7、如果外部类和成员内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问
案例1
package com.lian.pojo.f.demo16;
public class MemberInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.t1();
//外部其他类,使用成员内部类的三种方式
//解读
// 第一种方式
// outer.new Inner(); 相当于把 new Inner()当做是outer成员
// 成员内部类的地位和成员变量、方法是一样的,类可直接调用,也有用修饰符的权利
Outer.Inner inner = outer.new Inner();
inner.say();
// 第二方式 在外部类中,编写一个方法,可以返回 Inner对象
Outer.Inner innerInstance = outer.getInnerInstance();
innerInstance.say();
}
}
class Outer { //外部类
private int n1 = 10;
public String name = "张三";
//方法,返回一个Inner实例
public Inner getInnerInstance(){
return new Inner();
}
private void hi() {
System.out.println("hi()方法...");
}
//1.注意: 成员内部类,是定义在外部内的成员位置上
//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
public class Inner {//成员内部类
private double sal = 99.8;
private int n1 = 66;
public void say() {
//可以直接访问外部类的所有成员,包含私有的
//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
//,可以通过 外部类名.this.属性 来访问外部类的成员
System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer.this.n1);
hi(); //成员内部类可以调用父类外部类的所有属性和方法,灵魂伴侣,拥有和母体一样的权利
}
}
//写方法
public void t1() {
//使用成员内部类
//创建成员内部类的对象,然后使用相关的方法
Inner inner = new Inner();
inner.say();
System.out.println(inner.sal);
}
}
案例2
public class InnerClassExercise {
public static void main(String[] args) {
}
}
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;
}
public static void main(String[] args) {
Test t = new Test();
Inner r = t.new Inner();//5
System.out.println(r.a);//5
}
}
四、静态内部类
1、静态内部类是定义在外部类的成员位置,并且有static修饰
2、可以直接访问外部类包括私有访问权限的所有成员,但不能直接访问外部类的非静态成员
3、作用域和外部类的其他成员一样是整个类
4、静态内部类访问外部类成员,直接访问所有静态成员
5、外部类访问静态内部类,创建对象再访问
6、外部其他类访问静态内部类
7、如果外部类和成员内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问
package com.lian.pojo.f.demo16;
public class StaticInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
//外部其他类 使用静态内部类
//方式1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer.Inner inner = new Outer.Inner();
inner.say();
//方式2
//编写一个方法,可以返回静态内部类的对象实例.
Outer.Inner inner1 = outer.getInner();
System.out.println("============");
inner1.say();
// Outer.Inner inner2 = Outer.getInner();
// System.out.println("************");
// inner2.say();
}
}
class Outer { //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner10就是静态内部类
//1. 放在外部类的成员位置
//2. 使用static 修饰
//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
//5. 作用域 :同其他的成员,为整个类体
static class Inner {
private static String name = "教育";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
System.out.println(name + " 外部类name= " + Outer.name);
cry(); //静态内部类可调用父类的所有属性和方法
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
Inner inner = new Inner();
inner.say();
}
public Inner getInner() {
return new Inner();
}
// public static Inner getInner() {
// return new Inner();
// }
}