Java学习4

learnspf / 2023-08-07 / 原文

Java学习4

主要学习了java基本语法中的方法和数组,并学习了如冒泡排序和稀疏数组等算法

方法

定义

类似于C语言中的函数,是一段用来完成特定功能的代码片段,一般包括下列语法

修饰符 返回值类型 方法名(参数类型 参数名){
    ...
    方法体
    ...
    return 返回值;
}

image-20230804150349284

示例:

public class Demo2 {
    //main方法
    public static void main(String[] args) {
        int out = max(5,5); //5 5是实际参数
        System.out.println(out);
    }
    public static int max(int a, int b){ //a与b是 形式参数
        int result;
        if(a == b)
        {
            System.out.println("两数字相等!");
            return 0;  //用于终止方法
        }
        if(a > b)
        {
            result = a;
        }else
            result = b;
        return result; //返回值
    }
}

设计原则

  • 本意是功能块,即实现某功能的语句块集合。设计方法最好保持方法的原子性,即1个方法只完成1个功能,这样利于后期拓展

方法重载

  • 举例:
public class Demo2 {
    //main方法
    public static void main(String[] args) {
//        int out = max(5,5);
        double out = max(5.0,10.0);
        System.out.println(out);
    }
    public static double max(double a, double b){...}
    public static int max(int a, int b){...}//方法重载:方法名字相同但参数类型不同

}
  • 规则:
  1. 方法名称必须相同
  2. 参数列表必须不同(个数不同或类型不同、参数排列顺序不同等)
  3. 仅仅返回类型不同不足以成为方法的重载
  4. 方法返回类型可以相同也可以不相同
  • 实现理论:方法名称相同时,编译器会根据调用方法的参数个数、参数类型等逐个去匹配,以选择对应的方法,如果匹配失败,则编译器报错

递归

  • 递归即 方法自己调用自己
  • 递归结构组成
  1. 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环(类似于终止条件?)
  2. 递归体:什么时候需要调用自身方法。
  • 能不用递归就不用递归,对于小基数问题解决起来会很快,但是大基数容易造成内存溢出(无限压栈)
  • 解释:利用递归可以用简单的程序来解决一些复杂的问题。它通常将一个大型复杂问题层层转化为一个与原问题相似规模较小的问题来求解,递归策略只需要少量的程序就可描述出解题过程所需要的多次重复计算,大大减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合

递归典例:递归求 n!

  public class Demo6 {
      //递归求 n! 递归学主要学的是思想
      public static void main(String[] args){
          System.out.println(f(0));
      }
      public static int f(int n){
          if((n == 0) || (n==1))
          {
              return 1;
          }else if(n > 1){
              return n*f(n-1);
          }else {
              System.out.println("n必须是自然数!");
              return 0;
          }
      }
  }

*命令行传递参数

  • 有时候希望运行一个程序时候再给它传递参数,这要靠传递命令行参数给main()函数实现

​ 示例:

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

Doc窗口运行:

image-20230804162947855

注意:class文件必须返回到src类下才能运行,图中也可以看到这一点

*可变参数

个人理解

可变参数目的:减少方法重载的变量定义组数,方便书写同时让程序更加清晰

注:

  1. JDK1.5开始,Java支持传递同类型的可变参数给一个方法
  2. 方法声明中,指定参数类型后加一个省略号(...)
  3. 一个方法只能指明一个可变参数,它必须是方法的最后一个参数,任何普通的参数必须在它之前声明
  • 简单举例1
public class Demo4 {
    public static void main(String[] args){
        Demo4 demo4 = new Demo4();  
        demo4.out(1,2,3,4,5);
    }
    public static void out(int... i){
        System.out.println(i[0]);
        System.out.println(i[1]);
        System.out.println(i[2]);
        System.out.println(i[3]);
        System.out.println(i[4]);
    }
}

举例2

public class Demo5 {
    public static void main(String[] args){
        printMax(1,2,54,51,54161,5,151,415,3.141588);
    }
    public static void printMax(double... num){  //等价于double[] num
        if(num.length == 0) {
            System.out.println("没有数字输入!");
            return;
        }
        double result = num[0];
        for(int i = 0;i<num.length;i++){
            if(num[i] > result){
                result = num[i];
            }
        }
        System.out.println(result);
    }
}

理解与扩展

  1. java是方法语句的集合,它们在一起执行一个功能
  • 方法是解决一类问题的步骤的有序组合
  • 方法包含于类或对象中
  • 方法在程序中被创建,在其他地方被引用
  1. 方法命名符合驼峰原则(多个单词首字母小写后面的首字母大写,如totalNum)

  2. java是值传递!与引用传递区分开

  3. java利用的是栈机制,如递归就是一层层压栈

问题:new还是不太理解

  1. 例题:写一个计算器,要求实现加减乘除功能,并且能够循环接收新的数据,通过用户交互实现
package com.spf.method;
import java.util.Scanner;
public class HomeWork {
    public static void main(String[] args){
        double a,b,c;
        c=  0;
        int flag = 1;
        int error = 0;
        Scanner scanner = new Scanner(System.in);
        while(flag == 1){
            System.out.println("请输入要计算的算式,数字和符号间以空格隔开:");
            double q1 = scanner.nextDouble() ;
            String q2 = scanner.next();
            double q3 = scanner.nextDouble();
            switch(q2){
                case "+":
                    c= add(q1,q3);
                    break;
                case "-":
                    c= subtract(q1,q3);
                    break;
                case "*":
                    c= multiply(q1,q3);
                    break;
                case "/":
                    c= divide(q1,q3);
                    break;
                default:
                    System.out.println("输入符号错误!");
                    error = 1;
                    break;
            }
            if(error == 0){
                System.out.println("计算结果为: " + c);
            }else{
                error = 0;
            }
            System.out.println("是否要继续进行计算?1:继续,2:结束");
            int process = scanner.nextInt();
            if(process == 1){
                flag = 1;
            }else
                flag = 0;
        }
    }
    public static double add(double a, double b){
        return a+b;
    }
    public static double subtract(double a, double b){
        return a-b;
    }
    public static double multiply(double a, double b){
        return a*b;
    }
    public static double divide(double a, double b){
        return a/b;
    }
}

数组

定义

  • 数组是相同类型数据的有序集合,数组描述的是相同类型的若干数据,按照一定的先后次序,其中每一个数据称作一个数组元素,每个数组元素可以通过一个下标访问它们。

  • 数组元素下标是从0开始 如num[0] = 1;所以定义一个n个元素的数组,数组最大的下标为n-1

  • 不同类型数据的的数组默认赋值

  1. int类型定义的数组,初始化默认是0

  2. String类型定义的数组,默认值是null

  3. char类型定义的数组,默认值是0对应的字符

  4. double类型定义的数组,默认值是0.0

  5. float类型定义的数组,默认值是0.0

  6. boolean类型定义的数组,默认值是false

初始化

两种:

  • int[] array; //首选
  • int array[]; //等效于第一种,但是不建议(方便区别于C)

可以直接声明加定义:

  • int[] num = new int[10];

静态初始化和动态初始化

public class ArrayDemo2 {
    public static void main(){
        //静态初始化,创建+赋值
        int[] array = {1,2,3,4,5,6,7};//元素多少个数组就为多大,但创建后数组大小就不可改变了
        // Man是一个类 Man[] people = {new Man(),new Man(),new Man(),new Man()}; 引用类型数组

        //动态初始化:包含默认初始化(未赋值都是默认值)
        int[] num = new int[10];
    }
}
  • 初始化的内存空间分配

image-20230805171449973

特点

  1. 数组长度是确定的,数组一旦被创建,它的大小就是不可以改变
  2. 其元素必须是相同类型,不允许出现混合类型
  3. 数组中元素可以是任何,包括基本类型和引用类型(自己创建的类型,如上面初始化版块代码中的 Man)
  4. 数组变量属于引用类型,数组也可以看成是对象,数组中每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的

二维数组

public class ArrayDemo5 {
    public static void main(String[] args){
        //二维数组定义
        int[][] num =  {{0, 1}, {2, 3}, {4, 5},{6,7}};
        //int[][] num = new int[2][4];
        //System.out.println(num[0][0]);
        //System.out.println(num[0][1]);
        //二维数组遍历
        for(int i=0;i<num.length;i++){
            for(int j = 0;j<num[i].length;j++){
                System.out.println(num[i][j]);
            }
        }
    }
 }

Array类

  • 数组的工具类java.util.Arrays (IDEA中只需要输入Arrays.即可自动生成import)
  • 数组对象本身并没有什么方法供我们调用,但API中提供了工具类Arrays供我们使用,从而可以对数据对象进行一些基本操作
  • 查看JDK文档看详情(IDEA中对于import java.util.Arrays crtl+“Arrays”即可查看详细源码)
  • Arrays类中的方法都是static修饰的静态方法,使用的时候可以直接使用类名进行调用,而不用使用对象来调用(注意是“不用”而不是“不能”)
  • 常用Arrays类下的方法示例
public class ArrayDemo6 {
    public static void main(String[] args) {
        //定义数组

        int[] num = {1,2,4,54,41,846,16184,165416,1151,5848,41};
        System.out.println(Arrays.toString(num));//1.打印输出数组
        printArray(num);//自定义打印输出数组方法
        Arrays.sort(num); //2.升序
        System.out.println(Arrays.toString(num));
        Arrays.fill(num,2,4,0); //3.填充区间:[2,4) 填充数字:0
        System.out.println(Arrays.toString(num));
    }
    public static void printArray(int[] a){ //自定义打印输出数组方法 与toString()效果基本相同
        for(int i=0;i<a.length;i++)
        {
            if(i == 0){
                System.out.print("[");
            }
            if(i == a.length -1){
                System.out.print(a[i] + "]");
            }else{
                System.out.print(a[i] + "," + " ");
            }
        }
        System.out.println();
    }
}

冒泡排序

  • 两层循环,外层冒泡轮数,里层依次比较。通过不断比较相邻两位之间的大小实现排序,

  • 时间复杂度O(n2)

  • 步骤

       1. 比较数组中,相邻的两个元素,如果第一个元素比第二个数大,就交换二者的位置
       2. 每次比较都会产出一个最大 or 最小的数字
       3. 下一轮则可以少一次排序
       4. 依次排序直到结束
    
  • 代码示例

import java.util.Arrays;
public class ArrayDemoBubbleSort {
    public static void main(String[] args){
        int[] num = {1,2,4,54,41,846,16184,165416,1151,5848,41};
        System.out.println(Arrays.toString(num));
        num = bublleSort(num);
        System.out.println(Arrays.toString(num));
    }

    public static int[] bublleSort(int[] a){ 
        for(int i = 0 ;i< a.length-1;i++){ //轮次是 数组长度-1 次
            boolean flag = false; //加入标志位,如果顺序已经好了就可以跳出循环,减少一定的时间成本
            for(int j = 0;j<(a.length - 1 - i);j++){ 
                //注意这里初始就是 length-1(即第一次到到a[a.length-1]就可以了) 
                //也就是倒数第二个数字就不用排了,再排下次if比较时a[j+1]就越界了
                //考虑到不断排序还要-i
                int m;
                if(a[j] > a[j+1]){  
             //升序排列(每次确认一个最大的数到最高位),把括号里的>改成<即可实现降序排列(每次确认一个最小的数到最高位)
                    m = a[j];
                    a[j] = a[j+1];
                    a[j+1] = m;
                    flag = true;
                }
            }
            if(flag == false)
                break;
        }
        return a;
    }
}
  • 八大排序算法

    1、直接插入排序;2、希尔排序;3、简单选择排序;4、堆排序;5、冒泡排序;6、快速排序;7、归并排序;8、桶排序/基数排序

    后续进行拓展学习

稀疏数组

image-20230807132603082

  • 具体代码
public class ArrayDemoSparseArray {
    public static void main(String[] args) {
        //初始数组定义打印
        int[][] array = new int[11][11];
        array[1][2] = 1;
        array[2][3] = 2;
        array[5][6] = 21;
        for (int[] ints : array) {
            for (int anInt : ints) {
                System.out.print(anInt + "\t");
            }
            System.out.println();
        }
        //
        int[][] result;
        result = sparseCreate(array); //调用方法,原始数组改为稀疏数组
        System.out.println("============================");//换行
        for (int[] ints : result) {  //result.for for-each循环
            for (int i : ints) {
                System.out.print(i + "\t");
            }
            System.out.println();
        }
        //
        //调用方法,稀疏数组改为原始数组
        System.out.println("============================");//换行
        for (int[] ints : sparseBack(result)) {
            for (int i : ints) {
                System.out.print(i + "\t");
            }
            System.out.println();
        }
    }
    public static int[][] sparseCreate(int[][] a){  //方法1:创建稀疏数组
        int sum = 0;
        for (int[] ints : a) {
            for (int anInt : ints) {
                if(anInt != 0){
                    sum += 1;
                }
            }
        }
        int[][] result = new int[sum+1][3];
        result[0][0] = a.length;
        result[0][1] = a[0].length;
        result[0][2] = sum;
        int count = 0;
        for(int i = 0;i<a.length;i++){
            for(int j = 0;j<a[i].length;j++){
                if(a[i][j] != 0){
                    count++;
                    result[count][0] = i;
                    result[count][1] = j;
                    result[count][2] = a[i][j];
                }
            }
        }
        return result;
    }
    public static int[][] sparseBack(int[][] a){ //方法2:还原稀疏数组
        int[][] back = new int[a[0][0]][a[0][1]];
        for (int i = 1; i < a.length ; i++) {
            back[a[i][0]][a[i][1]] = a[i][2];
        }
        return back;
    }
}

部分内容结果:

image-20230807132840941

拓展

  1. 数组越界问题

    • 下标合法区间:[0,length-1],如果越界就会报错

    • 错误:ArrayIndexOutOfBoundsException: 数组下标越界异常!

  2. 数组相关小结

    • 数组是相同数据类型(数据类型可以为任何类型)的有序集合
    • 数组也是对象。数组元素相当于对象的成员变量
    • 数组长度是确定的,不可变的,如果越界会报错(见1)
  3. 数组使用

public class ArrayDemo4 {
    public static void main(String[] args){
        int[] num = {1,2,3,4,5,6};
        for (int i : num) { //1.增强for循环,没有下标 jdk1.5新增
            System.out.println(i);
        }
        printarray(num);
        System.out.println(); //换行
        int[] verse = reverse(num);
        printarray(verse);
    }
    public static void printarray(int[] array){//2.数组作为方法入参 功能:数组打印输出
        for(int i = 0;i<array.length;i++){ 
            System.out.print(array[i] + " ");//3.普通for循环
        }
    }
    public static int[] reverse(int[] array){ //4.数组作为方法返回值 功能:数组翻转
        int[] result = new int[array.length];
        for(int i = 0, j = (result.length - 1);i < array.length;i++,j--){
            result[j] = array[i]; //将值重新赋值给一个空数组
        }
        return result;
    }
}
//4.普通for循环

Java内存

  • 存放new对象数组
  • 可以被所有线程共享,不会存放别的对象引用
  • 存放基本变量类型(会包含这个基本类型的具体数值
  • 引用对象的变量(会存放这个引用在堆里的具体地址
  1. 方法区
  • 可以被所有线程共享
  • 包含了所有的class和static变量