精华内容
下载资源
问答
  • 关于javaboolean问题

    2015-10-28 03:39:14
    如图示,定义了一个boolean变量,赋值false,在test方法中if语句判断值,如果if判断代码是(b==false),则会执行语句;而为(b=false)或(b=true)时也可以,这样代表什么意思呢?![图片]...
  • //filename:Bool.java import java.util.Scanner; /** * * @author D3 * */ class Tri { public double i,j,k; public Tri(double b1,double b2,double b3) { i=b1; j=b2; k=b3; } public ...
  • Java(2)-Java IO输入输出流

    万次阅读 多人点赞 2012-04-01 11:28:55
    Java中I/O操作主要是指使用Java进行输入,输出操作.Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的...

     

     

    一.什么是IO


          Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。  

          Java.io是大多数面向数据流的输入/输出类的主要软件包。此外,Java也对块传输提供支持,在核心库 java.nio中采用的便是块IO。

      流IO的好处是简单易用,缺点是效率较低。块IO效率很高,但编程比较复杂。 
          Java IO模型  :
          Java的IO模型设计非常优秀,它使用Decorator模式,按功能划分Stream,您可以动态装配这些Stream,以便获得您需要的功能。例如,您需要一个具有缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。

     

    二.数据流的基本概念


            数据流是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。 

          “流是磁盘或其它外围设备中存储的数据的源点或终点。”

        在电脑上的数据有三种存储方式,一种是外存,一种是内存,一种是缓存。比如电脑上的硬盘,磁盘,U盘等都是外存,在电脑上有内存条,缓存是在CPU里面的。外存的存储量最大,其次是内存,最后是缓存,但是外存的数据的读取最慢,其次是内存,缓存最快。这里总结从外存读取数据到内存以及将数据从内存写到外存中。对于内存和外存的理解,我们可以简单的理解为容器,即外存是一个容器,内存又是另外一个容器。那又怎样把放在外存这个容器内的数据读取到内存这个容器以及怎么把内存这个容器里的数据存到外存中呢?

         在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:

             标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流等等,java中将输入输出抽象称为流,就好像水管,将两个容器连接起来。将数据冲外存中读取到内存中的称为输入流,将数据从内存写入外存中的称为输出流。

        流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。

    总结的基本概念如下:

    1) 数据流

        一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

      

    2) 输入流(Input  Stream):

          程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道

         

     3) 输出流

        程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。

             

    采用数据流的目的就是使得输出输入独立于设备。

    Input  Stream不关心数据源来自何种设备(键盘,文件,网络)
    Output  Stream不关心数据的目的是何种设备(键盘,文件,网络)

    4) 数据流分类:

    流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种:
     1)  字节流:数据流中最小的数据单元是字节
     2)  字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

     

     

    三. 标准I/O


          Java程序可通过命令行参数与外界进行简短的信息交换,同时,也规定了与标准输入、输出设备,如键盘、显示器进行信息交换的方式。而通过文件可以与外界进行任意数据形式的信息交换。

    1. 命令行参数

    public class TestArgs {
    	public static void main(String[] args) {
    		for (int i = 0; i < args.length; i++) {
    			System.out.println("args[" + i + "] is <" + args[i] + ">");
    		}
    	}
    }

    运行命令:java Java C VB
    运行结果:
    args[0] is <Java>
    args[1] is <C>
    args[2] is <VB>

     

    2. 标准输入,输出数据流

    java系统自带的标准数据流:java.lang.System:

    java.lang.System 
    public final class System  extends Object{ 
       static  PrintStream  err;//标准错误流(输出)
       static  InputStream  in;//标准输入(键盘输入流)
       static  PrintStream  out;//标准输出流(显示器输出流)
    }

    注意:
    (1)System类不能创建对象,只能直接使用它的三个静态成员。
    (2)每当main方法被执行时,就自动生成上述三个对象。

    1) 标准输出流 System.out

       System.out向标准输出设备输出数据,其数据类型为PrintStream。方法:

          Void print(参数)
          Void println(参数)

    2)标准输入流 System.in

        System.in读取标准输入设备数据(从标准输入获取数据,一般是键盘),其数 据类型为InputStream。方法:

            int read()  //返回ASCII码。若,返回值=-1,说明没有读取到任何字节读取工作结束。
             int read(byte[] b)//读入多个字节到缓冲区b中返回值是读入的字节数
    例如:
    import java.io.*;
    public class StandardInputOutput {
    	public static void main(String args[]) {
    		int b;
    		try {
    			System.out.println("please Input:");
    			while ((b = System.in.read()) != -1) {
    				System.out.print((char) b);
    			}
    		} catch (IOException e) {
    			System.out.println(e.toString());
    		}
    	}
    }
    等待键盘输入,键盘输入什么,就打印出什么:

     

    3)标准错误流

       System.err输出标准错误,其数据类型为PrintStream。可查阅API获得详细说明。

        标准输出通过System.out调用println方法输出参数并换行,而print方法输出参数但不换行。println或print方法都通 过重载实现了输出基本数据类型的多个方法,包括输出参数类型为boolean、char、int、long、float和double。同时,也重载实现 了输出参数类型为char[]、String和Object的方法。其中,print(Object)和println(Object)方法在运行时将调 用参数Object的toString方法。

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class StandardInputOutput {
    	public static void main(String args[]) {
    		String s;
    		// 创建缓冲区阅读器从键盘逐行读入数据
    		InputStreamReader ir = new InputStreamReader(System.in);
    		BufferedReader in = new BufferedReader(ir);
    		System.out.println("Unix系统: ctrl-d 或 ctrl-c 退出"
    				+ "\nWindows系统: ctrl-z 退出");
    		try {
    			// 读一行数据,并标准输出至显示器
    			s = in.readLine();
    			// readLine()方法运行时若发生I/O错误,将抛出IOException异常
    			while (s != null) {
    				System.out.println("Read: " + s);
    				s = in.readLine();
    			}
    			// 关闭缓冲阅读器
    			in.close();
    		} catch (IOException e) { // Catch any IO exceptions.
    			e.printStackTrace();
    		}
    	}
    }

     

    四.java.IO层次体系结构


         在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable.掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了

          Java I/O主要包括如下几个层次,包含三个部分:

       1.流式部分――IO的主体部分;

       2.非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;

       3.其他类--文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

       主要的类如下:

         1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。

         2. InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。

         3. OutputStream(二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。

         Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。

         4. Reader(文件格式操作):抽象类,基于字符的输入操作。

         5. Writer(文件格式操作):抽象类,基于字符的输出操作。

         6. RandomAccessFile(随机文件操作):它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作

     

         Java中IO流的体系结构如图:

         

     

     

    五. 非流式文件类--File类


    在Java语言的java.io包中,由File类提供了描述文件和目录的操作与管理方法。但File类不是InputStream、OutputStream或Reader、Writer的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件与目录。
     
    作用:File类主要用于命名文件、查询文件属性和处理文件目录。
    public    class   File   extends Object 
        implements Serializable,Comparable
    {}

     

    File类共提供了三个不同的构造函数,以不同的参数形式灵活地接收文件和目录名信息。构造函数:
    1)File (String   pathname)   
         例:File  f1=new File("FileTest1.txt"); //创建文件对象f1,f1所指的文件是在当前目录下创建的FileTest1.txt
    2)File (String  parent  ,  String child)
         例:File f2=new  File(“D:\\dir1","FileTest2.txt") ;//  注意:D:\\dir1目录事先必须存在,否则异常
    3)File (File    parent  , String child)
         例:File  f4=new File("\\dir3");
              File  f5=new File(f4,"FileTest5.txt");  //在如果 \\dir3目录不存在使用f4.mkdir()先创建
            一个对应于某磁盘文件或目录的File对象一经创建, 就可以通过调用它的方法来获得文件或目录的属性。    
           1)public boolean exists( ) 判断文件或目录是否存在
           2)public boolean isFile( ) 判断是文件还是目录 
           3)public boolean isDirectory( ) 判断是文件还是目录
           4)public String getName( ) 返回文件名或目录名
           5)public String getPath( ) 返回文件或目录的路径。
           6)public long length( ) 获取文件的长度 
           7)public String[ ] list ( ) 将目录中所有文件名保存在字符串数组中返回。 
           File类中还定义了一些对文件或目录进行管理、操作的方法,常用的方法有:
           1) public boolean renameTo( File newFile );    重命名文件
           2) public void delete( );   删除文件
           3)  public boolean mkdir( ); 创建目录
     
    例子:

     

    import java.io.File;
    import java.io.IOException;
    public class TestFile {
    	public static void main(String args[]) throws IOException {
    		File dir = new File("\\root");
    		File f1 = new File(dir, "fileOne.txt");
    		File f2 = new File(dir, "fileTwo.java");
    		// 文件对象创建后,指定的文件或目录不一定物理上存在
    		if (!dir.exists())
    			dir.mkdir();
    		if (!f1.exists())
    			f1.createNewFile();
    		if (!f2.exists())
    			f2.createNewFile();
    		System.out.println("f1's AbsolutePath=  " + f1.getAbsolutePath());
    		System.out.println("f1 Canread=" + f1.canRead());
    		System.out.println("f1's len= " + f1.length());
    		String[] FL;
    		int count = 0;
    		FL = dir.list();
    		for (int i = 0; i < FL.length; i++) {
    			count++;
    			System.out.println(FL[i] + "is in \\root");
    		}
    		System.out.println("there are" + count + "file in //root");
    	}
    
    }

     

    说明:File类的方法:
    (1) exists()测试磁盘中指定的文件或目录是否存在
    (2) mkdir()创建文件对象指定的目录(单层目录)
    (3) createNewFile()创建文件对象指定的文件

    (4) list()返回目录中所有文件名字符串

     

    六. Java.IO流类库


    1. io流的四个基本类

          java.io包中包含了流式I/O所需要的所有类。在java.io包中有四个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流:

    基本数据流的I/O

    输入/输出

    字节流

    字符流

    输入流

    Inputstream

    Reader

    输出流

    OutputStream

    Writer

     
    IO框架:

     

        

     

     Java中其他多种多样变化的流均是由它们派生出来的:

          JDK1.4版本开始引入了新I/O类库,它位于java.nio包中,新I/O类库利用通道和缓冲区等来提高I/O操作的效率。

          在java.io包中, java.io.InputStream 表示字节输入流, java.io.OutputStream表示字节输出流,处于java.io包最顶层。这两个类均为抽象类,也就是说它们不能被实例化,必须生成子类之后才能实现一定的功能。

     

    2. io流的具体分类

    一、按I/O类型来总体分类:

         1. Memory 1)从/向内存数组读写数据: CharArrayReader、 CharArrayWriter、ByteArrayInputStream、ByteArrayOutputStream
                       2)从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream
         2.Pipe管道  实现管道的输入和输出(进程间通信): PipedReader、PipedWriter、PipedInputStream、PipedOutputStream
         3.File 文件流。对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream
         4. ObjectSerialization 对象输入、输出 :ObjectInputStream、ObjectOutputStream
         5.DataConversion数据流 按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数)):DataInputStream、DataOutputStream
         6.Printing 包含方便的打印方法 :PrintWriter、PrintStream
         7.Buffering缓冲  在读入或写出时,对数据进行缓存,以减少I/O的次数:BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream
         8.Filtering 滤流,在数据进行读或写时进行过滤:FilterReader、FilterWriter、FilterInputStream、FilterOutputStream过
         9.Concatenation合并输入 把多个输入流连接成一个输入流 :SequenceInputStream
        10.Counting计数  在读入数据时对行记数 :LineNumberReader、LineNumberInputStream
        11.Peeking Ahead 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream
        12.Converting between Bytes and Characters 按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader,Writer的转换类):InputStreamReader、OutputStreamWriter

    、按数据来源(去向)分类: 
    1、File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter 
    2、byte[]:ByteArrayInputStream, ByteArrayOutputStream 
    3、Char[]: CharArrayReader, CharArrayWriter 
    4、String: StringBufferInputStream, StringReader, StringWriter 
    5、网络数据流:InputStream, OutputStream, Reader, Writer 

     

     

    七. 字节流InputStream/OutputStream


     1. InputStream抽象类 

          InputStream 为字节输入流,它本身为一个抽象类,必须依靠其子类实现各种功能,此抽象类是表示字节输入流的所有类的超类。 继承自InputStream  的流都是向程序中输入数据的,且数据单位为字节(8bit);

    InputStream是输入字节数据用的类,所以InputStream类提供了3种重载的read方法.Inputstream类中的常用方法: 
      (1) public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。
      (2) public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的 
      (3) public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。 
      (4) public int available( ):返回输入流中可以读取的字节数。注意:若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用, 
      (5) public long skip(long n):忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取 
      (6) public int close( ) :我们在使用完后,必须对我们打开的流进行关闭. 

             主要的子类:

            

             1) FileInputStream把一个文件作为InputStream,实现对文件的读取操作     
        2) ByteArrayInputStream:把内存中的一个缓冲区作为InputStream使用 
        3) StringBufferInputStream:把一个String对象作为InputStream 
        4) PipedInputStream:实现了pipe的概念,主要在线程中使用 
        5) SequenceInputStream:把多个InputStream合并为一个InputStream 

     

     2.OutputStream抽象类

      OutputStream提供了3个write方法来做数据的输出,这个是和InputStream是相对应的。 
      1. public void write(byte b[ ]):将参数b中的字节写到输出流。 
      2. public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。 
      3. public abstract void write(int b) :先将int转换为byte类型,把低字节写入到输出流中。 
      4. public void flush( ) : 将数据缓冲区中数据全部输出,并清空缓冲区。 
      5. public void close( ) : 关闭输出流并释放与流相关的系统资源。 

     

           主要的子类:

            

          1) ByteArrayOutputStream:把信息存入内存中的一个缓冲区中 
          2) FileOutputStream:把信息存入文件中 
          3) PipedOutputStream:实现了pipe的概念,主要在线程中使用 
          4) SequenceOutputStream:把多个OutStream合并为一个OutStream 

    流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。

    3. 文件输入流: FileInputStream类

          FileInputStream可以使用read()方法一次读入一个字节,并以int类型返回,或者是使用read()方法时读入至一个byte数组,byte数组的元素有多少个,就读入多少个字节。在将整个文件读取完成或写入完毕的过程中,这么一个byte数组通常被当作缓冲区,因为这么一个byte数组通常扮演承接数据的中间角色。

     

     

    作用:以文件作为数据输入源的数据流。或者说是打开文件,从文件读数据到内存的类。
    使用方法(1)    
       File  fin=new File("d:/abc.txt"); 
      FileInputStream in=new FileInputStream( fin);

     

    使用方法(2)
       FileInputStream  in=new  FileInputStream(“d: /abc.txt”);

    程序举例:
    将InputFromFile.java的程序的内容显示在显示器上

     

    import java.io.IOException;
    import java.io.FileInputStream;
    ;
    public class TestFile {
    	public static void main(String args[]) throws IOException {
    		try{	
    		       FileInputStream rf=new   FileInputStream("InputFromFile.java");
    		       int n=512;   byte  buffer[]=new  byte[n];   
    		       while((rf.read(buffer,0,n)!=-1)&&(n>0)){
    		           System.out.println(new String(buffer) );
    		        }
    		        System.out.println();
    		        rf.close();
    		} catch(IOException  IOe){	    
    		      System.out.println(IOe.toString());
    		}
    
    	}
    
    }

     

    4.文件输出流:FileOutputStream类

       作用:用来处理以文件作为数据输出目的数据流;或者说是从内存区读数据入文件

          FileOutputStream类用来处理以文件作为数据输出目的数据流;一个表示文件名的字符串,也可以是File或FileDescriptor对象。 
      创建一个文件流对象有两种方法: 
      方式1: 
       File   f=new  File (“d:/myjava/write.txt ");
            FileOutputStream  out= new FileOutputStream (f);
      方式2: 
      FileOutputStream out=new FileOutputStream(“d:/myjava/write.txt "); 
      方式3:构造函数将 FileDescriptor()对象作为其参数。 
      FileDescriptor() fd=new FileDescriptor(); 
      FileOutputStream f2=new FileOutputStream(fd); 
      方式4:构造函数将文件名作为其第一参数,将布尔值作为第二参数。 
      FileOutputStream f=new FileOutputStream("d:/abc.txt",true); 
      注意: (1)文件中写数据时,若文件已经存在,则覆盖存在的文件;(2)的读/写操作结束时,应调用close方法关闭流。 

     

    程序举例:使用键盘输入一段文章,将文章保存在文件write.txt中
    import java.io.IOException;
    import java.io.FileOutputStream;
    public class TestFile {
    	public static void main(String args[]) throws IOException {
    		try {
    			System.out.println("please Input from      Keyboard");
    			int count, n = 512;
    			byte buffer[] = new byte[n];
    			count = System.in.read(buffer);
    			FileOutputStream wf = new FileOutputStream("d:/myjava/write.txt");
    			wf.write(buffer, 0, count);
    			wf.close(); // 当流写操作结束时,调用close方法关闭流。
    			System.out.println("Save to the write.txt");
    		} catch (IOException IOe) {
    			System.out.println("File Write Error!");
    		}
    	}
    
    }

    5. FileInputStream流和FileOutputStream的应用

    利用程序将文件file1.txt 拷贝到file2.txt中。
    import java.io.File;
    import java.io.IOException;
    import java.io.FileOutputStream;
    import java.io.FileInputStream;
    
    public class TestFile {
    	public static void main(String args[]) throws IOException {
    		try {
    			File inFile = new File("copy.java");
    			File outFile = new File("copy2.java");
    			FileInputStream finS = new FileInputStream(inFile);
    			FileOutputStream foutS = new FileOutputStream(outFile);
    			int c;
    			while ((c = finS.read()) != -1) {
    				foutS.write(c);
    			}
    			finS.close();
    			foutS.close();
    		} catch (IOException e) {
    			System.err.println("FileStreamsTest: " + e);
    		}
    	}
    
    }
    

     

    6. 缓冲输入输出流 BufferedInputStream/ BufferedOutputStream

            

     

          

           计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样可以减少访问硬盘的次数,提高传输效率。

    BufferedInputStream: BufferedInputStream比FileInputStream多了一个缓冲区。它提供了一个缓冲数组,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容部分或全部返回给用户.由于从缓冲区里读取数据远比直接从物理数据源(譬如文件)读取速度快。BufferedInputStream的默认缓冲区大小是8192字节。当每次读取的数据量很小时,FileInputStream每次都是从硬盘读入,而BufferedInputStream大部分是从缓冲区读入。读取内存速度比读取硬盘速度快得多,因此BufferedInputStream效率高。

    例如:BUFFER_SIZE < 8192时候,BufferedInputStream性能是很高。

    BufferedInputStream  bufferedInput = new BufferedInputStream(new FileInputStream(fileA));
    byte[] buffer = new byte[BUFFER_SIZE];
    int len;
    while ((len = bufferedInput.read(buffer)) > 0){
        System.out.println(new String(buffer, 0, len));
    }

    BUFFER_SIZE > 8192时候, FileInputStream和BufferedInputStream两者效率就没有明显差别了。

    BufferedOutputStream :执行wirte时先写入缓冲区,待缓冲区写满后,系统再写入输出设备。

     

    1)将文件读入内存:

    BufferedInputStreamFileInputStream相接

      FileInputStream in = new  FileInputStream( “file1.txt ” );

      BufferedInputStream bin = new  BufferedInputStreamin); 

    2)将内存写入文件:

    BufferedOutputStream FileOutputStream相接:

    String fileB = "/Users/huangguisu/b.txt";
    FileOutputStream out = new FileOutputStream(fileB);
    BufferedOutputStream bOut = new BufferedOutputStream(out);
    byte[] buffer = {0x1,0x2,0x3};
    bOut.write(buffer);
    bOut.flush();
    bOut.close();


    3)键盘输入流读到内存
    BufferedReader标准的数据流相接 
     InputStreamReader sin = new InputStreamReader (System.in) ;
    BufferedReader bin = new       BufferedReader(sin);
    import java.io.*;
    
    public class ReadWriteToFile {
    	public static void main(String args[]) throws IOException {
    		InputStreamReader sin = new InputStreamReader(System.in);
    		BufferedReader bin = new BufferedReader(sin);
    		FileWriter out = new FileWriter("myfile.txt");
    		BufferedWriter bout = new BufferedWriter(out);
    		String s;
    		while ((s = bin.readLine()).length() > 0) {
    			bout.write(s, 0, s.length());
    		}
    
    	}
    }

    程序说明:
    从键盘读入字符,并写入到文件中BufferedReader类的方法:String readLine()
    作用:读一行字符串,以回车符为结束。
    BufferedWriter类的方法:bout.write(String s,offset,len)
    作用:从缓冲区将字符串s从offset开始,len长度的字符串写到某处。

     

     

    八. 字符流Writer/Reader


            Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。

    1. Reader抽象类

        用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

           

            1) FileReader :与FileInputStream对应  
               主要用来读取字符文件,使用缺省的字符编码,有三种构造函数: 
          (1)将文件名作为字符串 :FileReader f = new FileReader(“c:/temp.txt”); 
          (2)构造函数将File对象作为其参数。 
                  File f=new file(“c:/temp.txt”); 
                  FileReader f1=new FileReader(f); 
         (3)  构造函数将FileDescriptor对象作为参数 
                FileDescriptor() fd = new FileDescriptor() 
                FileReader f2=new FileReader(fd); 
                   (1) 用指定字符数组作为参数:CharArrayReader(char[]) 
                   (2) 将字符数组作为输入流:CharArrayReader(char[], int, int) 
              读取字符串,构造函数如下: public StringReader(String s); 
            2) CharArrayReader:与ByteArrayInputStream对应  
      3) StringReader : 与StringBufferInputStream对应 
      4) InputStreamReader 
            从输入流读取字节,在将它们转换成字符:Public inputstreamReader(inputstream is); 
      5) FilterReader: 允许过滤字符流 
            protected filterReader(Reader r); 
      6) BufferReader :接受Reader对象作为参数,并对其添加字符缓冲器,使用readline()方法可以读取一行。 
         Public BufferReader(Reader r); 

          主要方法:

          (1)  public int read() throws IOException; //读取一个字符,返回值为读取的字符 

      (2)  public int read(char cbuf[]) throws IOException; /*读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量*/ 
      (3)  public abstract int read(char cbuf[],int off,int len) throws IOException; 
      /*读取len个字符,从数组cbuf[]的下标off处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现*/ 

    2. Writer抽象类

         写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。 其子类如下:

        

          1) FileWrite: 与FileOutputStream对应  
      将字符类型数据写入文件,使用缺省字符编码和缓冲器大小。 
      Public FileWrite(file f); 
      
    2)  chararrayWrite:与ByteArrayOutputStream对应 ,将字符缓冲器用作输出。 
          Public CharArrayWrite(); 
      3) PrintWrite:生成格式化输出 
          public PrintWriter(outputstream os); 
      4) filterWriter:用于写入过滤字符流 
          protected FilterWriter(Writer w); 
      5) PipedWriter:与PipedOutputStream对应   

          6) StringWriter:无与之对应的以字节为导向的stream  

          主要方法:

      (1)  public void write(int c) throws IOException; //将整型值c的低16位写入输出流 
      (2)  public void write(char cbuf[]) throws IOException; //将字符数组cbuf[]写入输出流 
      (3)  public abstract void write(char cbuf[],int off,int len) throws IOException; //将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流 
      (4)  public void write(String str) throws IOException; //将字符串str中的字符写入输出流 
      (5)  public void write(String str,int off,int len) throws IOException; //将字符串str 中从索引off开始处的len个字符写入输出流 
      (6)  flush( ) //刷空输出流,并输出所有被缓存的字节。 
      (7)close()    关闭流 public abstract void close() throws IOException

     

     

    3 .InputStream与Reader差别 OutputStream与Writer差别

     

    InputStream和OutputStream类处理的是字节流,数据流中的最小单位是字节(8个bit)
    Reader与Writer处理的是字符流,在处理字符流时涉及了字符编码的转换问题

    import java.io.*;
    public class EncodeTest {
        private static void readBuff(byte [] buff) throws IOException {
           ByteArrayInputStream in =new ByteArrayInputStream(buff);
            int data;
            while((data=in.read())!=-1)   System.out.print(data+"  ");
            System.out.println();     in.close();     }
    
       public static void main(String args[]) throws IOException {
           System.out.println("内存中采用unicode字符编码:" );
           char   c='好';
           int lowBit=c&0xFF;     int highBit=(c&0xFF00)>>8;
           System.out.println(""+lowBit+"   "+highBit);
           String s="好";
           System.out.println("本地操作系统默认字符编码:");
           readBuff(s.getBytes());
           System.out.println("采用GBK字符编码:");
           readBuff(s.getBytes("GBK"));
           System.out.println("采用UTF-8字符编码:");      
           readBuff(s.getBytes("UTF-8"));      }
    }
    


    Reader类能够将输入流中采用其他编码类型的字符转换为Unicode字符,然后在内存中为其分配内存

    Writer类能够将内存中的Unicode字符转换为其他编码类型的字符,再写到输出流中。

     

    4、BufferedInputStream 和BufferedReader区别

    首先虽然都作为一个包装类,但是BufferedReader针对字符流,BufferedInputStream针对字节流

    BufferedInputStream在实现的时候是在自身read方法中提供缓存,是一次取1024或更多字节然后再慢慢读,一个个的返回,它并没有实现读一行的方法

    BufferedReader在实现时通过提供一个readLine方法,使用数组或者stringBuilder存储一行数据,并一次性返回

     

    九. FileWriter类(字符输出流类)


    构造方法:FileWriter fw = new FileWriter(String fileName);//创建字符输出流类对象和已存在的文件相关联。文件不存在的话,并创建。

                   如: FileWriter fw = new FileWriter("C:\\1.txt");

                     FileWriter fw = new FileWriter(String fileName,boolean append);//创建字符输出流类对象和已存在的文件相关联,并设置该该流对文件的操作是否为续写。

                   如:FileWriter fw = new FileWriter("C:\\1.txt",ture); //表示在fw对文件再次写入时,会在该文件的结尾续写,并不会覆盖掉。

    主要方法: 

            void write(String str)   //写入字符串。当执行完此方法后,字符数据还并没有写入到目的文件中去。此时字符数据会保存在缓冲区中。此时在使用刷新方法就可以使数据保存到目的文件中去。

            viod flush()                //刷新该流中的缓冲。将缓冲区中的字符数据保存到目的文件中去。

            viod close()               //关闭此流。在关闭前会先刷新此流的缓冲区。在关闭后,再写入或者刷新的话,会抛IOException异常。

    十. 如何选择IO流


     

    1、确定是数据源和数据目的(输入还是输出

                  源:输入流 InputStream Reader
                  目的:输出流 OutputStream Writer

    2、明确操作的数据对象是否是纯文本

                 是:字符流Reader,Writer
                 否:字节流InputStream,OutputStream

    3、明确具体的设备。

           是硬盘文件:File++:
                  读取:FileInputStream,, FileReader,
                   写入:FileOutputStream,FileWriter
          是内存用数组
                  是byte[]:ByteArrayInputStream, ByteArrayOutputStream
                  是char[]:CharArrayReader, CharArrayWriter
                  是String:StringBufferInputStream(已过时,因为其只能用于String的每个字符都是8位的字符串), StringReader, StringWriter
                  是网络用Socket流: 输入socket.getOutputStream()      输出socket.getInputStream()
                 是键盘:用System.in(是一个InputStream对象)读取,用System.out(是一个OutoutStream对象)打印

    4、是否需要转换流

                是,就使用转换流,从Stream转化为Reader,Writer:InputStreamReader,OutputStreamWriter 

    5、是否需要缓冲提高效率

            是就加上Buffered:BufferedInputStream, BufferedOuputStream, BuffereaReader, BufferedWriter
       

    6、是否需要格式化输出

     

    例如:将一个文件中数据存储到另一个文件中。JAVA的文件读取主要有字节流读取和字符流读取两种方式,字节流可以既可以操作文本文件,也可以操作非文本文件,如一些二进制数据(图片,视频,对象),而字符流只能操作文本。
         

    将一个文件中数据存储到另一个文件中:

    1) 数据源和数据目的:读取流,InputStream/Reader  输出:OutputStream/Writer
    2)是否纯文本:是!可以选择Reader Writer和InputStream/OutputStream。
    3)设备:是硬盘文件。Reader体系中可以操作文件的对象是 FileReader FileWriter。
                  FileReader fr = new FileReader("a.txt");  
                  FileWriter fw = new FileWriter("b.txt"); 
    4)是否需要提高效率:是,加Buffer
                 BufferedReader bfr = new BufferedReader(new FileReader("a.txt");  );  
                 BufferedWriter bfw = new BufferedWriter(new FileWriter("b.txt");  );

    package com.javademo.demo.io;
    import java.io.*;
    
    public class IoFileDemo {
        private static int BUFFER_SIZE = 1024;
        public static void main(String[] args) throws Exception {
            IoFileDemo  ioFileDemo = new IoFileDemo();
            String  fileA = "/Users/huangguisu/a.txt";
            String  fileB = "/Users/huangguisu/b.txt";
            ioFileDemo.copyFileWithBufferStream(fileA, fileB);
    
        }
    
        /**
         * 字符流读取:只能操作文本文件
         * @param srcFile
         * @throws IOException
         */
        public String readFile(String srcFile)  throws IOException {
            FileReader fr = new FileReader(srcFile);//读取流
            //不要直接用String直接拼接文本。。效率太低,每次要new新对象,用StringBuilder,StringBuffer都可以
            StringBuffer sb = new StringBuffer();
            char[] buf = new char[10];
            int len;
            //读取文件并把它存入buf中,用len返回读到字符的个数,一直读到结尾
            while ((len = fr.read(buf)) != -1) {
                //buf字符数组里仍有空白没有读入的位置,所以不要直接new String(buf);
                String str = new String(buf,0,len);
                sb.append(str);
            }
            fr.close();
            return  sb.toString();
    
        }
    
        /**
         * 字符流读取和写入:只能操作文本
         * @param srcFile
         * @param dstFile
         * @throws IOException
         */
        public void copyFileWithString(String srcFile, String dstFile)  throws IOException {
            FileReader fr = new FileReader(srcFile);//读取流
            FileWriter fw = new FileWriter(dstFile);//输出流
            char[] buf = new char[10];
            int len;
            //读取文件并把它存入buf中,用len返回读到字符的个数,一直读到结尾
            while ((len = fr.read(buf)) != -1) {
                //buf字符数组里仍有空白没有读入的位置,所以不要直接fw.write(buf);可以直接使用fw.write(buf, 0, len);
                String str = new String(buf,0,len);
                fw.write(str);
            }
            fr.close();
            fw.close();
    
        }
    
        /**
         * 使用buffer读取和写入字符流::只能操作文本
         * @param srcFile
         * @param dstFile
         * @throws IOException
         */
        public void copyFileWithBufferedString(String srcFile, String dstFile)  throws IOException {
            FileReader fr = new FileReader(srcFile);//读取流
            FileWriter fw = new FileWriter(dstFile);//输出流
            //使用Buffer提高效率
            BufferedReader bfr = new BufferedReader(fr);
            BufferedWriter bfw = new BufferedWriter(fw);
    
            String str = null;
            int j = 1;
            while ((str = bfr.readLine()) != null) {
                System.out.println("第" + j + "行内容:" + str);
                bfw.write(str);
                bfw.newLine();//跨平台的换行符
                j++;
            }
            bfr.close();
            bfw.close();
            fr.close();
            fw.close();
    
    
        }
    
        /**
         * 例:将一个二进制数据(图片,视频,对象)文件中数据存储到另一个文件中,只能用xxxStream.
         * 当然也可以读取文本文件。
         * 字节输入流和输出流
         * @param srcFile
         * @param dstFile
         * @throws IOException
         */
        public void copyFileWithStream(String srcFile, String dstFile)  throws IOException {
            FileInputStream finS = new FileInputStream(srcFile);
            FileOutputStream foutS = new FileOutputStream(dstFile);
            int c;
            while ((c = finS.read()) != -1) {
                foutS.write(c);
            }
            //或者
            /*
            byte[] buffer = new byte[1024];
            int len;
            while ((len = finS.read(buffer)) > 0){
                foutS.write(buffer, 0, len);
                len.flush();
            }
            */
            finS.close();
            foutS.close();
        }
    
        /**
         * 使用Buffer
         * @param srcFile
         * @param dstFile
         * @throws IOException
         */
        public void copyFileWithBufferStream(String srcFile, String dstFile) throws IOException{
            FileInputStream finS = new FileInputStream(srcFile);
            FileOutputStream foutS = new FileOutputStream(dstFile);
            BufferedInputStream  bufferedInput = new BufferedInputStream(finS);
            BufferedOutputStream bufferedOutput = new BufferedOutputStream(foutS);
            byte[] buffer = new byte[BUFFER_SIZE];
            int len;
            while ((len = bufferedInput.read(buffer)) > 0){
                bufferedOutput.write(buffer, 0, len);
                bufferedOutput.flush();
            }
    
            bufferedInput.close();
            bufferedOutput.close();
            finS.close();
            foutS.close();
        }
    
        /**
         * BufferedReader则是比InputStreamReader更高级,它封裝了StreamReader类,一次读取取一行的字符
         * @param srcFile
         * @param dstFile
         * @throws IOException
         */
        public void copyFileWithBufferStream2(String srcFile, String dstFile)  throws IOException {
            FileInputStream finS = new FileInputStream(srcFile);
            FileOutputStream foutS = new FileOutputStream(dstFile);
    
            InputStreamReader inputStreamReader = new InputStreamReader(finS,"utf8");
            OutputStreamWriter outputStreamWriter = new  OutputStreamWriter(foutS, "utf8");
            BufferedReader brRead = new BufferedReader(inputStreamReader);
            BufferedWriter brWriter = new BufferedWriter(outputStreamWriter);
            String line = null;
            while((line= brRead.readLine()) != null){
                System.out.println(line);
                brWriter.write(line);
                brWriter.newLine();//跨平台的换行符
                brWriter.flush();//刷新流
            }
            brRead.close();
            brWriter.close();
            finS.close();
            foutS.close();
    
        }
    
    
    }
    

     

    7、网络(socket)数据交互:

    package com.javademo.demo.socket;
    
    import java.io.*;
    import java.net.Socket;
    
    public class SocketClient {
        public static void main(String args[]) throws Exception{
            //1、创建客户端Socket,指定服务器地址和端口
            Socket socket = new Socket("127.0.0.1",10000);
            //2、获取输出流,向服务器端发送信息:由Socket对象得到输出流
            OutputStream output = socket.getOutputStream();//字节输出流
            //构造PrintWriter对象:将输出流包装成打印流
            PrintWriter printWriter = new PrintWriter(output);
            printWriter.write("{'user':'hgs'}");
            printWriter.flush();
    
    
            //3、获取输入流,并读取服务器端的响应信息:由Socket对象得到输入流
            InputStream inputStream = socket.getInputStream();
            //构造相应的BufferedReader对象
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String info = null;
            while((info = bufferedReader.readLine()) != null){
                System.out.println("Hello,我是客户端,服务器说:"+info);
            }
    
            //4、由系统标准输入设备构造BufferedReader对象
            BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in));
            String readline;
            readline= stdIn.readLine(); //从系统标准输入读入一字符串
            //若从标准输入读入的字符串为 "ok"则停止循环
            while(!readline.equals("ok")){
                //将从系统标准输入读入的字符串输出到Server
                printWriter.println(readline);
                //刷新输出流,使Server马上收到该字符串
                printWriter.flush();
                //在系统标准输出上打印读入的字符串
                System.out.println("Client:"+readline);
                //从Server读入一字符串,并打印到标准输出上
                System.out.println("Server:"+bufferedReader.readLine());
                readline = stdIn.readLine(); //从系统标准输入读入一字符串
            }
    
        }
    }

     

    11. IOException异常类的子类


    1.public class  EOFException :
       非正常到达文件尾或输入流尾时,抛出这种类型的异常。
    2.public class FileNotFoundException:
       当文件找不到时,抛出的异常。
    3.public class InterruptedIOException:
       当I/O操作被中断时,抛出这种类型的异常。




     

    感谢您的支持,我会继续努力的! 扫码打赏,你说多少就多少

             

     

     

     

    展开全文
  • Java输入输出

    万次阅读 多人点赞 2018-05-12 15:27:45
    引言:在平时java开发中,被输入输出搞得头疼。特此写下这篇博客,一是为了总结输入输出,二是为了和大家分享。如果大家觉得写得好,就请高抬贵手点个赞呗!!! 1.输入格式,输出格式控制 1.1输入处理 java的...

    源代码见:点击打开链接

    引言:在平时java开发中,被输入输出搞得头疼。特此写下这篇博客,一是为了总结输入输出,二是为了和大家分享。如果大家觉得写得好,就请高抬贵手点个赞呗!!!

    1.输入格式,输出格式控制

    1.1输入处理

        java的输入,我们用到Scanner类,可以用它创建一个对象

        Scanner reader=new Scanner(System.in);

        然后reader对象调用nextBoolean(),nextByte(),nextShort(),nextInt(),nextLong(),nextFloat(),nextDouble()方法来从输入流中获取数据。这些方法在执行时都会阻塞,程序等待用户在输入流中输入enter键(\n)时继续执行。这里的nextInt,hasNextInt()这些方法的调用,会判断当前字节流里面是否有东西,没有就阻塞等待输入直到用户按enter键(\n)结束输入,在Scanner类中有一个变量needInput,当需要读取数据时,needInput=true(也就是调用nextInt,hasNextInt()这些函数的时候)。有一个readInput方法,当字节流中有东西可读时,让needInput=false(表示不需要阻塞等待输入);下面是Scanner.class源码:

     // Tries to read more input. May block.
        private void readInput() {
            if (buf.limit() == buf.capacity())
                makeSpace();
    
            // Prepare to receive data
            int p = buf.position();
            buf.position(buf.limit());
            buf.limit(buf.capacity());
    
            int n = 0;
            try {
                n = source.read(buf);//这儿
            } catch (IOException ioe) {
                lastException = ioe;
                n = -1;//这儿
            }
    
            if (n == -1) {
                sourceClosed = true;
                needInput = false;//这儿
            }
    
            if (n > 0)
                needInput = false;/这儿
    
            // Restore current position and limit for reading
            buf.limit(buf.position());
            buf.position(p);
        }
    

        总之,在调用next(),hasNext()方法时,字节流里面有东西,就不用等待,没有东西就阻塞等待。例如:

    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Scanner s=new Scanner(System.in);
    		int a,b;
    		a=s.nextInt();
    		System.out.println(a);
    		b=s.nextInt();
    		System.out.println(b);
    		/*a=s.nextInt();
    		b=s.nextInt();
    		System.out.println(a+"  "+b);*/
    	}

        当在命令行时输入时,我可以这样输入(在一行就输入两个数据再按enter),当运行到b=s.nextInt()时,发现字节流里面有东西,就没有阻塞等待输入了。

        当然我们也可以这样输入(第一行输入2后,按enter键,然后在输入3,再按enter键)。运行过程是这样的,首先,当运行到a=s.nextInput()时发现,字节流里面没东西,等待输入,于是我们在命令行的第一行输入了2,按回车确认,这时程序继续执行。当运行到b=s.nextInt()时,发现字节流里面没东西,则阻塞等待输入,于是我们在命令行第三行输入3,按enter键确认,程序继续执行。

        Scanner 使用分隔符模式将其输入分解为标记,默认情况下该分隔符模式与空白匹配(当然可以自定义分隔符)。

         在下面1.3中我们举一个输入控制实例来熟练输入控制的使用。

     

    1.2 hasNext源码解析

    首先我们分输入源是System.in(也就是控制台),和从文件中读取(FileInputStream)。

    (1)对于System.in,不管如何都是要返回true的。我们看源码:

            /*这是测试代码*/
            Scanner scanner=new Scanner(System.in);
    
    
            while (scanner.hasNext()){
                System.out.println(scanner.next());
            }

     我们进入hasNext()函数

    
    public boolean hasNext() {
            ensureOpen();
            saveState();
            while (!sourceClosed) {
                /*看buffer里面是否有东西,有就返回ture*/
                if (hasTokenInBuffer())
                    return revertState(true);
                
                /*没有就从System.in中读入数据。该方法可能会阻塞*/
                readInput();
            }
            
            /*对于从控制台中读入数据,这两句代码是永远不会被执行的。相当于从控制台读入数据永远不会结束,sourceClosed永远为false*/
            boolean result = hasTokenInBuffer();
            return revertState(result);
    }

    (2) 从文件中读取(FileInputStream)

     Scanner scanner1=new Scanner(new FileInputStream("/home/fengli/a.txt"));
    
     while (scanner1.hasNext()){
           System.out.println(scanner1.next());
     }

     在进入hasNext()方法:

    public boolean hasNext() {
            ensureOpen();
            saveState();
            while (!sourceClosed) {
                /*看buffer里面是否有东西,有就返回ture*/
                if (hasTokenInBuffer())
                    return revertState(true);
                
                /*没有就从System.in中读入数据。该方法可能会阻塞*/
                readInput();
            }
            
            /*用文件中读取,sourceClosed就可能为ture。所以这个方法就可以返回false*/
            boolean result = hasTokenInBuffer();
            return revertState(result);
    }

     

    1.3 输出处理

        可用System.out.println()或System.out.print()来向屏幕进行输出。jdk1.5新增了和C语言中printf函数类似的数据输出方法,

    System.out.printf(“格式控制部分”,表达式1,表达式2,……,表达式n)

        格式控制部分由格式控制符号:%d,%c,%f,%s和普通字符组成,普通字符原样输出。格式控制符用来输出表达式的值。

        %d:输出int类型数据值

        %c:输出char类型数据

        %f:输出浮点型数据,小数点部分最多保留6位

        %s:输出字符串数据

        %md:输出int型数据占m列

        %m.nf:输出的浮点型数据占m列,小数点保留n位

    格式字符串语法:

        每个格式控制以%开始,以空格或标点符号结尾。

        

     

    1.3输入输出实例--读入指定形状,输出指定形状

        实例为输出九九乘法表,源码如下:

    public class Main {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		for(int i=1;i<=9;i++)
    		{
    			for(int j=1;j<=i;j++)
    			{
    				System.out.printf("%2d X%2d=%3d ",j,i,i*j);
    			}
    			System.out.println();
    		}
    	}
    }

        运行结果:

    2.从文件输入,输出

    2.1实现方法

        可通过重定向标准输入输出流的方法来实现从文件中输入输出数据。具体使用到的方法如下:

        

    static void setIn(InputStream in)//重定向标准输入
    static void setOut(PrintStream out) //重定向标准输出

       

    2.2从文件输入输出实例--拷贝

        通过重定向的方法,实现从一个文件拷贝东西到另外一个文件。

        代码如下:

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.PrintStream;
    import java.util.Scanner;
    
    public class Main {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		/*
    		 * FileInputStream里面的路径可以写绝对路径又可以写相对路径,为了程序的移植性,推荐使用相对路径,相对路径
    		 * 的根路径是Copy
    		 */
    		FileInputStream fis = null;
    		PrintStream fos=null;
    		try {
    			fis = new FileInputStream("src/source");
    			fos=new PrintStream("src/dest");
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		System.setIn(fis);//重定向标准输入
    		
    		//接下来就和从标准输入流读入数据一样了
    		Scanner sc = new Scanner(System.in);
    		String s=sc.nextLine();
    		System.out.println(s);
    		
    		//重定向输出流
    		System.setOut(fos);
    		
    		//接下来就和从标准输出流读入数据一样了
    		System.out.println(s);
    	}
    
    }
    

     

     

     

     

     

     

     

     

     

    展开全文
  • java.lang解析Boolean

    千次阅读 2016-10-18 13:42:50
    Boolean类: public final class Boolean extends Object implements Serializable, Comparable< Boolean>   字段摘要 static Boolean FALSE

    Boolean类:

    public final class Boolean
         
          extends 
          Object
    
          implements 
          Serializable, 
          Comparable<
          Boolean>
         

      
    字段摘要
    static Boolean FALSE
              对应基值 falseBoolean 对象。
    static Boolean TRUE
              对应基值 trueBoolean 对象。
    static Class<Boolean> TYPE
              表示基本类型 boolean 的 Class 对象。
     
    构造方法摘要
    Boolean(boolean value)
              分配一个表示 value 参数的 Boolean 对象。
    Boolean(String s)
              如果 String 参数不为 null 且在忽略大小写时等于 "true",则分配一个表示true 值的Boolean 对象。
     
    方法摘要
     boolean booleanValue()
              将此 Boolean 对象的值作为基本布尔值返回。
     int compareTo(Boolean b)
              将此 Boolean 实例与其他实例进行比较。
     boolean equals(Object obj)
              当且仅当参数不是 null,而是一个与此对象一样,都表示同一个 Boolean 值的 boolean 对象时,才返回 true
    static boolean getBoolean(String name)
              当且仅当以参数命名的系统属性存在,且等于 "true" 字符串时,才返回 true
     int hashCode()
              返回该 Boolean 对象的哈希码。
    static boolean parseBoolean(String s)
              将字符串参数解析为 boolean 值。
     String toString()
              返回表示该布尔值的 String 对象。
    static String toString(boolean b)
              返回一个表示指定布尔值的 String 对象。
    static Boolean valueOf(boolean b)
              返回一个表示指定 boolean 值的 Boolean 实例。
    static Boolean valueOf(String s)
              返回一个用指定的字符串表示值的 Boolean 值。
     首先我们先定义一个CBoolean类来跟Boolean以同样的方式实现:
    public class CBoolean extends Object implements Serializable,Comparable<CBoolean>{
    先看字段摘要:
        /**
         * The {@code Boolean} object corresponding to the primitive
         * value {@code true}.
         * 
         *对应基值 true 的 Boolean 对象。
         *Boolean,50行
         */
        public static final Boolean TRUE = new Boolean(true);
    
        /**
         * The {@code Boolean} object corresponding to the primitive
         * value {@code false}.
         * 
         *  对应基值 false 的 Boolean 对象。
         *  Boolean,56行
         */
        public static final Boolean FALSE = new Boolean(false);
    
        /**
         * The Class object representing the primitive type boolean.
         *
         * @since   JDK1.1
         * 
         * 表示基本类型 boolean 的 Class 对象。
         * 由于Class.getPrimitiveClass是native方法,这里并不能实现
         * Boolean,64行
         */
    //    @SuppressWarnings("unchecked")
    //    public static final Class<CBoolean> TYPE = (Class<CBoolean>) Class.getPrimitiveClass("boolean");

    这里TRUE和FALSE都是被定义成了Boolean类的对象,TYPE为boolean的Class对象

    下面是构造方法:

    首先是定义一个boolean存储Boolean对象的值:

        /**
         * The value of the Boolean.
         * 存储Boolean的值
         * Boolean,71行
         * @serial
         */
        private final boolean value;

    这个值是final类型的,也就是说在value初始化之后值是不可变的。

    然后是构造方法的实现:

        /**
         * 构造方法,设置Boolean类的值
         * Boolean,87行
         * @param value
         */
        public CBoolean(boolean value) {
            this.value = value;
        }

    第一个构造方法比较简单,直接把传入的boolean值传进来就行了。

    然后是第二种:

        /**
         * 构造方法,实际调用的是CBoolean(boolean value)方法
         * @param s
         */
        public CBoolean(String s) {
            this(parseBoolean(s));
        }
        
        
        /**
         * 判断传入的字符串是否和true相等,返回boolean值
         * Boolean,121行
         * @param s
         * @return
         */
        public static boolean parseBoolean(String s) {
            return ((s != null) && s.equalsIgnoreCase("true"));
        }

    第二种是传入一个String类型的对象,判断是否和true相等,然后返回对应的boolean值,关于equalsIgnoreCase的使用,我们在解析String的时候再说吧,下一篇就说一下String。

    下面是方法摘要:

    booleanValue方法:

        /**
         * 返回Boolean对象的值
         * Boolean,131行
         * @return
         */
        public boolean booleanValue() {
            return value;
        }
    

    直接返回Boolean对象当中存的boolean值

    compareTo方法:

    	/**
    	 * 将此Boolean实例和其他实例比较
    	 * Boolean,273行
    	 */
    	@Override
    	public int compareTo(CBoolean b) {
            return compare(this.value, b.value);
    	}
    	
    	/**
    	 * 将x,y值进行比较,相等返回0,不等时x为true返回1,为false返回-1
    	 * Boolean,290行
    	 * @param x
    	 * @param y
    	 * @return
    	 */
        public static int compare(boolean x, boolean y) {
            return (x == y) ? 0 : (x ? 1 : -1);
        }
    将x,y值进行比较,相等返回0,不等时x为true返回1,为false返回-1

    equals方法:

        /**
         * 当且仅当参数不是 null,而是一个与此对象一样,都表示同一个 Boolean 值的 boolean 对象时,才返回 true。
         * Boolean,225行
         */
        public boolean equals(Object obj) {
            if (obj instanceof Boolean) {
                return value == ((Boolean)obj).booleanValue();
            }
            return false;
        }
    
    在Object类中我们知道equals的方法实现是这样的:

    public boolean equals(Object obj) {  
       // TODO Auto-generated method stub  
          return (this == obj);  
    } 
    
    在Boolean类里面,对equals方法进行了复写,判断Boolean里面存储值的value变量是否相等。

    getBoolean方法:

        /**
         *   当且仅当以参数命名的系统属性存在,且equals传入的字符串name为true时,才返回 true。
         * Boolean,251行
         * @param name
         * @return
         */
        public static boolean getBoolean(String name) {
            boolean result = false;
            try {
                result = parseBoolean(System.getProperty(name));
            } catch (IllegalArgumentException | NullPointerException e) {
            }
            return result;
        }

    这个getBoolean方法的JDK1.6API中文文档的翻译上有问题,英文文档解释是:

        /**
         * Returns {@code true} if and only if the system property
         * named by the argument exists and is equal to the string
         * {@code "true"}. (Beginning with version 1.0.2 of the
         * Java<small><sup>TM</sup></small> platform, the test of
         * this string is case insensitive.) A system property is accessible
         * through {@code getProperty}, a method defined by the
         * {@code System} class.

    /

    翻译的结果是:当且仅当以参数命名的系统属性存在,且等于 "true" 字符串时,才返回 true

    正确的理解应该是:当且仅当以参数命名的系统属性存在,且equals传入的字符串name为true时,才返回 true。

    如果要让返回值为true,首先要添加一个参数到系统属性里面,具体情况可以后面测试结果。

    hashCode方法:

    在Object类里面,hashCode方法是native的,而Boolean类里面是这样实现的:

        /**
         * 返回该 Boolean 对象的哈希码。
         * Boolean,199行
         */
        @Override
        public int hashCode() {
            return Boolean.hashCode(value);
        }
    
        /**
         * Returns a hash code for a {@code boolean} value; compatible with
         * {@code Boolean.hashCode()}.
         *
         * @param value the value to hash
         * @return a hash code value for a {@code boolean} value.
         * @since 1.8
         * 
         * 返回该 Boolean 对象的哈希码。
         * Boolean,212行
         */
        public static int hashCode(boolean value) {
            return value ? 1231 : 1237;
        }
    也就是说boolean值为true的Boolean对象的hashCode一定为1231,false为1237,还有一个前提,这个是JDK1.8以后实现的方法。

    parseBoolean方法:

        /**
         * 判断传入的字符串是否和true相等,返回boolean值
         * Boolean,121行
         * @param s
         * @return
         */
        public static boolean parseBoolean(String s) {
            return ((s != null) && s.equalsIgnoreCase("true"));
        }
    判断字符串是否和true相等,返回boolean值,这个前面说过了。

    toString方法:

        /**
         * 返回表示该布尔值的 String 对象
         * Boolean,188行
         */
        public String toString() {
            return value ? "true" : "false";
        }

        /**
         * 返回表示该布尔值的 String 对象
         * Boolean,176行
         * @param b
         * @return
         */
        public static String toString(boolean b) {
            return b ? "true" : "false";
        }
    相当于是直接吧Boolean对象转化为对应的“true”和“false”String对象,跟当前的Boolean值是没有任何关系的

    valueof方法:

        
        /**
         * 根据boolean值返回Boolean对象
         * @param b
         * @return
         */
        public static CBoolean valueOf(boolean b) {
            return (b ? TRUE : FALSE);
        }
    
        /**
         * 返回一个用指定的字符串表示值的 Boolean 值。
         * 如果字符串不为"true"返回的都是 FALSE Boolean对象
         * @param s
         * @return
         */
        public static CBoolean valueOf(String s) {
            return parseBoolean(s) ? TRUE : FALSE;
        }

    下面测试方法:

    测试过程中发现一个问题,如下:

    		boolean b;
    		Boolean a = b = true;
    		System.out.println(a.booleanValue());
    我们发现这段代码运行是没有任何问题的,打印出来的结果也是true,但是会发现true是一个 boolean变量也是一个关键字,是一个基本数据类型,而Boolean是一个类,怎么能够直接把一个基本数据类型赋值给一个类呢?这个是不是有些问题呢?我回头看了一下代码,发现里面有这么一段:

        /**
         * The Class object representing the primitive type boolean.
         *
         * @since   JDK1.1
         * 
         * 表示基本类型 boolean 的 Class 对象。
         * 由于Class.getPrimitiveClass是native方法,这里并不能实现
         * Boolean,64行
         */
    //    @SuppressWarnings("unchecked")
    //    public static final Class<CBoolean> TYPE = (Class<CBoolean>) Class.getPrimitiveClass("boolean");

    这里面应该是通过这个Class.getPrimitiveClass方法来实现的,在Class中,方法如下:
        /*
         * Return the Virtual Machine's Class object for the named
         * primitive type.
         */
        static native Class<?> getPrimitiveClass(String name);

    也就是说这个方法可以返回当前基本数据类型的对象。

    测试booleanValue方法:

    	public static void main(String args[]){
    		CBoolean cBoolean = new CBoolean(true);//因为不能使用getPrimitiveClass方法,所以只能这样初始化
    //		test();
    		testBooleanValue(cBoolean);
    	}


    先初始化一个cBoolean对象,并且赋值为true,

    	/**
    	 * 测试booleanValue方法
    	 * @param cBoolean
    	 */
        public static void testBooleanValue(CBoolean cBoolean){
        	System.out.println(cBoolean.booleanValue());
        }

    测试结果是打印为true

    测试compareTo方法:

        /**
         * 测试compare方法
         * @param cBoolean
         */
        public static void testCompare(){
        	
        	//定义一个false的CBoolean对象
        	CBoolean booleanFalse = new CBoolean(false);
        	
        	//定义一个true的CBoolean对象
        	CBoolean booleanTrue = new CBoolean(true);
        	
        	//打印输出结果
        	testCompareTo(booleanTrue,booleanTrue);
        	testCompareTo(booleanTrue,booleanFalse);
        	testCompareTo(booleanFalse,booleanTrue);
        	testCompareTo(booleanFalse,booleanFalse);
        	
        }
        
        /**
         * 测试comareTo的结果
         * @param from
         * @param to
         */
        public static void testCompareTo(CBoolean from,CBoolean to){
        	System.out.println(String.format("from:%s,to:%s,result:%s", from.booleanValue(),to.booleanValue(),from.compareTo(to)));
        }

    结果为:

     将from,to值进行比较,相等返回0,不等时from为true返回1,为false返回-1

    测试equals、getBoolean、hashCode和parseBoolean方法:

     /**
         *测试equals、getBoolean、hashCode和parseBoolean方法: 
         *@param cBoolean
         */
        public static void test4Function(){
        	
        	//定义一个值为true的CBoolean对象
        	CBoolean booleanTrue = new CBoolean(true);
        	
        	//定义一个值为false的CBoolean对象
        	CBoolean booleanFalse = new CBoolean(false);
        	
        	//测试equals方法
        	testEquals(booleanTrue,booleanTrue);
        	testEquals(booleanTrue,booleanFalse);
        	
        	//测试getBoolean方法
        	testGetBoolean("true",false);
        	testGetBoolean("true",true);
        	testGetBoolean("oathchen",false);
        	testGetBoolean("oathchen",true);
        	
        	//测试hashCode方法
        	testHashCode(booleanTrue);
        	testHashCode(booleanFalse);
        	
        	//测试parseBoolean方法
        	testParseBoolean(null);
        	testParseBoolean("true");
        	testParseBoolean("false");
        }
       
        /**
         * 测试equals方法
         * @param from
         * @param to
         */
        public static void testEquals(CBoolean from,CBoolean to){
        	System.out.println(String.format("euqals from:%s,to:%s,result:%s", from.booleanValue(),to.booleanValue(),from.equals(to)));
        }
           	
         /**
         * 测试getBoolean方法
         * @param cBoolean
         * @param isSetProperty 是否设置成系统属性
         */
        public static void testGetBoolean(String str,Boolean isSetProperty){
         if(isSetProperty)
         System.setProperty(str, "true");
         
         System.out.println(String.format("getBoolean str:%s,isSetProperty:%s,getBoolean:%s", str,isSetProperty,CBoolean.getBoolean(s	tr)));
        }
        
        /**
         * 
         * 测试hashCode方法
         * @param cBoolean
         */
        public static void testHashCode(CBoolean cBoolean){
        	System.out.println(String.format("hashCode Boolean值:%s,hashCode:%d",cBoolean.booleanValue(),cBoolean.hashCode()));
        }
        
        /**
         * 测试ParseBoolean方法
         * @param str
         */
        public  static void testParseBoolean(String str){
        	System.out.println(String.format("parseBoolean str:%s,result:%s", str,CBoolean.parseBoolean(str)));
        }
    测试结果为:


    当使用equals方法的时候,只有from和to的boolean值相同的时候才会返回true

    getBoolean方法是判断系统内部的变量键值对的”值“是否为true,getBoolean方法参数相当于是键值对的”键“,传入“true”字符串并不会使得返回结果为true。

    hashCode说过了,在JDK1.8之后,Boolean对象的boolean值为true的实例hashCode一定为1231,为false的hashCode一定为1237

    parseBoolean判断传入的字符串是否和true相等,返回boolean值

    测试toString和valueOf方法:

     /**
         * 测试toString和valueOf
         * @param cBoolean
         */
        public static void test2StringAndValueOf(){
        	//定义一个值为true的CBoolean对象
        	CBoolean booleanTrue = new CBoolean(true);
        	
        	//定义一个值为false的CBoolean对象
        	CBoolean booleanFalse = new CBoolean(false);
        	
        	//打印输出toString的结果:
        	System.out.println(String.format("booleanTrue toString: %s", booleanTrue.toString()));
        	System.out.println(String.format("booleanTrue toString(boolean true): %s", booleanTrue.toString(true)));
         	System.out.println(String.format("booleanTrue toString(boolean false): %s", booleanTrue.toString(false)));
         	
         	//测试valueof的结果:
         	System.out.println(String.format("valueOf(boolean true):%s", CBoolean.valueOf(true)));
           	System.out.println(String.format("valueOf(boolean false):%s", CBoolean.valueOf(false)));
           	System.out.println(String.format("valueOf(String true):%s", CBoolean.valueOf("true")));
           	System.out.println(String.format("valueOf(String true):%s", CBoolean.valueOf("false")));
           	System.out.println(String.format("valueOf(String other):%s", CBoolean.valueOf("other")));
         	
        }

    结果为:

    toString无参方法直接返回的是当前CBoolean对象的Value值,有参方法直接返回的是参数当中传入的boolean值

    valueOf方法其实没什么好说的,输入的是true,返回true,不是true的返回false,

    要注意的一点是:

    	System.out.println(String.format("valueOf(boolean true):%s", CBoolean.valueOf(true)));
    我们这里valueOf返回的是一个CBOOlean对象,而format里面是%s,也就是说是字符串,这里是直接调用了CBoolean对象的toString方法打印出来的结果。


    展开全文
  • Java和JavaScript的Boolean区别

    千次阅读 2015-01-27 22:33:26
    今天在做前台js的时候,发现了之前的一个js的function(),这个function里面,使用了boolean类型,返回值都是String...后来,才知道,这就是Java和Js的对于boolean的区别之处; 具体代码如下示-- /****身份证号校

           今天在做前台js的时候,发现了之前的一个js的function(),这个function里面,使用了boolean类型,返回值都是String。在其中一个if语句判断中,条件是一个String,这把我迷惑了,我当时就觉得没见过,条件语句是String的啊,不应该是boolean吗??后来,才知道,这就是Java和Js的对于boolean的区别之处;

    具体代码如下示--

    /****身份证号校验*****/
    function checkIdCard(str){
    	 if(isEmpty(str)) return "请输入身份证号码";
    	 if(IdCardValidate(str)) return "身份证号码格式不正确";
    	 return "";
    }
    /***身份证号校验function()***/
    function IdCardValidate(idCard) { 
    	  idCard = trim(idCard.replace(/ /g, ""));                   
    	  if (idCard.length == 15) {   
    	    return isValidityBrithBy15IdCard(idCard);   
    	  } else if (idCard.length == 18) {   
    	    var a_idCard = idCard.split("");
    	    if(isValidityBrithBy18IdCard(idCard)&&isTrueValidateCodeBy18IdCard(a_idCard)){
    	      return "身份证格式不正确";   
    	    }else {
    	      return "";
    	    }   
    	  } else {   
    	    return "身份证格式不正确";   
    	  }   
    }

           原因在于,在js中,boolean会自己进行强制类型转换,跟java相比,对类型的要求不是很高。当条件语句中的字符串不为空时,会转化为true,否则为false;当然,条件语句里还可以是数字和对象;

    var b1 = Boolean("");//返回false,空字符串
    var b2 = Boolean("s");//返回true,非空字符串
    var b3 = Boolean(0);//返回false,数字0
    var b4 = Boolean(1);//返回true,非0数字
    var b5 = Boolean(-1);//返回true,非0数字
    var b6 = Boolean(null);//返回false
    var b7 = Boolean(undefined);//返回false
    var b8 = Boolean(new Object());//返回true,对象 
    (摘自:http://www.jb51.net/article/32601.htm)


    展开全文
  • 深入剖析Java输入输出的那些细节
  • java数据类型之boolean

    千次阅读 2014-10-27 16:48:02
    前面我们已经学习了一些表示数字和字符的数据类型(java基础之数据类型篇),但是事物往往还有真假之分,比如在判断一件艺术品的时候常说:“这是真的”或“这是假的”。另外还有一些判断性的,比如地铁1号线的首发...
  • java string类型转换boolean类型

    万次阅读 2017-05-13 15:27:17
    今天偶然想把string 类型转换成 boolean 类型 ,查了下api文档,发现文档似乎有点不太对经。。。 嗯,就直接发测试代码吧,废话懒得说了。。。 [java] view plain copy String s1...
  • java 输入流循环输入异常处理处理。
  • Java输入输出流详解

    万次阅读 多人点赞 2017-06-17 21:15:21
    Java语言的输入输出功能是十分强大而灵活的,美中不足的是看上去输入输出的代码并不是很简洁,因为你往往需要包装许多不同的对象。 在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出...
  • Java中的数据类型详解——boolean类型

    千次阅读 2020-08-06 17:54:06
    1、在java语言中boolean类型只有两个值,没有其他值: true和false。 不像c或者c++,c语言中1和0也可以表示布尔类型。 2、boolean类型在实际开发中使用在哪里呢? 使用在逻辑判断当中,通常放在条件的位置...
  • Java输入/输出——File类详解

    千次阅读 2019-02-28 00:39:41
    本文主要讲述了Java输入和输出(IO)方面的内容,IO是所有程序都必须的部分,使用输入机制,允许程序读取外部数据(包括来自磁盘、光盘等存储设备的数据)和用户输入数据;使用输出机制,允许程序记录运行状态,将...
  • java输入输出流详解

    千次阅读 2016-07-03 22:03:57
    Java语言的输入输出功能是十分强大而灵活的,美中不足的是看上去输入输出的代码并不是很简洁,因为你往往需要包装许多不同的对象。 在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,...
  • java 输入的几种方式

    千次阅读 2015-05-19 23:33:39
    以下两种方法是我们在java输入中较为常用的方法 第一种:scanner类(较为简单) Scanner sc= new Scanner(System.in); while (sc.hasNextInt()) { int a=sc.nextInt(); int b=sc.nextInt(); System.o
  • String: String name = xx.next(); int: int age = xx.nextInt(); double: double weight = xx.nextDouble(): boolean: boolean xx = xx.nextBoolean();
  • java输入、输出流的简单入门

    万次阅读 多人点赞 2019-03-30 19:17:24
    程序在运行期间可能需要从外部的存储媒介或其他程序中读取所需要的数据,这就需要使用输入流,输入流的指向称为它的源;另一方面,程序在处理数据后,可能需要将处理的结果写入到永久的存储媒介中或传送给其他的应用...
  • 输入一个整型变量(int)的值 定义一个boolean类型的变量把b int 转化为boolean:,先判断这个变量是否等于0,不等就赋值b=1,等于就赋值b=0 再boolean 转 int 代码 public class Main{ public static void main...
  • Java 输入、输出流】

    千次阅读 多人点赞 2021-04-15 17:02:47
    Java 输入、输出流(I/O流)1 File类1.1 文件的属性1.2 目录2 文件字节输入、输出流二级目录三级目录3 文件字符输入、输出流二级目录三级目录4 缓冲流二级目录三级目录 概述:输入、输出流提供一条通道程序,可以...
  • org.apache.catalina.core.ApplicationDispatcher invoke SEVERE: Servlet.service() for servlet jsp threw exception ...javax.el.ELException: Cannot convert 0 of type class java.lang.Integer to class java.l
  • Java输入

    千次阅读 2019-04-24 21:10:39
    Java类型: 1、逻辑类型:boolean 常量:true,false boolean x,ok = true,关闭 = false; 2、整型类型 (1)int型 int x = 12; (2)byte型 byte x = -12,漂亮 = 98; byte范围:-2^7^~2^7^-1 (3)short型 short...
  • 今天在帮助一位小伙伴解决传值的问题的时候,发现他使用的是boolean类型的带参方法。程序执行没有问题,但是boolen类型的值传不出来。怎么找问题都找不出来。今天就该问题所产生的原因给大家分享一下,下面我做了一...
  • java面试题001】Boolean b = new Boolean(“abcde”); 会编译报错吗?  不会编译报错,在Boolean的构造函数中,除了”true”和”false”之外的字符串虽然不会造成编译错误,但是会返回false。 因为这个Boolean...
  • 截的图里面_if(bool)_里的bool什么意思?是_bool=true_吗? 为什么我把_bool_换成_bool=true_ 编译后输入_40_会把_-1_也打印出来? 先谢好心人解答了,刚学java,好多都不懂
  • Java读取输入

    千次阅读 2019-02-26 15:30:08
    java.lang.Scanner Scanner(InputStream in) 用给定的输入流创建一个Scanner对象。 String nextLine() 读取输入的下一行内容。 String next() 读取输入的下一个单词(以空格作为分隔符)。 int nextInt() ...
  • Java输入日期判断星期几

    千次阅读 2018-06-03 15:45:24
    package nn; import java.util.*; //import java.lang.*;... public static boolean judgeYear(int year) { if (year % 400 == 0) return true; else if ((year % 4 == 0) &amp;&amp; (year...
  • Java输入IP,判断IP是否正确

    千次阅读 2019-08-02 12:53:31
    Java输入IP,判断IP是否正确 方法一:字符判断 import java.util.Scanner; /* * 输入ip,判断ip是否正确 */ public class IPJudge { public static void main(String[] args) { System.out.println("请...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 340,897
精华内容 136,358
关键字:

java输入boolean

java 订阅