1. 继承
  2. 成员的覆盖
  3. 多态
  4. 抽象类和接口
  5. 其他

继承

定义:
继承就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
注:父类又称为超类或者基类。子类又称为派生类! 

继承的格式:

通过 extends 关键字,可以声明一个子类继承另外一个父类

class A {undefined

}
class b extends A {      // b继承A

}

继承的实现:

class Person{
    String name;
    int age ;
}
class Student extends Person{
    void study(){
        System.out.println("student study..." + age);
    }
}
class Worker extends Person{
    void work(){
        System.out.println("worker work..." + age);
    }
}
class ExtendDemo{
    public static void main(String[] args){
        Student s = new Student();
        s. name = "zhangsan" ;
        s. age = 20;
        s.study();
        Worker w = new Worker();
        w. name = "lisi" ;
        w. age = 30;
        w.work();
    }
}

注意:

1)Java不支持多重继承,一个子类只能有一个父类,不允许出现以下情况

class 子类 extends 父类1,父类2{}

2)在Java中可以有多层继承,如A继承了B,B又继承了C。此时相当于A间接继承了C。

3)如果一个成员要被子类继承之后使用,这个成员不能是private类型,因为私有的成员不能在类的外部使用,当然也不能被子类使用。一般情况下,成员变量定义为protect类型,成员函数定义为public类型

继承的底层本质:

从本质上来讲,子类继承父类之后实例化子类对象的时候,系统会首先实例化父类对象

只要实例化子类对象,系统就会先自动实例化一个父类对象与之对应,当然此时掉用的是父类没有参数的构造函数。

这就出现了一个问题--------父类构造函数万一有参数呢?

因此,此时,系统必须要求在实例化父类对象之时传入参数,否则会报错 

解决问题有以下两种方法:

1)给父类增加一个不带参数的空构造函数

2)在子类的构造函数中,第一句用super给父类构造函数传参数

注意:

"super(title);"必须写在子类构造函数的第一句,传入的参数必须和父类的构造函数中的参数列表类型相匹配

 成员的覆盖:

问题:

在父类和子类中都有函数show(),子类对象调用“fd.shou();”,调用的是子类的show,还是父类的show?

如果子类中的函数定义和父类相同,最后调用时是调用子类中的方法,如此称为覆盖或重写(Override)

注意:

1)将override和overload相区别

2)如果在子类中定义了一个名称和参数列表与父类相同的函数,但是返回类型不同,此时系统会报错

3)在重写时,子类函数的访问权限不能比父类更加严格。例如,父类的成员函数的访问权限是public,子类在重写时就不能定义为protect。

4)在覆盖的情况下,如果一定要在子类中调用父类的成员函数,可以使用关键字super,调用方法是"super.函数名"

下面来通过一个例子了解一下:

public class Father

{
public static String name1 = "父类的类变量";

public String name2 = "父类的实例变量名";

//定义一个类方法

public static void classMethod()

{
System.out.println("Father父类通过类方法调用它的类变量:" + name1);

}

//定义一个实例方法

public void instanceMethod()

{
System.out.println("Father父类通过实例方法调用它的实例变量:" + name2);

}

}

public class Son extends Father

{
public static String name1 = "子类的类变量";

public String name2 = "子类的实例变量";

//定义一个类方法

public static void classMethod()

{
System.out.println("Son子类通过类方法调用它的类变量:" + name1);

}

//定义一个实例方法

public void instanceMethod()

{
System.out.println("Son子类通过实例方法调用它的实例变量:" + name2);

}

}

public class Test

{
public static void main(String[] args)

{
System.out.println("\n-----------------------------------------");

Son mySon1 = new Son();

Father myFather1 = mySon1; //对象类型转换的上转型

myFather1.instanceMethod();

System.out.println("\n------------------------------------");

Father myFather2 = new Father();

if (myFather2 instanceof Son)

{
//隐式对象类型转换

Son mySon2 = (Son) myFather2;

//调用myFather对象的实例方法

mySon2.instanceMethod();

}

}

}

 多态:

多态是面向对象的基本特征之一

注意:

函数重载也是一种多态,称为静态多态

 动态多态的理论基础是父类引用可以指向子类对象,示例代码如下:

 

 在main函数中"Dialog dialog=new FontDialog();",首先定义了一个Dialog类型的父类引用,却指向了一个子类对象。

在这种情况下,"dialog.show();"到底是调用父类的show函数还是子类的show函数呢?

运行代码,控制台打印结果如下所示:

FontDialog.show()

 可以看出,调用的是子类的show函数。

多态的使用:

  1. 函数传入的形参可以是父亲类型,而实际传入的可以是子类对象
...
public class main(){
       public static void fun(Dialog dialog){   //父类类型形参
           dialog show();
}
      public static void main(String[]args){
          fun(new FontDialog());     //实际传入的是子类对象
      }
}

 实际调用时传入的是子类对象,因此调用的是子类对象的show方法。

2. 函数的返回类型是父类类型,而实际返回的可以是子类对象

...
public class main{
      public static Dialog fun(){   //函数返回类型是父类类型
          return new FontDialog();    //实际返回类型是子类对象
      }
      public static void main(String [] args){
         Dialog dialog=fun();
         dialog.show();
      }
}

父类和子类对象的类型转换:

1:子类类型对象转换成父类类型

Dialog dialog=new FontDialog();

 2:父类类型对象转换成子类类型

Dialog dialog=new FontDialog();

FontDialog fd=(FontDialog)dialog;

注意:

这是一种特殊情况,父类类型对象原来就是子类类型的对象,则可以像上面一样强制转换成相应子类类型

 问答:

问)如何知道一个对象是什么类型的?

答)可以使用 instanceof操作符进行判断,格式为:

       对象名 instanceof 类名

 抽象类和接口:

抽象类
首先看抽象方法:
抽象方法: 只有方法签名,没有方法的实现。并且被abstract修饰。
例如: 

abstract void test();

有抽象方法的类只能被定义为抽象类,抽象类里可以没有抽象方法。

抽象类的规则
抽象类必须使用abstract修饰符来修饰。

抽象类不能被实例化,无法使用new关键字来创建实例,即使抽象类中没有抽象方法,也不可以创建实例。只能当做父类被其他子类继承。

抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器(不能用于创建实例,主要是被子类调用)、初始化块、内部类(接口、枚举)5种成分。

含有抽象方法的类。(直接定义了一个抽象方法;或者继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或者实现了一个接口,但没有完全实现接口包含的抽象方法)这三种情况,只能被定义成抽象类。

当使用abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须由子类提供实现(即重写),因此abstract方法不能定义为private访问权限(必须用public和protected修饰,缺省情况下为public),即private和abstract不能同时修饰方法。而final修饰的类不能被继承,final修饰的方法不能被重写。因此,final和abstract永远不能同时使用。

static和abstract不能同时修改某个方法,即没有所谓的类抽象方法。

抽象类的作用

作为子类的模板,从而避免了子类设计的随意性。抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为。

接口

介绍

接口定义了一种规范。

基本语法如下:

[修饰符] interface 接口名 extends 父接口1, 父接口2...
{
  零到多个常量定义... (因为接口是一种规范,不能包含构造器和初始化块定义。只能是静态常量)
  零到多个抽象方法定义...
  零到多个内部类、内部接口、内部枚举定义...
  零到多个默认方法或类方法定义... (只有在java8以上版本才允许)
}

说明:

修饰符可以是public或者省略,如果省略了public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下可以访问该接口。

接口名应该与类名采用相同的命名规则。合法的标识符,并具有可读性,可以使用形容词。

一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。

注意:

接口中定义的是多个类共同的公共行为规范,因此接口里所有成员,包括常量、方法、内部类和内部枚举都是public访问权限。定义接口成员时,可以忽略访问控制修饰符,如果指定访问控制修饰符,则只能使用public访问控制修饰符。

对于接口里定义的静态常量而言,它们是接口相关的,因此系统会自动为这些成员变量增加static和final两个修饰符。也就是说,在接口中定义成员变量时,不管是否使用public static final修饰符,接口中的成员变量总是使用这三个修饰符修饰。 而且接口中没有构造器和初始化块,因此接口里定义的成员变量只能在定义时指定默认值。

下面两行代码的结果是一样的,系统会自动增加public static final修饰符
int MAX_SIZE = 10;
public static final int MAX_SIZE = 10;

接口里定义的方法只能是抽象方法、类方法和默认方法,因此如果不是定义默认方法,系统将自动为普通方法增加abstract修饰符;定义接口里的普通方法时,不管是否使用public abstract修饰符,接口里的普通方法总是使用public abstract来修饰。 接口里的普通方法不能有方法实现(方法体);但类方法、默认方法都是必须有方法实现(方法体)。

接口里定义内部类、内部接口、内部枚举默认都采用public static两个修饰符,不管定义时是否指定这两个修饰符,系统都会自动使用public static对它们进行修饰。

使用接口

接口不能创建实例,但是可以用于声明引用类型变量。当使用接口来声明引用类型变量时,这个引用类型变量必须引用到到其实现类的对象。用途如下:

  1. 定义变量,也可以用于强制类型装换
  2. 调用接口中定义的常量
  3. 被其他类实现

 一个类可以继承一个父类,但是可以实现一个或多个接口, 类实现接口的语法如下:

[修饰符] class 类名 extends 父类 implements 接口1, 接口2, ....
{
	类体部分
}

一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里的所定义的全部抽象方法(也就是重写这些抽象方法);
否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义为抽象类。

抽象类和接口的比较

相同之处

  1. 接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。
  2. 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

差别

  1. 接口里只能包含抽象方法、静态方法和默认方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
  2. 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
  3. 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让子类调用这些构造器来完成属于抽象类的初始化操做。
  4. 接口里不能包含初始化块;但抽象类里完全可以包含初始化块。
  5. 一个类最多只有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多接口,可以弥补Java单继承的不足。

其他

关键字 final:

  1. 用final修饰一个类,则该类不能被继承
  2. 用final修饰一个函数,该函数不能被其子类重写
  3. 用final修饰一个成员变量,则该成员变量的值不允许被改变

注意: 

final成员变量必须在声明时或在构造函数中显式赋值才能使用。一般情况下,在定义时就进行赋值。

Object 类

Object类是所有类的父类,如果没有用extends明确标明,那就默认继承Object类

Object类中的两个方法:
1): toString()

Object类具有toString()方法,你创建的每个类都会继承该方法。它返回对象的一个String表示,并且对于调试非常有帮助。然而对于默认的toString()方法往往不能满足需求,需要覆盖这个方法。

toString()方法将对象转换为字符串。看以下代码

package sample;
class Villain {
    private String name;
    protected void set(String nm) {
       name = nm;
    }
    public Villain(String name) {
       this.name = name;
    }
    public String toString() {
       return "I'm a Villain and my name is " + name;
    }
}
public class Orc extends Villain {
    private int orcNumber;
    public Orc(String name, int orcNumber) {
       super(name);
       this.orcNumber = orcNumber;
    }
    public void change(String name, int orcNumber) {
       set(name);
       this.orcNumber = orcNumber;
    }
    public String toString() {
       return "Orc" + orcNumber + ":" + super.toString();
    }
    public static void main(String[] args) {
       Orc orc = new Orc("Limburger", 12);
       System.out.println(orc);
       orc.change("Bob", 19);
       System.out.println(orc);
    }
}

toString的好处是在碰到“println”之类的输出方法时会自动调用,不用显式打出来

1): equals()

java equals 方法是在 java 中用于将字符串与指定的对象比较,在 String 类中重写了 equals () 方法用于比较两个字符串的内容是否相等,如果给定对象与字符串相等,则返回 true;否则返回 false。

public class Test {
    public static void main(String args[]) {
        String Str1 = new String("runoob");
        String Str2 = Str1;
        String Str3 = new String("runoob");
        boolean retVal;

        retVal = Str1.equals( Str2 );
        System.out.println("返回值 = " + retVal );

        retVal = Str1.equals( Str3 );
        System.out.println("返回值 = " + retVal );
    }
}

输出结果:

返回值 = true

返回值 = true 

更多推荐