JavaSE第一阶段
JavaSE第一阶段模块五
任务二:新特性
第二十二章 新特性
22.1 Java8的新特性
- 该版本于2014年3月发布,
- 是自Java5以来最具革命性的版本,
- 这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。
22.1.2 函数式接口
- 接口中只有一个抽象方法
- Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会
报错。 - Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:
点击它
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)模块化的优势
- 减少内存的开销。
- 可简化各种类库和大型应用的 开发和维护。
- 安全性,可维护性,提高性能。
什么意思???? 可以使用java8运行java11的版本
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 软件的需求分析
在线考试系统的主要功能分析如下:
- (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集合中。
客户端输入要增加学员的用户名和密码信息,通过对象输出流发送给服务器。
服务器接收客户端发来的消息,判断集合中是否存在该学员账户信息并实现具体添加功能。
服务器将增加学员功能的处理结果发送给客户端,客户端给出对应的提示。
当项目退出时,将集合中的所有学员账户信息整体写入到文件中
代码实现
服务器初始化和关闭–》服务器测试
客户端初始化和关闭–》客户端测试
客户端主界面绘制和功能实现(界面绘制–》管理员登录(用户+用户信息))
管理员登录(引入客户端初始化和关闭)(传数据)
连接成功后就没有反应了????服务器一直在等待客户端的信息
不报错的错误–>客户端先创建输出流再创建输入流
/**
* @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;
-- 查看当前数据库的基本信息
SHOW CREATE DATABASE db1;
-- 删除数据库
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;
注意事项:
- 创建数据库后要对创建的连接刷新后再使用;
- 编码格式是utf8,不是utf-8
- 修改库使用的是alter
- 除了使用数据库外,操作数据库的时候都要加上database
- 查看正在使用数据库的时候使用的是select,database后要加()
- 显示所有的数据库,显示的是多个数据库,不要忘记加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.查询的表
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`;
- 使用左右外连接,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 开发步骤
- 获取连接
- 开启事务
- 获取到 PreparedStatement , 执行两次更新操作
- 正常情况下提交事务
- 出现异常回滚事务
- 最后关闭资源
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访问
-
getElementById:通过id属性获得元素节点对象
-
getElementsByName:通过name属性获得元素节点对象集
-
getElementsByTagName:通过标签名称获得元素节点对象集
tag : 标签
DOM修改
修改 HTML DOM 意味着许多不同的方面:
- 改变 HTML 内容
- 改变 CSS 样式
- 改变 HTML 属性
- 创建新的 HTML 元素
- 删除已有的 HTML 元素
- 改变事件(处理程序)
1.改变 HTML 内容
注意:.innerHTML=“还真的不错!
”; 是等号;不是()
<body>
<button onclick="changeP()">点击我试试</button>
<script>
function changeP(){
document.getElementById("p1").innerHTML="<h1>还真的不错!</h1>";
}
</script>
<p id="p1">这是一个不错的选择!</p>
</body>
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>
3.改变 HTML 属性
有空的时候可以试试
- 创建新的 HTML 元素
- 删除已有的 HTML 元素
- 改变事件(处理程序)
添加节点
点击按钮,添加一张图片
<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>
删除节点
<!-- 点击按钮,改变一个 <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>
-
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>