第九天(下) 匿名對象和內部類
匿名對象是指在創建對象的時,只有創建對象的語句,卻沒有把對象地址賦值給某個變量.
public class Person{ public void eat(){ System.out.println(""); } }
創建一個普通對象
Person p = new Person();
匿名對象
new Person();
特點
創建匿名對象直接使用,沒有變量名.
new Person(),eat();
在只使用一次的情況下使用,因為每次new都會出來一個新的對象.
例如接受一次鍵盤輸入:int number = new Scanner(system.in).nextInt();
但要多使用,因該為其指定引用變量
可以作為方法的接受參數和返回參數
Class Demo{ Public static Person getPerson(){ //普通方式 //Person p = new Person(); //return p; //匿名對象作為方法返回值 return new Person(); } public static void method(person){ } }
Class Test{ Public static void main (String[] args){ //調用getperson方法得到person對象 Person p = Demo.getPerson(); //調用method方法 Demo.method(p); //匿名對象作為方法接受參數 Demo.method(new Person()); } }
內部類的概述
將類寫在其他類的內部,可以寫在其他類的成員位置和局部位置,這時寫在其他類內部的類就稱為內部類,其他類被稱為外部類
什麽時候使用內部類
在描述事物的時候,若一個事物內部還包含其他可能包含的事物,比如在描述汽車的時候,汽車中還包含著發動機,這個時候就可以用內部類來描述
內部類的分類
根據定義的位置的方式不同可以分為4類
靜態內部類
成員內部類
方法內部類
匿名內部類
靜態內部類
和定義靜態變量和靜態方法一樣,在類名前面加上static關鍵字,語法上靜態類除了位置放在被的類內部以為,和獨立類區別不大,可以有靜態方法,靜態變量,成員方法,成員變量,構造方法等
public class demo { private static int shared = 100; public static class StaticInner { public void innerMethod(){ System.out.println("inner " + shared); } } public void test(){ StaticInner si = new StaticInner(); si.innerMethod(); } }
註意:
與其他三種內部類相比,靜態內部類與外部類的聯系也不大,可以訪問外部類的靜態方法和靜態變量,但不可以訪問實例變量和方法.
靜態內部類可以聲明靜態方法靜態變量,而非靜態內部類不可以聲明靜態變量和靜態方法
public靜態內部類可以被外部使用,只是需要通過"外部類.靜態內部類"的方式使用,如下所示:
Outer.StaticInner si = new Outer.StaticInner();
si.innerMethod();
使用場景:
如果內部類與外部類關系密切有不依賴於外部類就可以考慮定義為靜態內部類一個類內部,如果既要計算最大值,也要計算最小值,可以在一次遍歷中將最大值和最小值都計算出來,但怎麽返回呢?可以定義一個類Pair,包括最大值和最小值,但Pair這個名字太普遍,而且它主要是類內部使用的,就可以定義為一個靜態內部類。
成員內部類:
定義在外部類的成員變量的位置,與外部類的成員變量相似可以使用成員變量的修飾符,可通過外部類對象進行訪問,與靜態內部類,相比少了static修飾,但有了很大的不同
定義方式
Class 外部類{ Class 外部類{ } }
實例化內部類
外部類.內部類 變量名 = new 外部類().new外部類();
或者分開寫
外部類 變量名1 = new 外部類
變量名.內部類 變量名2 = 變量名1.new 內部類
與靜態不同,成員內部類總是與一個外部類的對象向連,外部類在使用的時候要內部類的方法就必須有一個該類的對象,因此,使用內部類就必須先有個外部類,在通過外部類去找到內部類,在調用內部類的方法.
class Body {//外部類,身體 private boolean life= true; //生命狀態 public class Heart { //內部類,心臟 public void jump() { System.out.println("心臟噗通噗通的跳") System.out.println("生命狀態" + life); //訪問外部類成員變量 } } }
訪問內部類
public static void main(String[] args) { //創建內部類對象 Body.Heart bh = new Body().new Heart(); //調用內部類中的方法 bh.jump(); }
內部類訪問外部類
成員內部類可以無條件的訪問外部類的所有成員屬性和成員方法(包括private和static)畢竟內部類也是外部類的成員
當成員內部類和外部類有同名的成員,內部類的成員就會暫時將外部類給隱藏,這個時候需要以下的方式訪問
外部類.this.成員變量(成員方法)
public class Outer { int i = 1; class Inner { int i = 2; public void inner(){ int i = 3; System.out.println(Outer.this.i); } } }
平常如果屬性名不重復,那麽我們在內部類中調用外部類的屬性和方法時,前面就隱式的調用了Outer.this。
題
//要求:使用已知的變量,在控制臺輸出30,20,10。
class Outer { public int num = 10; class Inner { public int num = 20; public void show() { int num = 30; System.out.println(?); System.out.println(??); System.out.println(???); } } } class InnerClassTest { public static void main(String[] args) { Outer.Inner oi = new Outer().new Inner(); oi.show(); } }
答案:num、this.num、Outer.this.num
成員內部類的使用場景
如果內部類與外部類關系密切,且操作或依賴外部類實例變量和方法,則可以考慮定義為成員內部類。
外部類的一些方法的返回值可能是某個接口,為了返回這個接口,外部類方法可能使用內部類實現這個接口,這個內部類可以被設為private,對外完全隱藏。
局部內部類
局部內部類定義在一個方法或者一個作用域裏面,他和成員內部類的區別在於僅限於在方法內或作用域呢,與訪問方法的局部變量相似,可以通過調用方法進行訪問.
定義格式
class 外部類 { 修飾符 返回值類型 方法名(參數) { class 內部類 { //其他代碼 } } }
外部類訪問局部內部類
在外部類方法中,創建局部內部類進行訪問
定義類
class Party {//外部類,聚會
public void puffBall(){// 吹氣球方法
class Ball {// 內部類,氣球
public void puff(){
System.out.println("氣球膨脹了");
}
}
//創建內部類對象,調用puff方法
new Ball().puff();
}
}
訪問內部類
public static void main(String[] args) {
//創建外部類對象
Party p = new Party();
//調用外部類中的puffBall方法
p.puffBall();
}
局部內部類訪問局部變量
局部變量要被final修飾才能被內部類訪問
jdk1.8以後已經不需要在內部類引用局部變量時加final關鍵字了
使用場景
方法內部類都可以用成員內部類代替,至於方法參數,也可以作為參數傳遞給成員內部類。不過,如果類只在某個方法內被使用,使用方法內部類,可以實現更好的封裝。
匿名內部類
和匿名對象一個道理.
定義格式:
new 父類(參數列表) {
//匿名內部類實現部分
}
new 父接口() {
//匿名內部類實現部分
}
前提:必須有接口或者繼承,匿名內部類的本質就是實現了接口或者繼承了某個子類的匿名對象
例子
定義一個接口
public interface Smoking{
Public adstract void smoking();
}
普通做法實現類,實現接口,重寫抽象方法,創建實現類對象
XXX x = new XXX();
x.smoking();
Smoking s = new XXX();
s.smoking();
匿名內部類 簡化問題 定義實現類,重寫方法,建立實現類對象,合為一步完成
public class Test{
new Smoking (){
public static void main(String[] args){
public void smoking() {
System.out.println(“人在吸煙”;)
}
}.smoking;
}
}
使用匿名內部類
定義實現類,重寫方法,創建實現類對象,一步搞定
格式:
new 接口或者父類(){
重寫抽象方法
};
從 new開始,到分號結束
創建了接口的實現類的對象
例子
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
public class Test2 {
public static void main(String[] args) {
Animal a= new Animal(){
public void eat(){
System.out.println("在吃飯");
}
public void sleep(){
System.out.println("在睡覺");
}
};
a.eat();
a.sleep();
}
}
註意
匿名內部類只能被用一次,用來創建一個對象,由於沒有名字,所以也就沒有構造方法,但可以根據參數列表,調用對應的父類構造方法.可以定義實例變量和方法,可以有初始化代碼塊,初始化代碼塊可以起到構造方法的作用.
與方法內部類一樣,匿名內部類也可以訪問外部類的所有變量和方法,可以訪問方法中的final參數和局部變量
使用場景
匿名內部類能做的方法內部類都能做,但如果對象只會創建一次,就可以使用匿名內部類,代碼上的書寫更簡潔.
用於回調
題
按照要求,補齊代碼
interface Inter { void show(); }
class Outer {
//補齊代碼
}
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制臺輸出”HelloWorld”
interface Inter { void show(); }
class Outer {
//補齊代碼
public static Inter method(){
return new Inter(){
void show(){
System.out.println("HelloWorld");
}
};
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
為什麽要有內部類呢
1.每個內部類都能獨立的繼承一個接口的實現,所以無論外部類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。內部類使得多繼承的解決方案變得完整,
2.方便將存在一定邏輯關系的類組織在一起,又可以對外界隱藏。
3.方便編寫事件驅動程序
4.方便編寫線程代碼
成員內部類繼承問題
一般來說成員內部類是很少用來繼承的,但如果真要繼承
1)成員內部類的引用方式必須為 Outter.Inner.
2)構造器中必須有指向外部類對象的引用,並通過這個引用調用super()。這段代碼摘自《Java編程思想》
class WithInner {
class Inner{
}
}
class InheritInner extends WithInner.Inner {
// InheritInner() 是不能通過編譯的,一定要加上形參
InheritInner(WithInner wi) {
wi.super(); //必須有這句調用
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner obj = new InheritInner(wi);
}
}
第九天(下) 匿名對象和內部類