新闻详情

新闻详情

首页 / 资讯中心 / 详情

使用Docker容器化Python应用:从Dockerfile到docker-compose实战

发布时间:2026/9/30 11:58:54来源:尧图网络
使用Docker容器化Python应用:从Dockerfile到docker-compose实战
1. 为什么要用Docker来跑Python应用先说个我自己的经历。几年前我接了一个爬虫项目本地跑得好好的一到服务器上就各种报错。一会儿是Python版本不对一会儿是缺少某个系统依赖库最离谱的一次是对方服务器上已经装了Python 3.6而我的代码用了3.9才有的语法特性又不敢贸然升级生产环境最后折腾了两天才把环境对齐。从那以后我养成了一个习惯凡是要交付的Python应用一律先容器化。Docker本质上解决的就是在我机器上明明是好的这个经典问题。它把代码、运行环境、系统依赖、配置文件全部打包成一个独立的镜像只要目标机器上有Docker引擎就能以完全相同的方式运行起来。对于Python开发者来说这意味着你可以彻底告别环境地狱Python解释器版本、pip依赖库、系统级动态库、时区设置、环境变量全都固化在镜像里换机器、换环境、多人协作行为完全一致。这篇内容围绕使用Docker容器化你的Python应用展开核心覆盖几个方面镜像如何选型、Dockerfile怎么写才规范、基于FastAPI实际演示一个从零到部署的完整案例、常用运行场景如何编排、以及我在真实部署中踩过的坑。无论是刚入门的小白还是已经写了一段时间Python但没怎么接触Docker的开发者这篇内容都能给你一条可以照着做的完整路径。2. 容器化方案的选型逻辑2.1 镜像选型python:slim还是python:alpine很多新手第一步就卡在选镜像上。去Docker Hub搜python出来一大堆tags到底拉哪个先说结论我个人的默认选择是python:3.12-slim注意是slim版本不是完整版也不是alpine。完整版镜像python:3.12大概1GB左右里面塞了完整的构建工具链包括gcc、make、各种头文件。如果你是做CI构建用的这个没问题因为编译需要完整工具链。但是作为运行镜像它太臃肿了构建出来的镜像体积巨大拉取和传输都慢。alpine版本python:3.12-alpine体积确实小只有几十MB但它用的是musl libc而不是glibc。这意味着很多Python包如果没有对应的musl预编译wheel就需要在容器内现场编译而alpine里默认没有gcc你得额外装一堆编译工具反而把Dockerfile搞得很复杂。比如pydantic、numpy、pandas这类带C扩展的包在alpine下安装经常踩坑不是缺这个头文件就是缺那个库。slim版本是基于Debian精简出来的保留了glibc体积比完整版小很多又兼容绝大多数的Python预编译包。对于90%的应用场景它是性价比最高的选择。如果你的应用完全不用第三方包或者只用纯Python的包那alpine也行如果你的应用用了很多C扩展库老老实实用slim别在折腾musl上浪费时间。2.2 构建镜像和运行镜像要分开这是我在接触容器化半年后才真正理解的一个原则构建阶段和运行阶段要分开。Python应用尤其是用了FastAPI、Flask这类框架的通常在构建阶段需要pip install一堆依赖其中很多包需要编译。但如果把编译工具链留在运行镜像里镜像会凭空大出几百MB。业界成熟的方案是多阶段构建multi-stage build。第一阶段用完整版镜像安装依赖第二阶段只拷贝编译好的site-packages和代码进去用slim镜像作为运行基础。一个很典型的例子安装lxml这个库完整编译需要几百MB的依赖工具链但编译完成后的lxml本身只有不到30MB。如果不分离构建阶段光这一个包就让镜像膨胀很多。下面是一个多阶段构建的Dockerfile骨架# 阶段一构建依赖 FROM python:3.12-slim AS builder WORKDIR /app # 复制依赖声明文件 COPY requirements.txt . # 安装依赖到独立目录 RUN pip install --prefix/install -r requirements.txt # 阶段二运行环境 FROM python:3.12-slim WORKDIR /app # 从构建阶段拷贝已安装的依赖 COPY --frombuilder /install /usr/local # 复制应用代码 COPY . . CMD [python, app.py]第一阶段负责安装第二阶段负责运行。最终镜像里只有运行需要的东西构建工具链全部丢弃。这种方案让镜像体积从1GB级别直接降到200MB以内效果立竿见影。2.3 依赖管理requirements.txt还是Poetry围绕Python依赖管理有很多工具pip、pipenv、poetry、uvbar。Dockerfile里到底用哪个没有绝对标准但有一个核心原则构建要可重复。我自己常用的是pip requirements.txt因为最直接、最透明。关键是把依赖固定到精确版本不要用这样的范围指定。原因很简单如果今天是fastapi0.104.0明天pip解析到了0.105.0构建出来的镜像行为可能就变了。容器化的核心价值是可重复依赖版本不固定等于自毁这个价值。如果你用的是PoetryDockerfile里也可以直接用poetry导出依赖poetry export -f requirements.txt --output requirements.txt --without-hashes或者直接用poetry install安装但要注意官方镜像默认没有poetry需要在构建阶段额外安装。我个人的建议是无论你用不用Poetry做本地开发最终提交进Dockerfile的依赖清单都应该是精确版本的、扁平的requirements.txt这样构建步骤最简洁。3. Dockerfile编写的关键细节3.1 写一个规范的Python应用Dockerfile上节给的骨架适合能直接编译运行的极简应用但实际项目通常有更多细节。这里给出一个经过多次实战打磨的模板# 语法声明高版本docker都支持 # syntaxdocker/dockerfile:1.4 # 阶段一安装依赖 FROM python:3.12-slim AS builder # 设置环境变量避免生成.pyc和缓冲输出 ENV PYTHONDONTWRITEBYTECODE1 \ PYTHONUNBUFFERED1 # 设置工作目录 WORKDIR /app # 先单独复制requirements.txt充分利用Docker层缓存 COPY requirements.txt . # 安装依赖到独立前缀 RUN pip install --no-cache-dir --prefix/install -r requirements.txt # 阶段二运行镜像 FROM python:3.12-slim ENV PYTHONDONTWRITEBYTECODE1 \ PYTHONUNBUFFERED1 WORKDIR /app # 复制编译好的依赖 COPY --frombuilder /install /usr/local # 创建非root用户提升安全性 RUN groupadd --system app useradd --system --gid app --home-dir /app --no-create-home app # 复制代码用.dockerignore排除无关文件 COPY . . # 切换到低权限用户 USER app EXPOSE 8000 CMD [uvicorn, main:app, --host, 0.0.0.0, --port, 8000]几个值得注意的细节PYTHONDONTWRITEBYTECODE1不生成__pycache__目录容器内这些缓存文件毫无意义只会增加镜像体积和目录混乱。PYTHONUNBUFFERED1Python输出默认是行缓冲的在容器里如果没有这个环境变量日志可能攒在缓冲区里不及时输出排查问题时非常痛苦。设置后日志能实时打出来。先复制requirements.txt再复制代码这一步利用了Docker的分层缓存机制。只要requirements没变pip install这一层就不会重新执行构建速度大幅提升。我见过很多新手把代码全复制完再装依赖每次改一行代码都要重装所有依赖那体验太酸爽了。创建非root用户运行这是安全基线要求。容器里以root运行时一旦代码有漏洞攻击者直接获得容器内最高权限。切到低权限用户能显著降低风险。虽然容器本身有隔离机制但纵深防御总没错。3.2 .dockerignore90%的人会忽略的细节和.gitignore一样Docker也需要一个.dockerignore文件来排除不需要进入构建上下文的文件。很多人不写这个文件结果把本地的.venv目录、.git目录、__pycache__、测试数据全都打包进了镜像体积爆炸不说还有可能把本地敏感配置比如.env文件里的数据库密码一起打进去。一个Python项目的.dockerignore示例.git __pycache__ *.pyc *.pyo *.pyd .venv venv env .env .idea .vscode *.md tests data注意.env不能进镜像因为里面通常有密钥、数据库地址等敏感信息。容器运行时的配置应该通过docker run的环境变量参数或env_file注入而不是直接打进镜像。4. 实操容器化一个FastAPI应用4.1 项目结构与准备工作理论讲再多不如动手跑一遍。我准备了一个小的FastAPI示例项目目录结构如下fastapi-demo/ ├── .dockerignore ├── .env.example ├── Dockerfile ├── main.py ├── requirements.txt └── docker-compose.yml先看requirements.txtfastapi0.104.1 uvicorn[standard]0.24.0 pydantic2.5.0再看main.py这是一个非常简单的API返回一个JSONfrom fastapi import FastAPI from pydantic import BaseModel, Field app FastAPI( title容器化Demo API, version0.1.0 ) class Item(BaseModel): name: str Field(..., min_length1, max_length50) quantity: int Field(0, ge0, le100) app.get(/) def read_root(): return {message: Hello from Dockerized Python!} app.get(/healthz) def health_check(): return {status: ok} app.post(/items) def create_item(item: Item): return {name: item.name, quantity: item.quantity, note: created}这个例子虽然小但已经包含了路由、请求体模型校验、健康检查接口足够演示容器化的完整流程。4.2 用Docker构建并启动容器在项目目录下执行构建命令docker build -t fastapi-demo:0.1 .-t指定镜像名称和标签fastapi-demo是镜像名0.1是版本标签。构建过程会先走builder阶段安装依赖再走运行阶段组装最终镜像。第一次构建会拉取基础镜像并下载依赖通常需要几分钟耐心等待即可。构建完成后运行docker run -d --name demo-api -p 8000:8000 fastapi-demo:0.1-d后台运行--name demo-api给容器起名字-p 8000:8000把宿主机的8000端口映射到容器的8000端口此时打开浏览器访问http://localhost:8000/就能看到API返回的JSON了。可以用Docker命令查看容器运行状态和日志docker ps docker logs -f demo-api注意一个问题如果你的应用对外提供服务docker run的时候一定要显式指定端口映射-p否则宿主机上无法访问。我见过不止一个同事在服务器上跑了容器却忘记映射端口在机器上curl半天不通最后发现自己访问的是宿主机的端口而容器根本没监听。4.3 验证镜像体积和分层结构构建完成后建议看一下镜像的大小和分层docker images fastapi-demo docker history fastapi-demo:0.1docker history能清晰地看到每个层的构建记录和大小的变化。如果你是多阶段构建对比一下单个阶段构建的镜像体积差异会非常震撼。这个习惯有助于你持续理解镜像膨胀的根源在哪里也是排查为什么我的镜像这么大的第一步。5. 用docker-compose编排完整应用5.1 为什么需要docker-compose单个Python容器自己跑没什么问题但真实应用几乎不可能只有一个服务。比如你的FastAPI应用需要接一个Redis、一个PostgreSQL如果都用docker run管理你得维护一堆命令还要自己建网络非常痛苦。docker-compose就是干这个的——定义一组容器统一管理网络、卷、环境变量和启动顺序。5.2 一个包含Redis的compose配置把docker-compose.yml塞进上面的示例项目内容如下services: app: build: . ports: - 8000:8000 environment: - REDIS_HOSTredis - REDIS_PORT6379 depends_on: - redis restart: unless-stopped redis: image: redis:7.2-alpine ports: - 6379:6379 volumes: - redis_data:/data volumes: redis_data:重点解释几个字段build: .表示使用当前目录下的Dockerfile构建镜像。environment里设了REDIS_HOSTredis这里的redis是compose服务名它同时是容器在compose内部网络中的DNS名称。应用连接Redis时不需要访问localhost而是直接连redis:6379。depends_on声明启动顺序Redis先启动应用再启动。注意它只保证容器启动的顺序不保证Redis内部已经完全就绪。如果应用启动时Redis还没准备好可能会连接失败。更严谨的做法是让应用自带重连逻辑或者在healthcheck中对Redis做健康检查后再启动app。restart: unless-stopped指定自动重启策略尤其在服务崩溃或机器重启时容器能自动拉起对生产部署非常实用。volumes用持久化卷保存Redis数据避免容器销毁后数据丢失。启动命令很简单docker compose up -d查看所有服务状态docker compose ps查看日志docker compose logs -f app停止并移除容器docker compose down如果在使用较老版本的Dockerdocker compose命令是docker-compose中间有横杠。新版Docker Desktop和Linux上的Docker Engine 20.10都已经内置了docker compose插件直接用无横杠版本就行。5.3 开发场景下用compose挂载代码热更新docker-compose不仅用于生产部署还非常适合本地开发。有一个值得单独说透的技巧在开发模式下把代码目录挂载进容器实现修改代码即时生效而不需要每次改动都重新构建镜像。开发用的compose文件比如docker-compose.dev.yml可以额外加一个volumes配置services: app: build: . ports: - 8000:8000 volumes: - .:/app command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload这段配置把宿主机当前目录挂载到容器的/app配合--reload参数FastAPI的代码修改后能自动加载。这样你就得到了和本地直接跑Python几乎一致的开发体验但环境仍然是容器化的依赖保持和线上完全一致。一个常见的坑是如果本机是Mac代码里如果有编译过的C扩展或者使用了某些平台相关的东西挂载目录进去后可能因为宿主机的架构和容器不一致而出问题。纯Python项目影响不大但遇到pydantic这类有编译产物的包时一般建议保留镜像内安装的版本不要简单粗暴地整个目录覆盖。折中方案是把当前目录挂载到另一个位置比如./src挂载到/workspace只把源码放进去site-packages仍在原位置不互相干扰。6. 网络、健康检查与生产化部署6.1 容器网络模式这关系到你的应用能不能被外面访问Docker容器默认有隔离的网络命名空间容器的IP和宿主机IP是不同的。当你用docker run -p 8000:8000时Docker会在宿主机上开一个端口把流量转发到容器的8000端口。几个关键概念bridge网络默认容器有独立IP通过端口映射对外服务。最常用生产环境也推荐。host网络容器直接复用宿主机的网络栈端口不需要映射应用监听什么端口就对外提供什么端口。性能好但隔离性弱一般只用在对网络性能极其敏感的服务上或者是在容器里需要主动向宿主机网络发起广播的场景。none网络容器没有网络极少使用。在使用docker-compose时同一文件下的服务会自动加入同一个自定义bridge网络互相之间可以用服务名做DNS解析。这是容器间通信的最佳实践比通过宿主机IP通信要可靠得多。有一个典型的网络排查场景容器内运行Python代码访问外网比如爬虫或调用第三方API遇到Connection timed out。如果宿主机能正常上外网而容器不行最常见的原因是容器所处网络的出站规则存在问题。新版本Docker的bridge网络通常在CNI、iptables方面自动配置好了出站访问一般不需要额外处理。但如果你的服务器有复杂的防火墙规则需要注意Docker的iptables规则和系统防火墙是否冲突。实测经验在云服务器上遇到过docker run的容器无法访问宿主机上的MySQL而宿主机直接访问MySQL一切正常。原因就是容器内连接到了127.0.0.1而容器和宿主机是两个网络空间。解决方式很直接让容器连接宿主机的内网IP比如192.168.x.x或者让MySQL跑在另一个容器里通过compose网络进行服务名解析。6.2 给容器加健康检查生产环境里服务挂没挂不能靠猜。容器可以提供健康检查让Docker和编排系统知道服务是否真正可用。Dockerfile中可以这样声明HEALTHCHECK --interval30s --timeout3s --start-period5s --retries3 \ CMD python -c import urllib.request; urllib.request.urlopen(http://localhost:8000/healthz) || exit 1含义是每30秒执行一次健康检查命令如果返回非零就认为当前不健康连续3次不健康则容器状态变为unhealthy。运行容器时docker ps的STATUS列会显示(healthy)或(unhealthy)。在docker-compose中也可以配置对应的healthcheck思路一致。尤其当应用依赖数据库、缓存等外部服务时healthcheck不仅仅是给监控看的也是编排系统判断什么时候可以开始启动后续容器的依据。compose里可以让其他服务depends_on时带上condition: service_healthy这样只有当依赖服务健康后本服务才会启动。6.3 日志和监控容器化部署后日志处理方式和直接跑在裸机上完全不同。容器一旦被销毁再重建原本写在容器里的日志就没了所以必须把日志输出到stdout/stderr由Docker统一收集再通过日志采集工具转存到文件或日志平台。Python应用尤其要注意不要写文件日志直接打印到标准输出。这正好呼应了前面在Dockerfile里设置的PYTHONUNBUFFERED1。生产环境通常会搭配以下几个组件容器日志采集如docker log driver配置为json-file、gelf或fluentd。指标采集将应用的Prometheus指标端点暴露出来配合prometheus定期抓取。可视化面板Grafana展示CPU、内存、请求延迟等关键指标。这里不展开每个组件的配置细节但需要明确结论容器化的应用必须设计成无状态的、十二因素应用日志和状态都不要扔在本地而是打入标准输出和外部存储。7. 真实踩坑记录与排查方法7.1 依赖安装失败这是最常见的问题。很多Python包在安装时会编译C扩展容器内如果没有对应的系统库就会报错。比如psycopg2需要libpq-devPillow需要libjpeg-dev等。解决办法有两种一是在Dockerfile中先安装系统依赖RUN apt-get update apt-get install -y --no-install-recommends \ libpq-dev \ gcc \ rm -rf /var/lib/apt/lists/*注意装完要顺手清理apt缓存否则镜像会额外增加几十乃至上百MB。二是优先使用预编译的wheel包。PyPI上大部分包都发布了Linux x86_64平台的wheelpip会优先下载wheel而非走源码编译。如果发现pip在编译源码往往是该包没有对应平台的wheel此时要么换镜像版本要么手动补系统依赖。7.2 容器内时区不对导致的时间错乱Python应用打印日志、写入数据库时间默认是UTC和北京时间差8个小时。这是容器环境里非常隐蔽的一个问题。解决办法是在Dockerfile中设置时区ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezoneDebian系镜像需要先安装tzdata包否则/usr/share/zoneinfo目录可能不存在。也可以运行时通过环境变量注入docker run -e TZAsia/Shanghai ...7.3 容器中途退出导致的数据丢失很多人第一次跑MySQL容器都会因为忘记挂载卷而导致数据没了。Python应用如果往容器内写了文件比如使用了SQLite数据库那必须有卷来持久化。否则容器重建数据灰飞烟灭。判断规则很简单凡是需要持久化的数据都必须落到卷volume或绑定挂载bind mount上容器文件系统本身是临时的。7.4 Docker Desktop启动失败与网络故障在Windows环境用Docker Desktop时遇到过启动报错提示Virtualization support not detected。这个通常有两种原因BIOS中没有开启硬件虚拟化或者Windows的Hyper-V功能未启用。排查步骤是先确认BIOS里VT-x已开启再在启用或关闭Windows功能里勾选虚拟机平台和Hyper-V。如果是老版本Win10可能要去设置里打开适用于Linux的Windows子系统。另一类常见报错是Failed to connect to the Docker API at npipe:////./pipe/docker_desktop_linux。这种情况多半是Docker Desktop启动了一半就挂了或者WSL 2内核版本太低。试着在PowerShell里执行wsl --update更新WSL内核然后重启Docker Desktop。Linux环境下Docker网络不通的问题前面已经提过一些。如果容器之间能互相连通但宿主机访问不到容器优先检查端口映射是否正确如果容器能启动但网络完全不通检查iptables -L -n里的规则是否被外部防火墙初始化脚本清掉了重启Docker服务或者整个机器一般能恢复默认规则。7.5 镜像大小失控镜像越大拉取越慢、磁盘占用越高、安全暴露面越大。排查镜像里的空间黑洞最直接的办法是docker history --no-trunc 镜像名或者运行时进入容器看看docker run --rm -it 镜像名 /bin/bash du -sh /* # 逐级找大目录常见的镜像膨胀原因包括apt-get安装了包但没有清理缓存、pip没有加--no-cache-dir导致保留了下载缓存、构建阶段把整个虚拟环境复制进去了、代码里带了无用的数据文件等。针对每一项做优化镜像体积能减少30%到一半以上。7.6 常见问题速查表问题现象可能原因排查/解决方向镜像构建时pip install报编译错误缺少系统依赖库在Dockerfile中安装gcc及对应库或更换镜像版本容器启动后立刻退出应用启动异常或命令路径不对docker logs查看输出先本地跑一遍应用确认启动方式宿主机无法访问容器服务端口未映射检查docker run -p或compose的ports配置容器网络不通防火墙/iptables规则冲突检查系统防火墙与Docker iptables隔离策略尝试重启Docker容器时间与实际时间差8h默认UTC时区设置环境变量TZAsia/ShanghaiDocker Desktop报Virtualization错误BIOS未开启虚拟化或Hyper-V未启用开启BIOS虚拟化启用Windows虚拟机平台Docker API连接失败Docker引擎未就绪/WSL损坏更新WSL内核重启Docker服务容器内数据丢失未挂载卷使用-v/--mount或compose的volumes持久化镜像体积太大构建过程中有大量缓存/工具残留多阶段构建清理apt和pip缓存精简复制内容8. 个人经验总结容器化Python应用这件事技术上并不难真正难的是建立一套正确的思维方式。Dockerfile写得多漂亮是一方面更关键的是要把镜像当成不可变发布单元来看待一旦构建完成它就是一个确定的环境快照任何修改都应该通过重新构建完成而不是跑到容器里手动改来改去。这个观点我在团队里反复强调因为它直接决定了部署的稳定性和可追溯性。另外一个值得长期坚持的好习惯是把.dockerignore、多阶段构建、非root用户、健康检查这些看起来不是必须的细节从一开始就做好。它们不会让功能跑得更快但会在交付稳定性、安全性、可维护性上持续发挥作用。等你在生产环境里因为少写一个.dockerignore而把测试数据库的IP打进了镜像或者因为忘记健康检查导致监控平台一直告警就会明白这些细节的价值。如果你现在正准备把一个Python项目容器化可以按这个顺序一步步来先写好Dockerfile本地构建跑通然后用docker-compose把依赖服务编排起来接着把日志和持久化处理好最后再补上健康检查和安全加固。走完这条路你的应用就已经具备了一个可以上线的容器化基础。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Go语言for-range与switch的坑:break为何跳不出循环? 2026/9/30 12:54:20

Go语言for-range与switch的坑:break为何跳不出循环?

我先说个事儿。上个月给团队做代码评审,一位写了三年Go的同事提交了一段消息处理逻辑:for-range 遍历事件列表,switch 按类型分发,遇到"stop"类型就break,日志也打了"收到停止信号"。结果线上生产…

阅读更多 →
嵌入式驱动开发为何值得用C++?实战经验与避坑指南 2026/9/30 12:54:20

嵌入式驱动开发为何值得用C++?实战经验与避坑指南

干了十几年嵌入式,从最早的8位单片机一路做到多核应用处理器,被新人问得最多的一个问题就是"驱动开发到底在开发什么?是不是要用C?"。说实话,早些年嵌入式圈子里C语言几乎是驱动层的绝对霸主,寄存…

阅读更多 →
现代C++设计模式实战:避开常见坑,用智能指针与RAII写出优雅代码 2026/9/30 12:54:20

现代C++设计模式实战:避开常见坑,用智能指针与RAII写出优雅代码

如果让我选一个C项目里最容易被高估、也最容易被低估的技术点,我会选设计模式。说它被高估,是因为很多人把23种模式背得滚瓜烂熟,一到写代码仍然只会复制粘贴;说它被低估,是因为真正用得好的设计模式,能直接…

阅读更多 →
宽带故障排查全攻略:FTTH/FTTB灯态判读、光衰测试与网速慢定位 2026/9/30 12:54:20

宽带故障排查全攻略:FTTH/FTTB灯态判读、光衰测试与网速慢定位

简介:这份PDF资料聚焦宽带网络运维中的常见故障排查,面向一线装维人员、网络运维初学者及需要处理家庭宽带问题的技术人员。内容围绕FTTH、FTTB、网速慢、用户路由器故障四类典型场景,按步骤拆解排查逻辑,从光猫电源灯、LOS灯、PO…

阅读更多 →
个人微信API二次开发:大模型 RAG 与 Agent 智能助手落地架构 2026/9/30 12:54:20

个人微信API二次开发:大模型 RAG 与 Agent 智能助手落地架构

官方文档:GeWe API - GeWe API|微信 API 开发文档 一、业务痛点与技术背景 私域场景要的不是「能聊天的 Bot」,而是可控、可审计、可降级的 AI 助手: 会话粘性映射 故障转移与健康摘除 容量规划与演练剧本 多账号舰队调度 G…

阅读更多 →
Java操作符全解析:从分类优先级到进制与补码 2026/9/30 12:54:13

Java操作符全解析:从分类优先级到进制与补码

很多人学Java&#xff0c;第一天写Hello World还兴高采烈&#xff0c;第二天碰到一堆 、 & 、 << 、 >>> 就开始犯晕&#xff1b;学到循环和数组时&#xff0c;又栽在 i 和 i 上&#xff1b;等到看源码或者刷面试题&#xff0c;碰到 Integer.…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉