【Java3】lambda,Stream流,File类,乱码,流/Properties,Tcp/文件上传/UUID/Junit,反射/注解,类加载器,设计模式,req/res,c/s,ajax,DNS

编程知识 更新时间:2023-05-03 01:34:40

文章目录

  • 1.lambda表达式标准语法:()->{}
  • 2.lambda表达式简略语法:可推导即可省略
  • 3.lambda表达式原理:lambda效率比匿名内部类高
  • 4.两个函数式接口:Consumer ,Predicate
  • 5.Stream流引入: list.stream().filter
  • 6.Stream流的获取:.stream(),Stream.of(array)
  • 7.Stream流的终结方法:list.stream().count()
  • 8.Stream流的拼接方法:Stream.concat
  • 9.File类三个构造方法和获取方法:\
  • 10.File的判断/创建/删除/列举方法:listFiles
  • 11.FileOutputStream两个构造:OIWR,IO流(内存为中心)
  • 12.OutputStream的基本方法:追加和换行
  • 13.FileInputStream两个构造:一次读一个字节或字节数组
  • 14.文件复制(重要):System.currentTimeMillis(),fis.read,fos.write
  • 15.编码表与字符流:编码/解码/乱码
    • 15.1 编码:字节映射为字符。第一种编码:ascii码。
    • 15.2 乱码:FileReader(字符流)= FileInputStream(字节流 )+ 编码表
    • 15.3 linux文件编码转换:iconv,enca
    • 15.4 Linux中文字符集:/etc/locale.conf
    • 15.5 Oracle的字符集:NLS_LANG
      • 服务端的字符集:Oracle数据库实例创建后,如果没有开始业务运行,可修改字符集,如果已经业务化运行,不建议修改字符集,会造成数据中的汉字乱码
      • 客户端的字符集:Oracle客户端的字符集必须与服务端相同,否则中文会出现乱码,客户端的字符集由NLS_LANG环境变量控制
  • 16.flush和close方法:.getBytes()
  • 17.IO流异常处理:字符流用于读写文本文件,字节流outputStream(内存->硬盘:写,以内存为中心),BOP
  • 18.字节/字符缓冲流:readLine
  • 19.案例_出师表:\ \ . ,content = map.get(i)
  • 20.转换流:字节字符的中间流
  • 21.序列化流:oos.writeObject,Serializable
  • 22.打印流:最后一个流,字符数据(文本文件)的读或写(不是读写即复制,复制用字节流),用字符流方便
  • 23.Properties集合:Hashtable 与HashMap 区别,p.load,toString
  • 24.tcp通讯:ip相当于小区,port相当于门牌号
    • 24.1 单向:一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个
    • 24.2 双向:socket中介
  • 25.多线程优化服务端与UUID:is和fos
  • 26.Class对象三种获取方式:Class.forName(" ")
  • 27.反射:clazz.newInstance()
  • 28.类加载器:.class.getClassLoader(),引扩应,双亲委派
  • 29.设计模式:动态代理比普通代理的好处在于不用事先定义类,代理类在运行时动态生成 (反射)
    • 29.1 动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)
    • 29.2 模板:类里有抽象方法必须抽象类
    • 29.3 单例:某个类只能有唯一的一个实例对象
    • 29.4 工厂:工厂类(一个工厂),工厂接口(一车一工厂),clazz.newInstance()
  • 30.约束:word/txt(和xml一样都没有格式约束)传输数据,但xml格式规范
    • 30.1 DTD约束:配置文件
    • 30.2 Schema约束:命名空间
  • 31.nginx的conf文件:以前网络编程中B/S架构中服务器是用socket写,用文件输入流读一个文件,读到后socket通过outputstream写出去,这些过程有了nginx后再也不用写
    • 31.1 nginx反向代理与负载均衡:nginx是被广泛使用的网页服务器,在www.163的F12的Network中Headers中有server:nginx,使用nginx首当其冲,做反向代理,将请求分发到公司的多台服务器上
    • 31.2 location匹配方式:location / 默认匹配所有请求,相当于第四优先级(最弱)
    • 31.3 反向代理写法:http80
    • 31.4 负载均衡写法:基于反向代理
  • 32.服务器:软件为tomcat或nginx,程序为servlet
  • 33.servlet:多态,web.xml
  • 34.request对象:.getParameterMap(),post请求中文参数乱码
    • 34.1 请求转发:request表面上获取请求数据,还有快递员身份
    • 34.2 登陆案例:成功和失败页面相当于两个模块
  • 35.response对象:text/html,重定向(.setStatus,.setHeader)
      • 35.1 文件下载:request/response
  • 36.cookie:再次时请求头携带cookie到服务端
    • 36.1 登陆案例_记住我:js访问浏览器数据用document
  • 37.session:根据sessionid,服务器才能找到session(session对象在服务器内存中增加压力)
    • 37.1 登录案例_验证码:request.getSession().setAttribute("code",sb.toString())
  • 38.MVC:Spring_IOC(对象创建交给spring工厂),Spring_AOP(基于动态代理功能增强)。Runapplication启动类:控制层(UserController),业务层(UseService,UseServiceImpl),持久层(User即pojo,UseMapper.xml,UseMapper接口)
  • 39.RPC:远程过程调用,我这台电脑调用一个函数(这函数在另一个电脑上执行),像是本地正常运行函数一样,不需要关注远程(网络连接,断开,传数据)细节
  • 40.ajax:xhr对象,$.,浏览器输入框加载脚本
  • 41.json:new ObjectMapper()
    • 41.1 json在前端和后端中表达:json在js中的表达.html(前端)
  • 42.ajax和json综合:ajax_json_union.html


1.lambda表达式标准语法:()->{}

package com.itheima01.boot;
/*
*   函数式编程 语法 (JDK8): 函数(公式)  ->  java方法 。作用: 简化代码编写    
*       1. lambda表达式: 标准语法/简略语法
*       2. 运用: Stream流
*/
public class BootDemo {
    public static void main(String[] args) {
        //匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();

        //lambda表达式
        new Thread(
                () -> {
                    System.out.println(Thread.currentThread().getName());
                }
        ).start();
    }
}
package com.itheima02.lambda;
/*
    lambda表达式 -> 函数式编程(不在乎什么对象, 在乎是什么方法)
    方法重要三部分: 参数列表、方法体、返回值

*   标准语法:  (参数列表)
*              -> : lambda符号,连接作用
*              { 方法体,返回值 }
*/
public class StandardDemo {
    public static void main(String[] args) {
        Cooker cooker = new Cooker() { //匿名内部类 ,和下面的method()无关
            @Override
            public void cook() {
                System.out.println("猪肉炖粉条");
            }
        };
        method(cooker);

//11111111111111111111111111111111111111111111111111111111111111111	
        Cooker cooker2 =  () -> {
            System.out.println("小鸡炖蘑菇");
        };
        method(cooker2);
    }        
    private static void method(Cooker cooker) { //参数类型是接口 -> 多态。调用此方法必须传入此接口的实现类对象。
        // System.out.println(1); //1 先于 猪肉炖粉条 打印
        cooker.cook(); //父类调用方法执行子类重写方法
    }
}
interface Cooker{  
    void cook(); //这方法无参无返回
}
package com.itheima02.lambda;
/*
*  lambda表达式:在乎的方法: 拿什么参数去做什么事情,返回什么结果。
*         (参数列表) -> { return xx; }
*/
public class StandardDemo02 {
    public static void main(String[] args) {
        Calculator c1 = new Calculator() {
            @Override
            public int calc(int c, int d) {
                return c + d;
            }
        };
        method(c1);
        
//1111111111111111111111111111111111111111111111111111111111111111
        Calculator c2 = (int o1,int o2) -> {
            return o1 - o2;
        };
        method(c2);
    }

//1111111111111111111111111111111111111111111111111111111111111111
    private static void method(Calculator calculator) {
        int result = calculator.calc(1, 2); //父类调用方法执行子类重写方法
        System.out.println(result);
    }
}
interface Calculator{    
    int calc(int a, int b); //有参有返回
}
package com.itheima03.prefix;
/*
*   lambda表达式的使用前提: 1. 接口 : 有且仅有一个抽象方法 (需要被重写) 的接口
*       				     2. 上下文推导
*   lambda表达式可以看成 极为简化的 匿名内部类对象
*       接口的实现 (函数: 方法)
*/
public class PrefixDemo {
    public static void main(String[] args) {
        Cooker c1 = new Cooker() {
            @Override
            public void cooker() {
                System.out.println("翠花上酸菜");
            }
        };
        method(c1);

//11111111111111111111111111111111111111111111111111111111111111111	
        /*
        *   匿名内部类对比
        *   lambda表达式 : 1. 省略了new + 接口名 (因为上下文推导)
        *        2. 省略了方法名 (因为接口只有一个抽象方法,所以方法名不重要)
        */
        //上文 (Cooker c2 = 是上文 ,() ->...是下文) 推导
        Cooker c2 = () -> {
            System.out.println("蚂蚁上树");
        };
        method(c2);
        
//111111111111111111111111111111111111111111111111111111111111111111		
        //下文推导。上下文有其一即可
        method( () -> {
            System.out.println("蚂蚁上树");
        });
    }
    private static void method(Cooker cooker) {
        cooker.cooker();
    }
}
interface Cooker{
    void cooker(); //有且仅有一个抽象方法
}
package com.itheima03.prefix;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
*   lambda表达式的运用:1. Runnable接口
*       		        2. Comparator接口
*/
public class UseDemo {
    public static void main(String[] args) {
//        method01();
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,5,3,2,4,1);
        
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println(list); //[1,2,3,4,5]

//11111111111111111111111111111111111111111111111111111111111111111111111111        
        Collections.sort(list,(Integer o1,Integer o2) -> {
            return o2 - o1;
        });
        System.out.println(list); //[5,4,3,2,1]
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method01() {  
        new Thread(new Runnable() {
            @Override
            public void run() { 
                System.out.println(Thread.currentThread().getName() + ": 嘻嘻"); //Thread-0:嘻嘻
            }
        }).start();
        
//1111111111111111111111111111111111111111111111111111111111111111111111111
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName() + ":哈哈"); //Thread-1:哈哈
        };
        new Thread(runnable).start();
        
//1111111111111111111111111111111111111111111111111111111111111111111111111
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + ":呵呵"); //Thread-2:呵呵
        }).start();
    }
}

2.lambda表达式简略语法:可推导即可省略

package com.itheima04.simple;
/*
*       1. 参数类型可以省略 (接口抽象方法只有一个,参数列表只有一个,什么类型不需要特地声明)
*       2. 如果方法体中只有一行代码,那么 {}和return和; 都可以省略
*       3. 如果参数列表 有且只有一个参数,那么()可以省略
*/
public class SimpleDemo {
    public static void main(String[] args) {
        //lambda编写: 拿什么东西去做什么事,结果?
        method((int a,int b)-> { return a + b;});

        method((a,b) -> {return a+b;}); //可以

        method((a,b) -> a+b); //可以,如上的省略

        method((a,b) -> { 
            int sum = a+b;
            return sum; //两句不能去掉{}..return..
        });		
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111
        method02((String cai) -> {
            System.out.println("开水煮" + cai); //子类重写的方法 //开水煮大白菜
        });

        method02((cai) -> System.out.println("醋溜" + cai));

        method02(cai -> System.out.println("韩式泡" + cai)); //空参必须写()
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method02(Cooker cooker) {
        cooker.cook("大白菜"); //无返回值,父类调用方法执行子类重写的方法,此时cai="大白菜"。这行是主体。
    }
    private static void method(Calculator calculator) {
        int result = calculator.calc(1, 2);
        System.out.println(result);
    }
}
interface Calculator{
    int calc(int a,int b);
}
interface Cooker{
    void cook(String cai);
}
package com.itheima04.simple;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
*   lambda表达式简略语法运用
*       1. Runnbale接口
*       2. Comparator
*/
public class UseDemo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("嘻嘻");
            }
        }).start();
        
        new Thread(() -> System.out.println("呵呵")).start();	
        	
//11111111111111111111111111111111111111111111111111111111111111111
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,5,3,4,2,1);
        
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println(list);

//11111111111111111111111111111111111111111111111111111111111111111        
        Collections.sort(list,(o1,o2) -> o2 -o1);
        System.out.println(list);
    }
}

3.lambda表达式原理:lambda效率比匿名内部类高

package com.itheima05.theory;
import java.util.ArrayList;
import java.util.List;
/*
* lambda表达式和匿名内部类: 0.lambda表达式 可以算是 一种 匿名内部类的 简化语法
*       1.lambda表达式能不能完全替代匿名内部类?不能
*           1.lambda表达式只能用在(有且仅有一个抽象方法需要被重写的)接口
*           2.匿名内部类(接口比较多,类实际上也可以)
*       2.lambda表达式是不是匿名内部类的语法糖(原理完全相同,语法更为简练称为语法糖)?  不是         
*           1.匿名内部类 是 类
*           2.lambda表达式不是 类,是可以替代对象 的函数
*/
public class LambdaDemo {     // LambdaDemo.class
    public static void main(String[] args) {        
        Runnable runnable = new Runnable() { // LambdaDemo$1.class(编译生成) ,所以匿名内部类是类。idea中run集成了编译和运行
            @Override
            public void run() {
                System.out.println("嘻嘻");
            }
        };
  
//111111111111111111111111111111111111111111111111111111111111111111111111
        Runnable runnable2 = new Runnable() { // LambdaDemo$2.class 
            @Override
            public void run() {
                System.out.println("嘻嘻2");
            }
        };
    //接口类型 引用 = 接口实现类对象(看成,但实际上不是) //没有.class文件生成
        Runnable runnable3 = () -> System.out.println("呼呼");
    }
}

4.两个函数式接口:Consumer ,Predicate

package com.itheima06.function;
import java.util.Comparator;
/*
* 函数式接口:  1. 定义 : 有且仅有一个抽象方法(需要被重写)的接口
*              2. 作用 : 支持函数式编程(A.lambda表达式 B.方法引用)
*             3. 已学习 : Runnbale , Comparator两个函数式接口
*             4. 注解: @FunctionalInterface
*                写在接口声明上, 检查此接口是否是函数式接口,如果不是编译报错
*/
public class FunctionDemo {
    public static void main(String[] args) {
//        Runnable
    }
}
@FunctionalInterface
interface A{
    void eat(); //需要重写的抽象方法,只一个
    String toString(); // 不需要重写的抽象方法
    static void method01(){ //不影响@FunctionalInterface,静态方法一定不能被重写
    }
    default void method02(){
    }
}

//1111111111111111111111111111111111111111111111111111111111111
interface B{
    String toString();  
}
//Object和接口B中拥有相同方法,C先继承Object的方法,当成接口B的重写
class C implements B{ //class C extends Object implements B   //class C 都可以
    @Override
    public String toString() { //重写也可以
        return super.toString();
    }
}
package com.itheima06.function;
import java.util.function.Consumer;
/*
* JDK8 : java.util.function包提供了很多函数式接口 -> 函数式编程(Stream流)
*   1. Consumer : 消费者
*           public interface Consumer<T> {             
*               void accept(T t); //有参无返回
*           }
*   2. Predicate
*/
public class FunctionDemo02 {
    public static void main(String[] args) {
        method(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println("水煮" + s);
            }
        });        
        method(s -> System.out.println("清蒸" + s));  
        // 水煮大白菜
        // 清蒸大白菜
    }

//11111111111111111111111111111111111111111111111111111111111111    
    private static void method(Consumer<String> consumer) {
        consumer.accept("大白菜");  //父类引用调用方法执行子类重写的方法
    }
}
package com.itheima06.function;
import java.util.function.Predicate;
/*
* 2. Predicate : 推断
*       public interface Predicate<T> {       
*           boolean test(T t); //有参有boolean返回值
*       }
*/
public class FunctionDemo03 {
    public static void main(String[] args) {        
        method(new Predicate<String>() { //判断参数字符串 长度 是否超过5
            @Override
            public boolean test(String s) {
//                if(s.length() > 5){
//                    return true;
//                }
//                return false;
                return s.length() > 5; //可以。且只有一句
            }
        });
        method(s->s.length() > 5);
        // false
        // false
    }

//11111111111111111111111111111111111111111111111111111111111111111 
    private static void method(Predicate<String> predicate) {
        boolean result = predicate.test("july");
        System.out.println(result);
    }
}

5.Stream流引入: list.stream().filter

package com.itheima07.boot;
import java.util.ArrayList;
import java.util.List;
// 代码冗余: 1. 循环太多 2. 判断太多
public class StreamBootDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        //张集合
        List<String> zhangList = new ArrayList<>(); //空
        for (String name : list) {
            if (name.startsWith("张")) {
                zhangList.add(name);
            }
        }
        
        //短集合 : 张集合基础上,只要名字3个
        List<String> shortList = new ArrayList<>(); //空
        for (String name : zhangList) {
            if (name.length() == 3) {
                shortList.add(name);
            }
        }                
        for (String name : shortList) { //遍历打印
            System.out.println(name);
        }
    }
}

package com.itheima07.boot;
import java.util.ArrayList;
import java.util.List;
//Stream 流: 1. 基于函数式编程延伸出来的一种用法。 2. 作用: 简化 集合数据 处理过程中的代码冗余问题
public class StreamBootDemo02 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");        
        list.stream().filter(name -> name.startsWith("张")) //name就是集合中的每个元素
                .filter(name -> name.length() == 3).forEach(name-> System.out.println(name));
    }
}

6.Stream流的获取:.stream(),Stream.of(array)

package com.itheima08.stream;
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.util.*;
import java.util.stream.Stream;
/*
*   Stream 流: Stream流对象的获取:
*         1. 集合:Collection接口 默认方法 stream()
*         2. 数组:Stream.of(T...);  使用这个
*                 Stream.of(T);
*/
public class StreamGetDemo {
    public static void main(String[] args) {
        Collection<String> coll = new HashSet<>();
        Stream<String> stream = coll.stream();

//111111111111111111111111111111111111111111111111111111111111111111111111111
        HashMap<String, String> map = new HashMap<>();
//        Set set = map.keySet();
//        set.stream();
        Stream<String> stream1 = map.keySet().stream();
        Stream<Map.Entry<String, String>> stream2 = map.entrySet().stream();
        
//111111111111111111111111111111111111111111111111111111111111111111111111111
        String[] array = {"a","b"};
        Stream<String> stream3 = Stream.of(array);
        
        Integer[] array2 = {1,2,3};
        Stream<Integer> stream4 = Stream.of(array2); //T...  -> T=Integer,把每个元素拆开。//泛型 必须是 引用类型
        
        int[] array3 = {1,2,3};
        //如果说数组要获取Stream流,不要用基本类型数组, 把整个数组当成一个元素无法拆开,不要用如下
        Stream<int[]> stream5 = Stream.of(array3);// T -> T = int[]
    }
}

7.Stream流的终结方法:list.stream().count()

package com.itheima09.method;     
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
/*
* Stream流的方法: 1. 终结方法 : 不支持链式编程 (返回值: 基本类型)
*  				  2. 拼接方法 : 支持链式编程 (返回值: 当前类型,引用类型)
* 
*     终结方法:  1.foreach
*                   void forEach(Consumer<? super T> action);
*                   底层: 迭代器 , 需要消费参数(集合中每一个元素)
*               2.count
*                   long count() : 获取集合中的元素个数
*/
public class EndDemo {
    public static void main(String[] args) {
//       method();    //abc
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 5,3,2,4);
//        for (Integer integer : list) { //list.for快捷
//            System.out.println(integer); 
//        }

//11111111111111111111111111111111111111111111111111111111111111111111  
		//stream流打印,先获取stream流对象
       /* list.stream().forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer t) {
                System.out.println(t); //遍历打印list集合
            }
        });*/

		//如下等于上面
//      list.stream().forEach(t-> System.out.println(t));

//11111111111111111111111111111111111111111111111111111111111111111111
        long count = list.stream().count();
        System.out.println(count); //4
    }

    private static void method() {
       // StringBuilder sb = new StringBuilder();
       // StringBuilder sb2 = sb.append("a");
       // StringBuilder sb3 = sb2.append("b"); 
        StringBuilder sb = new StringBuilder();       
        sb.append("a").append("b").append("c").equals("abc"); //append拼接方法,equals终结方法,因为equals返回boolean基本类型,不能再调用方法
        System.out.println(sb);
    }
}

8.Stream流的拼接方法:Stream.concat

package com.itheima09.method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.function.Predicate;
import java.util.stream.Stream;
/*
*   Stream的拼接方法
*       1. filter 过滤
*            Stream<T> filter(Predicate<? super T> predicate);
*               1. Predicate : public boolean test(String s)
*               2. s是集合中每一个元素 , 迭代器
*               3. return true : 表示保留这个元素
*       2. limit 取用前几个
*              Stream<T> limit(long count)
*              只要前count个元素, 没有越界
*       3. skip 跳过前几个
*           Stream<T> skip(long n);
*       4. static concat 组合,静态方法
*           static <T> Stream<T> concat(Stream<T> a, Stream< T> b)
*           合并两个stream,整合一个stream
*/
public class ChainDemo { //Chain:链
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张三","张三丰","李四","李世石");
        
		//.filter返回值类型是stream,调完filter还能调filter,就像Stringbuild调完append还可再调append
//        list.stream().filter(new Predicate<String>() {
//            @Override
//            public boolean test(String s) {
//                return s.startsWith("张");
//            }
//        }).forEach(s -> System.out.println(s)); //张三 张三丰

//        list.stream().filter(s->s.startsWith("张")).forEach(s-> System.out.println(s)); //等同上面

//        list.stream().limit(5).forEach(t-> System.out.println(t)); //没有索引越界,因为迭代器没有索引,最多4个就给4个

//        list.stream().skip(3).forEach(t-> System.out.println(t)); //李世石

        ArrayList<String> list2 = new ArrayList<>();
        Collections.addAll(list2,"王五","王五百","马六");
        
        Stream<String> stream1 = list.stream();
        Stream<String> stream2 = list2.stream();
        Stream.concat(stream1,stream2).forEach(t-> System.out.println(t)); //张三...7个元素
    }
}
package com.itheima11.union;
import java.util.ArrayList;
import java.util.List;
//案例比较:
public class Demo01 {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");       
        List<String> oneA = new ArrayList<>();  // 第一个队伍只要名字为3个字的成员姓名;
        for (String name : one) {
            if (name.length() == 3) {
                oneA.add(name);
            }
        }        
        List<String> oneB = new ArrayList<>(); // 第一个队伍筛选之后只要前3个人;
        for (int i = 0; i < 3; i++) {
            oneB.add(oneA.get(i));
        }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111             
        List<String> twoA = new ArrayList<>(); // 第二个队伍只要姓张的成员姓名;
        for (String name : two) {
            if (name.startsWith("张")) {
                twoA.add(name);
            }
        }        
        List<String> twoB = new ArrayList<>(); // 第二个队伍筛选之后不要前2个人;
        for (int i = 2; i < twoA.size(); i++) {
            twoB.add(twoA.get(i));
        }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111                   
        List<String> totalNames = new ArrayList<>(); // 将两个队伍合并为一个队伍;
        totalNames.addAll(oneB);
        totalNames.addAll(twoB);                
        for (String name : totalNames) { // 打印整个队伍的姓名信息。
            System.out.println(name);
        }
    }
}
package com.itheima11.union;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class Demo02 {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");
//stream流式编程核心:隐藏了没有必要的循环迭代,把最核心的条件暴露出来并通过链式编程方式把代码顺起来
//        1. 第一个队伍只要名字为3个字的成员姓名;
//        2. 第一个队伍筛选之后只要前3个人;
        Stream<String> stream1 = one.stream().filter(s -> s.length() == 3).limit(3);
//        3. 第二个队伍只要姓张的成员姓名;
//        4. 第二个队伍筛选之后不要前2个人;
        Stream<String> stream2 = two.stream().filter(s -> s.startsWith("张")).skip(2);
//        5. 将两个队伍合并为一个队伍;
//        6. 打印整个队伍的姓名信息。
        Stream.concat(stream1,stream2).forEach(t-> System.out.println(t));
    }
}

9.File类三个构造方法和获取方法:\

package com.itheima01.constructor;
import java.io.File;  
/*
    File : 既表示文件,又表示文件夹
  	1. File(String pathname):通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
	2. File(File parent, String child):根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
	3. File(String parent, String child):根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

    路径分隔符:
        1. windows :  \
        2. Unix : / 正斜杠在java中, 用 / 或 \\ 都可以
*
*       正则表达式 : 用简短的符号来表示复杂的意思 
*           \t : 表示制表符 tab
*           \r\n : 表示回车换行
*           \\ : 表示一个\
*/
public class Demo01 {
    public static void main(String[] args) {
        // new File("C:\\test\\a.txt");
        File file = new File("C:/test/a.txt"); //直接指定一个完整路径
        System.out.println(file);
        
        File file2 = new File("C:/test", "a"); //a表示文件夹 //指定一个父路径+名字
        System.out.println(file2);        
        //如下同上  
        File fu = new File("C:/test");
        File file3 = new File(fu, "a");
        System.out.println(file3);
    }
}
package com.itheima02.method;
import java.io.File;
/*
   获取方法
		1. String getAbsolutePath() :返回此File的绝对路径名字符串。
		2. String getPath() :将此File转换为路径名字符串。(构造时使用的路径)
		3. String getName() :返回由此File表示的文件或目录的名称。(路径最后一级名称)
		4. long length() :返回由此File表示的文件的长度。(文件夹的大小无法获取)
    路径:
        1. 绝对路径 :  带盘符的路径
        2. 相对路径 :  相对而言的路径(java工程中,相对当前工程而言) 
*/ 
public class GetDemo {
    public static void main(String[] args) {
        File file1 = new File("E:\\mywork\\IdeaProjects\\class897\\day11\\a.txt");
        File file2 = new File("a.txt"); //在当前项目里找
        String absPath1 = file1.getAbsolutePath();
        String absPath2 = file2.getAbsolutePath();
        System.out.println(absPath1); //E:\mywork\IdeaProjects\class897\day11\a.txt
        System.out.println(absPath2); //打印同上行,没有包路径如com..

        String path1 = file1.getPath();
        String path2 = file2.getPath();
        System.out.println(path1); //E:\mywork\IdeaProjects\class897\day11\a.txt
        System.out.println(path2); //a.txt

        String name = file1.getName();
        String name2 = file2.getName();
        System.out.println(name); //a.txt
        System.out.println(name2); //a.txt

        long length = file1.length();
        System.out.println(length); //3 byte,因为a.txt里有abc三个字母
        File src = new File("src");
        System.out.println(src.length()); //0,不能获取文件夹大小
    }
}

10.File的判断/创建/删除/列举方法:listFiles

package com.itheima02.method;
import java.io.File;
/*
   判断方法:1. public boolean exists() :此File表示的文件或目录是否实际存在。
			2. public boolean isDirectory() :此File表示的是否为目录。
			3. public boolean isFile() :此File表示的是否为文件。
*/
public class BooleanDemo {
    public static void main(String[] args) {
        File f1 = new File("src"); //目录
        File f2 = new File("a.txt");
        File f3 = new File("xxx");
        System.out.println(f1.exists());//true //因为在day11模块下找
        System.out.println(f2.exists());//true
        System.out.println(f3.exists());//false
        System.out.println("-------------------");
        
        System.out.println(f1.isDirectory());//true
        System.out.println(f2.isDirectory());//false
        System.out.println(f3.isDirectory());//false
        System.out.println("---------------");		
        
        System.out.println(f1.isFile()); // false
        System.out.println(f2.isFile());// true
        System.out.println(f3.isFile());//false
    }
}
package com.itheima02.method;
import java.io.File;
import java.io.IOException;
/*
*       1. public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
*           1. 如果文件不存在,则创建,返回true
*           2. 如果文件存在,不创建,返回false
*           3. 如果文件父路径不存在,则抛出IOException
		2. public boolean mkdir() :创建由此File表示的目录。 (make directory)
            1. 如果父路径不存在, 不会创建
            2. 如果目录已存在,也不会创建,都返回false
	    3. public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。(多级)
*/
public class CreateDemo {
    public static void main(String[] args) throws IOException {
//        create();
        File dir = new File("ccc"); //单级路径也算多级路径中一种
        boolean result = dir.mkdirs();
        System.out.println(result); //true
    } 
    
    private static void create() throws IOException {
        File file = new File("dir/xxx");
        boolean result = file.createNewFile();
        System.out.println(result); //false,dir不存在
    }
}
package com.itheima02.method;
import java.io.File;  
/*
*  public boolean delete() :删除由此File表示的文件或目录。
*           1. 此方法不走回收站, 慎用
*           2. delete可以删除 空文件夹和文件
*           3. 删不了非空文件夹,返回false。一个文件被其他程序所占用,其他程序无法删除,win下word一直用原文件,绘图软件是复制一份。所以word在使用修改时原文件删不了,绘图可以删除原文件
*/
public class DeleteDemo {
    public static void main(String[] args) { 
        File file = new File("c:/test/b");
        boolean result = file.delete();
        System.out.println(result);
    }
}
package com.itheima02.method;
import java.io.File;
/*
  目录的遍历:1. public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
	  				  1. 此方法只能用在文件夹上
	  				  2. 列出当前文件夹的所有子文件and子文件夹
		2. public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
*/
public class ListDemo {
    public static void main(String[] args) {
        File file = new File("c:/test/filedoc");  // 需求: 打印目录 C:\test\filedoc 下的所有文件(单级)
      /*  String[] list = file.list(); 
        for (String name : list) {
            System.out.println(name);  
        }*/
        
		//如下(列出对象)等同于上面(列出名字)
        File[] files = file.listFiles(); 
        for (File sonFile : files) {
            System.out.println(sonFile.getAbsolutePath());
        }
    }
}
package com.itheima03.recursion;
import java.io.File;
/*
*   需求: 删除非空文件夹 filedoc
*       前提: delete方法不能直接删除非空文件夹
*   解决: 1. 列出文件夹下的所有子路径(子文件和子文件夹)
*         2. 进行遍历
*         3. 判断每个子路径 是文件还是文件夹
*           3.1. 如果是文件,直接删
*           3.2 如果是文件夹
*               1. 接着列出此文件夹的所有子路径
*               2. 进行遍历
*               3. 判断每个子路径 是文件还是文件夹
*                   3.1 如果是文件,直接删
*                   3.2 如果是文件夹...
*                                 ....
*  有一段逻辑不断重复,重复的次数不知道, 但是结束条件: 删除到最后一级文件夹的子文件全都删掉
*/
public class DeleteDemo {
    public static void main(String[] args) {
        File dir = new File("c:/test/filedoc");
//        file.delete(); //无用,删不了非空文件夹
        deleteDir(dir);
    }    
    
    private static void deleteDir(File dir) {
        File[] files = dir.listFiles();
        for (File sonFile : files) {
            if(sonFile.isFile()){//是文件
                sonFile.delete();
            }else{//是文件夹
                deleteDir(sonFile);
            }
        }
        //如上是把子文件夹删了
        dir.delete(); //自己变成空文件夹,就可以删掉 ,弹栈。
    }
}
package com.itheima03.recursion;
import java.io.File;
/*
*   案例 : 计算非空文件夹的大小。length() : 计算文件的大小
*   思路: 1. 列出此文件夹的所有子路径
*        2. 遍历每个子路径
*        3. 判断是文件还是文件夹
*           3.1 是文件, 加总其大小
*           3.2 是文件夹, 递归
*   隐含结束条件:  文件夹不可能无限创建的
*/
public class CalcDemo {
    static int sum = 0;    
    public static void main(String[] args) {
        File dir = new File("c:/test/filedoc");
        calc(dir);
        System.out.println(sum);
    }    

    private static void calc(File dir) {
        File[] files = dir.listFiles();
        for (File sonFile : files) {
            if(sonFile.isFile()){ // 子文件
                sum += sonFile.length();
            }else{ //文件夹
                calc(sonFile);
            }
        }
    }
}

不死神兔:上个月都不死。1是一对。||是或。n是n个月,不是兔子数量。前不死,第3开生1。如下红线就是生。

package com.itheima03.recursion;
/*
*   斐波那契数列 : 有一对兔子,从第三个月开始生一对小兔子,之后每个月都生一对小兔子,其他兔子具备相同特点.
*    而且兔子永远不死,问第20个月,兔子有几对?
*/
public class RabbitDemo {
    public static void main(String[] args) {
        int n = 6; //第6个月
        int result = calcRabbit(n);
        System.out.println(result); //8
    }     
           
    private static int calcRabbit(int n) { //计算n个月兔子的对数
        if(n == 1 || n == 2){
            return 1;
        }else{
           return calcRabbit(n-1) + calcRabbit(n-2); //上个月和上两个月兔子相加
        }
    }
}
package com.itheima03.recursion;
public class RabbitDemo02 { 
//用循环写,不用递归【递归不需要思考循环次数,但比循环占内存多,因为循环只有一个方法】
    public static void main(String[] args) {
        int n = 20;
        int[] array = new int[n]; // 20个月不同月份兔子数量
        array[0] = 1; // 第一个月
        array[1] = 1; // 第二个月
        for (int i = 2; i < n; i++) {
            array[i] = array[i-1] + array[i-2];
        }
        System.out.println(array[19]); //6765
    }
} 

11.FileOutputStream两个构造:OIWR,IO流(内存为中心)

8个电子元件组在一起表示字符a即一个byte,一般bit不会用就像一分钱一样不用,所以一般最小单位字节

jdk8前接口里方法都是抽象方法。jdk8后加入静态和默认方法,除了函数式编程很少见到接口里静态和默认方法。抽象类不能实例化,没法创建对象,没法调它的普通方法:所以在四个抽象类下面各选4个子类File…(因为java里数据用File类来表示)。

package com.itheima01.outputstream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/*
*   FileOutStream的构造方法。这里的stream和lambda里stream没有一点关系
*       1. FileOutputStream(File file)
		2. FileOutputStream(String name) throws FileNotFoundException
 
	    fos创建的时候需要跟一个路径进行绑定(将会写出数据)				
	    构造的特点:  1. 如果路径文件不存在,则新建
		             2. 如果路径文件存在,则覆盖(无论路径文件是否存在,创建一个新的覆盖)
		                异常处理 -> 待会详细的写一遍
		             3. 如果路径的父目录不存在, 就会抛出FileNotFoundException

		 结论:  1. 一般使用FileOutputStream(String name)构造,快捷
		        2. 但是如果不能保证父目录是否存在,就用FileOutputStream(File file)
*/
public class Demo01 {
    public static void main(String[] args) throws FileNotFoundException {
//        FileOutputStream fos = new FileOutputStream("dir/a.txt");

		//如下解决上面父路径不存在不能创建缺点
        File file = new File("aaa/dir/a.txt"); //1. 判断file的父目录aaa/dir/是否存在        
        File parentFile = file.getParentFile(); 
        if(!parentFile.exists()){            
            parentFile.mkdirs(); //2. 如果不存在,则创建多级目录
        }
        FileOutputStream fos = new FileOutputStream(file); //必须有这一行才创建出文件
        System.out.println("创建成功");
    }
}
//两种创建文件方式
//1.
File file = new File("d:/a.txt"); //只创建File类路径实例
file.createNewFile(); //必须要执行才创建
//2.
File file = new File("d:/a.txt");
FileOutputStream outputStream =new FileOutputStream(file); //这行可以创建出一个空文件了

outputStream.write(123);
outputStream.close();
outputStream.flush();//必须刷新文件才有内容

12.OutputStream的基本方法:追加和换行

package com.itheima01.outputstream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 1. OutputStream 抽象类,是表示输出字节流的所有类的超类
		1. close() :关闭此输出流并释放与此流相关联的任何系统资源。
		2. write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。
		3. write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
		4. write(int b) :将指定的字节输出流。
*/
public class Demo02 {
    public static void main(String[] args) throws IOException { //FileNotFoundException父类是IOException
        FileOutputStream fos = new FileOutputStream("a.txt");       
        fos.write(97); // ASCII码表,a.txt内容里多了一个a。 //1. write(int b) : 一次写一个字节
        fos.write('a'); //aa

        byte[] array = {65,66,67,68,69}; //2. write(byte[] b) : 一次写一个字节数组
        fos.write(array); //aaABCDE
        
        fos.write(array,1,3); //aaABCDEBCD (最后多了BCD 3个字母) (1是array从66即B开始)  //3.write(byte[] b, int offset, int len): 一次写一个字节数组的一部分 //offset(偏移量):从第几个开始
                
        fos.close(); //close()本身有个io编译异常,流用完要关掉, 释放资源,流横跨内存和硬盘开销大,所以要关
    }
}
package com.itheima01.outputstream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
/*
    FileOutputStream(String name, boolean append) : 数据追加
        1. append = true : 往原文件追加内容,不会覆盖了
    
    换行: 1. windows : \r\n
          2. unix : \n
          3. System.lineSeparator() : 根据不同的系统,返回不同的换行符 (跨平台)
*/
public class Demo03 {
    public static void main(String[] args) throws IOException {
//        FileOutputStream fos = new FileOutputStream("a.txt"); //会覆盖原内容
        FileOutputStream fos = new FileOutputStream("a.txt",true); //不覆盖原内容
//        fos.write(48); //尾部追加一个0

//1111111111111111111111111111111111111111111111111111111111111111111111111111
//        fos.write("\r\n".getBytes());  //win
        String line = System.lineSeparator();
        fos.write(line.getBytes());
        
//111111111111111111111111111111111111111111111111111111111111111111111111111
        String str = "hello,byebye!";
        byte[] bytes = str.getBytes();
        fos.write(bytes);
        fos.close();
    }
}

13.FileInputStream两个构造:一次读一个字节或字节数组

package com.itheima02.intputstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/*
 FileInputStream 构造 (读:硬盘流到内存),以内存为中心,如果文件不存在, 则抛出FileNotFoundException
		1. FileInputStream(File file)
		2. FileInputStream(String name)
*/
public class Demo01 {
    public static void main(String[] args) throws FileNotFoundException {
//       FileInputStream fis = new FileInputStream("a.txt"); //a.txt在上面FileOutputStream已创建
        FileInputStream fis = new FileInputStream(new File("a.txt"));//不会创建文件,和上行一样
        System.out.println(fis); //java.io.FileInputStream@6d6f6e28
    }
}
package com.itheima02.intputstream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
*  InputStream 抽象类是表示字节输入流的所有类的超类
		1. void close() :关闭此输入流并释放与此流相关联的任何系统资源。
		2. int read() : 从输入流读取数据的下一个字节。
		3. int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
*/
public class Demo02 {
    public static void main(String[] args) throws IOException {
//        method01();

        FileInputStream fis = new FileInputStream("a.txt");         
      /*  int content = -1 ;  //=0也可以,都会被覆盖掉
        while(content != -1){
            content = fis.read();
            System.out.println(content);
        }*/
        
       int content = -1;
       while((content = fis.read()) != -1){ //这样和上面区别是,这不会多打印-1
           System.out.println(content);
        }		
        fis.close();
    }
    
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method01() throws IOException {
        FileInputStream fis = new FileInputStream("a.txt"); //已存在a.txt且里面有abc        
        int content = 0 ;        
        content = fis.read(); //向后移动一位,并返回当前元素(迭代器) //int read() : 一次读一个字节
        System.out.println(content); //97  //a是97
        
        content = fis.read();
        System.out.println(content);//98
        
        content = fis.read();
        System.out.println(content);//99
        
        content = fis.read(); // 如果后面已经到文件的末尾,不会再移动直接返回-1
        System.out.println(content);//-1 , -1不在ascii表上,-1表示没有意义
        fis.close();
    }
}
package com.itheima02.intputstream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
/*
*   int read(byte[] b) : 一次读一个字节数组,迭代器
*       1. 参数 byte[] : 用来存放字节流读取到的数据
*       2. 返回值 int : 本次读取到的字节个数
*            返回值 =  数组长度 < 文件剩余的字节数量 ? 数组长度 : 文件剩余字节数量  
*/
public class Demo03 {
    public static void main(String[] args) throws IOException {
//        method01();

        FileInputStream fis = new FileInputStream("a.txt");
        byte[] buffer = new byte[3];// {0,0,0}
        int length = 0;
        /*while(length != -1){											   //A(如下)
            length = fis.read(buffer);
            System.out.println(length + "->" + Arrays.toString(buffer));
        }*/

        while((length = fis.read(buffer)) != -1){
//            System.out.println(length + "->" + Arrays.toString(buffer)); //B
            String str = new String(buffer, 0, length); 				   //C
            System.out.println(str);
        }
        fis.close();
    }

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method01() throws IOException {
        FileInputStream fis = new FileInputStream("a.txt"); //a.txt里内容:abcdefg
        byte[] buffer = new byte[3];//{0,0,0}  //System.out.println(Arrays.toString(buffer));  //[0,0,0] //Arrays工具类

        int length = -1;
        length = fis.read(buffer); //框子只有3个位置,读了给buffer
        System.out.println(length + "->" + Arrays.toString(buffer)); //3->[97,98,99]
        
        length = fis.read(buffer);
        System.out.println(length + "->" + Arrays.toString(buffer));// length=3,def
        
        length = fis.read(buffer);
        System.out.println(length + "->" + Arrays.toString(buffer));//length=1,gef,ef还是上面的没覆盖

        length = fis.read(buffer);
        System.out.println(length + "->" + Arrays.toString(buffer));// length=-1,gef
        fis.close();
    }
}



14.文件复制(重要):System.currentTimeMillis(),fis.read,fos.write

package com.itheima03.copy;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
*   文件复制: 先读后写:一个读,另一个写
*       1. 先创建一个输入流 指向  原文件
*       2. 再创建一个输出流 指向  副本
*/
public class CopyDemo01 {
    public static void main(String[] args) throws IOException {
//        method01(); // 1148ms
        method02();//59ms 
        
//1111111111111111111111111111111111111111111111111111111111111111111111111      
      /*  long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1024*4000; i++) {  //循环不耗时,打印耗时,打印就是IO流
        }
        long endTime = System.currentTimeMillis(); 
        System.out.println(endTime - startTime); // 2ms*/
    }

//111111111111111111111111111111111111111111111111111111111一次读写一个 字节数组  (重要)
    private static void method02() throws IOException {
        long startTime = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("柳岩.jpg");
        FileOutputStream fos = new FileOutputStream("ly2.jpg");
  
        byte[] buffer = new byte[1024]; //1kb
        int length = -1;
        while((length = fis.read(buffer)) != -1){
            fos.write(buffer,0,length);
        }
        fos.close();
        fis.close();
   
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

//1111111111111111111111111111111111111111111111111111111111111一次读写一个 字节
    private static void method01() throws IOException {
        long startTime = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("柳岩.jpg"); //已存在
        FileOutputStream fos = new FileOutputStream("ly.jpg");
 
        int content = -1;
        while((content = fis.read()) != -1){
            fos.write(content);
        }        
        fos.close(); 
        fis.close(); //先打开的后关闭     
                
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }
}

15.编码表与字符流:编码/解码/乱码

15.1 编码:字节映射为字符。第一种编码:ascii码。


如下是中文编码,gb2312(1980年)既是字符集又是编码,gb2312的1字节等于ascii码,2字节分为区字节+位字节。gb2312汉字收录太少,所以出了gbk(1995年)(编码规范)。


如上"啊"改为"瞭",并放开gb2312和gbk,如下打印3F就是ascii码中63即?即解码失败。

"㞎"是gb18030独有的CJK统一汉字扩充。unicode字符集有utf-8和utf-16编码方式,前面字符集和编码都同一个,所以叫统一名字了。

15.2 乱码:FileReader(字符流)= FileInputStream(字节流 )+ 编码表

1.程序文件有编码:用文件浏览器(编码B)或IDE(编码B)打开一个文件(编码A)会乱码。
2.数据文件和数据源有编码:程序读取编码A的数据并且将转成字符串(默认使用编码B转),也会乱码。
3.展示数据的平台有编码:网页浏览器向服务端请求的数据的编码是A,但是浏览器展示用的编码B,也会乱码。



读文件:FileInputStream+byte[ ]:这种方式不好,如下1.txt中10个"啊",utf8中每个汉字对应3字节,所以10个"啊"对应30个字节。


爬GBK编码的网页:直接使用爬虫sdk里getstream(如下getForObject)获取字符串,如下url网页以gbk编码。

如下还是乱码。

如下改进,不要拿String.class,String是加工过的,拿byte[ ].class,如下未出现乱码。

package com.itheima04.chard;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
*      1. ASCII码表 : 128个数字(0~127,2的7次方=128)。0 000 0000 ~ 0 111 1111  (一个字节后7位占了)
*      2. ISO-8859-1 : 欧码(拉丁码)  256个。0000 0000 ~ 1111 1111  (一个字节)。Tomcat软件 默认编码表
* 
*      3. GBK (国标扩)是GB2312的扩展:
*        兼容ASCII码表即承认英文字母占一个字节, 一个汉字占两个字节(2的16次方,0 ~ 65535)。
* 
*      4. 国际 Unicode (universal code):utf-8 : 兼容ASCII表, 其次 归纳其他主流文字
*               1. 一个英文一个字节
*               2. 一个中文三个字节(如果有1000个字用GBK只有2kb的大小,用utf-8占3kb)
* 
*      总结: ASCII码表 : 0,A,a
*           GBK: windows中文简体默认(GBK)
*           UTF-8: 开发常用!!!(ideal里默认UTF-8)
*/
public class CharDemo {
    public static void main(String[] args) throws IOException {        
        FileInputStream fis = new FileInputStream("a.txt"); //字节流:一个英文一个字节 , 一个中文3个字节。如“中国人”显示9个数字    
      // FileReader fis = new FileReader("a.txt"); //字符流:一次读一个字符(英文只读1字节, 中文只读3个字节,动态判断)。如“中国人”显示3个数字。【有了编码表后有了动态判断能力】
       
        int content = -1;
        while((content = fis.read()) != -1){
            System.out.println(content); //打印换行
        }
        fis.close();
    }
}


package com.itheima04.chard;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//字符流: 赋值文本文件,不能复制图片。
public class CharDemo02 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("a.txt"); //已存在
        FileWriter fw = new FileWriter("b.txt");
        
        int length = -1;
        char[] buffer = new char[1024];
        while((length = fr.read(buffer)) != -1){
            fw.write(buffer,0,length);
        }        
        fw.close();
        fr.close(); //结果:b.txt和a.txt一样,原来没有b.txt这个文件
    }
}
package com.itheima04.chard;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class CodeDemo {
    public static void main(String[] args) throws IOException {
    //系统里的a.txt文件: GBK编码(win下改编码:另存为-选择编码)。
        FileReader fr = new FileReader("c:/test/a.txt"); //编码(encode) : 将字符 -> 字节
        int content;
        while((content = fr.read()) != -1){
            System.out.println((char)content);  //不解码:System.out.println(content); 97 98 99 20013..
        }
        fr.close(); //FileReader : UTF-8 解码  乱码
    }
}

如下因为GBK和UTF-8都支持ascii码。

15.3 linux文件编码转换:iconv,enca

第一种:iconv命令用于文件编码的转换,碰到gbk编码的文件,需要转换成utf8,直接使用该命令即可。iconv --list :列出iconv支持的编码列表。iconv -f 原编码 -t 新编码 filename -o newfile
-f : from 来源编码
-t : to 转换后新编码
-c: 忽略无效字符
-s: --silent,忽略警告
-o file : 可选,没有的话直接转换当前文件, 使用-o 保留源文件

第二种:linux安装enca,多文件转编码:

enca -v
wget http://dl.cihar/enca/enca-1.13.tar.gz
tar -zxvf enca-1.13.tar.gz
cd enca-1.13
//根据REAMDE说明依次执行如下命令
./configure
make
make check
make install
// which enca 得知enca默认安装路径 /usr/local/bin/目录,默认头文件位置/usr/local/include
//查看configure可执行文件帮助文档,可不执行
//./configure -h
//在执行./configure时可配置安装目录,可不执行
//./configure --prefix=/usr/bin
enca -L zh_CN filename  //或 enca filename
enca `ls`  //查看本目录下文件编码格式
enca -L zh_CN -x UTF-8 filename  //或enca -x UTF-8 filename,enca -x GB2312 filename
enca -x utf-8 *  // 该目录下所有文件,一般只有gbk,gb2312在linux中不显示中文乱码(终端,linux,数据库)

15.4 Linux中文字符集:/etc/locale.conf

1、查看当前系统已安装的字符集
(1)locale命令用于查看当前系统全部的已安装的字符集,Linux支持的符集约800种:
locale -a
(2)查看已安装的中文字符集(只查看中国大陆的,不包括香港和台湾):
locale -a|grep zh_CN,下图表示已经安装了中文字符集。

2.安装中文字符集
如果您的Linux系统没有安装中文字符集,可以用yum命令安装。安装中文字符集软件包的方法比较多,没找到准确的说法,所以把多种方法都写了进来,以下命令都可以执行,不会有副作用:
yum -y groupinstall chinese-support
yum -y install chinese-support
yum -y install kde-l10n-Chinese
yum -y install ibus-table-chinese-1.4.6-3.el7.noarch
安装后,执行locale -a|grep zh_CN,如果显示的内容如下,表示安装成功。

3.修改字符集配置文件
CentOS6.x字符集配置文件在/etc/sysconfig/i18n文件中。
CentOS7.x字符集配置文件在/etc/locale.conf文件中,内容如下:

执行以下命令或者重启系统使修改生效:
CentOS6.x:source /etc/sysconfig/i18n
CentOS7.x:source /etc/locale.conf

4.centos7修改 /etc/locale.conf 不生效
centos7的语言环境变量是通过/etc/profile.d/lang.sh加载locale.conf来设置的。我在修改 /etc/locale.conf 内容为 LANG=“zh_CN.UTF-8” 并重启后发现并没有什么效果。local得出的依然是默认的en_US。于是我去检查lang.sh的代码。发现中间有一段是这样的:

可以看到当变量为zh*的时候,依然将LANG赋值为en_US.UTF-8。不知道官方为什么会采用这种默认设置,所以我将代码改成这样。重启之后再次查看locale就好了。

15.5 Oracle的字符集:NLS_LANG

服务端的字符集:Oracle数据库实例创建后,如果没有开始业务运行,可修改字符集,如果已经业务化运行,不建议修改字符集,会造成数据中的汉字乱码

1.查看服务端字符集
执行以下SQL可以查看服务端的字符集:
select * from NLS_DATABASE_PARAMETERS where parameter like ‘%CHARACTERSET%’;

执行以下SQL也可以查看服务端的字符集:select userenv(‘language’) from dual;
2.修改服务端字符集
用DBA权限,执行以下步骤修改Oracle数据库的字符集(例如修改为ZHS16GBK)。
(1)修改服务端操作系统的NLS_LANG环境变量:
export NLS_LANG=‘Simplified Chinese_China.ZHS16GBK’
(2)关闭Oracle数据库:
shutdown immediate;
(3)把数据库启动到mount状态:
startup mount;
(4)把数据库改为restricted模式:
alter system enable restricted session;
alter system set job_queue_processes=0;
alter system set aq_tm_processes=0;
(5)打开数据库:
alter database open;
(6)修改数据库的字符集。
alter database character set internal_use ZHS16GBK;
(7)重启数据库:
shutdown immediate;
startup;

客户端的字符集:Oracle客户端的字符集必须与服务端相同,否则中文会出现乱码,客户端的字符集由NLS_LANG环境变量控制

1.Linux环境
(1)查看NLS_LANG环境变量。
env|grep NLS_LANG

(2)设置环境变量
修改环境变量参数文件(系统或用户的profile文件)。
export NLS_LANG=‘Simplified Chinese_China.ZHS16GBK’

2.Windows环境
打开注册表( 执行regedit.exe)
HKEY_LOCAL_MACHINE -> SOFTWARE -> ORACLE -> KEY_OraClient11g_home1

16.flush和close方法:.getBytes()

如下两种方式都可以,OutputStream和Writer对应。

package com.itheima04.chard;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
/*
*   OutputStream : flush方法:刷新此输出流,强制写出所有缓冲的输出字节。
*   Writer : flush方法:刷新此流,将流中缓存的数据写到硬盘。
*   结论:OutputStream的flush方法没用, Writer的flush有点用。
* 
*   原理: 字符流 = 字节流 + 编码表 (缓存区:为了兼容编码表存在)。
	之前把一次读一个字节数组,那个数组称为缓存。字符流底层有一个字节数组byte[]:支持多个字节,8k(把前面字节缓存起来)
*/
public class FlushDemo {
    public static void main(String[] args) throws IOException {
//      FileOutputStream fos = new FileOutputStream("a.txt"); //a.txt里写啥都无所谓,之后会覆盖
//      fos.write("中文".getBytes()); //字节流没有字节数组,把中文拆成字节数组,把中文所包含的6个字节放到字节数组里,让字节流输出去,因为底层没有缓冲即byte[]数组。//有一个字节,就写一个,没有缓存不用刷新。

//11111111111111111111111111111111111111111111111111111111111111111111111111111111
        FileWriter fw = new FileWriter("a.txt");        
        fw.write("中文2"); // 先放到缓存(内存),等缓存满了,再写到硬盘上
        fw.flush(); //刷新缓存, 把缓存清空,写到硬盘上,怕断电  //fw.close(); :释放资源,释放之前,底层默认先调用flush,所以这行单写fw.close()也能写进去。

        fw.write("a");//上行close之后不能再使用这个流
        fw.flush(); //刷新显示
    }
}

17.IO流异常处理:字符流用于读写文本文件,字节流outputStream(内存->硬盘:写,以内存为中心),BOP

如下是四个抽象类的其他子类BufferedOs:后面Os是outputStream简写。OutputStreamWriter:是Writer的子类,是FileWriter的父类,没FileWriter封装程度高,暴露了os(字节流)和charset(编码表)。序列化流只有字节流,没有字符流。转换流只有字符流,没有字节流。

如下finally代码一定会执行。

如下实际开发a.txt(已存在)和b.txt路径由用户指定会发生异常,IO不能直接抛,程序会崩,所以自己处理try catch,不在try中编译异常就是IO异常。

如下改进就在try外面int i = 0;定义+赋值。同理FileInputStream fis = null;

package com.itheima01.exception;
import java.io.*;
/*
* 不论什么类型文件复制都用 字节流! 理由: 1.没有编解码就不会产生乱码, 用字符流有乱码的风险。                              			       *                                       2.没有编解码复制文件更快。
*/
public class ExceptionDemo {
    public static void main(String[] args)  { //之前图省事直接在main这行throws IOException
        //外边定义,里面赋值   //每个变量在使用的时候,必须有值        
        FileInputStream fis = null;
        FileOutputStream fos = null ;
        try { 
			//int i=1/0;  //直接跳到catch,下面fis没有赋上值,所以外面定义时赋null值
            fis = new FileInputStream("a.txt"); //刚指定完a.txt,有个程序将a.txt干掉了,出问题:文件找不到,所以IO不能直接抛			
            fos = new FileOutputStream("b.txt");

            int length = -1;
            byte[] buffer = new byte[1024];
            while((length = fis.read(buffer)) != -1){
                fos.write(buffer,0,length);
            }       
        } catch (IOException e) {   
            e.printStackTrace();            
        } finally{ 
            if(fos != null){//避免空指针,fos有可能为空(try中值没赋上),空对象调方法产生空指针异常
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

如上finally中代码冗余。

package com.itheima01.exception;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
// 多态: 简化代码  所有的IO流都实现了 Closeable 接口 (因为所有的IO流有close方法),选中Closeable按ctrl+h
public class ExceptionDemo02 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null ;
        try {
            fis = new FileInputStream("a.txt");
            fos = new FileOutputStream("b.txt");
            int length = -1;
            byte[] buffer = new byte[1024];
            while((length = fis.read(buffer)) != -1){
                fos.write(buffer,0,length);
            }
        } catch (IOException e) {
            e.printStackTrace();     
        } finally{
		   // release(fos);
            release(fos,fis); //多态
        }
    }
    private static void release(Closeable... ios) { //可变参数
        for (Closeable io : ios) { //遍历数组
            if(io != null){ //避免空指针
                try {
                    io.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
package com.itheima01.exception;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* JDK7 特性:  try...with resource
*       try( 要释放的IO流对象A,B) {
*           当这里的代码执行完, A B流就会被自动释放掉(不需要手动调用close)
*       }
*       特点: A,B 默认被final所修饰
*/
public class ExceptionDemo03 {
    public static void main(String[] args) {
        try(FileInputStream fis = new FileInputStream("a.txt");
            FileOutputStream fos = new FileOutputStream("b.txt")) {            
            int length = -1;
            byte[] buffer = new byte[1024];
            while((length = fis.read(buffer)) != -1){
                fos.write(buffer,0,length);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

18.字节/字符缓冲流:readLine

package com.itheima02.buffer;
import java.io.*;
/*
*   字节缓冲流:1. public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
		         public BufferedInputStream(InputStream in, int size)
	     	  2. public BufferedOutputStream(OutputStream out) :创建一个 新的缓冲输出流。	     	  
*              底层: 字节流 + 缓存区(默认8k) 。 目的: 用空间(内存)换时间 (提高效率)。           
*              方法: 构造方法注意一下,需要手动传入一个字节流对象。其他方法都跟父类一样          
*/
public class ByteDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream is = new FileInputStream("a.txt"); //注意是is不是fis
        FileOutputStream os = new FileOutputStream("b.txt");        
        //如下字节缓存流
        BufferedInputStream fis = new BufferedInputStream(is,80*1024);//字符默认8k,这个可以手动调 
        BufferedOutputStream fos = new BufferedOutputStream(os);
    
        int length = -1;  //80k(一级) -> 1k(二级)写入到硬盘
        byte[] buffer = new byte[1024];  //这里1kb【二级】最好和上面80k【一级】一致,这样不会有空间浪费问题
        while((length = fis.read(buffer)) != -1){
            fos.write(buffer,0,length);
        }
        fos.close();
        fis.close();
    }
}

package com.itheima02.buffer;
import java.io.*;
/*
* 字符缓冲流:1. 构造
			    1. public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
			    2. public BufferedWriter(Writer out) : 创建一个 新的缓冲输出流。		 
	    	 2. 特有方法
		    	1. BufferedReader: public String readLine() : 读一行文字。
			    2. BufferedWriter: public void newLine() : 写一行行分隔符,由系统属性定义符号。
*/
public class CharDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("a.txt")); //BufferedReader(字符流),BufferedReader也默认8kb
//        String line = br.readLine();
//        System.out.println(line);
        String line = null;
        while((line = br.readLine()) != null){
            System.out.println(line); //有几行打印几行
        }        
        br.close(); //关高级流,会默认将低级流也会一并关掉
        
//11111111111111111111111111111111111111111111111111111111111111111111111111111      
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
        bw.write("一句话");
        bw.write(System.lineSeparator()); //换行
        
        bw.write("一段话");
        bw.newLine(); //换行,底层就是上面System.lineSeparator()
        
        bw.write("一翻话");
        bw.close();
    }
}

19.案例_出师表:\ \ . ,content = map.get(i)


正则中一个单独的点表示任意字符。

package com.itheima02.buffer;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/*
*   需求: 将a.txt中的内容,按照每行的序号进行排序 b.txt
*       1.先把a.txt中的数据都读出来。 2.一次读一行, 进行切割 number -> 语句
* 
*       方案A
*       3. 放HashMap <Integer,String> map
*       4. for i 1-9 (i = key) //不需要排序,按序号取出并输出
*           value = map.get(key)
* 
*       方案B
*        3. 放TreeMap<Integer,String>  //TreeMap的key会自动排序的,Integer类型会按自然顺序排,底层是treeset比较器默认自动升序。
*        4. 遍历打印
*/
public class OutTeacherWatchDemo {
    public static void main(String[] args) throws IOException {
//        method01();
        TreeMap<Integer, String> map = new TreeMap<>();
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        String line = null;
        while((line = br.readLine()) != null){
            String[] split = line.split("\\.");
            Integer number = Integer.parseInt(split[0]);
            String content = split[1];
            map.put(number,content);
        }
        br.close();

        BufferedWriter bw = new BufferedWriter(new FileWriter("c.txt"));        
        Set<Map.Entry<Integer, String>> entrySet = map.entrySet(); //直接遍历treeMap,输出即可
        for (Map.Entry<Integer, String> entry : entrySet) {
            Integer key = entry.getKey();
            String value = entry.getValue();
            bw.write(key + "." + value);
            bw.newLine();
        }
        bw.close();
    }

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111       
    private static void method01() throws IOException {  //方案A
        HashMap<Integer, String> map = new HashMap<>();         
        BufferedReader br = new BufferedReader(new FileReader("a.txt")); //这是读取a.txt的流
        String line = null;
        while((line = br.readLine()) != null){                
            String[] split = line.split("\\."); // 注意: \\. 表示一个.  
            Integer number = Integer.parseInt(split[0]); //前面是数字                
            String content = split[1]; //后面是内容
            map.put(number,content);
        }
        br.close();

        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); //这是输出文本的流        
        for (int i = 1; i <= 9; i++) { // 遍历1~9 -> 指的就是上面的number
            String content = map.get(i);
            bw.write(i +"." + content);
            bw.newLine(); //添加一个换行符
        }
        bw.close();
        System.out.println("复制完成");
    }
}

20.转换流:字节字符的中间流

package com.itheima03.transfer;
import java.io.*;
/*
*   案例: 系统文件(GBK 编码)  ->  程序(utf-8 解码)
*   乱码: 编解码所使用的编码表不一致。 解决: 将编码和解码编码表一致
* 
*   方案A (最常用): 系统文件GBK -> UTF-8 。FileReader流 搞定
*   方案B: 程序默认编码表改成GBK (一般不会这么做!!!) 。FileReader 流 搞定
*   方案C: 将IO流的解码字符集 指定为gbk , 但是程序还是utf-8
*         InputStreamReader (在开发中一般也不用, 开发中的涉及的程序和资料都是UTF-8)
*/
public class Demo01 {
    public static void main(String[] args) throws IOException {
//  FileReader fr = new FileReader("c:/test/a.txt"); //FileReader底层默认编码表utf-8 ,与a.txt默认的GBK会乱码。 //这只能将win上的a.txt另存为utf-8搞定。
        FileInputStream fis = new FileInputStream("c:/test/a.txt"); //a.txt里内容:写一句话
        InputStreamReader fr = new InputStreamReader(fis, "gbk");        
        int content = -1;
        while((content = fr.read()) != -1){
            System.out.println((char)content); 
        }
        fr.close();
    }
}

package com.itheima03.transfer;
import java.io.*;
/*
    练习:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
	    1. 读GBK编码文件 : 用GBK编码的字符输入流:
	       1. FileReader : 底层 FileInputStream + utf-8 默认 (不行)
	       2. InputStreamReader(FileReader的父) : FileInputStream + gbk 指定 (可以)  
 			  	 									    字节输入流  + 编码表
 									  
	    2. 写utf-8编码文件 : 用utf-8编码的字符输出流:
	       1. FileWriter : FileoutputStream + utf-8 默认(可以)
	       2. OutputStreamWriter : FileoutputStream + utf-8 指定(可以)。两个都可以, 用FileWriter(简单)
*/
public class Demo02 {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr
                = new InputStreamReader(new FileInputStream("c:/test/a.txt"), "gbk");
										  // 指定 字节输入流  + 编码表
												
//        FileWriter fw = new FileWriter("c:/test/b.txt"); //用下行也可以
        OutputStreamWriter osw
                = new OutputStreamWriter(new FileOutputStream("c:/test/b.txt"), "utf-8");
             
        int length = -1;
        char[] buffer = new char[1024]; //用的字符流,不是byte[]
        while((length = isr.read(buffer)) != -1){
            osw.write(buffer,0,length);
        }
        osw.close();
        isr.close(); //运行后产生b.txt utf-8 9字节 ,原始a.txt gbk 6字节 里面3个“啊”
    }
}

21.序列化流:oos.writeObject,Serializable

如下把对象字符串化。

如下读字符串取出值,再重新新建一个对象。

package com.itheima04.serial;
import java.io.*;
/*
*   文本编码: 字符 -> 字节(也叫二进制 )(目的保存数据)
* 
*   序列化(serializable) : 对象/数据结构 -> 二进制 (目的将对象直接保存在硬盘上)存档。
*               Base64 编码等  和utf-8和gbk没有关系
* 
*   反序列化 : 二进制 -> 对象 (将硬盘上的数据 读到 内存中形成对象) 读档,不需要中间过程。
*               Base64 解码
* 
    public ObjectOutputStream(OutputStream out) : 序列化流
    public ObjectInputStream(InputStream in) :  反序列化流
        
    总结: 一个类要允许被序列化:  1. 首先: 实现Serializable接口
              2. 其次: 这个类要设置唯一的serialVersionUID (String/Integer类源码里也有..UID)   
 补丁:transient : 瞬态 (关键字),用这个关键字修饰的属性,不允许序列化/反序列化,如密码不希望被保存
*/
public class Demo01 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
//        method01(); //序
        method2();  //反序
    }
    private static void method2() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));        
        Object s1 = ois.readObject();// 反序列化,具体不知道什么对象, 设计成Object
//        Student s1 = (Student) ois.readObject(); //可以,同上行输出一样
        System.out.println(s1); //Student{name'张三',age=18}
        ois.close();
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
    private static void method01() throws IOException {
        Student s = new Student("张三", 18); //这对象在内存中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt")); //输出流new一下就产生文件obj.txt
        oos.writeObject(s); //序列化,存档
        oos.close();
    }
}
package com.itheima04.serial;
import java.io.Serializable;
/*
*      序列化相关的两个异常之一:
*           1. java.io.NotSerializableException : 无法序列化异常
*             java规定: 某个类的对象 要可以被序列化, 必须实现一个接口 Serializable(没有任何内容的接口 -> 授权 ,允许被人家保存)
* 
*           2. java.io.InvalidClassException:  无效的类异常
*             保存的时候(Student无int number),读取的时候(Student有int number)
* 
*             解决: 搞一个身份证 (这个类不论如何改变,始终JVM都能识别)
*             JVM识别一个类: serialVersionUID(不同的类,这个id不可以一样)
*/
public class Student implements Serializable{
    private final static long serialVersionUID = 1L; //长整型 //需要method01重新保存,保存完后,增加属性,method02能识别读出来。
    private String name;
    private transient int age; //age不希望暴露,int类型默认为0且永远0 //Student{name'张三',age=0}
    private String password;
    int number;
    int year;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

22.打印流:最后一个流,字符数据(文本文件)的读或写(不是读写即复制,复制用字节流),用字符流方便

package com.itheima05.print;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;

public class PrintDemo {
    public static void main(String[] args) throws IOException {
      // method();
      /*
      *系统打印流(System.java中): 输出在控制台不是在硬盘上(控制台也是内存,关了就没了),与字节输出流(输出硬盘上)不一样。
      *public final static PrintStream out = null; //在java中, 用final修饰,并且赋值为null, 还调用out方法竟然没有空指针 !!!
      
      *在java中赋值为null是无效,在JVM底层,这个流是C语言赋值的 (JNI),这样可以直接控制操作系统界面如控制台
      *private static native void setOut0(PrintStream out);  本地方法 (底层调用C)
      */
      
 // System.setOut(new PrintStream("print.txt")); //与下行同时运行,下行yyyyyyyyy打印到print.txt,控制台改到print.txt
        System.out.println("yyyyyyyyy"); //控制台 //out是PrintStream类型
        System.err.println("哈哈"); // 红色 (一般 底层打印错误信息)
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method() throws IOException {
        PrintStream ps = new PrintStream("print.txt"); //(硬盘上的.txt) //实际上new了一个PrintStream的父类FileOutputStream
        ps.write(97);
        ps.write("xxx".getBytes());  //对应下面ps.print
        ps.write(System.lineSeparator().getBytes());  //对应ps.println    
            
        ps.print("xxx"); //底层调用.write
        ps.println("xxx");
        ps.close();
    }
}

23.Properties集合:Hashtable 与HashMap 区别,p.load,toString

package com.itheima06.properties;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
/*
* Properties : 是一个放弃了泛型的Map,不是io流。
*       0. 父类 :Hashtable就是HashMap
*       Hashtable : 哈希表 (线程同步: 线程安全,但是效率低,大部分方法都加了synchronized关键字)(淘汰)
*       HashMap : 哈希表 (线程不同步 : 不安全效率高。真的有线程并发问题,自己可以加同步代码块,锁范围越小越好)
* 
*       1. 可以跟IO流相互合作来组织数据结构:把Map结构的数据存到硬盘,把硬盘中map结构读取到内存    
*  
		2. 方法:应用在读取配置文件
            1. setProperty(String key, String value):调用 Hashtable 的方法 相当于put()
            2. stringPropertyNames()返回此属性列表中的键集  相当于keySet()
       (上面方法用不到,因为不会用Property保存数据,用Property读配置文件,以下重要!!!)
            3. getProperty(String key)用指定的键在此属性列表中搜索属性
            4. load(InputStream) 和load(Reader):把指定流所对应的文件中的数据,读取出来,保存到Propertie集合中
*/
public class PropertiesDemo {
    public static void main(String[] args) throws IOException {
//        method();

        //1. 创建了Properties对象(Map对象)
        Properties p = new Properties(); //集合都在java.util包下  
              
        //2. 跟IO流合作 (读取map结构的数据 -> 数据以Map结构封装p对象)
        p.load(new FileInputStream("note.properties")); //note.properties里写了name=zs 换行 age=18 ..。note.txt也可以。 //以前读法:字符流一次读一行
        
//        Object name = p.get("name"); //返回object不好操作  //zs
//        Object age = p.get("age"); //常用下面api  //18
        String name = p.getProperty("name"); //网页,控制台,其他文件数据读到程序里都是字符串
        String age = p.getProperty("age"); //就算是数字,读进来也是字符串
        System.out.println(name + "," +  age);//用p.get时不能写成(name+age),因为两个object不能相加,所以中间加一个字符串",",底层会调用toString方法
	}

//111111111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method() {
        Properties map = new Properties(); //Map<Object,Object> map = new Properties();
        map.put(1,"张三");
        map.put(2,"李四");
        System.out.println(map); //{2=李四,1=张三}
    }
}

24.tcp通讯:ip相当于小区,port相当于门牌号

24.1 单向:一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个



下面将三要素融合,https中s就是secure。

socket封装了io流,所以要关。高层socket关了,低层也会关。如下accept会阻塞。

package com.itheima01.tcp;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) throws IOException {        
        String host = "127.0.0.1"; //1.连接服务端(指定服务端的ip和port)自动连接
        int port = 10010;
        Socket socket = new Socket(host, port);
       
        OutputStream os = socket.getOutputStream();  //2.写出数据
        os.write(" hello, tcp".getBytes());
        socket.close();
    }
}
package com.itheima01.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port。ip是路由器分配的
        System.out.println("1");        
        Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
        System.out.println("2");        
        InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据  

        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = is.read(buffer)) != -1){
            String msg = new String(buffer, 0, length);
            System.out.println("收到客户端的数据:" + msg);
        }
        socket.close();//关闭和客户端的连接
        serverSocket.close(); //关闭服务端
    }
}

24.2 双向:socket中介

package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
//准备,写,读
public class TcpClient {
    public static void main(String[] args) throws IOException {
        String host = "127.0.0.1";  //1.连接服务端 (指定服务端的ip和port) 自动连接
        int port = 10010;
        Socket socket = new Socket(host, port);

//11111111111111111111111111111111111111111111111111111111111111111111111  
        OutputStream os = socket.getOutputStream(); //2.写出数据
        os.write(" hello, tcp".getBytes());                
        socket.shutdownOutput(); //解决: 告知对方 写完了。没有关闭socket,关闭就连接不了

//1111111111111111111111111111111111111111111111111111111111111111111111 
        InputStream is = socket.getInputStream(); //3.客户端收到数据        
        int length=-1;
        byte[] buffer = new byte[1024];
        while((length = is.read(buffer)) != -1){
            String msg = new String(buffer, 0, length);
            System.out.println("收到服务端的数据:" + msg);
        }
        socket.close();
    }
}
package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port
        System.out.println("1");
        Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
        System.out.println("2");

//111111111111111111111111111111111111111111111111111111111111111111111111111111111        
        InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据
        int length = -1;
        byte[] buffer = new byte[1024];
        /*
        *   问题: 如下read方法是一个阻塞方法
        *      以前: 读文件,读到末尾返回-1
        *      现在: 读socket中传输的数据 (像电话), 对方socket关掉就算是末尾了
        *   解决: 1. 对方在输出结束,可以直接关闭socket,这个数据就结束了,但是对方就不能再接收了
        *        2. 对方直接告知已经输出结束,socket不需要关
        */
        while((length = is.read(buffer)) != -1){//客户端socket.shutdownOutput();服务端就不会阻塞在这一行
            String msg = new String(buffer, 0, length);
            System.out.println("收到客户端的数据:" + msg);
        }  

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111      
        OutputStream os = socket.getOutputStream(); //4.服务端写出数据  
        os.write(" 好,退下吧~~".getBytes()); 
        socket.close();//关闭和客户端的连接  //这里socket.shutdownOutput();可以不写,因为直接close了
        serverSocket.close(); // 关闭服务端
    }
}

文件上传:如下上面是服务端。

package com.itheima03.upload;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class UploadClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 10086);

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        FileInputStream fis = new FileInputStream("柳岩.jpg");//读本地文件(da14模块下有个柳岩.jpg),写到服务器socket中        
        OutputStream os = socket.getOutputStream();    
                             
        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = fis.read(buffer)) != -1){ //读
            os.write(buffer,0,length); //写
        }
        fis.close();
        socket.close();
    }
}
package com.itheima03.upload;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10086);
        Socket socket = serverSocket.accept();	

//1111111111111111111111111111111111111111111111111111111111111111111111111111111	
        InputStream is = socket.getInputStream();         
        long time = System.currentTimeMillis();//系统当前时间毫秒值
        FileOutputStream fos = new FileOutputStream("c:/test/server/" + time + ".jpg");    
             
        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = is.read(buffer)) != -1){ //读
            fos.write(buffer,0,length);  //写
        }
        fos.close();
        socket.close();
        serverSocket.close();
    }
}

25.多线程优化服务端与UUID:is和fos

package com.itheima04.good;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
/*
* 	服务器一般是24小时不关闭的,前面写的只能收到一张图片服务器就关闭了。解决方案: 
* 		1. 死循环
*          java.BindException: Address already in use: JVM_Bind。绑定异常(port重复占用)
* 
*       2. 想支持多个客户端的并发访问
*          解决: 引入多线程  代码加在阻塞之后
*          补充: 线程池 管理 线程(1. 避免线程频繁创建和销毁 2. 控制线程数量)
* 
*       2. 文件名 : 以系统当前时间命名(并发: 同一毫秒,有两个用户同时上传文件, 就会有用户文件覆盖)
*               1. 一般文件,不需要标识这个图片属于谁的 : 服务器用UUID                             
*               2. 文件名需要跟用户上传的文件名一致 : 需要另一个线程传输这个文件名(这个用户id + 文件名)
*/
public class UploadServer {
    public static void main(String[] args) throws IOException {        
        ServerSocket serverSocket = new ServerSocket(10086); //只创建一个服务端,一个端口不能被两个程序占用,不能在这行上面加while(true)
        while(true){  //死循环接收客户端请求            
            Socket socket = serverSocket.accept(); //虽然while(true),但这行会阻塞,单线程
            //有客户上门才开线程,不能无限开。
            new Thread(new Runnable() { //不能放accept()前面,不能无限开线程
                @Override
                public void run() {
                    try {
                        InputStream is = socket.getInputStream();                        
                        // long time = System.currentTimeMillis();//系统当前时间毫秒值
                        String name = UUID.randomUUID().toString().replace("-", ""); //下行time改为name
                        FileOutputStream fos = new FileOutputStream("c:/test/server/" + name + ".jpg");
                        
                        int length = -1;
                        byte[] buffer = new byte[1024];
                        while((length = is.read(buffer)) != -1){
                            fos.write(buffer,0,length);
                        }                        
                        fos.close(); //应该放在finally中
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
//         serverSocket.close(); // 服务器不能关闭,关了就不回到while(true)了
        }
    }
}
package com.itheima04.good;
import java.util.UUID;
// UUID: 通用唯一标识符(Universally Unique Identifier),永不重复
public class UuidDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
//            UUID uuid = UUID.randomUUID();
//            String str = uuid.toString().replace("-", ""); 
            String str = UUID.randomUUID().toString().replace("-", ""); //这行同上两行
            System.out.println(str);
        }
    }
}

如下未把UUID的 - 替换为空。

如下BS案例:socket被写,页面被读。写一个浏览器程序难度和操作系统一样,鸿蒙os就是浏览器。

package com.itheima05.bs;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class BsServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10090);
        Socket socket = serverSocket.accept(); //接收浏览器请求
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111
        OutputStream os = socket.getOutputStream(); //写出页面
        os.write("HTTP/1.1 200 OK\r\n".getBytes()); // http协议的响应行  第一行
        os.write("Content-Type:text/html\r\n\r\n".getBytes()); //http协议的响应头 第二行,第三行为空白
        FileInputStream fis = new FileInputStream("index.html"); //已有,用来被读

        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = fis.read(buffer)) != -1){
            os.write(buffer,0,length);  //第四行,响应体
        }
        fis.close();
        socket.close();
        serverSocket.close();
    }
}


26.Class对象三种获取方式:Class.forName(" ")

package com.itheima02.clazz;
import org.junit.Test;
/*
*   反射前提: Class对象
*       1. 引用类型: Class(大写C和关键字class不一样)
*       2. 回顾: 当程序用到一个类的时候, 会加载这个类进内存(方法区)
*       3. 在运行时, java用Class对象来描述.class文件(就算是一个文件也用对象描述)
*              .class文件(硬盘) ->  Class对象(内存)
*       4. Class对象的特点:
*           1. Class对象的创建: 
* 				一个.class文件被加载进内存,JVM会创建这个类的Class对象。
* 				因为一个.class文件在使用的时候只需要被加载一次, 所以这个.class文件对应的Class对象也只会有一个!!!
* 
*           重点: Class对象(类对象)是JVM创建的, 开发者是无法创建的。(开发者可以new一个object,这叫实例对象)
* 				 一个类的Class对象一般只有一个 (使用: 同步锁 xxx.class对象:别人不能创建,天然唯一安全)
* 
*           2. 三种Class对象的获取(不是创建)
*               1. 类名.class
*               2. 对象名.getClass()
*               3. Class.forName(全限定名);  -> 加载配置文件
*/
public class ClassDemo {
    @Test
    public void method01(){  
        Class<Student> clazz = Student.class;            
        System.out.println(clazz); //class com.itheima02.clazz.Student //全类名或全限定名 : 包名 + 类名
        synchronized (ClassDemo.class){ //当前类名.class  -> 锁对象
           //这段代码能够运行,意味着当前类一定被加载进内存-> JVM会创建当前类的Class对象
          //ClassDemo.class改为string.class浪费内存,因为不一定用到string,没必要加载string类
        }
    }
    
    @Test
    public void method02(){
        Student s = new Student();
        Class<?> clazz = s.getClass();
        System.out.println(clazz); //class com.itheima02.clazz.Student 同上
        System.out.println(clazz == Student.class); //true
    }
    
    @Test
    public void method03() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("com.itheima02.clazz.Student");
        System.out.println(clazz); //class com.itheima02.clazz.Student 同上
    }
}
package com.itheima02.clazz;
import java.util.Objects;

public class Student {
    private String name;    
    @Override  
    public boolean equals(Object o) {    
        if (this == o) return true;  //this表示student类
        // this.getClass() == o.getClass()
        // o instanceof Student
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name);
    }    
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

27.反射:clazz.newInstance()

package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
 *  反射(reflect) : 在程序运行时操作类
 *       1. 运行时 :  .java源码 -> .class字节码(编译) -> runtime(运行)
 *       2. 操作 :   
 *       3. 类 :   .java  -> .class(二进制指令让jvm读) -> Class对象(运行时的类的样子)
 *            1. 构造方法 / 构造器  Constructor  
 *            2. 方法 Method   
 *            3. 属性 Field  
 *   A. 操作构造方法: 以前: 用构造方法来 创建对象
 */
public class ConstructorDemo {
    public static void main(String[] args) {
//        Person p = new Person(); //Person{name='null',age=0}
        Person p = new Person("张三");
        System.out.println(p); //Person{name='张三',age=0}
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method01() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 反射 : 操作构造方法
        //1. 获取Class对象
        //2. 再获取Class对象中的构造方法
        //3. 使用构造方法创建对象
        Class clazz = Person.class;
        
        /*
        *  Constructor<T> getConstructor(Class<?>... parameterTypes)
        *   1. 参数: 需要指定想要获取的构造方法的参数列表  类型
        *   2. 返回值: 返回构造方法的对象
        */
        //Constructor constructor = clazz.getConstructor(String.class,int.class);
        Constructor constructor = clazz.getConstructor(String.class);
        
        /*
        *   T newInstance(Object ... initargs)
        *       1. 参数: 使用构造方法创建对象时传入的实参 (必须跟上面参数列表类型一致)
        *       2. 返回值: 返回创建好的实例
        */
        Object obj = constructor.newInstance("张三");
        System.out.println(obj); //Person{name='张三',age=0}
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method02() throws NoSuchMethodException, IllegalAccessException,   InvocationTargetException, InstantiationException {  //简易的api
        Class clazz = Person.class;                
       /* Constructor constructor = clazz.getConstructor();
        Object obj = constructor.newInstance();
        System.out.println(obj); //Person{name='null',age=0}  */ 

 //Class对象获取空参构造,并创建实例,如下简单,等同于上面两行//(JavaBean要求: 1. private属性 2. public 空参构造 3. public get set方法)
        Object obj = clazz.newInstance();
        System.out.println(obj); //Person{name='null',age=0}
    }

//111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //暴力反射: 知道(可以访问private方法)
        Class clazz = Person.class;
        /*
        *   getConstructor()  : 只能获取public修饰的构造
        *   getDeclaredConstructor() : 获取所有权限的构造(private也可以)
        */
//        Constructor constructor = clazz.getConstructor(String.class, int.class);
        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);        
        constructor.setAccessible(true); //暴力反射: 权限设置public //上面能拿到私有,但是不能调用,只能person本类内部调用,所以加这行可以调用
        Object obj = constructor.newInstance("李四", 18); 
        System.out.println(obj); //Person{name='李四',age=18} 
    }
}
package com.itheima03.reflect;

public class Person {
    public String name;
    private int age;
    public Person(){
    }
    public Person(String name){
        this.name = name;
    }
    private Person(String name,int age){ //注意private,用暴力反射
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public void speak(String msg){
        System.out.println("speak:" + msg);
//        return 1;
    }
    public void speak(double msg){
        System.out.println("speak:" + msg);
    }
    private void show(int year){
        System.out.println("show:" + year);
    }
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// B.方法的反射(反射中最重要的是构造方法和普通方法反射,没有属性)
public class MethodDemo {
    public static void main(String[] args) {
       Person p = new Person();
       // Person p = null; //speak方法设为静态,不报错
       p.speak("呵呵"); 
       System.out.println(number); //speak:呵呵
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1. 获取Class对象
        //2. 获取方法 .getMethod
        //3. 调用方法 .invoke
        Person p = new Person();
        Class<?> clazz = p.getClass();
        /*
        *   Method getMethod(String name, Class<?>... parameterTypes)
        *   1 .参数
        *        1. name 方法名
        *        2. name 方法名的参数列表
        *   2. 返回值: 获取的这个方法
        */
        Method method = clazz.getMethod("speak", String.class);
        /*
        *   Object invoke(Object obj, Object... args)
        *   1. 参数
        *       1. obj : 调动此方法的对象,speak前加一个static,null也可以
        *       2. args : 调用此方法传入的实参
        *   2. 返回值: 调用此方法产生的返回值,如果此方法没有返回值,将会null
        * */
        Object result = method.invoke(p, "嘻嘻");
        //Object result = method.invoke(null, "嘻嘻"); //speak方法设为静态,不报错
        System.out.println(result); //speak:嘻嘻
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person p = new Person();
        Class<? extends Person> clazz = p.getClass();
        Method show = clazz.getDeclaredMethod("show", int.class); //暴力反射   
        show.setAccessible(true); //临时修改权限
        show.invoke(p,15); //show:15
    }
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Field;
//属性的反射一般不用,关于属性值设置通过反射get和set方法,又回到了方法的反射
public class FieldDemo {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "啦啦";
        System.out.println(p); //Person{name='啦啦',age=0}
    }

//111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test  //下面写那么多代码为了实现上面3行
    public void method01() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
        //1.获取Class对象
        //2.获取属性对象
        //3.操作属性,get / set
        Class<?> clazz = Class.forName("com.itheima03.reflect.Person");
        Object p = clazz.newInstance();
        /*
        * Field getField(String name)
        *   1. 参数: 属性名
        *   2. 返回值: 通过属性名找到的属性对象
        */
        Field age = clazz.getDeclaredField("age");
        age.setAccessible(true);
        /*
        * set(Object obj, Object value) 
        *   1. 设置属性的对象
        *   2. 设置的属性值
        */
        age.set(p,18); //很少用,一般get和set方法,又回到方法的反射
        System.out.println(p);//Person{name='null',age=18}
    }
}

28.类加载器:.class.getClassLoader(),引扩应,双亲委派

package com.itheima01.loader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
/*
*       0. 类  .java文件   -> .class文件  -> Class对象(内存中)
*                源码           编译后         运行时
*               当.class文件被加载进内存,JVM会在堆中创建一个Class对象
* 
*       1. 类加载器 : 将.class文件加载进内存, 随之产生Class对象,看成输入流(读档)
* 
*       2. 三种类加载器:加载不同包下的类,像前面讲的不同的输入流
*           1. 引导类加载器 bootstrap (用c写的)。核心包下的类(rt.jar)
*           2. 扩展类加载器 extension。ext包下
*           3. 应用/系统 类加载器 application。 第三方编写的类
* 
*       Class对象.getClassLoader(); // 可以获取加载这个Class对象的类加载器
*
*   补充:  1. 三种类加载器存在继承关系的【不叫继承 (叫组合 composition,因为引导类加载器用C写的)】
*          2. 为什么一般情况下, 一个.class文件的Class对象只会有一个?
*             类加载器: 双亲委派机制(这机制保证一个类只会被一个类加载器加载,双亲:父类的父类)
* 
*            什么情况下, 一个.class文件的Class对象会有多个?
*            多个类加载器(程序员自定义类加载器,手动操作类加载器去加载)去加载同一个.class文件
*/
public class ClassLoaderDemo {
    public static void main(String[] args) {
        ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();        
        System.out.println(loader1); //sun.misc.Launcher$AppClassLoader@18b4aac2 //应用类加载器
        
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        Class clazz = DNSNameService.class; // Ext扩展包下       
        ClassLoader loader2 = clazz.getClassLoader();
        System.out.println(loader2);  //sun.misc.Launcher$ExtClassLoader@45ee12a7 //扩展类加载器
        
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        ClassLoader loader3 = String.class.getClassLoader();
        System.out.println(loader3); // null ,因为不是用java写的。 //引导类加载器
    }    
    @Test
    public void method01(){  //三种类加载器的关系:继承关系,组合关系,因为跨语言了
        ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
        System.out.println(loader1);//sun.misc.Launcher$AppClassLoader@18b4aac2 //应用 
              
        ClassLoader loader2 = loader1.getParent(); //获取父加载器
        System.out.println(loader2);//sun.misc.Launcher$ExtClassLoader@452b3a41 //扩展
              
        ClassLoader loader3 = loader2.getParent();  
        System.out.println(loader3);//null //引导 (父父)
    }
}

29.设计模式:动态代理比普通代理的好处在于不用事先定义类,代理类在运行时动态生成 (反射)

29.1 动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)

package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/*
*  动态代理: 动态: 代理中介类不是静态定义,是动态生成的
*     1. 静态定义: 把这个类写死了即class ZhongJie
*     2. 动态生成: 这个类(中介类)压根就没写,在运行时由JVM动态生成(肯定涉及反射)。前提: 必须有接口 
*/
public class DynamicDemo { // Dynamic反义词static
    public static void main(String[] args) {
        FangDong fd = new FangDong(); // 先看上房东房子        
        /*
         static Object newProxyInstance(ClassLoader loader, //这个方法最终目的: 创建一个中介类对象!
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
              1. loader : 类加载器(加载中介类的),这个中介类不存在,需要JVM动态生成
                    一般是应用类加载器(第三方类)。 直接 被代理类(房东类) 的类加载器
                    
              2. interfaces : 接口的Class对象(让中介类实现的)
                    就是 被代理类(房东类) 所实现的接口

          class对象在生成期间需要一个类加载器(将字节码文件加载生成class对象),上面1。
          这个中介类需要实现接口从而拥有被代理类方法,上面2。
          接下来需要重写接口里方法,下面3。
  
              3.InvocationHandler  h  : 调用处理器 (InvocationHandler是一个接口)
                   invoke方法可以看成中介类对接口所有方法的重写
        */
        ClassLoader loader = fd.getClass().getClassLoader();  //对应上面1      
        Class<?>[] interfaces = fd.getClass().getInterfaces();//获取此类实现的所有接口,对应上面2        
       // Class<?>[] interfaces = {Fd.class};  //效果同上行
       // Class<?>[] interfaces = {com.itheima03.dynamic.Fd.class}; //效果同上行 
       
// 实现接口后要重写方法,重写不了,因为中介类运行时才会存在,所以需要上面的3。
// System.out.println(loader); //sun..$APPClassLoader.. //应用类加载器
// System.out.println(Arrays.toString(interfaces)); //[interface.itheima03.dynamic.Fd] //就是FD接口

        InvocationHandler h = new InvocationHandler() {  //h是InvocationHandler接口实现类对象,只有这样的h才能传入Proxy.newProxyInstance这个方法
            /*
            *   invoke函数参数: 1. proxy : 当前代理类对象(几乎没用,因为下面有Fd proxy)
            *                2. method: 代理类对象当前调用的方法
            *               3. args: 代理类对象当前调用方法传入的参数列表
            */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName());
                System.out.println(Arrays.toString(args));
                return null;
            }
        };
        
//11111111111111111111111111111111111如上是Proxy.newProxyInstance方法的三个参数获取
         Fd proxy = (Fd) Proxy.newProxyInstance(loader, interfaces, h); //这行=号后面相当于new MyProxy 
//如上行平时MyProxy proxy = new MyProxy(.) ,因为MyProxy是JVM随机命名,所以上行用Fd(这个Fd是FangDong类下面定义的)
		proxy.zufang(1001); //调用h.invoke即本文件上面 ,父类引用h,子类new InvocationHandler()
        //proxy.maifang(2001); //接口回调(callback)(接口形式的多态):相当于直接调用本文件上面invoke方法
    }
}

package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class Demo02 {
    public static void main(String[] args) {
        FangDong fd = new FangDong(); //先看上房东房子
        ClassLoader loader = fd.getClass().getClassLoader();
        Class<?>[] interfaces = fd.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();                
                if("zufang".equals(name)){
                    int money = (int) args[0]; // Object[] param = {money};
                    if(money > 1000){
                        fd.zufang(money);
                    }else{
                        System.out.println("钱不够...");
                    }                    
                }/*else if("maifang".equals(name)){
                }*/else{ 
                    method.invoke(fd,args); //中介类.其他9000个方法,走这行,即其他9000个方法依然交给房东fd自己处理
                }
                return null;
            }
        };

//1111111111111111111111111111111111111111111111111111111111111111111111111111111
        Fd proxy = (Fd) Proxy.newProxyInstance(loader,interfaces,h);
        proxy.zufang(888);  // 被代理对象(房东) 有 10000万个方法,你只想修改其中一个
//        proxy.maifang(3000);
    }
}

29.2 模板:类里有抽象方法必须抽象类

package com.atguigu.test02.abstract_;
// 编写一个类,包含一个方法,可以统计 你执行任意代码的运行时间。
public class TestTemplate {
	public static void main(String[] args) {
		MyCalTime my = new MyCalTime();
		long time = my.getTime();				
		System.out.println("耗时:" + time + "毫秒");
	}}

//111111111111111111111111111111111111111111111111111111111111111111
abstract class CalTime{ 	
	public final long getTime(){//可以计算任意一段代码的运行时间 //这里加final的目的是不希望子类重写,改写我的算法的结构
		long start = System.currentTimeMillis(); //(1)获取开始时系统时间	 			
		doWork();                                //(2)执行xxxx			
		long end = System.currentTimeMillis();	 //(3)获取结束时系统时间							
		return end - start;          //(4)计算时间差
	}	
	protected abstract void doWork(); //protected的目的,希望只是子类中进行访问和重写
 }

//11111111111111111111111111111111111111111111111111111111111111111
class MyCalTime extends CalTime{
	@Override
	protected void doWork() { //重写抽象方法
		long sum = 0;
		for(int i=1; i<=100000; i++){
			sum += i;
		}
		System.out.println("sum = " + sum);
	}	
}

29.3 单例:某个类只能有唯一的一个实例对象

package com.atguigu.test17;import org.junit.Test;

public class Test17 {
	@Test
	public void test1(){
		SingleEnum s1 = SingleEnum.INSTANCE;
		SingleEnum s2 = SingleEnum.INSTANCE;
		System.out.println(s1 == s2); //true 
	}	
	
	@Test
	public void test2(){
    // SingleEnum.test();
    //此时我并没有需要用到这个INSTANCE对象,但是它也创建出来SingleEnum对象,单例恶汉式
	}	
	
	@Test
	public void test3(){
		SingleClass s1 = SingleClass.INSTANCE;
		SingleClass s2 = SingleClass.INSTANCE;
		System.out.println(s1==s2); //true,地址一样,只有一个对象。
	}
		
	@Test
	public void test4(){
		Single s1 = Single.getInstance();
		Single s2 = Single.getInstance();
		System.out.println(s1 == s2); //true
	}	

	@Test
	public void test5(){
		LazyClass s1 = LazyClass.getInstance(); //getInstance()里必有new
		LazyClass s2 = LazyClass.getInstance();
		System.out.println(s2 == s1);
	}

	LazyClass s1;
	LazyClass s2;	
	@Test
	public void test6(){
		//匿名的内部类,继承Thread类。=后面是子类,然后多态
		Thread t1 = new Thread(){
			public void run(){
				s1 = LazyClass.getInstance();
			}
		};		
		Thread t2 = new Thread(){
			public void run(){
				s2 = LazyClass.getInstance();
			}
		};		
		t1.start();
		t2.start();		
		try {
		//这里用join的目的是,为了两个子线程都执行完,再执行主线程的System.out.println(s1);
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
		System.out.println(s1);
		System.out.println(s2);
		System.out.println(s1 == s2);
	}	}
	
//11111111111111111111饿汉式: 不管我们使用者是否需要这个对象,它都上来先给你创建好这个唯一的对象。
//形式一:对应test1()
enum SingleEnum{
	INSTANCE; //单例,枚举只有一个   
    //public static void test(){ //用这个方法不用上面这个INSTANCE对象,对应test2() }
    }
	
//形式二:对应test3() 
class SingleClass{ //1.5之前老版的枚举 //用一个全局的静态的常量,来保存这个唯一的实例对象
	public static final SingleClass INSTANCE = new SingleClass();
	private SingleClass(){		
	}}
 
//形式三:对应test4()
class Single{  
    //用一个私有的静态的常量,来保存这个唯一的实例对象
	private static final Single INSTANCE = new Single(); 
	private Single(){		
	}
	public static Single getInstance(){ //提供一个静态方法,来返回这个常量对象
		return INSTANCE;
	}}
	
//111111111111111111111懒汉式: 延迟创建对象。当使用者来或者这个对象,要用到对象时,我再创建。
//形式一: 对应test6()
class LazyClass{ 
	private static LazyClass instance; //不加final,可以不用new出来  //加final必须new SingleClass()
	private LazyClass(){		
	}	
	public static LazyClass getInstance(){ 
		if(instance == null){//提高效率,已创建对象就没必要再进去,直接最后return instance; 未创建对象进入
			synchronized(LazyClass.class){ //当前类的Class对象,静态方法不能出现this
				if(instance == null){//安全判断
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					instance = new LazyClass();
				}
			}
		}
		return instance;
	}	

//形式二:内部类
class Lazy{
	private Lazy(){}	
	private static class Inner{
		public static final Lazy INSTANCE = new Lazy();//在内部类中,创建外部类的唯一对象
	}	
	public static Lazy getInstance(){ 
		return Inner.INSTANCE; //用到getInstance方法才会new Lazy(),也是懒汉式
	}
 }

29.4 工厂:工厂类(一个工厂),工厂接口(一车一工厂),clazz.newInstance()

package com.atguigu.test12;import org.junit.Test;
//1、简单工厂模式:优点:代码比较简洁。缺点:如果增加新的产品类型(奥迪),需要修改工厂类中getCar()。
//违反了面向对象的一个开发原则:对扩展开放,对修改关闭。public class TestFactory {
	@Test
	public void test03(){
		Car c = SimpleFactory2.getCar("奔驰");
		c.run();
	}		
		
	@Test
	public void test02(){
		Car c = SimpleFactory.getCar(); //静态,类名. 不用创建工厂类对象
		c.run();//这里也是运行宝马的run(),但是从头至尾没有出现BMW类,解耦合
	}		
		
	@Test
	public void test01(){  //没有工厂,如下创建对象和使用对象是同一个人完成		
		BMW b = new BMW();	//比如spring容器,new时要初始化很多配置信息才创建一个对象		
		b.run(); //使用对象,调用方法
	}}
	
//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Car{  //对应test01()
	void run();
	}	
	
class BMW implements Car{
	@Override
	public void run() {
		System.out.println("宝马让你在车里面哭");
	}	
}	

class Benz implements Car{
	@Override
	public void run() {
		System.out.println("奔驰让你在车盖上哭");
	}
}

class Audi implements Car{ //多了一类车,下面工厂类需要再改变,这就是简单工厂模式
	@Override
	public void run() {
		System.out.println("奥迪让你在...");
	}
}

//1111111111111111111111111111111111111111111111111111111111如下工厂类  
class SimpleFactory{ //对应test02()
	public static Car getCar(){
		return new BMW();
	}
}

class SimpleFactory2{ //对应test03()
	public static Car getCar(String type){
		if("宝马".equals(type)){
			return new BMW();
		}else if("奔驰".equals(type)){
			return new Benz();
		}
		return null;
	}
}
package com.atguigu.test12;
/*
 * 2、工厂方法模式:(1)为了生产对象与使用对象分开 (2)如果增加新产品,就不需要修改原来的工厂类
 * 优点:遵循了增加新产品,不修改原来的类的原则。 缺点:类太多了,一种车一个工厂
 */
public class TestFactory2 {
	public static void main(String[] args) {
		BaoMaFactory bf = new BaoMaFactory();
		Che c = bf.getChe();
		c.run();
	}
}

//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Che{  
	void run();
	}

class BaoMa implements Che{
	@Override
	public void run() {
		System.out.println("宝马");
	}
}

class BenChi implements Che{
	@Override
	public void run() {
		System.out.println("奔驰");
	}
}

class AoDi implements Che{
	@Override
	public void run() {
		System.out.println("奥迪");
	}
}

//1111111111111111111111111111111111111111111111111111111111111如下工厂接口
interface GongChang{
	Che getChe();
	}
	
class BaoMaFactory implements GongChang{ 
	@Override
	public Che getChe() {
		return new BaoMa();
	}	
}

class BenChiFactory implements  GongChang{ 
	@Override //只需要增加一个工厂类,专门生产奥迪车,不动上面代码
	public Che getChe() {
		return new BenChi();
	}	
}  

如下用反射结合上面的简单工厂和工厂方法:

package com.atguigu.test12;

public class TestFactory3 {
	public static void main(String[] args)throws Exception {
		Vehicle c = SimpleFactory3.getVehicle("com.atguigu.test12.QQ");
		c.run();   //qq车	
		Vehicle c2 = SimpleFactory3.getVehicle("com.atguigu.test12.Aoto");
		c2.run();  //奥拓
	}
}

//11111111111111111111111111111111111111111111111111111111111111111中间没变
interface Vehicle{    
	void run();
}

class QQ implements Vehicle{
	@Override
	public void run() {
		System.out.println("qq车");
	}	
}

class Aoto implements  Vehicle{
	@Override
	public void run() {
		System.out.println("奥拓");
	}	
}

//1111111111111111111111111111111111111111111111111111111111111111如下工厂类
class SimpleFactory3{
	public static Vehicle getVehicle(String className)throws Exception{
		Class clazz = Class.forName(className);
		return (Vehicle) clazz.newInstance(); //clazz.newInstance()返回Object强转为Vehicle类
	}
}

30.约束:word/txt(和xml一样都没有格式约束)传输数据,但xml格式规范

30.1 DTD约束:配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!--  如上行文档声明 : 版本 和 编码 -->
<!--
    xml基本语法:
        1. 标签分类 :
              1. 成对标签 :  start tag  -  内容 - end tag
                               开标签             闭标签
                              <标签名>           </标签名>
              2. 单独标签 : 自关闭标签 <标签名/>           
        2. xml有且仅有一个根标签 root tag (第一个标签,xml中的所有内容都在内)
        3. 标签可以拥有属性的(开标签和自关闭标签)  key=value 即 id="003"
 
        实体字符(转义字符):
                <  ->   &lt;    less than即缩写lt
                >   ->  &gt;    greater than
                &lt; &gt; 小于或大于就是不等于,前端支持,java不支持
                
        CDATA: unparsed character data 不解析的字符数据
        在这里的内容是不会被解析器所解析(看成注释)
-->
<list>
    <![CDATA[" if(x < > 1){ sout: xx} "]]>
    <hehe id="003"/>
        <book id="001">
            <name>java基础入门</name>
            <desc>人生必读第一本书</desc>
            <price>998</price>
            <extra>if(x &lt; &gt;  1){ sout: xx}</extra>
        </book>
        <book id="002">
            <name>java从入门到放弃</name>
            <desc>人生的最后一本</desc>
            <price>11</price>
        </book>
</list>
<?xml version="1.0" encoding="UTF-8" ?>
<!--
    可扩展: 开发者随意命名标签
          存数据  为了 以后 取数据(解析 parse)
          问题:会给数据解析带来麻烦
          解决: 约束(规则,规范)给xml指定约束, 其内部拥有什么内容,以及内容的编写格式都有要求
          规则: list里面必须是book标签(多个)
                book标签里还可以有name desc price等标签
                这些标签,里面有文本内容
                
          两种约束: DTD:  document type definition 文档类型定义 (了解)
                   Schema : 模式 (了解) 替代DTD,全称(XML Schema Definition,XSD)
-->
<?xml version="1.0" encoding="UTF-8" ?>
<!--
    DTD约束: 1. 根标签 必须是list
           2. list里有book (+ : 表示1个或多个book标签)
          3. book里有name desc price
          4. name desc price = PCDATA(parsed character data) 可以包含文本
          5. book 有一个属性 id 默认值 为 999
          如下写完约束后,会自动提示
-->
<!DOCTYPE list [
        <!ELEMENT list (book+)>
        <!ELEMENT book (name,desc,price)>
        <!ELEMENT name     (#PCDATA)>
        <!ELEMENT desc     (#PCDATA)>
        <!ELEMENT price     (#PCDATA)>
        <!ATTLIST book id CDATA "999">
        ]>

<!--111111111111111111111111111111111111111111111111111111111111111-->        
<list>
    <book id="001">
        <name>java基础入门</name>
        <desc>人生必读第一本书</desc>
        <price>998</price>
    </book>
    <book id="002">
        <name>java基础入门</name>
        <desc>人生必读第一本书</desc>
        <price>998</price>
    </book>
</list>

30.2 Schema约束:命名空间

//05_book.xsd
<?xml version='1.0' encoding='UTF-8' ?>
<xs:schema xmlns:xs='http://www.w3/2001/XMLSchema'
        targetNamespace='http://www.itheima'
        elementFormDefault="unqualified">
    <!--
    	上面几行就是命名空间技术,需要被另一个文件引用即06_book.xml引用才可以生效。
    	schema约束: 约束规则用xml语法来书写的。
        element : 元素(开标签到闭标签之间的内容,我们统称为标签)
        complexType : 复杂类型(但凡元素有子标签或属性)
        sequence : 序列 (子元素的序列)
        maxOccurs: 最大出现次数,如下书标签最少出现一次,至多随意
    -->

<!--11111111111111111111111111111111111111111111111111111111111111111111111111111-->  
    <xs:element name='书架' >
        <xs:complexType>
            <xs:sequence maxOccurs='unbounded' minOccurs="1">
                <xs:element name='' >
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name='书名' type='xs:string' />
                            <xs:element name='作者' type='xs:string' />
                            <xs:element name='售价' type='xs:string' />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
//06_book.xml调用05_book.xsd
<?xml version="1.0" encoding="UTF-8"?>
<itheima:书架 xmlns:itheima="http://www.itheima"
            xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.itheima 05_book.xsd">

<!--11111111111111111111111111111111111111111111111111111111111111111111111-->  
    <>
        <书名>呵呵</书名>
        <作者>啦啦</作者>
        <售价>嘻嘻</售价>
    </>
</itheima:书架>

如下xml用到在线的xsd,自动补全。

1. ArrayList底层是数组,默认长度为10,扩容系数1.5即下次15,ArrayList查询复杂度为o(1)。LinkedList底层双向链表,好处不用进行扩容,一直往后追加,坏处是要找第n个元素时间复杂度是o(n)。JDK -> JVM -> glibc(系统调用的封装)->Linux内核

2. HashMap底层也是数组,数组大小必须是2的幂次方倍【确保数组中的每一个位置发生hash冲突的概率相同】。LinkedHashMap底层和HashMap一样,只不过每个元素有一个指针指向下一个插入的元素,有序了。TreeMap底层红黑树实现,特点是按照key进行排序。HashTable线程安全(读写整个数组结构加了syn修饰即加了锁)(HashMap线程不安全,两者底层存储结构一样)。锁住整个结构代价大,所以出现了concurrent HashMap,只锁住数组的一个桶。HashSet将HashMap的value设为null。

31.nginx的conf文件:以前网络编程中B/S架构中服务器是用socket写,用文件输入流读一个文件,读到后socket通过outputstream写出去,这些过程有了nginx后再也不用写

nginx软件链接:https://pan.baidu/s/1zvF2irI6OenDKVfvSIOZmg ,提取码:gsn9。

如果http协议是80端口的话,80端口会被隐藏,本机浏览器输入http://localhost:80启动服务端显示如下,hello nginx是E\my81\index.html里内容。本机ipconfig显示192.168.33.71。上面红框浏览器(客户端)是别人的电脑【局域网下同一网段】,上面两个大框是服务端,浏览器和服务器三要素对应。

nginx作用:1.反向代理:s被代理1个公网ip,但有n台s对外服务。
2.负载均衡:基于反向代理。
3.正向代理:我们电脑接入VPN 后,我们对外IP地址就会变成VPN服务器的公网IP。
4.动静分离:网页f12显示js,css,img属静态数据。

31.1 nginx反向代理与负载均衡:nginx是被广泛使用的网页服务器,在www.163的F12的Network中Headers中有server:nginx,使用nginx首当其冲,做反向代理,将请求分发到公司的多台服务器上

原生nginx没有集成很多插件,使用不方便。推荐使用openresty(在nginx基础上集成了很多lua写的插件),官网下载openresty后解压后进一级目录输入nginx.exe回车运行服务端,浏览器输入localhost默认:80端口

31.2 location匹配方式:location / 默认匹配所有请求,相当于第四优先级(最弱)

1.如下最强级别=。

2.如下优先级第二,^~以什么开头。如下/是路径,不是转义符。

3.如下优先级第三,正则表达式~\w匹配数字、字母、下划线,转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\。如下/是路径。

31.3 反向代理写法:http80


31.4 负载均衡写法:基于反向代理

32.服务器:软件为tomcat或nginx,程序为servlet


nginx不支持java规范,tomcat支持。tomcat8:https://tomcat.apache/download-80.cgi。免安装,解压即可用:链接:https://pan.baidu/s/1UJM9kbIHGIXkNXlYLpcoGw ,提取码:g610。dos系统识别文件后缀名不能超过3位(先有dos后有windows),所以.htm。8080一般是用来连接代理的(8080不能省,只有80才能省)。 只有index.html是默认的,不用加在最后,localhost:8080/a.html。

如下javaee新建项目,apache-tomcat8…是前面tomcat软件解压路径。如下不需要自己开启tomcat,idea配置自动开启。


如下New和上面一样,关联tomcat软件解压路径。

如下将web文件夹当成前端static web项目,web文件夹里都可以访问,除了web-info无法访问。


如下是点乌龟运行后,浏览器自动打开的原理。改变路径 / 写法。若还是不会默认打开谷歌,需要File - Settings - Tools - Web Browsers。

33.servlet:多态,web.xml



http的request对象:由tomcat创建,并且里面数据由tomcat set进去,我们只需要get出来。

File-New-Project-Java Enterprise。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<!--111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
        <!--
            http协议两种主要请求协议
            1. get (默认, 只要看到可以发起请求,但是没看到请求方式设置)
            2. post (需要收到设置)
        -->
        <h1>get请求方式</h1>
        <form action="http://localhost:8080/MyServlet" method="get">
            <input type="text" placeholder="请输入用户名" name="name"> <br>
            <input type="text" placeholder="请输入密码" name="pwd"> <br>
            <input type="submit">
        </form>

        <h1>post请求方式</h1>  
        <form action="/MyServlet" method="post">    <!-- 本服务器前面可以省略 -->
            <input type="text" placeholder="请输入用户名" name="name"> <br>
            <input type="text" placeholder="请输入密码" name="pwd"> <br>
            <input type="submit">
        </form>
</body>
</html>
package com.itheima01.http;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
 *  http://192.168.139.236:8080/MyServlet 或 MyServlet换成..请求报文.html
 *  # request对象核心功能: 获取前端的请求数据如下:
 *  1. 请求行
 *      请求方式 / 请求url / 协议
 *		方法:
         1. String getMethod() : 获取请求方式的类型
         2. StringBuffer getRequestURL() : 获取客户端发出请求完整URL
         3. String getProtocol(): 获取当前协议的名称和版本   
         4. String getRemoteAddr() : 获取IP地址
 *
 *  2. 请求头
 *      	1. 获取指定请求头的信息: value = request.getHeader("name");
            2. 获取所有的请求头的name值:request.getHeaderNames();
 *
 *  3. 请求参数
 *      tomcat: 会根据不同的请求方式(自动get请求从url获取参数,post请求从请求体里获取参数),从不同的地方获取参数并设置到request对象,这样不需要知道过程,只有结果。
 **/
@WebServlet(urlPatterns = "/MyServlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //doGet方法相当于get请求方式下的service方法
//        System.out.println("服务器被访问了");  //idea下面显示
//        response.getWriter().print("hello http");  //在网页上输出

//        line(request); //请求行

        String agent = request.getHeader("user-agent"); //出bug执行的
        //用户访问我的网站突然崩溃,实际已获取了用户环境,在服务器所在地方可以模拟用户
        String referer = request.getHeader("referer"); //防盗链
        System.out.println(agent);
        System.out.println(referer);

//11111111111111111111111111111111111111111111111111111111111111111111111111111111
        HashMap<String, String> map = new HashMap<>();
        Iterator<String> it = map.keySet().iterator();
        while(it.hasNext()){
            String key = it.next();
            String value = map.get(key);
        }  
                
        //如下枚举Enumeration相当于上面迭代器      
        Enumeration<String> it2 = request.getHeaderNames(); // =号右边等价于map.keySet().iterator()
        while(it2.hasMoreElements()){
            String name = it2.nextElement();
            String value = request.getHeader(name);
            System.out.println(name + "->" + value);
        }
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111
    private void line(HttpServletRequest request) {  //请求行,抓包
        String method = request.getMethod();
        StringBuffer url = request.getRequestURL();
        String protocol = request.getProtocol();
		String remoteAddr = request.getRemoteAddr();		 
        System.out.println(method);
        System.out.println(url.toString());
        System.out.println(protocol);       
        System.out.println("访问者的ip:" + remoteAddr);
    }
}


如上浏览器网址中…报文.html是Referer中的上一次访问页面,因为已跳转到如下下个页面。

如下请求行中的请求url省略了如上的http://localhost:8080。cookie是浏览器缓存的一种。get请求中最后一行不是请求体,是谷歌浏览器工具抓包进行的参数强调渲染。

34.request对象:.getParameterMap(),post请求中文参数乱码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<!--111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
        <h1>get请求</h1>
        <form action="/ParamServlet" method="get">
            用户名: <input type="text" name="name"> <br>
            密码: <input type="password" name="pwd"> <br>
            性别: <input type="radio" name="gender" value="boy"><input type="radio" name="gender" value="girl"><br>
            爱好:
            <input type="checkbox" name="hobby" value="smoke"> 抽烟
                <input type="checkbox" name="hobby" value="drink"> 喝酒
                    <input type="checkbox" name="hobby" value="firehead"> 烫头 <br>
            <input type="submit">
        </form>

        <h1>post请求</h1>
        <form action="/ParamServlet" method="post">
            用户名: <input type="text" name="name"> <br>
            密码: <input type="password" name="pwd"> <br>
            性别: <input type="radio" name="gender" value="boy">
            <input type="radio" name="gender" value="girl"> <br>
            爱好:
            <input type="checkbox" name="hobby" value="smoke"> 抽烟
            <input type="checkbox" name="hobby" value="drink"> 喝酒
            <input type="checkbox" name="hobby" value="firehead"> 烫头 <br>
            <input type="submit">
        </form>
</body>
</html>
package com.itheima02.param;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/**
    请求参数: parameter   ?n1=v1&n2=v2...
    方法:
     1. 根据name值获取value值: String value = request.getParameter(name);
     2. 根据name值获取多个value值:String[] value = request.getParameterValues(name);
     3. 获取所有的请求参数,封装到map中:Map<String, String[]>  map = request.getParameterMap();
     								  name为String,value为String[]

    问题: post请求中文参数乱码
    原因: 编解码使用的字符集不一样
          编码:  浏览器 ( utf-8 )
          解码:  服务器 (tomcat + servlet)
                       ISO-8859-1   utf-8

    解决:  tomcat的编码表改成utf-8
    1. 永久
    2. 临时 (选用)  request.setCharacterEncoding("utf-8");  注意: 必须放在获取参数之前
	  【get请求 tomcat8以上被优化 不乱码(post请求参数在请求体中,走io流)】
 **/
@WebServlet(urlPatterns = "/ParamServlet")
public class ParamServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        doGet(request, response);
    }     
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//       request.setCharacterEncoding("utf-8");
        String name = request.getParameter("name");
        String pwd = request.getParameter("pwd");
        String gender = request.getParameter("gender");
        String[] hobbies = request.getParameterValues("hobby"); //复选框可重复
        System.out.println(name+"," + pwd + "," + gender + "," + Arrays.toString(hobbies));
        
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
        Map<String, String[]> map = request.getParameterMap();
//        System.out.println(map); //这个map没有重写tostring方法
        Set<String> set = map.keySet();
        for (String key : set) {
            String[] value = map.get(key); //value数组没有重写tostring方法,不能
//            System.out.println(key + "=" + Arrays.toString(value));
        }
    }
}


如下选中的是map遍历出来的结果和下面第一行一样。

34.1 请求转发:request表面上获取请求数据,还有快递员身份


如下forward方法中request是邮件及内容,response是回应权限。A模块指MyServlet,B模块指ZhouServlet。一次请求链如下只有一去一回。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<!--111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>        
        人事
        <!--<a href="http://localhost:8080/ShiServlet?msg=有人来面试">发送邮件</a>-->
        <a href="/ShiServlet?msg=有人来面试">发送邮件</a>  <!--点发送邮件后,地址栏修改为/Shi..,没写就是get请求-->
</body>
</html>
package com.itheima03.transfer;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/ShiServlet")
public class ShiServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 接收请求
        String msg = request.getParameter("msg");
        System.out.println("shi:" + msg);
        System.out.println("shi:我现在没空,叫周楠同学帮我面试");            
        request.setAttribute("extra","比我帅的不要~~");  //添加备注,这行注掉不写,ZhouServlet写request.getAttribute会得到null
        
        //2. 请求转发
//        RequestDispatcher dispatcher = request.getRequestDispatcher("/ZhouServlet"); //比作邮件服务器或快递员
//        dispatcher.forward(request,response); //快递员发货
        request.getRequestDispatcher("/ZhouServlet").forward(request,response); //这行等价上面两行
    }
}
package com.itheima03.transfer;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/ZhouServlet")
public class ZhouServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  //request和response两个参数由A模块传递来
        //1. 接收请求
        String msg = request.getParameter("msg");
        System.out.println("zhou:" + msg);                
        Object extra = request.getAttribute("extra");
        System.out.println("zhou:" + extra);
        //2. 业务处理
        System.out.println("周楠同学放下手机,难得做点事情: 面试");
        //3. 响应数据
        response.getWriter().print("this boy is ok,about 3k~~");
    }
}


34.2 登陆案例:成功和失败页面相当于两个模块

//JdbcUtil.java 
package com.itheima.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcUtil {
    private static ComboPooledDataSource ds = new ComboPooledDataSource(); //先new一个连接池
    public static JdbcTemplate getTemplate(){
        JdbcTemplate template = new JdbcTemplate(ds); //再new一个Template,把连接池交给给JdbcTemplate管理
        return template;
    }
}
//LoginServlet.java
package com.itheima.login;
import com.itheima.bean.User;
import com.itheima.utils.JdbcUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.接收请求
        String username = request.getParameter("userName"); //userName区分大小写
        String password = request.getParameter("password");

        //2.业务处理  //如下只有两种结果: 返回一行(一行是一个对象) 或 返回null
        String sql = "select * from user where username = ? and password = ?";
        JdbcTemplate template = JdbcUtil.getTemplate(); //一定要封装成JdbcUtil,不然new ComboPooledDataSource,每访问一次就会创建一次连接池,内存崩。
        User user =  template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), username, password); 
        
        //把如下注掉,用如上一行,即查不到数据,抛出异常,不抓异常,这样会密码错误直接跳转500(500服务器错误,404地址输错),而不是error.html               
        /*
        User user = null;
        try {
           user = template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), username, password);
        } catch (DataAccessException e) {
            e.printStackTrace();
        }*/

        if(user != null){
            request.getRequestDispatcher("/success.html").forward(request,response); //登录成功
        }else{
            request.getRequestDispatcher("/error.html").forward(request,response);
        }
    }
}
// User.java
package com.itheima.bean;

public class User {
    private Integer id; //和表字段一致
    private String username;
    private String password;
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
//login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录页面</title>
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/login.css" rel="stylesheet">
    <script src="js/jquery.js"></script>
    <script src="js/bootstrap.js"></script>
</head>

<!--1111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
    <div class="container text-center">
        <form class="form-signin" action="/LoginServlet">
            <h2 class="form-signin-heading">登录页面</h2>
            <input type="text"  name="userName" class="form-control" placeholder="用户名" required autofocus>
            <input type="password"  name="password" class="form-control" placeholder="密码" required>
            <button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
        </form>
    </div>
</body>
</html>

35.response对象:text/html,重定向(.setStatus,.setHeader)



package com.itheima01.http;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(urlPatterns = "/MyServlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException                      {
        doGet(request, response);
    }        
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("服务器被访问了");
//        int i = 1/0; //服务器崩了出异常,没有try catch,状态码500        
        response.setStatus(302); //状态码设置,只有302需要我们手动设置
//      response.setStatus(404); //这样设置状态码是404,servlet优先级高于tomcat,但是访问服务端依旧成功
//        response.getWriter().print("hello response"); //前端网页打印出

        //响应体输出流设置,字节输出流(写)  //待会下载文件案例中使用                
//       ServletOutputStream os = response.getOutputStream();
//       os.write("abc".getBytes()); //01_响应报文.html中点击“访问MyServlet”超链接跳转到显示“abc”的新页面

		//方便输出字符数据,不用转换为字节了,print和write一样
//       PrintWriter writer = response.getWriter();
//       response.getWriter().print("xx");  
    }
}

请求报文分get和post两种,在响应报文里看到了非servlet设置的内容,那就是tomcat设置的。将前端改为href=“/MyServlet123”,则会出现404。servlet关了,tomcat自动设置为500状态码。

如下体中hello response长度一共为Content-Lenth为14,头中GMT格林位置需+8小时为东八区北京时间。如下行和头都是tomcat默认自动设置,体是我们servlet手动设置。想修改状态码,需要在servlet(对我们暴露的小程序)中修改。





如下点击后,5秒后跳转到百度。

package com.itheima02.header;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/RefreshAndTypeServlet")
public class RefreshAndTypeServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("服务器被访问了");
//        response.setHeader("refresh","5;http://www.baidu");

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        /*
        *   问题: 响应体中文乱码 。request.setCharacterEncoding("utf-8");
        *   原因:  编码: 服务器(utf-8 : servlet:因为在servlet里写的"哈哈",idea右下角显示utf-8)
        *          解码: 浏览器(iso-8859-1 : tomcat默认设置如下行)     
  // response.setHeader("content-type","text/plain");  //在Response Headers(浏览器F12看出)中多出tomcat默认设置(不加这行不显示):Content-Type: text/plain;charset=ISO-8859-1

        *   解决: servlet中手动设置覆盖tomcat默认设置
        *
        *    windows系统:      txt        html          jpg          avi     ...
        *    早期MIMEType :  text/plain   text/html     image/jpeg (tomcat解压包中web.xml中有对应说明)
        */
//        response.setHeader("content-type","text/plain;charset=utf-8"); //新页面显示 <h1>哈哈</h1>
//        response.setHeader("content-type","text/html;charset=utf-8");  //点击网页上RefreshAnd..跳转新页面显示 哈哈
        response.setContentType("text/html;charset=utf-8"); //setContentType是简易API,同上一行
        response.getWriter().print("<h1>哈哈</h1>"); 
    }
}

因为重定向不是一次请求链,所以超过域对象,不能用request传递数据。ZhouServlet收不到人事的msg,能收到ShiServlet的response.setHeader(…/ZhouServlet?msg=…)。

package com.itheima03.redirect;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/ShiServlet")
public class ShiServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        doGet(request, response);
    }        
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 接收请求
        String msg = request.getParameter("msg");
        System.out.println("shi:" + msg);
        //2. 重定向
        response.setStatus(302);
        response.setHeader("location","/ZhouServlet?msg=xx"); //不写?msg=xx,ZhouServlet就收不到msg,写了就能收到
    }
}
package com.itheima03.redirect;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/ZhouServlet")
public class ZhouServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 接收请求
        String msg = request.getParameter("msg");
        System.out.println("zhou:" + msg);
        //2. 业务处理
        System.out.println("这个哥们还不错");
        //3. 响应数据
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print("这个哥们还可以,大约3K");
    }
}




如下浏览器F12,第二行没有带参数,拿不到msg。

前面登陆成功请求转发到成功.html页面,失败.html…。用请求转发不合适,因为没有传递数据,应该用重定向。请求转发一次请求,地址指向第一次访问位置如下(网址阅读性太差,实际页面应该为localhost:8080/success.html)。

如下红框用重定向,登陆失败逻辑错误,之后再改。

如下重定向,第一次请求收到302。

输入账号和密码后跳转如下。

35.1 文件下载:request/response

首先服务器要有文件,在网络编程做过文件上传。在servlet里写代码覆盖tomcat默认设置。

//02文件下载.html
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<!--1111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
 <!--方案A:浏览器打开此网页点击后自动下载的话,说明tomcat默认设置,因为servlet一个字没写-->
   <!-- <a href="dir/1.zip">1.zip</a> <br>   
    <a href="dir/2.exe">2.exe</a> <br>
    <a href="dir/3.txt">3.txt</a> <br>
    <a href="dir/4.jpg">4.jpg</a> <br>-->

   <a href="/DownloadServlet?file=1.zip">1.zip</a> <br>
   <a href="/DownloadServlet?file=2.exe">2.exe</a> <br>
   <a href="/DownloadServlet?file=3.txt">3.txt</a> <br>
   <a href="/DownloadServlet?file=4.jpg">4.jpg</a> <br>
</body>
</html>
package com.itheima04.down;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet(urlPatterns = "/DownloadServlet")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String file = request.getParameter("file"); // 拿到1.zip之类
        //1. 告诉浏览器如何处理文件
        response.setHeader("content-disposition","attachment;filename=x" + file); //下载以x命名
        //2. 读取服务器本地文件,写出浏览器。绝对路径 等会要改成 相对路径
           //以前java工程: web/dir/文件(相对路径,里面是1.zip,2.exe等需下载的文件) //FileInputStream fis = new FileInputStream("E:/mywork/IdeaProjects/class897/web-day03/web/dir/" + file);
           //web工程: main方法在tomcat里,造成和java工程相对路径不一样
        ServletContext servletContext = request.getServletContext(); //request小域对象可获取ServletContext大域对象 
        String realPath = servletContext.getRealPath("dir/" + file); //绝对路径换成相对路径 
                
        FileInputStream fis = new FileInputStream(realPath);
        ServletOutputStream os = response.getOutputStream();
        int length;
        byte[] buffer = new  byte[1024];
        while((length = fis.read(buffer)) != -1){
            os.write(buffer,0,length);
        }
        fis.close();
        os.close();
        System.out.println("文件下载完毕");
    }
}

响应体交给浏览器,浏览器自动下载。

36.cookie:再次时请求头携带cookie到服务端







如下localhost是域名,不包含协议和端口。


浏览器的设置中搜索cookies - 网站设置 - localhost。

如下方式只能查看当前域名的cookies,如上方式可以查所有域名cookies。


只有localhost(域名)默认cookie存活时间是浏览器打开到关闭(不是窗口),如下改为30天(游客访问京东购物车一般也为30天)。在浏览器的设置里搜索清除浏览数据。

如下name虽相同,但是路径不同,不会覆盖。name相当于文件名,value相当于文件内容,同路径同name就会覆盖。

如上浏览器现在有两个cookies:一个是 / product galaxyNote7,另一个是 /abc product huawei。这两个cookies都会被携带进如下/abc…服务器。

36.1 登陆案例_记住我:js访问浏览器数据用document

如下两个cookies,一个保存用户名,另一个保存密码。

//改进login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录页面</title>
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/login.css" rel="stylesheet">
    <script src="js/jquery.js"></script>
    <script src="js/bootstrap.js"></script>
    <script>
        //你传入key=name,getCookie函数返回value=admin
        var getCookie = function (key) {  //匿名函数,脱离对象,不需要用cookie对象调用,直接调函数名
            return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
        }
        //document.cookie 可以访问到当前页面域名下的所有cookie信息
        //console.log(document.cookie) -> name=admin; pwd=123  需要上面.replace按分号等切割        
    </script>

<!--1111111111111111111111111111111111111111111111111111111111111111111111-->
    <script> 
        $(function () { //页面加载后执行的事件
            var name = getCookie("name");  //上面script定义的getCookie
            var pwd = getCookie("pwd");
            /* if(name != null && name != ""){
             }*/
            if(name && pwd){ //判空同上 ,js中空字符串也就是false
                $("#name").val(name)  //id选择器,往body网页文本框填入
                $("#pwd").val(pwd)
                $("#remember").prop("checked",true)  //name和pwd都不为空,勾起
            }
        })
    </script>

<!--11111111111111111111111111111111111111111111111111111111111111111111111-->
    <script>
        function changeImg(img) { //浏览器检测到网页的元素或属性改变,会自动刷新网页(点击验证码刷新)
            var time = new Date().getTime();  //时间撮
            img.src = "/codeServlet?time="+time //让它变,不能不加参数或故意写错即/codeServlet2,会报404,time=..这个参数一并发给服务器,虽服务器用不到,但整个属性值变了。
        }
    </script>
</head>

<!--1111111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
    <div class="container">
        <form class="form-signin" action="/LoginServlet">
            <h2 class="form-signin-heading text-center">登录页面</h2>
            <input type="text"  id="name" name="username" class="form-control" placeholder="用户名" required autofocus>
            <input type="password"  id="pwd" name="password" class="form-control" placeholder="密码" required>

            <input type="text" placeholder="验证码" name="code" class="form-control">
                    <img src="/codeServlet" alt="" onclick="changeImg(this)"> <br>

            <input type="checkbox" id="remember" name="remember" value="yes"> 记住我
            <button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
        </form>
    </div>
</body>
</html>
package com.itheima.login.web;
import com.itheima.login.service.LoginService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet(urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*
        * TODO: session : code
        * */
        String code = request.getParameter("code"); //获取前端用户写的发送的code参数即输入验证码框
//      HttpSession session = request.getSession(); //request.getSession()挂号,response只有.addCookie()
        String rightCode = (String) request.getSession().getAttribute("code"); //服务器通过codeServlet.java生成的
        
        if(!rightCode.equalsIgnoreCase(code)){  
  //验证码错误其实不应该跳到error.html ,应该停留在当前页面显示错误提示,这整段应该写在其他逻辑前面   
            request.getRequestDispatcher("/error.html").forward(request,response); //跳转到失败页面
        }

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
        String username = request.getParameter("username"); //拿到前端的参数
        String password = request.getParameter("password");
        //逆向编程 : 没有的先当它有
        LoginService service = new LoginService();
        boolean result = service.login(username,password);     
        if(result){
            /*
            * TODO: cookie : username ,password 
            * */
            String remember = request.getParameter("remember"); //前端 记住我 传来的参数
            if("yes".equals(remember)){
                Cookie nameCookie = new Cookie("name", username); //没有做数据库校验
                Cookie pwdCookie = new Cookie("pwd", password);
                nameCookie.setMaxAge(60*60*24*30); //存活30天
                pwdCookie.setMaxAge(60*60*24*30);
                response.addCookie(nameCookie);
                response.addCookie(pwdCookie);
            }
            response.sendRedirect("/success.html"); //重定向的简易api
        }else{           
            request.getRequestDispatcher("/error.html").forward(request,response);  //登录失败,请求转发
        }
    }
}


如下在控制台输出,在html中一样,用于数据回显。

37.session:根据sessionid,服务器才能找到session(session对象在服务器内存中增加压力)

病历本session,session记录大量信息,直接进行传递会卡,所以用cookie传sessionid(Cookie将sessionid传来传去,4k不宜过大,Cookie用sessionid标记用户)。cookie默认生命周期是会话即关闭浏览器同理session也是。

如下挂号这一行代码都一样,小域对象request获取大域对象session。< session - config > 是在tomcat软件解压后的conf文件夹里的web.xml(如订单30分钟不付款就会自动取消了)。

解决关闭浏览器cookie就没了?用Session持久化方案覆盖了tomcat默认生成的cookie即上面灰字(因为session默认保留30分钟,所以sessionid即cookie设为30分钟)。



如下删除后每次访问,生成的sessionid都是不一样的。

37.1 登录案例_验证码:request.getSession().setAttribute(“code”,sb.toString())

如下红字:一个存,一个取,该用什么载体实现存取?如最右边。如下是两次请求,不是一次请求链,所以request不行。存在session里好处:30分钟后自动没了。

login.html和LoginServlet.java见上一章节中。

//如下gui不需要掌握,被JavaME嵌入式代替 , codeServlet.java 
package com.itheima.login.web;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/codeServlet")
public class CodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // gui 生成图片
        // 1 高和宽
        int height = 50;
        int width = 70;
        String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        Random random = new Random();
        // 2 创建一个图片
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 3 获得画板
        Graphics g = image.getGraphics();
        // 4 填充一个矩形
        // * 设置颜色
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, width, height);
        g.setColor(Color.WHITE);
        g.fillRect(1, 1, width - 2, height - 2);
        // * 设置字体
        g.setFont(new Font("宋体", Font.BOLD | Font.ITALIC, 25));
        StringBuffer sb = new StringBuffer();
        // 5 写随机字
        for (int i = 0; i < 4; i++) {
            // 设置颜色--随机数
            g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
            // 获得随机字
            int index = random.nextInt(data.length());
            String str = data.substring(index, index + 1);
            // 写入
            g.drawString(str, width / 6 * (i + 1), 20);
            sb.append(str);//  获取验证码数据
        }
        //TODO:  验证码保存到session中,完成上面的第一步:服务器生成验证码并进行保存
        request.getSession().setAttribute("code",sb.toString()); //这行最重要
        System.out.println("作弊:" + sb.toString());
        // 6 干扰线
        for (int i = 0; i < 3; i++) {
            // 设置颜色--随机数
            g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
            // 随机绘制先
            g.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height));
            // 随机点
            g.drawOval(random.nextInt(width), random.nextInt(height), 2, 2);
        }
        // end 将图片响应给浏览器
        ImageIO.write(image, "jpg", response.getOutputStream());
    }
}



如下在A1中只要说引入什么jar包就行。apache-maven-3.6.1解压到F/develop文件夹下,以前在WEB-INF右击new一个lib文件夹,现在不用,只需写dependency标签就行。

如下Maven解压包里boot文件夹里是一个.jar,所以maven主要代码是java写的,所以保证jdk要装。私服就是如阿里网速快把maven官方仓库下载下来,别人到阿里这里下载。resources文件夹里放c3p0-config.xml等,test包下的类必须以XxTest结尾,只有main文件夹里代码才会被打包。


从远程仓库中获取坐标:1. 中央仓库 : http://mvnrepository/ 。2. 私服: 阿里云: https://maven.aliyun/mvn/search。进入前面两个网站搜索并进行复制,groupId是com.。

由于网络的原因,jar包下载不完全,这些不完整的jar包都是以lastUpdated结尾。此时maven不会再重新帮你下载,需要你删除这些以lastUpdated结尾的文件。

如果本地仓库中有很多这样的以lastUpadted结尾的文件,可以执行如下脚本来删除,如下是如上clean…bat文件,第一行路径不要加双引号。

set REPOSITORY_PATH=E:\javaDevlopment\maven\health_repository
rem 正在搜索...
for /f "delims=" %%i in ('dir /b /s "%REPOSITORY_PATH%\*lastUpdated*"') do (
    del /s /q %%i
)
rem 搜索完毕
pause
//c3p0-config.xml
<c3p0-config>
<default-config>
  <!-- 数据库的连接参数 ,加上? useUnicode= 防止不是utf-8-->
  <property name="driverClass">com.mysql.jdbc.Driver</property>
  <property name="jdbcUrl">jdbc:mysql://localhost:3306/heima_travel89? useUnicode=true&amp;characterEncoding=UTF8</property>
  <property name="user">root</property>
  <property name="password">1234</property>
</default-config>
</c3p0-config>

非空和3-6等是前端校验。后端校验也用jquary-validator(里面支持ajax请求)。status和code是数据表中两个字段。


<!--apache mail-->
<dependency>
  	<groupId>org.apachemons</groupId>
  	<artifactId>commons-email</artifactId>
  	<version>1.5</version>
</dependency>
package com.heima.travel.utils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import java.util.ResourceBundle;

public class MailUtil {
    private static String host;
    private static String charset;
    private static String username;
    private static String password;
    static{
        ResourceBundle bundle = ResourceBundle.getBundle("mail");
        host = bundle.getString("mail.host");
        charset = bundle.getString("mail.charset");
        username = bundle.getString("mail.username");
        password = bundle.getString("mail.password");
    }

    public static void sendEmail(String toUser,String msg) throws EmailException {
        //1、创建一个HtmlEmail对象
        HtmlEmail htmlEmail = new HtmlEmail();
        //2、设置邮箱服务器的参数  (qq发件服务器)
        htmlEmail.setHostName(host); // smtp.163
        //设置编码 (程序: utf-8  -> GBK)   收件箱: GBK
        htmlEmail.setCharset(charset);
        //3、设置邮箱验证(客户端授权)  发件人
        //username:邮箱账号
        //password:客户端授权码
        htmlEmail.setAuthentication(username,password);

        //4、设置收件人 和 发件人
        htmlEmail.setFrom(username, "黑马897_boy");
        htmlEmail.addTo(toUser, "瘪三");

        //5、设置主题 和  正文
        htmlEmail.setSubject("【黑马官网:激活邮件】");
//        htmlEmail.setTextMsg("注册成功"); // 纯文本
        htmlEmail.setHtmlMsg(msg); // html : 激活码
        //6、发送邮件
        htmlEmail.send();
    }
}

如下在resources文件夹下。

//mail.properties
mail.host = smtp.qq.com
mail.charset = gbk
mail.username = nightswatch1@qq
mail.password = ulsfpuawdvdyffja


事务四大特性:1.原子性(A):如转账,一个人转出,一个人转入,这两步操作是不能分的,要么都做,要么都不做。
2.一致性(C):事务的成功与否,数据库的数据都满足生活正常逻辑,如转账,总额不会变。
3.隔离性(I):并发访问数据库时,一个用户的事务不被其他事务干扰。
4.持久性(D):事务提交后,它对数据库的改变是持久的,断电也变。

注意隔离性,虽然事务间没有影响,但两个事务同时并发且操作同一条数据会出现如下问题,oracle默认解决脏读,mysql默认解决不可重复读:
1.脏读:一个事务读到另一个事务未提交的数据。
2.不可重复读:事务A第一次读账户余额为1000,事务B这时操作了这条数据并且提交+1000的事务。事务A又去读,变为2000了,读不到重复的1000了。
3.幻读:不可重复读强调读取内容【select *,update】不一致,幻读强调读取数量【select count(*),delete或insert】不一致。

用事务隔离级别解决如上问题:
read uncommitted:不会去设的,未提交会出现脏读。
read committed:解决脏读,还是存在不可重复读和幻读。
repeatable-read:mysql数据库默认隔离级别,解决不可重复读,还是存在幻读。
serialize:串行化,解决所有。

38.MVC:Spring_IOC(对象创建交给spring工厂),Spring_AOP(基于动态代理功能增强)。Runapplication启动类:控制层(UserController),业务层(UseService,UseServiceImpl),持久层(User即pojo,UseMapper.xml,UseMapper接口)

微服务:每个功能模块都独立打包部署到不同的系统上去,分开后每个微服务怎么去调用呢?这时就用到springcloud或dubbo一些远程调用协议,也就是相当于每个人怎么去沟通呢?沟通使用的这个语言就是协议如普通话,这么多的微服务都是一个单体应用系统,用到了springboot来解决S(spring)S(springmvc)M(mybatis)搭建应用时大量的手工配置问题,所以springboot是一个快速搭建应用的框架,不是微服务。为了管理和编排这些微服务用springcloud。ORM:对象(编程语言中对象)关系(关系型数据库中库表)映射。

以前获取表单数据最终封装成一user对象,要先获取所有请求参数再手动封装一下,SpringMVC中在方法上声明一个user类型参数就可以了,让C和V变的更加的简单了。

如下printLog()比作System.out.println(“功能的增强”);的封装。Aspect就是Advice加在joinPoint的前面还是后面等。

39.RPC:远程过程调用,我这台电脑调用一个函数(这函数在另一个电脑上执行),像是本地正常运行函数一样,不需要关注远程(网络连接,断开,传数据)细节

1.为什么远程remote?a和b两个参数打包发给服务端,本机运行的程序主要干的事是A,执行A的过程需要借助B功能,但是B功能不属于A功能范围内事情。所以我们会调实现B功能的集群,获取它们计算出来的结果。请求和响应和HTTP协议很像,grpc谷歌框架就是使用了http协议。

3 .数据包该如果封?程序内存中数据结构封装到传输时数据结构(文本或字节数组),传来后还要将数据(即字节或文本)恢复到内存中,这传一次就叫做序列化和反序列化。HTTP中json等就是序列化方式,一个数据在内存中的表达可能没法直接给人看的,或者没法直接两个进程间无法互相阅读的,除了共享内存。json和xml都是文本化的序列化方式,高效的grpc中一般不会用json这种序列化方式,比如用jdk自己的序列化方式,protobuf等。

4 .如果需要提高rpc性能的话除了序列化和反序列化,数据传输过程中需要提高网络IO性能。网络IO中涉及很多的系统调用,有对这些系统调用较好的封装如java中有名的rpc框架dubbo。dubbo中使用netty作为网络传输的一个框架实现,netty又是基于java的NIO,NIO最终调用一些系统调用,这些系统调用能够实现网络传输过程中零拷贝,多路复用等等充分提高了网络性能。

40.ajax:xhr对象,$.,浏览器输入框加载脚本

同步加载1.当前页面直接跳转到另一个页面(覆盖),url发生变化。2.直接新开一个页面,url发生变化。同步注册:所有信息填完了,点提交等待服务器响应是同步请求(有顺序,等服务器响应结果,不是同步比异步差,之前写的都是同步请求,一个一个发,B覆盖A页面。前端没有线程,下面线程是比喻)。

如上四个箭头就是四次变化。要实现异步请求,必须借助ajax,不然都是同步请求。ajax(Asychronous异步 Javascript And XML)应用:1.搜索内容自动补全,2.注册页面自动检测用户名是否存在。

//01_js中的ajax请求.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        function method01() {
       //1.创建一个xhr对象(浏览器内核/引擎支持)
           var xhr = new XMLHttpRequest();           
       //2.设置一个监听事件  //5个状态4次改变, 当请求状态改变(点击前端发请求按钮)的时候被触发
            xhr.onreadystatechange = function (ev) {
                // console.log(xhr.readyState)  //1 2 3 4 
                if(xhr.readyState == 4){ // 前端可以拿到服务器响应后的数据,只4对于我们才有意义
                    if(xhr.status == 200){ 
                        alert(xhr.responseText) //响应成功,拿到后台发来的“hello boy”
                    }else{
                        alert("响应失败了~~")
                    }
                }
            }

       //3.设置请求数据 ,如上的2相当于最后一步
           /* method:请求的类型;GET 或 POST   //GET请求发送参数拼接在url中如下
                 url:文件在服务器上的位置
                async:true(异步)或 false(同步),一般默认true */
            xhr.open("get","/AjaxServlet?name=hehe",true)            
       //4.发送请求            
            xhr.send() //将请求发送到服务器。send里参数string:仅用于POST请求,用来放请求体 ,Get参数放在上行url中。
        }
    </script>

<!--111111111111111111111111111111111111111111111111111111111111111111111111111111111-->  
    <script>
        function method02() {
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function (ev) {
                if(xhr.readyState == 4 && xhr.status == 200){
                    alert(xhr.responseText)
                }
            }
            xhr.open("post","/AjaxServlet",true)           
            xhr.setRequestHeader("content-type","application/x-www-form-urlencoded") 
           //一般编码解码由浏览器完成,但ajax的post请求url编码要上行手动设置。
            xhr.send("name=haha")
        }
    </script>
</head>

<!--11111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
        <input type="button" value="js中的ajax(get)请求" onclick="method01()"> <br> <br>
        <input type="button" value="js中的ajax(post)请求" onclick="method02()"> <br> <br>
</body>
</html>

post请求参数(“name=haha”)需要url编码才能传到服务器。


//02_jquery中的ajax请求.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery-3.3.1.js"></script>
    <script>
        /*
         GET请求:$.get(url, [data], [callback], [type])    // []表示可有可无
                  1. url : 请求地址
                  2. data : 请求参数 (text,json)
                  3. callback : 响应成功的回调
                  4. type : 响应数据的类型(text 默认值,json)
        */
        function method01() { //name= 可以用?拼在前面/AjaxServlet的后面  //$是全局对象
            $.get("/AjaxServlet","name=xixi",function (result) {
                alert(result)  //result是服务器传来的"hello boy"
            },"text")
        }
        function method02() { //不可拼在/AjaxServlet后 //不用考虑url编码和post请求参数放哪里问题
            $.post("/AjaxServlet","name=huhu",function (result) {
                alert(result)
            },"text")
        }
        function method03() {
            /*
            AJAX完整请求: $.ajax([settings])  考虑请求失败的情况用完整请求,上面两个方法没有失败
                上行settings = object如下
                {
                    n1 : v1,
                    n2 : v2...
                }
             */
            $.ajax({
                url:"/AjaxServlet",
                async:true,
                data:"name=tom",
                type:"GET",   //  请求方式
                dataType:"text",  // 响应数据的类型
                success:function(data){ // 成功的回调
                    alert(data);
                },
                error:function(){  // 失败的回调
                    alert("数据没有成功返回!")
                }
            });
        }
        function method04() {
            $.post({
                url:"/AjaxServlet",
                async:true,
                data:"name=tom",
                dataType:"text",  // 响应数据的类型
                success:function(data){ // 成功的回调
                    alert(data);
                },
                error:function(){  // 失败的回调
                    alert("数据没有成功返回!")
                }
            });
        }
    </script>
</head>

<!--111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
        <input type="button" value="jquery中的ajax(get)请求" onclick="method01()"> <br> <br>
        <input type="button" value="jquery中的ajax(post)请求" onclick="method02()"> <br> <br>
        <input type="button" value="jquery中的ajax完整请求" onclick="method03()"> <br> <br>
        <input type="button" value="jquery3.0中的ajax签名方式请求" onclick="method04()"> <br> <br>
</body>
</html>
package com.itheima01.ajax;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/AjaxServlet") 
public class AjaxServlet extends HttpServlet {  //服务端
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        System.out.println(name);
        System.out.println("服务器被访问了!");
        response.getWriter().print("hello boy");
    }
}



浏览器输入框输入:data:text/html,<h1>hello<h1>,页面自动显示hello。data:text/html,<script src="https://cdn.bootcdn/ajax/libs/jquery/3.6.0/jquery.min.js"></script>,F12 Network中刷新页面显示如下,这时就可在console中使用jquery了。


监听99号端口的小程序,直接双击打开:链接:https://pan.baidu/s/1nHhdf7Qerq3bEkOfXr8FXw 提取码:eipc。

如上在console中回车发送后,如下Network中会多一条localhost请求。

41.json:new ObjectMapper()

JSON:JavaScript对象简单表示法(JavaScript Object Notation),与js对象区别是JSON只有属性没有方法,比XML小快(因为json不需要标签)。

如下右边java中list集合,前端看不懂无法解析,变成xml后,前端可以解析。任意数组皆为json,上面为混合模式。

41.1 json在前端和后端中表达:json在js中的表达.html(前端)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        var jsonObj = {"name" : "张三" , "age" : 18 , "married" : true}  //name必须string格式,value无所谓
        var jsonObj2 = {"name" : "李四" , "age" : 19 , "married" : false}
        console.log(jsonObj.name + "," + jsonObj.age + "," + jsonObj.married)  //对象.属性名

        var jsonArray = ["哈哈", 1 , true]  //也是json对象
        console.log(jsonArray[0])
        
        var jsonArray2 = [jsonObj,jsonObj2]  //[0,1]
        console.log(jsonArray2[1].name)

		//jsonComplex.list指的是jsonArray2数组,0索引位是jsonObj
        var jsonComplex = {"list" : jsonArray2, "count" : 2}
        console.log(jsonComplex.list[0].married)   // jsonObj.married
    </script>
</head>
<body>
</body>
</html>

//JsonDemo.java(后端)
package com.itheima02.json;
import org.junit.Test;
import java.util.ArrayList;
/*
*   Json在java中就是普通string (java不识别json,xml,sql,正则表达式,都认为是字符串)
*      Json    Java
*   1. 对象 : javaBean
*   2. 数组 : List
*   3. 混合 : Map<Key, List>      
*/
public class JsonDemo {  //Java数据(javaBean)变成 Json
    @Test
    public void method01(){
        Friend f = new Friend("张三", 18, true);
        //System.out.println(f.toString());  //.toString()可以不写,默认调用.toString()   //Friend{name='张三',age=18,..}
        System.out.println(f.toJson()); // {"name":"张三","age":18,"married":true}       
    }
    
    @Test
    public void method02(){
        ArrayList<Friend> list = new ArrayList<>();
        Friend f1 = new Friend("张三", 18, true);
        Friend f2 = new Friend("李四", 19, false);
        list.add(f1);
        list.add(f2);
        System.out.println(list); //打印如下,默认调用.toString() 
        //[Friend{name='张三', age=18, married=true}, Friend{name='李四', age=19, married=false}]
        
        StringBuilder sb = new StringBuilder();  //如下将上面list转化为json的数组
        sb.append("[");
        
        for (int i = 0; i < list.size(); i++) {  //i最多=2个对象
            String s = list.get(i).toJson();
                        
            if(i == list.size() - 1){ //这行意思为最后一个
                sb.append(s); //不加逗号
            }else{
                sb.append(s + ","); //中间加一个逗号
            }
        }
        sb.append("]");
        System.out.println(sb.toString());
    }
}

package com.itheima02.json;

public class Friend {
    private String name;
    private Integer age;
    private Boolean married;
    public Friend() {
    }
    public Friend(String name, Integer age, Boolean married) {
        this.name = name;
        this.age = age;
        this.married = married;
    }
    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", married=" + married +
                '}';
    }
    public String toJson() {
        return "{\"name\" : \""+name+"\",\"age\":"+age+",\"married\":"+married+"}";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Boolean getMarried() {
        return married;
    }
    public void setMarried(Boolean married) {
        this.married = married;
    }
}

42.ajax和json综合:ajax_json_union.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery-3.3.1.js"></script>
    <script>
         function method01() {
             /*
             *  url : 请求地址
             *  data : 请求参数 (text , json)
             *  callback : 响应成功的回调函数
             *  type : 响应数据的类型 (text , json)
             *       1. text : 响应回来的数据变成string
             *       2. json : 响应回来的数据变成json(Object)
             */
             // var param = "name=admin&pwd=123"  // text类型的参数
             var param = {"name" : "admin" , "pwd" : "123"} //json类型的参数
             $.get("/UnionServlet",param , function (result) {
                console.log(typeof result)
                console.log(result)               
                for (var i = 0; i < result.length; i ++) {  //前端解析json格式的数据
                    console.log(result[i].name)
                 }
             },"json")
         }
    </script>
</head>

<!--11111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
        <input type="button" value="发送一个异步请求" onclick="method01()">
</body>
</html>

如上把$.get中json换成text,如下的object就是string。

package com.itheima03.union;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima02.json.Friend;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

@WebServlet(urlPatterns = "/UnionServlet")
public class UnionServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name"); //后台解析json格式的数据
        String pwd = request.getParameter("pwd");
        System.out.println(name + "," + pwd);

        ArrayList<Friend> list = new ArrayList<>();
        list.add(new Friend("瘪三",18,true));
        list.add(new Friend("瘪四",19,false));
        ObjectMapper om = new ObjectMapper();
        String json = om.writeValueAsString(list);

        response.setContentType("text/html;charset=utf-8"); //响应体中文乱码问题
        response.getWriter().print(json);
    }
}

如下是main方法入口,换成猫(web工程的入口在猫里)再点乌龟加载servlet。

//05_校验用户名是否可用.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--
        异步请求:
        1. 事件:  鼠标离开输入框时候触发(失去焦点)   onblur
        2. 逻辑:
            1. 发起异步请求 : 局部刷新页面
            2. 请求地址 : /CheckServlet
            3. 请求参数 : name = ?
            4. 回调(接收响应) :
                true : 提示可以注册
                false : 不可以注册
        3. 后台:
            sql : select * from table where name = ?;
            查询数据库,查得到, 返回false(告诉前端你不可以注册了) , 查不到, 返回true
    -->
    <script src="js/jquery-3.3.1.js"></script>
    <script>
        function method01(value) {
            // console.log(value)
            $.get("/CheckServlet",{"name" : value},function (result) {                
                if(result == "true"){ //result为后台发来response.getWriter().print里内容
                    $("#myspan").text("恭喜你,用户名可用")
                    $("#myspan").css("color","green")
                }else{
                    $("#myspan").text("不好意思,此用户名已被使用~~")
                    $("#myspan").css("color","red")
                }
            },"text")
        }
    </script>
</head>

<!--111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
        <!--this.value指如下当前输入框输入的内容,当鼠标离开即onblur时获取-->
        <input type="text" placeholder="请输入要注册用户名" onblur="method01(this.value)">
        <span id="myspan"></span>
        <br>
        <input type="text" placeholder="请输入要注册的密码"> <br>
</body>
</html>
package com.itheima04.check;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/CheckServlet")
public class CheckServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");        
        if("jack".equalsIgnoreCase(name)){ //假设数据库里就一个jack
            response.getWriter().print("false"); //不要写成println,本来是换行,但在前端中体现是多一个空格
        }else{
            response.getWriter().print("true");
        }
    }
}

//search-page.jsp  //自动提示案例
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">
	.content{
		width:643px;
		margin:200px auto;
		text-align: center;
	}
	input[type='text']{
		width:530px;
		height:40px;
		font-size: 14px;
	}
	input[type='button']{
		width:100px;
		height:46px;
		background: #38f;
		border: 0;
		color: #fff;
		font-size: 15px
	}
	.show{
		position: absolute;
		width: 535px;
		height:100px;
		border: 1px solid #999;
		border-top: 0;
	}
</style>
<script type="text/javascript" src="js/jquery-3.3.1.js"></script>
<script type="text/javascript">
	/*
		点击onclick: 按下onkeydown + 抬起onkeyup   //文本框内容输入变化onchange不行
	* 	1. 事件 : onkeyup (键盘抬起)
	* 	2. 逻辑:
	* 		异步请求
	* 		请求地址: /AutoServlet
	* 		请求参数: value = x  文本框输入的内容
	* 		回调(响应):
	*   3. 服务器
	*   	sql : select * from table where name like ? limit 4;   下拉框显示4个
	*   	1. 查询数据库 : 	? = "value%"    返回list 再变成json数组 再遍历	
	*/
	function method01(value) {
		// console.log(value)
		if(!value){   // value是空的,到服务器就剩下%符号,变全部查询,所以在这阻断
            $(".show").css("display","none")   // if(!value)为true
		    return;   // 不往后执行
		}
		$.get("/AutoServlet",{"value" : value},function (result) {
			// console.log(result)
			var content = ""
			$(result).each(function (index,element) { //jquary的遍历
				// console.log(index + "," + element)
				content += element.name + "<br>"   //不写<br>,就是一行,拼接
            })
			$(".show").css("display","block")  //display由隐藏改回块级元素
			$(".show").html(content)  //.html自动识别换行<br>标签
        },"json")
    }
    function method02() {
		alert("xx")
    }
</script>
</head>

<!--11111111111111111111111111111111111111111111111111111111111111111111111111111-->
<body>
	<div class="content">
		<img alt="" src="img/logo.png"><br/><br/>
		<input type="text" name="word" onkeyup="method01(this.value)">
		<input type="button" value="搜索一下" onclick="method02()">
		<div class="show" style="display: none"></div>  <!--自动提示下拉框-->
	</div>  
</body>
</html>

console.log(index + “,” + element) 如下:

package com.itheima05.auto;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet(urlPatterns = "/AutoServlet")
public class AutoServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String value = request.getParameter("value");
        String sql = "select * from user where name like ? limit 4"; //limit0,4 默认0不写
        JdbcTemplate template = JdbcUtil.getTemplate();
        String param = value + "%";
        List<User> list = template.query(sql,new BeanPropertyRowMapper<>(User.class),param);

        ObjectMapper om = new ObjectMapper();
        String json = om.writeValueAsString(list);
        System.out.println(json);        
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print(json);
    }
}

System.out.println(json)如下:

package com.itheima05.auto;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcUtil {
    private static ComboPooledDataSource ds = new ComboPooledDataSource();
    public static JdbcTemplate getTemplate(){
//        JdbcTemplate template = new JdbcTemplate(ds);
//        return template;
        return new JdbcTemplate(ds);
    }
}
package com.itheima05.auto;

public class User {
    private Integer id;
    private String name;
    private String password;
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}


域名作用是方便记忆,dns作用是将域名解析成ip。

如下如果本机DNS知道baidu对应的ip直接返回ip。根dns服务器全球只有13个,根dns服务器可能也不知道baidu这域名对应的ip,但它一定知道这个域名对应的ip是哪个dns服务器(XX)知道。

如下是系统中配置的是上图第一个红圈。

更多推荐

【Java3】lambda,Stream流,File类,乱码,流/Properties,Tcp/文件上传/UUID/Junit,反射/注解,类加载器,设计模式,

本文发布于:2023-04-29 21:28:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/d234bdb98e9cd3d59ba229f92226e709.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:注解   乱码   反射   文件上传   加载

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!

  • 112330文章数
  • 28575阅读数
  • 0评论数