方法是java语言中非常基本的一个组成。
一个class可以包含多个field但是,直接把field用public暴露给外部可能会破坏封装性,使用private修饰field,可以拒绝外部访问。
class Person {
private String name;
private int age;
}
把field从public改成private,外部代码不能访问这些field,需要使用方法(method)来让外部代码可以间接修改field。
public class Main {
public static void main(String[] args) {
Person ming = new Person();
ming.setName("Xiao Ming"); // 设置name
ming.setAge(12); // 设置age
System.out.println(ming.getName() + ", " + ming.getAge());
}
}
class Person {
private String name;
private int age;
public String getName() {
return this.name;
}//this表示实例本身
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;
}
}
虽然外部代码不能直接修改private字段,但是,外部代码可以调用类中定义的方法setName()和setAge()来间接修改private字段。在方法内部,我们就有机会检查参数对不对。所以,一个类通过定义方法,就可以给外部代码暴露一些操作的接口,同时,内部自己保证逻辑一致性。
定义方法
外部代码通过public方法操作实例,内部代码可以调用private方法。
修饰符 方法返回类型 方法名(方法参数列表) {
若干方法语句;
return 方法返回值;
}
//返回类型void 无需return
private方法
相比于public方法,private方法和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;
public void setBirth(int birth) {
this.birth = birth;
}
public int getAge() {
return calcAge(2019); // 调用private方法
}
// private方法:
private int calcAge(int currentYear) {
return currentYear - this.birth;
}
}
这个Person类没有定义age字段,获取age时,通过方法getAge()返回的是一个实时计算的值,并非存储在某个字段的值。这说明方法可以封装一个类的对外接口,调用方不需要知道也不关心Person实例在内部到底有没有age字段。
this变量
在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。如果没有命名冲突,可以省略this,但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this:
class Person {
private String name;
public void setName(String name) {//局部变量 name
this.name = name; // 前面的this不可少,少了就变成局部变量name了
}
}
可变参数
可变参数用 类型... 定义,可变参数相当于数组类型:
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
也可以将可变参数改写为String[]类型,但调用方需要自己先构造String[],而且调用方可以传入null,
而可变参数可以保证无法传入null,因为传入0个参数时,接收到的实际值是一个空数组而不是null。
Group g = new Group();
g.setNames(new String[] {"Xiao Ming", "Xiao Hong", "Xiao Jun"});
// 传入1个String[]
Group g = new Group();
g.setNames(null);
//传入null
参数绑定
基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。
public class Main {
public static void main(String[] args) {
Person p = new Person();
int n = 15; // n的值为15
p.setAge(n); // 传入n的值
System.out.println(p.getAge()); // 15
n = 20; // n的值改为20
System.out.println(p.getAge()); // 15
}
}
class Person {
private int age;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方。
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // 传入bob变量
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // bob改名为Alice
System.out.println(p.getName()); // "Bob"
}
}
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("Xiao Ming", 15);
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}//构造方法
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
构造方法的名称就是类名,构造方法的参数没有限制,在方法内部,也可以编写任意语句。但是,和普通方法相比,构造方法没有返回值(也没有void),调用构造方法,必须用new操作符。如果一个类没有定义构造方法,编译器会自动生成一个默认构造方法,它没有参数,也没有执行语句,默认构造方法,初始化实例的时候:引用类型的字段默认是null,数值类型的字段用默认值,int类型默认值是0,布尔类型默认值是false。手动编写构造方法后,就不会在自动生成默认构造。
class Person {
public Person() {
}
}//编译器自动生成的默认构造方法
多构造方法
可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分。
class Person {
private String name;
private int age;
public Person(String name, int age) {//方法1
this.name = name;
this.age = age;
}
public Person(String name) {//方法2
this.name = name;
this.age = 12;
}
public Person() {//方法3
}
}
/*
如果调用new Person("Xiao Ming", 20);,会自动匹配到构造方法1
如果调用new Person("Xiao Ming");,会自动匹配到构造方法2
如果调用new Person();,会自动匹配到构造方法3
*/
一个构造方法可以调用其他构造方法,这样做的目的是便于代码复用。调用其他构造方法的语法是this(…)
class Person {
private String name;
private int age;
public Person(String name, int age) {//方法1
this.name = name;
this.age = age;
}
public Person(String name) {//方法2
this(name, 18); // 调用方法1
}
public Person() {//方法3
this("Unnamed"); // 调用方法2
}
}
方法重载
在一个类中,我们可以定义多个方法。如果有一系列方法,它们的功能都是类似的,只有参数有所不同,那么,可以把这一组方法名做成同名方法,也称为方法重载(Overload)。
注意:方法重载的返回值类型通常都是相同的。
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}
String类提供了多个重载方法indexOf(),可以查找子串:
int indexOf(int ch):根据字符的Unicode码查找;
int indexOf(String str):根据字符串查找;
int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;
int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。