第四章
-
在 Java 中,同一個類文件,僅可存在於一個 public 修飾類,且該 .java 文件要與public修飾類同名,否則將會報
-
遞歸的本質就是用壓棧與出棧操作 :
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) 然后依次進行。*/當遞歸調用時每次調用自己時可以看做是壓棧過程,當遞歸條件滿足結束時,遞歸一級一級的返回時可以看做是出棧的過程。
-
Java 中修飾類中屬性、方法修飾符:public、private、protected、default (默認)
修飾符是有四種的,所以在類里,如果不加就是默認類型[ 使用:僅在同一包內可見 ]
-
final修飾符有什么作用?
final是java中的關鍵字,可以修飾類、方法和變量
被final修飾的類不可以被繼承
被final修飾的變量最多僅能賦值一次,且不能被改變。
被final修飾的方法不能被重寫。
-
構造函數
構造器不能是abstract, static, final, native, strictfp, 或者synchronized的
構造器不是通過繼承得到的,所以沒有必要把它聲明為final的。
構造器總是關聯一個對象而被調用,所以把它聲明為static是沒有意義的(每當創建一個對象構造函數自動被調用)
構造方法沒有函數返回值,甚至連void類型也不是。
-
假設沒有static關鍵字,那意味着需要用生成一個實例后才可以調用這個Main方法,而Main方法是程序入口點,你沒有進入Main方法,自然無法生成一個實例,既然沒有實例,那就無法調用Main函數,豈不矛盾?所以Main函數被設置為static.
不能在main方法中打印this關鍵字的信息,這時想起了之前的知識,不能在靜態方法中調用this。理由很簡單,this表示“這個對象”,也就是聲明一個類的對象,然而靜態方法是不屬於某一個特定對象而是屬於這個類的。
-
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.
-
垃圾回收 finalize
在Java中,對象什么時候可以被垃圾回收?
-
當一個對象到GC Roots不可達時,在下一個垃圾回收周期中嘗試回收該對象,如果該對象重寫了finalize()方法,並在這個方法中成功自救(將自身賦予某個引用),那么這個對象不會被回收。但如果這個對象沒有重寫finalize()方法或者已經執行過這個方法,也自救失敗,該對象將會被回收。
-
當沒有任何對象的引用指向該對象時+在下次垃圾回收周期來到時=>對象才會被回收
-
如何證明一個垃圾對象被釋放了:
-
-
關於類中變量的初始化 dadada
Java盡力保證:所有變量在使用前都得到恰當的初始化。
-
這個的意義是什么
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.
-
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 不同於 父類聲明子類變量這個事情
-
對象實例化過程 書本p106 博客
-
抽象類
抽象類的使用原則如下: (1)抽象方法必須為public或者protected(因為如果為private,則不能被子類繼承,子類便無法實現該方法),缺省情況下默認為public; (2)抽象類不能直接實例化,需要依靠子類采用向上轉型的方式處理; (3)抽象類必須有子類,使用extends繼承,一個子類只能繼承一個抽象類; (4)子類(如果不是抽象類)則必須覆寫抽象類之中的全部抽象方法(如果子類沒有實現父類的抽象方法,則必須將子類也定義為為abstract類。);
抽象類可以沒有抽象方法和抽象域變量。。。
如果一個類要被聲明為static的,只有一種情況,就是靜態內部類
-
接口定義用關鍵字interface,而不是用class,interface前的修飾符要么為public,要么為缺省
接口中的字段(域)的值存儲在該接口的靜態存儲區域內,使用接口名.字段或實現類.字段均可訪問 [因為接口的域變量都是 public static final ]【在實現類里就相當於靜態變量了】
一個接口可以繼承多個接口,但接口不能繼承類 [類當然能實現接口,..實現類]
接口有什么用?
-
實現多重繼承
-
接口是一種標准,使用者按照接口使用,實驗者按照接口實現,當實現者內部發生變化時,只要接口不變,使用者就不必更改代碼。
-
擴展性強
-
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):敲擊鍵盤,發生在按鍵按下后,按鍵放開前。-
什么是數據隱藏?如何證明子類對父類同名方法進行重新定義,只能是方法的覆蓋,而不是方法的隱藏: 在子類對父類的繼承中,如果子類的成員變量和父類的成員變量同名,此時稱為子類隱藏(override)了父類的成員變量
變量是隱藏,父類引用(=子類對象之后).變量還是父類的變量
而方法不是 此時父類引用.方法就只還是子類的方法了 所以是直接覆蓋掉了
1、若子類覆蓋了某方法,則父類引用調用子類重新定義的新方法[!!!!!!]
3、若子類覆蓋了某屬性,但父類引用仍調用父類本身的舊屬性[!!!!!!!]
-
第九章 線程
-
什么是進程,什么是線程?
進程:是並發執行的程序在執行過程中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。
線程:是進程的一個執行單元,是進程內科調度實體。比進程更小的獨立運行的基本單位。線程也被稱為輕量級進程。
一個程序至少一個進程,一個進程至少一個線程。
進程線程的區別
1、地址空間:同一進程的線程共享本進程的地址空間,而進程之間則是獨立的地址空間。
2、資源擁有:同一進程內的線程共享本進程的資源,但是進程之間的資源是獨立的。
3、一個進程崩潰后,在保護模式下不會對其他進程產生影響,但是一個線程崩潰整個進程都死掉。所以多進程要比多線程健壯。
4、進程切換時,消耗的資源大,效率高。所以涉及到頻繁的切換時,使用線程要好於進程。同樣如果要求同時進行並且又要共享某些變量的並發操作,只能用線程不能用進程。
5、執行過程:每個獨立的進程程有一個程序運行的入口、順序執行序列和程序入口。但是線程不能獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
6、線程是處理器調度的基本單位,但是進程不是。[ 進程是資源分配的最小單位,線程是程序執行的最小單位 ]
7、兩者均可並發執行。
-
通過調用Thread類的start()方法來啟動一個線程
每個線程都是通過某個特定Thread對象所對應的方法run() 來完成其操作的,方法run()稱為線程體
-
start 和 run 方法解釋:
1) start: 用start方法來啟動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。通過調用Thread類的start()方法來啟動一個線程,這時此線程處於就緒(可運行)狀態,並沒有運行,一旦得到cpu時間片,就開始執行run()方法,這里方法 run()稱為線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。 2) run: 如果直接用Run方法,書p153 這只是調用一個方法而已,程序中依然只有主線程--這一個線程,其程序執行路徑還是只有一條,這樣就沒有達到寫線程的目的。
-
-
先調用start后調用run,這么麻煩,為了不直接調用run?就是為了實現多線程的優點,沒這個start不行。
1.start()方法來啟動線程,真正實現了多線程運行。這時無需等待run方法體代碼執行完畢,可以直接繼續執行下面的代碼; 2.run()方法當作普通方法的方式調用。(程序還是要順序執行,要等待run方法體執行完畢后,才可繼續執行下面的代碼; 程序中只有主線程——這一個線程, 其程序執行路徑還是只有一條, 這樣就沒有達到寫線程的目的。)
-
synchronized 寫的挺好的
如果程序是單線程的,就不必擔心此線程在執行時被其他線程“打擾”,就像在現實世界中,在一段時間內如果只能完成一件事情,不用擔心做這件事情被其他事情打擾。但是,如果程序中同時使用多線程,好比現實中的“兩個人同時通過一扇門”,這時就需要控制,否則容易引起阻塞。
為了處理這種共享資源競爭,可以使用同步機制。所謂同步機制,指的是兩個線程同時作用在一個對象上,應該保持對象數據的統一性和整體性。Java提供 synchronized 關鍵字,為防止資源沖突提供了內置支持。共享資源一般是文件、輸入/輸出端口或打印機。
當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個線程執行該段代碼。
一、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以后才e3能執行該代碼塊。
二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
-
線程是什么?進程是什么?二者有什么區別和聯系?
線程是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流
-
流從流動方向上看:一般分為輸入流和輸出流
•輸入流:如System.in是一個InputStream類型輸入流
•輸出流:如System.out 是一個PrintStream類型輸出流
-
從鍵盤輸入字符:
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,如果流的末尾已到達 如果到達流末尾, 就返回nulljava 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網絡通信
-
為什么稱TCP是面向連接的可靠的協議
[1] 確認和重傳機制:建立連接時三次握手同步雙方的“序列號 + 確認號 + 窗口大小信息”,是 確認重傳、流控的基礎 傳輸過程中,如果Checksum校驗失敗、丟包或延時,發送端重傳 [2] 數據排序 :TCP有專門的序列號SN字段,可提供數據re-order [3] 流量控制:窗口和計時器的使用。TCP窗口中會指明雙方能夠發送接收的最大數據量 [4] 擁塞控制
TCP的擁塞控制由4個核心算法組成。
“慢啟動”(Slow Start)
“擁塞避免”(Congestion avoidance)
“快速重傳 ”(Fast Retransmit)
“快速恢復”(Fast Recovery)
-
關於這章對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
-
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()))
-
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();
-
-
第八章 Java常用類庫
-
String
為什么說String是不可變的:
-
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)); // trueString 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);//falseString 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。

使用 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! -
了解字符串
"將unicode字符集轉為本地字符集(如GB2312或GBK)的過程叫做編碼,反之叫做解碼"
Unicode(統一碼、萬國碼、單一碼)是計算機科學領域里的一項業界標准,包括字符集、編碼方案等
因為計算機只能處理數字,如果要處理文本,就必須先把文本轉換為數字才能處理。最早的計算機在設計時采用8個比特(bit)作為一個字節(byte),所以,一個字節能表示的最大的整數就是255(二進制11111111=十進制255),0 - 255被用來表示大小寫英文字母、數字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母A的編碼是65,小寫字母z的編碼是122。
如果要表示中文,顯然一個字節是不夠的,至少需要兩個字節,而且還不能和ASCII編碼沖突,所以,中國制定了GB2312編碼,用來把中文編進去。
類似的,日文和韓文等其他語言也有這個問題。為了統一所有文字的編碼,Unicode應運而生。Unicode把所有語言都統一到一套編碼里,這樣就不會再有亂碼問題了。
-
為什么String被設計成不可變性
1.字符串常量池的需要
2.String哈希碼的唯一性,可緩存
3.String多線程安全
4.String常作為參數傳遞(網絡,數據庫,安全性)
-
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
