Oi! A lição de hoje será dividida em duas partes por conveniência. Repetiremos alguns tópicos antigos que abordamos anteriormente e consideraremos alguns novos recursos :) Vamos começar com o primeiro. Você já teve uma aula como BufferedReadermuitas vezes. Espero que você não tenha tido tempo de esquecer esta afirmação:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Antes de continuar a leitura, tente lembrar pelo que cada componente — System.in, InputStreamReader, BufferedReader— é responsável e por que é necessário. Você se lembrou? Se não, não se preocupe. :) Se você esqueceu alguma coisa, releia esta lição , que é dedicada às aulas de leitura. Vamos relembrar brevemente o que cada um deles pode fazer. System.in— este é um fluxo para receber dados do teclado.  Em princípio, bastaria apenas implementar a lógica necessária para ler o texto. Mas, como você deve se lembrar, System.insó pode ler bytes, não caracteres:

public class Main {

   public static void main(String[] args) throws IOException { 

       while (true) { 
           int x = System.in.read(); 
           System.out.println(x); 
       } 
   } 
} 
Se executarmos este código e inserirmos a letra cirílica "Й", a saída será:

Й
208
153
10 
Os caracteres cirílicos ocupam 2 bytes na memória e são exibidos na tela. O número 10 é a representação decimal de um caractere de alimentação de linha, ou seja, de pressionar Enter. Ler bytes é um prazer, então usar System.innão é muito conveniente. Para ler corretamente as letras cirílicas (e outras), usamos InputStreamReadercomo invólucro:

public class Main { 

   public static void main(String[] args) throws IOException { 

       InputStreamReader reader = new InputStreamReader(System.in); 
       while (true) { 
           int x = reader.read(); 
           System.out.println(x); 
       } 
   } 
} 
Entramos com a mesma letra "É", mas desta vez o resultado é diferente:

Й 
1049 
10
InputStreamReaderconverteu dois bytes (208 e 153) no número único 1049. Isso é o que significa ler caracteres. 1049 corresponde à letra cirílica "Й". Podemos facilmente nos convencer de que isso é verdade:

public class Main { 

   public static void main(String[] args) throws IOException { 
       char x = 1049; 
       System.out.println(x); 
   } 
} 
Saída do console:

Й
E como forBufferedReader(e em geral, BufferedAnythingYouWant), as classes em buffer são usadas para otimizar o desempenho. Acessar uma fonte de dados (arquivo, console, recurso da Web) é bastante caro em termos de desempenho. Portanto, para reduzir o número de acessos, BufferedReaderlê e acumula dados em um buffer especial e os obtemos a partir daí. Como resultado, o número de vezes que a fonte de dados é acessada é reduzido — possivelmente em várias ordens de grandeza! Outra BufferedReadercaracterística do , e sua vantagem sobre o comum , é o método InputStreamReaderextremamente útil , que lê linhas inteiras de dados, não números individuais. readLine()Isso, é claro, é super conveniente ao lidar com textos grandes. Veja como são as linhas de leitura:

public class Main { 

   public static void main(String[] args) throws IOException { 

       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 
       String s = reader.readLine(); 
       System.out.println ("The user entered the following text:"); 
       System.out.println(s); 
       reader.close(); 
   } 
}

BufferedReader+InputStreamReader is faster than InputStreamReader alone 
The user entered the following text: 
BufferedReader+InputStreamReader is faster than InputStreamReader alone
Pratique trabalhando com as classes BuffreredReader e InputStreamReader - 2Claro, BufferedReaderé muito flexível. Você não está limitado a trabalhar com o teclado. Por exemplo, você pode ler dados diretamente da web, simplesmente passando a URL necessária para um leitor:

public class URLReader { 

   public static void main(String[] args) throws Exception { 

       URL oracle = new URL("https://www.oracle.com/index.html"); 
       BufferedReader in = new BufferedReader( 
               new InputStreamReader(oracle.openStream())); 

       String inputLine; 
       while ((inputLine = in.readLine()) != null) 
           System.out.println(inputLine); 
       in.close(); 
   } 
}
Você pode ler dados de um arquivo passando o caminho do arquivo:

public class Main { 

   public static void main(String[] args) throws Exception { 

       FileInputStream fileInputStream = new FileInputStream("testFile.txt"); 
       BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); 

       String str; 

       while ((str = reader.readLine()) != null)   { 
           System.out.println (str); 
       } 

       reader.close(); 
   } 
}

Substituindo System.out

Agora vamos dar uma olhada em um recurso interessante que não abordamos antes. Como você certamente se lembra, a Systemclasse tem dois campos estáticos — System.ine  System.out. Esses irmãos gêmeos são objetos de fluxo. System.iné um InputStream. E System.out é um PrintStream. Agora, vamos falar sobre  System.out. Se entrarmos no Systemcódigo-fonte da classe, veremos isso:

public final class System { 
……………... 
public final static PrintStream out = null; 
 ………… 
} 
Portanto,  System.out  é simplesmente uma variável estática comum daSystem classe. Não há nada de mágico nisso :) A outvariável é uma PrintStreamreferência. Aqui está uma pergunta interessante: quando System.out.println()é executado, por que exatamente a saída vai para o console e não para outro lugar? E isso pode ser mudado de alguma forma? Por exemplo, suponha que queremos ler dados do console e gravá-los em um arquivo de texto. É possível de alguma forma implementar isso simplesmente usando,  System.outem vez de classes adicionais de leitor e escritor? De fato, é :) E podemos fazer isso mesmo que a System.outvariável esteja marcada com o finalmodificador!  Pratique trabalhando com as classes BuffreredReader e InputStreamReader - 3Então, o que precisamos para fazer isso acontecer? Em primeiro lugar, precisamos de um novo PrintStreamobjeto para substituir o atual. O objeto atual, definido noSystemclass por padrão, não atende aos nossos propósitos: aponta para o console. Você precisa criar um novo que aponte para um arquivo de texto — o "destino" de nossos dados. Em segundo lugar, precisamos entender como atribuir um novo valor à System.outvariável. Você não pode usar um operador de atribuição simples, porque a variável está marcada como final. Vamos trabalhar para trás a partir do final. Acontece que a Systemclasse tem o método que precisamos: setOut(). Ele pega um PrintStreamobjeto e o define como o destino da saída. Isso é exatamente o que precisamos! Tudo o que resta é criar um PrintStreamobjeto. Isso também é fácil:

PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
O código completo ficará assim:

public class SystemRedirectService { 

   public static void main(String arr[]) throws FileNotFoundException 
   { 
       PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
       /* Save the current value of System.out in a separate variable so that later 
       we can switch back to console output */ 

       PrintStream console = System.out; 
       // Assign a new value to System.out 
       System.setOut(filePrintStream); 
       System.out.println("This line will be written to the text file"); 

       // Restore the old value of System.out 
       System.setOut(console); 
       System.out.println("But this line will be output to the console!"); 
   } 
}
Como resultado, a primeira string é gravada no arquivo de texto e a segunda é exibida no console :) Você pode copiar este código para o seu IDE e executá-lo. Abra o arquivo de texto e você verá que a string foi escrita com sucesso lá :) Com isso, nossa lição chegou ao fim. Hoje relembramos como trabalhar com streams e leitores. Nós lembramos como eles diferem um do outro e aprendemos sobre alguns novos recursos do System.out, que usamos em quase todas as aulas :) Até as próximas aulas!