【JAVA学习笔记】42 - 内部类(难点,重点)

news2024/12/26 0:41:51

项目代码

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
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1128983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【蓝桥每日一题]-动态规划 (保姆级教程 篇12)#照相排列

这次是动态规划最后一期了,感谢大家一直以来的观看,以后就进入新的篇章了 目录 题目:照相排列 思路: 题目:照相排列 思路: 首先记录状态f[a][b][c][d][e]表示每排如此人数下对应的方案数,然…

java中按行读取文件内容

java中按行来读取文件内容,一般对文件也是又要求的,比如文件编码utf-8,内容是按行可读,而不是一堆字节码。这类文件,我们按行读取,主要是方便快速查看内容,并且用这些内容来作别的用途&#xff…

FileUpload控件上传文件时出现 不支持给定路径的格式.的解决方法

正常代码,部署到server 2012时,在上传音频mp3文件时,显示错误“不支持给定路径的格式”,上传控件使用FileUpload控件: 因为程序之前是正常的,因此应该不是程序的问题。 上传时,发现在选择文件时…

F28335-可移植新建工程模板-基于bitfield

文章目录 前言步骤新建工程工程管理拷贝底层文件 添加测试函数编写main.c测试函数 前言 实验要求利用28335芯片,重新学习一下DSP28335,并做个记录。 值得一提的是,28335只能用寄存器开发,而不能用库函数开发,相应的也…

LDAP注入漏洞

1、LDAP 注入 LDAP (Light Directory Access Portocol) 是基于X.500标准的轻量级目录访问协议,提供访问目录数据库方法的服务和协议,常用于与目录数据库组成目录服务。其中目录是一个为查询、浏览和搜索而优化的专业分布式数据库,它呈树状结…

Java学习 1.初识Java

1.类名 当这个类被public修饰时 class后的名字 类名必须和文件名相同 public class 类名(文件名); 2.main函数 方法一定是包含在类之中的 3.类里面是函数/方法 由返回值、方法名、参数列表组合而成,方法中可以定义变量,方法体 4.一个文件中可以有多个类 我们建…

【管理运筹学】第 10 章 | 排队论(3,标准的 M/M/1 排队系统)

文章目录 引言一、模型特征及分析二、系统指标1. 在系统中的平均顾客数(队长的期望)2. 在队列中的平均顾客数(队列长的期望)3. 在系统中顾客平均逗留时间4. 在队列中顾客的平均等待时间 写在最后 引言 前两篇文章,分别…

李沐——论文阅读——VIT(VIsionTransformer)

一、终极结论: 如果在足够多的数据上面去做预训练,那么,我们也可以不用 卷积神经网络,而是直接用 自然语言处理那边搬过来的 Transformer,也能够把视觉问题解决的很好 (tips:paperswithcode.co…

【刷题-PTA】堆栈模拟队列(代码+动态图解)

【刷题-PTA】堆栈模拟队列(代码动态图解) 文章目录 【刷题-PTA】堆栈模拟队列(代码动态图解)题目输入格式:输出格式:输入样例:输出样例: 分析题目区分两栈解题思路伪代码动图演示代码测试 题目 题目描述 : 设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q。 …

新华三路由器+华为交换机,实现华为交换机指定端口访问外网

需求背景: 多台服务器使用华为交换机组建了局域网,需要让交换机的指定端口可以访问外网。 需求分析: 交换机组建的局域网是二层组网,需借助路由器接入外网,然后通过DHCP分配内网IP地址给交换机指定端口连接的设备。 …

[资源推荐] 关于计算机毕设的方法论(重庆大学吕昱峰)

第一次刷到这个up主的视频是之前搜cpu设计的时候 视频链接:https://www.bilibili.com/video/BV1j7411P7gt?p1&vd_source0e8431ba6fd78bb2215c36307a75ac1a 最近学校毕设要开题了,但是感觉对毕业设计这个东西还是比较模糊,应该做到什么…

QML自定义电池状进度条

效果: 百分比显示保留两位小数,通过iValue的数值来显示当前进度,注意为了保留小数总值取的是10000,所以你的iValue值也要乘上100 变量说明: cBorderColor:进度条外框的颜色 cContentColor:表示进度的小方块颜色 cTextColor:显示进度百分比的文字颜色 iValue:当前进度,为整数(…

低代码软件的价格考量:成本效益与投资回报

数字化转型的今天,我们常听到“低代码”这个概念,那低代码软件价格到底如何呢?很多厂商并没有公布软件价格情况,让很多企业在采购的时候也是一头雾水。当然,市场上也存在一些厂商公开透明价格,比如Zoho Cre…

皮卡丘靶场——暴力破解

暴力破解 1. 基于表单的暴力破解 在登陆界面随便使用账号密码进行登录,使用bp抓包发送Intruder 我们需要破解账号(username)和密码(password),就应当选择ClusterBomb(集束炸弹)的攻击…

Java基础篇 | Java8流式编程

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Java从入门到精通 ✨特色专栏&#xf…

pytorch_lightning:Validation sanity check: 0%| | 0/2 [00:00<?, ?it/s]

在使用Lighting架构辅助训练时,对于出现的下述情况的原因: 解释: 注意到“ Validation sanity check ”。这是因为Lightning在开始训练之前进行了两批验证。这是一种单元测试,以确保如果你在验证循环中有一个bug,你不…

微信小程序开发之投票管理及小程序UI的使用

目录 一、小程序UI 1.讲述 2. 介绍vantWeapp 3. 使用vantWeapp 安装 构建 依赖 引用 二、后端 1. 后端实体对象 2. 后端接口 3. 实现类 4. 请求处理类 三、前端 1. 定义路径 2. 页面引用 3. 页面 4. 页面美化 5. 数据 6. 效果展示 一、小程序UI 1.讲述 小…

IMU预积分的过程详解

一、IMU和相机数据融合保证位姿的有效性: 当运动过快时,相机会出现运动模糊,或者两帧之间重叠区域太少以至于无法进行特征匹配,所以纯视觉SLAM对快速的运动很敏感。而有了IMU,即使在相机数据无效的那段时间内&#xff…

python网络爬虫实例

目录 1、访问百度 2、输入单词百度翻译 3、豆瓣电影排行榜 4、豆瓣电影top250 5、下载美女壁纸 1、访问百度 from urllib.request import urlopen url"http://www.baidu.com" respurlopen(url)with open("mybaidu.html",mode"w") as f:f.wr…

Java面试(JVM篇)——JVM 面试题合集 深入理解JVM虚拟机

关于什么是JVM? 作用: 运⾏并管理Java 源码⽂件所⽣成的Class⽂件,在不同的操作系统上安装不同的JVM ,从⽽实现了跨平台的保证。 ⼀般情况下,对于开发者⽽⾔,即使不熟悉JVM 的运⾏机制并不影响业务代码的…