Java SE 学习笔记 1️⃣
一、入门介绍
回顾:
- C 语言通过编译,将高级语言代码翻译为计算机能够直接执行的指令;
- Python 并不会先进行编译,而是直接交给解释器解释执行
1
print("Hello World!")
一般来说,编程语言分为两大类:
- 编译型语言:需要先编译为计算机可以直接执行的命令才可以运行。优点是计算机直接运行,性能高;缺点是与平台密切相关,在一种操作系统上编译的程序,无法在其他非同类操作系统上运行,比如 Windows 下的 exe 程序在 Mac 上就无法运行。
- 解释型语言:只需要通过解释器代为执行即可,不需要进行编译。优点是可以跨平台,因为解释是解释器的事情,只需要在各个平台上安装对应的解释器,代码不需要任何修改就可以直接运行;缺点是需要依靠解释器解释执行,效率肯定没直接编译成机器指令运行的快,并且会产生额外的资源占用。
Java语言(Java之父:James Gosling,詹姆斯·高斯林)
Write Once, Run Anywhere.
这是Java语言的标语,它的目标很明确:一次编写,到处运行,它旨在打破平台的限制,让Java语言可以运行在任何平台上,并且不需要重新编译,实现跨平台运行。
Java自1995年正式推出以来,已经度过了快28个春秋,而基于Java语言,我们的生活中也有了各种各样的应用:- 诺基亚手机上的很多游戏都是使用Java编写的。
- 安卓系统中的各种应用程序也是使用Java编写的。
- 著名沙盒游戏《Minecraft》也有对应的Java版本,得益于Java跨平台特性,无论在什么操作系统上都可以玩到这款游戏。
Java 运行机制
实际上,Java程序也是需要进行编译才可以运行的,这一点与C语言是一样的,Java程序编译之后会变成.class
结尾的二进制文件:
不过不同的是,这种二进制文件计算机并不能直接运行,而是需要交给JVM(Java虚拟机)执行。
JVM 类似于 Python 解释器,将编译完成的.class
文件交给 JVM 运行,程序中要做的事情,都由 JVM 告诉计算机该如何去执行。
在不同的操作系统下,都有对应的 JVM 实现,程序员只需要将 Java 程序编译为.class
文件就可以直接交给 JVM 运行,无论是什么操作系统,JVM 都采用的同一套标准读取和执行.class
文件,所以编译之后,在任何平台都可以运行,从而实现跨平台。
由于 Java 又需要编译同时还需要依靠 JVM 解释执行,所以说 Java 既是编译型语言,也是解释型语言。Java 版本
- **JavaSE:**标准版 Java
- **JavaME:**微缩版 Java,已经基本没人用了。
- **JavaEE:**企业级 Java,比如网站开发。
二、环境安装
(一)JDK 安装
- JRE 和 JDK 区别
- JRE(Java Runtime Environment):Java 的运行环境,安装了运行环境之后,Java 程序才可以运行,一般不做开发,只是需要运行 Java 程序直接按照 JRE 即可。
- JDK(Java Development Kit):包含 JRE,并且还附带了大量开发者工具。
- JDK 安装
- jenv 安装
(二)Java 版本管理
查看当前 Java 版本:
1 java --version查看已安装的所有版本:
1 jenv versions切换不同版本:
1 jenv global 1.8添加新安装版本:
1 jenv add /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home删除已安装版本:
1 jenv remove xxx
三、Java 基础
(一)基础语法
main 主方法,是整个程序的入口点,执行过程从 main 主方法内开始从下往下依次执行。
1
2
3
4
5
6
7public class Main {
public static void main(String[] args){
System.out.println("Hello World!");
System.out.println("Hello");
System.out.println("World");
}
}⚠️
- Java 中严格区分大小写;
- 每行代码写完后需要添加分号;
注释的使用
- 单行注释 //
- 多行注释 /* xxx */
- 说明文档 /** xxx */
1
2
3
4
5
6
7
8
9
10
11
12/**
* @author angfff
*/
public class Main {
public static void main(String[] args){
System.out.println("Hello World!"); //输出语句
/*
多行注释
Test
*/
}
}变量
变量声明
1
2
3
4
5
6
7
8
9
10
11
12
13[数据类型] [变量名称];
public class Main {
public static void main(String[] args){
// 方法一
int X; // 定义整数类型变量 X
X = 10; // 为 X 赋值为 10
// 方法二
int Y = 20; // 定义变量 Y 并赋值为 20
// 方法三
int Z = Y; // 定义变量 Z 并赋值为 Y 的值
}
}关键字 final : 在变量前添加 final 关键字,可以将变量定义为不可变变量(常量),也即只能进行一次赋值,后续不能对其进行修改。
1
2
3
4
5
6
7
8public class Main {
public static void main(String[] args){
final int x; // 常量 x
x = 10;
x = 20; // 报错,无法进行修改
System.out.println(x);
}
}
(二)基本数据类型
整数类型
- byte 字节型 (8 个 bit,也就是 1 个字节)范围:-128 ~ +127
- short 短整形(16 个 bit,也就是 2 个字节 / 一个字)范围:-32768 ~ +32767
- int 整形(32 个 bit,也就是 4 个字节 / 双字)最常用的类型,范围:-2147483648 ~ +2147483647
- long 长整形(64 个 bit,也就是 8 个字节 / 四字)范围:-9223372036854775808 ~ +9223372036854775807
补充:隐式类型转换
- 从存储范围小的类型到存储范围大的类型,会发生隐式类型转换,自动将某种类型的值,转换为另一种类型。
- 实际上我们在为变量赋一个常量数值时,也发生了隐式类型转换,比如:
1
2
3public static void main(String[] args) {
byte b = 10; //这里的整数常量10,实际上默认情况下是int类型,但是由于正好在对应类型可以表示的范围内,所以说直接转换为了byte类型的值
}由于直接编写的整数常量值默认为
int
,这里需要特别注意一下,比如下面这种情况:按照
long
类型的规定,实际上是可以表示这么大的数字的,但是为什么这里报错了呢?这是因为我们直接在代码中写的常量数字,默认情况下就是int
类型,这么大肯定是表示不下的,如果需要将其表示为一个 long 类型的常量数字,那么需要在后面添加大写或是小写的L
才可以。
1
2
3public static void main(String[] args) {
long a = 922337203685477580L; //这样就可以正常编译通过了
}- 针对于这种很长的数字,为了提升辨识度,我们可以使用下划线分割每一位:
1
2
3public static void main(String[] args) {
int a = 1_000_000; //当然这里依然表示的是1000000,没什么区别,但是辨识度会更高
}- 我们也可以以8进制或是16进制表示一个常量值:
- **十六进制:**以
0x
开头的都是十六进制表示法 - **八进制:**以 0 开头的都是八进制表示法
- **十六进制:**以
1
2
3
4public static void main(String[] args) {
System.out.println(0xA);
System.out.println(012);
}浮点类型
- float 单精度浮点数(32bit,4 字节)
- double 双精度浮点数(64bit,8 字节)
根据国际标准 IEEE 754,任意一个二进制浮点数 V 可以表示成下面的形式:
- (-1)^S 表示符号位,当 S=0,V 为正数;当 S=1,V 为负数。
- M 表示有效数字,大于等于 1,小于 2,但整数部分的 1 不变,因此可以省略。(例如尾数为 1111010,那么 M 实际上就是1.111010,尾数首位必须是 1,1 后面紧跟小数点,如果出现0001111 这样的情况,去掉前面的 0,移动 1 到首位;题外话:随着时间的发展,IEEE 754标准默认第一位为 1,故为了能够存放更多数据,就舍去了第一位,比如保存 1.0101 的时候, 只保存 0101,这样能够多存储一位数据)
- 2^E 表示指数位。(用于移动小数点,所以说才称为浮点型)
浮点类型的大致取值范围:
- 单精度:±3.40282347*10^38
- 双精度:±1.79769313486231570 *10^308
⚠️ 跟整数类型常量一样,小数类型常量默认都是
double
类型,所以说如果我们直接给一个float类型赋值:由于
float
类型的精度不如double
,如果直接给其赋一个double类型的值,会直接出现错误。同样的,我们可以给常量后面添加大写或小写的 F 来表示这是一个
float
类型的常量值:1
2
3public static void main(String[] args) {
float f = 9.9F; //这样就可以正常编译通过了
}⚠️ 隐式类型转换规则总结:
byte → short(char) → int → long → float → double
字符类型
- char 字符型(16个bit,2字节,不带符号)范围 0 ~ 65535
- String 字符串类型
⚠️ ’ ’ 单引号用于 char 字符类型;" " 双引号用于 String 字符串类型
布尔类型
- true - 真
- false - 假
布尔类型(boolean)只有
true
和false
两种值,也就是要么为真,要么为假,布尔类型的变量通常用作流程控制判断语句(不同于C语言,C语言中一般使用0表示false,除0以外的所有数都表示true)布尔类型占据的空间大小并未明确定义,而是根据不同的JVM会有不同的实现。1
2
3
4public static void main(String[] args) {
boolean b = true; //值只能是 true 或 false
System.out.println(b);
}
(三)运算符
赋值运算符
赋值运算符可以直接给某个变量赋值:
1
2
3public static void main(String[] args) {
int a = 666; //使用等号进行赋值运算
}**使用规则为:**赋值运算符的左边必须是一个可以赋值的目标,比如变量,右边可以是任意满足要求的值,包括变量。
算术运算符
" + " 加法运算,除了支持算数运算外,还可以对字符串进行拼接
1
2
3
4public static void main(String[] args) {
String str = "Hello" + "abc";
System.out.println(str);
}1
2
3
4public static void main(String[] args) {
String str = "Hello" + true + 1.5 + 'A';
System.out.println(str);
}
" - " 减法运算
" * " 乘法运算
" / " 除法运算
两个整数在进行除法运算时,得到的结果也是整数(会直接砍掉小数部分,注意不是四舍五入)
1
2
3
4public static void main(String[] args) {
int a = 8, b = 5;
System.out.println(a / b); // 结果为 1
}" % " 取模(取余数)运算
⚠️ 不同类型之间可以进行运算:
1
2
3
4
5
6
7
8 public static void main(String[] args) {
int a = 5;
short b = 10;
int c = a + b;
//不同类型的整数一起运算,小类型需要转换为大类型;
//其中 short、byte、char 一律转换为 int 再进行计算(无论算式中有无 int,都需要转换),结果也是 int ;
//如果算式中出现了long类型,那么全部都需要转换到long类型再进行计算,结果也是long,反正就是依大的来
}
括号运算符
提升优先级
1
2
3
4
5
6
7
8
9
10
11public static void main(String[] args) {
int a = 10;
int b = (a = 8) * (-a + 10);
/*
1. 括号的优先级是最高的,需要先计算括号中的内容,如果存在多个括号,就从左往右计算
2. 首先是 a = 8,计算完成之后a变成8,并且运算结果也为8
3. 然后是后面的加法,-a就是-8,加上10就是2
4. 最后才是乘法,左边此时是8,右边是2,最后结果为16
*/
System.out.println(b);
}强制类型转换
1
2
3
4public static void main(String[] args) {
int a = 10;
short b = (short) a; //在括号中填写上强制转换的类型,就可以强制转换到对应的类型了
}
自增自减运算符
- " ++ " 自增运算符
- " – " 自减运算符
- “+=”、“-=”、“*=”、“/=”、“%=”
1
2
3
4
5public static void main(String[] args) {
int a = 8;
int b = a++; //先出结果,再自增
System.out.println(b); //b得到的是a自增前的值 8
}1
2
3
4
5public static void main(String[] args) {
int a = 8;
int b = ++a; //先自增,再出结果
System.out.println(b); //b得到的是a自增之后的结果 9
}⚠️ 自增运算符放在前面,是先自增再得到结果;而自增运算符放到后面,是先出结果再自增(自减同理)
⚠️ 自增自减运算符的优先级与正负号等价比如:
1
2
3
4
5
6
7
8
9public static void main(String[] args) {
int a = 8;
int b = -a++ + ++a;
//我们首先来看前面的a,因为正负号和自增是同一个优先级,结合性是从右往左,所以说先计算a++
//a++的结果还是8,然后是负号,得到-8
//接着是后面的a,因为此时a已经经过前面变成9了,所以说++a就是先自增,再得到10
//最后得到的结果为 -8 + 10 = 2
System.out.println(b);
}位运算符
" & " 按位与 运算
1
2
3
4
5public static void main(String[] args) {
int a = 9, b = 3;
int c = a & b; //进行按位与运算
System.out.println(c);
}按位与,就是让这两个数每一位都进行比较,如果这一位两个数都是 1,那么结果就是 1,否则就是 0:
- a = 9 = 1001
- b = 3 = 0011
- c = 1 = 0001(因为只有最后一位,两个数都是1,所以说结果最后一位是1,其他都是0)
" | " 按位或 运算
1
2
3
4
5public static void main(String[] args) {
int a = 9, b = 3;
int c = a | b;
System.out.println(c);
}按位或,就是只要任意一个为1(不能同时为0)那么结果就是1:
- a = 9 = 1001
- b = 3 = 0011
- c =11= 1011(只要上下有一个是1或者都是1,那结果就是1)
" ^ " 按位异或 运算
1
2
3
4
5public static void main(String[] args) {
int a = 9, b = 3;
int c = a ^ b;
System.out.println(c);
}Java 中并没有乘方运算符,
^
是按位异或运算符异或,就是只有两边不相同的情况下,结果才是1,也就是说一边是1一边是0的情况:
- a = 9 = 1001
- b = 3 = 0011
- c =10= 1010(从左往右第二位、第四位要么两个都是0,要么两个都是1,所以说结果为0)
" ~ " 按位取反 运算
1
2
3
4public static void main(String[] args) {
byte c = ~127;
System.out.println(c);
}按位取反,跟前面的正负号一样,只操作一个数,最好理解,如果这一位上是1,变成0,如果是0,变成1:
- 127 = 01111111
- -128 = 10000000
" << " 左移;" << " 右移
- **左移操作 <<:**高位直接丢弃,低位补0
- **右移操作 >>:**低位直接丢弃,符号位是什么高位补什么
" >>> " 无符号右移
1
2
3
4public static void main(String[] args) {
int c = -1 >>> 1;
System.out.println(c);
}- -1 = 11111111 11111111 11111111 11111111
- 右移: 01111111 11111111 11111111 11111111(无符号右移使用0填充高位)
此时得到的结果就是正数的最大值
⚠️ 不存在无符号左移。
关系和逻辑运算符
- 关系判断的结果只可能是真或是假,所以说得到的结果是一个
boolean
类型的值。
1
2
3
4
5
6> 大于
< 小于
== 等于(注意是两个等号连在一起,不是一个等号,使用时不要搞混了)
!= 不等于
>= 大于等于
<= 小于等于- 逻辑运算符
1
2
3&& 与运算,要求两边同时为true才能返回true
|| 或运算,要求两边至少要有一个为true才能返回true
! 非运算,一般放在表达式最前面,表达式用括号扩起来,表示对表达式的结果进行反转- 关系判断的结果只可能是真或是假,所以说得到的结果是一个
三元运算符
三元运算符可以根据判断条件,返回不同的结果
1
2
3
4
5public static void main(String[] args) {
int a = 10;
char b = a > 10 ? 'A' : 'B'; //三元运算符需要三个内容,第一个是判断语句,第二个是满足判断语句的值,第三个是不满足判断语句的值
System.out.println(b);
}三元运算符:
1
判断语句 ? 结果1 : 结果2
运算符优先级总结
优先级 运算符 结合性 1 ( ) 从左向右 2 ~ - + (强制类型转换) ++ – 从右向左 3 * / % 从左向右 4 + - 从左向右 5 << >> >>> 从左向右 6 > < >= >= 从左向右 7 == != 从左向右 8 & 从左向右 9 ^ 从左向右 10 | 从左向右 11 && 从左向右 12 || 从左向右 13 ? : 从右向左 14 = += -= *= /= %= &= |= ^= <<= >>= >>>= 从右向左
(四)流程控制
代码块与作用域
作用域:在代码块内定义的变量无法在代码块之外使用。
1
2
3
4
5
6
7public static void main(String[] args) {
{
int a = 10; //此时变量在代码块内定义
System.out.println(a); //处于其作用域内部的代码可以调用
}
System.out.println(a); //作用域外的代码无法调用
}条件判断
- IF 语句
1
2
3
4
5
6
7if (条件判断) {
判断成功执行的代码 1;
}else if () {
判断成功执行的代码 2;
}else {
判断成功执行的代码 3;
}1
2
3
4
5
6
7
8
9
10
11public static void main(String[] args) {
int score = 2;
if(score >= 90)
System.out.println("优秀");
else if (score >= 70)
System.out.println("良好");
else if (score >= 60)
System.out.println("及格");
else
System.out.println("不及格");
}- Switch 语句
1
2
3
4
5
6
7
8
9
10
11
12switch (目标) { //目标变量 或 计算表达式
case 匹配值: //如目标值等于给定匹配值,执行case的代码
代码...
break; //代码执行结束后需要使用break来结束,否则会溜到下一个case继续执行代码
case 匹配值:
代码...
break;
default:
代码...
}循环语句
- for 循环
1
2
3for (表达式1;表达式2;表达式3) {
循环体;
}- while 循环
1
2
3while(循环条件) {
循环体;
}- do……while 循环
1
2
3
4
5
6
7public static void main(String[] args) {
int i = 0; //比如现在我们想看看i不断除以2得到的结果会是什么,但是循环次数我们并不明确
do { //无论满不满足循环条件,先执行循环体里面的内容
System.out.println("Hello World!");
i++;
} while (i < 10); //再做判断,如果判断成功,开启下一轮循环,否则结束
}continue 和 break
⚠️ 就近原则,也可以利用标签指定跳转位置
a.
continue + (标签)
跳过本轮循环进入下一轮b.
break + (标签)
跳出 / 结束循环1
2
3
4
5
6outer: for (int i = 1; i < 4; ++i) { //在循环语句前,添加 标签: 来进行标记
inner: for (int j = 1; j < 4; ++j) {
if(i == j) break outer; //break后紧跟要结束的循环标记,当i == j时终止外层循环
System.out.println(i+", "+j);
}
}