1、继承
超类是子类的公共属性和方法的集合,子类除了继承超类的所有功能,也可以修改继承或增加新属性和方法。继承也是一种抽象,提高了类的重用性,让类与类之间产生了关系(多态的基础),降低了编码和维护的工作量。
类与类、对象与对象之间除了继承关系,还有组合等关系。继承表达的是从属关系“是一种(is-a)”,而组合是将已存在类的对象放到新类中,表达包含关系“有一个(has-a)”。
[classModifier] class ClassName extends SuperClassName
{
//类体
}
注意:
(1)继承是为“是一种”关系建模的,不要为了获取其它类的功能而盲目扩展一个类。
(2)Java仅支持单继承,即一个子类只能直接继承一个超类。多继承容易带来安全隐患,如当多个父类中有相同方法名,但内容不同。但是支持多层继承。
2、方法重写和super
(1)方法重写
子类虽然继承了父类的功能,但有时需要修改算法或增加、取消功能,此时尽量不要修改源代码,修改不止一处,调用它的程序也得改。为了提高程序的扩展性,需要重写父类该功能。
当子类的方法和父类方法的签名和返回值类型均相同时,父类的方法被覆盖。复写时还要用父类中的同名方法时,用super.方法名(<参数>);。在一个类之前添加重写标注@Override,之后必须重写父类方法,不然编译器会报错。
注意:
①子类覆盖父类时,必须保证子类权限大于父类权限,否则会出现编译错误。父类中的私有方法在类外不能被访问,所以不能被覆盖。
②父类中的抽象方法必须被覆盖,否则子类也为抽象类。静态方法能被继承,但不能被覆盖,如果父类中的静态方法被子类重新定义,通过<父类名>.<静态方法名>调用隐藏的静态方法。final方法也不能被覆盖(类的继承打破了封装性,final避免类和方法被继承和复写)。
(2)属性的隐藏
当子类声明了与超类相同的成员变量名,可以不同类型,从超类继承的变量将被隐藏。当子类执行继承自超类的操作时,处理继承自超类的变量,当子类操作它自己声明的方法时,操作它自己的变量。在本类声明的方法中使用“super.属性”访问从超类继承的属性。
(3)调用父类的构造方法
父类的构造方法不会被子类继承,只能在子类的构造方法中使用关键字super调用。super()必须放在子类构造方法的第一行,如果没有被显示地调用,自动将super()作为子类构造方法的第一句。从最远超类Object类的构造方法开始执行,最后执行当构造方法体的其它语句,因为子类先参考父类初始化动作。
最好为每个类提供无参构造方法,否则必须在其子类中显式指定构造方法。
子类中构造函数通过this调用子类中其它构造函数时,子类中至少有一个函数访问super。this和super不能同时出现,因为它们都要写第一行,初始化动作必须先做。
对于构造方法,尽量少的动作把对象的状态初始化,如果可以避免,不要调用任何方法。如果方法具有多态性,被覆盖,会出现潜在问题,能够安全调用的仅有final和private方法。
3、多态
多态指在有继承的情况下,超类及其子类的对象可以响应同名的消息(覆盖),具体的实现方法却不同;父类或接口的引用指向了其子类的对象。多态提高了程序的扩展性。
Object o=new Circle();//类型提升,向上转型
o.toString();//o调用哪个toString()由o的实际类型决定,称为动态绑定
Circle c=(Circle)o;//向下转型,强制将父类引用转换为子类类型
在多态中非静态方法成员的特点: 编译时,参阅引用型变量所属类中是否有调用的方法,没有则编译失败;运行时,参阅对象所属类中是否有调用的方法,从对象创建时的类开始,沿类层次向上查找,一旦找到一个实现就停止查找。简单地说,编译看左边,运行看右边。
(限面试)成员变量和静态方法的特点:无论编译运行,都参考左边,引用型变量所属类。静态方法与对象无关,静态区只有类名调用。而非静态方法存储在方法区的非静态区,非静态区方法被对象所引用
引用变量的类型转换,将引用转换为另一类型的引用,并不改变对象本身的类型。只能被转为:任何一个直接或间接超类的类型或实现的接口(向上转型,一般隐式转换),引用所指的对象的类型(唯一向下转型)。当一个引用被转换为其超类引用后,通过它能够访问的只有超类中声明的方法。需要访问子类中的方法,必须转型为子类的引用。
类型比较运算符instanceof,比较某个对象是否属于某个类型。o instanceof Circle相当于if(this.getClass()!=o.getClass()) return flase;。
4、Object类
Object类处在类层次的最高点,是所有类的直接或间接的超类,包含所有Java类都具有的属性和行为。当一个类没有指定继承性,它的父类默认是Object类,构造函数第一句的super()即为Object()。Object类不是抽象类,因为如果抽象,子类必须强制实现其中的所有抽象方法,使类的实现更复杂。
public String toString()返回该对象的字符串表示。默认返回该对象所属类名+该对象用十六进制表示的哈希码getClass().getName()+”@”+Integer.toHexString(hashCode());。(getClass()在Object类中,返回此对象运行时的类;用Class类描述所有类的共同特征,其中getName()返回此Class对象所表示的类的名称)通常被覆盖,返回该对象的描述性字符串。
boolean equals(Object obj)通过==比较两引用是否指向同一对象,即内存地址是否相同。通常该方法会被重写,用来判断两对象是否具有相同内容。
class Demo //extends Object
{
private int num;
Demo(int num)
{
this.num=num;
}
public boolean equals(Object obj)
//复写Object类中的equals方法,参数必须是Object类,不然是重载
{
//return this.num==obj.num//编译失败,obj类中没有num变量
if(!(obj instanceof Demo))//obj是否是Demo类型
return false;
Demo d=(Demo)obj;//向下转型
return num==d.num;
}
}
class Person
{
}
class ObjectDemo
{
public static void main(String[] args)
{
Demo d1=new Demo(2);
Demo d2=new Demo(2);
Person p=new Person();
System.out.println(d1.equals(d2));//true
System.out.println(d1.equals(p));//false,抛出异常更确切
}
}