Java基础 2. Java基础语法

Java基础 2. Java基础语法

2.1. 标识符

  • 标识符可以标识 : 类名, 方法名, 变量名, 接口名, 常量名, 包名, 注解…

  • 凡是程序员自己有权利命名的单词都是标识符

2.1.1. 标识符的命名规则 :

  • 规则1 : 只能由数字, 字母 ( 中文 ), 下划线, 美元符号 ($) 组成, 不能含其它符号 ( 这里的字母代表的是任何一个国家的文字, java是采用Unicode编码, 支持全球任何一个国家的语言 )
  • 规则2 : 不能以数字开头
  • 规则3 : 关键字不能做标识符
  • 规则4 : 标识符严格区分大小写
    - - 但是对于类名来说, 如果一个java源文件中同时出现了A类和a类, 那么谁在前就生成谁 ( 只生成一个 ), 建议不要让类名"相同"
  • 规则5 : 标识符理论上没有长度限制

2.1.2. 标识符的命名规范:

  • 命名规则 ( 法律 ): 是语法, 不遵守会报错

  • 命名规范 ( 道德 ): 不符合也行, 遵守规范代码的可读性很好

  • 具体的命名规范:

    • 规范1 : 见名知意 ( 看到标识符的名字后, 就知道这个什么意思 )

    • 规范2 : 遵循驼峰原则 ( 一高一低 )

      • 有利于单词与单词之间的分隔
      • 如 : BiaoShiFu 一看是3个单词
    • 规范3 : 类名和接口名首字母大写, 后面每个单词首字母大写

      • StudenTest, UserTest
    • 规范4 : 变量名和方法名首字母小写, 后面每个单词首字母大写

      • nianLing, mingZi
    • 规范5 : 所有常量名全部大写,并且单词与单词之间采用下划线衔接

      • USER_AGE
    • 规范6 : 包名全部小写

2.2. 关键字

  • Java 关键字是编程语言里事先定义的,有特殊意义的单词,Java 中所有的关键字都是小写的英语单词.

  • java语言中是严格区分大小写的, public和Public不一样, class和Class也不一样.

  • 关键字不需要花时间去记忆, 随着后面程序的积累, 会接触到所有的关键字.

  • Java所有的关键字, 一共50个

2.3. 字面量

  • 字面量指的是在程序中直接使用的数据, 字面量是Java中最基本的表达式, 不需要进行计算或转换, 直接使用即可

2.3.1. Java中有哪些字面量

  • 整数型
    • 10, -5, 0…
  • 浮点型
    • 3.14, -0.5…
  • 布尔型
    • true, flase
  • 字符型 (必须使用单引号括起来)
    • ‘a’, ‘1’, ‘田’…
  • 字符串型 (必须使用双引号括起来)
    • “Hello”, “你好”…
public class Const01{
	public static void main(String[] args){

		// 整数型的字面量
		System.out.println(10);

		// 浮点型字面量
		System.out.println(3.14);

		// 布尔型字面量
		System.out.println(true);

		// 字符型字面量(单个字符)
		// 必须使用单引号括起来
		System.out.println('a');

		// 字符串型字面量
		// 必须使用双引号括起来
		System.out.println("Hello");	
	}
}

2.3.2. 加号运算符

  • 作用1
    • 求和 (当加号两边都是数字)
  • 作用2
    • 字符串拼接 (当加号两边有任意一边是字符串类型时会进行字符串拼接, 结果还是一个字符串)
public class Plus{
	public static void main(String[] args){
		// 求和
		System.out.println(10 + 1);//11

		// 字符串拼接
		System.out.println("1" + 23);//123

		// 当出现多个+, 如果没有小括号, 遵循从左向右的顺序
		System.out.println(10 + 1 + "23");//1123

		// 添加了小括号优先级比较高
		System.out.println(10 + (20 + "30"));//102030
    }
}

2.4. 变量

  • 变量: 是内存当中的一块空间, 是计算机中存储数据最基本的单元

  • 变量三要素

    • 数据类型 (决定空间大小) (int, double, String)
    • 变量名
    • 变量值
  • 变量的定义, 赋值, 访问

    • int i; // 定义一个整数型的变量, 起名i
    • i = 100; // 给变量i 赋值100
    • System.out.println(i); // 访问i
  • 变量的作用

    • 便于代码的维护
    • 增强代码的可读性
  • 变量使用的小细节

    • 变量必须先定义, 再赋值, 才能访问
    • 一行代码上可以同时定义多个变量
    • 在同一个作用域当中, 变量名不能重名, 可以重新赋值
public class Variable01{
	public static void main(String[] args){

		// 变量定义
		int num;
		// 变量赋值
		num = 100;
		// 变量访问
		System.out.println(num);
		// 修改
		num = 200;
		System.out.println(num);

		// 定义一个变量, 用来存储数学当中的Π
		double Π = 3.14;
		System.out.println("圆周率: " + Π);

		// 定义一个String类型的name变量, 用来存储人的名字
		String name = "stephen";
		System.out.println("name: " + name);


		// 一行代码上可以同时定义多个变量
		int a, b, c = 100;	// 表示100赋值给了c, 但是a和b没有初始化
		a = 50;
		System.out.println(a);
		b = 80;
		System.out.println(b);
		System.out.println(c);

		int x = 1, y = 2, z = 3;	// 表示xyz三个变量定义并赋值
		System.out.println(x + y + z);
  • 变量的作用域
    • 作用域就是变量的有效范围, 变量的作用域就是在一个大括号内当中有效
    • 作用域的不同主要是因为定义在不同位置的变量具有不同的生命周期 (生命周期指的是从内存开辟到内存释放)
// 在同一个作用域当中, 变量名不能重名
		// 一个{}就是一个作用域
		// int j = 100;
		// int j = 200;

		{
			int age = 20;
		}
		//Variable01.java:43: 错误: 找不到符号
		System.out.println(age);
  • 变量的分类
    • 变量根据定义的位置来进行分类, 可以分为:
      • 局部变量 (凡是在方法体当中定义的变量)
      • 成员变量 (在类体中方法体外定义的变量叫做成员变量)
        • 静态变量
        • 实例变量
public class Variable01{
	public static void main(String[] args){
		// 凡是在方法体当中定义的变量, 一定是局部变量
		// 局部变量只在当前方法体当中有效
		int local;
	}

	// 在类体中方法体外定义的变量叫做成员变量
		// 实例变量
		int example;

		// 静态变量
		static int stc;
}

2.5. 二进制

  • 计算机底层只能识别二进制, 在计算机内部, 所有的数据都被转化为二进制形式处理和存储, 虽然计算机可以通过不同的编程语言和程序来处理不同的数据类型和格式, 但最终都需要将其转化为二进制形式才能被计算机底层识别和处理.
  • 二进制, 满二进一
  • 二, 八, 十六进制 转 十进制 :
    • 每一位与权值相乘, 最后求和
      在这里插入图片描述
  • 十进制 转 二, 八, 十六进制 :
    • 除2/8/16取余, 一直到商0为止, 最后将所有的余数逆序输出
      在这里插入图片描述
  • 二进制转换为十进制
    • 权值
      • 在二进制中, 权值指的是每个位所代表的数值大小, 即二进制中每个位的位置所代表的数值的大小
      • 比如: 在二进制数1101中, 最高位的权值是8, 次高位权值是4, 第三位权值为2, 最低为权值为1
/*
	1. 十进制数字转换成二进制
		243		11110011
		165		10100101
		89		01011001

	2. 二进制数字转换成十进制
		101010 	2+8+32=42
		111100 	4+8+16+32=60
		011001 	1+8+16=25
*/

public class Binary{
	public static void main(String[] args){
		// 这是一个二进制的字面量, 二进制的字面量需要以0b开头
		System.out.println(0b1001011);//75

		System.out.println(0b11110011);
		System.out.println(0b10100101);
		System.out.println(0b01011001);

		System.out.println(0b101010);
		System.out.println(0b111100);
		System.out.println(0b011001);
	}
}

2.6. 八进制与十六进制

  • 八进制: 满八进一
  • 十六进制: 满十六进一
  • 和十进制之间的转换与二进制同理
public class OX{
	public static void main(String[] args){
		// 以下字面量表示的是一个八进制的数, 以0开头的字面量是一个八进制数
		System.out.println(0102);//66
		// 以下字面量表示的是一个十六进制的数, 以0x开头的字面量是一个十六进制数
		System.out.println(0x102);//258

		// 十进制
		System.out.println(10);
		// 二进制
		System.out.println(0b10);
		// 八进制
		System.out.println(010);
		// 十六进制
		System.out.println(0x10);
	}
}

2.7. 原码反码补码

2.7.1. byte与bit

  • byte (字节) 是计算机存储和处理数据的基本单位, 由8个比特 (bit) 组成, bit是计算机中最小的存储单位, 只能存储0和1两个状态

  • 1byte = 8bit

    单位描述
    bit (比特位)1bit表示一个1或0, 计算机是将接收到的信息转化成能识别的二进制码
    byte (字节)1byte=8bit
    KB1KB=1024byte
    MB1MB=1024KB
    GB1GB=1024MB
    TB1TB=1024GB

2.7.2. 原码反码补码

  • 原码反码补码是计算机二进制的三种表示形式

  • 计算机在底层都是采用二进制补码形式表示

  • 二进制最高位成为符号位, 符号位当中, 0表示正数, 1表示负数

  • 正数的三码

    • 正数的三码都是相同的
    • 127的三码是01111111
  • 负数的三码

    • 负数的原码 (原码是人类最好理解的, 但是计算机最好理解的是补码)

      • 将绝对值转换为二进制后, 最高位改为1
      • -6的原码是10000110, 最高位是符号位, 1表负数
    • 负数的反码 (以原码作为参考, 符号位不变, 其他位取反)

      • -6的反码是11111001
    • 负数的补码 (以补码作为参考, 符号位不变, 然后加1)

      • -6的补码是11111010
    • 一个特殊的数

      • 当-128占一个byte的空间时, 它的原码和补码是相同的

      • 所以才会有1byte的取值范围是-128~127, 一共有256个数

        image-20240808174821855

  • 计算机为什么使用补码

    • 可以简化电路设计

      • 采用补码形式可以将加减法运算转化为相同的操作, 从而简化电路设计
    • 解决了0的正负问题

      • 在原码中, 0有两个表示, +0和-0, 这样会导致计算结果不唯一, 而在补码中, 0只有一种表示, 即全0, 可以避免这个问题
    • 解决了负数溢出的问题

      • 在原码中, 负数的表示范围比正数少1, 这样在进行减法运算时容易出现负数溢出的情况, 而在补码中, 负数的范围与正数相同, 可以避免负数溢出的问题
    • 方便计算机进行运算

      • 补码形式可以方便计算机进行加减法运算, 而且可以使用相同的电路进行运算, 从而提高了计算机的运算效率
    • 可以算一下 -3+2, 试着理解一下

      • image-20240809165608687

      • image-20240809170113880

      • image-20240809170605805

2.8. 数据类型

2.8.1. 数据类型概述

  • 数据类型可以分为基本数据类型和引用数据类型

  • 基本数据类型

    • 整数型

      • byte
      • short
      • int
        • 最常用的整数型
      • long
    • 浮点型

      • float
      • double
        • 最常用的浮点类型
    • 布尔型

      • boolean
    • 字符型

      • char
  • 引用数据类型

      • String
      • Object
    • 接口
    • 数组
    • 枚举
数据类型占据字节数取值范围具体取值范围默认值
byte1-27 ~ 27-1-128 ~ 1270
short2-215 ~ 215-1-32768 ~ 327670
int4-231 ~ 231-1-2147483648 ~ 21474836470
long8-263 ~ 263-1-9223372036854775808 ~ 92233720368547758070L
float4--0.0f
double8--0.0d
boolean1true/falsetrue/falsefalse
char20 ~ 216-10 ~ 65535‘\u0000’
  • 关于默认值

    • java中变量必须先定义, 再赋值, 才能使用. 对于局部变量来说, 必须手动赋值, 对于成员变量来说, 如果没有手动赋值, 系统会自动赋值默认值
    • 所有基本数据类型的默认值都是0, 所有的引用数据类型的默认值都是null[^null是一个关键字]
    public class Default{
    	static int stc;
    	static String str;
    
    	public static void main(String[] args){
            int i = 100;
            System.out.println(i);
    
            System.out.println(stc);//0
            System.out.println(str);//null
    	}
    }
    

2.8.2. 整数型详解

  • java中任何一个整数型字面量都会默认被当作int类型来处理

  • 小容量可以自动赋值给大容量, 叫做自动类型转换

    • byte < short < int < long < float[^为啥float比long大呢, 这和它的存储机制有关] < double
    • 如果在整数型字面量后面添加L或者l[^这里建议使用大写的L, 因为小写的l和数字1傻傻分不清], 那么这个整数型字面量就会被当作long类型来处理了
    public class IntTest01{
        public static void main(String[] args){
            // 这个没有类型转换
    		// 程序先执行右边, 分配一块空间去存储100这个数字, 给100分配的是4个字节(当作int来处理)
    		// i变量正好也是4个字节, 所以不存在类型转换
    		int i = 100;
    
    		// 100被当作了int, 是4个字节, j是8个字节
    		// 小容量可以自动赋值给大容量, 叫做自动类型转换
    		long j = 100;
    
    		// 100L一上来就是分配8个字节, 所以这个代码不存在类型转换
    		long k = 100L;
    
    		// 存在自动类型转换
    		long a = 2147483647;
    
    		// IntTest.java:19: 错误: 整数太大
    		// 不是long存不下, 而是这个整数型字面量被当作int来处理, 分配4个字节, 但是4个字节的空间已经超出了范围
    		// long b = 2147483648;
    
    		// 解决办法: 添加一个L	
    		long b = 2147483648L;
        }
    }
    
  • 大容量是无法直接转换成小容量的, 因为这种操作可能导致精度损失, 所以这种行为交给了程序员来决定, 后果也由他们自己来承担. 这时应该在大容量的字面量前面加上强制类型转换符, 程序才能编译通过

    • 强转时, 底层二进制的变化原则: 砍掉左侧多余的二进制
    • 强转时, 精度可能损失, 也可能不会损失, 这样看具体的数据是否真正的超出了强转后的类型的取值范围
    • 当一个整数型字面量没有超出byte或short的取值范围的时候, 可以直接赋值给byte或short类型的变量
    public class IntTest02{
        public static void main(String[] args){
            // 强制类型转换
    		byte c = (byte)150;
    		System.out.println(c);//-106
    
    		long d = 1000L;
    		int e = (int)d;
    		System.out.println(e);//1000
    
    
    		int m = 128;
    		byte n = (byte)m;
    		System.out.println(n);//-128
    
    		// 
    		byte x = 1;
    		byte y = 127;
    
    		// 错误: 不兼容的类型: 从int转换到byte可能会有损失
    		// 如果想编译通过, 只能进行强转
    		// byte z = 128;
    
    		short s1 = 32767;
    		
    		// 错误: 不兼容的类型: 从int转换到short可能会有损失
    		short s2 = 32768;
        }
    }
    
  • 混合运算

    • 当两个int类型的数据进行运算之后, 结果还是int类型

    • 多种数据类型在混合运算时, 先各自转换成容量最大的, 再做运算

    • byte和short混合运算时, 各自先转换成int再做运算

public class IntTest03{
  public static void main(String[] args){
      int num1 = 10;
	int num2 = 3;
	System.out.println(num1 / num2);//3

	// X, Y, Z都转换成long之后再做运算
	byte X = 10;
	int Y = 3;
	long Z = 100L;
	long result = X + Y + Z;
	System.out.println(result);//113
  }
}
```
  • 编译器的小心思

    public class IntTest04{
        public static void main(String[] args){
            // 编译优化, 以下代码10/3, 编译阶段会直接计算出结果是3
    		// 编译之后的.class文件中直接就是 byte num3 = 3;
    		byte num3 = 10 / 3;
    
    		// 而这里, num4/num5各自先转换成int类型再做运算
            // 编译阶段只知道num4/num5的结果是int类型, 但是编译阶段不知道具体的结果是多少
    		byte num4 = 10;
    		byte num5 = 3;
    		int num6 = num4 / num5;
        }
    }
    

2.8.3. 浮点型详解

  • double是常用的

  • 浮点型字面量默认都被当作double类型来处理, 如果想让其当作float类型来处理的话, 需要在字面量后面添加F/f

  • 浮点型数据两种表示形式

    • 十进制
      • double x = 1.23;
      • double y = 0.23;
    • 科学计数法
      • double x = 0.123E2; // 0.123 * 10的平方
      • double y = 123.34E-2;// 123.34 / 10的平方
public class DoubleTest{
	public static void main(String[] args){
		// 不存在类型转换
		double d = 3.14;

		// 错误: 不兼容的类型: 从double转换到float可能会有损失
		// float f = 3.14;

		// 解决方案1, 不存在类型转换
		float f = 3.14F;

		// 解决方案2, 类型转换
		float f2 = (float)3.14;

		double x = 1.123456789012345;//x = 1.123456789012345
		float y = 1.123456789012345F;//y = 1.1234568
		System.out.println("x = " + x);
		System.out.println("y = " + y);
        
        double x2 = 0.123E2; // 0.123 * 10的平方
		double y2 = 123.34E-2;// 123.34 / 10的平方
		System.out.println(x2);// 12.3
		System.out.println(y2);// 1.2334
	}
}
  • 浮点型数据存储原理

    • 符号位
      • 0表正数
      • 1表负数
    • 指数位
      • 比如说0.123E30, 其中2, 表示0.123 * 10的30次方. 最大指数位可以表示2127
    • 尾数位
      • 浮点数的小数部分的有效数字, 比如0.00123, 那么尾数位存储123对应的二进制
    • 二进制的指数位决定了数字呈现指数级增大, 因此float虽然是4个字节, 但是却可以表示比long更大的数值.

    image-20240810100004798

  • 注意事项

    • 一旦有浮点型数据参与运算得出的结果, 一定不要使用"==“与其他数字进行"相等比较”, 因为任何浮点型数据, 在计算机底层存储的都是它的近似值
    public class DoubleTest{
        public static void main(String[] args){
            double a1 = 9.9;
    		double a2 = 3.0;
    		double a3 = a1 / a2;
    		System.out.println(a3);//3.3000000000000003
    		System.out.println(a3 == 3.3);//false
    		if(a3 == 3.3){
    			System.out.println("相等");
    		}
    
    		if(a3 - 3.3 < 0.00000001){
    			System.out.println("相等");//相等
    		}
        }
    }
    

2.8.4. 字符型详解

  • char

    • char类型是字符型, 占用2byte

    • 取值范围

      • 0~65535
      • char和short都是2byte
      • char可以取到更大的正整数
      • char和short所能表示的数量是一样的, 都是65536个
    • char类型的字面量必须使用单引号括起来

      • ‘a’, ‘A’, ‘田’
    • 空字符和空格字符是不同的, 空字符表示什么都没有[^而且会报错], 空格字符表示一个空格

    • char类型统一采用的字符编码方式为Unicode编码

    • char的默认值

      • ‘\u0000’[\u后面的数字都是十六进制的Unicode编码][\u0000表示空字符, 跟空格不一样, 但是它是可以正常输出的]
public class CharTest{
    public static void main(String[] args){
		char a = 'a';
		System.out.println(a);
		char a2 = 'A';
		System.out.println(a2);
		char a3 = '田';
		System.out.println(a3);

		// 错误: 未结束的字符文字
		// char a4 = 'ab';
		//错误: 空字符文字
		// char a5 = '';

		char a6 = ' ';
		System.out.println("abc" + a6 + "abc");

		char a7 = '\u0000';// 空字符, 跟空格不一样
		System.out.println("abc" + a7 + "abc");
    }
}
  • 转义字符

    转义字符含义
    \tTab键
    \n换行符
    \""双引号
    \'单引号
    \\反斜杠
    \uxxxxxxxx是一个十六进制的数字, 这个数是字符对应的Unicode码
    public class CharTest02{
        public staic void main(String[] args){
            char c1 = '\t';
    		System.out.println("a" + c1 + "b");
    
    		System.out.print("Hello\n");
    		System.out.print("World\n");
    
    		// 输出一个双引号
    		System.out.println("\"");
    
    		// 输出一个单引号
    		System.out.println("\'");
    
    		// 输出一个\
    		System.out.println("\\");
        }
    }
    
  • 字符编码

    • 是人为规定的文字与二进制之间的转换关系
    • 在早期计算机系统上, 字符编码主要采用的是ASCII编码, 采用1个字节编码, 最多可以表示256个字符 (实际上ASCII码表只用了128个), 程序员需要记住:
      • a对应ASCII码97 (b是98, 以此类推)
      • A对应ASCII码65 (B是66, 以此类推)
      • 0对应ASCII码48 (1是49, 以此类推)
    • 编码, 解码, 乱码
      • 编码 (Decoding) : ‘a’ ----按照ASCII编码—> 01100001
      • 解码 (Encoding) : 01100001 ----按照ASCII解码—> ‘a’
      • 乱码: 出现乱码一定是因为使用的编码方式跟解码方式不一样导致的
    • 常见的字符编码
      • ASCII编码
        • 采用1个字节编码, 包括字母, 数字, 符号和控制字符等
      • Latin-1编码
        • 为了表示欧洲语言而设计的
      • ANSI编码
        • 支持英文, 拉丁文, 两个ANSI码可以表示一个汉字
      • Unicode编码
        • 可表示所有语言的字符, 采用了十六进制表示, 占用2个字节或4个字节, 最多可表示超过一万个字符 (使用这种方式有点浪费空间, 例如英文字符’a’其实采用一个字节存储就够了), 这个是java里面使用的
      • UTF-8编码
        • 是一种基于Unicode编码的可变长度字符编码, 使用1~4个字节来表示一个字符, 是目前Web开发中最常用的字符编码方式 (一个英文字母1个字节, 一个汉字3个字节)
      • UTF-16
        • 2或4个字节来表示一个字符
      • UTF-32
        • 每个字符占用4个字节
      • GB2312编码 (小)
        • 是中国国家标准的简体中文字符集, 使用2个字节表示一个汉字, 占据内存小
      • GBK编码 (中)
        • 占据内存中等, 建议使用这个
      • GB18030编码 (大)
        • 占据内存大
      • Big5编码
        • 中国台湾省的繁体中文字符集
  • char总结

    • 当整数型字面量没有超出char的取值范围, 可以直接将其赋值给char类型的变量

    • char类型变量定义有三种形式

      • char c = ‘A’;
      • char c = ‘\u0041’;
      • char c = 65;
    • 当定义char类型变量时, 如果值是一个整数型字面量, 那么这个字面量会被当作ASCII码值来处理

    public class CharTest03{
        public static void main(String[] args){
            System.out.println('A');
    		System.out.println('\u0041');
    
    		// 当整数型字面量没有超出char的取值范围, 可以直接将其赋值给char类型的变量
    		// 当定义char类型变量时, 如果值是一个整数型字面量, 那么这个字面量会被当作ASCII码值来处理
    		char c2 = 97;
    		System.out.println(c2);
    
    		// 错误: 不兼容的类型: 从int转换到char可能会有损失
    		// char c3 = 65536;
    		// System.out.println(c3);
        }
    }
    
  • char参与的运算

    • byte, short, char混合运算时, 各自先转换成int再做运算
    • 多种数据类型混合运算时, 各自先转换成最大的再做运算
    public class CharTest{
        public static void main(String[] args){
            System.out.println('a' + 1);//98
    
    		char c3 = 'a' + 1;//char c3 = 98
    		System.out.println(c3);//b
    
    		int c4 = 100;
    		short c5 = 200;
    		long c6 = 300L;
    		char c7 = 100;
    		long result = c4 + c5 + c6 + c7;
    		System.out.println(result);
        }
    }
    

2.8.5. 布尔类型详解

  • boolean值只有两个true和false, 没有1和0这一说
  • boolean类型的数据主要用在逻辑判断和条件判断
public class Boolean{
	public static void main(String[] args){
		// 错误: 不兼容的类型: int无法转换为boolean
		// boolean bool = 1;

		boolean flag = true;
		char gender;
		if(flag){
			gender = '男';
		}
		else{
			gender = '女';
		}
		System.out.println(gender);

		int num1 = 1;
		int num2 = 2;
		int max;
		if(num1 > num2){	// num1 > num2也是布尔类型, 因为它们的结果是true或者false
			max = num1;
		}
		else{
			max = num2;
		}
		System.out.println(max);
	}
}

2.8.6. 基本数据类型转换规则总结

  • 八种基本数据类型, 除了布尔类型之外, 其他类型都可以相互转换

  • 小容量转换为大容量, 叫做自动类型转换, 容量从小到大的排序为

    • byte < short (char) < int < long < float < double
    • 注意: char比short可以表示更大的正数
  • 大容量转换为小容量, 叫做强制类型转换, 需要加强制类型转换符才能编译通过, 运行时可能损失精度, 也可能不损失

  • 整数型字面量如果没有超出byte short char的取值范围, 可以直接赋值给byte short char类型的变量

  • byte short char混合运算, 各自先转换成int再做运算

  • 多种类型混合运算, 各自先转成容量最大的类型, 再做运算

2.8.7. Test

/*
	1. 请定义变量存储个人信息, 并打印输出, 输出样例如下:
		姓名		年龄		性别		联系电话
		张三		20		男		1234567890
		李四		30		女		1098765432

	2. 有两个变量a和b, a存储100, b存储200, 请交换两个变量中的数据, 让a存储200, b存储100. 并计算两个int类型数据的和, 要求最终输出200+100=300的效果

	3. 请分析以下程序中哪些是可以编译通过的, 哪些是报错的
		short s = 100;
		s = s - 99;
		byte b = 100;
		b = b + 1;
		char c = 'a';
		int i = 20;
		float f = 3F;
		double d = c + i + f;
		byte b1 = 11;
		short s1 = 22;
		short x = b1 + s1;
*/

public class DataTypeTest{
	public static void main(String[] args){
		// 第一题
		// 姓名
		String name1 = "张三";
		String name2 = "李四";
		// 年龄
		int age1 = 20;
		int age2 = 30;
		// 性别
		char gender1 = '男';
		char gender2 = '女';
		// 联系电话
		String mobile1 = "1234567890";
		String mobile2 = "1098765432";
		// 输出并格式化
		System.out.println("姓名\t年龄\t性别\t联系电话");
		System.out.println(name1 + "\t" + age1 + "\t" + gender1 + "\t" + mobile1);
		System.out.println(name2 + "\t" + age2 + "\t" + gender2 + "\t" + mobile2);


		// 第二题
		// 定义a, b
		// int a = 100;
		// int b = 200;
		// // 定义临时变量tmp存放a的值
		// int tmp = a;
		// a = b;
		// b = tmp;
		// // 求和
		// int sum = a + b;
		// // 输出并格式化
		// System.out.println(a + " + " + b + " = " + sum);


		// 第三题
		// 可以
		short s = 100;
		// 报错, s - 99为int, int转换为short应该强转
		// s = s - 99;

		// 修改
		s = (short)(s - 99);

		// 可以
		byte b = 100;
		// 报错, b + 1为int, int转换为byte应该强转
		// b = b + 1;

		// 修改
		b = (byte)(b + 1);

		// 可以
		char c = 'a';
		int i = 20;
		float f = 3F;
		// 可以
		double d = c + i + f;
		// 可以
		byte b1 = 11;
		short s1 = 22;
		// 报错, 因为b1 + s1为int类型, int转为short应该强转
		// short x = b1 + s1;

		// 修改
		short x = (short)(b1 + s1);
	}
}

2.9. 运算符

  • 注意: 运算符有优先级, 不确定的添加小括号, 添加小括号的优先级高, 会先执行

2.9.1. 算术运算符

算术运算符作用
+求和, 字符串拼接, 正数
-相减, 负数
*
/
%取余数, 取余公式 x - x / y * y
++自增1
自减1
/*
	++可以出现在变量的前或后
	++i;
		先++后i
	i++;
		先i后++

	++只有一边有操作数, 所以是单目运算符
*/

public class Calculator01{
	public static void main(String[] args){
		int num1 = 10;
		int num2 = 3;
		int sum = num1 + num2;
		int cha = num1 - num2;
		int ji = num1 * num2;
		int shang = num1 / num2;
		int yu = num1 % num2;
		System.out.println(num1 + " + " + num2 + " = " + sum);
		System.out.println(num1 + " - " + num2 + " = " + cha);
		System.out.println(num1 + " * " + num2 + " = " + ji);
		System.out.println(num1 + " / " + num2 + " = " + shang);
		System.out.println(num1 + " % " + num2 + " = " + yu);


		// 取余公式 x - x / y * y
		int x = 13425;
		int y = 10;
		System.out.println(x % y);

		int i = 10;
		i++;
		System.out.println("i = " + i);// i = 11
        
		System.out.println(x);	//13425
		System.out.println(++x);//13426

		System.out.println(y);	//10
		System.out.println(y++);//10
   		System.out.println(y);//11
	}
}

/*
	System.java下的.out可以对应到PrintStream

	PrintStream.java下的println()的源码:

	public void println(int x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    System.out.println(y++);

    这段代码的意思是
    int x = y++;//先把y赋值再++
    System.out.println(x);//所以x里面存放的是y的值, 而不是y++的值
*/
  • 键盘接收用户输入
    • java.util.Scanner s = new java.util.Scanner(System.in);
    • 接收整数
      • int num = s.nextInt();
    • 接收浮点数
      • double num2 = s.nextDouble();
    • 接收字符串
      • String name = s.next(); //接收的是第一个空格之前的内容
      • String name2 = s.nextLine(); //接收的是第一个换行符之前的内容
public class KeyInput{
	public static void main(String[] args){
		// 创建一个键盘扫描器
		// 以下代码中s是变量名, 名字是随意的, 只要是合法的标识符就行
		// 可以把现在的s叫做键盘扫描器
		java.util.Scanner s = new java.util.Scanner(System.in);

		// 程序执行到这里就会停下来, 等待键盘的输入, 键盘如果没有输入, 这里就会一直卡着, 直到用户输入了内容, 敲回车, 这行代码就之行结束了.
		// s.nextInt()会从键盘上扫描int类型的数字, 然后把扫描到的int数字赋值给num变量
		// 这样就完成了数据从控制台到内存
		// 针对nextInt()方法来说, 只能接收整数数字, 输入其他的字符串会报错
		System.out.print("请输入一个数: ");
		int num = s.nextInt();


		System.out.println("输入的数字是: " + num);

		System.out.print("请输入一个浮点数: ");
		// 想从键盘上接收一个double类型的数据
		double num2 = s.nextDouble();
		System.out.println("输入的浮点数是: " + num2);


		System.out.print("请输入你的姓名: ");
		// 从键盘上接收一个字符串, 但是接收的是第一个空格之前的内容
		String name = s.next();
		System.out.println("你的姓名是: " + name);
        
        
        System.out.print("请输入你的姓名: ");
		// 从键盘上接收一个字符串, 但是接收的是第一个换行符之前的内容
		
		s.nextLine();// 这个是负责吃掉上面输入留下的回车的, 因为s.nextInt(), s.nextDouble()这些输入完之后都会在内存里面留下一个\r(回车)

		String name2 = s.nextLine();
		System.out.println("你的姓名是: " + name2);
	}
}
/*
	作一个四则运算计算器
*/

public class Compute{
	public static void main(String[] args){
		// 输入
		java.util.Scanner s = new java.util.Scanner(System.in);
		System.out.println("这是一个四则运算计算器, 你可以分别输入四个内容, 每个内容输入完毕之后点回车表示输入完成, num1和num2是你想要进行操作的两个数, str1是加减乘除, str2是等于号, 输入等于号之后回车表示输入结束");

		// 输入第一个数
		System.out.print("请输入num1: ");
		double num1 = s.nextDouble();

		// 输入四则运算符
		System.out.print("请输入str1: ");
		// 这个东西可以接收一个char类型的字符, 但是现在不懂里面的具体内容是啥
		char str1 = s.next().charAt(0);

		// 输入第二个数
		System.out.print("请输入num2: ");
		double num2 = s.nextDouble();


		// 输入等于号
		System.out.print("请输入str2: ");
		char str2 = s.next().charAt(0);

		// 结果变量
		double result = 0;

		// 判断运算符
		if(str1 == '+'){
			// 对两个数进行四则运算
			result = num1 + num2;
		}
		else if(str1 == '-'){
			result = num1 - num2;
		}
		else if(str1 == '*'){
			result = num1 * num2;
		}
		else if(str1 == '/'){
			result = num1 / num2;
		}
		else{
			// 如果输入的不是四则运算符则报错
			System.out.println("输入错误, str1应该输入加减乘除号!");
		}

		// 判断等于号
		if(str2 == '='){
			// true则输出最后结果
			System.out.println(num1 + " + " + num2 + " = " + result);
		}
		else{
			// 否则报错
			System.out.println("输入错误, str2应该输入等于号!");
		}

	}
}
  • 栈数据结构 (stack)
    • 栈结构特点
      • 先进后出
      • 后进先出
    • 相关术语
      • 入栈, 压栈, Push
      • 出栈, 弹栈, Pop
      • 栈帧
      • 栈顶, 栈底

image-20240811074813002

  • 查看程序的字节码

    • 命令: javap -c ReadClass.class
    public class ReadClass01{
    	public static void main(String[] args){
    		int num1 = 10;
    	}
    }
    
    /*
    	Compiled from "ReadClass01.java"
    	public class ReadClass01 {
    	  public ReadClass01();
    	    Code:
    	       0: aload_0
    	       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
    	       4: return
    
    	  public static void main(java.lang.String[]);
    	    Code:
    	       0: bipush        10
    	       2: istore_1
    	       3: return
    	}
    */
    
    /*
    	1. 重点要解读的字节码是:
    		0: bipush        10	:	把10这个字面量压入操作数栈当中
            2: istore_1	:	把操作数栈顶元素弹出, 然后把其存储到局部变量表的1号槽位上
    
    	2. 在java中, 任何一个方法执行时, 都会为这个方法分配所属的内存空间, 供这个方法使用, 每个方法都有自己独立的内存空间, 这个内存空间中有两块比较重要的内存空间:
    		一块叫做局部变量表(存储局部变量的)
    		另一块叫做操作数栈(存储程序运行过程中参与运算的数据)
    */
    

image-20240811084112134

image-20240811084800415

image-20240811084926490

public class ReadClass02{
	public static void main(String[] args){
		int num1 = 10;
		int num2 = num1;
	}
}

/*
	Compiled from "ReadClass02.java"
	public class ReadClass02 {
	  public ReadClass02();
	    Code:
	       0: aload_0
	       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
	       4: return

	  public static void main(java.lang.String[]);
	    Code:
	       0: bipush        10
	       2: istore_1
	       3: iload_1
	       4: istore_2
	       5: return
	}
*/

/*
	3: iload_1	:	把局部变量表1号槽位上的数据复制一份, 压入操作数栈
	4: istore_2	:	把操作数栈顶元素弹出, 然后把其存储到局部变量表的2号槽位上
*/

image-20240811090321351

image-20240811090520837

image-20240811090741779

image-20240811090804657

public class ReadClass03{
	public static void main(String[] args){
		int num1 = 10;
		int num2 = num1;
		num2++;
	}
}

/*
	Compiled from "ReadClass03.java"
	public class ReadClass03 {
	  public ReadClass03();
	    Code:
	       0: aload_0
	       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
	       4: return

	  public static void main(java.lang.String[]);
	    Code:
	       0: bipush        10
	       2: istore_1
	       3: iload_1
	       4: istore_2
	       5: iinc          2, 1
	       8: return
	}
*/

/*
	5: iinc          2, 1 	:	把2号槽位上的值加一
*/

image-20240811091321730

public class ReadClass04{
	public static void main(String[] args){
		int num1 = 10;
		int num2 = num1++;
	}
}

/*
	Compiled from "ReadClass04.java"
	public class ReadClass04 {
	  public ReadClass04();
	    Code:
	       0: aload_0
	       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
	       4: return

	  public static void main(java.lang.String[]);
	    Code:
	       0: bipush        10
	       2: istore_1
	       3: iload_1
	       4: iinc          1, 1
	       7: istore_2
	       8: return
	}
*/

image-20240811092526535

image-20240811092659715

image-20240811092802787

image-20240811092854034

public class ReadClass05{
	public static void main(String[] args){
		int num1 = 10;
		int num2 = ++num1;
	}
}

/*
	Compiled from "ReadClass05.java"
	public class ReadClass05 {
	  public ReadClass05();
	    Code:
	       0: aload_0
	       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
	       4: return

	  public static void main(java.lang.String[]);
	    Code:
	       0: bipush        10
	       2: istore_1
	       3: iinc          1, 1
	       6: iload_1
	       7: istore_2
	       8: return
	}
*/

image-20240811093454422

image-20240811093539380

image-20240811093633337

image-20240811093650212

  • CalculateTest
/*
	1. 以下程序输出结果是什么(面试经典题)
		面试的时候会问到, 但是实际开发过程中用不到
		int i = 10;
		i = i++;
		System.out.println(i);

		int j = 10;
		j = ++j;
		System.out.println(j);

	2. 从键盘上接收一个三位数, 分别输出它的每一位数

	3. 681分钟是多少小时多少分钟
*/

public class CalculateTest{
	public static void main(String[] args){
		// 第一题
		int i = 10;
		i = i++;
		System.out.println(i);//10

		int j = 10;
		j = ++j;
		System.out.println(j);//11

		// 第二题
		// 输入
		java.util.Scanner s = new java.util.Scanner(System.in);
		System.out.print("请输入一个三位数的整数: ");
		int num = s.nextInt();
		// 取出个位数
		int geWei = num % 10;
		// 取出十位数
		int shiWei = num / 10 % 10;
		// 取出百位数
		int baiWei = num / 100;
		// 输出
		System.out.println("个位数: " + geWei + ",十位数: " + shiWei + ",百位数 : " + baiWei);

		// 第三题
		// 定义681分钟
		int time = 681;
		// 小时
		int hour = time / 60;
		// 分钟
		int minute = time % 60;
		// 输出
		System.out.println(hour + "时" + minute + "分");
	}
}

/*
	第一题解题分析
	int i = 10;
	i = i++;

   0: bipush        10
   2: istore_1
   3: iload_1
   4: iinc          1, 1
   7: istore_1

   底层实现原理, 实际上是找了一个临时的变量, 把10先存起来一份, 再做++, 然后把10再重新覆盖掉11
*/

2.9.2. 关系运算符

  • 又叫做比较运算符
    • >, <, >=, <=, ==, !=
    • 所有的关系运算符的结果都是布尔类型, 不是true, 就是false

2.9.3. 逻辑运算符

  • 逻辑运算符
    • & (逻辑与)
      • 两边为true, 都为true
    • | (逻辑或)
      • 两边只要有一边为true, 则为true
    • ! (逻辑非)
      • 取反
    • ^ (逻辑异或)
      • 不一样就是true
    • && (短路与)
      • 和逻辑与的运算结果相同, 只是存在一种短路现象 (左边操作数为false时, 右边操作数不执行)
    • || (短路或)
      • 和逻辑或的运算结果相同, 只是存在一种短路现象 (左边操作数为true时, 右边操作数不执行)
    • 注意, 开发中优先使用短路与短路或, 虽然短语与效率高于逻辑与, 但是逻辑与也有它的具体用处
  • 特点
    • 运算符两边要求必须是布尔类型, 并且最终运算结果也是布尔类型
public class Logic{
	public static void main(String[] args){
		int a = 100;
		int b = 99;
		int c = 98;
		System.out.println(a > b & a < c);//false
		System.out.println(a > b | a < c);//true
		System.out.println(!(a > b));//false
		System.out.println(a > b ^ a < c);//true

		System.out.println(a < b && a < ++c);//false
		System.out.println(a > b || a < ++c);//true
		System.out.println("c = " + c);//98
	}
}

2.9.4. 按位运算符

  • 按位运算符是对二进制位的补码进行按位

    • 左移<<

      • 将二进制左移n位, 相当于将数值乘以2n
      • 不会改变操作数的符号, 左移后, 溢出的会被截断, 右补0

      image-20240811121231142

    • 右移>>

      • 将二进制左移n位, 相当于将数值除以2n
      • 符号位不变, 正数右移左补0, 负数右移左补1, 溢出的会被截断
    • 无符号右移>>>

      • 将二进制左移n位, 相当于将数值除以2n, 并将最高位和左边的补0

      • 任意一个数字经过无符号位右移之后, 最终结果一定是非负数 (0或正整数), 溢出的会被截断

    • 按位与&

      • 操作数是整数时, 是按位与, 操作数是布尔类型是逻辑与
      • 上下的二进制位都为1时, 结果才是1, 否则为0
    • 按位或|

      • 上下的二进制位都为0时, 结果才是0, 否则为1
    • 按位取反~

      • 将整数的二进制位进行取反
    • 按位异或^

      • 上下的二进制位不同时, 结果才是1, 否则为0
      • 按位异或的具体应用, 实现间的加密和解密, 但是这种加密很低级
        • a ^ b ^ b --> a
  • 按位运算符的操作数要求必须是整数, 否则会出现编译错误

  • 学习这些按位运算符的目的是为了

    • 看懂java的源码
    • 使程序高效运行
public class AnWei{
	public static void main(String[] args){
		// 左移
		int num1 = 1;
		int num2 = -1;
		System.out.println(num1 << 3);//8 (1 * 2^3)
		System.out.println(num2 << 3);//-8 (-1 * 2^3)

		// 经典面试题, 如何让2快速变成8
		int num = 2;
		System.out.println(num << 2);//8

		// 右移
		int i = 1;
		int j = -1;
		System.out.println(i >> 1);//0
		System.out.println(j >> 1);//-1

		// 无符号右移
		int a = 1;
		int b = -1;
		// 原码 10000000 00000000 00000000 00000001
		// 反码 11111111 11111111 11111111 11111110
		// 补码 11111111 11111111 11111111 11111111
   // 无符号右移 00111111 11111111 11111111 11111111
		System.out.println(a >>> 1);//0
		System.out.println(b >>> 2);//1073741823
		System.out.println(0b00111111111111111111111111111111);//1073741823

		// 按位与
		int yu1 = 30;// 00000000 00000000 00000000 00011110
		int yu2 = 35;// 00000000 00000000 00000000 00100011
		   // yu1 & yu2 00000000 00000000 00000000 00000010
		System.out.println(yu1 & yu2);//2

		

		// 按位与的具体应用, 判断一个数是否为奇数
		if((yu1 & 1) == 1){	// 注意, 运算符优先级问题
			System.out.println("是奇数");
		}

		// 判断一个数是否为偶数数
		if(((yu1 | 0) & 1) == 0){
			System.out.println("是偶数");
		}

		// 按位或
		int huo1 = 30;
		int huo2 = 35;
		System.out.println(huo1 | huo2);// 63

		// 按位或的具体应用, 把30这个数字中第6位的二进制位设置为1
		// 00000000 00000000 00000000 00011110
		// 00000000 00000000 00000000 00000001
		// 000 00000000 00000000 0000000100000
		// 000 00000000 0000000000000 00111110

		huo1 = huo1 | (1 << 6 - 1);
		System.out.println(huo1);


		// 按位取反
		int quFan1 = 30;
		// 00000000 00000000 00000000 00011110
	//取反补码 11111111 11111111 11111111 11100001
		//反码 10000000 00000000 00000000 00011110
		//原码 10000000 00000000 00000000 00011111
		System.out.println(~quFan1);//-31

		// 按位取反的具体应用, 位清除, 把二进制位中指定的位清除为0
		// 把30的第4位清除为0

		// 00000000 00000000 00000000 00011110
		// 00000000 00000000 00000000 00000001
		// 00000 00000000 00000000 00000001000
 		// 11111 11111111 11111111 11111110111
 		// 00000 00000000 00000000 00000010110
 		System.out.println(quFan1 & ~(1 << 4 - 1));//22


		// 按位异或
		int yiHuo1 = 30;
		int yiHuo2 = 35;
		// 00000000 00000000 00000000 00011110
		// 00000000 00000000 00000000 00100011
		// 00000000 00000000 00000000 00111101
		System.out.println(yiHuo1 ^ yiHuo2);//61

		// 按位异或的具体应用, 实现间的加密和解密, 但是这种加密很低级
		// a ^ b ^ b --> a
		// a是原始数据, 这个数据将来要进行加密
		// b是密钥(私人的, 只有我知道这个钥匙)
		System.out.println(yiHuo1 ^ yiHuo2 ^ yiHuo2);//30

	}
}

2.9.5. 赋值运算符

  • 基本赋值运算符
    • =
  • 复合赋值 (中间没有空格)
    • +=
    • -=
    • *=
    • /=
    • %=
    • >>=
    • <<=
    • >>>=
    • &=
    • |=
    • ^=
    • ~=
public class FuZhi{
	public static void main(String[] args){
		// =
		int num1 = 1;
		System.out.println(num1);//1
		// +=
		num1 += 2;
		System.out.println(num1);//3
		// -=
		num1 -= 2;
		System.out.println(num1);//1
		// *=
		num1 *= 2;
		System.out.println(num1);//2
		// /=
		num1 /= 2;
		System.out.println(num1);//1
		// %=
		num1 %= 2;
		System.out.println(num1);//1

		byte m = 10;
		// 错误: 不兼容的类型: 从int转换到byte可能会有损失
		// m = m + 20;

		// 编译通过, 底层实际上对应的是: m = (byte)(m + 20);
		// 所以m = m + 20和m += 20并不是完全一样的, 复合赋值自带强转, 它们永远不会改变运算结果类型, 哪怕精度损失
		m += 20;
	}
}

2.9.6. 条件运算符

  • 条件运算符又叫做三目运算符
    • 布尔表达式 ? 表达式1 : 表达式2
    • 当布尔表达式true时, 执行表达式1, 否则执行表达式2
    • 这种运算符常用于简化if-else语句的代码量

2.9.7. Test

/*
	1. 输入一个半径值, 计算圆的面积和周长, 并输出结果. 注意: 圆的面积公式为Π * r * r, 周长公式为2 * Π * r, 其中Π取3.14

	2. 变量a, b, c分别为6, 9, 10, 编写代码输出它们的最大值

	3. 变量n为整数, 判断它是不是一个偶数

	4. 输入三个整数, 分别判断第一个数是否大于0, 第二个数是否小于10, 第三个数是否是偶数, 如果满足条件, 则输出"三个条件都满足", 否则输出"不满足所有条件"

	5. 输入一个年份, 判断它是否是闰年, 若该年份能被4整除且不能被100整除, 或者能被400整除, 则该年份为闰年. 输出结果位"该年是闰年"或"该年不是闰年"
*/

public class Test{
	public static void main(String[] args){
		// 第一题
		// 输入
		java.util.Scanner scanner = new java.util.Scanner(System.in);
		System.out.print("请输入一个半径值: ");
		// 半径
		int r = scanner.nextInt();
		// 面积
		double s =  3.14 * r * r;
		// 周长
		double c1 = 2 * 3.14 * r;
		// 输出
		System.out.println("面积: " + s);
		System.out.println("周长: " + c1);

		// 第二题
		// 定义变量
		int a = 6;
		int b = 9;
		int c = 10;
		// 判断a和b, 如果a大则a和c比较, 否则b和c比较, 最后赋值给max
		int max = a > b ? (a > c ? a : c) : (b > c ? b : c);
		// 输出
		System.out.println("max = " + max);


		// 第三题
		// 输入
		// java.util.Scanner scanner = new java.util.Scanner(System.in);
		System.out.print("请输入一个整数, 用来判断它是否为偶数: ");
		int n = scanner.nextInt();
		// 判断
		// 使用除法进行判断
		// if(n % 2 == 0){
		// 	System.out.println("这个数是偶数");
		// }
		// else{
		// 	System.out.println("这个数不是偶数");
		// }

		// 使用按位运算符进行判断
		if(((n & 1) | 0) == 0){
			System.out.println("这个数是偶数");
		}
		else{
			System.out.println("这个数不是偶数");
		}


		// 第四题
		// 输入
		// java.util.Scanner scanner = new java.util.Scanner(System.in);
		System.out.print("请输入三个整数: ");
		int num1 = scanner.nextInt();
		int num2 = scanner.nextInt();
		int num3 = scanner.nextInt();
		// 判断
		// 如果三个条件都满足则返回true, 否则返回false
		boolean isTF = (num1 > 0 && num2 < 10 && num3 % 2 == 0) ? true : false;
		// 如果满足条件则输出
		if(isTF){
			System.out.println("三个条件都满足");
		}
		else{
			System.out.println("不满足所有条件");
		}


		// 第五题
		// 该年份能被4整除且不能被100整除, 或者能被400整除, 则该年份为闰年. 输出结果位"该年是闰年"或"该年不是闰年"
		// 输入
		// java.util.Scanner scanner = new java.util.Scanner(System.in);
		System.out.print("请输入一个年份, 用来判断是否是闰年: ");
		int year = scanner.nextInt();
		// 判断, 如果满足条件则返回true, 否则false
		boolean isRunNian = ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? true : false;
		// 如果满足则输出
		if(isRunNian){
			System.out.println("该年是闰年");
		}
		else{
			System.out.println("该年不是闰年");
		}
	}
}

2.10. 控制语句

2.10.1. If

  • 第一种写法

    • if(布尔表达式){
          语句;
      }
      // 原理: 如果布尔表达式true, 则执行语句, 如果为false, 则不执行
      
    • 对于if语句来说, 如果分支中只有一条语句, 大括号是可以省略的, 但是为了程序的可读性, 建议不要省略.

  • 第二种写法

    • if(布尔表达式){
          语句1;
      }
      else{
          语句2;
      }
      // 原理: 如果布尔表达式true, 则执行语句1, 如果为false, 则执行语句2
      
  • 第三种写法

    • if(布尔表达式1){
          语句1;
      }
      else if(布尔表达式2){
          语句2;
      }
      else if(布尔表达式3){
          语句3;
      }
      ...
      // 原理: 如果布尔表达式1true, 则执行语句1, 如果为false, 则判断布尔表达式2, 如果为true, 则执行语句2, 为false则判断布尔表达式3...
      
  • 第四种写法

    • if(布尔表达式1){
          语句1;
      }
      else if(布尔表达式2){
          语句2;
      }
      else if(布尔表达式3){
          语句3;
      }
      else{
          语句4;
      }
      // 原理: 如果布尔表达式1true, 则执行语句1, 如果为false, 则判断布尔表达式2, 如果为true, 则执行语句2, 为false则判断布尔表达式3, 如果true, 则执行语句3, false则执行语句4.
      
  • 注意事项

    • 对于任何一个if语句来说, 最多只能有一个分支执行
    • 分支中如果只有一条语句, 大括号可以省略
    • 对于第二种和第四种写法, 可以保证一定会有一个分支执行的, 因为都有else
    • 对于第一第三种写法, 可能会没有分支执行
    • if结构中还可以嵌套别的选择结构或循环结构
/*
	1. 输入一个人的年龄, 如果年龄大于等于18, 则输出"你已经成年了", 否则不输出任何东西

	2. 输入一个学生的分数score(百分制), 如果学生的分数大于等于60, 则输出"你已经及格了", 否则不输出任何东西

	3. 输入一个学生的分数score(百分制), 如果学生的分数大于等于60, 则输出"你已经及格了", 如果学生的分数小于60, 则输出"很抱歉, 你不及格"

	4. 输入一个数字num1, 判断它是否为7的倍数, 如果是, 则输出"num1是7的倍数", 否则输出"num1不是7的倍数"

	5. 输入一个数字num2, 判断它是否同时为3的倍数和5的倍数, 如果是, 则输出"num2既是3的倍数又是5的倍数", 否则输出"num2不同时是3的倍数和5的倍数"

	6. 编写程序模拟用户登录, 用户名stephen和密码123456正确则登录成功, 反之则登录失败

	7. 输入一个年份year和一个月份month, 判断这个月份有多少天, 判断方法如下:
		month为1,3,5,7,8,10,12中的一个, 输出"month有31天"

		如果month为4,6,9,11中的一个, 输出"month有30天"

		month为2且year为闰年, 输出"month有29天"(如果一个年份能够被4整除但不能被100整除, 或者能被400整除, 那么它是闰年)

		month为2且year不是闰年, 输出"month有28天"
*/

// 导入Scanner这个类
import java.util.Scanner;

public class If{
	public static void main(String[] args){
		// 第一题
		// 输入
		// 这里直接使用这个类
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入你的年龄: ");
		int age = scanner.nextInt();
		// 判断
		if(age >= 18){
			// 输出
			System.out.println("你已经成年了");
		}


		// 第二三题
		// 输入
		// Scanner scanner = new Scanner(System.in);
		System.out.print("请输入你的分数(百分制): ");
		double score = scanner.nextDouble();
		// 判断
		if(score >= 60){
			// 输出
			System.out.println("你已经及格了");
		}
		else {
			System.out.println("很抱歉, 你不及格");
		}


		// 第四题
		// 输入
		// Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个数: ");
		int num1 = scanner.nextInt();
		// 判断
		if(num1 % 7 == 0){
			// 输出
			System.out.println(num1 + "是7的倍数");
		}
		else{
			System.out.println(num1 + "不是7的倍数");
		}


		// 第五题
		// 输入
		// Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个数 ");
		int num2 = scanner.nextInt();
		// 判断
		if(num2 % 3 == 0 && num2 % 5 == 0){
			System.out.println(num2 + "既是3的倍数又是5的倍数");
		}
		else{
			System.out.println(num2 + "不同时是3的倍数和5的倍数");
		}


		// 第六题
		// 输入
		
		// Scanner scanner = new Scanner(System.in);
		System.out.print("请输入用户名: ");
		String userName = scanner.next();
		System.out.print("请输入密码: ");
		String password = scanner.next();

		// 判断
		// 注意: 字符串的比较不能使用"==", 必须手动调用equals方法来进行比较(后面还会讲到)
		// userName.equals("stephen")
		if(userName.equals("stephen") && password.equals("123456")){
			System.out.println("登录成功!");
		}
		else{
			System.out.println("登录失败, 请重新输入");
		}


		// 第七题
		// 输入
		// Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个年份: ");
		int year = scanner.nextInt();
		System.out.print("请输入一个月份: ");
		int month = scanner.nextInt();
		// 单一出口原则, 定义一个符号变量, 便于修改
		String str = "month有28天";
		// 判断
		if(month < 1 || month > 12){
			System.out.println("对不起, 输入的月份不合法!");
			// 直接结束当前方法
			return;
		}
		// 程序如果能够执行到这里, 说明月份一定是合法的

		
		else if(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12){
			str = "month有31天";
		}
		else if(month == 4 || month == 6 || month == 9 || month == 11){
			str = "month有30天";
		}
		else if(month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)){
			str = "month有29天";
		}
		System.out.println(str);
	}
}

2.10.2. Switch

  • 格式

    • switch(表达式){
          case value1:
              语句;
              break;// 遇到break直接跳出整个switch语句
          case value2:
              语句;
              break;
         	...
          default:
              语句;
              break;
              
      }
      
    • 表达式可以是int, 枚举, 字符串

      • 可以直接把byte, short, char类型的放进去, 会进行自动类型转换成int
    • case后面只能是字面量(int, 枚举, 字符串类型的), 不能使用变量

    • 表达式跟value进行匹配, 相等则进入这个case中, 并执行语句, 遇到break直接跳出整个switch语句

    • 如果所有的value都不匹配, 则进入default, default不是必须的

  • 注意事项

    • switch语句适用于判断固定值, if语句适用于判断范围或区间时使用, switch能做的if肯定能做, if能完成的switch不一定能完成
    • JDK7之前, switch只支持int, 枚举类型, 在JDK7之后, 增加了对字符串类型的支持
    • case语句中的值必须是字面量, 不能是变量
    • case语句中的值必须和switch后面的值是同一类型, 或者能够相互转换
    • case可以合并
    • 在每个case分支中要加上break语句, 避免case穿透现象
    • 在switch语句中, 一般都应该有一个default分支, 用于处理一些特殊情况 (不写也不会报错)
    • default可以放在switch块[^{}]当中的任意位置, 但是建议将default放在最后 (可读性好)
  • JDK12新特性

    • switch(x){
          case 1 -> System.out.println(1);// case后面的冒号省了, break也省了
          case 2 -> System.out.println(2);
          default -> System.out.println("default");
      }
      switch(x){
          case 1,2,3 -> System.out.println("123");// case合并
      }
      switch(x){
          case 1 -> {
              System.out.println(1);// case中多条语句加{}
              System.out.println(1);
          }
      }
      
      
      
      	// JDK12新特性完成第七题
      		// 输入
      		// Scanner scanner = new Scanner(System.in);
      		System.out.print("请输入一个年份: ");
      		int year = scanner.nextInt();
      		System.out.print("请输入一个月份: ");
      		int month = scanner.nextInt();
      		// 单一出口原则, 定义一个符号变量, 便于修改
      		String str;
      		// 判断
      
      		switch(month){
      			// JDK12新特性
      			case 1, 3, 5, 7, 8, 10, 12 -> str = "month有31天";
      			
      			case 4, 6, 9, 11 -> str = "month有30天";
      			
      			case 2 -> {
      				if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0){
      					str = "month有29天";
      				}
      				else{
      					str = "month有28天";
      				}
      			}
      			default -> str = "对不起, 输入的月份不合法!";
      		}
      		System.out.println(str);
      
/*
	7. 输入一个年份year和一个月份month, 判断这个月份有多少天, 判断方法如下:
		month为1,3,5,7,8,10,12中的一个, 输出"month有31天"

		如果month为4,6,9,11中的一个, 输出"month有30天"

		month为2且year为闰年, 输出"month有29天"(如果一个年份能够被4整除但不能被100整除, 或者能被400整除, 那么它是闰年)

		month为2且year不是闰年, 输出"month有28天"

	8. 输入月份, 输出该月份所属的季节
		3,4,5 春季
		6,7,8 夏季
		9,10,11 秋季
		12,1,2 冬季

	9. 根据输入的运算符符号, 输出两个数的运算结果, 例如输入符号为"+", 则输出两个数的和, 输入符号为"-", 则输出两个数的差

	10. 输入成绩, 输出对应的等级
		输入90~100, 输出"优秀"
		70~89, "良好"
		60~69, "及格"
		0~59, 不及格""
*/

import java.util.Scanner;

public class Switch{
	public static void main(String[] args){
		// 第七题
		// 输入
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个年份: ");
		int year = scanner.nextInt();
		System.out.print("请输入一个月份: ");
		int month = scanner.nextInt();
		// 单一出口原则, 定义一个符号变量, 便于修改
		String str;
		// 判断

		switch(month){
		// case是可以合并的
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			str = "month有31天";
			break;
		case 4:
		case 6:
		case 9:
		case 11:
			str = "month有30天";
			break;
		case 2:
			if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0){
				str = "month有29天";
			}
			else{
				str = "month有28天";
			}
			break;
		default:
			str = "对不起, 输入的月份不合法!";
			break;
		}
		System.out.println(str);


		// 第八题
		// 输入
		System.out.print("请输入一个月份: ");
		int yue = scanner.nextInt();
		// 单一出口
		String flag;
		// 判断
		switch(yue){
		case 3:
		case 4:
		case 5:
			flag = "春季";
			break;
		case 6:
		case 7:
		case 8:
			flag = "夏季";
			break;
		case 9:
		case 10:
		case 11:
			flag = "秋季";
			break;
		case 12:
		case 1:
		case 2:
			flag = "冬季";
			break;
		default:
			flag = "输入的月份不合法";
		}
		System.out.println(flag);


		// 第九题
		// 输入
		// Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个整数num1: ");
		int num1 = scanner.nextInt();
		System.out.print("请输入一个运算符, 用来计算num1和num2: ");
		String calculator = scanner.next();
		System.out.print("请输入一个整数num2: ");
		int num2 = scanner.nextInt();
		
		int result = 0;
		// 判断
		switch(calculator){
		case "+":
			result = num1 + num2;
			break;
		case "-":
			result = num1 - num2;
			break;
		case "*":
			result = num1 * num2;
			break;
		case "/":
			result = num1 / num2;
			break;
		default:
			System.out.println("输入的符号不合法");
			return;// 结束此方法
		}
		System.out.println(num1 + calculator + num2 + "=" + result);


		// 第十题
		// Scanner scanner = new Scanner(System.in);
		double score = scanner.nextDouble();
		int grade = (int)(score / 10);
		// 单一出口
		String string = " ";
		if(score < 0 || score > 100){
			System.out.println("输入的成绩不合法");
			return;
		}
		// 判断
		switch(grade){
		case 9:
			string = "优秀";
			break;
		case 8:
		case 7:
			string = "良好";
			break;
		case 6:
			string = "及格";
			break;
		default:
			string = "不及格";
			break;
		}
		System.out.println("你的成绩的等级是: " + string);
	}
}

2.10.3. For

  • 当某代码需要频繁多次执行时, 可以采用循环语句

  • For

    • for(初始化1; 条件2; 调整4){	// 初始化最先执行, 并且只执行1次. 条件必须是一个布尔类型的值, 为true则进入循环, 否则跳出. 调整一般是负责调整某个变量值的. (只有调整了某个变量的值, 条件才有机会变成false)
      	循环体3;
      }	// 它们后面的数字代表执行顺序
      
      // ()中的三个表达式不是必须的
      
      for(int i = 0; i < 10; i++){
      	System.out.println("hello");
      }
      
  • Test1

/*
	1 * 1 = 1
	1 * 2 = 2  2 * 2 = 4
	1 * 3 = 3  2 * 3 = 6  3 * 3 = 9
	1 * 4 = 4  2 * 4 = 8  3 * 4 = 12 4 * 4 = 16
	1 * 5 = 5  2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
	1 * 6 = 6  2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
	1 * 7 = 7  2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
	1 * 8 = 8  2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
	1 * 9 = 9  2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
	
	j * i = j*i    
*/

import java.util.Scanner;

public class For{
	public static void main(String[] args){
		for(int i = 0; i < 10; i++){
			System.out.println("i = " + i);
		}

		// 输出1~100中所有的偶数
		int j;
		int sum2 = 0;
		for(j = 2; j <= 100; j += 2){
			System.out.print(j + " ");
		}

		System.out.println();

		// 输出100, 97, 94, 91 ... 1
		int k;
		for(k = 100; k > 0; k -= 3){
			System.out.print(k + " ");
		}

		System.out.println();

		// 计算1~100所有奇数的和
		int num;
		int sum = 0;
		for(num = 1; num < 100; num += 2){
			sum += num;
		}
		System.out.println("1~100所有奇数的和: " + sum);

		// 计算n的阶乘
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个整数n, 计算它的阶乘: ");
		int n = scanner.nextInt();
		int jieCheng = 1;
		int index;
		for(index = 1; index <= n; index++){
			jieCheng *= index;
		}
		System.out.println("n的阶乘是: " + jieCheng);


		// 使用for循环嵌套, 打印九九乘法表
		int num1;
		for(num1 = 1; num1 < 10; num1++){
			int num2;
			for(num2 = 1; num2 <= num1; num2++){
				System.out.print(num2 + " * " + num1 + " = " + num2 * num1);
				if(num2 < num1){
					System.out.print(" ");
					if(num1 * num2 < 10){
						System.out.print(" ");
					}
				}
			}
			System.out.println();
		}


		// // 找出1~100的所有素数
		// // 输入
		// int suShu;
		// // 产生2到99之间的数
		// for(suShu = 2; suShu < 100; suShu++){//2
		// 	// 注意: 素数为1, 不是为0, 初始化的位置很关键
		// 	int isPrime = 1;
		// 	// 产生2到suShu-1之间的数
		// 	int chuShu;
		// 	for(chuShu = 2; chuShu < suShu; chuShu++){//2
		// 		// 试除, 能除尽说明不是素数
		// 		if(suShu % chuShu == 0){
		// 			// 不是素数则将isPrime的值修改
		// 			isPrime = 0;
		//			break;
		// 		}
				
		// 	}
		// 	// 试除完之后且isPrime的值没变则说明是素数
		// 	if(isPrime == 1){
		// 		// 输出素数
		// 		System.out.print(suShu + " ");
		// 	}

		// }


		// // 找出1~1000的所有素数, 输出时每8数换一行
		// // 输入
		// int suShu;
		// // 计数器
		// int count = 0;
		// // 产生2到99之间的数
		// for(suShu = 2; suShu < 1000; suShu++){//2
		// 	// 注意: 素数为1, 不是为0, 初始化的位置很关键
		// 	int isPrime = 1;
		// 	// 产生2到suShu-1之间的数
		// 	int chuShu;
		// 	for(chuShu = 2; chuShu < suShu; chuShu++){//2
		// 		// 试除, 能除尽说明不是素数
		// 		if(suShu % chuShu == 0){
		// 			// 不是素数则将isPrime的值修改
		// 			isPrime = 0;
		//			break;
		// 		}
				
		// 	}
		// 	// 试除完之后且isPrime的值没变则说明是素数
		// 	if(isPrime == 1){
		// 		// 输出素数
		// 		System.out.print(suShu + " ");
		// 		// 得到一个素数则计数器加一
		// 		count++;
		// 		// 如果计数器等于8
		// 		if(count == 8){
		// 			// 则换行
		// 			System.out.println();
		// 			// 并且将计数器重新赋值为0, 以边下一次计数
		// 			count = 0;
		// 		}
		// 	}

		// }


		// 输入一个正整数x, 计算1-2+3-4+5-6+...(x-1)+x的和
		// 输入
		// Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个整数: ");
		int x = scanner.nextInt();
		// 正负号变量
		int flag = 1;
		// 求和变量
		int sum1 = 0;
		// 产生1到x之间的数
		int y;
		for(y = 1; y <= x; y++){
			// System.out.print(y + " ");
			// 根据通项公式1-2+3-4+5-6+...(x-1)+x求和
			sum1 += y * flag;
			flag *= -1;
		}
		System.out.println("它们的和是: " + sum1);



		// 求100到999之间的水仙花数, 水仙花数的每个位上的数字的位数次幂之和等于它本身(比如: 1^3 + 5^3 + 3^3 = 153)
		// 输入产生100到999之间的数
		int flower;
		for(flower = 100; flower < 1000; flower++){//153
			// 保留flower的值
			int f = flower;
			// 求和变量
			int sum3 = 0;
			// 取出flower的每一位数
			for(; f > 0; f /= 10){
				// 次幂变量
				int ciMi = 1;	
				// 取出最后一位
				int digit = f % 10;
				// 求次幂
				int k2;
				for(k2 = 0; k2 < 3; k2++){
					ciMi *= digit;
				}
				// 求和
				sum3 += ciMi;
			}
			// 判断flower与和是否相等
			if(flower == sum3){
				System.out.println(flower);
			}
		}



		// // 找出1~100的所有素数, 并输出两两相邻的素数差值等于2的素数对, 比如(3, 5), (5, 7), (11, 13)等
		// 输入
		int suShu;
		// 临时变量tmp, 存放上一个suShu的值
		int tmp = -1;
		// 产生2到99之间的数
		for(suShu = 2; suShu < 100; suShu++){//2
			// 注意: 素数为1, 不是为0, 初始化的位置很关键, 打布尔标记
			boolean isPrime = true;
			// 产生2到suShu-1之间的数
			int chuShu;
			for(chuShu = 2; chuShu < suShu / 2; chuShu++){//2
				// 试除, 能除尽说明不是素数
				if(suShu % chuShu == 0){
					// 不是素数则将isPrime的值修改
					isPrime = false;
					// 不是素数则终止循环
					break;
				}
				
			}
			// 试除完之后且isPrime的值没变则说明是素数
			if(isPrime){
				// tmp+2与suShu比较
				if(tmp + 2 == suShu){
					// 相等则输出上一个素数和本次的素数
					System.out.print("(" + tmp + ", " + suShu + ")");
					// 疑问: 结尾的逗号如何处理
				}
				// 把上一个素数存放到tmp当中
				tmp = suShu;
			}

		}
	}
}
  • Test2
/*
	// 输出以下图形
		1. 
			********
			********
			********
			********
			********

		2. 
			    ********
			   ********
			  ********
			 ********
			********

		3. 
			    *
			   ***
			  *****
			 *******
			*********

		4. 
			*********
			 *******
			  *****
			   ***
			    *
			   ***
			  *****
			 *******
			*********


		5. 
			    1
			   121
			  12321
			 1234321
			123454321
*/

import java.util.Scanner;

public class ForXing{
	public static void main(String[] args){

		// 第一个图形
		// 外层循环控制行数
		int i1;
		for(i1 = 0; i1 < 5; i1++){
			// 内层循环控制列数
			int j1;
			for(j1 = 0; j1 < 8; j1++){
				// 每列输出一个
				System.out.print('*');
			}
			// 一行输出完成之后换行
			System.out.println();
		}


		// 第二个图形
		// 最外层循环控制行数, 且每次循环空格的数量减一
		int i2;
		int spaceNum2 = 4;
		for(i2 = 0; i2 < 5; i2++, spaceNum2--){// i2 = 0
			// 内层循环一控制空格数量
			int k2;	// spaceNum2 = 4 3 2 1 0		  
			for(k2 = spaceNum2; k2 > 0; k2--){// k2 = 43210 3210 210 10 0
				// 输出空格
				System.out.print(' ');//SpaceSpaceSpaceSpace SpaceSpaceSpace SpaceSpace Space
			}
			// 内层循环二控制星号数量
			int j2;
			for(j2 = 0; j2 < 8; j2++){// j2 = 0 1 2 3 4 5 6 7 8
				// 输出星号
				System.out.print('*');//********
			}
			// 每一行输出结束后换行
			System.out.println();
			 
		}



		// 第三个图形
		// 外层循环控制行数, 且每次循环空格数量减一, 星号数量加二
		int i3;
		int spaceNum3 = 4;
		int starNum3 = 1;
		for(i3 = 0; i3 < 5; i3++, spaceNum3--, starNum3 += 2){//i3 = 0 1 2 3 4 5
			// 内层循环一控制空格数量
			int j3;
			for(j3 = spaceNum3; j3 > 0; j3--){//j3 = 43210 3210 210 10 0
				// 输出空格
				System.out.print(' ');//SpaceSpaceSpaceSpace SpaceSpaceSpace SpaceSpace Space
			}
			// 内层循环二控制星号数量
			int k3;
			for(k3 = 0; k3 < starNum3; k3++){//k3 = 01 0123 012345 01234567 0123456789
				// 输出星号
				System.out.print('*');//* *** ***** ******* *********
			}
			// 每一行输出结束后换行
			System.out.println();
		}


		System.out.println();

		// 第四个图形
		// 外层循环控制行数, 且每次循环空格数量减一, 星号数量加二
		int i4;
		int spaceNum4 = 0;
		int starNum4 = 9;
		int j4;
		int k4;
		for(i4 = 0; i4 < 5; i4++, spaceNum4++, starNum4 -= 2){//i4 = 0 1 2 3 4 5
			// 内层循环一控制空格数量
			
			for(j4 = 0; j4 < spaceNum4; j4++){//j4 = 43210 3210 210 10 0
				// 输出空格
				System.out.print(' ');//SpaceSpaceSpaceSpace SpaceSpaceSpace SpaceSpace Space
			}
			// 内层循环二控制星号数量
			
			for(k4 = starNum4; k4 > 0; k4--){//k4 = 01 0123 012345 01234567 0123456789
				// 输出星号
				System.out.print('*');//* *** ***** ******* *********
			}
			// 每一行输出结束后换行
			System.out.println();
		}
		spaceNum4 = 3;
		starNum4 = 3;
		for(i4 = 0; i4 < 4; i4++, spaceNum4--, starNum4 += 2){
			for(j4 = spaceNum4; j4 > 0; j4--){
				System.out.print(' ');
			}
			for(k4 = 0; k4 < starNum4; k4++){
				System.out.print('*');
			}
			System.out.println();
		}



		// 第五个图形
		// 外层循环控制行数
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个整数, 这个数代表了杨辉三角的行数: ");
		int row5 = scanner.nextInt();
		int i5;
		int spaceNum5 = row5 - 1;
		int changeNum5 = 1;
		for(i5 = 1; i5 <= row5; i5++, spaceNum5--, changeNum5++){
			// 内层循环一控制空格数量
			int j5;
			for(j5 = spaceNum5; j5 > 0; j5--){
				System.out.print(' ');
			}
			// 内层循环二控制左边数字
			int k5;
			for(k5 = 1; k5 <= changeNum5; k5++){//k5 = 12 123 1234 12345 123456
				System.out.print(k5);
			}
			// 内层循环三控制右边数字
			for(k5 -= 2; k5 >= 1; k5--){//k5 = 0 10 210 3210 43210
				System.out.print(k5);
			}
			// 每行循环结束之后换行
			System.out.println();
		}
	}
}
  • Test3
/*
	1. 猴子第一天摘下若干个桃子, 当即吃了一半, 还不过瘾, 又吃了一个, 第二天早上又将剩下的桃子吃了一半, 又多吃了一个, 以后每天早上都是吃了前一天剩下的一半零一个, 到第10天早上再吃的时候, 发现只剩下一个桃子了, 问一共多少个桃子

	分析:
		第1天: peachNum = peachNum / 2 - 1
		第2天: peachNum = peachNum / 2 - 1
		第3天: peachNum = peachNum / 2 - 1
		第4天: peachNum = peachNum / 2 - 1
		第5天: peachNum = peachNum / 2 - 1
		第6天: peachNum = peachNum / 2 - 1
		第7天: peachNum = peachNum / 2 - 1
		第8天: peachNum = peachNum / 2 - 1
		第9天: peachNum = peachNum / 2 - 1
		第10天: 1
		逆推
		peachNum + 1 = peachNum / 2
		(peachNum + 1) * 2 = peachNum
		peachNum = (peachNum + 1) * 2

	2. 100个和尚吃了100个馒头, 100和尚有大和尚和小和尚, 一个大和尚能吃3馒头, 三个小和尚吃1个馒头, 问大和尚和小和尚有多少个?

	3. 已知一只公鸡5块钱, 母鸡3块钱, 小鸡1块钱3只, 问100元买100只鸡有哪些方案?
		提示:
			如果拿100元买公鸡, 最多买20个
			如果拿100元买母鸡, 最多买33个
			如果拿100元买小鸡, 最多买300个
*/

public class ForTest03{
	public static void main(String[] args){
		// 第一题
		// 天数
		int day;
		// 桃子数量
		int peachNum = 1;
		// 循环9次
		for(day = 1; day < 10; day++){
			// 规律, 通过逆推法求出
			peachNum = (peachNum + 1) * 2;	// 1 4 10 22 46 94 190 382 766 1534
		}
		// 输出
		System.out.println("桃子总数: " + peachNum);// count = count / 2 - 1


		// 第二题
		// 馒头总数
		int manTou = 100;
		// 大小和尚
		int old, yong;
		// 大和尚的数量小于33, 一个大和尚吃三个馒头, 33个大和尚吃99个馒头(最大限度)
		for (old = 1; old < manTou / 3; old++) {
			// 小和尚的数量小于300, 三个小和尚吃1个馒头, 300个大和尚吃100个馒头(最大限度)
			for (yong = 1; yong < manTou * 3; yong++) {
				// 大和尚和小和尚一共吃的馒头的数量为100, 大和尚和小和尚的个数相加为100
				if (old * 3 + yong / 3 == manTou && old + yong == manTou) {
					// 输出
					System.out.println("大和尚有" + old + "个, 小和尚有" + yong + "个.");
				}
			}
		}


		// 第三题
		// 钱
		int money = 100;
		// 公鸡母鸡小鸡
		int gong, mu, xiao;
		// 公鸡数量小于20个
		for(gong = 0; gong < money / 5; gong++){
			// 母鸡数量小于33个
			for(mu = 0; mu < money / 3; mu++){
				// 小鸡数量小于300个
				for(xiao = 0; xiao < money * 3; xiao++){
					// 公鸡母鸡小鸡的总钱数为100元, 公鸡母鸡和小鸡的总数为100个
					if(gong * 5 + mu * 3 + xiao / 3.0 == money && gong + mu + xiao == 100){
						// 输出
						System.out.println("100元可以买" + gong + "个公鸡, 加上" + mu + "个母鸡和" + xiao + "个小鸡.");
					}
				}
			}
		}
	}
}

2.10.4. While

  • While

    • while(布尔表达式){
          循环体;
      }
      // 原理: 只要布尔表达式为true则执行循环体, 为false则跳出循环
      // 执行次数为0~n次, 有可能死循环
      
  • Test

/*
	1. 猜数字游戏:
		程序生成一个1~100之间的一个随机数, 要求用户猜这个数, 如果猜中了则输出恭喜信息, 并记录猜的次数, 如果猜错了可以提是用户大了或者小了并再猜一次, 直到猜中或者猜的次数为10为止
*/

import java.util.Scanner;
import java.util.Random;

public class While{
	public static void main(String[] args){
		// 输入
		Scanner scanner = new Scanner(System.in);
		System.out.print("欢迎进入猜数字游戏, 请在1~100之间猜一个数字: ");
		int num;
		// 生成随机数
		Random random = new Random();// Random是一个类
		int randomNum = random.nextInt(101);// random.nextInt(101)可以生成1~100之间的随机数
		// 单一出口
		String str = "";
		// 计数器
		int count = 0;
		// 10次直接跳出循环
		while(count < 10){
			// 每次循环输入
			num = scanner.nextInt();
			// 计数器++
			count++;
			// 猜中之后终止循环
			if(num == randomNum){
				str = "恭喜你猜中了!";
				break;
			}
			else if(num > randomNum){
				str = "大了, 请再猜一次!";
			}
			else{
				str = "小了, 请再猜一次!";
			}
			System.out.println(str);
		}
		if(count == 10){
			System.out.println("次数已经用完, 你一共猜了" + count + "次");
		}
		else{
			System.out.println(str + "你一共猜了" + count + "次");
		}
	}
}

2.10.5. Do While

  • Do While

    • do{
          循环体
      }while(布尔表达式);
      // 原理: 先执行一次循环体, 再判断布尔表达式, 条件为true继续循环, 为false跳出循环
      // 执行次数为1~n次, 特点是至少执行一次
      // 注意: 后面的分号不能丢
      
  • Test

/*
	求平均数:
		要求用户输入一组数字, 用-1表示输入结束, 使用do-while循环计算这些数字的平均数并输出, 要使用一个计数器来记录输入的数字个数, 遇到-1则终止输入并计算平均数
*/

import java.util.Scanner;

public class DoWhile{
	public static void main(String[] args){
		// 输入
		Scanner scanner = new Scanner(System.in);
		System.out.println("请依次输入一组数, 用来计算它们的平均数(输入-1表示结束, -1不会算到结果当中)");
		double num = 0.0;
		// 求和变量
		double sum = 0;
		// 计数器
		int count = 0;
		// 循环
		do{
			// 将输入的数加到求和变量当中
			sum += num;
			// 循环输入
			System.out.print("请输入一个数: ");
			num = scanner.nextInt();
			// 每输入一个数计数器++
			count++;
		}while(num != -1);
		// 平均数变量
		double average = sum / --count;
		// 输出
		System.out.println("它们的平均数是: " + average);
	}
}

2.10.6. 跳转语句

  • break

    • break语句可以使用在switch语句当中, 终止switch的执行
    • 可以使用在循环当中, 用来终止循环
    • 默认情况下, break终止的是离它最近的循环
    • 在java中, break后面是可以加标记的
    public class BreakContinue{
    	public static void main(String[] args){
    		// int i;
    		// for(i = 0; i < 2; i++){
    		// 	int j;
    		// 	for(j = 0; j < 10; j++){
    		// 		if(j == 5){
    		// 			break;
    		// 		}
    		// 		System.out.println("j = " + j);// 0123401234
    		// 	}
    		// }
    
    
    
    		int i;
    		for1:for(i = 0; i < 2; i++){//可以在循环的前面打标记
    			int j;
    			for2:for(j = 0; j < 10; j++){
    				if(j == 5){
    					break for1;// 用来终止标记为for1的循环, 跟C语言当中的goto语句的作用相似, 都是终止嵌套循环的
    				}
    				System.out.println("j = " + j);// 01234
    			}
    		}
    	}
    }
    
  • continue

    • 翻译为: 继续
    • 也是使用在循环中
    • 作用: 终止当前本次循环下面的语句, 调整之后直接进入下一次循环, 重新判断
    • continue后面也是可以加标记的
    public class Continue{
    	public static void main(String[] args){
    		int i;
    		for(i = 0; i < 10; i++){
    			if(i == 5){
    				continue;// i==5时, 下面的内容不执行, 调整之后直接进入下次循环
    			}
    			System.out.println("i = " + i);// 012346789
    		}
    	}
    }
    
  • return

    • 终止方法
    public class Return{
    	public static void main(String[] args){
            // return
    		int i;
    		for(i = 0; i < 10; i++){
    			if(i == 5){
    				return;// 终止方法
    			}
    			System.out.println(i);//01234
    		}
    		System.out.println("hello");//方法被终止, 不输出
        }
    }
    
  • Test

/*
	1. 不断地从键盘上接收一个正整数或者负整数, 要求计算所有正整数的和, 如果接受到0, 则程序退出

	2. 打印1~100所有的奇数, 但是跳过所有以3结尾的数字

	3. 韩信点兵, 三人一组余两人, 五人一组余三人, 七人一组余四人, 请问最少需要多少士兵
*/

import java.util.Scanner;

public class ControlTest{
    public static void main(String[] args){
        // 第一题
		// 输入
		Scanner scanner = new Scanner(System.in);
		System.out.println("请依次输入正整数或者负整数, 最终会返回正整数的和, 输入0表示结束输入");
		// num1表示用户输入的数
		int num1 = 1;
		// 求个变量
		int sum = 0;
		// 不等于0的时候进入循环
		while(num1 != 0){
			// 循环输入
			System.out.print("请输入一个数: ");
			num1 = scanner.nextInt();
			// 判断正负
			if(num1 <= 0){
				continue;
			}
			sum += num1;
		}
		System.out.println("所有正整数的和是: " + sum);


		// 第二题
		// 产生1~100之间的奇数
		int num2;
		for(num2 = 1; num2 <= 100; num2 += 2){
			// 判断3结尾的数
			if(num2 % 10 == 3){
				continue;
			}
			// System.out.print(num2 + " ");
		}


		// 第三题
		// // 写成死循环
		// int num3 = 1;
		// while(true){
		// 	// 判断
		// 	if(num3 % 3 == 2 && num3 % 5 == 3 && num3 % 7 == 4){
		// 		System.out.print("至少" + num3 + "个士兵");
		// 		break;
		// 	}
		// 	num3++;
		// }


		// 第三题优化
		int num3 = 1;
		// 不满足条++
		while(!(num3 % 3 == 2 && num3 % 5 == 3 && num3 % 7 == 4)){
			num3++;
		}
		// 直到++到了满足条件为止, 把满足条件的数输出
		System.out.print("至少" + num3 + "个士兵");
    }
}

2.11. 方法

2.11.1. 方法的定义和调用

  • 方法本质

    • 一段可以被重复利用的代码片段

    • 并且每一个方法都是一个独立封装好的功能

    • 在C语言中叫做函数 (Function)

    • 方法只定义不去调用, 是不会执行的

  • 方法的定义

    • 方法定义的语法格式

      [修饰符列表] 返回值类型 方法名(参数列表){
          方法体;
      }
      // []这个里面不是必须的, 其他的都是必须的
      
    • 修饰符列表

      • 目前修饰符列表这一块, 统一编写成public static, 后面讲
    • 返回值类型

      • 可以是java语言中的任何一种数据类型, 包括基本数据类型和引用数据类型
      • 如果方法执行结束时没有返回任何数据给调用者, 返回类型写void[^不能空着不写]
      • main方法是JVM调用的, 所以它前面的void就是main方法不返回任何数据给JVM
      • 当返回值类型不是void时, 方法在结束的时候必须使用"return 值";语句来完成数据的返回
    • return语句两种写法

      • return 值;
        • 写在返回类型不是void的方法中
      • return;
        • 写在返回类型为void类型的方法中, 但是不是必须写的
      • 不管是哪一种, 只要return语句执行, 方法必然结束
    • 当调用一个返回值类型不是void的方法时, 方法结束的时候会返回值, 这个值可以采用变量接收[^对于调用者来说, 可以选择接收, 也可以不接收], 注意变量的类型, 变量的类型一定要和返回值类型一致, 或者能够自动类型转换, 或者强制类型转换

    • 方法名

      • 只要是合法的标识符即可, 首字母小写, 后面每个单词首字母大写 (一般方法都是一个行为, 所以一般情况下都是动词)
    • 参数列表

      • 参数个数: 0~n, 多个的话, 使用逗号隔开
      • 参数是局部变量, 参数中起决定性作用的是, 参数类型
    • 任何一个方法都有一个方法体, 方法体用大括号括起来

      • 在大括号中编写java语句
      • 并且方法体中的代码有执行顺序, 遵循自上而下
  • 方法的调用

    • 当这个方法修饰符列表有static关键字的时候, 调用的语法格式
      • 类名.方法名(传递的值的列表);
    • 传递的值和参数必须一一对应
      • 类型要一一对应
      • 个数要一一对应
    • 调用方法时, 类名.什么情况下可以省略
      • 调用者和被调用者在同一个类中时, 可以省略类名.
    • 在同一个块中, return语句后面的代码时无法执行的, 请不要写, 否则会报错
public class MethodSum{
	// 入口
	public static void main(String[] args){
		// 调用sum方法
		sum(1, 2);
		sum(10, 20);
		sum(100, 200);

		// 调用concat方法
		// 返回值必须使用和concat返回值的类型相同的类型的变量接收
		String content = MethodSum.concat("hello,","stephen");

		// 调用add方法
		// 自动类型转换
		long result = add(2, 3);
		System.out.println(result);// result就变成了long


		// 调用m1
		// 不在同一个类, 必须加上类名.
		A.m1();
	}

	// 定义sum方法, 实现求和功能
	public static void sum(int a, int b){
		int c = a + b;
		System.out.println(a + " + " + b + " = " + c);
	}

	// 定义concat方法, 实现字符串拼接
	public static String concat(String x, String y){
		// 返回类型是String
		return x + y;
	}

	// 定义add方法, 实现求和
	public static int add(int a, int b){
		// 返回类型为int
		return a + b;
	}


}


class A{
	public static void m1(){
		System.out.println("m1");
	}



	// 错误: 缺少返回语句
	// public static int m2(){
	// 	int i = 100;
	// 	if(i > 99){
	// 		return 1;
	// 	}
	// }


	// 可以
	// public static int m2(){
	// 	int i = 100;
	// 	if(i > 99){
	// 		return 1;
	// 	}
	// 	else{
	// 		return 0;
	// 	}
	// }

	// 所以在同一个块中, return后面不要写代码
	public static int m2(){
		int i = 100;
		if(i > 99){
			System.out.println("位置1");
			return 1;
			// 错误: 无法访问的语句
			// System.out.println("位置2");
		}
		else{
			System.out.println("位置3");
			return 0;
			// 错误: 无法访问的语句
			// System.out.println("位置4");
		}
	}

}
  • Test
/*
	1. 找出1~100所有的素数, 使用方法(Method)

	2. 系统可以接收用户名和密码, 判断用户名和密码, 如果用户名是admin, 密码是abc123, 则登录成功, 如果不对, 请继续让用户输入用户名和密码, 直到登录成功为止
*/

import java.util.Scanner;

public class MethodPrime{
	public static void main(String[] args){
		int num;
		for (num = 2; num < 100; num++) {
			if(isPrime(num) == true){
				System.out.print(num + " ");
			}
		}
	}


	public static boolean isPrime(int num){
		// 产生2到num-1之间的数
		for(int i = 2; i <= num / 2; i++){
			// 试除
			if(num % i == 0){
				return false;
			}
		}
		return true;
	}
}

class MethodLogin{
	// 主方法
	public static void main(String[] args){
		// 调用login方法
		login();
	}
	// login方法,  输入用户名和密码
	public static void login(){

		// 输入
		Scanner scanner = new Scanner(System.in);
		String userName = "";
		String password = "";
		while(true){
			System.out.print("请输入用户名: ");
			userName = scanner.nextLine();
			System.out.print("请输入密码: ");
			password = scanner.nextLine();

			// 调用check方法, 并判断check方法的返回值
			if(check(userName, password)){
				System.out.println("恭喜你, 登录成功");
				// 为true则直接终止方法
				return;
			}
			else{
				// 为false则继续循环输入
				System.out.println("用户名或密码不对, 请重新输入");
			}
		}
	}

	// check方法, 检查用户名和密码是否正确
	public static boolean check(String userName, String password){
		// return后面的语句本身就是布尔类型的结果, 所以可以直接把if语句缩写成这个样子
		return userName.equals("admin") && password.equals("abc123");
	}
}

2.11.2. 方法执行时的内存图

  • 方法如果定义, 不调用是不会分配内存空间的 (从java8开始, 方法的字节码指令存储在元空间metaspace当中, 元空间使用的是本地内存)
  • 方法调用的瞬间, 会在栈内存当中分配活动场所, 此时放生压栈动作
  • 方法一旦结束, 给该方法分配的内存空间就会释放, 此时发生弹栈动作

image-20240816092238664

public class MethodNeiCun{
	public static void main(String[] args){
		System.out.println("main begin");
		m1();
		System.out.println("main over");
	}

	public static void m1(){
		System.out.println("m1 begin");
		m2();
		System.out.println("m1 over");
	}

	public static void m2(){
		System.out.println("m2 begin");
		m3();
		System.out.println("m2 over");
	}

	public static void m3(){
		System.out.println("m3 begin");
		System.out.println("m3 over");
	}


	/*
		输出结果:
		main begin
		m1 begin
		m2 begin
		m3 begin
		m3 over
		m2 over
		m1 over
		main over
	*/
}

2.11.3. 方法的重载 (over load)

  • 方法重载的好处
    • 简化代码调用
    • 更易于维修
    • 代码美观
  • 构成方法重载的条件
    • 在同一个类中
    • 方法名相同
    • 参数列表不同
      • 类型不同算不同
      • 顺序[^参数的类型的顺序]不同算不同
      • 个数不同算不同
  • 编译阶段的一种机制 (静态多态)
    • 在编译阶段已经完成了方法的绑定
    • 在编译阶段已经确定了要调用哪个方法了
  • 使用方法重载的情况
    • 在同一个类中, 如果功能相似, 建议使用方法重载
public class OverLoad{
	public static void main(String[] args){
		// 当前代码先不使用方法重载, 看看有什么缺点
			// 缺点一: 代码不美观
			// 缺点二: 不方便调用, 程序员需要记忆更多的方法名
		// 调用方法
		sumInt(10, 20);
		sumLong(10L, 20L);
		sumDouble(10.0, 20.0);
	}

	public static void sumInt(int a, int b){
		System.out.println(a + b);
	}

	public static void sumLong(long a, long b){
		System.out.println(a + b);
	}

	public static void sumDouble(double a, double b){
		System.out.println(a + b);
	}
}


class OverLoad02{
	public static void main(String[] args){
		// 当前代码使用方法重载, 看看有什么优点
			// 在java中允许在一个类中定义多个方法, 这些方法的名字可以一致			
			// 优点一: 代码美观
			// 优点二: 方便调用了, 程序员需要记忆的方法名少了
		// 调用方法
		sum(10, 20);
		sum(10L, 20L);
		sum(10.0, 20.0);
	}

	public static void sum(int a, int b){
		System.out.println(a + b);
	}

	public static void sum(long a, long b){
		System.out.println(a + b);
	}

	public static void sum(double a, double b){
		System.out.println(a + b);
	}
}


class OverLoad03{
	public static void main(String[] args){
		// 调用方法
		m1();
		m1("abc");

		
		m2(10, 20);
		m2(10L, 20L);



		m3("abc", 1);
		m3(1, "abc");
	}

	// 参数个数不同
	public static void m1(){
		System.out.println("m1()");
	}
	public static void m1(String s){
		System.out.println("m1(String s)");
	}


	// 参数类型不同
	public static void m2(int a, int b){
		System.out.println("m2(int a, int b)");
	}
	public static void m2(long a, long b){
		System.out.println("m2(long a, long b)");
	}



	// 参数顺序不同
	public static void m3(String s, int a){
		System.out.println("m3(String s, int a)");
	}
	public static void m3(int a, String s){
		System.out.println("m3(int a, String s)");
	}

	/*
		m1()
		m1(String s)
		m2(int a, int b)
		m2(long a, long b)
		m3(String s, int a)
		m3(int a, String s)
	*/	


	// 错误: 已在类 OverLoad03中定义了方法 doSome(int,int)

	// 以下两个方法没有构成方法重载, 属于方法重复定义
	// 所以参数顺序不同的顺序指的是参数类型的顺序, 而不是参数名称的顺序, 也证实了一点, 参数列表中, 起决定性作用的是参数类型, 而不是参数名称
	public static void doSome(int a, int b){

	}
	public static void doSome(int x, int y){
		
	}
}
  • println[^System.out.println(“hello world”);]构成方法重载
    • System是一个类, out是一个变量, println()是一个方法
    • 在java源码中找System类

image-20240817105053721

image-20240817104726031

image-20240817112358100

image-20240817112532941

image-20240817112711431

    • 找到PrintStream类
    • 发现println可以构成方法重载

image-20240817112820767

image-20240817113301834

class OverLoad04{
	public static void main(String[] args){
		// println方法重载, 可以让程序员使用起来很轻松
		// 只需要记住println方法名, 参数可以随便填
		System.out.println("hello world");
		System.out.println(1);
		System.out.println(1L);
		System.out.println(1.1);
		System.out.println(1.1F);
		System.out.println('1');
		System.out.println(true);
	}
}

2.11.4. 方法的递归

  • 递归调用

    • 方法自己调用自己
  • 递归调用如果没有结束条件的话, 会出现栈内存溢出错误

    • 因为一直有压栈的动作, 但是没有弹栈的动作, 最后导致了栈内存溢出
      • java.lang.StackOverflowError
    • 所以所有的递归调用必须要有结束条件
  • 在实际开发中, 使用递归调用的时候, 即使有时候, 结束条件是存在的, 并且结束条件也是合法的, 但仍然会发生栈内存溢出错误, 这可能是因为递归太深, 栈内存不够导致的. 所以递归调用一般是不建议使用的, 只有在不使用递归调用时这个问题解决不了的情况下, 才能使用递归调用

    • 原则: 能使用循环尽量使用循环, 循环解决不了再使用递归
    • 因为递归调用太耗费栈内存
  • 在实际开发中, 如果因为递归调用发生了栈内存溢出错误, 该怎么办

    • 首先可以调整栈内存的大小, 扩大栈内存
    • 如果扩大之后, 运行一段时间还是出现了栈内存溢出错误, 可能是因为递归结束条件不对, 需要进行代码的修改
public class MethodRecursion{
	public static  void main(String[] args){
		recursion();
	}

	public static void recursion(){
		System.out.println("recursion begin");

		// 方法的递归调用
		recursion();

		// 递归调用如果没有结束条件的话, 会出现栈内存溢出错误
		// java.lang.StackOverflowError
		// 所以所有的递归调用必须要有结束条件

		System.out.println("recursion over");
	}
}
import java.util.Scanner;

public class MethodRecursion01{
	public static  void main(String[] args){
		// 1. 不使用递归调用, 计算1+2+3+...+n的和	
		// 输入n
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个整数n, 完成1+2+3+...+n的功能: ");
		int n = scanner.nextInt();
		// 调用sum方法, 返回值给result
		int result = sum(n);
		System.out.println("result = " + result);
	}

	// 定义sum方法, 计算1到n的和
	public static int sum(int n){
		int num;
		int sum = 0;
		for(num = 1; num <= n; num++){
			sum += num;
		}
		return sum;
	}
}

class MethodRecursion02{
	public static void main(String[] args){
		// 2. 使用递归调用, 计算1+2+3+...+n的和

		// 输入n
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个整数n, 完成1+2+3+...+n的功能: ");
		int n = scanner.nextInt();
		// 调用sum方法, 返回值给result
		int result = sum(n);
		System.out.println("result = " + result);
	}

	public static int sum(int n){
		if(n == 1){
			return 1;
		}
		return n + sum(n - 1);
	}
}

class MethodRecursion03{
	public static void main(String[] args){
		// 3. 使用递归, 计算n的阶乘(经常考的面试题)
		// n * (n-1) * ... * 3 * 2 * 1

		// 输入n
		Scanner scanner = new Scanner(System.in);
		System.out.print("请输入一个整数n, 完成1*2*3*...*n的功能: ");
		int n = scanner.nextInt();
		// 调用sum方法, 返回值给result
		int result = jieCheng(n);
		System.out.println("result = " + result);
	}

	public static int jieCheng(int n){
		if(n == 1){
			return 1;
		}
		return n * jieCheng(n - 1);
	}
}

image-20240817170517585

image-20240817171107934

image-20240817174629371

2.11.5. 斐波那契数列

  • 规律

    • 当前数字 = 前两个相邻的数字之和
    • 这种数字被称为斐波那契数字
    • 斐波那契数字组合起来的叫做斐波那契数列
    /*
    	// 4. 假如有一对兔子, 从出生后第三个月起每一个月都生一对兔子, 小兔子长到第三个月后每个月又生一对兔子, 假如兔子都不死, 请问第n个月后的兔子有多少对
    	
    	月份		兔子数量(多少对)													最终多少对
    	1 		1(新生1)																1
    	2  		1(新生2)																1
    	3 		1(成熟)+1(新生1)														2
    	4 		1(成熟)+1(新生1)+1(新生2)												3
    	5 		1(成熟)+1(新生1)+1(新生2)+1(成熟)+1(新生1)							5
    	6		1(成熟)+1(新生1)+1(新生2)+1(成熟)+1(新生1)+1(成熟)+1(新生1)+1(新生2)	8
    
    	当月的兔子总数 = 上个月兔子总数 + 本月所有的新生的兔子
    
    	本月所有的新生的兔子 = 上上个月兔子总数
    
    	当月的兔子总数 = 上个月兔子总数 + 上上个月兔子总数
    
    
    	1
    	1
    	2
    	3
    	5
    	8
    	13
    	21
    	34
    	55
    	89
    	144		12月份兔子的总对数
    */
    
    class MethodRecursion04{
    	public static void main(String[] args){
    		// 输入n
    		Scanner scanner = new Scanner(System.in);
    		System.out.print("请输入一个整数n, n表示第几月: ");
    		int n = scanner.nextInt();
    		int num = rabbit(n);
    		System.out.println(n + "月的时候兔子总数" + num);
    	}
    
    	// 计算第n个月的兔子
    	public static int rabbit(int n){
    		if(n == 1 || n == 2){
    			// 递归终止条件
    			return 1;
    		}
    		return rabbit(n - 1) + rabbit(n - 2);
    	}
    }
    

2.12. package和import语句

2.12.1. package (包)

  • 包机制的作用

    • 便于代码管理, 不同的类放在不同的包下, 好维护
  • 定义

    • package 包名;
    • 在java源代码第一行[^有效代码的第一行]编写, 且只能写一行
  • 包名命名规则

    • 全部小写
  • 包名命名规范

    • 公司域名倒序+项目名+模块名+功能名
    • 例如: com.powernode.oa.empgt.service
  • 带包编译

    • javac -d 编译后的存放路径 java源文件路径
    • 例如: javac -d .[^.表示当前路径] Package02.java
  • 完整类名是包含包名的

  • java org.apache.struts.Package02

  • 注意

    • 这是package的原理, 如果用到了IDEA之后, 这些操作是用不到的

// package语句只能出现在java有效语句的第一行
package com;

public class Package{
	public static void main(String[] args){
		System.out.println("java包机制");
	}
}

/*
	编译通过: javac Package.java
	运行报错: java Package
	错误: 找不到或无法加载主类 Package
	原因: java.lang.NoClassDefFoundError: Package (wrong name: com/Package)

	改正: 类名变成com.Package, 同时在当前路径下带包编译

		编译: javac -d . Package.java
		运行: java com.Package

*/
package org.apache.struts;

public class Package02{
	public static void main(String[] args){
		System.out.println("Package02的main方法执行了");
	}
}

/*
	编译: javac -d . Package02.java
		.表示当前路径
	运行: java org.apache.struts.Package02
*/

image-20240821212633346

2.12.2 import

  • 只能出现在package和class定义之间
  • 可以编写多个
  • 必须使用import的情况
    • A类中使用B类, 当A和B类不在同一个包下, 并且B类也不属于java.lang包, 必须使用import引入
  • import支持模糊导入
    • import java.util.*;
    • 写上包名, 模糊类名
  • import还支持静态导入
    • 不常用, 了解即可
    • import static java.lang.System.*;
      • 静态导入, 将System类中的所有静态变量和静态方法全部导入
  • 不使用import, 得加上包名
package com;

public class Package{
	public static void main(String[] args){
		System.out.println("java包机制");

		// 类名.方法名(传的值);
		com.Package03.doSome();

		com.powernode.oa.empgt.service.UserService.delUser();
	}
}
package com;

public class Package03{
	public static void doSome(){
		System.out.println("Package03 do some");
	}
}
package com.powernode.oa.empgt.service;

public class UserService{
	public static void main(String[] args){
		delUser();
	}

	public static void delUser(){
		System.out.println("正在执行删除用户操作, 请稍后...");
	}
}

image-20240822084953958

    • 加上import
package com;

import com.powernode.oa.empgt.service.UserService;

public class Package{
	public static void main(String[] args){
		System.out.println("java包机制");

		// 类名.方法名(传的值);
		Package03.doSome();

		// 不在同一个包下, 不能省略
		// 如果想要把包名省略, 就得使用import导入包
		// com.powernode.oa.empgt.service.UserService.delUser();
		UserService.delUser();
	}
}

image-20240822084953958


package com;

import com.powernode.oa.empgt.service.UserService;
import java.util.Scanner;

// 模糊导入
// import java.util.*;

// 静态导入, 将System类中的所有静态变量和静态方法全部导入
import static java.lang.System.*;


public class Package{
	public static void main(String[] args){
		System.out.println("java包机制");

		// 类名.方法名(传的值);
		Package03.doSome();

		// 不在同一个包下, 不能省略
		// 如果想要把包名省略, 就得使用import导入包
		// com.powernode.oa.empgt.service.UserService.delUser();
		UserService.delUser();



		// 按理来说, java.lang包跟现在的包不在同一个包,应该import导入包才是
		// java.lang.String name = "stephen";
		// 因为java.lang包是java源码中一个特殊的包, 它下面的类使用时不需要加上包名 
		String name = "stephen";

		// java.util包名必须使用import进行导入
		Scanner scanner = new Scanner(System.in);


		// 静态导入
		// System.out.println(1);
		out.println(1);
		System.out.println(Math.abs(-100));

	}
}

2.13. IntelliJ IDEA的安装和使用

2.13.1. IDEA介绍

  • 是一种集成开发环境IDE[^Integrated Development Environment]
    • 可以提高程序员的开发效率
  • Java的IDE有很多
    • IntelliJ IDEA
      • 功能强大, 但是有点重, 且收费
    • eclipse
    • netbeans

2.13.2. IDEA安装

image-20240822111320421

image-20240822111352153

image-20240822111438064

image-20240822111608172

image-20240822111754364

image-20240822112115984

  • 安装

image-20240822112244293

image-20240822112652440

image-20240822112916230

image-20240908171818830

image-20240908171916524

  • 破解

百度网盘, 提取码: tian

image-20240908171948829

image-20240908172025184

image-20240822120131464

image-20240822120215996

  • IDEA激活

image-20240822175559674

激活码网址

image-20240908172110618

image-20240908172223794

image-20240908172254116

image-20240822184357829

2.13.3. IDEA使用

项目组成部分
  • 项目[^一章一个项目]
    • 模块[^一模块一个小节]
      • 包[^一包一个源文件]
新建项目

image-20240908172333607

image-20240908172404475

image-20240908172428595

设置JDK

image-20240908172504449

image-20240824212851294

创建模块

image-20240825205353083

image-20240908172534268

image-20240908172602638

创建包

image-20240825210725500

image-20240825210807104

image-20240825210848932

创建类

image-20240825211053367

image-20240825211222305

image-20240825211805041

字体

image-20240825211314656

image-20240825211438597

快捷键
  • alt + insert

    • 新建/新增任何东西
  • esc

    • 关闭任何窗口
  • 上下箭头

    • 切换选项
  • 在新建时输入, enter确定

  • ctrl + shift + f12

    • 编辑源码窗口最大化, 再点一下会恢复
  • psvm或者main

    • 自动生成main方法

    image-20240825212133951

    image-20240825212152846

  • 输出内容.sout

    • 自动生成输出语句

    image-20240825212547695

    image-20240908172640794

  • ctrl + shift + f10

    • 运行
    • IDEA会自动保存, 自动编译

    image-20240908172707054

  • alt + 1

    • 项目[^project]窗口

    image-20240908172733898

  • alt + 4

    • shell控制台

    image-20240908172757401

  • 敲两次shift

    • 搜索java源码中的内容
    • alt + 左右键
      • 切换选项卡
    • Classes选项是类, 输入类名点击回车就可以进入该类的源文件

    image-20240908172832518

    image-20240908172857926

  • 快速定义变量

    • 赋给变量的值.var, 敲回车

    image-20240908172917512

    image-20240908172934811

    image-20240908172953759

    • 黄线警告, 红线错误

    image-20240825215757144

  • ctrl + y

    • 删除本行
  • ctrl + d

    • 复制并粘贴本行
  • fori, 敲回车

    image-20240908173017842

    image-20240908173033247

    image-20240908173056993

    image-20240908173114681

  • ctrl + f12

    • 在一个类当中找方法

    image-20240908173204486

    image-20240908173230372

  • 查看源码

    • 按ctrl别松手, 鼠标移动到对应的类的下方, 出现下划线, 点击过去, 可以查看类源码

    image-20240903151122492

  • 多行编辑

    • 按alt别松手, 鼠标拖动d多行, 完成多行编辑

    image-20240903151201638

  • 快速创建封装getter和setter方法

    • 先将实例变量私有化

    image-20240905151157362

    • 然后alt+insert

      • 选择Getter and Setter

      image-20240905151303757

      • 将要生成对应方法的实例变量勾选

      image-20240905151435138

      image-20240905151542796

注释
  • ctrl + /
    • // 单行注释
  • ctrl + shift + /
    • /*多行注释*/
自动换行
  • 单个文件

    image-20240826213058675

  • 整个IDEA

    image-20240826220136860

    image-20240826220646667

调试
  • 18
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值