Java_2 接口 继承 多态

1.接口的使用

Java语言只支持单重继承,一个类只能有一个父类。java语言提供接口实现类的多重继承功能。

1.1 接口的定义

[修饰符]interface  接口名  [extends  父类接口名列表]{
    [public] [static] [final] 变量;
    [public] [abstract] 方法;
}

定义接口的语法格式的参数说明

例:

public interface ICalculate{
final float PI=3.14159f; //定义用于表示圆周率的常量PI
float getArea(float r); //定义一个用于计算面积的方法getArea()
float getCircumfernce(float r); //定义一个用于计算周长的方法getCircumference()
}

1.2 接口的实现

[修饰符] class <类名> [extend 父类名] [implements 接口列表]{
}

实现接口的语法格式的参数说明

在类中实现接口时,方法名、返回值类型、参数的个数及类型必须与接口中的完全一致,并且必须实现接口中的所有方法。

例:实现上述代码的接口

public class Cire implements ICalculate{
    //定义计算圆面积的方法
    Public float getArea(float r){
        float area = PI*r*r;
        return area;
    }
    //定义计算周长的方法
    public float getCircumference(float r){
        float circumference = 2*PI*r;
        return circumference;
    }
}

在类的继承中,只能做单重继承,而实现接口时,一次则可以实现多个接口,每个接口间使用逗号“,”分割,这时就可能出现变量或者方法名冲突的情况。解决该问题时,如果变量冲突,则需要明确指定变量的接口,可以通过“接口名.变量”实现。如果出现方法冲突,则只要实现一个方法即可。

1.3 例 1:图片的不同格式保存

(1)编写ImageSaver接口,在该接口中定义save()方法。

public interface ImageSaver{
    void save();  //定义save()方法
}

(2)在项目中创建GIFSaver类,该类实现了ImageSaver接口,在实现save()方法时将图片保存为GIF格式。

public class GIFSaver implements ImageSaver{
    @Override
    public void save(){
        System.out.println("将图片保存为GIF格式");
    }
}

例 2:为汽车增加GPS定位功能

(1)在项目中创建Car类,在该类中首先定义两个属性,一个是name(表示汽车的名字),另一个是speed(表示汽车的速度),并为其提供了getXXX()和setXXX()方法,然后通过重写toSteing()方法来方便输出Car对象。

public class Car{
    private String name;
    private double speed;
    //省略getXXX()和setXXX()方法
    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("车名:"+name+",");
        sb.append("速度:"+speed+"千米/小时");
        return sb.toString();
    }
}

(2)在项目中编写接口GPS,在接口中定义了getLocatiion()方法,用来确定汽车的位置。

public interface GPS{
    Point getLocation();   //提供定位功能
}

(3)在项目中编写GPSCar类,该类继承Car并实现GPS接口。在该类中首先实现getLocation()方法,用于实现确定汽车的位置功能,然后重写toString()方法,用于实现确定汽车位置的功能,然后重写toString()方法方便输出GPSCar对象。

public class GPSCar extends Car implements GPS{
    @Override
    public Point getLocation(){
        Point point = new Point();
        point.setLocation(super.getSpeed(),super.getSpeed());
        return point;
    }
    @Override
    public String toString(){
        STringBuilder sb = new StringBuild();
        sb.append(super.toString());
        sb.append(",座标:("+getLocation().x+","+getLocation().y+")");
        return sb.toString();
    }
}

2.类的继承

2.1 继承的实现

在Java语言中,继承通过extends关键字来实现。也就是用extends指明当前类是子类,并指明从哪个类继承而来。即在子类的声明中,通过extends关键字来显示地指明其父类。

[修饰符] class 子类名 extends 父类名{
    类体
}

子类,父类名必选参数,子类名首字母大写,父类用于指定要定义的子类继承与哪个父类。
修饰符,可选参数,用于指定类的访问权限(public abstract final)

父类Bird

public class Brid{
    String color = "白色";    //颜色
    String skin = "羽毛";     //皮毛
}

子类Pigeon

public class Pigeon extends Bird{
    public static void main(String[] args){
        Pigeon pigeon = new Pigeon();
        System.out.println(pigeon.color);  //输出成员变量color
    }
}

2.2 继承中的重写

重写是指父子类之间的关系,当子类继承父类中所有可能被子类访问的成员方法时,如果子类的方法名与父类的方法名相同,那么子类就不能继承父类的方法,此时,称为子类的方法重写了父类的方法。重写体现了子类补充或者修改父类方法的能力。通过重写,可以使一个方法在不同的子类中表现出不同的行为。

例:

(1)创建一个名称为Animal的类,在该类声明的一个成员方法cry()。

public class Animal{
    public Animal(){
    }
    public void cry(){
        Systme.out.println("动物发出叫声!");
    }
}

(2)创建一个Animal类的子类Dog,在该类中重写了父类的成员方法cry()

public class Dog extends Animal{
    public Dog(){
    }
    public void cry(){
        System.out.println("狗发出‘汪汪汪’声!");
    }
}

(3)创建一个Animal类的子类Sheep,在该类中不定义任何方法。

public class Sheep extends Animal{
}

(4)创建一个名称为Zoo的类,在该类的main()方法中分别创建子类Dog、Sheep的对象并为该对象分配内存,然后分别调用各对象的cry()方法。

public class Zoo{
    public static void main(String[] args){
        Dog dog = new Dog();
        System.out.println("执行dog.cry();语句时的输出结果:");
        dog.cry();
        Sheep sheep = new Sheep();
        System.out.println("执行sheep。cry();语句时的输出结果:");
        sheep.cry();
    }
}

由于Dog类重写了父类的方法cry(),所以执行的是子类中的cry()方法,但是Sheep类没有重写方法,所以执行的是父类中的cry()方法。

2.3 使用super关键字

子类可以继承父类的非私有成员变量和成员方法(不是以private关键字修饰的)作为自己的成员变量和成员方法。但是,如果子类中声明的成员变量与父类的成员变量同名,则子类不能继承父类的成员变量,此时称子类的成员变量隐藏了父类的成员变量。如果子类中的声明成员方法与父类的成员方法同名,并且方法的返回值及参数个数和类型也相同,则子类不能继承父类的成员方法,此时称子类的成员方法重写了父类的成员方法。

这时,如果想在子类中访问父类 中被子类隐藏的成员方法或变量,就可以使用super关键字。

super关键字主要的两个用途:

1.调用父类的构造方法

子类可以调用由父类声明的构造方法。但必须在子类的构造方法中使用super关键字来调用

super([参数列表]);

如果父类的构造方法中包括参数,则参数列表为必选项,用于指定父类构造方法的入口参数。

例:

(1)在项目中创建Beast类,在类中添加一个默认的构造方法和一个带参数的构造方法。

public class Beast{
    String skin = "";      //成员变量
    public Beast(){        //默认构造方法
    }
    public Beast(String strSkin){  //带参数的构造方法
        skin = strSkin;
    }
    public void move(){    //成员方法
        System.out.println("跑");
    }
}

(2)如果想在子类Tiger中使用父类的带参数的构造方法,则需要在子类Tiger的构造方法中进行调用

public class Tiger extends Beast{
    public Tiger(){
        super("条纹");     //使用父类的带参数的构造方法
    }
}

2.操作被隐藏的成员变量和被重写的成员方法,也可以使用super关键字

super.成员变量名
super.成员方法名([参数列表])

如果想在子类Tiger的方法中改变父类Beast的成员变量skin的值。
super.skin = “条纹”;

如果想在子类中Tiger的方法使用父类Beast的成员方法move()。
super.move();

2.4 例:经理与员工的差异

(1)在项目中创建Employee类,在该类中定义3个属性,分别是name(表示员工姓名),salary(表示员工的工资)和birthday(表示员工的生日),并分别为他们定义了getXXX()和setXXX()方法。

import java.util.Date;
public class Employee{
    private String name;
    private double salary;
    private Date birthday;

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }
    public double getSalary(){
        return salary;
    }
    public void setSalary(double salary){
        this.salary = salary;
    }
    public Date getBirthday(){
        return birthday;
    }
    public void setBirthday(Date birthday){
        this.birthday = birthday;
    }
}

(2)在项目中创建一个名称为Manager的类,该类继承自Employee。在该类中,定义了一个bonu域,表示经理的奖金,并为其设置了getXXX()和setXXX()方法

public class Manager extends Employee{
    private double bonus;
    public double getBonus(){
        return bonus;
    }
    public void setBonus(double bonus){
        this.bonus = bonus;
    }
}

(3)在项目中在创建一个名称为Test的类,用于测试。在该类中分别创建Empliyee和Manager对象,并为其赋值,然后输出属性。

import java.util.Date;
public class Test{
    public static main(String[] args){
        Employee empioyee = new Employee();
        employee.setName("张三");
        employee.setSalay(1000);
        employee.setBirthday(new Date());
        Manager manager = new Manager();
        manager.setName("李四");
        manager.setSalary(3000);
        manager.setBirthday(new Date());
        manager.setBonus(2000);

        System.out.println("员工的姓名:"+employee.getName());
        System.oout.println("员工的工资:"+employee.getSalary());
        System.out.println("员工的生日:"+employee.getBirthday());
        System.out.println("经理的姓名:"+manager.getName());
        System.out.println("经理的工资:"+manager.getSalary());
        System.out.println("经理的生日:"+manager.getBithday());
        System.out.println("经理的奖金:"+manager.getBonus());
    }
}

2.5 例:重写父类中的方法

在继承一个类后,就可以使用父类中定义的方法,然而父类中的方法可能并不完全适用于子类。此时,如果不想定义新的方法,则可以重写父类中的方法。

(1)在项目中创建Employee类,在该类中添加getInfo()方法,返回值为字符串“父类:我是这里的员工!”。

public class Employee{
    public String getInfo(){
        return"父类:我是这里的员工!";
    }
}

(2)在包中在创建一个名称为Manager的类,该类继承自Employee。在该类中,重写getInfo方法。

public class Manager extends Employee{
    @Override
    public String getInfo(){
        return"子类:我是这里的经理!";
    }
}

3.多态

在Java语言中,通常使用方法的重载和重写实现类的多态性

重写之所以具有多态性,是因为父类的方法在子类中被重写,子类和父类的方法名称相同,但完成的功能却不一样,所以说,重写也具有多样性。

方法的重载是值在一个类中出现多个方法名相同,但参数个数或参数类型不同的方法,则称为方法的重载。Java语言在执行具有重载关系的方法时,将根据调用参数的个数和类型区分具体执行的是哪个方法。

例:

public class Calculate{
    final float PI = 3.14159f;

    public float getArea(float r){
        float area = PI*r*r;
        return area;
    }
    public float getArea(float l,float w){
        float area = l*w;
        return area;
    }

    public void draw(int num){
        System.out.println("画"+num+"个任意形状的图形");
    }
    public void draw(String shape){
    System.out.println("画一个"+shape);
    }
    public static void main(String[] args){
    Calculate calculate = new Calculate();
    float l = 20;
    float w = 30;
    float areaRectangle = calculate.getArea(l,w);
    System.out.println("求长为"+l+" 宽为"+w+"的矩形的面积是:"+areaRectangle);
    float r = 7;
    float areaCirc = calculate.getArea(r);
    System.out.println("求半径为"+r+"的圆的面积是:"+areaRectangle);
    int num = 7;
    calculate.draw(num);
    calculate.draw("三角形");
    }
}

重载的方法之间并不一定必须有联系,但是为了提高程序的可读性,一般只重载功能相似的方法。在进行方法的重载时,方法的返回值的类型不能作为区分方法的标志。

3.3 简单的汽车销售商城

(1)在项目中创建一个抽象类,名称为Car,在该类中定义一个抽象方法getInfo()。

public abstract class Car{
    public abstract String getInfo();
}

(2)在项目中创建一个名称为BMW的类,该类继承自Car并实现了其getInfo()方法。

public class BMW extends Car{
    @Override
    public String getInfo(){
        return "BMW";
    }
}

在项目中创建一个名称为Benz的类,该类继承自Car并实现了其getInfo()方法

public class Benz extends Car{
    @Ocerride
    public String getInfo(){
    if(name.equalslgnoreCase("Bmw")){
        return new BMW();
    }else if(name.equalslgnoreCase("Benz")){
        return new Benz();
    }else{
        return null;
    }
    }
}

(5)在项目中创建一个名称为Customer的类,用来进行测试,在main()方法中,根据用户的需求提取不同的汽车。

public class Customer{
    public static void main(String[] aargs){
        System.out.println("顾客要购买BMW:");
        Car bmw = CarFactory.getCar("BMW");
        System.out.println("要提取汽车:"+bmw.getInfo());
        System.out.println("顾客需要购买Benz:");
        Car benz = CarFactory.getCar("Benz");
        System.out.println("提取汽车:"+benz.getInfo());     
    }
}

4.经典范例

4.1 使用Comparable接口自定义排序

在项目中创建Employee类,在该类中首先定义3个属性,分别为id(员工的编号)、name(员工的姓名)和age(员工的年龄),然后在构造方法中初始化这3个属性,最后在实现接口中定义的compareTo()方法,经对象按照编号进行升序排列。

public class Employee inplements Comparable<Employee>{
    private int id;
    private String name;
    prinate int age;
    public Employee(int id,String name,int age){
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Employee o){
        if(id>o.id){
            return 1;
        }else if(id<o.id){
            return -1;
        }
        return 0;
    }
    @Ocerride
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("员工的编号:"+id+",");
        sb.append("员工的姓名:"+name+",");
        sb.append("员工的姓名:"+age);
        return sb.toString();
    }
}

4.2 动态设置类的私有域

为了保证面向对象的封装特性,通常会将域设置为私有的,然后提供对应的getXXX()和setXXX()方法来操作该域。然而利用反射机制,就可以在运行是修改类的私有域。

(1)在项目中创建Student类,在该类中定义4个域及对应的getXXX()和setXXX()方法。这四个域分别对应是id(学生的序号)、name(学生的姓名)、male(学生是否为男性)和account(学生的账户余额)。

public class Student{
    private int id;
    private String name;
    private boolean male;
    private double account;
    //getXXX()   setXXX()
}

(2)在包中编写Test类进行测试。在main()方法中,分别为不同的域设置不同的值,并输入初始值和信值作为对比。

public class Test{
    public static void main(String[] args){
        Student student = new Student();
        Class<?>class = student.getClass();
        System.out.println("类的标准名称:"+clazz.getCanonicalName());
        try{
            Field id = clazz.getDeclaredField("id");
            System.out.println("设置前的id:"+student.getId());
            id.setAccessible(true);
            id.setInt(student,10);
            System.out.println("设置后的id:"+student.getId());

            Field name = clazz.getDeclaredField("name");
            System.out.println("设置前的name:"+student.getName());
            name.setAccessible(true);
            name.set(student,"Java");
            System.out.println("设置后的name:"+student.getName);

            Field male = clazz.getDeclaredField("made");
            System.out.println("设置前的male:"+student.isMale());
            male.setAccessible(true);
            male.setBoolean(student,true);
            System.out.println("设置后的male:"+student.isMale());

            Field account = clazz.getDeclareField("account");
            System.out.println("设置前的account:"+student.getAccount());
        }catch(SecurityException e){
            e.printStackTraace();
        }catch(NoSuchFieldException e){
            e.printStackTRACE();
        }catch(IllegalArgumentException e){
            e.printStackTrace();
        }catch(IllgalAccessException e){
            e.printStackTrace();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章