面向對象(下)
6.1 java8增強的包裝類
int Integer
char Character
其他的都是直接首字母變大寫
可以自動裝箱,自動拆箱。
如果int想變成Integer,直接賦值給一個Integer 變量就行。
如果Integer想變成int,直接賦值給一個int變量就行。
基本類型和字符串之間的轉換方法
- 字符串轉換成基本類型
int i=Integer.parseInt(str);
或者
int i=new Integer(str); - 基本類型轉換成字符串
String flStr=String.valueOf(float變量);
或者
String flStr=float變量+“”;
包裝類比較大小
- 如果直接給integer數字,如果數字在-128-127之間,則兩個數字相等,包裝類相等。
- 如果用new integer(2)賦值,則必須兩個包裝類指向同一個對象才相等。
- 有一個Integer.compare(a,b)可以直接用來比較包裝類大小.
a>b 返回1
a=b 返回0
a<b 返回-1
6.2 處理對象
6.2.1 打印對象和toString方法
toString方法
toString () 是Object類里面的一個實例方法,所有對象都具有這個方法。
系統自帶的tostring“類名+@+hashcode”
可以自己重寫這個方法。
6.2.2 ==和equals方法
“==”
- 對于基本類型的數值類型(包括char),只要兩個變量的值相等,返回true。
- 對于引用類型,必須指向同一個對象,才返回true。
注意:“==“不能用于比較沒有父子關系的兩個對象。
”hello“直接量和new String (“hello”)區別
直接使用hello這種可以在編譯時候計算出來的字符串值,jvm會用常量池來管理。jvm會保證相同的字符串直接量只有一個,不會產生多個副本。
第二種,jvm會先用常量池來管理直接量,再調用string構造器來創建 i 個新的string對象。新創建的對象保存在堆中
equals()
equals()方法可以用來判斷字符串的內容是否相等。
但是當用于對象時,仍然必須是指向同一個對象才能夠返回true。
一般需要重寫equals()。
- 首先判斷是否是同一個對象
- 如果不是同一個對象,在判斷是否是同一個類型
- 如果是的話,把object轉化為目標類型,采用equals方法比較內容。
6.3 類成員
6.3.1 理解類成員
類成員就是用static修飾的成員變量,方法,初始化塊,內部類的統稱。能通過對象訪問這些類成員。即使對象是null 。類成員不能訪問實例成員,因為類成員的作用域更大,可能類成員還存在,但是成員變量已經不存在了,所以不能夠訪問實例成員。
6.3.2 單例類
一個類始終只能創建一個實例,稱為單例類。
如果不想讓別的類輕易的創建該類的對象,就需要把類的構造函數設置成private,但是這個時候就需要有一個方法來創建一個對象,由于使用這個方法的時候還沒有對象,因此需要給方法是static的。
同時。為了滿足只能創建一個對象,則必須把已經創建的對象保存起來,每次創建對象之前都檢查一下是否只有一個對象。由于存儲對象的成員變量需要通過上面的static方法調用,因此需要設置為static。
package demo6;
public class Singleton {
//定義一個類變量用來保存創建出來的對象
private static Singleton instance;
private Singleton(){}
//定義一個能夠返回對象的方法
public static Singleton getSingleton()
{
if(instance==null)
{
instance=new Singleton();
}
return instance;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton s1=Singleton.getSingleton();
Singleton s2=Singleton.getSingleton();
System.out.print(s1==s2);
}
}
6.4 final修飾符
final修飾變量時候表示該變量一旦獲得了初始值,就無法再改變。
6.4.1 final成員變量
類變量:在靜態初始化塊或者聲明該變量時初始化
實例變量:在普通初始化塊,或者聲明該變量時候,或者構造器。如果已經在普通初始化塊賦值,則不可以再在構造器中賦值。
注意?:普通方法不能訪問final修飾的成員變量。
final修飾的成員變量必須程序員自己顯示初始化,系統不會默認賦值。
6.4.2 final局部變量
因為系統不會給局部變量初始化,因此在用final修飾的局部變量不一定非由程序員顯示初始化。
方法中的形參不能夠在方法里面初始化,因為形參的初始化是在被調用的時候由實參傳入的。
6.4.3 final修飾基本類型變量和引用類型變量的區別
final修飾基本類型變量,就不能更改值了。如果是引用類型,則只要不改變地址就行,里面的內容可以隨意
package demo6;
import java.util.Arrays;
class Person1
{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person1() {}
public Person1(int age)
{
this.age=age;
}
}
public class FinalReferenceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//可以無限的更改數組里面的內容,但是不能夠更改數組的地址
final int[] iArr={3,4,5,6};
iArr[2]=19;
System.out.print(Arrays.toString(iArr));
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
//iArr=null;
Person1 p1=new Person1();
p1.setAge(7);
//這里竟然可以更改,,,,,不懂不懂
p1=null;
System.out.print(iArr);
}
}
6.4.4 可執行“宏替換”的final變量
變量滿足三個條件,則變量相當于一個直接量
- final修飾
- 在定義的時候指定了初始值(只有在定義的時候賦值才有這種效果)
- 值可以在編譯的時候確定下來
6.4.5 final 方法
final方法不能夠被重寫。如果父類的方法不希望子類重寫,只要加上final 就好。
但是父類的private 是不會被子類繼承的,因此也不會有重寫這個說法。因此如果父類的private方法被final了,子類仍然可以寫一個一樣的方法。
6.4.6 final類
不能有子類的類
6.4.7 不可變類
不可變類是創建該類的實例后,實例變量不能夠更改。
創建自定義的不可變類方法:
1,類的成員變量用private和final修飾
2,提供帶參數的構造函數,用于根據傳入參數來初始化類的成員變量
3,該類僅有getter
4,重新定義equals()和hashcode()
package demo6;
public class Address {
private final String detail;
private final String postCode;
public Address()
{
this.detail="";
this.postCode="";
}
public Address(String detail,String postCode)
{
this.detail=detail;
this.postCode=postCode;
}
public String getDetail()
{
return detail;
}
public String getPostCode()
{
return postCode;
}
public boolean equals(Object obj)
{
if(obj==this)
{
return true;
}
if(obj!=null&&obj.getClass().equals(Address.class))
{
Address AddObj=(Address)obj;
if(AddObj.getDetail().equals(this.getDetail()))
{
return true;
}
}
return false;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
當創建不可變類的時候,如果成員變量里面有引用類型,則很可能創建出一個可變類,因為成員變量的內容可以更改。必須采用一些其他的方法,才能創建真正的不可變類。
6.4.8 緩存實例的不可變類
緩存實例的不可變類,是因為有的時候一個對象的某個成員被多次引用,為了節省開銷,可以把它緩存起來。下邊是把它緩存在數組里面,如果緩存里面已經有了,就直接返回實例,如果沒有,就新建實例加進去。
package demo6;
//這個類主要是弄一個數組,然后緩存string數據
class CacheInnutale
{
private static int MAX_SIZE;
private static CacheInnutale[] cache=new CacheInnutale[MAX_SIZE];
//pos為什么要是static
private static int pos=0;
private final String name;
public String getName() {
return name;
}
private CacheInnutale(String name)
{
this.name=name;
}
// 使用數組緩存已有的實例,并且記錄緩存的位置
public static CacheInnutale valueOf(String name)
{
//遍歷已經緩存的對象,如果有相同的,直接返回緩存的實例
for(int i=0;i<name.length();i++)
{
if(name!=null&&cache[i].getName().equals(name))
{
return cache[i];
}
}
//如果緩存已經滿了,則從頭開始緩存
if(MAX_SIZE==pos)
{
cache[0]=new CacheInnutale(name);
pos=1;
}
//緩存沒滿的話,把新建的對象緩存起來
else
{
cache[pos++]=new CacheInnutale(name);
}
//因為是后?,所以pos-1
return cache[pos-1];
}
}
public class CacheInnutaleTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
6.5 抽象類
抽象方法是只有方法的簽名,沒有方法的實現
6.5.1 抽象方法和抽象類
抽象方法和抽象類必須使用abstract修飾,有抽象方法的類一定是抽象類。
規則:
- 抽象方法不能有方法體
- 抽象類不能夠被實例化
- 抽象類的構造函數不能夠用來創造實例,主要用于被子類調用
- 含有抽象方法的類(包括直接定義了一個抽象方法;繼承了一個抽象父類,但是沒有完全實例化父類包含的抽象類;或是實現了一個接口,但是沒有完全實例化接口包含的抽象方法)
public class Triangle extends Shape{
//定義三角形的三邊
private double a;
private double b;
private double c;
public Triangle(String color,double a,double b,double c)
{
super(color);
this.sideSides(a,b,c);
}
public void sideSides(double a,double b,double c)
{
if(a+b<=c||a+c<=b||c+b<=a)
{
System.out.print("兩邊之和必須大于第三邊");
return ;
}
this.a=a;
this.b=b;
this.c=c;
}
//重寫計算周長的方法
public double calPerimeter()
{
return a+b+c;
}
public String getType()
{
return "三角形";
}
public static void main (String[] args)
{
//如果不用抽象類的話,s1是不能夠直接調用gettype方法的。
Shape s1=new Triangle("yello", 3, 4,5);
System.out.println(s1.getType());
System.out.print(s1.calPerimeter());
}
}
abstract與final不能同時出現:abstract類表示只能被繼承,但是final類不能被繼承。
abstract和static一般不能同時修飾方法:static修飾的方法表示屬于類的,可以通過類來訪問。但是如果同時也是abstract的話,則沒有方法體。這就沒辦法調用。(內部類除外)
abstract和private不能同時修飾方法:private修飾的方法是不會被繼承的。但是abstract需要繼承。
6.5.2 抽象類的作用
作為子類的模版。
6.6 java8改進的接口
將抽象類“抽象”到極致,只包含抽象方法。就是接口。
6.6.1 接口的概念
接口定義的是多個類共同的公共行為規范,接口里通常是定義一組公共方法。
接口不提供任何實現,體現的是實現和規范相分離的設計哲學。
6.6.2 java8中接口的定義
關鍵詞:interface
修飾符:public 或者省略,省略是默認default
一個接口可以繼承多個接口
由于接口定義的是一種規范,因此接口沒有構造器和初始化塊,接口可以包含成員變量(靜態常量),靜態方法和抽象方法以及默認方法。都必須是public
- 靜態常量:無論是否有修飾符,都是public static final的,需要在定義的時候指定默認值。可以跨包訪問,但是因為是final,不能修改值。
- 接口里面的普通方法只能是public的抽象abstract方法
- 在接口定義默認方法,需要使用default修飾(默認都是public修飾,不能static修飾)
- 在接口定義類方法,需要使用static(默認都是public ,不能用default修飾)
- java里面最多定義一個public的接口,如果有public的接口,則主文件名和接口名相同
6.6.3 接口的繼承
支持多繼承,以逗號分格
6.6.4 使用接口
implements實現多個接口。如果一個類繼承了一個接口,就必須把里面的抽象方法都實現,否則就必須定義成抽象類。
實現接口的方法時必須使用public
模擬多繼承:接口名 引用變量名=new 類(初始化參數),類就可以訪問接口的方法以及自己的方法。類的方法就變的很多。
package demo6;
//定義一個product接口
interface Product
{
int getProductTime();
}
public class Printer implements Product , Output{
private String[] printData=new String[MAX_CACHE_LINE];
//記錄當前需打印的作業數
private int dataNum=0;
public void out()
{
//只要還有作業就繼續打印
while(dataNum>0)
{
System.out.println(printData[0]);
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
public void getData(String msg)
{
if(dataNum>=MAX_CACHE_LINE)
{
System.out.println("輸出隊列已經滿了。添加失敗");
}else {
printData[dataNum++]=msg;
}
}
public int getProductTime()
{
return 45;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//創建一個printer對象,當成output使用
Output o=new Printer();
o.getData("瘋狂jav");
o.getData("瘋狂jaba講義");
o.out();
o.getData("瘋狂安卓講義");
o.getData("瘋狂安卓");
o.out();
}
}
6.6.5 接口和抽象類
接口:體現一種規范,對于接口的實現者,接口定義了必須實現那些服務;對于接口的調用者,規定了調用者可以調用哪些方法。
抽象類:體現一種模版的設計,他是沒有設計完的一個類,需要子類補充將它完成。
6.6.6 面向接口編程
- 簡單的工廠模式
不太懂啊
package demo6;
public class Computer {
private Output out;
public Computer(Output out)
{
this.out=out;
}
//定義一個模擬獲得字符串的方法
public void keyIn(String msg)
{
out.getData(msg);
}
public void print()
{
out.out();
}
}
package demo6;
public class OutputFactory {
public Output getOutput()
{
return new Printer();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
OutputFactory oF=new OutputFactory();
Computer c=new Computer(oF.getOutput());
c.keyIn("java");
c.keyIn("ilovayou");
c.print();
}
}
2.命令模式
定義一個接口,接口里面定義一個抽象的方法,作用在一個數組上。然后實例化這個接口,可以實例化多個,每個都是作用在數組上的一種方法,
???????????
6.7 內部類
定義在其他類內部的類叫做內部類。
包含內部類的類叫做外部類。
內部類的作用:
- 提供了更好的封裝性,不允許同一個包中的其他類訪問。
- 內部類可以直接訪問外部類的私有數據。因為內部類可以當作外部類成員。外部類不可以訪問內部類的實現細節
- 匿名內部類適合用于創建只需要一次使用的類。
內部類外部類區別: - 內部類比外部類多3個修飾符,private protected static
- 非靜態內部類不能有靜態成員
6.7.1 非靜態內部類
在外部類里面使用非靜態內部類時,和使用普通的類沒有什么區別。非靜態內部類可以訪問外部類的pirvate成員,因為非靜態內部類的對象里面,保存了一個外部類對象的引用。
外部類成員變量,內部類成員變量,內部類里面方法的局部變量可以同名,用this區分。
外部類不能夠訪問非晶態內部類的成員,必須創建一個對象才行。new inner(),,,因為外部類存在的時候,內部類不一定存在,但是內部類存在,外部類一定存在。
不允許在外部類的靜態成員中直接使用非靜態內部類
不允許在非靜態內部類里面定義靜態成員。
6.7.2 靜態內部類
用static修飾的內部類叫做靜態內部類。這個內部類屬于外部類本身,不屬于外部類的任何一個對象。
外部類不能夠用statc修飾,因為外部類的上一級是包,所以沒有類的概念,但是內部類的上一層是外部類,所以可以用static修飾。
靜態內部類可以有靜態成員和非靜態成員,靜態內部類不能夠訪問外部類的實例成員,只能訪問類成員。(因為靜態內部類里面只有外部類的引用,沒有外部類對象的引用)
外部類依舊不能訪問內部類的成員,但是可以通過類名或者對象訪問內部類成員對象。
java允許定義接口內部類,默認是public static修飾。也就是說,接口的內部類一定是靜態內部類。
6.7.3 使用內部類
在外部類內部使用內部類
基本上與平常使用普通類沒有區別。唯一的區別是不要在外部類的靜態成員中使用非靜態內部類。
在外部類以外使用非靜態內部類
在外部類以外的地方定義內部類變量的語法:
outclassname.innerclassname name;
創建非靜態內部類對象(非靜態內部類的構造器必須用外部類對象調用)
outerInstance.new InnerConstructor()
class Out
{
class In
{
public In(String msg)
{
System.out.println(msg);
}
}
}
public class CreateInnerInstance {
public static void main(String[] args) {
// TODO Auto-generated method stub
//使用outclass.innerclass的形式定義內部類變量
Out.In in;
//創建外部類對象
Out out=new Out();
//通過外部類對象創建內部類對象
in=out.new In("測試出錯");
}
}
1.創建非靜態內部類的子類
package demo6;
public class SubClass extends Out.In {
public SubClass(Out out)
{
out.super("hello");
}
}
- 在外部類以外使用靜態內部類
new outclass.innerConstruction
可以看出無論是靜態內部類還是非靜態內部類,聲明變量的方法都是一樣的。區別在于創建內部類對象。優先考慮靜態內部類。
6.7.4 局部內部類
放在方法里面的內部類
一般不用
6.7.5 java8改進的匿名內部類
匿名內部類適合創建只需要一次使用的類。創建匿名內部類時會立即創建一個該類的實例,這個類定義立即消失,匿名內部類不能重復使用。
匿名內部類必須繼承一個父類,或者實現一個接口,但是最多只能是一個。
匿名內部類的兩條規則:
- 不能是抽象類,因為抽象類不能被實例化,但是匿名內部類創建的時候就要創建對象
- 不能定義構造器,因為匿名內部類沒有類名。
最常用的創建匿名內部類是需要創建某個接口類型的對象。
局部變量被匿名內部類訪問,局部變量相當于自動加了final修飾。因此不能夠再被修改。
6.8 java8新增的lambda表達式
6.8.1 lambda表達式入門
lambda表達式支持代碼作為方法參數,可以創建只有一個抽象方法的接口的實例。
lambda表達式由形參列表 ->和方法體組成
6.8.2 lambda表達式和函數式接口
lambda表達式的目標類型必須是函數式接口
函數式接口代表只包含一個抽象方法的接口。函數式接口可以包含多個默認方法,類方法,但只能有一個抽象方法。
java8為函數式接口加了@FunctionalInterface注解 用于告訴編譯器更嚴格的檢查
6.8.3 方法引用和構造器引用
如果代碼塊只有一行代碼,則可以在lambda表達式中使用方法引用和構造引用。
引用類方法 類名::類方法
引用特定對象的實力方法 特定對象::實例方法
引用某類對象的實例方法 類名::實例方法
引用構造器 類名::new
6.8.4 lambda表達式和匿名內部類的聯系和區別
lambda表達式是匿名內部類的一種簡化。
相同點:
都可以直接訪問“effectively final”的局部變量,以及外部類的成員變量
所創建的對象可以直接調用從接口中繼承的默認方法
區別
匿名內部類可以為任意接口創建實例,而lambda表達式必須是函數式接口
匿名內部類可以為抽象類甚至普通類創建實例。
匿名內部類的方法體可以調用接口中定義的默認方法,但是lambda不可以,它只有對象可以調用。
6.9 枚舉類
枚舉類是指實例有限而且固定的類
6.9.2 枚舉類入門
枚舉類是一種特殊的類,可以有自己的成員變量,方法,可以實現一個或者多個接口。
- 默認繼承java.lang.Enum類,不能顯式繼承其他父類。
- 使用Enum定義,非抽象的枚舉類默認是final修飾,不能派生子類
- 構造器為private
- 所有實例必須在第一行顯式列出,系統默認加上public static final
- 如果需要使用某個實例,用EnumClass.variable
6.9.3 枚舉類的成員變量,方法和構造器
枚舉類的成員變量最好都使用private final修飾
如果構造函數有參數,則在第一行列出實例的時候,要寫上參數。
枚舉類的實例只能是枚舉值,不能隨意通過new來創建。???
6.9.4 實現接口的枚舉類
與普通類完全一樣,也需要實現該接口所包含的方法
如果不同的枚舉值想在調用一個方法時呈現不同的行為方式,則可以讓每個枚舉值分別實現該方法,這個時候,不是在創建枚舉類的實例,而是創建匿名子類的實例。
6.9.5 包含抽象方法的枚舉類
每個枚舉值都必須為抽象方法提供實現,否則報錯。
6.10 對象和垃圾回收
6.10.1 對象在內存中的狀態
可達狀態
可恢復狀態
不可達狀態
6.10.2 強制垃圾回收
System.gc()
Runtime.getRuntime().gc()