1. 程式人生 > >Java基礎知識回顧之二 ----- 修飾符和String

Java基礎知識回顧之二 ----- 修飾符和String

表達式 概述 xxx 實驗 有時 原則 得到 私有 ali

前言

在上一篇中,回顧了Java的基本數據類型 ,這篇就來回顧下Java中的一些修飾符以及String。

修飾符介紹

Java修飾符主要分為兩類:

  • 訪問修飾符
  • 非訪問修飾符

其中訪問修飾符主要包括 private、default、protected、public。
非訪問修飾符主要包括 static、final、abstract、synchronized。

訪問修飾符

訪問修飾符可以使用下圖這張表來說明訪問權限:

修飾符 當前類 同一包內 子類 其它包
public Y Y Y Y
protected Y Y Y N
default Y Y N N
private Y N N N

簡單點查看訪問級別的話,級別是由低到高。

 private<default<protected<public

private

被private修飾的變量、方法僅限在本類中使用。
所以private是最嚴的的訪問級別,主要用於隱藏類的一些細節實現和保護類的數據。
例如pojo類就是使用private修飾變量,對外提供setter和getter的方法。
還有如果使用private用來修飾構造方法的話,該類是不能實例化的。這種在單例模式中可以經常看到!

雖然private主要用於修飾變量和方法,不過也可以修飾內部類,只不過是內部類。

例如:

public class Test{
    //修飾一個私有變量
    private int count=1;
    //修飾一個私有方法
    private int add(int i,int j){
        return i+j;
   }
    private class Test1{            
    }
}

註意:private不能修飾外部類。

因為Test類中的變量和方法是私有的,所以其他類無法調用!
例:

public class Test2 {
    public static void main(String[] args) {
        Test t=new Test();
        //下面的變量和方法是無法獲取的
        //t.count=2;
        //t.add(1,2);
    }
}

說明:其實private修飾的方法和變量是可以使用反射調用,不過這裏就不說明了。

default

default:就是不使用任何修飾符。類、接口、變量、方法都可以使用。不過僅限在同一包下。

例如:

class Test{
     int count=1;
     int add(int i,int j){
            return i+j;
     }
    interface Test1{
    }
}

protected

被protected修飾的變量、方法僅僅對同一包內的類和所有子類可見。

例如:

public class Test{
    protected int count=1;
    protected int add(int i,int j){
            return i+j;
     }
     protected class Test1{
     }
}

在同包下可以直接調用,如果不在同包,則需要繼承才可以使用。

public class Test2 extends Test{
    public static void main(String[] args) {
        Test t=new Test();
        t.count=2;
        t.add(1,2);
    }
}

註意:protected不能修飾外部類。

public

public:修飾的類、接口、變量、方法對所有類都可以使用。

例如:

   public class Test{
    public int count=1;
    public int add(int i,int j){
            return i+j;
     }
  }

非訪問修飾符

為了實現一些其他的功能,Java 也提供了許多非訪問修飾符。

static

static: 用來修飾類變量和類方法。

靜態變量
static在修飾類變量的時候,無論該類被實例化了多少次,它的靜態變量只有一份拷貝。靜態變量也被稱為類變量。局部變量是不能被聲明為static變量的。

靜態方法
static在修飾類方法的時候,靜態方法是不能使用類的非靜態變量。靜態方法可以直接通過類名調用,因此靜態方法中是不能用thissuper關鍵字的。

示例:


 public class Test{
    public  String name="xuwujing";
    public  static String name2="xuwujing";

    public  static String getName() {
    //這個一句 會報錯  因為靜態方法是不能使用類的非靜態變量
    //String reult=name;
    //這一句就可以
    String reult=name2;
    return reult;
     }

    //main方法是靜態方法,可以直接調用本類中的靜態方法和靜態變量
    public static void main(String[] args) {
        System.out.println(name2);
        System.out.println(getName());
    }

    //該方法是不靜態方法,所以調用本類中的靜態方法和靜態變量時,
    //需要使用classname.variablename和 classname.methodname的方式訪問
    private void print(){
        System.out.println(Test.name2);
        System.out.println(Test.getName());
     }
    }

在這裏順便提一下,static 靜態塊。
在JVM類加載機制中,如果類存在直接的父類並且這個類還沒有被初始化,那麽就先初始化父類;如果類中存在初始化語句,就依次執行這些初始化語句。
可能上述的兩句話不太好理解,那麽這裏我們來運行下代碼查看其結果,通過結果可能就能更好的理解上述語句的話了。

示例:

class HelloA {

    public HelloA() {
        System.out.println("HelloA"); 
    }

    { System.out.println("I‘m A class"); } 

    static { System.out.println("static A"); } 

}
public class HelloB extends HelloA{
    public HelloB() {
        System.out.println("HelloB"); 
    }

    { System.out.println("I‘m B class"); }  

    static { System.out.println("static B"); } 

    public static void main(String[] args) {
        new HelloB();   
}

結果:

static A
static B
I‘m A class
HelloA
I‘m B class
HelloB

那麽根據這個類返回的結果是不是感覺更好理解了呢?
創建對象時構造器的調用順序是:

先初始化靜態成員,然後調用父類構造器,再初始化非靜態成員,最後調用自身構造器。

那麽static修飾符這塊的運用可以總結如下:

  1. 靜態變量在內存中只有一個拷貝,在類的所有實例中共享。
  2. 在靜態方法中不能直接訪問實例方法和實例變量,反之可以。
  3. 在靜態方法中不能使用this和super關鍵字。
  4. 靜態方法不能被abstract修飾。
  5. 靜態方法和靜態變量都可以通過類名直接訪問。
  6. 當類被加載時,靜態代碼塊只被加載一次。有多個靜態變量或塊時,按聲明順序加載。

final

final :用來修飾類、方法和變量。
final 修飾的類不能夠被繼承,修飾的方法不能被繼承類重新定義,修飾的變量為常量,是不可修改的。
如果上述語句不好理解的話,我們可以通過編寫相關代碼進行實驗。
定義一個final修飾的變量、方法以及類。然後進行相關的測試

示例:


 public class Test{
        //定義一個final修飾的變量
    public  static final String name="xuwujing";

  public static void main(String[] args) {
        //這句會報錯  因為該變量已經被final修飾了
        name="張三";
    }
    //類加上final之後,該類是無法被繼承的
    final class Test2{
    }
    //這句會報錯,因為Test2是被final修飾的類
    class Test3 extends Test2{
    }

    class Test4{
        //定義一個被final修飾的方法
         final Date getTime(){
            return new Date();
        }
    }

    class Test5 extends Test4{
        //這句會報錯,因為final方法是不能被子類修改的。
        Date getTime(){
        return new Date();
        }
    }
  }

從上述 代碼結果,我們可以得出一下結論:

final修飾類:表示該類不能被繼承;
final修飾方法:表示方法不能被重寫;
final修飾變量:表示變量只能一次賦值以後值不能被修改(常量);

abstract

abstract :用來創建抽象類和抽象方法。

Java是面向對象的語言,而抽象類是Java語言中對抽象概念進行定義的一種機制,也正是因為這個,所以賦予了Java強大的面向對象的能力。

修飾類

會使這個類成為一個抽象類,這個類將不能生成對象實例,但可以做為對象變量聲明的類型(見後面實例),也就是編譯時類型。抽象類就相當於一類的半成品,需要子類繼承並覆蓋其中的抽象方法。

修飾方法

會使這個方法變成抽象方法,也就是只有聲明而沒有實現,需要子類繼承實現。

這裏依舊使用一個簡單例子來進行理解。

public class AbstractTest{
    public static void main(String[] args) {
        //這句會報錯,因為抽象類不能實例化
        // Animal a=new Animal();
        //抽象類可以實例化重寫該類抽象方法的子類
        Animal a = new Dog();
        a.show();
    }
}
abstract class Animal{
    abstract void show();
    public void print(){
        System.out.println("Animal");
    }
}
//繼承抽象類需要實現抽象類的方法
class Dog extends Animal{   
    @Override
    void show() {
        System.out.println("This is Dog!");
    }
}

總結:

1、抽象類和抽象方法都需要被abstract修飾。抽象方法一定要定義在抽象類中。
2、抽象類不可以創建實例,原因:調用抽象方法沒有意義。
3、只有覆蓋了抽象類中所有的抽象方法後,其子類才可以實例化。否則該子類還是一個抽象類。

註意事項:

1、抽象類不能用來實例化對象,聲明抽象類的唯一目的是為了將來對該類進行擴充。 2、一個類不能同時被 abstract 和 final
修飾。如果一個類包含抽象方法,那麽該類一定要聲明為抽象類,否則將出現編譯錯誤。
3、抽象方法是一種沒有任何實現的方法,該方法的的具體實現由子類提供。 4、抽象方法不能被聲明成 final 和 static。
5、任何繼承抽象類的子類必須實現父類的所有抽象方法,除非該子類也是抽象類。
6、如果一個類包含若幹個抽象方法,那麽該類必須聲明為抽象類。抽象類可以不包含抽象方法。

synchronized

synchronized: 修飾的方法同一時間只能被一個線程訪問。在多線程中運用很常見。
synchronized 的解釋如下:

synchronized 方法控制對類成員變量的訪問:每個類實例對應一把鎖,每個 synchronized方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的線程方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類實例,其所有聲明synchronized 的成員函數中至多只有一個處於可執行狀態(因為至多只有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。

簡單的來說,就是使用synchronized 修飾的方法,在多線程進行同時訪問的時候,只會讓一個線程先進行訪問,其它的線程等候,當這個線程訪問完了之後,再讓下一個進行訪問,依次類推。

Java中還有兩個不太常見的修飾符,transientnative

transient:被 transient 修飾的實例變量時,java 虛擬機(JVM)跳過該特定的變量。
native: 被native修飾的方法實際是由另一種語言進行實現的本地方法。例如Java中獲取的Long類型的時間戳 :System.currentTimeMillis(); 實際是由native 修飾的,
源碼為:

 public static native long currentTimeMillis();

String

String 類型可能就是我們最常用的的對象了。
首先說明,String並不是基本數據類型,而是一個對象,並且是不可變的對象。查看源碼可以String類是被final修飾的,是不可被繼承的!

String的在未被初始化的時候為null,表示它還沒有被創建,自然也就沒有分配空間;
而" "和 new String()不是null,它們是已經被創建,只是值為空而已!並且也分配了內存空間。

String有15種構造方法,有兩種是過時的,其中包含char[],byte[],int[],String,StringBuffer,StringBuilder。
我們在創建String對象的的時候,一般是使用 String str="xxx",但有時也會用new String()來初始話字符串。
例如:

    String hello="hello";
    String newHello=new String("hello");
    char []cHello ={‘h‘,‘e‘,‘l‘,‘l‘,‘o‘};
    String str=new String(cHello);

註意:String 類是不可改變的,所以你一旦創建了 String 對象,那它的值就無法改變了。

String常用方法

大概講述了String的用法之後,這裏我們來列舉一些String常用的方法。

1.length :返回此字符串的長度。
2.charAt:返回指定索引處的 char 值。
3.compareTo:把這個字符串和另一個對象比較。
4.concat:將指定字符串連接到此字符串的結尾。
5.split:根據給定正則表達式的匹配拆分此字符串。
6.equals:將此字符串與指定的對象比較。
7.endsWith:測試此字符串是否以指定的後綴結束。
8.startsWith:測試此字符串是否以指定的前綴結束。
9.getBytes: 使用平臺的默認字符集將此 String 編碼為 byte 序列,並將結果存儲到一個新的 byte 數組中。
10.indexOf:返回指定字符在此字符串中第一次出現處的索引。
11.replace:返回一個新的字符串,它是通過用 newChar 替換此字符串中出現的所有 oldChar 得到的。 12:substring:返回一個新的字符串,它是此字符串的一個子字符串。
...

更多可以參考Api文檔。

String對象比較

String作為我們最常用的對象,在面試中估計也會接觸不少。一般來說,會考到String的常量池相關問題,主要是使用String進行比較的時候,==和equals這兩種方法來判斷是否相當。這裏收集了一些String經常遇到的問題。
代碼如下:


            String s1 = "test";
            String s2 = new String("test");
            String s3 = "te";
            String s4 = "st";
            String s5 = "te" + "st";
            String s6 = s3 + s4;
            String s7 = new String(s1);
            System.out.println(s1 == s2); 
            System.out.println(s1 == s5); 
            System.out.println(s1 == s6); 
            System.out.println(s7==s1);       
            System.out.println(s7.equals(s1)); 

結果:

false
true
false
false
true

如果有經驗的話,大概可以一眼看出結果。但是如果經驗不足的話,往往會吃這個虧。這裏來解釋下為什麽會出現這種結果。

1.雖然看起來是一樣的,但是新建一個String類的時候會重新分配引用地址,而 == 就是比較引用地址,所以為false。
2.在編譯之前就可以確認s5=test, 並且引用地址一樣,所以為true;
3.字符串常量池的原則 這時 s6 的值是在運行時得到的,它會重新構造字符串對象 所以為false。
4.和第一個一樣的,就是換湯不換藥,所以為false。
5.equals 只比較值相等,不關心它的引用地址。

看完上面的例子之後,再來看看下面的這個
代碼示例:

 String ab="ab";
 String c="c";
 String ab_c=ab+c;
 String ab_c1="ab"+"c";
 String abc="abc";
 System.out.println(ab_c == abc + " : " + ab_c.equals(abc));

 System.out.println((ab_c == abc) + " : " + ab_c.equals(abc));

 System.out.println((ab_c1 == abc) + " : " + ab_c1.equals(abc));

運行結果:

false
false : true
true : true

到這裏,可能就會詫異了,為什麽和我想的不一樣呢?
這裏其實是有陷阱的,也就是運算符的優先級。
第一個結果就是優先級的問題導致的,它會先計算 abc + " : " + ab_c.equals(abc) ,然後再來進行比較,所以為false。同理,下面的也是如此,基本和上面的那個例子差不多,這裏就不再概述了。

String、StringBuffer和StringBuilder

String、StringBuffer和StringBuilder的區別:

  • String: String的特點是一旦賦值,便不能更改其指向的字符對象,如果更改,則會指向一個新的字符對象。
  • StringBuffer:StringBuffer對象可以調用其方法動態的進行增加、插入、修改和刪 除操作,且不用像數組那樣事先指定大小,從而實現多次插入字 符,一次整體取出的效果,因而操作字符串非常靈活方便。並且生成數據之後可以toString轉為String,線程安全。
  • StringBuilder:它是在單線程環境下使用的,因為它的所有方面都沒有被synchronized修飾,因此它的效率也比StringBuffer要高。

關於字符串拼接方式,在String類中,我們最常用的是 + ,其次是使用StringBuffer或StringBuilder 的append方法,至於String類中的concat幾乎很少用到。
一般來說,如果在少量的字符串進行拼接的話,我們會使用+,如果拼接過多的話,單線程使用 StringBuilder ,多線程使用StringBuffer 進行拼接。因為使用String 的 + 在過多的字符串進行拼接的時候會極大的使用內存,因為它在憑借的時候還是使用 append()方法,然後再進行toString轉換,如果是少量的時候,是感覺不到差異的,但是在大量拼接的時候就會明顯感受得到。

代碼示例:

String str="Hello World";
String str1="";
StringBuffer sbr=new StringBuffer(str); 
StringBuilder sbd=new StringBuilder(str); 
long start=System.currentTimeMillis();
   for(int i=0;i<10000;i++){
     str1+=str;
   }
   System.out.println("String累加用時:"+(System.currentTimeMillis()-start)+"ms");
   long start2=System.currentTimeMillis();
   for(int i=0;i<10000;i++){
     sbr.append(str);
   }
   System.out.println("StringBuffer累加用時:"+(System.currentTimeMillis()-start2)+"ms");
   long start3=System.currentTimeMillis();
   for(int i=0;i<10000;i++){
     sbd.append(str);
   }
   System.out.println("StringBuilder累加用時:"+(System.currentTimeMillis()-start3)+"ms");

結果:

String累加用時:701ms
StringBuffer累加用時:2ms
StringBuilder累加用時:0ms

這裏從輸出結果中可以看到String 的+拼接方法的耗時了。但是使用 + 實在是方便。所以在這裏建議如果字符串拼接次數在10一下,可以使用+,過多的則用StringBuffer或StringBuilder。

其它

參考:
https://blog.csdn.net/qiumengchen12/article/details/44939929

https://blog.csdn.net/chenssy/article/details/13004291

到此,本文就結束了,謝謝閱讀!歡迎留言和點贊,你的支持是我寫作最大的動力!
版權聲明:
作者:虛無境
博客園出處:http://www.cnblogs.com/xuwujing
CSDN出處:http://blog.csdn.net/qazwsxpcm    
個人博客出處:http://www.panchengming.com
原創不易,轉載請標明出處,謝謝!

Java基礎知識回顧之二 ----- 修飾符和String