Java基础

12/20/2021
Java基础

# 程序结构

首先了解一个Java程序的开发步骤。

Java开发步骤
  1. 编写源代码文件:我们可以使用任何一个文本编辑器来编写源代码,例如记事本,VS Code等。一般推荐入门使用VS Code,因为毕竟不是开发有一点规模的程序,不太建议一开始就使用eclipse或者IDEA等集成环境。
  2. 编译Java源程序:使用Java编译器(javac.exe)编译源代码,获得字节码文件。
  3. 运行Java程序:使用Java解释器(java.exe)来解析执行字节码文件。

例如以下这个入门级程序

/**
    Java Hello World程序 
    Hello.java
 */
public class Hello{ //Hello为类名
    public static void main(String args[]){
        System.out.println("Hello Java"); //控制台输出
    }
}
1
2
3
4
5
6
7
8
9

说明:

  1. 一个Java应用程序的源代码,是由一个个类组成,当一个类具有public static void main(String args[])方法时,我们称这个类为主类。文件名需要和主类名一致。
  2. Java文件后缀为.java。良好的编程习惯,从记得写注释开始。
  3. 运行javac Hello.java编译java文件(.java)为字节码文件(.class)
  4. 运行java Hello运行java字节码文件,控制台输出Hello Java

# 注释

Java的单行注释以//注释内容为主

Java的多行注释以/*注释内容*/为主

Java的文档注释以/**注释*/为主

其中文档注释与多行注释作用基本相同,唯一的区别是文档注释可以用javadoc.exe命令生成文档API。

/*
 * 实现步骤:
 * 1.定义一个类 class
 * 2.编写程序执行的入口方法,main主方法
 * 3.通过输出语句System.out.println()将信息”HelloWorld!”打印在控制台上
 */
// 定义一个类 class
class HelloWorld {
	// 编写程序执行的入口方法,main主方法
	public static void main(String[] args) {
		// 通过Java提供的输出语句, 将信息”HelloWorld!”打印在控制台上
		System.out.println("HelloWorld!");
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 关键字

关键字是被Java语言赋予特殊含义,具有专门用途的单词,比如之前接触的class,public,static,void均为Java已经预设好的。

Java中的关键字均是小写,以下列出了常见的Java关键字。

image-20211104203651392

# 标识符

标识符就是名称的意思,所有的名称都统称为标识符。在之后Java中经常要定义类、方法和变量等,在定义它们时所给它们起的名字就是标识符。

标识符主要是由英文字符、数字和符号组成。其中不能以数字开头,不能使用关键字,严格区分大小写。

# 数据类型

Java语言有8种基本数据类型,按照习惯可分为:

# 逻辑类型:boolean

  • 常量表达:true、false

  • 变量表达:使用关键字boolean来声明逻辑变量

    boolean on = true,off = false;
    
    1

# 整数类型:byte、short、int、long

  • 常量表达:整型的常量就是我们平时熟悉的不带小数点的数字

  • 变量表达:

    • byte类型

      使用关键字byte来声明整型变量

      byte x = -12;
      
      1

      byte类型变量内存分配1个字节,占8位,取值范围为27271-2^7 \sim 2^7-1

    • short类型

      使用关键字short来声明整型变量

      short x = -12;
      
      1

      short类型变量内存分配2个字节,占16位,取值范围为2152151-2^{15} \sim 2^{15}-1

    • int类型

      使用关键字int来声明整型变量

      int x = -12;
      
      1

      int类型变量内存分配4个字节,占32位,取值范围为2312311-2^{31} \sim 2^{31}-1

    • long类型

      使用关键字long来声明整型变量

      long x = -12;
      
      1

      long类型变量内存分配8字节,占64位,取值范围为2632631-2^{63} \sim 2^{63}-1

    注意:Java没有无符号类型的byte、short、int、long,与C/C++有很大不同。

# 字符类型:char

  • 常量表达:用单引号括起来的Unicode表种的一个字符

  • 变量表达:使用关键字char来声明字符变量

    char ch = 'A';
    
    1

    char类型内存分配2个字节,占16位,最高位不是符号位,没有负数的char,取值范围为0~65535。

    对于

    char x = 'a';
    
    1

    那么内存x中存储的是97,97是字符a在Unicode表中的排序位置。

# 浮点类型:float、double

  • 常量表达:浮点的常量就是我们平时熟悉的带小数点的数字

  • 变量表达:

    • float类型

      使用关键字float声明浮点变量

      float x = -12.0;
      
      1

      float类型内存分配4个字节,占32位,取值范围为1.4E453.4028235E381.4E^{-45} \sim 3.4028235E383.4028235E381.4E45-3.4028235E38 \sim -1.4E^{-45}

      float变量在存储float数据类型时保留到8位有效数字

    • double

      使用关键字double来声明浮点变量

      double x = -12.0;
      
      1

      double类型内存分配8字节,占64位,取值范围为

      double变量在存储double数据类型时保留到16位有效数字

# 数据类型转换

  • 当把级别低的变量的值赋值给级别高的变量时,系统会自动完成数据类型转换

    int x = 50;
    float y;
    y = x
    //如果把y输出,会是50.0
    
    1
    2
    3
    4
  • 当把级别高的变量的值赋给级别低的变量时,必须使用类型转换运算,格式如下:

    //(类型名)要转换的值
    int x = (int)34.89;
    long y = (long)56.98F;
    int z = (int)1999L;
    
    1
    2
    3
    4

    注意:当把一个int类型常量赋值给一个byte、short和char类型变量时,不可以超过这些变量的取值范围,否则必须进行类型转换运算。

    例如,常量128的属于int类型常量,超过byte变量的取值范围,如果赋值给byte,则必须进行byte类型转换运算,这将会导致精度的损失

    byte a = (byte) 128 //-128
    byte b = (byte)(-129) //127
    //具体值是如何变化的,需要参考补码和反码等相关知识
    
    1
    2
    3

    另外,一个常见的错误就是把一个double类型的常量赋值给float类型变量没有进行类型转换运算。

    例如:

    float x = 12.4; //报错 possible loss of percision
    float x = 12.4F; //正确
    float x = (float)12.4; //正确
    
    1
    2
    3

# 常量

常量就是不变的数据量,例如100就是常量,任何数据量都有其类型。

  • 整数类型
    • 十进制表示方式:正常数字 如 13、25等
    • 二进制表示方式:以0b(0B)开头 如0b1011 、0B1001
    • 十六进制表示方式:以0x(0X)开头 数字以0-9及A-F组成 如0x23A2、0xa、0x10
    • 八进制表示方式:以0开头 如01、07、0721
  • 小数类型 如1.0、-3.15、3.168等
  • 布尔类型 true、false
  • 字符类型 如'a','A', '0', '家'。字符必须使用’’ 包裹,并且其中只能且仅能包含一个字符。
  • 字符串类型 字符串String类型是一种引用类型,我们先了解作为常量类型的使用方式,如“我爱Java”,“0123”,“”,“null”,字符串必须使用“”包裹,其中可以包含0~N个字符。

# 变量

变量是内存中的小容器,用于存储数据。计算机存储设备的最小信息单元叫“位(bit)”,我们又称为“比特位”,通常用小写的字母b表示。而计算机最小的存储单元叫“字节(byte)”,通常用大写字母B表示,字节是由连续的8个位组成。

当程序需要使用存储空间时,操作系统最小会分派给程序1个字节,而不是1个位。你可能会说,如果程序只需要1个位的空间,系统分派不能只分派1个位吗?答案是不能!这就像你只需要1支烟,你到商店去买烟,商店分派的最小单元是1盒(20支),他不可能卖给你1支烟。

你可能会想,1个字节(8位)可以存储很大的数值了,1位最大是9那么8位最大值为99999999。你错了,因为计算机是采用二进行存储的,而不是我们生活中常用的十进制。所以1个字节存储的最大数据是11111111的二进制数。

除了字节外还有一些常用的存储单位,大家可能比较熟悉,我们一起来看看:

  • 1B(字节) = 8bit
  • 1KB = 1024B
  • 1MB = 1024KB
  • 1GB = 1024MB
  • 1TB = 1024GB

# 变量的创建

大衣柜不能用来装载水,水杯也不能用来装载衣裤。这说明不同的容器装载不同的物品。变量也是如此,在创建变量时需要指定变量的数据类型,例如整型变量、浮点型变量等等。

结论:变量必须要有明确的类型,什么类型的变量装载什么类型的数据。

水杯是用来装水的,那么水杯能装多少水呢?一吨?我们知道水杯在创建时不只确定了要装载的是水(数据类型),而且还确定了能装多少水(数据类型的具体种类)。变量也是如此,需要指定变量能装载什么类型的数据,同时也要指定变量能装载多大的数据。

定义变量的语法格式:

数据类型  变量名  =  数据值;
int     a    =  100;
1
2
  • 其中int是数据类型,指定了变量只能存储整数,而且指定了存储范围为-2147483648~2147483648。

  • 其中a表示变量名,变量名是标识符,这说明只要是合法的标识符都可以用来做变量名。在程序中可以通过变量名来操作变量(内存中的小盒子)。

  • 其中“=100”是给变量赋值,即向a变量中写入100(变量是个小盒子,现在小盒子中保存的是100)。注意,给变量赋的值一定要与类型符合,也就是说int类型只能存储整数,而且必须是在-2147483648~2147483648范围内的整数。100满足了这两个条件,所以是正确的。

/*
变量定义格式:
数据类型  变量名  =  变量值;
*/
public class Variable {
	public static void main(String[] args) {
		int a = 10;
		double b = 3.14;
		char c = 'z';
		String s = "i love java";
	
		a = 20;
		System.out.println(a);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 变量的注意事项

  • 变量定义后可以不赋值,使用时再赋值。不赋值不能使用。

    public static void main(String[] args) {
        int x;
        x = 20; //为x赋值20
        System.out.println(x);//读取x变量中的值,再打印
    }
    
    1
    2
    3
    4
    5
  • 变量使用有作用域的限制

    public static void main(String[] args) {
        int x = 20;
        {
            int y = 20;
        }
        System.out.println(x);//读取x变量中的值,再打印
        System.out.println(y);//读取y变量中的值失败,失败原因,找不到y变量,因为超出了y变量作用范围,所以不能使用y变量
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 变量不可以重复定义

    public static void main(String[] args){
          int x = 10;
          double x = 5.5;//编译失败,变量重复定义
    }
    
    1
    2
    3
    4

# 数据类型转换

若想要不同的数据类型直接进行运算,就需要进行数据类型的转换。数据类型的转换遵循以下原则:

  • 范围小的数据类型值(如byte),可以直接转换为范围大的数据类型值(如int);
  • 范围大的数据类型值(如int),不可以直接转换为范围小的数据类型值(如byte)

将各种数据类型按照数据范围从小到大依次列出:

byte -> short -> int -> long -> float -> double
1

数据类型有两种转换方法

  • 自动类型转换

    表示范围小的数据类型转换成范围大的数据类型,这种方式称为自动类型转换。

    自动类型转换格式:范围大的数据类型 变量 = 范围小的数据类型值;

    int i = 100;
    double d2 = i;
    
    1
    2
  • 强制类型转换

    表示范围大的数据类型转换成范围小的数据类型,这种方式称为强制类型转换。强制类型转换会导致精度损失。

    强制类型转换格式:范围小的数据类型 变量 = (范围小的数据类型) 范围大的数据类型值;

    double  d = 3.14;
    int  i2 = (int)d;     //i2的值为3
    
    1
    2

# 运算符

运算符是用来计算数据的符号。数据可以是常量,也可以是变量。被运算符操作的数我们称为操作数。

# 算术运算符

算术运算符最常见的操作就是将操作数参与数学计算,具体使用看下图:

image-20211104211127750

在使用算术运算符时,需要注意下列事项:

  • 加法运算符在连接字符串时要注意,只有直接与字符串相加才会转成字符串。

  • 除法“/”当两边为整数时,取整数部分,舍余数。当其中一边为浮点型时,按正常规则相除。

  • “%”为整除取余符号,小数取余没有意义。结果符号与被取余符号相同。

  • 整数做被除数,0不能做除数,否则报错。

/*
 * 算术运算符
 */
public class OperatorDemo1 {
	public static void main(String[] args) {
		/*
		 * 常量使用算数运算符
		 */
		System.out.println(10+20);
		
		/*
		 * 变量使用算数运算符
		 */
		int x = 10;
		int y = 20;
		//"+"作为加法运算使用
		int z = x + y; 
		//"+"作为连接字符串使用
		System.out.println("x="+x);
		System.out.println("y="+y);
		System.out.println("z="+z);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

在一般情况下,算数运算符不会改变参与计算的变量值。而是在原有变量值不变的情况下,计算出新的值。但是有些操作符会改变参与计算的变量的值,比如++,--

int a = 3;
int b = 3;
a++;
b--;
System.out.println(a);
System.out.println(b);
1
2
3
4
5
6

上面代码的输出结果a值为4,b值为2;这说明a的原有值发生了改变,在原有值的基础上自增1;b的原有值也发生了改变,在原有值的基础上自减1。

  • ++运算符,会在原有值的基础上自增1
  • --运算符,会在原有值的基础上自减1
int a = 3;
int b = 3;
++a;
--b;
System.out.println(a);
System.out.println(b);
1
2
3
4
5
6

上面代码的输出结果a值为4,b值为2; 这说明++,--运算符单独使用,不参与运算操作时,运算符前后位置导致的运算结果是一致的。

接下来,介绍下++,--运算符参与运算操作时,发生了怎样的变化。

  • ++,--运算符前置时,先将变量a的值自增1或者自减1,然后使用更新后的新值参与运算操作。

    int a = 3;
    int b;
    b = a++ + 10;
    System.out.println(a);
    System.out.println(b);
    
    1
    2
    3
    4
    5

    上面代码的输出结果a值为4,b值为13;

  • ++和--运算符后置时,先使用变量a原有值参加运算操作,运算操作完成后,变量a的值自增1或者自减1

    int a = 3;
    int b;
    b = ++a + 10;
    System.out.println(a);
    System.out.println(b);
    
    1
    2
    3
    4
    5

    上面代码的输出结果a值为4,b值为14;

# 赋值运算符

赋值运算符就是为变量赋值的符号,赋值运算符的使用看下图:

image-20211104211838930

注意:诸如+=这样形式的赋值运算符,会将结果自动强转成等号左边的数据类型。

/*
 * 赋值运算符
 * +=, -=, *=, /=, %= : 
 * 上面的运算符作用:将等号左右两边计算,会将结果自动强转成等号左边的数据类型,再赋值给等号左边的
 * 注意:赋值运算符左边必须是变量
 */
public class OperatorDemo2 {
	public static void main(String[] args) {
		byte x = 10;
		x += 20;// 相当于 x = (byte)(x+20);
		System.out.println(x);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 比较运算符

比较运算符,又叫关系运算符,它是用来判断两个操作数的大小关系及是否相等关系的,结果是布尔值true或者false。

image-20211104212004869

赋值运算符的 = 符号与比较运算符的 == 符号是有区别的,如下:

  • 赋值运算符的 = 符号,是用来将 = 符号右边的值,赋值给 = 符号左边的变量;
  • 比较运算符的 == 符号,是用来判断 == 符号 左右变量的值是否相等的。
int a = 3;
int b = 4;
System.out.println( a=b );
System.out.println( a==b );
1
2
3
4

上面代码输出的结果第一个值为4,第二个值为false。

# 位运算符

在计算机内部所有的数据都是二进制的形式存储在设备内,即0,1两种状态。所谓位运算是指对二进制数据进行的运算。

运算符 描述 运算规则
& 两个位都为1时,结果为1
| 两个位都位0时,结果位0
^ 异或 两个位相同位为0,想异为1
~ 取反 0变1,1变0
<< 左移 各二进制全部左移若干位,高位丢弃,低位补0
>> 右移 各二进制全部右移若干位,高位丢弃,低位补0

以3和5的位运算为例

  • 与运算

    运算规则:0&0=0  0&1=0  1&0=0  1&1=1
    3&5 即 0000 0011 & 0000 0101 = 0000 0001,因此 3&5 的值得1。
    
    1
    2

    用途:

    • 清零

      如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

    • 取一个数的指定位

      比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

    • 判断奇偶

      只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

  • 或运算

    运算规则:0|0=0  0|1=1  1|0=1  1|1=1
    3 | 5 即 0000 0011 | 0000 0101 = 0000 0111,因此3 | 5 的值为7。  
    
    1
    2

    用途:

    • 常用来对一个数据的某些位设置为1

      比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

  • 异或运算

    运算规则:0^0=0  0^1=1  1^0=1  1^1=0
    总结:参加运算的两个对象,如果两个相应位相同为0,相异为1。
    
    1
    2

    异或的几条性质:

    1. 交换律
    2. 结合律 (a^b)^c == a^(b^c)
    3. 对于任何数x,都有 x^x=0,x^0=x
    4. 自反性: a^b^b=a^0=a

    用途:

    • 翻转指定位

      比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。

    • 与0相异或值不变

      例如:1010 1110 ^ 0000 0000 = 1010 1110(性质3)

    • 交换两个数

      if (a != b){
              a ^= b;
              b ^= a;
              a ^= b;
       }
      
      1
      2
      3
      4
      5
  • 取反

    运算规则:~1= 0 ~0 = 1
    总结:对一个二进制数按位取反,即将0变1,1变0。
    
    1
    2

    用途:

    • 使一个数的最低位为零

      使a的最低位为0,可以表示为:a & ~1。~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。因为" ~"运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

  • 左移

    定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

    设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。

    若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

  • 右移

    定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

    例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。

    操作数每右移一位,相当于该数除以2。

参考:传送门1 (opens new window)传送门2 (opens new window)

# 逻辑运算符

逻辑运算符,它是用于布尔值进行运算的,运算的最终结果为布尔值true或false。

运算符 运算规则 范例 结果
&& 短路与 false && true false
|| 短路或 false || true true
! !true False

逻辑运算符的常规使用方式:

  • 逻辑运算符通常连接两个其他表达式计算后的布尔值结果
  • 当使用短路与或者短路或时,只要能判断出结果则后边的部分就不再判断。
boolean b = 100>10;
boolean b2 = false;
System.out.println(b&&b2); // 打印结果为 false
System.out.println(b||b2); //打印结果为 true
System.out.println(!b2); //打印结果为 true
System.out.println(b && 100>10); //打印结果为 true
1
2
3
4
5
6

总结一下运算符的结果规律:

  • 短路与&&:参与运算的两边数据,有false,则运算结果为false;
  • 短路或||:参与运算的两边数据,有true,则运算结果为true;
  • 逻辑非!:参与运算的数据,原先是true则变成false,原先是false,则变成true。

# 三目运算符

格式:(条件表达式)?表达式1:表达式2;

表达式:通俗的说,即通过使用运算符将操作数联系起来的式子,例如

  • 3+2,使用算数运算符将操作数联系起来,这种情况,我们称为算数表达式。
  • 3>2,使用比较运算符(也称为条件运算符)将操作数联系起来,这种情况,我们称为条件表达式。

三元运算符运算规则:先判断条件表达式的值,若为true,运算结果为表达式1;若为false,运算结果为表达式2。

方式一:
	System.out.println( 3>2 ? “正确” : “错误” ); 
// 三元运算符运算后的结果为true,运算结果为表达式1的值“正确”,然后将结果“正确”,在控制台输出打印

方式二:
	int a = 3;
	int b = 4;
	String result = (a==b) ? “相等” : “不相等”;  
//三元运算符运算后的结果为false,运算结果为表达式2的值“不相等”,然后将结果赋值给了变量result

方式三:
	int n = (3>2 && 4>6) ? 100 : 200;
	//三元运算符运算后的结果为false,运算结果为表达式2的值200,然后将结果200赋值给了变量n
1
2
3
4
5
6
7
8
9
10
11
12
13

# 运算符的优先级

当多个运算符一起使用时,需要考虑运算之间的优先级。下图是每种运算符的优先级,按照运算先后顺序排序(优先级相同的情况下,按照从左到右的顺序依次运算)。

image-20211104212652072

# 引用数据类型

与基本数据类型变量不同,引用数据类型的变量定义及赋值有一个相对固定的步骤和格式。

数据类型  变量名  =  new 数据类型();
1

每种引用数据类型都有其功能,我们可以调用该类型实例的功能。

变量名.方法名();
1

# 输入与输出

# 输入

Scanner是JDK1.5新增的一个类,可以创建一个对象用于输入。创建格式如下

Scanner reader = new Scanner(System.in);
1

使用reader对象,调用以下方法可以输入各种基本数据类型;

  • nextBoolean(); 布尔型
  • nextByte()、nextShort()、nextInt()、nextLong();整型
  • nextFloat()、nextDouble();浮点型

例如:

/**
    输入程序
 */
import java.util.Scanner;

public class Example3{// Example3为类名

    public static void main(String[] args){

        System.out.println("请输入若干个数,每输入一个数回车确认");
        System.out.println("输入数字0表示结束");
        Scanner reader = new Scanner(System.in);
        double sum = 0;
        double x = reader.nextDouble();
        while(x!=0){
            sum = sum+x;
            x= reader.nextDouble();
        }
        System.out.println("sum="+sum);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 输出

System.out.println()System.out.print()可以用于输出,两者主要区别是前者输出换行,后者输出不换行。允许使用+,将变量、表达式或者一个常量数值与一个字符串连接一起输出。

例如:

System.out.println("你的年龄是:"+age);
System.out.println("你的身份是:"+job);
1
2

注意:在使用这两个语句时,不可以出现"回车",不然无法编译。若要输出的字符串太长,可以用+将它们连接。

另外JDK1.5新增格式控制,格式如下:

System.out.printf("格式控制部分",表达式1、表达式2、表达式3...、表达式n);
1
  • %d:输出int
  • %c:输出char
  • %f:输出浮点型,小数部分保留6位
  • %s:输出字符串
  • %md:输出的int占m列
  • %m.nf:输出的浮点型占m列,小数点占n位。

例如:

System.out.printf("%d,%f",8858,88.59);
1

# 程序流程控制

# 顺序结构

顺序结构是指程序从上到下执行按照顺序执行一次,通常我们所写的正常的程序都为顺序结构。

# 条件结构

条件结构是指程序到某个节点会执行条件判断,从而执行不同的内容。Java的条件结构主要分为以下几种。参考传送门 (opens new window)

# if

if(布尔表达式)
{
   //如果布尔表达式为true将执行的语句
}
1
2
3
4

如果布尔表达式为true,则执行if语句内的代码块,否则执行if后面的代码块。

public class Test {
 
   public static void main(String args[]){
      int x = 10;
 
      if( x < 20 ){
         System.out.print("这是 if 语句");
      }
   }
}
1
2
3
4
5
6
7
8
9
10

image-20211105200828908

# if else

if 语句后面可以跟 else 语句,当 if 语句的布尔表达式值为 false 时,else 语句块会被执行。

if(布尔表达式){
   //如果布尔表达式的值为true
}else{
   //如果布尔表达式的值为false
}
1
2
3
4
5
public class Test {
 
   public static void main(String args[]){
      int x = 30;
 
      if( x < 20 ){
         System.out.print("这是 if 语句");
      }else{
         System.out.print("这是 else 语句");
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12

image-20211105200846009

# if...else if...else

if语句后面可以跟 else if…else 语句,这种语句可以检测到多种可能的情况。

使用 if,else if,else 语句的时候,需要注意下面几点:

  • if 语句至多有 1 个 else 语句,else 语句在所有的 else if 语句之后。
  • if 语句可以有若干个 else if 语句,它们必须在 else 语句之前。
  • 一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行。
if(布尔表达式 1){
   //如果布尔表达式 1的值为true执行代码
}else if(布尔表达式 2){
   //如果布尔表达式 2的值为true执行代码
}else if(布尔表达式 3){
   //如果布尔表达式 3的值为true执行代码
}else {
   //如果以上布尔表达式都不为true执行代码
}
1
2
3
4
5
6
7
8
9
public class Test {
   public static void main(String args[]){
      int x = 30;
 
      if( x == 10 ){
         System.out.print("Value of X is 10");
      }else if( x == 20 ){
         System.out.print("Value of X is 20");
      }else if( x == 30 ){
         System.out.print("Value of X is 30");
      }else{
         System.out.print("这是 else 语句");
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

image-20211105200901602

# switch case

switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。

switch(expression){
    case value :
       //语句
       break; //可选
    case value :
       //语句
       break; //可选
    //你可以有任意数量的case语句
    default : //可选
       //语句
}
1
2
3
4
5
6
7
8
9
10
11

switch case 语句有如下规则:

  • switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
  • switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
  • case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
  • 当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
  • 当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
  • switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。

switch case 执行时,一定会先进行匹配,匹配成功返回当前 case 的值,再根据是否有 break,判断是否继续输出,或是跳出判断。

public class Test {
   public static void main(String args[]){
      int i = 5;
      switch(i){
         case 0:
            System.out.println("0");
         case 1:
            System.out.println("1");
         case 2:
            System.out.println("2");
         default:
            System.out.println("default");
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 循环结构

若想要语句被多次执行,则需要使用循环结构。参考传送门 (opens new window)

Java中有三种主要的循环结构:

  • while循环
  • do...while循环
  • for循环

# while

while( 布尔表达式 ) {
  //循环内容
}
1
2
3

在上面的语法结构中,{}中的执行语句被称作循环体,循环体是否执行取决于循环条件。当循环条件为true时,循环体就会执行。循环体执行完毕时会继续判断循环条件,如条件仍为true则会继续执行,直到循环条件为false时,整个循环过程才会结束。

image-20211105201725938

public class Test {
   public static void main(String args[]) {
      int x = 10;
      while( x < 20 ) {
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }
   }
}
1
2
3
4
5
6
7
8
9
10

# do...while

对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。

do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。

do {
       //代码语句
}while(布尔表达式);
1
2
3

image-20211105201905018

布尔表达式在循环体的后面,所以语句块在检测布尔表达式之前已经执行了。 如果布尔表达式的值为 true,则语句块一直执行,直到布尔表达式的值为 false。

public class Test {
   public static void main(String args[]){
      int x = 10;
 
      do{
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }while( x < 20 );
   }
}
1
2
3
4
5
6
7
8
9
10
11

# for

Java提供了for循环,for循环的结构相对清晰和简单。

for循环执行的次数是在执行前就确定的。语法格式如下:

for(初始化; 布尔表达式; 更新) {
    //代码语句
}
1
2
3

关于 for 循环有以下几点说明:

  • 最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
  • 然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
  • 执行一次循环后,更新循环控制变量。
  • 再次检测布尔表达式。循环执行上面的过程。
public class Test {
   public static void main(String args[]) {
 
      for(int x = 10; x < 20; x = x+1) {
         System.out.print("value of x : " + x );
         System.out.print("\n");
      }
   }
}
1
2
3
4
5
6
7
8
9

# 增强for

Java5 引入了一种主要用于数组的增强型 for 循环。

Java 增强 for 循环语法格式如下:

for(声明语句 : 表达式)
{
   //代码句子
}
1
2
3
4

声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

表达式:表达式是要访问的数组名,或者是返回值为数组的方法。

public class Test {
   public static void main(String args[]){
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ){
         System.out.print( x );
         System.out.print(",");
      }
      System.out.print("\n");
      String [] names ={"James", "Larry", "Tom", "Lacy"};
      for( String name : names ) {
         System.out.print( name );
         System.out.print(",");
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# break和continue

break主要在循环语句和switch语句内使用,用来跳出整个语句块。break 跳出最里层的循环,并且继续执行该循环下面的语句。

public class Test {
   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ) {
         // x 等于 30 时跳出循环
         if( x == 30 ) {
            break;
         }
         System.out.print( x );
         System.out.print("\n");
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。

在 for 循环中,continue 语句使程序立即跳转到更新语句。

在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。

public class Test {
   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ) {
         if( x == 30 ) {
        continue;
         }
         System.out.print( x );
         System.out.print("\n");
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 循环嵌套

嵌套循环是指在一个循环语句的循环体中再定义一个循环语句的语法结构。while、do…while、for循环语句都可以进行嵌套,并且它们之间也可以互相嵌套,如最常见的在for循环中嵌套for循环,格式如下:

for(初始化表达式; 循环条件; 操作表达式) {
	………
	for(初始化表达式; 循环条件; 操作表达式) {
		执行语句
		………
	}
	………
}

1
2
3
4
5
6
7
8
9
public class ForForDemo {
    public static void main(String[] args) {
        int i, j; // 定义两个循环变量
        for (i = 1; i <= 9; i++) { // 外层循环
            for (j = 1; j <= i; j++) { // 内层循环
                System.out.print("*"); // 打印*
            }
            System.out.print("\n"); // 换行
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

在上述代码中定义了两层for循环,分别为外层循环和内层循环,外层循环用于控制打印的行数,内层循环用于打印“”,每一行的“”个数逐行增加,最后输出一个直角三角形。由于嵌套循环程序比较复杂,下面分步骤进行详细地讲解,具体如下:

  • 第一步:在第3行代码定义了两个循环变量i和j,其中i为外层循环变量,j为内层循环变量。
  • 第二步:在第4行代码将i初始化为1,条件i <= 9为true,首次进入外层循环的循环体。
  • 第三步:在第5行代码将j初始化为1,由于此时i的值为1,条件j <= i为true,首次进入内层循环的循环体,打印一个“*”。
  • 第四步:执行第5行代码中内层循环的操作表达式j++,将j的值自增为2。
  • 第五步:执行第5行代码中的判断条件j<=i,判断结果为false,内层循环结束。执行后面的代码,打印换行符。
  • 第六步:执行第4行代码中外层循环的操作表达式i++,将i的值自增为2。
  • 第七步:执行第4行代码中的判断条件i<=9,判断结果为true,进入外层循环的循环体,继续执行内层循环。
  • 第八步:由于i的值为2,内层循环会执行两次,即在第2行打印两个“*"。在内层循环结束时会打印换行符。
  • 第九步:以此类推,在第3行会打印3个“*”,逐行递增,直到i的值为10时,外层循环的判断条件i <= 9结果为false,外层循环结束,整个程序也就结束了。

# 数组

在生活中,我们可能会碰到如下的场景。现在需要统计某公司员工的工资情况,例如计算平均工资、最高工资等。假设该公司有50名员工,用前面所学的知识完成,那么程序首先需要声明50个变量来分别记住每位员工的工资,这样做会显得很麻烦。其实在Java中,我们可以使用一个数组来记住这50名员工的工资。数组是指一组数据的集合,数组中的每个数据被称作元素。在数组中可以存放任意类型的元素,但同一个数组里存放的元素类型必须一致。

# 声明与创建

Java使用new操作符来创建数组,语法如下:

数据类型[] 数组名 = new 数据类型[元素个数或数组长度];
1

例如

int[] x = new int[100]
1

上述语句就相当于在内存中定义了100个int类型的变量,第一个变量的名称为x[0],第二个变量的名称为x[1],以此类推,第100个变量的名称为x[99],这些变量的初始值都是0。

为了更好地理解数组的这种定义方式,可以将上面的一句代码分成两句来写,具体如下:

int[] x;	          // 声明一个int[]类型的变量
x = new int[100];	// 创建一个长度为100的数组
1
2

第一行代码 int[] x; 声明了一个变量x,该变量的类型为int[],即一个int类型的数组。变量x会占用一块内存单元,它没有被分配初始值。内存中的状态如下图所示。

image-20211105202738289

第二行代码 x = new int[100]; 创建了一个数组,将数组的地址赋值给变量x。在程序运行期间可以使用变量x来引用数组,这时内存中的状态会发生变化,如下图所示。

image-20211105202759458

在上图中描述了变量x引用数组的情况。该数组中有100个元素,初始值都为0。数组中的每个元素都有一个索引(也可称为角标),要想访问数组中的元素可以通过“x[0]、x[1]、……、x[98]、x[99]”的形式。需要注意的是,数组中最小的索引是0,最大的索引是“数组的长度-1”。在Java中,为了方便我们获得数组的长度,提供了一个length属性,在程序中可以通过“数组名.length”的方式来获得数组的长度,即元素的个数。 接下来,通过一个案例来演示如何定义数组以及访问数组中的元素,如下所示。

public class ArrayDemo01 {
    public static void main(String[] args) {
        int[] arr; // 声明变量
        arr = new int[3]; // 创建数组对象
        System.out.println("arr[0]=" + arr[0]); // 访问数组中的第一个元素
        System.out.println("arr[1]=" + arr[1]); // 访问数组中的第二个元素
        System.out.println("arr[2]=" + arr[2]); // 访问数组中的第三个元素
        System.out.println("数组的长度是:" + arr.length); // 打印数组长度
    }
}
1
2
3
4
5
6
7
8
9
10

在上述代码中声明了一个int[]类型变量arr,并将数组在内存中的地址赋值给它。在5~7行代码中通过角标来访问数组中的元素,在第8行代码中通过length属性访问数组中元素的个数。从打印结果可以看出,数组中的三个元素初始值都为0,这是因为当数组被成功创建后,数组中元素会被自动赋予一个默认值,根据元素类型的不同,默认初始化的值也是不一样的。具体如下表所示。

image-20211105202931677

如果在使用数组时,不想使用这些默认初始值,也可以显式地为这些元素赋值。接下来通过一个程序来学习如何为数组的元素赋值,如下所示。

public class ArrayDemo02 {
    public static void main(String[] args) {
        int[] arr = new int[4]; // 定义可以存储4个整数的数组
        arr[0] = 1; // 为第1个元素赋值1
        arr[1] = 2; // 为第2个元素赋值2
        // 下面的代码是打印数组中每个元素的值
        System.out.println("arr[0]=" + arr[0]);
        System.out.println("arr[1]=" + arr[1]);
        System.out.println("arr[2]=" + arr[2]);
        System.out.println("arr[3]=" + arr[3]);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

在上述代码中,第3行代码定义了一个数组,此时数组中每个元素都为默认初始值0。第2、3行代码通过赋值语句将数组中的元素arr[0]和arr[1]分别赋值为1和2,而元素arr[2]和arr[3]没有赋值,其值仍为0,因此打印结果中四个元素的值依次为1、2、0、0。

在定义数组时只指定数组的长度,由系统自动为元素赋初值的方式称作动态初始化。

在初始化数组时还有一种方式叫做静态初始化,就是在定义数组的同时就为数组的每个元素赋值。数组的静态初始化有两种方式,具体格式如下:

1、类型[] 数组名 = new 类型[]{元素,元素,……};
2、类型[] 数组名 = {元素,元素,元素,……};	   
1
2

上面的两种方式都可以实现数组的静态初始化,但是为了简便,建议采用第二种方式。如下所示。

public class ArrayDemo03 {
    public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4 }; // 静态初始化
        // 下面的代码是依次访问数组中的元素
        System.out.println("arr[0] = " + arr[0]);
        System.out.println("arr[1] = " + arr[1]);
        System.out.println("arr[2] = " + arr[2]);
        System.out.println("arr[3] = " + arr[3]);
    }
}
1
2
3
4
5
6
7
8
9
10

# 数组访问

数组是通过索引访问的,索引从0开始到length-1。数组的访问常用for或者增强for的方式。

public class ArrayDemo04 {
	public static void main(String[] args) {
		int[] arr = { 1, 2, 3, 4, 5 }; // 定义数组
		// 使用for循环遍历数组的元素
		for (int i = 0; i < arr.length; i++) {
			System.out.println(arr[i]); // 通过索引访问元素
		}
	}
}
1
2
3
4
5
6
7
8
9

上述代码中,定义一个长度为5的数组arr,数组的角标为0~4。由于for循环中定义的变量i的值在循环过程中为0~4,因此可以作为索引,依次去访问数组中的元素,并将元素的值打印出来。

# 数组异常

# 数组越界异常

每个数组的索引都有一个范围,即0~length-1。在访问数组的元素时,索引不能超出这个范围,否则程序会报错,如下所示。

public class ArrayDemo06 {
    public static void main(String[] args) {
        int[] arr = new int[4]; // 定义一个长度为4的数组
        System.out.println("arr[0]=" + arr[4]); // 通过角标4访问数组元素
    }
}
1
2
3
4
5
6

运行结果中所提示的错误信息是数组越界异常ArrayIndexOutOfBoundsException,出现这个异常的原因是数组的长度为4,其索引范围为0~3,而上述代码中的第4行代码使用索引4来访问元素时超出了数组的索引范围。

所谓异常指程序中出现的错误,它会报告出错的异常类型、出错的行号以及出错的原因

# 空指针异常

在使用变量引用一个数组时,变量必须指向一个有效的数组对象,如果该变量的值为null,则意味着没有指向任何数组,此时通过该变量访问数组的元素会出现空指针异常,接下来通过一个案例来演示这种异常,如下所示。

public class ArrayDemo07 {
    public static void main(String[] args) {
        int[] arr = new int[3]; // 定义一个长度为3的数组
        arr[0] = 5; // 为数组的第一个元素赋值
        System.out.println("arr[0]=" + arr[0]); // 访问数组的元素
        arr = null; // 将变量arr置为null
        System.out.println("arr[0]=" + arr[0]); // 访问数组的元素
    }
}
1
2
3
4
5
6
7
8
9

上述代码中第4、5行代码都能通过变量arr正常地操作数组。第6行代码将变量置为null,当第7行代码再次访问数组时就出现了空指针异常NullPointerException。

# 二维数组

二维数组的定义有很多方式,接下来针对几种常见的方式进行详细地讲解,具体如下。

  • 第一种方式

    int[][] arr = new int[3][4];
    
    1

    上面的代码相当于定义了一个3*4的二维数组,即二维数组的长度为3,二维数组中的每个元素又是一个长度为4的数组,接下来通过一个图来表示这种情况,如下图所示。

    image-20211105203731436

  • 第二种方式

    int[][] arr = new int[3][];
    
    1

    第二种方式和第一种类似,只是数组中每个元素的长度不确定,接下来通过一个图来表示这种情况,如下图所示。

    image-20211105203802896

  • 第三种方式

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

    上面的二维数组中定义了三个元素,这三个元素都是数组,分别为{1,2}、{3,4,5,6}、{7,8,9},接下来通过一个图来表示这种情况,如下图所示。

    image-20211105203849865

对二维数组中元素的访问也是通过角标的方式,如需访问二维数组中第一个元素数组的第二个元素,具体代码如下:

arr[0][1]
1

# 二维数组遍历

一维数据靠for能快速循环遍历,二维数据也可以通过for循环快速遍历,但是要通过循环嵌套的方式。

class ArrayDemo09 {
	public static void main(String[] args){
		//一维数组的求累加和并遍历
		int[] arr = {10,20,30,40,50};
		int sum = 0; 
		for (int i=0; i<arr.length; i++) {
              //System.out.println(arr[i]);
			sum += arr[i];
		}
		System.out.println("sum= " + sum);
		System.out.println("---------------------");
		
		//二维数组的求累加和并遍历
		int[][] arr2 = { {1,2},{3,4,5},{6,7,8,9,10} };
		int sum2 = 0;
		for (int i=0; i<arr2.length; i++) {
			for (int j=0; j<arr2[i].length; j++) {
                 //System.out.println(arr2[i][j])
				sum2 += arr2[i][j];
			}
		}
		System.out.println("sum2= "+ sum2);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 数组操作

java.util.Arrays类能方便地操作数据,它提供的所有方法是静态的。

具备以下功能:

  • 给数组赋值:通过 fill 方法。
  • 对数组排序:通过 sort 方法,按升序。
  • 比较数组:通过 equals 方法比较数组中元素值是否相等。
  • 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。

image-20211028191617127

# 函数 | 方法

参考传送门 (opens new window)

Java的函数或者方法是代码块的集合,用来完成某个功能。

使用方法的能够使得程序变得清晰、利于程序维护、提高开发效率和代码复用性。

# 方法的定义

一般情况下,定义一个方法包含以下语法:

修饰符 返回值类型 方法名(参数类型 参数名){
    ...
    方法体
    ...
    return 返回值;
}
1
2
3
4
5
6

方法包含一个方法头和一个方法体。下面是一个方法的所有部分:

  • 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。

  • 返回值类型:方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void

  • 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。

  • 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。

    扩展:在JDK1.5开始,Java支持传递同类型的可变参数,只要在方法声明中,在指定参数类型后加一个省略号。但是要注意一点是一个方法中只能指定一个可变参数,同时它必须是方法的最后一个参数,任何普通的参数必须在它之前声明。

  • 方法体:方法体包含具体的语句,定义该方法的功能。

image-20211028200817232

/** 返回两个整型变量数据的较大值 */
public static int max(int num1, int num2) {
   int result;
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}
1
2
3
4
5
6
7
8
9
10

# 方法的调用

Java 支持两种调用方法的方式,根据方法是否返回值来选择。

当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。

当方法返回一个值的时候,方法调用通常被当做一个值。例如:

int larger = max(30, 40);
1

如果方法返回值是void,方法调用一定是一条语句。例如,方法println返回void。下面的调用是个语句:

System.out.println("欢迎访问Snake8859!");
1

下面的例子演示了如何定义一个方法,以及如何调用它:

public class TestMax {
   /** 主方法 */
   public static void main(String[] args) {
      int i = 5;
      int j = 2;
      int k = max(i, j);
      System.out.println( i + " 和 " + j + " 比较,最大值是:" + k);
   }
 
   /** 返回两个整数变量较大的值 */
   public static int max(int num1, int num2) {
      int result;
      if (num1 > num2)
         result = num1;
      else
         result = num2;
 
      return result; 
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

这个程序包含 main 方法和 max 方法。main 方法是被 JVM 调用的,除此之外,main 方法和其它方法没什么区别。

main 方法的头部是不变的,如例子所示,带修饰符 public 和 static,返回 void 类型值,方法名字是 main,此外带个一个 String[] 类型参数。String[] 表明参数是字符串数组。

# 方法的重载

Java允许在一个类中定义多个名称相同的方法,但是参数的类型或个数必须不同,这就是方法的重载。

假设要在程序中实现一个对数字求和的方法,由于参与求和数字的个数和类型都不确定,因此要针对不同的情况去设计不同的方法。接下来通过一个案例来实现对两个整数相加、对三个整数相加以及对两个小数相加的功能,具体实现如下所示。

public class MethodDemo02 {
	public static void main(String[] args) {
		// 下面是针对求和方法的调用
		int sum1 = add01(1, 2);
		int sum2 = add02(1, 2, 3);
		double sum3 = add03(1.2, 2.3);
		// 下面的代码是打印求和的结果
		System.out.println("sum1=" + sum1);
		System.out.println("sum2=" + sum2);
		System.out.println("sum3=" + sum3);
	}

	// 下面的方法实现了两个整数相加
	public static int add01(int x, int y) {
		return x + y;
	}
	// 下面的方法实现了三个整数相加
	public static int add02(int x, int y, int z) {
		return x + y + z;
	}
	// 下面的方法实现了两个小数相加
	public static double add03(double x, double y) {
		return x + y;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

从上述代码不难看出,程序需要针对每一种求和的情况都定义一个方法,如果每个方法的名称都不相同,在调用时就很难分清哪种情况该调用哪个方法。因此为解决这个问题,需要使用Java中的重载。

下面的三个方法互为重载关系:

  • public static int add(int x,int y) {逻辑} //两个整数加法
  • public static int add(int x,int y,int z) {逻辑} //三个整数加法
  • public static int add(double x,double y) {逻辑} //两个小数加法

接下来通过方法重载的方式进行修改,如下所示。

public class MethodDemo03 {
	public static void main(String[] args) {
		// 下面是针对求和方法的调用
		int sum1 = add(1, 2);
		int sum2 = add(1, 2, 3);
		double sum3 = add(1.2, 2.3);
		// 下面的代码是打印求和的结果
		System.out.println("sum1=" + sum1);
		System.out.println("sum2=" + sum2);
		System.out.println("sum3=" + sum3);
	}

	// 下面的方法实现了两个整数相加
	public static int add(int x, int y) {
		return x + y;
	}
	// 下面的方法实现了三个整数相加
	public static int add(int x, int y, int z) {
		return x + y + z;
	}
	// 下面的方法实现了两个小数相加
	public static double add(double x, double y) {
		return x + y;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

注意事项:

  • 重载方法参数必须不同

    • 参数个数不同,如method(int x)与method(int x,int y)不同
    • 参数类型不同,如method(int x)与method(double x)不同
    • 参数顺序不同,如method(int x,double y)与method(double x,int y)不同
  • 重载只与方法名与参数类型相关与返回值无关

    如void method(int x)与int method(int y)不是方法重载,不能同时存在

  • 重载与具体的变量标识符无关

    如method(int x)与method(int y)不是方法重载,不能同时存在

# 参数传递

参数传递,可以理解当我们要调用一个方法时,我们会把指定的数值,传递给方法的参数,这样方法中的参数就拥有了这个指定的值,可以使用该值,在方法体内运算。

  • 定义方法时,参数列表中的变量,称为形式参数
  • 调用方法时,传递给方法的数值,称为实际参数

关于参数传递有一个问题,请看下面两段代码。

public class ArgumentsDemo01 {
	public static void main(String[] args) {
		int a=5;
		int b=10;
		change(a, b);//调用方法时,传入的数值称为实际参数
		System.out.println("a=" + a);
		System.out.println("b=" + b);
	}

	public static void change(int a, int b){//方法中指定的多个参数称为形式参数
		a=200;
		b=500;
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ArgumentsDemo02 {
	public static void main(String[] args) {
		int[] arr = { 1, 2, 3 };
		change(arr);// 调用方法时,传入的数值称为实际参数
		
		for (int i = 0; i < arr.length; i++) {
			System.out.println(arr[i]);
		}
	}

	public static void change(int[] arr) {// 方法中指定的多个参数称为形式参数
		for (int i = 0; i < arr.length; i++) {
			arr[i] *= 2;
		}
	}
}	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

image-20211105205404772

通过上面的两段程序可以得出如下结论:

  • 当调用方法时,如果传入的数值为基本数据类型(包含String类型),形式参数的改变对实际参数不影响。
  • 当调用方法时,如果传入的数值为引用数据类型(String类型除外),形式参数的改变对实际参数由影响。

# 递归

递归就是A方法调用A方法,也就是自己调用自己。

递归能够大大地减少代码量,但是代码逻辑相对难以理解。递归的能力在于利用有限的语句来解决对象的无限处理。

递归的结构包括两个部分:

  • 递归头:什么时候不调用自身方法。如果没有该部分,递归会陷入死循环
  • 递归体:什么时候需要调用自身方法。

# 递归案例:阶乘

public class MethodDemo03 {
    public static void main(String[] args) {
        int result = f(5); // 5*4*3*2*1
        System.out.println(result);
    }

    public static int f(int n) {
        if (n == 1) {
            return 1;
        }else {
            return n * f(n - 1);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 包机制

参考传送门1 (opens new window)传送门2 (opens new window)

image-20211028195641867

Last Updated: 11/21/2022, 10:03:43 PM