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