Java-1-基础语法
之前虽然学过 Java,但时间比较久了,并且没有做好整理总结。所以,阅读这本 《Core Java SE 9 for the Impatient》来做一下整理回顾。这本书是两卷 《Core Java》 的精华版,具体的,它去除了历史介绍部分,以及很少使用的 Java 用户接口程序的内容。此外,还去除了单个特性多版本的介绍,全部内容都以 Java SE 9 为根据编写,并且仅突出语言的 “good parts”。
本书要求读者有一定的编程基础和经验。具体内容部分,提到介绍了java concurrent programming 的新方法。使用 Java 新特性处理该问题,而不是使用传统的 “error-prone low-level constructs”. 最后,作者说这本书 “Not for college course and not for systems wizards.” 就是说书中会介绍应用工程师会使用遇到的一些技术问题,例如 logging ,working with file。但是不会介绍 实现链表,或者自己写一个 web server 之类的偏基础知识理解,练习的内容(造轮子)。
A. Primitive Type
Java 共有8种基础数据类型:4种是有符号整型(default 为 int 型,为 4 byte);2种浮点型(default 为 double,8 byte,有 15 significant decimal digits);1种char型;1种boolean型。
有符号整数型:在 Java 中,一个很 nice 的性质是,int 型占用的位数与所使用的运行平台无关(C,C++与编译程序的 processor 有关),对于所有的 JVM int 的长度都是相同的,从而保证了 Java 优秀的兼容特性。最长的整型为 long 型,为 8 bytes,赋值时使用 suffix L
, e.g. 400000000L
.
浮点数 由于 float型是是使用 binary number system,在计算除法时,会出现小数部分不准确的现象,例如: 2.0 - 1.1
给出的结果是 0.8999..
。如果需要精确的数值计算而没有这种 roundoff error,可以使用 BigDecimal
类。BigDecimal.valueOf(n,e)
, 会返回数 $n\times 10^{-e}$。例如上面 2.0 - 1.1
可以表达为 BigDecimal.valueOf(2,0).subtract(BigDecimal.valueOf(11, 1))
.
Double 类中有一些常数,处理一些特殊的运算结果,例如 1.0 / 0.0 的结果为 Double.POSITIVE_INFINITY
表示 $\infty$。而 0.0 / 0.0 的结果,以及负数的开方值为 Double.NaN
, 表示 not a number。
布尔型 需要注意的是,对于 boolean type,他有两个值 true and false,boolean value 和 integers 0 和 1 之间没有联系。
字符型,Java 中的 char 为 UTF-16 编码的 ‘code units’。(具体参考 part “d. String”, and refLink)
Line feed(\n) 和 carriage return (\r) 有啥区别? Line feed 意思是向前移动一行,而 carriage return 表示 将游标移动到行首。通常 windows 使用 \r\n
在文档中表示换行,而 unix 系统中则大多使用 \n
。reflink
B. Variables
变量声明:Convention:通常class 的名字首字母大写, methods 和 variables 的名字首字母小写。variables 的名字常常使用 “camel case”。(对于 python 习惯是使用下划线表示变量)
初始化:使用变量前必须先初始化。
Constant: final
key word 表示一个变量给定后不能改变,即是一个 constant。通常 constant 的变量名全部大写。
1 | final int DAYS_PER_WEEK = 7; |
C. Arithmetic operations
基础运算 如果 operands 都是整型的话,那么对于除法,java 默认认为是 integer division,例如:17/5 = 3, 而 17.0/5 = 3.4。此外,一定要注意的是,integer 除0的会引发异常,是一个 exception 需要被 catch。(浮点数除0不会异常,结果是一个 无穷值 or NaN值)
Math类的方法 在 Java 的 Math 类中,提供了一些 static 的方法,处理数学问题。e.g. Math.pow(x,y)
表示 $x^y$。此外,使用 Math 类的好处还有它可以使得整数运算更加安全。例如:1’000‘000’000 * 3,如果赋值给一个 int,会因为溢出输出一个负值结果,而不报任何错误。但使用 Math.multiplyExact(1000000000,3)
, 会产生一个异常。
Type conversion: 在数据的 type conversion 的时候,要注意是否有 loss of information。例如 float 类型,通常只有约7位小数位,如果小数部分多于7位,则可能丢失精度。
一些特别的运算符 conditional operator: 如果条件为真,结果为第一个值,否则为第二个值。 time < 12 ? "am" : "pm"
bitwise operator: 按位与:&
,按位或:|
, 按位异或:^
, 按位非:~
,还有位位移等。
D. Strings
String 是字符的序列,a sequence of characters. 在 java 中,string 可以包含任意 Unicode characters。本节介绍了 字符串的拼接、子串、比较、数字与字符串之间的转换、String API、code points 和 code units 问题。
字符串的拼接 使用 +
完成两个 string 的 concatenation。如果拼接大量字符串,使用 StringBuilder
.
子串 String 类的 subString(a,b)
方法,可以用于截取子串。split(a)
可以根据字符 a 将 String 切割划分。
字符串比较: 使用 String 类的 equals
方法。(不要用 ==
! 作用 是比较对象是否为同一个)此外,String 类中的 compareTo
方法,可以比较两个字符串在字典中的顺序。
字符串与其他类型间的转换: 将数字转为字符串可以使用封装类的静态方法 toString
, 将字符串转为数字则使用 parseInt
方法。e.g.
1 | int n = 42; |
String API:需要注意的是, String 类的方法,不改变操作对象本身,而是生成一个新的对象。
Code points and code units: 主要是编码长度的问题,最开始的 unicode 是 16位的(65535个),但后来发现仍然不够用(汉字),Unicode 也被扩到了 21bits。此外,还有 variable-length 的 utf-16 编码。因此,当处理包含汉字or特殊符号(emoji)的字符串时,需要使用 codePointCount()
方法来统计确切的字符数。这里的 “code point” 是字符对应的编码值,为一个整型数,一般的 Unicode 的字符,code Point 值在 0 到 65535之间。(refLink)
1 | String str = "Hello🐷"; |
E. Input and Output
从 Reading input, formatted output 两个角度分别简单介绍了数据的读写。
Reading input 需要构建一个 scanner 对象,然后调用它的 nextLine()
方法读取一行,next()
方法读取下一个单词(以 whitespace 作为分隔)。需要注意的是,如果读取 password 之类的,因为希望读取的内容不要显示在 terminal 上,推荐使用 Console
类,然后调用它的 readPassword()
方法。
1 | Scanner in = new Scanner(System.in); |
Formatted Output:可以使用 System.out.printf()
配合 “format specifiers” 来规定数字的格式,例如%8.2f
, 表示“field width” 为8,也就是占据8个位置。.2
表示有两位小数。此外,还可以使用 String
类的静态方法 format
来实现规定格式输出。
F. Control Flow
介绍了程序基础中的分支选择(Branches),循环语句(loops)的相关技巧。
Branches: if ... else ...
, switch(){case 0: ...break;}
Loops: while
, 此外,while 是先判定后执行,如果需要先执行后判定,则使用 do{...} while(constraints)
. while 的 iteration 次数不是非常明显的显示,如果 iteration 次数确定的话,推荐使用 for
loop, for(initialization; test; update)
, 将初始化,判断,更新,放在一起,更加方便了解程序逻辑。
Breaking and continuing: 如果想在程序中间退出循环,可以使用 break
关键字。此外,还可以使用 continue
退出当次循环,强行进入下一轮循环。
Local variable scope: 变量的作用域通常以 { }
为界限,即从声明开始,到 }
结束。注意相同名字的变量的作用域不能重复。
G. Arrays and Array Lists
本节介绍了数组的基本操作,ArrayList 是一个泛型类,可以指定 list 中的对象的数据类型。
Arrays最简单形式: int[]
, String[]
, 分别表示整型数组,字符串数组。数组初始化可以使用 new String[100]
来实现。
Array construction:使用 new 关键字初始化数组时,数值型的初始值默认都为0,boolean 型默认都为 false,objects 的数组初始值默认都为 null。
ArrayList:为了获得更加自由的数组操作-数组构造完成后依然可以更改长度,可以使用 泛型类(“Generic class”)ArrayList
来操作数组。它的常用方法有 add()
, remove()
,set()
…
1 | ArrayList<String> fr; |
Wrapper classes for primitive types: 需要注意的是,generic class 不支持 primitive types 的数作为 type parameters,即 ArrayList<int>
是不允许的。解决方法是使用 基本数据类型的 包装类(wrapper class)。例如,int
的包装类为 integer
. 将基础类型 和 包装类 之间的转换的过程是自动的,称为 “autoboxing” 和 “unboxed”. 但有一点需要注意的是对于对于包装类,==
and !=
比较的是对象的引用是否相同,而不是比较对象的值,因此比较两个包装类对象的值是否相等必须使用 equals
方法。
Enhanced for loop: 如果你想遍历 array 中的元素,一个更加简洁的写法如下,它会从 number[] 开始遍历 numbers 数组中的元素,然后依次赋值给 n,一直到最后一个。
1 | int sum = 0; |
Copying Arrays and Array Lists: Array 或者 ArrayList 声明的时候,int[] a
, 实际上 a 是一个指针,如果再有 int[] b = a;
那么,a,b 都是指向同一个数组的指针,也就是说并不涉及一个值的复制。如果只希望进行值的复制,而不是将地址进行复制,我们需要使用静态方法 Arrays.copyOf()
进行复制。
Array Algorithm:我们 Arrays
中的静态方法处理基本数据类型的数组,使用 Collections
的静态方法处理 ArrayList<>
。常用方法包括:fill()
, sort()
.
Command-line arguments: 程序的 main 函数,public static void main(String[] args)
中的 String 数组的用处是传入 command line 中给如的参数,i.e. java Greeting -g cruel world
, 这里, Greeting 是一个编译好的java 程序,然后 args[0] = "-g", args[1] = "cruel", args[2] = "world"
.
Multidimensional Arrays: java 没有真正意义上的多维数组,实际上是通过 “数组的数组” 的形式来实现的。二维数组可以表示如下,square[1][2]
表示第2行,第3列,也就是9(先行后列). 单独的 square[1]
表示第二行。此外,Arrays 类中也有一些静态方法可以处理二维数组,例如 deepToString()
方法。
1 | int[][] square = { |