一个class包含多个field,但是直接把field直接用public暴露给外部可能会破坏封装性,
class Person{public String name;public int age;}Person ming = new Person();ming.name = "xiao ming";ming.age = -99;
显然,直接操作field,容易造成逻辑混乱。为了避免外部代码直接去访问field,可以用private修饰field 拒绝外部访问
class Person{private String name;private int age;}public class Main{public static void main(Stringp[] args){Person ming = new Person();ming.name = "xiao ming";ming.age = 12;}}
实际上,上述代码会编译报错。
拒绝外部访问,那我们定义这些field有什么用?怎么才能给它赋值?怎么才能读取它的值?
所以我们需要使用方法(Method)来让外部代码可以间接的修改field
public class Main{
public static void main(String[] args){
}
}
class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
if(age < 0 || age >120){
throw new IllegalArgumentExeception("invalid age value");
}
this.age = age;
}
public int getAge(){
return this.age;
}
}
虽然外部代码不能直接修改private字段,但是可以通过调用getName()和setName()来设置或获取private字段。在方法内部,我们就有机会检查输入法是否合法,外部就没有机会把age设置成不合理的值。
对setName() 方法同样可以做检查,例如,不允许传入null和字符串
public void setName(String name) {
if(null == name || name.isBlank()) {
throw new IllegalArgumentException()"invalid name");
}
this.name = name.strip();
}
定义方法
从上面的代码可以看出,定义方法的语法是:
修饰符 方法返回类型 方法名(方法参数列表) {
方法语句..
return 方法返回值;
}
如果没有返回值,返回类型设置为void,可以省略return
private方法
定义private方法的理由是 内部方法可以调用private方法。
public class Main{
public static void main(String[] args){
Person ming = new Person();
ming.setBirth(2008);
System.out.println(ming.getAge());
}
}
class Person{
private String name;
private int birth;
private final int CURRENT_YEAR = 2021;
public void setBirth(int birth){
this.birth = birth;
}
public int getAge() {
return calcAge();
}
private int calcAge(){
return this.CURRENT_YEAR - this. birth;
}
}
此外,我们还注意到,这个Person类只定义了birth字段,并没有age字段。获取age时,通过方法getAge()返回的是一个实时计算的值,并非存储在某个字段的值。这说明方法可以封装一个类的对外接口,调用方不需要知道,也不关心Person实例内部到底有没有age字段。
this变量
在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。
如果没有命名冲突,可以省略this。
class Person {
private String name;
public String getName(){
return name;
}
}
但是,如果有局部变量和字段重名,那么局部变量优先级会更高,必须要加this
class Person{
private String name;
public void setName(String name) {
this.name = name;
}
}
方法参数
方法可以包含0个或任意个参数。方法参数用于接收传递给方法的变量值。调用方法时,必须严格按照参数的定义一一传递。
class Person{
....
public void setNameAndAge(String name, int age){
...
this.name = name;
this.age = age;
}
}
调用这个setNameAndAge时,必须要有两个参数,且第一个参数类型必须是String,第二个参数类型必须是int。
Person ming = new Person();
ming.setNameAndAge("xiao ming");//编译错误,参数个数不对
ming.setNameAndAge(12,"xiaoming");//编译错误,参数类型不对
可变参数
可变参数用类型...定义,可变参数相当于数组类型
class Group{
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames("Xiao Ming"); // 传入1个String
g.setNames(); // 传入0个String
可变参数可以保证无法传入null因为传入0个参数时, 实际上接收到的是一个空数组。
参数绑定
调用方把参数传递给实例方法时,调用时传递的值会按参数位置一一绑定。
那什么是参数绑定?
我们先观察一个基本类型参数的传递
public class Main{
public static void main(String[] args){
Person p = new Person();
int n = 15;
p.setAge(n);
System.out.println(p.getAge());// 15
n= 20;
System.out.println(p.getAge());//15还是20?
}
}
class Person{
private int age;
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
运行代码,从结果可知,修改外部的局部变量n,不影响实例p的age字段,原因是setAge()方法获得的参数,复制了n的值,因此,p.age和变量n互不影响。
结论:基本类型参数的传递,是调用方值的复制,双方各自的修改,互不影响。
我们再看一个传递引用参数的例子
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());
fullname[0] = "Bart";
System.out.println(p.getName()); //"Homer Simpson" or "Bart Simpson"?
}
}
class Person {
private String[] name;
public String[] getName(){
return this.name;
}
public void setName(String[] name) {
this.name = name;
}
}
结论:引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方。
有了上面的结论,我们再看一个例子
public class Main{
public static void main(String[] args){
Person p = new Person();
String bob = "bob";
p.setName(bob);
System.out.println(p.getName());
bob = "Alice";
System.out.println(p.getName());// bob 还是 alice?
}
class Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name =name;
}
}
}
试解释为什么上面的代码两次输出的都是"bob"
我们来复习一下字符串这一章
String 是引用类型,但是 String 不可变,给
bob重新赋值, 改变了 bob 的内存地址,指向了另一个对象.
小结:
- 方法可以让外部代码安全地访问实例字段
- 方法是一组执行语句,并且可以执行任意逻辑
- 方法内部遇到return时返回,void表示不返回任何值
- 外部代码通过public方法操作实例,内部代码可以调用 private方法
- 参数绑定
String 是引用类型,但是 String 不可变,给