#1编程基础#
##1.1数据类型和变量##
基本数据类型
1.整数类型:4种(byte/short/int/long)
2.小数类型:double/float
3.字符类型:char,表示单个字符
4.真假类型:boolean
5.声明变量其实是在内存中分配一块空间
##1.2赋值##
变量声明之后,就在内存中分配了一块位置,赋值就是给位置内容一个确定的值
1.基本类型
1.整数类型
byte/short/int/long,分别占1,2,4,8个字节
/long a = 3232343433L/数字默认是int类型,需要加L才能赋值给long
2.小数类型
float/double,分别占4字节和8字节
/float f = 333.33F/小数默认是double类型,需要加F才能赋值给float
3.真假类型
loolean;直接利用true或者false赋值
4.字符类型
char,占用2字节
2.数组类型
三种赋值形式
int [] arr = {1,2,3};
int [] arr = new int []{1,2,3};
int [] arr = new int [3];
arr[0] = 1;arr[1] = 2;arr[2] = 3;
虽然没有先给赋值,但每个元素会有个默认值,可能为 0,true,char空字符串等
动态确定长度
int length = 4;
int arr = new int [lenght];
不能赋值的同时给定长度,防止不一致无法匹配
数组类型和基本类型不同,
一个基本类型变量,内存中只会有一块对应的内存空间
一个数组中有两块,一块用来存储数据本身,另一块用来存储内容的位置
数组赋值本质是让变量指向另一个不同的位置
##1.3基本运算##
1.算数运算
1.运算时候注意超出范围
2.小数计算结果不精确
3.自增自减
a++;先对原来值进行操作,然后再对自己进行修改
++a;先对自己进行修改,再用修改后的值进行其他操作
2.比较运算
==比较数组时是判断两个变量是否指向同一个数组,而不是数组元素是否相等;比较内容需要逐个比较
3.逻辑运算
与或非
异或(^)相同为false
短路与(&&)与&类似
短路或(||)与|类似
单个的 | 和 & 不会影响后边的判断
&& 和 || 前边判断成功会短路不进行后边的判断
##1.4条件执行##
1.语法
- if else
- 三元运算符int max = x > y ? x : y;true返回1表达式,false返回2表达式
- switch case
case 值1: 代码1;break; case 值2: 代码2;break; ... case 值n: 代码n;break; default:代码n+1; } ``` **2实现原理** <hr> 程序最终都是一条一条的指令,cpu指令指示器指向下一条要执行的指令并执行。大部分指令都是顺序执行,但是有<strong>跳转指令</strong>会跳转到指定位置执行。分为<STRONG>条件跳转</STRONG>和**非条件跳转** if/else利用到了条件跳转和非条件跳转 switch分支较多,条件跳转效率低,使用**跳转表** 跳转表是一个映射表,储存了可能的值及跳转的地址 跳转表很高效的原因是其中的值时按大小排序的,可以使用二分查找,甚至连续时可以直接利用数组下标 源码中的case值排列不要排序,编译器会自动排序 当case值为char string等时也会转换为整数
1.5循环
java中循环有四种形式
while(和if很像)
do/while(不管条件语句是什么都要执行一次)
for(循环次数已知时候使用 注:for中每条语句都可以是空的,可以省略某些语句 for(;;)这个语句是可运行的,一个死循环
foreach(变量1:数组或集合)变量1接收每个元素,用于简短遍历时语法简洁
循环控制
- break(用于提前跳出循环)
- continue(跳过循环体中剩余代码,但是不会跳出循环)
实现原理
与if一样,也是利用条件跳转和无条件跳转指令实现
1.6函数的用法
某一操作需要运行多遍的时候利用函数来减少重复代码和分解复杂操作
函数概念
由输入,操作,输出组成
修饰符 返回值类型 函数名字 (参数类型 参数名字,…) {
操作;
return 返回值;
}
java中任何一个函数都要放在类中
public static void main(String[] args){
...
}
这个函数是主函数,是程序入口
String[] args 表示从控制台接收到的参数参数传递
(1)数组
基本类型参数在函数中不会对调用着中变量产生影响,但数组会产生影响。数组传入后是共用的一个内存块,函数中修改的也是同一块内存内容。
(2)可变长度参数
public static int max(int min,int … a){…}
max(1,2,3,4,3)
在实际使用中,函数声明实际上会转换成数组max(int min,int[] a)
调用时也会转换为数组max(1,new int[] {2,3,4,4})
函数返回
return(可用于函数中的任何地方,void类型也可用return,但是不用带值。函数的返回值最多只有一个,需要多个返回结果时候可以利用数组)
重复的命名
不同类中是可以重复的
同一个类中,名字可以重复,但是参数类型或者参数个数要不同
调用的匹配过程
形参和实参类型需要匹配,但不要求完全一样,java编译器会自动进行类型转换并匹配最适合的函数,比如char型实参可以放入int类型形参的函数中
递归函数
自己调用自己,然后依次回退,递归开销极大,慎用!
##函数调用的基本原理##
栈
执行过程就是cpu的跳转指令
在函数运行过程中有数据传递的问题
解决方式是利用内存来存放这些数据,存放这些数据的内存叫做栈
执行原理
函数中的参数和函数内定义的变量,都分配在栈中,函数调用时分配,调用结束后释放,但这只针对基本数据类型
数组和对象的内存分配
数组合对象都拥有两块内存,一块存放实际的内容,一块存放实际内容的地址
因此,java中实际的内容空间一般不是分配在栈上,而是分配在堆
存放地址的空间是分配在栈上的
对于数组,栈中只存放实际内容的地址,地址会随着入栈分配,出栈释放,但堆是不会收影响的。但是当栈中没有变量指向堆的时候,java系统会自动进行垃圾回收
递归调用的原理
栈不断加深,一直到返回值存储器中有了值然后再逐个返回
#2数据背后的二进制#
##1整数的二进制表示与位运算##
正整数的二进制表示
与10进制类似,每个位置有0,1两个数字
负整数的二进制表示
十进制利用-表示
二进制类似,使用最高位(即最左边的一位)表示符号位,利用1表示是负数
但是与我们直觉 原码表示法 不同,二进制负数利用补码表示法
补码就是在原码表示的基础上取反然后加1
负数的二进制就是利用对应正数的补码表示
-2:2的原码00000010,取反:11111101,再加1:11111110
注:负数的补码表示做补码运算可以得到对应正数的原码(有负负得正的效果)
计算机只能做加法1-1实际是1+(-1)用原码表示结果是错误的。
在我们进行正数运算时可能会出现负数,当计算结果超过表示范围时,一般最高位都是1,就会被计算机理解为负数
十六进制
二进制写起来太长,就将4位二进制转换为一个0~15的数字
0~15分别表示 为0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
程序中书写16进制时要在数字前加(0x)如:int a = 0x7b;
java7之前不支持直接写二进制,java7开始支持,在数字前加(0b)
如:int a = 0b11001;
java中可以方便的查看二进制和十六进制
二进制:Integer.toBinaryString(数字);或Long.toBinaryString(数字);
十六进制:Integer.toHexString(数字);或Long.toHexString(数字);
位运算
移位运算
- 左移:操作符<<向左移动,右边的低位补0,高位舍弃掉,(左移一位相当于乘2)
- 无符号右移:操作符为>>>,向右移动,右边的舍弃掉,左边补0
- 有符号右移:操作符为>>,向右移动,右边的舍弃,左边与原来最高位相同的(右移一位相当于除以2)
逻辑运算
- 按位与&:两位都为1才是1
- 按位或|:只要有一位为1,就为1
- 按位取反~:1变0,0变1
- 按位亦或^:不同为1,相同为0
##2小数的二进制表示##
小数计算为什么会出错
float f = 0.1f*0.1f;
System.out.println(f);
输出结果不是0.01,而是0.010000001
实际上,运算本身没有错,但是计算机无法精确的表示很多数
二进制格式不能精确的表示0.1,只能使用接近0.1但有不是0.1的数,类似于十进制无法表示1/3,二进制只能表示那些可以是2的多少次方和的数(如2(-1)=0.5,2(-2)=0.25,2^(-4)=0.0625)
计算机中这种不精确是常态
大部分时候我们不需要那么高的精度,
真的需要精度很高时候一种方法是将小数转化为整数,运算结束后转化为小数,或者使用十进制的数据类型,但效率低下
二进制表示
float,double被我们称为浮点数,是因为在小数的二进制表示中,表示小数点的时候点不是固定不动的,而是浮动的
二进制中的小数类似于科学计数法,形如m×(2^e)。m称为尾数,e称为指数,指数可以为 正也可以为负。
二进制中,单独表示尾数部分和指数部分,另外还有一个符号位表示正负
几乎所有硬件和编程语言表示都是这样的,这个格式是一个叫做IEEE 754标准,两种格式32位和64位,对应java的float和double
32位格式中,1位表示符号,23为表示尾数,8为位表示指数
64位中,1位表示符号,52位表示尾数,11位表示指数
想要查看浮点数的二进制形式可使用如下代码
Integer.toBinaryString(Float.floatToIntBits(value));
Long.toBinaryString(Double.doubleToLongBits(value));
##3字符的编码与乱码##
常见的非Unicode编码
1、ASCII
计算机发明之初没考虑太多,只考虑了美国的需求,就规定了128个字符的二进制表示方法,这个标准称为ASCII编码American Standard Code for Information Interchange(美国信息互换标准代码)
128刚好可以用7位字符表示,计算机最小存储单位byte是8位,ASCII中最高位设为0,规定了0~127每个数字代表的含义。
ASCII码对美国够用,但是对其他国家不够,其他国家计算机厂商在发明了各自的编码,并且一般都是将最高位设为1,为了与之区别
2、ISO 8859-1(Latin-1)
也是使用一个字节表示一个字符,0127与ASCII相同,128159表示一些控制字符,160~255表示一些西欧字符
3、Windows-1252
ISO 8859-1号称标准,用于西欧国家,但是连欧元€符号都没有,标准早于欧元,所以 实际中更多用Windows-1252编码,与ISO 8859-1基本一样,128~159有部分区别
很多程序中即使文件声明采用的是ISO 8859-1编码,但解析时候还是会被当做Windows-1252编码
HTML5中明确规定,文件声明是ISO 8859-1编码,应该被看做Windows-1252编码
4、GB2312
中文的第一个标准是GB2312,主要针对简体中文中的常见字符,包括约7000个汉字和罕用词和繁体字。
GB2312固定使用两个字节表示汉字,在两个字节中最高位都是1,如果最高位是0被认为是ASCII字符
最高位字节范围0xA10xF7,低位字节范围0xA10xFE
5、GBK
建立在GB2312的基础上,向下兼容GB2312,多增加了14000多个汉字
高位字节范围是0x810xFE,低位字节范围0x400x7E和0x80~0xFE
6、GB18030
向下兼容GBK,增加了55000多个字符,包括部分少数民族和日韩统一字符。
利用到了边长编码,有的字符是两个字节,有的字符是四个字节。两字节编码与GBK一样,四字节中,第一个字节值0x810xFE,第二个字节值0x300x39,第三个字节值0x810xFE,第四个字节值0x300x39,解析时候通过第二个字节的范围判断是四个字节还是两个字节
7、Big5
是针对繁体中文的,包括了13000多个繁体和GB2312类似,使用两个字节表示
高位字节范围0x810xFE,低位字节范围0x400x7E和0xA1~0xFE
Unicode编码
Unicode给世界上所有的字符都分配了一个唯一的数字编码,这个编码范围从0x000000~0x10FFFF,包括了110多万。
每个字符都有一个Unicode编号,这个编码一般写成十六进制,在前边加U+。范围U+4E00~U+9FFF
编号对应到二进制表示的时候主要有UTF-32、UTF-16、UTF-8
UTF-32
就是字符编号的整数二进制形式,4个字节。
字节的排列顺序:如果第一个字节是整数二进制中的最高位,最后一个字节是整数二进制中的最低位,那么这种字节序叫做大端BE,否则,就叫小端LE,对应的编码方式分别是UTF-32BE和UTF-32LE,每个字符都用4个字节表示 ,浪费
UTF-16
使用变长字节表示:编码在U+0000~U+FFFF中直接用两个字节表示
编码在U+10000~U+10FFFF的字符,需要用4个字节表示,前两个字节叫做高代理项,后两个字节叫低代理项
判断是两个字节还是四个字节表示看前两个字节的编号范围
UTF-8
使用变长字节表示,每个字符使用的字节个数与其Unicode编号大小有关,编号小的使用的字节就少,编号 大的使用的字节就大,使用1~4个字节不等
UTF-8兼容ASCII
中文字符大多数占三个字节
编码转换
不同编码直接可以借助UTF-8进行编码转换,相当于每个编码都有一个映射表,有特定的对应关系
编码转换只改变了字符的二进制内容,但是并没有改变字符看上去的样子
乱码的原因
解析错误
例如:Windows-1252写的文件发送给了利用GB18030来解析文件,就会产生乱码
错误的解析和编码转换
如果怎么改变查看方式都不对,那么可能在错误解析的基础上还进行了编码转换
从乱码中恢复
发生了一次错误的编码转换,恢复要恢复两个关键信息,一个是原来二进制编码方式A,和错误解读的编码方式B。然后尝试逆向操作
可以尝试利用UItraEdit编辑器查看编码并修改
利用java恢复乱码
public byte[] getBytes(String charsetName)这个方法可以获取一个字符串的给定编码格式的二进制。
public String (byte bytes[],String charsetName),这个构造方法以给定的二进制数组bytes按照编码格式charsetName解读为一个字符串
String str = "value"; String newStr = new String(str.getBytes("windows-1252"),"GB18030"); System.out.println(newStr);
在实际应用中可以写一个嵌套的循环,进行测试
不是所有乱码都是可以恢复的。
##4char的真正含义##
java内部进行字符处理的时候,采用的都是Unicode,具体编码格式是UTF-16
char本质上是一个固定占用两个字节的无符号正整数,这个正整数对应于Unicode编号,用于表示Unicode对应的字符,由于固定占两个字节,超出的部分会使用两个char
char有多种赋值行为
char c = 'A'; char c = '马'; char c = 39532; char c = 0x9a6c; char c = '\u9a6c';
第一种是最常见的将一个能用ASCII码表示的字符赋值给一个字符变量
第二种也常见,但这里是个中文字符
第三种是直接将十进制的常量赋值给字符,也是‘马’
第四种是将十六进制赋值给字符,‘马’
第五种赋值方式是直接按Unicode字符形式,‘马’
由于char本质是一个整数,所以可以进行一些运算,在运算时会被看做int
查看char的二进制和查看整数的相同
#类的基础#
java中,除了8种基本的数据类型,其他的类型的数据都需要用类来表达 类可以定义在一个类的内部 ##类的基本概念## **函数容器** >修饰符 public static private final等 >static表示类方法,也叫静态方法,与之相对应的是实例方法,需要通过实例或者对象调用,而类方法可以直接通过类名调用,不用创建实例 >通过private封装和隐藏内部实现细节,避免被误操作,是计算机程序的一种基本思维方式 >final在修饰变量的时候表示常量 >Math常用 函数 >`int round(float a)//四舍五入 >double sqrt(double a)//平方根 >double ceil(double a)//向上取整 >double floor(double a)//向下取整 >double pow(double a,double b)//a的b次方 >int abs(int a)//绝对值 >int max(int a,int b)//最大值 >double log(double a)//自然对数 >double random()//产生一个大于等于0小于1的随机数` >Arrays常用函数 >`void sort(int[] a)//排序,按升序排列 >void sort(double[] a)//排序按升序排列 >int binarySearch(long[] a,long key)//二分查找,数组已经按升序排列 >void fill(int[] a,int val)//给所有数组元素赋相同的值 >int[] copyOf(int[] original,int newLength)//数组复制 >boolean equals(char[] a,char[] a2)//判断两个数组是否相同`
自定义数据类型
一个数据类型由其包含的属性以及该类型可以进行的操作组成
一个数据类型由4部分组成
- 类型本身具有的属性,通过类变量实现
- 类型本身可以进行的操作,通过类方法实现
- 类型实例具有的属性,通过实例变量体现
- 类型实例可以进行的操作,通过实例方法体现
对于一个具体类型,不必要每一部分都有
类变量
类型本身具有的属性通过类变量体现,类变量static是必须有的
实例变量和实例方法
实例变量不能有static修饰符
实例方法可以直接访问实例变量 ,类方法不行
声明的实例变量一般和数组一样,有两块内存,由于本身不储存数据,所以都是引用类型变量
创建一个新的实例或对象:Point p;p= new Point();
系统会先进行内存分配,再给实例变量设置默认值
变量默认值
每个实例变量都有默认值,想要修改可以在定义的同时就赋值
或者使用初始化代码块
int x =1; int y; { y=2; }
代码块用{}包围
静态初始化代码块在{}前加static
构造方法
public Point(){ this(0,0); } public Point(int x,int y){ this.x = x; this y = y; }
构造方法名称固定,与类名相同
没有返回值
构造 方法可以重载
构造方法的使用:Point p = new Point(2,3);
默认构造方法
每个类都有默认方法,通过new调用,没有自己写构造方法时会调用默认的方法
私有构造方法
修饰符为private
1、不能创建类的实例,类只能被静态调用的时候
2、能创建类实例,但是只能被类的静态方法调用(单例-有实例,但是只有一个实例,如果已经被调用过了就重用,在单例场景时常用)
3、只是用来被其他多个构造方法调用,用于减少重复代码
类和对象的生命周期
new的时候或者直接通过类名访问类变量和类方法的时候java会为类开辟一片内存空间,包含各种 细节
类加载内存之后一般不释放,一直到程序结束
new创建对象时候对象产生。每个对象在保存值的同时还保存着类的地址,用来确定是哪个类
对象释放是java用垃圾回收机制管理的,一般不用我们操心,会 自己释放
和数组一样,存地址的在栈中 ,具体内容在堆中,堆也是java自动判断释放的