本文是《Java 入门指南》的第十八篇文章,介绍了 Java 的 方法。

方法

描述:

方法的本质是功能块,用于完成某个功能的语句块。

类可以视为一个整体,相当于一个人,而类中的方法就相当于人的行为。一个人拿出水杯,然后接水,最后喝掉。这一系列的动作,构成了喝水的这一个行为,相当于完成了喝水的这一个方法。

建议一个方法只完成一个功能,这样有利于进行扩展。

定义:


修饰符 返回值类型 方法名 (参数类型 参数名){

    方法体

    return 返回值;

}

以下是方法的组成部分:

  • 修饰符(可选):定义了方法的访问类型。
  • 返回值类型:定义了方法返回的参数类型。无返回值时,返回值类型是 void。有返回值时,返回值类型是返回的参数类型。
  • 方法名:定义了方法的名字。方法名首字母小写,遵循驼峰命名原则,比如说 drinkWater
  • 参数类型(可选):定义了传递给方法的参数的类型。当方法被调用时,可以向方法传递参数,可以通过不同的参数类型调用不同的方法。
  • 参数名(可选):定义了传递给方法的参数的名字。参数名改变,不会对方法有影响。
  • 方法体:完成某个功能的语句块。
  • 返回值:如果返回值类型不是 void,则返回值不能为空,并且返回值的类型和返回值类型相同。

其中,参数类型可分为两种:

  • 实参(实际参数):调用方法时,传递给方法的数据。
  • 形参(形式参数):调用方法时,传递给方法的参数。

实例:

示例代码:

package com.jianrry.test;

public class Test {

   public static void main(String[] args) {

       // 1 和 2 是实参(实际参数),实参的值发生变化,对结果会有影响。
       int maxValue=max(1,2);

       System.out.println(maxValue);

   }

   // num1 和 num2 是形参(形式参数),形参的名字发生变化,对结果无影响。
   public static int max(int num1,int num2){

       int result;

       if(num1>num2){

           result=num1;

       } else {

           result=num2;

       }

       return result;

   }

}

运行结果:

2

构造方法

描述:

构造方法是一个特殊的方法,方法名和类名相同,并且没有返回值。

当使用 new 关键字创建一个对象时,会调用一个构造方法,并给该对象赋值(初始值)。

定义:

构造方法需要满足以下的条件:

  1. 方法名和类名相同。
  2. 没有返回值。

实例:

示例代码:

package com.jianrry.oop;

public class Person {

    String name;

    int age;

    //无参构造方法
    public Person() {

    }

    //有参构造方法
    public Person(String name,int age) {

        this.name = name;

        this.age=age;

    }

}
package com.jianrry.oop;

public class Application {

    public static void main(String[] args) {

        //调用无参构造方法。
        Person p1=new Person();

        System.out.println("p1.name="+p1.name+", p1.age="+p1.age);

        //调用有参构造方法。
        Person p2=new Person("Jianrry",21);

        System.out.println("p2.name="+p2.name+", p2.age="+p2.age);

    }

}

运行结果:

p1.name=null, p1.age=0
p2.name=Jianrry, p2.age=21

注意事项:

  1. 无论构造方法是否存在,在编译的过程中都会自动生成一个无参构造方法。
  2. 如果存在有参构造方法,则必须在类中定义一个无参构造方法。

方法的调用

描述:

调用方法:对象名.方法名(参数类型 参数名);

如果方法前有 static 修饰词,则可以通过 方法名(参数类型 参数名); 进行调用。

根据方法是否有返回值,有两种调用方法。

有返回值时,方法调用通常被当作一个值。

   int maxValue=max(1,2);

无返回值时,方法调用通常被当作一条语句(println 方法的返回值类型是 void,println 方法无返回值)。

   System.out.println(maxValue);

实例:

示例代码:

package com.jianrry.test;

public class Test {

   public static void main(String[] args) {

       // 1 和 2 是实参(实际参数),实参的值发生变化,对结果会有影响。
       int maxValue=max(1,2);

       System.out.println(maxValue);

   }

   // num1 和 num2 是形参(形式参数),形参的名字发生变化,对结果无影响。
   public static int max(int num1,int num2){

       int result;

       if(num1>num2){

           result=num1;

       } else {

           result=num2;

       }

       return result;

   }

}

运行结果:

2

方法的重载

定义:

在一个类中,存在多个方法名相同、而参数列表不同的方法,这种情况被称为方法的重载。

方法的重载满足以下的条件:

  1. 方法名相同。
  2. 参数列表不同(参数个数不同、参数类型不同、参数顺序不同)。

其中返回值类型可以相同,也可以不相同,返回值类型不是判断方法的重载的条件。如果一个类中有2个方法,方法名相同,参数列表相同,而返回值类型不同,这2个方法不构成方法的重载。

实例:

示例代码:

package com.jianrry.test;

public class Test {

    public static void main(String[] args) {

        int sum1=add(1,2);

        System.out.println("sum1="+sum1);


        int sum2=add(1,2,3);

        System.out.println("sum2="+sum2);


        double sum3=add(1,2.0);

        System.out.println("sum3="+sum3);


        double sum4=add(1.0,2);

        System.out.println("sum4="+sum4);

    }

    public static int add(int a,int b){

        return a+b;

    }

    //参数个数不同
    public static int add(int a,int b,int c){

        return a+b+c;

    }

    //参数类型不同
    public static double add(int a,double b){

        return a+b;

    }

    //参数顺序不同
    public static double add(double b,int a){

        return a+b;

    }

}

运行结果:

sum1=3
sum2=6
sum3=3.0
sum4=3.0

注意事项:

//方法1
public static int add(int a,int b){...}

//方法2
public static int add(int b,int a){...}

ab 是形参(形式参数),参数名改变,也不会对方法有影响。

虽然参数顺序不同,但是这2个方法是等价的。

这2个方法可以视为同一个方法,不构成方法的重载。

方法的重写

定义:

子类重写父类的非私有的方法,这种情况被称为方法的重写。

方法的重写满足以下的条件:

  1. 方法名相同。
  2. 参数列表相同。
  3. 子类的方法访问权限更高。
  4. 子类的方法抛出的异常范围更小。

其中返回值类型可以相同,也可以不同,返回值类型不是判断方法的重写的条件。如果返回值类型不同,则子类的返回值类型必须是父类的返回值类型的派生类。

实例:

示例代码:

package com.jianrry.oop;

public class Animal {

    public void say(){

        System.out.println("Animal 在说悄悄话。");

    }

}
package com.jianrry.oop;

public class Cat extends Animal {

    public void say(){

        System.out.println("Cat 在说悄悄话。");

    }

}
package com.jianrry.oop;

public class Application {

    public static void main(String[] args) {

        Cat cat=new Cat();

        cat.say();

    }

}

运行结果:

Cat 在说悄悄话。

注意事项:

  1. 构造方法不能被重写。
  2. static 关键词修饰的方法不能被重写,但是能够被再次声明。
  3. final 关键词修饰的方法不能被重写。

值传递和引用传递

定义:

值传递:

在调用方法时,将实参(实际参数)复制一份,传递到方法中。

即使实参的副本发生了改变,也不会对实参有任何影响。

相当于将文本文档复制了一份,编辑了副本的内容,源文件的内容也不会改变。

引用传递:

在调用方法时,将实参(实际参数)的内存地址复制一份,传递到方法中。

实参的内容地址的副本 和 实参的内容地址 指向同一个实参,方法中的参数发生了改变,也会对实参有影响。

相当于为文本文档创建了一个快捷方式,打开快捷方式后,编辑了文件的内容,源文件的内容也会同时改变。

基本数据类型(byte、short、int、long、float、double、char、boolean)是值传递,而引用数据类型(数组、类、对象等)是引用传递。

String 是一个特例,String 虽然是引用数据类型,但是它是值传递。因为 String 的值是不可变的,修改 String 的值就相当于重新创建了一个对象,所以它是值传递。

实例:

示例代码(int):

package com.jianrry.test;

public class Test {

    public static void main(String[] args) {

        int num1=10;

        int num2=20;

        System.out.println("num1="+num1+", num2="+num2);


        // swap 方法中交换了 num1 和 num2 的值,main 方法中 num1 和 num2 的值并没有发生变化。
        swap(num1,num2);

        System.out.println("num1="+num1+", num2="+num2);

    }

    public static void swap(int a,int b){

        int temp=a;

        a=b;

        b=temp;

        System.out.println("a="+a+", b="+b);

    }

}

运行结果(int):

num1=10, num2=20
a=20, b=10
num1=10, num2=20

示例代码(数组):

package com.jianrry.test;

public class Test {

    public static void main(String[] args) {

        int[] arr={10,20,30};

        System.out.println("arr[0]="+arr[0]);

        
        //change 方法中修改了数组下标为0的元素的值,main 方法中数组下标为0的元素的值也同时改变。
        change(arr);

        System.out.println("arr[0]="+arr[0]);

    }

    public static void change(int[] array){

        array[0]=0;

        System.out.println("array[0]="+array[0]);

    }

}

运行结果(数组):

arr[0]=10
array[0]=0
arr[0]=0

示例代码(String):

package com.jianrry.test;

public class Test {

    public static void main(String[] args) {

        String str="Hello";

        System.out.println("str="+str);


        //change 方法中改变了 str 的值,main 方法中 str 的值并没有发生变化。
        change(str);

        System.out.println("str="+str);

    }

    public static void change(String a){

        a="Hello, World!";

        System.out.println("a="+a);

    }

}

运行结果(String):

str=Hello
a=Hello, World!
str=Hello

可变参数

定义:

参数类型 ... 参数名;

Java 方法的参数列表可以是可变的(不定项的),这是 Java 5 的新特性。

可变参数需要满足以下的条件:

  1. 指定的参数类型后加上 ...
  2. 必须是最后一个参数。

实例:

示例代码:

package com.jianrry.test;

public class Test {

    public static void main(String[] args) {

        printMax(10,20,30);

        printMax(new double[]{1,2.33,3.14});

    }

    public static void printMax(double ... number){

        if(number.length==0){

            System.out.println("无参数!");

            return;

        }

        double result=number[0];

        for(int i=1;i<number.length;i++){

            if(number[i]>result){

                result=number[i];

            }

        }

        System.out.println("最大值为:"+result);

    }

}

运行结果:

最大值为:30.0
最大值为:3.14

递归

描述:

程序不停地调用自己,直到满足一定条件后,不再调用自己,这一种行为被称为递归。

递归可以将一个复杂的大型问题,拆分为一个个相似的简单的小问题,最后逐一解决。

递归只需要少量的代码就可以解决问题,提供了代码的复用性,大大地减少了代码量。

递归需要满足以下的条件:

  1. 边界条件。
  2. 前进阶段。
  3. 返回阶级。

当方法不满足边界条件,继续前进,直到满足边界条件为止。满足边界条件之后,不再前进,返回运算得到的数据。返回数据之后,不再调用自己,结束递归。

实例:

示例数据(5的阶乘):

package com.jianrry.test;

public class Test {

    public static void main(String[] args) {

        //1! = 1
        //2! = 2*1 = 2*1!
        //3! = 3*2*1 = 3*2!
        //4! = 4*3*2*1 = 4*3!
        //5! = 5*4*3*2*1 = 5*4!
        //n! = n*(n-1)*...*1 = n*(n-1)!

        System.out.println(f(5));

    }

    public static int f(int n){

        //当 n = 1 时,1! = 1
        //n = 1 是递归的边界条件。
        //n = 1 时,不再前进。2! = 2*1! = 2,返回 2! 的值。
        //以此类推,直到返回 5! 的值为止,最后结束这个递归。
        if(n==1){

            return 1;

        } else {

            // 当 n != 1 时,n! = n*(n-1)!
            // 当 n != 1 时,继续前进,直到 n = 1 为止。
            return n*f(n-1);

        }

    }

}

运行结果(5的阶乘):

120

注意事项:

递归嵌套的层数多,会占用大量的内存,从而导致栈溢出。

如果嵌套的层数不多,可以使用递归,可以方便我们理解。

如果嵌套的层数过多,尽量不要使用递归,建议使用迭代。