1.Java开发环境搭建
1.1搭建Java开发环境
下载JDK
1、选择适合自己操作系统的版本,并且一般来说在1.6以上的java版本。一般来说很多应用软件,包括hadoop都是需要1.6以上的java版本,这个版本比较完善。
2、下载相应版本,开始安装,安装完毕后,需要配置环境变量。环境变量的意思其实很简单。比如说在windows cmd窗口中我需要运行一个程序,那就执行xxx.exe,这个和我们用鼠标点击它的快捷方式是一样的。但是系统并不知道xxx.exe的执行路径情况下,系统会无法识别的,所以我们就要告诉系统执行这个命令的路径在哪里。环境变量就是配置执行程序命令的路径,告诉操作系统,命令可以在哪个路径下找到并且执行。例如path,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程。
配置路径
对于java来说,需要配置一个路径:
1、一个是path,这个路径是用于配置执行命令,路径包括两个,都是和bin目录相关的,即:C:\Program Files\Java\jdk1.5.0_08\bin\java.exe,C:\Program Files\Java\jdk1.5.0_08\jre\bin\java.exe,这两个目录和安装所在盘有关。
2、另一个是classpath,这个路径是java内置的类(class)所在的路径,一般用于编译。这个路径在jdk1.5版本以后就不需要配置的,其实就是jdk 的lib下的两个jar包。示例%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar。JAVA_HOME是java所在的主目录。目
备注:对于windows系统来说,是在我的电脑——管理——系统属性中的高级,来配置环境变量的,对于linux系统,是在/etc/profile中进行配置的,配置完之后,刷新这个文件,即source /etc/profile。如果该文件是只读的,修改时候执行:wq!即可用强制写入就行了。
2.简单的java程序
Java程序运行机制:
Java是一种特殊的高级语言,具有解释型和编译型语言的特征,因为java程序需要先编译后解释,只是编译时候编译的不是机器码而是特殊的字节码。然后由虚拟机来解释字节码。
编译型语言和解释型语言的区别
编译型语言指的是使用专门的编译器、针对特定平台(操作系统)将某种高级语言源代码翻译成该平台硬件可执行的机器码(包括机器指令和操作数),并包装成该平台能识别的可执行性程序格式。这个过程称为编译,编译生成的可执行性代码可以脱离开发环境,在特定平台上运行。
Java程序的从编写到运行
这一章主要给用户一个入门,介绍一个简单的java程序Hello World!最著名的一句了。通过这个简单java程序介绍java程序的运行的流程。即编码、编译、解释运行程序。3个步骤解释如下:
编码
这一步是编写java程序,可以用很多工具编写,编写完毕后后缀名以.java结束。例如Hello World!
1 class Hello{ 2 3 public static void main(String[] args){ 4 5 //输出时候,凡是字符串类的,都需要加上双引号的,对于定义变量时候,也是一样的 6 System.out.println("Hello World!"); 7 } 8 }
备注:编码时候有编码风格或者说编码规范,不同公司有不同的编码风格。编码风格有很多方面,在排版方面,本来我觉得一个代码块的{和对应的}要在同一列上对齐的,不知道为啥,大家编码都不放在一列,可能是为了紧凑考虑吧。那我也就跟着学了。还有一点,对于一个语句后面都需要加上大括号,包括if后面的子句,哪怕只有一句也需要加上大括号,这样显得逻辑清晰。另外对于一个类,整体也需要加上一个分号的,这样也显得结构清晰。
编译
编译这一步骤是将编写的源代码OOXX.java,编译成java虚拟机里能够执行的字节码,即OOXX.class。这种字节码据说是特别设计的,长度刚好能够与目前的计算机能够处理的字长兼容。注意:如果不是在eclipse中开发,用普通的记事本编写代码,编译时候,使用命令javac OOXX.java,注意要带上后缀名java。字节码和机器码是不一样的,机器码是用0和1表示的。
解释运行
java虚拟机执行编译后的类文件,OOXX.class。使用命令是java OOXX.class
Java程序的结构
对于一个java源文件来说,一个源程序包括两大部分(类以上级别的,即类和包的引用)。程序头 包的引用和类的定义,有时候也需要加上包名。例如
1 package abc 2 3 import java.util.* 4 5 class OOXX{ 6 7 }
注意:
除了正式的程序以外,还有程序的注释部分。
写程序一定要加上注释!!!!
写程序不加注释的人,小JJ和注释一样长!!!
注释有三种:
1、单行注释 //
2、多行注释/* ....*/
3、文档注释 /**...*/
3.Java数据类型与变量
3.1标识符概述
标识符是用于给包、类、方法、属性命名的符号,标识符需要用字母、数字、下划线_以及美元符号$组成。字母不限于英文字母,可以包含其他语种的字母。这四种标识符都不能以数字开头,不能讲关键字用作标识符。
关键字:一些特殊的字符串是java的关键字,例如true,boolean等,这些字符串有特殊的用途,不能用作变量。关键字都是小写的,大写的不算关键字,例如true是关键字,但是TRUE就不是,Java有48个关键字,除了48个关键字外,还有2个保留字,goto和const,这两个保留字不是关键字,但是未来可能成为关键字,目前也不能当做标识符。
Java是强类型的语言,意思是变量和表达式在编译之前就需要确定数据类型。所有的变量必须显式声明类型。强类型的语言在编译时候进行变量类型检查,可以减少编程错误
Java 标识符命名规则
包:所有字母小写,如ora.abc.pak
类:所有单词首字母大写
常量:所有单词的所有字母都大写
变量:第一个单词的首字母小写,其他单词首字母全部大写
方法:同变量,第一个单词的首字母小写,其他单词的首字母全部大写,但是因为方法后面无论有没有参数都是有括号的,所以变量和方法能够区分开。
3.2 变量
变量在程序设计中必须先定义再使用,定义后,系统才会分配给内存给它,在栈内存中分配内存储存变量地址,在堆内存中分配内存储存变量的内容。不定义变量,程序执行时候,鬼知道它从哪里冒出来的?先定义后使用。remember!
变量的命名:除了在一般的循环中可以简要定义一个无意义的循环变量(比如输出一个数组),其他时候定义变量名称时候,尽量有和实际对应的意义。并且大小写规则如下:所有单词的第一个字母小写,其他单词的首字母都需要大写。如studentName
常量的命名:所有字母都需要大写,如flag
关键字都是小写的
3.2.1 变量定义
type varname 即变量类型 变量名
关键字是每一种编程语言都有的,相当于语言的语法骨架部分,java的关键字有七七四十九个,有些关键字虽然列出来了,但是在实际使用时候是不使用的。
另外true,false,null虽然不是关键字,但也是特别的标识符,不能直接使用。true和false是boolean类型的两个指定的值,null则是初始化字符串、字符时候的默认值。
3.2.2变量的分类
变量类型可以分为基本数据类型和引用数据类型
基本数据类型和引用数据类型可以声明一个变量,并且对应的值是相应类型的范围内,但是这两种数据类型的值都不止传统定义的范围,比如引用这种数据类型,它的值除了传统范围的字符,还有null,即空的可能性,null是它的一种值,并不代表一种数据类型而是初始化值时候一种类型,null类型没有名称,所以不能声明一个null类型的变量或者将其他数据类型转换成null类型。但是值为null的数据类型可能转换成其他数据类型。
注意:值为null的类型只能是引用数据类型,不能转换成基本数据类型,或者说null只能是引用数据类型的值,也就是引用类型的变量不指向任何一个对象,也就是堆内存中间没有开辟空间
数值型 整型(byte、short 、int 、long) 浮点型(float double)
基本数据类型 布尔型 即boolean型,取值只有true和false两种。这个和int等数值型是一样的,只不过int取值可以无限
字符型 char
引用数据类型 类(class)和接口(interface)
数值型和字符型变量占用内存大小:
变量类型 占用内存大小(byte/字节) 占用内存大小(byte/字节) 变量类型
char 2byte或16bit(注意byte和bit的区别,1byte=16bit)
byte 1
short 2
int 4 ========== 4 float
long 8 ========== 8 double
boolean取值有两种,即true和false,由于每个字母是char,即2个字节,所以它占用的内存有8个字节和10个字节两种。
备注:
字节、字、位的区别
先区分字节和为的英文:字节byte 位:bit,又叫比特,所以通常我们说一个字节等于8位,这里的位是指等于8个比特位
字
在计算机中,一串数码作为一个整体来处理或运算的,称为一个计算机字,简称字。字通常分为若干个字节(每个字节一般是8位)。在存储器中,通常每个单元存储一个字,因此每个字都是可以寻址的。字的长度用位数来表示。
在计算机的运算器、控制器中,通常都是以字为单位进行传送的。字出现在不同的地址其含义是不相同。例如,送往控制器去的字是指令,而送往运算器去的字就是一个数。
字长
计算机的每个字所包含的位数称为字长。根据计算机的不同,字长有固定的和可变的两种。固定字长,即字长度不论什么情况都是固定不变的;可变字长,则在一定范围内,其长度是可变的。
计算的字长是指它一次可处理的二进制数字的数目。计算机处理数据的速率,自然和它一次能加工的位数以及进行运算的快慢有关。如果一台计算机的字长是另一台计算机的两倍,即使两台计算机的速度相同,在相同的时间内,前者能做的工作是后者的两倍。一般地,大型计算机的字长为32―64位,小型计算机为12―32位,而微型计算机为4一16位。字长是衡量计算机性能的一个重要因素。例如酷睿2双核处理器的字长是32位,也就是一次性可处理二进制数字长度最长有32位,超出了将无法处理,计算机可以装的系统是32位的,不能装64位的。
字节
字节是指一小组相邻的二进制数码。通常是8位作为一个字节。它是构成信息的一个小单位,并作为一个整体来参加操作,比字小,是构成字的单位。
在微型计算机中,通常用多少字节来表示存储器的存储容量。例如,在C++的数据类型表示中,通常 char为1个字节,int为4个字节,double为8个字节。
理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分:字符人们使用的记号,抽象意义上的一个符号。 '1', '中', 'a', '$', '¥' ……字节计算机中存储数据的单元,是一种长度概念,一个8位的二进制数,是一个很具体的存储空间。0x01, 0x45, 0xFA……
3.3基本数据类型
表示范围:以byte类型来看
byte类型是一个字节,也就是8位,byte类型表示的数是有符号的,也就是说数是有正有负的,数的第一位符号位,用0表示为正,用1表示为负
注意数的理论表示或者说数学表示与物理表示是并完全相符合的模式
数的物理表示的符号位与权限位是合一的,并且每一位与其他位都是不相干的。
比如说对于一个十进制数22,它的符号位在数学中是省略的+,但是在计算机中这个正号是不能省略的,需要用物理电路表示出来。22=2*10^1+2*2^0
在计算机用二进制,符号位也要用数来表示:
例如1000 0001:这里第一位1有两种含义,一种是符号位,一种是权限位,也就是(-1)*2^7+0*2^6...+1*2^0=-127,有符号位的表示中,第一位是很特别的一位。
另外还需要注意一点是数的物理表示是有数据溢出的,也就是是说不停的对一个数+1,总会超出它的表示范围,但是数学中的数是没有数据溢出这种说法的。
int数据类型是4个字节,和浮点数float也是4个字节。
注意:系统在处理整数型数据时候,对于较小的数(byte/int范围内的),会自动把数做byte或者int数据来进行处理例如:int a = 55;是没有问题的,但是对于较大的数(超过byte/int类型表示范围的),系统并不会把数自动处理为long或者更大范围表示的,需要指定类型,用L或l,建议用大写的L,因为小写的l容易与数字1混淆。在大型数据处理中,建议整数都用L进行处理,因为数据很容易超出int的表示范围,虽然int能表示很大的范围,但是在大数据面前,还是不够的
Java整数常量的表示方式有3种:8进制、10进制和16进制,计算机用2进制与物理电路的高低电位对应。计算机是用二进制进行计算的,不是我们平时用的十进制。因为用二进制表示一个数太麻烦,太长(例如64用二进制表示为1000000),于是人们就用八进制和十六进制表示数。由于c语言是高级语言,支持十进制,所以我们一般用的都是十进制的,很少用其他进制。但是计算机内部地址常用16进制表示,八进制在对变量进行移位操作等比较底层的程序设计是很有用的并且8进制是用于ACSIC码的如'\065'是表示A。
八进制:数以0开头,基数有0~7 共计8个数字
十六进制:数以0x或者0X开头,0~9,外加a~f 共计16个基数字
十六进制转为10进制:AA=10*16^1+10*16^0=170
一个数是由该数的基数和权限位共同决定的。同样的数符,不同的进制,表示的值是不一样(实际上上述表示两种进制的数是不科学的,没有带上进制符号,不完整)
037=3*8+7
0x37=55=6*8+7=067
字符型数据
字符型是指单个字符,字符型是属于基本数据类型的,而字符串类型的是属于引用数据类型的,java使用unicode 2个字节的编码方式来编码世界上的任何一种语言字符。字符型常量通常有三种:普通字符、转义的、通过unicode编码表示的三种,这三种都可以表示字符,其中通过unicode编码的可以通杀。
普通字符
形如 'a','A'这种的,例如char ch = 'a';
转义字符
转义字符,顾名思义,字符表达的东西不是字符本身,而是转成别的用普通字符无法表达的,如制表符(几个空格组成)、回车符(键盘上面没有画一个回车形状的字符给你敲吧),用常规的字符表达转义的意思。
\n:换行符号
\t:制表符
\b:backspace退格符
\r:回车符
上面这四种符号是很常用的,不用死记硬背,除了换行符外,记住键盘上有这几个键,并且用这几种符号来表示就可以了
除了这几种字符,要让程序展示出双引号" 反斜线\,直接输出是输出不了这两种字符的,而单引号'可以直接输出,就是不用\'进行转义输出,当然用转义输出也可以得到相同的结果。
unicode编码
unicode采用16位编码,总共能够编65535种字符,英文52种,汉字基本笔画也不多,6万多种足够全世界近200种语言了。unicode前256种字符和ascii(艾斯gi码)编码完全一致。
char基本类型数据,2个字节,总计16位,char类型的值也可以直接作为整数型的值来使用,但是只能表示0~65535范围内的无符号整数。
1 char ren = '香'; 2 int renValue = ren; 3 System.out.println(renValue);
char类型的数据和int类型数据有一个对应关系,char类型的任对应的int是39321,注意这个39321不对应unicode,unicode是带有\u的,在unicode中香对应的是\u9999。就是说char类型的数据用unicode进行编码,int类型数据的基本数字也有用unicode编码,这些编码中有些是对应了。
可以把char类型的字符赋给int类型,int会把char类型的转换成一个数字。反过来如果把一个数字例如97赋给一个char类型的变量,那么输出的将是一个字符。
字符串是一个引用类型变量,是用一个类进行实例化得到的。
注意:用System.out.println()输出变量对应的内容时候,变量本身不能打上双引号,否则会当作一个字符串处理了。而字符串初始化时候用双引号,字符初始化时候用单引号
浮点型数据
java提供了两种浮点型数据,float和double类型
科学计数法:假设一个整数X,有n位,用科学计数法可以表示为a*10^(n-1),例如1000可以表示为1.0*10^3,注意次方是n-1
java有两种形式表示数:1、十进制,比如22 2、科学计数法
对于float类型的数来说,4个字节,其中第一位是符号位,8位是指数位,另外23位是尾数位,例如1.23456795E15,其中234567895是指数,E后面的15是尾数
布尔(boolean)类型
在java语言中,布尔类型的变量的值只能用true和false来代表,不能用0或非0的来赋值
字符串"true"和"false"不会转换成boolean值,但是如果使用boolean值与一个字符串进行连接,boolean值可以自动转换成字符串类型。
1 public class TestBoolean 2 { 3 public static void main(String[] args) 4 { 5 boolean b1 = true; 6 boolean b2 = false; 7 //下面代码将出现错误:字符串不能直接变成boolean型的值 8 //boolean b3 = "true"; 9 //使用boolean和字符串进行连接运算,boolean会自动转换成字符串 10 String str = true + "hello"; 11 //下面将输出true 12 System.out.println(str); 13 } 14 } 15 将输出truehello
boolean值或变量主要用于旗标即flag进行流程控制。java语言中使用boolean进行流程控制的语句有如下几种:
1.if条件控制语句
2.while循环控制语句
3.do循环控制语句
4.for循环控制语句
除此之外还可以在三元运算符中使用?:
3.4基本数据类型的转换
3.4.1 自动转换
基本数据类型自动转换图:从左到右进行自动转换
char
int long float double
byte short
也就是说下游的变量类型可以自动转换成上游的变量类型,例如
byte a = 3;
double c = a;
char 转换成short需要强制转换
上述是基本数据类型的转换
基本数据类型与字符串的转换
基本数据类型和引用类型中的字符串也有转换,规则是:
所有的基本数据类型自动转换成字符串类型,但是注意转换时候只有在连接符+两端至少有一个是字符串时候才进行转换,如下的表达式输出结果是不同的:
1 str1 = 3 + 4 + "Hello"; 2 str2 = "Hello" + 3 + 4; 3 第一种输出结果是7Hello 4 第二种输出结果是Hello34
也就是说转换时候,首先必须遵守运算规则
自动转换的作用范围是:基本数据类型的小范围往大范围转换,另外还有基本数据类型与字符串连接时候的转换
3.4.2 强制转换
如果想把自动转换规则的顺序图逆过来,则需要强制转换。
强制转换语法
targetType varname = (targetType)originalType_varname
目标类型 变量 = (目标类型)原始类型变量
可以这么理解,先把赋值运算符右边的变量转换,然后再赋值给左边的,在对象多态也会有类似的用法。
强制转换有一个问题是对于大范围的转换成小范围会造成数据溢出
原码、补码与反码
计算机都以二进制形式进行存储,并且以补码存储
原码、补码、反码关系
将原码除了符号位以外进行取反就得到的是反码,将反码加1得到的是补码
原码取反得反码,反码加1得补码
例如-23
原码是:1001 0111
反码是:1110 1000
补码是:1110 1001
float和double之间的转换
如果赋值给一个float变量,不带f,因为默认是double处理的,所以会报错,因为系统并不自动转换,需要强制转换才行
基本数据转换成字符串,通过连接符+,按照运算规则可以自动转换,反过来字符串转换成基本数据需要用String类的方法进行解析转换
表达式类型的自动提升
当一个算术表达式中包含多个基本类型的值时,整个算术表达式的类型将自动提升。
1.所有byte、short和char型将被提升到int型
2.整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型。就是说,如果一个表达式里面最高等级是float,那么最终的表达式结果将是float型,而中间的运算将按照自动转换规则进行转换,但并非一次性各个操作数直接转换成float型。
例如c = 'a' + 7 + "Hello!"
运算过程如下:首先a是字符型,7是int型,首先对a进行转换,a对应的ascii码是97故97+7=104,104是int型,与字符串进行连接时候,自动进行转换(注意int与字符连接是转换成int,与字符串连接时候,int的数字被当作字符串进行处理),所以最后的结果是104Hello!
long型和int型
系统默认会将一个不超过int型的数据当作int来处理,如果超出了int的需要明确指定用long处理,否则会报错。
1 long a = 3;//3系统会当作int来处理,但是会进行转换成long型赋给a 2 long c= 333333333333333333333;//错的,超出范围 3 long c = 333333333333333333333L;//需要指定是L型即
变量赋值总结
上面说了基本数据类型之间的数据转换规则以及基本数据类型与引用数据类型部分细节,总的来说规则如下:
基本数据类型之间的自动转换:
以int为分支点的两条转换线,小范围往大范围转换自动转换,当一个表达式有short、char、byte进行运算时候,自动转换成int进行运算
基本数据类型之间的强制转换:
自动转换的逆行,范围大的往范围小的转换,可能会造成数据溢出或者精度下降。强制转换的一个重要一点是int型往char类型的转换,这个可以生成验证码。
基本数据类型与引用数据类型的转换
这里的引用数据类型其实就是基本数据类型对应的包装类,在java中一切都是对象,一个数也是一个对象。byte对应的是Byte类,char对应Character类,boolean对应boolean类等等.基本数据类型与它对应的包装类,会有相应的解析函数进行解析转换,包装类内部之间也有相应的函数进行解析转换
另外一个需要特别说明的是基本数据类型自动转换成String类,就是用+进行连接时候自动转换;还有boolean的值初始化时候只能赋值给boolean型,null值只能赋值给应用对象,与0可以初始化赋值给int等数值型对应。
3.5 数组类型
数组是一种数据结构,数组元素之间的类型由数组定义时候决定,一旦数组初始化完成,数组所占的内存空间将被固定下来,因此数组的长度将不会再改变。即使清空数组元素,数组所对应的内存空间将仍然保留,除非用命令释放了内存。java数组可以存储基本数据类型,也可以存储引用数据类型,只要数组元素的类型是相同的即可。数组本身是一种数据类型,属于引用数据类型。
3.5.1 定义数组
1 type[] varname; 2 数组类型[] 数组名
也可以用type varname[],这一种方式可读性太差,不建议用,C#就不支持这一种,越来越多语言,不支持这一种。
定义数组时候不能定义它的长度,只有在初始化时候才行,也就是
1 int[10] matrix是非法的
3.5.2 数组的初始化
数组的初始化有两种,一种是静态的,一种是动态的,静态的定义数组元素是什么,不定义长度,由系统来决定长度,动态的定义数组长度不定义数组元素是什么,由系统来初始化默认值。
静态初始化语法
1 数组名 = new 数组类型[]{数组元素值列表} 2 或者简化方式:数组名 = {数组元素值列表}
注意:数组元素值之间用逗号隔开
例如
char[] ch; ch = new char[]{'a','b','c'}; ch = {'a','b','c'};//注意这种初始化方式是错的 如下两种任选其一: 方式一: char[] ch = {'a','b','c'}; //将上面初始化方式分开写成下面的是错误的 char[] ch; ch = {'a','b','c'}; 方式二: char[] ch = new char[]{'a','b','c'}; //将上面初始化方式分开写是正确的 char[] ch; ch = new char[]{'a','b','c'};
注意:对于char类型的变量或者数组定义来说,元素值都需要用单引号的
通常定义和初始化可以同时完成:
char[] ch = {'a','b','c'}; 动态初始化语法: char[] ch; ch = new char[10]; 定义和初始化可以同时完成 char[] ch = new char[10];
动态初始化时候的元素初始值:
byte,short,int ,long元素初始值为0 float,double元素初始值0.0 char元素初始值为\u0000 boolean元素初始值是false 引用类型(类、接口、包装类数组)元素初始值是null
不要在初始化数组时候,动态和静态同时使用,也就是说不要限制长度时候,又赋值也就是
char[] ch = new char[10]{1,2,3};//是非法的
3.5.3 使用数组
使用数组最常用的方式是访问元素值,用分隔符[索引号]来对数组进行访问即可
例如:
1 String[] str; 2 str = new String[10]; 3 str[7] = "Hello World"; 4 str[2] = "AQ"; 5 for (int i = 0;i < str.length;i++){ 6 System.out.println(str[i]); 7 } 8 //也可以用如下foreach方式输出 9 for(String a:str){ 10 System.out.println(a); 11 }
注意方式是foreach,但关键字的依然是for循环,没有foreach循环
for(循环变量类型 数组循环变量:数组名){ System.out.println(循环变量名) }
3.5.4 数组在内存中的存储机制
内存分为两部分,堆内存和栈内存,栈内存存放数组变量,堆内存存放数组变量引用的数组值。
数组元素和数组变量在内存中是分开放的,数组变量可以与内存中数组元素存储的位置对应起来,需要使用的时候,数组变量引用数组元素的值。也就是说在使用内存中的数组值时候,并不是直接使用数组元素的,而是通过数组变量间接来使用,数组变量可以指向任何有效的堆内存。
堆内存是heap,栈内存是stack,数组变量是引用堆内存中的数据唯一方式。
为什么要将内存分为栈内存和堆内存呢?这其实也利用了缓存思想,把那些经常利用、建立成本高的东西放在一个数据区里面,这样经常使用时候能够提高效率,如果不这样做,比如用了就销毁,那么再建立时候会效率低。
当一个方法执行时,方法会建立自己的栈内存,在这部分内存中存放方法使用时候需要的变量,当方法执行完毕后,这部分栈内存会被销毁,而创建对象时候,对象创建成本是比较高的,会放在堆内存中,当方法执行完毕后,对象占用的堆内存并不会被销毁,因为这个对象还有可能被别的方法使用,只有一个对象没有任何变量引用它时候,系统的垃圾回收机制才会在合适的时候回收。
如果堆内存中数组不再有任何变量指向自己,则这个数组将成为垃圾,数组占用的内存会被自动销毁,为了让数组不被引用,可以讲数组变量赋值为null,这样就可以切断数组变量与实际数组的引用关系,实际数组就成了垃圾。注意:数组变量引用的数组元素必须与数组变量的类型是兼容的。
例如:定义两个数组变量和两个数组:
1 int[] a = {1,2,3};//数组长度为3,但至少数组长度而已,和数组变量没有关系,数组变量只关心类型 2 int[] b =new int[10];//数组长度为10 3 //将a引用的数组使用权利共享给b 4 b=a;//此时的b数组长度是3 5 a=b;//这种引用就是有问题的,因为b还没有断开与它当前数组的连接,所以长度是10,而a长度是3,显然a长度装不下b
数组变量本身没有长度(这个长度不是变量占用的内存),只有引用到堆内存数组时候,堆内存数组的长度才决定数组变量的长度。但是需要注意的是,如果数组变量已经被初始化了,它当前的长度就已经明确了,没有引用别的数组的数组变量长度是任意的。
总之看一个数组的时候,把一个数组分成两部分看,栈内存的数组变量,以及堆内存的实际数组,实际数组通过栈内存的数组变量被外界访问,实际数组长度决定了数组变量引用别的数组长度
3.5.6 引用类型数组的定义及初始化
前面定义的是基本数据类型的数组,就是说数组元素是普通的基本类型,比如说堆内存数据元素都是整型的 而栈内存的数组变量刚好也是整型的,那么就可以用数组变量来引用堆内存的数据。类似地,只要未初始化的栈内存数组变量类型与定义的堆内存数组数组元素类型兼容,那么就可以用栈内存的引用数组变量类型来引用堆内存的引用类型数组元素。
可以定义一个类,用该类来定义一个数组变量存放在栈内存中,同时用类来实例化若干个对象,组成数组放在堆内存中,然后将栈内存的类数组变量与堆内存的数组连在一起,然后通过栈内存类变量来引用堆内存的数据。
对于接口以及包装类来说都可以按照上面的方式来处理。
例如:
1 //定义一个Person类,然后定义一个Person类数组变量 2 Person[] p; 3 //然后用Person类实例化若干个对象,组成堆内存的数据 4 Person ren; 5 Person zhang; 6 //把堆内存的对象与栈内存的数组变量连接起来 7 p[0] = ren; 8 p[1] = zhang;
注:上面的实例的行为和方法略去了,这里只写了最关键的思路
其实上面说明了:通过数组变量来访问堆内存的数据,和通过实例来访问堆内存的对象行为和熟悉是等效的,两种方式都可以修改对象的熟悉和行为。也就是通过ren这个实例访问属性和通过p[0]访问是一样的
3.5.7 多维数组
多维数组可以通过降维方式处理,三维化为二维,二维化为一维(这种抽象方式思考很重要)
二维数组定义
数组类型[] 数组名
例如有如下数组
1,3,4
2,5,6
这是一个二维数组,如何化为一维数组呢?
这样看数组Aarray = {a1,a2,a3}是一个一维数组
只要把a1={1,2},a2={3,4},a3={4,6}就可以处理了
我们不必关心一维数组Aarray里面的元素是什么,它里面的元素是一个比较复杂的数据
二维降维成一维时候,如果二维是m*n型的,如果一维是含义m个元素,那么每个元素里面含有n个值
初始化一个二维数组例子:
1 //定义一个二维数组 2 int[][] a; 3 //对数组降维,降维成1维 4 a = new int[3][]; 5 //初始化一维数组的第一个元素 6 a[0] = new int[2]; 7 a[0][0] = 1; 8 a[0][1] = 2; 9 //输出一维数组的第一个元素,相当于第一列 10 for(int i = 0;i < a[0].length;i++){ 11 System.out.print(a[0][i]); 12 } 13 14 package chapter3; 15 //本类用于检测一个类具有的数组函数功能 16 import java.util.*; 17 18 public class TestStringDemo01 { 19 public static void main(String[] args){ 20 //定义若干个数组 21 int[] a; 22 a = new int[]{1,3,5,8,9,3,4}; 23 int[] b; 24 b = new int[]{2,4,6,8,9,2,3}; 25 int[] d; 26 d = new int[]{1,3,5,8,9,3,4}; 27 //定义一个空数组 28 int[] c = null; 29 //检测两个数组是否元素个数及元素内容是否相同 30 checkEquals(a,b); 31 checkEquals(a,d); 32 //检测数组复制功能 33 c = Arrays.copyOf(b, 4); 34 for(int array:c){ 35 System.out.println(array); 36 } 37 //对数组进行排序 38 Arrays.sort(d); 39 for(int array:d){ 40 System.out.print(array); 41 } 42 System.out.println(); 43 //批量赋值,第3(包括)到第5(不包括)个数组元素赋值为3 44 Arrays.fill(b, 2,3,3); 45 for(int array:b){ 46 System.out.print(array); 47 } 48 } 49 public static void checkEquals(int[] x,int[] y){ 50 if(Arrays.equals(x, y)){ 51 System.out.println("两个数组元素长度及内容相同"); 52 }else{ 53 System.out.println("两个数组元素长度及内容不同"); 54 } 55 } 56 }
4.运算符
Java语言的运算符和C语言基本相同,只是Java中对运算符的操作数类型有更多限制,而C语言中的指针运算符和逗号运算符在Java中不再使用。
就像数学中的四则运算一样,java也有自己的运算符,运算符与操作数连接,构成表达式。运算符之间是有优先级的,同四则运算一样,比如除法优先于加法。
java运算符按照优先级有如下:
分隔符 . () [] {} ; 注:逗号运算符在其他语言中比如c++,c中会有,但是java中不用了
单目运算符 ++ -- ~ !
类型转换运算符 (type)注: 也就是说小括号有多种用法
算术运算符: 乘除求余 * / %
加减 + -
移位 << >> >>> 注:没有<<<
关系运算符 > >= < <= instanceof
等价运算符 == !=
按位运算符
按位与 &
按位异或 ^
按位或 |
条件运算符
条件与 &&
条件或 ||
三目运算符 ? :
赋值运算符 =以及=与算术、移位、按位运算符的组合即:= *= /= %= += -= <<= >>= >>>= &= ^= |= &&= ||=
下面详细介绍上面的运算符:
4.1分隔符
Java的分隔符有空格、逗号,、分号;、小括号()、中括号[]、大括号{}。
Java的语句采用分号;作为分隔符,点号.主要用于类和对象对其成员的访问,()可以用于改变表达式的运算顺序,[]用于数组,{}用于语句分隔
空格:空格几乎可以出现任何一个地方而不会编译错误,但是注意禁止将一个变量字符串用空格分开,这将导致编译错误。
点号.:点号主要用于类或对象与它的(属性、方法或内部类)之间,用于类或对象对它的成员之间的访问
分号;:无需多说,用于语句之间的分隔,虽然运行一行用分号分割多个语句,但是不建议这样写,这样会使代码不清晰。
小括号():小括号的作用很多。主要有如下用法:
1.包含表达式用于优先计算
2.数据类型之间的转换
3.方法定义时候的包含形参
4.方法调用时候传递实参
中括号[]:用于数组定义以及数组变量的访问
大括号{}:用于包含语句块,一个函数、循环语句、分支语句都需要用大括号来包含起来。
只要名称里面带有"括号"两个字的,都需要是成对的。
单元运算符:++ -- ~ !
++自增运算符
每次自增量是1,自增运算符放在操作数左边和右边是有区别的。放在操作数左边,比如++a,使用是先对操作数a增1,然后再用a与另一个操作数进行运算,放在右边的时候,a++ ,先用a与另一个操作数进行运算,然后再对a进行自增1
例如
1 int a = 3; 2 int b = a++ + 3;//输出结果是b=6,a此时已经自加为4 因为++在操作数a右边,所以先用a与其他操作数运算,然后再对a自加 3 int c = ++a +3;//输出结果是b = 8,a此时为5 因为++在操作数左边,所以先对a自加1,然后与其他操作数运算
--自减运算符
每次自减量是1,自减运算符放在操作数左边和右边是有区别的。放在操作数左边,比如--a,使用是先对操作数a减1,然后再用a与另一个操作数进行运算,放在右边的时候,a-- ,先用a与另一个操作数进行运算,然后再对a进行自减1
例如
1 int a = 3; 2 int b = a-- + 3;//输出结果是b=6,a此时已经自减为2 因为--在操作数a右边,所以先用a与其他操作数运算,然后再对a自减 3 int c = --a +3;//输出结果是b = 4,a此时为1 因为--在操作数左边,所以先对a自减1,然后与其他操作数运算
~按位取反运算符
所谓按位,就是操作的粒度是二进制位,并不是将3,取反,得到-3,能够完成这样的功能的是减号-,减号-有四则运算功能,也有将一个数的符号改变。按位取反,就是将一个数的每一个二进制位1改成0,0改成1。注意按位的操作对象是计算机存储的补码,不是第一次计算的原码,计算机保存数用补码保存。另外反码和取反运算符机制是不同的,反码是符号位不变的,取反是将符号位也改变的。例如~-5是这样进行计算的
-5假设用int 4个字节存储
原码:1000 0000 0000 0000 0000 0000 0000 0101
(符号位不变)反码:1111 1111 1111 1111 1111 1111 1111 1010
(反码加1)补码:1111 1111 1111 1111 1111 1111 1111 1011
取反:0000 0000 0000 0000 0000 0000 0000 0100
也就是~-5 =4 注意负号-用于四则运算,但不表示取负这种运算符的意思。-5是一个整体
逻辑非运算符!
所谓逻辑就是它的操作数及操作结果是逻辑值,也就是是true或者false
类型转换运算符(type)
这个运算符用于基本数据之间的转换、基本数据与相应的包装类转换、以及引用类型的多态使用
算术运算符
加加减减,哐哐当当
移位运算符 << >> >>>
左移位运算符<<
操作数是整数,右操作数是要移动的位数,左移后空下来的用0来补充。3<<2,把3的二进制位往左边移动2位
右移位运算符>>
往右移动后空下来的数位以原来的符号位补充,如果原先是正数就填0,负数就填1
无符号右移运算符>>>
移动相应位数后总用0来补充
移位运算符有如下限制:
1.对低于int类型(byte,char,int)的数据进行移位,系统会自动转型成int型再进行移位
2.对于超过基本数据类型存储空间的移位,例如int 是32位,如果移动位数超过三位那么将用该数对32求余,余数和移动该数效果是一样的(循环),例如33,那么和移动1位是一样的。
关系运算符
除了instanceof外,其他的操作数都是两个数值型,操作结果是逻辑值即boolean值
等价运算符
判断两个操作数(操作数数值型、字符型、字符串均可)是否一样
按位运算符
按位与&:如果是整型或者字符型数据,两个同为1情况下,按位计算时结果为1,其他情况均为0;如果是逻辑值,同为真的情况下,结果才是真
按位或|:如果是整型或者字符型数据,有一个为1的情况下,按位计算时结果就是1;如果是逻辑值,有一个是真的情况下,结果才是真
按位异或^:如果是整型或者字符型数据,两个操作数相同情况下,按位计算时结果是0,不同为1;如果是逻辑值,相同为假,不同为真
&& ||与& |区别
&& 和||都称为短路型的,就是shortcut 走捷径,意思是进行判断的时候,如果能够从第一个操作数判断出结果,就不对计算第二个操作数,而& |是不走捷径的,也就是两个操作数都需要计算结果,然后再判断整个表达式的值,参考下面的例子
1 //定义两个变量,并赋值 2 int a = 3; 3 int b = 4; 4 //短路型处理 5 if(a > 2 || b++ > 4){ 6 System.out.println("a = " + a + " b = " + b); 7 } 8 输出结果是a为3,b为4, 9 另一种算法 10 //定义两个变量,并赋值 11 int a = 3; 12 int b = 4; 13 //短路型处理 14 if(a > 2 | b++ > 4){ 15 System.out.println("a = " + a + " b = " + b); 16 } 17 输出结果是a为3,b为5
两种算法的区别是:短路型只判断了第一个操作数,也就是a>2成立,所以整个表达式肯定是正确的,因此就偷懒没有去计算第二个表达式,也就是没有对b再进行自加,而第二种非短路型的,它必须计算两个表达式的值,然后再得出最终的结果
注意:
一个表达式是由运算符连接操作数组成的,一个运算符并不是可以连接任意个数和任意种类的操作数。一般来说一个运算符连接1个或2个操作数,只有一个三目运算符连接三个操作数,操作数有两种类型,一种是基本数据类型,一种是引用类型。
单独说运算符是没有意义的,需要与操作数结合起来说,运算符一定要和操作数一起讨论才会有意义
相对来说,只有几个运算符操作数不是比较复杂,或者说一种运算符可能的操作数有两种
分隔符 . () [] {} ; 注:逗号运算符在其他语言中比如c++,c中会有,但是java中不用了 分隔符中()里面不确定,通 常由别的运算符决定,[数字]
单目运算符 ++ -- ~ ! ++ --都是数值型变量,整型和浮点型都可以,~操作数是整型,浮点型和boolean型报错
!操作数是逻辑值即true或false,或者经过运算结果是逻辑值的表达式,例如!true或者!(3>2)
类型转换运算符 (type) 小括号有多种用法
类型转换运算符里面有个type,它与()是一个整体,与单独的()是不同的,类型转换运算符的操作 数是基本数据类型和引用数据类型(注意不是值 仅仅是类型)
以上分隔符、单目运算符和类型转换运算符的操作数均不是两个,()包含的是一个大的表达式
算术运算符 乘除求余 * / % 均为两个数值型操作数
加减 + - -是数值型,+有三种情况,一种是两个操作数均为数值型,一种是一个字符型一个数值型(处理结果 可以是字符型或者数值型,看定义情况,例如int a = 'a' + 7;处理结果是数值型,char c = 'c' + 7;处理结果是字符型),另一种情况是基本数据类型与字符串类型的连接,连接结果是字符串
移位 << >> >>> 注:没有<<< 操作数是两个,都是整型数据
关系运算符 > >= < <= instanceof 除了instanceof外,其他的操作数都是两个数值型,操作结果是逻辑值即boolean值
等价运算符 == != 两个操作数都是数值型或者都是字符型或字符串型,操作结果是逻辑值
按位运算符 操作数是整型或字符型或逻辑值或逻辑表达式,但不能混搭,如果操作数是数值或字符型,得到的 是数值
按位与 &
按位异或 ^
按位或 |
条件运算符 操作数是逻辑值或逻辑表达式
条件与 &&
条件或 ||
三目运算符 ? :
赋值运算符 =以及=与算术、移位、按位运算符的组合即:= *= /= %= += -= <<= >>= >>>= &= ^= |= &&= ||=
总结:
1.除了三目运算符外,其他的操作数均为两个,
2.算术运算符、按位运算符(操作数位数值型)、三目运算符(操作数最终结果需要是数值型)、移位运算符操作结果均是数值型的
3.关系运算符、按位运算符(操作数为逻辑值)、条件运算符的运算结果是逻辑值,通常用于条件语句的判断
4、组合型的赋值运算符和没有=要求是一样的,例如*=要求的操作数是两个,x*=y 和x=x*y是等价的
5.操作数是什么类型的,结果就会是什么型的(按位运算符的操作数是字符型除外)
6.算术运算符中的+是特殊的
7.条件运算符操作数是逻辑型数据,结果是逻辑型
8.按位运算符操作数是整型或者字符型或者逻辑型,字符型和整型运算结果都是整型,逻辑型运算结果是逻辑值
9.等价运算符操作数是字符型、字符串型或者数值型,结果是逻辑值
10.关系运算符(除了instanceof)和等价运算符操作结果都是逻辑值
关于表达式代码:
1、不要把表达式写的过于复杂,如果一个表达式过于复杂,把它分成几步完成,代码就是文档
2、不要依赖表达式的优先级来控制计算顺序,多利用()来控制计算顺序,这样会使代码可读性提高
一条非常好的寓言
有人问一个有多年航海经验的船长,"这条航线的暗礁你都非常清楚吧?",船长的回答是:我不知道,我只知道哪里是深水航线。这是很有哲理的故事。它告诉我们写程序时,最好采用良好的编码风格,养成良好的编码习惯。不要随心所欲的乱写,不要把所有的错误都犯完!世界上对的路可能只有一条,错的路却有成千上万条,我们不要成为别人的前车之鉴!
5.Java流程控制
5.1概述
java流程控制有三种结构:顺序结构、分支结构、循环结构,已经证明用这三种结构可以组合成任何一种流程
顺序结构:顺序结构是最常见的结构,也是程序流程的总体结构,就是说如果没有分支语句和循环语句,程序将一行一行执行
分支结构:if语句和switch语句
循环语句:while循环、do...while...循环、for循环、foreach循环(第二for循环)
5.2 顺序结构
顺序结构没有什么好说的,下面介绍分支语句和循环语句
5.3 分支结构
分支语句:if语句和switch语句
这两种语句可以参考我在linux编程里面介绍的,原理都是一样的,只是关键字略有区别。
5.3.1 if语句
if语句有三种形式:if if...else.... if ...else if...else if
三种语句的处理能力分别是1种,2种,3种
第一种if语句
if(逻辑表达式){
语句
}
这种情况是:如果逻辑表达式的值是true,则执行下面的语句,如果为false,则跳过改逻辑分支,继续执行if条件句下面的
比如说:
1 int a = 3; 2 if( a > 4){ 3 System.out.println("a大于4"); 4 }
由于a不大于4,因此,if语句块将不会被执行
第二种:if...else...
这种条件句本质是二分法,非此即彼,如果if条件句里面包含的集合是A,那么else所包含的条件集合就是A的补集
例如:
1 int a = 3; 2 if( a > 4){ 3 System.out.println("a 大于4"); 4 } 5 else{ 6 System.out.println("a小于等于4") 7 }
if里面条件对应的集合是a>4的域,而else则对应a>4以外的所有,也就是相应的补集,a<=4,由于只有两个分支,所有处理情况只有两种。
第三种:if...else if....else if
这种情况并非二分法,给出若干个条件集合,符合其中,则执行,这些条件集合一定是互斥的,不能有交集,否则系统将无法确定走哪一个分支,但这些集合未必能构成满足条件的一个完整集合。一个if表示一个分支情况
例如:
1 int a = 3; 2 if(( a > 0)&&(a < 2)){ 3 System.out.println("a属于0到2"); 4 }else if( ( a >= 3)){ 5 System.out.println("a大于等于3"); 6 }
注意:if或者else后面跟的执行语句(哪怕执行语句是空语句),最好带上{}与其他语句分隔,如果是单条语句,可以不带,但这会降低代码的可读性,记住选择最好,别尝试去试探所有的,浪费精力,可读性第一,效率第二
5.3.2 switch语句
switch语句也是一种分支语句,它相当于if条件句的第三种,但是与之不同的是switch语句的表达式只能是整型的或者char型的,也就是只有byte,char,short,int四种类型
使用语法:
1 switch(表达式变量){ 2 case 值1: 3 处理语句; 4 break; 5 case 值2: 6 处理语句; 7 break; 8 default: 9 处理语句 10 }
注意:
1.switch里面的判定条件表达式集合,表达式里面的变量取值只能是byte,short,char,int 四种,对于case里面的判定值,如果是char类型的,需要加上单引号,如果不是,不需要加上单引号
2.如果case里面都没有匹配上,会默认执行default的处理语句,break是跳出整个switch语句
3.每一个case语句的condition值后面都有一个冒号:,切勿忘记
5.4 循环结构
循环语句就是满足一定条件下反复执行循环体的语句,循环结构由于把初始条件、判断条件以及其他自增处理写在一起了,因此不容易看明白循环结构的处理顺序,但是习惯就好了
循环结构组成
1.初始化语句:一条或多条用于初始化变量的语句
2.循环条件:这是一个boolean表达式,值是true或者false,用于确定是否执行循环体
3.循环体:执行的循环语句
4.迭代语句:控制循环条件,用于结束循环
这几个部分放的位置,一般是确定的,怎么执行是,系统内部处理的事情,只需要掌握总体怎样进行循环处理控制流程就可以了
while语句
语法:
1 初始化语句; 2 while(循环条件boolean表达式){ 3 循环体 4 迭代语句 5 }
注意:while(表达式)后面是没有分号,否则循环体就是一个空语句,这样的话循环条件就没法改变,造成循环是一个死循环
do...while...语句
语法
1 初始化语句 2 do{ 3 循环体 4 迭代条件语句 5 } 6 while(循环条件);
区别:
与while语句不同的有两点
1.while语句后面有一个分号;而while循环里面是没有这个的
2.对于整个循环,先执行循环体一次,再判断,所以do...while语句最少会有一次循环执行。
for循环(第一式)
语法:
for(初始化语句;判断条件;迭代条件){ 循环体 }
for循环可以替代while,do...while循环,使用非常常见
注意:
for循环和while,do...while循环的区别
for循环的迭代条件是不与循环体放在一起的,而while do...while循环是把迭代条件与循环体放在一起,这样的话,当使用中断循环的continue语句时候,比如把continue语句放在迭代条件之前,那么对于while和do...while循环来说,迭代条件语句将不会被执行,而for循环因为没有放在一起,同样会执行迭代条件
for循环的初始化语句初始化的变量可以不止一个,如果是多个初始化变量,可以用逗号隔开。例如:
1 for( int a = 0,b = 2,c = 3;(a > 1)&&(b > 3)&&(c > 4);c++) 2 { 3 System.out.println(a + b + c); 4 }
注意:
1.并非迭代语句就一定能够控制循环条件,如果在循环体内,加入了对循环变量的处理,循环条件有可能也会一直被满足,造成一个死循环
2.for循环括号里面只有两个分号是必须要的,初始化语句、条件语句、迭代语句都是非必须的,都可以省略,如果把初始化语句和迭代条件语句分开像while语句那样放,就很像while的语句了
3.for循环里面的循环变量,只在循环内有效,超出for循环无效,习惯选用i,j,k作为循环变量
4.可以在for循环外面定义一个变量,然后把循环变量赋值给这个变量,这样相当于扩大了循环变量的作用于(间接)
for循环(第二式)
这也是一种for循环,不过形式更加简洁。有人称之为foreach循环,但关键字其实不是foreach。
语法:
1 for(循环变量类型 循环变量:数组或集合类){ 2 循环体 3 }
5.5嵌套循环
在循环体内再放置一个循环,嵌套循环就像一个轮子带动另外一个轮子,外轮子转一个值,内轮子狂转N圈,如是而已
5.6 中断循环
有三种方式来结束循环,continue,break,return,三种关键字结束循环的能力变强。
continue只结束continue所在直属循环的循环体在continue语句下面的部分
例如:
下面是一个嵌套循环
1 外循环A{ 2 外循环B{ 3 内循环C{ 4 执行语句1; 5 执行语句2; 6 //continue; 7 //break; 8 //return; 9 执行语句3; 10 执行语句4; 11 } 12 } 13 }
上面一个嵌套循环里面放置了三种结束循环,它们的结束能力是不同的,如果只有continue,那么它只能控制循环C的某一次执行循环体语句时,continue语句一下的部分,也就是说只执行执行语句1,2,而执行语句3和4是不会执行的,然后继续进行循环C的下一次循环体执行,也就是说continue只控制一个循环,某一次循环体的执行。
break语句控制它直属循环的所有次循环体的执行,如果上面的循环里面放的是break关键字,那么直接跳出循环C,但并不影响循环B,也就是不会让循环B中止。
return语句是控制整个嵌套循环执行,也就是说如果方有return关键字,直接跳出ABC三个循环
也可以用一个标签标识一个循环,然后用break 标识来结束outer所标识的循环,标识符标识的循环可以是单个循环,也可以是多层循环,同样continue也可以结束一个标识符标识的当次循环(不结束所有次循环)