Class(二)
静态域和静态方法
静态域
public class Employee {
private static int nextId = 1;
private int id;
private static double name = 1;
}
其中nextId和name变量用static修饰,即成为静态域。这个类和这个类所有的对象共享同一个值,他在内存中只存在一份,严格来说在每个jvm只存在一份。即使类没有实例化,这个域也存在。这个域属于类,而不属于对象。
即每个Employee对象都有一个id的拷贝,他们的值各不相同,所有的Employee对象都共享nextId和name。
静态常量
使用关键字 static final修饰的为静态常量
public class Employee {
private static final int MONEY = 11;
}
静态常量可以用通过类访问,即Employee.MONEY访问。由于每个类对象都可以对公有域进行更改,所以最好不要讲域设计为public。但是用final修饰则无妨,final修饰则无法更改。
静态方法
静态方法是一种不能向对象实施操作的方法。例如Math类中的pow方法就是一个静态方法。表达式:Math.pow(x,a)
在运算时,不使用任何Math对象,即没有隐式参数(没有this参数)。
Employee类的静态方法不能访问Id实例域, 因为它不能操作对象。但是,静态方法可以访问自身类中的静态域。
public static int getNextld()
{
return nextld; // returns static field
}
可以通过类名.方法名访问,即Employee.getNextld()。
这个方法可以省略关键字 static吗? 答案是肯定的。但是, 需要通?过Employee类对象的 引用调用这个方法。
可以使用对象调用静态方法,但是不建议
在下面两种情况下使用静态方法:
- 方法不需要访问对象状态, 其所需参数都是通过显式参数提供(例如: Math.pow ) 。
- 方法只需要访问类的静态域(例如: Employee.getNextld)
工厂方法
使用静态工厂方法构造对象。例如:NumberFormat类使用如下工厂方法生成不同风格的格式化对象。
NumberFormat currencyFormatter = NumberFormat.getCurrencylnstance();
NumberFormat percentFormatter = NumberFormat.getPercentlnstance();
double x = 0.1; System.out.println(currencyFormatter.format(x)); // prints $0.10
System.out.println(percentFomatter.format(x)); // prints 10%
为什么 NumberFormat 类不利用构造器完成这些操作呢? 这主要有两个原因:
- 无法命名构造器。 构造器的名字必须与类名相同。但是, 这里希望将得到的货币实例和百分比实例采用不用的名字。
- 当使用构造器时, 无法改变所构造的对象类型。 而 Factory 方法将返回一个 DecimalFormat类对象, 这是 NumberFormat 的子类
main方法
main方法也是一个静态方法,其不对任何方法进行操作。事实上,程序启动时还没有任何一个对象。静态的main方法将执行并创建程序所需要的对象。
方法参数
Java总是按值传递参数的,方法得到的是所有参数值的一个拷贝, 特别是,方法不能修改传递给它的任何参数变量的内容。
此处争议颇多,我会单独拿出来详细学习一下。
下面总结一下 Java 中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数 (即数值型或布尔型)。
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象。
对象构造
重载
类有多个构造器,每个构造器名字相同,但是参数不同,这就是重载。当然重载不仅仅适用于构造器。要完整地描述一个方法,需要指出方法名和参数类型,这就叫做方法的签名。返回类型不是方法签名的一部分,即不能有着方法名,参数类型相同,但返回类型不同的两个方法。
默认域初始化
如果在构造器没有显式地给域赋初值,则会自动地赋默认值:数值为0,布尔值为false,对象引用为null。
这是域与局部变量主要的不同点,必须明确地初始化方法中的局部变量。
无参构造器
如果在编写一个类时没有编写构造器, 那么系统就会提供一个无参数构造器。 这个构造 器将所有的实例域设置为默认值。 于是, 实例域中的数值型数据设置为 0、 布尔型数据设置 为 false、 所有对象变量将设置为 nul。l
如果类中提供了至少一个构造器, 但是没有提供无参数的构造器, 则在构造对象时如果 没有提供参数就会被视为不合法。
调用另一个构造器
this关键字通常用于:参数变量用同样的名字将实例域屏蔽起来。实例如下:
public Employee(String name, double salary)
{
this.name = name;
this,salary = salary;
}
然而这个关键字还有另一个含义。
如果构造器的第一个语句形如 this(…), 这个构造器将调用同一个类的另一个构造器。 下 面是一个典型的例子:
public Employee(double s) {
// calls Employee(String, double)
this("Employee #" + nextld, s);
nextld++;
}
当调用 new Employee(60000) 时, Employee(double) 构造器将调用 Employee(String,double) 构造器。
采用这种方式使用 this 关键字非常有用, 这样对公共的构造器代码部分只编写一次 即可。
初始化块
前面已经讲过两种初始化数据域的方法:
- 在构造器中设置值
- 在声明中赋值
实际上, Java 还有第三种机制, 称为初始化块(initializationblock )。 在一个类的声明中, 可以包含多个代码块。只要构造类的对象, 这些块就会被执行。
class Employee
{
private static int nextld;
private int id;
private String name;
private double salary;
// object initialization block
{
id = nextld;
nextld++;
}
public Employee(String n, double s) {
name = n;
salary = s;
}
public Employee() {
name = "";
salary = 0;
}
在这个示例中, 无论使用哪个构造器构造对象,id 域都在对象初始化块中被初始化。首先运行初始化块,然后才运行构造器的主体部分。
即使在类的后面定义, 仍然可以在初始化块中设置域。但是, 为了避免循环定义, 不要读取在后面初始化的域。 因此建议将初 始化块放在域定义之后。
由于初始化域途径很多,所以总结一下调用构造器具体步骤:
- 所有数据域被初始化为默认值(0、 false 或 null )。
- 按照在类声明中出现的次序, 依次执行所有域初始化语句和初始化块。
- 如果构造器第一行调用了第二个构造器, 则执行第二个构造器主体
- 执行这个构造器的主体.
可以通过提供一个初始化值, 或者使用一个静态的初始化块来对静态域进行初始化。
- private static int nextld = 1;
- // static initialization block static
{
Random generator = new Random();
nextld = generator.nextlnt(10000);
}
在类第一次加载的时候, 将会进行静态域的初始化。与实例域一样, 除非将它们显式地设置成其他值, 否则默认的初始值是 0、 false 或 nul。l 所有的静态初始化语句以及静态初始 化块都将依照类定义的顺序执行。
导入
import 语句不仅可以导入类, 还增加了导入静态方法和静态域的功能。
包作用域
前面已经接触过访问修饰符 public 和 private。 标记为 public 的部分可以被任意的类使用;标记为private的部分只能被定义它们的类使用。如果没有指定public或private, 这个部分(类、 方法或变量)可以被同一个包中的所有方法访问。
类路径
后面单独学习