OOP的继承特性使OOP语言的代码具有可拓展性,拓展类也称为继承(inheritance)或子类化(subclass)。在Java中,默认情况下所有类都是可以拓展的,也可以使用final
关键子来防止类被子类化。
extends
在类声明时我们使用extends
关键字来拓展类,其位置位于子类名后,父类名前,例如:
public class Child entends Parents{
......
}
所有没有拓展父类的Java类都将自动拓展java.lang.Object
类作为父类,即Object
是Java中的最终超类,在上面的代码中,Parents
的父类就是Object
。
is-a关系
子类和超类具有is-a关系,例如Fruit类包含所有水果,那么Peach、Orange、Banana都是Fruit类的子类,因此Peach、Orange、Banana与Fruit便是is-a关系。
is-a关系不可逆,我们可以说Peach是Fruit,但是不能说Fruit就是Peach,在代码中我们可以这么理解:
public class Fruit{
public void eat(); //Fruit具有eat的方法
}
class Peach extends Fruit{ //Peach是Fruit的子类
public void pink();
//Peach具有pink的方法,但是也继承了eat的方法
}
Fruit peach1 = new Peach(); //Peach是Fruit的子类,存在is-a关系
Peach peach2 = new Peach(); //正常创建对象
Peach fruits = new Fruit(); //由于is-a关系不可逆,因此这行代码是错误的!
子类继承了其超类所有的公共方法和字段,以上面的代码为例,存在如下的情况:
Peach aPeach = new Peach();
aPeach.eat(); //Peach继承了Fruit的eat方法
可访问性
在子类中我们可以访问其超类的public
与protected
级别的方法和字段,但是不能访问private
级别的方法和字段,如果子类和超类位于同一个包中,子类也可以访问到超类默认级别的方法和字段。例如:
public class A{
public static int test1(){...};
protected static int test2(){...};
private static int test3(){...};
}
class B extends A{
public void test(){
test1(); //B可以访问到A的public方法
test2(); //B可以访问到A的protected方法
test3(); //B无法访问到A的private方法
}
}
同时这种关系是子类与父类间的,我们无法通过A的子类B来访问到A的非公开方法,例如在上面的代码基础上:
public class C{
public static void main(String[] args){
B obj = new B();
obj.test2(); //无法编译,无法从外部访问A的protected方法
}
}
重写与重载
重载不等同于重写:方法的重写发生在父类与子类中,而重载发生在本类;重载的方法名必须相同,重写的方法名相同且返回值类型必须相同;重载的参数列表不同,重写的参数列表必须相同。
方法重载
方法重载的定义是如果有两个方法的方法名相同,但参数不一致,哪么可以说一个方法是另一个方法的重载。
对于定义我们可以有以下的理解:
- 方法名相同
- 方法的参数类型,参数个数不同
- 方法的返回类型可以不相同
- 方法的修饰符可以不相同
main
方法也可以被重载
方法重载例子如下:
public class Person {
String name; //name字段
int age; //age字段
public Person(){ //无参构造方法,命名与类名一致
this.name = "Jack";
this.age = 18;
}
public Person(String name){ //重载 构造方法1(含一个参数name)
this.name = name;
this.age = 18;
}
public Person(String name, int age){ //重载 构造方法2(含两个参数name,age)
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Person mine = new Person(); //创建对象引用mine,此时调用 无参构造方法
Person he = new Person("Lucy"); //创建对象引用he,此时调用构造方法1
Person anybody = new Person("Helen", 3); //创建对象引用anybody,此时调用构造方法2
System.out.println("My name is " + mine.name + " and age is " + mine.age); //输出mine对象的字段
System.out.println("His name is " + he.name + " and age is " + he.age); //输出he对象的字段
System.out.println("Your name is " + anybody.name + " and age is " + anybody.age); //输出anybody的字段
}
}
方法重写
当拓展一个类时,我们可以在子类中修改父类的方法,这个操作便称为方法的重写,但是子类中编写的方法与父类的方法要具有相同的签名(相同返回类型,名称相同,参数列表相同)。需要注意的是重写时方法的权限可以从低到高,但是不能从高到低,也就是说,子类重写方法的权限不能低于父类被重写方法原本的权限。
权限的大小顺序如下:Public > Protected > 默认 > private
和继承的权限特点是相同的,我们可以在子类中重写父类public
与protected
级别的方法,如果父子类位于同一个包中,也可以重写默认级别的方法。
public class Override{
public static void main(String[] args){
System.out.println("This is Class A print:");
A a = new A();
a.echo("Test");
System.out.println("This is Class B print:");
A b = new B();
b.echo("Test");
}
}
class A{
public void echo(String str){
System.out.println(str);
}
}
class B extends A{
public void echo(String str){
System.out.println("Override is " + str);
}
}
运行结果如下
超类的调用
我们可以通过super
关键字来实现在子类中对超类成员的调用,以菜鸟教程的例程为例:
class Country {
String name;
void value() {
name = "China";
}
}
class City extends Country {
String name;
void value() {
name = "Shanghai";
super.value(); //调用父类的方法
System.out.println(name);
System.out.println(super.name);
}
public static void main(String[] args) {
City c=new City();
c.value();
}
}
调用超类的构造方法:
class Person {
//构造方法(1)
Person() {
System.out.println("父类无参数构造方法");
}
//构造方法(2)
Person(String name) {
System.out.println("父类含一个参数的构造方法:" + name);
}
}
public class Chinese extends Person {
Chinese() {
super(); //调用父类构造方法(1)
System.out.println("调用父类无参数构造方法");
}
Chinese(String name) {
super(name); //调用父类具有相同形参的构造方法(2)
prt("调用父类含一个参数的构造方法:" + name);
}
public static void main(String[] args) {
Chinese cn = new Chinese();
cn = new Chinese("codersai");
}
}
转换
将子类的一个实例转换为他的父类类型,这个过程称为向上转换(upcast)。相反,将一个类的实例类型转换为其子类的类型便叫做向下转换(downcast)
class Parents{
......
}
class Child extends Parents{
......
}
public class Cast{
public static void main(String[] args){
//向上转换
Child child1 = new Child();
Parents parents1 = child1; //由Child类型的child1变为Parents类型的parents1
//向下转换
//只有当父类引用已经指向子类实例时才能进行向下转换
Parents parents = child1; //parents指向了Child的一个实例
//向下转换强制要求在括号内写入子类型
Child child2 = (Child) parents; //由Parents类型的parents变为Child类型的child2
}
}
final类
在类声明中,我们使用final
可以使其变为最终类,无法被继续继承和拓展。
instanceof
instanceof
运算符可以检验某个对象是否是指定的类型,常用于if语句中,例如
public class Test{
public static void main(String[] args){
String a = "Ye";
if (a instanceof java.lang.String){
System.out.println("Yes");
}else{
System.out.println("No");
}
}
}
同时,子类也是属于其父类的类型,因此也可以判断一个实例是否属于某个父类类型中。
class Parten{
......
}
class Child extends Partens{
......
}
public class Test{
public static void main(String[] args){
Child child = new Child();
if (child instanceof Parents){ //判断child实例是否属于Parents类型
System.out.println("Yes");
}else{
System.out.println("No");
}
}
}