day04--java 고급 프로그래밍: API: 개체, 문자열, 버퍼, 패키징 클래스, 날짜, Java 비교기, BigInteger, 기본, 길이 차이, IO 스트림, 경로, 직렬화 ID, 인코딩

1개의 API

1.1 API 문서 다운로드

1.API(Application Programing Interface, 애플리케이션 프로그래밍 인터페이스 )는 Java에서 제공하는 기본 프로그래밍 인터페이스로 호출할 수 있는 모든 것이 API입니다.

2. Java 언어는 다수의 기본 클래스를 제공하므로 Oracle은 이러한 기본 클래스에 해당하는 API 문서도 제공하여 개발자에게 이러한 클래스와 해당 클래스에 포함된 메소드를 사용하는 방법을 알려줍니다.

3. API 다운로드 :
추가 리소스-Java SE 8 설명서를 다운로드합니다.
http://www.oracle.com/technetwork/java/javase/downloads/index.html

4. 자세한 내용은 JDK8 버전 Api download-installation-configuration.doc를 참조하세요.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

1.2 공통 카테고리

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

1.3 객체

1.3.1 개념

  1. Object 클래스는 모든 Java 클래스의 루트 상위 클래스입니다.

  2. 상위 클래스를 나타내기 위해 클래스 선언에 확장 키워드가 사용되지 않은 경우 기본 상위 클래스는 java.lang.Object 클래스입니다.

  3. Object 클래스의 함수(속성, 메서드)는 보편적입니다.
    속성 : 없음
    메소드 : equals() / toString() / getClass() /hashCode() / clone() / finalize() / wait() / inform() / informAll() 생성자: 객체 클래스는 빈 매개변수 생성자
    선언
    여기에 이미지 설명을 삽입하세요.

  4. Object 클래스는 java.lang 패키지에 존재하며, 이 lang 패키지에서는 패키지를 수동으로 가져올 필요가 없습니다.
    여기에 이미지 설명을 삽입하세요.

1.3.2 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 ==와 같음()의 차이점

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
		
		
	}
}

고객 등급 :

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 toString

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 문자열

1.4.1 상속관계

여기에 이미지 설명을 삽입하세요.

1.4.2 특징

public final class String: 문자열은 최종 수정되어 상속될 수 없습니다.
private final char value[ ]: String은 하단에 char[ ]를 유지하며 배열의 길이는 변경할 수 없으며 최종 수정되며 배열의 값은 수정할 수 없습니다. - - - 즉, 문자열은 상수이므로 일단 정의되면 수정할 수 없습니다.
여기에 이미지 설명을 삽입하세요.

1.4.3 문자열 객체 생성

방법 1: 리터럴 메서드 생성

  1. 구문: String s1 = "abc";//리터럴 정의 방법
  2. 문자열이 처음 사용되는 경우 Java는 문자열 상수 풀에 객체를 생성합니다.
  3. 상수 풀(기본적으로 여전히 char[])에서 직접 객체를 생성하고 동일한 콘텐츠를 다시 사용하는 경우 상수 풀에서 기존 객체를 찾고 새 객체를 생성하지 않습니다. (메모리 오버헤드 감소)

시험:
참고: 다양한 버전의 JVM 가상 머신마다 저장 방법에 차이가 있습니다. 즉, jdk1.6 및 jdk1.8의 상수 풀은 메소드 영역에 있고 jdk1.7의 상수 풀은 힙에 있습니다.
여기에 이미지 설명을 삽입하세요.

/*
    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

    }

방법 2: new + 생성자 메서드
구문:

第一种:
//本质上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

시험:

여기에 이미지 설명을 삽입하세요.

 /*
    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 다양한 문자열 접합 작업 비교

여기에 이미지 설명을 삽입하세요.

 /*
    结论:
    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 일반적인 방법


        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 일반적인 테스트 방법

     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 문자열과 문자 배열 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 문자열과 바이트 배열 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 스트링버퍼/스트링빌더

1.5.1 특징

  1. 캡슐화된 char[] 배열
  2. 가변적인 문자 시퀀스입니다.
  3. 문자 내용을 수정하기 위한 일련의 방법을 제공합니다.
  4. 문자열 연결을 위해 문자열 대신 add()가 자주 사용됩니다.
  5. 내부 문자 배열의 기본 초기 용량은 16입니다: 초기 용량은 16자입니다.
  6. 16보다 크면 용량 확장을 시도하고, 새로운 배열 크기는 2배 + 2로 변경되며, 용량이 부족하면 필요한 용량까지 직접 확장됩니다. int newCapacity = value.length * 2 + 2;
  7. StringBuffer 1.0은 출시 이후 스레드로부터 안전하며, StringBuilder1.5는 출시 이후부터 스레드로부터 안전하지 않습니다.
  8. public final class StringBuilder -----최종 클래스는
    char[] 값을 상속받을 수 없습니다. --------하단 레이어는 여전히 String과 유사하며 char[] 하나만 유지하지만 값은 언제든지 수정할 수 있습니다. 시간.

1.5.2 String, StringBuffer, 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 일반적인 방법

참고: StringBuffer와 StringBuilder의 메서드 이름은 동일하며 사용법도 동일합니다.

 /*
    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 세 가지 문자열 유형의 효율성 비교


/**
 * 关于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 문자열 순회

①字符串的遍历:
 
// 方法一:使用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 스트링 방과후 연습(473화)

1.6.1 역방향 문자열

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 문자열이 다른 문자열에 나타나는 횟수 얻기

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 두 문자열에서 가장 큰 동일한 부분 문자열 얻기

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 포장

보다 완전한 기능을 제공하기 위해 기본 유형을 패키지화합니다.

1.7.1 기본 유형과의 대응(int 및 char 참고)

여기에 이미지 설명을 삽입하세요.

1.7.2 숫자형 래퍼 클래스의 부모 클래스: Number

소스 코드 발췌 : 추상 상위 클래스 공용 추상 클래스 번호
상속 관계 입니다 .
여기에 이미지 설명을 삽입하세요.

일반적으로 사용되는 방법 : (사용법은 거의 동일)

여기에 이미지 설명을 삽입하세요.

1.7.3 기본 유형, 래퍼 클래스 및 문자열 클래스 간의 변환 지침

여기에 이미지 설명을 삽입하세요.
요약하다:

  1. 기본 유형 간 변환:
    소형->대형: 자동으로 전송
    대형->소형:강제이체
  2. 참조 유형 간 변환:
    소형->대형: 자동으로 회전, 즉 위로 향하는 형상
    대형->소형: 부모-자식 관계, 즉 하향 모델링이 있어야 함
  3. 기본 유형과 기본 유형에 해당하는 포장 유형이 서로 변환됩니다.
    기본 유형 -> 포장 등급: ①.패키징 클래스 생성자 호출(구식), ②해당 패키징 클래스의 valueOf() 메서드 호출(참고: 범위 내 Integer의 캐싱 효과), ③자동 박싱(jdk1.5에 나타남) )
    포장등급 -> 기본형: ①.패키징 클래스 Xxx의 xxxValue() 메소드 호출, ②자동 언박싱(jdk1.5 등장)
  4. 기본 유형과 문자열 유형 간의 변환:
    기본 유형 -> 문자열 유형: ①. String 오버로드된 valueOf(Xxx xxx)를 호출하고, ② "+"를 사용하여 연결 작업을 수행합니다.
    문자열 유형 -> 기본 유형: 래퍼 클래스의 parsXxx(String s)를 호출합니다.
  5. 기본 유형에 해당하는 패키징 유형과 문자열 유형이 서로 변환됩니다.
    패키징 클래스 -> 문자열 유형:toString 메소드
    문자열 유형 -> 래퍼 클래스: 래퍼 클래스의 parsXxx(String s)를 호출합니다.

1.7.4 기본 유형과 해당 패키징 클래스 간의 상호 변환

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 기본 유형과 문자열 유형 간의 변환

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 기본 타입에 해당하는 래퍼 클래스와 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 JDK8 이전의 날짜 및 시간 API

1.8.1 java.lang.System 클래스

여기에 이미지 설명을 삽입하세요.

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

1.8.2 java.util.Date 클래스

여기에 이미지 설명을 삽입하세요.

 /*
    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类

여기에 이미지 설명을 삽입하세요.

/**
 * 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 java.util.Calendar(캘린더) 클래스

여기에 이미지 설명을 삽입하세요.

 /*
    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 JDK8의 새로운 날짜 및 시간 API

1.9.1 배경이 나타납니다

여기에 이미지 설명을 삽입하세요.

@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
    }

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

1.9.2 로컬 날짜, 로컬 시간, 로컬 날짜 시간

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

/*
    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 인스턴트

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

 /*
    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 java.time.format.DateTimeFormatter 클래스

여기에 이미지 설명을 삽입하세요.

/*
    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 jdk8의 기타 날짜 및 시간 API

여기에 이미지 설명을 삽입하세요.

1.10 자바 비교기

설명: 객체 간의 크기를 비교하는 데 사용되며 실제로 객체 간의 속성을 기준으로 비교됩니다.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

1.10.1 자연 정렬: java.lang.Comparable

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
비교테스트:

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));
    }

   

상품:

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 사용자 정의 정렬: java.util.Comparator

여기에 이미지 설명을 삽입하세요.
비교테스트:

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));
    }

}

상품:

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 기타 일반적으로 사용되는 카테고리

1.11.1 시스템 클래스

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

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 수학 수업

여기에 이미지 설명을 삽입하세요.

1.11.3 BigInteger와 BigDecimal

1).BigInteger: 매우 큰 정수 연산을 해결하는 데 자주 사용됩니다. 둘 다 같은 방식으로 사용됩니다.

여기에 이미지 설명을 삽입하세요.

여기에 이미지 설명을 삽입하세요.
2) .BigDecimal: 정확한 부동 소수점 연산을 해결하는 데 자주 사용됩니다. 이전 ±*/ 연산을 두 객체(십진수:십진수) 간의 연산으로 최적화할 수 있습니다.

여기에 이미지 설명을 삽입하세요.
객체 생성:

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

일반적으로 사용되는 방법:

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

테스트 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);
       }
      
}

테스트 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 확장

1.12.1 베이스

개념:
기본 시스템은 캐리 계산 시스템으로, 캐리를 인위적으로 정의한 계산 방법으로 통계의 "긍정적" 단어와 유사합니다.
모든 기본 시스템 - X 기본 시스템의 경우 X에 도달할 때마다 각 위치의 숫자 연산이 한 자리씩 수행됨을 의미합니다.
10진법은 10분의 1마다 1씩 전진하고, 16진수 시스템은 16분의 1마다 1씩 전진하며, 2진법 시스템은 2마다 1씩 전진하는 식입니다.
일반적으로 1바이트=8진수
이므로 숫자를 이진수로 표현하면 0000 0000과 같이 표현할 수 있다.
이 8비트를 조합하면 세 자리 조합이 모두 8진수 체계를 이루고, 네 자리 조합이 모두 형성된다. 8진법 시스템.
특징
이진수: 0과 1, 두 개는 모두 1입니다. 0b로 시작합니다.
팔진수: 0-7, 여덟 개는 모두 1이고, 0으로 시작합니다.
십진수: 0-9, 10개는 모두 1입니다. 16진수
: 0-9, abcdef , 0x부터 시작하여 매 10개의 16 앞으로

10진수에서 2진수로의 변환
: 몫이 0이 될 때까지 계속 2로 나누고 나머지를 취하여 거꾸로 씁니다.
10진수 11을 2진수: 1011로 변환합니다.
여기에 이미지 설명을 삽입하세요.

이진수를 십진수로 변환: 낮은 순서부터 각 비트에 2의 거듭제곱을 곱한 다음 합산합니다.
이진 데이터 계산: 0000 1101에 해당하는 십진수
이진 데이터 계산: 0110 1110에 해당하는 십진수
여기에 이미지 설명을 삽입하세요.

2진수에서 8진수로: 하위 비트부터 시작하여 세 자리마다 하나의 그룹으로 그룹화되어 8진수를 생성하고 가장 높은 비트는 0으로 채워집니다.
이진 데이터 110 0111에 해당하는 8진수 계산
여기에 이미지 설명을 삽입하세요.

8진수를 2진수로 변환: 숫자를 3자리로 변환합니다. 3자리 미만인 경우 가장 높은 자리에 0을 추가합니다.
8진수 데이터 계산: 023 0653에 해당하는 이진수 데이터
여기에 이미지 설명을 삽입하세요.

2진수에서 16진수로: 4개의 그룹으로, 0x로 시작하는 1개의 숫자로 변환됩니다(
생략됨). . .
16진수를 2진수로 변환: 하나의 숫자가 4자리가 됩니다
. . .

1.12.2 길이의 차이

문자열에는 길이 방법이 있습니다. 배열에는 길이 방법이 없지만 길이 속성이 있습니다. 컬렉션에는 길이 방법이 없으며 크기 방법은 컬렉션의 길이를 계산하는 데 사용됩니다.

2 IO유류–01

일반적으로 파일 전송에는 컨테이너(파일 클래스) – 노드 스트림 – 처리 스트림의 세 가지 개체가 필요합니다.

컨테이너: 데이터를 읽고 쓰려면 데이터를 읽고 쓰는 엔드포인트가 필요합니다. Java의 모든 것은 객체이고 File 클래스는 이 객체입니다.
노드 흐름: File 클래스에서 직접 작동하는 흐름을 노드 흐름이라고 합니다.
처리 스트림: File 클래스에 직접 또는 간접적으로 연결된 스트림입니다.

2.1 IO 소개

참고 1:

  1. 데이터 읽기 및 쓰기는 파이프라인의 데이터와 흐름으로 추상화됩니다.여기에 이미지 설명을 삽입하세요.
  2. I------>입력: 입력 스트림
  3. O->출력: 출력 스트림
  4. 흐름은 한 방향으로만 흐를 수 있다
  5. 데이터는 처음부터 끝까지 순차적으로만 읽고 쓸 수 있습니다.
  6. 스트림 닫기 작업: 데이터베이스 연결, 입력 및 출력 스트림, 소켓 연결과 같은 물리적 연결에 대해서는 수행할 수 있는 작업이 없으며 이러한 리소스는 수동으로 닫아야 합니다.
  7. IO 스트림용 모든 API는 java.io 패키지에서 제공됩니다.
    여기에 이미지 설명을 삽입하세요.

참고 2: io 스트림의 입력과 출력은 程序메모리 관점에서 분석됩니다( ).

  1. 메모리로 전송되는 영구 데이터를 입력 스트림이라고 합니다.
  2. 메모리에서 영구 데이터로 전송된 데이터를 출력 스트림이라고 합니다.
    여기에 이미지 설명을 삽입하세요.

2.2 IO 분류

여기에 이미지 설명을 삽입하세요.

  1. 데이터 흐름의 다양한 흐름 방향에 따라 입력 스트림과 출력 스트림으로 구분됩니다.

  2. 다양한 운영 데이터 단위에 따라 바이트 스트림(8비트), 문자 스트림(16비트)으로 구분됩니다
    字节流 . 사진, 비디오 등에 적합합니다. 바이트 스트림의 데이터 전송은 0101과 같은 바이너리 데이터입니다.
    字符流: 파일 전송에 적합하며, 문자 스트림의 데이터 전송은 각각의 char 형태의 문자이므로 어떤 내용이 전송되고 있는지 알 수 있다. 16bit(비트) = 2byte(바이트) = 1 char형 문자입니다.


  3. 스트림의 다양한 역할에 따라 노드 스트림, 처리 스트림, 노드 스트림/저수준 스트림 으로 구분됩니다 . 파일에 직접 작용하는 스트림을 노드 스트림이라고 합니다.
    처리 흐름/고급 흐름 : 노드 흐름을 기반으로 흐름의 상위 계층을 직접 래핑하거나 간접적으로 래핑하며, 이러한 외부 흐름을 처리 흐름이라고 하며 여러 흐름을 래핑할 수 있습니다.

2.3 상속 구조

참고: io 흐름의 모든 클래스는 4개의 추상 상위 클래스에서 나옵니다.
여기에 이미지 설명을 삽입하세요.
참고: 어두운 셀은 더 일반적으로 사용되는 흐름을 나타냅니다. 접미사로 구분 가능

字节流:针对二进制文件 更广泛   用来操作电脑里的所有格式的数据。
|---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  //字符打印输出流 (高级流)




여기에 이미지 설명을 삽입하세요.

2.4 File 클래스 사용

2.4.1 개요

  1. 파일 또는 파일 디렉터리(일반적으로 폴더라고 함)를 나타내는 File 클래스의 개체입니다.
  2. File 클래스는 java.io 패키지 아래에 선언됩니다.
  3. File 클래스는 파일이나 파일 디렉터리의 생성, 삭제, 이름 바꾸기, 수정 시간, 파일 크기 등의 메서드를 포함하지만 파일 내용을 쓰거나 읽는 작업은 포함하지 않습니다. 파일 콘텐츠를 읽거나 써야 하는 경우 IO 스트림을 사용하여 완료해야 합니다.
  4. File 클래스의 후속 객체는 읽기 또는 쓰기의 "종료점"을 나타내기 위해 스트림 생성자에 매개변수로 전달되는 경우가 많습니다.

2.4.2 객체를 인스턴스화하는 생성자 방법(경로 문제)

건설 방법:
여기에 이미지 설명을 삽입하세요.
상대 경로에 대한 참고 사항:
1. 일반 Java 프로젝트: 아이디어상 단위 테스트 메소드에 작성된 코드의 상대 경로는 현재 모듈(각 프로젝트 아래)을 참조합니다. 기본 메소드에 있는 경우 현재 프로젝트(소위 작업공간)를 기준으로 합니다.
2. 일반 Java 프로젝트: Eclipse에서는 각 개별 프로젝트가 별도의 프로젝트로 간주되므로 단위 테스트와 기본 메소드는 현재 프로젝트에 대한 상대 경로입니다.

경로 구분 기호에 대한 참고 사항:
여기에 이미지 설명을 삽입하세요.

2.4.3 생성자 메서드 테스트

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 공통 방법 테스트

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 연습 1: 전체 파일 크기를 재귀적으로 찾기

재귀: 메서드 자체를 계속 호출합니다.
요구 사항: 파일 크기 통계
1. 지정된 디렉터리를 File 개체로 캡슐화
2. 폴더 목록 나열
3. 파일인지 확인하고 f.length()를 직접 추가합니다.
4. 폴더인지 확인하고 다음을 호출합니다. it recursively 메소드 자체의 비즈니스 로직은 반복해서 다시 판단하는 것인데, 파일이면 추가하고, 폴더이면 목록을 계속해서 판단하고, 파일이면 추가하고... 참고 : 파일만 크기가 있고 디렉터리에는 크기가 없습니다
.

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 연습 2: 폴더를 반복적으로 삭제

재귀: 메서드 자체를 계속 호출합니다.
요구 사항: 폴더를 반복적으로 삭제
1. 지정된 디렉터리를 File 개체로 캡슐화
2. 폴더 목록 나열
3. 판단, 파일이면 직접 삭제
4. 판단, 폴더인 경우 비즈니스 로직 호출 메소드 자체를 재귀적으로 반복하여 다시 판단합니다. 파일이면 삭제합니다. 폴더이면 목록을 계속하고 계속 판단합니다. 파일이면 삭제합니다... 참고: 삭제하려면 삭제하세요
. 성공적으로 실행하려면 파일 디렉터리에 하위 디렉터리나 파일이 없어야 합니다. Java에서 삭제하면 휴지통으로 이동되지 않습니다.

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循环之后可以删除空目录了。
    }
}

2.5 바이트 스트림 읽기

설명하다:

  1. 바이트 스트림은 바이트로 구성되고 문자 스트림은 문자로 구성됩니다. Java의 문자는 2바이트로 구성되며 바이트 스트림이 가장 기본이며, InputStream 및 OutputStream의 모든 하위 클래스는 다음과 같습니다.주로 이진 데이터를 처리하는 데 사용됩니다.

  2. 스트리밍이란 주로 전체 내용을 전송하는 것을 의미합니다.오디오, 비디오, 3차원 미디어 등의 멀티미디어 파일특정 압축 방식을 거쳐 압축된 패키지로 파싱되어 비디오 서버를 통해 순차적으로 또는 실시간으로 사용자의 컴퓨터로 전송됩니다. 스트리밍 전송을 사용하는 시스템에서는 사용자가 다운로드 방식처럼 파일 전체가 다운로드될 때까지 기다릴 필요 없이, 몇 초 또는 수십 초만에 사용자 컴퓨터에 있는 압축해제 장치를 사용할 수 있다. 시작 지연 압축된 A/V, 3D 및 기타 멀티미디어 파일은 압축 해제 후 재생 및 시청이 가능합니다. 이때 멀티미디어 파일의 나머지 부분은 백그라운드 서버에서 계속 다운로드됩니다.
    여기에 이미지 설명을 삽입하세요.

2.5.1 InputStream 추상 상위 클래스

참고: 이 추상 클래스는 모든 바이트 입력 스트림의 상위 클래스를 나타내며 객체를 생성하는 구성 방법을 배우지 않고 일반적인 방법만 학습합니다.

일반적으로 사용되는 방법:

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

2.5.2 FileInputStream 하위 클래스(노드 스트림: 파일 바이트 스트림)

설명: 파일에 직접 삽입하고 파일 데이터를 직접 읽습니다.

객체 생성:

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

시험:

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 BufferedInputStream 하위 클래스(처리 스트림: 버퍼링된 바이트 스트림)

참고: BufferedInputStream을 생성할 때 내부 버퍼 배열(기본값 8M=8*1024 크기)이 생성됩니다. 이 내부 버퍼는 스트림의 바이트를 읽거나 건너뛸 때 필요에 따라 포함된 입력 스트림에서 한 번에 여러 바이트로 다시 채워질 수 있습니다 缓存流的作用都是提高读写效率的.

객체 생성:

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

시험:

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 ObjectInputStream 하위 클래스(처리 스트림: 객체 입력 스트림/역직렬화)

역할: 基本数据类型数据 或 对象(主要用于对象)저장 및 읽기를 위한 처리 흐름입니다. 그 기능은 Java로 개체를 데이터 소스에 쓰고 데이터 소스에서 개체를 복원할 수 있다는 것입니다.

명사 설명:

  1. 직렬화: ObjectOutputStream 클래스를 사용하여 기본 유형 데이터 또는 객체를 저장하는 메커니즘(객체 정보를 고정 형식의 바이트 값 문자열로 변환하여 출력하고 디스크에 유지)
  2. 역직렬화: ObjectInputStream 클래스를 사용하여 기본 유형 데이터 또는 객체를 읽는 메커니즘(디스크에서 직렬화된 데이터를 읽고 객체를 복원함)
    여기에 이미지 설명을 삽입하세요.

객체 직렬화 메커니즘: 메모리의 Java 객체를 플랫폼 독립적인 바이너리 스트림으로 변환하여 이 바이너리 스트림을 디스크에 유지하거나 네트워크를 통해 다른 네트워크 노드로 전송할 수 있습니다. 다른 프로그램이 이 바이너리 스트림을 얻으면 원래 Java 개체로 복원할 수 있습니다.

특징:

  1. 직렬화해야 하는 파일은 직렬화 기능을 활성화하기 위해 직렬화 가능 인터페이스를 구현해야 합니다.
  2. 직렬화할 필요가 없는 데이터는 static으로 수정이 가능하며, static은 클래스에 속하므로 직렬화되어 객체와 함께 출력되지 않는다.
  3. 직렬화가 필요하지 않은 데이터는 일시적으로 수정될 수도 있으며, 프로그램 실행 중에만 메모리에만 존재하며 직렬화 및 지속되지 않습니다.
  4. 역직렬화 중에 직렬화된 버전 번호와 일치하지 않으면 역직렬화를 완료할 수 없습니다.
  5. 직렬화된 각 파일에는 고유한 ID가 있으며, ID가 추가되지 않은 경우 컴파일러는 클래스 정의 정보를 기반으로 버전 번호를 계산하고 생성합니다.
  6. 서버 간 데이터 전송, 파일 직렬화, 데이터 읽기를 위한 역직렬화에 일반적으로 사용됩니다.
  7. 일반적으로 소켓 스트림을 사용하여 호스트 간에 객체를 전달하는 데 사용됩니다.

테스트:
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();
                }

            }
        }



    }

}


개인 클래스:

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;
    }
}


여기에 이미지 설명을 삽입하세요.

2.6 바이트 스트림 쓰기

2.6.1 OutputStream 추상 상위 클래스

참고: 이 추상 클래스는 출력 바이트 스트림을 나타내는 모든 클래스의 상위 클래스이며 구성 방법은 연구되지 않습니다.

일반적으로 사용되는 방법:

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 FileOutputStream 하위 클래스(노드 스트림: 파일 바이트 스트림)

설명: 파일에 직접 삽입하고 파일 데이터를 직접 씁니다.

객체 생성:

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 的文件中写入数据的输出文件流。 

시험:

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 BufferedOutputStream 하위 클래스(처리 스트림: 버퍼링된 바이트 스트림)

설명: 이 클래스는 버퍼링된 출력 스트림을 구현합니다. 이 출력 스트림을 설정하면 애플리케이션은 각 바이트 쓰기에 대해 기본 시스템을 호출하지 않고도 기본 출력 스트림에 개별 바이트를 쓸 수 있습니다.

객체 생성:

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

테스트·: 2.5.3의 테스트를 확인하세요.

2.6.4 ObjectOutputStream 하위 클래스(처리 스트림: 객체 출력 스트림/직렬화)

생략합니다. 자세한 내용은 2.5.4를 참조하세요.

2.7 바이트 스트림 방과후 연습

2.7.1 이미지 암호화 및 복호화

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 텍스트에서 문자 발생 횟수를 가져와 파일에 데이터 쓰기

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 문자 스트림 읽기

설명: 일반적으로 일반 텍스트 데이터를 처리하는 데 사용됩니다.

3.1.1 리더 추상 부모 클래스

참고: 문자 스트림을 읽는 데 사용되는 추상 부모 클래스입니다. 추상 클래스이므로 새 객체를 생성할 수 없으므로 일반적으로 사용되는 방법만 배우고 구성 방법은 연구하지 않습니다.

일반적으로 사용되는 방법:

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 FileReader 하위 클래스(노드 스트림: 파일 문자 스트림)

설명: 문자 파일을 읽기 위한 편의 클래스입니다.

건설 방법:

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

일반적으로 사용되는 메소드: 고유한 메소드는 없으며 모두 java.lang.Object, java.io.Reader 및 java.io.InputStreamReader 클래스에서 상속된 메소드입니다.

시험

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();
                }

            }
        }

    }
 }

여기에 이미지 설명을 삽입하세요.

3.1.3 BufferedReader 하위 클래스(처리 스트림: 버퍼링된 문자 스트림)

설명: 문자 입력 스트림에서 텍스트를 읽고 개별 문자를 버퍼링하여 문자, 배열 및 줄을 효율적으로 읽을 수 있습니다. 버퍼 크기를 지정하거나 기본 크기를 사용할 수 있습니다. 대부분의 경우 기본값은 충분히 큽니다.

객체 생성:

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

시험:

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 InputStreamReader 하위 클래스(스트림 처리: 스트림 변환)

설명하다:

  1. 바이트 스트림을 문자 스트림으로 변환하는 브리지로 사용됩니다.
  2. 문자 스트림 읽기 및 쓰기가 왜곡되는 문제를 해결하는 데 사용됩니다.

건설 방법:

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

일반적으로 사용되는 방법:

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

시험:

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 공통 문자 인코딩 테이블

코딩 테이블의 기원: 컴퓨터는 이진 데이터만 인식할 수 있으며 초기 기원은 전기 신호입니다. 컴퓨터의 응용을 용이하게 하기 위해 다양한 나라의 문자를 인식할 수 있습니다. 각 나라의 문자를 숫자로 표현하고 하나씩 대응시켜 표를 구성하였습니다. 코딩 테이블입니다.

글자 깨짐 현상은 주로 중국어 글자 깨짐을 의미합니다.

  1. 어떻게 생겼습니까? 저장 시 사용한 코드 테이블과 열 때 사용한 코드 테이블이 일치하지 않기 때문입니다.
  2. 해결 방법: 유니코드 테이블을 저장하고 엽니다.
코딩 소개 설명하다 코딩 범위 바이트
아스키 미국 표준 정보 교환 코드 영어, 구두점, 기본설명 0~127 7비트의 바이트로 표현될 수 있다. (싱글바이트)
ISO8859-1 라틴 코드 테이블, 유럽 코드 테이블 ASCII와 호환되며 이를 기반으로 서유럽 문자를 확장합니다. 128~255 8비트의 바이트로 표현됩니다. (싱글바이트)
GB2312 중국의 중국어 코딩 테이블 ASCII 호환 모든 문자를 인코딩하는 최대 2바이트
GBK 중국 중국어 코딩 테이블 업그레이드 ASCII와 호환되며 더 많은 한자와 기호를 통합합니다. 최대 65535 최대 2바이트가 모든 문자를 인코딩합니다. (영어는 1바이트, 중국어는 2바이트를 사용합니다.)
유니코드 국제표준코드 국제표준코드는 현재 인간이 사용하는 모든 문자를 조합한 것이다. ASCII와 호환되는 각 문자에 고유한 문자 코드를 할당합니다. 100만 개 이상의 코딩 비트, 공통 문자 테이블, 희귀 문자 테이블 등으로 구분됩니다. 우리는 공통 테이블만 사용합니다. 모든 텍스트는 2바이트로 표시됩니다. (일반적으로 사용되는 문자표의 모든 문자는 더블바이트를 사용합니다.)
UTF-8 최적화된 국제 표준 코드 유니코드 영어 문자의 바이트 크기가 두 배로 늘어나는 문제를 해결하기 위해 ASCII와 호환되는 가변 길이 인코딩 형식이 제안되었습니다. 한 문자는 1~4바이트로 표현될 수 있습니다. (영문 1바이트, 일부 문자 2바이트, 중국어 3바이트, 일부 특수 기호 4바이트)

유니코드 인코딩 문제:
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

지침:

  1. ANSI 인코딩은 일반적으로 플랫폼의 기본 인코딩을 나타냅니다.예를 들어 영어 운영 체제는 ISO-8859-1이고 중국어 시스템은 GBK입니다.

  2. 유니코드 문자 집합은 문자의 집합과 고유 개수만 정의합니다. 유니코드 인코딩은 UTF-8, UCS-2/UTF-16 등과 같은 특정 인코딩 체계에 대한 일반적인 용어이며 특정 인코딩 체계가 아닙니다.

향후 연구를 위한 영감:

  1. 클라이언트/브라우저 <---------> 백엔드(java, GO, Python, Nod.js, php) <---------> 데이터베이스
  2. 전후에 사용되는 문자 집합은 UTF-8로 통일되어야 합니다.

3.2 문자 스트림 쓰기

3.2.1 작성기 추상 상위 클래스

참고: 문자 스트림을 작성하기 위한 추상 클래스도 구성 방법을 연구하지 않습니다.

일반적으로 사용되는 방법:

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 FileWriter 하위 클래스(노드 스트림: 파일 문자 스트림)

설명: 문자 파일 작성을 위한 편의 클래스입니다.

객체 생성:

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 对象。 

일반적으로 사용되는 메소드: 고유한 메소드는 없으며 모두 java.lang.Object, java.io.Writer 및 java.io.OutputStreamWriter 클래스에서 상속된 메소드입니다.

시험

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 BufferedWriter 하위 클래스(처리 스트림: 버퍼링된 스트림)

설명: 문자 출력 스트림에 텍스트를 쓰고 개별 문자를 버퍼링하여 단일 문자, 배열 및 문자열의 효율적인 쓰기를 제공합니다. 버퍼 크기를 지정하거나 기본 크기를 적용할 수 있습니다. 대부분의 경우 기본값은 충분히 큽니다.

객체 생성:

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

테스트: 자세한 내용은 3.1.3을 참조하세요.

3.2.4 OutputStreamWriter 하위 클래스(스트림 처리: 스트림 변환)

설명하다:

  1. 문자 스트림을 바이트 스트림으로 변환하는 브리지로 사용됩니다.
  2. 문자 스트림에 작성된 문자가 왜곡되는 문제를 해결하는 데 사용됩니다.

건설 방법:

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

일반적으로 사용되는 방법:

 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) 
          写入字符串的某一部分。 

테스트: 자세한 내용은 3.1.4를 참조하세요.

3.3 다른 스트림의 사용(일반적으로 사용되지 않음)

3.3.1 표준 입력 및 출력 스트림(System.in, System.out)

설명하다:

  1. System.in과 System.out은 각각 시스템의 표준 입력 및 출력 장치를 나타냅니다.
  2. 기본 입력 장치는 키보드, 출력 장치는 모니터입니다.
  3. System.in의 유형은 InputStream입니다.
  4. System.out의 유형은 OutputStream의 하위 클래스인 FilterOutputStream의 하위 클래스인 PrintStream입니다.
  5. 리디렉션: System 클래스의 setIn 및 setOut 메소드를 통해 기본 장치를 변경합니다. ( 예를 들어 콘솔에 인쇄된 데이터가 파일에 인쇄되도록 변경됩니다. 자세한 내용은 인쇄 스트림을 참조하세요. )
    public static void setIn(InputStream in) 매개 변수는 InputStream 바이트 입력 스트림입니다.
    public static void setOut(PrintStream out) 매개 변수 PrintStream 바이트 인쇄 흐름입니다.

상속 관계:
여기에 이미지 설명을 삽입하세요.
참고: 키보드를 사용하여 콘솔에 문자를 입력하는 경우(스캐너 입력, 표준 입력 스트림 출력 스트림 System.in System.out) 데이터가 입력되지 않을 수 있습니다.

  1. 아이디어에서 main 메소드로 작성하면 콘솔에서는 데이터 입력이 가능하지만, 테스트 메소드로는 콘솔에서 데이터 입력이 불가능하다.
  2. Eclipse에서는 기본 메소드로 작성하든 테스트 메소드로 작성하든 데이터를 입력할 수 있습니다.
    여기에 이미지 설명을 삽입하세요.

관행:

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 인쇄 스트림(고급 스트림: PrintStream 및 PrintWriter)

설명하다:

  1. 기본 데이터형의 데이터 형식을 문자열 출력으로 변환하여 다양한 형태의 데이터를 출력할 수 있습니다.
  2. 인쇄 스트림: PrintStream 및 PrintWriter
  3. 여러 데이터 유형의 출력을 위해 일련의 오버로드된 print() 및 println() 메서드를 제공합니다.
  4. PrintStream 및 PrintWriter의 출력에서 ​​IOException이 발생하지 않습니다.
  5. PrintStream 및 PrintWriter에는 자동 플러시 기능이 있습니다.
  6. PrintStream에 의해 인쇄된 모든 문자는 플랫폼의 기본 문자 인코딩을 사용하여 바이트로 변환됩니다.
    바이트 대신 문자를 써야 하는 경우에는 PrintWriter 클래스를 사용해야 합니다.
  7. System.out은 PrintStream 인스턴스를 반환합니다.

상속 관계:

  1. 바이트 인쇄 출력 스트림
    여기에 이미지 설명을 삽입하세요.
  2. 문자 인쇄 출력 스트림
    여기에 이미지 설명을 삽입하세요.
    테스트:
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();
            }
        }

    }

    

}


여기에 이미지 설명을 삽입하세요.

3.3.3 데이터 스트림(고급 스트림: DataInputStream 및 DataOutputStream)

설명:
여기에 이미지 설명을 삽입하세요.
상속 관계:

  1. 데이터 입력 스트림
    여기에 이미지 설명을 삽입하세요.
  2. 데이터 출력 스트림
    여기에 이미지 설명을 삽입하세요.
    테스트:
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 랜덤 액세스 파일 스트림(RandomAccessFile)

개요:
여기에 이미지 설명을 삽입하세요.
상속 구조:
여기에 이미지 설명을 삽입하세요.

건설 방법:

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

시험:

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 NIO 소개

3.4.1 상식 소개

  1. Blocking IO, BIO는 전통적인 java.io 패키지이며 흐름 모델을 기반으로 구현됩니다. 상호 작용 방법은 동기화 및 차단입니다. 즉, 입력 스트림이나 출력 스트림을 읽을 때 읽기 및 쓰기 작업이 수행되기 전에 완료되면 스레드는 항상 신뢰할 수 있는 선형 순서로 스레드 간 호출을 통해 차단됩니다. 장점은 코드가 상대적으로 간단하고 직관적이라는 점이며, 단점은 IO의 효율성과 확장성이 매우 낮고 애플리케이션 성능 병목 현상이 쉽게 발생할 수 있다는 것입니다.

  2. Non-blocking IO, NIO는 Java 1.4에 도입된 java.nio 패키지로, 채널, 선택기, 버퍼와 같은 새로운 추상화를 제공합니다. 다중화 동기식 비차단 IO 프로그램을 구축할 수 있으며 동시에 높은 성능을 제공합니다. 기본 운영 체제에 더 가까운 수준의 인터페이스 성능 데이터 조작 방법.

  3. Asynchronous IO, AIO는 Java 1.7 이후에 소개된 패키지로, NIO의 업그레이드 버전으로, 비동기식 Non-Blocking IO 동작 모드를 제공하여 AIO(Asynchronous IO)라고 부른다. 비동기식 IO는 이벤트와 콜백 메커니즘을 기반으로 구현된다. 즉, 애플리케이션 작업이 거기에서 차단되지 않고 직접 반환되며, 백그라운드 처리가 완료되면 운영 체제는 해당 스레드에 후속 작업을 수행하도록 알립니다. 하지만 아직 충분히 성숙되지 않았고 적용 사례도 거의 없습니다.

3.4.2 NIO 개요

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

3.5 읽고 쓰기 위해 타사 jar 패키지 가져오기

3.5.1 개요

설명하다:

  1. 개발 중 실제 io 흐름 코드는 직접 작성하거나 jar 패키지의 Api를 직접 호출할 수 있습니다. 기본 소스 코드는 여전히 이러한 io 작업을 캡슐화하여 호출을 더 쉽게 만듭니다.
  2. springboot를 배우기 전에 jar 패키지를 사용하려면 jar 패키지를 수동으로 추가해야 합니다.

3.5.2 jar 패키지 테스트 추가

jar 패키지를 추가하는 방법을 예로 들어 보겠습니다.

  1. jar 패키지를 복사합니다.
    여기에 이미지 설명을 삽입하세요.
  2. 아이디어 프로젝트에서 생성된 디렉터리는 일반적으로 libs입니다.여기에 이미지 설명을 삽입하세요.
  3. jar 패키지를 이 디렉터리에 복사합니다.
    여기에 이미지 설명을 삽입하세요.
  4. jar 패키지를 적용합니다.
    여기에 이미지 설명을 삽입하세요.

3.5.3 코드 테스트 작성

여기에 이미지 설명을 삽입하세요.

3.6 확장

3.6.1 IO에서 Flush()와 close()의 차이점

  1. Flush()는 데이터를 대상으로 플러시하고 스트림을 계속 사용할 수 있습니다.
  2. Close()는 스트림을 닫고 닫기 전에 데이터를 대상으로 플러시합니다. 닫은 후에는 스트림을 더 이상 사용할 수 없습니다.

Supongo que te gusta

Origin blog.csdn.net/aa35434/article/details/131211548
Recomendado
Clasificación