二. Java类

Java类与C++的类区别不大,这里主要说一些两者之间细微的差别,以及一些值得注意的地方。

一. 关于类和对象

1. 自定义类。 最基本的,Java中所有的方法都存放在类中,每个Java文件有且只有一个public类,该类与文件名相同,但可以有任意数目的非public类。要想创建一个完整的项目,往往会组合多个类,但只有一个类有main方法,这是项目的入口。

2. Java中所有的对象都存放在堆里,都是new出来的。 Java的对象变量可以看作是C++的对象指针(不是对象引用,因为Java的对象变量可以为null,但是C++的引用不能为null)

C++中,若指针未初始化,将会产生一个随机错误,但是Java中会产生一个运行时错误。

3. 对象与对象变量
对象是被new出来的,而对象变量是用来指向(或者说引用)对象变量的。

Date deadline; //创建一个对象变量,该对象变量可以引用一个Date型的对象
deadline = new Date(); //让deadline指向一个新的对象

注意:一个对象变量并没有实际包含一个对象,而仅仅是引用一个对象。new的返回值也是一个引用。

4. 一个方法可以访问所属类对象的私有数据。
这个不太好理解,举个例子:

class Employee{
	public boolean equals(Employee other){
		return this.name.equals(other.name);
	}
	private String name;
}
//调用方式
if(harry.equals(boss)) ...

可以看到在调用Employee的equals方法时,该方法访问了harry的私有数据name(this.name),这并不奇怪,私有数据能被该类中的方法调用。但是它也访问了boss对象的私有数据(other.name)!这是因为boss是Employee的对象,而一个类的方法可以访问其所属类对象的私有数据。
其实换个角度看,this对象也是一个特殊的当前类的对象,所以才能被该类的方法访问其私有数据。

5. 为了类的封装性,方法不应该返回可变对象的引用
如果一个类的方法返回的引用是指向可变对象的,那么该对象很可能在外部被修改,破坏了类的封装性。

class Employee{
	private Date hireDay;
	public Date getHireDay(){
		return hireDay;
	}
}

Employee harry = ...;
Date d = harry.getHireDay();
d.set(...); //可通过外部修改Harry的hireDay.

上述代码中,hireDay是一个Date对象的引用,当它被返回并赋值给另一个Date变量d时,对d的修改也会造成对harry对象的hireDay的修改。这破坏了类的封装性,一个类的数据成员最好只能被它本身的方法更改。

如果需要返回一个可变对象的引用,应该对它进行clone

class Employee{
	private Date hireDay;
	public Date getHireDay(){
		return hireDay.clone();
	}
}

二. 静态量和静态方法

1. 静态量
静态量分为静态变量和静态常量。静态变量往往用于对象的某些属性值需要唯一的情况,如员工的id号。这时就需要一个静态变量存储当前id号的累计值,以防出现相同的id。
但其实静态常量使用得更多。比如Math类中定义的PI:

public class Math{
	public static final double PI = 3.141592653...
}

如果static被省略,PI就变成了Math类的一个实例域,需要通过Math对象访问它,且每个Math对象都有PI的一份拷贝。而加上static,就可以通过类名访问Math.PI,且只存储了一份。

2. 静态方法
静态方法是一种不能向对象实施操作的方法,它不含隐式参数this,所以不能访问实例域,但是可以访问静态域。(也即静态方法不能访问除静态变量和方法外的其他变量和方法)
可以通过对象调用静态方法,但是静态方法不属于任何一个对象,所以为产生混淆,建议通过类名调用静态方法。

使用静态方法的两种情况:

  • 当一个方法与对象无关,其所需参数都是通过显式参数提供;
  • 当一个方法只需要访问类的静态域时。

三. 方法参数

C++有传值和传址调用,但Java只有传值调用。方法得到的所有参数都是原参数值的一个拷贝,这里的所有参数包括基本数据类型对象类型。但关于基本数据类型和对象引用,会有些许不同。

基本数据类型:

public void tripleValue(double x){
	x = 3 * x;
}
double y = 10;
tripleValue(y); //y的值不发生改变,还是10

上述代码中,只传入了y的一个拷贝给tripleValue方法,参数x被初始化为这个拷贝值(10),后来x变为了3倍,但在出了这个方法后,x就不再使用,y的值不发生改变。所以,一个方法不可能修改一个基本数据类型的参数

对象类型:

public void tripleSalary(Employee x){
	x.raiseSalary(200);
}
Employee harry = new Employee(...);
tripleSalary(harry); //harry的工资会加200

上述代码中,harry对象的值会被改变,但是它也是传的拷贝值。过程如下:x被初始化为harry值的拷贝,但是harry值本身就是指向Employee对象的引用,所以它的拷贝也是一个指向同样对象的另一个引用;raiseSalary作用于这个拷贝引用,该拷贝引用指向的对象的salary发生改变,则原引用指向的对象同样发生改变(因为是同一个对象)。
需要注意的是即使是对象类型,也是传值调用。一个方法可以改变一个对象参数的状态,但不能让它引用一个新的对象。

四. 对象构造

1. 显式域初始化
Java类的构造方法与C++类似,支持重载和默认构造函数。
但是,Java支持在类定义时,直接将一个值赋给成员变量,且这个值不一定是常量:

class Employee{
	private String name = ""; //C++不支持显式域初始化,只能使用构造方法
	private static int nextId;
	private int id = assignId(); //还可以这样初始化
	private static int assignId(){
		int r = nextId;
		nextId ++;
		return r;
	}
}

2. this对象
this除了作为隐式参数指示当前对象外,还可以用在构造器的第一句,调用另一个构造器:

class Employee{
	public Employee(double s){
		this("Employee" + nextId, s); //将调用Employee(String n, double s)构造器
		nextId++;
	}
	public Employee(String n, double s){
		name = n;
		salary = s;
	}
	......
}

采用this可以对公共的构造器代码只编写一次。在C++中,一个构造器不能调用另一个构造器。

3. 初始化块

还可以通过初始化块对成员变量进行初始化。初始化块是一个大括号括起来的初始化语句。初始化块在构造器之前运行,但不推荐使用初始化块。

4. 对象析构

Java有自动的垃圾回收器,所以不支持析构器。可以为任何一个类添加finalize方法,该方法在垃圾回收器清除对象之前调用。但最好不要依赖finalize方法回收任何短缺资源,因为不确定该方法什么时候会被调用。

如果某个资源需要在使用完毕后立刻被关闭,可以使用close方法来完成清理操作。

五. 包作用域与类的设计

1. 作用域
1.public修饰的部分(包括类,方法或变量)可以被任意的类使用;
2. private修饰的部分只能被定义它的类使用;
3. 没有指定的部分可以被同一个包中的所有方法使用。

2.类的设计
1.数据私有,保证类的封装性;
2.一定要对数据初始化。Java不对局部变量初始化,但是会对对象的实例域(也就是成员变量)进行初始化,可我们最好不要依赖于这种默认的初始化。我们可以为其提供默认值,或者在构造函数中进行初始化;
3.不要在类中使用过多的基本类型,可以用其他类封装代替;
4.不是所有的域都需要独立的域访问器和域更改器;
5.将大类进行适当的分解;
6. 类名和方法名尽量表义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值