Java面向对象(基础)--方法应用

news2024/10/5 15:34:40

文章目录

  • 一、方法的重载
    • 介绍
    • 案例
      • (1)案例1
    • 练习
      • (1)练习1
      • (2)练习2
      • (3)练习3
      • (4)练习4
  • 二、可变个数形参的方法
    • 介绍
    • 举例
      • (1)举例1
      • (2)举例2
      • (3)举例3
      • (4)举例4
      • (5)举例5
    • 练习
  • 三、方法值传递机制
    • 介绍
    • 举例
      • (1)案例1
      • (2)案例2
    • 练习
      • (1)练习1
      • (2)练习2
      • (3)练习3
  • 四、递归方法
    • 介绍
    • 举例
      • (1)举例1
      • (2)举例2
      • (3)举例3
      • (4)举例4
    • 练习
      • (1)练习1
      • (2)练习2
      • (3)练习3
      • (4)练习4

一、方法的重载

再谈方法之1:方法的重载(overload)

介绍

在idea里面键盘输入Ctrl+N,然后输入arrays:
image.png

点击第一个工具类,进入之后按Ctrl+F12,可以看到很多同名的方法,比如equals方法:
image.png

这些方法方法名一样,形参列表不一样,这些方法其实就可以称为重载的方法。

  1. 定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。
    满足这样特征的多个方法,彼此之间构成方法的重载。

  2. 总结为:“两同一不同
    两同:同一个类、相同的方法名
    一不同:参数列表不同。① 参数个数不同 ② 参数类型不同

(int,String)与(String,int)不同

注意:方法的重载与形参的名、权限修饰符、返回值类型都没有关系

  • 以下都是add方法重载:
public class test{
    public void add(int i,int j){
        
    }
    public void add(int i,int j,int k){
        
    }
    public void add(String s1,String s2){
        
    }
    public void add(int i,String s){
        
    }
    public void add(String s,int i){
        
    }
    
}
  • 下面两个就无法构成重载:
 public void add(int i,int j){
        
}
 public void add(int m,int n){
        
}

编译器报错信息:
image.png

此时编译器分辨不出两个方法的区别,比如现在调用它们:

public class test{
    public static void main(String[] args){
        test p=new test();
        test.add(12,4);
    }
    public void add(int i,int j){
        
	}
	public void add(int m,int n){
        
	}
}

这时候编译器会报错,因为分辨不出来调用的add是哪一个。

  • 下面两个也无法构成重载:
public void add(int i,int j){
        
}
public int add(int m,int n){
    
}

方法名之前的内容根本不影响方法重载的判断,就是add前面的public int,只需要看add后面的()里面的内容即可。只要括号里面的东西不一样就叫重载,一样就不能构成重载,编译器分辨不出到底是哪一个方法,就会报错。

  1. 举例
    Arrays类中sort(xxx[] arr)、binarySearch(xxx[] arr,xxx)、equals(xxx[] ,yyy[])

  2. 如何判断两个方法是相同的呢?(换句话说,编译器是如何确定调用的某个具体的方法呢?)
    先看方法名,再看形参列表。

  • 如何判断两个方法是相同的呢?

方法名相同,且形参列表相同。(形参列表相同指的是参数个数和类型都相同,与形参名没关系)

要求:在一个类中,允许存在多个相同名字的方法,只要他们的形参列表不同即可。

  • 编译器是如何确定调用的某个具体的方法呢?

先通过方法名确定了一波重载的方法,进而通过不同的形参列表,确定具体的某一个方法。

  1. 在同一个类中不允许定义两个相同的方法

案例

(1)案例1

看下面代码:

public class test{
    public static void main(String[] args){
        test p=new test();
        test.add(12,4);
    }
    public void add(int i,int j){
        System.out.println("111111");
	}
	public void add(double m,double n){
        System.out.println("222222");
	}
}

当我们调用test.add(12,4)的时候,12和4可以看作int类型–>匹配第一个add方法,12和4也可以看作自动类型提升–>匹配第二个add方法。

那么到底匹配哪一个方法呢?

其实编译器很懒,能不自动提升就不提升,两个add方法可以同时存在,同时存在的时候优先调用没有提升的(第一个add方法);

若没有第一个add方法,它就会自动提升为double类型,进而调用第二个add方法。

如若非要让test.add(12,4);调用第二个add方法,那么可以强制类型转换它:test.add((double)12,4);


但是如果现在调用的是这样:

public class test{
    public static void main(String[] args){
        test p=new test();
        test.add(6,3.0);
    }
    public void add(int i,int j){
        System.out.println("111111");
	}
	public void add(double m,double n){
        System.out.println("222222");
	}
}

因为3.0是double类型,所以前面的6只能自动类型提升为double类型,进而调用的是第二个add方法。

练习

(1)练习1

练习1:判断与void show(int a,char b,double c){}构成重载的有:


a)void show(int x,char y,double z){}     //no

b)int show(int a,double c,char b){}      //yes

c) void show(int a,double c,char b){}    //yes

d) boolean show(int c,char b){}          //yes

e) void show(double c){}                 //yes

f) double show(int x,char y,double z){}  //no

g) void shows(){double c}                //no

(2)练习2

练习2:

编写程序,定义三个重载方法并调用。方法名为mOL。

三个方法分别接收一个int参数、两个int参数、一个字符串参数。

分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。

public class test1 {
    public int mOL(int a){
        return a*a;
    }
    public int mOL(int a,int b){
        return a*b;
    }
    public void mOL(String a){
        System.out.println(a);
    }
}
public class test1_1 {
    public static void main(String[] args) {
        test1 ob=new test1();
        int c=ob.mOL(5);
        System.out.println(c);
        int d=ob.mOL(4,6);
        System.out.println(d);
        ob.mOL("helllo");

    }
}

运行结果:
image.png

(3)练习3

练习3:

定义三个重载方法max():

第一个方法求两个int值中的最大值,

第二个方法求两个double值中的最大值,

第三个方法求三个double值中的最大值,并分别调用三个方法。

public class test2 {
    public int max(int a,int b){
        return (a>=b)?a:b;
    }
    public double max(double a,double b){
        return (a>=b)?a:b;
    }
    public double max(double i,double j,double k){
        /*double tempMax=max(i,j);
        return max(tempMax,k);*/
        return (max(i,j)>k)?max(i,j):k;
    }
}

(4)练习4

//面试题
public class InterviewTest {
    public static void main(String[] args) {

        int[] arr = new int[]{1,2,3};
        System.out.println(arr);//地址值

        char[] arr1 = new char[]{'a','b','c','d','e'};
        System.out.println(arr1);//abc

        boolean[] arr2 = new boolean[]{false,true,true};
        System.out.println(arr2);//地址值

    }
}

输出结果:
image.png

看似我们调用的是一个方法,其实是不一样的。

可以将鼠标光标放在println上。

可以看到,第一个println调用的形参是Object:(第三个也一样)

数组是引用类型,编译器认为调用的是Object,默认打印地址值。
image.png

而第二个println调用的形参是char[]:(当数组是char类型的,编译器会帮咱们遍历一下)
image.png

将鼠标光标放在println上面,按住Ctrl键,点击println,然后按Ctrl+F12
image.png

二、可变个数形参的方法

再谈方法之2:可变个数形参的方法(jdk5.0)

介绍

  1. 使用场景
    在调用方法时,可能会出现方法形参的类型是确定的,但是参数的个数不确定。此时,我们就可以使用可变个数形参的方法

方法形参的类型是确定的,但是参数的个数不确定

在操作数据库的时候,可能会使用到下面的方法:

String sql="update customers set name=?,email=? where id=?";   //修改客户表

String sql1="update customers set name=? where id=?";

public void update(String sql,Object ... objs);	//上述两种情况,第一个传进来的有多少个问号第二个参数就有多少项
  1. 格式:(参数类型 ... 参数名)

  2. 说明:
    ① 可变个数形参的方法在调用时,针对于可变的形参赋的实参的个数可以为:0个、1个或多个
    ② 可变个数形参的方法与同一个类中,同名的多个方法之间可以构成重载
    ③ 特例:可变个数形参的方法与同一个类中方法名相同,且与可变个数形参的类型相同的数组参数不构成重载。
    ④ 可变个数的形参必须声明在形参列表的最后
    ⑤ 可变个数的形参最多在一个方法的形参列表中出现一次

举例

(1)举例1

public class ArgsTest {
    public static void main(String[] args) {
        ArgsTest test=new ArgsTest();
        test.print();
        test.print(1,2);
    }
    public void print(int...num){
        System.out.println("111");
    }
}

结果:
image.png

(2)举例2

public class ArgsTest {
    public static void main(String[] args) {
        ArgsTest test=new ArgsTest();
        test.print();
        test.print(1);
        test.print(1,2);
    }
    public void print(int...num){
        System.out.println("111");
    }
    public void print(int i){
        System.out.println("222");
    }
    public void print(int i,int j){
        System.out.println("333");
    }
}

结果:
image.png

先找确定的,没有冲突就行。

(3)举例3

可变个数形参的方法与同一个类中方法名相同,且与可变个数形参的类型相同的数组参数不构成重载。

下面代码有误:

    public void print(int...num){
        System.out.println("111");
    }
    
    public void print(int[] nums){
        
    }

注意:编译器认为int...numint[] nums一致。

因此这样也不会报错:

public class ArgsTest {
    public static void main(String[] args) {
        ArgsTest test=new ArgsTest();
        
        test.print(new int[]{1,2,3});
    }
    public void print(int...num){
        System.out.println("111");
    }

    /*public void print(int[] nums){

    }*/

}

在方法里面,将它们都遍历一遍:

编写的时候不一样,调用的时候就当它是数组。

public void print(int...num){
    System.out.println("111");
    for (int i = 0; i <num.length ; i++) {
        System.out.println(num[i]);
    }
}

/*public void print(int[] nums){
    for (int i = 0; i <num.length ; i++) {
        System.out.println(num[i]);
    }
}*/

若是数组,调用的时候需要new一下,但现在只需要将数放在这即可。

test.print(1,2,3);

//test.print(new int[]{1,2,3});

(4)举例4

可变个数的形参必须声明在形参列表的最后

以下方式是正确的:

public void print(int i,int...num){

}

以下方式会报错:

public void print(int...num,int i){

}

报错信息:
image.png

(5)举例5

这俩个方法在一个类中可以同时存在:

//①
public void print(int...num){
 
}
//②
public void print(int i,int...num){

}

但是不建议写,虽然不报错,但是调用的时候编译器会懵:
image.png

练习

题目: n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回 空字符串" "

public class StringConCatTest {
    public static void main(String[] args) {
        StringConCatTest test=new StringConCatTest();
        String info=test.concat("-","hello","world");
        System.out.println(info);   //hello-world

        System.out.println(test.concat("/", "hello"));  //hello
        System.out.println(test.concat("-"));   //无
    }
    public String concat(String operate,String...strs){
        String result="";
        for (int i = 0; i < strs.length; i++) {
            if(i==0){
                result+=strs[i];
            }else{
                result+=(operate+strs[i]);
            }
        }
        return result;
    }
}

测试结果:
image.png

三、方法值传递机制

介绍

再谈方法之3:方法的值传递机制(针对变量赋值操作)

对于方法中的变量有两种:①方法内声明的变量(普通局部变量)②方法形参的位置声明的变量,在调用方法的时候给它传具体的实参。

  1. (复习)对于方法内声明的局部变量来说:如果出现赋值操作

    如果是基本数据类型的变量,则将此变量保存的数据值传递出去。
    如果是引用数据类型的变量,则将此变量保存的地址值传递出去。

public class ValueTransferTest {
    public static void main(String[] args) {
        //main方法中声明的变量是局部变量
        //1.基本数据类型的局部变量
        int m=10;
        int n=m;	//传递的是数据值
        System.out.println("m="+m+",n="+n);	//m=10,n=10

        m++;
        System.out.println("m="+m+",n="+n);	//m=11,n=10

        //2.引用数据类型的局部变量
        //2.1数组类型
        int[] arr1=new int[]{1,2,3,4};
        int[] arr2=arr1;	//传递的是地址值

        arr2[0]=10;
        System.out.println(arr1[0]);    //10

        //2.2对象类型
        Order order1=new Order();
        order1.orderId=1001;

        Order order2=order1;
        order2.orderId=1002;

        System.out.println(order1.orderId); //1002

    }
}

class Order{
    int orderId;
}

👻图示:

①基本数据类型
image.png

②数组类型
image.png

③对象类型
image.png

  1. 方法的参数的传递机制:值传递机制(存什么传什么)

2.1 概念(复习)

形参:在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。

实参:在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。

2.2 规则:实参给形参赋值的过程
> 如果形参是基本数据类型的变量,则将实参保存的数据值赋给形参。
> 如果形参是引用数据类型的变量,则将实参保存的地址值赋给形参。

①基本数据类型

public class ValueTransferTest1 {
    public static void main(String[] args) {
        ValueTransferTest1 test=new ValueTransferTest1();

        //1.对于基本数据类型的变量来说
        int m=10;
        test.method1(m);
        System.out.println("m="+m); //10
    }
    public void method1(int m){
        m++;
    }

}

内存:
image.png

②引用数据类型

public class ValueTransferTest1 {
    public static void main(String[] args) {
        ValueTransferTest1 test=new ValueTransferTest1();
        
        //2.对于引用数据类型的变量来说
        Person p=new Person();
        p.age=10;

        test.method2(p);
        System.out.println(p.age);  //11
    }
    public void method2(Person p){
        p.age++;
    }
}
class Person{
    int age;
}

内存:
image.png

  1. 面试题:Java中的参数传递机制是什么?值传递。(不是引用传递)

举例

4.案例

(1)案例1

交换两个变量的值。

public class ValueTransferTest2 {
    public static void main(String[] args) {
        int m=10;
        int n=20;

        System.out.println("交换前:m="+m+",n="+n);

        //交换两个变量的值
        int temp=m;
        m=n;
        n=temp;

        System.out.println("交换后:m="+m+",n="+n);

    }
}

能不能将交换两个变量的值封装到一个方法中?

下面的方法不可行

public class ValueTransferTest2 {
    public static void main(String[] args) {
        int m=10;
        int n=20;

        System.out.println("交换前:m="+m+",n="+n);	//交换前:m=10,n=20

        //1.操作1
        /*//交换两个变量的值
        int temp=m;
        m=n;
        n=temp;*/

        //2.操作2
        //调用方法
        ValueTransferTest2 test=new ValueTransferTest2();
        test.swap(3,4);	//调用完就会出栈

        System.out.println("交换后:m="+m+",n="+n);	//交换后:m=10,n=20

    }
    public void swap(int m,int n){
        int temp=m;
        m=n;
        n=temp;
    }
}

swap方法压入栈之后,只在swap里交换了值,然后swap弹出栈,不会改变原有的值

核心代码解析:
image.png

(2)案例2

下面这种方式能不能实现互换呢?

public class ValueTransferTest3 {
    public static void main(String[] args) {
        Data data=new Data();
        data.m=10;
        data.n=20;

        System.out.println("交换前:m="+data.m+",n="+data.n);

        //1.操作1
        int temp=data.m;
        data.m=data.n;
        data.n=temp;

        System.out.println("交换后:m="+data.m+",n="+data.n);

    }

}
class Data{
    int m;
    int n;
}

显然是可以的,结果如下:
image.png


接下来,将交换的代码写在方法里面,可不可行呢?(见操作2)

public class ValueTransferTest3 {
    public static void main(String[] args) {
        Data data=new Data();
        data.m=10;
        data.n=20;

        System.out.println("交换前:m="+data.m+",n="+data.n);

        //1.操作1
        /*int temp=data.m;
        data.m=data.n;
        data.n=temp;*/

        //2.操作2
        ValueTransferTest3 test=new ValueTransferTest3();
        test.swap(data);
        System.out.println("交换后:m="+data.m+",n="+data.n);

    }
    public void swap(Data data){
        int temp=data.m;
        data.m=data.n;
        data.n=temp;
    }
}
class Data{
    int m;
    int n;
}

运行结果如下:
image.png

核心代码解析:
image.png

练习

(1)练习1

1.定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。

public class Circle {
    double radius;  //半径

    public double findArea(){
        return Math.PI*radius*radius;
    }
}
  1. 定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:
    public void printAreas(Circle c, int time)
  2. 在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
    例如,time为5,则输出半径1,2,3,4,5,以及对应的圆面积。
  3. 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示。
    image.png

代码展示:

public class PassObject {
    public static void main(String[] args) {
        PassObject test=new PassObject();
        Circle circle=new Circle();
        test.printAreas(circle,5);

        System.out.println("now radious is:"+circle.radius);
    }
 public void printAreas(Circle c, int time){
        System.out.println("Radious\t\tArea");
        int i = 1;
        for (; i <= time; i++) {
            c.radius=i;
            System.out.println(c.radius+"\t\t\t"+c.findArea());
        }
        c.radius=i;
    }
}

这里将circle作为实参传递给形参c的时候,它们指向的是堆空间的同一个对象,所以在printArea方法里面改变c的半径的时候,circle指向的对象里面的数据也会更改。

输出结果:
image.png

(2)练习2

针对下面MyArrays类的如下方法进行改进:数组排序,可以指明排序的方式(从小到大、从大到小)

package Object9;

/**
 * ClassName: MyArrays
 * Package: Object9
 * Description:
 *案例:
 *
 * 根据上一章数组中的常用算法操作,自定义一个操作int[]的工具类。
 * 涉及到的方法有:求最大值、最小值、总和、平均数、遍历数组、复制数组、数组反转、
 *              数组排序(默认从小到大排序)、查找等
 *
 * @Author 雨翼轻尘
 * @Create 2023/9/25 0025 12:02
 */
public class MyArrays {
    /**
     * 获取int[]数组的最大值
     * @param arr 要获取最大值的数组
     * @return 数组的最大值
     */
    public int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[0] > max) {
                max = arr[0];
            }
        }
        return max;
    }

    /**
     * 获取int[]数组的最小值
     * @param arr 要获取最小值的数组
     * @return  数组的最小值
     */
    public int gexMin(int[] arr){
        int min=arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(arr[0]<min){
                min=arr[0];
            }
        }
        return min;
    }

    /**
     * 获取int[]数组的和
     * @param arr   要求和的数组
     * @return  数组的和
     */
    public int getSum(int[] arr){
        int sum=0;
        for (int i = 0; i < arr.length; i++) {
            sum+=arr[i];
        }
        return sum;
    }

    /**
     * 获取int[]数组的平均数
     * @param arr   要求平均值的数组
     * @return  数组的平均值
     */
    public int getAvg(int[] arr){
        /* int sum=0,avg=0;
        for (int i = 0; i < arr.length; i++) {
            sum+=arr[i];
        }
        return sum/avg;*/
        return getSum(arr)/arr.length;
    }

    /**
     * 获取int[]数组的元素
     * @param arr   要遍历的数组
     */
    public void print(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
        }
        System.out.println();
    }

    //拷贝
    public int[] copy(int[] arr){
        int[] newArr=new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            newArr[i]=arr[i];
        }
        return newArr;
    }
    //反转
    public void reserve(int[] arr){
        for (int i = 0,j=arr.length-1; i <j ; i++,j--) {
            int temp=arr[i];
            arr[i]=arr[j];
            arr[j]=temp;
        }
    }
    //排序
    public void sort(int[] arr){
        for(int i=0;i<arr.length-1;i++){
            for(int j=0;j<arr.length-1-i;j++){
                if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                }
            }
        }
    }



    /**
     * 线性查找查找指定元素
     * @param arr   待查找的数组
     * @param target    要查找的元素
     * @return  target元素在arr数组中的索引位置,若未找到,则返回-1
     */
    public int LinearSearch(int[] arr,int target){
        for (int i = 0; i < arr.length; i++) {
            if(target==arr[i]){
                return i;
            }
        }
        //没有找到
        return -1;
    }

}

排序代码如下:

 //排序
 public void sort(int[] arr){
        for(int i=0;i<arr.length-1;i++){
            for(int j=0;j<arr.length-1-i;j++){
                if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                }
            }
        }
    }

改进之后:

/**
 * 针对于数组进行排序操作
 * @param arr   待排序的数组
 * @param sortMethod    asc:升序   desc:降序
 */
public void sort(int[] arr,String sortMethod){
    if(sortMethod.equals("asc")){   //ascend:升序
        for(int i=0;i<arr.length-1;i++){
            for(int j=0;j<arr.length-1-i;j++){
                if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                }
            }
        }
    }else if(sortMethod.equals("desc")){
        for(int i=0;i<arr.length-1;i++){
            for(int j=0;j<arr.length-1-i;j++){
                if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                }
            }
        }
    }else{
        System.out.println("你输入的排序方式有误");
    }

}

现在健壮性不够好,比如在main方法里面调用:

arrs.sort(arr,null);

编译器会报错:(空指针异常)
image.png


可以在方法里面判断是不是null,也可以这样:

sortMethod.equals("asc")改为"asc".equals(sortMethod)

sortMethod.equals("desc")改为"desc".equals(sortMethod)

修改之后的代码如下:

/**
 * 针对于数组进行排序操作
 * @param arr   待排序的数组
 * @param sortMethod    asc:升序   desc:降序
 */

public void sort(int[] arr,String sortMethod){
    if("asc".equals(sortMethod)){   //ascend:升序
        for(int i=0;i<arr.length-1;i++){
            for(int j=0;j<arr.length-1-i;j++){
                if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                }
            }
        }
    }else if("desc".equals(sortMethod)){
        for(int i=0;i<arr.length-1;i++){
            for(int j=0;j<arr.length-1-i;j++){
                if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                }
            }
        }
    }else{
        System.out.println("你输入的排序方式有误");
    }

}

接下来再来试一下:

在main方法里面调用:

arrs.sort(arr,null);

输出结果:
image.png

if判断的时候,将字面量(“asc”、“desc”)写在前面,这样就会规避空指针的问题。


刚才写的代码,交换两个数据的值用了两次,可以将它抽取出来重新造一个方法。

if(arr[j]>arr[j+1]){
        int temp=arr[j];
        arr[j]=arr[j+1];
        arr[j+1]=temp;
}

上面抽取的代码怎么写呢?

这样写可以吗?

重新定义一个方法:

public void swap(int i,int j){
        int temp=i;
        i=j;
        j=temp;
}

然后做如下的改动:
image.png

现在整体代码长这样:

/**
     * 针对于数组进行排序操作
     * @param arr   待排序的数组
     * @param sortMethod    asc:升序   desc:降序
     */

    public void sort(int[] arr,String sortMethod){
        if("asc".equals(sortMethod)){   //ascend:升序
            for(int i=0;i<arr.length-1;i++){
                for(int j=0;j<arr.length-1-i;j++){
                    /*if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                    }*/
                    if(arr[j]>arr[j+1]){
                        swap(arr[j],arr[j+1]);
                    }
                }
            }
        }else if("desc".equals(sortMethod)){
            for(int i=0;i<arr.length-1;i++){
                for(int j=0;j<arr.length-1-i;j++){
                    /*if(arr[j]<arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                    }*/
                    if(arr[j]<arr[j+1]){
                        swap(arr[j],arr[j+1]);
                    }
                }
            }
        }else{
            System.out.println("你输入的排序方式有误");
        }

    }

    public void swap(int i,int j){
        int temp=i;
        i=j;
        j=temp;
    }

在主方法里面发现,并不成功:
image.png

这个原因和上面举例说过的数值传递一样:(回顾)
image.png

再看看刚才的代码:(精简)

m就是arr[j],n就是arr[j+1]。调用swap方法时,将arr[j]arr[j+1]这两个值赋值给swap方法里面新的变量i和j,i和j在swap方法里面一顿操作,结束之后swap方法栈帧弹出,而在sort方法里面的arr[j]arr[j+1]并没有变化。

 public void sort(int[] arr,String sortMethod){
        //...
     	if(arr[j]<arr[j+1]){
        	swap(arr[j],arr[j+1]);
		}
}

public void swap(int i,int j){
        int temp=i;
        i=j;
        j=temp;
}

⚡所以该怎么写呢?

以后遇到这种情况都得这样写:

想要交换某数组中两个元素,告诉我数组是谁,要交换的是哪个位置上的元素。

代码如下:

public void swap(int[]arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
}

将数组传递过去,其实就是将地址值传递过去了,swap里面的数组指向和sort里面数组指向一致,任何一方修改数据,另一方的数据也会修改。

swap方法代码前后修改对比:(重要!!)
image.png

image.png

最后整体代码:

 /**
     * 针对于数组进行排序操作
     * @param arr   待排序的数组
     * @param sortMethod    asc:升序   desc:降序
     */

    public void sort(int[] arr,String sortMethod){
        if("asc".equals(sortMethod)){   //ascend:升序
            for(int i=0;i<arr.length-1;i++){
                for(int j=0;j<arr.length-1-i;j++){
                    /*修改
                    if(arr[j]>arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                    }*/
                    /*错误
                    if(arr[j]>arr[j+1]){
                        swap(arr[j],arr[j+1]);
                    }*/
                    if(arr[j]>arr[j+1]){
                        swap(arr,j,j+1);
                    }
                }
            }
        }else if("desc".equals(sortMethod)){
            for(int i=0;i<arr.length-1;i++){
                for(int j=0;j<arr.length-1-i;j++){
                    /*修改
                    if(arr[j]<arr[j+1]){
                        int temp=arr[j];
                        arr[j]=arr[j+1];
                        arr[j+1]=temp;
                    }*/
                    /*错误
                    if(arr[j]<arr[j+1]){
                        swap(arr[j],arr[j+1]);
                    }*/
                    if(arr[j]<arr[j+1]){
                        swap(arr,j,j+1);
                    }
                }
            }
        }else{
            System.out.println("你输入的排序方式有误");
        }

    }

    //不可行
  /*  public void swap(int i,int j){
        int temp=i;
        i=j;
        j=temp;
    }*/

    public void swap(int[]arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }

测试结果也是对的:
image.png

(3)练习3

需要在method方法被调用之后,仅打印出a=100,b=200,请写出method方法中的代码。
image.png

public class Test{
    public static void main(String[] args){
        int a=10;
        int b=10;
        method(a,b);	//需要在method方法被调用之后,仅打印出a=100,b=200,请写出method方法中的代码
        System.out.println("a="+a);
        System.out.println("b="+b);
    }
}

提供以下几种做法:

public class Answer {
    //错误做法
    public static void method0(int a,int b){
        a *= 10;
        b *= 20;
    }

    //正确做法一:
    public static void method1(int a, int b) {
        // 在不改变原本题目的前提下,如何写这个函数才能在main函数中输出a=100,b=200?
        a = a * 10;
        b = b * 20;
        System.out.println(a);
        System.out.println(b);
        System.exit(0);//和上面错误做法一样直接改数值,改完之后直接输出,不给它机会出去继续执行Test类后边的语句了,直接退出
    }

    //正确做法二:
    public static void method2(int a, int b) {

        PrintStream ps = new PrintStream(System.out) {
            @Override
            public void println(String x) {

                if ("a=10".equals(x)) {
                    x = "a=100";
                } else if ("b=10".equals(x)) {
                    x = "b=200";
                }
                super.println(x);	//将println方法“重写”,若发现a是10,则输出100;若b是10,则输出200
            }
        };

        System.setOut(ps);

    }

}

四、递归方法

介绍

再谈方法之4:递归方法

  1. 何为递归方法?方法自己调用自己的现象就称为递归。

  2. 递归方法分类:直接递归、间接递归

  • 直接递归:方法自身调用自己。
public void methodA(){
    methodA();
}
  • 间接递归:可以理解为A()方法调用B()方法,B()方法调用C()方法,C()方法调用A()方法。
public static void A(){
	B();
}
public static void B(){
	C();
}
public static void C(){
	A();
}
  1. 使用说明:
  • 递归方法包含了一种隐式的循环
  • 递归方法会重复执行某段代码,但这种重复执行无须循环控制。
  • 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,停不下来,类似于死循环。最终发生栈内存溢出
    比如:
public class Recursion {
    public static void main(String[] args) {
        Recursion test=new Recursion();
        test.method1();
    }
    public void method1(){
        System.out.println("method()...");
        method1();     //自己调用自己:递归
    }
}

这样会存在问题:
image.png

会导致StackOverflowError(栈溢出):
image.png

注意:

  1. 递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多
    所以在使用递归时要慎重。
  2. 在要求高性能的情况下尽量避免使用递归,递归调用既花时间又耗内存。考虑使用循环迭代

举例

(1)举例1

计算1-100内自然数的总和
方法一、用循环的方式(getSum1方法)

public class Recursion {
    public static void main(String[] args) {
        Recursion test=new Recursion();
        System.out.println(test.getSum1(100));
    }
    //计算1-100内自然数的总和
    public int getSum1(int num){
        int sum=0;
        for (int i = 0; i <=num; i++) {
            sum+=i;
        }
        return sum;
    }
}

运行结果:
image.png

方法二、用递归的方式(getSum2方法)

分析:
image.png

public int getSum2(int num){
        if(num==1){
            return 1;
        }else{
            return getSum2(num-1)+num;
        }
}

若num等于2,返回getSum2(1)+2=1+2=3。

若num等于3,返回getSum2(2)+3=getSum2(1)+2+3=1+2+3=6。

整体代码:

public class Recursion {
    public static void main(String[] args) {
        Recursion test=new Recursion();
        System.out.println(test.getSum2(100));
    }

    //计算1-100内自然数的总和

    public int getSum2(int num){
        if(num==1){
            return 1;
        }else{
            return getSum2(num-1)+num;
        }
    }
}

执行结果:
image.png


再举个递归的简单例子:
image.png

(2)举例2

计算n!

和上一个例子一样。

public class Recursion {
    public static void main(String[] args) {
        Recursion test=new Recursion();
        System.out.println(test.getMul(5));
    }
    
    public int getMul(int n){
        if(n==1){
            return 1;
        }else{
            return getMul(n-1)*n;
        }
    }
}

运行结果:
image.png

分析图:
image.png

(3)举例3

在I/O流中,我们会说到File类,它的对象可以表示一个文件目录。那如何获取文件目录的大小呢?

下图是某个文件夹的大小:
image.png

计算指定文件目录大小,遍历指定的文件目录中所有的文件,删除指定的文件目录

有兴趣的可以自己试试。

(4)举例4

计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律: 1,1,2,3,5,8,13,21,34,55,… , 即从第三个数开始,一个数等于前两个数之和。假设f(n)代表斐波那契数列的第n个值,那么f(n)满足: f(n) = f(n-2) + f(n-1);

public class Recursion {
    public static void main(String[] args) {
        Recursion test=new Recursion();
        System.out.println(test.f(10));
    }
    //斐波那契数列
    //1 1  2  3  5  8...
    //f(n)=f(n-1)+f(n-2)
    public int f(int n){
        if(n==1){
            return 1;
        } else if (n==2) {
            return 1;
        }else{
            return f(n-1)+f(n-2);
        }
    }
}

输出结果:
image.png

练习

(1)练习1

👻题目:

已知一个数列:f(20) = 1,f(21) = 4,f(n+2) = 2*f(n+1)+f(n),

其中n是大于0的整数,求f(10)的值。

代码:

public class RecusionExer01 {
    public static void main(String[] args) {
        Recursion test=new Recursion();
        System.out.println(test.f(10));
    }
    public int f(int n){
        if(n==20){
            return 1;
        }else if(n==21){
            return 4;
        }else{
            return f(n+2)-2*f(n+1);
        }
    }
}

运行结果:
image.png

分析:

下面这种方式是正确的,计算的数值在往已知的f(20)和f(21)靠拢。
image.png

若是将已知的条件f(n+2) = 2*f(n+1)+f(n)改为f(n)=2*f(n-1)+f(n-2),则是错误的:

数值往小的地方跑,就是往不已知的方向跑,是算不出结果的。(可是我的编译器怎么能执行出来正确结果。。。)大家还是尽量别这样写。
image.png

(2)练习2

👻题目:

已知有一个数列:f(0) = 1,f(1) = 4,

f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。

代码:

public class RecusionExer02 {
    public static void main(String[] args) {
        RecusionExer02 test=new RecusionExer02();
        System.out.println(test.func(10));
    }
    public int func(int n){
        if(n==0){
            return 1;
        } else if (n==1) {
            return 4;
        }else{
            //错误的
            //return func(n+2)-2*func(n+1);

            //正确的
            return 2*func(n-1)+func(n-2);
        }
    }
}

执行结果:
image.png

(3)练习3

👻题目:不死神兔

用递归实现不死神兔:故事得从西元1202年说起,话说有一位意大利青年,名叫斐波那契(Fibonacci)。

在他的一部著作中提出了一个有趣的问题:假设一对刚出生的小兔一个月后就能长成大兔,

再过一个月就能生下一对小兔,并且此后每个月都生一对小兔,没有发生死亡,

问:现有一对刚出生的兔子2年后(24个月)会有多少对兔子?

image.png

分析:斐波那契数列 f(n) = f(n-2) + f(n-1);

月份 1 2 3 4 5

兔子对数 1 1 2 3 5

代码:

public class RabbitExer {
    public static void main(String[] args) {
        RabbitExer rabbit=new RabbitExer();
        System.out.println("兔子的对数为:"+rabbit.getRabbitNumber(24));
    }
    public int getRabbitNumber(int month){
        if(month==1){
            return 1;
        } else if (month==2) {
            return 1;
        }else{
            return getRabbitNumber(month-1)+getRabbitNumber(month-2);
        }
    }
}

执行结果:
image.png

(4)练习4

拓展:走台阶问题

假如有10阶楼梯,小朋友每次只能向上走1阶或者2阶,请问一共有多少种不同的走法呢?

分析:

阶数 1 2 3 4 。。。

走法 1 2 3 5 。。。

从n为3开始:

f(n) = f(n - 1) + f(n - 2) 【斐波那契数列】

代码:

public class test {
    public static void main(String[] args) {
        test k=new test();
        System.out.println("4阶台阶有"+k.walk(4)+"种走法");
    }
    public int walk(int m){
        if(m==1){
            return 1;
        } else if (m==2) {
            return 2;
        }else{
            return walk(m-1)+walk(m-2);
        }
    }
}

执行结果:
image.png

【奇妙的属性】随着数列的增加,斐波那契数列前一个数与后一个数的比值越来越逼近黄金分割的数值0.618。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1119840.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机组成原理 new07 真值和机器数 无符号整数 定点整数 定点小数 $\color{red}{Δ}$

文章目录 真值和机器数 无符号整数无符号整数的定义无符号整数的特征无符号整数的表示范围无符号整数的加法无符号数的减法 有符号整数(定点整数)有符号整数的定义原码原码的特点反码反码的特点补码补码的特点快速求解n位负数补码的方法为什么补码能够多表示一个范围(重点)变形…

java1.8新特性流

案例描述 今天跟着黑马程序员的视频&#xff0c;完成“瑞吉外卖”项目的菜品信息管理模块的时候&#xff0c;遇到了一个比较陌生的写法 用到了Java8的新特性 stream().map((item) -> {}).collect() List<DishDto> collect records.stream().map((item) -> {DishDt…

人工智能(5):深度学习简介

1 深度学习 —— 神经网络简介 深度学习&#xff08;Deep Learning&#xff09;&#xff08;也称为深度结构学习【Deep Structured Learning】、层次学习【Hierarchical Learning】或者是深度机器学习【Deep Machine Learning】&#xff09;是一类算法集合&#xff0c;是机器学…

MySQL表操作—存储

建表&#xff1a; mysql> create table sch( -> id int primary key, -> name varchar(50) not null, -> glass varchar(50) not null -> ); Query OK, 0 rows affected (0.01 sec) 插入数据&#xff1a; mysql> insert into sch (id,name,…

【微信小程序】6天精准入门(第5天:利用案例与后台的数据交互)附源码

一、什么是后台交互&#xff1f; 在小程序中&#xff0c;与后台交互指的是小程序前端与后台服务器之间的数据通信和请求处理过程。通过与后台交互&#xff0c;小程序能够获取服务器端的数据、上传用户数据、发送请求等。 小程序与后台交互可以实现数据的传输、用户认证、实时消…

Babylonjs学习笔记(一)——搭建基础场景

React typescript umi Babylonjs 搭建基础场景 yarn add --save babylonjs babylonjs-loaders 1、封装基础场景 import { Engine, Scene } from "babylonjs"; import { useEffect,useRef,FC } from "react"; import "./index.less"type Prop…

自用bat脚本,命令

redis配置环境变量后 关机脚本 redis-server --service-stop启动脚本 :: 注释 rem echo off cd /d d:\\Redis :: redis-cli :: shutdown :: exit :: netstat -ano |findstr "6639" :: taskkill /pid {pid} /F redis-server redis.windows.conf pausecmd中替代gr…

BFS专题8 中国象棋-马-无障碍

题目&#xff1a; 样例&#xff1a; 输入 3 3 2 1 输出 3 2 1 0 -1 4 3 2 1 思路&#xff1a; 单纯的BFS走一遍即可&#xff0c;只是方向坐标的移动变化&#xff0c;需要变化一下。 代码详解如下&#xff1a; #include <iostream> #include <vector> #include…

上次的那段代码后续

之前写了一篇文章&#xff0c;说是一个要修改一个代码&#xff0c;很多人评论说代码说得不清不楚&#xff0c;不过在评论说又解释了一波之后&#xff0c;大家至少对这个代码有理解了&#xff0c;至少知道这个代码是做什么事情了。 如果是你&#xff0c;会不会修改这段代码&…

数据结构初阶——时间复杂度

朋友们我们又见面了&#xff0c;今天我们来学习数据结构的时间复杂度&#xff0c;在讲数据结构之前&#xff0c;大家可能只知道我们学习的是数据结构&#xff0c;但是还是不知道数据结构的具体定义&#xff0c;其实就是在内存上的数据。然后我们就像通讯录一样对它进行增删查改…

Qt 目录操作(QDir 类)及展示系统文件实战 QFilelnfo 类介绍和获取文件属性项目实战

一、目录操作(QDir 类) QDir 类提供访问系统目录结构 QDir 类提供对目录结构及其内容的访问。QDir 用于操作路径名、访问有关路径和文件的信息以及操作底层文件系统。它还可以用于访问 Qt 的资源系统 Qt 使用“/”作为通用目录分隔符&#xff0c;与“/”在 URL 中用作路径分…

istio介绍(一)

1. 概念 1.1 虚拟服务 虚拟服务提供流量路由功能&#xff0c;它基于 Istio 和平台提供的基本的连通性和服务发现能力&#xff0c;让您配置如何在服务网格内将请求路由到服务 示例&#xff1a; apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata:nam…

高项.项目管理经验、理念、教训

一、项目管理的一些经验 管项目重在管理&#xff0c;而不是死抠无关紧要的技术细节等等。 真正的团队一定是11>2&#xff0c;要把重心放在凝聚团队协力&#xff0c;共同完成目标上。 项目的推进永远都是不确定性的&#xff0c;真正考验项目经理的是不断出现的需求变更和状…

vue重修之路由【上】

文章目录 单页应用程序: SPA - Single Page Application路由简介Vue Reouter简介VueRouter的使用&#xff08;52&#xff09;组件的存放目录问题组件分类存放目录 路由的封装抽离 单页应用程序: SPA - Single Page Application 单页面应用(SPA): 所有功能在 一个html页面 上 单…

常用的跨域解决方案有哪些?

在 Web 开发中,跨域是指在浏览器环境下,通过 JavaScript 代码从一个域名的网页去访问另一个域名的资源。由于同源策略的限制,跨域请求通常会被浏览器阻止,为了实现跨域访问,HTML5 提供了一些机制来解决这个问题。 以下是一些常用的跨域解决方案: 1:JSONP(JSON with P…

展馆导览系统之AR互动式导航与展品语音讲解应用

一、项目背景 随着科技的进步和人们对于文化、艺术、历史等方面需求的提升&#xff0c;展馆在人们的生活中扮演着越来越重要的角色。然而&#xff0c;传统的展馆导览方式&#xff0c;如纸质导览、人工讲解等&#xff0c;已无法满足参观者的多元化需求。为了提升参观者的体验&a…

​CUDA学习笔记(六)Warp解析

本篇博文转载于https://www.cnblogs.com/1024incn/tag/CUDA/&#xff0c;仅用于学习。 Warp 逻辑上&#xff0c;所有thread是并行的&#xff0c;但是&#xff0c;从硬件的角度来说&#xff0c;实际上并不是所有的thread能够在同一时刻执行&#xff0c;接下来我们将解释有关wa…

​CUDA学习笔记(五)GPU架构

本篇博文转载于https://www.cnblogs.com/1024incn/tag/CUDA/&#xff0c;仅用于学习。 GPU架构 SM&#xff08;Streaming Multiprocessors&#xff09;是GPU架构中非常重要的部分&#xff0c;GPU硬件的并行性就是由SM决定的。 以Fermi架构为例&#xff0c;其包含以下主要组成…

什么是SpringMVC?简单好理解!

1、SpringMVC是什么&#xff1f; SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级web框架&#xff0c;通过把Model&#xff0c;View&#xff0c;Controller分离&#xff0c;将web层进行职责解耦&#xff0c;把复杂的web应用分成逻辑清晰的几部分。简化开发&…

并发编程-线程池ThreadPoolExecutor底层原理分析(一)

问题&#xff1a; 线程池的核心线程数、最大线程数该如何设置&#xff1f; 线程池执行任务的具体流程是怎样的&#xff1f; 线程池的五种状态是如何流转的&#xff1f; 线程池中的线程是如何关闭的&#xff1f; 线程池为什么一定得是阻塞队列&#xff1f; 线程发生异常&…