基本类型,或者叫做内置类型,是JAVA中不同于类的特殊类型。它们是我们编程中使用最频繁的类型。java是一种强类型语言,第一次申明变量必须说明数据类型,第一次变量赋值称为变量的初始化。

一、 Java的简单类型及其封装器类

Java基本类型共有八种,基本类型可以分为三类,字符类型char,布尔类型boolean以及数值类型byte、short、int、long(整数型)float、double(浮点型)。

java 基本类型和String类型相加 string java基本数据类型_包装类

java 基本类型和String类型相加 string java基本数据类型_c#_02

Java基本类型存储在栈中,因此它们的存取速度要快于存储在堆中的对应包装类的实例对象。从Java5.0(1.5)开始,JAVA虚拟机(Java Virtual Machine)可以完成基本类型和它们对应包装类之间的自动转换。因此我们在赋值、参数传递以及数学运算的时候像使用基本类型一样使用它们的包装类,但这并不意味着你可以通过基本类型调用它们的包装类才具有的方法。另外,所有基本类型(包括void)的包装类都使用了final修饰,因此我们无法继承它们扩展新的类,也无法重写它们的任何方法。

  • 基本类型的优势:数据存储相对简单,运算效率比较高
  • 包装类的优势:有的容易,比如集合的元素必须是对象类型,满足了java一切皆是对象的思想

二、Java中的常量(必须放进常量池)

十六进制整型常量:以十六进制表示时,需以0x或0X开头,如0xff,0X9A。
八进制整型常量:八进制必须以0开头,如0123,034。
长整型:长整型必须以L作结尾,如9L,342L。
浮点数常量:由于小数常量的默认类型是double型,所以float类型的后面一定要加f(F)。同样带小数的变量默认为double类型。
如:float f;
f=1.3f;//必须声明f。
字符常量:字符型常量需用两个单引号括起来(注意字符串常量是用两个双引号括起来)。Java中的字符占两个字节。一些常用的转义字符:
①\r表示接受键盘输入,相当于按下了回车键;
②\n表示换行;
③\t表示制表符,相当于Table键;
④\b表示退格键,相当于Back Space键;
⑤’表示单引号;
⑥’'表示双引号;
⑦\表示一个斜杠\。

三、Java引用类型

Java有 5种引用类型(对象类型):类 接口 数组 枚举 标注 引用类型:底层结构和基本类型差别较大
JVM的内存空间
(1). Heap 堆空间:分配对象 new Student()
(2). Stack 栈空间:临时变量 Student stu
(3).Code 代码区 :类的定义,静态资源 Student.class

Student stu = new Student(); //new 在内存的堆空间创建对象
stu.study(); //把对象的地址赋给stu引用变量

上例实现步骤:

  • a.JVM加载Student.class 到Code区
  • b.new Student()在堆空间分配空间并创建一个Student实例
  • c.将此实例的地址赋值给引用stu, 栈空间

四、Java中的数据转换

A.简单类型数据间的转换,有两种方式:自动转换和强制转换,通常发生在表达式中或方法的参数传递时。
1.自动转换
具体地讲,当一个较"小"数据与一个较"大"的数据一起运算时,系统将自动将"小"数据转换成"大"数据,再进行运算。而在方法调用时,实际参数较"小",而被调用的方法的形式参数数据又较"大"时(若有匹配的,当然会直接调用匹配的方法),系统也将自动将"小"数据转换成"大"数据,再进行方法的调用,自然,对于多个同名的重载方法,会转换成最"接近"的"大"数据并进行调用。这些类型由"小"到"大"分别为 (byte,short,char)--int--long--float—double。这里我们所说的"大"与"小",并不是指占用字节的多少,而是指表示值的范围的大小

  • 下面的语句可以在Java中直接通过:
byte b;int i=b; long l=b; float f=b; double d=b;
  • 如果低级类型为char型,向高级类型(整型)转换时,会转换为对应ASCII码值,例如
char c='c'; int i=c;
System.out.println("output:"+i);输出:output:99;
  • 对于byte,short,char三种类型而言,他们是平级的,因此不能相互自动转换,可以使用下述的强制类型转换。
short i=99 ; char c=(char)i; System.out.println("output:"+c);输出:output:c;

2.强制转换
将"大"数据转换为"小"数据时,你可以使用强制类型转换。即你必须采用下面这种语句格式:

int n=(int)3.14159/2;

可以想象,这种转换肯定可能会导致溢出或精度的下降。

B.表达式的数据类型自动提升, 关于类型的自动提升,注意下面的规则。

  • 所有的byte,short,char型的值将被提升为int型;
  • 如果有一个操作数是long型,计算结果是long型;
  • 如果有一个操作数是float型,计算结果是float型;
  • 如果有一个操作数是double型,计算结果是double型;
byte b; b=3; b=(byte)(b*3);//必须声明byte。

C.包装类过渡类型转换
一般情况下,我们首先声明一个变量,然后生成一个对应的包装类,就可以利用包装类的各种方法进行类型转换了。例如:

  • 当希望把float型转换为double型时:
float f1=100.00f;
Float F1=new Float(f1);
double d1=F1.doubleValue();//F1.doubleValue()为Float类的返回double值型的方法
  • 当希望把double型转换为int型时:
double d1=100.00;
Double D1=new Double(d1);
int i1=D1.intValue();
  • 简单类型的变量转换为相应的包装类,可以利用包装类的构造函数。即:
Boolean(boolean value)、Character(char value)、Integer(int value)、
Long(long value)、Float(float value)、Double(double value)

而在各个包装类中,总有形为××Value()的方法,来得到其对应的简单类型数据。利用这种方法,也可以实现不同数值型变量间的转换,例如,对于一个双精度实型类,intValue()可以得到其对应的整型变量,而doubleValue()可以得到其对应的双精度实型变量。

D.字符串与其它类型间的转换
1.其它类型向字符串的转换

  • 调用类的串转换方法:X.toString();
  • 自动转换:X+"";
  • 使用String的方法:String.volueOf(X);

2.字符串作为值,向其它类型的转换

  • 先转换成相应的封装器实例,再调用对应的方法转换成其它类型
//字符中"32.1"转换double型的值的格式为:
new Float("32.1").doubleValue()。
也可以用:Double.valueOf("32.1").doubleValue()
  • 静态parseXXX方法
String s = "1";
byte b = Byte.parseByte( s );
short t = Short.parseShort( s );
int i = Integer.parseInt( s );
long l = Long.parseLong( s );
Float f = Float.parseFloat( s )
Double d = Double.parseDouble( s );
  • Character的getNumericValue(char ch)方法

E.Date类与其它数据类型的相互转换

整型和Date类之间并不存在直接的对应关系,只是你可以使用int型为分别表示年、月、日、时、分、秒,这样就在两者之间建立了一个对应关系,在作这种转换时,你可以使用Date类构造函数的三种形式:

Date(int year, int month, int date):以int型表示年、月、日
Date(int year, int month, int date, int hrs, int min):以int型表示年、月、日、时、分
Date(int year, int month, int date, int hrs, int min, int sec):以int型表示年、月、日、时、分、秒

在长整型和Date类之间有一个很有趣的对应关系,就是将一个时间表示为距离格林尼治标准时间1970年1月1日0时0分0秒的毫秒数。对于这种对应关系,Date类也有其相应的构造函数:Date(long date)。
获取Date类中的年、月、日、时、分、秒以及星期你可以使用Date类的getYear()、getMonth()、getDate()、getHours()、getMinutes()、getSeconds()、getDay()方法,你也可以将其理解为将Date类转换成int。
而Date类的getTime()方法可以得到我们前面所说的一个时间对应的长整型数,与包装类一样,Date类也有一个toString()方法可以将其转换为String类
有时我们希望得到Date的特定格式,例如20020324,我们可以使用以下方法

//首先在文件开始引入
import java.text.SimpleDateFormat;
import java.util.*;
java.util.Date date = new java.util.Date();
//如果希望得到YYYYMMDD的格式
SimpleDateFormat sy1=new SimpleDateFormat("yyyyMMDD");
String dateFormat=sy1.format(date);
//如果希望分开得到年,月,日
SimpleDateFormat sy=new SimpleDateFormat("yyyy");
SimpleDateFormat sm=new SimpleDateFormat("MM");
SimpleDateFormat sd=new SimpleDateFormat("dd");
String syear=sy.format(date);
String smon=sm.format(date);
String sday=sd.format(date);

F.总结:只有boolean不参与数据类型的转换

  • 自动类型的转换:
a.常数在表数范围内是能够自动类型转换的
b.数据范围小的能够自动数据类型大的转换
 (注意特例)int到float,long到float,long到double 是不会自动转换的,不然将会丢失精度
c.引用类型能够自动转换为父类的
d.基本类型和它们包装类型是能够互相转换的
  • 强制类型转换:用圆括号括起来目标类型,置于变量前

五、String类

String是java的 一个类,而不是基本数据类型,以下是关于String的几个面试题:

  • 例1:String类型的st1和st2是否相等
public class MyString {
   public static void main(String[] args) {
     String st1 = "abc";
     String st2 = "abc";
     System.out.println(st1 == st2);//True
     System.out.println(st1.equals(st2)); //True
   }
}

解释:在Java中==这个符号是比较运算符,它可以基本数据类型和引用数据类型是否相等,如果是基本数据类型,==比较的是值是否相等,如果是引用数据类型,==比较的是两个对象的内存地址是否相等。而equals是Object这个父类的方法,在String类中重写了这个equals方法。

java 基本类型和String类型相加 string java基本数据类型_常量池_03


内存大致流程:

1)运行先编译,然后当前类MyString.class文件加载进入内存的方法区
2)第二步,main方法压入栈内存
3)常量池创建一个“abc”对象,产生一个内存地址
4)然后把“abc”内存地址赋值给main方法里的成员变量st1,这个时候st1根据内存地址,指向了常量池中的“abc”。
5)前面一篇提到,常量池有这个特点,如果发现已经存在,就不在创建重复的对象
6)运行到代码 Stringst2 =”abc”, 由于常量池存在“abc”,所以不会再创建,直接把“abc”内存地址赋值给了st2
7)最后st1和st2都指向了内存中同一个地址,所以两者是完全相同的。
  • 例2:下面这句话在内存中创建了几个对象
String st1 = new String(“abc”);

内存中创建两个对象,一个在堆内存,一个在常量池堆内存对象是常量池对象的一个拷贝副本

java 基本类型和String类型相加 string java基本数据类型_bc_04


new出来的对象都是存储在堆内存。那为什么堆中对象是常量池的对象的拷贝副本。“abc”属于字符串,字符串属于常量,所以应该在常量池中创建,所以第一个创建的对象就是在常量池里的“abc”。第二个对象在堆内存为啥是一个拷贝的副本呢,这个就需要在JDK API 1.6找到String(String original)这个构造方法的注释:初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。所以,答案就出来了,两个对象

  • 例3:以下定义为String类型的st1和st2是否相等
public class MyString {
   public static void main(String[] args) {
     String st1 = new String("abc");
     String st2 = "abc";
     System.out.println(st1 == st2);//比较内存地址Flase
     System.out.println(st1.equals(st2));//比较值True
   }
}

java 基本类型和String类型相加 string java基本数据类型_java_05

  • 例4:以下定义为String类型的st1和st2是否相等
public class MyString {
   public static void main(String[] args) {
     String st1 = "a" + "b" + "c";
     String st2 = "abc";
     System.out.println(st1 == st2);//true
     System.out.println(st1.equals(st2));//true
   }
}

解释:“a”,”b”,”c”三个本来就是字符串常量,进行+符号拼接之后变成了“abc”,“abc”本身就是字符串常量(Java中有常量优化机制),所以常量池立马会创建一个“abc”的字符串常量对象,在进行st2=”abc”,这个时候,常量池存在“abc”,所以不再创建。所以,不管比较内存地址还是比较字符串序列,都相等。

  • 例5:以下st2和st3是否相等
public class MyString {
   public static void main(String[] args) {
     String st1 = "ab";
     String st2 = "abc";
     String st3 = st1 + "c";
     System.out.println(st2 == st3);//false
     System.out.println(st2.equals(st3));//true
   }
}

第二个是true不用解释,可第一个判断为什么是false?需要结合API的注释说明和内存图来解释这个为什么不相等。API的解释可以浓缩成:str1 与 "c" 进行字符串连接时,底层是通过StringBuffer 进行 append,生成一个StringBuffer对象,然后通过toString()方法,将StringBuffer对象转为String,此时会产生一个新的堆内存地址,str3指向这个新的内存地址。

java 基本类型和String类型相加 string java基本数据类型_c#_06


内存流程:

1.常量池创建“ab”对象,并赋值给st1,所以st1指向了“ab”
2.常量池创建“abc”对象,并赋值给st2,所以st2指向了“abc”
3.由于这里走的+的拼接方法,所以第三步是使用StringBuffer类的append方法,得到了“abc”,这个时候内存0x123表示的是一个StringBuffer对象,注意不是String对象。
4.调用了Object的toString方法把StringBuffer对象装换成了String对象。
5.把String对象(0x456)赋值给st3