整型类型:
类型名 | 取值范围 | 占用内存 |
---|---|---|
byte | -2^7 ~ (2^7) - 1 即 -128~127 | 1B |
short | -2^15 ~ (2^15) - 1 即 -32768~32767 | 2B |
int | -2^31 ~ (2^31) - 1 | 4B |
long | -2^63 ~ (2^63) - 1 | 8B |
在给long类型赋值时,如果常量超过了int的表示范围,需要在常量后面加大写或小写字母L,即L或1
因为数字常量默认为是int类型
类型名 | 取值范围 | 占用内存 |
---|---|---|
float | 1.4E-45 ~ 3.4E+38 -3.4E+38~-1.4E-45 | 4B |
double | 4.9E-324~1.7E+308 -1.7E+308~-4.9E-324 | 8B |
E表示以10为底的指数,E后面的+号和-号代表正指数和负指数,例如:14E-45表示14乘以10的-45次方
对于 double,直接把熟悉的小数表示赋值给变量即可
但对于float,需要在数字后面加大写字母F或小写字母f
这是由于小数常量默认是 double类型
1. int[] arr = {1,2,3}; 2. int[] arr = new int[]{1,2,3}; 3. int[] arr = new int[3]; //即使没有给每个元素赋值,每个元素也都有一个默认值, //这个默认值跟数组类型有关,数值类型的值为0, boolean为false,char为空字符 arr[0]=1; arr[1]=2; arr[2]=3;
注意:
数组长度虽然可以动态确定,但定了之后就不可以变 数组有一个 length属性,但只能读,不能改
不能在给定初始值的同时给定长度,即如下格式是***不允许***的:
int[] arr =new int[3]{1,2,3}
理解:因为初始值已经决定了长度,再给个长度,如果还不一致,计算机将无所适从
一个基本类型変量,内存中只会有一块对应的内存空间
但数组有两块:一块用于存储数组内容本身,另一块用于存储内容的位置
double d = 10/4;//结果是2,不是2.5 //正确写法 a) double d = 10/4.0; b) double d = 10/(double)4;
自增与自减
如果只是对自己操作,这两种形式也没什么差别,区别在于还有其他操作的时候 放在变量后
(a++)是先用原来的值进行其他操作,然后再对自己做修改,而放在变量前(++a)是先对自己做修
改,再用修改后的值进行其他操作
比较运算
对于数组,==判断的是两个变量指向的是不是同一个数组,而不是两个数组的元素内容是否一样,即使两个数组的内容是一样的,但如果是两个不同的数组,==依然会返回 false,如:
int[] a = new int[] {1,2,3}; int[] b = new int[] {1,2,3}; //a==b的结果是false
如果需要比较数组的内容是否一样,需要逐个比较里面存储的每个元素
逻辑运算
短路与(&&):和&类似,不同之处:&两边都计算,&&左边为假则右边的不计算
短路或(||):与|类似,不同之处同上
条件语句为tue,则执行括号{}中的代码,如果后面没有括号,则执行后面第一个分号(;)前的代码
if的陷阱:初学者有时会忘记在if后面的代码块中加括号,有时希望执行多条语句而没有加括号,结果只会执行第一条语句,建议所有if后面都加括号
if/else陷阱:需要注意的是,在 if/else中,判断的顺序是很重要的,后面的判断只有在前面的条件为 false的时候オ会执行
switch(表达式){ case 值1: 代码1; break; case 值2: 代码2; break; ... ... case 值n: 代码n; break; default:代码n+1 }
根据表达式的值找匹配的case,找到后执行后面的代码,碰到 breakl时结束,如果没有找到匹配的值则执行 default后的语句 表达式值的数据类型只能是byte、 short、int、char、枚举和 String(Java7以后)
简单总结下:
switch的转换和具体系统实现有关 如果分支比较少,可能会转换为跳转指令
如果分支比较多,使用条件跳转会进行很多次的比较运算,效率比较低,可能会使用一种更为高效的方式,叫跳转表 跳转表是一个映射表,存储了可能的值以及要跳转到的地址
跳转表高效的原因:因为其中的值必须为整数,且按大小顺序排序
switch表达式的数据类型限制的原因:
while
do-while:如果不管条件语句是什么,代码块都会至少执行一次,则可以使用do/ while循环
for
foreach
foreach不是一个关键字,它使用冒号:,冒号前面是循环中的每个元素,包括数据类型和变量名称,冒号后面是要遍历的数组或集合(第9章介绍),每次循环 element都会自动更新 对于不需要使用索引变量,只是简单遍历的情况, foreach语法上更为简洁
int[] arr = {1,2,3,4}; for(int element : arr){ System.out.println(element); }
public static void main(String[] args){ ... }
有两类特殊类型的参数:数组和可变长度的参数
数组作为参数与基本类型是不一样的,基本类型不会对调用者中的变量造成任何影响,但数组不是,在函数内修改数组中的元素会修改调用者中的数组内容
public static void reset(int[] arr){ for(int i=0;i<arr.length;i++){ arr[i] = i; } } public static void main(String[] args) { int[] arr = {10,20,30,40}; reset(arr); for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); } }
在reset函数内给参数数组元素赋值,在main函数中数组arr的值也会变
前面介绍的函数,参数个数都是固定的,但有时候可能希望参数个数不是固定的,比如求若干个数的最大值,可能是两个,也可能是多个。Java支持可变长度的参数
public static int max(int min, int ... a){ int max = min; for(int i=0;i<a.length;i++){ if(max<a[i]){ max = a[i]; } } return max; } public static void main(String[] args) { System.out.println(max(0)); System.out.println(max(0,2)); System.out.println(max(0,2,4)); System.out.println(max(0,2,4,5)); }
可变长度参数的语法是在数据类型后面加三个点“… ”,在函数内,可变长度参数可以看作是数组 可变长度参数必须是参数列表中的最后一个,一个函数也只能有一个可变长度的参数
可变长度参数实际上会转换为数组参数,也就是说,函数声明max(int min,int… a)实际上会转换为max(int min, int[] a),在main函数调用max(0,2,4,5)的时候,实际上会转换为调用max(0, new int[]{2,4,5}),使用可变长度参数主要是简化了代码书写
同一个类里,函数可以重名,但是参数不能完全一样,即要么参数个数不同,要么参数个数相同但至少有一个参数类型不一样 同一个类中函数名相同但参数不同的现象,一般称为函数重载 为什么需要函数重载呢?一般是因为函数想表达的意义是一样的,但参数个数或类型不一样
栈一般是从高位地址向低位地址扩展,换句话说,栈底的内存地址是最高的,栈顶的是最低的
针对基本数据类型,函数中的参数和函数内定义的变量,都分配在栈中,这些变量只有在函数被调用的时候才分配,而且在调用结束后就被释放了
public class ArrayMax { public static int max(int min, int[] arr) { int max = min; for(int a : arr){ if(a>max){ max = a; } } return max; } public static void main(String[] args) { int[] arr = new int[]{2,3,4}; int ret = max(0, arr); System.out.println(ret); } }
main函数新建了一个数组,然后调用函数max计算0和数组中元素的最大值,在程序执行到max函数的return语句之前的时候,内存中栈和堆的情况如图
对于数组和对象类型,它们都有两块内存,一块存放实际的内容,一块存放实际内容的地址,实际的内容空间一般不是分配在栈上的,而是分配在堆中,但存放地址的空间是分配在栈上的
存放地址的栈空间会随着入栈分配,出栈释放,但存放实际内容的堆空间不受影响 但说堆空间完全不受影响是不正确的,在这个例子中,当main函数执行结束,栈空间没有变量指向它的时候,Java系统会自动进行垃圾回收,从而释放这块空间
在Java中,可以使用如下代码查看Integer和Long的二进制、十六进制
System.out.println(Integer.toBinaryString(a)); //二进制 System.out.println(Integer.toHexString(a)); //十六进制 System.out.println(Long.toBinaryString(a));//二进制 System.out.println(Long.toHexString(a)); //十六进制
位运算有移位运算和逻辑运算 移位有以下几种
逻辑运算有以下几种
❑ 按位与&:两位都为1才为1。
❑ 按位或|:只要有一位为1,就为1。
❑ 按位取反~:1变为0,0变为1。
❑ 按位异或^:相异为真,相同为假
如果想查看浮点数的具体二进制形式,在Java中,可以使用如下代码:
Integer.toBinaryString(Float.floatToIntBits(value)) Long.toBinaryString(Double.doubleToLongBits(value));
为了保持与ASCII码的兼容性,一般都是将最高位设置为1。也就是说,当最高位为0时,表示ASCII码,当为1时就是各个国家自己的字符 在这些扩展的编码中,在西欧国家中流行的是ISO 8859-1和Windows-1252,在中国是GB2312、GBK、GB18030和Big5。
Unicode主要做了这么一件事,就是给所有字符分配了唯一数字编号 它并没有规定这个编号怎么对应到二进制表示,这是与上面介绍的其他编码不同的,其他编码都既规定了能表示哪些字符,又规定了每个字符对应的二进制是什么,而Unicode本身只规定了每个字符的数字编号是多少
那编号怎么对应到二进制表示呢?有多种方案,主要有UTF-32、UTF-16和UTF-8。
对于一个Unicode编号,具体怎么编码呢?首先将其看作整数,转化为二进制形式(去掉高位的0),然后将二进制位从右向左依次填入对应的二进制格式x中,填完后,如果对应的二进制格式还有没填的x,则设为0。
恢复乱码的方法
Java中处理字符串的类有String, String中有我们需要的两个重要方法
1)public byte[] getBytes(String charsetName),这个方法可以获取一个字符串的给定编码格式的二进制形式
2)public String(byte bytes[], String charsetName),这个构造方法以给定的二进制数组bytes按照编码格式charsetName解读为一个字符串
public static void recover(String str) throws UnsupportedEncodingException{ String[] charsets = new String[]{"windows-1252","GB18030","Big5","UTF-8"}; for(int i=0;i<charsets.length;i++){ for(int j=0;j<charsets.length;j++){ if(i!=j){ String s = new String(str.getBytes(charsets[i]),charsets[j]); System.out.println("---- ܻ原来的编码(A)假设是: " +charsets[j]+", 被错误解读为了(B): "+charsets[i]); System.out.println(s); System.out.println(); } } } }
在Java内部进行字符处理时,采用的都是Unicode,具体编码格式是UTF-16BE。
**char本质上是一个固定占用两个字节的无符号正整数,这个正整数对应于Unicode编号,用于表示那个Unicode编号对应的字符 **
由于固定占用两个字节,char只能表示Unicode编号在65 536以内的字符,而不能表示超出范围的字符 那超出范围的字符怎么表示呢?使用两个char。类Character、String有一些相关的方法
由于char本质上是一个整数,所以可以进行整数能做的一些运算,在进行运算时会被看作int,但由于char占两个字节,运算结果不能直接赋值给char类型,需要进行强制类型转换,这和byte、short参与整数运算是类似的 char类型的比较就是其Unicode编号的比较
查看char的二进制,用Integer方法:
char c = '马'; System.out.println(Integer.toBinaryString(c))