项目代码
https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter10/src/com/yinhai/innerclass_
一、基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
类的五大成员是哪些?
属性 方法 构造器 代码块 内部类
基本语法
class Outer{
class Inner{
}
}
class Other{}
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("代码块");
}
class Inner{//内部类,在Outer类的内部
}
}
二、内部类的分类
1.定义在外部类局部位置上(比如方法内)
1)局部内部类(有类名)
2)匿名内部类(没有类名,重点!!!!)
2.定义在外部类的成员位置上:
1)成员内部类(没用static修饰)
2)静态内部类(使用static修饰)
三、局部内部类细节
说明:局部内部类是定义在外部类的局部位置,比如方法中或者代码块,并且有类名。
0.本质仍然是一个类
1.可以直接访问外部类的所有成员,包含私有的
2.不能添加访问修饰符
因为它的地位就是一个局部变量。 局部变量是不能使用修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
3.作用域
仅仅在定义它的方法或代码块中。
4.局部内部类-访向--->外部类的成员 访问方式:直接访问
5.外部类-----局部内部类的成员访问方式创建对象,再访问(注意:必须在作用域内)
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02{
private int n1 = 100;
private void m2(){
System.out.println("m2");
}
public void m1(){
final class Inner02{//局部内部类 本质仍然是一个类
//2.不能添加访问修饰符,因为它的地位就是一个局部变量。
//局部变量是不能使用修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
//可以直接访问外部类的所有成员
public void f1(){
System.out.println("n1=" + n1);
m2();
}
}
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
6.外部其他类--不能访问 --->局部内部类(因为局部内部类地位是一个局部变量)
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员) 去访问(很像形参的传递)
这里的Outer02.this表示的是调用n1的对象,谁调用谁就是Outer02.this,但是如果只写this表示的是当前类的对象 代表的是inner02,用hashcode进行验证
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02{
private int n1 = 100;
private void m2(){
System.out.println("m2");
}
public void m1(){
final class Inner02{
private int n1 = 800;
//局部内部类 本质仍然是一个类
//2.不能添加访问修饰符,因为它的地位就是一个局部变量。
//局部变量是不能使用修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
//可以直接访问外部类的所有成员
public void f1(){
System.out.println("n1=" + n1);
System.out.println("Outer n1=" + Outer.this.n1);
m2();
}
}
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
四、匿名内部类!!!
(1)本质是类 (2)内部类 (3)该类没有名字(其实也有 系统分配 但我们看不见也无法调用)(4)同时它还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
1.匿名内部类的基本语法
new 类或接口(参数列表){
类体
};
匿名接口 匿名类 匿名抽象类
/**
* 演示匿名内部类的使用
*/
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 类只是使用一次,后面再不使用
// IA tiger = new Tiger();
// tiger.cry();//4. 可以使用匿名内部类来简化开发
//7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给 tiger
IA tiger = new IA() {//创建了IA的实例 就使用了一次
//5. tiger的编译类型是IA
//6. tiger的运行类型就是匿名内部类 Outer04$1
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger的运行类型=" + tiger.getClass());//类名 getClass就是获取运行类型
tiger.cry();
tiger.cry();
tiger.cry();//8. 匿名内部类使用一次,就不能再使用 但是对象可以再调用,已经把地址指向了一个空间
/*运行类型
class Outer04$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
//演示基于类的匿名内部类
//1. father编译类型 Father 2. father运行类型是Outer04$2,不带大括号就是Father
Father father = new Father("jack"){//匿名内部类 5. 注意("jack") 参数列表会传递给 构造器
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2
father.test();
//3. 底层会创建匿名内部类
/*
class Outer04$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
}
*/
//4. 同时也直接返回了 匿名内部类 Outer04$2的对象
//基于抽象类的匿名内部类
//抽象类必须实现抽象类的方法
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.匿名内部类的语法比较奇特,因为匿名内部类既是个类的定义,同时它本身也是一个对象,
因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
}
}
class Outer05{
private int n1 = 99;
public void f1(){
//床加一个给予类的匿名内部类
Person p = new Person(){
public void hi(){
System.out.println("匿名内部类重写了hi方法");
}
};//注意要带分号
p.hi();//动态绑定,运行类型是Outer$5
//也可以直接调用
new Person(){
public void hi(){
System.out.println("匿名内部类重写了hi方法,hhhhhh");
}
}.hi();//其实更简单来说 new后到.hi()前的一长串其实就是一个运行类型 相当于new Person();然后这个Person符合匿名的规则
//于是乎只在底层创建,不符合就是调用显式的Person,本质是多态
}
}
class Person{
public void hi(){
System.out.println("Person hi");
}
}
3.可以直接访问外部类的所有成员, 包含私有的
4. 不能添加访问修饰符
因为他的地位就是一个局部变量
5.作用域
仅仅在定义它的方法或代码块中。
6.匿名内部类--访向--->外部类成员(直接访问)
7.外部其他类--不能访向---->匿名内部类(因为匿名内部类地位是一一个局部变量)
同局部内部类
8.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想
访问外部类的成员,则可以使用(外部类名.this.成员) 去访问
同局部内部类
五、匿名内部类的最佳实践
1.
当做实参直接传递,简洁高效
public class InnerClassExercise01 {
public static void main(String[] args) {
f1(new IL() {
@Override
public void show() {
System.out.println("这是一幅名画~~~");
}
});
f1(new Picture());//传统方法
}
public static void f1(IL il){
il.show();
}
}
interface IL{
void show();
}
class Picture implements IL{// 类 -- 接口对象 硬编程
@Override
public void show() {
System.out.println("这是一幅名画....");
}
}
2.
1.有一个铃声接口Bell,里面有个ring方法
2.有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型
3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
public class InnerClassExercise02 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
//1.传递的是匿名内部类 传给了alarmClock
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();//动态绑定,使用的ring是对象的类内的ring
}
}
六、成员内部类
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。
1.可以直接访问外部类的所有成员,包含私有的
2.可以添加任意访问修饰符
(public、protected、 默认、private),因为它的地位就是一个成员。
public class MemberInnerClass01 {
public static void main(String[] args) {
Ourter08 ourter08 = new Ourter08();
ourter08.t1();
}
}
class Ourter08{
private int n1 = 10;
public String name = "张三";
//成员内部类是定义在外部内的成员位置上
//可以添加任意访问修饰符(public 默认 protected private)
class Inner08{
public void say(){
//可以访问外部类的所有成员,包含私有的
System.out.println("Outer01的n1" + n1 + "Outer01的name" + name);
}
}
//写一个方法
public void t1(){
Inner08 inner08 = new Inner08();
inner08.say();
}
}
3.作用域
和外部类的其他成员一一样, 为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法.
4.成员内部类---访问---- >外部类成员
(比如:属性) [访问方式: 直接访问]
5.外部类-- -访问----->成员内部类
访问方式: 创建对象, 再访问
6.外部其他类-- -访问---->成员内部类
有三种方式
1)直接调用
在外部的成员可以直接调用
2)在外部类中,编写一个方法,可以返回Inner08对象
7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员) 去访问
同上的局部内部类和匿名内部类
七、静态内部类
静态内部类的使用
说明:静态内部类是定义在外部类的成员位置,并且有static修饰
1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员。
3.作用域
同其他的成员,为整个类体
4.静态内部类--访向---- >外部类(比如:静态属性) [访问方式:直接访问所有静态成员]
5.外部类--访-----静态内部类访问方式:创建对象,再访问
6.外部其他类-----> 静态内部类
两种方式
1. 因为是静态内部类,可以通过类名直接访问(前提是满足访问权限)
2.编写一个方法,返回一个静态实例对象
7.如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员) 去访问(注意不加this)
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类 使用静态内部类
//方式1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式2
//编写一个方法,可以返回静态内部类的对象实例.
Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
Outer10.Inner10 inner10_ = Outer10.getInner10_();
System.out.println("************");
inner10_.say();
}
}
class Outer10 { //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner10就是静态内部类
//1. 放在外部类的成员位置
//2. 使用static 修饰
//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
//5. 作用域 :同其他的成员,为整个类体
static class Inner10 {
private static String name = "韩顺平教育";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
System.out.println(name + " 外部类name= " + Outer10.name);
cry();
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10() {
return new Inner10();
}
public static Inner10 getInner10_() {
return new Inner10();
}
}
小结
1)内部类有四种,局部内部类,匿名内部类,成员内部类,静态内部类
2)重点还是掌握匿名内部类使用
new 类/接口(参数){
//
};
3)局部内部类是放在代码块或者方法内
成员内部类、静态内部类是放在外部类的成员位置,本质就是一个成员
八、内部类课堂练习
输出两个5
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
}
}