【java2】lambda,Stream流,File类,乱码,流,tcp,class,设计模式,nginx,req/res,cookie/session,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,正则表达式,都认为是字符串)。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农编程录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值