当前位置:   article > 正文

Java的基本学习(六)——多线程与输入输出_标准输入输出 多线程

标准输入输出 多线程

前言:

接着前面的内容,我们来继续学习Java,我们来了解一下多线程与输入输出

 

 

 

 


多线程

Java中的多线程技术:

每个Java程序都有一个缺省的主线程。对于Application,主线程是main()方法执行的线索。对于Applet,主线程是浏览器加载并执行Java小程序。要实现多线程,必须在主线程中创建新的线程对象。

Java语言使用Thread类及其子类的对象来表示线程。新建的线程在它的一个完整的生命周期中通常要经历新生、就绪、运行、阻塞和死亡等五种状态。

  • 新生状态:

当用new和某线程的构造方法创建一个新对象后,这个线程对象处于新生状态,此时它有了自己响应的内存空间,并已经被初始化。处于该状态的线程可以通过调用start()方法进入就绪状态。

  • 就绪状态

处于就绪状态的线程已经具备了运行的条件,但尚未得到CPU资源,因而它将进入线程队列排队,等待系统为它分配CPU。一旦获得了CPU资源,该线程就进入运行状态,并自动调用自己的run()方法。此时,它脱离创建它的主线程,独立开始了自己的线程

  • 运行状态

进入运行状态的线程执行自己的run方法中的代码。遇到下列情况将终止run方法的执行:

  1. 终止操作。调用当前线程的stop方法或destroy方法进入死亡状态
  2. 等待操作。调用当前线程的join(millis)方法或wait(millis)方法进入阻塞状态,当线程进入阻塞状态,在millis毫秒可由其他线程调用notify或notifyAll方法将其唤醒,进入就绪状态。在millis内若不被唤醒,则需等待到当前线程结束。
  3. 睡眠操作。调用sleep(millis)方法来实现。当前线程停止执行后,会处于阻塞状态,睡眠millis(毫秒)之后重新进入就绪状态
  4. 挂起操作。通过调用suspend来实现。将当前线程挂起,进入阻塞状态,之后当其他线程调用当前线程的resume方法后,才使其进入就绪状态。
  5. 退让操作。通过调用yield方法来实现。当前线程放弃执行,进入就绪状态
  6. 当前线程要求I/O时,则进入阻塞状态
  7. 若分配给当前线程的时间片用完,则当前线程进入就绪状态,若当前线程的run方法执行完,则线程进入死亡状态
  • 阻塞状态

一个正在执行的线程在某些特殊情况下,如执行了suspend、join、sleep方法,或等待I/O设备的使用权,那么它将让出CPU并终止自己的执行,进入阻塞状态。阻塞时他就不能进入就绪队列,只有当引起阻塞状态的原因被消除时,线程才可以转入就绪状态,重新进入到线程队列中排队等待CPU资源,以便从原终止处开始继续进行

  • 死亡状态

处于死亡状态的线程将永远不再执行。线程死亡有两个原因:一是正常运行的线程完成了它的全部工作。二是线程被提前强制性地终止了。例如,通过执行stop或destroy方法来终止线程。

 

 

 

 

通过集成Thread类方式创建线程:

示例:

  1. package 测试;
  2. import java.util.Calendar;
  3. class test extends Thread{
  4. int pauseTime;
  5. String name;
  6. public test(int hTime,String hStr) {
  7. pauseTime=hTime;
  8. name=hStr;
  9. }
  10. public void run() {
  11. //Calendar是Java系统提供的日期时间类的类型标识符
  12. Calendar now;
  13. int hour,minute,second;
  14. for(int i=1;i<10;i++) {
  15. try {
  16. //得到系统时间
  17. now=Calendar.getInstance();
  18. //取小时值
  19. hour=now.get(Calendar.HOUR);
  20. //取分值
  21. minute=now.get(Calendar.MINUTE);
  22. //取秒值
  23. second=now.get(Calendar.SECOND);
  24. System.out.println(" "+name+"时间:"+hour+":"+minute+":"+second);
  25. Thread.sleep(pauseTime);
  26. }
  27. catch(Exception e){
  28. System.out.println(name+":"+"线程错误"+e);
  29. }
  30. }
  31. }
  32. static public void main(String[] args) {
  33. //线程A执行一次后睡眠2000毫秒
  34. test myThread1=new test(2000,"线程A");
  35. myThread1.start();
  36. //线程B执行一次后睡眠1000毫秒
  37. test myThread2=new test(1000,"线程B");
  38. myThread2.start();
  39. }
  40. }

通过实现Runnable接口方式创建线程:

创建线程对象的另一个途径就是实现Runnable接口,而Runnable接口只有一个方法run(),用户新建线程的操作就由这个方法来决定。run()方法必须由实现此接口的类来实现。定义好run()方法之后,当用户程序需要建立新线程时,只要以这个实现了run()方法的类为参数创建系统类Thread的对象,就可以把用户实现的run方法继承过来。

示例:通过创建两个线程实现“Java Now!”与矩形框在屏幕上呈相反方向不停走动。

这个示例需要三个文件:CString.java、CSquare.java、test.java

  1. #test.java
  2. package 测试;
  3. import java.awt.Color;
  4. import java.awt.BorderLayout;
  5. import java.awt.Container;
  6. import java.awt.Dimension;
  7. import javax.swing.JApplet;
  8. public class test extends JApplet{
  9. @Override
  10. public void init() {
  11. //得到窗口容器对象
  12. Container cp=getContentPane();
  13. //创建JPanel对象
  14. CString pa=new CString();
  15. //创建JPanel对象
  16. CSquare pa1=new CSquare();
  17. //设置pa对象的尺寸
  18. pa.setPreferredSize(new Dimension(300,150));
  19. //设置pa的对象背景颜色
  20. pa.setBackground(Color.cyan);
  21. pa1.setPreferredSize(new Dimension(300,150));
  22. //设置pa1的对象背景颜色
  23. pa1.setBackground(Color.cyan);
  24. //cp容器的布局为BorderLayout,添加pa及pa1的对象到cp容器中
  25. cp.add(pa,BorderLayout.NORTH);
  26. cp.add(pa1,BorderLayout.SOUTH);
  27. }
  28. }
  29. #CString.java
  30. package 测试;
  31. import java.awt.Font;
  32. import java.awt.Graphics;
  33. import java.awt.Graphics2D;
  34. import javax.swing.JPanel;
  35. public class CString extends JPanel implements Runnable{
  36. int x=10,y=30;
  37. //创建字符串对象
  38. String Message="Java Now!";
  39. //创建字体对象
  40. Font f=new Font("TimesRoman",Font.BOLD,24);
  41. //以这个实现了run()方法的类为参数创建系统类Thread的对象
  42. //就可以把Runnable的方法继承过来
  43. Thread th1=new Thread(this);
  44. public CString() {
  45. start();
  46. }
  47. private void start() {
  48. th1.start();
  49. }
  50. @Override
  51. public void run() {
  52. while(true) {
  53. x=x-40;
  54. if(x<=0) {
  55. x=300;
  56. }
  57. //repaint()方法调用paint()方法重画字符串
  58. repaint();
  59. try {
  60. Thread.sleep(500);
  61. }
  62. catch(InterruptedException e){
  63. }
  64. }
  65. }
  66. public void paint(Graphics g) {
  67. super.paint(g);
  68. Graphics2D g2=(Graphics2D)g;
  69. g2.setFont(f);
  70. g2.drawString(Message, x, y);
  71. }
  72. }
  73. #CSquare.java
  74. package 测试;
  75. import java.awt.Graphics;
  76. import java.awt.Graphics2D;
  77. import java.awt.geom.Rectangle2D;
  78. import javax.swing.JPanel;
  79. public class CSquare extends JPanel implements Runnable{
  80. int x1,y1,w1,h1;
  81. Thread th2=new Thread(this);
  82. public CSquare() {
  83. x1=5;
  84. y1=100;
  85. w1=40;
  86. h1=40;
  87. start();
  88. }
  89. void start() {
  90. th2.start();
  91. }
  92. @Override
  93. public void run() {
  94. while(true) {
  95. x1=x1+45;
  96. if(x1>=250)
  97. x1=0;
  98. //repaint方法调用paint()方法重新画矩阵框
  99. repaint();
  100. try{
  101. //线程二睡眠500毫秒
  102. Thread.sleep(500);
  103. }
  104. catch(InterruptedException e) {
  105. }
  106. }
  107. }
  108. public void paint(Graphics g) {
  109. super.paint(g);
  110. Graphics2D g2=(Graphics2D)g;
  111. Rectangle2D.Double rec1=new Rectangle2D.Double(x1,y1,w1,h1);
  112. g2.draw(rec1);
  113. }
  114. }

多线程的管理:

在单CPU计算机上运行多线程程序,或者当线程数多于处理的数目时,势必存在多个线程争用CPU的情况,这时需要提供一种机制来合理的分配CPU,使多个线程有条不紊、无不干扰的工作,这种机制称为调度。在Java运行系统中,由线程调度器对线程按优先级进行调度。线程调度器中写好了相应的调度算法,当有多个线程在同一时刻处于就绪状态时,线程调度器就会选择优先级最高的线程运行。

Java的线程调度算法可分为两种:优先抢占式调度,轮转调度。

线程优先级:

在Java系统中,运行的每个线程都有优先级(一个1~10的正整数,数值越大越优先)。未设定优先级的线程取缺省值5。Java线程的优先级设置遵从下述原则:

  • 线程创建时,子线程继承父线程的优先级
  • 线程创建时,可在程序中通过setPriority()方法改变线程的优先级
  • 线程的优先级是1~10之间的正整数,并用标识符常量MIN_PRIORITY表示优先级为1,用NORM_PRIORITY表示优先级为5,用MAX_PRIORITY表示优先级为10。其他级别的优先级既可以直接用1~10之间的正整数来设置,也可以在标识符常量的基础上加一个常数,例如setPriority(Thread.NORM_PRIORITY+3) 这个值即代表8。

示例:

  1. package 测试;
  2. class test{
  3. public static void main(String args[]) {
  4. //创建A线程
  5. Thread First=new MyThread("A");
  6. //A线程优先级为1
  7. First.setPriority(Thread.MIN_PRIORITY);
  8. Thread Second=new MyThread("B");
  9. Second.setPriority(Thread.NORM_PRIORITY+1);
  10. Thread Third=new MyThread("C");
  11. Third.setPriority(Thread.MAX_PRIORITY);
  12. First.start();
  13. Second.start();
  14. Third.start();
  15. }
  16. }
  17. class MyThread extends Thread{
  18. String message;
  19. MyThread(String message){
  20. this.message=message;
  21. }
  22. public void run() {
  23. for(int i=0;i<2;i++) {
  24. System.out.println(message+" "+getPriority());
  25. }
  26. }
  27. }

输出:

  1. C 10
  2. C 10
  3. A 1
  4. B 6
  5. B 6
  6. A 1

线程同步:

由于Java支持多线程,具有并发功能,从而大大提高了计算机的处理能力,在各线程之间不存在共享资源的情况下,几个线程的执行顺序可以是随机的。但是,当两个或两个以上的线程需要共享同一资源时,线程之间的执行顺序就需要协调,并且在某一线程占用资源时,其他线程就要等待。

可以这么理解,Java第一个线程是一个生产者,第二个线程是一个消费者,中间资源是一个货架。当货架被生产者占用(放货),消费者不能去用。当货架被消费者占用(消费),生产者不能去用,在这个问题中,两个线程要共享货架这一临界资源,需要在某些时刻(货空/货满)协调他们的工作,即货空时消费者应等待,货满时生产者应等待,这种机制在操作系统中称为线程间的同步。在同步机制中,将那些访问临界资源的程序段称为临界区。

在Java中,临界区是用关键字“synchronized”来标注,并通过一个称为监控器的系统软件来管理的。当执行被冠以“synchronized”的程序段即临界区程序时,监控器将这段程序(访问的临界资源)加锁,此时,称该线程占有临界资源,知道这段程序执行完,才释放锁。只有锁被释放后,其他线程才可以访问这些临界资源。用关键字 synchronized 定义临界区的语句形式是:

synchronize (expression) statement

其中,expression 代表类的名字,是可选项。

statement 可以是一个方法;也可以是一个语句或一个语句块。

示例:

  1. package 测试;
  2. public class test{
  3. public static void main(String args[]) {
  4. //h为键控器
  5. HoldInt h=new HoldInt();
  6. //生产者
  7. ProduceInt p=new ProduceInt(h);
  8. //消费者
  9. ConsumeInt c=new ConsumeInt(h);
  10. p.start();
  11. c.start();
  12. }
  13. }
  14. class HoldInt{
  15. private int sharedInt;
  16. //writeAble=true表示生产者线程能产生新数据
  17. private boolean writeAble=true;
  18. //临界区程序段,也称为同步方法
  19. public synchronized void set(int val) {
  20. while(!writeAble) {
  21. //生产者线程不能生产新数据时进入等待
  22. try {
  23. wait();
  24. }catch(InterruptedException e) {}
  25. }
  26. //生产者被唤醒后继续执行下面的语句
  27. writeAble=false;
  28. sharedInt=val;
  29. notify();
  30. }
  31. //同步方法
  32. public synchronized int get() {
  33. while(writeAble) {
  34. //消费者线程不能消费数据时进入等待状态
  35. try {
  36. wait();
  37. }catch(InterruptedException e) {}
  38. }
  39. //消费者被唤醒后继续执行下面的语句
  40. writeAble=true;
  41. notify();
  42. return sharedInt;
  43. }
  44. }
  45. //生产者线程
  46. class ProduceInt extends Thread{
  47. private HoldInt hi;
  48. public ProduceInt(HoldInt hiForm) {
  49. hi=hiForm;
  50. }
  51. public void run() {
  52. for(int i=1;i<=4;i++) {
  53. hi.set(i);
  54. System.out.println("产生的新数据是:"+i);
  55. }
  56. }
  57. }
  58. //消费者线程
  59. class ConsumeInt extends Thread{
  60. private HoldInt hi;
  61. public ConsumeInt(HoldInt hiForm) {
  62. hi=hiForm;
  63. }
  64. public void run() {
  65. for(int i=1;i<=4;i++) {
  66. int val=hi.get();
  67. System.out.println("读到的数据是:"+val);
  68. }
  69. }
  70. }

输出:

  1. 产生的新数据是:1
  2. 读到的数据是:1
  3. 读到的数据是:2
  4. 产生的新数据是:2
  5. 产生的新数据是:3
  6. 产生的新数据是:4
  7. 读到的数据是:3
  8. 读到的数据是:4

注解:shareInt就是我们的共享资源,一开始writeAble是true的,在这个程序中,共享数据的shareInt方法set()和get()头部的修饰符 synchronized 使HoldInt的每个对象都有一把锁。当ProduceInt对象调用set方法时,HoldInt对象就被锁定(所以即使是不同线程,ConsumeInt也不能调用HoldInt对象的get方法),若set()方法中的writeAble的值为false,则调用set()方法中的wait()方法,把调用set()方法的ProduceInt对象放到HoldInt对象的等待队列中,并将HoldInt对象的锁打开,使该对象的其他synchronized方法可被调用。这个ProduceInt对象就一直在等待队列中等待,直到被唤醒使他进入就绪状态,等待分配CPU,当Producement对象再次进入运行状态时,就从刚刚的wait往后继续执行,如此往复。

线程组:

Java系统的每一个线程都属于某一个线程组。采用线程组结构以后,可以对多个线程进行集中管理。比如,可以同时启动、挂起或者终止一个线程组中的全部线程。Java系统专门在java.lang包中提供了ThreadGroup类来实现对线程组的管理功能。

大多数情况下,一个线程属于哪一个线程组是由编译人员在程序中指定的,若编译人员没有指定,则Java系统会自动将这些线程归于“main”线程组。main线程组是java系统启动时创建的。一个线程组不仅可以包含多个线程,而且线程组中还可以包含其他的线程组。

 


输入与输出:

基本输入/输出流类

流是数据的有序序列,它既可以是未加工的原始二进制数据,也可以是经过一定编码处理后的符合某种规定格式的特定数据。在Java.io包中,基本输入/输出流类可按读/写数据的不同类型分为两种:字节流和字符流

  • 字节流用于读/写字节类型的数据(包括ASCII表中的字符)。字节流类可分为表示输入流的InputStream类及其子类,表示输出流的额OutputStream类及其子类
  • 字符流用于读/写Unicode字符。它包括表示输入流的Reader类及其子类,表示输出流的Writer类及其子类

用于ASCII的字节流:

我们来看个例子:键盘输入数据的存储

  1. package 测试;
  2. import java.io.BufferedInputStream;
  3. import java.io.DataInputStream;
  4. import java.io.IOException;
  5. public class test{
  6. public static void main(String args[]) {
  7. int count;
  8. byte b[]=new byte[256];
  9. String str;
  10. //输入缓冲区流的对象
  11. BufferedInputStream bis=new BufferedInputStream(System.in);
  12. //根据输入缓冲区构造字节流输入对象
  13. DataInputStream in=new DataInputStream(bis);
  14. try {
  15. //判断当前输入流是否支持mark和reset方法
  16. if(in.markSupported()) {
  17. System.out.println("支持mark");
  18. System.out.println("输出字符串,按Enter结束");
  19. //在输入流的当前位置上设置标记,并保留256
  20. in.mark(256);
  21. //读键盘输入的数据到b数组,count得到输入长度
  22. count=in.read(b);
  23. System.out.println("读入字符数"+count);
  24. //将b数组转换为字符串
  25. str=new String(b,0,count);
  26. System.out.println("输入的字符串为:"+str);
  27. //重新回到标记处读取数据
  28. in.reset();
  29. //读前两个字符
  30. in.read(b,0,2);
  31. str=new String(b,0,2);
  32. System.out.println("字符串的前两个为:"+str);
  33. in.reset();
  34. in.skip(count/2);
  35. in.read(b,0,count/2);
  36. str=new String(b,0,count/2);
  37. System.out.println("字符串的后半段"+str);
  38. }else {
  39. System.out.println("不支持mark");
  40. }
  41. bis.close();
  42. in.close();
  43. }catch(IOException E) {
  44. System.out.println("发生I/O错误!");
  45. }
  46. }
  47. }

输出:

  1. 支持mark
  2. 输出字符串,按Enter结束
  3. sajdflsjadf
  4. 读入字符数13
  5. 输入的字符串为:sajdflsjadf
  6. 字符串的前两个为:sa
  7. 字符串的后半段sjadf

原理:

从键盘读入字符串并由屏幕输出:

  1. package 测试;
  2. import java.io.*;
  3. public class test{
  4. public static void main(String args[]) {
  5. int count;
  6. byte b[]=new byte[256];
  7. //输入缓冲区流对象
  8. BufferedInputStream in=new BufferedInputStream(System.in);
  9. //输出缓冲区流对象
  10. BufferedOutputStream bout=new BufferedOutputStream(System.out);
  11. //输出流对象
  12. DataOutputStream out=new DataOutputStream(bout);
  13. //输出流对象
  14. PrintStream p=new PrintStream(System.out);
  15. try {
  16. p.println("请输入字符串");
  17. //从键盘读入数据给b数组,count得到b的长度
  18. count=in.read(b);
  19. in.close();
  20. p.println("读入字节数:"+count);
  21. p.println("输入的字符串为:");
  22. //将b数组从0位置开始的count长度的字节写到out对象中
  23. out.write(b,0,count);
  24. //将缓冲流缓冲区中的数据输出到屏幕上
  25. bout.flush();
  26. p.close();
  27. out.close();
  28. }catch(IOException e) {
  29. System.out.println("发生I/O错误!");
  30. }
  31. }
  32. }

输出就不演示了,看执行过程:

创建BufferedInputStream输入缓冲区类in的对象的构造方法中的参数“System.in”是InputStream类的标准输入流,表示从键盘上读入数据到in的对象中,程序通过in.read方法将in的对象数据写入到b数组中。

创建BufferedOutputStream输出流类bout的对象的构造方法中的参数“System.out”是OutputStream类的标准输出流,表示向屏幕输出。

创建DataOutputStream输出流类out的对象的构造方法中的参数“bout”,表示将out的对象与bout的对象组合成输出流链,这样可以实现动态地增加输出流的功能。

程序的运行过程是通过out.write(b,0,count)方法,将b数组的数据输出到out的对象中,再通过组合输出流链将out的对象的数据输出到bout的对象中,当引用bout.flush,系统自动将缓冲输出流的数据写到屏幕上。

PrintStream类是打印输出流。Java的标准输出System.out是PrintStream的子类,通过引用print()方法或println()方法可向屏幕输出不同的数据

 

用于Unicode的字符流:

示例:利用InputStreamReader类、BufferedReader类、OutputStreamWriter类实现从键盘输入字符串,再输出到屏幕上。利用CharArrayReader类、CharArrayWriter类实现存储器读/写操作

  1. package 测试;
  2. import java.io.*;
  3. public class test{
  4. public static void main(String args[]) {
  5. char c1[],c2[];
  6. String str;
  7. CharArrayReader cin;
  8. CharArrayWriter cout;
  9. //将键盘上输入的数据放入到BufferedReader类in的对象中
  10. InputStreamReader sin=new InputStreamReader(System.in);
  11. BufferedReader in=new BufferedReader(sin);
  12. //屏幕输出
  13. OutputStreamWriter out=new OutputStreamWriter(System.out);
  14. try {
  15. System.out.println("请输入一个字符串,请按Enter结束");
  16. //读入字符串
  17. str=in.readLine();
  18. //将字符串转换成字符数组
  19. c1=str.toCharArray();
  20. //创建CharArrayReader类cin的对象,并与输入流c1数组绑定
  21. cin=new CharArrayReader(c1);
  22. cout=new CharArrayWriter();
  23. //读cin的对象数据内容到cout的对象中
  24. //(cin.ready)返回输入流是否是可读信息
  25. while(cin.ready()) {
  26. //读cin中的一个字符并写到cout中,读/写指针后移一个字符的位置
  27. cout.write(cin.read());
  28. }
  29. System.out.print("c2=");
  30. //将cout的对象数据写到字符数组c2
  31. c2=cout.toCharArray();
  32. //用c2字符数组创建字符串对象并打印
  33. System.out.println(new String(c2));
  34. System.out.print("将cout的对象数据写入out的对象中,并输出:");
  35. cout.writeTo(out);
  36. //强制输出out中的数据到屏幕
  37. out.flush();
  38. }catch(IOException E) {
  39. System.out.println("I/O错误!");
  40. }
  41. }
  42. }

输出:

  1. 请输入一个字符串,请按Enter结束
  2. asdfsdfsa
  3. c2=asdfsdfsa
  4. 将cout的对象数据写入out的对象中,并输出asdfsdfsa

BufferedReader 类和 InputStreamReader 类都是 Reader 的子类。但由于 InputStreamReader 类的对象每读一次都要用read()方法进行字节和字符的转化,效率很低;而BufferedReader类是具有缓冲功能的字符输入流类,可实现字符、数组和行的高效读取。若要用readLine()方法一次一行地进行读取,需要将 System.in 包装成 BufferedReader 来使用,此时必须用 InputStreamReader 把 System.in 转换成 Reader 。

创建 InputStreamReader 类sin的对象的构造方法的参数是 “System.in” ,创建BufferedReader 类in的对象的构造方法的参数是“sin”,这表示建立sin的对象与in的对象的组合式链接通道。

程序运行过程是将键盘上输入的数据放入到sin的对象中进行字节到字符的转化,并输入到in的对象中,再通过“str=in.readLine()”语句,实现将 in 的对象的数据按行读取并放入 str 中。

CharArrayReader 和 CharArrayWriter 类可以将字符数组当作字符数据输入或输出的来源。根据这个特性,可以将文本文件的内容读入字符数组,对字符数组作随机存储,然后写回文本。

writeTo()方法不能直接将结果输出到屏幕上,本书借助 OutputStreamWriter 类(这个类是字符流到字节流的转换桥梁,它的方法可以将字符转换为字节再写入字节流,从而实现外部输出)来完成。

 

文件的输入/输出

在计算机系统中,需要长期保留的数据是以文件的形式存放在磁盘、磁带等外部存储设备中的。程序运行时常常要从文件中读取数据,同时也要把需要长期保留的数据写入文件中。我们来介绍Java的文件与目录管理。

File类:

Java语言的java.io包中的File类是专门用来管理磁盘文件和目录的。每个File类的对象表示一个磁盘文件或目录,其对象属性中包含了文件或目录的相关信息,如文件或目录的名称、文件的长度、目录中所含文件的个数等。调用File类的方法可以完成对文件或目录的常用管理操作,如创建文件或目录,删除文件或目录,查看文件的有关信息等。

示例:获取文件的文件名、长度、大小等特性:

  1. package 测试;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. import java.util.Date;
  7. public class test{
  8. public static void main(String[] args) {
  9. String Path;
  10. //键盘输入
  11. InputStreamReader din=new InputStreamReader(System.in);
  12. BufferedReader in=new BufferedReader(din);
  13. try {
  14. System.out.print("请输入相对或绝对路径:");
  15. //读取输入
  16. Path=in.readLine();
  17. File f=new File(Path);
  18. System.out.println("路径:"+f.getParent());
  19. System.out.println("档案:"+f.getName());
  20. System.out.println("绝对路径:"+f.getAbsolutePath());
  21. System.out.println("文件大小:"+f.length());
  22. System.out.println("是否为文件:"+(f.isFile()?"是":"否"));
  23. System.out.println("是否为目录:"+(f.isDirectory()?"是":"否"));
  24. System.out.println("是否为隐藏:"+(f.isHidden()?"是":"否"));
  25. System.out.println("是否为读取:"+(f.canRead()?"是":"否"));
  26. System.out.println("是否为写入:"+(f.canWrite()?"是":"否"));
  27. System.out.println("最后修改时间:"+new Date(f.lastModified()));
  28. }catch(IOException e) {
  29. System.out.println("I/O错误!");
  30. }
  31. }
  32. }

这个是读取已经存在的文件,注意,输入路径的时候不要有空格

示例:显示“E:/Java”文件夹的内容

  1. package 测试;
  2. import java.io.*;
  3. import java.util.*;
  4. public class test{
  5. public static void main(String[] args) {
  6. File ListFile[];
  7. long totalSize=0;
  8. int FileCount=0, DirectoryCount=0;
  9. //生成File对象
  10. File f=new File("E:/Java");
  11. System.out.println("目录:"+f.getParent()+"\n");
  12. if(f.exists()!=true) {
  13. System.out.println(f.getPath()+"不存在!");
  14. return;
  15. }
  16. //若路径为目录
  17. if(f.isDirectory()) {
  18. //得到文件列表
  19. ListFile=f.listFiles();
  20. for(int i=0;i<ListFile.length;i++) {
  21. System.out.print((ListFile[i].isDirectory()?"D":"X")+" ");
  22. System.out.print(new Date(ListFile[i].lastModified())+" ");
  23. System.out.print(ListFile[i].length()+" ");
  24. System.out.print(ListFile[i].getName()+"\n");
  25. if(ListFile[i].isFile())
  26. FileCount++;
  27. else
  28. DirectoryCount++;
  29. totalSize+=ListFile[i].length();
  30. }
  31. }else { //路径为文件时
  32. System.out.print((f.isDirectory()?"D":"X")+" ");
  33. System.out.print(new Date(f.lastModified())+" ");
  34. System.out.print(f.length()+" ");
  35. System.out.print(f.getName()+"\n");
  36. FileCount++;
  37. totalSize+=f.length();
  38. }
  39. System.out.println("\n\t\t 目录数:"+DirectoryCount);
  40. System.out.println("\t\t 文件数:"+FileCount);
  41. System.out.println("\t\t 总字节:"+totalSize);
  42. }
  43. }

我的输出:

 

FileInputStream 类和 FileOutputStream 类:

程序中会经常用到文件的读/写操作。例如,从已经存在的数据文件中读入数据,或者将程序中产生的大量数据写入磁盘文件中。这时我们就需要使用文件输入/输出流类。Java 系统提供的 FileInputStream 类是用于读取文件中的字节数据的字节文件输入流类;FileOutputStream类是用于向文件写入字节数据的字节文件输出流类。

字节文件输入/输出流的读写:

利用字节文件输入/输出流完成磁盘文件的读/写,首先要利用文件名字符串或File对象创建输入输出流,其次是从文件输入/输出流中读/写数据。从文件输入/输出流中读/写数据有以下两种方式:

  1. 用文件输入/输出类自身的读/写功能完成文件的读/写操作: FileInputStream 类和 FileOutputStream 类自身的读/写功能是直接从父类 InputStream 和 OutputStream 那里继承来的,并未做任何功能的补充。
  2. 配合其他功能较强的输入/输出流完成文件的读/写操作:以 FileInputStream 和 FileOutputStream 为数据源,完成与磁盘文件的映射连接后,再创建其他流类的对象,如 DataInputStream 类和 DataOutputStream 类,这样就可以从 FileInputStream 和 FileOutputStream 对象中读/写数据了。

示例:直接利用 FileInputStream 类和 FileOutputStream 类完成从键盘读入数据写入文件中,再从写入的文件中读出数据打印到屏幕上的操作。

  1. package 测试;
  2. import java.io.*;
  3. public class test{
  4. public static void main(String[] args) {
  5. char c;
  6. int c1;
  7. //在当前目录下建目录,也可用绝对路径
  8. File filePath=new File("d:/");
  9. //若目录不存在,则建之
  10. if(!filePath.exists())
  11. filePath.mkdir();
  12. //在指定目录下建文件类对象
  13. File f1=new File(filePath,"d1.txt");
  14. try {
  15. FileOutputStream fout=new FileOutputStream(f1);
  16. System.out.println("请输入字符,输入结束按#:");
  17. //将从键盘输入的字符写入磁盘文件
  18. while((c=(char)System.in.read())!='#') {
  19. fout.write(c);
  20. }
  21. fout.close();
  22. System.out.println("\n打印从磁盘读入的数据");
  23. FileInputStream fin=new FileInputStream(f1);
  24. //磁盘文件读入程序
  25. while((c1=fin.read())!=-1) {
  26. //将从磁盘读入的数据打印到屏幕上(将int变量c1强制转换为char)
  27. System.out.print((char)c1);
  28. }
  29. fin.close();
  30. }catch(FileNotFoundException e){
  31. System.out.println(e);
  32. }catch(IOException e) {
  33. System.out.println(e);
  34. }
  35. }
  36. }

然后就可以通过控制台的输入给文件增加内容。

 

示例:利用FileInputStream 和 FileOutputStream 输入/输出流,再套接上 DataInputStream 类和 DataOutputStream 类输入/输出流完成文件的读/写操作。本程序是将程序中的数据写到“t1.txt”文件,再从该文件中读出,输出到屏幕上。

  1. package 测试;
  2. import java.io.*;
  3. public class test{
  4. public static void main(String[] args) {
  5. boolean lo=true;
  6. short si=-32768;
  7. int i=65534;
  8. long l=134567;
  9. float f=(float)1.4567;
  10. double d=3.14159265359;
  11. String str1="ABCD";
  12. String str2="Java 语言数学";
  13. try {
  14. FileOutputStream fout=new FileOutputStream("t1.txt");
  15. //文件输出流对象为参数
  16. DataOutputStream out=new DataOutputStream(fout);
  17. FileInputStream fin=new FileInputStream("t1.txt");
  18. DataInputStream in=new DataInputStream(fin);
  19. //将数据写入t1.txt文件
  20. out.writeBoolean(lo);
  21. out.writeShort(si);
  22. out.writeByte(i);
  23. out.writeInt(i);
  24. out.writeLong(l);
  25. out.writeFloat(f);
  26. out.writeDouble(d);
  27. out.writeBytes(str1);
  28. out.writeUTF(str2);
  29. out.close();
  30. //将t1.txt文件的数据读出,并输出到屏幕
  31. System.out.println("Boolean lo="+in.readBoolean());
  32. System.out.println("Short si="+in.readShort());
  33. System.out.println("Byte i="+in.readByte());
  34. System.out.println("Int i="+in.readInt());
  35. System.out.println("Long l="+in.readLong());
  36. System.out.println("Float f="+in.readFloat());
  37. System.out.println("Double d="+in.readDouble());
  38. byte b[]=new byte[4];
  39. in.readFully(b);
  40. System.out.print("str1=");
  41. for(int j=0;j<4;j++) {
  42. System.out.print((char)b[j]);
  43. }
  44. System.out.println();
  45. System.out.println("str2="+in.readUTF());
  46. in.close();
  47. }catch(IOException e) {
  48. System.out.println(e.toString());
  49. }
  50. }
  51. }

输出:

  1. Boolean lo=true
  2. Short si=-32768
  3. Byte i=-2
  4. Int i=65534
  5. Long l=134567
  6. Float f=1.4567
  7. Double d=3.14159265359
  8. str1=ABCD
  9. str2=Java 语言数学

FileReader类和FileWriter类

FileReader类和FileWriter类用于读取文件和向文件写入字符数据。

示例:复制文件

  1. package 测试;
  2. import java.io.*;
  3. public class test{
  4. public static void main(String[] args) {
  5. String temp;
  6. //创建File对象
  7. File sourceFile,targetFile;
  8. BufferedReader source;
  9. BufferedWriter target;
  10. try {
  11. InputStreamReader din=new InputStreamReader(System.in);
  12. BufferedReader in=new BufferedReader(din);
  13. System.out.println("请输入来源文件路径");
  14. sourceFile=new File(in.readLine());
  15. source =new BufferedReader(new FileReader(sourceFile));
  16. System.out.println("请输入目标文件路径");
  17. targetFile=new File(in.readLine());
  18. target =new BufferedWriter(new FileWriter(targetFile));
  19. System.out.print("确定要复制?(y/n)");
  20. if((in.readLine()).equals("y")) {
  21. //源文件的内容不为空
  22. while((temp=source.readLine())!=null) {
  23. //向目标文件写入
  24. target.write(temp);
  25. target.newLine();
  26. target.flush();
  27. }
  28. System.out.println("复制文件完成!!");
  29. }else {
  30. System.out.println("复制文件失败!!!");
  31. return;
  32. }
  33. din.close();
  34. in.close();
  35. }catch(IOException e) {
  36. System.out.println("I/O错误!");
  37. }
  38. }
  39. }

测试输出:

  1. 请输入来源文件路径
  2. t1.txt
  3. 请输入目标文件路径
  4. d:/t2.txt
  5. 确定要复制?(y/n)y
  6. 复制文件完成!!

程序中使用了FileReader类输入流链接BufferedReader类缓冲区输入流、FileWriter类输出流链接BufferedWriter类缓冲区输出流的策略,加快了复制文件的速度。

 

注意flush:

BufferedWriter是缓冲输入流,意思是调用BufferedWriter的write方法时候。数据是先写入到缓冲区里,并没有直接写入到目的文件里。必须调用BufferedWriter的flush()方法。这个方法会刷新一下该缓冲流,也就是会把数据写入到目的文件里。或者你可以调用BufferedWriter的close()方法,该方法会在关闭该输入流之前先刷新一下该缓冲流。也会把数据写入到目的文件里。如果没有在里面的for()循环中添加 bw.flush();这句话,在if 的时候重新 new  BufferedWriter(); 就把原来bw(缓冲区)中的覆盖掉了。于是就不能写进文件字符。

RandomAccessFile类

前面介绍的文件存取方式属于顺序存储,即只能从文件的起始位置向后顺序读/写。java.io包提供的RandomAccessFile类是随机文件访问类,该类的对象可以引用与文件位置指针有关的成员方法,读/写任意位置的数据,实现对文件的随机读/写操作。文件的随机存取要比顺序存取更灵活。

从键盘输入五个整数并写入文件t3.txt,再从这个文件中随机读出其中的某个数(由键盘输入确定),将它显示在屏幕上,同时允许用户对这个数进行修改。

  1. package 测试;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. import java.io.RandomAccessFile;
  5. public class test{
  6. public static void main(String[] args) {
  7. int num,a;
  8. long fp;
  9. try {
  10. //键盘输入
  11. InputStreamReader din=new InputStreamReader(System.in);
  12. BufferedReader in=new BufferedReader(din);
  13. //建立随机存取文件(以读写方式打开)
  14. RandomAccessFile rf=new RandomAccessFile("t3.txt", "rw");
  15. System.out.println("请输入五个整数");
  16. int b[]=new int[5];
  17. for(int i=0;i<5;i++) {
  18. System.out.print("第"+(i+1)+"个数 ");
  19. //Integer.parseInt将字符串转换为int
  20. b[i]=Integer.parseInt(in.readLine());
  21. rf.writeInt(b[i]);
  22. }
  23. while(true) {
  24. //移动文件指针到文件头
  25. rf.seek(0);
  26. System.out.println("请输入要显示第几个数(1-5):");
  27. //读入序号
  28. num=Integer.parseInt(in.readLine());
  29. num=num-1;
  30. //每个整数四个字节,计算移动位置
  31. fp=(num)*4;
  32. rf.seek(fp);
  33. a=rf.readInt();
  34. System.out.println("第"+(num+1)+"个数是"+a);
  35. System.out.print("改写此数:");
  36. b[num]=Integer.parseInt(in.readLine());
  37. fp=num*4;
  38. rf.seek(fp);
  39. //写入文件
  40. rf.writeInt(b[num]);
  41. System.out.print("继续吗?(y/n)");
  42. if((in.readLine()).equals("n"))
  43. break;
  44. }
  45. }catch(Exception e) {
  46. System.out.println("I/O错误!");
  47. }
  48. }
  49. }

 

 

 

 

 

 


 

欢迎访问我的博客 is-hash.com

商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/822748
推荐阅读
相关标签
  

闽ICP备14008679号