注解入门
Annotation是JDK5.0开始引入的新技术
Annotation的作用
不是程序本身,可以对程序作出解释。
可以被其他程序(比如:编译器等)读取。(注解信息处理流程,是注解和注释的重大区别。如果没有注解信息处理流程,则注解毫无意义)。
Annotation格式
注解是以“@注释名”在代码中存在的,可以添加一些参数值,例如:@SeppressWarnings(value=”unchecked”)。
Annotation在哪里使用?
可以附加在package,class,method,field等上面,相当于它它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些数据的访问。
内置注解
@Override:定义在java.lang.Override中,此注释只适用修饰方法,表示一个方法声明打算重写超类中的另一个方法声明。
@Deprecated:定义在java.lang.Deprecated中,此注释可以用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险吗或存在更好的选择。(一般不建议使用)
@SuppressWarnings:用来抑制编译器运行时的警告信息。
@SuppressWarnings("unchecked") 告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
@SuppressWarnings("serial") 如果编译器出现这样的警告信息:The serializable class WmailCalendar does not declare a static final serialVersionUID field of type long 使用这个注释将警告信息去掉。
@SuppressWarnings("deprecation") 如果使用了使用@Deprecated注释的方法,编译器将出现警告信息。 使用这个注释将警告信息去掉。
@SuppressWarnings("unchecked", "deprecation") 告诉编译器同时忽略unchecked和deprecation的警告信息。
@SuppressWarnings(value={"unchecked", "deprecation"}) 等同于@SuppressWarnings(“unchecked”, “deprecation”)
自定义注解、元注解 元注解
|所修饰的范围|取值ElemnetType| |-|-|-| |package包|PACKAGE| |类、接口、枚举、Annotation类型|TYPE| |类型成员(方法、构造器、成员变量、枚举值)|CONSTRUCTOR:用于描述构造器。 FIELD:用于描述域 METHOD:用于描述方法| |方法参数和本地变量|LOCAL_VALUE:用于描述局部变量 PARANETER:用于描述参数|
@Target(value=ElementType.Type)
@Retention
作用:表示需要在什么级别保存该注解,用于描述注解的生命周期。
取值 RetentionPolicy
作用
SOURCE
在源文件中有效(即源文件保存)
CLASS
在class文件中有效(即class保留)
RUNTIME
在运行时有效(即运行时保留) 为Runtime可以被反射机制读取
注意:
注解元素的参数的默认值一般设置为0或空字符窜;
也可能是-1,表示什么也没有
@Documented
作用:用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。它是一个标记注解,没有成员。
@Inherited
作用:用于表示某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类
反射机制读取注解
什么是ORM(Object Relationship Mapping)
使用注解完成类和表结构的映射关系
学了反射机制后,我们可以定义注解的执行流程读取这些注解,实现更复杂的功能
1 2 3 4 5 6 7 8 9 10 11 12 package annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target; @Target (value = ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)public @interface Table { String value () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target (value = ElementType.FIELD)@Retention (RetentionPolicy.RUNTIME)public @interface Field { String columnName () ; String type () ; int length () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package annotation;@Table ("tb_student" )public class Student { @Field (columnName="id" ,type="int" ,length=3 ) private int id; @Field (columnName="sname" ,type="varchar" ,length=10 ) private String studentName; @Field (columnName="age" ,type="int" ,length=10 ) private int age; public int getId () { return id; } public void setId (int id) { this .id = id; } public String getStudentName () { return studentName; } public void setStudentName (String studentName) { this .studentName = studentName; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package annotation;import java.lang.annotation.Annotation;public class Demo3 { public static void main (String[] args) { try { Class clazz=Class.forName("annotation.Student" ); Annotation[] annotations=clazz.getAnnotations(); for (Annotation a:annotations) { System.out.println(a); } Table table=(Table) clazz.getAnnotation(Table.class); System.out.println(table.value()); Field f=clazz.getDeclaredField("studentName" ).getAnnotation(Field.class); System.out.println(f.columnName() + "--->" + f.type() + "--->" + f.length()); } catch (Exception e) { e.printStackTrace(); } } }
Java的动态性
动态语言:程序运行时,可以改变程序结果或变量类。
Java可以称为准动态语言。
反射机制 reflection
指的是可以于运行时加载、探知、使用编译期间完全未知的类。
程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
1 Class c=Class.forName("包名.类名" );
加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,通过这个镜子可以看到类的结构,所以我们形象的称之为:反射。
Class类的对象如何获取
运用getClass()
运用Class.forName()(最常被使用)
运用类.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.zhuchuli.reflection;import com.zhuchuli.testBean.User;@SuppressWarnings ("all" )public class Demo01 { public static void main (String[] args) { String path= "com.zhuchuli.testBean.User" ; try { Class clazz = Class.forName(path); System.out.println(clazz.hashCode()); Class strClazz=String.class; Class strClazz2=path.getClass(); System.out.println(strClazz==strClazz2); Class intClaszz=int .class; } catch (Exception e) { e.printStackTrace(); } } }
动态加载类、动态获取类的信息(属性、方法、构造器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.zhuchuli.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class Demo02 { public static void main (String[] args) { String path= "com.zhuchuli.testBean.User" ; try { Class clazz = Class.forName(path); System.out.println("---------------------------------通过反射获取类名---------------------------------" ); System.out.println(clazz.getName()); System.out.println(clazz.getSimpleName()); System.out.println("------------------------------------------------------------------------------" ); System.out.println("---------------------------------通过反射获取属性---------------------------------" ); Field[] fields=clazz.getDeclaredFields(); Field f=clazz.getDeclaredField("uname" ); System.out.println(f); for (Field field:fields) { System.out.println("属性:" +field); } System.out.println("------------------------------------------------------------------------------" ); System.out.println("---------------------------------通过反射获取方法---------------------------------" ); Method[] methods=clazz.getMethods(); Method[] methodss=clazz.getDeclaredMethods(); Method m01=clazz.getDeclaredMethod("getUname" , null ); System.out.println(m01); Method m02=clazz.getDeclaredMethod("setUname" , String.class); System.out.println(m02); for (Method method:methodss) { System.out.println("方法:" +method); } System.out.println("------------------------------------------------------------------------------" ); System.out.println("---------------------------------通过反射获取构造器---------------------------------" ); Constructor[] constructors=clazz.getConstructors(); Constructor[] constructorss=clazz.getDeclaredConstructors(); for (Constructor constructor:constructorss) { System.out.println("构造器:" +constructor); } System.out.println("根据参数类型获取构造器" ); Constructor c=clazz.getDeclaredConstructor(null ); System.out.println("构造器:" +c); Constructor cc=clazz.getConstructor(int .class,String.class,int .class); System.out.println("构造器:" +cc); System.out.println("------------------------------------------------------------------------------" ); } catch (Exception e) { e.printStackTrace(); } } }
动态构造对象
动态调用类和对象的任意方法、构造器
动态调用和处理属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.zhuchuli.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import com.zhuchuli.testBean.User;public class Demo03 { public static void main (String[] args) { String path= "com.zhuchuli.testBean.User" ; try { Class clazz = Class.forName(path); User u=(User) clazz.getConstructor().newInstance(); System.out.println(u); Constructor<User> c=clazz.getConstructor(int .class,String.class,int .class); User u2=c.newInstance(1001 ,"lee" ,18 ); System.out.println(u2.getId()+"--" +u2.getUname()+"--" +u2.getAge()); User u3=(User) clazz.getConstructor().newInstance(); Method method=clazz.getDeclaredMethod("setUname" , String.class); method.invoke(u3, "lee2" ); System.out.println(u3.getUname()); User u4=(User) clazz.getConstructor().newInstance(); Field f=clazz.getDeclaredField("uname" ); f.setAccessible(true ); f.set(u4, "lee3" ); System.out.println(u4.getUname()); System.out.println(f.get(u4)); } catch (Exception e) { e.printStackTrace(); } } }
获取泛型信息(Generic)
Java使用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器JavaC使用的,确保数据的安全性和免去强制类型转换的麻烦。但是编译一旦完成,所有和泛型有关的类全部被擦除。
为了通过反射操作这些类型以迎合实际开发的需要,Java新增了PatameterType,GenericArrayType,TypeVariable,和WildCardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
ParameterizedType:表示一种参数化的类型,比如Collection< String >.
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型.
TypeVariable:是各种类型变量的公共父接.
WildcardType:代表一种通配符类型表达式,比如?、? extends Number、? super Integer。(wildcard是一个单词:就是”通配符“).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.zhuchuli.reflection;import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.List;import java.util.Map;import com.zhuchuli.testBean.User;public class Demo04 { public void test01 (Map<String,User> map,List<User> list) { System.out.println("Demo04 test01()" ); } public Map<Integer,User> test02 () { System.out.println("Demo01 test02()" ); return null ; } public static void main (String[] args) { try { Method method = Demo04.class.getDeclaredMethod("test01" , Map.class, List.class); Type[] t=method.getGenericParameterTypes(); for (Type paramType:t) { System.out.println("#" +paramType); if (paramType instanceof ParameterizedType) { Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments(); for (Type genericType:genericTypes) { System.out.println("泛型类型:" + genericType); } } } Method method2=Demo04.class.getDeclaredMethod("test02" ,null ); Type returnType=method2.getGenericReturnType(); if (returnType instanceof ParameterizedType) { Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments(); for (Type genericType:genericTypes) { System.out.println("泛型类型:" + genericType); } } } catch (Exception e) { e.printStackTrace(); } } }
反射机制的性能问题
setAccessible
启动和禁用访问安全检查的机关,值为true则指示反射的对象在使用时应该取消Java语言访问检查。值为false则指示表明反射的对象应该实施Java语言访问检查。并不是为true就能访问为false就不能访问。
禁止安全检查,可以提高反射的运行速度。
可以考虑使用:cglib/javaassist字节码操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.zhuchuli.reflection;import java.lang.reflect.Method;import com.zhuchuli.testBean.User;public class Demo05 { public static void test01 () { User user=new User(); long before=System.currentTimeMillis(); for (var i=0 ;i<1000000000L ;i++) { user.getUname(); } long after=System.currentTimeMillis(); System.out.println("没有反射,共消耗:<" +(after-before)+">毫秒" ); } public static void test02 () throws Exception { User user=new User(); Method method=Class.forName("com.zhuchuli.testBean.User" ).getDeclaredMethod("getUname" , null ); long before=System.currentTimeMillis(); for (var i=0 ;i<1000000000L ;i++) { method.invoke(user, null ); } long after=System.currentTimeMillis(); System.out.println("加安全检查,共消耗:<" +(after-before)+">毫秒" ); } public static void test03 () throws Exception { User user=new User(); Method method=Class.forName("com.zhuchuli.testBean.User" ).getDeclaredMethod("getUname" , null ); method.setAccessible(true ); long before=System.currentTimeMillis(); for (var i=0 ;i<1000000000L ;i++) { method.invoke(user, null ); } long after=System.currentTimeMillis(); System.out.println("不加安全检查,共消耗:<" +(after-before)+">毫秒" ); } public static void main (String[] args) throws Exception { test01(); test02(); test03(); } }
动态编译
Java6.0引入了动态编译机制
动态编译的应用场景
可以做一个浏览器端编写java代码,上传服务器编译和运行的在线测评系统。
服务器动态加载某些类文件进行编译。
动态编译的两种方式
通过Runtime调用javac,启动新的进程去操作
Runtime run=Runtime.getRuntime();
Process process=run.exec(javac -cp d:/myjava/ HelloWorld.java);
通过JavaCompiler动态编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.zhuchuli.test;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import javax.tools.JavaCompiler;import javax.tools.ToolProvider;public class Demo01 { public static void main (String[] args) throws IOException { String str="public class Hi{public static void main(String[] args[]) {System.out.println(\"Hello Hi haha\");}}" ; BufferedWriter br=new BufferedWriter(new FileWriter("d:/MyJava/Hi.java" )); br.write(str); br.flush(); JavaCompiler complier=ToolProvider.getSystemJavaCompiler(); int result=complier.run(null , null , null ,"d:/MyJava/Hi.java" ); System.out.println(result==0 ?"编译成功" :"编译失败" ); br.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.zhuchuli.test;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import java.io.InputStreamReader;import javax.tools.JavaCompiler;import javax.tools.ToolProvider;public class Demo02 { public static void main (String[] args) throws IOException { String str="public class Hi{public static void main(String[] args) {System.out.println(\"Hello Hi haha\");}}" ; BufferedWriter bw=new BufferedWriter(new FileWriter("d:/MyJava/Hi.java" )); bw.write(str); bw.flush(); JavaCompiler complier=ToolProvider.getSystemJavaCompiler(); int result=complier.run(null , null , null ,"d:/MyJava/Hi.java" ); System.out.println(result==0 ?"编译成功" :"编译失败" ); Runtime run=Runtime.getRuntime(); Process process=run.exec("java -cp d:/MyJava Hi" ); BufferedReader br=new BufferedReader(new InputStreamReader(process.getInputStream())); String info="" ; while ((info=br.readLine())!=null ) { System.out.println(info); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.zhuchuli.test;import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;import javax.tools.JavaCompiler;import javax.tools.ToolProvider;public class Demo03 { public static void main (String[] args) throws IOException { String str="public class Hi{public static void main(String[] args) {System.out.println(\"Hello Hi haha\");}}" ; BufferedWriter bw=new BufferedWriter(new FileWriter("d:/MyJava/Hi.java" )); bw.write(str); bw.flush(); JavaCompiler complier=ToolProvider.getSystemJavaCompiler(); int result=complier.run(null , null , null ,"d:/MyJava/Hi.java" ); System.out.println(result==0 ?"编译成功" :"编译失败" ); try { URL[] urls=new URL[] {new URL("file:/" +"d:/MyJava/" )}; URLClassLoader loader=new URLClassLoader(urls); Class c=loader.loadClass("Hi" ); Method m=c.getMethod("main" , String[].class); m.invoke(null , (Object)new String[] {}); }catch (Exception e) { e.printStackTrace(); } } }
编译结果
动态执行javascript代码
Java脚本引擎是从JDK6.0之后添加的新功能
脚本引擎介绍
使得Java应用程序可以通过一套固定的接口与各种脚本引擎交互,从而达到在Java平台上调用各种脚本语言的目的。
Java脚本API是连通Java平台和脚本语言的桥梁。
可以把一些复杂异变的业务逻辑交给与脚本语言处理,这又大大提高了开发效率。
获得脚本引擎
ScriptEngineManager sem=new ScriptEngineManager();
ScriptEngine engine=sem.getEngineByName(“javascript”);
Java脚本API为开发者提供了如下功能:
获取脚本程序输入,通过脚本引擎运行脚本并返回运行结果,这是最核心的接口。
注意是:接口。Java可以使用各种不同的表现,从而通用的调用js、groovy、python等脚本
js使用了:Rhino,Rhino是一种使用Java语言编写的JavaScript的开源实现,原先是由Mozilla开发,现在被集成进入JDK6.0
通过脚本引擎的运行上下文在脚本和Java平台间交换数据。
通过Java应用程序调用脚本函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.zhuchuli.rhino;import java.io.FileNotFoundException;import java.io.FileReader;import java.net.URL;import java.util.List;import javax.script.Invocable;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import javax.script.ScriptException;public class Demo01 { public static void main (String[] args) throws ScriptException, NoSuchMethodException { ScriptEngineManager sem=new ScriptEngineManager(); ScriptEngine engine=sem.getEngineByName("javascript" ); engine.put("msg" , "lee is a good student" ); String str="var user= {name:'aaa',age:18,schools:['gdaib','dgef']};" ; str += "print(user.schools);" ; engine.eval(str); engine.eval("msg='very happy today'" ); System.out.println(engine.get("msg" )); System.out.println("--------------------" ); engine.eval("function add(a,b){var sum=a+b;return sum;}" ); Invocable jsInvoke=(Invocable) engine; Object i=jsInvoke.invokeFunction("add" , new Object[] {10 ,20 }); System.out.println(i); String jsCode="var list=java.util.Arrays.asList([\"北京尚学堂\",\"北京大学\"]);" ; engine.eval(jsCode); List<String> list2=(List<String>) engine.get("list" ); for (String temp:list2) { System.out.println(temp); } URL url=Demo01.class.getClassLoader().getResource("a.js" ); try { FileReader fr = new FileReader(url.getPath()); engine.eval(fr); fr.close(); } catch (Exception e) { e.printStackTrace(); } } }
动态字字节码操作:javaassist
Java动态性的两种常见实现方式:
运行时操作字节码可以让我们实现如下功能
动态生成新的类。
动态改变某个类的结构(添加、删除、修改 新的属性、方法 )
优势
比反射开销小,性能高。
Javaassist性能高于反射,低于ASM
常见的字节码操作类库
BCEL 基于底层Jvm的操作和指令
ASM 基于底层Jvm的操作和指令
CGLIB 基于ASM实现
Javaassist
Javaassist库的API详解
javaassist的最外层的API和java的反射包中的API颇为类似。
它主要有CtClass,CtMethod,以及CtField几个类组成。用以执行和JDK反射API中java.lang.Class,java.lang.reflection.Method,java.lang.reflection.Field相同的操作。
JAVAassist库的简单使用。
创建一个全新的类
使用XJAD反编译工具,将生成的class文件反编译成Java文件\
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.zhuchuli.javaassist;import java.io.IOException;import javassist.CannotCompileException;import javassist.ClassPool;import javassist.CtClass;import javassist.CtConstructor;import javassist.CtField;import javassist.CtMethod;import javassist.NotFoundException;public class Demo01 { public static void main (String[] args) throws CannotCompileException, NotFoundException, IOException { ClassPool pool=ClassPool.getDefault(); CtClass cc=pool.makeClass("com.zhuchuli.bean.Emp" ); CtField f1=CtField.make("private String ename;" ,cc); CtField f2=CtField.make("private int empno;" ,cc); cc.addField(f1); cc.addField(f2); CtMethod c1=CtMethod.make("public String getEname(){return ename;} " , cc); CtMethod c2=CtMethod.make("public void setEname(String ename){this.ename=ename;} " , cc); cc.addMethod(c1); cc.addMethod(c2); CtMethod c3=CtMethod.make("public int getEmpno(){return empno;} " , cc); CtMethod c4=CtMethod.make("public void setEmpno(int empno){this.empno=empno;} " , cc); cc.addMethod(c3); cc.addMethod(c4); CtConstructor constructor=new CtConstructor(new CtClass[] {pool.get("java.lang.String" ), CtClass.intType}, cc); constructor.setBody("{this.ename=ename;this.empno=empno;}" ); CtConstructor constructor2=new CtConstructor(null ,cc); cc.addConstructor(constructor); cc.addConstructor(constructor2); cc.writeFile("d:/MyJava/Emp.java" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.zhuchuli.javaassist;import java.lang.reflect.Method;import java.util.Arrays;import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import javassist.Modifier;public class Demo02 { public static void test01 () { ClassPool pool=ClassPool.getDefault(); try { CtClass cc = pool.get("com.zhuchuli.javaassist.Emp" ); byte [] bytes=cc.toBytecode(); System.out.println(Arrays.toString(bytes)); System.out.println(cc.getName()); System.out.println(cc.getSuperclass()); System.out.println(cc.getInterfaces()); System.out.println(cc.getSimpleName ()); } catch (Exception e) { e.printStackTrace(); } } public static void test02 () { ClassPool pool=ClassPool.getDefault(); try { CtClass cc = pool.get("com.zhuchuli.javaassist.Emp" ); CtMethod m=new CtMethod(CtClass.intType, "add" , new CtClass[] {CtClass.intType,CtClass.intType},cc); m.setModifiers(Modifier.PUBLIC); m.setBody("{System.out.println(\"test02\");return $1+$2;}" ); cc.addMethod(m); Class<?> clazz=cc.toClass(); Object obj=clazz.getConstructor().newInstance(); Method method=clazz.getDeclaredMethod("add" , int .class,int .class); Object result=method.invoke(obj, 200 ,300 ); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } public static void main (String[] args) { test01(); } }
JVM运行和类加载全过程
为什么要研究加载全过程?
有助于了解JVM运行过程
更深入了解Java动态性(解耦部署,动态加载),提高程序的灵活性。
类加载全过程 (如下图所示)
加载
链接 (验证 准备 解析):将Java类的二进制代码合并到JVM的运行状态之中的过程。
初始化:静态属性或静态方法或静态块只会被初始化一次。
使用
卸载
类的主动引用(一定会发生类初始化)
new一个类的对象。
调用类的静态成员(除了final常量)和静态方法。
使用java.lang.reflect包的方法对类进行反射调用。
当虚拟机启动,java hello则一定会初始化Hello类,说白了就是先启动main方法所在的类。
当初始化一个类,如果其父类没有被初始化,则会先初始化他的父类。
类的被动引用
当访问一个静态域时,只有真正声明这个域的类才会被初始化。
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)。
深入类加载器
类加载器原理
类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾集成器可以回收这些Class对象。
类加载器树状结构,双亲委托(代理)机制
引导类加载器(bootstrap class loader)
扩展类加载器(extensions class loader)
应用程序类加载器(application clasas loader)
自定义类加载器
双亲委托机制是为了保证Java核心库的类型安全。(请求 —> 交给父类 —> 交给爷爷辈)
tomcat则与此相反
自定义类加载器(文件、网络、加密)
注意:同一个类被不同的加载器加载,JVM也会认为他们不是相同的类
线程上下文类加载器
服务器类加载器原理和OSGI介绍(面向Java的动态模块系统)