1. 程式人生 > >Java程式碼塊與內部類

Java程式碼塊與內部類

原始檔中有且只能有一個public類,且該原始檔的檔名為該public類的類名。

1.程式碼塊

使用 { } 定義的一段程式碼

  • 根據程式碼塊定義的位置以及關鍵字,又可分為以下四種:

普通程式碼塊 :定義在方法中的程式碼塊

構造塊:定義在類中的程式碼塊(構造一個類的物件時,被執行的程式碼塊),不加任何修飾符,也稱為非靜態程式碼塊。

靜態塊:(static+構造塊)

同步程式碼塊

  • 1.1 普通程式碼塊

如果方法中程式碼過長,為避免變數重名,使用普通程式碼塊。

  程式碼示例:

public class CodeBlock{
	public static void main(String[] args){
		{
			int x=10;
		    System.out.println(x);
		}
		int x=100;
		System.out.println(x);
	}
}
  • 1.2 構造塊

在物件產生時,優先於構造方法執行。有幾個物件產生,就呼叫幾次構造塊。

用於在構造方法執行前完成一些屬性的初始化操作。

  • 1.3 靜態塊

  • 靜態塊的主要作用是為static屬性進行初始化
  • 靜態塊優先於構造塊執行,優先於主方法(main)執行。無論產生多少例項化物件,靜態塊都只執行一次。

原因:第一次使用這個類的時候,將該類編譯完成的位元組碼,載入到JVM中解釋執行。完成後,JVM中已包含該類,將類中的靜態塊都執行一遍。因此,靜態塊在建立物件之前執行,且只執行一次。

  程式碼示例:

public class CodeBlock{
	private int codeline;
	private String codeFileName;
	private int salary;

	// 暗示靜態塊優先於構造塊執行
	static{
		System.out.println("靜態塊,屬於類");
	}
	
	//暗示構造塊的執行在構造方法之前
	{
		System.out.println("CodeBlock  非靜態塊");
		codeline=10;
		//假設salary的初始化較為複雜,則可以在此對其進行初始化
		salary=codeline*2;
	}
	
	public CodeBlock(){
		//也可以在此對其進行初始化
		System.out.println("構造方法執行");
	}

	public static void main(String[] args){
		CodeBlock codeBlock=new CodeBlock();
		CodeBlock codeBlock2=new CodeBlock();
		System.out.println("main()");
	}
}

2.內部類

在一個類的內部進行其他類結構的巢狀的操作

  • 內部類——區域性封裝,不讓外部類輕易的訪問內部類中的屬性、方法。內部類可以方便的操作外部類的私有訪問
  • 內部類使用外部類的屬性——外部類的類名.this.屬性名
  • 內部類使用自身的屬性——this.屬性名

  程式碼示例:

public class Outer{
	private String msg="this is a attribute";
	
	//Inner:內部類  Outer:外部類
	//內部類——區域性封裝,不讓外部類輕易的訪問內部類中的屬性、方法
	class Inner{
		private String msg="this is a inner attribute";
		
		public void print(){
			//內部類使用外部類的屬性——外部類的類名.this.屬性名
			System.out.println("Inner中輸出的外部類屬性;" + Outer.this.msg);
			//內部類使用自身的屬性——this.屬性名
			System.out.println("Inner中輸出的內部類屬性;" + this.msg);
		}
	}
	
	public void fun(){
		System.out.println(this.msg);
		//內部類可以直接訪問外部類的屬性,但外部類不能直接訪問內部類的屬性
		//System.out.println(Inner.this.msg);   //error
		//this指當前物件——Outer,而print()是Inner的方法,無法呼叫
		//this.print();  //error
		
		//外部類訪問內部類的屬性,必須建立一個內部類的物件
		//呼叫print()的正確方法
		Inner inner = new Inner();
		inner.print();	
	}
	
	public static void main(String[] args){
		Outer outer=new Outer();
		outer.fun();
	}
}

  • 2.1 內部類為什麼存在

a.內部類與外部類可以方便的訪問彼此的私有域(包含私有方法、私有屬性)

b.內部類是另一種封裝(保護性),可以對同一包中的其他類進行隱藏

c.內部類可以實現Java 單繼承的侷限

d.想要定義一個回撥函式卻不想寫大量程式碼的時候,可以選擇使用匿名內部類來實現


  • 使用內部類來實現"多繼承"(Java是單繼承)

程式碼示例:

public class MultipleInheritance{
	public static void main(String[] args){
		Outer outer=new Outer(); 
		System.out.println(outer.getName());
		System.out.println(outer.getAge());	
	}
}

class A{
	private String name="A屬性";
	//getter
	public String getName(){
		return this.name;
	}
}

class B{
	private int age = 20;
	public int getAge(){
		return this.age;
	}
}

class Outer{
	//內部類InnerA繼承普通類A
	class InnerA extends A{
		public String name(){
			return this.getName();  //this表示當前物件
		}
	}
	
	//內部類InnerB繼承普通類B
	class InnerB extends B{
		public int age(){
			return super.getAge();  //super表示呼叫父類的方法
		}
	}
	
	public String getName(){
		return new InnerA().name();
	}
	
	public int getAge(){
		return new InnerB().age();
	}
}


  • 2.2 內部類與外部類的關係

   a.對於非靜態內部類,內部類的建立依賴外部類的例項物件,在沒有外部類例項之前是無法建立內部類的

   b.內部類是一個相對獨立的實體,與外部類不是is-a關係

   c.內部類可以直接訪問外部類的元素(包含私有域),但是外部類不可以直接訪問內部類的元素

   d.外部類可以通過內部類引用,間接訪問內部類元素

  程式碼示例:

public class Test3{
	public static void main(String[] args){
		Outer3 outer=new Outer3(); 
		
		//內部類的建立依賴外部類的例項物件,在沒有外部類例項之前是無法建立內部類的
		//new Inner();  //Inner()被隱藏  error
		
		//非靜態內部類在其他地方使用的時候,建立物件的方式
                //InnerA 成員內部類,非靜態內部類
                //InnerA 物件內部 引用了一個外部類物件的引用
		Outer.InnerA innerA = new Outer().new InnerA();
		System.out.println(innerA.name());
		System.out.println(innerA.getNum());
	}
}

class A{
	private String name="A屬性";
	//getter
	public String getName(){
		return this.name;
	}
}

class Outer{
	private int num = 20;
	
	//內部類InnerA繼承普通類A
	class InnerA extends A{
		public String name(){
			return this.getName();  //this表示當前物件
		}
		public int getNum(){
			System.out.println("原始值:"+Outer.this.num);
                        //Outer.this.num——對於非靜態內部類,內部類的建立依賴外部類的例項物件
		        Outer.this.num=22;
		        return Outer.this.num;
	        }
         }
	
	//InnerA().name()——外部類訪問內部類的方法或屬性,通過建立內部類的例項化物件或者必須持有內部類的例項化物件的引用
	public String getName(){
		return new InnerA().name();
	}
}

class Outer3 extends A{
	
}

  •  2.3 內部類分類

2.3.1 內部類建立語法

  • 在使用內部類的時候建立內部類的物件(在外部類的外部)

建立非靜態內部類

 外部類.內部類 內部類物件 = new 外部類().new 內部類();
 
 Outer.Inner in = new Outer().new Inner();

建立靜態內部類

外部類.內部類 內部類物件 = new 外部類.內部類();

Outer.Inner in = new Outer.Inner();
  • 在外部類的內部使用內部類物件
Inner in = new Inner();

2.3.2 成員內部類(類比成員方法)

  • 成員內部類中不能存在任何static的變數和方法,但可以訪問外部類的靜態域(以成員方法為例,static的變數是類變數,若存在成員方法中,則依賴於物件,與類變數矛盾)
  • 成員內部類是依附於外部類的,所以只有先建立了外部類才能夠建立內部類

2.3.3靜態內部類

使用static修飾的內部類我們稱之為靜態內部類

  • 靜態內部類的建立是不需要依賴於外部類,可以直接建立
  • 靜態內部類不可以訪問任何外部類的非static成員變數和方法,但可以存在自己的成員變數(private)

2.3.4方法內部類(區域性內部類)

方法內部類定義在外部類的方法中,方法內部類只能 在該方法中被使用,出了該方法就會失效。

  • 方法內部類不允許使用訪問許可權修飾符 public private protected 均不允許
  • 方法內部類對外完全隱藏,除了建立這個類的方法可以訪問它,其他的地方均不能訪問
  • 方法內部類要想使用方法形參,該形參必須用final宣告(JDK8將形參變為隱式fina宣告)

  程式碼示例:

class Outer{
	private int num;
	//相當於public void display(final int test)   test已經不可修改
	//在一個方法的內部類中訪問了引數、變數,
	//這時不管是否給變數新增final修飾符,編譯器會自動新增
	public void display(int test){
                //方法內部類不允許使用訪問許可權修飾符
		class Inner{
			private void fun(){
				num++;
				System.out.println(num);
				System.out.println(test);
			}
		}
                //方法內部類對外完全隱藏,除了建立這個類的方法可以訪問它
		new Inner().fun();
	}
}

public class Test4 {
	public static void main(String[] args){
		Outer out=new Outer();
		out.display(20);
	}
}

2.3.4匿名內部類(lamdba表示式前身)

是一個沒有名字的方法內部類,所以它符合方法內部類的所有約束。

  • 匿名內部類是沒有訪問修飾符的。
  • 匿名內部類必須繼承一個抽象類或者實現一個介面
  • 匿名內部類中不能存在任何靜態成員或方法
  • 匿名內部類是沒有構造方法的(因為它沒有類名)
  • 與方法內部類相同,匿名內部類也可以引用方法形參。此形參也必須宣告為 final

  程式碼示例:

public class Test4 {
	public static void main(String[] args){
		Outer outer = new Outer();
		outer.display(15);
	}
}

interface MyInterFace{
	void test();  //public abstract void test();  抽象方法
}

class Outer{
	private int num = 20;
	//final int value
	public void display(int value){
		//匿名內部類  實現了一個介面MyInterFace
		//匿名類不能建立更多的物件,只能有一個(匿名物件、有名字的物件)
		new MyInterFace(){
			public void test(){
				//來自Outer的成員方法的引數value
				System.out.println("Value = "+value);  
				//來自Outer的屬性
				System.out.println("num = "+num);  //20
			}
		}.test();
	}
}