前言
上节我们学习了对象和对象变量的概念,以及它们之间的区别。与此同时,我们还知道了如何自定义一个属于我们自己的类,以及多个源文件的时候的编译机制。这节课我们来剖析一下我们上节课建造的这个类。
解读类
Employee类如下:
public class Employee {
//fields
private String name;
private double salary;
private LocalDate hireday;
//constructor
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
hireday = LocalDate.of(year, month, day);
}
//methods
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireday() {
return hireday;
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
}
}
对这个的方法进行剖析,我们会发现这个类有一个构造器和四个方法:
//构造器
public Employee(String n, double s, int year, int month, int day) {}
//方法
public String getName(){}
public double getSalary(){}
public localDate getHireDay(){}
public void raiseSalary(double byPercent){}
这些类的所有方法被标记为public,关键字public意味着任何类的任何方法都可以通过构造一个对象的方式来调用这些方法。
当然,在Employee类的实例中有三个实例域用来存放将要操作的数据:
private String name;
private double salary;
private LocalDate hireDay;
关键字private确保只有Employee类自身的方法能够访问这些实例域,而其他类的方法不能访问这些实例域,这确保了类与类之间的隔离性,它们之间的数据不会相互影响。在这需要注意一点,类通常包括类型属于另外某个类类型的实例域,比如name域是String类对象,hireDay是LocalDate类对象。
构造器
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
hireday = LocalDate.of(year, month, day);
}
可以看到,构造器必须与类名保持一致,在构造Employee类的对象时,构造器会运行,以便将实例域初始化为所希望的状态。这里需要注意一点,构造器与其他的方法有一个重要的不同,构造器总是伴随着new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。比如:
faker.Employee("Faker", 1000000000, 1997, 08, 25) //会报编译错误
后面我会单开一节详细讲解构造器,我们现在只需记住:
- 构造器与类同名
- 每个类可以有一个以上的构造器
- 构造器可以有0个,1个或多个参数
- 构造器没有返回值
- 构造器总是随着new操作一起调用
方法以及它的参数
方法用于操作对象以及存取它们的实例域,比如:
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
faker.raiseSalary(5)
方法调用的结果是把faker.salary域的值增加5%,实际上,它执行了以下的过程:
double raise = faker.salary * byPercent / 100;
faker.salary += raise;
由此可以看出,这个方法并不像我们想像的那样只有一个byPercent参数,它实际上还包括了一个隐式参数,是出现在方法名前的Employee对象,第二个参数是位于方法名后面括号中的数值。这是一个显式参数。
在每一个方法中,关键字this
表示隐式参数,比如刚刚那个方法可以写成如下形式:
public void raiseSalary(double byPercent) {
double raise = this.salary * byPercent / 100;
this.salary += raise;
}
域访问器和域修改器
//域访问器
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireday() {
return hireday;
}
//域修改器
public void setName(String name) {
this.name = name;
}
public void setSalary(double salary) {
this.salary = salary;
}
public void setHireday(int year, int month, int day) {
this.hireday = LocalDate.of(year, month, day);
}
我们为什么会需要这些方法呢?直接将实例域设置为public不是更好?这里就需要知道一点,类的实例域如果是public ,当我们在许多地方构造了这个类的对象并对实例域进行修改,出现错误之后,你将无法找到是哪个方法对他进行了修改,比如,上例中的salary,它只能通过raiseSalary()
进行修改,一旦出错,我们只需要去调试这个方法即可。
如果想要获得或者设置实例域的值,应该提供以下三个内容:
- 私有的数据域
- 公有的域访问器(get方法)
- 公有的域修改器(set方法)
这样看起来要比提供一个公有数据域要复杂,但是却有其他的好处,利大于弊:
- 可以改变内部实现,除了该类的其他方法以外,不会影响其他代码(安全性)
- 更改器可以进行错误检查,而直接对域赋值不能进行这样的操作
- 这样做,更符合面向对象的编程思想,每个对象的个体都是隐私且独立的。
final实例域
可以将实例域定义为final,构造对象的时候必须初始化这样的域,也就是说,必须确保在每一个构造器执行之后,这个域的值被设置,并且在后面的操作中,不能再对它进行修改。
final修饰符大都应用于基本类型域,或不可变类的域(比如String),对于可变的类,虽然可以使用final修饰符,但是我们需要注意一点,final指定的该域的地址值不会变,而这个对象本身可变,则不受影响,只是表示这个对象变量不会指向其他的对象。
//定义一个可变的实例域
private final StringBuilder sb;
//初始化
sb = new StringBuilder();
//更改
sb.append("Hello Java!")
下节预告
下节我们来了解Java中的静态(static)
公众号
扫码或微信搜索Vi的技术博客,关注公众号,不定期送书活动各种福利~