學習了繼承後,咱們知道,子類能夠在父類的基礎上改寫父類內容,好比,方法重寫。那麼咱們能不能隨意的繼承 API中提供的類,改寫其內容呢?顯然這是不合適的。爲了不這種隨意改寫的狀況,Java提供了 final 關鍵字, 用於修飾不可改變內容ide
final關鍵字能夠修飾:學習
類:被修飾的類,不能被繼承。this
方法:被修飾的方法,不能被重寫。spa
變量:被修飾的變量,不能被從新賦值code
final修飾的類:不能被繼承。對象
格式blog
1 final class 類名 { 2 // 類 3 }
格式繼承
1 修飾符 final 返回值類型 方法名(參數列表){ 2 //方法體 3 }
重寫被 final 修飾的方法,編譯時就會報錯接口
基本類型的局部變量,被final修飾後,只能賦值一次,不能再更改內存
1 public class Demo01Final { 2 3 public static void main(String[] args) { 4 int num1 = 10; 5 System.out.println(num1); // 10 6 num1 = 20; 7 System.out.println(num1); // 20 8 9 // 一旦使用final用來修飾局部變量,那麼這個變量就不能進行更改。 10 // 「一次賦值,終生不變」 11 final int num2 = 200; 12 System.out.println(num2); // 200 13 14 // num2 = 250; // 錯誤寫法!不能改變! 15 // num2 = 200; // 錯誤寫法! 16 17 // 正確寫法!只要保證有惟一一次賦值便可 18 final int num3; 19 num3 = 30; 20 } 21 }
被final修飾的兩種局部變量比較:
1 // 第一種:編譯報錯 2 final int c = 0; 3 for (int i = 0; i < 10; i++) { 4 c = i; 5 System.out.println(c); 6 } 7 8 9 ---------------------------------------- 10 // 第二種:不報錯 11 // 想當與每次都從新定義了一個c變量 12 for (int i = 0; i < 10; i++) { 13 final int c = i; 14 System.out.println(c); 15 }
引用類型的局部變量,被final修飾後,只能指向一個對象,地址不能再更改。可是不影響對象內部的成員變量值的修改。
1 public class Student { 2 3 private String name; 4 5 public Student() { 6 } 7 8 public Student(String name) { 9 this.name = name; 10 } 11 12 public String getName() { 13 return name; 14 } 15 16 public void setName(String name) { 17 this.name = name; 18 } 19 } 20 21 22 --------------------------------------------------------- 23 public class Demo01Final { 24 25 public static void main(String[] args) { 26 27 // 對於基本類型來講,不可變說的是變量當中的數據不可改變 28 // 對於引用類型來講,不可變說的是變量當中的地址值不可改變 29 Student stu1 = new Student("趙麗穎"); 30 System.out.println(stu1); 31 System.out.println(stu1.getName()); // 趙麗穎 32 stu1 = new Student("霍建華"); 33 System.out.println(stu1); 34 System.out.println(stu1.getName()); // 霍建華 35 System.out.println("==============="); 36 37 final Student stu2 = new Student("高圓圓"); 38 // 錯誤寫法!final的引用類型變量,其中的地址不可改變 39 // stu2 = new Student("趙又廷"); 40 System.out.println(stu2.getName()); // 高圓圓 41 stu2.setName("高圓圓圓圓圓圓"); 42 System.out.println(stu2.getName()); // 高圓圓圓圓圓圓 43 } 44 }
對於成員變量來講,若是使用final關鍵字修飾,那麼這個變量也照樣是不可變。
1. 因爲成員變量具備默認值,因此用了final以後必須手動賦值,不會再給默認值了。
2. 對於final的成員變量,要麼使用直接賦值,要麼經過構造方法賦值。兩者選其一。
3. 若是沒有對變量直接賦值。那麼必須保證類當中全部重載的構造方法,都最終會對final的成員變量進行賦值。
1 public class Person { 2 3 private final String name/* = "鹿晗"*/; 4 5 public Person() { 6 name = "關曉彤"; 7 } 8 9 public Person(String name) { 10 this.name = name; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 // set方法就無效了,並且this.name這一行會直接報錯,final修飾後其變量值就不可在變 18 // public void setName(String name) { 19 // this.name = name; 20 // } 21 }
在Java中提供了四種訪問權限,使用不一樣的訪問權限修飾符修飾時,被修飾的內容會有不一樣的訪問權限,
public:公共的。
protected:受保護的
default:默認的
private:私有的
public > protected > (default) > private
public | protected | (default) | private | |
同一個類(我本身) | Yes | Yes | Yes | Yes |
同一個包(我兒子【子類】或我鄰居【無關類】) | Yes | Yes | Yes | No |
不一樣包子類(我兒子) | Yes | Yes | No | No |
不一樣包非子類(陌生人) | Yes | No | No | No |
注意事項:(default)並非關鍵字「default」,而是根本不寫。
若是一個事物的內部包含另外一個事物,那麼這就是一個類內部包含另外一個類。例如:身體和心臟的關係。又如:汽車和發動機的關係。
將一個類A定義在另外一個類B裏面,裏面的那個類A就稱爲內部類,B則稱爲外部類。
成員內部類 :定義在類中方法外的類。
語法
1 修飾符 class 外部類名稱 { 2 修飾符 class 內部類名稱 { 3 // ... 4 } 5 // ... 6 }
1 // 外部類 2 public class Body { 3 4 // 成員內部類 5 public class Heart { 6 7 // 內部類的方法 8 public void beat() { 9 System.out.println("心臟跳動:蹦蹦蹦!"); 10 System.out.println("我叫:" + name); // 正確寫法! 11 } 12 13 } 14 15 // 外部類的成員變量 16 private String name; 17 18 // 外部類的方法 19 public void methodBody() { 20 System.out.println("外部類的方法"); 21 new Heart().beat(); 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 }
在定義內部類的時候,若是內部類的成員變量和外部重名的時候,範圍外部類成員變量:外部類名稱.this.外部類成員變量名
1 // 若是出現了重名現象,那麼格式是:外部類名稱.this.外部類成員變量名 2 public class Outer { 3 4 int num = 10; // 外部類的成員變量 5 6 public class Inner /*extends Object*/ { 7 8 int num = 20; // 內部類的成員變量 9 10 public void methodInner() { 11 int num = 30; // 內部類方法的局部變量 12 System.out.println(num); // 局部變量,就近原則 13 System.out.println(this.num); // 內部類的成員變量 14 System.out.println(Outer.this.num); // 外部類的成員變量 15 } 16 } 17 } 18 19 20 --------------------------------------------------------------------------------- 21 public class Demo02InnerClass { 22 23 public static void main(String[] args) { 24 // 外部類名稱.內部類名稱 對象名 = new 外部類名稱().new 內部類名稱(); 25 Outer.Inner obj = new Outer().new Inner(); 26 obj.methodInner(); 27 } 28 }
方法一:間接方式:在外部類的方法當中,使用內部類;而後main只是調用外部類的方法。
方法二:直接方式,公式:類名稱 對象名 = new 類名稱();
【外部類名稱.內部類名稱 對象名 = new 外部類名稱().new 內部類名稱();】
1 public class Body { // 外部類 2 3 public class Heart { // 成員內部類 4 5 // 內部類的方法 6 public void beat() { 7 System.out.println("心臟跳動:蹦蹦蹦!"); 8 System.out.println("我叫:" + name); // 正確寫法! 9 } 10 11 } 12 13 // 外部類的成員變量 14 private String name; 15 16 // 外部類的方法 17 public void methodBody() { 18 System.out.println("外部類的方法"); 19 20 // 外部類想要訪問內部類須要先建立對象 21 new Heart().beat(); 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 } 32 33 34 --------------------------------------------------- 35 public static void main(String[] args) { 36 Body body = new Body(); // 外部類的對象 37 // 經過外部類的對象,調用外部類的方法,裏面間接在使用內部類Heart 38 // 這裏雖然沒有new內部類,可是在外部類中訪問方法的時候已經new了 39 body.methodBody(); 40 System.out.println("====================="); 41 42 // 按照公式寫: 43 Body.Heart heart = new Body().new Heart(); 44 heart.beat(); 45 }
若是一個類是定義在一個方法內部的,那麼這就是一個局部內部類。
「局部」:只有當前所屬的方法才能使用它,出了這個方法外面就不能用了。
語法
1 修飾符 class 外部類名稱 { 2 修飾符 返回值類型 外部類方法名稱(參數列表) { 3 class 局部內部類名稱 { 4 // ... 5 } 6 } 7 }
局部內部類的權限修飾符:
1. 外部類:public / (default)
2. 成員內部類:public / protected / (default) / private
3. 局部內部類:什麼都不能寫
1 class Outer { 2 3 public void methodOuter() { 4 // 局部內部類 5 class Inner { 6 int num = 10; 7 public void methodInner() { 8 System.out.println(num); // 10 9 } 10 } 11 12 Inner inner = new Inner(); 13 inner.methodInner(); 14 } 15 } 16 17 18 ------------------------------------------------ 19 public class DemoMain { 20 21 public static void main(String[] args) { 22 Outer obj = new Outer(); 23 obj.methodOuter(); 24 } 25 }
局部內部類,若是但願訪問所在方法的局部變量,那麼這個局部變量必須是【有效final的】。
備註:從Java 8+開始,只要局部變量事實不變,那麼final關鍵字能夠省略。【只要在代碼中沒有在更改局部變量的值】
緣由:
1. new出來的對象在堆內存當中。
2. 局部變量是跟着方法走的,在棧內存當中。
3. 方法運行結束以後,馬上出棧,局部變量就會馬上消失。
4. 可是new出來的對象會在堆當中持續存在,直到垃圾回收消失。
1 public class MyOuter { 2 3 public void methodOuter() { 4 int num = 10; // 所在方法的局部變量 5 6 class MyInner { 7 public void methodInner() { 8 System.out.println(num);// num的值不在更改就能夠訪問 9 } 10 } 11 } 12 }
若是接口的實現類(或者是父類的子類)只須要使用惟一的一次,
那麼這種狀況下就能夠省略掉該類的定義,而改成使用【匿名內部類】。
語法:
1 接口名稱 對象名 = new 接口名稱() { 2 // 覆蓋重寫全部抽象方法 3 };
對格式「new 接口名稱() {...}」進行解析:
1. new表明建立對象的動做
2. 接口名稱就是匿名內部類須要實現哪一個接口
3. {...}這纔是匿名內部類的內容
1 public interface MyInterface { 2 3 void method1(); // 抽象方法 4 5 void method2(); 6 7 } 8 9 10 ---------------------------------------------- 11 public class DemoMain { 12 13 public static void main(String[] args) { 14 15 // 使用匿名內部類,但不是匿名對象,對象名稱就叫objA 16 MyInterface objA = new MyInterface() { 17 @Override 18 public void method1() { 19 System.out.println("匿名內部類實現了方法!111-A"); 20 } 21 22 @Override 23 public void method2() { 24 System.out.println("匿名內部類實現了方法!222-A"); 25 } 26 }; 27 objA.method1(); 28 objA.method2(); 29 System.out.println("================="); 30 31 // 及使用匿名內部類,又使用匿名對象 32 // 使用了匿名內部類,並且省略了對象名稱,也是匿名對象 33 new MyInterface() { 34 @Override 35 public void method1() { 36 System.out.println("匿名內部類實現了方法!111-B"); 37 } 38 39 @Override 40 public void method2() { 41 System.out.println("匿名內部類實現了方法!222-B"); 42 } 43 }.method1(); 44 // 由於匿名對象沒法調用第二次方法,因此須要再建立一個匿名內部類的匿名對象 45 new MyInterface() { 46 @Override 47 public void method1() { 48 System.out.println("匿名內部類實現了方法!111-B"); 49 } 50 51 @Override 52 public void method2() { 53 System.out.println("匿名內部類實現了方法!222-B"); 54 } 55 }.method2(); 56 } 57 58 }
匿名內部類和匿名對象的區別:
1. 匿名內部類,在【建立對象】的時候,只能使用惟一一次。
若是但願屢次建立對象,並且類的內容同樣的話,那麼就須要使用單獨定義的實現類了。
2. 匿名對象,在【調用方法】的時候,只能調用惟一一次。
若是但願同一個對象,調用屢次方法,那麼必須給對象起個名字。
3. 匿名內部類是省略了【實現類/子類名稱】,可是匿名對象是省略了【對象名稱】
強調:匿名內部類和匿名對象不是一回事!!!
-------------------