Java基础语法
一、关键字和保留字
1.1概念
Java关键字(Key Word): 对Java的编译器有特殊的意义,他们用来表示一种数据类型或者表示程序的结构.
保留字(Reserve Word):即它们在Java现有版本中没有特殊含义,以后版本可能会作为有特殊含义的词,或者该词虽然在Java中没有特殊含义,以后版本也不打算使用,但在其它语言中有特殊含义,不宜在Java中定义为变量名称等,因为容易混淆。
注意:关键字和保留字均不能用作变量名、方法名、类名、包名和参数。
官方地址: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html 可以参考
1.2具体的关键字(51个)
1.访问修饰符(3个)
public、protected、private
作用:用来修饰类(接口、抽象类)、方法、属性、构造方法、常量、主函数
2.类、接口、抽象类(9个)
class、interface、abstract——定义
extends——继承类、implements——实现接口
new——新建一个对象、super——调用父类方法、this——指代当前对象
instanceof——通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
3.数据类型(13个)
void——没有返回值
byte、short、int、long——整型数据
float、double——浮点型数据
char——字符型数据
boolean——判断型数据
enum——枚举
null、true、false——值类型
4.线程(2个)
synchronized——线程同步(修饰方法、代码块,方法、代码块的同步)
volatile——线程同步(修饰属性,属性的同步)
5.异常(5个)
throw——抛出方法代码中的异常给方法自身。使用位置:方法中间
throws——抛出方法中的异常给调用者。使用位置:方法外部
try——捕获{}中代码是否有发生异常
catch——处理try捕获的异常
finally——不管有没有异常发生都会执行的代码块
6.返回(1个)
return
7.循环、条件(10个)
if、else、switch、case、break、default、continue、while、do、for
8.包(2个)
package、import
9.瞬时的(1个)
transient
10.断言(1个)
assert
11.调用底层代码(C\C++)(1个)
native
12、不可变的——final(1个)
修饰属性、常量、局部变量、参数——作用:数据是不可改变的
修饰类——作用:修饰的类不能被继承
修饰普通方法——作用:修饰的方法不能被重写
13.静态的——static(1个)
修饰属性、常量
修饰内部类
修饰普通方法
作用:所有使用static关键字修饰的内容会最先执行。static修饰的内容在内存中只有唯一的一份(存储在静态内存空间中)。
14.格式规范——strictfp(1个)
修饰类、接口或方法。
修饰方法时,该方法中所有的float和double表达式都严格遵守FP-strict的限制,符合IEEE-754规范。
修饰类或接口时,该类中的所有代码,包括嵌套类型中的初始设定值和代码,都将严格地进行计算。
严格约束意味着所有表达式的结果都必须是 IEEE 754 算法对操作数预期的结果,以单精度和双精度格式表示。
1.3具体的保留字
goto、const
二、标识符
2.1、什么是标识符(Identifier)
- Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符
- 技巧:凡是自己可以起名字的地方都叫标识符。
2.2、定义合法标识符规则【重要】
- 由 26 个英文字母大小写,0-9,_或$ 组成
- 数字不可以开头。
- 标识符不能包含空格。
- 不可以使用关键字和保留字,但能包含关键字和保留字。
- Java 中严格区分大小写,长度无限制。
2.3、Java 中的名称命名规范
1、Java 中的名称命名规范:
- 包名:多单词组成时所有字母都小写:xxxyyyzzz
- 类名、接口名:多单词组成时,**所有单词的首字母大写:**XxxYyyZzz
- 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
- 常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
2、注意点
- 注意 1:在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
- 注意 2:java 采用 unicode 字符集,因此标识符也可以使用汉字声明,但是不建议使用。
- 更多细节详见《代码整洁之道》
三、变量
1、变量的概念:
- 内存中的一个存储区域;
- 该区域的数据可以在同一类型范围内不断变化;
- 变量是程序中最基本的存储单元。包含变量类型、变量名和存储的值。
2、变量的作用:
- 用于在内存中保存数据。
3、使用变量注意:
- Java 中每个变量必须先声明,后使用;
- 使用变量名来访问这块区域的数据;
- 变量的作用域:其定义所在的一对{ }内;
- 变量只有在其作用域内才有效;
- 同一个作用域内,不能定义重名的变量;
4、声明变量
- 语法:<数据类型> <变量名称>
- 例如:int var;
5、变量的赋值
- 语法:<变量名称> = <值>
- 例如:var = 10;
6、声明和赋值变量
- 语法:<数据类型><变量名>= <初始化值>
- 例如:int var = 10
7、补充:变量的分类-按声明的位置的不同
-
在方法体外,类体内声明的变量称为成员变量。
-
在方法体内部声明的变量称为局部变量。
8、注意:二者在初始化值方面的异同:
- 同:都有生命周期
- 异:局部变量除形参外,需显式初始化。
四、数据类型
整数类型:byte、short、int、long
- Java 各整数类型有固定的表数范围和字段长度,不受具体 OS 的影响,以保证 java 程序的可移植性。
- java 的整型常量默认为 int 型,声明 long 型常量须后加‘l’或‘L’
- java 程序中变量通常声明为 int 型,除非不足以表示较大的数,才使用 long
类型 | 占用存储空间 | 表数范围 |
---|---|---|
byte | 1字节=8bit位 | -128 ~ 127 |
short | 2字节 | -2^15~ 2^15-1 |
int | 4字节 | -2^31~ 2^31-1 (约21亿) |
long | 8字节 | -2^63~ 2^63-1 |
- 1PB=1024TB
- 1TB=1024GB
- 1GB=1024MB
- 1MB(兆字节)包含1024个KB(千字节)。
- 1KB(千字节)包含1024个B(字节)。
- 1B(字节)包含8个bit(位)。
- bit: 计算机中的最小存储单位。byte:计算机中基本存储单元。
浮点类型:float、double
- 与整数类型类似,Java 浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响。
- 浮点型常量有两种表示形式:
- 十进制数形式:如:5.12 512.0f .512 (必须有小数点)
- 科学计数法形式:如:5.12e2 512E2 100E-2
- float:单精度,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求。
- double:双精度,精度是float的两倍。通常采用此类型。
- Java 的浮点型常量默认为double型,声明float型常量,须后加‘f’或‘F’。
类型 | 占用存储空间 | 表数范围 |
---|---|---|
单精度float | 4字节 | -3.403E38 ~ 3.403E38 |
双精度double | 8字节 | -1.798E308 ~ 1.798E308 |
字符类型:char
- char 型数据用来表示通常意义上“字符”(2字节)
- Java中的所有字符都使用Unicode编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符。
- 字符型变量的三种表现形式:
- 字符常量是用单引号(‘ ’)括起来的单个字符。例如:char c1 = ‘a’; char c2 = ‘中’; char c3 = ‘9’;
- Java中还允许使用转义字符‘\’来将其后的字符转变为特殊字符型常量。例如:char c3 = ‘\n’; //’\n’表示换行符
- 直接使用Unicode值来表示字符型常量:‘\uXXXX’。其中,XXXX代表一个十六进制整数。如:\u000a 表示\n。
- char类型是可以进行运算的。因为它都对应有Unicode码。
了解:ASCII 码
- 在计算机内部,所有数据都使用二进制表示。每一个二进制位(bit)有0 和1 两种状态,因此8个二进制位就可以组合出256 种状态,这被称为一个字节(byte)。一个字节一共可以用来表示256 种不同的状态,每一个状态对应一个符号,就是256 个符号,从0000000 到11111111。
- ASCII码:上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码。ASCII码一共规定了128个字符的编码,比如空格“SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
- 缺点:
- 不能表示所有字符。
- 相同的编码表示的字符不一样:比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel(ג)。
了解:Unicode 编码
- 乱码:世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。
- Unicode:一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,使用Unicode 没有乱码的问题。
- Unicode 的缺点:Unicode 只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储:无法区别Unicode 和ASCII:计算机无法区分三个字节表示一个符号还是分别表示三个符号。另外,我们知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费。
了解:UTF-8
- UTF-8 是在互联网上使用最广的一种Unicode 的实现方式。
- UTF-8 是一种变长的编码方式。它可以使用1-6 个字节表示一个符号,根据不同的符号而变化字节长度。
- UTF-8的编码规则:
- 对于单字节的UTF-8编码,该字节的最高位为0,其余7位用来对字符进行编码(等同于ASCII码)。
- 对于多字节的UTF-8编码,如果编码包含n 个字节,那么第一个字节的前n位为1,第一个字节的第n+1 位为0,该字节的剩余各位用来对字符进行编码。在第一个字节之后的所有的字节,都是最高两位为"10",其余6位用来对字符进行编码。
布尔类型:boolean
- boolean 类型用来判断逻辑条件,一般用于程序流程控制:
- if条件控制语句;
- while循环控制语句;
- do-while循环控制语句;
- for循环控制语句;
- boolean类型数据只允许取值true和false,无null。
- 不可以使用0或非0 的整数替代false和true,这点和C语言不同。
- Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达所操作的boolean值,在编译之后都使用java虚拟机中的int数据类型来代替:true用1表示,false用0表示。———《java虚拟机规范8版》
字符串类型:String
- String不是基本数据类型,属于引用数据类型
- 使用方式与基本数据类型一致。例如:String str= “abcd”;
- 一个字符串可以串接另一个字符串,也可以直接串接其他类型的数据。
五、基本数据类型转换
- 自动类型转换:容量小的类型自动转换为容量大的数据类型。数据类型按容量大小排序为:
- 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。
- byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型。
- boolean类型不能与其它数据类型运算。
- 当把任何基本数据类型的值和字符串(String)进行连接运算时(+),基本数据类型的值将自动转化为字符串(String)类型。
强制类型转换
- 自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符:(),但可能造成精度降低或溢出,格外要注意。
- 通常,字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。
- 如:
String a = “43”; inti= Integer.parseInt(a);
boolean
类型不可以转换为其它的数据类型。
六、运算符
算术运算符 | |
---|---|
一元运算符 | ++,– |
二元运算符 | +,-,*,/,% |
赋值运算符 | = |
扩展运算符 | +=,-=,*=,/= |
关系运算符 | >,<,>=,<=,==,!=,instanceof |
逻辑运算符 | &&,||,!,^ |
位运算符 | &,|,^,~,>>,<<,>>> |
条件运算符 | ?: |
字符串连接符 | + |
1、算数运算符
1、一元运算符
算数运算符中++,–属于一元运算符, 该类运算符只需要一个操作数。
-
1、无论是++还是–,每次运算量为1。
-
2、运算符在前面++a;先运算,再赋值。
-
3、运算符在后面a++;先赋值,再运算。
public class TestArithmeticOneOperator {
public static void main(String[] args) {
int a = 2;
System.out.println("a="+a); //a=2
int b = a++; //先给b赋值,a再加1
System.out.println("a="+a); //a=3
System.out.println("b="+b); //b=2
int c =++a; //a先加1,再给c赋值
System.out.println("a="+a); //a=4
System.out.println("c="+c); //c=4
int d =--a; //a先减1,再给d赋值
System.out.println("a="+a); //a=3
System.out.println("d="+d); //d=3
}
}
结果
a=2
a=3
b=2
a=4
c=4
a=3
d=3
2、二元运算符
算数运算符中+,-,*,/,%属于二元运算符, 二元运算符指的是需要两个操作数才能完成运算的运算符,其中%是取模运算符,就是我们常说的求余数操作。
二元运算符的运算规则
整数运算:
1、如果两个操作数有一个为long,则结果也为long.
2、没有long时,结果为int。即使操作数全为short,byte,结果也为int。
浮点运算:
1、如果两个操作数有一个为double,则结果为double。
2、只有两个操作数都是float,则结果为float。
取模运算:
1、其操作数可以为浮点数,一般使用整数,结果是“余数”,“余数”符号和左边操作数相同,如:10%3=1,-11%3=-2;11%-3=2
public class TestArithmeticTwoOperator {
/**
* @param args
*/
public static void main(String[] args) {
byte a = 'a'; //a为97
System.out.println("a="+a); //a为97
short b = 2;
System.out.println("b="+b); //b=2
//byte c = a+b; //报错Type mismatch: cannot convert from int to byte,可见a+b是int类型,而不是short类型
int c = a+b; //97+2=99
System.out.println("c=a+b="+c); //c=97+2=99
long d = 3L;
System.out.println("d="+d); //d=3
//int e = a+d; //报错Type mismatch: cannot convert from long to int,可见a+d是long类型
long e = a+d; //97+3=100
System.out.println("e=a+d="+e); //e=97+3=100
int f = 2*++b; //
System.out.println("f=2*++b="+f); //f=2*(++b)=2*3=6,b先运算为3,在*2赋值给f,此时b=3
System.out.println("b="+b); //b=3
long g = 2*--d;
System.out.println("g=2*--d="+g); //g=2*(--d)=2*2=4,d先运算为2,在*2赋值给g,此时d=2
System.out.println("d="+d); //d=2
long h = 2*d--;
System.out.println("h=2*d--="+h); //h=2*2=4,d先*2赋值给h,再运算--为1,此时d=d--=1
System.out.println("d="+d); //d=1
System.out.println("-11%3="+-11%3); //-11%3=-3......-2
System.out.println("-11%-3="+-11%-3); //-11%-3=3......-2
System.out.println("11%-3="+11%-3); //11%-3=-3......2
}
}
结果
a=97
b=2
c=a+b=99
d=3
e=a+d=100
f=2*++b=6
b=3
g=2*--d=4
d=2
h=2*d--=4
d=1
-11%3=-2
-11%-3=-2
11%-3=2
2、赋值运算符
int a = 3;int b = a;相当于把3赋值给a,把a的值赋给b;
3、扩展运算符
扩展运算符 | ||
---|---|---|
运算符 | 用法举例 | 等效的表达式 |
+= | 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 class TestExtendedOperator {
public static void main(String[] args) {
int a = 1;
int b = 2;
a+=b; //a=a+b=1+2=3
System.out.println("a="+a);
a-=b; //a=a-b=3-2=1
System.out.println("a="+a);
a*=b; //a=a*b=1*2=2
System.out.println("a="+a);
a/=b; //a=a/b=2/2=1
System.out.println("a="+a);
a%=b; //a=a%b=1%2=0......1
System.out.println("a="+a);
a*=b+1; //a=a*(b+1)=1*(2+1)=3
System.out.println("a="+a);
a*=++b; //a=a*(++b)=3*3=9,b=3
System.out.println("a="+a);
System.out.println("b="+b);
a*=b--;
System.out.println("a="+a); //a=a*b=9*3=27,b=b--=2
System.out.println("b="+b); //b=b--=2
a*=b++; //a=a*b=27*2=54,b=b++=3
System.out.println("a="+a);
System.out.println("b="+b); //b=b++=3
}
}
a=3
a=1
a=2
a=1
a=1
a=3
a=9
b=3
a=27
b=2
a=54
b=3
4、关系运算符
关系运算符用来比较运算,且运算的结果是布尔值:true/false
运算符 含义 示例 == 等于 a==b != 不等于 a!=b > 大于 a>b < 小于 a<b >= 大于或者等于 a>=b <= 小于或者等于 a<=b
注意事项:
1、=是赋值运算符,而真正的判断两个操作数是否相等的运算符是==。
2、==、!=是所有(基本和引用)数据类型都有可以使用。
3、>、>=、<、<=仅针对数值类型(byte、short、int、long、float、double。以及char)。
public class TestRelationalOperator {
public static void main(String[] args) {
char a = 'a';
short b = 97;
int c = 98;
float d = 97.1f;
if(b==a){
System.out.println("b==a");
}
if(c>=a){
System.out.println("c>=a");
}
if(a<=d){
System.out.println("a<=d");
}
}
}
结果
b==a
c>=a
a<=d
5、逻辑运算符
逻辑运算的操作数和运算结果都是 boolean值。
逻辑运算符 |
---|
运算符 | 说明 |
---|
逻辑与 | &(与) | 两个操作数位true,结果才是true,否则是false |
---|---|---|
逻辑或 | |(或) | 两个操作数有一个true,结果就是true |
短路与 | &&(与) | 只要有一个为false,则直接返回false |
短路或 | ||(或) | 只要有一个为true,则直接返回true |
逻辑非 | !(非) | 取反:!false为true,!true为false |
逻辑异或 | ^(异或) | 相同为false,不同为true |
public class TestLogicalOperator {
public static void main(String[] args) {
boolean a1 = true;
boolean a2 = false;
int b1 = 2;
int b2 = 2;
System.out.println("a1&a2="+(a1&a2)); //false,a2为false,所以为fasle
System.out.println("a1|a2="+(a1|a2)); //true,a1为true,所以为true
System.out.println("a1&&a2="+(a1&&a2)); //false,a2为false,所以为fasle
System.out.println("a1||a2="+(a1||a2)); //true,a1为true,所以为true
System.out.println("!a1="+(!a1)); //false,a1为true,所以!a1为false
System.out.println("a1^a2="+(a1^a2)); //true,不一样,为true
System.out.println("a2^a2="+(a2^a2)); //false,一样,为fasle
System.out.println("a1&b1++==3="+(a1&++b1==3)); //true,a1为true,并且++b1此时为3,3==3位true,所以为true
System.out.println("b1="+b1); //b1=3
System.out.println("a2&&b2++==3="+(a2&&++b2==3));//false,a2为fasle,此时结果为fasle,后面的不在判断,所以b2还是2
System.out.println("b2="+b2); //b2=2
}
}
结果
a1&a2=false
a1|a2=true
a1&&a2=false
a1||a2=true
!a1=false
a1^a2=true
a2^a2=false
a1&b1++==3=true
b1=3
a2&&b2++==3=false
b2=2
6、位运算符
位运算指的是进行二进制位的运算
public class TestBitwiseOperators {
/**
* @param args
*/
public static void main(String[] args) {
int a = 3; //(前面28个0)0011=3
int b = 4; //(前面28个0)0100=4
int c = 0b11111111111111111111111111111011;
System.out.println("~a="+~a); //(前面28个1)1100=-4,涉及符号位
System.out.println("~b="+~b); //(前面28个1)1011=-5,涉及符号位
System.out.println("c="+c); //-5,涉及符号位
System.out.println("a&b="+(a&b)); //(前面28个0)0000=0
System.out.println("a|b="+(a|b)); //(前面28个0)0111=7
System.out.println("a^b="+(a^b)); //(前面28个0)0111=7
System.out.println("a>>1="+(a>>1)); //(前面28个0)0011>>1=(前面28个0)0001=1
System.out.println("b<<2="+(b<<2)); //(前面28个0)0100<<2=(前面27个0)0000=16
}
}
结果
~a=-4
~b=-5
c=-5
a&b=0
a|b=7
a^b=7
a>>1=1
b<<2=16
7、条件运算符
语法格式:x?y:z
其中x为boolean类型表达式,先计算x的值,若为true,则整个运算的结果为表达式y的值,否则,整个运算的结果为表达式z的值。
public class TestConditionalOperation {
/**
* @param args
*/
public static void main(String[] args) {
int a = 0;
int a2 = 2;
String string1 = a>0?"大于0":a==0?"等于0":"小于0";
String string2 = a2%2==0?"偶数":"奇数";
System.out.println(string1); //等于0
System.out.println(string2); //偶数
}
}
等于0
偶数
8、字符串连接符
"+"运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。
public class TestStringConnector {
/**
* @param args
*/
public static void main(String[] args) {
String a = "3";
int b = 4;
int c = 5;
System.out.print("a+b+c=");
System.out.println(a+b+c); //a+b+c="3"+4+5="34"+5="345"
System.out.print("b+c+a=");
System.out.print(b+c+a); //b+c+a=4+5+"3"=9+"3"="93"
}
}
a+b+c=345
b+c+a=93
流程控制语句结构
一.顺序结构
(在同一个方法中)从上往下执行
流程控制语句结构:
顺序:(在同一个方法中)从上往下执行
从键盘输入一个值,并打印输出
用核心类库中的类:String,System,Math,Scanner等
如果这个类型定义在java.lang包下,那么直接使用,无需导包,直接使用简名称
但是如果这个类型定义在其他包下,那么需要写全名称或导包
全名称:java.util.Scanner
*/
//导包语句:
//格式:import 包.类名;
//位置:必须在源文件的上面,在class的上面
import java.util.Scanner;
class TestStatement{
public static void main(String[] args){
System.out.println("欢迎使用尚硅谷登记系统:");
System.out.println("请输入个人信息:");
//System.out.println(Math.pow(2,3));
//Scanner键盘输入的工具类
//input是一个变量名,自己命名
//方式一:全名称
//java.util.Scanner input = new java.util.Scanner(System.in);
//方式二:导包,简名称
Scanner input = new Scanner(System.in);
System.out.print("请输入年龄:");
int age = input.nextInt();
System.out.println("您的年龄是:" + age);
System.out.print("请输入入学成绩:");
double score = input.nextDouble();
System.out.println("您的分数是:" + score);
System.out.print("请告知我是否帅/美:");
boolean flag = input.nextBoolean();
System.out.println("帅/美否:" + flag);
System.out.print("请输入您的姓名:");
String name = input.next();
System.out.println("您的名字是:"+name);
}
}
二.复合语句
与C语言及其他语言相同,Java语言的复合语句是以整个块区为单位的语句,所以又称为块语句。复合语句由开括号“{”开始,闭括号“}”结束。
复合语句中的每一语句都是从上到下执行。复合语句以整个块为单位,并且在复合语句中可以嵌套复合语句。
Java代码结构:
class 类{
{
//代码块
}
方法签名{
//方法体
}
内部类{
内部类体
}
}
复合语句为变量创建了一个作用域。在该作用域中某个变量被创建并能够使用,如果在某个变量的作用域外使用该变量,则会发生错误。
三.分支结构
1.条件语句
1.if条件语句
语法格式:
if(条件表达式){
//当条件表达式结果为true时,需要执行的语句块
}
说明:
(1)条件表达式必须是布尔表达式(关系表达式或逻辑表达式)、布尔变量。
(2)语句块只有一个语句时,{}可以省略,但是建议保留
执行过程:
条件成立就执行,条件不成立就不执行。
2.if…else双分支条件语句
语法格式:
if(条件表达式){
//当条件表达式结果为true时,需要执行的语句块1
}else{
//当条件表达式结果为false时,需要执行的语句块2
}
执行过程:
当条件成立执行语句块1,不成立执行语句块2
说明:
(1)条件表达式必须是布尔表达式(关系表达式或逻辑表达式)、布尔变量。
(2)if或else的语句块只有一个语句时,{}可以省略,但是建议保留
3.if…else if多分支条件语句
语法结构:
if(条件表达式1){
//当条件表达式1结果为true时,需要执行的复合语句1
}else if(条件表达式2){
//当条件表达式2结果为true时,需要执行的复合语句2
}
....
【else{
//当上述条件表达式结果都为false时,需要执行的语句
}】
执行过程:
从上至下判断条件,如果一旦某个条件成立了,就执行对应的语句块,后面的条件就不看了,如果所有条件都不满足,如果存在else,那么执行else,如果不存在else,就什么也不执行。
注意:
(1)每个if或者else后面的{}不是必须的,但是如果没有{},默认只能带一条语句。即if或else后面的语句块只有一条语句时,可以省略{},但是可读性不够好。
(2)最后的else不是必须的,可以缺省
(3)当多个条件是互斥关系时,顺序无所谓
当多个条件是包含关系时,“小上大下/子上父下”
public static void main(String[] args) {
int score = 89;
if(score>90){
System.out.println("优秀");
}else if(score>80){
System.out.println("良好");
}else if(score>60){
System.out.println("及格");
}else{
System.out.println("不合格");
}
}
public static void main(String[] args) {
int score = 89;
if(score>90 && score<=100){
System.out.println("优秀");
}else if(score>80 && score<=90){
System.out.println("良好");
}else if(score>60 && score<=80){
System.out.println("及格");
}else{
System.out.println("不合格");
}
}
4.嵌套
if...else系列语句可以嵌套,在任意一个语句块中都可以再嵌套其他的条件语句。
执行特点:
当外层条件满足时,内层条件才判断
2.选择结构
语法结构:
switch(表达式){
case 常量值1:
//执行语句块1
[break;]
case 常量值2:
//执行语句块2
[break;]
......
case 常量值n:
//执行语句块n
[break;]
【default:
//执行缺省语句
[break;]
】
}
注意:
(1)表达式返回的结果类型只能是byte,short,char,int,枚举(JDK1.5之后),String(JDK1.7之后)
(2)表达式返回的结果的类型需要与case后的值的类型保持一致
(3)case后面只能跟常量值,不能是变量值或不确定的表达式值
(4)同一个switch语句,case的常量值必须互不相同
(5)break是可选的,当表达式返回结果与某个case后的值进行匹配成功后,执行相应分支的语句,一旦进入某个分支后,只有直到遇到break才会退出switch,否则将会继续执行下一个case的语句。
(6)default是可选的。位置也不一定是最后,也可以在任意case的位置。但是不管再哪里,都是要所有case的常量值不匹配后才会进入default分支,一旦进入default分支,也是需要遇到break或switch的闭括号“}”才会停止。
总结:switch的入口,表达式值与某个case匹配,或都不匹配,从default进入。
switch的出口,遇到break或者switch的闭括号“}”结束
3、条件判断与选择结构的选择
当条件判断是等值判断,并且表达式的结果是byte,short,char,int,枚举,String类型的时候,用switch会更合适,
其他都使用条件判断
可以使用switch…case的,肯定可以使用if…else,返回来不一定。
四.循环结构
循环语句就是在满足一定条件的情况下反复执行某一个操作。在Java中提供了3中常用的循环语句,分别是while循环语句、do…while循环语句和for循环语句。
1、三种循环语句的语法
while循环语句
语法结构:
while(条件表达式){
//循环体语句块
}
执行过程:
(1)先判断循环条件
(2)条件成立,执行循环体语句块
(3)回到(1)
(4)直到条件不成立,结束while循环
do…while循环语句
语法结构:
do{
//循环体语句块
}while(条件表达式);
执行过程:
(1)先无条件执行一次循环体语句块
(2)再判断循环条件
(3)条件成立,再次执行循环体语句块
(4)回到(2)
(5)直到条件不成立,结束do…while循环
先执行一次循环体后,再判断条件,如果条件成立,继续下一次循环体,因此do…while循环至少执行一次循环体。
for循环结构
语法结构:
for(初始化表达式1; 循环条件表达式2; 迭代表达式3){
//循环体语句块
}
执行过程:
(1)执行初始化表达式1
(2)判断循环条件表达式2
(3)如果条件成立,执行循环体语句块
(4)执行迭代表达式3
(5)重复(2)(3)(4)(5)
(6)直到循环条件不成立,结束for循环
注意:
- 两个分号必不可少
- 三个表达式可以省略,但要结合break,否则死循环。
- 表达式1可以有多个变量声明,但必须是同一个类型,用逗号分隔
- 表达式3可以有多个变量更新,用逗号分隔
foreach循环结构(后面数组和集合部分再讲)
语法结构:
for(元素类型 元素临时名:数组/集合){
//循环体
}
2.三种循环语句的比较
-
都能实现循环重复执行某段代码,可以互相转换
-
执行顺序不同:for、while是先判断后执行,do…while是先执行后判断
执行效果不同:while和for,如果第一次条件判断不满足,那么循环体将一次都不执行,而do…while是先执行一次循环体,然后才判断条件,看是否第二次执行循环体 -
执行效率不同:do…while效率最高
每一种循环都有四个循环要素,而for循环体现的最明显,循环变量初始值,循环条件,循环体,循环变量更新迭代。
for循环适用于循环次数比较明确的,而do…while适用于至少执行一次的循环。while适用于循环条件比较明确的。
3、嵌套循环
一个循环体中嵌套了另一个完整的循环结构。一个循环充当另一个循环的循环体。
三种循环体中都可以嵌套另外任意一种循环结构
执行特点,先执行外层循环,外循环执行一次,内循环从头至尾执行一轮。总次数=外循环次数*内循环次数,详细说:总次数= 内循环第一轮的次数+内循环第二轮的次数+内循环第三轮的次数…+内循环第n轮的次数。轮数即是外循环的次数。
例如:找出1000以内的所有完数
分析:(1)1-1000个数需要判断是否是否是完数,因此需要循环1000次
for(int i=1; i<=1000;i++){
//i是否是完数
//如果是,就打印i,如果不是就不打印i
}
(2)i是否是完数的判断,要把i的所有因子找出来,并相加
int sum = 0;
for(int j=1; j<i; j++){
if(i%j==0){
sum += j;
}
}
(3)如果是,就打印i
if(sum == i){
System.out.println(i);
}
4、跳转
break:只能用在switch或循环中,用于跳出所在的当层循环或switch
continue:只能用于循环中,提前结束本次循环,继续下一次循环
return:用于结束当前方法
public class TestBreakAndContinue {
public static void main(String[] args) {
for (int i = 1; i <=5; i++) {
for (int j = 1; j <=5; j++) {
System.out.print("*");
if(i==j){
break;
}
}
System.out.println();
}
}
}
输出结果:
*
**
***
****
*****
public class TestBreakAndContinue {
public static void main(String[] args) {
for (int i = 1; i <=5; i++) {
for (int j = 1; j <=5; j++) {
System.out.print("*");
if(i==j){
continue ;
}
}
System.out.println();
}
}
}
输出结果:
*****
*****
*****
*****
*****
数组
一、一维数组
1、Java语言中的数组是一种 引用数据类型。不属于基本数据类型。数组的父类是 Object。
2、数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合)
3、数组当中可以存储“基本数据类型”的数据,也可以存储“引用数据类型”的数据。
4、数组因为是引用类型,所以数组对象存储在 堆内存 当中。(数组是存储在堆当中的)
5、数组当中如果存储的是“java对象”的话,实际上存储的是对象的“引用(内存地址)”,数组中不能直接存储java对象。
6、数组一旦创建,在java中规定,长度不可变。(数组长度不可变)
7、数组的分类:一维数组、二维数组、三维数组、多维数组…(一维数组较多,二维数组偶尔使用!)
8、所有的数组对象都有 length 属性(java自带的),用来获取数组中元素的个数。
9、java中的数组要求数组中元素的 类型统一。
比如:int类型数组只能存储int类型,Person类型数组只能存储Person类型。
10、数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的。内存地址连续。(数组特点)
11、所有的数组都是拿“第一个小方框的内存地址”作为整个数组对象的内存地址。
(数组中首元素的内存地址作为整个数组对象的内存地址。)
12、数组中每一个元素都是有下标的,下标从0开始,以1递增。最后一个元素的下标是:length - 1
13、数组这种数据结构的优点和缺点是什么?
优点:查询/查找/检索某个下标上的元素时效率极高。
原因:
第一:每一个元素的内存地址在空间存储上是连续的。
第二:每一个元素类型相同,所以占用空间大小一样。
第三:知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以
通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位
元素,所以数组的检索效率是最高的。
注意:
数组中存储100个元素,或者存储100万个元素,在元素查询/检索方面,效率是相同的,
因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的。(算出一个
内存地址,直接定位的。)
缺点:
第一:由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
第二:数组不能存储大数据量。
因为很难在内存空间上找到一块特别大的连续的内存空间。
注意:
对于数组中最后一个元素的增删,是没有效率影响的。
14、怎么声明/定义一个一维数组?
语法格式:
int[] array1;
double[] array2;
boolean[] array3;
String[] array4;
Object[] array5;
15、怎么初始化一个一维数组呢?
包括两种方式:静态初始 化一维数组,动态初始化 一维数组。
静态初始化语法格式:
java风格:
int[] array = {100, 2100, 300, 55};
C++风格:
int array[] = {100, 2100, 300, 55};
动态初始化语法格式:
Java风格:
int[] array = new int[5]; // 这里的5表示数组的元素个数。
// 初始化一个5个长度的int类型数组,每个元素默认值0
String[] names = new String[6]; // 初始化6个长度的String类型数组,每个元素默认值null。
C++风格:
int array[] = new int[5]; // 这里的5表示数组的元素个数。
// 初始化一个5个长度的int类型数组,每个元素默认值0
String names[] = new String[6]; // 初始化6个长度的String类型数组,每个元素默认值null。
注意:
采用动态初始化,数组会赋默认值!
注意:
和c++的区别
c++定义数组
//静态初始化
int a[10];
int a[] = {1, 2, 3, 4};
int a[100] = {1, 2, 3, 4};
//动态初始化
int *a = new int[10];
16、怎么修改一维数组某一个元素的值?
语法格式:
数组名[下标] = 值;
eg.
a[1] = 100;
16、 什么时候采用静态初始化方式,什么时候使用动态初始化方式呢?
当你创建数组的时候,确定数组中存储哪些具体的元素时,采用静态初始化方式。
当你创建数组的时候,不确定将来数组中存储哪些数据,你可以采用动态初始化的方式,预先分配内存空间。
17、方法形参为一维数组如何传参?
public void print(int[] num){
for (int i = 0; i < num.length; i++){
System.out.println(num[i]);
}
}
传参:
int[] a = {1, 2, 3, 4};
print(a);
还可以这样传参:
传静态数组
print(new int[]{1, 2, 3, 4});
new int[]{1, 2, 3, 4}还可以.length是一个数组对象!
18、数组扩容(效率低)
方法:(System类的方法)
/**
*@src 拷贝源
*@srcPos 拷贝源起始位置
*@dest 目标数组
*@destPos 目标数组起始位置
*@length 拷贝长度
*/
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
eg.
int[] a = {1, 2, 3, 4};
int[] b = new int [10];
System.arraycopy(a, 0, b, 0, aa.length);
//从a数组下标为0开始拷贝,拷贝到从b数组下标为0开始存入,长度为整个a数组
1
2
3
4
二、二维数组
1、二维数组其实是一个 特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。
2、三维数组是什么?
三维数组是一个特殊的二维数组,特殊在这个二维数组 中每一个元素是一个一维数组。
实际的开发中使用最多的就是一维数组。二维数组也很少使用。三维数组几乎不用。
3、二维数组静态初始化
int[][] array = {
{1,1,1},
{2,3,4,5},
{0,0,0,0},
{2,3,4,5},
{2,3,4,5},
{2,3,4,5},
{2,3,4,5}
};
3、二维数组动态初始化
eg.
int[][] array = new int[3][4];
5、关于二维数组中元素的:读和改。
a[二维数组中的一维数组的下标][一维数组的下标]
a[0][0]:表示第1个一维数组中的第1个元素。
a[3][100]:表示第4个一维数组中的第101个元素。
注意:
对于a[3][100]来说,其中 a[3] 是一个整体。[100]是前面a[3]执行结束的结果然后再下标100。
6、方法形参为二维数组如何传参?
public void print(int[][] num){
for (int i = 0; i < num.length; i++){
for (int j = 0; j < num[i].length; j++){
System.out.println(num[i][j]);
}
}
}
传参:
int[][] a = {{1,2,3,4},{4,5,6,76},{1,23,4}};
print(a);
还可以这样传参:
传静态数组
print(new int[][]{{1,2,3,4},{4,5,6,76},{1,23,4}});
new int[][]{{1,2,3,4},{4,5,6,76},{1,23,4}}还可以.length是一个数组对象!
总结
1.1、数组的优点和缺点,并且要理解为什么。
第一:空间存储上,内存地址是连续的。
第二:每个元素占用的空间大小相同。
第三:知道首元素的内存地址。
第四:通过下标可以计算出偏移量。
通过一个数学表达式,就可以快速计算出某个下标位置上元素的内存地址,
直接通过内存地址定位,效率非常高。
优点:检索效率高。
缺点:随机增删效率较低,数组无法存储大数据量。
注意:数组最后一个元素的增删效率不受影响。
1.2、一维数组的静态初始化和动态初始化
静态初始化:
int[] arr = {1,2,3,4};
Object[] objs = {new Object(), new Object(), new Object()};
动态初始化:
int[] arr = new int[4]; // 4个长度,每个元素默认值0
Object[] objs = new Object[4]; // 4个长度,每个元素默认值null
1.3、一维数组的遍历
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
1.4、二维数组的静态初始化和动态初始化
静态初始化:
int[][] arr = {
{1,2,34},
{54,4,34,3},
{2,34,4,5}
};
Object[][] arr = {
{new Object(),new Object()},
{new Object(),new Object()},
{new Object(),new Object(),new Object()}
};
动态初始化:
int[][] arr = new int[3][4];
Object[][] arr = new Object[4][4];
Animal[][] arr = new Animal[3][4];
// Person类型数组,里面可以存储Person类型对象,以及Person类型的子类型都可以。
Person[][] arr = new Person[2][2];
....
1.5、二维数组的遍历
for(int i = 0; i < arr.length; i++){ // 外层for循环负责遍历外面的一维数组。
// 里面这个for循环负责遍历二维数组里面的一维数组。
for(int j = 0; j < arr[i].length; j++){
System.out.print(arr[i][j]);
}
// 换行。
System.out.println();
}
1.6、数组的拷贝:System.arraycopy()方法的使用
数组有一个特点:长度一旦确定,不可变。
所以数组长度不够的时候,需要扩容,扩容的机制是:新建一个大数组,将小数组中的数据拷贝到大数组,然后小数组对象被垃圾回收。
补充:
数组长度为0:
int[] f = {};//数组长度为0
System.out.println(f.length);//0
int[] g = new int[0];//数组长度为0
System.out.println(g.length);//0
int[] h = null;
//System.out.println(h.length);//空指针异常
三、数组的算法
冒泡排序算法
冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素为止。
以下是冒泡排序的详细步骤和Java代码实现:
步骤:
- 比较相邻的元素:如果第一个比第二个大,就交换它们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
// 外层循环控制遍历的次数
for (int i = 0; i < n - 1; i++) {
// 内层循环进行相邻元素的比较和交换
for (int j = 0; j < n - 1 - i; j++) {
// 如果前一个元素大于后一个元素,则交换它们
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
bubbleSort(arr);
System.out.println("排序后的数组:");
for (int i : arr) {
System.out.print(i + " ");
}
}
}
代码解释
- 外层循环:
for (int i = 0; i < n - 1; i++)
,控制遍历的次数,每次遍历都会将当前未排序部分的最大值“冒泡”到最后。 - 内层循环:
for (int j = 0; j < n - 1 - i; j++)
,进行相邻元素的比较和交换。n - 1 - i
是因为每次遍历后,数组的最后部分已经是排序好的,不需要再比较。 - 交换元素:
if (arr[j] > arr[j + 1])
,如果前一个元素大于后一个元素,则交换它们的位置。
通过这种方式,冒泡排序逐步将最大的元素移动到数组的末尾,直到整个数组有序
选择法排序
选择法排序(Selection Sort)
是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
下面是一个使用Java实现选择法排序的示例代码:
public class SelectionSort {
public static void selectionSort(int[] arr) {
int n = arr.length;
// 遍历所有元素
for (int i = 0; i < n - 1; i++) {
// 找到未排序部分的最小元素的索引
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 将找到的最小元素与当前元素交换
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
// 测试方法
public static void main(String[] args) {
int[] arr = {64, 25, 12, 22, 11};
selectionSort(arr);
System.out.println("Sorted array");
for (int i : arr) {
System.out.print(i + " ");
}
}
}
代码解释:
selectionSort
方法接受一个整数数组arr
作为参数。- 外层循环从数组的第一个元素遍历到倒数第二个元素,
i
表示当前未排序部分的第一个元素。 - 内层循环从
i + 1
开始遍历到数组的最后一个元素,找到未排序部分的最小元素的索引minIndex
。 - 找到最小元素后,将其与当前元素
arr[i]
交换位置。 - 在
main
方法中,创建一个测试数组并调用selectionSort
方法进行排序,最后打印排序后的数组。
这样,通过选择法排序算法,数组中的元素将按从小到大的顺序排列。
交换法排序
交换法排序(也称为冒泡排序)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就交换它们的位置。这个过程会持续进行,直到没有需要交换的元素为止。
下面是一个使用Java实现的冒泡排序的示例代码:
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
boolean swapped;
for (int i = 0; i < n - 1; i++) {
swapped = false;
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换 arr[j] 和 arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
// 如果没有发生交换,说明数组已经有序,提前退出
if (!swapped) break;
}
}
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
bubbleSort(arr);
System.out.println("排序后的数组:");
for (int i : arr) {
System.out.print(i + " ");
}
}
}
CopyInsert
代码解释:
bubbleSort
方法接受一个整数数组arr
作为参数。- 使用两个嵌套的
for
循环来遍历数组。外层循环控制遍历的轮数,内层循环进行相邻元素的比较和交换。 - 如果在一轮遍历中没有发生任何交换,说明数组已经有序,可以提前退出循环。
- 在
main
方法中,创建一个示例数组并调用bubbleSort
方法进行排序,最后打印排序后的数组。
这个实现是经典的冒泡排序算法,时间复杂度为 O(n^2),适用于小规模数据的排序。
插入法排序
插入法排序(Insertion Sort)是一种简单的排序算法,其基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。下面是一个用Java实现的插入法排序的示例代码:
public class InsertionSort {
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; ++i) {
int key = arr[i];
int j = i - 1;
// 将 arr[0..i-1] 中大于 key 的元素向后移动
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6};
insertionSort(arr);
System.out.println("排序后的数组:");
for (int num : arr) {
System.out.print(num + " ");
}
}
}
CopyInsert
代码解释:
insertionSort
方法接受一个整数数组arr
作为参数。- 外层循环从数组的第二个元素开始(索引为1),遍历到数组的最后一个元素。
- 对于每一个元素
arr[i]
,将其存储在key
变量中,并将j
初始化为i - 1
。 - 内层循环从
i - 1
开始向前遍历,直到找到arr[j]
小于或等于key
的位置,或者j
变为负数。 - 在内层循环中,将大于
key
的元素向后移动一个位置。 - 将
key
插入到正确的位置arr[j + 1]
。 - 在
main
方法中,创建一个示例数组并调用insertionSort
方法进行排序,最后打印排序后的数组。
这样,通过上述代码,你可以实现一个简单的插入法排序算法。
折半法排序
折半法排序(Binary Insertion Sort)是一种改进的插入排序算法,它通过使用二分查找来减少插入排序中查找插入位置的时间复杂度。下面是使用Java实现折半法排序的代码,并详细描述其实现步骤。
public class BinaryInsertionSort {
public static void binaryInsertionSort(int[] array) {
int n = array.length;
for (int i = 1; i < n; ++i) {
int key = array[i];
int insertedPosition = findPosition(array, 0, i - 1, key);
// 移动元素以腾出插入位置
for (int j = i - 1; j >= insertedPosition; --j) {
array[j + 1] = array[j];
}
array[insertedPosition] = key;
}
}
private static int findPosition(int[] array, int start, int end, int key) {
while (start <= end) {
int mid = start + (end - start) / 2;
if (key < array[mid]) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return start;
}
public static void main(String[] args) {
int[] array = {37, 23, 0, 17, 12, 72, 31, 46, 100, 88, 54};
binaryInsertionSort(array);
for (int i : array) {
System.out.print(i + " ");
}
}
}
代码解释
- binaryInsertionSort方法:
int n = array.length;
:获取数组的长度。for (int i = 1; i < n; ++i)
:从数组的第二个元素开始遍历,因为第一个元素默认是已排序的。int key = array[i];
:将当前元素作为关键元素(key)。int insertedPosition = findPosition(array, 0, i - 1, key);
:使用二分查找找到关键元素的插入位置。for (int j = i - 1; j >= insertedPosition; --j)
:从当前元素的前一个元素开始,将元素向后移动,直到插入位置。array[insertedPosition] = key;
:将关键元素插入到找到的插入位置。
- findPosition方法:
while (start <= end)
:当start小于等于end时,继续查找。int mid = start + (end - start) / 2;
:计算中间位置。if (key < array[mid])
:如果关键元素小于中间位置的元素,则在左半部分继续查找。else
:否则,在右半部分继续查找。return start;
:返回插入位置。
- main方法:
int[] array = {37, 23, 0, 17, 12, 72, 31, 46, 100, 88, 54};
:定义一个待排序的数组。binaryInsertionSort(array);
:调用排序方法对数组进行排序。for (int i : array)
:遍历排序后的数组并打印每个元素。
实现步骤描述
-
初始化:从数组的第二个元素开始(索引为1),遍历整个数组。
-
选择关键元素:将当前元素作为关键元素(key)。
-
查找插入位置
:使用二分查找法在已排序的部分(从索引0到当前元素的前一个元素)中找到关键元素的插入位置。
- 二分查找的步骤:
- 计算中间位置(mid)。
- 如果关键元素小于中间位置的元素,则在左半部分继续查找。
- 否则,在右半部分继续查找。
- 当start大于end时,start即为插入位置。
- 二分查找的步骤:
-
移动元素:将插入位置及其后的元素向后移动一位,为关键元素腾出插入位置。
-
插入关键元素:将关键元素插入到找到的插入位置。
-
重复步骤:继续处理下一个元素,直到所有元素都被处理。
通过这种方式,折半法排序利用二分查找减少了查找插入位置的时间复杂度,从而提高了插入排序的效率。
package cn.tedu.day06;
//父类
class Parent {
int age;
String name;
void eat() {
System.out.println("parent吃饭");
}
}
//子类
class Son extends Parent{
//子类方法和父类一样(方法名称和参数列表一样),方法体不同造成方法的重写,
void eat() {
System.out.println("son吃饭");//重写提高了方法的扩展性,
//当子类不满足从父类继承到的方法时,可以使用方法的重写
}
}
public class Text4 {
public static void main(String[] args) {
new Son().eat();//调用时看new的是什么对象,new Son()则使用子类中的方法
}
}
运行结果
son吃饭
rt方法**:
int n = array.length;
:获取数组的长度。for (int i = 1; i < n; ++i)
:从数组的第二个元素开始遍历,因为第一个元素默认是已排序的。int key = array[i];
:将当前元素作为关键元素(key)。int insertedPosition = findPosition(array, 0, i - 1, key);
:使用二分查找找到关键元素的插入位置。for (int j = i - 1; j >= insertedPosition; --j)
:从当前元素的前一个元素开始,将元素向后移动,直到插入位置。array[insertedPosition] = key;
:将关键元素插入到找到的插入位置。
- findPosition方法:
while (start <= end)
:当start小于等于end时,继续查找。int mid = start + (end - start) / 2;
:计算中间位置。if (key < array[mid])
:如果关键元素小于中间位置的元素,则在左半部分继续查找。else
:否则,在右半部分继续查找。return start;
:返回插入位置。
- main方法:
int[] array = {37, 23, 0, 17, 12, 72, 31, 46, 100, 88, 54};
:定义一个待排序的数组。binaryInsertionSort(array);
:调用排序方法对数组进行排序。for (int i : array)
:遍历排序后的数组并打印每个元素。
实现步骤描述
-
初始化:从数组的第二个元素开始(索引为1),遍历整个数组。
-
选择关键元素:将当前元素作为关键元素(key)。
-
查找插入位置
:使用二分查找法在已排序的部分(从索引0到当前元素的前一个元素)中找到关键元素的插入位置。
- 二分查找的步骤:
- 计算中间位置(mid)。
- 如果关键元素小于中间位置的元素,则在左半部分继续查找。
- 否则,在右半部分继续查找。
- 当start大于end时,start即为插入位置。
- 二分查找的步骤:
-
移动元素:将插入位置及其后的元素向后移动一位,为关键元素腾出插入位置。
-
插入关键元素:将关键元素插入到找到的插入位置。
-
重复步骤:继续处理下一个元素,直到所有元素都被处理。
通过这种方式,折半法排序利用二分查找减少了查找插入位置的时间复杂度,从而提高了插入排序的效率。
package cn.tedu.day06;
//父类
class Parent {
int age;
String name;
void eat() {
System.out.println("parent吃饭");
}
}
//子类
class Son extends Parent{
//子类方法和父类一样(方法名称和参数列表一样),方法体不同造成方法的重写,
void eat() {
System.out.println("son吃饭");//重写提高了方法的扩展性,
//当子类不满足从父类继承到的方法时,可以使用方法的重写
}
}
public class Text4 {
public static void main(String[] args) {
new Son().eat();//调用时看new的是什么对象,new Son()则使用子类中的方法
}
}
运行结果
son吃饭