Java语言基础
1、关键字
关键字的定义:关键字是被Java赋予了特殊含义的单词。
关键字的特点:关键字中所有的字母都小写。
关键字的分类:
(1)用于定义数据类型的关键字:class、interface、byte、short、int、long、float、double、char、boolean、void。
(2)由于定义数据类型值的关键字:true、false、null。
(3)用于定义流程控制的关键字:if、else、switch、case、default、while、do、for、break、continue、return。
(4)用于定义访问权限修饰符的关键字:private、public、protect。
(5)用于定义类、函数、变量修饰符的关键字:abstract、final、static、synchronized。
(6)用于定义类与类之间、类与接口之间关系的关键字:extends、implements。
(7)用于定义建立实例、使用实例、判断实例的关键字:new、this、super、instanceof。
(8)用于异常处理的关键字:try、catch、finally、throw、throws。
(9)用于包的关键字:package、import。
(10)其他修饰关键字:native、strictfp、transient、volatile、assert。
2、标识符
在程序中自定义的一些名称。
由26个英文字母大小写,数字:0—9 符号:_$组成
规则:数字不可以开头;不可以使用关键字。(若非用,将首字母大写,一般不建议这么做)
Java中严格区分大小写。
注意:在起名字的时候,为了提高阅读性,为了提高阅读性,要尽量有意义。
Java中的名称规范:
(1)包名:多单词组成时所有字母都小写。如xxxyyyzzz
(2)类名和接口名:多单词组成时,所有单词首字母大写。如:XxxYyyZzz
(3)变量名和函数名:多个单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写。如:xxxYyyZzz
(4)常量名:所有字母都大写。多个单词时每个单词用下划线连接。如XXX_YYY_ZZZ
3、注释
(1)对于单行和多行注释,被注释的文字,不会被jvm解释执行。
(2)对于文档注释,是Java特有的注释,其中注释内容可以被jdk提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。
(3)注释是一个程序员必须要具备的良好的编程习惯。、
(4)初学者编写程序可以养成习惯:先写注释再写代码。
(5)将自己的思想通过注释先整理出来,再用代码去体现。
(6)因为代码仅仅是思想的一种体现形式而已。
4、常量与变量
(1)常量
1)常量表示不可以改变的数值。
2)Java中的常量的分类:
① 整数常量
② 小数常量
③ 布尔型常量
④ 字符常量
⑤ 字符串常量
⑥ NULL常量
3)对于整数:Java有三种变现形式。
① 十进制:0—9,满10进1。
② 八进制:0—7,满8进1,用0开头。
③ 十六进制:0—9,A—F,满16进1,用0x开头。
进制的由来:
任何数据在计算机中都是以二进制的形式存在的。二进制是早期由电信号开关演变而来。一个整数在内存中一样也是二进制的,但是使用一大串的1或者0组成的数值进行使用很麻烦。所以就想把一大串缩短点,将二进制中的三位用一位表示。这三位可以取到的最大值就是7,超过7就进位。这就是八进制。将二进制中的四位用一位表示,这四位可以取到的最大值是F,超过F就进位,这就是十六进制。规律:进制越大,表现形式就越短。
拓展:
输出语句:System.out.println();括号中可以是任意形式的整数(但显示结果是十进制的)、字符、字符串,浮点数、布尔值、对象等任意类型的数据。
十进制-->二进制:对十进制数进行除2运算。
二进制-->十进制:对二进制数进行乘2的过程。
其他进制转换:
二进制-->十六进制:每四个二进制位就是一个十六进制位。
二进制-->八进制:每三个二进制位代表一个八进制位。
负数的二进制表现形式:其绝对值的二进制表现形式取反加1。负数的二进制位的最高位数是1。
任何运算在计算机底层都是通过二进制形式来计算的。
(2)变量
变量的概念:
内存中的一个存储区域,该区域有自己的名称(变量名)和类型(数据类型),该区域的数据可以在同一类型范围内不断变化。
使用变量的场合:
用来不断的存放同一类型的常量,并可以重复使用。只要有不确定的数据,就定义一个变量进行存储。比如主函数中的打印语句,打印的值可能不同,因此可以在内存中给它开辟一个空间,把要操作的值存放到该空间中。空间存在的好处:空间里的值可以任意变化,只要操作该空间就能操作空间的值。变量:就是将不确定的数据进行存储,也就是需要在内存中开辟一个空间。运算时都在空间中进行,内存就是运算区域。
拓展:内存越大,运算越快。内存里装的数据越多,同时也必须提高cpu处理性能。Cpu处理的数据越多代表越快。若内存小,内存里只能装这些数据,cpu越快也没有意义,内存即为这台机器的评定。配合使用,cpu够快,内存越大。
使用变量注意:
变量的作用范围(一对{}之间有效)
初始化值
定义变量的格式:
数据类型 变量名=初始化值
如何开辟内存空间:
名称:变量名 空间类型:数据类型 |
其实,就是通过明确数据类型、变量名称、数据来完成的。
如:int a = 3,b;定义了两个变量,名称分别为a、b,a被初始化了,b没有,b不能直接被使用,只有赋值了才能使用,变量才有效。
Java语言是强类型语言,对于每一种数据都定义了明确的具体数据类型,在内存中分配了不同的大小内存空间。
数据类型
|--基本数据类型
|--数值型
|--整数类型(byte、short、int、long)
1 2 4 8
-128~127 -32768~32767
-2^7~2^7-1
|--浮点类型(float、double)
4 8
|--字符型(char)
0~65535
|--布尔型(boolean)
|--引用数据类型
|--类(class)
|--接口(interface)
|--数组([])
整数默认:int 小数默认:double
拓展:整数之所以是有4种类型,是因为byte只有8位二进制数,排列形成的数是有限的。整数之所以这么划分,和地层的运算也有关系,比如芯片级运算,一般有运算指令和缓存。缓存?Cpu有二级缓存,缓存越大越好。Cpu执行一条指令的过程:取指、解释、执行。Cpu从内存中取指一般是比较慢的,因此出现了缓存,直接从缓存中取指,速度是比较快的。Cup中的内存就叫做缓存,cpu中的内存比较小,所以在定义数据的时候,就要考虑到数据的大小,若运算数据不会超过100,就没有必要定义int类型,省的占个位置,节约内存。缓存是缓冲数据用的,缓存越大,存放数据越多,处理东西越多,所以去买cpu,买硬盘,要看缓存。硬盘为什么有缓存?现在硬盘有一些技术在里面,如防毒死等,这些方式通过软件来操作,里边有小芯片来操作这些东西,那么芯片要运算的时候,硬盘本身要是带内存的话,在硬盘的内存中运算就可以了,硬盘的内存就叫缓存。
4 |
Ps:float = 2.3;编译会出错。原因:2.3默认为double,此时应为float = 2.3f。如int=4;被虚拟机运行,它就会在内存中开辟一个空间:
int x
但是如果是byte = 2;这种情况下不会出现这种错误。原因:会自动判断是不是在范围内,只要在范围内即可。(-128~127)
但是byte b = 128;则不正确。变医护会提示损失精度。
拓展:System.out.println();打印后换行;
System.out.print();打印后不换行;
System.out.print(“\n”);打印后换行。
自动类型转换(也叫隐式转换)
强制类型转换(也叫显示转换)
类型转换原理:
byte->short->int->long->float->double
Char->int
所有的byte型,short型和char型的值将被提升到int型;
如果一个操作数是long型,计算结果就是long型;
如果一个操作数是float型,计算结果就是float型;
如果一个操作数是double型,计算结果就是double型。
分析:
对程序段:
byte = 3;
b=b+2;
System.out.println(b);
编译后:
找到:int
需要:byte
b=b+2
^
1错误
画图解释:
byte = b+2;
b = b+2;
+
||
+
||
|
所以报错。
但是此时非要把
赋给
:=>b = (byte)(b+2)--强制类型转换
应用:小数-->整数
char类型也可进行运算:因为它的字符在ASCII码表里都有相应的数字。ASCII码表所有计算机都支持。比如:System.out.println((char)5);打印出梅花
Syste.out.println((char)4);打印出方块
5、运算符
计算机就是在处理数据。原理:运算数据。
(1)算术运算符
① +(正号) +3——>3
② -(负号) b=4;-b;——>-4
③ +(加号) 5+5——>10
④ -(减号) 6-4——>2
⑤ *(乘) 3*4——>12
⑥ /(除) 5/5——>1
⑦ %(取模) 5%5——>0
⑧ ++(自增(前)) a=2;b=++a;——>a=3;b=3
⑨ ++(自增(后)) a=2;b=a++;——>a=3;b=2
⑩ --(自减(前)) a=2;b=--a;——>a=1;b=1
11 --(自减(后)) a=2;b=a--;——>a=1;b=2
12 +(字符串连接) “he”+“llo”——>“hello”
面试:
X=1%-5=1
X=-1%5=-1
b=a++;运算顺序:先把a的值进行赋值运算,赋给b,然后再自增。
字符串数据和任何数据使用+相连接,最终都会变成字符串。通常应用再打印数据中,是打印结果更直观。
转义字符:通过\来转变后面字母或者符号的含义。\n:换行。
\b:退格。相当于backspace。
\r:按下回车键。
\t:制表符。相当于tab键。
在linux系统里,换行由一个字符\n来表示。
在Windows系统里,换行由两个字符\n\r来表示。
在dos系统里,换行由一个字符\n来表示。
打印带双引号的hello:
System.out.println(“\“hello\””);
(2)赋值运算符
符号:=,+=,-=,*=,/=,%=
X+=4;把左边和右边的和赋给左边
X=X+4;把右边赋给左边
面试:
short s=3;
s=s+2;
s+=2;
有什么区别?
s=s+2;//编译失败,因为s会被提升为int类型,运算后的结果还是int类型,无法赋值给short类型,容易丢失精度。
s+=2;//编译成功,因为+=运算在给s赋值时,自动完成了强转操作。
(3)比较运算符
① == 相等于 4==3——>false
② != 不等于 4!=3——>true
③ < 小于 4<3——>false
④ > 大于 4>3——>true
⑤ <= 小于等于 4<=3——>false
⑥ >= 大于等于 4>=3——>true
⑦ Instanceof “hello”instanceof String——>true
比较运算符的结果都是boolean型,也就是要么true,要么false。
(4)逻辑运算符
① &(按位与)
只要两边的boolean表达式结果,有一个为false,那么结果就是false。只有两边都为false,结果位true。
② |(按位或)
两边只要有一个为true,结果为true。只有两边都有false,结果为false。
③ ^(按位异或)
两边相同结果是false。两边不同结果是true。
④ !(非)
⑤ &&(与)
⑥ ||(或)
&:无论左边是true还是false。右边都运算。
&&:当左边为false时,右边不运算。
|:两边都参与运算。
||:当左边为true时,右边不运算。
单&,单|连接boolean型表达式外,还有一个作用就是参与位运算。
6>x>3 => 6>x&&x>3
逻辑运算符用于连接boolean类型的表达式。
(5)位运算符
位运算是直接对二进制进行运算。
位运算是最快的运算。
计算机的底层运算都是二进制的。
① <<:其实就是乘以2的移动的位数次幂。
② >>:其实就是除以2的移动的位数次幂。
③ >>>:当我们向右移动时,高位空出来到底要补什么,看原来最高位是几,是1补1,是0补0,这是有符号右移。而>>>无符号右移,不管原来最高位是几,都补0。
④ &:全为1才为1,只要有一个是0都为0.
⑤ |:全为0才为0,只要有一个是1都为1。
⑥ ^:相同为0,不同为1。
PS:一个数异或同一个数两次,结果还是那个数。可用于加密。(“加密狗”)
⑦ ~:取反运算必须写出所有的二进制位数。
练习:^
对两个整数变量的值进行互换。
int n=3,m=8;
方法一:通过第三方变量。
int temp;
temp=n;
n=m;
m=temp;
方法二:不用第三方变量。
n=n+m;
m=n-m;
n=n-m;
Ps:如果n和m的值非常大,容易超出int范围。
方法三:^(技巧)
n=n^m;
m=n^m;
n=n^m;
练习:&
求一个数的二进制数。
System.out.println(Interger.toBinaryString(60));
二进制转化为十六进制:
0000-0000 0000-0000 0100-1100 1110-0110——>二进制
0 0 0 0 4 12 14 6——>十六进制
若通过程序求,怎么求?
现想得到最低4位,即有效位1:
0000-0000 0000-0000 0100-1100 1110-0110
&0000-0000 0000-0000 0000-0000 0000-1111
0000-0000 0000-0000 0000-0000 0000-0110
现想得到第二组低4位:
猜想:是不是可以将0000-1111中的0000 变为1111,1111变为0000,结果的低八位是1110-0000,把它作为十六进制数依次类推,将会超出十六进制范围,太多。所以由于低4位取完了可以舍弃,将原数向右移动4位,得:
0000-0000-0000 0000-0000 0100-1100 1110-0110
&0000-0000 0000-0000 0000-0000 0000-1111
0000-0000 0000-0000 0000-0000 0000-1110
则通过右移法将二进制位中每4位取出。上述思想是通用的,对于求一个数的二进制,八进制变现形式都是适用的。
比如用程序实现60的十六进制:
class OperateDemo
{
public static void main(String[] args)
{
//int num=60;
//获取60的最低4位,通过&15;
//num&15=12;
//要获取下一组4位,将60右移4位。
//int temp=60>>4;
//对temp的值进行最低4位的获取。
//temp&15=3;
int num=60;
int n1=num&15;//=12;
System.out.println((char)(n1-10+'A'));
int temp=60>>>4;//最好用>>>(适用于整数或负数才能把有效位移光)
int n2=temp&15;
System.out.println(n2);
//运行后:c
// 3
//而60=3c(16)倒着打印
}
}
有人问:你怎么知道num&15>9?
三元运算符。
(6)三元运算符
格式:
(条件表达式?)表达式1:表达式2;
如果条件为true,运算结果是表达式1;
如果条件为false,运算结果是表达式2。
实例:
获取两个数中的大数。
int x=3,y=4,z=0;
z=(x>y)?x:y;//z变量存储的就是两个数的大数。
因此:上例中两条输出语句可改为:
System.out.println(n1>9?(char)(n1-10+’A’):n1)
System.out.println(n2>9?(char)(n2-10+’A’):n2)
执行后:
67?
3
若把结果强转,若返回n2,n2被强转,结果不行,不合适。(该问题待留)
三元运算:三个元素参与运算的符号。
6、程序流程控制
(1)判断结构
判断结构虽然有很多行,但是是单条语句,是一个整体。 1)if(条件表达式)
{
执行语句;
}
当if控制的语句只有一条,大括号可以省略。若if后没有大括号,if只控制他最近的单条语句,但需要注意阶梯层次。作为初学者大括号保留。
2)if(条件表达式)
{
执行语句;
}
else
{
执行语句;
}
有点类似于三元运算,三元运算其实就是if-else语句的简写形式,但有区别。
if-else结构简写格式:变量=(条件表达式)?表达式1:表达式2;
三元运算符:好处,可以简化if-else代码,弊端,因为是一个运算符,所以运算完必须要有一个结果。
Ps:
int a=9,b=0;
b=(a>1)?System.out.println(“haha”):200;
编译失败。若成立,直接打印到屏幕上去,b没有结果,矛盾。
if(a>1)
System.out.println(“haha”);
else
b=200;
编译成功。
3)if(条件表达式)
{
执行语句;
}
else if(条件表达式)
{
执行语句;
}
...
else
{
执行语句;
}
它是单条语句,是一个整体。只要有一个满足条件,语 句结束。效率高,在有些程序中,比连续的if语句高效。它的特殊形式也可以是:
if(条件表达式)
{
执行语句;
}
else if(条件表达式)
{
执行语句;
}
练习:
根据用于制定月份,打印该月份所属的季节。
3,4,5 春季 6,7,8 夏季 9,10,11秋季 12,1,2冬季
class IFDemo
{
public static void main(String[] args)
{
int x = 2;
if(x==3||x==4||x=5)
System.out.println(x+"月份是春季");
else if(x==6||x==7||x==8)
System.out.println(x+"月份是夏季");
else if(x==9||x==10||x==11)
System.out.println(x+"月份是秋季");
else if(x==12||1==1||x==2)
System.out.println(x+"月份是冬季");
else//保证程序的健壮性(容错性)。
System.out.println(x+"月份不存在");
}
//以上条件中还可以写成区间的形式。
}
(2)选择结构
switch语句
格式:
switch(表达式)
{
case 取值1:
执行语句
break;
case 取值1:
执行语句
break;
case 取值1:
执行语句
break;
...
default:
执行语句
break;
}
switch语句特点:
a、switch语句选择的类型只有4种:byte,short,int,char。
b、Case之间与default没有顺序。先执行第一个case,没有匹配的case执行default。
c、结束switch语句的两种情况:遇到break,执行switch语句结束。
d、如果匹配的case或者default没有对应的break,那么程序会继续向下执行,运行可以执行的语句,知道遇到break或者switch结尾结束。
练习:
class SwitchTest
{
public static void main(String[] args)
{
//需求:根据指定的月份,打印该月份所属的季节。
//3,4,5 春季 6,7,8 夏季 9,10,11 秋季 12,1,2冬季
int x=4;
switch(x)
{
case 3:
case 4:
case 5:
System.out.println(x+"月份是春季");
break;
case 6:
case 7:
case 8:
System.out.println(x+"月份是春季");
break;
case 9:
case 10:
case 11:
System.out.println(x+"月份是春季");
break;
case 12:
case 1:
case 2:
System.out.println(x+"月份是春季");
break;
default:
System.out.println("nono");
//break;此处不需要break,已是结尾。
}
}
}
if-else-if与switch语句很像。如果判断的具体数值不多,而是符合byte、short、int、char这四种类型,虽然两个语句都可以使用,建议使用switch语句。因为效率稍高。其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广。
Ps:随着jdk不断升级,升级到5.0、6.0、7.0,7.0里面对switch语句的功能进行了增强。它不仅可以判断byte,short,int,char,还能判断字符串。而升级到5.0的时候,也稍微增强了一下,switch可对枚举类型进行判断。枚举类型是jdk5.0的新特征。Sun引进了一个全新的关键字enum来定义一个枚举类。下面就是一个典型的枚举类型定义:
public enum Color
{
RED,BLUE,BLACK,YELLOW,GREEN;
}
public class TestEnum
{
eunm Size{Small,Midum,Large};
public static void mian(String[] args)
{
System.out.println(Size.Large);
}
}
(3)循环结构
1)while
while(条件表达式)
{
执行语句;
}
2)do-while
do
{
执行语句;
}while(条件表达式);
综上所述:do-while无论条件是否满足,循环体至少执行 一次。
Ps:int y=1;
while(y<3);
{
System.out.println(“y=”+y);
Y++;
}
这样写没有问题,编译通过,大括号作为一个独立的代码块,可以单独存在,但意义不大,可以这么刺耳,y=1,y<3满足,后面没有循环体,继续回来判断y<3;一直判断,cpu一直处理相同的事情,程序会停在那不动。(“冒烟”就停了,呵呵)。若在public static viod
Main(String[] args)后面加分号,肯定挂。
3)for
格式:for(初始化表达式;循环条件表达式;循环后的操 作表达式)
{
执行语句;
}
Ps:初始化表达式不一定是int x=0;也可以是输出语句。但是循环条件不能是输出语句。若有多条语句可以用逗号隔开。但是如果for的初始表达式是int x=1,System.out.println;是不允许的。这中情况不清楚到底表达什么意思。且不符合int x,y;所以判错。
注:(1)for里面的表达式运行的顺序,初始化表达式只读一次,判断循环条件,为真就执行循环体,然后再执行循环后的操作表达式,接着继续判断循环条件,重复整个过程,直到条件不满足为止。
(2)while与for可以互换,区别在于for为了循环而定义的变量在for循环结束,就在内存中释放,而while循环使用的变量在循环结束后还可以继续使用。(变量有自己的作用域)
(3)最简单无限循环格式,while(true){},for(;;){}无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环。
练习:
a. 累加思想
//获取1~10的和,并打印。
class ForTest
{
public static void main(String[] args)
{
int sum=0;
for(int x=0;x<=10;x++)
{
sum+=x;
}
System.out.println("sum="+sum);
}
}
b. 计算器思想
class Fortest
{
public static void main(String[] args)
{
int count=0;
for(int x0;x<=100;x++)
{
if(x%7==0)
{
//System.out.println(“x=”+x);
count++;
}
}
System.out.println(“count=”+count);
}
}
(4)循环嵌套结构
语句嵌套其实就是语句中还有语句。比如for语句中还有if语句,这是比较简单的语句嵌套结构。循环嵌套结构就是循环中还有循环。
① 发展一:
class ForForDemo
{
public static void main(String[] args)
{
for(int x=0;x<3;x++)
{
for(int y=0;y<4;y++)
{
System.out.println("ok");
}
}
}
}
② 发展二:
class ForForDemo
{
public static void main(String[] args)
{
for(int x=0;x<3;++)
{
for(int y=0;y<4;y++)
{
System.out.print("*");
}
System.out.println();
//不能是System.out.print();
//一点意义都没有,相当于废话
//,Java会对其进行报错。
}
}
}
③ 发展三:
④ 发展四:
⑤ 发展五:
⑥ 发展六:
for(int x=1;x<=9;x++)
{
for(int y=1;y<=9;y++)
{
System.out.print(y+“*”+x+“=”+y*x+“\t”);
}
System.out.println();
}
⑦ 练习
class ForForTest
{
public static void main(String[] args)
{
for(int x=0;x<5;x++)
{
for(int y=x+1;y<5;y++)
{
System.out.print(“”);
}
for(int z=0;z<=x;z++)
{
System.out.print(“*”);
}
System.out.println();
}
}
}
(5)顺序结构(简单)
(6)其他流程控制语句
break(跳出) continue(继续)
break语句:应用范围:选择结构和循环结构。
continue语句:应用范围:应用于循环结构。
注:
① 这两个语句离开应用范围,存在是没有意义的。
② 这两个语句单独存在下面都不可以有语句,因为执行不到。如:
for(int x=0;x<3;x++)
{
break;
System.out.println(“x=”+x);——A
}
或者是:
for(int x=0;x<3;x++)
{
System.out.println(“x=”+x);
break;
System.out.println(“x=”+x);——B
}
A和B语句肯定执行不到,这句话叫作无法到达,或者叫作无法执行到,称之为废话。所以这两种情况是不可以存在的。
但是:
for(int x=0;x<3;x++)
{
if(x==1)
break;
System.out.println(“x=”+x);——C
}
这种情况是可以存在的,C语句是有可能被执行到的,因此不能称之为废话。
根据break的特点,我们可以总结出以上的几种情况;因此,我们根据continue的特点,我们也可以对应的总结出类似的几种情况。这里不在一一阐述。
③ continue语句是结束本次循环继续下一次循环。
④ break语句是跳出所在的最靠近的一层循环或者说是当前循环。
⑤ 标号的出现,可以让两个语句作用于指定的范围。标号只能用于循环结构,给循环起名字的一种形式。
比如在:
for(int x=0;x<3;x++)
{
for(int y=0;y<4;y++)
{
System.out.println(“x=”+x);
break;
}
}
我们可以给内循环和外循环分别加上标号。则变成下述代码。此时代码中break默认跳出内循环。
W:for(int x=0;x<3;x++)
{
q:for(int y=0;y<4;y++)
{
System.out.println(“x=”+x);
break;//等价于:break q;
}
}
但如果是下面这种情况则是跳出外循环。
w:for(int x=0;x<3;x++)
{
q:for(int y=0;y<4;y++)
{
System.out.println(“x=”+x);
break w;
}
}
根据break的特点,我们可以总结出以上的几种情况;因此,我们根据continue的特点,我们也可以对应的总结出类似的几种情况。这里不在一一阐述。
7、函数
(1)函数的定义
什么是函数:
函数及时定义在类中的具体特定功能的一段独立的小程序。 函数也称之为方法。
函数的格式:
修饰符 返回值类型 函数名(参数类型 形式参数1,参数 类型 形式参数2)
{
执行语句;
Return 返回值;
}
返回值类型:函数运行后的结果的数据类型。
参数类型:是形式参数的数据类型。
实际参数:是一个变量。用于存储调用函数时传递给函数的 实际参数。
return:用于结束函数。
返回值:该值会返回给调用者。
函数是一个功能,运算完往往会有一个结果,那个结果就是所谓的返回值。
比如:
class FunctionDemo
{
public static void main(String[] args)
{
System.out.println(3*3+5);
}
}
但是如果运算中的被乘数不确定就需要定义一个变量,则:
class FunctionDemo
{
public static void main(String[] args)
{
int x=4;
System.out.println(x*3+5);
x=6;
System.out.println(x*3+5);
}
}
通过以上程序可发现x*3+5的运算会重复地去使用,于是,将相同的部分进行抽取,封装成一个独立的功能,功能里边有对某一个数乘3加5的运算并打印的结果,你只要告诉我这个数,我就能帮你算。还有就是因为获取不同数据的运算结果,代码出现了重复。为了提高代码的复用性。对代码进行抽取。将这个部分定义成一个独立的功能。方便日后重复使用。Java中对功能的定义是通过函数的形式来体现的。需要定义功能,完成一个整数的*3+5的运算。
class FunctionDemo
{
public static void main(String[] args)
{
Int x=getResult(3);//用一个变量来记录这个结果。
System.out.println(“x=”+x);
}
public static int getResult(int num)
{
return num*3+5;
}
}
(2)函数的特点
① 定义函数可以将功能代码进行封装。
② 便于对该功能进行复用。
③ 函数只有被调用了才会被执行。
④ 函数的出现提高了代码的复用性。
⑤ 对于函数没有具体的返回值类型的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写,系统会自动加上,若写了,也不算错,但是应该是return;。(但是如果返回值类型不是void,就必须写,否则会编译失败。)
Ps:函数中只能调用函数,不可以在函数内部定义函数。定义函数时,函数的结果应该返回给调用者,交由调用者处理。具体还要看函数定义的功能是什么。
(3)函数的应用
如何定义一个函数,只需要搞清楚两个明确:明确要定义的功能最后的结果是什么;明确在定义该功能的过程中,是否需要未知内容参与运算(这就是所谓的形参,函数的调用也可以作为形参,这就更表明了有些封装操作来定义一个独立功能的必要性了,因为我们我不知道函数调用的结果具体数值是什么,但是我们直接可以往里边一扔)。
我们在编写程序其实就是在不断的实现功能,而java当中最小的功能单元就是函数,所以日后在写代码的时候,你只要在处理或者定义功能时,都把它们定义到独立的函数中去方便日后使用,而不要把代码乱七八糟都塞进主函数里边。因此,现在主函数的功能就是有一个,调用函数。我们用主函数调用写好的函数来运行测试。测试函数这个运行结果是不是正确的。
练习1:
Public static boolean compare(int a,int b)
{
if(a==b)
return a==b;
else
return false;
}
其实上述代码中的else关键字可以省略不写,若if语句中条件成立,rerun true,程序结束,不会再执行return false,写了也没关系。再者,上述代码中还可优化:return (a==b)?true:false;还可以直接return a==b;
练习2:
//定义一个功能,用于打印矩形。
class FunctionTest
{
public static viod main(String[] args)
{
for(int x=0;x<4;x++)
{
for(int y=0;y<5;y++)
{
System.out.println(“*”);
}
System.out.println();
}
//现在要打印一个8行9列的矩形,则直接复
//制一个上述代码,改一下数字即可。
for(int x=0;x<8;x++)
{
for(int y=0;y<9;y++)
{
System.out.println(“*”);
}
System.out.println();
}
//发现代码重复。若打印的矩形越多,代码就越复
//制,不利于应用。
}
}
对于以上问题,以及考虑到代码优化问题,采取以下解决办法:
class FunctionTest
{
public static void mian(String[] args)
{
draw(5,6);
printHr();
draw(7,9);
printHr();
}
public static void draw(int row,int col)
{
for(int x=0;x<8;x++)
{
for(int y=0;y<9;y++)
{
System.out.println(“*”);
}
System.out.println();
}
}
public static void printHr()
{
System.out.println(“-------------”);
}
}
嘿嘿。。。玩java玩啥?最基本的函数搞定就完事了,记住了,语句都定义在函数内,别莫名奇妙写出下列代码。
class Demo
{
for(int x=0;x<3;x++)
{
System.out.println(“hah”);
}
}
以上代码必定编译失败。非法类型开始。这代码是不可能执行起来的。它没有入口。虚拟机找半天没有找着入口。因此:得先有函数。若需要独立运行,就写主函数。若不需要,就不写主函数。
函数在写的过程中需要注意的一些小问题:
① 若返回值类型是void的函数,不能作为调用者中输出语句的参数进行输出。因为函数功能被运算完了以后,它没有具体的值,打印语句不知道打印什么。而返回值类型是其他类型的话是可以的。
② 不能逾越功能定义的范围,功能定义的思想要清楚,不能多定义功能,也不能少定义功能。
(4)函数的重载
重载的概念:在同一个类中,允许存在一次以上的同名函数,只要它们的参数个数或者参数类型不同即可。
重载的特点:
与返回值类型无关,只看参数列表。需要注意的是参数是有顺序的。并且函数名、参数相同,但是返回值类型不同的函数是不能同时存在于同一个类中的。
重载的好处:
方便于阅读,优化了程序设计。
重载的实例:
//返回两个整数的和。
int add(int x,int y){return x+y;}
//返回三个整数的和。
int add(int x,int y,int z){return x+y+z;}
//返回两个小数的和。
double add(double x,double y){return x+y;}
比如:
class FunctionOverload
{
Public static void main(String[] args)
{
add(4,5);
add1(4,5,6);
System.out.println(“hello world!”);
}
//定义一个加法运算,获取两个整数的和。
public static int add(int x,int y)
{
return x+y;
}
//定义一个加法,获取三个整数的和。
public static int add1(int x,int y,int z)
add
{
return x+y+z;
}
}
通过上述代码可以知道,add()和add1()原理相同。add的作用:用于标识函数,方便使用;用于通过该名称体现这个函数的功能。
之前说过,定义名称尽量有意义,意义就是为了阅读性而体现的。一看见函数名,就知道函数是干什么的。因此,新起的名add1,这样阅读起来其实很烂,虽然这样写是没有问题的,但是,阅读时就让我们很郁闷,add()和add1()功能到底有什么不同,没发现有什么不同。因此它们的原理都相同,算加法。这个时候,就不要再定义多余的名称,因为功能一致,所以用的功能名称也一致。那么都取名add,它还能区分吗?可以。但你调用add(4,5)时,它就去找名字为add方法,参数有两个整数的函数。自动寻找。虚拟机会帮助你自动识别。以上,即是所说的这个add方法,在这个类中就出现了重载形式。
在仔细观察以上代码,发现第二个add方法中,返回值是x+y+z,而x+y的运算第一个add方法就已经解决了这个问题,因此我们想到提高代码复用性的思想,将第二个add方法中的return后面的值改成return add(x,y)+z的形式,让一个函数调用另外一个函数。这种思想在打印乘法表的时候一样可以运用到。要知道打印44乘法表和打印99乘法表的区别在什么地方,它们唯一的不同就是传入的参数不同。
class FunctionzOverload
{
public static void main(String[] args)
{
Print99();
}
public static void print99(int num)
{
for(int x=1;x<=num;x++)
{
for(int y=1;y<=x;y++)
{
System.out.print(y+“*”+x+“=”+y*x+“\t”);
}
System.out.println();
}
}
//打印99乘法表
public static void print99()
{
print99(9);
}
}
总之,在重载的同时要注意代码优化,提高代码的复用性。
8、数组
(1)数组的定义
概念:同一中类型数据的集合。其实数组就是一个容器。
数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些元素。
格式1:
元素类型[] 数组名=new 元素类型[元素个数或数组长度]
示例:int[] arr=new int[5];
格式2:
元素类型: 数组名[]=new 元素类型[元素个数或数组长度]
示例:int arr[]=new int[5];
Ps:建议使用格式1和格式2定义格式。而且,它可以在数组元素不明确的的情况下使用,要进行分别赋值。
格式3:
元素类型[] 数组名=new 元素类型[]{元素1,元素2,元素3...}
示例:int[] arr=new int[]{3,5,1,7};
格式4:
元素类型[] 数组名={元素1,元素2,...}
示例:int[] arr={1,2,3,4};
Ps:格式3和格式4是静态初始化方式。在数据明确的情况下使用。
(2)数组的内存分配及特点
① 内存地址值
仔细分析:int[] x=new int[3];
new:它是用来在内存中产生一个容器实体,大家知道,数据得存,存哪?得先有个地儿,这个地儿,用来存储很多数据的地儿,用new操作符来完成。
int:代表元素的类型。
[]:代表数组。
X:该数组为了方便使用,就定义了一个名称叫x。并且x是数组类型。
new int[3]:在内存中定义一个真实存在的数组,能存储3个元素。
拓展:任何一个应用程序在运行的时候都需要在内存中开辟一个空间。Windows运行要占内存空间,运行qq也要占内存空间,开的程序越多,就越慢。原因;内存空间很大,开的程序越多,cpu处理的也就越多,所以会变慢。Cpu处理A程序,就不去处理B程序。处理玩A程序再处理B程序,B程序就要稍微等一下。
int[] x=new int[3];在内存中什么样?设计到java这个程序在启动虚拟机执行程序时它所开辟的空间了。
数组在内存中的结构:int [ ] arr=new int[4];
Java程序在运行时,需要在内存中分配空间。为了提高运算效率,有对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
栈内存:
用于存储局部变量,当数据使用完,所占空间会自动释放。
堆内存:
数组和队形,通过new建立的实例都存放在堆内存中。
每一个实体都有内存地址值。
实体中的变量都有默认初始化值。
实体不在被使用,会在不确定的时间内被垃圾回收器回收。
方法区、本地方法区、寄存器(后边再说)
② 默认的初始化值
拓展:凡是局部变量都在栈内存里面。什么叫局部变量?定义在方法中的变量,定义在方法上的那个参数变量、定义在for循环里边的局部变量都是局部的。凡是new出来的实体都在堆里边,堆里边存放的就是个实体。实体包括数组和对象。数组也可称之为对象。
堆和x是如何练习起来的呢?
堆内存中的每一个实体都有一个存放位置。内存里边都是地址值,都是二进制的那些地址,就是这些数字。用这个地址来标识数据存放的位置,那么,这个数组在内存中存放的位置,它总有一个起始地址,这个起始地址我们如果要用十六进制(它表示起来更短一些),这个地址假设是0x0079,该地址就作为这个数组在堆内存中存放的首地址值,用它来标识数组的位置时,只需要将其赋值给x,x就有值了,x它就指向了该数组,或者叫做x引用了这个数组。接下来,x指向这个数组以后,x就可以使用这个数组当中定义的这个[0][1][2]这个角标元素。怎么用?比如说:我想打印一下这个数组的[0]角标所对应的元素。有吗?没有!因为我们没往这个“大箱子里边的小格子”中放过数,那么能不能用呢?看:
class ArrayDemo
{
public static void main(String[] args)
{
int[] x=new int[3];
System.out.println(x[0]);
}
}
编译运行后:0;
此时,可以明确一点,原来这个数组一被定义,里边的元素都有值。这就是堆内存中的第二个特性,堆内存中这个实体是用来封装数据的,而堆内存实体中的数据都有默认初始化值,初始化值是几,那么要根据你所数据元素的类型而定,如果是int型数组默认为0;若double,0.0;若是float,0.0f;若boolean,false。
若不想让x指向数组,怎么做?
x=null;//x值为空。
(null是一个特殊的常量值,只有引用数据类型才能用null)
那么在内存中发生的变化如下;
一没关系后,这些数组元素在对内存里边就没人是用了,这时候,当一个实体在堆内当中没有任何引用所使用它的话,我们就视它为垃圾(java虚拟机就视它为垃圾),它不会立刻被内存中清除掉,而是在不定时的时间内启动一个垃圾回收机制将这个数组实体在对内存中清除。
③ 垃圾回收机制
Java垃圾回收机制:java语言本省是成长之c++,它优化了c++语言的一些特征而变得更简单易学易用。尤其在内存这一块,它的优化要比c++做的好,c++语言的特点在于,我们写了很多程序,运行的时候会在内存中开辟空间,当内存中的实体越来越多是,比较占用内存空间。哪一个内存不用了,是需要给它清除掉,把内存腾出来去存放其他的数据,c++的做法由程序员调用一个功能去将内存中这部分数据清除掉。若程序员忘做了,他写的程序运行时间越长,即程序越慢,甚至到最后死机,而java把这块给优化了,程序员不用手动清除,你只要这个对象或者这个实体在堆内存中变成了垃圾,java虚拟机会自动地启动垃圾回收机制将堆内存中不再被使用的实体清除掉。
练习:
int [] x=new int[3];
int [] y=x;
y[1]=89;
x[1]=77;
x=null;
请问:对内存中有没有垃圾?
Ps:以上是引用数据类型特点。而非引用数据类型就不一样了。
演示:
int a = 5;
int b=a;
b=8;
s.o.p(a);
演化:
(3)数组操作常见问题
问题一:
int[] arr=new int[3];
s.o.p(arr[3]);
虽然没有这个角标,但是编译通过。编译只检查语法错误。且编译的时候其实还没有完全建立数组,new的时候是在运行的时候建立数组,在堆内存中才会开辟一个数组空间,分配[0]、[1]、[2]这三个角标,当你去找[3]角标的时候,它发现数组中不存在才产生问题,因此运行失败。ArrayIndexOutofBundsException:3;
问题二:
int[] arr=new int[3];
arr=null;
s.o.p(arr[1]);
同样编译通过,运行失败。arr都不再指向数组了,和数组都没关系了,你怎么能操作数组中[1]角标的元素呢?而运行后存在问题。引用没有任何指向值为null的情况,该用引用还在用于操作实体。NullPointException。
问题三:
程序段:
int[] arr={3,6,5,1,8,9,6,7};
s.o.p(arr);
编译运行后:[[email protected]
[:指数组
I:指数组中的类型
deced:指数组的内存存放地址(哈希算法算出来的哈希值,目前不需要深究)
(4)数组中的操作
1)获取数组中的元素,将其打印到屏幕上。或者是求数组中所有元素的和,通常采用的方法是遍历。需要知道的是,当数组是采用静态初始化格式的时候,循环中的控制条件的参数不需要我们去一个一个的数,我们可以利用数组中一个属性可以直接获取到数组元素的个数,格式是:数组名.length。
for(int x=0;x<arr.length;x++)
{
............
}
2)格式化输出数组中的元素
class ArrayDemo
{
public static void main(String[] args)
{
int[] arr={3,6,5,1,8,9,6};
printArray(arr);
}
Public static void printArray(int[] arr)
{
System.out.println(“[”);
For(int x=0;x<arr.length;x++)
{
if(x!=arr[arr.length-1])
System.out.print(arr[x]+“,”)
else
System.out.println(arr[x]+“,”);
}
}
}
3)获取数组中的最大值以及最小值。
获取最值的思想只适用于数值型数据。如果想获取double类型的数组的最大值。因为功能一致,所以定义相同的函数名称。以重载的形式存在,注意提高代码的复用性。下面以获取数组的最大值为例进行讲解,获取最小值可以对应思考。
获取数组中的最大值有两种方式:
方法一:让变量记录住数组中的元素值。
class ArrayTest
{
public static int getMax(int[] arr)
{
int max=arr[0];
for(int x=1;x<arr.length;x++)
{
if(arr[x]>max)
{
max=arr[x];
}
}
return max;
}
public static void main(String[] args)
{
int[] arr={5,1,6,4,2,8,9};
int max=getMax(arr);
System.out.println(“max=”+max);
}
}
针对上述代码,我们需要知道的是,getMax()中for循环的初始条件也可以是int x=0;但是我们已经将max初始化为arr[0],因此本身没有必要再和本省比较。还有就是,主函数和功能函数中的变量max不会发生冲突,因为变量有其作用域。
方法二:让变量记录住数组元素的角标。
4)对给定的数组进行排序
① 选择排序
而且这个给数组排序的方法不需要返回值,我们也可以画图分析:
所以我们可以写出下列代码:
完整的代码,就是在主函数中调用它就行了。再完美一点就是,排序前打印一次,排序后打印一次,注意格式化打印就行了。打印数组中元素的方法在前面已经写过了。
② 冒泡排序(面试)
排序算法有很多,最快,效率最高的排序方式是希尔排序。只要求了解,不需要写出代码。上面介绍的两种排序算法面试中是经常问到的,并要求写代码。在数组元素不多的情况下,上述方法是可以的,但性能不高,代码却很好记。
我们观察到上述图片中,每一次符合条件我们都会在数组当中堆组内部的元素进行位置的置换,而堆内存中换位置是比较消耗资源的,当然不是特别大。我们能不能说,如果它们需要换位置的话,我们先不置换它们的位置,我们先把它们需要置换位置的角标以及元素在栈内存中定义两个变量临时存储,记录下来。当我们全部比较完了以后,我取最终需要换位置的值就可以了。该种代码真实开发不多见。但真实开发碰到排序了得写啊。我们可以在文件开头写上import java.util.*;然后在函数中加上Array.Sort(arr);即可完成排序。Java语言就怕你不会排序,给你提供了一个功能。呵呵。。。真实开发时使用它来完成排序。但是之前的代码不是没有必要去学。学了代码有两个目的,第一个我们能明白一些排序算法,第二个应付面试。
再观察以上图片中的代码,我们发现它们的共性之处就是都要交换位置。因此可以将它们封装成一个功能,提高代码复用性。则:
5)数组的查找
查找按照我们一般性的思想,想要找一个元素在数组中的位置,最简单的方式:在遍历的过程中进行判断。
比如:定义一个功能,获取key第一次出现在数组中的位置。如果返回-1,那么代表该key在数组中不存在。
public static int getIndex(int[] arr,int key)
{
for(int x=0;x<arr.length;x++)
{
if(arr[x]==key)
return x;
}
return -1;
}
问:没有找到为什么是返回-1。我们知道数组角标从0~...,都是正数,拿-1表示没有被找到的情况。
这种方式是ok的,但是它从头往后找,给它提高点效率该怎么做呢?
折半查找。但是,必须要保证该数组是有序的数组。
第一种方式:
第二种方式:
6)数组的插入
练习:(面试)
注意到,这个和查找算法唯一的区别就是,最后返回值是mid,而不是-1。
插入算法一般不要求写出代码,只问你怎么办。因此回答:先用折半查找查找这个数在数组中存在的位置,如果存在就在这个位置上插入,若不存在,返回最小角标的值,就可以得到要插入的位置。
(5)用数组解决进制转化中遗留的问题
1)十进制转化成二进制
比如现在我们相求60的二进制表现形式,一般的话我们肯定会这样做:
class ArrayTest
{
public static void main(String[] args)
{
toBin(60);
}
//十进制——>二进制
public static void toBin(int num)
while(num>0)
{
System.out.println(num%2);
num>>1;//num=num/2;
}
}
可是,运行的结果却不尽如人意,是011,而60的二进制表现形式是110,我们需要将它倒着打印才行。我们很容易想到用数组来实现,但是,现在我们先不用它,先来个与数组功能类似却更容易使用的方法。我们可以这样做:
class ArrayTest
{
public static void main(String[] args)
{
toBin(60);
}
//十进制——>二进制
public static void toBin(int num)
{
//容器类StringBuffer
StringBuffer sb=new StringBuffer();
while(num>0)
{
sb.append(num%2);
Num>>1;
}
System.out.println(sb);//打印的还是011
System.out.println(sb.reverse());//打印110
}
通过代码我们可以知道,为什么我们要使用StringBuffer类,它提供了一个倍牛的功能reverse()。
2)十进制转化成十六进制
下面我们来求60的十六进制表现形式,因为原理和求十进制的二进制表现形式相同,因此,我们也可以用上述方法来做。但是,现在我们换一种方式来做。这种方式当然也可以适用于十进制转化成二进制。
class ArrayTest
{
public static void main(String[] args)
{
toHex(60);
}
public static void toHex(int num)
{
StringBuffer sb=new StringBuffer();
for(int x=0;x<8;x++)
{
int temp=num&15;
if(temp>9)
sb.append((char)(temp-10+’A’));
else
sb.append(temp);
num=num>>>4;
}
System.out.println(sb.reverse());
}
}
运行结果:0000003c
查表法的引入:
我们通过求十进制转化成二进制的代码可以看到,temp-10+‘A’其实还是比较难想的。因此我们可以通过查表法来技巧性的实现。
用查表法求十进制转化成十六进制:
class ArrayTest
{
public static void main(String[] args)
{
toHex(60);
}
public static void toHex(int num)
{
char[] chs={‘0’,’1’,’2’,’3’,’4’,
‘5’,’6’,’7’,’8’,’9’,
‘A’,’B’,’C’,’D’,’E’,
‘F’};
StringBuffer sb=new StringBuffer();
for(int x=0;x<8;x++)
{
int temp=num&15;
sb.append(chs[temp]);
num=num>>>4;
}
System.out.println(sb.reverse());
}
}
运行结果:0000003c
到这里我们在代码中都在使用一个容器类StringBuffer,虽然简单好用,但现在我们想通过数组来实现。因此可以:
class ArrayTest
{
public static void main(String[] args)
{
toHex(60);
}
public static void toHex(int num)
{
char[]chs={‘0’,’1’,’2’,’3’,’4’,
‘5’,’6’,’7’,’8’,’9’,
‘A’,’B’,’C’,’D’,’E’,
‘F’};
//定义一个临时容器。
char[] ar=new char[8];
for(int x=0;x<8;x++)
{
int temp=num&15;
arr[x]=chs[temp];
num=num>>>4;
}
//存储数据的arr数组遍历。
for(int x=arr.length-1;x>=0;x--)
{
System.out.print(arr[x]+“,”);
}
}
}
运行结果是0,0,0,0,0,0,3,c
想去掉前面几个没有意义的0,则可以这样做:
上图中的代码的思想是正着存,倒着打印。现在我们可以倒着存,正着大印,可以看下图中的代码。
由于while中的条件是num!=0;所以也适用于求负数十进制数的其他机制。
综上所述,一个方法可以用于求十进制转化成二进制,那么也可以用于十进制转化成其他进制。
下面来写一个求十进制转化为任何其他进制的通用代码。
(6)数组中的数组
二维数组[][]
1)格式1:int[][] arr=new int[3][2];
① 定义了名称为arr的二维数组。
② 二维数组中有3个一维数组。
③ 每个一维数组中有2个元素。
④ 一维数组的名称分别为arr[0]、arr[1]、arr[2]。
⑤ 给第一个一维数组1角标位赋值为78的写法是:arr[0][1]=78。
2)格式2:int[][] arr=new int[3][];
① 二维数组中有3个一维数组。
② 每一个一维数组都是默认的初始化值null。
③ 可以对这个一维数组分别进行初始。
arr[0]=new int[3];
arr[0]=new int[1];
arr[0]=new int[2];
演示一:
演示二:
演示三:
ps:
int[] x,y[];
可以等价于:
int[]x;
int[]y[];
所以x是一维数组,y是二维数组。这种定义格式,只有在java考试,sun国际认证中会用到。
一维:int[]x;intx[];
二维:int[][]y;int y[][];int[]y[];
2015-08-05至2015-08-21著