Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

java基础(一)之初级概念

2017-06-10
nivelle

奇数性

java对取余操作(%)的定义产生的后果。该操作符被定义为对所有的int数值a和所有的非零数值b,都满足下面恒等式:

(a/b)*b+(a%b)==a

与java的截尾整数正常操作符号相结合时,意味着:当取余操作返回一个非零的结果时,它与做操作数具有相同的正负符号。

正确做法:

public static bolean isOdd(int i){
    return i%2 !=0;
}

或者:

pulic static boolean isOdd(int i){
    return (i&1)!=0
}

找零

public class Change{
  public static void main(String args[]){
    System.out.println(2.00-1.10);
  }
}

问题在于1.1这个数字不能被精确表示为一个double,因此被表示为最接近它的double值。然后2减去的就是这个值。

浮点运算在一个范围很广的值域上提供了很好的近似,但是通常不能产生精确的的结果。二进制浮点数对于货币计算是非常不合适的,因为它不可能将0.1或者10的其他任何次负幂,精确表示为一个有限长度的二进制小数。

解决方法:

  • 解决该问题的一种方式是使用某种整数类型,例如int或long,并且以分为单位来执行计算。如果采纳了次路线,请确保该整数类型大到足以表示程序中将要用的所有值。

  • 使用执行精确小数运算的BigDecimal。一定要用BigDecimal(String)构造器,而不使用BigDecimal(double)。后一个构造器将用它的参数的精确值来创建一个实例。例如new Bigdecimal(.1),它将返回一个BigDecimal,也即0.1000000000000000055511151231257827021181583404541015625 正确使用BigDecimal就可以正确打印出期望值0.90:

 System.out.println(new BigDecimal("2.00").subtract(new BigDecimal("1.10")));

注意:在需要精确答案的地方,要避免使用float和double;对于货币计算,要使用int、long或者BigDecimal。

长整除

被除数表示一天里的微妙数,而除数表示一天里的毫秒数:

public class LongDivision {
  public static void main(String[] args) {
    final long MICROS_PER_DAY=24*60*60*1000*1000;
    final long MILLIS_PER_DAY=24*60*60*1000;
    System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
  }
}

问题产生的原因:MICROS_PER_DAY的计算溢出了。虽然计算结果适合放入到long中,但是这个乘积并不适合放入int中。这个计算完全是以int运算来执行的,并且只有在运算完成之后,其结果才被提升为long.而此时已经太迟:计算已经溢出。从int提升为long是一种拓宽原生类型转换,他保留了不正确的数值。这个值之后被MILLIS_PER_DAY整除,而MILLIS_PER_DAY的计算是正确的,因为它适合int运算。

java不具有目标确定类型的特性,这时一种语言特性,其含义是指存储结果的变量类型会影响计算所使用的类型。

修正:

public class LongDivision {
  public static void main(String[] args) {
    final long MICROS_PER_DAY=24L*60*60*1000*1000;
    final long MILLIS_PER_DAY=24L*60*60*1000;
    System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
  }
}

当在操作很大的数字时,千万要防止溢出。即使用来保存结果的变量已经足够大,也不意味着要产生结果的计算具有正确的类型。

初级问题

在long类型字面量中,一定要用大写的L,千万不要用小写的l。

十六进制的趣事

public class JoyOfHex {
  public static void main(String[] args) {
    System.out.println(Long.toHexString(0x100000000L +0xcafebbe));
  }
}


十进制字面量都是正的,而十六进制或是八进制字面量并不具备这个属性。如果十六进制和八进制字面量的最高为被置位了,所以它是一个负数。该程序执行的加法是混合类型的计算:左操作数是long类型,而右操作数是int。为了执行计算,java将int类型的数值用拓宽原生类型转换提升为long类型,然后对两个long类型数值相加。因为int是有符号的整数类型,所以这个转换执行的符号拓展:它将负的int类型数值提升为一个在数值上相等的long类型数值,然后对两个long类型数值相加。因为int是有符号的整数类型,所以这个转换执行的是符号拓展:它将负的int类型数值提升为一个在数值上相等的long类型数值。

解决方法:

避免混合类型的计算。

java原生类型

java中,数据类型分为基本数据类型和引用类型。java不是纯面向对象的语言,不纯的地方就是这些基本数据类型不是对象

  • 基本类型的存储空间。byte-8位,short-16位,int-32位,long-64位,float-32位,double-64位。这六种数字类型都是有符号的。固定的存储空间正是java可移植性、跨平台的原因之一。

  • 使用公式-2的(位数-1)次幂到2的(位数-1)次幂-1确定整数类型的范围(byte、short、int、long)

  • char是16位Unicode字符或者是16位无符号整数,范围从0到65535。即便如此,可以强制转换非法的数据,如char c1=(chr)10000; char c2=(char)-200

  • 整数有八进制(以0开头的整数)、十进制、十六进制(以0x或0X开头的整数)表示。

  • char可以用单引号表示单个字符,如‘符’。也可以用unicode值‘ucafe’(四位十六进制数)。

  • 默认的浮点类型是双精度(double),要想一个float必须在浮点数后面加F或者f。如:float pi =3.14;是错误的。

  • 默认的整数类型是int型,要想使用长整型可在后面加“l”或“L”,如:1000L。

  • float可以精确到7位有效数字,第8位的数字时第9位数字四舍五入取得的;double可以精确到16位有效数字,第17位的数字时第18位数字四舍五入上取得的。

  • java为所有的成员变量提供了默认初始化:byte\short\int\long-0 \float-0.0f \double-0.0\boolean-false\char-‘u0000’,特别地对象应用全被初始化为null。

  • 基本类型之间的转化。java的类型检查很严格,从低精度转化到高精度是不需要显式转换的,doule d=123;但是反过来,进行窄化转换,由高精度向低精度转换,则必须使用强制类型转化。

浮点型转化为整型时,不进行四舍五入,直接截断小数点后面的数。

  • 各种基本数据类型进行混合运算,结果会是表达力最强的那种。如int和long运算,结果是long,整型和浮点型运算,结果是浮点型。特殊的一点是:只要类型比int小,(如char\byte\short),那么在运算之前会自动地转换成int.

  • 浮点类型的柯旭表示法。在数学中e代表自然对数(Math.E给出了double值),在java中e代表10的次幂。

多重转型

java对象的转型
  • 向上转型

将子类对象转为父类对象。此处父类独享可以使接口。弗雷的引用指向子类对象,多态。

注意:向上转型是,父类指向子类引用对象会遗失除与父类对象共享的其他方法,也就是在转型过程中,子类的新加的方法都会遗失掉,在编译时,系统会提示找不到方法的错误。

向下转型::父类引用的对象转换为子类类型称为向下转型。这种情况分为两种类型:

  1. 如果父类引用的对象时子类对象,那么在向下转型的过程是安全的。
Animal animal = new Bird();
Bird bird =(Bird)animal;

  1. 如果父类引用的对象不是子类对象,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现Java.Lang.ClassCastException错误。它可以使用instanceof 来判断父类引用是否是指向子类对象。
Animal animal = new Animal();
Bird bird = (Bird) animal;
bird.eat();//java.lang.ClassCastException(不安全的向下转型,编译无错但会运行会出错)

Animal animal = new Animal();
if(animal instanceof Bird){
    Bird bird = (Bird) animal;
    bird.eat();
}


基础数据类型的转换

1.byte的转型方法:

//byte注意事项 byte字段在进行运算的时候发生转型,
// 这个时候不能再将byte运算结果直接赋值给byte字段了,需要强制类型转换才可以。

    byte b1=1;
    byte b2=2;   
    //byte b3 =b1+b2;//编译不通过
    byte b4 =(byte)(b1+b2);
    int i1=10;
    //byte b5 = i1;//编译不通过
    byte b6=20;
    int i2=b6;
    System.out.println(i2);

  1. short类型的转换方法和byte类似,如果直接在short类型上进行相互运算就会发生编译不通过的情形了。

    short s1=1;
    short s2=2;
    //short s3=s1+s2;//编译不通过
    short s4=(short)(s1+s2);

  1. long类型的转换就完全可以相互运算了。
    long l1=1;  
    long l2=2;  
    long l3=l1+l2;  
    long l4=1+2; 
    System.out.println(l3);
    System.out.println(l4);

4.并不是所有的类型都可以互相转换的,有些类型差别太大转换会编译不通过或者运行出错的。

范围大的转为范围小的可以不需要强制类型转换就可以完成,反之范围小的转换为范围大,因为会发生精度损失,所以必须要强制类型转换才可以通过,否则编译不通过。

但是类型之间转换也是要看字段属性是否相同。例如int和double就可以进行相互转换,可是int和String就不可以了。

String str ="str";

//int i = (int)str;//编译不通过

//这里有个特例就是Objec的强制类型转换,因为Object是一个高度抽象的类型,所以可以强制转换为各种类型,编译都可以通过的,可是如果类型不符合,在运行的时候会发生挫折。

Object o = "str";
int i =(int)o;

  1. 数字字符串转换成相应的数据类型。
   int i =Integer.valueOf("1");
    float f=Float.valueOf("1.1");
    double d=Double.valueOf("1.23");
    
    String strI=String.valueOf(1);
    String strf=String.valueOf(1.0f);
    String strd=String.valueOf(1.22d);
    
    //更简单的方法
    String strIi=1+"";
    String strff=1.0f+"";
    String strDd=1.23d+"";

6.类型转换过程中精度损失案例:

  int i = 1;
    float f1 = 1.3f;
    float f2 = 1.7f;

    double d1 = 1.4d;
    double d2 = 1.8d;
    double d3 = 2.0;

    int i1 = (int) f1;
    int i2 = (int) f2;

    int i3 = (int) d1;
    int i4 = (int) d2;
    int i5 = (int) d3;

    float ff1 = i;
    float ff2 = (float) d1;
    double dd1 = i;
    double dd2 = f1;

    System.out.println("i1:" + i1);
    System.out.println("i2:" + i2);
    System.out.println("i3:" + i3);
    System.out.println("i4:" + i4);
    System.out.println("i5:" + i5);
    System.out.println("ff1:" + ff1);
    System.out.println("ff2:" + ff2);
    System.out.println("dd1:" + dd1);
    System.out.println("dd2:" + dd2);

    // 从结果可以看出在转型的过程中如果是int类型的话,在转化为float或者double都是变成(数值.0)的存在。
    // 如果是float或者double转换为int的时候都是浮点型前面的整数部分给int,浮点型小树后面的精度损失掉了。
    // 如果是float和double进行互相转换的话就会发生很奇怪的事情,就是例如1.3f变成1.2999999523162842d了
    // 所以最好不要再float和double类型上面直接进行互相转换,如果想要原值转换最好是利用字符串来进行转换。
    // 例如下面将给出的这个例子:

    float f = 1.23f;
    double dd11, dd22;
    String strF = f + "";
    dd11 = Double.valueOf(strF);
    dd22 = f;
    System.out.println("f:" + f);
    System.out.println("dd11:" + dd11);
    System.out.println("dd22:" + dd22);

7 . 字符和其他数据类型转换过程:

字符转换时有字符参与运算,当有字符参与运算时,系统首先回去找到对应的ASCII码值,然后在参与运算,如’a’+6=103 注意:int类型是可以赋值给char类型的,如char a=(char)97;

char c=(char)97;
int i='a';
system.out.println("c:"+c);
system.out.println("i:"+i);

内容来自《java解惑》 作者:布洛赫


上一篇 linux学习之zk

下一篇 一致性Hash

评论