当前位置: 代码迷 >> 综合 >> 新职课(chapter4)
  详细解决方案

新职课(chapter4)

热度:44   发布时间:2023-12-20 12:59:28.0

文章目录

  • 常用类库
    • 泛型
      • 泛型类
      • 泛型方法
      • 泛型限制
      • 通配符
  • Objects
  • Math
  • Arrays
  • BigDecimal
  • Date
    • DateFormat
    • Calendar
  • System
  • String
    • 常用方法
  • 集合
    • List接口
    • ArrayList
    • LinkedList
  • IO
    • 字节流
  • 多线程
    • Thread
  • 网络编程
  • 调试和优化
    • IDEA快捷键
  • XML/Json
    • XML语法
    • 解析XML文件
    • XPATH解析XML
    • 生成XML
    • json
    • Java—Json
  • 枚举
  • 注解
    • 内置注解
    • 自定义注解
    • 反射
      • Class
    • 内省

常用类库

  • 常用的库:握字符串、时间、集合、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;}@Overridepublic 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>{
          	// 泛型包含的类型必须是动物类的子类,这里只能用CatT 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.30000000000000004BigDecimal 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.33System.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:38Date date = null;try {
          // 解析,返回Datedate = 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();   // 不是newint year = c1.get(Calendar.YEAR);   // 这个YEAR是一个全局常量,存的是一个数组下标,获取年System.out.println(year);  // 2021c1.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) {
          // 接口的实现类:ArrayListList<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) {
          // 如果不指定初始化大小,默认10ArrayList<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) {
          // 如果不指定初始化大小,默认10Vector<Integer> data = new Vector<>();System.out.println(data.size());    // 0data.add(1);data.add(10);System.out.println(data.size());    // 0System.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}; // ABCDEfile.write(con1);byte[] con2 = "abcde".getBytes();   // abcdefile.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;// 直接跳过序列化,反序列化得到的是nullprivate 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();}@Overridepublic 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;// 直接跳过序列化,反序列化得到的是nullprivate transient List<String> teacherList;//无参数构造方法public Student() {
          }// 全参构造方法public Student(String stuNum, String stuName, List<String> teacherList) {
          this.stuNum = stuNum;this.stuName = stuName;this.teacherList = teacherList;}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {
          /*使用Externalizable,重写两个方法即可,还要定义无参构造方法还是Serializable使用频率高一些*/out.writeObject(stuNum);out.writeObject(stuName);out.writeObject(teacherList);}@Overridepublic 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{
          @Overridepublic 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{
              @Overridepublic 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 {
              @Testpublic 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+getTextSystem.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,传入levelValueLOW(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{
          @Overridepublic void show(){
          System.out.println("低级别");}}, MEDIUM{
          @Overridepublic void show(){
          System.out.println("中级别");}},HIGH{
          @Overridepublic void show(){
          System.out.println("高级别");}},URGENT{
          @Overridepublic 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) {
          }@MyAnnotationpublic 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

内省