java-从菜鸟到大神

JavaSE第一阶段

JavaSE第一阶段模块五

任务二:新特性

第二十二章 新特性
22.1 Java8的新特性
  • 该版本于2014年3月发布,
  • 是自Java5以来最具革命性的版本,
  • 这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。
22.1.2 函数式接口
  • 接口中只有一个抽象方法
  • Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会
    报错。
  • Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:

image-20200913160845925

image-20200913162255394

点击它

package demo;

import java.util.Comparator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FunctionalInterfaceTest {
    public static void main(String[] args) {
        System.out.println("-------无参无返回值------------");
        // 1.匿名内部类的语法格式: 父类/接口类型  引用变量名 = new 父类/接口类型(){ 方法的重写 };
        //无参无返回值
        Runnable runnable=new Runnable() {  //无参无返回值
            @Override
            public void run() {
                System.out.println("无参无返回值方法1!");
            }
        }; //这个不能忘记
        runnable.run();
        // 2.使用lambda表达式实现函数式接口对象的创建: (参数列表)->{方法体;}
        Runnable runnable1=()-> System.out.println("无参无返回值方法2!");
        runnable1.run();
        System.out.println("-------------有参无返回值----------------");
        //有参无返回值
        Consumer consumer=new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        };
        consumer.accept("有参无返回值1!");

        Consumer consumer1=o->System.out.println(o);
        consumer1.accept("有参无返回值2!");

        System.out.println("-------------无参有返回值----------------");
        Supplier supplier=new Supplier() {
            @Override
            public Object get() {
                return "无参有返回值1";
            }
        };
        //supplier.get();  //注意:有返回值的需要使用打印语句打印出来
        System.out.println(supplier.get());

        Supplier supplier1=()->"无参有返回值2"; //连return都可以省去
        //supplier1.get();
        System.out.println(supplier1.get());

        System.out.println("-------------有参有返回值----------------");
        Function function=new Function() {
            @Override
            public Object apply(Object o) {
                return o;
            }
        };
        //function.apply("有参有返回值1");
        System.out.println(function.apply("有参有返回值1"));

        Function function1=s->s;
        //function1.apply("有参有返回值2");
        System.out.println(function1.apply("有参有返回值2"));

        System.out.println("-------------两参数有返回值(int)----------------");
        Comparator comparator=new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return 0;
            }
        };
        //comparator.compare("有两个参数","一个返回值");//有返回值的要打印
        System.out.println(comparator.compare("有两个参数","一个返回值"));

        Comparator comparator1=(o1,o2)->0;
        //comparator1.compare("有两个参数","一个返回值");
        System.out.println(comparator1.compare("有两个参数","一个返回值"));

        System.out.println("-------------有参有返回值(boolean)----------------");
        Predicate predicate=new Predicate() {
            @Override
            public boolean test(Object o) {
                return false;
            }
        };
        System.out.println(predicate.test("true"));

        Predicate predicate1=o->false;
        System.out.println(predicate1.test(false));
    }
}

运行结果:

-------无参无返回值------------
无参无返回值方法1!
无参无返回值方法2!
-------------有参无返回值----------------
有参无返回值1!
有参无返回值2-------------无参有返回值----------------
无参有返回值1
无参有返回值2
-------------有参有返回值----------------
有参有返回值1
有参有返回值2
-------------两参数有返回值(int)----------------
0
0
-------------有参有返回值(boolean)----------------
false
false
22.1.3 Lambda表达式
  • Lambda 表达式是实例化函数式接口的重要方式,使用 Lambda 表达式可以使代码变的更加简洁
    紧凑。
  • lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句
    块。
  • 语法格式:(参数列表) -> { 方法体; } - 其中()、参数类型、{} 以及return关键字 可以省略。
22.1.4 方法引用
  • 通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行。??

    因为函数式接口(Runnable)中的方法(run())和show方法结构类似【都没有参数和返回值】,使用方法引用我们只需要指定要调用的方法名,将调用的任务交给函数式接口;(函数式接口怎么调用我们不用理会)

  • 方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:

    • 对象的非静态方法引用 ObjectName :: MethodName
    • 类的静态方法引用 ClassName :: StaticMethodName
    • 类的非静态方法引用 ClassName :: MethodName
    • 构造器的引用 ClassName :: new
    • 数组的引用 TypeName[] :: new
  • 方法引用是在特定场景下lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加
    紧凑简洁,从而减少冗余代码。

package demo;

import java.util.Arrays;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class MethodReferenceTest {
    public static void main(String[] args) {
        System.out.println("-------------【1】对象的非静态方法引用-------------------------------");
        // 1.使用匿名内部类的方式通过函数式接口Runnable中的方法实现对Person类中show方法的调用
        Student student = new Student(1,"zhangsan",12);
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                student.show();
            }
        };
        runnable.run();
        // 2.使用lambda表达式的方式实现Person类中show方法的调用
        Runnable runnable1=()->student.show();
        runnable1.run();
        // 3.使用方法引用的方式实现Person类中show方法的调用
        Runnable runnable2=student::show;
        runnable2.run();
        System.out.println("----------------------------");
        // 4.使用匿名内部类的方式通过函数式接口Consumer中的方法来实现Person类中setName方法的调用
       /* Consumer consumer=new Consumer() {
            @Override
            public void accept(Object o) {
                student.setName((String)o);
            }
        };*/
       //要知道函数式接口大部分都可以指定泛型的
        Consumer<String> consumer=new Consumer<String>() {
            @Override
            public void accept(String s) {
                student.setName(s);
            }
        };
        consumer.accept("lisi");
        System.out.println(student);
        // 5.使用lambda表达式的方式实现Person类中setName方法的调用
        Consumer<String> consumer1=s->student.setName(s);
        consumer1.accept("wangwu");
        System.out.println(student);
        // 6.使用方法引用的方式实现Person类中setName方法的调用
        Consumer<String> consumer2=student::setName; //总感觉不用考虑参数了
        consumer2.accept("consumer引用");
        System.out.println("======================");
        // 7.使用匿名内部类的方式通过函数式接口Supplier中的方法来实现Person类中getName方法的调用
        Supplier<String> supplier=new Supplier<String>() {
            @Override
            public String get() {
               return student.getName();
            }
        };
        System.out.println(supplier.get());
        //8.lambda表达式
        Supplier<String>  supplier1=()->student.getName();
        System.out.println(supplier1.get());
        //9.方法引用
        Supplier<String> supplier2=student::getName;//与lambda表达式相比,传入的参数不用考虑了
        System.out.println(supplier2.get());
        System.out.println("---------------------------------");
        System.out.println("-----------------【2.类的静态方法引用】--------------------------");
        // 10.使用匿名内部类的方式通过函数式接口Function中的方法实现Integer类中parseInt方法的调用
        //泛型前面的是参数类型,后面的是返回值类型
        Function<String,Integer> function=new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        };
        //有返回值就要使用打印语句打印
        System.out.println(function.apply("124"));
        //11.lambda表达式
        Function<String,Integer> function1=(s)->Integer.parseInt(s);
        System.out.println(function1.apply("234"));
        //12.方法引用
        Function<String,Integer> function2=Integer::parseInt;//注意没有括号()
        System.out.println(function2.apply("345"));
        System.out.println("----------------------------");
        // 13.使用匿名内部类的方式通过函数式接口Comparator中的方法实现Integer类中compare方法的调用
        //泛型:两个参数的泛型是一样的,使用一个泛型
        Comparator<Integer>  comparator=new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        };
        System.out.println(comparator.compare(12,34)); //-1
        System.out.println(comparator.compare(23,12));//1
        //14.lambda表达式
        Comparator<Integer> comparator1=(i1,i2)->Integer.compare(i1,i2);
        System.out.println(comparator1.compare(23,12));//1
        //15.方法引用
        Comparator<Integer> comparator2=Integer::compare;
        System.out.println(comparator.compare(23,12));//1
        System.out.println("-----------------【3.构造器的引用 】--------------------------");
        // 16.使用匿名内部类的方式通过Supplier函数式接口创建Person类型的对象并返回
        Supplier<Student> supplier3=new Supplier<Student>() {
            @Override
            public Student get() {
                return new Student();
            }
        };
        System.out.println(supplier3.get());
        //lambda表达式
        Supplier<Student> supplier4=()->new Student();
        System.out.println(supplier4.get());
        //方法引用
        Supplier<Student> supplier5=Student::new;
        System.out.println(supplier5.get());
        System.out.println("---------------------------------------");
        // 12.使用匿名内部类的方式通过BiFunction函数式接口采用有参方式创建Person类型的对象并返回
        BiFunction<Integer,String,Student> biFunction=new BiFunction<Integer, String, Student>() {
            @Override
            public Student apply(Integer integer, String s) {
                return new Student(integer,s); //没有找到三个参数的函数呼死你接口
            }
        };
        System.out.println(biFunction.apply(1,"张三"));//Student中没哟toString显示的是地址
        //lambda表达式
        BiFunction<Integer,String,Student> biFunction1=(i,s)->new Student(i,s);
        System.out.println(biFunction1.apply(2,"lisi"));
        //方法引用
        BiFunction<Integer,String,Student> biFunction2=Student::new;//参数不用管
        System.out.println(biFunction2.apply(3,"wangwu"));
        System.out.println("------------【5.数组的引用】-----------------------------");
        // 12.使用匿名内部类的方式通过Function函数式接口创建指定数量的Person类型的对象数组并返回
        Function<Integer,Student[]> function3=new Function<Integer, Student[]>() {
            @Override
            public Student[] apply(Integer integer) {
                return new Student[integer];
            }
        };
        Student[] students = function3.apply(3);
        System.out.println(Arrays.toString(students));
        //lambda表达式
        Function<Integer,Student[]> function4=(i)->new Student[i];
        Student[] students1 = function4.apply(3);
        System.out.println(Arrays.toString(students1));
        //方法引用
        Function<Integer,Student[]> function5=Student[]::new;
        Student[] students2 = function5.apply(3);
        System.out.println(Arrays.toString(students2));//不使用Student中的toString也可以显示
    }
}

运行结果:

-------------1】对象的非静态方法引用-------------------------------
展示出你的英姿!
展示出你的英姿!
展示出你的英姿!
----------------------------
Student{id=1, name='lisi', age=12}
Student{id=1, name='wangwu', age=12}
======================
consumer引用
consumer引用
consumer引用
---------------------------------
-----------------2.类的静态方法引用】--------------------------
124
234
345
----------------------------
-1
1
1
1
-----------------3.构造器的引用 】--------------------------
Student{id=0, name='null', age=0}
Student{id=0, name='null', age=0}
Student{id=0, name='null', age=0}
---------------------------------------
Student{id=1, name='张三', age=0}
Student{id=2, name='lisi', age=0}
Student{id=3, name='wangwu', age=0}
------------5.数组的引用】-----------------------------
[null, null, null]
[null, null, null]
[null, null, null]

22.1.5 Stream接口
  • 案例题目:
    准备一个List集合并放入Person类型的对象,将集合中所有成年人过滤出来放到另外一个集合并打
    印出来。
public class PersonTest {
    public static void main(String[] args) {

        //使用的是之前的方法
        List<Person> personList=new ArrayList<>();
        personList.add(new Person("沈万三",34));
        personList.add(new Person("苏城",8));
        personList.add(new Person("陈素素",15));

        List<Person> personList1=new ArrayList<>();
        for (Person person : personList) {
            if(person.getAge()<=18){
                personList1.add(person);
            }
        }
        for (Person person : personList1) {
            System.out.println(person);
        }

    }
}

打印结果:

Person{name='苏城', age=8}
Person{name='陈素素', age=15}
  • (1)基本概念
    java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选
    等操作。
    Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两
    种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。

  • (2)使用步骤
    创建Stream,通过一个数据源来获取一个流。
    转换Stream,每次转换返回一个新的Stream对象。
    对Stream进行聚合操作并产生结果。

  • (3)创建方式
    方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
    方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
    方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
    方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T>
    s)

  • (4)中间操作

    筛选与切片的常用方法如下:

    方法声明 功能介绍
    Stream filter(Predicate<? super T> predicate) 返回一个包含匹配元素的流
    Stream distinct() 返回不包含重复元素的流
    Stream limit(long maxSize) 返回不超过给定元素数量的流
    Stream skip(long n) 返回丢弃前n个元素后的流


    映射的常用方法如下:

    方法声明 功能介绍
    Stream map(Function<? super T,? extends R> mapper) 返回每个处理过元素组成的流

    Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
    返回每个被替换过元素组成的流,并将所有流合成一个流


    排序的常用方法如下:

    方法声明 功能介绍
    Stream sorted() 返回经过自然排序后元素组成的流
    Stream sorted(Comparator<? super T> comparator) 返回经过比较器排序后元素组成的流

  • (5)终止操作

    匹配与查找的常用方法如下:

    方法声明 功能介绍
    Optional findFirst() 返回该流的第一个元素
    boolean allMatch(Predicate<? super T> predicate) 返回所有元素是否匹配
    boolean noneMatch(Predicate<? super T> predicate) 返回没有元素是否匹配
    Optional max(Comparator<? super T> comparator) 根据比较器返回最大元素
    Optional min(Comparator<? super T> comparator) 根据比较器返回最小元素
    long count() 返回元素的个数
    void forEach(Consumer<? super T> action) 对流中每个元素执行操作


    规约的常用方法如下:

    方法声明 功能介绍
    Optional reduce(BinaryOperator accumulator) 返回结合后的元素值


    收集的常用方法如下:

    方法声明 功能介绍
    <R,A> R collect(Collector<? super T,A,R> collector) 使用收集器对元素进行处理

    import demo01.Person;
    
    import java.util.ArrayList;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Optional;
    import java.util.function.BinaryOperator;
    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.stream.Collector;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class PersonTest {
        public static void main(String[] args) {
    
            List<Person> personList=new ArrayList<>();
            personList.add(new Person("沈万三",34));
            personList.add(new Person("苏城",28));
            personList.add(new Person("陈素素",15));
            personList.add(new Person("小小",25));
            personList.add(new Person("名人",36));
            personList.add(new Person("小微",15));
            System.out.println("--------------打印集合所有元素------------------");
            for (Person person : personList) {
                System.out.println(person);
            }
            System.out.println("----------------打印成人--------------------------");
            List<Person> personList1=new ArrayList<>();
            for (Person person : personList) {
                if(person.getAge()>=18){
                    personList1.add(person);
                }
            }
            for (Person person : personList1) {
                System.out.println(person);
            }
    
            System.out.println("-------------------------");
            // 3.使用Stream接口实现上述功能
            personList.stream().filter(new Predicate<Person>() {
                @Override
                public boolean test(Person person) {
                    return person.getAge()>=18;
                }
            }).forEach(new Consumer<Person>() {//因为返回的还是Stream对象
                @Override
                public void accept(Person person) {
                    System.out.println(person);
                }
            });
            System.out.println("---------------lambda表达式-----------------");
            // 4.使用lambda表达式对上述代码进行优化
            personList.stream().filter(person -> person.getAge()>=18).forEach(person -> System.out.println(person));
            System.out.println("---------------通过流跳过2个元素后再取3个元素-----------------");
            // 5.实现对集合中元素通过流跳过2个元素后再取3个元素后打印
            personList.stream().skip(2).forEach(person -> System.out.println(person));
            System.out.println("-----------");
            personList.stream().skip(2).limit(3).forEach(person -> System.out.println(person));
            System.out.println("---------6.实现集合中所有元素中的年龄获取出来并打印----------------");
            // 6.实现集合中所有元素中的年龄获取出来并打印
            personList.stream().map(new Function<Person, Integer>() {
                @Override
                public Integer apply(Person person) {
                    return person.getAge();
                }
            }).forEach(System.out::println);
            //list.stream().map(person -> person.getAge()).forEach(System.out::println);
            System.out.println("------lambda表达式-------");
            personList.stream().map(person ->person.getAge() ).forEach(System.out::println);
            System.out.println("-------方法引用----------");
            personList.stream().map(Person::getAge).forEach(System.out::println);
            // 7.实现集合中所有元素的自然排序并打印
            System.out.println("------7.实现集合中所有元素的自然排序并打印---------------");
            //ClassCastException
            //personList.stream().sorted().forEach(System.out::println);//class demo01.Person cannot be cast to class java.lang.Comparable
            //Person继承   public class Person implements Comparable<Person>
            personList.stream().sorted().forEach(System.out::println);
            // 8.判断集合中是否没有元素的年龄是大于45岁的
            System.out.println("-----8.判断集合中是否没有元素的年龄是大于45岁的----------");
            boolean b = personList.stream().noneMatch(person -> person.getAge() > 45);
            System.out.println(b);
            // 9.按照指定的比较器规则获取集合所有元素中的最大值
            System.out.println("------ 9.按照指定的比较器规则获取集合所有元素中的最大值-----------");
            Optional<Person> max = personList.stream().max(new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    return o1.getAge() - o2.getAge();
                }
            });
            System.out.println(max);
            System.out.println("------Lambda-------");
            Optional<Person> max1 = personList.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
            System.out.println("最大值"+max);
            Optional<Person> min = personList.stream().min((o1, o2) -> o1.getAge() - o2.getAge());
            System.out.println("最小值"+min);
            // 10.实现将集合中所有元素的年龄映射出来并进行累加后打印
            System.out.println("-----10.实现将集合中所有元素的年龄映射出来并进行累加后打印-------");
            Optional<Integer> reduce = personList.stream().map(Person::getAge).reduce(new BinaryOperator<Integer>() {
                @Override
                public Integer apply(Integer integer, Integer integer2) {
                    return Integer.sum(integer,integer2);
                }
            });
            System.out.println(reduce);
            System.out.println("---方法引用-----");
            Optional<Integer> reduce1 = personList.stream().map(Person::getAge).reduce(Integer::sum);
            System.out.println(reduce1);
            // 11.实现将集合中所有元素的姓名映射出来并收集到集合中打印
            System.out.println("------11.实现将集合中所有元素的姓名映射出来并收集到集合中打印");
            personList.stream().map(Person::getName).collect(Collectors.toList()).forEach(System.out::println);
        }
    }
    
    

    运行结果:

    --------------打印集合所有元素------------------
    Person{name='沈万三', age=34}
    Person{name='苏城', age=28}
    Person{name='陈素素', age=15}
    Person{name='小小', age=25}
    Person{name='名人', age=36}
    Person{name='小微', age=15}
    ----------------打印成人--------------------------
    Person{name='沈万三', age=34}
    Person{name='苏城', age=28}
    Person{name='小小', age=25}
    Person{name='名人', age=36}
    -------------------------
    Person{name='沈万三', age=34}
    Person{name='苏城', age=28}
    Person{name='小小', age=25}
    Person{name='名人', age=36}
    ---------------lambda表达式-----------------
    Person{name='沈万三', age=34}
    Person{name='苏城', age=28}
    Person{name='小小', age=25}
    Person{name='名人', age=36}
    ---------------通过流跳过2个元素后再取3个元素-----------------
    Person{name='陈素素', age=15}
    Person{name='小小', age=25}
    Person{name='名人', age=36}
    Person{name='小微', age=15}
    -----------
    Person{name='陈素素', age=15}
    Person{name='小小', age=25}
    Person{name='名人', age=36}
    ---------6.实现集合中所有元素中的年龄获取出来并打印----------------
    34
    28
    15
    25
    36
    15
    ------lambda表达式-------
    34
    28
    15
    25
    36
    15
    -------方法引用----------
    34
    28
    15
    25
    36
    15
    ------7.实现集合中所有元素的自然排序并打印---------------
    Person{name='陈素素', age=15}
    Person{name='小微', age=15}
    Person{name='小小', age=25}
    Person{name='苏城', age=28}
    Person{name='沈万三', age=34}
    Person{name='名人', age=36}
    -----8.判断集合中是否没有元素的年龄是大于45岁的----------
    true
    ------ 9.按照指定的比较器规则获取集合所有元素中的最大值-----------
    Optional[Person{name='名人', age=36}]
    ------Lambda-------
    最大值Optional[Person{name='名人', age=36}]
    最小值Optional[Person{name='陈素素', age=15}]
    -----10.实现将集合中所有元素的年龄映射出来并进行累加后打印-------
    Optional[153]
    ---方法引用-----
    Optional[153]
    ------11.实现将集合中所有元素的姓名映射出来并收集到集合中打印
    沈万三
    苏城
    陈素素
    小小
    名人
    小微
    
    
    public class Person implements Comparable<Person>{
        @Override
        public int compareTo(Person o) {
            //return getName().compareTo(o.getName());
            return getAge()-o.getAge();
        }
    
22.1.6 Optional类
  • 案例题目
    判断字符串是否为空,若不为空则打印字符串的长度,否则打印0。

  • java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在
    或不存在。

  • 该类的引入很好的解决空指针异常,不用显式进行空值检测。

常用的方法

方法声明 功能介绍
static Optional ofNullable(T value) 根据参数指定数值来得到Optional类型的对象

Optional map(Function<? super T,? extends U> 根据参数指定规则的结果来得到Optional类
mapper) 型的对象

T orElse(T other) 若该值存在就返回,否则返回other的数值。

public class OptionalTest {

    public static void main(String[] args) {

        //String str1 = "hello";
        String str1 = null;
        if (null != str1) {
            System.out.println("字符串的长度是:" + str1.length()); // 5  空指针异常
        } else {
            System.out.println("字符串为空,因此长度为0!");
        }

        System.out.println("----------------------------------------------------");
        // Java8中使用Optional类实现空值的处理
        // 1.将数据str1装到Optional对象代表的容器中
        Optional<String> optional = Optional.ofNullable(str1);
        // 2.建立映射关系  使用字符串的长度与字符串建立映射关系
        /*Optional<Integer> integer = optional.map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return s.length();
            }
        });*/
        //Optional<Integer> integer = optional.map(s -> s.length());
        Optional<Integer> integer = optional.map(String::length);
        // 3.若字符串为空则打印0,否则打印字符串的数值
        System.out.println("integer = " + integer); // Optional.empty
        System.out.println(integer.orElse(0)); // 0
    }
}
22.2 Java9的新特性
22.2.1 Java9的概述
  • Java9发布于2017年9月发布,带来了很多新特性,其中最主要的变化是模块化系统
  • 模块就是代码和数据的封装体,模块的代码被组织成多个包,每个包中包含Java类和接口,模块的
    数据则包括资源文件和其他静态信息。
  • 项目中可以有多个模块(项目-》模块-》类/接口–》方法/变量)

创建模块:在项目上右击——》new–》Module

22.2.2 模块化的使用
  • (1)语法格式
    在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,具体如下:
    module 模块名称 {
    }

------反正在java11中,直接使用快捷键alt+enter直接建立关联成功导入Person类了

/**
 * @author liujunqiang
 * @create 2020-09-14 10:50
 */module java01 {
     //将demo这个包暴露出去
     exports demo;
}
/**
 * @author liujunqiang
 * @create 2020-09-14 10:52
 */module java02 {
     //需要java01这个模块的信息
     requires java01;
}
package demo01;

import demo.Person;

/**
 * @author liujunqiang
 * @create 2020-09-14 10:45
 */
public class PersonTest {

    public static void main(String[] args) {
        Person person= new Person(); //使用快捷键进行关联
        System.out.println(person);
        Person person1 = new Person("zhagnsan", 23);
        System.out.println(person1);
    }
}
  • (2)模块化的优势
    • 减少内存的开销。
    • 可简化各种类库和大型应用的 开发和维护。
    • 安全性,可维护性,提高性能。

image-20200914102559346

什么意思???? 可以使用java8运行java11的版本

image-20200914103750848

jdk1.8中有java11?

22.2.3 钻石操作符的使用升级
  • 在Java9中允许在匿名内部类的使用中使用钻石操作符。
  • 注意允许了在匿名内部类中使用钻石操作符
22.2.4 集合工厂方法
  • (1)基本概念
    Java9的List、Set和Map集合中增加了静态工厂方法of实现不可变实例的创建
    不可变体现在无法添加、修改和删除它们的元素。
    不允许添加null元素对象。
  • (2)实际意义
    保证线程安全:在并发程序中既保证线程安全性,也大大增强了并发时的效率。
    被不可信的类库使用时会很安全。
    如果一个对象不需要支持修改操作,将会节省空间和时间的开销。
    可以当作一个常量来对待,并且这个对象在以后也不会被改变。
 List<Integer> integers = List.of(1, 2, 3, 4, 5, 6);
        System.out.println(integers);
        //integers.add(4);//编译没有问题,运行出错 //java.lang.UnsupportedOperationException

        Set<String> sets = Set.of("zhagnan", "afjds", "kasjdfk");
        System.out.println(sets);
        //sets.add("askjdf");//编译ok,运行出错 //java.lang.UnsupportedOperationException
22.2.5 InputStream的增强
  • InputStream类中提供了transferTo方法实现将数据直接传输到OutputStream中。
InputStream inputStream = null;
        OutputStream outputStream=null;
        try {
            inputStream = new FileInputStream("d:/a.txt"); //路径不存在的话会报错
            outputStream=new FileOutputStream("d:/b.txt");  //不存在的话会新建
            inputStream.transferTo(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(null!=outputStream){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null!=inputStream){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
22.3 Java10的新特性
22.3.1 Java10的概述
  • Java10于2018年3月发布,改进的关键点包括一个本地类型推断、一个垃圾回收的增强
  • Java10计划只是一个短期版本,因此公开更新将在六个月内结束,9月份发布的Java11将是Java的
    长期支持(LTS)版本,LTS版本的发布每三年发布一次。
22.3.2 局部变量类型推断
  • (1)基本概念
    Java10可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索
    引,以及传统for循环的本地变量。
    它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch形式参数或任何其
    他类型的变量声明。
  • (2)实际意义
    标识符var不是关键字,只是一个保留的类型名称。这意味着var用作变量,方法名或包名的代码不
    会受到影响,但var不能作为类或则接口的名字
    避免了信息冗余。
    对齐了变量名。
    更容易阅读。
public class VarTest {
    public static void main(String[] args) {
        int num=10;
        List<Integer> list=new ArrayList<>();
        list.add(num);
        System.out.println(list);

        var num1=10;
        var integers = new ArrayList<>();
        integers.add(num1);
        System.out.println(integers);
    }
}
22.4 Java11的新特性
22.4.1 Java11的概述
  • Java11于2018年9月正式发布,这是 Java 大版本周期变化 后的第一个长期支持版本,非常值得关
    注。
22.4.2 简化的编译运行操作
  • 在Java11中可以使用java命令一次性进行编译和运行操作。
  • 执行源文件中的第一个类必须包含主方法。
  • 不可以使用其它源文件中自定义的类。
22.4.3 String类新增方法

方法声明 功能介绍
boolean isBlank() 判断字符串是否为空或只包含空白代码点
Optional map(Function<? super T,? extends U> 根据参数指定规则的结果来得到Optional类型的对象
mapper)

T orElse(T other) 若该值存在就返回,否则返回other的数值。

第二十三章 在线考试系统
23.1 在线考试系统
23.1.1 软件开发的流程
  • 需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级
23.1.2 软件的需求分析

01 功能框架图

在线考试系统的主要功能分析如下:

  • (1)学员系统
    用户模块:登录、修改密码、退出
    考试模块:开始考试、查询成绩、导出成绩(选)
  • (2)管理员系统
    学员管理模块:增加学员、删除学员、修改学员、查找学员
    考题管理模块:增加考题、删除考题、修改考题、查找考题、导入考题(选)
23.1.3 软件的概要设计

在线考试系统采用C(Client客户端)/S(Server服务器)架构进行设计,具体如下:

  • 客户端(Client) - 主要用于提供字符界面供用户选择并将处理结果显示出来。
  • 服务器(Server) - 主要用于针对字符界面的选择实现真正业务功能的处理。
  • 数据库(Database) - 主要用于进行数据的存取。
23.1.4 软件的详细设计
  • 客户端和服务器之间采用基于tcp协议的编程模型进行通信。
    • 客户端的对象输出流连接服务器的对象输入流。
    • 服务器的对象输出流连接客户端的对象输入流。
  • 客户端采用消息的类型作为具体业务的代表,伴随着账户信息等一并发送给服务器。
    • 当客户端发来的消息类型为"managerCheck"时,则表示要实现管理员账户信息的校验功能。
    • 当客户端发来的消息类型为"userCheck"时,则表示要实现学员账户信息的校验功能。
  • 服务器采用消息的类型作为是否校验成功的标志发送给客户端。
    • 当客户端收到的消息类型为"success"时,则表示账户信息正确。
    • 当客户端收到的消息类型为"fail"时,则表示账户信息错误。
23.1.5 软件的编码流程

(1)管理员登录功能
编写基于tcp协议的服务器端,也就是初始化服务器;
编写基于tcp协议的客户端,来连接服务器;
编写客户端的字符界面并提示客户进行业务的选择;
将客户的选择和输入的相关信息通过对象输出流发送给服务器;
服务器通过对象输入流接收客户端发来的消息并进行功能处理,将处理结果发送给客户端;
客户端通过对象输入流接收服务器的处理结果并给出提示;
(2)学员管理系统的功能
当项目启动时,将文件中的所有学员账户信息全部读取出来放到一个List集合中。
客户端输入要增加学员的用户名和密码信息,通过对象输出流发送给服务器。
服务器接收客户端发来的消息,判断集合中是否存在该学员账户信息并实现具体添加功能。
服务器将增加学员功能的处理结果发送给客户端,客户端给出对应的提示。
当项目退出时,将集合中的所有学员账户信息整体写入到文件中

02 在线考试系统的架构分析

03 学生信息管理系统

代码实现

服务器初始化和关闭–》服务器测试

客户端初始化和关闭–》客户端测试

客户端主界面绘制和功能实现(界面绘制–》管理员登录(用户+用户信息))

​ 管理员登录(引入客户端初始化和关闭)(传数据)

​ 连接成功后就没有反应了????服务器一直在等待客户端的信息

不报错的错误–>客户端先创建输出流再创建输入流

/**
 * @author liujunqiang
 * @create 2020-09-14 13:47
 * 编程实现:客户端的初始化和关闭操作
 */
public class ClientInitClose {
    Socket socket = null;
    ObjectInputStream objectInputStream = null;
    ObjectOutputStream objectOutputStream = null;
    /**
     * 自定义成员方法实现客户端的初始化操作
     */
    public void clientInit() throws IOException {
        //1.创建Socket对象并指定通信的ip地址和端口号
        Socket socket = new Socket(InetAddress.getLocalHost(), 6666);
        //2.使用输入输出流实现通信
        objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        objectInputStream = new ObjectInputStream(socket.getInputStream());
        System.out.println("连接服务器成功!");
        //3.关闭客户端
    }

list集合没有初始化之前是null,是不能调用方法的

list集合初始化后,size大小为0,是可以调用方法的


获取数据和保存数据出现序列化和反序列化问题

java.io.EOFException
	at java.base/java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2763)
	at java.base/java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:3258)
	at java.base/java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:873)
	at java.base/java.io.ObjectInputStream.<init>(ObjectInputStream.java:350)
	at com.xuebaqiang.server.ServerDao.serverGetUser(ServerDao.java:53)
	at com.xuebaqiang.server.ServerView.serverReceive(ServerView.java:47)
	at com.xuebaqiang.test.ServerTest.main(ServerTest.java:31)

在公司的电脑报这个错误,在自己的电脑报拒绝访问。修改为相对路径就可以访问了


合成复用原则:不能把值传进去了

package com.xuebaqiang.test;

import com.xuebaqiang.model.User;
import com.xuebaqiang.server.ServerDao;
import com.xuebaqiang.server.ServerInitClose;
import com.xuebaqiang.server.ServerView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author liujunqiang
 * @create 2020-09-14 13:40
 */
public class ServerTest {


    public static void main(String[] args) {
        ServerInitClose serverInitClose = null;
        List<User> users=new ArrayList<>(); //2个值
        ServerDao serverDao = null;
        try {
            //1.对服务器进行初始化操作
            serverInitClose = new ServerInitClose();
            serverInitClose.serverInit();
            //2.接收服务器端的数据并将数据处理后返回
            //4.声明ServerDao类型的引用指向该类型的对象
            serverDao = new ServerDao(users); //这里需要优化
            //将文件中的数据导入进来
            users=serverDao.serverGetUser();
            while (true) { //实现服务器不断的相应
                //3.声明ServerView类型的引用指向该类型的对象
                ServerView serverView = new ServerView(serverInitClose, serverDao,users);
                int i = serverView.serverReceive();
                if(0==i){
                    break;//退出循环
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            //将list中的学员信息保存到文件中
            try {
                serverDao.serverSaveUser(); //调用的话,按理上说应该是有两个值的
            } catch (IOException e) {
                e.printStackTrace();
            }
            //2.关闭服务器
            try {
                serverInitClose.serverClose();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

直接传入的话可以传入


什么叫合成复用原则:应该是传的能调方法的,不是让传带有数值的吧;

  • 复用的是方法,值穿不进去(例如:集合)

客户端关闭出现问题

Exception in thread "main" java.lang.NullPointerException
	at com.xuebaqiang.client.ClientInitClose.clientClose(ClientInitClose.java:37)
	at com.xuebaqiang.test.ClientTest.main(ClientTest.java:31)

diagrams 图标

查看继承关系图:


使用合成复用原则,集合类型中的数据是可以进去的,但是为什么上面的不行呢

  • 使用构造方法的时候创建后,调用的时候可以传进去;没有构建,之前传进去的数据为空;
  • 合成复用原则使用的时候感觉应该分为两种情况:1、不带数据的引用 2、带有数据的引用

第二阶段

任务一任务一_MySQL基础&SQL入门

DDL 操作 数据库
-- 创建数据库
CREATE DATABASE db1;
CREATE DATABASE db2 CHARACTER SET utf8;
-- 使用数据库
USE db2;
-- 查看正在使用的数据库  注意:这里使用的是select
SELECT DATABASE();
-- 查看所有数据库
SHOW DATABASES;

-- 将数据库db1的编码格式设置为utf-8
ALTER DATABASE db1 CHARACTER SET utf8;

image-20200916171259205

-- 查看当前数据库的基本信息
SHOW CREATE DATABASE db1;

image-20200916171523303

-- 删除数据库
DROP DATABASE db1;
DROP DATABASE db2;

对数据库的操作:

  • 创建数据库
  • 创建数据库并指明编码格式
  • 修改数据库编码格式
  • 使用数据库
  • 查看当前正在使用的数据库
  • 查看所有数据库
  • 查看数据库的基本信息
  • 删除数据库
-- 创建数据库
-- 创建数据库并指明编码格式
-- 修改数据库编码格式
-- 使用数据库
-- 查看当前正在使用的数据库
-- 查看所有数据库
-- 查看数据库的基本信息
-- 删除数据库

-- 创建数据库
CREATE DATABASE db1;
-- 创建数据库并指明编码格式
CREATE DATABASE db2 CHARACTER SET utf8;
-- 修改数据库编码格式
ALTER DATABASE db1 CHARACTER SET utf8;
-- 使用数据库
USE db2;
-- 查看当前正在使用的数据库
SELECT DATABASE();
-- 查看所有数据库
SHOW DATABASES;
-- 查看数据库的基本信息
SHOW CREATE DATABASE db1;
-- 删除数据库
DROP DATABASE db1;
DROP DATABASE db2;

注意事项:

  1. 创建数据库后要对创建的连接刷新后再使用;
  2. 编码格式是utf8,不是utf-8
  3. 修改库使用的是alter
  4. 除了使用数据库外,操作数据库的时候都要加上database
  5. 查看正在使用数据库的时候使用的是select,database后要加()
  6. 显示所有的数据库,显示的是多个数据库,不要忘记加s

DDL 操作 数据表
-- 创建表
CREATE TABLE test1(
	id INT,
	user_name VARCHAR(10)
);
-- 创建一个跟test1结构一样的表
CREATE TABLE test2 LIKE test1;
-- 查看test2的表结构
DESC test2;

-- 创建表
CREATE TABLE test1(
	id INT,
	user_name VARCHAR(10)
);
-- 创建一个跟test1结构一样的表
CREATE TABLE test2 LIKE test1;
-- 查看test2的表结构
DESC test2;
-- 查看所有表
SHOW TABLES;
-- 查看创建表的SQL语句
SHOW CREATE TABLE test2;
-- 删除表
DROP TABLE test1;
-- 如果存在,删除表
DROP TABLE IF EXISTS test2;
-- 修改表名
-- alter table rename test1 test01;
RENAME TABLE test1 TO test01;
-- 修改表的字符集
ALTER TABLE test01 CHARACTER SET gbk;
-- 修改表,向表中添加列
ALTER TABLE test01 ADD age INT;
-- 修改表中的数据类型或长度
ALTER TABLE test01 MODIFY age VARCHAR(10);
-- 修改表中的列名称
ALTER TABLE test01 CHANGE age t_date DATE;

注意事项:

  1. 创建表的时候,字段间使用,隔开;名在前,数据类型在后;

--  查询每个分类下的商品个数   
/*
	1.查询的表
	2.查询的条件 分组 统计
	3.查询的字段 分类 分类下商品个数信息
	4.表的连接条件
*/
SELECT 
	c.`cname`,
	COUNT(c.`cname`)
FROM `products` p 
RIGHT JOIN `category` c ON p.`category_id`=c.`cid`
GROUP BY c.`cname`


SELECT 
	c.`cname`,
	COUNT(p.`pid`)
FROM
-- 表连接
category c  LEFT JOIN products p ON c.`cid` = p.`category_id`
-- 分组
GROUP BY c.`cname`;

image-20200917140052358

image-20200917140104392

  • 使用左右外连接,count的选值很重要,使用左连接就先左边表的主键作为,右连接以右边的主键作为
  • count(左id,右id)

讲师表
		讲师ID 主键 int类型
		讲师姓名 VARCHAR类型
		讲师简介 VARCHAR类型
		讲师级别 char类型 高级讲师&首席讲师
		为讲师姓名添加索引
		
CREATE TABLE lagou_teacher(
	t_id INT PRIMARY KEY AUTO_INCREMENT,
	t_name VARCHAR(30),
	t_common VARCHAR(255),
	t_level CHAR(12)
)

讲师级别 char类型 高级讲师&首席讲师,使用几个字节合适??

  • 使用的是UTF-8

-- 根据课程号看分类
SELECT  s.s_name
FROM (
	SELECT * FROM lagou_course WHERE c_teacher_id = (
	SELECT t_id FROM lagou_teacher WHERE t_name='刘德华'
	)
) tea_cou tc LEFT JOIN lagou_subject s ON tc.c_subject_id = s.s_id;
  • 已经取了别名,不用再取了

4) 查询开发部与财务部所有的员工信息,分别使用子查询和表连接实现
-- 查询开发部与财务部所有的员工信息(子查询) (有点问题)
SELECT e.*,d.name
FROM `employee` e,`dept` d
WHERE e.dept_id=d.id  AND d.name IN ('开发部','财务部')
-- 表连接
SELECT e.*,d.name
FROM `employee` e LEFT JOIN `dept` d ON  e.dept_id=d.id
WHERE d.name='开发部' OR d.name='财务部'
  • 不知道为什么要使用子查询

SELECT e.*,d.name
FROM `employee` e,`dept` d
WHERE e.dept_id=d.id  AND d.name IN ('开发部','财务部')
  • 要是使用 where e.dept_id=d.id and d.name=‘开发部’ or d.name='财务部’跟上面的实现效果不同

SELECT e.*,d.*
FROM `employee` e,`dept` d
WHERE e.dept_id=d.id AND join_date>'2011-00-00'
  • 判断时间的话,使用‘ ’

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yku0h5e7-1602230781456)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200920165811613.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SoKOpaf6-1602230781457)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200920170459457.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6sFSlpm-1602230781459)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200920170827522.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-otxq2ZXW-1602230781461)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200920170900995.png)]


以后笔记重在记载联系的内容:


使用事务模拟转账操作
  • 查看态。
SHOW VARIABLES LIKE 'autocommit';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FXdc1ngw-1602230781462)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921225046534.png)]

  • 把 autocommit 改成 off;(手动提交)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KOkFZAsi-1602230781464)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921225207414.png)]


为什么同时打开两个窗口,查询的结果不一样???

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XXVIxPFA-1602230781466)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921225522400.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RprTvq1V-1602230781467)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921225945552.png)]

容易出错的地方:


mysql> SHOW VARIABLES LIKE 'autocommit';  -- 1、查询自动提交状态
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> start transaction;     -- 2、开始事务
Query OK, 0 rows affected (0.00 sec)

mysql> use db_user;			-- 3、使用的数据库
Database changed
mysql> update account set money=money-100 where name='tom';   -- 4、修改数据
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WeXyL8GL-1602230781470)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921230318923.png)]

mysql> commit;   -- 5、提交事务
Query OK, 0 rows affected (0.00 sec)

mysql>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BmKFh7qX-1602230781471)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921230417601.png)]

如果事务中,有某条sql语句执行时报错了,我们没有手动的commit,那整个事务会自动回滚


数据并发访问

相同的数据有多个事务进行同时访问呢造成的问题:‘


脏读:
  • 一个事务读取到另一个事务尚未提交的数据
  • 查询到的是未提交的数据
  • 解过有回滚了,那查出来的数据就是错误的了;
  • 使用的隔离级别:设置隔离级别为最低 读未提交

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SxyjDVxz-1602230781473)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921232651687.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYgMneM6-1602230781474)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921232814444.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-waK1c4RY-1602230781476)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921233446634.png)]

  • 回滚前查询的是 修改了但未提交了的数据(太心急了)
  • 回滚后查询的是 没有开启事务前的数据
  • 回滚前和回滚后查询的的数据是不一样的

窗口一:

mysql> use db_user;
Database changed
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update account set money=money-100 where name='tom';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update account set money=money+100 where name='jack';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

窗口二:

mysql> use db_user;
Database changed
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;
+----+------+-------+
| id | NAME | money |
+----+------+-------+
|  1 | tom  |  1000 |
|  2 | jack |  1000 |
+----+------+-------+
2 rows in set (0.00 sec)

mysql> select * from account;
+----+------+-------+
| id | NAME | money |
+----+------+-------+
|  1 | tom  |   900 |
|  2 | jack |  1100 |
+----+------+-------+
2 rows in set (0.00 sec)

mysql> select * from account;
+----+------+-------+
| id | NAME | money |
+----+------+-------+
|  1 | tom  |  1000 |
|  2 | jack |  1000 |
+----+------+-------+
2 rows in set (0.00 sec)

mysql>

解决脏读:
  • 脏读非常危险的,比如张三向李四购买商品,张三开启事务,向李四账号转入 500 块,然后打电
    话给李四说钱 已经转了。李四一查询钱到账了,发货给张三。张三收到货后回滚事务,李四的再
    查看钱没了。
    • 张三转钱 李四一看收到了钱—》发货
    • 张三收到货回滚 李四一看钱没有了
  • 解决方案
    将全局的隔离级别进行提升为: read committed
set global transaction isolation level read committed;

​ 查看时候设置成功:

select @@tx_isolation;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NjvaYZsu-1602230781481)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200921235333628.png)]

  • 为什么设置后没有改变呢?? 从新打开后设置就可以了,一个窗口设置后要关闭后才能再次设置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxwPkYFK-1602230781482)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200922000037132.png)]

  • 可以看到,脏读已经解决了

不可重复读演示

不可重复读: 同一个事务中,进行查询操作,但是每次读取的数据内容是不一样的

  • 同一事务中两次查询结果不一致的情况;
  • 我们可以考虑这样一种情况:
    比如银行程序需要将查询结果分别输出到电脑屏幕和发短信给客 户,结果在一个事务
    中针对不同的输出目的地进行的两次查询不一致,导致文件和屏幕中的结果不一致,银
    行工作 人员就不知道以哪个为准了
  • 不可重复读的情况:
    • 窗口二:开启事务,查询数据
    • 窗口一:开启事务,修改数据,再提交数据
    • 窗口二:再次查询数据,查询的结果与第一次不一样

笔记还不够完善,以后会更加完善!!!!谢谢导师!

第二阶段模块二

jdbc:
  • 为Java语言访问数据库提供一套标准的规范,可以为多种关系型数据库提供统一的访问
package com.lagou.demo01;

import java.sql.*;

public class JdbcSelect {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        //1.加载驱动(可省略)
        Class.forName("com.mysql.jdbc.Driver");
//        2.获取连接
        Connection connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/db_user?characterEncoding=UTF-8", "root", "123456");
//        3.获取sql语句执行平台
        Statement statement = connection.createStatement();
//        4.执行创建表的Sql
        String sql="select * from t_user";
//        5.增删改操作 使用executeUpdate,增加一张表
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            System.out.print(id+"   ");
            System.out.println(name);
        }
//        6.关闭资源
        statement.close();
        connection.close();

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wu8yF8p1-1602230781484)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200922215346570.png)]


为什么是注册驱动?–>jdbc3后就可以省略了

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

创建工具类(JdbcUtils)

因为获取数据库连接对象和关闭连接经常要用到(要重复的使用)

package com.lagou.demo01;

import java.sql.*;

public class JDBCUtils {
    private static final String DRIVERNAME="com.mysql.jdbc.Driver";
    private static final String USERNAME="root";
    private static final String PASSWORD="123456";
    private static final String URL="jdbc:mysql://localhost:3306/db_user?characterEncoding=utf-8";

    //第一次的第一次  加载驱动
    static{
        try {
            Class.forName(DRIVERNAME);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取数据库连接对象
     */
    public static Connection getConnection() {
        try {
            Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 关闭数据库资源对象
     */
    public static void closeJdbc(Connection connection, Statement statement){
        if(statement!=null && connection!=null){
            try {
                statement.close();
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 关闭数据库资源对象
     */
    public static void closeJdbc(Connection connection, Statement statement, ResultSet resultSet){
        if(statement!=null && connection!=null && resultSet!=null){
            try {
                resultSet.close();
                statement.close();
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

package com.lagou.demo01;

import com.alibaba.druid.util.JdbcUtils;
import org.junit.Test;

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

public class JDBCUtilsTest01 {

    @Test
    public void insertTest() {
        Connection connection=null;
        Statement statement=null;
        try {
            //1.获取连接
            connection = JDBCUtils.getConnection();
            //2.获取语句执行对象
            statement = connection.createStatement();
            //2.1编写sql语句
            String sql="insert into t_user values(default,'小伙子')";
            //3.执行sql语句
            int i = statement.executeUpdate(sql);
            System.out.println(i);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeJdbc(connection,statement);
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Zezq5S7-1602230781485)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200922220826874.png)]


SQL注入演示:
  • 造成的原因:用户输入的内容拼接到了SQL语句上;
  • 用户输入的内容作为了 SQL 语句语法的一部分,改变了 原有
    SQL 真正的意义
package com.lagou.demo02;

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

public class LoginTest {

    public static void main(String[] args) {
        Connection connection =null;
        Statement statement=null;
        ResultSet resultSet =null;
        try {
            //1、获取数据库连接对象
            connection = JDBCUtils.getConnection();
            //2、获取Statement对象
            statement = connection.createStatement();
            //3、提示并获取用户名和密码
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入用户名:");
            String username = scanner.nextLine();
            System.out.println("请输入密码:");
            String password = scanner.nextLine();
            ///4、用输入的用户名和密码对sql语句进行拼接
            //String sql="select * from jdbc_user where username='+username+' and password='+password+' ";
            String sql="select * from jdbc_user where username="+" '"+username+"' and password="+" '"+password+"' ";
            //5、执行sql
            resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                System.out.println("登录成功!");
            }else{
                System.out.println("登录失败!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeJdbc(connection,statement,resultSet);
        }

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-57L7Mcti-1602230781487)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200922223401268.png)]

select * from jdbc_user where username =  'abc'  and password =  'abc' or '1'='1'

abc’ or ‘1’='1


预处理对象
  • 防止依赖注入
  • 提高SQL的执行效率
package com.lagou.demo02;

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

public class PrepareTest {
    public static void main(String[] args) throws SQLException {
        //1、获取数据库连接
        Connection connection = JDBCUtils.getConnection();
        //3、提示并获取用户名和密码
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = scanner.nextLine();
        System.out.println("请输入密码:");
        String password = scanner.nextLine();
        //2、创建prepareStatement对象

        //2。1、编写动态SQL
        String sql="select * from jdbc_user where username=? and password=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //2.2、设置占位符参数
        preparedStatement.setString(1,username);
        preparedStatement.setString(2,password);
        //3、执行查询、处理结果集
        ResultSet resultSet = preparedStatement.executeQuery();
        if (resultSet.next()) {
            System.out.println("登录成功!");
        }else{
            System.out.println("登录失败!");
        }
        //4、关闭连接
        JDBCUtils.closeJdbc(connection,preparedStatement,resultSet);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1JfaQZA0-1602230781489)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200922224832377.png)]


事务相关API
  • 6.3 开发步骤
    1. 获取连接
    2. 开启事务
    3. 获取到 PreparedStatement , 执行两次更新操作
    4. 正常情况下提交事务
    5. 出现异常回滚事务
    6. 最后关闭资源
package com.lagou.demo02;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class AccountTest {

    public static void main(String[] args) {
        Connection connection=null;
        PreparedStatement preparedStatement=null;
        try {
            //1、获取数据库连接
            connection = JDBCUtils.getConnection();
            //2、开启事务
            connection.setAutoCommit(false);
            preparedStatement = connection.prepareStatement("update account set money=money-? where username=?");
            preparedStatement.setDouble(1,500.0);
            preparedStatement.setString(2,"tom");
            preparedStatement.execute();

            System.out.println(10/0);
            preparedStatement=connection.prepareStatement("update account set money=money+? where username=?");
            preparedStatement.setDouble(1,500.0);
            preparedStatement.setString(2,"jack");

            preparedStatement.execute();
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            JDBCUtils.closeJdbc(connection,preparedStatement);
        }


    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eA6cLR5M-1602230781491)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200922230822903.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTYPojxy-1602230781493)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200922230832347.png)]


连接池:
DBCP连接池
package com.lagou.pool;

import org.apache.commons.dbcp.BasicDataSource;

import java.sql.*;

public class DBCPUtils {

    //1.定义常量 保存数据库连接的相关信息
    public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
    public static final String URL = "jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8";
    public static final String USERNAME = "root";
    public static final String PASSWORD = "123456";

    //2.创建连接池对象
    public static BasicDataSource dataSource=new BasicDataSource();

    //3.使用静态代码块进行配置
    static{
        dataSource.setDriverClassName(DRIVERNAME);
        dataSource.setUrl(URL);
        dataSource.setUsername(USERNAME);
        dataSource.setPassword(PASSWORD);
    }

    //4.获取连接的方法

    public static Connection getConnection() throws SQLException {
        //从连接池中获取连接
        Connection connection = dataSource.getConnection();
        return connection;
    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if(con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

}

package com.lagou.pool;

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

public class DBCPTest {

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

        //1、从DBCP连接池中拿到连接
        Connection connection = DBCPUtils.getConnection();
//        2、获取sql语句执行平台对象
        Statement statement = connection.createStatement();
//        3、查询所有员工的姓名
        String sql="select ename from employee;";
        ResultSet resultSet = statement.executeQuery(sql);
//        4、处理结果集
        while (resultSet.next()) {
            String ename = resultSet.getString("ename");
            System.out.println(ename);
        }
//        5、释放资源
        DBCPUtils.close(connection,statement,resultSet);


    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x6xyh2Cs-1602230781494)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923213557615.png)]

出错的原因:没有导入驱动包:

mysql-connector-java-5.1.37-bin


运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YUnqQoPJ-1602230781496)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923214145971.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dXFq5DBR-1602230781497)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923214200173.png)]


为什么会出现这种情况???

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DsKD68UN-1602230781500)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923214430836.png)]


出现的原因:数据库添加数据的时候换行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mHywadqZ-1602230781501)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923215103351.png)]


C3P0连接池:
package com.lagou.pool;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;

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

public class C3P0Utils {

    //1.创建连接池对象 C3P0对DataSource接口的实现类
    //使用的配置是 配置文件中的默认配置
    //public static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    //使用指定的配置
    public static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");

    //获取连接的方法
    public static Connection getConnection() throws SQLException {

        return dataSource.getConnection();
    }

    //释放资源
    public static void close(Connection con, Statement statement){


        if(con != null && statement != null){
            try {
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if(con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}

package com.lagou.pool;

import java.sql.*;

public class C3p0Test {

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

//        1、获取连接
        Connection connection = C3P0Utils.getConnection();
//        2、获取预处理对象
        String sql="select * from employee where ename=?";
        PreparedStatement ps = connection.prepareStatement(sql);
//        3、设置占位符的值
        ps.setString(1,"林黛玉");
//        4.处理结果集
        ResultSet resultSet = ps.executeQuery();
        while (resultSet.next()) {
            int eid = resultSet.getInt("eid");
            String ename = resultSet.getString("ename");
            int age = resultSet.getInt("age");
            String sex = resultSet.getString("sex");
            double salary = resultSet.getDouble("salary");
            Date empdate = resultSet.getDate("empdate");
            System.out.println(eid+" "+ename+" "+age+" "+sex+" "+salary+" "+empdate);
        }
//        5.关闭资源
        C3P0Utils.close(connection,ps,resultSet);

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yB6o3spk-1602230781503)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923221313224.png)]


显示不出的数据的原因:

  • 插入数据的时候换行了
 ps.setString(1,"林黛\n" +
                "玉");
//        4.处理结果集

这样也不行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7EtcDZsJ-1602230781504)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923221700792.png)]

特别注意:插入数据的时候不要换行,否则会发生意想不到的错误


Druid连接池:

Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功
能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况。

package com.lagou.pool;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DruidUtils {

    //1.定义成员变量
    public static DataSource dataSource;

    //2.静态代码块
    static{
        try {
            //3.创建属性集对象
            Properties p=new Properties();
            //4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件
            //InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            //InputStream is = new FileInputStream("D://druid.properties");
            InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            //5. 使用Properties对象的 load方法 从字节流中读取配置信息
            p.load(inputStream);
            //6. 通过工厂类获取连接池对象
            dataSource = DruidDataSourceFactory.createDataSource(p);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接的方法
    public static Connection getConnection(){

        try {

            return dataSource.getConnection();

        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    //获取Druid连接池对象的方法
    public static DataSource getDataSource(){
        return dataSource;
    }


    //释放资源
    public static void close(Connection con, Statement statement){

        if(con != null && statement != null){
            try {
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if(con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

}

package com.lagou.pool;


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

public class TestDruid {

    // 需求 查询 薪资在3000 到 5000之间的员工的姓名
    public static void main(String[] args) throws SQLException {

        //1.获取连接
        Connection con = DruidUtils.getConnection();

        //2.获取Statement对象
        Statement statement = con.createStatement();

        //3.执行查询
        ResultSet resultSet = statement.executeQuery("select ename from employee where salary between 3000 and 5000");

        //4.处理结果集
        while(resultSet.next()){
            String ename = resultSet.getString("ename");
            System.out.println(ename);
        }

        //5.释放资源
        DruidUtils.close(con,statement,resultSet);
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mDPo44Rd-1602230781506)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923231813426.png)]

第一次:直接复制进去,不发执行;不知道什么原因;

配置文件的路径要怎么写呢??


使用文件输入流也可以获取文件,为什么还要使用反射机制要获取输入就对象呢??


DBUtils工具类的使用:

简化代码+使用连接池+不会对性能造成影响

1.创建QueryRunner(手动或自动)
2.占位符方式 编写SQL
3.设置占位符参数
4.执行

5.关闭资源

package com.lagou.test;

import com.lagou.pool.DruidUtils;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;

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

public class DBUtilsTest {
    public static void main(String[] args) throws SQLException {

        //1、创建QueryRunner对象(手动的方法)
        QueryRunner queryRunner = new QueryRunner();
//        2、编写SQL 已占位符的方式
        String sql="insert into employee values(?,?,?,?,?,?)";
//        3、设置占位符的参数
        Object[] param={null,"乔冠一",24,"男",10000,"1996-3-1"};
//        4、执行update方法
        Connection connection = DruidUtils.getConnection();
        int update = queryRunner.update(connection, sql, param);
//        5、关闭资源
        DbUtils.closeQuietly(connection);

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FuXs62Dh-1602230781508)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200923234259242.png)]


实现批处理
url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&rewriteBatchedStatements=true
package com.lagou.test;

import com.lagou.pool.DruidUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestBatch {

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

        Connection connection = DruidUtils.getConnection();
        String sql="insert into testBatch(uname) values(?)";
        PreparedStatement ps = connection.prepareStatement(sql);
        for (int i = 0; i < 100000; i++) {
            ps.setString(1,"谢邦书"+i);
            ps.addBatch();
        }
        long start = System.currentTimeMillis();
        ps.executeBatch();
        long end = System.currentTimeMillis();
        System.out.println(end-start);
        DruidUtils.close(connection,ps);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrWWfOy4-1602230781509)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200924000342294.png)]

  • 特别注意,写对了是粗体,写错 了不是粗体;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xRkKCOMv-1602230781511)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200924000420430.png)]

插入十万条数据还不到一秒


MySql元数据

除了表之外的数据都是元数据,可以分为三类


DTD约束

引入DTD:

  • 外部DTD
  • 内部DTD

第三阶段

第三阶段模块一


DOM操作

为了方便查找元素

大树(启发)—》顺藤摸瓜–》文档对象模型(提出)

DOM访问

  1. getElementById:通过id属性获得元素节点对象

  2. getElementsByName:通过name属性获得元素节点对象集

  3. getElementsByTagName:通过标签名称获得元素节点对象集

    tag : 标签

DOM修改

修改 HTML DOM 意味着许多不同的方面:

  1. 改变 HTML 内容
  2. 改变 CSS 样式
  3. 改变 HTML 属性
  4. 创建新的 HTML 元素
  5. 删除已有的 HTML 元素
  6. 改变事件(处理程序)

1.改变 HTML 内容
注意:.innerHTML=“

还真的不错!

”; 是等号;不是()

<body>
    <button onclick="changeP()">点击我试试</button>
    <script>
        function changeP(){
            document.getElementById("p1").innerHTML="<h1>还真的不错!</h1>";
        }
    </script>
    <p id="p1">这是一个不错的选择!</p>
</body>

image-20200930124655288

image-20200930124708289

2.改变 CSS 样式

body>
    <button onclick="changeP()">点击我试试</button>
    <script>
        function changeP(){
           var p1= document.getElementById("p1");
           p1.style.color="red";
           p1.style.fontSize="30px"
        }
    </script>
    <p id="p1">这是一个不错的选择!</p>
    
</body>

image-20200930125246569

3.改变 HTML 属性

有空的时候可以试试

  1. 创建新的 HTML 元素
  2. 删除已有的 HTML 元素
  3. 改变事件(处理程序)

添加节点

点击按钮,添加一张图片

<body>
    <button onclick="addImg()">添加一张图片</button>
    <script>
        function addImg(){
            var div=document.getElementsByTagName("div")[0];
            var img=document.createElement("img");
            img.setAttribute("src","img/logo.png");
            img.setAttribute("title","car");
            img.setAttribute("id","cat");
            div.appendChild(img);
        }
    </script>
    <div></div>
</body>

image-20200930154322340


删除节点

<!-- 点击按钮,改变一个 <h2> 元素的 HTML 内容 : -->
    <!-- 点击按钮,改变一个<h2>的 HTML 样式 -->
    <!-- 添加节点:点击图片添加一张图片 -->
    <!-- 删除节点:点击按钮,把上面刚创建的图片从页面上删除 -->
    <!-- 替换节点:点击按钮,把上面刚创建的图片替换成另一张 -->
<body>
    <!-- 点击按钮,改变一个 <h2> 元素的 HTML 内容 : -->
    <!-- 点击按钮,改变一个<h2>的 HTML 样式 -->
    <!-- 添加节点:点击图片添加一张图片 -->
    <!-- 删除节点:点击按钮,把上面刚创建的图片从页面上删除 -->
    <!-- 替换节点:点击按钮,把上面刚创建的图片替换成另一张 -->
    <button onclick="addImg()">添加一张图片</button>
    <script>
        function addImg(){
            var div=document.getElementsByTagName("div")[0];
            var img=document.createElement("img");
            img.setAttribute("src","img/logo.png");
            img.setAttribute("title","lagou");
            img.setAttribute("id","lagou");
            div.appendChild(img);
            //我的疑问:设置的id值相同,为什么可以插入多条图片呢
            //在第一个位置添加也可以理解
        }
    </script>
    <div></div>
    <!-- 删除节点:点击按钮,把上面刚创建的图片从页面上删除 -->
    <button onclick="subImg()">删除图片</button>
    <script>
        function subImg(){
            var img=document.getElementById("lagou");
            //img.parentElement.removeChild("img");  //  这样写是错误的 
            //img.parentElement.removeChild(img);
            img.parentNode.removeChild(img); //父节点和父元素是有区别的
            //我的疑问:删除为什么也可以多次删除(是不是ID可以有多个相同的呢?)
            //理解为获取第一个,删除第一个的话可以理解
        }
    </script>
    <!-- 替换节点:点击按钮,把上面刚创建的图片替换成另一张 -->
    <button onclick="changeImg()">替换图片</button>
    <script>
        function changeImg(){
            var img=document.getElementById("lagou");
            // 方式一:这样只可以替换第一张图片
            // img.setAttribute("src","img/1.jpg");
            // img.setAttribute("width","400px");
            //方式二:
            //img.parentNode.replaceChild(img);//方法使用错误
            var img2=document.createElement("img");
            img2.setAttribute("src","img/2.jpg");
            img2.setAttribute("width","400px");
            img.parentNode.replaceChild(img2,img);//使用的时候看好传入的参数;再使用
            //我的疑问:这样的方式都可以将图片替换
            //这理解不了——》替换不应该是替换的第一个吗,永远是第一个
        }
    </script>
</body>

4.3.5 事件冒泡

点击子div,先子后父;

点击父div,只会触发父div;

<style>
    #father {
        width: 100px;
        height: 100px;
        background: yellow;
    }
    #child {
        width: 50px;
        height: 50px;
        background: orange;
    }
</style>

<body>
    <div id="father">父元素
        <div id="child">子元素</div>
    </div>
    <script>
        document.getElementById("father").addEventListener("click",function(){
            alert("点击父元素"+this.id)
        })
        document.getElementById("child").addEventListener("click",function(){
            alert("点击子元素"+this.id)
        })
    </script>
</body>
</html>

取消冒泡事件

<body>
    <div id="father">父元素
        <div id="child">子元素</div>
    </div>
    <script>
        document.getElementById("father").addEventListener("click",function(){
            alert("点击父元素"+this.id)
        })
        document.getElementById("child").addEventListener("click",function(e){
            e.stopPropagation(); //取消冒泡事件
            alert("点击子元素"+this.id)
        })
    </script>
</body>
</html>

4.3.6 事件捕获

点击子div,先父后子;

点击父div, 只有父;

<body>
    <div id="father">父元素
        <div id="child">子元素</div>
    </div>
    <script>
        document.getElementById("father").addEventListener("click",function(){
            alert("点击父元素"+this.id)
        },true)
        document.getElementById("child").addEventListener("click",function(){
            alert("点击子元素"+this.id)
        },true)
    </script>
</body>
</html>

事件冒泡和事件捕获的区别:在事件的后面加上true;


4.4 面向对象OOP(三种创建对象的方式)

1、使用Object创建通用对象

<body>
    <script>
        var cat=new Object();
        cat.name='小苗';
        cat.age=2;
        cat.say=function(){
            console.log("我叫"+this.name+",年龄为:"+this.age)
        }
        cat.say();
    </script>
</body>
</html>

2、使用构造函数

 function User(name,age){
            this.name=name;
            this.age=age;
            this.say=function(){
                console.log("我叫"+this.name+",年龄为:"+this.age);
            }
        }
        var user=new User("zhgasdf",23); //创建对象,调用构造
        user.say();

3、使用直接量

 <script>
        // var cat=new Object();
        // cat.name='小苗';
        // cat.age=2;
        // cat.say=function(){
        //     console.log("我叫"+this.name+",年龄为:"+this.age)
        // }
        // cat.say();
        function User(name,age){
            this.name=name;
            this.age=age;
            this.say=function(){
                console.log("我叫"+this.name+",年龄为:"+this.age);
            }
        }
        var user=new User("zhgasdf",23); //创建对象,调用构造
        user.say();
        // 使用直接量的方式
        var dog={
            name: "狗蛋",//注意是:
            age: 2,
            say: function(){  //注意也是:
                console.log("我叫"+this.name+",年龄为:"+this.age);
            }
        };
        dog.say();
    </script>

4.5 JSON

传递数据的习惯(方式)—》方式的不同,解析会比较麻烦,不知道用哪种方式解析—》Json格式

json格式:

{
属性1:值1,
属性2:值2,
。。。。
}

<script>
    var json1 = { username: "吕布", age: 31 };
    console.log("姓名:" + json1.username + ",年龄:" + json1.age + "岁");
    // json数组
    var josnarr = [{ name: "貂蝉", age: 18 }, { name: "小乔", age: 17 }];
    console.log("貂蝉" + josnarr[0].age + "岁了");
    console.log("小乔" + josnarr[1].age + "岁了");
    // 复杂的json对象
    var long = {
        name: "赵云",
        sex: "男",
        hobby: ["玉兰白龙驹", "龙胆亮银枪", "青釭剑"]
    };
    console.log(long.name + "的主攻武器:" + long.hobby[1]);
</script>

  1. BOM操作

    就是javascript对浏览器的一些常规操作的方法

5.1 window对象

 <script>
        function lagou(){
            //window.open("http://lagou.com","拉钩教育","width=400px,height=300px,left=200px,top=100px")//注意属性写在“”内
            window.open("http://lagou.com","拉钩教育","fullscreen=yes")//注意:大小跟打开窗口的大小相同
        }
    </script>

5.1.1 screen屏幕对象

我想知道我的电脑屏幕多大?实际上,得到的就是分辨率

<body>
    <button onclick="getScreen()">获取屏幕分辨率</button>
    <script>
        function getScreen(){
            alert("屏幕的分辨率:"+window.screen.width+"|"+window.screen.height)
        }
    </script>
</body>
</html>

5.1.2 location定位(location的三种用法)

<body>
    <button onclick="getUrl()">获取utl</button>
    <script>
        function getUrl(){
           // console.log("当前url:"+location.getUrl);//是错误的
           console.log("当前url:"+location.href); //是没有() 的
           location.reload();  //是有() 的
           location.href="http://www.baidu.com";//跳转页面,也可以使用a标签跳转
        }
    </script>
</body>

5.1.3 history浏览器历史
history对象会记录浏览器的痕迹

<body>
    <button onclick="backLast()">返回上一页面</button>
    <script>
        function backLast(){
            //方式一
           // history.go(-1); //()中不能是其他的
           //方式二
           history.back();

        }
    </script>
</body>
</html>

5.1.4 navigator 导航(了解)
window.navigator 对象包含有关访问者浏览器的信息;

例如:浏览器的名称,浏览器的版本。

<script>
    var str = "";
    str += "<p>浏览器的代号:"+ navigator.appCodeName +"</p>";
    str += "<p>浏览器的名称:"+ navigator.appName+"</p>";
    str += "<p>浏览器的版本:"+ navigator.appVersion+"</p>";
    str += "<p>硬件平台:"+ navigator.platform+"</p>";
    str += "<p>用户代理:"+ navigator.userAgent +"</p>";
    str += "<p>启用Cookies:"+navigator.cookieEnabled+"</p>";
    
    document.write(str);
</script>

5.1.5 存储对象
用起来和我们在java中map很相似,都是键值对的方式存数据

5.1.5.1 本地存储 localStorage
在关闭窗口或标签页之后将会删除这些数据

  • 保存数据
  • 提取数据
  • 删除数据
    localStorage.setItem(“name”,“curry”);
    localStorage.getItem(“name”);
    localStorage.removeItem(“name”);

三种存、取方式

// 三种方式保存数据
localStorage["a"] = 1;
localStorage.b = 2;
localStorage.setItem("c",3);
        
// 查看数据类型
console.log( typeof localStorage["a"] )
console.log( typeof localStorage["b"] )
console.log( typeof localStorage["c"] )
// 第一种方式读取
var a = localStorage.a;
console.log(a);
        
// 第二种方式读取
var b = localStorage["b"];
console.log(b);
// 第三种方式读取
var c = localStorage.getItem("c");
console.log(c);

5.1.5.2 会话存储 sessionStorage
会话,就是保持浏览器别关闭。
关闭浏览就等于结束了一次会话。
开启浏览器就意味着创建了一次会话。

从新打开一个标签页,数据接着加;

保存数据
提取数据
删除指定键的数据
sessionStorage.setItem("name", "klay");
var lastname = sessionStorage.getItem("name");
sessionStorage.removeItem("name");
删除所有数据
sessionStorage.clear();

案例:记录点击了几下按钮

<body>
    <button onclick="addOne()">点击加一</button>
    <h3 id="result"></h3>
    <script>
        function addOne(){
            if(sessionStorage.getItem("clickCount")){
                sessionStorage.setItem("clickCount", Number(sessionStorage.getItem("clickCount")) + 1);
            }else{
                sessionStorage.setItem("clickCount", 1);
            }
            document.getElementById("result").innerHTML="已经点击了"+sessionStorage.getItem("clickCount")+"次";
        }
       
    </script>
</body>
</html>

会话:只要浏览器不关闭,刷新后(刷新之前就会将值记录下来);点击加一:不会从新开始


5.2 计时操作
5.2.1 周期性定时器 setInterval
setInterval(1,2):周期性触发代码exp (常用)
1:执行语句
2:时间周期,单位为毫秒

  • 案例:闪烁的字体 (1秒1变色)
<body>
    <h1 id="title">拉勾网:极速入职</h1>
    <script>
      var colors = ["red","blue","yellow","pink","orange","black"];
      var i = 0;
      function bian(){
        document.getElementById("title").style.color = colors[i++];
        if(i == colors.length){
            i = 0; // 颜色重新开始
        }
      }
      setInterval(bian,100); // 每隔0.1秒,执行一次bian函数
    </script>
</body>
  • 案例:在闪烁字体的基础上扩展,闪烁的电子时钟
  <body>
       <h1 id="title"></h1>
    <script>
      var colors = ["red","blue","yellow","pink","orange","black"];
      var i = 0;
      function bian(){
        document.getElementById("title").style.color = colors[i++];
        if(i == colors.length){
            i = 0; // 颜色重新开始
        }
      }
      function time(){
        var d = new Date();
        var str = d.getFullYear()+"年"+(d.getMonth()+1)+"月"+d.getDate()+"号 
"+d.getHours()+""+d.getMinutes()+""+d.getSeconds()+"秒";
        document.getElementById("title").innerHTML = str;   
      }
      setInterval(bian,100); // 每隔1秒,执行一次变色函数bian
      setInterval(time,1000); // 每隔1秒,执行一次时间函数time
    </script>
  </body>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值