
class Employee
{
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
hireDay = LocalDate.of(year, month, day);
}
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;
salary += raise;
}
}
这个Employee类包含一个构造器和四个方法,类的所有方法都标记为public,这意味着任何方法都可以调用这些方法
Employee类的实例中有三个实例域用来存放将要操作的数据
private String name; private double salary; private LocalDate hireDay;
private确保了只有Employee类自身的方法能够访问这些实例域,而其他类的方法不能读写这些域
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
hireDay = LocalDate.of(year, month, day);
}
构造器的其他特性:
4.3.5 隐式参数与显式参数Java对象都是在堆中构造的,构造总是伴随着new操作符一起使用
double raise = number007.raiseSalary(5);
raiseSalary方法有两个参数:
在类中的方法中,关键词this表示隐式参数
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent / 100;
this.salary += raise;
}
4.3.6 封装的优点
可以确保实例域不被外界破坏
不要写返回可变对象的访问器方法,如果需要返回一个可变对象的引用,应该首先对它进行克隆(clone),对象clone是指存放在另一台位置上的对象副本
class Employee
{
...
public Date getHireDay()
{
return (Data) hireDay.clone();
}
}
4.3.7 基于类的访问权限
方法可以访问所调用对象的私有数据,并且一个方法可以访问所属类的所有对象的私有数据
class Employee
{
...
public boolean equals(Employee other)
{
return name.equals(other.name);
}
}
4.3.8 私有方法
final在每一个构造器执行之后,这个域的值被设置,并且在后面的操作中,不能够再对它进行修改
例如Employee类中的name域声明为final,因为在对象构建之后,这个值不会再被修改,机没有setName方法
class Employee
{
private final String name;
...
}
4.4 静态域与静态方法
main方法都被标记为static修饰符
4.4.1 静态域如果将域定义为static,每个类中只有一个这样的域。而每一个对象对于所有的实例域却都有自己的一份拷贝。
class Employee
{
private static int nextId = 1;
private int id;
...
public void setId()
{
id = nextId;
nextId++;
}
}
该类的每一个雇员对象都有一个自己的id域,但这个类的所有实例将共享一个nextId域
静态变量用的比较少,静态常量使用的比较多,可以设置为public
public class math
{
...
public static final double PI = 3.141592657;
}
4.4.3 静态方法
静态方法是一种不能向对象实施操作的方法,即没有隐式的参数
public static int getNextId()
{
return nextId;
}
Employee类的静态方法不能访问Id实例域,因为他不能操作对象,但是静态方法可以访问自身类中的静态域
可以使用对象调用静态方法,但是容易造成混淆,所以还是建议使用类名来调用静态方法
int n = Employee.getNextId();
Java程序设计语言总是采用按值调用
ParamTest.java
public class ParamTest {
public static void main(String[] args){
System.out.println("Testing triplevalue: ");
double percent = 10;
System.out.println("Before: percent = " + percent);
triplevalue(percent);
System.out.println("After: percent = " + percent);
System.out.println("nTesting tripleSalary:");
Employee harry = new Employee("Harry", 50000);
System.out.println("Before: salary = " + harry.getSalary());
tripleSalary(harry);
System.out.println("After: salary = " + harry.getSalary());
System.out.println("nTesting Swap:");
Employee a = new Employee("Alice", 70000);
Employee b = new Employee("Bob", 60000);
System.out.println("Before: a = " + a.getName());
System.out.println("Before: b = " + b.getName());
swap(a, b);
System.out.println("After: a = " + a.getName());
System.out.println("After: b = " + b.getName());
}
public static void triplevalue(double x)
{
x = 3 * x;
System.out.println("End of the method: x = " + x);
}
public static void tripleSalary(Employee x)
{
x.raiseSalary(200);
System.out.println("End of the method: salary = " + x.getSalary());
}
public static void swap(Employee x, Employee y)
{
Employee temp = x;
x = y;
y = temp;
System.out.println("End of method: x = " + x.getName());
System.out.println("End of method: y = " + y.getName());
}
}
class Employee
{
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
4.6 对象构造
4.6.1 重载
出现多个方法有相同的名字、不同的参数,便产生了重载
编译器根据各个方法给出的恩参数类型与特定方法调用说使用的值类型进行匹配来挑选出相应的方法,如果编译器找不到匹配的参数,就会产生编译错误,这个过程叫做重载解析(overloading resolution)
4.6.2 默认域初始化不只是构造器,Java允许重载任何方法,完整的描述一个方法,需要指出方法名以及参数类型,这叫做方法的签名(signature)
构造器没有显式地赋予初值,那么就会自动地赋为默认值,建议还是赋予初值,不然会出现getName方法等为null引用
LocalDate h = harry.getHireDay(); int year = h.getYear(); // throws exception if h is null4.6.3 无参数的构造器
可以使用this指示隐式参数
public Employee(String name, double salary)
{
this.name = name;
this.salary = salary;
}
4.6.6 调用另一个构造器
构造器的第一个语句形如this(...),这个构造器将调用同一个类的另一个构造器
public Employee(double s)
{
this("Employee #" + nextId, s);
nextId++;
}
采用这种方式使用this关键词非常有用,这样对公共的构造器代码部分只编写一次即可。
4.6.7 初始化块前面的两种初始化数据域的方法:
实际上,Java还有第三种机制,称为初始化块(initialization block)。在一个类的声明中,可以包含多个代码块,只要构造类的对象,这些块就会被执行
class Employee
{
private static int nextId;
private int id;
private String name = "";
private double salary;
static
{
Random generator = new Random();
nextId = generator.nextInt(10000);
}
{
id = nextId;
nextId++;
}
}
在这个示例中,无论使用哪个构造器构造对象,id域都在初始化块中被初始化。首先运行初始化块,然后才运行构造器的主体部分。
调用构造器的具体处理步骤:
如果对类的静态域进行初始化的代码比较复杂,那么可以使用静态的初始化块
static
{
Random generator = new Random();
nextId = generator.nextInt(10000);
}
在类第一次加载的时候,将会进行静态域的初始化
ConstructorTest.java
package chap4;
import java.util.*;
public class ConstructorTest{
public static void main(String[] args)
{
Employee1[] staff = new Employee1[3];
staff[0] = new Employee1("Harry", 4000);
staff[1] = new Employee1(60000);
staff[2] = new Employee1();
for(Employee1 e : staff)
System.out.println("name = " + e.getName() + ", id = " + e.getId() + ", salary = " + e.getSalary());
}
}
class Employee1
{
private static int nextId;
private int id;
private String name = "";
private double salary;
static
{
Random generator = new Random();
nextId = generator.nextInt(10000);
}
{
id = nextId;
nextId++;
}
public Employee1(String n, double s)
{
name = n;
salary = s;
}
public Employee1(double s)
{
this("Employee #" + nextId, s);
}
public Employee1()
{
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public int getId()
{
return id;
}
}
4.7 包
4.7.1 类的导入
可以使用两种方式访问另一个包中的公有类:
第一种方法是在每个类名之前添加完整的包名
java.time.LocalDate today = java.time.LocalDate.now();
简单常用的方式是使用import语句,import语句是一种引用包含在包中的类的简明描述
import java.util.*; ... LocalDate today = LocalDate.now();
如果类名相同,在每个类名的前面加上完整的包名
import java.sql.*; import java.util.Date; java.util.Date deadline = new java.util.Date(); jave.sql.Date today = new java.sql.Date();4.7.2 静态导入
import语句不仅可以导入类,还增加了导入静态方法和静态域的功能
import static java.lang.System.*;
out.println("Goodbye, world!");
exit(0);
4.7.3 将类放入包中如上,导入了System类的静态方法和静态域,而不必加类名前缀
要想将一个类放入包中,就必须将包的名字放在源文件的开头
package com.horstmann.corejava;
public class Employee
{
... ...
}
将包中的文件放到与完整的包名匹配的子目录中,例如上述代码放置在com/horstmann/corejava目录下
4.7.4 包作用域包树状结构基目录为classdir,jar文件路径在archives
设置类的路径可以使用-classpath或-cp选项指定类路径
// UNIX java -classpath classdir:.:archives // Windows java -classpath classdir;.;archives4.9 文档注释
在源代码添加专用的定界符开始的注释,就可以生成一个专业水准的文档,使用javadoc命令就可以完成了
4.9.2 类注释类注释必须放在import语句之后,类定义之前
class Employee
{
private String name;
private double salary;
...
}
4.9.3 方法注释
方法注释除了通用标记,还可以使用下面的标记
public double raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
return raise;
}
4.9.4 域注释
只需要对公有域(通常是指静态变量)建立文档
private static int nextId;4.9.5 通用注释
@author 可以使用多个@author标记
@version 对当前版本的描述
@since 对引入特性的版本描述
@deprecated 对类、方法或变量添加一个不再使用的注释
@see 超级链接
@see com.horstmann.corejava.Employee#raiseSalary(double)
@see Baidu main page
@see "Core Java 2 volume 2"
如果愿意的话,可以在注释中的任何文字指向其他类或方法的超级链接,以及插入一个专用的标记
{@link package.class#feature label}
切换到包含想要生成文档的源文件目录
如果是一个包,运行命令
javadoc -d docDirectory nameOfPackage
对于多个包生成文档
javadoc -d docDirectory nameOfPackage nameOfPackage2
如果文件在默认包中
javadoc -d docDirectory *.java