新职课(chapter4)

常用类库

  • 常用的库:握字符串、时间、集合、IO、多线程、网络编程、XML、JSON 等

泛型

  • 将参数的类型也设置成可变参数
  • 提高代码复用率,泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)

泛型类

  • 类名后跟上泛型参数
    public class Person<T> {
        private int age;
        private String name;
        private T city; // 由泛型决定
    
        public T getCity() {
            return city;
        }
    
        public void setCity(T city) {
            this.city = city;
        }
    }
    
    public class PersonTest {
        public static void main(String[] args) {
            Person<String> p = new Person<>();    // 后面的<String>可省略
            p.setCity("北京");	// city属性在此对象中被设置为String类型
        }
    }
    
  • 类似的,还有泛型接口
    public interface Students<T> {
        T getData();    // 返回值类型为T,默认abstract
    }
    
    public class StudentsTest implements Students<String> { // 也可以不指定类型,直接使用<T>
        private String text;    // 这里也用<T>
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    
        @Override
        public String getData() {   // 这里也用<T>
            return text;
        }
    }
    
  • 实例化或继承时指明类型
  • 继承接口时可以不指定类型,因为实现接口的类还需要实例化

泛型方法

  • 形如:private static <T> T 方法名(T a, T b) {}
    public class PersonTest {
        public static void main(String[] args) {
            show("Roy");	// 根据传入参数类型而定
        }
    
        public static <T> void show(T a){	// 前面的<T>不能省略
            System.out.println(a);
        }
    }
    
  • 声明泛型,传泛型参数

泛型限制

  • 在使用泛型时, 可以指定泛型的区域(范围)
  • 例如:必须是xx类的子类或xx接口的实现类
    public class PersonTest {
        JiaFeiMao<Cat> cat = new JiaFeiMao<>();
    }
    
    interface Animals {}
    
    class Cat implements Animals{}
    
    class JiaFeiMao<T extends Animals>{	// 泛型包含的类型必须是动物类的子类,这里只能用Cat
        T color;
    }
    

通配符

  • 除了在定义时限制泛型的范围,还可以通过通配符?规定上限和下限
    public class PersonTest {
    	// 这里?就是泛型,但是必须是Animals的子类,即规定了上限
        JiaFeiMao<? extends Animals> cat = new JiaFeiMao<Cat>();	// 后面的Cat可以不写
        // 必须是Cat的父类,即规定了下限
        JiaFeiMao<? super Cat> dog = new JiaFeiMao<Animals>();	// 后面的Animals可以不写
    }
    
    interface Animals {}
    
    class Cat implements Animals{}
    
    class JiaFeiMao<T>{
        T color;
    }
    
  • 如果只指定?,即没有限制泛型类型,相当于普通的<T>,一旦实例化就确定了对象的类型(不变)
  • 如果规定了上下限,例如Object,则实例化的对象可以是其子类(变)
  • 小结:最常用的还是泛型类

Objects

  • java.lang.Object是所有类的父类,他的一些子类定义的方法我们很常用,称之为核心类库
  • 第一个:java.util.Objects
  • 查阅API文档可以发现:public final class Objects
  • 此类包含static实用程序方法,用于操作对象或在操作前检查某些条件
    public Foo(Bar bar, Baz baz) {
         this.bar = Objects.requireNonNull(bar, "bar must not be null");
         this.baz = Objects.requireNonNull(baz, "baz must not be null");
    }
    // 或者可以抛出异常
    

Math

  • public final class Math,静态运算方法
    public static void main(String[] args) {
        System.out.println(Math.max(100,200));  // 两个参数
        System.out.println(Math.round(-100.2)); // -100 四舍五入(大)
        System.out.println(Math.floor(4.5));    // 小于参数的最大整数 4.0
    }
    
  • 遇到运算先别慌,Math用着才更香

Arrays

  • java.util.Arrays,该类包含用于操作数组的各种方法(例如排序和搜索)。 此类还包含一个静态工厂,允许将数组视为列表
    public static void main(String[] args) {
        int[] arr1 = {2,4,8,9,78,7,15};
        for (int i=0; i<7; i++){
            System.out.println(arr1[i]);    // 比较麻烦
        }
        Arrays.sort(arr1);  // 原对象操作,默认升序
        System.out.println(Arrays.toString(arr1));  // [2, 4, 7, 8, 9, 15, 78] 字符串
        arr1 = Arrays.copyOf(arr1, 15); // 扩容为15(newLength)
        System.out.println(arr1.length);    // 15
    }
    
  • 这些方法都比较常用

BigDecimal

  • java.lang.Number的子类java.math.BigDecimal,不可变的,任意精度的带符号十进制数(最高精度)
    public static void main(String[] args) {
        // float同类型和double类型运算时会有误差
        System.out.println(0.1+0.2);    // 0.30000000000000004
        BigDecimal b1 = new BigDecimal("0.13");
        BigDecimal b2 = new BigDecimal("0.2");
        BigDecimal b3 = b1.add(b2);
        BigDecimal b4 = b1.subtract(b2);
        System.out.println(b3); // 0.33
        System.out.println(b4); // -0.07
    }
    

Date

  • Date类表示特定的时刻,精度为毫秒
  • 为了更符合国际化规范,Calendar类应该用于在日期和时间字段之间进行转换,而DateFormat类应该用于格式化和解析日期字符串

DateFormat

  • java.text.DateFormat 日期/时间格式化子类的抽象类
  • 作用:格式化和分析日期或时间,主要是这个格式,使用format方法
    public static void main(String[] args) {
        SimpleDateFormat s = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");  // M和H大写
        String time = s.format(new Date());
        System.out.println(time);   // 2021年09月03日 23:26:38
        
        Date date = null;
        try {
            // 解析,返回Date
            date = s.parse("2021年8月18日 18:23:34");  // 1629282214000
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println(date.getTime());
    }
    

Calendar

  • Calendar的getInstance()方法返回一个Calendar对象,其日历字段已使用当前日期和时间初始化
    public static void main(String[] args){
        Calendar c1 = Calendar.getInstance();   // 不是new
        int year = c1.get(Calendar.YEAR);   // 这个YEAR是一个全局常量,存的是一个数组下标,获取年
        System.out.println(year);  // 2021
        c1.set(Calendar.YEAR, 2022);    // 也可以设置
        int day = c1.get(Calendar.DAY_OF_YEAR); // 今天是今年的第几天
        System.out.println(day);
    
        c1.add(Calendar.MONTH, 2);
        System.out.println(c1.get(Calendar.MONTH)); // 10   现在是9月,但月份是从0-11,玛德
        // 注:后续操作是在之前更改的对象的基础上,即直接操作原数据
    
        Date d = c1.getTime();  // 转换成Date对象
        System.out.println(d.getTime());    // 1667530255970
    }
    

System

  • System类提供的设施包括标准输入,标准输出和错误输出流; 访问外部定义的属性和环境变量; 加载文件和库的方法; 以及用于快速复制阵列的一部分的实用方法
    Scanner input = new Scanner(System.in);
    
    // 返回当前时间戳
    currentTimeMillis()
    // 将指定源数组中的数组从指定位置开始复制到目标数组的指定位置
    arraycopy​(Object src, int srcPos, Object dest, int destPos, int length)
    
  • 详细查看API文档

String

  • 字符串属于“不可变类型”,不论是否为对象(值或引用),不可变意味着修改时只会产生新的内容(返回新的对象)
  • 之前了解了栈、堆、方法区
    • 方法区存储类、方法的定义,因为只需要实例化时加载一次(一套模型、多个实例)
    • 堆在逻辑上分为三类
      • 新生代
      • 老年代
      • 永久代:不会GC
  • 字符串常量池还是存放在方法区,JDK1.8以前使用永久代实现方法区(注意这个关系)
    • 1.8用元空间代替了永久代,常量池还是在方法区
    • 既然放在了方法区,而且String对象是不可变的,所以可以共享它们
      String str = "abc";	// 最常用的构造方法
      // 等价于
      char data[] = {'a', 'b', 'c'};
      String str = new String(data);
      
  • 从存放的位置和可变类型来理解数据类型和结构,更深入!
  • 一般可变类型的实例(都是对象),存放在堆的新生代到老年代,用栈指针指向

常用方法

  • 根据API文档
  • 字符串拼接
    • 最常见的可能是+,但要注意,这种操作会产生很多垃圾,因为拼接后并不会回收各个字符串,字符串常量池中无用变量太多
    • 如何操作更优雅呢?用两个常见类
  • StringBuffer
    • 线程安全,可变的字符序列。 字符串缓冲区类似于String ,但可以进行修改
    • 在任何时间点它都包含一些特定的字符序列,但序列的长度和内容可以通过某些方法调用来改变
    • 根据API文档,有很多种构造方法
      StringBuffer() 	// 构造一个字符串缓冲区,其中没有字符,初始容量为16个字符。  
      StringBuffer(int capacity) 	// 构造一个字符串缓冲区,其中没有字符和指定的初始容量。  
      StringBuffer(CharSequence seq) 	// 构造一个字符串缓冲区,其中包含与指定的 CharSequence相同的字符。  
      StringBuffer(String str) 	// 构造一个初始化为指定字符串内容的字符串缓冲区。  
      
    • 常用的方法是append()和转为StringtoString()
  • StringBuilder
    • 一个可变的字符序列。 此类提供与StringBuffer兼容的API,但不保证同步(所以更快)
    • 此类设计用作StringBuffer替代品,用于单个线程使用字符串缓冲区的位置
    • 常用方法相同
  • 任务训练
    package com.strings;
    
    public class StringFunc {
        /**
         *
         */
        // 翻转一句话中每个单词
        public String revStr(String str){
            String[] arr = str.split(" ");  // 空格分隔开的每个单词
            StringBuffer newStr = new StringBuffer();
            for (String s : arr){
                StringBuffer i = new StringBuffer(s);
                i.reverse();
                newStr.append(i).append(" ");
            }
            return newStr.toString();
        }
    
        // 将字符串信息存放到对应的类中
        // String s="name=王五 age=18 classNum=1101";
        public void stuInfo(){
            Student stu = new Student();
            String s="name=王五 age=18 classNum=1101";
            String[] arr = s.split(" ");
            for (String i : arr){
                String[] j = i.split("=");
                if (j[0].equals("name")){
                    stu.setName(j[1]);
                }else if (j[0].equals("age")){
                    stu.setAge(Integer.valueOf(j[1]));  // 从字符串提取数字 包装成Integer
                }else if (j[0].equals("classNum")){
                    stu.setClassNum(Integer.valueOf(j[1]));
                }else{
                    stu.setName("Roy");
                    stu.setAge(18);
                    stu.setClassNum(666);
                }
            }
            System.out.println(stu.toString());
        }
    
        // 字符串压缩 字符串“aabccdd”会变成“a2b1c2d2”
        public String comStr(String s){
            /*
            算法思想:两个标志位,ch比较当前字符,count计数当前字符;
            ch如果发现跟前一个字符不一样,ch变化,计数清零
             */
            // 校验
            if (s.equals("")){
                return s;
            }
            StringBuffer sb = new StringBuffer();
            char ch = s.charAt(0);    // 当前检测的字符
            int count = 0;  // 压缩字符个数
            for (int i = 0; i < s.length(); i++) {
                if (ch == s.charAt(i)){
                    count++;
                }else {
                    // 上一个字符的情况保存
                    sb.append(ch);
                    sb.append(count);
                    ch = s.charAt(i);
                    count=1;
                }
            }
            // 追加最后一个
            sb.append(ch);
            sb.append(count);
            return sb.length()>s.length()?s:sb.toString();  // 如果压缩完更长久不用压缩了
        }
    
        // 写一个和 trim 功能相同的方法,即去掉字符串前后的空格
        public String myTrim(String s){
            /*
            思路:从前从后遍历,找到第一个不是空格的字符串,记录索引并截取!
             */
            int idx1 = 0;
            int idx2 = s.length()-1;
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i) != ' '){
                    idx1 = i;
                    break;
                }
            }
            for (int i = s.length()-1; i >= 0; i--) {
                if (s.charAt(i) != ' '){
                    idx2 = i;
                    break;
                }
            }
            return s.substring(idx1, idx2+1);   // 取头不取尾
        }
    
        public static void main(String[] args) {
            // 主函数中测试
            StringFunc funcs = new StringFunc();
            String s = "I am a handsome boy";
            String result = funcs.revStr(s);
            System.out.println(result); // I ma a emosdnah yob
            // funcs.stuInfo();
            // System.out.println(funcs.comStr("aabbbbccdd")); // a2b4c2d2
            // System.out.println(funcs.myTrim("   roy  handsome  "));
        }
    }
    

集合

  • 类集,可以认为是核心类库最重要的部分,实现了很多常用的数据结构
    • 如果将语言的数据类型分为两种:基本数据类型、数据结构
    • Java中的基本类型包括Int/Double/String/Array等
    • 为了避免动态扩容等操作,实现了数据结构,类似Python中的列表,字典,迭代器等
  • 其中最常用的接口:集合Collections、Map映射、Iterator迭代器

List接口

  • Collection的子接口,有更多的操作方法
    public class Main {
        public static void main(String[] args) {
        	// 接口的实现类:ArrayList
            List<Integer> data = new ArrayList<>();
            data.add(1);
            data.add(2);
            data.get(0);    // 根据index获取
            data.remove(0); // 根据index删除
            System.out.println(data.size());
        }
        // get 和 remove 方法重写了,根据索引进行
        // 重载实现于一个类中;重写实现于子类中
    }
    
  • ArrayList(用的最多,基于动态数组)、Vector、LinkedList是List的常用实现类

ArrayList

  • 底层使用数组实现,当容量满时,使用grow方法动态扩容
  • 查找快,增删慢,要注意
  • 有一个动态扩容的算法,每次扩容到原来的1.5倍(移位运算);0和1时需要特殊考虑
    public class Main {
        public static void main(String[] args) {
            // 如果不指定初始化大小,默认10
            ArrayList<Integer> data = new ArrayList<>();
            System.out.println(data.size());    // 0
            // 添加int时会自动装箱
            data.add(1);    // 添加时会动态扩容
            data.get(0);    // index
        }
    }
    
  • Vector,类似ArrayList,但它通过维护capacity和capacityIncrement来优化存储管理,而不是动态算法(Vector比较简单)
  • 如果没有定义Increment,每次翻倍扩容
    public class Main {
        public static void main(String[] args) {
            // 如果不指定初始化大小,默认10
            Vector<Integer> data = new Vector<>();
            System.out.println(data.size());    // 0
            data.add(1);
            data.add(10);
            System.out.println(data.size());    // 0
            System.out.println(data.get(1));    // 10
        }
    }
    

LinkedList

  • 使用双向链表结构,增删快,查找慢
  • 里面封装了链表的各种操作方法,但是不常用
    public class Main {
        public static void main(String[] args) {
            //
            LinkedList<Integer> stack = new LinkedList<>();
            stack.addFirst(1);
            stack.removeFirst();    // 先进后出
            stack.push(2);
            stack.pop();    // 相当于上面的两个方法
            LinkedList<Integer> queue = new LinkedList<>();
            queue.addFirst(1);
            queue.removeLast();     // 先进先出
        }
    }
    

IO

  • 除了常见的输入输出
    Scanner input = new Scanner(System.in);
    String n = input.nextLine();
    System.out.println(n)
    
  • File,文件对象
    public class Main {
        public static void main(String[] args) throws IOException {
            // 创建文件,路径必须存在
            File fs = new File("C://Users//Windows10//Desktop//test//1.txt");
            boolean f = fs.createNewFile();
            System.out.println(f?"创建成功":"创建失败");
            // boolean d = fs.delete();
            // System.out.println(d?"删除成功":"删除失败");
    
            // 创建文件夹(已存在就会失败)
            File dirs = new File("C://Users//Windows10//Desktop//test1");
            boolean dir = dirs.mkdir();
            System.out.println(dir?"创建文件夹成功":"创建文件夹失败");
    
            File dirs1 = new File("C://Users//Windows10//Desktop//test2//test3");
            boolean dir1 = dirs1.mkdirs();   // 递归创建
            System.out.println(dir1?"创建文件夹成功":"创建文件夹失败");
    
            // 指明已存在文件夹,创建文件(第一种分开写)
            File dfs = new File("C://Users//Windows10//Desktop//test1", "b.txt");
            // boolean dd = dfs.mkdir();
            // System.out.println(dd?"创建文件成功":"创建文件失败");
            boolean df = dfs.createNewFile();
            System.out.println(df?"创建文件成功":"创建文件失败");
            // 没有同时创建文件夹和文件的
    
            File file = new File("C://Users//Windows10//Desktop//test//1.txt");
            File rfile = new File("C://Users//Windows10//Desktop//test1//2.txt");
            boolean r = file.renameTo(rfile);   // 移动并重命名
            System.out.println(r?"移动文件成功":"移动文件失败");
        }
    }
    
    public class Main {
        public static void main(String[] args) throws IOException {
            // 路径分隔符,不同路径分号隔开
            System.out.println(File.pathSeparator); // ;
            // 名称分隔符,路径内使用\连接
            System.out.println(File.separator); // \
            // 由操作系统决定,但是Windows这个\在很多地方是转义,所以需要两个再转义
            File fs = new File("C:\\Users\\Windows10\\Desktop\\test\\c.txt");
            boolean f = fs.createNewFile();
            System.out.println(f?"创建成功":"创建失败"); // 创建成功
        }
    }
    

字节流

  • 包括OutputStreamInputStream,任何流在底层传输时都是二进制
  • 先从文件流说起:
    public class Main {
        public static void main(String[] args) throws IOException {
            // 文件字节流,文件不存在会创建,路径要存在
            FileOutputStream file = new FileOutputStream("C://Users//Windows10//Desktop/test/a.txt");
            byte[] con1 = {65,66,67,68,69}; // ABCDE
            file.write(con1);
            byte[] con2 = "abcde".getBytes();   // abcde
            file.write(con2, 1, 3); // 开始下标和长度
            file.close();   // 及时关闭,关闭后不能再写入
            System.out.println("写入!");  // ABCDEbcd
        }
    }
    // 再次运行文件时是以w模式,覆盖原来的内容
    
  • 案例:序列化对象到文件,然后反序列化
    • 有两大方式:使用SerializableExternalizable
    • 后者还是继承前者的接口,都可以实现序列化对象的自定义
    import java.io.IOException;
    import java.io.Serializable;
    import java.util.List;
    
    public class Student implements Serializable {
        private transient String stuNum;  // 学号
        // 使用static修饰,会序列化;但反序列化时不会从序列化文件读取,可以修改
        private static String stuName;
        // 直接跳过序列化,反序列化得到的是null
        private transient List<String> teacherList;
    
        // 全参构造方法
        public Student(String stuNum, String stuName, List<String> teacherList) {
            this.stuNum = stuNum;
            this.stuName = stuName;
            this.teacherList = teacherList;
        }
    
        // 优先级高于transient,但static修饰仍然生效
        private void writeObject(java.io.ObjectOutputStream out) throws IOException {
            /*
             指定要序列化的属性
             方法必须是 private void 名称必须为 writeObject;在 ObjectStreamClass 有定义
             */
            System.out.println("序列化..........");
            out.writeObject(stuNum);
            out.writeObject(stuName);
        }
    
        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
            System.out.println("反序列化.........");
            stuNum = (String)in.readObject();
            stuName = (String)in.readObject();
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "stuNum='" + stuNum + '\'' +
                    ", stuName='" + stuName + '\'' +
                    ", teacherList=" + teacherList +
                    '}';
        }
    }
    
    import java.io.*;
    import java.util.List;
    
    public class Student implements Externalizable {
        private transient String stuNum;  // 学号
        // 使用static修饰,会序列化;但反序列化时不会从序列化文件读取;相当于反序列化时有个默认值
        private static String stuName;
        // 直接跳过序列化,反序列化得到的是null
        private transient List<String> teacherList;
    
        //无参数构造方法
        public Student() {
    
        }
    
        // 全参构造方法
        public Student(String stuNum, String stuName, List<String> teacherList) {
            this.stuNum = stuNum;
            this.stuName = stuName;
            this.teacherList = teacherList;
        }
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            /*
            使用Externalizable,重写两个方法即可,还要定义无参构造方法
            还是Serializable使用频率高一些
             */
            out.writeObject(stuNum);
            out.writeObject(stuName);
            out.writeObject(teacherList);
        }
    
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            /*
            优先级还是高于transient
             */
            stuNum = (String) in.readObject();
            stuName = (String) in.readObject();
            teacherList = (List<String>) in.readObject();
        }
    }
    
    20
    • 然后就可以改造快递柜的功能,不用每次重启代码后数据清空

多线程

  • 相关概念
    • 同步:排队执行,强制约关系
    • 异步:同时执行(任务之间没有强制约关系),效率高但数据不安全
    • 并发:同一时刻只有一个线程执行,一段时间内多个线程执行(针对单核)
    • 并行:同一时刻都在执行,需要多核CPU
    • 并发并行要看核的个数,异步同步指的是任务之间的制约关系;异步才有并发并行

Thread

  • 实现多线程方法之一
    public class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("666");
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 和主线程并发执行,抢占时间片
            MyThread mt = new MyThread();
            mt.start();
            // 主线程
            for (int i = 0; i < 20; i++) {
                System.out.println("999");
            }
        }
    }
    

网络编程

调试和优化

  • debug
  • Junit
    • 创建lib文件夹,引入 junit-4.8.jar 包
      2
    • 在Modules的Dependencies就能看到,而且必须看到
    • 工程目录下创建test文件夹,并更改为TestResourceRoot
    • 接下来在想要测试的类中使用快捷键:Ctrl+shift+t,选择junit4,选择要测试的方法
      // MyThread.java
      public class MyThread extends Thread{
          @Override
          public void run() {
              for (int i = 0; i < 20; i++) {
                  System.out.println("666");
              }
          }
      }
      
      // test下 快捷生成的测试方法
      import org.junit.Test;
      
      import static org.junit.Assert.*;
      
      public class MyThreadTest {
      
          @Test
          public void run() {
              // 快捷方法只能在test下创建相应的类和方法(就是名称相关)
              // 下面的测试逻辑是自定义的
              MyThread mt = new MyThread();
              mt.start();
              // 主线程
              for (int i = 0; i < 20; i++) {
                  System.out.println("999");
              }
          }
      } 
      

IDEA快捷键

  • 代码生成
    Alt + Enter  IDEA 根据光标所在问题,提供快速修复选择
    Ctrl + Alt + T 对选中的代码弹出环绕选项弹出层(try,if等语句包裹)
    Alt + Insert 代码自动生成,如生成对象的 set / get 方法,构造函数,toString() 等
    Ctrl + O 选择可重写的方法
    Ctrl + I 选择可实现的方法
    Ctrl + / 注释光标所在行代码,会根据当前不同文件类型使用不同的注释符号
    
  • 选择和移动
    Ctrl + W 递进式选择代码块
    ctrl+Shift+w   递进式取消选择代码块
    Ctrl + D 复制光标所在行 或 复制选择内容,并把复制内容插入光标位置下面
    Ctrl+Shift+↑或↓ : 移动当前行或代码结构
    Ctrl + Y 删除光标所在行 或 删除选中的行
    Ctrl + X 剪切光标所在行 或 剪切选择内容
    
  • 优化
    Ctrl + Alt + O 优化导入的类,可以对当前文件和整个包目录使用
    Ctrl + Alt + L 格式化代码,可以对当前文件和整个包目录使用
    
  • 阅读、替换和查找
    Ctrl + F12 弹出当前文件结构层(类的方法属性等),可以在弹出的层上直接输入,进行筛选
    Ctrl + Q 光标所在的变量 / 类名 / 方法名等上面(也可以在提示补充的时候按),显示文档内容
    
    Ctrl + F 在当前文件进行文本查找
    Ctrl + Shift + F 根据输入内容查找整个项目 或 指定目录内文件
    ​
    Ctrl + R 在当前文件进行文本替换
    Ctrl + Shift + R 根据输入内容替换对应内容,范围为整个项目 或 指定目录内文件
    
    Shift + End 选中光标到当前行尾位置
    Shift + Home 选中光标到当前行头位置
    

XML/Json

  • xml文件的作用
    • 数据传输
    • 数据存储
    • 配置文件
  • json文件的作用
    • 数据传输(更常用)

XML语法

  • 文档声明
    <?xml version="1.0" encoding="UTF-8"?>
    
  • 标记,全部都是由标记组成
    <name>Roy</name>
    <!-- 
    规则:
    1.名称可以含字母、数字以及其他的字符
    2.名称不能以数字或者标点符号开始
    3.名称不能以字符 “xml”(或者 XML、Xml)开始
    4.名称不能包含空格,不能包含冒号(:)
    5.名称区分大小写 
    -->
    
  • 必须有且仅允许有一个根标记
    <names>
       <name>张三</name>
       <name>李四</name>
    </names>
    <!-- 不能直接写两个name标记 -->
    <!-- name标记里还可以嵌套 -->
    
  • 标记除了开始和结束,还能有属性(键值对,不限个数)
    <persons>
       <person id="10001" groupid="1">
         <name>李四</name>
         <age>18</age>
       </person>
       <person id="10002" groupid="1">
         <name>李四</name>
         <age>20</age>
       </person>
    </persons>
    
  • 注释,不能写在文档文档声明前
    <!-- 这是一个可爱的注释! -->
    
  • CDATA, 是不应该由 XML 解析器解析的文本数据,类似python的三引号
    <![CDATA[ 这里想写啥就写啥,< & 也能随便写,xml不解析,不报错! ]]>
    

解析XML文件

  • 使用工具解析,不是随便搞的
  • 同样的,复制jar包到lib,然后project structure 下 new project library新建库,确保modules引入了
    package com.xml;
    
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import javax.print.Doc;
    import javax.xml.parsers.SAXParser;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URL;
    import java.net.URLConnection;
    import java.nio.file.FileSystem;
    import java.util.List;
    
    public class XmlReader {
        public static void main(String[] args) throws IOException, DocumentException {
            FileInputStream fs = new FileInputStream("D:\\IDEA\\IDEAProjects\\xinzhike\\chapter4\\fanxing\\src\\com\\xml\\books.xml");
            SAXReader sr = new SAXReader(); // xml解析工具
            Document doc = sr.read(fs);
            Element root = doc.getRootElement();
            System.out.println(root.getName());
            // 获取标记信息
    //        Element book = root.element("book");
    //        Element name = book.element("name");
    //        System.out.println(name.getText());
            // 或者这样搞
            List<Element> e = root.elements();
            for (int i = 0; i < e.size(); i++) {
                Element book = e.get(i);
                System.out.println(book.attributeValue("id"));  // getName只是标记名,这个是属性值
                System.out.println(book.elementText("name"));   // element+getText
                System.out.println(book.elementText("price"));
            }
            fs.close();
    
            // 解析网络文件,获取xml输入流,后面都一样
            String phone = "18516955565";
            URL url = new URL("http://apis.juhe.cn/mobile/get?phone="+phone+"&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
            URLConnection conn = url.openConnection();
            InputStream is = conn.getInputStream();
            // 后面都一样
        }
    }
    

XPATH解析XML

  • xpath的语法可以在这里学习一下,简单总结
     1. /  :  从根节点开始查找
     2. // :  从发起查找的节点位置 查找后代节点 ***
     3. .  :  查找当前节点
     4. .. :  查找父节点
     5. @  :  选择属性. * 
           属性使用方式:
           [@属性名='值']
           [@属性名>'值']
           [@属性名<'值']
           [@属性名!='值']
    

生成XML

  • 生成xml文档的基本步骤,哎还不如自己写呢
    1. 通过文档帮助器 (DocumentHelper) , 创建空的文档对象
        Document doc = DocumentHelper.createDocument();
    2. 通过文档对象, 向其中添加根节点
        Element root = doc.addElement("根节点名称");
    3. 通过根节点对象root , 丰富我们的子节点
        Element e = root.addElement("元素名称");
    4. 创建一个文件输出流 ,用于存储XML文件
        FileOutputStream fos = new FileOutputStream("要存储的位置");
    5. 将文件输出流, 转换为XML文档输出流
        XMLWriter xw = new XMLWriter(fos); 
    6. 写出文档
        xw.write(doc);
    7. 释放资源
        xw.close();
    
  • 使用xstream,根据类属性生成
    Person p = new Person(1001, "张三", "不详");
    XStream x = new XStream();
    x.alias("haha", Person.class);
    String xml = x.toXML(p);
    System.out.println(xml);	// 直接生成以haha为根节点的包含name/age标记的xml格式
    
  • 面试:解析xml文件有几种方式?
    • 四种:SAX、DOM、JDOM、DOM4J
    • 本质上就两种:
      • SAX逐行解析
      • DOM创建树模型,最常用的是DOM4J,集成了Xpath

json

  • JSON: JavaScript Object Notation JS对象简谱 , 是一种轻量级的数据交换格式
  • json对象全部使用字符串存储数据,所以也叫json字符串
  • 有很多优点,很多地方取代了xml
    • 体积小
    • 解析快
    • 太长了不易读
  • 看个例子便知
    js:
     var b = new Object();
     b.name = "金苹果";
     b.info = "种苹果";
     
    XML:	
    <book>
    	<name>金苹果</name>
    	<info>种苹果</info>
    </book>
    
    JSON:
    {
    	"name":"金苹果",
    	"info":"种苹果"
    }
    

Java—Json

  • 将Java中的对象快速的转换为 JSON格式的字符串
  • 将JSON格式的字符串,转换为Java的对象
  • 有两种方式:Gson(谷歌)和FastJson(阿里)

枚举

  • JDK1.5引入了新的类型——枚举,比较简单

    • 之前,我们定义常量都是: public static fianl....,难管理
      61
  • 反射和注解用的都比较浅,涉及框架,很多是别人写好的

  • 看个枚举的例子

    public enum Level {
    	// 相当于上面使用final,传入levelValue
    	LOW(30), MEDIUM(15), HIGH(7), URGENT(1);
    	
    	private int levelValue;
    	
    	// 只有private构造方法
    	private Level(int levelValue) {
    		this.levelValue = levelValue;
    	}
    	
    	public int getLevelValue() {
    		return levelValue;
    	}
    }
    
    // 更常见的是这样定义:如果不重写构造方法,不能指定数字
    class enum Level {
    	LOW, MEDIUM, HIGH;	// 不用指定数值,英文意思表示即可
    }
    
  • 自定义的枚举类默认继承了父类的方法,包括:
    62

  • 所有的枚举都继承自java.lang.Enum类,由于Java 不支持多继承,所以枚举对象不能再继承其他类

  • 这个问题就需要定义接口解决,也叫枚举接口

    interface LShow{
        void show();
    }
    
    public enum Level implements LShow{
        LOW{
            @Override
            public void show(){
                System.out.println("低级别");
            }
        }, MEDIUM{
            @Override
            public void show(){
                System.out.println("中级别");
            }
        },HIGH{
            @Override
            public void show(){
                System.out.println("高级别");
            }
        },URGENT{
            @Override
            public void show(){
                System.out.println("紧急!");
            }
        };
    	//    private int levelValue;
    	//
    	//    private Level(int levelValue) {
    	//        this.levelValue = levelValue;
    	//    }
    	//    public int getLevelValue() {
    	//        return levelValue;
    	//    }
    }
    
  • 枚举类不能有子类,因为其枚举类默认被final修饰

  • 只能有private构造方法,不用提供set方法

  • switch中使用枚举时,直接使用常量名,不用携带类名

注解

  • 可以理解为给机器看的注释机制(运行代码过程中会产生点影响,不是完全没用)
  • 可以使用在类、方法、变量、参数和包等,类似python的装饰器,扩展代码
  • 标注(注解)可以被嵌入到字节码中,可以通过反射获取标注内容
  • 一般用于:
    • 编译格式检查
    • 反射中解析
    • 生成帮助文档
    • 跟踪代码依赖

内置注解

  • 就是Java设置好的注解,例举几个
    @Override	// 定义在java.lang.Override
    @SafeVarargs	// 忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告
    @Deprecated	// 废弃;这个方法被废弃,但仍支持以前调用此方法的代码
    @SuppressWarnings	// 抑制编译时的警告信息
    
  • 元注解:注解的注解,自定义注解时用到
    @Retention 	// 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
    @Documented 	// 标记这些注解是否包含在用户文档中 javadoc。
    @Target 	// 标记这个注解应该是哪种 Java 成员。
    @Inherited 	// 标记这个注解是自动继承的
    
  • 注解继承有以下特点(接口不传、类传)
    • 子类会继承父类使用的注解中被@Inherited修饰的注解
    • 接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有
      被@Inherited修饰
    • 类实现接口时不会继承任何接口中定义的注解

自定义注解

  • 类似python自定义装饰器,注解的基类是java.lang.Annotation
  • 任何注解的定义都是以下结构:必须定义ElementType和RetentionPolicy
    在这里插入图片描述
    import java.lang.annotation.*;
    
    @MyAnnotation
    public class Demo {
        public static void main(String[] args) {
    
        }
    
        @MyAnnotation
        public void add() {
    
        }
    }
    
    @Documented
    // 可以作用在类和方法上
    @Target({ElementType.TYPE, ElementType.METHOD})
    // 推荐使用runtime
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation {
    
    }
    
  • 注解中的每一个方法,实际是声明的注解配置参数,后面学反射会理解

反射

  • JAVA反射机制是在运行状态中,获取任意一个类的结构;创建对象 , 得到方法、属性,执行方法
  • 类加载器
    • 是Java运行时环境(Java Runtime Environment)的一部分
    • 负责动态加载Java类到Java虚拟机的内存空间中
      64
    • 类通常是按需加载,掌握Java的委派概念很重要
      65
  • 我们可以设置目录为ResourceRoot,用类加载器获取文件流
    public class Demo2 {
        public static void main(String[] args) throws IOException {
            InputStream in = Demo2.class.getClassLoader().getResourceAsStream("test.txt");// 假设这是个配置文件
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String text = br.readLine();
            System.out.println(text);
            br.close();
        }
    }
    
  • 每一个字节码文件,被加载到内存后,都变成一个对应的Class类型的对象
    66
  • 如图所示,加载到内存后,类定义的class文件变成了Class类的对象,即Class对象
  • 这个Class对象包含类定义的信息,是反射要用的

Class

  • 回到反射,反射可以获取类的结构,即获取Class对象,有哪些方式呢?
  • 三种方式
    public class Demo3 {
        public static void main(String[] args) throws ClassNotFoundException {
            // 方式一:知道类的名称, 且类已经存在
            Class<Demo2> c1 = com.annotation.Demo2.class;
            System.out.println(c1);
    
            // 方式二:拥有类的对象
            Demo2 d2 = new Demo2();
            Class<Demo2> c2 = (Class<Demo2>) d2.getClass();
    
            System.out.println(c1==c2);
    
            // 方式三:知道类的名称
            Class<Demo2> c3 = (Class<Demo2>) Class.forName("com.annotation.Demo2");
            System.out.println(c1==c3); // true
        }
    }
    
  • 在调用时, 如果类定义在内存中不存在, 则会加载到内存 ! 如果类已经在内存中存在, 不会重复加载, 而是重复利用(类的定义只需要加载一次)
    67
  • 获取类的构造方法,通过构造方法创建对象
    public class Demo4 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            Class<Demo2> c3 = (Class<Demo2>) Class.forName("com.annotation.Demo2");
            Constructor<Demo2> con2 = c3.getConstructor();
            // 创建类对象
            Demo2 d2 = con2.newInstance();
            System.out.println(d2);
    
            // 获取含参构造器
            Constructor<Demo2> con3 = c3.getConstructor(String.class, int.class);
            Demo2 d3 = con3.newInstance("Roy", 18);
    
            // 获取所有权限的构造方法
            Constructor<Demo2> con4 = c3.getDeclaredConstructor();
            con4.setAccessible(true);   // 忽略权限检查,让private修饰的构造方法也可使用
        }
    }
    
  • 类似的,可以获取method、field、annotation

内省

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Roy_Allen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值