学习参考:https://www.liaoxuefeng.com/wiki/1252599548343744/1255883729079552

只记录不熟悉/需要注意的部分

数据类型

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
       ┌───┐
byte │ │
└───┘
┌───┬───┐
short │ │ │
└───┴───┘
┌───┬───┬───┬───┐
int │ │ │ │ │
└───┴───┴───┴───┘
┌───┬───┬───┬───┬───┬───┬───┬───┐
long │ │ │ │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
┌───┬───┬───┬───┐
float │ │ │ │ │
└───┴───┴───┴───┘
┌───┬───┬───┬───┬───┬───┬───┬───┐
double │ │ │ │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┘
┌───┬───┐
char │ │ │
└───┴───┘
boolean型不确定,理论上存储布尔类型只需要1 bit,但是通常JVM内部会把boolean表示为4字节整数

byte恰好就是一个字节,而longdouble需要8个字节。

整型

对于整型类型,Java只定义了带符号的整型,因此,最高位的bit表示符号位(0表示正数,1表示负数)。各种整型能表示的最大范围如下:

  • byte:-128 ~ 127
  • short: -32768 ~ 32767
  • int: -2147483648 ~ 2147483647
  • long: -9223372036854775808 ~ 9223372036854775807

浮点型

对于float类型,需要加上f后缀。

1
float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38

浮点数可表示的范围非常大,float类型可最大表示3.4x1038,而double类型可最大表示1.79x10308。

常量

如果加上final修饰符,这个变量就变成了常量:

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

常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。

常量的作用是用有意义的变量名来避免魔术数字(Magic number),例如,不要在代码中到处写3.14,而是定义一个常量。如果将来需要提高计算精度,我们只需要在常量的定义处修改,例如,改成3.1416,而不必在所有地方替换3.14

var关键字

如果想省略变量类型,可以使用var关键字:

1
var sb = new StringBuilder();

编译器会根据赋值语句自动推断出变量sb的类型是StringBuilder

整数运算

自增/自减

注意++写在前面和后面计算结果是不同的,++n表示先加1再引用n,n++表示先引用n再加1。

(哪个在前面就是先做哪个)

移位运算

  • 左移1相当于*2,注意溢出
  • 右移相当于/2,负数右移仍是负数

还有一种无符号的右移运算,使用>>>,它的特点是不管符号位,右移后高位总是补0,因此,对一个负数进行>>>右移,它会变成正数,原因是最高位的1变成了0

1
2
3
4
5
int n = -536870912;
int a = n >>> 1; // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2; // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1

byteshort类型进行移位时,会首先转换为int再进行位移。

位运算

找到一个数二进制里其中最右边一个“1”所在的位置

该位置标为1,其他位置为0。例如14(1110),返回00102

1
int rightOne = n & (~n+1); //n&补码(n)

异或的妙用

参考

  1. swap(a,b)

    1
    2
    3
    a=a^b;
    b=a^b;
    a=a^b;
  2. 一群数int[] nums中,只有一个数出现了奇数次,其他均出现偶数次,找到那个数n

    把所有数相异或的结果就是n

    1
    2
    3
    4
    int n = 0;
    for(int num:nums){
    n ^= num;
    }
  3. 一群数int[] nums中,只有两个数出现了奇数次,其他均出现偶数次,找到那两个数ab

    所有数异或为n,则n=a^b。因为a!=b所以num的二进制上肯定有一位为1,用n上最右边一个1rightOne来区分ab

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int n = 0;
    for(int num:nums){
    n ^= num;
    }

    int rightOne = n & (~n+1) ;

    int a = 0; //a在rightOne那一位上为0
    for(int num:nums){
    if((num&rightOne)==0){
    a ^= rightOne;
    }
    }
    int b = n^a;

运算优先级

在Java的计算表达式中,运算优先级从高到低依次是:

  • ()
  • ! ~ ++ --
  • * / %
  • + -
  • << >> >>>
  • &
  • |
  • += -= *= /=

关系运算符的优先级从高到低依次是:

  • !
  • >>=<<=
  • ==!=
  • &&
  • ||

浮点数运算

浮点数表示问题

浮点数常常无法精确表示。

浮点数0.1在计算机中就无法精确表示,**因为十进制的0.1换算成二进制是一个无限循环小数,**很显然,无论使用float还是double,都只能存储一个0.1的近似值。

但是,0.5这个浮点数又可以精确地表示。

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
double x = 1.0 / 10;
double y = 1 - 9.0 / 10;
// 观察x和y是否相等:
System.out.println(x); // 0.1
System.out.println(y); // 0.09999999999999998
}
}

强制转型

可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉(即正数变小,负数变大)。如果转型后超过了整型能表示的最大范围,将返回整型的最大值。例如:

1
2
3
4
5
int n1 = (int) 12.3; // 12
int n2 = (int) 12.7; // 12
int n2 = (int) -12.7; // -12
int n3 = (int) (12.7 + 0.5); // 13
int n4 = (int) 1.2e20; // 2147483647

如果要进行四舍五入,可以对浮点数加上0.5再强制转型:

1
int n = (int) (d + 0.5);

布尔运算

  • 短路运算
  • 三元运算符(条件表达式) 表达式a?b:c

字符和字符串

Unicode编码

要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可:

1
2
int n1 = 'A'; // 字母“A”的Unicodde编码是65
int n2 = '中'; // 汉字“中”的Unicode编码是20013

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

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

【Java基础篇】Unicode、进制转换_cc的博客-CSDN博客_java unicode转16进制

方法功能
Integer.toBinaryString(int i)将十进制转换成二进制
Integer.toOctalString(int i)将十进制转换成八进制
Integer.toHexString(int i)将十进制转换成十六进制
Integer.toString(int i, int radix)将十进制转换成指定的进制

其他进制(字符串)转10进制Integer.parseInt(String s, int radix)

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
// 请将下面一组int值视为字符的Unicode码,把它们拼成一个字符串:
int a = 72;
int b = 105;
int c = 65281;

String s = ""+(char)a+(char)b+(char)c; //""转换为字符串
System.out.println(s);
}

多行字符串

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

多行字符串前面共同的空格会被去掉(以最短的对齐)

1
2
3
4
5
6
String s = """ 
SELECT * FROM
users
WHERE id > 100
ORDER BY name DESC
""";

上述多行字符串实际上是5行,在最后一个DESC后面还有一个\n

数组

初始化

Java的数组有几个特点:

  • 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false
  • 数组一旦创建后,大小就不可改变。

几种初始化表示方法

1
2
3
4
5
6
7
8
9
10
11
//1.
int[] ns = new int[5];
ns[0] = 1;
ns[1] = 2;
ns[2] = 3;
ns[3] = 5;
ns[4] = 8;
//2.
int[] ns = new int[] { 68, 79, 91, 85, 62 };
//3.
int[] ns = { 68, 79, 91, 85, 62 };

输入输出

输出

  • println是print line的缩写,表示输出并换行。

  • print不换行

  • printf格式化输出 JDK文档java.util.Formatter

    占位符 说明
    %d 格式化输出整数
    %x 格式化输出十六进制整数
    %f 格式化输出浮点数
    %e 格式化输出科学计数法表示的浮点数
    %s 格式化字符串

    注意,由于%表示占位符,因此,连续两个%%表示一个%字符本身。

输入

  • 需要import java.util.Scanner

  • 创建Scanner对象传入System.in

    • 要读取用户输入的字符串,使用scanner.nextLine()
    • 要读取用户输入的整数,使用scanner.nextInt()
    • Scanner会自动转换数据类型,因此不必手动转换。scanner.nextLine() / nextInt() / nextDouble() / …
    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); // 格式化输出
    }
    }

条件选择

判断引用类型相等

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

注意:执行语句s1.equals(s2)时,如果变量s1null,会报NullPointerException

要避免NullPointerException错误,可以利用短路运算符&&,或者把一定不是null的对象"hello"放到前面:例如:if ("hello".equals(s)) { ... }

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
String s1 = null;
if (s1 != null && s1.equals("hello")) {
System.out.println("hello");
}
}
}

switch表达式

普通用法跟c差不多

使用switch时,如果遗漏了break,就会造成严重的逻辑错误,而且不易在源代码中发现错误。

从Java 12开始,switch语句升级为更简洁的表达式语法,使用类似模式匹配(Pattern Matching)的方法,保证只有一种路径会被执行,并且不需要break语句。新语法使用->,如果有多条语句,需要用{}括起来。

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> 0;
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
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");
}
}
}

yield

大多数时候,在switch表达式内部,我们会返回简单的值。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
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);
}
}

for each循环

for each循环,它可以更简单地遍历数组等所有“可迭代”的数据类型,包括后面会介绍的ListMap

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}
}
}

快速打印数组内容

Java标准库提供了Arrays.toString(),可以快速打印数组内容

数组排序

可以直接使用Java标准库提供的Arrays.sort(数组)进行排序,改变数组本身/引用

多维数组

定义

二维数组的每个数组元素的长度并不要求相同,例如,可以这么定义ns数组:

1
2
3
4
5
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6 },
{ 7, 8, 9 }
};

这个二维数组在内存中的结构如下:

1
2
3
4
5
6
7
8
9
                    ┌───┬───┬───┬───┐
┌───┐ ┌──>│ 1 │ 2 │ 3 │ 4 │
ns ─────>│░░░│──┘ └───┴───┴───┴───┘
├───┤ ┌───┬───┐
│░░░│─────>│ 5 │ 6 │
├───┤ └───┴───┘
│░░░│──┐ ┌───┬───┬───┐
└───┘ └──>│ 7 │ 8 │ 9 │
└───┴───┴───┘

打印数组

使用Java标准库的Arrays.deepToString()

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

命令行参数

可以利用接收到的命令行参数,根据不同的参数执行不同的代码。例如,实现一个-version参数,打印程序版本号:

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