Java继承

1基础

  1. 定义子类
    Java用extends表示继承,与C++的:相同。
public class Manager extends Employee
{
}

Java中,所有的继承都是公有继承,没有C++中的私有继承和保护继承。

  1. 覆盖方法
    直接使用同名方法来覆盖父类的方法。
    使用super.可以指定调用父类的方法,相当于C++中使用 父类名::。
//java
public double getSalary()
{
	double baseSalary=super.getSalary();
	return baseSalary+bonus;
}
//c++
public:
	double getSalary()
	{
		double baseSalary=Employee::getSalary();
		return baseSalary+bonus;
	}

在Java中,方法默认是动态绑定的,不需要声明为virtual。如果不希望方法有虚拟特性,可以将其标记为final的。

  1. 构造函数的调用
    子类构造函数可以使用super来调用父类构造函数,而在C++中,在子类的初始化列表中调用父类构造函数。

使用super调用父类构造函数的语句必须出现在子类构造函数的第一句。

//java
public Manager(String name,double salary,int year, int month,int day)
{
	super(name,salary,year,month,day);
	bonus=0;
}
//c++
Manager(String name,double salary,int year,int month,int day)
	:Employee(name,salary,year,month,day),bonus(0){
	}

如果子类没有显示地调用父类的构造函数,将会调用父类的默认构造函数,如果父类没有默认构造函数,编译器将报错。

在构造函数中,可以使用this.调用其它构造函数,该语句也必须出现在第一句,并且不能和super语句同时出现,否则编译器将报错

  1. 多态
    在java中,子类数组的引用可以转换成父类数组的引用, 而不需要采用强制类型转换。例如如下代码
Manager[] managers=new Manager[10];
Employee[] staff=managers;
staff[0]=new Employee();

该语句编译可以通过,但是我们将一个父类对象赋给了一个子类的引用,当使用managers[0].setBonus(1000)的时候,用到了一个不存在的成员变量。

  1. 方法的调用
    对于语句
x.f(param);

Java调用方法的过程如下:
首先,如果x的类型为C,编译器将列举出C类型所有名为f的方法,以及其父类中访问属性为public名为f的方法。

第二步,根据参数列表的类型,从所有名为f的方法中筛选出参数列表符合的方法,(过程中可能涉及到类型转换)。如果编译器没有找到与参数类型匹配的方法,或者发现经过类型转换后有多个方法与之匹配,就会报告一个错误。

第三步,如果是private、static、final、或者构造函数,那么编译器可以准确知道该调用哪个方法,这称为静态绑定

第四步,如果是需要动态绑定的方法,虚拟机会调用与x所引用对象的实际类型最合适的那个方法。假设x的实际类型是D,它是C的子类,如果D类中定义了方法f()且参数类型符合,就直接调用它。否则将在D类的父类中寻找合适的f()。虚拟机预先为每个类创建一个方法表(method table),包括了该类所有的方法和该类的父类所有的方法都在方法表中,在调用该类的方法时直接搜索该类的方法表就知道该调用哪个方法。如果使用了super关键字,将会在父类的方法表中搜索方法。

在覆盖一个方法时,子类方法不能低于超类方法的可见性,如果超类方法是public,子类就一定要是public。

  1. 阻止继承和覆盖方法——final关键字
final使用位置作用
成员变量声明前该成员变量不可修改(相当于const)
方法声明前不可以被覆盖
类声明前不可以被继承,其中的方法也默认是final的,但变量不是。

Java的内联是由编译器自动处理的。

  1. 强制类型转换
    除了内置类型之间的转换外,类类型之间也可以进行转换,其要求如下:
  • 只能在继承层次内进行类型转换
  • 在将超类转换成子类之前,应该使用instanceof进行检查。
  1. 抽象类
  • 包含一个或多个抽象方法的类本身必须被声明为抽象的,使用abstract关键字声明。
  • 抽象类的子类如果实现了它全部的抽象方法,就可以不再是抽象类,否则仍然是抽象类。
  • 类即使不含抽象方法也可以被声明为抽象类。
  • 抽象类不能创建实例,但可以定义引用,只能引用到它的具体子类对象上。
  • 在C++中,使用=0标记抽象方法,称为纯虚函数,含有纯虚函数的类就是抽象类。
  1. protected
  • protected标记的方法和成员变量子类可以访问。
  • 事实上,Java的protected和C++稍有不同,Java中的protected对所有子类及同一个包中的其他类可将。
  • 四种访问修饰符的可见性
修饰符可见性
private仅本类中可见
protected本包和所有子类中可见
public所有类可见
默认(不需要修饰符)本包可见

2 Object——所有类的父类

  1. equals方法
  • Object类中的equals方法判断两个对象是否有相同的引用。
  • 可以重写子类的equals方法使其具有合适的意义。
  1. 相等测试与继承
  • Java语言规范要求方法具有下面的特性
特性意义
自反性x.equals(x)应返回true
对称性x.equals(y)和y.equals(x)应返回同样的结果
传递性如果x.equals(y)&&y.equals(z),则x.equals(z)
一致性如果x、y引用的对象不变,则x.equals(y)不变
非空性如果x非空,则x.equals(null)返回false
  • 一个编写出完美的equals方法的建议
//显式参数命名为otherObject
public boolean equals(Object otherObject)
{
	//检测this与otherObject是否引用到同一个对象
	if(this==otherObject) return true;
	//检测otherObject是否为null
	if(otherObject==null) return false;
	//比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测
	if(getClass()!=otherObject.getClass()) return false;
	//如果所有的子类都拥有统一的语义,就使用instanceof检测
	if(!(otherObject instanceof ClassName)) return false;
	//将otherObject转换为相应的类类型变量
	ClassName other=(ClassName)otherObject;
	//比较所有需要比较的域
	return field1==other.field1&&.......;
	//如果在子类中重新定义了equals,就首先调用super.equals(other);
}
  • 在覆盖equals方法时,参数一定是Object类型的,否则,实际上没有覆盖Object的equals方法。为了避免发生此类错误,可以用@Override对覆盖超类的方法进行标记,告诉编译器此处覆盖超类的方法,如果没有成功覆盖,编译器就会给出错误报告。
  1. hashCode方法
  • Object类的默认hashCode方法导出对象的存储地址。
  • 如果重新定义equals方法,就必须重新定义hashCode方法,也就是说如果x.equals(y)返回true,那么它们的hashCode也应该相等。
  • 可以使用Objects.hash()传入多个参数,快速返回它们组合的hashCode。
public int hashCode()
{
	//将会返回三者的hashCode组合后的结果
	return Objects.hash(name,salary,hireDay);
}
  • 对于数组,可以使用静态的Arrays.hashCode方法计算一个hashCode,这个hashCode由数组元素的hashCode组成。
  1. toString方法
  • 绝大多数的toString方法都遵循这样的格式:类的名字+方括号括起来的域值。
  • 一个建议的toString实现如下:
public String toString()
{
	return getClass().getName()
	+"[name="+name
	+",salary"+salary
	+",hireDay"+hireDay
	+"]";
}
  • 只要对象鱼一个字符串通过操作符“+“连接,编译器就会自动调用其toString方法。""+x相当于x.toString()。
  • Object类的toString方法打印对象所属类名和散列码。
  • 数组继承了Object类的toString方法,可以使用Arrays.toString打印数组,对于多维数组可以使用Arrays.deepToString方法。
  • 强烈建议为自定义的每一个类增加toString方法。

3 泛型数组列表

ArrayList<T> 与C++的vector<T>类似,不同之处在于不支持下标操作[],而要使用get、set方法来访问和修改元素。

4 对象包装器与自动装箱

有时,需要将int这样的基本类型转换为对象,例如:声明ArrayList<int>就是不允许的,要使用ArrayList<Integer>(这是什么鬼特性,怀念STL),这时候就需要把int打包为Integer类型。各种基本类型对应的包装器类型如下

基本类型包装器类型
intInteger
longLong
floarFloat
doubleDouble
shortShort
byteByte
charCharacter
voidVoid
booleanBoolean
  • 大部分情况下,编译器会为我们自动装箱和拆箱,我们可以像使用基本类型一样使用它们。
  • ==运算符有所不同,它比较的是两个装箱对象的引用地址是否相同,应使用equals方法。
  • 装箱对象是不可变的。
  • 装箱类型是final的。
  • 装箱类型可以自动类型升级,例如在一个表达式中同时含有Integer和Double时,编译器会自动将Integer升级为Double。

5 参数数量可变的方法

意味着方法可接受的参数数量可变,printf就是一个可变参数方法,其定义如下。

public class PrintStream
{
	public PrintStream printf(String fmt,Object...args){return format(fmt,args);
}

其中Object…是java语法的一部分,表示这个方法可以接受任意数量的对象。

下面一段代码用于计算若干个double数据的最大值:

public static double max(double...values)
{
	double result=Double.NEGATIVE_INFINITY;
	for(double x:values) result=result>x?result:x;
	return result;
}

6 枚举类

  • 一个示例如下
public enum Size{SMALL,MEDIUM,LARGE,EXTRA_LARGE};
  • 所有枚举类型都是Enum的子类,继承了它许多方法。
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值