动态编译:
通过API将一些动态生成的内容进行编译。
动态编译的应用场景:
----可以做一个浏览器端编写JAVA代码,上传服务器编译和运行的在线评测系统。
----服务器动态加载某些类文件进行编译。
动态编译的两种做法:
通过Rnutime调用javac,启动新的进程去操作。
--之前使用,不属于动态编译
Runtime run=Runtime.getRuntime();
Process process=run.exec("javac -cp d:/myjava/ HelloWorld.java");//-cp表示类路径。
--通过JavaCompiler动态编译:
/**
* 测试java的动态编译
* @author 尚学堂高淇 www.sxt.cn
*
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//若想动态编译此字符串,通过IO流操作,想办法将字符串存储成一个临时文件(Hi.java),然后调用动态编译方法!
String str = "public class Hi {public static void main(String[] args){System.out.println(\"HaHa,sxt!\");}}";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null, "c:/myjava/HelloWorld.java");
System.out.println(result==0?"编译成功":"编译失败");
//通过Runtime调用执行类
// Runtime run = Runtime.getRuntime();
// Process process = run.exec("java -cp c:/myjava HelloWorld");
//
// InputStream in = process.getInputStream();
// BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// String info = "";
// while((info=reader.readLine())!=null){
// System.out.println(info);
// }
try {
URL[] urls = new URL[] {new URL("file:/"+"C:/myjava/")};
URLClassLoader loader = new URLClassLoader(urls);//classLoader类加载器
Class c = loader.loadClass("HelloWorld");
//调用加载类的main方法
Method m = c.getMethod("main",String[].class);
m.invoke(null, (Object)new String[]{});
//由于可变参数是JDK5.0之后才有。
//m.invoke(null, (Object)new String[]{"aa","bb"});会编译成:m.invoke(null,"aa","bb"),就发生了参数个数不匹配的问题。
//因此,必须要加上(Object)转型,避免这个问题。
//public static void main(String[] args)
} catch (Exception e) {
e.printStackTrace();
}
}
}
脚本引擎执行javascript代码:
如前台传来一个字符串如下:“2*3+5-7*8”如果是java的话则需要字一个解析类。但是,如果是javascript的话,则只需要一个eval函数即可。e("2*3+5-7*8")
再如:公司30个人的工资计算方法都不一样,可以把计算方法写到xml文件当中,然后动态加载到java中,通过动态调用脚本引擎执行javascript代码即可:
脚本引擎:使得JAVA程序可以通过一套固定的接口与各种脚本打交道,可以把一些复杂的业务逻辑交给脚本语言来处理,大大提高的开发效率。
其中js使用Rhino:一种使用java编写的javascript开源实现。
获得脚本引擎对象:
//获得脚本引擎
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("javascript");
/**
* 测试脚本引擎执行javascript代码
* @author 尚学堂高淇 www.sxt.cn
*
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//获得脚本引擎对象
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("javascript");
//定义变量,存储到引擎上下文中,通过脚本能获取,也能通过java获取。
engine.put("msg", "gaoqi is a good man!");
String str = "var user = {name:'gaoqi',age:18,schools:['清华大学','北京尚学堂']};";
str += "println(user.name);";
//执行脚本
engine.eval(str);
engine.eval("msg = 'sxt is a good school';");//修改msg的值
System.out.println(engine.get("msg"));//通过java获取这个值。
System.out.println("###########################");
//定义函数
engine.eval("function add(a,b){var sum = a + b; return sum;}");
//取得调用接口
Invocable jsInvoke = (Invocable) engine;
//执行脚本中定义的方法
Object result1 = jsInvoke.invokeFunction("add", new Object[]{13,20});
System.out.println(result1);
//导入其他java包,使用其他包中的java类.若需要深入了解细节,可以详细学习Rhino的语法
String jsCode = "importPackage(java.util); var list=Arrays.asList([\"北京尚学堂\",\"清华大学\",\"北京大学\"]);";
engine.eval(jsCode);
List<String> list2 = (List<String>)engine.get("list");
for (String temp : list2) {
System.out.println(temp);
}
//执行一个js文件(我们将a.js至于项目的src下即可)
URL url = Demo01.class.getClassLoader().getResource("a.js");
FileReader fr = new FileReader(url.getPath());
engine.eval(fr);
fr.close(); //由于只是测试,就不那么规范了。大家实际用时要使用try catch finally!
}
}
JAVA动态性的两种常见实现方式:
字节码操作,
反射
运行时操作字节码可以让我们实现如下功能:
动态生成新的类。
动态改变某个类的结构。(添加、删除。修改,新的属性和方法)
优势::
比反射开销小,性能高。
javaasist性能高于反射,低于ASM
常见的字节码操作类库:
BCEL:Byte Code Engineering Library,是apache下Jakarta项目的一部分,它是java classWorking广泛使用的一种框架。它可以让您深入JVM汇编语言进行类操作的细节。它在实际的JVM指令层次上操作。
ASM:是一个轻量级JAVA字节码操作框架,直接涉及到JVM底层的操作和实现。
CGLIB:基于ASM实现,是一个强大的,高性能,高质量生成类库。
Javassist:性能较ASM差,跟CGLIB差不多,但是使用简单,很多开源框架都使用它。
在AOP编程当中底层很多都可以使用javassist。
javassit can be a good tool adding new methods into a class and for inserting before/after/around advice at the both caller callee sides
AOP:面向切面编程。
javassist的最外层的API和反射中的API颇为类似:它主要由CtClass,CtMethod,CtField几个类组成,用以执行Class,java.lang.reflection.Method,java.lang.Method.Field相同的操作。
创建一个全新的类:
使用XJAD反编译工具查看类:将生成的class类反编译成源文件。
package basic;
@Author(name="gaoqi", year=2014)
public class Emp {
private int empno;
private String ename;
public void sayHello(int a){
System.out.println("sayHello,"+a);
}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Emp(int empno, String ename) {
super();
this.empno = empno;
this.ename = ename;
}
public Emp() {
}
}
package basic;
public @interface Author {
String name();
int year();
}
/**
* 测试使用javassist生成一个新的类
* @author 尚学堂高淇 www.sxt.cn
*
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("com.bjsxt.bean.Emp");
//创建属性
CtField f1 = CtField.make("private int empno;", cc);
CtField f2 = CtField.make("private String ename;", cc);
cc.addField(f1);
cc.addField(f2);
//创建方法
CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc);
CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno=empno;}", cc);
cc.addMethod(m1);
cc.addMethod(m2);
//添加构造器
CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc);
constructor.setBody("{this.empno=empno; this.ename=ename;}");
cc.addConstructor(constructor);
cc.writeFile("c:/myjava"); //将上面构造好的类写入到c:/myjava中
System.out.println("生成类,成功!");
}
}
package basic;
import java.lang.reflect.Method;
import java.util.Arrays;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;
/**
* 测试javassist的API
* @author 尚学堂高淇 www.sxt.cn
*
*/
public class Demo02 {
/**
* 处理类的基本用法
* @throws Exception
*/
public static void test01() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bjsxt.test.Emp");
byte[] bytes = cc.toBytecode();
System.out.println(Arrays.toString(bytes));
System.out.println(cc.getName()); //获取类名
System.out.println(cc.getSimpleName()); //获取简要类名
System.out.println(cc.getSuperclass()); //获得父类
System.out.println(cc.getInterfaces()); //获得接口
}
/**
* 测试产生新的方法
* @throws Exception
*/
public static void test02() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bjsxt.test.Emp");
// CtMethod m = CtNewMethod.make("public int add(int a,int b){return a+b;}", cc);
CtMethod m = new CtMethod(CtClass.intType,"add",
new CtClass[]{CtClass.intType,CtClass.intType},cc);
m.setModifiers(Modifier.PUBLIC);
m.setBody("{System.out.println(\"www.sxt.cn\");return $1+$2;}");
cc.addMethod(m);
//通过反射调用新生成的方法
Class clazz = cc.toClass();
Object obj = clazz.newInstance(); //通过调用Emp无参构造器,创建新的Emp对象
Method method = clazz.getDeclaredMethod("add", int.class,int.class);
Object result = method.invoke(obj, 200,300);
System.out.println(result);
}
/**
* 修改已有的方法的信息,修改方法体的内容
* @throws Exception
*/
public static void test03() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bjsxt.test.Emp");
CtMethod cm = cc.getDeclaredMethod("sayHello",new CtClass[]{CtClass.intType});
cm.insertBefore("System.out.println($1);System.out.println(\"start!!!\");");
cm.insertAt(9, "int b=3;System.out.println(\"b=\"+b);");//在每一行前面添加代码
cm.insertAfter("System.out.println(\"end!!!\");");
//通过反射调用新生成的方法
Class clazz = cc.toClass();
Object obj = clazz.newInstance(); //通过调用Emp无参构造器,创建新的Emp对象
Method method = clazz.getDeclaredMethod("sayHello", int.class);
method.invoke(obj, 300);
}
/**
* 属性的操作
* @throws Exception
*/
public static void test04() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bjsxt.test.Emp");
// CtField f1 = CtField.make("private int empno;", cc);
CtField f1 = new CtField(CtClass.intType,"salary",cc);
f1.setModifiers(Modifier.PRIVATE);
cc.addField(f1);
// cc.getDeclaredField("ename"); //获取指定的属性
//增加相应的set和get方法
cc.addMethod(CtNewMethod.getter("getSalary", f1));;
cc.addMethod(CtNewMethod.getter("setSalary", f1));;
}
/**
* 构造方法的操作
* @throws Exception
*/
public static void test05() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bjsxt.test.Emp");
CtConstructor[] cs = cc.getConstructors();
for (CtConstructor c : cs) {
System.out.println(c.getLongName());
}
}
public static void test06() throws Exception{
CtClass cc = ClassPool.getDefault().get("com.bjsxt.test.Emp");
Object[] all = cc.getAnnotations();
Author a = (Author)all[0];
String name = a.name();
int year = a.year();
System.out.println("name: " + name + ", year: " + year);
}
public static void main(String[] args) throws Exception {
test06();
}
<span style="font-size:14px;">}</span>
JVM内存分析:
JVM运行和类加载全过程:
类加载机制:JVM把class文件加载到内存,并对数据进行校验,解析和初始化。最终形成JVM可以直接使用的JAVA类型的全过程。
加载------链接(验证,准备,解析)------初始化
字节码的表现形式本质上就是一个字节数组。
加载:::::将CLASS文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象。作为方法区类数据的访问入口。这个过程需要类加载器参与。
链接:将JAVA类的二进制代码合并到JVM的运行状态之中的过程。
验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
准备:正式为类变量分配内存并设置类变量初始值的阶段。这些内存都将在方法区中进行分配。
解析:虚拟机常量池内的符号引用替换为直接引用的过程。
初始化:::
初始化阶段是执行类构造器<clinit>()方法的过程,类构造器<clinitt>()方法是由编译器来自动的收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的。
当初始化一个类的时候,如果其父类没有初始化,需要先初始化其父类。
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
当访问一个JAVA类的静态域时,只有真正声明这个域的类才会被初始化。
内存:堆,栈,方法区(特殊的堆)
先在方法区生成类的运行时数据:静态变量,静态方法,常量池,类的代码。
堆中:生成java.lang.Class对象。代理方法区中生成的类。
然后执行main方法,一个main方法栈。之后不断压栈。
类加载全过程之类的主动引用与被动引用:
主动引用(一定会发生类的初始化):
new一个类的对象
调用类的静态成员和静态方法(切记,除了final常量)
通过反射进行加载
当虚拟机启动时,java hello一定会初始化hello类
当初始化一个类,如果其父类没有初始化,则会初始化它的父类。
类的被动引用(不会发生类的初始化):
当访问一个静态域时,只有真正声明这个域的类才会发生初始化。
通过子类引用父类的静态域, 不会导致子类的初始化
通过数据定义类引用,不会触发此类的初始化。
引用常量不会触发此类的初始化。(常量在编译阶段就存入常量池中了)
package basic;
public class Demo01 {
static{
System.out.println("静态初始化Demo01");
}
public static void main(String[] args) throws Exception {
System.out.println("Demo01的main方法!");
System.out.println(System.getProperty("java.class.path"));
//主动引用
// new A();
// System.out.println(A.width);//静态初始化类A(静态变量和静态域)-->创建A类的对象(构造方法)-->300
// Class.forName("com.bjsxt.test.A");//反射调用也会导致初始化
//被动引用
// System.out.println(A.MAX);访问常量
// A[] as = new A[10];使用类声明数组
System.out.println(B.width);
}
}
class B extends A {
static {
System.out.println("静态初始化B");
}
}
class A extends A_Father {
public static int width=100; //静态变量,静态域 field
public static final int MAX=100; //final常量不会触发静态初始化
static {
System.out.println("静态初始化类A");
width=300;//先赋值为100,之后又更改成为300
}
public A(){
System.out.println("创建A类的对象");
}
}
class A_Father extends Object {
static {
System.out.println("静态初始化A_Father");
}
}