JavaSE基礎學習筆記及案例(一)IO流與多執行緒(上)
IO流
1. IO流知識點
IO流(字元輸入流FileReader) 位元組輸入流 FileInputStream
IO流(字元輸出流FileWriter) 位元組輸出流 FileOutputStream
字元緩衝區輸入流( BufferedReader)
位元組緩衝區輸入流BufferedInputStream
字元緩衝區輸出流( BufferedWriter)
位元組緩衝區輸出流BufferedOutputStream
2. 實戰案例
案例2.1 將鍵盤錄入拷貝到當前專案下的text.txt中,鍵盤錄入資料遇到quit時就退出
public class demo12 { /** * @param args * 將鍵盤錄入拷貝到當前專案下的text.txt中,鍵盤錄入資料遇到quit時就退出 * * 1.建立鍵盤錄入物件 * 2.建立輸出流物件,關聯text.txt檔案 * 3.定義無限迴圈 * 4.遇到quit退出 * 5.沒有quit就將內容寫出 * 6.關流 * @throws IOException */ public static void main(String[] args) throws IOException{ Scanner sc = new Scanner(System.in);//建立鍵盤錄入物件 //2.建立輸出流物件,關聯text.txt FileOutputStream fos = new FileOutputStream("text.txt"); //3.定義無限迴圈 while(true){ String nl = sc.nextLine();//將鍵盤錄入物件寫在line中 //4.遇到quit退出 if("quit".equals(nl)){ break; }else{//5.沒有quit就將內容寫出 fos.write(nl.getBytes());//字串必須轉換成位元組陣列 fos.write("\r\n".getBytes()); } fos.close(); } } }
結果:將111111.txt寫入到了text檔案中。。。。。。。。。。。。。。。
***案例2.2***模擬收費軟體試用次數
public class test { /** * @param args * 模擬試用版軟體,10次機會 * * 1.建立帶緩衝的輸入流物件,使用readLine方法,保證資料的原樣性 * 2.將讀到的字串轉換成int數; * 3.對int數進行判斷,如果大於0,將其--寫回去,如果不大於0,提示請購買正版 * 4.在if判斷將要--的結果列印,並將結果通過輸出流寫到檔案上 * @throws IOException */ public static void main(String[] args) throws IOException{ //1.建立帶緩衝的輸入流物件,使用readLine方法,保證資料的原樣性 BufferedReader br = new BufferedReader(new FileReader("config.txt")); //2將讀到的字串轉換成int型別 String line = br.readLine(); //將數字字串轉換成數字 int times = Integer.parseInt(line); //3.對int數進行判斷,如果大於0,將其--寫回去,如果不大於0,提示請購買正版 if(times>0){ System.out.println("您還有"+times--+"次機會"); FileWriter fw = new FileWriter("config.txt"); fw.write(times+""); fw.close(); }else{ System.out.println("已結束,請購買正版"); } //關閉流 br.close(); } }
案例3 IO流獲取文字字元出現次數
/** * @author * *1.建立帶緩衝的輸入流物件 *2.建立雙列集合物件 *3.將讀到的字元儲存在雙列集合中,儲存的時候做判斷,如果不包含這個鍵 * 就將鍵和1儲存,如果包含這個鍵,就將鍵和值加1儲存 *4.關閉輸入流 *5.建立輸出流 *6.遍歷集合,將集合中的內容寫到times.txt中, *7.關閉輸出流 */ public class demo1_Wrap { public static void main(String[] args) throws IOException{ //1.建立待緩衝的輸入流物件 BufferedReader br = new BufferedReader(new FileReader("aaa.txt")); //2.建立雙列集合 TreeMap<Character, Integer> tm = new TreeMap<>(); //3.判斷 int ch; while((ch=br.read())!=-1){ //向下強轉 char c = (char)ch; /*if(!tm.containsKey(c)){//如果集合不包含,就把字元加入,計數1次 tm.put(c, 1); }else{//如果集合已經包含該字元,則次數+1(次數通過鍵獲取值>>>tm.get(c)+1) tm.put(c, tm.get(c)+1); }*/ //三元運算子表示 tm.put(c, !tm.containsKey(c)? 1 :tm.get(c)+1); } //4.關流 br.close(); //5.建立輸出流物件 BufferedWriter bw = new BufferedWriter(new FileWriter("times.txt")); //6.遍歷集合,將集合內容寫到times.txt中 for (Character key : tm.keySet()) { bw.write(key+"="+tm.get(key));//寫出鍵和值 bw.newLine();//換行 } //7.關閉輸出流 bw.close(); } }
案例4 拷貝檔案
public class demo11 {
public static void main(String[] args) throws IOException{
//獲取檔案
File file = getFile();
//BufferedInputStream和BufferOutputStream拷貝
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));//讀取資料,建立輸入流物件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName()));//建立輸出流物件
int b;
while((b=bis.read())!=-1){
bos.write(b);
}
bis.close();
bos.close();
}
/**
* @return
* 定義一個方法獲取鍵盤錄入的檔案路徑(僅獲取檔案)
*/
public static File getFile(){
Scanner sc = new Scanner(System.in);
while(true){
String nc = sc.nextLine();//接收鍵盤錄入路徑
File file = new File(nc);//封裝成File物件,並對其進行判斷
if(!file.exists()){
System.out.println("檔案不存在");
}else if(file.isDirectory()){
System.out.println("不是檔案");
}else{
return file;
}
}
}
}
案例5 從鍵盤輸入一個資料夾路徑,列印該資料夾下的所有.java檔名
/*案例:從鍵盤輸入一個資料夾路徑,列印該資料夾下的所有.java檔名
*
*從鍵盤接受一個資料夾路徑
*1.如果資料夾不存在,提示
*2.如果輸入的不是資料夾,也提示
*3.如果是資料夾路徑,則直接返回
*列印該資料夾下的以java結尾的檔案
*4.獲取該資料夾路徑下所有的檔案和資料夾,儲存在File陣列中
*5.遍歷陣列,對每一個檔案或者資料夾做判斷
*6.如果是檔案,且字尾是.java的>>列印
*7.如果是資料夾就遞迴呼叫
*/
public class demo4_File {
public static void main(String[] args){
File dir = getDir();//獲取資料夾路徑
getJava(dir);
}
/**
* @return
* 獲取資料夾路徑
*/
public static File getDir(){
Scanner sc = new Scanner(System.in);
System.out.println("請輸入路徑:");
while(true){
String line = sc.nextLine();//將路徑儲存
//封裝路徑
File dir = new File(line);
if(!dir.exists()){
System.out.println("您錄入的文降價不存在");
}else if(dir.isFile()){
System.out.println("您錄入的是檔案路徑,請重新錄入");
}else{
return dir;//返回資料夾路徑
}
}
}
/**
* 獲取資料夾下的所有java檔案
*/
public static void getJava(File dir){
File[] files = dir.listFiles();//獲取資料夾下的所有檔案和資料夾,儲存在files中
//遍歷陣列,對每一個檔案或者資料夾列印
for (File file : files) {
if(file.isFile()&&file.getName().endsWith(".java")){
System.out.println(file);
}else{
//遞迴呼叫
if(file.isDirectory()){
getJava(file);//如果file是一個資料夾,那麼就把這個資料夾在進行呼叫
}
}
}
}
}
遞迴呼叫練習
案例一 運用遞迴計算資料夾大小
/**
* @author ZHENG
* 計算資料夾大小
*/
public class test2 {
public static void main(String[] args){
File dir = getDir();
long l = getDirLength(dir);
System.out.println(l);
}
/**
* 獲取資料夾
* @return
*/
public static File getDir(){
Scanner sc = new Scanner(System.in);
System.out.println(">>請輸入資料夾路徑:");
while(true){
String nl = sc.nextLine();
//接受的資料封裝成Flie物件
File dir = new File(nl);
if(!dir.exists()){
System.out.println("資料夾不存在");
}else if(dir.isFile()){
System.out.println("不是資料夾");
}else{
return dir;//返回資料夾
}
}
}
/**
* @param dir
* @return
* 計算資料夾大小
*/
public static long getDirLength(File dir){
int len = 0;
//獲取該資料夾下的所有檔案,並進行遍歷
File[] files = dir.listFiles();
for (File file : files) {
if(file.isFile()){
len += file.length();
}else{
//否則遞迴呼叫自身,直到找到檔案為止
len+=getDirLength(file);
}
}
return len;
}
}
案例2刪除空的資料夾
public class test3 {
public static void main(String[] args){
File dir = test2.getDir();//獲取資料夾
deleteFile(dir);
}
public static void deleteFile(File dir){
File[] files = dir.listFiles();
for (File file : files) {
if(file.isFile()){
file.delete();
}else{//如果是資料夾遞迴呼叫
deleteFile(file);
}
}
dir.delete();//刪除空資料夾
}
}
案例3將資料夾中的所有檔案按照層級列印
public class test4{
public static void main(String[] args){
//依據test2裡的getDir方法獲取資料夾路徑
File dir = test2.getDir();
printLev(dir,0);
}
private static void printLev(File dir,int lev){
//將資料夾中的所有檔案按照層級列印
File[] files = dir.listFiles();
for(File file:files){
//無論是檔案還是資料夾都需要直接列印
system.out.println(file);
//如果是資料夾,遞迴呼叫
if(file.isDirectory()){
printLev(file,lev+1);
}
}
}
}
多執行緒
1.多執行緒之多執行緒的兩種實現方式
1.1繼承Thread方式開啟執行緒
public class demo1_Thread {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();//開啟執行緒
for (int i = 0; i < 1000; i++) {
System.out.println("主方法");
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("aaaa");
}
}
}
1.2實現Runnable介面方式開啟執行緒
public class demo2_Thread {
public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt).start();//Runnable介面實現開啟執行緒
for (int i = 0; i < 1000; i++) {
System.out.println("主方法");
}
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("aaaaaaaa");
}
}
}
2.多執行緒(兩種實現方式的區別)
檢視原始碼的區別:
1.繼承Thread:由於子類重寫了Thread類的run()方法,當呼叫start()時,直接找子類的run()方法;
2.實現Runable:建構函式中傳入了Runable的引用,成員變數記住了它,start()呼叫run()方法時內部判斷成員變數Runable的引用是否為空,不為空編譯時看的是Runable的run()方法,執行時執行的是子類的Run()方法。
繼承Thread
好處:可直接使用Thread類中的方法,程式碼簡單;
弊端:若有父類,則不能使用這種方法;
實現Runable介面
好處:介面可以多實現
弊端:不能直接使用Thread中的方法,先獲取執行緒物件後,才可以得到Thread方法,程式碼複雜
3.多執行緒之匿名內部類實現執行緒的兩種方式
好處:不需要繼承,也不需要實現介面,程式碼簡潔
匿名內部類之實現Runnable介面方式開啟執行緒方式:需要把Runnable子類物件當做引數傳給Thread的子類物件方可開啟執行緒。
/**
1. @author ZHENG
2. 匿名內部類實現多執行緒
*/
public class demo3_Thread {
public static void main(String[] args){
//第一種匿名內部類方式:繼承Thread類
new Thread(){//1.繼承Thread類
//2.重寫Run方法
public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println("aaaaaaa");
}
}
}.start();//3.開啟執行緒
//第二種匿名內部類方式:實現Runnable介面
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("bbbbbb");
}
}
}).start();//Runnable方式開啟執行緒
}
}
4.多執行緒之獲取執行緒名字與設定執行緒的名字
------獲取名字:getName()
------設定名字: setName()
/**
* @author ZHENG
*
* 獲取與設定執行緒名字的三種方式
*
*/
public class demo4 {
public static void main(String[] args){
/* 方式1 */
new Thread("執行緒名字"){
public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println(getName()+"aaaaaa");
}
}
}.start();
/* 方式2 */
new Thread(){
public void run(){
this.setName("設定執行緒名字");
for (int i = 0; i < 1000; i++) {
System.out.println(getName()+"bbbbb");
}
}
}.start();
/* 方式3 父類引用指向子類物件*/
Thread t1 = new Thread("執行緒名字"){
public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println(getName()+"aaaaaa");
}
}
};
t1.setName("新的名字1");
t1.start();
Thread t2 = new Thread(){
public void run(){
this.setName("設定執行緒名字");
for (int i = 0; i < 1000; i++) {
System.out.println(getName()+"bbbbb");
}
}
};
t2.setName("新的名字2");
t2.start();
}
}
5.多執行緒之獲取當前執行緒物件
-----Thread.currentThread();
------Thread.currentThread().getName();//獲取當前執行緒的名字
以及如何設定與獲取主執行緒的名字
public class demo6 {
public static void main(String[] args){
new Thread(){
public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println(getName()+"aaaaaaaa");
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+"bbbbbb");
}
}
}).start();
//獲取主執行緒的名字
Thread.currentThread().setName("主執行緒");
System.out.println(Thread.currentThread().getName());
}
}
6.多執行緒之執行緒休眠
----Thread.sleep(1000)//休眠1s
運用休眠實現倒計時功能
public class demo7 {
public static void main(String[] args){
for (int i=5; i>=0; i--) {
if(i==0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("已通知");
break;
}else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("倒計時"+i+"秒");
}
}
}
}
7.多執行緒之守護執行緒
----setDaemon():設定一個守護執行緒,該執行緒不會單獨執行,當非守護執行緒結束後,該執行緒自動停止。
守護執行緒=======“車馬相”等;
非守護執行緒=====“帥”
public class demo8_Daemon {
public static void main(String[] args){
//將t2設定為守護執行緒:當t1(帥)掛掉,t2(車馬相等)自動掛掉,但是由於緩衝的原因
//t2不會立刻停止,但執行次數一定小於50次
Thread t1 = new Thread(){
public void run(){
for (int i = 0; i < 2; i++) {//執行2次
System.out.println(getName()+"...aaaa");
}
}
};
Thread t2 = new Thread(){//執行50次
public void run(){
for (int i = 0; i < 50; i++) {
System.out.println(getName()+"...bbb");
}
}
};
t2.setDaemon(true);//將t2設定為守護執行緒
t1.start();
t2.start();
}
}
8.多執行緒之加入執行緒
-------join():當前執行緒暫停,等待其他執行緒結束後再執行;
-------join(1000)等待1s後繼續交叉執行
public class demo9_join {
public static void main(String[] args){
final Thread t1 = new Thread(){
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(getName()+"aaaaa");
}
}
};
Thread t2 = new Thread(){
public void run(){
for (int i = 0; i < 10; i++) {
try {
t1.join(1);//插隊指定的時間,過了時間,兩條執行緒交替執行,1ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"bbbbbbb");
}
}
};
t1.start();
t2.start();
}
}
9.多執行緒之設定執行緒的優先順序
---------setPriority();
t1.setPriority(Thread.MIN_PRIORITY);//將t1設定成最小的執行緒優先順序
t2.setPriority(Thread.MAX_PRIORITY);//將t2設定成最大的執行緒優先順序,即保證t2執行緒首先執行完
/**
* @author ZHENG
* 設定執行緒優先順序
*/
public class demo10_Priority {
public static void main(String[] args){
Thread t1 = new Thread(){
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"aaaaaaaa");
}
}
};
Thread t2 = new Thread(){
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"bbbbbbbb");
}
}
};
/*t1.setPriority(1);
t2.setPriority(10);*/
t1.setPriority(Thread.MIN_PRIORITY);//將t1設定成最小的執行緒優先順序
t2.setPriority(Thread.MAX_PRIORITY);//將t2設定成最大的執行緒優先順序
t1.start();
t2.start();
}
}
10.多執行緒之同步程式碼塊——同步方法
-----synchronized關鍵字加上一個鎖物件來定義一段程式碼,叫做同步程式碼塊
多個同步程式碼塊如果使用相同的鎖物件,即是同步的;
鎖物件可以使用任意物件,隨便建立一個物件也可以,但是不能使用匿名物件
例子:
class Demo{
}
class Print{
Demo d = new Demo();
public void print1(){
synchronized(d){
}
}
}
1.非靜態的同步方法鎖物件是什麼?--------this
即synchronized(this){
}
2.靜態方法的鎖物件是-------------Print.class物件
即synchronized(Print.class){
}
public class demo10 {
public static void main(String[] args){
final Print p = new Print();
new Thread(){
public void run(){
while(true){
p.print1();
}
}
}.start();
new Thread(){
public void run(){
while(true){
p.print2();
}
}
}.start();
}
}
class Print{
public static synchronized void print1(){
System.out.print("1");
System.out.print("2");
System.out.print("3");
System.out.print("4");
System.out.println();
}
public static void print2(){
synchronized (Print.class) {
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("d");
System.out.println();
}
}
}
11.多執行緒之執行緒安全問題----多執行緒實現4個窗口出售100張火車票
/**
* @author ZHENG
* 多執行緒實現4個窗口出售100張火車票
*/
public class demo12 {
public static void main(String[] args){
Tickets t1 = new Tickets();
t1.setName("1號視窗");
t1.start();
Tickets t2 = new Tickets();
t2.setName("2號視窗");
t2.start();
Tickets t3 = new Tickets();
t3.setName("3號視窗");
t3.start();
Tickets t4 = new Tickets();
t4.setName("4號視窗");
t4.start();
}
}
class Tickets extends Thread{
//定義初始化100張火車票(定義成靜態的代表4個執行緒共享100張票,即4個視窗一共出售100張票)
private static int tickets = 100;
public void run(){
while(true){
synchronized (Tickets.class) {
if(tickets==0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"票已售完");
break;
}else{
try {
Thread.sleep(10);//如果有大量資料如10w資料如果不睡眠則無法全部加載出來
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【"+getName()+"】:"+"已售出"+tickets--+"號票");
}
}
}
}
}
12.多執行緒之死鎖
避免死鎖:不要巢狀同步程式碼塊
13多執行緒之執行緒安全問題
通過檢視原始碼可以得出:執行緒安全的都是有synchronized修飾的類
- 以Hashtable為例通過ctrl+o找到put方法可以看出Hashtable由synchronized修飾
- Vector:執行緒安全的; ArrayList:執行緒不安全
- StringBuffer:執行緒安全的;StringBuilder執行緒不安全的
- Hashtable:執行緒安全的;HashMap執行緒不安全的