原文写于 2016 年,个人学习笔记,闲来无事,搬运至此,希望于各位有用。主要内容是:关键字、标识符、注释、常量和变量、运算符、语句、程序结构、方法和数组等。
关键字(Key Words)
定义及组成
定义:被Java语言赋予特定含义的单词。
特点:组成关键字的字符全部是小写的。
全部关键字
注意:const和goto作为保留字,暂时不可使用。可能在JDK更高的版本中,提升为关键字
// 蓝色字体就是关键字
public class Demo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
P.S.
1、所有的关键字都不需要记忆,在后期的学习中会慢慢的掌握的
2、类名的首字母要大些,这是Java命名规范之一
3、写代码一定要注意代码规范,注重代码的阅读性
4、命名时,要做到见名知意,例如Abc就不是友好,Demo则可以
标识符(Identifier)
定义以及组成
定义:程序中自定义的一些名称。类、方法、接口、变量等起名字的字符序列。
组成:26个大小写字母、0~9数字、_和$
合法标识符规则
- (1)、数字不可以开头,例如:2Demo是不合法的
- (2)、不可以使用关键字,例如:public就不可以,public是关键字
- (3)、不可以使用除了
_
和$
之外的特殊字符
P.S.
1)、Java中严格区分大小写,例如:Demo和demo是不同的
2)、在起名字时,尽量做到见名知意,提高代码的阅读性
3)、公司中经常使用_
代表某一类名称,例如:_temp。通过$
分割主名称和子名称,例如:TrafficLamp$Red。
Java名称命名规范
(1)、 包名命名规范
『包:相当于是Windows下的文件夹,用于解决相同类名问题』
包名必须均为小写。包名需要使用英文说明包内代码功能,最好不要采用拼音。
- 以com.rupeng.elec.web为例:
- 第一段 com代表公司,org代表组织
- 第二段 rupeng为公司名称 如鹏
- 第三段 elec为项目名称
- 第四段 web为模块名称(分层)
(2)、类名命名规范
Java类名称必须以大写字母开头,可以使用2-4个英文单词(尽量不要用缩写)组成每个单词的首字母大写。文件名称必须能说明文件内代码功能。
- java类大致分为接口,实现类,模型,静态类型声明,逻辑类,工具类。
- 接口类命名:IXxx。
- 实现类命名:XxxImpl。 实现类的名称前段必须与接口一致说明是哪个接口的实现。
- 持久层类:XxxDao
- 业务层类:XxxService
- Action:XxxAction
- Servlet:XxxServlet
- 工具类:如StringUtil,DateUtil等。
- 也可以:StringTools
- java里面的 Arrays 和 Collections
(3)、方法名命名规范
- 方法名应该能够简单描述出该方法实现的功能,如deleteUser
- 方法名尽量不使用缩写,除非它是众所周知的,如可以使用db表示database
- 方法名可以有两个或三个单词组成,但尽量不要多于三个以上
- 方法名中由多个单词合成,第一个单词通常为动词,首字母小写,中间的每个单词的首字母都要大写,如删除用户:deleteUser
- 方法名中不要使用下划线
(4)、变量名命名规范
- 在程序中变量的名称要求能够体现出变量的功能,如果是单个单词那么变量名用小写,例如:email。
- 如果变量名是多个单词组成那么首字母小写,中间的每个单词的首字母都要大写。例如:isSuperUser; userLogin
- 如果是循环变量,那么变量名必须是一个小写字母,如:i,j,k。
- 变量名也尽量不使用缩写,且一定要有含义,不能使用没有表达意义的变量名,如:a1; a2; mmm。
(5)、常量名命名规范
常量名也应当有一定的意义,常量名字母全部大写。如果是由多个单词构成,可以用下划线隔开
- private final int PI=3.1415926;
- int YEAR;
- String TITLE = “xxxx”;
- int WEEK_OF_MONTH = 0;
注释
定义以及特点
定义:由于注解说明解释程序的文字。
特点:提高了代码的阅读性。
Java中的注释格式
(1)、单行注释
格式:
// 说明文字
(2)、多行注释
格式:
/*
* 说明文字
*/
(3)、文档注释
『Java特有』
格式:
/**
* 说明文字
* @author JoianSUN
*/
示例:
/**
* 我的 Hello World 程序。
* @author JoianSun
*/
class Demo
{
/*
* 主函数,是程序的入口。
* 它的出现可以保证程序的独立运行。
*/
public static void main(String[] args)
{
// 输出语句用于将括号内的数据打印到控制台。
System.out.println("Hello World");
}
}
P.S.
1)、对于单行和多行注释,被注释的文字,不会被JVM(java虚拟机)解释执行。所以,即使添加再多的注释,编译后生成的class文件占用硬盘字节多少不变。2)、对于文档注释,是java特有的注释,其中注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。
3)、注释是一个程序员必须要具有的良好编程习惯。初学者编写程序必须养成习惯:先写注释再写代码。
4)、将自己的思想通过注释先整理出来,再用代码去体现,因为代码仅仅是思想的一种体现形式而已。
5)、单行注释可以嵌套单行注释,单行注释可以嵌套多行注释,多行注释可以嵌套单行注释。但是,多行注释不能嵌套多行注释,因为多行注释的开头会和被嵌套的多行注释的结尾配对,导致后面的注释失效。
6)、可以使用注释对代码中的错误进行定位。
方法:当程序运行报错时,将某些代码注释掉,然后重新编译,运行。如果程序不再报错,那么说明注释掉的部分代码中包含错误代码。
常量
定义
定义:程序执行过程中其值保持不变。
Java中常量的分类
1、字面值常量:
- 1)、整数常量:所有整数。
- 2)、小数常量:所有小数。
- 3)、布尔(boolean)型常量:只有两个数值,true、false。
- 4)、字符常量:将一个数字字母或者符号用单引号(’’)标识,如:‘a’。
- 5)、字符串常量:将一个或者多个字符用双引号(“”)标识,如:“helloworld”、“a”、“”(空字符串)。
- 6)、null常量:只有一个数值就是:null。
2、自定义常量(final关键字时讲解)
进制的由来
对于整数,有四种表现形式:
- 二进制:0-1,满2进1。
- 八进制:0-7,满8进1,用0开头表示,如:012。
- 十进制:0-9,满10进1。
- 十六进制:0-9,A-F,满16进1,用0x开头表示。如:0x4A2C。
任何数据在计算机中都是以二进制的形式存在的,二进制早期由电信号开关演变而来。一个整数在内存中一样也是二进制的,但是使用一大串的1或者0组成的数值进行使用很麻烦。所以就想把一大串缩短点,将二进制中的三位用一位表示。这三位可以取到的最大值就是7,超过7就进位了,这就是八进制。但是对于过长的二进制变成八进制还是较长,所以出现的用4个二进制位表示一位的情况,四个二进制位最大是15,这就是十六进制。
规律:进制越大,表现形式越短。
P.S.
- 1Byte=8bit
- 1KiB=1024Byte
- 1MiB=1024KiB
- 1GiB=1024MiB
进制的基本转换
(1)、十进制转二进制
原理:对十进制数进行除2运算。
示例: 求十进制数6的二进制数。
6/2=3余0
3/2=1余1
1/2=0余1
故,6(十进制)=110(二进制)。
(2)、二进制转十进制
原理:二进制乘以2的过程。
示例: 求二进制数110的十进制数。
110= 020 + 121 + 1*22 = 0 + 2 + 4 = 6
附:括号中的数值代表次方。
(3)、十进制转八进制
原理:八进制,其实就是二进制位,3个二进制位,1个八进制位。
示例: 求十进制数43的八进制数。
十进制43
二进制101011
三位分割000-101-011
八进制053
因此,43(十进制)=101011(二进制)=053(八进制)。
(4)、十进制转十六进制
原理:十六进制,其实就是二进制位,4个二进制位,1个十六进制位。
示例: 求十进制数43的十六进制数。
十进制43
二进制101011
四位分割0010-1011
十六进制2(2)11(B)
因此,43(十进制)=101011(二进制)=0x2B(十六进制)
负数的进制
原理:负数的二进制表现形式就是对应的正数二进制取反加1。
示例: 求-6的二进制表现形式,其实就是对应的正数6的二进制取反加1。
6
00000110
取反:11111001
加 1:00000001
--------------------
11111010
P.S. 负数的二进制最高位永远是1。
变量
概念
定义:内存中的一个存储区域,该区域有自己的名称(变量名)和类型(数据类型),该区域的数据 可以在同一类型范围内不断变化。
特点:变量其实就是将不确定的数据进行存储,也就是需要在内存中开辟一个空间。
为什么要定义变量?
用来不断存放同一类型的常量,并可以重复使用。
定义变量的格式: 数据类型 变量名 = 初始值 ;
例如:byte b = 3;
P.S.
1)、格式是固定不变的,记住格式,以不变应万变
2)、变量的作用域(一对 { } 之间有效)
3)、变量只能存放某一种类型的数据
Java语言的数据类型
Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型。在内存中分配额了不同大小的内存空间:
类型 | 字节数 |
---|---|
byte | 1 |
short | 2 |
int | 4 |
long | 8 |
float | 4 |
double | 8 |
char | 2 |
boolean | 1 |
Java语言的数据类型包括8种基本类型、3种引用类型。
P.S.
1)、整数默认类型:int类型; 小数类型默认:double类型 【很重要】
2)、double类型的小数值精度比float类型的小数更高。
常见错误
错误示例1:
class VarDemo
{
public static void main(String[] args)
{
byte b = 3;
byte b = 8;
}
}
运行结果:“错误:已在方法中 main(String[] ) 中定义了变量b”
错误原因:同一个作用域中,同一个变量名只可以定义一次。
错误示例2:
class VarDemo
{
public static void main(String[] args)
{
long l = 123456789123;
System.out.println(l);
}
}
运行结果: “错误: 过大的整数:123456789123”
错误原因:由于整数类型默认是int
类型,如果超过了int
类型的范围,那么就会报错。即使是赋给long
类型的变量,但是由于后面的常量已经超出了int
的范围,同样导致错误。
解决方案:在数值后面加上一个L或l
(建议使用'L'
,可识别度 更高),就可以让编译器知道后面的常量是long类型的。
long l = 123456789123L;
错误示例3:
class VarDemo
{
public static void main(String[] args)
{
float f = 3.14;
System.out.println(f);
}
}
运行结果:“错误:不兼容的类型:从double转换到float可能会有损失”
错误原因:由于小数类型默认是double
(8 byte)类型,赋值给float
(4 byte)类型的变量,可能会损失精度,所以编译通不过。
解决方案:在数值后面加上一个F或f
(建议使用'F'
)让编译器知道后面的常量是float
类型
float f = 3.14F;
错误示例4:
class VarDemo
{
public static void main(String[] args)
{
int y;
System.out.println(y);
}
}
运行结果:“错误:可能尚未初始化变量y”
错误原因:变量需要必须先初始化,才可以使用
错误示例5:
class VarDemo
{
public static void main(String[] args)
{
int y = 1;
}
System.out.println(y);
}
运行结果:“错误:需要标识符”
错误原因:变量在其作用域{ }
之内才有效,可以使用。System.out.println(y);
已经超出了 变量y 的作用域,所以 此处的 y 是没有定义的。需要定义标识符 y 。
错误示例6:
class VarDemo
{
public static void main(String[] args)
{
char ch = 'a';
System.out.println(ch);
}
}
运行结果:“错误:非法标识符:‘\uff1b’”。
错误原因:编写程序时,不能使用中文符号。 char ch = 'a';
中的’;’ 是中文下的分号。
类型转换
类型转换在开发中也很常用,简单来说就是类型之间相互的转化,类型转换共分两种,自动类型转换『隐式类型转换』和强制类型转换『显示类型转换』。
(1)、自动类型转换『隐式类型转换』
定义:自动类型转换就是不需要我们手动对类型来进行强制转换。从小到大的转换
byte,short,char–>
int
–>long–>float–>double
byte,short,char三者之间不进行任何转换,运算时,先转换为 int 再运算
示例:
class VarDemo
{
public static void main(String[] args)
{
int x = 3;
byte b = 5;
x = x + b;
System.out.println(x);
}
}
运行结果: 8
说明:int类型的变量占4个字节,当byte类型的变量与其相加的时候,首先会将byte类型的变量自动转化为4个字节的int类型,然后再进行加法操作。
(2)、强制类型转换『显示类型转换』
定义:强制类型转换需要把类型进行手动转换,否则无法正常使用。是一种从大到小的转换
示例:
class VarDemo
{
public static void main(String[] args)
{
byte b = 3;
b = b + 200;
System.out.println(b);
}
}
运行结果:“错误:不兼容的类型:从int转换成byte可能会有损失”
错误原因:当byte类型的变量提升为int类型和int类型的常量200进行相加后,结果依然是int类型,再赋值给byte类型,当然会出现损失精度的错误。
解决方法:进行强制类型转换,也就是将占4个字节的int类型值,再强硬存储到占1个字节的byte变量中。
代码如下:
b = (byte)(b + 200);
运行结果: -53
说明:结果为-53的原因是,200+3结果是int类型的203,也就是二进制(四个字节)的 00000000 000000000 00000000 11001011,截取成占1个字节空间的byte类型的二进制也就是11001011。由于首位为1,所以是负数,转换为原码:除符号位外,其余部分取反加1,得1,0110101,原值是 -(00110101)即-53。
P.S.
明确:只有数值类型才能进行四则运算,非数值类型不可以。
示例:
class VarDemo
{
public static void main(String[] args)
{
System.out.println(true + 1);
}
}
运行结果:“错误:二元运算符 “+” 的操作数类型错误 【第一个类型:int 第二个类型:boolean】”
P.S.
明确:字符’0’的数值:49 字符’a’的数值:98 字符’A’的数值:66
char 类型数据也可以和 int 类型相加,但是首先 char 类型数据会被自动提升为 int 类型。
示例:
class VarDemo
{
public static void main(String[] args)
{
System.out.println('a' + 1);
System.out.println('你' + 1);
}
}
运行结果:
98
20321
说明:字符类型数据之所以能够自动提升为int类型是因为字符类型数据在计算机中也是用0、1表示的,int类型数据在计算机中也用0、1表示,所以char类型数据当然可以转换为int类型数据。但是,字符类型数据在计算机中使用0、1表示是按照何种顺序组合排列的则需要依据某个码表而定。Java中的内置码表是Unicode
,既包含中文,也包含英文。
P.S.
通过强转也可以把数字强转成字符。
示例:
class VarDemo
{
public static void main(String[] args)
{
System.out.println((char)('a' + 1));
}
}
运行结果: b
表达式的数据类型自动提升:
所有的byte型、short型和char的值将被提升到int型
如果一个操作数是long型,计算结果就是long型
如果一个操作数是float型,计算结果就是float型
如果一个操作数是double型,计算结果就是double型
面试题
1、说出报出如下错误的原因:
class VarDemo
{
public static void main(String[] args)
{
byte b = 3 + 7;
byte b1 = 3;
byte b1 = 7;
b = b1 + b3;
System.out.println(b);
}
}
运行结果: “错误:不兼容的类型:从int转换成byte可能会有损失”
错误原因:涉及到编译器编译程序时候的细节,之所以byte b=3+7;,没有报错,是因为3和7都是常量,编译器知道结果是10,并且在byte范围之内,因此就自动进行了强转,所以不会报错『如果是3+187的话,会报错:可能会损失精度』
。而b=b1+b2;
中b1和b2都是变量,编译器编译程序是一行一行编译的,它根本不知道b1和b2到底是多少,两个byte类型的数据相加时,首先都会被提升为int类型,他们的和也是int类型,其值可能会超过byte的范围,因此就会报错。
常量值的计算是在编译期间完成,变量值的运算是在运行时确认
。
而以下程序不会报错:
class VarDemo
{
public static void main(String[] args)
{
int x;
int x1 = Integer.MAX_VALUE;
int x2 = 2;
x = x1 + x2;
System.out.println(x);
}
}
说明:int 类型的两个变量相加,最后还是int类型,虽然结果溢出,但是编译时不会报错,因为是运行时错误。
2、说明以下问题:
byte b = 130 ; 可否通过编译?为什么?如何改进?改进之后的结果又是多少呢?请说明原因?
- 参看"(2)、强制类型转换『显示类型转换』"示例
3、写出以下代码片段的结果:
System.out.println("hello" + 'a' + 1);
System.out.println('a' + "hello" + 1);
System.out.println('a' + 1 + "hello");
解答:
helloa1
ahello1
99hello
4、问答题:Java语言中
char
是否可以存储中文汉字,为什么?
解答:可以,因为中文汉字是2个字节组成,而Java中的
char
也是2个字节长度
Java 语言使用的是 全球语言统一编码(Unicode
)
运算符
算术运算符
示例1:
class OperatorDemo
{
public static void main(String[] args)
{
int x = 23;
x = 23 /12 + 1;
System.out.println(x);
}
}
>>> 2
说明:整数与整数相除的结果,永远是整数,小数部分被忽略!
示例2:
class OperatorDemo
{
public static void main(String[] args)
{
System.out.println(-5 % 2);
System.out.println(-5 % -2);
System.out.println(5 % 2);
System.out.println(5 % -2);
}
}
>>> -1
-1
1
1
说明:负整数与任意非0整数取余的结果是负整数;正整数与任意非0整数取余的结果是正整数!
示例3:
class OperatorDemo
{
public static void main(String[] args)
{
int a = 3;
int b = a++;
System.out.println("a = " + a + " b = " + b);
}
}
>>>> a = 4 b = 3
说明:计算机中的实际操作是:当执行
b = a ++
语句时,先把a的值放在一个临时内存空间中,然后将 a 自身加1,再将内存空间中的a赋值给b,因此b还是a原来的值,也就是3(注意:使用的是左运算
)
示例4:
class OperatorDemo
{
public static void main(String[] args)
{
int a = 3;
int b = ++a;
System.out.println("a = " + a + " b = " + b);
}
}
>>> a = 4 b = 4
说明:当执行语句
b = ++a
时,现将a的值加1,再将a赋值给b,也就是4 .
赋值运算符
符号:=、+=、-=、*=、/=、%=
明确:a += b 的作用 相当于 a = a + b;(其它运算符 同理类比)
因为各种赋值运算符,具有相同性质,重点解析+=
示例1: 比较 s = s + 4 和 s += 4 的区别
class OperatorDemo
{
public static void main(String[] args)
{
short s = 3;
s = s + 4;
System.out.println("s = " + s);
}
}
运行结果:“错误:不兼容的类型:从int转换成int可能会有损失”
说明:在执行s=s+4;语句时,编译器在编译的时候,默认并没有强制类型转换。所以,s是short类型,4是int类型,s会自动提升为int类型,相加的和也是int类型,赋值给short类型的变量必然造成损失精度
的错误。这时候就需要进行强制类型转换: s = (short)(s + 4);
。
那如果将 s = s + 4;
替换为 s += 4;
,结果又会是什么样子的呢?
运行结果:s = 7
;这是为什么呢?
说明:在执行**s+=4;
**语句时,编译器在编译的时候,默认进行了强制类型转换,也就是将int类型的数据转换成short类型的数据。
s+=4;
等价于s = (short)(s + 4);
而非s = s + 4;
比较运算符
P.S.
比较运算符结果是boolea类型值 也就是 true 或 false
注意比较==
和=
示例1
class OperatorDemo
{
public static void main(String[] args)
{
int a = 3;
int b = 2;
boolran c = true;
System.out.println(a > b);
System.out.println(a == b);
System.out.println( c = false);
System.out.println( c == false);
}
}
>>> true
false
false
false
说明:前面打印的两句很好理解,重点是后两句,虽然都是false但是完全不同,第三句打印的是c的新值false,第四句打印的是c和false等于比较的结果
。
// System.out.println( c = false);
// 实际上等价于:先赋值、再打印c的新值
c = false;
System.out.println(c);
逻辑运算符
逻辑运算符用于连接两个boolean类型的表达式
运算规律:
- &:同为true结果为true,否则false
- | :存在为true结果为true,否则false
- ^ :相同为false,不同为true
- ! :!(true)为false,!(false)为true,!!(true)=true
- &&:同为true结果为true,否则false
- ||:存在为true结果为true,否则false
重点在于比较
&&
和&
以及||
和|
的区别1、
&&
和&
运算结果一样
2、&&
具有短路的效果,当左边为false,右边的不参与运算,提高效率;&
不具有短路效果,无论左边的是否为false,右边依然参与运算
1、
||
和|
运算结果一样
2、||
具有短路的效果,当左边为true,右边的不参与运算,提高效率;|
不具有短路效果,无论左边的是否为true,右边依然参与运算
鉴于运行效率,使用中,多为 "&&" 和 "||"
示例:
class OperatorDemo
{
public static void main(String[] args)
{
int x = 3;
int y = 6;
System.out.println("x = " + x + " y = " + y);
if( y > 5 & (++x) == 5 )
{
System.out.println("Hehe");
}
System.out.println("x = " + x + " y = " + y);
}
}
>>> x = 3 y = 6
x = 4 y = 6
说明:结果显示 if 语句块并没有执行,但是x的值加1,因为
&
没有短路,换成&&
,结果又会怎样呢? x的值还是3
位运算符
P.S.
位运算符是直接针对二进制位进行运算。
&
运算的例子:
6 & 3 = 2
110与011按位&
得到010,也就是十进制下的2
用“与运算”可以很方便的获取某一个二进制数的其中其中几位数。
例如:获取17476二进制数的后四位
0100 0100 0100 0100
&0000 0000 0000 1111
---------------------------------------
0000 0000 0000 0100
-
|
运算的例子:
6 | 3 = 7
110和011按位|
得到111,也就是十进制下的7 -
^
运算的例子:
6 ^ 3 = 5
110和011按位|
得到101,也就是十进制下的5
一个数异或 同一个数两次,结果还是这个数
- 6 ^ 3 ^ 3 = 6
011和011异或的结果是000,再和6进行异或就是6本身
技巧
:利用异或运算可以实现数据简单地进加解密,例如对一张图片的所有数据异或3加密,那么这幅画就无法查看了,解密只需要再对图片的数据执行异或3操作即可。
-
!
运算:取反操作就是对二进制数值的每一位0变1,1变0。 -
<<
运算的例子:
3 << 2 = 12 相当于 3 * 22 = 12
3 << 3 = 12 相当于 3 * 23 = 24
总结:
- 左移几位其实就是该数据乘以2的几次方。
- <<:可以完成2的次幂的运算
>>
运算的例子:
6 >> 1 = 3 相当于 6 / 22 = 3
6 >> 2 = 2 相当于 6 / 23 = 2
总结: 右移几位其实就是该数据除以2的几次方。
>>
右移:高位出现的空位,原来是什麽就用什么来填补>>>
无符号位右移:高位出现的空缺永远用0
来填补
-24 >> 2 = -6
-24 >>> 2 = 1073741818
PS:计算机中乘除法的原理:左移、右移
三目运算符
格式:
(条件表达式) ? 表达式1 : 表达式2;
- 如果条件表达式为true,则执行表达式1
- 如果条件表达式为false,则执行表达式2
表达式:具有一定语法规则的语句。
示例1:获取两个整数中的较大值
max = (x > y) ? x : y;
示例2:获取三个整数中的最大值
max = (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z);
程序流程控制结构
顺序结构
【最基础的结构:自上而下执行】
选择结构
(1)、if语句
格式1:
if(条件表达式)
{
执行语句;
}
示例1:
class IfDemo
{
public static void main(String[] args)
{
int score = 70;
if(score >= 60)
{
System.out.println("you pass the exam");
}
}
}
如果if语句没写大括号,if 就只能控制离它最近的单条语句
建议永远不要省略{ }
,这样具有更好的阅读性
格式2:
if(条件表达式)
{
执行语句;
}
else
{
执行语句;
}
示例2:
class IfDemo
{
public static void main(String[] args)
{
int score = 70;
if(score >= 60)
{
System.out.println("you win");
}
else
{
System.out.println("you fail");
}
}
}
P.S.
三元运算符就是 if else 语句的简写格式
简写格式什么时候可以使用呢?
当 if else 运算之后,有一个具体的结果时,可以简写成三元运算符。但是,如果是 输出语句,就不可以使用三元运算符了
格式3:
if(条件表达式)
{
执行语句;
}
else if(条件表达式)
{
执行语句;
}
......
{
执行语句;
}
示例3:
class IfDemo
{
public static void main(String[] args)
{
int score = 70;
if(score >= 90)
{
System.out.println("A");
}
else if(score >= 80)
{
System.out.println("B");
}
else if(score >= 70)
{
System.out.println("C");
}
else if(score >= 60)
{
System.out.println("D");
}
else
{
System.out.println("E");
}
}
}
P.S.
if(条件表达式) 之后切记不可加分号";
",否则无论表达式真假,都不执行任何语句
if(条件表达式);
相当于if(条件表达式) {};
其后的{ System.out.println(" "); }
为局部代码块
**
局部代码块
**可以定义局部变量的生命周期,主要用来控制资源的释放,在移动端的开发中比较常见,因为移动端的资源是很宝贵的。
示例:
class IfDemo
{
public static void main(String[] args)
{
if(true);
{
int m = 100;
System.out.println("inner : " + m);
}
System.out.println("outter : " + m);
}
}
运行结果:“错误: 找不到符号 m ”【outter 处的 m】
说明:变量m是局部变量,生命周期仅在局部代码块括号中,当代码执行到局部代码块右括号外,变量m便消失了。局部代码块外的代码当然也就访问不到变量m了。
if语句特点:
- 1、每一种格式都是单条语句。
- 2、第二种格式与三元运算符的区别:三元运算符运算完一定要有值出现。好处是:可以写在其他表达式中。
- 3、条件表达式无论写成什么样子,只看最终的结构是否是true或者false。
(2)、switch语句
格式:
switch(表达式)
{
case 取值1:
执行语句;
break;
case 取值2:
执行语句;
break;
... ...
default:
执行语句;
break;
}
switch语句特点
- 1、
switch语句选择的类型只有四种:byte short int char;JDK5.0之后支持 枚举,JDK7.0之后支持String【面试题】
- 2、case与default没有顺序,先尝试第一个case,一次往下执行,没有匹配的case,就执行default
- 3、结束switch语句的两种情况:①遇到break;②执行到switch语句结束
}
- 4、如果没有匹配的case或default,没有对应的break,那么程序会继续执行下去,运行所有可以执行的语句,直到switch结束
}
- 5、进入switch语句后,执行顺序:自上而下,先执行case,依次往下,没有符合的case,最后再执行default。即使 default 定义在所有case之前,执行顺序不变
- 6、
default可以没有
示例1:
class SwitchDemo
{
public static void main(String[] args)
{
int score = 70;
char grade = ' ';
switch(score / 10)
{
case 9:
grade = 'A';
break;
case 8:
grade = 'B';
break;
case 7:
grade = 'C';
break;
case 6:
grade = 'D';
break;
// case 0-5 时 执行同一条语句 grade='E';
case 5:
case 4:
case 3:
case 2:
case 1:
case 0:
grade = 'E';
break;
default:
break;
}
System.out.println(grade);
}
}
示例2:case贯穿现象
class SwitchDemo
{
public static void main(String[] args)
{
int a = 2;
int b = 3;
switch(a)
{
default:
b++;
case 3:
b++;
case 4:
b++;
}
System.out.println(b);
}
}
运行结果: 6
总结:if和switch语句的应用:
if:
- 1)、针对常量值的判断【多常量值时,使用
switch
】- 2)、针对一个范围判断
- 3)、针对运算结果是 boolean 类型表达式的判断【执行语句返回的是值时,使用
三目运算符
】switch:
- 1)、针对常量值的判断
- 2)、值的个数通常是固定的
PS:针对常量值的判断,建议使用switch语句,因为switch语句会将具体的答案都加载进内存,效率相对较高。
循环结构
关键词:while、do…while、for
(1)、while语句
格式:
初始化语句;
while(判断条件表达式)
{
循环体语句;
控制条件语句;
//必须包含 控制条件语句,否则造成死循环
}
示例:
class WhileDemo
{
public static void main(String[] args)
{
int x = 1;
while(x < 3)
{
System.out.println("x = " + x);
// 控制条件语句
x++;
}
}
}
P.S.
一定要注意不要写while(x < 3);
相当于是while(x < 3) {};
,代表不执行任何语句遗失了控制条件语句
,这个循环就成了一个死循环。
(2)、do…while语句
格式:
初始化语句;
do
{
循环语句;
控制条件语句;
// 必须包含 控制条件语句,否则造成死循环
} while(判断条件语句);
示例:
class WhileDemo
{
public static void main(String[] args)
{
int x = 1;
do
{
System.out.println("x = " + x);
// 控制条件语句
x++;
}while(x < 3);
}
}
总结:对比while和do...while
- do…while语句的特点:无论条件是否满足,循环体至少执行一次
- while如果条件不满足,循环体一次都不会执行
(3)、for语句
格式:
for(初始化语句;判断条件语句;控制条件语句)
{
循环语句;
}
说明: for里面的三个表达式运行的顺序,
初始化语句只执行一次
,判断条件语句为真就执行循环语句,然后再执行控制条件语句,接着执行判断条件语句,重复整个过程,直到条件不满足为止。
示例:1~10之间的偶数
class FoDemo
{
public static void main(String[] args)
{
for(int i = 1; i <= 10; i++)
{
if(i % 2 == 0)
{
System.out.println(i);
}
}
/* 等价于:
* for(int i = 2; i <= 10; i += 2)
* {
* System.out.println(i);
* }
}
}
示例:统计100以内所有3的倍数的个数–统计思想
class CountDemo
{
public static void main(String[] args)
{
int count = 0;
for(int i = 0; i <= 100 ; i++)
{
if(i % 3 == 0)
{
count ++;
}
}
System.out.println(count);
}
}
>>> 33
『可以使用和求偶数一样的方式:for(int i = 3; i <= 100; i += 3){}』
示例:求N的阶乘N!–求积思想
import java.util.Scanner;
class MultiplyDemo
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int q = 1;
for(int i = 1; i <= n; i++)
{
q *= i;
}
System.out.println(q);
}
}
P.S.
Scanner
是键盘录入类,import java.util.Scanner;
将类所在的包导入;使用Scanner sc = new Scanner(System.in);
新建一个对象,sc.nextInt();
获取键盘录入的一个int类型值
示例:求1-N的总和–求和思想
import java.util.Scanner
class SumDemo
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int sum = 0;
for(int i = 1; i <= n; i++)
{
sum += i;
}
System.out.println(sum);
}
}
P.S.
for循环的初始化语句、控制条件语句可以有多个,之间使用,
隔开
总结:for和while对比
- 1、使用区别:如果初始化语句中的变量在循环之外还需使用的话,使用while;否则使用for,变量及早地从内存中消失,提高内存的利用率
『变量的作用域』
- 2、对于一个明确的范围,使用for;否则使用while
- 3、在不影响阅读性的基础上,建议使用for,而不使用while
注意:死循环
- 1、控制条件语句必须有,正确控制变量
- 2、最简单的两种死循环:
while(true)
、for(;;)
(4)、for嵌套
示例:输出正三角形
/* 需求:打印一下图形
* *
* **
* ***
* ****
* 分析: 第1行,输出1个'*'
* 第2行,输出2个'*'
* ......
* 第n行,输出n个'*'
* 实现: 用一个 i 控制第几行
* 用一个 j 控制打印的数目
* j 的范围应该由 i 来控制
* 每行输出完成之后,执行换行 System.out.println();
* 编码:
*/
class StarDemo
{
public static void main(String[] args)
{
for(int i = 1; i <= 4; i++)
{
for(int j = 1; j <= i; j++)
{
System.out.print('*');
}
System.out.println();
}
}
}
示例:输出99乘法表
/* 需求:打印以下格式的99乘法表
* 1×1=1
* 1×2=2 2×2=4
* 1×3=3 2×3=6 3×3=9
* 1×4=4 2×4=8 3×4=12 4×4=16
* ......
* 分析: 第1行 1×1
* 第2行 1×2 2×2
* 第3行 1×3 2×3 3×3
* ......
* 第n行 1×n 2×n 3×n ... n×n
* 实现: 用 i 控制第几行
* 用 j 控制该行有多少列
* j 的范围应该由 i 来控制
* 每行输出完成之后,执行换行 System.out.println();
* 编码:
*/
class TableDemo
{
public static void main(String[] args)
{
for(int i = 1; i <= 9; i++)
{
for(int j = 1; j <= i; j++)
{
System.out.print(j + "×" + i + "=" + i*j + "\t");
}
System.out.println();
}
}
}
> 1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
跳转控制语句
break(中断)、continue(继续)、return(返回)
goto作为保留字,不可使用
(1)、break(中断)
使用场景:switch结构中、循环语句中的 if 判断,离开上述场景毫无意义!
如何使用:
- ① 跳出单层循环:默认跳出最内层循环
- ② 跳出多层循环(几乎不用):结合标签使用
outer:for(int i = 0; i < 3; i++)
{
inner:for(int j = 0; j < 5; j++)
{
if(j == 2)
{
break outer;
// break inner;
}
}
}
(2)、continue(继续)
使用场景:循环语句中的 if 判断,离开此场景毫无意义!
如何使用:
- ① 跳出单层循环:
break
跳出当前循环;continue
跳出本次循环 - ② 跳出多层循环(几乎不用):结合标签使用
(3)、return(返回)
使用场景:方法中,用于退出当前方法体,跳转到上层调用的方法;在main方法中的return用于结束程序
综合示例:写出程序的执行结果
class GotoDemo
{
public static void main(String[] args)
{
for(int i = 1; i < 10; i++)
{
if(i % 2 == 0)
{
System.out.println("Exit");
____①;____
}
System.out.println(i);
}
System.out.println("Over");
}
}
>>> 分别写出 ① 为break、continue、return的结果
① 为 break
>>> 1
Exit
Over
① 为 continue
>>> 1
Exit
3
Exit
5
Exit
7
Exit
9
Over
① 为 return
>>> 1
Exit
PS:
以上三种语句应该放在局部代码块的最后,因为放于其后的代码永不执行
。
方法
定义
定义:类中完成特定功能的代码块。也称为函数,Java称为方法。
格式
修饰符 返回值类型 方法名(参数类型 形式参数1,参数类型 形式参数1,...)
{
方法体;
return 返回值;
}
>>> "参数类型 形式参数1,参数类型 形式参数1,..."也就是参数列表
同一个代码块在出现两次以上考虑定义方法。
P.S.
- 1、修饰符:目前只需要固定写成public static即可
- 2、返回值类型:返回值的数据类型『数值类型、引用类型』
- 3、方法名:标识符,方便调用
- 4、参数:实际参数:实际参与运算的变量,形式参数:方法上用于接收实际参数值的变量
- 5、参数类型:参数的数据类型
- 6、方法体:完成特定功能的代码块
- 7、return:结束方法,跳转到上层调用的方法
- 8、返回值:return返回给调用者
如何定义方法
方法的定义:格式 + 两个明确『参数列表、返回值类型』
方法的特点:
- 1、定义方法可以将功能代码进行封装
- 2、便于对该功能进行复用,提高了代码的复用性
- 3、方法不调用不执行
- 4、方法之间是平级关系,不可以嵌套定义
- 5、方法定义时,参数类表使用","分割
- 6、方法调用时,不传递参数类型
- 7、方法有明确的返回值
注意:
- 1、对于方法没有具体返回值的情况,返回值类型使用关键字void,该函数中的return语句可以省略不写,或者写上
return;
- 2、函数中只能调用函数,不可以在函数内部定义函数。否则,编译时期就会报错。
- 3、定义函数时,函数的结果应该返回给调用者,交由调用者处理。
对于第3点,请对比以下完成两个数值和的代码块:
public static void sum(int a,int b)
{
System.out.println(a+b);
}
public static int sum(int a,int b)
{
return a + b;
}
虽说两种都可以执行出正确的数据,下面的代码比较好,因为调用者只需要获取两数相加的结果,而不需要方法做打印的操作!
按照方法定义的要求:格式 + 两个明确,完成以下示例:
示例1:输出n列m行的"*"方法
/* 需求:输出n行m列的 *
* 例如:当n=3,m=4时
* ****
* ****
* ****
* 分析:n代表一共几行,m代表一行打印多少个
* 方法的需求中强调将其直接打印出来
* 可以明确返回值类型是void,
* 也就是说return可以省略不写或者使用return;
* n行m列,所以参数列表应该是(int n,int m)
*
* 综上,格式为:
* public static void PrintStar(int n,int m)
* {
* //return;
* }
*
* 编码实现
*/
import java.util.Scanner;
class StarDemo
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
System.out.println("Please input an integer(n):");
int n = sc.nextInt();
System.out.println("Please input an integer(m):");
int m = sc.nextInt();
PrintStar(n,m);
}
public static void PrintStar(int n,int m)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
System.out.print("*");
}
System.out.println();
}
}
}
示例2:输出nn乘法表(1~9)
/* 需求:输出nn乘法表,且n在1-9之间
* 借鉴之前的99乘法表
* 1×1=1
* 1×2=2 2×2=4
* 1×3=3 2×3=6 3×3=9
* ... ...
* 分析:根据需求中的方法的功能是输出,明确返回值类型是void,
* 返回值是return; 或者省略
* 明确是nn乘法表。所以方法的参数是一个int类型的n
* 第一个n确认的是行数,第二个n确认改行打印的程式的个数
* 综上,格式是:
* public static void PrintTable(int n)
* {
* //return;
* }
* 编码实现:
*/
import java.util.Scanner;
class TableDemo
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
System.out.println("Please inoput an integer:");
int n = sc.nextInt();
PrintTable(n);
}
public static void PrintTable(int n)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= i; j++)
{
System.out.print(j + "*" + i + "=" + j*i +"\t");
}
System.out.println();
}
}
}
示例3:获取两个整数中的最大数
/* 需求:获取两个整数中的最大数;
* 分析:明确参数列表是两个int类型的参数
* 返回值类型也是int类型
* 根据功能需求是获取,也就是需要把方法处理后结果返还给调用者
* 综上:格式为:
* public static int max(int a,int b)
* {
* return max;
* }
* 编码实现:
*/
class MaxDemo
{
public static void main(String[] args)
{
int a = 12;
int b = -1;
int max = max(a, b);
System.out.println(max);
}
public static int max(int a,int b)
{
return (a > b) ? a : b;
}
}
练习:判断两个整数是否相等
练习:获取两个整数的和
示例4:获取三个整数中的最大数
/*
* 需求分析参考上述示例3
*/
class MaxDemo
{
public static void main(String[] args)
{
int a = 12;
int b = -1;
int c = 23;
int max = max(a, b, c);
System.out.println(max);
}
public static int max(int a,int b,int c)
{
return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
}
}
对比示例3、4可以发现两个方法的功能相同,方法名相同,只是参数列表不同,具有类似功能的方法应该定义在相同的类中完成封装。这就引出了Java中的方法重载(OverLoad)。
方法重载(OverLoad)
方法重载(OverLoad):
- 同一类中,完成同样功能的方法,为了见名知意,允许具有相同的方法名,不同的参数列表,这种现象就叫做方法重载(OverLoad)。
- 不同的参数列表包含两种含义:参数的数据类型不同、参数的个数不同。
- 从定义中可以看到,重载只和参数列表有关,与方法的返回值无关。
调用时候,JVM(Java Virtual Machine)根据参数列表的不同来区分同名方法
示例:实现获取最大值的类
class MaxDemo
{
public static void main(String[] args)
{
int a = 12;
int b = -1;
int c = 23;
int max = max(a, b, c);
System.out.println(max);
}
public static int max(int a,int b,int c)
{
//return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
return max(max(a, b), c);
}
public static int max(int a,int b)
{
return (a > b) ? a : b;
}
public static float max(float a,float b)
{
return (a > b) ? a : b;
}
}
引用数据类型之一:数组(『一维数组』)
定义
定义:数组是存储同一数据类型的多个元素的集合(容器)
格式
- 格式1:数据类型[] 数组名 (推荐方式)
- 格式2:数据类型 数组名[] (C#已无)
初始化
数组初始化:为数组中的元素分配内存空间并为其赋值,Java中的数组必须先进行初始化才可以使用
- 1、
动态初始化
:只指定数组的长度(内存空间),由系统为数组分配初始值(0\null) - 格式:数据类型[] 数组名 = new 数据类型[数组长度]
- 案例:int[] arr = new int[9];
- 2、
静态初始化
:只指定每个元素的值,由系统为其指定长度(内存空间) - 格式:数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3…}
- 案例:int[] arr = new int[]{1,2,3,4,5,6,7,8,9};
new 为数组申请内存空间
数组的下标是从 0 开始,、最大值 为:length-1
堆栈的特点
Java中的内存分配:
- Java中程序在运行时,需要在内存中为其分配空间,为了提高效率,将内存空间进行了不同的划分,让每块区域都有自己特定的数据处理方式和内存管理方法。
大致可以分为以下5种:
1、栈:存储局部变量,当变量所属的作用域一旦结束,所占空间会自动释放
2、堆:通常new出来的数组或对象都放置在堆内存中
3、方法区:class文件内容
- 4、本地方法区:与系统相关
- 5、寄存器:共CPU使用
局部变量:在方法声明中或方法定义上的变量。
示例代码1:
class ArrayDemo
{
public static void main(String[] args)
{
int[] arr = new int[3];
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
>>> [I@659e0bfd // 地址值:"[" 代表是一位数组;"I" 代表是int类型;
// "@"之后的是映射的十六进制地址值
0
0
0 // 打印的三个元素值,是由JVM系统给定的初始值
内存图解:
堆内存的特点:
- A:每一个new出来的对象都有地址值
- B:每一个变量都有默认值
- C:使用完成(栈中没有指向它的变量)就成了垃圾,但并没有马上回收。在垃圾回收器空闲时回收。
注意:C++内类型的析构函数就是为了释放空间,但在Java中是自动进行的基本数据类型默认值:
- byte、short、int、long默认是 0
- float、double默认是 0.0
- char 默认是 ‘、u0000’ - 空字符
- boolean 默认是 false
- 引用数据类型 默认是 null
栈内存的特点
数据用完就释放掉(超出作用域"}
")
示例代码2:
{
int a = 10;
System.out.println(a) ;
} // 在这里 a 已经被释放了,之后再对 a 访问造成 未定义的错误
在示例代码2
的基础之上,进行以下的操作:
arr[0] = 100;
arr[1] = 100;
内存图解:
示例代码3:
class ArrayDemo
{
public static void main(String[] args)
{
int[] arr = new int[3];
int[] ar = arr;
arr[0] = 100;
arr[1] = 100;
ar[0] = 10;
ar[1] = 20;
ar[2] = 30;
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
>>> 10
20
30
内存图解:
静态初始化:指定元素的值,由系统分配内存长度
格式:数据类型[] 数组名 = new 数据类型[]{元素1,元素2…};
简写格式是:数据类型[] 数组名 = {元素1, 元素2…};
int[] arr = new int[]{1, 2, 3};
// int[] arr = {1, 2, 3};
内存图解:
切记:不可同时对数组进行动态和静态初始化,即:int[] arr = new int[3]{1, 2, 3}; 是不合法的。
数组常见问题
Exception:异常
- 1、识别
现象
- 2、分析
原因
- 3、如何
改进
-
(1)、ArrayIndexOutOfBoundsException:数组索引(下标)越界异常
-
原因:数组的下标不在
[0,length-1]
之间 - 改进:对数组进行访问时,时刻注意索引越界异常 (2)、NullPointerException:空指针异常
-
原因:数组不再指向堆内存,却还使用数组名访问元素;譬如:
int[] arr = null;
- 改进:对数组进行访问时,时刻注意数组是否还可正常访问
数组常见操作
(1)、遍历
for(int i = 0; i < array.length; i++)
{
// 操作
}
>>> 数组名.length 是获取数组的长度 int类型值
(2)、获取最值
int max = array[0];
// 获取数组中的第一个元素值,作为参照
// 不可以使用非数组中元素作为参照,比如说:0
// 遍历之后的 [1,length-1] 元素,与参照对比
// 如果大于参照,就把他的值赋给参照
// 如果小于参照,则继续下一个元素的比较
for(int i = 1; i < array.length; i++)
{
if(array[i] > max)
{
max = array[i];
}
}
// 通过遍历全部元素之后,获得最大值
System.out.println(max);
>>> 数组名.length 是获取数组的长度 int类型值
PS:最小值的问题与此类似,请自行分析!
(3)、逆序(Reverse)
所谓的数组逆序,就是倒序;譬如{1, 2, 3}逆序为{3, 2, 1}
第1种实现:定义新的数组
// 定义新的同类型数组(使用元数组的长度动态初始化定义)
int[] rArray = new int[array.length];
// 遍历数组所有元素,实现倒序
for(int i = 0; i < array.length; i++)
{
rArray[array.length-1-i] = array[i];
}
// 实现比较简单:但是耗费了额外的内存空间。好处是没有改变原来数组
第2种实现:使用原数组
// 以中间元素为基准,将数组元素进行交换
// 第一个和倒数第一个,第二个和倒数第二个,以此类推
for(int i = 0; i < (array.length-1) / 2; i++)
{
// 实现两个正整数的交换-四种方式
// 第一种方式:使用中间变量
int t = array[i];
array[i] = array[array.length-1-i];
array[array.length-1-i] = t;
}
// 使用while实现:从两头向中间靠近,到达中间之后就停止
int start = 0;
int end = array.length-1;
while(start != end)
{
int t = array[start];
array[start] = array[end];
array[end] = t;
// 两头向中间靠近
start++;
end--;
}
(4)、数组查表法:以索引获取指定元素
方法实现:明确参数列表是(int[] arr,int index)
;返回值类型 int
类型
public static int getValue(int[] arr, int index)
{
if(index >= 0 && index < arr.length)
{
return arr[index];
}
else
{
// 暂时使用 -1 代表ArrayIndexOutOfBoundsException异常
return -1;
}
}
(5)、基本查找:查找指定元素在数组中第一次出现的位置
方法实现:明确参数列表是(int[] arr,int value)
;返回值类型 int
类型
public static int getIndex(int[] arr, int value)
{
for(int i = 0; i < arr.length; i++)
{
if(arr[i] == value)
{
return i;
}
}
// -1 表示数组中不存在该元素
return -1;
}
上述两个部分代码看似实现了功能,但是不具有很好的阅读性:
public static int getIndex(int[] arr, int value)
{
// -1 表示数组中不存在该元素
int index = -1;
for(int i = 0; i < arr.length; i++)
{
if(arr[i] == value)
{
index = i;
// 找到第一个元素之后,应该结束当前的循环
break;
}
}
return index;
}
参照getIndex,请自行修改方法getValue,使其具有更好的阅读性。
二维数组
定义
定义:元素是一维数组的数组
格式
- 格式1:数据类型[][] 数组名 = new 数据类型[m][n];由m个长度为n的一维数组组成的数组
: 数据类型[] 数组名[] = new 数据类型[m][n];
: 数据类型 数组名[][] = new 数据类型[m][n];
示例1:分析以下的代码片段:
int[] x, y[]; // x是一维数组,y是二维数组:拆开定义就显而易见了
// x是一维数组,y是二维数组:拆开定义就显而易见了
int[] x;
int[] y[];
class ArrayDemo
{
public static void main(String[] args)
{
int[][] arr = new int[3][2];
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[1][0]);
System.out.println(arr[1][2]);
}
}
>>> [[I@659e0bfd // "[[" 代表是二维数组,"I" 代表是int类型,
// "@"之后的是映射的内存地址
[I@2a139a55 // "[" 代表的是一维数组,其他的同上
[I@15db9742
[I@6d06d69c
0 // 堆内存中,int类型的默认值是 0
0
内存图解:
- 格式2:数据类型[][] 数组名 = new 数据类型[m][];由m个一维数组组成的二维数组,以为数组的长度动态给出
示例2:分析以下的代码片段:
class ArrayDemo
{
public static void main(String[] args)
{
int[][] arr = new int[3][];
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
arr[0] = new int[2];
arr[1] = new int[3];
arr[2] = new int[1];
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[1][0]);
System.out.println(arr[1][2]);
arr[1][0] = 100;
arr[1][2] = 200;
System.out.println(arr[1][0]);
System.out.println(arr[1][2]);
}
}
>>> [[I@659e0bfd // "[[" 代表是二维数组,"I" 代表是int类型,
// "@"之后的是映射的内存地址
null // 堆内存中,一维数组是引用类型,默认值是 null
null
null
0 // 堆内存中,int类型的默认值是 0
0
[I@2a139a55 // "[" 代表的是一维数组,其他的同上
[I@15db9742
[I@6d06d69c
100 // 完成了数组元素的赋值
200
内存图解
- 格式3:数据类型[][] 数组名 = new 数据类型[][]{{,…},{,…}…}
- 简化格式是:
数据类型[][] 数组名 = {{,..},{,..}...}
示例3:分析以下代码片段:
class ArrayDemo
{
public static void main(String[] args)
{
int[][] arr = {{1, 2, 3},{4, 5},{6}};
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[0][1]);
System.out.println(arr[2][0]);
}
}
>>> [[I@659e0bfd
[I@2a139a55
[I@15db9742
[I@6d06d69c
2
6
内存图解
初始化
动态初始化:『只指定数组的长度,具体的值由JVM虚拟机默认初始化』格式1、格式2
动态初始化:『只给出所有元素的值,数组的长度由JVM虚拟机给出』格式3
二维数组操作及应用
(1)、遍历
class ArrayDemo
{
public static void main(String[] args)
{
int[][] arr = {{1, 2, 3}, {4, 5}, {6}};
for(int m = 0; m < arr.length; m++)
{
for(int n = 0; n < arr[m].length; n++)
{
System.out.print(arr[m][n] + "\t");
}
System.out.println();
}
}
}
>>> 1 2 3
4 5
6
(2)、求和(求和思想+遍历)
class ArrayDemo
{
public static void main(String[] args)
{
int[][] arr = {{1, 2, 3}, {4, 5}, {6}};
int sum = 0;
for(int m = 0; m < arr.length; m++)
{
for(int n = 0; n < arr[m].length; n++)
{
sum += arr[m][n];
}
}
System.out.println(sum);
}
}
>>> 21
(3)、杨辉三角性
-综合案例
/*
* 需求:根据用户输入的行号,输出杨辉三角性
* 譬如:输入行号为 5
* 输出: 1
* 1 1
* 1 2 1
* 1 3 3 1
* 1 4 6 4 1
* 1 5 10 10 5 1
*
* 分析:使用二维数组作为数据结构
* 输出的行数是输入的行号+1
* 每行的列数和该行行号相同
* 第一列的数字均为1,对角线的数字均为1
* 从第三行开始,除掉第一数字1、最后一个数字1;
* 从第二个数字开始,该数字的值等于上一行的
* 该列数字和它前一列的数字之和
*
* 实现:
* 定义一个二维数组:int[][] arr = new int[n+1][]
* 初始化二维数组中的每一个一维数组的值
* 1、定义一个直角三角形的二维数组,其中每个一维数组的长度
* 等于二维数组下标+1,同时将一维数组的第一列和
* 对角线上的数字初始化为 1
* for(int i = 0; i < arr.length; i++)
* {
* arr[i] = new int[i + 1];
* arr[i][0] = 1;
* arr[i][i] = 1;
* }
*
* 2、从第三行开始每行的第二个元素到对角线之前的那个元素,
* 都等于上一行同列的元素以及前列的元素之和
* for(int i = 2; i < arr.length; i++)
* {
* for(int j = 1; j < arr[i].length-1; j++)
* {
* arr[i][j] = arr[i-1][j] + arr[i-1][j-2];
* }
* }
*
* 3、遍历打印二维数组
* for(int i = 0; i < arr.length; i++)
* {
* for(int j = 0; j < arr[i].length; j++)
* {
* System.out.print(arr[i][j] + " ");
* }
* System.out.println();
* }
*/
import java.util.Scanner;
/**
* 这是一个实现打印杨辉三角性的类
* @author JoianSun
*/
class TriangleDemo
{
public static void main(String[] args)
{
// 定义键盘录入对象,接收用户录入的行数
Scanner sc = new Scanner(System.in);
System.out.println("Please input a number(n > 0):");
// 接收用户输入的行数
int line = sc.nextInt();
// 判断用户输入的行数是否合法,如果不合法,立即结束程序
if(line < 0)
{
System.out.println("Please input a postive integer!");
return;
}
// 用户输入的line合法的话,继续实现
// 1、实现数据结构的初始化
int[][] arr = new int[line + 1][];
/* 2、定义二维数组中每个一维数组的长度
* (一维数组的长度等于该一维数组在二维数组中的下标+1)
* 并对每个一维数组第一个元素和对角线上
* (也就是最后一个元素)的元素进行 1 赋值
*/
for(int i = 0; i < arr.length; i++)
{
// 定义一维数组的长度
arr[i] = new int[i + 1];
// 首尾元素赋值为 1
arr[i][0] = 1;
arr[i][i] = 1;
}
// 3、将一维数组中的非首尾元素按规律赋值
for(int i = 2; i < arr.length; i++)
{
for(int j = 1; j < arr[i].length-1; j++)
{
arr[i][j] = arr[i-1][j] + arr[i-1][j-1];
}
}
// 4、遍历打印二维数组
for(int i = 0; i < arr.length; i++)
{
for(int j = 0; j < arr[i].length; j++)
{
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
思考题
Java中的参数传递问题
class Demo
{
public static void main(String[] args)
{
int a = 10;
int b = 20;
System.out.println("a=" + a + ",b=" + b);
change(a, b);
System.out.println("a=" + a + ",b=" + b);
int[] arr = {1, 2, 3, 4, 5};
change(arr);
System.out.println(arr[1]);
}
public static void change(int a, int b)
{
System.out.println("a=" + a + ",b=" + b);
a = b;
b = a + b;
System.out.println("a=" + a + ",b=" + b);
}
public static void change(int[] arr)
{
for(int i = 0; i < arr.length; i++)
{
if(arr[i] % 2 == 0)
{
arr[i] *= 2;
}
}
}
}
>>> a=10,b=20 // main方法中,打印a、b的值
a=10,b=20 // change方法中,打印a、b的值
a=20,b=40 // change方法中,在将a=b; b=a+b;执行完毕后的打印
a=10,b=20
/* main方法中,打印a、b的值,值没变的原因是:change方法上形式参数
接受了a、b值,只是把main方法中a、b的值传给了change方法
中的a、b,并在change方法内部做了操作,但是在main方法中
并没有对a、b做任何的操作,所以并没有改变main方法中的a、b值 */
4
/* main方法中的数组下标为1的元素的值是2,将数组的地址值传递到change
方法中,实际上相当于两个不同方法中的arr指向了同一块堆内存区域,
所以在change中改变了的数组元素的值,会被main方法中的数组
同样引用到,所以arr[1]的值被改变 */
P.S.
Java 中只有一种参数传递的方式:值传递
基本数据类型:传递的是数值的大小『形式参数和实际参数互不影响』
引用数据类型:传递的是 地址值 『形式参数改变直接作用到实际参数』