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. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。