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线程不安全的