Java编程的逻辑——编程基础和二进制
2021/6/8 20:25:37
本文主要是介绍Java编程的逻辑——编程基础和二进制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
编程基础和二进制
数据类型和变量
基本类型
-
整型类型:
类型名 取值范围 占用内存 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类型
- 真假类型boolean:true/false
- 字符类型char,占两个字节,用单引号
数组
- 赋值方式
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
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以后)
简单总结下:
- 单一条件满足时,执行某操作使用if;
- 根据一个条件是否满足执行不同分支使用 if/else;
- 表达复杂的条件使用if/else;
- 条件赋值使用三元运算符
- 根据某个表达式的值不同执行不同的分支使用 switch
- 从逻辑上讲, if/else、if/ else if/else、三元运算符、 switchi都可以只用if代替,但使用不同的语法表达更简洁,在条件比较多的时侯, switch从性能上看也更高
swich性能高的原因
switch的转换和具体系统实现有关 如果分支比较少,可能会转换为跳转指令
如果分支比较多,使用条件跳转会进行很多次的比较运算,效率比较低,可能会使用一种更为高效的方式,叫跳转表 跳转表是一个映射表,存储了可能的值以及要跳转到的地址
跳转表高效的原因:因为其中的值必须为整数,且按大小顺序排序
- 按大小排序的整数可以使用高效的二分査找
- 如果值是连续的,则跳转表还会进行特殊优化,优化为一个数组,连找都不用找了,值就是数组的下标索引,直接根据值就可以找到跳转的地址
- 即使值不是连续的,但数字比较密集,差的不多,编译器也可能会优化为一个数组型的跳转表,没有的值指向 default分支
- 程序源代码中的case值排列不要求是排序的,编译器会自动排序
switch表达式的数据类型限制的原因:
- 其中byte/ short/int本来就是整数,char本质上也是整数
- 不可以使用long,为什么呢?跳转表值的存储空间一般为32位,容纳不下long
- 而枚举类型也有对应的整数
- String用于 switche时也会转换为整数
- String是通过hash Code方法转换为整数的,但不同 Stringl的 hashCode可能相同,跳转后会再次根据 String的内容进行比较判断
循环的4中形式
-
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); }
循环控制
- break:用于提前结束循环
- continue:跳过循环体中剩下的代码,然后执行步进操作
函数
public static void main(String[] args){ ... }
- main函数:表示程序的入口
- String[] args 表示从控制台接受到的参数
- Java中运行一个程序的时候,需要指定一个定义了main函数的类,Java会寻找main函数,并从main函数开始执行
- 定义函数时声明参数,实际上就是定义变量,只是这些变量的值是未知的,调用函数时传递参数,实际上就是给函数中的变量赋值
- 对于需要重复执行的代码,可以定义函数,然后在需要的地方调用,这样可以减少重复代码 对于复杂的操作,可以将操作分为多个函数,会使得代码更加易读
参数传递
有两类特殊类型的参数:数组和可变长度的参数
数组
数组作为参数与基本类型是不一样的,基本类型不会对调用者中的变量造成任何影响,但数组不是,在函数内修改数组中的元素会修改调用者中的数组内容
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的值也会变
- 数组变量有两块空间,一块用于存储数组内容本身,另一块用于存储内容的位置,给数组变量赋值不会影响原有的数组内容本身,而只会让数组变量指向一个不同的数组内容空间
- 在上例中,函数参数中的数组变量arr和main函数中的数组变量arr存储的都是相同的位置,而数组内容本身只有一份数据,所以,在reset中修改数组元素内容和在main中修改是完全一样的
可变长度的参数
前面介绍的函数,参数个数都是固定的,但有时候可能希望参数个数不是固定的,比如求若干个数的最大值,可能是两个,也可能是多个。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)); //十六进制
位移
位运算有移位运算和逻辑运算 移位有以下几种
- 左移:操作符为<<,向左移动,右边的低位补0,高位的就舍弃掉了,将二进制看作整数,左移1位就相当于乘以2。
- 无符号右移:操作符为>>>,向右移动,右边的舍弃掉,左边补0。
- 有符号右移:操作符为>>,向右移动,右边的舍弃掉,左边补什么取决于原来最高位是什么,原来是1就补1,原来是0就补0,将二进制看作整数,右移1位相当于除以2。
逻辑运算有以下几种
❑ 按位与&:两位都为1才为1。
❑ 按位或|:只要有一位为1,就为1。
❑ 按位取反~:1变为0,0变为1。
❑ 按位异或^:相异为真,相同为假
浮点数
如果想查看浮点数的具体二进制形式,在Java中,可以使用如下代码:
Integer.toBinaryString(Float.floatToIntBits(value)) Long.toBinaryString(Double.doubleToLongBits(value));
字符的编码与乱码
常见非Unicode编码
为了保持与ASCII码的兼容性,一般都是将最高位设置为1。也就是说,当最高位为0时,表示ASCII码,当为1时就是各个国家自己的字符 在这些扩展的编码中,在西欧国家中流行的是ISO 8859-1和Windows-1252,在中国是GB2312、GBK、GB18030和Big5。
Unicode编码
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(); } } } }
char的真正含义
在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))
这篇关于Java编程的逻辑——编程基础和二进制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-06JS面试真题详解:新手必备的JavaScript面试指南
- 2024-11-06JavaScript大厂面试真题详解与实战指南
- 2024-11-05安全渗透学习入门指南
- 2024-11-05内存马学习:从入门到实践
- 2024-11-05初学者指南:渗透攻防学习入门教程
- 2024-11-05渗透技术学习入门指南
- 2024-11-05数据库服务漏洞学习指南
- 2024-11-05网络安全学习:新手入门指南
- 2024-11-05Web开发入门指南
- 2024-11-05初学者指南:理解和防范跨域漏洞