— 学无止境 转向Java 拥抱开源 —
旅行的意义在于找到自己,而非浏览他人。
这短短的一生,我们最终都会失去。你不妨大胆一些,爱一个人,攀一座山,追一个梦。

编码规范

类名要求:

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

方法要求

  • 同类名要求
  • 习惯以小写字母开头

注释是一种给人阅读的文本,不是程序的一部分,所以编译器会自动忽略注释。

1
// 单行注释
1
2
3
/*
多行注释
*/
1
2
3
4
5
6
7
8
9
10
/**
* 一般写在方法或类的定义处,可以用来自动创建文档的注释
*
* @auther eiunui
*/
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

Java定义类或方法的{}{与类名或方法名不换行,C#的{需要进行换行

Eclipse 快捷键
格式化代码 Ctrl+Shift+F

Intellij Idea 快捷键
格式化代码 Ctrl+Shift+L

变量

  • 变量必须先定义后使用;
  • 变量的特点是可以重新赋值;
  • 变量可以赋值给其他变量;
  • 定义一个变量,JVM在内存中为该变量分配一个“存储单元”;
  • 变量分为基本类型和引用类型。

基本数据类型

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

计算机内存的基本结构

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

内存单元从0开始编号,称为内存地址。每个内存单元可以看作一间房间,内存地址就是门牌号。

img

不同数据类型占用字节数不一样:

  • 1字节:byte

  • 2字节:short char

  • 4字节:int float

  • 8字节:long double

整形

Java只定义了带符号的整形,所以,最高位的bit表示符号位(0表示正数,1表示负数)。所以各类型整形的值范围如下:

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

浮点型

浮点类型的数就是小数,小数用科学计数法表示的时候,小数点是可以“浮动”的,如1234.5可以表示成12.345x102,也可以表示成1.2345x103,所以称为浮点数。

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

float最大可表示 $3.4\times10^{38}$

double最大可表示$1.79\times10^{308}$

布尔类型

boolean只有truefalse两个值。

Java语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要1bit,但通常JVM内部会把boolean表示为4字节正数。

字符类型

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

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

常量

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

定义变量的时候,如果加上final修饰符,这个变量就变成了常量:

1
2
3
4
final double PI = 3.14; // PI是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // compile error!

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

根据习惯,常量名通常全部大写;使用常量便于理解程序意图。

var关键字

有时变量类型名太长太麻烦,可以省略变量类型,使用var关键字替代,编译器会根据赋值语句自动推断出变量的类型。

变量作用域

{}中表示多行语句,在语句块中定义的变量,有一个作用域,就是从定义处{开始,到语句块结束}。超出了作用域引用这些变量,编译器会报错。

运算

整数运算

整数的数值表示不但是精确的,而且整数运算永远是精确的,即使是除法也是精确的,因为两个整数相除只能得到结果的整数部分:

1
int x = 12345 / 67; // 184

求余运算使用%

1
int y = 12345 % 67; // 12345÷67的余数是17

特别注意:整数的除法对于除数为0时运行时将报错,但编译不会报错。

溢出

要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错

位移运算

在计算机中,整数总是以二进制的形式表示。位移运算就是将整数的二进制数向左或向右移动指定位数,移动后产生的新位数值默认为0,最高位(符号位)不变化,即负数时,位移后最高位依旧为1。

1
2
3
int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14
int b = n << 2; // 00000000 00000000 00000000 00011100 = 28
1
2
int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n >> 1; // 00000000 00000000 00000000 00000011 = 3
1
2
3
int n = -536870912; //对一个负数进行右移,最高位的1不动,结果仍然是一个负数
int a = n >> 1; // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2; // 11111000 00000000 00000000 00000000 = -134217728

不带符号的右移运算,使用>>>,它的特点是符号位跟着动,因此,对一个负数进行>>>右移,它会变成正数,原因是最高位的1变成了0

1
2
3
4
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

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

观察可发现,左移实际上就是不断地×2,右移实际上就是不断地÷2。

位运算

  • & 与运算 n = 0 & 1; // 0同时为1,才为1
  • | 或运算 n = 0 | 1; // 1 任意为1,结果为1
  • ~ 非运算 n = ~0; // 1 0和1互换
  • ^ 异或运算 n = 0 ^ 1; // 1 两个数不同为1

运算优先级

  • 在Java的计算表达式中,运算优先级从高到低依次是:
    • ()
    • ! ~ ++ --
    • * / %
    • + -
    • << >> >>>
    • &
    • |
    • += -= *= /=

类型自动提升与强制转型

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

注意,超出范围的强制转型会得到错误的结果,原因是转型时,int的两个高位字节直接被扔掉,仅保留了低位的两个字节

浮点数运算

浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算。

在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示。

由于浮点数存在运算误差,所以比较两个浮点数是否相等常常会出现错误的结果。正确的比较方法是判断两个浮点数之差的绝对值是否小于一个很小的数:

1
2
3
4
5
6
double r = Math.abs(x - y); // 比较x和y是否相等,先计算其差的绝对值:
if (r < 0.00001) { // 再判断绝对值是否足够小:
// 可以认为相等
} else {
// 不相等
}

溢出

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

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

强制转换

将浮点数强制转换为整数时,浮点数的小数部分会被丢掉,如果转型后超过了整形的最大范围,将返回整形的最大值。

布尔运算

对于布尔类型boolean,永远只有truefalse两个值。

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

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

短路运算

因为false && x的结果总是false,无论xtrue还是false,因此,与运算在确定第一个值为false后,不再继续计算,而是直接返回false;类似的,对于||运算,只要能确定第一个值为true,后续计算也不再进行,而是直接返回true

三元运算符

三元运算符b ? x : y,它根据第一个布尔表达式的结果,分别返回后续两个表达式之一的计算结果。

注意到三元运算b ? x : y会首先计算b,如果btrue,则只计算x,否则,只计算y。此外,xy的类型必须相同,因为返回值不是boolean,而是xy之一。

字符和字符串

字符类型char是基本数据类型,它是character的缩写。一个char保存一个Unicode字符。

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

也可以用转义字符\u+Unicode编码来表示一个字符

字符串

字符串类型String是引用类型,我们用双引号"..."表示字符串。一个字符串可以存储0个到任意个字符。

注意,在java中String是大写的S,C#中string是小写的s

"..."中间出现"时,可使用转义字符\

1
String s = "abc\"xyz"; // 包含7个字符: a, b, c, ", x, y, z

常见的转义字符包括:

  • \" 表示字符"
  • \' 表示字符'
  • \\ 表示字符\
  • \n 表示换行符
  • \r 表示回车符
  • \t 表示Tab
  • \u#### 表示一个Unicode编码的字符

多行字符串

  • 使用+连接任意字符串和其他数据类型,如果用+连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接;

  • 从Java 13开始,字符串可以用"""..."""表示多行字符串(Text Blocks)。由于多行字符串是作为Java 13的预览特性(Preview Language Features)实现的,编译的时候,我们还需要给编译器加上参数:

    1
    javac --source 13 --enable-preview Main.java

不可变特性

Java的字符串除了是一个引用类型外,还有个重要特点,就是字符串不可变。

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
String s = "hello";
System.out.println(s); // 显示 hello
s = "world";
System.out.println(s); // 显示 world
}
}

观察执行结果,难道字符串s变了吗?其实变的不是字符串,而是变量s的“指向”。

执行String s = "hello";时,JVM虚拟机先创建字符串"hello",然后,把字符串变量s指向它:

1
2
3
4
5
6
      s


┌───┬───────────┬───┐
│ │ "hello" │ │
└───┴───────────┴───┘

紧接着,执行s = "world";时,JVM虚拟机先创建字符串"world",然后,把字符串变量s指向它:

1
2
3
4
5
6
      s ──────────────┐


┌───┬───────────┬───┬───────────┬───┐
│ │ "hello" │ │ "world" │ │
└───┴───────────┴───┴───────────┴───┘

原来的字符串"hello"还在,只是我们无法通过变量s访问它而已。因此,字符串的不可变是指字符串内容不可变。

空值null

引用类型的变量可以指向一个空值null,它表示不存在,即该变量不指向任何对象。

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

数组类型

定义一个数组类型的变量,使用数组类型“类型[]”,和单个基本类型变量不同,数组变量初始化必须使用new 类型[n]表示创建一个可容纳n个类型元素的数组。

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

要访问数组中的某一个元素,需要使用索引。数组索引从0开始。

数组是引用类型,在使用索引访问数组元素时,如果索引超出范围,运行时将报错。