1 Day02–变量+数据类型+类型转换
1.1 前言
1.1.1 标识符
可以简单的理解为一个名字。在Java中,我们需要标识代码的很多元素,包括包名、类名、方法、字段、变量等。我们选择的名称就称为标识符,并且遵循以下规则:
- A.标识符可以由字母、数字、下划线(_)、美元符($)组成,但不能包含 @、%、空格等其它特殊字符。
- B.不能以数字开头。
- C.标识符是严格区分大小写的。
- D.标识符的命名最好能反映出其作用,做到英文的见名知意。
- E.可以写使用中文命名,但一般不建议使用,也不建议使用拼音,很low。
1.1.2 关键字
在java语言中已经被赋予特定意义的一些单词。一共有53个关键字。其中有两个保留字:const(常数,不变的)和goto(转到)。关键字不能被用作标识符!!
保留字:暂时没用未来也许会用。
关键字中所有字母都为小写。
Java中关键字很多,不需要强制记忆,因为在编译程序是如果你使用了关键字做标识符编译器会提醒你出错。
1.1.3 注释
1) java规范的三种注释方式:
// 单行注释 (快捷键:ctrl+/, 取消注释:重复使用一次)
/* 多行注释 */ (快捷键:ctrl+shift+/),取消注释:重复使用一次
/** 文档注释(java特有) */ (快捷键:/**+enter)
使用位置:
类上,类的属性上,类的方法上-----推荐使用文档注释
方法内部----推荐使用单行、多行注释
2) 单行注释和多行注释:
2.1) 作用:
- Java代码的解释说明。不影响程序的运行,用于辅助读程序。
- 调试所写的代码
2.2) 特点:
单行注释和多行注释,注释了的内容不参与编译。
换句话说,编译以后生成的.class结尾的字节码文件中不包含注释掉的信息。
2.3) 注意事项:多行注释不可以嵌套使用,其它注释虽然可以嵌套但没有啥意义。
3). 文档注释:
3.1) 作用:注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。
3.2) 案例:
生成自己的API文档:通过命令行或者通过开发工具如idea,具体百度一下。
命令:javadoc 参数 文件
1.1.4 eclipse中的快捷键
查看源码, ctrl+鼠标点击。
syso+alt+/: 输出语句快捷键
ctrl +/ : 选中多行代码,快速添加单行注释。(取消掉,再次快捷键)
Ctrl+shift+o: 导包快捷键
Ctrl +d: 每次删除一行的快捷键(在要删除的代码块,后使用快捷键可以删除一行)
ctrl +n 快捷键相当于new在用上下键 (创建项目,包,类全程不用鼠标)
Alt+上下箭头(对代码进行上下移动)
1.1.5 java中的转义字符
转义字符:用来表示特殊的符号或特殊意义.
\b :表示后退一个字符
\t :表示一个制表位
\n :表示换到下一行
\r:回车
\” :表示双引号
\’ :表示单引号
\ :表示反斜杠字符\
1.1.6 java中的输出语句
System,out.print(); //输出不换行
System,out.println(); //换行
System,out.println(“ ”);//输出一个空格
System,out.println(“\t”); //对齐:输出的数加上空格一共8位。也可以改为(“ “)的形式。
1.1.7 变量(默认值)
举例:
房间 --------- 变量
房间名字 --------- 变量名
房间类型-------- 变量类型
入住的客人------- 变量值
概念:在JAVA中,有的数据值是不固定的,总在变,我们还需要记录这些值,我们可以把这些值理解为变量。
变量的本质:其实是内存里的一部分空间,创建变量的时候,意味着向操作系统申请一部分内存的空间,声明不同类型的变量本质上就是申请的内存空间大小不一样;(程序的执行的第一步,都是载入内存,接着按照程序的执行和硬件的关系)
变量的使用原则:就近原则,尽量控制到最小范围。
变量的声明:变量类型 变量名 = 变量值。
= :赋值号,右边的数据,赋予给左边的变量。
int num = 18; //声明的同时赋值
等价于:
int num; //先声变量
num = 20;//在进行赋值
注意:
- 变量名必须是一个有效的标识符。
- 变量名不可以使用java关键字。
- 同一个作用域内,变量名不能重复。
- java中的标点符号是英文的。
- 变量每句话都以分号结尾。
Java中变量使用的规则:
- java中的变量需要先声明后使用。(局部变量没有默认值,想要输出使用,必须声明并且赋值好才能使用。成员变量有默认值,想要输出使用,不赋值也能输出)
- 变量使用时,可以声明变量的同时进行初始化(初始化:第一次赋值),也可以先声明后赋值。
- 变量中每次只能赋一个值,但可以修改多次。
- Main方法中定义的变量必须先赋值,然后才能输出。
- 虽然语法中没有提示错误,但实际开发中,变量名不建议使用中文,容易产生安全隐患,譬如后期跨平台操作时出现乱码等等。
- 变量的使用必须与数据类型匹配。
- 变量都定义在其作用域内。在作用域内,它是有效的。换句话说,出了作用域,就失效了
- 变量作用域范围:从变量的声明开始一直到包含它最近的{ }的程序结束。
- 变量命名规则:驼峰命名法"score,myScore,myJavaScore(第二个单词首字母大写)。
变量的分类:
- 按被声明的位置划分:
成员变量
(类里方法外)和局部变量
(方法内部)。成员变量又分为:实例变量
和静态变量
(添加Static关键字) - 按所属的数据类型划分:
基本数据类型
和引用数据类型(类,接口,数组)
。
局部变量:
- 定义在方法中(包括方法的参数),或者局部代码块中。
- 必须手动初始化(赋初值),来分配内存。如:int i=5;
- 作用域也就是方法里或者局部代码块里,方法运行完内存就释放了。
成员变量:
- 定义在类里方法外。
- 不用初始化,也会自动被初始化成默认值。
- 作用域是整个类中,类消失了,变量才释放。
成员变量的默认值:
1).引用类型(除了8大基本类型):默认值为 null
2).基本类型:
其中char: /u0000
(默认值,一般不显示。空白符的code是0x20,0x20以下是不可见得控制字符。某些系统在遇到不可见字符时会输出一个方框表示遇到了不可见字符)
基本类型 | 默认值 |
---|---|
byte | 0 |
shot | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
boolean | false |
1.1.8 常量
在day03 – 3.3 --final中学习的常量。
概述:在程序运行过程中一直不会改变的量成为常量。
常量的定义形式: final 数据类型 常量名 = 值;
,一般为了方便外界调用,都会被static修饰,可以直接被类名.访问。最终形式为:public static final 数据类型 常量名 = 值;
。
常量命名规范:推荐常量名所有字母大写,多个单词用下划线(“_”)隔开(推荐就是不强制)如:MAX_VALUE
常量注意事项:
- 必须声明同时初始化(必须直接赋初值,不能修改),(静态块中赋值也可以,但不推荐)
- 由类名点来访问,并且不能改变。
常量优点:常量在编译期被直接替换为具体的值- -效率高
常量何时用?数据永远不变并且经常被使用。
1.1.9 变量测试案例
//测试一:
package cn.tedu.variable;
import org.junit.Test;
//测试 变量的使用
public class Test6_Variable {
//TODO 测试其他类型 的默认值
//2,成员变量:位置是在类里方法外 + 有默认值 + 作用范围是整个类里
double count ;
//3,就近原则---前提是---当成员变量 和 局部变量同名时,你使用的一定是局部变量
int sum = 20 ;
//单元测试junit :要求方法要有标志@Test + 方法必须是public的 + 方法返回值是void,没有参数列表
@Test //需要导包(1 2 都可以)
public void show() {
//1,局部变量:位置是在方法里 + 必须初始化 + 作用范围是方法里
int sum = 10;
System.out.println(sum);//10,就近原则
System.out.println(count);//0.0
}
}
//测试二:
package cn.tedu.arrays;
public class Test2_Variable {
//1、成员变量:在类里方法外
//作用范围就是整个类里
//可以不初始化,也会有默认值
int age = 20;
int sum = 30;
public static void main(String[] args) {
//2局部变量:在方法里
//作用范围就是方法里,出了这个方法就不认识了
//必须初始化
int sum = 10;
System.out.println(sum);//10
//3、变量的就近原则,附近有同名的会去执行最近的
System.out.println(sum);//10
}
}
1.1.10 成员变量注意点
注意:成员变量不能先声明后赋值。
解释:成员变量在类的范围内,存在于堆内存中,会有默认的初始值,在声明的同时已经给变量赋值了。再次”赋值“实际是使用了语句,而类体里面只能出现变量和方法,不能出现语句。
package com.cn.ins;
public class Aa {
private int age = 10;
int b;
b= 20;//报错:成员变量不能先声明后赋值。
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
1.2 数据类型
强类型语言:要求变量的使用要严格符合规定,所有变量必须先定义后使用。如:Java、.net 、Python、C++等语言。
弱类型语言:数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。如:vb 、PHP、javascript等语言。
Java的数据类型分为2大类:
- 基本数据类型:变量存的是数据本身。(相当于抽屉里的东西)
- 引用数据类型:(除了基本类型都是引用类型,如:类,接口,数组)变量存的是保存数据的空间地址。(相当于抽屉的钥匙)
1.2.1 基本类型(八种)
从小到大排序:byte short (char) int long float double
解释:布尔类型不参与排序,char会自动转化为int。
名词解释:什么是字节。
1.2.2 练习1:最大值最小值
package day0102;
//总结
//1,基本类型:byte short int long float double char boolean
//2,工具类:Byte Short Integer Long Float Double Character Boolean
//3,工具类提供了很多功能,其中就包含了大小值
//基本类型对应的包装类提供了一些功能,最大值,最小值 没有括号不是方法
//注意包装类首字母大写,尤其2个特殊
public class Test1 {
public static void main(String[] args){
//=====1、整型测试开始
//变量类型 变量名 = 变量值
byte a=-128; 打印变量的值,不需要" "
byte b=127;
short c=Short.MIN_VALUE;
short d=Short.MAX_VALUE;
int e=Integer.MIN_VALUE;
int f=Integer.MAX_VALUE;
long g=Long.MIN_VALUE;
long h=Long.MAX_VALUE;
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);
System.out.println(f);
System.out.println(g);
System.out.println(h);
//=====整型测试结束
//=====2、浮点测试开始
/**
*float是单精度,对小数运算不精确。double是双精度,对小数运算精确。
*对于编程人员来说,double和float的区别是double精度高,但double消耗内存是float的两倍。
*且double的运算速度较float稍慢。
*/
float i=Float.MIN_VALUE; //1.4E-45(代表10的负45次方)
float j=Float.MAX_VALUE; //3.4028235E38(代表10的38次方)
double k=Double.MIN_VALUE; //4.9E-324
double l=Double.MAX_VALUE; //1.7976931348623157E308
System.out.println(i);
System.out.println(j);
System.out.println(k);
System.out.println(l);
//=====浮点测试结束
//=====3、字符测试开始
/**
*字符char 单引号
*字符串String(S大写) 双引号
*/
//① 定义char型变量,通常使用一对'',内部只能写一个字符
char c = 'a'; //可以存一个字母或者数字或者符号不能没有,可以是空格
char c4 ='1'; //数字也只能存一个。
char c2 = 97; //可以直接存数字(可以写多个数字)
char c3 = '中'; //可以存一个中文汉字
//char类型可以存一个数字,但是在使用时,并不是使用了数字本身,而是去查数字对应的字符,然后使用字符。
//ascii码表,规定了数字和字符的对应关系。其中数字97就是对应了字符a。 97----a 65---A 48---0
//ascii码表里,规定了0~127数字对应的字符,char类型的取值
//范围是0~65535.那么从128~65535默认对应的字符是?.
System.out.println(c);
System.out.println(c2);//a
System.out.println(c3);
//② 表示方式:1.声明一个字符 2.转义字符 3.直接使用 Unicode 值来表示字符型常量
char c5 = '\n';//换行符
c5 = '\t';//制表符
System.out.print("hello" + c5);
System.out.println("world");
char c6 = '\u0043';
System.out.println(c6);
//=====4、布尔
//① 只能取两个值之一:true 、 false
//② 常常在条件判断、循环结构中使用
boolean b = true;
boolean b2 = false;
}
}
//long start = System.currentTimeMillis()用于获取自1970.1.1零时到此时此刻的毫秒数,返回值类型为long类型·
1.2.3 练习2:输入个人信息
王海涛今年20岁,月薪20000,奉劝大家拼命学java,才能早日走向人生巅峰。
package day0201_规则;
import java.util.Scanner;
public class Test1_个人信息 {
public static void main(String[] args) {
System.out.println("姓名:");
String name= new Scanner(System.in).nextLine();
//注意:String对应的是Line
System.out.println("性別:");
String gender= new Scanner(System.in).nextLine();
System.out.println("年龄:");
//其它的都是首字母大写
int age= new Scanner(System.in).nextInt();
System.out.println("您输入的个人信息是:");
System.out.println("姓名:"+name);
System.out.println("性別:"+gender);
System.out.println("年龄:"+age);
}
}
1.2.4 练习3:String类型变量的使用
/*
String类型变量的使用
1. String属于引用数据类型,翻译为:字符串
2. 声明 String类型变量时,使用一对""
3. String可以和 8种基本数据类型变量做运算,且运算只能是连接运算:+
4. 运算的结果仍然是String类型
*/
class StringTest {
public static void main(String[] args) {
String s1 = "Hello World!";
System.out.println(s1);
String s2 = "a";
String s3 = "";
//char c = '';//编译不通过
//***********************
int number = 1001;
String numberStr = "学号:";
String info = numberStr + number;// +:连接运算
boolean b1 = true;
String info1 = info + b1;// +:连接运算
System.out.println(info1);
//***********************
//练习1
char c = 'a';//97 A:65
int num = 10;
String str = "hello";
System.out.println(c + num + str);//107hello
System.out.println(c + str + num);//ahello10
System.out.println(c + (num + str));//a10hello
System.out.println((c + num) + str);//107hello
System.out.println(str + num + c);//hello10a
//练习2
//* *
System.out.println("* *");
System.out.println('*' + '\t' + '*');
System.out.println('*' + "\t" + '*');
System.out.println('*' + '\t' + "*");
System.out.println('*' + ('\t' + "*"));
//***********************
//String str1 = 123;//编译不通过
String str1 = 123 + "";
System.out.println(str1);//"123"
//数字类型不能和字符串类型进行 自动和强制类型转换
//int num1 = str1;
//int num1 = (int)str1;//"123"
//基本类型和引用类型,可以使用包装类进行转换
int num1 = Integer.parseInt(str1);
System.out.println(num1);//123
}
}
1.2.5 练习4:字符串的拼接过程。
大家好,我叫渣渣辉,今年28。
package cn.tedu.basic;
//这个类用来测试字符串拼接
public class Test2_Info {
public static void main(String[] args) {
//值是一串,Java里表示一串数据,就是String类型。
String name = "蔡徐坤";
byte age = 28;
//通过+拼接字符串, "+?+"
System.out.println("大家好,我叫"+name+",今年"+age+"。");
}
}
1.2.6 练习5:圆形面积
圆的面积:π*r*r
圆的周长:2*π*r
package day0104;
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
//定义变量,记录半径r
// double r = 10.1;//不好,值写死了。
//动态的接受键盘输入的值
//测试时:console试图中小红点常亮,表示等待用户的输入,输入完毕后敲回车,表示输入结束。
double r = new Scanner(System.in).nextDouble();
double result = 3.14*r*r;//带入公式
System.out.println(result);
//或者 System.out.println(3.14*r*r);
}
}
1.2.7 练习6:变量交换(3种)
接收用户输入的值:假设a=1,b=2将a和b的值做交换。
package day0103;
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
案例1:
//1,接受键盘输入整数a和整数b
System.out.print("输入整数a:");
int a=new Scanner(System.in).nextInt();
//前后对照
System.out.print("输入整数b:");
int b=new Scanner(System.in).nextInt();
//第一种方式:
//2.开始交换
int t=a; //把a的值给t 首尾相连
a=b; //把b的值给a
b=t; //把t的值给b
System.out.println(a);
System.out.println(b);
}
//第二种方式:a=a+b; b=a-b;a=a-b 另一种2个数进行交换
}
案例2:
//练习:交换两个变量的值
int num1 = 10;
int num2 = 20;
System.out.println("num1 = " + num1 + ",num2 = " + num2); //num1 = 10,num2 = 20
//方式一:定义临时变量的方式
//推荐的方式
int temp = num1;
num1 = num2;
num2 = temp;
//方式二:好处:不用定义临时变量
//弊端:① 相加操作可能超出存储范围 ② 有局限性:只能适用于数值类型
//num1 = num1 + num2;
//num2 = num1 - num2;
//num1 = num1 - num2;
//方式三:使用位运算符
//有局限性:只能适用于数值类型
//num1 = num1 ^ num2;
//num2 = num1 ^ num2;
//num1 = num1 ^ num2;
System.out.println("num1 = " + num1 + ",num2 = " + num2);//num1 = 20,num2 = 10
1.3 基本类型的默认字面类型(5条)
1.3.1 整数字面类型(默认值)是int类型
int a = 9999999999;//错,右侧是int类型,但是超出范围
1.3.2 byte,short,char三种比int小的整数可以用范围内的值直接赋值
byte b=127;//对
byte b=128;//错,右面已经超过byte范围是int类型的数据
超过范围的相加 加2次最大值等于它本身(所有的基本类型)
1.3.3 浮点数的字面值是double类型
double a=3.14;//对
float a=3.14;//错,右面是double,float是四字节double是八字节存不下
1.3.4 字面值后缀l f d
//因为它默认的是int类型虽然可以自动转,但是它超出本身的范围了。所以加上一个L 表示long 类型。
//这个整数默认是int类型,但是超出了int范围,还想使用怎么办?
//加后缀L,用来把字面值是int类型的数据,提升成long类型
L –long 如:long a = 99999999999;//错,超出范围,解决方案加L
//后缀f,用来把默认的double类型的小数,转成float类型
F –float 如:float a = 3.14;//错,右面是double类型,解决方案加F或者(float)
//后缀d,用来把字面值是int类型的3,转成double类型
D –double 如:double a=3;//错,右面是int,解决方案加D或者改成3.0
//后缀一般默认是大写,因为小写有时候想数字1不好区分。
注意不加也不报错,因为3在double类型的取值范围之内。
1.3.5 进制前缀
0x - 16进制
0 -8进制
\u -char类型,16进制
0b -2进制 0b0011
1.4 基本类型的类型转换
注意事项:
1.不能对布尔值进行转换。(char类型可以,因为它对应的编码表为数字)
2.不能把对象类型转换为不相干的类型。
3.在把高容量转换为低容量时需要强制类型转换。
4.小类型转大类型会自动转换。
5.转换时可能出现内存溢出或者精度问题。
1.4.1 小到大(隐式转换)
不需要强制类型转换
//自动类型转换
Byte a =120;
Int b=a;//直接转
整形可以转换为浮点型
char可以直接转化为int类型
1.4.2 大到小(显式转换)
需要强制类型转换
//强制类型转换
int xx = 356;
byte y=(byte) xx;
//内存溢出问题
int i=128;
byte b=(byte)i;//-128 内存溢出,byte范围为-128~127
//精度问题:小数转成整数,小数直接舍弃。
double y = 9.1;
int x = (int)y;//右侧的y是大类型,给小类型x赋值时,需要强转、
System.out.println(x);//9,丢弃小数部分
//不管0.1还是0.9全都舍弃
//char类型的转换
char c='a'; //97
int d=c+1;
System.out.println(d);//98
System.out.println((char)d);//b
1.5 运算规则(5条)
1.5.1 计算结果是数据类型,与最大类型一致
3/2 得1 ,而不是1.5,结果是int类型
3d/2 得1.5,相当于double/int,结果是double类型
1.5.2 byte,short,char三种比int小的整数,运算时会先自动转换成int
byte a=3;
byte b=4;
byte c=a+b;//错,运行时,byte会先自动转成int再运算,int+int还是int
byte c = (byte)(a+b);
//右侧的a+b运算时,会自动变成int类型,是大类型。给左面的c小类型赋值??--不能直接赋值需要强转
1.5.3 整数运算溢出
整数运算,类似于一个钟表,转到最大时,再转会回到最小。
注意:在操作比较大的数时,注意溢出问题.
解决方案:提前扩大变量值得范围,把int字面值加L后缀扩大,变成long类型、一般是第一个数后加L。
//jdk1.7新特性:数字之间可以用下划线分割,输出时不会影响。
int money=10_0000_0000;
int years=20;
int total=money*years; //内存溢出 -1474836480
long total2=money*years; //内存溢出,计算完后默认是int类型,转换为long之前就出现问题了。 -1474836480
long total3=(long)money*years;//20000000000
System.out.println(total);
System.out.println(total2);
System.out.println(total3);
//计算:光速运行一年的长度是多少米
System.out.println(300000000*60*60*24*365);
System.out.println(300000000l*60*60*24*365);
//运算结果,字面值就是int类型。但是,已经超出了int取值范围,就会整数运算溢出现象
//解决方案:把int字面值加L后缀扩大,变成long类型、一般是第一个数后加L。
1.5.4 浮点数运算不精确
原因:浮点型数字特点是 有限 ,离散的,会有舍入误差,结果只能是一个约数,接近但不等于。
解决:java提供了解决方案,后面就会讲到,BigDecimal。
System.out.println(1-0.8);//0.19999999999999996
System.out.println(4.35*100);//434.99999999999994
//最好完全避免使用浮点数进行比较
float f= 0.1f;//0.1
double d=1.0/10;//0.1
System.out.println(f==d);//false
1.5.5 浮点数的特殊值
Infinity 无穷大 3.14/0
Nan not a number 0/0.0
//输出结果不能出现这种情况,一旦出现检查代码(分母为0或分子分母都为0)
1.6 拓展
1.6.1 char 型变量中能不能存储一个中文汉字,为什么?
char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。
1.6.2 用户交互Scanner
注意:
- String对应的是Line,其它的都是首字母大写.
- 对于char型的获取,Scanner没有提供相关的方法。只能获取一个字符串。如果非要用char类型来接收可以调用 gender.charAt(0)方法。
import java.util.Scanner;
/*
如何从键盘获取不同类型的变量:需要使用Scanner类
具体实现步骤:
1.导包:import java.util.Scanner;
2.Scanner的实例化:Scanner scan = new Scanner(System.in);可以写一块
3.调用Scanner类的相关方法(next() / nextXxx()),来获取指定类型的变量
注意:
需要根据相应的方法,来输入指定类型的值。如果输入的数据类型与要求的类型不匹配时,会报异常:InputMisMatchException
导致程序终止。
*/
public class Test1_个人信息 {
public static void main(String[] args) {
System.out.println("姓名:");
String name= new Scanner(System.in).nextLine();//写一块的写法
//注意:String对应的是Line
System.out.println("性別:");
String gender= new Scanner(System.in).nextLine();
System.out.println("年龄:");
//其它的都是首字母大写
int age= new Scanner(System.in).nextInt();
System.out.println("您输入的个人信息是:");
System.out.println("姓名:"+name);
System.out.println("性別:"+gender);
System.out.println("年龄:"+age);
//对于char型的获取,Scanner没有提供相关的方法。只能获取一个字符串
System.out.println("请输入你的性别:(男/女)");
String gender = scan.next();//"男"
char genderChar = gender.charAt(0);//获取索引为0位置上的字符
System.out.println(genderChar);
}
}
输出结果为:hello
输出:hello world
原因:
如果是数字的话用对应的nextxxx()方法进行判断:
package lianxi;
import java.util.Scanner;
public class Ta {
public static void main(String[] args) {
Scanner scanner =new Scanner(System.in);
//从键盘接收数据
int i=0;
float f=0.0f;
System.out.println("请输入整数:");
if(scanner.hasNextInt()) {
/*
* 如果什么也不写默认为true(老手写法)
* if(scanner.hasNextInt()==true) { 新手写法
*/
i= scanner.nextInt();
System.out.println("整数数据"+i);
}else {
System.out.println("输入的不是整数数据");
}
scanner.close();
}
}
2 Day03–运算符+分支结构+循环
2.1 运算符
2.1.1 概述:常用运算符
2.1.2 位运算符
除了常用运算符外还有位运算符:
如何区分&,|,^
何时是逻辑运算符何时是位运算符???
当符号两侧是逻辑运算符且结果为布尔类型,则为逻辑运算符。若符号两侧为整数且结果为整数,则是位运算符。
案例1:
/*
A = 0011 1100
B = 0000 1101
------------------
A&B = 0000 1100 上下2个都是1才为1,否则为0
A/B = 0011 1101 上下2个都是0才为0,否则为1
A^b = 0011 0001 上下2个位置相同则为0,否则则为1.
~B = 1111 0010 取反
2*8 = 16 2*2*2*2
效率极高!!!
<< *2 左移扩大
>> /2 右移缩小
0000 0000 0
0000 0001 1
0000 0010 2
0000 0011 3
0000 0100 4
0000 1000 8
0001 0000 16
*/
System.out.println(2<<3);//16
案例2:
package lianxi;
import java.util.Arrays;
/*
运算符之五:位运算符 (了解)
结论:
1. 位运算符操作的都是整型的数据
2. << :在一定范围内,每向左移1位,相当于 * 2
>> :在一定范围内,每向右移1位,相当于 / 2
面试题:最高效方式的计算2 * 8 ? 2 << 3 或 8 << 1
*/
class BitTest {
public static void main(String[] args) {
int i = 21;
i = -21;
System.out.println("i << 2 :" + (i << 2));//i << 2 :-84 不改变符号
System.out.println("i << 3 :" + (i << 3));//i << 3 :-168
System.out.println("i << 27 :" + (i << 27));//i << 27 :1476395008
int m = 12;
int n = 5;
System.out.println("m & n :" + (m & n));//m & n :4
System.out.println("m | n :" + (m | n));//m | n :13
System.out.println("m ^ n :" + (m ^ n));//m ^ n :9
}
}
2.2 运算符优先级
口诀:单目乘除为关系,逻辑三目后赋值。
单目运算符:一次作用一个变量的运算符,又叫一元运算符
单目:单目运算符+ –(正负数) ,++ –,!(逻辑非),~(按位取反)
乘除:算数运算符:* / % + - (* / %优先级肯定是大于+-的)
为:位运算符:~(按位取反)<<(左移) >>(右移),^(也可以位运算,二进制异或)
关系:关系运算符:> < >= <= == !=
逻辑:逻辑运算符(除!)&& || & | ^
三目:条件运算符A > B ? X : Y
后:无意义,仅仅为了凑字数
赋值:= += -= *= /= %= |= &= ^=
说明:前优先级大于后,比如单目运算符~也是位运算符,~的优先级是单目级别的。至于赋值运算符中没有见过的自行测试
PS:大家没必要去死记运算符的优先级顺序,实际开发中,一般会使用小括号辅助进行优先级管理。如:
2.3 案例练习
2.3.1 练习1:平年闰年
输入年号,判断是否是闰年。两个条件:
1、能被4整除,并且不能被100整除
2、或者能被400整除
package day0203_平年闰年;
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
System.out.println("年号:");
int y = new Scanner(System.in).nextInt(); //1,接受用户输入的年号
String r="平年"; //默认都是平年
//2,判断,这个年号到底是平年还是闰年
if(y%4==0){
if(y%100!=0){
r="闰年";
}
}
if(y%400==0){
r="闰年";
}
或者
//说明:
//两个大条件之间是或者的关系,为了高效,用双或
//大条件1有两个小条件,两个小条件之间是并且的关系,为了高效,用双与
//能否被整除,要取余数,余数为0就是能整除。余数不是0就是不能整除。
if( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0){
//如果年号,能满足判断条件,会进来修改r的默认值为闰年。如果没满足,就一直是默认值平年
r = "闰年";//修改r的值为闰年
if((y%4==0&&y%100!=0)||y%400==0){
r="闰年";
}
System.out.println(y+"年是"+r);
}
}
2.3.2 练习2:自增自减
package com.jia.jian;
public class JiaJian {
public static void main(String[] args) {
//++,--代表自身加一和减一,运算结果与符号顺序有关。
//情况1:单独使用时,++ --无论放在变量前还是变量后,结果都是一样的
int number1 = 10;
//输出方式一: 输出number1++
//System.out.println(number1++);//10
//输出方式二: 输出number1
number1++;
System.out.println(number1);//11
//总结执行过程:符号在后,先使用后变化(先使用:number1++变为10,后变化:number1自身变为11),
//所以输出number1++为10,输出number1为11.
int number2 = 10;
//输出方式一:输出++number2
//System.out.println(++number2);//11
//输出方式二:输出number2
++number2;
System.out.println(number2);//11
//总结执行过程:符号在前,先变化后使用(先变化:++number2变为11,后使用:number2自身变为11),
//所以输出number2++为11,输出number2为11.
int number3 = 10;
number3--;
System.out.println(number3);//9
int number4 = 10;
--number4;
System.out.println(number4);//9
//情况2:参与操作时,符号在后:先使用后变化
//执行过程:符号在后,先执行运算(先把10赋值给num2),后变化(num1++,num1变为11)
int num1 = 10;
int num2 = num1++;
System.out.println("num1:"+num1+",num2:"+num2);//num1:11,num2:10
//情况3:参与操作时,符号在前:先变化后使用
//执行过程:符号在前,先变化(++num3,num3变为11),后执行运算(在把11赋值给num4),
int num3 = 10;
int num4 = ++num3;
System.out.println("num3:"+num3+",num4:"+num4);//num3:11,num4:11
}
}
2.3.3 练习3:求两个数里的大值
/*
三元运算符:
1.结构:(条件表达式)? 表达式1 : 表达式2
2. 说明
① 条件表达式的结果为boolean类型
② 根据条件表达式真或假,决定执行表达式1,还是表达式2.
如果表达式为true,则执行表达式1。
如果表达式为false,则执行表达式2。
③ 表达式1 和表达式2要求是一致的。
④ 三元运算符可以嵌套使用
3.
凡是可以使用三元运算符的地方,都可以改写为if-else
反之,不成立。
4. 如果程序既可以使用三元运算符,又可以使用if-else结构,那么优先选择三元运算符。原因:简洁、执行效率高。
*/
package cn.tedu.basic;
import java.util.Scanner;
//这个类用来测试两个数里的大值
public class Test3_Max {
public static void main(String[] args) {
//1,接受键盘输入的两个整数
//注意:scanner可以直接写,然后三目运算符boolean是他的返回值,前面可以写其他的类型来接受
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
//2,比大小 1 ? 2 :3 ,1判断完如果成立,得到的结果就是2
//a > b成立的话,把a交给max记录,否则,把b交给max记录
int max = a > b ? a : b ;
System.out.println(max);
}
}
2.3.4 练习4:求三个数的最大值
package day0203_平年闰年;
import java.util.Scanner;
public class Test1_三个数的最大值 {
public static void main(String[] args) {
System.out.println("整数a:");
int a = new Scanner(System.in).nextInt();
System.out.println("整数b:");
int b = new Scanner(System.in).nextInt();
System.out.println("整数c:");
int c = new Scanner(System.in).nextInt();
int max = a>b?a:b;
max=max>c?max:c; //分开写
或者
int max = a>b?(a>c?a:c):(b>c?b:c); //合一块写
System.out.println(max);
}
}
2.3.5 练习5:幂运算
//2^3 2*2*2=8 很多运算我们会借助一些工具类来操作。
double pow=Math.pow(3,2);
System.out.println(pow);
2.3.6 练习6:复合赋值运算符
偷懒写法,自带强转功能。
例1:
int a=10;
int b=20;
a+=b; //a=a+b;
a-=b; //a=a-b
System.out.println(a);//30
例2:
byte a = 1;
// a=(byte) (a+4);//右侧int,左侧byte,大转小,强转。
// a=(byte) (a+4);//右侧int,左侧byte,大转小,强转。
a+=4;//会自动完成数据类型的转换
//注意:a=a+4和a+=4是有区别的!!
System.out.println(a);
2.3.7 练习7:字符串连接符 “+”
//字符串连接符:若+一端为字符串,则会转化为Sring类型并进行拼接。
int a=10;
int b=20;
System.out.println(" "+a+b);//1020
System.out.println(a+b+" ");//30
2.3.8 练习8:测试逻辑运算符短路问题
逻辑运算符:&和&&,|与||之间的关系。
当一个&表达式在求值的时候,两个操作数都会被求值,&&更像 是一个操作符的快捷方式。当一个&&表达式求值的时候,先计算第一个操作数,如果它返回true才会计算第二个操作数。如果第一个操作数 取值为fale,第二个操作数就不会被求值。
一般在程序中想要表示true或者false,可以用1表示true,用0表示false
单与:& -- 表示并且的关系
1 & 2 -- 1和2都为true,结果为true
单或:| -- 表示或者的关系
1 | 2 -- 1和2有一个为true,结果为true
双与/短路与:&& -- 表示并且的关系,高效
1 && 2 -- 2会被短路,前提是1为false
双或/短路或:|| -- 表示或者的关系,高效
1 || 2 -- 2会被短路,前提是1为true
真 & 真 -》真
真 & 假 -》假
假 & 真 -》假
假 & 假 -》假
假 | 假 -》假
假 | 真 -》真
真 | 假 -》真
真 | 真 -》真
package lianxi;
import java.util.Arrays;
/*
运算符之四:逻辑运算符
& && | || ! ^
说明:
1.逻辑运算符操作的都是boolean类型的变量
*/
class LogicTest {
public static void main(String[] args) {
//区分& 与 &&
//相同点1:& 与 && 的运算结果相同
//相同点2:当符号左边是true时,二者都会执行符号右边的运算
//不同点:当符号左边是false时,&继续执行符号右边的运算。&&不再执行符号右边的运算。
//开发中,推荐使用&&
boolean b1 = true;
b1 = false;
int num1 = 10;
if(b1 & (num1++ > 0)){
System.out.println("我现在在北京");
}else{
System.out.println("我现在在南京");
}
System.out.println("num1 = " + num1); //我现在在南京 num1 = 11
boolean b2 = true;
b2 = false;
int num2 = 10;
if(b2 && (num2++ > 0)){
System.out.println("我现在在北京");
}else{
System.out.println("我现在在南京");
}
System.out.println("num2 = " + num2); //我现在在南京 num2 = 10
// 区分:| 与 ||
//相同点1:| 与 || 的运算结果相同
//相同点2:当符号左边是false时,二者都会执行符号右边的运算
//不同点3:当符号左边是true时,|继续执行符号右边的运算,而||不再执行符号右边的运算
//开发中,推荐使用||
boolean b3 = false;
b3 = true;
int num3 = 10;
if(b3 | (num3++ > 0)){
System.out.println("我现在在北京");
}else{
System.out.println("我现在在南京");
}
System.out.println("num3 = " + num3);//我现在在北京 num3 = 11
boolean b4 = false;
b4 = true;
int num4 = 10;
if(b4 || (num4++ > 0)){
System.out.println("我现在在北京");
}else{
System.out.println("我现在在南京");
}
System.out.println("num4 = " + num4);// 我现在在北京 num4 = 10
}
}
2.4 分支结构1:if
2.4.1 概述
问题:顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用分支结构。
总结:
任何复杂的程序逻辑都可以通过三种结构来实现:
1)顺序结构:从上往下逐行执行,每句必走
2)分支结构:有条件的执行某语句 一次,并非每句必走
3)循环结构:有条件的执行某语句多次,并非每句必走
2.4.2 形式
单分支:满足判断条件,则执行大括号里面的内容。
注意:判断条件又叫条件表达式
条件表达式:结果是boolean。
if(判断条件/条件表达式){
代码。。。
}
双分支:
if(判断条件){
代码1。。。
}else{
代码2。。。
}
多分支:
if(判断条件1){
代码1。。。
}else if(条件2){
代码2。。。
} else if(判断条件3){
代码3。。。
}else{
代码4。。。
}
嵌套分支:
if(判断条件){
if(判断条件){
代码
}
}
注意事项:
- if语句至多有一个else语句,else语句在所有的else if语句之后。
- if语句可以有若干个else if 语句,它们必须在else语句之前。
- 一旦其中一个else语句检测为true,则其它的else if语句以及else语句都不在在执行。
- 对于条件结构中,如果省略了大括号,则只会对第一句代码生效(及:如果没有大括号到第一个分号结束)换句话说:如果条件结构中只有一句代码,则大括号可以省略。但是不建议省略。
- else是可选的。
- if分支可以嵌套
2.4.3 练习1:商品打折
接收用户输入的原价。满1000打9折。满2000打8折。满5000打5折。
注意:java中的多个条件不能写成如:a>b>c 而是要用逻辑运算符a>b&&b>c来输出。
原因:因为比较运算符的结果是boolean类型,布尔类型无法比较大小。
package day999;
import java.util.Scanner;
public class ttt {
public static void main(String[] args) {
/*
说明:
1. else 结构是可选的。
2. 针对于条件表达式:
> 如果多个条件表达式之间是“互斥”关系(或没有交集的关系),哪个判断和执行语句声明在上面还是下面,无所谓。
> 如果多个条件表达式之间有交集的关系,需要根据实际情况,考虑清楚应该将哪个结构声明在上面。
> 如果多个条件表达式之间有包含的关系,通常情况下,需要将范围小的声明在范围大的上面。否则,范围小的就没机会执行了。
*/
System.out.println("输入总原价");
//1,接受用户输入的原价
double price = new Scanner(System.in).nextDouble();
//2,计算折后价,并输出
double now = price;//记录折后价
//注意java中的多个条件不能写成如:a>b>c 而是要用逻辑运算符a>b&&b>c来输出。
if( price >= 5000 ) {//满5000
now = price * 0.5 ;//now记录5折的价
}else if( price >= 2000 ) {//满2000
now = price * 0.8 ;//now记录8折的价
}else if( price >= 1000 ) {//满1000
now = price * 0.9 ;//now记录9折的价
}
或者
if( price >= 1000 && price < 2000) {//满5000
now = price * 0.9 ;//now记录9折的价
}else if( price >= 2000 && price < 5000 ) {//满2000
now = price * 0.8 ;//now记录8折的价
}else if( price >= 5000 ) {//满1000
now = price * 0.5 ;//now记录5折的价
}
System.out.println("原价是:"+price+",折后价是:"+now);
}
}
2.4.4 练习2:统计学员得分
90分以上 优秀
80~89 良好
70~79 中等
60~69 及格
60分以下 不及格
package game;
import java.util.Scanner;
public class aa {
public static void main(String[] args) {
double score = new Scanner(System.in).nextDouble();
//!!!用来增强代码的健壮性
if (score >= 100 || score <= 0) {
System.out.println("请输入0~100以内的值");
return; //结束程序
}
if (score > 90 && score <= 100) {
System.out.println("优秀");
} else if (score >= 80 && score <= 90) {
System.out.println("良好");
} else if (score >= 70 && score <= 79) {
System.out.println("中等");
} else if (score >= 60 && score <= 69) {
System.out.println("及格");
}else if (score < 60) {
System.out.println("不及格");
}
}
}
//优化
String desc = "优秀";
if(score >= 90) {//90分以上 优秀
desc = "优秀";
}else if(score >= 80 && score < 90 ) {//80~89 良好
desc = "良好";
}else if(score >= 70 && score < 80 ) {//70~79 中等
desc = "中等";
}else if(score >= 60 && score < 70 ) {//60~69 及格
desc = "及格";
}else if(score < 60) {//60分以下 不及格
desc = "不及格";
}
System.out.println("得分是:"+score+",属于:"+desc);
2.4.5 练习3:用if 比较3个数的大小
/*
编写程序:由键盘输入三个整数分别存入变量num1、num2、num3,
对它们进行排序(使用 if-else if-else),并且从小到大输出。
说明:
1. if-else结构是可以相互嵌套的。
2. 如果if-else结构中的执行语句只有一行时,对应的一对{}可以省略的。但是,不建议大家省略。
*/
import java.util.Scanner;
class IfTest2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个整数:");
int num1 = scanner.nextInt();
System.out.println("请输入第二个整数:");
int num2 = scanner.nextInt();
System.out.println("请输入第三个整数:");
int num3 = scanner.nextInt();
if(num1 >= num2){
if(num3 >= num1)
System.out.println(num2 + "," + num1 + "," + num3);
else if(num3 <= num2)
System.out.println(num3 + "," + num2 + "," + num1);
else
System.out.println(num2 + "," + num3 + "," + num1);
}else{
if(num3 >= num2)
System.out.println(num1 + "," + num2 + "," + num3);
else if(num3 <= num1)
System.out.println(num3 + "," + num1 + "," + num2);
else
System.out.println(num1 + "," + num3 + "," + num2);
}
}
}
2.5 [了解]分支结构2:switch
2.5.1 概述
当一个case成立,从这个匹配到的case向后穿透所有case,包括default,直到程序结束或者遇到break程序才结束。
优点:效率高、结构清晰
缺点:只适用于 整数、相等
2.5.2 形式
switch(expr1)中,expr1是一个整数表达式, 整数表达式可以是int基本类型或Integer包装类型,由于byte,short,char都可以隐含转换为int,所以也支持。
注意:
- case标签必须为字符串常量或字面量, 不能声明范围。
- 注意是表达式而不是条件表达式(条件表达式:结果是boolean)。
- switch结构中的表达式,只能是如下的6种数据类型之一:
byte 、short、char、int、枚举类型(JDK5.0新增)、String类型(JDK7.0新增) - break,可以使用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构
- break关键字是可选的。
- default:相当于if-else结构中的else.
default结构是可选的,而且位置是灵活的,写在最后不需要加break。
switch(变量或者表达式){
case 1:
break;
case 2:
break; //break: 跳出switch循环,可选。
case 3:
break;
case 4:
break;
default: //default 加个保险也可以不写,当前面都没有匹配到的时候相当于加个保险走默认的。
}
2.5.3 练习1:数字匹配
package cn.tedu.ifdemo;
//测试switch语法
public class Test7_Switch {
public static void main(String[] args) {
测试1:
int i = 2;
// i 的类型 也就是括号里表示的类型 -- byte short int char jdk1.7新增了String
switch( i ) {
case 1 :
System.out.println(1);
break;
case '1' :
System.out.println('1');
break;
/*1, 当匹配到case时,程序会继续向后穿透所有case包括default,即
*满足一个case条件时会把剩余所有case包括default里面的内容都会
*进行输出,直到程序结束或者遇到break程序才结束。
*/
case 2 :
System.out.println(2);
break;
//2, break用来结束程序
case 3 :
System.out.println(3);
break;
case 4 :
System.out.println(4);
break;
case 5 :
System.out.println(5);
break;
case 6 :
System.out.println(6);
break;
default:
System.out.println(0);//default之后不需要再写break
}
//如果输出的条件一样则可以简写为如:
case 0:
case 1:
case 2:
case 3:
System.out.println("小明");
break;
测试2:
String season = "summer";
switch (season) {
case "spring":
System.out.println("春暖花开");
break;
case "summer":
System.out.println("夏日炎炎");
break;
case "autumn":
System.out.println("秋高气爽");
break;
case "winter":
System.out.println("冬雪皑皑");
break;
default:
System.out.println("季节输入有误");
break;
}
测试3:
//**************如下的两种情况都编译不通过*********************
//情况一:表达式为布尔类型
/*
boolean isHandsome = true;
switch(isHandsome){
case true:
System.out.println("我好帅啊~~~");
break;
case false:
System.out.println("我好丑啊~~~");
break;
default:
System.out.println("输入有误~~~");
}
*/
//情况二:case值不是常量
/*
int age = 10;
switch(age){
case age > 18:
System.out.println("成年了");
break;
default:
System.out.println("未成年");
}
*/
}
}
2.5.4 练习2:case的简写
/*
例题:对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”。
说明:如果switch-case结构中的多个case的执行语句相同,则可以考虑进行合并。
*/
class SwitchCaseTest1 {
public static void main(String[] args) {
/*
int score = 78;
switch(score){
case 0:
case 1:
case 2:
...
case 100:
}
*/
/*
int score = 78;
if(score >= 60){
}else{
}
*/
int score = 78;
switch(score / 10){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("不及格");
break;
case 6:
case 7:
case 8:
case 9:
case 10:
System.out.println("及格");
break;
}
//更优的解决方案:
switch(score / 60){
case 0:
System.out.println("不及格");
break;
case 1:
System.out.println("及格");
break;
}
}
}
2.6 if与switch的对比
/*
从键盘分别输入年、月、日,判断这一天是当年的第几天
注:判断一年是否是闰年的标准:
1)可以被4整除,但不可被100整除
或
2)可以被400整除
说明:
1. 凡是可以使用switch-case的结构,都可以转换为if-else。反之,不成立。
2. 我们写分支结构时,当发现既可以使用switch-case,(同时,switch中表达式的取值情况不太多),
又可以使用if-else时,我们优先选择使用switch-case。原因:switch-case执行效率稍高。
*/
import java.util.Scanner;
class SwitchCaseExer {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入year:");
int year = scan.nextInt();
System.out.println("请输入month:");
int month = scan.nextInt();
System.out.println("请输入day:");
int day = scan.nextInt();
//定义一个变量来保存总天数
int sumDays = 0;
switch(month){
case 12:
sumDays += 30;
case 11:
sumDays += 31;
case 10:
sumDays += 30;
case 9:
sumDays += 31;
case 8:
sumDays += 31;
case 7:
sumDays += 30;
case 6:
sumDays += 31;
case 5:
sumDays += 30;
case 4:
sumDays += 31;
case 3:
//sumDays += 28;
//判断year是否是闰年
if((year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0){
sumDays += 29;
}else{
sumDays += 28;
}
case 2:
sumDays += 31;
case 1:
sumDays += day;
}
System.out.println(year + "年" + month + "月" + day + "日是当年的第" + sumDays + "天");
}
}
3 Day04–循环
3.1 循环结构1:for
3.1.1 概述
循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。它由循环体中的条件,判断继续执行某个功能还是退出循环。根据判断条件,循环结构又可细分为先判断后执行的循环结构和先执行后判断的循环结构。
for循环的优点:for循环语句是支持迭代的一种通用结构,是最有效,最灵活的循环结构。
循环三要素:
- 循环变量的初始化
- 循环的条件,是布尔类型 (以循环变量为基础)
- 循环变量的改变(向着循环的结束变)
循环变量:在整个循环过程中所反复改变的那个数.
3.1.2 形式
for(开始条件;循环条件;更改条件){
循环体代码…
}
For循环的特殊用法:
1. 死循环
For(; ;){ //死循环
}
2. 可以拆开写
int i=1;
for(;i<10;i++){ //向上拆
}
for(int i=1;i<10;){ //向下拆
i++; //注意这个地方多了个分号(;)}
3. 可以同时声明2个数:
for(int i=1,j=6;i<6;i+2,j--){
} //for中表达式一和表达式三可以使用逗号,而表达式二多个只能使用 &&, || ,!(且,或,非)
3.1.3 练习1:打印0到10
package cn.tedu.fordemo;
//测试for循环
public class Test8_For {
public static void main(String[] args) {
//for( 开始条件 ; 判断条件 ; 更改条件 ){ 循环体 }
// 打印0到10
//i用来记录每次获取到的值,默认是从0开始
//i定义在for循环里面:在for循环内有效。出了for循环就失效了。
for( int i = 0 ; i <= 10 ; i++){
System.out.println(i);
}
}
}
3.1.4 练习2:打印10到0
//从10开始,只要>=0,就打印,打印的数据依次递减
for( int i = 10 ; i >= 0 ; i-- ){
System.out.println(i);
}
3.1.5 练习3:打印8,88,888,8888
for( int i = 8 ; i <= 8888 ; i=i*10+8 ){
System.out.print(i+",");//同行展示数据。如8,88,....
}
3.1.6 练习4:求【1,100】中,奇数的个数,偶数的个数
int count = 0,为什么不是int count而要多赋值为0呢??在day03异常时讲的.
int count = 0;//定义变量,记录奇数的个数
int oucount = 0;//定义变量,记录偶数的个数,(定义为0,下面赋值不会影响程序)
for(int i = 1 ; i <= 100 ; i++) {
//判断,如果是奇数
if( i % 2 == 1 ) { //也可以写!=0
count++;//每次遇到一个奇数就+1
}
//判断,如果是偶数
if( i % 2 == 0 ) {
oucount++;//每次遇到一个偶数就+1
}
}
System.out.println("奇数的个数是:"+count+",偶数的个数是:"+oucount);
3.1.7 练习5:求【1,100】中,偶数的和奇数和
int sum = 0;//定义变量,记录偶数和
int jisum = 0;//定义变量,记录奇数和
for(int i = 1 ; i <= 100 ; i++) {
//判断,如果是偶数
if(i % 2 == 0) {
//对获取到的每个偶数求和
sum = sum + i;
}
//判断,如果是奇数
if(i % 2 == 1) { //或则i%2!=0也行
jisum = jisum + i;//把每个奇数求和
}
}
System.out.println("偶数和是:"+sum+",奇数和是:"+jisum);
3.2 嵌套for循环
3.2.1 概述
根据外层的条件,判断里层能否执行,如果能执行,就把里层代码都循环完毕后,再继续执行外层,继续判断。。
3.2.2 形式
for(…){
for(…){
}
}
3.2.3 入门案例
package day0000;
/*
嵌套循环的使用
1.嵌套循环:将一个循环结构A声明在另一个循环结构B的循环体中,就构成了嵌套循环
2.
外层循环:循环结构B
内层循环:循环结构A
3. 说明
① 内层循环结构遍历一遍,只相当于外层循环循环体执行了一次
② 假设外层循环需要执行m次,内层循环需要执行n次。此时内层循环的循环体一共执行了m * n次
4. 技巧:
外层循环控制行数,内层循环控制每行的列数
*/
public class T {
public static void main(String[] args) {
// f1();
f2();
}
//总结1:当i=1时,j取到了所有满足条件的数据,1,2,3,4,5。
//也就是说外循环执行1次,内循环执行多次
private static void f1() {
for(int i=1;i<=3;i++){//外循环
System.out.println("i="+i);//1,2,3
for(int j=1;j<=5;j++){//内循环
System.out.println("j="+j);//1,2,3,4,5
}
}
}
//总结2:外循环控制行,内循环控制列
private static void f2() {
for(int i=1;i<=3;i++){
for(int j=1;j<=5;j++){
System.out.print("*");
}
System.out.println();
}
}
}
3.2.4 练习1:打印正方形
****
****
****
****
for(int i=1;i<5;i++){
for(int j=1;j<5;j++){
System.out.print("*");
}
System.out.println();
}
3.2.5 练习2:打印左直角三角形
*
**
***
****
*****
//正直角三角形
private static void f4() {
for (int i = 0; i < 5; i++) {//外循环,控制行,是一定的
for (int j = 0; j <= i; j++) {//内循环,列是不固定的,是递增的
System.out.print("*");//保证第一行打印出来一个*,注意条件
}
System.out.println();
}
}
3.2.6 练习3:打印99乘法表
//99乘法表 正序
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
private static void f4() {
for (int i = 1; i < 9; i++) {//外循环,控制行,是一定的
for (int j = 1; j <= i; j++) {//内循环,列是不固定的,是递增的
System.out.print(i+"*"+j+"="+i*j+" ");//保证第一行打印出来一个*,注意条件
}
System.out.println();
}
}
//乘法表倒序
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
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*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*4=4 2*4=8 3*4=12 4*4=16
1*3=3 2*3=6 3*3=9
1*2=2 2*2=4
1*1=1
for(int i=9;i>=1;i--) {
for(int j=1;j<=i;j++) {
System.out.print(j+"*"+i+"="+i*j+"\t");
}
System.out.println();
}
3.2.7 练习4:打印右直角三角形
打印右直角
*
**
***
****
*****
package day999;
public class a {
public static void main(String[] args) {
//输出5行
for(int i=1;i<=5;i++){
//空格三角
for(int x=5;x>i;x--){//还可以改为:for(int x=1;j<=5-i;x++)
System.out.print(" ");
}
//*号三角
for(int j=1;j<=i;j++){
System.out.print("*");
}
System.out.println();
}
}
}
3.2.8 练习5:打印全三角形
打印全三角
*
***
*****
*******
*********
package day999;
public class a {
public static void main(String[] args) {
//打印5行
for(int i=1;i<=5;i++){
//打印空格的倒三角(原理是:每轮输出一个空格)
for(int j=5;j>=i;j--){ //可换:for(int j=1;j<=5-i;j++)
System.out.print(" ");
}
//打印*号的正三角
for(int k=1;k<=i*2-1;k++){
System.out.print("*");
}
System.out.println();
}
}
}
3.3 增强for循环
package lianxi;
import java.util.Random;
import java.util.Scanner;
public class Ta {
public static void main(String[] args) {
int [] numbers = {10,20,50,7,0,9,22,11};//定义一个数组
for (int aa: numbers) {
System.out.println(aa);
}
}
}
3.4 break和continue
用来终止循环,可以用这两种方式。
注意事项:break,continue直接项后面不能声明语句,否则编译或报错。如下图:
3.4.1 形式
break: 中断当前循环,简单粗暴
for(){
代码1
if(条件){
代码3…
break;//如果成立,直接跳出这个for循环
}
代码2…
}
continue:跳出本次循环,进入下一轮
for(){
代码1
if(条件){
代码3…
continue;//如果成立,跳出本次for循环,进入下一轮
}
代码2…
}
3.4.2 练习1:找数字88
接收用户输入的100次数字,如果不是88继续输入,找到88就返回
package cn.tedu.breakdemo;
import java.util.Scanner;
//测试break和continue
//总结:break和continue都可以结束循环。
//1,break可以立刻结束循环简单粗暴。continue是结束当前循环可以继续下一次循环。
//2,break和continue之后都不能出现代码,出现了也是不可到达的代码
public class Test3_Break {
public static void main(String[] args) {
for(int i = 1 ; i <= 100 ; i++) {
//1,接受用户输入的数字
int input = new Scanner(System.in).nextInt();
//2,判断是不是88
if(input == 88) {//是88
System.out.println("中了");
break;//程序结束
}else if(input != 88) {//不是88
continue;//继续输入
}
}
}
}
3.5 循环结构2:while
先判断,再执行
3.5.1 格式
while(执行条件){
代码…
}
3.5.2 练习1:产生随机数1~100,猜数字
产生一个随机数,和用户一直在输入的数字比较。
生成随机数具体查看Api
package cn.tedu.whiledemo;
import java.util.Random;
import java.util.Scanner;
/**
*测试while循环
*总结:
* 1.while也是一种循环结构:while(循环条件){循环体}
* 2.while(true)是简单的实现了死循环的形式,for也可以实现代码显得复杂一些。
* 3.这种结构 -- 先判断,再执行
*/
public class Test4_While {
public static void main(String[] args) {
/**
* 产生随机数的2种方法:
* 第1种:int num=(int)(Math.random()*1000+1); 生成随机数1~1000
* 解释:Math.random()返回的是0(包含)到1(不包含)之间的double值,转化为整数需要强转。
* 第2种:int random = new Random().nextInt(100) +1; 生成随机数1~100
* 解释:要产生m以内的随机数,默认从0开始 ,不包括m 如:0~100 不包括100(0~99)可以加个1变为(1~100)
*/
//1,让程序产生随机数
int random = new Random().nextInt(100) +1;
System.out.println(random);
//2,开始猜 --重复做a b的事情,放入循环结构中
//while(random > 50) {
//for形式的死循环:让循环条件一直为true就行
//for(int i = 1; i >= 1 ; i++ ) {
while(true) { //死循环 -- 必须设置程序的出口!!
//a,接受键盘输入的值
int input = new Scanner(System.in).nextInt();
//b,拿到输入的值,和 ,random去比
if(input > random) {
System.out.println("大了");
}else if(input < random) {
System.out.println("小了");
}else if(input == random) {
System.out.println("中了");
break;//程序结束 -- 死循环的出口!!!
}
}
}
}
3.5.2 练习2:产生随机数10~100
//如何获取一个随机数:10 - 99
// [0.0,1.0) --> [0.0,90.0) --->[10.0, 100.0) -->[10,99]
/*解释:
Math.random()生成double类型的值:[0.0,1.0),包含0无限接近于1。
乘以90变为[0.0,90.0),包含0无限接近于90。
加10变为[10.0, 100.0),包含10无限接近于100(99.99999999...)。
在强转为int类型,舍弃掉小数位变成-->[10,99]
*/
int value = (int)(Math.random() * 90 + 10);
System.out.println(value);
//公式:[a,b] : (int)(Math.random() * (b - a + 1) )+ a
3.6 循环结构3:do-while
先执行,再判断
3.6.1 格式
do{
代码…
}while(执行条件); //注意这里有个分号
3.6.2 练习1:猜数字
产生一个随机数,和用户一直在输入的数字比较。
package cn.tedu.whiledemo;
import java.util.Random;
import java.util.Scanner;
//测试do...while循环
//总结:
//do...while 结构,保证循环体代码 ,最少,执行一次
//能不能继续循环,要看能不能满足循环条件
public class Test5_Dowhile {
public static void main(String[] args) {
int random = new Random().nextInt(100) + 1;
System.out.println(random);
do {
// a,接受键盘输入的值
int input = new Scanner(System.in).nextInt();
// b,拿到输入的值,和 ,random去比
if (input > random) {
System.out.println("大了");
} else if (input < random) {
System.out.println("小了");
} else if (input == random) {
System.out.println("中了");
break;// 程序结束 -- 死循环的出口!!!
}
}while (random > 50);//明确循环条件
}
}
3.7 总结:三种循环的区别
三种循环都可以互相代替
1、for:知道循环次数(应用率最高)
2、while/do while:当循环次数不确定时
3、while:先判断,不符合规则,不执行代码(有可能一次都不执行)
4、do while:代码最少被执行一次,再去判断,符合规则,再次执行代码。
如何判断是用哪个循环?
1.如果与次数有关则用for循环
2.在判断条件1和条件3是否相同:如果相同则用do while 不同则用while (变量初始化,条件, 改变量)
4 Day05–方法+数组
4.1 方法
4.1.1 概述:方法(函数,过程)
1.方法:封装一段特定的业务逻辑功能。
2.方法尽可能的独立,一个方法只干一件事。
3.方法可以被反复多次调用。
4.减少代码的重复,有利于代码的维护,有利于团队的协作。
5.方法可以有参,也可以无参,多个参数用逗号隔开(看需求)。
6.方法与方法是同级并列关系。
7. 方法名命名规则:驼峰命名法。
8. 方法里面可以调用别的方法,但是方法里面不能定义方法。
4.1.2 形式
修饰符 返回值类型 方法名(参数列表){
方法体;
return 返回值;// void(无返回值) int,double,String…有返回值
}
4.1.3 练习1:方法调用
方法调用:根据方法的签名来调用。
方法的签名:方法名+参数列表
语法:
- 无返回值类型(void):
方法名(有参传参);
- 有返回值类型:
数据类型 变量 = 方法名(有参传参);
注意:
- 在一个类中,不可以有2个方法的签名完全相同。
- 只要返回值类型不为void,就一定有返回值retuen,接收时就需要声明一个数据类型来接收。
- 实参: 在调用方法时传递的参数(可以是确定的数 也可以是不确定的数)
- 形参: 定义方法时,在方法的参数列表定义的参数。
- 在方法调用时,会将实际的参数值传递给方法的参数变量。必须保证传递参数的类型和个数符合方法的声明。
说明:下面的调用都是在同一个类
中方法之间的调用,并且需要把方法修饰为static静态资源,因为静态方法之间可以相互调用。
如果想要在静态资源中调用非静态资源可以通过创建对象的方式调用。
package cn.tedu.method;
//包名可以直接在代码上修改,鼠标放上去会提示创建没有的包。
//同理 :类也一样
//测试方法的使用+-------------------------------
public class Test6_Method {
public static void main(String[] args) {
System.out.println(1);
function();//调用了指定的function()
System.out.println(2);
}
//创建function() 如果没有在main方法中调用方法,则程序不会主动执行这个方法。
//修饰符 返回值 方法名(参数列表){方法体}
public static void function() {
System.out.println(3);
System.out.println(4);
System.out.println(5);
}
}
通过创建对象可以在静态调用非静态:
package com.cn.ins;
//包名可以直接在代码上修改,鼠标放上去会提示创建没有的包。
//同理 :类也一样
//测试方法的使用+-------------------------------
public class Test6_Method {
public static void main(String[] args) {
System.out.println(1);
// function();//调用了指定的function()
System.out.println(2);
Test6_Method a=new Test6_Method();
a.function();
}
//创建function() 如果没有在main方法中调用方法,则程序不会主动执行这个方法。
//修饰符 返回值 方法名(参数列表){方法体}
public void function() {
System.out.println(3);
System.out.println(4);
System.out.println(5);
}
}
4.1.4 练习2:方法传参
package cn.tedu.method;
//测试方法的使用
public class Test6_Method {
public static void main(String[] args) {
System.out.println(1);
// function();//调用了指定的function()
// param(10);//方法传参
// param2("jack",10);//传两参数--叫实参
//注意:调用方法时传的参数叫做实参,不管是数字还是一个变量都是实参
// param3(5,10,"jack"); 参数的个数和类型相同,但是顺序不同也代表参数列表不同,方法不同。
param4("jack",5,10);
System.out.println(2);
//在括号外面赋值,与方法内直接赋值一样。
int m=10;
String n=”rose”;
Param5(m,n);
}
//创建param5的方法
public static void param5(inta,String b) {
System.out.println(a+b+);
}
//TODO 创建param4("jack",5,10) --方法传参:参数类型必须匹配
public static void param4(String a,int b,int c) {
System.out.println(a+b+c);//jack510
}
//TODO 创建param3(5,10,"jack") --方法传参:参数类型必须匹配
//多个参数之间用”,”隔开。
public static void param3(int a,int b,String c) {
//+在数字间是加法运算,和字符串+是拼接字符串
System.out.println(a+b+c);//15jack
}
//创建param2("jack",10) --方法传参:参数类型必须匹配
//String n,int a -- 叫形参:方法里面的参数叫形参
public static void param2(String n,int a) {
System.out.println(n);
System.out.println(a);
}
//创建param() --方法传参:参数类型必须匹配
//修饰符 返回值 方法名(参数列表){方法体}
// public static void param(参数类型 参数名) {
public static void param(int num) {
System.out.println(num);
}
//创建function()
//修饰符 返回值 方法名(参数列表){方法体}
public static void function() {
System.out.println(3);
System.out.println(4);
System.out.println(5);
}
}
4.1.5 练习3:方法返回值
return 返回值;
:方法结束后可以返回一个数据,称之为返回值,方法在声明时必须指定返回值的类型。
注意:
- 方法可以有返回值,也可以没有返回值(void)。
- return直接项之后不可以声明执行语句类似于break和continue,否则编译就会报错。
何时有返回值?何时无返回值?
当方法执行结束时:
- 若还需要用到方法中的某一个数据----有返回值(返回值类型不能为void,和return连用,有返回值就需要定义一个变量或数组来接受它。)
- 若不在需要用到方法中的某一个数据----没有返回值(返回值类型为void,不需要写return.)
Return两种形式的作用?
第一种:return 值 ;
1.结束方法的执行
2.返回给调用方。(注意:只要有返回值类型一定有return 数值)
第二种: return ;
3.结束方法的执行(注意:无返回值类型不需要写return,因为程序执行完照样结束,直接写没有意义。有时可以和if分支结构连用,用于跳出程序。)
package cn.tedu.method;
//测试方法返回值
public class Test7_Method2 {
public static void main(String[] args) {
//result记录着method()的执行结果a b的和
int result = method(10,5);
System.out.println(result);
//TODO
String name = method2("jack","rose","jerry");
System.out.println(name);
}
//TODO 创建 method2("jack","rose","jerry")
public static String method2(String a,String b,String c){
return a+b+c;//返回值不单单是一个变量,数。也可以返回一个式子
}
//创建method(10,5)
//注意:方法的返回值要和result变量的类型一致
public static int method(int a,int b) {
//通过 return 可以把方法执行的结果,返回给,调用位置!!
return a+b;
}
}
//注意:例子,返回的是不同类型看是否是字符串拼接,用String
String sum2=method("jack",10,5);
System.out.println(sum2);
public static String method(String c,int a,int b) {
return c+a+b; //这里是一个字符串的拼接所以用String类型。返回值类型与return后的类型一致。
}
4.2 方法的重载Overload
4.2.1 概念
1.方法重载是指在同一个类中,方法名相同,参数列表不同,方法体一般不同,相同没啥意义。重载和方法体没有关系。
2.编译器在编译期间会根据方法的签名自动绑定调用不同的方法。(我们可以把重载的方法可以看成是完全不同的方法,只不过恰恰好方法名相同而已。
3.重载针对的是方法。
注意:判断方法重载跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
重载的意义:对于程序员而言,需要记忆的方法名很简单只需要记住一个名字的方法,方法的参数可以很灵活的传入。
参数列表不同:包括参数类型,顺序,个数。
4.2.2 练习1:数字求和
package cn.tedu.overload;
//测试方法重载:
//要求:方法名相同+参数列表不同
//意义:由于用户在使用时,传入的参数我们不知道,为了体现我的程序非常的灵活
//尽量多提供一些参数不同,名字相同的方法
public class Test8_Overload {
public static void main(String[] args) {
add(5);
add(10,5);
add("jack",5);
add(10,"rose");
//TODO
print(10);
print("jerry");
print('中');
}
//创建add(10,"rose")
public static void add(int a,String b) {
System.out.println(a+b);//10rose
}
//创建add("jack",5)
public static void add(String a,int b) {
System.out.println(a+b);//jack5
}
//创建add(10,5)
public static void add(int a,int b) {
System.out.println(a+b);
}
//创建add(5)
public static void add(int a) {
System.out.println(a*a);
}
}
//总结:方法,传参,数据类型与参数列表保持一致,(即:个数,类型,顺序保持一致)。
//返回值:若有返回值应与返回值类型,返回值接收的类型保持一致。
//如:
public static void main(String[] args) {
//方法练习
method(2,3,"小明");
String aa=method2(2,3,"小红");
System.out.println(aa);
}
public static void method(int a,int b,String c) {
System.out.println(a+b+c);
}
public static String method2(int a,int b,String c) {
return a+b+c;//有String类型,int 类型。它拼接的是字符串类型,所以应该用String作为它的返回值类型,它的接受值也是String类型。
}
4.2.3 练习2:数据的打印
package day004;
public class Test07 {
public static void main(String[] args) {
// TODO Auto-generated method stub
print(10);
print(2.2);
print(true);
print("jack");
}
public static void print(int a) {
System.out.println(a);
}
public static void print(double a) {
System.out.println(a);
}
public static void print(boolean a) {
System.out.println(a);
}
public static void print(String name) {
System.out.println(name);
}
}
4.2.4 练习3:重载的自动类型提升
方法重载时,如果调用方法时输入的实参没有对应的方法,而恰好又满足自动类型转换,则会调用符合条件的大类型的方法。
package com.cn.ca;
public class StudentTest {
public static void main(String[] args) {
Teacher teacher = new Teacher();
/*解释:自动类型提升
* getSum方法没有注释之前根据方法的调用规则,调用getSum(int i,int j)输出为1
* getSum方法没有注释之后虽然调用时输入的值为int类型,但会自动类型提升,调用getSum(double d1,double d2)输出为2
*/
teacher.getSum(1, 5);
}
}
class Teacher{
// public void getSum(int i,int j){
// System.out.println("1");
// }
public void getSum(double d1,double d2){
System.out.println("2");
}
}
4.3 可变参数
可变参数本质上是数组。
package com.cn.ca;
/*
* 可变个数形参的方法
*
* 1.jdk 5.0新增的内容
* 2.具体使用:
* 2.1 可变个数形参的格式:数据类型 ... 变量名
* 2.2 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,。。。
* 2.3 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
* 2.4 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
* 2.5 可变个数形参在方法的形参中,必须声明在末尾
* 2.6 可变个数形参在方法的形参中,最多只能声明一个可变形参。
* 2.7 同一类型普通方式优先级比可变参数更高
* 2.8 遍历方式和数组相同
*
*
*/
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show(12);
// test.show("hello");
// test.show("hello","world");
// test.show();
//调用方法形参是数组相比是可变参数更麻烦些,当然这个数组的调用方式也可以调用可变参数
test.show(new String[]{"AA","BB","CC"});
}
public void show(int i){
}
public void show(String s){//同一类型普通方式优先级比可变参数更高
System.out.println("show(String)");
}
public void show(String ... strs){//调用时输入的参数可以是0个,可以是多个
System.out.println("show(String ... strs)");
for(int i = 0;i < strs.length;i++){//遍历方式和数组相同
System.out.println(strs[i]);
}
}
/*不能与上一个方法同时存在String[] strs和String ... strs,
编译器认为方法名相同 参数列表相同的数组和可变参数是同一个方法,不能共存。
*/
// public void show(String[] strs){
//
// }
//The variable argument type String of the method
//show must be the last parameter
// public void show(String ...strs,int i){ 多个参数,可变参数只能声明在最后
//
// }
}
4.4 方法的值传递
4.4.1 回顾:变量的赋值操作
package com.cn.ca;
/*
*
* 关于变量的赋值:
*
* 如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
* 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
*
*/
public class ValueTransferTest {
public static void main(String[] args) {
System.out.println("***********基本数据类型:****************");
int m = 10;
int n = m;
System.out.println("m = " + m + ", n = " + n);//m = 10, n = 10
n = 20;
System.out.println("m = " + m + ", n = " + n);//m = 10, n = 20
System.out.println("***********引用数据类型:****************");
Order o1 = new Order();
o1.orderId = 1001;
Order o2 = o1;//赋值以后,o1和o2的地址值相同,都指向了堆空间中同一个对象实体。
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);//o1.orderId = 1001,o2.orderId = 1001
o2.orderId = 1002;//改变了o2的值 o1也随之改变
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);//o1.orderId = 1002,o2.orderId = 1002
}
}
class Order{
int orderId;
}
4.4.2 同理:方法的形参赋值操作–基本数据类型
package com.cn.ca;
/*
* 方法的形参的传递机制:值传递
*
* 1.形参:方法定义时,声明的小括号内的参数
* 实参:方法调用时,实际传递给形参的数据
*
* 2.值传递机制:
* 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
* 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
*
*/
public class ValueTransferTest1 {
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println("m = " + m + ", n = " + n);//m = 10, n = 20
//交换两个变量的值的操作
// int temp = m ;
// m = n;
// n = temp;
ValueTransferTest1 test = new ValueTransferTest1();
test.swap(m, n);
System.out.println("m = " + m + ", n = " + n);//m = 10, n = 20 此时仍输出的是main中的m n而不是swap方法中的m n
}
public void swap(int m,int n){//相当于是把10 20赋值给了新的变量m n,形参是局部变量出了方法就失效
//如果复制的是引用地址,堆的数据只有一份可以交换成功,出了方法也不会失效
int temp = m ;
m = n;
n = temp;
System.out.println("m = " + m + ", n = " + n);//m = 20, n = 10
}
}
4.4.3 同理:方法的形参赋值操作–引用数据类型
package com.cn.ca;
public class ValueTransferTest2 {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = " + data.m + ", n = " + data.n);//m = 10, n = 20
//交换m和n的值
// int temp = data.m;
// data.m = data.n;
// data.n = temp;
ValueTransferTest2 test = new ValueTransferTest2();
test.swap(data);
System.out.println("m = " + data.m + ", n = " + data.n);//m = 20, n = 10交换成功
}
public void swap(Data data){
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data{
int m;
int n;
}
4.5 递归
注意:
- 递归层级不能太多,如果过多会导致电脑效率大大降低。
- 能不用递归就不用,可以通过其它一些算法来实现。
4.5.1 递归案例练习
package com.atguigu.java2;
/*
* 递归方法的使用(了解)
* 1. 递归方法:一个方法体内调用它自身。
* 2. 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无需循环控制。
* 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
*
*/
public class RecursionTest {
public static void main(String[] args) {
// 例1:计算1-100之间所有自然数的和
// 方式一:for循环
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println(sum);
// 方式二:递归
RecursionTest test = new RecursionTest();
int sum1 = test.getSum(100);
System.out.println(sum1);
System.out.println("*****************");
int value = test.f(10);
System.out.println(value);
}
// 例1:计算1-n之间所有自然数的和
public int getSum(int n) {// 3
if (n == 1) {
return 1;
} else {
return n + getSum(n - 1);
}
}
// 例2:计算1-n之间所有自然数的乘积:n! 阶乘如5*4*3*2*1
public int getSum1(int n) {
if (n == 1) {
return 1;
} else {
return n * getSum1(n - 1);
}
}
//例3:已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),
//其中n是大于0的整数,求f(10)的值。
public int f(int n){
if(n == 0){
return 1;
}else if(n == 1){
return 4;
}else{
// return f(n + 2) - 2 * f(n + 1);
return 2*f(n - 1) + f(n - 2);
}
}
//例4:斐波那契数列
//例5:汉诺塔问题
//例6:快排
}
4.6 数组
4.6.1 数组的概述和分类
概述:数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
数组的分类:
- ① 按照维数:一维数组、二维数组,…
- ② 按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组
4.6.2 数组相关的概念
数组Array的标志是:[ ]
获取数组中的元素值:可以通过元素的下标来获取,下标是从0开始的,下标的最大值是它的长度-1。
下标:又叫作角标、索引
数组的长度:元素的个数
数组的类型:数组 Array是引用类型,储存的是地址,地址在空间中占得内存很少(就是一串数字)。数组的元素,既可以是基本数据类型,也可以是引用数据类型
4.6.3 数组的特点
- 数组是有序排列的。
- 创建数组对象会在内存中开辟一整块连续的空间。
- 数组的长度是确定的,一旦创建,就不能修改。
- 数组中的元素是相同类型,不允许出现混合类型。
- 数组属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型。
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的的成员变量。
数组本身就是对象,java中对象是在堆中,因此数组数组无论保存原始类型还是其它对象类型,数组对象本身是在堆中的。
4.6.4 一维数组的声明
一般分为动态初始化和静态初始化.
语法:
数组类型[ ] 数组名 = 数组对象;//推荐使用
或者
数组类型 数组名[ ] = 数组对象;//不推荐使用,这是c++语言的习惯,早期为了c++的程序员学习java创建的。
案例:
动态初始化:先声明后赋值,需要指定存入的元素个数。
int[] a = new int[4];//数组的类型应该与所存入的每一个数据类型一致
a[0] =1;
a[1] =4;
a[2] =7;
a[3] =5;
静态初始化:声明数组的同时赋值
int[] b = new int[]{1,2,3,4,5};//数组之间的元素用逗号隔开。
int[] c = {1,2,3,4,5};//简写形式
数组的默认初始化:数组是引用类型,它的元素相当于类的实例化变量,因此数组已经分配空间,其中每个元素也按照实例变量同样的方式被隐式初始化。
int[] aa = new int[5];
此时已经在内存中分配好空间了,每个元素的默认值都为int类型的值 0.
4.6.5 数组的长度
- length属性获取数组长度。
- 数组一旦创建,长度不可变 ,即:数组是定长的。
- 允许0长度的数组。
4.6.6 数组的边界
4.6.7 练习1:数组中存入hello
package cn.tedu.array;
import java.util.Scanner;
//这个类用来测试数组的创建
public class Test1_Array {
public static void main(String[] args) {
//1,创建数组。存入hello
//确定数组类型,数组长度
// 静态
//因为每个格子放一个字符,所以用char类型。
char[] a = new char[] {'h','e','l','l','o'};
char[] b = {'h','e','l','l','o'};
System.out.println(a);
System.out.println(b);
// 2,动态 创建数组,需要,指定数组的长度!!
char[] c = new char[5];
//注意:存字符串是用的char 数组。不是string
//3,数组中的元素都有下标,默认从0开始
c[0]='h';//给第1个元素,也就是下标为0的元素,赋值
c[1]='e';//给第2个元素,也就是下标为1的元素,赋值
c[2]='l';//给第3个元素,也就是下标为2的元素,赋值
c[3]='l';//给第4个元素,也就是下标为3的元素,赋值
c[4]='o';//给第5个元素,也就是下标为4的元素,赋值
//4,java.lang.ArrayIndexOutOfBoundsException数组下标越界异常
//原因是数组的长度如果是5,最大下标是4,根本不存在下标为5的情况,下标5超出范围
//c[5]='o';//给第6个元素,也就是下标为5的元素,赋值 --- 报错!!
System.out.println(c);
}
}
4.7 数组的遍历
从头到尾,依次访问数组的位置。
4.7.1 形式
1.普通for循环
for(从下标为0的位置开始;到数组长度-1结束;下标++){
循环体代码
}
2.增强for循环,但是取不到下标。
for(声明语句:表达式){
循环体代码
}
4.7.2 练习1:增强for循环遍历
package lianxi;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Ta {
public static void main(String[] args) {
int[] arrays = {1,3,6,3,8,9,0};
for(int aa:arrays) {
System.out.println(aa);
}
}
}
4.7.3 练习2:输出每个月的天数
package cn.tedu.array;
//数组练习
public class Test2_Array2 {
public static void main(String[] args) {
method();// 输出每个月的天数
}
// 创建method()
public static void method() {
// 1,静态创建数组
int[] days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// 2,用下标遍历数组
// i代表了下标
// int i = 0 ,从下标 0开始
// i <= days.length-1,到下标的最大值结束
// i++,依次向后遍历
for (int i = 0; i <= days.length - 1; i++) {
// days[i] 指days数组中,每个下标存着的值
System.out.println((i + 1) + "月有" + days[i] + "天");
}
}
}//直接输出数组则输出的是地址,输出对应的下标才是它代表的元素
依次输出数组中每个下标代表的元素。
4.7.4 练习3:遍历数组,存入1到10
package lianxi;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Ta {
public static void main(String[] args) {
//1,动态 创建数组
int[] a = new int[10];
System.out.println(Arrays.toString(a));//[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
//2,遍历数组,存数据
for(int i = 0 ; i <= a.length-1 ; i++ ) {
//获取每个元素a[i]
a[i] = i+1;
}
//3,查看数组中的元素
System.out.println(a);//[I@659e0bfd]
/**
* 除了char数组可以直接输出里面的元素,其它的数组都是地址.
* 想看不是char[]的数组里的元素是啥--Arrays工具类 toString()可以将指定的数组数据显示成一个字符串
*/
System.out.println(Arrays.toString(a));//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
}
直接输出数组:
4.7.5 练习4:创建随机数组
获取100以内的随机值的数组:new Random()方式
// 创建method3()
public static void method3() {
//1, 动态 创建数组
int a[] = new int[10];
//2. 遍历数组,重新赋值
for(int i = 0 ; i <= a.length-1 ; i++) {
//a[i]获取元素
a[i]=new Random().nextInt(100);
}
//3. 查看数组里的元素
System.out.println(Arrays.toString(a));
}
4.7.6 练习5:创建随机数组求 最大 最小 平均数
获取[10,99]以内的随机值的数组:new Random()方式
package com.atguigu.java;
/*
* 算法的考查:求数值型数组中元素的最大值、最小值、平均数、总和等
*
* 定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,
* 然后求出所有元素的最大值,最小值,和值,平均值,并输出出来。
* 要求:所有随机数都是两位数。
*
* [10,99]
* 公式:(int)(Math.random() * (99 - 10 + 1) + 10)
*
*/
public class ArrayTest1 {
public static void main(String[] args) {
int[] arr = new int[10];
for(int i = 0;i < arr.length;i++){
arr[i] = (int)(Math.random() * (99 - 10 + 1) + 10);
}
//遍历
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + "\t");
}
System.out.println();
//求数组元素的最大值
int maxValue = arr[0];//注意这个地方写 0,如果里面的值都是负数无法比较。
for(int i = 1;i < arr.length;i++){
if(maxValue < arr[i]){
maxValue = arr[i];
}
}
System.out.println("最大值为:" + maxValue);
//求数组元素的最小值
int minValue = arr[0];
for(int i = 1;i < arr.length;i++){
if(minValue > arr[i]){
minValue = arr[i];
}
}
System.out.println("最小值为:" + minValue);
//求数组元素的总和
int sum = 0;
for(int i = 0;i < arr.length;i++){
sum += arr[i];
}
System.out.println("总和为:" + sum);
//求数组元素的平均数
int avgValue = sum / arr.length;
System.out.println("平均数为:" + avgValue);
}
}
4.7.7 练习6:数组的赋值解释
package com.atguigu.exer;
/*
* 使用简单数组
(1)创建一个名为ArrayExer2的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
(2)使用大括号{},把array1初始化为8个素数:2,3,5,7,11,13,17,19。
(3)显示array1的内容。
(4)赋值array2变量等于array1,修改array2中的索引元素,使其等于索引值(如array[0]=0,array[2]=2)。打印出array1。
*
* 思考:array1和array2是什么关系?array1和array2地址值相同,都指向了堆空间的唯一的一个数组实体。
* 拓展:修改题目,实现array2对array1数组的复制
*/
public class ArrayExer2 {
public static void main(String[] args) { //alt + /
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
//显示array1的内容
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
//赋值array2变量等于array1
//不能称作数组的复制。
array2 = array1;
/*修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)
即下标为偶数时,元素值等于它本身的下标值。
*/
for(int i = 0;i < array2.length;i++){
if(i % 2 == 0){
array2[i] = i;
}
}
System.out.println();
//打印出array1,发现结果和修改后的array2结果相同
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
}
}
解释:因为只new了一次对象,堆里面只有一个值。
array1 = new int[]{2,3,5,7,11,13,17,19};
array2 = array1;
这2步表示把堆得地址值赋给array1,再把array1的地址值赋值给array2,array1 array2此时地址值相同。而修改array2的值也就是堆得值只有一个,那么array1的值当然也发生了变化。
现实类比:相当于在磁盘创建一个文件,之后发送桌面快捷方式。你在桌面打开和在磁盘打开的文件里的内容相同。
4.7.8 练习7:数组的复制 反转 查找(线型 二分)
package com.atguigu.java;
/*
* 算法的考查:数组的复制、反转、查找(线性查找、二分法查找)
*
*
*/
public class ArrayTest2 {
public static void main(String[] args) {
String[] arr = new String[]{"JJ","DD","MM","BB","GG","AA"};
//数组的复制(区别于数组变量的赋值:arr1 = arr)
//相当于磁盘文件复制到桌面一份,对桌面的文件进行修改
String[] arr1 = new String[arr.length];
for(int i = 0;i < arr1.length;i++){
arr1[i] = arr[i];
}
//数组的反转
/*方法一:不管是除进还是除不尽都不影响,除于2是因为 第一步第一个元素和最后一个进行交换
最后一步 最后一个与第一个进行交换,相当于交换了2次。*/
// for(int i = 0;i < arr.length / 2;i++){
// String temp = arr[i];
// arr[i] = arr[arr.length - i -1]; 把最后元素赋值给第一个
// arr[arr.length - i -1] = temp; 把第一个交换给第二个
// }
//方法二:
// for(int i = 0,j = arr.length - 1;i < j;i++,j--){
// String temp = arr[i];
// arr[i] = arr[j];
// arr[j] = temp;
// }
//遍历
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + "\t");
}
System.out.println();
//查找(或搜索)
//线性查找:重头到尾依次查找
String dest = "BB";
dest = "CC";
boolean isFlag = true;
for(int i = 0;i < arr.length;i++){
if(dest.equals(arr[i])){
System.out.println("找到了指定的元素,位置为:" + i);
isFlag = false;
break;
}
}
if(isFlag){
System.out.println("很遗憾,没有找到的啦!");
}
//二分法查找:(熟悉)先分为2半,如果查找的第1份有对应值,则另一份就不找了。效率更高
//前提:所要查找的数组必须有序。
int[] arr2 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int dest1 = -34;
dest1 = 35;
int head = 0;//初始的首索引
int end = arr2.length - 1;//初始的末索引
boolean isFlag1 = true;
while(head <= end){
int middle = (head + end)/2; //4
if(dest1 == arr2[middle]){
System.out.println("找到了指定的元素,位置为:" + middle);
isFlag1 = false;
break;
}else if(arr2[middle] > dest1){
end = middle - 1;
}else{//arr2[middle] < dest1
head = middle + 1;
}
}
if(isFlag1){
System.out.println("很遗憾,没有找到的啦!");
}
}
}
4.8 数组工具类Arrays
java.util.Arrays:操作数组的工具类,里面定义了很多操作数组的方法
说明:像数组的排序,复制,二分算法等封装好了,我们用的时候直接调用工具类方法即可,不像在上面自己手写。开发中如果工具类有对应的方法使用工具类没有的在自己手写。
4.8.1 Arrays.toString(数组)
把数组里的数据,用逗号连接成一个字符串。(返回值是String)
格式:[10, 14, 20, 46, 51]
package day006;
import java.util.Arrays;
public class Test02 {
public static void main(String[] args) {
// to String
method();
}
public static void method() {
int[] aa= {1,2,3,4,5,6};
//直接输出整个数组不需要遍历,之前遍历是插入。
// for(int i=0;i<aa.length-1;i++) {
// }
//直接打印是地址值如:[I@15db9742使用工具类后是:[1, 2, 3, 4, 5, 6]
System.out.println(Arrays.toString(aa));
}
}
4.8.2 Arrays.sort(数组)
对数组升序排序:对于基本类型的数组使用优化后的快速排序算法,效率高。对引用类型数组,使用优化后的合并排序算法。
无返回值。
例子:
package lianxi;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Ta {
public static void main(String[] args) {
int[] as = {4,9,4,1,2,5,9,6,4,5,2};
Arrays.sort(as);
System.out.println(Arrays.toString(as));
}
}
4.8.3 Arrays.copyOf(数组,新的长度)
把数组复制成一个指定长度的新数组。(改变的只是新数组,原数组没有发生变化)
新数组长度大于原数组,相当于复制,并增加位置。-- 数组的扩容
新数组长度小于原数组,相当于截取前一部分数据。-- 数组的缩容
有返回值。
4.8.4 测试toString, sort, copyOf
package cn.tedu.array;
import java.util.Arrays;
//测试数组工具类
//为什么sort()没有返回值是,void
//为什么copyOf()有返回值,如:int[]
//原因是:sort()在原数组基础上,把数组调整位置
//copyOf():数组一旦创建,长度不可变!!!所以必须把产生的新数组返回/而这里改变了长度。
public class Test4_Arrays {
public static void main(String[] args) {
int[] a = {97, 7, 41, 37, 55, 3, 3, 34, 34, 83};
// sortArray(a);//a是实参,给无序数组排序
int[] newA = copyArray(a);//数组复制a是原数组
// [97, 7, 41, 37, 55, 3, 3, 34, 34, 83, 0, 0, 0, 0, 0]
// [97, 7, 41, 37, 55]
System.out.println(Arrays.toString(newA));
}
//创建copyArray第一种方式,可以扩容,缩容(及新数组的长度大于小于新数组的长度都可以)
public static int[] copyArray(int[] from) {
//copyOf(from,num)--from是原数组名,num是新数组长度 -- 新数组长度 > 原数组长度 -- 扩容
int[] to = Arrays.copyOf(from, 15);
// [97, 7, 41, 37, 55, 3, 3, 34, 34, 83, 0, 0, 0, 0, 0]
//from是原数组名,5是新数组长度 -- 新数组长度 < 原数组长度 -- 缩容 --相当于截取数据。
int[] to2 = Arrays.copyOf(from, 5);
// [97, 7, 41, 37, 55]
return to2;
}
//创建sortArray
public static void sortArray(int[] a) {//形参
//sort()用来对无序数组进行排序
Arrays.sort(a);//底层源码
// [3, 3, 7, 34, 34, 37, 41, 55, 83, 97]
//toString()对指定的数组内容,显示成字符串
System.out.println(Arrays.toString(a));
}
}
例子:
//数组的复制 第2种arraycopy 都是在新的数组上改变,注意两者写法的区别。
int [] we= {4,8,5,8,9}; //新数组小了自动截取,大了会自动填充。
int[] we1=Arrays.copyOf(we, 4);
System.out.println(Arrays.toString(we));
System.out.println(Arrays.toString(we1));
System.out.println("11111111111");//打桩
int[] we2=new int[5];//这个是新数组长度小则会有数组下标越界异常。那个不会。优点是显得更灵活。
System.arraycopy(we,0,we2,0,5);
System.out.println(Arrays.toString(we2));
//数组的扩容
int we3[]=Arrays.copyOf(we, we.length+1);
System.out.println(Arrays.toString(we3));
4.8.5 Arrays.equals
//boolean equals(int[] a,int[] b):判断两个数组是否相等。
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,3,2,4};
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals);//false
4.8.6 Arrays.fill
int[] arr1 = new int[]{1,2,3,4};
//void fill(int[] a,int val):将指定值填充到数组之中。
Arrays.fill(arr1,10);
System.out.println(Arrays.toString(arr1));//[10, 10, 10, 10]
4.8.7 Arrays.binarySearch二分算法查找
//int binarySearch(int[] a,int key)
int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int index = Arrays.binarySearch(arr3, 210);
if(index >= 0){//返回值为整数代表找到,为什么正负代表找到没找到,因为源码是这样设计的
System.out.println(index);
}else{//返回值为负数没找到
System.out.println("未找到");
}
4.9 数组常见异常
package com.atguigu.java;
/*
* 数组中的常见异常:
* 1. 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion
*
* 2. 空指针异常:NullPointerException
*
*/
public class ArrayExceptionTest {
public static void main(String[] args) {
//1. 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion
int[] arr = new int[]{1,2,3,4,5};
// for(int i = 0;i <= arr.length;i++){
// System.out.println(arr[i]);
// }
// System.out.println(arr[-2]);
// System.out.println("hello");
//2.2. 空指针异常:NullPointerException
//情况一:原来有地址,现在改为Null了没有地址,在根据地址值找对的值
// int[] arr1 = new int[]{1,2,3};
// arr1 = null;
// System.out.println(arr1[0]);
//情况二:
// int[][] arr2 = new int[4][];
// System.out.println(arr2[0][0]);
//情况三:
String[] arr3 = new String[]{"AA","BB","CC"};
arr3[0] = null;
System.out.println(arr3[0].toString());
}
}
4.10 拓展:
4.10.1 了解二维数组
存放数组的数组,也就是说数组里存的还是数组的数据形式。
案例1:
public class Test06 {
//ctrl +n 快捷键相当于new在用上下键 (创建项目,包,类全程不用鼠标)
public static void main(String[] args) {
// 二维数组: 数组套数组
//格式: 定位:如,c[0][1] 遍历2次.
int [][] c={{1,2},{3,4},{5,6}};//声明形式
for(int i=0;i<=c.length-1;i++) {//遍历外层数组
for(int j=0;j<=c[i].length-1;j++) { //遍历内层数组
System.out.println(c[i][j]);//输出形式
}
}
案例2:
package com.atguigu.java;
/*
* 二维数组的使用
*
* 1.理解:
* 对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。
* 其实,从数组底层的运行机制来看,其实没有多维数组。
*
* 2. 二维数组的使用:
* ① 二维数组的声明和初始化
* ② 如何调用数组的指定位置的元素
* ③ 如何获取数组的长度
* ④ 如何遍历数组
* ⑤ 数组元素的默认初始化值 :见 ArrayTest3.java
* ⑥ 数组的内存解析 :见 ArrayTest3.java
*
*
*/
public class ArrayTest2 {
public static void main(String[] args) {
//1.二维数组的声明和初始化
int[] arr = new int[]{1,2,3};//一维数组
//静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
//动态初始化1
String[][] arr2 = new String[3][2];
//动态初始化2
String[][] arr3 = new String[3][];
//错误的情况
// String[][] arr4 = new String[][4];
// String[4][3] arr5 = new String[][];
// int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};
//也是正确的写法:
int[] arr4[] = new int[][]{{1,2,3},{4,5,9,10},{6,7,8}};
int[] arr5[] = {{1,2,3},{4,5},{6,7,8}};
//2.如何调用数组的指定位置的元素
System.out.println(arr1[0][1]);//2
System.out.println(arr2[1][1]);//null
arr3[1] = new String[4];
System.out.println(arr3[1][0]);
//3.获取数组的长度
System.out.println(arr4.length);//3
System.out.println(arr4[0].length);//3
System.out.println(arr4[1].length);//4
//4.如何遍历二维数组
for(int i = 0;i < arr4.length;i++){
for(int j = 0;j < arr4[i].length;j++){
System.out.print(arr4[i][j] + " ");
}
System.out.println();
}
}
}
案例3:
package com.atguigu.java;
/*
* 二维数组的使用:
* 规定:二维数组分为外层数组的元素,内层数组的元素
* int[][] arr = new int[4][3];
* 外层元素:arr[0],arr[1]等
* 内层元素:arr[0][0],arr[1][2]等
*
* ⑤ 数组元素的默认初始化值
* 针对于初始化方式一:比如:int[][] arr = new int[4][3];
* 外层元素的初始化值为:地址值
* 内层元素的初始化值为:与一维数组初始化情况相同
*
* 针对于初始化方式二:比如:int[][] arr = new int[4][];
* 外层元素的初始化值为:null
* 内层元素的初始化值为:不能调用,否则报错。
*
* ⑥ 数组的内存解析
*
*/
public class ArrayTest3 {
public static void main(String[] args) {
int[][] arr = new int[4][3];
System.out.println(arr[0]);//[I@15db9742
System.out.println(arr[0][0]);//0
// System.out.println(arr);//[[I@6d06d69c
System.out.println("*****************");
float[][] arr1 = new float[4][3];
System.out.println(arr1[0]);//地址值
System.out.println(arr1[0][0]);//0.0
System.out.println("*****************");
String[][] arr2 = new String[4][2];
System.out.println(arr2[1]);//地址值
System.out.println(arr2[1][1]);//null
System.out.println("*****************");
double[][] arr3 = new double[4][];
System.out.println(arr3[1]);//null
// System.out.println(arr3[1][0]);//报错
}
}
4.10.2 了解冒泡排序
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
冒泡排序解释:
- 比较数组中2个相邻的元素,如果第一个数比第二个数大,我们就交换他的位置。
- 每一次比较都会产生一个最大或者最小的数字。
- 下一轮则可以减少一次排序。
- 依次循环直到结束。
冒泡排序的优化:有很多,最基础的可以通过变量标识进行判断,如果排序正确则,没有必要进行比较
4.10.3 冒泡排序代码
package cn.tedu.bubble;
//第一种 20个数比 20轮 每轮比-i-1次
//第二种 20个数比19轮 每轮比-i次
import java.util.Arrays;
//这个类用来测试冒泡排序算法
public class Test3_Bubble {
public static void main(String[] args) {
//1,定义无序数组
int[] a = {21, 94, 21, 58, 47, 16, 10, 9, 50, 44};
//2,排序
//5个数,比4轮。外循环控制轮数,让外循环执行4次
//i < a.length-1 a.length是5,-1 就是 4,i<4,外循环执行4次
for(int i = 0 ; i < a.length-1 ; i++ ) {
//内循环控制每轮需要相邻比较多少次
//-i :第1轮,需要比4次,五个数参与比较 -0,
//第2轮,需要比3次,四个数参与比较 -1,(因为第一轮沉出来一个大值不参与比较)
//第3轮,需要比2次,四个数参与比较 -2,(因为前两轮沉出来两个大值不参与比较)
//第4轮,需要比1次,四个数参与比较 -3,(因为前三轮沉出来三个大值不参与比较)
for(int j = 0 ; j < a.length-1 - i; j++ ) {
//1,相邻元素比较
//前一个值a[j]大于后面的值a[j+1]
if(a[j]>a[j+1]) {
//2,交换顺序s
int t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
}
System.out.println(Arrays.toString(a));
}
}
4.10.4 冒泡排序代码优化写法
package lianxi;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Ta {
public static void main(String[] args) {
//1,定义无序数组
int[] a = {21, 94, 21, 58, 47, 16, 10, 9, 50, 44};
//2,排序
//5个数,比4轮。外循环控制轮数,让外循环执行4次
//i < a.length-1 a.length是5,-1 就是 4,i<4,外循环执行4次
for(int i = 0 ; i < a.length-1 ; i++ ) {
boolean flag=false;//通过flag标识减少没有意义的比较
//内循环控制每轮需要相邻比较多少次
//-i :第1轮,需要比4次,五个数参与比较 -0,
//第2轮,需要比3次,四个数参与比较 -1,(因为第一轮沉出来一个大值不参与比较)
//第3轮,需要比2次,四个数参与比较 -2,(因为前两轮沉出来两个大值不参与比较)
//第4轮,需要比1次,四个数参与比较 -3,(因为前三轮沉出来三个大值不参与比较)
for(int j = 0 ; j < a.length-1 - i; j++ ) {
//1,相邻元素比较
//前一个值a[j]大于后面的值a[j+1]
if(a[j]>a[j+1]) {
//2,交换顺序s
int t = a[j];
a[j] = a[j+1];
a[j+1] = t;
flag=true;
}
}
if(flag==false) {
break;
}
}
System.out.println(Arrays.toString(a));
}
}
4.10.5 了解其他算法
如:合并算法,二分算法,快速算法等,冒泡最常见,也是面试中出现率最高的
4.10.6 稀疏数组
B站狂神稀疏数组视频连接:https://www.bilibili.com/video/BV12J41137hu?p=59&spm_id_from=pageDriver