Java-day 26
目錄
訪問控制
對象中的屬性和方法,在被訪問的時候,是可以根據類中聲明這些屬性和方法的時候所用的修飾符來進行控制的。對這些屬性和方法的訪問控制,就是控制他們可以在什麼地方被訪問,在什麼地方不能被訪問,四種修飾符表示可以被訪問的範圍從大到小,依次是:
public > protected > default > private
類 | 同包同類 | 同包子類 | 同包非子類 | 不同包子類 | 不同包非子類 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
注意,在做以上測試的時候,如果是在子類中,那麼就直接訪問的父類中繼承過來這個四個屬性,如果不是在子類中,那麼就先創建出Person類型的對象,然後使用對象去訪問這個四個屬性,最終得到以上結果。
內部類
內部類不是在一個Java源文件中編寫兩個平行的類,而是在一個類的內部再定義另外一個類。
【不屬於內部類】例如:
//Person.java文件中
//雖然這個倆個類在一個文件中,但是它們是平行的關係
//所以這種情況不是內部類
public class Person{
}
class PersonTest{
}
【屬於內部類】例如:
//Person.java文件中
public class Person{
//這個就是一個內部類
//類A的是定義在Person類中的
public class A{
}
}
注意,內部類是類和類之間的嵌套關係,我們可以把定義在外面的類叫做外部類,嵌套定義在裏面的類叫做內部類。Java中的內部類,可以分爲四種:成員內部類,靜態內部類,局部內部類,匿名內部類
通過以下幾個方法,對內部類進行學習瞭解:
1、成員內部類
成員內部類例子
//MemberOutterClass是當前外部類
public class MemberOutterClass{
/* 成員內部類 聲明開始 */
public class MemberInnerClass{
}
/* 成員內部類 聲明結束 */
}
編譯後的class文件:
當前這個情況,編譯成功後,會生成倆個class文件,一個對象外部類,一個對應內部類class文件的名字爲:
MemberOutterClass.class
MemberOutterClass$MemberInnerClass.class
定義屬性、方法、構造器
//例如:
public class MemberOutterClass{
/* 成員內部類 聲明開始 */
public class MemberInnerClass{
private String name;
private int age;
public void run(){}
}
/* 成員內部類 聲明結束 */
}
注意,成員內部類中,【不能】編寫靜態的屬性和方法,成員內部類中,可以編寫構造器,如果有需要的話,如果不編寫的話,那麼默認使用無參的構造器。
成員內部類和外部類的相互訪問
public class MemberOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(){
//在外部類中,任何訪問成員內部類中的屬性和方法
//MemberInnerClass mic = this.new MemberInnerClass();
//在外部類中,可以把 this. 省去
//這個意思就是成員內部類對象的創建,是需要依託於【外部類對象】的
//就像一個類中的成員屬性、成員方法,需要依託於這個類的對象才能訪問
MemberInnerClass mic = new MemberInnerClass();
System.out.println(mic.name);
System.out.println(mic.age);
mic.run();
}
/* 成員內部類 聲明開始 */
public class MemberInnerClass{
private String name;
private int age;
public void run(){}
public void test(String name){
//這個方法的輸參數name
System.out.println(name);
//訪問當前內部類中name屬性
System.out.println(this.name);
System.out.println(MemberInnerClass.this.name);
//訪問外部類中的name屬性(非靜態)
System.out.println(MemberOutterClass.this.name);
//訪問外部類中的age屬性(靜態)
System.out.println(MemberOutterClass.age);
//訪問外部類中的run方法(非靜態)
MemberOutterClass.this.run();
//訪問外部類中的go方法(靜態)
MemberOutterClass.go();
//訪問內部類中自己的run方法
MemberInnerClass.this.run();
}
}
/* 成員內部類 聲明結束 */
}
注意,在內部類中,可以使用 【類名.this】 的形式,當前使用的this指的是哪一個類中的this,在外部類中,要訪問成員內部類中的屬性和方法,需要先創建這個成員內部類的對象,成員內部類對象,是要依託於外部類對象,也就是一定先創建了外部類對象,然後才能創建這個成員內部類對象。
在其他類中使用成員內部類
導入方式
import com.zzb.day26.MemberOutterClass.MemberInnerClass;
調用方式
MemberInnerClass mic = new MemberOutterClass().new MemberInnerClass();
mic.run();
運行截圖
2、靜態內部類
格式:
//靜態內部類
public class StaticOutterClass{
/*聲明開始*/
public static class StaticInnerClass{
}
/*聲明結束*/
}
編譯後的class文件:
StaticOutter$StaticInnerClass.class
StaticOutterClass.class
定義屬性、方法、構造器
//靜態內部類
public class StaticOutterClass{
/*聲明開始*/
public static class StaticInnerClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
}
/*聲明結束*/
}
注意,靜態內部類中,【可以編寫靜態的屬性和方法】(其他都不可以),靜態內部類中,可以編寫構造器,如果有需要的話,如果不編寫的話,那麼默認使用無參的構造器。
內部類和外部類相互訪問
//靜態內部類
public class StaticOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(){
//在外部類中,訪問靜態內部類中的靜態屬性和方法
System.out.println(StaticInnerClass.age);
StaticInnerClass.go();
//在外部類中,訪問靜態內部類中的非靜態屬性和方法
//這時候需要先創建靜態內部類對象,使用對象來訪問
StaticInnerClass sic = new StaticInnerClass();
System.out.println(sic.name);
sic.run();
}
/*聲明開始*/
public static class StaticInnerClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(String name){
//靜態內部類訪問自己的屬性和方法
//靜態和非靜態的都可以訪問
System.out.println(this.name );
System.out.println(StaticInnerClass.this.name);
System.out.println(StaticInnerClass.age);
StaticInnerClass.this.run() ;
StaticInnerClass.go();
//靜態內部類中訪問外部類中的靜態屬性和靜態方法
//在靜態內部類中訪問不了外部類中的非靜態屬性和方法
//System.out.println(StaticOutterClass.this.name);【報錯】
System.out.println(StaticOutterClass.age);
StaticOutterClass.go();
}
}
/*聲明結束*/
}
在靜態內部類中訪問不了外部類的非靜態屬性和方法。
外部類中要訪問靜態內部類的靜態屬性和方法可以直接訪問。
外部類要訪問靜態內部類的非靜態屬性和方法,要先創建內部類對象再用對象進行訪問。
注意,靜態內部類和成員內部類不同,靜態內部類由於是靜態的,它可以獨立存在,並不會依賴於外部類的對象。而成員內部類就必須依賴於外部類對象,也就是必須先要創建出外部類對象,才能接下來創建成員內部類對象。
外部類中調用靜態內部類【通過創建對象訪問】
public class StaticOutterClassTest{
public static void main(String[] args){
StaticInnerClass sic = new StaticInnerClass();
sic.run();
}
}
3、局部內部類
【用的最少的內部類形式,一般寫在方法裏】
格式:
//局部內部類
public class LocalOutterClass{
public void test(){
/*局部內部類聲明開始*/
class LocalInnerClass{
}
/*局部內部類聲明結束*/
}
}
注意,局部內部類是定義在方法中的一種內部類,這種情況下,這個內部類的作用範圍就比較小了,只能在方法中起作用,出了這個方法代碼塊,局部內部類就沒有了。
編譯後的class文件:
LocalOutter$LocalInnerClass.class
LocalOutterClass.class
定義屬性、方法、構造器
//局部內部類
public class LocalOutterClass{
public void test(){
/*局部內部類聲明開始*/
class LocalInnerClass1{
private String name;
private int age;
public void run(){}
}
/*局部內部類聲明結束*/
}
}
注意,局部內部類中,【不能】編寫靜態的屬性和方法,局部內部類中,可以編寫構造器,如果有需要的話,如果不編寫的話,那麼默認使用無參的構造器。
內部類和外部類相互訪問
public class LocalOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void say(final String myName){
final int a = 1;
/* 局部內部類 聲明開始 */
class LocalInnerClass{
private String name;
private int age;
public void run(){}
public void test(String name){
//訪問當前最近的name變量,也就是這個參數
System.out.println(name);
//訪問say方法中的myName屬性【final】
System.out.println(myName);
//也可以訪問這個方法中的局部變量
//但是要求這個局部變量必須是final修飾的
//JDK1.8的時候,這個局部變量不是final修飾的也可以被局部內部類訪問,
//但是訪問之後,這個變量就自動變爲final的了。
System.out.println(a);
//訪問LocalInnerClass類中的屬性name
System.out.println(this.name);
System.out.println(LocalInnerClass.this.name);
//訪問LocalOutterClass類中的屬性和方法(靜態和非靜態)
System.out.println(LocalOutterClass.this.name);
System.out.println(LocalOutterClass.age);
LocalOutterClass.this.run();
LocalOutterClass.go();
}
}
/* 局部內部類 聲明結束 */
//由於局部內部類在方法中,所以也只能在這個方法中使用
LocalInnerClass lic = new LocalInnerClass();
System.out.println(lic.name);
System.out.println(lic.age);
lic.run();
}
}
在其他類中,怎麼使用這個內部類創建對象。出了這個局部內部類所在的方法之後,其他任何地方都使用不到這個局部內部類
4、匿名內部類
【將來在程序中使用最多的內部類】
匿名內部類可以定義在方法中,也可以在類中屬性賦值的時候進行使用。但是絕大多數都會是在方法中定義和使用。匿名內部類由於沒有名字,所以在定義的形式比較特殊。匿名內部類必須依附在一個類或者接口上來進行定義,如果是依附在一個類上,那麼這個匿名內部類就默認是這個類的子類。如果依附在一個接口上,匿名匿名內部類就默認是這個接口的實現類。匿名內部類不能先聲明再使用。
格式:
在方法中定義匿名內部類
//匿名內部類
public class AnonymousOutterClass{
public void test(){
//雖然抽象類不能直接new對象,但是可以依附在
//抽象類上聲明並創建出一個匿名內部類的對象。
//這個大括號就是匿名內部類的代碼實現
//這個匿名內部類繼承了Person父類型
//父類型的引用指向子類對象(匿名內部類對象)
Person p = new Person(){
public void run(){
System.out.println("依附類的匿名內部類的實現");
}
};
Action a = new Action(){
public void go(){
System.out.println("依附接口的匿名內部類的實現");
}
};
}
}
abstract class Person{
public abstract void run();
}
interface Action{
public void go();
}
在屬性是的匿名內部類
//匿名內部類
public class AnonymousOutterClass{
//在給類中屬性private Person person; 賦值的時候
//=號右邊使用了匿名內部類對象,來給=號左邊的引用person進行賦值
private Person person = new Person(){
public void run(){
System.out.println("在屬性上的匿名內部類的實現");
}
};
}
abstract class Person{
public abstract void run();
}
編譯後的class文件:
1、2、3分別代碼中的三個匿名內部類
AnonymousOutterClass$1.class
AnonymousOutterClass$2.class
AnonymousOutterClass$3.class
AnonymousOutterClass.class
反編譯查看
> javap AnonymousOutterClass$1.class
Compiled from "AnonymousOutterClass.java"
class com.zzb.day26.AnonymousOutterClass$1 extends com.zzb.day26.Person {
final com.zzb.day26.AnonymousOutterClass this$0;
com.zzb.day26.AnonymousOutterClass$1(com.zzb.day26.AnonymousOutterClass);
public void run();
}
> javap AnonymousOutterClass$2.class
Compiled from "AnonymousOutterClass.java"
class com.zzb.day26.AnonymousOutterClass$2 extends com.zzb.day26.Person {
final com.zzb.day26.AnonymousOutterClass this$0;
com.zzb.day26.AnonymousOutterClass$2(com.zzb.day26.AnonymousOutterClass);
public void run();
}
> javap AnonymousOutterClass$3.class
Compiled from "AnonymousOutterClass.java"
class com.zzb.day26.AnonymousOutterClass$3 implements com.zzb.day26.Action {
final com.zzb.day26.AnonymousOutterClass this$0;
com.zzb.day26.AnonymousOutterClass$3(com.zzb.day26.AnonymousOutterClass);
public void go();
}
定義屬性、方法、構造器
匿名內部類和其他的內部類不太一樣。其他的內部類都可以定義自己的屬性、方法、構造器,然後將來將來需要的時候可以使用。匿名內部類,雖然可以定義屬性和方法,但是定義之後無法調用到。匿名內部類,我們是沒有辦法編寫構造器的,因爲這個類沒有名字,但是編譯之後會自動生成構造器的,通過 javap 就可以看到這個構造器的聲明。
總結:在匿名內部類中,一般情況,我們不會編寫單獨的屬性、方法(如果有需要的話,可以編寫),因爲在外面不能能直接調用,我們也編寫不了構造器,因爲沒有名字。我們在匿名內部類中,做的最多的事情,就是【重寫】父類中的方法,或者【實現】接口中的抽象方法。
Action a = new Action(){
private String name;
public void run(){
}
//go方法是實現接口Action中的抽象方法
public void go(){
this.name = "tom";
this.run();
System.out.println("這裏就是匿名內部類的實現");
}
};
//編譯報錯
a.run();
因爲run方法是匿名內部類中獨有的方法,變量a是Action類型的,Action中沒有定義run方法,所以使用變量a無法調用到run方法把變量a進行類型轉換,轉爲這個匿名內部類的類型就可以了,因爲這個run方法就是這個匿名內部類中獨有的。但是由於匿名內部類沒有名字,所以我們也無法把變量a轉爲這個類型。那麼也就是我們無法直接調用到這個獨有的run方法。但是可以【間接】的調用到,例如可以在go方法中調用run方法,然後再使用變量a倆調用go方法,最後也可以讓run方法執行。
總結:在你們內部類中,定義直接的獨有的屬性和方法,一般情況意義不大,除非是想在這匿名內部類中自己單獨使用。因爲外部是沒有辦法【直接】調用到的。
匿名內部類和外部類的相互訪問:
匿名內部類在方法中定義的時候,其實就是一個沒有名字的局部內部類,所以這時候它和外部類相互訪問的情況和局部內部類是一樣的。
public class AnonymousOutterClass{
private String name;
private static int age;
public void test(){
final int num = 1;
Person p = new Person(){
public void run(){
System.out.println(num);
System.out.println(AnonymousOutterClass.this.name);
System.out.println(AnonymousOutterClass.age);
}
};
p.run();
}
}
在其他類中,怎麼使用這個內部類創建對象
匿名內部類大多定義在方法中,並且沒有名字,所以其他地方都無法使用這個匿名內部類。但是我們可以使用return語句把這個匿名內部類的對象,返回出去,讓別人使用。
public Person test(){
final int num = 1;
Person p = new Person(){
public void run(){
System.out.println(num);
System.out.println(AnonymousOutterClass.this.name);
System.out.println(AnonymousOutterClass.age);
}
};
return p;
}