【JavaSE基础】01-Java 基础语法

原文写于 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语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型。在内存中分配额了不同大小的内存空间:

类型字节数
byte1
short2
int4
long8
float4
double8
char2
boolean1

 
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");
	}
}

>>>   分别写出 ① 为breakcontinuereturn的结果
       ① 为 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 中只有一种参数传递的方式:值传递

  • 基本数据类型:传递的是数值的大小『形式参数和实际参数互不影响』
  • 引用数据类型:传递的是 地址值 『形式参数改变直接作用到实际参数』
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老坛算粉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值