Java


swing awt區別

第四章

  1. 在 Java 中,同一個類文件,僅可存在於一個 public 修飾類,且該 .java 文件要與public修飾類同名,否則將會報

  2. 遞歸的本質就是用壓棧與出棧操作 :

    def dict(x):
       if x==1:
           return 1
       else:
           return x*dict(x-1)
    a=dict(3)
    print(a)

    /*對於這個代碼:
    (1) 首先調用函數dict(3),此時將函數dict以及變量x的值為3,這些壓入棧上。並執行dict函數
    (2) if判斷完之后調用函數dict(2),同樣的再將函數dict和變量x的值為2壓入棧上。並執行dict函數
    (3) 再循環一次步驟2,然后就結束,下面就是執行階段。
    (4) 首先執行最上面一層,就是dict(1),返回值1.
    (5) 然后依次進行。*/

    當遞歸調用時每次調用自己時可以看做是壓棧過程,當遞歸條件滿足結束時,遞歸一級一級的返回時可以看做是出棧的過程。

  3. Java 中修飾類中屬性、方法修飾符:public、private、protected、default (默認)

    修飾符是有四種的,所以在類里,如果不加就是默認類型[ 使用:僅在同一包內可見 ]

  4. Java中直接輸出一個對象會怎么樣

  5. final修飾符有什么作用?

    final是java中的關鍵字,可以修飾類、方法和變量

    被final修飾的類不可以被繼承

    被final修飾的變量最多僅能賦值一次,且不能被改變。

    被final修飾的方法不能被重寫。

  6. 構造函數

    構造器不能是abstract, static, final, native, strictfp, 或者synchronized的

    構造器不是通過繼承得到的,所以沒有必要把它聲明為final的。

    構造器總是關聯一個對象而被調用,所以把它聲明為static是沒有意義的(每當創建一個對象構造函數自動被調用)

    構造方法沒有函數返回值,甚至連void類型也不是。

  7. 假設沒有static關鍵字,那意味着需要用生成一個實例后才可以調用這個Main方法,而Main方法是程序入口點,你沒有進入Main方法,自然無法生成一個實例,既然沒有實例,那就無法調用Main函數,豈不矛盾?所以Main函數被設置為static.

    不能在main方法中打印this關鍵字的信息,這時想起了之前的知識,不能在靜態方法中調用this。理由很簡單,this表示“這個對象”,也就是聲明一個類的對象,然而靜態方法是不屬於某一個特定對象而是屬於這個類的。

  8. java application

    Java語言中,能夠獨立運行的程序稱為Java應用程序(Application)。 Java語言還有另外一種程序——Applet程序。Applet程序(也稱Java小程序)是運行於各種網頁文件中,用於增強網頁的人機交互、動畫顯示、聲音播放等功能的程序。 Java Applet和Java Application在結構方面的主要區別表現在: (1)運行方式不同。Java Applet程序不能單獨運行,它必須依附於一個用HTML語言編寫的網頁並嵌入其中,通過與Java兼容的瀏覽器來控制執行。 Java Application是完整的程序,可以獨立運行,只要有支持Java的虛擬機,它就可以獨立運行而不需要其他文件的支持。 (2)運行工具不同。運行Java Applet程序的解釋器不是獨立的軟件,而是嵌在瀏覽器中作為瀏覽器軟件的一部分。Java Application程序被編譯以后,用普通的Java 解釋器就可以使其邊解釋邊執行,而Java Applet必須通過網絡瀏覽器或者Applet觀察器才能執行。

    我們入門第一步寫的HelloWorld就是javaapplication

    Application程序執行時,為什么不能帶后綴名?

    java命令是執行一個類。 若寫 java xxx.yyy 是代表要運行 package xxx 中的class yyy 里面的 main(String[]) 所以當你寫 java xxx.class 時, 它會以為要找一個叫xxx的package里面的一個叫class的class.

  9. 垃圾回收 finalize

    在Java中,對象什么時候可以被垃圾回收?

    1. 當一個對象到GC Roots不可達時,在下一個垃圾回收周期中嘗試回收該對象,如果該對象重寫了finalize()方法,並在這個方法中成功自救(將自身賦予某個引用),那么這個對象不會被回收。但如果這個對象沒有重寫finalize()方法或者已經執行過這個方法,也自救失敗,該對象將會被回收。

    2. 當沒有任何對象的引用指向該對象時+在下次垃圾回收周期來到時=>對象才會被回收

    大神

    1. 如何證明一個垃圾對象被釋放了:

  10. 關於類中變量的初始化 dadada

    Java盡力保證:所有變量在使用前都得到恰當的初始化。

  11. 這個的意義是什么

    public class Test {

    public static void print(String[] args) {
    for (int i = 0; i < args.length; i++) {
    System.out.println("args[i]:"+args[i]);
    }
    }

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



### 第五章

1. 封裝的目的在於保護信息,使用它的主要優點如下。

  >- 保護類中的信息,它可以阻止在外部定義的代碼隨意訪問內部代碼和數據。
  >- 隱藏細節信息
  >- 有助於建立各個系統之間的松耦合關系,提高系統的獨立性.
  >- 提高軟件的復用率,降低成本。
  >
  >Java 封裝是如何實現的:
  >
  > . 修改屬性的可見性來限制對屬性的訪問(一般限制為private)
  >
  > 對每個值屬性提供對外的公共方法訪問,也就是創建一對賦取值方法,用於對私有屬性的訪問
  >
  >

2. 單例模式

  > 單例模式有很多種寫法 懶漢模式 就是書上的那個:
  >
  >```java
  >
  >public class Singleton{
  >   private static Singleton instance = null;
  >   private Singleton(){}
  >   public static Singleton newInstance(){
  >       if(null == instance){
  >           instance = new Singleton();
  >       }
  >       return instance;
  >   }
  >
  >```
  >
  >懶漢模式中單例是在需要的時候才去創建的,如果單例已經創建,再次調用獲取接口將不會重新創建新的對象,而是直接返回之前創建的對象。如果某個單例使用的次數少,並且創建單例消耗的資源較多,那么就需要實現單例的按需創建,這個時候使用懶漢模式就是一個不錯的選擇。但是這里的懶漢模式並沒有考慮線程安全問題,在多個線程可能會並發調用它的getInstance()方法,導致創建多個實例,因此需要加鎖解決線程同步問題; [單例模式VS靜態類 ]( https://www.cnblogs.com/cielosun/p/6582333.html )

3.
 
  1. java 子類父類相互轉換

    Java 子類強轉父類

    父類引用指向子類對象:

    java中子類強轉父類,實際上依然是子類;

    該引用只能調用父類中定義的方法和變量;

    如果子類中重寫了父類中的一個方法,那么在調用這個方法的時候,將會調用子類中的這個方法;

    Java 父類強轉子類

    只有父類對象本身就是用子類new出來的時候, 才可以在將來被強制轉換為子類對象.


    一、父類引用指向子類對象時

    1、若子類覆蓋了某方法,則父類引用調用子類重新定義的新方法[!!!!!!]

    2、若子類未覆蓋某方法,則父類引用調用父類本身的舊方法

    3、若子類覆蓋了某屬性,但父類引用仍調用父類本身的舊屬性[!!!!!!!]

    4、若子類未覆蓋某屬性,則父類引用調用父類本身的舊屬性

    5、父類引用不能訪問子類新定義的方法

    (看1,3 java 屬性可以隱藏 方法沒有隱藏的概念)

    二、子類引用指向自身對象時

    1、若子類覆蓋了某方法,則子類引用調用子類重新定義的新方法

    2、若子類未覆蓋某方法,則子類引用調用父類本身的舊方法

    3、若子類覆蓋了某屬性,則子類引用調用子類重新定義的新屬性

    4、若子類未覆蓋某屬性,則子類引用調用父類本身的舊屬性

    5、子類引用可以訪問子類新定義的方法

    和super不一樣啊啊啊啊啊super人家調用的就是父類的信息啊

    不管有沒有被覆蓋[就因為被覆蓋了才出來的]super.變量 super.函數

    class Country {
    String name;

    void value() {
    name = "China";
    System.out.println("這里是父類");
    }
    }

    class City extends Country {
    String name;
    void value() {
    name = "Shanghai";
    super.value(); // 調用父類的方法 System.out.println("這里是父類");
    System.out.println(name); // shang hai
    System.out.println(super.name);// bei jing
    }

    public static void main(String[] args) {
    City c = new City();
    c.value();
    }
    }
    class Country {
    String name = "China";;
    void value() {
    System.out.println("這里是父類name: "+name);
    }
    }

    class City extends Country {
    String name;

    void value() {
    name = "Shanghai";
    super.value(); //這里是父類name: China
    super.name = "China changeed";
    super.value(); //這里是父類name: China changeed
    System.out.println(name); // shang hai
    System.out.println(super.name);// China changeed
    //同書本p76 想說的是 在子類中更改了父類的域變量,接下來這個類用這個變量都是更改過的了【不會影響其他子類/因為是域變量嘛 各個子類都復制到自己的區域了 不影響】
    //回到該子類 更改之后在立馬super.父類方法(子類已覆蓋):"相當於將父類代碼復制到此處,所用的域變量均為父類域變量"     but父類域變量剛剛被我更改了!!
    //改了 這個真的改了 不更改就都還是china
    //but this還是指的是子類對象【ppt10張】
    }
    public static void main(String[] args) {
    City c = new City();
    c.value();
    }
    }

    super 不同於 父類聲明子類變量這個事情

    1. 對象實例化過程 書本p106 博客

    2. 抽象類

      抽象類的使用原則如下: (1)抽象方法必須為public或者protected(因為如果為private,則不能被子類繼承,子類便無法實現該方法),缺省情況下默認為public; (2)抽象類不能直接實例化,需要依靠子類采用向上轉型的方式處理; (3)抽象類必須有子類,使用extends繼承,一個子類只能繼承一個抽象類; (4)子類(如果不是抽象類)則必須覆寫抽象類之中的全部抽象方法(如果子類沒有實現父類的抽象方法,則必須將子類也定義為為abstract類。);

      抽象類可以沒有抽象方法和抽象域變量。。。

      如果一個類要被聲明為static的,只有一種情況,就是靜態內部類

    3. 接口定義用關鍵字interface,而不是用class,interface前的修飾符要么為public,要么為缺省

      接口中的字段(域)的值存儲在該接口的靜態存儲區域內,使用接口名.字段或實現類.字段均可訪問 [因為接口的域變量都是 public static final ]【在實現類里就相當於靜態變量了】

      一個接口可以繼承多個接口,但接口不能繼承類 [類當然能實現接口,..實現類]

    接口有什么用?

    1. 實現多重繼承

    2. 接口是一種標准,使用者按照接口使用,實驗者按照接口實現,當實現者內部發生變化時,只要接口不變,使用者就不必更改代碼。

    3. 擴展性強

    1. applet當中的text field每輸入一個字符 在一個label當中都能動態刷新跟蹤

    import java.applet.Applet;
    import java.awt.Label;
    import java.awt.TextField;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;

    public class MyApplet2 extends Applet implements KeyListener {
    TextField tf = new TextField(10);
    Label l = new Label("這里顯示輸入的字符                   ");

    public void init() {
    tf.addKeyListener(this);
    add(tf);
    add(l);
    setSize(500, 100);
    tf.addKeyListener(this);
    }
    public void keyPressed(KeyEvent arg0) { //按下鍵盤
    }
    public void keyReleased(KeyEvent arg0) {//釋放按鍵
    }    //這兩個函數不能刪
    public void keyTyped(KeyEvent ke) { //敲擊完立馬顯示
    l.setText(tf.getText() + ke.getKeyChar());
    }
    }
    //鍵盤事件類(KeyEvent)是容器內的任意組件獲得焦點時,組件發生鍵擊事件,當按下
    //釋放或鍵入某一個鍵時,組件對象將產生該事件。使用鍵盤事件必須給組件添加一個
    //KeyListener 接口的事件處理器,該接口包含以下 3 個方法。
    //
    //       void keyPressed(KeyEvent e):按下按鍵時發生。
    //       void keyReleased(KeyEvent e):松開按鍵時發生。
    //       void keyTyped(KeyEvent e):敲擊鍵盤,發生在按鍵按下后,按鍵放開前。
    1. 什么是數據隱藏?如何證明子類對父類同名方法進行重新定義,只能是方法的覆蓋,而不是方法的隱藏: 在子類對父類的繼承中,如果子類的成員變量和父類的成員變量同名,此時稱為子類隱藏(override)了父類的成員變量

      變量是隱藏,父類引用(=子類對象之后).變量還是父類的變量

      而方法不是 此時父類引用.方法就只還是子類的方法了 所以是直接覆蓋掉了

      1、若子類覆蓋了某方法,則父類引用調用子類重新定義的新方法[!!!!!!]

      3、若子類覆蓋了某屬性,但父類引用仍調用父類本身的舊屬性[!!!!!!!]


       

       

第九章 線程

  1.  

    什么是進程,什么是線程?

    進程:是並發執行的程序在執行過程中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。

    線程:是進程的一個執行單元,是進程內科調度實體。比進程更小的獨立運行的基本單位。線程也被稱為輕量級進程。

    一個程序至少一個進程,一個進程至少一個線程。

    進程線程的區別

    1、地址空間:同一進程的線程共享本進程的地址空間,而進程之間則是獨立的地址空間。

    2、資源擁有:同一進程內的線程共享本進程的資源,但是進程之間的資源是獨立的。

    3、一個進程崩潰后,在保護模式下不會對其他進程產生影響,但是一個線程崩潰整個進程都死掉。所以多進程要比多線程健壯。

    4、進程切換時,消耗的資源大,效率高。所以涉及到頻繁的切換時,使用線程要好於進程。同樣如果要求同時進行並且又要共享某些變量的並發操作,只能用線程不能用進程。

    5、執行過程:每個獨立的進程程有一個程序運行的入口、順序執行序列和程序入口。但是線程不能獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。

    6、線程是處理器調度的基本單位,但是進程不是。[ 進程是資源分配的最小單位,線程是程序執行的最小單位 ]

    7、兩者均可並發執行。

  2. 通過調用Thread類的start()方法來啟動一個線程

    每個線程都是通過某個特定Thread對象所對應的方法run() 來完成其操作的,方法run()稱為線程體

    1. start 和 run 方法解釋: 

    1) start:   用start方法來啟動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。通過調用Thread類的start()方法來啟動一個線程,這時此線程處於就緒(可運行)狀態,並沒有運行,一旦得到cpu時間片,就開始執行run()方法,這里方法 run()稱為線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。 2) run: 如果直接用Run方法,書p153 這只是調用一個方法而已,程序中依然只有主線程--這一個線程,其程序執行路徑還是只有一條,這樣就沒有達到寫線程的目的。

  3. 先調用start后調用run,這么麻煩,為了不直接調用run?就是為了實現多線程的優點,沒這個start不行。

    1.start()方法來啟動線程,真正實現了多線程運行。這時無需等待run方法體代碼執行完畢,可以直接繼續執行下面的代碼; 2.run()方法當作普通方法的方式調用。(程序還是要順序執行,要等待run方法體執行完畢后,才可繼續執行下面的代碼; 程序中只有主線程——這一個線程, 其程序執行路徑還是只有一條, 這樣就沒有達到寫線程的目的。)

  4. synchronized 寫的挺好的

    如果程序是單線程的,就不必擔心此線程在執行時被其他線程“打擾”,就像在現實世界中,在一段時間內如果只能完成一件事情,不用擔心做這件事情被其他事情打擾。但是,如果程序中同時使用多線程,好比現實中的“兩個人同時通過一扇門”,這時就需要控制,否則容易引起阻塞。

    為了處理這種共享資源競爭,可以使用同步機制。所謂同步機制,指的是兩個線程同時作用在一個對象上,應該保持對象數據的統一性和整體性。Java提供 synchronized 關鍵字,為防止資源沖突提供了內置支持。共享資源一般是文件、輸入/輸出端口或打印機。

當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個線程執行該段代碼。

一、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以后才e3能執行該代碼塊。

二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。

三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。

四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。

五、以上規則對其它對象鎖同樣適用 . add1 add2

  1. 線程是什么?進程是什么?二者有什么區別和聯系?

    線程是CPU獨立運行和獨立調度的基本單位; 進程是資源分配的基本單位; 聯系: 進程和線程都是操作系統所運行的程序運行的基本單元。 區別:

    進程具有獨立的空間地址,一個進程崩潰后,在保護模式下不會對其它進程產生影響。 線程只是一個進程的不同執行路徑,線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉。

    3、線程和進程的關系以及區別?

    進程和線程的關系:

    (1)一個線程只能屬於一個進程,而一個進程可以有多個線程,但至少有一個線程。 (2)資源分配給進程,同一進程的所有線程共享該進程的所有資源。 (3)處理機分給線程,即真正在處理機上運行的是線程。 (4)線程在執行過程中,需要協作同步。不同進程的線程間要利用消息通信的辦法實現同步。線程是指進程內的一個執行單元,也是進程內的可調度實體.

    進程與線程的區別:

    (1)調度:線程作為調度和分配的基本單位,進程作為擁有資源的基本單位 (2)並發性:不僅進程之間可以並發執行,同一個進程的多個線程之間也可並發執行 (3)擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但可以訪問隸屬於進程的資源. (4)系統開銷:在創建或撤消進程時,由於系統都要為之分配和回收資源,導致系統的開銷明顯大於創建或撤消線程時的開銷。

 

 

 

通信同步問題

如果調用某個對象的wait()方法,當前線程必須擁有這個對象的monitor(即鎖),因此調用wait()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)。

 

sleep和wait的區別有 1,這兩個方法來自不同的類分別是Thread和Object 2,最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。 3,wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在 任何地方使用 synchronized(x){ x.notify() //或者wait() } 4,sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常


sleep()方法是Thread類里面的,主要的意義就是讓當前線程停止執行,讓出cpu給其他的線程,但是不會釋放對象鎖資源以及監控的狀態,當指定的時間到了之后又會自動恢復運行狀態。

wait()方法是Object類里面的,主要的意義就是讓線程放棄當前的對象的鎖,進入等待此對象的等待鎖定池,只有針對此對象調動notify方法后本線程才能夠進入對象鎖定池准備獲取對象鎖進入運行狀態


wait方法依賴於同步,而sleep方法可以直接調用

 

1 sleep() 使當前線程進入停滯狀態,所以執行sleep()的線程在指定的時間內肯定不會執行, 同時sleep函數不會釋放鎖資源. sleep可使優先級低的線程得到執行的機會,當然也可以讓同優先級和高優先級的線程有執行的機會

2 yield() 只是使當前線程重新回到可執行狀態,所以執行yield()線程有可能在進入到可執行狀態后馬上又被執行. 只能使同優先級的線程有執行的機會。同樣, yield()也不會釋放鎖資源.

sleep和yield的區別在於, sleep可以使優先級低的線程得到執行的機會, 而yield只能使同優先級的線程有執行的機會.

 

 2.wait()方法   在其他線程調用對象的notify或notifyAll方法前,導致當前線程等待。線程會釋放掉它所占有的“鎖標志”,從而使別的線程有機會搶占該鎖。   當前線程必須擁有當前對象鎖。如果當前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常。   喚醒當前對象鎖的等待線程使用notify或notifyAll方法,也必須擁有相同的對象鎖,否則也會拋出IllegalMonitorStateException異常。   waite()和notify()必須在synchronized函數或synchronized block中進行調用。

 

synchronized, wait, notify 線程的同步需要依靠上面兩個函數和一個同步塊實現.

(1)調用wait方法后,線程是會釋放對monitor對象的所有權的。

(2)一個通過wait方法阻塞的線程,必須同時滿足以下兩個條件才能被真正執行:

  • 線程需要被喚醒(超時喚醒或調用notify/notifyll)。

  • 線程喚醒后需要競爭到鎖(monitor)。

 

為什么會產生死鎖? 在多線程環境里, 產生死鎖的原因是由於幾個線程同時共用一個同步資源. 這是產生死鎖的前提,

產生死鎖的原因, 所有線程都在等待共享資源的釋放.

https://www.cnblogs.com/dolphin0520/p/3920385.html

 

什么是線程安全?為什么會產生線程安全問題?如何解決線程安全問題

線程安全就是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現數據不一致或者數據污染。

當一個類被多個線程進行訪問並且正確運行,它就是線程安全的

線程不安全就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據


出現原因 1)某一個操作不是原子性的操作 2)同一時間有多個線程同時執行這個操作


1、 使用synchronized同步代碼塊,或者用Lock鎖 2、不共享狀態: 3、不可變對象:可以使用final修飾的對象保證線程安全 4. 使用線程安全的類

 

 

內置鎖

Java內置鎖通過synchronized關鍵字使用,使用其修飾方法或者代碼塊,就能保證方法或者代碼塊以同步方式執行。使用起來非常近簡單,就像下面這樣:

// synchronized關鍵字用法示例
public synchronized void add(int t){// 同步方法
   this.v += t;
}

public static synchronized void sub(int t){// 同步靜態方法
   value -= t;
}
public int decrementAndGet(){
   synchronized(obj){// 同步代碼塊
       return --v;
  }
}

這就是內置鎖的全部用法,你已經學會了。

內置鎖使用起來非常方便,不需要顯式的獲取和釋放,任何一個對象都能作為一把內置鎖。使用內置鎖能夠解決大部分的同步場景。“任何一個對象都能作為一把內置鎖”也意味着出現synchronized關鍵字的地方,都有一個對象與之關聯,具體說來:

  • 當synchronized作用於普通方法是,鎖對象是this;

  • 當synchronized作用於靜態方法是,鎖對象是當前類的Class對象;

  • 當synchronized作用於代碼塊時,鎖對象是synchronized(obj)中的這個obj。

 

十四章 IO流

  1. 流從流動方向上看:一般分為輸入流和輸出流

    •輸入流:如System.in是一個InputStream類型輸入流

    •輸出流:如System.out 是一個PrintStream類型輸出流

  2. 從鍵盤輸入字符:

     byte[] buffer = new byte[512];
    int count = System.in.read(buffer);

    //接下來就可以應用啦
    //for(int i=0;i<count;i++) {
     //System.out.print(" "+(char)buffer[i]);
    //}

    讀取文件:

    FileInputStream in = new FileInputStream("D:\\211\\child-5000.dat");
    int count = 512,c= 0; //函數里的變量要自己賦值 系統不會默認賦初值的[域變量系統會自動~]
    byte[] b = new byte[count];
    while((c = in.read(b,0,count))!= -1) {
      System.out.println(new String(b,0,c));
     }
    in.close();
    

    寫入文件

     int count = 512,n=512;
     byte[] b = new byte[n]; //我的天啊啊啊啊啊啊啊啊啊注意這里是小寫的byte!!
     count = System.in.read(b);//從鍵盤輸入的 都存在緩沖區【數組b】中 是有返回值的!!!
     FileOutputStream out =  FileOutputStream("write.txt");
     out.write(b,0,count); //從b中讀 寫入out中
     out.close();
    

    BufferedReader 用於緩存字符流,可以一行一行的讀

    BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));    	
    String c1;
    int i = 0;
    int[] e = new int[10];//10 [] 所以是arraysize
    while(i<10) {
       c1 = keyin.readLine();
      e[i]  = Integer.parseInt(c1);
      i++;
    } //輸入10次
    for(i = 0; i<10;i++) {
       System.out.print(e[i]);
    }
    //BufferedReader的readline() 通過下列字符之一即可認為某行已終止:換行 ('\n')、回車 ('\r') 或回車后直接跟着換行
    //返回值是字符串 包括此行內容 不包括任何行終止符或者null,如果流的末尾已到達  如果到達流末尾, 就返回null
    

    java DataOutputSteam / DataInputStream

    DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("test.txt")));
    //數據輸出流 允許應用程序以與機器無關方式將Java基本數據類型寫到底層輸出流。
    dos.writeInt(3);
    dos.writeDouble(3.14);
    dos.writeUTF("hello");
    dos.close();//將指定的基本數據類型以字節的方式寫入到輸出流 就是這些很簡單的類型 這樣 就寫入到流里了
    
    DataInputStream dis = new DataInputStream(new BufferedInputStream(new  FileInputStream("test.txt")));
    //數據輸入流 允許應用程序以與機器無關方式從底層輸入流中讀取基本 Java 數據類型
    System.out.println(dis.readInt());
    System.out.println(dis.readDouble());
    System.out.println(dis.readUTF());
    dis.close(); //從流里一讀 讀出來就是想用的簡單格式
    //你把BufferedInputStream/BufferedOutputStream刪掉也可以:實現了緩沖功能的輸入流/輸出流。使用帶緩沖的輸入輸出流,效率更高,速度更快。
    //https://blog.csdn.net/zhaoyanjun6/article/details/54894451
    

    我說一下為什么這么寫[上文就是流裝配]:DateInputStream是FilterInputStream的子類,要使用過濾流,必須把他連接在某個輸入輸出流上,所以要往里綁

    緩沖流,“把節點流捆綁到緩沖流上可以提高讀寫效率” 所以要使用緩沖流加快效率的話,格式是

    BufferedInputStream(InoutStream xx【里面這個參數就是節點流了 】)

     

    PrintWriter

    //printwriter 可以向該字符流寫入Java基本數據類型
    public static void main(String[]gs) throws FileNotFoundException {
        PrintWriter out = new PrintWriter(new BufferedOutputStream(new FileOutputStream("test1.txt")));
        out.println("hello world");
        out.println(3);
        out.close();
        //書p222 當調用他的println()方法或者是字符串本身有換行====》自動執行flush()
    }
    

    將字符串用裝配流的方式輸入到屏幕上

    PrintWriter outted = new PrintWriter(new OutputStreamWriter(System.out),true);
    outted.println("abc");
    //PrintWriter(Writer out,boolean autoFlush)  
    //PrintWriter(OutputStream out, boolean autoFlush)
    //當autoFlush為true時,當調用println方法會自動清空緩沖區  s
    

    對象串行化

    串行化(Serialization):又稱序列化,將實現了Seriallizable接口的對象轉換成一個字節序列,並能夠在以后將這個字節序列完全恢復為原來的對象,后者又稱反序列化

串行化的目的:便於介質存儲和網絡傳輸

使用ObjectInputStream類和ObjectOutputStream類

關於byte char string

char與byte的區別: https://blog.csdn.net/luoweifu/article/details/7770588

一 char和string的區別https://blog.csdn.net/qauchangqingwei/article/details/80831797

1 char是表示的是字符,定義的時候用單引號,只能存儲一個字符。例如; char='d'. 而String表示的是字符串,定義的時候用雙引號,可以存儲一個或者多個字符。例如: String=“we are neuer”。 2 char是基本數據類型,而String是個類,屬於引用數據類型。String類可以調用方法,具有面向對象的特征。

關於書上 p227文件過濾器的例題講解: https://blog.csdn.net/lj_pyt/article/details/44830761

關於file類 http://c.biancheng.net/view/1133.html

File 類不具有從文件讀取信息和向文件寫入信息的功能,它僅描述文件本身的屬性。

( 注意:假設在 Windows 操作系統中有一文件 D:\javaspace\hello.java,在 Java 中使用的時候,其路徑的寫法應該為 D:/javaspace/hello.java 或者 D:\\javaspace\\hello.java。 )

File類提供了如下三種形式構造方法。使用任意一個構造方法都可以創建一個 File 對象,然后調用其提供的方法對文件進行操作

 

   directory = new File("Destination"); 
   if (!directory.exists()) {
   directory.mkdir(); // 如果沒有就新建
   }

IO流總結: https://www.cnblogs.com/QQ846300233/p/6046388.html

 

第十五章 Java網絡通信

  1. 為什么稱TCP是面向連接的可靠的協議

    [1] 確認和重傳機制:建立連接時三次握手同步雙方的“序列號 + 確認號 + 窗口大小信息”,是 確認重傳、流控的基礎 傳輸過程中,如果Checksum校驗失敗、丟包或延時,發送端重傳 [2] 數據排序 :TCP有專門的序列號SN字段,可提供數據re-order [3] 流量控制:窗口和計時器的使用。TCP窗口中會指明雙方能夠發送接收的最大數據量 [4] 擁塞控制

    TCP的擁塞控制由4個核心算法組成。

    “慢啟動”(Slow Start)

    “擁塞避免”(Congestion avoidance)

    “快速重傳 ”(Fast Retransmit)

    “快速恢復”(Fast Recovery)

  2. 關於這章對IO流的查漏補缺

    1.BufferedReader

    BufferedReader 是緩沖字符輸入流。它繼承於Reader。

    int      read()
    int      read(char[] buffer, int offset, int length)
    String   readLine()
    

    BufferReader的作用是為其它Reader提供緩沖功能。創建BufferReader時,我們會通過它的構造函數指定某個Reader為參數。 如果到達流末尾, 就返回null

    BufferedReader br = new BufferedReader("c:/test.txt");
    

    (BufferReader會將該Reader中的數據分批讀取,每次讀取一部分到緩沖中;操作完緩沖中的這部分數據之后,再從Reader中讀取下一部分的數據。 )

    br=new BufferedReader(new FileReader(fileName));
    String str = null;
     while((str = br.readLine()) != null){
     //System.out.println(str);//此時str就保存了一行字符串
    }
    

    書上的socket通信:代碼

    BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
    //BuferedReader內的參數要是字符流 而input streamReader內的參數要是字節流
    BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()))
    //套接字的getInput/OutputStream()方法返回的是字節流
    String s1 = sin.readline();
    String s2 = is.readline(); //input輸入流read 別人發過來de
    
  3. PrintStream

    PrintStream繼承了FilterOutputStream.是"裝飾類"的一種,所以屬於字節流體系中

    (與PrintStream相似的流PrintWriter繼承於Writer,屬於字符流體系中)

    為其他的輸出流添加功能.使它們能夠方便打印各種數據值的表示形式.此外,值得注意的是:

    與其他流不同的是,PrintStream流永遠不會拋出異常.因為做了try{}catch(){}會將異常捕獲,出現異常情況會在內部設置標識,通過checkError()獲取此標識. PrintStream流有自動刷新機制,例如當向PrintStream流中寫入一個字節數組后自動調用flush()方法.

    // 將“輸出流out”作為PrintStream的輸出流,不會自動flush,並且采用默認字符集
    // 所謂“自動flush”,就是每次執行print(), println(), write()函數,都會調用flush()函數;
    // 而“不自動flush”,則需要我們手動調用flush()接口。
    PrintStream(OutputStream out)
    // 將“輸出流out”作為PrintStream的輸出流,自動flush,並且采用默認字符集。
    PrintStream(OutputStream out, boolean autoFlush)
    

    PrintStream和DataOutputStream異同點

    相同點:都是繼承與FileOutputStream,用於包裝其它輸出流。

    不同點

    (01) PrintStream和DataOutputStream 都可以將數據格式化輸出;但它們在“輸出字符串”時的編碼不同。

    PrintStream是輸出時采用的是用戶指定的編碼(創建PrintStream時指定的),若沒有指定,則采用系統默認的字符編碼。而DataOutputStream則采用的是UTF-8。

    (02) 它們的寫入數據時的異常處理機制不同。

    DataOutputStream在通過write()向“輸出流”中寫入數據時,若產生IOException,會拋出。 ​ 而PrintStream在通過write()向“輸出流”中寫入數據時,若產生IOException,則會在write()中進行捕獲處理;並設置trouble標記(用於表示產生了異常)為true。用戶可以通過checkError()返回trouble值,從而檢查輸出流中是否產生了異常。

    (03) 構造函數不同

    DataOutputStream的構造函數只有一個:DataOutputStream(OutputStream out)。即它只支持以輸出流out作為“DataOutputStream的輸出流”。 ​ 而PrintStream的構造函數有許多:和DataOutputStream一樣,支持以輸出流out作為“PrintStream輸出流”的構造函數;還支持以“File對象”或者“String類型的文件名對象”的構造函數。 ​ 而且,在PrintStream的構造函數中,能“指定字符集”和“是否支持自動flush()操作”。

    (04) 目的不同

    DataOutputStream的作用是裝飾其它的輸出流,它和DataInputStream配合使用:允許應用程序以與機器無關的方式從底層輸入流中讀寫java數據類型。 ​ 而PrintStream的作用雖然也是裝飾其他輸出流,但是它的目的不是以與機器無關的方式從底層讀寫java數據類型;而是為其它輸出流提供打印各種數據值表示形式,使其它輸出流能方便的通過print(), println()或printf()等輸出各種格式的數據。

    書上的示例

    PrintStream os = new PrintStream(new BufferedOutputStream(socket.getOutputStream()))
    
  4. PrintWriter

    具有自動行刷新的緩沖字符輸出流,特點是可以按行寫出字符串,並且可以自動行刷新.

     

    在文件操作方面:

    PW支持兩個直接對文件寫操作的構造方法:

    PrintWriter(File f)傳文件名: PrintWriter pw = new PrintWriter("f://aaa.txt");

    PrintWriter(String s)傳路徑

    (1)print(String str):向文件寫入一個字符串。

    (2)print(char[] ch):向文件寫入一個字符數組。

    (3)print(char c):向文件寫入一個字符。

    (4)print(int i):向文件寫入一個int型值。

    PrintWriter os = new PrintWriter(socket.getOutputStream());
    os.println(line); os.flush();
    

     

     

     

     

  5.  

  6.  

     

 

 

第八章 Java常用類庫

  1. String

    為什么說String是不可變的:

    1. https://blog.csdn.net/goudu7509/article/details/81088090

    2. String s = "ABCabc";
      s = "123456";
      System.out.println("s = " + s); //123456
      
     
               

    s只是一個引用,s=“123456”; 它指向了一個具體的對象,當這句代碼執行過之后,又創建了一個新的對象“123456”, 而引用s重新指向了這個心的對象,原來的對象“ABCabc”還在內存中存在,

    不可變針對的是對象

    看源碼得到: 【value,offset和count這三個變量都是private的,沒有公共方法來修改這些值,所以在String類的外部無法修改String。也就是說一旦初始化就不能修改, 並且在String類的外部不能訪問這三個成員。此外,value,offset和count這三個變量都是final的, 也就是說在String類內部,一旦這三個值初始化了, 也不能被改變。所以可以認為String對象是不可變的了。】

    但是在String中,明明存在一些方法,調用他們可以得到改變后的值。這些方法包括substring, replace, replaceAll, toLowerCase

    ```java String a = "ABCabc"; System.out.println("a = " + a); a = a.replace('A', 'a');//!! System.out.println("a = " + a); // aBCabc //a的值看似改變了,其實也是同樣的誤區。再次說明, a只是一個引用, 不是真正的字符串對象,在調用a.replace('A', 'a')時, 方法內部創建了一個新的String對象,並把這個新對象重新賦給a

    ```java
    String ss = "123456";
    System.out.println("ss = " + ss);//ss = 123456
    ss.replace('1', '0');//!!!
    System.out.println("ss = " + ss);//ss = 123456
    //方法內部重新創建新的String對象,並且返回這個新的對象【上面那個例子就是重新返回的值又賦給a了 所以輸出變了】,原來的對象是不會被改變的
    

    【所以看新輸出到底是啥 就看TA有沒有被重新賦值】


    關於初始化:

    String 類有 11 種構造方法,這些方法提供不同的參數來初始化字符串,比如提供一個字符數組參數:

    char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'};
    String helloString = new String(helloArray); 
    

    String方法

    byte[] getBytes()
    使用平台的默認字符集將此 String 編碼為 byte 序列,並將結果存儲到一個新的 byte 數組中。
    byte[] getBytes(String charsetName)
    使用指定的字符集將此 String 編碼為 byte 序列,並將結果存儲到一個新的 byte 數組中。
    char[] toCharArray()
    將此字符串轉換為一個新的字符數組。
    String toString()
    返回此對象本身(它已經是一個字符串!)
    
    • 1、length() 方法是針對字符串來說的,要求一個字符串的長度就要用到它的length()方法;

    • 2、length 屬性是針對 Java 中的數組來說的,要求數組的長度可以用其 length 屬性;

    • 3、Java 中的 size() 方法是針對泛型集合說的, 如果想看這個泛型有多少個元素, 就調用此方法來查看

     

    String s1 = "abc";            // 常量池
    String s2 = new String("abc");     // 堆內存中
    System.out.println(s1==s2);        // false兩個對象的地址值不一樣。
    System.out.println(s1.equals(s2)); // true
    
    String str1 = "hello world";
    String str2 = new String("hello world");
    String str3 = "hello world";
    String str4 = new String("hello world");
    System.out.println(str1==str2);//false
    System.out.println(str1==str3);//true
    System.out.println(str2==str4);//false
    
    String str1 = "hello world"; 和 String str3 = "hello world"; 都在編譯期間生成了字面常量和符號引用,運行期間字面常量 "hello world" 被存儲在運行時常量池(當然只保存了一份)。通過這種方式來將 String 對象跟引用綁定的話,JVM 執行引擎會先在運行時常量池查找是否存在相同的字面常量,如果存在,則直接將引用指向已經存在的字面常量;否則在運行時常量池開辟一個空間來存儲該字面常量,並將引用指向該字面常量。 
    
    眾所周知,通過 new 關鍵字來生成對象是在堆區進行的,而在堆區進行對象生成的過程是不會去檢測該對象是否已經存在的。因此通過 new 來創建對象,創建出的一定是不同的對象,即使字符串的內容是相同的。
    

     

    String s1="a"+"b"+"c";
    String s2="abc";
    System.out.println(s1==s2);//true
    System.out.println(s1.equals(s2));//true
    //java 中常量優化機制,編譯時 s1 已經成為 abc 在常量池中查找創建,s2 不需要再創建。
    
    String s1="ab";
    String s2="abc";
    String s3=s1+"c";
    System.out.println(s3==s2);         // false
    System.out.println(s3.equals(s2));  // true
    //先在常量池中創建 ab ,地址指向 s1, 再創建 abc ,指向 s2。對於 s3,先創建StringBuilder(或 StringBuffer)對象,通過 append 連接得到 abc ,再調用 toString() 轉換得到的地址指向 s3。故 (s3==s2) 為 false。
    

    。。。。。所以 下面這個例子:

    String str1 = "HelloFlyapi";
    String str4 = "Hello";
    String str5 = "Flyapi";
    String str7 = str4 + str5;
    String str8 = "Hello"+ "Flyapi";
    System.out.println(str1 == str7);//false
    System.out.println(str1 == str8);//true 
    

    其中前三句變量存儲的是常量池中的引用地址。

    第四句執行時,JVM會在堆(heap)中創建一個以str4為基礎的一個StringBuilder對象,然后調用StringBuilder的append()方法完成與str5的合並,之后會調用toString()方法在堆(heap)中創建一個String對象,並把這個String對象的引用賦給str7。

     

    常見String面試題

    String str = new String(“abc”)創建了多少個實例?

    這個問題其實是不嚴謹的,but:

    解: 創建了兩個

    1、當加載類時,”abc”被創建並駐留在了字符創常量池中(如果先前加載中沒有創建駐留 過)。

    2、當執行此句時,因為”abc”對應的String實例已經存在於字符串常量池中,所以JVM會將此實例復制到會在堆(heap)中並返回引用地址。

    https://segmentfault.com/a/1190000009888357#comment-area 可以看一下 講的還不錯 部分存疑

    【常量池中一個對象。堆內存中一個對象。堆內存的對象是常量池中的副本。 】


     

    Java:String、StringBuffer 和 StringBuilder 的區別

    String:字符串常量,字符串長度不可變。Java中String 是immutable(不可變)的。用於存放字符的數組被聲明為final的,因此只能賦值一次,不可再更改。

    StringBuffer:字符串變量(Synchronized,即線程安全)。如果要頻繁對字符串內容進行修改,出於效率考慮最好使用 StringBuffer,如果想轉成 String 類型,可以調用 StringBuffer 的 toString() 方法。Java.lang.StringBuffer 線程安全的可變字符序列。在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。可將字符串緩沖區安全地用於多個線程。

    StringBuilder:字符串變量(非線程安全)。在內部 StringBuilder 對象被當作是一個包含字符序列的變長數組。

    基本原則:

    • 如果要操作少量的數據用 String ;

    • 單線程操作大量數據用StringBuilder ;

    • 多線程操作大量數據,用StringBuffer。

    1

    使用 StringBuffer 類則結果就不一樣了,每次結果都會對 StringBuffer 對象本身進行操作StringBuffer 對象的拼接,所以這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是以下的字符串對象生成中, String 效率是遠要比 StringBuffer 快的:

    String S1 = “This is only a” + “ simple” + “ test”;
    StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
    

    你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 居然速度上根本一點都不占優勢。其實這是 JVM 的一個把戲,在 JVM 眼里,這個 String S1 = “This is only a” + “ simple” + “test”; 其實就是: String S1 = “This is only a simple test”; 所以當然不需要太多的時間了。但大家這里要注意的是,如果你的字符串是來自另外的 String 對象的話,速度就沒那么快了,譬如: String S2 = “This is only a”; String S3 = “ simple”; String S4 = “ test”; String S1 = S2 +S3 + S4;

    這時候 JVM 會規規矩矩的按照原來的方式去做 //相加一次生成一個新對象

    關於這個執行時間https://blog.csdn.net/qq_37856300/article/details/84340288 后半截

    整個StringBuilder的append方法 不會重新生成新的StringBuilder對象

    StringBuilder的toString方法 : 方法直接new 一個String對象,將StringBuilder對象的value進行一個拷貝,重新生成一個對象

    public String toString(){
    return new String(value,0,count);
    }
    

     

    String、StringBuffer、StringBuilder 比較:【PPT上的】
    
    String、StringBuffer、StringBuilder相同點
    
    1、內部實現基於字符數組,封裝了對字符串處理的各種操作
    
    2、可自動檢測數組越界等運行時異常
    
    String、StringBuffer、StringBuilder不同點
    
    1、String內部實現基於常量字符數組,內容不可變; 
    
    ​StringBuffer、StringBuilder基於普通字符數組,數組 大小可根據  
    
    ​字符串的實際長度自動擴容,內容可變
    
    2、性能方面,對於字符串的處理,相對來說
    
    ​StringBuilder>StringBuffer>String
    
    3、StringBuffer線程安全;StringBuilder非線程安全
    

     

    java 泛型 https://blog.csdn.net/s10461/article/details/53941091

     

    Java toString()方法

    toString()方法返回反映這個對象的字符串

    因為toString方法是Object里面已經有了的方法,而所有類都是繼承Object,所以“所有對象都有這個方法”。

    它通常只是為了方便輸出,比如System.out.println(xx),括號里面的“xx”如果不是String類型的話,就自動調用xx的toString()方法

    var n = 17;    n.toString(2);//'10001'
    
    public static class A
    {
    public String toString()
    {
    return "this is A";
    }
    }
    public static void main(String[] args)
    {
    A obj = new A();
    System.out.println(obj);//this is A
    }
    

    值得注意的是, !!!!!若希望將StringBuffer在屏幕上顯示出來, 則必須首先調用toString方法把它變成字符串常量, 因為PrintStream的方法println()不接受StringBuffer類型的參數. 【StringBuffer轉String】

    StringBuffer MyStrBuff1 = new StringBuffer();
    MyStrBuff1.append("Hello, Guys!");
    System.out.println(MyStrBuff1.toString());
    
    String MyStr = new StringBuffer().append("hello").toString();
    MyStr = new StringBuffer().append(MyStr).append(" Guys!").toString();
    System.out.println(MyStr);//hello Guys!
    

     

  2. 了解字符串

    "將unicode字符集轉為本地字符集(如GB2312或GBK)的過程叫做編碼,反之叫做解碼"

    Unicode(統一碼、萬國碼、單一碼)是計算機科學領域里的一項業界標准,包括字符集、編碼方案等

    因為計算機只能處理數字,如果要處理文本,就必須先把文本轉換為數字才能處理。最早的計算機在設計時采用8個比特(bit)作為一個字節(byte),所以,一個字節能表示的最大的整數就是255(二進制11111111=十進制255),0 - 255被用來表示大小寫英文字母、數字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母A的編碼是65,小寫字母z的編碼是122。

    如果要表示中文,顯然一個字節是不夠的,至少需要兩個字節,而且還不能和ASCII編碼沖突,所以,中國制定了GB2312編碼,用來把中文編進去。

    類似的,日文和韓文等其他語言也有這個問題。為了統一所有文字的編碼,Unicode應運而生。Unicode把所有語言都統一到一套編碼里,這樣就不會再有亂碼問題了。

  3. 為什么String被設計成不可變性

    1.字符串常量池的需要

    2.String哈希碼的唯一性,可緩存

    3.String多線程安全

    4.String常作為參數傳遞(網絡,數據庫,安全性)

  4.  

     

    2,常見構造方法
    
    public String():空構造
    
    public String(byte[] bytes):把字節數組轉成字符串
    
    public String(byte[] bytes,int index,int length):把字節數組的一部分轉成字符串
    
    public String(char[] value):把字符數組轉成字符串
    
    public String(char[] value,int index,int count):把字符數組的一部分轉成字符串
    
    public String(String original):把字符串常量值轉成字符串
    

     

     

 

 java return this 返回當前對象的引用

https://blog.csdn.net/qq_38521014/article/details/90416718

https://zhidao.baidu.com/question/77602318.html

https://zhidao.baidu.com/question/617994061089104092.html

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM