詳解Java中的封裝、繼承、多態(tài)
封裝
在如何理解面向?qū)ο筮@篇文章中,提到所謂的封裝就是“功能都給你做好了,你不必去理解它是怎么寫出來(lái)的,直接使用即可。”。但你得清楚一點(diǎn),那就是這句話是相對(duì)于使用者來(lái)說(shuō)的,而作為開發(fā)者,封裝就得我們自己來(lái)干。
那么作為開發(fā)者,我們應(yīng)該如何去封裝呢?其實(shí)你應(yīng)該反過(guò)來(lái)問(wèn),他們應(yīng)該如何去使用,這樣一想會(huì)簡(jiǎn)單很多,作為使用者,自然是希望越簡(jiǎn)單越好,也就是說(shuō),一些復(fù)雜的東西,我們不應(yīng)該讓使用者去操作,那也就是說(shuō)我們應(yīng)該把復(fù)雜的,以及不必要的參數(shù)給它封死,不讓使用者去操作。
為什么不讓使用者去操作?
因?yàn)橥褂谜呤遣惶珜I(yè)的,如果暴露太多的接口給他們,就很有可能出現(xiàn)一些稀奇古怪的問(wèn)題,好比一個(gè)不會(huì)做水煮魚的,如果讓他去做那肯定是不好的,那怎么辦,給他買一包水煮魚的調(diào)料,讓他直接放進(jìn)鍋里就好,這樣就減少了不必要的麻煩。我們封裝程序也是這樣,把復(fù)雜的代碼封死,不讓操作者去操作,以免出錯(cuò)。
比如下面這個(gè)例子:
class Average{
private int[] fractions = new int[3]; //分?jǐn)?shù)
private int average = 0; //平均分
public void setFraction(int[] fraction){
fractions = fraction;
}
public double getAverage(){
for(int cell:fractions){
average += cell;
}
return (double) (average / fractions.length);
}
}
class app{
public static void main(String[] args){
int[] a = {50,40,50};
Average average = new Average();
average.setFraction(a); //設(shè)置分?jǐn)?shù)
double n = average.getAverage(); //獲取平均分
System.out.println(average.average); //報(bào)錯(cuò)
System.out.println(n); //46.0
}
}
提示:Java通過(guò)private設(shè)置私有變量,通過(guò)public將變量設(shè)置成公開的。
這里我們之所以將分?jǐn)?shù)和平均分設(shè)置成私有變量是為了防止使用者誤操作,而且也不必讓使用者知道有這么一個(gè)變量,只需要讓使用者知道怎么去設(shè)置分?jǐn)?shù),和獲取平均分就好了。
當(dāng)然這只是一個(gè)很基礎(chǔ)的封裝,如果想封裝出一個(gè)好的程序,還得多費(fèi)一些心思。
繼承
拿貓和狗來(lái)說(shuō),它們都是動(dòng)物,而且它們有一些共同點(diǎn),比如:名字,年齡,聲音,吃等。把這段話寫成代碼就是下面這個(gè)樣子。
class Animal{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}
class Cat extends Animal{
public void voice(){
System.out.println(super.getName() + " 喵");
}
public void eat(){
System.out.println(super.getName() + " fish");
}
}
class Dog extends Animal{
public void voice(){
System.out.println(super.getName() + " 汪");
}
public void eat(){
System.out.println(super.getName() + " Bone");
}
}
class app{
public static void main(String[] args){
Cat cat = new Cat();
cat.setName("貓大王"); //Cat本身沒(méi)有setName方法,但是它的基類有,所以java解析器會(huì)到Cat的基類那里拿
cat.voice();
Dog dog = new Dog();
dog.setName("大黑");
dog.setAge(13);
dog.voice();
System.out.println(dog.getName() + dog.getAge());
}
}
------Output------
貓大王 喵
大黑 汪
大黑13
提示:Java通過(guò)extends關(guān)鍵字來(lái)實(shí)現(xiàn)繼承,父類中通過(guò)private定義的變量和方法不會(huì)被繼承,也就是你不能在子類中直接操作父類通過(guò)private定義的變量以及方法。
在上面代碼中,我們可以看到,Cat和Dog并沒(méi)有定義setName、setAge、getName、getAge方法,但是我們依然可以在Cat和Dog類中使用,這是因?yàn)槲覀兺ㄟ^(guò)extends關(guān)鍵字繼承了Animal類,因此在Animal中定義的變量和方法,我們可以在子類中直接使用,除private定義的變量和方法。
反過(guò)來(lái)說(shuō),姓名和年齡是貓和狗的基本信息也是它們的共同特性。
重寫父類方法或變量
一般重寫父類方法,是因?yàn)槟惆沿埉?dāng)成是一個(gè)基類,而將狗繼承自貓類??此七@很好笑,但如果你去翻翻你的代碼,這種情況多如牛毛。當(dāng)然,如果你不需要繼承,那就另說(shuō)了。那么如果碰到這種情況,我們?cè)趺粗貙懟惸??很?jiǎn)單,在子類中定義一個(gè)和父類中一樣的方法,如下面這樣:
class Animal{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}
class Dog extends Animal{
public String getName(){
return super.getName() + "2";
}
public void voice(){
System.out.println(super.getName() + " 汪");
}
public void eat(){
System.out.println(super.getName() + " Bone");
}
}
class app{
public static void main(String[] args){
Dog dog = new Dog();
dog.setName("大黑");
System.out.println(dog.getName()); //執(zhí)行的是Dog中的getName方法
}
}
提示:通過(guò)super可以在子類中直接調(diào)用父類的方法以及變量,通過(guò)this調(diào)用當(dāng)前類。
我覺得把這叫做重寫不太好,因?yàn)槿绻麖谋举|(zhì)來(lái)講,它不算重寫,只是Java尋找變量以及方法的規(guī)則罷了。Java會(huì)先看一下,自己身上有沒(méi)有某個(gè)變量或方法,如果沒(méi)有,它會(huì)接著到父類中找,如果父類中還是沒(méi)有,那么它又會(huì)到更上一級(jí)中找,如果一直找上去都沒(méi)有,那么才報(bào)錯(cuò)。
在重寫父類時(shí),需要注意一下,重寫時(shí),方法的返回值類型必須和父類中定義的一致,如果是數(shù)字類型,只要重寫時(shí)類型不大于父類中定義的,那么也是可以的。比如下面這樣就會(huì)報(bào)錯(cuò)
class Animal{
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
class Dog extends Animal{
public int getName(){ //和父類中的getName返回值不同,報(bào)錯(cuò)
return 123;
}
}
class app{
public static void main(String[] args){
Dog dog = new Dog();
System.out.println(dog.getName());
}
}
另外還需要注意,如果重寫時(shí),和父類中的參數(shù)不一致,則會(huì)發(fā)生意想不到的事,比如下面這個(gè)
class Animal{
private String name;
public void setName(String name){
this.name = name;
}
public String getName(String hello){
return this.name + hello;
}
}
class Dog extends Animal{
public String getName(){
return "123";
}
}
class app{
public static void main(String[] args){
Dog dog = new Dog();
dog.setName("大黑");
System.out.println(dog.getName("hello"));
}
}
------Output------
大黑hello
可以看到當(dāng)我們給getName傳達(dá)了參數(shù)時(shí),執(zhí)行的是Animal中的方法,而非Dog中的getName方法,也就是說(shuō)如果參數(shù)不一致最后執(zhí)行的可能就不是重寫的那個(gè)方法。另外也不可將父類公開的方法或變量改成私有(如將public改成private),否則也會(huì)報(bào)錯(cuò),我估計(jì)是Java有一套覆蓋規(guī)則,如果沒(méi)有達(dá)到條件就不會(huì)進(jìn)行覆蓋。
總結(jié)來(lái),覆蓋父類方法或變量時(shí),對(duì)其只能更寬松,而反過(guò)來(lái)則不行。
多態(tài)
先來(lái)幾個(gè)例子,再講理論
class Animal{
public int age = 5;
public int getAge(){
return age;
}
}
class Dog extends Animal{
public int age = 8;
public int getAge(){
return age + 2;
}
}
class app{
public static void main(String[] args){
Animal dog = new Dog();
System.out.println(dog.age);
}
}
------Output------
5
看Animal dog = new Dog();這么一句話,可以發(fā)現(xiàn)它們的類型并不一樣,但卻可以正常運(yùn)行,之所以可以運(yùn)行是因?yàn)椋珼og類是Animal的子類,而父類是包括子類的。我們說(shuō)動(dòng)物,那么狗是不是就是動(dòng)物中的一員呢,這是肯定的,而這里之所以如果運(yùn)行也正是這個(gè)理。
不過(guò)需要注意一下,通過(guò)這種方式創(chuàng)建的對(duì)象,在獲取實(shí)例變量時(shí),獲取到的是父類中的實(shí)例變量,如果是方法,則看子類中是否存在和父類中同名的方法,如果存在則使用子類中的方法,但是如果子類中有某個(gè)方法,而父類中沒(méi)有,那么就會(huì)報(bào)錯(cuò)。如下這段代碼就會(huì)報(bào)錯(cuò)
class Animal{
public int age = 5;
public int getAge(){
return age;
}
}
class Dog extends Animal{
public int age = 8;
public int getAge(){
return age + 2;
}
public setAge(int a){
this.age = a;
}
}
class app{
public static void main(String[] args){
Animal dog = new Dog();
System.out.println(dog.setAge(5));
}
}
因?yàn)楦割愔袥](méi)有setAge這個(gè)方法,因此會(huì)報(bào)錯(cuò)。
也就是說(shuō),通過(guò)這種方式來(lái)寫,只能達(dá)到覆蓋方法的效果,沒(méi)有其他的功能。
這里所謂的多態(tài),在程序中你可以理解成,一個(gè)方法,它可以有不同的效果,那怎么實(shí)現(xiàn)不同的效果呢?在java中通過(guò)切換類型來(lái)實(shí)現(xiàn)(不一定正確)。
多態(tài)有什么用?
還是再來(lái)看幾個(gè)例子吧
class Animal{
public int age = 5;
public int getAge(){
return age;
}
}
class Dog extends Animal{
public int getAge(){
return age + 2;
}
}
class Cat extends Animal{
public int getAge(){
return age + 3;
}
}
class app{
public static void main(String[] args){
Animal dog = new Dog();
Animal cat = new Cat();
System.out.println(dog.getAge());
System.out.println(cat.getAge());
}
}
------Output------
7
8
可以看到,它會(huì)根據(jù)自身執(zhí)行不同的方法。不過(guò)話說(shuō)回來(lái),這并不能代表什么,畢竟我們按照正常情況來(lái)創(chuàng)建,效果也可以一樣,不過(guò)還真有它的用武之處,比如下面這段代碼
class Animal{
public int age = 5;
public int getAge(){
return age;
}
}
class Dog extends Animal{
public int getAge(){
return age + 2;
}
}
class Cat extends Animal{
public int getAge(){
return age + 3;
}
}
class app{
public static void main(String[] args){
Animal[] animals = new Animal[2];
animals[0] = new Dog();
animals[1] = new Cat();
System.out.println(animals[0].getAge());
System.out.println(animals[1].getAge());
}
}
------Output------
7
8
這段代碼和上面一段差不多,不過(guò)這段代碼中用的是一個(gè)數(shù)組,這種情況就比較適合使用多態(tài)了,不然好像沒(méi)有其他辦法來(lái)弄了吧(初學(xué)java,不太懂)。在這里面多態(tài)不僅僅只是指一個(gè)方法有不同的效果,在這里還指類型的多樣性。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
Java 數(shù)據(jù)結(jié)構(gòu)七大排序使用分析
這篇文章主要介紹了Java常用的排序算法及代碼實(shí)現(xiàn),在Java開發(fā)中,對(duì)排序的應(yīng)用需要熟練的掌握,這樣才能夠確保Java學(xué)習(xí)時(shí)候能夠有扎實(shí)的基礎(chǔ)能力。那Java有哪些排序算法呢?本文小編就來(lái)詳細(xì)說(shuō)說(shuō)Java常見的排序算法,需要的朋友可以參考一下2022-04-04
RestTemplate報(bào)錯(cuò)I/O?error?on?POST?request?for的解決辦法
這篇文章主要給大家介紹了關(guān)于RestTemplate報(bào)錯(cuò)I/O?error?on?POST?request?for的解決辦法,文中通過(guò)代碼實(shí)例將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
詳解spring-cloud與netflixEureka整合(注冊(cè)中心)
這篇文章主要介紹了詳解spring-cloud與netflixEureka整合(注冊(cè)中心),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
RocketMQ源碼分析之Broker過(guò)期消息清理機(jī)制
這篇文章主要為大家介紹了RocketMQ源碼分析之Broker過(guò)期消息清理機(jī)制示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
mongo分布式鎖Java實(shí)現(xiàn)方法(推薦)
下面小編就為大家?guī)?lái)一篇mongo分布式鎖Java實(shí)現(xiàn)方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07

