文章目录
- 1.lambda表达式:使用前提:有且仅有一个抽象方法 (需要被重写) 的接口 + 上下文推导
- 2.Stream流: 基于函数式编程延伸出来的一种用法,简化集合数据处理过程中代码冗余问题
- 3.File类:既表示文件,又表示文件夹
- 4.编码表与字符流:编码/解码/乱码
- 5.流:字符流 = 字节流 + 编码表 (缓存区是为了兼容编码表存在)
- 6.tcp通讯:一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个。B/S(浏览器对服务器)和C/S(客户端对服务器)
- 7.Class对象:构造方法和普通方法反射(属性是通过反射get和set方法)
- 8.设计模式:动态代理比普通代理的好处在于不用事先定义类,代理类在运行时动态生成 (反射)
- 9.nginx的conf文件:以前网络编程中B/S架构用socket写,有了nginx后再也不用
- 10.servlet:服务器软件为tomcat或nginx,程序为servlet
- 11.RPC:远程过程调用,我这台电脑调用一个函数(这函数在另一个电脑上执行),像是本地正常运行函数一样,不需要关注远程(网络连接,断开,传数据)细节
1.lambda表达式:使用前提:有且仅有一个抽象方法 (需要被重写) 的接口 + 上下文推导
1.1 标准语法:函数式编程(不在乎什么对象, 在乎是什么方法)
package com.itheima01.boot;
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.itheima03.prefix;
public class PrefixDemo {
public static void main(String[] args) {
Cooker c1 = new Cooker() { // 匿名内部类
@Override
public void cooker() {
System.out.println("翠花上酸菜");
}
};
method(c1);
//111111111111111111111111111 如下同如上
/*
* lambda表达式 : 1. 省略了new + 接口名 (因为上下文推导)
* 2. 省略了方法名 (因为接口只有一个抽象方法,所以方法名不重要)
*/
//上文 (Cooker c2 = 是上文 ,() ->...是下文) 推导
Cooker c2 = () -> {
System.out.println("蚂蚁上树");
};
method(c2);
//1111111111111111111111111 如下同如上
//下文推导。上下文有其一即可
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;
public class UseDemo { // lambda表达式的运用:Comparator和Runnable接口
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]
//11111111111111111111 如下同如上
Collections.sort(list,(Integer o1,Integer o2) -> {
return o2 - o1;
});
System.out.println(list); //[5,4,3,2,1]
}
//111111111111111111111111111111111111111111111111111111111111111111111
private static void method01() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": 嘻嘻"); //Thread-0:嘻嘻
}
}).start();
//11111111111111111111 如下同如上
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName() + ":哈哈"); //Thread-1:哈哈
};
new Thread(runnable).start();
//11111111111111111111 如下同如上
new Thread(()->{
System.out.println(Thread.currentThread().getName() + ":呵呵"); //Thread-2:呵呵
}).start();
}
}
1.2 简略语法:可推导即可省略
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..
});
//1111111111111111111111111111111111111111111111111111111111111111111111
method02((String cai) -> {
System.out.println("开水煮" + cai); //子类重写的方法 //开水煮大白菜
});
method02((cai) -> System.out.println("醋溜" + cai));
method02(cai -> System.out.println("韩式泡" + cai)); //空参必须写()
}
//1111111111111111111111111111111111111111111111111111111111111111111111
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;
public class UseDemo { // lambda表达式简略语法运用: Comparator和Runnable接口
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("嘻嘻");
}
}).start();
//1111111111111111111 如下同如上
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);
//1111111111111111111 如下同如上
Collections.sort(list,(o1,o2) -> o2 -o1);
System.out.println(list);
}
}
2.Stream流: 基于函数式编程延伸出来的一种用法,简化集合数据处理过程中代码冗余问题
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;
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));
}
}
2.1 Stream流的终结方法:foreach和count
package com.itheima09.method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
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);
}
}
2.2 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;
/*
* 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));
}
}
3.File类:既表示文件,又表示文件夹
package com.itheima01.constructor;
import java.io.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,不能获取文件夹大小
}
}
3.1 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
}
}
3.2 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();//必须刷新文件才有内容
3.3 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();
}
}
3.4 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();
}
}
3.5 文件复制: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);
}
}
4.编码表与字符流:编码/解码/乱码
4.1 编码:字节映射为字符。第一种编码:ascii码。
如下是中文编码,gb2312(1980年)既是字符集又是编码
,如下53,35都是举例,小于128都是ascii。gb2312汉字收录太少,所以出了gbk(1995年)(编码规范)。
如上"啊"改为"瞭",并放开gb2312和gbk,如下打印3F就是ascii码中63即?即解码失败。
"㞎"是gb18030独有的CJK统一汉字扩充。unicode字符集
有utf-8和utf-16编码方式,前面字符集和编码都同一个,所以叫统一名字了。
4.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码。
5.流:字符流 = 字节流 + 编码表 (缓存区是为了兼容编码表存在)
如下两种方式都可以,OutputStream和Writer对应。
package com.itheima04.chard;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
public class FlushDemo {
public static void main(String[] args) throws IOException {
// FileOutputStream fos = new FileOutputStream("a.txt"); //a.txt里写啥都无所谓,之后会覆盖
// fos.write("中文".getBytes()); //字节流没有字节数组,把中文拆成字节数组,把中文所包含的6个字节放到字节数组里,让字节流输出去,因为底层没有缓冲即byte[]数组。//有一个字节,就写一个,没有缓存不用刷新。
//1111111111111111111111111111111111111111111111111111111111111111111111111111
FileWriter fw = new FileWriter("a.txt");
fw.write("中文2"); // 先放到缓存(内存),等缓存满了,再写到硬盘上
fw.flush(); //刷新缓存, 把缓存清空,写到硬盘上,怕断电 //fw.close(); :释放资源,释放之前,底层默认先调用flush,所以这行单写fw.close()也能写进去。
fw.write("a");//上行close之后不能再使用这个流
fw.flush(); //刷新显示
}
}
5.1 IO流异常处理:字符流用于读写文本文件,字节流outputStream(内存->硬盘:写,以内存为中心)
如下是四个抽象类的其他子类
: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;
5.2 字节/字符缓冲流:字节流 + 缓存区(默认8k) , 目的是用空间(内存)换时间
package com.itheima02.buffer;
import java.io.*;
/*
* 字节缓冲流:1. public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
public BufferedInputStream(InputStream in, int size)
2. public BufferedOutputStream(OutputStream out) :创建一个 新的缓冲输出流。
*
*/
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();
}
}
5.3 案例:正则中一个单独的点表示任意字符
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();
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111
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("复制完成");
}
}
5.4 转换流:字节字符的中间流
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个“啊”
}
}
5.5 序列化流: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();
}
}
5.6 打印流:最后一个流,字符数据(文本文件)的读或写(不是读写即复制,复制用字节流),用字符流方便
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();
}
}
6.tcp通讯:一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个。B/S(浏览器对服务器)和C/S(客户端对服务器)
下面将三要素融合,https中s就是secure。
如下文件上传:上面是服务端,socket封装了io流,所以要关。高层socket关了,低层也会关。
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);
//11111111111111111111111111111111111111111111111111111111111111111111111111111
FileInputStream fis = new FileInputStream("柳岩.jpg"); //读本地文件
OutputStream os = socket.getOutputStream();
int length = -1;
byte[] buffer = new byte[1024];
while((length = fis.read(buffer)) != -1){ //读
os.write(buffer,0,length); //写到服务器socket中
}
fis.close();
socket.close();
}
}
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;
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)了
}
}
}
如下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();
}
}
7.Class对象:构造方法和普通方法反射(属性是通过反射get和set方法)
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 同上
}
}
7.1 反射:在程序运行 【.java源码 -> .class字节码(编译后的二进制指令让jvm读) -> Class对象(运行时的类的样子)】 时操作类
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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}
}
//1111111111111111111111111111111111111111111111111111111111111111111
@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}
}
//1111111111111111111111111111111111111111111111111111111111111111111111111
@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}
}
//1111111111111111111111111111111111111111111111111111111111111111111111111
@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);
}
}
7.2 类加载器:将.class文件加载进内存, 随之产生Class对象,看成输入流(读档)
package com.itheima01.loader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
/*
* 三种类加载器:加载不同包下的类,像前面讲的不同的输入流
* 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 //引导 (父父)
}
}
8.设计模式:动态代理比普通代理的好处在于不用事先定义类,代理类在运行时动态生成 (反射)
8.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.com.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);
}
}
8.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);
}
}
8.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(),也是懒汉式
}
}
8.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类
}
}
9.nginx的conf文件:以前网络编程中B/S架构用socket写,有了nginx后再也不用
nginx软件链接:https://pan.baidu.com/s/1zvF2irI6OenDKVfvSIOZmg ,提取码:gsn9。
如果http协议是80端口的话,80端口会被隐藏,本机浏览器输入http://localhost:80启动服务端
显示如下,hello nginx是E\my81\index.html里内容。本机ipconfig显示192.168.33.71。上面红框浏览器
(客户端)是别人的电脑【局域网下同一网段】,上面两个大框是服务端,浏览器和服务器三要素对应。
9.1 nginx反向代理与负载均衡:www.163.com的F12的Network中Headers中有server:nginx,使用nginx首当其冲,做反向代理即将请求分发到公司的多台服务器上
原生nginx没有集成很多插件,使用不方便。推荐使用openresty(在nginx基础上集成了很多lua写的插件),官网下载openresty后解压后进一级目录输入nginx.exe回车运行服务端,浏览器输入localhost默认:80端口
。
如下执行location里echo。
9.2 location匹配方式:location / 默认匹配所有请求,相当于第四优先级(最弱)
1.
如下最强级别=。
2.
如下优先级第二,^~
以什么开头。如下/
是路径,不是转义符。
3.
如下优先级第三,正则表达式~
。\w
匹配数字、字母、下划线,转义字符\
可以转义很多字符,比如\n
表示换行,\t
表示制表符,字符\
本身也要转义,所以\\
表示的字符就是\
。如下/
是路径。
9.3 反向代理写法:http80
9.4 负载均衡写法:基于反向代理
10.servlet:服务器软件为tomcat或nginx,程序为servlet
tomcat8:https://tomcat.apache.org/download-80.cgi,免安装,解压即可用。【链接:https://pan.baidu.com/s/1UJM9kbIHGIXkNXlYLpcoGw ,提取码:g610】。dos系统识别文件后缀名不能超过3位(先有dos后有windows),所以.htm。8080一般是用来连接代理的(8080不能省,只有80才能省)。 只有index.html是默认的,不用加在最后,localhost:8080/a.html。
http的request对象:由tomcat创建,并且里面数据由tomcat set进去,我们只需要get出来。
<!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请求中最后一行不是请求体,是谷歌浏览器工具抓包进行的参数强调渲染。
如下header是上面的头,u/m/q是上面的行。
10.1 request对象:请求参数: ?n1=v1&n2=v2…
<!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;
/**
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遍历出来的结果和下面第一行一样。
请求转发: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~~");
}
}
10.2 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:获取,例如获取系统帐户讯息GET /redfish/v1/AccountService
POST:
1.新增,例如新增一个帐户 POST /redfish/v1/AccountService/Accounts {"id":"xxx" "password":"xxx"}
2.执行,例如执行韧体更新,开关机等动作
PATCH:更新,例如更新帐户名字 PATCH /redfish/v1/AccountService/Accounts/01 {"Name": "123"}
DELETE:删除,例如删除一个帐户 DELETE /redfish/v1/AccountService/Accounts/01
1xx:Informational (信息):提供协议级信息
2xx:Success(成功):客户端请求被接受(成功)
200:OK
201: Created:申请资源创建成功
202:Accepted (已接受):用于报告异步操作成功
204:No content (无内容):当 API 想要发送空内容或没有内容时响应体
3xx:Redirection (重定向):客户端请求由服务器重定向到满足客户端请求的不同端点
301:Moved Permanently (永久移动):用于重新定位的资源
302:Found (找到)
4xx:Client error(客户端错误):客户端错误
400:Bad request (错误请求)
401:Unauthorized (未经授权)
403:Forbidden (禁止)
404:Not found (未找到,请求地址不对)
405:Method not allowed (方法不允许)
5xx:Server error (服务器错误)
500: Internal server error(内部服务器错误, 如servlet关了,tomcat自动设置为500)
如下点击后,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.com");
//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>");
}
}
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。
输入账号和密码后跳转如下。
文件下载: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("文件下载完毕");
}
}
响应体交给浏览器,浏览器自动下载。
10.3 cookie:再次时请求头携带cookie到服务端
如下localhost是域名,不包含协议和端口。
浏览器的设置中搜索cookies - 网站设置 - localhost。
只有localhost(域名)默认cookie存活时间是浏览器打开到关闭(不是窗口),如下改为30天(游客访问京东购物车一般也为30天)。在浏览器的设置里搜索清除浏览数据。
如下name虽相同,但是路径不同,不会覆盖。name相当于文件名,value相当于文件内容,同路径同name就会覆盖。
如上浏览器现在有两个cookies:一个是 / product galaxyNote7,另一个是 /abc product huawei。这两个cookies都会被携带进如下/abc…服务器。
登陆案例_记住我: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>
// LoginServlet.java
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中一样,用于数据回显。
10.4 session:根据sessionid,服务器才能找到session
病历本session,session记录大量信息,直接进行传递会卡,所以用cookie传sessionid(Cookie将sessionid传来传去,4k不宜过大,Cookie用sessionid标记用户)。cookie默认生命周期是会话
即关闭浏览器同理session也是。
如下挂号这一行代码都一样,小域对象request获取大域对象session。< session - config >
是在tomcat软件解压后的conf文件夹里的web.xml(如订单30分钟不付款就会自动取消了)。
如下红字:一个存,一个取,该用什么载体实现存取?如最右边。如下是两次请求,不是一次请求链,所以request不行。存在session里好处:30分钟后自动没了。
status和code是数据表中两个字段。
11.RPC:远程过程调用,我这台电脑调用一个函数(这函数在另一个电脑上执行),像是本地正常运行函数一样,不需要关注远程(网络连接,断开,传数据)细节
微服务
:每个功能模块都独立打包部署到不同的系统上去,分开后每个微服务怎么去调用呢?这时就用到springcloud或dubbo(管理和编排这些微服务)
一些远程调用协议,也就是相当于每个人怎么去沟通呢?沟通使用的这个语言就是协议
如普通话。
这么多的微服务都是一个单体应用系统,用到了springboot(是一个快速搭建应用的框架,不是微服务)
来解决S(spring)S(springmvc)M(mybatis)搭建应用时大量的手工配置问题。ORM
:对象(编程语言中对象)关系(关系型数据库中库表)映射。
如下func就是a+b,R就是remote远程,程序内存中数据结构
封装到传输时数据结构
(文本或字节数组),传来后还要将数据(即字节或文本)恢复到内存中,这传一次就叫做序列化(json和xml)和反序列化
。如果需要提高rpc性能除了序列化和反序列化,数据传输过程中需要提高网络IO性能。
同步单线程,异步多线程,要实现异步请求,必须借助ajax。浏览器输入框输入:data:text/html,<h1>hello<h1>
,页面自动显示hello。data:text/html,<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
,F12 Network中刷新页面显示如下,这时就可在console中使用jquery了。
如下在网页的console中,data就是main-win.exe返回的数据。
如上在console中回车发送后,如下Network中会多一条localhost请求。
如下main-win.exe监听99号端口的小程序,直接双击打开:链接:https://pan.baidu.com/s/1nHhdf7Qerq3bEkOfXr8FXw 提取码:eipc。
JSON:JavaScript对象简单表示法,与js对象
区别是JSON只有属性没有方法,比XML小快(因为json不需要标签)。任意数组皆为json。 Json在java中就是普通string (java不识别json,xml,sql,正则表达式,都认为是字符串)。