java原生的一点东西-01【关键字】

1.native

代表java底层实现,也就是你看不到java语言对这个方法的实现,其实就是JNI(java native interface)。比如Object的 hashCode()方法,返回的是这个对象在内存中的地址,可以被任何一个类重载掉。不过这种,正常的java程序员是不需要用到的,跨语言交流,有各种成熟的rpc系统。不过如果是单机应用,比如接入汉王,做一个图像识别模块,还是有点意义的。下面贴一段JNI的编写教程

JNI的书写步骤如下:
a.编写带有native声明的方法的Java类
b.使用javac命令编译编写的Java类
c.使用java -jni **来生成后缀名为.h的头文件
d.使用其他语言(C、C++)实现本地方法
e.将本地方法编写的文件生成动态链接库

以下是一个在Java中调用本地C程序的简单的例子:

a.编写HelloWorld.java类  
class HelloWorld{

public native void hello();

static{  
System.loadLibrary("hello");  
}

public static void main(String[] args){

new HelloWorld().hello();  
}

}
b.编译  
javac HelloWorld.java  
c.生成.h文件  
javah -jni HelloWorld  
生成内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {  
#endif
/*
* Class: HelloWorld
* Method: hello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_hello  
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
第一个参数是调用JNI方法时使用的JNI Environment指针。第二个参数是指向在此Java代码中实例化的Java对象HelloWorld的一个句柄。其他参数是方法本身的参数
d.c实现  
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_HelloWorld_hello(JNIEnv *env,jobject obj){  
printf("Hello World!\n");  
return;  
}
其中,第一行是将jni.h文件引入(在%JAVA_HOME%\include目录下),里边有JNIEnv和jobject的定义。
e.编译c实现  
这里以在Windows中为例,需要生成dll文件。在保存HelloWorldImpl.c文件夹下面,使用VC的编译器cl成。
cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll  
注意:生成的dll文件名在选项-Fe后面配置,这里是hello,因为在HelloWorld.java文件中我们loadLibary的时候使用的名字是hello。当然这里修改之后那里也需要修改。另外需要将-I%java_home%\include -I%java_home%\include\win32参数加上,因为在第四步里面编写本地方法的时候引入了jni.h文件。
f.运行程序  
java HelloWorld就ok了!  
以上就是关于Java中native关键字的详细介绍,希望对大家的学习有所帮助。

2.transient

transient是在java对象序列化的时候用到的,嗯,它用到的地方就是标志这个字段不可以被序列化。偷个懒,用fastjson做实验如下

class test{  
        transient String name;
        String code;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }
    }

然后打印一下

        test t=new test();
        t.setCode("code");
        t.setName("name");
        System.out.println(JSON.toJSONString(t,true));

结果

{
    "code":"code"
}

3.位运算

搞算法的估计会经常写位运算,<<,<<<,>>,>>>,~,^,| 这些东西正常人都用不到,估计时间久了,看到都忘记什么意思了。自己左移用的比较多,<<,而且只有一个case,投机取巧吧,用了设置capacity时,方便阅读

//获得一个2的n次方
private static final int CAPACITY = 1 << n;  

4. volatile

一般意义上的锁提供了两种主要特性:

  • 互斥(mutual exclusion)
  • 可见性(visibility)

而volatile只能提供可见性,这注定了它不可能用来实现原子操作。

而volatile能实现可见性,代表了用volatile修饰的变量,使用全部在内存中,不会走任何缓存。jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈, 线程栈保存了线程运行时候变量值信息。

当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。

所以现在大家都不建议用volatile实现什么功能,一方面是容易误用,另一方便,它完全可以用完善的锁来实现(当然,它比synchronized轻多了)

我能想到的用处,就是,嗯比如一个温度计,展示温度值M,只有一个线程可以修改M,但是很多线程都可以读取到M(只读),用来展示。这样,volatile修饰的M,可以实现即时展示,不会出现温度不同步的情况~

5. synchronized

通过 synchronized 关键字来实现,所有加上synchronized和块语句,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized 修饰的方法或者代码块。

在方法块里面,使用synchronized(this),代表锁定改段代码

  • 1.修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  • 2.修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  • 3.修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  • 4.修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

最后来说:

  • A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
  • B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  • C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

刘摸鱼

退堂鼓表演艺术家

杭州