本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
Course I notes
main
的 method
//class
public class project {
//method
public static void main(String[] args) {
//statements
String x = "Hello world";
//print line
System.out.println(x);
}
}
.java
bytecode
类型的文件,存储在 .class
文件中。bytecode
文件不基于指令集,但足够接近底层。bytecode
文件后,Java 会以解释器的方式,将 bytecode
文件转换为特定的机器语言。
# complie java
javac + file_name.java
# a file_name.class file will generated
# initiate interpreter, where JVM comes in
# no extension needed!
java + file_name
Java Development Kit(JDK) 是 java 用于编译和执行文件的工具集合。与之前概念一些区别:
bytecode
文件转化为对应机器的机器码文件。跨平台三者从上到下是依次包含的关系。
java 的类名,函数名,变量名等一切由用户自定义的 name 被称为 identifier:
_
,和美元符号 $
。Type | Size | Range |
---|---|---|
byte | 8 bits | $[-2^7, 2^7)$ |
short | 16 bits | $[- 2^{16}, 2^{16})$ |
int | 32 bits | $[- 2^{32}, 2^{32})$ |
long | 64 bits | $[- 2^{64}, 2^{64})$ |
#类型必须匹配
int a = 78;
double d = a * 5;
跟 C++ 类似,Java 的运算中存在隐式转换。赋值的时候需要注意。
Objects 的两种属性:
// this is inline comment
/* this is
block comment
*/
/** javadoc comments
this scan your source code for certain comments
and automatically creates html files to describe your code,
starts with forward slash and to asterisks
end with an astrisk and forward slash
*/
final
关键字:
final double PI = 3.1415926
Type | Size | Range |
---|---|---|
byte | 8 bits | $[-2^7, 2^7)$ |
short | 16 bits | $[- 2^{16}, 2^{16})$ |
int | 32 bits | $[- 2^{32}, 2^{32})$ |
long | 64 bits | $[- 2^{64}, 2^{64})$ |
float | 32 bits | $[3.40282347 x 10^{38}, 1.40239846 x 10^{-45}]$ |
double | 64 bits | $[1.7976931348623157 x 10^{308}, 4.9406564584124654 x 10^{-324}]$ |
int
double
因此,如下的语句会错误:
long bigNum = 99999999999999
因为此处将 99999999999999
视作是 int
类型数据,但该数据已经超过了 int
类型所能表示的上限。因此我们需要指定其为 long
类型:
//adding L to the end of the number
long bigNum = 99999999999999L
同理,如果要使用 float
表示浮点数,也需要再后面加 F
或者 f
:
float PI = 3.14159F
float PI = 3.14159f
我们也可以使用类似后缀强制将默认为 int
的数据转化为浮点型:
double num1 = 2D;
double num1 = 2d;
低精度的数据有助于减少内存的使用。
定义 char 使用单引号(single qoute):
char yes = 'Y'
double quote 代表 string,不能用于 char
boolean
类型的定义:
boolean parked = true;
使用 back slash 作为 escape sequence 的开始,表示特殊字符:
// single quote
\'
// double quote
\"
// back slash
\\
// tab
\t
// new line
\n
// carriage return
\r
Java 的整数除法会完全忽略小数部分,比如 9/2 = 4
。如果希望得到浮点数结果:
9.0 / 2 = 4.5
9 / 2.0 = 4.5
9.0 / 2.0 = 4.5
9D / 2 = 4.5
9 / 2D = 4.5
9D / 2D = 4.5
String 的加法代表链接两个 string:
//result is "1331"
"13" + "31"
在有不同类型参与的表达式计算中,Java 会对某些不合要求的 operand 进行 “promotion”,以此达到完成运算的目的。具体的类说:
分为两种:
double average = 4.0;
int gpa = average;
就是非法的,因为 int
不能表示 double
。因此,导致丢失精度的 assignment 是非法的。
Casting 是手动的强制转换。写法为:
(type)expression
比如下面的表达式:
//casting 5 from int to double
//then do the 5.0 / 9
(double)5/9
注意,casting 的优先级高于所有的运算,除了括号。因此在括号参与的运算中,要先运算括号中的内容:
//result is 0.0
(double)(5/9)
casting 不单用于算术类型。
Java 中 Object 的实例化被称为 Instantiation。写法如下:
new ClassName(parameters)
实例化通过构造函数(constructor)建立。以 String
类为例,其构造函数为 String
。
String major = new String("computer scinece);
Methods 通过 instance 的 identfier,也就是某个类的 reference 进行调用。比如打印:
System.out.println("Hello");
//no newline version
System.out.print("Hello");
out
是指向 PrintStream
类对象的引用。System
是 JAVA STL 的一部分。
实例化的只有对象的数据。methods 属于 class,是唯一的。
跟 C++ 不同的是,如果在 Java 中更改了指向某个地址的引用,但忘记了释放指向的资源,Java 会在随后的恰当时间内自动回收这部分资源。这个功能被称为 Java 的 Garbage Collection。
“Hello”
) 也是 String 实例,也可以直接调用 String method。Docs: check here
major = "CS"
interest = "system"
//compute the length of the string, return int
//ret_num has val 2
int ret_num = major.length()
//return a new string will switching all uppercases to lowercases.
//dosen't modifiy the original string
//"newString" has value "cs"
String newString = major.toLowerCase();
//concatnate a string with another string
//return a new string
//"concated" has value "CSsystem"
String concated = major.concat(interest)
//return a new string
//replace oldchar with newchar
//"replaced" has value "PS"
String replaced = major.replace('C', 'P')
//return new string
//copying a range of characters from the string
//range is LEFT include.
//"subStr" has value "sys": 0,1,2
String subStr = interest.substring(0,3);
// returns the index of the first occurrence of the sequence, or return - 1 if no match
// support int, char, string
// char will be converted to Unicode int
indexOf(String str)
indexOf(int char)
indexOf(String str, int fromIndex)
indexOf(int char, int fromIndex)
Docs: check here
Scanner
类是 Java 提供的,用于接受输入信息的类。实例化一个 Scanner
对象需要提供一个 input 对象:
import java.util.Scanner;
public class FahrenheitToCelsius {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); //read keyboard input
}
}
这里的 System.in
就是一个输入对象。Scanner
实例可以通过该对象按顺序读取从键盘输入的信息。比如下面的信息:
78 long
walks
会以:
78 long\n\walks\n
这种 “流(stream)” 的形式被接收。流中的各个部分被称为 Token,每种类型的 Token 可以被指定的 Sanner
method 进行筛选。比如 nextInt()
就可以在流中读取下一个可见的 int
数据。
Scanner
通过特殊的,被称为 delimiter 的边界来分割 Token。默认情况下,delimiter 是 whitespace(\t
, \n
, space)。
int
就用 nextInt()
)
import java.util.Scanner;
public class FahrenheitToCelsius {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); //read keyboard input
System.out.print("Please enter the temperture(F): ");
int fah = input.nextInt();
System.err.print("The current temperture in F is: " + fah);
}
}
import java.util.Scanner;
nextLIne()
) 会自动忽略 Token 前后的所有 whitespace
hasNext 系列函数会返回一个 bool 值。为 true 时输入有效,反之无效。以 int
数据为例:
int num;
if (input.hasNextInt()) {
num = input.nextInt();
}
else {
System.out.println("invalid input.")
}
多个输入可以通过顺序性的调用 next
系列的 method 来完成:
int fah = input.nextInt();
System.out.print("Enter a day of the week: ");
String day = input.next();
但有一点需要注意的是,nextLIne()
method 不会忽略 \n
。这会带来什么样的后果呢?
这里的回车(Enter)实际上产生的效果是添加 \n
到流的末尾。假设我们输入一个数 78
,那么回车以后,78
存储到本地变量,流就会变为 \n
。之前提到过,其他 next
系列的 Methods 都会忽略 Token 之前的 \n
,除了 nextLine()
; 因此,其他的 next
method 都会在此处等待用户的下一次输出,但 nextLine()
会在这里读取到的首个 Token \n
,这将导致当前的输入立即结束。
输入数据 + 回车 + 输入数据只是一种流程,原理上是使用了 \n
作为 delimiter。但我们也可以通过 space 来做 delimiter,比如本节开头的,使用 next()
例子中,如果使用 78 monday
只输入一行,也可以达到效果。此时流通过 space 将 Token 分为了 78
和 monday
,fah
接受了 78
, day
接受了 monday
,因此只需要输入一行也能达到效果。
使用 nextLIne()
可以一次性的获取单个 String(可以包含各种 whitespace,除了 \n
)。但在使用之前,需要将之前的 \n
信息全部清空。我们可以额外的调用一次 nextLine()
来实现这个目的:
int fah = input.nextInt();
//clean up the newline info
input.nexLine();
//read the input
System.out.print("Enter a day of the week: ");
String day = input.nextLine();
Java 也提供了 method trim()
来实现这个功能:
String day = input.nextLine().trim();
Docs:check here
Class 可以按照其提供的函数进行打包。 这些包通常被称为 packages,比如 System, String 等等。Java 中存在一个 package Long list,使用属于该 list 中的 package 时无需做额外的准备。任何不属于该 list 的 package,都需要 import,格式如下:
import packageName.memberName;
需要注意的是,package 之间存在着继承关系。在导入的时候,需要按层级将所有的 package 写出来。比如 Scanner
就继承自 util
Package,而 util
继承自 java
,因此导入时就需要写成:
import java.util.Scanner;
Import 的 overhead cost 处于编译期,不影响运行期的 performance。
如果需要导入当前 package 的所有内容,可以使用通配符(wildcard) *
(asterisk):
import java.util.*;
假设有如下的两个 package:
long.walks.Beach
swim.surf.Beach
这两个 package 都被称为 Beach
,但来自于不同的 package,其实现也不一样。如果我们同时导入这个两个 package,再进行调用的话:
import long.walks.*;
import swim.surf.Beach;
//using the package
Beach tybee;
那么此时编译器会报错包冲突。比较好的解决方法是,导入其中一个,使用另外一个的时候直接使用其 Full-qualified name,即:
import long.walks.*;
//using full-qualified name to make the declaration
swim.surf.Beach tybee;
Java 使用 printf()
来控制输出格式:
printf()
由成对的 placeholder 与变量组成,可以存在多组%
开头,按位置对应变量
下面例子中,%s
代表的是 String
类型的 placeholder, 对应 day
;%f
代表 double
/ float
类型的 placeholder, 对应 celsius
:
System.out.printf("%s Celsius: %f\n", day, celsius);
printf()
也不会自动换行,因此需要再 %f
后加上一个 \n
%
,需要输入 %%
placeholder 的结构如下:
%[flag][width][.precision]type
中间方括号的内容是可选的部分,type 是指之前例子中的 s
等。具体的来说:
d
代表整型f
代表 double
或是 float
s
代表 String
再来看看可选部分:
.percision
。比如 25.6666666
,如果希望只打印一位小数的话,使用 .1%f
:
double fahd = 25.6666666
System.out.printf("The current temperture in F is: %.1f\n" ,fahd);
打印结果为 25.7
,最后一位进行了四舍五入。
width
。控制宽度的具体意思是,无论被打印的内容有多长,显示的内容长度都会占 witdh
指定的数据。如果给定的数据没有这么长,则使用空格填充。这个参数对自动对齐非常有用,比如:
System.out.printf("The temperture is: %4.1f\n" ,fahd);
System.out.printf("Next day is: %10s", day);
这里设置 fahd
占用 4 个长度,day
占用 10 个长度,打印的结果正好上下对齐。因此,不管 fahd
和 day
的长度是多少,这两行都会对其在文本末尾:
The temperture is: 3.1
Next day is: Monday
The temperture is: 82.1
Next day is: Tuesday
默认情况下长度是按照右对齐的。如果希望左对齐,将 witdh
的值改为负数:
System.out.printf("The temperture is: %-4.1f\n" ,fahd);
System.out.printf("Next day is: %-10s", day);
Please enter the day: Monday
The temperture is: 3.1
Next day is: Monday
The temperture is: 82.1
Next day is: Tuesday
String
提供了一个 method format
,与 printf()
功能一致。唯一不同的是,它会将格式化的 String
对象返回,因此需要一个变量来存储返回值:
String celsiusOutput = String.format("%s %-11s %,.1f \n", day, cText, celsius);
Java 提供了一个 package: NumberFormat
,专门用于自动将数字打印为对应的货币格式。其导入方式为:
import java.text.NumberFormat;
在打印的时候需要建立一个 NumberFormat
对象,使用该对象调用 getCurrencyInstance()
method 得到对应的货币字符串,再使用 format
method 对其进行输出:
import java.text.NumberFormat;
double total = 25.666666;
NumberFormat currencyFmt = NumberFormat.getCurrencyInstance();
System.out.println("Total is: " + currencyFmt.format(total));
打印结果为:
Total is: ?25.67
可以看到货币符号为 ?
,输出结果格式化为小数点后两位。如果希望打印指定国家的货币符号,需要配合 util
下的 Locale
package 一起使用:
import java.text.NumberFormat;
import java.util.Locale;
double total = 25.666666;
NumberFormat currencyFmt = NumberFormat.getCurrencyInstance(Locale.CANADA);
System.out.println("Total is: " + currencyFmt.format(total));
打印结果为:
Total is: $25.67
Docs: check here
DecimalFormat
是 Java 提供的,按照 parrtern 输出的数字的 package。同 NumberFormat
类似,DecimalFormat
需要建立对象,然后通过该对象调用 format()
method 来实现目的。几种常用的用法有:
0
:0
用于占位使用,代表当前位至少会有数字显示。该用法区分几种情况:
如果 0
的位数大余实际数字的位数:那么多出来的位数会被 0
填充:
double num = 13.1;
DecimalFormat outNum = new DecimalFormat("000.00");
// print 013.10
System.out.println(outNum.format(num));
如果 0
的位数等于实际数字的位数:那么打印当前数字:
double num = 13.1;
DecimalFormat outNum = new DecimalFormat("00.0");
// print 13.1
System.out.println(outNum.format(num));
如果 0
的位数小于实际数字的位数:整数部分打印数字的实际位数,小数部分按照 0
的位数进行四舍五入:
double num = 1313.11933334;
DecimalFormat outNum = new DecimalFormat("000.00");
// print 1313.12
System.out.println(outNum.format(num));
%
:如果 pattern 的末尾带有带有 %
,那么数字会以百分比的形式输出,比如:
double num = 0.119333334;
DecimalFormat outNum = new DecimalFormat("00.00%");
// print 11.93%
System.out.println(outNum.format(num));
注意这里小数部分,也就是 .00
的占位,会直接应用到百分比上;也就是原来小数部分的第四位。
#
:用法与 0
基本相同,唯一的差别是不会显示 0
:
double num = 0.119333334;
DecimalFormat outNum = new DecimalFormat("#.00");
// print .12
System.out.println(outNum.format(num));
用法 与 C++ 相同,格式:
if (condition) {
statement;
//...
}
else if (condition) {
statement;
//...
}
else {
statement;
//...
}
if / else 配对是找最近的彼此,与 C++ 相同。
同 C++。 格式:
switch (expression) {
case value1:
statement(s)
break;
case value2:
statement(s)
break;
default:
statement(s)
}
expression
可以是任意的 primtive type, 以及结果是 primtive type、emum、Character、Byte、Short、Interger(注意大小写,这些是和 primtive 不一样的类型)break
。忘记 break
会导致会导致 switch 继续执行接下来的语句。
Java 中比较两个 String 的时候,通常会出现两种方式:==
和 x.equals(y)
。这两种方式比较的内容不同:
==
比较的是两个 String 的 reference 是否指向同一个地址,也就是两个 String 是否是同一个 instanceequals()
只是再单纯的比较两个 String 的内容是否相同。以上的区别解释了 Java 为什么会存在两种初始化 String 的方式:
//method 1
String xOne = "park";
//method 2
String yOne = new String("park");
如果是第一种方式,那么 park
只会在第一次创建时在堆上申请空间。Java 在堆上有一个专门的空间被称作 string constant pool 用于存储这类 string literial。当之后再使用 assignement 创建相同内容的 string literial 时,创建的对象会直接引用到第一次 创建时的地址。也就是说,所有非 new 方式的 String 对象创建均指向第一次创建该对象时的地址。
String xOne = "park";
String xTwo = "park";
String yOne = new String("park");
//True, same address
if (xOne == xTwo) { //......}
//True, same content
if xOne.equals(xTwo) {//... }
//False, different address
if (xOne == yOne ) { //...}
//True, same content
if xOne.equals(yOne) {//... }
compareTo
method 来比较
compareTo()
method 会将 String 中的每一位字符转换为 ASCII 值,并按位进行比较。compareTo()
的逻辑(以 a.compareTo(b)
为例):
int
,为正代表 a > b
,为负代表 a < b
。int
的是两个字符之间的 ASCII 码值之差a
,b
之间,有一个 String 是另外一个 的 subString,则返回的 int
是两者长度之差 一些实例:
String a = "pppp";
String b = "pppq";
String c = "ppppbbb";
// return -1
System.out.println(a.compareTo(b));
// return -3
System.out.println(a.compareTo(c));
// return 1
System.out.println(b.compareTo(c));
!
> &&
> ||
Java 在逻辑表达式评估中会首先评估 left operand:
&&
,如果值为 false 则不会再对 right operand 进行评估。||
,如果值为 true 则不会再对 right operand 进行评估。这种评估方式被称为 Short circuit evaluation。
尽量将运算复杂的条件作为 right operand,比如 function call 之类的。
与 C++ 一致。
condition ? expression1 : expression2
使用输入作为条件的循环:
int condition = myInput.nextInt();
while(condition != 100)
{
// ...do sth
condition = myInput.nextInt();
}
< = looptimes
声明的方式:
//both are fine
//first is recommonded
elementType[] identifier;
elementType identifier[];
定义的方式:
//default value of elements is 0.0
double[] weekHighs = new double[7];
0
/ 0.0
false
nulll
(java 关键字,表示对象没有 address)
//direct init
double[] weekHighs = {80, 70, 75, 69, 72, 74, 90};
//assignment init
double[] weekHighs; //declaration
weekHighs = {80, 70, 75, 69, 72, 74, 90}; //initialization
//accessing by index(starts from 0, end at len - 1)
weekHighs[0];
//modifiying
weekHighs[0] = 80;
//modifiying with the value from input
weekHighs[0] = input.nextDouble();
//accessing the length
weekHighs.length;
//looping array
for (int i = 0; i < weekHighs.length; ++i) {
System.out.println(weekHighs[i]);
}
定义如下:
for (arrayType element : array) {...}
//example
for (int element : weekHighs) {
System.out.println(element);
}
如果遍历的数组是对象数组(比如 String array):
null
null
的元素,该 array 被称为 sparse array
For-each 在遍历 sparse array 时,如果遇到值为 null
的元素并且需要与其比较的话,会抛出 NullPointerException。解决的方法是做一个 null check:
String concepts[] = new String[5];
for (int i = 0; i < 4; ++i) {
concepts[i] = "item";
}
String result = "not found.";
for (String element : concepts) {
if ((element != null) && element.equals("items")) {
result = "found";
break;
}
}
System.out.println(result);
注意这里的 null check 必须要优先检查,也就是至于 &&
之前。如果比较之于检查之前发生,那么依然会抛出异常。
Java 的 main()
函数自带一个 String array,这个 array 会接受来自于命令行的内容,元素以空格区分。我们可以从命令行对该数组进行初始化:
//calculating average in args[]
double total = 0;
for (String num : args) {
total += Double.parseDouble(num);
}
System.out.println(total / args.length);
编译之后,只需要从命令行输入数据就可以得到结果了:
java Array 10 10 10 10 20
12.0
需要注意的是,程序中自带了:
total += Double.parseDouble(num);
Double
是 Java 提供的一个 package, parseDouble()
是其中的一个 method,用于将 String 转化为 double。
array[row][col]
,row
的元素是 array 的首地址,col
是每个 array 中元素的 index, 元素内容是 array 中的元素或(地址)
//declaration
elementType[][] indentifier;
indentifier elementType[][];
//direct init
double[][] array2d = {{80,70,75}, {69, 72, 74}};
//init with diaensions, with default value
//2 arrays, each has 3 double elements
double[][] array2d = new double[2][3]
//assignments
array2d[0][0] = 80;
//traversing 2d-array
//array2d.length is how many arrays that array2d has
//array2d[0].length is how many elements that each array has
for (int row = 0; row < array2d.length; ++row) {
for (int col = 0; col < array2d[0].length; ++ col) {
System.out.println(array2d[row][col]);
}
}
必须记得使用 .length() method。2d array 中很可能存在 array 长度不一的情况。
Java 的 method 定义分为四大块:
public static
。public
代表的是该 method 的 visibility,也就是说该 method 可见,并且可以被其他类型的对象调用。static
表示该 method 不基于 obejct(也就是没有 this,不是成员函数),使用时无需创建对象void
简单的示例:
public static String serachKeywords(String keyword, String[] array) {
String result = "not found.";
for (String element : array) {
if (element != null && element.equals(keyword)) {
result = "found";
break;
}
}
return result;
}
// calling
public static void main(String[] args) {
String[] myDict = {"love", "me"};
String result = serachKeywords("love", myDict);
System.out.println(result);
}
假设 serachKeywords()
被定义于 Methods.java
。假设我们希望在另一个文件 External.java
中调用 serachKeywords()
:
Methods.java
与 External.java
置于同一目录Methods.serachKeywords()
进行调用
public class External {
public static void main(String[] args) {
String[] newArray = {"123", "456", "789"};
System.out.println(Methods.serachKeywords("123", newArray));
}
}
// overloading
public static boolean searchArray(String target, String[] array)
public static boolean searchArray(int target, int[] array)
// not a overloading
public static int searchArray(int target, int[] array)
public static boolean searchArray(int target, int[] array)