|     java thinking in java initialization   |    

摘要

进入本章之前,我先扯了点对象的初始化。本属于上一章的遗留问题,在这章做个总结。

进入正题,就是各种操作符了。中间在讲到赋值符号 =,按位操作符 <<>> 以及数据类型变换符 ( type ) 的时候扯了点基本型的数据结构。因为本身八种基本型在java里太特殊了,非常重要。大多数的操作符都只对基本型有效。当然除了 == 等价符能作用于所有对象。

另外java里不像C++那样能对操作符进行重载。唯一的例外,好像就是String的 + 号被用来连接字符串。实际是被转换成StringBuffer的append()方法。不能重载是不是少了点灵活性,只有实际使用的时候才知道。

类中字段的初始化

第二章课后练习的第11题中,遇到了字段初始化的问题。认真看了Java Turorial:Java字段初始化实例化的方法:静态或者final初始化block。我这里做个总结,然后通过一个示例具体演示一下。

Java在声明类的字段的时候,可以简单在声明的同一行里,同时初始化,比如简单赋值(默认值)或者实例化(用new关键字)。比如以下代码,

public class BedAndBreakfast {

    // initialize to 10
    public static int capacity = 10;

    // initialize to false
    private boolean full = false;
}

但是,在声明后面直接跟复杂的对实例赋值,java是不允许的。比如以下代码,会抛出异常。

public class BedAndBreakfast {

    // initialize to 10, then 21
    public static int capacity = 10;
    capacity = 21;

    // initialize with condition
    private boolean full;
    if (capacity>20) {
    	full=true;
    } else {
    	full=false;
    }
}

这时候最常用的赋值方法是使用构造函数。比如这样写,把起床后的状态设为:饱了。

public class BedAndBreakfast {

    // initialize to 10
    public static int capacity = 10;

    // initialize to false
    private boolean full = false;

    //constructor
    public BedAndBreakfast() {
    	capacity=21;
    	if (capacity>20) {
    		this.full=true;
    	} else {
    		this.full=false;
    	}
    }
}

但还有两种方法允许我们定义一个实例化赋值过程。

第一种:block

block就是指类中以一对花括号{ }括起来的代码。

{
    // whatever code is needed for initialization goes here
}

Java允许一个类的代码中有任意多个block。会在解析任意一个构造函数constructor之前,解析block里的代码。因此,block很适合用来存放所有构造函数共有的一些代码。

给一个block加一个static关键字,就可以用来为静态字段赋值。

static {
    // whatever code is needed for static initialization goes here
}

第二种: 定义并调用一个初始化的函数

另一个方法是直接写一个返回值为字段类型的函数。这样再复杂的初始化过程都可以封装在函数中。一般我们加一个final关键字作防护,防止初始化代码在子类中被重载。具体代码如下,

class Whatever {
    private varType myVar = initializeInstanceVariable();
    //一般加上final关键字保护一下,可以防止在子类中被重载。
    protected final varType initializeInstanceVariable() {

        // initialization code goes here
    }
}

实例演示

实例里,我创建了3个队列(FIFO先进先出)。

  1. 一个是静态的。用静态block初始化。
  2. 一个是正常用new初始化。会调用构造函数。
  3. 一个直接用初始化函数初始化。

每个初始化过程一旦对某个队列赋值了,就会依次留下”某某某,到此一游!”的标记。最后打印队列,就能知道初始化过程访问字段并赋值的先后顺序。

/**
 *  This class is used to test different way of initializing fields in a class.
 *
 *  We attempt to assign values in three different ways:
 *      1. constructor
 *      2. block
 *      3. inicialize method
 *
 *  @author Wei SHEN
 *  @author wei.shen@iro.umontreal.ca
 *  @version 1.0
 */

package com.ciaoshen.thinkinjava.chapter3;

import java.util.*;

/**
 *  Our class used to implement this test.
 */
public class TestInicializeField {

    //Three queues: invisible to users
    private LinkedList<String> blockVisitor = new LinkedList<String>();
    private static LinkedList<String> staticBlockVisitor = new LinkedList<String>();
    private LinkedList<String> methodVisitor = createVisitor();

    //constructor of the class
    public TestInicializeField(){
        //constuctorHashMap is inicialized by constructor.
        staticBlockVisitor.push("构造函数,到此一游!");
        this.blockVisitor.push("构造函数,到此一游!");
    }

    //free initialize block
    {
        staticBlockVisitor.push("自由代码块,到此一游!");
        this.blockVisitor.push("自由代码块,到此一游!");
    }

    //static initialize block
    static{
        staticBlockVisitor.push("静态代码块,到此一游!");
    }

    //initialize method
    private LinkedList<String> createVisitor(){
        LinkedList<String> newQueue = new LinkedList<String>();
        newQueue.push("初始化函数,到此一游!");
        return newQueue;
    }


    /**
     *  Responsable for printing the content in no metter which HashMap.
     *  invisible to users
     *  @param aQueue Just give me a LinkedList<String> please.
     */
    private void printQueue(LinkedList<String> aQueue){
        while (aQueue.peekFirst()!=null){
            System.out.println("  "+aQueue.pollFirst());
        }
    }

    /**
     *  Print two HashMap fields in this class one by one.
     *  The only interface in this class for user.
     *  @param Need no parameter.
     */
    public void printThreeVisitor(){
        System.out.println("普通字段: ");
        this.printQueue(this.blockVisitor);
        System.out.println("静态字段: ");
        this.printQueue(staticBlockVisitor);
        System.out.println("调用函数字段:");
        this.printQueue(this.methodVisitor);
    }

    /**
     *  Main method. Just create a new object of this class, and see what happend.
     *  @param args Need no parameter.
     */
    public static void main (String args[]){
        TestInicializeField myTest = new TestInicializeField();
        myTest.printThreeVisitor();
    }  
}

输出结果: initializeTest 从结果中可以看到无论测试类中block,赋值函数,以及构造函数的顺序怎么摆放,最后的输出结果都显示java处理赋值的固定顺序是:

  1. 静态block
  2. 普通block
  3. 构造函数

这和java tutorial的描述相符,在调用构造函数之前,java编译器会自动把block中的代码复制到构造函数里,而且是置于构造函数代码的前面。然后,静态block的优先级当然是高于普通block,因为静态字段是在所有实例化之前完成的动作。

然后我们看到,直接调用初始化函数赋值的话,就不会再调用构造函数,自然也就不包括所有的block。

操作符操作的对象

Almost all operators work only with primitives. The exceptions are ‘=‘, ‘==‘ and ‘!=‘, which work with all objects (and are a point of confusion for objects). In addition, the String class supports ‘+’ and ‘+=‘.

这句话很好地总结了Java操作符操作对象的一般规则。

赋值操作

因为java基本型存在stack中,储存的是实际的值,而不是引用。因此,如果操作符赋值基本数据类型a=b,是直接把b的值复制到了a,就是所谓的“传值”。接下来修改a,不会对b有影响。

int b=5;
int a;
a=b

但当a和b不是基本型,而是对象的时候,再进行a=b的赋值操作,就只是把b对对象的引用复制一份给a。这时,a和b指向同一个对象,改变其中任意一个,一会改变另一个。这就是所谓的“传址”。也叫别名问题

String a="boy";
String b="girl";
a=b; //此时,a,b都指向"girl"
a="boy"; //此时,a,b都指向"boy"

将一个对象传递给方法时,也会产生“别名问题”,也就是也是“传址”。下面练习3中的forceToChangeTo5方法是个很好的例子。对象toto作为参数,传递了自己的地址进去,而不是自己的一份拷贝。

练习2,练习3

Create a class containing a float and use it to demonstrate aliasing. Create a class containing a float and use it to demonstrate aliasing during method calls.

/**
 *  use float to demonstrate aliasing.
 */

package com.ciaoshen.thinkinjava.chapter3;
import java.util.*;

class AssignmentOperator {
    public float f = 0.0f;

    public AssignmentOperator(float f){
        this.f = f;
    }

	//toto参数“传址”,而不是他本身的一个拷贝。所以,函数内部对toto的操作,直接作用到toto引用的对象,改变toto本身。
    public void forceToChangeTo5(AssignmentOperator toto){
        toto.f = 5.5f;
    }

    public static void main(String args[]){
        AssignmentOperator objA = new AssignmentOperator(1.1f);
        AssignmentOperator objB = new AssignmentOperator(2.2f);

        //objA and objB refer to the same object
        objA = objB;
        System.out.println(Float.toString(objA.f)+",  "+Float.toString(objB.f)); // output: 2.2f,  2.2f

        //objA and objB still refer to the same object
        objA.f = 3.3f;
        System.out.println(Float.toString(objA.f)+",  "+Float.toString(objB.f)); // output: 3.3f,  3.3f

        //objA change the value of objB. They still refer to the same object.
        //objB把自己的“引用”传进去,自己也被影响。
        objA.forceToChangeTo5(objB);
        System.out.println(Float.toString(objA.f)+",  "+Float.toString(objB.f)); // output: 5.5f,  5.5f
    }
}

运算操作符

加减乘除

练习4

Write a program that calculates velocity using a constant distance and a constant time.

package com.ciaoshen.thinkinjava.chapter3;
import java.util.*;

/**
 *  Generate randomly length and time
 *  Then calculate the speed.
 */
public class Speed {

    private Random rand = new Random(99);
    private int length = 0;
    private int time = 0;

    //default constructor use the default seed 99.
    public Speed(){
        this.length = this.rand.nextInt(1000)+1;
        this.time = this.rand.nextInt(100)+1;
    }

    //initialize the object by specifying the random seed.
    public Speed(int seed){
        Random newRand = new Random(seed);
        this.rand=newRand;
        this.length = this.rand.nextInt(1000)+1;
        this.time = this.rand.nextInt(100)+1;
    }

    //method that calculate the speed.
    public float getSpeed(){
        return (float)this.length/(float)this.time;
    }

    //public main method
    public static void main(String args[]){
        Speed test1 = new Speed(88);
        Speed test2 = new Speed(77);
        System.out.println(Float.toString(test1.getSpeed())); //output: 6.982143
        System.out.println(Float.toString(test2.getSpeed())); // output: 35.2
    }
}

关系操作符

<,>,<=,>=,==,!=,这些都是关系操作符,都能作用与所有基本型(除了boolean)。

对象等价性

作者特别提到==equals()的区别。

==
  1. 判定的是对象的等价性,必须是实实在在地是同一个对象才返回true
  2. 判定基本型的值是否相等。
//
int i1 = 47;
int i2 = 47;
System.out.println(i1 == i2); //output: true
//基本型包装类
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1 == n2); //output: false
//String大人
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); //output: false
equals()

equals()是可以判定对象内容的方法。但不能用于基本型。而且要用这个方法,必须自己重载。不然他默认只是从普通类object继承下来的,还是比较对象的“引用”,也就是内存地址。

//Value类型的object没有被重载(调教)过。当然不能比较值的大小。
class Value {
      public int i;

      public static void main(String[] args) {
        Value v1 = new Value();
        Value v2 = new Value();
        v1.i = v2.i = 100;
        System.out.println(v1.equals(v2)); //output: false
      }
}

JDK自带重载好equals()方法的类很少。最常用的就是String以及基本型的包装类,比如Integer。

//基本型包装类
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2)); //output: true
//String大人
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); //output: true

可爱的练习题5,6

Create a class called Dog containing two Strings: name and says. In main( ), create two dog objects with names “spot” (who says, “Ruff!”) and “scruffy” (who says, “Wurf!”). Then display their names and what they say.

Following Exercise 5, create a new Dog reference and assign it to spot’s object. Test for comparison using == and equals( ) for all references.

/**
 *  Lets dogs to talk.
 *  @author Wei SHEN
 *  @author wei.shen@iro.umontreal.ca
 *  @version 1.0
 */
package com.ciaoshen.thinkinjava.chapter3;

import java.util.*;


/**
 *  Class Dog
 */
public class Dog {
    //two fields name and says.
    //invisible to users.
    private String name = new String();
    private String says = new String();

    //default constructor
    public Dog(){

    }

    //constructor
    public Dog(String inName, String inSays){
        this.name = inName;
        this.says = inSays;
    }

    //interface for user.
    //to get the name of the dog.
    public String getName(){
        return this.name;
    }

    //interface for user.
    //to let them yelp.
    public String yelp(){
        return this.says;
    }

    /**
     *  main class: create two dogs, get their names and let them yelp.
     *  @param args not used here.
     */
    public static void main(String args[]){
        //give me three dogs
        Dog littleBlack = new Dog("spots","Ruff!");
        Dog littleGray = new Dog("cruffy","Wurf!");
        Dog copyBlack = new Dog();

        //get their name and let them yelp.
        System.out.println(littleBlack.getName());
        System.out.println(littleBlack.yelp());
        System.out.println(littleGray.getName());
        System.out.println(littleGray.yelp());

        //compare littleBlack and copyBlack
        copyBlack = littleBlack;
        System.out.println(copyBlack == littleBlack); //TRUE
        System.out.println(copyBlack.name == littleBlack.name); //TRUE
        System.out.println(copyBlack.equals(littleBlack)); //TRUE
        System.out.println(copyBlack.says.equals(littleBlack.says)); //TRUE
    }
}

练习7

Write a program that simulates coin-flipping.

package com.ciaoshen.thinkinjava.chapter3;
import java.util.*;

/**
 *  Our class: can be seen as a "coin" which can call flipping method to stimulate "flipping a coin".
 */
public class FlipCoin {
    private Random rander = new Random(99);


    //default constructor
    public FlipCoin(){ }

    //constructor with new random seed
    public FlipCoin(int seed){
        this.rander = new Random(seed);
    }

    //flipping a coin
    public String flipping (){
        int result = this.rander.nextInt(2);
        if (result == 0){
            return "head";
        } else {
            return "back";
        }
    }

    /**
     *  main method
     *  @param args no used.
     */
    public static void main(String args[]){
        FlipCoin myCoin = new FlipCoin(47);
        for (int i=0; i<20; i++){
            System.out.println(myCoin.flipping());
        }
    }
}

八进制,十六进制为基本型赋值

直接用八进制或十六进制为基本型赋值,电脑都能看得懂。

  1. 0开头,表示八进制
  2. 0x开头,表示十六进制

最后加上数据类型的标识:

  1. 后面加L(l):提示是long型
  2. 后面加F(f):提示是float型
  3. 后面加D(d):提示是double型

一大波儿栗子:

    //many fields
    public int i8 = 012345; //0开头,八进制
    public int i16 = 0x3ab589; //0x开头,十六进制

    public long l8 = 012345L; //0开头,八进制,大写L比小写l好,看的清楚。
    public long l16 = 0x4cd424L; //0x开头,十六进制

    public float f8 = 012345F; //0开头,八进制
    public float f16 = 0x5ef6F; //0x开头,十六进制,等等,战斗机?
    public double d16 = 0x5ef6D; //0x开头,十六进制,等等,战斗机?

    public byte byteMax = 0x7f; //byte允许的最大十六进制。7位二进制,最高位被占用。
    public char charMax = 0xffff; //char允许的最大十六进制。16位二进制。
    public short shortMax = 0x7fff; //short允许的最大十六进制。15位二进制,最高位被占用。

练习8

Show that hex and octal notations work with long values. Use Long.toBinaryString( ) to display the results.

package com.ciaoshen.thinkinjava.chapter3;
import java.util.*;

public class Literal {

    //many fields
    public int i8 = 012345; //0开头,八进制
    public int i16 = 0x3ab589; //0x开头,十六进制

    public long l8 = 012345L; //0开头,八进制,大写L比小写l好,看的清楚。
    public long l16 = 0x4cd424L; //0x开头,十六进制

    public float f8 = 012345F; //0开头,八进制
    public float f16 = 0x5ef6F; //0x开头,十六进制,等等,战斗机?
    public double d16 = 0x5ef6D; //0x开头,十六进制,等等,战斗机?

    public byte b16 = 0x7f; //byte允许的最大十六进制。8bit,其中最高位为符号位,二进制1111111。
    public char c16 = 0xffff; //char允许的最大十六进制。16bit,没有符号位,二进制16个1。
    public short s16 = 0x7fff; //short允许的最大十六进制。16bit,最高位符号位,二进制15个1。
    //public short sToInt = 0x7fffff; //超出最大值,会自动改成int,并提示我们越界了。

    //default constructor
    public Literal(){ };

    /**
     *  main method: to print these fields
     *  @param args void
     */
    public static void main(String args[]){

        Literal test = new Literal();

                //print all fields in the class
        System.out.println(Integer.toBinaryString(test.i8));
        System.out.println(Integer.toBinaryString(test.i16));

        System.out.println(Long.toBinaryString(test.l8));
        System.out.println(Long.toBinaryString(test.l16));

        System.out.println(Float.toString(test.f8));
        System.out.println(Float.toString(test.f16));
        System.out.println(Double.toString(test.d16));

        System.out.println("byte允许最大值:"+Integer.toBinaryString(test.byteMax));
        System.out.println("char允许最大值:"+Integer.toBinaryString(test.charMax));
        System.out.println("short允许最值:"+Integer.toBinaryString(test.shortMax));
    }
}

Output:

1010011100101
1110101011010110001001
1010011100101
10011001101010000100100
12345.0
388975.0
388973.0
byte允许最大值1111111
char允许最大值1111111111111111
short允许最值111111111111111

指数的表示方法

1.39e-43f表示1.39乘以10的-43次方

练习9

Display the largest and smallest numbers for both float and double exponential notation.

借这道题科普一下float和double类型的结构。解释一下为什么他们的最大值是这样的。

我们都知道float型占32bit内存,double型占64个bit。

但他们当然不会把所有位都用来直接存数字,内部是有分工的。根据IEEE 784协议规定: float的32位中: 最高位符号位,8位指数位,23位尾数位。如下图, float double的64位中: 最高位符号位,11位指数位,52位尾数位。如下图, double

package com.ciaoshen.thinkinjava.chapter3;
import java.util.*;

public class Literal {
    //float 最高位符号位,8位指数位,23位尾数位,共32位。
    public float floatMin = Float.MIN_VALUE; //float允许最大值。
    public float floatMax = Float.MAX_VALUE; //float允许最小正值。
    //double 最高位符号位,11位指数位,52位尾数位,共64位。
    public double doubleMin = Double.MIN_VALUE; //double允许最大值。
    public double doubleMax = Double.MAX_VALUE; //double允许最小正值。

    //default constructor
    public Literal(){ };

    /**
     *  main method: to print these fields
     *  @param args void
     */
    public static void main(String args[]){

        Literal test = new Literal();

        System.out.println("float允许最小正值:"+Float.toString(test.floatMin));
        System.out.println("float允许最大值:"+Float.toString(test.floatMax));
        System.out.println("double允许最小正值:"+Double.toString(test.doubleMin));
        System.out.println("double允许最大值:"+Double.toString(test.doubleMax));
    }
}

Output:

float允许最小正值1.4E-45
float允许最大值3.4028235E38
double允许最小正值4.9E-324
double允许最大值1.7976931348623157E308

按位操作符

漏掉了。在第四章的习题5,补上了。 传送门: Think in Java 读书笔记:第三章 - 控制执行流程

移位操作符

练习题11,12

十一题:Start with a number that has a binary one in the most significant position (hint: Use a hexadecimal constant). Using the signed right-shift operator, right shift it all the way through all of its binary positions, each time displaying the result using Integer.toBinaryString( ).

十二题:Start with a number that is all binary ones. Left shift it, then use the unsigned right-shift operator to right shift through all of its binary positions, each time displaying the result using Integer.toBinaryString( ).

package com.ciaoshen.thinkinjava.chapter3;
import java.util.*;

public class Bitwise {
    public int max = 0x7fffffff; //最大int数。31个1。
    public int x =0x7abcdefa; // 二进制:1111010101111001101111011111010. int最高位标正负,31位有效位,这个数最高有效位为1,符合要求。
    public char c = 'x';

    //default constructor
    public Bitwise(){ };

    //right shift for 31 times.
    public void exercise11 (){
        //there are 31 bits in the int number
        int i =31;

        while (true) {
            System.out.println(Integer.toBinaryString(this.x));
            if (i>0){
                this.x >>= 1; //带符号右位移一位
                i--;
            } else {
                break;
            }
        }
    }

    public void exercise12 (){
        //there are 31 bits in the int max
        int i =31;

        System.out.println(Integer.toBinaryString(this.max));
        //left-shift
        this.max <<= 1;
        //right shift unsigned through all of its binary positions.
        while (true) {
            System.out.println(Integer.toBinaryString(this.max)+"   //十进制:"+this.max);
            if (i>0){
                this.max >>>= 1;
                i--;
            } else {
                break;
            }
        }
    }

    //print char c in different form
    public void exercise13 (){
        System.out.println(Character.toString(this.c)+" //text");
        System.out.println(Integer.toString(this.c)+" //十进制,Integer.toString()默认10进制");
        System.out.println(Integer.toBinaryString(this.c)+"    //二进制");
        System.out.println(Integer.toString(this.c,8)+"    //八进制");
        System.out.println(Integer.toString(this.c,16)+"    //十六进制");
    }

    /**
     *  main class
     *  @param args void
     */
    public static void main(String args[]){
        Bitwise testInt = new Bitwise();

        testInt.exercise11();
        testInt.exercise12();
        testInt.exercise13();
    }
}
练习11,Output:
1111010101111001101111011111010
111101010111100110111101111101
11110101011110011011110111110
1111010101111001101111011111
111101010111100110111101111
11110101011110011011110111
1111010101111001101111011
111101010111100110111101
11110101011110011011110
1111010101111001101111
111101010111100110111
11110101011110011011
1111010101111001101
111101010111100110
11110101011110011
1111010101111001
111101010111100
11110101011110
1111010101111
111101010111
11110101011
1111010101
111101010
11110101
1111010
111101
11110
1111
111
11
1
0
12题我要解释一下

我取了int的MAX_VALUE作为初始值(也就是31位1),怎么左移一位变-2了,而且显示了32位。

int是4个字节,32bit,这没问题。最高位32位是正负标记:0表示正数,1表示负数。当是正数时,最高位正负标记0一般不显示。所以我们的初始值是31位。

1111111111111111111111111111111 //31位

实际上内存里是32位:

0-1111111111111111111111111111111 //32位,最高位标记为0

因为左移一位,把最高位的正负标记覆盖掉了。当正负标记为1的时候,不省略标记。然后最低位用0补足,所以变成了这样:

1-1111111111111111111111111111110 //32位,最高位为1,是个负数

乍一看,这个负数负地好厉害呀,但实际上Java采用”2的补码“(Two’s Complement)编码负数。如图分两步: minusInt

  1. 第一步,每一个二进制位都取相反值,0变成1,1变成0。
  2. 第二步,将上一步得到的值加1。

所以,要解读一个负数,就要把步骤倒过来,先值减1,然后再二进制取反。具体我们这个例子:

//java解读一个负数的编码
1-1111111111111111111111111111110 //32位,最高位为1,是个负数
1-1111111111111111111111111111101 //最高位保持不变,后31位减1。
1-0000000000000000000000000000010 //最高位保持不变,后31位取反=2
//这个数等于-2
下面是练习12,output:
1111111111111111111111111111111		//初始值,十进制:2147483647
11111111111111111111111111111110   //十进制:-2
1111111111111111111111111111111   //十进制:2147483647
111111111111111111111111111111   //十进制:1073741823
11111111111111111111111111111   //十进制:536870911
1111111111111111111111111111   //十进制:268435455
111111111111111111111111111   //十进制:134217727
11111111111111111111111111   //十进制:67108863
1111111111111111111111111   //十进制:33554431
111111111111111111111111   //十进制:16777215
11111111111111111111111   //十进制:8388607
1111111111111111111111   //十进制:4194303
111111111111111111111   //十进制:2097151
11111111111111111111   //十进制:1048575
1111111111111111111   //十进制:524287
111111111111111111   //十进制:262143
11111111111111111   //十进制:131071
1111111111111111   //十进制:65535
111111111111111   //十进制:32767
11111111111111   //十进制:16383
1111111111111   //十进制:8191
111111111111   //十进制:4095
11111111111   //十进制:2047
1111111111   //十进制:1023
111111111   //十进制:511
11111111   //十进制:255
1111111   //十进制:127
111111   //十进制:63
11111   //十进制:31
1111   //十进制:15
111   //十进制:7
11   //十进制:3
1   //十进制:1
练习13,output:
x //text
120 //十进制
1111000    //二进制
170    //八进制
78    //十六进制

三元操作符

形式如下。如果判断结果为true,执行value0。结果为false,执行value1

//语法
boolean-exp ? value0 : value1

//例子
return i < 10 ? i * 100 : i * 10;

//换成if-else语句
if(i < 10) {
	return i * 100;
} else {
    return i * 10;
}

( )类型转换符

( )类型转换符,支持除了boolean之外所有基本型的互相转换。下面的实验说明,就算放不下,也不会报错,但没有正常赋值,这个要小心。

//随便做个试验
int i = 0x7fffffff; //最大int,char和short里放不下
System.out.println(i);	//正常:2147483647
System.out.println((float)i);	//正常:2.14748365E9
System.out.println((long)i);	//正常:2147483647
System.out.println((short)i);	//放不下:-1
System.out.println((char)i);	//放不下:￿