JAVA 语法(2、数据类型)

JAVA 语法(2、数据类型)

一、数据类型

1.分类

注意:字符串不属于基本数据类型,属于引用数据类型;字符属于基本数据类型

//字符
char name = '国'
//字符串
String namea = '国家'

2.基本数据类型的字节数及表示范围

  • 2.1、 整型变量默认为 int 类型

    byte a = 100; 
    short s = 1000; 
    int a = 100000; 
    long a = 100000L;   // 错误写法:long a = 100000; 
    

    因为整型变量默认为 int 类型,前三行默认int 型,且int会隐含强制转换为低级别的 byte 和 short,

    但转换为 long 型只能显式强制转换,所以必须写为 long a = 100000L; 或者 long a = (long)100000;

  • 2.2、浮点数的默认类型为 double 类型

    float f1 = 234.5f;
    double d1 = 123.4;
    

3、基本数据类型对应的包装类

Java 是面向对象的语言,但是为了便于开发者的使用,Java 中却沿用了 C 语言的基本数据类型,在进行基本的数据计算时,开发者可以直接使用基础类。但是基本数据类型是不具备对象的特征的,不能调用方法,而且基本数据类型不能存入集合中,所以就需要将基础数据类型实例封装为 Java 对象,使其具有了对象的属性和方法。而且原始类型不能为 null,但包装类可以 为null。包装类也可用于实现多态性。

  • 3.1、基本类型与包装类的区别

    ①存储位置不同:
    基本数据类型直接将值放在栈中;
    包装类型是把对象放在堆中,然后通过对象的引用来调用他们 ;
    ②初始值不同:
    int的初始值为 0 、 boolean的初始值为false ;
    包装类型的初始值为null ;
    ③使用方式不同:
    基本数据类型直接赋值使用就好;
    在集合如 coolectionMap 中只能使用包装类型;

  • 3.2、基本类型与包装类的相互转换

    • 3.2.1 手动转换

      ● 基本数据类型 → 包装类:

      通过对应包装类的构造方法实现,除了Character外,其他包装类都可以传入一个字符串参数构建包装类对象。

      ● 包装类 → 基本数据类型

      通过包装类的实例方法 xxxValue() 实现; // xxx表示包装类对应的基本数据类型

    • 3.2.2 自动装箱&自动拆箱(jdk1.5以后)

      基本类型添加到集合中时,进行自动装箱
      包装类型在涉及到运算的时候,“加,减,乘, 除” 以及 “比较 equals,compareTo”,进行自动拆箱

4、数据在内存中的存储位置

4.1、static final 修饰的常量

存放在常量池中,JDK1.8以前在方法区,JDK1.8及以后在堆中。

4.2、方法中声明的局部变量

​ 在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束。

​ ① 当声明的是基本类型的变量时,其变量名及值(变量名及值是两个概念)是放在方法栈中;
​ ② 当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆内存中。

4.3、类中声明的成员变量

● 在类中声明的没有用 static 修饰的变量是成员变量,也叫全局变量,放在堆中的(因为全局变量不会随着某个方法执行结束而销毁)。

​ ① 当声明的是基本类型的变量时,其变量名及其值放在堆内存中的
​ ② 当声明的是引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象,引用变量名和对应的对象仍然存储在相应的堆中。

● 类中的成员变量用 static 修饰是静态成员变量,存储在方法区。

二、常数

1、十六进制

十六进制表示必须以 0x 或 0X 开头,如0xff、0X9A。

2、八进制

八进制表示必须以 0 开头,如0123、034。

3、十进制

(1)整数

在java中,整数常量默认是 int 类型

  • 1、long 类型

    • 若数值没有超出 int 范围,可以直接赋值给 long 类型,发生自动向上转换( int —> long)。
    long num = 123;  // ok,注意123默认是int类型,发生自动转换
    long num = 99999999999999;  //  error,99999999999999默认是int类型,超出了int类型,所以还没赋值就出错了
    long num = 99999999999999L; //  ok,99999999999999L被L修饰,不能默认是int类型,已经是long类型,可以直接赋值
    
  • 2、byte、short 和 char 类型( int —> byte、short 和、char 需要向下转换 )

    • 若数值没有超出 byte、short 和 char 类型的范围,可以直接赋值给该类型

       byte d = 135 ;       //  error
       byte d = 3 + 5 ;     //  ok
       byte d = a +b ;       //  error,a 和 b 都是变量。编译器在编译期间无法得知变量相加的值
      
    • 若数值超出byte、short 和 char 类型范围,赋值给该类型必须强制转换

      byte d = 135 ;       //  error
      byte d = (byte)135 ;     //  ok
      

​ 我们一般向下转型的时候,会产生丢失进度的问题。但是当我们使用的整形常量位于 -128~127 不超过 byte 类型范围时,将其赋值给 byte 类型变量是不会产生这样的错误的。

(2)浮点数

​ 浮点数常量的默认类型是 double 型

​ ① double 类型

​ 浮点数常量的默认类型是 double 型,因此可以直接赋值。

double num = 1.23;  // ok

​ ② float 类型

​ 浮点数赋值给 float 类型的变量必须要用 F(f) 修饰,或者强制类型转换。

float num = 1.23F;  // ok,1.23F 被F修饰,不能默认是double类型,已经是float类型,可以直接赋值,不发生向下转换
float num = (float)1.23;  // ok
(3)字符类型

​ Java 的字符型常量值是用单引号引起来的一个字符,如 ‘e’、E’。

​ 注:双引号用来表示字符串,像 “11”、“d” 等都是表示单个字符的字符串。

​ Java 还允许使用一种特殊形式的字符常量值来表示一些难以用一般字符表示的字符,这种特殊形式的字符是以 ‘\’
开头的字符序列,称为转义字符。

(4)布尔类型

​ Java 的布尔型常量只有两个值,即 false(假)和 true(真)。

​ 注:在Java中,不能用 0 代表 false(假),非零代表 true(真)。

三、数据类型之间的转换

	在Java中,整型、实型和字符型被视为简单数据类型,这些类型由低级到高级分别为
	(byte、short、char)—> int —> long —> folat —> double。
	简单数据类型之间的转换又可以分为:
	低级到高级的自动类型转换、高级到低级的强制类型转换、包装类过渡类型转换,通常发生在表达式中或方法的参数传递时

1、自动类型转换(运算、传参)

  • 当一个较低级数据与一个较高级的数据一起运算时,系统将自动将低级数据转换成高级数据,再进行运算

    • 如果低级类型为char型,向高级类型(整型)转换时,会转换为对应ASCII码值

    • char c='c';  
      int i=c; 
      System.out.println("output:"+i);  //输出:output:99; 
      
  • 在方法调用时,低级实参赋值给高级形参时系统将自动将低级数据转换成高级数据,再进行方法的调用。对于多个同名的重载方法,会转换成最"接近"的高级数据并进行调用

    • 对于 byte、short 和 char 三种类型而言,他们是平级的,因此不能相互自动转换,可以使用强制类型转换

    • byte i=99 ;  
      short s=(short)i; 
      char c=(char)i;
      

2、强制类型转换

  • 将高级变量转换为低级变量时,需要用到强制类型转换,这种转换可能导致溢出或精度的下降

3、隐含强制类型转换(初始化)

  • 在变量的初始化时,整数的默认类型是 int。byte b = 123; 123 默认是 int 类型,他会隐含强制转换为低级别的 byte 和 short 类型,所以不用显示强制转换;但是 int 类型只能显式强制转换为 long 型,所以 long a = 123L;
  • 浮点型不存在这种情况,因为在定义 float 类型时必须在数字后面跟上 F 或者 f。

4、包装类型与基本类型的转换

  • 使用包装类的构造函数将基本类型的变量手动装箱为相应的包装类

  • Boolean(boolean value)、 Character(char value)、 Integer(int value)、 Long(long value)、 Float(float value)、 Double(double value)

    int i = 2;//定义int类型变量,值为2
    Integer m = new Integer(value: 5);//定义interger包装类对象,值为5
    
  • 使用的包装类静态方法 valueOf() 将基本类型的变量手动装箱为相应的包装类

    Integer i = Integer.valueOf(1);     // 手动装箱
    

    注意:valueOf() 内部是用的 new 方法来构造对象的

    public static Double valueOf(double d) {
     return new Double(d);
    }
    
  • 调用包装类中 ××Value() 的实例方法将包装类型手动拆箱为任意类型的简单类型数据

    以 Interger 包装类为例,Interger 包装类的所有方法:

    利用这种方法,也可以实现不同数值型变量间的转换,例如,对于一个双精度实型类,intValue() 可以得到其对应的整型变量,而 doubleValue() 可以得到其对应的双精度实型变量。

  • 自动装箱和拆箱

    在 JDK1.5 引入自动装箱和拆箱的机制后,包装类和基本类型之间的转换就更加轻松便利了。
    1)自动装箱:把基本类型转换成包装类,使其具有对象的性质,又可分为手动装箱和自动装箱。

    自动装箱实际上自动调用了 valueOf()。

    2)自动拆箱:和装箱相反,把包装类对象转换成基本类型的值,又可分为手动拆箱和自动拆箱

    自动拆箱实际上自动调用了 xxxValue()。

public class HelloWorld {
    public static void main(String[] args) {
        // 定义double类型变量
        double a = 91.5;
        // 手动装箱
        Double b = new Double(a);
        // 自动装箱
        Double c = a;
        System.out.println("装箱后的结果为:" + b + "和" + c);
        // 定义一个Double包装类对象,值为8
        Double d = new Double(87.0);
        // 手动拆箱
        double e = d.doubleValue();
        // 自动拆箱
        double f = d;
        System.out.println("拆箱后的结果为:" + e + "和" + f);
    }
}

5、包装类型、基本类型与字符串的转换

(1)包装类型向字符串的转换

调用包装类的串转换方法:X.toString()

//字符串"32.1"转换double型的值的格式为:
String str = "32.1";
Double d = new Float("32.1");     
String str1 = d.toString();

(2)字符串向包装类型的转换

构造函数、parseXxx 静态方法或 valueOf() 方法

//字符串"32.1"转换double型的值的格式为:
String str = "32.1";
Double d = new Float("32.1");
Double d = Double.parseDouble("32.1");  
Double d = Double.valueOf("32.1"); 

(3)基本类型向字符串的转换

①调用包装类的串转换方法:X.toString();

②自动转换:X+"";

③使用 String 的方法:String.volueOf(X);

(4)字符串向基本类型的转换

① 先转换成相应的包装类实例(构造函数、parseXxx 静态方法或 valueOf() 方法),再进行拆箱(手动、自动)

//字符串"32.1"转换double型的值的格式为:
String str = "32.1";
double d = new Float("32.1").doubleValue();     // 手动拆箱
double d = new Float("32.1");     // 自动拆箱
double d = Double.parseDouble("32.1").doubleValue();    // 手动拆箱
double d = Double.parseDouble("32.1");    // 自动拆箱
double d = Double.valueOf("32.1").doubleValue();    // 手动拆箱
double d = Double.valueOf("32.1");    // 自动拆箱 

② Character 的 getNumericValue(char ch) 方法

四、基本类型与包装类型的比较

1、equals

equals 方法比较的是真正的值。
(1)对于基本类型,由于不是对象,所以不存在地址,更没有equals等方法,故此只有使用“==”来进行值比较
(2) 2个包装类通过 equals 比较,比较的是包装的基本数据类型的值;基本数据类型和包装类通过 equals 比较时,会先把基本数据类型自动装箱包装成对应的包装类型,再进行比较。

double i0 = 0.1;
Double i1 = new Double(0.1);
Double i2 = new Double(0.1);

System.out.println(i1.equals(i2));  // true: 2个包装类比较,比较的是包装的基本数据类型的值
System.out.println(i1.equals(i0));  // true: 基本数据类型和包装类型比较时,会先把基本数据类型包装后再比较

阿里巴巴 Java 开发手册:

【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。

说明:对于Integer var = ? 在-128 至 127范围内的赋值,Integer对象是在 IntegerCache.cache 产生,会复用己有对象,这个区间内的 Integer 值可以直接使用 == 进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用己有对象,这是一个大坑,推荐使用 equals 方法进行判断。

2、==

对于基本数据类型,== (双等号)比较的是值;
对于包装类型,==(双等号)比较的则是 2 个对象的内存地址。
基本数据类型和包装类型通过 == 比较时,会先把包装类自动拆箱再进行值比较(和 equals 是反的)

double i0 = 0.1;
Double i1 = new Double(0.1);
Double i2 = new Double(0.1);
System.out.println(i1 == i2);    // false: new 出来的都是新的对象
System.out.println(Integer.valueOf(i0) ==Integer.valueOf(i0));   // false:valueOf() 内部是用的 new 方法来构造对象的
System.out.println(i1 == i0);    // true: 基本数据类型和包装类比较,会先把包装类拆箱

3、><

(1)在 Java 中,“>” 和 “<” 只能用来判断两个基本数据类型的数值大小关系;
(2)对于包装类型,必须先用 xxxValue() 方法将其手动拆箱为基本类型再进行比较。

五、包装类的缓存

Java 对部分经常使用的数据采用缓存技术,在包装类第一次被加载时会创建缓存,当使用等值对象时直接从缓存中获取,从而提高了程序执行性能。(通常只对常用数据进行缓存)。

(1)只有静态方法 valueOf() 方法装箱构造的包装类对象时会用到缓存,valueOf() 方法用到了缓存池;

(2)new 方法装箱构造的包装类对象不会使用缓存!

valueOf() 方法的源码:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];     //如果在缓存范围内,直接获取缓存中的地址
    return new Integer(i);                                      //否则新建一个对象
}

各个包装类型的缓存范围:

(1)valueOf(x) 方法可能会直接从缓存中去取被缓存的同一个对象,所以用 == 比较时可能会为 true。如果 valueOf(x) 中的 x 是缓存范围外的值,那么就会 new 一个新的对象出来,所以用 == 比较的结果一定是 false。
(2)因为 Float 和 Double 没有缓存,所以两个 Float 或 Double 通过 == 比较时,结果永远是 false。

举例:

Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);    // false:new 方法装箱构造的包装类对象不会使用缓存
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k);   // true:静态方法 valueOf() 方法装箱构造的包装类对象时会用到缓存
Integer m = Integer.valueOf(189);
Integer n = Integer.valueOf(189);
System.out.println(m == n);   // false: 189 大于缓存池的最大值127 因此每次都会新建一个对象 他们的地址不同 
Double p = Double.valueOf(189);
Double q = Double.valueOf(189);
System.out.println(p == q);   // false: Double 没有缓存

六、基本类和包装类在实际使用时的原则

1. 尽量使用 values 方法。最大可能使用缓存,提高程序的效率。

2. 类变量使用包装类。想象有一个和数据库表对应的实体类,如果你使用的基本数据类型,在插入时,可能会插入一些让你意想不到的初始值。

3. 方法的参数要使用包装类。使用包装类意味着你在调用时,可以令若干个参数为 null。null 是无意义的。但是如果你使用了基本数据类型,那么,你将不得不传入一个值,即使这个值对你来说,没有什么意义。

4. 方法的返回值要根据是否可为 null 来确定使用包装类还是基本类。当一个方法的返回值,一定不会出现 null 的情况时,推荐使用基本类来作为返回值,这样调用者在拿到这个方法的返回值时,就不必担心它是为 null 了;若返回值可能会出现 null 的情况时,推荐使用包装类来作为返回值。

5. 方法内部(局部变量)使用基本类型。基本类型的时间效率和效率上来说,都是要优于包装类的。所以,在方法内部,能使用基本类型尽量不要使用包装类。

6. 小数的计算。严格来说,这条不属于基本类型和包装类的内容,但是,这里顺便提交,当涉及到小数的计算时,要考虑到计算的精度问题,可以使用 BigDecimal,也可以通过缩小计量单位,达到化零为整的目的,这个,根据具体场景来确定。

posted @ 2022-05-07 17:12  k19  阅读(156)  评论(0)    收藏  举报