程序基本结构

因为Java是面向对象的语言,一个程序的基本单位就是classclass是关键字

1
2
3
public class Hello { // 类名是Hello
// ...
} // class定义结束

类名要求:

  • 类名必须以英文字母开头,后接字母,数字和下划线的组合
  • 习惯以大写字母开头

class内部,可以定义若干方法。方法定义了一组执行语句,方法内部的代码将会被依次顺序执行。

Java入口程序规定的方法必须是静态方法,方法名必须为main,括号内的参数必须是String数组。

方法命名要求:

  • 首字母小写

Java的每一行语句必须以分号结束

注释:和c语言一样

还有一种特殊的多行注释,以/**开头,以*/结束,如果有多行,每行通常以星号开头:

1
2
3
4
5
6
7
8
9
10
/**
* 可以用来自动创建文档的注释
*
* @auther liaoxuefeng
*/
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

这种特殊的多行注释需要写在类和方法的定义处,可以用于自动创建文档。

变量 && 数据类型

变量的默认值总是0, 使用方法和C语言相同。

基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:

  • 整数类型:byte,short,int,long
  • 浮点数类型:float,double
  • 字符类型:char
  • 布尔类型:boolean

计算机内存的最小存储单元是字节(byte),一个字节就是一个8位二进制数,即8个bit。 换算成十进制是0255,换算成十六进制是00ff

基本数据类型占用的字节数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
       ┌───┐
byte │ │
└───┘
┌───┬───┐
short │ │ │
└───┴───┘
┌───┬───┬───┬───┐
int │ │ │ │ │
└───┴───┴───┴───┘
┌───┬───┬───┬───┬───┬───┬───┬───┐
long │ │ │ │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
┌───┬───┬───┬───┐
float │ │ │ │ │
└───┴───┴───┴───┘
┌───┬───┬───┬───┬───┬───┬───┬───┐
double │ │ │ │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
┌───┬───┐
char │ │ │
└───┴───┘

整型

各种整型能表示的最大范围如下:

  • byte:-128 ~ 127
  • short: -32768 ~ 32767
  • int: -2147483648 ~ 2147483647(10位)
  • long: -9223372036854775808 ~ 9223372036854775807
1
long l = 9000000000000000000L; // long型的结尾需要加L

注意:同一个数的不同进制的表示是完全相同的,例如15=0xf0b1111

浮点型

因为小数用科学计数法表示的时候,小数点是可以“浮动”的,如1234.5可以表示成$ 12.345\times10^2 $,也可以表示成$ 1.2345\times10^3 $ ,所以称为浮点数。

1
2
3
float f1 = 3.14f;	 // float类型,需要加上f后缀。
float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
double d = 1.79e308;

float类型可最大表示$3.4\times10^{38}$,而double类型可最大表示$1.79\times10^{308}$。

布尔类型

1
2
boolean b1 = true;
boolean isGreater = 5 > 3; // 计算结果为true

字符类型

字符类型char表示一个字符。Java的char类型除了可表示标准的ASCII外,还可以表示一个Unicode字符

**注意char类型使用单引号',且仅有一个字符,要和双引号"的字符串类型区分开。 **

引用类型

除了上述基本类型的变量,剩下的都是引用类型。例如,引用类型最常用的就是String字符串:

1
String s = "hello";

引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置

输出某个位置的字符:

1
System.out.println(str.charAt(i))

常量

定义变量的时候,如果加上final修饰符,这个变量就变成了常量:【 常量名通常全部大写 】

1
final double PI = 3.14; // PI是一个常量

字符和字符串

因为Java在内存中总是使用Unicode表示字符,所以,一个英文字符和一个中文字符都用一个char类型表示,它们都占用两个字节。要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可。

还可以直接用转义字符\u+Unicode编码来表示一个字符:

1
2
3
// 注意是十六进制:
char c3 = '\u0041'; // 'A',因为十六进制0041 = 十进制65

字符串类型String是引用类型,我们用双引号"..."表示字符串。

注意要区分空值null和空字符串"",空字符串是一个有效的字符串对象,它不等于null

字符串连接

可以使用+连接任意字符串和其他数据类型,这样极大地方便了字符串的处理。 如果用+连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接 。

从Java 13开始,字符串可以用"""..."""表示多行字符串(Text Blocks):

1
2
3
4
5
6
7
String s = """		
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";
// 注意: 多行字符串前面共同的空格会被去掉

如果多行字符串的排版不规则,那么总是以最短的行首空格为基准。

最后,由于多行字符串是作为预览特性(Preview Language Features)实现的,编译的时候,我们还需要给编译器加上参数:

1
2
javac --source 14 --enable-preview Main.java

字符串数组

1
2
3
4
String[] names = {
"ABC", "XYZ", "zoo"
};

$var$ 关键字

如果类型名字太长写起来会比较麻烦,如果想省略变量类型可以使用var关键字:

1
2
3
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();

数组

1
2
3
4
5
6
int[] ns = new int[5];
System.out.println(ns.length); // 5

// 也可以:
int[] ns = new int[] { 68, 79, 91, 85, 62 };

多维数组

1
2
3
4
5
6
7
8
9
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(ns.length); // 3
int[] arr0 = ns[0];
System.out.println(arr0.length); // 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int[][][] ns = {
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
},
{
{10, 11},
{12, 13}
},
{
{14, 15, 16},
{17, 18}
}
};

运算

整数除法的结果只保留整数部分。

+=-=*=/=++-- 这些运算操作Java都有。

类型自动提升与强制转型

在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型。例如,shortint计算,结果总是int,原因是short首先自动被转型为int

1
2
3
int i = 12345;
short s = (short) i; // 12345 语法和C一样

溢出

整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值:

  • NaN表示Not a Number
  • Infinity表示无穷大
  • -Infinity表示负无穷大

输入和输出

println是print line的缩写,表示输出并换行。因此,如果输出后不想换行,可以用print()

1
System.out.print("..");

格式化输出

如果要把数据显示成我们期望的格式,就需要使用格式化输出的功能。格式化输出使用System.out.printf(),通过使用占位符%?printf()可以把后面的参数格式化成指定格式

1
2
double d = 3.1415926;
System.out.printf("%.2f\n", d); // 显示两位小数3.14

输入

一个从控制台读取一个字符串和一个整数的例子:

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建Scanner对象
System.out.print("Input your name: "); // 打印提示
String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.print("Input your age: "); // 打印提示
int age = scanner.nextInt(); // 读取一行输入并获取整数
System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
}
}

System.out代表标准输出流,而System.in代表标准输入流。直接使用System.in读取用户输入虽然是可以的,但需要更复杂的代码,而通过Scanner就可以简化后续的代码。

有了Scanner对象后,要读取用户输入的字符串,使用scanner.nextLine(),要读取用户输入的整数,使用scanner.nextInt()Scanner会自动转换数据类型,因此不必手动转换。

输入字符串 遇到空格或者换行结束 :

1
2
3
Scanner sc=new Scanner(System.in);
String str=new String();
str=sc.next();

输入一行字符串,可以包括空格:

1
2
3
Scanner sc=new Scanner(System.in);
String str=new String();
str=sc.nextLine();

判断

1
2
3
if (条件) {
// 条件满足时执行
}

判断值类型的变量是否相等,可以使用==运算符。但是,判断引用类型的变量是否相等,==表示“引用是否相等”,或者说,是否指向同一个对象。

1
2
3
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
// s1 != s2

要判断引用类型的变量内容是否相等,必须使用equals()方法:

1
2
3
if (s1 != null && s1.equals(s2)) {		// 避免NullPointerException错误
System.out.println("s1 equals s2");
}

switch

1
2
3
4
5
6
7
8
9
10
11
12
String fruit = "apple";
switch (fruit) {
case "apple":
System.out.println("Selected apple");
break;
case "pear":
System.out.println("Selected pear");
break;
default:
System.out.println("No fruit selected");
break;
}

从Java 12开始,switch语句升级为更简洁的表达式语法,使用类似模式匹配(Pattern Matching)的方法,保证只有一种路径会被执行,并且不需要break语句:

1
2
3
4
5
6
7
8
9
10
11
String fruit = "apple";
switch (fruit) {
case "apple" -> System.out.println("Selected apple");
case "pear" -> System.out.println("Selected pear");
case "mango" -> {
System.out.println("Selected mango");
System.out.println("Good choice!");
}
default -> System.out.println("No fruit selected");
}

还可以直接返回值:

1
2
3
4
5
6
7
8
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> 0;
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);

如果需要复杂的语句,我们也可以写很多语句,放到{...}里,然后,用yield返回一个值作为switch语句的返回值:

1
2
3
4
5
6
7
8
9
10
11
String fruit = "orange";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> {
int code = fruit.hashCode();
yield code; // switch语句返回值
}
};
System.out.println("opt = " + opt);

循环

1
2
3
while (条件表达式) {
循环语句
}
1
2
3
do {
执行循环语句
} while (条件表达式);
1
2
3
4
5
6
7
for (初始条件; 循环检测条件; 循环后更新计数器) {
// 执行语句
}
for (int i=0; i<ns.length; i++) {
int n = ns[i];
System.out.println(n);
}

命令行参数

Java程序的入口是main方法,而main方法可以接受一个命令行参数,它是一个String[]数组。

这个命令行参数由JVM接收用户输入并传给main方法:

1
2
3
4
5
6
7
8
9
10
11
public class TestArgs {
public static void main(String[] args){
for(String arg : args) {
System.out.println(arg);
if ("-version".equals(arg)) {
System.out.println("v 1.0");
break;
}
}
}
}

命令行执行:

1
2
3
$ javac Main.java
$ java Main -version
v 1.0

如果是vsc的话已经生成了.class文件,就无需第一行命令了。