面向物件更重要的是思想,具體的代碼使用點很簡單,我們先介紹幾個概念,使用點之后一起介紹
一、面向物件的三大特性
面向物件三大特性-----封裝、繼承、多型是面向物件的中心(有的人會把抽象也加上),我們分別介紹一下
封裝
字面意思就是把資料封裝起來,不讓外部進行訪問,封裝的是什么資料,自然就是你不想讓別人知道的,比如:楊冪的年齡,羅志祥的運動量,馬云的資產及怎么賺到的等等,對應到程式那就是隱藏物件內部的復雜性,只對外公開簡單的方法,便于外界呼叫,
具體操作就是將屬性進行私有化,提供一個公共的方法用于屬性的訪問,也就是getter/setter方法,而且訪問方法里面可以實作更加復雜細致的內部邏輯判斷,比如訪問美女的年齡,如果超過30就說自己20,
封裝的特性可以保護資料的安全性:在面向程序設計中,都是圍繞資料結構和演算法展開的,資料結構是公開的,任何人都能訪問,萬一哪天要離職的人和剛入職的新人有意無意將資料進行訪問并將其修改,如果修改錯亂了之后,程式業務必然會出錯或者崩潰,
? 而在面向物件設計中,由于類將屬性進行了封裝,不會暴露具體的屬性,也就只能通過提供的外部方法進行訪問,這樣操作屬性的手段是統一的,不會造成資料錯亂,
還可以隔離復雜度:在代碼中可以將某個代碼塊變成方法,抽象出某些工具類,供不同的程式進行呼叫,
使用的時候直接呼叫對應的功能方法,不用關心具體的實作,就比如現實世界開空調,你只按了一下遙控器,就啟動了空調制冷功能,而具體制冷的程序實作你沒關心過,
用一段代碼演示一下有封裝和無封裝的例子
//顧客
public class Customer {
String name;
double balance; //余額
public Customer() {
}
public Customer(String name, double balance) {
this.name = name;
this.balance = balance;
}
}
//收銀員
class Cashier {
public static void main(String[] args) {
Customer customer = new Customer("jack", 100);
double price = 50;
if (customer.balance > price) {
//由于余額是公開的,收銀員直接訪問余額多少錢然后進行操作,
//如果你的余額很多,有些圖謀不軌的人肯定就針對上你了(把你的錢盜走)
customer.balance -= price;
System.out.println("付款成功!");
} else {
System.out.println("付款失敗!");
}
System.out.println("剩余:" + customer.balance);
}
}
而面向物件的思維的代碼如下:
//顧客
public class Customer {
private String name;
private double balance;
public Customer() {
}
public Customer(String name, double balance) {
this.name = name;
this.balance = balance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public boolean payment(double money) {
if (money > balance) {
return false;
} else {
balance -= money;
return true;
}
}
public void show(Customer customer) {
System.out.println("剩余:" + customer.getBalance());
}
}
//訂單
public class Order {
private String name;
private double price;
public Order() {
}
public Order(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
//收銀員
public class Cashier {
private double money;
public Cashier() {
}
public Cashier(double money) {
this.money = money;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
//扣款
public boolean deduction(Cashier cashier,Order order,Customer customer){
cashier.setMoney(order.getPrice());
boolean result = customer.payment(cashier.getMoney());
if(result){
System.out.println("付款成功!");
}
else {
System.out.println("付款失敗!");
}
return result;
}
}
//測驗類
public class Test {
public static void main(String[] args) {
Customer customer=new Customer("jack",5000);
Order order=new Order("p40",4488);
Cashier cashier=new Cashier();
cashier.setMoney(order.getPrice());
//這里收銀員就只能呼叫收款方法,方法里面只把訂單價格和顧客傳入里面,剩下的就只能對應的角色才能看到
cashier.deduction(cashier,order,customer);
customer.show(customer);
}
}
封裝的作用:保護資料的安全性和隔離復雜度
繼承
單從字面意思理解的話,可能會有些歧義,先和生物學做類比:程式中的繼承就相當于生物學中的遺傳相似的地方,而生物學中的遺傳變異的部分對應程式中就是重寫或者子類添加的屬性和方法,
在現實的認知中,繼承大多數指代父母遺留的事業、財產之類的,比如:李澤鉅繼承李嘉誠的事業,而對應到面向物件設計中,把“繼承”理解成“遺傳”就能更好的理解,生物學中的遺傳都能理解,比如:兒子和父親某些地方很像,比如五官的某些特點等等,有像的地方就有異的地方也就是“變異”,對應程式中也是如此,多個類有共同的屬性和方法時,就可以抽取成子類和父類,然后讓子類繼承父類,就代表子類與父類具有某些相同的屬性和方法;而那些異的地方就需要子類進行對應的更改,
另外,之前說過類是對物件的抽象,而現在的繼承是對類的抽象,抽象和繼承是前后銜接的關系,先有抽象,通過抽象得出類,后通過繼承來表達抽象結果
對前面說的舉個例子:教師、員工、學生有共同的特點,比如:都有姓名、年齡,都要吃飯睡覺等,但是也有不同的地方,教師有教師編號,要教學;學生有學生編號,要學習;員工有員工編號,要作業,
教師:
屬性:姓名、年齡、教師編號
方法:吃飯、睡覺、教學
員工:
屬性:姓名、年齡、員工編號
方法:吃飯、睡覺、作業
學生:
屬性:姓名、年齡、學生編號
方法:吃飯、睡覺、學習
優化一下:它們首先都可以抽象為人類,然后具有相同屬性和方法的地方就可以抽取到人類中,不同的地方就單獨寫一個子類繼承父類,這樣就繼承了父類所有的屬性和方法

人類:
屬性:姓名、年齡
方法:吃飯、睡覺
教師繼承人類:
屬性:教師編號
方法:教學
員工繼承人類:
屬性:員工編號
方法:作業
學生繼承人類:
屬性:學生編號
方法:學習
就把姓名、年齡、吃飯、睡覺抽取為父類,然后教師、員工、學生繼承人類就有了姓名、年齡、吃飯、睡覺資訊,然后可以子類對應更改屬性、方法
//人類
public class Person {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat(){
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
}
//教師繼承人類
public class Teacher extends Person{
private Integer teacherId;
public Integer getTeacherId() {
return teacherId;
}
public void setTeacherId(Integer teacherId) {
this.teacherId = teacherId;
}
public void teach(){
System.out.printf("%s教學,編號:%d",super.getName(),teacherId);
}
}
//學生繼承人類
public class Student extends Person{
private Integer studentId;
public Integer getStudentId() {
return studentId;
}
public void setTeacherId(Integer studentId) {
this.studentId = studentId;
}
public void study(){
System.out.printf("%s學習,編號:%d",super.getName(),studentId);
}
}
//員工繼承人類
public class Employee extends Person {
private Integer empno;
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public void work() {
System.out.printf("%s作業,編號:%d,年齡:%d", super.getName(), empno, super.getAge());
}
}
//測驗類
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setTeacherId(42);
teacher.setName("James");
teacher.teach();
System.out.println();
Student student = new Student();
student.setTeacherId(18);
student.setName("張三");
student.study();
System.out.println();
Employee emp = new Employee();
emp.setEmpno(1001);
emp.setName("smith");
emp.setAge(18);
emp.work();
}
}
/*
執行結果:
James教學,編號:42
張三學習,編號:18
smith作業,編號:1001,年齡:18
*/
再舉個子類重寫的例子
//體育比賽,會有很多專案,先只寫一個大概的方法,可以理解成占位作用
public class SportsCompetition {
private String name;
public void playSports(String name){
System.out.printf("進行%s專案比賽!",name);
}
}
//田徑比賽,重寫父類的playSports方法,等待實際使用的時候傳此方法的實參
public class TrackFieldCompetition extends SportsCompetition{
@Override
public void playSports(String name) {
//此方法表示呼叫父類的方法,但傳入的是子類自己的引數
super.playSports(name);
}
}
//球類比賽,同樣重寫父類的playSports方法,等待實際使用的時候傳此方法的實參
public class BallGame extends SportsCompetition {
@Override
public void playSports(String name) {
super.playSports(name);
}
}
//測驗類
TrackFieldCompetition competition=new TrackFieldCompetition();
competition.playSports("田徑");
BallGame competition2=new BallGame();
System.out.println();
competition.playSports("球類");
/*
執行結果:
進行田徑專案比賽!
進行球類專案比賽!
*
總結:繼承就是表示子類和父類的關系,當多個類有相同的屬性和方法時,就對應的抽取成父類,然后子類繼承父類,就擁有父類的所有資訊,即:子類與父類是is-a的關系

繼承的好處:1. 提高代碼的復用性;2. 提高代碼的可維護性和擴展性;是多型的前提
多型
還是和生物學做類比,生物中多型指生物體或物種有許多不同的展現形式或階段,在面向物件中的含義其實也是如此,就是指呼叫同一個方法,不同的物件產生不同的結果,上面的體育比賽案例其實就可以運用多型,我們先用多型重新測驗一下
//父類參考指向子類物件
SportsCompetition competition1=new TrackFieldCompetition();
competition1.playSports("田徑");
SportsCompetition competition2=new BallGame();
competition3.playSports("球類");
SportsCompetition中定義的playSports方法,傳入了一個引數name,但是實際使用的時候,還是要看傳入的實參是TrackFieldCompetition物件還是BallGame物件
再寫一個案例
public class Animal {
public void talk() {
System.out.println("xxx叫!");
}
}
public class Dog extends Animal {
@Override
public void talk() {
System.out.println("Dog...wangwangwang~~~");
}
}
public class Cat extends Animal {
@Override
public void talk() {
System.out.println("Cat....miaomiaomiao~~~");
}
}
測驗類
public class Test {
public static void main(String[] args) {
test(new Dog());//呼叫的實參是Dog物件
test(new Cat());//呼叫的實參是Cat物件
}
//形參是animal物件,根據傳進的實參是哪個物件判斷呼叫哪個方法
public static void test(Animal animal){
animal.talk();
}
}
/*
執行結果:
Dog...wangwangwang~~~
Cat....miaomiaomiao~~~
*
如果不用多型的話,代碼就變成這樣
public class Test2 {
public static void main(String[] args) {
test(new Dog());
test(new Cat());
}
public static void test(Dog dog){
dog.talk();
}
public static void test(Cat cat){
cat.talk();
}
}
上面為啥test方法名可以相同,那是因為形參串列不同,構成了多載,如果不用多載的話方法每次都要起不一樣的比如testDog,testCat等等:,萬一有100個動物呢?那起名字就浪費點時間
顯然Test的代碼比Test2的代碼要簡潔一些,而且如果用Test2的代碼的話,如果有Pat,Chicken等動物子類的話,都需要添加對應的方法,即:再添加對應的test方法,這樣代碼重復度就有點高了,違反了面向物件的設計原則:開閉原則(面向擴展開放,面向修改關閉),如果用Test的代碼增加新的子類時,呼叫者不用更改代碼就能適用,即:不用更改test方法的代碼,
總結一下:
多型的要素:繼承,重寫,父類參考指向子類物件
多型的好處:實作代碼的通用性;提高程式的可擴展性
多型的應用:父類當做方法的形參,傳入具體的子類的物件;父類當做方法的回傳值,回傳的是具體的子類的物件
介面當做方法的形參,傳入具體的實作類的物件;介面當做方法的回傳值,回傳的是具體的實作類的物件
二、抽象類
像上面的體育比賽類和Animal類本身沒有什么作用也沒必要進行實體化,就是給子類提供了一個模板,也可以說是占位,
那么其實可以設計成抽象類,然后讓子類繼承這樣的抽象類
//體育比賽,abstract class就表示這個類是抽象類
public abstract class SportsCompetition {
//方法加abstract修飾,即:這是一個抽象方法,只包含定義方法,沒有實作,即方法沒有大括號,
public abstract void playSports(String name);
}
//田徑比賽,實作SportsCompetition中定義的playSports方法
public class TrackFieldCompetition extends SportsCompetition {
@Override
public void playSports(String name) {
System.out.printf("進行%s專案比賽!",name);
}
}
//球類比賽,也實作SportsCompetition中定義的playSports方法
public class BallGame extends SportsCompetition {
@Override
public void playSports(String name) {
System.out.printf("進行%s專案比賽!",name);
}
}
//測驗結果和上面一樣
再修改一下Animal的那個案例
public abstract class Animal {
public abstract void talk();
}
剩下的Dog和Cat與上面的代碼相同,使用也不變,最后的執行結果也相同
總結:抽象類的作用就是為子類提供了一個抽象模板,子類重寫對應的抽象方法然后可以在它的基礎上進行擴充實作,
三、介面
說到介面,在現實都見到過各種各樣的USB介面,比如:



還有其它各式各樣的介面,那到底什么是介面呢?
介面英譯interface,而interface翻譯過來還有界面,埠,介面的意思,綜合一下你就可能理解了,我們小學的時候數學老師就教過我們點連成線,線連成面;你看上面圖片的介面是不是由許多點組成,這些點其實就是一個個功能點;至于界的含義呢,也很簡單,就是組成面之后如果單獨存在就沒意義了,一定需要與設備進行連接才能發揮出特定的功能,在這里就可以把這個用來連接設備的東東看作是連接的媒介,把這兩個定義分析之后組成一起就是界面了,翻譯為介面可能主要原因是:它是用來連接設備的,以此產生特定的功能,比如充電介面與手機連接就是開啟了充電功能,在程式中就是呼叫了充電的方法,
我們一句話再闡述一下介面的概念:是相關互動功能點定義的集合,再分別解釋一下
- 相關:一個介面包含的功能點是單一相關的,比如:僅支持USB協議或者VGA協議等等
- 互動:就是我上面文字描述的介面肯定要與具體的設備產生連接才能產生作用
- 功能點:還是文字描述的每一個點點都是特定的功能組成
- 定義:介面只提供了一個規范約定,不包含實作,就比如一個電腦USB插槽可以接鍵盤、滑鼠、U盤等,它們符合USB規范/協議就可以互聯,并正常通信,至于這個電腦、以及其他設備是哪個廠家制造的,內部是如何實作的,我們都無需關心,
- 集合:可以有多個功能點組成一個介面
舉個例子
public interface USB {
//傳輸資料
public void transferData();
//規范設備連接
public void standardizeDeviceConnection();
//供電
public void poweredBy();
}
對應一下:相關功能點的集合就是上面的介面定義的一個個方法,而且符合USB協議
? 互動就是訪問修飾符是public,即符合這個介面規范,實作此介面的物件都可以訪問
? 定義就是一個個的功能只包含定義方法,沒有實作,即方法沒有大括號,也就是抽象方法
針對介面必定有實作,比如U盤實作USB介面規范,就有了USB所定義的方法能力,即has-a的關系,
//U盤實作USB介面,就具備了介面定義的傳輸資料,規范設備連接,供電的能力,實作類針對自身進行改動即可
public class UDisk implements USB {
@Override
public void transferData() {
System.out.println("U盤傳輸檔案~~~");
}
@Override
public void StandardizeDeviceConnection() {
System.out.println("U盤連接設備~~~");
}
@Override
public void poweredBy() {
System.out.println("U盤與設備進行供電");
}
}
總結:介面就是一個規范、能力(這點也是介面的核心,隨著代碼量時間的積累會掌握更加深刻)
拓展:介面和抽象類乍一看達到的目標差不多,但差異的根本就是:抽象類脫離不了繼承,而繼承是is-a,關注的是類之間的關系,而介面是has-a的關系,更關注規范和實作,設計的時候就看類之間是什么關系,
下圖就簡單描述了介面的has-a和抽象類的is-a的區別

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/228905.html
標籤:面向對象
上一篇:Java面向物件基礎總結
