当前位置: 首页 > news >正文

初识Java 9-1 内部类

 

目录

创建内部类

到外部类的链接

使用.this和.new

内部类和向上转型

在方法和作用域中的内部类

匿名内部类

嵌套类

接口中的类

从多嵌套的内部类中访问外部人员


本笔记参考自: 《On Java 中文版》


        定义在另一个类中的类称为内部类。利用内部类,将逻辑上存在关联的类组织在一起,并且可以控制一个类在另一个类中的可见性。

创建内部类

        创建内部类的方式就是把类定义在一个包围它的类中。

public class Parcel_1 {class Contents {private int i = 1;public int value() {return i;}}class Destination {private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}// 内部类的使用看起来和使用其他类没有区别public void ship(String dest) {Contents c = new Contents();Destination d = new Destination(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel_1 p = new Parcel_1();p.ship("一串字符串");}
}

        程序执行,输出:一串字符串

        在上述程序中,ship()方法进行了内部类对象的创建,这与使用普通类并无什么区别。除了这种使用内部类的方式外,在外部类中设置一个方法,用来返回一个指向内部类的引用,这种形式也很常见:

public class Parcel_2 {class Contents {private int i = 1;public int value() {return i;}}class Destination {private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}public Destination toDest(String s) {return new Destination(s);}public Contents toCon() {return new Contents();}public void ship(String dest) {Contents c = toCon();Destination d = toDest(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel_2 p_1 = new Parcel_2();p_1.ship("第二串字符串");Parcel_2 p_2 = new Parcel_2();// 定义指向内部类的引用Parcel_2.Contents c = p_2.toCon();Parcel_2.Destination d = p_2.toDest("这是一个输入");}
}

        在外部类的非静态方法之外的任何地方创建内部类的对象,其对象类型的指定需要遵循以下格式:

OuterClassName.InnerClassName

到外部类的链接

    对于一个负责创建内部类对象的特定外围类对象而言,内部类对象会获得一个隐藏的指向外围类的引用。

        当创建一个内部类时,这个内部类的对象中会隐含一个链接,这个链接用于创建该对象的外围对象。通过这一链接,无需任何条件就可以直接访问外围对象的成员。除此之外,内部类还拥有对外围对象所有元素的访问权

interface Selector {boolean end();Object current();void next();
}public class Sequence {private Object[] items;private int next = 0;public Sequence(int size) {items = new Object[size];}public void add(Object x) {if (next < items.length)items[next++] = x;}private class SequenceSelector implements Selector {private int i = 0;@Overridepublic boolean end() {return i == items.length;}@Overridepublic Object current() {return items[i];}@Overridepublic void next() {if (i < items.length)i++;}}public Selector selector() {return new SequenceSelector();}public static void main(String[] args) {Sequence sequence = new Sequence(10);for (int i = 0; i < 10; i++)sequence.add(Integer.toString(i));Selector selector = sequence.selector();while (!selector.end()) {System.out.print(selector.current() + " ");selector.next();}System.out.println();}
}

        程序执行的结果是:

        通过sequence中的每一个对象,可以使用Selector接口。这就是一个迭代器设计模式的例子。因为Selector是一个接口,其他类可以使用自己的方式去实现这一接口,而其他方法可以通过Selector这个接口去创建更加通用的代码。

        注意,上述程序中,private字段items并不是内部类SequenceSelector的一部分,但是内部类的end()current()next()方法都使用到了该引用。这就是因为内部类可以访问外围对象的所有方法和字段

使用.this和.new

        要在内部类中生成外部类对象的引用,可以使用 外部类的名字+.this

public class DotThis {void f() {System.out.println("这是外部类DoThis的f()");}public class Inner {public DotThis outer() {return DotThis.this;// 若直接使用this,得到的是一个Inner类的引用}}public Inner inner() {return new Inner();}public static void main(String[] args) {DotThis dt = new DotThis();DotThis.Inner dti = dt.inner();dti.outer().f();}
}

        程序执行,输出:这是外部类DoThis的f()

        若要创建内部类的对象,我们还需要使用其外部类的对象。此时会使用到.new语法:

public class DotNew {public class Inner {}public static void main(String[] args) {DotNew dn = new DotNew();DotNew.Inner dni = dn.new Inner();}
}

        通过这种方式,解决了内部类的名字作用域问题。也因此,不需要使用 dn.new DotNew.Inner() 这种更加冗余的方式(不过这种方式也确实不被允许使用)。

        .new的使用例(部分代码重复多次,因此这次放入图片)

        内部类的对象会隐式地连接到用于创建它的外部类对象

    在之后会出现,嵌套类(static修饰的内部类)不需要指向外部类对象的引用。

内部类和向上转型

        内部类在进行向上转型,特别是转型为接口时有其独特的优势。因为内部类(即接口的实现)对外部而言是不可见、不可用的,这会方便隐藏实现:外部类只会获得一个指向基类或接口的引用。

        还是引用之前的例子,假设现在存在两个接口DestinationContents

        正如图中所示的,这两个接口可以让客户程序员进行使用。若客户程序员得到的是一个指向这些接口(指向基类同理)的引用,那么他们就无法从这个引用中得知其确切的类型:

class Parcel_4 {private class PContents implements Contents { // 访问权限为private,无法从外部直接访问private int i = 1;@Overridepublic int value() {return i;}}protected final class PDestination implements Destination {private String label;private PDestination(String whereTo) {label = whereTo;}@Overridepublic String readLabel() {return label;}}public Destination destination(String s) {return new PDestination(s);}public Contents contents() {return new PContents();}
}public class TestParcel {public static void main(String[] args) {Parcel_4 p = new Parcel_4();Contents c = p.contents();Destination d = p.destination("这是第四串字符串");// 注意:不能访问private类// Parcel_4.PContents pc = p.new PContents();}
}

        在Parcel_4中,内部类PContentsprivate的,这表示只有Parcel_4有权对其进行访问。另外,PDestinationprotected的,这表示其的访问权限同样是受限的。

    不能向下转型为访问权限是private的内部类(若无继承关系,也无法向下转型为protected的内部类)。

        private内部类为类的设计者提供了一种方式,这种方式可以完全阻止任何与类型有关的编码依赖,并且可以完全隐藏实现细节。

在方法和作用域中的内部类

        内部类可以在一个方法或是任何一个作用域内创建。有两个理由支持这种做法:

  1. 像上述例子中展示的,需要实现某种接口,以便创建和返回一个引用
  2. 为解决一个复杂问题,在自己的解决方案中创建了一个类用于辅助,但不希望这个类被公开

局部内部类

        修改之前的例子,现在创建一个局部内部类。这种类是一个完整的类,它存在于一个方法的作用域中:

public class Parcel_5 {public Destination destination(String s) {final class PDestination implements Destination {private String label;private PDestination(String whereTo) {label = whereTo;}@Overridepublic String readLabel() {return label;}}return new PDestination(s);}public static void main(String[] args) {Parcel_5 p = new Parcel_5();Destination d = p.destination("这也是一个字符串");}
}

        在上述程序中,PDestination类是destination()方法的一部分,而不是Parcel_5的一部分因此,PDestinationdestination()外是无法访问的。另外,尽管PDestination类在是destination()进行了定义,但即使destination()方法已经返回,PDestination的对象依旧会是合法的

    在同一子目录下的每一个类中,都可以使用类标识符PDestination来命名内部类,这不会产生命名冲突。

        接下来的例子会展示如何如何将内部类嵌入到一个条件判断的作用域中:

public class Parcel_6 {private void internalTracking(Boolean b) {if (b) {class TrackingSlip {private String id;TrackingSlip(String s) {id = s;}String getSlip() {return id;}}TrackingSlip ts = new TrackingSlip("可以使用");String s = ts.getSlip();}// 超出if的作用域,无法使用内部类// TrackingSlip ts = new TrackingSlip("不能使用");}public void track() {internalTracking(true);}public static void main(String[] args) {Parcel_6 p = new Parcel_6();p.track();}
}

        上述程序中,虽然内部类被布置到了if语句中,但这并不表示这个内部类的创建是有条件的,它会与其他代码一起被编译。


匿名内部类

        一个内部类可以是匿名的:这种类通常会与方法返回值的创建结合在一起,在值被返回之前插入一个类的定义。

public class Parcel_7 {// Contents是之前声明的接口,它的方法未被定义public Contents contents() {return new Contents() { // 在进行返回时,插入类的定义private int i = 1;@Overridepublic int value() {return i;}}; // 必要的分号}public static void main(String[] args) {Parcel_7 p = new Parcel_7();Contents c = p.contents();}
}

        这段代码看起来是在准备创建一个Contents的对象,但返回值却被插入了一个类的定义:

return new Contents() { // ...
};

这种语法的意思是“创建一个继承自Contents的匿名类的对象”。在这里,通过new表达式返回的引用会被自动向上转型为一个Contents引用。上述的匿名内部类的语法是以下代码的缩写:

---

        另外,上面展示的匿名内部类中,Contents是用无参构造器创建的。若基类需要的是一个带有参数的构造器,那么:

        首先,这个匿名内部类的基类构造器需要带有参数:

public class Wrapping { // 基类Wrappingprivate int i;public Wrapping(int x) { // 含参构造器i = x;}public int value() {return i;}
}

        尽管Wrapping只是一个带有实现的普通类,但它也是其子类的公共“接口”。

        然后是匿名内部类的创建:

public class Parcel_8 {public Wrapping Wrapping(int x) {return new Wrapping(x) { // 需要将合适的参数传递给基类构造器@Overridepublic int value() {return super.value() * 12;}}; // 这个分号标记表达式的结束,但它刚好包含了这个匿名类}public static void main(String[] args) {Parcel_8 p = new Parcel_8();Wrapping w = p.Wrapping(10);}
}

        上述程序中,return语句末尾的分号标记着表达式的结束,但它并不会标记类体的结束。

---

        若正在构建一个匿名类,并且这个匿名类一定需要使用这个匿名类外部定义的对象,此时,编译器会要求被使用的参数引用使用final修饰,或是“实际上的最终变量”(这种变量在初始化后不再改变,因此被视为final)。

public class Parcel_9 {public Destination destination(final String dest) {return new Destination() {private String label = dest;@Overridepublic String readLabel() {return label;}};}public static void main(String[] args) {Parcel_9 p = new Parcel_9();Destination d = p.destination("这里是Parcel_9");}
}

        在上述程序中,方法destination()的参数可以不用加上final,但通常会把final写上作为提示。

---

        由于匿名类没有名字,所以也不可能有命名的构造器。但如果我们必须对匿名类执行某个类似于构造器的动作,这应该怎么办?借助实例初始化,就可以在效果上为匿名内部类创建一个构造器:

abstract class Base {Base(int i) {System.out.println("这是Base的构造器,i = " + i);}public abstract void f();
}public class AnonymousConstructor {public static Base getBase(int i) {return new Base(i) {{ // 进行实例初始化System.out.println("内部类的实例初始化");}@Overridepublic void f() {System.out.println("匿名类的f()");}};}public static void main(String[] args) {Base base = getBase(10);base.f();}
}

        程序执行的结果是:

        在这里,传入匿名类的变量i并不一定需要是最终变量,尽管i被传入匿名类的基类构造器,但匿名类内部没有直接使用到它。而下方的程序中,由于匿名类使用了参数,所以被使用的参数必须是最终变量:

public class Parcel_10 {public Destination destination(final String dest, final float price) {return new Destination() {private int cost;{// 为每个对象执行实例初始化cost = Math.round(price);if (cost > 100)System.out.println("太贵了吧!");}private String label = dest;@Overridepublic String readLabel() {return label;}};}public static void main(String[] args) {Parcel_10 p = new Parcel_10();Destination d = p.destination("买什么好呢?", 120);}
}

        实例初始化操作中包含了一段if语句,这段if语句不能作为字段初始化的一部分来执行。在效果上,实例初始化部分就是匿名内部类的构造器。但因为我们无法重载实例初始化的部分,所以只能有一个这样的构造器。

    与普通的继承相比,匿名构造器只能扩展一个类,或是实现一个接口,且二者不能兼得。

嵌套类

        将内部类设置为static的,这就变成了嵌套类。这种类不同与普通的匿名类:

  1. 不需要一个外部类对象来创建嵌套类对象;
  2. 无法从嵌套类对象内部访问非static的外部类对象。

        除此之外,嵌套类内部还能存放其他嵌套类,或者static数据及static字段。这些是普通内部类无法做到的:

public class Parcel_11 {// 嵌套类:带有static的内部类private static class ParceContents implements Contents {private int i = 1;@Overridepublic int value() {return i;}}protected static final class ParceDestination implements Destination {private String label;private ParceDestination(String whereTo) {label = whereTo;}@Overridepublic String readLabel() {return label;}// 嵌套类可以包含其他静态元素public static void f() {}static int x = 10;static class AnotherLevel {public static void f() {}static int x = 10;}}public static Destination destination(String s) {return new ParceDestination(s);}public static Contents contents() {return new ParceContents();}public static void main(String[] args) {Contents c = contents();Destination d = destination("不知道写什么,随便写点");}
}

        普通内部类(即非static的)可以使用特殊的this引用创建向外部类对象的连接。而嵌套类没有特殊的this引用,这使得它和static方法类似。

接口中的类

        嵌套类可以是接口的一部分,因为类是static的,所以被嵌套的类只是被放到了这个接口的命名空间里。甚至于,可以在嵌套类中实现包围它的接口:

public interface ClassInterface {void howdy();class Test implements ClassInterface {@Overridepublic void howdy() {System.out.println("在嵌套类内部实现了外围接口的方法");}}public static void main(String[] args) {new Test().howdy();}
}

        程序执行的结果是:

        当需要创建一个接口的所有不同实现的公用代码时,一个嵌套在接口中的类会很有用。

    有时,为了测试一个独立的类,会用到一个单独的main()。这种main()就可以被放入到嵌套类中,在交付产品时将其删去即可。


从多嵌套的内部类中访问外部人员

        一个类被嵌套了多少层都不重要,因为它可以透明地访问包含它的所有类的所有成员:

class MNA {private void f() {}class A {private void g() {}public class B {void h() {g();f();}}}
}public class MultiNestingAcess {public static void main(String[] args) {MNA mna = new MNA();MNA.A mnaa = mna.new A();MNA.A.B mnaab = mnaa.new B();mnaab.h();}
}

        不需要在调用构造器时限定类的名字,因为.new语法会寻找到正确的作用域。

相关文章:

初识Java 9-1 内部类

目录 创建内部类 到外部类的链接 使用.this和.new 内部类和向上转型 在方法和作用域中的内部类 匿名内部类 嵌套类 接口中的类 从多嵌套的内部类中访问外部人员 本笔记参考自&#xff1a; 《On Java 中文版》 定义在另一个类中的类称为内部类。利用内部类&#xff0c;…...

合宙Air724UG LuatOS-Air LVGL API控件-屏幕横屏竖屏切换(Rotation)

屏幕横屏竖屏切换(Rotation) lvgl.disp_set_rotation(nil, lvgl.DISP_ROT_angle) 屏幕横屏竖屏切换显示&#xff0c;core版本号要>3202参数 参数类型释义取值nil无意义nilangle显示角度0,90,270,360 返回值nil 例子 lvgl.init()- -初始化 lvgl.disp_set_rotation(nil,…...

在Unity中,Instantiate函数用于在场景中创建一个新的游戏对象实例

在Unity中&#xff0c;Instantiate函数用于在场景中创建一个新的游戏对象实例。它的语法如下所示&#xff1a; public static Object Instantiate(Object original, Vector3 position, Quaternion rotation); original&#xff1a;要实例化的原始游戏对象。position&#xff1…...

解决 tesserocr报错 Failed to init API, possibly an invalid tessdata path : ./

问题描述 我们在初次使用tesserocr库的时候&#xff0c;可能会报以下错误&#xff1a; RuntimeError: Failed to init API, possibly an invalid tessdata path: ./ 这是因为我们在使用 conda 创建的环境中找不到"tessdata"这个文件夹。 解决办法 这时候把Tessera…...

使用Python CV2融合人脸到新图片--优化版

优化说明 上一版本人脸跟奥特曼图片合并后边界感很严重&#xff0c;于是查找资料发现CV2还有一个泊松函数很适合融合图像。具体代码如下&#xff1a; import numpy as np import cv2usrFilePath "newpic22.jpg" atmFilePath "atm2.jpg" src cv2.imrea…...

Python分享之对象的属性

Python一切皆对象(object)&#xff0c;每个对象都可能有多个属性(attribute)。Python的属性有一套统一的管理方案。 属性的__dict__系统 对象的属性可能来自于其类定义&#xff0c;叫做类属性(class attribute)。类属性可能来自类定义自身&#xff0c;也可能根据类定义继承来的…...

编程参考 - std::exchange和std::swap的区别

这两个功能是C standard library中的Standard template library中的一部分。容易混淆&#xff0c;我们来看下它们的区别。 exchange&#xff1a; 这个函数是一个返回原先值的set函数。 std::exchange is a setter returning the old value. int z std::exchange(x, y); Af…...

Sentinel整合RestTemplate

resttemplate开启sentinel保护配置resttemplate.sentinel.enabledtrue配置sentinel-dashboard地址spring.cloud.sentinel.transport.dashboardlocalhost:8858\ spring.cloud.sentinel.transport.dashboard.port8739 实例化RestTemplate并加入SentinelRestTemplate注解Configura…...

微前端学习(下)

一、课程目标 qiankun 整体运行流程微前端实现方案二、课程大纲 qiankun 整体流程微前端方案实现DIY微前端核心能力1、微前端方案实现 基于 iframe 完全隔离的方案、使用纯的Web Components构建应用EMP基于webpack module federationqiankun、icestark 自己实现JS以及样式隔离2…...

Android Splash实现

1、创建Activity package com.wsy.knowledge.ui.splashimport android.animation.Animator import android.animation.AnimatorListenerAdapter import android.annotation.SuppressLint import android.os.Build import android.os.Looper import android.util.Log import an…...

FPGA projet : VGA

在vga屏幕上显示 &#xff1a; 野火科技 相比于上个工程&#xff0c;只需要修改 vga_pix 模块即可。 注意存储器类型变量的定义&#xff1a;reg 【宽度】<名称>【深度】 赋值 always &#xff08;poseedge vga_clk&#xff09;begin 为每一行赋值&#xff0c;不可位赋…...

JDK8 升级至JDK19

优质博文IT-BLOG-CN 目前部分项目使用JDK8&#xff0c;部分项目使用JDK19因此&#xff0c;环境变量中还是保持JDK8&#xff0c;只需要下载JDK19免安装版本&#xff0c;通过配置IDEA就可以完成本地开发。 一、IDEA 环境设置 【1】通过快捷键CTRL SHIFT ALT S或者File->P…...

Python3.10 IDLE更换主题

前言 自定义主题网上有很多&#xff0c;3.10IDLE的UI有一些新的东西&#xff0c;直接扣过来会有些地方覆盖不到&#xff0c;需要自己测试着添几行配置&#xff0c;以下做个记录。 配置文件路径 Python安装目录下的Lib\idlelib\config-highlight.def。如果是默认安装&#xf…...

C# OpenVino Yolov8 Pose 姿态识别

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp;namespace OpenVino_Yolov8_Demo {public…...

北邮22级信通院数电:Verilog-FPGA(1)实验一“跑通第一个例程” 过程中遇到的常见问题与解决方案汇总(持续更新中)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 问题一&#xff1a;Verilog代码没有跑通 报…...

CSS - 鼠标移入整行高亮显示,适用于会员套餐各参数对比页面(display: table,div 转表格形式)

效果图 可根据基础示例和进阶示例&#xff0c;复制进行改造样式。 如下图所示&#xff0c;本文提供 2 个示例。 基础示例 找个 HTML 页面&#xff0c;一键复制运行。 <body><h1 style"text-align: center;">基础示例</h1><section class"…...

无涯教程-JavaScript - ATAN2函数

描述 The ATAN2 function returns the arctangent, or inverse tangent, of the specified x- and ycoordinates, in radians, between -π/2 and π/2. 语法 ATAN2 (x_num, y_num)争论 Argument描述Required/OptionalX_numThe x-coordinate of the point.RequiredY_numThe…...

Tomcat 下部署 jFinal

1、检查web.xml 配置&#xff0c;在 tomcat 下部署需要检查 web.xml 是否存在&#xff0c;并且要确保配置正确&#xff0c;配置格式如下。 <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns:xsi"http://www.w3.org/2001/XMLSchema-i…...

【Spatial-Temporal Action Localization(二)】论文阅读2017年

文章目录 1. ActionVLAD: Learning spatio-temporal aggregation for action classification [code](https://github.com/rohitgirdhar/ActionVLAD/)[](https://github.com/rohitgirdhar/ActionVLAD/)摘要和结论引言&#xff1a;针对痛点和贡献相关工作模型框架思考不足之处 2.…...

二维码智慧门牌管理系统:数据现势性,满足应用需求的根本保证

文章目录 前言一、项目背景二、数据的现势性三、系统的优势四、应用前景 前言 在当今信息化社会&#xff0c;数据的重要性日益凸显&#xff0c;尤其是数据的现势性&#xff0c;它决定着服务的质量和满足应用需求的能力。近日&#xff0c;一个创新的二维码智慧门牌管理系统项目…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中&#xff0c;云安全与网络安全作为信息安全的两大支柱&#xff0c;常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异&#xff0c;并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全&#xff1a;聚焦于保…...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...