Java基础语法

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、定义合法标识符规则【重要】
  1. 由 26 个英文字母大小写,0-9,_或$ 组成
  2. 数字不可以开头。
  3. 标识符不能包含空格。
  4. 不可以使用关键字和保留字,但能包含关键字和保留字。
  5. 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
类型占用存储空间表数范围
byte1字节=8bit位-128 ~ 127
short2字节-2^15~ 2^15-1
int4字节-2^31~ 2^31-1 (约21亿)
long8字节-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’。
类型占用存储空间表数范围
单精度float4字节-3.403E38 ~ 3.403E38
双精度double8字节-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+=ba=a+b
-=a-=ba=a-b
*=a*=ba=a*b
/=a/=ba=a/b
%=a%=ba=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循环
注意:

  1. 两个分号必不可少
  2. 三个表达式可以省略,但要结合break,否则死循环。
  3. 表达式1可以有多个变量声明,但必须是同一个类型,用逗号分隔
  4. 表达式3可以有多个变量更新,用逗号分隔
foreach循环结构(后面数组和集合部分再讲)
语法结构:
for(元素类型 元素临时名:数组/集合){
//循环体
}
2.三种循环语句的比较

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 都能实现循环重复执行某段代码,可以互相转换

  2. 执行顺序不同:for、while是先判断后执行,do…while是先执行后判断
    执行效果不同:while和for,如果第一次条件判断不满足,那么循环体将一次都不执行,而do…while是先执行一次循环体,然后才判断条件,看是否第二次执行循环体

  3. 执行效率不同: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代码实现:

步骤:

  1. 比较相邻的元素:如果第一个比第二个大,就交换它们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
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 + " ");
        }
    }
}

代码解释

  1. 外层循环for (int i = 0; i < n - 1; i++),控制遍历的次数,每次遍历都会将当前未排序部分的最大值“冒泡”到最后。
  2. 内层循环for (int j = 0; j < n - 1 - i; j++),进行相邻元素的比较和交换。n - 1 - i 是因为每次遍历后,数组的最后部分已经是排序好的,不需要再比较。
  3. 交换元素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 + " ");
        }
    }
}

代码解释:

  1. selectionSort 方法接受一个整数数组 arr 作为参数。
  2. 外层循环从数组的第一个元素遍历到倒数第二个元素,i 表示当前未排序部分的第一个元素。
  3. 内层循环从 i + 1 开始遍历到数组的最后一个元素,找到未排序部分的最小元素的索引 minIndex
  4. 找到最小元素后,将其与当前元素 arr[i] 交换位置。
  5. 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

代码解释:

  1. bubbleSort 方法接受一个整数数组 arr 作为参数。
  2. 使用两个嵌套的 for 循环来遍历数组。外层循环控制遍历的轮数,内层循环进行相邻元素的比较和交换。
  3. 如果在一轮遍历中没有发生任何交换,说明数组已经有序,可以提前退出循环。
  4. 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

代码解释:

  1. insertionSort 方法接受一个整数数组 arr 作为参数。
  2. 外层循环从数组的第二个元素开始(索引为1),遍历到数组的最后一个元素。
  3. 对于每一个元素 arr[i],将其存储在 key 变量中,并将 j 初始化为 i - 1
  4. 内层循环从 i - 1 开始向前遍历,直到找到 arr[j] 小于或等于 key 的位置,或者 j 变为负数。
  5. 在内层循环中,将大于 key 的元素向后移动一个位置。
  6. key 插入到正确的位置 arr[j + 1]
  7. 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 + " ");
        }
    }
}

代码解释

  1. 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;:将关键元素插入到找到的插入位置。
  2. findPosition方法
    • while (start <= end):当start小于等于end时,继续查找。
    • int mid = start + (end - start) / 2;:计算中间位置。
    • if (key < array[mid]):如果关键元素小于中间位置的元素,则在左半部分继续查找。
    • else:否则,在右半部分继续查找。
    • return start;:返回插入位置。
  3. main方法
    • int[] array = {37, 23, 0, 17, 12, 72, 31, 46, 100, 88, 54};:定义一个待排序的数组。
    • binaryInsertionSort(array);:调用排序方法对数组进行排序。
    • for (int i : array):遍历排序后的数组并打印每个元素。

实现步骤描述

  1. 初始化:从数组的第二个元素开始(索引为1),遍历整个数组。

  2. 选择关键元素:将当前元素作为关键元素(key)。

  3. 查找插入位置

    :使用二分查找法在已排序的部分(从索引0到当前元素的前一个元素)中找到关键元素的插入位置。

    • 二分查找的步骤:
      • 计算中间位置(mid)。
      • 如果关键元素小于中间位置的元素,则在左半部分继续查找。
      • 否则,在右半部分继续查找。
      • 当start大于end时,start即为插入位置。
  4. 移动元素:将插入位置及其后的元素向后移动一位,为关键元素腾出插入位置。

  5. 插入关键元素:将关键元素插入到找到的插入位置。

  6. 重复步骤:继续处理下一个元素,直到所有元素都被处理。

通过这种方式,折半法排序利用二分查找减少了查找插入位置的时间复杂度,从而提高了插入排序的效率。

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;:将关键元素插入到找到的插入位置。
  1. findPosition方法
    • while (start <= end):当start小于等于end时,继续查找。
    • int mid = start + (end - start) / 2;:计算中间位置。
    • if (key < array[mid]):如果关键元素小于中间位置的元素,则在左半部分继续查找。
    • else:否则,在右半部分继续查找。
    • return start;:返回插入位置。
  2. main方法
    • int[] array = {37, 23, 0, 17, 12, 72, 31, 46, 100, 88, 54};:定义一个待排序的数组。
    • binaryInsertionSort(array);:调用排序方法对数组进行排序。
    • for (int i : array):遍历排序后的数组并打印每个元素。

实现步骤描述

  1. 初始化:从数组的第二个元素开始(索引为1),遍历整个数组。

  2. 选择关键元素:将当前元素作为关键元素(key)。

  3. 查找插入位置

    :使用二分查找法在已排序的部分(从索引0到当前元素的前一个元素)中找到关键元素的插入位置。

    • 二分查找的步骤:
      • 计算中间位置(mid)。
      • 如果关键元素小于中间位置的元素,则在左半部分继续查找。
      • 否则,在右半部分继续查找。
      • 当start大于end时,start即为插入位置。
  4. 移动元素:将插入位置及其后的元素向后移动一位,为关键元素腾出插入位置。

  5. 插入关键元素:将关键元素插入到找到的插入位置。

  6. 重复步骤:继续处理下一个元素,直到所有元素都被处理。

通过这种方式,折半法排序利用二分查找减少了查找插入位置的时间复杂度,从而提高了插入排序的效率。

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吃饭
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值