Appearance
Method
Field
A class can contain multiple field . For example, we define two field for the Person class:
java
class Person {
public String name;
public int age;
}
However, directly exposing field
to the outside as public
may break encapsulation. For example, the code can be written like this:
java
Person jack = new Person();
jack.name = "Jack";
jack.age = -99;
Obviously, directly operating field
can easily cause logical confusion. In order to prevent external code from directly accessing field
, we can modify field
with private
to deny external access:
java
class Person {
private String name;
private int age;
}
Try the effect of private
modified field
:
java
// private field
public class Main {
public static void main(String[] args) {
Person jack = new Person();
jack.name = "Jack";
jack.age = 12;
}
}
class Person {
private String name;
private int age;
}
Is there a compilation error? After removing the assignment statement for accessing field , it can be compiled normally.
Change field from public
to private
. External code cannot access these field , so what is the use of defining these field ? How can I assign a value to it? How can I read its value?
So we need to use a method to allow external code to modify field indirectly:
java
public class Main {
public static void main(String[] args) {
Person jack = new Person();
jack.setName("jack"); // set name
jack.setAge(12); // set age
System.out.println(ming.getName() + ", " + ming.getAge());
}
}
class Person {
private String name;
private int age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
if (age < 0 || age > 100) {
throw new IllegalArgumentException("invalid age value");
}
this.age = age;
}
}
Although external code cannot directly modify private
fields, external code can call the methods setName()
and setAge()
to modify private
fields indirectly. Inside the method, we have the opportunity to check whether the parameters are correct.
For example, setAge()
will check the incoming parameters. If the parameters are out of range, an error will be reported directly. This way, there is no chance for external code to set age
to an unreasonable value.
setName()
method can also be checked. For example, null
and empty strings are not allowed to be passed in:
java
public void setName(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("invalid name");
}
this.name = name.strip(); // Remove leading and trailing spaces
}
Similarly, external code cannot read private
fields directly, but can obtain the value of private
fields indirectly through getName()
and getAge()
.
Therefore, by defining methods, a class can expose some operation interfaces to external code, while at the same time ensuring logical consistency internally.
The syntax for calling a method is variable.method(parameters);
A method call is a statement, so don't forget to add ;
at the end. For example: jack.setName("jack");
.
Define method
As can be seen from the above code, the syntax for defining a method is:
java
modifier returnType methodName(parameterList) {
method body;
return returnValue;
}
The method return value is implemented through the return
statement. If there is no return value, the return type is set to void
and return
can be omitted.
Private method
There are public
methods, and naturally there are private
methods. Like private
fields, private
methods do not allow external calls, so what is the use of defining private
methods?
The reason for defining private
methods is that internal methods can call private
methods. For example:
java
public class Main {
public static void main(String[] args) {
Person jack = new Person();
jack.setBirth(2008);
System.out.println(jack.getAge());
}
}
class Person {
private String name;
private int birth;
public void setBirth(int birth) {
this.birth = birth;
}
public int getAge() {
return calcAge(2019); // Call private method
}
private int calcAge(int currentYear) {
return currentYear - this.birth;
}
}
Observing the above code, calcAge()
is a private
method and cannot be called by external code. However, the internal method getAge()
can call it.
In addition, we also noticed that this Person
class only defines birth
field and not age
field. When obtaining age
, the method getAge()
returns a real-time calculated value, not a value stored in a certain field. This shows that the method can encapsulate the external interface of a class, and the caller does not need to know or care whether Person
instance has an age
field internally.
This variable
Inside a method, you can use an implicit variable this
, which always points to the current instance. Therefore, the fields of the current instance can be accessed through this.field
.
If there is no naming conflict, this
can be omitted. For example:
java
class Person {
private String name;
public String getName() {
return name; // Equivalent to this.name
}
}
However, if there are local variables and fields with the same name, then the local variables have higher priority and this
must be added:
java
class Person {
private String name;
public void setName(String name) {
this.name = name; // The previous this is indispensable. If it is missing, it becomes a local variable name.
}
}
Method parameters
Methods can contain 0 or any number of parameters. Method parameters are used to receive variable values passed to the method. When calling a method, parameters must be passed one by one in strict accordance with the definitions. For example:
java
class Person {
...
public void setNameAndAge(String name, int age) {
...
}
}
When calling this setNameAndAge()
method, there must be two parameters, and the first parameter must be String
and the second parameter must be int
:
java
Person jack = new Person();
jack.setNameAndAge("jack"); // Compilation error: wrong number of parameters
jack.setNameAndAge(12, "jack"); // Compilation error: wrong parameter type
Variable parameters
Variable parameters are defined with type...
, and variable parameters are equivalent to array types:
java
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
setNames()
above defines a variable parameter. When calling, you can write:
java
Group g = new Group();
g.setNames("a", "b", "c"); // Pass in 3 Strings
g.setNames("a", "b"); // Pass in 2 Strings
g.setNames("a"); // Pass in 1 String
g.setNames(); // Pass in 0 Strings
It is completely possible to rewrite the variable parameters as String[]
type:
java
class Group {
private String[] names;
public void setNames(String[] names) {
this.names = names;
}
}
However, the caller needs to construct String[]
first, which is more troublesome. For example:
java
Group g = new Group();
g.setNames(new String[] {"a", "b", "c"}); // Pass in 1 String[]
Another problem is that the caller can pass in null
:
java
Group g = new Group();
g.setNames(null);
Variable parameters can guarantee that null
cannot be passed in, because when 0 parameters are passed in, the actual value received is an empty array instead of null
.
Parameter binding
When the caller passes parameters to an instance method, the values passed during the call will be bound one by one according to the parameter position.
So what is parameter binding?
Let’s first observe the passing of a basic type parameter:
java
public class Main {
public static void main(String[] args) {
Person p = new Person();
int n = 15; // The value of n is 15
p.setAge(n); // Pass in the value of n
System.out.println(p.getAge()); // 15
n = 20; // Change the value of n to 20
System.out.println(p.getAge()); // 15 or 20?
}
}
class Person {
private int age;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
Running the code, we can see from the results that modifying the external local variable n
does not affect age
field of instance p
. The reason is that the parameters obtained by setAge()
method copy the value of n
. Therefore, p.age
and local variable n
do not affect each other. .
Conclusion: The passing of basic type parameters is a copy of the caller's value. Subsequent modifications made by both parties will not affect each other.
Let’s look at another example of passing reference parameters:
java
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "Homer", "Simpson" };
p.setName(fullname);
System.out.println(p.getName()); // "Homer Simpson"
fullname[0] = "Bart"; // The first element of the fullname array is modified to "Bart"
System.out.println(p.getName()); // "Homer Simpson" or "Bart Simpson"?
}
}
class Person {
private String[] name;
public String getName() {
return this.name[0] + " " + this.name[1];
}
public void setName(String[] name) {
this.name = name;
}
}
Notice that the argument to setName()
is now an array. First, the fullname
array is passed in, and then the contents of fullname
array are modified. It turns out that the field p.name
of instance p
has also been modified!
in conclusion
When passing reference type parameters, the caller's variables and the receiver's parameter variables point to the same object. Modifications to this object by either party will affect the other party (because it points to the same object).
With the above conclusion, let's look at another example:
java
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // Pass in the bob variable
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // Bob changed his name to Alice
System.out.println(p.getName()); // "Bob" or "Alice"?
}
}
class Person {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Don't doubt the mechanism of reference parameter binding. Try to explain why the above code outputs "Bob" twice.
Practise
Add getAge
/ setAge
methods to the Person class:
java
public class Main {
public static void main(String[] args) {
Person p = new Person();
p.setName("Bob");
p.setAge(12);
System.out.println(p.getAge());
}
}
class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Summary
- Methods allow external code to safely access instance fields;
- A method is a set of execution statements and can execute arbitrary logic;
- It returns when return is encountered inside the method, and void means that no value is returned (note that it is different from returning null);
- External code operates instances through public methods, and internal code can call private methods;
- Understand method parameter binding.