步子百科步子百科

jc是什么意思(Java中的屠龙之术)

面试终败“高并发”,什意思J术25天苦心钻研,什意思J术居然整出一份并发宝典?都是什意思J术“算法”惹的祸,字节三面处处坑,什意思J术我的什意思J术offer要凉了?春招指南之“性能调优”:MySQL+Tomcat+JVM,还怕面试官的什意思J术轰炸?JCTree的介绍

JCTree是语法树元素的基类,包含一个重要的什意思J术字段pos,该字段用于指明当前语法树节点(JCTree)在语法树中的什意思J术位置,因此我们不能直接用new关键字来创建语法树节点,什意思J术即使创建了也没有意义。什意思J术此外,什意思J术结合访问者模式,什意思J术将数据结构与数据的什意思J术处理进行解耦,部分源码如下:

1publicabstractclassJCTreeimplementsTree,Cloneable,DiagnosticPosition{ 2 3publicintpos=-1; 4 5... 6 7publicabstractvoidaccept(JCTree.Visitorvisitor); 8 9...10}

我们可以看到JCTree是什意思J术一个抽象类,这里重点介绍几个JCTree的什意思J术子类

JCStatement:声明语法树节点,常见的子类如下 JCBlock:语句块语法树节点 JCReturn:return语句语法树节点 JCClassDecl:类定义语法树节点 JCVariableDecl:字段/变量定义语法树节点JCMethodDecl:方法定义语法树节点JCModifiers:访问标志语法树节点JCExpression:表达式语法树节点,常见的子类如下 JCAssign:赋值语句语法树节点 JCIdent:标识符语法树节点,可以是变量,类型,关键字等等

TreeMaker介绍

TreeMaker用于创建一系列的语法树节点,我们上面说了创建JCTree不能直接使用new关键字来创建,所以Java为我们提供了一个工具,就是TreeMaker,它会在创建时为我们创建的JCTree对象设置pos字段,所以必须使用上下文相关的TreeMaker对象来创建语法树节点。

具体的API介绍可以参照,TreeMakerAPI,接下来着重介绍一下常用的几个方法。

TreeMaker.Modifiers

TreeMaker.Modifiers方法用于创建访问标志语法树节点(JCModifiers),源码如下

1publicJCModifiersModifiers(longflags){ 2returnModifiers(flags,List.<JCAnnotation>nil()); 3} 4 5publicJCModifiersModifiers(longflags, 6List<JCAnnotation>annotations){ 7JCModifierstree=newJCModifiers(flags,annotations); 8booleannoFlags=(flags&(Flags.ModifierFlags|Flags.ANNOTATION))==0; 9tree.pos=(noFlags&&annotations.isEmpty())?Position.NOPOS:pos;10returntree;11}flags:访问标志annotations:注解列表

其中flags可以使用枚举类com.sun.tools.javac.code.Flags来表示,例如我们可以这样用,就生成了下面的访问标志了。

1treeMaker.Modifiers(Flags.PUBLIC+Flags.STATIC+Flags.FINAL);23publicstaticfinalTreeMaker.ClassDef

TreeMaker.ClassDef用于创建类定义语法树节点(JCClassDecl),源码如下:

1publicJCClassDeclClassDef(JCModifiersmods, 2Namename, 3List<JCTypeParameter>typarams, 4JCExpressionextending, 5List<JCExpression>implementing, 6List<JCTree>defs){ 7JCClassDecltree=newJCClassDecl(mods, 8name, 9typarams,10extending,11implementing,12defs,13null);14tree.pos=pos;15returntree;16}mods:访问标志,可以通过TreeMaker.Modifiers来创建name:类名typarams:泛型参数列表extending:父类implementing:实现的接口defs:类定义的详细语句,包括字段、方法的定义等等TreeMaker.MethodDef

TreeMaker.MethodDef用于创建方法定义语法树节点(JCMethodDecl),源码如下

1publicJCMethodDeclMethodDef(JCModifiersmods, 2Namename, 3JCExpressionrestype, 4List<JCTypeParameter>typarams, 5List<JCVariableDecl>params, 6List<JCExpression>thrown, 7JCBlockbody, 8JCExpressiondefaultValue){ 9JCMethodDecltree=newJCMethodDecl(mods,10name,11restype,12typarams,13params,14thrown,15body,16defaultValue,17null);18tree.pos=pos;19returntree;20}2122publicJCMethodDeclMethodDef(MethodSymbolm,23Typemtype,24JCBlockbody){ 25return(JCMethodDecl)26newJCMethodDecl(27Modifiers(m.flags(),Annotations(m.getAnnotationMirrors())),28m.name,29Type(mtype.getReturnType()),30TypeParams(mtype.getTypeArguments()),31Params(mtype.getParameterTypes(),m),32Types(mtype.getThrownTypes()),33body,34null,35m).setPos(pos).setType(mtype);36}mods:访问标志name:方法名restype:返回类型typarams:泛型参数列表params:参数列表thrown:异常声明列表body:方法体defaultValue:默认方法(可能是interface中的哪个default)m:方法符号mtype:方法类型。包含多种类型,泛型参数类型、方法参数类型、异常参数类型、返回参数类型。

返回类型restype填写null或者treeMaker.TypeIdent(TypeTag.VOID)都代表返回void类型

TreeMaker.VarDef

TreeMaker.VarDef用于创建字段/变量定义语法树节点(JCVariableDecl),源码如下

1publicJCVariableDeclVarDef(JCModifiersmods, 2Namename, 3JCExpressionvartype, 4JCExpressioninit){ 5JCVariableDecltree=newJCVariableDecl(mods,name,vartype,init,null); 6tree.pos=pos; 7returntree; 8} 910publicJCVariableDeclVarDef(VarSymbolv,11JCExpressioninit){ 12return(JCVariableDecl)13newJCVariableDecl(14Modifiers(v.flags(),Annotations(v.getAnnotationMirrors())),15v.name,16Type(v.type),17init,18v).setPos(pos).setType(v.type);19}mods:访问标志name:参数名称vartype:类型init:初始化语句v:变量符号TreeMaker.Ident

TreeMaker.Ident用于创建标识符语法树节点(JCIdent),源码如下

1publicJCIdentIdent(Namename){ 2JCIdenttree=newJCIdent(name,null); 3tree.pos=pos; 4returntree; 5} 6 7publicJCIdentIdent(Symbolsym){ 8return(JCIdent)newJCIdent((sym.name!=names.empty) 9?sym.name10:sym.flatName(),sym)11.setPos(pos)12.setType(sym.type);13}1415publicJCExpressionIdent(JCVariableDeclparam){ 16returnIdent(param.sym);17}TreeMaker.Return

TreeMaker.Return用于创建return语句(JCReturn),源码如下

1publicJCReturnReturn(JCExpressionexpr){ 2JCReturntree=newJCReturn(expr);3tree.pos=pos;4returntree;5}TreeMaker.Select

TreeMaker.Select用于创建域访问/方法访问(这里的方法访问只是取到名字,方法的调用需要用TreeMaker.Apply)语法树节点(JCFieldAccess),源码如下

1publicJCFieldAccessSelect(JCExpressionselected, 2Nameselector) 3{ 4JCFieldAccesstree=newJCFieldAccess(selected,selector,null); 5tree.pos=pos; 6returntree; 7} 8 9publicJCExpressionSelect(JCExpressionbase,10Symbolsym){ 11returnnewJCFieldAccess(base,sym.name,sym).setPos(pos).setType(sym.type);12}selected:.运算符左边的表达式selector:.运算符右边的表达式

下面给出一个例子,一语句生成的Java语句就是二语句

1一.TreeMaker.Select(treeMaker.Ident(names.fromString("this")),names.fromString("name"));23二.this.nameTreeMaker.NewClass

TreeMaker.NewClass用于创建new语句语法树节点(JCNewClass),源码如下:

1publicJCNewClassNewClass(JCExpressionencl,2List<JCExpression>typeargs,3JCExpressionclazz,4List<JCExpression>args,5JCClassDecldef){ 6JCNewClasstree=newJCNewClass(encl,typeargs,clazz,args,def);7tree.pos=pos;8returntree;9}encl:不太明白此参数的含义,我看很多例子中此参数都设置为nulltypeargs:参数类型列表clazz:待创建对象的类型args:参数列表def:类定义TreeMaker.Apply

TreeMaker.Apply用于创建方法调用语法树节点(JCMethodInvocation),源码如下:

1publicJCMethodInvocationApply(List<JCExpression>typeargs,2JCExpressionfn,3List<JCExpression>args){ 4JCMethodInvocationtree=newJCMethodInvocation(typeargs,fn,args);5tree.pos=pos;6returntree;7}typeargs:参数类型列表fn:调用语句args:参数列表TreeMaker.Assign

TreeMaker.Assign用户创建赋值语句语法树节点(JCAssign),源码如下:

1ublicJCAssignAssign(JCExpressionlhs,2JCExpressionrhs){ 3JCAssigntree=newJCAssign(lhs,rhs);4tree.pos=pos;5returntree;6}lhs:赋值语句左边表达式rhs:赋值语句右边表达式TreeMaker.Exec

TreeMaker.Exec用于创建可执行语句语法树节点(JCExpressionStatement),源码如下:

1publicJCExpressionStatementExec(JCExpressionexpr){ 2JCExpressionStatementtree=newJCExpressionStatement(expr);3tree.pos=pos;4returntree;5}

TreeMaker.Apply以及TreeMaker.Assign就需要外面包一层TreeMaker.Exec来获得一个JCExpressionStatement

TreeMaker.Block

TreeMaker.Block用于创建组合语句的语法树节点(JCBlock),源码如下:

1publicJCBlockBlock(longflags,2List<JCStatement>stats){ 3JCBlocktree=newJCBlock(flags,stats);4tree.pos=pos;5returntree;6}flags:访问标志stats:语句列表com.sun.tools.javac.util.List介绍

在我们操作抽象语法树的时候,有时会涉及到关于List的操作,但是这个List不是我们经常使用的java.util.List而是com.sun.tools.javac.util.List,这个List比较奇怪,是一个链式的结构,有头结点和尾节点,但是只有尾节点是一个List,这里作为了解就行了。

1publicclassList<A>extendsAbstractCollection<A>implementsjava.util.List<A>{ 2publicAhead; 3publicList<A>tail; 4privatestaticfinalList<?>EMPTY_LIST=newList<Object>((Object)null,(List)null){ 5publicList<Object>setTail(List<Object>var1){ 6thrownewUnsupportedOperationException(); 7} 8 9publicbooleanisEmpty(){ 10returntrue;11}12};1314List(Ahead,List<A>tail){ 15this.tail=tail;16this.head=head;17}1819publicstatic<A>List<A>nil(){ 20returnEMPTY_LIST;21}2223publicList<A>prepend(Avar1){ 24returnnewList(var1,this);25}2627publicList<A>append(Avar1){ 28returnof(var1).prependList(this);29}3031publicstatic<A>List<A>of(Avar0){ 32returnnewList(var0,nil());33}3435publicstatic<A>List<A>of(Avar0,Avar1){ 36returnnewList(var0,of(var1));37}3839publicstatic<A>List<A>of(Avar0,Avar1,Avar2){ 40returnnewList(var0,of(var1,var2));41}4243publicstatic<A>List<A>of(Avar0,Avar1,Avar2,A...var3){ 44returnnewList(var0,newList(var1,newList(var2,from(var3))));45}4647...48}com.sun.tools.javac.util.ListBuffer

由于com.sun.tools.javac.util.List使用起来不方便,所以又在其上面封装了一层,这个封装类是ListBuffer,此类的操作和我们平时经常使用的java.util.List用法非常类似。

1publicclassListBuffer<A>extendsAbstractQueue<A>{ 2 3publicstatic<T>ListBuffer<T>of(Tx){ 4ListBuffer<T>lb=newListBuffer<T>(); 5lb.add(x); 6returnlb; 7} 8 9/**Thelistofelementsofthisbuffer.10*/11privateList<A>elems;1213/**Apointerpointingtothelastelementof'elems'containingdata,14*ornullifthelistisempty.15*/16privateList<A>last;1718/**Thenumberofelementinthisbuffer.19*/20privateintcount;2122/**Hasalistbeencreatedfromthisbufferyet?23*/24privatebooleanshared;2526/**Createanewinitiallyemptylistbuffer.27*/28publicListBuffer(){ 29clear();30}3132/**Appendanelementtobuffer.33*/34publicListBuffer<A>append(Ax){ 35x.getClass();//nullcheck36if(shared)copy();37List<A>newLast=List.<A>of(x);38if(last!=null){ 39last.tail=newLast;40last=newLast;41}else{ 42elems=last=newLast;43}44count++;45returnthis;46}47........48}com.sun.tools.javac.util.Names介绍

这个是为我们创建名称的一个工具类,无论是类、方法、参数的名称都需要通过此类来创建。它里面经常被使用到的一个方法就是fromString(),一般使用方法如下所示。

1Namesnames=newNames()2names.fromString("setName");实战演练

上面我们大概了解了如何操作抽象语法树,接下来我们就来写几个真实的案例加深理解。

变量相关

在类中我们经常操作的参数就是变量,那么如何使用抽象语法树的特性为我们操作变量呢?接下来我们就将一些对于变量的一些操作。

生成变量

例如生成private String age;这样一个变量,借用我们上面讲的VarDef方法

1//生成参数例如:privateStringage;2treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString("age"),treeMaker.Ident(names.fromString("String")),null);对变量赋值

例如我们想生成private String name = "BuXueWuShu",还是利用VarDef方法

1//privateStringname="BuXueWuShu"2treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString("name"),treeMaker.Ident(names.fromString("String")),treeMaker.Literal("BuXueWuShu"))两个字面量相加

例如我们生成String add = "a" + "b";,借用我们上面讲的Exec方法和Assign方法

1//add="a"+"b"2treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")),treeMaker.Binary(JCTree.Tag.PLUS,treeMaker.Literal("a"),treeMaker.Literal("b"))))+=语法

例如我们想生成add += "test",则和上面字面量差不多。

1//add+="test"2treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG,treeMaker.Ident(names.fromString("add")),treeMaker.Literal("test")))++语法

例如想生成++i

1treeMaker.Exec(treeMaker.Unary(JCTree.Tag.PREINC,treeMaker.Ident(names.fromString("i"))))方法相关

我们对于变量进行了操作,那么基本上都是要生成方法的,那么如何对方法进行生成和操作呢?我们接下来演示一下关于方法相关的操作方法。

无参无返回值

我们可以利用上面讲到的MethodDef方法进行生成

1/* 2无参无返回值的方法生成 3publicvoidtest(){ 4 5} 6*/ 7//定义方法体 8ListBuffer<JCTree.JCStatement>testStatement=newListBuffer<>(); 9JCTree.JCBlocktestBody=treeMaker.Block(0,testStatement.toList());1011JCTree.JCMethodDecltest=treeMaker.MethodDef(12treeMaker.Modifiers(Flags.PUBLIC),//方法限定值13names.fromString("test"),//方法名14treeMaker.Type(newType.JCVoidType()),//返回类型15com.sun.tools.javac.util.List.nil(),16com.sun.tools.javac.util.List.nil(),17com.sun.tools.javac.util.List.nil(),18testBody,//方法体19null20);有参无返回值

我们可以利用上面讲到的MethodDef方法进行生成

1/* 2无参无返回值的方法生成 3publicvoidtest2(Stringname){ 4name="xxxx"; 5} 6*/ 7ListBuffer<JCTree.JCStatement>testStatement2=newListBuffer<>(); 8testStatement2.append(treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("name")),treeMaker.Literal("xxxx")))); 9JCTree.JCBlocktestBody2=treeMaker.Block(0,testStatement2.toList());1011//生成入参12JCTree.JCVariableDeclparam=treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER),names.fromString("name"),treeMaker.Ident(names.fromString("String")),null);13com.sun.tools.javac.util.List<JCTree.JCVariableDecl>parameters=com.sun.tools.javac.util.List.of(param);1415JCTree.JCMethodDecltest2=treeMaker.MethodDef(16treeMaker.Modifiers(Flags.PUBLIC),//方法限定值17names.fromString("test2"),//方法名18treeMaker.Type(newType.JCVoidType()),//返回类型19com.sun.tools.javac.util.List.nil(),20parameters,//入参21com.sun.tools.javac.util.List.nil(),22testBody2,23null24);有参有返回值 1/* 2有参有返回值 3publicStringtest3(Stringname){ 4returnname; 5} 6*/ 7 8ListBuffer<JCTree.JCStatement>testStatement3=newListBuffer<>(); 9testStatement3.append(treeMaker.Return(treeMaker.Ident(names.fromString("name"))));10JCTree.JCBlocktestBody3=treeMaker.Block(0,testStatement3.toList());1112//生成入参13JCTree.JCVariableDeclparam3=treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER),names.fromString("name"),treeMaker.Ident(names.fromString("String")),null);14com.sun.tools.javac.util.List<JCTree.JCVariableDecl>parameters3=com.sun.tools.javac.util.List.of(param3);1516JCTree.JCMethodDecltest3=treeMaker.MethodDef(17treeMaker.Modifiers(Flags.PUBLIC),//方法限定值18names.fromString("test4"),//方法名19treeMaker.Ident(names.fromString("String")),//返回类型20com.sun.tools.javac.util.List.nil(),21parameters3,//入参22com.sun.tools.javac.util.List.nil(),23testBody3,24null25);特殊的

我们学完了如何进行定义参数,如何进行定义方法,其实还有好多语句需要学习,例如如何生成new语句,如何生成方法调用的语句,如何生成if语句。j接下来我们就学习一些比较特殊的语法。

new一个对象 1//创建一个new语句CombatJCTreeMaincombatJCTreeMain=newCombatJCTreeMain(); 2JCTree.JCNewClasscombatJCTreeMain=treeMaker.NewClass( 3null, 4com.sun.tools.javac.util.List.nil(), 5treeMaker.Ident(names.fromString("CombatJCTreeMain")), 6com.sun.tools.javac.util.List.nil(), 7null 8); 9JCTree.JCVariableDecljcVariableDecl1=treeMaker.VarDef(10treeMaker.Modifiers(Flags.PARAMETER),11names.fromString("combatJCTreeMain"),12treeMaker.Ident(names.fromString("CombatJCTreeMain")),13combatJCTreeMain14);方法调用(无参) 1JCTree.JCExpressionStatementexec=treeMaker.Exec( 2treeMaker.Apply( 3com.sun.tools.javac.util.List.nil(), 4treeMaker.Select( 5treeMaker.Ident(names.fromString("combatJCTreeMain")),//.左边的内容 6names.fromString("test")//.右边的内容 7), 8com.sun.tools.javac.util.List.nil() 9)10);方法调用(有参) 1//创建一个方法调用combatJCTreeMain.test2("helloworld!"); 2JCTree.JCExpressionStatementexec2=treeMaker.Exec( 3treeMaker.Apply( 4com.sun.tools.javac.util.List.nil(), 5treeMaker.Select( 6treeMaker.Ident(names.fromString("combatJCTreeMain")),//.左边的内容 7names.fromString("test2")//.右边的内容 8), 9com.sun.tools.javac.util.List.of(treeMaker.Literal("helloworld!"))//方法中的内容10)11);if语句 1/* 2创建一个if语句 3if("BuXueWuShu".equals(name)){ 4add="a"+"b"; 5}else{ 6add+="test"; 7} 8*/ 9//"BuXueWuShu".equals(name)10JCTree.JCMethodInvocationapply=treeMaker.Apply(11com.sun.tools.javac.util.List.nil(),12treeMaker.Select(13treeMaker.Literal("BuXueWuShu"),//.左边的内容14names.fromString("equals")//.右边的内容15),16com.sun.tools.javac.util.List.of(treeMaker.Ident(names.fromString("name")))17);18//add="a"+"b"19JCTree.JCExpressionStatementexec3=treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")),treeMaker.Binary(JCTree.Tag.PLUS,treeMaker.Literal("a"),treeMaker.Literal("b"))));20//add+="test"21JCTree.JCExpressionStatementexec1=treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG,treeMaker.Ident(names.fromString("add")),treeMaker.Literal("test")));2223JCTree.JCIfanIf=treeMaker.If(24apply,//if语句里面的判断语句25exec3,//条件成立的语句26exec1//条件不成立的语句27);总结

纸上得来终觉浅,绝知此事要躬行。希望大家看完此篇文章能够自己在本机上自己试验一下。自己设置几个参数,自己学的Lombok学着生成一下get、set方法,虽然本篇知识在日常开发中基本上不会用到,但是万一用到了这些知识那么别人不会而你会,差距其实就慢慢的给拉开了。

作者:不学无数的程序员原文链接:https://juejin.im/post/5e7c540ff265da42e16b02fc