IO流
1.File
File 文件和目录(文件夹)路径名的抽象表示。
1.构造方法
1)File(String pathname) :参数就是指定的路径/如果没有指定路径(默认是在当前项目下)
通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
2)File(File parent, String child)
从父抽象路径名和子路径名字符串创建新的 File实例。
3)File(String parent, String child):参数1:父目录地址 参数2:具体的子文件地址
2.成员方法:
创建/删除/重名
1)public boolean createNewFile() throws IOException :表示创建文件 :如果不存在,则创建
2)public boolean mkdir():创建文件夹,如果不存在,则创建;否则就返回false
3)public boolean mkdirs():创建多个文件,如果父目录不存在,则创建
4)public boolean delete():删除文件或者文件夹(如果删除文件夹,文件夹必须为空目录)
5)public boolean renameTo(File dest):重命名
参数传递的修改的File对象
public class FileDemo {
public static void main(String[] args) throws IOException {
//表示:E盘下的demo文件夹中的a.txt文件
//File(String pathname) 方式1 (推荐)
// File file = new File("e://demo//a.txt") ;只是表示这个路径,如果创建a.txt文件,系统找不到指定路径
//File(File parent, String child) 方式2
/* File file2 = new File("E://demo") ;
File file3 = new File(file2,"a.txt") ;
//File(String parent, String child):方式3
File file4 = new File("E://demo","a.txt") ;*/
File file = new File("D:\\EE_2106\\day25\\code\\a.txt"); //绝对路径
File file2 = new File("aaa.txt");//没有带路径,就默认在当前项目下(相对路径)
File file3 = new File("D:\\EE_2106\\day25\\code\\demo") ;
File file4 = new File("aaa\\bbb\\ccc\\ddd") ;
// public boolean createNewFile()
System.out.println(file.createNewFile());
System.out.println(file2.createNewFile());
System.out.println(file3.mkdir());
System.out.println(file4.mkdirs());
System.out.println(file3.delete());
System.out.println(file.delete());
System.out.println("------------------------");
//D:\EE_2106\day25\code路径下logo.jpg :描述下这个地址File
// File srcFile = new File("D:\\EE_2106\\day25\\code\\logo.jpg") ;
File srcFile = new File("D:\\EE_2106\\day25\\code\\mv.jpg") ;
File destFile = new File("高圆圆.jpg") ;//当前项目路径下了
//public boolean renameTo(File dest)
System.out.println(srcFile.renameTo(destFile)) ;
}
}
3.判断功能
1)public boolean canRead()是否可读
2)public boolean canWrite()是否可写
3)public boolean exists():是否存在
4)public boolean isFile():是否是文件
5)public boolean isDirectory():是否是文件夹
6)public boolean isHidden():是否隐藏
4.高级获取功能:
1)public long length()
2)public String getName():获取抽象路径 名所表示的文件或者目录的名称
3)public File[] listFiles():获取某个目录下的所有的文件以及文件夹的File数组
4)public String[] list():获取某个抽象路径名所表示的文件以及目录的字符串数组
public class FileDemo2 {
public static void main(String[] args) {
//创建File对象,描述当前项目下的aaa.txt文件
File file = new File("aaa.txt") ;
System.out.println(file.canRead());
System.out.println(file.canWrite());
System.out.println(file.exists());
System.out.println(file.isDirectory());
//false
System.out.println(file.isFile());
System.out.println(file.isHidden());
System.out.println(file.length());
System.out.println(file.getName());
System.out.println("------------------------");
/*需求:获取
D盘下的所有的文件夹以及文件的名称....*/
// public File[] listFiles():获取某个目录下的所有的文件以及文件夹的File数组
//描述D盘
File file2 = new File("d://") ;
File[] fileArray = file2.listFiles();
//防止空指针异常
if(fileArray!=null){
for(File f :fileArray){
System.out.println(f.getName());
}
}
System.out.println("----------------------------------");
//public String[] list():获取某个抽象路径名所表示的文件以及目录的字符串数组
String[] strArray = file2.list();
if(strArray!=null){
for(String s:strArray){
System.out.println(s);
}
}
}
}
需求2:
获取D盘下所有的以.jpg结尾的文件
分析:
1)描述下D盘
2) public File[] listFiles():获取D盘下的所有的文件以及文件夹的File数组
2.1)对获取到的数组进行非判断
如果不为null,再去判断
2.2)判断File是一个文件
2.3)判断:文件必须以.jpg结尾
String类 endsWith(".jpg")
提供了另一个重载功能:
public File[] listFiles(FilenameFilter filter)
String[] list(FilenameFilter filter)
参数为:文件名称过滤器FilenameFilter:接口
成员方法:
boolean accept(File dir,String name):测试指定文件是否包含在文件列表中
返回如果true,将文件添加到文件列表中
1) 描述下D盘
2) public File[] listFiles(FilenameFilter filenamefilter):
获取D盘下的File数组的时候,就已经指定文件进行过滤...
public class FileTest {
public static void main(String[] args) {
//方法一
//1)描述D盘
File file = new File("D://") ;
//2)获取盘符下的所有的文件以及文件夹的File数组
File[] fileArray = file.listFiles();//这一步并没有直接获取到要的.jpg文件
//后面一些判断
if(fileArray!=null){
for(File f:fileArray){
//判断f是文件
if(f.isFile()){
//是文件
//判断它的名称是否以.jpg结尾
if(f.getName().endsWith(".jpg")){
System.out.println(f.getName());
}
}
}
}
System.out.println("-------------------------------------------------------");
//方法二
//描述下D盘
File srcFile = new File("D://") ;
//获取当前D盘下的所有文件以及文件夹File数组,并进行文件名过滤
//public File[] listFiles(FilenameFilter filter)
File[] files = srcFile.listFiles(new FilenameFilter() {//接口的匿名内部类
@Override
public boolean accept(File dir, String name) {
//返回true:表示将指定文件添加列表文件中
//描述文件所表示抽象路径File
File file = new File(dir, name);
//两个条件:file是文件并且file的文件名称以".jpg结尾"
return file.isFile() && file.getName().endsWith(".jpg");
}
});
if(files!=null){
for(File f :files){
System.out.println(f.getName());
}
}
}
}
2.IO流的分类
IO流的分类:
按流的方向:
输入和输出流
按类型分:
字节和字符流:字节先出现,后面在有字符流
再次流的方向划分
字节输入流:InputStream:表示输入字节流的所有类的超类(父类)
字节输出流:OutputStream:表示字节输出流的所有类的超类
字符输入流:Reader表示输入字符流的抽象类
字符输出流:Writer表示输出字符流的抽象类
字节/字符流都很多子类
XXXInputStream
XXXOutputStream
XXXReader
XXXWriter
3.字节流
1)OutputStream
字节输出流:OutputStream抽象类 子类进行实例化FileOutputStream:将指定的内容写到文件中 实现步骤: 1)创建文件输出流对象 :FileOutputStream(String name) :推荐:可以指定参数地址 FileOutputStream(File file) 写入文件末尾处构造函数:public FileOutputStream(File file,boolean append)throws FileNotFoundException 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。 2)写数据 public void write(int b) throws IOException 写一个字节 public void write(byte[] bytes) throws IOException 写一个字节数组 public void write(byte[] bytes,int off,int len) throws IOException:写一部分字节数组 3)关闭资源
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
//创建文件输出流对象
//指向某个盘符下的文件中(或者当前项目下)
FileOutputStream fos = new FileOutputStream("my.txt") ;//文件需要被创建当前项目下
//2)写数据
/* fos.write(97);
fos.write(98);
fos.write(99);*/
//写一个字节数组
/* byte[] bytes = {97,98,99,100,101,102} ;
// fos.write(bytes);
fos.write(bytes,2,2);*/
for(int x = 0 ; x < 10 ; x ++){
fos.write(("hello world").getBytes());
//windows操作系统 "\r\n"代表换换行
fos.write("\r\n".getBytes());
}
//3)关闭资源
fos.close(); //释放fos流对象所指向的my.txt的系统资源
}
}
public class FileOutputStreamDemo2 {
public static void main(String[] args) {
// method1() ;
method2() ;//
}
//标准方式:try...catch...finally
//统一处理
private static void method2() {
FileOutputStream fos = null ;
//alt+ctrl+t--->
try {
fos = new FileOutputStream("fos2.txt") ;
//写数据
fos.write("hello,我来了".getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//分别try...catch...(不用,这种方式阅读性很差)
private static void method1() {
//在当前项目下输出fos.txt
//创建字节输出流对象
FileOutputStream fos = null ;
try {
fos = new FileOutputStream("fos.txt") ;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//写数据
try {
fos.write("hello,IO".getBytes()) ;
} catch (IOException e) {
e.printStackTrace();
}
//关闭资源
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2)InputStream
字节输入流:InputStream 子类:FileInputStream实现步骤: 需要读取当前项目下的fis.txt文件 1)创建文件字节输入流对象,指向 fis.txt public FileInputStream(String name) throws FileNotFoundException 2)读内容 public int read(byte[] bytes) throws IOException一次读取一个字节数组 ,返回值值的是每次读取的实际字节数 read():一次读取一个字节 3)释放资源 读取文件的时候,一次读取一个字节,存在中文乱码, 在最终输出的结果 (char)by (场景:将某一个文件内容打印在控制台上了) 注意: "abc" 英文 读一个字节 a--->97 b --98 针对中文字符: 现在idea环境: 编码格式:utf-8格式: 一个中文对应三个字节 ,和前面的英文拼接出现问题,只要中文都会乱码 才出现了字符流(加入编码和解码的格式)
public class FileInputStreamDemo {
public static void main(String[] args) {
//创建一个字节输入流对象
FileInputStream fis = null ;
try {
// fis = new FileInputStream("fis.txt") ;
fis = new FileInputStream("DiGuiTest.java") ;
//读取当前项目下的DiGuiTest.java
//读取内容
//使用循环优化: 结束条件:获取的字节数为-1
//当前不知道循环多少次:while循环
//将判断,赋值一块去使用 (模板代码)
int by = 0 ; //字节数为0
while((by=fis.read())!=-1) {
System.out.print((char)by);
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class FileInputStreamDemo2 {
public static void main(String[] args) {
//创建字节输入流对象
FileInputStream fis = null ;
try {
fis = new FileInputStream("fis2.txt") ;
//一次读取一个字节数组
//创建一个数组:长度:1024或者1024的整数倍
byte[] buffer = new byte[1024] ;
//长度虽然1024个长度
int len = 0 ;
while((len=fis.read(buffer))!=-1){
//每次获取的从0开始获取实际字节长度
System.out.println(new String(buffer,0,len)) ;
//应该描述:每一次从0这个位置读取实际长度
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
读写复制操作:一次读取一个字节的方式 一次读取一个字节数组需求: 将当前项目下的DiGuiTest.java 的内容 复制到D盘下的Copy.java文件中分析: 源文件: 当前项目下 "DiGuiTest.java" 封装源文件:FileInputStraem(String pathname) 一次读取一个字节 目的地文件: D://Copy.java 封装目的地文件:FileOutputStream(String pathname) 一次写一个字节
public class CopyFileDemo {
public static void main(String[] args) {
long start = System.currentTimeMillis() ;//时间毫秒值
// method("DiGuiTest.java","D://Copy.java") ;
method2("DiGuiTest.java","D://Copy.java") ;
long end = System.currentTimeMillis() ;
System.out.println("共耗时:"+(end-start)+"毫秒");
}
private static void method2(String srcFile, String destFile) {
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
// 封装源文件:FileInputStraem(String pathname)
//字节输入流
fis = new FileInputStream(srcFile) ;
//字节输出流
// 封装目的地文件:FileOutputStream(String pathname)
fos = new FileOutputStream(destFile) ;
//读写复制操作
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=fis.read(bytes))!=-1){
//赋值
//fos流对象中写
//带上len的使用
fos.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fos.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 一次读取一个字节:读写复制
* @param srcFile 源文件
* @param destFile 目的地文件
*/
private static void method(String srcFile, String destFile) {
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
// 封装源文件:FileInputStraem(String pathname)
//字节输入流
fis = new FileInputStream(srcFile) ;
//字节输出流
// 封装目的地文件:FileOutputStream(String pathname)
fos = new FileOutputStream(destFile) ;
//读写复制操作
//一次读取一个字节
int by = 0 ;
while((by=fis.read())!=-1){
//没有读完,继续复制 :写一个字节
fos.write(by);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fos.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.缓冲字节流
1)BufferedInputStream
字节缓冲输入流构造方法
BufferedInputStream(InputStream in) :默认缓冲区大写 (足够大了) 提供一个指定的缓冲区大小 BufferedInputStream(InputStream in, int size)
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
//创建一个字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bos.txt")) ;
/**
* class BufferedInputStream{
*
* private static int DEFAULT_BUFFER_SIZE = 8192; //8kb
*
* public BufferedInputStream(InputStream in) {
* this(in, DEFAULT_BUFFER_SIZE);
* }
*
* public BufferedInputStream(InputStream in, int size) {
* super(in);
* if (size <= 0) {
* throw new IllegalArgumentException("Buffer size <= 0");
* }
* buf = new byte[size]; //底层还是一个字节数组
* }
* }
*/
//读内容
//一次读取一个字节
/* int by = 0 ;
while((by=bis.read())!=-1){
//打印控制台上
System.out.print((char)by);
}*/
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
}
}
2)BufferedInputStream
缓冲流的特点:提供缓冲区大写:1024的8倍 还是通过底层流提高读速度(FileInputStream)构造方法:
BufferedInputStream
BufferedOutputStream(OutputStream out) 推荐使用这个:默认缓冲区大小 足够大了 8kb
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size)
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
//BufferedOutputStream(OutputStream out) //参数父类:抽象类
//创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;
/**
* 源码:
* public BufferedOutputStream(OutputStream out) {
* this(out, 8192);
* }
* public BufferedOutputStream(OutputStream out, int size){
*super(out);
*if (size <= 0) {
*throw new IllegalArgumentException("Buffer size <= 0");
*}
*buf = new byte[size]; //创建了字节数组 byte[] buf = new byte[8192]
*}
*/
//写数据
//写一个字节数组
bos.write("hello.bufferedOutputStream".getBytes());
//释放资源
bos.close();
}
}
复制对比
读取D://a.mp4
将这个文件内容复制到 当前项目下的copy.mp4中
基本的字节流一次读取一字节
共耗时89021毫秒
基本的字节流一次读取一字节数组
共耗时116毫秒
字节缓冲流一次读取一个字节
共耗时348毫秒
字节缓冲流一次读取一个字节数组
共耗时47毫秒
public class CopyMp4 {
public static void main(String[] args) {
//起始时间
long start = System.currentTimeMillis() ;
// copyMp4("D://a.mp4","copy.mp4") ;
//copyMp4_2("D://a.mp4","copy.mp4") ;
//copyMp4_3("D://a.mp4","copy.mp4") ;
copyMp4_4("D://a.mp4","copy.mp4") ;
//结束时间
long end = System.currentTimeMillis() ;
System.out.println("共耗时"+(end-start)+"毫秒");
}
//缓冲流一次读取一个字节数组
private static void copyMp4_4(String srcFile, String destFile) {
BufferedInputStream bis = null ;
BufferedOutputStream bos = null ;
try {
bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;
//读写操作
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bos.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//缓冲流一次读取一个字节
private static void copyMp4_3(String srcFile, String destFile) {
BufferedInputStream bis = null ;
BufferedOutputStream bos = null ;
try {
bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;
//读写操作
int by = 0 ;
while((by=bis.read())!=-1){
bos.write(by);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bos.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//基本的字节流一次读取一个字节数组
public static void copyMp4_2(String srcFile,String destFile){
//封装源文件和目的地文件
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
fis = new FileInputStream(srcFile) ;
fos = new FileOutputStream(destFile) ;
//读写复制
byte[] bytes = new byte[1024] ;
int len = 0 ;//实际字节数
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
//强制输出流将缓冲的这字节数写出来
fos.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fos.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//基本的字节流一次读取一个字节
public static void copyMp4(String srcFile, String destFile) {
//封装源文件和目的地文件
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
fis = new FileInputStream(srcFile) ;
fos = new FileOutputStream(destFile) ;
//读写复制
int by = 0 ;
while((by=fis.read())!=-1){
fos.write(by);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fos.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.字符流
1)Writer–OutputStreamWriter
Writer:抽象类 (字符流的出现是在字节流的后面,可以解决中文乱码问题)
提供子类:字符转换输出流: 字节输出流通向字符输出流的桥梁!
构造方法
OutputStreamWriter(OutputStream out) :使用平台默认编码集写入数据
OutputStreamWriter(OutputStream out, String charsetName) :使用指定的字符集进行编码 写入数据 write write(String str)
write(int ch):写入字符: 字符--->对应的ASCII码表
write(char[] ch)
write(char[] ch,int off,int len)
write(String str,int off,int len)
public class WriterDemo {
public static void main(String[] args) throws Exception {
//创建字符缓冲输出流对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt")) ;//使用平台的默认编码集(utf-8)
//写入数据
/**
* write(String str)
* write(int ch):写入字符: 字符--->对应的ASCII码表
* write(char[] ch)
* write(char[] ch,int off,int len)
* write(String str,int off,int len)
*/
osw.write("hello,字符流我来了");
osw.write(97);
char[] chs = {'A','B','C','D','E'} ;
osw.write(chs);
osw.write(chs,2,2);
//使用flush():刷新字符输出流
osw.flush();
//关闭资源,释放相关的资源
osw.close();
//关闭之前,flush
//osw.write(98);
}
}
2)Reader–InputStreamReader
Reader:抽象类 具体的子类:字符转换输入流 InputStreamReader构造方法 InputStreamReader(InputStream in) :使用平台默认解码集进行读 InputStreamReader(InputStream in,String charset) :使用指定的解码集进行读
public class ReaderDemo {
public static void main(String[] args) throws Exception {
//创建字符转换输入流对象
//InputStreamReader(InputStream in,String charset) :使用指定的解码集进行读
// InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"),"UTF-8") ;
//InputStreamReader(InputStream in)
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"));
//平台默认的解码集进行读取
//读一个字符数组
char[] chs = new char[1024] ;
//实际字符数
int len = 0 ;
while((len=isr.read(chs))!=-1){
//打印控制台上:每次获取的实际长度
System.out.println(new String(chs,0,len));
}
//释放资源
isr.close();
}
}
3)便捷类
Reader/Writer子类:转换流
InputStreamReader(InputStream in)
OutputStreamWriter(OutputStream out)
他们不能直接去操作文件,jdk提供了这两种类型的便捷类,可以直接操作文件
FileReader(File file)
FileReader(String pathname)
FileWriter(File file)
FileWriter(String filename)
public FileWriter(File file,boolean append)throws IOException
根据给定的 File 对象构造一个 FileWriter 对象。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。 这两个类:使用的平台的默认编码和解码 (utf-8)
需求: 当前项目下的ReaderDemo.java 复制到 D://Demo.java 针对文本文件:优先采用字符流
6.字符缓冲流
1)BufferedReader
字符缓冲输入流: BufferedReader ---- 键盘录入数据 Scanner(InputSteram in)
String nextLine()
BufferedReader(Reader in)
Reader
InputStreamReader(InputStream in):转换流
如果直接进行读的操作(直接操作文件)FileReader(String pathName)
创建使用默认大小的输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int size) :指定缓冲区大小特有功能:
public String readLine() :一次读取一行内容
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
//创建字符缓冲输入流对象 :使用键盘录入数据!(流的方式)
// InputStreamReader(InputStream in):
//InputStream in = System.in ;
//标准输入流
// BufferedReader(Reader in)
//Reader reader = new InputStreamReader(in) ;
//BufferedReader br = new BufferedReader(reader) ;
//一步走
/* BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
System.out.println("请您输入一个字符串数据:");
// public String readLine()
String line = br.readLine();
//阻塞式方法
System.out.println("您输入的数据是:"+line);
System.out.println("------------------------------------------");*/
//使用BufferedReader读取bw.txt的文件内容
//readLine:读取一行内容
BufferedReader br2 = new BufferedReader(new FileReader("bw.txt")) ;
//定义变量
String line2 = null ;
while((line2=br2.readLine())!=null){
System.out.println(line2);
}
}
}
2)BufferedWriter
BufferedWriter:字符缓冲输出流
BufferedWriter(Writer out) :创建默认的缓冲区大小: 默认大小:defaultcharbuffersize:8192(默认值足够大) BufferedWriter(Writer out, int sz) :指定的大小
public void newLine() throws IOException:写入行的分隔符号特有功能:
利用BufferedReader的readLine()读取一行
利用BufferedWriter写一行,然后换行 public void newLine() throws IOException:
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
//输出bw.txt文件,并给里面写内容,而且去实现换行效果 write("\r\n")----现在可以使用newLine
//创建字符缓冲输出流对象
//BufferedWriter(Writer out) :
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
//写入数据
//写字符串/写字符
bw.write("hello");
//public void newLine()
bw.newLine();
bw.write("world");
bw.newLine();
bw.write("javaEE");
bw.newLine();
//刷新流
bw.flush();
//释放资源
bw.close();
}
}
3)Copy
BufferedReaer/BufferedWriter :读写复制操作
一次读取一个字符
一次读取一个字符数组特有功能:
利用BufferedReader的readLine()读取一行
利用BufferedWriter写一行,然后换行 将当前项目下的
ReaderDemo.java 复制到当前项目下:copy.java
一个文本文件读写复制:
阻塞流 (传统的IO流)
当一个线程如果操作的是读的动作, read(byte[] byte/char[] ..)/readLine():都属于阻塞式方法
另一个线程如果操作的是写的动作,读的线程如果开始读,这边写的线程才能开始进行写的复制操作!
基本的字节流:
一次读取一个字节/一次读取一个字节数组
字节缓冲流:
一次读取一个字节/一次读取一个字节数组
字符转换流:
InputStreamReader(InputStream in)
OutputStreamWriter(OutputStream out)
一次读取一个字符/一次读取一个字符数组
转换流的便捷类
FileReader(String name)
FileWriter(String writer)
一次读取一个字符/一次读取一个字符数组
public class CopyFile {
public static void main(String[] args) {
BufferedReader br = null ;
BufferedWriter bw = null ;
try {
//封装源文件:前项目下的ReaderDemo.java
br = new BufferedReader(new FileReader("ReaderDemo.java")) ;
bw = new BufferedWriter(new FileWriter("copy.java")) ;
//使用特有功能读取一行内容
String line = null ;
while((line=br.readLine())!=null){
//读取一行,bw写一行并换行,然后刷新
bw.write(line);
bw.newLine();
bw.flush();;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
bw.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7.SequenceInputStream
SequenceInputStream: 字节流的逻辑串联!
可以将两个或者是两个以上的文件进行读的操作 ,只能操作源文件构造方法
public SequenceInputStream(InputStream s1,InputStream s2)
参数1和参数2:分别要读取的字节输入流对象
1)合并两个文件
现在:合并流
当前项目a.java+b.java---->当前项目下的c.java文件中
将当前项目下的BufferedWriterDemo.java+copy.java文件---->复制到当前项目下b.java文件中
public class CopyMulFile {
public static void main(String[] args) throws Exception {
//创建两个字节输入流对象
InputStream is = new FileInputStream("BufferedWriterDemo.java") ;
InputStream is2 = new FileInputStream("copy.java") ;
//public SequenceInputStream(InputStream s1,InputStream s2)
//创建字节合并流
SequenceInputStream sis = new SequenceInputStream(is,is2) ;
//SequenceInputStream extends InputStream
//封装目的地文件:
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.java")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len = sis.read(bytes))!=-1){
bos.write(bytes,0,len); }
//释放资源
bos.close();
sis.close();
}
}
2)合并两个以上文件
public SequenceInputStream(Enumeration<? extends InputStream> e) :将两个以上的文件进行读取 Vector<InputStream> add(添加多个字节输入流对象)
需求:
BufferedWriterDemo.java
ReaderDemo.java
CopyMp4.java
复制到D://hello.java文件中
public class CopyFileDemo2
{
public static void main(String[] args) throws Exception {
//public SequenceInputStream(Enumeration<? extends InputStream> e)
//需要三个字节输入流对象
InputStream is1 = new FileInputStream("BufferedWriterDemo.java") ;
InputStream is2 = new FileInputStream("ReaderDemo.java") ;
InputStream is3 = new FileInputStream("CopyMp4.java") ;
//创建一个Vector集合
Vector<InputStream> vector = new Vector<>() ;
//添加流对象
vector.add(is1) ;
vector.add(is2) ;
vector.add(is3) ;
//public Enumeration<E> elements() ---- >类似于Collection的Iterator迭代器 Enumeration<InputStream> enumeration = vector.elements();
//创建合并流对象 封装源文件
SequenceInputStream sis = new SequenceInputStream(enumeration);
//创建字节缓冲输出流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d://hello.java")) ;
//一次读取一个字节
int by = 0 ;
while((by=sis.read())!=-1){
bos.write(by);
bos.flush();
}
bos.close();
sis.close();
}
}
8.序列化
序列化:ObjectOutputStream
将某个实体对象写入到流对象中---->将一个对象变成 流数据
构造方法
protected ObjectOutputStream()
protected ObjectOutputStream(OutputStream out) :
将某个对象通过底层的基本字节输出进行写的动作
public final void writeObject(Object obj) throws IOException
需求:将Person p = new Person("高圆圆",42) ;---->变成流对象 进行传输反序列化:ObjectInputStream
将流数据----还原成 "对象"
构造方法
public ObjectInputStream(InputStream in)
public final Object readObject() throws IOException, ClassNotFoundException
NotSerializableException:未实现序列化接口的异常
一个类在进行序列化的时候,(将类的对象写入序列化流中),标记接口Serializable 它有个特点
为当前这个Person进行编码(为类以及类的成员----->序列化化版本ID(签名):SerialversonUID)
java.io.InvalidClassException: com.qf.serailizable_05.Person; local class incompatible:
stream classdesc serialVersionUID = 2588921225545403990,
local class serialVersionUID = -862808041229244683
在进行序列化的时候:当前的 serialVersionUID:序列化版本id号 (假设100): 内存中生成一个id值
在进行反序列化的时候:将流--->对象 :
Person类的字段信息更改了(类的签名信息就会被更改),那么直接进行反序列化产生serialVersionUID=(假设200) 序列化版本id保证,
在idea中生成一个固定的序列版本id号 (一般都针对实体类)
一个实体类:
1)当前类必须为具体类 class 类名{}
2)必须存在私有字段(私有成员变量)
3)必须提供公共的setXXX()/getXXX()
4)如果当前类需要在网络中进行传输(分布式系统中)必须implements Serializable
idea- file--->setting---->editor---->Inspections----->java---->序列化serializion issue --->serializable class 打上对钩即可!
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// myWrite() ;
//写
myRead() ;
//读
}
//反序列化
private static void myRead() throws IOException, ClassNotFoundException {
//创建反序列化流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;
//读
//public final Object readObject()
Object object = ois.readObject();
//关闭流
ois.close();
System.out.println(object);
//toString()
//java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
//当从对象流读取的控制信息违反内部一致性检查时抛出。
}
//序列化
//将Person p = new Person("高圆圆",42) ;---->变成流对象 进行传输
private static void myWrite() throws IOException {
//创建Person对象
Person p = new Person("高圆圆",42) ;
//protected ObjectOutputStream(OutputStream out):创建一个序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ;
//将p对象写入到oos流对象中
//public final void writeObject(Object obj)throws IOException
oos.writeObject(p);
//释放资源
oos.close();
}
}public class Person implements Serializable {
//产生一个固定的序列化版本Id
private static final long serialVersionUID = 8988325735017562383L;
//常量值
String name ;
//姓名
private transient int age ;
//年龄
//transient :想让某个字段不参与序列化:这个字段(成员变量就使用transient)
public Person(){
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
对多个对象进行操作
class Person0 implements Serializable{
private static final long serialVersionUID = 9166507990237193254L;
private String name ;
private int age ;
public Person0() {
}
public Person0(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age; }
@Override
public String toString() {
return "Person0{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write();
read();
}
public static void read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));
Object obj =null;
while((obj=ois.readObject())!=null) {
System.out.println(obj);
}
ois.close();
}
private static void write() throws IOException {
//创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Object.txt"));
//创建对象
Person0 p1 = new Person0("Mike",22);
Person0 p2 = new Person0("Like",44);
//存入
oos.writeObject(p1);
oos.writeObject(p2);
oos.writeObject(null);
//加入null 用来判断是否到末尾,如果不加会报错EOFException
oos.close();
}
}
9.Properties
Properties extends Hashtable<K,V> ,它没有泛型,Key和Value都是String
表示一组持久的属性。 Properties可以保存到流中或从流中加载。
属性列表中的每个键及其对应的值都是一个字符串。
1)可以使用Map的功能
put(K ,V)
遍历:
keySet()通用
2)有自己特有功能添加元素和遍历
public Object setProperty(String key,String value):给属性列表中添加属性描述(key和value)
public Set<String> stringPropertyNames():获取属性列表中的所有的键
public String getProperty(String key):在属性列表中通过键获取值
public class PropertiesDemo {
public static void main(String[] args) {
//Properties() :空参构造
Properties properties = new Properties() ;
//利用Map集合的功能去添加元素
properties.put("高圆圆","赵又廷") ;
properties.put("文章","姚笛") ;
properties.put("文章","马伊琍") ;
properties.put("王宝强","马蓉") ;
//遍历
Set<Object> keySet = properties.keySet();
for(Object key :keySet){
//通过key获取value
Object value = properties.get(key);
System.out.println(key+"---"+value);
}
System.out.println("---------------------------------------");
/**
* 推荐Properties作为集合类 的遍历方式
*/
//创建一个空的属性列表
Properties prop = new Properties() ;
prop.setProperty("张三","30") ;
prop.setProperty("李四","40") ;
prop.setProperty("王五","35") ;
prop.setProperty("赵六","45") ;
//遍历:
Set<String> set = prop.stringPropertyNames();//获取所有键
for(String key:set){
String value = prop.getProperty(key);
System.out.println(key+"---"+value);
}
}
}
文件操作
Propeties:键和值都是字符串类型:可能需要在src(类所在的路径:类路径)作为一个配置文件
xxx.properties
将字节输入流或者字符输入中所在的文件中的内容加载属性列表中
void load(Reader reader)
void load(InputSteram in)
将Properties里面存储的内容----->保存到某个目录的xxx配置文件中 保存
public void store(Writer writer,String comments)
public void store(OutputStream out,String comments)
将属性列表中的内容保存到指定字节输出流/字符输出流所指向那个文件中
需求:
有一个文本文件
username.txt
key1=value1
key2=value2
....
...
将文本文件中的内容读取到属性列表中Properties 加载
将字节输入流或者字符输入中所在的文件中的内容加载属性列表中
void load(Reader reader)
void load(InputSteram in)
将Properties里面存储的内容----->保存到某个目录的xxx配置文件中 保存
public void store(Writer writer,String comments)
public void store(OutputStream out,String comments)
将属性列表中的内容保存到指定字节输出流/字符输出流所指向那个文件中
public class PropertiesDemo2 {
public static void main(String[] args) throws IOException {
// myLoad() ;
myStore() ;
}
//将属性集合类中的内容 ---保存到指定文件中
private static void myStore() throws IOException {
Properties prop = new Properties() ;
//添加key-value
prop.setProperty("张三丰","56") ;
prop.setProperty("吴奇隆","60") ;
prop.setProperty("成龙","65") ;
prop.setProperty("刘德华","70") ;
//public void store(Writer writer,String comments):
//参数2:给当前属性列表中加入一个描述
//保存指定的文件中
prop.store(new FileWriter("user.txt"),"name's list'");
System.out.println("保存完毕");
}
//加载:需要将names.txt文件加载到属性列表中,然后遍历
private static void myLoad() throws IOException {
//创建一个空的属性列表
Properties prop = new Properties() ;
System.out.println(prop);
//void load(Reader reader)
// Reader r = new FileReader("names.txt") ;//names.txt是在当前项目下路径下
// prop.load(r) ;
//读取src路径下的names.properties (类路径)
//使用步骤
//1)获取当前类所在的字节码文件对象
Class clazz = PropertiesDemo2.class ;
//2)获取当前类所在的类加载器Class:public ClassLoader getClassLoader()
ClassLoader classLoader = clazz.getClassLoader();
//3)在类加载器中:获取当前资源文件(配置文件names.proprites)所在的输入流对象
//public InputStream getResourceAsStream(String name) {
InputStream inputStream = classLoader.getResourceAsStream("names.properties");
//将inputStream加载到属性集合类中
// void load(InputSteram in)
prop.load(inputStream);
System.out.println(prop);
Set<String> keySet = prop.stringPropertyNames();
for(String key:keySet){
String value = prop.getProperty(key);
System.out.println(key+"---"+value);
}
}
}
xxx.txt操作
/* 有一个文本文件user.txt,如果里面键是lisi,
* 然后将他的年龄改为45,然后重新写入到user.txt文件中
*
* user.txt
* zhangsan=30
* lisi=40
* wangwu=50
*/
public class Test3 {
public static void main(String[] args) throws IOException {
//创建一个属性集合列表:Properties
Properties prop = new Properties() ;
//空的,需要将user.txt内容加载到属性列表中
Reader r = new FileReader("user.txt") ;
prop.load(r);
//System.out.println(prop);
//遍历属性集合列表,获取每一个元素
Set<String> keySet = prop.stringPropertyNames();
for (String key : keySet) {
//获取每一个元素 :键
//判断如果"lisi"和key一致,那么改变它的值
if("lisi".equals(key)){
//通过属性列表设置
prop.setProperty(key,"45") ;
}
}
//将修改后的内容,重写在保存到user.txt文件中
Writer w = new FileWriter("user.txt") ;
prop.store(w,"name's lis"); //参数2:对属性列表的信息面搜
}
}
网络编程
网络编程------>Socket编程
特点:
发送端/客户端
接收端/服务器端 这两端必须存在Socket对象
网络编程的三要素:
举例:
我要找到高圆圆 说一句话
1)ip:
知道高圆圆的ip地址
2)port 0-65535 (0-1024属于保留端口)
知道高圆圆的端口号
3)规定一种协议
协议:
udp协议
1)不需要建立连接通道
2)属于一种不可靠连接
4)执行效率高 (不同步的)
TCP/Ip协议
1)建立连接通道
2)属于安全连接(可靠连接)
3)发送文件(使用基本字节流),相对udp协议来说没有限制
4)执行效率低(同步的)
InetAddress:互联网ip地址
获取InetAddress:ip地址对象
public static InetAddress getByName(String host):参数:主机名称(计算机电脑名称)
public String getHostAddress():获取ip地址字符串形式
public class NetDemo {
public static void main(String[] args) throws UnknownHostException {
//如何获取自己电脑上的ip地址----》String形式!
//10.12.156.107
InetAddress inetAddress = InetAddress.getByName("DESKTOP-Q62EUJH");
String ip = inetAddress.getHostAddress();
System.out.println(ip);
//10.12.156.107
}
}
1.udp通信
/* Udp编程的发送端:
* 1)创建Socket对象
* 2)发送内容 :内容数据一种数据报文(报包)DatagramPacket
* 3)释放资源
*/
public class UdpSend {
public static void main(String[] args) throws IOException {
//1)创建Socket对象
//DatagramSocket:发送和接收数据报数据包的套接字。
//public DatagramSocket() throws SocketException
DatagramSocket ds = new DatagramSocket() ;
//2)创建一个数据报包对象DatagramPacket
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
//参数1:当前数据的字节数组
//参数2:当前数据的长度
//参数3:InetAddress:ip地址对象
//参数4:绑定的端口号
byte[] bytes = "hello,马三奇".getBytes() ;
int length = bytes.length ;
//public static InetAddress getByName(String host)
InetAddress inetAddress = InetAddress.getByName("10.12.156.107");
int port = 12306 ;
DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;
//3)发送数据报包
//public void send(DatagramPacket p)
ds.send(dp);
//4)释放资源
ds.close();
}
}
/* Udp接收端
* 1)创建Socket对象
* 2)创建一个接收容器:数据报包:DatagramPacket
* 3)接收
* 4)解析容器的的实际数据大小
* 5)展示发送端发送的数据
*/
public class UdpReceive {
public static void main(String[] args) throws IOException {
//)创建Socket对象
//public DatagramSocket(int port)
DatagramSocket ds = new DatagramSocket(12306) ;
//2)创建一个接收容器:数据报包:DatagramPacket 实际数据没有这么大
//public DatagramPacket(byte[] buf, int length)
//自定义字节缓冲区
byte[] bytes = new byte[1024] ;
int lentgth = bytes.length ;
DatagramPacket dp = new DatagramPacket(bytes,lentgth);
//3)public void receive(DatagramPacket p)
ds.receive(dp);
//4)解析实际数据
//byte[] getData() 获取实际字节数
//返回数据缓冲区。
//int getLength() 获取实际长度
byte[] bytes2 = dp.getData();
int length2 = dp.getLength();
String receiverStr = new String(bytes2,0,length2) ;
//获取ip地址
//InetAddress getAddress()
//InetAddress
//public String getHostAddress():
String ip = dp.getAddress().getHostAddress();
//展示数据
System.out.println("data from "+ip+" ,content is :"+receiverStr);
}
}
发送端不断键盘录入数据,接收端不断接收数据,然后展示内容
接收端端不断的接收数据
接收端一般不关闭
接收端只能开启一次,多次就出现 BindException:绑定一次
Address already in use: Cannot bind:端口号被占用!
public class UdpSend {
public static void main(String[] args) {
DatagramSocket ds = null ;
try {
//创建发送端的Socket
ds = new DatagramSocket() ;
//键盘录入数据
//IO流的方式
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
//使用readLine()读取一行内容
String line = null ;
while((line=br.readLine())!=null){ //readLine():阻塞式方法
//自定义结束条件
if("886".equals(line)){
break ;
}
//创建数据报包
DatagramPacket dp = new DatagramPacket(line.getBytes(),
line.getBytes().length,
InetAddress.getByName("10.12.156.107"),6666);
//发送数据报包
ds.send(dp);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
ds.close();
}
}
}
public class UdpReceive {
public static void main(String[] args) {
//创建接收端的Socket
try {
DatagramSocket ds = new DatagramSocket(6666) ;
while(true){
//创建一个接收容器
byte[] bytes = new byte[1024] ;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length) ;
//接收
ds.receive(dp);
//解析实际数据
String receiveStr = new String(dp.getData(), 0, dp.getLength());
//获取ip
String ip = dp.getAddress().getHostAddress();
//展示数据
System.out.println("data from "+ip+"data is--->"+receiveStr);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//接收不关闭
//接收socket对象一一直开着
}
}
}
2.TCP通信
TCP特点:需要建立连接通道(就是以一种字节流的方式写入,读取)
什么时候建立连接(服务器端如果监听到端口,客户端就立即和服务器端建立连接!)
TCP客户端写数据
1)创建TCP客户端的Socket对象
2)写数据
3)释放资源
TCP服务器端的实现步骤:
1)创建服务器端的Socket对象
public ServerSocket(int port)throws IOException
2)监听客户端连接
public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象
3)获取通道内的输入流
public InputStream getInputStream() throws IOException
4)读取数据:一次读取一个字节数组
5)展示数据 而且获取ip地址
//public InetAddress getInetAddress()
public class ScoketDemo {
public static void main(String[] args) throws IOException {
//1)创建TCP客户端的Socket对象
// public Socket(String host, int port)throws UnknownHostException, IOException
//参数1:主机名称/或者ip地址字符串形式
//参数2:指定的端口(0-65535(不包含0-1024))
Socket s = new Socket("10.12.156.107",8888) ;
//2)写数据
//public OutputStream getOutputStream()throws IOException
OutputStream out = s.getOutputStream();//获取通道内的输出流对象
out.write("hello,TCP".getBytes());
//读取服务器端反馈的数据
//3)释放资源
s.close();
}
}
/* TCP服务器端的实现步骤:
1)创建服务器端的Socket对象
public ServerSocket(int port)throws IOException
2)监听客户端连接
public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象
3)获取通道内的输入流
public InputStream getInputStream() throws IOException
4)读取数据:一次读取一个字节数组
5)展示数据 而且获取ip地址
//public InetAddress getInetAddress() */
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1)创建服务器端的Socket对象
//public ServerSocket(int port)throws IOException
ServerSocket ss = new ServerSocket(8888) ;
System.out.println("服务器正在等待连接...");
//2)监听客户端连接
// public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象
Socket socket = ss.accept(); //阻塞式方法
//3)取通道内的输入流
//public InputStream getInputStream() throws IOException
InputStream inputStream = socket.getInputStream();
//4)读取数据:一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
//获取内容
String clientStr = new String(bytes,0,len) ;
//再去反馈数据
//5)获取ip
String ip = socket.getInetAddress().getHostAddress();
//展示数据
System.out.println("data from "+ip+" content is--->"+clientStr);
//关闭
ss.close();
}
}
一个有回复的TCP通信
public class ScoketDemo {
public static void main(String[] args) throws IOException {
//1)创建TCP客户端的Socket对象
// public Socket(String host, int port)throws UnknownHostException, IOException
//参数1:主机名称/或者ip地址字符串形式
//参数2:指定的端口(0-65535(不包含0-1024))
Socket s = new Socket("10.12.156.107",8888) ;
//2)写数据
//public OutputStream getOutputStream()throws IOException
OutputStream out = s.getOutputStream();//获取通道内的输出流对象
out.write("hello,TCP".getBytes());
//读取服务器端反馈的数据
//获取通道内的输入流
InputStream in = s.getInputStream();
//读
byte[] bytes = new byte[1024] ;
int len = in.read(bytes);
String fkMsg = new String(bytes,0,len) ;
System.out.println("fkMsg:"+fkMsg);
//3)释放资源
s.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1)创建服务器端的Socket对象
//public ServerSocket(int port)throws IOException
ServerSocket ss = new ServerSocket(8888) ;
System.out.println("服务器正在等待连接...");
//2)监听客户端连接
// public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象
Socket socket = ss.accept(); //阻塞式方法
//3)取通道内的输入流
//public InputStream getInputStream() throws IOException
InputStream inputStream = socket.getInputStream();
//4)读取数据:一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
//获取内容
String clientStr = new String(bytes,0,len) ;
//服务器给客户端再去反馈数据,
//获取通道内的输出流
OutputStream outputStream = socket.getOutputStream();
//写入到通道内的流对象中
outputStream.write("ok,数据已经收到".getBytes());
outputStream.flush();
//5)获取ip
String ip = socket.getInetAddress().getHostAddress();
//展示数据
System.out.println("data from "+ip+" content is--->"+clientStr);
//关闭
ss.close();
}
}
可以连续发送的TCP通信
客户端:
1)TCP客户端不断键盘录入数据,服务器端不断接收数据,然后展示数据
2)TCP客户端文本文件(XXX.java文件)----->服务器端将文件进行读写复制,输出到当前项目的Copy.java文件中
BuferedReader
3)TCP客户端文本文件(XXX.jpg文件)----->服务器端将文件进行读写复制,输出到当前项目的Copy.jpg文件中
BufferedInputStream
public class ClientDemo {
public static void main(String[] args) {
//创建客户端的Socket
Socket socket = null ;
try {
socket = new Socket("10.12.156.107",10010) ;
//创建BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
//录入一行
//读取一行
String line = null ;
while((line=br.readLine())!=null){//null只是作为 一个文件是否读完
if("over".equals(line)){
break ;
}
//获取通道内的输出流(字节流OutputStream)
//封装通道内字节输出流
//分步走
//OutputStream outputStream = socket.getOutputStream(); //字节流---->字符流
//创建字符缓冲输出流
//BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)) ;
//一步走
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;
//给封装的通道内的流对象bw写一行
bw.write(line);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//需要获取服务器端的反馈数据 (已经读完完毕了)
}
}
//服务器端不断读取数据并展示
public class ServerDemo {
public static void main(String[] args) {
//创建服务端的Socket,一直开启
ServerSocket ss = null ;
try {
ss = new ServerSocket(10010) ;
//ArrayList<Socket>:ArrayList存储一个列表:都是多个客户端 对象
while(true){
System.out.println("服务器正在等待连接");
Socket socket = ss.accept();
//不断监听,不断展示数据
//获取通道内的输入流(InputStream)
//封装通道内的输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null){ //阻塞式方法
// //展示数据
System.out.println(line);
}
//复制完毕之后,给客户端反馈
}
} catch (IOException e) {
e.printStackTrace();
}
//服务器端不关闭
}
}
发送文本文件并有反馈
客户端的一个文本文件,服务器端进行复制到指定的某个文件中 ,复制完毕了,服务器端需要给客户端反馈!(反馈的消息,客户端能不能获取到)
加入反馈操作:出现客户端和服务器端互相等待的情况--->但是文件已经复制完毕!
针对服务器端:不知道客户端是否还需要从通道内的输出流对象中写入数据(此时文件读写复制结束条件:只是null)
文件读完毕的条件是null,但是TCP通过流的方式 要进行结束; 服务器端不知道客户端是否还需要写入数据,客户端等待着服务器反馈的数据!
解决方案:
1)自定义结束条件
在客户端读完文件中,通知一下服务器端
写入一行内容("886/over"),服务器端只要读取到886或者over
2)可以使用客户端Socket的一个方法 标记(通知服务器端,客户端已经没有数据输出了)
public void shutdownOutput()throws IOException
public class ClientTest {
public static void main(String[] args) throws IOException {
//将当前项目下的Test.java 客户端将文件写入到 服务器端,服务器端进行复制
//创建客户端Socket对象
Socket socket = new Socket("10.12.156.107",10086) ;
//创建BufferedReader流对象,去操作当前项目下的Test.java文件
BufferedReader br = new BufferedReader(new FileReader("Test.java")) ;
//获取通道内字节输出流,写数据----封装通道内的字节流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;
//一次读取一行,然后给通道内的流对象中写入过去
String line = null ;
while((line=br.readLine())!=null){//阻塞式方法
bw.write(line);
bw.newLine();
bw.flush();
}
//方案1 :自定义结束条件:客户端通道内的流中已经没有数据了
/*bw.write("over");
bw.newLine();
bw.flush();*/
//方案2:使用socket对象的方法
socket.shutdownOutput(); //禁用套接字流输出,通知服务器端 客户端已经不会在写入数据了
//客户端读取反馈
//获取通道内字节输入流对象
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);//阻塞方法
String fkMsg = new String(bytes,0,len) ;
System.out.println("客户端到读取的反馈数据是:"+fkMsg);
//关闭
br.close();
socket.close();
}
}
/*
服务器端复制Test.java到当前项目下的Copy.java文件中
*/
public class ServerTest {
public static void main(String[] args) throws IOException {
//创建服务器端Socket
ServerSocket ss = new ServerSocket(10086) ;
Socket socket = ss.accept();
//创建BufferedReader去读取通道内写入过来的数据,封装通道内的流对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
//创建字符缓冲输出流,写入数据,输出到当前项目下的Copy.java文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("copy.java")) ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null){//阻塞方法 :可能客户端的文件已经null了,但是服务器端不知道!
/* if("over".equals(line)){
break ;
}*/
bw.write(line);
bw.newLine();
bw.flush();
}
//加入反馈操作
//继续通道内输出流对象
OutputStream outputStream = socket.getOutputStream();
outputStream.write("数据已经收到了,已经复制完毕".getBytes());
outputStream.flush();
//释放资源
bw.close();
ss.close();
}
}
客户端的图片文件
/*
客户端的图片文件,服务器端将图片进行复制,并反馈给客户端
*/
public class ClientImgDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket
Socket socket = new Socket("10.12.156.107",12306) ;
//创建BuferedInputStream 读取图片文件 :d://mm.jpg
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d://mm.jpg")) ;
//写入到通道内流中同时 封装通道内的流
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()) ;
//一次读取一个字节数组
byte[] buffer = new byte[1024] ;
int len = 0 ;
while((len=bis.read(buffer))!=-1){
//写
bos.write(buffer,0,len);
bos.flush(); //强制刷新,将缓冲的字节数全部写出
}
//通知服务器端,通道内输出流对象没有数据了
socket.shutdownOutput();
//读取反馈
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024] ;
int length = inputStream.read(bytes);
System.out.println("反馈内容为:"+new String(bytes,0,length));
//关闭资源
bis.close();
socket.close();
}
}
/*
* 服务器端将图片进行复制,并反馈给客户端
* 将图片文件写入到当前项目下的高圆圆.jpg
*/
public class ServlerImgDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12306) ;
//监听
Socket socket = ss.accept() ;
//读取:封装通道内的字节输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()) ;
//输出
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("高圆圆.jpg")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){ //阻塞式方法
bos.write(bytes,0,len);
bos.flush();
}
//加入反馈
OutputStream outputStream = socket.getOutputStream();
outputStream.write("图片文件复制完毕".getBytes());
outputStream.flush();
//释放资源
bos.close();
ss.close();
}
}