在开始叨叨数据类型之前,先花一分钟时间讲明白一样东西:拆箱和装箱。
很简单,一句话的事儿:基本类型转换成类类型叫做装箱,类类型转换成基本类型叫做拆箱。
至于为什么开始先说这个,因为后面会有用。
基本数据类型:int,float,double,char,byte。
当你这么写代码的时候,编译器永远会报错。试试看:
float f = 3.2;
是不是错的?当然是。为什么?
在Java中,小数类型默认double,整数则是int。刚才的3.2,类型实际上是double而不是float。
如果要对小数进行初始化,得在小数后加个f才行。
float f = 3.2f;
但是如果f是double类型呢?那就好办多了。不管你用下面哪种方法都OK。
double d = 3.2;
d = 3.2f;
这两行代码一点问题都没有。
想想也简单,先科普一下,float是4个字节,double是8个字节。
第一段代码里,float强制转化为double,用4个字节的东西去盛放8个字节的数据,当然瞬间爆炸。
而第二段代码里,4个字节的东西存放4个字节的数据,刚好。
第三段代码,8个字节的东西,不管是放8个字节的double还是4个字节的float,都放得下。
先说一个简单的例子,你猜下结果是多少。
Integer i = new Integer(10);
System.out.println(i);
估计你也猜到结果会等于10了。是不是和大学课堂上讲的不太一样?当然。
Java已经对基本的类型进行了装箱。比较常用的有
Integer,Float,Double,Character,Byte。
(哦,当然一般最常用的还是String,后面会扯到。)
像Integer就是对int类型的数据进行了装箱。
当然Java类型还支持自动装箱和拆箱。
简单点吧,两段代码就能告诉你我在说什么。
Integer i = new Integer(30); //手动装箱
Integer x = 30; //自动装箱
int n = i.intValue(); //手动拆箱
int m = i; //自动拆箱
这就是自动装箱拆箱和手动装箱拆箱。自己理解吧。
不过,好端端的int类型,干嘛要装箱?装箱以后有什么好处?那,下面就告诉你有什么好处。
一个最容易举出来的例子就是,装箱过后的值终于可以设置为空(null)啦。在编写一些代码的时候值为空特别有帮助。比如:
Integer i = null;
我们还可以轻易找出该类型(比如,int)下的最大值和最小值。妈妈再也不用担心我要死记硬背啦好伐。
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
最后,我们还可以做一些之前不敢想的事情!
不得不说Java封装的有用的东西太多了,以至于以前需要沉思良久的东西,现在只需一行。比如:
//将字符串类型转换成装箱好的整型,并且+1
System.out.println(Integer.parseInt("123")+1);
//整数直接转换成二进制
System.out.println(Integer.toBinaryString(123));
//整数直接转换成八进制
System.out.println(Integer.toOctalString(123));
//同理十六进制的代码肯定也有就是我懒
//等等等等
好了,数据类型到此结束,不叨叨了。next
初入Java提及字符串,总有一些需要考虑的经典问题。下面给一个例子:
String str1 = new String("abcdefg");
String str2 = new String("abcdefg");
System.out.println(str1==str2);
输出结果是……false。
str1和str2当然相等,但是为什么是false??
这就得提到Java有关指针的问题了。在学Java之前天天听学长们在那里说,Java大法好,退C++保平安……问他们为什么,理由千奇百怪什么理由都有,不过肯定少不了的一条就是,Java不需要指针。
Java真的没有指针么?
首先,Java有指针。没有不需要指针的语言。只不过Java把他们隐藏在代码里,而且藏得非常好。
那么,指针在哪里?str1,str2,其实就可以看作是一个放在堆栈(stack)中的指针。
比如刚才说的那个例子(终于绕回来了)。还记得之前学过的Java变量在内存中的布局方式吗?
如果忘了可以回去简单看一下,赶快回来哦,想起怎么回事就行了。
我们分析一下,在print之前,线程中存放了两个数据,一个是String str1,另一个就是String str2。
然后,在堆中的数据是这样分布的:String str1指向了一个它自己new出来的abcdefg对象,String str2也指向了一个它自己new出来的abcdefg对象。但是问题在于,这两个abcdefg并不是一个对象,他们是分别独立的整体。
所以说,这时候输出这句话
System.out.println(str1 == str2);
它的真实作用其实是比较两个引用变量,是否指向同一个字符串对象。
由于不是一个对象,所以,当然就false啦。
我们在这种情况下想比较str1,str2的值怎么办呢?这么写就OK啦。
System.out.println(str1.equals(str2));
这样最后会返回true的。
还是要先看一段例子:
String s1 = "xyz";
String s2 = "xyz";
System.out.println(s1==s2);
现在这行代码如果运行,输出的是true还是false?
答案应该是true。
但!是!我不是说过,如果不是一个对象的话,输出的会是false吗?
没错,所以s1和s2指向的,就是同一个对象啊。
好了,到这里就要提及一下Java所说的享元模式了。什么是享元模式?
首先,我们来分三次解析一下每行的代码吧。
首先第一行
String s1 = "xyz";
这行代码没有任何问题,s1在线程的栈中,放了一行代码String s1。
然后,在堆中放置了一个对象,new(“xyz”)。注意这里用到了前面讲过的自动装箱。所以,只需要写s1 = “xyz”,Java就会自动把堆栈中的s1指到新建的对象去。
然后第二行,这一行是重点
String s2 = "xyz";
这行代码有什么问题?也没任何问题。
首先s2在线程的栈中,放了一行代码String s2;
然后,在堆中什么都没放!这次Java会把s2指到之前s1指向的xyz!理由很简单,因为已经存在了一个xyz,没有必要再新建一个了。
最后第三行,询问s1和s2是否指向同一个对象?当然是了。
java字符串一旦定义,将不能被修改,没有必要创建多个相同的字符串对象,完全可以复用。
为了提高效率,降低字符串对象的构建数量,java默认会复用字符串对象。
这就是享元模式经典的体现之一,Java。