JAVA常用基礎知識點[繼承,抽象,介面,靜態,列舉,反射,泛型,多執行緒...]
類的繼承
Java只支援單繼承,不允許多重繼承
- 一個子類只能有一個父類
- 一個父類可以派生出多個子類
這裡寫圖片描述
子類繼承了父類,就繼承了父類的方法和屬性。
在子類中,可以使用父類中定義的方法和屬性,也可以建立新的資料和方法。
因而,子類通常比父類的功能更多。
在Java 中,繼承的關鍵字用的是“extends”,即子類不是父類的子集,而是對父類的“擴充套件”。
關於繼承的規則:
子類不能繼承父類中私有的(private)的成員變數和方法。
訪問控制
可以對Java類中定義的屬性和方法進行訪問控制—-規定不同的保護等級: public、protected、default、private
這裡寫圖片描述
訪問控制舉例
class Parent{
private int f1 = 1;
int f2 = 2; //default
protected int f3 = 3;
public int f4 = 4;
private void fm1() {
System.out.println("in fm1() f1=" + f1);
}
void fm2() { //default
System.out.println("in fm2() f2=" + f2);
}
protected void fm3() {
System.out.println("in fm3() f3=" + f3);
}
public void fm4() {
System.out.println("in fm4() f4=" + f4);
}
}
//設父類和子類在同一個包內
class Child extends Parent{
private int c1 = 21;
public int c2 = 22;
private void cm1(){
System.out.println("in cm1() c1=" + c1);
}
public void cm2(){
System.out.println("in cm2() c2=" + c2);
}
public static void main(String args[]){
int i;
Parent p = new Parent();
i = p.f2; // i = p.f3;i = p.f4;
p.fm2(); // p.fm3();p.fm4();
Child c = new Child();
i = c.f2; // i = c.f3;i = c.f4;
i = c.c1; // i = c.c2;
c.cm1(); // c.cm2(); c.fm2(); c.fm3();c.fm4()
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
訪問控制分析
父類Parent和子類Child在同一包中定義時:
這裡寫圖片描述
覆蓋方法
在子類中可以根據需要對從父類中繼承來的方法進行改造—覆蓋方法(方法的重置、重寫),在程式執行時,子類的方法將覆蓋父類的方法。
覆蓋方法必須和被覆蓋方法具有相同的方法名稱、引數列表和返回值型別。
覆蓋方法不能使用比被覆蓋方法更嚴格的訪問許可權。
覆蓋方法舉例
public class Person {
public String name;
public int age;
public String getInfo() {
return "Name: "+ name + "\n" +"age: "+ age;
}
}
public class Student extends Person {
public String school;
public String getInfo() { //覆蓋方法
return "Name: "+ name + "\nage: "+ age
+ "\nschool: "+ school;
}
public static void main(String args[]){
Student s1=new Student();
s1.name="Bob";
s1.age=20;
s1.school="school2";
System.out.println(s1.getInfo()); //Name:Bob age:20 school:school2
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
說明:
Person p1=new Person();
p1.getInfo();
//呼叫Person類的getInfo()方法
Student s1=new Student();
s1.getInfo();
//呼叫Student類的getInfo()方法
這是一種“多型性”:同名的方法,用不同的物件來區分呼叫的是哪一個方法。
覆蓋方法舉例
class Parent {
public void method1() {}
}
class Child extends Parent {
private void method1() {}
//非法,子類中的method1()的訪問許可權private比被覆蓋方法的訪問許可權public弱
}
public class UseBoth {
public void doOtherThing() {
Parent p1 = new Parent();
Child p2 = new Child();
p1.method1();
p2.method1();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
關鍵字super
在Java類中使用super來引用父類的成分
super可用於訪問父類中定義的屬性
super可用於呼叫父類中定義的成員方法
super可用於在子類構造方法中呼叫父類的構造方法
super的追溯不僅限於直接父類
super舉例
public class Person {
private String name;
private int age;
public String getInfo() {
return "Name: " + name + "\nage: " + age;
}
}
public class Student extends Person {
private String school = "New Oriental";
public String getSchool(){ return school; }
public String getInfo() {
// 呼叫父類的方法
return super.getInfo() +"\nschool: " +school;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
構造方法不能繼承
子類繼承父類所有的成員變數和成員方法,但不繼承父類的構造方法
在一個Java類中可以通過兩種方式獲得構造方法
使用系統預設的無引數構造方法
顯式定義一個或多個構造方法
一旦顯式定義了構造方法,則系統不再提供預設構造方法
呼叫父類構造方法
注意 注意 注意 重要的事說三遍:↓↓↓
在子類的構造方法中可使用super(引數列表)語句呼叫父類的構造方法
如果子類的構造方法中沒有顯示地呼叫父類構造方法,也沒有使用this關鍵字呼叫過載的其它構造方法,則系統預設呼叫父類無引數的構造方法
如果子類構造方法中既未顯式呼叫父類構造方法,而父類中又沒有無參的構造方法,則編譯出錯
呼叫父類構造方法舉例
public class Person {
private String name;
private int age;
private Date birthDate;
public Person(String name, int age, Date d) {
this.name = name;
this.age = age;
this.birthDate = d;
}
public Person(String name, int age) {
this(name, age, null);
}
public Person(String name, Date d) {
this(name, 30, d);
}
public Person(String name) {
this(name, 30);
}
// ……
}
public class Student extends Person {
private String school;
public Student(String name, int age, String s) {
super(name, age);
school = s;
}
public Student(String name, String s) {
super(name);
school = s;
}
public Student(String s) {
/**
* 編譯出錯: no super(),
* 系統將呼叫父類無引數的構造方法。
* */
school = s;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
子類物件的例項化過程
這裡寫圖片描述
思考
1).為什麼super(…)和this(…)呼叫語句不能同時在一個建構函式中出現?
2).為什麼super(…)或this(…)呼叫語句只能作為建構函式中的第一句出現?
看個例子解答以上兩個問題。
this() super()是你如果想用傳入當前構造器中的引數或者構造器中的資料呼叫其他構造器或者控制父類構造器時使用的,在一個構造器中你只能使用this()或者super()之中的一個,而且呼叫的位置只能在構造器的第一行,在子類中如果你希望呼叫父類的構造器來初始化父類的部分,那就用合適的引數來呼叫super(),如果你用沒有引數的super()來呼叫父類的構造器(同時也沒有使用this()來呼叫其他構造器),父類預設的構造器會被呼叫,如果父類沒有預設的構造器,那編譯器就會報一個錯誤。
假如我們允許把this和super放置到任何位置。那麼請看下面程式碼:
class A
{
A()
{
System.out.println("You call super class non-args constructor!");
}
}
class B extends A
{
B()
{
//這裡,編譯器將自動加上 super();
System.out.println("You call subclass constructor!");
}
B(String n)
{
super();
this();//實際就是呼叫了B(){...},而在B(){...}中編譯器自動加上了
//super();這樣就相當於兩次呼叫了super();也就是說對父類進//行了兩次初始化。而在例項化一個物件時,一個構造方法只能呼叫一次,這說明this和super不能同時存在一個構造方法中。同時因為系統沒有在第一行發現this()或super()呼叫,就會自動加上super(),如果沒有將this()和super()放在第一行就會產生矛盾。
//因為總有一個super()在第二句上。所以該程式不能通過編譯!!!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
結論
也就是說你必須在構造器的第一行放置super或者this構造器,否則編譯器會自動地放一個空引數的super構造器的,其他的構造器也可以呼叫super或者this,呼叫成一個遞迴構造鏈,最後的結果是父類的構造器(可能有多級父類構造器)始終在子類的構造器之前執行,遞迴的呼叫父類構造器。無法執行當前的類的構造器。也就不能例項化任何物件,這個類就成為一個無為類。
從另外一面說,子類是從父類繼承而來,繼承了父類的屬性和方法,如果在子類中先不完成父類的成員的初始化,則子類無法使用,應為在java中不允許呼叫沒初始化的成員。在構造器中是順序執行的,也就是說必須在第一行進行父類的初始化。而super能直接完成這個功能。This()通過呼叫本類中的其他構造器也能完成這個功能。
因此,this()或者super()必須放在第一行。
多型性
多型—在Java中,子類的物件可以替代父類的物件使用
一個變數只能有一種確定的資料型別
一個引用型別變數可能指向(引用)多種不同型別的物件
Person p = new Student();
Object o = new Person();//Object型別的變數o,指向Person型別的物件
o = new Student(); //Object型別的變數o,指向Student型別的物件
//父類型別的變數可以指向子類的物件
1
2
3
4
5
一個引用型別變數如果宣告為父類的型別,但實際引用的是子類物件,那麼該變數就不能再訪問子類中新增的屬性和方法
Student m = new Student();
m.school = “pku”; //合法,Student類有school成員變數
Person e = new Student();
e.school = “pku”; //非法,Person類沒有school成員變數
1
2
3
4
屬性是在編譯時確定的,編譯時e為Person型別,沒有school成員變數,
因而編譯錯誤。
虛擬方法呼叫(Virtual Method Invocation)
// 正常的方法呼叫
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
// 虛擬方法呼叫(多型情況下)
Person e = new Student();
e.getInfo(); //呼叫Student類的getInfo()方法
1
2
3
4
5
6
7
8
編譯時型別和執行時型別
編譯時e為Person型別,而方法的呼叫是在執行時確定的,所以呼叫的是Student類的getInfo()方法。—— 動態繫結
多型性應用舉例
方法宣告的形參型別為父類型別,可以使用子類的物件作為實參呼叫該方法
public class Test{
public void method(Person e) {
//……
e.getInfo();
}
public static void main(Stirng args[]){
Test t = new Test();
Student m = new Student();
t.method(m); //子類的物件m傳送給父類型別的引數e
}
}
1
2
3
4
5
6
7
8
9
10
11
instanceof 操作符
x instanceof A:檢驗x是否為類A的物件,返回值為boolean型。
要求x所屬的類與類A必須是子類和父類的關係,否則編譯錯誤。
如果x屬於類A的子類B,x instanceof A值也為true。
public class Person extends Object {…}
public class Student extends Person {…}
public class Graduate extends Person {…}
public void method1(Person e) {
if (e instanceof Person)
// 處理Person類及其子類物件
if (e instanceof Student)
//處理Student類及其子類物件
if (e instanceof Graduate)
//處理Graduate類及其子類物件
}
1
2
3
4
5
6
7
8
9
10
11
12
物件型別轉換 (Casting )
基本資料型別的Casting:
小的資料型別可以自動轉換成大的資料型別
如long g=20; double d=12.0f
可以把大的資料型別強制轉換(casting)成小的資料型別
如 floate f=(float)12.0 int a=(int)1200L
對Java物件的強制型別轉換稱為造型
從子類到父類的型別轉換可以自動進行
從父類到子類的型別轉換必須通過造型(強制型別轉換)實現
無繼承關係的引用型別間的轉換是非法的
在造型前可以使用instanceof操作符測試一個物件的型別
物件型別轉換舉例
public class Test{
public void method(Person e) { //設Person類中沒有getschool()方法
System.out.pritnln(e.getschool()); //非法,編譯時錯誤
if(e instanceof Student){
Student me = (Student)e; //將e強制轉換為Student型別
System.out.pritnln(me.getschool());
}
}
public static void main(Stirng args[]){
Test t = new Test();
Student m = new Student();
t.method(m);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Object 類
Object類是所有Java類的根父類
如果在類的宣告中未使用extends關鍵字指明其父類,則預設父類為Object類
public class Person {
...
}
等價於:
public class Person extends Object {
...
}
1
2
3
4
5
6
7
例:
method(Object obj){…}//可以接收任何類作為其引數
Object o=new Person;
method(o);
1
2
3
==操作符與equals方法
==操作符與equals方法的區別:
==:引用型別比較引用(是否指向同一個物件);
Person p1=new Person();
Person p2=new Person();
if (p1==p2){…}
1
2
3
基本型別比較值;
int a=5; if(a==6){…}
用”==”進行比較時,符號兩邊的資料型別必須一致(可自動轉換的基本資料型別除外),否則編譯出錯;
equals()方法是Object類的方法,由於所有類都繼承Object類,也就繼承了equals()方法。只能比較引用型別,其作用與“==”相同,比較是否指向同一個物件。格式:obj1.equals(obj2)
特例:當用equals()方法進行比較時,對類File、String、Date及封裝類(Wrapper Class)來說,是比較型別及內容而不考慮引用的是否是同一個物件;
原因:在這些類中覆蓋了equals()方法。
toString 方法
toString()方法在Object類中定義,其返回值是String型別,返回類名和它的引用地址。
在進行String與其它型別資料的連線操作時,自動呼叫toString()方法
Date now=new Date();
System.out.println(“now=”+now);
// 相當於
System.out.println(“now=”+now.toString());
//
1
2
3
4
5
可以根據需要在使用者自定義型別中重寫toString()方法
如String 類重寫了toString()方法,返回字串的值。
s1="hello";
System.out.println(s1);
//相當於
System.out.println(s1.toString());
1
2
3
4
在ToString1.java中的類A裡覆蓋toString方法,使其輸出類A物件的cint屬性值。
基本型別資料轉換為String型別時,呼叫了對應封裝類的 toString()方法int a=10; System.out.println(“a=”+a);
封裝類
針對八種基本定義相應的引用型別—封裝類
這裡寫圖片描述
關鍵字static
描述
在Java類中宣告變數、方法和內部類時,可使用關鍵字static做為修飾符。
static標記的變數或方法由整個類(所有例項)共享,如訪問控制權限允許,可不必建立該類物件而直接用類名加‘.’呼叫。
static成員也稱類成員或靜態成員,如:類變數、類方法、靜態方法等。
類變數(class Variable)
類變數(類屬性)由該類的所有例項共享
這裡寫圖片描述
public class Person {
private int id;
//類屬性類似於全域性變數
public static int total = 0;
public Person() {
total++;
id = total;
}
}
1
2
3
4
5
6
7
8
9
類屬性應用舉例
class Person {
private int id;
public static int total = 0;
public Person() {
total++;
id = total;
}
public static void main(String args[]){
Person Tom=new Person()
Tom.id=0;
total=100; // 不用建立物件就可以訪問靜態成員
}
}
public class OtherClass {
public static void main(String args[]) {
Person.total = 100; // 不用建立物件就可以訪問靜態成員
//訪問方式:類名.類屬性類名.類方法
System.out.println(Person.total);
Person c = new Person();
System.out.println(c.total); //輸出101
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
類方法(class Method)
描述
在靜態方法裡只能直接呼叫同類中其它的靜態成員(包括變數和方法),而不能直接訪問類中的非靜態成員。這是因為,對於非靜態的方法和變數,需要先建立類的例項物件後才可使用,而靜態方法在使用前不用建立任何物件。
靜態方法不能以任何方式引用this和super關鍵字。與上面的道理一樣,因為靜態方法在使用前不用建立任何例項物件,當靜態方法被呼叫時,this所引用的物件根本就沒有產生。
main() 方法是靜態的,因此JVM在執行main方法時不建立main方法所在的類的例項物件,因而在main()方法中,我們不能直接訪問該類中的非靜態成員,必須建立該類的一個例項物件後,才能通過這個物件去訪問類中的非靜態成員,這種情況,我們在以後的例子中會多次碰到。
類名.方法名()
沒有物件的例項時,可以用類名.方法名()的形式訪問由static標記的類方法。
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
return total;
}
public Person() {
total++;
id = total;
}
}
public class TestPerson {
public static void main(String[] args) {
System.out.println("Number of total is " +Person.getTotalPerson());
//沒有建立物件也可以訪問靜態方法
Person p1 = new Person();
System.out.println( "Number of total is "+ Person.getTotalPerson());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
訪問類的static屬性
在static方法內部只能訪問類的static屬性,不能訪問類的非static屬性。
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
id++; //非法
return total;
}
public Person() {
total++;
id = total;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
訪問static方法注意
因為不需要例項就可以訪問static方法,因此static方法內部不能有this,(也不能有super )
class Person {
private int id;
private static int total = 0;
public static void setTotalPerson(int total){
this.total=total; //非法,在static方法中不能有this,也不能有super
}
public Person() {
total++;
id = total;
}
}
public class TestPerson {
public static void main(String[] args) {
Person.setTotalPerson();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
類屬性、類方法的設計思想
類屬性作為該類各個物件之間共享的變數。在設計類時,分析哪些類屬性不因物件的不同而改變,將這些屬性設定為類屬性。相應的方法設定為類方法。
如果方法與呼叫者無關,則這樣的方法通常被宣告為類方法,由於不需要建立物件就可以呼叫類方法,從而簡化了方法的呼叫
靜態初始化
一個類中可以使用不包含在任何方法體中的靜態程式碼塊(static block ),當類被載入時,靜態程式碼塊被執行,且只被執行一次,靜態塊經常用來進行類屬性的初始化。
static塊通常用於初始化static (類)屬性
class Person {
public static int total;
static {
total = 100;//為total賦初值
}
…… //其它屬性或方法宣告
}
1
2
3
4
5
6
7
靜態初始化舉例
class Person {
public static int total;
static {
total = 100;
System.out.println("in static block!");
}
}
public class Test {
public static void main(String[] args) {
System.out.println("total = "+ Person.total);
System.out.println("total = "+ Person.total);
}
}
//輸出:
//in static block
//total=100
//total=100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
單子 Singleton 設計模板(單例,單態)
描述
設計模式是在大量的實踐中總結和理論化之後優選的程式碼結構、程式設計風格、以及解決問題的思考方式。設計模式就想是經典的棋譜,不同的棋局,我們用不同的棋譜,免得我們自己再去思考和摸索。
所謂類的單態設計模式,就是採取一定的方法保證在整個的軟體系統中,對某個類只能存在一個物件例項,並且該類只提供一個取得其物件例項的方法。如果我們要讓類在一個虛擬機器中只能產生一個物件,我們首先必須將類的構造方法的訪問許可權設定為private,這樣,就不能用new 操作符在類的外部產生類的物件了,但在類內部仍可以產生該類的物件。因為在類的外部開始還無法得到類的物件,只能呼叫該類的某個靜態方法以返回類內部建立的物件,靜態方法只能訪問類中的靜態成員變數,所以,指向類內部產生的該類物件的變數也必須定義成靜態的。
單子Singleton 設計模板
class Single{
private static Single onlyone = new Single();//私有的,只能在類的內部訪問
private String name;
public static Single getSingle() { //getSingle()為static,不用建立物件
//即可訪問
return onlyone;
}
private Single() {} //private的構造器,不能在類的外部建立該類的物件
}
public class TestSingle{
public static void main(String args[]) {
Single s1 = Single.getSingle(); //訪問靜態方法
Single s2 = Single.getSingle();
if (s1==s2){
System.out.println("s1 is equals to s2!");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
理解main方法的語法
描述
由於java虛擬機器需要呼叫類的main()方法,所以該方法的訪問許可權必須是public,又因為java虛擬機器在執行main()方法時不必建立物件,所以該方法必須是static的,該方法接收一個String型別的陣列引數,該陣列中儲存執行java命令時傳遞給所執行的類的引數。
這裡寫圖片描述
命令列引數用法舉例
public class CommandPara {
public static void main(String[] args) {
for ( int i = 0; i < args.length; i++ ) {
System.out.println("args[" + i + "] = " + args[i]);
}
}
}
//執行程式CommandPara.java
java CommandPara lisa "bily" "Mr Brown"
//輸出結果:
args[0] = lisa
args[1] = bily
args[2] = Mr Brown
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
關鍵字 final
描述
在Java中宣告類、屬性和方法時,可使用關鍵字final來修飾。
final標記的變數(成員變數或區域性變數)即成為常量,只能賦值一次。
final標記的類不能被繼承。提高安全性,提高程式的可讀性。
final標記的方法不能被子類重寫。增加安全性。
final標記的成員變數必須在宣告的同時或在每個構造方法中顯式賦值,然後才能使用。
final PI=3.14;
關鍵字final應用舉例
public final class Test{
public static int totalNumber= 5 ;
public final int id;
public Test(){
id = ++totalNumber;//只能在構造方法中給final變數賦值
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.id);
final int i = 10;
final int j;
j = 20;
j = 30; //非法
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
抽象類(abstract class)
描述
隨著繼承層次中一個個新子類的定義,類變得越來越具體,而父類則更一般,更通用。類的設計應該保證父類和子類能夠共享特徵。有時將一個父類設計得非常抽象,以至於它沒有具體的例項,這樣的類叫做抽象類。
用abstract關鍵字來修飾一個類時,這個類叫做抽象類;用abstract來修飾一個方法時,該方法叫做抽象方法。
抽象方法:只有方法的宣告,沒有方法的實現。以分號結束。
abstract int abstractMethod1( int a );
含有抽象方法的類必須被宣告為抽象類。
抽象類不能被例項化。抽象類是用來被繼承的,抽象類的子類必須重寫父類的抽象方法,並提供方法體。
不能用abstract修飾私有方法,構造方法,靜態方法。
抽象類舉例
abstract class A{
abstract void m1( );
public void m2( ){
System.out.println("A類中定義的m2方法");
}
}
class B extends A{
void m1( ){
System.out.println("B類中定義的m1方法");
}
}
public class Test{
public static void main( String args[ ] ){
A c = new B( );
c.m1( );
c.m2( );
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
抽象類應用
抽象類是用來模型化那些父類無法確定全部實現,而是由其子類提供具體實現的物件的類。
這裡寫圖片描述
在航運公司系統中,Vehicle類需要定義兩個方法分別計算運輸工具的燃料效率和行駛距離。
練習:
卡車(Truck)和駁船(RiverBarge)的燃料效率和行駛距離的計算方法完全不同。Vehicle類不能提供計算方法,但子類可以。
解決方案
Java允許類設計者指定:超類宣告一個方法但不提供實現,該方法的實現由子類提供。這樣的方法稱為抽象方法。有一個或更多抽象方法的類稱為抽象類。
Vehicle是一個抽象類,有兩個抽象方法。
public abstract class Vehicle{
public abstract double calcFuelEfficiency(); //計算燃料效率的抽象方法
public abstract double calcTripDistance(); //計算行駛距離的抽象方法
}
public class Truck extends Vehicle{
public double calcFuelEfficiency( ) { //寫出計算卡車的燃料效率的具體方法 }
public double calcTripDistance( ) { //寫出計算卡車行駛距離的具體方法 }
}
public class RiverBarge extends Vehicle{
public double calcFuelEfficiency( ) { //寫出計算駁船的燃料效率的具體方法 }
public double calcTripDistance( ) { //寫出計算駁船行駛距離的具體方法}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
接 口
描述
有時必須從幾個類中派生出一個子類,繼承它們所有的屬性和方法。但是,Java不支援多重繼承。有了介面,就可以得到多重繼承的效果。
介面(interface)是抽象方法和常量值的定義的集合。
從本質上講,介面是一種特殊的抽象類,這種抽象類中只包含常量和方法的定義,而沒有變數和方法的實現。
介面定義舉例
public interface Runner {
int id = 1;
public void start();
public void run();
public void stop();
}
1
2
3
4
5
6
7
介面的特點:
用 interface 來定義。
介面中的所有成員變數都預設是由public static final修飾的。
介面中的所有方法都預設是由public abstract修飾的。介面沒有構造方法。
實現介面的類中必須提供介面中所有方法的具體實現內容。
多個無關的類可以實現同一個介面
一個類可以實現多個無關的介面
與繼承關係類似,介面與實現類之間存在多型性
介面也可以繼承另一個介面,使用extends關鍵字。
實現介面的類中必須提供介面中所有方法的具體實現內容。
多個無關的類可以實現同一個介面
一個類可以實現多個無關的介面
與繼承關係類似,介面與實現類之間存在多型性
定義Java類的語法格式:
< modifier> class < name> [extends < superclass>]
[implements < interface> [,< interface>]* ] {
< declarations>*
}
1
2
3
4
介面應用舉例
public interface Runner {
public void start();
public void run();
public void stop();
}
public class Person implements Runner {
public void start() {
// 準備工作:彎腰、蹬腿、咬牙、瞪眼 // 開跑
}
public void run() {
// 擺動手臂
// 維持直線方向
}
public void stop() {
// 減速直至停止、喝水。
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
這裡寫圖片描述
一個類可以實現多個無關的介面
interface Runner { public void run();}
interface Swimmer {public double swim();}
class Animal {public int eat(){…}}
class Person extends Animal implements Runner,Swimmer{
public void run() {……}
public double swim() {……}
public int eat() {……}
}
1
2
3
4
5
6
7
8
與繼承關係類似,介面與實現類之間存在多型性
public class Test{
public static void main(String args[]){
Test t = new Test();
Person p = new Person();
t.m1(p);
t.m2(p);
t.m3(p);
}
public String m1(Runner f) { f.run(); }
public void m2(Swimmer s) {s.swim();}
public void m3(Animal a) {a.eat();}
}
1
2
3
4
5
6
7
8
9
10
11
12
注意
如果實現介面的類中沒有實現介面中的全部方法,必須將此類定義為抽象類。
介面也可以繼承另一個介面,使用extends關鍵字。
interface MyInterface
{
String s=“MyInterface”;
public void absM1();
}
interface SubInterface extends MyInterface
{
public void absM2();
}
public class SubAdapter implements SubInterface
{
public void absM1(){System.out.println(“absM1”);}
public void absM2(){System.out.println(“absM2”);}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
實現類SubAdapter必須給出介面SubInterface以及父介面MyInterface中所有方法的實現。
介面和繼承的區別
記住一句話:
介面是“有 沒 有”的關係,繼承是“是 不 是”的關係。
內部類
描述
在Java中,允許一個類的定義位於另一個類的內部,前者稱為內部類
內部類和外層封裝它的類之間存在邏輯上的所屬關係
Inner class一般用在定義它的類或語句塊之內,在外部引用它時必須給出完整的名稱。 Inner class的名字不能與包含它的類名相同;
Inner class可以使用包含它的類的靜態和例項成員變數,也可以使用它所在方法的區域性變數;
內部類舉例
class A {
private int s;
public class B{
public void mb() {
s = 100;
System.out.println("在內部類B中s=" + s);
}
}
public void ma() {
B i = new B();
i.mb();
}
}
public class Test {
public static void main(String args[]){
A o = new A();
o.ma();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class A{
private int s = 111;
public class B {
private int s = 222;
public void mb(int s) {
System.out.println(s); // 區域性變數s
System.out.println(this.s); // 內部類物件的屬性s
System.out.println(A.this.s); // 外層類物件屬性s
}
}
public static void main(String args[]){
A a = new A();
A.B b = a.new B();
b.mb(333);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
內部類特性
Inner class可以宣告為抽象類 ,因此可以被其它的內部類繼承。也可以宣告為final的。
和外層類不同,Inner class可以宣告為private或protected;
Inner class 可以宣告為static的,但此時就不能再使用外層封裝類的非static的成員變數;
非static的內部類中的成員不能宣告為static的,只有在頂層類或static的內部類中才可宣告static成員;
泛型
描述
下面是那種典型用法:
List myIntList = new ArrayList();// 1
myIntList.add(new Integer(0));// 2
Integer x = (Integer) myIntList.iterator().next();// 3
1
2
3
第 3 行的型別轉換有些煩人。通常情況下,程式設計師知道一個特定的 list 裡邊放的是什麼型別的資料。但是,這個型別轉換是必須的(essential)。編譯器只能保證 iterator 返回的是 Object 型別。為了保證對 Integer 型別變數 賦值的型別安全,必須進行型別轉換(這是關鍵)。
當然,這個型別轉換不僅僅帶來了混亂,它還可能產生一個執行時錯誤 (run time error),因為程式設計師可能會犯錯。
程式設計師如何才能明確表示他們的意圖,把一個 list(集合) 中的內容限制 為一個特定的資料型別(這是關鍵)呢?這就是 generics 背後的核心思想。這是上面程 序片斷的一個泛型版本:
List<Integer> myIntList = new ArrayList<Integer>(); //1
myIntList.add(new Integer(0)); // 2
Integer x = myIntList.iterator().next(); // 3
1
2
3
注意變數 myIntList 的型別宣告。它指定這不是一個任意的 List,而是 一個 Integer 的 List,寫作:List<Integer>。我們說 List 是一個帶一個型別 引數的泛型介面(這是關鍵)(a generic interface that takes a type parameter),本 例中,型別引數是 Integer。我們在建立這個 List 物件的時候也指定了一個 型別引數。
另一個需要注意的是第 3 行沒了型別轉換。
現在,你可能認為我們已經成功地去掉了程式裡的混亂。我們用第 1 行的型別引數取代了第 3 行的型別轉換。然而,這裡還有個很大的不同。 編譯器現在能夠在編譯時檢查程式的正確性。當我們說 myIntList 被宣告為 List<Integer>型別,這告訴我們無論何時何地使用 myIntList 變數,編譯器 保證其中的元素的正確的型別。
實際結果是,這可以增加可讀性和穩定性(robustness),尤其在大型的 程式中。
定義簡單的泛型
下面是從 java.util 包中的 List 介面和 Iterator 介面的定義中摘錄的片斷:
public interface List<E> {
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
1
2
3
4
5
6
7
8
這些都應該是很熟悉的,除了尖括號中的部分,那是介面 List 和 Iterat or 中的形式型別引數的宣告(the declarations of the formal type param eters of the interfaces List and Iterator)。
型別引數在整個類的宣告中可用,幾乎是所有可以使用其他普通型別的 地方
在介紹那一節我們看到了對泛型型別宣告 List (the generic type decl aration List) 的呼叫,如 List<Integer>。在這個呼叫中(通常稱作一個引數 化型別 a parameterized type),所有出現的形式型別引數(formal type pa rameter,這裡是 E)都被替換成實體型別引數(actual type argument)(這裡 是 Integer)。
你可能想象,List<Integer>代表一個 E 被全部替換成 Integer 的版本:
public interface IntegerList {
void add(Integer x)
Iterator<Integer> iterator();
}
1
2
3
4
型別引數就跟在方法或建構函式中普通的引數一樣。就像一個