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();
}
}
}