PHP Tutorial 01 - Hello, world!

安装 Chrome#

我推荐装完新系统之后第一时间安装 Chrome 浏览器

使用 Chrome 浏览器进行开发,可以为您避免许多麻烦,并且有很多优点。

我使用 Chrome 64-bit 这个非官方网站下载离线安装包。(下载到的安装包是官方的,这个网站只是跳转到官方网站的链接,使用这个网站的原因只是因为官方不直接提供离线安装包的链接)

Chrome 是全自动安装的,下载完安装包之后,直接运行即可自动安装完成。

Chrome 默认安装位置是 C:\Users\用户名\AppData\Local\Google\Chrome\Application

打开 Chrome 之后,安装操作说明将 Chrome 设置为默认浏览器。

安装 PHP#

PHP 官方网站 下载最新版 PHP,因为我们使用的是 Windows,所以去 PHP For Windows 下载,本地开发通常使用最新版的 VC15 x64 Non Thread Safe 版本。

下载页面旁边的 “Which version do I choose?” 介绍了如何选择版本,如果您有兴趣可以看一看。

PHP 是免安装的,下载之后解压到某个文件夹即可使用。我是将 PHP 解压到了 C:\Program1\php

如果你想知道为什么我选择这个路径,解释如下:

对于需要安装的程序,安装程序默认会安装到 C:\Program FilesC:\Program Files (x86),那么对于免安装程序,我也统一放在一个位置,因为 C:\Program Files 这个文件夹是需要管理员权限访问的,所以我们使用这个文件夹将带来诸多不便,所以就另外建一个文件夹,最开始我使用的是 C:\Program,但是重启之后系统提示这样命名可能会存在问题,将其改成了 C:\Program1,我也就这样沿用下来了。

环境变量 PATH#

之后可能经常要跟命令行打交道,设置 PATH 可以让你从任何位置打开命令行即均轻松地访问 php.exe

Win + S 打开 Cortana,输入 环境变量,即可找到 编辑系统环境变量 的选项。

选择 环境变量,在下方的 系统变量中找到PATH,双击这一行,弹出修改框,然后点击添加,在最后一行输入 C:\Program1\php 即可。

这时使用 Win + R 打开运行窗口,输入 cmd 回车。在弹出的命令行窗口中输入

1
php --version

成功显示版本号了,证明你可以从任何位置访问 php 了。

Win + S 中的 S 表示 Search

Win + R 中的 R 表示 Run

VCRUNTIME#

由于找不到 VCRUNTIME140.dll 无法继续执行代码,重新安装程序可能会解决此问题。

PHP For Windows 可以看到左侧有说明

The VC15 builds require to have the Visual C++ Redistributable for Visual Studio 2017 x64 or x86 installed

VC15 构建的程序需要您已安装 Visual C++ Redistributable for Visual Studio 2017 x64 或 x86。

点击链接,下载相应的版本,直接安装即可。

安装之后应该就可以正常执行 php --version 了。

安装 Notepad++#

Notepad++ 官方网站 下载最新版的 Notepad++。通常使用最新版的 Notepad++ Installer 64-bit x64 即可。

正常安装即可。如果要不想使用英文界面,勾选上 Localization 即可安装中文界面。

使用 Notepad++ 可以避免使用 Windows 自带的记事本的很多问题,最常见的问题就是某些情况下记事本会添加 BOM 头,而通常程序代码并不希望遇到 BOM 头。

Hello World#

首先需要“显示文件扩展名”

打开 我的电脑 (即文件资源管理器),点击菜单里的 查看,勾选 文件扩展名

显示文件扩展名 对开发有很多好处,最显而易见的好处就是,不会弄混相同文件名、不同扩展名的文件。

除了开发以外,还可以避免一些简单的病毒,例如某些病毒会把图标设置成文件夹的图形,如果你显示了扩展名,看到一个文件夹的扩展名是 .exe,就会发现异常了。

在桌面右键,新建,文本文档,会出现一个文件 新建文本文档.txt,重命名为 index.php

右键使用 Notepad++ 打开

原样输出#

直接写入

1
Hello, world!

保存。

然后右键 Notepad++ 的文件选项卡,选择 打开所在文件夹(命令行)

输入

1
php index.php

回车。

你可以看到文本文档内容原样输出出来了。

你的第一个 php 命令行程序已经完成了。

echo 语句#

为了体现区别,把内容改成

1
2
<?php
echo 'Hello, world! 2';

注意这里用的是单引号 ',键盘上 L 右边的第 2 个键

回到命令行窗口,按 键,可以看到上一条 php index.php 命令直接自动输入好了,然后回车。

可以看到 echo 语句的引号内的内容。

你的第一个包含 php 代码的 php 命令行程序已经完成了。

PHP Development Server#

同样为了体现区别,把内容改成

1
2
<?php
echo 'Hello, world! 3';

回到命令行窗口,执行

1
php -S 127.0.0.1:8000

注意:S 是大写的,其含义是 Server

打开浏览器,访问 http://127.0.0.1:8000/

现在你使用 php 开发的第一个网页应用也完成了。

PHP Tutorial 00 - Why PHP?

你是怎么得知 PHP 的?#

这个问题需要你自己来回答。

  • 从同学那听说的。
  • 选课时候发现的。
  • 听说做互联网开发赚钱,上网搜索到的。

搜索互联网开发,与 PHP 并列的可能就是 Java 了。你可能会很纠结,请继续往下看。

  • 或者是《年薪百万程序员教你两个小时写出淘宝网站》←_←

不管你了解的途径如何,有一个梗你需要了解一下。

PHP 是最好的语言

当然,这是一个梗,你需要用审视的眼光去看。

PHP 能做什么?#

既然你知道了 PHP,那么你知道 PHP 是做什么的吗?

我们来看看 PHP 的版本更新历史

  • PHP 5 是 2004 年诞生的。
  • PHP 5.3 是 2009 年。
  • PHP 5.4 是 2012 年。

我所了解的,PHP 5.3 之后这门语言基本上没发生巨大的变化,即使到现在的 PHP 7 引入了很多新语言特性,PHP 的目标定位还是没有明显的变化。

所以,你需要知道的是,PHP 的定位仍然是网页(2004 ~ 2009 年那个时候的网页),而不是目前某些实时性特别强的游戏或者直播等等。

不要认为这个定位太古老了,我们现在用的绝大多数网站 PHP 都可以胜任。如果你想从零开始开发一个同样功能的网站,使用 PHP 往往会比其他语言开发时间短。

使用 PHP 的网站#

  • WordPress:WordPress 是 PHP 最有名的开源项目,通常是作为个人博客使用,可以通过一些免费的插件把它改成论坛。

一些关于不同版本 PHP 的 Benchmark 通常会选用 WordPress 作为测试项目。

Benchmark:标准检查程序(其实就是跑分)

PHP?还是其他语言?#

你会在网上看到一些关于 PHP、Java、Python、ASP.NET、Ruby、Node.js、Golang 等的比较

PHP 优点#

  • PHP 是专门为网页开发设计的语言,PHP 对于网页开发比其他语言方便。
  • PHP 是弱类型解释型语言,开发效率高。
  • PHP 有广大的开源社区,有很多现成的可以免费使用的产品。
  • PHP 的代码修改即可生效,可以轻松地基于已有项目修改。
  • PHP 安装简单,入门快,开发容易,部署方便。

PHP 缺点#

  • PHP 的执行效率不如其他语言。不应该使用 PHP 执行大量计算任务。
  • PHP 不支持多线程,一次请求就是按顺序执行代码直到完成。其他语言可以利用多线程做一些其他的事。
  • PHP 高并发性不如其他语言。不适用于突然涌入大量用户的场景,比如双十一抢购,比如抢票、抢课之类的。
  • PHP 不支持长连接,想做实时聊天需要不断地每隔几秒发送请求。
  • PHP 对流的支持不好,不可以做直播。

请注意这些缺点,他们本来就不符合 PHP 的定位,所以这算是强人所难。使用 PHP 的时候要扬长避短,干不了的事可以使用其他语言开发,多语言合作一下就可以了,不用非得井水不犯河水。

例如#

如果你仅仅想建一个论坛或者博客,PHP 可行。用 WordPress,非常成熟,大量插件,不必自己开发。一个普通的云主机几百并发没什么问题。

如果你是要为某公司做一个主页,PHP 可行。你可以基于 WordPress 自己开发或修改一套主题,PHP 易于开发、易于修改的特性就体现出来了。

如果你是要为某公司做一个管理系统之类的,这是 PHP 常见的用途。

如果你想单靠 PHP 语言做一个大容量高并发的网页,至少目前不太可能了。不过你可以把网页部分交给 PHP,同时使用一些缓存技术。而像抢课、抢票这类并发要求高的 API 交给 Java。这样既有开发效率,又满足了要求,岂不美哉。

你为什么要学习 PHP?#

为了赚钱#

如果你想学 PHP,从而去大公司找工作,我并不推荐。大公司通常不会以 PHP 作为主要技术栈。

但也并不是用不到 PHP,那种短期的活动,紧急上线,不久后就下线,用 PHP 开发很方便。由于 PHP 易于学习,通常了解其他语言的话都可以很快入门,所以不会专门招收 PHP 程序员。

小公司会使用 PHP,因为 PHP 本身的特性,开发网站十分方便,遇到改需求的情况也比较容易应对。

另外用 PHP 写一些 WordPress、DiscuzX 插件或主题来赚一些小钱也是可行的。

为了兴趣#

自己用 PHP 写一些小工具处理互联网相关的东西真的很方便。

人生苦短,我用 Python

Life Is Short, Use Python.

比如计算一段字符串的 MD5,PHP 比 Python 要容易的多。PHP 把所有常用的函数全部都暴露在全局中,需要使用随时可以调用,有些小工具用 PHP 写要比 Python 容易。

其实这就是我支持“PHP 是最好的语言”的一个原因

如何学习 PHP?#

  • 我是零基础小白:只要你是真的想学,没问题。
  • 我有编程基础
    • 我只会类似谭浩强那套 C 语言:虽然被谭坑得挺惨,但是那些东西没有白学。
    • 我有网络开发基础:学过其他语言的网络编程,PHP 简直再简单不过了。

我建议带着目的去学习,就是

我有一个想法,我要做出一个这样的东西。为了达成这个目标,我需要什么工具,然后去学习这些工具。结果发现又需要使用另外的工具,再去学习另外的工具。

注意:每学一个东西都要及时做出反馈,就是我学了这个东西就可把我的项目往前推进一步,而不是全部学完之后才回头去搞最初那个目标。

我是 2015 年开始接触 PHP,我有一些编程基础,大约学习 1 个月就可以写出一个能用的东西了。

我觉得国内没有什么比较好的视频教程,所以自己尝试做了一份,虽然不能保证自己的教程如何,但是就当做自己的总结吧,同时方便后来的同学。

TL;DR#

TL;DR “Too long; didn’t read.”

  1. 想去大公司,请不要专注于 PHP。其他,学 PHP 完全没问题。
  2. PHP 安装简单,入门快,开发容易,部署方便。
  3. PHP 的定位是传统网页开发
  4. PHP 有局限性,某些时候需要多语言合作。
  5. PHP 可以写一些小工具

Go Simple HTTP Server Demo

Build an http server in go is really easy. By using net/http, we can build a http server easily.

Simple HTTP Server#

main.goview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Hello, world!"))
}

func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8000", nil)
}

Run go run main.go

Visit http://127.0.0.1:8000/

Hello, world!

The net/http package provided by golang is really powerful, so sometimes you even don’t need any web framework for a small project.

Server Mux#

mux.goview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Hello, world!"))
}

func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", hello)
http.ListenAndServe(":8000", mux)
}

Run go run mux.go

We use a new mux as http handler.

Why Should We Use Mux#

If we don’t use mux, http.HandleFunc makes http package messy. It polluted the global namespace. It’s not so good when we are developing a package. We shouldn’t modify other packages. We can only do things with a new instance.

The first example use nil handler. When nil provided, http.DefaultServeMux is used. All routes are registered in default mux. Any other package may be affected by this code.

When we use a new mux, routes are save in this mux. We can create multiple server listening different ports. Run them with goroutines.

1
2
3
go http.ListenAndServe(":8000", mux)
go http.ListenAndServe(":8001", mux1)
// wait

My JetBrains IDEA Settings

  • Keymap
    • Scheme: Visual Studio copy
      • Editor Actions
        • Add or Remove Caret: Alt + Button1 Click
      • Main menu
        • Active Editor
          • Use Soft Wraps: Alt + Z
        • Window
          • Editor Tabs
            • Split Vertically: Ctrl + \
            • Move Right: Alt + \
      • Tool Windows
        • Database: Alt + D
        • Terminal: Alt + Grave accent
      • Other
        • New…: Ctrl + N
        • Show in Explorer: Alt + Shift + R
  • Editor
    • General
      • Mouse
        • Change font size (Zoom) with Ctrl + Mouse Wheel
    • Font
      • Font: Consolas
      • Size: 16
      • Line spacing: 1.7
    • Code Style
      • Hard wrap at 500 columns

调试分析 BiliBili 直播 WebSocket 协议

本文主要讲如何使用 Chrome DevTools,Bilibili 的直播间 WebSocket 协议只是作为一个案例,互联网上有很多现成的博客已经讲了协议内容。

准备工具#

最新版的 Chrome 浏览器

我的当前版本是 Chrome 70

定位代码#

  1. 打开直播间,打开 F12 开发者工具,刷新页面。

  2. 切换到 Network 选项卡。

  3. 打开筛选,筛选类型为 WS(即 WebSocket)的请求。

Network 选项卡,筛选 WS

  1. 鼠标悬停在代码位置的上方,可以看到请求的发出位置,以及它的调用栈。

    通常类不会是最内层(也就是第一个),往下找几个,估计一个大概的位置。

    如果估计不好的话,最麻烦的方法也就是一个一个看了。

请求调用堆栈

这种顺序逻辑的代码一般都是同步执行的,通常不会越过 (async)。这工作量瞬间就减小到只剩 10 行了。

一般特别要注意 (anonymous) 前后,可能就是跨类调用的部分。这一条的准确性不好,用处通常不大。

如果嫌麻烦的话,可以用稍微高级的方法,继续看下一部分。

下断点反查调用栈#

刚才那个太麻烦了,要用点简单的方法。

我们直接点进第一条,也就是请求真正触发的位置。

调用栈的最顶层就是整个调用的最内层、最深的位置。

点击左下角的 {},格式化一下代码。

然后在这行下一个断点。

刷新页面。

当这个断点断下时,右侧有当前的调用栈,也能看到当前层中的局部变量。可以点击某一层,将代码切换到这一层的调用位置,同时把上下文切换到对应的位置,可以看到对应层的局部变量。左侧的代码中也会内联显示对应行内有关的变量的信息,方便调试。

调用堆栈调试

这个在 Source 选项卡中的栈和之前在 Network 选项卡中的栈是一样的,我们只需要依次往下找,看看哪两层栈之间的上下文变化过于剧烈,就可以找到这个模块大概的层次了。

因为这个 js 文件是通过 webpack 编译出来的,所以才会有模块这种东西。某些其他的 JavaScript 的前端软件,原本就是可以直接用在浏览器,模块也都写在全局变量中的,情况可能会有些不一样。

找到具体位置#

因为代码的大致位置都找到了,在附近找一找 sendonmessageon('message', ...) 就行了。就是通过 Ctrl + F 查找。

可以通过下断点逆推堆栈了解程序执行过程,也可以直接分析代码。

js 代码经过 Uglify 之后,变量名会变成英文字母表,但通常 uglify 只是单纯地替换变量名,不会把不同的变量名替换成相同的变量名,所以静态分析还是可以分析明白的。

分析算法#

这个部分其实没什么,就可以就是单步运行就好了。

A Simple Vue Project Build With Webpack

Create Project#

1
2
mkdir vue-webpack-example
cd vue-webpack-example/

Initialize Project#

1
npm init
1
2
3
4
5
6
7
8
9
package name: (vue-webpack-example)
version: (1.0.0)
description: A Simple Vue Project Build With Webpack
entry point: (index.js) src/index.js
test command:
git repository: https://github.com/ganlvtech/vue-webpack-example
keywords: vue webpack example demo
author: Ganlv
license: (ISC) MIT

Requirements#

1
2
npm install vue --save
npm install webpack vue-template-compiler vue-loader --save-dev

Files#

  • /
    • index.html
    • webpack.config.js
    • src/
      • index.js
      • App.vue
      • components/
        • MyComponent.vue
index.htmlview raw
1
2
<div id="app"></div>
<script src="/dist/bundle.js"></script>
src/index.jsview raw
1
2
3
4
5
6
7
8
9
import App from './App.vue';
import Vue from 'vue';

const vm = new Vue({
el: '#app',
render(h) {
return h(App);
},
})
src/App.vueview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div id="app">
<h1>Hello, world!</h1>
<my-component />
<my-component msg="A message" />
</div>
</template>

<script>
import MyComponent from "./components/MyComponent.vue";

export default {
components: {
"my-component": MyComponent
}
};
</script>
src/components/MyComponent.vueview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<h2>My component</h2>
<p>{{ msg }}</p>
</div>
</template>

<script>
export default {
props: {
msg: {
default: "default message",
type: String
}
}
};
</script>
webpack.config.jsview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader'
}]
},
plugins: [
new VueLoaderPlugin()
]
};

Webpack build#

1
webpack

Visit the website#

1
2
npm install http-server -g
http-server

Visit http://127.0.0.1:8080/

Software on My Windows

The list item that don’t has a link is not freeware, opensource software, or don’t provide free license for student.

Basic#

For Developers#

Small Tools#

Chrome Extensions#

Media#

  • OBS
  • ffmpeg
  • Adobe Photoshop
  • Adobe Premiere Pro
  • Adobe Audition

Entertainment#

Rarely Used#

虚拟机安装 Ubuntu Server 18.04

安装系统#

系统安装过程很简单,基本上一路回车,最后输入个用户名密码,静静等待登录界面呈现在你的面前就行了。

https://www.bilibili.com/video/av34732105

共享文件夹#

本段参考了 Ubuntu 社区文档 VirtualBox/GuestAdditionsVirtualBox/SharedFolders

虚拟机设置#

在虚拟机设置中的共享文件夹选项中,可以添加共享文件夹。

选择共享文件夹路径,设置一个共享文件夹名称,自动挂载、固定分配。

那个共享文件夹名称叫做 share name,在虚拟机挂载时用于区分不同来源。

临时分配(transient)可以在虚拟机运行中随时添加,固定分配必须重启。

自动挂载(automount),自动挂载到 /media/USER/sf_<name>/media/sf_<name>

详情参考 VBoxManage 的说明文档

虚拟机内安装扩展功能#

想使用这个共享文件夹的功能,必须让虚拟机安装一个程序。

向虚拟机插入 Guest Additions 光盘。

Insert Guest Additions CD image

然后挂载光盘

1
2
3
cd /media/
sudo mkdir cdrom
sudo mount /dev/cdrom /media/cdrom/

然后安装即可

1
2
cd /media/cdrom/
sudo ./VBoxLinuxAdditions.run

虚拟机内挂载 vboxsf#

最后挂载 vboxsf 即可

1
sudo mount -t vboxsf -o uid=1000,gid=1000 sharename ~/target

注意:这里的 sharename 需要替换成前面设置“共享文件夹”时设置的 share name,~/target 替换成挂载的目标文件夹。

vboxsf 并不是 vboxfs(VirtualBox Filesystem)写错了,应该是 VirtualBox Shared Folders 的简写。

双网卡#

一块网卡使用 NAT(Network Address Translation,网络地址转换)方式连接外网,另一块使用 Host-Only 方式提供 SSH 服务

安装双网卡#

首先在 管理 主机网络管理器 中添加一块网卡,IPv4 填 192.168.10.1,IPv4 子网掩码填 255.255.255.0,DHCP 服务器不启用。

因为我们想要在本地通过 SSH 连接这台服务器,所以不让 DHCP 动态分配 IP 地址,手动设一个固定的 IP。

然后到虚拟机设置中添加两块网卡,一块 NAT,另一块 Host-Only

VirtualBox 有两种 NAT,网络地址转换(NAT)NAT 网络,其实就是 NATNAT Network

在只有一台虚拟机的时候看不出什么意义,但是当有两台虚拟机同时开启的时候,你就会明白什么是 NAT 网络 了。

第一种是每个虚拟机单独连接外网。第二种是虚拟机先组成一个局域网,然后统一连接外网。第二种虚拟机之间可以在局域网中互相访问。

中文翻译看不懂的时候,把语言调成英文说不定就明白了。

虚拟机内系统设置#

Ubuntu 18.04 使用 netplan 管理网络,/etc/network/interfaces 已经弃用。

单个 NAT 很简单,无需任何配置,直接联网(因为虚拟机中相当于直接插网线,并且由 DHCP 分配 IP 地址,想上外网完全没有问题。

双网卡需要通过路由(route)的方式指定与哪个 IP 通信,走哪一个网卡。

手动新建一个 /etc/netplan/01-netcfg.yaml

1
2
3
4
5
6
7
8
9
network:
version: 2
renderer: networkd
ethernets:
enp0s8:
addresses: [192.168.10.10/24]
routes:
- to: 192.168.10.1/24
via: 192.168.10.1

然后应用设置即可

1
sudo netplan apply

我最开始写的是

enp0s8:
  addresses: [192.168.10.10/24]
  gateway4: 192.168.10.1
  routes:
    - to: 192.168.10.1/24
      via: 192.168.10.1

然后无论如何,所有的请求都会从这个网卡走,导致我不能通过 NAT 访问外网。

我在这里困惑了很久,最后发现 routes 不能和 gateway4 同时出现,启用 gateway4 相当于自动把全部目标都路由到这个网关了。

系统自带一个 /etc/netplan/50-cloud-init.yaml,直接改这个也会生效,但是不推荐直接改这个,这个是 cloud-init 自动生成的,之后可能会被 cloud-init 覆盖掉。

enp0s8 的意思是 ethernet network peripheral 0 serial 8

安装系统的目的#

用过 Laravel 之后,就会觉得这个使用脚本自动控制虚拟机非常神奇。所以我也尝试自己安装一下,想学习一下 Homestead 是如何配置 Ubuntu 的,遇到了不少问题,也参考了 Homestead 的 Ruby 代码以及 shell 脚本,确实受益匪浅。

相关链接#

Photoshop 内存问题无法打开性能选项

要求 96 和 8 之间的整数。已插入最接近的数值

无法打开首选项中的性能

这个问题困扰了我一段时间。这个方法也是从网上找到的。

手动修改注册表#

打开注册表编辑器 regedit

找到 HKEY_CURRENT_USER\Software\Adobe\Photoshop\110.0,最后面的版本号可能不一样。

右键,新建DWORD (32 位) 值,名称填 OverridePhysicalMemoryMB

双击修改,把 基数 改成 十进制。然后把数值修改成你的内存大小(比如 4GB 内存填写 4096)。确定。

注册表编辑器界面

重新打开 Photoshop。

下载注册表文件#

photoshop-memory-problem.regview raw
1
2
3
4
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Adobe\Photoshop\110.0]
"OverridePhysicalMemoryMB"=dword:00001000

这里面的 00001000 是 16 进制,转换成 10 进制就是 4096,这是我自己的电脑的总内存大小。

根据注册表文件格式,这里只能是 16 进制,必须手动进行把 10 进制和 16 进制的转换。

如果用的时候需要改成自己电脑的内存大小对应的 16 进制数值。

原因分析#

解决问题之后,我认为原因可能就是 Photoshop 没有正常识别内存大小。

它认为总内存大小只有 8MB。但是 Photoshop 又要求最小值是 96MB。于是出现了“要求 96 和 8 之间的整数”。

然后估计是抛出异常了,但是被 catch 住了,然后就什么也没发生,但是也什么都做不了。