13天Java进阶笔记-day9-方法引用、Lambda表达式、stream流

第一章 方法引用

方法引用概述

方法引用是为了进一步简化Lambda表达式的写法。

方法引用的格式:类型或者对象::引用的方法。

方法引用有四种形式:

  • 静态方法的引用
  • 实例方法的引用
  • 特定类型方法的引用
  • 构造器引用
ist<String> lists = new ArrayList<>();
lists.add("java1");
lists.add("java2");
lists.add("java3");

lists.forEach( s -> System.out.println(s));
// 方法引用!
lists.forEach(System.out::println);

静态方法引用

引用格式:类名::静态方法

简化步骤:定义一个静态方法,把需要简化的代码放到一个静态方法中去。

静态方法引用的注意事项:被引用的方法的参数列表要和函数式接口中的抽象方法的参数列表一致

Student

public class Student {
    private String name ;
    private int age ;
    private char sex ;

    public static int compareByAge(Student o1 , Student o2){
        return  o1.getAge() - o2.getAge();
    }
    // ...
}

排序

List<Student> lists = new ArrayList<>();
Student s1 = new Student("李铭",18,'男');
Student s2 = new Student("冯龙",23,'男');
Student s3 = new Student("王乐乐",21,'男');
Collections.addAll(lists , s1 , s2 , s3);

Collections.sort(lists, ( o1, o2) -> Student.compareByAge(o1 , o2));
// 如果前后参数是一样的,而且方法是静态方法,既可以使用静态方法引用
Collections.sort(lists, Student::compareByAge);

实例方法引用

格式: 对象::实例方法

简化步骤:定义一个实例方法,把需要的代码放到实例方法中去。

实例方法引用的注意事项:被引用的方法的参数列表要和函数式接口中的抽象方法的参数列表一致

List<String> lists = new ArrayList<>();
lists.add("java1");
lists.add("java2");
lists.add("java3");

// 对象是 System.out = new PrintStream();
// 实例方法:println()
// 前后参数正好都是一个
lists.forEach(s -> System.out.println(s));
lists.forEach(System.out::println);

特定类型方法的引用

特定类型:String ,任何类型

格式:特定类型::方法

注意:如果第一个参数列表中的形参中的第一个参数作为了后面的方法的调用者,并且其余参数作为后面方法的形参,那么就可以用特定类型方法引用了

String[] strs = new String[]{"James", "AA", "John",
                             "Patricia","Dlei" , "Robert","Boom", "Cao" ,"black" ,
                             "Michael", "Linda","cao","after","sBBB"};

// public static  void sort(T[] a, Comparator c)
// 需求:按照元素的首字符(忽略大小写)升序排序!!!
Arrays.sort(strs, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return s1.compareToIgnoreCase(s2);// 按照元素的首字符(忽略大小写)比较。
    }
});
Arrays.sort(strs, (String s1, String s2) -> {
    return s1.compareToIgnoreCase(s2);// 按照元素的首字符(忽略大小写)比较。
});

Arrays.sort(strs, ( s1,  s2 ) ->  s1.compareToIgnoreCase(s2));

// 特定类型的方法引用:
Arrays.sort(strs,  String::compareToIgnoreCase);

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

构造器引用

格式是:类名::new

注意点:前后参数一致的情况下,又在创建对象就可以使用构造器引用s -> new Student(s) => Student::new

List<String> lists = new ArrayList<>();
lists.add("java1");
lists.add("java2");
lists.add("java3");

// 集合默认只能转成Object类型的数组。
Object[] objs = lists.toArray();
System.out.println("Object类型的数组:"+ Arrays.toString(objs));

// 我们想指定转换成字符串类型的数组!!
// 最新的写法可以结合构造器引用实现 。
// default  T[] toArray(IntFunction generator)
String[] strs = lists.toArray(new IntFunction<String[]>() {
    @Override
    public String[] apply(int value) {
        return new String[value];
    }
});

String[] strs1 = lists.toArray(s -> new String[s] );

String[] strs2 = lists.toArray(String[]::new);

System.out.println("String类型的数组:"+ Arrays.toString(strs2));

第二章 Lambda表达式

Lambda表达式是JDK1.8开始之后的新技术,是一种代码的新语法,作用是为了简化匿名内部类的代码写法

Lambda表达式的格式

(匿名内部类被重写方法的形参列表) -> {
	// 被重写方法的方法代码
}

Lambda表达式的使用前提:

  • Lambda表达式并不能简化所有匿名内部类的写法。
  • Lambda表达式只能简化接口中只有一个抽象方法的匿名内部类形式。

Lambda表达式只能简化函数式接口的匿名内部类写法

  • 首先必须是接口
  • 接口中只能有一个抽象方法

Lambda表达式简化Runnable接口匿名内部类

@FunctionalInterface函数式接口注解:一旦某个接口加上了这个注解,这个接口只能有且仅有一个抽象方法。
这个接口就可以被Lambda表达式简化。

Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":执行~~~");
    }
});
t.start();

Thread t1 = new Thread(() -> {
    System.out.println(Thread.currentThread().getName()+":执行~~~");
});
t1.start();

new Thread(() -> {
    System.out.println(Thread.currentThread().getName()+":执行~~~");
}).start();

new Thread(() -> System.out.println(Thread.currentThread().getName()+":执行~~~")).start();

Lambda表达式简化Comparator接口匿名内部类写法

List<Student> lists = new ArrayList<>();
Student s1 = new Student("李铭",18,'男');
Student s2 = new Student("冯龙",23,'男');
Student s3 = new Student("王乐乐",21,'男');
Collections.addAll(lists , s1 , s2 , s3);

// 按照年龄进行升序排序!
Collections.sort(lists, new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getAge() - s2.getAge();
    }
});

// 简化写法
Collections.sort(lists ,(Student t1, Student t2) -> {
    return t1.getAge() - t2.getAge();
});

Collections.sort(lists ,(Student t1, Student t2) -> t1.getAge() - t2.getAge());

// 参数类型可以省略
Collections.sort(lists ,( t1,  t2) -> t1.getAge() - t2.getAge());

System.out.println(lists);

Lambda表达式的省略写法

Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)

  • 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号
  • 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略";"不写
  • 参数类型可以省略不写
  • 如果只有一个参数,参数类型可以省略,同时()也可以省略
List<String> names = new ArrayList<>();
names.add("胡伟光");
names.add("甘挺");
names.add("洪磊");

names.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
});

names.forEach((String s) -> {
    System.out.println(s);
});

names.forEach((s) -> {
    System.out.println(s);
});

names.forEach(s -> {
    System.out.println(s);
});

names.forEach(s -> System.out.println(s) );

names.forEach(System.out::println);

第三章 Stream流

Stream流概述

在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念 ,用于解决已有集合/数组类库有的弊端。

Stream流能解决什么问题:

  • 可以解决已有集合类库或者数组API的弊端。
  • Stream认为集合和数组操作的API很不好用,所以采用了Stream流简化集合和数组的操作
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");

list.stream().filter(s -> s.startsWith("张")).filter( s -> s.length()== 3 )
    .forEach(System.out::println);

Stream流的获取

Stream流式思想的核心:

  • 是先得到集合或者数组的Stream流(就是一根传送带)
  • 然后就用这个Stream流操作集合或者数组的元素
  • 然后用Stream流简化替代集合操作的API
default Stream<E> stream();
Collection<String> c = new ArrayList<>();
Stream<String> ss = c.stream();

/** --------------------Map集合获取流-------------------------------   */
Map<String, Integer> map = new HashMap<>();
// 先获取键的Stream流。
Stream<String> keyss = map.keySet().stream();
// 在获取值的Stream流
Stream<Integer> valuess = map.values().stream();
// 获取键值对的Stream流(key=value: Map.Entry
Stream<Map.Entry<String,Integer>> keyAndValues = map.entrySet().stream();

/** ---------------------数组获取流------------------------------   */
// 数组也有Stream流。
String[] arrs = new String[]{"Java", "JavaEE" ,"Spring Boot"};
Stream<String> arrsSS1 = Arrays.stream(arrs);
Stream<String> arrsSS2 = Stream.of(arrs);

Stream流的常用API

  • forEach : 逐一处理(遍历)

  • count:统计个数

    long count();

  • filter: 过滤元素

  • Stream filter(Predicate predicate)

  • limit: 取前几个元素

  • skip: 跳过前几个

  • map : 加工方法

    把原来的元素加工以后,重新放上去

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    
  • concat : 合并流

    public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
    
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");

list.stream().filter( s -> s.length() == 3 ).filter( s -> s.startsWith("张"))
    .forEach( System.out::println);
// 统计数量
long count = list.stream().filter( s -> s.length() == 3 )
    .filter( s -> s.startsWith("张")).count();
System.out.println(count);
// 取前2个
list.stream().filter(s -> s.length() == 3).limit(2)
    .forEach(System.out::println);
// 跳过前2个
list.stream().filter(s -> s.length() == 3).skip(2)
    .forEach(System.out::println);
// 把名称加工成学生对象放入list中
list.stream().map(Student::new).forEach(System.out::println);

合并流

List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");

// 数组流
Stream<Integer> s1 = Stream.of(10, 20 ,30 ,40);
// 集合流
Stream<String> s2 = list.stream();
// 合并流
Stream<Object> s3 = Stream.concat(s1,s2);
s3.forEach(System.out::println);

Stream流的综合应用

List<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("老子");
one.add("庄子");
one.add("孙子");
one.add("洪七公");

List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("张三丰");
two.add("赵丽颖");
two.add("张二狗");
two.add("张天爱");
two.add("张三");

/**
         * 1. 第一个队伍只要名字为3个字的成员姓名;
         * 2. 第一个队伍筛选之后只要前3个人;
         */
Stream<String> oneStream =
    one.stream().filter(s -> s.length() == 3).limit(3);

/**
         * 3. 第二个队伍只要姓张的成员姓名;
         * 4. 第二个队伍筛选之后不要前2个人;
         * 5. 将两个队伍合并为一个队伍;
         */
Stream<String> twoStream =
    two.stream().filter(s -> s.startsWith("张")).skip(2);

Stream<String> allStream = Stream.concat(oneStream , twoStream);

/**
         * 6. 根据姓名创建`Student`对象; (加工)
         * 7. 打印整个队伍的Student对象信息。
         */
//allStream.map(s -> new Student(s)).forEach(System.out::println);
allStream.map(Student::new).forEach(System.out::println);

Stream流的终结与非终结方法

一旦Stream调用了终结方法,流的操作就全部终结了,不能继续使用,只能创建新的Stream操作。

终结方法: foreach , count

非终结方法:每次调用完成以后返回一个新的流对象,可以继续使用,支持链式编程!

List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");

// foreach终结方法
list.stream().filter(s -> s.startsWith("张"))
    .filter(s -> s.length() == 3).forEach(System.out::println);

long count =  list.stream().filter(s -> s.startsWith("张"))
    .filter(s -> s.length() == 3).count();
System.out.println(count);

收集Stream流

收集Stream流:把Stream流的数据转回成集合。

Stream的作用是:把集合转换成一根传送带,借用Stream流的强大功能进行的操作。但是实际开发中数据最终的形式还是应该是集合,最终Stream流操作完毕以后还是要转换成集合。这就是收集Stream流。

收集Stream流的含义:就是把Stream流的数据转回到集合中去。

List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");

Stream<String> zhangLists = list.stream().filter(s -> s.startsWith("张"));
// 把stream流转换成Set集合。
Set<String> sets = zhangLists.collect(Collectors.toSet());
System.out.println(sets);

// 把stream流转换成List集合。
Stream<String> zhangLists1 = list.stream().filter(s -> s.startsWith("张"));
List<String> lists= zhangLists1.collect(Collectors.toList());
System.out.println(lists);

// 把stream流转换成数组。
Stream<String> zhangLists2 = list.stream().filter(s -> s.startsWith("张"));
Object[] arrs = zhangLists2.toArray();
// 可以借用构造器引用申明转换成的数组类型!!!
//String[] arrs1 = zhangLists2.toArray(String[]::new);

第四章 File类

File类的概述

File类:代表操作系统的文件对象

File类:是用来操作操作系统的文件对象的,删除文件,获取文件信息,创建文件(文件夹)…

广义来说操作系统认为文件包含(文件和文件夹)

File类的创建文件对象的API:

  • 包:java.io.File
  • 构造器:
    • public File(String pathname):根据路径获取文件对象
    • public File(String parent, String child):根据父路径和文件名称获取文件对象!

File类创建文件对象的格式:

  • File f = new File("绝对路径/相对路径");
    • 绝对路径:从磁盘的的盘符一路走到目的位置的路径。
      • 绝对路径依赖具体的环境,一旦脱离环境,代码可能出错!!
      • 一般是定位某个操作系统中的某个文件对象。
    • 相对路径:不带盘符的。(重点)
      • 默认是直接相对到工程目录下寻找文件的。
      • 相对路径只能用于寻找工程下的文件。
      • 能用相对路径就应该尽量使用,可以跨平台!
  • File f = new File("文件对象/文件夹对象");
    广义来说:文件是包含文件和文件夹的。
  • 创建文件对象可以用绝对路径也可以用相对路径。
  • 相对路径只能用于寻找工程下的文件。
  • 文件对象可以表示文件也可以表示文件夹!
File f1 = new File("D:\\itcast\\图片资源\\beautiful.jpg");
System.out.println(f1.length()); // 获取文件的大小,字节大小

// 2.创建文件对象:使用相对路径
File f2 = new File("Day09Demo/src/dlei01.txt");
System.out.println(f2.length());

// 3.创建文件对象:代表文件夹。
File f3 = new File("D:\\itcast\\图片资源");
System.out.println(f3.exists());// 判断路径是否存在!!

File类的获取功能的方法

  • public String getAbsolutePath() :返回此File的绝对路径名字符串。
  • public String getPath() : 获取创建文件对象的时候用的路径
  • public String getName() : 返回由此File表示的文件或目录的名称。
  • public long length() : 返回由此File表示的文件的长度。
// 1.绝对路径创建一个文件对象
File f1 = new File("D:/itcast/图片资源/meinv.jpg");
// a.获取它的绝对路径。
System.out.println(f1.getAbsolutePath());
// b.获取文件定义的时候使用的路径。
System.out.println(f1.getPath());
// c.获取文件的名称:带后缀。
System.out.println(f1.getName());
// d.获取文件的大小:字节个数。
System.out.println(f1.length());

System.out.println("------------------------");

// 2.相对路径
File f2 = new File("Day09Demo/src/dlei01.txt");
// a.获取它的绝对路径。
System.out.println(f2.getAbsolutePath());
// b.获取文件定义的时候使用的路径。
System.out.println(f2.getPath());
// c.获取文件的名称:带后缀。
System.out.println(f2.getName());
// d.获取文件的大小:字节个数。
System.out.println(f2.length());

File类的判断功能的方法

  • public boolean exists() :此File表示的文件或目录是否实际存在。
  • public boolean isDirectory():此File表示的是否为目录。
  • public boolean isFile() :此File表示的是否为文件
// 1.文件对象。
File f1 = new File("D:\\itcast\\图片资源\\meinv.jpg");
// a.判断文件路径是否存在
System.out.println(f1.exists()); // true
// b.判断文件对象是否是文件,是文件返回true ,反之
System.out.println(f1.isFile()); // true
// c.判断文件对象是否是文件夹,是文件夹返回true ,反之
System.out.println(f1.isDirectory()); // false

// 1.文件对象。
File f2 = new File("D:\\itcast\\图片资源");
// a.判断文件路径是否存在
System.out.println(f2.exists()); // true
// b.判断文件对象是否是文件,是文件返回true ,反之
System.out.println(f2.isFile()); // false
// c.判断文件对象是否是文件夹,是文件夹返回true ,反之
System.out.println(f2.isDirectory()); // true

File类的创建和删除方法

  • public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,
    创建一个新的空文件。 (几乎不用的,因为以后文件都是自动创建的!)
  • public boolean delete() :删除由此File表示的文件或目录。 (只能删除空目录)
  • public boolean mkdir() :创建由此File表示的目录。(只能创建一级目录)
  • public boolean mkdirs() :可以创建多级目录(建议使用的)
File f = new File("Day09Demo/src/dlei02.txt");
// a.创建新文件,创建成功返回true ,反之
System.out.println(f.createNewFile());

// b.删除文件或者空文件夹
System.out.println(f.delete());
// 不能删除非空文件夹,只能删除空文件夹
File f1 = new File("D:/itcast/aaaaa");
System.out.println(f1.delete());

// c.创建一级目录
File f2 = new File("D:/itcast/bbbb");
System.out.println(f2.mkdir());

// d.创建多级目录
File f3 = new File("D:/itcast/e/a/d/ds/fas/fas/fas/fas/fas/fas");
System.out.println(f3.mkdirs());

File目录的遍历

  • public String[] list()
    获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
  • public File[] listFiles()常用
    获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
File dir = new File("day09/src/com/itheima");
// a.获取当前目录对象下的全部一级文件名称到一个字符串数组返回。
String[] names = dir.list();
for (String name : names) {
    System.out.println(name);
}
// b.获取当前目录对象下的全部一级文件对象到一个File类型的数组返回。
File[] files = dir.listFiles();
for (File file : files) {
    System.out.println(file.getAbsolutePath());
}

// ---------拓展------------
File f1 = new File("C:\\Users\\Administrator\\Documents\\codes\\notes\\java-notes\\java补充知识点\\codes\\seniorJava\\day09\\src\\com\\itheima\\_20File目录的遍历\\FileDemo.java");
long time = f1.lastModified(); // 最后修改时间!
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(time));

第五章 递归

递归的概述

方法在方法中又调用了自己

  • 直接递归:自己的方法调用自己
  • 间接递归:自己的方法调用别的方法,别的方法又调用自己
  • 递归是自己调用自己。
  • 递归如果控制的不恰当,会形成递归的死循环,从而导致栈内存溢出错误!!
  • 递归应该防止进入递归的死循环!

一个简单的例子,计算
f ( x ) = f ( x − 1 ) + 1 f(x) = f(x-1) + 1 f(x)=f(x1)+1

public class RecursionDemo02 {
    public static void main(String[] args) {
        System.out.println(f(10));
    }

    public static int f(int x){
        if(x == 1) {
            return 1;
        }else{
            return f(x - 1) + 1 ;
        }
    }
}

递归的核心

递归算法分为三个要素:

  • 递归公式
    f ( x ) = f ( x ) + 1 f(x)=f(x)+1 f(x)=f(x)+1

  • 递归终结点
    f ( 1 ) = 1 f(1)=1 f(1)=1

  • 递归方向

    必须走向终结点

必须满足三要素,否则递归会出现死亡

递归实现文件搜索

这是一个非规律递归,实现步骤是:

  • 定义一个方法用于做搜索
  • 进入方法中进行业务搜索分析
/**
     * 去某个目录下搜索某个文件
     * @param dir 搜索文件的目录。
     * @param fileName 搜索文件的名称。
     */
public static void searchFiles(File dir , String fileName){
    // 1.判断是否存在该路径,是否是文件夹
    if(dir.exists() && dir.isDirectory()){
        // 2.提取当前目录下的全部一级文件对象
        File[] files = dir.listFiles(); // null/[]
        // 3.判断是否存在一级文件对象(判断是否不为空目录)
        if(files!=null && files.length > 0){
            // 4.判断一级文件对象
            for (File f : files) {
                // 5.判断file是文件还是文件夹
                if(f.isFile()){
                    // 6.判断该文件是否为我要找的文件对象
                    if(f.getName().contains(fileName)){
                        System.out.println(f.getAbsolutePath());
                        try {
                            // 启动它(拓展)
                            Runtime r = Runtime.getRuntime();
                            r.exec(f.getAbsolutePath());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }else{
                    // 7.该文件是文件夹,文件夹要递归进入继续寻找
                    searchFiles(f ,fileName);
                }
            }
        }
    }
}

第六章 字节流

字符集

字符集:各个国家为自己国家的字符取的一套编号规则。计算机的底层是不能直接存储字符的。计算机的底层只能存储二进制。010101二进制就是可以转成10进制的。10进制就是整数编号。101 = 12^0 + 02^1 + 1*2^2 = 5

  • 中国用的编码:GBK编码
  • 美国用的编码:ACSII编码

IO流读写数据

IO输入输出流:输入/输出流。

  • Input:输入。
  • Output:输出。

引入:
File类只能操作文件对象本身,不能读写文件对象的内容。
读写数据内容,应该使用IO流。

IO流是一个水流模型:IO理解成水管,把数据理解成水流。

IO流的分类

按照流的方向分为:输入流,输出流。

  • 输出流:以内存为基准,把内存中的数据写出到磁盘文件或者网络介质中去的流称为输出流。
    输出流的作用:写数据到文件,或者写数据发送给别人。
  • 输入流:以内存为基准,把磁盘文件中的数据或者网络中的数据读入到内存中去的流称为输入流。
    输入流的作用:读取数据到内存。

按照流的内容分为: 字节流,字符流。

  • 字节流:流中的数据的最小单位是一个一个的字节,这个流就是字节流。
  • 字符流:流中的数据的最小单位是一个一个的字符,这个流就是字符流。(针对于文本内容)

所以流大体分为四大类:

  • 字节输入流:以内存为基准,把磁盘文件中的数据或者网络中的数据以一个一个的字节的形式读入到内存中去的流称为字节输入流。
  • 字节输出流:以内存为基准,把内存中的数据以一个一个的字节写出到磁盘文件或者网络介质中去的流称为字节输出流。
  • 字符输入流:以内存为基准,把磁盘文件中的数据或者网络中的数据以一个一个的字符的形式读入到内存中去的流称为字符输入流。
  • 字符输出流:以内存为基准,把内存中的数据以一个一个的字符写出到磁盘文件或者网络介质中去的流称为字符输出流。

IO流是读写传输数据的,IO流有很多种,每种流有自己的功能特点。

字节流的使用

IO流的体系

字节流 字节流 字符流 字符流
字节输入流 字节输出流 字符输入流 字符输出流
InputStream OutputStream Reader Writer(抽象类)
FileInputStream FileOutputStream FileReader FileWriter(子类实现类)

输入流

FileInputStream文件字节输入流

作用:以内存为基准,把磁盘文件中的数据按照字节的形式读入到内存中的流,简单的来说,就是按照字节读取文件数据到内存

构造器

  • public FileInputStream(File path):创建一个字节输入流管道与源文件对象接通
  • public FileInputStream(String pathName):创建一个字节输入流管道与文件路径对接

方法

  • public int read() 每次读取一个直接返回,读取完毕会返回-1
  • public int read(byte[] buffer) 从字节输入流中读取字节到字节数组中去,返回读取的字节数量,没有字节可读返回-1
  • 一个一个字节读取英文和数字没有问题。
  • 但是一旦读取中文输出无法避免乱码,因为会截断中文的字节。
  • 一个一个字节的读取数据,性能也较差,所以禁止使用此方案!
// 1.创建文件对象定位dlei01.txt
File file = new File("Day09Demo/src/dlei01.txt");
// 2.创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream(file);
// 3.读取一个字节的编号返回,读取完毕返回-1
//        int code1 = is.read(); // 读取一滴水,一个字节
//        System.out.println((char)code1);
//
//        int code2 = is.read(); // 读取一滴水,一个字节
//        System.out.println((char)code2);
//
//        int code3 = is.read(); // 读取一滴水,一个字节
//        System.out.println((char)code3);
//
//        int code4 = is.read(); // 读取一滴水,一个字节 ,读取没有字节返回-1
//        System.out.println(code4);

// 4.使用while读取字节数
// 定义一个整数变量存储字节
int ch = 0 ;
while((ch = is.read())!= -1){
    System.out.print((char) ch);
}

// 读法优化,必须使用循环     // abc xyz i
// a.定义一个字节数组代表桶   // ooo ooo o
byte[] buffer = new byte[3];
int len ; // 存储每次读取的字节数。
while((len = is.read(buffer)) != -1){
    // 读取了多少就倒出多少!
    String rs = new String(buffer , 0 , len);
    System.out.print(rs);
}

解决中文乱码

定义一个字节数组与文件的大小刚刚一样大,然后一桶水读取全部字节数据再输出

// 0.定位文件对象
File f = new File("C:\\Users\\Administrator\\Documents\\codes\\notes\\java-notes\\java补充知识点\\codes\\seniorJava\\day09\\src\\com\\itheima\\_25字节流的使用\\FileInputStreamDemo03.java");
// 1.定义一个字节输入流通向源文件路径,简化写法!
InputStream is = new FileInputStream(f);

// 2.定义一个字节数组与文件的大小刚刚一样大
//        System.out.println("文件大小:"+f.length());
//        byte[] buffer = new byte[(int) f.length()];
//        int len = is.read(buffer);
//        System.out.println("读取了:"+len);
//        String rs = new String(buffer);
//        System.out.println(rs);

byte[] buffer = is.readAllBytes();
String rs = new String(buffer);
System.out.println(rs);

输出流

FileOutputStream文件字节输出流

作用:以内存为基准,把内存中的数据,按照字节的形式写出到磁盘文件中去。简单来说,把内存数据按照字节写出到磁盘文件中去。

构造器:

  • public FileOutputStream(File file):创建一个字节输出流管道通向目标文件对象。
  • public FileOutputStream(String file):创建一个字节输出流管道通向目标文件路径。
  • public FileOutputStream(File file , boolean append):创建一个追加数据的字节输出流管道通向目标文件对象。
  • public FileOutputStream(String file , boolean append):创建一个追加数据的字节输出流管道通向目标文件路径。

方法:

  • public void write(int a):写一个字节出去 。

  • public void write(byte[] buffer):写一个字节数组出去。

  • public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。

    参数一,字节数组;参数二:起始字节索引位置,参数三:写多少个字节数出去。

  • 字节输出流只能写字节出去
  • 字节输出流默认是覆盖数据管道
  • 换行用: os.write("\r\n".getBytes());
  • 关闭和刷新:刷新流可以继续使用,关闭包含刷新数据但是流就不能使用了!

FileOutputStream字节输出流每次启动写数据的时候都会先清空之前的全部数据

字节流做文件复制

字节是计算机中一切文件的组成,所以字节流适合做一切文件的复制。

复制是把源文件的全部字节一字不漏的转移到目标文件,只要文件前后的格式一样,绝对不会有问题。

复制步骤:

  • 创建一个字节输入流管道与源文件接通。
  • 创建一个字节输出流与目标文件接通。
  • 创建一个字节数组作为桶
  • 从字节输入流管道中读取数据,写出到字节输出流管道即可。
  • 关闭资源!
InputStream is = null ;
OutputStream os = null ;
try{
    /** (1)创建一个字节输入流管道与源文件接通。 */
    is = new FileInputStream("D:\\itcast\\图片资源\\meinv.jpg");
    /** (2)创建一个字节输出流与目标文件接通。*/
    os = new FileOutputStream("D:\\itcast\\meimei.jpg");
    /** (3)创建一个字节数组作为桶*/
    byte[] buffer = new byte[1024];
    /** (4)从字节输入流管道中读取数据,写出到字节输出流管道即可。*/
    int len = 0;
    while((len = is.read(buffer)) != -1){
        // 读取多少就倒出多少
        os.write(buffer, 0 , len);
    }
    System.out.println("复制完成!");
}catch (Exception e){
    e.printStackTrace();
} finally {
    /**(5)关闭资源! */
    try{
        if(os!=null)os.close();
        if(is!=null)is.close();
    }catch (Exception e){
        e.printStackTrace();
    }
}

JDK1.7 开始之后释放资源的新方式

try-with-resources:

try(
    // 这里只能放置资源对象,用完会自动调用close()关闭
){

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

什么是资源?

  • 资源类一定是实现了Closeable接口,实现这个接口的类就是资源
  • 有close()方法,try-with-resources会自动调用它的close()关闭资源。
try(
    /** (1)创建一个字节输入流管道与源文件接通。 */
    InputStream is  = new FileInputStream("D:\\itcast\\图片资源\\meinv.jpg");
    /** (2)创建一个字节输出流与目标文件接通。*/
    OutputStream os = new FileOutputStream("D:\\itcast\\meimei.jpg");
    /** (5)关闭资源!是自动进行的 */
){
    /** (3)创建一个字节数组作为桶*/
    byte[] buffer = new byte[1024];
    /** (4)从字节输入流管道中读取数据,写出到字节输出流管道即可。*/
    int len = 0;
    while((len = is.read(buffer)) != -1){
        // 读取多少就倒出多少
        os.write(buffer, 0 , len);
    }
    System.out.println("复制完成!");
}catch (Exception e){
    e.printStackTrace();
}

你可能感兴趣的:(Java学习笔记,java,开发语言)