Java类剖析
构造器
1、构造器与类同名;
2、每个类可以有一个以上的构造器;
3、构造器可以有0个、1个或者多个参数;
4、构造器没有返回值;
5、构造器总是伴随着new操作一起使用;
注释:java构造器和C++中构造函数的工作方式不同,java中构造器对象都是在堆中完成的,构造器总是伴随着new操作来一起使用。
Empoyee number2019("James",100000,1999,12,15); //C++构造器
Empoyee number2019=new Employee("James",100000,1999,12,15); //Java构造器
隐式参数和显式参数
对于一个方法:
public double raiseSalary(byPrecent)
{
double raise=salary*byPrecent/100;
salary+=raise;
}
隐式参数是指出现在方法名之前的类对象;
显式参数位于方法名后面括号内的数值。
C++注释:在C++中通常在类里面对成员函数进行声明,在类外面进行定义;如果在类的内部定义这个方法,那么这个方法将自动的成为内联(inline)函数。
在java中,所有的方法都必须在类的内部进行定义。
封装的优点
需要获得或者设置实例域的值,需要提供下面三项内容:
1、一个私有的数据域;
2、一个公有的域访问器方法;
3、一个公有的域更改器方法;
**注意:**不要编写一个返回引用可变对象的访问器方法。如果要返回一个可变对象的引用,首先应该对它进行克隆(clone)。
类的访问权限
私有方法
在java中,为实现一个私有的方法,只需将关键字public改成private即可。
对于私有方法,如果改用其它方法实现相应的操作,则不必保存原有的方法。如果数据的表达方式发生变化,这个方法可能变得难以实现,或者不再需要。然而,只要方法是私有的,类的设计者就可以确信:它不会被外部的其它类操作所调用,可以将其删去。如果方法是公有的,就不能将其删除,因为其他的代码可能依赖它。
final实例域
可以将实例域定义为final。构造对象时必须初始化这样的域。也就是说,必须确保在一个构造器执行之后,这个域的值被设置,并且在后面的操作中,不再能够对它进行修改。
private final String name;
静态域与静态方法
静态域
如果将域设置为static,每个类只能有一个这样的域。而每一个对象对于所有的实例域却都有自己的一份拷贝。
class Employee
{
private static int nextId=1;
...
private int id;
...
}
现在,每一个雇员对象都一个自己的id域,但这个类的所有实例将共享一个nextId域。换句话说,如果有1000个Employee类的对象,则有1000个实例域id。但是,只有一个静态的nextId。即使没有一个雇员对象,静态域nextId仍然存在。它属于类,不属于任何一个对象。
静态常量
public Math
{
...
public static final double PI=3.1415926538979323846;
...
}
**注意:**由于每个类对象都可以对公有域进行修改,所以最好不要将域设计为public。然而,共有常量却没有问题。因为out被声明为final,所以不允许再将其他打印流程赋给它。
静态方法
静态方法是一种不能向对象实施操作的方法。例:
Math.pow(x,a);
也可以认为,静态方法是没有this参数的方法。
下面两种情况使用静态方法:
一个方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如Math.pow)。
一个方法只需要访问类的静态域。
main方法
main方法不对任何对象进行操作。事实上,在启动程序时还没有任何一个对象。静态方法main将执行并创建程序所需要的对象。
方法参数
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一份拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。
方法参数总共有两种类型:
基本数据类型(数字,布尔值)
对象引用
public static void tripleSalary(Employee x)
{
x.raiseSalary(200);
}
当调用
harry=new Employee(...);
tripleSalary(harry);
可以看到,实现一个改变对象参数状态的方法并不是难事。理由很简单,方法得到的对象引用的拷贝,duixiang引用及其他的拷贝同时引用同一个对象。
java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。
总结:java中参数的使用情况:
一个方法不能修改基本数据类型的参数(数值或者布尔型)
一个方法可以改变对象参数的状态
一个方法不能让对象引用新的对象
对象构造
重载
**重载:**函数名相同,参数不同;
**重载解析:**编译器必须挑选出具体执行那个方法,他通过用各个方法给出的参数和特定方法调用所使用的值类型进行匹配来挑选出相应的方法。如果编译器找不到匹配的参数,就会产生编译错误,因为根本不存在匹配,或者没有一个比其他的更好。这个过程交重载解析。
默认初始化
必须明确的初始化方法中的局部变量。但是,如果没有初始化类中的域,将会被自动初始化为默认值(0、false或null)。
无参数的构造器
如果再编写一个类时没有构造器,则系统就不提供一个无参数的构造器。这个构造器将所有的实例域设置为默认值。于是,实例域中的数值型数据设置为0,布尔型设置为false、所有对象变量设置为null。
如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象时如果没有提供参数江被视为不合法。
显式域初始化
在执行构造器之前,先执行赋值操作。当一个类的所有构造器都希望把相同的值赋给某个特定的实例域时,这种方法特别有用。
初始值不一定是常量值。下面的例子中可以调用方法对域进行初始化。
class Employee
{
private static int nextId;
private int id=assignId();
...
private static int assignId()
{
int r=nextId;
nextId++;
return r;
}
...
}
C++注释在C++中,不能直接初始化类的实例域。所有的实例域在构造器中设置。但是,有一个特殊的初始化器列表语法。如下:
Employee::Employee(String n,double s,int y,int m,int d):name(n),salary(s),hireDay(y,m,d);
{
}
C++中使用这种特殊的语法来调用与构造器。在java中没有这种必要,因为对象没有子对象,只有指向其他对象的指针。
调用另一个构造器
如果构造器的第一个语句如this(…),这个构造器将调用同一个类的另一个构造器。下面是一个典型的例子:
public Employee(double s)
{
this("Employee #"+nextId,s);
nextId++;
}
当调用new Employee(6000)时,Employee(double s)构造器将调用Employee(String ,double )构造器。
C++注释在java中this等价于C++中的this指针。但是,在c++中,一个构造器不能调用另一个构造器。在C++中,必须将抽取的公共初始化代码编写成一个独立的方法。
初始化块
前面已经讲了两种初始化数据域的方法:
在构造器出初始化
在声明时赋值
实际上,java中还有第三种机制,称为初始化块,在一个类中可以包括多个初始化块。只要构造类的对象,这些块就会被执行。
class Employee
{
private static int nextId;
private int id;
private String name;
private double salary;
//初始化块
{
id=nextId;
nextId++;
}
public Employee(String n, double s)
{
name=n;
salary=s;
}
public Employee(String n, double s)
{
name="";
salary=0;
}
...
}
在这个实例中,无论使用那个构造器构造对象,id域都在对象初始化块中被初始化。首先运行初始化块,然后才运行构造器的主体部分。
初始化块的调用步骤:
1、所有数据域被初始化为默认值(0,false或null);
2、按照声明中出现的次序,依次执行所有的初始化语句或者初始化块
3、如果构造器的第一行调用了第二个构造器,则执行第二个构造器主体
4、执行这个构造器主体
对象析构与finalize方法
在C++中,有显式的析构函数,其中放置一些对象不再使用时需要执行的清理代码。在析构器中,最常见的操作就是回收分配给对象的存储空间。由于java中有自动的垃圾回收器,不需要人工进行回收,所以java不支持析构器。
包
使用包的主要原因是为了保证类名的唯一性。
将类放入包中
要想将一个类放入包中,就必须将包的名字放在源文件的开头,包中定义的类的代码之前。例:
package com.horstmann.corejava;
public class Employee
{
...
}
如果没有在源文件中放置package语句,这个源文件中的类就被放置在一个默认包中。默认包是一个没有名字的包。
注意
编译器在编译源文件时不检查目录结构。例如,假定有一个源文件开头有下列语句:
package com.mycompany;
即使这个源文件没有在子目录com/mycompany下,也可以进行编译。如果它不依赖于其他的包,就不会出现编译错误。但是,最终的程序将无法运行,除非先将所有类文件移到正确的位置上。如果包与目录不匹配,虚拟机就找不到类。
包的作用域
标记为public的部分可以被任意的类使用;标记为private的部分可以被定义它们的类使用。如果没有指定public或private,这个部分可以被同一包中的所有方法访问。
类设计技巧
1、一定要保证数据私有
2、一定要保证数据初始化
3、不要在类中使用过多的基本类型
4、不是所有的域都需要独立的域访问器和域更改器
5、将职责过多的类进行分解
6、类名和方法名要能体现它们的职责
7、优先使用不可改变的类