目录

Java 方法与构造方法

1. 类的方法

类似 C/C++ 的函数

1.1 实例方法

类内部各个实例方法之间可以相互调用,也可直接读写类内变量。

实例方法可以调用静态变量和静态方法。

1.1.1 getter 与 setter

实例方法中,getter 与 setter 方法是类成员属性提供读取和修改的方法。

以下例子参考 5. 构造方法 中的无参构造方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Constructor {

    public static void main(String[] args) {
        Character character = new Character();
        character1.setName("MicroLOONG");
        character1.setAge(20);
        System.out.println(character.getName() + " " + character.getAge());
    }
}

class Character {

    private String name;
    private int age;

    /**
     * 获取名字
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置名字
     * @param name 名字
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取年龄
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置年龄
     * @param age 年龄
     */
    public void setAge(int age) {
        this.age = age;
    }
}

结果为:

1
MicroLOONG 20

以上例子中,类中属性定义为 private 并使用 get()set() 进行访问和修改,能利于统一控制和体现面向对象的封装性。

1.1.2 this 关键字

可见,上面的例子出现了一个关键字 this

this 关键字用来表示当前对象本身,或当前类的一个实例,通过 this 可以调用本对象的所有方法和属性。

创建对象后 JVM 会给这个对象分配一个引用自身的指针,即 this,因此 this 只能出现在实例方法中。

this 有以下用法:

① 区分同名变量

在上面例子中,出现了方法内部变量名和成员变量重名的情况,在方法内需要使用 this 访问类中的成员变量。

没有重名情况或无局部变量时 this 可省略。

② 作为方法名来初始化对象

this 可调用本类的其它构造方法,它必须作为构造方法的第一句。

以下例子参考 5. 构造方法 中的有参构造方法,结果相同。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Constructor {

    public static void main(String[] args) {
        Character character = new Character();
        System.out.println(character.getName() + " " + character.getAge());
    }
}

class Character {

    private String name;
    private int age;

    /**
     * 调用有参构造方法
     */
    public Character() {
    	this("MicroLOONG", 20);
    }

    /**
     * 有参构造方法
     * @param name 名字
     * @param age 年龄
     */
    public Character(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取名字
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 获取年龄
     * @return age
     */
    public int getAge() {
        return age;
    }
}
③ 作为参数传递

将当前对象的一个引用作为参数传递:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class This {

    public static void main(String[] args) {
        new A();
    }
}

class A {

    public A() {
        new B(this).print();
    }

    public void print() {
        System.out.println("From A");
    }
}

class B {

    A a;
    public B(A a) {
        this.a = a;
    }

    public void print() {
        a.print();
        System.out.println("From B");
    }
}

结果为:

1
2
From A
From B

类 A 中 new B(this) 把对象 A 自己作为参数传递给了对象 B 的构造函数,new B(this).print(); 使用了匿名对象调用了 print 方法。

注意
  • this() 必须在构造器的首行,且一个构造器中最多只能调用一个其他的构造器。

  • this 除了上述情况,在大部分情况可以省略。

  • 静态方法和静态的代码块中不能出现 this

1.2 静态方法 (类方法)

静态方法调用 非静态方法实例方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Circle {

    public static void main(String[] args) {
        Circle circle = new Circle();
        double area = circle.area(2);
        System.out.println("半径为2的圆面积是:" + area);
    }

    /**
     * 计算圆面积
     * @param r 半径
     * @return 面积
     */
    public double area(double r) {
        return 3.14 * r * r;
    }
}

注:静态方法不能访问非静态的成员变量。

或不实例化对象直接使用静态方法(静态方法调用静态方法):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Circle {

    public static void main(String[] args) {
        double area = area(2);
        System.out.println("半径为2的圆面积是:" + area);
    }

    /**
     * 计算圆面积
     * @param r 半径
     * @return 面积
     */
    public static double area(double r) {
        return 3.14 * r * r;
    }
}

2. 方法的重载

多个同名的方法在一个类中共存,要求这些方法的参数不同,如参数的个数或参数数据类型不一样,也可以是参数的顺序不一样。

重载可存在于同一个类或者继承关系中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Overload {

    public static void main(String[] args) {
        Overload ol = new Overload();

        // test1
        System.out.println(ol.test());
        // test2
        ol.test(1);
        // test3
        System.out.println(ol.test(1,"test3"));
        // test4
        System.out.println(ol.test("test4",1));
    }

    public int test() {
        System.out.println("test1");
        return 1;
    }

    public void test(int a) {
        System.out.println("test2");
    }

    public String test(int a, String s) {
        System.out.println("test3");
        return "return test3";
    }

    public String test(String s, int a) {
        System.out.println("test4");
        return "return test4";
    }
}

结果为:

1
2
3
4
5
6
7
test1
1
test2
test3
return test3
test4
return test4

3. 方法的可变参数

可变参数是指参数的长度可变,与方法的重载不同。

优点:有多个不同参数调用方法时更方便。

限制:一个方法最多只能有一个可变参数,可变参数必须是方法的最后一个参数。

形参格式: 类型... 参数名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Varargs {
    public static void main(String[] args) {
        System.out.println("普通数组 1+2+3 = " + sum1(new int[]{1,2,3}));
        // 可变参数,传递任意数量的参数
        System.out.println("无参数 sum = " + sum2());
        System.out.println("可变参数 1 = " + sum2(1));
        System.out.println("可变参数 1+2 = " + sum2(1, 2));
        System.out.println("可变参数 1+2+3 = " + sum2(1, 2, 3));
    }

    /**
     * 普通数组
     * @param arr 数组
     * @return sum
     */
    public static int sum1(int[] arr) {
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        return sum;
    }

    /**
     * 可变参数用法
     * @param arr 数组
     * @return sum
     */
    public static int sum2(int... arr) {
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        return sum;
    }
}

结果为:

1
2
3
4
5
普通数组 1+2+3 = 6
无参数 sum = 0
可变参数 1 = 1
可变参数 1+2 = 3
可变参数 1+2+3 = 6

int... arr 相当于 sum1 方法的 int[] arr

sum2 方法的调用相当于:

1
2
3
4
sum2(new int[0]);
sum2(new int[] {1});
sum2(new int[] {1, 2});
sum2(new int[] {1, 2, 3});

这个例子实现用同一个方法进行不同数的运算。

4. 参数绑定

调用方把参数传递给实例方法时,调用时传递的值会按参数的位置一一绑定

4.1 基本类型参数绑定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class paramBind {
    public static void main(String[] args) {
        Person p = new Person();
        int n = 15;
        p.setAge(n);
        n = 20;
        System.out.println(p.getAge());

        System.out.println(p.getAge());
    }
}

class Person {
    private int age;

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

结果为:

1
2
15
15

原因是 setAge() 方法获得的参数,复制了 n 的值。因此,p.age 和局部变量 n 互不影响。

结论:基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。

4.2 引用类型参数绑定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class paramBind {
    public static void main(String[] args) {
        Person p = new Person();
        String[] fullName = new String[] { "Steve", "Jobs" };
        p.setName(fullName);
        System.out.println(p.getName());
        fullName[0] = "Paul";
        System.out.println(p.getName());
    }
}

class Person {
    private String[] name;

    public String getName() {
        return this.name[0] + " " + this.name[1];
    }

    public void setName(String[] name) {
        this.name = name;
    }
}

结果为:

1
2
Steve Jobs
Paul Jobs

原因是这里修改了数组元素的内容,但数组变量指向的内存地址没有变。

结论:引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方。

另外一种情况:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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());
    }
}

class Person {
    private String name;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

结果为:

1
2
Bob
Bob

原因是 bob = "Alice"; 改变了 bob 的指向,但是 p.name 的指向并没有改变,与修改前指向的是相同的内存空间,没有改变先传入方法中的对象。

5. 构造方法

构造方法的作用:创建类的对象并初始化

  • 构造方法名称必须与类名相同。

  • 构造方法没有返回类型,void 也不能有。

  • 构造方法不能被继承,不能被覆写,不能被直接调用,可以被重载。

  • 类没定义构造方法时编译器会自动生成一个默认构造方法,但是如果自定义了一个构造方法,那么编译器就不再自动创建默认构造方法。

  • 构造方法可以私有,不能被 abstract、static、final 等修饰。

以下给出无参构造方法和有参构造方法的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class Constructor {

    public static void main(String[] args) {
        // 调用无参构造方法
        Character character1 = new Character();
        character1.setName("MicroLOONG");
        character1.setAge(20);
        System.out.println(character1.getName() + " " + character1.getAge());

        // 调用有参构造方法
        Character character2 = new Character("MicroLOONG", 20);
        System.out.println(character2.getName() + " " + character2.getAge());
    }
}

class Character {

    private String name;
    private int age;

    /**
     * 无参构造方法
     */
    public Character() {

    }

    /**
     * 有参构造方法
     * @param name 名字
     * @param age 年龄
     */
    public Character(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取名字
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置名字(有参构造方法非必要)
     * @param name 名字
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取年龄
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置年龄(有参构造方法非必要)
     * @param age 年龄
     */
    public void setAge(int age) {
        this.age = age;
    }
}

结果为:

1
2
MicroLOONG 20
MicroLOONG 20

这是一个无参和有参构造方法并存的例子。

其中,new Character(); 的调用匹配了无参构造方法 public Character()

new Character("MicroLOONG", 20); 的调用匹配了有参构造方法 public Character(String name, int age)

实际上两种方法可以相互独立,不过由于有参构造方法属于自定义构造方法,故默认构造方法 public Character() 不再自动创建或被覆盖,如果需要重载无参构造方法则必须自行给出默认构造方法。

无参构造方法需要使用 set() 方法访问类 Character 里的变量,并进行赋值;而有参构造方法直接在创建对象时使用构造器传递参数。