概述
类(class)是构造对象的模板。由类构造对象的过程称为创建类的实例。
封装从形式上看,封装是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式。对象中的数据称为实例域,而操纵数据的过程称之为方法。对于每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态。而实现封装的关键就是绝对不能让类中的方法直接访问其他类的实例域,仅仅可以通过对象的方法来与对象的数据进行交互。
在Java中,所有的类都来源于一个叫做Object的父类,我们将在后面的篇幅中详细讲解这个类。
对象与对象变量
如果我们想要使用对象,就必须要先来构造一个对象,然后指定它的初始状态。然后,再对这个对象应用方法。
在Java中,我们使用构造器来构造新的实例,构造器是一种特殊的方法,用来构造并初始化对象,构造器的名字应该与类的名字相同,如果想要构造一个对象,需要在构造器前面加上new操作符,比如:
new Date()
这个表达式就是构造了一个对象,这个对象被初始化为当前的日期和时间。我们可以把这个对象作为参数,也可以立即调用这个对象的方法,比如:
System.out.println(new Date());
new Date().toString();
但是,我们如果希望构造的对象可以多次使用,这是就需要将这个对象存到一个变量当中:
Date birthday = new Date();
这里需要注意,对象和对象变量是不同的:
Date deadline; //deadline并没有指向任何一个对象
这里定义了一个对象变量deadline,它可以引用Date类型的变量,但是,变量deadline不是一个对象,也没有引用任何对象。
Date deadline;
String s = deadline.toString(); //这里会报一个编译错误
必须首先初始化变量,然后才能使用它,我们可以使用以下两种方式来初始化一个变量
//1.使用new操作符
Date deadline;
deadline = new Date();
//2.引用一个已经存在的对象
Date deadline;
Date birthday = new Date();
deadline = birthday;
一定要认识到:一个对象变量并没有实际包含一个对象,而是仅仅引用一个对象。对象变量的值就是引用的对象在堆中的地址。如下:
Date deadline = new Date();
这里分为两个部分,表达式new Date()
构造了一个Date类型的对象,它的值是它在堆中的地址值,然后将该值赋给了deadline。
可以显式地将对象变量设置为null,表明这个对象变量目前没有引用任何对象,但是需要注意,如果将一个方法应用到一个值为null的对象上,那么就会产生运行时错误,抛出NullPointerException。
自定义类
在Java中,最简单的类定义形式为:
class ClassName {
field1;
field2;
...
constructor1;
constructor2;
...
method1;
method2;
}
如下程序是一个简单的小例子:
import java.time.LocalDate;
public class EmployeeTest {
public static void main(String[] args) {
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
staff[0] = new Employee("Carl Cracker", 85000, 1988, 4, 15);
staff[0] = new Employee("Carl Cracker", 105000, 1989, 8, 15);
for (Employee employee : staff) {
employee.raiseSalary(5);
}
for (Employee employee : staff) {
System.out.println("name = " + employee.getName() + ", salary = " + employee.getSalary() + ", hireday = " + employee.getHireday());
}
}
}
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;
}
}
注意,在这个示例程序中包括两个类:Employee类和带有public 访问修饰符的EmployeeTest类,源文件名是EmployeeTest.java,这是因为文件名必须与public类的名字相匹配。在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。
接下来,在编译这段源代码的时候,编译器将在目录下创建两个类文件:EmployeeTest.class和Employee.class。
将程序中包含main方法的类名提供给字节码解释器,以便启动这个程序:
java EmployeeTest
字节码解释器开始运行EmployeeTest类的main方法中的代码,在这段代码中,先后构造了三个新Employee对象。
多个源文件的情况
在刚刚的例子中,一个源文件中包含了两个类,但是在日常的开发当中,我们通常习惯将每一个类存放到一个源文件中,如果我们将每个类都分别放到一个源文件中,刚刚的程序将可以有两种编译源程序的方法。一种是使用通配符调用 Java编译器:
javac Employee*.java
于是,所有与通配符匹配的源文件都将被编译成类文件,第二种方式如下:
javac EmployeeTest.java
这里我们并没有显式的编译Employee.java。但是,当Java编译器发现EmployeeTest.java使用了Employee类时会查找名为Employee.class的文件,如果没有找到这个文件,就会自动的搜索Employee.java,然后,对它进行编译。更重要的是:如果Employee.java版本较已有的Employee.class文件版本新,Java编译器就会自动地重新编译这个文件。
下节预告
下节我们来了解对象与类地奥秘~
公众号
扫码或微信搜索Vi的技术博客,关注公众号,不定期送书活动各种福利~