`

Java语言拾贝

    博客分类:
  • Java
阅读更多
1.volatile关键字
在Java中设置变量值的操作,除了long和double类型的变量外都是原子操作,也就是说,对于变量值的简单读写操作没有必要进行同步。这在JVM 1.2之前,Java的内存模型实现总是从主存读取变量,是不需要进行特别的注意的。而随着JVM的成熟和优化,现在在多线程环境下volatile关键字的使用变得非常重要。在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。

这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。

而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。

使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

2.操作符
【1】& 与操作符
二进制两位都是1,则输出1,否则为0
例如 5&9 = 1   101&1001=0001
【2】| 或操作符
二进制两位都是0,则输出0,否则为1
例如 5|9 =13   101 | 1001= 1101=13
【3】>> 移位操作符
>>> 无符号的移位操作符 无论正负都在高位插入0
例如 5>>1 = 2  5>>>1 = 2
【4】^ 异或操作符
例如3^1 = 2  
3 = 11
1 = 01
11 XOR 01 = 10 = 2

3.Transient关键字
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。

首先,让我们看一些Java serialization的代码:
public class LoggingInfo implements java.io.Serializable
{
    private Date loggingDate = new Date();
    private String uid;
    private transient String pwd;
   
    LoggingInfo(String user, String password)
    {
        uid = user;
        pwd = password;
    }
    public String toString()
    {
        String password=null;
        if(pwd == null)
        {
        password = "NOT SET";
        }
        else
        {
            password = pwd;
        }
        return "logon info: \n   " + "user: " + uid +
            "\n   logging date : " + loggingDate.toString() +
            "\n   password: " + password;
    }
}

现在我们创建一个这个类的实例,并且串行化(serialize)它 ,然后将这个串行化对象写入磁盘。

LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS");
System.out.println(logInfo.toString());
try
{
    ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("logInfo.out"));
    o.writeObject(logInfo);
    o.close();
}
catch(Exception e) {//deal with exception}

To read the object back, we can write

try
{
    ObjectInputStream in =new ObjectInputStream(new FileInputStream("logInfo.out"));
    LoggingInfo logInfo = (LoggingInfo)in.readObject();
    System.out.println(logInfo.toString());
}
catch(Exception e) {//deal with exception}

如果我们运行这段代码,我们会注意到从磁盘中读回(read——back (de-serializing))的对象打印password为"NOT SET"。这是当我们定义pwd域为transient时,所期望的正确结果。
现在,让我们来看一下粗心对待transient域可能引起的潜在问题。假设我们修改了类定义,提供给transient域一个默认值,
代码如下:

public class GuestLoggingInfo implements java.io.Serializable
{
    private Date loggingDate = new Date();
    private String uid;
    private transient String pwd;
   
    GuestLoggingInfo()
    {
        uid = "guest";
        pwd = "guest";
    }
    public String toString()
    {
        //same as above
     }
}
现在,如果我们串行化GuestLoggingInfo的一个实例,将它写入磁盘,并且再将它从磁盘中读出,我们仍然看到读回的对象打印password 为 "NOT SET"。当从磁盘中读出某个类的实例时,实际上并不会执行这个类的构造函数,而是载入了一个该类对象的持久化状态,并将这个状态赋值给该类的另一个对象。

分享到:
评论

相关推荐

    jdk8-linux.zip

    Java Development Kit (JDK) 是Java编程语言的核心组件,它为开发者提供了编译、调试和运行Java应用程序所需的所有工具。这个“jdk8-linux.zip”文件是Oracle公司为Linux操作系统提供的JDK 8版本的安装包。JDK 8是...

    protobuf.zip

    在protobuf中,编译.proto文件会生成对应的源代码,通常为C++、Java或Python等编程语言,这些源代码提供了序列化和反序列化的API,使得我们能够方便地在程序中操作protobuf定义的数据结构。 protobuf的编译过程包括...

    固定列宽,表格默认风格,表格刷新,人性化展示等

    为了实现这样的功能,开发者需要掌握HTML、CSS、JavaScript以及可能的后端语言如Java(WAR文件通常是Java Web应用的部署包),并理解如何处理数据交互和前端渲染。同时,良好的用户体验设计原则也是必不可少的。

    Scala容器的核心编程.pdf

    JAVA 程序设计师的 SCALA 入门教学, ...此教学将对 Scala 语言以及编译器做一个简易介绍。设定的读者为具有程设经验且想要看 Scala 功能概要的人。内文假设读者有着基本、特别是 Java 上的对象导向程设知识。

    proxypattern.zip

    在Java或其他面向对象语言中实现代理模式,可以使用以下几种方式: 1. **静态代理**:通过手动创建代理类并实现与真实对象相同的接口,然后在代理类的方法中调用真实对象的方法并添加额外逻辑。 2. **动态代理**:...

    seckill.zip

    Java作为后端开发的主流语言,通常用于构建秒杀系统的业务逻辑。在Java中,可以利用Spring Boot框架简化开发,并通过RabbitMQ等消息队列来处理异步任务,比如订单生成和后续的库存更新,这样可以降低秒杀高峰期对...

    处理文件缓存的方法

    在编程中,可以使用各种语言的内置库函数,如Python的`os`模块的`walk()`函数,或者Java的`java.io.File`类的`length()`方法。此外,还可以利用shell命令,如在Linux中使用`du`命令,或者在Windows中使用`dir /s`...

    MemoryAnalyzer.zip

    7. **兼容性**:可能兼容多种编程语言,如C、C++、Java、Python等,因为这些语言的内存管理方式各不相同,需要专门的工具进行分析。 8. **易用性**:一个好的内存分析工具应该具有直观的用户界面和清晰的操作指南,...

    教育监管可视化系统的设计与实现(新)

    开发过程中采用了多种现代软件开发工具和技术,包括但不限于Java语言、Spring框架、MySQL数据库等。这些技术和工具的选择是为了更好地支持系统的稳定性和扩展性,以及满足系统的高性能需求。 ##### 2.2 系统设计...

    多功能jQuery日期控件 jeDate

    - **多语言支持**:jeDate内置多语言包,可以轻松适应不同地区的用户需求,只需简单配置即可切换语言。 - **多种显示格式**:它支持多种日期格式展示,如“YYYY-MM-DD”、“YYYY/MM/DD”等,可以根据项目需求自由...

Global site tag (gtag.js) - Google Analytics