package 第3章;
import java.time.*;
public class 第3章a
{
public static void main(String[] args)
{
Employee[] man=new Employee[2];
man[0]=new Employee("hello",200000,2003,2,8);
man[1]=new Employee("well",300000,2005,1,29);
for(Employee a:man)
System.out.println("name is "+a.getName()+", salary is "+a.getSalary()+", hireTime is "+a.getHireTime());
for(Employee b:man)
{
b.increaseSalary(5);
}
for(Employee c:man)
System.out.println(c.getSalary());
}
}
class Employee
{
private double salary;
private String name;
private LocalDate hireTime;
public Employee(String n,double s,int year,int month,int day)
{
name=n;
salary=s;
hireTime=LocalDate.of(year, month, day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireTime()
{
return hireTime;
}
public void increaseSalary(double number)
{
double increase=salary*number/100;
salary+=increase;
}
}
下面对Employee类进行剖析。首先从这个类的方法开始。通过查看源代码会发现,这个类包含一个构造器和4个方法。
public Employee(String n,double s,int year,int month,int day);
public String getName();
public double getSalary()
public LocalDate getHireTime();
public void increaseSalary(double number);
这个类的所有方法都被标记为public。关键字public意味着任何类的任何方法都可以调用这些方法(共有4种访问级别)
接下来,需要注意在Employee类的实例中有三个实例域用来存放将要操作的数据:
private double salary;
private String name;
private LocalDate hireTime;
关键字private确保只有Employee类自身的方法能够访问这些实例域,而其他类的方法不能够读写这些域。
类=域+构造器+方法;
可以用public标记实例域,但这是一种极为不提倡的方法。public 数据与允许程序中的任何方法对其进行读取和修改。这就完全破坏了封装。任何类的任何方法都可以修改public域。从我们的经验看来,某些代码将使用这种存取权限,而这并不是我们希望的。因此,强烈建议将实例域标记为private
最后,请注意,有两个实例域本身就是对象:name域是String对象,hireDay域是LocalDate类对象。
?salary是不是double的实力对象?
这种情形十分常见:类通常包括属于某个类类型的实例域。 ?
从构造器开始
构造器:
public Employee(String n,double s,int year,int month,int day)
{
name=n;
salary=s;
hireTime=LocalDate.of(year, month, day);
}
可以看到,构造器与类同名。在构造Employee类的对象时,构造器会运行,以便将实例域初始化为所希望的状态。
例如:当使用下面这条代码创建Employee类实例时:
new Employee("James Bond",1000,1950,1,1);
将会把实例域设置为:
name="James Bond";
salary=1000;
hireDay=LocalDate.of(1950,1,1);
构造器与其他方法有一个重要的不同。构造器总是伴随着new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。例如:
james.Employee("James Bond",1000,1950,1,1) //编译错误
本章稍后还有更详细的介绍有关构造器的内容,现在需要记住:
1、构造器与类同名;
2、每个类有一个以上的构造器;
3、构造器可以有0个,1个,或者多个参数;
4、构造器没有返回值;
5、构造器总是伴随着new操作一起调用;
public Employee(String n,double s,int year,int month,int day)
{
name=n;
salary=s;
hireTime=LocalDate.of(year, month, day);
}
Employee a=new Employee("James Bond",1000,1950,1,1);
所有的Java对象都是在堆中构造的,构造器总是伴随着new操作符一起使用。
注意,不要在构造器中定义域与实例域重名的局部变量。例如,下面的构造器将无法设置salary
//实例域
private double salary;
private String name;
private LocalDate hireTime;
public Emplyee(String n , double s)
{
String name=n ;
double salary=s ;
}
这个构造器声明了局部变量name和salary。这些变量只能在构造器内部访问。
这些局部变量屏蔽了同名的实例域。
隐式参数与显式参数
方法用于操作对象以及存取它们的实例域。例如:方法raiseSalary:
public void raiseSalary(double byPercent)
{
double raise=salary*byPercent/100;
salary+=raise;
}
public double getSalary()
{
return salary;
}
将调用这个方法的对象的salary实例域设置为新值
private double salary;
for(Employee b:man)
{
b.increaseSalary(5);
}
for(Employee c:man)
System.out.println(c.getSalary());
下面的这个调用:
number007.raiseSalary(5);
它的结果将number007.salary的值增加5%。具体的说,这个调用将执行下列命令:
double raise=number007.salary*byPercent/100;
number007. salary+=raise;
raiseSalary方法有两个参数。第一个参数称为隐式(implicit)参数,时出现在方法名前的Employee类对象。第二个参数为与方法后面括号中的数值,这是一个显式(explicit)参数。(有人把隐式参数称为方法调用的目标或者接受者)。
可以看到,显示参数是明显的列在方法声明中的,例如double byPercent。隐式参数没有出现在方法声明中。
在每一个方法中,关键字this表示隐式参数。如果需要的话,可以用下列方式编写raiseSalary方法:
public void raiseSalary(double byPercent)
{
double raise=this.salary*byPercent/100;
this.salary+=raise;
}
//对比
public void increaseSalary(double number)
{
double increase=salary*number/100;
salary+=increase;
}
类的方法的调用者都有一个隐式的此类的实例作为调用对象:
Employee[] man=new Employee[2];
man[0]=new Employee("hello",200000,2003,2,8);
a.getName()
a.getSalary()
a.getHireTime()
b.increaseSalary(5);
有些程序员偏爱这样的风格,因为这样可以将实例域与局部变量声明明显的区分开。
increase=salary*number/100;
salary+=increase;
//对比:
double raise=this.salary*byPercent/100;
this.salary+=raise;
在java中,所有的方法都必须在类的内部定义,但并不代表他们是内联方法。是否将某个方法设置为内联方法是java虚拟机的任务。即时编译器会监事调用那些简洁、经常被调用,没有被重载以及可优化的方法。
封装的优点
简单的看一下下面的方法:
public String getName()
{
return this.name;
}
public double getSalary()
{
return this.salary;
}
public LocalDate getHireTime()
{
return this.hireTime;
}
这些都是典型的访问器方法。由于它们只返回实例域值,因此又称为域访问器。
将this.name,this.salary,this.hireDay域标记为public,以此来取代独立的访问器会不会更容易一些呢?
关键在于name是一个只读域。一旦在构造器中设置完毕,就没有任何一个办法对他进行修改,这样来确保name域不受到外界的破坏。
private double salary;
private String name; //String字符串型,为只读域,不可以修改
private LocalDate hireTime;
salary不是只读域(double不是只读),它可以用raiseSalary方法修改。
public void increaseSalary(double number)
{
double increase=salary*number/100;
salary+=increase;
}
特别是一旦这个值域出现了错误,只要调试这个方法就可以了。如果salary域是public的,破坏salary域值的因素就有很多。
private double salary;
public double salary;
在有些时候,需要获得或者设置实例域的值,因此,应该提供下面三项内容:
1、一个私有的数据域 private double salary;
2、一个公有的域访问器方法
//访问salary
public double getSalary()
{
return salary;
}
3、一个公有的域更改器方法:
//更改salary
public void increaseSalary(double number)
{
double increase=salary*number/100;
salary+=increase;
}
某实例域满足以上三点,则对这个实例域来说,就可以构造、访问、更改了。
这样做的好处如下:
首先,可以改变内部实现,除了该类的方法之外,不会影响其他代码。例如,如果将存储名字name的域改为:
String firstName;
String laseName;
那么getName方法可以改为返回
private String firstName;
private String laseName;
public String getName()
{
return firstName+“ ”+lastName;
}
//对比:
private String name;
public String getName()
{
return name;
}
这点,程序的其他部分完全不可见。