今天的主要內(nèi)容是構(gòu)造方法娘纷,static用法嫁审,繼承,多態(tài)赖晶,抽象類律适,接口。由于從今天開始JAVA的學習基本上都是面向?qū)ο蟮膬?nèi)容遏插,雖然這些內(nèi)容底層的實現(xiàn)是由C++寫的編譯器實現(xiàn)的捂贿,在用法上與C語言已經(jīng)大相徑庭,故不再比較二者的差異胳嘲。開始闡釋JAVA的知識點為主厂僧。
構(gòu)造方法
所謂構(gòu)造方法,就是在創(chuàng)建對象時自動執(zhí)行的方法了牛,主要用于對成員變量的初始化颜屠,當然也可以用于對運行環(huán)境的初始化,如加載驅(qū)動等鹰祸。
- 空參構(gòu)造
空參構(gòu)造即以不傳遞參數(shù)的方式下創(chuàng)建對象甫窟。如Demo d = new Demo(); 就是用空參構(gòu)造的方式創(chuàng)建對象,Demo后面的括號蛙婴,就是用來傳遞參數(shù)的粗井,當其為空時,便是空參構(gòu)造。 - 空參構(gòu)造方法
在類中編寫的通過無參構(gòu)造創(chuàng)建對象系統(tǒng)自動執(zhí)行的方法浇衬。 - 空參構(gòu)造方法沒有返回值懒构,連void也沒有
- 空參構(gòu)造方法與類同名
class Demo_constructor {
public static void main(String[] args) {
Person p = new Person();
System.out.println(p.name+"..."+p.age); //張三...18
}
}
class Person{
String name;
int age;
public Person(){
name = "張三";
age = 18;
}
}
代碼中通過空參構(gòu)造方法Person(),對類中的name和age賦值為張三和18径玖,在主方法中new一個Person對象的時候沒有傳遞參數(shù)痴脾,系統(tǒng)自動執(zhí)行空參構(gòu)造方法,對象中的name和age便有了初值張三和18梳星;
- 有參構(gòu)造方法
當創(chuàng)建對象時傳遞參數(shù)赞赖,便自動執(zhí)行有參構(gòu)造方法。
有參構(gòu)造方法與無參大致相同冤灾,只需在編寫時寫上參數(shù)列表即可前域。
class Demo_constructor {
public static void main(String[] args) {
Person p = new Person("張三",18);
System.out.println(p.name+"..."+p.age); //張三...18
}
}
class Person{
String name;
int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
}
當局部變量名與成員變量名的相同時,局部變量會隱藏成員變量韵吨,但在開發(fā)中常要求見名知意匿垄,對于這種情況,可以用this.來引用成員變量归粉,與局部變量區(qū)分開來椿疗。
- 在寫類的時候,倘若沒有寫構(gòu)造方法糠悼,系統(tǒng)會自動加上空參構(gòu)造届榄,如果寫了有參構(gòu)造方法,沒寫空參構(gòu)造方法倔喂,系統(tǒng)不再加上空參構(gòu)造铝条,在創(chuàng)建對象時便不可以用空參的方式創(chuàng)建,因此席噩,在寫完有參構(gòu)造方法后盡量將空參構(gòu)造方法也寫上班缰。
static
static是靜態(tài)的意思,在類中定義static成員變量和static成員方法悼枢,這些方法是存放在內(nèi)存的方法區(qū)中埠忘,在創(chuàng)建對象的時候,不需對這些變量和方法分配空間萧芙,可以達到資源共享的作用给梅。
- 靜態(tài)成員變量
靜態(tài)成員變量由所有對象共享,在一個對象該變量修改后双揪,所有的對象靜態(tài)變量都會修改,因為他們其實都在引用同一個變量包帚。
class Demo_static {
public static void main(String[] args) {
Person p = new Person();
p.name = "張三";
p.country ="中國";
p.speak(); //張三...中國
Person p2 = new Person();
p2.speak(); //null...中國
p2.country = "America";
p.speak(); //張三...America
}
}
class Person{
String name;
static String country; //在c語言的解釋中就是將country聲明周期延長渔期,實現(xiàn)共享,節(jié)省空間資源
public void speak(){
System.out.println(name+"..."+country);
}
}
從代碼中可以看到,創(chuàng)建對象p之后疯趟,將p的name和country修改為張三和中國拘哨,然后創(chuàng)建對象p2,雖然沒有對p2的屬性進行修改信峻,但由于country是static變量倦青,所以,p2的country也為中國盹舞,而非靜態(tài)變量的name為null产镐。然后p2將其變量country修改為America,p的變量country也變?yōu)榱薃merica踢步。
- 靜態(tài)成員方法
將類中的成員方法修飾為static癣亚,可以在不創(chuàng)建對象的情況下對方法進行引用,節(jié)省空間获印。其實主方法public static void main(String[] arg){}中述雾,main就是JVM識別的入口,聲明為static就可以在不創(chuàng)建該類的情況下調(diào)用主函數(shù)兼丰。
class ArrayTool {
private ArrayTool(){};
//若類只有靜態(tài)方法,要設(shè)置私有構(gòu)造方法,防止創(chuàng)建對象,浪費內(nèi)存
public static int getMax(int[] arr){
int max = arr[0];
for (int i = 1;i < arr.length ;i++ ){
if (arr[i] > max){
max = arr[i];
}
}
return max;
}
}
上面定義的類中含有static方法玻孟,在主函數(shù)中可以不創(chuàng)建該類的對象而直接調(diào)用方法
class Array_Test {
public static void main(String[] args) {
int[] arr = {11,22,33,44,55};
int max = ArrayTool.getMax(arr);
}
}
下面的內(nèi)存分配圖可以幫助我們更好的理解static的機制,了解static各個特點的原理
- 注意事項
- 共性用static,特性用auto
- 靜態(tài)變量又叫類變量,非靜態(tài)變量又叫對象變量
- 靜態(tài)變量隨類產(chǎn)生和消失,非靜態(tài)變量隨對象產(chǎn)生和消失
- 靜態(tài)只能訪問靜態(tài)
繼承
有時候類與類之間有很多相同的成員變量和方法鳍征,倘若全部都要重寫黍翎,代碼的復用性太低,不利于開發(fā)蟆技。怎么通過類與類之間的聯(lián)系玩敏,減少代碼量?繼承由此誕生质礼。
JAVA的繼承通過extends實現(xiàn)旺聚,實現(xiàn)繼承的類成為子類,被繼承的類成為父類眶蕉。子類繼承了父類砰粹,相同的成員變量和方法不需重寫便可使用,大大提高了代碼的復用性造挽。
class Demo_extends {
//讓類與類之間產(chǎn)生關(guān)系,提高代碼的復用性,維護性,也是多態(tài)的前提
public static void main(String[] args) {
Cat c = new Cat();
c.color = "白";
c.legs = 4;
System.out.println(c.color+"..."+c.legs);
}
}
class Animal{
String color;
int legs;
public void sleep(){
System.out.println("sleep");
}
}
class Cat extends Animal{
}
通過extends關(guān)鍵字,Cat類繼承了Animal類碱璃,即使Cat中的代碼塊為空,仍然可以使用Animal中的成員變量和方法饭入。
- JAVA不支持多繼承嵌器,只支持單繼承,即一個子類只有一個父類谐丢,因為當兩個父類中有同名的方式時會造成沖突爽航,造成安全隱患蚓让。
- JAVA在創(chuàng)建子類對象的時候,會先訪問父類的構(gòu)造方法讥珍,在訪問子類的構(gòu)造方法历极。從繼承的設(shè)計理念來不難理解為什么要這么做,因為子類繼承父類是為了使用父類的資源,所以在構(gòu)造子類時要訪問父類衷佃。
class Demo_extends3 {
public static void main(String[] args) {
Son s = new Son(); //這是父類構(gòu)造,這是子類構(gòu)造
} //在構(gòu)造子類時會動訪問父類構(gòu)造
}
class Father{
int num1;
int num2;
public Father(){
System.out.println("這是父類構(gòu)造");
}
}
class Son extends Father{
public Son(){ //所有的子類構(gòu)造方法會默認訪問父類的無參構(gòu)造方法
//super(); //這是一條語句,沒寫會自動加上,用來訪問父類
System.out.println("這是子類構(gòu)造");
}
}
這種機制在父類對象沒有空參構(gòu)造的時候會出現(xiàn)錯誤趟卸,因為系統(tǒng)默認的super();語句是訪問父類的空參構(gòu)造方法,在這種情況下氏义,要在子類的構(gòu)造方法中進行處理锄列,即用super語句訪問父類的有參構(gòu)造方法。
class Demo_extends4 {//關(guān)于父類沒有無參構(gòu)造的解決方法
public static void main(String[] args) {
Son s = new Son(1);
System.out.println("-----------------");
Son s2 = new Son();
}
}
class Father{
int num;
public Father(int num){
System.out.println("父類有參構(gòu)造");
}
}
class Son extends Father{
public Son(){
super(1);
System.out.println("子類無參構(gòu)造");
}
public Son(int num){
super(num);
System.out.println("子類有參構(gòu)造");
}
}
對于在創(chuàng)建的時候觅赊,是否會創(chuàng)建父類對象右蕊,因為要調(diào)用父類構(gòu)造方法,需要一個實例對象吮螺。網(wǎng)上的答案魚龍混雜饶囚,有說要的,有說不要的鸠补。但從我查閱的資料看萝风,在創(chuàng)建子類對象的時候不會創(chuàng)建父類對象。
- 從內(nèi)存的使用來看紫岩,倘若在創(chuàng)建子類對象的時候創(chuàng)建父類對象规惰,所有的對象都是Object的子類對象,那么內(nèi)存必然會被各種Object對象充斥泉蝌,可用內(nèi)存少之又少歇万,幾乎不可能有可用內(nèi)存。這與我們的顯示顯然是沖突的勋陪。
-
但是要使用父類構(gòu)造方法贪磺,必須有一個實例,這個實例在哪诅愚?在知乎一個回答中看到寒锚,父類,子類的實例其實是同一個违孝,也就是說在訪問父類方法的時候刹前,將該實例作為父類對象,在訪問子類構(gòu)造方法的時候雌桑,又把這個實例當做子類對象喇喉。這個實例中有一部分是地址用super表示,是作為父類的儲存區(qū)校坑,而其余部分是子類的儲存區(qū)域轧飞,地址用this表示衅鹿。
多態(tài)
編譯和運行的時候產(chǎn)生不同的形態(tài)成為多態(tài)撒踪。
- 多態(tài)前提:要有繼承过咬,要有方法重寫,要有父類引用子類對象
- 對于多態(tài)編譯與運行時類中元素的規(guī)則
成員變量: 編譯看左邊(父類),運行看左邊(父類);
成員方法: 編譯看左邊(父類),運行看右邊(子類); 動態(tài)綁定
靜態(tài)方法: 編譯看左邊(父類),運行看左邊(父類);
class Demo_Polymorthic {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.num); //10
f.print(); //Son
f.speak(); //fat
}
}
class Demo_Polymorthic {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.num); //10
f.print(); //Son
f.speak(); //fat
}
}
class Father{
int num = 10;
public void print(){
System.out.println("Father");
}
public static void speak(){
System.out.println("fat");
}
}
class Son extends Father {
int num = 5;
public void print(){
System.out.println("Son");
}
public static void speak(){
System.out.println("so");
}
}
- 多態(tài)的好處與應用場景
在方法中制妄,父類對象用作參數(shù)掸绞,子類方法重寫,使得代碼的復用性提高
class Demo_Polymorthic3 {
public static void main(String[] args) {
make(new Dog());
make(new Cat());
}
public static void make(Animal a){ //多態(tài)常應用于父類作參數(shù),子類方法重寫的應用場景
a.eat();
}
}
class Animal{
void eat(){
System.out.println("動物吃飯");
}
}
class Cat extends Animal{
void eat (){
System.out.println("貓吃魚");
}
void catchmouse(){
System.out.println("捉老鼠");
}
}
- 在多態(tài)的情況下耕捞,當父類引用要使用子類特有的方法衔掸,要將父類引用強制轉(zhuǎn)換向下轉(zhuǎn)型。以下例子也許可以方便理解和記憶俺抽。
class Demo_Polymorthic2 {
public static void main(String[] args) {
Person p = new SuperMan();
System.out.println(p.name); //man
p.談生意(); //談幾個億的生意
//p.fly(); //p實際上就是子類向上提升
SuperMan sm = (SuperMan) p; //向下轉(zhuǎn)型
sm.fly();
}
}
class Person{
String name="man";
public void 談生意(){
System.out.println("談生意");
}
}
class SuperMan extends Person{
String name = "sup";
public void 談生意(){
System.out.println("談幾個億的生意");
}
public void fly(){
System.out.println("去救人");
}
}
在上面的代碼中敞映,父類引用p 指向子類對象superman,所以在正常引用下磷斧,superman就偽裝成普通人person振愿,調(diào)用成員變量,當然是用偽裝的名字man弛饭,而當調(diào)用成員方法使冕末,實際上就是superman在“”談生意“,而不是普通的person,故表現(xiàn)其真實的行為侣颂,即superman重寫的談生意()這一方法档桃,調(diào)用結(jié)果是“談幾個億的大生意”。當要用到superman特有的方法fly()時憔晒,必須向下轉(zhuǎn)型成superman藻肄,無法再偽裝了。
抽象類
抽象拒担,即無法具體的嘹屯,所謂抽象類,即用abstract修飾符修飾的類澎蛛。
abstract 修飾的方法抚垄,沒有方法體,如 abstarct eat();
- 抽象類的成員變量不能抽象
- 抽象類也有構(gòu)造方法
- 抽象類的抽象方法使子類必須重寫該方法
- 抽象類可以有非抽象方法谋逻,子類可以不重寫該方法
- 抽象類無法創(chuàng)造對象呆馁,無法創(chuàng)造出實例,但可以通過多態(tài)引用其子類
class Demo_Abstract {
public static void main(String[] args) {
Animal a = new Cat(); //通過多態(tài)來引用抽象類,抽象類本身無法創(chuàng)造實例
}
}
abstract class Animal{
int num1; //抽象類成員變量不能抽象
final int num2 = 10;
Animal(){
System.out.println("抽象類的有參構(gòu)造"); //抽象類也有構(gòu)造方法
}
abstract void eat();
public void method(){ //抽象類可以有非抽象方法,子類繼承可以不重寫
System.out.println("抽象類的非抽象方法");
}
}
class Cat extends Animal{
void eat(){ //抽象類使得子類必須重寫其抽象方法
System.out.println("貓吃魚");
}
}
抽象類規(guī)定了子類必須重寫的方法毁兆,即規(guī)定了子類都必須有的方法浙滤,且要求其根據(jù)實際重寫。
接口
廣義上的接口气堕,就是向外提供或者說制定的一種規(guī)則纺腊,如usb接口畔咧,規(guī)定了生產(chǎn)接口的廠商應該使用的規(guī)格,線路的電壓等規(guī)則揖膜。
在JAVA中接口可以說是抽象類進一步抽象誓沸,對實現(xiàn)接口的類制定了特定的規(guī)則。
- 接口沒有構(gòu)造方法
- 接口所有的都是抽象方法壹粟,所以實現(xiàn)接口拜隧,就是要重寫接口所規(guī)定的方法
- 接口所有的變量和方法都是public,因為接口的目的就是表露其規(guī)則趁仙,讓類去實現(xiàn)洪添,所以權(quán)限要足夠大。
- 類實現(xiàn)接口用implements雀费,值得注意的是一個類可以實現(xiàn)多個接口干奢,因為接口的方法都是抽象的,沒有方法體盏袄,不會產(chǎn)生沖突忿峻,這彌補了單繼承的缺陷。
- 接口與接口之間可以繼承
class Demo_Interface {
public static void main(String[] args) {
InterA a = new Demo();
}
}
//設(shè)計理念,抽象類是為了規(guī)定共性的東西,接口是為了實現(xiàn)擴展性
interface InterA{
public static final int num = 1; //接口沒有構(gòu)造方法
public abstract void printA(); //接口的方法都是抽象方法
}
interface InterB{
void printB();
}
interface InterC extends InterA,InterB{ //接口可以繼承接口,并且是多繼承
}
class Demo implements InterC { //類可以實現(xiàn)多個接口
public void printA(){;}
public void printB(){;}
}
關(guān)于抽象類和接口的設(shè)計理念貌矿,抽象類規(guī)定了子類共同需要重寫的方法炭菌,是規(guī)定了子類的共性,而接口的多實現(xiàn)逛漫,可以使類具有擴展性黑低,如下面的例子
class Demo_Interface2 {
public static void main(String[] args) {
Cat c = new Cat();
c.eat();
JumpCat j = new JumpCat();
j.eat();
j.jump();
}
}
abstract class Animal{
String name;
abstract void eat();
}
interface Jump{
void jump();
}
class Cat extends Animal{
public void eat(){
System.out.println("貓吃魚");
}
}
class JumpCat extends Cat implements Jump{
public void jump(){
System.out.println("跳高");
}
}
在抽象類Animal中定義了抽象方法eat();因為所有繼承他的子類都具有這一行為酌毡,必須根據(jù)自身重寫該方法克握。而接口Jump定義了jump();這一抽象方法枷踏,不同的類根據(jù)自己的需求菩暗,是否去實現(xiàn)這一接口,去擴展該類的內(nèi)容旭蠕。而且由于接口是多實現(xiàn)的停团,同一個類可以實現(xiàn)不同的接口,展現(xiàn)于其他類不同的行為掏熬。
以上便是今天學習的內(nèi)容中的要點及最應該注意的地方佑稠,其中有什么錯誤的地方,特別是關(guān)于內(nèi)存的分配方面旗芬,由于是自己的理解舌胶,可能與實際有偏差,希望讀者指出并諒解疮丛。