职业生涯知识回顾-关于抽象类和接口的思考
发表于|更新于|码不能停
|总字数:677|阅读时长:2分钟|浏览量:
抽象类和接口是两个很容易产生疑惑的概念,分不清它们的使用场景,其实只要记住两点就比较好理解:
- 接口是对行为的抽象
- 抽象类是对子类有哪些属性和行为的抽象
当你需要对一个类有哪些行为进行约束时,使用接口;需要为其他类提供一个模板以及一些通用的属性和行为,使用抽象类。
在理解什么是抽象类和接口的前提下,延伸出一些思考:在一定程度上,接口似乎是比抽象类更底层的存在,是否可以理解为先有行为,对行为进行组合才能有类?
那么下面代码中,抽象类对接口的实现有没有实际意义?
1 | <?php |
如果更进一步,抽象类的方法和接口相同,并且都是抽象方法的情况下,接口和抽象类谁更有存在的意义?
抽象类是为了提高代码复用,定义类之间的层次关系,接口是为了实现多态性和解耦。我们要知道两者并不是对立关系,甚至目标一致,都是为了提高代码质量。
工具是死的,但人是活的,所以我认为使用者并不用太过于纠结,代码没有百分百完美,也不是死板的教条。只要在需求和设计目标明确的前提下,选择抽象类或接口,甚至组合起来使用都是可以的,解决实际问题才是我们最终的目的……
最后,再来回答一下前边的三个问题:
- 抽象类和接口的层级关系:抽象类可以被视为介于接口和类之间的一种中间形式。
- 抽象类实现接口的意义:统一接口规范,确保抽象类和子类都遵循接口的约束。
- 全抽象方法的抽象类和接口谁更有意义:如果只是定义方法契约而不提供具体实现,接口更有意义;抽象类有明确扩展目标时,保留抽象类。
文章作者: m-finder
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 M-finder!
相关推荐

2021-04-13
一句话看懂集群、微服务和分布式的区别
一个完整的服务拆分成多个微小的服务,就是微服务。 每个服务不止一个就是集群。 微服务部署在多台服务器上,服务器之间可以相互通信就是分布式。 微服务与分布式的细微差别是微服务可以部署在一台服务器,也可以部署在多台服务器。

2025-09-12
Mac 菜单栏多合一工具 FancyTool 更新啦!
本次更新聚焦「轻量体验」深度优化:不仅重构了 CPU 占用逻辑与系统唤醒机制,让后台运行更高效;更让动画交互全程保持丝滑流畅,资源消耗却低到近乎无感 —— 哪怕它常驻菜单栏,你也几乎察觉不到它的存在,既不拖慢系统,又能随时响应需求~ 下载地址: [ github ] [ gitee ] 目前软件主要包含以下功能: 🚀 智能CPU动态图标 让性能可视化 将任何GIF图片设置为你的菜单栏图标,它的播放速度会实时响应你的CPU使用率 空闲时悠然自得,高负荷时急速狂飙,用最酷的方式监控系统状态 支持完全自定义上传,打造你的专属动画 🌈 渐变彩色心情签名 用美丽的渐变色彩表达每日心情状态 完全可自定义的颜色和文字,展现独特个性 为您的菜单栏增添一抹艺术气息 📋 高效剪切板管理 记录多次复制历史,随时找回需要的内容 智能分类整理,快速定位所需片段 支持文本、图片等多种格式,提高工作效率 🖥️ 屏幕圆角美化 为Mac屏幕添加优雅圆角,提升视觉美感 智能适配多显示器设置,每块屏幕完美呈现 无性能影响的背景运行,细腻改善视觉体验 📎 菜单栏折叠工具 自动整理拥挤的菜单栏图标,保持界面整洁 一键展开/折叠,平衡简洁与便捷 自定义排序和分组,完全按您的方式组织 技术特点 原生 Swift 开发,完美兼容最新系统 轻量级设计,资源占用极低 直观易用的界面,无需学习成本 定期更新,持续改进功能和体验 如果有好的建议或者使用时遇到任何问题欢迎随时反馈👏

2024-09-28
Mac m1 编译安装 Aseprite
!!!需要用到 brew 和 xcode,如果没安装要先装好。 克隆源码1git clone --recursive https://github.com/aseprite/aseprite.git 下载 skia1https://github.com/aseprite/skia/releases 选择对应平台的文件下载,解压后重命名文件夹为 skia。 安装 cmake && ninja12brew install cmakebrew install ninja 编译1234567891011121314151617cd asepritenkdir buildcd buildcmake \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_OSX_ARCHITECTURES=arm64 \ -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \ -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \ -DLAF_BACKEND=skia \ -DSKIA_DIR=/opt/homebrew/var/www/myself/skia \ -DSKIA_LIBRARY_DIR=/opt/homebrew/var/www/myself/skia/out/Release-arm64 \ -DSKIA_LIBRARY=/opt/homebrew/var/www/myself/skia/out/Release-arm64/libskia.a \ -DPNG_ARM_NEON:STRING=on \ -G Ninja \ ..ninja aseprite !!!一定要注意 skia 的相关路径 编译完成后可以做成应用程序,以下两种方案二选一,建议采用第二种方法,因为第一种在启动台不显示应用。 组装APP访达进入应用程序目录,新建 Aseprite.app 目录,build/bin/data/icons目录下有各种图标,右键 Aseprite.app 目录,选择显示简介,把心仪的图标拖到左上角。 然后继续右键目录,选择显示包内容,新建目录 Contents/MacOS,进入目录,把编译好的 build/bin 目录下的 data 和 aseprite 复制进去即可。 替换试用版从 Aseprite 官网下载其提供的免费测试版,重命名为 Aseprite.dmg,和刚才的源码放在同一个文件夹。 1234567891011121314151617# 将免费测试版安装包挂载至 mount 目录mkdir mountyes qy | hdiutil attach -quiet -nobrowse -noverify -noautoopen -mountpoint mount Aseprite.dmg# 复制得到 App 包cp -rf mount/Aseprite.app .# 卸载免费测试板安装包hdiutil detach mountrm -rf mount# 删除免费测试板 App 包中的可执行程序以及相关数据文件rm -rf Aseprite.app/Contents/MacOS/asepriterm -rf Aseprite.app/Contents/Resources/data# 用刚编译好的可执行程序以及相关数据文件替换cp -rf Aseprite/build/bin/aseprite Aseprite.app/Contents/MacOS/asepritecp -rf Aseprite/build/bin/data Aseprite.app/Contents/Resources/data# 将替换好的 App 包复制到 Applications/ 目录下cp -rf Aseprite.app /Applications/ 如果一切顺利的话,可在启动台看到安装好的 App,点击启动已确认是否可以正常打开。 汉化字体下载 参考在 Mac 上编译安装 Aseprite

2024-09-19
Mac 用 Brew 安装旧版本 PHP
最近又用到 php7.4,奈何本地早就已经升到了 8,想搞回来,发现一个简单好用爽歪歪的三方库,需要的可以试一下,5.6 - 8.4 都有。[ homebrew-php ] 添加地址库1brew tap shivammathur/php 安装指定版本1brew install shivammathur/php/php@7.4 切换版本12345brew services stop php@7.4brew unlink php@xxbrew link php@7.4brew services start php@7.4 搞定收工。

2025-08-25
Mac 菜单栏多合一工具自荐:FancyTool
鉴于本人特别喜欢花里花哨的菜单栏,但又不想开机自启太多软件,所以自己开发了一个新的、轻量的,有点花里胡哨但又有些实用功能的多合一工具:[ FancyTool ], 感兴趣的朋友可以点击链接下载使用。 我承认,这里的介绍是有点啰嗦😊 目前软件主要包含以下功能: 🚀 智能CPU动态图标 让性能可视化! 将任何GIF图片设置为你的菜单栏图标,它的播放速度会实时响应你的CPU使用率。 空闲时悠然自得,高负荷时急速狂飙,用最酷的方式监控系统状态。 支持完全自定义上传,打造你的专属动画! 🌈 渐变彩色心情签名 用美丽的渐变色彩表达每日心情状态 完全可自定义的颜色和文字,展现独特个性 为您的菜单栏增添一抹艺术气息 📋 高效剪切板管理 记录多次复制历史,随时找回需要的内容 智能分类整理,快速定位所需片段 支持文本、图片等多种格式,提高工作效率 🖥️ 屏幕圆角美化 为Mac屏幕添加优雅圆角,提升视觉美感 智能适配多显示器设置,每块屏幕完美呈现 无性能影响的背景运行,细腻改善视觉体验 📎 菜单栏折叠工具 自动整理拥挤的菜单栏图标,保持界面整洁 一键展开/折叠,平衡简洁与便捷 自定义排序和分组,完全按您的方式组织 技术特点 原生 Swift 开发,完美兼容最新系统 轻量级设计,资源占用极低 直观易用的界面,无需学习成本 定期更新,持续改进功能和体验 使用方法 赞赏如果觉得有用,可以请我喝杯咖啡☕️ 感谢以下老板投喂,🫰❤️ 姓名 金额 时间 留言 *龙 ¥ 10.00 2025-08-27 15:00:00 ☕️

2023-05-11
php 国密 sm2 sm3 sm4 完整测试类
应用范围及描述 算法类型 国密算法 应用范围及描述 对称加密 SM1 128位数据加密,算法不公开,仅以IP核的形式存在于芯片中。智能IC卡、智能密码钥匙、加密卡、加密机。 非对称加密 SM2 被用来替换RSA算法。常用于身份认证,数据签名,密码交换,256位椭圆曲线。 完整性运算 SM3 256位数据摘要计算,相当于SHA256,数字签名及验证、消息认证码生成及验证、随机数生成 对称加密 SM4 128位数据加密,相当于AES(128) 相关代码php sm2 sm3 sm4 完整测试类,可拖入 laravel unit test 模块运行。基于扩展包 [ lpilp/guomi ] , sm2 与兴业银行有部分区别,sm4 已互通,未做招行验证。 sm2 密钥长度一般为 128 或 130 位,部分使用压缩密钥长度为 66,也就是将密钥分成 x、y,y是偶数就是02,y是奇数就是03,通过 x 可以算出 y。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290<?phpnamespace Tests\Unit;use FG\ASN1\ASNObject;use FG\ASN1\Exception\ParserException;use Mdanter\Ecc\Crypto\Signature\Signature;use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;use PHPUnit\Framework\TestCase;use Rtgm\sm\RtSm2;use Rtgm\sm\RtSm3;use Rtgm\sm\RtSm4;/** * 国密加密测试 * sm4 已与兴业银行调通 */class GmTest extends TestCase{ /** * 获取 sm2 * @return RtSm2 */ private function getSm2(): RtSm2 { return new RtSm2('base64'); } /** * 获取 sm3 * @return RtSm3 */ private function getSm3(): RtSm3 { return new RtSm3(); } /** * 获取 sm4 * @return RtSm4 */ private function getSm4(): RtSm4 { $privateKey = $this->getSm4PrivateKey(); return new RtSm4($privateKey); } /** * 获取 16进制 sm2 密钥 * 生成于工具站 https://www.lzltool.com/SM2 base64格式 * @return string */ private function getSm2PrivateKey(): string { return bin2hex(base64_decode('L8TbMByc+rQmKECWMBjnDQHrXrExqZKdl5S6sBbP07M=')); } /** * 获取 16进制 sm2 公钥 * 生成于工具站 https://www.lzltool.com/SM2 base64格式 * @return string */ private function getSm2PublicKey(): string { return bin2hex(base64_decode('BCxc4cDX1OQEpCD8O7wzPhTOljYg0uzfsMAEanCvYgBIj966+i5pgjwyIOtFSNWLWjoDzLmMJP9nf2cVmiH+aYI=')); } /** * sm2 数据格式化 * @param $dec * @return string */ private function sm2FormatHex($dec): string { $hex = gmp_strval(gmp_init($dec, 10), 16); $len = strlen($hex); if ($len == 64) { return $hex; } return $len < 64 ? str_pad($hex, 64, "0", STR_PAD_LEFT) : substr($hex, $len - 64, 64); } /** * 获取 16位 密钥 * @return bool|string */ private function getSm4PrivateKey(): bool|string { return base64_decode('NmQzZDQ2YTcxMmRjNGE0NQ=='); } /** * 获取待加密字符串 * @return string */ private function getDataStr(): string { return '{"bankCardNo":"6212028190240439021","certNo":"41052619700925136X","userName":"南瓜"}'; } /** * 拼接 sm2 待加密字符串 * @return bool|string */ private function getSm2SignStr(): bool|string { $params = json_decode($this->getDataStr(), true); $signStr = ''; if ($params != null) { ksort($params); foreach ($params as $k => $v) { $signStr .= "{$k}={$v}&"; } } return substr($signStr, 0, strlen($signStr) - 1); } public function test_sm2_sign() { $sm2 = $this->getSm2(); $signStr = $this->getSm2SignStr(); // 加密 $sign = $sm2->doSign($signStr, $this->getSm2PrivateKey()); $encryptStr = base64_decode($sign); try { $a = ASNObject::fromBinary($encryptStr)->getChildren(); } catch (ParserException $e) { $this->fail('加密失败: ' . $e->getMessage()); } $aa = $this->sm2FormatHex($a[0]->getContent()); $bb = $this->sm2FormatHex($a[1]->getContent()); $encryptStr = base64_encode(hex2bin($aa . $bb)); $this->assertNotEmpty($encryptStr); return $encryptStr; } public function test_sm2_verify_sign() { $sm2 = $this->getSm2(); $encryptSignStr = bin2hex(base64_decode($this->test_sm2_sign())); echo 'sm2 sign str: ', $encryptSignStr, PHP_EOL; $r = substr($encryptSignStr, 0, 64); $s = substr($encryptSignStr, 64, 64); $r = gmp_init($r, 16); $s = gmp_init($s, 16); $signature = new Signature($r, $s); $serializer = new DerSignatureSerializer(); $sign = base64_encode($serializer->serialize($signature)); $boolean = $sm2->verifySign($this->getSm2SignStr(), $sign, $this->getSm2PublicKey()) ?? false; echo $boolean ? 'sm2 验签通过' : 'sm2 验签失败', PHP_EOL; $this->assertTrue($boolean); } public function test_sm2_encrypt() { $sm2 = $this->getSm2(); // 压缩公钥 $key = $this->decompressPublicKey('0315edd9126410e9b94b83ee2bcdfeebe9166e84d7aad1b9d16fa923995d28e81f'); $encrypt = $sm2->doEncrypt($this->getDataStr(), $key); $this->assertNotEmpty($encrypt); return $encrypt; } public function test_sm2_decrypt() { $sm2 = $this->getSm2(); $encrypt = $this->test_sm2_encrypt(); $privateKey = 'bf5e3e47e5392a8cdba8e3f854db2d3f5e2c536235303a02898b58d085a8246a'; echo 'sm2 encrypt str: ', $encrypt, PHP_EOL; $decryptStr = $sm2->doDecrypt($encrypt, $privateKey); echo 'sm2 decrypt str: ', $decryptStr, PHP_EOL; $this->assertNotEmpty($decryptStr); $this->assertTrue($decryptStr === $this->getDataStr()); } /** * 获取未压缩公钥 * @param $compressedKey * @return string|null */ function decompressPublicKey($compressedKey): ?string { // 获取压缩标志和X坐标 $flag = substr($compressedKey, 0, 2); $x = substr($compressedKey, 2); // 将16进制字符串转换为大整数 $p = gmp_init('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16); $a = gmp_init('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16); $b = gmp_init('28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16); $gx = gmp_init('32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1711D7AFB1B8B4E16', 16); $gy = gmp_init('BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0', 16); $n = gmp_init('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123', 16); // 计算Y坐标 $x = gmp_init($x, 16); $alpha = gmp_powm($x, 3, $p); $beta = gmp_add(gmp_mod(gmp_mul($a, $x), $p), $b); $y2 = gmp_mod(gmp_add($alpha, $beta), $p); $y = gmp_powm($y2, gmp_div_q(gmp_add($p, 1), 4), $p); if ($flag == "02") { // 如果压缩标志为 02,则Y坐标为偶数 if (gmp_strval(gmp_mod($y, 2)) != "0") { $y = gmp_sub($p, $y); } return "04" . gmp_strval($x, 16) . str_pad(gmp_strval($y, 16), 64, "0", STR_PAD_LEFT); } if ($flag == "03") { // 如果压缩标志为 03,则Y坐标为奇数 if (gmp_strval(gmp_mod($y, 2)) != "1") { $y = gmp_sub($p, $y); } return "04" . gmp_strval($x, 16) . str_pad(gmp_strval($y, 16), 64, "0", STR_PAD_LEFT); } return null; } public function test_sm3() { $sm3 = $this->getSm3(); $signStr = $sm3->digest($this->getDataStr()); echo 'sm3 sign str: ', $signStr, PHP_EOL; $this->assertNotEmpty($signStr); } /** * 测试 byteArr to string * @return string */ public function test_sm4_iv() { $byteArr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; $iv = call_user_func_array('pack', array_merge(['C*'], $byteArr)); $this->assertTrue(base64_encode($iv) === 'AAAAAAAAAAAAAAAAAAAAAA=='); return base64_encode($iv); } public function test_sm4_encrypt() { try { $iv = $this->test_sm4_iv(); $sm4 = $this->getSm4(); $encryptJsonStr = $sm4->encrypt($this->getDataStr(), 'sm4', base64_decode($iv)); } catch (\Exception $e) { $this->fail('加密异常:' . $e->getMessage()); } $encryptJsonStr = base64_encode(hex2bin($encryptJsonStr)); $this->assertNotEmpty($encryptJsonStr); return $encryptJsonStr; } public function test_sm4_decrypt() { try { $sm4 = $this->getSm4(); $encryptJsonStr = $this->test_sm4_encrypt(); echo "sm4 encrypt str: " . $encryptJsonStr, PHP_EOL; $decryptJsonStr = $sm4->decrypt(bin2hex(base64_decode($encryptJsonStr)), 'sm4', base64_decode($this->test_sm4_iv())); } catch (\Exception $e) { $this->fail('解密异常:' . $e->getMessage()); } echo "sm4 decrypt str: " . $decryptJsonStr, PHP_EOL; echo $decryptJsonStr === $this->getDataStr() ? 'sm4 数据一致' : 'sm4 数据不一致', PHP_EOL; $this->assertNotEmpty($decryptJsonStr); }}