zoukankan      html  css  js  c++  java
  • 03_Java数组与方法_02

    3.3 多维数组

    经过前面一、二维数组的练习后不难发现,想要提高数组的维数,只要在声明数组的时候将索引与中括号再加一组即可,所以三维数组的声明为int A[][][],而四维数组为int A[][][][] ……,以此类推。

    使用多维数组时,输入、输出的方式和一、二维相同,但是每多一维,嵌套循环的层数就必须多一层,所以维数越高的数组其复杂度也就越高。以三维数组为例,在声明数组时即赋初值,再将其元素值输出并计算总和。

    范例:TestJava3_7.java

    //下面程序说明了三维数组的使用方法,要输出数组的内容需要采用三重循环
    public class TestJava3_7
    {
      public static void main(String args[])
      {
        int sum=0;
        int A[][][] = {
          {{5,1}, {6,7}},
          {{9,4}, {8,3}}
           };  // 声明数组并设置初值
    
        // 三维数组的输出需要采用三层for循环方式输出
        for(int i = 0; i < A.length; i++)     // 输出数组内容并计算总和
        {
          for(int j = 0; j < A[i].length; j++)
          {
            for(int k = 0; k < A[i][j].length; k++)
            {
              System.out.println("A["+i+"]["+j+"]["+k+"]=" + A[i][j][k]);
              sum += A[i][j][k];
            }
          }
        }
        System.out.println("sum="+sum);
      }
    }

    输出结果:

    A[0][0][0]=5

    A[0][0][1]=1

    A[0][1][0]=6

    A[0][1][1]=7

    A[1][0][0]=9

    A[1][0][1]=4

    A[1][1][0]=8

    A[1][1][1]=3

    sum=43

    由于使用的是三维数组,所以嵌套循环有三层。如果利用for_each遍历,则如下:

    for(int[][] i : A)
    {
      for(int[] j : i)
      {
        for(int iVal : j)
        {
          System.out.println(iVal);
          sum += iVal;
        }
      }
    }

    3.4 引用类型数组

    对象数组概念应用几乎与数组一致,不同的是,对象数组是一组相同类型对象而组成的集合,因此初始化时,对象的默认值是null。

    与基本数据类型数组一样,对象数组的声明和初始化采取的方法类似,如下: 

    类名称[] 对象数组名称 = new 类名称[对象个数]

    使用方法与基本数据类型数组类似。例如:要取得对象数组的长度也是使用对象数组名称.length。

    3.5 浅拷贝与深拷贝

    浅拷贝是直接定义一个新的数组变量等于原始数组变量。

    实例如下:

    int a[] = {1, 2, 3, 4};
    
    int b[] = a;
    
    b[3] = 9;
    
    for(int iVal : a)
    
    System.out.println(iVal);

    此时输出数组a的所有元素,发现a[3]也变为了9。因为b和a指向同一个内存空间。这种数组赋值方式称为浅拷贝

    在一维数组中,我们介绍了System类的arraycopy可以实现将一维数组的所有元素拷贝到另一个一维数组中,相当于是将数据“克隆”了一份,而不是复制一个变量,让两个变量来引用同一个数组,保存数据的独立性。这就是深拷贝

    实例如下:

    int a[] = {1, 2, 3, 4};
    
    int b[] = new int[4];
    
    System.arraycopy(a, 0, b, 0, b.length);
    
    b[3] = 9;
    
    for(int iVal : a)
    
    System.out.println(iVal);

    此时输出数组a的所有元素,发现a[3]仍为4。因为b和a指向不同的堆空间。这种数组赋值的方式称为深拷贝

    如果是二维数组,则示例如下:

    public class TestJava
    {
      public static void main(String[] args) {
      int[][] source = new int[5][]; // 定义源数组
    
      // 给源数组赋值
      for (int i = 0; i < 5; i++)
    {     source[i]
    = new int[i + 1];
        
    for (int j = 0; j < source[i].length; j++)       source[i][j] = j + 1;   }   // 打印源数组的数据   System.out.println("-------------源数据-------------");   for (int i = 0; i < source.length; i++)
      {     
    for (int j = 0; j < source[i].length; j++)       System.out.print(source[i][j] + " ");     System.out.println();   }   // 定义目的数组   int[][] target1 = source;//浅拷贝   //如果只拷贝一维,也是浅拷贝   //int[][] target1 = new int[5][];   //System.arraycopy(source, 0, target1, 0, source.length);   // 改变目的1数组的值   target1[1][0] = 100;     // 打印源数组的信息,可以看到值改变,说明没有深拷贝   System.out.println("-----------浅拷贝后输出-----------");   for (int i = 0; i < source.length; i++)
      {   
      for (int j = 0; j < source[i].length; j++)       System.out.print(source[i][j] + " ");     System.out.println();   }   int[][] target2 = new int[5][];   // 数组的深拷贝,先拷贝"第一维"的   System.arraycopy(source, 0, target2, 0, source.length);   // 再深拷贝   for (int i = 0; i < 5; i++)
      {     target2[i]
    = new int[i + 1];     System.arraycopy(source[i], 0, target2[i], 0, i + 1);   }   // 改变目的2数组的数据   target2[1][0] = 999;   // 打印源数组的信息,可以看到值没有改变,说明是深拷贝   System.out.println("-----------深拷贝后输出未把100改成999-----------");   for (int i = 0; i < source.length; i++)
      {     
    for (int j = 0; j < source[i].length; j++)       System.out.print(source[i][j] + " ");   System.out.println();   }  } }

    3.6 方法

    方法可以简化程序的结构,也可以节省编写相同程序代码的时间,达到程序模块化的目的。在每一个类里出现的main()即是一个方法。使用方法来编写程序代码有相当多的好处,它可简化程序代码、精简重复的程序流程,并把具有特定功能的程序代码独立出来,使程序的维护成本降低。

    在面向对象的世界里,方法只能作为类和对象的附属,不能独立定义,只能在类里面定义。方法要么属于一个类,要么属于一个对象。方法不能独立执行,执行方法必须使用类或者对象作为调用者。

    方法分为两种:

    普通方法(对象的方法):没有static关键字修饰的方法 

    静态方法(类的方法):使用static关键字修饰的方法

    方法的定义格式:

     

    方法名称规则:第一个单词的字母小写,以后每个单词的首字母大写。如果不需要传递参数到方法中,只要将括号写出,不必填入任何内容。此外,如果方法没有返回值,则返回值类型要指明为void,return语句可以省略或写成“return;”。

    类中方法的定义示例如下:

     

    类中方法的调用:

    普通方法(对象的方法):通过类的实例化对象来调用

    静态方法(类的方法):通过类来调用

     

    3.6.1 方法操作的简单范例

    TestJava3_8是一个简单的方法操作范例,它在显示器上先输出19个星号“*”,换行之后再输出“I Like Java!”这一字符串,最后再输出19个星号。

    范例:TestJava3_8.java 

    //以下程序主要说明如何去声明并使用一个方法
    public class TestJava3_8
    {
      public static void main(String args[])
      {
        star(); // 调用star() 方法
        System.out.println("I Like Java !");
        star(); //调用star() 方法
      }
    
      public static void star() // star() 方法
      {
        for(int i=0;i<19;i++)
          System.out.print("*");  // 输出19个星号
        System.out.print("
    ");   // 换行
      }
    }

    输出结果:

    *******************

    I Like Java !

    *******************

    TestJava3_8中声明了两个方法,分别为main()方法与star()方法。因为main()方法是程序进入的起点,所以把调用star()的程序代码编写在main()里。在main()的第6行调用start() 方法,此时程序的运行流程便会进到10~15行的star()方法里执行。执行完毕后,程序返回main()方法,继续运行第7行,输出“I Like Java !”字符串。接着第8行又调用sart()方法,程序再度进到第10~15行的star()方法里运行。运行完后,返回main()方法里,因main()方法接下来已经没有程序代码可供执行,于是结束程序TestJava3_8。

    从本程序中,可以很清楚地看出,当调用方法时,程序会跳到被调用的方法里去运行,结束后则返回原调用处继续运行。在TestJava3_8中,调用与运行star()方法的流程如图3-6所示:

     

    图3-6  调用与运行star()方法的流程

    star()方法并没有任何返回值,所以star()方法前面加上了一个void关键字。此外,因为star()没有传递任何的参数,所以star()方法的括号内保留空白即可。

    至于在star()方法之前要加上static关键字,这是因为main()方法本身也声明成static,而在static方法内只能访问到static成员变量(包括数据成员和方法成员),因star()方法被main()方法所调用,自然也要把star()声明成static才行。此时如果还不了解static的真正用意也没有关系,将在以后的章节对static关键字做详尽的介绍。

    3.6.2 方法的参数传递与返回值

    如果方法有返回值,则在声明方法之前就必须指定返回值的数据类型。相同的,如果有参数要传递到方法内,则在方法的括号内必须填上该参数及其类型。

    TestJava3_9是一个关于计算长方形对角线长度的范例,其中show_length()方法可接收长方形的宽与高,计算后返回对角线的长度。

    范例:TestJava3_9.java 

     

    //以下的程序说明了方法的使用
    public class TestJava3_9
    {
      public static void main(String args[])
      {
        double num;
        num=show_length(22, 19);         // 输入22与19两个参数到show_length()里
        System.out.println("对角线长度= "+num);
      }
      
    public static double show_length(int m, int n)   {     return Math.sqrt(m*m + n*n); // 返回对角线长度   } }

     

    输出结果:

    对角线长度= 29.068883707497267

    TestJava3_9的第7行调用show_length(22,19),把整数22和19传入show_length()方法中。第12行则利用Math类里的sqrt()方法计算对角线长度。而sqrt(n)的作用是将参数n开根号。因sqrt()的返回值是double类型,因此show_length()返回值也是double类型。

    Java中参数传递实质是值传递。Java中进行赋值操作或函数调用中传递参数时,遵循值传递的原则:

    基本类型数据传递的是该数据的值本身

    引用类型数据传递的是对对象的引用(句柄),而非对象本身

    只有引用类型数据传递方式才能将数据写回(传出)。

    范例:TestJava3_10.java

    public class TestJava3_10
    {
      public static void main(String[] args) 
      {
        int x = 10;
        int y = 20;
    
        System.out.println("main:x="+x+"	y="+y);
        swith(x, y);
        System.out.println("main:x="+x+"	y="+y);
    
        int arr[] = {10, 20};
        System.out.println("main:x=" + arr[0] + "	y=" + arr[1]);
        change(arr);
        System.out.println("main:x=" + arr[0] + "	y=" + arr[1]);
      }
    
      public static void swith(int x, int y)
      {     x
    =x+y;     y=x-y;     x=x-y;     System.out.println("swith:x="+x+" y="+y);   }   public static void change(int a[])
      {     a[
    0] = (a[0] + a[1]) - (a[1] = a[0]);     System.out.println("change:x="+a[0]+" y="+a[1]);   } }

    3.6.3 将数组传递到方法里

    方法不只可以用来传递一般的变量,也可用来传递数组。本节将讲述在Java里是如何传递数组以及如何处理方法的返回值是一维数组的问题。

    3.6.3.1 传递一维数组

    要传递一维数组到方法里,只要指明传入的参数是一个数组即可。TestJava3_11是传递一维数组到largest()方法的一个范例,当largest()接收到此数组时,便会把数组的最大值输出。

    范例:TestJava3_11.java

    //一维数组作为参数来传递,这里的一维数组采用静态方式赋值
    public class TestJava3_11
    {
      public static void main(String args[])
      {
        int score[] = {7, 3, 8, 19, 6, 22};  // 声明一个一维数组score
        
    // 将一维数组score传入largest()方法中,并将最大值返回来     System.out.println("最大的数= " + largest(score));   }
      
    public static int largest(int arr[])   {     int tmp = arr[0];
        
    for(int i=0; i<arr.length; i++)       if(tmp < arr[i])
        tmp
    = arr[i];     return tmp;   } }

    输出结果:

    最大的数= 22 

    TestJava3_11的第10~17行声明largest()方法,并将一维数组作为该方法的参数。第12~16行找出数组的最大值,并将它返回。注意如果要传递数组到方法里,只要在方法内填上数组的名称即可,如本题的第8行所示。

    3.6.3.2 传递二维数组

    二维数组的传递与一维数组相当类似,只要在方法里声明传入的参数是一个二维数组即可。程序TestJava3_12是有关传递二维数组的一个范例,把二维数组A传递到print_mat()方法里,并在print_mat()方法里把该数组值输出。

    范例:TestJava3_12.java 

    //以下程序说明了如何将一个二维数组作为参数传递到方法中
    public class TestJava3_12
    {
      public static void main(String args[])
      {
        int A[][]={{51,38,22,12,34}, {72,64,19,31}}; // 定义一个二维数组A
        print_mat(A);
      }
    
      public static void print_mat(int arr[][])   // 接收整数类型的二维数组
      {
        for(int i=0; i<arr.length; i++)
        {
          for(int j=0; j<arr[i].length; j++)
            System.out.print(arr[i][j]+" ");  // 输出数组值
        
          System.out.print(
    " "); // 换行     }   } }

    输出结果:

    51 38 22 12 34

    72 64 19 31

    TestJava3_12的第9~17行声明了print_mat()方法,它可接收二维数组,并利用两个for循环把数组的值输出来。注意可以利用.length取出数组的行数或列数,如程序的第11与13行所示。

    3.6.3.3 返回数组的方法

    如果方法返回整数,则必须在声明时在方法的前面加上int关键字。相反的如果返回的是一维的整型数组,则必须在方法的前面加上int[]。若是返回二维的整型数组,则加上int[][],以此类推。

    TestJava3_13.java是返回二维数组的一个范例。将一个二维数组传入addTen()方法中,在addTen()方法内将每一个元素加10之后返回它,最后在main()里输出此数组。

    范例:TestJava3_13.java 

    //以下的程序说明了方法中返回一个二维数组的实现过程
    public class TestJava3_13
    {
      public static void main(String args[])
      {
        int A[][] = {{51,38,82,12,34}, {72,64,19,31}}; // 定义二维数组
        int B[][] = new int[2][5];
        B = addTen(A);    // 调用addTen(),并把返回的值设给数组B
    
        for(int i=0; i<B.length; i++)// 输出数组的内容
        {
          for(int j=0; j<B[i].length; j++)
            System.out.print(B[i][j]+" ");
    
          System.out.print(
    " ");     }   }   public static int[][] addTen(int arr[][])   {     for(int i=0;i<arr.length;i++)       for(int j=0;j<arr[i].length;j++)         arr[i][j] += 10; // 将数组元素加10

        return arr; // 返回二维数组   } }

    输出结果:

    61 48 92 22 44

    82 74 29 41

    第16行赋值addTen()是可接收二维数组,且返回类型是二维的整型数组。第20行是完成了在循环内将数组元素值加10的操作,而运算之后的结果再由第21行的return语句返回。

    3.6.4 方法的重载

    方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要它们的参数个数或参数类型不同即可。在这种情况下,该方法就叫被重载了。举例如下:

    老板指派采购员买东西,当老板没有指明买什么时,采购员可能默认买地瓜;

    如老板指明要采购员买大米,采购员可能到最近的超市买10斤大米;

    如老板指明采购员今天晚上到龙华宜家超市买5斤大米,那采购员将不得不按老板指定的时间、地点去购买5斤大米。如图3-8所示:

     

    图3-7  采购食材

    方法的重载:方法名相同,参数不同(参数个数、参数类型、参数顺序)。仅仅依靠方法的返回值不同,不能构成方法的重载。

    范例:TestJava3_14.java

    //以下程序说明了方法的重载操作
    public class TestJava3_14
    {
      public static void main(String[] args)
      {
        int int_sum;
        double double_sum ;
        int_sum = add(3, 5);   // 调用有两个参数的add方法
        System.out.println("int_sum = add(3, 5)的值是:"+int_sum);
        int_sum = add(3, 5, 6); // 调用有三个参数的add方法
        System.out.println("int_sum = add(3, 5, 6)的值是:"+int_sum);
        double_sum = add(3.2, 6.5); // 传入的数值为doule类型
        System.out.println("double_sum = add(3.2, 6.5)的值是:"+double_sum);
        double_sum = add(3.2, 6);//传入的前一个是double,后一个是int
        System.out.println("double_sum = add(3.2, 6)的值是:"+double_sum);
        double_sum = add(3, 6.5);//传入的前一个是int,后一个是double
        System.out.println("double_sum = add(3, 6.5)的值是:"+double_sum);
      }
    
      public static int add(int x, int y)
      {
        return x + y;
      }
    
      public static int add(int x, int y, int z)
      {
        return x + y + z;
      }
    
      public static double add(double x, double y)
      {
        return x + y;
      }
    
      public static double add(double x, int y)
      {
        return x + y;
      }
    
      public static double add(int x, double y)
      {
      return x + y;
      }
    }

    输出结果:

    int_sum = add(3, 5)的值是:8

    int_sum = add(3, 5, 6)的值是:14

    double_sum = add(3.2, 6.5)的值是:9.7

    double_sum = add(3.2, 6)的值是:9.2

    double_sum = add(3, 6.5)的值是:9.5

    可以发现上题中的add被重载了五次,但每个重载了的方法所能接受参数的个数或类型或顺序不同,相信大家现在应该可以明白方法重载的概念了。那么为什么要用方法重载?

    我们知道,在现实中,往往一个类会实现复杂的功能,其中定义的多种方法可能实现的功能意义都是一样,比如我们已经熟悉的System类中的静态方法println(),在该类中println()被定义了多个,每一个方法都有不同的参数,现在我们已知道每一个println()都具有相同的功能:在控制台上输出内容!

    我们来假想一下,如果按照每个方法定义一个不同名称,那么我们将在System类中定义十多种不同名称的打印方法,虽然功能实现了,首先,我们是否需要编写代码前给这十几种方法取不同名称,并且还得保证名称唯一,这就会增加我们的工作量;其次我们还得记住每一个方法名对应的功能,如果稍有记错,那就会得到错误的结果!因此,我们有更好的解决办法,通过重载,可以在一个类中定义相同名称、不同参数的实现相同功能的多个方法,这样就避免了给每个方法取不同名称、熟记每个不同名的方法对应的功能的额外工作量,提高了我们的开发效率。

    3.6.5 方法的参数可变

    从JDK1.5以后,Java允许定义方法的参数长度可变,从而允许为方法指定数量不确定的形参,如果在定义方法时,在最后一个参数的类型后增加三点(…),则表明该形参可以接受多个参数值,参数可变被当成数组传入

    范例:TestJava3_15.java

    //以下程序说明了方法的参数可变
    
    public class TestJava3_15
    {
      public static void main(String[] args)
      {     
    int[] arr={1, 34, 4, 3, 6, 36};     add1(1, 34, 4, 3, 6, 36);     add1(1, 9, 10, 22, 77);     add1(1, 9, 10);     add2(arr);   }   public static void add1(int... y)
      {     
    int iSum = 0;
        
    for(int i : y)
        {       iSum
    += i;     }     System.out.println("total:" + iSum);     iSum = 0;     for(int i = 0; i < y.length; i++)       iSum += y[i];
        System.out.println(
    "total:" + iSum);   }   public static void add2(int[] y)
      {     
    int iSum=0;     for(int i : y)
        {       iSum
    += i;     }     System.out.println("total:" + iSum);     iSum = 0;     for(int i = 0; i < y.length; i++)       iSum += y[i];     System.out.println("total:" + iSum);   } }

    需要注意的是,不能同时出现add(int)和add(int [])的形式。

    3.6.6 递归

    一个方法调用自己的过程称为递归调用

    递归方法的运行实现原理:

    我们发现,递归就是一个不停调用方法自身的一个过程,即方法的反复调用!计算机是通过栈的机制来实现方法调用的。首先,我们来了解下计算机的方法调用机制:

    1.程序执行前,计算机会在内存中创建一个调用栈 ,一般会比较大;

    2.当调用某个方法时,会有一个和该被调用方法相关的记录信息被推入到栈中;

    3.被推入到栈中的记录信息包括内容:传递到被调用方法中的参数值、该方法的局部变量、该方法的返回值。

    4. 当返回某个方法或者方法结束时,会从栈中取出对应的方法记录信息。

    5. 栈的使用机制:后进先出(LIFO)。

    注意:虽然递归方法简洁,但是效率不是完全就比循环高,有时甚至低。因为我们考虑算法不仅要从时间、增长率来考虑,还要考虑空间(一般指内存)问题,递归的栈空间是我们必须考虑的,因为每次方法的调用都需额外的空间来存储相关信息。

    构造递归方法的关键在于寻找递归算法和终结条件。例如:求n的阶乘:

    n! = n*(n-1)*(n-2)*(n-3)*2*1 = n*(n-1)!//递归算法

    1! = 1//终结条件

    计算n!必须算出(n-1)!,计算(n-1)!必须计算出(n-2)!...由此推到1!=1。可以归纳阶乘的递归算法如下:f(n) = n * f(n-1);   结束条件:f(1) = 1。

    范例:TestJava3_16.java

    import java.util.*;
    
    public class TestJava3_16
    {
      public static void main(String args[])
      {
        System.out.print("请输入一个正整数:");
        Scanner scan = new Scanner(System.in);
        int iNum = scan.nextInt();
        System.out.println(iNum + "!=" + factorial(iNum));
      }
    
      public static int factorial(int num)
      {
        /*
        * 利用非递归实现
        * int iRet = 1;
        * for(int i = 1; i <= num; i++)
        *iRet *= i;
        * return iRet;
        */
    
        //利用递归实现
        if(num == 1)
        return 1;
        return num * factorial(num-1);
      }
    }

    注意事项:

    递归容易造成死循环,所以一定要注意退出条件。

    ·本章摘要:

    1、数组是由一组相同类型的变量所组成的数据类型,它们是以一个共同的名称来表示的。数组按存放元素的复杂程度,分为一维、二维及多维数组。

    2、使用Java中的数组,必须经过两个步骤:(1)声明数组、(2)开辟内存给该数组。

    3、在Java中欲取得数组的长度(也就是数组元素的个数),可以利用.length 来完成。

    4、如果想在声明时就给数组赋初值,只要在数组的声明格式后面加上初值的赋值即可。

    5、Java允许二维数组中每行的元素个数均不相同。

    6、在二维数组中,若是想取得整个数组的行数,或是某行元素的个数时,也可以利用.length 来获取。

    7、方法的重载:在同一个类中允许同时存在一个以上的同名方法,只要它们的参数个数或类型不同即可。在这种情况下,该方法就叫被重载了,这个过程称为方法的重载。

    8、方法的参数可变:当作数组处理。

    感谢阅读。如果感觉此章对您有帮助,却又不想白瞟

                                     

  • 相关阅读:
    2.6
    zuoye
    练习1
    练习
    练习
    4
    3
    2
    1
    1.3
  • 原文地址:https://www.cnblogs.com/springl/p/8962682.html
Copyright © 2011-2022 走看看