JavaSE學習筆記(5)---內部類和String類

JavaSE學習筆記(5)---內部類和String類

一.內部類基礎

轉自菜鳥教程html

​ 在 Java 中,能夠將一個類定義在另外一個類裏面或者一個方法裏面,這樣的類稱爲內部類。普遍意義上的內部類通常來講包括這四種:成員內部類、局部內部類、匿名內部類和靜態內部類。下面就先來了解一下這四種內部類的用法。java

1.成員內部類

​ 成員內部類是最普通的內部類,它的定義爲位於另外一個類的內部,形以下面的形式:app

class Circle {
    double radius = 0;
     
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //內部類
        public void drawSahpe() {
            System.out.println("drawshape");
        }
    }
}

​ 這樣看起來,類Draw像是類Circle的一個成員,Circle稱爲外部類。成員內部類能夠無條件訪問外部類的全部成員屬性和成員方法(包括private成員和靜態成員)。ide

class Circle {
    private double radius = 0;
    public static int count =1;
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //內部類
        public void drawSahpe() {
            System.out.println(radius);  //外部類的private成員
            System.out.println(count);   //外部類的靜態成員
        }
    }
}

​ 不過要注意的是,當成員內部類擁有和外部類同名的成員變量或者方法時,會發生隱藏現象,即默認狀況下訪問的是成員內部類的成員。若是要訪問外部類的同名成員,須要如下面的形式進行訪問:函數

外部類.this.成員變量
外部類.this.成員方法

​ 雖然成員內部類能夠無條件地訪問外部類的成員,而外部類想訪問成員內部類的成員卻不是這麼爲所欲爲了。在外部類中若是要訪問成員內部類的成員,必須先建立一個成員內部類的對象,再經過指向這個對象的引用來訪問:學習

class Circle {
    private double radius = 0;
 
    public Circle(double radius) {
        this.radius = radius;
        getDrawInstance().drawSahpe();   //必須先建立成員內部類的對象,再進行訪問
    }
     
    private Draw getDrawInstance() {
        return new Draw();
    }
     
    class Draw {     //內部類
        public void drawSahpe() {
            System.out.println(radius);  //外部類的private成員
        }
    }
}

​ 成員內部類是依附外部類而存在的,也就是說,若是要建立成員內部類的對象,前提是必須存在一個外部類的對象。建立成員內部類對象的通常方式以下:測試

public class Test {
    public static void main(String[] args)  {
        //第一種方式:
        Outter outter = new Outter();
        Outter.Inner inner = outter.new Inner();  //必須經過Outter對象來建立
         
        //第二種方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}
 
class Outter {
    private Inner inner = null;
    public Outter() {         
    }
     
    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }
      
    class Inner {
        public Inner() {             
        }
    }
}

​ 內部類能夠擁有 private 訪問權限、protected 訪問權限、public 訪問權限及包訪問權限。好比上面的例子,若是成員內部類 Inner 用 private 修飾,則只能在外部類的內部訪問,若是用 public 修飾,則任何地方都能訪問;若是用 protected 修飾,則只能在同一個包下或者繼承外部類的狀況下訪問;若是是默認訪問權限,則只能在同一個包下訪問。這一點和外部類有一點不同,外部類只能被 public 和包訪問兩種權限修飾。我我的是這麼理解的,因爲成員內部類看起來像是外部類的一個成員,因此能夠像類的成員同樣擁有多種權限修飾。this

2.局部內部類

​ 局部內部類是定義在一個方法或者一個做用域裏面的類,它和成員內部類的區別在於局部內部類的訪問僅限於方法內或者該做用域內。code

class People{
    public People() {
         
    }
}
 
class Man{
    public Man(){
         
    }
     
    public People getWoman(){
        class Woman extends People{   //局部內部類
            int age =0;
        }
        return new Woman();
    }
}

 

注意: 局部內部類就像是方法裏面的一個局部變量同樣,是不能有 public、protected、private 以及 static 修飾符的。htm

3.匿名內部類

匿名內部類應該是平時咱們編寫代碼時用得最多的,在編寫事件監聽的代碼時使用匿名內部類不但方便,並且使代碼更加容易維護。下面這段代碼是一段 Android 事件監聽代碼:

scan_bt.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub         
    }
});
 
history_bt.setOnClickListener(new OnClickListener() {
     
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         
    }
});

這段代碼爲兩個按鈕設置監聽器,這裏面就使用了匿名內部類。這段代碼中的:

new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         
    }
}

就是匿名內部類的使用。代碼中須要給按鈕設置監聽器對象,使用匿名內部類可以在實現父類或者接口中的方法狀況下同時產生一個相應的對象,可是前提是這個父類或者接口必須先存在才能這樣使用。固然像下面這種寫法也是能夠的,跟上面使用匿名內部類達到效果相同。

private void setListener()
{
    scan_bt.setOnClickListener(new Listener1());       
    history_bt.setOnClickListener(new Listener2());
}
 
class Listener1 implements View.OnClickListener{
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub             
    }
}
 
class Listener2 implements View.OnClickListener{
    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub             
    }
}

​ 這種寫法雖然能達到同樣的效果,可是既冗長又難以維護,因此通常使用匿名內部類的方法來編寫事件監聽代碼。一樣的,匿名內部類也是不能有訪問修飾符和 static 修飾符的。

​ 匿名內部類是惟一一種沒有構造器的類。正由於其沒有構造器,因此匿名內部類的使用範圍很是有限,大部分匿名內部類用於接口回調。匿名內部類在編譯的時候由系統自動起名爲 Outter$1.class。通常來講,匿名內部類用於繼承其餘類或是實現接口,並不須要增長額外的方法,只是對繼承方法的實現或是重寫。

4.靜態內部類

​ 靜態內部類也是定義在另外一個類裏面的類,只不過在類的前面多了一個關鍵字static。靜態內部類是不須要依賴於外部類的,這點和類的靜態成員屬性有點相似,而且它不能使用外部類的非static成員變量或者方法,這點很好理解,由於在沒有外部類的對象的狀況下,能夠建立靜態內部類的對象,若是容許訪問外部類的非static成員就會產生矛盾,由於外部類的非static成員必須依附於具體的對象。

public class Test {
    public static void main(String[] args)  {
        Outter.Inner inner = new Outter.Inner();
    }
}
 
class Outter {
    public Outter() {      
    }
     
    static class Inner {
        public Inner() {    
        }
    }
}

String類

  1. String類又稱做不可變字符序列。

  2. String位於java.lang包中,Java程序默認導入java.lang包下的全部類。

  3. Java字符串就是Unicode字符序列,例如字符串「Java」就是4個Unicode字符’J’、’a’、’v’、’a’組成的。

  4. Java沒有內置的字符串類型,而是在標準Java類庫中提供了一個預約義的類String,每一個用雙引號括起來的字符串都是String類的一個實例。
  5. Java容許使用符號"+"把兩個字符串鏈接起來。

String類的實例

String e = ""  ; // 空字符串
String greeting = " Hello World ";

字符串鏈接

String s1 = "Hello";
String s2 = "World! ";
String s = s1 + s2; //HelloWorld!

​ n-符號"+"把兩個字符串按給定的順序鏈接在一塊兒,而且是徹底按照給定的形式。

​ n-當"+"運算符兩側的操做數中只要有一個是字符串(String)類型,系統會自動將另外一個操做數轉換爲字符串而後再進行鏈接。

"+"鏈接符

int age = 18;
String str = "age is" + age;  //str賦值爲"age is 18"
//這種特性一般被用在輸出語句中:
System.out.println("age  is" + age);

常量池

在Java的內存分析中,咱們會常常聽到關於「常量池」的描述,實際上常量池也分了如下三種:

1. 全局字符串常量池(String Pool)

全局字符串常量池中存放的內容是在類加載完成後存到String Pool中的,在每一個VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串對象實例)。

2. class文件常量池(Class Constant Pool)

class常量池是在編譯的時候每一個class都有的,在編譯階段,存放的是常量(文本字符串、final常量等)和符號引用。

3. 運行時常量池(Runtime Constant Pool)

運行時常量池是在類加載完成以後,將每一個class常量池中的符號引用值轉存到運行時常量池中,也就是說,每一個class都有一個運行時常量池,類在解析以後,將符號引用替換成直接引用,與全局常量池中的引用值保持一致。

常量池示例

String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);// true
System.out.println(str2 == str4);// false
System.out.println(str4 == str5);// true

示例中首先通過編譯以後,在該類的class常量池中存放一些符號引用,而後類加載以後,將class常量池中存放的符號引用轉存到運行時常量池中,而後通過驗證,準備階段以後,在堆中生成駐留字符串的實例對象(也就是上例中str1所指向的「abc」實例對象),而後將這個對象的引用存到全局String Pool中,也就是String Pool中,最後在解析階段,要把運行時常量池中的符號引用替換成直接引用,那麼就直接查詢String Pool,保證String Pool裏的引用值與運行時常量池中的引用值一致,大概整個過程就是這樣了。

回到示例的程序,如今就很容易解釋整個程序的內存分配過程了,首先,在堆中會有一個「abc」實例,全局String Pool中存放着「abc」的一個引用值,而後在運行第二句的時候會生成兩個實例,一個是「def」的實例對象,而且String Pool中存儲一個「def」的引用值,還有一個是new出來的一個「def」的實例對象,與上面那個是不一樣的實例,當在解析str3的時候查找String Pool,裏面有「abc」的全局駐留字符串引用,因此str3的引用地址與以前的那個已存在的相同,str4是在運行的時候調用intern()函數,返回String Pool中「def」的引用值,若是沒有就將str2的引用值添加進去,在這裏,String Pool中已經有了「def」的引用值了,因此返回上面在new str2的時候添加到String Pool中的 「def」引用值,最後str5在解析的時候就也是指向存在於String Pool中的「def」的引用值,那麼這樣一分析以後,結果就容易理解了。

String類經常使用方法

轉自尚學堂筆記

String類是咱們最常使用的類。字符串類的方法咱們必須很是熟悉!咱們列出經常使用的方法,請你們熟悉。

String類的經常使用方法列表

圖2.png

String類經常使用方法一

public class StringTest1 {
    public static void main(String[] args) {
        String s1 = "core Java";
        String s2 = "Core Java";
        System.out.println(s1.charAt(3));//提取下標爲3的字符
        System.out.println(s2.length());//字符串的長度
        System.out.println(s1.equals(s2));//比較兩個字符串是否相等
        System.out.println(s1.equalsIgnoreCase(s2));//比較兩個字符串(忽略大小寫)
        System.out.println(s1.indexOf("Java"));//字符串s1中是否包含Java
        System.out.println(s1.indexOf("apple"));//字符串s1中是否包含apple
        String s = s1.replace(' ', '&');//將s1中的空格替換成&
        System.out.println("result is :" + s);
    }
}

執行結果如圖所示:

圖5-31 示例5-29運行效果圖.png

String類經常使用方法二

public class StringTest2 {
    public static void main(String[] args) {
        String s = "";
        String s1 = "How are you?";
        System.out.println(s1.startsWith("How"));//是否以How開頭
        System.out.println(s1.endsWith("you"));//是否以you結尾
        s = s1.substring(4);//提取子字符串:從下標爲4的開始到字符串結尾爲止
        System.out.println(s);
        s = s1.substring(4, 7);//提取子字符串:下標[4, 7) 不包括7
        System.out.println(s);
        s = s1.toLowerCase();//轉小寫
        System.out.println(s);
        s = s1.toUpperCase();//轉大寫
        System.out.println(s);
        String s2 = "  How old are you!! ";
        s = s2.trim();//去除字符串首尾的空格。注意:中間的空格不能去除
        System.out.println(s);
        System.out.println(s2);//由於String是不可變字符串,因此s2不變
    }
}

執行結果如圖所示:

圖5-32 示例5-30運行效果圖.png

字符串相等的判斷

  1. equals方法用來檢測兩個字符串內容是否相等。若是字符串s和t內容相等,則s.equals(t)返回true,不然返回false。

  2. 要測試兩個字符串除了大小寫區別外是不是相等的,須要使用equalsIgnoreCase方法。

  3. 判斷字符串是否相等不要使用"=="。

忽略大小寫的字符串比較

"Hello".equalsIgnoreCase("hellO");//true

字符串的比較"=="與equals()方法

public class TestStringEquals {
    public static void main(String[] args) {
        String g1 = "北京尚學堂";
        String g2 = "北京尚學堂";
        String g3 = new String("北京尚學堂");
        System.out.println(g1 == g2); // true  指向一樣的字符串常量對象
        System.out.println(g1 == g3); // false  g3是新建立的對象
        System.out.println(g1.equals(g3)); // true  g1和g3裏面的字符串內容是同樣的
    }
}

執行結果如圖所示:

圖5-33 示例5-32運行效果圖.png

示例的內存分析如圖所示:

圖5-34 示例5-32內存分析圖.png

相關文章
相關標籤/搜索