JavaRush /Java 博客 /Random-ZH /方法、它们的参数、交互和重载

方法、它们的参数、交互和重载

已在 Random-ZH 群组中发布
再一次问好!在上一课中,我们熟悉了类和构造函数,并学习了如何创建我们自己的类和构造函数。 方法、它们的参数、交互和重载 - 1今天我们将仔细研究类中不可或缺的一部分——方法。 方法是一组命令,允许您在程序中执行某些操作。换句话说,方法就是函数;你的班级可以做的事情。在其他编程语言中,方法通常被称为“函数”,但在 Java 中,“方法”这个词变得更流行 :) 在上一课中,如果你还记得的话,我们为 Cat 类创建了简单的方法,以便我们的猫可以喵喵叫并跳跃:
public class Cat {

    String name;
    int age;

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

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

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

        barsik.sayMeow();
        barsik.jump();
    }
}
sayMeow()jump()是我们班级的方法。他们的工作结果是输出到控制台:
Мяу!
Прыг-скок!
我们的方法非常简单:它们只是将文本打印到控制台。但在 Java 中,方法有一个主要任务 -它们必须对对象的数据执行操作。更改对象数据的值、对其进行转换、将其输出到控制台或对其执行其他操作。我们当前的方法不会对对象的数据执行任何操作Cat。让我们看一个更清楚的例子:
public class Truck {

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

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

方法参数

方法可以接受值作为输入,这些值称为“方法参数”。我们当前getVolume()类中的方法Truck不接受任何参数,因此让我们尝试用卡车来扩展示例。让我们创建一个新类 - 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。如果你还没有完全理解“return”、“method returns a value”这句话” - 让我们暂时放下编程,用现实生活中的一个简单例子来看看这个问题:) 假设您生病了,并且已经好几天没有上班了。你带着病假来到会计部门,你必须支付病假费用。如果用方法来比喻的话,会计师就有方法paySickLeave()(“带薪病假”)。您将病假证明作为参数传递给此方法(没有它,该方法将无法工作,您将不会获得任何报酬!)。在工作表方法内部,进行必要的计算(会计师用它来计算公司应该向您支付多少钱),并将工作结果返回给您 - 一笔钱。 该程序的工作方式相同。它调用一个方法,向那里传递数据,最后接收结果。main()这是我们程序的方法BridgeOfficer
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 number 1! May I pass, officer?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("Truck number 2! May I?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
我们正在创建两辆卡车,负载分别为 10,000 和 20,000。同时,人员执勤的桥梁的最大重量为 15,000。程序调用了 方法,该方法officer.checkTruck(first)计算了所有内容并将结果返回给程序 - true,程序将其保存在变量 中boolean 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, overweight!");
    } else {
        return true;
        System.out.println("Alright, move on!");
    }
}
军官所说的这句话不会输出到控制台,因为该方法已经返回了结果并完成了它的工作!程序返回到调用该方法的地方。您自己不必担心这一点 - 如果您尝试在return.

复仇者联盟:选择之战

在某些情况下,我们的程序需要多个选项来确定方法的工作方式。 我们为什么不创造自己的人工智能呢?Amazon 有 Alexa,Yandex 有 Alice,那为什么我们会更差呢?:) 在有关钢铁侠的电影中,托尼·斯塔克创造了自己杰出的人工智能 - JARVIS 让我们向这个精彩的角色致敬,并以他的名字命名我们的人工智能:)我们必须教贾维斯的第一件事是——向进入房间的人打招呼(如果如此伟大的智慧最终被证明是不礼貌的,那就太奇怪了)。
public class Jarvis {

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

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
控制台输出:
Добрый вечер, Тони Старк, How ваши дела?
伟大的!贾维斯知道如何迎接进来的人。当然,最常见的是他的主人——托尼·斯塔克。但他可能不会一个人来!我们的方法sayHi()只接受一个参数作为输入。因此,他只能向其中一位来者打招呼,而忽略另一位。不太有礼貌,同意吗?:/ 在这种情况下,为了解决这个问题,我们可以简单地在类中编写两个同名但参数不同的方法:
public class Jarvis {

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

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
    }
}
这称为方法重载。重载使我们的计划更加灵活并适应不同的工作选择。让我们检查一下它是如何工作的:
public class Jarvis {

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

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

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Captain America");
    }
}
控制台输出:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Тони Старк, Капитан Америка, How ваши дела?
太棒了,两种选择都有效:)但是,我们没有解决问题!如果有三个客人怎么办?当然,我们可以再次重载该方法sayHi()以接受三位客人的名字。但也可以有 4 或 5 个,以此类推,无止境。是否有另一种方法可以教 Jarvis 使用任意数量的名称,而不需要一百万个方法重载sayHi()?:/ 当然有!否则,Java 会成为世界上最流行的编程语言吗?;)
public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ", How are you doing?");
    }
}
作为参数传递的记录 ( String...names) 允许我们指示将一定数量的字符串传递给该方法。我们没有提前指定应该有多少个,因此我们方法的操作现在变得更加灵活:
public class Jarvis {

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

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
    }
}
控制台输出:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Капитан Америка, How ваши дела?
Добрый вечер, Черная Вдова, How ваши дела?
Добрый вечер, Халк, How ваши дела?
这里的一些代码对您来说不熟悉,但不要介意。它的本质很简单——该方法依次遍历所有的名字并向每一位客人打招呼!此外,它适用于任意数量的传输线路!两个、十个、甚至一千个——该方法对于任意数量的客人都可以可靠地工作。比对所有可能的选项进行重载要方便得多,您同意吗?:) 另一个重要的一点:参数的顺序很重要!假设我们的方法接受一个字符串和一个数字作为输入:
public class Man {

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

    public static void main(String[] args) {
        sayYourAge("My age - ", 33);
        sayYourAge(33, "My age - "); //error!
    }
}
如果sayYourAge类方法Man接受字符串和数字作为输入,那么这就是它们在程序中传递的顺序!如果我们以不同的顺序传递它们,编译器将抛出错误,并且该人将无法说出他的年龄。顺便说一句,我们在上一讲中介绍的构造函数也是方法!它们还可以重载(使用不同的参数集创建多个构造函数),并且对于它们来说,传递参数的顺序也非常重要。真正的方法!:)

再次关于参数

是的,是的,我们还没有完成它们:)我们现在要考虑的主题非常重要。 有 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 = 2020;

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

        timeMachine.goToPast(currentYear);
        System.out.println("And now?");
        System.out.println(currentYear);
    }
}
时间机器有两种方法。两者都将代表当前年份的数字作为输入,并增加或减少该值(取决于我们是想回到过去还是未来)。但是,从控制台输出可以看出,该方法不起作用!控制台输出:
Какой сейчас год?
2020
А сейчас?
2020
我们currentYear向方法传递了一个变量goToPast(),但它的值没有改变。2020年依然如此。但为什么?:/ 因为 Java 中的原语是按值传递给方法的。这是什么意思?当我们调用一个方法goToPast()并将变量传递到那里时int currentYear = 2020,进入该方法的不是变量本身currentYear,而是变量的副本。当然,这个副本的值也等于 2020,但是副本发生的所有更改都不会以任何方式影响我们的原始变量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!");
        System.out.println("The currentYear value inside the goToPast method (at the beginning) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("The currentYear value inside the goToPast method (at the end) = " + currentYear);
    }

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

        System.out.println("What is the year at the very beginning of the program?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("What year is it now?");
        System.out.println(currentYear);
    }
}
控制台输出:
Какой год в самом начале работы программы?
2020
Метод goToPast начал работу!
Значение currentYear внутри метода goToPast (в начале) = 2020
Значение currentYear внутри метода goToPast (в конце) = 2010
А сейчас Howой год?
2020
这清楚地表明传递给该方法的变量goToPast()只是一个副本currentYear。更改副本对“原件”的含义没有影响。“通过引用传递”具有完全相反的含义。让我们用猫来练习吧!我的意思是,让我们以猫为例看看通过链接是什么样子:)
public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
现在,在时间机器的帮助下,我们将把世界上第一个猫时间旅行者 Barsik 发射到过去和未来!让我们更改类,TimeMachine以便机器可以处理对象Cat
public class TimeMachine {

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

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }
}
方法现在不仅更改传递的数字,还更改age特定对象的字段Cat。就原语而言,正如您所记得的,我们没有成功:原始数字没有改变。让我们看看这里会发生什么!
public static void main(String[] args) {

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

    System.out.println("How old is Barsik at the very beginning of the program?");
    System.out.println(barsik.age);

    timeMachine.goToFuture(barsik);
    System.out.println("And now?");
    System.out.println(barsik.age);

    System.out.println("Firs-sticks! Barsik has aged 10 years! Drive back quickly!");
    timeMachine.goToPast(barsik);
    System.out.println("Did it work? Have we returned the cat to its original age?");
    System.out.println(barsik.age);
}
控制台输出:
Сколько лет Барсику в самом начале работы программы?
5
А теперь?
15
Елки-палки! Барсик постарел на 10 лет! Живо гони назад!
Получилось? Мы вернули коту его изначальный возраст?
5
哇!现在方法不同了:我们的猫突然变老了,然后看起来又年轻了!:) 让我们试着找出原因。与基元的示例不同,在对象的情况下,对对象的引用将传递给方法。对原始对象的引用被传递goToFuture(barsik)给方法。因此,当我们更改内部方法时,我们访问的是存储对象的内存区域。这是我们一开始创建的同一个 Barsik 的链接。这就是所谓的“通过引用传递”!然而,有了这些链接,一切就不那么简单了:)让我们尝试改变我们的例子: goToPast(barsik)barsikbarsik.age
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 barsik = new Cat(5);

        System.out.println("How old is Barsik at the very beginning of the program?");
        System.out.println(barsik.age);

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

        System.out.println("And if you try in the past?");
        timeMachine.goToPast(barsik);
        System.out.println(barsik.age);
    }
}
控制台输出:
Сколько лет Барсику в самом начале работы программы?
5
Барсик отправился в будущее! Его возраст изменился?
5
А если попробовать в прошлое?
5
又不行了!O_O 让我们弄清楚发生了什么:) 这都是关于链接如何工作的方法goToPast/goToFuture和机制。 现在注意!这一点对于理解链接和方法如何工作是最重要的。事实上,当我们调用一个方法时,goToFuture(Cat cat)传递给它的并不是对象引用本身cat,而是这个引用的副本。 也就是说,当我们将一个对象传递给方法时,有两个对该对象的引用。这对于理解正在发生的事情非常重要。毕竟,这就是为什么我们的最后一个例子没有改变猫的年龄。在前面更改年龄的示例中,我们只是在方法中获取传递的引用goToFuture(),使用它在内存中找到对象并更改其年龄(cat.age += 10)。现在在方法中goToFuture()我们创建一个新对象
(cat = new Cat(cat.age)),
并且传递给该方法的相同复制链接被分配给该对象。因此:
  • 第一个链接 ( Cat barsik = new Cat(5)) 指向原来的猫(年龄为 5)
  • 当我们将变量传递cat给方法goToPast(Cat cat)并将其分配给一个新对象后,引用就被复制了。
之后,我们就得到了最终的情况:两个链接指向两个不同的对象。但我们只更改了其中之一的年龄 - 我们在方法内创建的年龄。
cat.age += 10;
当然,当我们在方法中将其输出main()到控制台时barsik.age,我们看到它的年龄并没有改变。毕竟barsik,这是一个引用变量,它仍然指向年龄为 5 的旧的原始对象,而该对象没有发生任何事情。我们对年龄的所有操作都是在一个新对象上进行的。因此,事实证明对象是通过引用传递给方法的。对象的副本永远不会自动创建。如果您将猫对象传递给方法并更改其年龄,它将成功更改。但是引用变量的值在赋值和/或调用方法时会被复制!让我们在这里重复一下关于传递原语的段落:“当我们调用一个方法changeInt()并将变量传递到那里时int x = 15,进入该方法的不是变量本身x,而是它的副本。毕竟,副本中发生的所有更改都不会改变。”以任何方式影响我们的原始变量x。” 通过复制链接,一切都完全相同!您将 cat 对象传递给该方法。如果您对猫本身(即内存中的对象)执行某些操作,则所有更改都会成功完成 - 我们只有一个对象并且仍然拥有它。但是,如果在方法内部创建一个新对象并将其保存在引用变量(该变量是该方法的参数)中,那么从现在开始我们就有两个对象和两个引用变量。就这样!没那么容易,甚至可能要讲好几遍。但最主要的是你已经学会了这个超级重要的话题。您经常会遇到关于 Java 中如何传递参数的争论(即使是在经验丰富的开发人员之间)。现在您确切地知道它是如何工作的。继续努力吧!:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION