代碼塊用于限定變量的范圍,在java中,還有兩個特殊的代碼塊:
(1)構造代碼塊:在類的成員位置,用{ }括起來的代碼。每次執行構造方法前,都會先執行構造代碼塊中的代碼。 可以把多個構造方法中的共同代碼寫在構造代碼塊中,用于對創建的每個對象進行統一初始化,而構造方法只是對用此構造創建的對象初始化。
(2)靜態代碼塊:在類的成員位置,用{ }括起來的代碼,并且此段代碼用static修飾。 靜態代碼塊是對類進行初始化,只是在加載類時執行一次。
代碼的執行順序:靜態代碼塊—構造代碼塊—構造方法。強調:靜態代碼塊只執行一次,構造代碼塊每次調用構造方法都執行。
(1)如果想使用靜態代碼塊中的變量,必須將變量提升為類的靜態成員,然后外部可以調用。本類的靜態方法可訪問此變量,實例方法不可以。例子:
public class Student { public static String str; static { // 靜態代碼塊中初始化 str = "hello world"; } // 靜態方法中可訪問 public static void hello() { System.out.PRintln(str); }}使用:
public class Demo { public static void main(String[] args) { System.out.println(Student.str); Student.hello(); }}(2)實例成員變量、構造代碼塊和構造方法操作同一個變量時的先后順序:
Java編譯后,會把成員變量的定義提升到類的最前面,但是對象成員的初始化都是在構造方法中完成的,即成員變量的賦值和構造代碼塊都會在構造方法中先執行,最后執行構造方法的內容。至于成員變量的賦值和構造代碼塊誰先執行,就是按照原本代碼的順序來的。例子:
public class Student { int age = 100; { age = 200; } public Student() { age = 300; } public void showAge() { System.out.println(age); // 實例可訪問實例變量 }}//如果將構造中的age = 300注釋掉,那么輸出的就是200,如果再把int age = 100放在構造代碼塊下面,那么下面輸出的就是100了。注意不要重復定義變量,都是在類中定義類變量的,否則外部訪問不到。如果在代碼塊中重新定義了變量,值就不一樣了,能訪問的只有類中定義的變量。使用:
public class Demo { public static void main(String[] args) { Student stu = new Student(); System.out.println(stu.age); stu.showAge(); }}(1)整體與部分的關系。現在有兩個類,一個是地址類,一個是人類。因為人有住址,所以人類中應該有個“住址”屬性,該屬性的類型就是地址類。這種關系滿足“has a”的關系。
例子:
地址類:
public class Address { private String province; private String city; private String street; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; }}人類:
public class Person { private String name; private int age; private Address addr; // 地址信息 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Address getAddr() { return addr; } public void setAddr(Address addr) { this.addr = addr; }}(2)繼承關系。現在有兩個類,一個是動物類,一個是貓類,那么貓類應該繼承自動物類,因為貓滿足“is a”動物的關系。動物類中是共性,通過繼承,有以下好處:
1. 提高代碼的復用性,不用寫多個類中相同的代碼;
2. 提高代碼的維護性,關系清晰;
3. 讓類與類產生了關系,這是多態的前提。
下面就講繼承。
Java中類的繼承只支持單繼承,但支持多層繼承。即一個類只能繼承一個類,允許這個類再被繼承,形成繼承體系。
繼承中,子類只能繼承父類非私有的成員;不能繼承父類的私有成員和構造方法,但是可以通過super關鍵字來訪問父類的構造方法。
不要為了部分功能去繼承。要滿足“子類對象 is a 父類對象”的關系。
繼承的例子:
(1)Animal類
public class Animal { // 有名稱屬性 private String name; // 動物有吃的行為 public void eat() { System.out.println(this.name + " is eating"); } public String getName() { return name; } public void setName(String name) { this.name = name; }}(2)Cat類
public class Cat extends Animal { // name屬性和eat方法在動物中有了,繼承過來的不用再重復 // 這里寫貓特有的叫的行為 public void miao() { System.out.println(this.getName() + "喵喵叫"); }}(3)Demo類中使用
public class Demo { public static void main(String[] args) { Cat cat = new Cat(); // cat中可以調用父類和本類的非私有成員 cat.setName("小貓"); cat.miao(); cat.eat(); }}super關鍵字指向父類對象。這個父類對象是從哪里來的。實際上繼承中,創建子類對象之前先會創建父類對象,默認子類構造方法的第一行就是super(),就是調用父類的無參構造創建父類對象。
因為默認調用父類的無參構造,所以如果手動寫了父類的構造方法(這樣父類就沒有了默認的無參構造),那么需要在子類的構造方法第一行手動用super關鍵字調用父類對應的構造方法。
總結this和super的用法:
(1)調用成員變量
this.成員變量 調用本類的成員變量
super.成員變量 調用父類的成員變量
(2)調用構造方法
this(…) 調用本類的構造方法
super(…) 調用父類的構造方法
(3)調用成員方法
this.成員方法() 調用本類的成員方法
super.成員方法() 調用父類的成員方法
方法重寫就是子類重寫父類的方法,這樣當子類對象調用該方法時,會表現出子類的行為,而父類對象調用該方法時,表現的還是父類的行為。
子類方法與父類方法同名,自動重寫。無法重寫父類的私有方法。
子類重寫父類的方法時,訪問權限不能更低,最好一致。而且返回值類型必須是父類函數的返回值類型或該返回值類型的子類。不能返回比父類更大的數據類型。
instanceof屬于比較運算符,該關鍵字用來判斷一個對象是否是指定類的對象。返回的結果是boolean類型。用法是“對象 instanceof 類”。要求是該對象的類必須和instanceof后的類有繼承(或者實現接口)的關系,否則報錯。
public class Demo { public static void main(String[] args) { Cat cat = new Cat(); Animal animal = new Animal(); System.out.println(cat instanceof Cat); System.out.println(cat instanceof Animal); System.out.println(animal instanceof Cat); // 不能用 cat instanceof System,因為System類和Cat類沒有繼承關系。 String[] strs = {""}; System.out.println(strs instanceof String[]); }}final的引入:現在需要訪問類中的變量,但是不希望這個值被改變,則用final修飾一個這個類的靜態變量即可,即:
public static final NUM = 100.
比如在Math類中的PI變量,通過Math.PI訪問,但是不能改變他的值。
關于final的作用:
(1)final修飾類:該類不能被繼承。
(2)final修飾方法,該方法不能被重寫。
(3)final修飾變量,該變量不能被重新賦值。常來定義常量。
(4)final修飾成員變量
若修飾基本類型,則其值不能改變
若修飾引用類型:引用類型的地址值不能改變。但該對象的屬性可以改變,因為他在堆內存中。也就是可以:
final Person p = new Person();不能p = new Person(),可以p.setName(“張三”)
(5)final修飾形參,則在該方法中不能修改這個形參。比如某個方法中只需要遍歷傳遞進來的數組,那么為了數據安全性,則可以將這個數組形參加上final關鍵字。
(6)final變量的初始化
如果用靜態常量,則必須是聲明時便賦值,即:static final int a = 9;否則沒有其他時機賦值。 如果是非靜態常量,則在每個構造方法里面賦值(或之前已經賦好值)。
多態就是一個對象可以有多種狀態。實現多態的前提:
(1)類之間有繼承關系;
(2)子類重寫父類的方法。
然后,用父類類型對象接收子類創建的對象(父 f = new 子()),這個就是多態,因為其特點是:
(1)由于是父類類型接收的,“名義”上是父類類型,表現的是父類的屬性。
(2)但是該對象“本質”是子類對象,由于子類重寫了父類的方法,那么雖然現在的父類類型,當調用這個被重寫的方法時,還是會調用被重寫的方法。并且,該對象可被強制轉換成子類類型對象。
例子:
(1)父類
public class Fu { public int num = 100; // 成員變量 // 實例方法 public void show() { System.out.println("Show Fu"); } // 靜態方法 public static void showStatic() { System.out.println("Show Fu Static"); }}(2)子類
// 繼承父類Fupublic class Zi extends Fu { // 成員變量 public int num = 200; // 重寫實例方法 public void show() { System.out.println("Show Zi"); } // 靜態方法 public static void showStatic() { System.out.println("Show Zi Static"); }}(3)多態使用
public class Demo { public static void main(String[] args) { Fu f = new Zi(); System.out.println(f.num); // 輸出成員變量 f.show(); // 實例方法 f.showStatic(); // 靜態方法 }}說明:
上面例子中,子類“重寫”父類的成員變量、方法和靜態方法。然后創建子類,但是用父類接收。
對于變量,并沒有“重寫變量”這個概念,所以用父類調用,還是會找到父類的num也就是1000.
對于實例方法,子類已經重寫了,所以表現出子類的方法。
對于靜態方法,因為靜態和類相關,不算是重寫。所以還是調用父類的方法。
多態常見的是“轉型”問題:
(1)向上轉型: Fu f = new Zi();
(2)向下轉型: Zi z = (Zi)f; //要求f必須是能夠轉換為Zi的。
如果一個類中的方法是抽象的,不知如何實現,那么可將此方法和此類定義成抽象的,不實現抽象方法(即不寫方法體),然后,繼承該類的具體實現類可以重寫該方法,完成具體實現。用abstract關鍵字定義抽象方法和抽象類。
使用abstract應該注意: (1)抽象類和抽象方法用abstract關鍵字修飾;
(2)有抽象方法的類必須定義為抽象類,但是抽象類中不一定有抽象方法。
(3)抽象類由于有方法沒有具體實現,因此不能直接實例化。抽象類也可用構造方法進行數據的初始化。
(4)抽象類中也可以有非抽象的成員,比如變量、方法等。
(5)抽象類的實例化是靠具體的子類實現的。是多態的方式。例如:Animal a = new Cat();
(6)抽象類的子類如果還是一個抽象類,則可以不實現抽象方法;如果是一個具體類,則必須實現(重寫)所有的抽象方法。
(7)abstract不能和private、final和static共存,因為這些關鍵字與重寫方法沖突(這些關鍵字使得外界不能重寫方法)。
下面就是一個例子,Shape(形狀)類中不知如何實現求面積的方法,因此設計為抽象的,然后他的具體實現(矩形、圓形)可以進行具體實現,然后可以用多態的方式。
(1)Shape類
public abstract class Shape { // 定義抽象的方法,求面積,不要寫實現,就是不寫大括號及其里面的內容 public abstract double area();}(2)Rect類
public class Rect extends Shape { // 定義寬高屬性 private double width; private double height; // 在構造中初始化寬高 public Rect(int width, int height) { this.width = width; this.height = height; } // 實現求面積的方法 public double area() { return this.width * this.height; } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; }}(3)Circle類
public class Circle extends Shape { // 定義半徑 private double radius; // 構造 public Circle(double radius) { this.radius = radius; } // 實現求面積的方法 public double area() { return Math.PI * this.radius * this.radius; }}(4)使用的Demo類
public class Demo { public static void main(String[] args) { // 多態創建矩形 Shape rect = new Rect(12, 4); System.out.println(rect.area()); // 多態創建圓形 Shape circle = new Circle(2); System.out.println(circle.area()); }}當然也可以不用多態,直接用本類類型接收。
接口,就是用來擴展功能的,可以彌補Java中單繼承的缺點。比如現在有帶橡皮擦的鉛筆,如果此類繼承了橡皮類,就能有erase的功能,如果此類繼承了鉛筆類,就有了write的功能。但是由于Java的單繼承的,因此不能同時實現上述兩個功能。所以需要引入接口。
接口中只需聲明抽象的方法,如果某類實現了接口,那么就要實現接口中所有的方法。然后通過多態方式創建實例,調用方法。因此接口也是實現多態的一種方法。
此外,接口還能定義開發規范,實現類必須實現接口的方法。
注意:
(1)用關鍵字interface定義接口,例如:
interface 接口名 {}
(2)類實現接口用implements關鍵字,實現接口的類一般以Impl作為后綴。
class 類名 implements 接口名 {}
(3)接口不能直接被實例化,而是按照多態的方式來實例化。
(4)接口的子類,如果是抽象類,不需要寫具體實現;如果是具體類,則要重寫接口中的所有方法。
(5)接口沒有構造方法。
(6)接口中只能有抽象方法,不能有具體的方法。接口中方法的默認修飾符是public abstract,可以省略,比如直接寫void run();等。但是為了加強意識,可以主動加上。
(7)接口沒有構造方法。
(8)接口中可定義屬性,默認的修飾符是public static final,即靜態常量,必須定義的時候賦值。這些常量可以通過“接口名.屬性名”來使用。
(9)一個類可以實現多個接口。
下面是帶橡皮的鉛筆的例子。
(1)Erase接口
public interface Erase { void erase(); // 擦除的功能}(2)Write接口
public interface Write { void write(); //寫的功能}(3)PencilWithErase類實現上述兩個接口
public class PencilWithErase implements Erase, Write { private String name; //名稱 public PencilWithErase(String name) { this.name = name; } // 實現擦除和寫的功能 public void erase() { System.out.println(this.name + "is erasing..."); } public void write() { System.out.println(this.name + "is writing..."); }}(4)在Demo中多態使用
public class Demo { public static void main(String[] args) { // 多態實現擦除和寫 Erase erase = new PencilWithErase("帶橡皮的鉛筆"); erase.erase(); Write write = new PencilWithErase("帶橡皮的鉛筆"); write.write(); }}Java類的多層繼承實現了Java的繼承體系。實際上,所有的類默認繼承自Object類,Object 是類層次結構的根類。
每個類都使用 Object 作為超類,即每個類直接或間接是Object的子類。如果一個類沒有繼承別的類,默認是繼承Object類,這樣,就算別的類繼承了別的類,最終繼承的還是Object類。
類和接口的繼承:
(1)類可以繼承一個類和實現多個接口。
(2)接口之間也有繼承關系,可以多繼承,即一個接口可以繼承多個接口。interface A extends B, C{},但是繼承過來也不要實現,只是實現此接口的類要實現所有方法。
即類和接口之間是實現關系,接口和接口之間是繼承關系。
如果參數或返回值:
(1)是一個普通的類類型,那么直接傳遞該類對象作為參數,或者用該類對象接收返回值;
(2)是一個抽象類,那么需要傳遞該抽象類的一個實現類對象作為參數,或者用該抽象類的一個實現類對象來接收返回值。
(3)是一個接口,那么需要傳遞該接口的一個實現類對象作為參數,或者用該接口的一個實現類對象來接收返回值。
由于數值是基本數據類型,因此作為方法參數傳遞,不會改變實參的值。
但是數組、對象是引用類型,將引用類型作為參數傳遞,是會改變實參的值的。可以參考C語言,引用相當于指針指向。
例外的是,雖然String是引用類型,但是將String類型作為參數傳遞,不會改變實參的值。
比如一個方法返回的是一個對象,那么還可再調用這個對象的方法,這就是鏈式編程。比如new Student().getTeacher().getName(),Student對象的getTeacher返回的是一個教師對象,然后再調用教師對象的getName方法,得到教師的姓名。這就是為什么有那么多的“點.”。
包(package)其實就是文件夾,可把Java類文件分類管理在不同的包中,不同的包中可以有相同的類名。
首先新建包文件夾,比如com/zhang/test,然后在test文件夾下新建類,那么這個類就是在包com.zhang.test下。需要用package關鍵字在Java文件的第一行定義此文件所在的包:
package com.zhang.test;
注意點:
(1)多級包名用.隔開。
(2)package語句必須位于java文件第一行,且一個文件中只能有一條package語句,就是表明文件在哪個文件夾下。
(3)如果沒有package語句,則默認無包名,以前的代碼就是這樣的,但是實際開發中不這樣做。
如果Java文件以包來管理,如何編譯和運行?下面是詳細的步驟。
(1)新建各級包的文件夾,并在包中寫好Java文件。舉例,寫好了com.zhang.test.Demo.java這個文件(com.zhang.test就是包)。
(2)用javac編譯時加上-d <目錄>參數指明路徑,例如:
javac –d . com.zhang.test
這樣會在當前目錄下生成各級包的目錄,并把字節碼放在包下面。如果你不加-d <目錄>,那么就不會生成各級文件夾,還需要自己創建文件夾然后放進去。
(3)在當前目錄下(不要進入class字節碼文件目錄下)執行:
java com.zhang.test.Demo 即可執行Java程序了。
不要認為能在字節碼目錄下執行java Demo,因為類是包管理起來的,只能在包外面執行字節碼。這也解釋了為何在IDE中生成了*.class文件,然后找到這個class文件,在當前目錄下運行卻無法運行,提示“找不到主類”,就是因為在IDE中設置了包名。所以需要在文件夾以外執行。
在同一個包中,一個類想使用另外一個類可以直接使用。但是如果兩個類在不同包中,就需要用import關鍵字導入需要使用的包了。
導包的格式:import 包名.類名;也可以使用import 包名.,就是導入該包下的所有類,但是一般不推薦這樣用。使用時不能導入包中的子類包的class文件。
導完后就能在這個類中使用別的包的類了。
一個java文件中package/import/class的順序關系:
(1)package:只能有一個,在代碼的最前面;
(2)import:可以有多個,需要寫在package下面;
(3)class:可以有多個,但是public class只有一個,并且與文件名一致。為了模塊化,建議一個文件中只有一個class。執行時執行主方法所在類(主類)。
注意:如果不使用import導包,或者一個文件中使用了不同包下同名的類,那么只能寫上類的全路徑,即比如:com.zhang.tools.ArrayTools。
補充權限修飾符可訪問的范圍:
| 訪問修飾符/位置 | 本類中 | 同一個包下(子類和無關類) | 不同包下(子類) | 不同包下(無關類) |
|---|---|---|---|---|
| private | Y | |||
| 默認 | Y | Y | ||
| protected | Y | Y | Y | |
| public | Y | Y | Y | Y |
jar包(Java Archive File)就是一個打包后的文件。jar與zip兼容,又叫jar包。第三方工具類常以jar包形式提供,jar包中打包了class字節碼等文件。
使用jar命令進行打包和解包。
jar命令可以對任何文件和文件夾進行打包和解包,由于和zip兼容的,也可以用其他軟件處理jar生成的壓縮包。只不過用jar打包的文件中有清單文件。
(1)打包:一般用“jar cvf 生成的文件名 打包的文件/目錄”來打包。比如:
jar cvf a.jar com,就把com目錄下所有文件打包成a.jar文件。實際上cvf是組合參數,順序可以改變,解釋如下:
c:創建新檔案,v:生成詳細輸出,f:指定檔案文件名。實際上也可以用jar cf a.jar com來打包,只不過不會顯示詳細信息。
如果文件少,直接將多個文件打包成一個文件:
jar cvf a.jar b.class c.class
(2)查看包信息:一般用tvf,t就是列出檔案目錄。別的字母還是一樣的意思,例如:jar tvf a.jar。也可用此查看zip文件信息
(3)解包:使用的是xvf,x就是解包的指令。例如jar xvf a.jar就是解包到當前目錄。
jar包就是多個class文件的壓縮包。導出jar包可以讓別人進行引用。有的jar包是可運行的,運行jar包的方法:
Java –jar jar文件,例如java –jar a.jar
支付寶 
微信 
新聞熱點
疑難解答