今日主要內容:包,修飾符總結,內部類
包
所謂包,可以說就是一個文件夾。通過定義包,可以管理字節碼文件,也產生了不同的訪問權限。
包的定義:在源文件的第一個可執行語句中用package 聲明。一般通過域名的反寫命名,如package com.gdut.java;
在定義的包之后,在控制臺執行編譯的語句也發生了變化,在源文件所在目錄下打開控制臺輸入javac -d . filename.java
,運行則要輸入文件的全名,即要包括包名
java com.gdut.java.filename
包的產生,也產生了protected訪問權限。說到訪問權限,之前說private是對類的封裝,因為限制了其他類訪問該類內部成員的方式。但并不是說封裝就是private,而只能說private是封裝的一種形式,封裝是相對的,在不同包下,用默認權限修飾符修飾的類就不允許其他包的類對其訪問,也是一種封裝形式。
視頻中老師說了一個例子,生動形象hhhhh,教室的學生要訪問他的錢包是不允許的,這是違法,他的錢包對學生就是封裝的,當他回到家,他老婆要訪問他的錢包是可以的,不然就是作死,他的錢包對他老婆就不是封裝的,所以說封裝是相對的。
修飾符總結
權限修飾符權限表
修飾符 | 本類 | 同一報下(子類與無關類) | 不同包下(子類) | 不同包下(無關類) |
---|---|---|---|---|
private | Y | |||
(默認) | Y | Y | ||
protected | Y | Y | Y | |
public | Y | Y | Y | Y |
- 修飾符
- 權限修飾符:private,默認,protected,public
- 狀態修飾符:final,static
- 抽象修飾符:abstract
- 類
- 權限修飾符:默認,public
- 狀態修飾符:final
- 抽象修飾符:abstract
- 成員變量
- 權限修飾符:private,默認,protected,public
- 狀態修飾符:final,static
- 構造方法
- 權限修飾符:private,默認,protected,public
- 成員方法
- 權限修飾符:private,默認,protected,public
- 狀態修飾符:final,static
- 抽象修飾符:abstract
內部類
內部類,顧名思義就是定義在類內部的類。按照內部類所在的區域可以分為成員內部類和局部內部類。局部內部類就是定義在方法中的內部類。
- 成員內部類
內部類可以直接訪問外部類的變量,包括私有變量,但外部類要訪問內部類成員必須要創建對象
其他類創建內部類對象的方法:外部類名.內部類名 = new 外部類名() .new 內部類名();其實,只要把內部類當做外部類的成員就很好理解,new 外部類名()就是創建外部類對象,用點表示引用其成員,由于該成員是個類,所以要創建對象,new 內部類名();于是上面的語句可以理解為外部類名.內部類名 = 外部類對象.內部類對象
class Demo_InnerClass {
public static void main(String[] args) {
//外部類名.內部類名 = 外部類對象.內部類對象
Outer.Inner io = new Outer().new Inner();
io.print();
new Outer().new Inner().print(); //匿名引用
}
}
class Outer{
private int num = 10; //外部類要訪問內部類成員必須創建對象
class Inner{
public void print(){
System.out.println(num); //內部可以訪問外部類成員變量,即使私有
}
}
}
- 靜態內部類
class Outer{
private int num = 10;
static class Inner2{ //靜態成員內部類
public void print(){
System.out.println("inner2");
}
}
在創建靜態內部類對象時,不用創建外部類對象,即Inner i =Outer.new Inner2();
但由于書寫習慣,編譯器不允許這樣的語句,要將new提前,于是變成
Inner i =new Outer.Inner2()
-
局部內部類
局部內部類與普通內部類大致相同,但有一個應特別注意的地方,內部類所在方法的局部變量必須用final修飾,即要變成常量,否則編譯會報錯,但在jdk1.8版本中取消了該機制,個人理解這應該是個bug。采用這種機制的原因如下:
當調用這個方法時,不用final修飾,局部變量的聲明周期和該方法時一致的,當方法彈棧,這個局部變量也會消失,如果局部內部類對象沒有馬上消失,仍想調用該局部變量,該局部變量就沒有了,用final修飾的變量會進入常量池,即使方法彈棧,常量池中的變量依然存在,仍然可以使用。
通過內存圖理解這一原因
局部內部類內存圖
練習代碼
class Demo_InnerClass3 { //局部內部類(方法內部類)
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
class Outer {
public void method(){
final int num =10; //局部內部類成員方法必須是常量,防止在方法彈棧后對象無法使用該值
class Inner{
public void print(){
System.out.println(num);
}
}
Inner i = new Inner();
i.print();
}
}
- 匿名內部類
可能很多人在匿名內部類這里會很暈。其實,只要看到它的實質就很容易理解他的用法。他的實質就是將:1、創建一個類繼承或實現某接口2、重寫其抽象方法3、創建對象 三個步驟融合到一個語句中進行簡化。本質是一個繼承了該類或者實現了該接口的子類匿名對象
從上面的本質也看出了其存在的前提:在一個方法內,有可以繼承的父類或可以實現的接口。
從下面的題目理解
class Demo_NonameClass2 {
public static void main(String[] args) {
//如何調用PersonDemo中的method方法
PersonDemo pd = new PersonDemo();
}
}
abstract class Person{
public abstract void show();
}
class PersonDemo{
public void method(Person p){ //父類引用指向子類對象
p.show();
}
}
題目中要求用Person為參數,但Person是接口,無法創造實例,只能通過創建他的子類來實現。
方法一:創建有名字的類,實現Person接口,重寫里面的show()方法,再創建該類的對象作為參數傳入pd中的method方法。創建了Student類實現接口,創建Student類作為method的方法。
class Demo_NonameClass2 {
public static void main(String[] args) {
//如何調用PersonDemo中的method方法
PersonDemo pd = new PersonDemo();
//寫有名字的類
pd.method(new Student()); //showname
}
}
abstract class Person{
public abstract void show();
}
class PersonDemo{
public void method(Person p){ //父類應用指向子類對象
p.show();
}
}
class Student extends Person{
public void show(){
System.out.println("showname");
}
}
方法而:通過匿名內部類,省略創建一個新的有名字的類的過程。
class Demo_NonameClass2 {
public static void main(String[] args) {
//如何調用PersonDemo中的method方法
PersonDemo pd = new PersonDemo();
//用匿名內部類
pd.method(new Person(){ //匿名內部類當做參數傳遞
public void show (){
System.out.println("shownoname"); //showname
}
});
}
}
abstract class Person{
public abstract void show();
}
new Person()可以看做是創建一個匿名的Person的接口并創建對象,之后的大括號就是對接口的實現,即重寫方法。就省略了創建新類的過程。
- 弊端:上面的例子看到了匿名內部的好處,但也是有弊端的,就是顯然,每次使用匿名內部類要重寫全部的抽象方法,當其繼承的類或者接口的抽象方法很多時,直接寫有名的類效率更高。而且,由于匿名內部類沒有名字,不能進行向下轉型,故無法調用其特有的方法。
到目前為止大致學完了JAVA有關面向對象的所有概念,接下來轉向JAVA標準類庫用法的學習,即API的學習。