Java小记-反射/数据库连接池/IO流(超级无敌认真好用,万字收藏篇!!!!)

反射/数据库连接池/IO流

1 Java反射机制

  1. 什么是反射?

    反射又被称为类的“自省”,动态获得类自身的内容(属性,方法,构造器…), 并动态创建类的对象,然后调用这些属性和方法

    我们之前编写的代码大多是在编译期定义的,类的对象创建及属性或方法的调用都在编译期已经创建成功

    编译期写的代码不具有通用性,如果想要编写出来更加通用,更加灵活的代码,我们则不能在编译器创建对象和调用属性和方法

    需要使用反射,在运行期运行期动态创建类的对象并动态调用对象中的属性和方法

    • 反射操作的是.class文件而非编译期代码

    Java执行过程:.java编译>.class解释>JVM

Java是面向对象的语言,Java中任何元素都是对象,在反射中把Java类中的任何元素都使用对象来进行处理

  • 属性是对象:Field
  • 方法是对象:Method
  • 构造器是对象:Constructor
  • 修饰符也是对象:Modifier
  • void是对象:Void

1.1 Java反射的基本使用-创建对象及操作类中属性

  1. 获得类的Class对象

    • 由于反射是在运行期工作,所以我们使用反射时需先获取类Class的对象
    • Java的类或接口在编译后都存在一个Class对象,我们可以认为Class对象是类的字节码对象
    • Class对象表示所有编译后的类或接口
    • Java中获取类的Class对象有三种方式
    1. 通过类的"完整路径字符串"
    Class class1=Class.forName("类完整路径字符串");
    
    1. 通过类名获取
    Class class2=类名.class;
    
    1. 通过类的对象名获取
    类名 对象名 =new 类名();
    Class class3=对象名.getClass();
    
  2. 通过类的Class对象获得类的属性

    扫描二维码关注公众号,回复: 15131135 查看本文章
    /**
     * 1. 获得类的Class对象
     * 2.通过该类的Class对象获得该类的属性
     *    对象名.getFields():获得的属性是访问权限为public的属性(包括static属性)
     *
     *    对象名.getDeclaredFields();可或得该类所有方法(含static)
     * 3.获得类的属性名,修饰符,属性类型
     */
    public class 通过反射操作类的属性 {
          
          
        public static void main(String[] args) {
          
          
            Class userClass= User.class;
    //          通过得类的Class对象获得该类的属性
    //        Field[] userFields   = userClass.getFields();
            Field[] userFields= userClass.getDeclaredFields();
            for (Field field :userFields){
          
          
                System.out.println(field);
                //获得属性名
                String fieldName =field.getName();
                System.out.println(fieldName);
    
                //获得属性类型
                Class type=field.getType();
                System.out.println(type);
    
                //获得属性修饰符
                int modifierNum =field.getModifiers();
                //由于通过getModifiers()获得的是整数形式,我们需要Modifier类这个整数进行解码
                String modifier=Modifier.toString(modifierNum);
                System.out.println(modifier);
                System.out.println("*******************");
            }
        }
    }
    
  3. 通过属性名获得属性并调用该属性

    /**
     * 1.或得该类Class对象
     * 2.或得属性名
     * 3.调用该属性
     *
     */
    public class 通过属性名获得属性并调用该属性 {
          
          
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
          
          
            Class userClass= User.class;
    
            Field username=userClass.getDeclaredField("username");
    
            //反射调用该属性的方法
            //1.创建该类对象
            Object obj= userClass.newInstance();
            /**
             *   2.设置属性值
             *属性名.set(对象名,"属性值")
             */
           username.set(obj,"大王");
            /**
             * 3.调用属性
             */
            Object username1=username.get(obj);
            System.out.println(username1);
    
        }
    }
    

1.2 Java反射的基本使用-操作类中方法及构造器

  1. 通过反射或得该方法并调用

    public class 通过反射或得该方法并调用该方法 {
          
          
    
        public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
          
          
            Class<User> userClass= User.class;
    
            //获得方法对象,根据方法名和参数列表
            Method setUserId=userClass.getDeclaredMethod("setUserId", Integer.class);
            //创建类对象
            User user=userClass.newInstance();
            //调用该方法
            /**
             * 反射中调用方法
             * 方法对象名.invoke(类对象名,方法参数列表)
             */
            setUserId.invoke(user,2);
    
            Method getUserId=userClass.getDeclaredMethod("getUserId");
            Object userId=getUserId.invoke(user);
            System.out.println(userId);
    
        }
    }
    
  2. 使用反射操作类中的构造方法

    public class 通过反射操作类的构造器 {
          
          
        public static void main(String[] args) {
          
          
            Class<User> userClass = User.class;
            //获得类中构造方法
            Constructor[] constructors = userClass.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
          
          
                //修饰符
                int modifiers = constructor.getModifiers();
                String modifier = Modifier.toString(modifiers);
                //参数列表
                Class[] aClass = constructor.getParameterTypes();
                System.out.print("参数列表");
                for (Class object : aClass) {
          
          
                    System.out.print(object+"/");
                }
                //异常
                System.out.println();
                System.out.print("异常列表:");
                Class[] exception = constructor.getExceptionTypes();
                for (Class c:exception){
          
          
                    System.out.print(c+" ");
                }
                //方法名
                String name = constructor.getName();
            }
        }
    }
    
  3. 使用反射调用构造器

    public class 获得构造方法并调用 {
          
          
        public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
          
          
            Class<User> userClass=User.class;
            //使用默认构造方法创建对象(JDK11后已过时)
            //  Object o =userClass.newInstance();
            //获得无参构造方法
            Constructor constructor=userClass.getConstructor();
            //使用构造方法创建类的对象
            Object o =constructor.newInstance();
    
            //获得有参数的构造方法
            Constructor constructor1=userClass.getConstructor(Integer.class,String.class,Integer.class);
            Object o1=constructor1.newInstance(1,"张三",1);
    
            System.out.println(o1);
        }
    }
    
    

1.3 反射Test

  1. 赋值器
    使用反射编写一个赋值器,通过赋值器将值赋给对象的相关属性中
    定义一个方法,参数为Class和Map集合
    参数说明:
    1.Class 实体类的字节码对象
    2.Map集合的key为实体的属性名,value为属性的值
    返回值:设置属性的对象

      /**
         * 赋值器创建方法
         * 1.获得属性名和属性值
         * 2.根据属性名拼接set方法
         * 3.调用set方法,并赋值
         *
         * @param clas    类的字节码对象
         * @param map     map集合key是属性名  value是属性值
         * @return
         */
        public static <T> T setter(Class<T> clas, Map<String,Object> map) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
            T t=clas.getDeclaredConstructor().newInstance();
            for (Map.Entry<String,Object> entry : map.entrySet() ){
                //获得属性名
                String fieldName =entry.getKey();
                //获得属性值
                Object fieldValue=entry.getValue();
                //根据属性名拼接set方法
                String methodName ="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
                //创建属性对象
                Field field=clas.getDeclaredField(fieldName);//主要用来获得该属性对应的参数类型
                //创建方法对象
                Method method=clas.getDeclaredMethod(methodName,field.getType());
                //调用该方法对象
                method.invoke(t, fieldValue);
            }
           return t;
        }
    
  2. 封装一个用于request的参数字符的封装,返回Javabean对象

    • 定义一个请求参数自动封装的工具类,类中定义一个静态方法,该方法用于将请求的参数字符封装为一个JavaBean对象并返回;
      参数说明:
      1.Class要封装的类的字节码对象
      2.HttpServletRequest 请求对象
      返回值:封装好的对象
      public static <T> T fenzhuangByRequest(Class<T> clas, HttpServletRequest request) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, ParseException {
          
          
         //获得客户端提交的参数信息,获得一个Map集合key是参数名,value是参数值
         Map<String, String[]> map = request.getParameterMap();
         //获得类字节码对象
         T javabean = clas.getDeclaredConstructor().newInstance();
         for (Map.Entry<String, String[]> entry : map.entrySet()) {
          
          
             //判断是否有值
             if (entry.getValue() == null || entry.getValue().length == 0) {
          
          
                 continue;
             }
             //拼接set方法
             String methodName = "set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1);
             //获得属性对象和方法对象
             Field field = clas.getDeclaredField(entry.getKey());
             Method method = clas.getDeclaredMethod(methodName, field.getType());
             //调用该方法
             if (field.getType() == String.class) {
          
          
                 method.invoke(javabean, entry.getValue()[0]);
             } else if (field.getType() == Date.class) {
          
          
                 Date date = new SimpleDateFormat("yyyy-MM-dd").parse(entry.getValue()[0]);
                 method.invoke(javabean, date);
             } else if (field.getType() == Integer.class) {
          
          
                 method.invoke(javabean,Integer.parseInt(entry.getValue()[0]));
             }else if (field.getType().isArray()){
          
          
                 method.invoke(javabean, (Object) entry.getValue());
             }
         }
         return javabean;
     }
    
    
  3. 使用BeanUtils的类型转换器对日期类型进行处理

              /** 方案1:判断属性类型如果是util.Date则进行手动转换
                 */
                if (field.getType() == Date.class) {
          
          
                    Date value1 = new SimpleDateFormat("yyyy-MM-dd").parse(entry.getValue()[0]);
                    method.invoke(t, value1);
                } else {
          
          
                    method.invoke(t, ConvertUtils.convert(entry.getValue(), field.getType()));
                }
                /**
                 * 方案2:向Convert中注册一个类型转换,该类型转换器BeanUtils中提供的转换器
                 *
                 *   向ConvertUtils中注册一个转换器,DteLocaleConverter
                 *   参数1:类型转换器对象
                 *   参数2:转换类型,当需要转换时使用该类型当前注册的类型转换器进行转换
                 */
                ConvertUtils.register(new DateLocaleConverter(Locale.getDefault(), "yyyy-MM-dd"), Date.class);
                /**
                 * 使用自己的转换器,向Convert中注册
                 */
                ConvertUtils.register(new Converter() {
          
          
                    @Override
                    public Object convert(Class aClass, Object o) {
          
          
                        if (entry.getValue()[0] == null) {
          
          
                            return null;
                        }
                        if (!(entry.getValue()[0] instanceof String)) {
          
          
                            throw new ConversionException("只支持字符类转换");
                        }
                           String str=(String) entry.getValue()[0];
                        try {
          
          
                            return new SimpleDateFormat("yyyy-MM-dd").parse(str);
                        } catch (ParseException e) {
          
          
                            throw new RuntimeException(e);
                        }
                    }
                }, Date.class);
    

1.4 反射封装查询功能

    /**
     * 封装查询多行数据功能
     *
     * @param clas
     * @param sql
     * @param params
     * @return
     */
    public List<T> executeQuery(Class<T> clas, String sql, Object... params) {
    
    
        try {
    
    
            getConn();
            ps = conn.prepareStatement(sql);
            if (params != null && params.length != 0) {
    
    
                for (int i = 0; i < params.length; i++) {
    
    
                    ps.setObject(i + 1, params[i]);
                }
            }
            rs = ps.executeQuery();
            List<T> list = new ArrayList<>();
            //获取元数据
            ResultSetMetaData metaData = ps.getMetaData();
            //获取查询列数
            int countNum = metaData.getColumnCount();
            while (rs.next()) {
    
    
                //创建对象
                T t = clas.getDeclaredConstructor().newInstance();
                for (int i = 0; i < countNum; i++) {
    
    

                    //列名与属性名一致
                    String fieldName = metaData.getColumnName(i + 1);
                    //拼接得到set方法
                    String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    //获得属性对象
                    Field field = clas.getDeclaredField(fieldName);
                    //获得对应方法对象
                    Method method = clas.getDeclaredMethod(methodName, field.getType());
                    //获得一列数据,并将该数据转换为指定的类型
                    Object value = rs.getObject(i + 1, field.getType());
                    method.invoke(t, value);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            closeAll();
        }
        return null;
    }

    /**
     * 封装查询单行数据功能
     *
     * @param clas
     * @param sql
     * @param params
     * @param <T>
     * @return
     */
    public <T> T executeQueryOne(Class<T> clas, String sql, Object... params) {
    
    
        try {
    
    
            getConn();
            ps = conn.prepareStatement(sql);
            if (params != null && params.length != 0) {
    
    
                for (int i = 0; i < params.length; i++) {
    
    
                    ps.setObject(i + 1, params[i]);
                }
            }
            rs = ps.executeQuery();
            //获取元数据
            ResultSetMetaData metaData=ps.getMetaData();
            //获取列数
            int countNum = metaData.getColumnCount();
            if (rs.next()){
    
    
//                创建类对象
                T t=clas.getDeclaredConstructor().newInstance();
                for (int i=0;i<countNum;i++){
    
    
                    //获取列名=属性名
                    String fieldName= metaData.getColumnName(i+1);
                    //获取方法名
                    String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    //获得属性对象
                    Field field = clas.getDeclaredField(fieldName);
                    //获得对应方法对象
                    Method method = clas.getDeclaredMethod(methodName, field.getType());
                    //获取列值
                    Object value =rs.getObject(i+1,field.getType());
                    method.invoke(t,value);
                }
                return t;
            }

        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            closeAll();
        }
        return null;
    }


    /**
     * 通用查询-统计查询
     * @param sql       SQL语句
     * @param params    SQL语句中?号占位符的值
     * @return          存有查询结果的Bean对象
     */
    public Long executeQueryCount(String sql, Object... params){
    
    
        try {
    
    
            //连接数据库
            this.getConn();
            //创建预处理对象
            ps = conn.prepareStatement(sql);
            //判断SQL语句中是否包含?号占位符(params有值表示有?号占位符,有一个值表示有一个?,有多个值表示有多个?)
            if(params!=null && params.length!=0){
    
    
                //循环设置?号占位符的
                for (int i = 0; i < params.length; i++) {
    
    
                    ps.setObject(i+1,params[i]);
                }
            }
            //执行查询语句
            rs = ps.executeQuery();
            if(rs.next()){
    
    
                return Long.valueOf(rs.getInt(1));
            }

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }  finally {
    
    
            closeAll();
        }
        return 0L;
    }

1.5 使用反射封装servlet[简易用户管理项目]

源码在这,需要自取

在这里插入图片描述

反射优缺点

  • 反射的优点:

    反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创和控制任何类的对象,无需提前硬编码目标类

  • 反射的缺点

    性能问题,使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用

2 数据库连接池

传统的数据库连接是通过程序来连接数据库和释放数据库资源的

  • 频繁连接和释放会极大浪费系统资源,效率低下
  • 实际开发中,都使用的是远程数据库,加上网络时延,频繁连接和关闭数据据库让效率更低

所以我们引入数据库连接池

2.1 数据库连接池概念

数据库连接池是基于池化思想实现的

数据库连接池基本原理

  1. 在系统创建时自动创建一部分(初始化对象)连接对象与数据库连接

  2. 当系统中有功能要操作数据库时无需与数据库建立连接,而是从连接池中获得一个空闲连接对象来使用,并将该连接对象标注为繁忙

  3. 当数据库操作完毕后,断开与连接池连接对象的关联,并将该对象标注为空闲

  4. 当连接池中连接对象不能满足使用需求时,连接池会自动创建一部分连接对象放到连接池中等待使用

  5. 当连接池连接对象到达上限后将不在创建新的连接对象

  6. 如果连接池中的连接对象都被占用,此时新的操作要被放入等待队列中等待连接池中连接对象空闲

  7. 当连接池存在大量空闲连接时,连接池会释放一部分连接对象

数据库连接池是基于Map集合实现的

有大量开源三方连接池:C3P0连接池,DBCP连接池,Druid连接池

我们使用aibaba的Druid连接池

2.2 基于Druid的数据库连接池技术实现

配置properties

#配置数据库连接属性
#驱动字符串
druid.driverClassName=com.mysql.cj.jdbc.Driver
#数据库连接url
druid.url=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
#数据库账号和密码
druid.username=****
druid.password=****



#配置数据库连接池相关属性
#初始化连接数量
druid.initialSize=5
#最大活动连接
druid.maxActive=100
#最小空闲连接
druid.midIdle=1
#最大等待时间
druid.maxWait=1000
  private static  DruidDataSource dataSource;
    /**
     * 静态代码块,在类加载时被执行,值执行一次
     * 读取db.properties配置文件并初始化数据库连接池
     */
    static {
    
    
        try {
    
    
            /**
             * 1.读取db.properties中的属性文件
             * 2.创建Druid数据库连接池对象
             */
            //获得输出流对象
            InputStream inputStream = DBUntils.class.getClassLoader().getResourceAsStream("db.properties");
            //创建properties对象,用于存储db.properties文件中的数据
            Properties properties = new Properties();
            properties.load(inputStream);
            //创建Druid数据库连接对象
            //DataSource称为数据源,他是数据的提供者
            dataSource =new DruidDataSource();
            dataSource.configFromPropety(properties);

        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 获得连接对象
     * @return
     */
   public Connection getConn() {
    
    
       try {
    
    
           conn=dataSource.getConnection();
       } catch (SQLException e) {
    
    
           e.printStackTrace();
       }
       return conn;
   }

2.3 基于Druid配置数据库连接池[简易用户管理项目]

源码在这,需要自取

3 IO流

IO流:输入输出流(InputOutput),主要用来操作磁盘中的文件的

  • 在Java的java.io包下

  • 所有的IO流操作都包含输入输出流

    • 输入流:将文件中的内容拆分成一个一个的字节码,然后将这些字节码按照一定的顺序通过IO流管道传递道程序,程序或得这些字节码后,对这些字节码进行组装
    • 输出流:将程序中的内容拆分成一个一个字节码,按一定顺序和规则,传递到磁盘文件中吗,并对这些字节码进行组装,组装为具体文件内容
  • IO流的相关操作:
    ①File类
    ②字节流
    ③字符流
    ④数据流
    ⑤对象流

3.1 File类

File类实例表示磁盘中文件或者文件夹

  • 通过File的实例可以获取文件或文件的相关属性
  • Flie类只能读取或设置文件的相关属性,不能操作文件内容
  • 编程中一般文件称为文件,文件夹称为目录
  1. File类的构造方法

    File(File parent, String child):根据父文件(parent)创建子文件对象

    File(Sring pathname):根据文件路径创建一个文件对象(通常用)

    File(String parent, Sting child):根据父文件路径和子文件名创建一个文件对象

    File(URL uri):根据URI地址创建文件对象

    • File file = new File(“d:/key.pri”)

      • 注意:文件路径的分隔符在Windows中"/“反斜杠或”\"双斜杠表示\

        为了满足不同操作系统的需要我们可以使用File.separator自动根据所在的操作系统生成分隔符

  2. File类的常用方法

    (1)file.exists:检测file对象指定的文件是否存在

    (2)file.createNewFile();:创建一个新的文件夹

    (3)file.getName():获取文件名字符串

    (4)file.getAbsolutePath():获取文件绝对路径字符串

    (5)file.getCanonicalPath():获取文件规范路径的字符串

    ps:<先像file.getAbsolutePath()获取绝对路径字符串后,然后转成规范路径字符串>

    (6)file.getName().substring(file.getName().lastIndexOf(".")+1):获取文件扩展名

    (7)file.getTotalSpace():获取所在磁盘的大小(单位为字节:B)

    (8)file.length():获得文件大小(单位为字节:B)

    (9)file.lastModified():获得最后修改时间(以毫秒为单位)

    (10)file.delete():删除一个文件

    (11)file.deleteOnExit():JVM运行结束时删除,删除并退出系统(延迟删除)

    (12)file.isFile():判断是否是文件

    (13)file.isDirectory():判断是否是目录

    (14)file.mkdir():创建一级目录

    (15)file.mkdirs():创建一个多级目录

    (16)file.getParent():获得文件的父目录名字符串

    (17)file.getParentFile():获得文件的父目录的对象

    (18)file.list()返回目录下所有子文件的文件名字符串

    (19)file.listFiles()获得目录下所有子文件的File对象

    (20)file.listFiles(FileFilter fileFilter):使用文件过滤接口获得子文件中所有指定的文件

    (21)File.listRoots():获得当前系统中的所有盘符文件,返回File数组

  • 问题1:编程实现创建"d:/abc/xyz/hello.java"目录及文件

  • 问题2:编程实现删除"d:/abc/xyz/hello.java"目录及文件

  • 问题3:扫描磁盘的所有文件

    package com.jiazhong.IO.File类;
    import java.io.File;
    /**
     * 扫描指定目录的所有文件
     * 使用递归调用
     */
    public class Demo04 {
          
          
        public static void main(String[] args) {
          
          
            File[] roots = File.listRoots();//获得当前电脑所有磁盘
            for (File file:roots){
          
          
                scannerFile(file);
            }
    
        }
        /**
         * 扫描某个路径所有文件
         * @param file
         */
        static  void scannerFile(File file){
          
          
            if (file==null){
          
          //文件无效,退出本次循环
                return;
            }
            if (file.isFile()){
          
          //如果是文件输出文件地址,退出本次调用
                String filePath=file.getAbsolutePath();
                System.out.println(filePath);
                return;
            }
    
            if (file.isDirectory()){
          
          
                 //获得当前目录所有子文件
                File[] files = file.listFiles();
                if (files!=null&&files.length!=0){
          
          
                    //遍历子文件
                    for (File file1:files){
          
          
                        scannerFile(file1);
                    }
                }
            }
        }
    }
    

3.2 字节输入输出流(InPutStream/OutPutStream)

字节流是以字节码的方式传输数据,他是所有流的基础,其他流都是在字节流的基础上进行操作

字节流提供了InPutStream/OutPutStream两个基础流,但这两个流都是抽象类,是字节流的基类

我们在使用字节输入输出流一般使用FileInPutStream和FileOutOutStream这两个流来实现基于字节流的输入和输出操作

字节流可以操作文本文件但是不建议,因为字节流操作的是字节,很容易乱码,经常用来操作二进制文件

带有缓冲区的字节流,BufferedInputStream和BufferedOutputStream,这两个流有默认缓冲区,可以提高读取效率

  • FileInPutStream和FileOutOutStream是节点流(原始流)

  • BufferedInputStream和BufferedOutputStream(处理流),针对原始流进行处理的

    • BufferedInputStream和BufferedOutputStream两个类存在一个默认缓冲区,大小为8K,如果自定义缓冲区大于8k则用自定义的,反之则用磨人的
  • 外部流关闭内部流自动关闭

  1. 字节输入流
/**
 * 字节输入流实例
 */
public class Demo01 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        InputStream inputStream = new FileInputStream("d:/hello java.txt");
        //读取文件内容
        //读取一个字节
       /* int num=inputStream.read();
        System.out.println((char)num);
        */
        //一次读取多个字节,组装到字节数组中
        byte[] buf = new byte[1024];
        int len = -1;
//        将读取到的字节存入到buf中,返回值实际读到的内容
        while ((len = inputStream.read(buf)) != -1) {
    
    
            String str = new String(buf,0,len);//从下标为0开始到下标为len结束创建字符串
            System.out.println(str);
        }
        inputStream.close();
    }
}
  1. 字节输出流

    字节输出流在向目标文件输出内容时,如果目标文件不存在,创建文件写入内容,如果存在,则更新内容

    若不想更新,FileInputStream对象的第二个参数指定为true

    OutputStream outputStream=new FileOutputStream("D:/hello.txt",true);
    
    /**
     * 字节输出流
     */
    public class Demo02 {
          
          
        public static void main(String[] args) throws IOException {
          
          
            OutputStream outputStream=new FileOutputStream("D:/hello.txt",true);
            Scanner scanner=new Scanner(System.in);
            System.out.println("请输入要输出的内容,输出N时结束:");
            Boolean flag=false;
            while (!flag){
          
          
                String str=scanner.nextLine();//输出一行数据
                if (str.equals("N")||str.equals("n")){
          
          
                    break;
                }
                //将一个字符串转化为字节数组
                byte[] bytes = str.getBytes();
                outputStream.write(bytes);
                outputStream.write("\n".getBytes());
               outputStream.flush();//刷新流
            }
             outputStream.close();//关闭流
        }
    }
    
    • 文件复制
    /**
         * 文件复制
         * @param src  源文件
         * @param desc  目标文件
         * @throws FileNotFoundException
         */ 
    public static void fileCopy(File src,File desc) throws IOException {
          
          
            InputStream inputStream=new FileInputStream(src);
            OutputStream outputStream=new FileOutputStream(desc,true);
    
            byte[] buf = new byte[1024*1024*10];
            int len=-1;
            while ((len=inputStream.read(buf))!=-1){
          
          
                outputStream.write(buf,0,len);
                outputStream.flush();
            }
            inputStream.close();
            outputStream.close();
        }
    

3.3 文件编码方式

① ISO-8859-1:西方编码,智能识别西方的语言

②GBK:中国标准编码,可以识别简体和繁体中文,一个字符占2个字节

③GB2312:中国简体标准编码,智能识别简体中文,一个字符占2个字节

④UTF-8:国际编码,可以识别几乎所有国家的语言,一个字符占3个字节

⑤Unicode:java所用编码

⑥ANSI:操作系统编码,我们国内使用的WindowsOS都是GBK或GB2312

  • 我们操作文件时,由于外部文件编码方式和Java所用编码(Unicode)不统一,所以要进行转码

    • 将外部文件编码转换为Java编码,我们称为解码

      String str=new String(buf,o,len,"GBK")将外部文件编码"GBK"转化为Java编码

    • 将Java编码转换为外部文件编码,我们称为编码

      byte[] buf=str.getBytes("GBK")“将Java编码转化为外部编码”

3.4 桥接流(字节到字符的转换流)

字节到字符的转换流属于处理流

字节流的底层就是通过"字节流"读取原始数据,然后再将这些读取到的"字节数据"转换为"字符数据"

字节到字符的转换流是按照外部文件编码,将字节数据转化为字符数据,并指定外部编码

字节到字符的转换流是通过具体字符流的基础

字节到字符的转换流:InputStreamReader和OutPutStreamWrite这两个类来实现

InputStreamReader:是字节到字符的输入流,他继承Reader类

OutPutStreamWrite:是字节到字符的输出流,他继承Write类

Reader类:是一个抽象类,是字符输入流的基类

Write类:是一个抽象类,是字符输出流的基类

  • 使用字节到字符的转换流可以根据外部文件编码设置转换编码
  1. 字节到字符输入流
/**
 * 字节到字符输入流
 */
public class Demo01 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        InputStream inputStream = new FileInputStream("D:\\hello java.txt");
        //该流会将读到的字节转换为字符,并根据指定的编码进行转换
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"GBK");
        char[] buf = new char[1024];
        int len = -1;
        while ((len = inputStreamReader.read(buf)) != -1) {
    
    
              String str=new String(buf,0,len);
            System.out.println(str);
        }
        inputStream.close();
    }
}
  1. 字节到字符的输出流
/**
 * 字节到字符输入流
 */
public class Demo01 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        InputStream inputStream = new FileInputStream("D:\\hello java.txt");
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"GBK");

        char[] buf = new char[1024];
        int len = -1;
        while ((len = inputStreamReader.read(buf)) != -1) {
    
    
              String str=new String(buf,0,len);
            System.out.println(str);
        }
        inputStream.close();
    }
}

3.5 字符流

字符流包含FileReader和FileWrite两个类,他们分别继承InputStreamReader和OutputStream这两个转换源

FileWrite和FileReader是InputStreamReader和OutputStream的的简洁实现,这两个字符流类将编码固化,编码统一使用UTF-8

  • 在FileReader和FileWriter中无法设置编码只能使用UTF-8编码进行转换,所以只能操作编码为UTF-8的文本文件
    • JDK11后FileReader和FileWriter支持设置编码集
  • BufferedReader和BufferedWriter这两个类内部带有缓冲区,效率会适当提高,但我们使用这两流不是为了提高效率,而是这两个类中提高操作字符数据的便捷方法
    • BufferedReader和BufferedWriter属于包装流,对Reader和Writer子类对象的包装,我们在使用时可以使用FileReader和FileWriter做为基础流
      也可以使用InputStreamReader和OutputStreamWriter做为基础流
  1. 字符输入流
/**
 * 字符输入流
 */
public class Demo02 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        Reader reader=new FileReader("d:/hello java.txt");
        char[] buf=new char[1024];
        int len=-1;
        while ((len=reader.read(buf))!=-1){
    
    
            String str=new String(buf,0,len);
            System.out.println(str);
        }
        reader.close();
    }
}
  1. 字符输出流
public class Demo01 {
    
    
    public static void main(String[] args) throws IOException {
    
    
         Writer writer=new FileWriter("d:/hello.txt");
        writer.write("你好");
        writer.flush();
        writer.close();
    }
}
  1. 字符输入输出缓冲流
public class BufferDemo  {
    
    
    public static void main(String[] args) throws IOException {
    
    
//        bufferReader();
        bufferWrite();
    }
    /**
     * 缓冲字符输出流
     * @throws IOException
     */
    private static void bufferWrite() throws IOException {
    
    
        Writer writer=new FileWriter("d:/hello.txt");
        BufferedWriter bufferedWriter=new BufferedWriter(writer);
        Scanner scanner=new Scanner(System.in);
        String str=scanner.nextLine();
        bufferedWriter.write(str);
        bufferedWriter.newLine();//输出一个换行符
        writer.flush();

        writer.close();
    }

    /**
     * 缓冲输入流
     */
     static void bufferReader() throws IOException {
    
    
       /**
        Reader reader=new FileReader("d:/hello java.txt");
        BufferedReader bufferedReader=new BufferedReader(reader);
        */
       //创建字节输入流对象
       InputStream inputStream=new FileInputStream("d:/hello java.txt");
       //创建字节到字符流对象
       InputStreamReader inputStreamReader=new InputStreamReader(inputStream);
       //创建字符缓冲流对象
       BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
        String len=null;
        while ((len=bufferedReader.readLine())!=null){
    
    
            System.out.println(len);
        }
        inputStream.close();
    }
}

3.6 打印流和数据流

  1. 打印流

    Printwriter:向目标位置写入文本

    public class Demo01 {
          
          
        public static void main(String[] args) throws IOException {
          
          
            //控制台输入
    //        Scanner scanner=new Scanner(System.in);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            bufferedReader.read();
            //控制台输出
            PrintWriter printWriter = new PrintWriter(System.out);
            while (true) {
          
          
                String s = bufferedReader.readLine();
                printWriter.write(s);
                if (s.equals("n")) {
          
          
                    break;
                }
                printWriter.flush();
            }
            bufferedReader.close();
            printWriter.close();
        }
    }
    
  2. 数据流

    使用该流可以对基本数据类型输入和输出

    可以根据数据流读取任意基本数据类型数据

    数据流根据所要操作的数据类型所占的字节数,一次性读取或写出对应字节数,以达到操作基本数据类型数据的精准性和效率

    数据流由两个类组成(DataInputStream/DataOutPutStream)

    public class Demo01 {
          
          
        public static void main(String[] args) throws IOException {
          
          
            dataOutputStream();
            dataInputStream();
        }
        /**
         * 数据输出流
         * @throws IOException
         */
        private static void dataOutputStream() throws IOException {
          
          
            DataOutputStream dataOutputStream=new DataOutputStream(new FileOutputStream("D:/hello.num"));
            dataOutputStream.writeInt(256);
            dataOutputStream.writeInt(5256);
            dataOutputStream.writeInt(2256);
            dataOutputStream.writeInt(2526);
            dataOutputStream.writeDouble(2562.06);
            dataOutputStream.flush();
            dataOutputStream.close();
        }
        /**
         * 数据输入流
         * @throws IOException
         */
        public static void dataInputStream() throws IOException {
          
          
            DataInputStream dataInputStream=new DataInputStream(new FileInputStream("d:/hello.num"));
            try {
          
          
                while (true){
          
          
                    int num = dataInputStream.readInt();
                    System.out.println(num);
                }
            }catch (EOFException e){
          
          
                System.out.println("文件读取结束");
            } dataInputStream.close();
        }
    }
    

3.7 对象流/序列化和反序列化

对象流主要用来操作Java对象,将Java对象写出到文件中,或从文件中读取一个java对象

将一个Java对象写入到磁盘中或网络中称为序列化

将文件或网络中的数据转化Java对象中称为反序列化

序列化:将一个Java对象转化为字节码的过程(流化)
反序列化:将字节码转化为Java对象的过程

ObjectInputStream:对象输入流/ObjectOutputStream对象输出流
ObjectOutputStream:实现对象序列化
ObjectInputStream:实现对象反序列化

Java规定只有实现了接口(Serializable)的对象才能进行序列化和反序列化操作

序列化接口(Serializable)中没有定义任何属性和方法,他是一个标识接口(做标识使用)

当对一个Java对象进行序列化操作时,JVM会检测该对象所属的类是否实现了Serializable接口,如果没有实现则JVM会阻止该对象序列化
反之如果对象实现了Serializable接口,则允许其序列化

  • 一个集合类对象,里面的元素要想被序列化也必须实现Serializable接口

  • 一个被序列化的对象存在另一个类的属性,则该属性要想被序列化也必须其对应的类也必须实现Serializable接口

  • 某个属性不能被序列化时我们用trainsient修饰,即不允许其序列化,即在序列化时将标有trainsient的属性特殊处理不让其序列化。注意的是:一个静态属性无论是否有trainsient修饰都不能被序列化

package com.jiazhong.IO.对象流;
import com.jiazhong.IO.对象流.model.User;
import java.io.*;
public class ObjectStream {
    
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    
    
//        ObjOutputStream();
        ObjInputStream();
    }

    /**
     * 将Java对象存储到磁盘中
     *
     * @throws IOException
     */
    public static void ObjOutputStream() throws IOException {
    
    
        User user = new User(2, "校长", "123456");
        User user1 = new User(1, "校长", "123456");
        User user2 = new User(3, "校答长", "123456");
        User user3 = new User(5, "校按时长", "123456");
        User user4 = new User(4, "校达个长", "123456");
        User user5=new User(6,"dasd","123456","男");
        //创建对象输出流对象
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/user.obj"));
        objectOutputStream.writeObject(user);
        objectOutputStream.writeObject(user1);
        objectOutputStream.writeObject(user2);
        objectOutputStream.writeObject(user3);
        objectOutputStream.writeObject(user4);
        objectOutputStream.writeObject(user5);
        objectOutputStream.flush();
        objectOutputStream.close();
    }
    /**
     * 从磁盘中读取数据并转化为Java对象
     */
    public static void ObjInputStream() throws IOException, ClassNotFoundException {
    
    
        //创建对象输入流对象
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:/user.obj"));
        try {
    
    
            while (true) {
    
    
                User user = (User) objectInputStream.readObject();
                System.out.println(user);
            }
        } catch (IOException e) {
    
    
            System.out.println("已读完");
            e.printStackTrace();
        }
        objectInputStream.close();
    }
}

  • 学习来自于西安加中实训

猜你喜欢

转载自blog.csdn.net/woschengxuyuan/article/details/129160665
今日推荐