黑马程序员--IO(二)--File类、Properties类、打印流、序列流等

黑马程序员--IO(二)--File类、Properties类、打印流、序列流等

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、File类

File类用来将文件或文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。一旦创建,File对象表示的抽象路径名将永不改变。

//封装对象的方式:可以将已存在或不存在的文件或目录封装成file对象。
import java.io.File;
class  FileDemo
{
	public static void main(String[] args) 
	{
		constructorDemo();
	}
	public static void constructorDemo()
	{
		//方式一:
		File f1 = new File("d:\\demo\\a.txt");

		//方式二:
		File f2 = new File("d:\\demo","a.txt");

		//方式三:
		File f = new File("d:\\demo");
		File f3 = new File(f,"a.txt");

		//跨系统方式.File.separator是与系统有关的默认名称分隔符。UNIX系统上值为'/';
		File f4 = new File("d:"+File.separator+"demo"+File.separator+"a.txt");
	}
}
PS:
流只能操作数据。
1、常见方法:
1.1 创建
boolean creatNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。只能创建一级文件夹。
boolean mkdirs():创建多级文件夹
1.2 删除
boolean delete():删除文件或目录。文件存在,返回true。文件不存在或正在被执行,返回false。
void deleteOnExit():在程序退出时删除指定文件。避免程序异常时,删除动作执行不到的问题。
1.3 判断
boolean canExecute():测试程序是否可以执行此路径名表示的文件。
boolean exists():判断文件是否存在。
boolean isDirectory():判断路径名是否是目录(文件夹)。
boolean isFile():判断路径名是否是文件。
boolean isHidden():判断路径名是否是一个隐藏文件。
boolean isAbsolute():判断路径名是否是绝对路径名。
PS:
判读文件对象是否是文件或目录时,必须要判断该文件对象封装的内容是否存在。
1.4 获取信息。
String getName():获取文件名。
String getPath():获取文件的相对路径。即创建的对象传入的参数是什么就获取到什么。
String getParent():返回文件绝对路径中的父目录。若获取相对路径,有明确父目录时,返回父目录,否则返回null。
String getAbsolutePath():获取文件的绝对路径。
long lastModified():返回文件最后一次被修改时间。判断文件是否被修改过。
long length():返回文件长度。

//File类常见方法:
import java.io.*;
class  FileDemo1
{
	public static void main(String[] args) throws IOException 
	{
		method_create();
		method_delete();
		method_judge();
		method_get();
		method_list();
	}
	public static void method_create() throws IOException
	{
		File f = new File("file.txt");
		//指定位置创建文件。因为它调用、启用资源有产生异常的可能。
		sop("create:"+f.createNewFile());

		File dir1 = new File("abc");
		//创建一级文件夹
		sop("mkdirs:"+dir1.mkdir());

		File dir2 = new File("abc\\a\\aad\\e");
		//创建多级文件夹
		sop("mkdirs:"+dir2.mkdirs());
	}
	public static void method_delete() throws IOException
	{
		File f = new File("a.txt");
		//删除文件或目录。文件存在,返回true。文件不存在或正在被执行,返回false。
		sop("delete:"+f.delete());

		//在程序退出时删除指定文件。
		File f2 = new File("file.txt");
		f2.deleteOnExit();
	}
	public static void method_judge() throws IOException
	{
		File f = new File("d:\\Demo\\IO3\\FileDemo\\file.txt");
		
		sop("canExecute:"+f.canExecute());
		//如果流存在才可以读写,所以exists方法可以直接判断流存在问题。
		sop("exists:"+f.exists());

		f.mkdir();
		//当判断文件对象是否是文件或目录时,必须要先判断该文件对象封装的内存是否存在。
		sop("dir:"+f.isDirectory());
		sop("file:"+f.isFile());
		sop("hidden:"+f.isHidden());

		sop(f.isAbsolute());
	}
	public static void method_get() throws IOException
	{
		File f = new File("d:\\a.txt");

		sop("path:"+f.getPath());
		sop("abspath:"+f.getAbsolutePath());
		sop("parent:"+f.getParent());
		sop("length:"+f.length());
	}
	public static void method_list() throws IOException
	{
		File f1 = new File("d:\\helloworld.java");
		File f2 = new File("d:\\a.java");
		
		//列出可以文件系统根目录
		sop("root"+f2.listRoots());
		//列出当前目录下所有文件
		sop("list"+f2.list());
		sop("rename:"+f1.renameTo(f2));
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
1.5 列出文件及文件过滤
static File[] listRoots():列出可用的文件系统根目录,即系统盘符。
String[] list():列出当前目录下所有文件,包括隐藏。调用list方法的file对象必须是封装了一个已存在的目录。
String[] list(FilenameFilter filter):返回一个字符串数组,获取目录中满足指定过滤器的文件和目录。
FilenameFilter:文件名过滤器,是一个接口,其中包含一个方法:accept(File dir,String name),返回的是boolean。
File[] listFiles():返回一个抽象路径名数组,获取当前文件夹下的所有文件和文件夹。
boolean renameTo(File dest):重命名。
list和listFiles区别:
list返回当前目录下的文件和名称;listFiles返回当前目录下的文件和文件对象。
即list只能获取名,listFiles可以通过方法获取更多属性。

//list方法
import java.io.*;
class FileDemo2
{
	public static void main(String[] args) 
	{
		listRootsDemo();
		listDemo();
		listDemo_2();
		listfileDemo();
	}
	public static void listfileDemo()
	{
		File dir = new File("D:\\Demo\\IO3");

		File[] files = dir.listFiles();

		for(File f:files)
		{
			System.out.println(f.getName()+"::"+f.length());
		}	
	}
	public static void listDemo_2()
	{
		File dir = new File("D:\\Demo\\IO3");

		String[] arr = dir.list(new FilenameFilter()
		{
			//list依据accept的返回值来判断数组内容。
			public boolean accept(File dir,String name)
			{
				return name.endsWith(".txt");
			}
		});
		System.out.println("len:"+arr.length);
		for(String name:arr)
		{
			System.out.println(name);
		}
	}
	public static void listDemo()
	{
		File f = new File("D:\\");

		//调用list方法的file对象必须是封装了一个目录。该目录必须存在。
		String[] names = f.list();
		for(String name:names)
		{
			System.out.println(name);
		}
	}
	//打印系统有效盘符。
	public static void listRootsDemo()
	{
		File[] files = File.listRoots();

		for(File f:files)
		{
			System.out.println(f);
		}
	}
}

2、递归
2.1 定义
递归:函数自身调用自身的表现形式或编程手法。因为目录中还有目录,只有使用同一个
列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。
2.2 注意事项:
a 限定次数。
b 注意递归次数,避免内存溢出。因为每次调用自己都会执行下一次调用自己的方法,所以会不断
在栈内存中开辟新空间,次数过多,会导致内存溢出。

/*
列出指定目录下文件或文件夹,包含子目录的内容。

因为目录中还有目录,只有使用同一个列出目录功能的函数完成即可。
在列出过程中出现的还是目录的话,还可以再次调用本功能。
也就是函数自身调用自身。
这种表现形式或编程手法,称为递归。

递归注意事项:
1,限定次数。
2,注意递归次数,避免内存溢出。
*/
import java.io.*;
class FileDemo3
{
	public static void main(String[] args) 
	{
		File dir = new File("D:\\Demo");
		showDir(dir);
		//int n = getSum(25000);
		//System.out.println(n);
	}
	//递归会不断开辟栈对象次数过多,有内存溢出风险。
	public static int getSum(int n)
	{
		if(n==1)
			return 1;
		return n+getSum(n-1);
	}
	//递归:自我调用。因为没有次数限制,会成为无限循环,失去价值。
	public static void method()
	{
		method();
	}
	public static void showDir(File dir)
	{
		//打印目录
		System.out.println(dir);
		File[] files = dir.listFiles();

		//递归
		for(int x=0; x<files.length; x++)
		{
			//如果文件中有文件夹,再次调用自己。
			if(files[x].isDirectory())
				showDir(files[x]);
			System.out.println(files[x]);
		}
	}
}

二、Properties类

Properties是hashtable的子类。所以它具备Map集合的特点。而且它存储的键值对都是字符串。无泛型定义。
它是集合和IO技术集合的集合容器。
1、特点:
a 可以用于键值对形式的配置文件。
b 在加载数据时,需要数据有固定格式:键=值。
c 集合中的数据可以保存到流中或从流中获取。
2、特有方法:
2.1 设置
Object setProperty(String key,String value):设置键和值,调用Hashtable的方法put。
2.2 获取
String getProperty(String key):通过键获取值。
Set<String> stringPropertyNames():返回此属性列表中的键集。将map变成set,带泛型。
3.3 加载与存储
void load(InputStream ism):将字节流中的数据加载进集合。
void load(Reader reader):将字符流中的数据加载进集合。
void list(PrintStream out):将属性列表输出到指定的输出流中。
void store(OutputStream out,String comments):对应load(InputStream)将属性列表(键值对)写入输出流。comments注释信息。
void store(Writer writer,String comments):对应load(Reader)将属性列表(键值对)写入输出流。
/*
Properties类实例:
本文件解决问题:
1,设置和获取元素。
2,将流中的数据存储到集合中。
	思路:
	1,用一个流和info.txt文件关联。
	2,读取一行数据,将该行数据用“=”进行切割。
	3,等号左边作为键,右边作为值。存入到Properties集合中即可。
*/
import java.io.*;
import java.util.*;

class PropertiesDemo
{
	public static void main(String[] args) throws IOException 
	{
		setAndGet();
		method_1();
		loadDemo();
	}
	public static void loadDemo() throws IOException 
	{
		Properties prop = new Properties();
		FileInputStream fis = new FileInputStream("info.txt");

		//将流中的数据加载进集合
		prop.load(fis);

		//修改数据信息,但是无法改源文件。
		prop.setProperty("wangwu","39");

		FileOutputStream fos = new FileOutputStream("info.txt");

		//将属性列表(键值对)写入输出流。
		prop.store(fos,"haha");

		//System.out.println(prop);
		//另一种输出方式。
		prop.list(System.out);	

		fos.close();
		fis.close();
	}
	//模拟load方法。将流中的数据存储到集合中。
	public static void method_1() throws IOException
	{
		BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));

		String line = null;
		Properties prop = new Properties();

		while((line=bufr.readLine())!=null)
		{
			String[] arr = line.split("=");
			//将键值对存入Properties中
			prop.setProperty(arr[0],arr[1]);
		}

		bufr.close();

		System.out.println(prop);
	}
	//设置和获取元素。
	public static void setAndGet()
	{
		Properties prop = new Properties();

		//设置
		prop.setProperty("zhangsan","30");
		prop.setProperty("lisi","34");
		//System.out.println(prop);

		//获取
		String value = prop.getProperty("lisi");
		//System.out.println(value);

		//修改信息、
		prop.setProperty("lisi",98+"");

		//取出所有元素
		Set<String> names = prop.stringPropertyNames();
		for(String s : names)
		{
			System.out.println(s+":"+prop.getProperty(s));
		}
	}
}
应用:创建文件访问计数器。
/*
需求:记录应用程序运行次数。如果使用次数已到,给出注册提示。
思路:
1、在程序中定义计数器,随着程序的运行存在并自增。但是普通计数器会随着程序退出而清0;
2、建立一个配置文件,记录该软件的使用次数。
3、为便于阅读、操作数据,该配置文件使用键值对的形式。
4、键值对数据是map集合;数据是以文件形式存储,使用io技术;那么map+io -->properties。
配置文件可以实现应用程序数据的共享。
*/
import java.io.*;
import java.util.*;
class RunCount
{
	public static void main(String[] args) throws IOException
	{
		Properties prop = new Properties();

		//将文件独立封装可以使用判读方法,防止文件不存在时抛出异常。
		File file = new File("count.ini");
		if(!file.exists())
			file.createNewFile();

		FileInputStream fis = new FileInputStream(file);

		prop.load(fis);

		int count = 0;
		String value = prop.getProperty("time");

		if(value!=null)
		{
			//将字符串转成基本数据类型。静态装换方法
			count = Integer.parseInt(value);
			if(count>=5)
			{
				System.out.println("使用次数已到,请充值!");
				return;
			}
		}
		count++;

		prop.setProperty("time",count+"");
		
		FileOutputStream fos = new FileOutputStream(file);

		prop.store(fos,"");

		fos.close();
		fis.close();
	}
}

三、打印流

打印流:PrintStream、PrintWriter。可直接操作输入流和文件,将传入数据原样打印。
1、PrintStream:字节打印流。为其他输出流添加打印各种数据值的功能。特点:不会抛出IOException。
PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。当需要写入字符时应使用PrintWriter类。
构造函数可以接收的参数类型:
1.1 File对象。File
1.2 字符串路径。String
1.3 字节输出流。OutputStream
2、PrintWriter:字符打印流。
构造函数可以接收的参数类型:
2.1 File对象。File
2.2 字符串路径。String
2.3 字节输出流。OutputStream
2.4 字符输出流。Writer。
//打印流:提供打印方法,可以将各种数据类型的数据原样打印。
import java.io.*;

class PrintStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		PrintStream ps = new PrintStream("print.txt"); 

		//write(int b):只写最低8位。
		ps.write(97);//a
		//print方法原数打印
		ps.print(97);//97

		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));

		//PrintWriter构造函数的第二个参数设置为true,表示自动刷新。
		PrintWriter pw = new PrintWriter(System.out,true);

		String line = null;

		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			pw.println(line.toUpperCase());//ln是刷新标记
			//pw.flush();
		}
		ps.close();
		pw.close();
		bufr.close();
	}
}

四、序列流

SequenceInputStream:序列流(合并流)。多源对应一个目的时,省去部分关联和续写步骤,简化代码。
SequenceInputStream(InputStream s1, InputStreamS2 ): 依顺序读取s1,s2
SequenceInputStream(Enumeration<? extends InputStream> e):多个参数合并
/*
SequenceInputStream:序列流(合并流)。
需求:将1.txt、2.txt、3.txt合并为一个文件4.txt。
*/
import java.io.*;
import java.util.*;
class SequenceDemo
{
	public static void main(String[] args) throws IOException
	{
		Vector<FileInputStream> v = new Vector<FileInputStream>();

		v.add(new FileInputStream("D:\\1.txt"));
		v.add(new FileInputStream("D:\\2.txt"));
		v.add(new FileInputStream("D:\\3.txt"));

		Enumeration<FileInputStream> en = v.elements();
	
		//合并流,统一源。
		SequenceInputStream sis = new SequenceInputStream(en);

		//目的。
		FileOutputStream fos = new FileOutputStream("D:\\4.txt");

		byte[] buf = new byte[1024];

		int len = 0;
		while((len=sis.read(buf))!=-1)
		{
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();
	}
}

五、操作对象

操作对象:ObjectInputStream、ObjectOutputStream。
ObjectOutputStream:将Java对象的基本数据类型和图形写入OutputStream。
被操作的对象需要实现Serializable接口才能使用序列化功能。
Serializable接口是标记接口,它用于给被序列化的类加入ID号,以判断类和对象是否是同一个版本。
静态不能被序列化,因为静态在方法区中,不在堆中。
serialVersionUID:给类定义固定标识,方便序列化。新类可以操作曾经被序列化的对象。
transient:是让非静态成员无法序列化的关键字。保证它的值在堆内存中存在,而不显示在文本文件中。
/*
操作对象:ObjectInputStream、ObjectOutputStream
NotSerializableException:某个要序列化的对象不能实现 java.io.Serializable 接口异常。
*/ 
import java.io.*;
class Person implements Serializable
{
	//serialVersionUID:给类定义固定标识,使新类还可以用之前的序列化对象。
	public static final long serialVersionUID = 42L;

	private String name;
	transient int age;//不想非静态成员序列化加关键字:transient

	//静态不能被序列化,因为静态在方法区中,不在堆中。
	static String country = "cn"; 
	Person(String name,int age,String country)
	{
		this.name=name;
		this.age=age;
		this.country = country;
	}
	public String toString()
	{
		return name+":"+age+":"+country;
	}
}
class ObjectStreamDemo 
{
	public static void main(String[] args) throws Exception
	{
		writeObj();
		readObj();
	}

	public static void readObj() throws Exception
	{
		ObjectInputStream ois = 
			new ObjectInputStream(new FileInputStream("obj.txt"));

		Person p = (Person)ois.readObject();

		System.out.println(p);
		ois.close();

	}
	public static void writeObj() throws IOException
	{
		//目的:将对象保存成文件
		ObjectOutputStream oos =
			new ObjectOutputStream(new FileOutputStream("obj.txt"));

		oos.writeObject(new Person("lisi0",339,"USA"));

		oos.close();
	}
}
PS:
1 对象的实体化存储:将对象存储在硬盘上。也可称为对象的序列化、对象的可串联性。
2 标记接口:没有方法的接口。只是为了标识其信息。

六、RandomAccessFile

RandomAccessFile:随机访问文件,自身具备读写方法。它直接继承自Object,不算是IO体系中子类。
1、特点:
1.1 具备读和写功能;
1.2 该对象内部封装了一个byte数组,并通过指针可以操作数组中的元素;
1.3 可以通过getFilePointer方法获取指针位置,通过seek方法改变指针位置。
1.4 该对象只能操作文件,而且操作文件还有模式:只读:r,读写:rw。
PS:
1 读写原理:内部封装了字节输入流和输出流。
2 操作文件模式:
如果模式为只读 r:不创建文件。只读取一个已存在的文件,如果该文件不存在,则会出现异常。
如果模式为读写rw:操作的文件不存在,会自动创建。如果存在不会覆盖。
2、常用方法:
skipBytes()跳过指定的字节数; 
seek()调整对象中指针;
//RandomAccessFile:随机访问文件。
import java.io.*;
class RandomAccessFileDemo 
{
	public static void main(String[] args) throws IOException 
	{
		writeFile();
		readFile();
		writeFile_2();
	}

	public static void readFile() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","r");//只读

		//通过seek访问任意位置。
		//raf.seek(8);

		//跳过指定的字节数。不能回跳
		raf.skipBytes(8);

		byte[] buf = new byte[4];

		raf.read(buf);

		String name = new String(buf);

		int age = raf.readInt();

		System.out.println("name="+name);
		System.out.println("age="+age);

		raf.close();
	}
	public static void writeFile() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

		raf.write("Lisi".getBytes());

		//使用write方法写入最后一个字节。只取8位,超过256时存在数据丢失。
		raf.write(257);

		//使用writeInt方法写入四个字节(int类型)
		raf.writeInt(97);
		raf.write("wagnwu".getBytes());
		raf.writeInt(99);

		raf.close();
	}
	public static void writeFile_2() throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

		//往指定位置写入数据
		raf.seek(2*8);
		raf.write("周期".getBytes());
		raf.writeInt(103);

		raf.close();
	}
}

七、管道流

管道流:PipedInputStream、PipedOutputStream。
1、特点:
1.1 将输入流和输出流连接,省去了用数组存储数据的中转环节。
1.2 使用多线程解决管道执行顺序问题,防止死锁情况。
2、连接方式:
2.1 connect(PipedOutputStream src):将PipedInputStream连接到PipedOutputStream。
2.2 PipedInputStream():创建尚未连接的PipedInputStream。
2.3 PipedOutputStream():创建尚未连接的PipedOutputStream。
//管道流:PipedInputStream、PipedOutputStream
import java.io.*;
class Read implements Runnable
{
	private PipedInputStream in;
	Read(PipedInputStream in)
	{
		this.in = in;
	}
	public void run()
	{
		try
		{
			byte[] buf = new byte[1024];

			System.out.println("读取前。。。未阻塞");
			int len = in.read(buf);
			System.out.println("读取后。。。阻塞结束");

			String s = new String(buf,0,len);
			System.out.println(s);
			in.close();
		}
		catch (IOException e)
		{
			throw new RuntimeException("管道读取流失败");
		}	
	}
}
class Write implements Runnable
{
	private PipedOutputStream out;
	Write(PipedOutputStream out)
	{
		this.out = out;
	}
	public void run()
	{
		try
		{
			System.out.println("6秒后写入数据");
			//释放执行权
			Thread.sleep(6000);
			out.write("piped coming".getBytes());
			out.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("管道输出流失败");
		}
	}
}
class PipedStreamDemo  
{
	public static void main(String[] args) throws IOException
	{
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		//连接输入流和输出流
		in.connect(out);

		Read r = new Read(in);
		Write w = new Write(out);
		new Thread(r).start();
		new Thread(w).start();
	}
}
PS:
集合中涉及到IO流的是Properies;IO中涉及到多线程的是管道流。

八、其他

1、操作基本数据类型:DataInputStream、DataOutputStream:用于操作基本数据类型的流对象。
相关方法:
writeUTF(String str):以与机器无关方式使用UTF-8修改版编码将一个字符串写入基础输出流。
意味着:只能通过对应的方法读取数据,用转换流读不出来。
readUTF(String str):专门用于读取UTF-8修改版编码。
PS:
UTF-8修改版占8个字节;UTF-8占6个字节;gbk占4个字节。假如用其他方法读取UTF-8修改版编码,
会出现EOFException异常,因为它需要读取8个字节才能结束,用其他方法读取会提前结束。
//操作基本数据类型:DataInputStream、DataOutputStream
import java.io.*;
class DataStreamDemo 
{
	public static void main(String[] args) throws IOException 
	{
		writeData();
		readData();
		writeUTFDemo();
		readUTFDemo();
	}
	public static void readUTFDemo() throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));

		String s = dis.readUTF();

		System.out.println(s);
		dis.close();
	}
	public static void writeUTFDemo() throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utf.txt"));

		dos.writeUTF("你好");

		dos.close();
	}
	public static void readData() throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));

		//读取顺序要与写入顺序相同
		int num = dis.readInt();
		boolean b = dis.readBoolean();
		double d = dis.readDouble();

		System.out.println("num="+num);
		System.out.println("b="+b);
		System.out.println("d="+d);
		
		dis.close();
	}
	public static void writeData() throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

		dos.writeInt(255);
		dos.writeBoolean(true);
		dos.writeDouble(9885.1516);

		dos.close();
	}
}
2、操作字节数组
ByteArrayInputStream:在构造时,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream:在构造时,不用定义数据目的。因为该对象中已经在内部封装了可变长度的字节数组,
也就是数据目的地。其缓冲区随着数据的不断写入而自动增长。
特点:这两个流对象都操作的是数组,没使用系统资源,不用进行close关闭。
优点:增加了对象的封装性;代码的复用性;用流的读写思想来操作数组,简化了书写。
流操作规律:
源设备:
  键盘 System.in  硬盘 FileStream  内存 ArrayStream
目的设备:
  控制台 System.out 硬盘 FileStream  内存 ArrayStream

//操作字节数组:ByteArrayInputStream、ByteArrayOutputStream
import java.io.*;
class ByteArrayStream 
{
	public static void main(String[] args) 
	{
		//数据源
		ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());
	
		//数据目的
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		int by = 0;

		while((by=bis.read())!=-1)
		{
			bos.write(by);
		}
		//获取输出数组大小
		System.out.println(bos.size());
		System.out.println(bos.toString());
		//将此byte数组输出流的全部内容写入到指定的输出流参数中,会报出异常。
		//bos.writeTo(new FileOutputStream("a.txt"));
	}
}
3、操作字符数组:CharArrayReader、CharArrayWrite
4、操作字符串:StringReader、StringWriter

九、字符编码

1、编码表由来:
计算机只能识别二进制数据,早期由来是电信号。为了方便用于计算机,让它可以识别各个国家的文字。
就将各个国家的文字用数字来表示,并一一对应,形成一张表--->编码表。
2、分类
ASCII:美国标准信息交换码:用一个字节的7位表示;
ISO8859-1:拉丁码表(欧洲码表):用一个字节的8位表示;
GB2312:中国的中文编码表;两个字节表示一个字符,两个字节的高位都是1,兼容和避免了跟ASCII码重复;
GBK:升级版的中文编码表,融合了更多的中文文字符号;有两万左右字符;
Unicode:国际标准码,融合入了多种文字。所有文字都用两个字节来表示,java语言使用的就是Unicode;
UTF-8:按字符大小分配存储空间,最多三个字节表示一个字符,并在首字母加入标识符,便于区分。
3、字符编码
3.1字符流的出现为了方便操作字符,更重要的是加入了编码转换。
编码转换通过子类转换流来完成:InputStreamReader、OutputStreamWriter。
在两个对象进行构造的时候可以加入字符集。
//字符编码:编码转换:InputStreamReader、OutputStreamWriter
import java.io.*;
class EncodeStream 
{
	public static void main(String[] args) throws IOException
	{
		writeText();
		readText();
	}
	public static void readText() throws IOException
	{
		InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"UTF-8");

		char[] buf = new char[10];
		int len =isr.read(buf);

		String str = new String(buf,0,len);

		System.out.println(str);

		isr.close();
	}
	public static void writeText() throws IOException
	{
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");

		osw.write("你好");

		osw.close();
	}
}
3.2 编码:
字符串变字节数组:String-->byte[];  
byte[] getBytes(String charsetName):使用指定的字符集将此String解码为字节序列,并存储到数组中。
3.3 解码:
字节数组变字符串:byte[]-->String;  
String(byte[],charsetName):将数组变成字符串。

/*
编码:字符串变字节数组;解码:字节数组变字符串。
注意utf-8和GBK都识别中文的情况,因为都是别中文,它们之间解码会造成源码改变的情形。
*/
import java.util.*;
class  EncodeDemo
{
	public static void main(String[] args) throws Exception
	{
		String s = "你好";
		//编码
		byte[] b1 = s.getBytes("gbk");
		
		System.out.println(Arrays.toString(b1));
		//错误解码  因为ISO8859-1是拉丁码表,不包含中文码表,必然造成乱码。
		String s1 = new String(b1,"ISO8859-1");

		System.out.println("s1="+s1);

		//再编码  虽然s1是乱码,但也是一个字符对象,它的字节数组未变。
		//所以我们依旧可以通过字节数组获取正确的解码值。
		byte[] b2 = s1.getBytes("ISO8859-1"); 

		System.out.println(Arrays.toString(b2));

		//正确解码
		String s2 = new String(b2,"gbk");
		System.out.println("s2="+s2);		
	}
}
运行结果:

十、应用

/*
有五个学生,每个学生有3门课的成绩。
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhangsan,30,40,60计算出总成绩。
并把学生的信息和总分数从高到低存放在磁盘文件"stud.txt"中。

步骤:
1,描述学生对象。
2,定义一个可操作学生对象的工具类。

思路:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,学生有很多,那么就需要存储,使用到集合。要对学生的总分排序。可以使用TreeSet。
3,将集合的信息写入到一个文件中。
*/
import java.io.*;
import java.util.*;

class Student implements Comparable<Student>
{
	private String name;
	private int ma,cn,en;
	private int sum;
	Student(String name,int ma,int cn,int en)
	{
		this.name = name;
		this.ma = ma;
		this.cn = cn;
		this.en = en;
		sum = ma + cn + en;
	}
	//自然排序:返回负整数、零或正整数。根据此对象是小于、等于还是大于指定对象。
	public int compareTo(Student s) 
	{
		int num = new Integer(this.sum).compareTo(new Integer(s.sum));
		if(sum==0)
			return this.name.compareTo(s.name);
		return num;
	}
	public String getName()
	{
		return name;
	}
	public int getSum()
	{
		return sum;
	}
	public int hashCode()
	{
		return name.hashCode()+sum*78;//保证值的唯一性。
	}
	//判断是否是同一个人。
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Student))
			throw new ClassCastException("类型不匹配");
		Student s = (Student)obj;

		return this.name.equals(s.name) && this.sum==s.sum;
	}
	public String toString()
	{
		return "student["+name+","+ma+","+cn+","+en+"]";
	}
}
class StudentInfoTool
{
	//默认比较方式
	public static Set<Student> getStudents() throws IOException
	{
		return getStudents(null);
	}
	//比较器
	public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException
	{
		BufferedReader bufr =
			new BufferedReader(new InputStreamReader(System.in));

		String line = null;

		Set<Student> stus = null;
		if(cmp==null)
			stus = new TreeSet<Student>();
		else
			stus = new TreeSet<Student>(cmp);
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			String[] info = line.split(",");
			//解析一个字符串,并返回一个整数
			Student stu = new Student(info[0],Integer.parseInt(info[1]),
							Integer.parseInt(info[2]),
							Integer.parseInt(info[3]));

			stus.add(stu);
		}
		bufr.close();

		return stus;
	}
	public static void writeFile(Set<Student> stus) throws IOException
	{
		BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));

		for(Student stu : stus)
		{
			bufw.write(stu.toString() + "\t");
			bufw.write(stu.getSum() + "");
			bufw.newLine();
			bufw.flush();
		}
		bufw.close();
	}
}
class  StudentInfoTest
{
	public static void main(String[] args) throws IOException
	{
		//定义比较器,强行逆转
		Comparator<Student> cmp = Collections.reverseOrder();

		 Set<Student> stus = StudentInfoTool.getStudents(cmp);

		 StudentInfoTool.writeFile(stus);
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值