JAVA SE NOTE


基本数据类型前端

  • 整数型:byte/short/int/long
  • 浮点型:float/double
  • 字符型:char
  • 布尔型:boolean

引用数据类型java

  • 字符串/数组/类/接口/Lambda

注意mysql

  • byte占用8位,每位用0或1表示,可以表示256(2^8)个数据,范围-128~127
  • int范围:-2147483648——2147483647(约为21.4亿)
  • byte/short/char这三种数据类型在运算时,会首先提高为int类型,而后再进行计算。
  • 基本类型中只有boolean类型不能进行运算

switch (i % 2) {
        case 1:
            System.out.println("奇数");
            break;
        case 0:
            System.out.println("偶数");
            break;
        default:
            System.out.println("输入不合法");
            break;
    }

switch括号中得数据类型:
基本:byte/short/char/int   引用:String字符串/enum枚举linux


动态初始化数组
int[] arrayA=new int[20];
静态初始化数组
String[] arrayB={"hello","world","java"};程序员






数组的长度运行期间不可改变
正则表达式





方法覆盖重写的注意事项:spring

  1. 必须保证父子类之间方法的名称相同,参数列表也相同。
    @Override:写在方法前面,用来检测是否是有效的正确覆盖重写。
    这个注解就算不写,只要知足要求,也是正确的方法覆盖重写。sql

  2. 子类方法的返回值必须【小于等于】父类方法的返回值范围。
    小扩展提示:java.lang.Object类是全部类的公共最高父类(祖宗类),java.lang.String就是Object的子类。数据库

  3. 子类方法的权限必须【大于等于】父类方法的权限修饰符。
    小扩展提示:public > protected > (default) > private
    备注:(default)不是关键字default,而是什么都不写,留空。编程


一旦使用static修饰成员方法,那么这就成为了静态方法。静态方法不属于对象,而是属于类的。

若是没有static关键字,那么必须首先建立对象,而后经过对象才能使用它。
若是有了static关键字,那么不须要建立对象,直接就能经过类名称来使用它。

不管是成员变量,仍是成员方法。若是有了static,都推荐使用类名称进行调用。
静态变量:类名称.静态变量
静态方法:类名称.静态方法()

注意事项:

  1. 静态不能直接访问非静态。
    缘由:由于在内存当中是【先】有的静态内容,【后】有的非静态内容。
    “先人不知道后人,可是后人知道先人。”
  2. 静态方法当中不能用this。
    缘由:this表明当前对象,经过谁调用的方法,谁就是当前对象。

若是是Java 7,那么接口中能够包含的内容有:

  1. 常量
  2. 抽象方法

若是是Java 8,还能够额外包含有:

  1. 默认方法
  2. 静态方法

若是是Java 9,还能够额外包含有:

  1. 私有方法

小节一下类的权限修饰符:
public > protected > (default) > private
定义一个类的时候,权限修饰符规则:

  1. 外部类:public / (default)
  2. 成员内部类:public / protected / (default) / private
  3. 局部内部类:什么都不能写

局部内部类,若是但愿访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

备注:从Java 8+开始,只要局部变量事实不变,那么final关键字能够省略。

缘由:

  1. new出来的对象在堆内存当中。
  2. 局部变量是跟着方法走的,在栈内存当中。
  3. 方法运行结束以后,马上出栈,局部变量就会马上消失。
  4. 可是new出来的对象会在堆当中持续存在,直到垃圾回收消失。

    public class MyOuter {

    public void methodOuter() {
         int num = 10; // 所在方法的局部变量
    
         class MyInner {
             public void methodInner() {
                 System.out.println(num);
             }
         }
     }

    }


package cn.itcast.day11.demo05;

/*
若是接口的实现类(或者是父类的子类)只须要使用惟一的一次,
那么这种状况下就能够省略掉该类的定义,而改成使用【匿名内部类】。

匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() {
    // 覆盖重写全部抽象方法
};

对格式“new 接口名称() {...}”进行解析:
1. new表明建立对象的动做
2. 接口名称就是匿名内部类须要实现哪一个接口
3. {...}这才是匿名内部类的内容

另外还要注意几点问题:
1. 匿名内部类,在【建立对象】的时候,只能使用惟一一次。
若是但愿屡次建立对象,并且类的内容同样的话,那么就须要使用单独定义的实现类了。
2. 匿名对象,在【调用方法】的时候,只能调用惟一一次。
若是但愿同一个对象,调用屡次方法,那么必须给对象起个名字。
3. 匿名内部类是省略了【实现类/子类名称】,可是匿名对象是省略了【对象名称】
强调:匿名内部类和匿名对象不是一回事!!!
 */
public class DemoMain {

    public static void main(String[] args) {
//        MyInterface obj = new MyInterfaceImpl();
//        obj.method();

//        MyInterface some = new MyInterface(); // 错误写法!

        // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
        MyInterface objA = new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名内部类实现了方法!111-A");
            }

            @Override
            public void method2() {
                System.out.println("匿名内部类实现了方法!222-A");
            }
        };
        objA.method1();
        objA.method2();
        System.out.println("=================");

        // 使用了匿名内部类,并且省略了对象名称,也是匿名对象
        new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名内部类实现了方法!111-B");
            }

            @Override
            public void method2() {
                System.out.println("匿名内部类实现了方法!222-B");
            }
        }.method1();
        // 由于匿名对象没法调用第二次方法,因此须要再建立一个匿名内部类的匿名对象
        new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名内部类实现了方法!111-B");
            }

            @Override
            public void method2() {
                System.out.println("匿名内部类实现了方法!222-B");
            }
        }.method2();
    }

}

Scanner:

Scanner sc=new Scanner(System.in);
    int i = sc.nextInt();
    System.out.println(i);
    String next = sc.next();
    System.out.println(next);

Random:

Random ran=new Random();
    int in1 = ran.nextInt();
    System.out.println(in1);
    System.out.println("---------");
    for (int i = 0; i < 100; i++) {
        int in2 = ran.nextInt(10);
        System.out.print(in2+" ");
    }

ArrayList经常使用方法:

1. public boolean add(E e):向集合当中添加元素,参数的类型和泛型一致。返回值表明添加是否成功。
2. public E get(int index):从集合当中获取元素,参数是索引编号,返回值就是对应位置的元素。
3. public E remove(int index):从集合当中删除元素,参数是索引编号,返回值就是被删除掉的元素。
4. public int size():获取集合的尺寸长度,返回值是集合中包含的元素个数。
5. public <T> T[] toArray(T[] a): 按适当顺序(从第一个到最后一个元素)返回包含此列表中全部元素的数组;返回数组的运行时类型是指定数组的运行时类型。

String:

java.lang.String类表明字符串。
API当中说:Java 程序中的全部字符串字面值(如 "abc" )都做为此类的实例实现。
其实就是说:程序当中全部的双引号字符串,都是String类的对象。(就算没有new,也照样是。)

字符串的特色:

  1. 字符串的内容永不可变。【重点】
  2. 正是由于字符串不可改变,因此字符串是能够共享使用的。
  3. 字符串效果上至关因而char[]字符数组,可是底层原理是byte[]字节数组。

建立字符串的常见3+1种方式。
三种构造方法:
public String():建立一个空白字符串,不含有任何内容。
public String(char[] array):根据字符数组的内容,来建立对应的字符串。
public String(byte[] array):根据字节数组的内容,来建立对应的字符串。
一种直接建立:
String str = "Hello"; // 右边直接用双引号

注意:直接写上双引号,就是字符串对象。

package cn.itcast.day08.demo01;

/*
字符串常量池:程序当中直接写上的双引号字符串,就在字符串常量池中。

对于基本类型来讲,==是进行数值的比较。
对于引用类型来讲,==是进行【地址值】的比较。
 */
public class Demo02StringPool {

    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";

        char[] charArray = {'a', 'b', 'c'};
        String str3 = new String(charArray);

        System.out.println(str1 == str2); // true
        System.out.println(str1 == str3); // false
        System.out.println(str2 == str3); // false
    }

}

1.  boolean equals(Object anObject) 将此字符串与指定的对象比较。
2.  boolean equalsIgnoreCase(String anotherString) 将此 String 与另外一个 String 比较,不考虑大小写。
3.  int length() 返回此字符串的长度。 
4.  String concat(String str)  将指定字符串链接到此字符串的结尾。
5.  char charAt(int index) 返回指定索引处的 char 值。  
6.  int indexOf(String str)  返回指定子字符串在此字符串中第一次出现处的索引。 
7.  String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串。 
8.  String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串。 
9.  char[] toCharArray() 将此字符串转换为一个新的字符数组。 
10. byte[] getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 
11. replace(CharSequence target, CharSequence replacement) 使用指定的字面值替换序列替换此字符串全部匹配字面值目标序列的子字符串。
12. String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。 
13. String trim() 返回字符串的副本,忽略前导空白和尾部空白。 
14. String toLowerCase() 使用默认语言环境的规则将此 String 中的全部字符都转换为小写。 
15. String toUpperCase() 使用默认语言环境的规则将此 String 中的全部字符都转换为大写。 
16. boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开始。 
17. boolean startsWith(String prefix, int toffset) 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
18. boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结束。 
19.  boolean contains(CharSequence s) 当且仅当此字符串包含指定的 char 值序列时,返回 true。

Arrays工具类

package cn.itcast.day08.demo04;

import java.util.Arrays;

/*
java.util.Arrays是一个与数组相关的工具类,里面提供了大量静态方法,用来实现数组常见的操做。

public static String toString(数组):将参数数组变成字符串(按照默认格式:[元素1, 元素2, 元素3...])
public static void sort(数组):按照默认升序(从小到大)对数组的元素进行排序。

备注:
1. 若是是数值,sort默认按照升序从小到大
2. 若是是字符串,sort默认按照字母升序
3. 若是是自定义的类型,那么这个自定义的类须要有Comparable或者Comparator接口的支持。
 */
public class Demo01Arrays {

    public static void main(String[] args) {
        int[] intArray = {10, 20, 30};
        // 将int[]数组按照默认格式变成字符串
        String intStr = Arrays.toString(intArray);
        System.out.println(intStr); // [10, 20, 30]

        int[] array1 = {2, 1, 3, 10, 6};
        Arrays.sort(array1);
        System.out.println(Arrays.toString(array1)); // [1, 2, 3, 6, 10]

        String[] array2 = {"bbb", "aaa", "ccc"};
        Arrays.sort(array2);
        System.out.println(Arrays.toString(array2)); // [aaa, bbb, ccc]
    }

}

Math

package cn.itcast.day08.demo04;

/*
java.util.Math类是数学相关的工具类,里面提供了大量的静态方法,完成与数学运算相关的操做。

public static double abs(double num):获取绝对值。有多种重载。
public static double ceil(double num):向上取整。
public static double floor(double num):向下取整。
public static long round(double num):四舍五入。

Math.PI表明近似的圆周率常量(double)。
 */
public class Demo03Math {

    public static void main(String[] args) {
        // 获取绝对值
        System.out.println(Math.abs(3.14)); // 3.14
        System.out.println(Math.abs(0)); // 0
        System.out.println(Math.abs(-2.5)); // 2.5
        System.out.println("================");

        // 向上取整
        System.out.println(Math.ceil(3.9)); // 4.0
        System.out.println(Math.ceil(3.1)); // 4.0
        System.out.println(Math.ceil(3.0)); // 3.0
        System.out.println("================");

        // 向下取整,抹零
        System.out.println(Math.floor(30.1)); // 30.0
        System.out.println(Math.floor(30.9)); // 30.0
        System.out.println(Math.floor(31.0)); // 31.0
        System.out.println("================");

        System.out.println(Math.round(20.4)); // 20
        System.out.println(Math.round(10.5)); // 11
    }

}

包装类

package com.itheima.demo07Integer;
/*
    装箱:把基本类型的数据,包装到包装类中(基本类型的数据->包装类)
        构造方法:
            Integer(int value) 构造一个新分配的 Integer 对象,它表示指定的 int 值。
            Integer(String s) 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。
                传递的字符串,必须是基本类型的字符串,不然会抛出异常 "100" 正确  "a" 抛异常
        静态方法:
            static Integer valueOf(int i) 返回一个表示指定的 int 值的 Integer 实例。
            static Integer valueOf(String s) 返回保存指定的 String 的值的 Integer 对象。
    拆箱:在包装类中取出基本类型的数据(包装类->基本类型的数据)
        成员方法:
            int intValue() 以 int 类型返回该 Integer 的值。
 */
public class Demo01Integer {
    public static void main(String[] args) {
        //装箱:把基本类型的数据,包装到包装类中(基本类型的数据->包装类)
        //构造方法
        Integer in1 = new Integer(1);//方法上有横线,说明方法过期了
        System.out.println(in1);//1 重写了toString方法

        Integer in2 = new Integer("1");
        System.out.println(in2);//1

        //静态方法
        Integer in3 = Integer.valueOf(1);
        System.out.println(in3);

        //Integer in4 = Integer.valueOf("a");//NumberFormatException数字格式化异常
        Integer in4 = Integer.valueOf("1");
        System.out.println(in4);

        //拆箱:在包装类中取出基本类型的数据(包装类->基本类型的数据)
        int i = in1.intValue();
        System.out.println(i);
    }
}


    package com.itheima.demo07Integer;
/*
    基本类型与字符串类型之间的相互转换
    基本类型->字符串(String)
        1.基本类型的值+""  最简单的方法(工做中经常使用)
        2.包装类的静态方法toString(参数),不是Object类的toString() 重载
            static String toString(int i) 返回一个表示指定整数的 String 对象。
        3.String类的静态方法valueOf(参数)
            static String valueOf(int i) 返回 int 参数的字符串表示形式。
    字符串(String)->基本类型
        使用包装类的静态方法parseXXX("字符串");
            Integer类: static int parseInt(String s)
            Double类: static double parseDouble(String s)
 */
public class Demo03Integer {
    public static void main(String[] args) {
        //基本类型->字符串(String)
        int i1 = 100;
        String s1 = i1+"";
        System.out.println(s1+200);//100200

        String s2 = Integer.toString(100);
        System.out.println(s2+200);//100200

        String s3 = String.valueOf(100);
        System.out.println(s3+200);//100200

        //字符串(String)->基本类型
        int i = Integer.parseInt(s1);
        System.out.println(i-10);

        int a = Integer.parseInt("a");//NumberFormatException
        System.out.println(a);
    }
}

StringBuilder

package com.itheima.demo06StringBuilder;
/*
    StringBuilder和String能够相互转换:
        String->StringBuilder:可使用StringBuilder的构造方法
            StringBuilder(String str) 构造一个字符串生成器,并初始化为指定的字符串内容。
        StringBuilder->String:可使用StringBuilder中的toString方法
            public String toString():将当前StringBuilder对象转换为String对象。
 */
public class Demo03StringBuilder {
    public static void main(String[] args) {
        //String->StringBuilder
        String str = "hello";
        System.out.println("str:"+str);
        StringBuilder bu = new StringBuilder(str);
        //往StringBuilder中添加数据
        bu.append("world");
        System.out.println("bu:"+bu);

        //StringBuilder->String
        String s = bu.toString();
        System.out.println("s:"+s);
    }
}

Date

package com.itheima.demo02.Date;
/*
    java.util.Date:表示日期和时间的类
    类 Date 表示特定的瞬间,精确到毫秒。
    毫秒:千分之一秒 1000毫秒=1秒
    特定的瞬间:一个时间点,一刹那时间
    2088-08-08 09:55:33:333 瞬间
    2088-08-08 09:55:33:334 瞬间
    2088-08-08 09:55:33:334 瞬间
    ...
    毫秒值的做用:能够对时间和日期进行计算
    2099-01-03 到 2088-01-01 中间一共有多少天
    能够日期转换为毫秒进行计算,计算完毕,在把毫秒转换为日期

    把日期转换为毫秒:
        当前的日期:2088-01-01
        时间原点(0毫秒):1970 年 1 月 1 日 00:00:00(英国格林威治)
        就是计算当前日期到时间原点之间一共经历了多少毫秒 (3742767540068L)
    注意:
        中国属于东八区,会把时间增长8个小时
        1970 年 1 月 1 日 08:00:00

    把毫秒转换为日期:
        1 天 = 24 × 60 × 60 = 86400 秒  = 86400 x 1000 = 86400000毫秒
 */
public class Demo01Date {
    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());//获取当前系统时间到1970 年 1 月 1 日 00:00:00经历了多少毫秒
    }
}


    package com.itheima.demo02.Date;

import java.util.Date;

public class Demo02Date {
    public static void main(String[] args) {
        demo03();
    }

    /*
        long getTime() 把日期转换为毫秒值(至关于System.currentTimeMillis()方法)
          返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
     */
    private static void demo03() {
        Date date = new Date();
        long time = date.getTime();
        System.out.println(time);//3742777636267
    }

    /*
        Date类的带参数构造方法
        Date(long date) :传递毫秒值,把毫秒值转换为Date日期
     */
    private static void demo02() {
        Date date = new Date(0L);
        System.out.println(date);// Thu Jan 01 08:00:00 CST 1970

        date = new Date(3742767540068L);
        System.out.println(date);// Sun Aug 08 09:39:00 CST 2088
    }

    /*
        Date类的空参数构造方法
        Date() 获取当前系统的日期和时间
     */
    private static void demo01() {
        Date date = new Date();
        System.out.println(date);//Sun Aug 08 12:23:03 CST 2088
    }
}

DateFormat

package com.itheima.demo03.DateFormat;

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

/*
    java.text.DateFormat:是日期/时间格式化子类的抽象类
    做用:
        格式化(也就是日期 -> 文本)、解析(文本-> 日期)
    成员方法:
        String format(Date date)  按照指定的模式,把Date日期,格式化为符合模式的字符串
        Date parse(String source)  把符合模式的字符串,解析为Date日期
    DateFormat类是一个抽象类,没法直接建立对象使用,可使用DateFormat类的子类

    java.text.SimpleDateFormat extends DateFormat

    构造方法:
        SimpleDateFormat(String pattern)
          用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
        参数:
             String pattern:传递指定的模式
        模式:区分大小写的
            y   年
            M   月
            d   日
            H   时
            m   分
            s   秒
            S   毫秒
        写对应的模式,会把模式替换为对应的日期和时间
            "yyyy-MM-dd HH:mm:ss"
        注意:
            模式中的字母不能更改,链接模式的符号能够改变
             "yyyy年MM月dd日 HH时mm分ss秒"

 */
public class Demo01DateFormat {
    public static void main(String[] args) throws ParseException {
        demo02();
    }

    /*
         使用DateFormat类中的方法parse,把文本解析为日期
         使用步骤:
            1.建立SimpleDateFormat对象,构造方法中传递指定的模式
            2.调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串,解析为Date日期
            注意:
                public Date parse(String source) throws ParseException
                parse方法声明了一个异常叫ParseException
                若是字符串和构造方法的模式不同,那么程序就会抛出此异常
                调用一个抛出了异常的方法,就必须的处理这个异常,要么throws继续抛出这个异常,要么try catch本身处理
     */
    private static void demo02() throws ParseException {
        //1.建立SimpleDateFormat对象,构造方法中传递指定的模式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        //2.调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串,解析为Date日期
        //Date parse(String source)  把符合模式的字符串,解析为Date日期
        Date date = sdf.parse("2088年08月08日 15时51分54秒");
        System.out.println(date);
    }

    /*
        使用DateFormat类中的方法format,把日期格式化为文本
        使用步骤:
            1.建立SimpleDateFormat对象,构造方法中传递指定的模式
            2.调用SimpleDateFormat对象中的方法format,按照构造方法中指定的模式,把Date日期格式化为符合模式的字符串(文本)
     */
    private static void demo01() {
        //1.建立SimpleDateFormat对象,构造方法中传递指定的模式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        //2.调用SimpleDateFormat对象中的方法format,按照构造方法中指定的模式,把Date日期格式化为符合模式的字符串(文本)
        //String format(Date date)  按照指定的模式,把Date日期,格式化为符合模式的字符串
        Date date = new Date();
        String d = sdf.format(date);
        System.out.println(date);//Sun Aug 08 15:51:54 CST 2088
        System.out.println(d);//2088年08月08日 15时51分54秒
    }
}

Calendar

package com.itheima.demo04.Calendar;

import java.util.Calendar;

/*
    java.util.Calendar类:日历类
    Calendar类是一个抽象类,里边提供了不少操做日历字段的方法(YEAR、MONTH、DAY_OF_MONTH、HOUR )
    Calendar类没法直接建立对象使用,里边有一个静态方法叫getInstance(),该方法返回了Calendar类的子类对象
    static Calendar getInstance() 使用默认时区和语言环境得到一个日历。
 */
public class Demo01Calendar {
    public static void main(String[] args) {
        Calendar c = Calendar.getInstance();//多态
        System.out.println(c);
    }

}


package com.itheima.demo04.Calendar;

import java.util.Calendar;
import java.util.Date;

/*
    Calendar类的经常使用成员方法:
        public int get(int field):返回给定日历字段的值。
        public void set(int field, int value):将给定的日历字段设置为给定值。
        public abstract void add(int field, int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量。
        public Date getTime():返回一个表示此Calendar时间值(从历元到如今的毫秒偏移量)的Date对象。
    成员方法的参数:
        int field:日历类的字段,可使用Calendar类的静态成员变量获取
            public static final int YEAR = 1;   年
            public static final int MONTH = 2;  月
            public static final int DATE = 5;   月中的某一天
            public static final int DAY_OF_MONTH = 5;月中的某一天
            public static final int HOUR = 10;      时
            public static final int MINUTE = 12;    分
            public static final int SECOND = 13;    秒
 */
public class Demo02Calendar {
    public static void main(String[] args) {
        demo04();
    }

    /*
        public Date getTime():返回一个表示此Calendar时间值(从历元到如今的毫秒偏移量)的Date对象。
        把日历对象,转换为日期对象
     */
    private static void demo04() {
        //使用getInstance方法获取Calendar对象
        Calendar c = Calendar.getInstance();

        Date date = c.getTime();
        System.out.println(date);
    }

    /*
        public abstract void add(int field, int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量。
        把指定的字段增长/减小指定的值
        参数:
            int field:传递指定的日历字段(YEAR,MONTH...)
            int amount:增长/减小指定的值
                正数:增长
                负数:减小
     */
    private static void demo03() {
        //使用getInstance方法获取Calendar对象
        Calendar c = Calendar.getInstance();

        //把年增长2年
        c.add(Calendar.YEAR,2);
        //把月份减小3个月
        c.add(Calendar.MONTH,-3);


        int year = c.get(Calendar.YEAR);
        System.out.println(year);

        int month = c.get(Calendar.MONTH);
        System.out.println(month);//西方的月份0-11 东方:1-12

        //int date = c.get(Calendar.DAY_OF_MONTH);
        int date = c.get(Calendar.DATE);
        System.out.println(date);
    }

    /*
        public void set(int field, int value):将给定的日历字段设置为给定值。
        参数:
            int field:传递指定的日历字段(YEAR,MONTH...)
            int value:给指定字段设置的值
     */
    private static void demo02() {
        //使用getInstance方法获取Calendar对象
        Calendar c = Calendar.getInstance();

        //设置年为9999
        c.set(Calendar.YEAR,9999);
        //设置月为9月
        c.set(Calendar.MONTH,9);
        //设置日9日
        c.set(Calendar.DATE,9);

        //同时设置年月日,可使用set的重载方法
        c.set(8888,8,8);

        int year = c.get(Calendar.YEAR);
        System.out.println(year);

        int month = c.get(Calendar.MONTH);
        System.out.println(month);//西方的月份0-11 东方:1-12

        int date = c.get(Calendar.DATE);
        System.out.println(date);
    }

    /*
        public int get(int field):返回给定日历字段的值。
        参数:传递指定的日历字段(YEAR,MONTH...)
        返回值:日历字段表明的具体的值
     */
    private static void demo01() {
        //使用getInstance方法获取Calendar对象
        Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        System.out.println(year);

        int month = c.get(Calendar.MONTH);
        System.out.println(month);//西方的月份0-11 东方:1-12

        //int date = c.get(Calendar.DAY_OF_MONTH);
        int date = c.get(Calendar.DATE);
        System.out.println(date);
    }
}

System

package com.itheima.demo05.System;

import java.util.Arrays;

/*
    java.lang.System类中提供了大量的静态方法,能够获取与系统相关的信息或系统级操做,在System类的API文档中,经常使用的方法有:
        public static long currentTimeMillis():返回以毫秒为单位的当前时间。
        public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):将数组中指定的数据拷贝到另外一个数组中。
 */
public class Demo01System {
    public static void main(String[] args) {
        demo02();
        StringBuilder sb = new StringBuilder();
    }

    /*
        public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):将数组中指定的数据拷贝到另外一个数组中。
        参数:
            src - 源数组。
            srcPos - 源数组中的起始位置(起始索引)。
            dest - 目标数组。
            destPos - 目标数据中的起始位置。
            length - 要复制的数组元素的数量。
        练习:
            将src数组中前3个元素,复制到dest数组的前3个位置上
                复制元素前:
                src数组元素[1,2,3,4,5],dest数组元素[6,7,8,9,10]
                复制元素后:
                src数组元素[1,2,3,4,5],dest数组元素[1,2,3,9,10]
     */
    private static void demo02() {
        //定义源数组
        int[] src = {1,2,3,4,5};
        //定义目标数组
        int[] dest = {6,7,8,9,10};
        System.out.println("复制前:"+ Arrays.toString(dest));
        //使用System类中的arraycopy把源数组的前3个元素复制到目标数组的前3个位置上
        System.arraycopy(src,0,dest,0,3);
        System.out.println("复制后:"+ Arrays.toString(dest));
    }

    /*
        public static long currentTimeMillis():返回以毫秒为单位的当前时间。
        用来程序的效率
        验证for循环打印数字1-9999所须要使用的时间(毫秒)
     */
    private static void demo01() {
        //程序执行前,获取一次毫秒值
        long s = System.currentTimeMillis();
        //执行for循环
        for (int i = 1; i <=9999 ; i++) {
            System.out.println(i);
        }
        //程序执行后,获取一次毫秒值
        long e = System.currentTimeMillis();
        System.out.println("程序共耗时:"+(e-s)+"毫秒");//程序共耗时:106毫秒
    }
}

集合

Collection

package com.itheima.demo01.Collection;

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

/*
    java.util.Collection接口
        全部单列集合的最顶层的接口,里边定义了全部单列集合共性的方法
        任意的单列集合均可以使用Collection接口中的方法


    共性的方法:
      public boolean add(E e):  把给定的对象添加到当前集合中 。
      public void clear() :清空集合中全部的元素。
      public boolean remove(E e): 把给定的对象在当前集合中删除。
      public boolean contains(E e): 判断当前集合中是否包含给定的对象。
      public boolean isEmpty(): 判断当前集合是否为空。
      public int size(): 返回集合中元素的个数。
      public Object[] toArray(): 把集合中的元素,存储到数组中。
 */
public class Demo01Collection {
    public static void main(String[] args) {
        //建立集合对象,可使用多态
        //Collection<String> coll = new ArrayList<>();
        Collection<String> coll = new HashSet<>();
        System.out.println(coll);//重写了toString方法  []

        /*
            public boolean add(E e):  把给定的对象添加到当前集合中 。
            返回值是一个boolean值,通常都返回true,因此能够不用接收
         */
        boolean b1 = coll.add("张三");
        System.out.println("b1:"+b1);//b1:true
        System.out.println(coll);//[张三]
        coll.add("李四");
        coll.add("李四");
        coll.add("赵六");
        coll.add("田七");
        System.out.println(coll);//[张三, 李四, 赵六, 田七]

        /*
            public boolean remove(E e): 把给定的对象在当前集合中删除。
            返回值是一个boolean值,集合中存在元素,删除元素,返回true
                                集合中不存在元素,删除失败,返回false
         */
        boolean b2 = coll.remove("赵六");
        System.out.println("b2:"+b2);//b2:true

        boolean b3 = coll.remove("赵四");
        System.out.println("b3:"+b3);//b3:false
        System.out.println(coll);//[张三, 李四, 田七]

        /*
            public boolean contains(E e): 判断当前集合中是否包含给定的对象。
            包含返回true
            不包含返回false
         */
        boolean b4 = coll.contains("李四");
        System.out.println("b4:"+b4);//b4:true

        boolean b5 = coll.contains("赵四");
        System.out.println("b5:"+b5);//b5:false

        //public boolean isEmpty(): 判断当前集合是否为空。 集合为空返回true,集合不为空返回false
        boolean b6 = coll.isEmpty();
        System.out.println("b6:"+b6);//b6:false

        //public int size(): 返回集合中元素的个数。
        int size = coll.size();
        System.out.println("size:"+size);//size:3

        //public Object[] toArray(): 把集合中的元素,存储到数组中。
        Object[] arr = coll.toArray();
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        //public void clear() :清空集合中全部的元素。可是不删除集合,集合还存在
        coll.clear();
        System.out.println(coll);//[]
        System.out.println(coll.isEmpty());//true
    }
}

Iterator接口(迭代器)

package com.itheima.demo02.Iterator;

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

/*
    java.util.Iterator接口:迭代器(对集合进行遍历)
    有两个经常使用的方法
        boolean hasNext() 若是仍有元素能够迭代,则返回 true。
            判断集合中还有没有下一个元素,有就返回true,没有就返回false
        E next() 返回迭代的下一个元素。
            取出集合中的下一个元素
    Iterator迭代器,是一个接口,咱们没法直接使用,须要使用Iterator接口的实现类对象,获取实现类的方式比较特殊
    Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象
        Iterator<E> iterator() 返回在此 collection 的元素上进行迭代的迭代器。

    迭代器的使用步骤(重点):
        1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
        2.使用Iterator接口中的方法hasNext判断还有没有下一个元素
        3.使用Iterator接口中的方法next取出集合中的下一个元素
 */
public class Demo01Iterator {
    public static void main(String[] args) {
        //建立一个集合对象
        Collection<String> coll = new ArrayList<>();
        //往集合中添加元素
        coll.add("姚明");
        coll.add("科比");
        coll.add("麦迪");
        coll.add("詹姆斯");
        coll.add("艾弗森");

        /*
            1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
            注意:
                Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
         */
        //多态  接口            实现类对象
        Iterator<String> it = coll.iterator();


        /*
            发现使用迭代器取出集合中元素的代码,是一个重复的过程
            因此咱们可使用循环优化
            不知道集合中有多少元素,使用while循环
            循环结束的条件,hasNext方法返回false
         */
        while(it.hasNext()){
            String e = it.next();
            System.out.println(e);
        }
        System.out.println("----------------------");
        for(Iterator<String> it2 = coll.iterator();it2.hasNext();){
            String e = it2.next();
            System.out.println(e);
        }


       /* //2.使用Iterator接口中的方法hasNext判断还有没有下一个元素
        boolean b = it.hasNext();
        System.out.println(b);//true
        //3.使用Iterator接口中的方法next取出集合中的下一个元素
        String s = it.next();
        System.out.println(s);//姚明

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);//没有元素,返回false
        s = it.next();//没有元素,在取出元素会抛出NoSuchElementException没有元素异常
        System.out.println(s);*/
    }
}

迭代器的实现原理

加强for循环

package com.itheima.demo02.Iterator;

import java.util.ArrayList;

/*
    加强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写
    是JDK1.5以后出现的新特性
    Collection<E>extends Iterable<E>:全部的单列集合均可以使用加强for
    public interface Iterable<T>实现这个接口容许对象成为 "foreach" 语句的目标。

    加强for循环:用来遍历集合和数组

    格式:
        for(集合/数组的数据类型 变量名: 集合名/数组名){
            sout(变量名);
        }
 */
public class Demo02Foreach {
    public static void main(String[] args) {
        demo02();
    }

    //使用加强for循环遍历集合
    private static void demo02() {
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        for(String s : list){
            System.out.println(s);
        }
    }

    //使用加强for循环遍历数组
    private static void demo01() {
        int[] arr = {1,2,3,4,5};
        for(int i:arr){
            System.out.println(i);
        }
    }
}

通配符高级使用----受限泛型
以前设置泛型的时候,其实是能够任意设置的,只要是类就能够设置。可是在JAVA的泛型中能够指定一个泛型的上限和下限。
泛型的上限:
格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
泛型的下限:
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型
好比:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
~~~java public static void main(String[] args) {
Collection list1 = new ArrayList();
Collection list2 = new ArrayList(); 
Collection list3 = new ArrayList(); 
Collection list4 = new ArrayList();
getElement(list1);
getElement(list2);//报错
getElement(list3);
getElement(list4);//报错

getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
} 
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类 public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类 public static void getElement2(Collection<? super Number> coll){} ~~~

数据结构

List接口

package com.itheima.demo01.List;

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

/*
    java.util.List接口 extends Collection接口
    List接口的特色:
        1.有序的集合,存储元素和取出元素的顺序是一致的(存储123 取出123)
        2.有索引,包含了一些带索引的方法
        3.容许存储重复的元素

    List接口中带索引的方法(特有)
        - public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
        - public E get(int index):返回集合中指定位置的元素。
        - public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
        - public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
    注意:
        操做索引的时候,必定要防止索引越界异常
        IndexOutOfBoundsException:索引越界异常,集合会报
        ArrayIndexOutOfBoundsException:数组索引越界异常
        StringIndexOutOfBoundsException:字符串索引越界异常
 */
public class Demo01List {
    public static void main(String[] args) {
        //建立一个List集合对象,多态
        List<String> list = new ArrayList<>();
        //使用add方法往集合中添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("a");
        //打印集合
        System.out.println(list);//[a, b, c, d, a]  不是地址重写了toString

        //public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
        //在c和d之间添加一个itheima
        list.add(3,"itheima");//[a, b, c, itheima, d, a]
        System.out.println(list);

        //public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
        //移除元素
        String removeE = list.remove(2);
        System.out.println("被移除的元素:"+removeE);//被移除的元素:c
        System.out.println(list);//[a, b, itheima, d, a]

        //public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
        //把最后一个a,替换为A
        String setE = list.set(4, "A");
        System.out.println("被替换的元素:"+setE);//被替换的元素:a
        System.out.println(list);//[a, b, itheima, d, A]

        //List集合遍历有3种方式
        //使用普通的for循环
        for(int i=0; i<list.size(); i++){
            //public E get(int index):返回集合中指定位置的元素。
            String s = list.get(i);
            System.out.println(s);
        }
        System.out.println("-----------------");
        //使用迭代器
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-----------------");
        //使用加强for
        for (String s : list) {
            System.out.println(s);
        }

        String r = list.get(5);//IndexOutOfBoundsException: Index 5 out-of-bounds for length 5
        System.out.println(r);

    }
}

LinkedList

package com.itheima.demo01.List;

import java.util.LinkedList;

/*
    java.util.LinkedList集合 implements List接口
    LinkedList集合的特色:
        1.底层是一个链表结构:查询慢,增删快
        2.里边包含了大量操做首尾元素的方法
        注意:使用LinkedList集合特有的方法,不能使用多态

        - public void addFirst(E e):将指定元素插入此列表的开头。
        - public void addLast(E e):将指定元素添加到此列表的结尾。
        - public void push(E e):将元素推入此列表所表示的堆栈。

        - public E getFirst():返回此列表的第一个元素。
        - public E getLast():返回此列表的最后一个元素。

        - public E removeFirst():移除并返回此列表的第一个元素。
        - public E removeLast():移除并返回此列表的最后一个元素。
        - public E pop():今后列表所表示的堆栈处弹出一个元素。

        - public boolean isEmpty():若是列表不包含元素,则返回true。

 */
public class Demo02LinkedList {
    public static void main(String[] args) {
        show03();
    }

    /*
        - public E removeFirst():移除并返回此列表的第一个元素。
        - public E removeLast():移除并返回此列表的最后一个元素。
        - public E pop():今后列表所表示的堆栈处弹出一个元素。此方法至关于 removeFirst
     */
    private static void show03() {
        //建立LinkedList集合对象
        LinkedList<String> linked = new LinkedList<>();
        //使用add方法往集合中添加元素
        linked.add("a");
        linked.add("b");
        linked.add("c");
        System.out.println(linked);//[a, b, c]

        //String first = linked.removeFirst();
        String first = linked.pop();
        System.out.println("被移除的第一个元素:"+first);
        String last = linked.removeLast();
        System.out.println("被移除的最后一个元素:"+last);
        System.out.println(linked);//[b]
    }

    /*
        - public E getFirst():返回此列表的第一个元素。
        - public E getLast():返回此列表的最后一个元素。
     */
    private static void show02() {
        //建立LinkedList集合对象
        LinkedList<String> linked = new LinkedList<>();
        //使用add方法往集合中添加元素
        linked.add("a");
        linked.add("b");
        linked.add("c");

        //linked.clear();//清空集合中的元素 在获取集合中的元素会抛出NoSuchElementException

        //public boolean isEmpty():若是列表不包含元素,则返回true。
        if(!linked.isEmpty()){
            String first = linked.getFirst();
            System.out.println(first);//a
            String last = linked.getLast();
            System.out.println(last);//c
        }
    }

    /*
        - public void addFirst(E e):将指定元素插入此列表的开头。
        - public void addLast(E e):将指定元素添加到此列表的结尾。
        - public void push(E e):将元素推入此列表所表示的堆栈。此方法等效于 addFirst(E)。
     */
    private static void show01() {
        //建立LinkedList集合对象
        LinkedList<String> linked = new LinkedList<>();
        //使用add方法往集合中添加元素
        linked.add("a");
        linked.add("b");
        linked.add("c");
        System.out.println(linked);//[a, b, c]

        //public void addFirst(E e):将指定元素插入此列表的开头。
        //linked.addFirst("www");
        linked.push("www");
        System.out.println(linked);//[www, a, b, c]

        //public void addLast(E e):将指定元素添加到此列表的结尾。此方法等效于 add()
        linked.addLast("com");
        System.out.println(linked);//[www, a, b, c, com]
    }
}

Set接口

package com.itheima.demo02.Set;

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

/*
    java.util.Set接口 extends Collection接口
    Set接口的特色:
        1.不容许存储重复的元素
        2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
    java.util.HashSet集合 implements Set接口
    HashSet特色:
         1.不容许存储重复的元素
         2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
         3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致
         4.底层是一个哈希表结构(查询的速度很是的快)
 */
public class Demo01Set {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        //使用add方法往集合中添加元素
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(1);
        //使用迭代器遍历set集合
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()){
            Integer n = it.next();
            System.out.println(n);//1,2,3
        }
        //使用加强for遍历set集合
        System.out.println("-----------------");
        for (Integer i : set) {
            System.out.println(i);
        }
    }
}

HashSet集合存储数据的结构(哈希表)

Set集合存储元素不重复的原理

LinkedHashSet

package com.itheima.demo02.Set;

import java.util.HashSet;
import java.util.LinkedHashSet;

/*
    java.util.LinkedHashSet集合 extends HashSet集合
    LinkedHashSet集合特色:
        底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序
 */
public class Demo04LinkedHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("www");
        set.add("abc");
        set.add("abc");
        set.add("itcast");
        System.out.println(set);//[abc, www, itcast] 无序,不容许重复

        LinkedHashSet<String> linked = new LinkedHashSet<>();
        linked.add("www");
        linked.add("abc");
        linked.add("abc");
        linked.add("itcast");
        System.out.println(linked);//[www, abc, itcast] 有序,不容许重复
    }
}

TreeSet

可变参数

package com.itheima.demo04.VarArgs;
/*
    可变参数:是JDK1.5以后出现的新特性
    使用前提:
        当方法的参数列表数据类型已经肯定,可是参数的个数不肯定,就可使用可变参数.
    使用格式:定义方法时使用
        修饰符 返回值类型 方法名(数据类型...变量名){}
    可变参数的原理:
        可变参数底层就是一个数组,根据传递参数个数不一样,会建立不一样长度的数组,来存储这些参数
        传递的参数个数,能够是0个(不传递),1,2...多个

 */
public class Demo01VarArgs {
    public static void main(String[] args) {
        //int i = add();
        //int i = add(10);
        int i = add(10,20);
        //int i = add(10,20,30,40,50,60,70,80,90,100);
        System.out.println(i);

        method("abc",5.5,10,1,2,3,4);
    }

    /*
        可变参数的注意事项
            1.一个方法的参数列表,只能有一个可变参数
            2.若是方法的参数有多个,那么可变参数必须写在参数列表的末尾
     */
    /*public static void method(int...a,String...b){

    }*/

    /*public static void method(String b,double c,int d,int...a){
    }*/

    //可变参数的特殊(终极)写法
    public static void method(Object...obj){

    }

    /*
        定义计算(0-n)整数和的方法
        已知:计算整数的和,数据类型已经肯定int
        可是参数的个数不肯定,不知道要计算几个整数的和,就可使用可变参数
        add(); 就会建立一个长度为0的数组, new int[0]
        add(10); 就会建立一个长度为1的数组,存储传递来过的参数 new int[]{10};
        add(10,20); 就会建立一个长度为2的数组,存储传递来过的参数 new int[]{10,20};
        add(10,20,30,40,50,60,70,80,90,100); 就会建立一个长度为2的数组,存储传递来过的参数 new int[]{10,20,30,40,50,60,70,80,90,100};
     */
    public static int add(int...arr){
        //System.out.println(arr);//[I@2ac1fdc4 底层是一个数组
        //System.out.println(arr.length);//0,1,2,10
        //定义一个初始化的变量,记录累加求和
        int sum = 0;
        //遍历数组,获取数组中的每个元素
        for (int i : arr) {
            //累加求和
            sum += i;
        }
        //把求和结果返回
        return sum;
    }

    //定义一个方法,计算三个int类型整数的和
    /*public static int add(int a,int b,int c){
        return a+b+c;
    }*/

    //定义一个方法,计算两个int类型整数的和
    /*public static int add(int a,int b){
        return a+b;
    }*/
}

Collections工具类

package com.itheima.demo05.Collections;

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

/*
    - java.utils.Collections是集合工具类,用来对集合进行操做。部分方法以下:
        - public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素。
        - public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。
 */
public class Demo01Collections {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        //往集合中添加多个元素
        /*list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");*/

        //public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素。
        Collections.addAll(list,"a","b","c","d","e");

        System.out.println(list);//[a, b, c, d, e]

        //public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。
        Collections.shuffle(list);
        System.out.println(list);//[b, d, c, a, e], [b, d, c, a, e]
    }
}

package com.itheima.demo05.Collections;

public class Person implements Comparable<Person>{
    private String name;
    private int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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 int compareTo(Person o) {
        //return 0;//认为元素都是相同的
        //自定义比较的规则,比较两我的的年龄(this,参数Person)
        //return this.getAge() - o.getAge();//年龄升序排序
        return o.getAge() - this.getAge();//年龄升序排序
    }
}


package com.itheima.demo05.Collections;

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

/*
    - java.utils.Collections是集合工具类,用来对集合进行操做。部分方法以下:
        public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。

    注意:
         sort(List<T> list)使用前提
         被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则

    Comparable接口的排序规则:
        本身(this)-参数:升序
 */
public class Demo02Sort {
    public static void main(String[] args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01);//[1, 3, 2]

        //public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
        Collections.sort(list01);//默认是升序

        System.out.println(list01);//[1, 2, 3]

        ArrayList<String> list02 = new ArrayList<>();
        list02.add("a");
        list02.add("c");
        list02.add("b");
        System.out.println(list02);//[a, c, b]

        Collections.sort(list02);
        System.out.println(list02);//[a, b, c]

        ArrayList<Person> list03 = new ArrayList<>();
        list03.add(new Person("张三",18));
        list03.add(new Person("李四",20));
        list03.add(new Person("王五",15));
        System.out.println(list03);//[Person{name='张三', age=18}, Person{name='李四', age=20}, Person{name='王五', age=15}]

        Collections.sort(list03);
        System.out.println(list03);
    }
}

package com.itheima.demo05.Collections;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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;
    }
}


    package com.itheima.demo05.Collections;

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

/*
    - java.utils.Collections是集合工具类,用来对集合进行操做。部分方法以下:
        public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。

     Comparator和Comparable的区别
        Comparable:本身(this)和别人(参数)比较,本身须要实现Comparable接口,重写比较的规则compareTo方法
        Comparator:至关于找一个第三方的裁判,比较两个

    Comparator的排序规则:
        o1-o2:升序
 */
public class Demo03Sort {
    public static void main(String[] args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01);//[1, 3, 2]

        Collections.sort(list01, new Comparator<Integer>() {
            //重写比较的规则
            @Override
            public int compare(Integer o1, Integer o2) {
                //return o1-o2;//升序
                return o2-o1;//降序
            }
        });

        System.out.println(list01);

        ArrayList<Student> list02 = new ArrayList<>();
        list02.add(new Student("a迪丽热巴",18));
        list02.add(new Student("古力娜扎",20));
        list02.add(new Student("杨幂",17));
        list02.add(new Student("b杨幂",18));
        System.out.println(list02);

        /*Collections.sort(list02, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //按照年龄升序排序
                return o1.getAge()-o2.getAge();
            }
        });*/

        //扩展:了解
        Collections.sort(list02, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //按照年龄升序排序
                int result =  o1.getAge()-o2.getAge();
                //若是两我的年龄相同,再使用姓名的第一个字比较
                if(result==0){
                    result =  o1.getName().charAt(0)-o2.getName().charAt(0);
                }
                return  result;
            }

        });

        System.out.println(list02);
    }
}

HashMap

package com.itheima.demo01.Map;

import java.util.HashMap;
import java.util.Map;

/*
    java.util.Map<k,v>集合
    Map集合的特色:
        1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
        2.Map集合中的元素,key和value的数据类型能够相同,也能够不一样
        3.Map集合中的元素,key是不容许重复的,value是能够重复的
        4.Map集合中的元素,key和value是一一对应
    java.util.HashMap<k,v>集合 implements Map<k,v>接口
    HashMap集合的特色:
        1.HashMap集合底层是哈希表:查询的速度特别的快
            JDK1.8以前:数组+单向链表
            JDK1.8以后:数组+单向链表|红黑树(链表的长度超过8):提升查询的速度
        2.hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
   java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
   LinkedHashMap的特色:
        1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
        2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
 */
public class Demo01Map {
    public static void main(String[] args) {
        show04();
    }

    /*
        boolean containsKey(Object key) 判断集合中是否包含指定的键。
        包含返回true,不包含返回false
     */
    private static void show04() {
        //建立Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);

        boolean b1 = map.containsKey("赵丽颖");
        System.out.println("b1:"+b1);//b1:true

        boolean b2 = map.containsKey("赵颖");
        System.out.println("b2:"+b2);//b2:false
    }

    /*
        public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
            返回值:
                key存在,返回对应的value值
                key不存在,返回null
     */
    private static void show03() {
        //建立Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);

        Integer v1 = map.get("杨颖");
        System.out.println("v1:"+v1);//v1:165

        Integer v2 = map.get("迪丽热巴");
        System.out.println("v2:"+v2);//v2:null
    }

    /*
        public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
            返回值:V
                key存在,v返回被删除的值
                key不存在,v返回null
     */
    private static void show02() {
        //建立Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);
        System.out.println(map);//{林志玲=178, 赵丽颖=168, 杨颖=165}

        Integer v1 = map.remove("林志玲");
        System.out.println("v1:"+v1);//v1:178

        System.out.println(map);//{赵丽颖=168, 杨颖=165}

        //int v2 = map.remove("林志颖");//自动拆箱  NullPointerException
        Integer v2 = map.remove("林志颖");
        System.out.println("v2:"+v2);//v2:null

        System.out.println(map);//{赵丽颖=168, 杨颖=165}
    }

    /*
        public V put(K key, V value):  把指定的键与指定的值添加到Map集合中。
            返回值:v
                存储键值对的时候,key不重复,返回值V是null
                存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值
     */
    private static void show01() {
        //建立Map集合对象,多态
        Map<String,String> map = new HashMap<>();

        String v1 = map.put("李晨", "范冰冰1");
        System.out.println("v1:"+v1);//v1:null

        String v2 = map.put("李晨", "范冰冰2");
        System.out.println("v2:"+v2);//v2:范冰冰1

        System.out.println(map);//{李晨=范冰冰2}

        map.put("冷锋","龙小云");
        map.put("杨过","小龙女");
        map.put("尹志平","小龙女");
        System.out.println(map);//{杨过=小龙女, 尹志平=小龙女, 李晨=范冰冰2, 冷锋=龙小云}
    }
}

package com.itheima.demo01.Map;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /*
        Map集合的第一种遍历方式:经过键找值的方式
        Map集合中的方法:
             Set<K> keySet() 返回此映射中包含的键的 Set 视图。
        实现步骤:
            1.使用Map集合中的方法keySet(),把Map集合全部的key取出来,存储到一个Set集合中
            2.遍历set集合,获取Map集合中的每个key
            3.经过Map集合中的方法get(key),经过key找到value
     */
    public class Demo02KeySet {
        public static void main(String[] args) {
            //建立Map集合对象
            Map<String,Integer> map = new HashMap<>();
            map.put("赵丽颖",168);
            map.put("杨颖",165);
            map.put("林志玲",178);
    
            //1.使用Map集合中的方法keySet(),把Map集合全部的key取出来,存储到一个Set集合中
            Set<String> set = map.keySet();
    
            //2.遍历set集合,获取Map集合中的每个key
            //使用迭代器遍历Set集合
            Iterator<String> it = set.iterator();
            while (it.hasNext()){
                String key = it.next();
                //3.经过Map集合中的方法get(key),经过key找到value
                Integer value = map.get(key);
                System.out.println(key+"="+value);
            }
            System.out.println("-------------------");
            //使用加强for遍历Set集合
            for(String key : set){
                //3.经过Map集合中的方法get(key),经过key找到value
                Integer value = map.get(key);
                System.out.println(key+"="+value);
            }
            System.out.println("-------------------");
            //使用加强for遍历Set集合
            for(String key : map.keySet()){
                //3.经过Map集合中的方法get(key),经过key找到value
                Integer value = map.get(key);
                System.out.println(key+"="+value);
            }
        }
    }

package com.itheima.demo01.Map;

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

/*
    Map集合遍历的第二种方式:使用Entry对象遍历

    Map集合中的方法:
        Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。

    实现步骤:
        1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
        2.遍历Set集合,获取每个Entry对象
        3.使用Entry对象中的方法getKey()和getValue()获取键与值
 */
public class Demo03EntrySet {
    public static void main(String[] args) {
        //建立Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);

        //1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
        Set<Map.Entry<String, Integer>> set = map.entrySet();

        //2.遍历Set集合,获取每个Entry对象
        //使用迭代器遍历Set集合
        Iterator<Map.Entry<String, Integer>> it = set.iterator();
        while(it.hasNext()){
            Map.Entry<String, Integer> entry = it.next();
            //3.使用Entry对象中的方法getKey()和getValue()获取键与值
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key+"="+value);
        }
        System.out.println("-----------------------");
        for(Map.Entry<String,Integer> entry:set){
            //3.使用Entry对象中的方法getKey()和getValue()获取键与值
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key+"="+value);
        }
    }
}

package com.itheima.demo03.Map;

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

/*
    java.util.LinkedHashMap<K,V> entends HashMap<K,V>
    Map 接口的哈希表和连接列表实现,具备可预知的迭代顺序。
    底层原理:
        哈希表+链表(记录元素的顺序)
 */
public class Demo01LinkedHashMap {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<>();
        map.put("a","a");
        map.put("c","c");
        map.put("b","b");
        map.put("a","d");
        System.out.println(map);// key不容许重复,无序 {a=d, b=b, c=c}

        LinkedHashMap<String,String> linked = new LinkedHashMap<>();
        linked.put("a","a");
        linked.put("c","c");
        linked.put("b","b");
        linked.put("a","d");
        System.out.println(linked);// key不容许重复,有序 {a=d, c=c, b=b}
    }
}

package com.itheima.demo03.Map;

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

/*
    java.util.Hashtable<K,V>集合 implements Map<K,V>接口

    Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
    HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快

    HashMap集合(以前学的全部的集合):能够存储null值,null键
    Hashtable集合,不能存储null值,null键

    Hashtable和Vector集合同样,在jdk1.2版本以后被更先进的集合(HashMap,ArrayList)取代了
    Hashtable的子类Properties依然活跃在历史舞台
    Properties集合是一个惟一和IO流相结合的集合
 */
public class Demo02Hashtable {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<>();
        map.put(null,"a");
        map.put("b",null);
        map.put(null,null);
        System.out.println(map);//{null=null, b=null}

        Hashtable<String,String> table = new Hashtable<>();
        //table.put(null,"a");//NullPointerException
        //table.put("b",null);//NullPointerException
        table.put(null,null);//NullPointerException
    }
}

package com.itheima.demo03.Map;

import java.util.HashMap;
import java.util.Scanner;

/*
    练习:
        计算一个字符串中每一个字符出现次数

    分析:
        1.使用Scanner获取用户输入的字符串
        2.建立Map集合,key是字符串中的字符,value是字符的个数
        3.遍历字符串,获取每个字符
        4.使用获取到的字符,去Map集合判断key是否存在
            key存在:
                经过字符(key),获取value(字符个数)
                value++
                put(key,value)把新的value存储到Map集合中
            key不存在:
                put(key,1)
        5.遍历Map集合,输出结果
 */
public class Demo03MapTest {
    public static void main(String[] args) {
        //1.使用Scanner获取用户输入的字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = sc.next();
        //2.建立Map集合,key是字符串中的字符,value是字符的个数
        HashMap<Character,Integer> map = new HashMap<>();
        //3.遍历字符串,获取每个字符
        for(char c :str.toCharArray()){
            //4.使用获取到的字符,去Map集合判断key是否存在
            if(map.containsKey(c)){
                //key存在
                Integer value = map.get(c);
                value++;
                map.put(c,value);
            }else{
                //key不存在
                map.put(c,1);
            }
        }
        //5.遍历Map集合,输出结果
        for(Character key :map.keySet()){
            Integer value = map.get(key);
            System.out.println(key+"="+value);
        }
    }
}

异常

package com.itheima.demo01.Exception;

/*
    java.lang.Throwable:类是 Java 语言中全部错误或异常的超类。
        Exception:编译期异常,进行编译(写代码)java程序出现的问题
            RuntimeException:运行期异常,java程序运行过程当中出现的问题
            异常就至关于程序得了一个小毛病(感冒,发烧),把异常处理掉,程序能够继续执行(吃点药,继续革命工做)
        Error:错误
            错误就至关于程序得了一个没法治愈的毛病(非典,艾滋).必须修改源代码,程序才能继续执行
 */
public class Demo01Exception {
    public static void main(String[] args) /*throws ParseException*/ {
        //Exception:编译期异常,进行编译(写代码)java程序出现的问题
        /*SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");//用来格式化日期
        Date date = null;
        try {
            date = sdf.parse("1999-0909");//把字符串格式的日期,解析为Date格式的日期
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println(date);*/

        //RuntimeException:运行期异常,java程序运行过程当中出现的问题
        /*int[] arr = {1,2,3};
        //System.out.println(arr[0]);
        try {
            //可能会出现异常的代码
            System.out.println(arr[3]);
        }catch(Exception e){
            //异常的处理逻辑
            System.out.println(e);
        }*/

        /*
            Error:错误
            OutOfMemoryError: Java heap space
            内存溢出的错误,建立的数组太大了,超出了给JVM分配的内存
         */
        //int[] arr = new int[1024*1024*1024];
        //必须修改代码,建立的数组小一点
        int[] arr = new int[1024*1024];
        System.out.println("后续代码");
    }
}

异常的产生过程解析

throw

package com.itheima.demo01.Exception;
/*
    throw关键字
    做用:
        可使用throw关键字在指定的方法中抛出指定的异常
    使用格式:
        throw new xxxException("异常产生的缘由");
    注意:
        1.throw关键字必须写在方法的内部
        2.throw关键字后边new的对象必须是Exception或者Exception的子类对象
        3.throw关键字抛出指定的异常对象,咱们就必须处理这个异常对象
            throw关键字后边建立的是RuntimeException或者是 RuntimeException的子类对象,咱们能够不处理,默认交给JVM处理(打印异常对象,中断程序)
            throw关键字后边建立的是编译异常(写代码的时候报错),咱们就必须处理这个异常,要么throws,要么try...catch
 */
public class Demo03Throw {
    public static void main(String[] args) {
        //int[] arr = null;
        int[] arr = new int[3];
        int e = getElement(arr,3);
        System.out.println(e);
    }
    /*
        定义一个方法,获取数组指定索引处的元素
        参数:
            int[] arr
            int index
        之后(工做中)咱们首先必须对方法传递过来的参数进行合法性校验
        若是参数不合法,那么咱们就必须使用抛出异常的方式,告知方法的调用者,传递的参数有问题
        注意:
            NullPointerException是一个运行期异常,咱们不用处理,默认交给JVM处理
            ArrayIndexOutOfBoundsException是一个运行期异常,咱们不用处理,默认交给JVM处理
     */
    public static int getElement(int[] arr,int index){
        /*
            咱们能够对传递过来的参数数组,进行合法性校验
            若是数组arr的值是null
            那么咱们就抛出空指针异常,告知方法的调用者"传递的数组的值是null"
         */
        if(arr == null){
            throw new NullPointerException("传递的数组的值是null");
        }

        /*
            咱们能够对传递过来的参数index进行合法性校验
            若是index的范围不在数组的索引范围内
            那么咱们就抛出数组索引越界异常,告知方法的调用者"传递的索引超出了数组的使用范围"
         */
        if(index<0 || index>arr.length-1){
            throw new ArrayIndexOutOfBoundsException("传递的索引超出了数组的使用范围");
        }

        int ele = arr[index];
        return ele;
    }
}

package com.itheima.demo01.Exception;

import java.util.Objects;

/*
    Obects类中的静态方法
    public static <T> T requireNonNull(T obj):查看指定引用对象不是null。
    源码:
        public static <T> T requireNonNull(T obj) {
            if (obj == null)
                throw new NullPointerException();
            return obj;
        }
 */
public class Demo04Objects {
    public static void main(String[] args) {
        method(null);
    }

    public static void method(Object obj){
        //对传递过来的参数进行合法性判断,判断是否为null
        /*if(obj == null){
            throw new NullPointerException("传递的对象的值是null");
        }*/

        //Objects.requireNonNull(obj);
        Objects.requireNonNull(obj,"传递的对象的值是null");
    }
}

throws

package com.itheima.demo01.Exception;

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

/*
    throws关键字:异常处理的第一种方式,交给别人处理
    做用:
        当方法内部抛出异常对象的时候,那么咱们就必须处理这个异常对象
        可使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(本身不处理,给别人处理),最终交给JVM处理-->中断处理
    使用格式:在方法声明时使用
        修饰符 返回值类型 方法名(参数列表) throws AAAExcepiton,BBBExcepiton...{
            throw new AAAExcepiton("产生缘由");
            throw new BBBExcepiton("产生缘由");
            ...
        }
     注意:
        1.throws关键字必须写在方法声明处
        2.throws关键字后边声明的异常必须是Exception或者是Exception的子类
        3.方法内部若是抛出了多个异常对象,那么throws后边必须也声明多个异常
            若是抛出的多个异常对象有子父类关系,那么直接声明父类异常便可
        4.调用了一个声明抛出异常的方法,咱们就必须的处理声明的异常
            要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM
            要么try...catch本身处理异常
 */
public class Demo05Throws {
    /*
        FileNotFoundException extends IOException extends Excepiton
        若是抛出的多个异常对象有子父类关系,那么直接声明父类异常便可
     */
    //public static void main(String[] args) throws FileNotFoundException,IOException {
    //public static void main(String[] args) throws IOException {
    public static void main(String[] args) throws Exception {
        readFile("c:\\a.tx");

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

    /*
        定义一个方法,对传递的文件路径进行合法性判断
        若是路径不是"c:\\a.txt",那么咱们就抛出文件找不到异常对象,告知方法的调用者
        注意:
            FileNotFoundException是编译异常,抛出了编译异常,就必须处理这个异常
            可使用throws继续声明抛出FileNotFoundException这个异常对象,让方法的调用者处理
     */
    public static void readFile(String fileName) throws FileNotFoundException,IOException{
        if(!fileName.equals("c:\\a.txt")){
            throw new FileNotFoundException("传递的文件路径不是c:\\a.txt");
        }

        /*
            若是传递的路径,不是.txt结尾
            那么咱们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对

         */
        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");
        }

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

try...catch...

package com.itheima.demo02.Exception;

import java.io.IOException;

/*
    try...catch:异常处理的第二种方式,本身处理异常
    格式:
        try{
            可能产生异常的代码
        }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
            异常的处理逻辑,异常异常对象以后,怎么处理异常对象
            通常在工做中,会把异常的信息记录到一个日志中
        }
        ...
        catch(异常类名 变量名){

        }
    注意:
        1.try中可能会抛出多个异常对象,那么就可使用多个catch来处理这些异常对象
        2.若是try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try...catch以后的代码
          若是try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try...catch以后的代码
 */
public class Demo01TryCatch {
    public static void main(String[] args) {
        try{
            //可能产生异常的代码
            readFile("d:\\a.tx");
            System.out.println("资源释放");
        }catch (IOException e){//try中抛出什么异常对象,catch就定义什么异常变量,用来接收这个异常对象
            //异常的处理逻辑,异常异常对象以后,怎么处理异常对象
            //System.out.println("catch - 传递的文件后缀不是.txt");

            /*
                Throwable类中定义了3个异常处理的方法
                 String getMessage() 返回此 throwable 的简短描述。
                 String toString() 返回此 throwable 的详细消息字符串。
                 void printStackTrace()  JVM打印异常对象,默认此方法,打印的异常信息是最全面的
             */
            //System.out.println(e.getMessage());//文件的后缀名不对
            //System.out.println(e.toString());//重写Object类的toString java.io.IOException: 文件的后缀名不对
            //System.out.println(e);//java.io.IOException: 文件的后缀名不对

            /*
                java.io.IOException: 文件的后缀名不对
                    at com.itheima.demo02.Exception.Demo01TryCatch.readFile(Demo01TryCatch.java:55)
                    at com.itheima.demo02.Exception.Demo01TryCatch.main(Demo01TryCatch.java:27)
             */
            e.printStackTrace();
        }
        System.out.println("后续代码");
    }

    /*
       若是传递的路径,不是.txt结尾
       那么咱们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对

    */
    public static void readFile(String fileName) throws IOException {

        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");
        }

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

package com.kiramie.demo07;

import java.io.IOException;

/*
    finally代码块
     格式:
        try{
            可能产生异常的代码
        }catch(定义一个异常的变量,用来接收try中抛出的异常对象){
            异常的处理逻辑,异常异常对象以后,怎么处理异常对象
            通常在工做中,会把异常的信息记录到一个日志中
        }
        ...
        catch(异常类名 变量名){

        }finally{
            不管是否出现异常都会执行
        }
     注意:
        1.finally不能单独使用,必须和try一块儿使用
        2.finally通常用于资源释放(资源回收),不管程序是否出现异常,最后都要资源释放(IO)
 */
public class Demo02TryCatchFinally {
    public static void main(String[] args) {
        try {
            //可能会产生异常的代码
            readFile("c:\\a.tx");
        } catch (IOException e) {
            //异常的处理逻辑
            e.printStackTrace();
        } finally {
            //不管是否出现异常,都会执行
            System.out.println("资源释放");
        }
        System.out.println("--------");//比finally先执行
    }

    /*
       若是传递的路径,不是.txt结尾
       那么咱们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对

    */
    public static void readFile(String fileName) throws IOException {

        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");
        }

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

package com.itheima.demo03.Exception;

import java.util.List;

/*
    异常的注意事项
 */
public class Demo01Exception {
    public static void main(String[] args) {
        /*
            多个异常使用捕获又该如何处理呢?
            1. 多个异常分别处理。
            2. 多个异常一次捕获,屡次处理。
            3. 多个异常一次捕获一次处理。
         */

        //1. 多个异常分别处理。
       /* try {
            int[] arr = {1,2,3};
            System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println(e);
        }

        try{
            List<Integer> list = List.of(1, 2, 3);
            System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
        }catch (IndexOutOfBoundsException e){
            System.out.println(e);
        }*/

        //2. 多个异常一次捕获,屡次处理。
        /*try {
            int[] arr = {1,2,3};
            //System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
            List<Integer> list = List.of(1, 2, 3);
            System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println(e);
        }catch (IndexOutOfBoundsException e){
            System.out.println(e);
        }*/

        /*
            一个try多个catch注意事项:
                catch里边定义的异常变量,若是有子父类关系,那么子类的异常变量必须写在上边,不然就会报错
                ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException
         */
        /*try {
            int[] arr = {1,2,3};
            //System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
            List<Integer> list = List.of(1, 2, 3);
            System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
        }catch (IndexOutOfBoundsException e){
            System.out.println(e);
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println(e);
        }*/

        //3. 多个异常一次捕获一次处理。
        /*try {
            int[] arr = {1,2,3};
            //System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
            List<Integer> list = List.of(1, 2, 3);
            System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
        }catch (Exception e){
            System.out.println(e);
        }*/

        //运行时异常被抛出能够不处理。即不捕获也不声明抛出。
        //默认给虚拟机处理,终止程序,何时不抛出运行时异常了,在来继续执行程序
        int[] arr = {1,2,3};
        System.out.println(arr[3]);//ArrayIndexOutOfBoundsException: 3
        List<Integer> list = List.of(1, 2, 3);
        System.out.println(list.get(3));//IndexOutOfBoundsException: Index 3 out-of-bounds for length 3

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

package com.itheima.demo03.Exception;
/*
    若是finally有return语句,永远返回finally中的结果,避免该状况,尽可能不要在finally中写return语句.
 */
public class Demo02Exception {
    public static void main(String[] args) {
        int a = getA();
        System.out.println(a);
    }

    //定义一个方法,返回变量a的值
    public static int getA(){
        int a = 10;
        try{
            return a;
        }catch (Exception e){
            System.out.println(e);
        }finally {
            //必定会执行的代码
            a = 100;
            return a;
        }

    }
}

package com.itheima.demo03.Exception;
/*
    子父类的异常:
        - 若是父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
        - 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
    注意:
        父类异常时什么样,子类异常就什么样
 */
public class Fu {
    public void show01() throws NullPointerException,ClassCastException{}
    public void show02() throws IndexOutOfBoundsException{}
    public void show03() throws IndexOutOfBoundsException{}
    public void show04() throws Exception {}
}

class Zi extends Fu{
    //子类重写父类方法时,抛出和父类相同的异常
    public void show01() throws NullPointerException,ClassCastException{}
    //子类重写父类方法时,抛出父类异常的子类
    public void show02() throws ArrayIndexOutOfBoundsException{}
    //子类重写父类方法时,不抛出异常
    public void show03() {}

    /*
        父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。

     */
    //public void show04() throws Exception{}

    //此时子类产生该异常,只能捕获处理,不能声明抛出
    public void show04()  {
        try {
            throw  new Exception("编译期异常");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

自定义异常

package com.itheima.demo04.MyException;
/*
    自定义异常类:
        java提供的异常类,不够咱们使用,须要本身定义一些异常类
    格式:
        public class XXXExcepiton extends Exception | RuntimeException{
            添加一个空参数的构造方法
            添加一个带异常信息的构造方法
        }
     注意:
        1.自定义异常类通常都是以Exception结尾,说明该类是一个异常类
        2.自定义异常类,必须的继承Exception或者RuntimeException
            继承Exception:那么自定义的异常类就是一个编译期异常,若是方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try...catch
            继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
 */
public class RegisterException extends /*Exception*/ RuntimeException{
    //添加一个空参数的构造方法
    public RegisterException(){
        super();
    }

    /*
        添加一个带异常信息的构造方法
        查看源码发现,全部的异常类都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息
     */
    public RegisterException(String message){
        super(message);
    }
}
/*-------------------------------------------------------------------------------------------------------*/
    package com.itheima.demo04.MyException;

import java.util.Scanner;

/*
    要求:咱们模拟注册操做,若是用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。

    分析:
        1.使用数组保存已经注册过的用户名(数据库)
        2.使用Scanner获取用户输入的注册的用户名(前端,页面)
        3.定义一个方法,对用户输入的中注册的用户名进行判断
            遍历存储已经注册过用户名的数组,获取每个用户名
            使用获取到的用户名和用户输入的用户名比较
                true:
                    用户名已经存在,抛出RegisterException异常,告知用户"亲,该用户名已经被注册";
                false:
                    继续遍历比较
            若是循环结束了,尚未找到重复的用户名,提示用户"恭喜您,注册成功!";
 */
public class Demo01RegisterException {
    // 1.使用数组保存已经注册过的用户名(数据库)
    static String[] usernames = {"张三","李四","王五"};

    public static void main(String[] args) /*throws RegisterException*/ {
        //2.使用Scanner获取用户输入的注册的用户名(前端,页面)
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入您要注册的用户名:");
        String username = sc.next();
        checkUsername(username);

    }

    //3.定义一个方法,对用户输入的中注册的用户名进行判断
    public static void checkUsername(String username) /*throws RegisterException*/ {
        //遍历存储已经注册过用户名的数组,获取每个用户名
        for (String name : usernames) {
            //使用获取到的用户名和用户输入的用户名比较
            if(name.equals(username)){
                //true:用户名已经存在,抛出RegisterException异常,告知用户"亲,该用户名已经被注册";
                try {
                    throw new RegisterException("亲,该用户名已经被注册");
                } catch (RegisterException e) {
                    e.printStackTrace();
                    return; //结束方法
                }
            }
        }

        //若是循环结束了,尚未找到重复的用户名,提示用户"恭喜您,注册成功!";
        System.out.println("恭喜您,注册成功!");
    }
}
/*-------------------------------------------------------------------------------------------------------*/
    package com.itheima.demo04.MyException;

import java.util.Scanner;

/*
    要求:咱们模拟注册操做,若是用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。

    分析:
        1.使用数组保存已经注册过的用户名(数据库)
        2.使用Scanner获取用户输入的注册的用户名(前端,页面)
        3.定义一个方法,对用户输入的中注册的用户名进行判断
            遍历存储已经注册过用户名的数组,获取每个用户名
            使用获取到的用户名和用户输入的用户名比较
                true:
                    用户名已经存在,抛出RegisterException异常,告知用户"亲,该用户名已经被注册";
                false:
                    继续遍历比较
            若是循环结束了,尚未找到重复的用户名,提示用户"恭喜您,注册成功!";
 */
public class Demo02RegisterException {
    // 1.使用数组保存已经注册过的用户名(数据库)
    static String[] usernames = {"张三","李四","王五"};

    public static void main(String[] args) {
        //2.使用Scanner获取用户输入的注册的用户名(前端,页面)
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入您要注册的用户名:");
        String username = sc.next();
        checkUsername(username);

    }

    //3.定义一个方法,对用户输入的中注册的用户名进行判断
    public static void checkUsername(String username)  {
        //遍历存储已经注册过用户名的数组,获取每个用户名
        for (String name : usernames) {
            //使用获取到的用户名和用户输入的用户名比较
            if(name.equals(username)){
                //true:用户名已经存在,抛出RegisterException异常,告知用户"亲,该用户名已经被注册";
                throw new RegisterException("亲,该用户名已经被注册");//抛出运行期异常,无需处理,交给JVM处理,中断处理
            }
        }

        //若是循环结束了,尚未找到重复的用户名,提示用户"恭喜您,注册成功!";
        System.out.println("恭喜您,注册成功!");
    }
}

多线程

并发与并行

进程与线程

主线程

经过继承Thread类实现多线程

package com.itheima.demo06.Thread;
//1.建立一个Thread类的子类
public class MyThread extends Thread{
    //2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要作什么?)
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println("run:"+i);
        }
    }
}
/*--------------------------------------------------------------------*/
package com.itheima.demo06.Thread;
/*
    建立多线程程序的第一种方式:建立Thread类的子类
    java.lang.Thread类:是描述线程的类,咱们想要实现多线程程序,就必须继承Thread类

    实现步骤:
        1.建立一个Thread类的子类
        2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要作什么?)
        3.建立Thread类的子类对象
        4.调用Thread类中的方法start方法,开启新的线程,执行run方法
             void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
             结果是两个线程并发地运行;当前线程(main线程)和另外一个线程(建立的新线程,执行其 run 方法)。
             屡次启动一个线程是非法的。特别是当线程已经结束执行后,不能再从新启动。
    java程序属于抢占式调度,那个线程的优先级高,那个线程优先执行;同一个优先级,随机选择一个执行
 */
public class Demo01Thread {
    public static void main(String[] args) {
        //3.建立Thread类的子类对象
        MyThread mt = new MyThread();
        //4.调用Thread类中的方法start方法,开启新的线程,执行run方法
        mt.start();

        for (int i = 0; i <20 ; i++) {
            System.out.println("main:"+i);
        }
    }
}

多线程随机性打印原理

多线程内存图解

Thread类中的经常使用方法

package com.itheima.demo01.getName;
/*
    获取线程的名称:
        1.使用Thread类中的方法getName()
            String getName() 返回该线程的名称。
        2.能够先获取到当前正在执行的线程,使用线程中的方法getName()获取线程的名称
            static Thread currentThread() 返回对当前正在执行的线程对象的引用。
 */
// 定义一个Thread类的子类
public class MyThread extends Thread{
    //重写Thread类中的run方法,设置线程任务
    @Override
    public void run() {
        //获取线程名称
        //String name = getName();
        //System.out.println(name);

        //Thread t = Thread.currentThread();
        //System.out.println(t);//Thread[Thread-0,5,main]
        //String name = t.getName();
        //System.out.println(name);

        //链式编程
        System.out.println(Thread.currentThread().getName());
    }
}
/*---------------------------------------------------------------*/
package com.itheima.demo01.getName;
/*
    线程的名称:
        主线程: main
        新线程: Thread-0,Thread-1,Thread-2
 */
public class Demo01GetThreadName {
    public static void main(String[] args) {
        //建立Thread类的子类对象
        MyThread mt = new MyThread();
        //调用start方法,开启新线程,执行run方法
        mt.start();

        new MyThread().start();
        new MyThread().start();

        //链式编程
        System.out.println(Thread.currentThread().getName());
    }
}

package com.itheima.demo03.sleep;
/*
    public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时中止执行)。
    毫秒数结束以后,线程继续执行
 */
public class Demo01Sleep {
    public static void main(String[] args) {
        //模拟秒表
        for (int i = 1; i <=60 ; i++) {
            System.out.println(i);

            //使用Thread类的sleep方法让程序睡眠1秒钟
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

经过实现Runnable接口的方式实现多线程

package com.itheima.demo04.Runnable;
/*
    建立多线程程序的第二种方式:实现Runnable接口
    java.lang.Runnable
        Runnable 接口应该由那些打算经过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
    java.lang.Thread类的构造方法
        Thread(Runnable target) 分配新的 Thread 对象。
        Thread(Runnable target, String name) 分配新的 Thread 对象。

    实现步骤:
        1.建立一个Runnable接口的实现类
        2.在实现类中重写Runnable接口的run方法,设置线程任务
        3.建立一个Runnable接口的实现类对象
        4.建立Thread类对象,构造方法中传递Runnable接口的实现类对象
        5.调用Thread类中的start方法,开启新的线程执行run方法

    实现Runnable接口建立多线程程序的好处:
        1.避免了单继承的局限性
            一个类只能继承一个类(一我的只能有一个亲爹),类继承了Thread类就不能继承其余的类
            实现了Runnable接口,还能够继承其余的类,实现其余的接口
        2.加强了程序的扩展性,下降了程序的耦合性(解耦)
            实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
            实现类中,重写了run方法:用来设置线程任务
            建立Thread类对象,调用start方法:用来开启新线程
 */
public class Demo01Runnable {
    public static void main(String[] args) {
        //3.建立一个Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //4.建立Thread类对象,构造方法中传递Runnable接口的实现类对象
        //Thread t = new Thread(run);//打印线程名称
        Thread t = new Thread(new RunnableImpl2());//打印HelloWorld
        //5.调用Thread类中的start方法,开启新的线程执行run方法
        t.start();

        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
/*---------------------------------------------------------------*/
package com.itheima.demo04.Runnable;
//1.建立一个Runnable接口的实现类
public class RunnableImpl implements Runnable{
    //2.在实现类中重写Runnable接口的run方法,设置线程任务
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
/*---------------------------------------------------------------*/
package com.itheima.demo04.Runnable;
//1.建立一个Runnable接口的实现类
public class RunnableImpl2 implements Runnable{
    //2.在实现类中重写Runnable接口的run方法,设置线程任务
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println("HelloWorld"+i);
        }
    }
}

匿名内部类实现多线程

package com.itheima.demo05.InnerClassThread;
/*
    匿名内部类方式实现线程的建立

    匿名:没有名字
    内部类:写在其余类内部的类

    匿名内部类做用:简化代码
        把子类继承父类,重写父类的方法,建立子类对象合一步完成
        把实现类实现类接口,重写接口中的方法,建立实现类对象合成一步完成
    匿名内部类的最终产物:子类/实现类对象,而这个类没有名字

    格式:
        new 父类/接口(){
            重复父类/接口中的方法
        };
 */
public class Demo01InnerClassThread {
    public static void main(String[] args) {
        //线程的父类是Thread
        // new MyThread().start();
        new Thread(){
            //重写run方法,设置线程任务
            @Override
            public void run() {
                for (int i = 0; i <20 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"黑马");
                }
            }
        }.start();

        //线程的接口Runnable
        //Runnable r = new RunnableImpl();//多态
        Runnable r = new Runnable(){
            //重写run方法,设置线程任务
            @Override
            public void run() {
                for (int i = 0; i <20 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"程序员");
                }
            }
        };
        new Thread(r).start();

        //简化接口的方式
        new Thread(new Runnable(){
            //重写run方法,设置线程任务
            @Override
            public void run() {
                for (int i = 0; i <20 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"传智播客");
                }
            }
        }).start();
    }
}

线程安全问题

package com.itheima.demo06.ThreadSafe;
/*
    实现卖票案例
 */
public class RunnableImpl implements Runnable{
    //定义一个多个线程共享的票源
    private  int ticket = 100;


    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操做重复执行
        while(true){
            //先判断票是否存在
            if(ticket>0){
                //提升安全问题出现的几率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }
        }
    }
}
/*-----------------------------------------------------------------------*/

package com.itheima.demo06.ThreadSafe;
/*
    模拟卖票案例
    建立3个线程,同时开启,对共享的票进行出售
 */
public class Demo01Ticket {
    public static void main(String[] args) {
        //建立Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //建立Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

线程安全问题产生的原理

同步代码块【解决安全问题的第一种方式】

package com.itheima.demo07.Synchronized;
/*
    卖票案例出现了线程安全问题
    卖出了不存在的票和重复的票

    解决线程安全问题的一种方案:使用同步代码块
    格式:
        synchronized(锁对象){
            可能会出现线程安全问题的代码(访问了共享数据的代码)
        }

    注意:
        1.经过代码块中的锁对象,可使用任意的对象
        2.可是必须保证多个线程使用的锁对象是同一个
        3.锁对象做用:
            把同步代码块锁住,只让一个线程在同步代码块中执行
 */
public class RunnableImpl implements Runnable{
    //定义一个多个线程共享的票源
    private  int ticket = 100;

    //建立一个锁对象
    Object obj = new Object();

    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操做重复执行
        while(true){
           //同步代码块
            synchronized (obj){
                //先判断票是否存在
                if(ticket>0){
                    //提升安全问题出现的几率,让程序睡眠
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //票存在,卖票 ticket--
                    System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                    ticket--;
                }
            }
        }
    }
}
/*----------------------------------------------------------------------*/
package com.itheima.demo07.Synchronized;

/*
    模拟卖票案例
    建立3个线程,同时开启,对共享的票进行出售
 */
public class Demo01Ticket {
    public static void main(String[] args) {
        //建立Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //建立Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

同步技术原理

(静态)同步方法【解决安全问题的第二种方式】

package com.itheima.demo08.Synchronized;
/*
    卖票案例出现了线程安全问题
    卖出了不存在的票和重复的票

    解决线程安全问题的二种方案:使用同步方法
    使用步骤:
        1.把访问了共享数据的代码抽取出来,放到一个方法中
        2.在方法上添加synchronized修饰符

    格式:定义方法的格式
    修饰符 synchronized 返回值类型 方法名(参数列表){
        可能会出现线程安全问题的代码(访问了共享数据的代码)
    }
 */
public class RunnableImpl implements Runnable{
    //定义一个多个线程共享的票源
    private static int ticket = 100;


    //设置线程任务:卖票
    @Override
    public void run() {
        System.out.println("this:"+this);//this:com.itheima.demo08.Synchronized.RunnableImpl@58ceff1
        //使用死循环,让卖票操做重复执行
        while(true){
            payTicketStatic();
        }
    }

    /*
        静态的同步方法
        锁对象是谁?
        不能是this
        this是建立对象以后产生的,静态方法优先于对象
        静态方法的锁对象是本类的class属性-->class文件对象(反射)
     */
    public static /*synchronized*/ void payTicketStatic(){
        synchronized (RunnableImpl.class){
            //先判断票是否存在
            if(ticket>0){
                //提升安全问题出现的几率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }
        }

    }

    /*
        定义一个同步方法
        同步方法也会把方法内部的代码锁住
        只让一个线程执行
        同步方法的锁对象是谁?
        就是实现类对象 new RunnableImpl()
        也是就是this
     */
    public /*synchronized*/ void payTicket(){
        synchronized (this){
            //先判断票是否存在
            if(ticket>0){
                //提升安全问题出现的几率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }
        }

    }
}
/*----------------------------------------------------------*/
package com.itheima.demo08.Synchronized;

/*
    模拟卖票案例
    建立3个线程,同时开启,对共享的票进行出售
 */
public class Demo01Ticket {
    public static void main(String[] args) {
        //建立Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        System.out.println("run:"+run);//run:com.itheima.demo08.Synchronized.RunnableImpl@58ceff1
        //建立Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

Lock锁【解决安全问题的第三种方式】(JKD1.5以后)

package com.itheima.demo09.Lock;

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

/*
    卖票案例出现了线程安全问题
    卖出了不存在的票和重复的票

    解决线程安全问题的三种方案:使用Lock锁
    java.util.concurrent.locks.Lock接口
    Lock 实现提供了比使用 synchronized 方法和语句可得到的更普遍的锁定操做。
    Lock接口中的方法:
        void lock()获取锁。
        void unlock()  释放锁。
    java.util.concurrent.locks.ReentrantLock implements Lock接口


    使用步骤:
        1.在成员位置建立一个ReentrantLock对象
        2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
        3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
 */
public class RunnableImpl implements Runnable{
    //定义一个多个线程共享的票源
    private  int ticket = 100;

    //1.在成员位置建立一个ReentrantLock对象
    Lock l = new ReentrantLock();

    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操做重复执行
        while(true){
            //2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
            l.lock();

            //先判断票是否存在
            if(ticket>0){
                //提升安全问题出现的几率,让程序睡眠
                try {
                    Thread.sleep(10);
                    //票存在,卖票 ticket--
                    System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
                    l.unlock();//不管程序是否异常,都会把锁释放
                }
            }
        }
    }

    /*//设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操做重复执行
        while(true){
           //2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
           l.lock();

            //先判断票是否存在
            if(ticket>0){
                //提升安全问题出现的几率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }

            //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
            l.unlock();
        }
    }*/
}
/*-----------------------------------------------------------*/
package com.itheima.demo09.Lock;

/*
    模拟卖票案例
    建立3个线程,同时开启,对共享的票进行出售
 */
public class Demo01Ticket {
    public static void main(String[] args) {
        //建立Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //建立Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

线程的状态

线程通讯

概念:多个线程在处理同一个资源,可是处理的动做(线程的任务)却不相同。
好比:线程A用来生成包子的,线程B用来吃包子的,包子能够理解为同一资源,线程A与线程B处理的动做,一个 是生产,一个是消费,那么线程A与线程B之间就存在线程通讯问题。

为何要处理线程间通讯:
多个线程并发执行时, 在默认状况下CPU是随机切换线程的,当咱们须要多个线程来共同完成一件任务,而且咱们 但愿他们有规律的执行, 那么多线程之间须要一些协调通讯,以此来帮咱们达到多线程共同操做一份数据。
如何保证线程间通讯有效利用资源:
多个线程在处理同一个资源,而且任务不一样时,须要线程通讯来帮助解决线程之间对同一个变量的使用或操做。 就 是多个线程在操做同一份数据时, 避免对同一共享变量的争夺。也就是咱们须要经过必定的手段使各个线程能有效 的利用资源。而这种手段即—— 等待唤醒机制

等待唤醒机制

生产者&消费者

package com.itheima.demo10.WaitAndNotify;
/*
    等待唤醒案例:线程之间的通讯
        建立一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
        建立一个老板线程(生产者):花了5秒作包子,作好包子以后,调用notify方法,唤醒顾客吃包子

    注意:
        顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行
        同步使用的锁对象必须保证惟一
        只有锁对象才能调用wait和notify方法

    Obejct类中的方法
    void wait()
          在其余线程调用此对象的 notify() 方法或 notifyAll() 方法前,致使当前线程等待。
    void notify()
          唤醒在此对象监视器上等待的单个线程。
          会继续执行wait方法以后的代码
 */
public class Demo01WaitAndNotify {
    public static void main(String[] args) {
        //建立锁对象,保证惟一
        Object obj = new Object();
        // 建立一个顾客线程(消费者)
        new Thread(){
            @Override
            public void run() {
               //一直等着买包子
               while(true){
                   //保证等待和唤醒的线程只能有一个执行,须要使用同步技术
                   synchronized (obj){
                       System.out.println("告知老板要的包子的种类和数量");
                       //调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
                       try {
                           obj.wait();
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                       //唤醒以后执行的代码
                       System.out.println("包子已经作好了,开吃!");
                       System.out.println("---------------------------------------");
                   }
               }
            }
        }.start();

        //建立一个老板线程(生产者)
        new Thread(){
            @Override
            public void run() {
                //一直作包子
                while (true){
                    //花了5秒作包子
                    try {
                        Thread.sleep(5000);//花5秒钟作包子
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //保证等待和唤醒的线程只能有一个执行,须要使用同步技术
                    synchronized (obj){
                        System.out.println("老板5秒钟以后作好包子,告知顾客,能够吃包子了");
                        //作好包子以后,调用notify方法,唤醒顾客吃包子
                        obj.notify();
                    }
                }
            }
        }.start();
    }
}

package com.itheima.demo10.WaitAndNotify;
/*
    进入到TimeWaiting(计时等待)有两种方式
    1.使用sleep(long m)方法,在毫秒值结束以后,线程睡醒进入到Runnable/Blocked状态
    2.使用wait(long m)方法,wait方法若是在毫秒值结束以后,尚未被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态

    唤醒的方法:
         void notify() 唤醒在此对象监视器上等待的单个线程。
         void notifyAll() 唤醒在此对象监视器上等待的全部线程。
 */
public class Demo02WaitAndNotify {
    public static void main(String[] args) {
        //建立锁对象,保证惟一
        Object obj = new Object();
        // 建立一个顾客线程(消费者)
        new Thread(){
            @Override
            public void run() {
                //一直等着买包子
                while(true){
                    //保证等待和唤醒的线程只能有一个执行,须要使用同步技术
                    synchronized (obj){
                        System.out.println("顾客1告知老板要的包子的种类和数量");
                        //调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //唤醒以后执行的代码
                        System.out.println("包子已经作好了,顾客1开吃!");
                        System.out.println("---------------------------------------");
                    }
                }
            }
        }.start();

        // 建立一个顾客线程(消费者)
        new Thread(){
            @Override
            public void run() {
                //一直等着买包子
                while(true){
                    //保证等待和唤醒的线程只能有一个执行,须要使用同步技术
                    synchronized (obj){
                        System.out.println("顾客2告知老板要的包子的种类和数量");
                        //调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //唤醒以后执行的代码
                        System.out.println("包子已经作好了,顾客2开吃!");
                        System.out.println("---------------------------------------");
                    }
                }
            }
        }.start();

        //建立一个老板线程(生产者)
        new Thread(){
            @Override
            public void run() {
                //一直作包子
                while (true){
                    //花了5秒作包子
                    try {
                        Thread.sleep(5000);//花5秒钟作包子
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //保证等待和唤醒的线程只能有一个执行,须要使用同步技术
                    synchronized (obj){
                        System.out.println("老板5秒钟以后作好包子,告知顾客,能够吃包子了");
                        //作好包子以后,调用notify方法,唤醒顾客吃包子
                        //obj.notify();//若是有多个等待线程,随机唤醒一个
                        obj.notifyAll();//唤醒全部等待的线程
                    }
                }
            }
        }.start();
    }
}

obj.notify()结果

obj.notifyAll()结果

等待与唤醒机制的代码实现

package com.itheima.demo01.WaitAndNotify;
/*
    资源类:包子类
    设置包子的属性
        皮
        陷
        包子的状态: 有 true,没有 false
 */
public class BaoZi {
    //皮
    String pi;
    //陷
    String xian;
    //包子的状态: 有 true,没有 false,设置初始值为false没有包子
    boolean flag = false;

}

package com.itheima.demo01.WaitAndNotify;
/*
    生产者(包子铺)类:是一个线程类,能够继承Thread
    设置线程任务(run):生产包子
    对包子的状态进行判断
    true:有包子
        包子铺调用wait方法进入等待状态
    false:没有包子
        包子铺生产包子
        增长一些趣味性:交替生产两种包子
            有两种状态(i%2==0)
        包子铺生产好了包子
        修改包子的状态为true有
        唤醒吃货线程,让吃货线程吃包子

    注意:
        包子铺线程和包子线程关系-->通讯(互斥)
        必须同时同步技术保证两个线程只能有一个在执行
        锁对象必须保证惟一,可使用包子对象做为锁对象
        包子铺类和吃货的类就须要把包子对象做为参数传递进来
            1.须要在成员位置建立一个包子变量
            2.使用带参数构造方法,为这个包子变量赋值
 */
public class BaoZiPu extends Thread{
    //1.须要在成员位置建立一个包子变量
    private BaoZi bz;

    //2.使用带参数构造方法,为这个包子变量赋值
    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    //设置线程任务(run):生产包子
    @Override
    public void run() {
        //定义一个变量
        int count = 0;
        //让包子铺一直生产包子
        while(true){
            //必须同时同步技术保证两个线程只能有一个在执行
            synchronized (bz){
                //对包子的状态进行判断
                if(bz.flag==true){
                    //包子铺调用wait方法进入等待状态
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //被唤醒以后执行,包子铺生产包子
                //增长一些趣味性:交替生产两种包子
                if(count%2==0){
                    //生产 薄皮三鲜馅包子
                    bz.pi = "薄皮";
                    bz.xian = "三鲜馅";
                }else{
                    //生产 冰皮 牛肉大葱陷
                    bz.pi = "冰皮";
                    bz.xian = "牛肉大葱陷";

                }
                count++;
                System.out.println("包子铺正在生产:"+bz.pi+bz.xian+"包子");
                //生产包子须要3秒钟
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //包子铺生产好了包子
                //修改包子的状态为true有
                bz.flag = true;
                //唤醒吃货线程,让吃货线程吃包子
                bz.notify();
                System.out.println("包子铺已经生产好了:"+bz.pi+bz.xian+"包子,吃货能够开始吃了");
            }
        }
    }
}

package com.itheima.demo01.WaitAndNotify;
/*
    消费者(吃货)类:是一个线程类,能够继承Thread
    设置线程任务(run):吃包子
    对包子的状态进行判断
    false:没有包子
        吃货调用wait方法进入等待状态
    true:有包子
        吃货吃包子
        吃货吃完包子
        修改包子的状态为false没有
        吃货唤醒包子铺线程,生产包子
 */
public class ChiHuo extends Thread{
    //1.须要在成员位置建立一个包子变量
    private BaoZi bz;

    //2.使用带参数构造方法,为这个包子变量赋值
    public ChiHuo(BaoZi bz) {
        this.bz = bz;
    }
    //设置线程任务(run):吃包子
    @Override
    public void run() {
        //使用死循环,让吃货一直吃包子
        while (true){
            //必须同时同步技术保证两个线程只能有一个在执行
            synchronized (bz){
                //对包子的状态进行判断
                if(bz.flag==false){
                    //吃货调用wait方法进入等待状态
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //被唤醒以后执行的代码,吃包子
                System.out.println("吃货正在吃:"+bz.pi+bz.xian+"的包子");
                //吃货吃完包子
                //修改包子的状态为false没有
                bz.flag = false;
                //吃货唤醒包子铺线程,生产包子
                bz.notify();
                System.out.println("吃货已经把:"+bz.pi+bz.xian+"的包子吃完了,包子铺开始生产包子");
                System.out.println("----------------------------------------------------");
            }
        }
    }
}

package com.itheima.demo01.WaitAndNotify;
/*
    测试类:
    包含main方法,程序执行的入口,启动程序
    建立包子对象;
    建立包子铺线程,开启,生产包子;
    建立吃货线程,开启,吃包子;
 */
public class Demo {
    public static void main(String[] args) {
        //建立包子对象;
        BaoZi bz =new BaoZi();
        //建立包子铺线程,开启,生产包子;
        new BaoZiPu(bz).start();
        //建立吃货线程,开启,吃包子;
        new ChiHuo(bz).start();
    }
}

线程池

概念:
咱们使用线程的时候就去建立一个线程,这样实现起来很是简便,可是就会有一个问题:

若是并发的线程数量不少,而且每一个线程都是执行一个时间很短的任务就结束了,这样频繁建立线程就会大大下降系统的效率,由于频繁建立线程和销毁线程须要时间。
其实就是一个容纳多个线程的容器,其中的线程能够反复使用,省去了频繁建立线程对象的操做,无需反复建立线程而消耗过多资源

原理:



合理利用线程池可以带来三个好处:

  1. 下降资源消耗。减小了建立和销毁线程的次数,每一个工做线程均可以被重复利用,可执行多个任务。
  2. 提升响应速度。当任务到达时,任务能够不须要的等到线程建立就能当即执行。
  3. 提升线程的可管理性。能够根据系统的承受能力,调整线程池中工做线线程的数目,防止由于消耗过多的内存,而把服务器累趴下(每一个线程须要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

    package com.itheima.demo02.ThreadPool;
    
     import java.util.concurrent.ExecutorService;
     import java.util.concurrent.Executors;
    
     /*
         线程池:JDK1.5以后提供的
         java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
         Executors类中的静态方法:
             static ExecutorService newFixedThreadPool(int nThreads) 建立一个可重用固定线程数的线程池
             参数:
                 int nThreads:建立线程池中包含的线程数量
             返回值:
                 ExecutorService接口,返回的是ExecutorService接口的实现类对象,咱们可使用ExecutorService接口接收(面向接口编程)
         java.util.concurrent.ExecutorService:线程池接口
             用来从线程池中获取线程,调用start方法,执行线程任务
                 submit(Runnable task) 提交一个 Runnable 任务用于执行
             关闭/销毁线程池的方法
                 void shutdown()
         线程池的使用步骤:
             1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
             2.建立一个类,实现Runnable接口,重写run方法,设置线程任务
             3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
             4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
      */
     public class Demo01ThreadPool {
         public static void main(String[] args) {
             //1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
             ExecutorService es = Executors.newFixedThreadPool(2);
             //3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
             es.submit(new RunnableImpl());//pool-1-thread-1建立了一个新的线程执行
             //线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程能够继续使用
             es.submit(new RunnableImpl());//pool-1-thread-1建立了一个新的线程执行
             es.submit(new RunnableImpl());//pool-1-thread-2建立了一个新的线程执行
    
             //4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
             es.shutdown();
    
             es.submit(new RunnableImpl());//抛异常,线程池都没有了,就不能获取线程了
         }
    
     }
     /*----------------------------------------------------------------------*/
     package com.itheima.demo02.ThreadPool;
     /*
         2.建立一个类,实现Runnable接口,重写run方法,设置线程任务
      */
     public class RunnableImpl implements Runnable{
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName()+"建立了一个新的线程执行");
         }
     }

Lambda

Lambda的使用前提:

Lambda的语法很是简洁,彻底没有面向对象复杂的束缚。可是使用时有几个问题须要特别注意:

  1. 使用Lambda必须具备接口(抽象类不能够),且要求接口中有且仅有一个抽象方法
    不管是JDK内置的RunnableComparator接口仍是自定义的接口,只有当接口中的抽象方法存在且惟一时,才可使用Lambda。
  2. 使用Lambda必须具备上下文推断
    也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda做为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

package com.itheima.demo03.Lambda;
/*
    Lambda表达式的标准格式:
        由三部分组成:
            a.一些参数
            b.一个箭头
            c.一段代码
        格式:
            (参数列表) -> {一些重写方法的代码};
        解释说明格式:
            ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
            ->:传递的意思,把参数传递给方法体{}
            {}:重写接口的抽象方法的方法体
 */
public class Demo02Lambda {
    public static void main(String[] args) {
        //使用匿名内部类的方式,实现多线程
        new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" 新线程建立了");
            }
        }).start();

        //使用Lambda表达式,实现多线程
        new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 新线程建立了");
            }
        ).start();

        //优化省略Lambda
        new Thread(()->System.out.println(Thread.currentThread().getName()+" 新线程建立了")).start();
    }
}

package com.itheima.demo07.Lambda;

import java.util.ArrayList;

/*
    Lambda表达式:是可推导,能够省略
    凡是根据上下文推导出来的内容,均可以省略书写
    能够省略的内容:
        1.(参数列表):括号中参数列表的数据类型,能够省略不写
        2.(参数列表):括号中的参数若是只有一个,那么类型和()均可以省略
        3.{一些代码}:若是{}中的代码只有一行,不管是否有返回值,均可以省略({},return,分号)
            注意:要省略{},return,分号必须一块儿省略
 */
public class Demo01ArrayList {
    public static void main(String[] args) {
        //JDK1.7版本以前,建立集合对象必须把先后的泛型都写上
        ArrayList<String> list01 = new ArrayList<String>();

        //JDK1.7版本以后,=号后边的泛型能够省略,后边的泛型能够根据前边的泛型推导出来
        ArrayList<String> list02 = new ArrayList<>();
    }
}

File

静态成员变量

package com.itheima.demo01.File;

import java.io.File;

/*
    java.io.File类
    文件和目录路径名的抽象表示形式。
    java把电脑中的文件和文件夹(目录)封装为了一个File类,咱们可使用File类对文件和文件夹进行操做
    咱们可使用File类的方法
        建立一个文件/文件夹
        删除文件/文件夹
        获取文件/文件夹
        判断文件/文件夹是否存在
        对文件夹进行遍历
        获取文件的大小
    File类是一个与系统无关的类,任何的操做系统均可以使用这个类中的方法

    重点:记住这三个单词
        file:文件
        directory:文件夹/目录
        path:路径
 */
public class Demo01File {
    public static void main(String[] args) {
        /*
            static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
            static char pathSeparatorChar 与系统有关的路径分隔符。

            static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
            static char separatorChar 与系统有关的默认名称分隔符。

            操做路径:路径不能写死了
            C:\develop\a\a.txt  windows
            C:/develop/a/a.txt  linux
            "C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
         */
        String pathSeparator = File.pathSeparator;
        System.out.println(pathSeparator);//路径分隔符 windows:分号;  linux:冒号:

        String separator = File.separator;
        System.out.println(separator);// 文件名称分隔符 windows:反斜杠\  linux:正斜杠/
    }

}

绝对和相对路径&构造方法

package com.itheima.demo01.File;

import java.io.File;

/*
    路径:
        绝对路径:是一个完整的路径
            以盘符(c:,D:)开始的路径
                c:\\a.txt
                C:\\Users\itcast\\IdeaProjects\\shungyuan\\123.txt
                D:\\demo\\b.txt
        相对路径:是一个简化的路径
            相对指的是相对于当前项目的根目录(C:\\Users\itcast\\IdeaProjects\\shungyuan)
            若是使用当前项目的根目录,路径能够简化书写
            C:\\Users\itcast\\IdeaProjects\\shungyuan\\123.txt-->简化为: 123.txt(能够省略项目的根目录)
        注意:
            1.路径是不区分大小写
            2.路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠表明一个普通的反斜杠
 */
public class Demo02File {
    public static void main(String[] args) {
        /*
            File类的构造方法
         */
        //show02("c:\\","a.txt");//c:\a.txt
        //show02("d:\\","a.txt");//d:\a.txt
        show03();

        File f = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        long length = f.length();
        System.out.println(length);
    }

    /*
        File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串建立一个新 File 实例。
        参数:把路径分红了两部分
            File parent:父路径
            String child:子路径
        好处:
             父路径和子路径,能够单独书写,使用起来很是灵活;父路径和子路径均可以变化
             父路径是File类型,可使用File的方法对路径进行一些操做,再使用路径建立对象
     */
    private static void show03() {
        File parent = new File("c:\\");
        File file = new File(parent,"hello.java");
        System.out.println(file);//c:\hello.java
    }

    /*
        File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串建立一个新 File 实例。
        参数:把路径分红了两部分
            String parent:父路径
            String child:子路径
        好处:
            父路径和子路径,能够单独书写,使用起来很是灵活;父路径和子路径均可以变化
     */
    private static void show02(String parent, String child) {
        File file = new File(parent,child);
        System.out.println(file);//c:\a.txt
    }

    /*
        File(String pathname) 经过将给定路径名字符串转换为抽象路径名来建立一个新 File 实例。
        参数:
            String pathname:字符串的路径名称
            路径能够是以文件结尾,也能够是以文件夹结尾
            路径能够是相对路径,也能够是绝对路径
            路径能够是存在,也能够是不存在
            建立File对象,只是把字符串路径封装为File对象,不考虑路径的真假状况
     */
    private static void show01() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
        System.out.println(f1);//重写了Object类的toString方法 C:\Users\itcast\IdeaProjects\shungyuan\a.txt

        File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        System.out.println(f2);//C:\Users\itcast\IdeaProjects\shungyuan

        File f3 = new File("b.txt");
        System.out.println(f3);//b.txt
    }
}

获取功能的方法

package com.itheima.demo01.File;

import java.io.File;

/*
    File类获取功能的方法
        - public String getAbsolutePath() :返回此File的绝对路径名字符串。
        - public String getPath() :将此File转换为路径名字符串。
        - public String getName()  :返回由此File表示的文件或目录的名称。
        - public long length()  :返回由此File表示的文件的长度。
 */
public class Demo03File {
    public static void main(String[] args) {
        show04();
    }

    /*
        public long length()  :返回由此File表示的文件的长度。
        获取的是构造方法指定的文件的大小,以字节为单位
        注意:
            文件夹是没有大小概念的,不能获取文件夹的大小
            若是构造方法中给出的路径不存在,那么length方法返回0
     */
    private static void show04() {
        File f1 = new File("C:\\develop\\a\\1.jpg");
        long l1 = f1.length();
        System.out.println(l1);//780831字节

        File f2 = new File("C:\\develop\\a\\2.jpg");
        System.out.println(f2.length());//0

        File f3 = new File("C:\\develop\\a");
        System.out.println(f3.length());//0 文件夹没有大小概念的
    }

    /*
        public String getName()  :返回由此File表示的文件或目录的名称。
        获取的就是构造方法传递路径的结尾部分(文件/文件夹)
     */
    private static void show03() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
        String name1 = f1.getName();
        System.out.println(name1);//a.txt

        File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        String name2 = f2.getName();
        System.out.println(name2);//shungyuan
    }

    /*
        public String getPath() :将此File转换为路径名字符串。
        获取的构造方法中传递的路径

        toString方法调用的就是getPath方法
        源码:
            public String toString() {
                return getPath();
            }
     */
    private static void show02() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
        File f2 = new File("a.txt");
        String path1 = f1.getPath();
        System.out.println(path1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
        String path2 = f2.getPath();
        System.out.println(path2);//a.txt

        System.out.println(f1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
        System.out.println(f1.toString());//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
    }

    /*
        public String getAbsolutePath() :返回此File的绝对路径名字符串。
        获取的构造方法中传递的路径
        不管路径是绝对的仍是相对的,getAbsolutePath方法返回的都是绝对路径
     */
    private static void show01() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
        String absolutePath1 = f1.getAbsolutePath();
        System.out.println(absolutePath1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt

        File f2 = new File("a.txt");
        String absolutePath2 = f2.getAbsolutePath();
        System.out.println(absolutePath2);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
    }
}

判断功能的方法

package com.itheima.demo01.File;

import java.io.File;

/*
    File类判断功能的方法
        - public boolean exists() :此File表示的文件或目录是否实际存在。
        - public boolean isDirectory() :此File表示的是否为目录。
        - public boolean isFile() :此File表示的是否为文件。
 */
public class Demo04File {
    public static void main(String[] args) {
        show02();
    }

    /*
        public boolean isDirectory() :此File表示的是否为目录。
            用于判断构造方法中给定的路径是否以文件夹结尾
                是:true
                否:false
        public boolean isFile() :此File表示的是否为文件。
            用于判断构造方法中给定的路径是否以文件结尾
                是:true
                否:false
        注意:
            电脑的硬盘中只有文件/文件夹,两个方法是互斥
            这两个方法使用前提,路径必须是存在的,不然都返回false
     */
    private static void show02() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shung");

        //不存在,就没有必要获取
        if(f1.exists()){
            System.out.println(f1.isDirectory());
            System.out.println(f1.isFile());
        }

        File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        if(f2.exists()){
            System.out.println(f2.isDirectory());//true
            System.out.println(f2.isFile());//false
        }

        File f3 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\shungyuan.iml");
        if(f3.exists()){
            System.out.println(f3.isDirectory());//false
            System.out.println(f3.isFile());//true
        }
    }

    /*
        public boolean exists() :此File表示的文件或目录是否实际存在。
        用于判断构造方法中的路径是否存在
            存在:true
            不存在:false
     */
    private static void show01() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        System.out.println(f1.exists());//true

        File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shung");
        System.out.println(f2.exists());//false

        File f3 = new File("shungyuan.iml");//相对路径 C:\Users\itcast\IdeaProjects\shungyuan\shungyuan.iml
        System.out.println(f3.exists());//true

        File f4 = new File("a.txt");
        System.out.println(f4.exists());//false
    }
}

建立删除功能的方法

package com.itheima.demo01.File;

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

/*
    File类建立删除功能的方法
        - public boolean createNewFile() :当且仅当具备该名称的文件尚不存在时,建立一个新的空文件。
        - public boolean delete() :删除由此File表示的文件或目录。
        - public boolean mkdir() :建立由此File表示的目录。
        - public boolean mkdirs() :建立由此File表示的目录,包括任何须需但不存在的父目录。
 */
public class Demo05File {
    public static void main(String[] args) throws IOException {
        show03();
    }

    /*
        public boolean delete() :删除由此File表示的文件或目录。
        此方法,能够删除构造方法路径中给出的文件/文件夹
        返回值:布尔值
            true:文件/文件夹删除成功,返回true
            false:文件夹中有内容,不会删除返回false;构造方法中路径不存在false
        注意:
            delete方法是直接在硬盘删除文件/文件夹,不走回收站,删除要谨慎
     */
    private static void show03() {
        File f1 = new File("08_FileAndRecursion\\新建文件夹");
        boolean b1 = f1.delete();
        System.out.println("b1:"+b1);

        File f2 = new File("08_FileAndRecursion\\abc.txt");
        System.out.println(f2.delete());
    }

    /*
       public boolean mkdir() :建立单级空文件夹
       public boolean mkdirs() :既能够建立单级空文件夹,也能够建立多级文件夹
       建立文件夹的路径和名称在构造方法中给出(构造方法的参数)
        返回值:布尔值
            true:文件夹不存在,建立文件夹,返回true
            false:文件夹存在,不会建立,返回false;构造方法中给出的路径不存在返回false
        注意:
            1.此方法只能建立文件夹,不能建立文件
     */
    private static void show02() {
        File f1 = new File("08_FileAndRecursion\\aaa");
        boolean b1 = f1.mkdir();
        System.out.println("b1:"+b1);

        File f2 = new File("08_FileAndRecursion\\111\\222\\333\\444");
        boolean b2 = f2.mkdirs();
        System.out.println("b2:"+b2);

        File f3 = new File("08_FileAndRecursion\\abc.txt");
        boolean b3 = f3.mkdirs();//看类型,是一个文件
        System.out.println("b3:"+b3);

        File f4 = new File("08_F\\ccc");
        boolean b4 = f4.mkdirs();//不会抛出异常,路径不存在,不会建立
        System.out.println("b4:"+b4);
    }

    /*
        public boolean createNewFile() :当且仅当具备该名称的文件尚不存在时,建立一个新的空文件。
        建立文件的路径和名称在构造方法中给出(构造方法的参数)
        返回值:布尔值
            true:文件不存在,建立文件,返回true
            false:文件存在,不会建立,返回false
        注意:
            1.此方法只能建立文件,不能建立文件夹
            2.建立文件的路径必须存在,不然会抛出异常

        public boolean createNewFile() throws IOException
        createNewFile声明抛出了IOException,咱们调用这个方法,就必须的处理这个异常,要么throws,要么trycatch
     */
    private static void show01() throws IOException {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion\\1.txt");
        boolean b1 = f1.createNewFile();
        System.out.println("b1:"+b1);

        File f2 = new File("08_FileAndRecursion\\2.txt");
        System.out.println(f2.createNewFile());

        File f3 = new File("08_FileAndRecursion\\新建文件夹");
        System.out.println(f3.createNewFile());//不要被名称迷糊,要看类型

        File f4 = new File("08_FileAndRecursi\\3.txt");
        System.out.println(f4.createNewFile());//路径不存在,抛出IOException
    }
}

遍历文件夹

package com.itheima.demo01.File;

import java.io.File;

/*
    File类遍历(文件夹)目录功能
        - public String[] list() :返回一个String数组,表示该File目录中的全部子文件或目录。
        - public File[] listFiles() :返回一个File数组,表示该File目录中的全部的子文件或目录。

    注意:
        list方法和listFiles方法遍历的是构造方法中给出的目录
        若是构造方法中给出的目录的路径不存在,会抛出空指针异常
        若是构造方法中给出的路径不是一个目录,也会抛出空指针异常
 */
public class Demo06File {
    public static void main(String[] args) {
        show02();
    }

    /*
        public File[] listFiles() :返回一个File数组,表示该File目录中的全部的子文件或目录。
        遍历构造方法中给出的目录,会获取目录中全部的文件/文件夹,把文件/文件夹封装为File对象,多个File对象存储到File数组中
     */
    private static void show02() {
        File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion");
        File[] files = file.listFiles();
        for (File f : files) {
            System.out.println(f);
        }
    }

    /*
        public String[] list() :返回一个String数组,表示该File目录中的全部子文件或目录。
        遍历构造方法中给出的目录,会获取目录中全部文件/文件夹的名称,把获取到的多个名称存储到一个String类型的数组中
     */
    private static void show01() {
        //File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion\\1.txt");//NullPointerException
        //File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_Fi");//NullPointerException
        File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion");
        String[] arr = file.list();
        for (String fileName : arr) {
            System.out.println(fileName);
        }
    }
}

递归

package com.itheima.demo02.Recursion;
/*
    递归:方法本身调用本身
    - 递归的分类:
      - 递归分为两种,直接递归和间接递归。
      - 直接递归称为方法自身调用本身。
      - 间接递归能够A方法调用B方法,B方法调用C方法,C方法调用A方法。
    - 注意事项:
      - 递归必定要有条件限定,保证递归可以中止下来,不然会发生栈内存溢出。
      - 在递归中虽然有限定条件,可是递归次数不能太多。不然也会发生栈内存溢出。
      - 构造方法,禁止递归
    递归的使用前提:
        当调用方法的时候,方法的主体不变,每次调用方法的参数不一样,可使用递归
 */
public class Demo01Recurison {
    public static void main(String[] args) {
        //a();
        b(1);
    }

    /*
        构造方法,禁止递归
            编译报错:构造方法是建立对象使用的,一直递归会致使内存中有无数多个对象,直接编译报错
     */
    public Demo01Recurison() {
        //Demo01Recurison();
    }

    /*
            在递归中虽然有限定条件,可是递归次数不能太多。不然也会发生栈内存溢出。
            11157
                Exception in thread "main" java.lang.StackOverflowError
         */
    private static void b(int i) {
        System.out.println(i);
        if(i==20000){
            return; //结束方法
        }
        b(++i);
    }

    /*
        递归必定要有条件限定,保证递归可以中止下来,不然会发生栈内存溢出。
        Exception in thread "main" java.lang.StackOverflowError
     */
    private static void a() {
        System.out.println("a方法!");
        a();
    }
}

package com.itheima.demo02.Recursion;
/*
    练习:
        使用递归计算1-n之间的和
 */
public class Demo02Recurison {
    public static void main(String[] args) {
        int s = sum(3);
        System.out.println(s);
    }

    /*
        定义一个方法,使用递归计算1-n之间的和
        1+2+3+...+n
        n+(n-1)+(n-2)+...+1
        已知:
            最大值:n
            最小值:1
        使用递归必须明确:
            1.递归的结束条件
                获取到1的时候结束
            2.递归的目的
                获取下一个被加的数字(n-1)
     */
    public static int sum(int n){
        //获取到1的时候结束
        if(n==1){
            return 1;
        }

        //获取下一个被加的数字(n-1)
        return n + sum(n-1);
    }
}

package com.itheima.demo02.Recursion;

import java.io.File;

/*
    练习:
        递归打印多级目录
    需求:
        遍历c:\\abc文件夹,及abc文件夹的子文件夹
        只要.java结尾的文件
        c:\\abc
        c:\\abc\\abc.txt
        c:\\abc\\abc.java
        c:\\abc\\a
        c:\\abc\\a\\a.jpg
        c:\\abc\\a\\a.java
        c:\\abc\\b
        c:\\abc\\b\\b.java
        c:\\abc\\b\\b.txt
 */
public class Demo05Recurison {
    public static void main(String[] args) {
        File file = new File("c:\\abc");
        getAllFile(file);
    }

    /*
        定义一个方法,参数传递File类型的目录
        方法中对目录进行遍历
     */
    public static void getAllFile(File dir){
        //System.out.println(dir);//打印被遍历的目录名称
        File[] files = dir.listFiles();
        for (File f : files) {
            //对遍历获得的File对象f进行判断,判断是不是文件夹
            if(f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //咱们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
                //因此直接调用getAllFile方法便可:递归(本身调用本身)
                getAllFile(f);
            }else{
                //f是一个文件,直接打印便可
                /*
                    c:\\abc\\abc.java
                    只要.java结尾的文件
                    1.把File对象f,转为字符串对象
                 */
                //String name = f.getName();//abc.java
                //String path = f.getPath();//c:\\abc\\abc.java
                //String s = f.toString();//c:\\abc\\abc.java

                //把字符串,转换为小写
                //s = s.toLowerCase();

                //2.调用String类中的方法endsWith判断字符串是不是以.java结尾
                //boolean b = s.endsWith(".java");

                //3.若是是以.java结尾的文件,则输出
                /*if(b){
                    System.out.println(f);
                }*/

                if(f.getName().toLowerCase().endsWith(".java")){
                    System.out.println(f);
                }
            }
        }
    }
}

过滤器

原理:

package com.itheima.demo03Filter;

import java.io.File;

/*
    需求:
        遍历c:\\abc文件夹,及abc文件夹的子文件夹
        只要.java结尾的文件
        c:\\abc
        c:\\abc\\abc.txt
        c:\\abc\\abc.java
        c:\\abc\\a
        c:\\abc\\a\\a.jpg
        c:\\abc\\a\\a.java
        c:\\abc\\b
        c:\\abc\\b\\b.java
        c:\\abc\\b\\b.txt
    咱们可使用过滤器来实现
    在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器
    File[] listFiles(FileFilter filter)
    java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
        做用:用来过滤文件(File对象)
        抽象方法:用来过滤文件的方法
            boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中。
            参数:
                File pathname:使用ListFiles方法遍历目录,获得的每个文件对象
    File[] listFiles(FilenameFilter filter)
    java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
        做用:用于过滤文件名称
        抽象方法:用来过滤文件的方法
            boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中。
            参数:
                File dir:构造方法中传递的被遍历的目录
                String name:使用ListFiles方法遍历目录,获取的每个文件/文件夹的名称
    注意:
        两个过滤器接口是没有实现类的,须要咱们本身写实现类,重写过滤的方法accept,在方法中本身定义过滤的规则
 */
public class Demo01Filter {
    public static void main(String[] args) {
        File file = new File("c:\\abc");
        getAllFile(file);
    }

    /*
        定义一个方法,参数传递File类型的目录
        方法中对目录进行遍历
     */
    public static void getAllFile(File dir){
        File[] files = dir.listFiles(new FileFilterImpl());//传递过滤器对象
        for (File f : files) {
            //对遍历获得的File对象f进行判断,判断是不是文件夹
            if(f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //咱们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
                //因此直接调用getAllFile方法便可:递归(本身调用本身)
                getAllFile(f);
            }else{
                //f是一个文件,直接打印便可
                System.out.println(f);
            }
        }
    }
}

package com.itheima.demo03Filter;

import java.io.File;

/*
    需求:
        遍历c:\\abc文件夹,及abc文件夹的子文件夹
        只要.java结尾的文件
        c:\\abc
        c:\\abc\\abc.txt
        c:\\abc\\abc.java
        c:\\abc\\a
        c:\\abc\\a\\a.jpg
        c:\\abc\\a\\a.java
        c:\\abc\\b
        c:\\abc\\b\\b.java
        c:\\abc\\b\\b.txt
    咱们可使用过滤器来实现
    在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器
    File[] listFiles(FileFilter filter)
    java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
        做用:用来过滤文件(File对象)
        抽象方法:用来过滤文件的方法
            boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中。
            参数:
                File pathname:使用ListFiles方法遍历目录,获得的每个文件对象
    File[] listFiles(FilenameFilter filter)
    java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
        做用:用于过滤文件名称
        抽象方法:用来过滤文件的方法
            boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中。
            参数:
                File dir:构造方法中传递的被遍历的目录
                String name:使用ListFiles方法遍历目录,获取的每个文件/文件夹的名称
    注意:
        两个过滤器接口是没有实现类的,须要咱们本身写实现类,重写过滤的方法accept,在方法中本身定义过滤的规则
 */
public class Demo02Filter {
    public static void main(String[] args) {
        File file = new File("c:\\abc");
        getAllFile(file);
    }

    /*
        定义一个方法,参数传递File类型的目录
        方法中对目录进行遍历
     */
    public static void getAllFile(File dir){
        //传递过滤器对象 使用匿名内部类
        /*File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
                return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
            }
        });*/

        //使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
        /*File[] files = dir.listFiles((File pathname)->{
            return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
        });*/

        File[] files = dir.listFiles(pathname->pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java"));

        /*File[] files = dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
                return new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".java");
            }
        });*/

        //使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
        /*File[] files = dir.listFiles((File d, String name)->{
            //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
            return new File(d,name).isDirectory() || name.toLowerCase().endsWith(".java");
        });*/

        //File[] files = dir.listFiles((d,name)->new File(d,name).isDirectory() || name.toLowerCase().endsWith(".java"));

        for (File f : files) {
            //对遍历获得的File对象f进行判断,判断是不是文件夹
            if(f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //咱们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
                //因此直接调用getAllFile方法便可:递归(本身调用本身)
                getAllFile(f);
            }else{
                //f是一个文件,直接打印便可
                System.out.println(f);
            }
        }
    }
}

字节流

IO流的概念和分类

文件存储的原理和记事本打开文件的原理

package com.itheima.demo01.OutputStream;

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

/*
    java.io.OutputStream:字节输出流
        此抽象类是表示输出字节流的全部类的超类。

    定义了一些子类共性的成员方法:
        - public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
        - public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
        - public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
        - public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
        - public abstract void write(int b) :将指定的字节输出流。

    java.io.FileOutputStream extends OutputStream
    FileOutputStream:文件字节输出流
    做用:把内存中的数据写入到硬盘的文件中

    构造方法:
        FileOutputStream(String name)建立一个向具备指定名称的文件中写入数据的输出文件流。
        FileOutputStream(File file) 建立一个向指定 File 对象表示的文件中写入数据的文件输出流。
        参数:写入数据的目的
            String name:目的地是一个文件的路径
            File file:目的地是一个文件
        构造方法的做用:
            1.建立一个FileOutputStream对象
            2.会根据构造方法中传递的文件/文件路径,建立一个空的文件
            3.会把FileOutputStream对象指向建立好的文件

    写入数据的原理(内存-->硬盘)
        java程序-->JVM(java虚拟机)-->OS(操做系统)-->OS调用写数据的方法-->把数据写入到文件中

    字节输出流的使用步骤(重点):
        1.建立一个FileOutputStream对象,构造方法中传递写入数据的目的地
        2.调用FileOutputStream对象中的方法write,把数据写入到文件中
        3.释放资源(流使用会占用必定的内存,使用完毕要把内存清空,提供程序的效率)
 */
public class Demo01OutputStream {
    public static void main(String[] args) throws IOException {
        //1.建立一个FileOutputStream对象,构造方法中传递写入数据的目的地
        FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\a.txt");
        //2.调用FileOutputStream对象中的方法write,把数据写入到文件中
        //public abstract void write(int b) :将指定的字节输出流。
        fos.write(97);
        //3.释放资源(流使用会占用必定的内存,使用完毕要把内存清空,提供程序的效率)
        //fos.close();
    }
}

package com.itheima.demo01.OutputStream;

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

/*
    一次写多个字节的方法:
        - public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
        - public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
 */
public class Demo02OutputStream {
    public static void main(String[] args) throws IOException {
        //建立FileOutputStream对象,构造方法中绑定要写入数据的目的地
        FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\\b.txt"));
        //调用FileOutputStream对象中的方法write,把数据写入到文件中
        //在文件中显示100,写个字节
        fos.write(49);
        fos.write(48);
        fos.write(48);

        /*
            public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
            一次写多个字节:
                若是写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
                若是写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
         */
        byte[] bytes = {65,66,67,68,69};//ABCDE
        //byte[] bytes = {-65,-66,-67,68,69};//烤紻E
        fos.write(bytes);

        /*
            public void write(byte[] b, int off, int len) :把字节数组的一部分写入到文件中
                int off:数组的开始索引
                int len:写几个字节
         */
        fos.write(bytes,1,2);//BC

        /*
            写入字符的方法:可使用String类中的方法把字符串,转换为字节数组
                byte[] getBytes()  把字符串转换为字节数组
         */
        byte[] bytes2 = "你好".getBytes();
        System.out.println(Arrays.toString(bytes2));//[-28, -67, -96, -27, -91, -67]
        fos.write(bytes2);

        //释放资源
        fos.close();
    }
}

package com.itheima.demo01.OutputStream;

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

/*
    追加写/续写:使用两个参数的构造方法
        FileOutputStream(String name, boolean append)建立一个向具备指定 name 的文件中写入数据的输出文件流。
        FileOutputStream(File file, boolean append) 建立一个向指定 File 对象表示的文件中写入数据的文件输出流。
        参数:
           String name,File file:写入数据的目的地
           boolean append:追加写开关
            true:建立对象不会覆盖源文件,继续在文件的末尾追加写数据
            false:建立一个新文件,覆盖源文件
    写换行:写换行符号
        windows:\r\n
        linux:/n
        mac:/r
 */
public class Demo03OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\c.txt",true);
        for (int i = 1; i <=10 ; i++) {
            fos.write("你好".getBytes());
            fos.write("\r\n".getBytes());
        }

        fos.close();
    }
}

FileInputStream

package com.itheima.demo02.InputStream;

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

/*
    java.io.InputStream:字节输入流
    此抽象类是表示字节输入流的全部类的超类。

    定义了全部子类共性的方法:
         int read()从输入流中读取数据的下一个字节。
         int read(byte[] b) 从输入流中读取必定数量的字节,并将其存储在缓冲区数组 b 中。
         void close() 关闭此输入流并释放与该流关联的全部系统资源。

    java.io.FileInputStream extends InputStream
    FileInputStream:文件字节输入流
    做用:把硬盘文件中的数据,读取到内存中使用

    构造方法:
        FileInputStream(String name)
        FileInputStream(File file)
        参数:读取文件的数据源
            String name:文件的路径
            File file:文件
        构造方法的做用:
            1.会建立一个FileInputStream对象
            2.会把FileInputStream对象指定构造方法中要读取的文件

    读取数据的原理(硬盘-->内存)
        java程序-->JVM-->OS-->OS读取数据的方法-->读取文件

    字节输入流的使用步骤(重点):
        1.建立FileInputStream对象,构造方法中绑定要读取的数据源
        2.使用FileInputStream对象中的方法read,读取文件
        3.释放资源
 */
public class Demo01InputStream {
    public static void main(String[] args) throws IOException {
        //1.建立FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("09_IOAndProperties\\c.txt");
        //2.使用FileInputStream对象中的方法read,读取文件
        //int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
        /*int len = fis.read();
        System.out.println(len);//97 a

        len = fis.read();
        System.out.println(len);// 98 b

        len = fis.read();
        System.out.println(len);//99 c

        len = fis.read();
        System.out.println(len);//-1

        len = fis.read();
        System.out.println(len);//-1*/

        /*
            发现以上读取文件是一个重复的过程,因此可使用循环优化
            不知道文件中有多少字节,使用while循环
            while循环结束条件,读取到-1的时候结束

            布尔表达式(len = fis.read())!=-1
                1.fis.read():读取一个字节
                2.len = fis.read():把读取到的字节赋值给变量len
                3.(len = fis.read())!=-1:判断变量len是否不等于-1
         */
        int len = 0; //记录读取到的字节
        while((len = fis.read())!=-1){
            System.out.print(len);//abc
        }

        //3.释放资源
        fis.close();
    }
}

package com.itheima.demo02.InputStream;

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

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

    String类的构造方法
        String(byte[] bytes) :把字节数组转换为字符串
        String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
 */
public class Demo02InputStream {
    public static void main(String[] args) throws IOException {
        //建立FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("09_IOAndProperties\\b.txt");
        //使用FileInputStream对象中的方法read读取文件
        //int read(byte[] b) 从输入流中读取必定数量的字节,并将其存储在缓冲区数组 b 中。
        /*byte[] bytes = new byte[2];
        int len = fis.read(bytes);
        System.out.println(len);//2
        //System.out.println(Arrays.toString(bytes));//[65, 66]
        System.out.println(new String(bytes));//AB

        len = fis.read(bytes);
        System.out.println(len);//2
        System.out.println(new String(bytes));//CD

        len = fis.read(bytes);
        System.out.println(len);//1
        System.out.println(new String(bytes));//ED

        len = fis.read(bytes);
        System.out.println(len);//-1
        System.out.println(new String(bytes));//ED*/

        /*
            发现以上读取时一个重复的过程,可使用循环优化
            不知道文件中有多少字节,因此使用while循环
            while循环结束的条件,读取到-1结束
         */
        byte[] bytes = new byte[1024];//存储读取到的多个字节
        int len = 0; //记录每次读取的有效字节个数
        while((len = fis.read(bytes))!=-1){
            //String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
            System.out.println(new String(bytes,0,len));
        }

        //释放资源
        fis.close();
    }
}

文件复制

package com.itheima.demo03.CopyFile;

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

/*
    文件复制练习:一读一写

    明确:
        数据源: c:\\1.jpg
        数据的目的地: d:\\1.jpg

    文件复制的步骤:
        1.建立一个字节输入流对象,构造方法中绑定要读取的数据源
        2.建立一个字节输出流对象,构造方法中绑定要写入的目的地
        3.使用字节输入流对象中的方法read读取文件
        4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
        5.释放资源
 */
public class Demo01CopyFile {
    public static void main(String[] args) throws IOException {
        long s = System.currentTimeMillis();
        //1.建立一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        //2.建立一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("d:\\1.jpg");
        //一次读取一个字节写入一个字节的方式
        //3.使用字节输入流对象中的方法read读取文件
        /*int len = 0;
        while((len = fis.read())!=-1){
            //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
            fos.write(len);
        }*/

        //使用数组缓冲读取多个字节,写入多个字节
        byte[] bytes = new byte[1024];
        //3.使用字节输入流对象中的方法read读取文件
        int len = 0;//每次读取的有效字节个数
        while((len = fis.read(bytes))!=-1){
            //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
            fos.write(bytes,0,len);
        }

        //5.释放资源(先关写的,后关闭读的;若是写完了,确定读取完毕了)
        fos.close();
        fis.close();
        long e = System.currentTimeMillis();
        System.out.println("复制文件共耗时:"+(e-s)+"毫秒");
    }

}

字符流

FileReader

package com.itheima.Demo04.Reader;

import java.io.FileReader;
import java.io.IOException;

/*
    java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类

    共性的成员方法:
        int read() 读取单个字符并返回。
        int read(char[] cbuf)一次读取多个字符,将字符读入数组。
        void close() 关闭该流并释放与之关联的全部资源。

    java.io.FileReader extends InputStreamReader extends Reader
    FileReader:文件字符输入流
    做用:把硬盘文件中的数据以字符的方式读取到内存中

    构造方法:
        FileReader(String fileName)
        FileReader(File file)
        参数:读取文件的数据源
            String fileName:文件的路径
            File file:一个文件
        FileReader构造方法的做用:
            1.建立一个FileReader对象
            2.会把FileReader对象指向要读取的文件
    字符输入流的使用步骤:
        1.建立FileReader对象,构造方法中绑定要读取的数据源
        2.使用FileReader对象中的方法read读取文件
        3.释放资源
 */
public class Demo02Reader {
    public static void main(String[] args) throws IOException {
        //1.建立FileReader对象,构造方法中绑定要读取的数据源
        FileReader fr = new FileReader("09_IOAndProperties\\c.txt");
        //2.使用FileReader对象中的方法read读取文件
        //int read() 读取单个字符并返回。
        /*int len = 0;
        while((len = fr.read())!=-1){
            System.out.print((char)len);
        }*/

        //int read(char[] cbuf)一次读取多个字符,将字符读入数组。
        char[] cs = new char[1024];//存储读取到的多个字符
        int len = 0;//记录的是每次读取的有效字符个数
        while((len = fr.read(cs))!=-1){
            /*
                String类的构造方法
                String(char[] value) 把字符数组转换为字符串
                String(char[] value, int offset, int count) 把字符数组的一部分转换为字符串 offset数组的开始索引 count转换的个数
             */
            System.out.println(new String(cs,0,len));
        }

        //3.释放资源
        fr.close();
    }
}

FileWriter

package com.itheima.Demo05Writer;

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

/*
    java.io.Writer:字符输出流,是全部字符输出流的最顶层的父类,是一个抽象类

    共性的成员方法:
        - void write(int c) 写入单个字符。
        - void write(char[] cbuf)写入字符数组。
        - abstract  void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        - void write(String str)写入字符串。
        - void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        - void flush()刷新该流的缓冲。
        - void close() 关闭此流,但要先刷新它。

    java.io.FileWriter extends OutputStreamWriter extends Writer
    FileWriter:文件字符输出流
    做用:把内存中字符数据写入到文件中

    构造方法:
        FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象。
        FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
        参数:写入数据的目的地
            String fileName:文件的路径
            File file:是一个文件
        构造方法的做用:
            1.会建立一个FileWriter对象
            2.会根据构造方法中传递的文件/文件的路径,建立文件
            3.会把FileWriter对象指向建立好的文件

    字符输出流的使用步骤(重点):
        1.建立FileWriter对象,构造方法中绑定要写入数据的目的地
        2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
 */
public class Demo01Writer {
    public static void main(String[] args) throws IOException {
        //1.建立FileWriter对象,构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("09_IOAndProperties\\d.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符。
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        //fw.flush();
        //4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();
    }
}

package com.itheima.Demo05Writer;

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

/*
    flush方法和close方法的区别
        - flush :刷新缓冲区,流对象能够继续使用。
        - close:  先刷新缓冲区,而后通知系统释放资源。流对象不能够再被使用了。
 */
public class Demo02CloseAndFlush {
    public static void main(String[] args) throws IOException {
        //1.建立FileWriter对象,构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("09_IOAndProperties\\e.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符。
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        fw.flush();
        //刷新以后流能够继续使用
        fw.write(98);

        //4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();

        //close方法以后流已经关闭了,已经从内存中消失了,流就不能再使用了
        fw.write(99);//IOException: Stream closed
    }
}

package com.itheima.Demo05Writer;

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

/*
    字符输出流写数据的其余方法
        - void write(char[] cbuf)写入字符数组。
        - abstract  void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        - void write(String str)写入字符串。
        - void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
 */
public class Demo03Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("09_IOAndProperties\\f.txt");
        char[] cs = {'a','b','c','d','e'};
        //void write(char[] cbuf)写入字符数组。
        fw.write(cs);//abcde

        //void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        fw.write(cs,1,3);//bcd

        //void write(String str)写入字符串。
        fw.write("传智播客");//传智播客

        //void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        fw.write("黑马程序员",2,3);//程序员

        fw.close();
    }
}

package com.itheima.Demo05Writer;

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

/*
    续写和换行
    续写,追加写:使用两个参数的构造方法
        FileWriter(String fileName, boolean append)
        FileWriter(File file, boolean append)
        参数:
            String fileName,File file:写入数据的目的地
            boolean append:续写开关 true:不会建立新的文件覆盖源文件,能够续写; false:建立新的文件覆盖源文件
     换行:换行符号
        windows:\r\n
        linux:/n
        mac:/r
 */
public class Demo04Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("09_IOAndProperties\\g.txt",true);
        for (int i = 0; i <10 ; i++) {
            fw.write("HelloWorld"+i+"\r\n");
        }

        fw.close();
    }
}

处理流中的异常

package com.itheima.demo06.trycatch;

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

/*
    在jdk1.7以前使用try catch finally 处理流中的异常
    格式:
        try{
            可能会产出异常的代码
        }catch(异常类变量 变量名){
            异常的处理逻辑
        }finally{
            必定会指定的代码
            资源释放
        }
 */
public class Demo01TryCatch {
    public static void main(String[] args) {
        //提升变量fw的做用域,让finally可使用
        //变量在定义的时候,能够没有值,可是使用的时候必须有值
        //fw = new FileWriter("09_IOAndProperties\\g.txt",true); 执行失败,fw没有值,fw.close会报错
        FileWriter fw = null;
        try{
            //可能会产出异常的代码
            fw = new FileWriter("w:\\09_IOAndProperties\\g.txt",true);
            for (int i = 0; i <10 ; i++) {
                fw.write("HelloWorld"+i+"\r\n");
            }
        }catch(IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }finally {
            //必定会指定的代码
            //建立对象失败了,fw的默认值就是null,null是不能调用方法的,会抛出NullPointerException,须要增长一个判断,不是null在把资源释放
            if(fw!=null){
                try {
                    //fw.close方法声明抛出了IOException异常对象,因此咱们就的处理这个异常对象,要么throws,要么try catch
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

package com.itheima.demo06.trycatch;

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

/*
    JDK7的新特性
    在try的后边能够增长一个(),在括号中能够定义流对象
    那么这个流对象的做用域就在try中有效
    try中的代码执行完毕,会自动把流对象释放,不用写finally
    格式:
        try(定义流对象;定义流对象....){
            可能会产出异常的代码
        }catch(异常类变量 变量名){
            异常的处理逻辑
        }
 */
public class Demo02JDK7 {
    public static void main(String[] args) {
        try(//1.建立一个字节输入流对象,构造方法中绑定要读取的数据源
            FileInputStream fis = new FileInputStream("c:\\1.jpg");
            //2.建立一个字节输出流对象,构造方法中绑定要写入的目的地
            FileOutputStream fos = new FileOutputStream("d:\\1.jpg");){

            //可能会产出异常的代码
            //一次读取一个字节写入一个字节的方式
            //3.使用字节输入流对象中的方法read读取文件
            int len = 0;
            while((len = fis.read())!=-1){
                //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
                fos.write(len);
            }

        }catch (IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }


    }
}

package com.itheima.demo06.trycatch;

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

/*
    JDK9新特性
    try的前边能够定义流对象
    在try后边的()中能够直接引入流对象的名称(变量名)
    在try代码执行完毕以后,流对象也能够释放掉,不用写finally
    格式:
        A a = new A();
        B b = new B();
        try(a,b){
            可能会产出异常的代码
        }catch(异常类变量 变量名){
            异常的处理逻辑
        }
 */
public class Demo03JDK9 {
    public static void main(String[] args) throws IOException {
        //1.建立一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        //2.建立一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("d:\\1.jpg");

        try(fis;fos){
            //一次读取一个字节写入一个字节的方式
            //3.使用字节输入流对象中的方法read读取文件
            int len = 0;
            while((len = fis.read())!=-1){
                //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
                fos.write(len);
            }
        }catch (IOException e){
            System.out.println(e);
        }

        //fos.write(1);//Stream Closed

    }
}

Properties集合

package com.itheima.demo07.Prop;

import java.io.FileOutputStream;
import java.io.FileReader;
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 可保存在流中或从流中加载。
    Properties集合是一个惟一和IO流相结合的集合
        可使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        可使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用

    属性列表中每一个键及其对应值都是一个字符串。
        Properties集合是一个双列集合,key和value默认都是字符串
 */
public class Demo01Properties {
    public static void main(String[] args) throws IOException {
        show03();
    }

    /*
        可使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
        void load(InputStream inStream)
        void load(Reader reader)
        参数:
            InputStream inStream:字节输入流,不能读取含有中文的键值对
            Reader reader:字符输入流,能读取含有中文的键值对
        使用步骤:
            1.建立Properties集合对象
            2.使用Properties集合对象中的方法load读取保存键值对的文件
            3.遍历Properties集合
        注意:
            1.存储键值对的文件中,键与值默认的链接符号可使用=,空格(其余符号)
            2.存储键值对的文件中,可使用#进行注释,被注释的键值对不会再被读取
            3.存储键值对的文件中,键与值默认都是字符串,不用再加引号
     */
    private static void show03() throws IOException {
        //1.建立Properties集合对象
        Properties prop = new Properties();
        //2.使用Properties集合对象中的方法load读取保存键值对的文件
        prop.load(new FileReader("09_IOAndProperties\\prop.txt"));
        //prop.load(new FileInputStream("09_IOAndProperties\\prop.txt"));
        //3.遍历Properties集合
        Set<String> set = prop.stringPropertyNames();
        for (String key : set) {
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }

    /*
        可使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        void store(OutputStream out, String comments)
        void store(Writer writer, String comments)
        参数:
            OutputStream out:字节输出流,不能写入中文
            Writer writer:字符输出流,能够写中文
            String comments:注释,用来解释说明保存的文件是作什么用的
                    不能使用中文,会产生乱码,默认是Unicode编码
                    通常使用""空字符串

        使用步骤:
            1.建立Properties集合对象,添加数据
            2.建立字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
            3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
            4.释放资源
     */
    private static void show02() throws IOException {
        //1.建立Properties集合对象,添加数据
        Properties prop = new Properties();
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");

        //2.建立字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
        //FileWriter fw = new FileWriter("09_IOAndProperties\\prop.txt");

        //3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        //prop.store(fw,"save data");

        //4.释放资源
        //fw.close();

        prop.store(new FileOutputStream("09_IOAndProperties\\prop2.txt"),"");
    }

    /*
        使用Properties集合存储数据,遍历取出Properties集合中的数据
        Properties集合是一个双列集合,key和value默认都是字符串
        Properties集合有一些操做字符串的特有方法
            Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
            String getProperty(String key) 经过key找到value值,此方法至关于Map集合中的get(key)方法
            Set<String> stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,此方法至关于Map集合中的keySet方法
     */
    private static void show01() {
        //建立Properties集合对象
        Properties prop = new Properties();
        //使用setProperty往集合中添加数据
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");
        //prop.put(1,true);

        //使用stringPropertyNames把Properties集合中的键取出,存储到一个Set集合中
        Set<String> set = prop.stringPropertyNames();

        //遍历Set集合,取出Properties集合的每个键
        for (String key : set) {
            //使用getProperty方法经过key获取value
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }
}

缓冲流


package com.itheima.demo01.BufferedStream;

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

/*
    java.io.BufferedOutputStream extends OutputStream
    BufferedOutputStream:字节缓冲输出流

    继承自父类的共性成员方法:
        - public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
        - public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
        - public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
        - public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
        - public abstract void write(int b) :将指定的字节输出流。

     构造方法:
        BufferedOutputStream(OutputStream out)  建立一个新的缓冲输出流,以将数据写入指定的底层输出流。
        BufferedOutputStream(OutputStream out, int size)  建立一个新的缓冲输出流,以将具备指定缓冲区大小的数据写入指定的底层输出流。
        参数:
           OutputStream out:字节输出流
                咱们能够传递FileOutputStream,缓冲流会给FileOutputStream增长一个缓冲区,提升FileOutputStream的写入效率
           int size:指定缓冲流内部缓冲区的大小,不指定默认
     使用步骤(重点)
        1.建立FileOutputStream对象,构造方法中绑定要输出的目的地
        2.建立BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提升FileOutputStream对象效率
        3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
        4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
        5.释放资源(会先调用flush方法刷新数据,第4部能够省略)
 */
public class Demo01BufferedOutputStream {
    public static void main(String[] args) throws IOException {
        //1.建立FileOutputStream对象,构造方法中绑定要输出的目的地
        FileOutputStream fos = new FileOutputStream("10_IO\\a.txt");
        //2.建立BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提升FileOutputStream对象效率
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
        bos.write("我把数据写入到内部缓冲区中".getBytes());
        //4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
        bos.flush();
        //5.释放资源(会先调用flush方法刷新数据,第4部能够省略)
        bos.close();
    }

}

package com.itheima.demo01.BufferedStream;

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

/*
    java.io.BufferedInputStream extends InputStream
    BufferedInputStream:字节缓冲输入流

    继承自父类的成员方法:
        int read()从输入流中读取数据的下一个字节。
        int read(byte[] b) 从输入流中读取必定数量的字节,并将其存储在缓冲区数组 b 中。
        void close() 关闭此输入流并释放与该流关联的全部系统资源。

    构造方法:
        BufferedInputStream(InputStream in) 建立一个 BufferedInputStream 并保存其参数,即输入流 in,以便未来使用。
        BufferedInputStream(InputStream in, int size) 建立具备指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便未来使用。
        参数:
            InputStream in:字节输入流
                咱们能够传递FileInputStream,缓冲流会给FileInputStream增长一个缓冲区,提升FileInputStream的读取效率
            int size:指定缓冲流内部缓冲区的大小,不指定默认

    使用步骤(重点):
        1.建立FileInputStream对象,构造方法中绑定要读取的数据源
        2.建立BufferedInputStream对象,构造方法中传递FileInputStream对象,提升FileInputStream对象的读取效率
        3.使用BufferedInputStream对象中的方法read,读取文件
        4.释放资源
 */
public class Demo02BufferedInputStream {
    public static void main(String[] args) throws IOException {
        //1.建立FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("10_IO\\a.txt");
        //2.建立BufferedInputStream对象,构造方法中传递FileInputStream对象,提升FileInputStream对象的读取效率
        BufferedInputStream bis = new BufferedInputStream(fis);
        //3.使用BufferedInputStream对象中的方法read,读取文件
        //int read()从输入流中读取数据的下一个字节。
        /*int len = 0;//记录每次读取到的字节
        while((len = bis.read())!=-1){
            System.out.println(len);
        }*/

        //int read(byte[] b) 从输入流中读取必定数量的字节,并将其存储在缓冲区数组 b 中。
        byte[] bytes =new byte[1024];//存储每次读取的数据
        int len = 0; //记录每次读取的有效字节个数
        while((len = bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

        //4.释放资源
        bis.close();
    }
}

package com.itheima.demo01.BufferedStream;

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

/*
    练习:
        对文本的内容进行排序
        按照(1,2,3....)顺序排序
    分析:
        1.建立一个HashMap集合对象,能够:存储每行文本的序号(1,2,3,..);value:存储每行的文本
        2.建立字符缓冲输入流对象,构造方法中绑定字符输入流
        3.建立字符缓冲输出流对象,构造方法中绑定字符输出流
        4.使用字符缓冲输入流中的方法readline,逐行读取文本
        5.对读取到的文本进行切割,获取行中的序号和文本内容
        6.把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4..)
        7.遍历HashMap集合,获取每个键值对
        8.把每个键值对,拼接为一个文本行
        9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
        10.释放资源
 */
public class Demo05Test {
    public static void main(String[] args) throws IOException {
        //1.建立一个HashMap集合对象,能够:存储每行文本的序号(1,2,3,..);value:存储每行的文本
        HashMap<String,String> map = new HashMap<>();
        //2.建立字符缓冲输入流对象,构造方法中绑定字符输入流
        BufferedReader br = new BufferedReader(new FileReader("10_IO\\in.txt"));
        //3.建立字符缓冲输出流对象,构造方法中绑定字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("10_IO\\out.txt"));
        //4.使用字符缓冲输入流中的方法readline,逐行读取文本
        String line;
        while((line = br.readLine())!=null){
            //5.对读取到的文本进行切割,获取行中的序号和文本内容
            String[] arr = line.split("\\.");
            //6.把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4..)
            map.put(arr[0],arr[1]);
        }

        //7.遍历HashMap集合,获取每个键值对
        for(String key : map.keySet()){
            String value = map.get(key);
            //8.把每个键值对,拼接为一个文本行
            line = key + "." + value;
            //9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
            bw.write(line);
            bw.newLine();//写换行
        }
        //10.释放资源
        bw.close();
        br.close();
    }
}

转化流

字符编码

计算机中储存的信息都是用二进制数表示的,而咱们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换以后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。好比说,按照A规则存储,一样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会致使乱码现象。

编码:字符(能看懂的)--字节(看不懂的)

解码:字节(看不懂的)-->字符(能看懂的)

  • 字符编码Character Encoding : 就是一套天然语言的字符与二进制数之间的对应规则。

    编码表:生活中文字和计算机中二进制的对应规则

字符集

  • 字符集 Charset:也叫编码表。是一个系统支持的全部字符的集合,包括各国家文字、标点符号、图形符号、数字等。

计算机要准确的存储和识别各类字符集符号,须要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。

可见,当指定了编码,它所对应的字符集天然就指定了,因此编码才是咱们最终要关心的。

  • ASCII字符集
    • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
    • 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲经常使用字符。
  • ISO-8859-1字符集
    • 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
    • ISO-8859-1使用单字节编码,兼容ASCII编码。
  • GBxxx字符集
    • GB就是国标的意思,是为了显示中文而设计的一套字符集。
    • GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一块儿时,就表示一个汉字,这样大约能够组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里原本就有的数字、标点、字母都通通从新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号如下的那些就叫"半角"字符了。
    • GBK:最经常使用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,彻底兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
    • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每一个字能够由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
  • Unicode字符集
    • Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
    • 它最多使用4个字节的数字来表达每一个字母、符号,或者文字。有三种编码方案,UTF-八、UTF-16和UTF-32。最为经常使用的UTF-8编码。
    • UTF-8编码,能够用来表示Unicode标准中任何字符,它是电子邮件、网页及其余存储或传送文字的应用中,优先采用的编码。互联网工程工做小组(IETF)要求全部互联网协议都必须支持UTF-8编码。因此,咱们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每一个字符编码,编码规则:
      1. 128个US-ASCII字符,只需一个字节编码。
      2. 拉丁文等字符,须要二个字节编码。
      3. 大部分经常使用字(含中文),使用三个字节编码。
      4. 其余极少使用的Unicode辅助字符,使用四字节编码。

编码引出的问题

在IDEA中,使用FileReader 读取项目中的文本文件。因为IDEA的设置,都是默认的UTF-8编码,因此没有任何问题。可是,当读取Windows系统中建立的文本文件时,因为Windows系统的默认是GBK编码,就会出现乱码。

```java
public class ReaderDemo {
    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("E:\\File_GBK.txt");
        int read;
        while ((read = fileReader.read()) != -1) {
            System.out.print((char)read);
        }
        fileReader.close();
    }
}
输出结果:
���
```

转换流原理

OutputStreamWriter

package com.itheima.demo03.ReverseStream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

/*
    java.io.OutputStreamWriter extends Writer
    OutputStreamWriter: 是字符流通向字节流的桥梁:可以使用指定的 charset 将要写入流中的字符编码成字节。(编码:把能看懂的变成看不懂)

    继续自父类的共性成员方法:
        - void write(int c) 写入单个字符。
        - void write(char[] cbuf)写入字符数组。
        - abstract  void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        - void write(String str)写入字符串。
        - void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        - void flush()刷新该流的缓冲。
        - void close() 关闭此流,但要先刷新它。
    构造方法:
        OutputStreamWriter(OutputStream out)建立使用默认字符编码的 OutputStreamWriter。
        OutputStreamWriter(OutputStream out, String charsetName) 建立使用指定字符集的 OutputStreamWriter。
        参数:
            OutputStream out:字节输出流,能够用来写转换以后的字节到文件中
            String charsetName:指定的编码表名称,不区分大小写,能够是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8
    使用步骤:
        1.建立OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        4.释放资源
 */
public class Demo02OutputStreamWriter {
    public static void main(String[] args) throws IOException {
        //write_utf_8();
        write_gbk();
    }

    /*
       使用转换流OutputStreamWriter写GBK格式的文件
    */
    private static void write_gbk() throws IOException {
        //1.建立OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\gbk.txt"),"GBK");
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
        //4.释放资源
        osw.close();
    }

    /*
        使用转换流OutputStreamWriter写UTF-8格式的文件
     */
    private static void write_utf_8() throws IOException {
        //1.建立OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\utf_8.txt"),"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\utf_8.txt"));//不指定默认使用UTF-8
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
        //4.释放资源
        osw.close();
    }
}

InputStreamReader

package com.itheima.demo03.ReverseStream;

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

/*
    java.io.InputStreamReader extends Reader
    InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)

    继承自父类的共性成员方法:
        int read() 读取单个字符并返回。
        int read(char[] cbuf)一次读取多个字符,将字符读入数组。
        void close() 关闭该流并释放与之关联的全部资源。
    构造方法:
        InputStreamReader(InputStream in) 建立一个使用默认字符集的 InputStreamReader。
        InputStreamReader(InputStream in, String charsetName) 建立使用指定字符集的 InputStreamReader。
        参数:
            InputStream in:字节输入流,用来读取文件中保存的字节
            String charsetName:指定的编码表名称,不区分大小写,能够是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8
     使用步骤:
        1.建立InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        2.使用InputStreamReader对象中的方法read读取文件
        3.释放资源
     注意事项:
        构造方法中指定的编码表名称要和文件的编码相同,不然会发生乱码
 */
public class Demo03InputStreamReader {
    public static void main(String[] args) throws IOException {
        //read_utf_8();
        read_gbk();
    }


    /*
        使用InputStreamReader读取GBK格式的文件
     */
    private static void read_gbk() throws IOException {
        //1.建立InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        //InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\gbk.txt"),"UTF-8");//???
        InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\gbk.txt"),"GBK");//你好

        //2.使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while((len = isr.read())!=-1){
            System.out.println((char)len);
        }
        //3.释放资源
        isr.close();
    }

    /*
        使用InputStreamReader读取UTF-8格式的文件
     */
    private static void read_utf_8() throws IOException {
        //1.建立InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        //InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\utf_8.txt"),"UTF-8");
        InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\utf_8.txt"));//不指定默认使用UTF-8
        //2.使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while((len = isr.read())!=-1){
            System.out.println((char)len);
        }
        //3.释放资源
        isr.close();
    }
}

序列化流

序列化与反序列化

package com.itheima.demo04.ObjectStream;

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

/*
    java.io.ObjectOutputStream extends OutputStream
    ObjectOutputStream:对象的序列化流
    做用:把对象以流的方式写入到文件中保存

    构造方法:
        ObjectOutputStream(OutputStream out) 建立写入指定 OutputStream 的 ObjectOutputStream。
        参数:
            OutputStream out:字节输出流
    特有的成员方法:
        void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。

    使用步骤:
        1.建立ObjectOutputStream对象,构造方法中传递字节输出流
        2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
        3.释放资源
 */
public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        //1.建立ObjectOutputStream对象,构造方法中传递字节输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\person.txt"));
        //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
        oos.writeObject(new Person("小美女",18));
        //3.释放资源
        oos.close();
    }
}

package com.itheima.demo04.ObjectStream;

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

/*
    java.io.ObjectInputStream extends InputStream
    ObjectInputStream:对象的反序列化流
    做用:把文件中保存的对象,以流的方式读取出来使用

    构造方法:
        ObjectInputStream(InputStream in) 建立从指定 InputStream 读取的 ObjectInputStream。
        参数:
            InputStream in:字节输入流
    特有的成员方法:
        Object readObject() 从 ObjectInputStream 读取对象。

    使用步骤:
        1.建立ObjectInputStream对象,构造方法中传递字节输入流
        2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
        3.释放资源
        4.使用读取出来的对象(打印)

     readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
     当不存在对象的class文件时抛出此异常
     反序列化的前提:
        1.类必须实现Serializable
        2.必须存在类对应的class文件
 */
public class Demo02ObjectInputStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.建立ObjectInputStream对象,构造方法中传递字节输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\person.txt"));
        //2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
        Object o = ois.readObject();
        //3.释放资源
        ois.close();
        //4.使用读取出来的对象(打印)
        System.out.println(o);
        Person p = (Person)o;
        System.out.println(p.getName()+p.getAge());
    }

}

package com.itheima.demo04.ObjectStream;

import java.io.Serializable;

/*
    序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
    类经过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将没法使其任何状态序列化或反序列化。
    Serializable接口也叫标记型接口
        要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
        当咱们进行序列化和反序列化的时候,就会检测类上是否有这个标记
            有:就能够序列化和反序列化
            没有:就会抛出 NotSerializableException异常
    去市场买肉-->肉上有一个蓝色章(检测合格)-->放心购买-->买回来怎么吃随意


    static关键字:静态关键字
        静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
        被static修饰的成员变量不能被序列化的,序列化的都是对象
        private static int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}

    transient关键字:瞬态关键字
        被transient修饰成员变量,不能被序列化
        private transient int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}

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

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

package com.itheima.demo04.ObjectStream;

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

/*
    练习:序列化集合
        当咱们想在文件中保存多个对象的时候
        能够把多个对象存储到一个集合中
        对集合进序列化和反序列化
    分析:
        1.定义一个存储Person对象的ArrayList集合
        2.往ArrayList集合中存储Person对象
        3.建立一个序列化流ObjectOutputStream对象
        4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
        5.建立一个反序列化ObjectInputStream对象
        6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        7.把Object类型的集合转换为ArrayList类型
        8.遍历ArrayList集合
        9.释放资源
 */
public class Demo03Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.定义一个存储Person对象的ArrayList集合
        ArrayList<Person> list = new ArrayList<>();
        //2.往ArrayList集合中存储Person对象
        list.add(new Person("张三",18));
        list.add(new Person("李四",19));
        list.add(new Person("王五",20));
        //3.建立一个序列化流ObjectOutputStream对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\list.txt"));
        //4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
        oos.writeObject(list);
        //5.建立一个反序列化ObjectInputStream对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\list.txt"));
        //6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        Object o = ois.readObject();
        //7.把Object类型的集合转换为ArrayList类型
        ArrayList<Person> list2 = (ArrayList<Person>)o;
        //8.遍历ArrayList集合
        for (Person p : list2) {
            System.out.println(p);
        }
        //9.释放资源
        ois.close();
        oos.close();
    }
}

打印流

package com.itheima.demo05.PrintStream;

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

/*
    java.io.PrintStream:打印流
        PrintStream 为其余输出流添加了功能,使它们可以方便地打印各类数据值表示形式。
    PrintStream特色:
        1.只负责数据的输出,不负责数据的读取
        2.与其余输出流不一样,PrintStream 永远不会抛出 IOException
        3.有特有的方法,print,println
            void print(任意类型的值)
            void println(任意类型的值并换行)
    构造方法:
        PrintStream(File file):输出的目的地是一个文件
        PrintStream(OutputStream out):输出的目的地是一个字节输出流
        PrintStream(String fileName) :输出的目的地是一个文件路径
    PrintStream extends OutputStream
    继承自父类的成员方法:
        - public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
        - public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
        - public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
        - public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
        - public abstract void write(int b) :将指定的字节输出流。
    注意:
        若是使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        若是使用本身特有的方法print/println方法写数据,写的数据原样输出 97->97
 */
public class Demo01PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        //System.out.println("HelloWorld");

        //建立打印流PrintStream对象,构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("10_IO\\print.txt");
        //若是使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        ps.write(97);
        //若是使用本身特有的方法print/println方法写数据,写的数据原样输出 97->97
        ps.println(97);
        ps.println(8.8);
        ps.println('a');
        ps.println("HelloWorld");
        ps.println(true);

        //释放资源
        ps.close();
    }
}

package com.itheima.demo05.PrintStream;

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

/*
    能够改变输出语句的目的地(打印流的流向)
    输出语句,默认在控制台输出
    使用System.setOut方法改变输出语句的目的地改成参数中传递的打印流的目的地
        static void setOut(PrintStream out)
          从新分配“标准”输出流。
 */
public class Demo02PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("我是在控制台输出");

        PrintStream ps = new PrintStream("10_IO\\目的地是打印流.txt");
        System.setOut(ps);//把输出语句的目的地改变为打印流的目的地
        System.out.println("我在打印流的目的地中输出");

        ps.close();
    }
}

网络编程

网络通讯概述

协议分类

通讯的协议仍是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通讯细节。咱们能够直接使用这些类和接口,来专一于网络程序开发,而不用考虑通讯的细节。

java.net 包中提供了两种常见的网络协议的支持:

  • UDP:用户数据报协议(User Datagram Protocol)。UDP是无链接通讯协议,即在数据传输时,数据的发送端和接收端不创建逻辑链接。简单来讲,当一台计算机向另一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,一样接收端在收到数据时,也不会向发送端反馈是否收到数据。

    因为使用UDP协议消耗资源小,通讯效率高,因此一般都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,由于这种状况即便偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

    可是在使用UDP协议传送数据时,因为UDP的面向无链接性,不能保证数据的完整性,所以在传输重要数据时不建议使用UDP协议。UDP的交换过程以下图所示。

特色:数据被限制在64kb之内,超出这个范围就不能发送了。

数据报(Datagram):网络传输的基本单位

  • TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向链接的通讯协议,即传输数据以前,在发送端和接收端创建逻辑链接,而后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。

    在TCP链接中必需要明确客户端与服务器端,由客户端向服务端发出链接请求,每次链接的建立都须要通过“三次握手”。

    • 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证链接的可靠。
      • 第一次握手,客户端向服务器端发出链接请求,等待服务器确认。
      • 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了链接请求。
      • 第三次握手,客户端再次向服务器端发送确认信息,确认链接。整个交互过程以下图所示。

​ 完成三次握手,链接创建后,客户端和服务器就能够开始进行数据传输了。因为这种面向链接的特性,TCP协议能够保证传输数据的安全,因此应用十分普遍,例以下载文件、浏览网页等。

网络编程三要素

协议

  • 协议:计算机网络通讯必须遵照的规则,已经介绍过了,再也不赘述。

IP地址

  • IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备作惟一的编号。假如咱们把“我的电脑”比做“一台电话”的话,那么“IP地址”就至关于“电话号码”。

IP地址分类

  • IPv4:是一个32位的二进制数,一般被分为4个字节,表示成a.b.c.d 的形式,例如192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多能够表示42亿个。

  • IPv6:因为互联网的蓬勃发展,IP地址的需求量越来越大,可是网络地址资源有限,使得IP的分配愈加紧张。

    为了扩大地址空间,拟经过IPv6从新定义地址空间,采用128位地址长度,每16个字节一组,分红8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称能够为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。

经常使用命令

  • 查看本机IP地址,在控制台输入:
ipconfig
  • 检查网络是否连通,在控制台输入:
ping 空格 IP地址
ping 220.181.57.216

特殊的IP地址

  • 本机IP地址:127.0.0.1localhost

端口号

网络的通讯,本质上是两个进程(应用程序)的通讯。每台计算机都有不少的进程,那么在网络通讯时,如何区分这些进程呢?

若是说IP地址能够惟一标识网络中的设备,那么端口号就能够惟一标识设备中的进程(应用程序)了。

  • 端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序须要使用1024以上的端口号。若是端口号被另一个服务或应用所占用,会致使当前程序启动失败。

利用协议+IP地址+端口号 三元组合,就能够标识网络中的进程了,那么进程间的通讯就能够利用这个标识与其它进程进行交互。

TCP协议

package com.itheima.demo01.TCP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
    TCP通讯的客户端:向服务器发送链接请求,给服务器发送数据,读取服务器回写的数据
    表示客户端的类:
        java.net.Socket:此类实现客户端套接字(也能够就叫“套接字”)。套接字是两台机器间通讯的端点。
        套接字:包含了IP地址和端口号的网络单位

    构造方法:
        Socket(String host, int port) 建立一个流套接字并将其链接到指定主机上的指定端口号。
        参数:
            String host:服务器主机的名称/服务器的IP地址
            int port:服务器的端口号

    成员方法:
        OutputStream getOutputStream() 返回此套接字的输出流。
        InputStream getInputStream() 返回此套接字的输入流。
        void close() 关闭此套接字。

    实现步骤:
        1.建立一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
        6.释放资源(Socket)
     注意:
        1.客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用本身建立的流对象
        2.当咱们建立客户端对象Socket的时候,就会去请求服务器和服务器通过3次握手创建链接通路
            这时若是服务器没有启动,那么就会抛出异常ConnectException: Connection refused: connect
            若是服务器已经启动,那么就能够进行交互了
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.建立一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1",8888);
        //2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        os.write("你好服务器".getBytes());

        //4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();

        //5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));

        //6.释放资源(Socket)
        socket.close();

    }

}

package com.itheima.demo01.TCP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
    TCP通讯的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
    表示服务器的类:
        java.net.ServerSocket:此类实现服务器套接字。

    构造方法:
        ServerSocket(int port) 建立绑定到特定端口的服务器套接字。

    服务器端必须明确一件事情,必须的知道是哪一个客户端请求的服务器
    因此可使用accept方法获取到请求的客户端对象Socket
    成员方法:
        Socket accept() 侦听并接受到此套接字的链接。

    服务器的实现步骤:
        1.建立服务器ServerSocket对象和系统要指定的端口号
        2.使用ServerSocket对象中的方法accept,获取到请求的客户端对象Socket
        3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
        5.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
        7.释放资源(Socket,ServerSocket)
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.建立服务器ServerSocket对象和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        //2.使用ServerSocket对象中的方法accept,获取到请求的客户端对象Socket
        Socket socket = server.accept();
        //3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        //5.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
        os.write("收到谢谢".getBytes());
        //7.释放资源(Socket,ServerSocket)
        socket.close();
        server.close();
    }
}

综合案例_文件上传

package com.itheima.demo02.FileUpload;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
    文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器回写的数据

    明确:
        数据源:c:\\1.jpg
        目的地:服务器

    实现步骤:
        1.建立一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
        2.建立一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
        3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
        4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
        5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
        6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
        7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
        8.释放资源(FileInputStream,Socket)
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.建立一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        //2.建立一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1",8888);
        //3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
        int len = 0;
        byte[] bytes = new byte[1024];
        while((len = fis.read(bytes))!=-1){
            //5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
            os.write(bytes,0,len);
        }

        /*
            解决:上传完文件,给服务器写一个结束标记
            void shutdownOutput() 禁用此套接字的输出流。
            对于 TCP 套接字,任何之前写入的数据都将被发送,而且后跟 TCP 的正常链接终止序列。
         */
        socket.shutdownOutput();

        //6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();

        System.out.println("333333333333333333333");

        //7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
        while((len = is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

        System.out.println("444444444444444444  while死循环打印不到");

        //8.释放资源(FileInputStream,Socket)
        fis.close();
        socket.close();
    }
}

package com.itheima.demo02.FileUpload;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
    文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"

    明确:
        数据源:客户端上传的文件
        目的地:服务器的硬盘 d:\\upload\\1.jpg

    实现步骤:
        1.建立一个服务器ServerSocket对象,和系统要指定的端口号
        2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
        3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
        4.判断d:\\upload文件夹是否存在,不存在则建立
        5.建立一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
        6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
        7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
        8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
        9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
        10.释放资源(FileOutputStream,Socket,ServerSocket)
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.建立一个服务器ServerSocket对象,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        //2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
        Socket socket = server.accept();
        //3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //4.判断d:\\upload文件夹是否存在,不存在则建立
        File file =  new File("d:\\upload");
        if(!file.exists()){
            file.mkdirs();
        }


        //5.建立一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
        FileOutputStream fos = new FileOutputStream(file+"\\1.jpg");
        //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件

        System.out.println("11111111111111111111");

        int len =0;
        byte[] bytes = new byte[1024];
        while((len = is.read(bytes))!=-1){
            //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
            fos.write(bytes,0,len);
        }

        System.out.println("22222222222222222222222  while死循环打印不到");

        //8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
        //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
        socket.getOutputStream().write("上传成功".getBytes());
        //10.释放资源(FileOutputStream,Socket,ServerSocket)
        fos.close();
        socket.close();
        server.close();
    }
}

上传文件优化

package com.itheima.demo03.FileUpload;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
    文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器回写的数据

    明确:
        数据源:c:\\1.jpg
        目的地:服务器

    实现步骤:
        1.建立一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
        2.建立一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
        3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
        4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
        5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
        6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
        7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
        8.释放资源(FileInputStream,Socket)
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.建立一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        //2.建立一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1",8888);
        //3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
        int len = 0;
        byte[] bytes = new byte[1024];
        while((len = fis.read(bytes))!=-1){
            //5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
            os.write(bytes,0,len);
        }

        /*
            解决:上传完文件,给服务器写一个结束标记
            void shutdownOutput() 禁用此套接字的输出流。
            对于 TCP 套接字,任何之前写入的数据都将被发送,而且后跟 TCP 的正常链接终止序列。
         */
        socket.shutdownOutput();

        //6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();



        //7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
        while((len = is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }


        //8.释放资源(FileInputStream,Socket)
        fis.close();
        socket.close();
    }
}

package com.itheima.demo03.FileUpload;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

/*
    文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"

    明确:
        数据源:客户端上传的文件
        目的地:服务器的硬盘 d:\\upload\\1.jpg

    实现步骤:
        1.建立一个服务器ServerSocket对象,和系统要指定的端口号
        2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
        3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
        4.判断d:\\upload文件夹是否存在,不存在则建立
        5.建立一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
        6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
        7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
        8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
        9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
        10.释放资源(FileOutputStream,Socket,ServerSocket)
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.建立一个服务器ServerSocket对象,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        //2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象

        /*
            让服务器一直处于监听状态(死循环accept方法)
            有一个客户端上传文件,就保存一个文件
         */
        while(true){
            Socket socket = server.accept();

            /*
                使用多线程技术,提升程序的效率
                有一个客户端上传文件,就开启一个线程,完成文件的上传
             */
            new Thread(new Runnable() {
                //完成文件的上传
                @Override
                public void run() {
                   try {
                       //3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
                       InputStream is = socket.getInputStream();
                       //4.判断d:\\upload文件夹是否存在,不存在则建立
                       File file =  new File("d:\\upload");
                       if(!file.exists()){
                           file.mkdirs();
                       }

                    /*
                        自定义一个文件的命名规则:防止同名的文件被覆盖
                        规则:域名+毫秒值+随机数
                     */
                       String fileName = "itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

                       //5.建立一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
                       //FileOutputStream fos = new FileOutputStream(file+"\\1.jpg");
                       FileOutputStream fos = new FileOutputStream(file+"\\"+fileName);
                       //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件


                       int len =0;
                       byte[] bytes = new byte[1024];
                       while((len = is.read(bytes))!=-1){
                           //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
                           fos.write(bytes,0,len);
                       }


                       //8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
                       //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
                       socket.getOutputStream().write("上传成功".getBytes());
                       //10.释放资源(FileOutputStream,Socket,ServerSocket)
                       fos.close();
                       socket.close();
                   }catch (IOException e){
                       System.out.println(e);
                   }
                }
            }).start();


        }

        //服务器就不用关闭
        //server.close();
    }
}

Junit单元测试

  • 测试分类:
    1. 黑盒测试:不须要写代码,给输入值,看程序是否可以输出指望的值。
    2. 白盒测试:须要写代码的。关注程序具体的执行流程。

    • Junit使用:白盒测试
      • 步骤:
        1. 定义一个测试类(测试用例)
          • 建议:
            • 测试类名:被测试的类名Test CalculatorTest
            • 包名:xxx.xxx.xx.test cn.itcast.test
        2. 定义测试方法:能够独立运行
          • 建议:
            • 方法名:test测试的方法名 testAdd()
            • 返回值:void
            • 参数列表:空参
        3. 给方法加@Test
        4. 导入junit依赖环境
      • 断定结果:
        • 红色:失败
        • 绿色:成功
        • 通常咱们会使用断言操做来处理结果
          • Assert.assertEquals(指望的结果,运算的结果);
      • 补充:
        • @Before:
          • 修饰的方法会在测试方法以前被自动执行
        • @After:
          • 修饰的方法会在测试方法执行以后自动被执行

    package cn.itcast.junit;

    /**
    • 计算器类
      */
      public class Calculator {

      /**
      • 加法
      • @param a
      • @param b
      • @return
        */
        public int add (int a , int b){
        //int i = 3/0;

        return a - b;
        }

      /**
      • 减法
      • @param a
      • @param b
      • @return
        */
        public int sub (int a , int b){
        return a - b;
        }

    }

package cn.itcast.junit;

public class CalculatorTest {

    public static void main(String[] args) {

        //建立对象
        Calculator c = new Calculator();
        //调用
       /* int result = c.add(1, 2);
        System.out.println(result);*/

        int result = c.sub(1, 1);
        System.out.println(result);

        String str = "abc";
    }
}

package cn.itcast.test;

import cn.itcast.junit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CalculatorTest {
    /**
     * 初始化方法:
     *  用于资源申请,全部测试方法在执行以前都会先执行该方法
     */
    @Before
    public void init(){
        System.out.println("init...");
    }

    /**
     * 释放资源方法:
     *  在全部测试方法执行完后,都会自动执行该方法
     */
    @After
    public void close(){
        System.out.println("close...");
    }


    /**
     * 测试add方法
     */
    @Test
    public void testAdd(){
       // System.out.println("我被执行了");
        //1.建立计算器对象
        System.out.println("testAdd...");
        Calculator c  = new Calculator();
        //2.调用add方法
        int result = c.add(1, 2);
        //System.out.println(result);

        //3.断言  我断言这个结果是3
        Assert.assertEquals(3,result);

    }

    @Test
    public void testSub(){
        //1.建立计算器对象
        Calculator c  = new Calculator();
        int result = c.sub(1, 2);
        System.out.println("testSub....");
        Assert.assertEquals(-1,result);
    }
}

反射

在java的世界里,一切皆对象。其实从某种意义上说,在java中有两种对象:实例对象和Class对象。实例对象就是咱们日常定义的一个类的实例
而Class对象是没办法用new关键字获得的,由于它是jvm生成用来保存对应类的信息的,换句话说,当咱们定义好一个类文件并编译成.class字节码后,编译器同时为咱们建立了一个Class对象并将它保存.class文件中。我不知道这样描述是否稳当,由于我也见过某些书上直接把.class文件称之为Class对象。同时在jvm内部有一个类加载机制,即在须要的时候(懒加载)将.class文件和对应的Class对象加载到内存中。总之要有这样一个意识,Person.java文件编译成Person.class的同时也会产生一个对应的Class对象。

  • 框架:半成品软件。能够在框架的基础上进行软件开发,简化编码
    • 反射:将类的各个组成部分封装为其余对象,这就是反射机制
      • 好处:
        1. 能够在程序运行过程当中,操做这些对象。
        2. 能够解耦,提升程序的可扩展性。
    • 获取Class对象的方式:
      1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
        • 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
      2. 类名.class:经过类名的属性class获取
        • 多用于参数的传递
      3. 对象.getClass():getClass()方法在Object类中定义着。
        • 多用于对象的获取字节码的方式
      • 结论:
        同一个字节码文件(*.class)在一次程序运行过程当中,只会被加载一次,不论经过哪种方式获取的Class对象都是同一个。
    • Class对象功能:
      • 获取功能:
        1. 获取成员变量们
          • Field[] getFields() :获取全部public修饰的成员变量
          • Field getField(String name) 获取指定名称的 public修饰的成员变量

          • Field[] getDeclaredFields() 获取全部的成员变量,不考虑修饰符
          • Field getDeclaredField(String name)
        2. 获取构造方法们
          • Constructor<?>[] getConstructors()
          • Constructor getConstructor(类<?>... parameterTypes)

          • Constructor getDeclaredConstructor(类<?>... parameterTypes)
          • Constructor<?>[] getDeclaredConstructors()
        3. 获取成员方法们:
          • Method[] getMethods()
          • Method getMethod(String name, 类<?>... parameterTypes)

          • Method[] getDeclaredMethods()
          • Method getDeclaredMethod(String name, 类<?>... parameterTypes)

        4. 获取全类名
          • String getName()
    • Field:成员变量
      • 操做:
        1. 设置值
          • void set(Object obj, Object value)
        2. 获取值
          • get(Object obj)
        3. 忽略访问权限修饰符的安全检查
          • setAccessible(true):暴力反射

    package cn.itcast.domain;

    public class Person {
    private String name;
    private int age;

    public String a;
      protected String b;
      String c;
      private String d;
    
    
      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 +
                  ", a='" + a + '\'' +
                  ", b='" + b + '\'' +
                  ", c='" + c + '\'' +
                  ", d='" + d + '\'' +
                  '}';
      }
    
    
      public void eat(){
          System.out.println("eat...");
      }
    
      public void eat(String food){
          System.out.println("eat..."+food);
      }

    }

package cn.itcast.reflect;

import cn.itcast.domain.Person;
import cn.itcast.domain.Student;

public class ReflectDemo1 {


    /**
        获取Class对象的方式:
            1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
            2. 类名.class:经过类名的属性class获取
            3. 对象.getClass():getClass()方法在Object类中定义着。

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

        //1.Class.forName("全类名")
        Class cls1 = Class.forName("cn.itcast.domain.Person");
        System.out.println(cls1);
        //2.类名.class
        Class cls2 = Person.class;
        System.out.println(cls2);
        //3.对象.getClass()
        Person p = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);

        //== 比较三个对象
        System.out.println(cls1 == cls2);//true
        System.out.println(cls1 == cls3);//true


        Class c = Student.class;
        System.out.println(c == cls1);


    }
}

package cn.itcast.reflect;

import cn.itcast.domain.Person;

import java.lang.reflect.Field;

public class ReflectDemo2 {

    /**
     Class对象功能:
         * 获取功能:
         1. 获取成员变量们
             * Field[] getFields()
             * Field getField(String name)

             * Field[] getDeclaredFields()
             * Field getDeclaredField(String name)
         2. 获取构造方法们
             * Constructor<?>[] getConstructors()
             * Constructor<T> getConstructor(类<?>... parameterTypes)

             * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
             * Constructor<?>[] getDeclaredConstructors()
         3. 获取成员方法们:
             * Method[] getMethods()
             * Method getMethod(String name, 类<?>... parameterTypes)

             * Method[] getDeclaredMethods()
             * Method getDeclaredMethod(String name, 类<?>... parameterTypes)

         4. 获取类名
             * String getName()



     */

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

        //0.获取Person的Class对象
        Class personClass = Person.class;
        /*
             1. 获取成员变量们
                 * Field[] getFields()
                 * Field getField(String name)

                 * Field[] getDeclaredFields()
                 * Field getDeclaredField(String name)

         */
        //1.Field[] getFields()获取全部public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("------------");
        //2.Field getField(String name)
        Field a = personClass.getField("a");
        //获取成员变量a 的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println(value);
        //设置a的值
        a.set(p,"张三");
        System.out.println(p);

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

        //Field[] getDeclaredFields():获取全部的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        //Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println(value2);

    }

}

  • Constructor:构造方法
    • 建立对象:
      • T newInstance(Object... initargs)

      • 若是使用空参数构造方法建立对象,操做能够简化:Class对象的newInstance方法

  • Method:方法对象
    • 执行方法:
      • Object invoke(Object obj, Object... args)
    • 获取方法名称:
      • String getName:获取方法名

package cn.itcast.reflect;

import cn.itcast.domain.Person;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo3 {

    /**
     Class对象功能:
         * 获取功能:
         1. 获取成员变量们
             * Field[] getFields()
             * Field getField(String name)

             * Field[] getDeclaredFields()
             * Field getDeclaredField(String name)
         2. 获取构造方法们
             * Constructor<?>[] getConstructors()
             * Constructor<T> getConstructor(类<?>... parameterTypes)

             * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
             * Constructor<?>[] getDeclaredConstructors()
         3. 获取成员方法们:
             * Method[] getMethods()
             * Method getMethod(String name, 类<?>... parameterTypes)

             * Method[] getDeclaredMethods()
             * Method getDeclaredMethod(String name, 类<?>... parameterTypes)

         4. 获取类名
             * String getName()



     */

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

        //0.获取Person的Class对象
        Class personClass = Person.class;
        /*
            2. 获取构造方法们
                 * Constructor<?>[] getConstructors()
                 * Constructor<T> getConstructor(类<?>... parameterTypes)

                 * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
                 * Constructor<?>[] getDeclaredConstructors()
         */


        //Constructor<T> getConstructor(类<?>... parameterTypes)
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //建立对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);

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


        Constructor constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
        //建立对象
        Object person1 = constructor1.newInstance();
        System.out.println(person1);

        Object o = personClass.newInstance();
        System.out.println(o);


        //constructor1.setAccessible(true);
    }


}

package cn.itcast.reflect;

import cn.itcast.domain.Person;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo4 {

    /**
     Class对象功能:
         * 获取功能:
         1. 获取成员变量们
             * Field[] getFields()
             * Field getField(String name)

             * Field[] getDeclaredFields()
             * Field getDeclaredField(String name)
         2. 获取构造方法们
             * Constructor<?>[] getConstructors()
             * Constructor<T> getConstructor(类<?>... parameterTypes)

             * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
             * Constructor<?>[] getDeclaredConstructors()
         3. 获取成员方法们:
             * Method[] getMethods()
             * Method getMethod(String name, 类<?>... parameterTypes)

             * Method[] getDeclaredMethods()
             * Method getDeclaredMethod(String name, 类<?>... parameterTypes)

         4. 获取类名
             * String getName()



     */

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

        //0.获取Person的Class对象
        Class personClass = Person.class;
        /*
          3. 获取成员方法们:
             * Method[] getMethods()
             * Method getMethod(String name, 类<?>... parameterTypes)

             * Method[] getDeclaredMethods()
             * Method getDeclaredMethod(String name, 类<?>... parameterTypes)
         */
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //执行方法
        eat_method.invoke(p);


        Method eat_method2 = personClass.getMethod("eat", String.class);
        //执行方法
        eat_method2.invoke(p,"饭");

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

        //获取全部public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            String name = method.getName();
            System.out.println(name);
            //method.setAccessible(true);
        }

        //获取类名
        String className = personClass.getName();
        System.out.println(className);//cn.itcast.domain.Person

    }


}

  • 案例:
    • 需求:写一个"框架",不能改变该类的任何代码的前提下,能够帮咱们建立任意类的对象,而且执行其中任意方法
      • 实现:
        1. 配置文件
        2. 反射
      • 步骤:
        1. 将须要建立的对象的全类名和须要执行的方法定义在配置文件中
        2. 在程序中加载读取配置文件
        3. 使用反射技术来加载类文件进内存
        4. 建立对象
        5. 执行方法

注解:

* 概念:说明程序的。给计算机看的
* 注释:用文字描述程序的。给程序员看的

* 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及之后版本引入的一个特性,与类、接口、枚举是在同一个层次。它能够声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
* 概念描述:
    * JDK1.5以后的新特性
    * 说明程序的
    * 使用注解:@注解名称
    

* 做用分类:
    ①编写文档:经过代码里标识的注解生成文档【生成文档doc文档】
    ②代码分析:经过代码里标识的注解对代码进行分析【使用反射】
    ③编译检查:经过代码里标识的注解让编译器可以实现基本的编译检查【Override】


* JDK中预约义的一些注解
    * @Override :检测被该注解标注的方法是不是继承自父类(接口)的
    * @Deprecated:该注解标注的内容,表示已过期
    * @SuppressWarnings:压制警告
        * 通常传递参数all  @SuppressWarnings("all")

* 自定义注解
    * 格式:
        元注解
        public @interface 注解名称{
            属性列表;
        }

    * 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
        * public interface MyAnno extends java.lang.annotation.Annotation {}

    * 属性:接口中的抽象方法
        * 要求:
            1. 属性的返回值类型有下列取值
                * 基本数据类型
                * String
                * 枚举
                * 注解
                * 以上类型的数组

            2. 定义了属性,在使用时须要给属性赋值
                1. 若是定义属性时,使用default关键字给属性默认初始化值,则使用注解时,能够不进行属性的赋值。
                2. 若是只有一个属性须要赋值,而且属性的名称是value,则value能够省略,直接定义值便可。
                3. 数组赋值时,值使用{}包裹。若是数组中只有一个值,则{}能够省略
    
    * 元注解:用于描述注解的注解
        * @Target:描述注解可以做用的位置
            * ElementType取值:
                * TYPE:能够做用于类上
                * METHOD:能够做用于方法上
                * FIELD:能够做用于成员变量上
        * @Retention:描述注解被保留的阶段
            * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
        * @Documented:描述注解是否被抽取到api文档中
        * @Inherited:描述注解是否被子类继承


* 在程序使用(解析)注解:获取注解中定义的属性值
    1. 获取注解定义的位置的对象  (Class,Method,Field)
    2. 获取指定的注解
        * getAnnotation(Class)
        //其实就是在内存中生成了一个该注解接口的子类实现对象

                public class ProImpl implements Pro{
                    public String className(){
                        return "cn.itcast.annotation.Demo1";
                    }
                    public String methodName(){
                        return "show";
                    }
                }
    3. 调用注解中的抽象方法获取配置的属性值


* 案例:简单的测试框架
* 小结:
    1. 之后大多数时候,咱们会使用注解,而不是自定义注解
    2. 注解给谁用?
        1. 编译器
        2. 给解析程序用
    3. 注解不是程序的一部分,能够理解为注解就是一个标签

MySQL

数据库的基本概念

1. 数据库的英文单词: DataBase 简称 : DB
2. 什么数据库?
    * 用于存储和管理数据的仓库。

3. 数据库的特色:
    1. 持久化存储数据的。其实数据库就是一个文件系统
    2. 方便存储和管理数据
    3. 使用了统一的方式操做数据库 -- SQL


4. 常见的数据库软件
    * 参见《MySQL基础.pdf》

MySQL数据库软件

1. 安装
    * 参见《MySQL基础.pdf》
2. 卸载
    1. 去mysql的安装目录找到my.ini文件
        * 复制 datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
    2. 卸载MySQL
    3. 删除C:/ProgramData目录下的MySQL文件夹。
    
3. 配置
    * MySQL服务启动
        1. 手动。
        2. cmd--> services.msc 打开服务的窗口
        3. 使用管理员打开cmd
            * net start mysql : 启动mysql的服务
            * net stop mysql:关闭mysql服务
    * MySQL登陆
        1. mysql -uroot -p密码
        2. mysql -hip -uroot -p链接目标的密码
        3. mysql --host=ip --user=root --password=链接目标的密码
    * MySQL退出
        1. exit
        2. quit

    * MySQL目录结构
        1. MySQL安装目录:basedir="D:/develop/MySQL/"
            * 配置文件 my.ini
        2. MySQL数据目录:datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
            * 几个概念
                * 数据库:文件夹
                * 表:文件
                * 数据:数据

SQL

1.什么是SQL?
    Structured Query Language:结构化查询语言
    其实就是定义了操做全部关系型数据库的规则。每一种数据库操做的方式存在不同的地方,称为“方言”。
    
2.SQL通用语法
    1. SQL 语句能够单行或多行书写,以分号结尾。
    2. 可以使用空格和缩进来加强语句的可读性。
    3. MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写。
    4. 3 种注释
        * 单行注释: -- 注释内容 或 # 注释内容(mysql 特有) 
        * 多行注释: /* 注释 */
    
3. SQL分类
    1) DDL(Data Definition Language)数据定义语言
        用来定义数据库对象:数据库,表,列等。关键字:create, drop,alter 等
    2) DML(Data Manipulation Language)数据操做语言
        用来对数据库中表的数据进行增删改。关键字:insert, delete, update 等
    3) DQL(Data Query Language)数据查询语言
        用来查询数据库中表的记录(数据)。关键字:select, where 等
    4) DCL(Data Control Language)数据控制语言(了解)
        用来定义数据库的访问权限和安全级别,及建立用户。关键字:GRANT, REVOKE 等

DDL:操做数据库、表

1. 操做数据库:CRUD
    1. C(Create):建立
        * 建立数据库:
            * create database 数据库名称;
        * 建立数据库,判断不存在,再建立:
            * create database if not exists 数据库名称;
        * 建立数据库,并指定字符集
            * create database 数据库名称 character set 字符集名;

        * 练习: 建立db4数据库,判断是否存在,并制定字符集为gbk
            * create database if not exists db4 character set gbk;
    2. R(Retrieve):查询
        * 查询全部数据库的名称:
            * show databases;
        * 查询某个数据库的字符集:查询某个数据库的建立语句
            * show create database 数据库名称;
    3. U(Update):修改
        * 修改数据库的字符集
            * alter database 数据库名称 character set 字符集名称;
    4. D(Delete):删除
        * 删除数据库
            * drop database 数据库名称;
        * 判断数据库存在,存在再删除
            * drop database if exists 数据库名称;
    5. 使用数据库
        * 查询当前正在使用的数据库名称
            * select database();
        * 使用数据库
            * use 数据库名称;


2. 操做表
    1. C(Create):建立
        1. 语法:
            create table 表名(
                列名1 数据类型1,
                列名2 数据类型2,
                ....
                列名n 数据类型n
            );
            * 注意:最后一列,不须要加逗号(,)
            * 数据库类型:
                1. int:整数类型
                    * age int,
                2. double:小数类型
                    * score double(5,2)
                3. date:日期,只包含年月日,yyyy-MM-dd
                4. datetime:日期,包含年月日时分秒  yyyy-MM-dd HH:mm:ss
                5. timestamp:时间错类型  包含年月日时分秒     yyyy-MM-dd HH:mm:ss    
                    * 若是未来不给这个字段赋值,或赋值为null,则默认使用当前的系统时间,来自动赋值

                6. varchar:字符串
                    * name varchar(20):姓名最大20个字符
                    * zhangsan 8个字符  张三 2个字符
            

        * 建立表
            create table student(
                id int,
                name varchar(32),
                age int ,
                score double(4,1),
                birthday date,
                insert_time timestamp
            );
        * 复制表:
            * create table 表名 like 被复制的表名;      
    2. R(Retrieve):查询
        * 查询某个数据库中全部的表名称
            * show tables;
        * 查询表结构
            * desc 表名;
    3. U(Update):修改
        1. 修改表名
            alter table 表名 rename to 新的表名;
        2. 修改表的字符集
            alter table 表名 character set 字符集名称;
        3. 添加一列
            alter table 表名 add 列名 数据类型;
        4. 修改列名称 类型
            alter table 表名 change 列名 新列别 新数据类型;
            alter table 表名 modify 列名 新数据类型;
        5. 删除列
            alter table 表名 drop 列名;
    4. D(Delete):删除
        * drop table 表名;
        * drop table  if exists 表名 ;
  • 客户端图形化工具:SQLYog

DML:增删改表中数据

1. 添加数据:
    * 语法:
        * insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n);
    * 注意:
        1. 列名和值要一一对应。
        2. 若是表名后,不定义列名,则默认给全部列添加值
            insert into 表名 values(值1,值2,...值n);
        3. 除了数字类型,其余类型须要使用引号(单双均可以)引发来
2. 删除数据:
    * 语法:
        * delete from 表名 [where 条件]
    * 注意:
        1. 若是不加条件,则删除表中全部记录。
        2. 若是要删除全部记录
            1. delete from 表名; -- 不推荐使用。有多少条记录就会执行多少次删除操做
            2. TRUNCATE TABLE 表名; -- 推荐使用,效率更高 先删除表,而后再建立一张同样的表。
3. 修改数据:
    * 语法:
        * update 表名 set 列名1 = 值1, 列名2 = 值2,... [where 条件];

    * 注意:
        1. 若是不加任何条件,则会将表中全部记录所有修改。

DQL:查询表中的记录

* select * from 表名;

1. 语法:
    select
        字段列表
    from
        表名列表
    where
        条件列表
    group by
        分组字段
    having
        分组以后的条件
    order by
        排序
    limit
        分页限定


2. 基础查询
    1. 多个字段的查询
        select 字段名1,字段名2... from 表名;
        * 注意:
            * 若是查询全部字段,则可使用*来替代字段列表。
    2. 去除重复:
        * distinct
    3. 计算列
        * 通常可使用四则运算计算一些列的值。(通常只会进行数值型的计算)
        * ifnull(表达式1,表达式2):null参与的运算,计算结果都为null
            * 表达式1:哪一个字段须要判断是否为null
            * 若是该字段为null后的替换值。
    4. 起别名:
        * as:as也能够省略
        

3. 条件查询
    1. where子句后跟条件
    2. 运算符
        * > 、< 、<= 、>= 、= 、<>
        * BETWEEN...AND  
        * IN( 集合) 
        * LIKE:模糊查询
            * 占位符:
                * _:单个任意字符
                * %:多个任意字符
        * IS NULL  
        * and  或 &&
        * or  或 || 
        * not  或 !
        
            -- 查询年龄大于20岁

            SELECT * FROM student WHERE age > 20;
            
            SELECT * FROM student WHERE age >= 20;
            
            -- 查询年龄等于20岁
            SELECT * FROM student WHERE age = 20;
            
            -- 查询年龄不等于20岁
            SELECT * FROM student WHERE age != 20;
            SELECT * FROM student WHERE age <> 20;
            
            -- 查询年龄大于等于20 小于等于30
            
            SELECT * FROM student WHERE age >= 20 &&  age <=30;
            SELECT * FROM student WHERE age >= 20 AND  age <=30;
            SELECT * FROM student WHERE age BETWEEN 20 AND 30;
            
            -- 查询年龄22岁,18岁,25岁的信息
            SELECT * FROM student WHERE age = 22 OR age = 18 OR age = 25
            SELECT * FROM student WHERE age IN (22,18,25);
            
            -- 查询英语成绩为null
            SELECT * FROM student WHERE english = NULL; -- 不对的。null值不能使用 = (!=) 判断
            
            SELECT * FROM student WHERE english IS NULL;
            
            -- 查询英语成绩不为null
            SELECT * FROM student WHERE english  IS NOT NULL;



            -- 查询姓马的有哪些? like
            SELECT * FROM student WHERE NAME LIKE '马%';
            -- 查询姓名第二个字是化的人
            
            SELECT * FROM student WHERE NAME LIKE "_化%";
            
            -- 查询姓名是3个字的人
            SELECT * FROM student WHERE NAME LIKE '___';
            
            
            -- 查询姓名中包含德的人
            SELECT * FROM student WHERE NAME LIKE '%德%';

DQL:查询语句

1. 排序查询
    * 语法:order by 子句
        * order by 排序字段1 排序方式1 ,  排序字段2 排序方式2...

    * 排序方式:
        * ASC:升序,默认的。
        * DESC:降序。

    * 注意:
        * 若是有多个排序条件,则当前边的条件值同样时,才会判断第二条件。


2. 聚合函数:将一列数据做为一个总体,进行纵向的计算。
    1. count:计算个数
        1. 通常选择非空的列:主键
        2. count(*)
    2. max:计算最大值
    3. min:计算最小值
    4. sum:计算和
    5. avg:计算平均值
    

    * 注意:聚合函数的计算,排除null值。
        解决方案:
            1. 选择不包含非空的列进行计算
            2. IFNULL函数

3. 分组查询:
    1. 语法:group by 分组字段;
    2. 注意:
        1. 分组以后查询的字段:分组字段、聚合函数
        2. where 和 having 的区别?
            1. where 在分组以前进行限定,若是不知足条件,则不参与分组。having在分组以后进行限定,若是不知足结果,则不会被查询出来
            2. where 后不能够跟聚合函数,having能够进行聚合函数的判断。

        -- 按照性别分组。分别查询男、女同窗的平均分

        SELECT sex , AVG(math) FROM student GROUP BY sex;
        
        -- 按照性别分组。分别查询男、女同窗的平均分,人数
        
        SELECT sex , AVG(math),COUNT(id) FROM student GROUP BY sex;
        
        --  按照性别分组。分别查询男、女同窗的平均分,人数 要求:分数低于70分的人,不参与分组
        SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex;
        
        --  按照性别分组。分别查询男、女同窗的平均分,人数 要求:分数低于70分的人,不参与分组,分组以后。人数要大于2我的
        SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;
        
        SELECT sex , AVG(math),COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2;



4. 分页查询
    1. 语法:limit 开始的索引,每页查询的条数;
    2. 公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
        -- 每页显示3条记录 

        SELECT * FROM student LIMIT 0,3; -- 第1页
        
        SELECT * FROM student LIMIT 3,3; -- 第2页
        
        SELECT * FROM student LIMIT 6,3; -- 第3页

    3. limit 是一个MySQL"方言"

约束

* 概念: 对表中的数据进行限定,保证数据的正确性、有效性和完整性。  
* 分类:
    1. 主键约束:primary key
    2. 非空约束:not null
    3. 惟一约束:unique
    4. 外键约束:foreign key

* 非空约束:not null,值不能为null
    1. 建立表时添加约束
        CREATE TABLE stu(
            id INT,
            NAME VARCHAR(20) NOT NULL -- name为非空
        );
    2. 建立表完后,添加非空约束
        ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;

    3. 删除name的非空约束
        ALTER TABLE stu MODIFY NAME VARCHAR(20);


* 惟一约束:unique,值不能重复
    1. 建立表时,添加惟一约束
        CREATE TABLE stu(
            id INT,
            phone_number VARCHAR(20) UNIQUE -- 添加了惟一约束
        
        );
        * 注意mysql中,惟一约束限定的列的值能够有多个null
    
    
    2. 删除惟一约束
    
        ALTER TABLE stu DROP INDEX phone_number;
    
    3. 在建立表后,添加惟一约束
        ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;

* 主键约束:primary key。
    1. 注意:
        1. 含义:非空且惟一
        2. 一张表只能有一个字段为主键
        3. 主键就是表中记录的惟一标识

    2. 在建立表时,添加主键约束
        create table stu(
            id int primary key,-- 给id添加主键约束
            name varchar(20)
        );

    3. 删除主键
        -- 错误 alter table stu modify id int ;
        ALTER TABLE stu DROP PRIMARY KEY;

    4. 建立完表后,添加主键
        ALTER TABLE stu MODIFY id INT PRIMARY KEY;

    5. 自动增加:
        1.  概念:若是某一列是数值类型的,使用 auto_increment 能够来完成值得自动增加

        2. 在建立表时,添加主键约束,而且完成主键自增加
        create table stu(
            id int primary key auto_increment,-- 给id添加主键约束
            name varchar(20)
        );


        3. 删除自动增加
        ALTER TABLE stu MODIFY id INT;
        4. 添加自动增加
        ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;


* 外键约束:foreign key,让表于表产生关系,从而保证数据的正确性。
    1. 在建立表时,能够添加外键
        * 语法:
            create table 表名(
                ....
                外键列
                constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
            );

    2. 删除外键
        ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

    3. 建立表以后,添加外键
        ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);
    
    
    4. 级联操做
        1. 添加级联操做
            语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名称 
                    FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE  ;
        2. 分类:
            1. 级联更新:ON UPDATE CASCADE 
            2. 级联删除:ON DELETE CASCADE

数据库的设计

1. 多表之间的关系
    1. 分类:
        1. 一对一(了解):
            * 如:人和身份证
            * 分析:一我的只有一个身份证,一个身份证只能对应一我的
        2. 一对多(多对一):
            * 如:部门和员工
            * 分析:一个部门有多个员工,一个员工只能对应一个部门
        3. 多对多:
            * 如:学生和课程
            * 分析:一个学生能够选择不少门课程,一个课程也能够被不少学生选择
    2. 实现关系:
        1. 一对多(多对一):
            * 如:部门和员工
            * 实现方式:在多的一方创建外键,指向一的一方的主键。
        2. 多对多:
            * 如:学生和课程
            * 实现方式:多对多关系实现须要借助第三张中间表。中间表至少包含两个字段,这两个字段做为第三张表的外键,分别指向两张表的主键
        3. 一对一(了解):
            * 如:人和身份证
            * 实现方式:一对一关系实现,能够在任意一方添加惟一外键指向另外一方的主键。

    3. 案例
        -- 建立旅游线路分类表 tab_category
        -- cid 旅游线路分类主键,自动增加
        -- cname 旅游线路分类名称非空,惟一,字符串 100
        CREATE TABLE tab_category (
            cid INT PRIMARY KEY AUTO_INCREMENT,
            cname VARCHAR(100) NOT NULL UNIQUE
        );
        
        -- 建立旅游线路表 tab_route
        /*
        rid 旅游线路主键,自动增加
        rname 旅游线路名称非空,惟一,字符串 100
        price 价格
        rdate 上架时间,日期类型
        cid 外键,所属分类
        */
        CREATE TABLE tab_route(
            rid INT PRIMARY KEY AUTO_INCREMENT,
            rname VARCHAR(100) NOT NULL UNIQUE,
            price DOUBLE,
            rdate DATE,
            cid INT,
            FOREIGN KEY (cid) REFERENCES tab_category(cid)
        );
        
        /*建立用户表 tab_user
        uid 用户主键,自增加
        username 用户名长度 100,惟一,非空
        password 密码长度 30,非空
        name 真实姓名长度 100
        birthday 生日
        sex 性别,定长字符串 1
        telephone 手机号,字符串 11
        email 邮箱,字符串长度 100
        */
        CREATE TABLE tab_user (
            uid INT PRIMARY KEY AUTO_INCREMENT,
            username VARCHAR(100) UNIQUE NOT NULL,
            PASSWORD VARCHAR(30) NOT NULL,
            NAME VARCHAR(100),
            birthday DATE,
            sex CHAR(1) DEFAULT '男',
            telephone VARCHAR(11),
            email VARCHAR(100)
        );
        
        /*
        建立收藏表 tab_favorite
        rid 旅游线路 id,外键
        date 收藏时间
        uid 用户 id,外键
        rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次
        */
        CREATE TABLE tab_favorite (
            rid INT, -- 线路id
            DATE DATETIME,
            uid INT, -- 用户id
            -- 建立复合主键
            PRIMARY KEY(rid,uid), -- 联合主键
            FOREIGN KEY (rid) REFERENCES tab_route(rid),
            FOREIGN KEY(uid) REFERENCES tab_user(uid)
        );


2. 数据库设计的范式
    * 概念:设计数据库时,须要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的全部范式要求

        设计关系数据库时,听从不一样的规范要求,设计出合理的关系型数据库,这些不一样的规范要求被称为不一样的范式,各类范式呈递次规范,越高的范式数据库冗余越小。
        目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。

    * 分类:
        1. 第一范式(1NF):每一列都是不可分割的原子数据项
        2. 第二范式(2NF):在1NF的基础上,非码属性必须彻底依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)
            * 几个概念:
                1. 函数依赖:A-->B,若是经过A属性(属性组)的值,能够肯定惟一B属性的值。则称B依赖于A
                    例如:学号-->姓名。  (学号,课程名称) --> 分数
                2. 彻底函数依赖:A-->B, 若是A是一个属性组,则B属性值得肯定须要依赖于A属性组中全部的属性值。
                    例如:(学号,课程名称) --> 分数
                3. 部分函数依赖:A-->B, 若是A是一个属性组,则B属性值得肯定只须要依赖于A属性组中某一些值便可。
                    例如:(学号,课程名称) -- > 姓名
                4. 传递函数依赖:A-->B, B -- >C . 若是经过A属性(属性组)的值,能够肯定惟一B属性的值,在经过B属性(属性组)的值能够肯定惟一C属性的值,则称 C 传递函数依赖于A
                    例如:学号-->系名,系名-->系主任
                5. 码:若是在一张表中,一个属性或属性组,被其余全部属性所彻底依赖,则称这个属性(属性组)为该表的码
                    例如:该表中码为:(学号,课程名称)
                    * 主属性:码属性组中的全部属性
                    * 非主属性:除过码属性组的属性
                    
        3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

数据库的备份和还原

1. 命令行:
    * 语法:
        * 备份: mysqldump -u用户名 -p密码 数据库名称 > 保存的路径
        * 还原:
            1. 登陆数据库
            2. 建立数据库
            3. 使用数据库
            4. 执行文件。source 文件路径
2. 图形化工具:

多表查询:

* 查询语法:
    select
        列名列表
    from
        表名列表
    where....
* 准备sql
    # 建立部门表
    CREATE TABLE dept(
        id INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(20)
    );
    INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
    # 建立员工表
    CREATE TABLE emp (
        id INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(10),
        gender CHAR(1), -- 性别
        salary DOUBLE, -- 工资
        join_date DATE, -- 入职日期
        dept_id INT,
        FOREIGN KEY (dept_id) REFERENCES dept(id) -- 外键,关联部门表(部门表的主键)
    );
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);
* 笛卡尔积:
    * 有两个集合A,B .取这两个集合的全部组成状况。
    * 要完成多表查询,须要消除无用的数据
* 多表查询的分类:
    1. 内链接查询:
        1. 隐式内链接:使用where条件消除无用数据
            * 例子:
            -- 查询全部员工信息和对应的部门信息

            SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
            
            -- 查询员工表的名称,性别。部门表的名称
            SELECT emp.name,emp.gender,dept.name FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
            
            SELECT 
                t1.name, -- 员工表的姓名
                t1.gender,-- 员工表的性别
                t2.name -- 部门表的名称
            FROM
                emp t1,
                dept t2
            WHERE 
                t1.`dept_id` = t2.`id`;


        2. 显式内链接:
            * 语法: select 字段列表 from 表名1 [inner] join 表名2 on 条件
            * 例如:
                * SELECT * FROM emp INNER JOIN dept ON emp.`dept_id` = dept.`id`;   
                * SELECT * FROM emp JOIN dept ON emp.`dept_id` = dept.`id`; 

        3. 内链接查询:
            1. 从哪些表中查询数据
            2. 条件是什么
            3. 查询哪些字段
    2. 外连接查询:
        1. 左外链接:
            * 语法:select 字段列表 from 表1 left [outer] join 表2 on 条件;
            * 查询的是左表全部数据以及其交集部分。
            * 例子:
                -- 查询全部员工信息,若是员工有部门,则查询部门名称,没有部门,则不显示部门名称
                SELECT  t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
        2. 右外链接:
            * 语法:select 字段列表 from 表1 right [outer] join 表2 on 条件;
            * 查询的是右表全部数据以及其交集部分。
            * 例子:
                SELECT  * FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`;
    3. 子查询:
        * 概念:查询中嵌套查询,称嵌套查询为子查询。
            -- 查询工资最高的员工信息
            -- 1 查询最高的工资是多少 9000
            SELECT MAX(salary) FROM emp;
            
            -- 2 查询员工信息,而且工资等于9000的
            SELECT * FROM emp WHERE emp.`salary` = 9000;
            
            -- 一条sql就完成这个操做。子查询
            SELECT * FROM emp WHERE emp.`salary` = (SELECT MAX(salary) FROM emp);

        * 子查询不一样状况
            1. 子查询的结果是单行单列的:
                * 子查询能够做为条件,使用运算符去判断。 运算符: > >= < <= =
                * 
                -- 查询员工工资小于平均工资的人
                SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
            2. 子查询的结果是多行单列的:
                * 子查询能够做为条件,使用运算符in来判断
                -- 查询'财务部'和'市场部'全部的员工信息
                SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部';
                SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;
                -- 子查询
                SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部');

            3. 子查询的结果是多行多列的:
                * 子查询能够做为一张虚拟表参与查询
                -- 查询员工入职日期是2011-11-11日以后的员工信息和部门信息
                -- 子查询
                SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2
                WHERE t1.id = t2.dept_id;
                
                -- 普通内链接
                SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` >  '2011-11-11'

    * 多表查询练习

            -- 部门表
            CREATE TABLE dept (
              id INT PRIMARY KEY PRIMARY KEY, -- 部门id
              dname VARCHAR(50), -- 部门名称
              loc VARCHAR(50) -- 部门所在地
            );
            
            -- 添加4个部门
            INSERT INTO dept(id,dname,loc) VALUES 
            (10,'教研部','北京'),
            (20,'学工部','上海'),
            (30,'销售部','广州'),
            (40,'财务部','深圳');
            
            
            
            -- 职务表,职务名称,职务描述
            CREATE TABLE job (
              id INT PRIMARY KEY,
              jname VARCHAR(20),
              description VARCHAR(50)
            );
            
            -- 添加4个职务
            INSERT INTO job (id, jname, description) VALUES
            (1, '董事长', '管理整个公司,接单'),
            (2, '经理', '管理部门员工'),
            (3, '销售员', '向客人推销产品'),
            (4, '文员', '使用办公软件');
            
            
            
            -- 员工表
            CREATE TABLE emp (
              id INT PRIMARY KEY, -- 员工id
              ename VARCHAR(50), -- 员工姓名
              job_id INT, -- 职务id
              mgr INT , -- 上级领导
              joindate DATE, -- 入职日期
              salary DECIMAL(7,2), -- 工资
              bonus DECIMAL(7,2), -- 奖金
              dept_id INT, -- 所在部门编号
              CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
              CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
            );
            
            -- 添加员工
            INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
            (1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
            (1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
            (1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
            (1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
            (1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
            (1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
            (1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
            (1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
            (1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
            (1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
            (1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
            (1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
            (1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
            (1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);
            
            
            
            -- 工资等级表
            CREATE TABLE salarygrade (
              grade INT PRIMARY KEY,   -- 级别
              losalary INT,  -- 最低工资
              hisalary INT -- 最高工资
            );
            
            -- 添加5个工资等级
            INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
            (1,7000,12000),
            (2,12010,14000),
            (3,14010,20000),
            (4,20010,30000),
            (5,30010,99990);
            
            -- 需求:
            
            -- 1.查询全部员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
            /*
                分析:
                    1.员工编号,员工姓名,工资,须要查询emp表  职务名称,职务描述 须要查询job表
                    2.查询条件 emp.job_id = job.id
            
            */
            SELECT 
                t1.`id`, -- 员工编号
                t1.`ename`, -- 员工姓名
                t1.`salary`,-- 工资
                t2.`jname`, -- 职务名称
                t2.`description` -- 职务描述
            FROM 
                emp t1, job t2
            WHERE 
                t1.`job_id` = t2.`id`;
            
            
            
            -- 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
            /*
                分析:
                    1. 员工编号,员工姓名,工资 emp  职务名称,职务描述 job  部门名称,部门位置 dept
                    2. 条件: emp.job_id = job.id and emp.dept_id = dept.id
            */
            
            SELECT 
                t1.`id`, -- 员工编号
                t1.`ename`, -- 员工姓名
                t1.`salary`,-- 工资
                t2.`jname`, -- 职务名称
                t2.`description`, -- 职务描述
                t3.`dname`, -- 部门名称
                t3.`loc` -- 部门位置
            FROM 
                emp t1, job t2,dept t3
            WHERE 
                t1.`job_id` = t2.`id` AND t1.`dept_id` = t3.`id`;
               
            -- 3.查询员工姓名,工资,工资等级
            /*
                分析:
                    1.员工姓名,工资 emp  工资等级 salarygrade
                    2.条件 emp.salary >= salarygrade.losalary and emp.salary <= salarygrade.hisalary
                        emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary
            */
            SELECT 
                t1.ename ,
                t1.`salary`,
                t2.*
            FROM emp t1, salarygrade t2
            WHERE t1.`salary` BETWEEN t2.`losalary` AND t2.`hisalary`;
            
            
            
            -- 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
            /*
                分析:
                    1. 员工姓名,工资 emp , 职务名称,职务描述 job 部门名称,部门位置,dept  工资等级 salarygrade
                    2. 条件: emp.job_id = job.id and emp.dept_id = dept.id and emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary
                        
            */
            SELECT 
                t1.`ename`,
                t1.`salary`,
                t2.`jname`,
                t2.`description`,
                t3.`dname`,
                t3.`loc`,
                t4.`grade`
            FROM 
                emp t1,job t2,dept t3,salarygrade t4
            WHERE 
                t1.`job_id` = t2.`id` 
                AND t1.`dept_id` = t3.`id`
                AND t1.`salary` BETWEEN t4.`losalary` AND t4.`hisalary`;
            
            
            
            -- 5.查询出部门编号、部门名称、部门位置、部门人数
            
            /*
                分析:
                    1.部门编号、部门名称、部门位置 dept 表。 部门人数 emp表
                    2.使用分组查询。按照emp.dept_id完成分组,查询count(id)
                    3.使用子查询将第2步的查询结果和dept表进行关联查询
                    
            */
            SELECT 
                t1.`id`,t1.`dname`,t1.`loc` , t2.total
            FROM 
                dept t1,
                (SELECT
                    dept_id,COUNT(id) total
                FROM 
                    emp
                GROUP BY dept_id) t2
            WHERE t1.`id` = t2.dept_id;
            
            
            -- 6.查询全部员工的姓名及其直接上级的姓名,没有领导的员工也须要查询
            
            /*
                分析:
                    1.姓名 emp, 直接上级的姓名 emp
                        * emp表的id 和 mgr 是自关联
                    2.条件 emp.id = emp.mgr
                    3.查询左表的全部数据,和 交集数据
                        * 使用左外链接查询
                
            */
            /*
            select
                t1.ename,
                t1.mgr,
                t2.`id`,
                t2.ename
            from emp t1, emp t2
            where t1.mgr = t2.`id`;
            
            */
            
            SELECT 
                t1.ename,
                t1.mgr,
                t2.`id`,
                t2.`ename`
            FROM emp t1
            LEFT JOIN emp t2
            ON t1.`mgr` = t2.`id`;

事务

1. 事务的基本介绍
    1. 概念:
        *  若是一个包含多个步骤的业务操做,被事务管理,那么这些操做要么同时成功,要么同时失败。
        
    2. 操做:
        1. 开启事务: start transaction;
        2. 提交:commit;
        3. 回滚:rollback;
    3. 例子:
        CREATE TABLE account (
            id INT PRIMARY KEY AUTO_INCREMENT,
            NAME VARCHAR(10),
            balance DOUBLE
        );
        -- 添加数据
        INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000);
        
        
        SELECT * FROM account;
        UPDATE account SET balance = 1000;
        -- 张三给李四转帐 500 元
        
        -- 0. 开启事务
        START TRANSACTION;
        -- 1. 张三帐户 -500
        
        UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan';
        -- 2. 李四帐户 +500
        -- 出错了...
        UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
        
        -- 发现执行没有问题,提交事务
        COMMIT;
        
        -- 发现出问题了,回滚事务
        ROLLBACK;
    4. MySQL数据库中事务默认自动提交
        
        * 事务提交的两种方式:
            * 自动提交:
                * mysql就是自动提交的
                * 一条DML(增删改)语句会自动提交一次事务。
            * 手动提交:
                * Oracle 数据库默认是手动提交事务
                * 须要先开启事务,再提交
        * 修改事务的默认提交方式:
            * 查看事务的默认提交方式:SELECT @@autocommit; -- 1 表明自动提交  0 表明手动提交
            * 修改默认提交方式: set @@autocommit = 0;


2. 事务的四大特征:
    1. 原子性:是不可分割的最小操做单位,要么同时成功,要么同时失败。
    2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
    3. 隔离性:多个事务之间。相互独立。
    4. 一致性:事务操做先后,数据总量不变
3. 事务的隔离级别(了解)
    * 概念:多个事务之间隔离的,相互独立的。可是若是多个事务操做同一批数据,则会引起一些问题,设置不一样的隔离级别就能够解决这些问题。
    * 存在问题:
        1. 脏读:一个事务,读取到另外一个事务中没有提交的数据
        2. 不可重复读(虚读):在同一个事务中,两次读取到的数据不同。
        3. 幻读:一个事务操做(DML)数据表中全部记录,另外一个事务添加了一条数据,则第一个事务查询不到本身的修改。
    * 隔离级别:
        1. read uncommitted:读未提交
            * 产生的问题:脏读、不可重复读、幻读
        2. read committed:读已提交 (Oracle)
            * 产生的问题:不可重复读、幻读
        3. repeatable read:可重复读 (MySQL默认)
            * 产生的问题:幻读
        4. serializable:串行化
            * 能够解决全部的问题

        * 注意:隔离级别从小到大安全性愈来愈高,可是效率愈来愈低
        * 数据库查询隔离级别:
            * select @@tx_isolation;
        * 数据库设置隔离级别:
            * set global transaction isolation level  级别字符串;

    * 演示:
        set global transaction isolation level read uncommitted;
        start transaction;
        -- 转帐操做
        update account set balance = balance - 500 where id = 1;
        update account set balance = balance + 500 where id = 2;

DCL:

* SQL分类:
    1. DDL:操做数据库和表
    2. DML:增删改表中数据
    3. DQL:查询表中数据
    4. DCL:管理用户,受权

* DBA:数据库管理员

* DCL:管理用户,受权
    1. 管理用户
        1. 添加用户:
            * 语法:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
        2. 删除用户:
            * 语法:DROP USER '用户名'@'主机名';
        3. 修改用户密码:
            
            UPDATE USER SET PASSWORD = PASSWORD('新密码') WHERE USER = '用户名';
            UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'lisi';
            
            SET PASSWORD FOR '用户名'@'主机名' = PASSWORD('新密码');
            SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123');

            * mysql中忘记了root用户的密码?
                1. cmd -- > net stop mysql 中止mysql服务
                    * 须要管理员运行该cmd

                2. 使用无验证方式启动mysql服务: mysqld --skip-grant-tables
                3. 打开新的cmd窗口,直接输入mysql命令,敲回车。就能够登陆成功
                4. use mysql;
                5. update user set password = password('你的新密码') where user = 'root';
                6. 关闭两个窗口
                7. 打开任务管理器,手动结束mysqld.exe 的进程
                8. 启动mysql服务
                9. 使用新密码登陆。
        4. 查询用户:
            -- 1. 切换到mysql数据库
            USE myql;
            -- 2. 查询user表
            SELECT * FROM USER;
            
            * 通配符: % 表示能够在任意主机使用用户登陆数据库

    2. 权限管理:
        1. 查询权限:
            -- 查询权限
            SHOW GRANTS FOR '用户名'@'主机名';
            SHOW GRANTS FOR 'lisi'@'%';

        2. 授予权限:
            -- 授予权限
            grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';
            -- 给张三用户授予全部权限,在任意数据库任意表上
            
            GRANT ALL ON *.* TO 'zhangsan'@'localhost';
        3. 撤销权限:
            -- 撤销权限:
            revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';
            REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%';

JDBC

JDBC:

1. 概念:Java DataBase Connectivity  Java 数据库链接, Java语言操做数据库
    * JDBC本质:实际上是官方(sun公司)定义的一套操做全部关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。咱们可使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

2. 快速入门:
    * 步骤:
        1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
            1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
            2.右键-->Add As Library
        2. 注册驱动
        3. 获取数据库链接对象 Connection
        4. 定义sql
        5. 获取执行sql语句的对象 Statement
        6. 执行sql,接受返回结果
        7. 处理结果
        8. 释放资源

    * 代码实现:
        //1. 导入驱动jar包
        //2.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //3.获取数据库链接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
        //4.定义sql语句
        String sql = "update account set balance = 500 where id = 1";
        //5.获取执行sql的对象 Statement
        Statement stmt = conn.createStatement();
        //6.执行sql
        int count = stmt.executeUpdate(sql);
        //7.处理结果
        System.out.println(count);
        //8.释放资源
        stmt.close();
        conn.close();

3. 详解各个对象:
    1. DriverManager:驱动管理对象
        * 功能:
            1. 注册驱动:告诉程序该使用哪个数据库驱动jar
                static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。 
                写代码使用:  Class.forName("com.mysql.jdbc.Driver");
                经过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
                 static {
                        try {
                            java.sql.DriverManager.registerDriver(new Driver());
                        } catch (SQLException E) {
                            throw new RuntimeException("Can't register driver!");
                        }
                    }

                注意:mysql5以后的驱动jar包能够省略注册驱动的步骤。
            2. 获取数据库链接:
                * 方法:static Connection getConnection(String url, String user, String password) 
                * 参数:
                    * url:指定链接的路径
                        * 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
                        * 例子:jdbc:mysql://localhost:3306/db3
                        * 细节:若是链接的是本机mysql服务器,而且mysql服务默认端口是3306,则url能够简写为:jdbc:mysql:///数据库名称
                    * user:用户名
                    * password:密码 
    2. Connection:数据库链接对象
        1. 功能:
            1. 获取执行sql 的对象
                * Statement createStatement()
                * PreparedStatement prepareStatement(String sql)  
            2. 管理事务:
                * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
                * 提交事务:commit() 
                * 回滚事务:rollback() 
    3. Statement:执行sql的对象
        1. 执行sql
            1. boolean execute(String sql) :能够执行任意的sql 了解 
            2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
                * 返回值:影响的行数,能够经过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
            3. ResultSet executeQuery(String sql)  :执行DQL(select)语句
        2. 练习:
            1. account表 添加一条记录
            2. account表 修改记录
            3. account表 删除一条记录

            代码:
                Statement stmt = null;
                Connection conn = null;
                try {
                    //1. 注册驱动
                    Class.forName("com.mysql.jdbc.Driver");
                    //2. 定义sql
                    String sql = "insert into account values(null,'王五',3000)";
                    //3.获取Connection对象
                    conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
                    //4.获取执行sql的对象 Statement
                    stmt = conn.createStatement();
                    //5.执行sql
                    int count = stmt.executeUpdate(sql);//影响的行数
                    //6.处理结果
                    System.out.println(count);
                    if(count > 0){
                        System.out.println("添加成功!");
                    }else{
                        System.out.println("添加失败!");
                    }
        
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    //stmt.close();
                    //7. 释放资源
                    //避免空指针异常
                    if(stmt != null){
                        try {
                            stmt.close();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
        
                    if(conn != null){
                        try {
                            conn.close();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
                }
            
    4. ResultSet:结果集对象,封装查询结果
        * boolean next(): 游标向下移动一行,判断当前行是不是最后一行末尾(是否有数据),若是是,则返回false,若是不是则返回true
        * getXxx(参数):获取数据
            * Xxx:表明数据类型   如: int getInt() ,    String getString()
            * 参数:
                1. int:表明列的编号,从1开始   如: getString(1)
                2. String:表明列名称。 如: getDouble("balance")
        
        * 注意:
            * 使用步骤:
                1. 游标向下移动一行
                2. 判断是否有数据
                3. 获取数据

               //循环判断游标是不是最后一行末尾。
                while(rs.next()){
                    //获取数据
                    //6.2 获取数据
                    int id = rs.getInt(1);
                    String name = rs.getString("name");
                    double balance = rs.getDouble(3);
    
                    System.out.println(id + "---" + name + "---" + balance);
                }

        * 练习:
            * 定义一个方法,查询emp表的数据将其封装为对象,而后装载集合,返回。
                1. 定义Emp类
                2. 定义方法 public List<Emp> findAll(){}
                3. 实现方法 select * from emp;
                    
    5. PreparedStatement:执行sql的对象
        1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会形成安全性问题
            1. 输入用户随便,输入密码:a' or 'a' = 'a
            2. sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a' 

        2. 解决sql注入问题:使用PreparedStatement对象来解决
        3. 预编译的SQL:参数使用?做为占位符
        4. 步骤:
            1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
            2. 注册驱动
            3. 获取数据库链接对象 Connection
            4. 定义sql
                * 注意:sql的参数使用?做为占位符。 如:select * from user where username = ? and password = ?;
            5. 获取执行sql语句的对象 PreparedStatement  Connection.prepareStatement(String sql) 
            6. 给?赋值:
                * 方法: setXxx(参数1,参数2)
                    * 参数1:?的位置编号 从1 开始
                    * 参数2:?的值
            7. 执行sql,接受返回结果,不须要传递sql语句
            8. 处理结果
            9. 释放资源

        5. 注意:后期都会使用PreparedStatement来完成增删改查的全部操做
            1. 能够防止SQL注入
            2. 效率更高

抽取JDBC工具类 : JDBCUtils

* 目的:简化书写
* 分析:
    1. 注册驱动也抽取
    2. 抽取一个方法获取链接对象
        * 需求:不想传递参数(麻烦),还得保证工具类的通用性。
        * 解决:配置文件
            jdbc.properties
                url=
                user=
                password=


    3. 抽取一个方法释放资源

* 代码实现:
    public class JDBCUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;
    /**
     * 文件的读取,只须要读取一次便可拿到这些值。使用静态代码块
     */
    static{
        //读取资源文件,获取值。

        try {
            //1. 建立Properties集合类。
            Properties pro = new Properties();

            //获取src路径下的文件的方式--->ClassLoader 类加载器
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            URL res  = classLoader.getResource("jdbc.properties");
            String path = res.getPath();
            System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties
            //2. 加载文件
           // pro.load(new FileReader("D:\\IdeaProjects\\itcast\\day04_jdbc\\src\\jdbc.properties"));
            pro.load(new FileReader(path));

            //3. 获取数据,赋值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
            //4. 注册驱动
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取链接
     * @return 链接对象
     */
    public static Connection getConnection() throws SQLException {

        return DriverManager.getConnection(url, user, password);
    }

    /**
     * 释放资源
     * @param stmt
     * @param conn
     */
    public static void close(Statement stmt,Connection conn){
        if( stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if( conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 释放资源
     * @param stmt
     * @param conn
     */
    public static void close(ResultSet rs,Statement stmt, Connection conn){
        if( rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if( stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if( conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

* 练习:
    * 需求:
        1. 经过键盘录入用户名和密码
        2. 判断用户是否登陆成功
            * select * from user where username = "" and password = "";
            * 若是这个sql有查询结果,则成功,反之,则失败

    * 步骤:
        1. 建立数据库表 user
            CREATE TABLE USER(
                id INT PRIMARY KEY AUTO_INCREMENT,
                username VARCHAR(32),
                PASSWORD VARCHAR(32)
            
            );

            INSERT INTO USER VALUES(NULL,'zhangsan','123');
            INSERT INTO USER VALUES(NULL,'lisi','234');

        2. 代码实现:
            public class JDBCDemo9 {

                public static void main(String[] args) {
                    //1.键盘录入,接受用户名和密码
                    Scanner sc = new Scanner(System.in);
                    System.out.println("请输入用户名:");
                    String username = sc.nextLine();
                    System.out.println("请输入密码:");
                    String password = sc.nextLine();
                    //2.调用方法
                    boolean flag = new JDBCDemo9().login(username, password);
                    //3.判断结果,输出不一样语句
                    if(flag){
                        //登陆成功
                        System.out.println("登陆成功!");
                    }else{
                        System.out.println("用户名或密码错误!");
                    }
            
            
                }
            
            
            
                /**
                 * 登陆方法
                 */
                public boolean login(String username ,String password){
                    if(username == null || password == null){
                        return false;
                    }
                    //链接数据库判断是否登陆成功
                    Connection conn = null;
                    Statement stmt =  null;
                    ResultSet rs = null;
                    //1.获取链接
                    try {
                        conn =  JDBCUtils.getConnection();
                        //2.定义sql
                        String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
                        //3.获取执行sql的对象
                        stmt = conn.createStatement();
                        //4.执行查询
                        rs = stmt.executeQuery(sql);
                        //5.判断
                       /* if(rs.next()){//若是有下一行,则返回true
                            return true;
                        }else{
                            return false;
                        }*/
                       return rs.next();//若是有下一行,则返回true
            
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }finally {
                        JDBCUtils.close(rs,stmt,conn);
                    }
            
            
                    return false;
                }
            }

JDBC控制事务:

1. 事务:一个包含多个步骤的业务操做。若是这个业务操做被事务管理,则这多个步骤要么同时成功,要么同时失败。
2. 操做:
    1. 开启事务
    2. 提交事务
    3. 回滚事务
3. 使用Connection对象来管理事务
    * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
        * 在执行sql以前开启事务
    * 提交事务:commit() 
        * 当全部sql都执行完提交事务
    * 回滚事务:rollback() 
        * 在catch中回滚事务

4. 代码:
    public class JDBCDemo10 {

        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement pstmt1 = null;
            PreparedStatement pstmt2 = null;
    
            try {
                //1.获取链接
                conn = JDBCUtils.getConnection();
                //开启事务
                conn.setAutoCommit(false);
    
                //2.定义sql
                //2.1 张三 - 500
                String sql1 = "update account set balance = balance - ? where id = ?";
                //2.2 李四 + 500
                String sql2 = "update account set balance = balance + ? where id = ?";
                //3.获取执行sql对象
                pstmt1 = conn.prepareStatement(sql1);
                pstmt2 = conn.prepareStatement(sql2);
                //4. 设置参数
                pstmt1.setDouble(1,500);
                pstmt1.setInt(2,1);
    
                pstmt2.setDouble(1,500);
                pstmt2.setInt(2,2);
                //5.执行sql
                pstmt1.executeUpdate();
                // 手动制造异常
                int i = 3/0;
    
                pstmt2.executeUpdate();
                //提交事务
                conn.commit();
            } catch (Exception e) {
                //事务回滚
                try {
                    if(conn != null) {
                        conn.rollback();
                    }
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            }finally {
                JDBCUtils.close(pstmt1,conn);
                JDBCUtils.close(pstmt2,null);
            }
    
    
        }
    
    }

数据库链接池

1. 概念:其实就是一个容器(集合),存放数据库链接的容器。
        当系统初始化好后,容器被建立,容器中会申请一些链接对象,当用户来访问数据库时,从容器中获取链接对象,用户访问完以后,会将链接对象归还给容器。

2. 好处:
    1. 节约资源
    2. 用户访问高效

3. 实现:
    1. 标准接口:DataSource   javax.sql包下的
        1. 方法:
            * 获取链接:getConnection()
            * 归还链接:Connection.close()。若是链接对象Connection是从链接池中获取的,那么调用Connection.close()方法,则不会再关闭链接了。而是归还链接

    2. 通常咱们不去实现它,有数据库厂商来实现
        1. C3P0:数据库链接池技术
        2. Druid:数据库链接池实现技术,由阿里巴巴提供的


4. C3P0:数据库链接池技术
    * 步骤:
        1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,
            * 不要忘记导入数据库驱动jar包
        2. 定义配置文件:
            * 名称: c3p0.properties 或者 c3p0-config.xml
            * 路径:直接将文件放在src目录下便可。

        3. 建立核心对象 数据库链接池对象 ComboPooledDataSource
        4. 获取链接: getConnection
    * 代码:
         //1.建立数据库链接池对象
        DataSource ds  = new ComboPooledDataSource();
        //2. 获取链接对象
        Connection conn = ds.getConnection();
5. Druid:数据库链接池实现技术,由阿里巴巴提供的
    1. 步骤:
        1. 导入jar包 druid-1.0.9.jar
        2. 定义配置文件:
            * 是properties形式的
            * 能够叫任意名称,能够放在任意目录下
        3. 加载配置文件。Properties
        4. 获取数据库链接池对象:经过工厂来来获取  DruidDataSourceFactory
        5. 获取链接:getConnection
    * 代码:
         //3.加载配置文件
        Properties pro = new Properties();
        InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
        pro.load(is);
        //4.获取链接池对象
        DataSource ds = DruidDataSourceFactory.createDataSource(pro);
        //5.获取链接
        Connection conn = ds.getConnection();
    2. 定义工具类
        1. 定义一个类 JDBCUtils
        2. 提供静态代码块加载配置文件,初始化链接池对象
        3. 提供方法
            1. 获取链接方法:经过数据库链接池获取链接
            2. 释放资源
            3. 获取链接池的方法


    * 代码:
        public class JDBCUtils {

            //1.定义成员变量 DataSource
            private static DataSource ds ;
        
            static{
                try {
                    //1.加载配置文件
                    Properties pro = new Properties();
                    pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
                    //2.获取DataSource
                    ds = DruidDataSourceFactory.createDataSource(pro);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        
            /**
             * 获取链接
             */
            public static Connection getConnection() throws SQLException {
                return ds.getConnection();
            }
        
            /**
             * 释放资源
             */
            public static void close(Statement stmt,Connection conn){
               /* if(stmt != null){
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
                if(conn != null){
                    try {
                        conn.close();//归还链接
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }*/
        
               close(null,stmt,conn);
            }
        
        
            public static void close(ResultSet rs , Statement stmt, Connection conn){
        
        
                if(rs != null){
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
        
                if(stmt != null){
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
                if(conn != null){
                    try {
                        conn.close();//归还链接
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        
            /**
             * 获取链接池方法
             */
        
            public static DataSource getDataSource(){
                return  ds;
            }
        
        }

Spring JDBC

* Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
* 步骤:
    1. 导入jar包
    2. 建立JdbcTemplate对象。依赖于数据源DataSource
        * JdbcTemplate template = new JdbcTemplate(ds);

    3. 调用JdbcTemplate的方法来完成CRUD的操做
        * update():执行DML语句。增、删、改语句
        * queryForMap():查询结果将结果集封装为map集合,将列名做为key,将值做为value 将这条记录封装为一个map集合
            * 注意:这个方法查询的结果集长度只能是1
        * queryForList():查询结果将结果集封装为list集合
            * 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
        * query():查询结果,将结果封装为JavaBean对象
            * query的参数:RowMapper
                * 通常咱们使用BeanPropertyRowMapper实现类。能够完成数据到JavaBean的自动封装
                * new BeanPropertyRowMapper<类型>(类型.class)
        * queryForObject:查询结果,将结果封装为对象
            * 通常用于聚合函数的查询

    4. 练习:
        * 需求:
            1. 修改1号数据的 salary 为 10000
            2. 添加一条记录
            3. 删除刚才添加的记录
            4. 查询id为1的记录,将其封装为Map集合
            5. 查询全部记录,将其封装为List
            6. 查询全部记录,将其封装为Emp对象的List集合
            7. 查询总记录数

        * 代码:
            
            import cn.itcast.domain.Emp;
            import cn.itcast.utils.JDBCUtils;
            import org.junit.Test;
            import org.springframework.jdbc.core.BeanPropertyRowMapper;
            import org.springframework.jdbc.core.JdbcTemplate;
            import org.springframework.jdbc.core.RowMapper;
            
            import java.sql.Date;
            import java.sql.ResultSet;
            import java.sql.SQLException;
            import java.util.List;
            import java.util.Map;
            
            public class JdbcTemplateDemo2 {
            
                //Junit单元测试,可让方法独立执行
            
            
                //1. 获取JDBCTemplate对象
                private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
                /**
                 * 1. 修改1号数据的 salary 为 10000
                 */
                @Test
                public void test1(){
            
                    //2. 定义sql
                    String sql = "update emp set salary = 10000 where id = 1001";
                    //3. 执行sql
                    int count = template.update(sql);
                    System.out.println(count);
                }
            
                /**
                 * 2. 添加一条记录
                 */
                @Test
                public void test2(){
                    String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
                    int count = template.update(sql, 1015, "郭靖", 10);
                    System.out.println(count);
            
                }
            
                /**
                 * 3.删除刚才添加的记录
                 */
                @Test
                public void test3(){
                    String sql = "delete from emp where id = ?";
                    int count = template.update(sql, 1015);
                    System.out.println(count);
                }
            
                /**
                 * 4.查询id为1001的记录,将其封装为Map集合
                 * 注意:这个方法查询的结果集长度只能是1
                 */
                @Test
                public void test4(){
                    String sql = "select * from emp where id = ? or id = ?";
                    Map<String, Object> map = template.queryForMap(sql, 1001,1002);
                    System.out.println(map);
                    //{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
            
                }
            
                /**
                 * 5. 查询全部记录,将其封装为List
                 */
                @Test
                public void test5(){
                    String sql = "select * from emp";
                    List<Map<String, Object>> list = template.queryForList(sql);
            
                    for (Map<String, Object> stringObjectMap : list) {
                        System.out.println(stringObjectMap);
                    }
                }
            
                /**
                 * 6. 查询全部记录,将其封装为Emp对象的List集合
                 */
            
                @Test
                public void test6(){
                    String sql = "select * from emp";
                    List<Emp> list = template.query(sql, new RowMapper<Emp>() {
            
                        @Override
                        public Emp mapRow(ResultSet rs, int i) throws SQLException {
                            Emp emp = new Emp();
                            int id = rs.getInt("id");
                            String ename = rs.getString("ename");
                            int job_id = rs.getInt("job_id");
                            int mgr = rs.getInt("mgr");
                            Date joindate = rs.getDate("joindate");
                            double salary = rs.getDouble("salary");
                            double bonus = rs.getDouble("bonus");
                            int dept_id = rs.getInt("dept_id");
            
                            emp.setId(id);
                            emp.setEname(ename);
                            emp.setJob_id(job_id);
                            emp.setMgr(mgr);
                            emp.setJoindate(joindate);
                            emp.setSalary(salary);
                            emp.setBonus(bonus);
                            emp.setDept_id(dept_id);
            
                            return emp;
                        }
                    });
            
            
                    for (Emp emp : list) {
                        System.out.println(emp);
                    }
                }
            
                /**
                 * 6. 查询全部记录,将其封装为Emp对象的List集合
                 */
            
                @Test
                public void test6_2(){
                    String sql = "select * from emp";
                    List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
                    for (Emp emp : list) {
                        System.out.println(emp);
                    }
                }
            
                /**
                 * 7. 查询总记录数
                 */
            
                @Test
                public void test7(){
                    String sql = "select count(id) from emp";
                    Long total = template.queryForObject(sql, Long.class);
                    System.out.println(total);
                }
            
            }
相关文章
相关标签/搜索