Android进阶宝典—Koin使用和原理分析

news2024/9/22 1:43:48

一、理解设计模式

控制反转

是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。

实现控制反转最常见的方式叫做依赖注入(Dependency Injection,简称DI),依赖注入(Dependency Injection)和控制反转(Inversion of Control)基本可以理解是同一个概念。

具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但使用依赖注入后,创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者实例的工作通常由DI框架(Koin/ARouter/Spring容器)来完成,然后注入调用者,因此也称为依赖注入。

依赖注入框架的优点

  • 依赖注入库会自动释放不再使用的对象,减少资源的过度使用。
  • 在指定范围内,可重用依赖项和创建的实例,提高代码的可重用性,减少了很多模板代码。
  • 代码变得更具可读性。
  • 易于构建对象。
  • 编写低耦合代码,更容易测试。

如需Android进阶学习资料 请点击免费领取

二、DI框架对比

Dagger2

优点:Google加持,社区广泛,性能佳

缺点:难上手,编译慢,编译错误晦涩,kotlin、android支持不简洁(代码量大)

Koin

优点:易上手、易调试、编译快、代码少,jetpack/Kotiln支持棒

缺点:性能不及dagger2/Hilt,java兼容不足,社区小

三、Koin使用和进阶

框架介绍

是一款轻量级的依赖注入框架,无代理,无代码生成,无反射。相对于dagger 而言更加适合Kotlin语言。
在这里插入图片描述

1.基本使用

  • 创建module

声明需要通过Koin创建的对象,以及创建对象的方式

包括factory、single、get、bind、quilifier关键字使用

val appModule =  module {
	factory { Girl() }//每次都会生成新的对象
	//single<Girl> { Girl() }//生成单一对象
	//single { Girl() }//生成单一对象
} 
val appModule1 =  module {
	factory { Name() }//每次都会生成新的对象
	factory { Girl(get<Name>()) }
        //获取之前声明的Name对象作为传入Girl构造的参数
	
	factory ( quilifier=named("GilrQuilifier0")) {
		Girl() 
	}//每次都会生成新的对象,带quilifier
	factory ( quilifier=named("GilrQuilifier1")) { params->
		Girl(param0 = param[0],param1 = param[1]) 
	}//每次都会生成新的对象,带quilifier,带参数
}


//bind使用,也就是绑定其他接口注入获取实例其实也是用这个单例
val xxModule = module {
	single { xx() } binds (arrayOf(//适用于一个实现类,有多个接口的情况
		interface1::class,
		interface2::class
	))
	single { xx() } bind (xx::class)
}
  • 注册module

用于将创建的module和Koin关联起来

startKoin {
        //一般在Application的onCreate或者attachBaseContext中调用,用于设置一些Koin框架的通用属性
	//设置log级别
	AndroidLogger(Level.DEBUG)
	//注入context,方法module中get()获取Context
	androidContext(this@KoinApplication)
	//设置module
	modules(appModule,appModule1)
}
loadKoinModules(appModule)
//用于在各自的类中调用,可以选择在执行到某些类后再将内部的能力暴露,
//能力其实和startKoin中的modules()是一样的
  • 依赖注入

当某个角色需要另一个角色的协助时,使用注入方式替代new的方式获取另一个角色的实例,分为by inject和get方式获取,其中parametersOf用于携带参数。

//方法一、必须是val 如果已经给定了类型inject不用使用类型
val girl1 : Girl by inject()
//方法二、必须是val  变量没给定类型,需要在inject中使用泛型
val girl2 by inject<Girl>()
//方法三 var 和val都行,直接获取
var girl3 = get<Girl>()
//带quilifier、带参数
var girl4:Girl by inject(quilifier = named("GilrQuilifier0"))
var girl5:Girl by inject(quilifier = named("GilrQuilifier1")){
	parametersOf("param0","param1")//其中parametersOf用于携带参数
}
var girl6:Girl by inject(){
	parametersOf("param0")
}


//可以解决koin找不到类报空错误的问题,
//这样提供方可以按需注册,使用方有就用,没有就不用
val xx = Injector.injectFactory<XX>()
private val xx: XX? by lazy {
		Injector.injectFactory<XX>()
}


object Injector : KoinComponent {


	inline fun <reified T> injectFactory() = try {
		val instance: T by inject()
		instance
	} catch (e: Exception) {
		Log.e(TAG, "inject has error:${e.message}")
		null
	}
}

by inject具有懒加载能力,get则是直接获取,如源码:

inline fun <reified T : Any> KoinComponent.get(    
qualifier: Qualifier? = null,    
noinline parameters: ParametersDefinition? = null): T {    
return if (this is KoinScopeComponent) {        
scope.get(qualifier, parameters)    
} else getKoin().get(qualifier, parameters)}
inline fun <reified T : Any> KoinComponent.inject(    
qualifier: Qualifier? = null,    
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),    
noinline parameters: ParametersDefinition? = null): Lazy<T> 
=    
lazy(mode) { get<T>(qualifier, parameters) }

2.进阶使用

  • Scope

注入对象都是有作用范围的,如果没有指定scope的话就是koin的一个rootScope,如果指定scope,注入时就会从该scope中去查找声明的对象

声明
val scopeModule=module{
    single{//新建
        Girl()
    }
    scope(named("high")){
        scoped(named("1")) { Girl(180) }//单例创建
        factory(named("2")) { Girl(179) }//新建
    }
    scope(named("low")){
        scoped { Girl(160) }
    }
}
//注入
val girlScope by getKoin()
.createScope("scopeId1",named("high"))
.inject<Girl>()

**a.**可以在创建scope后,通过bindScope绑定到当前Activity,Activity会在destory后自动销毁(和协程的lifecycleScope、viewModelScope异曲同工)
**b.**当申明作用域的限定名相同时,将合并两个作用域,并且合并的两个作用域scoped和factory不能相同
**c.**scope标签下的scoped和factory方法区别

scoped方法和single相似,scoped表示在当前申明的作用域下创建单例,生命跟随当前作用域,scope下无法使用single。

factory方法,在当前作用域下的工厂模式,每次获取都会创建。

  • override和createAtStart

createAtStart

声明一个类型或module 在什么时候创建(使用时 or 开始) false 使用时再创建,true startKoin 就创建

override

Koin 不允许同一名称同一类型声明多次,当你需要声明多次时,你可以使用override

    <!--
	val myModule = module {
	 // 声明Service 类型在开始就创建
		single<Service>(createAtStart=true) { 
          TestServiceImp() 
        }
	}
	-->


	<!--
	val helloModule = module {
		single { "a" }
		single(override = true) { "b" }
		single(override = true) { "c"}
	 
	}
	-->
  • 泛型列表处理

    module { single { ArrayList() } single { ArrayList() } }//会抛出异常 val helloModule = module { single(named(name = “Ints”)) { ArrayList() } single(named(name = “Strings”) ) { ArrayList() } }//ok

  • KoinComponent和 KoinJavaComponent

KoinComponent

在一般的类中,我们如何依赖注入?

实现KoinComponent,则普通类中就可以使用注入对象

该类需要实现KoinComponent,在该类中,我们就可以通过by inject和get来过去被注入过的对象了,原理其实就是inject属于KoinComponent的扩展函数,像Activity这种类中使用注入不需要事先KoinComponent原因是因为Koin自动对Activity的本身就已实现的接口ComponentCallbacks进行了扩展。
KoinJavaComponent

可以在java中使用依赖注入

同样可以使用inject和get方法来获取

例如:

private XX xx=KoinJavaComponent.get(XX.class,named(""));private XX xx=KoinJavaComponent.inject(XX.class);private XX xx=KoinJavaComponent.inject(XX.class,named(""));
  • 易混淆的几种匹配规则

声明类型可以通过<>泛型匹配,也可以通过bind、binds指定

获取类型可以通过var xx:XX 指定,也可以通过by inject()指定

1.如果注入是采用实例注入,不指定接口,获取的时候采用接口能找到实例吗?
不可以,注入和获取需要匹配类型,会报错
<!--
factory { Girl() } //声明
val girl2 by inject<IGirl>() //获取
-->
<!--
Caused by: org.koin.core.error.NoBeanDefFoundException
: No definition found for class
:'com.frame.koin.IGirl'. Check your definitions!
-->

2.通过接口注入,但是通过实现类型进行获取是否可以成功?
可以
<!--
factory { Girl() } bind (IGirl::class)//声明
val girl2 by inject<Girl>() //获取
-->
3.使用实现类注入,通过实现类获取?
可以
<!--
factory { Girl() } //声明
val girl2 by inject<Girl>() //获取
-->	

四、原理分析

我们拆分为Koin初始化、模块声明、模块加载、实例注入来介绍。

源码版本为 3.1.1

  • Koin初始化

代码调用

<!--
 startKoin {
            //使用Koin Android Logger
            androidLogger(Level.DEBUG)
            //声明Android上下文
            androidContext(this@MyApplication)
            //声明要使用的模块,除了在初始化的时候注册,也可以通过loadModule进行动态注册
            modules(myAppModules)
    }
-->

源码分析

<!--GlobalContext.kt
override fun startKoin(
appDeclaration: KoinAppDeclaration)
: KoinApplication = synchronized(this) {
        val koinApplication = KoinApplication.init()
        register(koinApplication)
        appDeclaration(koinApplication)
        return koinApplication
    }
-->

核心做了几件事情:

1.生成一个KoinApplication

KoinApplication持有Koin实例,Koin实例内部持有三个registry注册表,其中ScopeRegistry用于存储scope作用域实例注册表,InstanceRegistry注册表保存目标实例注册表,InstanceRegistry内部有一个HashMap<indexKey,InstanceFactory>存储key和实例创建工厂的映射,indexKey由class,qualifier,scopeQualifier共同组成的字符串唯一标识

2.将KoinApplication和内部的Koin赋值给GlobalContext

3.执行startKoin的代码块

  • 模块声明

代码调用

<!--
val appModule =  module {
        //每次都会生成新的对象
        factory { Girl() } bind (IGirl::class)
        //生成单一对象
        single(createdAtStart = true) { Girl() }
    }
-->

源码分析

<!--Module.kt
internal val mappings = 
hashMapOf<IndexKey, InstanceFactory<*>>()
//保存当前module内部的index和对应实例工厂的映射
fun module(createdAtStart: Boolean = false, 
moduleDeclaration: ModuleDeclaration): Module {
	//创建一个模块Module类
    val module = Module(createdAtStart)
	//执行传入的匿名扩展函数
    moduleDeclaration(module)
    return module
}
//单例创建
inline fun <reified T> single(
        qualifier: Qualifier? = null,
        createdAtStart: Boolean = false,
        noinline definition: Definition<T>
    ): Pair<Module, InstanceFactory<T>> {
		//将definition封装,用于后续调用方需要的时候匹配后,
        //直接执行并返回目标实例
        val def = createDefinition(Kind.Singleton, qualifier, 
        definition, scopeQualifier = rootScopeQualifier)
        val mapping = indexKey(def.primaryType, qualifier, 
        rootScopeQualifier)
        //SingleInstanceFactory内部会保存上次创建的实例,
        //下次获取的时候如果存在直接返回
        val instanceFactory = SingleInstanceFactory(def)
        saveMapping(mapping, instanceFactory)
        if (createdAtStart || this.createdAtStart) {
            eagerInstances.add(instanceFactory)
        }
        return Pair(this, instanceFactory)
    }
internal fun saveMapping(mapping: IndexKey, 
        factory: InstanceFactory<*>, 
        allowOverride : Boolean = false) {
        if (!allowOverride && mappings.contains(mapping)) {
            overrideError(factory, mapping)
        }
        mappings[mapping] = factory
    }
//factory重新创建
inline fun <reified T> factory(
        qualifier: Qualifier? = null,
        noinline definition: Definition<T>
    ): Pair<Module, InstanceFactory<T>> {
        return factory(qualifier, definition, rootScopeQualifier)
    }


    @PublishedApi
    internal inline fun <reified T> factory(
        qualifier: Qualifier? = null,
        noinline definition: Definition<T>,
        scopeQualifier: Qualifier
    ): Pair<Module, InstanceFactory<T>> {
        val def = createDefinition(Kind.Factory, qualifier, 
        definition, scopeQualifier = scopeQualifier)
        val mapping = indexKey(def.primaryType, qualifier, 
        scopeQualifier)
        //FactoryInstanceFactory每次都会调用create新建
        val instanceFactory = FactoryInstanceFactory(def)
        saveMapping(mapping, instanceFactory)
        return Pair(this, instanceFactory)
    }
-->
  • 模块加载

负责把声明的module加载到Koin当中。

代码调用

modules(appModule)
	KoinApplication.modules()
		Koin.loadModules()
			InstanceRegistry.loadModules
			ScopeRegistry.loadScopes

源码分析

<!--InstanceRegistry.kt
//用于存储刚才装载阶段放入各自module的key value映射,
//因此要注意独立module内的映射都会放入这里哦,也就是module会合并
private val _instances = safeHashMap<IndexKey, InstanceFactory<*>>()
internal fun loadModules(modules: List<Module>, allowOverride: Boolean) {
        modules.forEach { module ->
            loadModule(module, allowOverride)
            createEagerInstances(module.eagerInstances)
        }
    }
private fun loadModule(module: Module, allowOverride: Boolean) {
        module.mappings.forEach { (mapping, factory) ->
            saveMapping(allowOverride, mapping, factory)
        }
    }
fun saveMapping(
        allowOverride: Boolean,
        mapping: IndexKey,
        factory: InstanceFactory<*>,
        logWarning : Boolean = true
    ) {
        if (_instances.containsKey(mapping)) {
            if (!allowOverride) {
                overrideError(factory, mapping)
            } else {
                if (logWarning) _koin.logger.info("Override 
                Mapping '$mapping' with ${factory.beanDefinition}")
            }
        }
        if (_koin.logger.isAt(Level.DEBUG) && logWarning){
            _koin.logger.debug("add mapping '$mapping' for ${
            factory.beanDefinition}")
        }
        _instances[mapping] = factory
    }
-->
  • 实例注入

代码调用

inject()
	KoinContext.get<T>(qualifier, parameters)
		Koin.get<T>(qualifier, parameters)
                        //scope决定了使用全局还是局部,这里只写了全局,也就是默认的rootScope
			ScopeRegistry.rootScope.get(qualifier, parameters)
				Scope.get()
					Scope.resolveInstance
						Scope.resolveValue
							InstanceRegistry.resolveInstance

源码分析

<!--ScopeRegistry.kt
//不声明scope的时候默认使用这个作用域
val rootScope = Scope(rootScopeQualifier, ROOT_SCOPE_ID, isRoot = true, _koin = _koin)
private val _scopes = safeHashMap<ScopeID, Scope>() 
-->
<!--Scope.kt
 fun <T> get(
        clazz: KClass<*>,
        qualifier: Qualifier? = null,
        parameters: ParametersDefinition? = null
    ): T {
        return if (_koin.logger.isAt(Level.DEBUG)) {
            val qualifierString = qualifier?.let { " with qualifier 
            '$qualifier'" } ?: ""
            _koin.logger.debug("+- '${clazz.getFullName()}'$
            qualifierString")
            val (instance: T, duration: Double) = 
            measureDurationForResult {
                resolveInstance<T>(qualifier, clazz, parameters)
            }
            _koin.logger.debug("|- '${clazz.getFullName()}' 
            in $duration ms")
            return instance
        } else {
            resolveInstance(qualifier, clazz, parameters)
        }
    }
-->
<!--InstanceRegistry.kt
internal fun <T> resolveInstance(
        qualifier: Qualifier?,
        clazz: KClass<*>,
        scopeQualifier: Qualifier,
        instanceContext: InstanceContext
    ): T? {
        val indexKey = indexKey(clazz, qualifier, scopeQualifier)
        return _instances[indexKey]?.get(instanceContext) as? T
        //也就是执行Factory的get方法
    }
-->
<!--InstanceFactory.kt
open fun create(context: InstanceContext): T {
        val koin = context.koin
        if (koin.logger.isAt(Level.DEBUG)){
            koin.logger.debug("| create instance for $beanDefinition")
        }
        try {
            val parameters: ParametersHolder = context.parameters ?: 
            emptyParametersHolder()
            //执行声明的时候传入的代码块,也就是创建实例            
            return beanDefinition.definition.invoke(
                context.scope,
                parameters
            )
        } catch (e: Exception) {
            val stack = KoinPlatformTools.getStackTrace(e)
            koin.logger.error("Instance creation error : 
              could not create instance for $beanDefinition: $stack"
            )
            throw InstanceCreationException("Could not create 
              instance for $beanDefinition", e
            )
        }
    }
override fun get(context: InstanceContext): T {
        return create(context)//new
    }
-->

<!--SingleInstanceFactory.kt
private fun getValue() : T = value ?: error("Single instance 
  created couldn't return value")
override fun get(context: InstanceContext): T {
        KoinPlatformTools.synchronized(this) {
            if (!isCreated(context)) {
                value = create(context)
            }
        }
        return getValue()
    }
-->

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

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

相关文章

提高硬件设计能力的学习路线

不懂硬件的人&#xff0c;会觉得硬件高深莫测&#xff0c;“为什么他改几个电阻、电容就调出来&#xff0c;我弄个半天没搞定&#xff1f;”&#xff0c;“噢&#xff0c;靠的是经验”&#xff0c;但是经验又是什么呢&#xff1f;不能形容&#xff0c;反正就是不明觉厉。 就是…

Git(版本控制:前端git使用全流程)

1.版本控制&#xff08;简单理解&#xff1a;就是软件对每次操作之后提交的记录&#xff09; 文件版本 版本控制软件 版本控制的好处 版本控制的分类 本地版本控制系统 集中化版本控制系统 分布式版本控制系统 2. Git基础概念与工作流程 什么是Git&#xff1f; 开源的…

单片机编程中的裸机编程和多任务系统FreeRTOS系统详解,以及怎么学习FreeRTOS,看哪家的教程?(合集)

单片机编程中的裸机系统和多任务系统 学习了那么久的stm32还停留在裸机&#xff1f;&#xff1f;&#xff1f; 单片机编程中的裸机系统和多任务系统.1 裸机系统1.1轮询系统1.2 前后台系统 2 多任务操作系统3 为什么要学习多任务操作系统&#xff1f;&#xff1f;4 怎么学习Free…

诊断CAPL自动化(6) —— 诊断自动化测试,实战演示

🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】🍅 玩转CANoe,博客目录大全,点击跳转👉 下图是UDS协议测试的部分测试用例,该表格在文章末尾网盘自己获取经过前面几…

Cesium实践(4)——空间数据加载

文章目录 前言几何形体点线面体 标签文字图标 几何文件GeoJsonKMLCZML 三维模型总结 前言 本文介绍Cesium如何加载空间数据&#xff0c;空间数据即明确定义在三维空间中的数据&#xff0c;空间数据包括以下几类&#xff1a;1、几何形体&#xff08;点、线、面、体&#xff09;…

“支付+SaaS”赋能传统产业数字化转型

易观&#xff1a;传统支付业务利润空间在政策监管和市场竞争下不断被压缩&#xff0c;多家上市支付机构逐步将业务经营重点转移切入到企业交易环节&#xff0c;为企业提供包括SaaS服务、行业解决方案、营销服务及金融科技服务等在内的企业服务收入成为新的增长点。 伴随着“十四…

OSCP-AuthBy(Server 2008提权)

目录 扫描 FTP WEB 提权 扫描 nmap -sV -sC -p- 192.168.73.46 FTP 检查端口21上的匿名登录 导航到accounts目录,可以观察ftp服务上的帐户 Offsec, anonymous, admin

Java8函数式编程(Lambda表达式,Stream流,Optional)

一.函数式编程思想 面向对象思想主要是关注对象能完成什么事情&#xff0c;函数式编程思想就像函数式&#xff0c;主要是针对数据操作&#xff1b;代码简洁容易理解&#xff0c;方便于并发编程&#xff0c;不需要过分关注线程安全问题 二.lambda表达式 1.概念 lambda表达式…

提升金融写作效率:金融校对软件的优势与应用

金融行业的写作任务繁重且要求高度专业&#xff0c;涉及财务报告、研究报告、合同、政策文件等各类文档。金融校对软件应运而生&#xff0c;为金融专业人士提供有效的支持&#xff0c;帮助他们提高写作效率和质量。本文将探讨金融校对软件的优势与应用。 一、金融校对软件的优势…

【NFS共享存储服务】

目录 一、NFS (Network File System&#xff09;网络文件系统1.1、NFS工作原理1.2、举例1.2.1、共享文件总结 一、NFS (Network File System&#xff09;网络文件系统 依赖于RPC (远端过程调用) 需安装nfs-utils、rpcbind软件包 系统服务: nfs、rpcbind 共享配置文件: /etc/ex…

跳表的实现

目录 简介跳表的实现 简介 skiplist本质也是一种查找结构&#xff0c;和搜索树、哈希表一样可以作为key或者key/value模型的查找结构&#xff0c;从命名可以看出它也是一个链表结构&#xff0c;链表的查找效率是O(n),作为在链表基础上优化的一种查找结构&#xff0c;跳表的查找…

app拉新充场代理

我认为您可能是想了解APP充值拉新软件的功能吧。通常&#xff0c;APP充值拉新软件会具有以下功能&#xff1a; 充值服务&#xff1a;提供多种支付方式&#xff0c;让用户方便快捷地进行充值操作。 活动推广&#xff1a;通过不同的方式&#xff0c;如折扣、优惠码等&…

Tomcat的部署和优化

Tomcat的组件构成 &#xff08;1&#xff09;Web 容器&#xff1a;完成 Web 服务器的功能。 &#xff08;2&#xff09;Servlet 容器&#xff1a;名字为 catalina&#xff0c;用于处理 Servlet 代码。 &#xff08;3&#xff09;JSP 容器&#xff1a;用于将 JSP 动态网页翻译成…

centos系统安装mysql8.0

centos系统安装mysql8.0 环境说明开始1、查看centos7中是否有MariaDB&#xff0c;MariaDB与MySQL关系请自行查阅2、如果有MariaDB&#xff0c;需要将 步骤1 中查询到的mairadb全部卸载&#xff0c;否则MySQL安装会出现问题3、查看本机是否已经安装过MySQL4、如果安装过MySQL&am…

9.java程序员必知必会类库之加密库

前言 密码学在计算机领域源远流长&#xff0c;应用广泛。当前每时每刻&#xff0c;每一个连接到互联网的终端&#xff0c;手机&#xff0c;电脑&#xff0c;iPad都会和互联网有无数次的数据交互&#xff0c;如果这些数据都是明文传输那将是难以想象的。为了保护用户隐私&#…

算法--前缀和技巧 (蓝桥杯123-灵能传输)

文章目录 什么是前缀和用途什么时候用例题[蓝桥杯 2021 国 ABC] 123题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 思路代码 灵能传输(蓝桥杯96%&#xff0c;洛谷ac)[蓝桥杯 2019 省 B] 灵能传输题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1…

知识图谱实战开篇-讲述知识图谱是什么,要学哪些知识,一文讲通

大家好&#xff0c;我是微学AI&#xff0c;今天给大家带来知识图谱重要讲述&#xff0c;讲明白什么是知识图谱&#xff0c;知识图谱可以做什么&#xff0c;需要学哪些知识&#xff0c;与自然语言处理的关系。很多人认为知识图谱是关系图谱&#xff0c;可能涉及人工智能的东西不…

【LeetCode】650. 只有两个键的键盘

650. 只有两个键的键盘&#xff08;中等&#xff09; 思路 不同于以往通过加减实现的动态规划&#xff0c;这里需要乘除法计算位置。因为粘贴操作是倍数增加&#xff0c;使一个一维数组 dp&#xff0c;其中位置 i 表示延展到长度 i 的最少操作次数。对于每个位置 j &#xff0c…

C++学习 Day6

目录 1. 类对象模型 1.1 如何计算类对象的大小 1.2 类对象的存储方式 1.3 结构体内存对齐规则 2. this指针 2.1 this指针的引出 2.2 this指针的特性 3. 类的6个默认成员函数 4.构造函数 4.1 概念 4.2 特性 1. 类对象模型 1.1 如何计算类对象的大小 class A { publi…

【Java】『蓝桥杯』10道编程题及答案(一)

系列文章 【Java】『蓝桥杯』10道编程题及答案&#xff08;一&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/130223115 【Java】『蓝桥杯』10道编程题及答案&#xff08;二&#xff09; 本文链接&#xff1a;https://blog.csdn.net/y…