第一阶段:建立编程思想

自动类型转换

进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型
数据类型按精度(容量)大小排序为:
char -> int -> long -> float -> double
byte -> short -> int -> long -> float -> double

int num = 'a'; // char -> int 
double d1 = 80; // int -> double
sout(num); // 97
sout(d1); // 80.0

注意和细节:
(1)有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量大的那种数据类型,然后再进行运算。

int n1 = 10; // ok
// float f1 = n1 + 1.1; // 错误 n1 + 1.1 => 结果类型是 double
double d1 = n1 + 1.1; // 对 => 结果类型是 double
float f1 = n1 + 1.1f; // 对 => 结果类型是 float

(2)当我们把精度(容量)大的数据类型赋值给精度(容量小)的数据类型,就会报错,反之就会进行自动类型转换。

// int n1 = 1.1; // 错误 double => int

(3) byte,short和char之间不会相互自动转换。

// 当把数赋给 byte 时,先判断该数是否在byte范围内,如果是就可以
byte b1 = 10; // 对,-128~127
int n1 = 1; // n1是int类型
// byte b2 = n1; // 错误,原因:如果是按变量赋值,判断是类型
// char c1 = b1; // 错误,原因byte不能自动转成char

(4)byte、short、char他们三者可以计算,在计算时首先转换为int类型

byte b1 = 1;
short s1 = 1;
// short s2 = b1 + s1; // 错,b1 + s1 => int
int s2 = b1 + s1; // 对 
byte b3 = 1;
// byte b2 = b1 + b3; // 错误,b1 + b3 => int
char c1 ='a';
int = i1 + b1; // 98

(5)boolean 不参与转换

boolean pass = true;
// int num = pass; // boolean 不参与类型的自动转换

(6)自动提升原则:表达式结构的类型自动提升为 操作数中最大的类型

byte b1 = 1;
short s1 = 100;
int i1 = 1;
float f1 = 1.1f;
double d1 = 1.1;
double num = b1 + s1 + i1 + f1 + d1;
// int num1 = b1 + s1 + i1 + f1; // 错误不能float -> int
// float num2 = b1 + s1 + i1 + f1 + d1; // 错误不能double -> float
double num2 = b1 + s1 + i1 + f1; // float -> double

强制类型转换

自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。
使用时要加上强制转换符号 (),但可能造成精度降低或溢出要注意。

int i = (int)1.9;
sout(i); // 1,造成精度损失

int j = 2000;
byte b = (byte)j;
sout(b); // -48,造成数据溢出

细节说明:
(1)当进行数据的大小从 大 -> 小,就需要使用到强制转换
(2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级

// int x = (int)10*3.5+6*1.5; // double -> int
int y = (int)(10*3.5+6*1.5); // (int)44.0 -> 44

(3)char类型可以保存int的常量值,但不能保存int的变量值,需要强转

char c1 = 100;
int m = 100;
// char c1 = m;
char c2 = (char)m;
System.out.println(c2); // d
System.out.println(c2 + 1); // 101
System.out.println(c1 + c2); // 200

练习题

1.
short s = 12; // ok
s = s - 9; // 错误int->short
2.
byte b = 10; // ok
b = b + 11; // 错误int->byte
b = (byte)(b+11); // ok使用强转
3.
char c = 'a'; // ok
int i = 16; // ok
float d = 3.14f; // ok
double result = c + i + d; // ok float -> double
4.
byte b 16; // ok
short s = 14; // ok
short t = s + b; // 错误,int->short

基本数据类型和String类型的转换

  • 基本类型转String类型
    语法:将基本类型的值 + ""
int n1 = 100;
float f1 = 1.1f;
double d1 = 4.5;
boolean b1 = true;
String s1 = n1 + "";
String s2 = f1 + "";
String s3 = d1 + "";
String s4 = s1 + "";
sout(s1 + s2 + s3+ s4); // 100 1.1 4.5 true
  • String类型转基本数据类型
    语法:通过基本类型的包装类调用parseXX方法
    解读:使用基本数据类型对应的包装类,的相应方法,得到基本数据类型
String s1 = "123";
int num1 = Integer.parseInt(s1); // 123
double num2 = Double.parseDouble(s1); // 123.0
float num3 = Float.parseFloat(s1); // 123.0
long num4 = Long.parseLong(s1); // 123
byte num5 = Byte.parseByte(s1); // 123
boolean b = Boolean.parseBoolean(s1); // true
short num6 = Short.parseShort(s1); // 123
// 怎么把字符串转成字符char
// 解读 s1.charAt(0) 得到s1字符串的第一个字符 '1'
System.out.println(s1.charAt(0)); // 1
System.out.println(s1.charAt(1)); // 2

注意事项:
(1)在将String类型转成基本数据类型时,要确保String类型能够转成有效的数据,比如我们可以把“123”,转成一个整数,但是不能把“hello”转成一个整数。
(2)如果格式不正确,就 会抛出异常,程序就会终止。

String str = "hello";
int n1 = Integer.parseInt(str);
System.out.println(n1);

运算符

算术运算符

(1) +,-,*,/%++,–

%的本质看一个公式:a % b = a - (int)a / b * b
-10 % 3 => -10 - (-10) / 3 * 3 = -1
10 % -3 = 10 - 10 / -3 * -3 = 1
-10 % -3 = -10 - (-10) / -3 * -3 = -1

sout(10 / 4); // 2
sout(10.0 / 4); // 2.5
double d = 10 / 4; // 2.0
sout(10 % 3); // 1
sout(-10 % 3); // -1
sout(10 % -3); // 1
sout(-10 % -3); // -1
// 有小数运算得到结果是近似值
-10.5%3 = -10.5-(-10)/3*3 = -1.5
double aa = 300d; //ok

(2) 自增++
前++ 和 后++ 都完全等价于 i=i+1;
前++ ++i先自增后赋值
后++ i++先赋值后自增

int i = 10;
i++; // i = i + 1;
++i; // 12
int j = 8;
// int k = ++j; // j=j+1;k=j
int k = j++; // k = j;j=j+1;
sout(j + "," + k); // 9,8

double x = 5/9; // 0.0
double y = 5.0/9; // 0.5555555555555556

关系运算符(比较运算符)

  1. 关系运算符结果都是boolean型,要么是true,要么是false
  2. 关系表达式通常用在if结构的条件中或循环结构的条件中

== , != , < , > , <= , >= , instanceof
instanceof 检查是否是类的对象 “hsp” instanceof String结果是true

逻辑运算符

用于连接多个条件(多个关系表达式),最终的结果也是一个boolean值

  1. 短路 与&&、或||、取反!
  2. 逻辑 与&、或||
  3. 逻辑 异或^,当a和b不同时,结果为true,否则为false

短路&& 和 逻辑&的区别:

  1. &&短路与:如果第一个条件为false,第二个条件不会判断,结果为false
  2. & 逻辑与:不管第一个条件是否为false,第二个条件都要判断,效率低
  3. 开发中,使用的基本是短路与&&,效率高
boolean x = true;
boolean y = false;
short z = 46;
if((z++ == 46) && (y = true)){
	z++; 
}
if((x = false) || (++z == 49)){
	z++;
}
System.out.println("z = " + z); // 50

赋值运算符

赋值运算符就是将某个运算后的值,赋给指定的变量

  1. 基本赋值运算符 int a = 10;
  2. 复合赋值运算符 +=,-=,*=,/=,%= 等

特点:

  1. 运算顺序从右往左 int num = a + b + c;
  2. 赋值运算符的左边只能是变量,右边可以是变量、表达式、常量值
    int num = 20; int num2 = 78 * 34 -10; int num3 = a;
  3. 复合赋值运算符等价于:a += 3; 等价于 a = a + 3;
  4. 复合赋值运算会进行类型转换:
    byte b = 3;
    b += 2; // 等价于 b = (byte)(b + 2);
    b++; // b = (byte)b + 1;
    

三元运算符

条件表达式 ? 表达式1 : 表达式2;

  1. 表达式1和表达式2要为可以赋给接收变量的类型(或可以自动转换/或者强制转换)
  2. 三元运算符可以转成if-else语句
int a = 3;
int b = 8;
int c = 9;
int max1 = (a > b) ? a : b;
int max2 = max1 > c ? max1 : c;
System.out.println(max2); // 9

标识符的命名规则和规范

Java对各种变量,方法和类等命名时使用的字符序列称为标识符
凡是自己可以起名字的地方都叫标识符int num = 90;

规则:
1. 由26英文字母大小写,0-9,$组成
2. 数字不可以开头 int 3ab = 1;
3. 不可以使用关键字和保留字,但能包含关键字和保留字。
4. java中严格区分大小写,长度无限制。int totaNum = 10;
5. 标识符不能包含空格。int a b = 90;

规范:
1. 包名:多单词组成时所有字母都小写:aaa.bbb.ccc,com.hsp.com
2. 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz,TankShotGame
3. 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz,tankShotGame
4. 常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ,TAX_RATE

关键字和保留字

关键字: 被Java语言赋予了特殊的含义,用做专门用途的字符串(单词),关键字中所有字母都为小写。

用于定义数据类型的关键字
class,interface,enum,byte,short,int,long,float,double,char,boolean,void
用于定义数据类型值的关键字
true,false,null
用于定义流程控制的关键字
if,else,switch,case,default,while,do,fpr,break,continue,return
用于定义访问权限修饰符的关键字
private,protected,public
用于定义类,函数,变量修饰符的关键字
abstract,final,static,synchronize
用于定义类与类之间关系的关键字
extends,implements
用于定义建立实例及引用实例,判断实例的关键字
new,this,super,instanceof
用于异常处理的关键字
try,catch,finally,throw,throws
用于包的关键字
package,import
其他修饰符关键字
native,strictfp,transient,volatile,asset

保留字: 现有java版本尚未使用,但以后版本可能会作为关键字使用,自己命名标识符时要避免使用这些保留字。
byValue,cast,future,generic,inner,operator,outer,rest,var,goto,const

键盘输入语句

接收用户输入的数据,使用键盘输入语句来获取。
需要一个扫描器(对象)Scanner
步骤:
1. 导入该类的所在包 java.util.*
2. 创建该类对象(声明变量)
3. 调用里面的功能

import java.util.Scanner; // 把java.util下的Scanner类导入
public class Comment01{
    public static void main(String[] args) {
        // 创建Scanner类的对象
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入名字:");
        // 调用里面的功能
        String name = sc.next();
        System.out.println("请输入年龄:");
        int age = sc.nextInt();
        System.out.println("请输入薪水:");
        double sal = sc.nextDouble();
    }
}

四种进制

  1. 二进制:0,1,满2进1,以0b或0B开头
  2. 十进制:0-9,满10进1
  3. 八进制:0-7,满8进1以数字0开头表示
  4. 十六进制:0-9及A(10)-F(15),满16进1以0x或0X开头表示,A-F不区分大小写
int n1 = 0b1010; // 10
int n2 = 1010; // 1010
int n3 = 01010; // 520≈ΩΩΩΩΩΩ
int n4 = 0x1010; // 4112

进制的转换:

  1. 二进制转十进制,八进制转十进制,十六进制转十进制
    (1) 从最低位(右边)开始,将每个位上的数据取出来,乘以2的(位数-1)次方,然后求和
    0b1011转成十进制 1 + 2 + 8 = 11
    (2) 从最低位(右边)开始,将每个位上的数据取出来,乘以8的(位数-1)次方,然后求和
    0234转换十进制 4 + 24 + 128 = 156
    (3) 从最低位(右边)开始,将每个位上的数据取出来,乘以16的(位数-1)次方,然后求和,
    0x23A转成十进制 10 + 48 + 512 = 570

  2. 十进制转二进制,十进制转八进制,十进制转十六进制
    (1)将该数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制
    34转成二进制 100010
    (2)将该数不断除以8,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制
    131转成八进制 0203
    (3)将该数不断除以16,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制
    237转成十六进制 0xED

  3. 二进制转八进制,二进制转十六进制
    (1)从低位开始将二进制数 每三位一组,转成对应的八进制数即可。
    ob11010101转成八进制 0325
    (2)从低位开始将二进制数 每四位一组,转成对应的十进制数即可。
    ob11010101转成十六进制 0xD5

  4. 八进制转二进制,十六进制转二进制
    (1)将八进制数每一位,转成对应的一个3位的二进制数即可。
    0237转成二进制 0b010011111
    (2)将十六进制数每一位,转成对应的一个4位的二进制数即可。
    0x23B转成二进制 0b001000111011

原码、反码、补码

  1. 二进制的最高位是符号位:0表示正数,1表示负数
  2. 正数的原码,反码,补码都一样(三码合一)
  3. 负数的反码 = 它的原码符号位不变,其他位取反
  4. 负数的补码 = 它的反码+1,负数的反码 = 负数的补码-1
  5. 0的反码,补码都是0
  6. java没有无符号数,换言之,Java中的数都是有符号的
  7. 在计算机运算的时候,都是以补码的方式来运算的
  8. 看运算结果的时候,看他的原码

位运算符

Java中有7个位运算(按位与&、按位或|、按位异或^、按位取反~、>>、<<、>>>)

  1. &两位全为1,结果为1否则为0
  2. |两位有一个为1,结果为1否则为0
  3. ^两位一个为0一个为1,结果为1,否则为0
  4. ~取反 0-1 1-0
2&3 = 2
先得到2的原码 00000000 00000000 00000000 00000010
2的补码      00000000 00000000 00000000 00000010
3的原码      00000000 00000000 00000000 00000011
3的补码      00000000 00000000 00000000 00000011

~-2 = 1
先得到 -2的原码 10000000 00000000 00000000 00000010
-2 的反码      11111111 11111111 11111111 11111101
-2 的补码      11111111 11111111 11111111 11111110
~-2 =         00000000 00000000 00000000 00000001

~2 = -3
先得到2的原码补码   00000000 00000000 00000000 00000010
~2取反的补码       11111111 11111111 11111111 11111101 运算后的补码
运算后的反码       11111111 11111111 11111111 11111100
运算后的原码       10000000 00000000 00000000 00000011
  1. 算术右移>>:低位溢出,符号位不变,并用符号位补溢出的高位
  2. 算术左移<<:符号位不变,低位补0
  3. >>>逻辑右移也叫无符号右移,运算规则:低位溢出,高位补0
  4. 没有<<<符号
int a = 1 >> 2; //  00000001 = 00000000 本质1/2/2 = 0
int b = -1 >> 2; // -1/2/2 = -1
int c = 1 << 2; // 00000001 = 00000000 00000100 本质 1*2*2 = 4
int d = -1 << 2; // -1*2*2 = -4
int e = 3 >>> 2; // 00000011 = 00000000

控制结构

顺序控制程序从上到下逐行地执行,中间没有任何判断和跳转

分支控制(if,else,switch)

  • 单分支基本语法: 当条件表达式为true时,就会执行{}的代码。如果为false,就不执行。
if(条件表达式) {
    执行代码块;(可以有多条语句)
}
  • 双分支基本语法: 当条件表达式成立,执行代码块1,否则执行代码块2
if(条件表达式) {
    执行代码块1;
} else {
    执行代码块2;
}
  • 多分支基本语法: 当条件表达式1成立时,执行代码块1,如果表达式1不成立,才去判断表达式2是否成立,如果表达式2成立,就执行代码块2,以此类推,如果所有的表达式都不成立,则执行else的代码块,注意 只能就有一个执行入口
    (1)多分支可以没有else,如果所有的条件表达式都不成立,则一个执行入口都没有
    (2)如果有else,如果所有的条件表达式都不成立,则默认执行else代码块。
if(条件表达式1) {
    执行代码块1;
} else if(条件表达式2) {
    执行代码块2;
}
...
else {
    执行代码块n;
}
  • 嵌套分支基本语法: 在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构成为内层分支外面的分支结构称为外层分支。规范:不要超过3层可读性不好
if() {
    if() {
        // if-else...
    }else{
        // if-else
    }
}
  • switch分支结构基本语法: switch关键字表示switch分支,表达式对应一个值,当表达式的值等于常量1,就执行语句块1,break表示退出switch,如果没有匹配就继续匹配case常量2,如果一个都没有匹配上执行default
    细节:
    (1)表达式数据类型,应该和case后的常量类型一致,或者是可以自动转换成可以相互比较的类型,比如输入的是字符,而常量是int
    (2)switch(表达式)中表达式的返回值必须是:byte,short,int,char,enum,String
    (3)case子句中的值必须是常量,而不能是变量
    (4)default子句是可选的,当没有匹配的case时,执行default
    (5)break语句用来在执行完一个case分支后使程序调出switch语句块,如果没有写break,程序会顺序执行到switch结尾。
switch(表达式) { 
    case 常量1:
    语句块1;
    break;
    case 常量:
    语句块2;
    break;
    ...
    default:
    语句块;
    break;
}
  • switch和if比较
    (1) 如果判断的具体数值不多,而且符合byte,short,int,char,enum,String这6种类型。虽然两个语句都可以使用,建议使用switch语句。
    (2) 其他情况:这区间判断,对结果为boolean类型判断,使用if,if适用范围更广。

循环控制(for,while,do while,多重循环)

  • for循环控制基本语法: for关键字表示循环控制,for有四要素:(1)循环变量初始化 (2)循环条件(3)循环操作(4)循环变量迭代。循环操作,这里可以有多条语句,也就是要循环执行的代码。
for (循环变量初始化;循环条件;循环变量迭代) {
    循环操作(可以多条语句);
}

细节说明:
(1)循环条件是返回一个布尔值的表达式
(2)for(;循环判断条件;)中初始化和变量迭代可以写到其他地方,但是两边的分号不能省略
(3)循环初始值可以有多条初始化语句,但要求类型一致,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开。

int count = 3;
for(int i = 0,j = 0; i < count; i++,j+=2){
    sout("i = " + i + ",j = " + j); // 0,0 1,2 2,4
}

打印1-100之间所有是9的倍数的整数,统计个数及总和

int count = 0;
int sum = 0;
int t = 9;
int start = t;
int end = 100;
for(int i = start; i <= end; i+=t){
	System.out.print(i + " ");
	count++;
	sum = i + sum;
}
System.out.println();
System.out.println(count + "," + sum);

---

int n = 5;
for(int i = 0; i <= n; i++){
	System.out.println(i + " + " + (n - i) + " = " + n);
}
  • while循环控制基本语法: while循环也有四要素,只是四要素放的位置不一样。
    循环条件是返回一个布尔值的表达式,while循环是 先判断再执行语句
循环变量初始化;
while(循环条件){
    循环体(语句);
    循环变量迭代;
}
  • do-while循环控制基本语法: 也有循环四要素,只是位置不一样,先执行,在判断,因此它至少执行一次,最后有一个分号; while-和do-while区别
循环变量初始化;
do{
    循环体(语句);
    循环变量迭代;
}while(循环条件);
  • 多重循环控制: 讲一个循环放在另一个循环体内,就形成了嵌套循环,for,while,do…while均可以作为外层循环和内层循环,建议一般使用两层,最多不要超过3层,否则代码的可读性很差。
    嵌套循环就是把内层循环当作外层循环的循环体,当只有内层循环的循环条件为false,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的循环。
    设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。

统计3个班成绩情况,每个班有5个同学,求出各个班级的平均分和所有班级的平均分,统计三个班级及格人数。

Scanner sc = new Scanner(System.in);
double totalScore = 0; // 累计所有学生的成绩
int passNum = 0; // 累计及格人数
int classNum = 3; // 班级个数
int stuNum = 5; // 学生个数
for(int i = 1; i <= classNum; i++){ // i 表示班级
    double sum = 0; // 一个班级的总分
    for(int j = 1; j <= stuNum; j++){ // j 表示学生
        System.out.println("请输入" + i + "班第" + j + "同学的成绩");
        double score = sc.nextDouble();
        // 当成绩大于60,passNum++
        if(score >= 60){
        	passNum++;
        }
    	sum += score; // 积累
    }
    // 因为sum是五个学生的总成绩
    System.out.println("第"+i + "总分:" + sum + ",平均成绩 " + sum / stuNum);
    // 把sum积累到totalScore
    totalScore += sum;
}
System.out.println("三个班总分"+totalScore + "平均分" + totalScore / (classNum * stuNum));
System.out.println("及格人数是 " + passNum);

接受一个整数表示层数,打出空心金字塔

Scanner sc = new Scanner(System.in);
System.out.println("请输入层数:");
int totalLevel = sc.nextInt();
for(int i = 1; i <= totalLevel; i++){
    for(int k =1; k <= totalLevel - i;k++){
    	System.out.print(" ");
    }
    for(int j = 1; j <= 2*i-1; j++){
        if(j == 1 || j == 2*i-1 || i == totalLevel) {
        	System.out.print("*");
        }else {
        	System.out.print(" ");
        }
    }
	System.out.println();
}

跳转控制语句 - break

break语句用于终止某个语句块的执行,一般是用在switch或者循环中。

int count = 0;
while(true){
	count++;
	int num = (int)(Math.random() * 100) + 1;
	if(num == 97){
		break;
	}
}
System.out.println("用了" + count + "次");

break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪个一层语句块,abc1是标签名字由程序员指定,在实际开发中尽量不使用标签,如果没有指定break,默认退出最近的循环体。

abc1:
for(int i = 0; i < 4; i++){
	abc2:
	for(int j = 0; j < 8; j++){
		if(j == 2){
			break abc1;
		}
		System.out.println("j = " + j); // 0,1
	}
}

跳转控制语句 - continue

continue语句用于结束本次循环,继续执行下一次循环
continue语句出现在多层嵌套的循环语句体重时,可以通过标签指明要跳过的是哪一层循环。

int i = 1;
while (i <= 4){
	i++;
	if(i == 2){
		continue;
	}
	System.out.println("i = " + i); // 3,4,5
}
label1:
for(int i = 0; i < 2; i++){
	label2:
	for(int j = 0; j < 10; j++){
		if( j == 2){
			continue; // 等价于 continue label2
			// continue label1; // 输出2次[0,1]
		}
		System.out.println("j = " + j); // 输出2次[0,1,3,4,5,6,7,8,9]
	}
}

跳转控制语句 - return

return使用在方法,表示跳出所在的方法,注意:如果return写在main方法,退出程序。

public static void main(String[] args) {
	for(int i = 1; i <= 5; i++){
		if(i == 3){
			System.out.println("冰冰");
//			break; // hello,hello,冰冰,go on..
//			continue; // hello,hello,冰冰,hello,hello,go on..
			return; // hello,hello,冰冰
		}
		System.out.println("hello");
	}
	System.out.println("go on..");

}

练习题

某人有100000元,没经过一次路口,需要交费
1、当现金>50000时每次交5%
2、当现金<=50000时每次交1000
计算该人可以经过多少次路口。while+break

double money = 100000;
int count = 0;
while(true){
	if(money > 50000){
		// money = money - money * 0.05;
		money *= 0.95;
		count++;
	}else if(money >= 1000){
		money -= 1000;
		count++;
	}else { // 钱不够1000
		break;
	}
}
System.out.println(money + "\t" + count); // 767.4979115529641	62

输出1-100之间的不能被5整除的数,每5个一行。

int count = 0;
for (int i = 1; i <= 100; i++) {
	if (i % 5 != 0) {
		count++;
		System.out.print(i + "\t");
		if (count % 5 == 0) {
			System.out.println();
		}
	}
}

求出1-1/2+1/3-1/4+1/5…1/100的和

double sum  = 0;
for(int i = 1; i <=100; i++){
	if(i%2!=0){
		sum += 1.0/i;
	}else{
		sum -= 1.0/i;
	}
}
System.out.println(sum); // 0.688172179310195

求1+(1+2)+(1+2+3)+(1+2+3+4)+…+(1+2+3+…+100)的结果

int sum = 0;
for(int i = 1; i <= 100; i++){ // 3
	for(int j = 1; j <= i; j++){ // 1+2+3
		sum += j;
	}
}
System.out.println(sum); // 171700

数组、排序和查找

数组

数组可以存放多个同一类型的数据,数组也是一种数据类型,是引用类型。

动态初始化
数组的定义:数据类型[] 数组名 = new 数据类型[长度];

数据类型[] 数组名;	
数组名 = new 数据类型[长度];
int[] a = new int[5]; // 创建了一个数组,名字a存放5个int

数组的引用(使用):数组名[下标/索引]
比如:要使用a数组的第3个数:a[2]

静态初始化

数据类型[] 数组名 = {元素值, 元素值...};
int[] a = {1,3,5,7,9,2,4,6,8,0};
相当于:int[] a = new int[10];
a[0]=1,a[1]=3,a[2]=5,a[3]=7,a[4]=2,a[5]=4,a[6]=6,a[7]=8,a[8]=9,a[9]=0

数组使用注意事项和细节

  1. 数组是多个相同类型数据的组合,实现对这些数据的统一管理
  2. 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但不能混用
  3. 数组创建后,如果没有赋值,有默认值int=0,short=0,byte=0,long=0,float=0.0,double 0.0,char \u0000,boolean=false,String=null
  4. 使用数组的步骤:1. 声明数组并开辟空间 2. 给数组各个元素赋值 3. 使用数组
  5. 数组的下标是从0开始的。
  6. 数组下标必须是在指定范围内使用,否则报错:下标越界异常,比如int[] arr = new int[5]; 则有效下标为arr[0] - arr[4]
  7. 数组属于引用类型,数组型数据是对象(object)

数组赋值机制

  1. 基本数据类型赋值,这个值就是具体的数据,而且相互不影响
  2. 数组在默认情况下是引用传递,赋的值是地址。
 int n1 = 2; 
 int n2 = n1;
 n2 = 4;
 sout(n1 + "," + n2); // 2,4
int[] arr1 = {1,2,3};
int[] arr2 = arr1;
arr2[0] = 10;
for(int i = 0; i < arr1.length; i++){
	System.out.println(arr1[i]); // 10
}

在这里插入图片描述

数组反转

方式1:通过找规律反转

int[] arr = {1,2,3,4,5,6};
int temp = 0;
int len = arr.length;
for(int i = 0; i < len/2; i++){
	temp = arr[len - i - 1];
	arr[len - i - 1] = arr[i];
	arr[i] = temp;
}
for(int i = 0; i < len; i++){
	System.out.println(arr[i]);
}

方式2:使用逆序赋值方式

int[] arr = {1,2,3,4,5,6};
int len = arr.length;
int[] arr1 = new int[arr.length];
for(int i = 0; i < arr.length ; i++){
	arr1[len - i - 1] = arr[i];
}

/*
for(int i = arr.length - 1,j = 0; i >= 0; i--,j++){
	arr1[j] = arr[i];
}
*/

// arr1会被当做垃圾销毁
arr = arr1;
System.out.println(arr); // [I@4b67cf4d
System.out.println(arr1); // [I@4b67cf4d

for(int i = 0; i < arr.length; i++){
	System.out.println(arr[i]);
}

数组添加

Scanner sc = new Scanner(System.in);
int[] arr1 = {1,2,3};
do {
	int[] arr2 = new int[arr1.length + 1];
	for (int i = 0; i < arr1.length; i++) {
		arr2[i] = arr1[i];
	}
	System.out.println("请输入你要添加的元素:");
	int addNum = sc.nextInt();

	arr2[arr2.length - 1] = addNum;
	arr1 = arr2;

	for (int i = 0; i < arr1.length; i++) {
		System.out.println(arr1[i]);
	}
	System.out.println("请问是否继续添加 y/n");
	char key = sc.next().charAt(0);
	if(key == 'n'){
		break;
	}
}while(true);