What & How & Why

Foundations and Syntax Basics

Course I notes


Module 1

Introduction to Java

Histroy

Java 的基本运行条件
  • 至少有一个 Method
  • 至少有一个被称之为 main 的 method
  • mehod 需要被包含在 class 里
  • 程序至少要一个类

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

java 的文件名
  • 文件的名字必须与文件中的类名匹配
  • 后缀为 .java

Executing Java Programs

两种类型的程序转换
  • 编译器(Compiler):将整个程序一次性转换为机器码(二进制),然后再执行
    • 速度快:只用编译一次即可反复使用
    • 不同的机器支持的指令集不同(需要的中间文件也不一样),因此编译器是基于机器来设计的。因此,在一台机器上编译好的程序很可能不能在另外一台机器上跑。如果需要跨平台,那么针对每个平台都要编译一次。
  • 解释器(Interpreter):会立即转化(不会有 pre-processing),没有中间文件,逐行执行
  • 速度慢,每次运行都要执行
  • 没有中间文件的生成,因此可以在任何电脑上执行。
Java's Hybrid Approach
  • Java 不会直接编译基于机器码的中间文件,而是编译程序为 bytecode 类型的文件,存储在 .class 文件中。bytecode 文件不基于指令集,但足够接近底层。
  • 当编译了 bytecode 文件后,Java 会以解释器的方式,将 bytecode 文件转换为特定的机器语言。
  • 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 用于编译和执行文件的工具集合。与之前概念一些区别:

  • Java Development Kit(JDK) : 包含了写 java 的工具,以及编译 java 需要的环境(JRE)。跨平台
  • Java Runtime Environment(JRE):包含对执行 java 程序的支持。跨平台
  • Java Virtual Machine(JVM):将编译器生成的 bytecode 文件转化为对应机器的机器码文件。跨平台

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

Why OOP

Identifier

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

  • identifier 可以包括:字母(letters),数字(digits),下划线 _,和美元符号 $
  • 数字不能作为 name 的开头
  • identifier 不能为关键字(均为小写)
  • indentier 区分大小写
  • Java 使用骆驼写法(头字母小写)来对 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 的两种属性:

  • attribute 表示状态,比如车的颜色,大小,品牌
  • actions 代表行为,比如点火,熄火,踩刹车

Module 2

Basics

whitespace
  • blanks, tabs, newline
  • 编译器将其视作分界线
  • 增强代码的可读性

Errors

  • Error types: Complier, Runtime, Logical
  • Smantics vs systax : meaning of the code vs coding rule
Compile and Runtime Errors
  • Complie error: 指程序在编译过程中出现的错误,具体指systax error
  • Runtime error : 指程序在执行中出现的错误(比如除零)
Logical Errors
  • Logial error 指 semetics error
  • 推荐写完每一个 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

  • variable 拥有自己的 scope
  • 其区域通常以 method 为单位
constant 的定义
  • 使用 final 关键字:

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
  • 整型的 default type 是 int
  • 浮点型的 default type 是 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
  • char 的排序取决于 character set
  • Java 使用 unicode 作为 character set,该 set 是 ASCII 的超集

定义 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

  • 包括 operand 和 operator
Integer Division

Java 的整数除法会完全忽略小数部分,比如 9/2 = 4。如果希望得到浮点数结果:

  • 将任意一个 operand 改为浮点数
  • 强制转化 operand 为浮点数类型:

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”,以此达到完成运算的目的。具体的类说:

  • 整型和浮点型:整型会被提升为浮点型
  • 任何类型与 string 进行算术相加,都会被提升为 string,再进行连接。

Conversion

分为两种:

  • Assignment Convenrsion
  • Casting
Assignment Conversion
  • Coversion 是否合法取决于数据类型的范围是否能表示数据。比如:

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
  • Objects 变量被称为 reference 变量。其本身值是引用。
  • Java 中的对象存储在 heap 中,通过引用找到地址进行访问。
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 中的拷贝
  • primtive 类型的拷贝是直接复制
  • 对象名存储的是引用,因此拷贝对象等于拷贝引用,也就是复制地址。两者指向同一个实例。这种情况下,两者被称为 alias

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

String methods

  • Signiture Type:描述 method 的方式,包括了 method name, parameter 的数量及其位置。
  • String literal (比如 “Hello”) 也是 String 实例,也可以直接调用 String method。
  • String is IMMUTABLE!

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
  • method 使用的 paramter 被称为 actual parameter(实参,argument),method 定义中的 paramter 被称为 formal parameter(形参, parameter)
  • 直接传递 actual paramter 给 formal paramter 时,actual parameter 会拷贝后再传递给 formal parameter。这个过程被称为 pass by value
substring index
  • Java 的 index 也是从 $0$ 开始。
  • Index error 属于 runtime error。编译器不会检查

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() 来控制输出格式:

  • printf() 由成对的 placeholder 与变量组成,可以存在多组
  • placeholder% 开头,按位置对应变量

下面例子中,%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 等。具体的来说:

  • 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 个长度,打印的结果正好上下对齐。因此,不管 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 来实现目的。几种常用的用法有:

  • 数字 00 用于占位使用,代表当前位至少会有数字显示。该用法区分几种情况:

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

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

  • expression 可以是任意的 primtive type, 以及结果是 primtive type、emum、Character、Byte、Short、Interger(注意大小写,这些是和 primtive 不一样的类型)
  • 不要忘记 break。忘记 break 会导致会导致 switch 继续执行接下来的语句。

Comparing Non-numeric Data

  • 字符的大小比较通过 ASCII 码来比较
equals method

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

  • == 比较的是两个 String 的 reference 是否指向同一个地址,也就是两个 String 是否是同一个 instance
  • equals() 只是再单纯的比较两个 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 对象创建均指向第一次创建该对象时的地址。

第二种方式会在堆上强制申请新的空间,因此其内容与第一种方式相同,但代表的 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
  • String 不能用关系运算符比较大小
  • 需要使用 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
  • 与 C++ 相同。
  • 优先级:
    • 括号最优先(推荐使用)
    • ! > && > ||
Short-circuit Evaluation

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

  • 对于 &&,如果值为 false 则不会再对 right operand 进行评估。
  • 对于 ||,如果值为 true 则不会再对 right operand 进行评估。

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

  • 如何利用 short circuit evaluation?

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

Ternary Conditional Operator

与 C++ 一致。

condition ? expression1 : expression2

Iteration

while
  • 写法与 C++ 相同
  • 循环的条件一般写称左闭右开,也就是初始次数包含在条件里,而终止条件是小于循环次数。

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

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

Do-while
  • 写法与 C++ 相同
  • 循环次数是额外的,不包括初始运行的那一次,可以写成 < = looptimes
For
  • 同 C++
  • 循环变量是局部变量
  • 变量改变部分是运算表达式
break & continue
  • 同 C++

Module 4

Arrays

  • Java 的 Array 性质与 C++ 类似:长度和类型都是被定义的。
  • Array 是指向内存序列的首地址。
  • 每个 Array 都会附带一个 final variable(常量)用于存储数组的长度。
  • Java 的 Array 可以存储 Objects;准确的说,是存储指向 Objects 的 references
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
  • Primtive numeric type 的初始值是 0 / 0.0
  • boolean 的初始值是 false
  • char 的初始值为空(不打印)
  • String 的初始值为 nulll(java 关键字,表示对象没有 address)
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
  • 类似 C++ 的 Range based loop
  • 优势是不用担心 index 越界

定义如下:

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

Sparse Arrays and Null Checking

如果遍历的数组是对象数组(比如 String array):

  • Java 默认该数组为 non-sparse 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 必须要优先检查,也就是至于 && 之前。如果比较之于检查之前发生,那么依然会抛出异常。

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
  • 实质上是存储了一系列 array 首地址 的 array
  • 可以想象为 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 长度不一的情况。

Methods

method 的定义

Java 的 method 定义分为四大块:

  • modifier:处于定义的最开始,一般是 public static
    • public 代表的是该 method 的 visibility,也就是说该 method 可见,并且可以被其他类型的对象调用。
    • static 表示该 method 不基于 obejct(也就是没有 this,不是成员函数),使用时无需创建对象
  • return value type:如果没有返回类型,则使用 void
  • parameter list:method 需要的参数列表,必须指定类型。
  • method body: 如果 method 有返回类型,必须返回一个类型为返回类型的值,使用 return 语句。

简单的示例:

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

  • Methods.javaExternal.java 置于同一目录
  • 使用 Methods.serachKeywords() 进行调用

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

method overloading
  • 与 C++ 类似,相似功能但是 method 的 singature 不同,也就是至少满足下面一项:
    • parameter 的类型不同
    • parameter 的顺序不同
    • parameter 的数量不同
  • return type 不是 signature 的一部分,因此只有 return type 不同不是 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)

  • 编译器会根据 parameter 的类型来进行函数匹配。如果没有匹配的函数会报错。
  • 参考 C++ 的函数匹配(针对 overloading 的二义性)