很久前学习java总结的笔记

第一日内容

HelloWorld

public class HelloWorld {
	public static void main(String[] args) {
    	System.out.println("Hello,World!");
    	//System.out.println("Hello,World!");
        /*
        * 记住两个关键的快捷键
        * alt+enter   修复代码
        * ctrl+alt+l  格式化代码,让代码看起来好看
        * */
    }
}

Java内存划分

  1. 栈(Stack):存放的都是方法中的局部变量,方法的运行一定要在栈中
    1. 局部变量:方法的参数或者方法内的变量
    2. 作用域:一旦超出作用域,立刻从堆中消失
  2. 堆(Heap):凡是new出来的东西,都在堆里
    1. 堆内存中的东西都有一个地址值为十六进制
    2. 堆中存放的数据都有默认值
  3. 方法区:存储.class相关信息包括方法的信息
  4. 本地方法栈
  5. 寄存器

方法

  • 注意:

    1. 不可以方法内套方法,对于void方法,可以return但是不可以return任何值,可以有多个return但是必须保证之后一个return会被强制执行到。
    2. 直接输入5.fori就可以自动生成如下的第一层循环
  • 方法重载:方法名称相同但是参数列表不同,称为方法重载

    • 重载适用的情况
      1. 参数个数不同
      2. 参数类型不同
      3. 同类型参数顺序不同
    • 重载不适用的情况
      1. 参数类型相同,名称不同,换言之,与参数名称无关
      2. 返回类型不同,换言之,与返回类型无关
      3. 与修饰符无关,比如public or private or static,不论修饰的多么花里胡哨,都不算重载

数组

  1. 数组的默认值:

    1. 整数类型:0
    2. 浮点数类型:0.0
    3. 字符类型:\u0000
    4. 布尔类型:false
    5. 引用类型:null
  2. 数组的初始化

    public class Demo02Array {
        public static void main(String[] args) {
            /*
            数组初始化的两种格式
                1.动态初始化(长度)int[] a=new int[5]
                2.静态初始化(内容)
            */
            //动态初始化
            int[] array=new int[300];
            double[] array2=new double[10];
            String[] array3=new String[5];
            //静态初始化,直接指定内容
            int[] Array=new int[]{1,2,3,4};
            double[] Array2=new double[]{1.0,3.0,4,2};
            String[] Array3=new String[]{"Hello","World"};
            //静态初始化的省略格式
            int[] Array4={1,2,3,4};
        }
    }
    
  3. 注意

    1. shift+F6可以批量修改元素名称
    2. 多次运行不改变地址,改变元素值也不会
    3. 数组一旦创建,程序运行期间长度不可变
    4. array1=new int[5];这里不是改变了数组长度,而是数组的索引,本来那个长度为3的数组地址没有改变,但是不指向array1了

  1. 在方法内定义的是局部变量,在类中直接定义的是成员变量,成员变量有默认值,局部变量没有默认值,如果想要使用,必须手动赋值

    • 局部变量位于栈内存
    • 成员变量位于堆内存
  2. 类是抽象的,对象是具体的,类是对象的模板,对象是类的具体
    对象创建过程:

       1. 导包,若同属于一个包,则不需要导包
    2. 创建
    3. 使用
    
    Phone phone=new Phone();//创建
    phone.brand="HUAWEI";//使用
    
  3. 构造方法

    1. 构造方法的名称必须和所在类的名称完全一样

    2. 构造类型不要写返回值类型,void也不写

    3. new对象就是在调用构造方法

    4. 构造方法不可以有返回值

    5. 如果没有构造方法,编译器会默认一个构造方法

    6. 一旦有了构造方法,编译器就不在会赠送

    7. 构造方法也可以重载

第二天内容

ArrayList

  • 概念:ArrayList是集合,长度可变,可视为是可变长数组,一般用来存储对象,因为如果用数组存储对象的话,数组的长度是不可以改变的

  • 注意:

    1. ArrayList中的元素必须是同一类型
    2. ArrayLisy中的E代表泛型,泛型必须是引用类型,不可以是基本类型,如果希望存储基本类型,则必须使用基本类型的包装类
      • 各个包装类基本就是首字母大写 例如byte->Byte
      • 只有两个例外,一个是Integer和Character
    3. (import java.util.ArrayList)
  • 初始化:

    //右边的尖括号内可以不写内容但是必须有尖括号
    ArrayList<String> list=new ArrayList<>();
    
  • 常用方法

    1. add

      //向集合中添加内容add,add返回会返回一个boolean值,表明添加是否成功空就是空的中括
              list.add("赵丽颖");
              list.add("张三");
              list.add("李四");
              System.out.println(list);
      
    2. get(int index)

      System.out.println(list.get(0));//索引值都是从0开始
      
    3. remove(int index)

      System.out.println(list.remove(0));//删除并返回被删除元素
      
    4. size

      System.out.println(list.size());
      
      
    5. 注意:可以直接拿int接收Integer

      int i=list1.get(1);
      
      
      这里涉及到JDK1.5+后的特性,自动装箱和自动拆箱
      自动装箱是基本类型--》包装类型
      自动拆箱是包装类型--》基本类型
      
      

Random

import java.util.Random;
public class TestRandom {
    public static void main(String[] args) {
        Random r=new Random();
        int i = r.nextInt();//此时随机数的范围是int的取值范围,有正负,若有参数,则代表了取值范围
        //是左闭右开区间
        System.out.println(i);
        int i1=r.nextInt(3);//则取值范围是【0,3)
        System.out.println(i1);
        for (int i2 = 0; i2 < 100; i2++) {
            i1=r.nextInt(10);//则取值范围是【0,10)
            //若i1=r.nextInt(10)+1则则取值范围是【1,10】
            System.out.println(i1+": "+i2);
        }
    }
}

public class RandomGame {
    public static void main(String[] args) {
        Random r=new Random();
        int n=r.nextInt(10);
        System.out.println(n);
        Scanner sc=new Scanner(System.in);
        int j=0;
        while(true){
            j = sc.nextInt();
            if(j<n){
                System.out.println("猜小了");
            }
            else if(j>n){
                System.out.println("猜大了");
            }
            else{
                System.out.println("正确");
                break;
            }
        }
    }
}

Scanner

public static void getMax(){
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入三个值以比较出其最大值");
        int num1=sc.nextInt();
        int num2=sc.nextInt();
        int num3=sc.nextInt();
        if(num1>num2){
            num2=num1;
        }
        if(num3>num2){
            num2=num3;
        }
        System.out.println("最大值是:"+num2);
    }
}

String

  1. 字符串的特点:

    1. 字符串是常量,内容不可改变
    2. 字符串可以共享使用
    3. 字符串效果上相当于char[]字符组
    4. 注意,只要是使用双引号直接创建的字符串,会被放置与堆当中的字符串常量池中
  2. 创建字符串的主要方法

    public String()空白字符串,什么都没有只有个""
    public String(char[] array)
    public String(byte[] array)
    
    
  3. 常用的String方法

    1. 对string内容的比较 boolean equals(object o),和euqalsIgnoreCase(String str)

      String str="HELLO";
      char[] c={'H','E','L','L','O'};
      String str2=new String(c);
      String str3="Hello";
      System.out.println(str.equals(str2));//true
      //注意:我们推荐HELLO.equals(str2),不推荐反过来,也就是说,当比较对象是一个常量和一个变量
      //的时候,我们推荐常量在前,因为若变量在前且为null,使用equals则会导致空指针异常
      //第二种方法
      System.out.println(str2.equalsIgnoreCase(str3));//true
      
      
    2. 与获取相关的方法

      1. int length()
      2. String concat(String str)拼接
      3. char charAt(int index)
      4. int indexOf(String str)返回第一次出现的索引位置,若没有,返回-1
      System.out.println(str.length());//5
      System.out.println(str.concat(str2));//HELLOHELLO
      System.out.println(str==str.concat(""));//true
      System.out.println(str.charAt(2));//L
      System.out.println(str.indexOf('L'));//2
      
      
    3. 与截取有关的方法

      1. substring(int index)
      2. substring(int begin,int end) 截取范围[begin,end)
      String str4="HELLOWORLD";
      System.out.println(str4.substring(5));//WORLD
      System.out.println(str4.substring(5,7));//WO
      
      
    4. 与转换相关的方法

      1. public char[] toCharArray();转换为字符数组
      2. public byte[] getBytes()
      3. public String replace(charSequence oldString,charSequence newString):将字符串中的子旧字符串转换为新字符串
      char[] c2=str4.toCharArray();
      System.out.println(c2[2]);//L
      byte[] b2=str4.getBytes();
      System.out.println(b2[1]);//69
      System.out.println(str4.replace("LLO","WOAINI"));//HEWOAINIWORLD
      String str5="会不会玩啊,我你大爷";
      System.out.println(str5.replace("你大爷","***"));//会不会玩啊,我***
      
      
    5. 字符串的分割方法

      1. public String[] split(String regex)按规则将字符串切分成若干个部分

        注意,这里的regex其实是一个正则表达式,如果regex是.则会切分失败,应该写为\.

      String str6="aaa,bbb,ccc,ddd";
      String[] strs = str6.split(",");
      for (int i = 0; i < strs.length; i++) {
      System.out.println(strs[i]);
      }
      /*
      aaa
      bbb
      ccc
      ddd
      */
      
      
  4. 字符串练习

    //统计一个字符串中大写字母小写字母数字以及其他出现的次数
    public class StringPracticeTwo {
        public static void main(String[] args) {
            Scanner sc=new Scanner(System.in);
            System.out.println("请输入一个字符串");
            String str=sc.next();
            getTypeNumber(str);
    
        }
        public static void getTypeNumber(String str){
            byte[] b=str.getBytes();
            int q=0,w=0,e=0,r=0;
            for (int i = 0; i < b.length; i++) {
                if(b[i]>=48&&b[i]<58)
                    e++;
                else if(b[i]>=65&&b[i]<91)
                    q++;
                else if(b[i]>=97&&b[i]<123)
                    w++;
                else
                    r++;
    
            }
            System.out.println("大写字母:"+q+"小写字母:"+w+"数字:"+e+"其他字符:"+r);
        }
    }
    
    
    

Arrays

  • 概念:java.util.Arrays是一个与数组相关的工具类,里面提供了大量的静态方法,越来实现数组的常见操作

  • 基本方法:

    1. public static String toString(数组)将参数变成字符串

      int[] array={10,20,30};
      System.out.println(Arrays.toString(array));//[10, 20, 30]
      
      
    2. public static void sort(数组)按照默认从小到大进行排序

      int[] array2={1,3,4,6,55,1,34,2,6,7,98,67,43,1};
      Arrays.sort(array2);
      System.out.println(Arrays.toString(array2));//[1, 1, 1, 2, 3, 4, 6, 6, 7, 34, 43, 55, 67, 98]
      String[] str={"aaa1","aaa2","aaa","bbb","bac"};
      Arrays.sort(str);
      System.out.println(Arrays.toString(str));//[aaa, aaa1, aaa2, bac, bbb]
      
      

      注意:

      • 如果是数值,默认从小到大
      • 如果是字符串,按字母排序
      • 如果是自定义类型,那么需要comparable或者comparator支持

Collections

  • 基本概念:java.utils.collections是集合工具类,用来对集合进行操作

  • 常用方法:

    1. static boolean addAll(Collection c,T…element):往集合里面批量添加元素

      ArrayList<String> list=new ArrayList<>();
      Collections.addAll(list,"123","2312","21121","0","9");
      System.out.println(list);//[123, 2312, 21121, 0, 9]
      
      
    2. static void shuffle(List<?> list):打乱集合顺序

      Collections.shuffle(list);//洗牌
      System.out.println(list);//[0, 2312, 21121, 9, 123]
      
      
    3. static void sort(List list)按默认(升序)排序

      Collections.sort(list);
      System.out.println(list);//[0, 123, 21121, 2312, 9]
      
      
      • 注意:

        1. 如果排序的是字符串,哪怕是数字的字符串,也是按照字符串的比较方式排序的

        2. 只有Person类实现了Comparable接口(重写了其compareTo的方法)的才可以用sort排序

          public class Person implements Comparable<Person>{
              @Override
              public boolean equals(Object o) {
                  if (this == o) return true;
                  if (o == null || getClass() != o.getClass()) return false;
                  Person person = (Person) o;
                  return age == person.age &&
                          Objects.equals(name, person.name);
              }
              @Override
              public int compareTo(Person o) {
                  return this.age-o.age;//升序
                  //return o.age-this.age;//降序
              }
          }
          
          
        3. 注意看Person类里重写的compareTo方法,看实现升序和降序的不同方式

          Person s1=new Person("zhangsan",12);
          Person s2=new Person("lisi",22);
          Person s3=new Person("wangwu",19);
          ArrayList<Person> list1=new ArrayList<>();
          Collections.addAll(list1,s1,s2,s3);
          Collections.sort(list1);
          System.out.println(list1);//[Person{name='zhangsan', age=12}, Person{name='wangwu', age=19}, Person{name='lisi', age=22}]
          
          ArrayList<Integer> list2=new ArrayList<>();
          Collections.addAll(list2,1,2,3,4,5);
          //重写Camparator接口的compare方法可以使用匿名内部类
          Collections.sort(list2, new Comparator<Integer>() {
              @Override
              public int compare(Integer o1, Integer o2) {
                  //return o1-o2;//升序
                  return o2-o1;//降序
              }
          });
          System.out.println(list2);//[5, 4, 3, 2, 1]
          
          

Math

  • 基本概念:Math类中提供了大量与数学相关的静态方法

  • 常用方法:

    1. public static double abs(double number)获取绝对值

    2. public static double ceil(double number)向上取整(向大取整)

      ​ floor(double number)向下取整(向小取整)

      ​ long round(double number)四舍五入

    3. Math.PI近似的圆周率量

    public class TestMath {
        public static void main(String[] args) {
            System.out.println(Math.abs(-12));//12
            System.out.println(Math.ceil(-12.1));//-12.0
            System.out.println(Math.ceil(12.1));//13.0
            System.out.println(Math.floor(12.1));//12.0
            System.out.println(Math.round(12.1));//12
        }
    }
    
    
  • 练习

    //计算-10.8到5.9之间,绝对值大于6或者小于2.1的整数的个数
    public class MathPractice {
        public static void main(String[] args) {
            int z=0;
            for(int i=-10;i<6;i++){
    
                if(Math.abs(i)>6||Math.abs(i)<2.1){
                    z++;
                }
            }
            System.out.println(z);
            int p=-7;
            System.out.println(Math.abs(p));
            System.out.println(p);
        }
    }
    
    

修饰符static

  • 基本概念:

    1. 一旦使用了static,那么这样子的内容不在属于对象,而是属于类,凡是本类的对象,共享一个static
    2. 如果有了static,可以直接通过类名称调用(推荐),也可以通过对象名调用(不推荐)
    3. 如果没有static,必须通过对象名调用
    4. 注意:静态不可以访问非静态,因为内存中是先有的静态内容,后有的非静态内容
    5. 静态方法中不可以使用this因为this代表当前对象,通过谁调用的方法,谁就是当前对象,但是静态方法和对象无关,只和类有关
    6. 我们都知道内存主要有三个区域,栈,堆,和方法区,在方法区中有一个静态区专门存放静态数据
    7. 静态代码块,当第一次使用本类的时候,静态代码块执行唯一的一次
      • 因为静态内容总是优于非静态,所以静态代码块总是比构造方法先执行执行执行
      • 静态代码块的典型用途:一次性的对静态成员变量进行赋值
  • 例子

    public class Student {
        private  String name;
        private int age;
        static String room;
        static{
            System.out.println("静态代码块执行");
        }
        public Student() {
            System.out.println("构造代码块执行");
        }
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    public class TestStaticBlock {
        public static void main(String[] args) {
            Student s1=new Student();
            Student s2=new Student();
            /*静态代码块执行
            构造代码块执行
            构造代码块执行*/
        }
    }
    
    

继承

  • java有三大特性:封装性,继承性,多态性
  1. 继承性—>共性抽取(单继承,多级继承,最高父类是java.lang.Object)
    父类:超类,基类
    子类:派生类
    父类有的,子类一定有,同时,子类可以有自己的专有的内容,
    在继承的关系中,子类就是一个父类,例如父类是员工,子类是老师,我们可以说老师是员工,即子类可以 被当作父类

  2. 直接通过子类对象访问成员变量
    等号左边是谁,就优先用谁,没有则向上找
    间接通过成员方法访问成员变量
    该方法属于谁,就优先用谁,没有则向上找

    public class Fu {
        int num=100;
        public void methodfu(){
            //使用的是本类当中的num,不会向下找子类的
            System.out.println(num);
        }
    }
    public class Zi extends Fu{
        int num=200;
        public void methodzi(){
            //因为本类当中有num,所以用本类的num
            System.out.println(num);
        }
    }
    public static void main(String[] args){
        Zi zi=new Zi();
        //直接通过子类对象访问成员变量,等号左边是谁,就优先用谁,没有则向上找
        System.out.peintln(zi.num);//优先子类 200
        //这个方法是子类的,优先用子类的,没有再向上找
        zi.methodzi();//200
        //这个方法是再父类当中定义的,
        zi.methodfu();//100
    }
    
    
  3. 三种变量名(num)
    局部变量 num
    本类的成员变量 this.num
    父类的成员变量 super.num

  4. 重写(override)和重载(overload)
    重写要保证方法名相同且参数列表相同
    重载要求方法名相同但是参数列表不同
    重写的注意事项:
    子类方法的返回值必须小于等于父类方法的返回值,例如String<Object
    子类方法的权限必须大于等于父类方法的权限修饰符
    public > protected > default > private

  5. 继承关系中的构造方法
    子类的构造方法中默认有一个super(),所以,子类的构造方法调用前,一定会先调用父类的构造方法
    子类构造可以通过super关键字来调用父类重载构造
    super的父类构造调用,必须是子类构造方法的第一个语句,也就是说子类构造不可以多次调用父类构造
    总结就是子类必须调用父类构造方法,不写则默认隐含super()以调用父类无参构造方法,写了则用写的指定的super调用
    super只可以有一个,也必须是第一个

抽象方法&抽象类

  • 概念

    • 抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束

    • 抽象类:抽象方法所在的类必须是抽象类

      public abstract class Animal {
          public abstract void eat();
          //abstract中可以有普通的成员方法
          public void normalMethod(){
      
          }
      }
      
      
  • 如何使用

    • 不可以直接使用new创建抽象类对象
    • 必须使用子类来继承抽象父类
    • 子类必须实现父类所有的抽象方法
    • 注意:抽象类不一定有抽象方法,抽象方法必须在抽象类中

super和this

总结super的三种方法
    1.调用父类成员变量
    2.调用父类成员方法
    3.调用父类构造方法
总结this的三种方法
    1.在本类的成员方法中,访问本类的成员变量
    2.在本类的成员方法中,访问本类的另一个成员方法
    3.在本类的构造方法中,访问本类的另一个构造方法
public class Zi extends Fu{
    int num=20;

    public Zi(){
        super();//调用了父类的构造方法
    }
    public Zi(int num){
        this();//调用了本类的构造方法,注意,这里与super类似,在构造方法中只可以唯一出现在第一行
    }
    public void method(){
        this.method();//调用了本类的成员方法
        super.method();//调用了父类的成员方法
        System.out.println(super.num);//调用了父类的成员变量
        System.out.println(this.num);//调用了子类的成员变量
    }
}


第三天内容

接口

  • 概念:接口就是多个类的公共规范,是一种引用数据类型,其中最重要的就是其中的:抽象方法

  • 接口再不同版本可以包含的

    • jdk1.8 常量,抽象方法
    • jdk8 默认方法,静态方法
    • jdk9 私有方法
  • 注意:

    1. 接口不可以直接使用,必须有一个实现类来实现该接口
      1. 接口的实现类必须覆盖重写实现类中的所有抽象方法
      2. 若实现类没有覆盖所有的抽象方法,则该实现类就必须是抽象类
    2. 接口中可以有成员变量,但是必须使用public static final来进行修饰,所以从效果上来看,实际上就是接口的常量
    3. 接口中没有静态代码块和构造方法
    4. 一个类只有一个父类,但是一个类可以实现多个接口,同时,接口与接口之间是多继承的
      1. 多个父接口当中的抽象方法如果重复,没关系
      2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且必须带着default
    5. 如果实现的多个接口有重复的抽象方法,那么只要覆盖重写一个就可以
    6. 若实现类不完全覆盖重写接口的所有抽象方法,那该实现类就必须是一个抽象类
    7. 如果实现的多个接口有重复的默认方法,那么要对冲突的默认方法进行覆盖重写
    8. 一个类如果直接父类的方法和接口的默认方法冲突了,那么优先使用父类的方法
    public interface MyInterface {
    
        //使用final修饰代表不可变,说明不可变,idea编译器中,灰色关键字代表强制默认,不写也是他但是不可以写别的
        //接口中的变量必须手动赋值
        //而且作为习惯,不可以变的值最好全部大写,用下划线分割
        public static final int NUM=1;
        int NUM_OF_MY_CLASS =2;//摁住shift+f6可以将小写全部换为大写
    
        //接口内的抽象方法,前两个关键字必须是public abstract,不写也是他,可以选择性省略,但是绝对不可以写别的关键字
        //方法的三要素可以随意定义
        public abstract void methodAbs();
        public void methodAbs3();
        abstract void methodAbs4();
        void methodAbs2();
    
    
        //假如已经有类实现了该接口,那么如果该接口升级了一些新添加的方法,则他的所有实现类都会报错,都必须实现
        //这是很麻烦的,所以我们可以定义默认方法,其实现类会默认继承该默认方法,也可以对该默认方法进行覆盖重写
        //注意,默认方法必须有方法体
        public default void methodDef(){
            System.out.println("这是接口的默认方法");
            common();
        }
    
    
        //静态方法,必须有方法体,且不可以被实现类的对象进行调用,正确用法是通过接口名称进行调用
        public static void methodSta(){
            System.out.println("这是接口的静态方法");
            commonSta();
        }
    
        //当多个默认方法有重复代码的时候,我们可以将其重复代码提取出来再做成一个方法,但是这个方法绝对不可以
        /*是默认方法,因为如果是默认方法,其实现类就也会拥有这个方法,所以应该定义为私有方法(java9)
                普通私有方法:解决多个默认方法之间重复代码的问题
                静态私有方法:解决多个静态方法之间重复代码的问题
    
        */
        private void common(){
            System.out.println("默认方法调用了普通私有方法");
        }
        private static void commonSta(){
            System.out.println("静态方法调用了静态私有方法");
        }
    }
    
    
    

多态性

  • 概念:多态性是针对对象说的,比如说小明是一个对象,既有学生形态,拥有人类形态,一个对象有多种形态,就是多态性

  • 注意:

    1. 代码中体现多态性,其实就是:父类引用指向子类对象

      1. 父类名称 对象名=new 子类名称();
      2. 接口名称 对象名=new 实现类名称();
        • 调用方法的时候,看等号右边是谁,有则调用没有则向上找
        • 直接调用成员变量的时候,看等号左边是谁,有则调用,没有则向上找
        • 注意的是,变量是不可以重写的
        • 通过成员方法调用成员变量的时候,则要看等号右边,一句话,和方法有关看右边,直接和变量有关看左边
        • 编译看左,运行看右
    2. 多态性的好处:无论右边new的对象发生什么变化,等号左边都不会发生变化,减少了修改量

  • 向上转型:其实就是多态写法,含义是:创建一个子类对象,把他当作父类来看待

    • 向上转型一定是安全的,是从小范围转向了大范围
    • 但是也有个弊端,就是无法调用子类特有的方法了
    • 解决方案:用向下转型还原
  • 向下转型:其实是一个还原的过程 子类名称 对象名=(子类名称)父类对象-----》类似与强制类型转换,将父类对象还原为本来的子类对象,但必须还原对象本身就是子类对象,如果不是,会产生ClassCaseException运行时异常

  • 那么问题来了,我怎么知道这个对象本身是属于什么类呢,这里就要用到instanceof方法

    public class TestMutil {
        public static void main(String[] args) {
            Fu obj=new Zi();
            obj.method();
            System.out.println(obj.num);//10
            //obj.methodZ();
            Zi zi=(Zi)obj;
            zi.methodZ();//向下转型后就可以使用子类的特有方法了
            System.out.println(obj instanceof Zi);//注意用法,然后会返回一个boolean值
        }
    }
    
    

final

final常见的四种用法(代表不可变的)
1.修饰一个类 public final class 类名称,代表这个类不可以有子类,所以其所有的方法都不可以被覆盖重写
2.修饰一个方法 public final void 方法名称,代表这个方法就是最终方法,不可以被覆盖重写
注意:abstract和final不可以同时存在,是互相冲突的
3.修饰一个局部变量 final int num=20 必须保持只有唯一一次赋值,终生不变,不管值是否改变,就是不可以赋值
对于引用类型来说是地址不可变,但是内容可以变
4.修饰一个成员变量
由于成员变量具有默认值,所以用了final后必须手动赋值,不会有默认值了
对于final的成员变量,要么直接赋值,要么通过构造方法赋值,两个选一个

四种权限修饰符

java有四种权限修饰符
public > protected > (default) > private
同一个类 yes yes yes yes
同一个包 yes yes yes no
不同包子类 yes yes no no
不同包非子类 yes no no no

外部类和内部类

匿名内部类

  • 概念:如果接口类的实现类(或者是父类的子类)只使用一次,那么这种情况下就可以省略掉该类的定义,改为使用【匿名内部类】

  • 格式:

    ​ 对应格式: 接口名称 对象名=new 接口名称(){
    ​ //覆盖重写所有的抽象方法
    ​ };

  • 注意事项:对 new 接口名称(){…}进行解析

    1. new代表创建对象的当作

    2. 接口名称就是匿名内部类需要实现哪个接口

    3. {。。。}才是匿名内部类的内容

      另外注意:

    • 匿名内部类再创建对象的时候,只可以使用唯一的一次,如果想使用多次,那还是创建实现类吧
    • 匿名对象是再调用方法的时候,只可以调用唯一的一次,如果下调用多次,你还是起个名字吧
    • 匿名内部类是省略了实现类/子类,匿名对象是省略了名称,两者注意区分
    public class TestMain {
        public static void main(String[] args) {
    //        MyInterface obj=new MyInterfaceImpl();
    //        obj.method();
            //使用了匿名内部类,所以不必再单独创建实现类,直接再main方法内创建匿名内部类就可以,直接new接口就可以
            MyInterface  some=new MyInterface() {
                @Override
                public void method() {
                    System.out.println("匿名内部类覆盖重写了方法");
                }
            } ;//这个分号不要丢了
    
            some.method();
        }
    }
    
    

Calendar

package cn.itcast.day06.demo09;

import java.util.Calendar;

public class TestCalendar {
    public static void main(String[] args) {
        Calendar instance = Calendar.getInstance();
        System.out.println(instance);
        demo01();
        System.out.println("============");
        demo02();
        System.out.println("============");
        demo03();
        System.out.println("============");
        demo04();
    }
    /*
      getTime()方法将日历对象转换为Date对象
     */
    private static void demo04() {
        Calendar c = Calendar.getInstance();
        System.out.println(c.getTime());
    }

    /*
    public abstract void add(int field,int amount)越来个field增加或者减少amount
     */
    private static void demo03() {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.YEAR,6);
        System.out.println(c.get(Calendar.YEAR));

    }


    /*
    set(int field,int value)将指定的field设置为value
     */
    private static void demo02() {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR,2020);//将年份设置为2020
        System.out.println(c.get(Calendar.YEAR));

        //也可以使用重载的set方法同时设置年月日
        c.set(8888,8,8);
        System.out.println(c.get(Calendar.YEAR));
        System.out.println(c.get(Calendar.MONTH));
        System.out.println(c.get(Calendar.DAY_OF_MONTH));

    }

    /*get方法根据指定的field返回日历值
        field代表Calendar的静态成员变量,例如输入YEAR,则返回当前的年份

     */
    private static void demo01() {
        Calendar c = Calendar.getInstance();
        System.out.println(c.get(Calendar.YEAR));
        System.out.println(c.get(Calendar.MONTH));
        System.out.println(c.get(Calendar.DAY_OF_MONTH));
    }

}


Date

package cn.itcast.day06.demo09;

import javax.xml.crypto.Data;
import java.util.Date;

/*
java.util.Date表示日期和时间的类,表示特定的时间,精确到千分之一秒也就是毫秒,
    时间原点(0毫秒):1970年1月1日00:00:00
*/
public class TestDate {
    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());//计算出据时间原点有多少毫秒
        demo01();
        demo02();
        demo03();


    }

    private static void demo03() {
        Date date=new Date();
        long time = date.getTime();//该方法把日期转换为毫秒值
        System.out.println(time);
    }

    private static void demo02() {
        Date date=new Date(0L);//data带参的构造方法,将long类型的毫秒值转换为日期
        System.out.println(date);
    }

    private static void demo01() {
        Date date=new Date();
        System.out.println(date);
    }
}


DateFormat

package cn.itcast.day06.demo09;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/*
DateFormat是一个抽象类,所以我们一般用他的子类SimpleDateFormat
    作用是格式化:日期到文本  另一个作用是解析:文本到日期
    成员方法有:
        String format(Date date)按模式将date日期转换为符合模式的字符串
        Date parse(String source)将符合模式的日期解析为date日期
            y   年
            M   月
            d   日
            H   时
            m   分
            s   秒
*/
public class TestDateFormat {
    public static void main(String[] args) throws ParseException {//会抛出parseException解析异常
        demo01();
        System.out.println("==========");
        demo02();
    }

    private static void demo02() throws ParseException {
        DateFormat dateFormat=new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        Date date=dateFormat.parse("2021年01月15日 21时53分08秒");
        System.out.println(date);
    }

    private static void demo01() {
        DateFormat dateFormat=new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        Date date=new Date();
        System.out.println(date);
        System.out.println(dateFormat.format(date));



    }
}


Integer

package cn.itcast.day06.demo09;
/*
    装箱:
        1.构造方法 Integer(int value)
        2.静态方法 static Integer valueOf(int i)
    拆箱
        1.int intValue()
 */
public class TestInteger {
    public static void main(String[] args) {
        //装箱
        //构造方法
        Integer i=new Integer(1);//方法上有横线,代表该方法过时了
        //静态方法
        Integer i2=Integer.valueOf(1);
        System.out.println(i2);


        //拆箱
        int i3=i2.intValue();
        System.out.println(i3);


        demo01();
        demo02();
    }

    private static void demo02() {
        //字符串到基本数据类型的转换
        String str="123";
        System.out.println(Integer.parseInt(str)+1);//124
    }

    /*
        现在已经有了自动装箱和拆箱了
     */
    private static void demo01() {
        Integer i=1;//自动装箱
        System.out.println(i);
        int i2=i;//自动拆箱
        System.out.println(i2);
    }
}


StringBuilder

package cn.itcast.day06.demo09;
/*
1.String是常量,创建以后不可以改变,但是字符串缓冲区可以改变,也就是StringBuilder
    例如:我们进行字符串的相加,内存中就会有多个字符串,占用空间多,效率低下,比如三个字符串相加,则总共
         会产生五个字符串
2.StringBuildery可以提高字符串的操作效率,他的底层也是一个byte类型的数组,但是没有被final修饰,
     可以看作一个可以改变长度的数组(自动扩容),可以改变长度
 */
public class TestStringBuilder {
    public static void main(String[] args) {
        //构造方法
        StringBuilder stringBuilder=new StringBuilder();
        System.out.println(stringBuilder);//""默认是一个空字符串

        //带字符串的构造方法
        StringBuilder stringBuilder1 = new StringBuilder("abc");
        System.out.println(stringBuilder1);

        demo01();
        demo02();
    }



    /*
        StringBuilder的常用方法
            1.public StringBuilder append(...):添加任意类型数据的字符串形式,并返回当前对象自身
            2.String 和 StringBuilder互相转换的方法
                string-->stringbuilder:使用StringBuilder(String str)构造一个字符串生成器,并初始化为指定的字符串
                StringBuilder-->String: 使用StringBuilder中的toString方法
     */
    private static void demo01() {
        StringBuilder stringBuilder = new StringBuilder();
        StringBuilder stringBuilder1 = stringBuilder.append("abc");//把b的地址给了b1
        System.out.println(stringBuilder==stringBuilder1);//true  比较的是地址,所以append方法比较特殊
        /*
        append方法返回的是this,也就是调用方法的对象stringbuilder
         */
        stringBuilder.append("abc");
        stringBuilder.append('c');
        stringBuilder.append(102.9);
        stringBuilder.append(1);
        System.out.println(stringBuilder);//abcabcc102.91

        /*
        链式编程:方法返回值是一个对象,可以继续调用方法,一行就搞定了
         */
        stringBuilder.append("def").append(true).append("我爱你");
        System.out.println(stringBuilder);
    }


    //互相转换
    private static void demo02() {
        String str="Hello";
        System.out.println("str:"+str);
        StringBuilder bu=new StringBuilder(str);
        bu.append("World");
        System.out.println("bu:"+bu);
        String str2=bu.toString();
        System.out.println("str2:"+str2);
    }
}


System

package cn.itcast.day06.demo09;


import java.util.Arrays;

/*
System类用来获得与系统相关的信息
 */
public class TestSystem {
    public static void main(String[] args) {
        demo01();
        demo02();
    }

    /*
    static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
    用来复制数组
        src - 源数组。
        srcPos - 源数组中的起始位置。
        dest - 目标数组。
        destPos - 目标数据中的起始位置。
        length - 要复制的数组元素的数量。

     */
    private static void demo02() {
        int[] array1={1,2,3,4,5};
        int[] array2={6,7,8,9,10};
        System.out.println("输出前:"+ Arrays.toString(array2));
        System.arraycopy(array1,0,array2,0,3);
        System.out.println("输出后:"+Arrays.toString(array2));

    }

    /*
    public static long currentTimeMillis():返回以毫秒为单位的当前时间
    可以用来反应系统性能
     */
    private static void demo01() {
        long l = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            System.out.println(i);

        }
        long l1 = System.currentTimeMillis();
        System.out.println(l1 - l);
    }
}


第四天内容-Collection

Collection

  • 集合成员:

    collection接口包含
    1. List接口
    有序的集合(存储和取出元素的顺序相同)
    允许存储重复的元素
    有索引,可以for循环遍历
    a.Vector集合
    b.ArrayList集合
    c.LinkedList集合
    2. set接口
    不允许存储重复元素
    没有索引,不可以使用普通的for循环遍历
    a.TreeSet集合(无序)
    b.HashSet集合(无序)
    LinkedHash集合(有序)

package cn.itcast.day07.demo01;

import java.util.ArrayList;
import java.util.Collection;

/*
    研究collection接口,为学习他的子类打下基础,他是所有单列集合的最顶层接口
    常用的方法
        1.boolean add(E e)
        2.void clear();清空所有元素
        3.boolean remove(E e)把给定对象在当前集合删除
        4.boolean contains(E e)判断当前集合是否有包含给定的对象
        5.boolean isEmpty()
        6.int size();
        7.Object[] toArray();//把集合当中的元素,存储到数组中

 */
public class TestCollection {
    public static void main(String[] args) {
        Collection<Integer> coll=new ArrayList<>();
        System.out.println(coll);//[]
        System.out.println(coll.add(2));//true
        System.out.println(coll.add(3));//true
        System.out.println(coll.add(4));//true
        System.out.println(coll.add(56));//true
        System.out.println(coll.add(12));//true
        System.out.println(coll);//[2, 3, 4, 56, 12]
        System.out.println(coll.size());//5
        System.out.println(coll.isEmpty());//false
        System.out.print("删除元素:");
        System.out.println(coll.remove(4));//删除元素:true
        System.out.println(coll.remove(90));//删除失败,因为没有90
        System.out.println(coll);//[2, 3, 56, 12]
        System.out.print("包含90吗:");
        System.out.println(coll.contains(90));//包含90吗:false
        System.out.print("包含2吗:");
        System.out.println(coll.contains(2));//包含2吗:true
        System.out.print("遍历数组");
        Object[] a=coll.toArray();
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+"  ");
        }//遍历数组2  3  56  12 

    }

}


Foreach

package cn.itcast.day07.demo01;

import java.util.ArrayList;
import java.util.Collection;

/*
    public interface Iterable<T>  实现这个接口允许对象成为foreach语句的目标,也就是增强for循环
    其内部其实也是实现了迭代器,使用for循环的格式,简化了迭代器的书写
        增强for循环用来遍历集合和数组

 */
public class TestForeach {
    public static void main(String[] args) {
        demo01();
        demo02();
    }
    //使用增强for循环遍历集合
    private static void demo02() {
        //创建一个集合对象
        Collection<String> coll=new ArrayList<>();
        //往集合里面添加元素
        coll.add("詹姆士");
        coll.add("艾佛森");
        coll.add("科比");
        coll.add("奥尼尔");
        coll.add("杜兰特");
        for (String s:coll) {
            System.out.println(s);
        }
        /*
        詹姆士
        艾佛森
        科比
        奥尼尔
        杜兰特
         */
    }

    //使用了增强for循环遍历集合
    private static void demo01() {
        int[] arr={1,2,3,4};
        for (int i:arr) {
            System.out.println(i);
        }
        /*
            1
            2
            3
            4

         */
    }
    
}


Iterator

package cn.itcast.day07.demo01;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/*
    不同集合存储元素的方式不同,迭代的方法也不同,所以我们设置一个迭代器,方便的对各种集合
    进行迭代遍历,iterator也被成为迭代器
    有两个常用的方法
        1.booelan hasNext() 看还有没有下一个元素
        2.E next()  返回迭代的下一个元素
    注意:
        1.Iterator是一个接口,无法直接使用,需要使用他的实现类对象,获取实现类的方式比较特殊
            Collection接口中有一个方法,叫iterator(),这个方法返回的就是Iterator迭代器的实
            现类对象

 */
public class TestIterator {
    public static void main(String[] args) {
        //创建一个集合对象
        Collection<String> coll=new ArrayList<>();
        //往集合里面添加元素
        coll.add("詹姆士");
        coll.add("艾佛森");
        coll.add("科比");
        coll.add("奥尼尔");
        coll.add("杜兰特");

        //下面是使用迭代器的步骤
        //1.获取迭代器的同时,【并且会把迭代器的指针指向集合的-1索引】
        Iterator<String> it = coll.iterator();//注意,iterator也是有泛型的
        //2.判断还有没有下一个元素
        System.out.println(it.hasNext());
        //3.有则取出一个元素【注意:如果没有元素,会抛出NoSuchElementException异常】
        /*
        next作了两个事情
            1.取出当前索引的下一个元素【比如原来是-1,现在取出0索引的元素】
            2.然后指针后移以为【迭代器的指针指向0】
            所以,迭代器的指针总是指向被取出元素的前一位
         */
        System.out.println(it.next());


        //优化后的代码
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }

}


泛型

package cn.itcast.day07.demo02;

import java.util.ArrayList;
import java.util.Iterator;

/*
    泛型的上限限定:? extends E     代表使用的泛型只可以是e类型的子类或本身
    泛型的下限限定:? super E   代表使用的泛型只可以是e类型的父类或本身
 */
public class TestGeneric {
    public static void main(String[] args) {
        //demo01();
        //demo02();
        //demo03();
        //demo04();//使用了泛型的方法
        //demo05();//使用了泛型的静态方法
        ArrayList<Integer> list=new ArrayList<>();
        list.add(12);
        list.add(111);
        ArrayList<String> list1=new ArrayList<>();
        list1.add("hejiale");
        list1.add("woaini");
        prinArray(list);
        prinArray(list1);
        
        

    }
    public static void prinArray(ArrayList<?> list){
        Iterator<?> it=list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }

    public static void demo05() {
        UseGeneric.method02("string");
        UseGeneric.method02(1);
    }

    /*
        使用泛型的方法
     */
    private static void demo04() {
        UseGeneric useGeneric=new UseGeneric();
        useGeneric.method01("String");
        useGeneric.method01('c');
        useGeneric.method01(123);
        useGeneric.method01(true);

    }


    /*
        使用了泛型
            好处:
                1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
                2.把运行时异常提升到了编译时异常
            弊端:
                泛型是什么类型,集合中就必须存储什么类型,损失了多样性
     */
    private static void demo02() {
        ArrayList<String> list=new ArrayList<>();
        list.add("hjl");
        //list.add(1);会发生报错,因为使用了泛型,集合只可以存储String
        Iterator it=list.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

    }

    /*
        创建集合对象,不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
            但是弊端就是不安全,有可能引发异常

     */
    private static void demo01() {
        ArrayList list = new ArrayList();
        list.add("abc");
        list.add(1);
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Object obj=it.next();
            System.out.println(obj);
            //但是,这个时候如果想使用String特有的方法是不可以的,因为多态,比如length就不可以
            //需要向下转换
            String s=(String)obj;
            System.out.println(s.length());
            //但是,当循环到1的时候会发生异常,因为1不是String,不可以向下转换为String
        }




    }
    /*
        使用泛型的类
     */
    private static void demo03() {
        UseGeneric useGeneric = new UseGeneric();
//        useGeneric.setName("只可以是字符串");
//        System.out.println(useGeneric.getName());
        useGeneric.setName(1);//使用了泛型,不强调必须是String


    }
}


/*
    定义含有泛型的接口
 */
public interface GenericInterface<I>{
    void method(I i);
}


package cn.itcast.day07.demo02;
/*
    含有泛型的接口有两种使用方式:
        1.定义接口的实现类,实现接口,指定接口的泛型,当接口的泛型确定下后,重写的接口方法的泛型研究会确定下来


 */
public class GenericInterfaceImpl implements GenericInterface<String>{

    @Override
    public void method(String s) {
        System.out.println(s);
    }
}


package cn.itcast.day07.demo02;
/*
    含有泛型的接口的第二种使用方法:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走,
    就相当于定义了一个含有泛型的类,创建对象的时候,确定泛型的类型
 */
public class GenericricInterfaceImp2<E> implements GenericInterface<E>{

    @Override
    public void method(E e) {
        System.out.println(e);
    }
}


package cn.itcast.day07.demo02;

/*
    定义一个含有泛型的类,模拟ArrayListihe
 */
public class UseGeneric<E> {
    private E name;

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }
    //定义一个含有泛型的方法  <m>应该写在方法修饰符和返回类型之间
    public <M> void method01(M m){
        System.out.println(m);
    }
    public static <M> void method02(M m){
        System.out.println(m);
    }
}


//扑克牌游戏
package cn.itcast.day07.demo03;

import java.util.ArrayList;
import java.util.Collections;

public class PokerGame {
    public static void main(String[] args) {
        //组装牌
        ArrayList<String> list=new ArrayList<>();
        list.add("大王");
        list.add("小王");
        String[] color={"♠","♥","♣","♦"};
        String[] number={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
        for (String s : color) {
            for (String s1 : number) {
                list.add(s+s1);
            }
        }
        System.out.println(list);
        //2.洗牌
        Collections.shuffle(list);//这个方法是java.util.Collections下的静态方法
        System.out.println(list);
        //3.发牌
        //定义四个集合,三个用来存储牌,一个用来存储底牌
        ArrayList<String> list1=new ArrayList<>();
        ArrayList<String> list2=new ArrayList<>();
        ArrayList<String> list3=new ArrayList<>();
        ArrayList<String> dipai=new ArrayList<>();

        for(int i=0;i<list.size();i++){
            int j=i%3;
            if(i>=51) dipai.add(list.get(i));
            else if(j==0)list1.add(list.get(i));
            else if(j==1)list2.add(list.get(i));
            else list3.add(list.get(i));
        }
        //4.看牌
        System.out.println("周润发:"+list1+" "+list1.size()+"张牌");
        System.out.println("周星驰:"+list2+" "+list2.size()+"张牌");
        System.out.println("刘德华:"+list3+" "+list3.size()+"张牌");
        System.out.println(dipai);
    }
}


ArrayList

package cn.itcast.day07.demo04;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/*
    1.java.util.List接口 extends collection
    特点:
        1.有序的集合:存储的顺序和取出的顺序一样
        2.有索引
        3.允许存储重复的元素
    方法:
        void add(int index,E element)
        E get(int index)
        E remove(int index)
        E set(int index,E element)
    注意:防止索引的越界异常
    2.ArrayList的特点
        1.底层是可变长的数组,所以查询快增删慢
        2.多线程
 */
public class TestArrayList {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("d");
        System.out.println(list);
        list.add(0,"我是新插入的");
        System.out.println(list);
        System.out.println(list.remove(1));
        System.out.println(list);
        System.out.println("被替换的元素:"+list.set(3,"D"));
        System.out.println(list);


        /*list遍历方式有三种
            1.普通的for循环
            2.迭代器方法
            3.使用增强for循环
        */
        //for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println("============");
        //迭代器方法
        Iterator iterator=list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("============");
        //使用增强for循环
        for (String s : list) {
            System.out.println(s);
        }
    }
}


LinkedList

package cn.itcast.day07.demo04;

import java.util.LinkedList;
import java.util.List;

/*
    LinkedList是一个双向链表,有一个尾指针和首指针,找到头和尾非常方便
        1.多线程
        2.增删块,查询慢
        3.有大量的首尾操作的方法
    方法:
        E getFirst()
        E getLast()
        E removeFirst()
        E removeLast()
        E pop()弹出第一个元素
        E push()推入一个元素
        E addFirst()
        E addLast()
    注意:使用LinkedList特有的方法就不可以使用多态
 */
public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> linkedList=new LinkedList<>();
        linkedList.add("head");
        linkedList.add("tail");
        linkedList.addFirst("1");
        linkedList.addLast("2");
        System.out.println(linkedList);//[1, head, tail, 2]

        System.out.println(linkedList.getFirst());//1
        System.out.println(linkedList.getLast());//2 如果是空链表,则会抛出NoSuchElementException异常

    }
}


HashSet

package cn.itcast.day07.demo04;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/*
    java.util.Set接口 extends Collection接口
    1.set接口的特点:
        1.不允许值重复的元素
        2.没有带索引的方法,不可以使用for循环遍历
    2.HashSet
         1.哈希表结构,查询的速度非常快
         2.无序结构,无序是指存储和取出的顺序可能不一样
         哈希表:
            jdk1.8之前是:数组加链表
            之后是:数组加红黑树(当链表超过8位的时候,会将链表转换为红黑树以加快查询速度)
    3.set不允许存储重复值的原理
        1.set调用add方法的时候,add方法会调用hashCode方法和equals方法,判断元素哈希值是否重复,如果
        没有,就把s1存储到集合里面
        2.如果哈希值冲突了,就会对两者调用euqals方法,若返回true
        则不会把元素存储到set里面,若为false,则返回false
    4.【HashSet存储的元素必须重写了hashCode方法和equals方法】,只有重写了才可以保证元素唯一
        可以使用快捷键类似创建构造方法的方式重写就可以,很简单方便
        例如:如果两个人同名同年龄,就被视为同一个人,就不可以存储到一个hashset里面去


 */
public class TestHashSet {
    public static void main(String[] args) {
        demo01();
        demo02();
}

    private static void demo02() {
        Person p1=new Person("赵丽颖",19);
        Person p2=new Person("赵丽颖",19);
        Person p3=new Person("赵丽颖",19);
        Person p4=new Person("赵丽颖",20);
        Set<Person> s=new HashSet<>();
        s.add(p1);
        s.add(p2);
        s.add(p3);
        s.add(p4);
        System.out.println(s);//只会存入p1和p4
    }

    private static void demo01() {
        Set<Integer> set=new HashSet<>();
//        set.add(1);
//        set.add(1);
//        set.add(1);
//        set.add(1);
//        set.add(1);
//        set.add(1);这样子不会报错,但是集合内会只有1一个元素,所以,set不允许值重复
        System.out.println("===================");
        set.add(1);
        set.add(4);
        set.add(3);
        set.add(2);
        set.add(5);//输出12345
        //使用迭代器遍历
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("===================");
        //使用增强for循环遍历
        for (Integer integer : set) {
            System.out.println(integer);
        }
    }
}


LinkedHashSet

package cn.itcast.day07.demo04;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;

/*
    LinkedHashSet底层是一个哈希表(数组+红黑树)+链表:多了一条链表,用来记录元素的存储顺序
    可以保证元素有序
    1.LinkedHashSet继承了HashSet接口

 */
public class TestLinkedHashSet {
    public static void main(String[] args) {
        HashSet<String> set=new HashSet<>();
        set.add("abc");
        set.add("abd");
        set.add("abe");
        set.add("www");
        System.out.println(set);//[abd, abc, abe, www]说明hashset是无序的,并且不允许重复
        LinkedHashSet<String> linkedHashSet=new LinkedHashSet<>();
        linkedHashSet.add("abc");
        linkedHashSet.add("abd");
        linkedHashSet.add("abe");
        linkedHashSet.add("www");
        System.out.println(linkedHashSet);//[abc, abd, abe, www]是有序的
    }
}


可变参数

package cn.itcast.day07.demo04;
/*
    可变参数:JDK1.5后出现的新特性
    1.使用前提:
        当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数
    2.使用格式:
        定义方法的时候使用
        修饰符 返回值类型 方法名(数据类型...变量名)
    3.可变参数的原理
        可变参数底层是一个数组,根据传递参数个数的不同,会创建不同长度的数组,来存储这些参数
        传递的参数个数可以是0个到多个
    4.注意事项
        一个方法的参数列表,只可以有一个可变参数
        如果方法的参数有多个,那么可变参数必须在参数列表的末尾
    5.可变参数的特殊写法
        public static void method(Object...obj){}
 */
public class TestVarArgs {
    public static void main(String[] args) {

        System.out.println(demo01(1,2,3,4,5,6,7,8,9));
    }
    //计算n个数的和,n的个数不确定
    private static int demo01(int...arr) {
        int j=0;
        System.out.println(arr);//[I@50cbc42f  说明arr是一个数组
        for (int i = 0; i < arr.length; i++) {
            j+=arr[i];
        }
        return j;
    }
}


第五天内容-Map

Map

package cn.itcast.day08.demo01;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/*
    1.Map集合特点
        1.map集合是一个双列集合,一个元素包含两个值,一个是key,一个是value
        2.value和key的数据类型并不要求相同,可以不一样
        3.key保持唯一性,value可以相同
    2.常用的实现类:
        1.HashMap基于哈希表实现,查询非常快,是无序的集合,多线程
        2.LinkedHashMap基于哈希表加链表,有序
            java.util.TestHashMap<k,v> implement Map<k,v>接口
            java.util.LinkedHashMap<k,v> extends TestHashMap<k,v>集合
    3.Map常用方法
        1.public V put(K key ,V value):将指定键与指定的值添加到Map集合里面【注意其返回值】
            返回被替换的值(k重复)或null(k不重复)
        2.V remove(Object k);返回v,k存在返回被删除的值,否则返回null

        3.V get(Object k)
        4.boolean containsKey(Object key)
    4.Map遍历方法
        1.键找值方式:Set<K> KsySet(),把Map集合中的所有key取出来存储到Set集合里面,之后使用迭代器
            或者增强for循环遍历Set集合,然后通过get方法依次获得value,这就是常说的键找值
            的方式
        2.Set<Map.Entry<k,v>> entrySet(),Entry对象用来存储k和value的映射关系,其下有
            两个方法,一个是getKey,一个是getValue方法
            首先,使用该方法获得set集合,然后遍历取出Entry对象,然后使用其内部的两个方
            法就可以实现Map的遍历
 */
public class TestMap {
    public static void main(String[] args) {
        show01();//常用方法
        //show02();//遍历方法1
        //show03();//遍历方法2

    }

    private static void show03() {
        System.out.println("==============");
        Map<String ,String> map=new HashMap<>();
        map.put("1","何佳乐");
        map.put("2","武文强");
        map.put("3","曹越");
        map.put("4","吴超");

        Set<Map.Entry<String,String>> set=map.entrySet();
        for (Map.Entry<String, String> entry : set) {
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
         /*
        1:何佳乐
        2:武文强
        3:曹越
        4:吴超
         */
    }

    private static void show02() {
        Map<String ,String> map=new HashMap<>();
        map.put("1","何佳乐");
        map.put("2","武文强");
        map.put("3","曹越");
        map.put("4","吴超");

        //1.使用KeySet方法获得key值的Set集合
        Set<String> set=map.keySet();
        //2.使用迭代器
        Iterator<String> iterator=set.iterator();
        while(iterator.hasNext()){
            String str=iterator.next();
            //3.使用get方法
            System.out.println(str+":"+map.get(str));
        }
         /*
        1:何佳乐
        2:武文强
        3:曹越
        4:吴超
         */
    }

    private static void show01() {
        Map<String,String> hashMap=new HashMap<>();
        System.out.println(hashMap.put("1","我叫马牛逼"));//null
        System.out.println(hashMap.put("1","奥里给兄弟们"));//我叫马牛逼
        System.out.println(hashMap.put("2","赵丽颖"));//null
        //System.out.println(hashMap.remove("1"));
        System.out.println(hashMap.get("3"));//null
        System.out.println(hashMap.get("2"));//赵丽颖
        System.out.println(hashMap.containsKey("2"));//true
        System.out.println(hashMap.containsKey("4"));//false



    }

}


HashMap

package cn.itcast.day08.demo01;

import java.util.HashMap;
import java.util.Set;

/*
    HashMap存储自定义类型键值
    Map集合保证key是唯一的
        作为Key的元素,必须重写hashcode方法和equals方法,以保证key唯一
 */
public class TestHashMap {
    public static void main(String[] args) {
        show01();//person没有重写hashcode和equals方法,key为String,value为Person
        show02();//person没有重写hashcode和equals方法,Key为Person,则必须重写两个方法
    }

    private static void show02() {
        HashMap<Person ,String> hashMap=new HashMap<>();
        hashMap.put(new Person("四川",10),"lala");
        hashMap.put(new Person("安徽",30),"lulu");
        hashMap.put(new Person("海南",20),"heihei");
        hashMap.put(new Person("四川",10),"lala");
        Set<Person> set=hashMap.keySet();
        for (Person person : set) {
            System.out.println(person+":"+hashMap.get(person));
        }
        /*
        没有重写两个方法的时候输出
        Person{name='四川', age=10}:lala
        Person{name='安徽', age=30}:lulu
        Person{name='四川', age=10}:lala
        Person{name='海南', age=20}:heihei
        可以看出,第一行和第三行完全一样,不符合key不可重复的要求(我们认为名字相同
        年龄相同就是同一个人)
         */
        //之后我们在Person里面重写hashCode方法和equals方法
        /*
        Person{name='海南', age=20}:heihei
        Person{name='四川', age=10}:lala
        Person{name='安徽', age=30}:lulu
        可以看出,符合key唯一的要求了
         */
    }

    private static void show01() {
        HashMap<String ,Person> hashMap=new HashMap<>();
        hashMap.put("1",new Person("何佳乐",19));
        hashMap.put("2",new Person("北京",20));
        hashMap.put("3",new Person("上海",19));
        hashMap.put("1",new Person("成都",19));
        Set<String> set=hashMap.keySet();
        for (String s : set) {
            System.out.println(s+":"+hashMap.get(s));
        }
        /*
        1:Person{name='成都', age=19}
        2:Person{name='北京', age=20}
        3:Person{name='上海', age=19}
        因为key不可以重复,所以成都将何佳乐取代了
         */
    }
}


LinkedHashMap

package cn.itcast.day08.demo01;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

/*
    继承了HashMap
    map接口的hash表和链表实现 具有可预知的迭代顺序,也就是有序
 */
public class TestLinkedHashMap {
    public static void main(String[] args) {
        HashMap<String,String> hashMap=new HashMap<>();
        hashMap.put("a","a");
        hashMap.put("c","c");
        hashMap.put("b","b");
        hashMap.put("d","d");
        System.out.println(hashMap);
        /*
        输出:{a=a, b=b, c=c, d=d},说明HashMap是无序的
         */

        LinkedHashMap<String,String> linkedHashMap=new LinkedHashMap<>();
        linkedHashMap.put("a","a");
        linkedHashMap.put("c","c");
        linkedHashMap.put("b","b");
        linkedHashMap.put("d","d");
        System.out.println(linkedHashMap);
        /*
        输出:{a=a, c=c, b=b, d=d}说明是有序的
         */
    }
}


HashTable

package cn.itcast.day08.demo01;

import java.util.HashMap;
import java.util.Hashtable;

/*
    1.HashTable是map接口的实现,与HashMap不同的地方是【不允许存储null】,且是线程安全的,也就是同步的
    所以速度会慢
    2.HashTable和Vector一样,jdk1.2后已经被更高级的实现类给取代了,但是HashTable的子类
        Properties依然活跃,Properties集合是一个唯一和io流相结合的集合
 */
public class TestHashTable {
    public static void main(String[] args) {
        HashMap<String,String> hashMap=new HashMap<>();
        hashMap.put(null,"a");
        hashMap.put(null,"b");
        hashMap.put("hejiale","a");
        System.out.println(hashMap);//{null=b, hejiale=a}

        Hashtable<String,String> hashtable=new Hashtable<>();
        hashMap.put(null,"a");
        System.out.println(hashtable);//{}不允许存储空值,但是不会产生编译报错
    }
}


JDK9新特性

package cn.itcast.day08.demo01;

import java.util.ArrayList;
import java.util.List;

/*
    JDK9添加了几种集合工厂方法,更方便创建少量元素的集合、map实例,新的List、Set、Map的
    【静态】工厂方法of,可以更方便的创建集合的不可变实例
    使用前提:
        当集合中存储的元素的个数【已经确定】,不再改变的时候使用
    注意:
        1.of方法只适用于List接口,Set接口和Map接口,不适用于接口的实现类
        2.of方法的返回值是一个【不可以改变的集合】,集合【不可以再使用add方法和put方法添加元素】,
            否则会抛出异常
        3.Set接口和Map接口再调用of方法的时候,不可以有重复的元素,否则会抛出异常
 */
public class TestJDK9NewMethod {
    public static void main(String[] args) {
        List<Integer> list=List.of(1,2,3,4);
        System.out.println(list);//[1, 2, 3, 4]
        list.add(5);//UnsupportedOperationException不支持的操作异常

    }
}


debug

package cn.itcast.day08.demo02;
/*
    Debug调试程序:
        可以让代码逐行执行,查看代码的执行过程,调试程序中出现的bug
    使用方式:
        在行号的右边,鼠标左键单机,添加断点(每个方法的第一行)
        右键,选择Debug执行程序
        程序就会停留在第一个断电处
    执行过程:
        f8:逐行执行程序
        f7:进入到方法中
        shift+f8:跳出方法
        f9:跳到下一个断点,如果没有下一个断电,那么就结束程序
        ctrl+f2:跳出Debug模式,结束程序
 */
public class TestDebug {
    public static void main(String[] args) {
        int a=10;
        int b=20;
        int sum=a+b;
        System.out.println(sum);
    }
}


第六天-异常

异常

try{
    //可能会出现异常的代码
}catch(Exception e){
    //异常的处理逻辑
}

1.例如,数组有三个索引,我们通过main方法调用了getElement方法,访问索引为3的元素,但是数组是
没有3这个索引的,JVM就会检测出异常,JVM会做两件事情,
    1.JVM会根据异常产生的原因创建一个异常对象,这个异常对象包含了异常产生的(内容,原因,位置)
        new ArrayIndexOutOfBoundsException("3");
    2.在getElement方法中,没有异常的处理逻辑(try...catch),那么虚拟机就会把异常对象抛出
    给方法的调用者,也就是main方法,让main方法来处理这个异常
但是main方法收到了这个异常对象后,发现自己也没有(try...catch),就继续把这个异常交给JVM
处理
JVM收到了这个异常对象,做了两个事情
    1.把异常对象(内容,原因,位置)以红色的字体打印在控制台上
    2.JVM会终止当前正在进行的java程序,也就是中断处理


2.java异常处理的5个关键字:try catch finally throw throws

3.常见的异常处理方式
    1.多个异常分别处理-----多个try catch语句
    2.多个异常一次捕获,多次处理-----一个try多个catch
        若多个catch里边定义的异常变量有子父类关系,那么子类的异常变量必须写在上面,否则会报错
    3.多个异常一次捕获一次处理 例如catch只捕获Exception,那么别的都不用写
    

throws

package cn.itcast.day08.demo03;

import java.io.FileNotFoundException;
import java.io.IOException;

/*
    异常处理的第一种方式,交给别人处理
    作用:
        当方法内部抛出异常的时候,我们就必须处理这个异常对象,可以使用thorws关键字处理
        异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,交给比尔处理),
        最终交给JVM处理--->中断处理
    使用格式:在方法声明的时候使用
        修饰符 返回值类型 方法名(参数列表) throws AAAException ,BBBException{
            throw new AAAException;
            throw new BBBException;
        }

    注意:
        throws关键字必须写在方法的声明处,
        throws关键字后面的异常必须是Exception或者其子类
        方法内部如果抛出了多个异常对象,那么throws后边也必须声明多个异常,如果抛出的多个
            异常对象有子父类关系,那么直接声明父类异常就可以
        调用了一个声明抛出异常的方法,我们就必须处理声明的异常
            要么继续使用throws,把异常交给方法的调用者处理
            要么try...catch自己处理异常
 */
public class TestThrows {
    public static void main(String[] args) throws IOException {
        readFile("c:\\a.txt");
    }
    /*
        定义一个方法,对文件传送的路径进行合法性判断
        如果路径不是"c:\\a.txt",那么我们就抛出文件找不到异常,告知方法的调用者

        注意:FileNotFoundException是编译异常,我们抛出了编译异常,就必须处理他
     */
    public static void readFile(String fileName) throws IOException {
        if(!fileName.equals("c:\\a.txt")){
            throw new FileNotFoundException("传递的文件路径不是c:a.txt");
        }
        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");//注意IOException是FileNotFoundException的父类,所以只需要抛出父类就可以,而不用两个全抛出
        }
        System.out.println("路径没有问题,打印文件");
        //但是,如果出现问题,那么上面这行代码是无法运行的,这就是throws的弊端,可以用
        //try...catch语句解决

    }

}


throw

package cn.itcast.day08.demo03;

import java.util.Objects;

/*
    throw关键字
    作用:
        可以使用throw关键字在指定的方法中抛出指定的异常
    使用格式:
        throw new Exception或者其子类对象
    注意:
        1.throw关键字必须在方法的内部
        2.throw关键字后面new的对象必须是Exception或者其子类
        3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
            throw关键字后面创建的是RuntimeException或者是其子类对象的时候我们可以不处理
            throw后面是编译异常,我们就必须处理这个异常,要么throw,要么try...catch


*/
public class TestThrow {
    /*
        定义一个方法,获取数组指定索引处的元素
        参数:
            int[] arr
            int index
        以后在工作中,我们必须首先对传递过来的参数进行合法性检验,如果参数不合法,那么
        我们就必须使用抛出异常的方式,告知方法的调用者,传递的参数有问题
     */
    public static void main(String[] args) {
        int[] arr={1,2,3};
        System.out.println(getElement(arr,3));

    }

    public static int getElement(int[] arr,int index){
        //对传递的参数进行合法性检验,若arr为null,则抛出空指针异常,
        if(arr==null){
            throw new NullPointerException();//该异常属于运行期异常,我们可以不处理,默认交给JVM处理

        }
        //注意:其实Object类中定义了一个方法就是用来检验是不是空的
        //【Objects.requireNonNull(arr,"空指针异常");】

        //对index进行合法性检验,看是否在合理的区间内,否则抛出越界异常
        if(index<0||index>arr.length-1){
            throw new ArrayIndexOutOfBoundsException("数组索引越界了");//也是运行时异常
        }
        int ele=arr[index];
        return ele;
    }
}

try/catch

package cn.itcast.day08.demo03;
/*  try{
        //可能会出现异常的代码
    }catch(Exception e){
        //异常的处理逻辑
    }catch(Exception e){
        //异常的处理逻辑
    }catch(Exception e){
        //异常的处理逻辑
    }
    注意:
        1.try中可能会抛出多个异常,那么可以使用多个catch语句解决
        2.如果try中产生了异常,那么就会执行catch中的异常处理逻辑,
          如果没有异常,就会跳过catch之中的代码
*/

/*
    Throwable类中定义了三个常用方法
        1.String getMessage()返回此throwable的详细字符串
        2.String toString() 返回此throwable的简短描述
        3.void printStackTrace() JVM打印异常对象,默认此方法,因为此方法信息最全面

 */

import java.io.FileNotFoundException;
import java.io.IOException;

public class TestTryCatch {
    public static void main(String[] args) {
        try {
            readFile("d:\\a.txt");
            System.out.println("这句代码执行不到,可以使用finally代码块解决");
        } catch (IOException e) {
            System.out.println("catch - 传递的文件后缀不是.txt");
            System.out.println(e.toString());
            System.out.println(e.getMessage());
            e.printStackTrace();//信息最全面

        }
        System.out.println("后续代码");

    }
    public static void readFile(String fileName) throws IOException {
        if(!fileName.equals("c:\\a.txt")){
            throw new FileNotFoundException("传递的文件路径不是c:a.txt");
        }
        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");//注意IOException是FileNotFoundException的父类,所以只需要抛出父类就可以,而不用两个全抛出

        }
        System.out.println("路径没有问题,打印文件");


    }


}


finally

package cn.itcast.day08.demo03;
/*
    1.有一些特定的代码,无论异常是否发生,都需要执行,另外,因为异常会引发程序跳转,导致
    有些语句执行不到,而finally就是解决这个问题的,,在finally中的代码块是一定会执行的
    2.finally不可以单独使用,必须和try一起使用,一般被用于资源释放
    3.常用于io流
    注意事项
        1.要避免在finally代码块中有return语句,因为若这样子,不论try catch中写了声明
            返回的永远是finally中的结果
 */
public class TestFinally {
    public static void main(String[] args) {
        try {
            //可能会产生异常的代码
            readfile("a.txtt");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //无论是否异常,这里的代码都可以被执行
            System.out.println("资源释放");

        }
    }

    private static void readfile(String filename) throws Exception {
        if(!filename.endsWith(".txt")){
            throw new Exception("文件后缀名不对");

        }
        System.out.println("文件路径没有问题,继续执行");
    }
}



自定义异常

package cn.itcast.day08.demo03;
/*
    自定义异常类:
        java提供的异常类,不够我们使用,需要自己定义一些异常类
    格式:
        public class XXXException extends Exception|runtimeException{
            添加一个空参数的构造方法;
            添加一个带异常信息的构造方法;
        }
    注意事项:
        1.自定义异常类一般都以Exception结尾,说明该类是一个异常类
        2.自定义异常类,必须继承Exception或者Ru'n'ti'me'Exception
            继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了
                这个异常,就必须处理异常
                    继续抛出或者try   catch
            继承RuntimeException:那么就是一个运行时异常,无需处理,交给虚拟机处理(中断处理)

 */
public class RegisterException extends Exception{
    //添加一个空参数构造方法

    public RegisterException() {
        super();
    }

    public RegisterException(String message) {
        super(message);
    }
}


package cn.itcast.day08.demo03;

import java.util.Scanner;

/*
    要求我们模拟注册操作,如果用户名已经存在,则抛出异常并提示:该用户名已经注册
    过程:
        1.使用数组保存已经注册过的用户名
        2.使用Scanner输入用户名
        3.定义一个方法boolean类型,对用户输入的的注册名进行判断
        4.若用户名存在,则抛出异常,告知用户该用户名已经被注册

 */
public class PracticeRegisterException {
    static String[] usernames={"zhangsan","lisi","wangwu"};
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入你要注册的用户名:");
        String username=sc.next();
        if(checkname(username)){
            //用户名存在,抛出异常
            try {
                throw new RegisterException("用户名已经被注册");
            } catch (RegisterException e) {
                e.printStackTrace();
            }
        }
        else{
            System.out.println("您的用户名为:"+username);
        }

    }

    private static boolean checkname(String username) {
        for (String s : usernames) {
            if(s.equals(username)){
                return true;
            }
        }
        return false;
    }
}


新扑克牌

package cn.itcast.day08.Poker;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
//这个练习最重要的思想就是不带value玩,只看索引,对索引排序就是对牌排序,发索引就是发牌
//打乱索引就是打乱牌
public class DouDiZhu {
    public static void main(String[] args) {
        //使用map来存储牌,key为从0到53的索引,value为牌的内容
        HashMap<Integer,String> poker=new HashMap<>();
        ArrayList<Integer> pokerIndex=new ArrayList<>();
        //这样子组装牌,可以让牌按索引为从大到小的顺序
        List<String> colors=List.of("♥","♠","♣","♦");
        List<String> numbers=List.of("2","A","K","Q","J","10","9","8","7","6","5","4","3");
        int index=0;
        poker.put(index++,"大王");
        poker.put(index++,"小王");
        for (String number : numbers) {
            for (String color : colors) {
                poker.put(index++,color+number);
            }
        }
        //组装好牌以后,我们就不带value玩了,我们只看索引,按索引发牌,那么按索引排序其实也就是按照
        //牌的大小排序
        for (int i = 0; i < 54; i++) {
            pokerIndex.add(i);
        }
        System.out.println(poker);
//        System.out.println(pokerIndex);
        //洗牌,让索引无序
        Collections.shuffle(pokerIndex);
        System.out.println(pokerIndex);
        //定义四个集合,存储玩家牌的索引和底牌的索引,索引就代表着一张牌
        ArrayList<Integer> player01=new ArrayList<>();
        ArrayList<Integer> player02=new ArrayList<>();
        ArrayList<Integer> player03=new ArrayList<>();
        ArrayList<Integer> underPoker=new ArrayList<>();
        //发牌,按顺序发牌
        for (int i = 0; i < pokerIndex.size(); i++) {
            Integer in=pokerIndex.get(i);
            if(i>=51){
                //给底牌发牌
                underPoker.add(i);
            }
            else if(i%3==0){
                player01.add(i);
            }
            else if(i%3==1){
                player02.add(i);
            }
            else {
                player03.add(i);
            }
        }//for
        //之后给每个人手里的牌进行排序,从小到大进行排序
        Collections.sort(player01);
        Collections.sort(player02);
        Collections.sort(player03);
        Collections.sort(underPoker);

        //看牌
        lookPoker("刘德华",poker,player01);
        lookPoker("周润发",poker,player02);
        lookPoker("古天乐",poker,player03);
        lookPoker("周星驰",poker,underPoker);

    }
    //定义一个看牌的方法
    public static void lookPoker(String name,HashMap<Integer,String> poker,ArrayList<Integer> list){

        System.out.print(name+":");
        for (Integer key : list) {
            String value=poker.get(key);
            System.out.print(value+" ");
        }
        System.out.println();
    }
}


第七天-线程

主线程

package cn.itcast.day09.thread;
/*
    主线程:执行主方法(main)的线程
    单线程程序:java中只有一个线程,执行方法从main开始,从上到下依次执行
 */
public class MainThread {
    public static void main(String[] args) {
        Person p1=new Person("小强");
        p1.run();
        System.out.println(0/0);
        //但是单线程也会有弊端,比如上一行的代码就会导致旺财的run方法无法执行,只执行了
        //小强的,可以用多线程解决
        Person p2=new Person("旺财");
        p2.run();
        //如上就是单线程程序
        /*
            JVM执行main方法,main方法会进入到栈内存,JVM会找操作系统开辟一条main
            方法通向cpu执行路径
            cpu就可以通过这个路径来执行main方法,而这个路径有一个名字,就叫
                main主线程
         */

    }
}


创建线程-继承Thread

package cn.itcast.day09.thread;
/*
    1.创建多线程有两种方法,这里先学习第一种:
        1.创建一个类继承Thread类
        2.并且重写其下的run方法,设置线程任务
        3.然后创建Thread类的子类对象
        4.调用Thread类中的start方法开启新的线程,间接执行run方法内的任务
            void start()使该线程开始执行;JAVA虚拟机调用该线程的run方法
            结果是两个线程并发的运行,main线程和创建的新线程
            多次启动一个线程是非法的,特别是线程已经结束执行后,不能再重新启动

    java.lang.Thread类是描述线程的类,java属于抢占式调度,优先级相同则随机选择
    2.获取线程的名称
        1.使用Thread类中的方法getName()
        2.先获取当前正在执行的线程,使用线程中的getName()方法获取名称
        System.out.println(Thread.currentThread().getName());
        System.out.println(getName());
    3.设置线程的名称
        setName(String name)
        创建一个带参数的构造方法,参数传递线程的名称,调用父类的带参构造方法,把线程的
        名称传递给父类,让父类给子线程起一个名字
            Thread(String name)
 */
public class CreataThreadMethod01 {
    public static void main(String[] args) {
        MyThread MyThread=new MyThread();//会开辟一条到达cpu的路径,此时通向cpu就有了
        //两条路径,于是就有了程序的随机打印结果,两个线程一起抢夺cpu的执行权
        MyThread.start();//调用start方法来让jvm调用run方法
        for (int i = 0; i < 20; i++) {
            System.out.println("main : "+i);
            /*main : 0
            run : 1
            main : 1
            run : 2
            main : 2
            run : 3
            可以看出,两个线程随机的执行
            */
        }
    }
}


class MyThread extends Thread{
    @Override
    public void run() {
        //设置线程的任务;
        for (int i = 0; i < 20; i++) {
            System.out.println("run : "+i);
        }
        System.out.println(getName());
        System.out.println(Thread.currentThread().getName());
    }
}


创建线程2—实现Runnable接口

package cn.itcast.day09.thread;
/*
    1.创建线程的另一种方法是声明实现Runnable接口的类,该类然后实现run方法,然后可以分配该类
    的实例,在创建Thread时作为一个参数来传递并启用
    与第一种的区别就是是实现而不是继承,大多数情况下,如果我们只使用run方法,而不重写其他
    Thread方法,那么应该使用runnable接口,这很重要,因为除非程序员想修改或者增强类的基本
    行为,否则不应该为其创建子类
    2.实现步骤
        1.创建一个runnable接口的实现类
        2.在实现类中重写runnable的run方法
        3.创建一个Runnable接口的实现类对象
        4.创建Thread类对象,构造方法中传递runnable接口的实现类对象
        5.调用Thread类中的start方法,开启新的线程执行run方法
    3.好处
        1.避免了单继承的局限性
        2.增强了程序的扩展性,降低了程序的耦合性(解耦)
            实现runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
            实现类中,我们重写了run方法,用来设置线程任务
            创建Thread对象,调用start方法,用来开启新线程
 */
public class CreateThreadMethod02 {
    public static void main(String[] args) {
        /*RunnableImpl runnable=new RunnableImpl();
        Thread t=new Thread(runnable);
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+" : "+i);
        }*/



        //匿名内部类的方式
        new Thread(){
            public void run(){
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName()+" : "+i);
                }

            }
        }.start();
        System.out.println("=================");
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println("HelloWorld "+i);
                }
            }
        }).start();
    }
    /*
    HelloWorld 0
    HelloWorld 1
    HelloWorld 2
    HelloWorld 3
    HelloWorld 4
    HelloWorld 5
    HelloWorld 6
    HelloWorld 7
    HelloWorld 8
    HelloWorld 9
    HelloWorld 10
    HelloWorld 11
    HelloWorld 12
    HelloWorld 13
    HelloWorld 14
    HelloWorld 15
    HelloWorld 16
    HelloWorld 17
    HelloWorld 18
    HelloWorld 19
    Thread-0 : 0
    Thread-0 : 1
    Thread-0 : 2
    Thread-0 : 3
    Thread-0 : 4
    Thread-0 : 5
    Thread-0 : 6
    Thread-0 : 7
    Thread-0 : 8
    Thread-0 : 9
    Thread-0 : 10
    Thread-0 : 11
    Thread-0 : 12
    Thread-0 : 13
    Thread-0 : 14
    Thread-0 : 15
    Thread-0 : 16
    Thread-0 : 17
    Thread-0 : 18
    Thread-0 : 19
    */
}


模拟买票案例

package cn.itcast.day09.thread;
/*
    模拟卖票案例
    创建三个线程,让他们同时开启,对共享的票进行出售,会产生线程安全问题
    出现了不存在的-1票和多张重复的票

 */
/*
    解决线程安全的方法:线程同步
        1.同步代码块
            synchronized
        2.同步方法
        3.锁机制
 */
public class TestTicket {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl02 r = new RunnableImpl02();
        //创建Thread类对象,构造方法中传递runnable实现类对象
        Thread t0=new Thread(r);
        Thread t1=new Thread(r);
        Thread t2=new Thread(r);
        t0.start();
        t1.start();
        t2.start();

        //产生了线程安全问题,这是不可以出现的,我们可以让一个线程在访问共享数据的时候
        //无论是否失去了cpu的执行权,都让其他线程等待,等待当前线程卖完票,其他线程再进
        //行售票,保证一个时刻,只有一个线程在卖票

    }
}


线程安全-同步代码块

package cn.itcast.day09.thread;
/*
    解决线程安全的第一个办法:使用同步代码块
    格式 :
        synchronized(锁对象){
            可能会出现线程安全的代码块(访问了共享数据的代码)
        }
    注意事项:
         1.通过代码块中的锁对象,可以使用任意的对象
         2.但是必须保证多个线程使用的锁对象是同一个
         3.锁的作用:
            把同步代码块锁住,只让一个线程在同步代码块中执行
    弊端:
        虽然保证了安全,但是程序的频繁的判断锁,释放锁,程序的效率会降低
 */
public class RunnableImplSyn01 implements Runnable{

    private int ticket=100;
    Object o=new Object();//锁对象

    @Override
    public void run() {

        while(true){
            //创建一个同步代码块
            synchronized (o){
            /*
                举例:t0先进入,遇到了syn同步代码块,会检查代码块是否有锁对象,发现有,就会获取
                锁对象,然后进入执行,未退出前不会归还锁对象
                     之后t1进入,遇到同步代码块后检查,发现没有锁对象,则进入阻塞状态,直到t0
                归还了锁对象,他获得了锁对象才可以继续执行
             */
                if(ticket>0){
                    //还有票
                    try {
                        /*
                        注意此处,只可以try而不可以throws抛出异常,因为现在是在重写祖先类的
                        方法,所以不可以抛出,只可以捕获
                         */
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                    ticket--;
                }
            }

        }
    }
}


线程安全-同步方法

package cn.itcast.day09.thread;
/*
    解决线程安全的第二个方法:使用同步方法
    使用步骤:
        1,访问了共享数据的代码抽取出来放到一个方法里面
        2.添加synchronized修饰符
    格式:
        修饰符 synchornized 返回值类型 方法名(参数列表){

        }

 */
public class RunnableImplSyn02 implements Runnable{
    private static int ticket=100;
    @Override
    public void run() {
        System.out.println("this:"+this);
        while(true){
            paidTicketStatic();
        }

    }
    /*
        定义一个同步方法,同步方法也会把方法内部的代码锁住,只让一个线程执行
        同步方法的锁对象是谁?其实就是实现类对象 new RunnableImpl(),也就是this
     */
    public synchronized void paidTicket(){
        if (ticket>0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
            ticket--;
        }

    }
    /*
        静态方法的锁对象是本类的class属性
     */
    public static synchronized void paidTicketStatic(){
        if (ticket>0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
            ticket--;
        }

    }
}


lock锁

package cn.itcast.day09.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
    解决线程安全的第三个方法:lock锁
    java.util.concurrent.locks.lock接口
    实现了比同步代码块和同步方法更广泛的锁定操作,此实现允许更灵活的结构,可以具有差别很大的属性
    该接口提供了两个方法:
        void unlock 释放锁
        void lock 获取锁
    使用其实现类:java.util.concurrent.locks.ReentrantLock
    使用步骤:
        1.在成员位置创建一个ReentrantLock实现类
        2.在可能会出现安全问题的代码前调用Lock接口中的lock方法获取锁
        3.在可能会出现安全问题的代码后调用Lock接口中的unlock方法释放锁


 */
public class RunnableImplSyn03 implements Runnable{
    private static int ticket=100;
    //1
    Lock l=new ReentrantLock();
    @Override
    public void run() {
        while(true){
            //2
            l.lock();//上锁
            if (ticket>0){
                try {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //3 推荐将释放锁写到finally里面
                    l.unlock();
                }

            }

        }

    }
}


线程休眠

package cn.itcast.day09.thread;
/*
    public static void sleep(long millis):使当前线程以指定的毫秒数暂停
 */
public class TestSleep {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName());
    }
}


线程池

线程池:是一个容器(集合),一般使用LinkedList<Thread>
    当程序第一次启动的时候,我们可以创建多个集合,保存到一个集合里面
    当我们需要使用线程的时候,就可以从集合中取出线程,一般使用removeFirst取出
    当使用完毕线程,需要将线程归还的时候,我们可以使用add或者addList方法存回

    在jdk1.5之后,jdk内置了线程池,内置了线程池,我们可以直接使用
好处:
    降低系统消耗
    提高响应速度,任务到达的时候无需创建线程
    提高线程的可管理性


package cn.itcast.day09.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
    线程池:1.5之后提供的
    在java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
    Exectors类中的静态方法:
        static ExecutorService newFixedThreadPool(int number)
        参数是需要线程的数量,返回类型就是一个线程池(接口的实现类对象 面向接口编程)

    接口java.util.concurrent.ExecutorService就是一个线程池接口,提供了一个方法可以用来取出线程
        submit(Runnable task)提交一个Runnable任务用于执行
        shutDown             销毁线程池

    使用步骤:
        1.使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量
        的线程池
        2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
        3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,自动执行run方法
        4.调用ExecutorService销毁线程池(不建议使用)
 */
public class ThreadPool {
    public static void main(String[] args) {
        //1
        ExecutorService es=Executors.newFixedThreadPool(2);
        //3
        es.submit(new RunnableImpl());//使用完后,会自动把线程归还给线程池,线程可以继续使用
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());



    }

}

Object类中的方法

package cn.itcast.day09.thread;
/*
    Object类中的方法:
        wait:在调用此对象的notify或者notifyAll方法前,导致该线程等待
            wait(long m)带参方法类似于sleep。毫秒值结束后,若还没有被notify唤醒,就会自动
            醒来,线程睡醒进入Runnable或者Blocked状态
        notify:唤醒在此对象监视器上等待的单个线程,会继续执行wait之后的代码
        notifyAll:唤醒所有线程
 */
public class WaitAndNotify {
    public static void main(String[] args) {

        //创建锁对象,保证唯一
        Object obj=new Object();
        new Thread(){

            @Override
            public void run() {
                //花了5秒钟做包子

                while (true){
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj){
                        System.out.println("老板5秒钟之后做好包子并告知顾客可以吃了");
                        obj.notify();
                    }
                }

            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                //保证等待和唤醒线程只有一个执行,需要线程同步技术

                while(true){
                    synchronized (obj){
                        System.out.println("告知老板需要的包子的种类和数量");
                        //调用wait方法,放弃cpu的,进入无限等待状态
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("包子做好了,开吃");
                        System.out.println("=========");
                    }
                }

            }
        }.start();
    }
}
/*告知老板需要的包子的种类和数量
老板5秒钟之后做好包子并告知顾客可以吃了
包子做好了,开吃
=========
告知老板需要的包子的种类和数量*/


第八天-文件字节流

File

概述

package cn.itcast.day10.File;

import java.io.File;

/*
    1.java.io.File类
        文件和目录路径名的抽象表示形式
        java把电脑中的文件和文件夹封装成了一个File类,我们可以使用File类对文件和文件夹进行操作
        我们可以使用File类的方法
            创建一个文件或者文件夹
            删除
            获取
            判断是否存在
            遍历
            获取文件的大小
    2.File是一个与系统无关的类,任何操作系统都可以使用这个类中的方法
    3.重点:记住三个单词
        file:文件
        directory:文件夹
        path:路径
    4.常用静态方法:
        static String pathSeparator
            与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
        static char pathSeparatorChar
              与系统有关的路径分隔符。返回的是字符
        static String separator
              与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
        static char separatorChar
              与系统有关的默认名称分隔符。返回的是字符

        以后我们操作路径的时候,最好不要写死了,因为不同系统分隔符不同,所以我们要利用上面的方法
        例如C:\develop\a\a.txt
        "C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"

 */
public class TestFile {
    public static void main(String[] args) {
        System.out.println(File.pathSeparator);//;就是路径分隔符 window ;分号   linux :冒号
        System.out.println(File.separator);//\就是文件名称分隔符 windos \反斜杠  linux /正斜杠
        /*
        ;
        \
         */


    }
}

绝对路径与相对路径

绝对路径:是一个完整的路径,以盘符CDEF开始的路径
                C:\baiduyunDownload\05-异常与多线程
相对路径:是一个简化的路径,相对的是当前项目的根目录
        如果使用当前项目的根目录,路径可以简化书写,可以省略当前项目的根目录,
注意:
    1.路径不区分大小写
    2.路径中的文件分隔符window使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠


构造方法

package cn.itcast.day10.File;

import java.io.File;

/*
    File(File parent, String child)
          根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
    File(String pathname)
          通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
    File(String parent, String child)
          根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
    File(URI uri)
          通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例

 */
public class ConstructionMethod {
    public static void main(String[] args) {
        show01();
        show02("c:\\","a.txt");
        show03();
    }
    /*
        File(File parent, String child)
            根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
        好处:
            父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象
     */
    private static void show03() {
        File parent = new File("c:\\");
        File file=new File(parent,"hello.java");
        System.out.println(file);

    }

    /*
        File(String parent, String child)
            根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
        参数:
            parent:父路径
            child:子路径

        好处:
            父路径和子路径,可以单独书写,使用起来非常灵活,父路径和子路径都可以变化
     */
    private static void show02(String parent, String child) {
        File f2=new File(parent,child);
        System.out.println(f2);
    }

    /*
        File(String pathname)
            通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
            pathname:路径名称
                1.路径可以是文件也可以是文件夹
                2.可以是相对路径也可以是绝对路径
                3.路径可以存在,也可以不存在
                创建File对象,只是把字符串封装为File对象,不考虑路径的真假情况
     */
    private static void show01() {
        File f1=new File("C:\\baiduyunDownload");
        System.out.println(f1);//C:\baiduyunDownload
    }
    
}


CommonMethod

package cn.itcast.day10.File;

import java.io.File;

/*
     String getAbsolutePath()
          返回此抽象路径名的绝对路径名字符串
     String getPath()
          将此抽象路径名转换为一个路径名字符串。
     String getName()
          返回由此抽象路径名表示的文件或目录的名称。
     long length()
          返回由此抽象路径名表示的文件的长度。
 */
public class CommonMethod {
    public static void main(String[] args) {
        File f1=new File("C:\\java_code\\basic-code\\grammar-code\\src\\cn\\itcast\\day10\\File\\CreateAndDelete.java");
        System.out.println(f1.getAbsolutePath());//C:\java_code\basic-code\grammar-code\src\cn\itcast\day10\File\CreateAndDelete.java
        File f2=new File("a.txt");
        System.out.println(f2.getAbsolutePath());
        show02();
        show03();

    }
    /*
        long length()返回文件长度,注意:文件夹没有长度,若路径为不存在的文件,则返回0
     */
    private static void show03() {
        File f1=new File("C:\\java_code\\basic-code\\grammar-code\\src\\cn\\itcast\\day10\\File\\CreateAndDelete.java");
        System.out.println(f1.length());//1623
    }

    /*
    String getName()返回的是构造方法传递路径的结尾部分,可以是文件,也可以是文件夹
     */
    private static void show02() {
        File f1=new File("C:\\java_code\\basic-code\\grammar-code\\src\\cn\\itcast\\day10\\File\\CreateAndDelete.java");
        System.out.println(f1.getName());//CreateAndDelete.java
    }

}


booleanMethod

package cn.itcast.day10.File;

import java.io.File;

/*
    exists() 此File表示的文件或目录是否存在
    isDirectory()  此file是否是目录
    isFile()    此file是否是文件
 */
public class BooleanMethod {
    public static void main(String[] args) {
        show01();

    }

    private static void show01() {
        File f1=new File("C:\\Users\\Admin\\IdeaProjects\\basic-code\\grammar-code\\src\\cn\\itcast\\day10\\File");
        System.out.println(f1.exists());
        System.out.println(f1.isDirectory());
        System.out.println(f1.isFile());
        File f2=new File("grammar-code");
        System.out.println(f2.exists());
    }
}


createAndDeleteMethod

package cn.itcast.day10.File;
/*
    createNewFile();当且仅当具有该名称的文件不存在时,创建一个新的空文件
        若文件存在,返回false
        创建文件的路径必须存在,否则抛出异常
        不可以创建文件夹,并不是说会产生错误或者异常,而是无法满足要求,仍然创建的是一个文件
    delete();删除此file表示的【文件或者文件夹】
        直接在硬盘删,不会走回收站,所以要谨慎
    mkdir();创建由此file表示的目录---单级文件夹
    mkdirs();创建由此file表示的目录,包括任何必须但不存在的父目录---多级文件夹
 */

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

public class CreateAndDelete {
    public static void main(String[] args) {
        show01();
        show02();
        show03();

    }

    private static void show03() {
        System.out.println(new File("C:\\baiduyunDownload\\a\\b\\c\\a.txt").delete());
    }

    private static void show01() {
        File f1=new File("C:\\Users\\Admin\\IdeaProjects\\basic-code\\grammar-code\\src\\cn\\itcast\\day10\\File\\test2.txt");
        try {
            System.out.println(f1.createNewFile());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void show02() {
        File f1=new File("C:\\baiduyunDownload\\a\\b\\c\\a.txt");//注意,此处
        
        System.out.println(f1.mkdir());//false,不可以创建多级目录
        System.out.println(f1.mkdirs());//true//创建出来的是一个叫a.txt的文件夹而不是文件
    }
}


ErgodicMethod

package cn.itcast.day10.File;

import java.io.File;

/*
    String[] list();返回一个String数组,表示该File目录中的所有子文件或目录
    File[] listFiles();返回一个File数组,表示该File目录中的所有的子文件或目录
    注意:
        若构造方法中给出的路径不存在,会抛出空指针异常
        若给出的路径不是目录,也会抛出空指针异常
        隐藏的文件夹也可以获得

 */
public class ErgodicMethod {
    public static void main(String[] args) {
        show01();
        //show02();
    }

    private static void show02() {
        File f1=new File("C:\\Users\\57\\Documents\\笔记");
        File[] fs=f1.listFiles();
        for (File f : fs) {
            System.out.println(f);
        }
        /*
        C:\Users\57\Documents\笔记\01_JavaBasic
        C:\Users\57\Documents\笔记\02_Mysql
        C:\Users\57\Documents\笔记\03_JDBC
        C:\Users\57\Documents\笔记\04_JAVA_WEB
         */
    }

    private static void show01() {
        File f1=new File("C:\\Users\\57\\Documents\\笔记");
        String[] arr=f1.list();
        for (String s : arr) {
            System.out.println(s);
        }
        /*01_JavaBasic
          02_Mysql
          03_JDBC
          04_JAVA_WEB*/

    }

}


IO

各种流概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TXN5ix9M-1617604209652)(C:\Users\57\Documents\XMinde Files\png文件\流.png)]

inputStream

package cn.itcast.day10.IO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
    1.java.io.InputStream抽象类是所有字节输入流的超类
    定义了所有子类共性的方法
        int read()
          从此输入流中读取一个数据字节。
        int read(byte[] b)
          从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
        void close()
          关闭此文件输入流并释放与此流有关的所有系统资源。
    2.java.io.FileInputStream extends InputStream
        FileInputStream:文件字节输入流
        1.作用:把硬盘文件中的数据,读取到内存中使用
        2.构造方法:
            FileInputStream(File file)
                通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
            FileInputStream(String name)
                通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定
            file/name读取文件的数据源
            作用:
                1.会创建一个FileInputStream对象
                2.会把FileInputStream对象指向,构造方法中要读取的文件
        3.读取数据的原理:(硬盘-->内存)
            java程序--jvm--os--os读取数据的方法--读取文件
        4.字节输入流的使用步骤
            1.创建FIS对象,构造方法中绑定要读取的数据源
            2.使用FIS对象中的方法read,读取文件
            3.释放资源




 */
public class Demo01InputStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("C:\\java_code\\basic-code\\grammar-code\\src\\cn\\itcast\\day10\\IO\\a.txt");
        //2.
        /*System.out.println(fis.read());//每次read后指针后移一位
        System.out.println(fis.read());
        System.out.println(fis.read());
        System.out.println(fis.read());
        System.out.println(fis.read());*/
        /*
        发现是一个重复的过程,所以可以使用循环优化,不知道有多少个字节,使用while循环
         */
        int len=0;
        while((len=fis.read())!=-1){//read会返回读取的字节数,如果文件中已经没有读取的字节,则返									 //回-1
            System.out.print((char)len);
        }
        //3
        fis.close();

    }
}


package cn.itcast.day10.IO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;

/*
    字节输入流一次读取多个字节的办法:
        int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
            返回每次读写的有效字节个数,没有读到数据返回-1
    明确两个事情:
        1.参数byte[]的作用
            起到缓冲的作用,存储每次读取到的多个字节
            数组的长度一般定义为1024 也就是1kb或者1024的整数倍
        2.方法的返回值int是什么

 */
public class Demo02InputStreeam {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("C:\\baiduyunDownload\\c.txt");
        int len=0;
        byte[] bytes=new byte[1024];

        while((len=fis.read(bytes))!=-1){
            System.out.print(new String(bytes,0,len));
        }


        /*
          String(byte[] bytes)
            通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
          String(byte[] bytes, int offset, int length)
            通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String
                offset:数组的开始索引
                length:转换的字节个数

         */

        fis.close();

    }
}


outputStream

package cn.itcast.day10.IO;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/*
    1.java.io.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.FileOutputStream继承了该类,是文件字节输出流
     作用:把内存中的数据写入到硬盘文件中
     构造方法:
        FileOutputStream(File file)
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
        FileOutputStream(String name)
          创建一个向具有指定名称的文件中写入数据的输出文件流。
        1.参数:写入数据的目的地
            file目的地是文件
            name目的地是一个文件的路径
        2.作用:
            1.创建以恶个FileOutputStream对象
            2.会根据构造方法中传递的文件/文件路径,创造一个空的文件
            3.会把FileOutputStream对象指向创建好的文件

    写入数据的原理:
        java程序-->JVM虚拟机-->OS操作系统-->OS调用写数据的方法-->把数据写入到文件里
    字节输出流的使用步骤:
        1.创建一个FileOutputStream对象,构造方法中传递了写入数据的目的地
        2.调用其的write方法,将数据写入到文件里
        3.释放资源(流的使用会占用内存,所以要及时清空,提高效率)
 */
public class Demo01OutputStream {
    public static void main(String[] args) throws IOException {
        //1.
        FileOutputStream fos = new FileOutputStream("C:\\baiduyunDownload\\a.txt");
        //2.调用其的write方法,把数据写入到文件里,不论源文件有什么,会覆盖,文件里只有一个a
        fos.write(97);
        /*
            这里注意一点,任意的文本编辑器,在打开文件的时候,都会查询编码表,把字节转换为字符表示
            0-127:查询asc2表
                97--->a
            其他值查询默认码表,中文默认GBK
         */
        fos.close();
    }
}


package cn.itcast.day10.IO;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

/*
    一次写入多个字节的方法:
        1.public void write(byte[] b) 将b.length字节从指定的字节数组写入此输出流
        2.public void write(byte[] b,int off,int len)从指定的字节数组写入len字节,从偏移量off
            开始输出到此输出流

 */
public class Demo02OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File("C:\\baiduyunDownload\\b.txt"));
        fos.write(49);
        fos.write(48);
        fos.write(48);
        //面试题,在文件中显示100,需要几个字节   3个
        /*
        也可以传进去一个数组
            如果写的第一个字节是正数(0-127),那么显示的时候会查询asc2表
            如果写的第一个字节是负数,那么第一个和第二个字节会组成一个中文显示,查询系统默认码表
         */
        //fos.write(new byte[]{49,48,48,49,48,48,49,48,48});
        fos.write(new byte[]{65,66,67,68,69});
        /*
            2.public void write(byte[] b,int off,int len)从指定的字节数组写入len字节,从偏移量off
            开始输出到此输出流
                off:数组的开始索引
                len:写几个字节

         */
        fos.write(new byte[]{65,66,67,68,69},1,2);

        /*
            3.写入字符串的方法:可以使用String类中的方法把字符串,转换为字节数组
                byte[] getBytes()
         */
        byte[] bytes2="你好".getBytes();
        //UTF-8中三个字节是一个中文,GBK中两个字节是一个中文
        System.out.println(Arrays.toString(bytes2));
        fos.write(bytes2);
        fos.close();




    }
}


package cn.itcast.day10.IO;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/*
    1.数据追加续写:其实从demo01和demo02可以发现,我们写入文件,都是从头重新写入,文件内原本的信息
        会丢失
        使用两个参数的构造方法就可以了
            1.FileOutputStream(String name, boolean append)
                创建一个向具有指定 name 的文件中写入数据的输出文件流。
            2.FileOutputStream(File file, boolean append)
                创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
            name/file是写入数据的目的地
            append是追加续写的开关,若为true则不会覆盖源文件,而是在文件末尾续写文件
    2.写换行
        windos  \r\n
        linux   /n
        mac     /r
 */
public class Demo03OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("C:\\baiduyunDownload\\c.txt",true);
        fos.write("你好".getBytes());
        for (int i = 0; i < 10; i++) {
            fos.write("你好".getBytes());
            fos.write("\r\n".getBytes());
        }
        fos.close();

    }
}


Reader

package cn.itcast.day10.IO;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;

/*
    由于不同软件的对汉字不同编码格式,当使用字节流读取文本文件的时候,有可能会遇到一个问题,就是当
    遇到中文字符的时候,  可能会显示不完整的字符,那么因为在不同的编码格式中,有的汉字是占2个字节
    比如GBK,有的是3个字节,比如UTF-8,所以java提供一些字符流类,以字符为单位读写数据,专门用于处理
    文本文件


    java.io.Reader抽象类,用于读取字符流
        abstract  void close()
          关闭该流并释放与之关联的所有资源。
        int read()
          读取单个字符。并返回
        int read(char[] cbuf)
          将字符读入数组。
        abstract  int read(char[] cbuf, int off, int len)
          将字符读入数组的某一部分。

    我们今天学习的是Reader的子类InputStreamReader的子类FileReader(读取文件的字符输入流)
        1.作用:把硬盘文件中的数据以字符的方式读取到内存中
        2.构造方法

            FileReader(File file)
                在给定从中读取数据的 File 的情况下创建一个新 FileReader。
            FileReader(FileDescriptor fd)
                在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。
            FileReader(String fileName)
                在给定从中读取数据的文件名的情况下创建一个新 FileReader
            1.参数:读取文件的数据源
                String fileName 文件的路径
                File file 一个文件
            2.FileReader构造方法的作用:
                1.创建一个FileReader对象
                2.会把FileReader对象,指向,要读取的文件
            3.使用步骤
                1.创建FileReader对象,构造方法中绑定要读取的数据源
                2.使用FileReader对象中的方法read读取文件
                3.释放资源

 */
public class Demo01Reader {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("C:\\baiduyunDownload\\c.txt");
        int len=0;
        /*while((len=fr.read())!=-1){
            System.out.println((char)len);
        }*/
        char[] chars = new char[1024];
        while((len=fr.read(chars))!=-1){
            System.out.println(new String(chars));
        }
        fr.close();
    }
}


Writer

package cn.itcast.day10.IO;

import java.io.FileWriter;
import java.io.IOException;

/*
    与Reader类似,Writer是写入字符流的抽象类
    FileWriter extends OutputStreamWriter extends Writer
    1.作用:
        把内存中字符数据写入到文件中
    2.构造方法:
        FileWriter(File file)
            根据给定的File对象构造一个FileWriter对象
        FileWriter(String filename)
            根据给定的文件名构造一个FileWriter对象
        参数:写入数据的目的地
            filename:文件路径
            file:文件
        构造方法的作用:
            1.创建一个FileWriter对象
            2.根据构造方法中的路径,创建一个文件
            3.会把FileWriter对象指向创建好的文件
    3.使用步骤
        1.创建一个FileWriter对象,构造方法中绑定好写入数据的目的地
        2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        4.释放资源,先把内存缓冲区中的数据刷新到文件中
    4.常用方法
        abstract  void close()
          关闭此流,但要先刷新它。
        abstract  void flush()
          刷新该流的缓冲。
        void write(char[] cbuf)
          写入字符数组。
        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)
          写入字符串的某一部分。


 */
public class Demo01Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw=new FileWriter("C:\\baiduyunDownload\\d.txt");
        fw.write(97);//a
        fw.flush();
        //要极其注意这一步,与字符输出流不同,字符输入流是把数据写入到内存缓冲区中,如果不进行
        //刷新的话,数据是不会进入文件中的,一定要刷新
        fw.close();//注意:close会默认在之前先调用一次flush方法。
        /*
            close和flush的区别
                flush:刷新缓冲区,流对象可以继续使用
                close:先刷新缓冲区,然后通知系统释放资源,流对象不可以再被使用了
         */
    }
}


package cn.itcast.day10.IO;

import java.io.FileWriter;
import java.io.IOException;

/*
    字符输入流写数据的其他方法

 */
public class Demo02Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw=new FileWriter("C:\\baiduyunDownload\\d.txt",true);//追加续写
        fw.write("\r\n");//换行符号
        char[] chars={'a','b','c','d','e'};
        fw.write(chars);//abcde
        fw.flush();

        fw.write("-");
        fw.write(chars,0,2);//abcde-ab

        fw.write("我爱学习");

        fw.write("我爱学习",0,2);
        fw.close();

    }
}


字节字符输入流标准写法

package cn.itcast.day10.IO;

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

/*
    之前的字节字符输入流案例中,我们总是抛出异常,再之后的开发中我们不推荐这个写,我们应该捕获异常
    而不是抛出
    在jdk1.7 之前使用trycatch处理流中的异常
    格式:
        try{
            可能会产生异常的代码
        }catch(异常类对象){
            异常的处理逻辑
        }finally{
            一定会执行的代码,一般是资源释放代码
        }

    jdk7的新特性:
        在try的后面可以增加一个括号,在括号内可以定义流对象
        那么这个流对象的作用域就在try中有效
        try中的代码执行完毕会自动把流对象释放,不用在finally中再写fw.close

    jdk9新特性:
        try的前面可以定义流对象
        再try的后面的括号内可以直接引入流对象的名称,
        在try代码块执行完毕后流对象也会自动释放
        格式:
            A a=new A();
            B b=new B();
            try(a;b){
                可能会产生异常的代码
            }catch(异常对象变量名){
                异常的处理逻辑
            }
 */
public class Standard {
    public static void main(String[] args) {
        /*FileWriter fw=null;
        try {
            fw = new FileWriter("C:\\baiduyunDownload\\d.txt",true);
            for (int i = 0; i < 10; i++) {
                fw.write("HELLO"+i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fw.close();//声明抛出了IOException异常对象,所以我们就处理这个异常对象,要么
                //抛出,要么捕获
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/


        //jdk7

        try(FileInputStream fis = new FileInputStream("C:\\baiduyunDownload\\d.txt");
            FileOutputStream fos = new FileOutputStream("C:\\baiduyunDownload\\e.txt", true);){
            int len=0;
            byte[] bytes=new byte[1024];
            while((len=fis.read(bytes))!=-1){
                fos.write(bytes,0,len);
            }
        }catch(IOException e){
            e.printStackTrace();
        }



    }
}


Properties

package cn.itcast.day10.properties;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;

/*
    java.util.Properties集合extends Hashtable<k,v> implements Map<k,v>
    Properties类表示了一个持久的属性集,Properties可保存在流中,或从流中加载

    1.HashTable是map接口的实现,与HashMap不同的地方是【不允许存储null】,且是线程安全的,也就是同步的
    所以速度会慢
    2.HashTable和Vector一样,jdk1.2后已经被更高级的实现类给取代了,但是HashTable的子类
        Properties依然活跃,Properties集合是一个唯一和io流相结合的集合

        1.可以使用Properties集合中的方法story(outputStream或者writer,String comments),把集合中的临时数据持久化写入到硬盘中存储
            参数:comments 注释,用来解释说明保存文件是做什么用的
                            注释不可以使用中文,会产生乱码,默认是unicode编码,一般使用空字符串
            使用步骤:
                1.创建Properties集合对象,添加数据
                2.创建字节输出流或字符输出流对象,在他的构造方法中绑定要输出的目的地
                3.使用Properties集合中的store方法,把集合中的临时数据,持久化写入到硬盘中存储
                4.释放资源
        2.可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
            注意事项:
                1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格,
                2.存储键值对的文件中,可以使用#注释,被注释的键值对不会再被读取
                3.存储键值对的文件中,键与值默认都是字符串,不用再加引号

    属性列表中每个键及其对应值都是一个字符串
        Properties集合是一个双列集合,key和value默认都是字符串


 */
public class KnowProperties {
    public static void main(String[] args) throws IOException {
        show01();
        //show02();//story
        show03();//load
    }

    private static void show03() throws IOException {
        Properties prop=new Properties();
        prop.load(new FileReader("C:\\baiduyunDownload\\f.txt"));
        Set<String> set = prop.stringPropertyNames();
        for (String s : set) {
            System.out.println(s+prop.getProperty(s));
        }
    }

    private static void show02() throws IOException {
        //1.创建Properties集合对象,添加数据
        Properties prop=new Properties();
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","163");
        //2.创建字节字符输入流,构造方法中绑定输出的目的地
        FileWriter fw=new FileWriter("C:\\baiduyunDownload\\f.txt");
        //3
        prop.store(fw,"save data");
        fw.close();
        /*
            #save data
            #Sat Jan 23 23:36:38 CST 2021
            赵丽颖=168
            迪丽热巴=165
            古力娜扎=163
         */

    }

    /*
        使用Properties集合存储数据,遍历取出Properties集合中的数据
        Properties集合有一些操作字符串的特有方法
            1.Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
            2.String getProperty(String key) 用指定的键在此属性列表中搜索属性。通过key找到value,相当于get方法
            3. Set<String> stringPropertyNames()
                返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,
                则还包括默认属性列表中不同的键。相当于keySet方法
     */
    private static void show01() {
        Properties prop=new Properties();
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","163");
        Set<String> set = prop.stringPropertyNames();
        for (String s : set) {
            System.out.println(s+prop.getProperty(s));
        }
    }

}


C:\java_code\basic-code\grammar-code\src\pro.properties文件
className=cn.itcast.day14.domain.Student
methodName=sleep

过滤器

package cn.itcast.day10.Recursion;

import java.io.File;
import java.io.FileFilter;

/*
    递归计算1---n之间的和

    1.弊端:递归会导致内存中有多个sum方法,导致效率低下
    所以简单的问题如本例,是不推荐使用递归的

    2.本例中我们有个方法是只检索出.avi结尾的文件,其实有个更简便的方法:过滤器
        在File类中有两个和ListFile重载的方法,方法的参数,传递的就是过滤器
        1.File[] listFiles(FileFilter filter)
            java.io.FileFilter接口:用于过滤文件的,期内有一个抽象方法,
                boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中
                    pathname就是使用ListFiles方法遍历目录得到的每一个对象
        2.File[] listFiles(FilenameFilter filter)
            java.io.FilenameFilter接口:实现此接口的类可以过滤文件名称
                boolean accept(File dir , String name)测试指定文件是否应该包含在某一文件列表当中
                    dir:构造方法传递的被遍历的目录
                    String name:使用ListFiles方法遍历目录,获取的每一个文件或者文件夹的名称
        注意:两个接口是没有实现类的,需要我们自己手写实现类, 重写过滤方法accept,在方法中自己
             定义过滤的规则

 */
public class SunNumber {
    public static void main(String[] args) {
        File f1=new File("C:\\baiduyunDownload");
        //long currentime=System.currentTimeMillis();
        //System.out.println(sum(100));//求和
        //System.out.println(Factorial(5));//求阶乘
        //getAllFile(f1);//遍历打印多级目录
        //getAllFileOnlyJava(f1);//只打印.avi文件
        getAllFileFilter(f1);//利用过滤器来检索文件
    }

    private static void getAllFileFilter(File dir) {
        //File[] files = dir.listFiles(new FileFilterImpl());
        //使用匿名内部类
        /*File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                //过滤规则
                return pathname.toString().endsWith(".avi")||pathname.isDirectory();
            }
        });*/
        //使用Lambda表达式
        File[] files=dir.listFiles(pathname -> pathname.toString().endsWith(".avi")||pathname.isDirectory());


        /*
            listFiles方法一共做了三件事情
                1.对构造方法中传递的目录进行遍历,获取目录当中的没一个文件/文件夹-->封装为File对象
                2.listFiles方法会调用参数传递的过滤器中的方法accept
                3.listFiles方法会把遍历得到的每一个File对象,传递给accept方法的参数pathname
            accept方法返回值是一个布尔值
                1.true就会把传递过去的File对象保存到File数组中
                2.false不会保存
         */
        for (File file : files) {
            if(file.isDirectory()){
                getAllFileFilter(file);
            }
            else System.out.println(file.toString());
        }

    }

    private static void getAllFileOnlyJava(File dir) {
        File[] files = dir.listFiles();
        for (File file : files) {
            if (file.isDirectory()){
                getAllFileOnlyJava(file);
            }
            else{
                if(file.toString().endsWith(".avi")){
                    System.out.println(file);
                }
            }
        }
    }

    private static void getAllFile(File dir){
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file);
            if (file.isDirectory()){
                getAllFile(file);
            }
            else{
                System.out.println(file);
            }
        }
    }

    private static int Factorial(int i) {
        if(i==1){
            return 1;
        }
        else{
            return i*Factorial(i-1);
        }
    }

    private static int sum(int n) {
        if(n==1){
            return 1;
        }
        else
            return n+sum(n-1);
    }
}


package cn.itcast.day10.Recursion;

import java.io.File;
import java.io.FileFilter;

public class FileFilterImpl implements FileFilter {
    @Override
    public boolean accept(File pathname) {
        if (pathname.isDirectory()){
            return true;
        }
        return pathname.getName().toLowerCase().endsWith(".avi");
    }
}


BufferedInputStream

package cn.itcast.day11.Buffered;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
    字节缓冲输入流
    1.构造方法
        BufferedInputStream(InputStream in)
              创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
        BufferedInputStream(InputStream in, int size)
              创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
        参数:in 字节输入流
              size 缓冲区大小
        使用步骤:
            1.创建一个FileInputStream对象,里面绑定上我们要输入的数据源
            2.创建BIS对象,构造方法中传递fis对象,提高fis读取效率
            3.使用read方法,读取文件
            4.释放资源
    2.继承自父类的成员方法
        int read()
          从此输入流中读取一个数据字节。
        int read(byte[] b)
          从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
        void close()
          关闭此文件输入流并释放与此流有关的所有系统资源。

 */
public class Demo01BufferedInputStream {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\baiduyunDownload\\code\\a.txt"));
        byte[] bytes=new byte[1024];
        int len=0;
        while((len=bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

    }
}


BufferedOutputStream

package cn.itcast.day11.Buffered;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/*
    1.之前我们学习了四种基本流,字节输入输出流、字符输入输出流,今天学习的缓冲流,是更强大的流,相当于
    对基本流对象的一种增强。
    基本流对象的弊端是效率非常低下,一次只可以处理一个数据
    字节缓冲输入流给基本的字节输入流增加一个缓冲区(数组),提高基本的字节输入流的读取效率,通过
        缓冲区读写,减少系统io次数,从而提高读写效率
        BufferedInputStream(new FileInputStream())
        int len=fis.read();

        1.字节缓冲流 BufferedInputStream  Demo01BufferedOutputStream
        2.字符缓冲流 Demo01BufferedReader       Demo01BufferedWriter

    2.字节缓冲输出流:Java.io.Demo01BufferedOutputStream extends OutputStream
        1.构造方法:
            Demo01BufferedOutputStream(OutputStream out)
                创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
            Demo01BufferedOutputStream(OutputStream out, int size)
                创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
            参数:out:字节输出流,我们可以传递FileOutputStream,缓冲流会给FileOutputStream
                        增加一个缓冲区,提高写入效率
                  size:指定缓冲流内部缓冲区的大小,不指定则默认
            使用步骤:
                1.创建一个字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
                2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高效率
                3.使用BOS对象中的write方法,把数据写入到内部缓冲区中
                4.使用BOS对象中的flush方法,把内部缓冲区中的数据刷新到文件中
                5.释放资源(会先调用flush方法)



 */
public class Demo01BufferedOutputStream {
    public static void main(String[] args) throws IOException {
        java.io.BufferedOutputStream bos=new java.io.BufferedOutputStream(new FileOutputStream("C:\\baiduyunDownload\\code\\a.txt"));
        bos.write("我把数据写入到内部缓冲区中".getBytes());
        bos.flush();
        bos.close();
    }

}


BufferedReader

package cn.itcast.day11.Buffered;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;

/*
    BufferedReader extends Reader
    特有的成员方法
        1.String readLine() 读取一个文本行。读取一行数据
            行的终止符号,通过下列字符之一既可认为某行已终止:换行('\n')、回车('\r')或者\r\n
            返回值:包含该行的字符串而不包含终止符,如果已到达流末尾,则返回null
 */
public class Demo01BufferedReader {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("C:\\baiduyunDownload\\code\\b.txt"));
        //System.out.println(br.readLine());
        String line;
        while((line=br.readLine())!=null){
            System.out.println(line);
        }
        br.close();


    }
}


BufferedWriter

package cn.itcast.day11.Buffered;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

/*
    java.io.Demo01BufferedWriter extends Writer  字符缓冲输入流

    继承自父类的共性成员方法
        abstract  void close()
          关闭此流,但要先刷新它。
        abstract  void flush()
          刷新该流的缓冲。
        void write(char[] cbuf)
          写入字符数组。
        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)
          写入字符串的某一部分。
    构造方法:
        Demo01BufferedWriter(Writer out)
          创建一个使用默认大小输出缓冲区的缓冲字符输出流。
        Demo01BufferedWriter(Writer out, int sz)
          创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
        参数:
            Writer传入一个字符输出流
            sz 指定缓冲区的大小
    特有的成员方法:
        void newLine() 写入一个行分隔符,会根据不同的操作系统,获取不同的分隔符
    使用步骤:
        1.创建字符缓冲输入流,再构造方法中传递字符输入流
        2.调用write方法,把数据写入到内存缓冲区中
        3.调用flush方法,将内存缓冲区中的数据刷新到文件中
        4.释放资源


 */
public class Demo01BufferedWriter {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw=new BufferedWriter(new FileWriter("C:\\baiduyunDownload\\code\\b.txt"));
        for (int i = 0; i < 10; i++) {
            bw.write("我想要上郑州大学");
            bw.newLine();//其实我们经常写的println,调用的就是newLine
        }
        bw.flush();
        bw.close();
    }
}


练习-文本排序

package cn.itcast.day11.Buffered.Practice;

import java.io.*;
import java.util.*;

/*
    文本排序
 */
public class TextSort {
    public static void main(String[] args) throws IOException {
        //show01();//如果文件是使用=或者空格隔开,则可以使用Properties
        show02();
    }

    private static void show02() throws IOException {
        HashMap<String,String> map=new HashMap<>();
        BufferedReader br=new BufferedReader(new FileReader("C:\\baiduyunDownload\\code\\c.txt"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("C:\\baiduyunDownload\\code\\e.txt"));
        String line;
        while ((line=br.readLine())!=null){
            String[] arr=line.split("=");
            map.put(arr[0],arr[1]);
            //map中有数据后,会自动按key进行排序
        }

        for (String s : map.keySet()) {
            System.out.println(s);
            String value=map.get(s);
            line=s+"."+value;
            bw.write(line+"\n");
        }
        bw.flush();
        bw.close();
    }

    private static void show01() throws IOException {
        Properties pr=new Properties();
        pr.load(new BufferedReader(new FileReader("C:\\baiduyunDownload\\code\\c.txt")));
        Set<String> set = pr.stringPropertyNames();
        /*
            这里注意的是,set是没有排序方法的,List才可以使用Collections类方法
            但是TreeSet可以使用Comparator来进行排序
         */
        TreeSet<String> ts=new TreeSet<>(set);
        ts.comparator();
        for (String t : ts) {
            System.out.println(t+pr.getProperty(t));
        }
        BufferedWriter bw=new BufferedWriter(new FileWriter("C:\\baiduyunDownload\\code\\d.txt"));
        for (String t : ts) {
            bw.write(t+"."+pr.getProperty(t)+"\n");

        }
        bw.flush();
        bw.close();
    }
}


对象流

ObjectInputStream
  • 对象的反序列化流
package cn.itcast.day11.Object_Stream;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/*
    对象的反序列化流
    作用:
        把文件中保存的对象,以流的方式读取出来使用
    构造方法:
        protected  ObjectInputStream()
            为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由
             ObjectInputStream 的实现使用的私有数据。
        ObjectInputStream(InputStream in)
            创建从指定 InputStream 读取的 ObjectInputStream。
        参数:
            in 字节输入流
    方法:
        Object readObject() 从ObjectInputStream读取对象
    使用步骤:
        1.创建ois对象,构造方法中填入fis对象
        2.使用readObject方法
        3.释放资源

 */
/*
    ClassNotFoundException:也叫做class文件找不到异常
    当不存在对象的class文件时,抛出此异常
    反序列化前提:
        1.类必须实现serializable
        2.必须存在类对应的class文件
 */
public class Demo01ObjectInputStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\baiduyunDownload\\code\\03.txt"));
        Object o = ois.readObject();
        ois.close();
        System.out.println(o.toString());
    }
}


ObjectOutputStrean
  • 对象的序列化流
package cn.itcast.day11.Object_Stream;
/*
    Person p=new Person("小美女",19)
    1.把对象以流的方式,写入到文件中保存,叫写对象,也叫做对象的【序列化】
    对象中包含的不仅仅是字符,所以要使用字节流
    也就是ObjectOutputStream:对象的序列化流
        使用writeObject();这样子,
            Person p=new Person("小美女",19)----->看不懂的一堆东西
    2.把文件中保存的对象,以流的方式读取出来,叫做读对象,也叫对象的【反序列化】
    读取的文件保存的都是字节,使用字节流
    ObjectInputStream:对象的反序列化流
        使用readObject();这样子,
            看不懂的一堆东西----->Person p=new Person("小美女",19)

 */
/*Exception in thread "main" java.io.NotSerializableException: cn.itcast.day11.Object_Stream.Person
    at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
    at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
    at cn.itcast.day11.Object_Stream.Demo01ObjectOutputStream.main(Demo01ObjectOutputStream.java:45)*/

    /*
        NotSerializableException是未序列化异常:当实例需要具有序列化接口时,抛出此异常,
        序列化运行时或实例的类会抛出该异常,参数为该类的名称

        换言之,某个对象需要序列化,那么其对应的类就必须实现java.io.Serializable接口以启用其序列化
        功能,未实现此接口的类将无法使其任何序列化或反序列化,可序列化的所有子类型本身都是可序列化的
        序列化接口没有任何方法或者属性,仅用来标识
     */

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

/*
    java.io.ObjectOutputStream extends OutputStream
    对象的序列化流
    作用:把对象转换为字节流写入文件中保存

    构造方法:
        protected  ObjectOutputStream()
            为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由
            ObjectOutputStream 的实现使用的私有数据。
        ObjectOutputStream(OutputStream out)
            创建写入指定 OutputStream 的 ObjectOutputStream。
        参数:
            out 字节输出流
    特有的成员方法
        writeObject(Object obj) 将指定的对象写入ObjectOutputStream
    使用步骤:
        1.创建ObjectOutputStream对象,构造方法中传递字节输入流
        2.使用writeObject方法

 */
public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        Person p=new Person("何佳乐",21);
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("C:\\baiduyunDownload\\code\\03.txt"));
        oos.writeObject(p);
        oos.close();
    }
}


反序列化失败
package cn.itcast.day11.Object_Stream;
/*
    另外,当jvm反序列化对象的时候,可以找到class文件,但是class文件再序列化之后发生了修改,则反
    序列化失败,抛出InvalidClassException异常,异常原因如下:
        1.该类的序列版本号与从流中读取的类描述符的版本号不一致
        2.该类包含未知数据类型
        3.该类没有可访问的无参构造方法


    描述一遍序列化反序列化的过程:
        编译器javac.exe编译类的时候,会将Person.java编译为Person.class,Person类实现了Serializable
        接口,就会根据类的定义,给Person.class文件添加一个序列号
            这里:当之后修改了类的定义后,就会给.class文件重新编译生成一个新的序列号,之后反序列化就会
                出错。因为和.txt文件中的序列号不一样了
        反序列化的时候,会使用.class文件中的序列号和,序列化生成的txt文件中的序列号进行比较,如果是
        一样的,则反序列化成功,如果不一样,则抛出InvalidClassException
    问题:每次修改类的定义,都会给class文件生成一个新的序列号
    解决方案:无论是否对类的定义进行修改,我们都不重新生成新的序列号
              可以手动给类添加一个序列号
    格式再Serializable接口中规定
        可序列化类可以通过声明serizalVersionUID的字段(该字段必须是static final 的long型字段)
        显式声明其自己的serialVersionUID
        例如
            static final long serialVersionUID=42L;


 */
public class KnowException {
}


package cn.itcast.day11.Object_Stream;

import java.io.Serializable;

public class Person implements Serializable{
    private static final long serialVersionUID=1L;
    private String name;
    private transient int age;

    public Person() {
    }

    public Person(String name, int age) {

        this.name = name;
        this.age = 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;
    }

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


transient
transient:瞬态关键字
        被transien修饰的成员变量,不可以被序列化

static:静态关键字
        静态优先于非静态加载到内存中,静态优先于对象进入到内存中
        被static修饰的成员变量是不可以被序列化的,序列化的都是对象

练习
package cn.itcast.day11.Object_Stream.Practice;

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;

/*
    练习:
        序列化集合:当我们想在文件中保存多个对象的时候,我们可以把多个对象存储到一个集合中,对集合
        进行序列化和反序列化
    分析:
        1.定义一个存储Person对象的ArrayList集合
        2.往ArrayList集合中存储Person对象
        3.创建一个序列化流ObjectOutputStream
        4.使用Oos的writeObject对集合进行序列化
        5.创建OIS对象
        6.使用readObject方法读取文件中保存的集合
        7.把Object类型的对象转变为ArrayList类型
        8.遍历ArrayList集合
 */
public class SerialzableCollection {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ArrayList<Person> arr=new ArrayList<>();
        arr.add(new Person("何佳乐",19));
        arr.add(new Person("曹越",21));
        arr.add(new Person("吴超",22));
        arr.add(new Person("武文强",11));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\baiduyunDownload\\code\\04.txt"));
        oos.writeObject(arr);
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("C:\\baiduyunDownload\\code\\04.txt"));
        Object o = ois.readObject();
        ArrayList<Person> arr2=(ArrayList)o;
        Iterator<Person> it=((ArrayList) o).iterator();
        while(it.hasNext()){
            System.out.println(it.next().toString());
        }
        ois.close();
        oos.close();
    }

}


package cn.itcast.day11.Object_Stream.Practice;

import java.io.Serializable;

public class Person implements Serializable{
    private static final long serialVersionUID=1L;
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {

        this.name = name;
        this.age = 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;
    }

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


InputStreamReader

package cn.itcast.day11.ReverStream;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/*
    InputStreamReader是Reader的子类,是为了解决不同软件编码格式不同,所造成的乱码现象,InputStreamReader
    可以指定编码格式,解码(把看不懂的闹成看得懂的)
    InputStreamReader是字节流通向字符流的桥梁:他使用指定的charset读取字节并将其解析为字符

 */
public class Demo01InputStreamReader {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\baiduyunDownload\\code\\f.txt"), "gbk");
        int len=0;
        while((len=isr.read())!=-1){
            System.out.println((char)len);
        }
    }

}


OutputStream

package cn.itcast.day11.ReverStream;

import java.io.*;

/*
    OutputStreamReader extends Reader ,是字符流通向字节流的桥梁,可使用指定的charset将要写入
    流中的字符编码为字节

    继承自父类的共性成员方法:
        abstract  void close()
          关闭此流,但要先刷新它。
        abstract  void flush()
          刷新该流的缓冲。
        void write(char[] cbuf)
          写入字符数组。
        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)
          写入字符串的某一部分。
    构造方法:
        OutputStreamWriter(OutputStream out)
          创建使用默认字符编码的 OutputStreamWriter。
        OutputStreamWriter(OutputStream out, String charsetName)
          创建使用指定字符集的 OutputStreamWriter
        参数:
            out 字节输出流,可以用来写转换之后的字节到文件中
            charsetName 指定的编码表名称,不区分大小写
    使用步骤:
        1.创建一个OutputStreamWriter对象,构造方法中传递字节输出流对象OutputStream和指定的编码表名称
        2.使用OutputStreamWriter对象的writer方法,把字符转换为字节,存储到缓冲区中(编码)
        3.使用OutputStreamWriter对象的flush方法,把内存缓冲区中的字节给刷新到文件中,也就是
            用字节流写字节的方式
 */
public class Demo02OutputStreamWriter  {
    public static void main(String[] args) throws IOException {
        write_utf_8();
    }
    /*
        使用转换流
     */
    private static void write_utf_8() throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\baiduyunDownload\\code\\01.txt"),"gbk");
        osw.write("你好");
        osw.flush();
        osw.close();
    }
}


字符编码

ASCⅡ,美国用的,基本字符集用七位表示一个字符,共128字符,扩展字符集使用8位
iso-8859-1 拉丁码表,欧洲用的,使用单字节编码,兼容ASC编码
GBxxx字符集
    GBK使用双字节编码,支持繁体汉字以及日韩汉字
    GB18030额外支持了少数民族文字
Unicode--万国码
    最多用四个字节的数字表示字符
    utf-8  web开发要使用这个
    utf-16
    utf-32

PrintStream

package cn.itcast.day11.SoutStream;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

/*
    PrintStream为其他输出流添加了新功能,使他们可以方便的打印各种数据值的表示形式
        print
        println
    特点:
        1.与其他输出流不同,PrintStream永远不会抛出IOException异常
        2.只负责数据的输出,不负责数据的读取
    构造方法:
        1.PrintStream(File file):输出目的地是一个文件
        2.PrintStream(OutputStream out)输出目的地是一个字节输出流
        3.PrintStream(String fileName)输出目的地是一个文件路径
    PrintStream extends OutputStream
    注意事项:
        如果使用继承自父类的write方法写数据,那么查看数据的时候会查看编码表  97---1
        如果使用自己特有的方法println写数据,写的数据原样输出   97---97
 */

/*
    可以改变输出语句的目的地(打印流的流向)
    输出语句,默认控制台输出
    使用System类中的方法setOut方法改变输出语句的目的地为,参数中传递的打印流的目的地
        setOut(PrintStream ps)
            重新分配标准(控制台)输出流
 */
public class Demo01PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        //show01();
        show02();

    }

    private static void show01() throws FileNotFoundException {
        //System.out.println("Hello World!");

        //创建打印流对象,构造方法中绑定要输出的目的地,
        PrintStream ps=new PrintStream("C:\\baiduyunDownload\\code\\05.txt");
        ps.write(97);
        ps.println(97);
        ps.close();
    }

    private static void show02() throws FileNotFoundException {

        System.out.println("我是在控制台输出");
        PrintStream ps=new PrintStream("C:\\baiduyunDownload\\code\\06.txt");
        System.setOut(ps);
        System.out.println("我在打印流的目的地中输出");
        ps.close();
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值