文章目录
- 1.lambda表达式标准语法:()->{}
- 2.lambda表达式简略语法:可推导即可省略
- 3.lambda表达式原理:lambda效率比匿名内部类高
- 4.两个函数式接口:Consumer ,Predicate
- 5.Stream流引入: list.stream().filter
- 6.Stream流的获取:.stream(),Stream.of(array)
- 7.Stream流的终结方法:list.stream().count()
- 8.Stream流的拼接方法:Stream.concat
- 9.File类三个构造方法和获取方法:\
- 10.File的判断/创建/删除/列举方法:listFiles
- 11.FileOutputStream两个构造:OIWR,IO流(内存为中心)
- 12.OutputStream的基本方法:追加和换行
- 13.FileInputStream两个构造:一次读一个字节或字节数组
- 14.文件复制(重要):System.currentTimeMillis(),fis.read,fos.write
- 15.编码表与字符流:编码/解码/乱码
- 15.1 编码:字节映射为字符。第一种编码:ascii码。
- 15.2 乱码:FileReader(字符流)= FileInputStream(字节流 )+ 编码表
- 15.3 linux文件编码转换:iconv,enca
- 15.4 Linux中文字符集:/etc/locale.conf
- 15.5 Oracle的字符集:NLS_LANG
- 服务端的字符集:Oracle数据库实例创建后,如果没有开始业务运行,可修改字符集,如果已经业务化运行,不建议修改字符集,会造成数据中的汉字乱码
- 客户端的字符集:Oracle客户端的字符集必须与服务端相同,否则中文会出现乱码,客户端的字符集由NLS_LANG环境变量控制
- 16.flush和close方法:.getBytes()
- 17.IO流异常处理:字符流用于读写文本文件,字节流outputStream(内存->硬盘:写,以内存为中心),BOP
- 18.字节/字符缓冲流:readLine
- 19.案例_出师表:\ \ . ,content = map.get(i)
- 20.转换流:字节字符的中间流
- 21.序列化流:oos.writeObject,Serializable
- 22.打印流:最后一个流,字符数据(文本文件)的读或写(不是读写即复制,复制用字节流),用字符流方便
- 23.Properties集合:Hashtable 与HashMap 区别,p.load,toString
- 24.tcp通讯:ip相当于小区,port相当于门牌号
- 24.1 单向:一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个
- 24.2 双向:socket中介
- 25.多线程优化服务端与UUID:is和fos
- 26.Class对象三种获取方式:Class.forName(" ")
- 27.反射:clazz.newInstance()
- 28.类加载器:.class.getClassLoader(),引扩应,双亲委派
- 29.设计模式:动态代理比普通代理的好处在于不用事先定义类,代理类在运行时动态生成 (反射)
- 29.1 动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)
- 29.2 模板:类里有抽象方法必须抽象类
- 29.3 单例:某个类只能有唯一的一个实例对象
- 29.4 工厂:工厂类(一个工厂),工厂接口(一车一工厂),clazz.newInstance()
- 30.约束:word/txt(和xml一样都没有格式约束)传输数据,但xml格式规范
- 30.1 DTD约束:配置文件
- 30.2 Schema约束:命名空间
- 31.nginx的conf文件:以前网络编程中B/S架构中服务器是用socket写,用文件输入流读一个文件,读到后socket通过outputstream写出去,这些过程有了nginx后再也不用写
- 31.1 nginx反向代理与负载均衡:nginx是被广泛使用的网页服务器,在www.163的F12的Network中Headers中有server:nginx,使用nginx首当其冲,做反向代理,将请求分发到公司的多台服务器上
- 31.2 location匹配方式:location / 默认匹配所有请求,相当于第四优先级(最弱)
- 31.3 反向代理写法:http80
- 31.4 负载均衡写法:基于反向代理
- 32.服务器:软件为tomcat或nginx,程序为servlet
- 33.servlet:多态,web.xml
- 34.request对象:.getParameterMap(),post请求中文参数乱码
- 34.1 请求转发:request表面上获取请求数据,还有快递员身份
- 34.2 登陆案例:成功和失败页面相当于两个模块
- 35.response对象:text/html,重定向(.setStatus,.setHeader)
- 35.1 文件下载:request/response
- 36.cookie:再次时请求头携带cookie到服务端
- 36.1 登陆案例_记住我:js访问浏览器数据用document
- 37.session:根据sessionid,服务器才能找到session(session对象在服务器内存中增加压力)
- 37.1 登录案例_验证码:request.getSession().setAttribute("code",sb.toString())
- 38.MVC:Spring_IOC(对象创建交给spring工厂),Spring_AOP(基于动态代理功能增强)。Runapplication启动类:控制层(UserController),业务层(UseService,UseServiceImpl),持久层(User即pojo,UseMapper.xml,UseMapper接口)
- 39.RPC:远程过程调用,我这台电脑调用一个函数(这函数在另一个电脑上执行),像是本地正常运行函数一样,不需要关注远程(网络连接,断开,传数据)细节
- 40.ajax:xhr对象,$.,浏览器输入框加载脚本
- 41.json:new ObjectMapper()
- 41.1 json在前端和后端中表达:json在js中的表达.html(前端)
- 42.ajax和json综合:ajax_json_union.html
1.lambda表达式标准语法:()->{}
package com.itheima01.boot;
/*
* 函数式编程 语法 (JDK8): 函数(公式) -> java方法 。作用: 简化代码编写
* 1. lambda表达式: 标准语法/简略语法
* 2. 运用: Stream流
*/
public class BootDemo {
public static void main(String[] args) {
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
//lambda表达式
new Thread(
() -> {
System.out.println(Thread.currentThread().getName());
}
).start();
}
}
package com.itheima02.lambda;
/*
lambda表达式 -> 函数式编程(不在乎什么对象, 在乎是什么方法)
方法重要三部分: 参数列表、方法体、返回值
* 标准语法: (参数列表)
* -> : lambda符号,连接作用
* { 方法体,返回值 }
*/
public class StandardDemo {
public static void main(String[] args) {
Cooker cooker = new Cooker() { //匿名内部类 ,和下面的method()无关
@Override
public void cook() {
System.out.println("猪肉炖粉条");
}
};
method(cooker);
//11111111111111111111111111111111111111111111111111111111111111111
Cooker cooker2 = () -> {
System.out.println("小鸡炖蘑菇");
};
method(cooker2);
}
private static void method(Cooker cooker) { //参数类型是接口 -> 多态。调用此方法必须传入此接口的实现类对象。
// System.out.println(1); //1 先于 猪肉炖粉条 打印
cooker.cook(); //父类调用方法执行子类重写方法
}
}
interface Cooker{
void cook(); //这方法无参无返回
}
package com.itheima02.lambda;
/*
* lambda表达式:在乎的方法: 拿什么参数去做什么事情,返回什么结果。
* (参数列表) -> { return xx; }
*/
public class StandardDemo02 {
public static void main(String[] args) {
Calculator c1 = new Calculator() {
@Override
public int calc(int c, int d) {
return c + d;
}
};
method(c1);
//1111111111111111111111111111111111111111111111111111111111111111
Calculator c2 = (int o1,int o2) -> {
return o1 - o2;
};
method(c2);
}
//1111111111111111111111111111111111111111111111111111111111111111
private static void method(Calculator calculator) {
int result = calculator.calc(1, 2); //父类调用方法执行子类重写方法
System.out.println(result);
}
}
interface Calculator{
int calc(int a, int b); //有参有返回
}
package com.itheima03.prefix;
/*
* lambda表达式的使用前提: 1. 接口 : 有且仅有一个抽象方法 (需要被重写) 的接口
* 2. 上下文推导
* lambda表达式可以看成 极为简化的 匿名内部类对象
* 接口的实现 (函数: 方法)
*/
public class PrefixDemo {
public static void main(String[] args) {
Cooker c1 = new Cooker() {
@Override
public void cooker() {
System.out.println("翠花上酸菜");
}
};
method(c1);
//11111111111111111111111111111111111111111111111111111111111111111
/*
* 匿名内部类对比
* lambda表达式 : 1. 省略了new + 接口名 (因为上下文推导)
* 2. 省略了方法名 (因为接口只有一个抽象方法,所以方法名不重要)
*/
//上文 (Cooker c2 = 是上文 ,() ->...是下文) 推导
Cooker c2 = () -> {
System.out.println("蚂蚁上树");
};
method(c2);
//111111111111111111111111111111111111111111111111111111111111111111
//下文推导。上下文有其一即可
method( () -> {
System.out.println("蚂蚁上树");
});
}
private static void method(Cooker cooker) {
cooker.cooker();
}
}
interface Cooker{
void cooker(); //有且仅有一个抽象方法
}
package com.itheima03.prefix;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
* lambda表达式的运用:1. Runnable接口
* 2. Comparator接口
*/
public class UseDemo {
public static void main(String[] args) {
// method01();
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,5,3,2,4,1);
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(list); //[1,2,3,4,5]
//11111111111111111111111111111111111111111111111111111111111111111111111111
Collections.sort(list,(Integer o1,Integer o2) -> {
return o2 - o1;
});
System.out.println(list); //[5,4,3,2,1]
}
//11111111111111111111111111111111111111111111111111111111111111111111111111
private static void method01() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": 嘻嘻"); //Thread-0:嘻嘻
}
}).start();
//1111111111111111111111111111111111111111111111111111111111111111111111111
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName() + ":哈哈"); //Thread-1:哈哈
};
new Thread(runnable).start();
//1111111111111111111111111111111111111111111111111111111111111111111111111
new Thread(()->{
System.out.println(Thread.currentThread().getName() + ":呵呵"); //Thread-2:呵呵
}).start();
}
}
2.lambda表达式简略语法:可推导即可省略
package com.itheima04.simple;
/*
* 1. 参数类型可以省略 (接口抽象方法只有一个,参数列表只有一个,什么类型不需要特地声明)
* 2. 如果方法体中只有一行代码,那么 {}和return和; 都可以省略
* 3. 如果参数列表 有且只有一个参数,那么()可以省略
*/
public class SimpleDemo {
public static void main(String[] args) {
//lambda编写: 拿什么东西去做什么事,结果?
method((int a,int b)-> { return a + b;});
method((a,b) -> {return a+b;}); //可以
method((a,b) -> a+b); //可以,如上的省略
method((a,b) -> {
int sum = a+b;
return sum; //两句不能去掉{}..return..
});
//1111111111111111111111111111111111111111111111111111111111111111111111111111
method02((String cai) -> {
System.out.println("开水煮" + cai); //子类重写的方法 //开水煮大白菜
});
method02((cai) -> System.out.println("醋溜" + cai));
method02(cai -> System.out.println("韩式泡" + cai)); //空参必须写()
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111
private static void method02(Cooker cooker) {
cooker.cook("大白菜"); //无返回值,父类调用方法执行子类重写的方法,此时cai="大白菜"。这行是主体。
}
private static void method(Calculator calculator) {
int result = calculator.calc(1, 2);
System.out.println(result);
}
}
interface Calculator{
int calc(int a,int b);
}
interface Cooker{
void cook(String cai);
}
package com.itheima04.simple;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
* lambda表达式简略语法运用
* 1. Runnbale接口
* 2. Comparator
*/
public class UseDemo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("嘻嘻");
}
}).start();
new Thread(() -> System.out.println("呵呵")).start();
//11111111111111111111111111111111111111111111111111111111111111111
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,5,3,4,2,1);
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(list);
//11111111111111111111111111111111111111111111111111111111111111111
Collections.sort(list,(o1,o2) -> o2 -o1);
System.out.println(list);
}
}
3.lambda表达式原理:lambda效率比匿名内部类高
package com.itheima05.theory;
import java.util.ArrayList;
import java.util.List;
/*
* lambda表达式和匿名内部类: 0.lambda表达式 可以算是 一种 匿名内部类的 简化语法
* 1.lambda表达式能不能完全替代匿名内部类?不能
* 1.lambda表达式只能用在(有且仅有一个抽象方法需要被重写的)接口
* 2.匿名内部类(接口比较多,类实际上也可以)
* 2.lambda表达式是不是匿名内部类的语法糖(原理完全相同,语法更为简练称为语法糖)? 不是
* 1.匿名内部类 是 类
* 2.lambda表达式不是 类,是可以替代对象 的函数
*/
public class LambdaDemo { // LambdaDemo.class
public static void main(String[] args) {
Runnable runnable = new Runnable() { // LambdaDemo$1.class(编译生成) ,所以匿名内部类是类。idea中run集成了编译和运行
@Override
public void run() {
System.out.println("嘻嘻");
}
};
//111111111111111111111111111111111111111111111111111111111111111111111111
Runnable runnable2 = new Runnable() { // LambdaDemo$2.class
@Override
public void run() {
System.out.println("嘻嘻2");
}
};
//接口类型 引用 = 接口实现类对象(看成,但实际上不是) //没有.class文件生成
Runnable runnable3 = () -> System.out.println("呼呼");
}
}
4.两个函数式接口:Consumer ,Predicate
package com.itheima06.function;
import java.util.Comparator;
/*
* 函数式接口: 1. 定义 : 有且仅有一个抽象方法(需要被重写)的接口
* 2. 作用 : 支持函数式编程(A.lambda表达式 B.方法引用)
* 3. 已学习 : Runnbale , Comparator两个函数式接口
* 4. 注解: @FunctionalInterface
* 写在接口声明上, 检查此接口是否是函数式接口,如果不是编译报错
*/
public class FunctionDemo {
public static void main(String[] args) {
// Runnable
}
}
@FunctionalInterface
interface A{
void eat(); //需要重写的抽象方法,只一个
String toString(); // 不需要重写的抽象方法
static void method01(){ //不影响@FunctionalInterface,静态方法一定不能被重写
}
default void method02(){
}
}
//1111111111111111111111111111111111111111111111111111111111111
interface B{
String toString();
}
//Object和接口B中拥有相同方法,C先继承Object的方法,当成接口B的重写
class C implements B{ //class C extends Object implements B //class C 都可以
@Override
public String toString() { //重写也可以
return super.toString();
}
}
package com.itheima06.function;
import java.util.function.Consumer;
/*
* JDK8 : java.util.function包提供了很多函数式接口 -> 函数式编程(Stream流)
* 1. Consumer : 消费者
* public interface Consumer<T> {
* void accept(T t); //有参无返回
* }
* 2. Predicate
*/
public class FunctionDemo02 {
public static void main(String[] args) {
method(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("水煮" + s);
}
});
method(s -> System.out.println("清蒸" + s));
// 水煮大白菜
// 清蒸大白菜
}
//11111111111111111111111111111111111111111111111111111111111111
private static void method(Consumer<String> consumer) {
consumer.accept("大白菜"); //父类引用调用方法执行子类重写的方法
}
}
package com.itheima06.function;
import java.util.function.Predicate;
/*
* 2. Predicate : 推断
* public interface Predicate<T> {
* boolean test(T t); //有参有boolean返回值
* }
*/
public class FunctionDemo03 {
public static void main(String[] args) {
method(new Predicate<String>() { //判断参数字符串 长度 是否超过5
@Override
public boolean test(String s) {
// if(s.length() > 5){
// return true;
// }
// return false;
return s.length() > 5; //可以。且只有一句
}
});
method(s->s.length() > 5);
// false
// false
}
//11111111111111111111111111111111111111111111111111111111111111111
private static void method(Predicate<String> predicate) {
boolean result = predicate.test("july");
System.out.println(result);
}
}
5.Stream流引入: list.stream().filter
package com.itheima07.boot;
import java.util.ArrayList;
import java.util.List;
// 代码冗余: 1. 循环太多 2. 判断太多
public class StreamBootDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
//张集合
List<String> zhangList = new ArrayList<>(); //空
for (String name : list) {
if (name.startsWith("张")) {
zhangList.add(name);
}
}
//短集合 : 张集合基础上,只要名字3个
List<String> shortList = new ArrayList<>(); //空
for (String name : zhangList) {
if (name.length() == 3) {
shortList.add(name);
}
}
for (String name : shortList) { //遍历打印
System.out.println(name);
}
}
}
package com.itheima07.boot;
import java.util.ArrayList;
import java.util.List;
//Stream 流: 1. 基于函数式编程延伸出来的一种用法。 2. 作用: 简化 集合数据 处理过程中的代码冗余问题
public class StreamBootDemo02 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.stream().filter(name -> name.startsWith("张")) //name就是集合中的每个元素
.filter(name -> name.length() == 3).forEach(name-> System.out.println(name));
}
}
6.Stream流的获取:.stream(),Stream.of(array)
package com.itheima08.stream;
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.util.*;
import java.util.stream.Stream;
/*
* Stream 流: Stream流对象的获取:
* 1. 集合:Collection接口 默认方法 stream()
* 2. 数组:Stream.of(T...); 使用这个
* Stream.of(T);
*/
public class StreamGetDemo {
public static void main(String[] args) {
Collection<String> coll = new HashSet<>();
Stream<String> stream = coll.stream();
//111111111111111111111111111111111111111111111111111111111111111111111111111
HashMap<String, String> map = new HashMap<>();
// Set set = map.keySet();
// set.stream();
Stream<String> stream1 = map.keySet().stream();
Stream<Map.Entry<String, String>> stream2 = map.entrySet().stream();
//111111111111111111111111111111111111111111111111111111111111111111111111111
String[] array = {"a","b"};
Stream<String> stream3 = Stream.of(array);
Integer[] array2 = {1,2,3};
Stream<Integer> stream4 = Stream.of(array2); //T... -> T=Integer,把每个元素拆开。//泛型 必须是 引用类型
int[] array3 = {1,2,3};
//如果说数组要获取Stream流,不要用基本类型数组, 把整个数组当成一个元素无法拆开,不要用如下
Stream<int[]> stream5 = Stream.of(array3);// T -> T = int[]
}
}
7.Stream流的终结方法:list.stream().count()
package com.itheima09.method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
/*
* Stream流的方法: 1. 终结方法 : 不支持链式编程 (返回值: 基本类型)
* 2. 拼接方法 : 支持链式编程 (返回值: 当前类型,引用类型)
*
* 终结方法: 1.foreach
* void forEach(Consumer<? super T> action);
* 底层: 迭代器 , 需要消费参数(集合中每一个元素)
* 2.count
* long count() : 获取集合中的元素个数
*/
public class EndDemo {
public static void main(String[] args) {
// method(); //abc
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 5,3,2,4);
// for (Integer integer : list) { //list.for快捷
// System.out.println(integer);
// }
//11111111111111111111111111111111111111111111111111111111111111111111
//stream流打印,先获取stream流对象
/* list.stream().forEach(new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println(t); //遍历打印list集合
}
});*/
//如下等于上面
// list.stream().forEach(t-> System.out.println(t));
//11111111111111111111111111111111111111111111111111111111111111111111
long count = list.stream().count();
System.out.println(count); //4
}
private static void method() {
// StringBuilder sb = new StringBuilder();
// StringBuilder sb2 = sb.append("a");
// StringBuilder sb3 = sb2.append("b");
StringBuilder sb = new StringBuilder();
sb.append("a").append("b").append("c").equals("abc"); //append拼接方法,equals终结方法,因为equals返回boolean基本类型,不能再调用方法
System.out.println(sb);
}
}
8.Stream流的拼接方法:Stream.concat
package com.itheima09.method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.function.Predicate;
import java.util.stream.Stream;
/*
* Stream的拼接方法
* 1. filter 过滤
* Stream<T> filter(Predicate<? super T> predicate);
* 1. Predicate : public boolean test(String s)
* 2. s是集合中每一个元素 , 迭代器
* 3. return true : 表示保留这个元素
* 2. limit 取用前几个
* Stream<T> limit(long count)
* 只要前count个元素, 没有越界
* 3. skip 跳过前几个
* Stream<T> skip(long n);
* 4. static concat 组合,静态方法
* static <T> Stream<T> concat(Stream<T> a, Stream< T> b)
* 合并两个stream,整合一个stream
*/
public class ChainDemo { //Chain:链
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三","张三丰","李四","李世石");
//.filter返回值类型是stream,调完filter还能调filter,就像Stringbuild调完append还可再调append
// list.stream().filter(new Predicate<String>() {
// @Override
// public boolean test(String s) {
// return s.startsWith("张");
// }
// }).forEach(s -> System.out.println(s)); //张三 张三丰
// list.stream().filter(s->s.startsWith("张")).forEach(s-> System.out.println(s)); //等同上面
// list.stream().limit(5).forEach(t-> System.out.println(t)); //没有索引越界,因为迭代器没有索引,最多4个就给4个
// list.stream().skip(3).forEach(t-> System.out.println(t)); //李世石
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"王五","王五百","马六");
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list2.stream();
Stream.concat(stream1,stream2).forEach(t-> System.out.println(t)); //张三...7个元素
}
}
package com.itheima11.union;
import java.util.ArrayList;
import java.util.List;
//案例比较:
public class Demo01 {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("老子");
one.add("庄子");
one.add("孙子");
one.add("洪七公");
List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("张三丰");
two.add("赵丽颖");
two.add("张二狗");
two.add("张天爱");
two.add("张三");
List<String> oneA = new ArrayList<>(); // 第一个队伍只要名字为3个字的成员姓名;
for (String name : one) {
if (name.length() == 3) {
oneA.add(name);
}
}
List<String> oneB = new ArrayList<>(); // 第一个队伍筛选之后只要前3个人;
for (int i = 0; i < 3; i++) {
oneB.add(oneA.get(i));
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
List<String> twoA = new ArrayList<>(); // 第二个队伍只要姓张的成员姓名;
for (String name : two) {
if (name.startsWith("张")) {
twoA.add(name);
}
}
List<String> twoB = new ArrayList<>(); // 第二个队伍筛选之后不要前2个人;
for (int i = 2; i < twoA.size(); i++) {
twoB.add(twoA.get(i));
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
List<String> totalNames = new ArrayList<>(); // 将两个队伍合并为一个队伍;
totalNames.addAll(oneB);
totalNames.addAll(twoB);
for (String name : totalNames) { // 打印整个队伍的姓名信息。
System.out.println(name);
}
}
}
package com.itheima11.union;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Demo02 {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("老子");
one.add("庄子");
one.add("孙子");
one.add("洪七公");
List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("张三丰");
two.add("赵丽颖");
two.add("张二狗");
two.add("张天爱");
two.add("张三");
//stream流式编程核心:隐藏了没有必要的循环迭代,把最核心的条件暴露出来并通过链式编程方式把代码顺起来
// 1. 第一个队伍只要名字为3个字的成员姓名;
// 2. 第一个队伍筛选之后只要前3个人;
Stream<String> stream1 = one.stream().filter(s -> s.length() == 3).limit(3);
// 3. 第二个队伍只要姓张的成员姓名;
// 4. 第二个队伍筛选之后不要前2个人;
Stream<String> stream2 = two.stream().filter(s -> s.startsWith("张")).skip(2);
// 5. 将两个队伍合并为一个队伍;
// 6. 打印整个队伍的姓名信息。
Stream.concat(stream1,stream2).forEach(t-> System.out.println(t));
}
}
9.File类三个构造方法和获取方法:\
package com.itheima01.constructor;
import java.io.File;
/*
File : 既表示文件,又表示文件夹
1. File(String pathname):通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
2. File(File parent, String child):根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
3. File(String parent, String child):根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
路径分隔符:
1. windows : \
2. Unix : / 正斜杠在java中, 用 / 或 \\ 都可以
*
* 正则表达式 : 用简短的符号来表示复杂的意思
* \t : 表示制表符 tab
* \r\n : 表示回车换行
* \\ : 表示一个\
*/
public class Demo01 {
public static void main(String[] args) {
// new File("C:\\test\\a.txt");
File file = new File("C:/test/a.txt"); //直接指定一个完整路径
System.out.println(file);
File file2 = new File("C:/test", "a"); //a表示文件夹 //指定一个父路径+名字
System.out.println(file2);
//如下同上
File fu = new File("C:/test");
File file3 = new File(fu, "a");
System.out.println(file3);
}
}
package com.itheima02.method;
import java.io.File;
/*
获取方法
1. String getAbsolutePath() :返回此File的绝对路径名字符串。
2. String getPath() :将此File转换为路径名字符串。(构造时使用的路径)
3. String getName() :返回由此File表示的文件或目录的名称。(路径最后一级名称)
4. long length() :返回由此File表示的文件的长度。(文件夹的大小无法获取)
路径:
1. 绝对路径 : 带盘符的路径
2. 相对路径 : 相对而言的路径(java工程中,相对当前工程而言)
*/
public class GetDemo {
public static void main(String[] args) {
File file1 = new File("E:\\mywork\\IdeaProjects\\class897\\day11\\a.txt");
File file2 = new File("a.txt"); //在当前项目里找
String absPath1 = file1.getAbsolutePath();
String absPath2 = file2.getAbsolutePath();
System.out.println(absPath1); //E:\mywork\IdeaProjects\class897\day11\a.txt
System.out.println(absPath2); //打印同上行,没有包路径如com..
String path1 = file1.getPath();
String path2 = file2.getPath();
System.out.println(path1); //E:\mywork\IdeaProjects\class897\day11\a.txt
System.out.println(path2); //a.txt
String name = file1.getName();
String name2 = file2.getName();
System.out.println(name); //a.txt
System.out.println(name2); //a.txt
long length = file1.length();
System.out.println(length); //3 byte,因为a.txt里有abc三个字母
File src = new File("src");
System.out.println(src.length()); //0,不能获取文件夹大小
}
}
10.File的判断/创建/删除/列举方法:listFiles
package com.itheima02.method;
import java.io.File;
/*
判断方法:1. public boolean exists() :此File表示的文件或目录是否实际存在。
2. public boolean isDirectory() :此File表示的是否为目录。
3. public boolean isFile() :此File表示的是否为文件。
*/
public class BooleanDemo {
public static void main(String[] args) {
File f1 = new File("src"); //目录
File f2 = new File("a.txt");
File f3 = new File("xxx");
System.out.println(f1.exists());//true //因为在day11模块下找
System.out.println(f2.exists());//true
System.out.println(f3.exists());//false
System.out.println("-------------------");
System.out.println(f1.isDirectory());//true
System.out.println(f2.isDirectory());//false
System.out.println(f3.isDirectory());//false
System.out.println("---------------");
System.out.println(f1.isFile()); // false
System.out.println(f2.isFile());// true
System.out.println(f3.isFile());//false
}
}
package com.itheima02.method;
import java.io.File;
import java.io.IOException;
/*
* 1. public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
* 1. 如果文件不存在,则创建,返回true
* 2. 如果文件存在,不创建,返回false
* 3. 如果文件父路径不存在,则抛出IOException
2. public boolean mkdir() :创建由此File表示的目录。 (make directory)
1. 如果父路径不存在, 不会创建
2. 如果目录已存在,也不会创建,都返回false
3. public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。(多级)
*/
public class CreateDemo {
public static void main(String[] args) throws IOException {
// create();
File dir = new File("ccc"); //单级路径也算多级路径中一种
boolean result = dir.mkdirs();
System.out.println(result); //true
}
private static void create() throws IOException {
File file = new File("dir/xxx");
boolean result = file.createNewFile();
System.out.println(result); //false,dir不存在
}
}
package com.itheima02.method;
import java.io.File;
/*
* public boolean delete() :删除由此File表示的文件或目录。
* 1. 此方法不走回收站, 慎用
* 2. delete可以删除 空文件夹和文件
* 3. 删不了非空文件夹,返回false。一个文件被其他程序所占用,其他程序无法删除,win下word一直用原文件,绘图软件是复制一份。所以word在使用修改时原文件删不了,绘图可以删除原文件
*/
public class DeleteDemo {
public static void main(String[] args) {
File file = new File("c:/test/b");
boolean result = file.delete();
System.out.println(result);
}
}
package com.itheima02.method;
import java.io.File;
/*
目录的遍历:1. public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
1. 此方法只能用在文件夹上
2. 列出当前文件夹的所有子文件and子文件夹
2. public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
*/
public class ListDemo {
public static void main(String[] args) {
File file = new File("c:/test/filedoc"); // 需求: 打印目录 C:\test\filedoc 下的所有文件(单级)
/* String[] list = file.list();
for (String name : list) {
System.out.println(name);
}*/
//如下(列出对象)等同于上面(列出名字)
File[] files = file.listFiles();
for (File sonFile : files) {
System.out.println(sonFile.getAbsolutePath());
}
}
}
package com.itheima03.recursion;
import java.io.File;
/*
* 需求: 删除非空文件夹 filedoc
* 前提: delete方法不能直接删除非空文件夹
* 解决: 1. 列出文件夹下的所有子路径(子文件和子文件夹)
* 2. 进行遍历
* 3. 判断每个子路径 是文件还是文件夹
* 3.1. 如果是文件,直接删
* 3.2 如果是文件夹
* 1. 接着列出此文件夹的所有子路径
* 2. 进行遍历
* 3. 判断每个子路径 是文件还是文件夹
* 3.1 如果是文件,直接删
* 3.2 如果是文件夹...
* ....
* 有一段逻辑不断重复,重复的次数不知道, 但是结束条件: 删除到最后一级文件夹的子文件全都删掉
*/
public class DeleteDemo {
public static void main(String[] args) {
File dir = new File("c:/test/filedoc");
// file.delete(); //无用,删不了非空文件夹
deleteDir(dir);
}
private static void deleteDir(File dir) {
File[] files = dir.listFiles();
for (File sonFile : files) {
if(sonFile.isFile()){//是文件
sonFile.delete();
}else{//是文件夹
deleteDir(sonFile);
}
}
//如上是把子文件夹删了
dir.delete(); //自己变成空文件夹,就可以删掉 ,弹栈。
}
}
package com.itheima03.recursion;
import java.io.File;
/*
* 案例 : 计算非空文件夹的大小。length() : 计算文件的大小
* 思路: 1. 列出此文件夹的所有子路径
* 2. 遍历每个子路径
* 3. 判断是文件还是文件夹
* 3.1 是文件, 加总其大小
* 3.2 是文件夹, 递归
* 隐含结束条件: 文件夹不可能无限创建的
*/
public class CalcDemo {
static int sum = 0;
public static void main(String[] args) {
File dir = new File("c:/test/filedoc");
calc(dir);
System.out.println(sum);
}
private static void calc(File dir) {
File[] files = dir.listFiles();
for (File sonFile : files) {
if(sonFile.isFile()){ // 子文件
sum += sonFile.length();
}else{ //文件夹
calc(sonFile);
}
}
}
}
不死神兔:上个月都不死。1是一对。||是或。n是n个月,不是兔子数量。前不死,第3开生1
。如下红线就是生。
package com.itheima03.recursion;
/*
* 斐波那契数列 : 有一对兔子,从第三个月开始生一对小兔子,之后每个月都生一对小兔子,其他兔子具备相同特点.
* 而且兔子永远不死,问第20个月,兔子有几对?
*/
public class RabbitDemo {
public static void main(String[] args) {
int n = 6; //第6个月
int result = calcRabbit(n);
System.out.println(result); //8
}
private static int calcRabbit(int n) { //计算n个月兔子的对数
if(n == 1 || n == 2){
return 1;
}else{
return calcRabbit(n-1) + calcRabbit(n-2); //上个月和上两个月兔子相加
}
}
}
package com.itheima03.recursion;
public class RabbitDemo02 {
//用循环写,不用递归【递归不需要思考循环次数,但比循环占内存多,因为循环只有一个方法】
public static void main(String[] args) {
int n = 20;
int[] array = new int[n]; // 20个月不同月份兔子数量
array[0] = 1; // 第一个月
array[1] = 1; // 第二个月
for (int i = 2; i < n; i++) {
array[i] = array[i-1] + array[i-2];
}
System.out.println(array[19]); //6765
}
}
11.FileOutputStream两个构造:OIWR,IO流(内存为中心)
8个电子元件组在一起表示字符a即一个byte,一般bit不会用就像一分钱一样不用,所以一般最小单位字节
。
jdk8前接口里方法都是抽象
方法。jdk8后加入静态和默认方法,除了函数式编程很少见到接口里静态和默认方法。抽象类不能实例化,没法创建对象,没法调它的普通方法
:所以在四个抽象类下面各选4个子类File…(因为java里数据用File类来表示)。
package com.itheima01.outputstream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/*
* FileOutStream的构造方法。这里的stream和lambda里stream没有一点关系
* 1. FileOutputStream(File file)
2. FileOutputStream(String name) throws FileNotFoundException
fos创建的时候需要跟一个路径进行绑定(将会写出数据)
构造的特点: 1. 如果路径文件不存在,则新建
2. 如果路径文件存在,则覆盖(无论路径文件是否存在,创建一个新的覆盖)
异常处理 -> 待会详细的写一遍
3. 如果路径的父目录不存在, 就会抛出FileNotFoundException
结论: 1. 一般使用FileOutputStream(String name)构造,快捷
2. 但是如果不能保证父目录是否存在,就用FileOutputStream(File file)
*/
public class Demo01 {
public static void main(String[] args) throws FileNotFoundException {
// FileOutputStream fos = new FileOutputStream("dir/a.txt");
//如下解决上面父路径不存在不能创建缺点
File file = new File("aaa/dir/a.txt"); //1. 判断file的父目录aaa/dir/是否存在
File parentFile = file.getParentFile();
if(!parentFile.exists()){
parentFile.mkdirs(); //2. 如果不存在,则创建多级目录
}
FileOutputStream fos = new FileOutputStream(file); //必须有这一行才创建出文件
System.out.println("创建成功");
}
}
//两种创建文件方式
//1.
File file = new File("d:/a.txt"); //只创建File类路径实例
file.createNewFile(); //必须要执行才创建
//2.
File file = new File("d:/a.txt");
FileOutputStream outputStream =new FileOutputStream(file); //这行可以创建出一个空文件了
outputStream.write(123);
outputStream.close();
outputStream.flush();//必须刷新文件才有内容
12.OutputStream的基本方法:追加和换行
package com.itheima01.outputstream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 1. OutputStream 抽象类,是表示输出字节流的所有类的超类
1. close() :关闭此输出流并释放与此流相关联的任何系统资源。
2. write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。
3. write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
4. write(int b) :将指定的字节输出流。
*/
public class Demo02 {
public static void main(String[] args) throws IOException { //FileNotFoundException父类是IOException
FileOutputStream fos = new FileOutputStream("a.txt");
fos.write(97); // ASCII码表,a.txt内容里多了一个a。 //1. write(int b) : 一次写一个字节
fos.write('a'); //aa
byte[] array = {65,66,67,68,69}; //2. write(byte[] b) : 一次写一个字节数组
fos.write(array); //aaABCDE
fos.write(array,1,3); //aaABCDEBCD (最后多了BCD 3个字母) (1是array从66即B开始) //3.write(byte[] b, int offset, int len): 一次写一个字节数组的一部分 //offset(偏移量):从第几个开始
fos.close(); //close()本身有个io编译异常,流用完要关掉, 释放资源,流横跨内存和硬盘开销大,所以要关
}
}
package com.itheima01.outputstream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
/*
FileOutputStream(String name, boolean append) : 数据追加
1. append = true : 往原文件追加内容,不会覆盖了
换行: 1. windows : \r\n
2. unix : \n
3. System.lineSeparator() : 根据不同的系统,返回不同的换行符 (跨平台)
*/
public class Demo03 {
public static void main(String[] args) throws IOException {
// FileOutputStream fos = new FileOutputStream("a.txt"); //会覆盖原内容
FileOutputStream fos = new FileOutputStream("a.txt",true); //不覆盖原内容
// fos.write(48); //尾部追加一个0
//1111111111111111111111111111111111111111111111111111111111111111111111111111
// fos.write("\r\n".getBytes()); //win
String line = System.lineSeparator();
fos.write(line.getBytes());
//111111111111111111111111111111111111111111111111111111111111111111111111111
String str = "hello,byebye!";
byte[] bytes = str.getBytes();
fos.write(bytes);
fos.close();
}
}
13.FileInputStream两个构造:一次读一个字节或字节数组
package com.itheima02.intputstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/*
FileInputStream 构造 (读:硬盘流到内存),以内存为中心,如果文件不存在, 则抛出FileNotFoundException
1. FileInputStream(File file)
2. FileInputStream(String name)
*/
public class Demo01 {
public static void main(String[] args) throws FileNotFoundException {
// FileInputStream fis = new FileInputStream("a.txt"); //a.txt在上面FileOutputStream已创建
FileInputStream fis = new FileInputStream(new File("a.txt"));//不会创建文件,和上行一样
System.out.println(fis); //java.io.FileInputStream@6d6f6e28
}
}
package com.itheima02.intputstream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* InputStream 抽象类是表示字节输入流的所有类的超类
1. void close() :关闭此输入流并释放与此流相关联的任何系统资源。
2. int read() : 从输入流读取数据的下一个字节。
3. int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
// method01();
FileInputStream fis = new FileInputStream("a.txt");
/* int content = -1 ; //=0也可以,都会被覆盖掉
while(content != -1){
content = fis.read();
System.out.println(content);
}*/
int content = -1;
while((content = fis.read()) != -1){ //这样和上面区别是,这不会多打印-1
System.out.println(content);
}
fis.close();
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
private static void method01() throws IOException {
FileInputStream fis = new FileInputStream("a.txt"); //已存在a.txt且里面有abc
int content = 0 ;
content = fis.read(); //向后移动一位,并返回当前元素(迭代器) //int read() : 一次读一个字节
System.out.println(content); //97 //a是97
content = fis.read();
System.out.println(content);//98
content = fis.read();
System.out.println(content);//99
content = fis.read(); // 如果后面已经到文件的末尾,不会再移动直接返回-1
System.out.println(content);//-1 , -1不在ascii表上,-1表示没有意义
fis.close();
}
}
package com.itheima02.intputstream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
/*
* int read(byte[] b) : 一次读一个字节数组,迭代器
* 1. 参数 byte[] : 用来存放字节流读取到的数据
* 2. 返回值 int : 本次读取到的字节个数
* 返回值 = 数组长度 < 文件剩余的字节数量 ? 数组长度 : 文件剩余字节数量
*/
public class Demo03 {
public static void main(String[] args) throws IOException {
// method01();
FileInputStream fis = new FileInputStream("a.txt");
byte[] buffer = new byte[3];// {0,0,0}
int length = 0;
/*while(length != -1){ //A(如下)
length = fis.read(buffer);
System.out.println(length + "->" + Arrays.toString(buffer));
}*/
while((length = fis.read(buffer)) != -1){
// System.out.println(length + "->" + Arrays.toString(buffer)); //B
String str = new String(buffer, 0, length); //C
System.out.println(str);
}
fis.close();
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111
private static void method01() throws IOException {
FileInputStream fis = new FileInputStream("a.txt"); //a.txt里内容:abcdefg
byte[] buffer = new byte[3];//{0,0,0} //System.out.println(Arrays.toString(buffer)); //[0,0,0] //Arrays工具类
int length = -1;
length = fis.read(buffer); //框子只有3个位置,读了给buffer
System.out.println(length + "->" + Arrays.toString(buffer)); //3->[97,98,99]
length = fis.read(buffer);
System.out.println(length + "->" + Arrays.toString(buffer));// length=3,def
length = fis.read(buffer);
System.out.println(length + "->" + Arrays.toString(buffer));//length=1,gef,ef还是上面的没覆盖
length = fis.read(buffer);
System.out.println(length + "->" + Arrays.toString(buffer));// length=-1,gef
fis.close();
}
}
14.文件复制(重要):System.currentTimeMillis(),fis.read,fos.write
package com.itheima03.copy;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 文件复制: 先读后写:一个读,另一个写
* 1. 先创建一个输入流 指向 原文件
* 2. 再创建一个输出流 指向 副本
*/
public class CopyDemo01 {
public static void main(String[] args) throws IOException {
// method01(); // 1148ms
method02();//59ms
//1111111111111111111111111111111111111111111111111111111111111111111111111
/* long startTime = System.currentTimeMillis();
for (int i = 0; i < 1024*4000; i++) { //循环不耗时,打印耗时,打印就是IO流
}
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime); // 2ms*/
}
//111111111111111111111111111111111111111111111111111111111一次读写一个 字节数组 (重要)
private static void method02() throws IOException {
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("柳岩.jpg");
FileOutputStream fos = new FileOutputStream("ly2.jpg");
byte[] buffer = new byte[1024]; //1kb
int length = -1;
while((length = fis.read(buffer)) != -1){
fos.write(buffer,0,length);
}
fos.close();
fis.close();
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
//1111111111111111111111111111111111111111111111111111111111111一次读写一个 字节
private static void method01() throws IOException {
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("柳岩.jpg"); //已存在
FileOutputStream fos = new FileOutputStream("ly.jpg");
int content = -1;
while((content = fis.read()) != -1){
fos.write(content);
}
fos.close();
fis.close(); //先打开的后关闭
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}
15.编码表与字符流:编码/解码/乱码
15.1 编码:字节映射为字符。第一种编码:ascii码。
如下是中文编码,gb2312(1980年)既是字符集又是编码
,gb2312的1字节等于ascii码,2字节分为区字节+位字节。gb2312汉字收录太少,所以出了gbk(1995年)(编码规范)。
如上"啊"改为"瞭",并放开gb2312和gbk,如下打印3F就是ascii码中63即?即解码失败。
"㞎"是gb18030独有的CJK统一汉字扩充。unicode字符集
有utf-8和utf-16编码方式,前面字符集和编码都同一个,所以叫统一名字了。
15.2 乱码:FileReader(字符流)= FileInputStream(字节流 )+ 编码表
1.程序文件
有编码:用文件浏览器(编码B)或IDE(编码B)打开一个文件(编码A)会乱码。
2.数据文件和数据源
有编码:程序读取编码A的数据并且将转成字符串(默认使用编码B转),也会乱码。
3.展示数据的平台
有编码:网页浏览器向服务端请求的数据的编码是A,但是浏览器展示用的编码B,也会乱码。
读文件:FileInputStream+byte[ ]:
这种方式不好,如下1.txt中10个"啊",utf8中每个汉字对应3字节,所以10个"啊"对应30个字节。
爬GBK编码的网页:
直接使用爬虫sdk里getstream(如下getForObject)获取字符串,如下url网页以gbk编码。
如下还是乱码。
如下改进,不要拿String.class,String是加工过的,拿byte[ ].class,如下未出现乱码。
package com.itheima04.chard;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
* 1. ASCII码表 : 128个数字(0~127,2的7次方=128)。0 000 0000 ~ 0 111 1111 (一个字节后7位占了)
* 2. ISO-8859-1 : 欧码(拉丁码) 256个。0000 0000 ~ 1111 1111 (一个字节)。Tomcat软件 默认编码表
*
* 3. GBK (国标扩)是GB2312的扩展:
* 兼容ASCII码表即承认英文字母占一个字节, 一个汉字占两个字节(2的16次方,0 ~ 65535)。
*
* 4. 国际 Unicode (universal code):utf-8 : 兼容ASCII表, 其次 归纳其他主流文字
* 1. 一个英文一个字节
* 2. 一个中文三个字节(如果有1000个字用GBK只有2kb的大小,用utf-8占3kb)
*
* 总结: ASCII码表 : 0,A,a
* GBK: windows中文简体默认(GBK)
* UTF-8: 开发常用!!!(ideal里默认UTF-8)
*/
public class CharDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("a.txt"); //字节流:一个英文一个字节 , 一个中文3个字节。如“中国人”显示9个数字
// FileReader fis = new FileReader("a.txt"); //字符流:一次读一个字符(英文只读1字节, 中文只读3个字节,动态判断)。如“中国人”显示3个数字。【有了编码表后有了动态判断能力】
int content = -1;
while((content = fis.read()) != -1){
System.out.println(content); //打印换行
}
fis.close();
}
}
package com.itheima04.chard;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//字符流: 赋值文本文件,不能复制图片。
public class CharDemo02 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("a.txt"); //已存在
FileWriter fw = new FileWriter("b.txt");
int length = -1;
char[] buffer = new char[1024];
while((length = fr.read(buffer)) != -1){
fw.write(buffer,0,length);
}
fw.close();
fr.close(); //结果:b.txt和a.txt一样,原来没有b.txt这个文件
}
}
package com.itheima04.chard;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class CodeDemo {
public static void main(String[] args) throws IOException {
//系统里的a.txt文件: GBK编码(win下改编码:另存为-选择编码)。
FileReader fr = new FileReader("c:/test/a.txt"); //编码(encode) : 将字符 -> 字节
int content;
while((content = fr.read()) != -1){
System.out.println((char)content); //不解码:System.out.println(content); 97 98 99 20013..
}
fr.close(); //FileReader : UTF-8 解码 乱码
}
}
如下因为GBK和UTF-8都支持ascii码。
15.3 linux文件编码转换:iconv,enca
第一种
:iconv命令用于文件编码的转换,碰到gbk编码的文件,需要转换成utf8,直接使用该命令即可。iconv --list :列出iconv支持的编码列表。iconv -f 原编码 -t 新编码 filename -o newfile
。
-f : from 来源编码
-t : to 转换后新编码
-c: 忽略无效字符
-s: --silent,忽略警告
-o file : 可选,没有的话直接转换当前文件, 使用-o 保留源文件
第二种
:linux安装enca,多文件转编码:
enca -v
wget http://dl.cihar/enca/enca-1.13.tar.gz
tar -zxvf enca-1.13.tar.gz
cd enca-1.13
//根据REAMDE说明依次执行如下命令
./configure
make
make check
make install
// which enca 得知enca默认安装路径 /usr/local/bin/目录,默认头文件位置/usr/local/include
//查看configure可执行文件帮助文档,可不执行
//./configure -h
//在执行./configure时可配置安装目录,可不执行
//./configure --prefix=/usr/bin
enca -L zh_CN filename //或 enca filename
enca `ls` //查看本目录下文件编码格式
enca -L zh_CN -x UTF-8 filename //或enca -x UTF-8 filename,enca -x GB2312 filename
enca -x utf-8 * // 该目录下所有文件,一般只有gbk,gb2312在linux中不显示中文乱码(终端,linux,数据库)
15.4 Linux中文字符集:/etc/locale.conf
1、查看当前系统已安装的字符集
(1)locale命令用于查看当前系统全部的已安装的字符集,Linux支持的符集约800种:
locale -a
(2)查看已安装的中文字符集(只查看中国大陆的,不包括香港和台湾):
locale -a|grep zh_CN,下图表示已经安装了中文字符集。
2.安装中文字符集
如果您的Linux系统没有安装中文字符集,可以用yum命令安装。安装中文字符集软件包的方法比较多,没找到准确的说法,所以把多种方法都写了进来,以下命令都可以执行,不会有副作用:
yum -y groupinstall chinese-support
yum -y install chinese-support
yum -y install kde-l10n-Chinese
yum -y install ibus-table-chinese-1.4.6-3.el7.noarch
安装后,执行locale -a|grep zh_CN,如果显示的内容如下,表示安装成功。
3.修改字符集配置文件
CentOS6.x字符集配置文件在/etc/sysconfig/i18n文件中。
CentOS7.x字符集配置文件在/etc/locale.conf文件中,内容如下:
执行以下命令或者重启系统使修改生效:
CentOS6.x:source /etc/sysconfig/i18n
CentOS7.x:source /etc/locale.conf
4.centos7修改 /etc/locale.conf 不生效
centos7的语言环境变量是通过/etc/profile.d/lang.sh加载locale.conf来设置的。我在修改 /etc/locale.conf 内容为 LANG=“zh_CN.UTF-8” 并重启后发现并没有什么效果。local得出的依然是默认的en_US。于是我去检查lang.sh的代码。发现中间有一段是这样的:
可以看到当变量为zh*的时候,依然将LANG赋值为en_US.UTF-8。不知道官方为什么会采用这种默认设置,所以我将代码改成这样。重启之后再次查看locale就好了。
15.5 Oracle的字符集:NLS_LANG
服务端的字符集:Oracle数据库实例创建后,如果没有开始业务运行,可修改字符集,如果已经业务化运行,不建议修改字符集,会造成数据中的汉字乱码
1.查看服务端字符集
执行以下SQL可以查看服务端的字符集:
select * from NLS_DATABASE_PARAMETERS where parameter like ‘%CHARACTERSET%’;
执行以下SQL也可以查看服务端的字符集:select userenv(‘language’) from dual;
2.修改服务端字符集
用DBA权限,执行以下步骤修改Oracle数据库的字符集(例如修改为ZHS16GBK)。
(1)修改服务端操作系统的NLS_LANG环境变量:
export NLS_LANG=‘Simplified Chinese_China.ZHS16GBK’
(2)关闭Oracle数据库:
shutdown immediate;
(3)把数据库启动到mount状态:
startup mount;
(4)把数据库改为restricted模式:
alter system enable restricted session;
alter system set job_queue_processes=0;
alter system set aq_tm_processes=0;
(5)打开数据库:
alter database open;
(6)修改数据库的字符集。
alter database character set internal_use ZHS16GBK;
(7)重启数据库:
shutdown immediate;
startup;
客户端的字符集:Oracle客户端的字符集必须与服务端相同,否则中文会出现乱码,客户端的字符集由NLS_LANG环境变量控制
1.Linux环境
(1)查看NLS_LANG环境变量。
env|grep NLS_LANG
(2)设置环境变量
修改环境变量参数文件(系统或用户的profile文件)。
export NLS_LANG=‘Simplified Chinese_China.ZHS16GBK’
2.Windows环境
打开注册表( 执行regedit.exe)
HKEY_LOCAL_MACHINE -> SOFTWARE -> ORACLE -> KEY_OraClient11g_home1
16.flush和close方法:.getBytes()
如下两种方式都可以,OutputStream和Writer对应。
package com.itheima04.chard;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
/*
* OutputStream : flush方法:刷新此输出流,强制写出所有缓冲的输出字节。
* Writer : flush方法:刷新此流,将流中缓存的数据写到硬盘。
* 结论:OutputStream的flush方法没用, Writer的flush有点用。
*
* 原理: 字符流 = 字节流 + 编码表 (缓存区:为了兼容编码表存在)。
之前把一次读一个字节数组,那个数组称为缓存。字符流底层有一个字节数组byte[]:支持多个字节,8k(把前面字节缓存起来)
*/
public class FlushDemo {
public static void main(String[] args) throws IOException {
// FileOutputStream fos = new FileOutputStream("a.txt"); //a.txt里写啥都无所谓,之后会覆盖
// fos.write("中文".getBytes()); //字节流没有字节数组,把中文拆成字节数组,把中文所包含的6个字节放到字节数组里,让字节流输出去,因为底层没有缓冲即byte[]数组。//有一个字节,就写一个,没有缓存不用刷新。
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
FileWriter fw = new FileWriter("a.txt");
fw.write("中文2"); // 先放到缓存(内存),等缓存满了,再写到硬盘上
fw.flush(); //刷新缓存, 把缓存清空,写到硬盘上,怕断电 //fw.close(); :释放资源,释放之前,底层默认先调用flush,所以这行单写fw.close()也能写进去。
fw.write("a");//上行close之后不能再使用这个流
fw.flush(); //刷新显示
}
}
17.IO流异常处理:字符流用于读写文本文件,字节流outputStream(内存->硬盘:写,以内存为中心),BOP
如下是四个抽象类的其他子类
:BufferedOs
:后面Os是outputStream简写。OutputStreamWriter
:是Writer的子类,是FileWriter的父类,没FileWriter封装程度高,暴露了os(字节流)和charset(编码表)。序列化流
只有字节流,没有字符流。转换流
只有字符流,没有字节流。
如下finally代码一定会执行。
如下实际开发a.txt(已存在)和b.txt路径由用户指定会发生异常,IO不能直接抛,程序会崩,所以自己处理try catch,不在try中编译异常就是IO异常。
如下改进就在try外面int i = 0;定义+赋值。同理FileInputStream fis = null;
package com.itheima01.exception;
import java.io.*;
/*
* 不论什么类型文件复制都用 字节流! 理由: 1.没有编解码就不会产生乱码, 用字符流有乱码的风险。 * 2.没有编解码复制文件更快。
*/
public class ExceptionDemo {
public static void main(String[] args) { //之前图省事直接在main这行throws IOException
//外边定义,里面赋值 //每个变量在使用的时候,必须有值
FileInputStream fis = null;
FileOutputStream fos = null ;
try {
//int i=1/0; //直接跳到catch,下面fis没有赋上值,所以外面定义时赋null值
fis = new FileInputStream("a.txt"); //刚指定完a.txt,有个程序将a.txt干掉了,出问题:文件找不到,所以IO不能直接抛
fos = new FileOutputStream("b.txt");
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){
fos.write(buffer,0,length);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(fos != null){//避免空指针,fos有可能为空(try中值没赋上),空对象调方法产生空指针异常
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
如上finally中代码冗余。
package com.itheima01.exception;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
// 多态: 简化代码 所有的IO流都实现了 Closeable 接口 (因为所有的IO流有close方法),选中Closeable按ctrl+h
public class ExceptionDemo02 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null ;
try {
fis = new FileInputStream("a.txt");
fos = new FileOutputStream("b.txt");
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){
fos.write(buffer,0,length);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
// release(fos);
release(fos,fis); //多态
}
}
private static void release(Closeable... ios) { //可变参数
for (Closeable io : ios) { //遍历数组
if(io != null){ //避免空指针
try {
io.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.itheima01.exception;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* JDK7 特性: try...with resource
* try( 要释放的IO流对象A,B) {
* 当这里的代码执行完, A B流就会被自动释放掉(不需要手动调用close)
* }
* 特点: A,B 默认被final所修饰
*/
public class ExceptionDemo03 {
public static void main(String[] args) {
try(FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt")) {
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){
fos.write(buffer,0,length);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
18.字节/字符缓冲流:readLine
package com.itheima02.buffer;
import java.io.*;
/*
* 字节缓冲流:1. public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
public BufferedInputStream(InputStream in, int size)
2. public BufferedOutputStream(OutputStream out) :创建一个 新的缓冲输出流。
* 底层: 字节流 + 缓存区(默认8k) 。 目的: 用空间(内存)换时间 (提高效率)。
* 方法: 构造方法注意一下,需要手动传入一个字节流对象。其他方法都跟父类一样
*/
public class ByteDemo {
public static void main(String[] args) throws IOException {
FileInputStream is = new FileInputStream("a.txt"); //注意是is不是fis
FileOutputStream os = new FileOutputStream("b.txt");
//如下字节缓存流
BufferedInputStream fis = new BufferedInputStream(is,80*1024);//字符默认8k,这个可以手动调
BufferedOutputStream fos = new BufferedOutputStream(os);
int length = -1; //80k(一级) -> 1k(二级)写入到硬盘
byte[] buffer = new byte[1024]; //这里1kb【二级】最好和上面80k【一级】一致,这样不会有空间浪费问题
while((length = fis.read(buffer)) != -1){
fos.write(buffer,0,length);
}
fos.close();
fis.close();
}
}
package com.itheima02.buffer;
import java.io.*;
/*
* 字符缓冲流:1. 构造
1. public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
2. public BufferedWriter(Writer out) : 创建一个 新的缓冲输出流。
2. 特有方法
1. BufferedReader: public String readLine() : 读一行文字。
2. BufferedWriter: public void newLine() : 写一行行分隔符,由系统属性定义符号。
*/
public class CharDemo {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("a.txt")); //BufferedReader(字符流),BufferedReader也默认8kb
// String line = br.readLine();
// System.out.println(line);
String line = null;
while((line = br.readLine()) != null){
System.out.println(line); //有几行打印几行
}
br.close(); //关高级流,会默认将低级流也会一并关掉
//11111111111111111111111111111111111111111111111111111111111111111111111111111
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
bw.write("一句话");
bw.write(System.lineSeparator()); //换行
bw.write("一段话");
bw.newLine(); //换行,底层就是上面System.lineSeparator()
bw.write("一翻话");
bw.close();
}
}
19.案例_出师表:\ \ . ,content = map.get(i)
正则中一个单独的点表示任意字符。
package com.itheima02.buffer;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/*
* 需求: 将a.txt中的内容,按照每行的序号进行排序 b.txt
* 1.先把a.txt中的数据都读出来。 2.一次读一行, 进行切割 number -> 语句
*
* 方案A
* 3. 放HashMap <Integer,String> map
* 4. for i 1-9 (i = key) //不需要排序,按序号取出并输出
* value = map.get(key)
*
* 方案B
* 3. 放TreeMap<Integer,String> //TreeMap的key会自动排序的,Integer类型会按自然顺序排,底层是treeset比较器默认自动升序。
* 4. 遍历打印
*/
public class OutTeacherWatchDemo {
public static void main(String[] args) throws IOException {
// method01();
TreeMap<Integer, String> map = new TreeMap<>();
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
String line = null;
while((line = br.readLine()) != null){
String[] split = line.split("\\.");
Integer number = Integer.parseInt(split[0]);
String content = split[1];
map.put(number,content);
}
br.close();
BufferedWriter bw = new BufferedWriter(new FileWriter("c.txt"));
Set<Map.Entry<Integer, String>> entrySet = map.entrySet(); //直接遍历treeMap,输出即可
for (Map.Entry<Integer, String> entry : entrySet) {
Integer key = entry.getKey();
String value = entry.getValue();
bw.write(key + "." + value);
bw.newLine();
}
bw.close();
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
private static void method01() throws IOException { //方案A
HashMap<Integer, String> map = new HashMap<>();
BufferedReader br = new BufferedReader(new FileReader("a.txt")); //这是读取a.txt的流
String line = null;
while((line = br.readLine()) != null){
String[] split = line.split("\\."); // 注意: \\. 表示一个.
Integer number = Integer.parseInt(split[0]); //前面是数字
String content = split[1]; //后面是内容
map.put(number,content);
}
br.close();
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); //这是输出文本的流
for (int i = 1; i <= 9; i++) { // 遍历1~9 -> 指的就是上面的number
String content = map.get(i);
bw.write(i +"." + content);
bw.newLine(); //添加一个换行符
}
bw.close();
System.out.println("复制完成");
}
}
20.转换流:字节字符的中间流
package com.itheima03.transfer;
import java.io.*;
/*
* 案例: 系统文件(GBK 编码) -> 程序(utf-8 解码)
* 乱码: 编解码所使用的编码表不一致。 解决: 将编码和解码编码表一致
*
* 方案A (最常用): 系统文件GBK -> UTF-8 。FileReader流 搞定
* 方案B: 程序默认编码表改成GBK (一般不会这么做!!!) 。FileReader 流 搞定
* 方案C: 将IO流的解码字符集 指定为gbk , 但是程序还是utf-8
* InputStreamReader (在开发中一般也不用, 开发中的涉及的程序和资料都是UTF-8)
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
// FileReader fr = new FileReader("c:/test/a.txt"); //FileReader底层默认编码表utf-8 ,与a.txt默认的GBK会乱码。 //这只能将win上的a.txt另存为utf-8搞定。
FileInputStream fis = new FileInputStream("c:/test/a.txt"); //a.txt里内容:写一句话
InputStreamReader fr = new InputStreamReader(fis, "gbk");
int content = -1;
while((content = fr.read()) != -1){
System.out.println((char)content);
}
fr.close();
}
}
package com.itheima03.transfer;
import java.io.*;
/*
练习:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
1. 读GBK编码文件 : 用GBK编码的字符输入流:
1. FileReader : 底层 FileInputStream + utf-8 默认 (不行)
2. InputStreamReader(FileReader的父) : FileInputStream + gbk 指定 (可以)
字节输入流 + 编码表
2. 写utf-8编码文件 : 用utf-8编码的字符输出流:
1. FileWriter : FileoutputStream + utf-8 默认(可以)
2. OutputStreamWriter : FileoutputStream + utf-8 指定(可以)。两个都可以, 用FileWriter(简单)
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
InputStreamReader isr
= new InputStreamReader(new FileInputStream("c:/test/a.txt"), "gbk");
// 指定 字节输入流 + 编码表
// FileWriter fw = new FileWriter("c:/test/b.txt"); //用下行也可以
OutputStreamWriter osw
= new OutputStreamWriter(new FileOutputStream("c:/test/b.txt"), "utf-8");
int length = -1;
char[] buffer = new char[1024]; //用的字符流,不是byte[]
while((length = isr.read(buffer)) != -1){
osw.write(buffer,0,length);
}
osw.close();
isr.close(); //运行后产生b.txt utf-8 9字节 ,原始a.txt gbk 6字节 里面3个“啊”
}
}
21.序列化流:oos.writeObject,Serializable
如下把对象字符串化。
如下读字符串取出值,再重新新建一个对象。
package com.itheima04.serial;
import java.io.*;
/*
* 文本编码: 字符 -> 字节(也叫二进制 )(目的保存数据)
*
* 序列化(serializable) : 对象/数据结构 -> 二进制 (目的将对象直接保存在硬盘上)存档。
* Base64 编码等 和utf-8和gbk没有关系
*
* 反序列化 : 二进制 -> 对象 (将硬盘上的数据 读到 内存中形成对象) 读档,不需要中间过程。
* Base64 解码
*
public ObjectOutputStream(OutputStream out) : 序列化流
public ObjectInputStream(InputStream in) : 反序列化流
总结: 一个类要允许被序列化: 1. 首先: 实现Serializable接口
2. 其次: 这个类要设置唯一的serialVersionUID (String/Integer类源码里也有..UID)
补丁:transient : 瞬态 (关键字),用这个关键字修饰的属性,不允许序列化/反序列化,如密码不希望被保存
*/
public class Demo01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// method01(); //序
method2(); //反序
}
private static void method2() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Object s1 = ois.readObject();// 反序列化,具体不知道什么对象, 设计成Object
// Student s1 = (Student) ois.readObject(); //可以,同上行输出一样
System.out.println(s1); //Student{name'张三',age=18}
ois.close();
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
private static void method01() throws IOException {
Student s = new Student("张三", 18); //这对象在内存中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt")); //输出流new一下就产生文件obj.txt
oos.writeObject(s); //序列化,存档
oos.close();
}
}
package com.itheima04.serial;
import java.io.Serializable;
/*
* 序列化相关的两个异常之一:
* 1. java.io.NotSerializableException : 无法序列化异常
* java规定: 某个类的对象 要可以被序列化, 必须实现一个接口 Serializable(没有任何内容的接口 -> 授权 ,允许被人家保存)
*
* 2. java.io.InvalidClassException: 无效的类异常
* 保存的时候(Student无int number),读取的时候(Student有int number)
*
* 解决: 搞一个身份证 (这个类不论如何改变,始终JVM都能识别)
* JVM识别一个类: serialVersionUID(不同的类,这个id不可以一样)
*/
public class Student implements Serializable{
private final static long serialVersionUID = 1L; //长整型 //需要method01重新保存,保存完后,增加属性,method02能识别读出来。
private String name;
private transient int age; //age不希望暴露,int类型默认为0且永远0 //Student{name'张三',age=0}
private String password;
int number;
int year;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
22.打印流:最后一个流,字符数据(文本文件)的读或写(不是读写即复制,复制用字节流),用字符流方便
package com.itheima05.print;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
public class PrintDemo {
public static void main(String[] args) throws IOException {
// method();
/*
*系统打印流(System.java中): 输出在控制台不是在硬盘上(控制台也是内存,关了就没了),与字节输出流(输出硬盘上)不一样。
*public final static PrintStream out = null; //在java中, 用final修饰,并且赋值为null, 还调用out方法竟然没有空指针 !!!
*在java中赋值为null是无效,在JVM底层,这个流是C语言赋值的 (JNI),这样可以直接控制操作系统界面如控制台
*private static native void setOut0(PrintStream out); 本地方法 (底层调用C)
*/
// System.setOut(new PrintStream("print.txt")); //与下行同时运行,下行yyyyyyyyy打印到print.txt,控制台改到print.txt
System.out.println("yyyyyyyyy"); //控制台 //out是PrintStream类型
System.err.println("哈哈"); // 红色 (一般 底层打印错误信息)
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111
private static void method() throws IOException {
PrintStream ps = new PrintStream("print.txt"); //(硬盘上的.txt) //实际上new了一个PrintStream的父类FileOutputStream
ps.write(97);
ps.write("xxx".getBytes()); //对应下面ps.print
ps.write(System.lineSeparator().getBytes()); //对应ps.println
ps.print("xxx"); //底层调用.write
ps.println("xxx");
ps.close();
}
}
23.Properties集合:Hashtable 与HashMap 区别,p.load,toString
package com.itheima06.properties;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
/*
* Properties : 是一个放弃了泛型的Map,不是io流。
* 0. 父类 :Hashtable就是HashMap
* Hashtable : 哈希表 (线程同步: 线程安全,但是效率低,大部分方法都加了synchronized关键字)(淘汰)
* HashMap : 哈希表 (线程不同步 : 不安全效率高。真的有线程并发问题,自己可以加同步代码块,锁范围越小越好)
*
* 1. 可以跟IO流相互合作来组织数据结构:把Map结构的数据存到硬盘,把硬盘中map结构读取到内存
*
2. 方法:应用在读取配置文件
1. setProperty(String key, String value):调用 Hashtable 的方法 相当于put()
2. stringPropertyNames()返回此属性列表中的键集 相当于keySet()
(上面方法用不到,因为不会用Property保存数据,用Property读配置文件,以下重要!!!)
3. getProperty(String key)用指定的键在此属性列表中搜索属性
4. load(InputStream) 和load(Reader):把指定流所对应的文件中的数据,读取出来,保存到Propertie集合中
*/
public class PropertiesDemo {
public static void main(String[] args) throws IOException {
// method();
//1. 创建了Properties对象(Map对象)
Properties p = new Properties(); //集合都在java.util包下
//2. 跟IO流合作 (读取map结构的数据 -> 数据以Map结构封装p对象)
p.load(new FileInputStream("note.properties")); //note.properties里写了name=zs 换行 age=18 ..。note.txt也可以。 //以前读法:字符流一次读一行
// Object name = p.get("name"); //返回object不好操作 //zs
// Object age = p.get("age"); //常用下面api //18
String name = p.getProperty("name"); //网页,控制台,其他文件数据读到程序里都是字符串
String age = p.getProperty("age"); //就算是数字,读进来也是字符串
System.out.println(name + "," + age);//用p.get时不能写成(name+age),因为两个object不能相加,所以中间加一个字符串",",底层会调用toString方法
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111
private static void method() {
Properties map = new Properties(); //Map<Object,Object> map = new Properties();
map.put(1,"张三");
map.put(2,"李四");
System.out.println(map); //{2=李四,1=张三}
}
}
24.tcp通讯:ip相当于小区,port相当于门牌号
24.1 单向:一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个
下面将三要素融合,https中s就是secure。
socket封装了io流,所以要关。高层socket关了,低层也会关。如下accept会阻塞。
package com.itheima01.tcp;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws IOException {
String host = "127.0.0.1"; //1.连接服务端(指定服务端的ip和port)自动连接
int port = 10010;
Socket socket = new Socket(host, port);
OutputStream os = socket.getOutputStream(); //2.写出数据
os.write(" hello, tcp".getBytes());
socket.close();
}
}
package com.itheima01.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port。ip是路由器分配的
System.out.println("1");
Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
System.out.println("2");
InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据
int length = -1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){
String msg = new String(buffer, 0, length);
System.out.println("收到客户端的数据:" + msg);
}
socket.close();//关闭和客户端的连接
serverSocket.close(); //关闭服务端
}
}
24.2 双向:socket中介
package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
//准备,写,读
public class TcpClient {
public static void main(String[] args) throws IOException {
String host = "127.0.0.1"; //1.连接服务端 (指定服务端的ip和port) 自动连接
int port = 10010;
Socket socket = new Socket(host, port);
//11111111111111111111111111111111111111111111111111111111111111111111111
OutputStream os = socket.getOutputStream(); //2.写出数据
os.write(" hello, tcp".getBytes());
socket.shutdownOutput(); //解决: 告知对方 写完了。没有关闭socket,关闭就连接不了
//1111111111111111111111111111111111111111111111111111111111111111111111
InputStream is = socket.getInputStream(); //3.客户端收到数据
int length=-1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){
String msg = new String(buffer, 0, length);
System.out.println("收到服务端的数据:" + msg);
}
socket.close();
}
}
package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port
System.out.println("1");
Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
System.out.println("2");
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据
int length = -1;
byte[] buffer = new byte[1024];
/*
* 问题: 如下read方法是一个阻塞方法
* 以前: 读文件,读到末尾返回-1
* 现在: 读socket中传输的数据 (像电话), 对方socket关掉就算是末尾了
* 解决: 1. 对方在输出结束,可以直接关闭socket,这个数据就结束了,但是对方就不能再接收了
* 2. 对方直接告知已经输出结束,socket不需要关
*/
while((length = is.read(buffer)) != -1){//客户端socket.shutdownOutput();服务端就不会阻塞在这一行
String msg = new String(buffer, 0, length);
System.out.println("收到客户端的数据:" + msg);
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111
OutputStream os = socket.getOutputStream(); //4.服务端写出数据
os.write(" 好,退下吧~~".getBytes());
socket.close();//关闭和客户端的连接 //这里socket.shutdownOutput();可以不写,因为直接close了
serverSocket.close(); // 关闭服务端
}
}
文件上传:如下上面是服务端。
package com.itheima03.upload;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class UploadClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10086);
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111
FileInputStream fis = new FileInputStream("柳岩.jpg");//读本地文件(da14模块下有个柳岩.jpg),写到服务器socket中
OutputStream os = socket.getOutputStream();
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){ //读
os.write(buffer,0,length); //写
}
fis.close();
socket.close();
}
}
package com.itheima03.upload;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10086);
Socket socket = serverSocket.accept();
//1111111111111111111111111111111111111111111111111111111111111111111111111111111
InputStream is = socket.getInputStream();
long time = System.currentTimeMillis();//系统当前时间毫秒值
FileOutputStream fos = new FileOutputStream("c:/test/server/" + time + ".jpg");
int length = -1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){ //读
fos.write(buffer,0,length); //写
}
fos.close();
socket.close();
serverSocket.close();
}
}
25.多线程优化服务端与UUID:is和fos
package com.itheima04.good;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
/*
* 服务器一般是24小时不关闭的,前面写的只能收到一张图片服务器就关闭了。解决方案:
* 1. 死循环
* java.BindException: Address already in use: JVM_Bind。绑定异常(port重复占用)
*
* 2. 想支持多个客户端的并发访问
* 解决: 引入多线程 代码加在阻塞之后
* 补充: 线程池 管理 线程(1. 避免线程频繁创建和销毁 2. 控制线程数量)
*
* 2. 文件名 : 以系统当前时间命名(并发: 同一毫秒,有两个用户同时上传文件, 就会有用户文件覆盖)
* 1. 一般文件,不需要标识这个图片属于谁的 : 服务器用UUID
* 2. 文件名需要跟用户上传的文件名一致 : 需要另一个线程传输这个文件名(这个用户id + 文件名)
*/
public class UploadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10086); //只创建一个服务端,一个端口不能被两个程序占用,不能在这行上面加while(true)
while(true){ //死循环接收客户端请求
Socket socket = serverSocket.accept(); //虽然while(true),但这行会阻塞,单线程
//有客户上门才开线程,不能无限开。
new Thread(new Runnable() { //不能放accept()前面,不能无限开线程
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
// long time = System.currentTimeMillis();//系统当前时间毫秒值
String name = UUID.randomUUID().toString().replace("-", ""); //下行time改为name
FileOutputStream fos = new FileOutputStream("c:/test/server/" + name + ".jpg");
int length = -1;
byte[] buffer = new byte[1024];
while((length = is.read(buffer)) != -1){
fos.write(buffer,0,length);
}
fos.close(); //应该放在finally中
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
// serverSocket.close(); // 服务器不能关闭,关了就不回到while(true)了
}
}
}
package com.itheima04.good;
import java.util.UUID;
// UUID: 通用唯一标识符(Universally Unique Identifier),永不重复
public class UuidDemo {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// UUID uuid = UUID.randomUUID();
// String str = uuid.toString().replace("-", "");
String str = UUID.randomUUID().toString().replace("-", ""); //这行同上两行
System.out.println(str);
}
}
}
如下未把UUID的 - 替换为空。
如下BS案例:socket被写,页面被读。写一个浏览器程序难度和操作系统一样,鸿蒙os就是浏览器。
package com.itheima05.bs;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class BsServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10090);
Socket socket = serverSocket.accept(); //接收浏览器请求
//1111111111111111111111111111111111111111111111111111111111111111111111111111
OutputStream os = socket.getOutputStream(); //写出页面
os.write("HTTP/1.1 200 OK\r\n".getBytes()); // http协议的响应行 第一行
os.write("Content-Type:text/html\r\n\r\n".getBytes()); //http协议的响应头 第二行,第三行为空白
FileInputStream fis = new FileInputStream("index.html"); //已有,用来被读
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){
os.write(buffer,0,length); //第四行,响应体
}
fis.close();
socket.close();
serverSocket.close();
}
}
26.Class对象三种获取方式:Class.forName(" ")
package com.itheima02.clazz;
import org.junit.Test;
/*
* 反射前提: Class对象
* 1. 引用类型: Class(大写C和关键字class不一样)
* 2. 回顾: 当程序用到一个类的时候, 会加载这个类进内存(方法区)
* 3. 在运行时, java用Class对象来描述.class文件(就算是一个文件也用对象描述)
* .class文件(硬盘) -> Class对象(内存)
* 4. Class对象的特点:
* 1. Class对象的创建:
* 一个.class文件被加载进内存,JVM会创建这个类的Class对象。
* 因为一个.class文件在使用的时候只需要被加载一次, 所以这个.class文件对应的Class对象也只会有一个!!!
*
* 重点: Class对象(类对象)是JVM创建的, 开发者是无法创建的。(开发者可以new一个object,这叫实例对象)
* 一个类的Class对象一般只有一个 (使用: 同步锁 xxx.class对象:别人不能创建,天然唯一安全)
*
* 2. 三种Class对象的获取(不是创建)
* 1. 类名.class
* 2. 对象名.getClass()
* 3. Class.forName(全限定名); -> 加载配置文件
*/
public class ClassDemo {
@Test
public void method01(){
Class<Student> clazz = Student.class;
System.out.println(clazz); //class com.itheima02.clazz.Student //全类名或全限定名 : 包名 + 类名
synchronized (ClassDemo.class){ //当前类名.class -> 锁对象
//这段代码能够运行,意味着当前类一定被加载进内存-> JVM会创建当前类的Class对象
//ClassDemo.class改为string.class浪费内存,因为不一定用到string,没必要加载string类
}
}
@Test
public void method02(){
Student s = new Student();
Class<?> clazz = s.getClass();
System.out.println(clazz); //class com.itheima02.clazz.Student 同上
System.out.println(clazz == Student.class); //true
}
@Test
public void method03() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.itheima02.clazz.Student");
System.out.println(clazz); //class com.itheima02.clazz.Student 同上
}
}
package com.itheima02.clazz;
import java.util.Objects;
public class Student {
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true; //this表示student类
// this.getClass() == o.getClass()
// o instanceof Student
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
27.反射:clazz.newInstance()
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 反射(reflect) : 在程序运行时操作类
* 1. 运行时 : .java源码 -> .class字节码(编译) -> runtime(运行)
* 2. 操作 :
* 3. 类 : .java -> .class(二进制指令让jvm读) -> Class对象(运行时的类的样子)
* 1. 构造方法 / 构造器 Constructor
* 2. 方法 Method
* 3. 属性 Field
* A. 操作构造方法: 以前: 用构造方法来 创建对象
*/
public class ConstructorDemo {
public static void main(String[] args) {
// Person p = new Person(); //Person{name='null',age=0}
Person p = new Person("张三");
System.out.println(p); //Person{name='张三',age=0}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 反射 : 操作构造方法
//1. 获取Class对象
//2. 再获取Class对象中的构造方法
//3. 使用构造方法创建对象
Class clazz = Person.class;
/*
* Constructor<T> getConstructor(Class<?>... parameterTypes)
* 1. 参数: 需要指定想要获取的构造方法的参数列表 类型
* 2. 返回值: 返回构造方法的对象
*/
//Constructor constructor = clazz.getConstructor(String.class,int.class);
Constructor constructor = clazz.getConstructor(String.class);
/*
* T newInstance(Object ... initargs)
* 1. 参数: 使用构造方法创建对象时传入的实参 (必须跟上面参数列表类型一致)
* 2. 返回值: 返回创建好的实例
*/
Object obj = constructor.newInstance("张三");
System.out.println(obj); //Person{name='张三',age=0}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method02() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //简易的api
Class clazz = Person.class;
/* Constructor constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
System.out.println(obj); //Person{name='null',age=0} */
//Class对象获取空参构造,并创建实例,如下简单,等同于上面两行//(JavaBean要求: 1. private属性 2. public 空参构造 3. public get set方法)
Object obj = clazz.newInstance();
System.out.println(obj); //Person{name='null',age=0}
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //暴力反射: 知道(可以访问private方法)
Class clazz = Person.class;
/*
* getConstructor() : 只能获取public修饰的构造
* getDeclaredConstructor() : 获取所有权限的构造(private也可以)
*/
// Constructor constructor = clazz.getConstructor(String.class, int.class);
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true); //暴力反射: 权限设置public //上面能拿到私有,但是不能调用,只能person本类内部调用,所以加这行可以调用
Object obj = constructor.newInstance("李四", 18);
System.out.println(obj); //Person{name='李四',age=18}
}
}
package com.itheima03.reflect;
public class Person {
public String name;
private int age;
public Person(){
}
public Person(String name){
this.name = name;
}
private Person(String name,int age){ //注意private,用暴力反射
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void speak(String msg){
System.out.println("speak:" + msg);
// return 1;
}
public void speak(double msg){
System.out.println("speak:" + msg);
}
private void show(int year){
System.out.println("show:" + year);
}
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// B.方法的反射(反射中最重要的是构造方法和普通方法反射,没有属性)
public class MethodDemo {
public static void main(String[] args) {
Person p = new Person();
// Person p = null; //speak方法设为静态,不报错
p.speak("呵呵");
System.out.println(number); //speak:呵呵
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1. 获取Class对象
//2. 获取方法 .getMethod
//3. 调用方法 .invoke
Person p = new Person();
Class<?> clazz = p.getClass();
/*
* Method getMethod(String name, Class<?>... parameterTypes)
* 1 .参数
* 1. name 方法名
* 2. name 方法名的参数列表
* 2. 返回值: 获取的这个方法
*/
Method method = clazz.getMethod("speak", String.class);
/*
* Object invoke(Object obj, Object... args)
* 1. 参数
* 1. obj : 调动此方法的对象,speak前加一个static,null也可以
* 2. args : 调用此方法传入的实参
* 2. 返回值: 调用此方法产生的返回值,如果此方法没有返回值,将会null
* */
Object result = method.invoke(p, "嘻嘻");
//Object result = method.invoke(null, "嘻嘻"); //speak方法设为静态,不报错
System.out.println(result); //speak:嘻嘻
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person p = new Person();
Class<? extends Person> clazz = p.getClass();
Method show = clazz.getDeclaredMethod("show", int.class); //暴力反射
show.setAccessible(true); //临时修改权限
show.invoke(p,15); //show:15
}
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Field;
//属性的反射一般不用,关于属性值设置通过反射get和set方法,又回到了方法的反射
public class FieldDemo {
public static void main(String[] args) {
Person p = new Person();
p.name = "啦啦";
System.out.println(p); //Person{name='啦啦',age=0}
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test //下面写那么多代码为了实现上面3行
public void method01() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//1.获取Class对象
//2.获取属性对象
//3.操作属性,get / set
Class<?> clazz = Class.forName("com.itheima03.reflect.Person");
Object p = clazz.newInstance();
/*
* Field getField(String name)
* 1. 参数: 属性名
* 2. 返回值: 通过属性名找到的属性对象
*/
Field age = clazz.getDeclaredField("age");
age.setAccessible(true);
/*
* set(Object obj, Object value)
* 1. 设置属性的对象
* 2. 设置的属性值
*/
age.set(p,18); //很少用,一般get和set方法,又回到方法的反射
System.out.println(p);//Person{name='null',age=18}
}
}
28.类加载器:.class.getClassLoader(),引扩应,双亲委派
package com.itheima01.loader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
/*
* 0. 类 .java文件 -> .class文件 -> Class对象(内存中)
* 源码 编译后 运行时
* 当.class文件被加载进内存,JVM会在堆中创建一个Class对象
*
* 1. 类加载器 : 将.class文件加载进内存, 随之产生Class对象,看成输入流(读档)
*
* 2. 三种类加载器:加载不同包下的类,像前面讲的不同的输入流
* 1. 引导类加载器 bootstrap (用c写的)。核心包下的类(rt.jar)
* 2. 扩展类加载器 extension。ext包下
* 3. 应用/系统 类加载器 application。 第三方编写的类
*
* Class对象.getClassLoader(); // 可以获取加载这个Class对象的类加载器
*
* 补充: 1. 三种类加载器存在继承关系的【不叫继承 (叫组合 composition,因为引导类加载器用C写的)】
* 2. 为什么一般情况下, 一个.class文件的Class对象只会有一个?
* 类加载器: 双亲委派机制(这机制保证一个类只会被一个类加载器加载,双亲:父类的父类)
*
* 什么情况下, 一个.class文件的Class对象会有多个?
* 多个类加载器(程序员自定义类加载器,手动操作类加载器去加载)去加载同一个.class文件
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
System.out.println(loader1); //sun.misc.Launcher$AppClassLoader@18b4aac2 //应用类加载器
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Class clazz = DNSNameService.class; // Ext扩展包下
ClassLoader loader2 = clazz.getClassLoader();
System.out.println(loader2); //sun.misc.Launcher$ExtClassLoader@45ee12a7 //扩展类加载器
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
ClassLoader loader3 = String.class.getClassLoader();
System.out.println(loader3); // null ,因为不是用java写的。 //引导类加载器
}
@Test
public void method01(){ //三种类加载器的关系:继承关系,组合关系,因为跨语言了
ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
System.out.println(loader1);//sun.misc.Launcher$AppClassLoader@18b4aac2 //应用
ClassLoader loader2 = loader1.getParent(); //获取父加载器
System.out.println(loader2);//sun.misc.Launcher$ExtClassLoader@452b3a41 //扩展
ClassLoader loader3 = loader2.getParent();
System.out.println(loader3);//null //引导 (父父)
}
}
29.设计模式:动态代理比普通代理的好处在于不用事先定义类,代理类在运行时动态生成 (反射)
29.1 动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)
package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/*
* 动态代理: 动态: 代理中介类不是静态定义,是动态生成的
* 1. 静态定义: 把这个类写死了即class ZhongJie
* 2. 动态生成: 这个类(中介类)压根就没写,在运行时由JVM动态生成(肯定涉及反射)。前提: 必须有接口
*/
public class DynamicDemo { // Dynamic反义词static
public static void main(String[] args) {
FangDong fd = new FangDong(); // 先看上房东房子
/*
static Object newProxyInstance(ClassLoader loader, //这个方法最终目的: 创建一个中介类对象!
Class<?>[] interfaces,
InvocationHandler h)
1. loader : 类加载器(加载中介类的),这个中介类不存在,需要JVM动态生成
一般是应用类加载器(第三方类)。 直接 被代理类(房东类) 的类加载器
2. interfaces : 接口的Class对象(让中介类实现的)
就是 被代理类(房东类) 所实现的接口
class对象在生成期间需要一个类加载器(将字节码文件加载生成class对象),上面1。
这个中介类需要实现接口从而拥有被代理类方法,上面2。
接下来需要重写接口里方法,下面3。
3.InvocationHandler h : 调用处理器 (InvocationHandler是一个接口)
invoke方法可以看成中介类对接口所有方法的重写
*/
ClassLoader loader = fd.getClass().getClassLoader(); //对应上面1
Class<?>[] interfaces = fd.getClass().getInterfaces();//获取此类实现的所有接口,对应上面2
// Class<?>[] interfaces = {Fd.class}; //效果同上行
// Class<?>[] interfaces = {com.itheima03.dynamic.Fd.class}; //效果同上行
// 实现接口后要重写方法,重写不了,因为中介类运行时才会存在,所以需要上面的3。
// System.out.println(loader); //sun..$APPClassLoader.. //应用类加载器
// System.out.println(Arrays.toString(interfaces)); //[interface.itheima03.dynamic.Fd] //就是FD接口
InvocationHandler h = new InvocationHandler() { //h是InvocationHandler接口实现类对象,只有这样的h才能传入Proxy.newProxyInstance这个方法
/*
* invoke函数参数: 1. proxy : 当前代理类对象(几乎没用,因为下面有Fd proxy)
* 2. method: 代理类对象当前调用的方法
* 3. args: 代理类对象当前调用方法传入的参数列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
System.out.println(Arrays.toString(args));
return null;
}
};
//11111111111111111111111111111111111如上是Proxy.newProxyInstance方法的三个参数获取
Fd proxy = (Fd) Proxy.newProxyInstance(loader, interfaces, h); //这行=号后面相当于new MyProxy
//如上行平时MyProxy proxy = new MyProxy(.) ,因为MyProxy是JVM随机命名,所以上行用Fd(这个Fd是FangDong类下面定义的)
proxy.zufang(1001); //调用h.invoke即本文件上面 ,父类引用h,子类new InvocationHandler()
//proxy.maifang(2001); //接口回调(callback)(接口形式的多态):相当于直接调用本文件上面invoke方法
}
}
package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Demo02 {
public static void main(String[] args) {
FangDong fd = new FangDong(); //先看上房东房子
ClassLoader loader = fd.getClass().getClassLoader();
Class<?>[] interfaces = fd.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if("zufang".equals(name)){
int money = (int) args[0]; // Object[] param = {money};
if(money > 1000){
fd.zufang(money);
}else{
System.out.println("钱不够...");
}
}/*else if("maifang".equals(name)){
}*/else{
method.invoke(fd,args); //中介类.其他9000个方法,走这行,即其他9000个方法依然交给房东fd自己处理
}
return null;
}
};
//1111111111111111111111111111111111111111111111111111111111111111111111111111111
Fd proxy = (Fd) Proxy.newProxyInstance(loader,interfaces,h);
proxy.zufang(888); // 被代理对象(房东) 有 10000万个方法,你只想修改其中一个
// proxy.maifang(3000);
}
}
29.2 模板:类里有抽象方法必须抽象类
package com.atguigu.test02.abstract_;
// 编写一个类,包含一个方法,可以统计 你执行任意代码的运行时间。
public class TestTemplate {
public static void main(String[] args) {
MyCalTime my = new MyCalTime();
long time = my.getTime();
System.out.println("耗时:" + time + "毫秒");
}}
//111111111111111111111111111111111111111111111111111111111111111111
abstract class CalTime{
public final long getTime(){//可以计算任意一段代码的运行时间 //这里加final的目的是不希望子类重写,改写我的算法的结构
long start = System.currentTimeMillis(); //(1)获取开始时系统时间
doWork(); //(2)执行xxxx
long end = System.currentTimeMillis(); //(3)获取结束时系统时间
return end - start; //(4)计算时间差
}
protected abstract void doWork(); //protected的目的,希望只是子类中进行访问和重写
}
//11111111111111111111111111111111111111111111111111111111111111111
class MyCalTime extends CalTime{
@Override
protected void doWork() { //重写抽象方法
long sum = 0;
for(int i=1; i<=100000; i++){
sum += i;
}
System.out.println("sum = " + sum);
}
}
29.3 单例:某个类只能有唯一的一个实例对象
package com.atguigu.test17;import org.junit.Test;
public class Test17 {
@Test
public void test1(){
SingleEnum s1 = SingleEnum.INSTANCE;
SingleEnum s2 = SingleEnum.INSTANCE;
System.out.println(s1 == s2); //true
}
@Test
public void test2(){
// SingleEnum.test();
//此时我并没有需要用到这个INSTANCE对象,但是它也创建出来SingleEnum对象,单例恶汉式
}
@Test
public void test3(){
SingleClass s1 = SingleClass.INSTANCE;
SingleClass s2 = SingleClass.INSTANCE;
System.out.println(s1==s2); //true,地址一样,只有一个对象。
}
@Test
public void test4(){
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2); //true
}
@Test
public void test5(){
LazyClass s1 = LazyClass.getInstance(); //getInstance()里必有new
LazyClass s2 = LazyClass.getInstance();
System.out.println(s2 == s1);
}
LazyClass s1;
LazyClass s2;
@Test
public void test6(){
//匿名的内部类,继承Thread类。=后面是子类,然后多态
Thread t1 = new Thread(){
public void run(){
s1 = LazyClass.getInstance();
}
};
Thread t2 = new Thread(){
public void run(){
s2 = LazyClass.getInstance();
}
};
t1.start();
t2.start();
try {
//这里用join的目的是,为了两个子线程都执行完,再执行主线程的System.out.println(s1);
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} }
//11111111111111111111饿汉式: 不管我们使用者是否需要这个对象,它都上来先给你创建好这个唯一的对象。
//形式一:对应test1()
enum SingleEnum{
INSTANCE; //单例,枚举只有一个
//public static void test(){ //用这个方法不用上面这个INSTANCE对象,对应test2() }
}
//形式二:对应test3()
class SingleClass{ //1.5之前老版的枚举 //用一个全局的静态的常量,来保存这个唯一的实例对象
public static final SingleClass INSTANCE = new SingleClass();
private SingleClass(){
}}
//形式三:对应test4()
class Single{
//用一个私有的静态的常量,来保存这个唯一的实例对象
private static final Single INSTANCE = new Single();
private Single(){
}
public static Single getInstance(){ //提供一个静态方法,来返回这个常量对象
return INSTANCE;
}}
//111111111111111111111懒汉式: 延迟创建对象。当使用者来或者这个对象,要用到对象时,我再创建。
//形式一: 对应test6()
class LazyClass{
private static LazyClass instance; //不加final,可以不用new出来 //加final必须new SingleClass()
private LazyClass(){
}
public static LazyClass getInstance(){
if(instance == null){//提高效率,已创建对象就没必要再进去,直接最后return instance; 未创建对象进入
synchronized(LazyClass.class){ //当前类的Class对象,静态方法不能出现this
if(instance == null){//安全判断
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazyClass();
}
}
}
return instance;
}
//形式二:内部类
class Lazy{
private Lazy(){}
private static class Inner{
public static final Lazy INSTANCE = new Lazy();//在内部类中,创建外部类的唯一对象
}
public static Lazy getInstance(){
return Inner.INSTANCE; //用到getInstance方法才会new Lazy(),也是懒汉式
}
}
29.4 工厂:工厂类(一个工厂),工厂接口(一车一工厂),clazz.newInstance()
package com.atguigu.test12;import org.junit.Test;
//1、简单工厂模式:优点:代码比较简洁。缺点:如果增加新的产品类型(奥迪),需要修改工厂类中getCar()。
//违反了面向对象的一个开发原则:对扩展开放,对修改关闭。public class TestFactory {
@Test
public void test03(){
Car c = SimpleFactory2.getCar("奔驰");
c.run();
}
@Test
public void test02(){
Car c = SimpleFactory.getCar(); //静态,类名. 不用创建工厂类对象
c.run();//这里也是运行宝马的run(),但是从头至尾没有出现BMW类,解耦合
}
@Test
public void test01(){ //没有工厂,如下创建对象和使用对象是同一个人完成
BMW b = new BMW(); //比如spring容器,new时要初始化很多配置信息才创建一个对象
b.run(); //使用对象,调用方法
}}
//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Car{ //对应test01()
void run();
}
class BMW implements Car{
@Override
public void run() {
System.out.println("宝马让你在车里面哭");
}
}
class Benz implements Car{
@Override
public void run() {
System.out.println("奔驰让你在车盖上哭");
}
}
class Audi implements Car{ //多了一类车,下面工厂类需要再改变,这就是简单工厂模式
@Override
public void run() {
System.out.println("奥迪让你在...");
}
}
//1111111111111111111111111111111111111111111111111111111111如下工厂类
class SimpleFactory{ //对应test02()
public static Car getCar(){
return new BMW();
}
}
class SimpleFactory2{ //对应test03()
public static Car getCar(String type){
if("宝马".equals(type)){
return new BMW();
}else if("奔驰".equals(type)){
return new Benz();
}
return null;
}
}
package com.atguigu.test12;
/*
* 2、工厂方法模式:(1)为了生产对象与使用对象分开 (2)如果增加新产品,就不需要修改原来的工厂类
* 优点:遵循了增加新产品,不修改原来的类的原则。 缺点:类太多了,一种车一个工厂
*/
public class TestFactory2 {
public static void main(String[] args) {
BaoMaFactory bf = new BaoMaFactory();
Che c = bf.getChe();
c.run();
}
}
//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Che{
void run();
}
class BaoMa implements Che{
@Override
public void run() {
System.out.println("宝马");
}
}
class BenChi implements Che{
@Override
public void run() {
System.out.println("奔驰");
}
}
class AoDi implements Che{
@Override
public void run() {
System.out.println("奥迪");
}
}
//1111111111111111111111111111111111111111111111111111111111111如下工厂接口
interface GongChang{
Che getChe();
}
class BaoMaFactory implements GongChang{
@Override
public Che getChe() {
return new BaoMa();
}
}
class BenChiFactory implements GongChang{
@Override //只需要增加一个工厂类,专门生产奥迪车,不动上面代码
public Che getChe() {
return new BenChi();
}
}
如下用反射结合上面的简单工厂和工厂方法:
package com.atguigu.test12;
public class TestFactory3 {
public static void main(String[] args)throws Exception {
Vehicle c = SimpleFactory3.getVehicle("com.atguigu.test12.QQ");
c.run(); //qq车
Vehicle c2 = SimpleFactory3.getVehicle("com.atguigu.test12.Aoto");
c2.run(); //奥拓
}
}
//11111111111111111111111111111111111111111111111111111111111111111中间没变
interface Vehicle{
void run();
}
class QQ implements Vehicle{
@Override
public void run() {
System.out.println("qq车");
}
}
class Aoto implements Vehicle{
@Override
public void run() {
System.out.println("奥拓");
}
}
//1111111111111111111111111111111111111111111111111111111111111111如下工厂类
class SimpleFactory3{
public static Vehicle getVehicle(String className)throws Exception{
Class clazz = Class.forName(className);
return (Vehicle) clazz.newInstance(); //clazz.newInstance()返回Object强转为Vehicle类
}
}
30.约束:word/txt(和xml一样都没有格式约束)传输数据,但xml格式规范
30.1 DTD约束:配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 如上行文档声明 : 版本 和 编码 -->
<!--
xml基本语法:
1. 标签分类 :
1. 成对标签 : start tag - 内容 - end tag
开标签 闭标签
<标签名> </标签名>
2. 单独标签 : 自关闭标签 <标签名/>
2. xml有且仅有一个根标签 root tag (第一个标签,xml中的所有内容都在内)
3. 标签可以拥有属性的(开标签和自关闭标签) key=value 即 id="003"
实体字符(转义字符):
< -> < less than即缩写lt
> -> > greater than
< > 小于或大于就是不等于,前端支持,java不支持
CDATA: unparsed character data 不解析的字符数据
在这里的内容是不会被解析器所解析(看成注释)
-->
<list>
<![CDATA[" if(x < > 1){ sout: xx} "]]>
<hehe id="003"/>
<book id="001">
<name>java基础入门</name>
<desc>人生必读第一本书</desc>
<price>998</price>
<extra>if(x < > 1){ sout: xx}</extra>
</book>
<book id="002">
<name>java从入门到放弃</name>
<desc>人生的最后一本</desc>
<price>11</price>
</book>
</list>
<?xml version="1.0" encoding="UTF-8" ?>
<!--
可扩展: 开发者随意命名标签
存数据 为了 以后 取数据(解析 parse)
问题:会给数据解析带来麻烦
解决: 约束(规则,规范)给xml指定约束, 其内部拥有什么内容,以及内容的编写格式都有要求
规则: list里面必须是book标签(多个)
book标签里还可以有name desc price等标签
这些标签,里面有文本内容
两种约束: DTD: document type definition 文档类型定义 (了解)
Schema : 模式 (了解) 替代DTD,全称(XML Schema Definition,XSD)
-->
<?xml version="1.0" encoding="UTF-8" ?>
<!--
DTD约束: 1. 根标签 必须是list
2. list里有book (+ : 表示1个或多个book标签)
3. book里有name desc price
4. name desc price = PCDATA(parsed character data) 可以包含文本
5. book 有一个属性 id 默认值 为 999
如下写完约束后,会自动提示
-->
<!DOCTYPE list [
<!ELEMENT list (book+)>
<!ELEMENT book (name,desc,price)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT desc (#PCDATA)>
<!ELEMENT price (#PCDATA)>
<!ATTLIST book id CDATA "999">
]>
<!--111111111111111111111111111111111111111111111111111111111111111-->
<list>
<book id="001">
<name>java基础入门</name>
<desc>人生必读第一本书</desc>
<price>998</price>
</book>
<book id="002">
<name>java基础入门</name>
<desc>人生必读第一本书</desc>
<price>998</price>
</book>
</list>
30.2 Schema约束:命名空间
//05_book.xsd
<?xml version='1.0' encoding='UTF-8' ?>
<xs:schema xmlns:xs='http://www.w3/2001/XMLSchema'
targetNamespace='http://www.itheima'
elementFormDefault="unqualified">
<!--
上面几行就是命名空间技术,需要被另一个文件引用即06_book.xml引用才可以生效。
schema约束: 约束规则用xml语法来书写的。
element : 元素(开标签到闭标签之间的内容,我们统称为标签)
complexType : 复杂类型(但凡元素有子标签或属性)
sequence : 序列 (子元素的序列)
maxOccurs: 最大出现次数,如下书标签最少出现一次,至多随意
-->
<!--11111111111111111111111111111111111111111111111111111111111111111111111111111-->
<xs:element name='书架' >
<xs:complexType>
<xs:sequence maxOccurs='unbounded' minOccurs="1">
<xs:element name='书' >
<xs:complexType>
<xs:sequence>
<xs:element name='书名' type='xs:string' />
<xs:element name='作者' type='xs:string' />
<xs:element name='售价' type='xs:string' />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
//06_book.xml调用05_book.xsd
<?xml version="1.0" encoding="UTF-8"?>
<itheima:书架 xmlns:itheima="http://www.itheima"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itheima 05_book.xsd">
<!--11111111111111111111111111111111111111111111111111111111111111111111111-->
<书>
<书名>呵呵</书名>
<作者>啦啦</作者>
<售价>嘻嘻</售价>
</书>
</itheima:书架>
如下xml用到在线的xsd,自动补全。
1.
ArrayList
底层是数组,默认长度为10,扩容系数1.5即下次15,ArrayList查询复杂度为o(1)。LinkedList
底层双向链表,好处不用进行扩容,一直往后追加,坏处是要找第n个元素时间复杂度是o(n)。JDK -> JVM -> glibc(系统调用的封装)->Linux内核
。
2.
HashMap
底层也是数组,数组大小必须是2的幂次方倍【确保数组中的每一个位置发生hash冲突的概率相同】。LinkedHashMap
底层和HashMap一样,只不过每个元素有一个指针指向下一个插入的元素,有序了。TreeMap
底层红黑树实现,特点是按照key进行排序。HashTable
线程安全(读写整个数组结构加了syn修饰即加了锁)(HashMap线程不安全,两者底层存储结构一样)。锁住整个结构代价大,所以出现了concurrent HashMap
,只锁住数组的一个桶。HashSet将HashMap的value设为null。
31.nginx的conf文件:以前网络编程中B/S架构中服务器是用socket写,用文件输入流读一个文件,读到后socket通过outputstream写出去,这些过程有了nginx后再也不用写
nginx软件链接:https://pan.baidu/s/1zvF2irI6OenDKVfvSIOZmg ,提取码:gsn9。
如果http协议是80端口的话,80端口会被隐藏,本机浏览器输入http://localhost:80启动服务端
显示如下,hello nginx是E\my81\index.html里内容。本机ipconfig显示192.168.33.71。上面红框浏览器
(客户端)是别人的电脑【局域网下同一网段】,上面两个大框是服务端,浏览器和服务器三要素对应。
nginx作用:1.反向代理
:s被代理1个公网ip,但有n台s对外服务。
2.负载均衡
:基于反向代理。
3.正向代理
:我们电脑接入VPN 后,我们对外IP地址就会变成VPN服务器的公网IP。
4.动静分离
:网页f12显示js,css,img属静态数据。
31.1 nginx反向代理与负载均衡:nginx是被广泛使用的网页服务器,在www.163的F12的Network中Headers中有server:nginx,使用nginx首当其冲,做反向代理,将请求分发到公司的多台服务器上
原生nginx没有集成很多插件,使用不方便。推荐使用openresty(在nginx基础上集成了很多lua写的插件),官网下载openresty后解压后进一级目录输入nginx.exe回车运行服务端,浏览器输入localhost默认:80端口
。
31.2 location匹配方式:location / 默认匹配所有请求,相当于第四优先级(最弱)
1.
如下最强级别=。
2.
如下优先级第二,^~
以什么开头。如下/
是路径,不是转义符。
3.
如下优先级第三,正则表达式~
。\w
匹配数字、字母、下划线,转义字符\
可以转义很多字符,比如\n
表示换行,\t
表示制表符,字符\
本身也要转义,所以\\
表示的字符就是\
。如下/
是路径。
31.3 反向代理写法:http80
31.4 负载均衡写法:基于反向代理
32.服务器:软件为tomcat或nginx,程序为servlet
nginx不支持java规范,tomcat支持。tomcat8:https://tomcat.apache/download-80.cgi。免安装,解压即可用:链接:https://pan.baidu/s/1UJM9kbIHGIXkNXlYLpcoGw ,提取码:g610。dos系统识别文件后缀名不能超过3位(先有dos后有windows),所以.htm。8080一般是用来连接代理的(8080不能省,只有80才能省)。 只有index.html是默认的,不用加在最后,localhost:8080/a.html。
如下javaee新建项目,apache-tomcat8…是前面tomcat软件解压路径。如下不需要自己开启tomcat,idea配置自动开启。
如下New和上面一样,关联tomcat软件解压路径。
如下将web文件夹当成前端static web项目,web文件夹里都可以访问,除了web-info无法访问。
如下是点乌龟运行后,浏览器自动打开的原理。改变路径 / 写法。若还是不会默认打开谷歌,需要File - Settings - Tools - Web Browsers。
33.servlet:多态,web.xml
http的request对象:由tomcat创建,并且里面数据由tomcat set进去,我们只需要get出来。
File-New-Project-Java Enterprise。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<!--
http协议两种主要请求协议
1. get (默认, 只要看到可以发起请求,但是没看到请求方式设置)
2. post (需要收到设置)
-->
<h1>get请求方式</h1>
<form action="http://localhost:8080/MyServlet" method="get">
<input type="text" placeholder="请输入用户名" name="name"> <br>
<input type="text" placeholder="请输入密码" name="pwd"> <br>
<input type="submit">
</form>
<h1>post请求方式</h1>
<form action="/MyServlet" method="post"> <!-- 本服务器前面可以省略 -->
<input type="text" placeholder="请输入用户名" name="name"> <br>
<input type="text" placeholder="请输入密码" name="pwd"> <br>
<input type="submit">
</form>
</body>
</html>
package com.itheima01.http;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
* http://192.168.139.236:8080/MyServlet 或 MyServlet换成..请求报文.html
* # request对象核心功能: 获取前端的请求数据如下:
* 1. 请求行
* 请求方式 / 请求url / 协议
* 方法:
1. String getMethod() : 获取请求方式的类型
2. StringBuffer getRequestURL() : 获取客户端发出请求完整URL
3. String getProtocol(): 获取当前协议的名称和版本
4. String getRemoteAddr() : 获取IP地址
*
* 2. 请求头
* 1. 获取指定请求头的信息: value = request.getHeader("name");
2. 获取所有的请求头的name值:request.getHeaderNames();
*
* 3. 请求参数
* tomcat: 会根据不同的请求方式(自动get请求从url获取参数,post请求从请求体里获取参数),从不同的地方获取参数并设置到request对象,这样不需要知道过程,只有结果。
**/
@WebServlet(urlPatterns = "/MyServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //doGet方法相当于get请求方式下的service方法
// System.out.println("服务器被访问了"); //idea下面显示
// response.getWriter().print("hello http"); //在网页上输出
// line(request); //请求行
String agent = request.getHeader("user-agent"); //出bug执行的
//用户访问我的网站突然崩溃,实际已获取了用户环境,在服务器所在地方可以模拟用户
String referer = request.getHeader("referer"); //防盗链
System.out.println(agent);
System.out.println(referer);
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
HashMap<String, String> map = new HashMap<>();
Iterator<String> it = map.keySet().iterator();
while(it.hasNext()){
String key = it.next();
String value = map.get(key);
}
//如下枚举Enumeration相当于上面迭代器
Enumeration<String> it2 = request.getHeaderNames(); // =号右边等价于map.keySet().iterator()
while(it2.hasMoreElements()){
String name = it2.nextElement();
String value = request.getHeader(name);
System.out.println(name + "->" + value);
}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
private void line(HttpServletRequest request) { //请求行,抓包
String method = request.getMethod();
StringBuffer url = request.getRequestURL();
String protocol = request.getProtocol();
String remoteAddr = request.getRemoteAddr();
System.out.println(method);
System.out.println(url.toString());
System.out.println(protocol);
System.out.println("访问者的ip:" + remoteAddr);
}
}
如上浏览器网址中…报文.html是Referer中的上一次访问页面,因为已跳转到如下下个页面。
如下请求行中的请求url
省略了如上的http://localhost:8080。cookie是浏览器缓存的一种。get请求中最后一行不是请求体,是谷歌浏览器工具抓包进行的参数强调渲染。
34.request对象:.getParameterMap(),post请求中文参数乱码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<h1>get请求</h1>
<form action="/ParamServlet" method="get">
用户名: <input type="text" name="name"> <br>
密码: <input type="password" name="pwd"> <br>
性别: <input type="radio" name="gender" value="boy">男
<input type="radio" name="gender" value="girl"> 女<br>
爱好:
<input type="checkbox" name="hobby" value="smoke"> 抽烟
<input type="checkbox" name="hobby" value="drink"> 喝酒
<input type="checkbox" name="hobby" value="firehead"> 烫头 <br>
<input type="submit">
</form>
<h1>post请求</h1>
<form action="/ParamServlet" method="post">
用户名: <input type="text" name="name"> <br>
密码: <input type="password" name="pwd"> <br>
性别: <input type="radio" name="gender" value="boy">
<input type="radio" name="gender" value="girl"> <br>
爱好:
<input type="checkbox" name="hobby" value="smoke"> 抽烟
<input type="checkbox" name="hobby" value="drink"> 喝酒
<input type="checkbox" name="hobby" value="firehead"> 烫头 <br>
<input type="submit">
</form>
</body>
</html>
package com.itheima02.param;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/**
请求参数: parameter ?n1=v1&n2=v2...
方法:
1. 根据name值获取value值: String value = request.getParameter(name);
2. 根据name值获取多个value值:String[] value = request.getParameterValues(name);
3. 获取所有的请求参数,封装到map中:Map<String, String[]> map = request.getParameterMap();
name为String,value为String[]
问题: post请求中文参数乱码
原因: 编解码使用的字符集不一样
编码: 浏览器 ( utf-8 )
解码: 服务器 (tomcat + servlet)
ISO-8859-1 utf-8
解决: tomcat的编码表改成utf-8
1. 永久
2. 临时 (选用) request.setCharacterEncoding("utf-8"); 注意: 必须放在获取参数之前
【get请求 tomcat8以上被优化 不乱码(post请求参数在请求体中,走io流)】
**/
@WebServlet(urlPatterns = "/ParamServlet")
public class ParamServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
String pwd = request.getParameter("pwd");
String gender = request.getParameter("gender");
String[] hobbies = request.getParameterValues("hobby"); //复选框可重复
System.out.println(name+"," + pwd + "," + gender + "," + Arrays.toString(hobbies));
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
Map<String, String[]> map = request.getParameterMap();
// System.out.println(map); //这个map没有重写tostring方法
Set<String> set = map.keySet();
for (String key : set) {
String[] value = map.get(key); //value数组没有重写tostring方法,不能
// System.out.println(key + "=" + Arrays.toString(value));
}
}
}
如下选中的是map遍历出来的结果和下面第一行一样。
34.1 请求转发:request表面上获取请求数据,还有快递员身份
如下forward方法中request是邮件及内容,response是回应权限。A模块指MyServlet,B模块指ZhouServlet。一次请求链如下只有一去一回。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
人事
<!--<a href="http://localhost:8080/ShiServlet?msg=有人来面试">发送邮件</a>-->
<a href="/ShiServlet?msg=有人来面试">发送邮件</a> <!--点发送邮件后,地址栏修改为/Shi..,没写就是get请求-->
</body>
</html>
package com.itheima03.transfer;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/ShiServlet")
public class ShiServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 接收请求
String msg = request.getParameter("msg");
System.out.println("shi:" + msg);
System.out.println("shi:我现在没空,叫周楠同学帮我面试");
request.setAttribute("extra","比我帅的不要~~"); //添加备注,这行注掉不写,ZhouServlet写request.getAttribute会得到null
//2. 请求转发
// RequestDispatcher dispatcher = request.getRequestDispatcher("/ZhouServlet"); //比作邮件服务器或快递员
// dispatcher.forward(request,response); //快递员发货
request.getRequestDispatcher("/ZhouServlet").forward(request,response); //这行等价上面两行
}
}
package com.itheima03.transfer;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/ZhouServlet")
public class ZhouServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //request和response两个参数由A模块传递来
//1. 接收请求
String msg = request.getParameter("msg");
System.out.println("zhou:" + msg);
Object extra = request.getAttribute("extra");
System.out.println("zhou:" + extra);
//2. 业务处理
System.out.println("周楠同学放下手机,难得做点事情: 面试");
//3. 响应数据
response.getWriter().print("this boy is ok,about 3k~~");
}
}
34.2 登陆案例:成功和失败页面相当于两个模块
//JdbcUtil.java
package com.itheima.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcUtil {
private static ComboPooledDataSource ds = new ComboPooledDataSource(); //先new一个连接池
public static JdbcTemplate getTemplate(){
JdbcTemplate template = new JdbcTemplate(ds); //再new一个Template,把连接池交给给JdbcTemplate管理
return template;
}
}
//LoginServlet.java
package com.itheima.login;
import com.itheima.bean.User;
import com.itheima.utils.JdbcUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.接收请求
String username = request.getParameter("userName"); //userName区分大小写
String password = request.getParameter("password");
//2.业务处理 //如下只有两种结果: 返回一行(一行是一个对象) 或 返回null
String sql = "select * from user where username = ? and password = ?";
JdbcTemplate template = JdbcUtil.getTemplate(); //一定要封装成JdbcUtil,不然new ComboPooledDataSource,每访问一次就会创建一次连接池,内存崩。
User user = template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), username, password);
//把如下注掉,用如上一行,即查不到数据,抛出异常,不抓异常,这样会密码错误直接跳转500(500服务器错误,404地址输错),而不是error.html
/*
User user = null;
try {
user = template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), username, password);
} catch (DataAccessException e) {
e.printStackTrace();
}*/
if(user != null){
request.getRequestDispatcher("/success.html").forward(request,response); //登录成功
}else{
request.getRequestDispatcher("/error.html").forward(request,response);
}
}
}
// User.java
package com.itheima.bean;
public class User {
private Integer id; //和表字段一致
private String username;
private String password;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
//login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录页面</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/login.css" rel="stylesheet">
<script src="js/jquery.js"></script>
<script src="js/bootstrap.js"></script>
</head>
<!--1111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<div class="container text-center">
<form class="form-signin" action="/LoginServlet">
<h2 class="form-signin-heading">登录页面</h2>
<input type="text" name="userName" class="form-control" placeholder="用户名" required autofocus>
<input type="password" name="password" class="form-control" placeholder="密码" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
</form>
</div>
</body>
</html>
35.response对象:text/html,重定向(.setStatus,.setHeader)
package com.itheima01.http;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(urlPatterns = "/MyServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("服务器被访问了");
// int i = 1/0; //服务器崩了出异常,没有try catch,状态码500
response.setStatus(302); //状态码设置,只有302需要我们手动设置
// response.setStatus(404); //这样设置状态码是404,servlet优先级高于tomcat,但是访问服务端依旧成功
// response.getWriter().print("hello response"); //前端网页打印出
//响应体输出流设置,字节输出流(写) //待会下载文件案例中使用
// ServletOutputStream os = response.getOutputStream();
// os.write("abc".getBytes()); //01_响应报文.html中点击“访问MyServlet”超链接跳转到显示“abc”的新页面
//方便输出字符数据,不用转换为字节了,print和write一样
// PrintWriter writer = response.getWriter();
// response.getWriter().print("xx");
}
}
请求报文分get和post两种,在响应报文里看到了非servlet设置的内容,那就是tomcat设置的。将前端改为href=“/MyServlet123”,则会出现404。servlet关了,tomcat自动设置为500状态码。
如下体中hello response长度一共为Content-Lenth为14,头中GMT格林位置需+8小时为东八区北京时间。如下行和头都是tomcat默认自动设置,体是我们servlet手动设置。想修改状态码,需要在servlet(对我们暴露的小程序)中修改。
如下点击后,5秒后跳转到百度。
package com.itheima02.header;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/RefreshAndTypeServlet")
public class RefreshAndTypeServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("服务器被访问了");
// response.setHeader("refresh","5;http://www.baidu");
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
/*
* 问题: 响应体中文乱码 。request.setCharacterEncoding("utf-8");
* 原因: 编码: 服务器(utf-8 : servlet:因为在servlet里写的"哈哈",idea右下角显示utf-8)
* 解码: 浏览器(iso-8859-1 : tomcat默认设置如下行)
// response.setHeader("content-type","text/plain"); //在Response Headers(浏览器F12看出)中多出tomcat默认设置(不加这行不显示):Content-Type: text/plain;charset=ISO-8859-1
* 解决: servlet中手动设置覆盖tomcat默认设置
*
* windows系统: txt html jpg avi ...
* 早期MIMEType : text/plain text/html image/jpeg (tomcat解压包中web.xml中有对应说明)
*/
// response.setHeader("content-type","text/plain;charset=utf-8"); //新页面显示 <h1>哈哈</h1>
// response.setHeader("content-type","text/html;charset=utf-8"); //点击网页上RefreshAnd..跳转新页面显示 哈哈
response.setContentType("text/html;charset=utf-8"); //setContentType是简易API,同上一行
response.getWriter().print("<h1>哈哈</h1>");
}
}
因为重定向不是一次请求链,所以超过域对象,不能用request传递数据。ZhouServlet收不到人事的msg,能收到ShiServlet的response.setHeader(…/ZhouServlet?msg=…)。
package com.itheima03.redirect;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/ShiServlet")
public class ShiServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 接收请求
String msg = request.getParameter("msg");
System.out.println("shi:" + msg);
//2. 重定向
response.setStatus(302);
response.setHeader("location","/ZhouServlet?msg=xx"); //不写?msg=xx,ZhouServlet就收不到msg,写了就能收到
}
}
package com.itheima03.redirect;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/ZhouServlet")
public class ZhouServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 接收请求
String msg = request.getParameter("msg");
System.out.println("zhou:" + msg);
//2. 业务处理
System.out.println("这个哥们还不错");
//3. 响应数据
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("这个哥们还可以,大约3K");
}
}
如下浏览器F12,第二行没有带参数,拿不到msg。
前面登陆成功请求转发
到成功.html页面,失败.html…。用请求转发不合适,因为没有传递数据,应该用重定向。请求转发一次请求,地址指向第一次访问位置如下(网址阅读性太差,实际页面应该为localhost:8080/success.html)。
如下红框用重定向
,登陆失败逻辑错误,之后再改。
如下重定向,第一次请求收到302。
输入账号和密码后跳转如下。
35.1 文件下载:request/response
首先服务器要有文件,在网络编程做过文件上传。在servlet里写代码覆盖tomcat默认设置。
//02文件下载.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--1111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<!--方案A:浏览器打开此网页点击后自动下载的话,说明tomcat默认设置,因为servlet一个字没写-->
<!-- <a href="dir/1.zip">1.zip</a> <br>
<a href="dir/2.exe">2.exe</a> <br>
<a href="dir/3.txt">3.txt</a> <br>
<a href="dir/4.jpg">4.jpg</a> <br>-->
<a href="/DownloadServlet?file=1.zip">1.zip</a> <br>
<a href="/DownloadServlet?file=2.exe">2.exe</a> <br>
<a href="/DownloadServlet?file=3.txt">3.txt</a> <br>
<a href="/DownloadServlet?file=4.jpg">4.jpg</a> <br>
</body>
</html>
package com.itheima04.down;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet(urlPatterns = "/DownloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String file = request.getParameter("file"); // 拿到1.zip之类
//1. 告诉浏览器如何处理文件
response.setHeader("content-disposition","attachment;filename=x" + file); //下载以x命名
//2. 读取服务器本地文件,写出浏览器。绝对路径 等会要改成 相对路径
//以前java工程: web/dir/文件(相对路径,里面是1.zip,2.exe等需下载的文件) //FileInputStream fis = new FileInputStream("E:/mywork/IdeaProjects/class897/web-day03/web/dir/" + file);
//web工程: main方法在tomcat里,造成和java工程相对路径不一样
ServletContext servletContext = request.getServletContext(); //request小域对象可获取ServletContext大域对象
String realPath = servletContext.getRealPath("dir/" + file); //绝对路径换成相对路径
FileInputStream fis = new FileInputStream(realPath);
ServletOutputStream os = response.getOutputStream();
int length;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){
os.write(buffer,0,length);
}
fis.close();
os.close();
System.out.println("文件下载完毕");
}
}
响应体交给浏览器,浏览器自动下载。
36.cookie:再次时请求头携带cookie到服务端
如下localhost是域名,不包含协议和端口。
浏览器的设置中搜索cookies - 网站设置 - localhost。
如下方式只能查看当前域名的cookies,如上方式可以查所有域名cookies。
只有localhost(域名)默认cookie存活时间是浏览器打开到关闭(不是窗口),如下改为30天(游客访问京东购物车一般也为30天)。在浏览器的设置里搜索清除浏览数据。
如下name虽相同,但是路径不同,不会覆盖。name相当于文件名,value相当于文件内容,同路径同name就会覆盖。
如上浏览器现在有两个cookies:一个是 / product galaxyNote7,另一个是 /abc product huawei。这两个cookies都会被携带进如下/abc…服务器。
36.1 登陆案例_记住我:js访问浏览器数据用document
如下两个cookies,一个保存用户名,另一个保存密码。
//改进login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录页面</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/login.css" rel="stylesheet">
<script src="js/jquery.js"></script>
<script src="js/bootstrap.js"></script>
<script>
//你传入key=name,getCookie函数返回value=admin
var getCookie = function (key) { //匿名函数,脱离对象,不需要用cookie对象调用,直接调函数名
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
}
//document.cookie 可以访问到当前页面域名下的所有cookie信息
//console.log(document.cookie) -> name=admin; pwd=123 需要上面.replace按分号等切割
</script>
<!--1111111111111111111111111111111111111111111111111111111111111111111111-->
<script>
$(function () { //页面加载后执行的事件
var name = getCookie("name"); //上面script定义的getCookie
var pwd = getCookie("pwd");
/* if(name != null && name != ""){
}*/
if(name && pwd){ //判空同上 ,js中空字符串也就是false
$("#name").val(name) //id选择器,往body网页文本框填入
$("#pwd").val(pwd)
$("#remember").prop("checked",true) //name和pwd都不为空,勾起
}
})
</script>
<!--11111111111111111111111111111111111111111111111111111111111111111111111-->
<script>
function changeImg(img) { //浏览器检测到网页的元素或属性改变,会自动刷新网页(点击验证码刷新)
var time = new Date().getTime(); //时间撮
img.src = "/codeServlet?time="+time //让它变,不能不加参数或故意写错即/codeServlet2,会报404,time=..这个参数一并发给服务器,虽服务器用不到,但整个属性值变了。
}
</script>
</head>
<!--1111111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<div class="container">
<form class="form-signin" action="/LoginServlet">
<h2 class="form-signin-heading text-center">登录页面</h2>
<input type="text" id="name" name="username" class="form-control" placeholder="用户名" required autofocus>
<input type="password" id="pwd" name="password" class="form-control" placeholder="密码" required>
<input type="text" placeholder="验证码" name="code" class="form-control">
<img src="/codeServlet" alt="" onclick="changeImg(this)"> <br>
<input type="checkbox" id="remember" name="remember" value="yes"> 记住我
<button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
</form>
</div>
</body>
</html>
package com.itheima.login.web;
import com.itheima.login.service.LoginService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet(urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* TODO: session : code
* */
String code = request.getParameter("code"); //获取前端用户写的发送的code参数即输入验证码框
// HttpSession session = request.getSession(); //request.getSession()挂号,response只有.addCookie()
String rightCode = (String) request.getSession().getAttribute("code"); //服务器通过codeServlet.java生成的
if(!rightCode.equalsIgnoreCase(code)){
//验证码错误其实不应该跳到error.html ,应该停留在当前页面显示错误提示,这整段应该写在其他逻辑前面
request.getRequestDispatcher("/error.html").forward(request,response); //跳转到失败页面
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
String username = request.getParameter("username"); //拿到前端的参数
String password = request.getParameter("password");
//逆向编程 : 没有的先当它有
LoginService service = new LoginService();
boolean result = service.login(username,password);
if(result){
/*
* TODO: cookie : username ,password
* */
String remember = request.getParameter("remember"); //前端 记住我 传来的参数
if("yes".equals(remember)){
Cookie nameCookie = new Cookie("name", username); //没有做数据库校验
Cookie pwdCookie = new Cookie("pwd", password);
nameCookie.setMaxAge(60*60*24*30); //存活30天
pwdCookie.setMaxAge(60*60*24*30);
response.addCookie(nameCookie);
response.addCookie(pwdCookie);
}
response.sendRedirect("/success.html"); //重定向的简易api
}else{
request.getRequestDispatcher("/error.html").forward(request,response); //登录失败,请求转发
}
}
}
如下在控制台输出,在html中一样,用于数据回显。
37.session:根据sessionid,服务器才能找到session(session对象在服务器内存中增加压力)
病历本session,session记录大量信息,直接进行传递会卡,所以用cookie传sessionid(Cookie将sessionid传来传去,4k不宜过大,Cookie用sessionid标记用户)。cookie默认生命周期是会话
即关闭浏览器同理session也是。
如下挂号这一行代码都一样,小域对象request获取大域对象session。< session - config > 是在tomcat软件解压后的conf文件夹里的web.xml(如订单30分钟不付款就会自动取消了)。
解决关闭浏览器cookie就没了?用Session持久化方案覆盖了tomcat默认生成的cookie即上面灰字(因为session默认保留30分钟,所以sessionid即cookie设为30分钟)。
如下删除后每次访问,生成的sessionid都是不一样的。
37.1 登录案例_验证码:request.getSession().setAttribute(“code”,sb.toString())
如下红字:一个存,一个取,该用什么载体实现存取?如最右边。如下是两次请求,不是一次请求链,所以request不行。存在session里好处:30分钟后自动没了。
login.html和LoginServlet.java见上一章节中。
//如下gui不需要掌握,被JavaME嵌入式代替 , codeServlet.java
package com.itheima.login.web;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/codeServlet")
public class CodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// gui 生成图片
// 1 高和宽
int height = 50;
int width = 70;
String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
Random random = new Random();
// 2 创建一个图片
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 3 获得画板
Graphics g = image.getGraphics();
// 4 填充一个矩形
// * 设置颜色
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.WHITE);
g.fillRect(1, 1, width - 2, height - 2);
// * 设置字体
g.setFont(new Font("宋体", Font.BOLD | Font.ITALIC, 25));
StringBuffer sb = new StringBuffer();
// 5 写随机字
for (int i = 0; i < 4; i++) {
// 设置颜色--随机数
g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
// 获得随机字
int index = random.nextInt(data.length());
String str = data.substring(index, index + 1);
// 写入
g.drawString(str, width / 6 * (i + 1), 20);
sb.append(str);// 获取验证码数据
}
//TODO: 验证码保存到session中,完成上面的第一步:服务器生成验证码并进行保存
request.getSession().setAttribute("code",sb.toString()); //这行最重要
System.out.println("作弊:" + sb.toString());
// 6 干扰线
for (int i = 0; i < 3; i++) {
// 设置颜色--随机数
g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
// 随机绘制先
g.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height));
// 随机点
g.drawOval(random.nextInt(width), random.nextInt(height), 2, 2);
}
// end 将图片响应给浏览器
ImageIO.write(image, "jpg", response.getOutputStream());
}
}
如下在A1中只要说引入什么jar包就行。apache-maven-3.6.1解压到F/develop文件夹下,以前在WEB-INF右击new一个lib文件夹,现在不用,只需写dependency标签就行。
如下Maven解压包里boot文件夹里是一个.jar,所以maven主要代码是java写的,所以保证jdk要装。私服就是如阿里网速快把maven官方仓库下载下来,别人到阿里这里下载。resources文件夹
里放c3p0-config.xml等,test包下的类必须以XxTest结尾
,只有main文件夹里代码才会被打包。
从远程仓库中获取坐标:1. 中央仓库 : http://mvnrepository/ 。2. 私服: 阿里云: https://maven.aliyun/mvn/search。进入前面两个网站搜索并进行复制,groupId是com.。
由于网络的原因,jar包下载不完全,这些不完整的jar包都是以lastUpdated结尾。此时maven不会再重新帮你下载,需要你删除这些以lastUpdated结尾的文件。
如果本地仓库中有很多这样的以lastUpadted结尾的文件,可以执行如下脚本来删除,如下是如上clean…bat文件,第一行路径不要加双引号。
set REPOSITORY_PATH=E:\javaDevlopment\maven\health_repository
rem 正在搜索...
for /f "delims=" %%i in ('dir /b /s "%REPOSITORY_PATH%\*lastUpdated*"') do (
del /s /q %%i
)
rem 搜索完毕
pause
//c3p0-config.xml
<c3p0-config>
<default-config>
<!-- 数据库的连接参数 ,加上? useUnicode= 防止不是utf-8-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/heima_travel89? useUnicode=true&characterEncoding=UTF8</property>
<property name="user">root</property>
<property name="password">1234</property>
</default-config>
</c3p0-config>
非空和3-6等是前端校验。后端校验也用jquary-validator(里面支持ajax请求)。status和code是数据表中两个字段。
<!--apache mail-->
<dependency>
<groupId>org.apachemons</groupId>
<artifactId>commons-email</artifactId>
<version>1.5</version>
</dependency>
package com.heima.travel.utils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import java.util.ResourceBundle;
public class MailUtil {
private static String host;
private static String charset;
private static String username;
private static String password;
static{
ResourceBundle bundle = ResourceBundle.getBundle("mail");
host = bundle.getString("mail.host");
charset = bundle.getString("mail.charset");
username = bundle.getString("mail.username");
password = bundle.getString("mail.password");
}
public static void sendEmail(String toUser,String msg) throws EmailException {
//1、创建一个HtmlEmail对象
HtmlEmail htmlEmail = new HtmlEmail();
//2、设置邮箱服务器的参数 (qq发件服务器)
htmlEmail.setHostName(host); // smtp.163
//设置编码 (程序: utf-8 -> GBK) 收件箱: GBK
htmlEmail.setCharset(charset);
//3、设置邮箱验证(客户端授权) 发件人
//username:邮箱账号
//password:客户端授权码
htmlEmail.setAuthentication(username,password);
//4、设置收件人 和 发件人
htmlEmail.setFrom(username, "黑马897_boy");
htmlEmail.addTo(toUser, "瘪三");
//5、设置主题 和 正文
htmlEmail.setSubject("【黑马官网:激活邮件】");
// htmlEmail.setTextMsg("注册成功"); // 纯文本
htmlEmail.setHtmlMsg(msg); // html : 激活码
//6、发送邮件
htmlEmail.send();
}
}
如下在resources文件夹下。
//mail.properties
mail.host = smtp.qq.com
mail.charset = gbk
mail.username = nightswatch1@qq
mail.password = ulsfpuawdvdyffja
事务四大特性:1.原子性(A)
:如转账,一个人转出,一个人转入,这两步操作是不能分的,要么都做,要么都不做。
2.一致性(C)
:事务的成功与否,数据库的数据都满足生活正常逻辑,如转账,总额不会变。
3.隔离性(I)
:并发访问数据库时,一个用户的事务不被其他事务干扰。
4.持久性(D)
:事务提交后,它对数据库的改变是持久的,断电也变。
注意隔离性,虽然事务间没有影响,但两个事务同时并发且操作同一条数据会出现如下问题,oracle默认解决脏读,mysql默认解决不可重复读:
1.脏读
:一个事务读到另一个事务未提交的数据。
2.不可重复读
:事务A第一次读账户余额为1000,事务B这时操作了这条数据并且提交+1000的事务。事务A又去读,变为2000了,读不到重复的1000了。
3.幻读
:不可重复读强调读取内容【select *,update】
不一致,幻读强调读取数量【select count(*),delete或insert】
不一致。
用事务隔离级别解决如上问题:
read uncommitted:不会去设的,未提交会出现脏读。
read committed:解决脏读,还是存在不可重复读和幻读。
repeatable-read:mysql数据库默认隔离级别,解决不可重复读,还是存在幻读。
serialize:串行化,解决所有。
38.MVC:Spring_IOC(对象创建交给spring工厂),Spring_AOP(基于动态代理功能增强)。Runapplication启动类:控制层(UserController),业务层(UseService,UseServiceImpl),持久层(User即pojo,UseMapper.xml,UseMapper接口)
微服务
:每个功能模块都独立打包部署到不同的系统上去,分开后每个微服务怎么去调用呢?这时就用到springcloud或dubbo
一些远程调用协议,也就是相当于每个人怎么去沟通呢?沟通使用的这个语言就是协议
如普通话,这么多的微服务都是一个单体应用系统,用到了springboot来解决S(spring)S(springmvc)M(mybatis)搭建应用时大量的手工配置问题,所以springboot是一个快速搭建应用的框架,不是微服务
。为了管理和编排这些微服务用springcloud。ORM
:对象(编程语言中对象)关系(关系型数据库中库表)映射。
以前获取表单数据
最终封装成一user对象
,要先获取所有请求参数再手动封装一下,SpringMVC中在方法上声明一个user类型参数就可以了,让C和V变的更加的简单了。
如下printLog()比作System.out.println(“功能的增强”);的封装。Aspect就是Advice加在joinPoint的前面还是后面等。
39.RPC:远程过程调用,我这台电脑调用一个函数(这函数在另一个电脑上执行),像是本地正常运行函数一样,不需要关注远程(网络连接,断开,传数据)细节
1.
为什么远程remote?a和b两个参数打包发给服务端,本机运行的程序主要干的事是A,执行A的过程需要借助B功能,但是B功能不属于A功能范围内事情。所以我们会调实现B功能的集群,获取它们计算出来的结果。请求和响应和HTTP协议很像,grpc谷歌框架就是使用了http协议。
3 .
数据包该如果封?程序内存中数据结构
封装到传输时数据结构
(文本或字节数组),传来后还要将数据(即字节或文本)恢复到内存中,这传一次就叫做序列化和反序列化
。HTTP中json等就是序列化方式,一个数据在内存中的表达可能没法直接给人看的,或者没法直接两个进程间无法互相阅读的,除了共享内存。json和xml都是文本化的序列化方式
,高效的grpc中一般不会用json这种序列化方式,比如用jdk自己的序列化方式,protobuf等。
4 .
如果需要提高rpc性能的话除了序列化和反序列化,数据传输过程中需要提高网络IO性能。网络IO中涉及很多的系统调用,有对这些系统调用较好的封装如java中有名的rpc框架dubbo
。dubbo中使用netty作为网络传输的一个框架实现,netty又是基于java的NIO,NIO最终调用一些系统调用,这些系统调用能够实现网络传输过程中零拷贝,多路复用等等充分提高了网络性能。
40.ajax:xhr对象,$.,浏览器输入框加载脚本
同步加载
:1.
当前页面直接跳转到另一个页面(覆盖),url发生变化。2.
直接新开一个页面,url发生变化。同步注册
:所有信息填完了,点提交等待服务器响应是同步请求(有顺序,等服务器响应结果,不是同步比异步差,之前写的都是同步请求,一个一个发,B覆盖A页面。前端没有线程,下面线程是比喻)。
如上四个箭头就是四次变化。要实现异步请求,必须借助ajax,不然都是同步请求
。ajax(Asychronous异步 Javascript And XML)应用:1.
搜索内容自动补全,2.
注册页面自动检测用户名是否存在。
//01_js中的ajax请求.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function method01() {
//1.创建一个xhr对象(浏览器内核/引擎支持)
var xhr = new XMLHttpRequest();
//2.设置一个监听事件 //5个状态4次改变, 当请求状态改变(点击前端发请求按钮)的时候被触发
xhr.onreadystatechange = function (ev) {
// console.log(xhr.readyState) //1 2 3 4
if(xhr.readyState == 4){ // 前端可以拿到服务器响应后的数据,只4对于我们才有意义
if(xhr.status == 200){
alert(xhr.responseText) //响应成功,拿到后台发来的“hello boy”
}else{
alert("响应失败了~~")
}
}
}
//3.设置请求数据 ,如上的2相当于最后一步
/* method:请求的类型;GET 或 POST //GET请求发送参数拼接在url中如下
url:文件在服务器上的位置
async:true(异步)或 false(同步),一般默认true */
xhr.open("get","/AjaxServlet?name=hehe",true)
//4.发送请求
xhr.send() //将请求发送到服务器。send里参数string:仅用于POST请求,用来放请求体 ,Get参数放在上行url中。
}
</script>
<!--111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<script>
function method02() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function (ev) {
if(xhr.readyState == 4 && xhr.status == 200){
alert(xhr.responseText)
}
}
xhr.open("post","/AjaxServlet",true)
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded")
//一般编码解码由浏览器完成,但ajax的post请求url编码要上行手动设置。
xhr.send("name=haha")
}
</script>
</head>
<!--11111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<input type="button" value="js中的ajax(get)请求" onclick="method01()"> <br> <br>
<input type="button" value="js中的ajax(post)请求" onclick="method02()"> <br> <br>
</body>
</html>
post请求参数(“name=haha”)需要url编码才能传到服务器。
//02_jquery中的ajax请求.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery-3.3.1.js"></script>
<script>
/*
GET请求:$.get(url, [data], [callback], [type]) // []表示可有可无
1. url : 请求地址
2. data : 请求参数 (text,json)
3. callback : 响应成功的回调
4. type : 响应数据的类型(text 默认值,json)
*/
function method01() { //name= 可以用?拼在前面/AjaxServlet的后面 //$是全局对象
$.get("/AjaxServlet","name=xixi",function (result) {
alert(result) //result是服务器传来的"hello boy"
},"text")
}
function method02() { //不可拼在/AjaxServlet后 //不用考虑url编码和post请求参数放哪里问题
$.post("/AjaxServlet","name=huhu",function (result) {
alert(result)
},"text")
}
function method03() {
/*
AJAX完整请求: $.ajax([settings]) 考虑请求失败的情况用完整请求,上面两个方法没有失败
上行settings = object如下
{
n1 : v1,
n2 : v2...
}
*/
$.ajax({
url:"/AjaxServlet",
async:true,
data:"name=tom",
type:"GET", // 请求方式
dataType:"text", // 响应数据的类型
success:function(data){ // 成功的回调
alert(data);
},
error:function(){ // 失败的回调
alert("数据没有成功返回!")
}
});
}
function method04() {
$.post({
url:"/AjaxServlet",
async:true,
data:"name=tom",
dataType:"text", // 响应数据的类型
success:function(data){ // 成功的回调
alert(data);
},
error:function(){ // 失败的回调
alert("数据没有成功返回!")
}
});
}
</script>
</head>
<!--111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<input type="button" value="jquery中的ajax(get)请求" onclick="method01()"> <br> <br>
<input type="button" value="jquery中的ajax(post)请求" onclick="method02()"> <br> <br>
<input type="button" value="jquery中的ajax完整请求" onclick="method03()"> <br> <br>
<input type="button" value="jquery3.0中的ajax签名方式请求" onclick="method04()"> <br> <br>
</body>
</html>
package com.itheima01.ajax;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/AjaxServlet")
public class AjaxServlet extends HttpServlet { //服务端
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
System.out.println(name);
System.out.println("服务器被访问了!");
response.getWriter().print("hello boy");
}
}
浏览器输入框输入:data:text/html,<h1>hello<h1>
,页面自动显示hello。data:text/html,<script src="https://cdn.bootcdn/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
,F12 Network中刷新页面显示如下,这时就可在console中使用jquery了。
监听99号端口的小程序,直接双击打开:链接:https://pan.baidu/s/1nHhdf7Qerq3bEkOfXr8FXw 提取码:eipc。
如上在console中回车发送后,如下Network中会多一条localhost请求。
41.json:new ObjectMapper()
JSON:JavaScript对象简单表示法(JavaScript Object Notation),与js对象
区别是JSON只有属性没有方法,比XML小快(因为json不需要标签)。
如下右边java中list集合
,前端看不懂无法解析,变成xml后,前端可以解析。任意数组皆为json,上面为混合模式。
41.1 json在前端和后端中表达:json在js中的表达.html(前端)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
var jsonObj = {"name" : "张三" , "age" : 18 , "married" : true} //name必须string格式,value无所谓
var jsonObj2 = {"name" : "李四" , "age" : 19 , "married" : false}
console.log(jsonObj.name + "," + jsonObj.age + "," + jsonObj.married) //对象.属性名
var jsonArray = ["哈哈", 1 , true] //也是json对象
console.log(jsonArray[0])
var jsonArray2 = [jsonObj,jsonObj2] //[0,1]
console.log(jsonArray2[1].name)
//jsonComplex.list指的是jsonArray2数组,0索引位是jsonObj
var jsonComplex = {"list" : jsonArray2, "count" : 2}
console.log(jsonComplex.list[0].married) // jsonObj.married
</script>
</head>
<body>
</body>
</html>
//JsonDemo.java(后端)
package com.itheima02.json;
import org.junit.Test;
import java.util.ArrayList;
/*
* Json在java中就是普通string (java不识别json,xml,sql,正则表达式,都认为是字符串)
* Json Java
* 1. 对象 : javaBean
* 2. 数组 : List
* 3. 混合 : Map<Key, List>
*/
public class JsonDemo { //Java数据(javaBean)变成 Json
@Test
public void method01(){
Friend f = new Friend("张三", 18, true);
//System.out.println(f.toString()); //.toString()可以不写,默认调用.toString() //Friend{name='张三',age=18,..}
System.out.println(f.toJson()); // {"name":"张三","age":18,"married":true}
}
@Test
public void method02(){
ArrayList<Friend> list = new ArrayList<>();
Friend f1 = new Friend("张三", 18, true);
Friend f2 = new Friend("李四", 19, false);
list.add(f1);
list.add(f2);
System.out.println(list); //打印如下,默认调用.toString()
//[Friend{name='张三', age=18, married=true}, Friend{name='李四', age=19, married=false}]
StringBuilder sb = new StringBuilder(); //如下将上面list转化为json的数组
sb.append("[");
for (int i = 0; i < list.size(); i++) { //i最多=2个对象
String s = list.get(i).toJson();
if(i == list.size() - 1){ //这行意思为最后一个
sb.append(s); //不加逗号
}else{
sb.append(s + ","); //中间加一个逗号
}
}
sb.append("]");
System.out.println(sb.toString());
}
}
package com.itheima02.json;
public class Friend {
private String name;
private Integer age;
private Boolean married;
public Friend() {
}
public Friend(String name, Integer age, Boolean married) {
this.name = name;
this.age = age;
this.married = married;
}
@Override
public String toString() {
return "Friend{" +
"name='" + name + '\'' +
", age=" + age +
", married=" + married +
'}';
}
public String toJson() {
return "{\"name\" : \""+name+"\",\"age\":"+age+",\"married\":"+married+"}";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getMarried() {
return married;
}
public void setMarried(Boolean married) {
this.married = married;
}
}
42.ajax和json综合:ajax_json_union.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery-3.3.1.js"></script>
<script>
function method01() {
/*
* url : 请求地址
* data : 请求参数 (text , json)
* callback : 响应成功的回调函数
* type : 响应数据的类型 (text , json)
* 1. text : 响应回来的数据变成string
* 2. json : 响应回来的数据变成json(Object)
*/
// var param = "name=admin&pwd=123" // text类型的参数
var param = {"name" : "admin" , "pwd" : "123"} //json类型的参数
$.get("/UnionServlet",param , function (result) {
console.log(typeof result)
console.log(result)
for (var i = 0; i < result.length; i ++) { //前端解析json格式的数据
console.log(result[i].name)
}
},"json")
}
</script>
</head>
<!--11111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<input type="button" value="发送一个异步请求" onclick="method01()">
</body>
</html>
如上把$.get中json换成text,如下的object就是string。
package com.itheima03.union;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima02.json.Friend;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
@WebServlet(urlPatterns = "/UnionServlet")
public class UnionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name"); //后台解析json格式的数据
String pwd = request.getParameter("pwd");
System.out.println(name + "," + pwd);
ArrayList<Friend> list = new ArrayList<>();
list.add(new Friend("瘪三",18,true));
list.add(new Friend("瘪四",19,false));
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(list);
response.setContentType("text/html;charset=utf-8"); //响应体中文乱码问题
response.getWriter().print(json);
}
}
如下是main方法入口,换成猫(web工程的入口在猫里)再点乌龟加载servlet。
//05_校验用户名是否可用.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--
异步请求:
1. 事件: 鼠标离开输入框时候触发(失去焦点) onblur
2. 逻辑:
1. 发起异步请求 : 局部刷新页面
2. 请求地址 : /CheckServlet
3. 请求参数 : name = ?
4. 回调(接收响应) :
true : 提示可以注册
false : 不可以注册
3. 后台:
sql : select * from table where name = ?;
查询数据库,查得到, 返回false(告诉前端你不可以注册了) , 查不到, 返回true
-->
<script src="js/jquery-3.3.1.js"></script>
<script>
function method01(value) {
// console.log(value)
$.get("/CheckServlet",{"name" : value},function (result) {
if(result == "true"){ //result为后台发来response.getWriter().print里内容
$("#myspan").text("恭喜你,用户名可用")
$("#myspan").css("color","green")
}else{
$("#myspan").text("不好意思,此用户名已被使用~~")
$("#myspan").css("color","red")
}
},"text")
}
</script>
</head>
<!--111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<!--this.value指如下当前输入框输入的内容,当鼠标离开即onblur时获取-->
<input type="text" placeholder="请输入要注册用户名" onblur="method01(this.value)">
<span id="myspan"></span>
<br>
<input type="text" placeholder="请输入要注册的密码"> <br>
</body>
</html>
package com.itheima04.check;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/CheckServlet")
public class CheckServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
if("jack".equalsIgnoreCase(name)){ //假设数据库里就一个jack
response.getWriter().print("false"); //不要写成println,本来是换行,但在前端中体现是多一个空格
}else{
response.getWriter().print("true");
}
}
}
//search-page.jsp //自动提示案例
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">
.content{
width:643px;
margin:200px auto;
text-align: center;
}
input[type='text']{
width:530px;
height:40px;
font-size: 14px;
}
input[type='button']{
width:100px;
height:46px;
background: #38f;
border: 0;
color: #fff;
font-size: 15px
}
.show{
position: absolute;
width: 535px;
height:100px;
border: 1px solid #999;
border-top: 0;
}
</style>
<script type="text/javascript" src="js/jquery-3.3.1.js"></script>
<script type="text/javascript">
/*
点击onclick: 按下onkeydown + 抬起onkeyup //文本框内容输入变化onchange不行
* 1. 事件 : onkeyup (键盘抬起)
* 2. 逻辑:
* 异步请求
* 请求地址: /AutoServlet
* 请求参数: value = x 文本框输入的内容
* 回调(响应):
* 3. 服务器
* sql : select * from table where name like ? limit 4; 下拉框显示4个
* 1. 查询数据库 : ? = "value%" 返回list 再变成json数组 再遍历
*/
function method01(value) {
// console.log(value)
if(!value){ // value是空的,到服务器就剩下%符号,变全部查询,所以在这阻断
$(".show").css("display","none") // if(!value)为true
return; // 不往后执行
}
$.get("/AutoServlet",{"value" : value},function (result) {
// console.log(result)
var content = ""
$(result).each(function (index,element) { //jquary的遍历
// console.log(index + "," + element)
content += element.name + "<br>" //不写<br>,就是一行,拼接
})
$(".show").css("display","block") //display由隐藏改回块级元素
$(".show").html(content) //.html自动识别换行<br>标签
},"json")
}
function method02() {
alert("xx")
}
</script>
</head>
<!--11111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
<div class="content">
<img alt="" src="img/logo.png"><br/><br/>
<input type="text" name="word" onkeyup="method01(this.value)">
<input type="button" value="搜索一下" onclick="method02()">
<div class="show" style="display: none"></div> <!--自动提示下拉框-->
</div>
</body>
</html>
console.log(index + “,” + element) 如下:
package com.itheima05.auto;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet(urlPatterns = "/AutoServlet")
public class AutoServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String value = request.getParameter("value");
String sql = "select * from user where name like ? limit 4"; //limit0,4 默认0不写
JdbcTemplate template = JdbcUtil.getTemplate();
String param = value + "%";
List<User> list = template.query(sql,new BeanPropertyRowMapper<>(User.class),param);
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(list);
System.out.println(json);
response.setContentType("text/html;charset=utf-8");
response.getWriter().print(json);
}
}
System.out.println(json)如下:
package com.itheima05.auto;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcUtil {
private static ComboPooledDataSource ds = new ComboPooledDataSource();
public static JdbcTemplate getTemplate(){
// JdbcTemplate template = new JdbcTemplate(ds);
// return template;
return new JdbcTemplate(ds);
}
}
package com.itheima05.auto;
public class User {
private Integer id;
private String name;
private String password;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
域名作用是方便记忆,dns作用是将域名解析成ip。
如下如果本机DNS知道baidu对应的ip直接返回ip。根dns服务器全球只有13个,根dns服务器可能也不知道baidu这域名对应的ip,但它一定知道这个域名对应的ip是哪个dns服务器(XX)知道。
如下是系统中配置的是上图第一个红圈。
更多推荐
【Java3】lambda,Stream流,File类,乱码,流/Properties,Tcp/文件上传/UUID/Junit,反射/注解,类加载器,设计模式,
发布评论