MOOC 个人学习笔记

# 1. 反射基础

  • 反射:reflection
    • 程序可以访问、检测和修改它本身状态或行为的能力,即自描述
      和自控制。
    • 可以在运行时加载、探知和使用编译期间完全未知的类。
    • 给 Java 插上动态语言特性的翅膀,弥补强类型语言的不足。
    • java.lang.reflect 包,在 Java 2 时代就有,在 Java 5 得到完善

# 反射功能

  • 在运行中分析类的能力
  • 在运行中查看和操作对象
    • 基于反射自由创建对象
    • 反射构建出无法直接访问的类
    • set 或者 get 到无法访问的成员变量
    • 调用不可访问的方法
  • 实现通用的数组操作代码
  • 类似函数指针的功能

# 创建对象

  • 方法 1:静态编码 & 编译
A obj1 = new A();
  • 方法 2:克隆 (clone)
public class B implements Cloneable {
    
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}
//obj3 是 obj2 的克隆对象  没有调用构造函数
B obj2 = new B();
B obj3 = (B) obj2.clone();
  • 方法 3:序列化 (serialization) 和反序列化 (deserialization)
public class C implements Serializable {
    private static final long serialVersionUID = 1L;
}
// 没有调用构造函数
// 序列化会引发安全漏洞,未来将被移除出 JDK,请谨慎使用!!!
C obj4  = new C();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"));   
out.writeObject(obj4);   
out.close();   
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));   
C obj5 = (C) in.readObject();   
in.close();
  • 方法 4 和 5:反射
public class A {
    public void hello()
    {
        System.out.println("hello from A");
    }
}
// 第四种  newInstance  调用构造函数        
Object obj6 = Class.forName("A").newInstance();        
Method m = Class.forName("A").getMethod("hello");
m.invoke(obj6);
A obj7 = (A) Class.forName("A").newInstance();
// 第五种  newInstance  调用构造函数
Constructor<A> constructor = A.class.getConstructor();   
A obj8 = constructor.newInstance();
obj8.hello();

# 2. 反射关键类

  • Class:类型标识
    • JVM 为每个对象都保留其类型标识信息 (Runtime Type Identification)
    • 三种获取方式
String s1 = "abc";
Class c1 = s1.getClass();
System.out.println(c1.getName());
Class c2 = Class.forName("java.lang.String");
System.out.println(c2.getName());
Class c3 = String.class;
System.out.println(c3.getName());

# 获取详细信息

  • getFields()
    • 返回本类和所有父类所有的 public 成员变量
  • getDeclareFields()
    • 返回本类自己定义的成员变量,包括 private 变量,不包括父类变量
A obj = new A(20, "Tom");
Class c = obj.getClass();
// 获取本类及父类所有的 public 字段
Field[] fs = c.getFields(); 
System.out.println(fs[0].getName() + ":" + fs[0].get(obj));
// 获取本类所有声明的字段
Field[] fs2 = c.getDeclaredFields();
for(Field f : fs2)
{
    f.setAccessible(true);
    System.out.println(f.getName() + ":" + f.get(obj));
}
  • getMethods()
    • 返回本类和所有父类所有的 public 方法
  • getDeclareMethods()
    • 返回本类自己定义的方法,包括 private 方法,不包括父类方法
B obj = new B();
Class c = obj.getClass();
// 获取 public 方法 包括父类和父接口
Method[] ms = c.getMethods();
for (Method m : ms) {
    if ("f1".equals(m.getName())) {
        m.invoke(obj, null);// 传参调用方法
    }
}
// 获得该类的所有方法
Method[] ms2 = c.getDeclaredMethods();
for (Method m : ms2) {
    if ("f2".equals(m.getName())) {
        m.setAccessible(true);
        String result = (String) m.invoke(obj, "abc");// 传参获得结果
        System.out.println(result);
    }
}
  • getPackage()
    • 返回本类所在包
  • getModifiers()
    • 返回本类前缀修饰符
  • getInterfaces()
    • 返回本类继承的接口
  • getSuperClass()
    • 返回本类的父类
Son son = new Son();
Class c = son.getClass();
Class father = c.getSuperclass();
System.out.println(father.getName());
Class[] inters = c.getInterfaces();
for(Class inter : inters) {
    System.out.println(inter.getName());
}
  • getConstructors()
    • 返回本类所有的构造函数
D d = new D();
Class c = d.getClass();
Constructor[] cons = c.getConstructors();
for (Constructor con : cons) {
    if (con.getParameterCount() > 0) {
        // 有参构造函数
        D obj = (D) con.newInstance(100);
        obj.printNum();
    } else {
        // 无参构造函数
        D obj = (D) con.newInstance();
        obj.printNum();
    }
}
  • getAnnotations()
    • 返回本类的注解

# 3. 反射应用

# 数据库连接

  • JDBC
    • Connection,连接到各个不同数据库
try {
    Class.forName("com.mysql.jdbc.Driver");
    // Class.forName(className, true, currentLoader)
    // 通知类加载器加载此类的 class 文件
} catch (ClassNotFoundException e1) {
    e1.printStackTrace();
    return;
}

# 数组扩充器

  • 给定一个数组 (任意类型),将其长度扩大一倍
    • Java 的数组一旦创建,其长度是不再更改的
    • 新建一个大数组 (相同类型),然后将旧数组的内容拷贝过去
public static void main(String[] args) {
    int[] a = { 1, 2, 3, 4, 5 };
    a = (int[]) goodCopy(a, 10);
    for (int i : a) {
        System.out.println(i);
    }
}
public static Object goodCopy(Object oldArray, int newLength) {
    // Array 类型
    Class c = oldArray.getClass();
    // 获取数组中的单个元素类型
    Class componentType = c.getComponentType();
    // 旧数组长度
    int oldLength = Array.getLength(oldArray);
    // 新数组
    Object newArray = Array.newInstance(componentType, newLength);
    // 拷贝旧数据
    System.arraycopy(oldArray, 0, newArray, 0, oldLength);
    return newArray;
}

# 动态执行方法

  • 给定类名、方法名,即可执行
    • 加上定时器,即可做定时任务执行
Timer timer = new Timer();
Calendar now = Calendar.getInstance();
now.set(Calendar.SECOND, 
        now.get(Calendar.SECOND) + 1);
Date runDate = now.getTime();
MyTask task2 = new MyTask();
timer.scheduleAtFixedRate(task2, runDate, 3000); 
// 固定速率
Thread.sleep(15000);
timer.cancel(); // 取消定时器
class MyTask extends TimerTask {
    public void run() {
        try {
            Method m = Class.forName("Worker")
                    .getClass().getMethod("hello");
            m.invoke(null);// 静态方法可以不用 new 对象
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Worker {
    public static void hello() {
        System.out.println("Hello java!");
    }
}

# Json 和 Java 对象互转

  • Json:
Gson gson = new Gson();
String s =  "{\"name\":\"Jo\""
        + ",\"email\":\"a@b.com\"}";
Person p = gson.fromJson(s, Person.class);
System.out.println(p.getName());
System.out.println(p.getEmail());

# Tomcat 的 Servlet 对象创建

# MyBatis 的 OR/M

# Spring 的 Bean 容器

# org.reflections 包介绍

  • Reflection 的增强工具包
    • https://github.com/ronmamo/reflections
      • Java runtime metadata analysis
      • 获取某类的所有子类型
      • 获取有特殊 annotation 的类型或者成员变量 / 方法
      • 根据正则表达式获取资源 (类 / 成员变量 / 方法)・根据组合条件查询相应的方法
      • ……

# 编译器 API

  • 对.java 文件即时编译
  • 对字符串即时编译
  • 监听在编译过程中产生的警告和错误
  • 在代码中运行编译器 (并非:Runtime 命令行调用 javac 命令)

# JavaCompiler

  • 自 Java 1.6 推出,位于 javax.tools 包中
  • 可用在程序文件中的 Java 编译器接口 (代替 javac.exe)
  • 在程序中编译 java 文件,产生 class 文件
  • run 方法 (继承自 java.tools.Tools):较简单。可以编译 java 源文件,生成 class 文件,但不能指定输出路径,监控错误信息,调用后就在源码所在目录生成 class 文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 第一个参数:输入流,null 表示默认使用 system.in
// 第二个参数:输出流,null 表示默认使用 system.out
// 第三个参数:错误流,null 表示默认使用 system.err
// 第四个参数:String... 需要编译的文件名
// 返回值:0 表示成功,其他错误
int result = compiler.run(null, null, null, "F:/temp/Hello1.java", "F:/temp/Hello2.java");
System.out.println(0 == result ? "Success" : "Fail");
  • getTask 方法:更强大的功能。可以编译 java 源文件,包括在内存中的 java 文件 (字符串),生成 class 文件
public static void compileJavaFromString() {
        
    StringBuilder sb = new StringBuilder();
    String className = "Hello";
    //sb.append("package com.test;\n");
    sb.append("public class Hello{\n");
    sb.append("public static void main(String[]args){\n");
    sb.append("System.out.print(\"hello world\"); \n");
    sb.append("}\n");
    sb.append("}");
    // 将上述源码编译
    Class<?> c = compile(className, sb.toString());
    try {
        // 生成对象
        Object obj = c.newInstance();
        // 调用 main 方法
        Method m = c.getMethod("main", String[].class);
        m.invoke(obj, new Object[] { new String[] {} });
    } catch (Exception e) {
        e.printStackTrace();
    }
}
private static Class<?> compile(String className, String javaCodes) {
    // 将字符串包装为 SimpleJavaFileObject 对象
    JavaSourceFromString srcObject = new JavaSourceFromString(className, javaCodes);        
    System.out.println(srcObject.getCode());
    Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(srcObject);
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();  
    // 设置编译的输出目录,并包装在 options 中
    String flag = "-d";
    String outDir = "";
    try {
        File classPath = new File(Thread.currentThread().getContextClassLoader().getResource("").toURI());
        outDir = classPath.getAbsolutePath() + File.separator;
        System.out.println(outDir);
    } catch (URISyntaxException e1) {
        e1.printStackTrace();
    }        
    Iterable<String> options = Arrays.asList(flag, outDir);
    //JavaCompiler.getTask 方法:以 future 的任务形式 (多线程),来执行编译任务
    // 第一个参数:额外输出流,null 表示默认使用 system.err
    // 第二个参数:文件管理器,null 表示编译器标准文件管理器
    // 第三个参数:诊断监听器,null 表示使用编译器默认方法来报告诊断信息
    // 第四个参数:编译器参数,null 表示无参数
    // 第五个参数:需要经过 annotation 处理的类名,null 表示没有类需要 annotation 处理
    // 第六个参数:待编译的类
    JavaCompiler.CompilationTask task = 
            compiler.getTask(null, fileManager, diagnosticCollector, options, null, fileObjects);
    // 等待编译结束
    boolean result = task.call();
    if (result == true) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    else            
    {
        //print the Diagnostic's information   
        for  (Diagnostic diagnostic : diagnosticCollector  
                .getDiagnostics())  
        {  
            System.out.println("Error on line: "   
                    + diagnostic.getLineNumber() + "; URI: "   
                    + diagnostic.getSource().toString());  
        }  
    }
    return null;
}

# Java 编译器 API 作用

  • Java EE 的 JSP 编译
  • 在线编程环境
  • 在线程序评判系统 (Online Judge 系统)
  • 自动化的构建和测试工具
  • ……

# 基于 JavaCompiler 的集成工具