CodeGym /Java 博客 /随机的 /Java 中的方法
John Squirrels
第 41 级
San Francisco

Java 中的方法

已在 随机的 群组中发布
再一次问好!在上一课中,我们熟悉了类和构造函数,并学习了如何创建我们自己的类和构造函数。今天我们将更好地熟悉 Java 方法,它是类的重要组成部分。Java 中的方法是一组命令,允许您在程序中执行特定操作。换句话说,一个方法就是一个函数;你的班级能够做的事情。在其他编程语言中,方法通常被称为“函数”,但在 Java 中“方法”这个词更为常见。:) 如果你还记得,在上一节课中我们为Cat类创建了简单的方法,这样我们的猫就可以说喵叫和跳跃:

public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Meow!");
    }

    public void jump() {
        System.out.println("Pounce!");
    }

    public static void main(String[] args) {
        Cat smudge = new Cat();
        smudge.age = 3;
        smudge.name = "Smudge";

        smudge.sayMeow();
        smudge.jump();
    }
}
sayMeow()jump()是我们类的方法。运行这些方法会产生以下控制台输出:
Meow!
Pounce!
我们的方法非常简单:它们只是将文本输出到控制台。但在 Java 中,方法有一项重要任务:它们对对象的数据执行操作。它们更改对象的数据、转换它、显示它以及用它做其他事情。我们当前的方法不对Cat对象的数据做任何事情。让我们看一个更具说明性的例子:

public class Truck {

    int length;
    int width;
    int height;
    int weight;

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
例如,这里我们有一个代表卡车的类。半挂卡车有长度、宽度、高度和重量(我们稍后需要)。在getVolume()方法中,我们执行计算,将对象的数据转换为表示其体积的数字(我们将长度、宽度和高度相乘)。该数字将是该方法的结果。请注意,该方法的声明写为public int getVolume。这意味着此方法必须返回一个int。我们计算了方法的返回值,现在我们必须将它返回给调用我们方法的程序。要在 Java 中返回方法的结果,我们使用关键字 return。 返回量;

Java 方法参数

我们可以在调用方法时将称为“参数”的值传递给方法。一个方法的声明包括一个变量列表,它告诉我们该方法可以接受的变量的类型和顺序。该列表称为“方法参数”。我们的Truck类的getVolume()方法目前没有定义任何参数,所以让我们尝试扩展我们的卡车示例。创建一个名为BridgeOfficer的新类。这是在一座桥上执勤的警察,他检查所有经过的卡车,看它们的负载是否超过允许的重量。

public class BridgeOfficer {

    int maxWeight;

    public BridgeOfficer(int normalWeight) {
        this.maxWeight = normalWeight;
    }

    public boolean checkTruck(Truck truck) {
        if (truck.weight > maxWeight) {
            return false;
        } else {
            return true;
        }
    }
}
checkTruck方法接受一个参数,一个Truck对象,并确定官员是否允许卡车在桥上行驶在该方法内部,逻辑非常简单:如果卡车的重量超过允许的最大值,则该方法返回false。它必须找到另一条路:( 如果权重小于或等于最大值,它可以通过,并且该方法返回true. 如果您还不能完全理解“返回”或“方法返回一个值”这两个短语,让我们暂停一下编程,使用现实生活中的一个简单示例来考虑它们。:) 假设您生病了并在家休息了几天。你带着医生的证明去会计部门,因为病假应该是带薪的。如果我们将这种情况与方法进行比较,那么会计师有一个paySickLeave()方法。您将医生的证明作为参数传递给此方法(没有它,该方法将不起作用,您也不会得到报酬!)。然后使用你的笔记在方法内部进行必要的计算(会计师用它来计算公司应该付给你多少钱),并将你的工作结果(一笔钱)返回给你。我们的程序以类似的方式工作。它调用一个方法,将数据传递给它,并最终接收一个结果。这是我们的BridgeOfficer程序的main()方法:

public static void main(String[] args) {
    Truck first = new Truck();
    first.weight = 10000;
    Truck second = new Truck();
    second.weight = 20000;

    BridgeOfficer officer = new BridgeOfficer(15000);
    System.out.println("Truck 1! Can I go, officer?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("Truck 2! And can I?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
我们制造了两辆载重量分别为 10,000 和 20,000 的卡车。而军官工作的桥,最大承重为15000。该程序调用officer.checkTruck(first)方法。该方法计算所有内容,然后返回true,然后程序将其保存到布尔变量canFirstTruckGo中。现在你可以用它做任何你想做的事(就像你可以用会计师给你的钱一样)。在一天结束时,代码

boolean canFirstTruckGo = officer.checkTruck(first);
归结为

boolean canFirstTruckGo =  true;
这里有一个非常重要的点:return语句不只是返回方法的返回值,它还会停止方法运行!return语句之后的任何代码都不会被执行!

public boolean checkTruck(Truck truck) {

    if (truck.weight > maxWeight) {
        return false;
        System.out.println("Turn around, you're overweight!");
    } else {
        return true;
        System.out.println("Everything looks good, go ahead!");
    }
}
不会显示官员的评论,因为该方法已经返回结果并终止!程序返回调用方法的地方。您不必注意这一点:Java 编译器足够聪明,可以在您尝试在return语句之后编写代码时生成错误。

复仇者联盟:参数战争

在某些情况下,我们需要多种调用方法的方式。为什么不创造我们自己的人工智能?亚马逊有 Alexa,苹果有 Siri,那么我们为什么不应该有一个呢?:) 在电影钢铁侠中,托尼·斯塔克创造了他自己不可思议的人工智能贾维斯。让我们向这位了不起的人物致敬,并以他的名字命名我们的 AI。:) 我们需要做的第一件事是教 Jarvis 向进入房间的人打招呼(如果这样一个惊人的智慧竟然是不礼貌的,那将是奇怪的)。

public class Jarvis {

    public void sayHi(String name) {
        System.out.println("Good evening, " + name + ". How are you?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
控制台输出:
Good evening, Tony Stark. How are you?
非常好!贾维斯现在可以欢迎客人了。当然,更多时候是他的主人托尼·斯塔克。可万一他一个人不来呢!我们的sayHi()方法只接受一个参数。所以它只能迎接一个进入房间的人,而忽略另一个人。不太礼貌,你不同意吗?:/

Java 方法重载

在这种情况下,我们可以通过简单地编写 2 个同名但不同参数的方法来解决问题:

public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ". How are you?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
    }
}
这称为方法重载。方法重载使我们的程序更加灵活,并适应各种工作方式。让我们回顾一下它是如何工作的:

public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ". How are you?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Captain America");
    }
}
控制台输出:
Good evening, Tony Stark. How are you?
Good evening, Tony Stark and Captain America. How are you?
太好了,两个版本都有效。:) 但是我们没有解决问题!如果有三个客人怎么办?当然,我们可以再次重载sayHi()方法,以便它接受三个来宾姓名。但可能有 4 或 5 个。一直到无穷大。有没有更好的方法教 Jarvis 处理任意数量的名字,而不用重载sayHi()方法一百万次?:/ 当然有!如果没有,您认为 Java 会成为世界上最流行的编程语言吗?;)

public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ". How are you?");
    }
}
当 ( String... names ) 用作参数时,表示将向该方法传递一个 String 集合。我们不必事先指定会有多少,所以现在我们的方法更加灵活:

public class Jarvis {

    public void sayHi(String...names) {
        for (String name: names) {
            System.out.println("Good evening, " + name + ". How are you?");
        }
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
    }
}
控制台输出:
Good evening, Tony Stark. How are you?
Good evening, Captain America. How are you?
Good evening, Black Widow. How are you?
Good evening, Hulk. How are you?
这里的一些代码你会很陌生,但不要担心。它的核心很简单:该方法依次获取每个名字并问候每位客人!另外,它可以处理任意数量的传递字符串!两人、十人,甚至一千人——这种方法对任何数量的客人都适用。比为所有可能性重载方法更方便,你不觉得吗?:) 这是另一个重点:参数的顺序很重要!假设我们的方法接受一个字符串和一个数字:

public class Person {

    public static void sayYourAge(String greeting, int age) {
        System.out.println(greeting + " " + age);
    }

    public static void main(String[] args) {
        sayYourAge("My age is ", 33);
        sayYourAge(33, "My age is "); // Error!
    }
}
如果Person类的sayYourAge方法接受一个字符串和一个数字作为输入,那么程序必须按照特定的顺序传递它们!如果我们以不同的顺序传递它们,编译器将产生一个错误并且这个人将无法说出他的年龄。顺便说一句,我们在上一课中介绍的构造函数也是方法!您还可以重载它们(即创建多个具有不同参数集的构造函数)并且传递参数的顺序对它们来说也很重要。它们是真正的方法!:)

再次关于参数

是的,抱歉,我们还没有完成它们。:) 我们现在要研究的主题非常重要。有 90% 的机会你会在以后的每次面试中被问到这个问题!让我们谈谈将参数传递给方法。考虑一个简单的例子:

public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        currentYear = currentYear-10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2018;

        System.out.println("What year is it?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("How about now?");
        System.out.println(currentYear);
    }
}
时间机器有两种方法。它们都将代表当前年份的数字作为输入,然后增加或减少它的值(取决于我们是想回到过去还是未来)。但是,正如您从控制台输出中看到的那样,该方法不起作用!控制台输出:
What year is it?
2018
How about now?
2018
我们将currentYear变量传递给goToPast()方法,但它的值没有改变。我们在 2018 年,我们一直在这里。但为什么?:/ 因为 Java 中的原语是按值传递给方法的。这意味着什么?当我们调用goToPast()方法并将int变量currentYear (=2018)传递给它时,该方法不会获取currentYear变量本身,而是获取它的副本。当然,这个副本的值也是 2018,但是对副本的任何更改都不会以任何方式影响我们原来的currentYear变量!让我们的代码更明确一点,看看 currentYear 会发生什么:

public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        System.out.println("The goToPast method has started running!");
        System.out.println("currentYear inside the goToPast method (at the beginning) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("currentYear inside the goToPast method (at the end) = " + currentYear);
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2018;

        System.out.println("What was the year when the program started?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("And what year is it now?");
        System.out.println(currentYear);
    }
}
控制台输出:
What was the year when the program started?
2018
The goToPast method has started running!
currentYear inside the goToPast method (at the beginning) = 2018
currentYear inside the goToPast method (at the end) = 2008
And what year is it now?
2018
这清楚地表明传递给goToPast()方法的变量只是currentYear的一个副本。并且更改副本不会影响“原始”值。“按引用传递”的意思恰恰相反。让我们在猫身上练习吧!我的意思是,让我们使用 cat 示例看看按引用传递是什么样的。:)

public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
现在,借助我们的时间机器,我们将把世界上第一只时间旅行猫Smudge送入过去和未来!让我们修改TimeMachine类,使其与Cat对象一起工作;

public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }    
}
现在这些方法不只是改变传递的数字。相反,他们更改了特定的Catage字段。你会记得这对我们的基元不起作用,因为原始数字没有改变。让我们看看会发生什么!

public static void main(String[] args) {

    TimeMachine timeMachine = new TimeMachine();
    Cat smudge = new Cat(5);

    System.out.println("How old was Smudge when the program started?");
    System.out.println(smudge.age);

    timeMachine.goToFuture(smudge);
    System.out.println("How about now?");
    System.out.println(smudge.age);

    System.out.println("Holy smokes! Smudge has aged 10 years! Back up quickly!");
    timeMachine.goToPast(smudge);
    System.out.println("Did it work? Have we returned the cat to its original age?");
    System.out.println(smudge.age);
}
控制台输出:
How old was Smudge when the program started running?
5
How about now?
15
Holy smokes! Smudge has aged 10 years! Back up quickly!
Did it work? Have we returned the cat to its original age?
5
哇!现在这个方法做了一些不同的事情:我们的猫急剧老化,但随后又变得年轻了!:) 让我们试着找出原因。与使用原语的示例不同,当对象被传递给方法时,它们是通过引用传递的。对原始涂抹对象的引用已传递给changeAge()方法。因此,当我们在方法内部更改smudge.age时,我们引用的是存储对象的同一内存区域。它是对我们最初创建的同一个 Smudge 的引用。这就是所谓的“通过引用”!然而,并非所有带有参考文献的东西都那么容易。:) 让我们尝试改变我们的例子:

public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat = new Cat(cat.age);
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat = new Cat(cat.age);
        cat.age -= 10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        Cat smudge = new Cat(5);

        System.out.println("How old was Smudge when the program started?");
        System.out.println(smudge.age);

        timeMachine.goToFuture(smudge);
        System.out.println ("Smudge went to the future! Has his age changed?");
        System.out.println(smudge.age);

        System.out.println ("And if you try going back?");
        timeMachine.goToPast(smudge);
        System.out.println(smudge.age);
    }
}
控制台输出:
How old was Smudge when the program started running?
5
Smudge went to the future! Has his age changed?
5
And if you try going back?
5
又不行了!О_О让我们弄清楚发生了什么。:) 它与goToPast / goToFuture方法以及引用的工作方式有关。现在,请注意!这是理解引用和方法如何工作的最重要的事情。事实上,当我们调用goToFuture(Cat cat)方法时,传递的是对 cat 对象的引用的副本,而不是引用本身。因此,当我们将一个对象传递给一个方法时,有两个对该对象的引用。这对于了解正在发生的事情非常重要。这正是我们最后一个例子中猫的年龄没有改变的原因。在前面的示例中,当更改年龄时,我们只是将引用传递给 goToFuture ()方法,并用它来查找内存中的对象并更改其年龄(cat.age += 10)。但是现在,在goToFuture()方法中,我们正在创建一个新对象 ( cat = new Cat(cat.age) ),并且为该对象分配了传递给该方法的相同引用副本。因此:
  • 第一个引用(Cat smudge = new Cat (5))指向原始猫(5岁)
  • 之后,当我们将 cat 变量传递给goToPast()方法并为其分配一个新对象时,引用被复制。
这给我们带来了最终结果:指向两个不同对象的两个引用。但是我们只改变了其中一个的年龄(在方法内部创建的那个)。

cat.age += 10;
当然,在main()方法中,我们可以在控制台上看到猫的年龄smudge.age没有改变。毕竟,smudge是一个引用变量,它仍然指向年龄为 5 的旧的原始对象,而我们没有对该对象做任何事情。我们所有的年龄变化都是在新对象上进行的。 因此,事实证明对象是通过引用传递给方法的。永远不会自动创建对象的副本。如果将猫对象传递给方法并更改其年龄,则将更改其年龄。但是在赋值和/或调用方法时会复制引用变量!让我们在这里重复我们所说的关于传递原语的话:“当我们调用changeInt()方法并传递intvariable x (=15),该方法不会获取x变量本身,而是获取它的副本。因此,对副本所做的任何更改都不会影响我们的原始x您最终还是会不止一次地争论 Java 中参数的传递方式(即使是在有经验的开发人员之间)。但是,现在您确切地知道它是如何工作的。继续努力吧!:) 为了巩固您所学的知识,我们建议您观看我们的 Java 课程中的视频课程
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION