Java程序是什么
在这之前先说明一下:JavaSE系列的文章,小编这边看时间会在3-7天左右更新一篇(如果有事或其他情况可能会晚几天,但基本不会)
一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象.
方法和实例变量的概念。
- 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
- 类:类是一个模板,它描述一类对象的行为和状态。
- 方法:方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
- 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
注释
平时我们编写代码,在代码量比较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到一个注释了,注释就类似于我们上学时候写的笔记,我们看着笔记就知道自己写的什么东西了!在程序中也是如此。我们来看一下Java中的注释怎么写,看以下代码:
/**
* 主类Main包含了程序的入口点
* 该类的目的是演示程序的启动点
*/
public class Main {
/**
* 程序的入口方法
* @param args 命令行参数,本程序中未使用
*/
public static void main(String[] args) {
// 输出问候语,作为程序启动的标识
System.out.println("Hello world!");
}
}
注释并不会被执行,是给我们写代码的人看的,书写注释是一个非常好的习惯,在很多大公司都是强制要求各位去进行编写注释!
Java中的注释有三种:
- 单行注释:只能注释当前行,以//开始,直到行结束
- 多行注释:注释一段文字,以/*开始, */结束!
- 文档注释:用于生产API文档,配合JavaDoc。【注】文档注释我们现在只作为了解,目前知道有这样的注释就好。
// 输出HelloWorld!-- 这是单行注释
/*
这是多行注释
这是我们Java程序的主入口,
main方法也是程序的主线程。
*/
/*
* 这是文档注释
*
* @Description Hello World类
* @Author Calm
**/
Java标识符
Java所有的组成部分都需要名字,比如我们的HelloWorld程序:HelloWorld是类名,也是我们的文件名。它前面的 public class是关键字,不过是搞Java那群人已经定义好的有特殊作用的!类名、变量名以及方法名都被称为标识符。
关于Java标识符,有以下几点需要注意:
- 所有的标识符都应该以字母(A-Z或者a-z),美元符($)、或者下划线(_)开始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线()或数字的任何字符组合
- 关键字不能用作标识符
- 标识符是大小写敏感的
- 合法标识符举例:age、$salary、_value
- 非法标识符举例:123abc、-salary
Java的关键字,不要去背(你很闲当我没说):
abstract | assert | boolean | break | byte |
---|---|---|---|---|
case | catch | char | class | const |
continue | default | do | double | else |
enum | extends | final | finally | float |
for | goto | if | implements | import |
instanceof | int | interface | long | native |
new | package | private | protected | public |
return | strictfp | short | static | super |
switch | synchronized | this | throw | throws |
transient | try | void | volatile | while |
这些看起来非常的多,不要去背,但是随着以后的学习我们都会用到,所以完全不用担心自己看不懂或者记不住,这些被Java已经规定的关键字,我们自己就不能拿它当做名字了!
命名的规范:
- 表示类名的标识符用大写字母开始。
- 表示方法和变量的标识符用小写字母开始,后面的描述性词以大写开始。
如:eat(),eatFood()
//驼峰命名法: 后续单词首字母都大写
Java修饰符
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
- 访问控制修饰符:default,public,protected,private
- 非访问控制修饰符:fina,abstract,static,synchronized 和 volatile
数据类型
Java是一种强类型语言,每个变量都必须声明其类型。
1、强弱类型语言
- 强类型语言也称为强类型定义语言。要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用。Java、.NET、C++等都是强制类型定义的。就是说,一旦一个变量被指定了某个数据类型,如果不经过转换,那么它就永远是这个数据类型了。有以下特点:
- 安全性高,运行效率相对较慢。强类型定义语言在速度上可能略逊色于弱类型定义语言,但是强类型定义语言带来的严谨性能够有效的避免许多错误。
- 弱类型语言也称为弱类型定义语言。与强类型定义相反。像vb,python等就属于弱类型语言。在python中,可以将字符串‘12’和整数3进行连接得到字符串‘123’,也可以把它看成整数123,而不需要显示转换。是不是十分的随便,我们Java就不是这样的。
2、数据类型
Java的数据类型分为两大类:基本类型(primitive type)和引用类型 (reference type)
-
基本类型,Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
- byte型:byte数据类型是8位、有符号的,以二进制补码表示的整数;最小值是-128(-27);最大值是127(27-1);默认值是0。byte类型用在大型数组中节约空间,主要代替整数,因为byte变量占用的空间只有int类型的四分之一。
- short型(短整型):short数据类型是16位、有符号的以二进制补码表示的整数;最小值是-32768(-215);最大值是32767(215-1);默认值是0。Short数据类型也可以像byte那样节省空间。一个short变量是int型变量所占空间的二分之一。
- int型(整型):int数据类型是32位、有符号的以二进制补码表示的整数,最小值是-2147483648(-231);最大值是2147483647(231-1);默认值是0。一般地整型变量默认为int类型。
- long(长整型):long数据类型是64位、有符号的以二进制补码表示的整数,最小值是-9223372036854775808(-263);最大值是9223372036854775807(263-1);默认值是0L(L表示为长整型)。这种类型主要使用在需要比较大整数的系统上。
- float(单精度浮点型):float数据类型是单精度、32位、符合IEEE 754标准的浮点数;float在储存大型浮点数组的时候可节省内存空间;默认值是0.0f;浮点数不能用来表示精确的值,如货币。
- double(双精度浮点型):double数据类型是双精度、64位、符合IEEE 754标准的浮点数;浮点数的默认类型为double类型;默认值是0.0d;double类型同样不能表示精确的值,如货币。
- boolean(布尔型):boolean数据类型表示一位的信息;只有两个取值:true和false;这种类型只作为一种标志来记录true/false情况;默认值是false。
- char(字符型):char类型是一个单一的16位Unicode字符;最小值是’\u0000’(即为0);最大值是’\uffff’(即为65,535);char数据类型可以储存任何字符。
基本数据类型 占内存 取值范围 默认值 byte 1字节 -128 ~ 127 (byte)0 short 2字节 -32768 ~ 32767 (short)0 int 4字节 -2147483648 ~ 2147483647 即-231 ~ 231-1 0 long 8字节 -9223372036854775808L ~ 9223372036854775807L 即-263 ~ 263-1 0L float 4字节 对于负数-3.402823E38 ~ -1.401298E-45
对于正数1.401298E-45 ~ 3.402823E380.0F double 8字节 对于负数-1.79769313486232E308 ~ -4.94065645841247E-324
对于正数4.94065645841247E-324 ~ 1.79769313486232E3080.0 char 2字节 unicode字符,用单引号括起来 ‘\u0000’ boolean 1字节 true, false false 对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中
了。请看下面的例子:public class PrimitiveTypeTest { public static void main(String[] args) { // byte System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE); System.out.println("包装类:java.lang.Byte"); System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE); System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE); System.out.println(); // short System.out.println("基本类型:short 二进制位数:" + Short.SIZE); System.out.println("包装类:java.lang.Short"); System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE); System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE); System.out.println(); } }
科普一下字节是什么:
- 位(bit):是计算机内部数据储存的最小单位,11001100是一个八位二进制数。
- 字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写 B 来表示,1B(byte,字节)= 8bit(位)
- 字符:是指计算机中使用的字母、数字、字和符号
-
引用类型
- 引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型,比如Employee、Pubby等。变量一旦声明后,类型就不能被改变了。
- 类、接口、对象、数组都是引用数据类型。
- 所有引用类型的默认值都是null。
- 一个引用变量可以用来引用与任何与之兼容的类型。
自己定义一些变量来看:
public class PrimitiveTest {
public static void main(String[] args) {
//整型
int i1=100;
//长整型
long i2=998877665544332211L;
//短整型
short i3=235;
//浮点型
double d1=3.5; //双精度
double d2=3;
float f1=(float)3.5; //单精度
float f2=3.5f; //单精度
//布尔类型 boolean true真/false假
boolean isPass=true;
boolean isOk=false;
boolean isBig=5>8;
if(isPass) {
System.out.println("通过了");
} else {
System.out.println("未通过");
}
//单字符
char f='女';
char m='男';
}
}
3、整型拓展
在我们计算机中存在很多进制问题,十进制,八进制,十六进制等等
十进制整数,如:99, -500, 0。
八进制整数,要求以 0 开头,如:015。
十六进制数,要求 0x 或 0X 开头,如:0x15 。
整型表示演示:
//整型
int i=10;
int i2=010;
int i3=0x10;
System.out.println(i); //10
System.out.println(i2); //8
System.out.println(i3); //16
4、浮点型拓展
上面说了浮点类型float, double的数据不适合在金融计算领域。如果有业务需求,那么需要使用BigDecimal类。
主要理由:由于字长有限,浮点数能够精确表示的数是有限的,因而也是离散的。浮点数一般都存在舍入误差,很多数字无法精确表示,其结果只能是接近,但不等于;二进制浮点数不能精确的表示0.1,0.01,0.001这样10的负次幂。并不是所有的小数都能可以精确的用二进制浮点数表示。
public static void main(String[] args) {
float f = 0.1f;
double d = 1.0 / 10;
System.out.println(f == d); //false
float d1 = 2131231231f;
float d2 = d1 + 1;
if (d1 == d2) { // true
System.out.println("d1==d2");
} else {
System.out.println("d1!=d2");
}
}
大数值:Java.math下面的两个有用的类:BigInteger和BigDecimal,这两个类可以处理任意长度的数值。BigInteger实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点运算。
import java.math.BigDecimal; // 引入BigDecimal 类
public class Main {
/**
* 程序的入口方法
*
* @param args 命令行参数,本程序中未使用
*/
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("9999999.99999");
BigDecimal d2 = new BigDecimal("9999999.99999");
BigDecimal d3 = d2.add(new BigDecimal("0.0001"));
if (d1.equals(d2)) { // true
System.out.println("d1 == d2");
} else {
System.out.println("d1 != d2");
}
if (d1.equals(d3)) { // false
System.out.println("d1 == d3");
} else {
System.out.println("d1 != d3");
}
}
}
5、字符型拓展
单引号用来表示字符常量。例如’A’是一个字符,它与"A"是不同的,"A"表示一个字符串。char 类型用来表示在Unicode编码表中的字符。Unicode编码被设计用来处理各种语言的所有文字,它占2个字节,可允许有65536个字符;
public static void main(String[] args) {
char c1 = 'a';
char c2 = '中';
System.out.println(c1);
System.out.println((int) c1); //97
System.out.println(c2);
System.out.println((int) c2); //20013
}
Unicode具有从0到65535之间的编码,他们通常用从’u0000’到’uFFFF’之间的十六进制值来表示(前缀为u表示Unicode):
char c3 = '\u0061';
System.out.println(c3); //a
Java 语言中还允许使用转义字符 ‘’ 来将其后的字符转变为其它的含义,有如下常用转义字符:
符号 | 描述 | Unicode |
---|---|---|
\b | 退格 (backspace) | \u0008 |
\n | 换行 | \u000a |
\r | 回车 | \u000d |
\t | 制表符 (tab) | \u0009 |
" | 双引号 | \u0022 |
’ | 单引号 | \u0027 |
\ | 反斜杠 | \u005c |
类型转换
由于Java是强类型语言,所以要进行有些运算的时候的,需要用到类型转换。
- 整型、实型(常量)、字符型数据可以混合运算。
- 运算中,不同类型的数据先转化为同一类型,然后进行运算。
- 转换从低级到高级(根据容量来看)。
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double
数据类型转换必须满足如下规则:
-
不能对boolean类型进行类型转换。
-
不能把对象类型转换成不相关类的对象。
-
在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
-
转换过程中可能导致溢出或损失精度,例如:
int i =128; byte b = (byte)i;
因为 byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢
出。 -
浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23; (int)-45.89f == -45;
1、自动类型转换
自动类型转换:容量小的数据类型可以自动转换为容量大的数据类型。例如: short数据类型的位数为16位,就可以自动转换位数为32的int类型,同样float数据类型的位数为32,可以自动转换为64位的double类型。
public class Main {
public static void main(String[] args) {
char c1 = 'a'; //定义一个char类型
int i1 = c1; //char自动类型转换为int
System.out.println("char自动类型转换为int后的值等于" + i1);
char c2 = 'A'; //定义一个char类型
int i2 = c2 + 1; //char 类型和 int 类型计算
System.out.println("char类型和int计算后的值等于" + i2);
}
}
【解析:c1 的值为字符 a ,查 ASCII 码表可知对应的 int 类型值为 97, A 对应值为 65,所以i2=65+1=66。】
2、强制类型转换
强制类型转换,用于显式的转换一个数值的类型。在有可能丢失信息的情况下进行的转换是通过造型来完成的,但可能造成精度降低或溢出。强制类型转换的语法格式: (type)var ,运算符“()”中的type表示将值var想要转换成的目标数据类型。 条件是转换的数据类型必须是兼容的。当将一种类型强制转换成另一种类型,而超出了目标类型的表示范围,就会被截断成为一个完全不同的值,溢出。
public static void main(String[] args) {
double x = 3.14;
int nx = (int)x; //值为3
char c = 'a';
int d = c+1;
System.out.println(d); //98
System.out.println((char)d); //b
// 溢出
int x = 300;
byte bx = (byte)x; //值为44
System.out.println(bx);
}
变量和常量
1、变量(variable)
变量是什么:我们通过变量来操纵存储空间中的数据,变量就是指代这个存储空间!空间位置是确定的,但是里面放置什么值不确定!
-
Java是一种强类型语言,每个变量都必须声明其类型。
-
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。
-
变量在使用前必须对其声明, 只有在变量声明以后,才能为其分配相应长度的存储单元,声明格式为:
type varName [=value] [{,varName[=value]}] ; //数据类型 变量名 = 值;可以使用逗号隔开来声明多个同类型变量。
注意事项:
- 每个变量都有类型,类型可以是基本类型,也可以是引用类型。
- 变量名必须是合法的标识符。
- 变量声明是一条完整的语句,因此每一个声明都必须以分号结束
int a, b, c; // 声明三个int型整数:a、 b、c
int d = 3, e = 4, f = 5; // 声明三个整数并赋予初值,不提倡一行声明多个变量
byte z = 22; // 声明并初始化 z
String s = "runoob"; // 声明并初始化字符串 s
double pi = 3.14159; // 声明了双精度浮点型变量 pi
char x = 'x'; // 声明变量 x 的值是字符 'x'。
2、变量作用域
变量根据作用域可划分为三种:
- 类变量(静态变量: static variable):独立于方法之外的变量,用 static 修饰,生命周期伴随类始终,从类加载到卸载。
- 实例变量(成员变量:member variable):独立于方法之外的变量,不过没有 static 修饰。
- 局部变量(lacal variable):类的方法中的变量。
public class Variable{
static int allClicks=0; // 类变量
String str="hello world"; // 实例变量
public void method(){
int i =0; // 局部变量
}
}
3、常量
常量(Constant):初始化(initialize)后不能再改变值!不会变动的值。所谓常量可以理解成一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变。
final 常量名=值;
final double PI=3.14;
final String S="hello";
常量名一般使用大写字符。
程序中使用常量可以提高代码的可维护性。例如,在项目开发时,我们需要指定用户的性别,此时可以定义一个常量 SEX,赋值为 “男”,在需要指定用户性别的地方直接调用此常量即可,避免了由于用户的不规范赋值导致程序出错的情况。
4、变量的命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量:首字母小写和驼峰原则 : monthSalary
- 局部变量:首字母小写和驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰原则: Man, GoodMan
- 方法名:首字母小写和驼峰原则: run(), runRun()
运算符
Java 语言支持如下运算符:
- 算术运算符: +,-,*,/,%,++,–
- 赋值运算符: =
- 关系运算符: >,<,>=,<=,==,!= ,instanceof
- 逻辑运算符: &&,||,!
- 位运算符: &,|,^,~ , >>,<<,>>> (了解即可)
- 条件运算符 : ?
- 扩展赋值运算符:+=,-=,*=,/=
1、二元运算符
两个操作数:
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 25;
int d = 25;
System.out.println("a + b = " + (a + b) );
System.out.println("a - b = " + (a - b) );
System.out.println("a * b = " + (a * b) );
System.out.println("b / a = " + (b / a) );
}
关系运算符,返回布尔值!:
运算符 | 描述 | 例子 |
---|---|---|
== | 检查如果两个操作数的值是否相等,如果相等则条件为真。 | ( A == B ) 为假。 |
!= | 检查如果两个操作数的值是否不相等,如果值不相等则条件为真。 | ( A != B ) 为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是那么条件为真。 | ( A > B ) 为假。 |
< | 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。 | ( A < B ) 为真。 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 | ( A >= B ) 为假。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 | ( A <= B ) 为真。 |
2、取模运算
就是取余:5%3 余 2
要点:
- 负数%负数=负数;
- 负数%正数=负数;
- 正数%负数=正数;
public static void main(String[] args) {
System.out.println(9 % 4); //1
System.out.println(-9 % -4); //-1
System.out.println(-10 % 4); //-2
System.out.println(9 % -4); //1
}
3、一元运算符
自增(++)自减(–)运算符是一种特殊的算术运算符,在算术运算符中需要两个操作数来进行运算,而自增自减运算符是一个操作数,分为前缀和后缀两种。
public static void main(String[] args) {
int a = 3;
int b = a++; //执行完后,b=3。先给b赋值,再自增。
int c = ++a; //执行完后,c=5。先自增,再给c赋值。
// 注意:java中的乘幂处理
int a = 3^2; //java中不能这么处理, ^是异或符号。
double b = Math.pow(3, 2);
}
4、逻辑运算符
逻辑与:&&和&,逻辑或:||和|,逻辑非:!。下面A为真、B为假:
操作符 | 描述 | 例子 |
---|---|---|
&& | 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 | ( A && B ) 为假。 |
|| | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 | (A||B)为真 |
! | 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 | !( A && B ) 为真。 |
public static void main(String[] args) {
boolean a = true;
boolean b = false;
System.out.println("a && b = " + (a&&b));
System.out.println("a || b = " + (a||b) );
System.out.println("!(a && b) = " + !(a && b));
}
5、位运算符
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。位运算符作用在所有的位上,并且按位运算。
A = 0011 1100
B = 0000 1101
-----------------
A&b = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A= 1100 0011
操作符 | 描述 | 例子 |
---|---|---|
& | 如果相对应位都是1,则结果为1,否则为0 | ( A & B ),得到12,即0000 1100 |
| | 如果相对应位都是0,则结果为0,否则为1 | 如果相对应位都是0,则结果为0,否则为1 |
^ | 如果相对应位值相同,则结果为0,否则为1 | ( A ^ B ) 得到49,即 0011 0001 |
~ | 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 | ( ~A ) 得到-61,即1100 0011 |
<< | 按位左移运算符。左操作数按位左移右操作数指定的位数。左移一位相当于乘2 | A << 2 得到240,即 1111 0000 |
>> | 按位右移运算符。左操作数按位右移右操作数指定的位数。右移一位相当于除2取商 | A >> 2 得到15 即 1111 |
>>> | 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 | A >>> 2 得到15 即0000 1111 |
6、赋值运算符
运算符 | 用法举例 | 等效的表达式 |
---|---|---|
+= | a += b | a = a + b |
-= | a -= b | a = a - b |
*= | a *= b | a = a * b |
/= | a /= b | a = a / b |
%= | a %= b | a = a % b |
public static void main(String[] args) {
int a = 10;
int b = 20;
a += b; // a = a + b
System.out.println(a+":"+b);
}
7、三目条件运算符
三目条件运算符,语法格式:x ? y : z
其中x为boolean类型表达式,先计算x的值,若为true,则整个三目运算的结果为表达式y的值,否则整个运算结果为表达式z的值。
public static void main(String[] args) {
int score = 80;
String type = score < 60 ? "不及格" : "及格";
System.out.println("type= " + type);
}
8、instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型);instanceof运算符使用格式如下:
( Object reference variable ) instanceof (class/interface type)
String name = 'James';
boolean result = name instanceof String; // 由于name是String类型,所以返回真
9、运算符优先级
下表中具有最高优先级的运算符在的表的最上面,最低优先级的在表的底部。
类别 | 操作符 | 关联性 |
---|---|---|
后缀 | () [] . (点操作符) | 左到右 |
一元 | ++ – ! ~ | 从右到左 |
乘性 | * / % | 左到右 |
加性 | + - | 左到右 |
移位 | >> >>> << | 左到右 |
关系 | > >= < <= | 左到右 |
相等 | == != | 左到右 |
按位与 | & | 左到右 |
按位异或 | ^ | 左到右 |
按位或 | | | 左到右 |
逻辑与 | && | 左到右 |
逻辑或 | || | 左到右 |
条件 | ?: | 从右到左 |
赋值 | = += -= *= /= %= >>= <<= &= ^=|= | 从右到左 |
大家不需要去刻意的记住,表达式里面优先使用小括号来组织。
包机制
1、为什么用包
存在这样一个问题:当定义了多个类的时候,可能会发生类名的重复问题。
解决方式:在java中采用包机制处理开发者定义的类名冲突问题。
就好比我们平时的用电脑,一个文件夹下不能存在同名的文件,我们要是有这样的需求,但是又不想换名字,我们就可以考虑使用新建一个文件夹来存放!在我们的Java中也是这样的。
2、包的作用
- 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类
的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可
以避免名字冲突。 - 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。包语句的语法格式为:package pkg1[.1 pkg2[.pkg3…]];
以下是一些 Java 中的包:
- java.lang-打包基础的类
- java.io-包含输入输出功能的函数
开发者可以自己把一组类和接口等打包,并定义自己的包。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的。由于包创建了新的命名空间(namespace),所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。
3、import 关键字
为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 “import” 语句可完成此功能。在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
import package1[.package2…].(1 classname|*);
// 用 import 关键字引入,使用通配符 "*" , 导入io包下的所有类!【不建议这样使用,因为会全局扫描,影响速度!】
import java.io.*;
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。要是要用到其他包下的类,就必须要先导包!
用户交互Scanner
1、Scanner对象
上面所学的基本语法中并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,可以获取用户的输入。java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入。
下面是创建 Scanner 对象的基本语法:
Scanner s = new Scanner(System.in);
通过 Scanner 类的 next() 与 nextLine() 方法可以获取输入的字符串,在读取前我们一般需要 使用 hasNext() 与 hasNextLine() 判断是否还有输入的数据。
2、next & nextLine
我们使用next方式接收一下输入的数据!
public static void main(String[] args) {
// 创建一个扫描器对象,用于接收键盘数据
Scanner scanner = new Scanner(System.in);
// next方式接收字符串
System.out.println("Next方式接收:");
// 判断用户还有没有输入字符
if (scanner.hasNext()) {
String str = scanner.next();
System.out.println("输入内容:" + str);
}
// 凡是属于IO流的类如果不关闭会一直占用资源,要养成好习惯用完就关掉...
scanner.close();
}
测试数据:Hello World! 结果:只输出了Hello。 接下来我们使用另一个方法来接收数据:nextLine() :
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// nextLine方式接收字符串
System.out.println("nextLine方式接收:");
// 判断是否还有输入
if (scan.hasNextLine()) {
String str2 = scan.nextLine();
System.out.println("输入内容:" + str2);
}
scan.close();
}
测试数据:Hello World!结果:输出了Hello World!两者区别:
- next():
- 一定要读取到有效字符后才可以结束输入。
- 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
- next() 不能得到带有空格的字符串。
- nextLine():
- 以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
- 可以获得空白。
3、其他方法
如果要输入 int 或 float 类型的数据,在 Scanner 类中也有支持,但是在输入之前最好先使用hasNextXxx() 方法进行验证,再使用 nextXxx() 来读取:
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int i = 0;
float f = 0.0f;
System.out.print("输入整数:");
if (scan.hasNextInt()) { // 判断输入的是否是整数
i = scan.nextInt(); // 接收整数
System.out.println("整数数据:" + i);
} else {
// 输入错误的信息
System.out.println("输入的不是整数!");
}
System.out.print("输入小数:");
if (scan.hasNextFloat()) { // 判断输入的是否是小数
f = scan.nextFloat(); // 接收小数
System.out.println("小数数据:" + f);
} else { // 输入错误的信息
System.out.println("输入的不是小数!");
}
scan.close();
}
以下实例我们可以输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果:
public static void main(String[] args) {
//扫描器接收键盘数据
Scanner scan = new Scanner(System.in);
double sum = 0; //和
int m = 0; //输入了多少个数字
// 通过循环判断是否还有输入,并在里面对每一次进行求和和统计
while (scan.hasNextDouble()) {
double x = scan.nextDouble();
m = m + 1;
sum = sum + x;
}
System.out.println(m + "个数的和为" + sum);
System.out.println(m + "个数的平均值是" + (sum / m));
scan.close();
}
以上这些语句的具体作用是为了接下来的流程控制语句,Java中的流程控制语句可以这样分类:顺序结构,选择结构,循环结构!
顺序结构
JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行。顺序结构是最简单的算法结构。顺序结构在程序流程图中的体现就是用流程线将程序框自上而地连接起来,按顺序执行算法步骤。
public static void main(String[] args) {
System.out.println("Hello1");
System.out.println("Hello2");
System.out.println("Hello3");
System.out.println("Hello4");
System.out.println("Hello5");
}
//按照自上而下的顺序执行!依次输出。
选择结构
1、if单选择结构
我们很多时候需要去判断一个东西是否可行,然后我们才去执行,这样一个过程在程序中用if语句来表示:
if (布尔表达式) {
//如果布尔表达式为true将执行的语句
}
意义:if语句对条件表达式进行一次测试,若测试为真,则执行下面的语句,否则跳过该语句。
比如我们来接收一个用户输入,判断输入的是否为Hello字符串:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//接收用户输入
System.out.print("请输入内容:");
String s = scanner.nextLine();
if (s.equals("Hello")){ // equals方法是用来进行字符串的比较的,和==是有区别的:
System.out.println("输入的是:"+s);
}
System.out.println("End");
scanner.close();
}
==
运算符:- 它比较的是两个对象的引用(内存地址),而不是它们的内容。
- 对于基本数据类型(如int, char等),它比较的是实际的值。
- 在比较两个字符串时,如果使用
==
,只有当两个字符串是同一个对象(即它们存储在内存中的位置相同)时才会返回true
。
equals
方法:- 它是
Object
类中的一个方法,所有类都继承自Object
类,因此所有的对象都有这个方法。 - 默认情况下,
equals
方法的行为类似于==
,它也是比较对象的引用。 - 但是,像
String
类这样的类重写了equals
方法来比较对象的实际内容。所以对于字符串来说,equals
比较的是两个字符串的内容是否相同,而不关心它们是不是同一个对象实例。
- 它是
例如:
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println(str1 == str2); // true,因为字符串字面量池中会重用相同的 "hello" 实例
System.out.println(str1 == str3); // false,str3 是新创建的对象
System.out.println(str1.equals(str2)); // true,内容相同
System.out.println(str1.equals(str3)); // true,即使不是同一对象,但内容相同
因此,在需要比较两个字符串的内容时,应该使用 equals
方法,而不要使用 ==
运算符。如果你确实想要比较两个字符串对象是否是同一个实例,则可以使用 ==
。
2、if双选择结构
那现在有个需求,下班去买菜,有番茄,买番茄做番茄炒蛋,番茄卖完了,买青瓜做青瓜炒肉。这样的需求用一个if就搞不定了,我们需要有两个判断,需要一个双选择结构,所以就有了if-else结构。
if (布尔表达式) {
//如果布尔表达式的值为true
} else {
//如果布尔表达式的值为false
}
示例:考试分数大于60就是及格,小于60分就不及格:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入成绩:");
int score = scanner.nextInt();
if (score > 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
scanner.close();
}
3、if多选择结构
我们发现上面的示例不符合实际情况,真实的情况还可能存在ABCD,存在区间多级判断。比如90-100就是A,80-90 就是B等等,在生活中我们很多时候的选择也不仅仅只有两个,所以我们需要一个多选择结构来处理这类问题!
if(布尔表达式 1){
//如果布尔表达式 1的值为true执行代码
}else if(布尔表达式 2){
//如果布尔表达式 2的值为true执行代码
}else if(布尔表达式 3){
//如果布尔表达式 3的值为true执行代码
}else {
//如果以上布尔表达式都不为true执行代码
}
if 语句后面可以跟 else if…else 语句,这种语句可以检测到多种可能的情况。使用 if,else if,else 语句的时候,需要注意下面几点:
- if 语句至多有 1 个 else 语句,else 语句在所有的 else if 语句之后。
- if 语句可以有若干个 else if 语句,它们必须在 else 语句之前。
- 一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行。
改造一下上面的成绩案例,学校根据分数区间分为ABCD四个等级:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入成绩:");
int score = scanner.nextInt();
if (score == 100) {
System.out.println("恭喜满分");
} else if (score < 100 && score >= 90) {
System.out.println("A级");
} else if (score < 90 && score >= 80) {
System.out.println("B级");
} else if (score < 80 && score >= 70) {
System.out.println("C级");
} else if (score < 70 && score >= 60) {
System.out.println("D级");
} else if (score < 60 && score >= 0) {
System.out.println("不及格!");
} else {
System.out.println("成绩输入不合法!");
}
scanner.close();
}
4、嵌套的if结构
使用嵌套的 if…else 语句是合法的。也就是说你可以在另一个 if 或者 else if 语句中使用 if 或者 else if 语句。你可以像 if 语句一样嵌套 else if…else。所有的流程控制语句都可以互相嵌套,互不影响!
if(布尔表达式 1){
// 如果布尔表达式 1的值为true执行代码
if(布尔表达式 2){
// 如果布尔表达式 2的值为true执行代码
}
}
5、switch多选择结构
多选择结构还有一个实现方式就是switch case语句。switch case语句判断一个变量与一组值中某个值是否相等,每个值称为一个分支。
switch(变量值){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //可选
//语句
}
switch case 语句有如下规则:
- switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
- switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
- case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
- 当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
- 当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现break 语句。
- switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
switch case 执行时,一定会先进行匹配,匹配成功返回当前 case 的值,再根据是否有 break,判断是否继续输出,或是跳出判断。
public static void main(String args[]) {
char grade = 'C';
switch (grade) {
case 'A':
System.out.println("优秀");
break;
case 'B':
case 'C':
System.out.println("良好");
break;
case 'D':
System.out.println("及格");
break;
case 'F':
System.out.println("你需要再努力努力");
break;
default:
System.out.println("未知等级");
}
System.out.println("你的等级是 " + grade);
}
如果 case 语句块中没有 break 语句时,匹配成功后,从当前 case 开始,后续所有 case 的值都会输出。如果后续的 case 语句块有 break 语句则会跳出判断。【case穿透】
public static void main(String args[]) {
int i = 1;
switch (i) {
case 0:
System.out.println("0");
case 1:
System.out.println("1");
case 2:
System.out.println("2");
case 3:
System.out.println("3");
break;
default:
System.out.println("default");
}
// 输出:1,2,3。
}
循环结构
上面选择结构中,我们始终无法让程序一直跑着,我们每次运行就停止了。顺序结构的程序语句只能被执行一次。如果您想要同样的操作执行多次,,就需要使用循环结构。Java中有三种主要的循环结构(在Java5中引入了一种主要用于数组的增强型for循环):
- while 循环
- do…while 循环
- for 循环
1、while 循环
while是最基本的循环,它的结构为:
while( 布尔表达式 ) {
//循环内容
}
只要布尔表达式为 true,循环就会一直执行下去。我们大多数情况是会让循环停止下来的,我们需要一个让表达式失效的方式来结束循环。方式有:循环内部控制,外部设立标志位等等:
public static void main(String[] args) {
int i = 0;
//i小于100就会一直循环
while (i < 100) {
i++;
System.out.println(i);
}
}
循环条件一直为true就会造成无限循环【死循环】,我们正常的业务编程中应该尽量避免死循环。会影响程序性能或者造成程序卡死奔溃
2、do…while 循环
对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。
do {
//代码语句
}while(布尔表达式);
注意:布尔表达式在循环体的后面,所以语句块在检测布尔表达式之前已经执行了。 如果布尔表达式的值为 true,则语句块一直执行,直到布尔表达式的值为 false。
public static void main(String[] args) {
int i = 0;
int sum = 0;
do {
sum = sum + i;
i++;
} while (i <= 100);
System.out.println("Sum= " + sum);
}
While和do-While的区别:while先判断后执行。dowhile是先执行后判断!Do…while总是保证循环体会被至少执行一次!这是他们的主要差别。
public static void main(String[] args) {
int a = 0;
while (a < 0) {
System.out.println(a); // 不执行
a++;
}
System.out.println("-----");
do {
System.out.println(a); // 执行一次
a++;
} while (a < 0);
}
3、For循环
虽然所有循环结构都可以用 while 或者 do…while表示,但 Java 提供了另一种语句 —— for 循环,使一些循环结构变得更加简单。for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。for循环执行的次数是在执行前就确定的。语法格式如下:
for(初始化; 布尔表达式; 更新) {
//代码语句
}
关于 for 循环有以下几点说明:
- 最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
- 然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
- 执行一次循环后,更新循环控制变量(迭代因子控制循环变量的增减)。
- 再次检测布尔表达式。循环执行上面的过程。
public static void main(String[] args) {
int a = 1; //初始化
while (a <= 100) { //条件判断
System.out.println(a); //循环体
a += 2; //迭代
}
System.out.println("while循环结束!");
for (int i = 1; i <= 100; i++) { //初始化//条件判断 //迭代
System.out.println(i); //循环体
}
System.out.println("for循环结束!");
}
for循环在知道循环次数的情况下,简化了代码,提高了可读性。我们平时用到的最多的也是for循环!
4、增强for循环
Java5 引入了一种主要用于数组或集合的增强型 for 循环。Java 增强 for 循环语法格式如下(先简单做个了解):
for(声明语句 : 表达式){
//代码句子
}
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。(数组在后面一篇讲解)
public static void main(String[] args) {
int[] numbers = {10, 20, 30, 40, 50};
for (int x : numbers) {
System.out.print(x);
System.out.print(",");
}
System.out.print("\n");
String[] names = {"James", "Larry", "Tom", "Lacy"};
for (String name : names) {
System.out.print(name);
System.out.print(",");
}
}
break & continue
1、break 关键字
break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块。break 跳出最里层的循环,并且继续执行该循环下面的语句。
public static void main(String[] args) {
int i = 0;
while (i < 100) {
i++;
System.out.println(i);
if (i == 30) { // 等于30时结束循环
break;
}
}
}
2、continue 关键字
continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。在 for 循环中,continue 语句使程序立即跳转到更新语句。在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。
public static void main(String[] args) {
int i = 0;
while (i < 100) {
i++;
if (i % 10 == 0) {
// 条件达成,换行
System.out.println();
// 跳转到下一次循环,不执行下面的语句:打印i
continue;
}
System.out.print(i);
}
}
3、两者区别
- break在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。(break语句也在switch语句中使用)
- continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
方法
1、什么是方法?
在前面几个章节中经常使用到 System.out.println(),那么它是什么呢?
- println() 是一个方法。
- System 是系统类。
- out 是标准输出对象。
这句话的用法是调用系统类 System 中的标准输出对象 out 中的方法 println()。Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。方法的优点:
- 使程序变得更简短而清晰。
- 有利于程序维护。
- 可以提高程序开发的效率。
- 提高了代码的重用性。
2、方法的定义
Java的方法类似于其它语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:
修饰符 返回值类型 方法名(参数类型 参数名){
方法体
return 返回值;
}
方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
-
修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
-
返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
-
方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
-
参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
-
形式参数:在方法被调用时用于接收外界输入的数据。
-
实参:调用方法时实际传给方法的数据。
-
-
方法体:方法体包含具体的语句,定义该方法的功能。
比如我们写一个比大小的方法:
/**
* 返回两个整型变量数据的较大值
*/
public static int max(int num1, int num2) {
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
3、方法调用
Java 支持两种调用方法的方式,根据方法是否返回值来选择。当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。当方法返回一个值的时候,方法调用通常被当做一个值。例如:
int max1 = max(30, 40);
Java语言中使用下述形式调用方法:对象名.方法名(实参列表)。如果方法返回值是void,方法调用一定是一条语句。例如,方法println返回void。下面的调用是个语句:
System.out.println("Hello World!");
样例
:
public static void main(String[] args) {
int i = 5;
int j = 2;
int k = max(i, j);
System.out.println(i + " 和 " + j + " 比较,最大值是:" + k);
}
/**
* 返回两个整数变量较大的值
*/
public static int max(int num1, int num2) {
int result;
if (num1 > num2) {
result = num1;
} else {
result = num2;
}
return result;
}
4、方法的重载
上面使用的max方法仅仅适用于int型数据。但如果你想得到两个浮点类型数据的最大值呢?解决方法是创建另一个有相同名字但参数不同的方法,如下面代码所示:
public static double max(double num1, double num2) {
if (num1 > num2) // 可以省略大括号
return num1;
else
return num2;
}
public static int max(int num1, int num2) {
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
如果你调用max方法时传递的是int型参数,则 int型参数的max方法就会被调用;如果传递的是double型参数,则double类型的max方法体会被调用,这叫做方法重载;就是说一个类的两个方法拥有相同的名字,但是有不同的参数列表。
- Java编译器根据方法签名判断哪个方法应该被调用。
- 方法重载可以让程序更清晰易读。执行密切相关任务的方法应该使用相同的名字。
- 重载的方法必须拥有不同的参数列表。你不能仅仅依据修饰符或者返回类型的不同来重载方法。
5、拓展命令行传参
有时候你希望运行一个程序时候再传递给它消息。这要靠传递命令行参数给main()函数实现。命令行参数是在执行程序时候紧跟在程序名字后面的信息。
public class CommandLine {
public static void main(String args[]){
for(int i=0; i<args.length; i++){
System.out.println("args[" + i + "]: " + args[i]);
}
}
}
javac CommandLine.java
java CommandLine this is a command line 200 -100
args[0]: this
args[1]: is
args[2]: a
args[3]: command
args[4]: line
args[5]: 200
args[6]: -100
如果报错:【错误: 找不到或无法加载主类,解决方法】,在项目输出的项目目录下执行java命令,写完整路径即可。
java com.study.Test03 Hello World
6、可变参数
JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。方法的可变参数的声明如下所示:
typeName... parameterName
在方法声明中,在指定参数类型后加一个省略号(…) 。一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public static void main(String args[]) { // 调用可变参数的方法
printMax(34, 3, 3, 2, 56.5);
printMax(new double[]{1, 2, 3}); // 数组,在下面会讲解
}
public static void printMax(double... numbers) {
if (numbers.length == 0) {
System.out.println("No argument passed");
return;
}
double result = numbers[0];
for (int i = 1; i < numbers.length; i++) {
if (numbers[i] > result) {
result = numbers[i];
}
}
System.out.println("The max value is " + result);
}
7、递归
递归就是:A方法调用A方法!就是自己调用自己,因此在设计递归算法时,一定要指明什么时候停止调用。否则,就是个死循环!
递归算法重点:
- 递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。
- 利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
递归结构包括两个部分:
- 递归头。什么时候不调用自身方法。如果没有头,将陷入死循环。
- 递归体。什么时候需要调用自身方法。
利用递归代码计算5的乘阶
:
// 5*4*3*2*1
public static void main(String[] args) {
System.out.println(f(5));
}
public static int f(int n) {
if (1 == n)
return 1;
else
return n*f(n-1); // 调用自己
}
此题中,按照递归的三个条件来分析:
- 边界条件:阶乘,乘到最后一个数,即1的时候,返回1,程序执行到底;
- 递归前进段:当前的参数不等于1的时候,继续调用自身;
- 递归返回段:从最大的数开始乘,如果当前参数是5,那么就是5 * 4,即5 * (5-1),即n * (n-1)
递归其实是方便了程序员难为了机器,递归可以通过数学公式很方便的转换为程序。
其优点就是易理解,容易编程。但递归是用栈机制实现的,每深入一层,都要占去一块栈数据区域,对嵌套层数深的一些算法,递归会力不从心,空间上会以内存崩溃而告终,而且递归也带来了大量的函数调用,这也有许多额外的时间开销。所以在深度大时,它的时空性就不好了。(会占用大量的内存空间)
而迭代虽然效率高,运行时间只因循环次数增加而增加,没什么额外开销,空间上也没有什么增加,但缺点就是不容易理解,编写复杂问题时困难。
能不用递归就不用递归,递归都可以用迭代来代替。
数组概述
关于数组我们可以把它看作是一个类型的所有数据的一个集合,并用一个数组下标来区分或指定每一个数,例如有10个数字,这时候如果我们要用变量来存放它们的话,就要分别使用10个变量,而且要记住这10个变量的名字,这会十分的麻烦,这时候我们就可以用一个数组变量来存放他们。 使用数组会让程序变的简单,而且避免了定义多个变量的麻烦。
数组的定义:
- 数组是相同类型数据的有序集合。
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
数组的四个基本特点:
- 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
- 其元素必须是相同类型,不允许出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组声明创建
1、声明数组
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[] arrayVar; // 首选的方法
或者
dataType arrayVar[]; // 效果相同,但不是首选方法
// 样例
double[] myList; // 首选的方法
或
double myList[]; // 效果相同,但不是首选方法
2、创建数组
Java语言使用new操作符来创建数组,语法如下:
arrayVar = new dataType[arraySize];
上面的语法语句做了两件事:
- 使用 dataType[arraySize] 创建了一个数组。
- 把新创建的数组的引用赋值给变量 arrayVar。
数组变量的声明,和创建数组可以用一条语句完成,如下所示:
dataType[] arrayVar = new dataType[arraySize];
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。
获取数组长度:arrays.length
;样例:
public static void main(String[] args) {
//1.声明一个数组
int[] myList = null;
//2.创建一个数组
myList = new int[10];
//3.像数组中存值
myList[0] = 1;
myList[1] = 2;
myList[2] = 3;
myList[3] = 4;
myList[4] = 5;
myList[5] = 6;
myList[6] = 7;
myList[7] = 8;
myList[8] = 9;
myList[9] = 10;
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < myList.length; i++) {
total += myList[i];
}
System.out.println("总和为: " + total);
}
Java内存分析:
- 声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有关。因此,声明数组时不能指定其长度(数组中元素的个数),例如: int a[5]是非法的
- 声明一个数组的时候并没有数组被真正的创建。
- 构造一个数组,必须指定长度。
//1.声明一个数组
int[] myList = null;
//2.创建一个数组
myList = new int[10];
//3.像数组中存值
myList[0] = 1;
myList[1] = 2;
myList[2] = 3;
myList[3] = 4;
myList[4] = 5;
myList[5] = 6;
myList[6] = 7;
myList[7] = 8;
myList[8] = 9;
myList[9] = 10;
3、三种初始化
静态初始化,除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。
public static void main(String[] args) {
int[] a = {1,2,3};
Man[] mans = {new Man(1,1), new Man(2,2)};
}
public static class Man {
int a;
int b;
public Man(int a, int b) {
this.a = a;
this.b = b;
}
}
动态初始化,数组定义、为数组元素分配空间、赋值的操作、分开进行。
int[] a = new int[2];
a[0]=1;
a[1]=2;
数组的默认初始化,数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
public static void main(String[] args) {
int[] a=new int[2];
boolean[] b = new boolean[2];
String[] s = new String[2];
System.out.println(a[0]+":"+a[1]); //0,0
System.out.println(b[0]+":"+b[1]); //false,false
System.out.println(s[0]+":"+s[1]); //null, null
}
4、数组边界
下标的合法区间:[0, length-1],如果越界就会报错;
public static void main(String[] args) {
int[] a=new int[2];
System.out.println(a[2]);
}
/**
* 报错,越界:ArrayIndexOutOfBoundsException : 数组下标越界异常!
*Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2 at com.study.Test.main(Demo.java:6)
**/
数组使用
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者 For-Each 循环。
public static void main(String[] args) {
double[] myList = {1, 2.88, 3, 5};
// 打印所有数组元素
for (int i = 0; i < myList.length; i++) {
System.out.println(myList[i] + " ");
}
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < myList.length; i++) {
total += myList[i];
}
System.out.println("Total is " + total);
// 查找最大元素
double max = myList[0];
for (int i = 1; i < myList.length; i++) {
if (myList[i] > max) {
max = myList[i];
}
}
System.out.println("Max is " + max);
}
1、For-Each 循环
上面说了JDK 1.5 引进了一种新的循环类型,被称为 For-Each 循环或者加强型循环,它能在不使用下标的情况下遍历数组。
for(type element: array){
System.out.println(element);
}
public static void main(String[] args) {
double[] myList = {1, 2.88, 3, 5};
// 打印所有数组元素
for (double element: myList) {
System.out.println(element);
}
}
2、数组作方法入参
数组可以作为参数传递给方法。例如,下面的例子就是一个打印 int 数组中元素的方法 :
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
3、数组作返回值
public static int[] reverse(int[] list) {
int[] result = new int[list.length];
for (int i = 0, j = result.length - 1; i < list.length; i++, j--) {
result[j] = list[i];
}
return result; // result 数组作为函数的返回值。
}
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。多维数组的动态初始化(以二维数组为例);直接为每一维分配空间,格式如下:
type[][] typeName = new type[typeLength1][typeLength2];
type 可以为基本数据类型和复合数据类型,arraylenght1 和 arraylenght2 必须为正整数,arraylenght1 为行数,arraylenght2 为列数。比如定义一个二维数组:
int a[][] = new int[2][5]; // 解析:二维数组 a 可以看成一个两行五列的数组。
多维数组的引用(以二维数组为例);对二维数组中的每个元素,引用方式为 arrayName[index1] [index2],例如:num[1] [0]
;其实二维甚至多维数组十分好理解,我们把两个或者多个值当做定位就好。获取数组长度:a.length
获取的二维数组第一维数组的长度,a[0].length
才是获取第二维第一个数组长度。
Arrays 类
数组的工具类java.util.Arrays;由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从
而可以对数据对象进行一些基本的操作。
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而"不用"使用对象来调用(注意:是"不用" 而不是 “不能”)。java.util.Arrays 类能方便地操作数组. 使用之前需要导包!具有以下常用功能:
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
具体看下表:
序号 | 方法和说明 |
---|---|
1 | public static int binarySearch(Object[] a, Object key) 用二分查找算法在给定数组中搜索给定值的对象(Byte, Int, double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回(-(插入点) - 1)。 |
2 | public static boolean equals(long[] a, long[] a2) 如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
3 | public static void fill(int[] a, int val) 将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
4 | public static void sort(Object[] a) 对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
1、打印数组
public static void main(String[] args) {
int[] a = {1,2};
System.out.println(a); //[I@1b6d3586
System.out.println(Arrays.toString(a)); //[1, 2]
}
2、数组排序
对指定的 int 型数组按数字升序进行排序:
public static void main(String[] args) {
int[] a = {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println(Arrays.toString(a));
}
3、二分法查找
在数组中查找指定元素并返回其下标;注意:使用二分搜索法来搜索指定的数组,以获得指定的值。必须在进行此调用之前对数组进行排序(通过sort方法等)。如果没有对数组进行排序,则结果是不确定的。如果数组包含多个带有指定值的元素,则无法保证找到的是哪一个。
public static void main(String[] args) {
int[] a = {1,2,323,23,543,12,59};
Arrays.sort(a); //使用二分法查找,必须先对数组进行排序
System.out.println("该元素的索引:"+Arrays.binarySearch(a, 12));
}
4、元素填充
public static void main(String[] args) {
int[] a = {1,2,323,23,543,12,59};
Arrays.sort(a); //使用二分法查找,必须先对数组进行排序
Arrays.fill(a, 2, 4, 100); //将2到4索引的元素替换为100
System.out.println(Arrays.toString(a));
}
5、数组转换为List集合
int[] a = {3,5,1,9,7};
List<int[]> list = Arrays.asList(a); // List集合后续会详细讲
常见排序算法
具体查看我以前发的文章:常用十大排序算法
须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回(-(插入点) - 1)。 |
| 2 | public static boolean equals(long[] a, long[] a2)
如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
| 3 | public static void fill(int[] a, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
| 4 | public static void sort(Object[] a)
对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
1、打印数组
public static void main(String[] args) {
int[] a = {1,2};
System.out.println(a); //[I@1b6d3586
System.out.println(Arrays.toString(a)); //[1, 2]
}
2、数组排序
对指定的 int 型数组按数字升序进行排序:
public static void main(String[] args) {
int[] a = {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println(Arrays.toString(a));
}
3、二分法查找
在数组中查找指定元素并返回其下标;注意:使用二分搜索法来搜索指定的数组,以获得指定的值。必须在进行此调用之前对数组进行排序(通过sort方法等)。如果没有对数组进行排序,则结果是不确定的。如果数组包含多个带有指定值的元素,则无法保证找到的是哪一个。
public static void main(String[] args) {
int[] a = {1,2,323,23,543,12,59};
Arrays.sort(a); //使用二分法查找,必须先对数组进行排序
System.out.println("该元素的索引:"+Arrays.binarySearch(a, 12));
}
4、元素填充
public static void main(String[] args) {
int[] a = {1,2,323,23,543,12,59};
Arrays.sort(a); //使用二分法查找,必须先对数组进行排序
Arrays.fill(a, 2, 4, 100); //将2到4索引的元素替换为100
System.out.println(Arrays.toString(a));
}
5、数组转换为List集合
int[] a = {3,5,1,9,7};
List<int[]> list = Arrays.asList(a); // List集合后续会详细讲
常见排序算法
具体查看我以前发的文章:常用十大排序算法