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修饰
  1. Vector:线程安全的; ArrayList:线程不安全
  2. StringBuffer:线程安全的;StringBuilder线程不安全的
  3. Hashtable:线程安全的;HashMap线程不安全的

猜你喜欢

转载自blog.csdn.net/stack_xman/article/details/83625552
今日推荐