《Think in Java》閱讀筆記·第二卷
內部類
從外部類的非靜態方法之外的任意位置建立某個內部類的物件,都需要以外部類.內部類的格式指明物件型別
內部類擁有外圍類的所有元素的訪問權
外圍類物件建立一個內部類物件時,此內部類物件會祕密地捕獲一個指向那個外圍類物件的引用。
在外圍類靜態方法中建立內部類物件原始碼如下:
public class InnerClass {
class InnerClass1{
public InnerClass1() {
int i1=1;
System.out.println("InnerClass1");
}
public void f1() {
System.out.println("InnerClass1_f1");
}
}
class InnerClass2{
int i2=2;
public InnerClass2() {
System.out.println("InnerClass2");
}
public void f2() {
System.out.println("InnerClass2_f2");
}
}
public InnerClass1 getInnerClass1() {
return new InnerClass1();
}
public static final void main(String...args) {
InnerClass ic=new InnerClass();
InnerClass.InnerClass1 i1=ic.getInnerClass1();
i1.f1();
InnerClass.InnerClass2 i2=ic.new InnerClass2();
i2.f2();
}
}
InnerClass1
InnerClass1_f1
InnerClass2
InnerClass2_f2
匿名內部類
匿名類版:
new NoNameClass(){
public void f3() {
System.out.println("f3");
}
}
完整版:
interface INoNameClass{
public void f3();
}
class NoNameClass implements INoNameClass{
public void f3() {
System.out.println("f3");
}
}
public static final void main(String...args) {
InnerClass ic=new InnerClass();
INoNameClass innc=ic.new NoNameClass();
}
注意:
abstract class NoNameClass2{
public NoNameClass2(int i){
System.out.println("構造器:"+i);
}
public abstract void f2();
//{System.out.println("{ }");}
}
public static final void main(String...args) {
InnerClass ic=new InnerClass();
ic.new NoNameClass2(53){
{System.out.println("{ }");}
public void f2() {
System.out.println("f2");
}
};
}
構造器:53
{ }
abstract class NoNameClass2{
public NoNameClass2(int i){
System.out.println("構造器:"+i);
}
public abstract void f2();
//{System.out.println("{ }");}
}
public static final void main(String...args) {
InnerClass ic=new InnerClass();
ic.new NoNameClass2(53){
{System.out.println("{ }");}
public void f2() {
System.out.println("f2");
}
};
}
{ }
構造器:53
匿名內部類若想要訪問外部物件,則該物件需要加final
匿名內部類不能訪問外部類方法中的區域性變數,除非該變數被宣告為final型別
1. 這裡所說的“匿名內部類”主要是指在其外部類的成員方法內定義的同時完成例項化的類,若其訪問該成員方法中的區域性變數,區域性變數必須要被final修飾。原因是編譯器實現上的困難:內部類物件的生命週期很有可能會超過區域性變數的生命週期。
2. 區域性變數的生命週期:當該方法被呼叫時,該方法中的區域性變數在棧中被建立,當方法呼叫結束時,退棧,這些區域性變數全部死亡。而內部類物件生命週期與其它類物件一樣:自建立一個匿名內部類物件,系統為該物件分配記憶體,直到沒有引用變數指向分配給該物件的記憶體,它才有可能會死亡(被JVM垃圾回收)。所以完全可能出現的一種情況是:成員方法已呼叫結束,區域性變數已死亡,但匿名內部類的物件仍然活著。
3. 如果匿名內部類的物件訪問了同一個方法中的區域性變數,就要求只要匿名內部類物件還活著,那麼棧中的那些它要所訪問的區域性變數就不能“死亡”。
4. 解決方法:匿名內部類物件可以訪問同一個方法中被定義為final型別的區域性變數。定義為final後,編譯器會把匿名內部類物件要訪問的所有final型別區域性變數,都拷貝一份作為該物件的成員變數。這樣,即使棧中區域性變數已經死亡,匿名內部類物件照樣可以拿到該區域性變數的值,因為它自己拷貝了一份,且與原區域性變數的值始終保持一致(final型別不可變)。
只有該區域性變數為不可變時,即為final時才能保證資料的可靠性。
巢狀類
普通內部類不能包含靜態欄位和方法,而巢狀類可以
巢狀類與static欄位和方法都是一編譯便存在,並且記憶體地址不再變化,直到gc回收。
介面內部的類
public class Staticer implements IStaticer{
@Override
public void fun() {
}
public static final void main(String...args) {
IStaticer is=new Staticer();
is.fun();
}
}
IStaticer_fun
內部類的作用
當存在抽象類時實現多重繼承
public class Multipler extends Multipler1{
private AMultipler newClass() {
return new AMultipler(){
@Override
public void f1() {
System.out.println("AMultipler_f1");
}
};
}
public static final void main(String[] args) {
Multipler m=new Multipler();
m.f2();
AMultipler am=m.newClass();
am.f1();
}
}
class Multipler1{
public void f2() {
System.out.println("Multipler_f2");
}
}
abstract class AMultipler{
public abstract void f1();
}
Multipler_f2
AMultipler_f1
控制框架是一類特殊的應用程式框架,用來解決響應事件的需求
主要用來響應事件的系統被稱做事件驅動系統
回撥
類A呼叫類B的方法b,然後類B的方法b呼叫類A的方法a
interface ICallBacker{
public void fun();
}
class CallBacker2 implements ICallBacker{
@Override
public void fun() {
System.out.println("CallBacker2_ICallBackers_fun");
}
}
class CallBacker1{
public void fun() {
System.out.println("CallBacker1_fun");
}
private class InnerClass implements ICallBacker{
public void fun() {
System.out.println("CallBacker1_ICallBackers_fun");
CallBacker1.this.fun();
}
}
public InnerClass getInnerClass(CallBacker1 cb) {
return cb.new InnerClass();
}
}
public class CallBacker{
public static final void main(String...args) {
CallBacker1 cb1=new CallBacker1();
ICallBacker ic1=cb1.getInnerClass(cb1);
ic1.fun();
ICallBacker ic2=new CallBacker2();
ic2.fun();
}
}
CallBacker1_ICallBackers_fun
CallBacker1_fun
CallBacker2_ICallBackers_fun
在內部類InnerClass的fun方法中提供了一個CallBacker1鉤子進行回撥。
內部類的繼承
由於內部類含有指向外圍類的引用,應以以下方式使用繼承
public class InnerExtend extends InnerExtend1.InnerExtend2{
public InnerExtend(InnerExtend1 ie1) {
//super();
ie1.super();
}
public static final void main(String...args) {
InnerExtend1 ie1=new InnerExtend1();
InnerExtend ie=new InnerExtend(ie1);
}
}
class InnerExtend1{
public InnerExtend1() {
System.out.println("InnerExtend1");
}
class InnerExtend2{
public InnerExtend2() {
System.out.println("InnerExtend2");
}
}
}
InnerExtend1
InnerExtend2
若將ie1.super()改成super()會報以下錯誤:
No enclosing instance of type InnerExtend1 is available due to some intermediate constructor invocation
容器
注意:List介面位於java.util包中,而在java.awt 包中也有一個List類
Set:只有元素不存在才會新增
TreeSet:按照值大小的順序進行排序
LinkedHashSet:按照新增的順序進行排序
HashSet:無序散亂地分佈
List:不考慮重複,會按照新增順序進行排序
容器選擇:
要進行大量的隨機訪問:ArrayList
要從表中間插入或刪除元素:LinkedList
各種Queue佇列以及棧的行為,可由LinkedList構建
注意:
不應該適用vector,Hashtable和stack等過時的類
public static final void main(String...args) {
Vector v=new Vector();
System.out.print("showSet: ");
v.showSet();
System.out.println();
System.out.print("showList: ");
v.showList();
}
private void showList() {
Collection<Integer> cl=new ArrayList<Integer>();
Collection<Integer> cls=new ArrayList<Integer>();
cls.addAll(Arrays.<Integer>asList(1,2,3,4,5));
Collections.addAll(cls, 6,7,8,9,10);
cl.add(1);
cl.add(1);
cl.add(2);
cl.add(3);
cl.add(8);
cl.add(5);
for(Integer i:cl) {
System.out.print(i+" ");
}
System.out.print("|");
for(Integer i:cls) {
System.out.print(i+" ");
}
}
private void showSet() {
Collection<Integer> cl=new LinkedHashSet<Integer>();
Collection<Integer> clt=new TreeSet<Integer>();
cl.add(1);
cl.add(1);
cl.add(2);
cl.add(3);
cl.add(8);
cl.add(5);
clt.addAll(Arrays.asList(1,7,3,6,3));
for(Integer i:cl) {
System.out.print(i+" ");
}
System.out.print("|");
for(Integer i:clt) {
System.out.print(i+" ");
}
}
showSet: 1 2 3 8 5 |1 3 6 7
showList: 1 1 2 3 8 5 |1 2 3 4 5 6 7 8 9 10
在後續會探究各集合之間的演算法實現和效能區別
注:點線框表示介面,實線框表示類,空心箭頭的點線表示類實現了介面,實心箭頭表示某個類可以生產箭頭所指向類的物件。
迭代器
迭代器為輕量級物件也屬於一種設計模式
不必為數量和型別操心:
private void showIterator() {
List<Integer> l=new ArrayList<Integer>();
for(int i=0;i<5;i++) {
l.add(i);
}
Iterator<Integer> i=l.iterator();
Set<Integer> s=new HashSet<Integer>();
for(int j=0;j<5;j++) {
s.add(j);
}
display(s.iterator());
display(l.iterator());
i=l.iterator();
while(i.hasNext()) {
i.next();
i.remove();
}
System.out.println(l.toString());
}
private void display(Iterator i) {
while(i.hasNext()) {
System.out.print(i.next()+" ");
}
System.out.print("\n");
}
0 1 2 3 4
0 1 2 3 4
[]
注意:迭代器物件要先呼叫一次next才是list容器中的第一個資料
一種只適用於List容器可雙向遍歷的迭代器
棧(疊加棧)
一個後進先出(LIFO)容器
用LinkedList實現棧儲存:
public class Stacker {
public static final void main(String...args) {
String str="";
MyStack ms=new MyStack();
List<String> l=new ArrayList<String>(Arrays.asList("+","U","+","n","+","x","-","-"));;
System.out.println(l.toString());
Iterator i=l.iterator();
while(i.hasNext()) {
str=(String) i.next();
if(str.equals("+")) {
ms.push(i.next());
}
else if(str.equals("-")) {
ms.pop();
}
}
System.out.println(ms.toString());
}
}
class MyStack<T>{
public MyStack() {
}
LinkedList<T> stack=new LinkedList<T>();
public void push(T t) {
stack.add(t);
}
public T pop() {
//先進後出特性
return stack.removeLast();
}
public boolean isEmpty() {
return stack.isEmpty();
}
public String toString() {
return stack.toString();
}
}
[+, U, +, n, +, x, -, -]
[x]
佇列
一個典型的先進先出(FIFO)的容器
LinkedList向上轉型為Queue實現佇列
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
public class Queuer {
static void printer(Queue q) {
while(q.peek()!=null) {
//先進先出特性
System.out.print(q.remove()+" ");
}
System.out.println();
}
public static final void main(String...args) {
Random rand=new Random(53);
Queue q=new LinkedList<Integer>();
int r=0;
for(int i=0;i<10;i++) {
r=rand.nextInt(20);
q.offer(r);
System.out.print(r+" ");
}
System.out.println();
printer(q);
}
}
16 7 16 1 8 15 12 2 10 0
16 7 16 1 8 15 12 2 10 0
PriorityQueue的使用
import java.util.Comparator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
public class Queuer {
public static final void main(String...args) {
comparatorer();
}
private static void comparatorer() {
Comparator<Oer> comparator=new Comparator<Oer>() {
@Override
public int compare(Oer o1, Oer o2) {
// TODO Auto-generated method stub
if(o1.getPriority()>o2.getPriority()) {
//o2
return -1;
}
else if(o2.getPriority()>o1.getPriority()) {
//o1
return 1;
}
return 0;
}
};
Oer o1=new Oer("o1",4);
Oer o2=new Oer("o2",53);
PriorityQueue<Oer> pq=new PriorityQueue<Oer>(2,comparator);
pq.offer(o1);
pq.offer(o2);
System.out.println(pq.toString());
}
}
class Oer{
private int priority;
private String name;
public Oer(String name,int priority) {
this.priority=priority;
this.name=name;
}
@Override
public String toString(){
return name;
}
public int getPriority() {
return priority;
}
}
[o2, o1]
通過繼承AbstractCollection重寫迭代類
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class Collectioner {
public static final void main(String...args) {
List<Integer> list=new ArrayList<Integer>();
list.addAll(Arrays.asList(1,2,3,4,5));
Collectioner_child cc=new Collectioner_child(list);
Iterator<Integer> i=cc.iterator();
System.out.println(i.next());
}
}
class Collectioner_child extends AbstractCollection{
List<Integer> list;
public Collectioner_child(List<Integer> list) {
this.list=list;
}
@Override
public Iterator iterator() {
// TODO Auto-generated method stub
return new Iterator() {
private int index;
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return index < list.size();
}
@Override
public Object next() {
// TODO Auto-generated method stub
return list.get(index++);
}
};
}
@Override
public int size() {
// TODO Auto-generated method stub
return list.size();
}
}
1
如果直接繼承Collection介面需要實現大量的方法,而繼承實現過Collection方法的AbstrctCollection抽象類只需要再實現兩個抽象方法即可
通過該例項可進一步理解迭代器的使用。
自定義foreach規則
第一種(新建一個繼承於ArrayList的自定義迭代器類)
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Iteratorer {
public Iteratorer() {
}
public static final void main(String...args) {
List<String> list=new ArrayList<String>(Arrays.asList("12","33","22"));
MyIterator<String> mi=new MyIterator<String>(list);
for(String i:mi) {
System.out.print(i+" ");
}
System.out.print("\n");
for(String i:mi.reversed()) {
System.out.print(i+" ");
}
System.out.println();
//java 8中的迭代迴圈
mi.reversed().forEach(System.out::print);
}
}
class MyIterator<T> extends ArrayList<T>{
public MyIterator(List<T> list) {
super(list);
}
public Iterable<T> reversed() {
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
// TODO Auto-generated method stub
return new Iterator<T>() {
int current=size()-1;
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return current>-1;
}
@Override
public T next() {
// TODO Auto-generated method stub
return get(current--);
}
};
}
};
}
}
12 33 22
22 33 12
223312
第二種(直接在主類實現Iterator和Iterable介面)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Iterator2 implements Iterator<String>,Iterable<String>{
private List<String> list;
private int size;
public static final void main(String...ags) {
Iterator2 i2=new Iterator2();
i2.list=new ArrayList<String>();
i2.list.addAll(Arrays.asList("a","v","c","d"));
i2.size=i2.list.size();
for(String s:i2) {
System.out.print(s+" ");
}
}
@Override
public Iterator<String> iterator() {
// TODO Auto-generated method stub
return this;
}
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return size>0;
}
@Override
public String next() {
// TODO Auto-generated method stub
return list.get(--size);
}
}
d c v a