为您推荐:
精华内容
最热下载
问答
  • 5星
    26KB sinat_36710456 2021-04-20 09:54:23
  • 目录 1.算术操作符 2.移位操作符 ...算术操作符,移位操作符,位操作符,赋值操作符,单目操作符,关系操作符,逻辑操作符,条件操作符,逗号表达式,下标引用、函数调用和结构成员。 接下来一一...

    目录

    1.算术操作符

    2.移位操作符

    3.位操作符

    4.赋值操作符

    5.单目操作符

    6.关系操作符

    7.逻辑操作符

    8.条件操作符

    9.逗号表达式

    10.下标引用、函数调用和结构成员


    C语言中操作符总共有10种,分别是:

    算术操作符,移位操作符,位操作符,赋值操作符,单目操作符,关系操作符,逻辑操作符,条件操作符,逗号表达式,下标引用、函数调用和结构成员。


    接下来一一介绍每种操作符:

    1.算术操作符

    符号:+  -  *  /  %

    符号说明:

     +   --->   分别用于整数及浮点数的加法

     -   --->   分别用于整数及浮点数的减法

     *   --->   分别用于整数及浮点数的乘法

     /   --->   分别用于整数及浮点数的除法

    %   --->   用于返回两个整数相除的余数

    注意

    1.+、-、*、/这四个运算符均可用于整数及浮点数的运算。

    2.当使用/运算符时,如果两个操作数均为整型,那么执行整数除法,运算结果也为整型;如果两个操作数至少一个为浮点数,那么执行浮点数运算,运算结果为浮点型。

     3.%运算符只能用于两个整数相除,返回余数。


    2.移位操作符

    符号:<<   >>

    符号说明:

    <<   --->   左移运算符,用于将整数左移指定位数

    >>    --->   右移运算符,用于将整数右移指定位数

    移位规则:

    << (左移运算符):不论算术移位还是逻辑移位,均将左边的数舍弃,右边空缺位补0(左边丢弃,右边补0)

    >>(右移运算符):当进行逻辑移位时,右边位丢弃,左边空缺位补0(右边丢弃,左边补0) ;

                                                     当进行算术移位时,右边位丢弃,左边位补原数的符号位(右边丢弃,左边补符号位)。

    举例说明:

     

    注意

    1.移位操作不改变原值。

    2.移位时不能移负数位。 


    3.位操作符

    符号:&    |      ^

    符号说明:

    &    --->  按位与(有0出0)

     |     --->  按位或(有1出1)    

     ^     --->  按位异或(相同为0,相异为1) 

    举例说明:

    &(按位与):

    int a = -3;
    int b = -5;
    int c = a&b;

    因为计算机中存的是二进制的补码,所以将 a 、b的补码按位与之后再转化成原码可得c(-7)

     

    一个数&1可得该数二进制最低位是0还是1

    int a = 2;
    int b = -5;
    int c = a&1;    //结果为0
    int d = b&1;    //结果为1  

     

    若想将一个数的二进制第N位 置为1, 则可将1左移N-1位后与该数二进制做或运算,即(1<<(N-1)) | 该数二进制。

    如想将2的二进制第7位 置为1,则将1左移7-1位后或上2的二进制。

    若想将一个数的二进制第N位 置为0,则可将1左移N-1位后按位取反,再与该数二进制做与运算,即(~(1<<(N-1)))& 该数二进制。

    如想将9的二进制第4位 置为0,则将1左移4-1位后按位取反再与上9的二进制。

     

    |(按位或):

    int a = -3;
    int b = -5;
    int c = a|b;

     因为计算机中存的是二进制的补码,所以将 a 、b的补码按位或之后再转化成原码可得c(-1)

    ^(按位异或):  

    int a = -3;
    int b = -5;
    int c = a^b;

    因为计算机中存的是二进制的补码,所以将 a 、b的补码按位或之后再转化成原码可得c(6)

    注意

    1.位操作符的操作数必须为整数。

    2.计算机中存的是二进制的补码,所以进行的是补码运算,再转化成原码可得最终结果 。

    3.一个数的二进制&1可得该数二进制最低位是0还是1.

    4.若想将一个数的二进制第N位 置为0,则可将1左移N-1位后按位取反,再与该数二进制做与运算,即(~(1<<(N-1)))& 该数二进制。

    5.若想将一个数的二进制第N位 置为1, 则可将1左移N-1位后与该数二进制做或运算,即(1<<(N-1)) | 该数二进制。


    4.赋值操作符

    符号:=   +=   -=   *=   /=   %=   >>=   <<=   &=   |=    ^=

    符号说明:

    =   --->  进行简单赋值操作

     +=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=    --->    复合赋值符,进行复合赋值操作

    举例说明: 

    =(简单赋值) :

    int x = 10;
    x = 20;    //简单赋值操作
    double y = 10.0;
    y = 20.0;  //简单赋值操作
    
    int a = 5;
    int b = 7;
    int c = 9;
    c = b = a+1;   //连续赋值操作

     +=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=(复合赋值):

    int x = 10;
    x = x + 5;
    x += 5;   //复合赋值
    
    int y = 10;
    y = y + 5;
    y -= 5;   //复合赋值
    
    其它复合赋值符也是相同用法,此处不再一一列举。

    注意:赋值操作符可以连续使用。


    5.单目操作符

    符号: !    -    +    &     sizeof      ~     --    ++      *      (类型)

    符号说明:

      !    --->    逻辑反操作

      -    --->    负值

      +    --->    正值

      &   --->    取地址

    sizeof   --->   操作数的类型长度(单位:字节)

      ~    --->   对一个数的二进制按位取反

     --    --->    前置、后置--

    ++    --->   前置、后置++

     *     --->    解引用操作符

     (类型)    --->   强制类型转换

    举例说明:

    !(逻辑反操作):

    int a = 10;
    if(!a)    //!:逻辑反操作   
    {
        //doSomething;
    }

    -(负值)、+(正值):

    int a = 10;    //正值
    int b = -10;   //负值

    &(取地址):

    int a = 10;
    int *pa = &a;    //将变量a的地址保存在变量pa中,pa指向a,此时pa是一个指针变量

    sizeof(求操作数的类型长度):

    int a = 10;
    printf("%d\n",sizeof(a));   //结果为4(字节)
    printf("%d\n",sizeof(int)); //结果为4(字节)
    printf("%d\n",sizeof a);   //结果为4(字节)  求变量的长度时可以省略括号
    printf("%d\n",sizeof int); //错误,求类型的长度时不能去掉括号

    ~(对一个数的二进制按位取反):

    int a = 0;
    int b = ~a;  //b的结果为-1

     

    --(前置、后置--)、++(前置、后置++):

    int a = 5;
    int b = a++;   //此时a=6,b=5      前置++:先使用,后++
    int c = ++a;   //此时a=6,c=6      后置++:先++,后使用
    
    前置--和后置--分别同前置++、后置++,此处不再详述。

    *(解引用):

    int a = 10;
    int *pa = &a;
    *pa = 20;    //解引用

    (类型)(强制类型转换):

    srand((unsigned int)time(NULL)); 

    注意

    1.sizeof是一个操作符,关键字,而不是函数,求的是操作数的类型长度(以字节为单位)。

    2.sizeof求类型的长度时不可省略括号,求变量的长度时可以省略括号。

    3.!操作符是对一个数做逻辑反操作,而~操作符是对一个数的二进制按位取反。

    4.前置++、前置--是先使用,再++;而后置++、后置--是先--,再使用。


    6.关系操作符

    符号:· >    <    >=    <=   !=   ==

    符号说明: 

    >     --->  测试“大于”

     <     --->  测试“小于”

    >=     --->  测试“大于等于”

    <=     --->  测试“小于等于”

    !=     --->  测试“不等于”

     ==     --->  测试“等于”

    注意

    1.判断字符串是否相同应该用strcmp()函数,而不能用==比较。

    2.编写程序代码时一定注意=和==的区别,不要写错,=是做赋值操作,而==才是判断是否相等。


    7.逻辑操作符

    符号: &&    ||

    符号说明:

    &&  --->    逻辑与操作(只要有一个表达式为假便为假,不再执行后面的表达式)

     ||    --->    逻辑或操作(只要有一个表达式为真便为真,不再执行后面的表达式)

    举例说明:

    1 && 2 = 1;  //逻辑与,只要有一个为假便为假
    1 & 2 = 0;   //按位与(二进制位)
    
    1 || 2 = 1;  //逻辑或,只要有一个为真便为真
    1 | 2 = 3;   //按位或(二进制位) 
    int i = 0,a = 0,b = 2,c = 3,d = 4;
    i = a++ && ++b && d++;  //前置++是先使用再++,所以此时先使用a = 0,由于进行的是逻辑与操作,只要有一个表达式为假,便不再执行后面的表达式,直接返回假。而后a++,a变成1,b,c,d仍为原值
    printf("a = %d b = %d c = %d d = %d\n",a,b,c,d);  //a = 1,b = 2,c = 3,d = 4
    
    
    int i = 0,a = 0,b = 2,c = 3,d = 4;
    i = a++ || ++b || d++;  //前置++是先使用再++,所以此时先使用a = 0,++b后b变成3,此表达式为真。由于进行的是逻辑或操作,只要有一个表达式为真,便不再执行后面的表达式,直接返回真。而后a++,a变成1,不再执行d++。
    printf("a = %d b = %d c = %d d = %d\n",a,b,c,d);  //a = 1,b = 3,c = 3,d = 4

    注意

    1.一定注意按位与和逻辑与,按位或和逻辑或的区别。

    2.逻辑与中,只要有一个表达式为假,便不再执行后面的表达式,直接返回假;

       逻辑或中,只要有一个表达式为真,便不再执行后面的表达式,直接返回真。


    8.条件操作符

    符号:exp1 ? exp2 : exp3

    符号说明:

    exp1 ? exp2 : exp3    --->   三目表达式,若表达式1(exp1)为真则返回表达式2(exp2),否则返回表达式3(exp3)

    举例说明:

    int a = 5;
    int b = 9;
    int max = (a>b) ? a : b;   //如果a>b,则max = a;否则max = b

    9.逗号表达式

    符号:exp1, exp2, exp3, ..., expN

    符号说明:

    exp1, exp2, exp3, ..., expN   --->   逗号表达式(用逗号隔开的表达式),从左往右依次执行。整个表达式的结果为最后一个表达式的结果。

    举例说明:

    int i = 0, j = 0;
    for(i = 0,j = 0; i < 2, j < 4;i++, j++)   
    {
       printf("fine day!\n");
    }
    //由于for循环的循环次数只依赖于循环条件判断部分,而此部分又是一个逗号表达式,所以我们可以知道此次循环共打印fine day! 4次。
    
    
    
    int a = 1, b = 2;
    int c = (a>b,a=b+10,a,a+1);    //结果为13
    
    
    if(a = b + 1, c = a; d = 8)   //此时if语句的判断条件为d=8

    注意:逗号表达式的结果虽然是最后一个表达式的结果,但不可认为与前面的表达式就无关了,因为前面表达式可能会影响最后一个表达式的结果。


    10.下标引用、函数调用和结构成员

    10.1 下标引用

    符号:[ ]

    符号说明:

    [ ]    --->   下标引用操作符,有两个操作数(数组名和索引值)

    举例说明:

    int arr[10] = {0};
    arr[3] = 7;    //[ ]:下标引用操作符,其两个操作数为arr和3

    注意:下标引用共有两个操作数(数组名和索引值)。

    10.2 函数调用

    符号:()

    符号说明:

    ()   --->  函数调用操作符,有一个或多个操作数(函数名和参数)。

    举例说明:

    void test1()
    {
      printf("fine day!\n");
    }
    
    void test2(char *ch)
    {
      printf("%s\n",ch);
    }
    
    int main()
    {
      test1();    //():函数调用操作符
      test2("fine day!");    //():函数调用操作符
    }

      注意:函数调用操作符有一个或多个操作数。

    10.3结构成员

    符号: .    ->

    符号说明:

    .   --->   结构体对象.成员名

    ->  --->  结构体指针->成员名

    举例说明:

    struct Person
    {
      char name[10];
      char sex[5];
      int age;
      double height;
    }
    
    int main()
    {
      struct Person person1;
      struct Person person2;
      struct Person *pperson = &person2;
      strcpy(person.name,"zhangsan");   //结构体成员访问
      person.age = 20;   //结构体成员访问
      strcpy(pperson->name,"lisi");   //结构体成员访问
      pperson->age = 23;   //结构体成员访问
    }

    注意:当结构体中有数组成员时,给该成员赋值用用strcpy()函数,将目标串拷贝给该数组成员。

    展开全文
    ty6693 2019-01-27 17:32:50
  • 1、merge(),concat ()操作符 /** * ========================merge,concat 操作符 ====================================== * &amp;amp;amp;amp;lt;p&amp;amp;amp;amp;gt; * merge操作符是把多个...

    RxJava各类型操作符详解如下:

    RxJava操作符汇总
    RxJava操作符(一) —-创建操作符
    RxJava操作符(二)—-转换操作符
    RxJava操作符(三)—-合并操作符
    RxJava操作符(四)—-功能操作符
    RxJava操作符(五) —-过滤操作符
    RxJava操作符(六)—-条件操作符



    合并操作符 : 组合多个被观察者(Observable)&合并需要发送的事件。 包含:concatMap(),concat(), merge(),mergeArray(),concateArray(),reduce(),collect(),startWith(),zip(),count()。

    1、merge(),concat ()操作符

     /**
         * ========================merge,concat 操作符 ======================================
         * 
         * merge操作符是把多个Observable合并成一个进行发射。merge可能会让合并到Observable的数据顺序发生错乱(组合被观察者数量<=4个)(并行无序)
         * mergeArray操作符和merge作用一样,但不同的是组合被观察者数量>4个)(并行无序)
         * 
         * concat操作符也是把多个Observable合并成一个进行发射。但concat则保证合并的每个Observable的事件按顺序发射出去。(组合被观察者数量<=4个)(串行有序)
         * concatArray操作符和concat作用一样,但不同的是组合被观察者数量>4个)(串行有序)
         */
        public static void merge() {
            Observable observable1 = Observable.just(1, 2, 3);
            Observable observable2 = Observable.just("哈哈", "嘻嘻", "啊啊");
    
            Observable
                    .merge(observable1, observable2).delay(1, TimeUnit.SECONDS)
                    .subscribe(new Observer() {
                        @Override
                        public void onSubscribe(Disposable d) {
    
                        }
    
                        @Override
                        public void onNext(Object o) {
                            Log.d(TAG + "merge", o.toString());
                        }
    
                        @Override
                        public void onError(Throwable e) {
                        }
    
                        @Override
                        public void onComplete() {
                            Log.d(TAG + "merge", "onComplete");
                        }
                    });
        }

    输出如下:

    1

    2、concatDelayError()/mergeDelayError() 操作符

     /**
         * ========================concatDelayError()/mergeDelayError() 操作符 ======================================
         * 
         * 这两个操作符的作用是: 使用concat()和merge()操作符时,若其中一个被观察者发送onError事件,则会马上终止其它被观察者继续发送事件。所以呐,这时使用concatError()/
         * mergeDelayError()事件可以使onError事件推迟到其它被观察者发送事件结束后在再触发
         */
        public static void concatDelayError() {
    
            Observable
                    .concatArrayDelayError(Observable.create(new ObservableOnSubscribe<Integer>() {
                        @Override
                        public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                            emitter.onNext(1);
                            emitter.onNext(2);
                            emitter.onError(new NullPointerException());
                            emitter.onNext(3);
                            emitter.onNext(4);
                        }
                    }), Observable.just(5, 6))
    
    
                    .subscribe(new Observer<Integer>() {
                        @Override
                        public void onSubscribe(Disposable d) {
    
                        }
    
                        @Override
                        public void onNext(Integer integer) {
                            Log.d(TAG + "cDelayError", String.valueOf(integer));
                        }
    
                        @Override
                        public void onError(Throwable e) {
                            Log.d(TAG + "cDelayError", "onError");
                        }
    
                        @Override
                        public void onComplete() {
                            Log.d(TAG + "cDelayError", "onComplete");
                        }
                    });
        }

    输出如下:

    2

    3、zip 操作符

    /**
         * ========================zip 操作符 ======================================
         * 
         * 把多个Observable合并后,并且把这些Observable的数据进行转换再发射出去。转换之后的数据数目由最短数据长度的那个Observable决定。发射完最终会自动调用观察者的onComplete方法()
         * 
         * 如以下代码: 数据长度为4的observable1和数据长度为3的observable2进行合并转换后,观察者只接收到3个数据
         */
    
        public static void zip() {
    
            Observable observable1 = Observable.just(1, 2, 3, 4);
            Observable observable2 = Observable.just("哈哈", "嘻嘻", "啊啊");
    
    
            Observable
                    .zip(observable1, observable2, new BiFunction<Integer, String, String>() {
    
                        @Override
                        public String apply(Integer integer, String s) throws Exception {
                            return s + integer;
                        }
                    })
                    .subscribe(new Observer() {
                        @Override
                        public void onSubscribe(Disposable d) {
    
                        }
    
                        @Override
                        public void onNext(Object o) {
                            Log.d(TAG + "zip", o.toString());
                        }
    
                        @Override
                        public void onError(Throwable e) {
    
                        }
    
                        @Override
                        public void onComplete() {
                            Log.d(TAG + "merge", "onComplete");
                        }
                    });
        }

    输出如下:

    3

    4、combineLatest 操作符

     /**
         * ========================combineLatest 操作符 ======================================
         * 
         * 当两个Observable 中的任何一个发送了数据,将先发送了数据的Observable的最新(最后)一个数据和另一个Observable发送的每个数据结合,最终基于该结合的结果发送数据
         * 
         * 与zip()的区别: zip()是按个数合并,即11合并;而combineLatest()是基于时间合并,,即在同一时间点上合并
         */
    
    /**
         *
         *  ======================combineLatestDelayError =================================
         *
         *  作用类似于concatDelayError() / mergeDelayError(),用于错误处理
    
        public static void combineLatest() {
    
            Observable
                    .combineLatest(Observable.just(1, 2, 3)
                            , Observable.intervalRange(1, 4, 2, 1, TimeUnit.SECONDS)
                            , new BiFunction<Integer, Long, String>() {
                                @Override
                                public String apply(Integer integer, Long aLong) throws Exception {
                                    return "合并后的数据为:" + integer + aLong;
                                }
                            })
                    .subscribe(new Observer<String>() {
                        @Override
                        public void onSubscribe(Disposable d) {
    
                        }
    
                        @Override
                        public void onNext(String s) {
                            Log.d(TAG + "combineLatest", s);
                        }
    
                        @Override
                        public void onError(Throwable e) {
    
                        }
    
                        @Override
                        public void onComplete() {
                            Log.d(TAG + "combineLatest", "onComplete");
                        }
                    });
        }

    输出如下:

    4

    5、reduce ()操作符

    
        /**
         * ======================reduce  操作符=================================
         * 
         * 把被观察者需要发送的数据按照指定规则聚合成一个数据发送
         * 
         * 聚合的规则需要我们编写,内部流程是前两个数据按照我们的规则合并后,再与后面的数据按规则合并,依次类推。这样说有点抽象,看下面的例子。
         */
        public static void reduce() {
    
            Observable
                    .just(1, 2, 3, 4, 5)
                    .reduce(new BiFunction<Integer, Integer, Integer>() {
                        @Override
                        public Integer apply(Integer integer, Integer integer2) throws Exception {
                            Log.d(TAG + "reduce", "本次合并的过程是:  " + integer + "+" + integer2);
                            return integer + integer2;
                        }
                    })
                    .subscribe(new Consumer<Integer>() {
                        @Override
                        public void accept(Integer integer) throws Exception {
                            Log.d(TAG + "reduce", "最终计算的结果是 :  " + integer);
                        }
                    });
        }

    输出如下:

    5

    6、collect() 操作符

    
        /**
         * ========================collect 操作符=================================
         * 
         * 作用是把 Observable(被观察者)发送的事件收集到一个数据结构中
         */
        public static void collect() {
    
            Observable
                    .just(1, 2, 3, 4, 5)
                    .collect(new Callable<ArrayList<Integer>>() {
                        @Override
                        public ArrayList<Integer> call() throws Exception {
                            return new ArrayList<>();
                        }
                    }, new BiConsumer<ArrayList<Integer>, Integer>() {
                        @Override
                        public void accept(ArrayList<Integer> integers, Integer integer) throws Exception {
                            integers.add(integer);
                        }
                    })
                    .subscribe(new Consumer<ArrayList<Integer>>() {
                        @Override
                        public void accept(ArrayList<Integer> integers) throws Exception {
                            Log.d(TAG + "collect", integers.toString());
                        }
                    });
        }
    
    

    输出如下:

    6

    7、startWith()/startWithArray() 操作符

     /**
        * ========================startWith/startWithArray 操作符=================================
        * 
        * 在一个被观察者发送时间前,追加发送一些数据/一个新的被观察者
        */
        public static void startWith() {
    
            Observable.just(7, 8, 9)
                    .startWith(6)   //在发送序列去追加单个数据
                    .startWithArray(4, 5)  //在发送序列去追加多个数据
                    .startWith(Observable.just(1, 2, 3))  //在发送序列去追加单个被观察者
                    .subscribe(new Consumer<Integer>() {
                        @Override
                        public void accept(Integer integer) throws Exception {
                            Log.d(TAG + "startWith", String.valueOf(integer));
                        }
                    });
        }

    输出如下:

    7

    8、count() 操作符

    /**
        * ========================count 操作符=================================
        * 
        * 统计被观察者发送事件数量
        */
        public static void count() {
            Observable
                    .just(1, 2, 3, 4)
                    .count()
                    .subscribe(new Consumer<Long>() {
                        @Override
                        public void accept(Long aLong) throws Exception {
                            Log.d(TAG + "count", "发送事件的数量 : " + aLong);
                        }
                    });
        }

    输出如下:

    8


    上面代码地址


    RxJava各类型操作符详解如下:

    RxJava操作符汇总
    RxJava操作符(一) —-创建操作符
    RxJava操作符(二)—-转换操作符
    RxJava操作符(三)—-合并操作符
    RxJava操作符(四)—-功能操作符
    RxJava操作符(五) —-过滤操作符
    RxJava操作符(六)—-条件操作符

    展开全文
    zengke1993 2018-05-21 15:59:32
  • 操作符用于数据值,操作符包括算术操作符(如加好和减号)、位操作符、关系操作符和相等操作符等。JavaScript的操作符适用于很多值,例如字符串、数字值、布尔值,甚至对象。在应用于对象时,相应的操作符会调用对象...

    JavaScript操作符

    操作符用于数据值,操作符包括算术操作符(如加号和减号)、位操作符、关系操作符和相等操作符等。JavaScript的操作符适用于很多值,例如字符串、数字值、布尔值,甚至对象。在应用于对象时,相应的操作符会调用对象的valueOf()和(或)toString()方法,来取得可以操作的值。

    一、一元操作符

    只能操作一个值的操作符叫一元操作符

    1.递增和递减操作符

    1. 前置型:++(--)值先递增(递减),后执行语句;
    2. 后置型:值++(--)先使用值执行语句,后递增(递减)
    3. 这四个操作符对任何值都适用,不仅适用与整数,还可用于字符串、布尔值、浮点数值和对象
    4. 应用于不同的值时,遵循下列规则
      • 在应用于一个包含有效数字的字符串时,先将其转化为数字值,在执行加减1的操作
      • 在应用一个不包含有效数字的字符串时,将变量的值设为NaN
      • 在应用于布尔值时,先将其转换为数值变量,再执行j加减1的操作
      • 在应用于浮点数值时,执行加减1的操作
      • 在应用于对象时,先调用对象的valueOf()方法来取得一个可供操作的值,然后在对该值调用前述规则。如果结果是NaN,则在调用toString()方法后再应用前述规则。
    var s1="2";
    var s2="z";
    var b=false;
    var f=1.1;
    var o={
    	valueOf:function(){
    		return -1;
    	}
    };
    
    s1++;//3
    s2++;//NaN
    b++;//1
    f--;//0.10000000000000009(由于浮点舍入错误所致)
    o--;//-2
    

    2.一元加和减操作符

    一元加和减操作符主要用于基本的算术运算,也可以用于转换数据类型

    1. 一元加操作符以一个加号(+)表示,放在数值前面
      • 应用于数值时,对数值不会产生任何影响
      • 在对非数值应用一元加操作符时,该操作符会像Number()转型函数一样对这个值执行转换
      • 布尔值会转换成0和1,字符串值会按照一组特殊的规则进行解析,而对象是先调用它们的valueOf()和(或)toString()方法,在转换得到的值。
    2. 一元减操作符(-)主要用于表示负数
      • 应用与数值时,表示负数
      • 应用于非数值时,一元减操作符与一元加操作符相同的规则,最后在将得到的数值转换为负数
    var s1="01";
    var s2="1.1";
    var s3="z";
    var b=false;
    var f=1.1;
    var 0={
    	valueOf:function(){
    		return -1;
    	}
    };
    
    s1=-s1;//-1
    s2=-s2;//-1.1
    s3=-s3;//NaN
    b=-b;//0
    f=-f;//-1.1
    0=-0;//1
    

    二、位操作符

    1. 相关概念

    1. 位操作符用于最基本的层次上,即按内存中表示数值的位来操作数值。
    2. JavaScript中所有的数值都以64位格式储存,但位操作符并不直接操作64位的值。而是现将64位的值转换成32位的整数,然后执行操作,最后将结果转回64位
    3. 对于有符号的整数,32位中的前31位用于表示吧整数的值。第32位用于表示数值的符号:0表示正数,1表示负数。这个表示符号的位叫做符号位,符号位的值决定了其他位数值的格式
    4. 正数以纯二进制格式储存,31位中的每一位都表示2的幂。没有用到的位以0填充,即忽略不计
    5. 负数同样以二进制码存储,但使用的格式是二进制补码
    6. 计算一个数的二进制补码的步骤:
      • 求这个数值绝对值的二进制补码
      • 求二进制反码,0和1互换
      • 得到的二进制反码加1
    7. 在对特殊的NaNInfinity值应用位操作时,这两个值都会被当成0来处理
    8. 如果对非数值应用位操作符,会先使用Number()函数将该值转换为一个数值(自动完成),然后在应用位操作
    //求得-18的二进制码
    //1.首先求得18的二进制码
    0000 0000 0000 0000 0000 0000 0001 0010
    //2.求其二进制反码,即0和1互换
    1111 1111 1111 1111 1111 1111 1110 1101
    //3.二进制反码加1
    1111 1111 1111 1111 1111 1111 1110 1110
    //注意:在处理有符号整数时,是不能访问位31的
    //JavaScript会尽力地隐藏信息。在以二进制字符串形式输出一个负数时,能看到的是在这个负数绝对值的二进制码前面加上了一个符号
    

    2.分类

    1. 按位非(NOT)
      • 按位操作符(~),执行按位非的结果就是返回数值的反码
      • 按位非操作的本质:操作数的负值减1
    2. 按位与(AND)
      • 按位与操作符(&),它有两个操作数
      • 按位与操作只有在两个数值的对应位都是1时才返回1,任何一位是0,结果都是0
    3. 按位或(OR)
      • 按位或操作符(|),它有两个操作数
      • 按位或操作在有一位是1的情况下就返回1,而只有在两个位都是0的情况下才返回0
    4. 按位异或(XOR)
      • 按位异或符(^),它有两个操作数
      • 两个数值对应位值不同,则返回1;否则返回0
    5. 左移
      • 左移操作符(<<),这个操作符会将数值的所有位向左移动指定的位数
      • 左移操作后,会以0来填充右侧的空位
      • 左移不会影响操作数的符号位
    6. 有符号的右移
      • 有符号的右移操作符(>>),这个操作符会将数值向右移动,但保留符号位
      • 有符号的右移操作与左移操作正好相反
      • 向右移位后,将出现在原数值的左侧、符号位的右侧的空位,用符号为的值来填充
    7. 无符号右移
      • 无符号右移操作(>>>),这个操作符会将数值的32位都向右移动
      • 对正数来说,无符号右移的结果与有符号右移相同
      • 对负数来说
        • 无符号右移是0来填充空位,而不是向有符号右移那样以符号位的值来填充空位
        • 对正数的无符号右移与有符号右移结果相同,但对负数的结果就不一样了
        • 无符号右移操作符会把负数的二进制码当成正数的二进制码
        • 由于负数以其绝对值的二进制补码来表示,因此会导致无符号右移后的结果非常大
    //1.按位非(~)
    var num1=25;//二进制0000 0000 0000 0000 0000 0000 0001 1001
    var num2=~num1;//二进制1111 1111 1111 1111 1111 1111 1110 0110
    alert(num2);//-26
    
    var num1=25;
    var num2=-num1-1;
    alert(num2);//"-26"
    
    //2.按位与(&)
    var result=25&3;
    alert(result);//1
     25=0000 0000 0000 0000 0000 0000 0001 1001 
      3=0000 0000 0000 0000 0000 0000 0000 0011
     OR=0000 0000 0000 0000 0000 0000 0000 0001
    
    //3.按位或(|)
    var result=25|3;
    alert(result);//27
     25=0000 0000 0000 0000 0000 0000 0001 1001 
      3=0000 0000 0000 0000 0000 0000 0000 0011
     OR=0000 0000 0000 0000 0000 0000 0001 1011
    
    //4.按位异或(^)
    var result=25^3;
    alert(result);//26
     25=0000 0000 0000 0000 0000 0000 0001 1001 
      3=0000 0000 0000 0000 0000 0000 0000 0011
    XOR=0000 0000 0000 0000 0000 0000 0001 1010
    
    //5.左移(<<)
    var oldValue=2;//等于二进制的10
    var newValue=oldValue<<5;//等于二进制的1000000,十进制的64
    
    //6.有符号的右移(>>)
    var oldValue=64;//等于二进制的1000000
    var newValue=oldValue>>5;//等于二进制的10
    
    //7.无符号右移(>>>)
    //(1)正数
    var oldValue=64;//等于二进制的1000000
    var newValue=oldValue>>>5;//等于二进制的10
    //(2)负数
    var oldValue=-64;//二进制1111 1111 1111 1111 1111 1111 1100 0000
    var newValue=oldValue>>>5;//等于十进制134217726
    

    三、布尔操作符

    在一门编程语言中,布尔操作符非重要,有了它,流控制语句、循环语句才有作用

    1.逻辑非()

    1. 逻辑非()可以应用于JavaScript中的任何值。无论这个值是什么类型,都将会返回一个布尔值
    2. 逻辑非操作符会先将它的操作数转换为一个布尔值,然后在对其求反
    3. 遵循下列规则
      • 操作数是一个对象,则返回false
      • 操作数是一个空字符串,返回true
      • 操作符是一个非空字符串,返回flase
      • 操作数是数值0,返回true
      • 操作数是任意非0数值(包括Infinity),返回false
      • 操作数是null,返回true
      • 操作数是NaN,返回true
      • 操作数是undefined,返回true
    4. 同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数的行为。其中,第一个逻辑非操作会基于无论什么操作数返回一个布尔值,而第二个逻辑非操作则会对该布尔值取反,于是得到了这个值真正对应的布尔值。最终结果与对这个值使用Boolean()函数相同
    alert(!false);//true
    alert(!"blue");//false
    alert(!0);//true
    alert(!NaN);//true
    alert(!"");//true
    alert(!12345);//false
    
    alert(!!"blue");//true
    alert(!!0);//false
    alert(!!NaN);//false
    alert(!!"");//false
    alert(!!12345);//true
    

    2.逻辑与(&&)

    1. 逻辑与操作符(&&),它有两个操作数
    2. 逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值
    3. 在有一个操作数不是布尔值的情况下。逻辑与操作就不一定返回布尔值
    4. 遵循下列规则
      • 如果第一个操作数是对象,则返回第二个操作数
      • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为true的情况下才会返回该对象
      • 如果两个操作数都是对象,则返回第二个操作数
      • 如果第一个操作数是null,则返回null
      • 如果第一个操作数是NaN,则返回NaN
      • 如果第一个操作数是undefined,则返回undefined
    5. 逻辑与操作属于短路操作。即第一个操作数能决定结果,那就不会在对第二个操作数求值
    6. 对于逻辑与操作而言,如果第一个操作数是false,则不会继续对第二个操作数求值
    7. 不能在逻辑与操作中使用未定义的值
    var found=true;
    var result=(found&&someUndefinedVar);//发生错误
    alert(result);//这一行不会执行
    
    var found=false;
    var result=(found&&someUndefinedVar);
    alert(result);//false
    //第一个操作数的值是false,不会再对第二个操作数求值。所以即使someUndefinedVar没有定义,警告框也会显示出来
    

    3.逻辑或(||)

    1. 逻辑或操作符(||),它有两个操作数
    2. 两个操作数都为false,返回false;否则返回true
    3. 遵循下列规则
      • 如果第一个操作数是对象,则返回第一个操作对象
      • 如果第一个操作数的求值结果是false,则返回第二个操作数
      • 如果两个操作数都是对象,则返回第一个操作数
      • 如果两个操作数都是null,则返回null
      • 如果两个数都是NaN,则返回NaN
      • 如果两个数都是undefined,则返回undefined
    4. 与逻辑与操作相似,逻辑或操作符也是短路操作符 。如果第一个操作数的求值结果是true,就不会对第二个操作数求值
    5. 可以利用逻辑或来避免为变量赋值nullundefined
    var found=true;
    var result=(found||someUndefinedVar);//不会发生错误
    alert(result);//true
    //变量found值是true,不会对someUndefinedVar进行求值
    
    var myObject=preObject||backObject;
    //如果preObject的值不是空,则将preObject的值赋给myObject;如果是空,则将backObject的值赋给myObject
    

    四、乘性操作符

    在操作数为非数值时,会执行自动的类型转换.如果参与乘性计算的某个操作数不是数值,后台会先使用Number()转型函数将其转化为数值。即空字符串被当做0,布尔值true被当做为1

    1.乘法(*)

    1. 乘法操作符(*),用于计算两个数值的乘积
    2. 在处理特殊值时,遵循下列规则
      • 如果操作数都是数值,执行常规的乘法计算。如果乘积超过了JavaScript数值的表示范围,则返回Infinity-Infinity
      • 如果一个操作数是NaN,则结果是NaN
      • 如果Infinity与0相乘,则结果是NaN
      • 如果Infinity与非0数值相乘,则结果是Infinity-Infinity,取决于有符号操作数的符号
      • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则
    var result=34*56;
    

    2.除法(/)

    1. 除法操作符(/),执行第二个操作数除以第一个操作数的计算
    2. 在处理特殊值时,遵循下列规则
      • 如果操作数都是数值,执行常规的除法计算。如果商超过了JavaScript数值的表示范围,则返回Infinity-Infinity
      • 如果一个操作数是NaN,则结果是NaN
      • 如果是InfinityInfinity除,则结果是NaN
      • 如果是零被零除,则结果是NaN
      • 如果是非零的有限数被零除,则结果是Infinity-Infinity,取决于有符号操作数的符号
      • 如果是Infinity被任何非零数值除,则结果是Infinity-Infinity,取决于有符号操作数的符号
      • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后应用上面的规则
    var result=66/11;
    

    3.求模(%)

    1. 求模(余数)操作符(%
    2. 在处理特殊值时,遵循下列规则
      • 如果操作数都是数值,执行常规的除法计算,返回除得的余数
      • 如果被除数是无穷大值而除数是有限大的数值,则结果是NaN
      • 如果被除数是有限大的数值而除数是零,则结果是NaN
      • 如果是InfinityInfinity除,则结果是NaN
      • 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数
      • 如果被除数是零,则结果是零
      • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则
    var result=26%5;//1
    

    五、加性操作符

    与乘性操作符类似,加性操作符也会在后台转换不同的数据类型。对于加性操作符来说,相应的转化规则还有些复杂

    1.加法(+)

    1. 如果两个操作符都是数值,则执行常规的加法计算
    2. 遵循下列规则
      • 如果有一个操作数是NaN,则结果是NaN
      • 如果是InfinityInfinity,则结果是Infinity
      • 如果是-Infinity-Infinity,则结果是-Infinity
      • 如果是Infinity-Infinity,则结果是NaN
      • 如果是+0+0,则结果是+0
      • 如果是-0-0,则结果是-0
      • 如果是+0-0,则结果是+0
      • 如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来
      • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来
      • 如果一个操作数是对象、数值或布尔值,则调用它们的toString()方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于undefinednull,则分别调用String()函数并取得字符串"undefined""null"
    var result1=5+5;//两个数值相加
    alert(result1);//10
    var result2=5+"5";//一个数值和一个字符串相加
    alert(result2);//"55"
    
    var num1=5;
    var num2=10;
    var message="The sum of 5 and 10 is "+num1+num2;
    alert(message);//"The sum of 5 and 10 is 510"
    
    var num1=5;
    var num2=10;
    var message="The sum of 5 and 10 is "+(num1+num2);
    alert(message);//"The sum of 5 and 10 is 15"
    //一对圆括号把两个数值变量括在一起,这样就会告诉解析器先计算其结果,然后再将结果与字符串拼接起来
    

    2.减法(-)

    1. 遵循下列规则
      • 如果两个操作数都是数值,则执行常规的算术减法操作并返回结果
      • 如果一个操作数是NaN,则结果是NaN
      • 如果是InfinityInfinity,则结果是NaN
      • 如果是-Infinity-Infinity,则结果是NaN
      • 如果是Infinity-Infinity,则结果是Infinity
      • 如果是-InfinityInfinity,则结果是-Infinity
      • 如果是+0+0,则结果是+0
      • 如果是-0+0,则结果是-0
      • 如果是-0-0,则结果是+0
      • 如果有一个操作数是字符串、布尔值、nullundefined,则先在后台调用Number()函数将其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是NaN,则减法的结果就是NaN
      • 如果有一个操作数是对象,则调用对象的valueOf()方法以取得表示该对象 的数值。如果得到的值是NaN,则减法的结果就是NaN。如果对象没有valueOf()方法,则调用其toString()方法并将得到的字符串转换为数值
    var result1=5-true;//4
    var result2=NaN-1;//NaN
    var result3=5-3;//2
    var result4=5-"";//5
    var result=5-"2";//3
    var result=5-null;//5
    

    六、关系操作符

    1.相关概念

    1. 小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较。这几个操作符都返回一个布尔值
    2. 操作非数值时,遵循下列规则
      • 如果这两个操作数都是数值,则执行数值比较
      • 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值
      • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较
      • 如果一个操作数是对象,则调用这个对象的valueOf()方法,并用得到的结果根据前面的规则执行比较
      • 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较
    var result="Brick"<"alphabet";//true
    //字母B的字符编码为66,二字母a的字符编码为97
    
    var result="23"<"3";//true
    //两个操作数都是字符串,而字符串比较的是字符编码("2"的字符编码是50,而"3"的字符编码是51)
    
    var result="23"<3;//false
    //在比较字符串和数值时,字符串都会被转换为数值,然后再以数值的形式与另一个数值进行比较
    
    var result="a"<3;//false
    //由于字母a不能转换为合理的数值,因此被转换成了NaN
    
    var result1=NaN<3;//false
    var result2=NaN>=3;//false
    //按照常理,如果一个值不小于另一个值,则一定是大于或等于另一个值。在与NaN比较时,都返回了false
    

    七、相等操作符

    1.相等和不相等(==)

    1. 相等操作符(==),如果两个操作数相等,则返回true
    2. 不相等操作符(!=),如果两个操作数不相等,则返回true
    3. 这两个操作符都会先转换操作数(强制转型),然后再比较它们的相等性
    4. 转换不同经数据类型时,遵循下列规则
      • 如果一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换 为0,true转换为1
      • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
      • 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较
    5. 这两个操作符进行比较时,遵循下列规则
      • nullundefined是相等的
      • 要比较相等性之前,不能将nullundefined转换成其它任何值
      • 如果一个操作数是NaN,则相等操作符返回false,而不相等操作符返回true即使两个操作数都是NaN,相等操作符返回false,因为按照规则,NaN不等于NaN
      • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true;否则,返回false
    表达式表达式
    null==undefinedtruetrue==1true
    "NaN"==NaNfalsetrue==2false
    5==NaNfalseundefined==0false
    NaN==NaNfalsenull==0false
    NaN!=NaNtrue"5"==5true
    false==0true

    2.全等和不全等(===)

    1. 比较不转换操作数,全等和不全等操作符相同
    2. 全等操作符(===),只在两个操作数未经转换就相等的情况下返回true
    3. 不全等操作符(!==),在两个操作数未经转换就不相等的情况下返回true
    4. null==undeifned会返回true,因为它们是类似的值;但null===undefined会返回false,因为它们是不同类型的值
    var result1=("55"==55);//true,转换后相等
    var result2=("55"===55);//false,不转换,字符串不等于数值
    
    var result1=("55"!=55);//false,转换后相等
    var result2=("55"!==55);//true,不转换,字符串不等于数值
    

    八、条件操作符

    1.相关概念

    1. 条件操作符非常灵活,即 三元运算符
    2. 表达式?true:false;
    var max=(num1>num2)?num1:num2;
    //如果num1>num2(关系表达式返回true),则将num1的值赋给max;如果num1小于或等于num2(关系表达式返回false),则将num2的值赋给max
    

    九、赋值操作符

    1.相关概念

    1. 赋值操作符(=),将右侧的值赋给左侧的变量
    2. 在(=)前面在添加乘性操作符、加性操作符或位操作符,就可以完成复核赋值操作
    3. 复合赋值操作符
      • 乘(*)赋值:*=
      • 除(/)赋值:/=
      • 模(%)赋值:%=
      • 加(+)赋值:+=
      • 减(-)赋值:-=
      • 左移(<<):<<=
      • 有符号右移(>>)赋值:>>=
      • 无符号右移(>>>)赋值:>>>=
    4. 复合操作符主要目的是简化赋值运算,使用它们不会有任何性能的提升

    十、逗号操作符

    1.相关概念

    1. 使用逗号操作符(,)可以在一条语句中执行多个操作
    2. 逗号操作符多用于声明多个变量
    3. 在用于赋值时,逗号操作符总会返回表达式中的最后一项
    var num=(5,1,4,8,0);//num的值为0
    //由于0是表达式中的最后一项,因此num的值就是0
    

    源自
    源于整理《JavaScript高级程序设计》

    展开全文
    qq_41218152 2018-08-11 22:05:03
  • 1. 算术操作符 1.1 普通算术操作符 1.2 一元操作符 1.3 赋值算术操作符 2. 关系运算符 3. 逻辑运算符 3.1 优先级 3.2 短路特性 4. 位运算操作符 5. 条件运算符 5.1 非运算符 5.2 三元运算符 5.3 ...

    目录

    1. 算术操作符

    1.1 普通算术操作符

    1.2 一元操作符

    1.3 赋值算术操作符

    2. 关系运算符

    3. 逻辑运算符

    3.1 优先级

    3.2 短路特性

    4. 位运算操作符

    5. 条件运算符

    5.1 非运算符

    5.2 三元运算符

    5.3 埃尔维斯操作符

    6. 对象操作符

    6.1 安全导航操作符

    6.2 直接字段访问操作符

    6.3 方法指针操作符

    7. 正则表达式操作符

    7.1 模式操作符

    7.2 查找操作符

    7.3 匹配操作符

    8. 其他操作符

    8.1 展开操作符

    8.1.1 展开方法参数

    8.1.2 展开列表元素

    8.1.3 展开映射元素

    8.2 区间操作符

    8.3 比较操作符

    8.4 下标操作符

    8.5 成员关系操作符

    8.6 身份操作符

    8.7 强制转换操作符

    8.8 钻石操作符

    8.9 方法调用操作符

    9. 操作符优先级

    10. 操作符重载


    本文主要介绍 Groovy 语言的操作符,基于 Groovy 2.5.5 版本。

    1. 算术操作符

    Groovy 支持你所熟知的来自数学或其他编程语言中的常用算术运算符。Groovy 支持 Java 中的所有算术运算符。我们将在下面的一系列示例中学习它们。

     

    1.1 普通算术操作符

    Groovy 支持的二元算术操作符如下表所示:

    操作符用途说明

    +

    相加

     

    -

    相减

     

    *

    相乘

     

    /

    相除

    如果想使用整数除法,请使用 intdiv() 方法,请同时查看 integer division 章节了解更多关于整数除法的返回值类型信息。

    %

    取余

     

    **

    乘方

    请查看 the power operation 章节来了解更多关于乘方操作符的返回值信息。

    下面是关于这些操作符用法的一些例子:

    assert  1  + 2 == 3
    assert  4  - 3 == 1
    assert  3  * 5 == 15
    assert  3  / 2 == 1.5
    assert 10  % 3 == 1
    assert  2 ** 3 == 8

     

    1.2 一元操作符

    + - 操作符也可以被当作一元操作符来使用:

    assert +3 == 3
    assert -4 == 0 - 4
    
    assert -(-1) == 1      //1

    //1:将一元操作符 - 应用到表达式上时,需要在表达式上添加括号

    在一元算术操作符方面,Groovy 也同时支持前缀和后缀形式的自增(++)和自减(--)操作符:

    def a = 2
    def b = a++ * 3                 //1
    
    assert a == 3 && b == 6
    
    def c = 3
    def d = c-- * 2                 //2
    
    assert c == 2 && d == 6
    
    def e = 1
    def f = ++e + 3                 //3
    
    assert e == 2 && f == 5
    
    def g = 4
    def h = --g + 1                 //4
    
    assert g == 3 && h == 4

    //1: 后缀自增操作符会在表达式求值完成后,对 a 执行自增操作,并将表达式的值赋值给 b

    //2: 后缀自减操作符会在表达式求值完成后,对 c 执行自减操作,并将表达式的值赋值给 d

    //3: 前缀自增操作符会先对 e 执行自增操作,然后对表达式进行求值并赋值给 f

    //4: 前缀自减操作符会先对 g 执行自减操作,然后对表达式进行求值并赋值给 h

     

    1.3 赋值算术操作符

    前面我们看到的各种二元算术操作符都具有对应的赋值算术操作符形式:

    • +=

    • -=

    • *=

    • /=

    • %=

    • **=

    我们来看些实例:

    def a = 4
    a += 3
    
    assert a == 7
    
    def b = 5
    b -= 3
    
    assert b == 2
    
    def c = 5
    c *= 3
    
    assert c == 15
    
    def d = 10
    d /= 2
    
    assert d == 5
    
    def e = 10
    e %= 3
    
    assert e == 1
    
    def f = 3
    f **= 2
    
    assert f == 9

     

    2. 关系运算符

    关系操作符允许在对象之间执行比较运算,以确定两个对象是否相同,或者是否一个比另一个大、小或等于另一个。Groovy 中有如下的关系运算符:

    操作符用途

    ==

    等于

    !=

    不等于

    <

    小于

    <=

    小于等于

    >

    大于

    >=

    大于等于

    下面是使用这些操作符进行简单的算术比较的例子:

    assert 1 + 2 == 3
    assert 3 != 4
    
    assert -2 < 3
    assert 2 <= 2
    assert 3 <= 4
    
    assert 5 > 1
    assert 5 >= -2

     

    3. 逻辑运算符

    Groovy 为布尔表达式提供了 3 个逻辑运算符:

    • &&: 逻辑与

    • ||: 逻辑或

    • !: 逻辑非

    用下面例子作简单演示:

    assert !false           
    assert true && true     
    assert true || false    

    3.1 优先级

    逻辑非 !运算符的优先级高于逻辑与 && 运算符:

    assert (!false && false) == false     //1

    //1: 这里断言为真(因为括号内的表达式为假),因为逻辑非运算符的优先级高于逻辑与运算符,所有逻辑非操作符只作用于第一个 false,不然的话,它就得作用于逻辑与运算的结果,导致断言失败。

    逻辑与 && 运算符的优先级高于逻辑或 || 运算符:

    assert true || true && false     //1

    //1: 这里断言为真,因为 && 运算符的优先级比 || 运算符高,因此 || 运算最后执行,并返回真值。

     

    3.2 短路特性

    逻辑或 || 运算符具有短路特性:如果运算符的左操作数为真,那么它就已经知道结果无论如何都是真,因此右操作数就不会执行计算。仅当左操作数为假时,右操作数才会执行计算。

    类似地,逻辑与操作也具有短路特性:如果运算符的左操作数为假,那么它就已经知道结果无论如何都是假,因此右操作数就不会执行计算。仅当左操作数为真时,右操作数才会执行计算。

    boolean checkIfCalled() {       //1
        called = true
    }
    
    called = false
    true || checkIfCalled()
    assert !called                  //2
    
    called = false
    false || checkIfCalled()
    assert called                   //3
    
    called = false
    false && checkIfCalled()
    assert !called                  //4
    
    called = false
    true && checkIfCalled()
    assert called                   //5

    //1: 创建一个叫做 checkIfCalled 的函数,它内部会将 called 变量设置为 true

    //2: 将 called 标记置为 false 后,我们验证下 || 操作符的左操作数为真时,不会对右操作数执行计算,因为 || 操作符具有短路特性

    //3: 由于 || 操作符的左操作数为假,因此会对又操作数执行计算,导致 called 标记变为 true

    //4: 类似的,我们验证 && 操作符的左操作数为假时,不会对右操作数执行计算

    //5: 但是当 && 操作符的左操作数为真时,会对右操作数执行计算

     

    4. 位运算操作符

    Groovy 中有 4 个位运算操作符:

    • &: 位与

    • |: 位或

    • ^: 位异或

    • ~: 按位取反

    位运算操作符可以应用于 byteint 型数据,并返回一个 int 值:

    int a = 0b00101010
    assert a == 42
    int b = 0b00001000
    assert b == 8
    assert (a & a) == a                         // 位与
    assert (a & b) == b                         // 位与并返回同时为 1 的比特
    assert (a | a) == a                         // 位或
    assert (a | b) == a                         // 位或并返回所有为 1 的比特
    
    int mask = 0b11111111                       // 设置一个只检查最后 8 位的掩码
    assert ((a ^ a) & mask) == 0b00000000       // 对自身进行按位异或将返回 0
    assert ((a ^ b) & mask) == 0b00100010       // 位异或
    assert ((~a) & mask)    == 0b11010101       // 按位取反

    值得注意的是,原始类型的内部表示遵循 Java 语言规范。特别地,原始类型是有符号的,这意味着对于按位取反操作,通常比较好的实践是:使用掩码来提取那些必需的比特。

    在 Groovy 中,位操作符是可以重载的,意味着你可以针对任意类型的对象定义这些操作符作用在其上的行为。

     

    5. 条件运算符

    5.1 非运算符

    非运算符由感叹号 表示,它会反转底层布尔表达式的计算结果。特别是非运算符可以和 Groovy 真值(Groovy truth)结合使用:

    assert (!true)    == false   // 非 true 即 false                   
    assert (!'foo')   == false   // 'foo' 是非空字符串,求值后为 true, 取非后得到 false                   
    assert (!'')      == true    // '' 是空字符串,求值后为 false, 取非后得到 true

     

    5.2 三元运算符

    三元操作符是一个缩写的表达式,它等效于一个由 if/else 分支语句组织起来的赋值语句。

    除了使用下面的这个 if/else 语句外:

    if (string!=null && string.length()>0) {
        result = 'Found'
    } else {
        result = 'Not found'
    }

    你可以把它简写成:

    result = (string!=null && string.length()>0) ? 'Found' : 'Not found'

    三元操作符也是可以和 Groovy 真值(Groovy truth)结合使用的,所有上面的写法可以进一步简化:

    result = string ? 'Found' : 'Not found'

     

    5.3 埃尔维斯操作符

    埃尔维斯操作符是三元运算符的缩写形式。使用这种便捷写法的一个实际场景是:如果一个表达式求值为假(基于 Groovy 真值)时需要返回一个合理的默认值的情况。下面是个简单的例子:

    displayName = user.name ? user.name : 'Anonymous'    //1
    displayName = user.name ?: 'Anonymous'               //2

    //1: 使用三元操作符时,你必须重复你想要赋值的那个值

    //2: 使用埃尔维斯操作符时,如果被测试值为真时,就会使用该值作为返回值

    使用埃尔维斯操作符能够降低代码的复杂性也能够减小代码重构时发生错误的几率:因为不需要在条件和真值返回值部分重复被测试的表达式。

     

    6. 对象操作符

    6.1 安全导航操作符

    安全导航操作符 ?. 主要用来避免空指针异常(NullPointerException)。通常,当你有一个指向某个对象的引用时,在使用它进行方法或属性访问前,都需要检查它是否为 null。为了避免这种检查,安全导航操作符在引用为 null 时,会直接返回 null,而不是抛出空指针异常。如下例所示:

    def person = Person.find { it.id == 123 }    // find 将会返回 null
    def name = person?.name     // 使用安全导航操作符可以避免空指针异常                 
    assert name == null      // 结果为 null

     

    6.2 直接字段访问操作符

    在 Groovy 中,当你写了一段类似下面的代码时:

    class User {
        public final String name                     // 公有字段 name
        User(String name) { this.name = name}        
        String getName() { "Name: $name" }   // name 的读取器(getter),会返回一个定制化的字符串     
    }
    def user = new User('Bob')
    assert user.name == 'Name: Bob'     // 此处会调用读取器

    user.name 调用会触发一个到同名的属性的调用,也就是说,在这里会触发对 name 的读取器(getName)的调用。如果你的确是想获取字段 name 的值,而不是调用它的读取器,你就可以向下面这样使用直接属性访问操作符 .@ 来实现:

    assert user.@name == 'Bob'      //1

    //1: 使用 .@ 操作符强制访问字段自身,而不是对应的获取器

     

    6.3 方法指针操作符

    方法指针操作符(.&)被用来获取方法的引用,并存储到一个变量中,以便后续使用:

    def str = 'example of method reference'        // 变量 str 中存储了一个字符串       
    def fun = str.&toUpperCase          // 把 str 实例上的 toUpperCase 方法的引用存储到变量 fun 中                      
    def upper = fun()                   // 可以向普通的方法调用一样调用 fun                              
    assert upper == str.toUpperCase()   // 结果和在 str 上直接调用 toUpperCase 方法是一样的

    使用方法指针具有许多优点。首先,方法指针的类型是 groovy.lang.Closure,所以在任何可以使用闭包的地方都可以使用方法指针。特别地,它适合用来转换一个已有的方法以满足策略模式的需求:

    def transform(List elements, Closure action) {                    //1
        def result = []
        elements.each {
            result << action(it)
        }
        result
    }
    String describe(Person p) {                                       //2  
        "$p.name is $p.age"
    }
    def action = this.&describe                                       //3
    def list = [
        new Person(name: 'Bob',   age: 42),
        new Person(name: 'Julia', age: 35)]                           //4
    assert transform(list, action) == ['Bob is 42', 'Julia is 35']    //5

    //1: transform 方法会对参数列表中的每一个元素调用 action 闭包,并返回一个新列表

    //2: 定义一个接受 Person 类型参数,返回字符串的函数

    //3: 创建一个指向 describe 函数的方法指针

    //4: 创建一个参数列表

    //5: 可以在需要闭包的地方使用方法指针

    方法指针是绑定在方法接受者和方法名上的。方法参数是在运行时解析的,这就是说,如果你有多个相同名称的方法,语法也是一样的,并没有什么不同,只是解析具体要调用的方法是在运行时完成的:

    def doSomething(String str) { str.toUpperCase() }        //1
    def doSomething(Integer x) { 2*x }                       //2
    def reference = this.&doSomething                        //3
    assert reference('foo') == 'FOO'                         //4
    assert reference(123)   == 246                           //5

    //1: 定义一个重载的 doSomething 方法,接受字符串类型参数

    //2: 定义一个重载的 doSomething 方法,接受整形参数

    //3: 创建一个指向 doSomething 的方法指针,并没有指定参数类型

    //4: 使用字符串参数调用方法指针时,会调用字符串版本的 doSomething 方法

    //5: 使用整形参数调用方法指针时,会调用整形版本的 doSomething 方法

     

    7. 正则表达式操作符

    7.1 模式操作符

    模式操作符(~)提供了一个创建 java.util.regex.Pattern 实例的简单方式:

    def p = ~/foo/
    assert p instanceof Pattern

    尽管你通常看到模式操作符一般和斜线风格的字符串一起使用,但其实它可以和任何形式的 Groovy 字符串一起使用:

    p = ~'foo'                                   //1                     
    p = ~"foo"                                   //2                     
    p = ~$/dollar/slashy $ string/$              //3                     
    p = ~"${pattern}"                            //4

    //1: 使用单引号风格的字符串构建模式

    //2: 使用双引号风格的字符串构建模式

    //3: 使用美元斜线风格的字符中构建模式,不用在字符串内对 $ 和 / 字符进行转意

    //4: 也可以使用 GString 创建模式

     

    7.2 查找操作符

    除了先构建一个模式外,也可以直接使用查找操作符(=~)来构建一个 java.util.regex.Matcher 对象:

    def text = "some text to match"
    def m = text =~ /match/                                        //1       
    assert m instanceof Matcher                                    //2
    if (!m) {                                                      //3
        throw new RuntimeException("Oops, text not found!")
    }

    //1: 查找操作符 =~ 使用右侧的模式对左侧的 text 变量创建了一个 Matcher 

    //2: 查找操作符的返回类型是 Matcher

    //3: 等效于调用 if(!m.find())

    因为 Matcher 对象强制转换成 boolean 值是通过调用它的 find 方法实现的,所以查找操作符 =~ 用作判断式时(在 if, while 语句中等)表现出的行为和 Perl 语言中的 =~ 操作符一致。

     

    7.3 匹配操作符

    匹配操作符(==~)看起来像查找操作符(=~)的一个变种,它不返回 Matcher 对象,而是返回一个 boolean 值,并且要求输入值与模式严格匹配:

    m = text ==~ /match/                                                  //1
    assert m instanceof Boolean                                           //2
    if (m) {                                                              //3
        throw new RuntimeException("Should not reach that point!")
    }

    //1: 匹配操作符会使用右侧的模式来严格匹配左侧的变量

    //2: 匹配操作符的返回类型是布尔值

    //3: 等价与调用 if(text ==~ /match/)

     

    8. 其他操作符

    8.1 展开操作符

    展开点操作符(*.),简称为展开操作符,通常被用来在聚合对象的每一个元素上执行操作。它等效于在聚合对象的每个元素上调用操作,然后把所有结果收集到一个列表里:

    class Car {
        String make
        String model
    }
    def cars = [
           new Car(make: 'Peugeot', model: '508'),
           new Car(make: 'Renault', model: 'Clio')]      //1 
    def makes = cars*.make                               //2 
    assert makes == ['Peugeot', 'Renault']               //3 

    //1: 创建一个有 Car 类型元素组成的列表。列表是一个聚合对象。

    //2: 在列表上调用展开操作符,访问每个元素的 make 属性

    //3: 返回一个包含各个元素对应的 make 属性所组成的列表

     

    表达式 car*.make 等价于 car.collect { it.make }。当所访问的属性不是被操作列表自身的属性时,Groovy 的 GPath 语法允许使用展开点操作符的缩写形式,但是操作仍然会自动展开到列表的每一个元素上。在前面的例子中,我们就可以使用 car.make 这个简写形式,但是通常还是推荐显式地写出展开点操作符。

    展开点操作符是 null 安全的,这意味着,如果被操作集合中有元素为 null 时,将会返回 null,而不是抛出 NullPointerException 异常。

    cars = [
       new Car(make: 'Peugeot', model: '508'),
       null,                                                  //1
       new Car(make: 'Renault', model: 'Clio')]               
    assert cars*.make == ['Peugeot', null, 'Renault']         //2
    assert null*.make == null                                 //3
    

    //1: 创建一个包含 null 元素的列表

    //2: 使用展开操作符不会抛出空指针异常

    //3: 展开操作符的接受者也可能为 null,此时返回值也是 null

     

    展开操作符可以被用于任何实现了 Iterable 接口的类上:

    class Component {
        Long id
        String name
    }
    class CompositeObject implements Iterable<Component> {
        def components = [
            new Component(id: 1, name: 'Foo'),
            new Component(id: 2, name: 'Bar')]
    
        @Override
        Iterator<Component> iterator() {
            components.iterator()
        }
    }
    def composite = new CompositeObject()
    assert composite*.id == [1,2]
    assert composite*.name == ['Foo','Bar']

    当操作嵌套的聚合数据结构时,可以使用多个级联的展开点操作符,如下例中的 cars*.models.*name :

    class Make {
        String name
        List<Model> models
    }
    
    @Canonical
    class Model {
        String name
    }
    
    def cars = [
        new Make(name: 'Peugeot',
                 models: [new Model('408'), new Model('508')]),
        new Make(name: 'Renault',
                 models: [new Model('Clio'), new Model('Captur')])
    ]
    
    def makes = cars*.name
    assert makes == ['Peugeot', 'Renault']
    
    def models = cars*.models*.name
    assert models == [['408', '508'], ['Clio', 'Captur']]
    assert models.sum() == ['408', '508', 'Clio', 'Captur'] // 展平一层
    assert models.flatten() == ['408', '508', 'Clio', 'Captur'] // 展平所有层(此处仅有一层)

    对于嵌套集合类的情况,可以考虑使用 collectNested DGM 方法,而不是展开点操作符:

    class Car {
        String make
        String model
    }
    def cars = [
       [
           new Car(make: 'Peugeot', model: '408'),
           new Car(make: 'Peugeot', model: '508')
       ], [
           new Car(make: 'Renault', model: 'Clio'),
           new Car(make: 'Renault', model: 'Captur')
       ]
    ]
    def models = cars.collectNested{ it.model }
    assert models == [['408', '508'], ['Clio', 'Captur']]

     

    8.1.1 展开方法参数

    有时可能你要调用的那个方法的参数已经存在于某个列表中,你必须做些适配把它们转化为方法的参数。在这样情况下,你可以使用展开操作符来调用该方法。举个例子,假如你有如下的方法签名:

    int function(int x, int y, int z) {
        x*y+z
    }

    假设你还有下面这个列表:

    def args = [4,5,6]

    那么你可以向下面这样调用该方法,而不用定义任何中间变量:

    assert function(*args) == 26

    我们甚至允许混合正常参数和展开参数:

    args = [4]
    assert function(*args,5,6) == 26
    

     

    8.1.2 展开列表元素

    当在列表字面量中使用展开操作符时,效果看起来就像被展开的列表元素被直接内联到了被操作的列表字面量中:

    def items = [4,5]                      //1
    def list = [1,2,3,*items,6]            //2
    assert list == [1,2,3,4,5,6]           //3

    //1: 定义一个列表 items

    //2: 我们想把 items 列表中的元素直接插入到 list 列表中,而不调用 addAll 方法

    //3: items 列表的内容被内联到了 list 列表中

     

    8.1.3 展开映射元素

    展开映射操作符和展开列表操作符类似,只是它操作的是映射。它允许你将一个映射的元素内联到另一个映射字面量中,如下例所示:

    def m1 = [c:3, d:4]                       //1
    def map = [a:1, b:2, *:m1]                //2
    assert map == [a:1, b:2, c:3, d:4]        //3

    //1: m1 是我们想要内联的映射

    //2: 我们使用 *:m1 的语法来将 m1 的内容展开到 map 映射中

    //3: 现在 map 包含 m1 中的所有元素

     

    展开映射操作符的使用位置是会对最终结果产生影响的,如下面例子所示:

    def m1 = [c:3, d:4]                       //1
    def map = [a:1, b:2, *:m1, d: 8]          //2
    assert map == [a:1, b:2, c:3, d:8]        //3

    //1: m1 是我们想要内联的映射

    //2: 我们使用 *:m1 的语法来将 m1 的内容展开到 map 映射中,但是在展开操作后,我们重新定义了键 d 的值

    //3: 现在 map 包含 m1 中的所有的键,但是键 d 对应的值是修改后的

     

    8.2 区间操作符

    Groovy 支持区间的概念,并且提供了区间操作符(..)来创建区间对象:

    def range = 0..5                                    //1
    assert (0..5).collect() == [0, 1, 2, 3, 4, 5]       //2
    assert (0..<5).collect() == [0, 1, 2, 3, 4]         //3
    assert (0..5) instanceof List                       //4
    assert (0..5).size() == 6                           //5

    //1: 一个由整数组成的区间

    //2: 一个由整数组成的闭区间(包含首尾元素)

    //3: 一个有整数组成的左闭右开区间(包含首元素,不包含尾元素)

    //4: groovy.lang.Range 实现了 List 接口

    //5: 可以在区间上调用 size() 方法

     

    区间的实现是很轻量的,因为只有起始和结尾元素会被存储下来。你可以从任意具有 next() previous() 方法且实现了 Comparable 接口的对象来创建区间。nextprevious 方法分别用来确定区间里的后一个和前一个元素。例如你可以向下面这样创建一个字符组成的区间:

    assert ('a'..'d').collect() == ['a','b','c','d']

     

    8.3 比较操作符

    比较操作符(Spaceship operator)内部其实是委派给 compareTo 方法的:

    assert (1 <=> 1) == 0
    assert (1 <=> 2) == -1
    assert (2 <=> 1) == 1
    assert ('a' <=> 'z') == -1

     

    8.4 下标操作符

    下标操作符 []getAtputAt 方法的速写,具体代表的含有主要要看操作符是位于赋值运算符的左侧还是右侧:

    def list = [0,1,2,3,4]
    assert list[2] == 2                     //1    
    list[2] = 4                             //2    
    assert list[0..2] == [0,1,4]            //3    
    list[0..2] = [6,6,6]                    //4    
    assert list == [6,6,6,3,4]              //5    

    //1: [2] 可以使用 .getAt(2) 替换

    //2: 如果位于赋值操作符的左侧,实际相当于调用 putAt

    //3: getAt 也支持 Range 类型的参数

    //4: 同样 putAt 也支持 Range 类型的参数

    //5: 列表被改变了

     

    使用下标操作符,结合一个定制化的 getAt/putAt 实现,是一种方便的解析对象的方式:

    class User {
        Long id
        String name
        def getAt(int i) {                                               //1          
            switch (i) {
                case 0: return id
                case 1: return name
            }
            throw new IllegalArgumentException("No such element $i")
        }
        void putAt(int i, def value) {                                   //2
            switch (i) {
                case 0: id = value; return
                case 1: name = value; return
            }
            throw new IllegalArgumentException("No such element $i")
        }
    }
    def user = new User(id: 1, name: 'Alex')                             //3
    assert user[0] == 1                                                  //4
    assert user[1] == 'Alex'                                             //5
    user[1] = 'Bob'                                                      //6
    assert user.name == 'Bob'                                            //7

    //1: User 类型定义了一个定制化的 getAt 实现

    //2: User 类型定义了一个定制化的 putAt 实现

    //3: 创建一个 User 对象

    //4: 使用下标操作符和索引 0 来获取用户的 id

    //5: 使用下标操作符和索引 1 来获取用户的 name

    //6: 可以使用下标操作符来给属性赋值,这都归功于底层对 putAt 方法的调用

    //7: 校验 name 属性的确发生了变化

     

    8.5 成员关系操作符

    成员关系操作符(in)就等价于调用 isCase 方法。具体到一个 List 对象,它就等效于调用 contains 方法,请看下例:

    def list = ['Grace','Rob','Emmy']
    assert ('Emmy' in list)             //1

    //1: 等价于调用 list.contains('Emmy') list.isCase('Emmy')

     

    8.6 身份操作符

    在 Groovy 中使用 == 进行相等性测试和 Java 中是有区别的。在 Groovy 中,它实际会调用 equals 方法。如果你想比较引用的相等性,你应该像下面的例子一样使用 is 操作符:

    def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        //1
    def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        //2
    assert list1 == list2                                       //3
    assert !list1.is(list2)                                     //4

    //1: 创建一个字符串列表

    //2: 创建另一个包含相同元素的字符串列表

    //3: 使用 ==,我们是在测试对象的相等性

    //4: 使用 is,我们是在检查引用的相等性

     

    8.7 强制转换操作符

    强制转换操作符(as)是强制类型转换的变种。它将对象强制地由一种类型转换为另一种类型,而不需要这两种类型具有可赋值性。我们看个例子:

    Integer x = 123
    String s = (String) x     //1

    //1: IntegerString 类型之间不具有可赋值性,因此运行时该处将抛出 ClassCastException

     

    可以通过强制转换来修复该问题:

    Integer x = 123
    String s = x as String        //1

    //1: Integer 和 String 类型之间不具有可赋值性,但是使用 as 将会把这个整形强制转换为一个字符串

     

    当一个对象被强制转换成另一个类型的对象时,除非目标类型和源类型相同,否则强制转换都将会返回一个新的对象。强制转换的规则因着不同的源和目标类型而不同,并且如果两个类型之间没有转换规则的话,强制转换也可能会失败。定制化的转换规则可以使用 asType 方法来实现:

    class Identifiable {
        String name
    }
    class User {
        Long id
        String name
        def asType(Class target) {              //1                              
            if (target == Identifiable) {
                return new Identifiable(name: name)
            }
            throw new ClassCastException("User cannot be coerced into $target")
        }
    }
    def u = new User(name: 'Xavier')            //2                                       
    def p = u as Identifiable                   //3                                
    assert p instanceof Identifiable            //4                                
    assert !(p instanceof User)                 //5

    //1: User 类中定义了一个从 UserIdentifiable 类的转换规则

    //2: 创建一个 User 类的实例

    //3: 将 User 实例强制转换成 Identifiable 类型的对象

    //4: 强制转换后的对象是 Identifiable 类的实例

    //5: 强制转换后的对象不再是 User 类的实例

     

    8.8 钻石操作符

    钻石操作符(<>)只是一个语法糖操作符,它的引入只是为了兼容 Java 7 中的同名操作符。它用来表明泛型类型应该从声明中推导:

    List<String> strings = new LinkedList<>()

    在动态类型的 Groovy 中,这个操作符完全用不到。在静态类型检查的 Groovy 中,该操作符也是可选的,因为无论该操作符是否存在,Groovy 类型检查器都会执行类型推断。

     

    8.9 方法调用操作符

    方法调用操作符 () 被用来隐式地调用一个名叫 call 的方法。对于任意一个定义了 call 方法的对象,你都可以省略 .call 部分,而以方法调用操作符 () 代之:

    class MyCallable {
        int call(int x) {           //1
            2*x
        }
    }
    
    def mc = new MyCallable()
    assert mc.call(2) == 4          //2
    assert mc(2) == 4               //3

    //1: MyCallable 类定义了一个叫做 call 的方法。请注意,它不需要实现 java.util.concurrent.Callable 接口

    //2: 使用常规的方法调用语法来调用 call 方法

    //3: 可以省略 .call 部分,这都归功于方法调用操作符

     

    9. 操作符优先级

    下面的表格按照优先级顺序列出了所以 Groovy 操作符:

    优先级操作符名称

    1

    new   ()

    对象创建,显式括号

     

    ()   {}   []

    方法调用,闭包,列表/映射字面量

     

    .   .&   .@

    成员访问,方法指针,字段/属性直接访问

     

    ?.   *   *.   *:

    安全导航,展开,展开点,展开映射

     

    ~   !   (type)

    按位取反/模式,逻辑非,类型转换

     

    []   ++   --

    类标/映射/数组索引,后缀自增/自减

    2

    **

    乘方

    3

    ++   --   +   -

    前缀自增/自减,正号,负号

    4

    *   /   %

    乘,除,取余

    5

    +   -

    加,减

    6

    <<   >>   >>>   ..   ..<

    左移/右移,无符号右移,闭区间,左闭右开区间

    7

    <   <=   >   >=   in   instanceof  as

    小于,小于等于,大于,大于等于,成员操作符,实例判断,强制类型转换

    8

    ==   !=   <=>

    等于,不等于,比较

     

    =~   ==~

    正则查找,正则匹配

    9

    &

    位与

    10

    ^

    位异或

    11

    |

    位或

    12

    &&

    逻辑与

    13

    ||

    逻辑或

    14

    ? :

    三目运算符

     

    ?:

    埃尔维斯运算符

    15

    =   **=   *=   /=   %=   +=   -=  
    <<=   >>=   >>>=   &=   ^=   |=

    各种赋值运算符

     

    10. 操作符重载

    Groovy 允许你重载各种操作符,以便你能够在自定义类中使用它们。请看下面这个简单的类:

    class Bucket {
        int size
    
        Bucket(int size) { this.size = size }
    
        Bucket plus(Bucket other) {                         //1
            return new Bucket(this.size + other.size)
        }
    }

    //1: Bucket 类实现了一个名为 plus 的特殊方法

     

    仅仅通过实现 plus() 方法,现在 Bucket 类就可以像下面这样使用加法操作符:

    def b1 = new Bucket(4)
    def b2 = new Bucket(11)
    assert (b1 + b2).size == 15          //1

    //1: 可以使用加法操作符 + 对两个 Bucket 对象进行相加

     

    所有非比较型的 Groovy 操作符都有一个与之相关联的方法,你可以在自己的类中按需实现这些方法。唯一的要求是,该方法要是公有的,有正确的名称和正确个数的参数。而参数类型就依赖于你想在操作符右侧支持哪些类型了。例如你可以支持下面这样的调用:

    assert (b1 + 11).size == 15

    这只需要实现一个具有以下签名的 plus() 方法即可:

    Bucket plus(int capacity) {
        return new Bucket(this.size + capacity)
    }

     

    下面是操作符和其关联的方法名的完整列表:

    操作符关联方法操作符关联方法

    +

    a.plus(b)

    a[b]

    a.getAt(b)

    -

    a.minus(b)

    a[b] = c

    a.putAt(b, c)

    *

    a.multiply(b)

    a in b

    b.isCase(a)

    /

    a.div(b)

    <<

    a.leftShift(b)

    %

    a.mod(b)

    >>

    a.rightShift(b)

    **

    a.power(b)

    >>>

    a.rightShiftUnsigned(b)

    |

    a.or(b)

    ++

    a.next()

    &

    a.and(b)

    --

    a.previous()

    ^

    a.xor(b)

    +a

    a.positive()

    as

    a.asType(b)

    -a

    a.negative()

    a()

    a.call()

    ~a

    a.bitwiseNegate()

     

     

     

     

     

    展开全文
    qq_26886929 2019-01-22 21:42:22
  • llxxyy507 2018-08-30 16:44:20
  • memoryI 2019-03-12 10:30:55
  • weixin_43606158 2019-05-11 15:07:11
  • weixin_44368963 2020-01-08 09:20:00
  • weixin_39540651 2019-09-16 14:18:20
  • carson_ho 2017-10-23 09:24:11
  • Fine1938768839 2017-06-21 17:09:37
  • shanghx_123 2018-03-24 14:40:48
  • dangzhangjing97 2018-06-28 18:15:32
  • llq108 2019-09-13 12:07:13
  • johnny901114 2019-07-04 14:43:42
  • qq_31348733 2019-09-02 20:45:31
  • qq_38708084 2019-07-25 19:40:44
  • zengke1993 2018-05-21 16:35:29
  • qq_38677031 2018-11-19 09:12:33
  • weixin_46022434 2020-05-24 21:18:23
  • qq_45027204 2020-03-18 14:21:05
  • gsjthxy 2020-07-08 20:17:04
  • weixin_44475093 2021-03-03 14:58:11
  • weixin_43719811 2018-08-11 21:26:00
  • Lixuanshengchao 2015-02-14 21:39:26
  • dangnianmingyue_gg 2017-07-19 10:34:44
  • weixin_42905573 2020-05-03 23:05:00
  • u010890358 2018-05-28 16:27:35
  • chzphoenix 2017-09-22 17:55:08
  • sunlizhen 2017-06-16 16:16:58

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,831,951
精华内容 732,780
关键字:

操作符

友情链接: 机器学习.zip