一、注解(Annotation)
1、内置注解
- 注解与注释:注解除了可以看,还能被其他的程序所读取;
- 常用注解:@override、@deprecated--不建议使用的、@suppressWarining("all")--警告抑制
2、自定义注解
(1)注解关键字:@interface;
(2)四个元注解
- 元注解的作用:就是用来说明解释其他注解的注解;
- @Target:定义注解的作用目标;
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
- @Retention: 定义注解的保留策略(生命周期);
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
- @Document:说明该注解将被包含在javadoc中;
- @Inherited:说明子类可以继承父类中的该注解;
(3)注解的参数
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
int age() default 0;
String[] strs();
}
- 单个参数建议参数名写为 value;
- 参数没有赋默认值,用注解时必须赋值不然会报错;
3、注解读取
(1)应用示例:用注解完成 orm 映射
- 定义注解、在类中使用注解、用反射读取注解
二、反射(reflection)
1、概述
(1)概念:
- JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;
(2)获取class 文件对象的方法
- class.forName()
- obj.getClass()
- class.class
(3)说明:
- 对于一个类来说,类加载到内存中后,class 文件对象有且只有一个;
2、动态获取信息和动态操作
(1)动态获取
1、取属性
- 获取公开属性
Field[] fields = clazz.getFields();
Field age = clazz.getField("age");
- 获取所有属性
Field[] dfs = clazz.getDeclaredFields();
Field dage = clazz.getDeclaredField("age");
2、取方法
Method getage = clazz.getDeclaredMethod("setAge", int.class);
Method[] ms = clazz.getDeclaredMethods();
- 获取公开方法的方法参考属性;
3、取构造器
- 具体方法参考属性的;
(2)动态操作
1、api 动态调用构造器新建对象
- 空参:clazz.newInstance():本质就调用空参构造器;
- 有参:
Constructor<?> ins = clazz.getDeclaredConstructor(String.class,int.class);
Student obj = (Student) ins.newInstance("allen",12);
2、动态执行方法
Method getage = clazz.getDeclaredMethod("getAge", null);
int age = (int) getage.invoke(obj, null);
3、动态设置和获取属性(暴力反射)
Field dage = clazz.getDeclaredField("age");
dage.setAccessible(true);
int age = (int) dage.get(obj);
3、反射性能
(1)提高反射性能
- 反射提高了程序的灵活性的同时降低了性能;
- 通过关闭安全检查可以提高反射的性能(setAccessible(true));
(2)反射操作泛型
- type 是一个反射包下的接口;
- Class
- ParameterizedType
- GenericArrayType
- WildcardType
- TypeVariable
(3)反射读取注解
- 读取注解先要获得相应的class、field 和 method 对象;
- 然后就是调用方法读取注解
4、动态编译和脚本引擎
(1)动态编译
1、动态编译
- 第一种是利用 Runtime 类用exec 调用 javac 命令进行动态编译;
- 第二种用 JavaCompiler 类的 run 方法编译;
2、动态执行
- 第一种还是用 runtime 类来调用 java 命令;
- 第二种是反射来调用,要用到类加载器;
/*Runtime rt = Runtime.getRuntime();
rt.exec("javac d:/Hello.java");
Process pro = rt.exec("java -cp d: Hello");
InputStream is = pro.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String str = "";
while((str = br.readLine()) != null) {
System.out.println(str);
}*/
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
jc.run(null, null, null, "d:/Hello.java");
URL[] urls = {new URL("file:/d:/")};
URLClassLoader loader = new URLClassLoader(urls);
Class<?> clazz = loader.loadClass("Hello");
Method m = clazz.getDeclaredMethod("main", String[].class);
Object ins = clazz.newInstance();
m.invoke(ins, (Object)new String[] {});
(2)脚本引擎
- 概念:就是一套让java 程序能够操作js等脚本的API;
- 作用:通过 java 代码执行 js 等脚本;通过引擎的上下文,让java 和脚本之间进行数据交换;
- 基本使用:
//get engine
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine jsEngine = sem.getEngineByName("js");
//write js
String str = "msg = 'haha';print(msg)";
//run js by java
jsEngine.eval(str);
Object msg = jsEngine.get("msg");
System.out.println(msg);
- 还可以将 engine 强转为 invocable 执行 js 函数 ;
String str = "function getData() {print('ahah')}";
jsEngine.eval(str);
Invocable invo = (Invocable)jsEngine;
invo.invokeFunction("getData", null);
- 在js代码中导入java包,使用包中的java类
String jscode = "var list=java.util.Arrays.asList(['allen','mike','ai']);";
engine.eval(jscode);
List<String> list2 = (List<String>) engine.get("list");
for (String t : list2) {
System.out.println(t);
}
- 执行单独的js 文件
URL url=Demo01.class.getClassLoader().getResource("a.js");
FileReader reader=new FileReader(url.getPath());
engine.eval(reader);
reader.close();
三、字节码操作
1、概述
(1)java 动态性的两种实现方法:反射和字节码操作;
(2)作用:通过字节码操作可以动态新建和修改一个类;字节码操作的性能是高于反射的;
(3)常用的字节码操作类库:CGLIB和 javassist;
2、javassist入门
(1)最外层 api 包含 CtClass、Ctfield 和 CtMethod 三个类,这三个类的作用和反射中的calss、field 和 method 相类似;
(2)基本使用
ClassPool pool = ClassPool.getDefault();
CtClass stu = pool.getCtClass("com.stan.test.Stu");
//do with field
CtField f = CtField.make("public int age = 10;", stu);
stu.addField(f);
//do with method
CtMethod m = CtMethod.make("public int getOne(){return 2;}", stu);
stu.addMethod(m);
//do with constructor
CtConstructor cons = new CtConstructor(new CtClass[] {pool.get("java.lang.String")}, stu);
cons.setBody("this.name = name;");
stu.addConstructor(cons);
3、详细api
(1)获取类相关的基本信心,比如类名等;
(2)新增或者修改方法,两种方案;
(3)新增字段;
(4)构造器的获取;
(5)注解的读取;
四、JVM核心机制
1、类加载过程
(0)JVM 的内存结构
- 栈(方法):方法压栈执行,结束弹栈释放;
- 堆(对象):存对象的,class 对象也是存放在这个区;
- 方法区(实质也是堆):包含常量池、类的静态成员、类的代码;
(1)类加载:就是 class 文件转变为 JVM 可用的类的过程;
(2)过程概述:
1、加载
- class文件字节码加载到内存,方法区生成静态变量的数据结构,堆中生成一个 class 对象;
2、链接:类二进制代码合并到JVM 的运行状态之中
- 验证:安全检查;
- 准备:为静态变量分配内存并赋初始化值,此过程在方法区中;
- 解析:常量池内的符号引用替换为直接引用的过程;
3、初始化
- 就是把静态变量的赋值动作和静态代码块收到类构造器中,然后类构造器进行类的初始化操作;
(3)初始化时机
1、主动引用
- 主动引用类的时候,比如 new对象的时候,类会进行初始化;
- 类初始化的时候,如果父类没有被初始化,会先初始化父类;
2、被动引用
- 引用类中常量,不会初始化;
- 子类引用父类的静态域,子类不会初始化,父类会;
- 定义数组引用类不会初始化;
2、类加载器的层次结构
(1)引导类加载器
- 作用是加载核心类库;用C语言写的;不是继承lang 包里的 classloader
(2)扩展类加载器
- 加载jre中的扩展包(ext);继承lang 包里的 classloader;用java 写的;
(3)应用类加载器
- 我们在程序中自己写的类就是他加载的;默认记载 classpath 路径下的类;
(4)自定义类加载器
- 可以自己继承 classloader 实现自己的类加载器;
(5)代理模式--双亲委托机制
- java 的类加载采用的是代理模式:就是让別的类加载器来加载类;
- 具体采用的是双亲委托机制:就是要加载类的时候把这个事一层一层往上抛,到最顶层的引导类加载器,然后往下走,那一层能处理就加载;
- 作用:就是能保证 java 核心类库的安全;
3、自定义类加载器
(1)说明:
- 不一定非要用双亲委托;
- 同一个类被不同的加载器加载,也被 jvm 认为是不同的类;
(2)系统文件类加载器(示例)
(3)网络类加载器(示例)
(4)加密解密加载器(示例):取反
(5)线程上下文加载器(示例)
(6)tomcat 类加载和 OSGI(java 动态模块系统)