关于一些基础的Java问题的解答(八)
2017-07-09 16:31
603 查看
上一篇文章的传送门:关于一些基础的Java问题的解答(七)
36. JNI的使用
先简单介绍一下JNI,JNI即Java Native Interface的缩写,中文译为“Java本地调用”。通俗的说,JNI是一种实现Java层与Native层(C/C++)交互的技术。有时为了追求效率问题,或者是使用用native代码编写的函数库,我们就不得不使用JNI接口。
以下是一个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文件中的代码:
可以看到我们在Java层定义的printString方法对应成了Native层的Java_Main_printString方法,方法上面有javah给我们生成的注释,它提供了以下信息:
Class:方法所属于的类
Method:方法的名称
Signature:方法的签名。签名是由于Java中的方法允许重载,仅仅通过类与名称并不能确定该Native方法所对应的Java方法,因此JNI技术中就将参数类型和返回值的组合作为了一个函数的签名信息,有了签名和函数名我们才能顺利找到Javac层中对应的函数
接下来我们只要写一个.cpp文件实现该方法即可:
以上代码相信注释已解释的非常清楚,故此处不再赘述。值得一提的是在Native层中有多种与Java层中相对应的数据结构,如:jclass代表Class对象,jstring代表String对象,jboolean代表boolean基本类型等。有兴趣的童鞋可以自行去了解下。
写完该.cpp文件后,我们编译工程生成dll文件(Windows平台),并把文件加入我们Java工程的引用库中:
然后运行我们的Java代码,我们就可以看到使用JNI技术后来自Native层的问候:
concat是String方法,而“+”是String重载的操作符
concat只能连接String字符串,而“+”可以连接任何Object
由于String是不变的对象,concat底层是通过创建新的String对象实现拼接,而“+”使用的是StringBuilder工具实现拼接
用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据
当我们使用自定义的注解时,我们就会使用到Java提供的元注解。元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明,它们均被存放在java.lang.annotation包下面,分别是:
@Target
@Retention
@Documented
@Inherited
CONSTRUCTOR:用于描述构造器
FIELD:用于描述域
LOCAL_VARIABLE:用于描述局部变量
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述参数
TYPE:用于描述类、接口(包括注解类型)或enum声明
例子:
注解A可以用于注解类、接口(包括注解类型) 或enum声明,而注解B仅可用于注解类的成员变量
@Retention
@Retention定义了该Annotation被保留的时间长短,表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效),取值(RetentionPoicy)有:
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在运行时有效(即运行时保留,可以通过反射获取)
具体的例子可以查看这篇博客:http://blog.csdn.net/snow_crazy/article/details/39381695
String
Class
enum(枚举)
Annotation(注解)
以上所有类型的数组
例子如下:
上面的例子定义了一个用于描述类、接口(包括注解类型)或enum声明的、在运行时保留的(即可以反射获取的)注解,该注解拥有属性值tableName,默认值为空字符串,使用方法如下:
getDeclaredField:能获取当前类的所有字段、但不能获取从父类继承的字段
getDeclaredMethods:能获取当前类的所有方法、但不能获取从父类继承的方法
getDeclaredConstructors:返回所有的构造器
getDeclaredAnnotations:对于Class(类),返回当前类拥有注解,不包括@Inherited继承的
36. JNI的使用
先简单介绍一下JNI,JNI即Java Native Interface的缩写,中文译为“Java本地调用”。通俗的说,JNI是一种实现Java层与Native层(C/C++)交互的技术。有时为了追求效率问题,或者是使用用native代码编写的函数库,我们就不得不使用JNI接口。以下是一个JNI的小例子:
public class Main { public static void main(String[] args) throws Exception { // 动态库名字,windows平台自动拓展成makeStr_jni.dll System.loadLibrary("makeStr_jni"); // 打印字符串 printString("Java World!"); } // native关键字表示为本地方法 public static native void printString(String str); }我们在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文件中的代码:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include "jni.h" /* Header for class Main */ #ifndef _Included_Main #define _Included_Main #ifdef __cplusplus extern "C" { #endif /* * Class: Main 方法所在的类 * Method: printString 方法名 * Signature: (Ljava/lang/String;)V 签名 */ JNIEXPORT void JNICALL Java_Main_printString (JNIEnv *, jclass, jstring); #ifdef __cplusplus } #endif #endif
可以看到我们在Java层定义的printString方法对应成了Native层的Java_Main_printString方法,方法上面有javah给我们生成的注释,它提供了以下信息:
Class:方法所属于的类
Method:方法的名称
Signature:方法的签名。签名是由于Java中的方法允许重载,仅仅通过类与名称并不能确定该Native方法所对应的Java方法,因此JNI技术中就将参数类型和返回值的组合作为了一个函数的签名信息,有了签名和函数名我们才能顺利找到Javac层中对应的函数
接下来我们只要写一个.cpp文件实现该方法即可:
#include<iostream> #include"_Main.h" using namespace std; /* * JNIEnv : env JNI环境,一个提供JNI系统函数的结构体 * jclass : clazz 代表Java层的Class对象,由于printString方法是一个静态方法,故传入Class对象 * jstring : s 代表Java层的String对象,表示传入的参数 */ JNIEXPORT void JNICALL Java_Main_printString (JNIEnv * env, jclass clazz, jstring s) { jboolean iscopy; // 通过jstring对象生成本地字符串 const char *charData = env->GetStringUTFChars(s, &iscopy); // 打印字符串 cout << "A message from Native World: " << charData << endl; // 释放资源 env->ReleaseStringUTFChars(s, charData); }
以上代码相信注释已解释的非常清楚,故此处不再赘述。值得一提的是在Native层中有多种与Java层中相对应的数据结构,如:jclass代表Class对象,jstring代表String对象,jboolean代表boolean基本类型等。有兴趣的童鞋可以自行去了解下。
写完该.cpp文件后,我们编译工程生成dll文件(Windows平台),并把文件加入我们Java工程的引用库中:
然后运行我们的Java代码,我们就可以看到使用JNI技术后来自Native层的问候:
37.String拼接的两种方法,concat与+
总的来说,区别有以下几点:concat是String方法,而“+”是String重载的操作符
concat只能连接String字符串,而“+”可以连接任何Object
由于String是不变的对象,concat底层是通过创建新的String对象实现拼接,而“+”使用的是StringBuilder工具实现拼接
38.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声明
例子:
@Target(ElementType.TYPE) public @interface A { } @Target(ElementType.FIELD) public @interface B { }
注解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
39.自定义注解(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(注解)
以上所有类型的数组
例子如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DatabaseTable { String tableName() default ""; }
上面的例子定义了一个用于描述类、接口(包括注解类型)或enum声明的、在运行时保留的(即可以反射获取的)注解,该注解拥有属性值tableName,默认值为空字符串,使用方法如下:
@DatabaseTable(tableName = "tableBean") public class TableBean {}
40.Java反射中getXXX与getDeclaredXXX方法的区别
对于Fields:
getField:只能获取类的public字段、但可以获取从父类继承的public字段getDeclaredField:能获取当前类的所有字段、但不能获取从父类继承的字段
对于Methods:
getMethods:只能获取类的public方法、但可以获取从父类继承的public方法getDeclaredMethods:能获取当前类的所有方法、但不能获取从父类继承的方法
对于Constructors:
getConstructors:返回所有的public构造器getDeclaredConstructors:返回所有的构造器
对于Annotations:
getAnnotations:对于Class(类),返回所有注解,包括@Inherited继承的getDeclaredAnnotations:对于Class(类),返回当前类拥有注解,不包括@Inherited继承的
相关文章推荐
- 关于一些基础的Java问题的解答(六)
- 关于一些基础的Java问题的解答(三)
- 关于一些基础的Java问题的解答(九)
- 关于一些基础的Java问题的解答(三)
- 关于一些基础的Java问题的解答(七)
- 关于一些基础的Java问题的解答(七)
- 关于一些基础的Java问题的解答(一)
- 关于一些基础的Java问题的解答(五)
- 关于一些基础的Java问题的解答(四)
- 关于一些基础的Java问题的解答(二)
- 关于一些基础的Java问题的解答(四)
- 关于一些基础的Java问题的解答(六)
- 关于一些基础的Java问题的解答(一)
- 关于一些基础的Java问题的解答(五)
- 关于一些基础的Java问题的解答(二)
- 关于java的一些问题,比较基础,大家可以看看咯
- java基础知识整理 一些问题的解答(面试)
- 关于EL的一些基础问题和尝试性解答
- 基于《关于Java开发不明白的一些问题》,探讨一下Struts1和Struts2
- 关于Installshield里一些常见问题的解答—艾泽拉斯之海洋女神出品