1. 关键字与保留字
关键字 : 在java程序中有特殊含义作用的单词, 字母全部小写
保留字 : 从C++中保留下来的关键字, 在java中目前还不是关键字, 将来有可能是.
2. 标识符
2.1. 标识符
标识某个东西的符号, 简单的来讲就是名字.
2.2. 定义合法标识符规则
- 由52个字母, 0~9 10个数字字符, _和$(尽量不要使用)
- 数字不可以开头, 标识符3C 就是非法, C3是合法
- 不可以使用关键字和保留字, 但是可以包含关键字和保留字
- 大小写敏感的, 长度有限制(65535)
- 标识符中不能包含空格, 因为空格也有特殊含义.
2.3. 命名规范
- 包名 : 所有字母都小写 mypackage
- 类名 : 首字母大写, 后面的每个单词的首字母也大写 : MyClassName
- 变量名或方法名 : 首字母必须小写, 后面的单词的首字母大写 : myVarName
- 常量名 : 所有字母都大写, 单词之间用_隔开 : MY_CONST_NAME
3. **变量
3.1. * 变量的概念
3.1.1. 变量是什么?
变量是内存中的一块被命名的、被特定的数据类型约束的存储区域,此区域中可以保存数据类型范围内的数据。
数据类型: 决定空间大小, 空间可以保存什么数据, 这个数据能做什么.
变量声明: 数据类型 变量名;
3.1.2. 变量注意事项
- Java中每个变量必须先声明,后使用
- 该区域有自己的 变量名 和 数据类型
- 变量有其作用范围,变量的声明所在的
{}
内 - 同一范围内,变量不允许重复声明
- 变量内存空间中保存的数据只能在其范围内变化
public class VariableTest {
public static void main(String[] args) {
// 变量声明 : 数据类型 变量名;
int n1; // 作用是在内存中开辟一块4字节空间, 并用n1符号与此空间联系起来
n1 = 10; // 把10这个值写入n1符号指向的空间中. 这是一个赋值操作, 把右侧的值写入左面的变量
System.out.println(n1); // 把n1符号指向的空间中的值取出来并打印输出.
int n2 = n1; // 把n1变量中的值复制出来, 再写入n2变量内存空间中
System.out.println(n2);
n1 = n1 * n2; // 一个变量在某个时刻只能保存一个值, 一旦被重新赋值, 之前的老值永远无法找回
System.out.println(n1); // 变量有时间性, 不同时刻变量中的值有可能不一样.
}
}
3.1.3. 变量的类型
3.1.3.1. 按照数据类型
基本数据类型:内存区域中保存数据本身 不同的类型, 空间大小不同
引用数据类型:内存区域中保存内存地址 不同的类型, 空间大小相同, 和JDK位数相关。
内存地址:内存的每个字节都有一个顺序的编号, 这个编号就是了。本质上来讲就是无符号正整数. 0表示null 空地址.
3.1.3.2. 变量按照声明位置来分 : 声明位置决定了变量的作用范围
局部变量:在方法体内部声明的变量。范围小, 寿命短。局部变量除形参外,需显式初始化。
成员变量:在方法体外,类体内声明的变量。范围大, 寿命长。
所有变量
|---成员变量
| |---实例变量(不以static修饰)
| |---类变量(以static修饰)
|---局部变量
|---形参(方法签名中定义的变量)
|---方法局部变量(在方法内定义)
|---代码块局部变量(在代码块内定义)
3.2. 基本数据类型
整数 :是实打实存储, 精准
类型 | 占用 | 范围 |
---|---|---|
byte | 1字节 | -128~127 |
short | 2字节 | -32768~32767 or -2^15 ~ 2^15 -1 |
int | 4字节 | -20多亿~20多亿 or -2^31 ~ 2^31 -1 |
long | 8字节 | -巨大~巨大(900多亿亿) or -2^63 ~ 2^63 -1 |
浮点数 : 是近似值不精准
类型 | 占用 | 范围 |
---|---|---|
float | 4字节 | -10^38 ~ 10^38 |
double | 8字节 | -10^308 ~ 10^308 |
字符
类型 | 占用 | 范围 |
---|---|---|
char | 2字节 | 0~65535 |
布尔
类型 | 占用 | 范围 |
---|---|---|
boolean | 1字节 | true, false |
3.3. 基本数据类型转换
自动类型转换:容量小的类型自动转换为容量大的数据类型。
数据类型按容量大小排序为:
有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。
byte
,short
,char
之间不会相互转换,他们三者在计算时首先转换为int
类型。
当把任何基本类型的值和字符串值进行连接运算时+
,基本类型的值将自动转化为字符串类型。
3.3.1. 练习
class VariableTest4 {
public static void main(String[] args) {
byte b1 = 10;
short s1 = 20;
int i1 = 30;
// 任意非long整数变量作运算, 结果总是升级成int型
//s1 = b1 + s1; // 任意非long整数作运算, 结果总是升级成int型
//s1 = (short)b1 + (short)s1;
s1 = (short)(b1 + s1); // 变量中的值对于编译器来说, 是不确定.
s1 = 20 + 50; // 右侧如果是常量, 编译器很确定 翻译成 s1 = 70;
long l1 = 40L;
// 各种类型的变量混合运算, 结果总是升级成范围最大的那个类型
// 右侧相当是3个long型做运算, 结果肯定是long型.
//i1 = l1 * b1 + s1;
i1 = (int)l1 * b1 + s1;
float f1 = 0.22F;
double d1 = 0.33;
//l1 = f1 + l1 + b1; // 不可以
l1 = (long)(f1 + l1 + b1);
d1 = d1 * s1 + l1; // 可以
}
}
class VariableTest3 {
public static void main(String[] args) {
int n1 = 200; // n1是变量, 200是值 也有数据类型是int类型
// 常量 : 内容不可改变的量, 包括字面量和final修饰的量.
//300 = 300; // 这个语句不可以, 原因是左面的常量不允许被写入.
// 赋值符号 左侧 永远必须是变量
//int 3 = 3; 左面的3的变量名不合法.
//'a' = 'b'; "abc", 3.22 这些都是字面量
// 整数字面量默认是int型
byte b1 = 10; // 右侧是整数字面量
short s1 = 20; // 编译器优化, 20这个常量是在short的范围内的, 如果超出范围就无法优化
//short s2 = n1; // 编译器无法测量变量中的值
int i1 = 30; // 右侧是整数字面量, 是用int型4字节来保存
long l1 = 40; // 右侧是整数字面量, 也是用int型4字节来保存
//long l2 = 3000000000; // 右面的字面量尝试用4字节int来保存, 存不下, 所 以报错
long l2 = 3000000000L; // 后缀L的作用是告诉编译器,此字面量用8字节的long型来保存.
// 浮点数字面量默认数据类型是double型
double d1 = 3.22D;
//float f1 = 0.23; // 浮点数字面量默认数据类型是double型
float f1 = (float)0.23; // 浮点数不进行优化.
float f2 = 2.33F; // F的作用也是告诉编译器, 此字面量用4字节的float型来保存
f1 = i1; // 范围小的量值给范围大的变量写入. 可以写入
i1 = (int)f1; // 范围大的量值给范围小的变量写入, 不可以直接完成, 可以强转
l1 = (short)f1; // 可以!!
// 基本数据类型的数值范围大小, 从小到大排序 :
// byte < short < int < long < float < double
//d1 = ?; // 右面可以是什么类型, 任意类型, 兼容性最好.
}
}
class VariableTest2 {
public static void main(String[] args) {
// 数据类型 数值型 : byte, short, int, long, float, double
byte b1 = 10;
byte b2 = 11;
short s1 = 20;
short s2 = 2100;
// 看到赋值操作, 内心要紧张.
b2 = b1; // 这是赋值操作, 特点 : 反人类, 从右向左, 如果右面没有得出确定的值, 不能向左走.
s1 = b2; // 可以完成, 右面的数据类型范围小, 左面的数据类型的范围大
//b1 = s2; 直接操作不允许
// (目标类型)数据;
// 强制类型转换有风险, 风险自负, 程序员必须知道自已在做什么.
b1 = (byte)s2; // 如果要把范围大的变量中的值写入范围小的变量中, 必须强制类型转换
System.out.println(b1);
s2 = (short)b1; // 范围小的量值写入范围大的变量中时 不需要强制类型转换
}
}
public class VariableTest {
public static void main(String[] args) {
// 变量的使用事项
//n = 200; 变量应该先声明, 后使用
//int n;
//System.out.println(n);
{
int n = 10;
}
{
//System.out.println(n); // n的使用范围只在它声明语句 的{} 内
}
int n2 = 20;
//long n2 = 30; 同一个范围内, 变量只能声明一次, 不允许重复声明
//short s1 = 50000; 变量不可以超范围保存数据
int n3;
//System.out.println(n3); // ? 变量没有初始化不可以读取
n3 = 30; // 初始化赋值, 变量声明好后的第一次赋值.
n3 = 50; // 普通赋值
n3 = 60;
int n4 = 200; // 最好这样写, 声明和初始化同时进行, 防止忘记初始化.
}
}
3.4. String类
字符串 , 由一系列字符串起来
字符串可以和任意数据作拼接, 拼接的结果是原内容加上新内容的新字符串
字符串是内容不可以改变的对象, 所以任何的字符串连接都会产生新对象.
String s2 = ""; // 空串, 含义是有字符串对象, 但是里面没有内容
String s3 = null; // s3是空地址, 含义是什么也没有, 当然也不可能有内容
在程序中能用变量的地方绝不用常量, 因为变量灵活
// String str1 = 4; //判断对错:错误: 不兼容的类型: int无法转换为String
String str2 = 3.5f + ""; //判断str2对错:System.out.println(str2); //输出:
System.out.println(3+4+"Hello!"); //输出:7Hello!
System.out.println("Hello!"+3+4); //输出:Hello!34
System.out.println('a'+1+"Hello!"); //输出:98Hello!
System.out.println("Hello"+'a'+1); //输出:Helloa1
3.5. 进制
3.5.1. 十进制 二进制 十六进制
一个十六进制正好可以对应4个bit, 4个位
两个十六进制正好可以对应8个bit, 正好一个字节.
十进制 | 二进制 | 十六进制 |
---|---|---|
0 | 0000 | 0 |
1 | 0001 | 1 |
2 | 0010 | 2 |
3 | 0011 | 3 |
4 | 0100 | 4 |
5 | 0101 | 5 |
6 | 0110 | 6 |
7 | 0111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
10 | 1010 | A |
11 | 1011 | B |
12 | 1100 | C |
13 | 1101 | D |
14 | 1110 | E |
15 | 1111 | F |
练习:
0xA3
-> 1010 0011
0xC2B5E7A9
-> 1100 0010 1011 0101 1110 0111 1010 1001
0101 0110 1110 1010 0101 0110 0101 0100
-> 0x56EA5654
0xD752C9BD
-> 1101 0111 0101 0010 1100 1001 1011 1101
0101 1010 1101 0101 0101 1010 1110 1110
-> 0x5AD55AEE
3.5.2. 计算机底层存储
3.5.2.1. 存储
计算底层全部都是二进制, 并且都是以二进制补码的形式保存数据。
有符号二进制数的符号位永远在最高位, 如果符号位为0, 表示这个数是正数+;如果符号位为1 表示这个数是负数-。
3.5.2.2. 补码
正数的补码就是自身,负数的补码是它的相反数全部取反再加1。
对于正数:
byte b1 = 0b0010 1101;
这是正数
0x2D
-> 2 * 16 + 13 => 45 所以0010 1101是+45
对于负数:
Byte b2 = 0b1101 1001;
这是一个负数, 负多少, 找相反数
1101 1001 -1 -> 1101 1000 取反 -> 0010 0111 -> 0x27 -> 39
所以 1101 1001是-39的真实存储.
3.5.2.3. 最值
byte 型最值
二进制 | 十六进制 | ± | 补码 | 十进制 | 最值 |
---|---|---|---|---|---|
0111 1111 | 0x7F | + | 0111 1111 | 127 | 最大值 |
1111 1111 | 0x88 | - | 0000 0001 | -1 | |
1000 0000 | 0x80 | - | 1000 0000 | -128 | 最小值 |
正数最大 + 1 => 最小负数;最小负数 -1 => 最大正数
Byte最小值~最大值 : 1000 0000 ~ 0111 1111 (0x80 ~ 0x7F)
short 型最值
二进制 | 十六进制 | ± | 十进制 | 最值 |
---|---|---|---|---|
0111 1111 1111 1111 | 0x7FFF | + | 32767 | 最大值 |
1000 0000 0000 0000 | 0x8000 | - | -32768 | 最小值 |
int 型最值
int 型最大值 0111 1111 1111 1111 1111 1111 1111 1111 => 0x7FFFFFFF
int 型最小值 1000 0000 0000 0000 0000 0000 0000 0000 => 0x80000000
long型最值
long 型最值 => 0x7FFFFFFFFFFFFFFF
long 型最小值 => 0x8000000000000000
char型最值
因为没有符号位, 所以最高位也是数据部分
char 型最大值 1111 1111 1111 1111 => 0xFFFF,即65535
char 型最小值 0000 0000 0000 0000 => 0x0000,即0
4. 运算符
4.1. 位运算符
操作符 | 描述 | 例子 |
---|---|---|
& | 与 | (A&B),得到12,即0000 1100 |
| | 或 | (A |
^ | 亦或,如果相对应位值相同,则结果为0,否则为1 | (A ^ B)得到49,即 0011 0001 |
〜 | 取反 | (〜A)得到-61,即1100 0011 |
<< | 按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2得到240,即 1111 0000 |
>> | 按位右移运算符。左操作数按位右移右操作数指定的位数。 | A >> 2得到15即 1111 |
>>> | 无符号右移。被移位二进制最高位无论是0或者是1,空缺位都用0补按位右移补零操作符。 | A>>>2得到15即000 |
4.2. 算术运算符
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
+ | 正号 | 3 | 3 |
- | 负号 | b=4; -b | -4 |
+ | 加 | 5+5 | 10 |
- | 减 | 6月4日 | 2 |
* | 乘 | 3*4 | 12 |
/ | 除 | 5月3日 | 1 |
% | 取模 | 7%5 | 2 |
++ | 自增(前):先运算后取值 | a=2;b=++a; | a=3;b=3 |
++ | 自增(后):先取值后运算 | a=2;b=a++; | a=3;b=2 |
- - | 自减(前):先运算后取值 | a=2;b=- -a | a=1;b=1 |
- - | 自减(后):先取值后运算 | a=2;b=a- - | a=1;b=2 |
+ | 字符串相加 | “He”+”llo” | “Hello” |
注意:
+
是取一个数本身;-
取相反数。/
如果是两个整数相除, 结果会直接丢弃小数部分, 有可能会丢失精度.- % 取余
如果n % m == 0
说明n能被m整除.
如果n % 2 == 0
说明n是偶数, 如果n % 2 != 0
, 说明n是奇数
N % M
的结果总是小于M, 让一个完全未知的数落在一个已知的M范围内.
如果对负数取模,可以把模数负号忽略不记,如:5%-2=1
。 但被模数是负数则不可忽略。此外,取模运算的结果不一定总是整数。 ++n
前加加, 先加后用, 不需要临时空间 效率高
n++
后加加, 先用后加, 需要一个临时空间保存老值(也就是用的值), 效率低int n = 10; n += 30;
效果相当于n = n + 30
, 但是不会引起数据类型的变化, 更安全.
4.3. 比较运算符
instanceof
检查是否是类的对象 "Hello" instanceof String
true
比较运算的结果总是boolean
,比较大小的运算只适用于基本数据类型中的数值型
==
, !=
可以适用于任意数据类型.
在Java中不可以写成3 < x < 6. 为什么? 因为 3 < x结果是boolean, 让boolean再和6比大小出问题.
4.4. 逻辑运算符
只适用于布尔之间.
逻辑与 : 只要有假就是假
&
和 &&
的区别 就是 &&
有短路效果,在实际使用中必须使用 &&
.
逻辑或 : 只要有真就是真
|
和 ||
的区别 ||
有短路效果,在实际使用必须使用 ||
.
4.5. 三元运算符
变量 = (布尔表达式) ? 表达式1 : 表达式2;
表达式1和表达2的值类型要一致.
4.6. 运算符优先级
先记住最高的.() 最低的赋值和累操作
优先级 | 运算符 | 结合性 |
---|---|---|
1 | () [] . | 从左到右 |
2 | ! +(正) -(负) ~ ++ — | 从右向左 |
3 | * / % | 从左向右 |
4 | +(加) -(减) | 从左向右 |
5 | << >> >>> | 从左向右 |
6 | < <= > >= instanceof | 从左向右 |
7 | == != | 从左向右 |
8 | &(按位与) | 从左向右 |
9 | ^ | 从左向右 |
10 | | | 从左向右 |
11 | && | 从左向右 |
12 | || | 从左向右 |
13 | ?: | 从右向左 |
14 | = += -= *= /= %= &= |= ^= ~= <<= >>= >>>= | 从右向左 |
5. 程序流程控制
5.1. 顺序结构
程序从上到下逐行地执行,中间没有任何判断和跳转。
5.2. 分支结构
根据条件,选择性地执行某段代码。
有if…else和switch两种分支语句。
5.3. 循环结构
5.3.1. 循环
在某些条件满足的情况下, 反复执行特定的代码的功能.
5.3.2. 循环的组成
- 初始化语句 : 作准备工作, 通常是
int i = 0;
- 循环条件 : 控制循环的生死, 如果条件为真就一直循环, 直接条件为假.
i < n
- 循环体 : 被多次执行的语句
- 迭代语句 : 让i向n靠近, 每次
i++
, 使用循环趋于结束, 如果没有迭代, 循环不能自然结束
5.3.3. while
while : 适用于循环次数不确定的循环, 结束取决于外部条件
while (布尔条件) { // 如果布尔条件为假, 循环次数是0~N次
循环体; 在循环中控制布尔
}
5.3.4. do while
do while : 适用于循环次数不确定的循环
do { // 如果布尔条件为假, 至少也要循环1次. 循环次数是1~N次.
循环体; 在循环体中控制布尔
} while (布尔条件);
5.3.5. for
for : 适用于循环次数确定的循环
for (初始语句int i = 0; 循环条件 i < n; 迭代语句i++) {
//循环体;
}
5.3.6. break 与 continue
break
中断某个语句块的执行;
continue
中断某次循环,进入下一次循环。
当我们需要跳出或结束多重循环时,除了在每一个循环体后面加一个break(continue)外,还可以通过label(标号)跳出多重循环(有点类似与goto语句),如下例:
中断外层 for
循环:
public class Exer{
public static void main(String[] args) {
test1();
}
public static void test1(){
int count = 0;
out: for(int i = 0; i < 10; i++){ //语句块 { } 也可以
for(int j=0;j<10;j++){
count++;
if (j == 9) {
break out;
}
}
}
System.out.println(count);
}
}
中断代码块 {}
:
public class BreakTest {
public static void main(String[] args) {
boolean flag = true;
l1 : {
System.out.println("hello");
if (flag) {
break l1;
}
System.out.println("world");
}
System.out.println("three");
}
}
6. 方法
6.1. 方法
java程序中某个功能的封装, 一个独立的功能体. 也称为函数
6.2. 方法声明
修饰符 返回值类型 方法名(数据类型1 形式参数1, 数据类型2 形式参数2, ....) {
语句, 方法体
return 返回值;
}
- 返回值类型 : 方法的功能的最终体现, 成果是什么, 数据类型是什么.
- 方法名 : 标识方法的.
- 形式参数 : 形式上需要的数据, 但是实际数据是多少不影响功能, 在功能的完成时需要的数据, 没有这个数据也不行.
return
返回值, 最终的功能的结果数据要返回给调用者- 调用者 : 使用这个方法的角色. 方法在调用时必须要由调用者传递实际参数.
6.3. 方法结构
方法 = 方法签名 + 方法体(body)
方法签名 : 方法的使用说明书. API
方法体 : 实际执行的代码.
注意:
方法不可以嵌套, 必须是并行的.
6.4. 方法调用
方法调用(method invoke)是一个语句:
方法名(实参列表); // 实参列表必须完全遵照形参的要求来写. 类型和个数及顺序要完全匹配.
- 方法的返回值的接收只有一次机会, 错过了, 就错过了….
- 如果方法没有返回值, 必须使用void作为返回值类型
- 如果方法不需要参数, 参数列表空着, 但是
()
必须要有
注意:
- 没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。
- 定义方法时,方法的结果应该返回给调用者,交由调用者处理。
- 方法中只能调用方法,不可以在方法内部定义方法。
- 方法的返回值只有一次机会接收, 就是在调用时
- 如果在方法中又调用了方法本身, 称为递归调用
6.4.1 栈(Stack)的后进先出
public class MethodTest {
public static void test(int a){
System.our.println("test..." + a);
}
public static int add(int a, int b) {
test(a);
int c = a + b;
return c;
}
public static void main(String[] args) {
int a = 10;
int b = 30;
int c = add(a, b);
System.out.println©;
}
}
从入口方法 main
开始,将 main
压入栈底,执行到 add
,记录现场1,并将 add
压栈执行,add
执行到 test
,记录现场2,将 test
压栈执行,test
执行完成后出栈,返回现场2继续执行(add
继续执行),add
执行完出栈,返回现场1继续执行 main
。
6.4.2 递归 recursion
在方法中调用自身称为递归调用, 无限递归(死归)。
什么情况下用递归? 一个问题可以分解成一个已知处理和子问题。不是告诉计算该怎么做, 而是告诉计算机做什么?比如求n的阶乘。
以一个双重递归的例子来演示:求执行后,num
是多少?即 resursion()
执行了多少次。
@Test
public void Test(){recursion(10);}
private static int num = 0;
public static int recursion(int n){
num++;
if(n <= 0) return 0;
return recursion(n-1) * recursion(n-2);
}
由于 n
最小只能为 -1
,所以我们从此开始归纳:
当 n=-1
时,执行 1
次;
当 n=0
时,执行 1
次;
当 n=1
时,执行 1 + 1 + 1 = 3
次;
当 n=2
时,执行 1 + 3 + 1 = 5
次;
当 n=3
时,执行 1 + 3 + 5 = 9
次;
当 n=4
时,执行 1 + 9 + 5 = 15
次;
当 n=5
时,执行 1 + 15 + 9 = 25
次;
当 n=6
时,执行 1 + 25 + 15 = 41
次;
当 n=7
时,执行 1 + 41 + 25 = 67
次;
当 n=8
时,执行 1 + 67 + 41 = 109
次;
当 n=9
时,执行 1 + 109 + 67 = 177
次;
当 n=10
时,执行 1 + 177 + 109 = 287
次;
6.5. 方法的重载
6.5.1 重载
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
6.5.2. 重载的特点
- 重载(overload) 也可以理解为过载(太多)
- 同一个类中, 方法名相同, 但是参数不同(参数不同体现在类型不同或个数不同或顺序不同), 就可以形成重载, 重载的方法本质上不同的方法.
- 通常有一系列方法, 它们的功能类似, 这样重载才有意义.
- 重载和返回值无关
6.5.3. 重载示例
public class OverloadTest {
public static void add(int a, double b) {
System.out.println("A");
}
public static void add(double a, int b) {
System.out.println("B");
}
public static double add(short a) { // double add(int, double)
System.out.println("C");
}
public static double add(int a) {
System.out.println("D");
}
public static void main(String[] args) {
System.out.println(10, 10); //编译报错,因为两个方法都同时适用
System.out.println((byte) 1); //虽然非int小整数计算会自动转换为int,但这里不是计算,所以输出 C
}
}
6.6. *方法的参数传递
方法,必须有其所在类或对象调用才有意义。若方法含有参数:
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
Java里方法的参数传递方式只有一种:值传递
。
即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
基本数据类型-值是基本型-传递基本数据类型的值;
引用数据类型-值是地址值-传递地址值。