类和对象(Class and Objects)
声明对象
class Image
{
// various memberdeclarations
}
Image image = newImage();
Image类并没有显示的去声明构造函数,Java会自动创建一个缺省的无参构造函数。但是如果只要声明了一个构造函数,Java就不会自动去创建无参构造函数。
一个构造函数可以依赖于另外一个构造函数,通过this指针来实现。
构造函数没有返回值。
Image()
{
System.out.println("Image()called");
}
Image(Stringfilename)
{
this(filename, null);
System.out.println("Image(Stringfilename) called");
}
局部变量不能和函数输入参数同名,因为两者作用域重叠。
类成员变量可以和函数输入参数同名,类成员变量可以通过this.field来与函数输入参数区分,如下例子:
String make;
String model;
int numDoors;
Car(String make,String model)
{
this(make, model, 4);
}
Car(String make,String model, int nDoors)
{
this.make = make;
this.model = model;
numDoors = nDoors;
}
当一个对象被创建时,未被显示初始化的变量会被赋值为0.
静态成员变量在所有对象实例里只有唯一一份拷贝,依赖于类而不是对象,所以被称为class fields,而一般的成员变量称为instance fields。静态成员变量通过类名而不是对象名来访问。
声明只读成员变量
当想声明一个成员变量为只读的,可以加final关键字。
final变量有两种类型,根据变量初始化的地方不一样而不同。当在构造函数里初始化时,这种变量称为blank final,因为它只有在构造函数被调用了才有一个真正的值。
如果想要一个真正的常量,并且对所有对象的都一致,则需要创建class field,并加上static,如下例子:
class Employee
{
final static intRETIREMENT_AGE = 65;
}
类方法
当需要能将对象的几个方法级联使用时,需要用到this指针,如下例子:
public classSavingsAccount
{
int balance;
SavingsAccountdeposit(int amount)
{
balance += amount;
return this;
}
SavingsAccountprintBalance()
{
System.out.println(balance);
return this;
}
public static voidmain(String[] args)
{
newSavingsAccount().deposit(1000).printBalance();
}
}
当想不创建一个对象而给外界提供一个方法时,通过创建static的成员函数来实现,这种成员函数称为class methods。 class method不能访问instance field和instance method,只能访问class field和class method 。
Java传递参数给函数都是传值(pass by value),对于原子类型,是传递一个副本,对于对象类型,是传递引用本身。例如:
Employee emp = newEmployee("John ");
intrecommendedAnnualSalaryIncrease = 1000;
printReport(emp,recommendAnnualSalaryIncrease);
printReport(newEmployee("Cuifen"), 1500);
因此在 printReport 函数里,不能把别的 Employee 引用赋值给 emp 。
声明变参类型的函数为如下方法:
double sum(double. .. values)
{
int total = 0;
for (int i = 0; i< values.length; i++)
total += values[i];
return total;
}
Overload方法
函数名称相同,但是输入参数不同的方法,称为方法重载。不能仅有返回值类型不同的重载方法。
设计一个类时,目标是提供一个有用的接口给调用者使用,而屏蔽具体接口的实现。尽量不要让调用者直接访问对象的成员变量,这样可以仅改变对象成员函数的实现,而不需修改调用者的代码。
Java提供四种级别的访问控制:
声明为public的类必须被保存在同样命名的文件当中。
一个源代码文件里,最顶层的class仅能有一个声明为public。
Java也提供了一种方法可以访问另一个对象的private变量或方法:必须在这个类自身的上下文里才能直接访问同样类对象的private变量,如下例子:
public class PrivateAccess
{
private int x;
PrivateAccess(int x)
{
this.x = x;
}
boolean equalTo(PrivateAccess pa)
{
return pa.x == x;
}
public static void main(String[] args)
{
PrivateAccess pa1 = new PrivateAccess(10);
PrivateAccess pa2 = new PrivateAccess(20);
PrivateAccess pa3 = new PrivateAccess(10);
System.out.println("pa1 equal to pa2: " + pa1.equalTo(pa2));
System.out.println("pa2 equal to pa3: " + pa2.equalTo(pa3));
System.out.println("pa1 equal to pa3: " + pa1.equalTo(pa3));
System.out.println(pa2.x);
}
}
类和对象的初始化
类和对象的成员变量的初始化方法有以下几种:1:声明但不赋值,系统会自动初始化为0
2:声明且赋值
3:在构造函数里赋值
4:class initializers,在这个类导入系统时赋值
5:instance initializers,在这个对象创建时赋值
- class initializers是在类里以static开始并用{}包含起来的一段代码。会存在类的<clinit>()方法里,参考一下例子:
class C
{
static
{
System.out.println("class initializer 1");
}
static int counter = 1;
static
{
System.out.println("class initializer 2");
System.out.println("counter = " + counter);
}
}
以上class initializer和class field initializer依序执行。
- instance initializers
class Graphics
{
double[] sines;
double[] cosines;
{
sines = new double[360];
cosines = new double[sines.length];
for (int degree = 0; degree < sines.length; degree++)
{
sines[degree] = Math.sin(Math.toRadians(degree));
cosines[degree] = Math.cos(Math.toRadians(degree));
}
}
}
如果类没有构造函数,Java会创建一个缺省的<init>()方法来存储对象变量的初始化代码,如果有构造函数,则为每一个构造函数创建对应的 <init>()方法。
instance initializers一般很少使用。
以上各种初始化方法的顺序为:
- class field在类被导入后,值会被初始化为0或用户设定值。
- class initializers在<clinit>()返回前执行完。
- 接下来是intance field和instance initializer执行。
- 最后是构造函数执行。
在Android平台下实测下来,instance initializer和构造函数的执行顺序不固定,有可能是多线程执行了?