1、基本数据类型
Java提供8中基本数据类型,这些数据类型不是对象,这些基本类型的数据变量被声明后就会立刻在栈上被分配内存空间,其余的所以类型都是引用
Java中默认的整数是int类型,默认的小数是double类型
数据类型 | 字节长度 | 范围 | 默认值 | 包装类 |
boolean | 1 | true和false | false | Boolean |
byte | 1 | [-128,127] | 0 | Byte |
char | 2 | Unicode[0,65535] | u0000 | Character |
short | 2 | [-32768,32767] 万 5位的十进制数 | 0 | Short |
int | 4 | [-2147483648,2147483648] 十亿 10位的十进制数 | 0 | Integer |
long | 8 | [-9223372036854775808,9223372036854775807] 19位的十进制数 | 0L或0l | Long |
float | 4 | 32位单精度,小数点后6~7 | 0.0F或0.0f | Float |
double | 8 | 64位双精度,小数点后15~16位 | 0.0 | Double |
boolean的取值为true或false,理论上1bit就够了,考虑到字节对齐,占用一个字节,Java中的数值类型都是有符号的,不存在无符号的数,取值范围固定,不随硬件变化而变化(Java中还存在一种基本类型void和它的封装类型java.lang.void,但无法直接操作它)
原始类型与封装类型的区别:
- 原始类型的传递是值传递,封装类型的传递是引用传递
- 原始类型和封装类型作为某个类的实例数据时,默认的值不同,原始数据类型的默认值与具体类型有关,对象应用实例的默认值为null(null不是一个合法的Object实例,编译器不为其分配内存空间,仅仅用于表明该引用目前没有指向任何对象)
- 声明为基本类型时存储的是实际的值,声明为引用类型时存储的是实际对象的地址
2、不可变类
不可变类是指当创建了这个类的实例后,就不允许修改它的值了(一个对象一旦被创建出来,在其整个生命周期中,它的成员变量就不能被修改)
所有的包装类型都是不可变类,String类也是不可变类
String s = "Hello";//1
s +=" world";//2
- 语句1,声明了一个可以指向String类型对象的引用,这个引用的名字是s,指向一个字符串常量“Hello”,
- 语句2,并没有改变s所指向的对象,代码运行后s指向另一个String类型的对象,该对象的内容为“Hello
World”,原来的字符串常量“Hello”还存在内存中,并没有改变
创建一个不可变类需要遵循的原则:
- 类中所有成员变量被private所修饰
- 类中没有修改成员变量的方法,只提供构造函数,一次生成,永不改变
- 确保类中所有方法不会被派生类重写
- 如果一个类中成员不是不可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone()方法来确保类的不可变性
- 如果有必要可以重写Object类的equals和hashCode方法,在equals方法中,根据对象的属性值来比较两个对象是否相等,并确保用equals方法判断为相等的两个对象的hashCode方法返回值也相等
- 在创建对象时就需要初始化所有成员变了,最后提供带参数的构造函数来初始化
不可变类具有使用简单,线程安全,节省内存等有点,但是不可变类的对象会因为只的不同而产生新的对象,从而导致无法预料的问题
3、值传递和引用传递
值传递(原始数据类型):
在方法调用中,实参会把它的值传递给形参,形参只是用实参的值初始化一个临时的存储单元,因此形参和实参虽然有着相同的值,但是却有着不同的存储单元,因此对形参的改变不会影响实参的值
引用传递(包装类型或其他类型):
在方法调用中,传递的是对象(或者叫作对象的地址)这时候,形参和实参的对象指向同一块存储单元,因此形参的改变会影响实参的改变
不可变类的在引用传递时,可以理解为是按值传递;其实引用也是按值传递,只是传递的不是内容而是地址
4、数据类型的转换规则
当参与运算的2个变量数据类型不同时,就需要进行隐式的数据类型转换(从低精度向高精度转换)
- char类型的数据转换为高级类型时,会转换为其对应的ASCII码
- byte、char、short类型的数据在参与运算时会自动转换为int型,但是当使用+=时,不会产生自动类型转换
- 基本数据类型和boolean类型之间不能相互转换
- 高精度向低精度转换:byte、char、short之间需要强制类型转换
- 在进行强制类型转换的时候,会有精度的损失
- 在涉及byte、char、short类型的运算的时候,先要把它们转换为int型,然后进行计算,但是使用+=的时候,不会向int转换
5、Math类中round、ceil和floor方法
Math是一个包含很多数学常量与计算放的类,类中的方法都是静态的
round方法:四舍五入,原理是在原来数字的基础上加上0.5然后向下取整(int)Math.floor(x+0.5f)
floor方法:向下取整,Math.floor(a),取小于a的最大整数,返回类型是double;a是正数则把小数舍去,a是负数则把小数进位
ceil方法:向上取整,Math.ceil(a),取大于a的最小整数,返回类型是double;a是正数则把小数进位,a是负数则把小时舍去
6、++i与i++的区别
i++是在程序执行完毕后进行自增,++i是在程序开始前先进行自增
int i = 1;
i+++i++;//1+2=3,此时i=3
i+++++i;//3+5=8,此时i=5
i+++i+++i++;//5+6+7=18,此时i=8
7、数字的移位
‘>>’:有符号数右移运算,正数,高位补0;负数,高位补1
‘>>>’:无符号数右移运算,无论正负高位补0
- 对byte、char、short进行操作时,都会先自动的转换为int型
- int占32位,当右移运算超过32位时,会首先对32取模,然后在进行位移运算
- 负数在计算机中用补码表示(原码转换为补码:符号位不变,其他位取反后加1)
‘<<’:左右n位,表示原来的数值乘2的n次方,移除高位的同时在地位补0
8、char型变量中存储汉字
在Java中,默认使用Unicode编码方式,每个字符占用2个字节,因此可以用来存储中文汉字
英文占用1个字符,中文占用2个字符;因此可以使用使用字符串的长度和占用的字节数来区分是否存储了中文
英文:str.length() = str.getByte()
中文:2 * str.length() = str.getByte()
9、字符串创建和存储机制
字符传的声明和初始化:
- String s1 = new String(“abc”);String s2 = new String(“abc”);存在2个引用对象,内容相同,但是在内存中的地址不同
- String s1 = “abc”;String s2 = “abc”;在JVM中存在一个字符串常量池,保存这很多的String对象,并且可以共享,此处的s1和s2引用的是同一个常量池中的对象
10、==、equals和hashCode的区别
“==”:用来比较两个变量的值是否相等,即比较变量对应的内存中所存储的数值是否同(比较2个基本数据类型,或2个引用变量是否相等时使用)
equals():是Object类提供的方法,在Object中,equals()方法直接使用“==”运算符,因此在比较2个对象的内容是否相等时,要重写equals()方法
hashCode():从Object类中继承而来,Object类的hashCode()方法返回对象在内存中的地址转换成的int值,如果没有重写hashCode()方法,那么每个对象调用hashCode()方法都是不相等的
hashCode()方法相当于一个对象的编码,类似于文件的md5
一般在重写equals的时候,同时需要重写hashCode
如果x.equals(y)为true,那么x.hashCode()==y.hashCode();
如果x.equals(y)为false,那么x.hashCode()可能等于y.hashCode(),也可能不相等;
如果x.hashCode()==y.hashCode(),那么x.equals(y)为true;
如果x.hashCode()!=y.hashCode(),那么x.equals(y)可能为true也可能为false
11、String、StringBuffer、StringBuilder和StringTokenizer
Java中有4个类,可以对字符串或者字符进行操作:Character(单个字符)、String(字符串,不可变类)、StringBuffer(字符串,可变类)、StringTokenizer
在实例化的时候,String有2种初始化方式,StringBuffer只能通过构造函数;
StringBuilder和StringBuffer类似,都是字符串缓冲区,但是StringBuilder不是线程安全的,在单线程中使用,效率最高,多线程中使用StringBuffer,是线程安全的
StringTokenizer是用来分割字符串的工具类,java默认的分隔符是“空格”、“制表符(‘\t’)”、“换行符(‘\n’)”、“回车符(‘\r’)”
12、数组
数组是指具有相同类型的数据的集合,一般具有固定长度,并且在内存中占据连续的空间
在Java中数组是对象,有自己的属性(length),也有一些可以被调用的方法(clone),每个数组类型都有其对应的类型,通过instanceof来判断数据的类型;数组被创建后会根据数组的数据类型初始化成对应的初始值
数组的初始化方式:
声明一维数组:type[] arrayName 或 type arrayName[]
声明二维数组:type[][] arrayName 、type[] arrayName[]、 type arrayName [][]
- int[] a = new int[5];
- int[] a = {1,2,3,4,5};
- int a;a=int[]{1,2,3,4,5}; a = new int[5];
- int[][] b = new int[行数][列数]
二维数组中第二维的长度可以不同
length属性和length()方法的区别:
数组提供length属性来获得数组的长度,length()方法是针对字符串而言的
计算对象大小的时候使用size()方法,该方法是针对泛型集合而言的