day04--programmation avancée Java : API : objet, chaîne, tampon, classe d'empaquetage, date, comparateur Java, BigInteger, base, différence de longueur, flux IO, chemin, identifiant de sérialisation, encodage

1 API

1.1 Téléchargement des documents API

1.API (Application Programming Interface, Application Programming Interface ) est l'interface de programmation de base fournie par Java. Tout ce qui peut être appelé est une API.

2. Le langage Java fournit un grand nombre de classes de base, donc Oracle fournit également des documents API correspondants pour ces classes de base afin d'indiquer aux développeurs comment utiliser ces classes et les méthodes contenues dans ces classes.

3. Télécharger l'API :
Ressources supplémentaires - Téléchargement de la documentation Java SE 8.
http://www.oracle.com/technetwork/java/javase/downloads/index.html

4. Pour plus de détails, voir : JDK8 version Api download-installation-configuration.doc
Insérer la description de l'image ici
Insérer la description de l'image ici

1.2 Catégories communes

Insérer la description de l'image ici
Insérer la description de l'image ici

1.3 Objet

1.3.1 Concept

  1. La classe Object est la classe parent racine de toutes les classes Java

  2. Si le mot clé extends n'est pas utilisé dans la déclaration d'une classe pour indiquer sa classe parent, la classe parent par défaut est la classe java.lang.Object.

  3. Les fonctions (propriétés, méthodes) de la classe Object sont universelles.
    Propriétés : Aucune
    Méthodes : equals() / toString() / getClass() /hashCode() / clone() / finalize() / wait() / notify() / notifyAll() Constructeur : La classe Object déclare uniquement un constructeur de
    paramètre vide
    Insérer la description de l'image ici

  4. La classe Object existe dans le package java.lang. Ce package lang ne nécessite pas que nous importions manuellement le package.
    Insérer la description de l'image ici

1.3.2 Explication des méthodes dans Object

//1.clone()不常用,用的时候在查找资料,这里不在讲解。
protected  Object clone() 
          创建并返回此对象的一个副本

//2.此时讲解。          
 boolean equals(Object obj) 
          指示其他某个对象是否与此对象“相等”。 
 
 String toString() 
          返回该对象的字符串表示。

//3.剩下的方法在后续反射、集合、线程通信时讲。          
 int hashCode() 
          返回该对象的哈希码值。 
 protected  void finalize() 
          当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 
 Class<?> getClass()
          返回此Object运行时的类。
 ......

1.3.3 Différences entre == et égal()

Classe EqualsTest :

package com.atguigu.java1;

import java.util.Date;

/*
 * 
 * 面试题: == 和 equals() 区别
 * 
 * 一、回顾 == 的使用:
 * == :运算符
 * 1. 可以使用在基本数据类型变量和引用数据类型变量中
 * 2. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
 *    如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
 * 补充: == 符号使用时,必须保证符号左右两边的变量类型一致。(注意指的是一致而不是相同,如都是数字类型int 和double可以比较,而int和boolean不可以比较)
 * 
 * 二、equals()方法的使用:
 * 1. 是一个方法,而非运算符
 * 2. 只能适用于引用数据类型(解释:调用方法都是通过 对象名.方法名()为引用数据类型,基本类型没法调方法,除非包装类)
 * 3. Object类中equals()的定义:
 *    public boolean equals(Object obj) {
	        return (this == obj);
	  }
 *    说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
 * 
 * 4. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是
 *    两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
 *    
 * 5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们
 *    就需要对Object类中的equals()进行重写(可以是自己手写,也可以通过开发工具如eclipse,右键--source...自动生成).
 *    重写的原则:比较两个对象的实体内容是否相同.
 * 6.注意事项:1.x.equals(null) 在任何情况下返回值都为false,
 *            2.null.equals(y) 就不能这样写会报空指针异常。所以x.equals(y)一般进行equals判断时最好先判断x是否为null 
 *            不是Null时在向下比。如果后面的y是明确的字符串如:abc这样确定的值时,可以把确定的字符串写到前面避免出现空指针异常。
 *            例如:
 *              public class Student extends Person {
                    public static void main(String[] args) {

                        System.out.println("请输入字符串name:");
                        String name= new Scanner(System.in).nextLine();
                        System.out.println("判断输入的字符串name存的值是否为Tom:");
        
                      //if(name.equals("Tom")){有风险如果name为null会报空指针
                        if("Tom".equals(name)){//修改为:把确定的字符串放到前面,这样可以保证不会变为 null.(y),避免空指针。
                            System.out.println("name的值为Tom");
                        }else {
                            System.out.println("name值不是Tom");
                        }

                }           
 */
public class EqualsTest {
    
    
	public static void main(String[] args) {
    
    
		
		//基本数据类型
		int i = 10;
		int j = 10;
		double d = 10.0;
		System.out.println(i == j);//true
		System.out.println(i == d);//true
		
		boolean b = true;
//		System.out.println(i == b);
		
		char c = 10;
		System.out.println(i == c);//true
		
		char c1 = 'A';
		char c2 = 65;
		System.out.println(c1 == c2);//true
		
		//引用类型:
		Customer cust1 = new Customer("Tom",21);
		Customer cust2 = new Customer("Tom",21);
		System.out.println(cust1 == cust2);//false
		
		String str1 = new String("atguigu");
		String str2 = new String("atguigu");
		System.out.println(str1 == str2);//false
		System.out.println("****************************");
		System.out.println(cust1.equals(cust2));//false--->true
		System.out.println(str1.equals(str2));//true
		
		Date date1 = new Date(32432525324L);
		Date date2 = new Date(32432525324L);
		System.out.println(date1.equals(date2));//true
		
		
	}
}

Classe client :

package com.atguigu.java1;

public class Customer {
    
    
	
	private String name;
	private int age;
	public String getName() {
    
    
		return name;
	}
	public void setName(String name) {
    
    
		this.name = name;
	}
	public int getAge() {
    
    
		return age;
	}
	public void setAge(int age) {
    
    
		this.age = age;
	}
	public Customer() {
    
    
		super();
	}
	public Customer(String name, int age) {
    
    
		super();
		this.name = name;
		this.age = age;
	}

	//1.1通过工具自动生成的equals()(eclipse工具:右键--source...)
	@Override
	public boolean equals(Object obj) {
    
    
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Customer other = (Customer) obj;
		if (age != other.age)
			return false;
		if (name == null) {
    
    
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
	
	
	//重写的原则:比较两个对象的实体内容(即:name和age)是否相同
	//1.2手动实现equals()的重写
//	@Override
//	public boolean equals(Object obj) {
    
    
//		
		System.out.println("Customer equals()....");
//		if (this == obj) {
    
    
//      如果是地址值相同,就不用在比较了一定是同一对象。
//            return true;
//        }
//		
//		if(obj instanceof Customer){
    
    
//       如果地址值不同,则比较2个对象是否为同一类型,如果不是则不用比较了。是的话在比较里面的属性。如果属性相同则相等如果属性不同则不相等。
//			Customer cust = (Customer)obj;
//			//比较两个对象的每个属性是否都相同
			if(this.age == cust.age && this.name.equals(cust.name)){
    
    
				return true;
			}else{
    
    
				return false;
			}
//			
//			//或
//			return this.age == cust.age && this.name.equals(cust.name);
//		}else{
    
    
//			return false;
//			
//		}
//		
//	}



	//2.1手动实现
//	@Override
//	public String toString() {
    
    
//		return "Customer[name = " + name + ",age = " + age + "]"; 
//	}
	//2.2自动实现(eclipse工具:右键--source...)
	@Override
	public String toString() {
    
    
		return "Customer [name=" + name + ", age=" + age + "]";
	}
}

1.3.4 versChaîne

package com.atguigu.java1;

import java.util.Date;

/*
 * Object类中toString()的使用:
 * 
 * 1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
 * 
 * 2. Object类中toString()的定义:
 *   public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
     }
 * 
 * 3. 像String、Date、File、包装类等都重写了Object类中的toString()方法。
 *    使得在调用对象的toString()时,返回"实体内容"信息
 *    
 * 4. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"(自己手写或者通过eclipse工具自动生成)
 */
public class ToStringTest {
    
    
	public static void main(String[] args) {
    
    
		
		Customer cust1 = new Customer("Tom",21);
		System.out.println(cust1.toString());//com.atguigu.java1.Customer@15db9742-->Customer[name = Tom,age = 21]
		System.out.println(cust1);//com.atguigu.java1.Customer@15db9742-->重写后输出Customer[name = Tom,age = 21]
		
		String str = new String("MM");
		System.out.println(str);//MM
		
		Date date = new Date(4534534534543L);
		System.out.println(date.toString());//Mon Sep 11 08:55:34 GMT+08:00 2113
		
	}
}

1.4 Chaîne

1.4.1 Relation successorale

Insérer la description de l'image ici

1.4.2 Caractéristiques

public final class String : la chaîne est modifiée de manière finale et ne peut pas être héritée.
private final char value[ ] : La chaîne maintient un char[ ] en bas. La longueur du tableau ne peut pas être modifiée, elle est modifiée de manière finale et la valeur du tableau ne peut pas être modifiée. - - - Autrement dit, la chaîne est une constante et ne peut être modifiée une fois définie.
Insérer la description de l'image ici

1.4.3 Créer un objet String

Méthode 1 : création de méthode littérale

  1. Syntaxe : String s1 = "abc" ;//Comment définir des littéraux
  2. Si c'est la première fois qu'une chaîne est utilisée, Java créera un objet dans le pool de constantes de chaîne.
  3. Si vous créez un objet directement dans le pool constant (essentiellement toujours char[]) et que vous utilisez à nouveau le même contenu, vous trouverez un objet existant dans le pool constant et n'en créerez pas de nouveau. (Réduire la surcharge de mémoire)

test:
Remarque : Il existe des différences dans les méthodes de stockage selon les versions des machines virtuelles JVM : les pools constants de jdk1.6 et jdk1.8 se trouvent dans la zone des méthodes et le pool constant de jdk1.7 est dans le tas.
Insérer la description de l'image ici

/*
    String:字符串,使用一对""引起来表示。
    1.String声明为final的,不可被继承
    2.String实现了Serializable接口:表示字符串是支持序列化的。
            实现了Comparable接口:表示String可以比较大小
    3.String内部定义了final char[] value用于存储字符串数据
    4.String:代表不可变的字符序列。简称:不可变性。
        体现:1.当对字符串重新赋值时,需要重写指定内存区域赋值(地址值发生了变化),不能使用原有的value进行赋值。
             2. 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
             3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
             总结:以字面量的方式创建String字符串,一旦字符串里面的内容发生了变化,说明重新造了个新的字符串String,地址值发生了变化。
    5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
    6.字符串常量池中是不会存储相同内容的字符串的。
     */
    @Test
    public void test1(){
    
    
        String s1 = "abc";//字面量的定义方式
        String s2 = "abc";
        s1 = "hello";//因为String是final修饰的char类型的数组,一旦重新赋值说明新造了一个字符串 即地址值发生了变化。

        System.out.println(s1 == s2);// 比较s1和s2的地址值

        System.out.println(s1);//hello
        System.out.println(s2);//abc

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

        String s3 = "abc";
        s3 += "def";
        System.out.println(s3);//abcdef
        System.out.println(s2);

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

        String s4 = "abc";
        String s5 = s4.replace('a', 'm');
        System.out.println(s4);//abc
        System.out.println(s5);//mbc

    }

Méthode 2 : syntaxe de la méthode new + constructeur
 :

第一种:
//本质上this.value = new char[0];
String s1 = new String(); 
第二种:
//this.value = original.value;
String s2 = new String(String original); 
第三种:
//this.value = Arrays.copyOf(value, value.length);
String s3 = new String(char[] a); 
第四种:
String s4 = new String(char[] a,int startIndex,int count);
......还有很多种,具体查看Api

test:

Insérer la description de l'image ici

 /*
    String的实例化方式:
    方式一:通过字面量定义的方式
    方式二:通过new + 构造器的方式

     面试题:String s = new String("abc");方式创建对象,在内存中创建了几个对象?
            两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:"abc"

     */
    @Test
    public void test2(){
    
    
        //通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
        String s1 = "javaEE";
        String s2 = "javaEE";
        //通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
        String s3 = new String("javaEE");
        String s4 = new String("javaEE");

        System.out.println(s1 == s2);//true
        System.out.println(s1 == s3);//false
        System.out.println(s1 == s4);//false
        System.out.println(s3 == s4);//false

        System.out.println("***********************");
        Person p1 = new Person("Tom",12);
        Person p2 = new Person("Tom",12);

        System.out.println(p1.name.equals(p2.name));//true
        /*存储过程:1.创建的对象p1 p2储存在堆中,p1 p2栈的地址值不同。
        			2.p1 p2对象里面的value值是char类型的数组对应常量池的地址,
        			 常量池的值相同代表p1.name,p2.name的地址值相同。*/
        System.out.println(p1.name == p2.name);//true

        p1.name = "Jerry";
        System.out.println(p2.name);//Tom
    }

1.4.3 Comparaison de différentes opérations d'épissage de chaînes

Insérer la description de l'image ici

 /*
    结论:
    1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
    2.只要其中有一个是变量,结果就在堆中。
    3.如果拼接的结果调用intern()方法,返回值就在常量池中
     */
      @Test
    public void test4(){
    
    
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 + "hadoop";
        System.out.println(s1 == s3);//false

        final String s4 = "javaEE";//s4:常量
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5);//true

    }
 @Test
    public void test3(){
    
    
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);//true
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//false
        System.out.println(s3 == s7);//false
        System.out.println(s5 == s6);//false
        System.out.println(s5 == s7);//false
        System.out.println(s6 == s7);//false

        String s8 = s6.intern();//返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
        System.out.println(s3 == s8);//true


    }

1.4.4 Méthodes courantes


        char charAt(int index)  index 索引(即:下标,底层是数组)
          	返回指定索引处的 char 值。 
		String concat(String str) 
		          将指定字符串连接到此字符串的结尾。 
		boolean contains(CharSequence s) 
		          当且仅当此字符串包含指定的 char 值序列时,返回 trueboolean endsWith(String suffix) 
		          测试此字符串是否以指定的后缀结束。 
		boolean equals(Object anObject) 
		          将此字符串与指定的对象比较。 
		byte[] getBytes() 
	  	          使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
		int indexOf(String str) 
		 		  返回指定子字符串在此字符串中第一次出现处的索引。
		int lastIndexOf(String str) 
		          返回指定子字符串在此字符串中最右边出现处的索引。 
		注意:indexOf、lastIndexOf方法未找到结果,返回值都是-1(规定)
		boolean isEmpty() 
		          当且仅当 length()0 时返回 trueint length() 
		          返回此字符串的长度。
		String replace(char oldChar, char newChar) 
		          返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 
		String[] split(String regex) 
		          根据给定正则表达式的匹配拆分此字符串。 
		boolean startsWith(String prefix) 
		          测试此字符串是否以指定的前缀开始。 
		String substring(int beginIndex) 
		          返回一个新的字符串,它是此字符串的一个子字符串。 
		String substring(int beginIndex, int endIndex) 
		          返回一个新字符串,它是此字符串的一个子字符串。 
		char[] toCharArray() 
		          将此字符串转换为一个新的字符数组。 
		String toLowerCase() 
		          使用默认语言环境的规则将此 String 中的所有字符都转换为小写。 
		String toUpperCase() 
		          使用默认语言环境的规则将此 String 中的所有字符都转换为大写。 
		String trim() 
		          返回字符串的副本,忽略前导空白和尾部空白。
		static String valueOf(int i) 
		          返回 int 参数的字符串表示形式。

1.4.5 Méthodes d'essai courantes

     package cn.tedu.api;

		import java.util.Arrays;

		//测试 String工具类
		public class Test2_String {
    
    
			public static void main(String[] args) {
    
    
				//1, 字符串底层维护了一个char[]  , 而且是final的,也就是数组长度不能改,数组里的值也不能改,----字符串是一个常量 !!
				char[] c = new char[] {
    
    'a','b','c'} ;
				String s = new String(c) ;  //  触发char[]类型的含参构造--存在了堆内存中
				
				String str = "abc" ;  //  直接赋值,存在堆内存中的常量池中--高效--因为常量池的相同数据,只会存一次
				String str2 = "abc";
				System.out.println(str2==str);//true, 相同数据,拥有相同的存储空间,内存中就是相同的地址值
				System.out.println(s==str);//false, 在堆里的地址值,str在常量池的地址值,不相同
				
				//2,常用方法
//前面有返回值类型,
				System.out.println( s.charAt(1) ); // 根据下标获取对应的字符
				System.out.println( s.concat("123") );//在字符串的末尾处拼接自定字符串
				System.out.println( s.contains("bc") );//判断是否包含指定的字符串
				System.out.println( s.endsWith("c") );//判断字符串 是否以指定后缀 结尾
				System.out.println( s.equals("abc") );//判断字符是 是否 与指定的字符串  相等
				System.out.println( s.indexOf("a") );//获取指定字符串在s出现的第一次的下标值
				
				s = "abca";
				System.out.println( s.lastIndexOf("a") );//获取指定字符串在s出现的最后一次的下标值
				System.out.println( s.isEmpty() );//判断字符串是否为空
				System.out.println( s.length() );//获取字符串的长度
				System.out.println( s.replace('a', '0') );//把旧字符用新字符替换 , 0bc0
				System.out.println( s.startsWith("ab") );//判断是否以指定字符串开始
				System.out.println( s.substring(1) );//从指定下标处开始,截取所有字符串
				System.out.println( s.substring(0,2) );//从指定下标开始,到指定下标结束,截取中间段[0,2)--含头不含尾
				System.out.println( s.toLowerCase() );//自动转成小写
				System.out.println( s.toUpperCase() );//自动转成大写
				
				s = "   ab  ca    ";
				System.out.println( s.trim() );//去除前面空格和后面空格
				
				String num=String.valueOf(123);//用来把各种类型的数据  转成 String类型
				System.out.println(num+1);//1231
				
				System.out.println("------字符串转数组------");
				byte[] bs = s.getBytes();//把字符串的数据放入byte[]里
				System.out.println( Arrays.toString(bs) );
				
				char[] cs = s.toCharArray(); //把字符串的数据放入char[]里
				System.out.println( Arrays.toString(cs) );
				
				s = "a0b0c0";
				String[] ss = s.split("0");//按照指定的规则切割字符串
				System.out.println( Arrays.toString(ss) );//[a, b, c]
				
			}
		}

1.4.6 Conversion entre chaîne et tableau de caractères char[]

 /*
    String 与 char[]之间的转换

    String --> char[]:调用String的toCharArray()
    char[] --> String:调用String的构造器
     */
    @Test
    public void test2(){
    
    
        String str1 = "abc123";  //题目: a21cb3

        char[] charArray = str1.toCharArray();
        for (int i = 0; i < charArray.length; i++) {
    
    
            System.out.println(charArray[i]);
        }

        char[] arr = new char[]{
    
    'h','e','l','l','o'};
        String str2 = new String(arr);
        System.out.println(str2);
    }

1.4.7 Conversion entre chaîne et tableau d'octets byte[]

/*
    String 与 byte[]之间的转换
    编码:String --> byte[]:调用String的getBytes()
    解码:byte[] --> String:调用String的构造器

    编码:字符串 -->字节  (看得懂 --->看不懂的二进制数据)
    解码:编码的逆过程,字节 --> 字符串 (看不懂的二进制数据 ---> 看得懂)

    说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。
     */
    @Test
    public void test3() throws UnsupportedEncodingException {
    
    
        String str1 = "abc123中国";
        byte[] bytes = str1.getBytes();//使用默认的字符集,进行编码。
        System.out.println(Arrays.toString(bytes));

        byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码。
        System.out.println(Arrays.toString(gbks));

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

        String str2 = new String(bytes);//使用默认的字符集,进行解码。
        System.out.println(str2);

        String str3 = new String(gbks);
        System.out.println(str3);//出现乱码。原因:编码集和解码集不一致!


        String str4 = new String(gbks, "gbk");
        System.out.println(str4);//没有出现乱码。原因:编码集和解码集一致!


    }

1.5 StringBuffer/StringBuilder

1.5.1 Caractéristiques

  1. Tableau char[] encapsulé
  2. est une séquence variable de caractères
  3. Fournit un ensemble de méthodes pour modifier le contenu des caractères
  4. append() est souvent utilisé à la place des chaînes pour la concaténation de chaînes
  5. La capacité initiale par défaut du tableau de caractères interne est de 16 : capacité initiale de 16 caractères
  6. S'il est supérieur à 16, il tentera d'augmenter la capacité. La nouvelle taille du tableau sera modifiée à 2 fois + 2. Si la capacité n'est pas suffisante, elle sera directement étendue à la capacité requise. int newCapacity = valeur.longueur * 2 + 2;
  7. StringBuffer 1.0 est thread-safe depuis ses débuts, et StringBuilder1.5 est thread-unsafe depuis ses débuts.
  8. public final class StringBuilder -----La classe finale ne peut pas être héritée de
    la valeur char[] --------La couche inférieure est toujours comme String, ne conservant qu'un seul char[], mais la valeur peut être modifiée à tout moment temps.

1.5.2 Quelles sont les similitudes et les différences entre String, StringBuffer et StringBuilder ?

 /*
    String、StringBuffer、StringBuilder三者的异同?
    String:不可变的字符序列;底层使用 final char[]存储
    StringBuffer:可变的字符序列;jdk1.0线程安全的,效率低;底层使用char[]存储
    StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储

    源码分析:StringBuffer StringBuilder底层扩容方式相同。
    String str = new String();//char[] value = new char[0];
    String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};

    StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度是16的数组。
    System.out.println(sb1.length());//
    sb1.append('a');//value[0] = 'a';
    sb1.append('b');//value[1] = 'b';

    StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];

    //问题1. System.out.println(sb2.length());//3 输出的是长度是存储的个数
    //问题2. 扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
             默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。

			开发中如果对字符串需要频繁的修改,该如何选择???
			首先:排除String考虑 StringBuffer和 StringBuilder,因为String每次改变都要新创建一个效率低。
			其次:在考虑线程安全问题,如果是多线程操作共享的资源有线程安全问题时,考虑使用StringBuffer。否则不是多线程或者是                 
			     多线程但没有线程安全问题用 StringBuilder。
			之后:StringBuffer和 StringBuilder创建好后默认长度为16,当长度不够时需要进行扩容影响效率。所以开发中如果知
			    道存的字符个数时建议使用知道长度的构造方法创建对象:StringBuffer(int capacity)
			                                           StringBuilder(int capacity)


     */
    @Test
    public void test1(){
    
    
        StringBuffer sb1 = new StringBuffer("abc");
        sb1.setCharAt(0,'m');
        System.out.println(sb1);

        StringBuffer sb2 = new StringBuffer();
        System.out.println(sb2.length());//0
    }

1.5.3 Méthodes courantes

Remarque : Les noms de méthodes dans StringBuffer et StringBuilder sont identiques et leur utilisation est également la même.

 /*
    StringBuffer的常用方法:
    StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
    StringBuffer delete(int start,int end):删除指定位置的内容
    StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str  只要涉及到开始和结束位                                                                          置的都是左闭右开。
    StringBuffer insert(int offset, xxx):在指定位置插入xxx
    StringBuffer reverse() :把当前字符序列逆转
    public int indexOf(String str) 返回第一次出现的指定子字符串在该字符串中的索引。
    public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串
    public int length() 返回长度(字符数)。
    public char charAt(int n )返回此序列中指定索引处的 char 值。
    public void setCharAt(int n ,char ch)将给定索引处的字符设置为 ch。

        总结:
        增:append(xxx)
        删:delete(int start,int end)
        改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
        查:charAt(int n )
        插:insert(int offset, xxx)
        长度:length();
        *遍历获取内容:for() + charAt() / 直接toString()获取内容
     */
    @Test
    public void test2(){
    
    
        StringBuffer s1 = new StringBuffer("abc");
        s1.append(1);
        s1.append('1');
        System.out.println(s1);
//        s1.delete(2,4);
//        s1.replace(2,4,"hello");
//        s1.insert(2,false);
//        s1.reverse();
        String s2 = s1.substring(1, 3);
        System.out.println(s1);
        System.out.println(s1.length());
        System.out.println(s2);
    }

1.5.4 Comparaison d'efficacité de trois types de chaînes


/**
 * 关于StringBuffer和StringBuilder的使用
 *
 * @author shkstart
 * @create 2019 下午 3:32
 */
public class StringBufferBuilderTest {
    
    
    /*
    对比String、StringBuffer、StringBuilder三者的效率:
    从高到低排列:StringBuilder > StringBuffer > String
     */
    @Test
    public void test3(){
    
    
        //初始设置
        long startTime = 0L;
        long endTime = 0L;
        String text = "";
        StringBuffer buffer = new StringBuffer("");
        StringBuilder builder = new StringBuilder("");
        //开始对比
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
    
    
            buffer.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer的执行时间:" + (endTime - startTime));//7

        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
    
    
            builder.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder的执行时间:" + (endTime - startTime));//4

        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
    
    
            text = text + i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("String的执行时间:" + (endTime - startTime));//1026

    }

1.5.5 Traversée de chaînes

①字符串的遍历:
 
// 方法一:使用charAt(i):遍历字符串的每个元素,每个元素是字符的形式
 
for(int i=0;i < str.length();i++) {
    
    
     System.out.println(str.charAt(i)); 
}
 
//方法二:截取字符串中的每个字符
 
for(int i=0;i < str.length();i++) {
    
    
     System.out.println(str.substring(i,i+1)); 
     
}
 
//方法三:将字符串转变成字符数组
 
char[]  c = str.toCharArray();
 
for(int i=0;i < c.length;i++) {
    
    
    System.out.println(c[i]);
}
 
 

1.6 Exercices à cordes après les cours (épisode 473)

1.6.1 Chaînes inversées

package com.atguigu.exer;

import org.junit.Test;

/**
 * @author shkstart
 * @create 2019 上午 10:07
 */
public class StringDemo {
    
    

    /*
    将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”

    方式一:转换为char[]
     */
    public String reverse(String str,int startIndex,int endIndex){
    
    

        if(str != null){
    
    
            char[] arr = str.toCharArray();
            for(int x = startIndex,y = endIndex;x < y;x++,y--){
    
    
                char temp = arr[x];
                arr[x] = arr[y];
                arr[y] = temp;
            }

            return new String(arr);
        }
        return null;
    }

    //方式二:使用String的拼接
    public String reverse1(String str,int startIndex,int endIndex){
    
    
        if(str != null){
    
    
            //第1部分
            String reverseStr = str.substring(0,startIndex);
            //第2部分
            for(int i = endIndex;i >= startIndex;i--){
    
    
                reverseStr += str.charAt(i);
            }
            //第3部分
            reverseStr += str.substring(endIndex + 1);

            return reverseStr;

        }
        return null;
    }
    //方式三:使用StringBuffer/StringBuilder替换String
    public String reverse2(String str,int startIndex,int endIndex){
    
    
        if(str != null){
    
    
            StringBuilder builder = new StringBuilder(str.length());

            //第1部分
            builder.append(str.substring(0,startIndex));
            //第2部分
            for(int i = endIndex;i >= startIndex;i--){
    
    

                builder.append(str.charAt(i));
            }
            //第3部分
            builder.append(str.substring(endIndex + 1));

            return builder.toString();
        }
        return null;

    }

    @Test
    public void testReverse(){
    
    
        String str = "abcdefg";
        String reverse = reverse2(str, 2, 5);
        System.out.println(reverse);
    }

}

1.6.2 Obtenir le nombre de fois qu'une chaîne apparaît dans une autre chaîne

package com.atguigu.exer;

import org.junit.Test;

/**
 * @author shkstart
 * @create 2019 上午 10:26
 */
public class StringDemo1 {
    
    
    /*
    获取一个字符串在另一个字符串中出现的次数。
      比如:获取“ab”在 “abkkcadkabkebfkaabkskab” 中出现的次数

     */

    /**
     * 获取subStr在mainStr中出现的次数
     * @param mainStr
     * @param subStr
     * @return
     */
    public int getCount(String mainStr,String subStr){
    
    
        int mainLength = mainStr.length();
        int subLength = subStr.length();
        int count = 0;
        int index = 0;
        if(mainLength >= subLength){
    
    
            //方式一:
//            while((index = mainStr.indexOf(subStr)) != -1){
    
    
//                count++;
//                mainStr = mainStr.substring(index + subStr.length());
//            }
            //方式二:对方式一的改进
            while((index = mainStr.indexOf(subStr,index)) != -1){
    
    
                count++;
                index += subLength;
            }

            return count;
        }else{
    
    
            return 0;
        }
    }

    @Test
    public void testGetCount(){
    
    
        String mainStr = "abkkcadkabkebfkaabkskab";
        String subStr = "ab";
        int count = getCount(mainStr, subStr);
        System.out.println(count);
    }
}

1.6.3 Obtenir la plus grande sous-chaîne identique dans deux chaînes

package com.atguigu.exer;

import org.junit.Test;

import java.util.Arrays;

/**
 * @author shkstart
 * @create 2019 上午 10:42
 */
public class StringDemo2 {
    
    
    /*
    获取两个字符串中最大相同子串。比如:
   str1 = "abcwerthelloyuiodefabcdef";str2 = "cvhellobnm"
   提示:将短的那个串进行长度依次递减的子串与较长的串比较。

     */
    //前提:两个字符串中只有一个最大相同子串
    public String getMaxSameString(String str1,String str2){
    
    
        if(str1 != null && str2 != null){
    
    
            String maxStr = (str1.length() >= str2.length())? str1 : str2;
            String minStr = (str1.length() < str2.length())? str1 : str2;
            int length = minStr.length();

            for(int i = 0;i < length;i++){
    
    
                for(int x = 0,y = length - i;y <= length;x++,y++){
    
    
                    String subStr = minStr.substring(x,y);
                    if(maxStr.contains(subStr)){
    
    
                        return subStr;
                    }

                }
            }

        }
        return null;
    }

    // 如果存在多个长度相同的最大相同子串
    // 此时先返回String[],后面可以用集合中的ArrayList替换,较方便
    public String[] getMaxSameString1(String str1, String str2) {
    
    
        if (str1 != null && str2 != null) {
    
    
            StringBuffer sBuffer = new StringBuffer();
            String maxString = (str1.length() > str2.length()) ? str1 : str2;
            String minString = (str1.length() > str2.length()) ? str2 : str1;

            int len = minString.length();
            for (int i = 0; i < len; i++) {
    
    
                for (int x = 0, y = len - i; y <= len; x++, y++) {
    
    
                    String subString = minString.substring(x, y);
                    if (maxString.contains(subString)) {
    
    
                        sBuffer.append(subString + ",");
                    }
                }
//                System.out.println(sBuffer);
                if (sBuffer.length() != 0) {
    
    
                    break;
                }
            }
            String[] split = sBuffer.toString().replaceAll(",$", "").split("\\,");
            return split;
        }

        return null;
    }

    @Test
    public void testGetMaxSameString(){
    
    
        String str1 = "abcwerthello1yuiodefabcdef";
        String str2 = "cvhello1bnmabcdef";
        String[] maxSameStrings = getMaxSameString1(str1, str2);
        System.out.println(Arrays.toString(maxSameStrings));

    }

}

1.7 Emballage

Regroupez les types de base pour fournir des fonctions plus complètes.

1.7.1 Correspondance avec les types de base (notez int et char)

Insérer la description de l'image ici

1.7.2 La classe parent de la classe wrapper de type numérique : Number

Extrait du code source : Il s'agit d'une classe parent abstraite classe abstraite publique
Relation d'héritage numérique :
Insérer la description de l'image ici

Méthodes couramment utilisées : (L'utilisation est presque la même)

Insérer la description de l'image ici

1.7.3 Instructions de conversion entre les types de base, les classes wrapper et les classes String

Insérer la description de l'image ici
Résumer:

  1. Conversion entre types de base :
    Petit—>Grand type: Transférer automatiquement
    Grand—>Petit type:Transfert forcé
  2. Conversion entre types de référence :
    Petit—>Grand type: Rotation automatique, c'est-à-dire forme vers le haut
    Grand—>Petit type: Il doit y avoir une relation parent-enfant, c'est-à-dire une modélisation descendante
  3. Les types de base et les types d'emballage correspondant aux types de base sont convertis les uns aux autres :
    Type de base—>Classe d'emballage: ①. Appelez le constructeur de la classe de packaging (obsolète), ②. Appelez la méthode valueOf() de la classe de packaging correspondante (note : l'effet de mise en cache d'Integer dans la plage), ③. Boxing automatique (apparaît dans jdk1.5 )
    Classe d'emballage—>Type de base: ①. Appelez la méthode xxxValue() de la classe de packaging Xxx, ②. Unboxing automatique (jdk1.5 apparaît)
  4. Conversion entre types de base et types de chaînes :
    Type de base—>Type de chaîne: ①. Appelez la chaîne surchargée valueOf(Xxx xxx), ②. Utilisez "+" pour effectuer les opérations de connexion
    Type de chaîne—>Type de base : Appelez parseXxx(String s) de la classe wrapper
  5. Le type de packaging correspondant au type de base et le type String sont convertis l'un en l'autre :
    Classe d'emballage—>Type de chaîne:méthode toString
    Type de chaîne—>classe wrapper : Appelez parseXxx(String s) de la classe wrapper

1.7.4 Conversion mutuelle entre les types de base et leurs classes d'emballage correspondantes

package com.cn.ins;


import org.junit.Test;

/*
 * 包装类的使用:
 * 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
 * 
 * 2.掌握的:基本数据类型、包装类、String三者之间的相互转换
 */
public class WrapperTest {
    
    	
	//基本数据类型--->包装类,方式一:调用包装类的构造器(已过时)
	@Test
	public void test1(){
    
    
		
		int num1 = 10;
//		System.out.println(num1.toString());
		Integer in1 = new Integer(num1);
		System.out.println(in1.toString());//10
		
		Integer in2 = new Integer("123");//传入的值可以直接是数字,也可以是String类型的数字
		System.out.println(in2.toString());//123
		
		//报异常
//		Integer in3 = new Integer("123abc");
//		System.out.println(in3.toString());
		
		Float f1 = new Float(12.3f);
		Float f2 = new Float("12.3");//类型转化为包装类,想要转化成功必须是存纯字
		System.out.println(f1);//12.3
		System.out.println(f2);//12.3
		
		Boolean b1 = new Boolean(true);
		Boolean b2 = new Boolean("TrUe");
		System.out.println(b2); //true
		Boolean b3 = new Boolean("true123");//类型转化为包装类,boolean类型有优化,想要转化成功不一定是存纯数字
		System.out.println(b3);//false
		
		
		Order order = new Order();
		System.out.println(order.isMale);//false
		System.out.println(order.isFemale);//null
	}

/*
 * 基本数据类型--->包装类,方式二:static  Integer.valueOf(5);静态方法通过类名调用。
 * 在Integer类中,Integer底层里有一个缓存数组:包含256个Integer缓存对象,范围是 -128到127。
 * 使用valueOf()时,第一次使用时创建对象如果在范围内会进行缓存,如果第二次创建对象的数据相同,
 * 会先访问缓存对象,而不新建。如果指定范围外的值,直接新建对象>
 * 
 * 好处:在范围内创建对象,第二次创建时是相同的数据会节省一部分空间. 
 * 
 * 注意:只有Integer 有这个效果,换成别的类型在这个范围内也没有,与new对象效率一样。
 *     为了方便创建对象统一格式所以我们最好都是用第二种方式。
 */
@Test
public void test11(){
    
    
	
	Integer i2 = Integer.valueOf(5) ;//valueOf():2.如果数据再-128~127之间是高效的,相同数据只会创建一次
	Integer i3 = Integer.valueOf(5) ;
	System.out.println(i2==i3);//true 用的是同一个对象
	Integer i4 = Integer.valueOf("5") ;
	System.out.println(i3==i4);//true,值可以是数字 也可以是字符串类型的数字
	
}


//包装类--->基本数据类型:调用包装类Xxx的xxxValue()
	@Test
	public void test2(){
    
    
		Integer in1 = new Integer(12);
		
		int i1 = in1.intValue();//注意对应的是前面转化为包装类的。
		System.out.println(i1 + 1);
		
		
		Float f1 = new Float(12.3);
		float f2 = f1.floatValue();
		System.out.println(f2 + 1);
	}

/*
	 * JDK 5.0 新特性:自动装箱 与自动拆箱
	 */
	@Test
	public void test3(){
    
    
//		int num1 = 10;
//		//基本数据类型-->包装类的对象
//		method(num1);
		
		//自动装箱:基本数据类型 --->包装类
		int num2 = 10;
		Integer in1 = num2;//自动装箱
		
		boolean b1 = true;
		Boolean b2 = b1;//自动装箱
		
		//自动拆箱:包装类--->基本数据类型
		System.out.println(in1.toString());
		
		int num3 = in1;//自动拆箱
		
	}
	
	public void method(Object obj){
    
    
		System.out.println(obj);
	}	
}

class Order{
    
    
	
	boolean isMale;//默认值变为false
	Boolean isFemale;//默认值变为null了
}


1.7.5 Conversion entre les types de base et les types String

package com.cn.ins;


import org.junit.Test;

/*
 * 包装类的使用:
 * 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
 * 
 * 2.掌握的:基本数据类型、包装类、String三者之间的相互转换
 */
public class WrapperTest {
    
    
	
	
	
	//基本数据类型--->String类型:调用String重载的valueOf(Xxx xxx)
	@Test
	public void test4(){
    
    
		
		int num1 = 10;
		//方式1:连接运算
		String str1 = num1 + "";
		//方式2:调用String的valueOf(Xxx xxx)
		float f1 = 12.3f;
		String str2 = String.valueOf(f1);//"12.3"
		
		Double d1 = new Double(12.4);
		String str3 = String.valueOf(d1);
		System.out.println(str2);
		System.out.println(str3);//"12.4"
	
		
	}

	
//String类型 --->基本数据类型、包装类(方式相同):调用包装类的parseXxx(String s)
	@Test
	public void test5(){
    
    
		String str1 = "123";
		//错误的情况:
//		int num1 = (int)str1; 基本类型和引用数据类型不能直接进行转
//		Integer in1 = (Integer)str1;引用数据类型的强转即向下造型要有子父类关系
		//可能会报NumberFormatException
		int num2 = Integer.parseInt(str1);
		System.out.println(num2 + 1);//必须为纯数字
		
		String str2 = "true1";//同样有优化功能
		boolean b1 = Boolean.parseBoolean(str2);
		System.out.println(b1);
	}	
	
}	

1.7.6 Conversion entre la classe wrapper correspondant au type de base et le type String

package com.cn.ins;


import org.junit.Test;

/*
 * 包装类的使用:
 * 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
 * 
 * 2.掌握的:基本数据类型、包装类、String三者之间的相互转换
 */
public class WrapperTest {
    
    
	
//包装类--->化为String类型:toString方法
	@Test
	public void test6(){
    
    
		
		Double d1 = new Double(12.4);
		String d2 = d1.toString();
		System.out.println(d2);
		
	}
	
//String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
	@Test
	public void test5(){
    
    
		String str1 = "123";
		//错误的情况:
//		int num1 = (int)str1; 基本类型和引用数据类型不能直接进行转
//		Integer in1 = (Integer)str1;引用数据类型的强转即向下造型要有子父类关系
		//可能会报NumberFormatException
		int num2 = Integer.parseInt(str1);
		System.out.println(num2 + 1);//必须为纯数字
		
		String str2 = "true1";//同样有优化功能
		boolean b1 = Boolean.parseBoolean(str2);
		System.out.println(b1);
	}	
	
}	

1.8 API de date et d'heure avant JDK8

1.8.1 Classe java.lang.System

Insérer la description de l'image ici

//1.System类中的currentTimeMillis()
    @Test
    public void test1(){
    
    
        long time = System.currentTimeMillis();
        //返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
        //称为时间戳
        System.out.println(time);
    }

1.8.2 Classe java.util.Date

Insérer la description de l'image ici

 /*
    java.util.Date类
           |---java.sql.Date类

    1.两个构造器的使用
        >构造器一:Date():创建一个对应当前时间的Date对象
        >构造器二:创建指定毫秒数的Date对象
    2.两个方法的使用
        >toString():显示当前的年、月、日、时、分、秒
        >getTime():获取当前Date对象对应的毫秒数。(时间戳)

    3. java.sql.Date对应着数据库中的日期类型的变量,数据库交互时使用,平常用的还是 util.Date
        >如何实例化
        >如何将java.sql.Date对象转换为java.util.Date对象----多态
        >如何将java.util.Date对象转换为java.sql.Date对象
     */
    @Test
    public void test2(){
    
    
        //构造器一:Date():创建一个对应当前时间的Date对象
        Date date1 = new Date();
        System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019

        System.out.println(date1.getTime());//1550306204104

        //构造器二:创建指定毫秒数的Date对象
        Date date2 = new Date(155030620410L);
        System.out.println(date2.toString());

        //创建java.sql.Date对象
        java.sql.Date date3 = new java.sql.Date(35235325345L);
        System.out.println(date3);//1971-02-13 只显示年 月 日不显示时 分 秒

        //如何将java.util.Date对象转换为java.sql.Date对象
        //情况一:多态形式 new的是子类sql.Date,子类赋值给父类,父类转子类可以用多态。
//        Date date4 = new java.sql.Date(2343243242323L);
//        java.sql.Date date5 = (java.sql.Date) date4;
        //情况二:new的直接是util.Date对象,创建的对象是父类没办法直接转子类。
        Date date6 = new Date();//可以获取毫秒数,sql又恰好有一个毫秒数的构造方法。
        java.sql.Date date7 = new java.sql.Date(date6.getTime());


    }

1.8.3 java.text.SimpleDateFormat类

Insérer la description de l'image ici

/**
 * jdk 8之前的日期时间的API测试
 * 1. System类中currentTimeMillis();
 * 2. java.util.Date和子类java.sql.Date
 * 3. SimpleDateFormat
 * 4. Calendar
 *
 * @author shkstart
 * @create 2019 上午 11:35
 */
public class DateTimeTest {
    
    
    /*
    SimpleDateFormat的使用:SimpleDateFormat对日期Date类的格式化和解析

    1.两个操作:
    1.1 格式化:日期 --->字符串
    1.2 解析:格式化的逆过程,字符串 ---> 日期

    2.SimpleDateFormat的实例化

     */
    @Test
    public void testSimpleDateFormat() throws ParseException {
    
    
        //实例化SimpleDateFormat:使用默认的构造器
        SimpleDateFormat sdf = new SimpleDateFormat();

        //格式化:日期 --->字符串
        Date date = new Date();
        System.out.println(date);

        String format = sdf.format(date);
        System.out.println(format);

        //解析:格式化的逆过程,字符串 ---> 日期
        String str = "19-12-18 上午11:43";
        Date date1 = sdf.parse(str);
        System.out.println(date1);

        //*************按照指定的方式格式化和解析:调用带参的构造器*****************
//        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa");
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        //格式化
        String format1 = sdf1.format(date);
        System.out.println(format1);//2019-02-18 11:48:27
        //解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),
        //否则,抛异常
        Date date2 = sdf1.parse("2020-02-18 11:48:27");
        System.out.println(date2);
    }
    /*
    练习一:字符串"2020-09-08"转换为java.sql.Date

    练习二:"三天打渔两天晒网"   1990-01-01  xxxx-xx-xx 打渔?晒网?

    举例:2020-09-08 ? 总天数

    总天数 % 5 == 1,2,3 : 打渔
    总天数 % 5 == 4,0 : 晒网

    总天数的计算?
    方式一:( date2.getTime() - date1.getTime()) / (1000 * 60 * 60 * 24) + 1
    方式二:1990-01-01  --> 2019-12-31  +  2020-01-01 -->2020-09-08
     */
    @Test
    public void testExer() throws ParseException {
    
    
        String birth = "2020-09-08";

        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf1.parse(birth);
//        System.out.println(date);

        java.sql.Date birthDate = new java.sql.Date(date.getTime());
        System.out.println(birthDate);
    }

1.8.4 Classe java.util.Calendar (calendrier)

Insérer la description de l'image ici

 /*
    Calendar日历类(抽象类)的使用

     */
    @Test
    public void testCalendar(){
    
    
        //1.实例化
        //方式一:创建其子类(GregorianCalendar)的对象
        //方式二:调用其静态方法getInstance()
        Calendar calendar = Calendar.getInstance();
//        System.out.println(calendar.getClass());

        //2.常用方法
        //get() 返回给定日历字段的值。
        int days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);
        System.out.println(calendar.get(Calendar.DAY_OF_YEAR));

        //set()  将给定的日历字段设置为给定值。
        //calendar可变性
        calendar.set(Calendar.DAY_OF_MONTH,22);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);

        //add() 根据日历的规则,为给定的日历字段添加或减去指定的时间量。
        calendar.add(Calendar.DAY_OF_MONTH,-3);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);

        //getTime():日历类---> Date
        Date date = calendar.getTime();
        System.out.println(date);

        //setTime():Date ---> 日历类
        Date date1 = new Date();
        calendar.setTime(date1);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);

    }

1.9 Nouvelle API de date et d'heure dans JDK8

1.9.1 L'arrière-plan apparaît

Insérer la description de l'image ici

@Test
    public void testDate(){
    
    
        //偏移量 想要输出2020 9.8,就要年份减去1900  月份减去1,所以util.Date大部分方法都过时了。
        Date date1 = new Date(2020 - 1900,9 - 1,8);
        System.out.println(date1);//Tue Sep 08 00:00:00 GMT+08:00 2020
    }

Insérer la description de l'image ici
Insérer la description de l'image ici

1.9.2 LocalDate、LocalTime、LocalDateTime

Insérer la description de l'image ici
Insérer la description de l'image ici

/*
    LocalDate、LocalTime、LocalDateTime 的使用
    说明:
        1.LocalDateTime相较于LocalDate、LocalTime,使用频率要高
        2.类似于Calendar
     */
    @Test
    public void test1(){
    
    
        //now():获取当前的日期、时间、日期+时间
        LocalDate localDate = LocalDate.now();
        LocalTime localTime = LocalTime.now();
        LocalDateTime localDateTime = LocalDateTime.now();

        System.out.println(localDate);
        System.out.println(localTime);
        System.out.println(localDateTime);

        //of():设置指定的年、月、日、时、分、秒。没有偏移量
        LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
        System.out.println(localDateTime1);

		//2种实例化对象的方法相同
        //getXxx():获取相关的属性
        System.out.println(localDateTime.getDayOfMonth());
        System.out.println(localDateTime.getDayOfWeek());
        System.out.println(localDateTime.getMonth());
        System.out.println(localDateTime.getMonthValue());
        System.out.println(localDateTime.getMinute());//获取当前分钟

        //体现不可变性
        //withXxx():设置相关的属性
        LocalDate localDate1 = localDate.withDayOfMonth(22);
        System.out.println(localDate);//原来的日期没有变化
        System.out.println(localDate1);//变化的是新的日期


        LocalDateTime localDateTime2 = localDateTime.withHour(4);
        System.out.println(localDateTime);
        System.out.println(localDateTime2);

        //不可变性
        LocalDateTime localDateTime3 = localDateTime.plusMonths(3);
        System.out.println(localDateTime);
        System.out.println(localDateTime3);

        LocalDateTime localDateTime4 = localDateTime.minusDays(6);
        System.out.println(localDateTime);
        System.out.println(localDateTime4);
    }

1.9.3 Instantané

Insérer la description de l'image ici
Insérer la description de l'image ici

 /*
    Instant的使用
    类似于 java.util.Date类

     */
    @Test
    public void test2(){
    
    
        //now():获取本初子午线对应的标准时间,和东八区相差8小时,通过偏移量来解决。
        Instant instant = Instant.now();
        System.out.println(instant);//2019-02-18T07:29:41.719Z

        //添加时间的偏移量,相当于加了8小时。
        OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
        System.out.println(offsetDateTime);//2019-02-18T15:32:50.611+08:00

        //toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数  ---> Date类的getTime()
        long milli = instant.toEpochMilli();
        System.out.println(milli);

        //ofEpochMilli():通过给定的毫秒数,获取Instant实例  -->Date(long millis)
        Instant instant1 = Instant.ofEpochMilli(1550475314878L);
        System.out.println(instant1);
    }

1.9.4 Classe java.time.format.DateTimeFormatter

Insérer la description de l'image ici

/*
    DateTimeFormatter:格式化或解析日期、时间
    类似于SimpleDateFormat

     */

    @Test
    public void test3(){
    
    
//        方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        //格式化:日期-->字符串
        LocalDateTime localDateTime = LocalDateTime.now();
        String str1 = formatter.format(localDateTime);
        System.out.println(localDateTime);
        System.out.println(str1);//2019-02-18T15:42:18.797

        //解析:字符串 -->日期
        TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");
        System.out.println(parse);

//        方式二:
//        本地化相关的格式。如:ofLocalizedDateTime()
//        FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTime
        DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
        //格式化
        String str2 = formatter1.format(localDateTime);
        System.out.println(str2);//2019年2月18日 下午03时47分16秒


//      本地化相关的格式。如:ofLocalizedDate()
//      FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDate
        DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
        //格式化
        String str3 = formatter2.format(LocalDate.now());
        System.out.println(str3);//2019-2-18


//       重点: 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
        DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
        //格式化
        String str4 = formatter3.format(LocalDateTime.now());
        System.out.println(str4);//2019-02-18 03:52:09

        //解析
        TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
        System.out.println(accessor);

    }

1.9.5 Autres API de date et d'heure dans jdk8

Insérer la description de l'image ici

1.10 Comparateur Java

Description : Utilisé pour comparer les tailles entre les objets. Il est en fait comparé en fonction des attributs entre les objets.
Insérer la description de l'image ici
Insérer la description de l'image ici

1.10.1 Tri naturel : java.lang.Comparable

Insérer la description de l'image ici
Insérer la description de l'image ici
ComparerTest :

package com.atguigu.java;

import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;

/**
 * 一、说明:Java中的对象,正常情况下,只能进行比较:==  或  != 。不能使用 > 或 < 的
 *          但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
 *          如何实现?使用两个接口中的任何一个:Comparable 或 Comparator
 *
 * 二、Comparable接口与Comparator的使用的对比:
 *    Comparable接口的方式一旦确定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
 *    Comparator接口属于临时性的比较。
 *
 *
 *
 *
 *thor shkstart
 * @create 2019 下午 4:41
 */
public class CompareTest {
    
    
    /*
    Comparable接口的使用举例:  自然排序
    1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
    2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列。(系统提供的类重写compareTo方法默认是从小到大排序的自然排序)
    3. 重写compareTo(obj)的规则:
        如果当前对象this大于形参对象obj,则返回正整数,
        如果当前对象this小于形参对象obj,则返回负整数,
        如果当前对象this等于形参对象obj,则返回零。
    4. 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。
       在compareTo(obj)方法中指明如何排序
     */
     
    //1.java封装好的类,底层已经重写过compareTo方法了,直接使用sort方法比较即可。
    // 数组有:Arrays.sort,集合有:Collentions.sort 都可以调用compareTo方法。只不过写在数组或集合的自定义类的对象需要手动重写方法。
    @Test
    public void test1(){
    
    
        String[] arr = new String[]{
    
    "AA","CC","KK","MM","GG","JJ","DD"};

        Arrays.sort(arr);

        System.out.println(Arrays.toString(arr));//[AA, CC, DD, GG, JJ, KK, MM]

    }
    //2.自定义类
    @Test
    public void test2(){
    
    
        Goods[] arr = new Goods[5];
        arr[0] = new Goods("lenovoMouse",34);
        arr[1] = new Goods("dellMouse",43);
        arr[2] = new Goods("xiaomiMouse",12);
        arr[3] = new Goods("huaweiMouse",65);
        arr[4] = new Goods("microsoftMouse",43);

        Arrays.sort(arr);//sort方法会自动调用重写后的compareTo方法。

        System.out.println(Arrays.toString(arr));
    }

   

Marchandises:

package com.atguigu.java;

/**
 * 商品类
 * @author shkstart
 * @create 2019 下午 4:52
 */
public class Goods implements  Comparable{
    
    

    private String name;
    private double price;

    public Goods() {
    
    
    }

    public Goods(String name, double price) {
    
    
        this.name = name;
        this.price = price;
    }

    public String getName() {
    
    
        return name;
    }

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

    public double getPrice() {
    
    
        return price;
    }

    public void setPrice(double price) {
    
    
        this.price = price;
    }

    @Override
    public String toString() {
    
    
        return "Goods{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }

    //指明商品比较大小的方式:按照价格从低到高排序,再按照产品名称从高到低排序
    @Override
    public int compareTo(Object o) {
    
    
//        System.out.println("**************");
        if(o instanceof Goods){
    
    //判断对象是不是一个商品
            Goods goods = (Goods)o;
            //方式一:
            if(this.price > goods.price){
    
    
                return 1; //不一定是1,只要是个正数即可。
            }else if(this.price < goods.price){
    
    
                return -1;
            }else{
    
    
//                return 0;//只比较价格
        //如果商品价格相同在比较商品的名称
                //this.name为String类型的对象,String类本身提供的方法这个方法 是从低到高,加个负数从高到底
                return -this.name.compareTo(goods.name);
            }
            //方式二:可以直接调用包装类提供的compare方法,底层重写了这些代码步骤(方式一的if比较价格)
//           return Double.compare(this.price,goods.price);
        }
//        return 0;
        throw new RuntimeException("传入的数据类型不一致!");
    }
    //String提供了:int compareTo(String anotherString) 按字典顺序比较两个字符串。 
    //Double提供了:static int compare(double d1, double d2) 比较两个指定的 double 值。
    //            int compareTo(Double anotherDouble) 对两个 Double 对象所表示的数值进行比较。
 
 
}

1.10.2 Tri personnalisé : java.util.Comparator

Insérer la description de l'image ici
ComparerTest :

package com.atguigu.java;

import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;

/**
 * 一、说明:Java中的对象,正常情况下,只能进行比较:==  或  != 。不能使用 > 或 < 的
 *          但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
 *          如何实现?使用两个接口中的任何一个:Comparable 或 Comparator
 *
 * 二、Comparable接口与Comparator的使用的对比:
 *    Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
 *    Comparator接口属于临时性的比较。
 *
 *
 *
 *
 *thor shkstart
 * @create 2019 下午 4:41
 */
public class CompareTest {
    
    
   

    /*
    Comparator接口的使用:定制排序
    1.背景:
    当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码(比如jdk中的一些类,没有实现但自己又不能修改源码),
    或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,
    那么可以考虑使用 Comparator 的对象来排序
    2.比较规则:重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
    如果方法返回正整数,则表示o1大于o2;
    如果返回0,表示相等;
    返回负整数,表示o1小于o2。

     */
     //系统提供的类
    @Test
    public void test3(){
    
    
        String[] arr = new String[]{
    
    "AA","CC","KK","MM","GG","JJ","DD"};
        Arrays.sort(arr,new Comparator(){
    
    //调用sort(xx,xx)2个形参的构造方法。这里简写为匿名对象的匿名子类。

            //按照字符串从大到小的顺序排列
            @Override
            public int compare(Object o1, Object o2) {
    
    
                if(o1 instanceof String && o2 instanceof  String){
    
    //学了反泛型后不用在强转可以保证为String类型
                    String s1 = (String) o1;
                    String s2 = (String) o2;
                    return -s1.compareTo(s2);//compareTo从小到大,加上负数从大到小。
                }
//                return 0;
                throw new RuntimeException("输入的数据类型不一致");
            }
        });
        System.out.println(Arrays.toString(arr));
    }
//自定义的类
    @Test
    public void test4(){
    
    
        Goods[] arr = new Goods[6];
        arr[0] = new Goods("lenovoMouse",34);
        arr[1] = new Goods("dellMouse",43);
        arr[2] = new Goods("xiaomiMouse",12);
        arr[3] = new Goods("huaweiMouse",65);
        arr[4] = new Goods("huaweiMouse",224);
        arr[5] = new Goods("microsoftMouse",43);

        Arrays.sort(arr, new Comparator() {
    
    
            //指明商品比较大小的方式:按照产品名称从低到高排序,再按照价格从高到低排序(把自然排序规则改为定制排序)
            @Override
            public int compare(Object o1, Object o2) {
    
    
                if(o1 instanceof Goods && o2 instanceof Goods){
    
    
                    Goods g1 = (Goods)o1;
                    Goods g2 = (Goods)o2;
                    if(g1.getName().equals(g2.getName())){
    
    //产品名称相同 在按照价格排序
                        return -Double.compare(g1.getPrice(),g2.getPrice());
                    }else{
    
    
                        return g1.getName().compareTo(g2.getName());
                    }
                }
                throw new RuntimeException("输入的数据类型不一致");
            }
        });

        System.out.println(Arrays.toString(arr));
    }

}

Marchandises:

package com.atguigu.java;

/**
 * 商品类
 * @author shkstart
 * @create 2019 下午 4:52
 */
public class Goods implements  Comparable{
    
    

    private String name;
    private double price;

    public Goods() {
    
    
    }

    public Goods(String name, double price) {
    
    
        this.name = name;
        this.price = price;
    }

    public String getName() {
    
    
        return name;
    }

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

    public double getPrice() {
    
    
        return price;
    }

    public void setPrice(double price) {
    
    
        this.price = price;
    }

    @Override
    public String toString() {
    
    
        return "Goods{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
	/*问题:这个方式是自然排序时需要实现Comparable接口,重写compareTo(obj)的方法,那么我们用定制排序
	可以不写这个方法吗???
	答:定制排序出现的场景之一是:自然排序重写的写的方法不满足,所以需要定制排序重新指定规则,此时是
	   为了修改自然排序而改变,那么这个自定义类的方法就是为了自然排序,你实现接口不重写方法就不是自然排 
	   序了,当然不能省略。*/
    //指明商品比较大小的方式:按照价格从低到高排序,再按照产品名称从高到低排序
    @Override
    public int compareTo(Object o) {
    
    
//        System.out.println("**************");
        if(o instanceof Goods){
    
    
            Goods goods = (Goods)o;
            //方式一:是自己写的比较规则
            if(this.price > goods.price){
    
    
                return 1;
            }else if(this.price < goods.price){
    
    
                return -1;
            }else{
    
    
//                return 0;
                return -this.name.compareTo(goods.name);
            }
            //方式二:包装类的compare方法底层写好了比较规则,可以自己不用在写了直接调用方法即可。
//           return Double.compare(this.price,goods.price);
        }
//        return 0;
        throw new RuntimeException("传入的数据类型不一致!");
    }
}

1.11 Autres catégories couramment utilisées

1.11.1 Classe système

Insérer la description de l'image ici
Insérer la description de l'image ici

package com.atguigu.java;

import org.junit.Test;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * 其他常用类的使用
 * 1.System
 * 2.Math
 * 3.BigInteger 和 BigDecimal
 *
 * @author shkstart
 * @create 2019 下午 6:23
 */
public class OtherClassTest {
    
    

    @Test
    public void test1() {
    
    
        String javaVersion = System.getProperty("java.version");
        System.out.println("java的version:" + javaVersion);

        String javaHome = System.getProperty("java.home");
        System.out.println("java的home:" + javaHome);

        String osName = System.getProperty("os.name");
        System.out.println("os的name:" + osName);

        String osVersion = System.getProperty("os.version");
        System.out.println("os的version:" + osVersion);

        String userName = System.getProperty("user.name");
        System.out.println("user的name:" + userName);

        String userHome = System.getProperty("user.home");
        System.out.println("user的home:" + userHome);

        String userDir = System.getProperty("user.dir");
        System.out.println("user的dir:" + userDir);

    }

   

1.11.2 Cours de mathématiques

Insérer la description de l'image ici

1.11.3 BigInteger et BigDecimal

1).BigInteger : souvent utilisé pour résoudre des opérations sur des entiers extrêmement volumineux. Les deux sont utilisés de la même manière.

Insérer la description de l'image ici

Insérer la description de l'image ici
2) .BigDecimal : souvent utilisé pour résoudre des opérations précises en virgule flottante. L'opération ±*/ précédente peut être optimisée en une opération entre deux objets (décimal : décimal)

Insérer la description de l'image ici
Créer un objet :

BigDecimal(double val) -- 有坑,别用 java规定了定义浮点型运算不准确
BigDecimal(String val) -- 建议使用这个

Méthodes couramment utilisées :

BigDecimal  add(BigDecimal bd): 做加法运算
BigDecimal  substract(BigDecimal bd) : 做减法运算
BigDecimal  multiply(BigDecimal bd) : 做乘法运算
BigDecimal  divide(BigDecimal bd) : 做除法运算
divide(BigDecimal bd,保留位数,舍入方式):除不尽时使用
setScale(保留位数,舍入方式):同上(它是bd1 输入的数, 保留几位  舍入方式)
pow(int n):求数据的几次幂

Essai 1 :

接收用户输入的两个数字,做运算。
package cn.tedu.bigdecimal;
 
import java.math.BigDecimal;
import java.util.Scanner;
 
//浮点数的解决方案
public class Test1_BigDecimal {
    
    
       public static void main(String[] args) {
    
    
//           method();//用基本类型做运算
              method2();//BigDecimal
       }
      
       private static void method2() {
    
    
              //1,接受键盘输入的两个小数
              double a = new Scanner(System.in).nextDouble();
              double b = new Scanner(System.in).nextDouble();
             
              //!!!创建BigDecimal对象,建议使用String参数的构造方法!!!把它变为字符串的形式输出的才准确
              BigDecimal bd1 = new BigDecimal(a+"");
              BigDecimal bd2 = new BigDecimal(b+"");
             
              //2,做运算
              BigDecimal bd3 ;//保存计算结果
             
              bd3 = bd1.add(bd2);//加法运算
              System.out.println(bd3);
             
              bd3 = bd1.subtract(bd2);//减法运算
              System.out.println(bd3);
             
              bd3 = bd1.multiply(bd2);//乘法运算
              System.out.println(bd3);
             
              //除法运算,如果除不尽,会抛出异常java.lang.ArithmeticException
//           bd3 = bd1.divide(bd2);//除法运算
            
              //3是要保留3位小数,BigDecimal.ROUND_HALF_UP舍入方式是四舍五入
              bd3 = bd1.divide(bd2,3,BigDecimal.ROUND_HALF_UP);
              System.out.println(bd3);
       }
      
      
      
       public static void method() {
    
    
              //1,接受键盘输入的两个小数
              double a = new Scanner(System.in).nextDouble();
              double b = new Scanner(System.in).nextDouble();
             
              //2,普通方法做运算 ,可能都不精确!!88888888888
              System.out.println(a+b);
              System.out.println(a-b);
              System.out.println(a*b);
              System.out.println(a/b);
       }
      
}

Essai 2 :

 @Test
    public void test2() {
    
    
        BigInteger bi = new BigInteger("1243324112234324324325235245346567657653");
        BigDecimal bd = new BigDecimal("12435.351");
        BigDecimal bd2 = new BigDecimal("11");
        System.out.println(bi);
//         System.out.println(bd.divide(bd2));报错,除尽还好,如果除不尽有没有指定按照什么规则保留位数就报错。
        System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP));//除法运算,不指明按照默认保留位数显示,四舍五入
        System.out.println(bd.divide(bd2, 25, BigDecimal.ROUND_HALF_UP));//除法运算,保留15位,四舍五入

    }

1.12 Extension

1.12.1 Base

Concept :
Le système de base est le système de comptage de report. Il s'agit d'une méthode de comptage avec report définie artificiellement, similaire au mot « positif » dans les statistiques.
Pour tout système de base - le système de base X, cela signifie que l'opération numérique à chaque position est effectuée par un chiffre à chaque fois que X est atteint.
Le système décimal avance d’un après chaque dixième, le système hexadécimal avance d’un après chaque seizième, le système binaire avance d’un après deux, et ainsi de suite.
Normalement, 1 octet = 8 chiffres binaires,
donc si un nombre est exprimé en binaire, il peut être exprimé comme ceci : 0000 0000.
Combinez ces 8 bits, et chaque combinaison à trois chiffres forme un système octal, et chaque combinaison à quatre chiffres forme un système octal. un système octal.hexadécimal.
Caractéristiques
Binaire : 0 et 1, tous les deux valent un, commençant par 0b
Octal : 0-7, tous les huit valent un, commençant par 0
Décimal : 0-9, tous les dix valent un Hexadécimal
: 0-9, abcdef , tous les dix 16 en avant, en commençant par 0x

Conversion du décimal
en binaire : continuez à diviser par 2 jusqu'à ce que le quotient soit 0, prenez le reste et écrivez-le à l'envers.
Convertir le nombre décimal 11 en binaire : 1011.
Insérer la description de l'image ici

Convertir binaire en décimal : à partir de l’ordre inférieur, multipliez chaque bit par la puissance 2, puis additionnez.
Calculer une donnée binaire : la décimale correspondant à 0000 1101
Calculer une donnée binaire : la décimale correspondant à 0110 1110
Insérer la description de l'image ici

Binaire à octal : à partir du bit de poids faible, tous les trois chiffres sont regroupés en un groupe pour générer un nombre octal, et le bit le plus élevé est rempli de zéros.
Calculer l'octal correspondant aux données binaires 110 0111
Insérer la description de l'image ici

Convertir octal en binaire : convertissez un nombre en chiffres 3. S'il y a moins de trois chiffres, ajoutez des zéros au chiffre le plus élevé.
Calculer les données octales : données binaires correspondant à 023 0653
Insérer la description de l'image ici

Binaire en hexadécimal : un groupe de quatre, converti en 1 nombre, commençant par 0x (
omis). . .
Convertir l'hexadécimal en binaire : un nombre devient 4 chiffres
. . .

1.12.2 Différences entre longueurs

La chaîne a une méthode de longueur. Les tableaux n'ont pas de méthode de longueur mais un attribut de longueur. Les collections n'ont pas de méthode de longueur. La méthode de taille est utilisée pour calculer la longueur d'une collection.

2 IO流–01

Habituellement, le transfert de fichiers nécessite trois objets : conteneur (classe File) – flux de nœuds – flux de traitement.

Conteneur : la lecture et l'écriture de données nécessitent un point de terminaison, où les données sont lues et écrites. Tout en Java est un objet et la classe File est cet objet.
Flux de nœuds : Le flux qui agit directement sur la classe File est appelé flux de nœuds.
Flux de traitement : un flux attaché directement ou indirectement à la classe File.

2.1 Introduction aux IO

Note 1:

  1. La lecture et l'écriture des données sont résumées en données et circulent dans le pipeline.Insérer la description de l'image ici
  2. I----->entrée : flux d'entrée
  3. O—>sortie : flux de sortie
  4. Le flux ne peut circuler que dans une seule direction
  5. Les données ne peuvent être lues et écrites que de manière séquentielle du début à la fin.
  6. Opération de fermeture de flux : vous ne pouvez rien faire concernant les connexions physiques telles que les connexions de base de données, les flux d'entrée et de sortie et les connexions Socket. Ces ressources doivent être fermées manuellement.
  7. Toutes les API pour les flux IO proviennent du package java.io.
    Insérer la description de l'image ici

Note 2 : Les entrées et sorties du flux io sont 程序analysées du point de vue de la mémoire ( ) :

  1. Les données persistantes transférées en mémoire sont appelées flux d'entrée.
  2. Les données transférées de la mémoire vers les données persistantes sont appelées flux de sortie.
    Insérer la description de l'image ici

2.2 Classement IO

Insérer la description de l'image ici

  1. Selon les différentes directions du flux de données, il est divisé en : flux d'entrée et flux de sortie.

  2. Selon différentes unités de données d'exploitation, il est divisé en : flux d'octets (8 bits), flux de caractères (16 bits)
    字节流 : adapté aux images, vidéos, etc. La transmission de données du flux d'octets est constituée de données binaires telles que 0101.
    字符流: Convient pour la transmission de fichiers. La transmission de données du flux de caractères est constituée de chaque caractère de type char, vous pouvez donc savoir ce qui est transmis. 16 bits (bit) = 2 octets (octet) = 1 caractère de type char.

  3. Selon les différents rôles des flux, ils se répartissent en : flux de nœuds, flux de traitement,
    flux de nœuds/flux de bas niveau : les flux qui agissent directement sur les fichiers sont appelés flux de nœuds.
    Flux de traitement/flux avancé : Basé sur le flux de nœud, il encapsule directement ou indirectement la couche supérieure du flux. Ce flux externe est appelé flux de traitement et peut encapsuler plusieurs flux.

2.3 Structure successorale

Remarque : toutes les classes de io flow proviennent de 4 classes parentes abstraites.
Insérer la description de l'image ici
Remarque : Les cellules les plus sombres représentent les flux les plus couramment utilisés. Peut être distingué en fonction du suffixe

字节流:针对二进制文件 更广泛   用来操作电脑里的所有格式的数据。
|---InputStream   //字节输入流的抽象父类
    |---FileInputStream   //文件输入流 子类  (低级流/节点流)
    |---FilterInputStream //略,不常用
        |---BufferedInputStream //缓冲字节输入流 子类   (高级流/处理流)
        |---DataInputStream //数据输入流 子类(高级流)
    |---ObjectInputStream    //反序列化   子类(创建对象不用多态) (高级流)

|---OutputStream  //字节输出流抽象父类
    |---FileOutputStream //文件输出流 子类 (低级流)
    |---FilterOutputStream //略,不常用
        |---BufferedOutputStream //缓冲字节输出流 子类 (高级流)
        |---PrintStream //字节打印输出流 子类(高级流)
        |---DataOutputStream //数据输出流 子类(高级流)

    |---ObjectOutputStream //序列化   子类(创建对象不用多态) (高级流)
    



字符流:针对文本文件。读写容易发生乱码现象,在读写时最好指定编码集为utf-8, 用来处理文本格式的数据txt。
|---Reader  //字符输入流的抽象父类
   |---BufferedReader //缓冲字符输入流 子类 (高级流)
   |---InputStreamReader  //输入的转换流 (可以用多态) (高级流)
       |---FileReader  //字符输入流 子类(低级流/节点流)
  
   
|---Writer //字符输出流抽象父类
    |---BufferedWriter  //缓冲字符输出流 子类 (高级流)
    |---OutputStreamWriter   //输出的转换流(可以用多态)(高级流)
        |---FileWriter  //字符输出流 子类(低级流/节点流)
    |---PrintWriter  //字符打印输出流 (高级流)




Insérer la description de l'image ici

2.4 Utilisation de la classe File

2.4.1 Aperçu

  1. Un objet de la classe File, représentant un fichier ou un répertoire de fichiers (communément appelé : dossier)
  2. La classe File est déclarée sous le package java.io
  3. La classe File implique des méthodes telles que la création, la suppression, le renommage, l'heure de modification et la taille des fichiers ou des répertoires de fichiers, mais n'implique pas l'opération d'écriture ou de lecture du contenu du fichier. Si vous devez lire ou écrire le contenu d'un fichier, vous devez utiliser des flux IO pour le compléter.
  4. Les objets suivants de la classe File sont souvent transmis en tant que paramètres au constructeur du flux pour indiquer le « point final » de la lecture ou de l'écriture.

2.4.2 Méthode constructeur pour instancier des objets (problème de chemin)

Méthode de construction :
Insérer la description de l'image ici
Notes sur les chemins relatifs :
1. Projets Java ordinaires : En idée, le chemin relatif du code écrit dans la méthode de test unitaire fait référence au module actuel (sous chaque projet). S'il se trouve dans la méthode principale, il est relatif au projet en cours (ce qu'on appelle l'espace de travail).
2. Projets Java ordinaires : dans Eclipse, chaque projet individuel est considéré comme un projet distinct, donc le test unitaire et la méthode principale sont des chemins relatifs vers le projet en cours.

Remarques sur les séparateurs de chemin :
Insérer la description de l'image ici

2.4.3 Test des méthodes du constructeur

package com.file;


import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Date;


public class FileTest {
    
    
    /*
     路径分隔符:路径中的每级目录之间用一个路径分隔符隔开,和系统有关。
     windows和DOS系统:\,java中为了避免转义字符,通常写为\\.
     UNIX和URL:/,一般在windows中这样写也支持。
     为了解决在不同操作系统中路径分隔符的麻烦,File类提供了一个常量:
         public static final String separator。根据操作系统,动态的提供分隔符。

     */
    @Test
    public void test1(){
    
    
        //构造器1
        File file1 = new File("hello.txt");//相对路径:相对于当前module,E:\idea-workspace\IO
        File file2 =  new File("D:\\workspace_idea1\\JavaSenior\\day08\\he.txt");//绝对路径:因为java中,\单斜杠代表转义字符,所以使用\\双斜杠表示路径分隔符。

        System.out.println(file1);//hello.txt
        System.out.println(file2);//D:\workspace_idea1\JavaSenior\day08\he.txt

        //构造器2:
        File file3 = new File("D:\\workspace_idea1","JavaSenior");
        System.out.println(file3);//D:\workspace_idea1\JavaSenior

        //构造器3:
        File file4 = new File(file3,"hi.txt");
        System.out.println(file4);//D:\workspace_idea1\JavaSenior\hi.txt

        //总结:这三种构造器创建对象,此时调用toString()方法,输出的是内存层面的对象路径,而在磁盘中没有对应的文件或文件目录。
    }
}

2.4.4 Tests de méthode commune

package com.file;


import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Date;


public class FileTest {
    
    

    /*
    一:File类的获取功能:

    1.public String getAbsolutePath():获取绝对路径
    2.public String getPath() :获取路径
    3.public String getName() :获取名称
    4.public String getParent():获取上层文件目录路径。若无,返回null
    5.public long length() :获取文件长度(即:以字节为单位,字符数。如:aa 的长度为2)。不能获取目录的长度。
    6.public long lastModified() :获取最后一次的修改时间,毫秒值

    如下的两个方法适用于文件目录:
    7.public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
    8.public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
     */
    @Test
    public void test2(){
    
    
        File file1 = new File("hello.txt");//相对路径,值接在项目时右键---new file
        File file2 = new File("d:\\io\\hi.txt");//绝对路径
        //此时方法的调用还只是内存方面的,不涉及磁盘路径。
        System.out.println(file1.getAbsolutePath());//E:\idea-workspace\IO\hello.txt
        System.out.println(file1.getPath());//hello.txt
        System.out.println(file1.getName());//hello.txt
        System.out.println(file1.getParent());//null,文件存在仍为null 它是根据相对路径找的。
        System.out.println(file1.length());//0,因为文件不存在,如果存在可以获取到值。选中项目--右键new file
        System.out.println(new Date(file1.lastModified()));//Thu Jan 01 08:00:00 CST 1970

        System.out.println();

        //此时方法的调用还只是内存方面的,不涉及磁盘路径。
        System.out.println(file2.getAbsolutePath());//d:\io\hi.txt
        System.out.println(file2.getPath());//d:\io\hi.txt
        System.out.println(file2.getName());//hi.txt
        System.out.println(file2.getParent());//d:\io
        System.out.println(file2.length());//0
        System.out.println(file2.lastModified());//0
    }
    //测试文件目录:7,8
    @Test
    public void test3(){
    
    
        //磁盘路径必须真实存在才能获取。
        File file = new File("D:\\workspace_idea1\\JavaSenior");

        String[] list = file.list();//只获取到名称数组
        for(String s : list){
    
    
            System.out.println(s);//a.txt,aa,b.txt,bb
        }

        System.out.println();

        File[] files = file.listFiles();//获取的是完整路径数组
        for(File f : files){
    
    
            System.out.println(f);//D:\workspace_idea1\JavaSenior\a.txt,D:\workspace_idea1\JavaSenior\b.txt
                                  //D:\workspace_idea1\JavaSenior\aa,   D:\workspace_idea1\JavaSenior\bb
        }

    }

    /*
    *二:File类的重命名功能:
    * 9. public boolean renameTo(File dest):把文件重命名为指定的文件路径
    *    比如:file1.renameTo(file2)为例:
    *    要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在(路径存在,文件不存在)。
    */
    @Test
    public void test4(){
    
    
        File file1 = new File("hello.txt");//真实存在
        File file2 = new File("D:\\io\\hi.txt");//不能存在

        boolean renameTo = file1.renameTo(file2);
        System.out.println(renameTo);//true hello.txt消失  hi.txt自动生成,相当于把hello.txt文件剪贴到D:\io路径下并改名为hi.txt
                                     //再次执行因为file1已经到file2中了,所以false

    }
    /*三:File类的判断功能:
     10.public boolean isDirectory():判断是否是文件目录
     11.public boolean isFile() :判断是否是文件
     12.public boolean exists() :判断是否存在
     13.public boolean canRead() :判断是否可读
     14.public boolean canWrite() :判断是否可写
     15.public boolean isHidden() :判断是否隐藏

     */
    @Test
    public void test5(){
    
    
        //相对路径
        File file1 = new File("hello.txt");//文件真实存在
        file1 = new File("hello1.txt");//文件不存在

        System.out.println(file1.isDirectory());//false  false
        System.out.println(file1.isFile());//true  false
        System.out.println(file1.exists());//true  false
        System.out.println(file1.canRead());//true  false
        System.out.println(file1.canWrite());//true  false
        System.out.println(file1.isHidden());//false  false

        System.out.println();
        //绝对路径
        File file2 = new File("d:\\io");//文件目录存在
        //file2 = new File("d:\\io1");//文件目录不存在
        System.out.println(file2.isDirectory());//true  false
        System.out.println(file2.isFile());//false  false
        System.out.println(file2.exists());//true  false
        System.out.println(file2.canRead());//true  false
        System.out.println(file2.canWrite());//true  false
        System.out.println(file2.isHidden());//false  false

    }
    /*
    四:创建硬盘中对应的文件或文件目录:真正的在硬盘中创建
    16.public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
    17.public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
    18.public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建

    五:删除磁盘中的文件或文件目录:
    19.public boolean delete():删除文件或者文件夹
    删除注意事项:Java中的删除不走回收站。

     */
    @Test
    public void test6() throws IOException {
    
    
        File file1 = new File("hi.txt");
        if(!file1.exists()){
    
    
            //文件的创建
            file1.createNewFile();
            System.out.println("创建成功");
        }else{
    
    //文件存在
            file1.delete();
            System.out.println("删除成功");
        }


    }
    @Test
    public void test7(){
    
    
        //文件目录的创建 io 存在 io1 io3不存在,如果上层目录不包含则创建失败。
        File file1 = new File("d:\\io\\io1\\io3");

        boolean mkdir = file1.mkdir();
        if(mkdir){
    
    
            System.out.println("创建成功1");
        }
        //io 存在 io1 io4不存在,上层目录不包含,会帮你自动创建上层目录,创建成功。
        File file2 = new File("d:\\io\\io1\\io4");

        boolean mkdir1 = file2.mkdirs();
        if(mkdir1){
    
    
            System.out.println("创建成功2");
        }
        //要想删除成功,io4文件目录下不能有子目录或文件
        File file3 = new File("D:\\io\\io1\\io4");
        file3 = new File("D:\\io\\io1");
        System.out.println(file3.delete());
    }
}

2.4.5 Exercice 1 : Trouver de manière récursive la taille totale du fichier

Récursivité : appeler continuellement la méthode elle-même.
Exigences : Statistiques de taille de fichier
1. Encapsuler le répertoire spécifié dans un objet File
2. Répertorier la liste des dossiers
3. Déterminer s'il s'agit d'un fichier, ajouter directement f.length()
4. Déterminer s'il s'agit d'un dossier, appeler de manière récursive La logique métier de la méthode elle-même est de boucler et de juger à nouveau. S'il s'agit d'un fichier, ajoutez-le. S'il s'agit d'un dossier, continuez la liste et continuez à juger. S'il s'agit d'un fichier, ajoutez-le... Remarque : Seuls les fichiers ont une taille et les répertoires n'ont pas de taille
.

package com.file;

import java.io.File;

public class Test2_File2 {
    
    
    public static void main(String[] args) {
    
    
//		1、把指定目录封装成File对象
        File file = new File("D:\\teach\\a");
        int size =count(file);//为了使用File路径,要把它传参到那个方法
        System.out.println(size);
    }

    private static int count(File file) {
    
    //file是File类型的
//		2、把文件夹列表列出来
        File[] files = file.listFiles();//获取指定目录下的所有文件或者文件目录的File数组

        //2.1 遍历数组里的每个资源
        int sum = 0;//记录文件的大小
        for (int i = 0; i < files.length; i++) {
    
    
//		3、判断,如果是文件,直接把f.length()相加
// files[i]表示每次遍历到的资源
            if(files[i].isFile()) {
    
    
                sum += files[i].length();//求文件的和
            }else if(files[i].isDirectory()){
    
    
//		4、判断,如果是文件夹,继续列表,继续判断,如果是文件相加,如果又是文件夹,继续列表,继续判断,如果是文件相加......
//		5、如果是文件夹,递归调用方法本身的业务逻辑
                sum += count(files[i]);//把当前遍历到的文件夹继续循环判断求和
            }
        }

        return sum ;
    }


}

2.4.6 Exercice 2 : Supprimer des dossiers de manière récursive

Récursivité : appeler continuellement la méthode elle-même.
Conditions requises : Supprimer des dossiers de manière récursive
1. Encapsuler le répertoire spécifié dans un objet Fichier
2. Répertorier la liste des dossiers
3. Juger, s'il s'agit d'un fichier, supprimez-le directement
4. Juger, s'il s'agit d'un dossier, appeler la logique métier du méthode elle-même de manière récursive. Bouclez et jugez à nouveau. S'il s'agit d'un fichier, supprimez-le. S'il s'agit d'un dossier, continuez la liste et continuez à juger. S'il s'agit d'un fichier, supprimez-le... Remarque : Si vous souhaitez le
supprimer avec succès, il ne doit y avoir aucun sous-répertoire ou fichier dans le répertoire de fichiers. Les suppressions en Java ne vont pas dans la corbeille.

package com.file;

import java.io.File;
import java.util.Scanner;

public class Test1_Size {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("请输入想要删除的文件目录写的路径:");
        //1,接收用户输入的文件夹路径
        String path = new Scanner(System.in).nextLine();

        File dir = new File(path);

        //2,调用指定方法求大小
        //		long max = size(dir);
        //		System.out.println("文件夹总大小是:"+max);

        //i,调用指定方法删除文件夹
        del(dir);
        System.out.println("删除成功");
    }

    //ii,新建删除方法
    public static void del(File dir) {
    
    
        //1,列出所有资源
        File[] a = dir.listFiles();

        //遍历数组,拿到每次资源
        for (int i = 0; i < a.length; i++) {
    
    
            //2,判断资源是文件还是文件夹
            if (a[i].isFile()) {
    
    //如果是文件直接删除
                a[i].delete();
            } else if (a[i].isDirectory()) {
    
    //如果是文件夹
                del(a[i]);//重复的 列资源,重复删文件,重复的判断是文件夹继续列资源,删除........
            }
        }
        //TODO  把文件夹里的文件都删掉了,怎么删除空的文件夹呢??
        dir.delete();//文件夹没有文件了,出了for循环之后可以删除空目录了。
    }
}

Lecture de flux de 2,5 octets

illustrer:

  1. Un flux d'octets est composé d'octets et un flux de caractères est composé de caractères. Les caractères en Java sont composés de deux octets. Le flux d'octets est le plus basique. Toutes les sous-classes d'InputStream et OutputStream sont,Principalement utilisé pour traiter des données binaires

  2. Le streaming fait principalement référence au transfert de l'intégralitéFichiers multimédias tels que audio et vidéo et médias tridimensionnelsAprès une méthode de compression spécifique, il est analysé en packages compressés et transmis par le serveur vidéo à l'ordinateur de l'utilisateur de manière séquentielle ou en temps réel. Dans un système qui utilise la transmission en continu, l'utilisateur n'a pas besoin d'attendre que le fichier entier soit téléchargé comme dans la méthode de téléchargement, mais il peut utiliser le dispositif de décompression sur l'ordinateur de l'utilisateur après seulement quelques secondes ou dizaines de secondes de Délai de démarrage. Les fichiers compressés A/V, 3D et autres fichiers multimédias peuvent être lus et visualisés après décompression. À ce stade, les parties restantes du fichier multimédia continueront à être téléchargées sur le serveur en arrière-plan.
    Insérer la description de l'image ici

2.5.1 Classe parent abstraite InputStream

Remarque : Cette classe abstraite représente la classe parent de tous les flux d'entrée d'octets. Elle n'apprend pas ses méthodes de construction pour créer des objets, mais apprend uniquement ses méthodes communes.

Méthodes couramment utilisées :

abstract  int read() 
          从输入流中读取数据的下一个字节。 
int read(byte[] b)    注意是byte
          从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 
int read(byte[] b, int off, int len) 
          将输入流中最多 len 个数据字节读入 byte 数组。 
void close()   //使用流后不管是读入还是写出都需要关闭资源
          关闭此输入流并释放与该流关联的所有系统资源。
......

2.5.2 Sous-classe FileInputStream (flux de nœuds : flux d'octets de fichier)

Description : Insérez directement dans le fichier et lisez directement les données du fichier.

Créer un objet :

1. FileInputStream(File file) 
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 
2. FileInputStream(FileDescriptor fdObj) 
          通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。 
3. FileInputStream(String name) 
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。 

test:

package com.io_file;

import org.junit.Test;

import java.io.*;

/**
 * 测试FileInputStream和FileOutputStream的使用
 *
 * 结论:
 * 1. 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理  (因为字节流处理中文是乱码)
 *     注意这里的乱码指的是 数据到内存层面进行查看有乱码,如果是单纯的复制 硬盘-->硬盘 那么文本文件仍然可以使用字节流处理。
 * 2. 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),只能使用字节流处理   (因为这些非文本文件的基本单位比较小,字符的单位比较大处理不了)
 *
 *
 *
 * @author shkstart
 * @create 2019 下午 2:13
 */
public class FileInputOutputStreamTest {
    
    
    //把磁盘中的数据读入到内存中,并输出到控制台。
    //使用字节流FileInputStream处理文本文件,可能出现乱码(在各种编码中,一个字节可以存下一个英文字符,而存不一个中文存字符)。
    @Test
    public void testFileInputStream() {
    
    
        FileInputStream fis = null;
        try {
    
    
            //1. 造文件
            File file = new File("hello.txt");

            //2.造流
            fis = new FileInputStream(file);

            //3.读数据
            byte[] buffer = new byte[1024];//注意使用的是字节数组 每次读取直接的大小 这个值不能太大也不能太小,1024复制速度最快
            int len;//记录每次读取的字节的个数
            while((len = fis.read(buffer)) != -1){
    
    //数据的下一个字节,如果达到文件的末尾, -1 

                String str = new String(buffer,0,len);//字节数组转化为String类型
                System.out.print(str);

            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(fis != null){
    
    
                //4.关闭资源
                try {
    
    
                    fis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }

    }

   
}


2.5.3 Sous-classe BufferedInputStream (flux de traitement : flux d'octets mis en mémoire tampon)

Remarque : lors de la création d'un BufferedInputStream, un tableau de tampons interne (taille par défaut 8M=8*1024) sera créé. Ce tampon interne peut être rempli à nouveau, si nécessaire, à partir du flux d'entrée contenant, plusieurs octets à la fois, à mesure que les octets du flux sont lus ou ignorés 缓存流的作用都是提高读写效率的.

Créer un objet :

1. BufferedInputStream(InputStream in) 
          创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 
          
2. BufferedInputStream(InputStream in, int size) 
          创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 

test:

package com.bf_file;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之一:缓冲流的使用
 *
 * 1.缓冲流:
 * BufferedInputStream
 * BufferedOutputStream
 * BufferedReader
 * BufferedWriter
 *
 * 2.作用:提供流的读取、写入的速度
 *   提高读写速度的原因:内部提供了一个缓冲区 (把一定量的数据读入到缓冲区后 一次性写出 写入)
 *
 * 3. 处理流,就是“套接”在已有的流的基础上,处理流不能直接作用在文件上。
 *
 * @author shkstart
 * @create 2019 下午 2:44
 */
public class BufferedTest {
    
    

    /*
    实现非文本文件的复制,单纯的复制 字节流可以用于任何类型的文件数据
     */
    @Test
    public void BufferedStreamTest() throws FileNotFoundException {
    
    
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
    
    
            //1.造文件
            File srcFile = new File("爱情与友情.jpg");
            File destFile = new File("爱情与友情3.jpg");
            //2.造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节:读取、写入
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer)) != -1){
    
    
                bos.write(buffer,0,len);

//                bos.flush();//刷新缓冲区:如果不调用,默认数据存满缓冲区后读入 写出。如果在这里显示调用,可能数据还没存满缓冲区后就开始读入 写出。

            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流  同级别的流顺序无所谓
            if(bos != null){
    
    
                try {
    
    
                    bos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            if(bis != null){
    
    
                try {
    
    
                    bis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
//        fos.close();
//        fis.close();
        }



    }

    //工具Api:实现文件复制的方法 单纯的复制 字节流可以用于任何类型的文件数据
    public void copyFileWithBuffered(String srcPath,String destPath){
    
    
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
    
    
            //1.造文件
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);
            //2.造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节:读取、写入
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer)) != -1){
    
    
                bos.write(buffer,0,len);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            if(bos != null){
    
    
                try {
    
    
                    bos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            if(bis != null){
    
    
                try {
    
    
                    bis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
//        fos.close();
//        fis.close();
        }
    }

    @Test
    public void testCopyFileWithBuffered(){
    
    
        long start = System.currentTimeMillis();

        String srcPath = "D:\\aa\\bb\\01-尚硅谷-Java语言基础.avi";
        String destPath = "D:\\aa\\bb\\02-尚硅谷-Java语言基础.avi";


        copyFileWithBuffered(srcPath,destPath);


        long end = System.currentTimeMillis();

        System.out.println("复制操作花费的时间为:" + (end - start));//618 - 176
    }

}

2.5.4 Sous-classe ObjectInputStream (flux de traitement : flux d'entrée d'objet/désérialisation)

Rôle : 基本数据类型数据 或 对象(主要用于对象)Flux de traitement pour le stockage et la lecture. Sa puissance réside dans le fait qu'il peut écrire des objets en Java dans la source de données et restaurer des objets à partir de la source de données.

Description du nom :

  1. Sérialisation : mécanisme permettant de sauvegarder des données ou des objets de type de base à l'aide de la classe ObjectOutputStream (convertir les informations sur l'objet en une chaîne de valeurs d'octets dans un format fixe, les afficher et les conserver sur le disque)
  2. Désérialisation : mécanisme de lecture de données ou d'objets de type de base à l'aide de la classe ObjectInputStream (lecture des données sérialisées à partir du disque et restauration de l'objet)
    Insérer la description de l'image ici

Mécanisme de sérialisation d'objets: Permet de convertir des objets Java en mémoire en un flux binaire indépendant de la plate-forme, permettant à ce flux binaire d'être conservé sur le disque ou transmis sur le réseau vers un autre nœud du réseau. Lorsque d'autres programmes obtiennent ce flux binaire, ils peuvent être restaurés dans l'objet Java d'origine.

Caractéristiques:

  1. Les fichiers qui doivent être sérialisés doivent implémenter l'interface Serialisable pour activer leur fonctionnalité de sérialisation.
  2. Les données qui n'ont pas besoin d'être sérialisées peuvent être modifiées en tant que statiques. Puisque static appartient à la classe, elles ne seront pas sérialisées et sorties avec l'objet.
  3. Les données qui ne nécessitent pas de sérialisation peuvent également être modifiées comme transitoires. Elles n'existent en mémoire que pendant l'exécution du programme et ne seront ni sérialisées ni conservées.
  4. Pendant la désérialisation, si elle n'est pas cohérente avec le numéro de version sérialisé, la désérialisation ne peut pas être terminée.
  5. Chaque fichier sérialisé a un ID unique. S'il n'est pas ajouté, le compilateur calculera et générera un numéro de version basé sur les informations de définition de classe.
  6. Couramment utilisé pour la transmission de données entre serveurs, la sérialisation en fichiers et la désérialisation pour lire des données.
  7. Couramment utilisé pour transmettre des objets entre hôtes à l'aide de flux de socket.

Test :
classe ObjectInputOutputStreamTest :

package com.io_file;

import org.junit.Test;

import java.io.*;

/**
 * 对象流的使用
 * 1.ObjectInputStream 和 ObjectOutputStream
 * 2.作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
 *
 * 3.要想一个java对象是可序列化的,需要满足相应的要求。见Person.java
 *
 * 4.序列化机制:
 * 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种
 * 二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
 * 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

 *
 * @author shkstart
 * @create 2019 上午 10:27
 */
public class ObjectInputOutputStreamTest {
    
    

    /*
    序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
    使用ObjectOutputStream实现
    序列化的文件保存在磁盘中 直接打开有乱码读不懂,想要读懂 可以反序列化到内存中 输出到控制台查看。
     */
    @Test
    public void testObjectOutputStream(){
    
    
        ObjectOutputStream oos = null;

        try {
    
    
            //1.
            oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
            //2.
            oos.writeObject(new String("我爱北京天安门"));//系统提供的对象 String
            oos.flush();//刷新操作

            oos.writeObject(new Person("王铭",23));
            oos.flush();

            oos.writeObject(new Person("张学良",23,1001,new Account(5000)));
            oos.flush();

        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(oos != null){
    
    
                //3.
                try {
    
    
                    oos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }

    }

    /*
    反序列化:将磁盘文件中的对象还原为内存中的一个java对象
    使用ObjectInputStream来实现
     */
    @Test
    public void testObjectInputStream(){
    
    
        ObjectInputStream ois = null;
        try {
    
    
            ois = new ObjectInputStream(new FileInputStream("object.dat"));

            Object obj = ois.readObject();//读写顺序保持一致
            String str = (String) obj;

            Person p = (Person) ois.readObject();
            Person p1 = (Person) ois.readObject();

            System.out.println(str);
            System.out.println(p);
            System.out.println(p1);

        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(ois != null){
    
    
                try {
    
    
                    ois.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }



    }

}


Classe de personne :

package com.io_file;

import java.io.Serializable;

/**
 * Person需要满足如下的要求,方可序列化
 * 1.需要实现接口:Serializable    (String为系统提供的类 底层源码实现了序列化接口,这里自定义类需要手动实现序列化接口。没有实现序列化接口 在序列化对象时回报异常)
 *              Serializable是标识接口:里面没有方法和属性,表示凡是实现这个接口的类都可以序列化。
 *
 * 2.当前类提供一个全局常量:serialVersionUID
 *              问题1:为什么要提供这个全局变量序列化接口???
 *              答:因为如果有很多文件序列化磁盘同一个数据源中,在读取的时候根据序列化id可以清楚地还原为对应的对象。
 *              问题2:显示指定序列化id和默认生成的序列化id,在改变成员变量时 2则的区别???
 *              答:2.1显示序列化: 假如类Person显示指定序列化id为100 然后进行序列化生成文件为aa.txt到磁盘中,此时修改Person类的成员变量 因为是示显序列化,
 *                              所以即便修改变量值 序列化id也不会发生变化,此时反序列化根据序列化id为100 读取aa.txt文件中的数据仍能还原为没有修改成员变量
 *                              之前的Person对象。
 *                 2.2自动生成的序列化:假如类Person自动生成的序列化id为100 然后进行序列化生成文件为aa.txt到磁盘中,此时修改Person类的成员变量 因为是自动生成
 *                                  的序列化id,所以成员变量一旦修改序列化id会跟着改变假设为150,此时反序列化的id为150不等于100 还原失败报异常。
 *
 *
 *              如果类没有显示定义这个序列化id,它的值是Java运行时环境根据类的内部细节自动生成的,若类的实例变量做了修改,serialVersionUID 可能发生变化,
 *              这样很容易出现问题,故建议显式声明。
 *
 *
 * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性
 *   也必须是可序列化的。(默认情况下,基本数据类型可序列化)
 *
 *
 * 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
 *      在后面进行传输时,一般也不会直接传person 而是转化为json在进行传输,而json本质上是特殊格式的字符串。
 *
 * @author shkstart
 * @create 2019 上午 10:38
 */
public class Person implements Serializable{
    
    

    public static final long serialVersionUID = 475463534532L;

    private String name;
    private int age;
    private int id;
    private Account acct;

    public Person(String name, int age, int id) {
    
    
        this.name = name;
        this.age = age;
        this.id = id;
    }

    public Person(String name, int age, int id, Account acct) {
    
    
        this.name = name;
        this.age = age;
        this.id = id;
        this.acct = acct;
    }

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                ", acct=" + acct +
                '}';
    }

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

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

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    public Person(String name, int age) {
    
    

        this.name = name;
        this.age = age;
    }

    public Person() {
    
    

    }
}

class Account implements Serializable{
    
    
    public static final long serialVersionUID = 4754534532L;
    private double balance;

    @Override
    public String toString() {
    
    
        return "Account{" +
                "balance=" + balance +
                '}';
    }

    public double getBalance() {
    
    
        return balance;
    }

    public void setBalance(double balance) {
    
    
        this.balance = balance;
    }

    public Account(double balance) {
    
    

        this.balance = balance;
    }
}


Insérer la description de l'image ici

2.6 Écriture de flux d'octets

2.6.1 Classe parent abstraite OutputStream

Remarque : Cette classe abstraite est la super-classe de toutes les classes qui représentent les flux d'octets de sortie, et la méthode de construction n'est pas étudiée.

Méthodes couramment utilisées :

void close() 
          关闭此输出流并释放与此流有关的所有系统资源。 
 void flush() 
          刷新此输出流并强制写出所有缓冲的输出字节。 
 void write(byte[] b) 
          将 b.length 个字节从指定的 byte 数组写入此输出流。 
 void write(byte[] b, int off, int len) 
          将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 
abstract  void write(int b) 
          将指定的字节写入此输出流。
......

2.6.2 Sous-classe FileOutputStream (flux de nœuds : flux d'octets de fichier)

Description : Insérez directement dans le fichier et écrivez directement les données du fichier

Créer un objet :

1. FileOutputStream(File file) 
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
2. FileOutputStream(File file, boolean append) 
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
3. FileOutputStream(FileDescriptor fdObj) 
          创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 
4. FileOutputStream(String name) 
          创建一个向具有指定名称的文件中写入数据的输出文件流。 
5. FileOutputStream(String name, boolean append) 
          创建一个向具有指定 name 的文件中写入数据的输出文件流。 

test:

package com.io_file;

import org.junit.Test;

import java.io.*;

/**
 * 测试FileInputStream和FileOutputStream的使用
 *
 * 结论:
 * 1. 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理  (因为字节流处理中文是乱码)
 *     注意这里的乱码指的是 数据到内存层面进行查看有乱码,如果是单纯的复制 硬盘-->硬盘 那么文本文件仍然可以使用字节流处理。
 * 2. 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),只能使用字节流处理   (因为这些非文本文件的基本单位比较小,字符的单位比较大处理不了)
 *
 *
 *
 * @author shkstart
 * @create 2019 下午 2:13
 */
public class FileInputOutputStreamTest {
    
    
    
    /*
    实现对图片的复制操作
     */
    @Test
    public void testFileInputOutputStream()  {
    
    
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
    
    
            //
            File srcFile = new File("爱情与友情.jpg");
            File destFile = new File("爱情与友情2.jpg");

            //
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            //复制的过程
            byte[] buffer = new byte[1024];
            int len;
            while((len = fis.read(buffer)) != -1){
    
    
                fos.write(buffer,0,len);
            }

        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(fos != null){
    
    
                //
                try {
    
    
                    fos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(fis != null){
    
    
                try {
    
    
                    fis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }

    }

    //封装成工具Api的写法:   指定路径下文件(视频,图片,音频等)(文本)的复制     单纯的复制不管是任何类型的文件,字节流都可以使用。
    public void copyFile(String srcPath,String destPath){
    
    
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
    
    
            //
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);

            //
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            //复制的过程
            byte[] buffer = new byte[1024];
            int len;
            //数据的下一个字节,如果达到文件的末尾, -1 
            while((len = fis.read(buffer)) != -1){
    
    
                fos.write(buffer,0,len);
            }

        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(fos != null){
    
    
                //
                try {
    
    
                    fos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(fis != null){
    
    
                try {
    
    
                    fis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }


    }

    @Test
    public void testCopyFile(){
    
    

        long start = System.currentTimeMillis();

        String srcPath = "D:\\aa\\bb\\01-尚硅谷-Java语言基础.avi";
        String destPath = "D:\\aa\\bb\\02-尚硅谷-Java语言基础.avi";


//        String srcPath = "hello.txt";
//        String destPath = "hello3.txt";

        copyFile(srcPath,destPath);


        long end = System.currentTimeMillis();

        System.out.println("复制操作花费的时间为:" + (end - start));//618

    }

}


2.6.3 Sous-classe BufferedOutputStream (flux de traitement : flux d'octets mis en mémoire tampon)

Description : cette classe implémente un flux de sortie mis en mémoire tampon. En configurant ce flux de sortie, l'application peut écrire des octets individuels dans le flux de sortie sous-jacent sans avoir à appeler le système sous-jacent pour chaque écriture d'octet.

Créer un objet :

1. BufferedOutputStream(OutputStream out) 
          创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 
          
2. BufferedOutputStream(OutputStream out, int size) 
          创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。 

Tests · : Consultez les tests en 2.5.3.

2.6.4 Sous-classe ObjectOutputStream (flux de traitement : flux de sortie d'objet/sérialisation)

Omettre, voir 2.5.4 pour plus de détails

2.7 Exercices après les cours sur le flux d'octets

2.7.1 Cryptage et décryptage des images

package com.io_file;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author shkstart
 * @create 2019 下午 4:08
 */
public class PicTest {
    
    

    //图片的加密:把原来的图片加密复制后,因为是异或运算打乱顺序,所以图片大小相同 但是打不开。
    @Test
    public void test1() {
    
    

        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
    
    
             /* fis = new FileInputStream("爱情与友情.jpg");等价于 fis = new FileInputStream(new File("爱情与友情.jpg"));
             *  FileInputStream这个直接可以写路径的构造器,实际上里面的路径还是被new File()包裹在里面了。
             *
             * */
            fis = new FileInputStream("爱情与友情.jpg");//真实存在
            fos = new FileOutputStream("爱情与友情secret.jpg");//会自动生成  加密后的图片:因为是异或运算打乱顺序,所以图片大小相同 但是打不开。

            byte[] buffer = new byte[20];
            int len;
            while ((len = fis.read(buffer)) != -1) {
    
    
                //字节数组进行修改
                //错误的 增强for循环,这样写是把buffer赋给新的变量了,数组里面的值没有改变。
                //            for(byte b : buffer){
    
    
                //                b = (byte) (b ^ 5);    //b ^ 5:异或运算,打乱里面的字节。
                //            }
                //正确的
                for (int i = 0; i < len; i++) {
    
    
                    buffer[i] = (byte) (buffer[i] ^ 5); //buffer为byte类型,5位int类型,计算结果和大类型保持一致,所以需要强转。
                }


                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (fos != null) {
    
    
                try {
    
    
                    fos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            if (fis != null) {
    
    
                try {
    
    
                    fis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }


    }


    //图片的解密:  原理:异或运算,m^n^n=m
    @Test
    public void test2() {
    
    

        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
    
    
            fis = new FileInputStream("爱情与友情secret.jpg");
            fos = new FileOutputStream("爱情与友情4.jpg");

            byte[] buffer = new byte[20];
            int len;
            while ((len = fis.read(buffer)) != -1) {
    
    
                //字节数组进行修改
                //错误的
                //            for(byte b : buffer){
    
    
                //                b = (byte) (b ^ 5);
                //            }
                //正确的
                for (int i = 0; i < len; i++) {
    
    
                    buffer[i] = (byte) (buffer[i] ^ 5);
                }

                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (fos != null) {
    
    
                try {
    
    
                    fos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            if (fis != null) {
    
    
                try {
    
    
                    fis.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }


    }
}


2.7.2 Obtenir le nombre d'occurrences de caractères dans le texte et écrire les données dans le fichier

package com.io_file;

import org.junit.Test;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;


/**
 * 获取文本上字符出现的次数,把数据写入文件
 *
 * 思路:
 * 1.遍历文本每一个字符
 * 2.字符出现的次数存在Map中
 *
 * Map<Character,Integer> map = new HashMap<Character,Integer>();
 * map.put('a',18);
 * map.put('你',2);
 *
 * 3.把map中的数据写入文件
 *
 * @author shkstart
 * @create 2019 下午 3:47
 */
public class WordCount {
    
    
    /*
    说明:如果使用单元测试,文件相对路径为当前module
          如果使用main()测试,文件相对路径为当前工程
     */
    @Test
    public void testWordCount() {
    
    
        FileReader fr = null;
        BufferedWriter bw = null;
        try {
    
    
            //1.创建Map集合  k:存放字符 v:存放次数
            Map<Character, Integer> map = new HashMap<Character, Integer>();

            //2.遍历每一个字符,每一个字符出现的次数放到map中
            fr = new FileReader("dbcp.txt");
            int c = 0;
            while ((c = fr.read()) != -1) {
    
    
                //int 还原 char
                char ch = (char) c;
                // 判断char是否在map中第一次出现
                if (map.get(ch) == null) {
    
    
                    map.put(ch, 1);
                } else {
    
    
                    map.put(ch, map.get(ch) + 1);//有值在原有的次数加1
                }
            }

            //3.把map中数据存在文件count.txt,因为上面都是放在内存中不保险。
            //3.1 创建Writer
            bw = new BufferedWriter(new FileWriter("wordcount.txt"));

            //3.2 遍历map,再写入数据
            Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();
            for (Map.Entry<Character, Integer> entry : entrySet) {
    
    
                switch (entry.getKey()) {
    
    
                    case ' ':
                        bw.write("空格=" + entry.getValue());
                        break;
                    case '\t'://\t表示tab 键字符
                        bw.write("tab键=" + entry.getValue());
                        break;
                    case '\r'://
                        bw.write("回车=" + entry.getValue());
                        break;
                    case '\n'://
                        bw.write("换行=" + entry.getValue());
                        break;
                    default:
                        bw.write(entry.getKey() + "=" + entry.getValue());
                        break;
                }
                bw.newLine();//写完之后换行
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //4.关流
            if (fr != null) {
    
    
                try {
    
    
                    fr.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            if (bw != null) {
    
    
                try {
    
    
                    bw.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }

    }
}


3 I流–02

3.1 Lecture du flux de caractères

Description : Couramment utilisé pour traiter des données en texte brut.

3.1.1 Classe parent abstraite du lecteur

Remarque : La classe parent abstraite est utilisée pour lire les flux de caractères. Puisqu'il s'agit d'une classe abstraite, elle ne peut pas créer de nouveaux objets, nous n'apprenons donc que les méthodes couramment utilisées et n'étudions pas les méthodes de construction.

Méthodes couramment utilisées :

int read() 
          读取单个字符。 一次读取一个字符效率低。
int read(char[] cbuf)     注意是char
          将字符读入数组。 
abstract  int read(char[] cbuf, int off, int len) 
          将字符读入数组的某一部分。 5,3 数组长度为5,但每次最多读进去3个。
int read(CharBuffer target) 
          试图将字符读入指定的字符缓冲区。 
abstract  void close() 
          关闭该流并释放与之关联的所有资源。
......  

3.1.2 Sous-classe FileReader (flux de nœuds : flux de caractères de fichier)

Description : Classe pratique pour lire des fichiers de personnages.

Méthode de construction :

1. FileReader(String fileName) //参数为文件的路径
          在给定从中读取数据的文件名的情况下创建一个新 FileReader2. FileReader(File file) 
          在给定从中读取数据的 File 的情况下创建一个新 FileReader3. FileReader(FileDescriptor fd) 
          在给定从中读取数据 的 FileDescriptor 的情况下创建一个新 FileReader

Méthodes couramment utilisées : il n'existe pas de méthodes uniques, ce sont toutes des méthodes héritées des classes java.lang.Object, java.io.Reader et java.io.InputStreamReader.

test

package com.io_file;

import org.junit.Test;

import java.io.*;

/**
 *
 * 一、流的分类:
 * 1.操作数据单位:字节流、字符流
 * 2.数据的流向:输入流、输出流
 * 3.流的角色:节点流、处理流
 *
 * 二、流的体系结构
 * 抽象基类         节点流(或文件流)                               缓冲流(处理流的一种)
 * InputStream     FileInputStream   (read(byte[] buffer))        BufferedInputStream (read(byte[] buffer))
 * OutputStream    FileOutputStream  (write(byte[] buffer,0,len)  BufferedOutputStream (write(byte[] buffer,0,len) / flush()
 * Reader          FileReader (read(char[] cbuf))                 BufferedReader (read(char[] cbuf) / readLine())
 * Writer          FileWriter (write(char[] cbuf,0,len)           BufferedWriter (write(char[] cbuf,0,len) / flush()
 *
 *
 *
 * @author shkstart
 * @create 2019 上午 10:40
 */
public class FileReaderWriterTest {
    
    

    public static void main(String[] args) {
    
    
        File file = new File("hello.txt");//相对路径使用main方法,相较于当前工程
        System.out.println(file.getAbsolutePath());//E:\idea-workspace\hello.txt

        File file1 = new File("day09\\hello.txt");//相对路径使用main方法,从当前module下开始
        System.out.println(file1.getAbsolutePath());//E:\idea-workspace\day09\hello.txt
    }

    /*
    将idea中io项目下的hello.txt文件内容读入程序中,并输出到控制台

    说明点:
    1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
    2. 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理。如果用throws处理异常,一旦程序出现异常
       把异常抛出后 后续的代码不会执行。如在第二出 出现异常,那么接些来的代码 close关闭资源不会执行,资源浪费 显然不合适。
    3. 读入的文件一定要存在,否则就会报FileNotFoundException。

     */
    @Test
    public void testFileReader(){
    
    
        FileReader fr = null;
        try {
    
    
            //1.实例化File类的对象,指明要操作的文件
            File file = new File("hello.txt");//相较于当前Module
            //2.提供具体的流
            fr = new FileReader(file);//产生异常地方一:创建对象会报抛异常

            //3.数据的读入
            //read():返回读入的一个字符。如果达到文件末尾,返回-1    一次读取一个字符效率低。
            //方式一:
//        int data = fr.read();//字符为char类型,这里用int类型接收是因为对应的编码 97...这样存的数据。
//        while(data != -1){
    
    
//            System.out.print((char)data);//读第一个,如果不是-1 则进行读取,并且转化为char类型的字符接收 不然输出的是数字。输出不换行
//            data = fr.read();//开始读第二个,并把值赋值给data循环进行判断第二个是否为-1 ......
//        }

            //方式二:语法上针对于方式一的修改
            int data;
            while((data = fr.read()) != -1){
    
    //产生异常地方二:read方法会报异常
                System.out.print((char)data);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //4.流的关闭操作:对于物理连接如:数据库连接,输入输出流,Socket连接无能为力,这些资源需要手动关闭。
//            try {
    
    
//                if(fr != null)//因为fr写在外面声明为null,如果在第一处出现异常后创建对象不成功为null,在走到finally中会出现空指针异常,所以进行if判断。
//                    fr.close();//产生异常地方三:close会报异常,而关闭资源通常在finally中 所以单独的再用一个try-catch,不需要finally了。
//            } catch (IOException e) {
    
    
//                e.printStackTrace();
//            }
            //或
            if(fr != null){
    
    
                try {
    
    
                    fr.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }

    }

    //对read()操作升级:使用read的重载方法
    @Test
    public void testFileReader1()  {
    
    
        FileReader fr = null;
        try {
    
    
            //1.File类的实例化
            File file = new File("hello.txt");

            //2.FileReader流的实例化
            fr = new FileReader(file);

            //3.读入的操作
            //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
            char[] cbuf = new char[5];//每次读取指定个数的字符,5个
            int len;
            while((len = fr.read(cbuf)) != -1){
    
    //文件数据:helloworld123,每次读取5个到数组中,最后一次读入3个。
                //方式一:
                //错误的写法
//                for(int i = 0;i < cbuf.length;i++){
    
    
//                    System.out.print(cbuf[i]);helloworld123ld  因为数组是覆盖操作,第一次[h,e,l,l,o] 第二次[w,o,r,l,d] 第三次[1,2,3,l,d]
                                                //每次新读取的数据覆盖原来的数组值,最后一次只有三个数据,遍历输出数组把另外2个数据也输出了。
//                }
                //正确的写法
//                for(int i = 0;i < len;i++){ //每次读进去几个就遍历几个
//                    System.out.print(cbuf[i]);
//                }
                //方式二:
                //错误的写法,对应着方式一的错误的写法 任然保留ld
//                String str = new String(cbuf);//char转化为String类型
//                System.out.print(str);
                //正确的写法
                String str = new String(cbuf,0,len);//从头开始取值,每次只取len个
                System.out.print(str);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(fr != null){
    
    
                //4.资源的关闭
                try {
    
    
                    fr.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }

    }
 }

Insérer la description de l'image ici

3.1.3 Sous-classe BufferedReader (flux de traitement : flux de caractères tamponné)

Description : lit le texte à partir d'un flux d'entrée de caractères et met en mémoire tampon des caractères individuels pour obtenir une lecture efficace des caractères, des tableaux et des lignes. La taille du tampon peut être spécifiée ou la taille par défaut peut être utilisée. Dans la plupart des cas, la valeur par défaut est suffisamment élevée.

Créer un objet :

1. BufferedReader(Reader in) 
          创建一个使用默认大小输入缓冲区的缓冲字符输入流。 
2. BufferedReader(Reader in, int sz) 
          创建一个使用指定大小输入缓冲区的缓冲字符输入流。 

test:

package com.bf_file;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之一:缓冲流的使用
 *
 * 1.缓冲流:
 * BufferedInputStream
 * BufferedOutputStream
 * BufferedReader
 * BufferedWriter
 *
 * 2.作用:提供流的读取、写入的速度
 *   提高读写速度的原因:内部提供了一个缓冲区 (把一定量的数据读入到缓冲区后 一次性写出 写入)
 *
 * 3. 处理流,就是“套接”在已有的流的基础上,处理流不能直接作用在文件上。
 *
 * @author shkstart
 * @create 2019 下午 2:44
 */
public class BufferedTest {
    
    


    
    /*
    使用BufferedReader和BufferedWriter实现文本文件的复制

     */
    @Test
    public void testBufferedReaderBufferedWriter(){
    
    
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
    
    
            //创建文件和相应的流
            br = new BufferedReader(new FileReader(new File("dbcp.txt")));
            bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

            //读写操作
            //方式一:使用char[]数组
//            char[] cbuf = new char[1024];
//            int len;
//            while((len = br.read(cbuf)) != -1){
    
    
//                bw.write(cbuf,0,len);
//    //            bw.flush();
//            }

            //方式二:使用String   readLine()一次读一行,当为null时代表后面没数据。
            String data;
            while((data = br.readLine()) != null){
    
    
                
                //bw.write(data); //data中不包含换行符,默认不包含换行符 都写在一行中。

                //方法一加上换行符:
//                bw.write(data + "\n");
                //方法二加上换行符:
                bw.write(data);//data中不包含换行符
                bw.newLine();//提供换行的操作

            }


        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //关闭资源
            if(bw != null){
    
    

                try {
    
    
                    bw.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(br != null){
    
    
                try {
    
    
                    br.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }

    }

}


3.1.4 Sous-classe InputStreamReader (flux de traitement : flux de conversion)

illustrer:

  1. Utilisé comme pont pour convertir le flux d'octets en flux de caractères.
  2. Utilisé pour résoudre le problème de la lecture et de l'écriture de flux de caractères tronqués.

Méthode de construction :

1. InputStreamReader(InputStream in) 
          创建一个使用默认字符集的 InputStreamReader2. InputStreamReader(InputStream in, Charset cs) 
          创建使用给定字符集的 InputStreamReader3. InputStreamReader(InputStream in, CharsetDecoder dec) 
          创建使用给定字符集解码器的 InputStreamReader4. InputStreamReader(InputStream in, String charsetName) 
          创建使用指定字符集的 InputStreamReader

Méthodes couramment utilisées :

 void close() 
          关闭该流并释放与之关联的所有资源。 
 String getEncoding() 
          返回此流使用的字符编码的名称。 
 int read() 
          读取单个字符。 
 int read(char[] cbuf, int offset, int length) 
          将字符读入数组中的某一部分。 
 boolean ready() 
          判断此流是否已经准备好用于读取。 

test:

package com.io_file;


import org.junit.Test;

import java.io.*;

/**
 * 处理流之二:转换流的使用
 * 1.转换流:属于字符流
 *   InputStreamReader:将一个字节的输入流转换为字符的输入流
 *   OutputStreamWriter:将一个字符的输出流转换为字节的输出流
 *
 * 2.作用:提供字节流与字符流之间的转换
 *
 * 3. 解码:字节、字节数组  --->字符数组、字符串   (看不懂--->看得懂)
 *    编码:字符数组、字符串 ---> 字节、字节数组   (看的懂--->看不懂)
 *
 *
 * 4.字符集
 *ASCII:美国标准信息交换码。
 用一个字节的7位可以表示。
 ISO8859-1:拉丁码表。欧洲码表
 用一个字节的8位表示。
 GB2312:中国的中文编码表。最多两个字节编码所有字符
 GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
 Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
 UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

 *
 *
 * @author shkstart
 * @create 2019 下午 4:25
 */
public class InputStreamReaderTest {
    
    
    /* 把指定的文件读到内存中,并输出到控制台:
    此时处理异常的话,仍然应该使用try-catch-finally,这里是偷懒写法用throws。
    InputStreamReader的使用,实现字节的输入流到字符的输入流的转换
     */
    @Test
    public void test1() throws IOException {
    
    

        FileInputStream fis = new FileInputStream("dbcp.txt");
//        InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集,跟你用的idea中设置的编码保持一致。
        //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集(即:读取时的编码应该和读取的文件设置的编码保持一致)
        InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//使用指定的的字符集 和读取的文件字符集编码保持一致。

        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf)) != -1){
    
    
            String str = new String(cbuf,0,len);
            System.out.print(str);
        }

        isr.close();

    }

    /*
    此时处理异常的话,仍然应该使用try-catch-finally,这里是偷懒写法用throws。

    综合使用InputStreamReader和OutputStreamWriter
    过程:硬盘中的UTF8.txt--->文件字节流--->字节输入流--->字符输入转换流(UTF-8,和保存文件的编码一致)---->内存
         --->字符输出转换流(GBK,选择想要编码的字符)--->字节输出流--->文件输出流---硬盘中的GBK.txt文件
     */
    @Test
    public void test2() throws Exception {
    
    
        //1.造文件、造流
        File file1 = new File("dbcp.txt");
        File file2 = new File("dbcp_gbk.txt");//查看的话只能用 gbk的方式查看才不是乱码。

        FileInputStream fis = new FileInputStream(file1);
        FileOutputStream fos = new FileOutputStream(file2);

        InputStreamReader isr = new InputStreamReader(fis,"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");

        //2.读写过程
        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf)) != -1){
    
    
            osw.write(cbuf,0,len);
        }

        //3.关闭资源
        isr.close();
        osw.close();


    }


}


3.1.5 Tableau commun de codage des caractères

L'origine de la table de codage : les ordinateurs ne peuvent reconnaître que les données binaires, et la première origine est constituée de signaux électriques. Afin de faciliter l’application des ordinateurs, il peut reconnaître les caractères de différents pays. Les personnages de chaque pays sont représentés par des chiffres et correspondent un à un pour former un tableau. C'est la table de codage.

Le phénomène des caractères tronqués fait principalement référence aux caractères chinois tronqués :

  1. Comment est-ce arrivé ? C'est parce que la table de codes que vous avez utilisée lors de l'enregistrement n'est pas cohérente avec la table de codes que vous avez utilisée lors de l'ouverture.
  2. Solution : enregistrez et ouvrez simplement la table Unicode.
codage Introduction illustrer Plage de codage Octets
ASCII Code américain standard d'échange d'informations Anglais, ponctuation, instructions de base 0 à 127 Il peut être représenté par 7 bits d'un octet. (un seul octet)
ISO8859-1 Table de codes latins, table de codes européens Compatible avec ASCII et étend les caractères d'Europe occidentale sur cette base 128 à 255 Représenté par 8 bits d'un octet. (un seul octet)
GB2312 Table de codage chinoise de la Chine Compatible ASCII Jusqu'à deux octets codant tous les caractères
GBK Mise à niveau de la table de codage chinoise de la Chine Compatible avec ASCII, intégrant davantage de caractères et de symboles chinois Maximum 65535 Jusqu'à deux octets codent tous les caractères. (L'anglais utilise un seul octet, le chinois utilise deux octets)
Unicode code standard international Le code standard international combine tous les caractères actuellement utilisés par les humains. Attribuez un code de caractère unique à chaque caractère, compatible ASCII. Plus d'un million de bits de codage, divisés en tables de caractères communs, tables de caractères rares, etc. Nous utilisons simplement les tables communes Tout le texte est représenté par deux octets. (Tous les caractères de la table de caractères couramment utilisés utilisent des doubles octets)
UTF-8 Code standard international optimisé Afin de résoudre le problème du doublement de la taille des octets des caractères anglais Unicode, un format de codage de longueur variable est proposé, compatible avec l'ASCII. Un caractère peut être représenté par 1 à 4 octets. (Anglais à un octet, certains caractères à deux octets, chinois à trois octets, certains symboles spéciaux à quatre octets)

Problèmes d'encodage Unicode :
Insérer la description de l'image ici
Insérer la description de l'image ici

Précautions:

  1. Le codage ANSI fait généralement référence au codage par défaut de la plate-forme. Par exemple, le système d'exploitation anglais est ISO-8859-1 et le système chinois est GBK.

  2. Le jeu de caractères Unicode définit uniquement un nombre défini et unique de caractères. Le codage Unicode est un terme général désignant des schémas de codage spécifiques tels que UTF-8, UCS-2/UTF-16, etc., et n'est pas un schéma de codage spécifique.

Inspiration pour une étude future :

  1. Client/navigateur <--------> Backend (java, GO, Python, Nod.js, php) <--------> Base de données
  2. Il est nécessaire que le jeu de caractères utilisé avant et après soit unifié : UTF-8.

3.2 Écriture du flux de caractères

3.2.1 Classe parent abstraite Writer

Remarque : La classe abstraite pour l'écriture de flux de caractères n'étudie pas non plus la méthode de construction.

Méthodes couramment utilisées :

void write(char[] cbuf)    
          写入字符数组。  char
abstract  void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 
void write(int c) 
          写入单个字符。 
void write(String str) 
          写入字符串。 
void write(String str, int off, int len) 
          写入字符串的某一部分。
abstract  void close() 
          关闭此流,但要先刷新它。
......

3.2.2 Sous-classe FileWriter (flux de nœuds : flux de caractères de fichier)

Description : Classe pratique pour écrire des fichiers de caractères.

Créer un objet :

1. FileWriter(File file) 
          根据给定的 File 对象构造一个 FileWriter 对象。 
2. FileWriter(File file, boolean append) 
          根据给定的 File 对象构造一个 FileWriter 对象。 
3. FileWriter(FileDescriptor fd) 
          构造与某个文件描述符相关联的 FileWriter 对象。 
4. FileWriter(String fileName) 
          根据给定的文件名构造一个 FileWriter 对象。 
5. FileWriter(String fileName, boolean append) 
          根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 

Méthodes couramment utilisées : il n'existe pas de méthodes uniques, ce sont toutes des méthodes héritées des classes java.lang.Object, java.io.Writer et java.io.OutputStreamWriter.

test

package com.io_file;

import org.junit.Test;

import java.io.*;

/**
 *
 * 一、流的分类:
 * 1.操作数据单位:字节流、字符流
 * 2.数据的流向:输入流、输出流
 * 3.流的角色:节点流、处理流
 *
 * 二、流的体系结构
 * 抽象基类         节点流(或文件流)                               缓冲流(处理流的一种)
 * InputStream     FileInputStream   (read(byte[] buffer))        BufferedInputStream (read(byte[] buffer))
 * OutputStream    FileOutputStream  (write(byte[] buffer,0,len)  BufferedOutputStream (write(byte[] buffer,0,len) / flush()
 * Reader          FileReader (read(char[] cbuf))                 BufferedReader (read(char[] cbuf) / readLine())
 * Writer          FileWriter (write(char[] cbuf,0,len)           BufferedWriter (write(char[] cbuf,0,len) / flush()
 *
 *
 *
 * @author shkstart
 * @create 2019 上午 10:40
 */
public class FileReaderWriterTest {
    
    
    
    /*
    从内存中写出数据到硬盘的文件里。

    说明:
    1. 输出操作,对应的File可以不存在的。并不会报异常
    2.
         File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
         File对应的硬盘中的文件如果存在:
                如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原有文件的覆盖
                如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容

     */
    @Test
    public void testFileWriter() {
    
    //上去写代码可以先throws抛出异常,写完代码后再更改为try-catch这样代码更容易理解。
        FileWriter fw = null;
        try {
    
    
            //1.提供File类的对象,指明写出到的文件
            File file = new File("hello1.txt");

            //2.提供FileWriter的对象,用于数据的写出
            fw = new FileWriter(file,false);

            //3.写出的操作
            fw.write("I have a dream!\n");
            fw.write("you need to have a dream!");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //4.流资源的关闭
            if(fw != null){
    
    

                try {
    
    
                    fw.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }


    }
    //把硬盘hello.txt中的数据 读入到内存中,在把内存中的数据写出到硬盘hello2.txt中。
    @Test
    public void testFileReaderFileWriter() {
    
    
        FileReader fr = null;
        FileWriter fw = null;
        try {
    
    
            //1.创建File类的对象,指明读入和写出的文件
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");

            //不能使用字符流来处理图片等字节数据
//            File srcFile = new File("爱情与友情.jpg");
//            File destFile = new File("爱情与友情1.jpg");


            //2.创建输入流和输出流的对象
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);


            //3.数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;//记录每次读入到cbuf数组中的字符的个数
            while((len = fr.read(cbuf)) != -1){
    
    
                //每次写出len个字符,每次读几个字符 写几个字符
                fw.write(cbuf,0,len);

            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //4.关闭流资源   2个流的资源关闭先后顺序没有要求。
            //方式一:
//            try {
    
    
//                if(fw != null)
//                    fw.close();
//            } catch (IOException e) {
    
    
//                e.printStackTrace();
//            }finally{
    
    
//                try {
    
    
//                    if(fr != null)
//                        fr.close();
//                } catch (IOException e) {
    
    
//                    e.printStackTrace();
//                }
//            }
            //方式二:
            try {
    
    
                if(fw != null)
                    fw.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }

            try {
    
    
                if(fr != null)
                    fr.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }

        }

    }

}


3.2.3 Sous-classe BufferedWriter (flux de traitement : flux tamponné)

Description : Écrit du texte dans un flux de sortie de caractères, en mettant en mémoire tampon les caractères individuels, permettant ainsi une écriture efficace de caractères uniques, de tableaux et de chaînes. Vous pouvez spécifier la taille du tampon ou accepter la taille par défaut. Dans la plupart des cas, la valeur par défaut est suffisamment élevée.

Créer un objet :

1. BufferedWriter(Writer out) 
          创建一个使用默认大小输出缓冲区的缓冲字符输出流。 
2. BufferedWriter(Writer out, int sz) 
          创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 

Test : Voir 3.1.3 pour plus de détails

3.2.4 Sous-classe OutputStreamWriter (flux de traitement : flux de conversion)

illustrer:

  1. Utilisé comme pont pour convertir les flux de caractères en flux d'octets.
  2. Utilisé pour résoudre le problème des caractères tronqués écrits dans les flux de caractères.

Méthode de construction :

1. OutputStreamWriter(OutputStream out) 
          创建使用默认字符编码的 OutputStreamWriter2. OutputStreamWriter(OutputStream out, Charset cs) 
          创建使用给定字符集的 OutputStreamWriter3. OutputStreamWriter(OutputStream out, CharsetEncoder enc) 
          创建使用给定字符集编码器的 OutputStreamWriter4. OutputStreamWriter(OutputStream out, String charsetName) 
          创建使用指定字符集的 OutputStreamWriter

Méthodes couramment utilisées :

 void close() 
          关闭此流,但要先刷新它。 
 void flush() 
          刷新该流的缓冲。 
 String getEncoding() 
          返回此流使用的字符编码的名称。 
 void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 
 void write(int c) 
          写入单个字符。 
 void write(String str, int off, int len) 
          写入字符串的某一部分。 

Test : Voir 3.1.4 pour plus de détails

3.3 Utilisation d'autres flux (peu couramment utilisés)

3.3.1 Flux d'entrée et de sortie standards (System.in, System.out)

illustrer:

  1. System.in et System.out représentent respectivement les périphériques d'entrée et de sortie standard du système.
  2. Le périphérique d'entrée par défaut est : clavier, le périphérique de sortie est : moniteur
  3. Le type de System.in est InputStream
  4. Le type de System.out est PrintStream, qui est une sous-classe de FilterOutputStream, une sous-classe de OutputStream.
  5. Redirection : modifiez le périphérique par défaut via les méthodes setIn et setOut de la classe System. ( Par exemple, les données imprimées sur la console sont modifiées pour être imprimées dans le fichier, voir le flux d'impression pour plus de détails )
    public static void setIn(InputStream in) Le paramètre est InputStream byte input stream
    public static void setOut(PrintStream out) Le paramètre est le flux d'impression d'octets PrintStream

Relation d'héritage :
Insérer la description de l'image ici
Remarque : Si vous utilisez le clavier pour saisir des caractères sur la console (entrée du scanner, flux de sortie du flux d'entrée standard System.in System.out), les données peuvent ne pas être saisies.

  1. Si cela est écrit dans la méthode principale de l'idée, les données peuvent être saisies sur la console, mais les données ne peuvent pas être saisies sur la console en utilisant la méthode de test.
  2. Dans Eclipse, les données peuvent être saisies, qu'elles soient écrites dans la méthode principale ou dans la méthode de test.
    Insérer la description de l'image ici

pratique:

package com.io_file;


import org.junit.Test;

import java.io.*;

/**
 * 其他流的使用
 * 1.标准的输入、输出流
 * 2.打印流
 * 3.数据流
 *
 * @author shkstart
 * @create 2019 下午 6:11
 */
public class OtherStreamTest {
    
    

    /*
    1.标准的输入、输出流
    1.1
    System.in:标准的输入流,默认从键盘输入
    System.out:标准的输出流,默认从控制台输出
    1.2
    System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流。

    1.3练习:
    从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,
    直至当输入“e”或者“exit”时,退出程序。

    方法一:使用Scanner实现,调用next()返回一个字符串
    方法二:使用System.in实现。System.in (返回值为InputStream类型,字节流转化为字符流中间需要转换流)  --->  转换流 ---> BufferedReader的readLine()

     */
    public static void main(String[] args) {
    
    
        BufferedReader br = null;
        try {
    
    
            InputStreamReader isr = new InputStreamReader(System.in);
            br = new BufferedReader(isr);

            while (true) {
    
    
                System.out.println("请输入字符串:");
                String data = br.readLine();//一次读一行数据
                /*
                * equalsIgnoreCase() :忽略大小写进行相等比较
                * equals():不忽略大小写进相等判断。
                *
                * 这种方式不好 有可能出现空指针的问题
                * if (data.equalsIgnoreCase("e") || data.equalsIgnoreCase("exit")){
                *
                * }
                * */
                if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
    
    //可以更好的避免空指针的出现
                    System.out.println("程序结束");
                    break;
                }

                String upperCase = data.toUpperCase();//转化为大写
                System.out.println(upperCase);

            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (br != null) {
    
    
                try {
    
    
                    br.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }
    }

}


3.3.2 Flux d'impression (flux avancé : PrintStream et PrintWriter)

illustrer:

  1. Il convertit le format de données des types de données de base en sortie de chaîne et peut générer différents types de données.
  2. Flux d'impression : PrintStream et PrintWriter
  3. Fournit une série de méthodes print() et println() surchargées pour la sortie de plusieurs types de données
  4. La sortie de PrintStream et PrintWriter ne renvoie pas d'exception IOException
  5. PrintStream et PrintWriter ont une fonction de rinçage automatique
  6. Tous les caractères imprimés par PrintStream sont convertis en octets en utilisant le codage de caractères par défaut de la plateforme.
    Dans les situations où vous devez écrire des caractères au lieu d'octets, vous devez utiliser la classe PrintWriter.
  7. System.out renvoie une instance de PrintStream

Relation successorale :

  1. Flux de sortie d'impression d'octets
    Insérer la description de l'image ici
  2. Test du flux de sortie d'impression de caractères
    Insérer la description de l'image ici
     :
package com.io_file;


import org.junit.Test;

import java.io.*;

/**
 * 其他流的使用
 * 1.标准的输入、输出流
 * 2.打印流
 * 3.数据流
 *
 * @author shkstart
 * @create 2019 下午 6:11
 */
public class OtherStreamTest {
    
    



    /*
    2. 打印流:PrintStream 和PrintWriter  可以输出各种各样类型的数据。

    2.1 提供了一系列重载的print() 和 println()
    2.2 练习:输出0-255数字对应的ASCII字符(十进制),到对应的文件中

      什么需求下使用:把输出的数据 有打印到控制台 改为 打印到文件中即可使用。


     */

    @Test
    public void test2() {
    
    
        PrintStream ps = null;
        try {
    
    
            //注意这个文件路径:开始时,文件目录(磁盘写的小写io目录,这里写的大写IO,目录在windows中不区分大小写的)需要存在,文件(text.txt)可以不存在会帮你自动造。
            FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
            // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
            ps = new PrintStream(fos, true);//true代表自动flush操作
            if (ps != null) {
    
    
                System.setOut(ps);// 把标准输出流(控制台输出)改成文件   setOut():重新指定一个打印流对象ps
            }


            for (int i = 0; i <= 255; i++) {
    
     // 输出ASCII字符:a-->97(十进制)  A-->65(十进制)  0-->48(十进制)
                System.out.print((char) i);
                if (i % 50 == 0) {
    
     // 每50个数据一行
                    System.out.println(); // 换行
                }
            }


        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (ps != null) {
    
    
                ps.close();
            }
        }

    }

    

}


Insérer la description de l'image ici

3.3.3 Flux de données (flux avancés : DataInputStream et DataOutputStream)

Description :
Insérer la description de l'image ici
Lien de succession :

  1. flux d'entrée de données
    Insérer la description de l'image ici
  2. Test du flux de sortie de données
    Insérer la description de l'image ici
     :
package com.io_file;


import org.junit.Test;

import java.io.*;

/**
 * 其他流的使用
 * 1.标准的输入、输出流
 * 2.打印流
 * 3.数据流
 *
 * @author shkstart
 * @create 2019 下午 6:11
 */
public class OtherStreamTest {
    
    


    /*
    3. 数据流
    3.1 DataInputStream 和 DataOutputStream
    3.2 作用:用于读取或写出基本数据类型的变量或字符串

    练习:将内存中的字符串、基本数据类型的变量写出到文件中。但是不能读写对象,读写对象需要使用对象输入流,对象输出流(反序列化,序列化 )。

    注意:处理异常的话,仍然应该使用try-catch-finally.
     */

    //先写才能读:先把内存中的数据写到文件中,这个文件是二进制数据不能直接打开读,想要读取只能把数据读到内存中 输出到控制台查看
    @Test
    public void test3() throws IOException {
    
    
        //1.
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
        //2.
        dos.writeUTF("刘建辰");
        dos.flush();//刷新操作,将内存中的数据写入文件(一旦执行刷新操作,就会把内存中已有的数据刷新到文件中)
        dos.writeInt(23);
        dos.flush();
        dos.writeBoolean(true);
        dos.flush();
        //3.
        dos.close();


    }
    /*
    将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。

    注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!

     */
    @Test
    public void test4() throws IOException {
    
    
        //1.
        DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
        //2.
        String name = dis.readUTF();
        int age = dis.readInt();
        boolean isMale = dis.readBoolean();

        System.out.println("name = " + name);
        System.out.println("age = " + age);
        System.out.println("isMale = " + isMale);

        //3.
        dis.close();

    }

}


3.3.4 Flux de fichiers à accès aléatoire (RandomAccessFile)

Aperçu :
Insérer la description de l'image ici
Structure de succession :
Insérer la description de l'image ici

Méthode de construction :

                   参数1:文件名      参数2:详情查看测试代码
1.RandomAccessFile(File file, String mode) 
          创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 
          
                    参数1:路径      参数2
2.RandomAccessFile(String name, String mode) 
          创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 

test:

package Random_File;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * RandomAccessFile的使用
 * 1.RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
 * 2.RandomAccessFile既可以作为一个输入流,又可以作为一个输出流。但是创建对象还是需要2个,一个用来读 一个用来写
 *
 * 3.如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。
 *   如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖)
 *
 * 4. 可以通过相关的操作,实现RandomAccessFile“插入”数据的效果
 *
 * @author shkstart
 * @create 2019 上午 11:18
 */
public class RandomAccessFileTest {
    
    


    //测试图片的复制
    @Test
    public void test1() {
    
    

        RandomAccessFile raf1 = null;
        RandomAccessFile raf2 = null;
        try {
    
    
            //1.

           /* 参数2mode的取值:
            * r: 以只读方式打开  写r只能读,不能写
            * rw:打开以便读取和写入
            * rwd:打开以便读取和写入;同步文件内容的更新
            * rws:打开以便读取和写入;同步文件内容和元数据的更新
            *
            * 注意事项:
            *   如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。(只能用于读,并且读的时候如果文件不存在不会创建)
            *   如果模式为rw读写。如果文件不存在则会去创建文件,如果存在则不会创建。(如果文件不存在读、写都会创建文件,只不过文件里面什么也没有。如果在写的时候文件
            *                                                                                                               存在则会覆盖)
            *
            * */
            raf1 = new RandomAccessFile(new File("爱情与友情aa.jpg"),"rw");//作为输入
            raf2 = new RandomAccessFile(new File("爱情与友情aa1.jpg"),"rw");//作为输出
            //2.
            byte[] buffer = new byte[1024];
            int len;
            while((len = raf1.read(buffer)) != -1){
    
    
                raf2.write(buffer,0,len);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //3.
            if(raf1 != null){
    
    
                try {
    
    
                    raf1.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            if(raf2 != null){
    
    
                try {
    
    
                    raf2.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }
    }
    //测试覆盖效果:创建文件hello.txt,向此文件写出内容,看是否被覆盖。
    /*注意不是整个文件的覆盖,而是从头开始(指针为0),对里面的内容进行覆盖。
     * 如:原文件hello.txt:         hello123bbb
     *    写出数据为xyz,则文件内容变为:xyzlo123bbb
     * 此指针可以调整,如raf1.seek(3)将指针调到角标为3的位置,也就是说从角标为3(第4个数据)开始覆盖
     * 如:原文件hello.txt:         hello123bbb
     *    写出数据为xyz,则文件内容变为:helxyz23bbb
     *
     * 思考如何在hello123bbb后面进行追加数据:
     *   需要把指针指定到文件末尾:在File类中提供了一个方法length()可以获取文件的长度
     *    如:hello123bbb 的长度为11,下标从0开始,最后一个b为10,那么末尾为下标11 恰好是文件的长度。
     */
    @Test
    public void test2() throws IOException {
    
    

        RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");

        raf1.seek(3);//将指针调到角标为3的位置
        raf1.write("xyz".getBytes());//getBytes():使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。

        raf1.close();

    }
    /*
     * 使用RandomAccessFile实现数据的插入效果:它本身只能是覆盖那么如何实现插入效果呢???
     * 如:原文件hello.txt:         hello123bbb
     *    现在想要在123后面插入ccc
     * 思路:先把指针调到b的位置上,把bbb取出来 在复制的时候指针也会向后移动,此时指针在最后一个位置上,
     *      再把指针调到3的后面,在进行追加ccc,在把复制的数据拼接到后面。 hello123cccbbb
     *
     */
    @Test
    public void test3() throws IOException {
    
    

        RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");

        raf1.seek(8);//将指针调到角标为3的位置
        //保存指针3后面的所有数据到StringBuilder中  指定长度创建String
        StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());
        byte[] buffer = new byte[20];
        int len;
        while((len = raf1.read(buffer)) != -1){
    
    
            //append方法的参数不能直接传byte类型的数组,可以传String类型,String类型里面可以传char类型的数组
            builder.append(new String(buffer,0,len)) ;//进行拼接,如果数组没有数据不就是添加到数组中吗(abc+dd=abcdd,null+dd=dd)
        }
        //调回指针,写入“xyz”
        raf1.seek(8);
        raf1.write("ccc".getBytes());//此时指针在文件末尾

        //将StringBuilder中的数据写入到文件中
        raf1.write(builder.toString().getBytes());//write参数为字节,StringBuilder没有提供getBytes()方法,先转化为String类型 在调用getBytes()方法。

        raf1.close();

        //思考:将StringBuilder替换为ByteArrayOutputStream day617
    }
}


3.4 Introduction à NIO

3.4.1 Introduction de bon sens

  1. Blocage IO, BIO est le package java.io traditionnel. Il est implémenté sur la base du modèle de flux. La méthode d'interaction est la synchronisation et le blocage. C'est-à-dire lors de la lecture du flux d'entrée ou du flux de sortie, avant que les actions de lecture et d'écriture ne soient terminé, le thread se bloquera toujours là, avec des appels entre eux dans un ordre linéaire fiable. Son avantage est que le code est relativement simple et intuitif ; son inconvénient est que l'efficacité et l'évolutivité des E/S sont très faibles, et cela peut facilement devenir un goulot d'étranglement dans les performances des applications.

  2. IO non bloquant, NIO est le package java.nio introduit dans Java 1.4. Il fournit de nouvelles abstractions telles que Channel, Selector et Buffer. Il peut créer des programmes IO multiplexés et synchrones non bloquants, et en même temps fournit une haute Interface de niveau plus proche du système d'exploitation sous-jacent. Méthodes de manipulation des données de performances.

  3. Asynchronous IO, AIO est un package introduit après Java 1.7. Il s'agit d'une version améliorée de NIO. Il fournit un mode de fonctionnement asynchrone non bloquant, c'est pourquoi les gens l'appellent AIO (Asynchronous IO). L'IO asynchrone est implémenté en fonction d'événements et de mécanismes de rappel. . Autrement dit, l'opération de l'application reviendra directement sans y être bloquée. Une fois le traitement en arrière-plan terminé, le système d'exploitation informera le thread correspondant d'effectuer les opérations suivantes. Mais il n’est pas encore assez mature et a peu d’applications.

3.4.2 Présentation du NIO

Insérer la description de l'image ici
Insérer la description de l'image ici
Insérer la description de l'image ici
Insérer la description de l'image ici
Insérer la description de l'image ici
Insérer la description de l'image ici
Insérer la description de l'image ici

3.5 Importer des packages jar tiers pour réaliser la lecture et l'écriture

3.5.1 Aperçu

illustrer:

  1. Le code du flux io réel pendant le développement peut être écrit par vous-même, ou il peut appeler directement l'API du package jar. Son code source sous-jacent encapsule toujours ces opérations io, ce qui rend l'appel plus facile.
  2. Avant d'apprendre Springboot, vous devez ajouter manuellement des packages jar pour utiliser les packages jar.

3.5.2 Ajouter un test de package jar

Prenons l'idée comme exemple pour ajouter le package jar :

  1. Copiez le package jar.
    Insérer la description de l'image ici
  2. Le répertoire créé dans le projet idea est généralement : libsInsérer la description de l'image ici
  3. Copiez le package jar dans ce répertoire
    Insérer la description de l'image ici
  4. Mettez le paquet de pots en vigueur :
    Insérer la description de l'image ici

3.5.3 Écriture de tests de code

Insérer la description de l'image ici

3.6 Prolongations

3.6.1 Différence entre flush() et close() dans IO

  1. Flush() vide les données vers la destination et le flux peut continuer à être utilisé.
  2. Close() ferme le flux et vide les données vers la destination avant de fermer. Après la fermeture, le flux ne peut plus être utilisé.

Je suppose que tu aimes

Origine blog.csdn.net/aa35434/article/details/131211548
conseillé
Classement