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的值也随之被改变了。
因此,引用类型参数的传递,调用方的变量和接收方的参数变量,指向的是同一个对象,
任意一方发生变化,都会影响对方。

 

posted @ 2020-03-15 20:38  明王不动心  阅读(258)  评论(0编辑  收藏  举报