什麼是對象:EVERYTHING IS OBJECT(萬物皆對象) java
全部的事物都有兩個方面:有什麼(屬性):用來描述對象。程序員
可以作什麼(方法):告訴外界對象有那些功能。
後者之前者爲基礎。
大的對象的屬性也能夠是一個對象。
爲何要使用面向對象:
首先,面向對象符合人類看待事物的通常規律。
對象的方法的實現細節是屏蔽的,只有對象方法的實現者瞭解細節。
方法的定義很是重要。方法有參數,也可能有返回值。
注意區分:對象(自己)、對象的實現者、對象的調用者。
分析對象主要從方法開始。
咱們經過類來看待對象,類是對象的抽象。
其次,採用面向對象方法可使系統各部分各司其職、各盡所能。
對象之間的耦合性必定要低(好比不一樣硬盤和不一樣主板之間的關係)。這樣才能使每一個對象自己作成最好的。
對於對象的要求:高內聚、低耦合,這樣容易拼裝成爲一個系統。
實現高內聚就是要最大限度低提升複用性(複用性好是由於高內聚)。
可複用性是
OOP
的基礎。
比較面向過程的思想和麪向對象的思想:
面向過程的思想:由過程、步驟、函數組成,以過程爲核心;
面向對象的思想:以對象爲中心,先開發類,獲得對象,經過對象之間相互通訊實現功能。
面向過程是先有算法,後有數據結構。
面向對象是先有數據結構,而後再有算法。
在用面向對象思想開發的過程當中,能夠複用對象就進行復用,如沒法進行復用則開發新的對象。
開發過程是用對個簡單的對象的多個簡單的方法,來實現複雜的功能
。
從語法上來看,一個類是一個新的數據類型。
在面向對象編程中,除了簡單數據類型,就是對象類型。
定義類的格式:
class Student{
代碼
}
注意類名中單詞的首字母大寫。
實例變量:定義在類中但在任何方法以外。(
New
出來的均有初值)
局部變量:定義在方法之中的變量。
局部變量要先賦值,再進行運算,而實例變量均已經賦初值。這是局部變量和實例變量的一大區別。
實例變量的對象賦值爲
null
。
局部變量不容許範圍內定義兩個同名變量。實例變量的做用域在本類中徹底有效,當被其餘的類調用的時候也可能有效。
實例變量和局部變量容許命名衝突。
書寫方法的格式:
修飾符
返回值
方法名
調用過程當中
方法體
可能出現的例外
public int/void addNumber(
參數
) throw Excepion {}
例:
public int addNumber(int a,int b){
}
注:方法名中的參數
int a,int b
爲局部變量
類方法中的一類特殊方法:構造方法。
構造方法是當用類生成對象時,系統在生成對象的過程當中利用的方法。
注意:構造方法在生成對象的時候會被調用,但並非構造方法生成了對象。
構造方法沒有返回值。格式爲:
public
方法名。
構造方法的方法名與類名相同。
構造方法是在對象生成的過程當中自動調用,不可能利用指令去調用。
在一個對象的生成周期中構造方法只用一次,一旦這個對象生成,那麼這個構造方法失效。
用類來生成對象的語句:
Student s=new Student()
。
第一個
Student
表示這是用
Student
類進行定義。「
Student()
」表示調用一個無參數的構造方法。
若是
()
中有參數,則系統構造對象的過程當中調用有參的方法。
此時
S
稱爲一個對象變量。
Student s
的存儲區域存放的是地址:一個對象在硬盤上佔有一個連續地址,首地址賦予
s
空間。
S
稱爲對象
Student
的引用。
注意:在對象變量中存放的是引用(地址);在簡單變量中存放的是數值。
能夠構造多個構造方法,但多個構造方法的參數表必定不一樣,參數順序不一樣即屬於不一樣的構造方法:
public student(string name,int a){
}
public student(int a,string name){
}
爲兩個不一樣的構造方法。
若是咱們未給系統提供一個構造方法,那麼系統會自動提供一個爲空的構造方法。
練習:寫一個類,定義一個對象,定義兩個構造方法:一個有參,一個無參。
(編寫一個程序驗證對象的傳遞的值爲地址)
注意下面這種形式:
static void changename(student stu){stu.setName 「LUCY」}
注意生成新的對象與舊對象指向無關,生成新對象生命消亡與舊對象無關。
面向對象方法的重載(
overloading
)和覆蓋(
overriding
)。
在有些
JAVA
書籍中將
overriding
稱爲重載,
overloading
稱爲過載。
Overloading
在一個類中能夠定義多個同名方法,各個方法的參數表必定不一樣。但修飾詞可能相同,返回值也可能相同。
在程序的編譯過程當中根據變量類型來找相應的方法。所以也有人認爲
overloading
是編譯時的多態,之後咱們還會學到運行時多態。
爲何會存在
overloading
技術呢?做爲應對方法的細節。
利用類型的差別來影響對方法的調用。
吃()能夠分爲吃肉,吃菜,吃藥,在一個類中能夠定義多個吃方法。
構造方法也能夠實現
overloading
。例:
public void teach(){};
public void teach(int a){};
public void teach(String a){}
爲三種不一樣的方法。
Overloading
方法是從低向高轉。
Byte
—
short
—
float
—
int
—
long
—
double
。
在構造方法中,
this
表示本類的其餘構造方法:
student(){};
student(string n){
this();//
表示調用
student()
}
若是調用
student(int a)
則爲
this(int a)
。
特別注意
:用
this
調用其餘構造方法時,
this
必須爲第一條語句,而後纔是其餘語句。
This
表示當前對象。
Public void printNum(){
Int number=40
;
System.out.println(this.number);
}
此時打印的是實例變量,而非局部變量,即定義在類中而非方法中的變量。
This.number
表示實例變量。
誰調用
this.number
那麼誰即爲當前
(this)
對象的
number
方法。
封裝:使對象的屬性儘量私有,對象的方法儘量的公開。用
private
表示此成員屬性爲該類的私有屬性。
Public
表示該屬性(方法)公開;
Private
表示該屬性(方法)爲只有本類內部能夠訪問(類內部可見)。
(想用
private
還要用
set
和
get
方法供其餘方法調用,這樣能夠保證對屬性的訪問方式統一,而且便於維護訪問權限以及屬性數據合法性)
若是沒有特殊狀況,屬性必定私有,方法該公開的公開。
若是不指明誰調用方法,則默認爲
this
。
區分實例變量和局部變量時必定要寫
this
。
11.29
繼承:
父類(
SuperClass
)和
子類(
SonClass
)。
父類的非私有化屬性和方法能夠默認繼承到子類。
Class Son extends Father{
}
而若是父類中的私有方法被子類調用的話,則編譯報錯。
父類的構造方法子類不能夠繼承,更不存在覆蓋的問題。(非構造方法能夠)
若是子類訪問父類的構造方法,則在編譯的時候提示訪問不到該方法。
JAVA
中不容許多繼承,一個類有且只有一個父類(單繼承)。
JAVA
的數據結構爲樹型結構,而非網狀。(
JAVA
經過接口和內部類實現多繼承)
方法的覆蓋(
overriding
)
方法的重載並不必定是在一個類中:子類能夠從父類繼承一個方法,也能夠定義一個同名異參的方法,也稱爲
overloading
。
當子類從父類繼承一個無參方法,而又定義了一個一樣的無參方法,則子類新寫的方法覆蓋父類的方法,稱爲覆蓋。(注意返回值類型也必須相同,不然編譯出錯。)
若是方法不一樣,則成重載。
對於方法的修飾詞,子類方法要比父類的方法範圍更加的寬泛。
父類爲
public
,那麼子類爲
private
則出現錯誤。
之因此構造方法先運行父類再運行子類是由於構造方法是沒法覆蓋的。
如下範圍依次由嚴到寬:
private
:本類訪問;
default
:表示默認,不只本類訪問,並且是同包可見。
Protected
:同包可見
+
不一樣包的子類可見
Public
:表示全部的地方都可見。
當構造一個對象的時候,系統先構造父類對象,再構造子類對象。
構造一個對象的順序:(注意:構造父類對象的時候也是這幾步)
①
遞歸地構造父類對象;
②
順序地調用本類成員屬性賦初值語句;
③
本類的構造方法。
Super()
表示調用父類的構造方法。
Super()
也和
this
同樣必須放在第一行。
This()
用於調用本類的構造方法。
若是沒有定義構造方法,那麼就會調用父類的無參構造方法,即
super()
。
要養成良好的編程習慣:就是要加上默認的父類無參的構造方法。
思考:但是若是咱們沒有定義無參的構造方法,而在程序中構造了有參的構造方法,那麼若是方法中沒有參數,那麼系統還會調用有參的構造方法麼?應該不會。
多態:多態指的是編譯時類型變化,而運行時類型不變。
多態分兩種:
①
編譯時多態:編譯時動態重載;
②
運行時多態:指一個對象能夠具備多個類型。
對象是客觀的,人對對象的認識是主觀的。
例:
Animal a=new Dog()
;查看格式名稱;
Dog d=(Dog)a
。聲明父類來引用子類。
(思考上面的格式)
運行時多態的三原則:(應用時爲覆蓋)
一、
對象不變;(改變的是主觀認識)
二、
對於對象的調用只能限於編譯時類型的方法,如調用運行時類型方法報錯。
在上面的例子中:Animal a=new Dog();對象a的編譯時類型爲Animal,運行時類型爲dog。
注意:編譯時類型必定要爲運行時類型的父類(或者同類型)。
對於語句:Dog d=(Dog)a。將d強制聲明爲a類型,此時d爲Dog(),此時d就能夠調用運行時類型。注意:a和d指向同一對象。
三、
在程序的運行時,動態類型斷定。運行時調用運行時類型,即它調用覆蓋後的方法。
關係運算符:
instanceof
a instanceof Animal;(
這個式子的結果是一個布爾表達式
)
a
爲對象變量,
Animal
是類名。
上面語句是斷定
a
是否能夠貼
Animal
標籤。若是能夠貼則返回
true
,不然返回
false
。
在上面的題目中:
a instanceof Animal
返回
True
,
a instanceof Dog
也返回
True
,
instanceof
用於斷定是否將前面的對象變量賦值後邊的類名。
Instanceof
通常用於在強制類型轉換以前斷定變量是否能夠強制轉換。
若是
Animal a=new Animal()
;
Dog d=Dog()a;
此時編譯無誤,但運行則會報錯。
Animal a=new Dog()
至關於下面語句的功能:
Animal a=getAnimal()
;
Public static Animal.getAnimal;
Return new Dog()
;
封裝、繼承、多態爲面向對象的三大基石(特性)。
運行時的動態類型斷定針對的是方法。運行程序訪問的屬性仍爲編譯時屬性。
Overloading
針對的是編譯時類型,不存在運行時的多態。
習題:創建一個
shape
類,有
circle
和
rect
子類。
Shape
類有
zhouchang()
和
area()
兩種方法。
(正方形)
squ
爲
rect
子類,
rect
有
cha()
用於比較長寬的差。
覆蓋時考慮子類的
private
及父類的
public
(考慮多態),之因此這樣是避免調用
A
時出現實際調用
B
的狀況。而出現錯誤。
11.29
下午講的是教程上的
Module6
Module6-7
包括:面向對象高級、內部類、集合、反射(暫時不講)、例外。
面向對象高級、集合和例外都是面向對象的核心內容。
面向對象高級:
修飾符:
static:
①
可修飾變量(屬性);
②可修飾方法;③可修飾代碼塊。
Static int data
語句說明
data
爲類變量,爲一個類的共享變量,屬於整個類。
Int data
爲實例變量。
例:
static int data;
m1.data=0;
m1.data++
的結果爲
1,
此時
m2.data
的結果也爲
1
。
Static
定義的是一塊爲整個類共有的一塊存儲區域,其發生變化時訪問到的數據都時通過變化的。
其變量能夠經過類名去訪問:類名
.
變量名。與經過訪問對象的編譯時類型訪問類變量爲等價的。
Public static void
printData(){}
代表此類方法爲類方法(靜態方法)
靜態方法不須要有對象,可使用類名調用。
靜態方法中不容許訪問類的非靜態成員,包括成員的變量和方法,由於此時是經過類調用的,沒有對象的概念。
This.data
是不可用的。
通常狀況下,主方法是靜態方法,因此可調用靜態方法,主方法爲靜態方法是由於它是整個軟件系統的入口,而進入入口時系統中沒有任何對象,只能使用類調用。
覆蓋不適用於靜態方法。
靜態方法不可被覆蓋。(容許在子類中定義同名靜態方法,可是沒有多態,嚴格的講,方法間沒有多態就不能稱爲覆蓋)
當
static
修飾代碼塊時(注:此代碼塊要在此類的任何一個方法以外),那麼這個代碼塊在代碼被裝載進虛擬機生成對象的時候可被裝載一次,之後不再執行了。
通常靜態代碼塊被用來初始化靜態成員。
Static
一般用於
Singleton
模式開發:
Singleton
是一種設計模式,高於語法,能夠保證一個類在整個系統中僅有一個對象。
11.30
final
能夠修飾類、屬性、方法。
當用
final
修飾類的時候,此類不可被繼承,即
final
類沒有子類。這樣能夠用
final
保證用戶調用時動做的一致性,能夠防止子類覆蓋狀況的發生。
當利用
final
修飾一個屬性(變量)的時候,此時的屬性成爲常量。
JAVA
利用
final
定義常量(注意在
JAVA
命名規範中常量須要所有字母都大寫):
Final int AGE=10
;
常量的地址不可改變,但在地址中保存的值(即對象的屬性)是能夠改變的。
Final
能夠配合
static
使用。
?
Static final int age=10
;
在
JAVA
中利用
public static final
的組合方式對常量進行標識(固定格式)。
對於在構造方法中利用
final
進行賦值的時候,此時在構造以前系統設置的默認值相對於構造方法失效。
常量(這裏的常量指的是實例常量:即成員變量)賦值:
①在初始化的時候經過顯式聲明賦值。Final int x=3;
②在構造的時候賦值。
局部變量能夠隨時賦值。
利用final定義方法:這樣的方法爲一個不可覆蓋的方法。
Public final void print(){}
;
爲了保證方法的一致性(即不被改變),可將方法用final定義。
若是在父類中有final定義的方法,那麼在子類中繼承同一個方法。
若是一個方法前有修飾詞private或static,則系統會自動在前面加上final。即private和static方法默認均爲final方法。
注:final並不涉及繼承,繼承取決於類的修飾符是否爲private、default、protected仍是public。也就是說,是否繼承取決於這個方法對於子類是否可見。
Abstract(
抽象)能夠修飾類、方法
若是將一個類設置爲abstract,則此類必須被繼承使用。此類不可生成對象,必須被繼承使用。
Abstract
能夠將子類的共性最大限度的抽取出來,放在父類中,以提升程序的簡潔性。
Abstract
雖然不能生成對象,可是能夠聲明,做爲編譯時類型,但不能做爲運行時類型。
Final
和abstract永遠不會同時出現。
當abstract用於修飾方法時,此時該方法爲抽象方法,此時方法不須要實現,實現留給子類覆蓋,子類覆蓋該方法以後方法纔可以生效。
注意比較:
private void print(){}
;此語句表示方法的空實現。
Abstract void print()
; 此語句表示方法的抽象,無實現。
若是一個類中有一個抽象方法,那麼這個類必定爲一個抽象類。
反之,若是一個類爲抽象類,那麼其中可能有非抽象的方法。
若是讓一個非抽象類繼承一個含抽象方法的抽象類,則編譯時會發生錯誤。由於當一個非抽象類繼承一個抽象方法的時候,本着只有一個類中有一個抽象方法,那麼這個類必須爲抽象類的原則。這個類必須爲抽象類,這與此類爲非抽象衝突,因此報錯。
因此子類的方法必須覆蓋父類的抽象方法。方法纔可以起做用。
只有將理論被熟練運用在實際的程序設計的過程當中以後,才能說理論被徹底掌握!
爲了實現多態,那麼父類必須有定義。而父類並不實現,留給子類去實現。此時可將父類定義成abstract類。若是沒有定義抽象的父類,那麼編譯會出現錯誤。
Abstract
和
static
不能放在一塊兒,不然便會出現錯誤。(這是由於
static
不可被覆蓋,而
abstract
爲了生效必須被覆蓋。)
例:(本例已存在
\CODING\abstract\TestClass.java
文件中)
public class TestClass{
public static void main(String[] args){
SuperClass sc=new SubClass();
Sc.print();
}
Abstract class SuperClass{
Abstract void print();}
}
class SubClass extends SuperClass(){
void print(){
System.out.println(「print」);}
}
JAVA
的核心概念:接口(
interface
)
接口與類屬於同一層次,實際上,接口是一種特殊的抽象類。
如
:
interface IA{
}
public interface
:公開接口
與類類似,一個文件只能有一個
public
接口,且與文件名相同。
在一個文件中不可同時定義一個
public
接口和一個
public
類。
一個接口中,全部方法爲公開、抽象方法;全部的屬性都是公開、靜態、常量。
一個類實現一個接口的格式:
class IAImple implements IA{
};
一個類實現接口,至關於它繼承一個抽象類。
類必須實現接口中的方法,不然其爲一抽象類。
實現中接口和類相同。
接口中可不寫
public
,但在子類中實現接口的過程當中
public
不可省。
(若是剩去
public
則在編譯的時候提示出錯:對象沒法從接口中實現方法。)
注:
①
一個類除繼承另一個類,還能夠實現接口;
class IAImpl extends java.util.Arrylist implement IA{}
繼承類 實現接口
這樣能夠實現變相的多繼承。
②
一個類只能繼承另一個類,可是它能夠繼承多個接口,中間用「,」隔開。
Implements IA,IB
所謂實現一個接口,就是指實現接口中的方法。
③
接口和接口之間能夠定義繼承關係,而且接口之間容許實現多繼承。
例:interface IC extends IA,IB{};
接口也能夠用於定義對象
IA I=new IAImpl();
實現的類從父類和接口繼承的均可作運行時類型。
IAImple extends A implement IA,IB
IB I=new IAImple();
I instance of IAImple;
I instance of A;
I instance of IA;
I instance of IB;
返回的結果均爲true.
接口和多態都爲JAVA技術的核心。
接口每每被咱們定義成一類XX的東西。
接口其實是定義一個規範、標準。
①
經過接口能夠實現不一樣層次、不一樣體系對象的共同屬性;
經過接口實現write once as anywhere.
以JAVA數據庫鏈接爲例子:JDBC制定標準;數據廠商實現標準;用戶使用標準。
接口一般用來屏蔽底層的差別。
②接口也由於上述緣由被用來保持架構的穩定性。
JAVA
中有一個特殊的類:
Object
。它是
JAVA
體系中全部類的父類(直接父類或者間接父類)。
此類中的方法可使所的類均繼承。
如下介紹的三種方法屬於
Object:
(1)
finalize
方法:當一個對象被垃圾回收的時候調用的方法。
(2)
toString():
是利用字符串來表示對象。
當咱們直接打印定義的對象的時候,隱含的是打印
toString()
的返回值。
能夠經過子類做爲一個
toString()
來覆蓋父類的
toString()
。
以取得咱們想獲得的表現形式,即當咱們想利用一個自定義的方式描述對象的時候,咱們應該覆蓋
toString()
。
(3)equal
首先試比較下例:
String A=new String(「hello」);
String A=new String(「hello」);
A==B(
此時程序返回爲
FALSE)
由於此時
AB
中存的是地址,由於建立了新的對象,因此存放的是不一樣的地址。
附加知識:
字符串類爲
JAVA
中的特殊類,
String
中爲
final
類,一個字符串的值不可重複。所以在
JAVA VM
(虛擬機)中有一個字符串池,專門用來存儲字符串。若是遇到
String a=」hello」
時(注意沒有
NEW
,不是建立新串),系統在字符串池中尋找是否有
」hello」
,此時字符串池中沒有
」hello」
,那麼系統將此字符串存到字符串池中,而後將
」hello」
在字符串池中的地址返回
a
。若是系統再遇到
String b=」hello」
,此時系統能夠在字符串池中找到
「hello」
。則會把地址返回
b
,此時
a
與
b
爲相同。
String a=」hello」;
System.out.println(a==」hello」);
系統的返回值爲
true
。
故若是要比較兩個字符串是否相同(而不是他們的地址是否相同)。能夠對
a
調用
equal:
System.out.println(a.equal(b));
equal
用來比較兩個對象中字符串的順序。
a.equal(b)
是
a
與
b
的值的比較。
注意下面程序:
student a=new student(「LUCY」,20);
student b=new student(「LUCY」,20);
System.out.println(a==b);
System.out.println(a.equal(b));
此時返回的結果均爲
false
。
如下爲定義
equal
(加上這個定義,返回
ture
或
false
)
public boolean equals(Object o){
student s=(student)o;
if (s.name.equals(this.name)&&s.age==this.age)
else return false;
}
若是
equals()
返回的值爲
如下爲實現標準
equals
的流程:
public boolean equals(Object o){
if (this==o) return trun; //
此時二者相同
if (o==null) return false;
if (! o instanceof strudent) return false; //
不一樣類
studeng s=(student)o; //
強制轉換
if (s.name.equals(this.name)&&s.age==this.age) return true;
else return false;
}
以上過程爲實現
equals
的標準過程。
練習:創建一個
employee
類,有
String name,int id,double salary.
運用
get
和
set
方法,使用
toString
,使用
equals
。
封裝類:
JAVA
爲每個簡單數據類型提供了一個封裝類,使每一個簡單數據類型能夠被
Object
來裝載。
除了
int
和
char
,其他類型首字母大寫即成封裝類。
轉換字符的方式:
int I=10;
String s=I+」 」;
String s1=String.valueOf(i);
Int I=10;
Interger I_class=new integer(I);
看
javadoc
的幫助文檔。
附加內容:
「
==
」在任什麼時候候都是比較地址,這種比較永遠不會被覆蓋。
程序員本身編寫的類和
JDK
類是一種合做關係。(由於多態的存在,可能存在咱們調用
JDK
類的狀況,也可能存在
JDK
自動調用咱們的類的狀況。)
注意:類型轉換中
double\interger\string
之間的轉換最多。
12.01
內部類:
(注:全部使用內部類的地方均可以不用內部類,使用內部類可使程序更加的簡潔,便於命名規範和劃分層次結構)。
內部類是指在一個外部類的內部再定義一個類。
內部類做爲外部類的一個成員,而且依附於外部類而存在的。
內部類可爲靜態,可用
PROTECTED
和
PRIVATE
修飾。(而外部類不能夠:外部類只能使用
PUBLIC
和
DEFAULT
)。
內部類的分類:
成員內部類、
局部內部類、
靜態內部類、
匿名內部類(圖形是要用到,必須掌握)。
①
成員內部類:做爲外部類的一個成員存在,與外部類的屬性、方法並列。
內部類和外部類的實例變量能夠共存。
在內部類中訪問實例變量:
this.
屬性
在內部類訪問外部類的實例變量:外部類名
.this.
屬性。
成員內部類的優勢:
⑴
內部類做爲外部類的成員,能夠訪問外部類的私有成員或屬性。(即便將外部類聲明爲
PRIVATE
,可是對於處於其內部的內部類仍是可見的。)
⑵
用內部類定義在外部類中不可訪問的屬性。這樣就在外部類中實現了比外部類的
private
還要小的訪問權限。
注意:內部類是一個編譯時的概念,一旦編譯成功,就會成爲徹底不一樣的兩類。
對於一個名爲
outer
的外部類和其內部定義的名爲
inner
的內部類。編譯完成後出現
outer.class
和
outer$inner.class
兩類。
(編寫一個程序檢驗:在一個
TestOuter.java
程序中驗證內部類在編譯完成以後,會出現幾個
class.
)
成員內部類不能夠有靜態屬性。(爲何?)
若是在外部類的外部訪問內部類,使用
out.inner.
創建內部類對象時應注意:
在外部類的內部能夠直接使用
inner s=new inner();
(由於外部類知道
inner
是哪一個類,因此能夠生成對象。)
而在外部類的外部,要生成(
new
)一個內部類對象,須要首先創建一個外部類對象(外部類可用),而後在生成一個內部類對象。
Outer.Inner in=Outer.new.Inner()
。
錯誤的定義方式:
Outer.Inner in=new Outer.Inner()
。
注意:當
Outer
是一個
private
類時,外部類對於其外部訪問是私有的,因此就沒法創建外部類對象,進而也沒法創建內部類對象。
②
局部內部類:在方法中定義的內部類稱爲局部內部類。
與局部變量相似,在局部內部類前不加修飾符
public
和
private
,其範圍爲定義它的代碼塊。
注意:局部內部類不只能夠訪問外部類實例變量,還能夠訪問外部類的局部變量(但此時要求外部類的局部變量必須爲
final
)??
在類外不可直接生成局部內部類(保證局部內部類對外是不可見的)。
要想使用局部內部類時須要生成對象,對象調用方法,在方法中才能調用其局部內部類。
③
靜態內部類:(注意:前三種內部類與變量相似,因此能夠對照參考變量)
靜態內部類定義在類中,任何方法外,用
static
定義。
靜態內部類只能訪問外部類的靜態成員。
生成(
new
)一個靜態內部類不須要外部類成員:這是靜態內部類和成員內部類的區別。靜態內部類的對象能夠直接生成:
Outer.Inner in=new Outer.Inner()
;
而不須要經過生成外部類對象來生成。這樣實際上使靜態內部類成爲了一個頂級類。
靜態內部類不可用
private
來進行定義。例子:
對於兩個類,擁有相同的方法:
People
{
run();
}
Machine{
run();
}
此時有一個
robot
類:
class Robot extends People implement Machine.
此時
run()
不可直接實現。
注意:當類與接口(或者是接口與接口)發生方法命名衝突的時候,此時必須使用內部類來實現。
用接口不能徹底地實現多繼承,用接口配合內部類才能實現真正的多繼承。
④
匿名內部類(必須掌握):
匿名內部類是一種特殊的局部內部類,它是經過匿名類實現接口。
IA
被定義爲接口。
IA I=new IA(){};
注:一個匿名內部類必定是在
new
的後面,用其隱含實現一個接口或實現一個類,沒有類名,根據多態,咱們使用其父類名。
因其爲局部內部類,那麼局部內部類的全部限制都對其生效。
匿名內部類是惟一一種無構造方法類。
匿名內部類在編譯的時候由系統自動起名
Out$1.class
。
若是一個對象編譯時的類型是接口,那麼其運行的類型爲實現這個接口的類。
因匿名內部類無構造方法,因此其使用範圍很是的有限。
(下午:)
Exception
(例外
/
異常)(教程上的
MODEL7
)
對於程序可能出現的錯誤應該作出預案。
例外是程序中全部出乎意料的結果。(關係到系統的健壯性)
JAVA
會將全部的錯誤封裝成爲一個對象,其根本父類爲
Throwable
。
Throwable
有兩個子類:
Error
和
Exception
。
一個
Error
對象表示一個程序錯誤,指的是底層的、低級的、不可恢復的嚴重錯誤。此時程序必定會退出,由於已經失去了運行所必須的物理環境。
對於
Error
錯誤咱們沒法進行處理,由於咱們是經過程序來應對錯誤,但是程序已經退出了。
咱們能夠處理的
Throwable
對象中只有
Exception
對象(例外
/
異常)。
Exception
有兩個子類:
Runtime exception
(未檢查異常)
非
Runtime exception
(已檢查異常)
(注意:不管是未檢查異常仍是已檢查異常在編譯的時候都不會被發現,在編譯的過程當中檢查的是程序的語法錯誤,而異常是一個運行時程序出錯的概念。)
在
Exception
中,全部的非未檢查異常都是已檢查異常,沒有另外的異常!!
未檢查異常是由於程序員沒有進行必要的檢查,由於他的疏忽和錯誤而引發的異常。必定是屬於虛擬機內部的異常(好比空指針)。
應對未檢查異常就是養成良好的檢查習慣。
已檢查異常是不可避免的,對於已檢查異常必須實現定義好應對的方法。
已檢查異常確定跨越出了虛擬機的範圍。(好比「未找到文件」)
如何處理已檢查異常(對於全部的已檢查異常都要進行處理):
首先了解異常造成的機制:
當一個方法中有一條語句出現了異常,它就會
throw
(拋出)一個例外對象,而後後面的語句不會執行返回上一級方法,其上一級方法接受到了例外對象以後,有可能對這個異常進行處理,也可能將這個異常轉到它的上一級。
對於接收到的已檢查異常有兩種處理方式:
throws
和
try
方法。
注意:出錯的方法有多是
JDK
,也多是程序員寫的程序,不管誰寫的,拋出必定用
throw
。
例:
public void print() throws Exception.
對於方法
a
,若是它定義了
throws Exception
。那麼當它調用的方法
b
返回異常對象時,方法
a
並不處理,而將這個異常對象向上一級返回,若是全部的方法均不進行處理,返回到主方法,程序停止。(要避免全部的方法都返回的使用方法,由於這樣出現一個很小的異常就會令程序停止)。
若是在方法的程序中有一行
throw new Exception()
,返回錯誤,那麼其後的程序不執行。由於錯誤返回後,後面的程序確定沒有機會執行,那麼
JAVA
認爲之後的程序沒有存在的必要。
對於
try
……
catch
格式:
try {
可能出現錯誤的代碼塊
} catch(exception e){
進行處理的代碼
}
;
對象變量的聲明
用這種方法,若是代碼正確,那麼程序不通過
catch
語句直接向下運行;
若是代碼不正確,則將返回的異常對象和
e
進行匹配,若是匹配成功,則處理其後面的異常處理代碼。(若是用
exception
來聲明
e
的話,由於
exception
爲全部
exception
對象的父類,全部確定匹配成功)。處理完代碼後這個例外就徹底處理完畢,程序會接着從出現異常的地方向下執行(是從出現異常的地方仍是在
catch
後面呢?利用程序進行驗證)。最後程序正常退出。
Try
中若是發現錯誤,即跳出
try
去匹配
catch
,那麼
try
後面的語句就不會被執行。
一個
try
能夠跟進多個
catch
語句,用於處理不一樣狀況。當一個
try
只能匹配一個
catch
。
咱們能夠寫多個
catch
語句,可是不能將父類型的
exception
的位置寫在子類型的
excepiton
以前,由於這樣父類型確定先於子類型被匹配,全部子類型就成爲廢話。
JAVA
編譯出錯。
在
try
,
catch
後還能夠再跟一子句
finally
。其中的代碼語句不管如何都會被執行(由於
finally
子句的這個特性,因此通常將釋放資源,關閉鏈接的語句寫在裏面)。
若是在程序中書寫了檢查(拋出)
exception
可是沒有對這個可能出現的檢查結果進行處理,那麼程序就會報錯。
而若是隻有處理狀況(
try
)而沒有相應的
catch
子句,則編譯仍是通不過。
如何知道在編寫的程序中會出現例外呢
1.
調用方法,查看
API
中查看方法中是否有已檢查錯誤。
2.
在編譯的過程當中看提示信息,而後加上相應的處理。
Exception
有一個
message
屬性。在使用
catch
的時候能夠調用:
Catch(IOException e){System.out.println(e.message())};
Catch(IOException e){e.printStackTrace()};
上面這條語句回告訴咱們出錯類型所歷經的過程,在調試的中很是有用。
開發中的兩個道理:
①如何控制try的範圍:根據操做的連動性和相關性,若是前面的程序代碼塊拋出的錯誤影響了後面程序代碼的運行,那麼這個咱們就說這兩個程序代碼存在關聯,應該放在同一個try中。
②
對已經查出來的例外,有throw(積極)和try catch(消極)兩種處理方法。
對於try catch放在可以很好地處理例外的位置(即放在具有對例外進行處理的能力的位置)。若是沒有處理能力就繼續上拋。
當咱們本身定義一個例外類的時候必須使其繼承excepiton或者RuntimeException。
Throw
是一個語句,用來作拋出例外的功能。
而throws是表示若是下級方法中若是有例外拋出,那麼本方法不作處理,繼續向上拋出。
Throws
後跟的是例外類型。
斷言是一種調試工具(assert)
其後跟的是布爾類型的表達式,若是表達式結果爲真不影響程序運行。若是爲假系統出現低級錯誤,在屏幕上出現assert信息。
Assert
只是用於調試。在產品編譯完成後上線assert代碼就被刪除了。
方法的覆蓋中,若是子類的方法拋出的例外是父類方法拋出的例外的父類型,那麼編譯就會出錯:子類沒法覆蓋父類。
結論:子類方法不可比父類方法拋出更多的例外。子類拋出的例外或者與父類拋出的例外一致,或者是父類拋出例外的子類型。或者子類型不拋出例外。
若是父類型無
throws
時,子類型也不容許出現
throws
。此時只能使用
try catch
。
練習:寫一個方法:
int add(int a,int b)
{
return a+b
;
}
當
a+b=100;
拋出
100
爲異常處理。
12.02
集合(從本部分開始涉及
API
)
集合是指一個對象容納了多個對象,這個集合對象主要用來管理維護一系列類似的對象。
數組就是一種對象。(練習:如何編寫一個數組程序,並進行遍歷。)
java.util.*
定義了一系列的接口和類,告訴咱們用什麼類
NEW
出一個對象,能夠進行超越數組的操做。
(注:
JAVA1.5
對
JAVA1.4
的最大改進就是增長了對範型的支持)
集合框架接口的分類:(分
collection
接口
和
map
接口)
Collection
接口
Map
接口
List
接口
Set
接口
SortedMap
接口
SortedSet
接口
JAVA
中全部與集合有關的實現類都是這六個接口的實現類。
Collection
接口:集合中每個元素爲一個對象,這個接口將這些對象組織在一塊兒,造成一維結構。
List
接口表明按照元素必定的相關順序來組織(在這個序列中順序是主要的),
List
接口中數據可重複。
Set
接口是數學中集合的概念:其元素無序,且不可重複。(正好與
List
對應)
SortedSet
會按照數字將元素排列,爲「可排序集合」。
Map
接口中每個元素不是一個對象,而是一個鍵對象和值對象組成的鍵值對(
Key-Value
)。
Key-Value
是用一個不可重複的
key
集合對應可重複的
value
集合。(典型的例子是字典:經過頁碼的
key
值找字的
value
值)。
例子:
key1
—
value1;
key2
—
value2;
key3
—
value3.
SortedMap
:若是一個
Map
能夠根據
key
值排序,則稱其爲
SortedMap
。(如字典)
!!
注意數組和集合的區別:數組中只能存簡單數據類型。
Collection
接口和
Map
接口只能存對象。
如下介紹接口:
List
接口:(介紹其下的兩個實現類:
ArrayList
和
LinkedList
)
ArrayList
和數組很是相似,其底層
①
也用數組組織數據,
ArrayList
是動態可變數組。
①
底層:指存儲格式。說明
ArrayList
對象都是存在於數組中。
注:數組和集合都是從下標
0
開始。
ArrayList
有一個
add(Object o)
方法用於插入數組。
ArrayList
的使用:(完成這個程序)
先
import java.util.*
;
用
ArrayList
在一個數組中添加數據,並遍歷。
ArrayList
中數組的順序與添加順序一致。
只有
List
可用
get
和
size
。而
Set
則不可用(因其無序)。
Collection
接口都是經過
Iterator()
(即迭代器)來對
Set
和
List
遍歷。
經過語句:
Iterator it=c.iterator();
獲得一個迭代器,將集合中全部元素順序排列。而後能夠經過
interator
方法進行遍歷,迭代器有一個遊標(指針)指向首位置。
Interator
有
hasNext()
,用於判斷元素右邊是否還有數據,返回
True
說明有。而後就能夠調用
next
動做。
Next()
會將遊標移到下一個元素,並把它所跨過的元素返回。(這樣就能夠對元素進行遍歷)
練習:寫一個程序,輸入對象信息,比較基本信息。
集合中每個元素都有對象,若有字符串要通過強制類型轉換。
Collections
是工具類,全部方法均爲有用方法,且方法爲
static
。
有
Sort
方法用於給
List
排序。
Collections.Sort()
分爲兩部分,一部分爲排序規則;一部分爲排序算法。
規則用來判斷對象;算法是考慮如何排序。
對於自定義對象,
Sort
不知道規則,因此沒法比較。這種狀況下必定要定義排序規則。方式有兩種:
①
java.lang
下面有一個接口:Comparable(可比較的)
可讓自定義對象實現一個接口,這個接口只有一個方法comparableTo(Object o)
其規則是當前對象與o對象進行比較,其返回一個int值,系統根據此值來進行排序。
如 當前對象>o對象,則返回值>0;(可將返回值定義爲1)
如 當前對象=o對象,則返回值=0;
如 當前對象<o對象,則返回值〈0。(可將返回值定義爲-1)
看TestArraylist的java代碼。
咱們經過返回值1和-1位置的調換來實現升序和降序排列的轉換。
②
java.util
下有一個Comparator(比較器)
它擁有compare(),用來比較兩個方法。
要生成比較器,則用Sort中Sort(List,List(Compate))
第二種方法更靈活,且在運行的時候不用編譯。
注意:要想實現comparTo()就必須在主方法中寫上implement comparable.
練習:生成一個EMPLOYEE類,而後將一系列對象放入到ArrayList。用Iterator遍歷,排序以後,再進行遍歷。
集合的最大缺點是沒法進行類型斷定(這個缺點在
JAVA1.5
中已經解決),這樣就可能出現由於類型不一樣而出現類型錯誤。
解決的方法是添加類型的判斷。
LinkedList
接口(在代碼的使用過程當中和
ArrayList
沒有什麼區別)
ArrayList
底層是
object
數組,因此
ArrayList
具備數組的查詢速度快的優勢以及增刪速度慢的缺點。
而在
LinkedList
的底層是一種雙向循環鏈表。在此鏈表上每個數據節點都由三部分組成:前指針(指向前面的節點的位置),數據,後指針(指向後面的節點的位置)。最後一個節點的後指針指向第一個節點的前指針,造成一個循環。
雙向循環鏈表的查詢效率低可是增刪效率高。因此
LinkedList
具備查詢效率低但增刪效率高的特色。
ArrayList
和
LinkedList
在用法上沒有區別,可是在功能上仍是有區別的。
LinkedList
常常用在增刪操做較多而查詢操做不多的狀況下:隊列和堆棧。
隊列:先進先出的數據結構。
堆棧:後進先出的數據結構。
注意:使用堆棧的時候必定不能提供方法讓不是最後一個元素的元素得到出棧的機會。
LinkedList
提供如下方法:(
ArrayList
無此類方法)
addFirst();
removeFirst();
addLast();
removeLast();
在堆棧中,
push
爲入棧操做,
pop
爲出棧操做。
Push
用
addFirst()
;
pop
用
removeFirst()
,實現後進先出。
用
isEmpty()--
其父類的方法,來判斷棧是否爲空。
在隊列中,
put
爲入隊列操做,
get
爲出隊列操做。
Put
用
addFirst()
,
get
用
removeLast()
實現隊列。
List
接口的實現類(
Vector
)(與
ArrayList
類似,區別是
Vector
是重量級的組件,使用使消耗的資源比較多。)
結論:在考慮併發的狀況下用
Vector
(保證線程的安全)。
在不考慮併發的狀況下用
ArrayList
(不能保證線程的安全)。
面試經驗(知識點):
java.util.stack
(
stack
即爲堆棧)的父類爲
Vector
。但是
stack
的父類是最不該該爲
Vector
的。由於
Vector
的底層是數組,且
Vector
有
get
方法(意味着它可能訪問到並不屬於最後一個位置元素的其餘元素,很不安全)。
對於堆棧和隊列只能用
push
類和
get
類。
Stack
類之後不要輕易使用。
!!!實現堆棧必定要用
LinkedList
。
(在
JAVA1.5
中,
collection
有
queue
來實現隊列。)
Set-HashSet
實現類:
遍歷一個
Set
的方法只有一個:迭代器(
interator
)。
HashSet
中元素是無序的(這個無序指的是數據的添加順序和後來的排列順序不一樣),並且元素不可重複。
在
Object
中除了有
final()
,
toString()
,
equals()
,還有
hashCode()
。
HashSet
底層用的也是數組。
當向數組中利用
add(Object o)
添加對象的時候,系統先找對象的
hashCode
:
int hc=o.hashCode();
返回的
hashCode
爲整數值。
Int I=hc%n;
(
n
爲數組的長度),取得餘數後,利用餘數向數組中相應的位置添加數據,以
n
爲
6
爲例,若是
I=0
則放在數組
a[0]
位置,若是
I=1,
則放在數組
a[1]
位置。若是
equals()
返回的值爲
true
,則說明數據重複。若是
equals()
返回的值爲
false
,則再找其餘的位置進行比較。這樣的機制就致使兩個相同的對象有可能重複地添加到數組中,由於他們的
hashCode
不一樣。
若是咱們可以使兩個相同的對象具備相同
hashcode
,才能在
equals()
返回爲真。
在實例中,定義
student
對象時覆蓋它的
hashcode
。
由於
String
類是自動覆蓋的,因此當比較
String
類的對象的時候,就不會出現有兩個相同的
string
對象的狀況。
如今,在大部分的
JDK
中,都已經要求覆蓋了
hashCode
。
結論:如將自定義類用
hashSet
來添加對象,必定要覆蓋
hashcode()
和
equals()
,覆蓋的原則是保證當兩個對象
hashcode
返回相同的整數,並且
equals()
返回值爲
True
。
若是偷懶,沒有設定
equals()
,就會形成返回
hashCode
雖然結果相同,但在程序執行的過程當中會屢次地調用
equals()
,從而影響程序執行的效率。
咱們要保證相同對象的返回的
hashCode
必定相同,也要保證不相同的對象的
hashCode
儘量不一樣(由於數組的邊界性,
hashCode
仍是可能相同的)。例子:
public int hashCode(){
return name.hashcode()+age;
}
這個例子保證了相同姓名和年齡的記錄返回的
hashCode
是相同的。
使用
hashSet
的優勢:
hashSet
的底層是數組,其查詢效率很是高。並且在增長和刪除的時候因爲運用的
hashCode
的比較開肯定添加元素的位置,因此不存在元素的偏移,因此效率也很是高。由於
hashSet
查詢和刪除和增長元素的效率都很是高。
可是
hashSet
增刪的高效率是經過花費大量的空間換來的:由於空間越大,取餘數相同的狀況就越小。
HashSet
這種算法會創建許多無用的空間。
使用
hashSet
接口時要注意,若是發生衝突,就會出現遍歷整個數組的狀況,這樣就使得效率很是的低。
練習:
new
一個
hashset
,插入
employee
對象,不容許重複,而且遍歷出來。
添加知識點:
集合對象存放的是一系列對象的引用。
例:
Student S
Al.add(s);
s.setName(「lucy」);
Student s2=(Student)(al.get(o1));
可知
s2
也是
s
。