第四章 流程控制与数组

不管哪一种编程语言,都会提供两种基本的流程控制结构,分支结构和循环结构。其中分支结构用于现实根据条件来选择执行某段代码,循环结构则用于实现根据循环条件重复执行某段代码。Java同样提供了这两种流程控制结构的语法,Java提供了if和switch两种分支语句,并提供了while、dowhile和for三种循环语句。除此之外,JKD5还提供了一种新的循环:foreach循环,能以更简单的方法来遍历集合、数组的元素。Java还提供了break和continue来控制程序的循环结构

顺序结构

任何变成语言中最常见的程序结构就是顺序结构。循环结构就是程序从上到下执行的执行,中间没有任何判断和跳转

分支结构

Java提供了两种最常见的分支控制结构:if和switch语句,其中if语句使用布尔表达式或布尔值作为分支条件来进行流程控制,而switch语句则用于对多个整形值进行匹配,从而实现分支控制

if语句

if语句使用布尔表达式或布尔值作为分支条件来进行分支控制
第一种形式:

if(logic expression){
	statement...
}

第二种形式:

if(logic expression){
		statement...
	else{
		statement...
	}
}

第三种形式:

if(logic expression){
		statement...
	else if(logic expression){//可以有多个elseif else也可以省略
		statement
	}else{
		statement
	}
}
    public class Demo{
        public static void main(String[] args) {
            String str = "Java";
            if(str.equals("Java")){
                System.out.println("欢迎来学Java");
            }
            //if语句花括号括起来的就是一个整体要么都执行要么不执行
        }
    }

在这里插入图片描述

    public class Demo{
        public static void main(String[] args) {
            var a = 3;
            if(a>4)
                System.out.println("a大于5");
            else
                System.out.println("a小于5");
            }
            //这样的写法可行 但是后面只能有一行代码 剩下的代码不会被执行,不建议这样写if语句 因为丧失完整性和可读性
            //尽量写成 if{}else{} 括号要齐全 既美观可读性又高
        }

在这里插入图片描述
if语句的语法也很重要,如果编写不合理也会引发错误

public class Demo {
    public static void main(String[] args) {
        var age = 45;
        if(age>20){
            System.out.println("青年人");
        } else if (age > 40) {
            System.out.println("中年人");
        }else if(age>60){
            System.out.println("老年人");
        }
    }
}

在这里插入图片描述
这里可以看到,为什么我的age变量符合第二个条件却输出了第一个条件的执行语句,这明显是不合理的,这就是语法问题,如果改下下面的写法就会矫正这个问题

public class Demo {
    public static void main(String[] args) {
        var age = 45;
        if(age>60){
            System.out.println("老年人");
        } else if (age > 40) {
            System.out.println("中年人");
        }else if(age>20){
            System.out.println("青年人");
        }
    }
}

在这里插入图片描述
我们把最后的条件先写上,把条件判断顺序更改了一下发现问题没有再次发生,else语句的含义是否则,else本身就是一个条件,else的隐含条件就是对前面条件取反。因此应该将顺序书写合理
可以通过加强判断条件来增加程序的健壮性,比如添加逻辑运算符加强算术

public class Demo {
    public static void main(String[] args) {
        var age = 65;//年龄变量 判断条件
        if(age>60){//如果年龄大于 60 输出老年人
            System.out.println("老年人");//打印 老年人
        }
        if(age > 40&& !(age>60)){//如果年龄大于40并且不大于60 输出中年人
            System.out.println("中年人");//打印 中年人
        }
        if(age > 20 && !(age>60)&&!(age>40&&!(age>60))){//如果年龄大于20 并且年龄不大于60 并且年龄不大于40也不大于60
            System.out.println("青年人");//打印 青年人
        }
    }
}

在这里插入图片描述

在这里插入图片描述

Java11改进的switch分支语句

switch语句由一个控制表达式和多个case语句标签组成,和if语句不同的是,switch语句后面的控制表达式类型只能是 byte、short、char、int四种整数类型,枚举类型和Java.lang.String类型(Java7才允许),不能是boolean类型。
switch语句往往需要在case语句后紧跟一个代码块,case标签作为这个代码块的标识,switch语句的语法如下:

    public class Demo {
        public static void main(String[] args) {
            int a = 1;
            switch (a){
                case 1:{
                    System.out.println("a等于1");
                    break;
                }
                case 2:{
                    System.out.println("a等于2");
                    break;
                }
                case 3:{
                    System.out.println("a等于3");
                    break;
                }
                case 4:{
                    System.out.println("a等于4");
                    break;
                }
                case 5:{
                    System.out.println("a等于5");
                    break;
                }
                default:{
                    System.out.println("a的赋值不在判断条件内");
                }
            }
        }
    }

case后面紧跟着判断条件和花括号,花括号里是符合条件的执行语句,default是如果没有符合以上判断条件的时候触发的保底语句
在这里插入图片描述
在这里插入图片描述
下面将演示如果每个标签执行语句里去掉break会怎么样

    public class Demo {
        public static void main(String[] args) {
            int a = 4;
            switch (a){
                case 1:{
                    System.out.println("a等于1");
                }
                case 2:{
                    System.out.println("a等于2");
                }
                case 3:{
                    System.out.println("a等于3");
                }
                case 4:{
                    System.out.println("a等于4");
                }
                case 5:{
                    System.out.println("a等于5");
                }
                default:{
                    System.out.println("a的赋值不在判断条件内");
                }
            }
        }
    }

在这里插入图片描述
发现,其他条件语句也被执行了,证明break关键字是结束结束循环或判断,如果没有break语句,执行结果就容易丢入陷阱,运行结果会无法判断,失去精度
Java7开始增强了switch语句,允许switch语句的控制表达式是java.lang.String类型的变量或表达式,只能是java.lang.String不能是StringBuffer或StringBuilder这两种字符串类型

public class Demo{
    public static void main(String[] args) {
        switch ("春天"){
            case "春天":{
                System.out.println("春暖花开");
                break;
            }
            case "夏天":{
                System.out.println("夏日炎炎");
                break;
            }
            case "秋天":{
                System.out.println("硕果累累");
                break;
            }
            case "冬天":{
                System.out.println("白雪皑皑");
                break;
            }
        }
    }
}

在这里插入图片描述
增加保底代码的话只需要default关键字即可

default:{
	System.out.println("输入季节有误");
}

循环结构

循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被反复执行的代码被称为循环体 ,当反复执行这个循环体时,需要在合适的时候把循环条件改为假,从而结束循环,如果循环一直执行下去就是个死循环。
循环语句包含的4个部分
初始化语句、循环条件、循环体、迭代语句

while语句

while语句每次执行循环体之前会先对循环条件求值,如果为true则运行循环体部分

public class Demo {
    public static void main(String[] args) {
        int a = 0;
        var temp =0;
        while(a<10){
            temp+=1;
            System.out.println("while语句执行"+",当前执行次数"+temp);
            a++;
        }
    }
}

在这里插入图片描述
while(){}是while语句写法 括号里是布尔表达式,花括号内是循环体,a++是结束循环的条件,如果没有a++ 这个while循环将陷入死循环将变得毫无意义
如果while语句的循环体执行代码只有一则可以省略花括号,如果while语句后面是;号则是个空循环,后面的语句和while语句没有任何关系

while(a<5);
System.out.println("while语句");

do while语句

do while语句比较特殊,do while语句会先执行再判断

public class Demo {
    public static void main(String[] args) {
        int a=0;
        int temp = 0;
        do {
            temp+=1;
            System.out.println("do while语句执行"+",次数:"+temp);
            a++;
        }while(a<10);
    }
}

在这里插入图片描述
现在可能看着和while语句没有什么区别,但是如果让a变量直接不符合循环条件呢?

public class Demo {
    public static void main(String[] args) {
        int a=10;
        int temp = 0;
        do {
            temp+=1;
            System.out.println("do while语句执行"+",次数:"+temp);
            a++;
        }while(a<10);
    }
}

在这里插入图片描述
可以看到,循环条件刚开始就不符合但是仍然执行了一遍,这就是do while的特性:先执行后判断

****for循环 ***

for循环是Java中最常用的循环,也是重点学习的循环语句
for循环的语法更加简洁:

for(循环变量初始化;循环判断条件;迭代语句){
	循环体代码;
}
public class Demo{
    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            System.out.println("循环次数:"+i);
        }
    }
}

在这里插入图片描述
相比while和do while语句来说 for循环更为简洁
for语句先执行初始化语句,先计算循环条件的值,如果返回true,则执行循环体,循环体执行结束后执行循环迭代语句。因此,对于for语句而言,循环条件总比循环体要多执行一次,因为最后一次执行循环条件返回false,将不再执行循环体
if语句可以使用 break(结束循环)和continue(跳过本次循环)来控制流程
for语句不一样,不管continue跳过放在哪里也不会影响for语句的迭代,不建议再在循环体内添加迭代语句,会出现意想不到的异常
for语句循环体如果只有一行,也可以省略花括号
如果for循环的条件变量在循环结束后依然还有特殊用处的话就可以创建一个方法局部作用域变量来保存这个值

嵌套循环

嵌套循环意思是两个for循环一个在外部一个在内部
外层循环为n次,内层循环的循环次数为m次,那么内层循环的实际循环次数为NxM次

public class Demo{
    public static void main(String[] args) {
        for(var i=0;i<5;i++){
            for(var j=0;j<3;j++){
                System.out.println("i的值为:"+i+"j的值为:"+j);//外部循环每执行一次,内部循环就会全部执行一次
            }
        }
    }
}

在这里插入图片描述
看到运行结果可以看出,外层循环每循环一次,内层循环将会全部循环一次,其实外层循环就相当于想让内层循环全部执行几次的简洁写法,而不用封装在方法多次调用或复制代码多占几行
for循环还有标签,可以使用标签来精确结束循环,达到精准控制

控制循环结构

break结束循环

Java语音没有提供goto语句来控制循环的跳转,这种做法提高了可读性但是降低了程序流程控制的灵活性,为了弥补不足Java提供了break和continue来控制循环结构,除此之外,return可以结束整个方法,当然也就结束了一次循环
使用break结束循环:

 public class Demo{
     public static void main(String[] args) {
         for(int i=0;i<10;i++){
             if(i==4){
                 break;
             }
             System.out.println("当前循环次数:"+i);
         }
     }
 }

在这里插入图片描述
这是使用了break结束循环的情况。if语句判断如果循环条件到4则停止循环
还有标签结束或控制循环的语法,但是标签只能写在语句之前才有作用

 public class Demo{
     public static void main(String[] args) {
         outer:
         for(int i=0;i<10;i++){
             if(i==4){
                 break outer;
             }
             System.out.println("当前循环次数:"+i);
         }
     }
 }

在这里插入图片描述

continue跳过循环

continue关键字是跳过本次循环的用处,和break不一样,达到要求条件后不会结束循环,而是跳过此次条件循环,不会影响下次循环的执行

 public class Demo{
     public static void main(String[] args) {
         for(int i=0;i<10;i++){
             if(i==4){
                 continue;
             }
             System.out.println("当前循环次数:"+i);
         }
     }
 }

**加粗样式**
可以看到4并没有打印出来,说明continue关键字生效了,但是如果continue语句的执行顺序在后面效果会不一样

 public class Demo{
     public static void main(String[] args) {
         for(int i=0;i<10;i++){

             System.out.println("当前循环次数:"+i);
             if(i==4){
                 continue;
             }
         }
     }
 }

在这里插入图片描述
可以看到,和上一个程序的代码一样,只是把if语句和continue换了一下位置,结果4还是输出了,这就是顺序执行,所以很多代码一定要考虑他的执行顺序,break也是如此,如果不希望4输出,但是却写到后面和本次continue位置一样的话,4还会输出

使用return结束方法

这里不建议使用return结束循环,return的功能是结束一个方法,当方法体执行到return语句试,这个方法将被结束
Java程序中大部分循环都放在程序方法中执行,例如签名介绍所有循环示范程序,一旦在循环体内执行到一个return语句,return也会结束该方法,自然循环也会跟着结束

 public class Demo{
     public static void main(String[] args) {
         for(int i=0;i<10;i++){
             if(i==4){
                return;
             }
             System.out.println("当前循环次数:"+i);

         }
     }
 }

在这里插入图片描述

 public class Demo{
     public static void main(String[] args) {
         for(int i=0;i<10;i++){
             if(i==4){
                return;
             }
             System.out.println("当前循环次数:"+i);

         }
         System.out.println("方法已经结束,这行代码不会被输出");
     }
 }

在这里插入图片描述
这个程序2完全可以证明return结束了方法,而不只是单纯结束了循环

数组类型

数组是编程语言中最常见的一种数据结构,可用于存储多个数据,每个数组元素存放一个数据,通常可通过数组索引来访问每个元素的数据

理解数组,数组也是一种数据类型

因为Java是面向对象语言,而类与类之间支持基础关系,这样可能产生一个数组里可以存放多种数据类型的假象,假如有一个水果数组,要求每个数组元素都是水果,实际上数组元素既可以是苹果,也可以是香蕉,因为苹果香蕉都继承了水果,但这个数组的元素的类型还是唯一的,只能是水果类型

定义数组

定义数组分两大类
动态定义和静态定义

type[] arrayName;
type arrayNmae[];

这种这两种语法来说通常选择第一个,第一种格式不仅具有更好的语意,而且具有更高的可读性,对于type[]arrayName[]更像是定义了一个变量,可读性较低,很多语言都不支持 type arrayName[]这种数组定义语法
数组是一种引用类型的变量。因此使用它来定义一个变量时,仅仅表示定义了一个引用变量,也就是定义了一个指针(Java没有明确表示过指针的存在)这个引用变量还没有指向有效的内存,因此定义数组时不能指定数组的长度,而且犹豫定义数组只是定义了一个引用变量,并未指向任何有效的内存空间,所以还没有内存空间来存储数组元素,因此这个数组也不能使用,只有对数组进行初始化后才可以使用

数组的初始化

 public class Demo{
     public static void main(String[] args) {
         int [] intArr1 = new int[]{5,15,25};//静态数组创建
         int [] intArr2 = {5,15,25};//静态数组的简洁写法
         int [] prices = new int[5];//动态数组创建
         /*静态创建和动态创建数组的区别在于,静态创建时直接赋值,不指定具体内存,让系统去分配内存,
         而动态数组的创建是直接指定该数组的内存大小,然后再赋值,通常情况下都是使用静态创建数组
         * */
        System.out.println("intArr索引为0的元素数值:"+intArr1[0]);//访问intArr数组索引为0元素的数据
        intArr1[0] = 55;//通过同样的访问方式来修改数值
        System.out.println("修改后的值:"+intArr1[0]);//输出新修改后的数组元素值
     }
 }

在这里插入图片描述
如果输出或访问的索引是不存在的会抛出数组索引越界异常 java.lang.ArrayIndexOutOFBoundsException:N
N就是程序员试图访问的数组索引
编程程序会遇到非常多的异常信息,记录和学习一下异常可以方便以后开发对出现异常的代码的快速查询和修改
所有数组都提供了一个length属性,就是数组的长度,这个属性方便了对数组的遍历:

 public class Demo{
     public static void main(String[] args) {
      int [] intArr ={5,15,25,55,65};
      for(int i=0;i<intArr.length;i++){
            System.out.println("intArr数组的第"+i+"个数组的内容是:"+intArr[i]);
        }
     }
 }

在这里插入图片描述
数组访问索引元素的语法就是 数组索引+[索引值]
所以在for语句的循环体中
在这里插入图片描述
i变量是对数组长度的遍历,假如数组最大长度为5,那么每次i变量都会自增到数组的长度 0 1 2 3 4
直接在循环体代码中访问每次数组中i循环变量的索引就可以实现全部遍历一遍
实际运行:
在这里插入图片描述
整形数组的默认值是 0 如果没有赋值遍历5次就是输出5个0
如果是字符串的话将会输出两个null
在这里插入图片描述
因此Java数组索引从0开始算,所以5长度包含了0

foreach循环

foreach循环是for循环的简化,这种遍历数组和集合元素时无需获得数组长度,无需根据索引来访问数组元素和集合元素,foreach自动遍历数组和集合的每个元素

     public class Demo{
         public static void main(String[] args) {
          String [] StringArr =new String[4];
          StringArr[0]="Hello";
          StringArr[1]="Java";
          for(String str:StringArr){
              System.out.println(str);
          }
         }
     }

在这里插入图片描述
从上面程序可以看出foreach循环遍历数组元素时无需获得数组长度,也无需根据索引来访问数组元素,foreach循环和其他循环不同的是,它无需循环条件,无需循环迭代语句,这些部分由系统来完成,foreach自动迭代每个数组元素,当每个数组元素都被迭代后,foreach循环自动结束,对于foreach循环可言,循环变量的类型可以由编译器自动推断出来,而且使用var定义也不会与会降低程序的可读性,因此建议使用var来声明循环变量的类型
当foreach的迭代变量在循环时不要对循环变量进行赋值,虽然语法上是允许的,但是没有太大的实际意义,而且极容易引发错误

深入数组

实际上数组对象被存储在堆(heap)内存中,如果引用该数组的数组引用是一个局部变量那么它被存储在栈(stack)内存中
在这里插入图片描述
如果需要访问图中的数组元素,则程序中只能通过p[index]的形式实现。也就是说,数组引用变量是访问堆内存中数组元素的根本方式
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁,因此所有在方法中定义的局部变量也是放在栈内存中,在程序中创建一个对象将会被保存到运行时数据区中,以便反复使用(因为对象的创建代价通常比较大)这个运行时数据区就是堆内存,堆内存中的对象不会方法的结束而销毁即使方法结束后这个对象还可能被另外一个引用所引用(在方法的参数传递中很常见),则这个对象依然不会被销毁,只有当一个对象没有任何索引引用它的时,系统的垃圾回收器才会在合适的时候回收

public class Demo{//Demo类起始
    //static TestClass testClass = new TestClass();//静态创建一个测试类对象来证明堆内存中的对象
    TestClass testClass = new TestClass();//创建一个测试类对象来证明堆内存中的对象
    public void RunOne(){//第一个运行的方法
        testClass.Info();//调用了测试类的信息方法(Info是信息的意思)
    }
    public void RunTow(){//第二个运行的方法
        TestClass getTest = testClass;//添加第二个引用(参考上面那句 "这个对象可能还会被另一个引用变量所引用")
        getTest.Test();//通过新测试类对象引用来调用测试类的测试方法(Test是测试的意思)
    }
    public static void main(String[] args) {//主方法 main 程序入口
        Demo demo = new Demo();//创建主类 Demo的对象来进行所有操作 实例化类 称 实例对象
        demo.RunOne();//调用第一个运行的方法
        demo.RunTow();//调用第二个运行的方法
        TestClass getTestClass = demo.testClass;//再次创建一个新的测试类引用,如果这里不使用demo.testClass来指明就需要将成员变量testclass添加上static静态修饰
        //TestClass getTestClass = testClass; 静态的赋值方法 其实也可以通过实例对象来创建 但是不建议这么做
        getTestClass.Test();
        getTestClass.Info();
    }
}
class TestClass{//我们的测试类
    public void Info(){//测试类的信息方法
        System.out.println("这是测试类的Info方法");//打印内容
    }
    public void Test(){//测试类的测试方法
        System.out.println("测试");//打印内容
    }
}

在这里插入图片描述
这里用到了面向对象的内容,但是上述语法全部注释,语法也比较简单,阅读起来并不吃力
可以看到在程序中创建的 测试类对象 从开始到结束有了三个引用,也证明了这个对象没有随着方法的结束而销毁
如果堆内存中的数组不再有任何引用变量指向自己,则这个数组将成为垃圾,该数组占用的内存将会被系统的垃圾回收器回收,因此,为了让垃圾回收器回收一个数组所占的内存空间,可以将数组变量赋值为null,也就切断了数组引用变量和实际数组之间的引用关系,实际的数组就变成了垃圾

public class Demo{
    public static void main(String[] args) {
        int[]a={6,7,20};
        var b = new int[4];
        System.out.println("b数组的长度为:"+b.length);
        for(int i=0;i<a.length;i++){
            System.out.println(a[i]);
        }
        for(int i=0;i<b.length;i++){
            System.out.println(b[i]);
        }
        b=a;
        System.out.println("b数组的长度为:"+b.length);
    }
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

基本类型数组的初始化

对于基本类型数组而言,数组元素的值直接存储在对应的数组元素中,因此初始化数组时,先为该数组分配内存空间,然后直接将数组元素的值存入对应数组元素中

public class Demo{
    public static void main(String[] args) {
        int [] iArr;
        iArr = new int[5];//动态初始数组
        for(var i =0;i<iArr.length;i++){
            iArr[i]=i+10;
            System.out.println(iArr[i]);
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

引用类型数组的初始化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sutdents[1]和Lee引用指向的是同一个对象所以输出内容也相同,下面代码将再次证实它们指向的是同一个对象
在这里插入图片描述
使用lee引用将age变量改成11,再次调用info方法发现它们输出的age都变成了11

没有多维数组

Java语言支持多维数组的语法,但是还没多维数组,从数组底层的运行机制上来看

public class Demo{
    public static void main(String[] args) {
        int[][]intArr =new int[4][3];
        intArr[3][0]=5;
        intArr[3][1]=15;
        intArr[3][2]=25;
        for(int i=0;i<intArr.length;i++){
            for(int j=0;j<intArr[i].length;j++){
                System.out.println(intArr[i][j]);
                if(j==2){
                    System.out.println("第"+i+"轮循环"+"结束");
                }
            }
        }
    }
}

在这里插入图片描述
可以看到 表面上像是二维数组但是语法却和一位数组没有什么区别,虽然数组可以无限扩展但它的底层机制仍然和一位数组没有什么区别
在这里插入图片描述

操作数组的工具类:Arrays

Java提供的Arrays类里包含一些static修饰的方法可以直接操作数组,这个Arrays类里包含了如下几个static修饰的方法
int binarySearch(type[]a,type key):使用二分法查询key元素值在a数组中出现的索引:如果a数组不包含key元素值,则返回负数。调用该方法时要求数组中元素已经按升序排列,这样才能得到正确结果
int binarySearch(type[]a,int fromIndex,int toIndex,type key)::这个方法和前一个类似,但它只搜索a数组中fromIndex索引的元素。调用该方法时要求数组中已经按升序排列,这样才能得到正确的结果
type[]copyOf(type[]original,int length):这个方法将会把original数组复制成一个新数组,其中length是新数组的长度。如果length小于original数组的长度,则新数组就是原数组前面length个元素,如果length大于original数组长度,则新数组的前面元素就是原数组的所有元素,后面补充0(数值类型)、false(布尔类型)或者null(引用类型)。
type[]copyOfRange(type[]original,int from,int to):这个方法与前面方法相似,但这个方法只复制original数组的from索引到to索引的元素。
boolean equals(type[]a,type[]a2):如果a数组和a2数组的长度相等,而且a数组和a2数组的数组元素也一一相同,该方法返回true。
void fill(type[]a,type val):改方法将会把a数组的所有元素都复制为val。
void fill(type[]a,int fromIndex,int toIndex,type val):该方法与前一个方法的作用相同,区别只是该方法仅仅将a数组的fromIndex到toIndex索引的数组元素赋值为val。
void sort(type[]a):该方法对a数组的数组元素进行排序。
void sort(type[]a,int fromIndex,int toIndex):该方法与前一个方法相似,区别是该方法仅仅对fromIndex到toIndex索引的元素进行排序。
String toString(type[]a):该方法将一个数组转换为一个字符串。该方法按顺序把多个数组元素连缀在一起,多个数组元素使用英文逗号和空格隔开

import java.util.Arrays;

public class Demo{
    public static void main(String[] args) {
        var a = new int[]{3,4,5,6};//新建一个整形数组
        var a2 = new int[]{3,4,5,6};//新建一个整形数组
        System.out.println("a数组与a2数组是否相等:"+ Arrays.equals(a,a2));//比较a和a2数组是否相等
        var b = Arrays.copyOf(a,6);//复制a元素创建一个新数组
        System.out.println("a数组和b数组元素是否相等:"+Arrays.equals(a,b));//比较是否相等
        System.out.println("b数组的元素为:"+Arrays.toString(b));//数值类型数组元素默认值为0
        Arrays.fill(b,2,4,1);//从第二个索引到第三个索引元素值改为1
        System.out.println("b数组元素为:"+Arrays.toString(b));//输出b数组元素值
        Arrays.sort(b);//对数值进行排序
        System.out.println("b数组的元素为:"+Arrays.toString(b));//输出b数组元素值
    }
}

在这里插入图片描述
Arrays类处于java.util包下
System类里也包含了一个static void arraycopy方法
void parallePrefix(xxx[]array,XxxBinaryOperator op):该方法使用op参数指定的计算公式得到结果作为新的数组元素。op计算公式包括left、right两个形参,其中left代表新数组中的前一个索引处的元素,right代表array数组中当前索引处的元素。新数组的第一个元素无需计算,直接等于array数组的第一个元素
void parallelPrefix(xxx[]array,int fromIndex,int toIndex,XxxBinaryOperator op):该方法与上一个方法相似,区别是该方法仅重新计算fromIndex到toIndex索引的元素
void setAll(xxx[]array,IntToXxxFunction generator):该方法使用指定的生成器为所有元素设置值,该生成器控制数组元素的值生存算法
void parallelSetAll(xxx[]array,IntToXxxFunction generator):该方法的功能与上一个方法相同,只是该方法增加了并行能力,可以利用多CPU并行来提高性能
void parallelSort(xxx[]a):该方法的功能与Arrays类以前就有的sort()方法相似,只是该方法增加了并行能力,可以利用CPU并行来提高性能
void parallelSort(xxx[]a,int fromIndex,int toIndex):该方法与上一个相似的方法,区别是该方法仅对fromIndex到to Index的元素进行排序
Spliterator.OfXxx spliteator(xxx[]array):该数组所有元素转换为对应的Spliterator对象
Spliterator.OfXxx spliteator(xxx[]array,int startInclusive,int endExclusive):该方法与上一个方法相似,区别是该方法仅转换startInclusive到endExclusive索引的元素
XxxStream stream(xxx[]array):该方法将数组转换为Stream,Stream是Java8新增的流式编程的API
上面方法列表中,所有以parallel开头的方法都表示该方法可以利用CPU并行能力来提高性能,上面方法中的xxx代表不同的数据类型,比如处理int[]型数组应将xxx换成int,处理long[]型数组时应将xxx换成long

import java.util.Arrays;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;

public class Demo {
    public static void main(String[] args) {
        var arr1 = new int[]{3,-4,25,16,30,18};
        Arrays.parallelSort(arr1);
        System.out.println(Arrays.toString(arr1));//排序后的数组
        var arr2 = new int[]{3,-4,25,16,30,18};
        Arrays.parallelPrefix(arr2, new IntBinaryOperator() {
            @Override
            public int applyAsInt(int left, int right) {
                return left*right;
            }
        });
        System.out.println(Arrays.toString(arr2));
        var arr3 = new int[5];
        Arrays.parallelSetAll(arr3, new IntUnaryOperator() {
            @Override
            public int applyAsInt(int operand) {
                return operand*5;//每个数组索引元素加乘5公式
            }
        });
        System.out.println(Arrays.toString(arr3));
    }
}

上述内容涉及到接口和匿名内部类的知识以及都可以使用lambad表达式来简化

数组应用举例

人民币数值转换程序的代码在后一个文章,详细去查看,并且注释详细表明了逻辑思路
这里提出简单的代码全流程理解:

private String[]hanArr = {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
//定义一个大写的数组,0~9
private String[]unitArr ={"十","百","千"};
//定义一个单位数组 十 百 千 索引 0 1 2
private String[] divide(double num){//处理小写的方法
	var zheng = (long)num;//传进来的参数是双精度浮点型
	//使用long长整数来接受这个参数,强制变为整数
	var xiao = Math.round((num-zheng)*100);//获取小数点部分 保留两位数
	//round是四舍五入的函数*100是相当于保留两位小数点数值
	return new String[]{zheng+"",String.valueOf(xiao)};//拼接数组
	//返回一个数组 整数变量加空字符串变成字符串 和String的数值转字符串方法 ValueOf(type value);
	private String toHanStr(String numStr){//处理大写的方法 该方法比较复杂
		var reslut = "";//定义一个空字符串用于最后的数值接收
		int numLen = numStr.length();//获得传进来的参数长度
		//numLen用于对字符串的遍历操作使用
		for(var i=0;i<numLen;i++){//对字符串进行遍历
			var num =numStr.charAt(i)-48;//接收转换为整形的数值
			//在ASCII中恰好相差48 例如(char)'4'-48 = 4
			if(i!=numlen-1&&num!=0){//如果i(循环遍历变量)字符串长度 不是负一并且不等于0 也就是只要i不为0且不是最后一位数字
			//最后一位数字的数组会返回-1
			//详细说一下这里的判断逻辑思路:
			//首先是 i!=numlen-1 其实就是 假如i的长度是5 但是长度是0起始 numlen-1就相当于始最后一位数字
			//!=0 不等于0 比较好理解
				result +=hanArr[num]+unitArr[num-2-i];
				//大写的数值获取对应大写 第二个是单位 
				//单位有3个 索引为 012 假设长度为4个数值 5211 第一个5 索引是 0 4-2-0 = 2; 
				//千的索引为2 第二个2 4-2-1 = 1; 百的索引为1 第三个1 4-2-2 = 0 十索引为0 再后推就不加大单位了
			}
		}else{
				result +=hanArr[num];//如果不符合上述条件 则不加单位
		}			
	}
	return result;//返回最后拼接的数组
}
//main方法就不写了 内容大概就是创建一个对象 调用Arrays的.toString方法 
//参数十demo的小写方法 下一行代码是调用大写方法传入参数无需数组工具类

全部代码请看下一个文章 本文章只是对此大概描述重点内容

二维数组实现二维五子棋运行小程序

依旧是完整代码和逻辑思路请看和数字处理程序的文章,本文章只讲解重点内容

//首先既然弄二维可视化棋盘就要先创建一个棋盘
private static int BOARD_SIZE = 15;//棋盘的参数
private String[][]board;//棋盘变量
//先弄一个初始化棋盘的方法
public void initBoard(){
	board = new String[BOARD_SIZE][BOARD_SIZE];//传入参数 15*15完成数组初始化
	for(var i=0;i<BOARD_SIZE;i++){
		for(var j=0;j<BOARD_SIZE){
			board[i][j] = "✚";//双循环遍历
			//这个符号比较特殊 输入法的工具箱中的符号大全一般都有
		}
	}
}
//接下来再创建将棋盘打印到控制台的方法 打印棋盘
public void printBorad(){
	for(var i=0;i<BOARD_SIZE;i++){
		for(var j=0;j<BOARD_SIZE;j++){
			System.out.print(board[i][j]);//双循环遍历
			//打印遍历内容 输出语句没有 ln 否则将变成 一行直接一个的长队列
		}
		System.out.print('\n');//换行转义符 打印完一组就换行
	}
}
//主方法的语句:
//主方法语句比较复杂和特殊 这里专门挑选比较重点的代码来说
Demo demo = new Demo();//实例化对象
demo.initBorad();demo.printBorad();//初始化和打印棋盘
System.out.println("请输入您下棋的坐标,应以x,y的格式:");//提示语句 这是初始化语句 也就是第一次提示
var br = new BufferedReader(new InputStreamReader(System.in));//这行代码设计到IO流 后续文章的内容
//这里我也有点小忘所以详细内容也具体不了多少
//创建输入流对象 参数为扫描仪的输入获取 一般IO流初始化都需要添加异常处理
String inputStr = null;//创建一个接收输入内容的字符串
while((inputStr=br.readLine())!=null){//如果输入内容不为空
//这里 br.readLine 意思是输入的下一行内容
	String[]posStrArr = inputStr.split(",");//新建一个数组
	//新建一个数组并且设置字符串分隔符为','逗号
	var xPos = Integer.parseInt(posStrArr[0]);//x轴 第一个参数获取
	var yPos = Integer.parseInt(posStrArr[1]);//y轴 第二个参数获取
	demo.board[yPos-1][xPos-1] = "●";//将输入对应参数位置的内容变为 “●”符号 表示下棋的坐标
	//这里为什么是 各参数减一 因为数组是0起始的 为了校准位置正确
	demo.printBoard();//再次打印 新内容变化的棋盘
	System.out.println("请输入您下棋的坐标,应以x,y的格式:");//提示语句 这次每次都会有的提示语句 初始化语句只有一次
	//这里如果没有初始化第一次提示语句会显得没有那么人性,以及没有那么直观和简单
}

这个程序其实很多东西都没有得到完善,比如没有异常处理,如果玩家输入的不正确之类的,还有没有游戏结束的判断,比如五子棋,竖着、斜着,横着等,必须扫描"✚"来判断;这个算法比较复杂,还有可以添加人工智能和自己下棋,这个就比较久远的学习了,但是实际上也很简单。
这里实在由于时间紧迫,程序并没有多么优化和精美,健壮性也很差,比如如果分隔符输入成汉语的逗号 ‘,’ 会直接导致程序结束抛出数值 format异常 这些都可以通过后续的类来处理和解决,如果后面时间多了可以尝试着将这个程序进行更远更复杂的扩展,也没有必要非背会这些代码,只要自己理解能看懂就行,明白其中代码的逻辑思路,去消化和理解语法和流程。
两个程序的完整代码请看文章 《第四章 数组学习小程序》
这里再多一嘴,第五和第六章就是面向对象上和下了,是非常重要的内容,一定要理解好才能无压力的进行后面的学习,面向对象非常有趣,不要过度死记硬背,编程是一门爱好和过程享受,自己换一种思路去学习,效果总比无感情学习更好,面向对象的学习我的总结笔记将更为生动形象,进行很多比喻和抽象讲解,敬请期待!


版权声明:本文为qq_55868059原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。