1. 关键字与保留字

关键字 : 在java程序中有特殊含义作用的单词, 字母全部小写
保留字 : 从C++中保留下来的关键字, 在java中目前还不是关键字, 将来有可能是.

2. 标识符

2.1. 标识符

标识某个东西的符号, 简单的来讲就是名字.

2.2. 定义合法标识符规则

  1. 由52个字母, 0~9 10个数字字符, _和$(尽量不要使用)
  2. 数字不可以开头, 标识符3C 就是非法, C3是合法
  3. 不可以使用关键字和保留字, 但是可以包含关键字和保留字
  4. 大小写敏感的, 长度有限制(65535)
  5. 标识符中不能包含空格, 因为空格也有特殊含义.

2.3. 命名规范

  1. 包名 : 所有字母都小写 mypackage
  2. 类名 : 首字母大写, 后面的每个单词的首字母也大写 : MyClassName
  3. 变量名或方法名 : 首字母必须小写, 后面的单词的首字母大写 : myVarName
  4. 常量名 : 所有字母都大写, 单词之间用_隔开 : MY_CONST_NAME

3. **变量

3.1. * 变量的概念

3.1.1. 变量是什么?

变量是内存中的一块被命名的、被特定的数据类型约束的存储区域,此区域中可以保存数据类型范围内的数据。

数据类型: 决定空间大小, 空间可以保存什么数据, 这个数据能做什么.

变量声明: 数据类型 变量名;

3.1.2. 变量注意事项

  • Java中每个变量必须先声明,后使用
  • 该区域有自己的 变量名 和 数据类型
  • 变量有其作用范围,变量的声明所在的{}
  • 同一范围内,变量不允许重复声明
  • 变量内存空间中保存的数据只能在其范围内变化
  1. public class VariableTest {
  2. public static void main(String[] args) {
  3. // 变量声明 : 数据类型 变量名;
  4. int n1; // 作用是在内存中开辟一块4字节空间, 并用n1符号与此空间联系起来
  5. n1 = 10; // 把10这个值写入n1符号指向的空间中. 这是一个赋值操作, 把右侧的值写入左面的变量
  6. System.out.println(n1); // 把n1符号指向的空间中的值取出来并打印输出.
  7. int n2 = n1; // 把n1变量中的值复制出来, 再写入n2变量内存空间中
  8. System.out.println(n2);
  9. n1 = n1 * n2; // 一个变量在某个时刻只能保存一个值, 一旦被重新赋值, 之前的老值永远无法找回
  10. System.out.println(n1); // 变量有时间性, 不同时刻变量中的值有可能不一样.
  11. }
  12. }

3.1.3. 变量的类型

3.1.3.1. 按照数据类型

基本数据类型:内存区域中保存数据本身 不同的类型, 空间大小不同

引用数据类型:内存区域中保存内存地址 不同的类型, 空间大小相同, 和JDK位数相关。

内存地址:内存的每个字节都有一个顺序的编号, 这个编号就是了。本质上来讲就是无符号正整数. 0表示null 空地址.

3.1.3.2. 变量按照声明位置来分 : 声明位置决定了变量的作用范围

局部变量:在方法体内部声明的变量。范围小, 寿命短。局部变量除形参外,需显式初始化

成员变量:在方法体外,类体内声明的变量。范围大, 寿命长。

  1. 所有变量
  2. |---成员变量
  3. | |---实例变量(不以static修饰)
  4. | |---类变量(以static修饰)
  5. |---局部变量
  6. |---形参(方法签名中定义的变量)
  7. |---方法局部变量(在方法内定义)
  8. |---代码块局部变量(在代码块内定义)

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. 基本数据类型转换

自动类型转换:容量小的类型自动转换为容量大的数据类型。

数据类型按容量大小排序为:

基本语法 - 图1

有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。

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”

注意:

  1. + 是取一个数本身;- 取相反数。
  2. / 如果是两个整数相除, 结果会直接丢弃小数部分, 有可能会丢失精度.
  3. % 取余
    如果 n % m == 0 说明n能被m整除.
    如果 n % 2 == 0说明n是偶数, 如果 n % 2 != 0, 说明n是奇数
    N % M 的结果总是小于M, 让一个完全未知的数落在一个已知的M范围内.
    如果对负数取模,可以把模数负号忽略不记,如:5%-2=1。 但被模数是负数则不可忽略。此外,取模运算的结果不一定总是整数。
  4. ++n 前加加, 先加后用, 不需要临时空间 效率高
    n++ 后加加, 先用后加, 需要一个临时空间保存老值(也就是用的值), 效率低
  5. 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两种分支语句。

if else

switch case

5.3. 循环结构

5.3.1. 循环

在某些条件满足的情况下, 反复执行特定的代码的功能.

5.3.2. 循环的组成

  1. 初始化语句 : 作准备工作, 通常是int i = 0;
  2. 循环条件 : 控制循环的生死, 如果条件为真就一直循环, 直接条件为假. i < n
  3. 循环体 : 被多次执行的语句
  4. 迭代语句 : 让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作为返回值类型
  • 如果方法不需要参数, 参数列表空着, 但是()必须要有

注意:

  1. 没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。
  2. 定义方法时,方法的结果应该返回给调用者,交由调用者处理。
  3. 方法中只能调用方法,不可以在方法内部定义方法。
  4. 方法的返回值只有一次机会接收, 就是在调用时
  5. 如果在方法中又调用了方法本身, 称为递归调用

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©;
    }
}

基本语法 - 图2

从入口方法 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. 重载的特点

  1. 重载(overload) 也可以理解为过载(太多)
  2. 同一个类中, 方法名相同, 但是参数不同(参数不同体现在类型不同或个数不同或顺序不同), 就可以形成重载, 重载的方法本质上不同的方法.
  3. 通常有一系列方法, 它们的功能类似, 这样重载才有意义.
  4. 重载和返回值无关

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里方法的参数传递方式只有一种:值传递

即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。

基本数据类型-值是基本型-传递基本数据类型的值;
引用数据类型-值是地址值-传递地址值。