目录

Foundations and Syntax Basics

Course I notes


Module 1

Introduction to Java

Histroy

Java 的基本运行条件

//class
public class project {
    //method
    public static void main(String[] args) {
        //statements
        String x = "Hello world";
        //print line
        System.out.println(x);
    }
}

java 的文件名

Executing Java Programs

两种类型的程序转换
Java's Hybrid Approach
  • Java 的解释器被称为 Java Virtual Machine(JVM)
  • 这种混合的方式被称为 Just-In-Time compilation
Java 程序的编译与执行

# 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

The Java Development Kit

Java Development Kit(JDK) 是 java 用于编译和执行文件的工具集合。与之前概念一些区别:

三者从上到下是依次包含的关系。

Why OOP

Identifier

java 的类名,函数名,变量名等一切由用户自定义的 name 被称为 identifier:

Java 的数据类型
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

Objects 的两种属性:

Module 2

Basics

whitespace

Errors

Compile and Runtime Errors
Logical Errors
  • 推荐写完每一个 method 之后测试一次
  • 添加 comments
Comments

// 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
*/

Variables and Constants

constant 的定义

final double PI = 3.1415926

Primitive Types
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}]$
double64 bits $[1.7976931348623157 x 10^{308}, 4.9406564584124654 x 10^{-324}]$
default type

因此,如下的语句会错误:

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

定义 char 使用单引号(single qoute):

char yes = 'Y'

double quote 代表 string,不能用于 char

boolean

boolean 类型的定义:

boolean parked = true;

escape sequences

使用 back slash 作为 escape sequence 的开始,表示特殊字符:

// single quote
\'
// double quote
\"
// back slash
\\
// tab
\t
// new line
\n
// carriage return
\r

Arithmetic Expressions

Integer Division

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 Arithmetic

String 的加法代表链接两个 string:

//result is "1331"
"13" + "31"

order of precedence
Mixed Type Expressions

在有不同类型参与的表达式计算中,Java 会对某些不合要求的 operand 进行 “promotion”,以此达到完成运算的目的。具体的类说:

Conversion

分为两种:

Assignment Conversion

double average = 4.0;
int gpa = average;
就是非法的,因为 int 不能表示 double。因此,导致丢失精度的 assignment 是非法的。

Casting

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 不单用于算术类型。

Using Predefined Classes

Declaring Variables
Instantiation

Java 中 Object 的实例化被称为 Instantiation。写法如下:

new ClassName(parameters)
实例化通过构造函数(constructor)建立。以 String 类为例,其构造函数为 String
String major = new String("computer scinece);

Invoking Methods

Methods 通过 instance 的 identfier,也就是某个类的 reference 进行调用。比如打印:

System.out.println("Hello");
//no newline version
System.out.print("Hello");
out 是指向 PrintStream 类对象的引用。System 是 JAVA STL 的一部分。

实例化的只有对象的数据。methods 属于 class,是唯一的。

Java 中的拷贝

跟 C++ 不同的是,如果在 Java 中更改了指向某个地址的引用,但忘记了释放指向的资源,Java 会在随后的恰当时间内自动回收这部分资源。这个功能被称为 Java 的 Garbage Collection

String methods

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)

formal & actual parameter
substring index

Input & MutiInput

Scanner

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)。

整个输入环节的顺序:

  1. 实例化 Scanner 对象
  2. 使用 Scanner 对象调用对应的 Method (比如我要一个 int 就用 nextInt())
  3. 再创建一个变量接受 Scanner 对象调用 Method 的结果。

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);
    }
}

  • Scanner 需要被导入才能使用:import java.util.Scanner;
  • next 系列 method (除开 nextLIne()) 会自动忽略 Token 前后的所有 whitespace
  • 输入类型不匹配的数据会导致抛出异常
  • hasNext 系列可以检查 Token 的数据类型。
hasNext 的用法

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。这会带来什么样的后果呢?

首先要明确的是存在多个输入的情况时,我们的输入流程:

  1. 输入数据
  2. 回车输入下一个数据

这里的回车(Enter)实际上产生的效果是添加 \n 到流的末尾。假设我们输入一个数 78,那么回车以后,78 存储到本地变量,流就会变为 \n。之前提到过,其他 next 系列的 Methods 都会忽略 Token 之前的 \n,除了 nextLine(); 因此,其他的 next method 都会在此处等待用户的下一次输出,但 nextLine() 会在这里读取到的首个 Token \n,这将导致当前的输入立即结束。

输入数据 + 回车 + 输入数据只是一种流程,原理上是使用了 \n 作为 delimiter。但我们也可以通过 space 来做 delimiter,比如本节开头的,使用 next() 例子中,如果使用 78 monday 只输入一行,也可以达到效果。此时流通过 space 将 Token 分为了 78mondayfah 接受了 78, day 接受了 monday,因此只需要输入一行也能达到效果。

nextLine 的应用场景

使用 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();

Packages & import

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。

using wildcard in import

如果需要导入当前 package 的所有内容,可以使用通配符(wildcard) *(asterisk):

import java.util.*;

confilct in import

假设有如下的两个 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;

Output & Formating

printf()

Java 使用 printf() 来控制输出格式:

下面例子中,%s 代表的是 String 类型的 placeholder, 对应 day%f 代表 double / float 类型的 placeholder, 对应 celsius

System.out.printf("%s Celsius: %f\n", day, celsius);

  • printf() 也不会自动换行,因此需要再 %f 后加上一个 \n
  • 如果希望打印 %,需要输入 %%
placeholder

placeholder 的结构如下:

%[flag][width][.precision]type
中间方括号的内容是可选的部分,type 是指之前例子中的 s 等。具体的来说:

再来看看可选部分:

double fahd = 25.6666666
System.out.printf("The current temperture in F is: %.1f\n" ,fahd);
打印结果为 25.7,最后一位进行了四舍五入。

System.out.printf("The temperture is: %4.1f\n" ,fahd);
System.out.printf("Next day is: %10s", day);
这里设置 fahd 占用 4 个长度,day 占用 10 个长度,打印的结果正好上下对齐。因此,不管 fahdday 的长度是多少,这两行都会对其在文本末尾:
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.format

String 提供了一个 method format,与 printf() 功能一致。唯一不同的是,它会将格式化的 String 对象返回,因此需要一个变量来存储返回值:

String celsiusOutput = String.format("%s %-11s %,.1f \n", day, cText, celsius);

NumberFormat

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

DecimalFormat

Docs: check here

DecimalFormat 是 Java 提供的,按照 parrtern 输出的数字的 package。同 NumberFormat 类似,DecimalFormat 需要建立对象,然后通过该对象调用 format() method 来实现目的。几种常用的用法有:

如果 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));

double num = 0.119333334;
DecimalFormat outNum = new DecimalFormat("00.00%");
// print 11.93%
System.out.println(outNum.format(num));
注意这里小数部分,也就是 .00 的占位,会直接应用到百分比上;也就是原来小数部分的第四位。

double num = 0.119333334;
DecimalFormat outNum = new DecimalFormat("#.00");
// print .12
System.out.println(outNum.format(num));

Module 3

if & else statment

用法 与 C++ 相同,格式:

if (condition) {
    statement;
    //...
}
else if (condition) {
    statement;
    //...
}
else {
    statement;
    //...
}

if / else 配对是找最近的彼此,与 C++ 相同。

The switch Statement

同 C++。 格式:

switch (expression) {
    case value1:
            statement(s)
            break;
    case value2:
            statement(s)
            break;
    default:
            statement(s)
        }

Comparing Non-numeric Data

equals method

Java 中比较两个 String 的时候,通常会出现两种方式:==x.equals(y)。这两种方式比较的内容不同:

以上的区别解释了 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 对象创建均指向第一次创建该对象时的地址。

第二种方式会在堆上强制申请新的空间,因此其内容与第一种方式相同,但代表的 instance,在内存中的地址是不一样的。 因此:
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) 为例):

  1. 返回值为 int,为正代表 a > b,为负代表 a < b
  2. 按位比较,找出第一位不同的字符。返回的 int 的是两个字符之间的 ASCII值之差
  3. 如果 ab 之间,有一个 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));

Operators

Logical Operators
Short-circuit Evaluation

Java 在逻辑表达式评估中会首先评估 left operand:

这种评估方式被称为 Short circuit evaluation

尽量将运算复杂的条件作为 right operand,比如 function call 之类的。

Ternary Conditional Operator

与 C++ 一致。

condition ? expression1 : expression2

Iteration

while

使用输入作为条件的循环:

int condition = myInput.nextInt();
while(condition != 100)
{
    // ...do sth
    condition = myInput.nextInt();
}

Do-while
For
break & continue

Module 4

Arrays

Array 的定义

声明的方式:

//both are fine
//first is recommonded
elementType[] identifier;
elementType identifier[];
定义的方式:
//default value of elements is 0.0
double[] weekHighs = new double[7];

Default Values
Specifying Values

//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();

Using Arrays

//accessing the length
weekHighs.length;

//looping array
for (int i = 0; i < weekHighs.length; ++i) {
    System.out.println(weekHighs[i]);
}

The for-each Statement

定义如下:

for (arrayType element : array) {...}
//example
for (int element : weekHighs) {
    System.out.println(element);
}

Sparse Arrays and Null Checking

如果遍历的数组是对象数组(比如 String 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 必须要优先检查,也就是至于 && 之前。如果比较之于检查之前发生,那么依然会抛出异常。

Command-line Arguments

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。

2-D Arrays

//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 长度不一的情况。

Methods

method 的定义

Java 的 method 定义分为四大块:

简单的示例:

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);
}

外部调用 method

假设 serachKeywords() 被定义于 Methods.java。假设我们希望在另一个文件 External.java 中调用 serachKeywords()

public class External {
    public static void main(String[] args) {
        String[] newArray = {"123", "456", "789"};
        System.out.println(Methods.serachKeywords("123", newArray));
    }
}

method overloading

// 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)