写在前面:仅作记录

swing和awt部分可以不怎么看,前五章都是基础知识,有些部分可以熟练java后再看,后面的章节可以选择性地看,如:想刷算法题、了解数据结构,可以先看第六章接口部分再看第九章集合部分。

第二章——程序设计环境

1.老规矩,安装开发工具包,目前基于JAVA SE 8开发的项目比较流行,所以选择这个版本安装JDK(第十版也是符合SE 8写的),访问 Oracle 网站下载,因为本电脑是deepin系统,也就是要下载符合linux x64的,选择Linux x64 Compressed Archive——jdk-8u291-linux-x64.tar.gz,下载该压缩包后直接解压缩到我的主目录,主目录中会出现文件夹jdk1.8.0_291,这就是jdk的安装目录,然后在主目录中找到.bashrc文件(存有执行路径的文件,执行路径是操作系统查找可执行文件时所遍历的目录列表),将jdk/bin目录增加到该文件的最后一行,我的添加是这样的(erin是我的用户名):

export PATH="/home/erin/jdk1.8.0_291/bin:$PATH"

在终端中输入以下命令测试设置是否正确:

javac -version

显示设置成功:

javac 1.8.0_291

2.安装库源文件和文档

库源文件在jdk安装路径中以一个压缩文件src.Zip(包含了所有公共类库的源代码)的形式发布,必须将其解压缩后才能够访问源代码,按照书上的步骤,在主目录的javasrc文件中解压缩成功(执行命令:jar xvf jdk安装路径/src.zip),出现5个文件夹:com 、java、javax、launcher、org。

目录/home/erin/corejava中是本书的示例代码。

3.从命令行编译并运行Java程序,示例:

javac Welcome.java // 生成Welcome.class编译文件
java Welcome // 执行编译文件,运行程序

javac 程序是一个Java编译器,它将文件Welcome.java编译成Welcome.class,Java区分大小写。

4.在网站上有一个教程,其中提到了初学者经常容易犯的一些错误。

5.在选择集成开发环境时暂时不选择Eclipse而是选择visual studio code(因为可以同时使用其他语言,比较方便,而且安装相关java插件后和eclipse差别也不大),设置成中文格式

6.为Visual Studio Code (vscode) 配置Java环境参考学习,该文配置的环境是Windows系统,也适用于Linux(deepin),注意一下java.home配置的jdk安装路径即可。

7.第2.5节构建并运行applet暂时不看完,因为applet要在浏览器中查看,linux系统需要安装一些插件,避免出错,等到13章再详细了解一下applet编写部分。

8.安装Eclipse,按照链接安装,首先访问官网,下载安装包eclipse-inst-jre-linux64.tar.gz,然后解压该压缩包,然后在终端中执行如下命令

sudo dedit /usr/share/applications/eclipse.desktop

因为我的deepin电脑没有安装gedit编辑器,自带的是dedit编辑器,所以稍微改了一下,然后粘贴并保存如下内容:

[Desktop Entry] 
Type=Application 
Name=Eclipse 
Comment=Eclipse Integrated Development Environment 
Icon=eclipse 
Exec=/opt/eclipse/eclipse 
Terminal=false 
Categories=Development;IDE;Java;

但是没有在桌面上发现Eclipse的快捷方式,找了一下,在/home/erin/eclipse-installer目录下找到eclipse-inst可执行文件,点击打开,按照书上的内容选择eclipse IDE for Java Developers下载,虽然用上了清华镜像但过程很慢,大概半小时(可能也是我的网不好),

(1)按照书上的步骤成功导入/corejava/v1ch02/Welcome项目,file-new-project-java project-不勾上use default location-然后browse选择已存在的项目或有java代码的文件夹。

(2)按照已有的jre创建项目:

file-new-project-java project-输入project name-JRE选择Use default JRE ‘jre’ and workspace complier preferences,点击Configure JREs…出现下图

java核心技术卷一最新版 java核心技术卷一下载_开发语言

可以不用创建module-info.java文件,最后点击finish成功。

书上提到:在 Eclipse 中, 可以使用菜单选项 Source— Organize Imports Package,如import java.util.*; 将会自动地扩展指定的导入列表,如:import java.util.ArrayList; import java.util.Date;

第三章——基本程序设计结构

1.Java 中定义类名的规则很宽松,名字必须以字母开头,后面可以跟字母和数字的任意组合,长度基本上没有限制,但是不能使用 Java 保留字(例如, public 或 class) 作为类名,标准命名规范是驼峰命名法。

2.java和c/c++不一样,main方法没有为操作系统返回退出代码0。

3.三种注释方法:(1)//;(2)/* */;(3)/** */,这种注释自动生成文档。在 Java 中,第二种注释不能嵌套。

数据类型

4.Java 是一种强类型语言,这意味着必须为每一个变量声明一种类型: 在 Java 中,一共有 8 种基本类型( primitive type ), 其中有 4 种(从负数到正数)整型(byte——1字节,short——2字节,int——4字节,正数部分超过20亿(2×10^9),long——8字节,有后缀L或l)、2 种浮点类型(float——4字节,有效位数6-7位,有后缀F或f,double——8字节,有效位数15位,可以没有后缀,或者添加后缀D或d)、 1 种用于表示 Unicode 编码的字符单元的字符类型 char (请参见论述 char 类型的章节) 和 1 种用于表示真值的 boolean 类型。

5.从 Java 7 开始, 加上前缀 0b 或 0B 就可以写二进制数。例如,0b1001就是 9。另外还可以为数字字面量加下划线,如用 1_000_000(或0b1111_0100_0010_0100_0000 ) 表示一百万。这些下划线只是为了让人更易读,Java编译器会去除这些下划线。

6.在 C 和 C++ 中, int 和 long 等类型的大小与目标平台相关,16 位处理器上整型数值占 2 字节,32位处理器上占4字节;long在32位处理器上占4字节,64位处理器上占8字节。

7.Java 没有任何无符号(unsigned) 形式的 int、 long、short 或 byte 类型。

8.可以使用十六进制表示浮点数值。所有的浮点数值计算都遵循 IEEE 754 规范,具体来说,下面是用于表示溢出和出错情况的三个特殊的浮点数值:

(1)正无穷大 ,用常量Double.POSITIVE_INFINITY表示;

(2)负无穷大,用常量Double.NEGATIVE_INFINITY表示;

(3)NaN (不是一个数字),用常量Double.NaN表示,代码if (Double.isNaN(x)),用于检查x是否是数字,不能直接用if (x = Double.NaN)检查x是否是数字。

9.浮点数值不适用于无法接受舍入误差的金融计算中,如果在数值计算中不允许有任何舍入误差, 就应该使用 BigDecimal类。

10.char类型常量(字符常量),如:‘A’,而"A"代表的是字符串。char类型的值也可以表示为16进制值,范围从\u0000到\Uffff(如\u03C0 表示PI),\u是转义序列,还有其他的如:\b等,转义字符都可以出现在加引号的字符常量或字符串中,\u还可以出现在加引号的字符常量或字符串之外,注意当心注释中的 \u,可能会报错。

11.本书强烈建议不要在程序中使用 char 类型,除非确实需要处理 UTF-16 代码单元,最好将字符串作为抽象数据类型处理。

12.在java中整型值和布尔值之间不能像在c++中那样进行相互转换,整数表达式 x = 0 不能转换为布尔值,p36最上方的c++注释无法理解???

13.变量声明后,在使用前必须显示初始化,和c/c++一样,java中用final声明常量。

14.整数被 0 除将会产生一个异常, 而浮点数被 0 除将会得到无穷大或 NaN 结果。

15.floorMod方法解决了整数余数可能是负数的问题,但对于负除数会得到负数结果。

16.强制类型转换通过截断小数部分将浮点值转换为整型,如果想对浮点数进行舍入运算,以便得到最接近的整数,用函数Math.round(),返回的结果是long类型的,仍需要强制类型转换成int型。

17,和c++一样,&& 和 || 运算符是按照“ 短路” 方式来求值的: 如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。

java核心技术卷一最新版 java核心技术卷一下载_开发语言_02

18.将一个字符串和一个非字符串的值进行拼接时,后者被转换成一个字符串(任何一个 Java 对象都可以转换成字符串),但在python中比较严格,需要强制转换。

19.Java 字符串大致类似于C中的 char* 指针,C++可以修改字符串中的单个字符,但java不行,判断字符串是否相等用代码s.equals(t)表达,s和t可以是字符串常量,也可以是变量,要想检测两个字符串是否相等,而不区分大小写,可用equalsIgnoreCase 方法,不能用==判断字符串是否相等,只能确定两个字符串是否在同一个位置上,避免出现bug。

20.空串是一个 Java 对象, 有自己的串长度( 0 ) 和内容(空),检查字符串是否为空:if(str.length()==0)或if(str.equals("")),Null串表示目前没有任何对象与该变量关联,检查一个字符串是否为 null:if(str==null),如果检查一个字符串既不是null也不是空串,就要先检查是否是null。

21.第3.3.4节unicode和char类型、第3.6.6节码点与代码单元不太理解(待学习),先学编码格式,参考链接,UNICODE(简称UCS)用两个字节来表示为一个字符,UTF8(面向传输的标准:UCS Transfer Format)就是每次8个位传输数据,UTF16就是每次16个位。码点( code point) 是指与一个编码表中的某个字符对应的代码值,在 Unicode 标准中, 码点采用十六进制书写,并加上前缀 U+, 例如 U+0041 就是拉丁字母 A 的码点。UTF-16 编码采用不同长度的编码表示所有 Unicode 码点。在基本的多语言级别中(经典的Unicode代码,码点从 U+0000 到 U+FFFF,英文、汉字等都包括其中),每个字符用 16 位(两个字节)表示,通常被称为代码单元( code unit); 而辅助字符采用一对连续的代码单元进行编码,码点倒是只有一个。在 Java 中,char 类型描述了 UTF-16 编码中的一个代码单元(16位)。

22.StringBuffer和StringBuilder的API是一样的,单线程时最好用后者。

23.Scanner类定义在java.util 包中:

Scanner scanner=new Scanner(System.in);

因为输入是可见的,所以 Scanner 类不适用于从控制台读取密码,Java SE 6引入了 Console 类实现这个目的,要想读取一个密码,可以采用下列代码:

Console cons = System.console(); 
String username = cons.readLine("User name: "); 
char [] passwd = cons.readPassword("Password:");

但采用 Console 对象处理输入不如采用 Scanner 方便,前者每次只能读取一行输入,而没有能够读取一个单词或一个数值的方法。

24.可以使用静态的 String.format 方法创建一个格式化的字符串, 而不打印输出:

String message = String.format("Hello, %s. Next year, you'll be %d", name , age);

25.对文件进行读取需要用File对象构造一个 Scanner 对象,然后可以使用Scanner方法进行读写:

Scanner in = new Scanner(Paths.get("niyflle.txt"), "UTF-8");

指定了UTF-8编码,最好指定一个编码,而不是运行java程序的机器本身的”默认编码“,避免影响在其他机器上的可移植性。

写入文件需要构造一个 PrintWriter 对象、提供文件名,如果文件不存在,创建该文件,可以像输出到 System.out —样使用 print、 println 以及 printf 命令。

PrintWriter out = new PrintWriter("myfile.txt", "UTF-8");

26.查看当前程序所处的路径:

String dir = System.getProperty("user.dir");

27.数组a的长度:a.length,想打印数组a可以:

System.out.println(Arrays.toString(a));

结果类似于:"[2,3,5,7,11,13]"。Java 中的 [ ] 运算符被预定义为检查数组边界,而且没有指针运算,即不能通过 a 加 1 得到数组的下一个元素。

28.static void fill(type[] a , type v) 将数组a的所有数据元素值设置为v,快速地打印一个二维数组的数据元素列表, 可以调用:

System.out.println(Arrays.deepToString(a));

输出格式为: [[16, B, 2, 13], [5, 10, 11, 8], [9, 6, 7, 12], [4, 15, 14, 1]],Java 实际上没有多维数组,只有一维数组,多维数组被解释为“ 数组的数组”。

由于可以单独地存取数组的某一行, 所以可以让两行交换。

double[] temp = balances[i];
balances[i] = balances[i + 1]; balances[i + 1] = temp;

第四章——类

1.注意对象的三个主要特性:对象的行为、对象的状态、对象的标识。

2.在类之间, 最常见的关系有:(1)依赖(“uses-a”) ,如果一个类的方法操纵另一个类的对象,就是一个类依赖于另一个类;(2)聚合(“has-a”),意味着类 A 的对象包含类 B 的对象;(3)继承(“is-a”)。

3.一个对象变量并没有实际包含一个对象,而仅仅引用一个对象,以下代码中,deadline是一个对象变量,new Date() 构造了一个 Date 类型的对象, 返回的值是对新创建对象的引用,存储在deadline中。

Date deadline = new Date();

4.局部变量不会自动地初始化为 null,而必须通过调用 new 或将它们设置为 null 进行初始化。

5.利用 LocalDate 类可以编写一个日历程序(只返回年月日,不像data类,还返回具体的时间点),能处理星期几以及各月天数不同等复杂问题。

6.在一个源文件中,只能有一个公有类(包含main方法,并且源文件名和公共类名一样),但可以有任意数目的非公有类。

7.最好将实例域标记为 private,而不是public,避免别的类更改实例域,注意!!!在所有的方法(包括构造器)中不要命名与实例域同名的变量,注意显示参数和隐式参数(this,也就是调用方法的对象)。

8.注意不要编写返回可变对象的引用的访问器方法,如果需要返回一个可变对象的引用,应该首先对它进行克隆。

9.静态域属于类,而不属于类的任何独立的对象,可以直接类名.静态域名引用。注意System类有一个setOut方法,可以改变final变量out的值,因为这个方法是一个本地方法(?),不是java语言实现的,是一种特殊的方法。

10.类的静态方法只能访问自身类中的静态域,不能访问非静态域,也只能用类名(最好是类名,类的对象也可以,但不建议)调用该静态方法。但其他非静态方法也能访问静态域,只不过需要通过类的对象的引用调用这个方法,从而访问静态域。静态域也可以直接由类名.静态域的形式调用。

11.总之,static修饰的变量和函数是属于类且不属于类对象的变量和函数。(static修饰的变量和函数在 C 中表示不能被其他文件访问的全局变量和函数等)

12.每一个类可以有一个 main 方法,想对类进行单元测试,可以在要测试的类中添加一个main方法,如Employee类,如果测试,直接执行java Employee命令,但如果Employee类是一个更大型应用程序的application一部分,执行命令java application并不会执行Employee类的main方法。如果想执行Employee类的main方法,只能先编译application后,出现Employee.class文件后再执行java Employee。

13.在一个java程序中只能有一个public类,该类的main方法才是执行该程序时执行的方法。

14.Java 总是采用按值调用,即方法得到的是所有参数值的一个拷贝。Java 中方法参数的使用情况:

(1)一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。

(2)一个方法可以改变一个对象参数的状态(成员变量?)。

(3)一个方法不能让对象参数引用一个新的对象。

15.如果一个类的不同方法有相同的名字、不同的参数,就产生了重载,方法名以及参数类型是方法的签名,方法的返回类型不包含在其中,即不能有两个名字相同、 参数类型也相同却返回不同类型值的方法。

16.如果没有初始化类中的域,会被自动初始化为默认值(不建议,这是不好的习惯,最好一开始就初始化),方法中的局部变量不会被初始化,需要自行初始化,避免出错。

17.如果类中提供了至少一个构造器,但是没有提供无参数的构造器,那么在构造对象时如果没有提供参数就会报错;但如果类没有提供构造器,系统会自行提供一个无参构造器。可以调用(私有静态)方法对域进行初始化或直接在类中初始化赋值。

18.如果构造器的第一个语句形如 this(…),这个构造器将调用同一个类的另一个构造器(C++中则不可行)。

19.注意命令行形式在基目录编译(javac命令,javac编译器?)子目录下的.java文件(带有文件分隔符和扩展名 .java 的文件)和java解释器(虚拟机?)(java命令)加载类的方式(带有.分隔符)。

20.重要细节:设置类路径(4.8节)(待学习)。

21.第4.9节注释部分注意回头学习一下(重要,待学习)。产生包注释需要在每一个包目录中添加一个单独的文件,有两种方法;为所有的源文件提供一个概述性的注释,这个注释将被放置在一个名为 overview, html 的文件中,这个文件位于包含所有源文件的父目录中。

22.设计类时,优先使用不可变的类,LocalDate 类以及 java.time 包中的其他类是不可变的—没有方法能修改对象的状态。

第五章——继承

1.反射( reflection) 是指在程序运行期间发现更多的类 及其属性的能力,可以之后再学习(待学习)。

2.在 Java 中,所有的继承都是公有继承,而没有 C++ 中的私有继承和保护继承。目标:p196

3.在子类中可以增加域、增加方法或覆盖超类的方法,然而绝对不能删除继承的任何域和方法。子类的任何方法或构造器都不能访问超类的私有域或方法,只能通过super.域名调用超类的私有域或者super(params)调用超类的构造器。

4.一个对象变量(例如,变量 e )可以指示多种实际类型的现象被称为多态,在运行时能够自动地选择调用哪个方法的现象称为动态绑定。程序中出现超类对象的任何地方都可以用子类对象置换,如:可以将一个子类的对象赋给超类变量,但如果有一个超类数组变量引用和一个子类变量引用引用了同一个子类对象,超类变量引用不能调用子类的方法(???待研究)

5.在 Java 中,子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换。

6.如果调用 private 方法、 static 方法、 final 方法或者构造器, 编译器将可以准确地知道应该调用哪个方法,这种调用方式称为静态绑定。

7.在覆盖一个方法(签名一致,即方法名和参数名个数、类型一致)的时候,子类方法不能低于超类方法的可见性(修饰符的权限不能低于超类方法的权限),并且返回值应该和超类的一致或者是超类方法返回值的子类。

8.不允许扩展的类被称为 final 类,如果在定义类的时候使用了 final 修饰符(和权限修饰符不同)就表明这个类是 final 类。类中的特定方法也可以被声明为 final,如果这样做,子类就不能覆盖这个方法( final 类中的所有方法自动地成为 final 方法,但域不是final的。)

9.可以超类 = 子类形式,以子类 = (子类)超类对象引用的形式强制类型转换时,应该instanceof检查一下超类对象引用实际引用的类型是否是子类(超类对象的实际类型是超类),如果是,就可以进行强制类型转换,然后对子类的具体内容进行具体操作,不然在此之前不能操作具体内容。

instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,obj不能是基本类型,应该是引用类型,用法为:

boolean result = obj instanceof Class

其中 obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。参考链接:

10.在一般情况下,应该尽量少用类型转换和 instanceof 运算符。只要没有捕获 ClassCastException 异常,程序就会终止执行。

17.为了提高程序的清晰度,包含一个或多个抽象方法的类本身必须被声明为抽象的。但即使类不含抽象方法,也可以被声明为抽象的。抽象类不能被实例化,即不能创建这个类的对象,但可以创建一个具体子类的对象,并且可以定义一个抽象类的对象变量, 但是它只能引用非抽象子类的对象。

18.如果一个类没有明确地指出它的超类,Object 就被认为是这个类的超类。java中只有基本类型不是对象,如数值、字符、布尔值,但所有的数组类型,不管是对象数组还是基本类型的数组都扩展了 Object 类,即可以让Object对象引用数组(基本类型数组或对象数组)。Object类的equals方法只是比较两个对象引用的地址是否相同,就是==。

19.equals方法只能在对象之间使用,如Objects.equals(a, b)(a或b可能是null)或者a.equals(b)(a和b都不是null)。在子类中定义 equals 方法时,首先调用超类的 equals。如果检测失败,对象就不可能相等。如果超类中的域都相等,就需要比较子类中的实例域。

20.当参数不属于同一个类的时候equals方法需要仔细思考一下(待学习,细致),注意编写一个完美的 equals 方法的建议。

21.注意在继承Object类的equals方法时应该注意参数是Object,如果不是,就是定义了一个完全无关的方法。因此,为了避免发生类型错误,可以使用@Override对覆盖超类的方法进行标记,如:

@Override public boolean equals(Object other)

如果正在定义一个新方法而不是覆盖超类的方法,或者发生了错误(比如参数和要覆盖的超类方法不同)编译器就会给出错误报告。

22.通过调用 getClass( ).getName( ) 获得类名的字符串,在调用 x.toString( ) 的地方可以用 “”+x 替代。这条语句将一个空串与 x 的字符串 表示相连接

23.Object 类定义了 toString 方法,打印结果:输出对象所属的类名@散列码。PrintStream 类、数组等继承了 object 类的 toString 方法,而不是重写,如果是object类的方法,以“” + 数组名的形式输出,结果是[类型@散列码,想正确输出数组中的内容应该调用方法Arrays.toString(数组名),如果是多维数组,应该调用Arrays.deepToString方法。

24.在vs code中打开/home/erin/corejava/v1ch05/equals目录下的java项目步骤如下:文件——打开文件夹——选择equals文件夹——编译生成launch.json——运行EqualsTest.java没问题,但equals文件夹下的文件都报错(待解决):

The declared package "equals" does not match the expected package ""

打开equals文件夹的父文件夹之后没问题(待解决)。

25.运行时动态更改数组,需要使用 Java 中另外一个被称为 ArrayList 的类。

声明、构造一个保存 Employee 对象的数组列表:

ArrayList<Employee> staff = new ArrayList<Employee>(可以添加初始容量);

或者如下,分配一个包含 100 个对象的内部数组:

staff.ensureCapacity(1OO);

staff.size()相当于数组a的a.length。一旦能够确认数组列表的大小不再发生变化,就可以调用 trimToSize方法。这个方法将存储区域的大小调整为当前元素数量所需要的存储空间数目。

26.ArrayList 类似于 C++ 的 vector 模板,不过后者是值拷贝,前者是引用拷贝。

数组列表使用add()在尾部添加元素,add(i , boss)在第n个位置添加元素;从数组列表中间删除一个元素,并返回被删除的元素:

Employee e = staff.remove(i);

替换数组中已经存在的第 i 个元素,可以使用set方法,注意只有 i 小于或等于数组列表的大小时才能设置:

staff.set(i , harry):

方法get获得第i个元素:

Employee e = staff.get(i);

使用 toArray 方法将数组元素拷贝到一个数组中:

X[] a = new X[list.size()]; 
list.toArray(a);

27.所有的基本类型都有一个与之对应的类,例如,Integer 类对应基本类型 int,通常这些类被称为包装器 ( wrapper ) 。对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值,同时,对象包装器类还是 final , 因此不能定义它们的子类。

整形数组列表应该是ArrayList(效率远低于int[]数组,因此整形数组列表最好构造小型的),而不是ArrayList。

注意自动装箱(autoboxing),将int值赋给Integer对象:Integer.valueOf(3)。将一个 Integer 对象赋给一个 int 值时会自动拆箱:Integer对象.intValue();

==符号检查的是对象是否指向同一个存储区域,所以判断两个对象是否相等(值域等)时,应该用equals方法。装箱和拆箱是编译器(javac命令相关)认可的,而不是虚拟机(java命令)。

将数字字符串转换成数值:

int x = Integer.parseInt(s);

parselnt 是一个静态方法,与Integer 类没有任何关系,但可以放置这个方法。

Integer.toString(x);将int类型的x转换成字符串。

28.在比较两个枚举类型的值时,永远不需要调用方法equals,应该直接使用“= =” 。

29.一个 Class 对象实际上表示的是一个类型,而这个类型未必一定是一种类。

30.问题一:VScode中跑reflection文件夹下的程序出错:

Class is a raw type. References to generic type Class<T> should be parameterized

解决方法:

(1)在出现错误的方法外添加代码(不可行),抑制编译器产生警告信息:

@SuppressWarnings("unchecked")

(2)使用范型通配符。参考链接:

问题二:终端javac编译没问题,java命令执行代码报错:

错误: 找不到或无法加载主类 ReflectionTest

猜测是package的问题,参考该链接解决:

(1)注释掉package(去包),终端cd到代码所在目录,执行:

javac ReflectionTest.java
java ReflectionTest

(2)不去包,终端cd到代码所在目录的上一级目录,执行代码:

javac reflection/ReflectionTest.java
java reflection.ReflectionTest

reflection是包的名字,出现这种问题是因为cd到代码所在目录reflection后执行器(java命令)会寻找包reflection,但实际上没有,所以应该cd到上一极目录执行命令。