04.JavaIO流问题

目录介绍

  • 4.0.0.1 说一下Java IO里面的常见类,字节流,字符流、接口、实现类、方法阻塞?
  • 4.0.0.2 什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别?
  • 4.0.0.3 字符流和字节流有什么区别?如何选择字节流或者字符流?什么是缓冲区,有什么做用?
  • 4.0.0.4 IO流中用到哪些模式?谈一谈IO流中用到的适配器模式和装饰者模式的做用优点?
  • 4.0.0.5 说一下对NIO的理解?NIO和IO的主要区别?NIO和IO如何影响应用程序的设计?
  • 4.0.0.9 如何实现对象克隆?克隆有哪些方式?深克隆和浅克隆有何区别?深克隆和浅克隆分别说的是什么意思?
  • 4.0.1.0 浅拷贝会建立新的对象吗?对于基本类型拷贝的是什么?怎么样实现浅拷贝,浅拷贝先后对象地址值会同样吗?
  • 4.0.1.1 对字节流进行大量的从硬盘读取,要用那个流,为何?有什么须要注意的问题?

好消息

  • 博客笔记大汇总【15年10月到至今】,包括Java基础及深刻知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,固然也在工做之余收集了大量的面试题,长期更新维护而且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
  • 连接地址:github.com/yangchong21…
  • 若是以为好,能够star一下,谢谢!固然也欢迎提出建议,万事起于忽微,量变引发质变!全部博客将陆续开源到GitHub!

4.0.0.1 说一下Java IO里面的常见类,字节流,字符流、接口、实现类、方法阻塞?

  • 输出流和输入流
    • 输入流就是从外部文件输入到内存,输出流主要是从内存输出到文件。
  • IO里面常见的类
    • IO流中有不少类,IO流主要分为字符流和字节流。字节流中有抽象类InputStream和OutputStream,它们的子类FileInputStream,FileOutputStream,BufferedOutputStream等。字符流BufferedReader和Writer等。都实现了Closeable, Flushable, Appendable这些接口。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。
  • IO流中方法阻塞
    • java中的阻塞式方法是指在程序调用改方法时,必须等待输入数据可用或者检测到输入结束或者抛出异常,不然程序会一直停留在该语句上,不会执行下面的语句。好比read()和readLine()方法。
    • 技术博客大总结

4.0.0.2 什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别?

  • 什么是比特(Bit)?
    • Bit最小的二进制单位 ,是计算机的操做部分 取值0或者1
  • 什么是字节
    • Byte是计算机操做数据的最小单位由8位bit组成 取值(-128-127)
  • 什么是字符
    • Char是用户的可读写的最小单位,在Java里面由16位bit组成 取值(0-65535)
  • 各有什么区别
    • Bit 是最小单位 计算机 只能认识 0或者1

4.0.0.3 字符流和字节流有什么区别?如何选择字节流或者字符流?什么是缓冲区,有什么做用?

  • 字符流和字节流区别
    • 把二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一片二进制数据,无论输入输出设备是什么,咱们要用统一的方式来完成这些操做,用一种抽象的方式进行描述,这个抽象描述方式起名为IO流,对应的抽象类为OutputStream和InputStream ,不一样的实现类就表明不一样的输入和输出设备,它们都是针对字节进行操做的。
    • 在应用中,常常要彻底是字符的一段文本输出去或读进来,用字节流能够吗?计算机中的一切最终都是二进制的字节形式存在。对于“中国”这些字符,首先要获得其对应的字节,而后将字节写入到输出流。读取时,首先读到的是字节,但是咱们要把它显示为字符,咱们须要将字节转换成字符。因为这样的需求很普遍,人家专门提供了字符流的包装类。
    • 底层设备永远只接受字节数据,有时候要写字符串到底层设备,须要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为咱们向IO设别写入或读取字符串提供了一点点方便。
    • 技术博客大总结
    • 字符流和字节流的使用很是类似,可是实际上字节流的操做不会通过缓冲区(内存)而是直接操做文本自己的,而字符流的操做会先通过缓冲区(内存)而后经过缓冲区再操做文件。
  • 如何选择字节流或者字符流?
    • 字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的
    • 若是是音频文件、图片、歌曲,就用字节流好点(避免数据丢失)
    • 若是是关系到中文(文本)的,用字符流好点)
  • 什么是缓冲区,有什么做用?
    • 缓冲区就是一段特殊的内存区域,不少状况下当程序须要频繁地操做一个资源(如文件或数据库)则性能会很低,因此为了提高性能就能够将一部分数据暂时读写到缓存区,之后直接今后区域中读写数据便可,这样就显著提高了性能。
    • 对于 Java 字符流的操做都是在缓冲区操做的,因此若是咱们想在字符流操做中主动将缓冲区刷新到文件则可使用 flush() 方法操做。

4.0.0.4 IO流中用到哪些模式?谈一谈IO流中用到的适配器模式和装饰者模式的做用优点?

  • IO流中用到哪些模式
    • 大概有装饰者模式和适配器模式!
    • 要知道装饰者模式和适配器模式的做用;其次,能够本身举个例子把它的做用生动形象地讲出来;最后,简要说一下要完成这样的功能须要什么样的条件。
  • 谈一谈IO流中用到的适配器模式和装饰者模式的做用优点
    • 装饰器模式:就是动态地给一个对象添加一些额外的职责(对于原有功能的扩展)。
      //把InputStreamReader装饰成BufferedReader来成为具有缓冲能力的Reader。
      BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
      复制代码
      • 1.它必须持有一个被装饰的对象(做为成员变量)。
      • 2.它必须拥有与被装饰对象相同的接口(多态调用、扩展须要)。
      • 3.它能够给被装饰对象添加额外的功能。
      • 好比,在io流中,FilterInputStream类就是装饰角色,它实现了InputStream类的全部接口,并持有InputStream的对象实例的引用,BufferedInputStream是具体的装饰器实现者,这个装饰器类的做用就是使得InputStream读取的数据保存在内存中,而提升读取的性能。
    • 适配器模式:将一个类的接口转换成客户指望的另外一个接口,让本来不兼容的接口能够合做无间。
      //把FileInputStream文件字节流适配成InputStreamReader字符流来操做文件字符串。
      FileInputStream fileInput = new FileInputStream(file); 
      InputStreamReader inputStreamReader = new InputStreamReader(fileInput);
      复制代码
      • 1.适配器对象实现原有接口
      • 2.适配器对象组合一个实现新接口的对象
      • 3.对适配器原有接口方法的调用被委托给新接口的实例的特定方法(重写旧接口方法来调用新接口功能。)
      • 好比,在io流中,InputStreamReader类继承了Reader接口,但要建立它必须在构造函数中传入一个InputStream的实例,InputStreamReader的做用也就是将InputStream适配到Reader。InputStreamReader实现了Reader接口,而且持有了InputStream的引用。这里,适配器就是InputStreamReader类,而源角色就是InputStream表明的实例对象,目标接口就是Reader类。
      • 适配器模式主要在于将一个接口转变成另外一个接口,它的目的是经过改变接口来达到重复使用的目的;而装饰器模式不是要改变被装饰对象的接口,而是保持原有的接口,可是加强原有对象的功能,或改变原有对象的方法而提升性能。
  • 用到设计模式优点
    • 装饰者模式就是给一个对象增长一些新的功能,并且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例(各类字符流间装饰,各类字节流间装饰)。
    • 技术博客大总结
    • 适配器模式就是将某个类的接口转换成咱们指望的另外一个接口表示,目的是消除因为接口不匹配所形成的类的兼容性问题(字符流与字节流间互相适配)。

4.0.0.5 说一下对NIO的理解?NIO和IO的主要区别?NIO和IO如何影响应用程序的设计?

  • 说一下对NIO的理解?
    • 传统的IO流是阻塞式的,会一直监听一个ServerSocket,在调用read等方法时,它会一直等到数据到来或者缓冲区已满时才返回。调用accept也是一直阻塞到有客户端链接才会返回。每一个客户端链接过来后,服务端都会启动一个线程去处理该客户端的请求。而且多线程处理多个链接。每一个线程拥有本身的栈空间而且占用一些CPU时间。每一个线程遇到外部未准备好的时候,都会阻塞掉。阻塞的结果就是会带来大量的进程上下文切换。
    • 对于NIO,它是非阻塞式,核心类:
      • 1.Buffer为全部的原始类型提供 (Buffer)缓存支持。
      • 2.Charset字符集编码解码解决方案
      • 3.Channel一个新的原始I/O抽象,用于读写Buffer类型,通道能够认为是一种链接,能够是到特定设备,程序或者是网络的链接。
  • NIO和IO的主要区别?
    • 主要区别
      IO 	NIO
      面向流 	面向缓冲
      阻塞IO 	非阻塞IO
      无 	选择器
      复制代码
    • 面向流与面向缓冲
      • Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。JavaIO面向流意味着每次从流中读一个或多个字节,直至读取全部字节,它们没有被缓存在任何地方。此外,它不能先后移动流中的数据。若是须要先后移动从流中读取的数据,须要先将它缓存到一个缓冲区。JavaNIO的缓冲导向方法略有不一样。数据读取到一个它稍后处理的缓冲区,须要时可在缓冲区中先后移动。这就增长了处理过程当中的灵活性。可是,还须要检查是否该缓冲区中包含全部您须要处理的数据。并且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里还没有处理的数据。
    • 阻塞与非阻塞IO
      • Java IO的各类流是阻塞的。这意味着,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取,或数据彻底写入。该线程在此期间不能再干任何事情了。JavaNIO的非阻塞模式,使一个线程从某通道发送请求读取数据,可是它仅能获得目前可用的数据,若是目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,因此直至数据变的能够读取以前,该线程能够继续作其余的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不须要等待它彻底写入,这个线程同时能够去作别的事情。线程一般将非阻塞IO的空闲时间用于在其它通道上执行IO操做,因此一个单独的线程如今能够管理多个输入和输出通道(channel)。
    • 选择器
      • Java NIO的选择器容许一个单独的线程来监视多个输入通道,你能够注册多个通道使用一个选择器,而后使用一个单独的线程来“选择”通道:这些通道里已经有能够处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
  • NIO和IO如何影响应用程序的设计?
    • 不管您选择IO或NIO工具箱,可能会影响您应用程序设计的如下几个方面:技术博客大总结
      • 1.对NIO或IO类的API调用。
      • 2.数据处理。
      • 3.用来处理数据的线程数。
    • API调用
      • 固然,使用NIO的API调用时看起来与使用IO时有所不一样,但这并不意外,由于并非仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
    • 数据处理
      • 使用纯粹的NIO设计相较IO设计,数据处理也受到影响。在IO设计中,咱们从InputStream或 Reader逐字节读取数据。
      • 请注意处理状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道确定文本行就已读完, readline()阻塞直到整行读完,这就是缘由。你也知道此行包含名称;一样,第二个readline()调用返回的时候,你知道这行包含年龄等。正如你能够看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。

4.0.0.9 如何实现对象克隆?克隆有哪些方式?深克隆和浅克隆有何区别?深克隆和浅克隆分别说的是什么意思?

  • 如何实现对象克隆,有两种方式:
    • 1.实现Cloneable接口并重写Object类中的clone()方法;
    • 2.实现Serializable接口,经过对象的序列化和反序列化实现克隆,能够实现真正的深度克隆,代码以下。
    • 注意:若是不知足上面两个条件之一,那么就不能进行克隆的!
  • 克隆有哪些方式?
    • 克隆(复制)在Java中是一种常见的操做,目的是快速获取一个对象副本。克隆分为深克隆和浅克隆。
    • 浅克隆:建立一个新对象,新对象的属性和原来对象彻底相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
    • 深克隆:建立一个新对象,属性中引用的其余对象也会被克隆,再也不指向原有对象地址。
  • 深克隆和浅克隆有何区别?技术博客大总结
    • 深浅克隆都会在堆中新分配一块区域,区别在于对象属性引用的对象是否须要进行克隆(递归性的)。
  • Java的clone()方法
    • 1.clone方法将对象复制了一份并返回给调用者。通常而言,clone()方法知足
    • 2.对任何的对象x,都有x.clone() !=x 由于克隆对象与原对象不是同一个对象
    • 3.对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型同样
    • 4.若是对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立
  • 注意问题:
    • 基于序列化和反序列化实现的克隆不只仅是深度克隆,更重要的是经过泛型限定,能够检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。
  • 代码以下所示
    • 若是对象实现Cloneable并重写clone方法不进行任何操做时,调用clone是进行的浅克隆。而使用对象流将对象写入流而后再读出是进行的深克隆。
    public class MyUtil {  
      
        private MyUtil() {  
            throw new AssertionError();  
        }  
      
        public static <T> T clone(T obj) throws Exception {  
            ByteArrayOutputStream bout = new ByteArrayOutputStream();  
            ObjectOutputStream oos = new ObjectOutputStream(bout);  
            oos.writeObject(obj);  
      
            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());  
            ObjectInputStream ois = new ObjectInputStream(bin);  
            return (T) ois.readObject();  
              
            // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义  
            // 这两个基于内存的流只要垃圾回收器清理对象就可以释放资源  
        }  
    } 
    
    
    class CloneTest {  
        public static void main(String[] args) {  
            try {  
                Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));  
                p1.clone();//浅克隆
                Person p2 = MyUtil.clone(p1);   // 深度克隆  
                p2.getCar().setBrand("BYD");  
                // 修改克隆的Person对象p2关联的汽车对象的品牌属性  
                // 原来的Person对象p1关联的汽车不会受到任何影响  
                // 由于在克隆Person对象时其关联的汽车对象也被克隆了  
                System.out.println(p1);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }
    复制代码

4.0.1.0 浅拷贝会建立新的对象吗?对于基本类型拷贝的是什么?怎么样实现浅拷贝,浅拷贝先后对象地址值会同样吗?

  • 浅拷贝会建立新的对象吗?php

    • 会建立新对象。浅拷贝是按位拷贝对象,它会建立一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
  • 对于基本类型拷贝的是什么?java

    • 若是属性是基本类型,拷贝的就是基本类型的值;若是属性是内存地址(引用类型),拷贝的就是内存地址 ,所以若是其中一个对象改变了这个地址,就会影响到另外一个对象。
    • image
    • 在上图中,SourceObject有一个int类型的属性 "field1"和一个引用类型属性"refObj"(引用ContainedObject类型的对象)。当对SourceObject作浅拷贝时,建立了CopiedObject,它有一个包含"field1"拷贝值的属性"field2"以及仍指向refObj自己的引用。因为"field1"是基本类型,因此只是将它的值拷贝给"field2",可是因为"refObj"是一个引用类型, 因此CopiedObject指向"refObj"相同的地址。所以对SourceObject中的"refObj"所作的任何改变都会影响到CopiedObject。
  • 怎么样实现浅拷贝,浅拷贝先后对象地址值会同样吗?git

    • 下面来看一看实现浅拷贝的一个例子
    public class Subject {
     
       private String name; 
       public Subject(String s) { 
          name = s; 
       } 
    
       public String getName() { 
          return name; 
       } 
    
       public void setName(String s) { 
          name = s; 
       } 
    }
    复制代码
    public class Student implements Cloneable { 
     
       // 对象引用 
       private Subject subj; 
       private String name; 
     
       public Student(String s, String sub) { 
          name = s; 
          subj = new Subject(sub); 
       } 
     
       public Subject getSubj() { 
          return subj; 
       } 
     
       public String getName() { 
          return name; 
       } 
     
       public void setName(String s) { 
          name = s; 
       } 
     
       /** 
        *  重写clone()方法 
        * @return 
        */ 
       public Object clone() { 
          //浅拷贝 
          try { 
             // 直接调用父类的clone()方法
             return super.clone(); 
          } catch (CloneNotSupportedException e) { 
             return null; 
          } 
       } 
    }
    复制代码
    private void test1(){
        // 原始对象
        Student stud = new Student("杨充", "潇湘剑雨");
        System.out.println("原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
    
        // 拷贝对象
        Student clonedStud = (Student) stud.clone();
        System.out.println("拷贝对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
    
        // 原始对象和拷贝对象是否同样:
        System.out.println("原始对象和拷贝对象是否同样: " + (stud == clonedStud));
        // 原始对象和拷贝对象的name属性是否同样
        System.out.println("原始对象和拷贝对象的name属性是否同样: " + (stud.getName() == clonedStud.getName()));
        // 原始对象和拷贝对象的subj属性是否同样
        System.out.println("原始对象和拷贝对象的subj属性是否同样: " + (stud.getSubj() == clonedStud.getSubj()));
    
        stud.setName("小杨逗比");
        stud.getSubj().setName("潇湘剑雨大侠");
        System.out.println("更新后的原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
        System.out.println("更新原始对象后的克隆对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
    }
    复制代码
    • 输出结果以下:
    2019-03-23 13:50:57.518 24704-24704/com.ycbjie.other I/System.out: 原始对象: 杨充 - 潇湘剑雨
    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 拷贝对象: 杨充 - 潇湘剑雨
    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象是否同样: false
    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象的name属性是否同样: true
    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象的subj属性是否同样: true
    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠
    2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 更新原始对象后的克隆对象: 杨充 - 潇湘剑雨大侠
    复制代码
    • 能够得出的结论
      • 在这个例子中,让要拷贝的类Student实现了Clonable接口并重写Object类的clone()方法,而后在方法内部调用super.clone()方法。从输出结果中咱们能够看到,对原始对象stud的"name"属性所作的改变并无影响到拷贝对象clonedStud,可是对引用对象subj的"name"属性所作的改变影响到了拷贝对象clonedStud。
    • 能够经过案例得出:原始对象和拷贝对象是不同的,也就是地址值发生了变化,可是里面的属性仍是同样的。

4.0.1.1 对字节流进行大量的从硬盘读取,要用那个流,为何?有什么须要注意的问题?

  • 对字节流进行大量的从硬盘读取,要用那个流,为何?
    • 由于明确说了是对字节流的读取,因此确定是inputstream或者他的子类,又由于要大量读取,确定要考虑到高效的问题,天然想到缓冲流。技术博客大总结
    • 用BufferedInputStream,缘由:BufferedInputStream是InputStream的缓冲流,使用它能够防止每次读取数据时进行实际的写操做,表明着使用缓冲区。不带缓冲的操做,每读一个字节就要写入一个字节,因为涉及磁盘的IO操做相比内存的操做要慢不少,因此不带缓冲的流效率很低。带缓冲的流,能够一次读不少字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式能够减小磁盘操做次数,速度就会提升不少!而且也能够减小对磁盘的损伤。

其余介绍

01.关于博客汇总连接

02.关于个人博客

相关文章
相关标签/搜索