java方法
一个class可以包含多个field,例如:例如我们给Person类就定义了两个类field。
class Person { public String name; public int age; }
但是,直接把field用public暴露给外部可能会破坏属性的封装。可能会被重新定义,比如:
Person ming = Person(); ming.name = “ming”; ming.age = 18;
显然,直接操作field,容易造成逻辑混乱。为了避免外部代码直接去访问field,我们可以用private修饰field,拒绝外部访问:
class Person { private String name; private int age; }
示例:
package com.imooc.objectoriented; class Person { private String name; private int age; } public class ObjectMethod { public static void main(String[] args) { Person ming = new Person(); ming.name = "mingwang"; ming.age = 26; System.out.println(ming.name); } }
打印结果:
现在既然我们无法从外部读取对象的属性,那么我们该如何进行访问了?
我们可以在对象内部设置一些方法,这样就可以通过内部方法来访问对象的属性值。
package com.imooc.objectoriented; 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; } } public class ObjectMethod2 { public static void main(String[] args) { Person ming = new Person(); ming.setName("mingwang"); ming.setAge(18); System.out.println(ming.getName()); //mingwang System.out.println(ming.getAge()); //18 } }
这样我们就可以间接的查询和修改private字段。
还可以检查传入的参数,参数超出了范围,可以直接报错。
这样外部代码就没有任何机会把age设置成不合理的值。
当然我们还可以对参数进行检查:
public void setName(String name) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("invalid name"); } this.name = name; }
在python和go中,虽然没有public和private这两个字段,都有类似的处理方式。
在python中,类中属性的封装与Java基本类似,在go语言中,没有class,但是可以用方法来绑定对象。
定义方法
修饰符 方法返回类型 方法名(方法参数列表) { 若干方法语句 return 方法返回值; }
方法返回值通过return语句实现,如果没有返回值,返回类型设置为void,可以省略return;
private方法
public方法可以定义变量、方法和类。private也是可以,只是外部就无法访问了。
如果定义private无法从外部来访问,那么这种定义方式还有任何意义吗?
答案是否定的,存在即是必然,既然外部无法访问,那么就从内部进行访问。
示例如下:
package com.imooc.objectoriented; class PersonMan { private String name; private int birth; public void setBirth(int birth) { this.birth = birth; } //创建公开方法,返回年纪 public int getAge() { return calcAge(1992); } //创建私有方法,获取年纪 private int calcAge(int currentYear) { return this.birth - currentYear; } } public class ObjectPrivate { public static void main(String[] args) { PersonMan ming = new PersonMan(); ming.setBirth(2019); System.out.println(ming.getAge()); } }
由于对象PersonMan全部定义的都是private属性,因此是无法直接从外部赋值的。
在python中,由类创建对象的时候就会给属性赋值。
在此处,封装了一个类的对外接口,调用着不需要清楚PersonMan内部是否实现了age字段。
this变量
在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。
但是如果没有命名冲突,可以省略this:
class Person { private String name; public String getName() { return name; //相当于this.name } }
但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this。
class Person { private String name; public void setName(String name) { this.name = name; //前面的this不可少,少了就变成局部变量name了 } }
方法参数
方法可以包含0个或任意个参数。方法参数用于接收传递给方法的变量值。
调用方法时,必须严格按照参数的定义——传递。例如:
class Person { ... public void setNameAndAge(String name, int age) { ... } }
定义参数时,参数类型在前,参数名称在后。
在python中,变量属于动态定义,因此不需要注意参数类型与参数名称的顺序。
在go中,变量名称在前,变量类型在后。
Java与go在形式上面的最大差别,java类型在前,名称在后,Go名称在前,类型在后。
调用这个setNameAndAge()方法时,必须有两个参数,且第一个参数必须为String,第二个参数必须为int。
Person ming = new Person(); ming.setNameAndAge("ming"); //参数个数不对 ming.setNameAndAge(12,"ming") //参数类型不对
可变参数
可变参数用类型...定义,可变参数相当于数组类型(与python中,args*/kwargs**类似):
class Group { private String[] names; public void setNames(String... names) { this.names = names } }
上面的setNames()就定义了可变参数。调用时,可以这么写:
Group g = new Group(); g.setNames("kebi", "maoxian", "ming") //传入三个参数 g.setNames("kebi", "ming") //传入两个参数
在python中,也有类似功能,称为参数的处理与逆过程。
在Go语言中,也有类似变参的传参方法,比如func test(s string, a ...int)
貌似这个也没什么用,好像可以使用String[]来替换String...:
class Group { private String[] names; public void setNames(String[] names) { this.names = names; } }
这里setNames需要传入一个数组,而不是一个一个的参数。
这样你在传参的时候,需要先构建String[],比较麻烦。
Group g = new Group(); g.setNmaes(new String[] {"ming","kebi","song"}) //需要传入一个String[]
还有一个需要注意的问题,在使用可变参数的时候可以保证无法传入null,
因为传入0个参数时,接受到的实际值是一个空数组而不是null。
Group g = new Group(); g.setName(null);
参数绑定
将参数传递给实例方法时,调用时传递的值会按参数位置一一绑定。
package com.imooc.objectoriented; class PersonWoman { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public class ArgsBind { public static void main(String[] args) { int n = 18; PersonWoman fang = new PersonWoman(); fang.setAge(n); System.out.println(fang.getAge()); //18 n = 27; System.out.println(fang.getAge()); //18 } }
当我们改变n的值的时候,并不会影响对象的age属性。
原因是setAge方法获取的参数,复制了n的值,因此age和n的值互不影响。
因此基本类型参数的传递,是调用时传值的复制,双方各自的后续修改,互不影响。
但是上面的结论并不绝对。
package com.imooc.objectoriented; class ArgsPerson { private String[] names; public void setNames(String[] names) { this.names = names; } public String getNames() { return this.names[0] + " " + this.names[1] + " " + this.names[2]; } } public class ArgsBind2 { public static void main(String[] args){ ArgsPerson seven = new ArgsPerson(); String[] fullName = new String[] {"ming", "kai", "mao"}; seven.setNames(fullName); System.out.println(seven.getNames()); //ming kai mao fullName[0] = "xin"; System.out.println(seven.getNames()); //xin kai mao } }
当我们修改了fullname的值时,names的值也随之被改变了。
因此,引用类型参数的传递,调用方的变量和接收方的参数变量,指向的是同一个对象,
任意一方发生变化,都会影响对方。