API体系(Map/Collections二大家族)、异常、多态、Junit框架、序列化

1、API体系【二大家族:Collection、Map家族】

1.1、Collection【表】
/**
 * 集合工具类Collections常用的API演示
 */
public class Demo6 {
    public static void main(String[] args) {
        Book book1 = new Book(1 , "java编程思想" , 97.5);
        Book book2 = new Book(2 , "mysql从入门到精通" , 56.4);
        Book book3 = new Book(3 , "java设计模式" , 109.2);
        List<Book> list = new ArrayList<>();
        list.add(book1);
        list.add(book2);
        list.add(book3);
        // 对集合进行自定义排序!! 调用sort方法,传参两个参数:1、集合 ; 2、Comparator对象
        Collections.sort(list, (o1, o2)  -> (int)(o2.bookPrice - o1.bookPrice));
        System.out.println(list);
        // 反转
        Collections.reverse(list);
        System.out.println(list);
        // 从集合中查找最大的哪个对象  -> 表达的意思(找价格最高的哪个图书对象!!)
        Book maxBook = Collections.max(list ,  (o1, o2)  -> (int)(o1.bookPrice - o2.bookPrice));
        System.out.println("价格最高的图书是:" + maxBook);
        // 查找图书价格最低的图书对象
        Book minBook = Collections.min(list ,  (o1, o2)  -> (int)(o1.bookPrice - o2.bookPrice));
        System.out.println("价格最低的图书是:" + minBook);
        // 集合元素的拷贝 (不推荐使用)
        List<Book> newBooks = new ArrayList<>();
        Collections.copy(newBooks , list);
        System.out.println(newBooks);
        // 查找学生对象中年龄最大的,最小的。
    }
}
1.1.1、sort对List进行排序,但是要创建一个Comparator对象(外部排序,位于java.until包下)
1.1.2、reverse对集合元素进行反转
1.1.3、max查询集合元素中最大的元素,要配合Comparator一起使用
1.1.4、min查询集合元素中最小的元素,要配合Comparator一起使用
1.1.5、相关案列【使用集合保存3个学生对象】
需求:

1)Student类 :学号id, 姓名name,年龄age,生日birthday

2)创建3个Student对象,放到集合中。

3)遍历输出 : 输入学生所有的信息

public class Test {
    public static void main(String[] args) throws ParseException {
        // 新的技术,把日期格式的字符转换为Date类型
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); // 日期转换器对象
        Date d =  format.parse("1992-10-11"); // 字符串转换Date类型
        // 第一个学生:1  陈永仁  30  1992-10-11
        Student stu1 = new Student();
        stu1.setId(1);
        stu1.setName("陈永仁");
        stu1.setAge(30);
        stu1.setBirthday(d);
        // 第二个学生:2  刘建明  29  1993-10-11
        Student stu2 = new Student(2 , "刘建明" , 29 , format.parse("1993-10-11"));
        // 第三个学生:3  小明   20  2002-10-11
        Student stu3 = new Student(3 , "小明明" , 20 , format.parse("2002-10-11"));
        // 把三个学生对象撞到集里
        List<Student> list = new ArrayList<>();
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        System.out.println("===========学生列表==============");
        list.forEach((Student e) -> {
            //  1  陈永仁    30 Sun Oct 11 00:00:00 CST 1992
            // TODO 把日期Date转换为字符串
            System.out.println(e.getId() + "\t" + e.getName() + "\t" + e.getAge() + "\t" + e.getBirthday());
        });
    }
}
1、根据学号查询学生

​ 需求:定义一个静态方法

              //就是你要查询的学生对象        // 把你的集合对象, 学号
public static Student findStudentById(ArrayList coll, int id) {
    ......
}
2、根据姓氏查找学生

​ 需求:定义一个静态方法

public static Collection<Student> findStudentBySurName(Collection<Student> coll , String n) {
    ......
}
3、根据名查找学生

​ 需求:定义一个静态方法,应该用subString(1),或 endsWith

public static Collection findStudentByName(Collection coll , String n) {
	......
}
4、根据学号修改学生的姓名

​ 需求:定义一个静态方法(id 是学号, n是修改后的姓名)

public static void updateStudentNameById(List<Student> coll , int id , String n) {
    ......
}
5、根据学号删除学生

​ 需求:定义一个静态方法

public static void deleteStudentById(List<Student> coll ,int id) {
    ......
}
6、保存3本图书,价格进行排序
需求:TreeSet集合来完成

​ 图书类:Book, 属性有:图书编号id , 图书名称bookName , 图书价格bookPrice。重写toString方法。

/**
 * 图书类
 */
public class Book implements Comparable<Book> {
    public int id;
    public String bookName;
    public double bookPrice;
    @Override
    public int compareTo(Book o) {
        // 降序
        return (int)(o.bookPrice - this.bookPrice);
    }
    @Override
    public String toString() {
        return  id + "\t " + bookName + "\t bookPrice \t" + bookPrice;
    }
}
import java.util.Scanner;
import java.util.TreeSet;
public class Demo4 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        TreeSet<Book> treeSet = new TreeSet<>();
        for (int i = 1; i <= 3; i++) { // 循环3次添加3本本
            // 每循环一次就造一个图书对象
            Book book = new Book();
            System.out.println("请输入第"+i+"本图书的信息:");
            System.out.print("编号:");
            book.id = input.nextInt();
            System.out.print("名称:");
            book.bookName = input.next();
            System.out.print("价格:");
            book.bookPrice = input.nextDouble();
            // 添加到集合中
            treeSet.add(book);
        }
        System.out.println("编号\t\t名称\t\t价格");
        for (Book book : treeSet) {
            System.out.println(book);
        }
    }
}
1.2、Map【字典】
1.2.1、双列,存储键值对这种数据
1.2.2、HashMap
1、常用方法

​ put:添加元素,key不能重复

​ containsKey:集合中查询某个key是否存在

​ remove:根据key删除元素

​ get:根据key获取相应的value

​ values:对应内存圈,所有的value值,是一个Collection集合

​ entrySet:对应内存圈、代表一行,一行就是一个Entry对象

​ keySet:对应内存圈,左边所有的key值,是一个set集合

2、相关案例,一对一的关系
public class Mop {
    /*
    集合第二大家族:Mop(字典)
     */
    public static void main(String[] args) {
        //map内存结构
        HashMap<String ,String > map = new HashMap<>();
        map.put("001","貂蝉");//key不可重复,values可重复
        map.put("002","吕布");
        map.put("003","张飞");
        map.put("004","关羽");
        System.out.println(map);
        boolean r = map.containsKey("005");//判断005是否存在
        System.out.println(r);
        //拿到左边的key这一列
        Set<String> keys = map.keySet();
        //Map遍历方式1
        for (String key:keys) {
            Object val = map.get(key);//通过key来找对应的value
            System.out.println(key + "->" + val);
        }
        //Map遍历方式2【拿到右边的value这一列】
        Collection<String> values = map.values();
        for (String  value:values) {
            System.out.println(value);//不能通过value找到对应的key
        }
        //遍历每一行
        Set<Map.Entry<String,String>> entries = map.entrySet();
        for (Map.Entry<String,String> row:entries) {
            String key = row.getKey();
            String value = row.getValue();
            System.out.println(key + "->" + value);
        }
    }
}
3、保存电脑城的电子清单【一对多的关系:Map<String,List>map=new HashMap()】
1.3、Comparable和Comparator
1.3.1、区别
1)包的位置不同

​ Comparable(自然排序)是在集合内部定义的方法实现的排序,位于java.lang下;
   Comparator(外部排序)是在集合外部实现的排序,位于java.util下;

2)定义不同

​ Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而 Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。

Comparable相当于“内部比较器”,而Comparator相当于“外部比较器。

1.4、set集合
1.4.1、HashSet能存储不重复的对象
1.4.2、TreeSet要求自定义对象必须实现Comparable接口
1.4.3、遍历set集合也是通过foreach
1.5、排序
1.5.1、自然排序Comparable【compataTo(o)】
1)升序:this.xx - o.xx
2)降序:o.xx - this.xx
1.5.2、外部排序Comparator【compara(o1,o2)】
1)升序:o1.xx -o2.xx
2)降序:o2.xx - o1.xx
1.6、API差异

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-brbFPL9e-1664470090387)(E:\笔记\第四周\QQ截图20220913141703.png)]

1.7、构建相对文件的系统
1.7.1、try【尝试、可能】
1.7.2、catch【捕获】catch关闭JVM:System.exit()
1.7.3、finall【最终的】无论什么情况都要执行,除非catch语句里关闭JVM则不执行
1.7.4、throw【抛出】
throw new Exception("对不起,你输入错误!");
1.7.5、throws【声明】
public class Demo6 {
    static Logger logger = Logger.getLogger(Demo6.class);
    // throws 关键字是声明异常的类型。目的是告诉调用者产品的异常类型是什么。。
    public static void t() throws Exception {
        // 控制 , 数组下标越界, 算术  , 输入类型不匹配
        Scanner input = new Scanner(System.in);
        System.out.println("请输入课程代号:1~3");
        int num = input.nextInt();
        if (num == 1) {
            System.out.println("c#课程");
        } else if(num ==2) {
            System.out.println("java课程");
        } else if(num == 3) {
            System.out.println("html课程");
        } else {
            // 还不够严格。 我们想严格一点,我们想和JVM一样严格(JVM发送程序有问题直接产生一个异常对象)
            // 我们可不可以也产生一个异常对象呢,并抛出去
            // System.out.println("对不起,你输入错误");
            // 异常抛出去了,就一定要有捕获
            throw new Exception("对不起,你输入错误!");  // 走到这一行代码,会发生什么情况。。
            // System.out.println("---"); // throw挨着的下一行不能在写其它代码了,否则编译报错!
        }
    }
    public static void main(String[] args) throws Exception {
        try {
            t();
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}
1.7.6、异常的分类
	  	//异常
        int a = 10;
        int b = 0;
        //JVM会开一个ExceptionThread线程监听
        //程序如果出现问题,线程它首先会记录信息,然后
        //1、出现问题的位置
        //2、出现问题的原因(异常类型)
        //3、封装成异常对象,发送给程序员
        int c = a / b;//发生异常,因为除数不能为0
        System.out.println(c);//因此此代码没有被执行
1.7.7、常见的异常

​ InputMismatchException:输入类型不匹配异常

​ 打印异常信息:ex.printStackTrace()方法

1.7.8、try-catch捕获异常
public class Demo4 {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        //JVM会开一个ExceptionThread线程监听
        //程序如果出现问题,线程它首先会记录信息,然后
        //1、出现问题的位置
        //2、出现问题的原因(异常类型)
        //3、发送给程序员
        //int c = a / b;//发生异常,因为除数不能为0
        //System.out.println(c);//因此此代码没有被执行

        //数组下标越界异常
        int[] nums = new int[2];
//        int[] sum = null;
        //会出现问题的代码
        try{
            int d = nums[3];
//            int e = sum[2];
        }
//        catch (ArrayIndexOutOfBoundsException ex){//ArrayIndexOutOfBoundsException ex数组下标异常
//            System.out.println("程序下标越界了");
//            ex.printStackTrace();//打印程序异常的错误信息
//        }catch (NullPointerException ex){//NullPointerException ex空指针异常
//            System.out.println("空指针异常");
//        }
        catch (Exception ex){//Exception ex:所有异常都应该走这条分支
            System.out.println("程序错误了");

        }finally {//不管是否出现异常,都要执行
            System.out.println("程序死活都有执行");
        }
        System.out.println("程序正常终止");
    }
}
1.7.9、异常匹配机制
1.7.10、创建日志对象
import org.apache.log4j.Logger;

public class Demo5 {

    // 是受保护的,只有子类才能new
    // 创建一个日志记录对象
    static Logger logger = Logger.getLogger(Demo5.class);
    public static void main(String[] args) {

        // 键盘输入的地方,添加一个try-catch语句,防止出现异常

        int[] nums = null;
        // 寻找可能会出现问题的代码
        try { // try包含的代码有可能会出问题
            int c = nums[3];
            // 没出问题catch就不走
        } catch(Exception ex) { // 捕获所有异常
            // 打印出错误的相关信息
            ex.printStackTrace();
            //
           logger.error("数组下标越界了!!");

        } finally { // 不管是否出现异常,都要执行
            System.out.println("程序正常终止!");
        }
    }
1.8、数据持久化
1.8.1、学生序列化
public class Demo2 {
    public static void main(String[] args) {
        // 把学生对象进行持久化,保存到我们的硬盘里面
        // 占用N个字节
        Student stu = new Student("叶孤城" ,20 );
        // 创建一个IO流对象(就是哪个水管,水管里面的数据就是字节/字符),用于把数据发送到硬盘的某个文件里面
        // output单词:输出 - 》 把内存里面的数据输出到硬盘的文件里面,文件的名称是什么?
        try {
            // 猜一下,执行这款代码,我的d盘会不会创建stu.obj
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("d:/stu.obj"));
            // 把学生对象写入到stu.obj文件里面
            out.writeObject(stu);
            System.out.println("学生对象写入到文件成功!");
        }catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
1.8.2、把员工对象保存到本地
需求:

编写一个writeEmp方法,用junit启动,然后把员工对象序列化到磁盘的emp.obj文件中。

1、transient关键字

使用transient关键字修饰的属性不会被序列化

2、序列化:ObjectOutputStream
/**
 * 员工类
 */
public class Employee implements Serializable {
    // 4个属性,我希望密码不被序列化
    private int id; // 编号
    private String name; // 姓名
    private double salary; // 薪资
    // transient 关键字修饰的属性,不会被序列化!!
    // 当比较敏感的字段可以使用
    // 金额,积分,密码,身份证.....
    private transient String pwd; // 登录密码
    public Employee(int id, String name, double salary, String pwd) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.pwd = pwd;
    }
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                ", pwd='" + pwd + '\'' +
                '}';
    }
    public double getSalary() {
        return salary;
    }
}
// 写到磁盘。输出 . 序列化
@Test
public void writeEmp() {
   Scanner input = new Scanner(System.in);
   System.out.println("员工编号:");
   int id = input.nextInt();
   System.out.println("员工姓名:");
   String name = input.next();
   System.out.println("员工薪资:");
   double salary = input.nextDouble();
   System.out.println("员工密码:");
   String pwd = input.next();
   Employee emp = new Employee(id, name, salary, pwd);
   // 序列化到本地的emp.obj
   ObjectOutputStream out = null;
   try {
      out = new ObjectOutputStream(new FileOutputStream("d:/emp.obj"));
      out.writeObject(emp);  // 把对象写入到文件中
      System.out.println("员工对象序列化成功!");
   } catch (IOException e) {
      e.printStackTrace();
   } finally {
      try {
         out.close(); // 关闭流,释放资源。必须调用
         System.out.println("关闭流成功,释放资源!");
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}
1.8.3、把员工对象从本地读取
需求:

编写一个readEmp方法,用junit启动。把磁盘的emp.obj读取进来,转换为Employee对象并打印。

反序列化:
// 从磁盘中读取文件到内存。输入。 反序列化
@Test
public void readEmp() throws IOException, ClassNotFoundException {
   /* 如果修改了类,要记得重新写一次 */              // alt+回车
   ObjectInputStream in = new ObjectInputStream(new FileInputStream("d:/emp.obj"));
   Object obj = in.readObject(); // 读取到内存里面。返回值是Object
   Employee emp = (Employee)obj; // 强转
   System.out.println("读取成功,对象的信息是:");
   System.out.println(emp);
   in.close(); // 关闭流,释放字眼
}
1.8.4、保存一批员工对象到本地
需求:

编写一个writeEmps方法,用junit启动,然后把员工对象序列化到磁盘的emps.obj文件中。

// 保存一批员工对象到本地
@Test
public void writeEmps() throws FileNotFoundException {
   ArrayList<Employee> list = new ArrayList<>();// 集合保存员工对象  。 集合也是个对象
   Scanner input = new Scanner(System.in); // 扫描器对象,一个就够用了
   while (true){
      System.out.println("员工编号:");
      int id = input.nextInt();
      System.out.println("员工姓名:");
      String name = input.next();
      System.out.println("员工薪资:");
      double salary = input.nextDouble();
      System.out.println("员工密码:");
      String pwd = input.next();
      Employee emp = new Employee(id, name, salary, pwd);
      // 每创建一个员工对象,马上就加到集合里面去....
      list.add(emp);
      System.out.println("是否继续:");
      String ans = input.next();
      if (ans.equals("no")) break;
   }
   /*******很臃肿。try-catch不能加太多了******/
   ObjectOutputStream out = null;
   try {
      // 保存到本地的emps.mp3
      // 创建一个输出流对象
      out = new ObjectOutputStream(new FileOutputStream("emps.mp3"));
      out.writeObject(list);
   } catch (Exception ex) {
      ex.printStackTrace();
   } finally {
      try {
         out.close();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
   System.out.println("保存一批对象成功,同时资源关闭!");
}
1.8.5、把一批员工对象又从本地读取
需求:

编写一个readEmps方法,用junit启动,把一批员工对象从本地读取进来,然后按照薪资从高到低排序。

@Test
    public void readEmps() throws IOException, ClassNotFoundException {
        // 先读进来,再来排序。
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("emps.mp3"));
        // list集合,其实也是一个list集合
        Object o = in.readObject();
        // 转换
        ArrayList<Employee> list = (ArrayList<Employee>)o;
        // 排序使用比较器Compatator
        Collections.sort(list, (o1,  o2) -> (int)(o2.getSalary() - o1.getSalary())); // 降序
        // 写法二: 既排序又输出
        list.stream().sorted((o1,  o2) -> (int)(o2.getSalary() - o1.getSalary())).forEach((System.out::println));
        System.out.println("编号\t\t姓名\t\t薪资待遇");
        list.forEach(System.out::println);
        in.close();
    }
1.9、读取记事本的内容
1.9.1、使用字符串读取
// 读取china记事本的内容 (读文件内容)
    // 我们这个版本是初级版本,一个字符一个字符读的,也最好理解
    @Test
    public void readChina() throws IOException {
        // 1、准备记事本
        // 创建一个可以按照字符读取的流对象.就是那一条绿色的管子
        FileReader reader = new FileReader("d:/china.txt");
        int r = -1;
        //调一次read,返回一个字符
        // 效率不是特别高 , 要循环26次,要读26次
        while ( (r = reader.read()) != -1) {
            System.out.print((char)r);
        }
        reader.close(); // 关闭流,释放资源
    }
    // 升级版2.0,一次性读取多个字符!
    // 缺点:扩展性不好。 希望定义一个合适(也不大,也不小)大小的容器,能读取任何长度的内容
    // V3.0
    @Test
    public void readChina2() throws IOException {
        FileReader r = new FileReader("d:/china.txt");
        // 准备一个容器,用来容纳存储的字符,一共可容纳26个
        // 虽然是30,但是实际是只读了26-(N)个字符。。
        char[] buffers = new char[30];
        // 怎么知道读取的字符实际个数N呢?
        int n = r.read(buffers);   // 读的时候,把读的所有字符存到buffers数组里面去
        // 把字符串数组转换为字符串
        String str = String.valueOf(buffers , 0 , n);
        System.out.println(str);
        r.close();
    }
    // 优点:扩展性好。 定义一个合适(也不大,也不小)大小的容器,能读取任何长度的内容
    // V3.0 - > 最终版
    @Test
    public void readChina3() throws IOException {
        long start = System.currentTimeMillis(); // 开始时间(毫秒)
        FileReader reader = new FileReader("d:/china.txt");
        char[] buffers = new char[1024];
        int n = 0;
        int count = 0;
        // 添加循环
        while((n = reader.read(buffers)) != -1) {
            // 把字符串数组转换为字符串
            String str = String.valueOf(buffers , 0 , n);
            System.out.println(str);
            count += n; // 叠加实际读取的字符个数
        }
        reader.close();
        long end = System.currentTimeMillis(); // 结束时间
        System.out.println("共耗时:" + (end - start) + "毫秒");
        System.out.println("一共有"+count+"个字。");
    }
1.9.2、使用字节读取
@Test
public void read() throws IOException {
    // 昨天是字符, 今天是字节。
    // 记事本里面存的都是字符
    // 准备一个管道,通过字节来传送!!!
    FileInputStream in = new FileInputStream("d:/china.txt");
    int c = in.read(); // 读取1个字节
    System.out.println(c);  // 97 , 既陌生(没看出来是a),又熟悉(ascii码 , a对应的97)
    System.out.println((char)c);
    System.out.println("----------以下是中文!!-----------");
    // 用字节读取中文的时候,尽量不要去打印,不要去看!! 不看就不乱码, 看了就乱码!!
    int c2 = in.read(); // 读 中文: 实 - > 229
    System.out.println(c2);
    System.out.println((char)c2); // å (它是什么东西? 它是半个实 , 半个字)
    System.out.println("---------------------------------");
    in.close();
}
@Test
public void read2() throws IOException {
    // 昨天是字符, 今天是字节。
    // 记事本里面存的都是字符
    // 准备一个管道,通过字节来传送!!!
    FileInputStream in = new FileInputStream("d:/china.txt");
    byte[] bufs = new byte[1024]; // 可以容量3个字节
    // 昨天是返回的是实际读取的字符个数, 今天呢是读取的字节个数
    int len = -1;
    while ( (len =  in.read(bufs)) != -1) {
        //新知识点, 把字节数组转换为String,方便查看
        // String(byte[] bytes, int offset, int length)
                                            // 有3个格子,假设只读取了2个字节,所以我们只需要拿两个格子就行了,剩下的哪个不要
        String str =  new String(bufs , 0 , len);
        System.out.println(str);
    }
    in.close();
}
1.10、爬虫
1.10.1、爬取图片并拷贝到本地
// 图片的拷贝
@Test
public void copyImage() throws IOException {
    // 需求:把电脑上任意位置的图片,拷贝到项目工程里面。
    // 因为图片是二进制文件,所以我们不能使用FileReader,使用FileInputStream
    // 第一步:创建了一个管道输入流对象!! (绿色的)
    FileInputStream in = new FileInputStream("d:/images/2.jpg");
    // 创建一个管道,输出流对象!! (蓝色的)
    FileOutputStream out = new FileOutputStream("妹子.jpg");
    // 第二步读
    byte[] bufs = new byte[1024];
    int len = -1; // 保存每一次读取的字节个数
    // 一边读read,一边写write
    while ((len = in.read(bufs)) != -1) {  // 图片是一个整体,再读取过程中不能去看的
        out.write(bufs , 0 , len); // 输出
    }
    in.close(); // 关闭流
    out.close(); // 关闭流
}
// 网络编程。
// 从互联网上通过URL地址进行网络下载图片
// URL 也叫做统一资源定位符: 作用就是定位到互联网上的某台服务器(百度公司)的某个资源(这个图片)
@Test
public void downloadImage() throws IOException /*抛出以表示发生格式不正确的网址。*/ {
    String urlStr = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg9.51tietu.net%2Fpic%2F2019-090917%2F1mdtumszxxk1mdtumszxxk.jpg&refer=http%3A%2F%2Fimg9.51tietu.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1665978666&t=1d707ceb6734a9440d36c8e68e879997";
    // 创建一个URL对象
    URL url = new URL(urlStr);  // 这个类是Java之父, 詹姆斯.高斯林写的 , 1.0版本
    // 根据URL开启一个网络连接. 网络连接也是个对象, URLConnection
    URLConnection urlConnection = url.openConnection();
    System.out.println("连接成功!!");
    // 开辟一个管道 -》 通过流的形式进行传输
    // 这句代码:其实就是拿到哪个输入流(绿色的)
    InputStream in = urlConnection.getInputStream();
    FileOutputStream out = new FileOutputStream("5.jpg");
    byte[] bufs = new byte[1024];
    int len = -1;
    while ((len = in.read(bufs)) != -1 ) {
        out.write(bufs , 0 , len);
    }
    out.close();
    in.close();
    System.out.println("从网络上下载图片成功!!!");
}

2、回顾

2.1、编写一个递归,输出1到10的阶乘
public class Test1 {
    public static void main(String[] args) {
        DiGui();
    }
    public static void DiGui(int i){
        if(i>10){
            return;
        }
        System.out.println(i+"+"+i+"="+i*i);
    }
}
2.2、编写一个List集合,使用迭代器对象来遍历集合
2.2.1、迭代器: Iterator
public class Test2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("sss");
        list.add("dddw");
        //通过迭代器对象来完成
        Iterator iterator = list.iterator(); // 获取一个迭代器对象
        ListIterator<String> itr = list.listIterator();//也是通过迭代器遍历 优点:指针可以向前、向后
        System.out.println(itr.next());//向前
        System.out.println(itr.previous());//向后
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

    }
}
2.3、实现商品价格从高到底排序
/**
 * 商品类
 */
class Shop {
    private int id; // 编号
    private String name; // 商品名称
    private int price; // 价格
    private int sales; // 销量
    // 全参 - 》 方便初始化
    public Shop(int id, String name, int price, int sales) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.sales = sales;
    }
    @Override
    public String toString() {
        return id + "\t" + name + "\t\t" + price + "\t" + sales;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price;
    }
    public int getSales() {
        return sales;
    }
    public void setSales(int sales) {
        this.sales = sales;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        List<Shop> list = new ArrayList<>();
        list.add(new Shop(1 , "Iphone14 Pro" , 8700 , 5000));
        list.add(new Shop(2 , "SK-II" , 2510 , 9000));
        list.add(new Shop(3 , "thinkpad x1 carbon" , 11000 , 4000));
        list.add(new Shop(4 , "天梭(TISSOT)瑞士手表" , 4350 , 1500));
        /*价格从高到底排序,所以要用比较器
        * 1、Compatable - > 和类进行绑定的 , 必须实现接口
        * 2、Comparator  -> 不和类进行绑定,很灵活
        * */
        // 工具类Collections (集合工具类)
        // 写法一:传统工艺写法。 1.8之前大家都是这样用的
      /*  Collections.sort(list, (Shop o1, Shop o2) -> {
                return o2.getPrice() - o1.getPrice();
            }
        );*/
        System.out.println("编号\t\t名称\t\t价格\t\t销量");
        // 写法二:不使用工具类。 1.8 - 》 Lambda , 流(链式写法- 》  对象.方法.方法.方法....)
        // 返回此集合中元素的顺序流(集合->流)
        list.stream().sorted(( o1,  o2) ->  o2.getPrice() - o1.getPrice()).forEach(System.out::println);
        System.out.println("------------------------------------------------------------------------");
        // 从低到高
        list.stream().sorted(( o1,  o2) ->  o1.getPrice() - o2.getPrice()).forEach(System.out::println);
    }
}
2.4、定义一个方法conver 能实现小转大,大转小
public class Demo2 {
    public static void main(String[] args) {
        // 2、定义一个方法conver 能实现小转大,大转小
        String str = "AbCd";
        // 选中一段代码,自动生成方法定义: alt+ctrl+m
        String converStr = convert(str);
        System.out.println("转换前:" + str);
        System.out.println("转换后:" + converStr);
    }
    // 快速修改方法名: shift + f6
    private static String convert(String str) {
        char[] chars = str.toCharArray(); // 把字符串打碎,转换为char数组。
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i]; //  通过索引访问数组元素,存到字符c里面
            // 怎么知道是大写还是小写呢?
            // 推断(两种方式): 如果c是大写的,那么和c的大写形式进行比较,true就是大写,否则就是小写
            //                  如果c是小写的,那么和c的小写形式进行比较,trur就是小写,否则就是大写
            // public static char toUpperCase(char ch) // 把字符转大写
            if (c == Character.toUpperCase(c)) {
                // 证明c是大写的
                // c已经是大写的,转小写
                chars[i] = Character.toLowerCase(c);
            }  else {
                chars[i] = Character.toUpperCase(c);
            }
        }
        // 把char数组转换为String (新知识点!)
        // String.valueOf: 返回包含字符数组的字符的字符串。
        String converStr = String.valueOf(chars);
        return converStr;
    }
}

3、项目实战

3.1、吃货联盟
public static void main(String[] args) {
    // TODO Auto-generated method stub
    Scanner input = new Scanner(System.in);
    // 初始化3个数组存储菜品、价格、点赞数
    String[] dishNames = { "红烧带鱼", "鱼香肉丝", "时令蔬菜" };
    double[] prices = { 38.0, 20.0, 10.0, };
    int[] praiseNums = new int[5];
    // 在初始化6个数组用于存储订单信息
    String[] names = new String[5];// 保存订餐人姓名
    String[] dishMegs = new String[5];// 保存所选菜品名
    int[] dishNums = new int[5];// 保存所选菜品份数
    int[] times = new int[5];// 保存送餐时间
    String[] addresses = new String[5];// 保存送餐地址
    int[] states = new int[5];// 保存订单状态:0表示已预订,1表示已完成
    double[] sumPrices = new double[5];// 保存订单的总金额

    // 初始化2条订单信息
    // 第1条
    names[0] = "张三";
    dishMegs[0] = "鱼香肉丝";
    dishNums[0] = 2;
    times[0] = 12;
    addresses[0] = "成华区建设路";
    states[0] = 0;
    sumPrices[0] = 40.0;
    // 第2条
    names[1] = "李四";
    dishMegs[1] = "时令蔬菜";
    dishNums[1] = 1;
    times[1] = 19;
    addresses[1] = "成都东站";
    states[1] = 1;
    sumPrices[1] = 10.0;
     /* 实现菜单切换 */
    System.out.println("欢迎使用“吃货联盟订餐系统”");
     int num = 1;// 用户输入0返回主菜单否则退出系统
     boolean isExit = false;// 标记用户是否退出系统,true为退出
     // 循环控制用户输入选择并执行
		do {
         System.out.println("************************");
         System.out.println("1、我要订餐");
         System.out.println("2、查看餐袋");
         System.out.println("3、签收订单");
         System.out.println("4、删除订单");
         System.out.println("5、我要点赞");
         System.out.println("6、退出系统");
         System.out.println("************************");
         System.out.print("请选择:");
         num = input.nextInt();// 用户输入所要选择进行的操作


         switch (num) {
             case 1:// 我要订餐
                 System.out.println("***我要订餐***");
                 boolean isAdd = false;// 记录是否可以点餐
                 for (int i = 0; i < names.length; i++) {
                     if (names[i] == null) {
                         isAdd = false;
                         System.out.print("请输入订餐人姓名:");
                         String name = input.next();
                         System.out.println("序号" + "\t" + "菜名" + "\t\t\t" + "单价" + "\t\t" + "点赞数");
                         for (int j = 0; j < dishNames.length; j++) {
                             System.out.println(
                                     (j + 1) + "\t" + dishNames[j] + "\t\t" + prices[j] + "元" + "\t" + praiseNums[j]);
                         }
                         System.out.print("请选择您要点的菜品编号:");
                         int chooseDish = input.nextInt();
                         String dishMeg = dishNames[chooseDish - 1];
                         System.out.print("请选择您需要的份数:");
                         int number = input.nextInt();
                         System.out.print("请输入送餐时间(送餐时间是10点至20点间整点送餐):");
                         int time = input.nextInt();
                         while (time < 10 || time > 20) {
                             System.out.print("您的输入有误,请输入10-20间的整数");
                             time = input.nextInt();
                         }
                         System.out.print("请输入送餐地址:");
                         String address = input.next();
                         // 默认可以预定,不用写订单状态
                         System.out.println("订餐成功!");
                         System.out.println("您定的是:" + dishNames[chooseDish - 1] + number + "份");
                         System.out.println("送餐时间:" + time + "点");
                         double sumPrice = prices[chooseDish - 1] * number;// 计算餐费
                         double sendMoney = (sumPrice >= 50) ? 0 : 5;// 计算送餐费
                         System.out.println("餐费:" + sumPrice + "元," + "送餐费" + sendMoney + "元," + "总计:"
                                 + (sumPrice + sendMoney) + "元。");
                         // 添加数据
                         names[i] = name;
                         dishMegs[i] = dishMeg;
                         times[i] = time;
                         dishNums[i] = number;
                         addresses[i] = address;
                         sumPrices[i] = sumPrice + sendMoney;
                         break;
                     }
                     if (isAdd) {
                         System.out.println("对不起,您的餐袋已满!");
                     }
                 }
                 break;
             case 2:// 查看餐袋
                 System.out.println("***查看餐袋***");
                 System.out.println("序号\t订餐人\t餐品名称\t餐品数量\t送餐时间\t送餐地址\t总金额\t订单状态");
                 for (int i = 0; i < names.length; i++) {
                     if (names[i] != null) {
                         String state = (states[i] == 0) ? "已预订" : "已完成";
                         System.out.println((i + 1) + "\t" + names[i] + "\t" + dishMegs[i] + "\t" + dishNums[i] + "份"
                                 + "\t" + times[i] + "\t" + addresses[i] + "\t" + sumPrices[i] + "元" + "\t" + state);
                     }
                 }
                 break;
             case 3:// 签收订单
                 System.out.println("***签收订单***");
                 boolean isSignFind = false;
                 System.out.print("请选择要签收的订单序号:");
                 int sign = input.nextInt();
                 for (int i = 0; i < names.length; i++) {
                     if (names[i] != null && states[i] == 0 && sign == i + 1) {
                         states[i] = 1;
                         System.out.println("订单签收成功!");
                         isSignFind = true;// 标记已找到的订单
                     } else if (names[i] != null && states[i] == 1 && sign == i + 1) {
                         System.out.println("您选择的订单已完成签收,不能再次签收!");
                         isSignFind = true;// 标记已找到的订单
                     }
                 }
                 if (!isSignFind) {
                     System.out.println("您选择的订单不存在!");
                 }
                 break;
             case 4:// 删除订单
                 System.out.println("***删除订单***");
                 boolean isDelFind = false;
                 System.out.print("请输入要删除的订单序号:");
                 int delID = input.nextInt();
                 for (int i = 0; i < names.length; i++) {
                     // 根据状态值判断能不能删除,只有已完成的才能删除
                     if (names[i] != null && states[i] == 1 && delID == i + 1) {
                         isDelFind = true;
                         for (int j = delID - 1; j < names.length - 1; j++) {
                             names[j] = names[j + 1];
                             dishMegs[j] = dishMegs[j + 1];
                             dishNums[j] = dishNums[j + 1];
                             times[j] = times[j + 1];
                             addresses[j] = addresses[j + 1];
                             states[j] = states[j + 1];
                             sumPrices[j] = sumPrices[j + 1];
                         }
                         // 将最后一位清空
                         names[names.length - 1] = null;
                         dishMegs[names.length - 1] = null;
                         dishNums[names.length - 1] = 0;
                         times[names.length - 1] = 0;
                         addresses[names.length - 1] = null;
                         states[names.length - 1] = 0;
                         sumPrices[names.length - 1] = 0;
                         System.out.println("删除订单成功!");
                         break;
                     } else if (names[i] != null && states[i] == 0 && delID == i + 1) {
                         isDelFind = true;
                         System.out.println("您选择的订单未签收,不能删除!");
                         break;
                     }
                 } // 未找到的订单不能删除
                 if (!isDelFind) {
                     System.out.println("您要删除的订单不存在!");
                 }
                 break;
             case 5:// 我要点赞
                 System.out.println("***我要点赞***");
                 // 显示菜品信息
                 System.out.println("序号\t菜名\t单价");
                 for (int i = 0; i < dishNames.length; i++) {
                     String priaiseNum = (praiseNums[i] > 0) ? praiseNums[i] + "赞" : "";
                     System.out.println((i + 1) + "\t" + dishNames[i] + "\t" + prices[i] + "元" + priaiseNum);
                 }
                 System.out.print("请选择您要点赞的菜品序号:");
                 int number = input.nextInt();
                 praiseNums[number - 1]++;
                 System.out.println("点赞成功");
                 break;
             case 6:// 退出系统
                 isExit = true;
                 break;
             default:// 退出系统
                 isExit = true;
                 break;
         }

         // 返回主界面
         if (!isExit) {
             System.out.print("输入0返回:");
             num = input.nextInt();
         } else {
             break;
         }
     } while (num == 0);
		System.out.println("谢谢惠顾,欢迎您再次使用!");
		input.close();
	}
}
3.2、ATM
/*
用户类
T000 这个类没设计好,应该把卡和用户进行分开
 */
public class User {
    private int cardid;
    private String uname;
    public String pwd;
    private double blance;
    public User(){

    }
    public User(int cardId,String uname,String pwd,double blance){
        this.cardid=cardId;//卡号
        this.uname=uname;//用户名
        this.pwd=pwd;//密码
        this.blance=blance;//余额
    }

    public int getCardid() {
        return cardid;
    }

    public void setCardid(int cardid) {
        this.cardid = cardid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public double getBlance() {
        return blance;
    }

    public void setBlance(double blance) {
        this.blance = blance;
    }

}
/*
这个一个接口
 */
public interface ATM {
    /**
     * 登录ATM
     * @param cardId 卡号
     * @param pwd 密码
     * @return true代表登录成功,false代表登录失败
     */
    public boolean login(int cardId,String pwd);

    /**
     * 存钱
     * @param m 存多少前
     */
    public void save(double m);

    /**
     * 取钱
     * @param m 取多少钱
     * @return true表示取款成功 false表示取款失败
     */
    public boolean qu(double m);
    //查询所有用户
    public void findAll();
    //查询余额
    public double find();
    /**
     * 转账
     * @param cardId 转账账户
     * @param uname 接受转账账户
     * @param m 转账金额
     * @return
     */
    public boolean transfer(int cardId, String uname, double m);

    /**
     *
     * @param newPwd 修改后的新密码
     * @return
     */
    public boolean savePwd(String newPwd);

}
/*
接口实现类
 */
public class ATMImplement implements ATM {
    //初始化3个账户,存放在集合里
    private List<User> list = new ArrayList<>();
    //当前登录的用户
    //登录成功后,
    private User currentUser;
    public static String bankName = "成都华夏银行";
    public ATMImplement() {
        //进行初始化
        User u1 = new User(001, "路飞", "123456", 2000);
        User u2 = new User(002, "露娜", "zhe111", 4500);
        User u3 = new User(003, "索隆", "zxc147", 5100);
        list.add(u1);//把账户添加在集合里
        list.add(u2);
        list.add(u3);
    }

    @Override
    public boolean login(int cardId, String pwd) {
        //默认false,登录失败
        boolean r = false;
        //登录就是将控制台输入的卡号和密码保存起来,然后在对所有账户对象进行比较,如果对的上就登录成功
        for (User user : list) {
            if (user.getCardid() == cardId && user.getPwd().equals(pwd)) {
                r = true;
                currentUser = user;//将登录成功的用户的地址赋值给currentUser
                break;
            }
        }
        return r;
    }

    @Override
    public void save( double m) {
        currentUser.setBlance(currentUser.getBlance()+m);
    }

    @Override
    public boolean qu( double m) {
        boolean r = false;
        if(m > currentUser.getBlance()){
            r = true;
            System.out.println("您的余额已不足");
        }else {
            currentUser.setBlance(currentUser.getBlance() -m);
            System.out.println("取款成功!!!");
        }
        return r;
    }

    @Override
    public void findAll() {
        //遍历所有账户集合
        for (User user:list) {
            StringBuilder builder = new StringBuilder();
            builder.append("卡号:").append(user.getCardid());
            builder.append("\t\t用户名:").append(user.getUname());
            builder.append("\t\t密码:").append(user.getPwd());
            builder.append("\t\t余额:").append(user.getBlance());
            System.out.println(builder.toString());
        }

    }

    @Override
    public double find() {
        double balance = currentUser.getBlance();
        return balance;
    }

    @Override
    public boolean transfer(int cardId, String uname, double m) {
        // -------------------以下操作是个原子操作(要么全部成功,要么全部失败,失败就要回滚),涉及到事务--------
        // 举个例子: 比如你刚把钱转出去,自己的账户已经扣除了.. 突然网络出问你了,系统卡死了...
        // 要判断余额是否满足  && 对方的账户是否存在!!
        // 自己账户减
        // 对方账号加
        // ------------------------------------------------------
        //判断余额是否满足
        if (m > currentUser.getBlance()) {//余额不足
            System.out.println("余额不足,转账失败");
            return false;
        }
        User targetUser = new User();//对方接收转账的账户对象
        for (User user : list) {
            if (user.getCardid() == cardId && user.getUname().equals(uname)) {
                targetUser = user;
                break;
            }
        }
        if(targetUser == null ){//如果转账用户为null,则返回false
            System.out.println("输入转账用户有误");
            return false;
        }
            currentUser.setBlance(currentUser.getBlance() - m);
            targetUser.setBlance(currentUser.getBlance() + m);

        return true;
    }

    @Override
    public boolean savePwd(String newPwd) {
        currentUser.pwd = newPwd;
        currentUser.setPwd(currentUser.pwd);
        return true;
    }

}
/*
测试类
 */
public class Test {
    public static <simpDateFormat> void main(String[] args) {
        Scanner input = new Scanner(System.in);
        ATM atm = new ATMImplement();//创建一个ATM对象
        atm.findAll();//登录前显示所有用户
        //开始登录,最多有3次机会
        int count = 3;//默认输入密码的次数只有3次
        do {
            count--;
            System.out.print("请输入银行卡:");
            int cardId = input.nextInt();
            System.out.print("请输入密码:");
            String pwd = input.next();
            boolean r = atm.login(cardId,pwd);//显示登录验证,如果为true表示登录成功
            if (r) {
                //进入系统,就进行下面的循环
                //只要没退卡就可以连续操作
                while (true){
                    System.out.println("=====================================");
                    System.out.println(" || 1.查询余额               5.修改密码 ||");
                    System.out.println(" || 2.取款                  6.退卡     ||");
                    System.out.println(" || 3.存款                            ||");
                    System.out.println(" || 4.转账                            ||");
                    System.out.println(" =====================================");
                    System.out.print("请选择要操作的号码:");
                    int n = input.nextInt();
                    if(n==1){//查询余额
                        double balance = atm.find();
                        System.out.println("余额有"+balance);
                    }else if(n==2){
                        System.out.print("请输入取款金额:");
                        double m = input.nextDouble();
                        boolean result = atm.qu(m);
                        double balance = atm.find();
                        if (result) {
                            System.out.println("=====================================");
                            System.out.println("|| 1.返回主界面             2.打印发票||");
                            System.out.println("=====================================");
                            // TODO
                            // 获取当前时间
                            System.out.print("请输入要操作的数字:");
                            int chooes = input.nextInt();
                            if(chooes==1){
                                return;
                            }else if(chooes==2){
                                System.out.println("当前余额"+balance);
                                Date d = new Date(); // 要显示 年月日:时分秒格式
                                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                String f = format.format(d);
                                System.out.println(f);
                            }
                        }
                    }else if(n==3){
                        System.out.print("请输入要存款的金额:");
                        double m = input.nextDouble();
                        atm.save(m);
                        System.out.println("哔!哔!哔! 正在放钞");
                        System.out.println("存款成功!!!");
                    }else if(n==4){
                        System.out.print("请输入要转账的用户:");
                        String uname = input.next();
                        System.out.print("请输入要转账的金额:");
                        double m = input.nextDouble();
                        boolean result = atm.transfer(cardId,uname,m);
                        if(result){
                            double balance = atm.find();
                            System.out.print("您是否确定转账给该用户(y/n):");
                            String chooes = input.next();
                            if(chooes.equalsIgnoreCase("y")){
                                System.out.println("转账成功");
                                System.out.println("当前余额"+balance);
                            }else if(chooes.equalsIgnoreCase("n")){
                                System.out.println("放弃转账");
                                break;
                            }
                        }
                    }else if(n==5){
                        System.out.print("请输入六位的密码:");
                        String newPwd = input.next();
                        boolean re =atm.savePwd(newPwd);
                        if(re){
                            if(pwd.equals(newPwd)){
                                System.out.println("修改后的密码不可与当前密码一样");
                            }else if(newPwd == null || newPwd.length() != 6){
                                System.out.println("输入密码有误!");
                            }else {
                                System.out.println("修改成功");
                                    count = 3;
                                    break;

                            }
                        }


                    }else if(n==6){
                        System.out.println("欢迎下次使用");
                    }else if(n <= 0 || n > 6){
                        System.out.println("输入有误,请重写输入");
                        continue;
                    }
                }
            } else {
                if (count > 0) {
                    System.out.println("还有" + count + "次机会");
                } else {
                    System.out.println("卡已锁定,请到人工咨询处处理");
                }
            }
        }while (count > 0 );
    }
}
4、难点补充
4.1、谈接口与抽象类的区别?

封装:抽取对象共有的属性,然后编写类

类是抽象的,对象就具体的

//抽象类和接口的区别
//抽象类必须要有一个子类来继承,通过子类来创建实例(继承单一继承)
//接口一个类可以实现多个接口,弥补单一继承的问题
//抽象类继承体系 -》is - a -> 名词
//接口实例体系(能力) -》has -a -> 动词
abstract class Vei{
    //交通工具
}
class Car extends Vei implements Run,Fir{
    //汽车属于交通工具,汽车是交通工具
    @Override
    public void run() {
        System.out.println("地上跑");
    }
    @Override
    public void fir() {
        System.out.println("天上跑");
    }
}
interface Run{
    //运输,是交通工具的一种能力
    void run();
}
interface Fir{
   void fir();
}
public class Demo {

}
4.2、调用函数时,形参的改变能否影响实参?用实验证明您的观点
4.2.1、形参:方法定义的参数就是形参
4.2.2、实参:调用方法传参的实际值就是实参
public class Demo2 {
    //值传递
    public static void f1(int i/*形参*/){
        i++;
        System.out.println(i);//打印输出21
    }
    //引用传递,nums拿到的是地址
    public static void f2(int[] nums/*形参*/){
        //nums[0] = 999;//改的是形参

        //new关键字是在堆区重新开辟一块新的空间
        nums = new int[3];
        nums[0] = 999;
        nums[1] = 888;
        nums[2] = 777;
        System.out.println(Arrays.toString(nums));
    }
    //引用传递(String除外)
    public static void f3(Car c){
        c.color= "白色";
        System.out.println("汽车的颜色是:"+c.color);
    }
    public static void main(String[] args) {
        int num = 20;
        f1(num/*实参*/);
        System.out.println(num);//打印输出20

        //存的是地址
        int[] nums = {3,1,2};
        f2(nums/*实参*/);
        System.out.println(Arrays.toString(nums));

        Car c = new Car();
        c.color = "黑色";
        f3(c);
        System.out.println("汽车的颜色是:"+c.color);

    }
4.3、什么是多态
    // 参数类型是汽车,  汽车加速 , c 运行的时候类型是不是就是飞机。。。。
    // 动态是java中最重要的特性 - 》  -》
    // 动态绑定机制
    // 有了多态- 》 抽象工厂(造对象)的时候,就特别的方便
    public static void f1(Vei c) {
        c.up(); // 调用方法产生不同的结果了。
    }
    // 传子类对象
    public static void f2(抽象类类型 参数) {
    }
    // 传实现类对象
    public static void f2(接口类型 参数) {
    }
    public static void main(String[] args) {
        // 向上转型
        f1(new Car());
    }
4.4、log4j日志框架记录错误信息到本地

操作步骤:1)拷贝3个jar包到我们的工程里面。

​ 2)编写核心配置 log4j.properties

​ 3)创建日志记录对象Logger

# 格式非常的统一   key = 值
log4j.rootLogger=DEBUG,console,file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 控制台的内容格式
log4j.appender.console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss} method:%l%n%m%n
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=D://logs/log.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
# 控制文件里面的内容格式
log4j.appender.file.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss} method:%l%n%m%n
4.5、junit单元测试框架
4.5.框架的基本含义

已经写好的一套程序,我们只管引入进来用就可以了!java中的框架大多是以.jar包发布的。

  • Spring框架
  • Mybatis框架
  • Hibernate框架
4.5.2、Junit单元测试框架的作用
  • 系统上线前,要做大量的测试 。(内测1个月时间)

  • 接口写好了,可以来测试接口是否完整。

  • 如果一个程序没有经过大量的测试,几乎不可能正常运行

    @Test 用它标记的方法,就是一个程序入口了!!

    @Before 用它标记的方法,再程序入口执行前执行!

    @After 用它标记的方法,再程序入口执行完后执行!

    import org.junit.After;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    public class Demo1 {
        String name;
        /* show2方法之前执行 */
        @Before
        public void show1() {  // 1
            name = "张三";
            System.out.println("show1方法执行了");
        }
        @After
        public void show3() {  // 3
            System.out.println("show3方法执行了!");
        }
        // 数据库 , 再查询之前要先创建一个连接管道
        // 使用注解: @注解名
        @Test
        public void show2() {  // 这个方法必须是public的,不带参数的  // 2
            System.out.println("show2");
            System.out.println(name);
            // 断言 - 》可以做出判断, 如果为true给你一个绿色的勾勾 , false红色叉叉
            // 判断对象是否为空
            String str = null;
            Assert.assertNull(str); // 判断某个对象是不是为null,为null就true
            //Assert.assertNotNull(str); // 判断某个对象是不是不为null
            Assert.assertTrue(5 > 3); // 判断表达式是否为真,为真就正确
            Assert.assertEquals("abc" , "abcd");
        }
    }
    

5、面试题

5.1、每个字符出现的次数
需求:

1)定义一个需要被统计字符的字符串
2)将字符串转换成字符数组
3)定义Map集合,用来存储字符串中字符以及字符出现的次数
4)遍历字符数组获取每一个字符,并将字符存储在双列集合中,存储过程中要做判断,如果集合中不包含这个键,就将该字符当做键,值为1存储,如果集合中包含这个键,就将值加1存储
5)打印Map集合获取字符出现的次数

public class Demo5 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String tx = input.next();
        HashMap<Character, Integer> countMap = count(tx);
        for (Map.Entry<Character, Integer> row : countMap.entrySet()) {
            System.out.println("字符" + row.getKey() + "出现的次数是:" + row.getValue());
        }
        /* 新的需求:统计出现字符次数最多的哪个字符!!  找最大值,擂台算法 */
    }
    // 字符串统计出现个的数,返回一个HashMap,key是字符,value是次数
    private static HashMap<Character , Integer> count(String tx  /*假设:abc*/) {
        HashMap<Character, Integer> map = new HashMap<>();
        char[] chars = tx.toCharArray();    // 把字符串转换为char数组,方便统计
        for (char c : chars) { // 遍历数组
            Integer num = map.get(c); // 根据字符获取对应的value,当然如果第一次循环,集合里面是空的,所以拿出来的num为null
            if (num == null) {
                map.put(c , 1); // 存
            } else {
                map.put(c , num+1);
            }
        }
        return map;
    }
}
5.2、如何保证类的对象有且只有一个【使用单列模式】
5.2.1、饿汉式 (直接new)
/**
 * Created by ${wuyupku} on 2019/3/15 12:39
 */
 class Singleton01{
    private static Singleton01  modle = new Singleton01();//声明对象同时私有化
    private Singleton01(){}//构造函数私有化
    public static Singleton01 getInstance(){//向外声明访问该类对象的方法
        return modle;
    }

}
5.2.2、懒汉式 (再方法里面再来new)
/**
 * Created by ${wuyupku} on 2019/3/15 12:43
 */
 class Singleton02 {
    private static Singleton02 modle = null;//声明对象,不实例化

    private Singleton02()  {}//构造函数私有化
    public static Singleton02 getInstance(){//向外提供访问该类对象的方法
        if (modle == null)
            modle = new Singleton02();
            return modle;

    }
}
5.2.3、设计模式(共24个)
1、工厂模式:造对象
2、代理:横切面问题
3、单例:类只有一个对象
4、装饰:对象包装
5、观察者:事件驱动
// 无论如何,Person类只能产生一个对象
// 外部人家想new就new了, 一旦new就创建了新的对象了
// 问题第一层:  阻止外部new  , 就是不要外部来调用构造方法!!
// 问题第二层:既然外部都不能创建对象了,那么只能由类来自己控制
/**
 * 单例设计模式
 * 原理:把构造方法私有化,然后内部提供一个内类对象的实例,有且只有1个
 */
public class Person {
    // 要造多少个对象由Person来决定
    // static 还有映像不?
    static Person p = new Person();
    // 当把构造方法修饰为private后,外部就不能再创建该类对象了!!
    private Person() {
    }
}
Person p = Person.p;
Person p2 = Person.p;
System.out.println(p == p2);
// 无论如何,Person类只能产生一个对象
// 外部人家像new就new了, 一旦new就创建了新的对象了
// 问题第一层:  阻止外部new  , 就是不要外部来调用构造方法!!
// 问题第二层:既然外部都不能创建对象了,那么只能由类来自己控制
/**
 * 单例设计模式 - >  饿汉式  .
 * 比我这个方法还要好的话:詹姆斯.高斯林 提供了一种解决方案 , Enum枚举来实现!
 * 原理:把构造方法私有化,然后内部提供一个内类对象的实例,有且只有1个
 */
public class Person {
    // 要造多少个对象由Person来决定
    // static 还有映像不?
   private static Person p = new Person();
   // getter  - > 这种方式是最好了,因为了减少了麻烦
    // 饿汉式
    public static Person getPerson() {
        return p;
    }
    // 懒汉式
    public static Person getPerson2() {
        // TODO 多个线程同时访问,拿到的p是不同的!!
        if (p == null) {
            p = new Person();
        }
        return p;
    }
    // 当把构造方法修饰为private后,外部就不能再创建该类对象了!!
    private Person() {
    }
}
public class Dem4 {
    public static void main(String[] args) {
        //new就创建一个对象
        //Person p1 = new Person();private之后new就报错了
        //阻止外部new,一new就报错
        Person p1 = Person.getPerson();//通过类名调用对象属性
        Person p2 = Person.getPerson();
		System.out.println(p == p2);
    }
}
5.3、final的作用,尤其是修饰高级类型对象时,什么可变什么不可变?【对象地址可不变,堆区值可变】
5.3.1、final修饰的变量就是常量,不可变
public class Demo5 {
    public static void main(String[] args) {
        //1年12个月
        final int month = 12;//基本数据类型不可变
        /**
         * 引用数据类型比较特殊
         * 英语数据类型的对象,地址不可变,里面存储的值可以变
         */

        final int[] nums = new int[3];
        //nums的值不可变
        nums = new int[5];
        //同时又可以变
        nums[0]=999;
    }
}
5.3.2、final优点【不可大量使用】

​ final修饰的方法,还有属性变量等,会被JVM进行优化,效率更高

​ final修饰的属性变量等,可以防止线程数据安全等问题

5.3.3、final修饰的类不能继承
5.3.4、final修饰的方法不可被重写
5.4、排序为什么要用比较器,比较器中重写的那个方法会被触发1次还是多次?该片段对整个排序有何作用?
5.4.1、什么是比较器

​ Comparable、Comparator

5.4.2、为什么要使用比较器

​ 1、首先比较器的目的是为了排序而生, 当有多个对象排序才显得比较有意义。

​ 2、如果不用比较器,我们每一套业务场景,比如学生要按照年龄排序,商品要按照价格进行排序,员工要按照岗位级别及进行排序, 这种情况都需要自己编写一套排序算法,然后还要进行大量的交换位置操作,并且代码还不能复用,非常不利用团队协作。

​ 3、比较器出现的价值,就是为了解决不同业务场景下排序复用的问题,因为比较器内部把排序的交换等核心算法已经封装好了, 我们只管填充排序的业务逻辑即可,比如学生就按照年龄进行比较,商品就按照价格进行比较。

​ 4、最后比较器因为是接口,所以我们可以编写多套排序场景的实现,根据场景的不同进行切换,这样就非常有利于程序的可扩展性。

5.4.5、==、equal()、hashcode()区别

==:数值判断

equal():字符判断

hashcode():获取哈希码,根据关键码值(Key value)而直接进行访问的数据结构

5.4.6、String的hashcode()原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhkNi0UN-1664470090398)(E:\笔记\第四周\202209132235372.png)]

5.4.7、try catch语句里面写return会不会继续执行,finally呢?

finally:是必须执行的

// TODO 多个线程同时访问,拿到的p是不同的!!
    if (p == null) {
        p = new Person();
    }
    return p;
}
// 当把构造方法修饰为private后,外部就不能再创建该类对象了!!
private Person() {
}

}
public class Dem4 {
public static void main(String[] args) {
//new就创建一个对象
//Person p1 = new Person();private之后new就报错了
//阻止外部new,一new就报错
Person p1 = Person.getPerson();//通过类名调用对象属性
Person p2 = Person.getPerson();
System.out.println(p == p2);
}
}




#### 5.3、final的作用,尤其是修饰高级类型对象时,什么可变什么不可变?【对象地址可不变,堆区值可变】

##### 5.3.1、final修饰的变量就是常量,不可变

```Java
public class Demo5 {
    public static void main(String[] args) {
        //1年12个月
        final int month = 12;//基本数据类型不可变
        /**
         * 引用数据类型比较特殊
         * 英语数据类型的对象,地址不可变,里面存储的值可以变
         */

        final int[] nums = new int[3];
        //nums的值不可变
        nums = new int[5];
        //同时又可以变
        nums[0]=999;
    }
}
5.3.2、final优点【不可大量使用】

​ final修饰的方法,还有属性变量等,会被JVM进行优化,效率更高

​ final修饰的属性变量等,可以防止线程数据安全等问题

5.3.3、final修饰的类不能继承
5.3.4、final修饰的方法不可被重写
5.4、排序为什么要用比较器,比较器中重写的那个方法会被触发1次还是多次?该片段对整个排序有何作用?
5.4.1、什么是比较器

​ Comparable、Comparator

5.4.2、为什么要使用比较器

​ 1、首先比较器的目的是为了排序而生, 当有多个对象排序才显得比较有意义。

​ 2、如果不用比较器,我们每一套业务场景,比如学生要按照年龄排序,商品要按照价格进行排序,员工要按照岗位级别及进行排序, 这种情况都需要自己编写一套排序算法,然后还要进行大量的交换位置操作,并且代码还不能复用,非常不利用团队协作。

​ 3、比较器出现的价值,就是为了解决不同业务场景下排序复用的问题,因为比较器内部把排序的交换等核心算法已经封装好了, 我们只管填充排序的业务逻辑即可,比如学生就按照年龄进行比较,商品就按照价格进行比较。

​ 4、最后比较器因为是接口,所以我们可以编写多套排序场景的实现,根据场景的不同进行切换,这样就非常有利于程序的可扩展性。

5.4.5、==、equal()、hashcode()区别

==:数值判断

equal():字符判断

hashcode():获取哈希码,根据关键码值(Key value)而直接进行访问的数据结构

5.4.6、String的hashcode()原理
5.4.7、try catch语句里面写return会不会继续执行,finally呢?

finally:是必须执行的

try catch:在try或catch中的return语句会将它的返回值压入栈内,然后执行finally语句,当finally执行完成后,若finally语句里有return语句,则执行return语句并结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值