Java基础(中)

Java基础(中)

1. 使用Junit来分段测试

package com.itheima.demo01_junit;


import org.junit.Test;

/*
    案例: 演示Junit的用法.

    Junit单元测试简介:
        概述:
            它属于白盒测试, 早期是给测试人员使用的, 但是非常好用, 目前很多开发人员也都在使用.
        作用:
            让代码不用放到main方法中, 也能执行.
        步骤:
            1. 在项目(或者模块)下新建一个文件夹, lib
            2. 把要用的jar包拷贝进来.
            3. 配置运行变量.
               选中jar, 右键 -> add as library
        注意事项(细节):

 */
public class UsersUtils {
    @Test
    public void add() {
        //...
        System.out.println("往 users 表中 添加数据.");
    }

    @Test
    public void delete() {
        //...
        System.out.println("从 users 表中 删除数据.");
    }
}

2. 内部类

package com.itheima.demo02_inner;

/*
    案例: 演示匿名内部类的用法.

    内部类简介:
        概述:
            类里边还有一个类, 里边那个叫内部类, 外边那个叫外部类.
        格式:
            详见类A
        分类:
            成员内部类:
               定义在成员位置的内部类, 一般是用来对类的功能做延伸的, 多用于底层源码.
            局部内部类:
               定义在局部位置的内部类, 我们用的最多的匿名内部类就是属于 局部内部类.

    匿名内部类:
        概述:
            指的是没有名字的局部内部类.
        格式:
            new 类名或者接口名() {
                //重写类中所有的抽象方法
            };
        本质:
            专业版: 匿名内部类就是一个继承了类, 或者实现了接口的子类的匿名对象.
            大白话: 匿名内部类的本质是一个 子类对象.
        使用场景:
            1. 当对成员方法仅调用一次的时候.
            2. 可以作为方法的实参进行传递.
 */
public class Demo01 {
    public static void main(String[] args) {
        //需求: 调用Animal#eat()方法.
        //方式1: 普通做法, 搞一个类继承Animal, 然后: 抽象类多态
        //an解释: 就是一个继承了Animal类的子类Cat类的对象, 子类名叫:Cat, 对象名叫: an
        Animal an = new Cat();
        an.eat();
        System.out.println("--------------------");

        //方式2: 匿名对象, 即: 没有名字的对象.
        //new Cat()解释: 就是一个继承了Animal类的子类Cat类的对象, 子类名叫:Cat, 对象名叫: 不知道
        new Cat().eat();
        System.out.println("--------------------");

        //方式3: 匿名内部类, 本质就是一个 该类(该接口)的子类对象
        //匿名内部类解释: 就是一个继承了Animal类的子类对象, 子类名叫:不知道, 对象名叫: 不知道
        new Animal() {
            //重写类中所有的抽象方法
            @Override
            public void eat() {
                System.out.println("匿名内部类方式创建Animal的子类, 动物会吃!");
            }
        }.eat();
    }
}


package com.itheima.demo02_inner;

/*
    案例: 演示匿名内部类的使用场景之: 可以作为方法的实参进行传递.
 */
public class Demo02 {
    public static void main(String[] args) {
        //调用printAnimal()方法
        //格式: printAnimal(Animal类的子类对象);
        //方式1: 普通做法, 搞一个类继承Animal, 然后: 抽象类多态
        Animal an = new Cat();
        printAnimal(an);
        System.out.println("-----------------------------");

        //方式2: 匿名对象, 即: 没有名字的对象.
        printAnimal(new Cat());
        System.out.println("-----------------------------");

        //方式3: 匿名内部类, 本质就是一个 该类(该接口)的子类对象
        printAnimal(new Animal() {
            @Override
            public void eat() {
                System.out.println("匿名内部类实现, 动物会吃!");
            }
        });
    }


    //打印动物.
    public static void printAnimal(Animal an) {
        an.eat();
    }
}

3. collection

package com.itheima.demo03_collection;

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

/*
    案例: 演示Collection集合入门.

    集合简介:
        概述:
            就使用来同时存储 多个 同类型元素的容器, 其长度是 可变的.
        体系:
            Collection:	单列集合顶层接口
                List:
                    特点:
                        元素有索引, 有序, 可重复.
                    常用子类:
                        ArrayList:
                        LinkedList:
                Set:
                    特点:
                        元素无索引, 无序, 唯一.
                    常用子类:
                        HashSet:
                        TreeSet:
            Map: 双列集合顶层接口
                特点:
                    键具有唯一性, 值可以重复.
                常用子类:
                    HashMap:
                    TreeMap:

    泛型:
        概述:
            泛指某种具体的数据类型.
        格式:
            <数据类型>
        细节:
            1. 泛型是JDK1.5出来的, 必须是引用类型.
            2. 前后泛型必须一致, 或者后边的泛型可以省略不写(JDK1.7的特性: 菱形泛型 )
            3. 实际开发中, 泛型一般只结合集合一起使用.
            4. 泛型进阶, 泛型分为: 泛型类, 泛型方法, 泛型接口, 这些内容目前了解即可.
            5. 泛型一般用字母: T: type(类型), E: Element(元素), K: key(键), V: value(值) 表示.

    细节:
        1. 集合的顶层都是接口(例如: Collection, List, Set, Map), IO流的顶层都是抽象类(InputStream, OutputStream, Reader, Writer)
        2. 集合如何限定里边存储元素的数据类型呢?
            通过 泛型 实现.
        3. 当我们接触一个新的继承体系, 建议采用"学顶层, 用底层的"的方式学习.
            顶层: 是整个继承体系的共性内容, 大家都有.
            底层: 才是具体的体现, 实现.
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建集合对象, 不指定泛型.
        Collection list1 = new ArrayList();
        list1.add(10);
        list1.add(false);
        list1.add("abc");

        //2. 创建集合对象, 指定泛型.
        //Collection<String> list2 = new ArrayList<String>();
        Collection<Integer> list2 = new ArrayList<>();
        list2.add(10);
        //list2.add(false);
        //list2.add("abc");
    }
}

package com.itheima.demo03_collection;

import com.itheima.pojo.Student;

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

/*
    案例: List集合存储学生对象并遍历.
 */
public class Demo04 {
    public static void main(String[] args) {
        //1. 创建集合对象.
        List<Student> list = new ArrayList<>();

        //2. 创建元素对象.
        //3. 把元素对象添加到集合中.
        list.add(new Student("刘亦菲", 33));
        list.add(new Student("赵丽颖", 31));
        list.add(new Student("高圆圆", 35));

        //4. 遍历集合.
        //方式1: 普通迭代器.
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student s = it.next();
            System.out.println(s);
        }
        System.out.println("-------------------");

        //方式2: 普通for循环.
        for (int i = 0; i < list.size(); i++) {
            Student s = list.get(i);
            System.out.println(s);
        }
    }
}

package com.itheima.demo03_collection;

import com.itheima.pojo.Student;

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

/*
    案例:演示列表迭代器的用法.

    列表迭代器简介:
        概述:
            它指的是ListIterator, 是Iterator接口的子接口, 它独属于List体系, 即: Collection, Set用不了.
        成员方法:
            public booelan hasPrevious();       判断有没有上一个元素.
            public E previous();                如果有, 就获取上一个元素.

    细节:
        1. 进行逆向遍历之前, 必须进行一次正向遍历.
        2. 正向遍历和逆向遍历必须使用同一个(列表)迭代器.
 */
public class Demo05 {
    public static void main(String[] args) {
        //1. 创建集合对象.
        List<Student> list = new ArrayList<>();

        //2. 创建元素对象.
        //3. 把元素对象添加到集合中.
        list.add(new Student("刘亦菲", 33));
        list.add(new Student("赵丽颖", 31));
        list.add(new Student("高圆圆", 35));

        //4. 遍历集合.
        //4.1 根据集合对象, 获取其对应的迭代器对象.
        ListIterator<Student> lit = list.listIterator();

        //正向遍历, 从前往后.
        //4.2 判断迭代器中是否有下一个元素.
        while (lit.hasNext()) {
            Student s = lit.next();
            //4.3 如果有, 就获取.
            System.out.println(s);
        }
        System.out.println("-----------------");

        //逆向遍历.
        while (lit.hasPrevious()) {
            System.out.println(lit.previous());
        }
    }
}

package com.itheima.demo03_collection;

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

/*
    案例: 回顾上午内容, List集合存储字符串, 并遍历.

    List集合的遍历方式:
        1. 增强for
        2. 列表迭代器.
        3. 普通for循环.
        4. 普通迭代器.
 */
public class Demo08 {
    public static void main(String[] args) {
        //1. 创建集合对象.
        List<String> list = new ArrayList<>();
        //2. 创建元素对象.
        //3. 把元素对象添加到集合中.
        list.add("hello");
        list.add("world");
        list.add("java");
        //4. 遍历集合.
        //方式1: 普通迭代器
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("---------------------");

        //方式2: 列表迭代器
        ListIterator<String> lit = list.listIterator();
        while (lit.hasNext()) {
            String s = lit.next();
            System.out.println(s);
        }
        System.out.println("---------------------");

        //方式3: 普通for循环
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
        System.out.println("---------------------");

        //方式4: 增强for
        for (String s : list) {
            System.out.println(s);
        }
    }
}

4. map

package com.itheima.demo06_map;

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

/*
    案例: 演示Map集合入门.

    Map简介:
        概述:
           它是双列集合的顶层接口, 键具有唯一性, 值可以重复, 数据结构只针对于键有效.
        成员方法:
            V put(K key,V value)	                添加元素, 如果键是第一次添加, 就返回null, 如果键是重复添加, 就用新值覆盖旧值,并返回覆盖前的旧值.
            V remove(Object key)	                根据键删除键值对元素
            void clear()	                        移除所有的键值对元素
            boolean containsKey(Object key)	        判断集合是否包含指定的键
            boolean containsValue(Object value)	    判断集合是否包含指定的值
            boolean isEmpty()	                    判断集合是否为空
            int size()	                            集合的长度,也就是集合中键值对的个数
 */
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建集合对象.
        Map<String, String> hm = new HashMap();
        //2. 创建元素对象.
        //3. 把元素对象添加到集合中.
        System.out.println(hm.put("杨过", "大雕"));     //null
        System.out.println(hm.put("杨过", "小龙女"));   //大雕

        //4. 遍历集合.
        System.out.println("hm: " + hm);
    }
}

package com.itheima.demo06_map;

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

/*
    案例: 演示Map集合的获取功能.

    涉及到的Map集合的成员方法:
        public V get(K key);            根据键获取其对应的值.
        public Collection<V> values();  获取所有值的集合.
        public Set<K> keySet();         获取所有键的集合.
 */
public class Demo02 {
    public static void main(String[] args) {
        //1. 创建集合对象.
        HashMap<String, String> hm = new HashMap();
        //2. 创建元素对象.
        //3. 把元素对象添加到集合中.
        hm.put("乔峰", "阿朱");
        hm.put("虚竹", "梦姑");
        hm.put("段誉", "王语嫣");

        //public Collection<V> values();  获取所有值的集合.
        Collection<String> values = hm.values();
        System.out.println("values: " + values);
        System.out.println("-----------------------");

        //public Set<K> keySet();         获取所有键的集合.
        Set<String> keys = hm.keySet();
        System.out.println("keys: " + keys);
        System.out.println("-----------------------");

        //public V get(K key);            根据键获取其对应的值.
        System.out.println(hm.get("乔峰"));
        System.out.println(hm.get("虚竹"));
        System.out.println(hm.get("段誉"));
    }
}

package com.itheima.demo06_map;

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

/*
    案例: 演示Map集合的遍历方式.

    Map集合的遍历方式:   根据键找值, 即: 根据丈夫找妻子.
        1. 获取所有键的集合.     即: 获取所有的丈夫.   Map#keySet()
        2. 遍历, 获取到每一个键. 即: 获取到每一个丈夫.  增强for, 普通迭代器.
        3. 根据键获取其对应的值. 即: 根据丈夫找妻子.    Map#get()
 */
public class Demo03 {
    public static void main(String[] args) {
        //1. 创建集合对象.
        HashMap<String, String> hm = new HashMap();
        //2. 创建元素对象.
        //3. 把元素对象添加到集合中.
        hm.put("乔峰", "阿朱");
        hm.put("虚竹", "梦姑");
        hm.put("段誉", "王语嫣");

        //4. 遍历集合.
        //4.1 获取所有键的集合.     即: 获取所有的丈夫.   Map#keySet()
        Set<String> keys = hm.keySet();
        //4.2 遍历, 获取到每一个键. 即: 获取到每一个丈夫.  增强for, 普通迭代器.
        for (String key : keys) {
            //4.3 根据键获取其对应的值. 即: 根据丈夫找妻子.    Map#get()
            String value = hm.get(key);
            System.out.println(key + " = " + value);
        }
        System.out.println("----------------------");

        //实际开发写法
        for (String key : hm.keySet())
            System.out.println(key + " = " + hm.get(key));
    }
}

5. 可变参数

package com.itheima.demo08_params;

/*
    案例: 演示可变参数.

    需求:
        1.	定义getSum()方法, 用来获取n个整数的和(n可能是任意的一个数字).
        2.	在main方法中, 调用getSum()方法.

    可变参数解释:
        概述:
            JDK1.5的特性, 表示参数个数可变, 多用于方法的 形参列表.
        格式:
            数据类型... 变量名
        使用场景:
            当某一个方法有多个同类型参数的时候, 且具体个数不明确的时候, 就可以考虑使用可变参.
        本质:
            可变参数的本质就是一个: 数组.
        细节:
            1. 可变参数表示参数的个数可变, 最少0个, 最多无数个.
            2. 一个方法的形参列表有且只能有1个可变参数, 并且可变参必须放到形参列表的最后.
 */
public class Demo01 {
    public static void main(String[] args) {
        //测试getSum()方法
        System.out.println(getSum());                //可变参数表示参数的个数可变, 最少0个, 最多无数个.
        System.out.println(getSum(1, 2));       //可变参数表示参数的个数可变, 最少0个, 最多无数个.
        System.out.println(getSum(1, 2, 3, 4, 5));       //可变参数表示参数的个数可变, 最少0个, 最多无数个.
        System.out.println(getSum(200, 33));       //可变参数表示参数的个数可变, 最少0个, 最多无数个.
        int[] arr = {11, 22, 33};
        System.out.println(getSum(arr));
    }

    //定义getSum()方法, 求和.
    //public static int getSum(double b, int... arr) {
    public static int getSum(int... arr) {
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        return sum;
    }
}

6. reflection 反射

package com.itheima.demo09_reflect;

import com.itheima.demo02_inner.Cat;
import com.itheima.pojo.Student;

/*
    案例: 演示反射入门之 如何获取某个类的字节码文件对象.

    反射入门:
        概述:
            指的就是在程序的运行期间, 通过类的字节码文件对象 来操作 类中成员(成员变量, 构造方法, 成员方法)的过程.
        如何获取某个类的字节码文件对象?
            方式1: 通过类的 class属性, 一般用于充当 锁对象.
            方式2: 通过类的 getClass()方法实现, 一般用于判断两个对象是否是同一个类的对象.
            方式3: 通过反射的方式实现, 一般用于强制加载某个类的字节码文件进内存.
        反射案例:
            1. 反射操作类中的构造方法.
            2. 反射操作类中的成员变量.
            3. 反射操作类中的成员方法.
            4. 动态代理, 即: 运行指定配置文件中指定类的指定方法, 目的是告诉大家, 为什么很多框架我们只要配置了配置文件信息, 就会有不同的效果.

    反射步骤:
        1. 获取该类的字节码文件对象.
        2. 获取要操作的成员对象(构造器, 成员变量, 成员方法)
        3. 创建该类的实例.
        4. 操作指定的成员.
 */
public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //演示如何获取某个类的字节码文件对象
        //方式1: 通过类的 class属性
        Class<Student> clazz1 = Student.class;

        //方式2: 通过类的 getClass()方法实现
        Student s = new Student();
        Class<? extends Student> clazz2 = s.getClass();

        //方式3: 通过反射的方式实现
        Class<?> clazz3 = Class.forName("com.itheima.pojo.Student");

        System.out.println(clazz1 == clazz2);   //true
        System.out.println(clazz1 == clazz3);   //true
        System.out.println(clazz2 == clazz3);   //true
    }
}

package com.itheima.demo09_reflect;

import com.itheima.pojo.Student;

import java.lang.reflect.Field;

/*
    反射案例: 反射操作类中的成员变量.

    反射步骤:
        1. 获取该类的字节码文件对象.
        2. 获取要操作的成员对象(构造器, 成员变量, 成员方法)
        3. 创建该类的实例.
        4. 操作指定的成员.
 */
public class Demo03 {
    public static void main(String[] args) throws Exception {
        //1. 获取该类的字节码文件对象.
        Class<?> clazz = Class.forName("com.itheima.pojo.Student");
        //2. 获取要操作的成员对象(构造器, 成员变量, 成员方法)
        Field name = clazz.getDeclaredField("name");
        //3. 创建该类的实例.
        Student s = (Student) clazz.newInstance();

        //暴力反射
        name.setAccessible(true);

        //4. 操作指定的成员.
        name.set(s, "刘亦菲");
        //5. 打印对象
        System.out.println(s);
    }
}

package com.itheima.demo09_reflect;

import com.itheima.pojo.Student;

import java.lang.reflect.Method;

/*
    反射案例: 反射操作类中的成员方法.

    反射步骤:
        1. 获取该类的字节码文件对象.
        2. 获取要操作的成员对象(构造器, 成员变量, 成员方法)
        3. 创建该类的实例.
        4. 操作指定的成员.
 */
public class Demo04 {
    public static void main(String[] args) throws Exception {
        //1. 获取该类的字节码文件对象.
        Class<?> clazz = Class.forName("com.itheima.pojo.Student");
        //2. 获取要操作的成员对象(构造器, 成员变量, 成员方法)
        Method study = clazz.getMethod("study");
        //3. 创建该类的实例.
        Student s = (Student) clazz.newInstance();

        //4. 操作指定的成员.
        study.invoke(s);

        //5. 打印对象
        System.out.println(s);
    }
}

6. Lambda表达式

package com.itheima;

public interface Addable {
    int getSum(int x, int y);
}

package com.itheima;

public class Demo03 {
    public static void main(String[] args){
        //方式1: 匿名内部类, 体现的是面向对象思想, 自己创建对象, 自己重写方法, 自己写代码逻辑.
        useAddable(new Addable() {
            @Override
            public int getSum(int x, int y) {
                System.out.println("匿名内部类");
                return x + y;
            }
        });
        System.out.println("----------------------");

        //方式2: Lambda表达式, 体现的是面向函数式思想, 即: 只关心做什么, 而不是怎么做.
        useAddable((int x, int y)->{
            System.out.println("Lambda表达式");
            return x+y;
        });
        System.out.println("----------------------");

        //方式3: Lambda表达式, 体现的是面向函数式思想, 即: 只关心做什么, 而不是怎么做.
        //格式: useAddable(Addable接口的子类对象, int x, int y )
        useAddable((int x, int y)->{
            System.out.println("Lambda表达式");
            return x+y;
        }, 20, 20);
        System.out.println("----------------------");

        //省略模式 1. 参数类型可以省略, 但是如果有多个参数的情况下, 不能只省略一个.
        useAddable((x, y)->{
            System.out.println("Lambda表达式");
            return x+y;
        }, 20, 20);
        System.out.println("----------------------");

        //省略模式 3. 如果方法体只有一句话, 则: return, {}, ; 都可以省略
        useAddable((x, y)->x+y, 20, 30);


    }

    public static void useAddable(Addable a){
        int sum = a.getSum(10, 20);
        System.out.println(sum);
    }

    public static void useAddable(Addable a, int x, int y){
        int sum = a.getSum(x, y);
        System.out.println(sum);
    }
}

7. IO流

package com.itheima.demo02_io;

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

/*
    IO流简介:
        概述:
            指的是Input(输入) / Output(输出), 也叫输入/输出流, 就是用来实现文件传输的.
        应用场景:
            1. 文件上传.
            2. 文件下载.
        分类:
            按照流向分:
                输入流: 就是用来 读取 数据的.
                输出流: 就是用来 写   数据的.
            按照操作分:
                字节流:
                    特点:
                       以字节为单位拷贝数据, 能操作任意类型的文件, 也叫万能流.
                    分类:
                        字节输入流:   以字节为单位读取数据, 顶层抽象类: InputStream
                            FileInputStream:   基本的字节输入流.
                            BufferedInputStream: 高效的字节输入流.
                        字节输出流:   以字节为单位写数据, 顶层抽象类: OutputStream
                            FileOutputStream:     基本的字节输出流.
                            BufferedOutputStream: 高效的字节输出流.
                字符流:
                    特点:
                       以字符为单位拷贝数据, 只能操作纯文本文件.
                       纯文本文件解释: 一个文件能用微软自带的记事本打开, 且里边的内容你也能看懂, 就是纯文本文件.
                    分类:
                        字符输入流:   以字符为单位读取数据, 顶层抽象类: Reader
                            FileReader:   基本的字符输入流.
                            BufferedReader: 高效的字符输入流.
                        字符输出流:   以字符为单位写数据, 顶层抽象类: Writer
                            FileWriter:     基本的字符输出流.
                            BufferedWriter: 高效的字符输出流.
    记忆:
        1. 实际开发中, 优先使用字符流, 字符流搞不定再考虑使用字节流.
        2. 如果目的地文件不存在, 程序会自动创建(前提: 该文件的父目录必须存在)
        3. 只要出现乱码问题, 原因只有一个: 编解码不一致.
        4. 如果一个类的对象想实现序列化或者反序列化操作, 则该类必须实现 Serializable接口.
 */
public class Demo01 {
}

package com.itheima.demo02_io;

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

/*
    案例: 基本的字节输出流入门.

    需求:
        1.	创建FileOutputStream对象, 关联指定的目的地文件.
        2.	往文件中写入字符'a', 'b', 'c'.

    FileOutputStream简介:
        概述:
            它表示基本的字节输出流, 即: 以字节为单位往文件中写数据.
        构造方法:
            public FileOutputStream(String path);    往文件中添加数据, 默认是: 覆盖.
            public FileOutputStream(String path, boolean append);   往文件中添加数据, 第二个参数: true(追加), false(覆盖)
        成员方法:
            public void write(int n);            一次写一个字节
            public void write(byte[] bys);       一次写一个字节数组
            public void write(byte[] bys, int start, int len);  一次写一个字节数组的一部分
            public void close();                 关闭流对象, 释放资源.

    细节:
        如果目的地文件不存在, 程序会自动创建(前提: 该文件的父目录必须存在)
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
        //1. 创建输出流对象, 关联: 目的地文件.
        //FileOutputStream fos = new FileOutputStream("day05_lambda&IO&Socket/data/1.txt"); //覆盖
        FileOutputStream fos = new FileOutputStream("day05_lambda&IO&Socket/data/1.txt", true); //追加
        //2. 往文件中写数据.

        //方式1: 一次写一个字节.
        /*fos.write(97);
        fos.write(98);
        fos.write(99);*/

        //方式2: 一次写一个字节数组.
       /* //写入换行
        fos.write("\r\n".getBytes());       //写入换行, 默认用的是: utf-8码表

        byte[] bys = {65, 66, 67, 68, 69};      //ABCDE
        fos.write(bys);*/

        //方式3: 一次写一个字节数组的一部分.
        byte[] bys = {65, 66, 67, 68, 69};      //BCD
        fos.write(bys, 1, 3);

        //3. 关流, 释放资源.
        fos.close();
    }
}

package com.itheima.demo02_io;

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

/*
    案例: 字节输出流, 创建一个指定大小的空文件.

    需求:
        在指定的路径下(例如: d:/abc)创建一个大小为1G的空文件.
 */
public class Demo03 {
    public static void main(String[] args) throws IOException {
        //1. 创建输出流, 关联目的地文件.
        FileOutputStream fos = new FileOutputStream("d:/abc/房老师&鲁老师.avi");
        //2. 往文件中写数据.
        byte[] bys = new byte[1024];        //1024个字节 = 1KB

        //循环实现
        for (int i = 0; i < 1024 * 1024 * 1; i++) {
            fos.write(bys);
        }

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

package com.itheima.demo02_io;

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

/*
    案例: 演示字节输入流入门.

    需求:
        1.	创建FileInputStream对象, 关联指定的数据源文件.
        2.	通过一次读取一个字节的形式, 读取该文件中的数据.

    FileInputStream简介:
        概述:
            它表示基本的字节输入流, 即: 以字节为单位读取数据.
        构造方法:
            public FileInputStream(String path);   关联数据源文件, 如果文件不存在, 会报错.
        成员方法:
            public int read();
                一次读取一个字节, 并返回读取到的内容(例如: 'a' -> 97), 读不到返回-1
            public int read(byte[] bys);
                一次读取一个字节数组, 并将读取到的内容存储到字节数组中, 然后返回读取到的有效字节数(例如: 'a' -> 1), 读不到返回-1
            public void close();     释放资源

 */
public class Demo04 {
    public static void main(String[] args) throws IOException {
        //需求1: 一次读取一个字节.
       /* //1. 创建输入流, 关联: 数据源文件.
        FileInputStream fis = new FileInputStream("day05_lambda&IO&Socket/data/1.txt");
        //2. 定义变量, 记录读取到的内容(字节)
        int len = 0;
        //3. 循环读取.
        *//*
            (len = fis.read()) != -1 这样代码做了 3 件事儿:
                1. 执行 fis.read(), 一次读取一个字节, 并返回读取到的内容.
                2. 执行 len = fis.read(), 把上一步读取到的数据赋值给变量.
                3. 执行 len  != -1, 判断读取到的内容是否是-1, 如果是: 说明读取完毕, 如果不是说明还有内容.
        *//*
        while((len = fis.read()) != -1) {
            System.out.println(len);
        }

        //4. 释放资源.
        fis.close();*/


        //需求2: 一次读取有一个字节数组.
        //1. 创建输入流, 关联: 数据源文件.
        FileInputStream fis = new FileInputStream("day05_lambda&IO&Socket/data/1.txt");

        //2. 定义变量, 记录读取到的内容(字节)
        int len = 0;
        byte[] bys = new byte[3];       //实际开发, 数组长度一般是 1024 的整数倍.
        //3. 循环读取.
        //合并版
        while ((len = fis.read(bys)) != -1) {
            System.out.println(new String(bys, 0, len));   //abc  d
        }

        //分解版
        /*int len1 = fis.read(bys);
        System.out.println("bys: " + Arrays.toString(bys));   //a, b, c
        System.out.println("len1: " + len1);    //3

        int len2 = fis.read(bys);
        System.out.println("bys: " + Arrays.toString(bys));   //d, b, c
        System.out.println("len2: " + len2);    //1

        int len3 = fis.read(bys);
        System.out.println("bys: " + Arrays.toString(bys));   //d, b, c
        System.out.println("len3: " + len3);    //-1*/

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

package com.itheima.demo02_io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/*
    案例: 演示字节高效流的用法.

    字节高效流简介:
        概述:
            字节高效流也叫高效字节流, 字节缓冲流, 缓冲字节流, 指的是: BufferedInputStream, BufferedOutputStream.
        构造方法:
            BufferedInputStream:   字节高效输入流.
                public BufferedInputStream(InputStream is);
            BufferedOutputStream:   字节高效输出流.
                public BufferedOutputStream(OutputStream os);
        问题: 为什么高效流封装的不是数据源或者目的地文件的路径, 而是一个基本的字节流呢?
        答案: 因为高效流仅仅提供缓冲区的功能, 具体的读写还是有基本的流来实现的.
 */
public class Demo05 {
    public static void main(String[] args) throws IOException {
        //需求: 把1.txt -> 2.txt文件中.
        //方式1: 字节高效流 一次读写一个字节.
       /* //1. 创建输入流, 关联: 数据源文件.
        //分解版
        //创建基本的字节输入流, 关联: 数据源文件, 用于读取文件内容.
        FileInputStream fis = new FileInputStream("day05_lambda&IO&Socket/data/1.txt");
        //创建高效的字节输入流, 关联: 基本的字节流, 用来提供 缓冲区的功能.
        BufferedInputStream bis = new BufferedInputStream(fis);

        //2. 创建输出流, 关联: 目的地文件
        //合并版.
        //字节高效输出流           =                               普通的字节输出流  +                  文件路径
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day05_lambda&IO&Socket/data/2.txt"));

        //3. 定义变量, 记录读取到的内容(字节, 有效字节数, 字符, 有效字符数, 字符串)
        int len;
        //4. 循环读取, 并将读取到的内容赋值给变量, 只要条件满足, 就一直读.
        while ((len = bis.read()) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            bos.write(len);
        }
        //6. 释放资源.
        bis.close();
        bos.close();*/



        //方式2: 字节高效流 一次读写一个字节数组.
        //1. 创建输入流, 关联: 数据源文件.
        //分解版
        //创建基本的字节输入流, 关联: 数据源文件, 用于读取文件内容.
        FileInputStream fis = new FileInputStream("day05_lambda&IO&Socket/data/1.txt");
        //创建高效的字节输入流, 关联: 基本的字节流, 用来提供 缓冲区的功能.
        BufferedInputStream bis = new BufferedInputStream(fis);

        //2. 创建输出流, 关联: 目的地文件
        //合并版.
        //字节高效输出流           =                               普通的字节输出流  +                  文件路径
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day05_lambda&IO&Socket/data/3.txt"));

        //3. 定义变量, 记录读取到的内容(字节, 有效字节数, 字符, 有效字符数, 字符串)
        int len;
        byte[] bys = new byte[1024];
        //4. 循环读取, 并将读取到的内容赋值给变量, 只要条件满足, 就一直读.
        while ((len = bis.read(bys)) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            bos.write(bys, 0, len);
        }
        //6. 释放资源.
        bis.close();
        bos.close();
    }
}

package com.itheima.demo02_io;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/*
    案例: 演示编解码的问题.

    字符流简介:
        背景: 即, 为什么有字符流?
        原因: 因为中文字符在不同的码表中占用字节数不一样, 如果采用字节流拷贝文本文件, 当数据源和目的地文件码表不一致的时候, 可能会产生乱码问题.
             即: 字符流 = 字节流 + 编码表.

        常见的码表:
            ASCII: 美国信息通用交换码表, 定义了最基础的规则, 大多数的码表几乎都支持这个码表.
                   'a' -> 97, 'A' -> 65, '0' -> 48
            ISO-8859-1: 欧洲通用码表.
            GBK: 国内用的码表.
            utf-8: 国际通用码表, 也叫: 万国码, 统一码.

        细节:
            1. 英文字母, 数字, 特殊符号不管在什么码表中都只占1个字节, 且都是正数.
            2. 中文字符在GBK码表中占2个字节, UTF-8码表中占3个字节.
            3. 中文字符的第一个字节都是负数.
            4. 字符串及其对应的字节数组之间如何相互转换:
                字符串 -> 字节数组:
                    String类中的成员方法:
                        public byte[] getBytes();       把字符串转成对应的码表, 采用: 默认码表
                        public byte[] getBytes(String characterName); 把字符串转成对应的码表, 采用: 指定码表
                字节数组 -> 字符串:
                     String类中的构造方法:
                        public String(byte[] bys)  把字节数组转成其对应的字符串, 采用: 默认码表.
                        public String(byte[] bys, String characterName) 把字节数组转成其对应的字符串, 采用: 指定码表.

        概述:
            字符流指的是以字符为单位拷贝数据, 它的顶层抽象类是: Reader 和 Writer
 */
public class Demo06_ASCII {
    public static void main(String[] args) throws Exception {
        //字符串 -> 字节数组: 这个动作叫 编码.
        //utf-8码表: 97, 98, 99, 49, 50, 51, -28, -67, -96, -27, -91, -67
        //gbk码表:   97, 98, 99, 49, 50, 51, -60, -29, -70, -61
        String s = "abc123你好";
        byte[] bys1 = s.getBytes();     //utf-8
        byte[] bys2 = s.getBytes("utf-8");     //utf-8
        byte[] bys3 = s.getBytes("gbk");     //utf-8
        System.out.println("bys1: " + Arrays.toString(bys1));
        System.out.println("bys2: " + Arrays.toString(bys2));
        System.out.println("bys3: " + Arrays.toString(bys3));
        System.out.println("----------------------");

        //字节数组 -> 字符串 : 这个动作叫 解码.
        //byte[] bys = {97, 98, 99, 49, 50, 51, -28, -67, -96, -27, -91, -67};
        byte[] bys = {97, 98, 99, 49, 50, 51, -60, -29, -70, -61};
        String utf8Str = new String(bys);
        String gbkStr = new String(bys, "gbk");
        System.out.println("utf8Str: " + utf8Str);
        System.out.println("gbkStr: " + gbkStr);
    }
}

package com.itheima.demo02_io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
    案例: 演示字符流拷贝文件.

    字符流拷贝文件的4种方式:
        1. 基本的字符流一次读写一个字符.
        2. 基本的字符流一次读写一个字符数组.
        3. 高效的字符流一次读写一个字符.
        4. 高效的字符流一次读写一个字符数组.
        5. 高效的字符流独有的拷贝方式: 一次读写一行.

    细节:
        字符流只能拷贝纯文本文件.
 */
public class Demo07 {
    public static void main(String[] args) throws IOException {
        //拷贝方式1. 基本的字符流一次读写一个字符.
        //method01();

        //拷贝方式2. 基本的字符流一次读写一个字符数组.
        //method02();

        //拷贝方式3. 高效的字符流一次读写一个字符.
        //method03();

        //拷贝方式4. 高效的字符流一次读写一个字符数组.
        method04();

    }

    private static void method04() throws IOException {
        //1. 创建输入流, 关联: 数据源文件.
        BufferedReader br = new BufferedReader(new FileReader("day05_lambda&IO&Socket/data/1.txt"));
        //2. 创建输出流, 关联: 目的地文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("day05_lambda&IO&Socket/data/5.txt"));
        //3. 定义变量, 记录读取到的内容(字节, 有效字节数, 字符, 有效字符数, 字符串)
        int len;
        char[] chs = new char[1024];
        //4. 循环读取, 并将读取到的内容赋值给变量, 只要条件满足, 就一直读.
        while ((len = br.read(chs)) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            bw.write(chs, 0, len);
        }
        //6. 释放资源.
        br.close();
        bw.close();
    }

    private static void method03() throws IOException {
        //1. 创建输入流, 关联: 数据源文件.
        BufferedReader br = new BufferedReader(new FileReader("day05_lambda&IO&Socket/data/1.txt"));
        //2. 创建输出流, 关联: 目的地文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("day05_lambda&IO&Socket/data/4.txt"));
        //3. 定义变量, 记录读取到的内容(字节, 有效字节数, 字符, 有效字符数, 字符串)
        int len;
        //4. 循环读取, 并将读取到的内容赋值给变量, 只要条件满足, 就一直读.
        while ((len = br.read()) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            bw.write(len);
        }
        //6. 释放资源.
        br.close();
        bw.close();
    }

    private static void method02() throws IOException {
        //1. 创建输入流, 关联: 数据源文件.
        FileReader fr = new FileReader("day05_lambda&IO&Socket/data/1.txt");
        //2. 创建输出流, 关联: 目的地文件
        FileWriter fw = new FileWriter("day05_lambda&IO&Socket/data/3.txt");
        //3. 定义变量, 记录读取到的内容(字节, 有效字节数, 字符, 有效字符数, 字符串)
        int len;
        char[] chs = new char[1024];
        //4. 循环读取, 并将读取到的内容赋值给变量, 只要条件满足, 就一直读.
        while ((len = fr.read(chs)) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            fw.write(chs, 0, len);
        }
        //6. 释放资源.
        fr.close();
        fw.close();
    }

    private static void method01() throws IOException {
        //1. 创建输入流, 关联: 数据源文件.
        FileReader fr = new FileReader("day05_lambda&IO&Socket/data/1.txt");
        //2. 创建输出流, 关联: 目的地文件
        FileWriter fw = new FileWriter("day05_lambda&IO&Socket/data/2.txt");
        //3. 定义变量, 记录读取到的内容(字节, 有效字节数, 字符, 有效字符数, 字符串)
        int len;
        //4. 循环读取, 并将读取到的内容赋值给变量, 只要条件满足, 就一直读.
        while ((len = fr.read()) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            fw.write(len);
        }
        //6. 释放资源.
        fr.close();
        fw.close();
    }
}

package com.itheima.demo02_io;


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

/*
    案例: 演示IO流的两个常见面试题.

    问题1: 字节高效流, 字符高效流, Writer各自的缓冲区大小分别是多少?
    答案:   8kb          16KB         2KB

    问题2: flush() 和 close()方法的区别是什么?
    答案:
        flush(): 用来刷新缓冲区的, 把缓冲区的数据刷出来, 刷新之后, 流对象还可以继续使用.
        close(): 用来关闭流释放资源的, 关闭之前会自动刷新一次缓冲区, 关闭之后, 流对象不能继续使用.
 */
public class Demo08 {
    public static void main(String[] args) throws IOException {
        /*FileOutputStream fos = new FileOutputStream("day05_lambda&IO&Socket/data/2.txt");
        fos.write("abc".getBytes());*/


        FileWriter fw = new FileWriter("day05_lambda&IO&Socket/data/2.txt");

        fw.write("马老师好!");
        //fw.close();
        fw.flush();

        fw.write("赵老师好!");
        //fw.flush();
        fw.close();
    }
}

package com.itheima.demo02_io;

import com.itheima.pojo.Student;

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

/*
    案例: 演示序列化操作.

    序列化操作:
        概述:
            指的是把对象写入到文件中, 用的是 ObjectOutputStream.
        成员方法:
            public void writeObject(Object obj);   把对象序列化到文件中.
            public void close();   释放资源.
        需求: 序列化学生对象到文件中.

    细节:
        如果一个类的对象想实现序列化或者反序列化操作, 则该类必须实现 Serializable接口.

        Serializable接口解释:
            它是一个标记接口, 里边没有任何的成员, 只是做一个标记的.

    细节:
        1. 如果先序列化, 然后在修改JavaBean类内容, 再反序列化行不行?
           不行.
        2. 如何解决?
           方式1: 重新序列化一次, 再反序列化.
           方式2: 给一个 serialVersionUID;
        3. 如果某个属性不想被序列化怎么办?
            通过 transient(瞬态关键字) 修饰即可.
 */
public class Demo09 {
    public static void main(String[] args) throws IOException {
        //1. 创建对象序列化流, 关联: 目的地文件.
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day05_lambda&IO&Socket\\data\\2.txt"));
        //2. 创建学生对象, 并将其序列化到文件中.

        Student s = new Student("刘亦菲", 33);
        oos.writeObject(s);
        //3. 释放资源.
        oos.close();
    }
}

package com.itheima.demo02_io;

import com.itheima.pojo.Student;

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

/*
    案例: 演示反序列化操作.

    反序列化操作:
        概述:
            指的是从文件中读取对象, 用的是 ObjectInputStream.
        成员方法:
            public Object readObject();   读取对象.
            public void close();   释放资源.
        需求: 序列化学生对象到文件中.

    细节:
        如果一个类的对象想实现序列化或者反序列化操作, 则该类必须实现 Serializable接口.

        Serializable接口解释:
            它是一个标记接口, 里边没有任何的成员, 只是做一个标记的.
 */
public class Demo10 {
    public static void main(String[] args) throws Exception {
        //1. 创建反序列化流, 关联数据源文件.
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day05_lambda&IO&Socket/data/2.txt"));
        //2. 反序列化动作.
        Object obj = ois.readObject();
        Student s = (Student)obj;
        //3. 打印对象.
        System.out.println(s);
        //4. 释放资源.
        ois.close();
    }
}

8. JDBC数据链接

数据库连接地址的标准写法,必须要按照这种写法否则,插入数据时候中文就会产生乱码了。

jdbc:mysql://192.168.88.100:3306/day06?user=root&password=123456&useUnicode=true&characterEncoding=utf-8"
package com.itheima.demo01_jdbc;

import com.mysql.jdbc.Driver;
import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

/*
    案例: 演示JDBC入门案例.

    JDBC简介:
        概述:
            全称叫Java DataBase Connectivity, Java数据库连接, 就是通过Java代码操作不同的数据库.
        原理:
            由Sun公司提供统一的规则,规范(JDBC规范), 具体的实现和体现交给不同的数据库厂商来做.
            即: 我们要连接哪个数据库, 只要导入它的驱动(数据库厂商提供)即可.
        核心作用:
            1. 连接不同的数据库.
            2. 向数据库发送指令(SQL语句).
            3. 接收返回的结果集.
        JDBC入门案例:
            JDBC查询表数据.
        JDBC的核心步骤:
            0. 导入指定数据库的驱动(就是 jar包), 即: 连接哪个库, 就导入谁的驱动.
            1. 注册驱动.
            2. 获取连接对象.
            3. 根据连接对象, 获取可以执行SQL语句的对象.
            4. 执行SQL语句, 获取结果集.
            5. 操作结果集.
            6. 释放资源.

        JDBC的相关API介绍:
            DriverManager类解释:
                概述:
                    它是一个类, 表示驱动管理者, 主要用于: 注册驱动 和 获取连接对象的.
                成员方法:
                    public static void registerDriver(Driver driver);
                        注册驱动, 但是这种方式会导致驱动注册两次, 实际开发没人用.

                    public static Connection getConnection(String url, String username, String password);
                        获取连接对象的, 三个参数的解释如下:
                        参数1: 数据库连接字符串
                            格式:
                                协议:子协议://要连接的数据库的ip地址:端口号/具体的要连接的数据库.
                            示例:
                                jdbc:mysql://192.168.30.68:3306/day06
                                jdbc:mysql://localhost:3306/day06
                                jdbc:mysql://127.0.0.1:3306/day06
                                jdbc:mysql:///day06    如果操作本机, 可以省略: 主机名和端口号

           Connection接口:
                概述:
                    它表示连接对象, 即: Java 和 不同数据库之间的连接对象, 主要负责: 事务管理, 获取可以执行SQL语句的对象.
                成员方法:
                    事务管理方法:
                        setAutoCommit(), commit(), rollback()
                    获取可以执行SQL语句的对象:
                        public Statement createStatement();
                            获取可以执行SQL语句的对象, 没有预编译功能, 会发生SQL注入攻击问题.
                        public PreparedStatement prepareStatement(String sql);
                            获取可以执行SQL语句的对象, 有预编译功能, 不会发生SQL注入攻击问题.

           Statement接口:
                概述:
                    它表示可以执行SQL语句的对象, 没有预编译功能, 主要负责: 批处理, 执行SQL语句, 获取结果集.
                作用:
                    关于批处理的方法:
                        addBatch(), executeBatch(), clearBatch();
                    执行SQL语句, 获取结果集:
                        public ResultSet executeQuery(String sql)      执行查询语句, 获取结果集.
                        public int executeUpdate(String sql)           执行更新(增, 删, 改)语句, 获取结果集.

           ResultSet接口:
                概述:
                    它表示执行完查询语句后返回的结果集(本质是一张结果表), 主要用于获取 数据.
                成员方法:
                    public boolean next();      判断结果集中是否有下一条数据, 类似于  Iterator#hasNext()
                    public Xxx getXxx(int columnIndex);     根据列的编号获取列的数据, 编号从 1 开始.
                    public Xxx getXxx(String columnName);   根据列的名字获取列的数据, 推荐使用.
 */
public class Demo01 {
    //JDBC入门案例, 最粗糙版.
    @Test
    public void show1() throws Exception {
        //1. 注册驱动.
        DriverManager.registerDriver(new Driver());
        //2. 获取连接对象.
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06", "root", "123456");
        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();
        //4. 执行SQL语句, 获取结果集.
        String sql = "select * from users;";
        ResultSet rs = stat.executeQuery(sql);
        //5. 操作结果集.
        while (rs.next()) {      //类似于Iterator#hasNext()
            int uid = rs.getInt("uid");
            String username = rs.getString("username");
            String password = rs.getString("password");
            System.out.println(uid + ", " + username + ", " + password);
        }
        //6. 释放资源.
        rs.close();
        stat.close();
        conn.close();
    }

    //JDBC的相关API解释之: DriverManager类
    @Test
    public void show2() throws Exception {
        //1. 注册驱动.
        //DriverManager.registerDriver(new Driver());

        //实际开发注册驱动方式: 反射.
        Class.forName("com.mysql.jdbc.Driver");

        //2. 获取连接对象.
        //Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06", "root", "123456");
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day06", "root", "123456");


        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();
        //4. 执行SQL语句, 获取结果集.
        String sql = "select * from users;";
        ResultSet rs = stat.executeQuery(sql);
        //5. 操作结果集.
        while (rs.next()) {      //类似于Iterator#hasNext()
            int uid = rs.getInt("uid");
            String username = rs.getString("username");
            String password = rs.getString("password");
            System.out.println(uid + ", " + username + ", " + password);
        }
        //6. 释放资源.
        rs.close();
        stat.close();
        conn.close();
    }

    //JDBC的相关API解释之: Connection接口, 代码同上

    //JDBC的相关API解释之: Statement接口, 代码同上

    //JDBC的相关API解释之: ResultSet接口
    @Test
    public void show3() throws Exception {
        //1. 注册驱动.
        Class.forName("com.mysql.jdbc.Driver");

        //2. 获取连接对象.
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day06", "root", "123456");

        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();
        //4. 执行SQL语句, 获取结果集.
        String sql = "select username, uid, password from users;";
        ResultSet rs = stat.executeQuery(sql);


        //5. 操作结果集.
        while (rs.next()) {
            //方式1: 根据列的编号获取.
            /*int uid = rs.getInt(1);
            String username = rs.getString(2);
            String password = rs.getString(3);*/

            //方式2: 根据列的名字获取.
           int uid = rs.getInt("uid");
           String username = rs.getString("username");
           String password = rs.getString("password");
            System.out.println(uid + ", " + username + ", " + password);
        }


        //6. 释放资源.
        rs.close();
        stat.close();
        conn.close();
    }


    //JDBC入门案例: 最终版.
}

package com.itheima.demo01_jdbc;

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

/*
    案例: 演示JDBC的CURD操作.
 */
public class Demo02 {
    //JDBC的 查
    @Test
    public void method1() throws Exception {
        //1. 注册驱动.
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接对象.
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day06", "root", "123456");
        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();

        //4. 执行SQL语句, 获取结果集.
        String sql = "select * from users";
        ResultSet rs = stat.executeQuery(sql);
        //5. 操作结果集.
        while (rs.next()) {
            System.out.println(rs.getInt("uid") + ", " + rs.getString("username") + ", " + rs.getString("password"));
        }
        //6. 释放资源.
        rs.close();
        stat.close();
        conn.close();
    }

    //JDBC的 增
    @Test
    public void method2() throws Exception {
        //1. 注册驱动.
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接对象.
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day06", "root", "123456");
        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();

        //4. 执行SQL语句, 获取结果集.
        String sql = "INSERT INTO users VALUES(NULL, '唐老师', 'tanglaoshi');";
        int num = stat.executeUpdate(sql);

        //5. 操作结果集.
        System.out.println(num > 0 ? "添加成功" : "添加失败");

        //6. 释放资源.
        stat.close();
        conn.close();
    }

    //JDBC的 改
    @Test
    public void method3() throws Exception {
        //1. 注册驱动.
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接对象.
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day06", "root", "123456");
        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();

        //4. 执行SQL语句, 获取结果集.
        String sql = "update users set username='杨飞' where uid=4;";
        int num = stat.executeUpdate(sql);

        //5. 操作结果集.
        System.out.println(num > 0 ? "修改成功" : "修改失败");

        //6. 释放资源.
        stat.close();
        conn.close();
    }

    //JDBC的 删
    @Test
    public void method4() throws Exception {
        //1. 注册驱动.
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接对象.
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day06", "root", "123456");
        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();

        //4. 执行SQL语句, 获取结果集.
        String sql = "delete from users where uid=4;";
        int num = stat.executeUpdate(sql);

        //5. 操作结果集.
        System.out.println(num > 0 ? "删除成功" : "删除失败");

        //6. 释放资源.
        stat.close();
        conn.close();
    }
}

package com.itheima.demo01_jdbc;

import com.itheima.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

/*
    案例: 演示SQL注入攻击问题.

    SQL注入攻击问题:
		概述:
			指的是如果我们的SQL语句中部分代码是要求用户录入的, 当用户录入一些非法字符或者非法值的时候,
			被我们的SQL语句识别了, 从而改变了SQL语句的结构, 就会引发一系列的安全问题, 这些安全问题就叫:
			SQL注入攻击问题.
		案例: 模拟登陆
			select * from users where username = '用户录入的账号' and password = '用户录入的密码';

		听话的用户:
			账号: 刘亦菲
			密码: liuyifei

		小屌丝:
			账号: sdkjfsd
			密码: sldf ' or '1=1

		老屌丝:
			账号: slkdfs
			密码: klsdf ' or '1=1'; drop database day08;'
 */
public class Demo03 {
    public static void main(String[] args) throws Exception {
        //1. 提示用户录入账号和密码, 并接收.
        Scanner sc = new Scanner(System.in);
        System.out.println("请录入您的账号: ");
        String uname = sc.nextLine();
        System.out.println("请录入您的密码: ");
        String pwd = sc.nextLine();

        //2. 判断用户是否登录成功.
        //2.1. 注册驱动.
        //2.2. 获取连接对象.
        Connection conn = JDBCUtils.getConnection();
        //2.3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();

        //2.4. 执行SQL语句, 获取结果集.
        String sql = "select * from users where username = '" + uname + "' and password = '" + pwd + "';";
        ResultSet rs = stat.executeQuery(sql);

        //2.5. 操作结果集.
        System.out.println(rs.next() ? "登录成功" : "登录失败");

        //2.6. 释放资源.
        JDBCUtils.release(null, stat, conn);
    }
}

package com.itheima.demo01_jdbc;

import com.itheima.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

/*
    案例: 解决SQL注入攻击问题, 采用PreparedStatement的预编译功能.

    Connection接口的成员方法:
        public PreparedStatement prepareStatement(String sql);
           获取可以执行SQL语句的对象, 有预编译功能, 不会发生SQL注入攻击问题.
           PreparedStatement接口是Statement接口的子接口.

    预编译解释:
        即: 预先对SQL语句进行一次编译, 此时已经决定了SQL语句的格式, 把需要用户传入的内容用占位符替代即可, 预编译之后,
        不管用户传入什么内容, 都只会当做普通字符处理.

    细节:
        如果用PreparedStatement接口的预编译功能, 因为已经预先传入SQL语句了, 所以执行SQL语句的时候, 不需要额外传入SQL语句.
 */
public class Demo04 {
    public static void main(String[] args) throws Exception {
        //1. 提示用户录入账号和密码, 并接收.
        Scanner sc = new Scanner(System.in);
        System.out.println("请录入您的账号: ");
        String uname = sc.nextLine();
        System.out.println("请录入您的密码: ");
        String pwd = sc.nextLine();

        //2. 判断用户是否登录成功.
        //2.1. 注册驱动.
        //2.2. 获取连接对象.
        Connection conn = JDBCUtils.getConnection();

        //2.3. 根据连接对象, 获取可以执行SQL语句的对象.
        String sql = "select * from users where username = ? and password=? ;";
        PreparedStatement ps = conn.prepareStatement(sql);

        //给占位符填充值.
        ps.setString(1, uname);
        ps.setString(2, pwd);

        //2.4. 执行SQL语句, 获取结果集.
        //ResultSet rs = ps.executeQuery(sql);        //预编译之后, 不要再传入了, 否则报错.
        ResultSet rs = ps.executeQuery();

        //2.5. 操作结果集.
        System.out.println(rs.next() ? "登录成功" : "登录失败");

        //2.6. 释放资源.
        JDBCUtils.release(null, ps, conn);
    }
}

9. DBCP连接池

9.1 c3p0-config.xml文件

需要放在src目录下面

<c3p0-config>
  <!-- 使用默认的配置读取连接池对象 -->
  <default-config>
  	<!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/day06</property>
    <property name="user">root</property>
    <property name="password">123456</property>
    
    <!-- 连接池参数 -->
    <!-- 数据连接池中默认的 连接对象的个数 -->
    <property name="initialPoolSize">5</property>
    <!-- 数据连接池中最大的 连接对象的个数 -->
    <property name="maxPoolSize">10</property>
    <!-- 连接对象的最大超时时间, 单位是: 毫秒. -->
    <property name="checkoutTimeout">3000</property>
  </default-config>

  <named-config name="otherc3p0"> 
    <!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
    <property name="user">root</property>
    <property name="password">123456</property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">8</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>
</c3p0-config>
package com.itheima.demo03_dbcp;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

/*
    案例: 演示数据库连接池入门.

    数据库连接池简介:
        概述:
            大白话版:
                实际开发中, 当我们需要使用大量生命周期短的连接对象时, 每次频繁的创建和销毁这些连接对象是非常消耗资源的,
                针对于这种情况, 我们可以搞一个池子出来, 里边放一些连接对象, 用的时候从里边拿, 用完之后再放回去, 这个池子
                就叫: 数据库连接池(Data Base Connection Pool, 简称: DBCP), 这样做的好处是: 节约资源, 提高效率.
            专业版:
                数据库连接池就使用来解决频繁的创建和销毁连接对象的, 将其从销毁改为"自动归还到数据库连接池中".
        分类:
            DBCP: 属于Apache软件基金会, 不能自动回收空闲连接.
            C3P0: 属于Apache软件基金会, 能自动回收空闲连接.
                  Java的框架中, 底层但凡涉及到数据库连接池了, 默认用的都是: C3P0
            Druid: 属于阿里巴巴, 中文名叫: 德鲁伊, 采用分布式事务的方式实现的, 能更好的兼容分布式集群.
        作用:
            数据库连接池就使用来解决频繁的创建和销毁连接对象的, 将其从销毁改为"自动归还到数据库连接池中".
        步骤:
            0. 准备动作
                A. 拷贝C3P0的jar包到lib文件夹中, 并配置运行变量.
                B. 将C3P0数据库连接池的配置文件, 放到 src目录下.
            1. 创建数据库连接池对象, 它会自动去src目录下读取 c3p0-config.xml 这个配置文件.
            2. 从数据库连接池中获取 连接对象.
            3. 其他步骤和之前一样, 例如: 执行SQL语句, 获取结果集, 操作结果集.
            4. 释放资源即可, 同样都是些 conn.close(), 但是细节如下:
                如果连接对象是我们自己创建的, conn.close() 就是销毁.
                如果连接对象是从连接池获取的, conn.close() 就是 自动归还连接对象到连接池中, 因为连接池底层已经 重构了 Connection#close()方法.

    目前的问题:
        虽然加入数据库连接池后, 看起来效率好像是高了, 但其实效率反而低了, 因为目前我们的数据库连接池默认有 5 个连接对象,
        在这里我们就用了1个, 其它4个是闲置的状态, 之后每创建一个数据库连接池, 就默认会有5个连接对象, 效率反而低.
        之所以出现这样的问题, 原因是因为: 我们没有实现数据库连接池对象的共享, 应该让大家用1个数据库连接池.
        所以, 我们要自定义 C3P0Utils工具类, 实现连接池的操作.
 */
public class Demo01 {
    public static void main(String[] args) throws Exception {
        //1. 注册驱动.
        //Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接对象.
        //方式1: 我们自己创建连接对象.
        //Connection conn = DriverManager.getConnection("jdbc:mysql:///day06", "root", "123456");

        //方式2: 从数据库连接池中获取连接对象.
        //1. 创建数据库连接池对象, 它会自动去src目录下读取 c3p0-config.xml 这个配置文件.
        //ComboPooledDataSource cpds = new ComboPooledDataSource();  //默认读取 <default-config> 标签中的信息.
        ComboPooledDataSource cpds = new ComboPooledDataSource("otherc3p0");  //读取 name为 otherc3p0 标签中的信息.

        //2. 从数据库连接池中获取 连接对象.
        Connection  conn = cpds.getConnection();

        //3. 根据连接对象, 获取可以执行SQL语句的对象.
        Statement stat = conn.createStatement();
        //4. 执行SQL语句, 获取结果集.
        String sql = "select username, uid, password from users;";
        ResultSet rs = stat.executeQuery(sql);


        //5. 操作结果集.
        while (rs.next()) {
            //方式1: 根据列的编号获取.
            /*int uid = rs.getInt(1);
            String username = rs.getString(2);
            String password = rs.getString(3);*/

            //方式2: 根据列的名字获取.
            int uid = rs.getInt("uid");
            String username = rs.getString("username");
            String password = rs.getString("password");
            System.out.println(uid + ", " + username + ", " + password);
        }

        //6. 释放资源.
        rs.close();
        stat.close();
        conn.close();
    }
}

10. 事务

package com.itheima.demo04_transaction;
import utils.JDBCUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class Demo01 {
    public static void main(String[] args){
        Connection conn = null;
        Statement stat = null;

        try{
            conn = JDBCUtils.getConnection();
            stat = conn.createStatement();

            String sql1 = "update account set money = money - 1000 where aid = 1;";
            String sql2 = "update account set money = money + 1000 where aid = 2;";

            conn.setAutoCommit(false);

            int i1 = stat.executeUpdate(sql1);

            int i2 = stat.executeUpdate(sql2);

            System.out.println(i1);

            if (i1 == i2 && i1 == 1) {
                System.out.println("转账成功");
                conn.commit();   //提交事务
            }



        } catch (Exception e){
            System.out.println("转账失败");
            try{
                conn.rollback();
            }catch (SQLException throwables){
                throwables.printStackTrace();
            }
        }finally {
            JDBCUtils.release(null, stat, conn);
        }


    }
}

11. 线程

定义一个线程,使用Thread类

package com.itheima.thread;

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("run........." + i);
        }
    }
}

调用这个线程

package com.itheima.thread;

public class Demo02 {
    public static void main(String[] args){
        MyThread mt = new MyThread();
        mt.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main..." + i);
        }
    }
}

定义一个线程使用Runable接口

package com.itheima.thread;

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("run........." + i);
        }
    }
}

package com.itheima.thread;

public class Demo03 {
    public static void main(String[] args){
        MyRunnable mr = new MyRunnable();
        Thread th = new Thread(mr);
        th.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main..." + i);
        }
    }
}

综合

package com.itheima.thread;

public class Demo04 {
    public static void main(String[] args){
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("继承Thread类...." + i);
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("实现Runnable接口......." + i);
                }
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println("Lambda表达式......." + i);
            }
        }).start();
    }
}

11. 线程与同步锁

package com.itheima.demo07_tickets_synchronized;

public class MyThread extends Thread {
    private static int tickets = 100;

    public MyThread(){

    }

    public MyThread(String name){super(name);}

    @Override
    public void run() {
        while (true){
            synchronized (MyThread.class){
                if (tickets < 1){
                    break;
                }
            }

            try {
                Thread.sleep(50);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(getName() + " 正在售卖第 " + tickets-- + " 号票");
        }


    }
}

package com.itheima.demo07_tickets_synchronized;

public class Demo01 {
    public static void main(String[] args) {
        //1. 创建4个线程对象, 表示4个窗口.
        MyThread mt1 = new MyThread("窗口1");
        //mt1.setName("窗口1");

        MyThread mt2 = new MyThread("窗口2");
        MyThread mt3 = new MyThread("窗口3");
        MyThread mt4 = new MyThread("窗口4");

        //2. 开启线程.
        mt1.start();
        mt2.start();
        mt3.start();
        mt4.start();
    }

    //静态同步方法: 该类的字节码文件对象, 此处是: Demo01.class
    public static synchronized void method01() {
        System.out.println("start");
        System.out.println("hello world 1");
        System.out.println("hello world 2");
        System.out.println("hello world 3");
        System.out.println("end");
    }

    //非静态同步方法: this
    public synchronized void method02() {
        System.out.println("start");
        System.out.println("hello world 1");
        System.out.println("hello world 2");
        System.out.println("hello world 3");
        System.out.println("end");
    }
}

12. 死锁

package com.itheima.demo08_deadlock;

/*
    案例: 演示死锁(只在面试用).

    死锁指的是同步代码块的嵌套.

    死锁的思路:
        1. 死锁需要两个线程, 两把锁.
        2. 一个线程先抢锁A, 后抢锁B.
        3. 另一个线程先抢锁B, 后抢锁A, 此时就有可能引发死锁问题.
        4. 为了让效果更明显, 用 while(true)改进.
 */
public class Demo01 {
    //1. 定义两把锁.
    private static final String lockA = "锁A";
    private static final String lockB = "锁B";`

    public static void main(String[] args) {
        //2. 一个线程先抢锁A, 后抢锁B.
        new Thread("刘亦菲") {
            @Override
            public void run() {
                // 4. 为了让效果更明显, 用 while(true)改进.
                while (true) {
                    //先抢锁A,
                    synchronized (lockA) {
                        System.out.println(getName() + " 获取到" + lockA + ", 等待 " + lockB);
                        //后抢锁B.
                        synchronized (lockB) {
                            System.out.println(getName() + " 获取到" + lockB + ", 成功进入小黑屋!");
                        }
                    }
                }
            }
        }.start();

        //3. 另一个线程先抢锁B, 后抢锁A, 此时就有可能引发死锁问题.
        new Thread("林志颖") {
            @Override
            public void run() {
                // 4. 为了让效果更明显, 用 while(true)改进.
                while (true) {
                    //先抢锁B
                    synchronized (lockB) {
                        System.out.println(getName() + " 获取到" + lockB + ", 等待 " + lockA);
                        //后抢锁A.
                        synchronized (lockA) {
                            System.out.println(getName() + " 获取到" + lockA + ", 成功进入小黑屋!");
                        }
                    }
                }
            }
        }.start();
    }
}

13. 线程

package com.itheima.demo09_join_daemon;

//自定义的线程类
public class MyThread extends Thread{
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "....." + i);
        }
    }
}

优先级

package com.itheima.demo09_join_daemon;

/*
    案例: 演示线程的优先级问题.

    细节:
        1. 线程的默认优先级是5, 范围是: 1 ~ 10,  1最低, 10最高.
        2. 线程的优先级为10, 说明该线程一定第一个执行吗?
           不一定, 线程的优先级越高, 只是说明该线程获取资源的几率更大, 并不一定第一个执行.
 */
public class Demo01 {
    public static void main(String[] args) {
        //查看线程默认优先级.
        /*System.out.println(Thread.MAX_PRIORITY);        //10
        System.out.println(Thread.MIN_PRIORITY);        //1
        System.out.println(Thread.NORM_PRIORITY);       //5*/

        //1. 创建三个线程对象.
        MyThread mt1 = new MyThread("飞机");
        MyThread mt2 = new MyThread("高铁");
        MyThread mt3 = new MyThread("游艇");

        //2. 打印优先级.
        /*System.out.println(mt1.getPriority());
        System.out.println(mt2.getPriority());
        System.out.println(mt3.getPriority());*/

        //3. 设置优先级.
        mt1.setPriority(1);
        mt3.setPriority(10);

        //4. 开启线程.
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

加入线程

package com.itheima.demo09_join_daemon;

/*
    案例: 演示加入线程.

    加入线程解释:
        即: 等待这个线程执行结束, 类似于: 插队, 当他没有执行结束的时候, 别的线程无法执行.

    细节:
        加入线程必须第一个开启, 才有效, 目前能看懂即可.
 */
public class Demo02 {
    public static void main(String[] args) {

        //1. 创建三个线程对象.
        MyThread mt1 = new MyThread("康熙");
        MyThread mt2 = new MyThread("四阿哥");
        MyThread mt3 = new MyThread("八阿哥");

        //2. 开启线程.
        mt1.start();
        //设置mt1为加入线程.
        try {
            mt1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        mt2.start();
        mt3.start();
    }
}

守护线程

package com.itheima.demo09_join_daemon;

/*
    案例: 演示 守护线程.

    细节:
        1. 默认每个线程都是非守护线程.
        2. 当非守护线程死亡的时候, 和它关联的守护线程, 都是强制死亡.
        3. 多线程的执行具有 随机性 和 延迟性, 原因是因为CPU在做着高效的切换.


 */
public class Demo03 {
    public static void main(String[] args) {
        //1. 创建两个线程.
        MyThread mt1 = new MyThread("关羽");
        MyThread mt2 = new MyThread("张飞");

        //2. 设置它们为: 守护线程, 守护当前线程(即: 主线程).
        mt1.setDaemon(true);
        mt2.setDaemon(true);

        //3. 修改主线程的名字.
        //Thread.currentThread() 获取当前正在执行的线程对象.
        Thread.currentThread().setName("刘备");

        //4. 开启线程.
        mt1.start();
        mt2.start();

        //5. 给主线程分配任务, 任务量小一点, 让它提前结束.
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "............" + i);
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值