• Java进阶4表达式中的陷阱


    Java进阶4表达式中的陷阱 20131103

             表达式是Java中最基本的组成单元,各种表达式是Java程序员最司空见惯的内容,Java中的表达式并不是十分的复杂,但是也有一些陷阱。例如当程序中使用算术表达式的时候,表达式的类型自动提升,复合赋值运算符所隐含的类型转换,给程序带来一些潜在的陷阱。还有就是JDK1.5之后支持泛型也会带来一些陷阱,因为之前的Java版本是不支持泛型的,为了兼容之前的版本,引入了原始类型的概念,而原始类型在泛型编程中存在着极大的陷阱。

    1.字符串中的陷阱

             JVM对于字符串的处理都会在JVM的字符串缓冲池缓冲字符串,而且java中的字符串类型是不可以改变的,如果经常改字符串的话,使用String类型,效率会非常的低.

             对于Java语句: String str = new String(“yang”);其实在底层实现的话,是十分低效的,为什么呢?这一句代码创建了两个字符串对象,一个是”yang”这个直接的字符串对象,另一个是new String()构造器返回的字符串对象。(Java中创建对象的方式有如下四种:通过new 创建对象;通过class的getInstance()方法调用构造器创建对象;通过java的反序列化机制从IO流中恢复一个java对象;通过Java对象提供的clone方法复制一个Java对象;同时对于基本的类型,比如Integer 或者是 String类型的话,可以通过简单的算数表达式直接复制,比如Integer i= 3; String str = “yang”;)

             对于Java中的字符串,JVM会使用一个字符串缓冲池来保存他们,当第一次使用某个字符串直接量的时,JVM会将它放入字符串缓冲池中。一般来说,字符串缓冲池中的字符串对象是不被回收的,当程序中再次使用给字符串的时候,无需要重新创建一个新的字符串,而是直接让引用变量指向字符串缓冲池中已经有的字符串。

             而对于字符串连接表达式创建字符串的话,可以将一个字符串连接是直接赋值给字符串变量,如果字符串在编译的时候确定下来的话,JVM会在编译的时候计算字符串变量的值,并且让他指向字符串池中的对象。

             String str1 = “yangtengfei”;

             String str2 = “yang”+”teng”+”fei”;

             以上两种是等效的,str1== str 其实指向的是同一个对象。字符串连接必须只能呢个够是直接量,而不能够存在变量,也不能够有函数调用,否则在编译期间就无法确定字符传的内容。

             当然还有一种比较特殊的情况,就是执行宏替换,那么JVM一样可以在编译的时候确定字符串的内容,一样会让字符串变量指向JVM字符串池中的对应的字符串。

                      final  int num  = 10;

           String str1 = "yang10";

           String str2 = "yang" + num;

           System.out.println(str1==str2); //true

        当程序中使用String 或者是基本类型的封装的时候,尽量使用直接量赋值,避免使用new关键字。

        对于String而言,它代表的字符串序列是不可以改变的,因此程序中需要会发生改变的字符串的话,应该考虑使用StringBuffer或者是StringBuilder,两者的区别是StringBuffer是线程安全的,如果不是多线程程序的话,就应该优先使用StringBuilder,因为这样的话效率比较高。

    2.表达式类型的陷阱

        Java是一门强类型的语言,所有的数据都有指定的数据类型,因此表达式一定要注意他的数据类型。所谓的强类型的编程语言,就是所有的变量在使用之前必须实现声明,声明的变量必须指定该变量的数据类型;一旦一个变量的数据类型确定下来,那么这个变量将永远只能够接受该类型的值。

        当一个算数运算表达式包含多个基本类型的数据的时候,整个运算符表达式数据类型将会自动提升,提升规则如下:byte char short豆浆提升到int类型;整个算术表达式的数据类型自动提升到于表达式中最高级别的数据类型

        short sVal  = 2;

        short sVal = 10;

        //sVal =sVal - 2;编译不通过,因为int无法默认转换成为short

        sVal -=2;

        E1 op= E2 equals the E1 = (E1) (E1 op E2) not the E1 = E1 op E2

       

        在Java7中新增加了二进制的数据支持,但是也引入了新的陷阱

        int it = 0b1010_1010;

    3.多线程中的陷阱

        3.1不要直接调用线程中的run方法,而是start方法启动新的线程,否则只是当做一个简单的成员函数进行调用,而没有启动另一个线程。

        3.2静态同步方法、

        在Java中提供了synchronized关键字用于修饰方法,是哟给synchronized修饰的方法被称为同步方法,淡然还可以使用synchronized关键字修饰代码块,称之为同步代码块。

        Java语法规定:任何线程进入同步代码块,同步方法之前,必须获得同步代码块、同步方法的同步监视器。对于同步代码块而言,必须显示的指出同步监视器;对于非静态的同步方法,该方法获得的同步监视器是当前对象—机调用该方法的对象;对于静态的同步方法的话,则获得是同步监视器是类本身。其中同步的静态成员函数和this对象是不想冲突的。

    public class TestMain  implements Runnable{

        public static void main(String[] args) throws ClassNotFoundException {

           Thread ss = new Thread(new TestMain());

           Thread sss = new Thread(new TestMain());

           ss.start();

           sss.start();     

        }

        static boolean staticFlag = true;

        public static synchronized void test0() throws InterruptedException{

           for(int i = 0; i < 10; i++){

               System.out.println("test0 func " + i );

               Thread.sleep(1000);

           }

        }

        public void test1() throws InterruptedException{

           synchronized(this){

               for(int i = 0; i< 10; i++){

                  System.out.println("test1 func " + i);

                  Thread.sleep(1000);

               }

           }

        }  

        @Override

        public void run() {

           if(staticFlag){

               staticFlag = false;

               try {

                  test0();

               } catch (InterruptedException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

               }

           }else{

               staticFlag = true;

               try {

                  test1();

               } catch (InterruptedException e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

               }

           }

        }

    }

    其实上面两个同步代码块是不会相互影响的因为针对的不是同一个同步监视器。

    如何锁住当前类的,使用synchronized(MyClassName.class){}就实现了同步了这个类。

    3.3静态初始化代码块启动新线程执行执行初始化

    package yang.main;

     

    public class TestMain{

        static {

           Thread t = new Thread(){

               public void run(){

                  System.out.println("enter run func");

                  System.out.println("run func : " + name);

                  name = "huihui";

                  System.out.println("leave run func");

               }

           };

          

           t.start();

           try{

               t.join();

           }catch(Exception ex){

               ex.printStackTrace();

           }

        }

       

        static String name = "yangtengfei";

       

        public static void main(String []args){

           System.out.println("main thread "  + name);

        }

    }

    这个时候就会出现死锁的情况,输出的结果是”enter run func”,之后就一直处于死锁状态。

        分析一下代码的执行过程:为该类的所有静态field分配内存,调用静态代码块进行初始化。在这一段代码中首先会为static name 分配内存空间,但是这个时候name的值是null,接着main线程开始执行静态代码块,在静态代码块中启动了一个新的线程,并且在main线程中启动分支线程,并且调用join方法等待分支线程,这一意味着main线程必须等待分支线程结束之后才可以继续往下执行。

        在新线程开始的时候,运行输出进入run方法,然后程序试图输出name静态变量的时候,问题就会出现了,因为该类是有main线程进行初始化的,因此新的线程就会等待main线程将Class初始化完成之后,才会继续执行,否则是无法访问该Class的静态成员变量。所以就会出现了我们梦寐以求的死锁情况,是不是很爽啊,其实在代码层面上难以理解,为什么新的线程需要等待main这可能是设计到底层JVM的编译运行机制吧,当我们访问一个Class的静态成员编程,但是有没有初始化好该类,就会等待。

        我们在静态代码块中将t.join()方法注释掉,就会执行,但是结果是什么呢,两次访问的静态变量的值都是初始化的时候的值,而不是我们在线程中赋予的值。新的线程好像没有起什么作用。

        实际上主线程进入静态代码块之后,同样是创建并且启动了一个新的线程,因为没有调用join方法,main线程就不会等待新的线程,新的线程就是出于就绪的护着你给他,没有运行。Main线程继续执行初始化的操作,将name的值赋值为yangtengfei,至此main线程完成了对于Class的初始化工作,之后主线程进入main方法,输出对象的值,同时在调度新的线程运行。将静态的成员编修进行修改,但是此时main程序已经不再访问了。

        让主线程等待的话,虽然可以立马执行新的线程,但是因为访问静态成员变量的时候,阻塞新的线程,又会切换到main线程。

    追梦的飞飞

    于广州中山大学图书馆 20131103

    HomePage: http://yangtengfei.duapp.com

  • 相关阅读:
    物联网浪潮之下如何从 0 到 1开启智能化硬件开发?
    安卓开发工程师应该这样快速使用机智云APP开源框架
    hdu 1246
    UVa202
    CodeForces
    热身经验被打脸总结大会感想
    多重背包的二进制优化
    背包问题+欧拉筛法
    HDU 1106
    HDU 1215
  • 原文地址:https://www.cnblogs.com/hbhzsysutengfei/p/3409559.html
Copyright © 2020-2023  润新知