Method reference, Stream stream, File class, recursion, byte Stream

Keywords: Java JavaSE

JavaSE advanced Lambda expressions, method references, Stream streams, File classes

Chapter 1 new features of JDK8

New JDK features:

Lambda expression

Default method [learned]

Stream API

Method reference

Base64

1.1 Lambda expression

1.1.1 overview of functional programming ideas

In mathematics, a function is a set of calculation schemes with input and output, that is, "what to do with". Relatively speaking, object-oriented overemphasizes that "things must be done in the form of objects", while functional thinking tries to ignore the complex object-oriented syntax - emphasizing what to do rather than what form to do.

What to do, not how to do it

Do we really want to create an anonymous inner class object? no We just have to create an object to do this. What we really want to do is pass the code in the compare method body to the TreeSet collection.

Pass a piece of code -- that's our real purpose. Creating objects is only a means that has to be taken due to the limitation of object-oriented syntax. So, is there a simpler way? If we return our focus from "how to do" to "what to do", we will find that as long as we can better achieve the goal, the process and form are not important.

1.1.2 optimization of lambda

When you need to add events to Swing components, such as binding a mouse click event to a JButton button, you need to define the actions to be executed after triggering the event through the ActionListener interface.

Traditionally, the code is as follows:

public class LambdaDemo1 {
	public static void main(String[] args) {
        JButton button = new JButton("Button");
		button.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e){
                System.out.println("The button was clicked!");
            }
        });
	}
}

Based on the idea of "everything is an object", this approach is understandable: first, create an anonymous inner class object of ActionListener interface to specify the actions to be performed after the mouse click event of the button.

Code analysis:

For the anonymous inner class usage of ActionListener, several points can be analyzed:

  • The button binding listener requires the ActionListener interface as a parameter. The abstract actionPerformed method is used to specify the action to be performed after the button is clicked by the mouse;
  • In order to specify the method body of actionPerformed, you have to need the implementation class of ActionListener interface;
  • In order to save the trouble of defining an ActionListener implementation class, anonymous inner classes have to be used;
  • The abstract actionPerformed method must be overridden, so the method name, method parameters and method return value must be written again and cannot be written wrong;
  • In fact, it seems that only the method body is the key.

Lambda expression is written in the following code:

With the help of the new syntax of Java 8, the anonymous internal class writing method of the ActionListener interface can be equivalent through a simpler Lambda expression:

public class LambdaDemo1 {
	public static void main(String[] args) {
        JButton button = new JButton("Button");
		
        button.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e){
                System.out.println("The button was clicked!");
            }
        });
        
        //After simplifying Lambda expression
        button.addActionListener((ActionEvent e) -> {
       		System.out.println("The button was clicked!");
        });
	}
}

The execution effect of this code is exactly the same as that just now. It can be passed at the compilation level of 1.8 or higher. From the semantics of the code, we can see that the button triggers the operation executed after the mouse click event, and the executed operation is specified in a more concise form.

There is no longer the constraint of "having to create interface objects", and there is no longer the burden of "abstract method override", which is so simple!

1.1.3 format of lambda

standard format

Lambda omits the object-oriented rules and regulations, and the format consists of three parts:

  • Some parameters
  • An arrow
  • A piece of code

The standard format for Lambda expressions is:

(Parameter type parameter name) -> { Code statement }

Format Description:

  • The syntax in parentheses is consistent with the parameter list of traditional methods: leave blank if there is no parameter; Multiple parameters are separated by commas.
  • ->Is a newly introduced syntax format, which represents pointing action.
  • The syntax in braces is basically consistent with the requirements of traditional method body.

Comparison between anonymous inner class and lambda:

//Anonymous Inner Class 
button.addActionListener(new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent e){
        System.out.println("The button was clicked!");
    }
});

//lambda
button.addActionListener((ActionEvent e) -> {
    System.out.println("The button was clicked!");
});

Carefully analyze the code. The ActionListener interface has only one definition of actionPerformed method:

  • public abstract void actionPerformed(ActionEvent e);

That is, a plan for doing things (actually a method)

Summary: lambda expressions simplify anonymous inner classes. First, the interface is required. Second, the interface has only one abstract method.

No parameters

  • No parameter: the scheme can be executed without any conditions.
  • No return value: the scheme does not produce any results.
  • Code block (method): the specific implementation steps of the scheme.

The same semantics is embodied in Lambda grammar, which should be simpler:

//Writing in thread
() -> System.out.println("Multithreaded task execution!")
  • The first pair of parentheses are the parameters of the run method (none), which means that no conditions are required;
  • An arrow in the middle represents passing the previous parameter to the following code;
  • The following output statement is the business logic code.

There are parameters and return values

The following example demonstrates the use scenario code of the Java. Util. Comparator < T > interface, in which the abstract method is defined as:

  • public abstract int compare(T o1, T o2);

When sorting an array of objects, the Arrays.sort method needs a Comparator interface instance to specify the sorting rules. Suppose there is a Person class with two member variables: String name and int age:

public class Person { 
    private String name;
    private int age;
    
    // Omit constructor, toString method and Getter Setter 
}

Traditional writing

If the traditional code is used to sort the Person [] array, it is written as follows:

public class LambdaDemo2 {
    public static void main(String[] args) {
      	// An unordered array of objects
        Person[] array = { 
            new Person("Gulinaza", 19),
            new Person("Delireba", 18),       		
            new Person("Marzaha", 20) 
        };

      	// Anonymous Inner Class 
        Comparator<Person> comp = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        };
        Arrays.sort(array, comp); // The second parameter is the collation, that is, the Comparator interface instance

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

This approach seems to be "taken for granted" in the idea of object-oriented. The instance of Comparator interface (using anonymous inner class) represents the sorting rule of "from small to large by age".

code analysis

Let's find out what the above code really does.

  • In order to sort, the Arrays.sort method needs sorting rules, that is, the instance of the Comparator interface, and the abstract method compare is the key;
  • In order to specify the method body of compare, you have to need the implementation class of the Comparator interface;
  • In order to save the trouble of defining a ComparatorImpl implementation class, anonymous inner classes have to be used;
  • The abstract compare method must be overridden, so the method name, method parameters and method return value must be written again and cannot be written wrong;
  • In fact, only parameters and method bodies are critical.

Lambda writing

public class LambdaDemo2 {
    public static void main(String[] args) {
        Person[] array = {
          	new Person("Gulinaza", 19),
          	new Person("Delireba", 18),
          	new Person("Marzaha", 20) 
        };

        // Anonymous Inner Class 
        Arrays.sort(array, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        }); 

        for (Person person : array) {
            System.out.println(person);
        }
        
        System.out.println("--------------------------------");
        
        // Lambda
        Arrays.sort(array, (Person a, Person b) -> {
          	return a.getAge() - b.getAge();
        });

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

Omit format

Omission rule

On the basis of Lambda standard format, the rules for using ellipsis are as follows:

  1. The types of parameters in parentheses can be omitted;
  2. If there is only one parameter in the parenthesis, the parenthesis can be omitted;
  3. If there is only one statement in braces, you can omit braces and semicolons regardless of whether there is a return value. You must omit the return keyword.

It can be deduced and omitted

Lambda emphasizes "what to do" rather than "how to do", so any information that can be deduced from the context can be omitted.

For example, the above example can also use Lambda's ellipsis:

//Runnable interface simplification:
1. () -> System.out.println("Multithreaded task execution!")

//Comparator interface simplification:
2. Arrays.sort(array, (a, b) -> a.getAge() - b.getAge());

//ActionListener interface simplification:
3.button.addActionListener(e -> System.out.println("The button was clicked!"));

1.1.4 prerequisites for lambda

Lambda's syntax is very concise and completely free from the constraints of object-oriented complexity. However, there are several problems that need special attention when using:

  1. Lambda must have an interface, and there must be only one abstract method in the interface.
    Whether it is the built-in Runnable, Comparator interface or user-defined interface of JDK, Lambda can be used only when the abstract methods in the interface exist and are unique.
  2. Using Lambda must have context inference.
    That is, the parameter or local variable type of the method must be the interface type corresponding to Lambda, and Lambda can be used as an instance of the interface.

Note: an interface with only one abstract method is called "functional interface".

1.2 functional interface

1.2.1 functional interface concept

A functional interface is an interface that has only one abstract method, but can have multiple non abstract methods. The @ functional interface annotation can detect whether the interface is a functional interface. (in cooperative development, avoid errors caused by colleagues modifying this interface)

1.2.2 function definition and use

Define a functional interface:

//to greet
@FunctionalInterface
public interface GreetingService {
   void sayMessage();
}

Define the implementation class of this functional interface:

public class GreetingServiceImpl implements GreetingService {
    @Override
    public void sayMessage() {
        System.out.println("say hello!");
    }
}

Functional interfaces are generally used as parameter and return value types of methods:

public class Demo {
    //Define a method whose parameters use the functional interface GreetingService
    public static void show(GreetingService greetingService){
        greetingService.sayMessage();
    }

    public static void main(String[] args) {
        //Call the show method. The parameter of the method is an interface, so you can pass the implementation class of the interface
        show(new GreetingServiceImpl());

        //Call the show method. The parameter of the method is an interface, so you can pass the anonymous inner class of the interface
        show(new GreetingService() {
            @Override
            public void sayMessage() {
                System.out.println("Overriding abstract methods in interfaces with anonymous inner classes");
            }
        });
    }
}

lambda expression:

public class Demo {
    //Define a method whose parameters use the functional interface GreetingService
    public static void show(GreetingService greetingService){
        greetingService.sayMessage();
    }

    public static void main(String[] args) {
        //Call the show method and use a lambda expression
        show(()->System.out.println("use lambda Override abstract methods in interfaces"));
    }
}

1.2.3 benefits of using lambda expressions

Advantages: 1. Simplified code, more concise 2. Delayed loading

After the code of some scenarios is executed, the results may not be used, resulting in performance waste. Lambda expressions are delayed, which can be used as a solution to improve performance.

For example, log cases of performance waste
PS: logs can help us quickly locate problems and record the situation during program operation, so as to facilitate project monitoring and optimization.

A typical scenario is to conditionally use parameters. For example, after splicing log messages, print them when the conditions are met:

public class Test {
  public static void main(String[] args) {
    String msgA = "hello";
    String msgB = "world";
    String msgC = "java";
    showLog(1, msgA + msgB + msgC);
  }
  
  private static void showLog(int level, String msg) {
    //When the log level is 1, the log is printed
    if (level == 1) {
      System.out.println(msg);
    }
  }
}

There is a problem with this Code: whether the level meets the requirements or not, as the second parameter of the showLog method, the three strings must be spliced and passed into the method first, and then the level judgment will be made. If the level does not meet the requirements, the string splicing operation will be done in vain, resulting in performance waste.

To use lambda, you must first define a functional interface:

@FunctionalInterface
interface MessageBuilder{
    String buildMessage();
}

Modification of showLog method

public class Test {
    public static void main(String [] ags){
        String msgA = "hello";
        String msgB = "world";
        String msgC = "java";
        
        //lambda implementation display log
        showLogLambda(1,()->msgA+msgB+msgC);
        
        //Proving the delayed execution of lambda expressions
        showLogLambda(1,()->{
            System.out.println("Lambda Execute!");
            return msgA+msgB+msgC;
        });
    }
    
    private static void showLogLambda(int level,MessageBuilder builder){
        if(level==1){
            System.out.println(builder.buildMessage());
        }   
    }
}

In this way, only when the level meets the requirements will the three strings be spliced; Otherwise, the three strings will not be spliced.

1.2.4 common functional interfaces

To use lambda expressions, we need to create a functional interface. Isn't it troublesome to use lambda expressions every time? At this time, java has built-in four core functional interfaces for us.

Four core functional interfaces

Functional interfaceParameter typeReturn typepurpose
Supplier < T > interface, supply interfacenothingTReturn an object of type T, T get()
Consumer < T > interface, consumer interfaceTvoidApply an operation to an object of type T, void accept (T)
Function < T, R > functional interfaceTRGet the result of type R according to the parameters of type T, R apply (T)
Predicate < T > interfaceTbooleanScenario for condition judgment, Boolean test (T)

Supplier < T > interface

The java.util.function.Supplier interface contains only one parameterless method: T get(). Used to get object data of the type specified by a generic parameter. Because this is a functional interface, it means that the corresponding Lambda expression needs to "provide" an object data conforming to the generic type.

public class TestSupplier {
    private static String getString(Supplier<String> function){
        return function.get();  
    }

    public static void main(String[] args) {
        String msgA= "Hello";
        String msgB="World";
        System.out.println(getString(()->msgA+msgB));
  }
}

Consumer < T > interface

The java.util.function.Consumer interface is just opposite to the Supplier interface. It does not produce a data, but consumes a data, and its data type is determined by generics.
Consumer provides the abstract method of accept(T t) and the default method of andThen. The source code is as follows:

Abstract method accept

The Consumer interface contains the abstract method void accept (T), which means to consume data of a specified generic type.

Basic usage, such as:

public class TestConsumer {
    public static void main(String[] args) {
        consumerString(s-> System.out.println(s));
    }
    
    private static void consumerString(Consumer<String> consumer){
        consumer.accept("hello world");
    }
}
Default method andThen

If the parameters and return values of a method are all of Consumer type, the effect can be achieved: when consuming data, first do an operation, and then do an operation to realize combination. This method is the default method andThen in the Consumer interface.

public class TestConsumerAndThen {
    public static void main(String[] args) {
        cosumerAndthen(s-> System.out.println(s.toLowerCase()),s-> System.out.println(s.toUpperCase()));
    }

    private static void cosumerAndthen(Consumer<String> one,Consumer<String> two){
        one.andThen(two).accept("hello world");
    }
}

Classroom practice

//Formatted print information String[] array = {"Zhang San, female", "Li Si, female", "Wang Erma, male"};
There are multiple pieces of information in the above string array. Please name according to the format: XX. Gender: XX. Print the information in a format. The action of printing the name is required as the first one Consumer Interfaced Lambda Example, take the action of printing gender as the second Consumer Interfaced Lambda example.

Function < T, R > interface

The java.util.function.Function interface is used to obtain data of another type from data of one type. The former is called pre condition and the latter is called post condition.

Abstract method apply

The main abstract method in the function < T, R > interface is: R apply (T, t), which obtains the results of type R according to the parameters of type T.

//Convert String type to Integer type
public class TestFunction {
    public static void main(String[] args) {
        method(s->Integer.parseInt(s));
    }

    private static Integer method(Function<String,Integer> function){
        int num =  function.apply("10");
        System.out.println(num);
        return num;
    }
}
Default method andThen

There is a default andThen method in the Function interface, which is used for combination operations. This method is also used in the "do what first, then do what" scenario, which is similar to andThen in Consumer:

//The first operation is to parse the string into an int number, and the second operation is to multiply by 10. The two operations are combined in sequence by andThen.
public class TestFunctionAndthen {
    public static void main(String[] args) {
        method(s->Integer.parseInt(s) , i->i*10);
    }

    private static void method(Function<String,Integer> one,Function<Integer,Integer> two){
        int num = one.andThen(two).apply("10");
        System.out.println(num);
    }
}

Classroom practice

//Custom function model splicing
 Please use Function For the splicing of function models, multiple function operations to be performed in order are:
String str = "Zhang San,20";
Intercepting the digital age part of the string to obtain the string;
Convert the string from the previous step to int Type number;
Change the previous step int Add 100 to the number to get the result int Number.

Predict < T > interface

Sometimes we need to judge some type of data to get a boolean value result. You can use
Java.util.function.predict interface.

Abstract method test

The predict interface contains an abstract method: Boolean test (T).

Scenarios for condition judgment:

public class TestPredicate {
    public static void main(String[] args) {
        method(s -> s.length()>5);
    }
    
    private static void method(Predicate<String> predicate){
        boolean b = predicate.test("HelloWorld");
        System.out.println("Long string?"+b);
    }
}
Default method and

Since it is conditional judgment, there will be three common logical relationships: and, or and non. The default method and. Can be used when two Predicate conditions are connected with and logic to achieve the effect of "and"
Judge whether a string should contain both uppercase "H" and uppercase "W", then:

public class TestPredicateAnd {
    public static void main(String[] args) {
        method(s->s.contains("H"),s->s.contains("W"));

    }
    private static void method(Predicate<String> one,Predicate<String> two){
        boolean b = one.and(two).test("HelloWorld");
        System.out.println("Does the string meet the requirements?"+b);

    }
}
Default method or

If you want to realize the logic "string contains uppercase H or uppercase W", the code only needs to change "and" to "or" name, and everything else remains the same:

public class TestPredicateOr {
    public static void main(String[] args) {
        method(s->s.contains("H"),s->s.contains("W"));

    }
    private static void method(Predicate<String> one,Predicate<String> two){
        boolean b = one.or(two).test("HelloWorld");
        System.out.println("Does the string meet the requirements?"+b);
    }
}
The default method is negative

"And" and "or" have been understood, and the remaining "non" (negation) will be simple

public class TestPredicate {
    public static void main(String[] args) {
        method(s->s.length()>5);
    }
    
    private static void method(Predicate<String> predicate){
        boolean b = predicate.negate().test("HelloWorld");
        System.out.println("Long string?"+b);
    }
}

1.3 method reference

1.3.1 method Reference Overview

Method reference enables developers to directly reference existing methods, Java class construction methods or instance objects. Method reference and Lambda expression are used together to make the construction method of Java class look compact and concise without many complex template codes.

1.3.2 reference to static methods

Reference format:

Class name:: static method

Simplify steps:

Define a static method and put the code that needs to be simplified into a static method.

Precautions:

The parameter list of the referenced method should be consistent with the parameter list of the abstract method in the functional interface.

If an abstract method in a functional interface has a return value, the referenced method must also have the same return value.

If the abstract method in the functional interface has no return value, the referenced method can have or have no return value.

// Lambda
Arrays.sort(array, (Person a, Person b) -> {
    return a.getAge() - b.getAge();
});
//Comparator interface simplification:
Arrays.sort(array, (a, b) -> a.getAge() - b.getAge());


//Reference simplification of static methods:
public static int compareByAge(Person a, Person b) {
    return a.getAge() - b.getAge();
}
//Simplify 1:
Arrays.sort(array, (a, b) -> Person.compareByAge(a,b));
//Simplify 2:
Arrays.sort(array, Person::compareByAge);

1.3.3 reference of instance method

Reference format:

Object:: instance method

Simplify steps:

Define an instance method and put the required code into the instance method

Precautions:

The parameter list of the referenced method should be consistent with the parameter list of the abstract method in the functional interface.

List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");

//Simplify 1:
list.forEach(s -> System.out.println(s));
//Simplification 2: the object is System.out=new PrintStream(); The instance method is println()
list.forEach(System.out::println);

1.3.4 references to specific types of methods

Reference format:

Specific type: method

Feature type:

String, any type

Precautions:

If the first parameter in the parameter list of the abstract method in the functional interface is the caller of the following method, and the other parameters are the formal parameters of the following method, it can be referenced with a specific type of method.

Arrays.sort(strs, (String s1 , String s2) -> {
   return s1.compareToIgnoreCase(s2); 
});

//Simplify 1:
Arrays.sort(strs, (s1,s2) -> s1.compareToIgnoreCase(s2));
//Simplify 2:
Arrays.sort(strs, String::compareToIgnoreCase);

1.3.5 constructor reference

Reference format:

Class name:: new

Precautions:

The formal parameter list of the abstract method of the functional interface is consistent with that of the constructor. The return value type of the abstract method is the type of the class to which the constructor belongs.

Similar to s - > new student (s) = > Student:: New

//Nonparametric structure
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());

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

Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
//Parametric structure
Function<Integer,Employee> func1 = id -> new Employee(id);
System.out.println(func1.apply(1));

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

Function<Integer,Employee> func2 = Employee::new;
System.out.println(func2.apply(1));

Array reference: if the array is regarded as a special class, the writing method is consistent with the constructor reference

Function<Integer,String[]> func1 = (length)->new String[length];
String[] arr1 = func1.apply(6);
System.out.println(Arrays.toString(arr1));

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

Function<Integer,String[]> func2 = String[]::new;
String[] arr2=func2.apply(1);
System.out.println(Arrays.toString(arr2));

Chapter II Stream

In Java 8, thanks to the functional programming brought by Lambda, a new Stream concept is introduced to solve the existing disadvantages of the existing collection class library.

2.1 introduction

Multi step traversal code of traditional collection

Almost all collections (such as Collection interface or Map interface) support direct or indirect traversal operations. When we need to operate on the elements in the Collection, in addition to the necessary addition, deletion and acquisition, the most typical is Collection traversal.

For example:

public class Demo01ForEach {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("zhang wuji");
        list.add("Zhou Zhiruo");
        list.add("Zhao Min");
        list.add("Zhang Qiang");
        list.add("Zhang Sanfeng");
        for (String name : list) {
          	System.out.println(name);
        }
    }  
}

This is a very simple set traversal operation: print out each string in the set.

Disadvantages of loop traversal

Lambda of Java 8 allows us to focus more on What rather than How. This has been compared with internal classes. Now, let's take a closer look at the above example code, and we can find that:

  • The syntax of the for loop is "how"
  • The loop body of the for loop is "what to do"

Why use loops? Because of traversal. But is loop the only way to traverse? Traversal refers to the processing of each element one by one, rather than a cycle from the first to the last. The former is the purpose and the latter is the way.

Imagine if you want to filter the elements in the collection:

  1. Filter set A into subset B according to condition 1;
  2. Then it is filtered into subset C according to condition 2.

What can we do? Before Java 8, the practice may be as follows:

public class Demo02NormalFilter {
  	public static void main(String[] args) {
      	List<String> list = new ArrayList<>();
        list.add("zhang wuji");
        list.add("Zhou Zhiruo");
        list.add("Zhao Min");
        list.add("Zhang Qiang");
        list.add("Zhang Sanfeng");

        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("Zhang")) {
              	zhangList.add(name);
            }
        }

        List<String> shortList = new ArrayList<>();
        for (String name : zhangList) {
            if (name.length() == 3) {
              	shortList.add(name);
            }
        }

        for (String name : shortList) {
          	System.out.println(name);
        }
    }
}

This code contains three loops, each with different functions:

  1. First, screen all people surnamed Zhang;
  2. Then screen people whose names have three words;
  3. Finally, print out the results.

Whenever we need to operate on the elements in the collection, we always need to cycle, cycle and recycle. Is this taken for granted** No** Circulation is a way of doing things, not an end. On the other hand, using a linear loop means that it can only be traversed once. If you want to iterate again, you can only start from scratch with another loop.

How can Lambda's derivative Stream bring us more elegant writing?

Better writing of Stream

Let's take a look at what elegance is with the Stream API of Java 8:

public class Demo03StreamFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("zhang wuji");
        list.add("Zhou Zhiruo");
        list.add("Zhao Min");
        list.add("Zhang Qiang");
        list.add("Zhang Sanfeng");

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

Directly reading the literal meaning of the code can perfectly display the semantics of irrelevant logical methods: obtain stream, filter surname Zhang, filter length 3, and print one by one. The code does not reflect the use of linear loops or any other algorithm for traversal. What we really want to do is better reflected in the code.

2.2 overview of streaming ideas

Note: Please temporarily forget the inherent impression of traditional IO streams!

On the whole, the flow idea is similar to the "production line" in the factory workshop.

When multiple elements need to be operated (especially multi-step operation), considering the performance and convenience, we should first spell out a "model" step scheme, and then execute it according to the scheme.

This figure shows multi-step operations such as filtering, mapping, skipping and counting. This is a processing scheme for collection elements, and the scheme is a "function model". Each box in the figure is a "flow", which can be converted from one flow model to another by calling the specified method. The number 3 on the far right is the final result.

The filter, map and skip here are all operating on the function model, and the collection elements are not really processed. Only when the termination method count is executed, the whole model will execute the operation according to the specified policy. This is due to Lambda's delayed execution feature.

Note: "Stream stream" is actually a functional model of collection elements. It is neither a collection nor a data structure. It does not store any elements (or their address values).

2.3 access flow method

Java. Util. Stream. Stream < T > is the most commonly used stream interface newly added to Java 8. (this is not a functional interface.)

Obtaining a stream is very simple. There are several common methods:

  • All Collection collections can obtain streams through the stream default method;
  • The static method of the Stream interface can obtain the Stream corresponding to the array.

Method 1: get the stream according to the Collection

First, the default method stream is added to the java.util.Collection interface to obtain the stream, so all its implementation classes can obtain the stream.

import java.util.*;
import java.util.stream.Stream;

public class Demo04GetStream {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // ...
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        // ...
        Stream<String> stream2 = set.stream();

        Vector<String> vector = new Vector<>();
        // ...
        Stream<String> stream3 = vector.stream();
    }
}

Method 2: get the flow according to the Map

java.util.Map interface is not a sub interface of Collection, and its K-V data structure does not conform to the single characteristics of flow elements, so obtaining the corresponding flow needs to be divided into key, value or entry:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class Demo05GetStream {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        // ...
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

Method 3: get the stream according to the array

If you are using an array instead of a collection or mapping, since it is impossible to add a default method to the array object, the Stream interface provides a static method of, which is very simple to use:

import java.util.stream.Stream;

public class Demo06GetStream {
    public static void main(String[] args) {
        String[] array = { "zhang wuji", "Zhang Cuishan", "Zhang Sanfeng", "Zhang Yiyuan" };
        Stream<String> stream = Stream.of(array);
    }
}

Note: the parameter of the of method is actually a variable parameter, so arrays are supported.

2.4 common methods

The operation of flow model is very rich. Here are some common API s. These methods can be divided into two types:

  • Termination method: the return value type is no longer the method of the Stream interface itself, so chained calls like StringBuilder are no longer supported. In this section, the termination methods include count and forEach methods.
  • Non terminating method: the return value type is still the method of the Stream interface itself, so chain call is supported. (except the terminating method, all other methods are non terminating methods.)

Function splicing and termination method

Among the various methods described above, those whose return value is still the Stream interface are function splicing methods, which support chain calls; The return value is no longer the end method of the Stream interface, and chain calls are no longer supported. As shown in the following table:

Method nameMethod functionMethod typeWhether chain call is supported
countNumber of StatisticsEndno
forEachProcess one by oneEndno
filterfilterFunction splicingyes
limitTake the first fewFunction splicingyes
skipSkip the first fewFunction splicingyes
mapmappingFunction splicingyes
concatcombinationFunction splicingyes

Note: for more methods beyond this section, please refer to the API documentation.

forEach: process one by one

Although the method name is forEach, unlike the nickname "for each" in the for loop, this method does not guarantee that the consumption actions of elements one by one are executed orderly in the flow.

void forEach(Consumer<? super T> action);

This method receives a Consumer interface function and will give each stream element to this function for processing. For example:

import java.util.stream.Stream;

public class Demo12StreamForEach {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        stream.forEach(s->System.out.println(s));
    }
}

Count: count the number

Just like the size method in the old Collection, the flow provides the count method to count the number of elements:

long count();

This method returns a long value representing the number of elements (it is no longer an int value like the old collection). Basic usage:

public class Demo09StreamCount {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.filter(s -> s.startsWith("Zhang"));
        System.out.println(result.count()); // 2
    }
}

Filter: filter

You can convert a stream into another subset stream through the filter method. Method statement:

Stream<T> filter(Predicate<? super T> predicate);

The interface receives a Predicate functional interface parameter (which can be a Lambda or method reference) as a filter condition.

Basic use

The basic code used by the filter method in the Stream stream is as follows:

public class Demo07StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.filter(s -> s.startsWith("Zhang"));
    }
}

Here, the filter condition is specified through Lambda expression: must be Zhang.

limit: access the first few

The limit method can intercept convection, and only the first n can be used. Method signature:

Stream<T> limit(long maxSize);

The parameter is a long type. If the current length of the collection is greater than the parameter, it will be intercepted; Otherwise, do not operate. Basic usage:

import java.util.stream.Stream;

public class Demo10StreamLimit {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.limit(2);
        System.out.println(result.count()); // 2
    }
}

Skip: skip the first few

If you want to skip the first few elements, you can use the skip method to get a new stream after interception:

Stream<T> skip(long n);

If the current length of the stream is greater than N, skip the first n; Otherwise, you will get an empty stream with a length of 0. Basic usage:

import java.util.stream.Stream;

public class Demo11StreamSkip {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.skip(2);
        System.out.println(result.count()); // 1
    }
}

map: mapping

If you need to map elements in a stream to another stream, you can use the map method. Method signature:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

This interface requires a Function type interface parameter, which can convert T-type data in the current stream into another R-type stream.

Basic use

The basic code used by the map method in the Stream stream is as follows:

import java.util.stream.Stream;

public class Demo08StreamMap {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("10", "12", "18");
        Stream<Integer> result = original.map(s->Integer.parseInt(s));
    }
}

In this code, the parameters of the map method convert the string type into the int type through the method reference (and are automatically boxed into an Integer class object).

concat: combination

If there are two streams and you want to merge them into one Stream, you can use the static method concat of the Stream interface:

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

Note: This is a static method, which is different from the concat method in java.lang.String.

The basic usage code of this method is as follows:

import java.util.stream.Stream;

public class Demo12StreamConcat {
    public static void main(String[] args) {
        Stream<String> streamA = Stream.of("zhang wuji");
        Stream<String> streamB = Stream.of("Zhang Cuishan");
        Stream<String> result = Stream.concat(streamA, streamB);
    }
}

2.5 Stream comprehensive case

Now there are two ArrayList collections to store the names of multiple members in the team. The traditional for loop (or enhanced for loop) is required to perform the following steps in turn:

  1. The first team only needs the name of the member whose name is three words;
  2. After the first team screening, only the first three people;
  3. The second team only needs the names of members surnamed Zhang;
  4. After the second team is screened, do not use the first two people;
  5. Merge the two teams into one team;
  6. Create a Person object based on the name;
  7. Print the Person object information of the whole team.

The codes of the two teams (sets) are as follows:

public class DemoArrayListNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("Delireba");
        one.add("Song Yuanqiao");
        one.add("Su Xinghe");
        one.add("Laozi");
        one.add("Zhuangzi");
        one.add("grandson");
        one.add("master hongqi");

        List<String> two = new ArrayList<>();
        two.add("Gulinaza");
        two.add("zhang wuji");
        two.add("Zhang Sanfeng");
        two.add("Zhao Liying");
        two.add("Zhang Ergou");
        two.add("Zhang Tianai");
        two.add("Zhang San");
		// ....
    }
}

The code of the Person class is:

public class Person {
    
    private String name;

    public Person() {}

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "'}";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Traditional way

Using the for loop, example code:

public class DemoArrayListNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        // ...

        List<String> two = new ArrayList<>();
        // ...

        // The first team only needs the name of the member whose name is three words;
        List<String> oneA = new ArrayList<>();
        for (String name : one) {
            if (name.length() == 3) {
                oneA.add(name);
            }
        }

        // After the first team screening, only the first three people;
        List<String> oneB = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            oneB.add(oneA.get(i));
        }

        // The second team only needs the names of members surnamed Zhang;
        List<String> twoA = new ArrayList<>();
        for (String name : two) {
            if (name.startsWith("Zhang")) {
                twoA.add(name);
            }
        }

        // After the second team is screened, do not use the first two people;
        List<String> twoB = new ArrayList<>();
        for (int i = 2; i < twoA.size(); i++) {
            twoB.add(twoA.get(i));
        }

        // Merge the two teams into one team;
        List<String> totalNames = new ArrayList<>();
        totalNames.addAll(oneB);
        totalNames.addAll(twoB);

        // Create a Person object based on the name;
        List<Person> totalPersonList = new ArrayList<>();
        for (String name : totalNames) {
            totalPersonList.add(new Person(name));
        }

        // Print the Person object information of the whole team.
        for (Person person : totalPersonList) {
            System.out.println(person);
        }
    }
}

The operation result is:

Person{name='Song Yuanqiao'}
Person{name='Su Xinghe'}
Person{name='master hongqi'}
Person{name='Zhang Ergou'}
Person{name='Zhang Tianai'}
Person{name='Zhang San'}

Stream mode

The equivalent Stream stream processing code is:

public class DemoStreamNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        // ...

        List<String> two = new ArrayList<>();
        // ...

        // The first team only needs the name of the member whose name is three words;
        // After the first team screening, only the first three people;
        Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);

        // The second team only needs the names of members surnamed Zhang;
        // After the second team is screened, do not use the first two people;
        Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("Zhang")).skip(2);

        // Merge the two teams into one team;
        // Create a Person object based on the name;
        // Print the Person object information of the whole team.
        Stream.concat(streamOne, streamTwo).map(s-> new Person(s)).forEach(s->System.out.println(s));
    }
}

The operation effect is exactly the same:

Person{name='Song Yuanqiao'}
Person{name='Su Xinghe'}
Person{name='master hongqi'}
Person{name='Zhang Ergou'}
Person{name='Zhang Tianai'}
Person{name='Zhang San'}

2.6 collect Stream results

After the convection operation is completed, how to collect the results, such as obtaining the corresponding set, array, etc?

Collect into collection

The stream stream provides the collect method. Its parameters require a Java. Util. Stream. Collector < T, a, R > interface object to specify which collection to collect. Fortunately, the java.util.stream.Collectors class provides some methods that can be used as instances of the collector interface:

  • public static <T> Collector<T, ?, List < T > > tolist(): convert to list set.
  • public static <T> Collector<T, ?, Set < T > > toset(): convert to set.

Here are the basic usage codes of these two methods:

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Demo15StreamCollect {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        List<String> list = stream.collect(Collectors.toList());
        Set<String> set = stream.collect(Collectors.toSet());
    }
}

Collect into array

Stream provides toArray method to put the result into an array. Due to generic erasure, the return value type is Object []:

Object[] toArray();

Its usage scenarios are as follows:

import java.util.stream.Stream;

public class Demo16StreamArray {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        Object[] objArray = stream.toArray();
    }
}

Chapter 3 File class

3.1 general

java.io.File class is an abstract representation of file and directory pathnames. It is mainly used for file and directory creation, search, deletion and other operations.

3.2 construction method

  • public File(String pathname): create a new File instance by converting the given pathname string to an abstract pathname.

  • public File(String parent, String child): creates a new File instance from the parent pathname string and the child pathname string.

  • public File(File parent, String child): creates a new File instance from the parent abstract pathname and child pathname strings.

  • For example, the code is as follows:

// File pathname
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname); 

// File pathname
String pathname2 = "D:\\aaa\\bbb.txt";
File file2 = new File(pathname2); 

// Pass parent and child path strings
 String parent = "d:\\aaa";
 String child = "bbb.txt";
 File file3 = new File(parent, child);

// String through parent File object and child path
File parentDir = new File("d:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child);

Tips:

  1. A File object represents a File or directory that actually exists in the hard disk.
  2. Whether a File or directory exists in this path does not affect the creation of the File object.

3.3 common methods

Method of obtaining function

  • public String getAbsolutePath(): returns the absolute pathname string of this File.

  • public String getPath(): converts this File to a pathname string.

  • public String getName(): returns the name of the File or directory represented by this File.

  • public long length(): returns the length of the File represented by this File.

    Method demonstration, the code is as follows:

    public class FileGet {
        public static void main(String[] args) {
            File f = new File("d:/aaa/bbb.java");     
            System.out.println("File absolute path:"+f.getAbsolutePath());
            System.out.println("File construction path:"+f.getPath());
            System.out.println("File name:"+f.getName());
            System.out.println("file length:"+f.length()+"byte");
    
            File f2 = new File("d:/aaa");     
            System.out.println("Directory absolute path:"+f2.getAbsolutePath());
            System.out.println("Directory construction path:"+f2.getPath());
            System.out.println("Directory name:"+f2.getName());
            System.out.println("Directory length:"+f2.length());
        }
    }
    Output results:
    File absolute path:d:\aaa\bbb.java
     File construction path:d:\aaa\bbb.java
     File name:bbb.java
     file length:636 byte
    
    Directory absolute path:d:\aaa
     Directory construction path:d:\aaa
     Directory name:aaa
     Directory length:4096
    

Description in API: length() indicates the length of the File. However, if the File object represents a directory, the return value is not specified.

Absolute path and relative path

  • Absolute path: the path starting from the drive letter. This is a complete path.
  • Relative path: relative to the path of the project directory, this is a convenient path, which is often used in development.
public class FilePath {
    public static void main(String[] args) {
      	// bbb.java file on disk D
        File f = new File("D:\\bbb.java");
        System.out.println(f.getAbsolutePath());
      	
		// bbb.java file under the project
        File f2 = new File("bbb.java");
        System.out.println(f2.getAbsolutePath());
    }
}
Output results:
D:\bbb.java
D:\idea_project_test4\bbb.java

Method of judging function

  • public boolean exists(): whether the File or directory represented by this File actually exists.
  • public boolean isDirectory(): whether this File indicates a directory.
  • public boolean isFile(): this File indicates whether it is a File.

Method demonstration, the code is as follows:

public class FileIs {
    public static void main(String[] args) {
        File f = new File("d:\\aaa\\bbb.java");
        File f2 = new File("d:\\aaa");
      	// Judge whether it exists
        System.out.println("d:\\aaa\\bbb.java Does it exist:"+f.exists());
        System.out.println("d:\\aaa Does it exist:"+f2.exists());
      	// Determine whether it is a file or directory
        System.out.println("d:\\aaa file?:"+f2.isFile());
        System.out.println("d:\\aaa catalogue?:"+f2.isDirectory());
    }
}
Output results:
d:\aaa\bbb.java Does it exist:true
d:\aaa Does it exist:true
d:\aaa file?:false
d:\aaa catalogue?:true

Method for creating and deleting functions

  • public boolean createNewFile(): creates a new empty file if and only if the file with this name does not exist yet.
  • public boolean delete(): deletes the File or directory represented by this File.
  • public boolean mkdir(): create the directory represented by this File.
  • public boolean mkdirs(): create the directory represented by this File, including any required but nonexistent parent directory.

Method demonstration, the code is as follows:

public class FileCreateDelete {
    public static void main(String[] args) throws IOException {
        // File creation
        File f = new File("aaa.txt");
        System.out.println("Does it exist:"+f.exists()); // false
        System.out.println("Create:"+f.createNewFile()); // true
        System.out.println("Does it exist:"+f.exists()); // true
		
     	// Directory creation
      	File f2= new File("newDir");	
        System.out.println("Does it exist:"+f2.exists());// false
        System.out.println("Create:"+f2.mkdir());	// true
        System.out.println("Does it exist:"+f2.exists());// true

		// Create multi-level directory
      	File f3= new File("newDira\\newDirb");
        System.out.println(f3.mkdir());// false
        File f4= new File("newDira\\newDirb");
        System.out.println(f4.mkdirs());// true
      
      	// Deletion of files
       	System.out.println(f.delete());// true
      
      	// Directory deletion
        System.out.println(f2.delete());// true
        System.out.println(f4.delete());// false
    }
}

API Description: delete method. If this File represents a directory, the directory must be empty to delete.

3.4 directory traversal

  • public String[] list(): returns a String array representing all sub files or directories in the File directory.

  • public File[] listFiles(): returns a File array representing all sub files or directories in the File directory.

public class FileFor {
    public static void main(String[] args) {
        File dir = new File("d:\\java_code");
      
      	//Get the name of the file and folder in the current directory.
		String[] names = dir.list();
		for(String name : names){
			System.out.println(name);
		}
        //Get the files and folder objects in the current directory. As long as you get the file objects, you can get more information
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file);
        }
    }
}

Tips:

The File object calling the listFiles method must represent the actual directory, otherwise it returns null and cannot be traversed.

Chapter 4 recursion

4.1 General

  • Recursion: refers to the phenomenon of calling itself within the current method.
public static void a(){
    a();
}

4.2 recursive sum

Calculate the sum of 1 ~n

Analysis: the cumulative sum of num = the cumulative sum of num + (num-1), so the cumulative sum operation can be defined as a method and called recursively.

Implementation code:

public class DiGuiDemo {
	public static void main(String[] args) {
		//Calculate the sum of 1~num, using recursion
		int num = 5;
      	// Call the summation method
		int sum = getSum(num);
      	// Output results
		System.out.println(sum);
		
	}
  	/*
  	  It is realized by recursive algorithm
  	  Parameter list: int 
  	  Return value type: int 
  	*/
	public static int getSum(int num) {
      	/* 
      	   num When it is 1, the method returns 1,
      	   Equivalent to the exit of the method, num is always 1
      	*/
		if(num == 1){
			return 1;
		}
      	/*
          num When it is not 1, the method returns the cumulative sum of num +(num-1)
          Call getSum method recursively
        */
		return num + getSum(num-1);
	}
}

Tip: recursion must be conditionally limited to ensure that recursion can stop without too many times, otherwise stack memory overflow will occur.

4.3 recursive factorization

  • Factorial: the product of all positive integers less than or equal to the number.
n Factorial of: n! = n * (n-1) *...* 3 * 2 * 1 

Analysis: This is similar to summation, but it is replaced by multiplication. Students can practice by themselves. Note that the factorial value conforms to the range of int type.

It is inferred that: n! = n * (n-1)!

Code implementation:

public class DiGuiDemo {
  	//Calculate the factorial of n, using recursion
    public static void main(String[] args) {
        int n = 3;
      	// Call the factorial method
        int value = getValue(n);
      	// Output results
        System.out.println("Factorial is:"+ value);
    }
	/*
  	  It is realized by recursive algorithm
  	  Parameter list: int 
  	  Return value type: int 
  	*/
    public static int getValue(int n) {
      	// The factorial of 1 is 1
        if (n == 1) {
            return 1;
        }
      	/*
      	  n When it is not 1, the method returns n= n*(n-1)!
          Call getValue method recursively
      	*/
        return n * getValue(n - 1);
    }
}

4.4 file search

Search for. java files in the D:\aaa directory.

analysis:

  1. Directory search, can not determine how many levels of directories, so use recursion to traverse all directories.
  2. When traversing the directory, obtain the sub files and judge whether they meet the conditions through the file name.

Code implementation:

public class DiGuiDemo3 {
    public static void main(String[] args) {
        // Create File object
        File dir  = new File("D:\\aaa");
      	// Call the print directory method
        printDir(dir);
    }

    public static void printDir(File dir) {
      	// Get sub files and directories
        File[] files = dir.listFiles();
      	
      	// Cyclic printing
        for (File file : files) {
            if (file.isFile()) {
              	// Yes, determine the file name and output the absolute path of the file
                if (file.getName().endsWith(".java")) {
                    System.out.println("file name:" + file.getAbsolutePath());
                }
            } else {
                // It is a directory. Continue to traverse to form recursion
                printDir(file);
            }
        }
    }
}

Chapter V IO overview

5.1 coding

1B = 8b the smallest unit in a computer is byte B

U.S.A:

A group of 8 switches can encode characters, that is, 1 byte. 2 ^ 8 = 256, one byte is enough to store one character. This set is ASCII coding, such as a 97, b 98, A 65, B 66, 0 48, 1 49, etc.

English and numbers are stored in 1 byte at the bottom.

China:

There are many characters, about 90000 characters. Chinese people use two bytes to encode a Chinese character. This code is GBK code and must be compatible with ASCII code table.

U.S.A:

All characters in the world are collected and numbered uniformly. This set of code is called Unicode code (universal code). UTF-8 is its variant. UTF-8 is a Chinese character, accounting for 3 bytes. It must also be compatible with ASCII coding table.

One byte is enough to store one character

5.2 what is IO

You must have experienced such a scene in your life. When you edit a text file and forget ctrl+s, the file may be edited in vain. When you insert a U SB flash disk into your computer, you can copy a video to your computer hard disk. So what devices are the data on? Keyboard, memory, hard disk, external devices, etc.

We can regard this data transmission as a kind of data flow. According to the flow direction and based on memory, it is divided into input input and output, that is, the flow to memory is the input flow and the output flow out of memory.

I/O operation in Java mainly refers to the use of the contents under the java.io package for input and output operations. Input is also called read data, and output is also called write data.

5.3 classification of IO

According to the flow direction of data, it is divided into input flow and output flow.

  • Input stream: a stream that reads data from other devices into memory.
  • Output stream: a stream that writes data out of memory to other devices.

According to the encoding of data, it is divided into byte stream and character stream.

  • Byte stream: a stream that reads and writes data in bytes. For example: pictures, audio and video
  • Character stream: a stream that reads and writes data in character units. For example: text based

According to the wrapper object of the flow, it is divided into node flow and processing flow

  • Node flow: wrapper objects are specific data sources, such as File objects, strings, or arrays
  • **Processing flow: * * wrapper objects are other flow objects, such as FileInputStream

5.4 flow direction illustration of IO

[external link picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-QyFxuspP-1633587744683)(image/IO input and output. bmp)]

5.5 top level parent classes

Input streamOutput stream
Byte streamByte input stream
InputStream
Byte output stream
OutputStream
Character streamCharacter input stream
Reader
Character output stream
Writer

Chapter 6 byte stream

6.1 everything is a byte

All file data (text, pictures, videos, etc.) are stored in binary numbers, bytes by bytes, and the same is true during transmission. Therefore, byte stream can transfer any file data. When operating a stream, we should always make it clear that no matter what kind of stream object is used, the underlying transmission is always binary data.

6.2 byte output stream [OutputStream]

The java.io.OutputStream abstract class is a superclass that represents all classes of byte output stream and writes the specified byte information to the destination. It defines the basic common function method of byte output stream.

  • public void close(): close this output stream and release any system resources associated with this stream.
  • public void flush(): flushes this output stream and forces any buffered output bytes to be written out.
  • public void write(byte[] b): writes b.length bytes from the specified byte array to this output stream.
  • public void write(byte[] b, int off, int len): writes len bytes from the specified byte array and outputs them to this output stream starting from offset off.
  • public abstract void write(int b): outputs the specified bytes to the stream.

Tips:

close method, which must be called to release system resources when the operation of the stream is completed.

6.3 FileOutputStream class

OutputStream has many subclasses. Let's start with the simplest subclass.

The java.io.FileOutputStream class is a file output stream used to write data out to a file.

Construction method

  • public FileOutputStream(File file): creates a File output stream to write to the File represented by the specified File object.
  • public FileOutputStream(String name): creates a file output stream and writes it to the file with the specified name.

When you create a stream object, you must pass in a file path. Under this path, if there is no such file, it will be created. If there is this file, the data of this file will be cleared.

  • For example, the code is as follows:
public class FileOutputStreamConstructor throws IOException {
    public static void main(String[] args) {
   	 	// Create a stream object using a File object
        File file = new File("a.txt");
        FileOutputStream fos = new FileOutputStream(file);
      
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("b.txt");
    }
}

Write byte data

  1. Write byte: write(int b) method can write one byte of data at a time. The code usage demonstration:
public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// Write data
      	fos.write(97); // Write the first byte
      	fos.write(98); // Write the second byte
      	fos.write(99); // Write the third byte
      	// close resource
        fos.close();
    }
}
Output results:
abc

Tips:

  1. Although the parameter is four bytes of int type, only one byte of information will be reserved.
  2. After the stream operation is completed, you must release system resources and call the close method. Remember.

Write out byte array: write(byte[] b). You can write out the data in the array each time. The code usage demonstration:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// Convert string to byte array
      	byte[] b = "Geek camp programmer".getBytes();
      	// Write out byte array data
      	fos.write(b);
      	// close resource
        fos.close();
    }
}
Output results:
Geek camp programmer

Write out the byte array of the specified length: write(byte[] b, int off, int len). Each write starts from the off index, len bytes. Code usage demonstration:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// Convert string to byte array
      	byte[] b = "abcde".getBytes();
		// Write 2 bytes starting from index 2. Index 2 is c, two bytes, that is, cd.
        fos.write(b,2,2);
      	// close resource
        fos.close();
    }
}
Output results:
cd

Data append and continue

After the above demonstration, each time the program runs and creates an output stream object, the data in the target file will be cleared. How to keep the data in the target file and continue to add new data?

  • Public fileoutputstream (File, Boolean append): creates a File output stream to write to the File represented by the specified File object.
  • public FileOutputStream(String name, boolean append): creates a file output stream and writes it to the file with the specified name.

For these two construction methods, a boolean value needs to be passed in the parameters. true means to append data, and false means to empty the original data. The output stream object created in this way can be used to specify whether to append and continue. The code uses the following demonstration:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt",true);     
      	// Convert string to byte array
      	byte[] b = "abcde".getBytes();
		// Write 2 bytes starting from index 2. Index 2 is c, two bytes, that is, cd.
        fos.write(b);
      	// close resource
        fos.close();
    }
}
Before file operation: cd
 After file operation: cdabcde

Write line breaks

On Windows systems, the newline symbol is \ r\n. hold

To specify whether to append and continue. The code usage demonstration:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");  
      	// Define byte array
      	byte[] words = {97,98,99,100,101};
      	// Traversal array
        for (int i = 0; i < words.length; i++) {
          	// Write a byte
            fos.write(words[i]);
          	// Write a newline and convert the newline symbol into an array
            fos.write("\r\n".getBytes());
        }
      	// close resource
        fos.close();
    }
}

Output results:
a
b
c
d
e
  • Carriage returns \ r and line breaks \ n:
    • Carriage return: return to the beginning of a line.
    • newline: next line.
  • Line breaks in the system:
    • In Windows system, the end of each line is enter + line feed, that is \ r\n;
    • In Unix systems, there is only newline at the end of each line, that is \ n;
    • On the MAC system, the end of each line is enter, that is, \ r. Unified with Linux starting with Mac OS X.

6.4 byte input stream [InputStream]

The java.io.InputStream abstract class is a superclass representing all classes of byte input stream, which can read byte information into memory. It defines the basic common function method of byte input stream.

  • public void close(): close this input stream and release any system resources associated with this stream.
  • public abstract int read(): reads the next byte of data from the input stream.
  • public int read(byte[] b): read some bytes from the input stream and store them in byte array B.

Tips:

close method, which must be called to release system resources when the operation of the stream is completed.

6.5 FileInputStream class

The java.io.FileInputStream class is a file input stream that reads bytes from a file.

Construction method

  • FileInputStream(File file): create a FileInputStream by opening a connection to the actual file, which is named by the file object file in the file system.
  • FileInputStream(String name): create a FileInputStream by opening a connection to the actual file, which is named by the pathname in the file system.

When you create a stream object, you must pass in a file path. If there is no such file in this path, FileNotFoundException will be thrown

  • For example, the code is as follows:
public class FileInputStreamConstructor throws IOException{
    public static void main(String[] args) {
   	 	// Create a stream object using a File object
        File file = new File("a.txt");
        FileInputStream fos = new FileInputStream(file);
      
        // Create a stream object with a file name
        FileInputStream fos = new FileInputStream("b.txt");
    }
}

Read byte data

  1. Read bytes: the read method can read one byte of data at a time, promote it to int type, read it to the end of the file, and return - 1. Code usage demonstration:
public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt");
      	// Read data and return a byte
        int read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
      	// Read to the end and return - 1
       	read = fis.read();
        System.out.println( read);
		// close resource
        fis.close();
    }
}
Output results:
a
b
c
d
e
-1

Loop to improve the reading mode, code use demonstration:

public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt");
      	// Define variables and save data
        int b ;
        // Cyclic reading
        while ((b = fis.read())!=-1) {
            System.out.println((char)b);
        }
		// close resource
        fis.close();
    }
}
Output results:
a
b
c
d
e

Tips:

  1. Although a byte is read, it is automatically promoted to type int.
  2. After the stream operation is completed, you must release system resources and call the close method. Remember.

Read using byte array: read(byte[] b). Each time the length of B is read into the array, return the number of valid bytes read. When reading to the end, return - 1. Code usage demonstration:

public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt"); // abcde in file
      	// Define variables as valid numbers
        int len ;
        // Defines a byte array as a container for byte data   
        byte[] b = new byte[2];
        // Cyclic reading
        while (( len= fis.read(b))!=-1) {
           	// After each reading, the array is changed into a string for printing
            System.out.println(new String(b));
        }
		// close resource
        fis.close();
    }
}

Output results:
ab
cd
ed

The error data d is because only one byte e is read during the last reading. In the array, the last read data has not been completely replaced, so it is necessary to obtain valid bytes through len. The code usage demonstration:

public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt"); // abcde in file
      	// Define variables as valid numbers
        int len ;
        // Defines a byte array as a container for byte data   
        byte[] b = new byte[2];
        // Cyclic reading
        while (( len= fis.read(b))!=-1) {
           	// After each reading, the valid byte part of the array is changed into a string for printing
            System.out.println(new String(b,0,len));//  len number of valid bytes read each time
        }
		// close resource
        fis.close();
    }
}

Output results:
ab
cd
e

Tips:

Array reading is used to read multiple bytes each time, which reduces the number of IO operations between systems, thus improving the efficiency of reading and writing. It is recommended to be used in development.

6.6 byte stream exercise: picture copying

Schematic diagram of replication principle

Case realization

Copy the picture file and use the code to demonstrate:

public class Copy {
    public static void main(String[] args) throws IOException {
        // 1. Create flow object
        // 1.1 specifying data sources
        FileInputStream fis = new FileInputStream("D:\\test.jpg");
        // 1.2 designated destination
        FileOutputStream fos = new FileOutputStream("test_copy.jpg");

        // 2. Read and write data
        // 2.1 defining arrays
        byte[] b = new byte[1024];
        // 2.2 defining length
        int len;
        // 2.3 cyclic reading
        while ((len = fis.read(b))!=-1) {
            // 2.4 write data
            fos.write(b, 0 , len);
        }

        // 3. Close resources
        fos.close();
        fis.close();
    }
}

Tips:

Closing principle of flow: open first and then close, open later and close first.

Posted by Yossarian on Wed, 06 Oct 2021 23:15:49 -0700