java打印整形数 java打印数据_java

importjava.io.DataOutputStream ;importjava.io.File ;importjava.io.FileOutputStream ;public classDataOutputStreamDemo{public static void main(String args[]) throws Exception{ //所有异常抛出
DataOutputStream dos = null ; //声明数据输出流对象
File f = new File("d:" + File.separator + "order.txt") ; //文件的保存路径
dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f))) ; //实例化数据输出流对象
String names[] = {"衬衣","手套","围巾"} ; //商品名称
float prices[] = {98.3f,30.3f,50.5f} ; //商品价格
int nums[] = {3,2,1} ; //商品数量
for(int i=0;i
dos.writeChars(names[i]) ; //写入字符串
dos.writeChar('\t') ; //写入分隔符
dos.writeFloat(prices[i]) ; //写入价格
dos.writeChar('\t') ; //写入分隔符
dos.writeInt(nums[i]) ; //写入数量
dos.writeChar('\n') ; //换行
}
dos.close() ;//关闭输出流
}
};
使用DataOutputStream写入的数据要使用DataInputStream读取进来。前面说过,是给机器看的,人类看不懂.
importjava.io.DataInputStream ;importjava.io.File ;importjava.io.FileInputStream ;public classDataInputStreamDemo{public static void main(String args[]) throws Exception{ //所有异常抛出
DataInputStream dis = null ; //声明数据输入流对象
File f = new File("d:" + File.separator + "order.txt") ; //文件的保存路径
dis = new DataInputStream(new BufferedInputStream(new FileInputStream(f)) ); //实例化数据输入流对象
String name = null ; //接收名称
float price = 0.0f ; //接收价格
int num = 0 ; //接收数量
char temp[] = null ; //接收商品名称
int len = 0 ; //保存读取数据的个数
char c = 0 ; //'\u0000'
try{while(true){
temp= new char[200] ; //开辟空间
len = 0;while((c=dis.readChar())!='\t'){ //接收内容
temp[len] =c ;
len++ ; //读取长度加1
}
name= new String(temp,0,len) ; //将字符数组变为String
price = dis.readFloat() ; //读取价格
dis.readChar() ; //读取\t
num = dis.readInt() ; //读取int
dis.readChar() ; //读取\n
System.out.printf("名称:%s;价格:%5.2f;数量:%d\n",name,price,num) ;
}
}catch(Exception e){}
dis.close() ;
}
};

5.2f 表示的是 总共的数字长度为5位,其中2位表示小数,3位表示整数。

下面我们再看一个例子,即回顾到我们开始说的,一般DataInputStream 和 DataOutputStream 这种处理流,和对应的节点流ByteArrayInputStream ByteArrayOutputStream 关联在一起使用,即我们说的将字节数组中内存中存放当做一个小文件对待,例子如下:

importjava.io.BufferedInputStream;importjava.io.BufferedOutputStream;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.DataInputStream;importjava.io.DataOutputStream;importjava.io.IOException;/*** 数据类型(基本+String)处理流
* 1、输入流 DataInputStream readXxx()
* 2、输出流 DataOutputStream writeXxx()
* 新增方法不能使用多态
*
* java.io.EOFException :没有读取到相关的内容
*@authorAdministrator
**/
public classDataDemo02 {/***@paramargs*/
public static voidmain(String[] args) {try{byte[] data=write();
read(data);
System.out.println(data.length);
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}/*** 从字节数组读取数据+类型
*@throwsIOException*/
public static void read(byte[] src) throwsIOException{//选择流
DataInputStream dis =newDataInputStream(newBufferedInputStream(newByteArrayInputStream(src)
)
);//操作 读取的顺序与写出一致 必须存在才能读取
double num1 =dis.readDouble();long num2 =dis.readLong();
String str=dis.readUTF();
dis.close();
System.out.println(num1+"-->"+num2+"-->"+str);
}/*** 数据+类型输出到字节数组中
*@throwsIOException*/
public static byte[] write() throwsIOException{//目标数组
byte[] dest =null;double point =2.5;long num=100L;
String str="数据类型";//选择流 ByteArrayOutputStream DataOutputStream
ByteArrayOutputStream bos =newByteArrayOutputStream();
DataOutputStream dos=newDataOutputStream(newBufferedOutputStream(
bos
)
);//操作 写出的顺序 为读取准备
dos.writeDouble(point);
dos.writeLong(num);
dos.writeUTF(str);
dos.flush();
dest=bos.toByteArray();//释放资源
dos.close();returndest;
}
}

三、对象流【处理流】

因为前面的是针对于基本的数据类型的操作,那么针对对象,于是就有了对象流;ObjectInputStream 和 ObjectOutputStream 的作用是,对基本数据和对象进行序列化操作支持。

创建“文件输出流”对应的ObjectOutputStream对象,该ObjectOutputStream对象能提供对“基本数据或对象”的持久存储;当我们需要读取这些存储的“基本数据或对象”时,可以创建“文件输入流”对应的ObjectInputStream,进而读取出这些“基本数据或对象”。

注意: 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能被ObjectInputStream/ObjectOutputStream所操作!

主要的作用是用于写入对象信息与读取对象信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化了

使用:

对象的输出流将指定的对象写入到文件的过程,就是将对象序列化的过程,对象的输入流将指定序列化好的文件读出来的过程,就是对象反序列化的过程。既然对象的输出流将对象写入到文件中称之为对象的序列化,那么可想而知对象所对应的class必须要实现Serializable接口。(查看源码可得知:Serializable接口没有任何的方法,只是作为一个标识接口存在)。

1、将User类的对象序列化

class User implements Serializable{//必须实现Serializable接口
String uid;
String pwd;publicUser(String _uid,String _pwd){this.uid =_uid;this.pwd =_pwd;
}
@OverridepublicString toString() {return "账号:"+this.uid+" 密码:"+this.pwd;
}
}public classDemo1 {public static void main(String[] args) throwsIOException {//假设将对象信息写入到obj.txt文件中,事先已经在硬盘中建立了一个obj.txt文件
File f = new File("F:\\obj.txt");
writeObjec(f);
System.out.println("OK");
}//定义方法把对象的信息写到硬盘上------>对象的序列化。
public static void writeObjec(File f) throwsIOException{
FileOutputStream outputStream= new FileOutputStream(f);//创建文件字节输出流对象
ObjectOutputStream objectOutputStream = newObjectOutputStream(outputStream);
objectOutputStream.writeObject(new User("酒香逢","123"));//最后记得关闭资源,objectOutputStream.close()内部已经将outputStream对象资源释放了,所以只需要关闭objectOutputStream即可
objectOutputStream.close();
}
}

运行程序得到记事本中存入的信息:可见已经序列化到记事本中

java打印整形数 java打印数据_序列化_02

2、将序列化到记事本的内容反序列化

public classDemo1 {public static void main(String[] args) throwsIOException, ClassNotFoundException {//假设将对象信息写入到obj.txt文件中,事先已经在硬盘中建立了一个obj.txt文件
File f = new File("F:\\obj.txt");//writeObjec(f);
readObject(f);
System.out.println("OK");
}//定义方法把对象的信息写到硬盘上------>对象的序列化。
public static void writeObjec(File f) throwsIOException{
FileOutputStream outputStream= new FileOutputStream(f);//创建文件字节输出流对象
ObjectOutputStream objectOutputStream = newObjectOutputStream(outputStream);
objectOutputStream.writeObject(new User("酒香逢","123"));//最后记得关闭资源,objectOutputStream.close()内部已经将outputStream对象资源释放了,所以只需要关闭objectOutputStream即可
objectOutputStream.close();
}//把文件中的对象信息读取出来-------->对象的反序列化
public static void readObject(File f) throwsIOException, ClassNotFoundException{
FileInputStream inputStream= new FileInputStream(f);//创建文件字节输出流对象
ObjectInputStream objectInputStream = newObjectInputStream(inputStream);
User user=(User)objectInputStream.readObject();
System.out.println(user);
}
}
运行代码得到的结果:
账号:酒香逢 密码:123
OK
但是,如果这时候这个obj.txt是我们项目中一个文件,而项目到后期在原来User类的基础上添加成员变量String userName;
class User implements Serializable{//必须实现Serializable接口
String uid;
String pwd;
String userName="名字";//新添加的成员变量
publicUser(String _uid,String _pwd){this.uid =_uid;this.pwd =_pwd;
}
@OverridepublicString toString() {return "账号:"+this.uid+" 密码:"+this.pwd;
}
}
这时候如果我们再反序列化,则会引发下面的异常:
Exception in thread "main" java.io.InvalidClassException: com.User; local class incompatible: stream classdesc serialVersionUID = 2161776237447595412, local class serialVersionUID = -3634244984882257127at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:604)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
。。。。。
异常信息解读:
serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是JVM(JAVA虚拟界)通过一个类的类名、成员、包名、工程名算出的一个数字。而这时候序列化文件中记录的serialVersionUID与项目中的不一致,即找不到对应的类来反序列化。
3、如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后
在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
去掉刚才添加的成员变量userName;,并且在User类中指定一个serialVersionUID
class User implements Serializable{//必须实现Serializable接口
private static final long serialVersionUID = 1L;
String uid;
String pwd;//String userName="名字";//新添加的成员变量
publicUser(String _uid,String _pwd){this.uid =_uid;this.pwd =_pwd;
}
@OverridepublicString toString() {return "账号:"+this.uid+" 密码:"+this.pwd;
}
}
重新序列化到obj.txt文件中,然后再类中再将userName添加回来(将上面User类中userName字段解注释),再一次执行反序列化操作,执行的结果跟之前反序列化的结果是一致的。可见这样解决后我们后期修改类也是可行的。

4、如果在User类中再添加成员变量,而这个变量为一个class ,如Address,那么Address类也必须要实现Serializable接口。

class Address implementsSerializable{
String country;
String city;
}class User implements Serializable{//必须实现Serializable接口
private static final long serialVersionUID = 1L;
String uid;
String pwd;
String userName="名字";//新添加的成员变量
Address address;//成员变量为Address
publicUser(String _uid,String _pwd){this.uid =_uid;this.pwd =_pwd;
}
@OverridepublicString toString() {return "账号:"+this.uid+" 密码:"+this.pwd;
}
}
5、最后再提一下关键字transient关键字,当你不想要某些字段序列化时候,可以用transient关键字修饰
class User implements Serializable{//必须实现Serializable接口
private static final long serialVersionUID = 1L;
String uid;
String pwd;transient String userName="名字";//新添加的成员变量//添加关键字transient后,序列化时忽略
Address address;//成员变量为Address
publicUser(String _uid,String _pwd){this.uid =_uid;this.pwd =_pwd;
}
@OverridepublicString toString() {return "账号:"+this.uid+" 密码:"+this.pwd;
}
}

上面我们演示的文件的操作,如果换成字节数组流也是一样的方式。最后总结下:

1. 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。

2. 对象的反序列化创建对象的时候并不会调用到构造方法的、(这点文中没有说到,想要验证的同学在构造方法后面加一句System.out.println("构造方法执行吗?");,实际上构造方法是不执行的,自然这句话也没有输出了)

3. serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。

4. 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID

进行对比,如果这两个id不一致,反序列则失败。

5. 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后

在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。

6. 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。

7. 如果一个类维护了另外一个类的引用,则另外一个类也需要实现Serializable接口。

四、打印流【处理流】

PrintStream 用于向文本输出流打印对象的格式化表示形式。它实现在PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。

在理解PrintStream如何使用之前,先了解一下System类中三个字段:

java打印整形数 java打印数据_java打印整形数_03

往控制台输出时 ,使用System.out.println();
其中System.out这个字段返回的就是打印流,PrintStream
PrintStream ps=System.out; ps.print("hello"); 就等同于 System.out.println("hello");
err和out其实是一样的,只不过在控制台输出时,err输出内容是红色的
Scanner也是一个处理流,创建一个Scanner对象使用到的就是in字段
Scanner console=new Scanner(System.in);
Scanner类其实就是一个输入流,那么我们可以从控制台输入,怎样从文件中输入呢?
InputStream is=System.in;
File file=new File("F:/Picture/test/test2.txt");
is=new BufferedInputStream(newFileInputStream(file));
Scanner sc=newScanner(is);
System.out.println(sc.nextLine());
从上面也可以看出Scanner 其实就一个处理流,用于增强节点流。
使用打印流输出内容到文件中,也是很容易的
这是PrintStream的构造方法

java打印整形数 java打印数据_User_04

File file=new File("F:/Picture/test/test.txt");
PrintStream ps=new PrintStream(new BufferedOutputStream(newFileOutputStream(file)));
ps.append("hellohahaha");
ps.close();
最后总结一句话:对于标准的输入和输出,JDK 中封装好了比较好的操作类,输入的Scanner   输出PrintStream
最后一个小问题:如何将system.out 的输出不是输出到控制台,而是记录到文件中呢?即记录日志利用打印流来实现的。
文本信息中的内容为String类型。而像文件中写入数据,我们经常用到的还有文件输出流对象FileOutputStream.
1 File file = new File("F:\\a.txt");2 FileOutputStream outputStream = new FileOutputStream(file,true);//第二个参数为追加文本
3 outputStream.write(97);
上面的代码执行完之后,a.txt中的内容存的是a,因为write方法接收的为byte类型的数据,97对应的ASCII码为a。
假设我就想将97写入到文件中呢?那么得将第三行代码改为
outputStream.write("97".getBytes());//先将97作为字符串再转换为byte数组
而PrintStream得出现,是的我们写数据入文件变得十分方便,你传入的是什么,就会给你写入什么数据。原因是他内部帮我们转换了
File file = new File("F:\\a.txt");
PrintStream printStream= newPrintStream(file);
printStream.println(97);
printStream.println('a');
printStream.println("hello world");
printStream.println(true);
printStream.println(3.14);
printStream.println(new Student("酒香逢"));

上面这段代码得到的结果为:

java打印整形数 java打印数据_User_05

可见不管什么数据类型,都会转换为字符串,甚至是对象也不例外。

这里再说一下学习java时少不了用到的一句代码:System.out.println();代码中的out,为System类中的一个PrintStream对象,称之为标准输出流对象。标准输出流对象会将数据打印到控制台上。查阅API可知有如下方法,

static void setOut(PrintStream out) //重新分配“标准”输出流。

可以重新指定输出流对象,即将System.out.println();的输出内容打印到我们想打印到的地方。

1 File file = new File("F:\\a.txt");2 PrintStream printStream = newPrintStream(file);3System.setOut(printStream);4 System.out.println("打印到F:\\a.txt中");

这时候内容回写入到文件a.txt中去,而不是打印在控制台中。

最后回归本文重点,日志信息的保存。

假设有代码:

1 try{2 int n = 5/0;3 }catch(Exception e){4e.printStackTrace();5 }
执行结果会抛出我们想要的错误日志信息。
java.lang.ArithmeticException: / by zero
at log.DemoLog.main(DemoLog.java:26)

这时候想将日志信息保存起来怎么办呢?

java打印整形数 java打印数据_java_06

看到Exception类中的这3个重载方法,我们不难得知,只要给他指定一个打印输出流对象当中,即可将日志信息保存到我们想要的地方。
File file = new File("F:\\a.log");
PrintStream printStream= newPrintStream(file);try{int n = 5/0;//除数为零异常
}catch(Exception e){
e.printStackTrace(printStream);
}
上面这段代码执行重复执行多次,

java打印整形数 java打印数据_java数据流接收_07

但是记录的日志信息永远只会记录一条。这明显不是我们想得到的,日志信息,总不能只记录一条吧?那么它存在又有什么用?

其实,追加文本信息的决定者不是e.printStackTrace(printStream);方法,关键点在于流对象,

java打印整形数 java打印数据_java数据流接收_08

可见打印流对象是存在一个OutputStream接口作为参数的传入对象。既然是接口,那么就无法new出OutputStream的对象了,可以用他的子类FileOutputStream对象作为参数传入。并且,FileOutputStream流是可以追加的,

new FileOutputStream(file,true);//第二个参数为追加文本
此时将其作为参数传入,PrintStream流自然也就可以追加内容了。
File file = new File("F:\\a.log");
PrintStream printStream= new PrintStream(new FileOutputStream(file,true),true);try{int n = 5/0;//除数为零异常
}catch(Exception e){
e.printStackTrace(printStream);
}

将代码执行3次后:

java打印整形数 java打印数据_java打印整形数_09

可以看到日志信息是保存有3条的,日志信息记录保存目的达成!

最后如果我们如果想回来呢?

改为控制台输出需要借助FileDescript这个类,之后的输出就会显示在控制台了

System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out),true));