首页 > 其他分享 >内部类 --- (寄生的哲学)

内部类 --- (寄生的哲学)

时间:2025-01-05 19:01:57浏览次数:3  
标签:部类 内部 静态 匿名 --- 寄生 哲学 public name

内部类总共有 4 种(静态内部类、非静态内部类、局部内部类、匿名内部类)

作用:

一:内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。

二:内部类可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问,但是外部类不能访问内部类的实现细节,例如内部类的成员变量。

三:匿名内部类适合用于创建那些只需要创建一次使用的类。

四:内部类比外部类可以多使用三个修饰符:private、protected、static

五:非静态内部类不能拥有静态成员。

1.非静态内部类:

非静态内部类定义在外部类里面

public class Demo {

    private String name;

    private Integer age;
    
    //定义构造器重载
    public Demo(){}
    
    public Demo(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    
    //定义一个非静态内部类
    private class InnerDemo {
        //非静态内部类
        private double length;
        private String color;
        public InnerDemo(){}
    
        public InnerDemo(double length, String color) {
            this.length = length;
            this.color = color;
        }
        // 此处省略 length  的getter 和 setter 方法
        .....................

        //非静态内部类实例方法
        public void info() {
            System.out.print("名字是:" + name + ": " + "年龄是: " + age +"肤色是: " + color + "身高是: " + length);
        }
        
    }

     public void test() {
        
        var innerDemo = new InnerDemo(180, "黄");
        innerDemo.info();
    }

    public static void main(String[] args) {
        Demo demo = new Demo("李白", 30);
        demo.test();
    }

}

当非静态成员内部类的方法访问某个变量的时候,系统优先在该方法内查找是否存在该名字的局部变量,如果存在就使用该变量;如果不存在则查找该方法所在的内部类中是否存在该名字的成员变量, 如果不存在,则到外部类中查找是否存在该名字的成员变量,如果依然不存在,则会报编译错误:提示找不到该变量。

因此,如果外部类成员变量、内部类成员变量与内部类里面的方法同名,则可以通过使用 this、外部类类名.this 作为限定区分。

可以参考如下模板:

public class Demo {
    private String name = "外部类的成员变量";
    
    private class InClass {
        private String name = "内部类的成员变量";
        public void info(){
            var name = "局部变量";
            System.out.print("外部类的实例变量值:" + Demo.this.name);
            
            //通过 this.varName 访问内部类的成员变量
            System.out.print("内部类的成员变量:" + this.name);

            //直接访问局部变量
            System.out.print("局部变量的值:" + name);
        }
    }
    
    public void test() {
        var inClass = new InClass();
        inClass.info();
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.test();
        
    }

}

2. 静态内部类

使用 static 修饰的内部类被称为静态内部类,它属于外部类本身,不属于外部类某个实例对象。

静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。

public class Demo {
    private String name = "李白";

    private static int age = 2;

    static class StaticInnerClass {
        //静态内部类可以包含静态成员
        private static String womanName = "李清照";
        
        public void info() {
            //下面代码出现错误,静态内部类不能访问外部类的实例变量
            System.out.print(name);
            //下面代码正常
            System.out.print(age);
        }

    }

}

静态内部类是外部类的一个成员,因此外部类的所有方法、所有初始化块中可以使用静态内部类来定义变量、创建对象等

public class Demo {
    
    static class StaticInnerClass {
        private static int age = 10;
        private String name = "李白";
    }
    
    public void info() {
    
        //System.out.print(age)
        //上面代码出现错误,应改为如下格式
        //通过类名访问静态内部类的类成员
        System.out.print(StaticInnerClass.age);
        //System.out.print(name);
        //上面代码出现错误,应改为如下格式
        //通过实例访问静态内部类的实例成员
        System.out.print(new StaticInnerClass().name);
    }

}

Java 还允许在接口里定义内部类,接口里定义的内部类默认使用 public static 修饰,也就是说接口内部类只能是静态内部类。

3.使用内部类:

1.在外部类内部使用内部类

在外部类内部使用内部类的时,与平常使用普通类没有太大的区别。一样可以直接通过内部类类名来定义变量,通过 new 调用内部类构造器来创建实例。

唯一的区别是:不要在外部类的静态成员(包括静态方法和静态初始化)中去使用非静态内部类,因为静态成员不能访问非静态成员。

在外部类内部定义内部类的子类与平常定义子类也没有太大的区别。

2.在外部类外部使用非静态内部类

如果想在外部类外部使用非静态内部类,则内部类不能使用 private 访问控制权限,private 修饰的内部类只能在外部类内部使用。对于使用其他访问控制符修饰的内部类,则能在访问控制符对应的访问权限内使用。

由于非静态内部类的对象必须寄生在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象。在外部类外面创建内部类实例的语法如下:

outerInstance.new InnerContructor();

当创建一个子类的时候,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象

class Out  {

    class Inner {
        public Inner(String name) {
            System.out.print(name);
        }
    }
}


public class Demo extends Out.Inner {
    //显式定义构造器
    public Demo(Out out) {
        out.super("Hello")
    }

}

非静态内部类的子类不一定是内部类,它也可以是外部类。但非静态内部类的子类实例一样需要保留一个引用,该引用指向其父类所在外部类的对象。也就是说,如果有一个内部类子类的对象存在,则一定存在与之对应的外部类对象。

3.在外部类以外使用静态内部类。

因为静态内部类是外部类类相关的,因此创建静态内部类对象时无需创建外部类对象。创建语法如下:

new OutClass.InnerClass();

相比之下,使用静态内部类比使用非静态内部类要简单很多,只要把外部类当成静态内部类的包空间即可。因此当程序需要使用内部类时,应该优先考虑使用静态内部类。

4.局部内部类

把内部类定义在方法里面,这个就是局部内部类。局部内部类因为只能在方法里面有效,不能在外部类方法以外使用。所以不能使用访问控制符和 static 修饰符修饰。

局部内部类是一份非常鸡肋的语法,在实际开发中很少定义局部内部类,这是因为局部内部类的作用域太小了;只能在当前方法中使用。大部分时候,定义一个类之后,当然希望多次复用这个类,但局部内部类无法离开它所在的方法,因此在实际开发中很少使用。

5.匿名内部类

匿名内部类适合创建那种只需要创建一次使用的类,匿名内部类不能重复使用。

匿名内部类必须继承一个父类或者实现一个接口,但最多只能继承一个父类,或实现一个接口。

关于匿名内部类还有如下两条规则:

1.匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象,所以不能把不允许将匿名内部类定义成抽象类。

2.匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但可以定义初始化块,可以通过初始化块来完成构造器需要完成的事。

interface Animal {
    double getPrice();
    String getName();
}


public class Demo {

    public void test(Animal a) {

        System.out.print("购买了一个: " + a.getName() + "价格是:" + a.getPrice());
    }

    public static void main(String[] args) {
        
        var tar = new Demo ();
        //调用 test()方法时需要传入一个 Animal 参数
        //此处传入匿名实现类的实例
        tar.test(new Animal() {
            public double getPrice() {
                return 567.9;
            }
        
            public String getName() {

                return "二哈";
            }

        });
    }

}

上面程序中的 Animal 类定义了一个 test()方法,该方法需要一个 Animal 对象作为参数,但 Animal 只是一个接口,无法直接创建对象,因此此处创建一个 Animal 接口实现类的对象传入该方法----如果这个 Animal 接口实现类需要重复用,则应该将实现类定义为一个独立类:如果这个 Animal 接口实现类只需一次使用,则可以采用上面程序中的方式,定义一个匿名内部类。

定义匿名内部类无需 class 关键字,而是在定义匿名内部类的时候直接生成该匿名内部类的对象。

由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象类或者接口里面包含的所有抽象方法。

当通过实现接口创建匿名内部类时,匿名内部类不能显式地定义构造器,因此匿名内部类只有一个隐式的无参构造器,故 new 接口名后的括号里不能传入参数值。

但如果是通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处的相似指的时拥有相同的形参列表。

abstract class Device {
    private String name;

    public abstract double getPrice();
    public Device(){}
    public Device(String name) {
        this.name = name;
    }
    
    //此处省略 setter 和 getter 方法
    ...


}


public class AnonymousInner {

    public void test(Device d) {
        System.out.print("购买了一个"+d.getName() + ", 花掉了"+d.getPrice());
    }

    public static void main(String[] args) {

        var ai = new AnonymousInner();
        
        ai.test(new Device("电饭煲") {
        
            public double getPrice(){
                return 500.2;
            }
        });
    
        //调用无参数的构造器创建Device 匿名实现类的对象
        var d = new Device(){
            //初始化块
            {
                System.out.print("匿名内部类的初始化块....");
            }
            //实现抽象方法
            public double getPrice() {
                return 56.2;
            }
        
            //重写父类的实例方法
            public String getName() {
                return "鼠标";
            }
            
            
        };
        ai.test(d);
 
        
    }

    
}

上面的程序创建了一个抽象父类 Device 类,这个抽象父类包含两个构造器:一个无参数的,一个有参数的。当创建以Device 为父类的匿名内部类时,既可以传入参数,也可以不传入参数。

当创建匿名内部类时,必须实现接口或抽象类里面的所有抽象方法。如果有需要,也可以重写父类中的普通方法。

在 Java 8 之前,Java 要求被局部内部类、匿名内部类访问的局部变量必须使用 final 修饰,从 Java 8 开始这个限制被取消了,Java 8更加智能,如果局部变量被匿名内部类访问,那么该局部变量相当于自动加上了 final 修饰。

标签:部类,内部,静态,匿名,---,寄生,哲学,public,name
From: https://blog.csdn.net/jhk2026126055/article/details/144933580

相关文章

  • 大模型基础组件 - 位置编码
    为什么需要位置编码自注意力机制没有顺序感知:Transformer模型中的自注意力(Self-Attention)机制计算的是输入序列中所有位置的元素之间的关系,而这些关系仅仅依赖于元素的内容,而不考虑它们在序列中的顺序。例如,模型并不能自动地识别“第一个词”和“最后一个词”之间的顺序关系......
  • 【网络协议】IPv4 地址分配 - 第一部分
    文章目录十进制与二进制网络如何被寻址地址类型网络地址广播地址主机地址如何确定网络和主机部分的位数?网络中的主机数量与前缀号的关系计算每个前缀的主机数量公式子网掩码二进制与操作(BinaryANDing)与操作(ANDOperation)二进制与操作的结果关于子网划分为什么要进行......
  • floating panel - 带有锚点功能和可拖拽顶栏的浮动面板
    在现代的前端应用中,浮动面板是一个非常常见的UI组件,它能够为用户提供额外的信息和操作空间,同时又不会占据页面的主要内容区域。本文将详细记录如何实现一个带有锚点功能和可拖拽顶栏的浮动面板。设计思路这个浮动面板将由三个主要部分组成:父元素(floating-panel)、标头(floating-p......
  • C 语言奇幻之旅 - 第09篇:C 语言指针
    目录引言1.指针的基本概念1.1指针的定义与初始化1.1.1指针的定义1.1.2指针的初始化1.1.3指针的解引用1.1.4示例代码1.1.5栈帧模拟1.2指针的运算1.2.1指针的加减运算1.2.2指针的比较运算1.2.3示例代码1.2.4栈帧模拟2.指针与数组2.1指针与数组的关系2.1.1......
  • springboot儿童疫苗预约系统-计算机毕业设计源码58516
    摘  要随着人们对健康的重视和疫苗接种意识的提高,疫苗预约系统成为一种重要的健康管理工具。本研究旨在基于SpringBoot框架开发儿童疫苗预约系统,包括首页、网站公告、疫苗资讯和儿童疫苗预约模块,旨在为家长和监护人提供方便快捷的儿童疫苗预约服务,促进儿童健康管理和疫......
  • 孕妇地区推荐菜品助手(appinventor+掌控板esp32+su-03t语音模块)
    一、系统概述该系统主要由APP端和掌控板两部分组成,并通过连接多个硬件设备实现功能。系统利用云计算和物联网技术,能够实现用户与餐饮设备之间的高效交互。系统功能地区选择与菜品推荐用户可以通过APP选择所在地区,系统会根据所选地区在掌控板上显示当地的菜品信息。当......
  • springboot一德餐厅点餐系统-毕业设计源码83801
    摘 要本文介绍了基于SpringBoot框架开发的一德餐厅点餐系统的设计与实现。随着餐饮行业的数字化转型,点餐系统的重要性日益凸显。该系统旨在提供顾客便捷的点餐体验,包括菜单浏览、下单支付等功能,提升餐厅服务效率和顾客满意度。通过充分利用SpringBoot框架的优势,点餐系......
  • springboot闲置物品交易系统-计算机毕业设计源码01364
    摘 要本项目是一个基于SpringBoot的闲置物品交易系统。该平台旨在为大学生提供一个便捷、高效的交易平台,使他们能够在校园内买卖闲置物品。通过该平台,学生们可以方便地找到自己需要的物品,同时也可以将自己不再使用的物品转让给其他有需求的同学。这样的"买卖同体"理念促......
  • SpringBooot3.4.x,Mybatis-Plus3.5.x报错:Invalid value type for attribute 'factoryBe
    SpringBooot3.4.x,Mybatis-Plus3.5.x报错:Invalidvaluetypeforattribute'factoryBeanObjectType':java.lang.String解决方案原因mybatis-plus-boot-starter中mybatis-spring版本与SpringBooot3不兼容方式1将mybatis-plus-boot-starter替换为mybatis-plus-spring-boot3-sta......
  • Redpanda Console - 流数据管理控制台
    RedpandaConsole-流数据管理控制台简介Redpanda是一个与Kafka兼容的流媒体数据平台,该平台具有高性能、操作友好和云就绪性。这家总部位于旧金山的公司成立于2019年,专注于Kafka公司关键任务系统的替代产品。Redpanda使用C++重写Kafka,与KafkaAPI完全兼容,可以与所有Kafka流媒......