上一篇文章的传送门:关于一些基础的Java问题的解答(七)
JNI的使用
先简单介绍一下JNI,JNI即Java Native Interface的缩写,中文译为“Java本地调用”。通俗的说,JNI是一种实现Java层与Native层(C/C++)交互的技术。有时为了追求效率问题,或者是使用用native代码编写的函数库,我们就不得不使用JNI接口。
以下是一个JNI的小例子:
1 | public class Main { |
我们在Java中使用JNI接口只需要两步:
- 使用native关键字声明某方法为本地方法
- 使用System.loadLibrary加载由C/C++编写成的动态链接库(我们只需要写出库名字即可,Java会根据平台补充库的后缀名windows:dll,linux:so)
接下来我们来看看如何编写Native层的cpp文件(以下注册Native函数方法为静态注册,动态注册本文不提及):
为了让Java层中的函数与Native层中的函数一一对应,JNI规定了一套复杂的命名体系。在此本文就不深入介绍该命名方法了,我们使用JDK提供的javah工具生成对应的.h头文件:
首先我们把写好的Java代码编译成.class文件:
然后我们使用javah工具生成对应的.h头文件(-o后面第一个参数为.h的文件名,第二个参数为.class的文件名):
然后我们就能看到生成的.h文件了:
以下是.h文件中的代码:
1 | /* DO NOT EDIT THIS FILE - it is machine generated */ |
可以看到我们在Java层定义的printString方法对应成了Native层的Java_Main_printString方法,方法上面有javah给我们生成的注释,它提供了以下信息:
- Class:方法所属于的类
- Method:方法的名称
- Signature:方法的签名。签名是由于Java中的方法允许重载,仅仅通过类与名称并不能确定该Native方法所对应的Java方法,因此JNI技术中就将参数类型和返回值的组合作为了一个函数的签名信息,有了签名和函数名我们才能顺利找到Javac层中对应的函数
接下来我们只要写一个.cpp文件实现该方法即可:
1 |
|
以上代码相信注释已解释的非常清楚,故此处不再赘述。值得一提的是在Native层中有多种与Java层中相对应的数据结构,如:jclass代表Class对象,jstring代表String对象,jboolean代表boolean基本类型等。有兴趣的童鞋可以自行去了解下。
写完该.cpp文件后,我们编译工程生成dll文件(Windows平台),并把文件加入我们Java工程的引用库中:
然后运行我们的Java代码,我们就可以看到使用JNI技术后来自Native层的问候:
String拼接的两种方法,concat与+
总的来说,区别有以下几点:
- concat是String方法,而“+”是String重载的操作符
- concat只能连接String字符串,而“+”可以连接任何Object
- 由于String是不变的对象,concat底层是通过创建新的String对象实现拼接,而“+”使用的是StringBuilder工具实现拼接
Java元注解(Annotation)
自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分。开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解
用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据
当我们使用自定义的注解时,我们就会使用到Java提供的元注解。元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明,它们均被存放在java.lang.annotation包下面,分别是:
- @Target
- @Retention
- @Documented
- @Inherited
@Target
@Target说明了Annotation所修饰的对象范围,它的取值(ElementType)有:
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型)或enum声明
例子:
1 | (ElementType.TYPE) |
注解A可以用于注解类、接口(包括注解类型) 或enum声明,而注解B仅可用于注解类的成员变量
@Retention
@Retention定义了该Annotation被保留的时间长短,表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效),取值(RetentionPoicy)有:
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在运行时有效(即运行时保留,可以通过反射获取)
@Documented
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,表明这个注解应该被 javadoc工具记录, 默认情况下,javadoc是不包括注解的。但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理,所以注解类型信息也会被包括在生成的文档中。@Documented是一个标记注解,没有成员
@Inherited
@Inherited作用是,使用此注解声明出来的自定义注解时,如果自定义注解在类上面时,子类会自动继承此注解,否则的话,子类不会继承此注解。这里一定要记住,使用@Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。
具体的例子可以查看这篇博客:http://blog.csdn.net/snow_crazy/article/details/39381695
自定义注解(Annotation)
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。通过使用元注解来标明自定义注解的某些功能。
定义注解格式:
public @interface 注解名 {定义体}
注解参数的可支持数据类型如下:
- 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
- String
- Class
- enum(枚举)
- Annotation(注解)
- 以上所有类型的数组
例子如下:
1 | (ElementType.TYPE) |
上面的例子定义了一个用于描述类、接口(包括注解类型)或enum声明的、在运行时保留的(即可以反射获取的)注解,该注解拥有属性值tableName,默认值为空字符串,使用方法如下:
1 | "tableBean") (tableName = |
Java反射中getXXX与getDeclaredXXX方法的区别
对于Fields:
- getField:只能获取类的public字段、但可以获取从父类继承的public字段
- getDeclaredField:能获取当前类的所有字段、但不能获取从父类继承的字段
对于Methods:
- getMethods:只能获取类的public方法、但可以获取从父类继承的public方法
- getDeclaredMethods:能获取当前类的所有方法、但不能获取从父类继承的方法
对于Constructors:
- getConstructors:返回所有的public构造器
- getDeclaredConstructors:返回所有的构造器
对于Annotations:
- getAnnotations:对于Class(类),返回所有注解,包括@Inherited继承的
- getDeclaredAnnotations:对于Class(类),返回当前类拥有注解,不包括@Inherited继承的