一、继承
基于已存在的基类构造新类
java不支持多继承,只能继承一个类(但是可以实现多个接口)
(一)super()与this()
this的两个用途:
1隐式参数
2调用该类的其他ctor
super的两个用途:
1调用超类方法
2调用超类ctor
super不是一个对象的引用,不能将super赋给另一个对象变量,只是一个指示编译器调用超类方法的特殊关键字
引用调用非静态的被覆盖的方法时,是通过所引用的对象的实际类型所确定的(非静态的类方法有隐式参数this。this指向本类对象,也就是对象的本质类型,而不是对象的引用所声明的类型)
class Father{
public int id;
Father(int id){
this.id=id;
}
public int fun(int i){
return i*id;
}
}
class Son extends Father{
Son(int id){
super(id);
}
public int fun(int i){
return i*id*2;
}
}
public class test2 {
public static void main(String[] args) {
Father[] arr=new Father[]{new Father(3),new Son(10)};
for(Father t:arr){
System.out.println(t.fun(4));
}
//结果12(4×3),80(4×10×2),调用了引用所指向的对象的实际类型的fun()
}
}
(二)多态
一个对象变量可以指示多种实际类型的现象叫做多态
多态特点:
1.is-a即是置换法则,表明程序中出现超类对象的任何地方都可以用子类对象置换。(虽然子类是扩展,但是超类使用的方法子类都有,所以超类出现的地方,换成子类,子类也可以使用)
2.可以将一个子类对象赋给超类(对象)变量e.g. Employee e=new Manager();
3.不能将一个超类引用(引用的目标对象是超类对象)赋给子类(对象)变量
e.g. M m=newE();//错误
一种错误现象:父类引用子类数组时,修改数组成员为父类对象。当对这个父类对象使用子类方法则会产生异常。
(三)动态绑定
动态绑定:在运行时能够自动选择调用哪个方法的现象称为动态绑定
重要特性:不需要对现存代码进行修改,就可以对程序进行扩展
调用对象方法的过程
1)虚拟机预先为每个类创建方法表。真正调用时会通过查表确认须要使用的是哪个方法。
2)判断是静态还是动态绑定:
静态绑定:如果所调用的方法是private(子类不可访问超类的私有方法),static(静态方法没有隐式参数,不依赖对象的实际类型),final,则是静态绑定,可以准确确定调用的方法
动态绑定:依赖于隐式参数的实际类型。
3)编译器查看调用方法的对象的声明类型(这里对象指的是对象变量,声明类型不是对象变量所引用的对象的实际类型,比如声明为对象的超类)和方法名。一一列举所有超类的public和本类的方法中方法名为所需的方法。
4)编译器查看对象调用方法时所提供的参数类型,与列出的所有同名方法进行匹配。匹配的过程称为重载解析。
若只有一个匹配则为所求,若没有结果或者多个结果则产生异常。
确保覆盖的方法的返回值类型的兼容性,可以定义为原返回类型的子类型。称为覆盖与被覆盖的方法具有可协变的返回类型。e.g.
EmployeegetSalary();
ManagergetSalary();
另外,在覆盖方法时,子类方法不能低于父类方法的可见性,父类方法是public的话,子类必须是public,不能不加修饰符使用默认权限。
在调用e.getSalary()的解析过程:
1.虚拟机提取e的实际类型的方法列表。
2.搜索定义getSalary签名的类。确定调用方法。
3.虚拟机调用该方法。
(四)阻止继承:final
将方法或类声明为final的目的:确保它们不会在子类中改变语义。
final类是不允许扩展的类
类声明为final,其方法自动变为final,但是域不会自动变为final
类的方法可以单独声明为final,则在类的子类中不可被覆盖
内联:若方法没有被覆盖且很短,编译器就可以优化。比如内联调用e.getName()被替换成e.name
(五)强制类型转换
唯一原因:暂时忽视对象的实际类型后,使用对象的全部功能。(比如高级for中使用统一的超类对象变量,具体代码需要还原为子类使用子类特有方法)
可以将子类引用赋给超类变量E e=newM();
将超类引用赋给子类变量需要进行检查。如果是非法的,比如M m=new E();//error,则会产生ClassCastException
注意:
1.强制类型转换只能在继承层次内进行转换。
2.在将超类(对象变量)转换成子类(对象变量)前应使用instanceof进行检查
(六)抽象类
某些祖先类只作为派生其他类的基类而不作为可以使用的特定实例类。使用abstract关键字。
abstract class Person{
publicabstract String getDepression();
}
特点:
只要有一个抽象方法类就必须标明为abstract
但是抽象类中可以有具体方法和域
即使类中没有抽象方法也可以将类声明为抽象类
抽象类不能被实例化。但可以创建一个具体子类的对象。
(七)protected
允许子类访问超类的特定方法和域
意义:限制某个方法的使用。表明子类可信,可以正确使用该方法
二、Object类的使用
Object类是所有类的超类
可以使用Object类型的变量引用任何类型的对象
(一)equals
默认操作:判断两个对象的引用是否相同
覆盖equals:
public boolean equals(Object otherObject){}//注意参数类型一定是Object
较好的equals方法:
1. 显示命名参数名为otherObject,稍后将之转换成另一个叫other的变量
2.检测this与otherObject是否引用同一对象
if(this==otherObject)
returntrue;
3.检测otherObject是否为null,null则返回false
if(nul==otherObject)
returnfalse;
4.比较otherObject和this是否属于同一个类。
如果equals的语义在每个子类中有所改变(比如计算工资的方法父子类不同)则用getClass()比较。
if(getClass()!=otherObject.getClass())
returnfalse;
如果语义在每个子类中有统一的语义(比如比较的是ID)则使用instanceof检测
if(!(otherObjectinstanceof ClassName))
returnfalse;
5.将otherObject强制转换为相应的类型
ClassName other=(ClassName)otherObject;
6.对需要比较的域进行比较
a如果子类中重新定义了equals则需要调用
super.equals(other)
b==比较基本类型域;equals比较类类型域
比较域时,防止对象域为null的情况:
v1.7 java.util.Objects:
static boolean equals(Object a,Object b)
如果两个参数都是null返回true;
一个null返回false;
两个都不是null则调用a.equals(b);
e.g. Objects.equals(name,other.name);
对数组类型的域,可以使用静态方法:Arrays.equals方法进行检查数组元素是否相等
static boolean equals(T[] a1,T[] a2)
(二)hashCode
hashCode 由对象导出的一个整型值。
Object中hashCode默认返回对象的存储地址。
如果覆盖equals就应该覆盖hashCode,以便将对象插入到散列表
equals与hashCode的定义必须一致,如果equals为true则两者的hashCode返回值相同
java.util.Arrays
static int hashCode(T[] a)//获取数组散列码
java7 java.util.Objects
static hashCode(Object a);//null安全的获取散列码的方法
int hash(Object… objects)//可以返回多个对象的散列码的组合。
(三)toString
通用格式:类名[域值]
return getClass().getName()+”[”+…fields…+”]”;
子类定义toString可以先使用super.toString()再打印子类独有的域。如果父类使用了getClass().getName(),子类super.toString()会调用之,返回子类的实际类型,不需要在子类中再次使用。
数组继承Object的toString打印的是数组的地址值。想要显示数组内容可以使用Arrays.toString(T[] a);
三、对象包装器、自动装箱、自动拆箱
装箱和拆箱是编译器的行为。不是虚拟机。
对象包装器是不可变对象。
自动装箱:
List<Integer> list=newArrayList<Integer>();
list.add(3);
编译器自动翻译为:
list.add(Integer.valueOf(3));
自动拆箱:
int n=list.get(i);
编译器自动翻译为:
int n=list.get(i).intValue();
Ingeter n=3;
n++;
包装器类对象自增的过程:
1.自动拆箱
2.将值自增
3.自动装箱
对象包装器的对象使用==进行比较,比较的不是值而是存储位置(其对应基本类型==比较的是值)
自动装箱规范:
1.boolean,byte,char<=127
2.数值在-127~128间的short,int值只会建立一个对象,不会开辟多个空间