Java基础(上)

JAVA 上部

第一章 变量与运算符

一、变量的概念和使用

1、变量命名规则

①字:字母;下:下划线;美:美元符号$;人:人民币符号¥;数:可以包含数字,但是不能将数字放在前面;驼峰:字母命名采用驼峰式,如:javaDemo

②不能使用关键字,如:public,class等

二、数据类型转换

1、Java常用数据类型

①基本数据类型

数值型:整型:byte、short、int、long

​ 浮点型:float、double

字符型:字符型:char

​ 布尔型:boolean

引用数据类型:类:class

​ 接口:interface

​ 数组:[]

②数据类型说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H5OSmUYH-1689128132280)(D:\java\typore\图片\image-20230613195442028.png)]

2、自动数据类型转换

①两种数据要兼容,例如都是数值型(整型和浮点型)

②两种数据类型计算后,转换为两者中较大的数据类型

double dNum = 20.22;
int iNum = 2;
System.out.println(dNum+iNum);

则结果为:22.22

3、强制类型数据转换

在需要做类型转换的变量前加上(转换的目标类型)

例如:double a=1.1强制转换为int类型

int i = (int)a;

int sum = (int)dNum + iNum;
System.out.println(sum);

则结果为:22

三、Scanner类可以从键盘获取输入的信息

第一步,导入相关包:

import java.util.*;

第二步,创建Scanner对象:

Scanner input = new Scanner(System.in);

第三步,获得输入的数据:

System.out.println("");
int age = input.nextInt();

例题:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KelfJ3JJ-1689128132282)(D:\java\练习\day1\2使用scanner类获取键盘输入的信息\练习2.png)]

package com.sxt;

import java.util.Scanner;

public class Test1 {
	public static void main(String[] args) {
		Scanner inpt = new Scanner(System.in);
		System.out.println("请输入4位会员卡号:");
		int num = input.nextInt();
		System.out.println("会员卡号是:"+num);
	}
}

四、运算符

1、赋值运算符

=

int a = 1;意思是定义一个数据类型是整型,并把1赋值给a,此时a的值是1

2、算数运算符

①基本运算符:+:加、—:减、*:乘、/:除、%:取余–如10%4=2,即取10除以4的余数

++ 在原有的数值上+1

++a 先计算a+1,然后将得到的数据赋值给a;

a++ 先获得a的值,再计算a+1最后将得到的数据赋值给a;

– 在原有的数值上-1;

②复合运算符:+= 加等于 a+=3相当于a=a+3

​ -= 减等于 a-=3相当于a=a-3

​ *= 乘等于 a*=3相当于a=a*3

​ /= 除等于 a/=3相当于a=a/3

例题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bVxdYlKk-1689128132283)(D:\java\练习\day1\4运算符的运用\练习3.png)]

        Scanner input = new Scanner(System.in);
	    System.out.println("请输入4位会员卡号:")
	    int cardNum = input.nextInt();
		int qianwei = cardNum / 1000;
		System.out.println("千位数是:"+qianwei);
		int baiwei = cardNum % 1000 / 100;
		System.out.println("百位数是:"+baiwei);
		int shiwei = cardNum % 1000 % 100 / 10;
		System.out.println("十位数是:"+shiwei);
		int gewei = cardNum % 10;
		System.out.println("个位数是:"+gewei);
		System.out.println("会员卡号"+cardNum+"各位之和:"+qianwei+baiwei+shiwei+gewei);

3、关系运算符

== 等于 只用于基本数据类型比较,String类型数据不能使用==号判断是否相等,使用equals判断

int a = 10;
int b = 5;
System.out.println(a==b);

结果是false

String str1 = "asdfg";
String str2 = "asdfg";
System.out.println(str1.equals(str2));

结果为true

!= 不等于

int a = 10;
int b = 5;
System.out.println(a!=b);

结果为true

大于> 、小于< 、大于等于>= 、小于等于<=,这四个运算符只能用于数值类型判断

System.out.println(a>b);
System.out.println(a<b);
System.out.println(a>=b);
System.out.println(a<=b);

结果为true、false、true、false

4、逻辑运算符

&& 短路与 例如a>b && a>c当两边的表达式或者值都为true的时候,结果为true,有一项为false,结果为false;有短路功能,当发现一个表达式或者值为false的时候,不会再去执行计算其他表达式

& 与 基本判断与&&一致,但没有短路功能,即使一个表达式或者值判断为false,仍然会去执行计算其他表达式

		int a = 10;
		int b = 5;
		int c = 11;
		System.out.println(a<b && a<c);
		System.out.println(a<b & a<c);

结果为:false、false

|| 短路或 例如a>b || a>c 当两边的表达式或者值有任意一项为true,整体结果为true,否则为false;有短路功能,当发现一个表达式或者值为true的时候,不会再去执行计算其他表达式

| 或 基本判断与||一致,但是没有短路功能,即使一个表达式或者值判断为true,仍然会执行计算其他表达式

		System.out.println(a<c || a<b);
		System.out.println(a<c | a<b);

结果为:true、true

!非 取当前表达式或者值的判断结果相反的值

System.out.println(!(a<b));

结果为:true

5、三元(目)运算符

三元(目)运算符 例如c = a<b ? 1 : 2判断a<b是否为true,如果结果为true,得到返回数据1,此时c = 1,如果结果为false,得到返回数据2,此时c = 2

 		int a = 10;
		int b = 5;
		String c;
		System.out.println(c = a > b ? "结果为真": "结果为假" );

结果为:结果为真

例题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JXge54JN-1689128132284)(D:\java\练习\day2\day2练习1.png)]

package javaDay2;
	import java.util.*;
public class Sanmuyunsuanfulianxi {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入4位会员卡号:");
		int cardNum = input.nextInt();
		System.out.println("会员卡号是:");
		int qianwei = cardNum / 1000;
		System.out.println("千位数是:"+qianwei);
		int baiwei = cardNum % 1000 / 100;
		System.out.println("百位数是:"+baiwei);
		int shiwei = cardNum % 1000 % 100 / 10;
		System.out.println("十位数是:"+shiwei);
		int gewei = cardNum % 10;
		System.out.println("个位数是:"+gewei);
		int num =qianwei+baiwei+shiwei+gewei;
		System.out.println("会员卡各位数之和是:"+num);
		String i;
		System.out.println(i = num > 20 ? "恭喜您,中奖了!" : "很遗憾,您没中奖!");
		
	}
}

结果根据输入的值变化:

*请输入4位会员卡号:
1234
会员卡号是:
千位数是:1
百位数是:2
十位数是:3
个位数是:4
会员卡各位数之和是:10
很遗憾,您没中奖!

6、运算符优先级

①单目运算符包括 !++ --,优先级级别最高

②优先级别最低的是赋值运算符

③可以通过()控制表达式的运算顺序,()优先级最高

④从右向左结合性的只有赋值运算符、三目运算符和单目运算符

⑤算数运算符 > 关系运算符 > 逻辑运算符

第二章 选择结构与循环

一、if选择结构

1、选择结构if

if的条件必须是布尔值,或者表达式判断结果位true或者false,如果为true,就执行{}中的代码块

if(条件){

​ 执行代码块

}

例题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBtfRMbR-1689128132284)(D:\java\练习\day2\day2test6.png)]

package javaDay2;

import java.util.Scanner;

public class Ifliti1 {
	public static void main(String[] args) {
		//张三Java成绩大于90同时音乐成绩大于80或者Java成绩100同时音乐成绩大于70的时候奖励他
		Scanner input = new Scanner(System.in);
		System.out.println("请输入张三同学的Java成绩:");
		int javascore = input.nextInt();
		System.out.println("请输入张三同学的音乐成绩:");
		int musicscore = input.nextInt();
		if((javascore>90 && musicscore>80)|| (javascore==100 && musicscore>70)){
			System.out.println("奖励张三一部手机!");
		}
		
	}
}
注意:此题中对&&||的运用

结果为:

请输入张三同学的Java成绩:
92
请输入张三同学的音乐成绩:
89
奖励张三一部手机!

2、if-else结构

先判断if的判断条件,如果为true,执行if{}中的代码块A;如果判断条件为false,执行else{}中的代码块B

if(判断条件){

代码块A

}else{

代码块B

}

例题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I6RedVe2-1689128132285)(D:\java\练习\day2\day2test7.png)]

package javaDay2;

import java.util.Scanner;

public class Ifelseliti1 {
	public static void main(String[] args) {
		//先判断if的判断条件,如果为true,执行if{}中的代码块A;如果判断条件为false,执行else{}中的代码块B
		//*if(判断条件){
		//代码块A
		//}else{
			//代码块B
		//}
		//如果张三考试成绩大于90分,奖励他一部手机,否则就惩罚他蹲马步
		Scanner input = new Scanner(System.in);
		System.out.println("张三同学的成绩是:");
		int score = input.nextInt();
		if(score>90){
			System.out.println("奖励张三同学一部手机");
		}else{
			System.out.println("惩罚张三同学蹲马步");
		}
	}
}

结果为:

张三同学的成绩是:
99
奖励张三同学一部手机

3、if - else if - else结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ew61mLQV-1689128132285)(D:\java\练习\day2\day2练习2.png)]

package javaDay2;

import java.util.Scanner;

public class Ifelseifelseliti {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入参赛同学的姓名:");
		String studetName = input.next();
		System.out.println(studetName+"同学的成绩是:");
		int time = input.nextInt();
		if(time<10){
			System.out.println(studetName+"同学进入决赛了。");
			System.out.println("请输入"+studetName+"同学的性别:");
			String sex = input.next();
			if(sex.equals("男")){
				System.out.println(studetName+"同学进入男子组!");
			}else if(sex.equals("女")){
				System.out.println(studetName+"同学进入女子组!");
			}else{
				System.out.println("性别输入错误!");
			}
		}else{
			System.out.println(studetName+"你被淘汰了。");
		}
	}
}

结果为:

请输入参赛同学的姓名:
张三
张三同学的成绩是:
9
张三同学进入决赛了。
请输入张三同学的性别:

张三同学进入男子组!

4、小结

①基本if选择结构:可以处理单一或组合条件的情况

②if-else选择结构:可以处理简单的条件分支情况

③多重if选择结构:可以处理分段的条件分支情况

④嵌套if选择结构:可以处理复杂的条件分支情况

二、switch选择结构

多重分支并且条件判断是等值判断的情况

default相当于else 在所有项目都不匹配时,执行default中的代码块;default可以更改顺序,通常放在末尾,也可以省略

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-InAqM0Rz-1689128132286)(D:\java\练习\day2\Switch.png)]

package javaDay2;

import java.util.Scanner;

public class Switch1 {
	public static void main(String[] args) {
		//张三参加比赛根据拿到的名次获得奖励
		Scanner input = new Scanner(System.in);
		System.out.println("输入张三同学得到的名次:");
		int mingci = input.nextInt();
		switch(mingci){
		case 1:
			System.out.println("参加夏令营!");
			break;
		case 2:
			System.out.println("电脑一台");
			break;
		case 3:
			System.out.println("移动硬盘一个!");
			break;
		default :
			System.out.println("没有奖励");
			break;
		}
	}
}

三、switch与多重if的异同

1、相同点

都是用来处多分支条件的结构

2、不同点

switch选择结构只能处理等值条件判断的情况

多重if选择结构没有switch选择结构的限制,特别适合某个变量处于连续区间时的情况

四、循环

1、单循环

Ⅰ、while循环

定义循环变量int i = 1;

while(循环条件){

循环操作

控制循环语句

}

例题1:

package javaDay2;

import java.util.Scanner;

public class Whilexunhuan {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("检查是否合格?输入是/否:");
		String ispass = input.next();
		while(!"是".equals(ispass)){
			System.out.println("上午学习理论,下午上机编程!");
			System.out.println("检查是否合格?输入是/否:");
			ispass = input.next();
		}
	}
}

例题2:求100-999之间的所有水仙花数,并将其打印出来,计算一共有多少个。(水仙花数指此三位数的值等于各个位数的三次方和)

public static void main(String[] args) {
		int i = 100;
		int sum = 0;
		while(i<999){
			int a = i/100;
			int b = i%100/10;
			int c = i%10;
			int num =a*a*a+b*b*b+c*c*c;
			if(i==num){
				System.out.println(i+"是水仙花数");
				sum++;
			}
			i++;
		}
		System.out.println("一共有"+sum+"个水仙花数。");
	}
}

结果是:

370是水仙花数
371是水仙花数
407是水仙花数
一共有4个水仙花数。

Ⅱ、do-while循环

do-while循环先执行一次do{}中的代码块,然后再做while(条件判断);如果条件判断为true,继续循环执行{}中的代码,直到while(条件判断)中的条件为false结束循环

do{

执行代码块;

}while(循环条件);

例题1:编写一个程序测试题,然后检查是否合格,如果不合格则继续编写,知道合格为止。

public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		String isPass = "否";
		do{
			System.out.println("编写程序");
			System.out.println("检查是否完成?是/否:");
			isPass = input.next();
		}while(!"是".equals(isPass));
		System.out.println("完成了测试!");
	}
Ⅲ、for循环

for循环相比于while循环代码更简洁,并且循环变量再循环结束后会被释放;再循环次数确定的情况下推荐使用for循环

for(定义循环变量;条件判断;更新循环变量){

执行的代码块

}

例题:用for循环找出100-999之间的水仙花数并记录共有多少个。

public static void main(String[] args) {
		int sum = 0;
		for(int i = 100;i<999;i++){
			int a = i/100;
			int b = i%100/10;
			int c = i%10;
			int num =a*a*a+b*b*b+c*c*c;
			if(i == num){
				System.out.println(i+"是水仙花数");
				sum++;
			}
		}
		System.out.println("一共"+sum+"个水仙花数");
	}
}

结果同上

例题2:循环输入某同学的几门考试成绩,并计算平均分

package sarla;

import java.util.Scanner;

public class Forliti2 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入同学姓名:");
		String studentName = input.next();
		System.out.println("请输入考试总科目数量:");
		int num = input.nextInt();
		double sum =0.0;
		for(int i=1;i<=num;i++){
			System.out.println(studentName+"同学第"+i+"门课的成绩是:");
			int score =input.nextInt();
			sum+=score;
		}
		double avg =sum/num;
		System.out.println(studentName+"同学的"+num+"门课成绩平均分是:"+avg);
	}
}

例题3:

package sarla;

import java.util.Scanner;

public class Forliti3 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入一个值:");
		int num = input.nextInt();
		System.out.println("根据这个值可以输出以下加法表:");
		for (int i = 0, j = num; i <= num; i++, j--) {
			System.out.println(i + "+" + j + "=" + (i + j));
		}
	}
}

Ⅳ、三种循环综合练习

例题1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gXc3LQPw-1689128132287)(D:\java\练习\day3\单循环\三种循环综合练习1题目.png)]

package sarla;

public class Sanzhongxunhuan1 {
	public static void main(String[] args) {
		//使用while、do-while、for循环三种编程方式实现:计算100以内的偶数之和。
		//while循环
		int i = 0;
		int sum = 0;
		while(i<=100){
			if(i%2 == 0){
				sum+=i;
			}
			i++;
		}	
		System.out.println("100以内偶数和是:"+sum);
		//do-while循环
		int j = 0;
		int sum1 = 0;
		do{
			if(j%2 == 0){
				sum1+=j;
			}
			j++;
		}while(j<=100);
		System.out.println("100以内偶数和是:"+sum1);
		//for循环
		int sum2 = 0;
		for(int k=0;k<=100;k++){
			if(k%2 == 0){
				sum2+=k;
			}
			k++;
		}
		System.out.println("100以内偶数和是:"+sum2);
	}
}

结果为:

100以内偶数和是:2550
100以内偶数和是:2550
100以内偶数和是:2550

例题2:

public static void main(String[] args) {
		//while循环
		int i = 100;
		while(i>=5){
			i-=5;
			System.out.print(i+" ");
		}
		System.out.println();
		//do-whiel循环
		int j = 95;
		do{
			System.out.print(j+" ");
			j-=5;
		}while(j>=0);
		System.out.println();
		//for循环
		for(int k=95;k>=0;k-=5){
			System.out.print(k+" ");
		}
	}

结果为:

95 90 85 80 75 70 65 60 55 50 45 40 35 30 25 20 15 10 5 0
95 90 85 80 75 70 65 60 55 50 45 40 35 30 25 20 15 10 5 0
95 90 85 80 75 70 65 60 55 50 45 40 35 30 25 20 15 10 5 0

Ⅴ、总结

①执行顺序:

while循环:先判断,再执行;

do-while循环:先执行,再判断;

for循环:先判断,再执行

②适用情况:循环次数确定的情况下,通常选用for循环;循环次数不确定的情况,通常选用while或者do-while循环。

2、多重循环

多重循环的各循环可互相嵌套、一般不超过三层、外层循环变量变化一次,内层循环变量要变化一遍。

例题1:通过多重循环来打印矩形和三角形星阵。

public static void main(String[] args) {
		//打印一个4行6列的*号矩形
		for(int i=0;i<4;i++){
			for(int j=0;j<6;j++){
				System.out.print("*");
			}
			System.out.println();
		}
		System.out.println("-------------分割线--------------");
		//打印一个三角形*,下一行比上一行多颗,共5行
		//外层循环执行5次,每次输出一行
		for(int i=0;i<5;i++){
			//内层循环1 输出空格,执行次数从4-3-2-1-0
			for(int j=0;j<5-i-1;j++){
				System.out.print(" ");
			}
			//内层循环2 输出星号,执行次数从少到多1-2-3-4-5
			for(int k=0;k<i+1;k++){
				System.out.print("*");
			}
			System.out.println();
		}
		/*
		 * i=0       *    j<5-i-1 此时j=4即循环4次输出4个空格    k<1循环1次输出1个*
		 * i=1       *    j<5-i-1 此时j=3即循环4次输出3个空格    k<2循环2次输出2个*
		 * i=2       *    j<5-i-1 此时j=2即循环4次输出2个空格    k<3循环3次输出3个*
		 * i=3       *    j<5-i-1 此时j=1即循环4次输出1个空格    k<4循环4次输出4个*
		 * i=4       *    j<5-i-1 此时j<0即不输出空格                      k<5循环5次输出5个*
		 */
	}
}

例题2:利用多重循环打印九九乘法表

public static void main(String[] args) {
		//使用双重循环输出九九乘法表
		//外层循环控制被乘数
		for(int i=1;i<=9;i++){
			//内层循环控制乘数
			for(int j=1;j<=i;j++){
				System.out.print(i+"x"+j+"="+(i*j)+"\t");
			}
			System.out.println();
		}
	}

结果为:

1x1=1
2x1=2 2x2=4
3x1=3 3x2=6 3x3=9
4x1=4 4x2=8 4x3=12 4x4=16
5x1=5 5x2=10 5x3=15 5x4=20 5x5=25
6x1=6 6x2=12 6x3=18 6x4=24 6x5=30 6x6=36
7x1=7 7x2=14 7x3=21 7x4=28 7x5=35 7x6=42 7x7=49
8x1=8 8x2=16 8x3=24 8x4=32 8x5=40 8x6=48 8x7=56 8x8=64
9x1=9 9x2=18 9x3=27 9x4=36 9x5=45 9x6=54 9x7=63 9x8=72 9x9=81

例题3:输出数字金字塔,使用双重循环根据用户输入的数字,输出如下图形:

您想要几行数字?5

​ 1

​ 222

​ 33333

4444444

555555555

package sarla;

import java.util.Scanner;

public class Duochongxunhuan3 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("您想要几行数字?");
		int n = input.nextInt();
		//外层for循环控制输出的层数,内层佛如循环控制每层的个数
		for(int i=1;i<=n;i++){
			//控制输出的空格
			for(int k=n;k>i;k--){
					System.out.print(" ");
				}
			//控制输出的数字
			for(int j=0;j<i*2-1;j++){
				System.out.print(i);
			} 
			System.out.println();
		}
	}
}

例题4:

​ 1

​ 212

​ 32123

​ 431234

543212345

package sarla;

import java.util.Scanner;

public class Duochongxunhuan1 {
	public static void main(String[] args) {
		//编写倒数数字三角形
		Scanner input = new Scanner(System.in);
		System.out.println("请输入1-9之间的数字:");
		int count = input.nextInt();
		for(int i=1;i<=count;i++){
			for(int j=1;j<=count-i;j++){
				System.out.print(" ");
			}
			for(int k=i;k>=1;k--){
				System.out.print(k);
			}
			for(int n=2;n<=i;n++){
				System.out.print(n);
			}
			System.out.println();
		}
		/*i=1 --------1     j<=9-i j<=8 k=1 k>=1 k-- n=2 n<=i n<=1
		 *i=2 -------21     j<=9-i j<=7 k=2 k>=1 k-- n=2 n<=i n<=2
		 *i=3 ------321     j<=9-i j<=6 k=3 k>=1 k-- n=2 n<=i n<=3
		 */
	}
}

例题4:输入数字,根据数字指定行数,编写正序数字三角形

例题5:打印出1-100之间所有的质数,并统计个数

package sarla;

public class Zhishu {
	public static void main(String[] args) {
		//打印出1-100之间所有的质数,并统计个数
		int count = 0;
		//外层循环控制被除数
		for(int i=2;i<=100;i++){
			//内层循环控制除数,要去除1和他本身,通过控制j=2和j<i实现
			boolean flag = true;
			for(int j=2;j<i;j++){
				if(i%j == 0){
					flag = false;
					break;
				}
			}
			if(flag){
				System.out.println(i+"是质数");
				count++;
			}
		}
		System.out.println("一共有"+count+"个质数");
	}
}

例题6:从键盘输入一位整数,当输入1-7时,输出”星期一“-”星期日“,输入其他数字时,提示用户重新输入

package sarla;

import java.util.Scanner;

public class Duochongxunhuanxingqishu {
	public static void main(String[] args) {
		// 从键盘输入一位整数,当输入1-7时,输出”星期一“-”星期日“
		// 输入其他数字时,提示用户重新输入
		Scanner input = new Scanner(System.in);
		int num = 0;
		do {
			System.out.println("请输入一个1-7之间的数字,输入0退出程序:");
			num = input.nextInt();
			switch (num) {
			case 1:
				System.out.println("星期一");
				break;
			case 2:
				System.out.println("星期二");
				break;
			case 3:
				System.out.println("星期三");
				break;
			case 4:
				System.out.println("星期四");
				break;
			case 5:
				System.out.println("星期五");
				break;
			case 6:
				System.out.println("星期六");
				break;
			case 7:
				System.out.println("星期日");
				break;
			case 0:
				System.out.println("程序结束");
				break;
			default:
				System.out.println("输入错误,请重新输入");
				break;
			}
		} while (num != 0);
	}
}

五、流程控制语句

1、break语句

当break出现在循环体中的switch语句体内时,其作用只是跳出该switch语句体;当break出现在循环体中,但并不在switch语句体内是,则执行break后,退出本层循环体;在循环结构中,应用break语句使流程跳出本层循环体,从而提前结束本层循环。

例题1:

package sarla;

import java.util.Scanner;

public class Breaklianxi1 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入同学姓名:");
		String studentName = input.next();
		System.out.println("请输入考试总科目数量:");
		int num = input.nextInt();
		double sum = 0.0;
		// 方法一:定义变量用于判断是否继续计算平均分(推荐)
		boolean errFlag = true;
		for (int i = 1; i <= num; i++) {
			System.out.println(studentName + "同学第" + i + "门课的成绩是:");
			int score = input.nextInt();
			if (score < 0) {
				System.out.println("录入的成绩错误,程序停止运行。");
				errFlag = false;
				break;
			}
			sum += score;
			// 方法二:将计算平均分的代码块移入for中,并判断最后一次循环录入时才进行计算
			// if(i == num){
			// double avg = sum/num;
			// System.out.println(studentName+"同学的"+num+"门课成绩平均分是:"+avg);
			// }
			if (errFlag) {
				double avg = sum / num;
				System.out.println(studentName + "同学的" + num + "门课成绩平均分是:" + avg);
			}
		}
	}
}

例题2:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y5bPtqqK-1689128132287)(D:\java\练习\day3\多重循环\7break语句题目2.png)]

//1-10之间的整数相加得到累加值大于20的当前数
		//1、循环累加从1-10
		//2、每次循环累加是进行判断是否大于20
		//3、如果大于20跳出当前循环,并打印数据
		int sum = 0;
		for(int i=0;i<10;i++){
			sum+=i;
			if(sum>20){
				System.out.println("当前数值是:"+sum);
				System.out.println("加到数字"+i+"时超过20");
				break;
			}
		}

结果为:

当前数值是:21
加到数字6时超过20

2、continue语句

Continue语句,只能用在循环里,跳过循环体中剩余的语句而执行下一次循环,通常与条件语句一起使用,加速循环

例题1:循环输入学生成绩,统计分数大于等于80分的学生占比。

	//循环输入学生成绩,统计分数大于等于80分的学生占比
		//1、录入成绩
		//2、统计分数大于等于80分的人数
		//3、计算占比
		Scanner input = new Scanner(System.in);
		System.out.println("输入班级总人数:");
		int count = input.nextInt();
		
		int num = 0;//大于等于80分的人数
		for(int i=1;i<=count;i++){
			System.out.println("请输入第"+i+"位学生的成绩:");
			int score = input.nextInt();
			if(score<80){
				continue;//continue在循环中表示立即结束本次循环,开始下一次循环
			}
			num++;
		}
		System.out.println("分数大于等于80分的共有"+num+"位学生。");
		double rate = (double)num/count;
		System.out.println("80分以上的学生占比为:"+(rate*100)+"%");

例题2:求1-10之间的所有偶数和;

/*求1-10之间的所有偶数和
		 * 1、使用循环进行累加,循环的范围是从1-10
		 * 2、判断当前数是否为偶数
		 * 3、如果为奇数跳过,执行下一个循环,如果为偶数,进行累加
		 */
		int sum = 0;
		for(int i=1;i<=10;i++){
			if(i%2 !=0){
				continue;
			}
			sum+=i;
		}
		System.out.println("1-10之间所有的偶数和是:"+sum);

结果为:1-10之间所有的偶数和是:30

3、break语句与continue语句的比较

①使用场合:break常用于switch结构和循环结构中;continue一般用于循环结构中

②作用(循环结构中):break语句终止某个循环,程序跳转到循环块外的下一条语句;continue跳出本次循环,进入下一次循环;双重循环也是如此

return:结束当前方法的执行并退出,返回调用该方法的语句处

六、debug程序调试

1、相关概念

可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug。

程序调试:设置断点、单步运行、观察变量

2、使用方法

使用方法:①在行号的右边鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)

​ ②右键,选择debug执行程序;

​ ③程序就会停留在添加的第一个断点处

执行程序:f8:逐行执行程序;

​ f7:进入到方法中;

​ shift+f8:跳出方法;

​ f9:跳到下一个断点,如果没有下一个断点,那么就结束程序;

​ ctrl+f2:退出debug模式,停止程序;

​ console:切换到控制台

第三章 数组

一、一维数组及经典应用

数组是一个变量,存储相同数据类型的一组数据

声明一个变量就是在内存空间划出一块合适的空间

声明一个数组就是在内存空间划出一串连续的空间

基本要素:标识符、数组元素、元素下标:从0开始、元素类型

创建数组的步骤:

1、声明数组 数组数据类型[]数组名称 int [] arrayInt;

2、分配数组空间 数组名称=new 数组类型[分配的长度] arrayInt = new int[5];

3、给数组中的每个元素赋值 数组名称[下标] = 要赋的值 arrayInt[0] = 10; arrayInt[1] = 20;

4、使用数据 可以直接通过 数组名称[下标]的方式取出其中的数据 System.out.println(arrayInt[0]);

还可以这样写:int arr[] = {1,2,3,4,5};

8个基本数据类型的初始值:

整型的初始值全是0;

浮点型的初始值是0.0;

char类型的初始值是“ ”空字符;

Boolean类型的初始值是false;

引用数据类型的初始值是null.

两种赋值方式

第一种方式:可以在声明数组的同时进行赋值,例如int[] arrayInt = {10,20,30,40,50};
第二种方式:使用循环动态录入数据,分配的数组空间长度是多少,就循环多少次

例题1:键盘录入一组数据,并求这组数据的总和。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yGCbbCdY-1689128132288)(D:\java\练习\day4\数组\一维数组及其应用\例题及代码.png)]

package yiweishuzu;

import java.util.Scanner;

public class Yiweishuzi1 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("输入要定义的数组长度:");
		int len=input.nextInt();
		int arr[] = new int[len];
		for(int i =0;i<arr.length;i++){
			System.out.println("录入第"+(i+1)+"个数据:");
			arr[i] = input.nextInt();
		}
		int sum = 0;
		for(int j:arr){
			sum+=j;
		}
		System.out.println("数组中所有数据和是:"+sum);
	}
}

例题2:键盘输入班级所有人的考试成绩,并计算班级平均分。

package yiweishuzu;

import java.util.Scanner;

public class Yiweishuzu2 {
	public static void main(String[] args) {
		//1、键盘输入班级人数
		//2、循环输入学生成绩
		//3、计算平均分
		Scanner input = new Scanner(System.in);
		System.out.println("请输入班级人数:");
		int count = input.nextInt();
		int arr[] = new int[count];
		for(int i = 0;i<arr.length;i++){
			System.out.println("第"+(i+1)+"位学生的成绩是:");
			arr[i] = input.nextInt();
		}
		int sum = 0;
		for(int j:arr){
			sum+=j;
		}
		double avg = 0.0;
		avg	= (double)sum/count;
		System.out.println(count+"位学生成绩的平均分是:"+avg);
	}
}

栈内存与堆内存int arr[] = new arr[2];arr[0]=1;arr[2];

栈内存:存放的是局部变量,如:arr[]

堆内存:存放的是所有new出来的东西,如:其中的1

栈内存堆内存
arr1
arr2

数组对象重新赋值,那么数组对象重新指向新的数组地址,引用的旧数组变成垃圾,等待垃圾回收机制回收。

例题3:有一个数列:8、4、2、1、23、344、12,键盘中任意输入一个数据,判断数列中是否包含此数。

package Yiweishuzu;

import java.util.Scanner;

public class Liti1 {
	public static void main(String[] args) {
		// 创建数组并赋值
		int arr[] = { 8, 4, 2, 1, 23, 344, 12 };
		// 定义数组数据的和变量
		int sum = 0;
		System.out.print("这个数列是:");
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + "、");
		}
		System.out.println();
		for (int j : arr) {
			sum += j;
		}
		System.out.println("数列中所有值的和是:" + sum);
		Scanner input = new Scanner(System.in);
		System.out.println("请输入任意数字:");
		int num = input.nextInt();
		// 定义一个布尔变量,判断是否猜中
		boolean flag = false;
		for (int i : arr) {
			if (i == num) {
				flag = true;// 相等就是猜中了
				break;
			}
		}
		if (flag) {
			System.out.println("恭喜竞猜成功!");
		} else {
			System.out.println("很遗憾竞猜失败!");
		}
	}
}

例题4:键盘输入本次Java考试五位学生的成绩,求成绩最高分和最低分。

package Yiweishuzu;

import java.util.Scanner;

public class Liti2 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入Java考试人数:");
		int num = input.nextInt();
		int arr[] = new  int[num];
		int max = arr[0];//记录最大值要定义在循环外面
		int min = arr[0];
		for(int i=0;i<arr.length;i++){
			System.out.println("第"+(i+1)+"位同学的成绩是:");
			arr[i] = input.nextInt();
			//当i为0时,代表是第一次录入成绩,将录入的值直接赋值给max记录最大值
			if(i==0){
				max = arr[i];
				min = arr[i];
			}
			//当i大于0时,代表不是第一次录入成绩了
			//将录入的值和max对比,如果max小于新录入的数据,那么就用新录入的数据赋值给max
			if(i>0){
				if(max<arr[i]){
					max = arr[i];
				}
				if(min>arr[i]){
					min = arr[i];
				}
			}
		}	
		System.out.println("java考试最高分是:"+max);
		System.out.println("java考试最低分是:"+min);
		
		}
	
}

例题5:求出4家店手机的最高价和最低价。

package Yiweishuzu;

import java.util.Scanner;

public class Liti3 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入4家店的手机价格:");
		//1、定义数组存储手机价格,并利用循环输入
		int price[] = new int[4];
		for(int i=0;i<price.length;i++){
			System.out.println("第"+(i+1)+"家店的手机价格是:");
			price[i] = input.nextInt();
		}
		//2、定义变量max和min保存当前最高最低价
		int max = price[0];
		int min = price[0];
		//3、将max和min与数组中的其他元素一次比较
		for(int j:price){//此处的j不是循环变量,相当于price数组里的每一项
			if(j>max){
				max = j;
			}
			if(j<min){
				min = j;
				}
		}
		System.out.println("最高价格是:"+max);
		System.out.println("最高价格是:"+min);
	}
}

二、排序

1、插入排序

步骤:①输入数据,②循环对比找到数据应该放在什么位置没记录位置下标,③将记录的下标后面的数据全部往后移动一位④将录入的数据插入到数组指定位置

例题1:有一组学员的成绩{99,85,82,63,60},将它们降序排列。要增加一个学员的成绩,将它插入成绩序列,并保持降序。

package paixu;

import java.util.Scanner;

public class Paixu1 {
	public static void main(String[] args) {
		//1、创建数组
		int arr[] = new int[6];
		arr[0] = 99;
		arr[1] = 85;
		arr[2] = 82;
		arr[3] = 63;
		arr[4] = 60;
		//2、定义变量用于保存新增成绩要插入的下标
		int index = arr.length-1;
		//3、输入要新增的成绩
		Scanner input = new Scanner(System.in);
		System.out.println("请输入新增成绩:");
		int num = input.nextInt();
		//4、找出新成绩的插入下标
		for(int i=0;i<arr.length;i++){
			if(num>arr[i]){
				index = i;
				break;
			}
		}
		System.out.println("新成绩要插入的下标位置是:"+index);
		//5、将新成绩要插入的下标位置的数据以及下标之后的数据,依次往后移
		for(int j=arr.length-1;j>index;j--){
			arr[j] = arr[j-1];
		}
		//6、插入数据
		arr[index]= num;
		System.out.println("插入新成绩后的成绩列表是:");
		for(int k:arr){
			System.out.print(k+" ");
		}
	}
}

例题2:数组存储5笔购物金额,在控制台输出并计算总金额

package paixu;

import java.util.Scanner;

public class Paixu2 {
	public static void main(String[] args) {
		double arr[]= new double[5];
		System.out.println("请输入会员本月的消费记录:");
		for(int i=0;i<arr.length;i++){
			Scanner input = new Scanner(System.in);
			System.out.print("请输入第"+(i+1)+"笔购物金额:");
			arr[i] = input.nextDouble();
		}
		System.out.println();
		System.out.println("序号"+"\t"+"\t"+"金额(元)");
		double sum = 0.0;
		for(int j=0;j<arr.length;j++){
			System.out.println((j+1)+"\t\t"+arr[j]);
			sum+=arr[j];
		}
		System.out.println("总金额\t\t"+sum);
	}
}

2、冒泡排序

每次比较相邻的两个数,小的交换到前面,每轮结束后最大的数交换到最后。

用二重循环将5个数字升序排序

5个数字如何存放:数字,数组.length=5:

控制比较多少轮:外层循环,循环变量i;5-1轮

控制每轮比较多少次:内层循环,循环变量:j 5-i-1次

交换数据

例题1:使用冒泡排序对输入的5名学员成绩进行降序排列

package paixu;

import java.util.Scanner;

public class Maopao1 {
	public static void main(String[] args) {
		System.out.println("请输入5名学员的成绩:");
		Scanner input = new Scanner(System.in); 
		int arr[] = new int[5];
		for(int i=0;i<arr.length;i++){
			arr[i] = input.nextInt();
		}
		//需进行length-1次冒泡,即外层循环5-1轮
		for(int i=0;i<arr.length-1;i++){
			//内层循环5-i-i次
			for(int j=0;j<arr.length-1-i;j++){
				//判断大小
				if(arr[j]<arr[j+1]){
					//定义一个变量用于调整位置
					int a = arr[j];
					//根据数值大小调整在数组中的位置
					arr[j] = arr[j+1];
					arr[j+1] = a;
				}
			}
		}
		System.out.println("学员成绩按降序排列:");
		//循环输出排序后的数据
		for(int i = 0;i<arr.length;i++){
			System.out.print(arr[i]+" ");
		}
	}
}
package sort;

import java.util.Arrays;

public class BubbleSort {
    /*
        冒泡排序:响铃两个数进行比较,如果第一个比第二个大,交换他们
     */
    public static void main(String[] args) {
        int[] arr = {22,55,44,33,11};

        //外循环:比较的轮数
        for (int i = 0; i < arr.length-1;i++) {
            //内循环:比较的次数
            //-1:避免索引越界
            //-i:提高效率
            for (int j = 0; j < arr.length-1-i; j++) {
                if(arr[j]>arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}

3、Arrays类

Arrays 是在java.util包提供的一个工具类;提供操作数组的方法,如排序、查询等。

排序方法:

Arrays.sort(数组名),将指定的数组按照升序排序

比较方法:

Arrays.equals(数组1,数组2),判断数组1 和数组2 的数据是否一致;==比较的是两个数组在内存中的地址是否一致;数组本身也有equals方法,数组1 .equals(数组2) 数组本身的equals对比的也是两个数组的地址;如果需要对比两个数组中的数据内容是否一致,需要使用Arraya工具类的equals

转换字符串:

Arrays.toString(数组名) ,将指定的数组转换字符串格式

赋值替换:

Arrays.fall(数组名,数据值A),将数组中的所有数据都赋值替换为指定的数据值

复制数组:

Array.copyOf(要复制的数组名,复制的长度)将指定的数组复制成一个新数组,新数组的长度为方法中指定的长度

查询数组中数据的下标位置:

Arrays.binarySearch(数组名,要查找的内容),在数组中查询指定数据的下标位置,由于binarySearch使用的是二分查找法,被查询的数组一定要是升序排列的,否则查询的结果可能不准确·

package 数组;

import java.util.Arrays;

public class Arrays{
	public static void main(String[] args) {
		
		//排序方法  Arrays.sort(数组名)将指定的数组按照升序排列
		int arr1[] = {9,5,8,6,1};
		//for(int i:arr1){
			Arrays.sort(arr1);
			//System.out.print(i+" ");
		//}
		System.out.println(Arrays.toString(arr1));
		
		//比较方法  Arrays.equals(数组1,数组2)判断数组1和数组2的数据是否一致
		//==比较的是两个数组在内存中的地址是否一致
		//数组本身也有equals方法 数组1.equals(数组2) 数组本身的equals和==一样对比的也是两个数组的地址
		//如果需要对比两个数组中的数据内容是否一致,需要使用Arrays工具类的equals
		int arr2[] = {1,2,3,4,5};
		int arr3[] = {1,2,3,4,5};
		System.out.println(arr2==arr3);
		System.out.println(arr2.equals(arr3));
		System.out.println(Arrays.equals(arr2, arr3));
		
		//转换字符串  Arrays.toString(数组名)将指定的数组转换字符串格式
		int arr4[] = {1,2,3,4,5};
		String arr4Str = Arrays.toString(arr4);
		System.out.println(arr4Str);
		
		//赋值替换 Arrays.fill(数组名,数据值) 将指定的数组中的所有数据都赋值替换为指定的数据值
		int arr5[] = {1,2,3,4,5};
		System.out.println("赋值替换前:"+Arrays.toString(arr5));
		Arrays.fill(arr5,9);
		System.out.println("赋值替换后:"+Arrays.toString(arr5));
		
		//复制数组 Arrays.copyOf(要复制的数组名,复制的长度) 将指定的数组复制成一个新数组,新数组的长度为方法中指定的复制长度
		int arr6[] = {1,2,3,4,5};
		System.out.println("数组6:"+Arrays.toString(arr6));
		int arr7[] = Arrays.copyOf(arr6, 3);
		System.out.println("数组7:"+Arrays.toString(arr7));
		
		//查询数组中的数据的下标位置 Aeeays.binarySearch(数组名,要查找的内容),在数组中查询指定数据的下标位置
		//由于binorySearch 使用的是二分查找法,被查询的数组一定要是升序排序的,否则结果不准确
		int arr8[] = {1,2,3,4,5};
		System.out.println(Arrays.binarySearch(arr8,4));
	}
}

结果为:

[1, 5, 6, 8, 9]
false
false
true
[1, 2, 3, 4, 5]
赋值替换前:[1, 2, 3, 4, 5]
赋值替换后:[9, 9, 9, 9, 9]
数组6:[1, 2, 3, 4, 5]
数组7:[1, 2, 3]
3

例题1:Arrays类排列字符,使用Arrays类升序排列一组字符,并查找某个特殊字符在升序后数组中的位置。

package 数组;

import java.util.Arrays;
import java.util.Scanner;

public class Arrays类例题1 {
	public static void main(String[] args) {
		char chr1[] = {'a','b','我','d','e','s'};
		System.out.println("排序前:"+Arrays.toString(chr1));
		//排序字符数组
		Arrays.sort(chr1);
		System.out.println("排序后:"+Arrays.toString(chr1));
		//查看字符数组中的每个字符的编码
		for(char c:chr1){
			System.out.print((int)c+" ");
		}
		//查询指定字符的位置
		System.out.println();
		Scanner input = new Scanner(System.in);
		System.out.println("请输入查询的字符");
		//字符串的charAt(下标)方法用于将该字符指定下标位置的数据,转换为字符类型(char),下标从0开始
		char selectChar = input.next().charAt(0);
		System.out.println("字符"+selectChar+"在数组中的下标是:"+Arrays.binarySearch(chr1,selectChar));
	}
}

结果为:

排序前:[a, b, 我, d, e, s]
排序后:[a, b, d, e, s, 我]
97 98 100 101 115 25105
请输入查询的字符
e
字符e在数组中的下标是:3

4、选择排序

从0索引开始,拿着每一个索引上的元素跟后面的元素一次比较

package sort;

import java.util.Arrays;

public class SearchSort {
    public static void main(String[] args) {
        int[] arr = {22,11,44,33,55};

        for (int i = 0; i < arr.length-1; i++) {

            for (int j = i+1; j < arr.length; j++) {
                if(arr[i]<arr[j]){
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j]= temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}

5、二分查找(折半查找)

前提:数组必须是排好序的

思路:1、定义两个变量记录最小索引,和最大索引

​ 2、折半的动作不止一次,应该使用循环条件while(min<=max)

​ 3、循环中计算出中间索引

​ 4、加入判断:元素如果大于中间元素:min = min +1

​ 元素如果小于中间元素:max = mid-1

​ 元素如果等于中间元素:将索引返回(mid)

package sort;

import static java.util.Arrays.binarySearch;

public class BinarySearch {

    public static void main(String[] args) {
        int [] arr = {11,22,33,44,55,66,77,88,99};
        int index = binarySearch(arr,33);
        System.out.println(index);
    }
    private static int binarySearch(int[] arr,int num){
        //1、定义两个变量记录最小索引,和最大索引
        int min = 0;
        int max = arr.length-1;
        int mid;

        //2、折半的动作不止一次,应该使用循环条件while(min<=max)
        while(min<=max){
            //3、计算中间索引
            mid = (min+max)/2;
            // 4、加入判断
            if(num>arr[mid]){
                min = mid+1;
            }else if(num<arr[mid]){
                max = mid-1;
            }else{
                return mid;
            }
        }
        //5、没找到,返回-1
        return -1;
    }
}

三、多维数组

创建步骤:数据类型 二维数组名 【】【】= new 数组类型【数组维数】【】;

二维数组的本质就是一个存储了数组的一维数组;

设置数组参数,实际上就是设置这个一维数组里可以放几个一维数组。

package 二维数组;

import java.util.Arrays;

public class E二维数组 {
	public static void main(String[] args) {
		//定义二维数组
		//数据类型 二维数组名[][]=new 数据类型[维数][];
		//二维数组的本质,就是一个存储了数组的一维数组
		//设置数组的参数,实际上就是设置这个一维数组里可以放几个一维数组
		int arr1[][] = new int[4][5];
		System.out.println(arr1.length);
		System.out.println(arr1[0].length);
		for(int i=0;i<arr1.length;i++){
			for(int j=0;j<arr1[i].length;j++){
				arr1[i][j] = i;
			}
		}
		for(int[] arrNum:arr1){
			System.out.println(Arrays.toString(arrNum));
		}
	}
}

例题1:有5个班各5名学生Java课的考试成绩,计算这5个班各自的总成绩。

package 二维数组;

public class E二维数组例题1 {
	public static void main(String[] args) {
		//有5个班,各5名同学Java考试成绩,计算这5个班各自的总成绩
		int arr[][] = {{1,2,3,4,5,},{6,7,8,9,10},{11,12,13,14,15,},{16,17,18,19,20},{21,22,23,24,25}};
		System.out.println("成绩统计");
		for(int i=0;i<arr.length;i++){
			int sum = 0;//外层循环每次一个班,所以每个班在开始计算前都会将sum值归0
			for(int j=0;j<arr[i].length;j++){
				sum+=arr[i][j];
			}
			System.out.println("第"+(i+1)+"的总成绩是:"+sum);
		}
	}
}

结果为:

成绩统计
第1的总成绩是:15
第2的总成绩是:40
第3的总成绩是:65
第4的总成绩是:90
第5的总成绩是:115

例题2:显示班级学生总成绩:已知有3个班级各5名学员,请使用二维数组计算各个班级的总成绩。

package 二维数组;
import java.util.Scanner;
public class E二维数组例题2 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		//定义二维数组,记录三个班成绩
		int arr[][] = new int[3][5];
		//i班级,j各个班级的学生
		for(int i=0;i<arr.length;i++){
			System.out.println("**********第"+(i+1)+"个班***********");
			for(int j=0;j<arr[i].length;j++){
				System.out.println("请输入第"+(j+1)+"个学生的成绩:");
				arr[i][j] = input.nextInt();
			}
		}
		System.out.println("************成绩统计*************");
		for(int k=0;k<arr.length;k++){
			int sum = 0;//保存总成绩
			for(int s:arr[k]){
				sum+=s;
			}
			System.out.println("第"+(k+1)+"个班的总成绩是:"+sum);
		}
	}
}

小结

break用法:break一共三种用法,一种是Switch里,一种是for循环里,还有一种:标号

“:”前可以加任意自己喜欢的字母,如lo:当while前面写了一个lo:,后面break用到了这个lo,就表示跳出到标记了lo的这一层代码块,不是只跳出一层了

综合例题1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FvHy1qRn-1689128132289)(D:\java\练习\day6\多维数组\综合题目.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vsAa53XU-1689128132289)(D:\java\练习\day6\多维数组\字符串类型转换.png)]

第四章 类和对象

开发方法

结构化开发

①面向功能划分软件结构

②自顶向下

③最小的子系统是方法

④制约了软件的可维护性和可拓展性

面向对象开发

①把软件系统看成各种对象的集合

②系统结构较稳定

③子系统相对独立

④软件可重用性、可维护性和可拓展性强

一、对象

用来描述客观事物的一个实体,由一组属性和方法构成

对象的特征–属性

属性:对象具有的各种特征,每个对象的每个属性都拥有特定值

对象的特征–方法

方法:对象执行的操作

创建使用对象的步骤

创建对象:类名 对象名 = new 类名();如:School center = new School();

引用对象成员:使用"."进行以下操作

​ 引用类的属性:对象名.属性

​ 引用类的方法:对象名.方法名()

center.name = “第一中学”; //给name属性赋值

center.showCenter(); //调用showCenter()方法

二、类

具有相同属性和方法的一组对象的集合,类是对象的抽象(模板),对象是类的具体(实例)

类的方法定义类的某种行为。

定义方法:

public 返回值类型 方法名(){

​ 这里编写方法的主体

}

方法的返回值:

①如果方法具有返回值,方法中必须使用关键字return返回该值,返回值类型为该值的类型;返回值只能有一个

②如果方法没有返回值,返回值类型为void

创建方法

例题1:创建学员类和教师类,输出相关信息

学员类:

package 类和对象;
/*
 * 学员类
 */
public class Student {
	//姓名
	String name;
	//年龄
	int age = 0;
	//班级
	String className;
	//爱好
	String hobby;
	
	//展示信息方法
	public void show(){
		System.out.println(name+"\n年龄是"+age+"\n班级是:"+className+"\n爱好是:"+hobby);
	}
}

教师类:

package 类和对象;
/*
 * 教师类
 */
public class Teacher {
	//姓名
	String name;
	//专业
	String major;
	//课程
	String course;
	//工龄
	int seniority;
	
	//展示信息
	public void show(){
		System.out.println(name+"\n专业是:"+major+"\n课程是:"+course+"\n工龄是:"+seniority);
	}
}

具体对象输出信息;

package 类和对象;

public class ST示例 {
	public static void main(String[] args) {
		//编写学员类,输出学员相关信息
		Student stu1 = new Student();
		stu1.name = "sarla";
		stu1.age = 27;
		stu1.className = "java5班";
		stu1.hobby = "movies";
		stu1.show();
		
		System.out.println("--------------------------");
		//编写教师类,输出教师的相关信息
		Teacher t1 = new Teacher();
		t1.name = "zhou";
		t1.major = "java";
		t1.course = "java课";
		t1.seniority = 10;
		t1.show();
	}
}

例题2:一个景区根据有人的年龄收取不同价格的门票。请编写游人类,根据年龄段决定能购买的门票价格并输出。

定义一个游人类:

package 类和对象;

public class Visitor {
	//属性:姓名,年龄
	String name;
	int age = 0;
	//一个景区根据游人的两年零收取不同价格的门票。请编写游人类,根据年龄段决定能购买的门票价格并输出
	//方法:购票 根据不同年龄计算门票价格
	//年龄大于等于60,10元;小于等于10,0元;其他的全票
	public int getPrice(){
		if(age >=60){
			return 10;
		}else if(age<=10){
			return 0;
		}else{
			return 20;
		}
	}
}

具体实现:

package 类和对象;

import java.util.Scanner;

public class TestVisitor {
	public static void main(String[] args) {
		//循环输入姓名 年龄 获得应付的门票价格
		Scanner input = new Scanner(System.in);
		String flag = "Y";
		do{
			Visitor v1 = new Visitor();
			System.out.println("请输入姓名:");
			v1.name = input.next();
			System.out.println("请输入年龄:");
			v1.age = input.nextInt();
			//调用计算门票价格的方法,返回应付的门票价格
			int price = v1.getPrice();
			System.out.println(v1.name+"的年龄是"+v1.age+",门票的价格是:"+price);
			System.out.println("请输入是否继续Y/N?");
			flag = input.next();
		}while(!flag.equals("N"));
		System.out.println("程序结束!");
	}
}

例题3:模拟实现用户密码管理:输入旧的用户名和密码,如果正确,有权限更新;从键盘获取新的密码,进行更新。

方法一:

package 类和对象;

public class User {
	//属性:用户名,密码
	String userName = "admin";
	String passWord = "123456";
}
package 类和对象;

import java.util.Scanner;

public class Password {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		User users = new User();
		
		System.out.println("请输入用户名:");
		String username = input.next();
		System.out.println("请输入旧密码:");
		String passWord = input.next();
		
		//判断用户名密码是否正确
		//暂时将判断用户名密码是否一致的功能和修改密码的功能写在main方法,在学习参数传递之后,将这两个方法移入到User类中
		if(username.equals(users.userName) && passWord.equals(users.passWord)){
			System.out.println("请输入新密码:");
			users.passWord = input.next();
			System.out.println("修改成功,新密码为:"+users.passWord);
		}else{
			System.out.println("用户名和密码不匹配,没有权限更新管理员信息");
		}
	}
}

方法二:

package 类和对象;

public class User2 {
	    //属性:用户名,密码
		String userName = "admin";
		String passWord = "123456";
		
	    //方法:判断旧用户名密码是否正确
		public boolean login(String name,String pass){
			if(name.equals(userName)&&pass.equals(passWord)){
				return true;
			}else{
				return false;
			}
		}
		//方法:修改密码,并展示新密码
		public void updatePass(String pass){
			passWord = pass;
			System.out.println("修改成功,新密码是:"+passWord);
		}
}
package 类和对象;

import java.util.Scanner;

public class TestUsers2 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		User2 users2 = new User2();
		
		System.out.println("请输入用户名:");
		String username = input.next();
		System.out.println("请输入旧密码:");
		String passWord = input.next();
		
		/*判断用户名密码是否正确
		 * 调用user2中的带参方法,将输入用户名和密码当作参数传递过去,进行判断
		 * 返回True或false 根据返回值的内容来判断登录是否成功
		 * 
		 */
		boolean flag = users2.login(username, passWord);
		if(flag){
			//修改用户密码
			//调用user2中的带参方法,将输入的新密码当作参数传递过去,给属性password赋值
			System.out.println("请输入密码:");
			String newPass = input.next();
			users2.updatePass(newPass);
		}else{
			System.out.println("用户名和密码不匹配,没有权限更新管理员信息");
		}
	}
	
}

第五章 方法与方法重载

一、使用带参数的方法

什么叫方法:完成某一特定功能的代码块。把一些重复代码进行抽取

形参:定义方法时()中的参数

实参:在调用该方法时传递的参数

定义带参数的方法:<访问修饰符> 返回类型 <方法名>(<形式参数列表>){

​ //方法的主体

​ return;

}

**调用带参数的方法:**对象名.方法名(参数1,参数2,参数3,…参数n)//实参

方法传参

基本数据类型和引用数据类型在传参时的区别:基本数据类型,操作传递的时变量的值改变一个变量的值不会影响另一个变量的值。引用数据类型(类、数组和接口),赋值是把原对象的引用(可理解为内存地址)传递给另一个引用。

调用带参数的方法

对象名.方法名(实参列表);

调用带参方法传递的实参列表要和方法定义的形参列表从数量到数据类型都一一对应;

实参:实际参数,调用方法时根据形参列表传递的实际数据参数

多个参数之间用逗号隔开

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-amOLtgWm-1689128132290)(D:\java\练习\day7\方法和方法的重载\0620day7例1.png)]

方法:

package 方法与方法重载;

public class Juicer {
	//方法:榨果汁 String fruit = "西瓜";
	
	//定义带参数的方法
	/*权限修饰符 返回值类型 方法名(形参列表){
	 * 		执行代码
	 * }
	 * 形参:形式参数,在方法名后面()中声明,声明调用方法需要传递的参数形式
	 * 多个参数之间用逗号隔开
	 *String fruit = "西瓜";int count = 8;
	 */
	public String juicing(String fruit,int count){
		return fruit+"汁"+count+"杯。";
	}
}

重载:

package 方法与方法重载;

public class TesJuicer {
	public static void main(String[] args) {
		Juicer juicer = new Juicer();
		//调用带参数的方法
		/*对象名.方法名(实参列表)
		 * 调用带参方法传递的实参列表要和方法定义的形参列表从数量到数据类型都一一对应;
		 * 实参:实际参数,调用方法时根据形参列表传递的实际数据参数
		 * 多个参数之间用逗号隔开
		 */ 
		String user = juicer.juicing("西瓜",8);
		System.out.println(user);
	}
}

例题1:使用带参方法实现学员信息管理。在保存了多个学生姓名的数组中,指定查找区间,查找某个学生姓名并显示是否成功。

定义类:

package 方法与方法重载;

import java.util.Scanner;

package 方法与方法重载;

public class Student {
	//在保存了多个学生姓名的数组中,指定查找区间,查找某个学生姓名并显示是否查找成功
	//属性:学生姓名数组
	String names[]={"赵","钱","孙","李","周"};
	//在指定的区间查找学生姓名
	//start 开始位置 end 结束位置 name 要查找的姓名
	public boolean selectName(int start,int end,String name){
		//指定数组区间查找姓名
		for(int i=0;i<names.length;i++){
			if(names[i].equals(name)){
				return true;
			}
		}
		return false;
	}
}

调用执行:

package 方法与方法重载;

import java.util.Scanner;

public class TestStudent {
	public static void main(String[] args) {
		Scanner input =new Scanner(System.in);
		System.out.println("请输入查询开始的位置:");
		int start = input.nextInt();
		System.out.println("请输入查询结束的位置:");
		int end = input.nextInt();
		System.out.println("请输入查询的姓名:");
		String name = input.next();
		
		Student user = new Student();
		if(user.selectName(start, end, name)){
			System.out.println("查询成功!");
		}else{
			System.out.println("查无此人!");
		}
	}
}

二、值传递和引用传递

值传递:基本数据类型的参数传递时值传递,是指在调用方法时,将实参变量数据复制一份传递到方法的形参中,这样在方法内改变形参数据,不会影响到实参变量。

引用传递:引用数据类型的参数传递时引用传递,是指在调用方法时,将实参变量的地址直接传递到方法的形参中,实参变量和形参变量都指向了同一个内存地址,这样在方法内修改形参变量的数据会影响实参变量。

栈:先进后出,用来存放变量

堆:随用随取,用来存放对象

三、方法的重载

1、方法的重载

定义:在同一个类中,方法名相同参数列表不同的方法(指的是参数类型不同和个数不同,与变量名无关),与返回值无关,相互之间称之为方法的重载

可以这样理解:购物付款时可以通过现金、线上支付、刷信用卡等方式实现,付款(pay)

就是方法,现金(cash)、线上支付(online)、信用卡(card)就是不同的参数列表,即public boolean pay(double cash);public boolean pay(double online)…

参数列表:个数不同,数据类型不同,顺序不同

重载方法调用:JVM(Java虚拟机)通过方法的参数列表,调用不同方法

意义:可以通过调用同一个方法来实现传递不同的参数,从而实现不同的功能。

例题1:简易计算器;实现简易计算器,分别实现两个整数、三个浮点数的加法运算。

package 方法的重载;
/*
 * 计算器
 */
public class Computer {
	//分别实现两个整数、三个浮点数的加法运算
	public void add(int num1,int num2){
		int num = num1+num2;
		System.out.println(num1+"+"+num2+"="+num);
	}
	
	public void add(double num1,double num2,double num3){
		double num = num1 + num2 + num3;
		System.out.println(num1+"+"+num2+"+"+num3+"="+num);
	}
}
package 方法的重载;

public class TestComputer {
	public static void main(String[] args) {
		int num1 = 12;
		int num2 = 13;
		Computer com = new Computer();
		com.add(num1,num2);
		double aa = 1.1;
		double bb = 2.2;
		double cc = 3.3;
		com.add(aa,bb,cc);
	}
}

结果为:

12+13=25
1.1+2.2+3.3=6.6

2、构造方法

实例化一个对象后,如果需要给属性赋值,需要通过访问对象的属性或setxxx()方法。在实例化对象后同时给对象的属性赋值可以使用构造方法,构造方法又叫构造函数,用来对对象进行初始化,是在类中定义的方法。

public Student(){

}

1、方法名和类名相同

2、没有返回值

3、在方法中不能使用return语句返回一个值

4、构造方法一般定义为public

5、构造方法是在创建对象时被调用,使用new运算符调用构造方法

6、若自己不写构造方法,编译器会默认给一个无参构造,若自己写了将不再默认提供无参构造

7、构造方法可以重载

创建方法:

权限修饰词 构造方法名(参数列表){//方法名与类名相同

​ //初始化代码

}

作用:对象初始化,系统提供默认无参构造方法

没有参数的称为空参构造器

注意:1、如果提供非空参数的构造器,那么默认的空参数的构造器就失效了,如果还想要使用空参构造器,需要手动输入;

​ 2、有参数的构造器,往往会用于创建对象的时候的数据初始化。

package 封装;

public class Star {
	//成员变量
	private String name;
	private int age;
	//无参构造方法
	public Star(){
		System.out.println("无参构造方法执行啦!");
	}
	//全参构造方法
	public Star(String name,int age){
		System.out.println("全参构造方法执行啦");
		this.name = name;
		this.age = age;
	}
	//getter/setter
	public void setName(String name){
		this.name = name;
	}
	public String getName(){
		return name;
	}
	public void setAge(int age){
		this.age = age;
	}
	public int getAge(){
		return age;
	}
}
package 封装;

public class TestStar {
	public static void main(String[] args) {
		Star st = new Star();//无参构造
		System.out.println("***************************");
		Star st1 = new Star("赵丽颖",20);//全参构造
		System.out.println("姓名是:"+st1.getName()+",年龄是:"+st1.getAge());
		//如果需要改变对象中的数据内容,仍然还需要使用setXxxx()方法
		st1.setAge(23);//改变年龄
		System.out.println("姓名是:"+st1.getName()+",年龄是:"+st1.getAge());
	}
}

3、this关键字的用法

当方法的局部变量与参数重名时,根据“就近原则”,优先使用局部变量

this的类型:哪个对象调用,就是哪个对象的引用类型(谁就是this)

表示的是当前正在创建的对象,他指向当前对象。可以通过this来实现在当前对象内部,调用属性、方法、构造器。

构造函数的参数名和这个类的属性重复了,用this可以区分哪个是类的属性,哪个是构造函数的参数名

调用属性:this.health = 100;

​ this.name = “大黄”;

调用方法:this.print();

调用构造方法:this();

​ this(“小黑”,100,100,“雄”);//如果使用,必须是构造方法中的第一条语句

public class Test {
    public static void main(string[] args) {
        Dog d = new Dog("旺财", 3);
        Dog d2 = new Dog("大黄", 4);
        d.eatFood();
        d2.eatFood();
    }
}

public class Dog {
    public String name;
    public int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eatFood() {
        System.out.printlin(this.name + "吃狗粮");
    }

    public void eatMoreFood() {
        this.eatFood();//这里的this代指的就是当前的对象
        this.eatFood();
    }
}

4、局部变量与全局变量

①全局变量:定义在一个类的内部,方法的外部,就是全局变量,类中所有的方法都可以调用

②局部变量:声明在一个方法的内部,只能被本方法内部代码调用(从声明的位置开始到器所在的位置截止)

第六章 封装与继承

一、封装

面向对象的三大特征之一–封装

概念:将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。

封装的是自己的属性和方法

把尽可能多的东西藏起来,对外提供便捷的接口;把所有的属性藏起来,

步骤:

①修改属性的可见性-------设为private,防止错误的修改

②创建公有的getter/setter方法---------用于属性的读写

③在getter/setter方法中加入属性控制语句---------对属性值的合法性进行判断

封装的好处:

①便于使用者正确使用系统,防止错误修改属性

②有助于系统之间的松耦合,提高系统独立性

③提高软件的可重用性

④降低了构建大型系统的风险

package 封装;

public class Pet {
	private int age;
	private String name;
	private String color;
	/*
	 * 提供间接访问的接口
	 * 查看数据、修改数据
	 * 查看数据 get 后面跟变量的名字,变量名首字母大写
	 * 修改数据 set 后面也跟变量的名字,变量名首字母大写
	 * public 修饰限定词 共有的 
	 */
	public int getAge(){
		return this.age;
	}
	
	//在这个set方法中进行非法数据的控制0-15
	 public int setAge(int age) throws Exception{//抛出异常
		 if(age<0 || age>15){
			 System.out.println("您输入的数字不合理,请重新输入:");
			 //会抛出一个异常,终止代码的执行,指人为抛出的
			 throw new Exception("您输入的数字不合理,请输入0-15之间的数字:");
		 }
		 return this.age = age;
	 }
}
package 封装;

public class TestPet {
	public static void main(String[] args) throws Exception {
		Pet p = new Pet();
		int age = 10;
		String name = "旺财";
		String color = "yellow";
		p.getAge();
		
		p.setAge(18);
		System.out.println(p.getAge());
	}
}

private内容讲解:

package 封装;
//问题描述:定义person的年龄时无法阻止不合理的数值被放置进来
//解决方案:用private关键字将需要保护的成员变量进行修饰
//一旦使用private修饰,本类范围可以直接访问,超出范围就无法直接访问了
//间接访问private成员变量,就是定义一对儿getter/setter方法
//对于getter来说必须有返回值,不能有参数,返回值类型与成员变量一致
//对于setter来说不能有返回值,必须有成员变量,参数类型和成员变量对应
public class Person {
	String name ;
	private int age;
	
	public void show(){
		System.out.println("我叫"+name+",今年"+age+"岁");
	}
	//这个成员方法,专门用于向age设置数据
	public void setAge(int num){
		if(num<100 && num>0){
			age = num;
		}else{
			System.out.println("数据不合理!");
		}
		
	}
	//这个成员方法,专门用于向age获取数据
	public int getAge(){
		return age;
	}
}
package 封装;

public class TestPerson {
	public static void main(String[] args) {
		Person person = new Person();
		person.show();
		
		person.name = "sarla";
		//person.age = 27;//直接访问private内容的错误写法
		person.setAge(-12);
		person.show();
	}
}

例题1:

package 封装;
	//对于基本数据类型的boolean值,getter方法一定要写成isXxx(),setter方法不变
public class Student {
 
	private String name;
	private int age;
	private boolean male;//是不是男性
	public void setMale(boolean b){
		male = b;
	}
	public boolean isMale(){
		return male;
	}
	public void setName(String str){
		name = str;
	}
	public String getName(){
		return name;
	}
	public void setAge(int num){
		age = num;
	}
	public int getAge(){
		return age;
	}
}
package 封装;

public class TestStudent {
	public static void main(String[] args) {
		Student student = new Student();
		student.setName("张三"); 
		student.setAge(32);
		student.setMale(true);
		
		System.out.println("姓名是:"+student.getName());
		System.out.println("年龄是:"+student.getAge());
		System.out.println("是不是男生?"+student.isMale());
	}
}

二、static静态、工具类、单例模式

1、static静态

静态成员变量可以用类名.属性 去访问静态属性(静态成员变量),也可以使用对象.属性去访问静态成员变量(一般不建议这么写)。实例成员变量只能使用对象访问。

可以用来修饰变量、方法、代码块等,分别被称为静态变量、静态方法、静态代码块。

特点:

1、被类的所有对象共享

2、多了一种调用方式,可以通过类名进行调用(推荐使用类名调用)

3、随着类的加载而加载,优先于对象存在

作用:用于区分成员变量、方法、内部类、初始化块这四种成员到底属于类本身还是属于实例

注意:static方法中,只能访问静态成员(直接访问);static不允许使用this关键字。

package static工具类设计模式;

public class Student {
	String name;
	int age;
	static String school;
}

package static工具类设计模式;

public class Staticdemo {
	/*
	 * static 关键字:修饰符 可以修饰成员变量、成员方法
	 * 特点:
	 * 1、被类的所有对象共享
	 * 2、多了一种调用方式,可以通过类名进行调用(推荐使用类名调用)
	 * 3、随着类的加载而加载,优先于对象存在
	 */
	public static void main(String[] args) {
		Student.school = "安徽中医药大学";
		Student st1 = new Student();
		st1.name = "张三";
		st1.age = 23;
		
		
		System.out.println(st1.name+"-----"+st1.age+"------"+Student.school);
		
		System.out.println("------------");
		
		Student st2 = new Student();
		st2.name = "李四";
		st2.age = 27;
//		st2.school = "安徽中医药大学";
		
		System.out.println(st2.name+"-----"+st2.age+"------"+st2.school);
		
	} 
}

2、工具类

static成员方法常用于工具类

Arrayslist

3、设计模式–单例模式

设计模式VIP单例模式

饿汉单例设计步骤:定义一个类,把构造器私有,定义一个静态变量存储一个对象

懒汉单例设计步骤:定义一个类,把构造器私有,定义一个静态变量存储一个对象,提供一个返回单例对象的方法。

4、标准 JavaBean

实体类:封装数据的类

①、成员变量私有化 private

②空参、带参构造方法

③对于私有的成员变量,提供对应的setXxx和getXxx方法

ptg插件一键生成空参、含参构造方法、getter、setter方法

实体类的应用场景:实体类只负责数据存取,而对数据的处理交给其他类来完成,以实现数据和数据业务处理相分离

案例:

需求:1、展示系统中的全部电影(每部电影展示:名称,评分)。

​ 2、允许用户根据电影编号(id)查询出某个电影的详细信息

三、继承

1、继承

面向对象的三大特征之二–继承

继承是多态的前提,没有继承就没有多态。

继承主要解决的问题是:共性抽取。

父类又叫基类、超类;子类又叫派生类。

**定义:**Java中提供一个关键字extends,用这个关键字,我们就让一个类和另一个类建立起父子关系。

格式:

public class 子类名 extends 父类名{

}

**继承的好处:**可以提高代码的复用性,子类继承了父类后就可以直接使用父类公共的属性和方法了。

什么时候使用继承?

当类与类之间,存在相同(共性)的内容,并产生了is a的关系,就可以考虑使用继承来优化代码。

继承设计规范:子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类自己里面。

package 继承;

public class Extends {
	public static void main(String[] args) {
		Coder c = new Coder();
		c.setName("张三");
		c.setAge(25);
		c.setSalary(12000);
		
		System.out.println(c.getName()+"年龄是:"+c.getAge()+"工资是:"+c.getSalary());
	}
}
/*创建类的细节:
 * 一个Java文件中可以写多个class
 * 1、保证类与类之间是平级关系
 * 2、只能有一个被public修饰
 */

	class employee{
		private String name;
		private int age;
		private double salary;
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
		public double getSalary() {
			return salary;
		}
		public void setSalary(double salary) {
			this.salary = salary;
		}
		
		
	}
	class Coder extends employee{
	
	}

	class Manager extends employee{
	
	}

Java中继承的特点:

Java只支持单继承,不支持多继承,但支持多层继承

package 继承;

public class Extends4 {
	public static void main(String[] args) {
		C c = new C();
		c.methodC();
		c.methodB();
		c.methodA();
	}
}
	class A{
		public void methodA(){
			System.out.println("A......");
		}
	}
	
	class B extends A{
		public void methodB(){
			System.out.println("B......");
		}
	}
	
	class C extends B{
		public void methodC(){
			System.out.println("C......");
		}
	}

结果为:

C…
B…
A…

2、继承中的成员访问特点

2.1成员变量

子父类中,如果出现了重名的成员变量,使用的时候会遵循就近原则,使用子类的。

this:调用本类成员;super:调用父类成员

public class Fu{
	int num = 10;
}

class Zi extends Fu{
	int num = 20;
	public void method(){
		int num  = 30;
		System.out.println(num)//30
		System.out.println(this.num)//20
		System.out.println(super.num)//10
	}
}
2.2成员方法

访问特点:子父类中出现了方法声明一模一样的方法(方法名,参数,返回值)
在创建子类对象,调用方法的时候会优先使用子类的方法逻辑,
这虽然是就近原则的现象,其实是子类的方法对父类的方法进行了重写操作

package 继承;

public class Extends2 {
	/*
	 * 子父类中出现了方法声明一模一样的方法(方法名,参数,返回值)
	 * 在创建子类对象,调用方法的时候会优先使用子类的方法逻辑,
	 * 这虽然是就近原则的现象,其实是子类的方法对父类的方法进行了重写操作
	 */
	public static void main(String[] args) {
		Zi z = new Zi();
		z.method();
		z.show();
	}
}
	class Fu{
		int num = 10;
		
		public void show(){
			System.out.println("Fu.....show");
		}
	}
	
	class Zi extends Fu{
		int num = 20;
		
		public void method(){
			System.out.println(num);//20
			System.out.println(super.num);//10
		}
		public void show(){
			System.out.println("Zi.....show");
		}
	}
2.3构造方法

在所有的构造方法中,都默认隐藏了一句话 super();通过这句代码,来访问父类的空参构造方法。

Java当中所有的类都直接或者间接继承了Object类

person:

package mextends;

public class Person {
    private String name;
    private  int age;


    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
}

teacher:

package mextends;

public class Teacher extends Person{

    public Teacher() {
    }

    public Teacher(String name,int age) {
        super(name,age);
    }
    public void teach(){
        System.out.println("姓名为"+super.getName()+",年龄为:"+super.getAge()+"岁,正在讲课");
    }
}

student:

package mextends;

public class Student extends Person{

    private double score;

    public Student() {
    }

    public Student(String name, int age,double score) {
        super(name, age);
        this.score = score;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public void study(){
        System.out.println("姓名为"+super.getName()+",年龄为"+super.getAge()+"岁,成绩为"+score+"分的学生正在学习。");
    }
}

test:

package mextends;

public class Test {
    /*
    需求:
        人类:person
            成员变量:姓名,年龄
        老师类:
            成员变量:姓名,年龄
            成员方法:teach
                姓名为张三,年龄30岁,正在讲课
        学生类:
            成员变量;姓名,年龄,成绩
            成员方法:study
                姓名为李四,年龄20岁,正在学习
     */
    public static void main(String[] args) {
        Teacher t = new Teacher("张三",30);
        t.teach();

        Student stu = new Student("李四",20,78.9);
        stu.study();
    }
}

结果为:姓名为张三,年龄为:30岁,正在讲课
姓名为李四,年龄为20岁,成绩为78.9分的学生正在学习。

3、方法重写

在继承体系中,子类可以继承到父类的方法,但是有时候子类并不像原封不动地继承父类的方法,而是想做一定的修改,这就需要采用方法的重写,方法重写又称为方法覆盖。

标志:@Override

方法重载:(overload)在同一个类中,方法名相同,参数不同,与返回值无关
参数不同:个数不同、类型不同、顺序不同
方法重写:(override)在子父类中,出现了方法声明一模一样的方法(方法名字、参数、返回值)

  • 目标1、能够独立识别出,方法是是不是重写的方法
  • 注解:@Override
  • 目标2、方法重写的使用场景,
  • 当子类需要父类的方法,但是觉得父类的方法逻辑不好,需要做修改或增强,就可以对父类方法进行重写
package 继承;

import java.lang.reflect.Method;

public class Extends3 {
	/* 方法重载:(overload)在同一个类中,方法名相同,参数不同,与返回值无关
	 *                                 参数不同:个数不同、类型不同、顺序不同
	 * 方法重写:(override)在子父类中,出现了方法声明一模一样的方法(方法名字、参数、返回值)
	 * 目标1、能够独立识别出,方法是是不是重写的方法
	 *      注解:@Override
	 * 目标2、方法重写的使用场景,
	 *      当子类需要父类的方法,但是觉得父类的方法逻辑不好,需要做修改或增强,就可以对父类方法进行重写
	 */
	public static void main(String[] args) {
		Son s = new Son();
		s.method();
	}
}
	
	class Father{
		public void method(){
			System.out.println("Father...method...");
		}
	}
	
	class Son extends Father{
		@Override
		public void method(){
			System.out.println("Son.....method...");
		}
	}

注意事项:父类中私有的方法不能被重写;子类在重写父类方法时,访问权限必须大于等于父亲

权限修饰符:private < (default) < protected < public

4、权限修饰符

①private讲解见1、封装

②(default):默认权限,同一个类中,同一个包中

③protected:同一个类中,同一个包中,不同包的子类

④public:任意位置访问

权限修饰符同一个类中同一个包中不同包的子类不同包的无关类
private
(default)
protected
public

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7wMb9WBk-1689128132291)(D:\java\练习\day9\继承\protected.png)]

例题1:

5、IDEA

5.1 IDEA中的类操作

新建类文件、删除类文件、修改类文件(shift+F6)

5.2 IDEA中的模块操作

新建模块、删除模块、修改模块(shift+F6后选择第三个)、导入模块

5.3 IDEA中的项目操作

关闭项目、打开项目、修改项目、新建项目

快速向下复制一行Ctrl+D

案例:

Employee:

package mextends.anli;

public class Employee {
    private String name;
    private int age;
    private double sarlay;


    public Employee() {
    }

    public Employee(String name, int age, double sarlay) {
        this.name = name;
        this.age = age;
        this.sarlay = sarlay;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return sarlay
     */
    public double getSarlay() {
        return sarlay;
    }

    /**
     * 设置
     * @param sarlay
     */
    public void setSarlay(double sarlay) {
        this.sarlay = sarlay;
    }

    public void work(){
        System.out.println("员工工作......");
    }
}

Coder:

package mextends.anli;

public class Coder extends Employee{
    public Coder() {
    }

    public Coder(String name, int age, double sarlay) {
        super(name, age, sarlay);
    }

    @Override
    public void work() {
        System.out.println("姓名为:"+super.getName()+",年龄是"+super.getAge()+",工资为"+super.getSarlay()+"的程序员正在编写代码");
    }
}

Manager:

package mextends.anli;

public class Manager extends Employee{

    private double bonus;

    public Manager() {
    }

    public Manager(String name, int age, double sarlay,double bonus) {
        super(name, age, sarlay);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    @Override
    public void work() {
        System.out.println("姓名为:"+super.getName()+",年龄是"+super.getAge()+",工资为"+super.getSarlay()+"奖金为"+bonus+"的项目经理正在分配任务");
    }
}

Test:

package mextends.anli;

public class Test {
    public static void main(String[] args) {
        Manager manager = new Manager("李四",24,18000,5000);
        manager.work();

        Coder coder = new Coder("张三",23,15000);
        coder.work();
    }

结果为:

姓名为:李四,年龄是24,工资为18000.0奖金为5000.0的项目经理正在分配任务
姓名为:张三,年龄是23,工资为15000.0的程序员正在编写代码

6、super关键字的用法

代表父类存储空间的标识

关键字访问成员变量访问成员方法访问构造方法
thisthis.本类成员变量this.本类成员方法this();this(…);本类构造方法
supersuper.父类成员变量super.父类成员方法super();super(…);父类构造方法

注意:this()和super()都在争夺构造方法第一行的位置,所以二者不能共存

super调用父类成员的省略规则:

super.父类成员变量||super.父类成员方法()---------->被调用的变量和方法,在子类中不存在,super.可以直接省略

开闭原则:对功能拓展做开放,对修改代码做关闭

四、final关键字

final关键字是最终的意思,可以修饰(方法、变量,类)

final修饰的特点:

修饰方法:表明该方法是最终方法,不能被重写

修饰类:表明该类是最终类,不能被继承

修饰变量:表明该变量是常量,不能被再次赋值

修饰基本数据类型:数据值不可改变

修饰引用数据类型:地址值不可改变,内容可以改变

修饰成员变量:不允许修饰默认值

final修饰成员变量的初始化时机:①在定义的时候直接赋值 final int num = 20;②在构造方法中完成赋值public Studengt(){num= 20};

final修饰变量的命名规范:如果变量名是一个单词,所有字母大写 MAX;如果变量名是多个单词,所有字母大写,中间使用下划线分隔MAX-VALUE

第七章 面向对象进阶

一、package包和抽象类介绍

1、包

什么是包?

包是用来分门别类的的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护

建包的语法格式:package 公司名倒写.技术名称。包名建议英文全小写,且具备意义

建包语句一般在第一行,一般idea会帮助建立

导包:相同包下的类可以直接访问,不同包下的类必须导包,才可以使用。导包格式:import 包名.类名

假如一个类中需要用到不同类,而这两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问,可以使用全类名创建对象:包名+类名

2、抽象类

2、1抽象类

抽象类是一种特殊的父类,内部可以编写抽象方法

抽象方法:当我们将共性的的方法,抽取到父类之后,发现这个方法在父类中无法给出具体明确(描述不清),而且这个方法还是子类必须要有的方法,就可以设计为抽象方法。

抽象方法需要存活在抽象类中,继承抽象类后子类必须对父类的方法进行重写。

抽象方法格式:public abstract 返回值类型 方法名(参数列表);

抽象类格式:public abstract class 类名{};

package mabstract;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

public class AbstractTest {
    public static void main(String[] args) {

    }
}

abstract class Animal{
    public abstract void eat();//抽象方法必须存在于抽象类中
}

class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼");//继承抽象类后必须对方法进行重写
    }
}

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}
2、1注意事项

抽象类不能实例化;如果抽象类允许实例化就可以调用内部没有方法体的抽象方法了,这没有意义

抽象类存在构造方法;交给子类,通过super进行访问

抽象类中可以存在普通方法;可以让子类继承继续使用

抽象类的子类,要么重写抽象类中所有抽象方法,要么是抽象类。

2、3 abstract关键字的冲突

①final:被abstract修饰的方法,强制要求子类重写,被final修饰的方法子类不能重写

②private:被abstract修饰的方法,强制要求子类重写,被private修饰的方法子类不能重写

③static:被static修饰的方法可以被类名调用,类名调用抽象方法没有意义

二、接口 Interface

1、接口

体现的思想是对规则的声明,Java中的接口体现的是对行为的抽象。

如果发现一个类,所有的组成,都是抽象方法,没有成员变量,没有普通方法,这种类,我们通常会设计为Java中的接口,因为现在这个类存在的唯一价值就是声明规则了。

定义格式:interface 接口名{}

实现接口的格式:class 类名 implement 接口名{}

实现类(接口的子类):1、重写所有的抽象方法 2、将实现类也变成抽象类

接口和类之间是实现关系,通过implements关键字来完成

package minterface;

public class InterfaceTest1 {
    public static void main(String[] args) {
        InterImpl ii = new InterImpl();
        ii.show();
        ii.method();
    }
}
interface inter {
    public abstract void show();
    public abstract void method();
}
class InterImpl implements inter{

    public void show() {
        System.out.println("show.....");
    }

    public void method() {
        System.out.println("method.....");
    }
}

2、接口中的成员特点

①成员变量:只能定义常量,因为系统会默认加入三个关键字:public static final 这三个关键字没有顺序关系,这个常量的所有字母需要全部大写,因为被final修饰。

②成员方法:只能是抽象方法,因为系统会默认加如两个关键字 public abstract

③构造方法:没有构造方法

**类和类之间的关系:**继承关系,只支持单继承,不支持多继承,支持多层继承

**接口和类之间的关系:**实现关系,可以单实现,可以多实现,甚至可以在继承一个类的同时,实现多个接口

**接口和接口之间的关系:**继承关系,可以单继承,也可以多继承

3、抽象类和接口的对比

成员变量----抽象类:可以定义变量,也可以定义常量

​ 接口:只能定义常量

成员方法------抽象类:可以定义具体的方法,也可以当以抽象方法

​ 接口:只能定义抽象方法

构造方法------抽象类:有

​ 接口:没有

抽象类:对事物做抽象(描述事物)

接口:对行为抽象(指定规则)

4、JDK8和JDK9接口的新特性

4、1 JDK8接口特性

接口中默认方法的定义格式:

格式:public default 返回值类型 方法名(参数列表){}

范例:public default void show(){}

JDK8版本接口特性:

1、允许定义非抽象方法,需要加入default关键字

作用:解决接口升级问题

注意事项:1、public可以省略,但是default不能省略

			2、默认方法,实现类是允许重写的,但是需要去掉default关键字

​ 3、如果实现了多个接口,多个接口中存在相同的默认方法,实现类必须重写默认方法

2、允许定义静态方法

理解:既然接口已经允许方法带有方法体了,干脆也放开静态方法,可以类名调用

注意事项:1、public可以省略,但是static不能省略

			  2、接口中的静态方法,只允许接口名进行调用,不允许实现类通过对象调用
4、2 JDK9接口特性

接口中允许定义私有方法

格式1:private 返回值类型 方法名 (参数列表){}

范例1:private void show(){}

格式2:private static 返回值类型 方法名 (参数列表){}

范例2:private static void show(){}

三、代码块

1、局部代码块

​ 位置:方法中的一堆大括号

​ 作用:限定变量的生命周期,提早释放内存

2、构造代码块

​ 位置:类中方法外

​ 特点:在创建对象,执行构造方法的时候,就会执行构造代码块(优先于构造方法执行)

​ 作用:将多个构造方法中,重复的代码,抽取到构造代码块中,从而提升代码的复用性

3、静态代码块

​ 位置:类中方法外

​ 特点:随着类的加载而执行,因为类只加载一次,所以也就只执行一次

​ 作用:在类加载的时候对数据进行初始化

第八章 多态

一、多态

同一个行为具有多个不同表现形式或形态的能力

多态的表现形式

父类类型 对象名称 = new 子类构造;

接口 对象名称 = new 实现类构造器;

多态的前提:

有继承/实现关系;

有父类引用指向子类对象;有方法重写

1、对象多态
//Animal a = new Dog();
//Animal a = new Cat();
好处:方法的形参定义为父类类型,这个方法就可以接收到该父类的任意子类对象了
2、行为多态
好处:同一个方法,具有多种不同表现形式或形态的能力
package polymorphism;

public class PolymorphismTest1 {
    /*
    多态的前提:有继承/实现关系
    有方法重写
    有父类引用指向子类对象

    1、对象多态
    //Animal a = new Dog();
    //Animal a = new Cat();
    好处:方法的形参定义为父类类型,这个方法就可以接收到该父类的任意子类对象了
    2、行为多态
    好处:同一个方法,具有多种不同表现形式或形态的能力
     */
    public static void main(String[] args) {
        Animal a1 = new Dog();
        Animal a2 = new Cat();

        userAnimal(new Dog());
        userAnimal(new Cat());
    }
    public static void userAnimal(Animal a){//Animal a = new Dog();
                                            //Animal a = new Cat();
    }
}



abstract  class Animal{
    public abstract void eat();
}

class Dog extends Animal{

    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}

class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

二、多态的成员访问特点

多态的成员访问特点:
1、成员变量:编译看左边(父类),运行看左边(父类)
2、成员方法:编译看左边(父类),运行看右边(子类)
在编译的时候会检查有没有这个方法,没有的话直接编译报错,有的话编译通过,但是运行的时候,一定会执行子类的方法逻辑
原因:担心在调用的方法是一个抽象方法
多态创建对象,调用静态成员:
静态的成员,推荐类名进行调用
细节:静态的成员,可以使用对象名调用,但这一般是假象,生成了字节码文件后,会自动将对象名调用,改为类名调用

package polymorphism;

import java.sql.SQLOutput;

public class polymorphismTest2 {
    /*
        多态的成员访问特点:
            1、成员变量:编译看左边(父类),运行看左边(父类)
            2、成员方法:编译看左边(父类),运行看右边(子类)
                       在编译的时候会检查有没有这个方法,没有的话直接编译报错,有的话编译通过,但是运行的时候,一定会执行子类的方法逻辑
                       原因:担心在调用的方法是一个抽象方法
        多态创建对象,调用静态成员:
            静态的成员,推荐类名进行调用
            细节:静态的成员,可以使用对象名调用,但这一般是假象,生成了字节码文件后,会自动将对象名调用,改为类名调用
     */
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);
        f.show();
        f.print();

        System.out.println("------------------");
        Inter i = new InterImpl();
        i.method();
    }
}
interface Inter{
    void method();
}

class InterImpl implements Inter {

    public void method() {
        System.out.println("method...");
    }
}

class  Fu{
    int num =10;

    public void show(){
        System.out.println("Fu......show");
    }

    public static void print(){
        System.out.println("Fu....print");
    }
}

class Zi extends Fu{
    int num = 20;

    @Override
    public void show() {
        System.out.println("Zi......show");
    }

    public static void print(){
        System.out.println("Zu....print");
    }
}

结果为:10 Zi…show Fu…show method…

三、多态的好处和弊端

**多态的好处:**提高了程序的拓展性

对象多态:将方法的形参定义为父类类型,这个方法可以接收该父类的任意子类对象

行为多态:同一个行为,具有多个不同表现形式或形态的能力

多态的弊端:不能使用子类的特有成员

四、多态中的转型

①自动类型转换(向上转型):从子到父(父类引用指向子类对象)

Animal a = new Dog();

②强制类型转换(向下转型):从父到子(将父类引用所指向的对象,转交给子类类型)

Animal a = new Dog();

Dog d = (Dog) a;

语法格式:子类 对象变量 = (子类)父类类型的变量

作用:可以解决多态下的劣势,可以实现调用子类独有的功能

注意:有继承/实现关系的类就可以在编译阶段进行强制类型转换;但是,如果转型后的类型和对象真实对象的类型不是同一种类型,那么在运行代码时,就会出现ClassCastException (类型转换异常)

使用关键字instanceof解决,格式:对象名 instanceof 类型,判断一个对象是否是一个类的实例,通俗的理解是,判断关键字左边的对象是否是右边的类型,返回boolean类型结果。

案例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hSTO84ts-1689128132292)(D:\java\练习\day11\多态案例.jpg)]

第九章 内部类

一、成员内部类

内部类就是定义在一个类里面的类

class Outer{
	//内部类
	class Inner{
		}
}

创建对象的格式:

格式:外部类名.内部类名 对象名 = new 外部对象().new 内部对象();
范例:Outer.Inner in = new Outer().new Inner();

成员访问细节:

1、在内部类中,访问外部类成员:直接访问,包括私有

2、在外部类中,访问内部类成员:需要创建对象进行访问

在成员内部类中访问所在外部类对象,格式:外部类名.this

package inner;

public class InnerTest {
    public static void main(String[] args) {
        Outer.Inner in = new Outer().new Inner();
        System.out.println(in.num);
        in.show();

        MyOuter.MyInner i = new MyOuter().new MyInner();
        i.show();
    }
}

class MyOuter{
    int num = 10;
    class MyInner{
        int num = 20;
        public void show(){
            int num = 30;
            System.out.println(num);   //30
            System.out.println(this.num);   //20
            System.out.println(MyOuter.this.num);   //10
        }
    }
}
class Outer{
    private void method(){
        Inner i = new Inner();
        System.out.println(i.num);
        System.out.println("外部类....method");
    }
    class Inner{
        int num = 10;
        public void show(){
            System.out.println("show......");
            method();
        }
    }
}

结果为:10
show…
10
外部类…method
30
20
10

二、静态内部类

有static修饰的成员内部类

class Outer{
	static class Inner{
	
	}
}

格式:外部类名.内部类名 对象名 = new 外部类名.内部对象();

格式:外部类名.内部类名 对象名 = new 外部类名.内部对象();
范例:Outer.Inner in = new Outer.Inner();

注意:静态只能修饰静态

三、局部内部类

放在方法、代码块、构造器等执行体中

1、概述

匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)

前提:需要存在一个接口或类

格式:new 类名/接口(){}

new 类名(){}:代表继承这个类

new 接口(){}:代表实现这个接口

结论:可以让代码变得更加简洁,在定义类的时候对其进行实例化

package inner;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

public class AnonClassTest1 {
    public static void main(String[] args) {
        userInter(new InterImpl());
        userInter(new Inter() {
            public void show() {
                System.out.println("匿名内部类....show");
            }
        });
    }

    public static void userInter(Inter i){
        i.show();
    }
}

interface Inter{
    void show();
}

class InterImpl implements Inter{

    public void show() {
        System.out.println("show.....");
    }
}

四、匿名内部类

概述:匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)

前提:需要存在一个接口或类

格式:

new 类名/接口(){

}

作用:可以让代码变得更加简洁,在定义类的时候就对其进行了实例化

匿名内部类可以作为方法的实际参数进行传输,如果接口里的抽象方法很少可以使用匿名内部类,如果方法很多则重写一个实现类。

package inner;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

public class AnonClassTest1 {
    /*
        概述:匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)

            前提:需要存在一个接口或类

            格式:

                new 类名/接口(){

                    }
                new 类名(){}:代表继承这个类
                new 接口名(){}:代表实现这个接口,里面则需要重写方法
     */
    public static void main(String[] args) {
        userInter(new InterImpl());
        
        userInter(new Inter() {
            @Override
            public void show() {
                System.out.println("匿名内部类....show");
            }
        });
    }

    //问题:方法的形参是接口类型,我们该传入什么?
    //答案:传入的应该是该接口的实现类对象
    public static void userInter(Inter i){

        i.show();
    }
}

interface Inter{
    void show();
}

class InterImpl implements Inter{

    public void show() {
        System.out.println("show.....");
    }
}

第十章 Lambda表达式

一、Lambda表达式

概述:Lambda表达式时JDK8后开始的一种新语法形式

作用:简化匿名内部类的代码写法

Lambda表达式的简化格式:()->{}

(匿名内部类被重写方法的形参列表)->{

​ 被重写方法的方法体代码

}

注:->是语法形式,无实际含义

Lambda表达式,只允许操作函数式编程接口:有,且仅有一个抽象方法的接口,通常会用@FunctionalInterface注解标记该接口校验是否为函数式接口。

package Lambda;

public class LambdaDemo1 {
    public static void main(String[] args) {
        userInterA(new InterA(){
            public void show() {
                System.out.println("匿名内部类,重写后的show方法...");
            }
        });
        userInterA(() -> {
            System.out.println("Lambda表达式,重写后的show方法...");
          });
    }

    public static void userInterA(InterA a){
        a.show();
    }
}
interface InterA{
    void show();
}

结果为:show…
匿名内部类…show

二、Lambda表达式的省略写法

参数类型可以省略不写

如果只有一个参数,参数类型也可以省略,同时()也可以省略

如果Lambda表达式的方法体代码只有一行代码可以省略大括号不写,同时要省略分号,此时,如果这行代码是return语句,必须省略return不写,同时也必须省略“;”,不写

package Lambda;

public class LambdaDemo1 {
    public static void main(String[] args) {
        userInterA(new InterA(){
            public void show() {
                System.out.println("匿名内部类,重写后的show方法...");
            }
        });
        userInterA(() ->
            System.out.println("Lambda表达式,重写后的show方法..."));
    }

    public static void userInterA(InterA a){
        a.show();
    }
}
interface InterA{
    void show();
}

三、Lambda表达式和匿名内部类的区别

使用限制不同

匿名内部类:可以操作接口,类

lambda表达式:只能操作函数式接口

实现原理不同

匿名内部类:编译之后,产生一个单独的.class字节码文件

lambda表达式:编译之后,没有一个单独的.class字节码文件

第十一章 常用API

什么是API?

API(Application Programming interface)应用程序编程接口。

一、Object类

1、toString方法

public String toString(),返回该对象的字符串表示

shi用打印语句打印对象名的时候,println方法在源码层面会自动调用该对象的toString方法

student:

package object.toString;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

test:

package object.toString;

public class ToStringDemo {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a);
        System.out.println(a.toString());

        Student student = new Student("张三",23);
        System.out.println(student);
    }
}
class A{
    @Override
    public String toString() {
        return"重写了toString方法";
    }
}

结果为:重写了toString方法
重写了toString方法
Student{name = 张三, age = 23}

Object的toString方法:

方法名说明
public String toString()默认是返回当前对象在堆内存中的地址信息:类的全类名@十六进制哈希值

开发中直接输出地址,默认输出对象的地址是毫无意义的。

开发中输出随想变量,更多的时候是希望看到对象的内容数据而不是地址信息。

toString存在的意义

父类tiString()方法存在的意义就是为了被子类重写,一边返回对象的内容信息,而不是地址信息

2、equals方法

object类中的equals方法,默认比较的是对象的地址,通常会重写equals方法,让对象之间比较内容

student

package object.toString;

public class Student {
    private String name;
    private int age;

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Student){
            //向下转型的目的是为了调用子类特有的成员
            Student stu = (Student) obj;
            return this.age == stu.age && this.name.equals(stu.name);
        }else{
            return false;
        }

    }

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

test:

package object.equals;

import object.toString.Student;

public class EqualsDemo {
    public static void main(String[] args) {

        Student stu1 = new Student("张三",23);
        Student stu2 = new Student("张三",23);

        System.out.println(stu1.equals(stu2));
    }
}

Object的equals方法:

方法名说明
public boolean equals(Object o)默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false

equals存在的意义:父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则

3、Objects

概述:Objects类与Object海誓继承关系,Objects类是从JDK1.7开始之后才有的。

方法名说明
public static boolean equals (Object a,Object b)比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常,再进行equals比较
public static boolean isNull(Object obj)判断变量是否为null

二、Math类

包含执行基本数字运算的方法,Math类没有提供公开的构造器。因为他的类成员都是静态的,所以我们可以通过类名直接调用

方法名说明
public static int abs(int a)获取参数绝对值
public static double ceil(double a)向上取整
public static double floor(double a)向下取整
public static int round(double a)四舍五入
public static int max(int a,int b)获取两个int值中的较大值
public static double pow(double a,double b)返回a的b次幂的值
public static double random()返回值为double的随机值,范围[0.0,1.0)
/*
    目标:Math类的使用
    Math用于做数学运算
    Math类中的方法全部都是静态方法,直接用类名调用即可
    方法:
        方法名                                              说明
        public static int abs(int a)                       获取参数a的绝对值
        public static double  ceil(double a)               向上取整
        public static double  floor(double a)              向下取整
        public static int round(double a)                  四舍五入
        public static int max(int a,int b)                 获取两个int值中的较大值
        public static double pow(double a,double b)        返回a的b次幂的值
        public static double random()                      返回值为double的随机值,范围[0.0,1.0)
 */
public class MathDemo {
    public static void main(String[] args) {
        //1.取绝对值:返回正数
        System.out.println(Math.abs(10));//10
        System.out.println(Math.abs(-10.3));//10.3
        System.out.println(Math.abs(-10.5));//10.5

        //2.向上取整:5
        System.out.println(Math.ceil(4.000001));//5.0
        System.out.println(Math.ceil(4.0));//4.0
        System.out.println(Math.ceil(4.9));//5.0

        //3.向下取整 4.0
        System.out.println(Math.floor(4.000001));//4.0
        System.out.println(Math.floor(4.0));//4.0
        System.out.println(Math.floor(4.9));//4.0

        //4.四舍五入
        System.out.println("-------------");
        System.out.println(Math.round(4.4));//4
        System.out.println(Math.round(4.41));//4
        System.out.println(Math.round(4.0001));//4
        System.out.println(Math.round(4.5));//5
        System.out.println(Math.round(4.9999));//5

        //5.返回a和b中比较大的那个值
        System.out.println(Math.max(3,5));//5
        System.out.println(Math.max(6,5));//6

        //6.求指数次方
        System.out.println(Math.pow(2,3));//2^3 = 8.0

        //7.返回包含0,但不到1的随机数。[0,1)
        System.out.println(Math.random());//0.5467622065313078



        //返回一个3-9之间的随机数
        //random  [0,1) 3-9
        //[0,6] + 3
        //[3,9]
      int data = (int)(Math.random()*7) + 3;
      System.out.println(data);
    }
}

三、System类

特点:System的功能是通用的,都是直接用类名调用即可,所以System不能被实例化的

方法名说明
public static void exit(int status)终止当前运行的Java虚拟机,非零表示异常终止
public static long currentTimeMillis()返回当前系统的时间毫秒值形式
public static native void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数)数组拷贝
import java.util.Arrays;

/*
    目标:System系统类的使用
    System代表当前系统。(虚拟机系统)
    静态方法:
            1.public static void exit(int status):终止JVM虚拟机,非0是异常终止
            2.public static long currentTimeMillis():获取当前系统此刻时间毫秒值。(重点)时间戳
            3.可以做数组拷贝
                    arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
                    *

 */
 public class SystemDemo {
    public static void main(String[] args) {
        System.out.println("程序开始。。。");

//        System.exit(0);JVM

        //计算机认为时间有起源的:返回1970-1-1  00:00:00  走到现在的总的毫秒值   时间毫秒值
//        long time = System.currentTimeMillis();
//        System.out.println(time);



//        long startTime = System.currentTimeMillis();
//        for(int i = 0;i< 1000000;i++){
//            System.out.println("输出: "+i);
//        }
//        long endTime = System.currentTimeMillis();
//        System.out.println((endTime-startTime)/1000.0 + "s");//for循环执行所花费的时间

        //3、数组拷贝
        /*
            arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
            参数一:被拷贝的数组
            参数二:从哪个索引位置开始拷贝
            参数三:复制的目标数组
            参数四:粘贴位置
            参数五:拷贝元素的个数
         */
        int[] arr1 = {10,20,30,40,50,60,70};
        int[] arr2 = new int[6];//=>[0, 0, 0, 0, 0, 0]
        System.out.println("拷贝前的arr1===="+Arrays.toString(arr1));
        System.out.println("拷贝前的arr2===="+Arrays.toString(arr2));
        System.arraycopy(arr1,3,arr2,2,3);
        System.out.println("-------------------------------");
        System.out.println("拷贝后的arr1===="+Arrays.toString(arr1));
        System.out.println("拷贝后的arr2===="+Arrays.toString(arr2));

        System.out.println("程序结束");
    }
}

四、BigDecimal类

BigDecimal的作用:用于解决浮点型运算精度失真的问题

创建对象:

public BigDecimal(String val)
public static BigDecimal valueOf(double val):包装浮点数成为大数据对象
方法名说明
public BigDecimal add(BigDecimal value)加法
public BigDecimal subtract(BigDecimal value)减法
public BigDecimal multiply(BigDecimal value)乘法
public BigDecimal divide(BigDecimal value)除法
public BigDecimal divide(另外一个BigDecimal 对象,精确几位,舍入模式)除法

五、Date类

含义:Date类代表当前所在系统的日期时间信息。

Date的构造器

名称:public Date

说明:创建一个Date对象,代表的是系统当前此刻日期时间

Date的常用方法

名称:public long getTime()

说明:返回从1970年1月1日 00:00:00走到此刻的总的毫秒数

时间毫秒值->日期对象

构造器:public Date(long time)

说明:把时间毫秒值转换成Date日期对象

Date方法:public void setTime(long time)

说明:设置日期对象的时间为当前时间毫秒值对应的时间

package object.date;

import java.util.Date;
import java.util.Scanner;

public class Test1 {
    //1、构造方法:
    /*public Date():将当前时间,封装为Date日期对象
      public Date(long time):把时间毫秒值转换成Date日期对象
      2、常见方法:
      public long getTime():返回从1970年1月1日00:00:00走到此刻的总的毫秒值
      public void setTime(long time):设置日期对象时间为当前时间毫秒值对应的时间
     */

    public static void main(String[] args) {
        //将当前时间,封装为Date日期对象
        Date d1 = new Date();
        System.out.println(d1);

        //把时间毫秒值转换成Date日期对象
        Date d2 = new Date(0l);
        System.out.println(d2);

        //public long getTime():返回从1970年1月1日00:00:00走到此刻的总的毫秒值
        System.out.println(d1.getTime());
        System.out.println(d2.getTime());

        System.out.println("------------------");

        //public void setTime(long time):设置日期对象时间为当前时间毫秒值对应的时间
        Date d3 = new Date();
        d3.setTime(0l);
        System.out.println(d3);
    }
}

1、SimpleDateFormat类

用于日期格式化

构造器说明
public SimpleDateFormat()构造一个SimpleDateFormat,使用默认格式
public SimpleDateFormat(String pattern)构造一个SimpleDateFormat,使用指定的格式
格式化方法说明
public final String format(Date date)将日期格式化成日期/时间字符串
public final Date parse(String source)将字符串解析成日期类型
package object.format;


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatDemo {
    public static void main(String[] args) throws ParseException {
        String today = "2023年6月29日";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        Date date = simpleDateFormat.parse(today);
        System.out.println(date);
    }
    private static void method(){
        //创建一个日期格式化对象,使用默认模式
        SimpleDateFormat format = new SimpleDateFormat();

        //创建一个日期格式化对象,手动指定模式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss E a");

        //创建Date对象, 封装此刻的时间
        Date date = new Date();

        //将日期对象,转换为字符串
        String re = format.format(date);
        System.out.println(re);
        String result = simpleDateFormat.format(date);
        System.out.println(result);

    }
}

例题:计算你来到了这个世界多少天

package object.format;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class DateTest1 {
    /*
    需求:计算你来到这个世界多少天了
    分析:1、键盘录入用户生日(日期字符串)
         2、创建SimpleDateFormat对象,指定模式,用于将日期字符串解析为Date日期对象(birthdayDate)
         3、创建Date日期对象,封装此刻的时间(todayDate)
         4、long time = todayDate.getTime() - birthdayDate.getTime();
         5、将毫秒值,转换成为天的单位:time/1000/60/60/24;
     */
    public static void main(String[] args) throws ParseException {
        //1、键盘录入用户生日(日期字符串)
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入您的生日:xxxx年xx月xx日");
        String birthday = sc.nextLine();

        //2、创建SimpleDateFormat对象,指定模式,用于将日期字符串解析为Date日期对象(birthdayDate)
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");

        //生日当天的日期对象
        Date birthdayDate = simpleDateFormat.parse(birthday);

        //3、创建Date日期对象,封装此刻的时间(todayDate)
        Date todayDate = new Date();

        //4、计算用户活了多少毫秒
        long l = todayDate.getTime() - birthdayDate.getTime();

        //5、将毫秒值,转换成为天的单位:time/1000/60/60/24;
        System.out.println(l / 1000 / 60 / 60 / 24);
    }
}

2、Calendar类介绍

代表的是系统此时此刻时间对应的日历,通过它可以单独获取、修改时间中的年、月、日、时、分、秒等。

将已写好的方法抽取出来用快捷键:Ctrl+Alt+m

1、创建对象:

public static Calender getInstance():获取当前时间的日历对象
2、常用方法
        public int get(int field):取日历中的某个字段信息
                    get方法的参数:Calender类中的静态常量,即
                    Calender.YEAR 年份信息
                    Calendar.MONTH 月份信息,记得加1
                    Calendar.DAY_OF_MONTH 日信息
                    Calendar.DAY_OF_WEEK 星期几的信息,需要提前设计一个数组将计算方法改成与我们习惯相同
                    Calendar.DAY_OF_YEAR 一年中的第几天

        public void set(int field,int value):修改日历的某个字段信息
        public void add(int field,int amount):为某个字段增加/减少指定的值
package object.calendar;

import java.util.Calendar;

public class CalendarDemo {
 public static void main(String[] args) {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.YEAR,1);
        System.out.println(c.get(Calendar.YEAR));


    }

    private static void setMethod() {
        Calendar c = Calendar.getInstance();//获取修改对象

        c.set(Calendar.YEAR,1996);//确定修改内容
        c.set(1997,11,18);
        System.out.println(c.get(Calendar.YEAR));//查看修改结果
    }

    //将已经写好的内容抽取成一个方法用快捷键Ctrl+Alt+m
    private static void getMethod() {
        //Calender c :抽象类
        //Calendar.getInstance():获取的是子类对象
        Calendar c = Calendar.getInstance();

        //2、调用get方法,获取指定字段的信息
        int year = c.get(Calendar.YEAR);
        System.out.println(year);

        int month = c.get(Calendar.MONTH);
        System.out.println(month+1);//Calender月份是0-11,想要常规的结果需要对结果+1操作

        int day = c.get(Calendar.DAY_OF_MONTH);
        System.out.println(day);

        char[] weeks = {' ','日','一','二','三','四','五','六'};
        int weekIndex = c.get(Calendar.DAY_OF_WEEK);
        System.out.println(weeks[weekIndex]);

        int dayofyear = c.get(Calendar.DAY_OF_YEAR);
        System.out.println(dayofyear);
    }
}


案例:疯狂星期四

需求:使用程序判断出2050年3月1日是否是疯狂星期四

package object.calendar;

import java.util.Calendar;

public class Test {


    //需求:使用程序判断出2050年3月1日是否是疯狂星期四
    public static void main(String[] args) {
        //1、获取当前日历信息
        Calendar c = Calendar.getInstance();

        //2、修改日历时间为2050年3月1日
        c.set(2050,3,1);

        //3、获取想要的日期的星期数
        char[] weeks = {' ','日','一','二','三','四','五','六'};
        int weekIndex = c.get(Calendar.DAY_OF_WEEK);
        if(weeks[weekIndex] == '四'){
            System.out.println("2050年3月1日是疯狂星期四,可以奢侈一把");
        }else{
            System.out.println("2050年3月1日不是疯狂星期四,自己花大钱买");
        }
        
    }
}

结果为:2050年3月1日不是疯狂星期四,自己花大钱买

Calender类常用方法

方法名说明
public final Date getTime()获取日期对象
public final setTime(Date date)给日历设置日期对象

案例2:键盘录入一个日期字符串,程序输出这个日期是一年中的第多少天

package object.calendar;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;

public class Test2 {
    /*
        需求:键盘输入一个日期,程序输出这个日期是一年中的第多少天
        分析:1、使用SimpleDateFormat,将日期字符串转换为日期对象
             2、将日期对象,转换为Calender对象
             3、调用get方法,获取一年中的第几天
     */
    public static void main(String[] args) throws ParseException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个日期:xxxx年xx月xx日");
        String dateContent = sc.nextLine();

        //1、使用SimpleDateFormat,将日期字符串转换为日期对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");

        //2、将字符串解析成日期类型
        Date date = dateFormat.parse(dateContent);

        //3、获取Calender对象
        Calendar c = Calendar.getInstance();

        //4、调用setTime方法,接收键盘输入的日期
        c.setTime(date);

        //5、调用get方法,获取一年中的第几天
        int dayOfYear = c.get(Calendar.DAY_OF_YEAR);
        System.out.println("是一年中的第"+dayOfYear+"天");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YLf6eWU8-1689128132292)(D:\java\练习\day13\7116da86d401424e2bdcff873f8821b.jpg)]

3、JDK8+时间类

3、1日历类

LocalDate:年、月、日

LocalTime:时、分、秒

LocalDateTime:年、月、日、时、分、秒

创建对象

方法名:public static Xxxx now():获取系统当前时间对应的该对象

示例:LocalDate ld = LocalDate.now();

​ LocalTime lt = LocalTime.now();

​ LocalDateTime ldt = LocalDateTime.now();

方法名:public static Xxxx of(…):获取指定时间的对象

示例:LocalDate LocalDate1 = LocalDate.of(2099,11,11);

​ LocalTime LocalTime1 = LocalTime.of(9,8,59);

​ LocalDateTime LocalDateTime1 = LocalDateTime.of(2025,11,16,14,30,01);

LocalDate、LocalTime、LocalDateTime都是不可变的,下列修改的方法返回的都是一个新的对象。

package object.jdk8;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class TimeDemo {

    public static void main(String[] args) {
        //获取此刻的时间对象
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);

        System.out.println(now.getYear()+"年");
        System.out.println(now.getMonthValue()+"月");
        System.out.println(now.getDayOfMonth()+"日");
        System.out.println(now.getHour()+"时");
        System.out.println(now.getMinute()+"分");
        System.out.println(now.getSecond()+"秒");
        System.out.println(now.getNano()+"纳秒");

        //LocalDateTime转换成LocalDate,LocalTime
        LocalDate localDate = now.toLocalDate();
        LocalTime localTime = now.toLocalTime();

        //获取指定的时间对象
        LocalDateTime of = LocalDateTime.of(2008, 8, 8, 8, 8);
        System.out.println(of);

    }

}

修改年月日时分秒相关的方法

方法名说明
withHours、withMinute、withSecond、withNano修改时间,返回新的时间对象
plusHours、plusMinutes、plusSeconds、plusNanos把某个信息加多少,返回新的时间对象
minussHours、minusMinutes、minusSeconds、minusNanos把某个信息减多少,返回新的时间对象
equals、isBefore、isAfter判断两个时间对象,是否相等,在前还是在后

1、LocalDate、LocalTime、LocalDateTime对象时如何创建的?

now:当前时

of:指定时间

2、获取的相关方法是?

getXxx();

3、修改的相关方法是?

withXxx:修改

minusXxx:减

plusXxx:加

3、2日期格式化类

DateTimeFormatter:用于时间的格式化解析

方法名说明
ateTimFormatter ofPattern(格式)获取格式对象
String format(时间对象)按照指定方式格式化
package object.jdk8;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterDemo {
    /*
    用于时间的格式化和解析

    1、对象的获取:
            static DateTimFormatter ofPattern(格式):获取格式对象
    2、格式化:
            String format(时间对象):按照指定方式格式化
    3、解析:
            LocalDateTime.parse(“解析字符串”,格式化对象);
            LocalDate.parse("解析字符串",格式化对象);
            LocalTime.parse("解析字符串",格式化对象);
     */
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println("格式化之前"+now);

        //获取格式化对象
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");

        //格式化
        String result = formatter.format(now);
        System.out.println("格式化之后"+result);

        //解析
        String time = "2008年08月08日";
        LocalDate parse = LocalDate.parse(time, formatter);
        System.out.println(parse);
    }
}
3、3时间类

①Instant:时间戳/时间线

用于表示时间的对象,类似之前学习的Date

Instant instant = Instant.now();

System.out.println(“当前时间戳是:”+instant);

package object.jdk8;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class InstantDemo1 {
    /*
        Instant类:用于用于表示时间的对象,类似之前学习的Date
     */
    public static void main(String[] args) {
        Instant instant = Instant.now();
        System.out.println("当前时间戳是:"+instant);
        ZonedDateTime zonedDateTime = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println(zonedDateTime);
    }
}
方法名说明
static Instant now()获取当前时间的Instant对象(标准时间)
static Instant ofXxxx(long epocMilli)根据(秒/毫秒/纳秒)获取Instant对象
ZoneDateTime atZone(ZoneId zone)指定时区
boolean isXxx(Instant otherInstant)判断系列的方法
Instant minusXxx(long millisToSubtract)减少时间系列的方法
Instant plusXxx(long millisToSubtract)增加时间系列的方法

②ZoneId类:时区类

常见方法:
1、static Set getAvailableZoneIds():获取Java中支持的所有时区
2、static ZoneId systemDefault():获取系统默认时区
3、static ZoneId of(String zoneId):获取一个指定时区

package object.jdk8;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Set;

public class ZoneIdDemo {
    /*
        ZoneId类:时区类

        常见方法:
                1、static Set<String> getAvailableZoneIds():获取Java中支持的所有时区
                2、static ZoneId systemDefault():获取系统默认时区
                3、static ZoneId of(String zoneId):获取一个指定时区
     */
    public static void main(String[] args) {
        //1、获取Java中支持的所有时区
        Set<String> set = ZoneId.getAvailableZoneIds();
        System.out.println(set);
        System.out.println(set.size());

        //2、获取系统默认时区
        ZoneId zoneId = ZoneId.systemDefault();
        System.out.println(zoneId);

        //3、获取一个指定时区
        ZoneId of = ZoneId.of("Africa/Nairobi");
        System.out.println(of);

        ZonedDateTime zonedDateTime = Instant.now().atZone(of);
        System.out.println(zonedDateTime);

    }
}

③ZoneDateTime:带时区的时间对象

方法名说明
static ZoneDateTime now()获取当前时间的ZoneDateTime对象
static ZoneDateTime ofXxxx(…)获取指定时间的ZoneDateTime对象
ZoneDateTime withXxx(时间)修改时间系列的方法
ZoneDateTime minusXxx(时间)减少时间系列的方法
ZoneDateTime plusXxx(时间)增加时间系列的方法
3、4工具类

①Duration:用于计算两个“时间”间隔(秒,纳秒)

②Period:用于计算两个“日期”间隔(年、月、日)

package object.jdk8;

import java.time.LocalDate;
import java.time.Period;

public class PeriodDemo {
    //Period:用于计算两个“日期”间隔(年、月、日)
    public static void main(String[] args) {
        //此刻年月日
        LocalDate date = LocalDate.now();
        System.out.println(date);

        //昨天年月日
        LocalDate yesterday = LocalDate.of(2023,6,29);
        System.out.println(yesterday);

        //Period对象表示两个时间间隔对象
        Period period = Period.between(date, yesterday);//第二个桉参数减第一个参数

        System.out.println(period.getYears());//间隔多少年
        System.out.println(period.getMonths());//间隔多少月
        System.out.println(period.getDays());//间隔多少日
        System.out.println(period.toTotalMonths());//间隔总月份
    }
}

结果为:2023-06-30
2023-06-29
0
0
-1
0

③ChronoUnit:用于计算两个“日期”间隔

package object.jdk8;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class ChronoUnitDemo {
    public static void main(String[] args) {
        //本地的时间日期对象,此刻的
        LocalDateTime today = LocalDateTime.now();
        System.out.println(today);

        //生日时间
        LocalDateTime birthday = LocalDateTime.of(1996,11,18,10,8,18);
        System.out.println(birthday);

        System.out.println("相差的年数:"+ ChronoUnit.YEARS.between(birthday,today));
    }
}

结果为:2023-06-30T10:49:27.808
1996-11-18T10:08:18
相差的年数:26

案例:键盘录入用户生日,计算出用户的实际年龄

package object.jdk8;

import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Scanner;

public class Test {
    //案例:键盘录入用户生日,计算出用户的实际年龄
    public static void main(String[] args) {

        //1、键盘输入
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入您的生日:xxxx年x月x日");
        String birthday = sc.nextLine();

        //2、将获得的生日字符串解析为日期对象
        LocalDate birthdayDate = LocalDate.parse(birthday, DateTimeFormatter.ofPattern("yyyy年M月d日"));

        //3、获取今天的日期对象
        LocalDate today = LocalDate.now();

        //4、计算两个日期时间间隔
        long between = ChronoUnit.YEARS.between( birthdayDate,today);
        System.out.println("用户的实际年龄是:"+between);
    }
}

六、包装类

概念:将基本数据类型,包装成类(变成引用数据类型)

作用:变成类,就可以创建对象了,对象可以调用方法方便解决实际问题。

基本数据类型引用数据类型
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

1、Integer类

包装类:将基本数据类型,包装成类,变成引用类型

	手动装箱:调用方法,手动将基本数据类型,包装成类

            1、public Integer (int value):通过构造方法(不推荐)
            2、public static Integer valueOf(int i):通过静态方法

     手动拆箱:调用方法,手动将包装类,拆成(转换)基本数据类型
            1、public int intValue():以int类型返回该Integer的值


​ JDK5版本开始,出现了自动拆装箱
​ 1、自动装箱:可以将基本数据类型,直接赋值给包装类的变量
​ 2、自动拆箱:可以将包装类的数据,直接赋值给基本数据类型变量

​ 结论:基本数据类型,和对应的包装类,可以直接做运算了,不需要操心转换的问题了

package object.integer;

public class IntegerDemo {
 public static void main(String[] args) {
            int num = 10;
            Integer i1 = Integer.valueOf(num);//装箱

            int i = Integer.valueOf(num);//拆箱
            System.out.println(i);
            System.out.println(i1);


            System.out.println("----------------------------------");

            int n = 20;
            Integer n1 = 20;//装箱

            int j = n1;//拆箱
            System.out.println(n1);
            System.out.println(j);


        }

}

相关方法:
public static String toBinaryString(int i): 转二进制
public static String toOctalString(int i): 转八进制
public static String toHexString(int i): 转十六进制
public static int parseInt(String s): 将数字字符串,转换为数字

package object.integer;

public class IntegerMethodDemo {
 public static void main(String[] args) {
        int num = 100;
        System.out.println(Integer.toBinaryString(num));
        System.out.println(Integer.toOctalString(num));
        System.out.println(Integer.toHexString(num));

        String s = "123";
        System.out.println(Integer.parseInt("123") + 100);
    }
}

结果为:1100100
144
64
223

练习:已知字符串String s = “10,50,30,20,40”;请将该字符串转换成整数并存入数组,随后求出最大值打印在控制台

package object.integer;

public class IntegerTest {
    /*
        已知字符串String s = "10,50,30,20,40";
        请将该字符串转换成整数并存入数组,
        随后求出最大值打印在控制台
     */
    public static void main(String[] args) {
        String s = "10,50,30,20,40";

        //1、根据逗号做切割
        String[] sArr = s.split(",");

        //2、准备一个整数数组,用于存储转换后的数字
        int [] nums = new int[sArr.length];

        //3、遍历字符串数组
        for (int i = 0; i < sArr.length; i++) {
            //sArr[i]:每一个数字字符串
            //4、将数字字符串转换成整数,并存入数组
            nums [i] = Integer.parseInt(sArr[i]);
        }

        //5、求最大值
        int max = nums[0];
        for (int i = 0; i < nums.length; i++) {
            if(max<nums[i]){
                max = nums[i];
            }
        }
        System.out.println("最大值是:"+max);

    }
}

结果为:最大值是:50

2、包装类面试题

自动装箱的时候,如果装箱的数据范围是-127~128,==号比较的结果就是true,反之就是false

如果装箱的数据,不在-127~128之间,会重新创建新的对象

如果装箱的数据,在-127~128之间,不会创建新的对象,而是从底层数组中,取出一个提前创建好的Integer对象,并返回

即装箱在数据在范围内比较的是数据内容,不在范围内比较的是数据地址。

public class IntegerDemo{
	public static void main(String[] args){
		Integer i1 = 127;
		Integer i2 = 127;
		System,out.println(i1==i2);
		
		Integer i3 = 129;
		Integer i4 = 129;
		System,out.println(i3==i4);
	}
}

结果为:true,false

七、Arrays工具类

数组操作工具类,专门用于操作数组元素

方法名说明
public static String toString(类型[] a)将数组元素拼接为带有格式的字符串
public static boolean equals(类型[] a,类型[] b)比较两个数组内容是否相同
public static int binarySearch(int []a,int key)查找元素在数组中的索引(二分查找法:保证数组的元素是排好序的)
public static void sort(类型[]a)对数组进行默认升序排序

binarySearch:如果查找的元素在数组中不存在:返回(-(应该在的位置)-1

第十二章 正则表达式

概念:本质来说就是一个字符串,可以指定一些规则,来校验其他字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X5Q8BIjo-1689128132293)(D:\java\练习\day14\73ccee51885be2c894a21e5a55aeaa5.jpg)]

[]:对单个字符进行限制

package regex;

public class RegexDemo {
    /*
        1、QQ号正则
            不能以0开头
            全部是数字
            5-12位
        2、手机号正则
            必须是1 开头
            第二位:3456789
            全部是十足,必须是11位
        3、邮箱正则
            ZhangSan@itcast.cn
            ZhangSan@163.com
            123456@qq.com
            ZhangSan@sina.com
            ZhangSan@itcast.qq.cn
            ZhangSan@xxx.edu
            ZhangSan@xxx.org
     */
    public static void main(String[] args) {
        String qqRegex = "[1-9]\\d{4,11}";
        System.out.println("974383140".matches(qqRegex));

        String phoneRegex = "[1][3456789]\\d{9}";
        System.out.println("18709852083".matches(phoneRegex));

        String emailRegex = "\\w+[@][\\w&&[^_]]+(\\.[a-z]{2-3})+";
        System.out.println("974383140@qq.com ".matches(emailRegex));
    }
}

String类中与正则有关的常见方法

方法名说明
public String replaceAll(String regex,String newStr)按照正则表达式匹配内容进行替换
public String [] split(String regex)按照正则表达式匹配的内容进行分割字符串,返回一个字符串数组
package regex;

public class StringRegexMethod {
    public static void main(String[] args) {
        String s = "今天天气很晴朗,处处1好风光,好2风光,蝴蝶3忙呀,蜜蜂4也忙。";
        s = s.replaceAll("\\d","");
        System.out.println(s);
    }
}

使用正则做爬取

package regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PatternTest {
    /*
        需求:请把下面文本中的电话、邮箱、座机号码、热线都爬取出来
     */
    public static void main(String[] args) {
        String data = "来黑马程序员学习Java," +
                "电话:18666668888,18699997777或者联系" +
                "邮箱:boniu@itcast.cn 邮箱:bozai@itcast.cn 邮箱2:dlei0009@163.com" +
                "座机电话:01036517895,010-98951256 " +
                "热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";

        String regex = "[1][3-9]\\d{9}|\\w+[@][\\w&&[^_]]+(\\.[a-z]{2,3})+|[0]\\d{2,3}-?\\d{7,8}|400-?\\d{3}-?\\d{4}";

        //1、将正则表达式封装为pattern对象
        Pattern pattern = Pattern.compile(regex);

        //2、获取匹配器对象
        Matcher matcher = pattern.matcher(data);

        while(matcher.find()){
            System.out.println(matcher.group());
        }

    }
}

第十三章 String 类

一、String 类

特点:Java程序中所有的双引号字符串,都是String类的对象;

字符串在创建之后,其内容不可更改

字符串虽然不可改变,但是可以被共享

1、string类常见构造方法

方法名说明
public String():创建一个空白字符串,里面不含任何内容
public String(char[] chs)根据传入的字符数组,创建字符串对象
public String (String original)根据传入的字符串,来创建字符串对象

字符串对象,两种创建方式的区别

1、双引号直接创建

2、通过构造方法创建

2、String类用于比较的方法

字符串比较

public boolean equals(Object anObject):将此字符串与指定的对象比较

public boolean equalsIgnoreCase(String anotherString):将此String与另一个String比较,不考虑大小写

package string;

public class StringDemo {
    public static void main(String[] args) {
        String s1 = new String();
        System.out.println(s1);

        char[] chs = {'a','b','c'};
        String s2 = new String(chs);
        System.out.println(s2);

        String s3 = new String("def");
        System.out.println(s3);

        String s4 = "abc";
        String s5 = "ABC";
        System.out.println(s4.equals(s5)); 
        System.out.println(s4.equalsIgnoreCase(s5));
    }
}

案例:已知正确的用户名和密码,请用程序实现模拟用户登录,一共给三次机会,登录之后,给出相应的提示

package string;

import java.util.Scanner;

public class StringTest {
    /*
    已知正确的用户名和密码,请用程序实现模拟用户登录,一共给三次机会,登录之后,给出相应的提示
    分析:
        1、定义两个字符串变量,模拟已经存在的用户名和密码
        2、键盘录入用户输入的用户名,密码
        3、比对

     */
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        //1、定义两个字符串变量,模拟已经存在的用户名和密码
        String username = "admin";
        String password = "123456";




        for (int i = 1; i <= 3; i++) {
            //2、键盘录入用户输入的用户名,密码
            System.out.println("请输入用户名:");
            String inputUsername = sc.nextLine();

            System.out.println("请输入密码:");
            String userPassword = sc.nextLine();

            //3、比对
            if(inputUsername.equals(username) && userPassword.equals(password)){
                System.out.println("登录成功!");
                break;
            }else{
                if(i == 3){
                    System.out.println("今日此数已用尽,请明日再试。");
                }else{
                    System.out.println("登录失败,您还剩"+(3-i)+"次机会");
                }

            }
        }

    }
}

3、String字符串的遍历

String类用于遍历的方法:

public char[] toCharArray():将此字符串转换为一个新的字符数组

public char charAt(int index):返回指定索引处的char值

public int length():返回此字符串的长度

package string;

public class StringTest2 {
    public static void main(String[] args) {
        String s = "sarla";
	/*
	字符串遍历的第二种方式
	*/
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            System.out.println(c);
        }
    }

    /*
    字符串的第一种遍历方式
     */
    private static void print1() {
        String s = "sarla";
        char[] chars = s.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            System.out.print(chars[i]);
        }
    }
}

第一种方式效率更高

4、String字符串的截取、替换、切割

String字符串的截取方法:

public String substring(int beginIndex):根据传入的索引开始做截取,截取到字符串的末尾

public String substring(int beginIndex,int endIndex):根据传入的开始和结束索引,对字符串做截取,包含头,不包含尾

package string;

public class StringTest3 {
    /*
        String字符串的截取方法:

           public String substring(int beginIndex):

            public String substring(int beginIndex,int endIndex)
     */
    public static void main(String[] args) {
        String s = "sarla";
        String result = s.substring(2);
        System.out.println(result);

        String substring = s.substring(0, 2);
        System.out.println(substring);
    }
}

案例:手机号屏蔽。以字符串的形式从键盘接收一个手机号,将中间四位号码屏蔽,最终效果为:156****1234.

package string;

import java.util.Scanner;

public class StringTest4 {
    public static void main(String[] args) {
        System.out.println("请输入您的手机号:");
        Scanner sc = new Scanner(System.in);
        String phone = sc.nextLine();

        String substring1 = phone.substring(0, 3);
        String substring2 = phone.substring(7);
        System.out.println(substring1+"****"+substring2);
    }
}

String字符串的替换方法:

public String replace(CharSequence target,CharSequence replacement):用后面的参数替换前面的参数内容

String字符串的切割方法:

public String[] split(String regex):根据传入的字符串作为规则,切割当前字符串

package string;

public class StringTest5 {
    public static void main(String[] args) {
        String s = "sarla";
        String replace = s.replace("sar", "abv");
        System.out.println(replace);

        //public String[] split(String regex):根据传入的字符串作为规则,切割当前字符串
        String c = "192,168,1,1";
        String[] split = c.split(",");
        System.out.println(split.length);
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }
    }
}

二、StringBuilder类

作用:提高字符串的操作效率

概念:一个可变的字符序列,是字符串的缓冲区,将其理解为容器,这个容器可存储任意数据类型,但是只要进入这个容器,全部变成字符串。

1、构造方法

StringBuilder sb = new StringBuilder();
sb.append(100);
构造方法说明
public StringBuilder():构造一个不带任何字符的字符串生成器,其容量为16个字符;
public StringBuilder(String str):构造一个字符串生成器,并初始化为指定的字符串内容。
package stringbuilder;

public class StringBuilderDemo {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append(100);
        sb.append(23.5);
        sb.append(false);
        sb.append('中');
        sb.append("黑马程序员");

        System.out.println(sb);

        StringBuilder s = new StringBuilder("hhh");
        System.out.println(s);
    }
}

2、StringBuilder常用方法

方法名说明
public StringBuilder append(任意类型)添加数据,并返回对象本身
public StringBuilder reverse()反转容器中的内容,将缓冲区的内容进行反转
public int length()返回长度(字符出现的个数)
public String toString()通过toString()就可以实现把StringBuilder转换为String

链式编程:调用的方法,返回的结果是对象,就可以继续向下调用方法

问题:数据在StringBuilder中,但是要调用的方法在String中
解决:转换为String,再调用

案例:键盘输入一个字符串,程序判断该字符串是不是对称字符串,并在控制台打印出是或者不是

package stringbuilder;

import java.util.Scanner;

public class StringBuilderDemo3 {
    //键盘输入一个字符串,程序判断该字符串是不是对称字符串,并在控制台打印出是或者不是
    /*
    对称字符串:123321、111
    不对称字符串:123123
    思路:对拿到的字符串做反转操作,得到的字符串与原字符串相同即为对称字符串
     */
    public static void main(String[] args) {

        Scanner sc  = new Scanner(System.in);
        System.out.println("请输入一个对称字符串:");
        String num = sc.next();

        //将String转换为StringBuilder,为了调用内部的reverse方法
        StringBuilder sb = new StringBuilder(num);
        StringBuilder result = sb.reverse();

        //判断该字符串是否为对称字符串
        //String s = result.toString();
        if(result.toString().equals(num)){
            System.out.println("该字符串是对称字符串。");
        }else{
            System.out.println("该字符串不是对称字符串。");
        }

    }
}
package stringbuilder;

public class StringBuilderDemo4 {
    /*
    需求:
        定义一个方法,把int数组中的数据按照指定的格式拼接成一个字符串返回
        调用该方法,并在控制台打印出来
        例如:数组为int arr[] = {1,2,3};
        执行方法后的输出结果为:[1,2,3];
     */
    public static void main(String[] args) {
        int [] arr = {1,2,3};
        String result = arrToString(arr);
        System.out.println(result);
    }

    public static String arrToString(int [] arr){

        //创建StringBuilder,准备进行拼接
        StringBuilder s = new StringBuilder("[");

        //遍历数组,获取内部元素
        for (int i = 0; i < arr.length-1; i++) {

           //将获取的字符串存储到缓冲区
           s.append(arr[i]).append(",");
        }
        s.append(arr[arr.length-1]).append("]");
        return s.toString();
    }
}

第十四章 集合

集合体系结构

①单列集合

②双列集合

集合是一种容器,用来装数据,类似于数组

集合和数组的选择

数组:存储的元素个数固定不变

集合:存储的元素个数经常发生改变

collection(接口)

List(接口)Set(接口)
(实现类)ArrayList、LinkedList、Vector(实现类)HashSet、TreeSet
LinkedHashSet
添加的元素是有序、可重复、有索引添加的元素是无序、不重复、无索引

一、Collection集合

1、collection集合

是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的

方法名称说明
public boolean add(E e)把给定的对象添加到当前集合中
public void clear()清空集合中所有的元素
public boolean remove (E e)把给定的对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定的对象
public boolean isEmpty()判断当前集合是否为空
public int size()返回集合中元素的个数/集合的长度
package collection;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionDemo1 {
    // collection 是一个接口,因此我们学习他的方法时,只能创建他的实现类对象
    /*
        实现类:ArrayList
        为了学习Collection接口里面的方法
     */

    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();

        //1、添加元素
        //细节1:如果我们要往List系列集合中添加数据,那么方法永远返回true,因为list系列是允许元素重复的
        //细节2:如果我们要往Set系列集合中添加数据,如果当前要添加的元素不存在,方法返回true,表示添加成功;如果当前添加的元素已经存在,方法返回false,表示添加失败
        //因为set系列的集合不允许重复数据
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");

        System.out.println(coll);

        //2、清空元素
        //coll.clear();

        //3、删除元素
        //细节1:因为collection里定义的是共性方法,所以不能使用索引删除,只能通过元素的对象进行删除
        //细节2:方法会有一个返回值,删除成功返回true,删除失败返回false,当集合中不含有要被删除的元素则返回false
        coll.remove("aaa");
        System.out.println(coll);

        //4、判断是否有给定的对象
        //细节:底层是依赖于equals方法进行判断是否存在的
        //所以,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在JavaBean类中,一定要重写equals方法
        boolean d = coll.contains("ddd");
        System.out.println(d);

        //5、集合是否为空
        boolean empty = coll.isEmpty();
        System.out.println(empty);

        //6、获取集合的长度
        int size = coll.size();
        System.out.println(size);

    }
}

2、Collection的遍历方式

2、1迭代器遍历

迭代器不依赖索引

迭代器在Java中的类是iterator,迭代器是集合专用的遍历方式

Collection集合获取迭代器

方法名称说明
Iterator iterator()返回迭代器对象,默认指向当前集合的0索引

Iterator中的常用方法

方法名称说明
boolean hasNext()判断当前位置是否有元素,有元素返回true,没有元素返回false
E next()获取当前位置的元素,并将迭代器对象移向下一个位置

细节注意点:

①报错NoSuchElementException

②迭代器遍历完毕,指针不会复位

③循环中只能用一次next方法

④迭代器遍历时,不能用集合的方法进行增加或删除,如果非要删除可以使用迭代器的remove方法进行,但是暂时没有办法添加

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionDemo2 {
    /*
        Collection系列集合的三种遍历方式:
            1、迭代器遍历
            2、增强for循环遍历
            3、lambda表达式遍历

            迭代器遍历的三种方法:
                Iterator<E> iterator():获取一个迭代器对象
                boolean hasNext():判断当前位置是否有元素
                E next():获取当前位置的元素,并将迭代器对象移向下一个位置
     */
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");

        //2、获取迭代器对象
        Iterator<String> it = coll.iterator();

        //3、利用循环不断获取集合中的元素
        while (it.hasNext()) {

            //4、next方法的两件事情,获取元素并移动指针
            String next = it.next();
            System.out.println(next);
        }

        //当上面的循环结束后,迭代器的指针已经指向了最后没有元素的位置
        //System.out.println(it.next());//NoSuchElementException

        //如果我们要继续第二次遍历,只能再次获取一个新的迭代器对象
        Iterator<String> it2 = coll.iterator();
        while (it2.hasNext()) {
            String result = it2.next();
            System.out.println(result);
        }
    }
}
2、2增强for遍历

增强for的底层就是迭代器,为了简化迭代器的代码书写的;

所有的单列集合和数组才能用增强for进行遍历

格式:for(元素的数据类型 变量名:数组或者集合){

}

修改增强for中的变量,不会改变集合中原本的数据

package collection;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionDemo3 {
    /*
        增强for格式:for(元素的数据类型 变量名:数组或者集合){

        }
     */
    public static void main(String[] args) {
        //1、创建一个集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("zhangsan");
        coll.add("lisi");
        coll.add("wangwu");

        //2、使用增强for遍历集合中的元素
        for (String s : coll) {     //注意点:s其实就是一个第三方变量,在循环的过程中一次表示集合的每一个数据
            System.out.println(s);
            //修改增强for中的变量,不会改变集合中原本的数据
        }
    }
}
2、3 lambda表达式遍历
方法名称说明
default void forEach(Consumer<? super T>action):结合lambda遍历集合
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;

public class CollectionDemo4 {
    /*
        lambda表达式遍历
            格式:default void forEach(Consumer<? super T>action):
     */

    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("zhangsan");
        coll.add("lisi");
        coll.add("wangwu");

        //2、利用匿名内部类遍历
        //底层原理
        //方法的底层其实也会自己遍历集合,依次得到每一个元素
        //把得到的每一个元素传递给下面的accept方法
        coll.forEach(new Consumer<String>() {
            @Override
            //s一次表示集合中的每一个数据
            public void accept(String s) {
                System.out.println(s);

            }
//        });

        //lambda表达式
        coll.forEach((s)->System.out.println(s));
    }
}

总结:Collection是单列集合的顶层接口,所有方法被list和set系列集合共享

三种通用的遍历方式:

迭代器:在遍历的过程中需要删除元素,请使用迭代器遍历

增强for、lambda:仅仅想遍历,那么使用增强for或者lambda表达式

二、List集合

1、list集合

特点:

有序:存和取的元素顺序一致

有索引:可以通过索引操作元素

可重复:存储的元素可以重复

collection的方法list都继承了,特有的方法如下:

方法名称说明
void add(int index,E element)在此集合中的指定位置插入指定的元素,原来索引的元素会依次往后移动
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
package list;

import java.util.ArrayList;
import java.util.List;

public class ListDemo1 {

    public static void main(String[] args) {
        //1、创建一个集合
        List<String> list = new ArrayList<>();

        //2、添加元素
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        //void add(int index,E element):在此集合中的指定位置插入指定的元素
        //原来索引的元素会依次往后移动
        list.add(1,"ddd");

        //E remove(int index):删除指定索引处的元素,返回被删除的元素
        String remove = list.remove(2);
        System.out.println(remove);

        //E set(int index,E element):修改指定索引处的元素,返回被修改的元素
        String fff = list.set(1, "fff");
        System.out.println(fff);

        //E get(int index):返回指定索引处的元素
        String s = list.get(2);
        System.out.println(s);

        //3、打印集合
        System.out.println(list);


    }
}

在调用方法的时候,如果方法出现了重载现象,优先调用实参跟形参类型一致的那个方法。

2、List集合的遍历方式

迭代器遍历、列表迭代器遍历、增强for遍历、Lambda表达式遍历、普通for循环

package list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;

public class ListDemo2 {
    public static void main(String[] args) {
        //1、创建一个集合并添加元素
        List<String> list = new ArrayList<>();
        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");

        //迭代器遍历,获取迭代器对象
        Iterator<String> iterator = list.listIterator();

        //利用循环去获取集合中的元素
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println(next);
        }
        System.out.println("-----------------------");

        //增强for遍历
        for (String s : list) {
            System.out.println(s);
        }
        System.out.println("-----------------------");

        //lambda表达式遍历
        list.forEach(s -> System.out.println(s));
        System.out.println("-----------------------");

        //普通for循环遍历
        for (int i = 0; i < list.size(); i++) {
            String result = list.get(i);
            System.out.println(result);
        }
        System.out.println("-----------------------");

        //列表迭代器遍历
        //获取一个列表迭代器的对象,里面的指针也是默认指向0索引的
        //额外添加了一个方法,在遍历的过程中,可以添加元素
        ListIterator<String> itlist = list.listIterator();
        while (itlist.hasNext()) {
            String result1 = itlist.next();
            if ("lisi".equals(result1)) {
                itlist.add("liuliu");
            }
        }
        System.out.println(list);

    }
}

结果为:zhangsan lisi wangwu

zhangsan lisi wangwu

zhangsan lisi wangwu

zhangsan lisi wangwu

[zhangsan, lisi, liuliu, wangwu]

五种遍历方式对比:

迭代器遍历 :在遍历的过程中需要删除元素,请使用迭代器

列表迭代器:在遍历的过程中需要添加元素,请使用列表迭代器

增强for遍历:仅仅想遍历

Lambda表达式:仅仅想遍历

普通for:如果遍历的时候想操作索引,可以使用普通for

三、ArrayList集合

构造方法:

public ArrayList():创建一个空的集合容器

集合获取长度使用size

现象:可以添加任意类型的数据

弊端:数据不够严谨

**<>:泛型:**使用泛型可以对集合中存储的数据,进行类型限制
泛型中不允许编写基本数据类型,如果需要使用基本数据类型,则需要使用基本数据类型的包装类,变成引用数据类型

ArrayList集合常用的成员方法

方法名说明
public boolean add(E e)将指定元素添加到此集合的末尾
public void add(int index,E element)在此集合中的指定位置插入指定的元素
public E get (int index)返回指定索引处的元素
public int size()返回集合中的元素的个数
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public boolean remove(Object o)删除指定的元素,返回给删除是否成功
public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
package arraylist;

import java.util.ArrayList;

public class ArrayListDemo2 {

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        list.add("张三");
        list.add("李四");
        list.add("王五");

        String s = list.get(2);
        System.out.println(s);

    }

    private static void updateMethod() {
        ArrayList<String> list = new ArrayList<>();

        list.add("张三");
        list.add("李四");
        list.add("王五");

        list.set(1,"哈哈哈");
        System.out.println(list);
    }

    private static void removeMethod() {
        ArrayList<String> list = new ArrayList<>();

        list.add("张三");
        list.add("李四");
        list.add("王五");

        list.remove(2);
        System.out.println(list);
    }

    private static void addMethod() {
        ArrayList<String> list = new ArrayList<>();

        list.add("张三");
        list.add("李四");
        list.add("王五");

        list.add(1,"刘老麻");
        System.out.println(list);
    }
}

案例:

package arraylist;

import domain.Student;

import java.util.ArrayList;

public class ArrayListDemo3 {
    /*
        需求:创建一个存储字符的集合,内部存储3个字符串元素,使用程序实现在控制台遍历该集合
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
     */
    /*
        需求:创建一个存储字符串的集合,内部存储字符串元素
        钢门吹雪,西城狂鸭,张三,李四,王五
        使用程序实现在控制台遍历该集合,将四个字的人名打印在控制台
        ArrayList<String> list = new ArrayList<>();
        list.add("钢门吹雪");
        list.add("西城狂鸭");
        list.add("张三");
        list.add("李四");
        list.add("王五");

        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if(s.length() == 4){
                System.out.println(s);
            }
        }
     */
    /*
        需求:创建一个存储学生对象的集合,存储3个学生对象,在控制台展示出年龄小于18岁的学生信息
     */
    public static void main(String[] args) {
        Student stu1 = new Student("张三",26);
        Student stu2 = new Student("李四",13);
        Student stu3 = new Student("王五",15);

        ArrayList<Student> list = new ArrayList<>();
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        for (int i = 0; i < list.size(); i++) {
            Student stu = list.get(i);
            if(stu.getAge()<18){
                System.out.println(stu.getName()+"-------"+stu.getAge());

            }
        }


    }
}
package arraylist;

import domain.Student;

import java.util.ArrayList;
import java.util.Scanner;

public class ArrayListDemo4 {
    /*
         需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
                学生的姓名,年龄来自键盘录入
     */
    public static void main(String[] args) {

        //1、准备集合容器,用于存储学生对象
        ArrayList<Student> list = new ArrayList<>();

        for(int i = 0;i<3;i++){
            System.out.println("请输入第"+i+"个学生的信息:");
            addStudent(list);
        }


        for (int i = 0; i < list.size(); i++) {
            Student stu = list.get(i);
            System.out.println(stu.getName()+"--------"+stu.getAge());
        }
    }

    private static void addStudent(ArrayList<Student> list) {
        //2、键盘录入学生信息
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学生的姓名:");
        String name = sc.next();
        System.out.println("请输入学生年龄:");
        int age = sc.nextInt();

        //3、将录入的学生信息封装为学生对象
        Student stu = new Student(name,age);

        //4、将封装好的学生对象存入集合
        list.add(stu);
    }
}

ArrayList集合底层原理:

①利用空参创建的集合,在底层创建一个默认长度为0的数组

②添加第一个元素时,底层会创建一个新的长度为10的数组

③存满时,会扩容1.5倍

④如果一次添加多个元素,1.5倍还放不下,则新创建的数组长度以实际为准

四、LinkedList集合

底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的。

每个结点包含三个部分:分别为上一个结点的地址,数据值和下一个结点的地址。

LinkedList本身 多了很多直接操作首尾元素的特有API

特有方法说明
public void sddFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

五、泛型

概念:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查

格式:<数据类型>

泛型中只支持引用数据类型,基本数据类型需要使用其对应的包装类

泛型的好处:

统一了数据类型;把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来

扩展知识点:Java中的泛型是伪泛型

指定泛型的具体数据类型后,传递数据时,可以传入该类类型或者其子类类型;如果不写泛型,类型默认是Object类。

1、泛型类

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类

格式:修饰符 class 类名<类型>{

}

举例:public class ArrayList{

}

此处的E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型,可以写成:T,E,K,V等等。

泛型类的书写:

package generics;

import java.util.Arrays;

/*
        当我在编写一个类的时候,如果不确定类型,那么这个类就可以定义为泛型类
         */
public class MyArrayList<E> {
    Object[] obj = new Object[10];
    int size;
    /*
        E:表示的是不确定的数据类型,该类型在类名后面已经定义过了,e表示的是形参的名字,是个变量名

     */
    public boolean add(E e){
        obj[size] = e;
        size++;
        return true;
    }

    public E get(int index){
        return (E)obj[index];
    }

    @Override
    public String toString() {
        return Arrays.toString(obj);
    }

泛型类的使用:

package generics;

public class GenericsDemo {
    public static void main(String[] args) {
        MyArrayList<String>  list = new MyArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        System.out.println(list);

        MyArrayList<Integer> list1 = new MyArrayList<>();
        list1.add(123);
        list1.add(456);
        list1.add(457);

        int i = list1.get(0);
        System.out.println(i);

        System.out.println(list1);
    }
}

2、泛型方法

方法中形参类型不确定时:

①可以使用类名后面定义的泛型,该泛型在所有的方法里都可以使用

②在方法申明上定义自己的泛型,该泛型只有本方法可以使用

格式:修饰符<类型> 返回值类型 方法名(类型 变量名){

}

举例:publicvoid show(T t){

}

泛型方法的定义:

package generics;

import java.util.ArrayList;

public class ListUtil{
    private ListUtil(){

    }

    //类中定义一个静态方法addAll,用来添加多个集合的元素
    /*
        参数一:集合
        参数二~最后:要添加的元素
     */
    public static<E>  void addAll(ArrayList<E> list,E e1,E e2,E e3,E e4){
        list.add(e1);
        list.add(e2);
        list.add(e3);
        list.add(e4);

    }

    public static<E>  void addAll2(ArrayList<E> list,E...e){//E...e:表示可变参数,可以添加多个元素
        for (E e1 : e) {
            list.add(e1);
        }
    }
}

泛型方法的调用:

package generics;

import java.util.ArrayList;

public class GenericsDemo1 {
    /*
        定义一个工具类:ListUtil
        类中定义一个静态方法addAll,用来添加多个集合的元素
     */
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        ListUtil.addAll(list,"aaa","bbb","ccc","ddd");
        System.out.println(list);

        System.out.println("------------------------");

        ArrayList<Integer> list1 = new ArrayList<>();
        ListUtil.addAll2(list1,1,3,3,3,3,3,3,3,3,3,2);
        System.out.println(list1);
    }
}

3、泛型接口

格式:修饰符 interface 接口名<类型>{

}

举例:public interface List{

}

使用方式:

①实现类给出具体类型

②实现类延续泛型,创建对象时再确定类型

package generics;

import java.util.ArrayList;

public class GenericsDemo2 {
    /*
        使用方式:
                ①实现类给出具体类型
                ②实现类延续泛型,创建对象时再确定类型
     */
    public static void main(String[] args) {
        //①实现类给出具体类型
        MyArrayList2 list1 = new MyArrayList2();
        list1.add("aaa");
        list1.add("bbb");
        list1.add("ccc");

        System.out.println(list1);

        //②实现类延续泛型,创建对象时再确定类型
        MyArrayList3<String> list2 = new MyArrayList3<>();
        list2.add("111");
        list2.add("222");
        list2.add("333");

        System.out.println(list2);
    }
}

package generics;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class MyArrayList2 implements List<String> {
    @Override
    public int size() {
        return 0;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<String> iterator() {
        return null;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return null;
    }

    @Override
    public boolean add(String s) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean addAll(Collection<? extends String> c) {
        return false;
    }

    @Override
    public boolean addAll(int index, Collection<? extends String> c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {

    }

    @Override
    public String get(int index) {
        return null;
    }

    @Override
    public String set(int index, String element) {
        return null;
    }

    @Override
    public void add(int index, String element) {

    }

    @Override
    public String remove(int index) {
        return null;
    }

    @Override
    public int indexOf(Object o) {
        return 0;
    }

    @Override
    public int lastIndexOf(Object o) {
        return 0;
    }

    @Override
    public ListIterator<String> listIterator() {
        return null;
    }

    @Override
    public ListIterator<String> listIterator(int index) {
        return null;
    }

    @Override
    public List<String> subList(int fromIndex, int toIndex) {
        return null;
    }
}

package generics;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class MyArrayList3<E> implements List<E> {
    @Override
    public int size() {
        return 0;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<E> iterator() {
        return null;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return null;
    }

    @Override
    public boolean add(E e) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {

    }

    @Override
    public E get(int index) {
        return null;
    }

    @Override
    public E set(int index, E element) {
        return null;
    }

    @Override
    public void add(int index, E element) {

    }

    @Override
    public E remove(int index) {
        return null;
    }

    @Override
    public int indexOf(Object o) {
        return 0;
    }

    @Override
    public int lastIndexOf(Object o) {
        return 0;
    }

    @Override
    public ListIterator<E> listIterator() {
        return null;
    }

    @Override
    public ListIterator<E> listIterator(int index) {
        return null;
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        return null;
    }
}

4、泛型的继承和通配符

泛型不具备继承性,但是数据具备继承性

使用泛型的通配符可以解决只想要接受某种特定的类型的问题
通配符:?表示的是不确定的类型,它可以进行类型的限定
? extends E:表示可以传递E或者E所有的子类类型
?super E:表示可传递E或者E所有的父类类型

应用场景:
1、如果我们在定义类、方法、接口的时候,类型不确定,就可以定义泛型类、泛型方法、泛型接口
2、如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型通配符
泛型通配符的关键点:可以限定类型的范围

package generics;

import java.util.ArrayList;

public class GenericsDemo4 {
    /*
        需求:定义一个方法,形参是一个集合,但是集合中的数据类型不确定
     */
    /*
        应用场景:
            1、如果我们在定义类、方法、接口的时候,类型不确定,就可以定义泛型类、泛型方法、泛型接口
            2、如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型通配符
            
        泛型通配符的关键点:
                可以限定类型的范围
     */
    public static void main(String[] args) {
        ArrayList<ye> list1 = new ArrayList<>();
        ArrayList<fu> list2 = new ArrayList<>();
        ArrayList<zi> list3 = new ArrayList<>();

        //调用method方法
        method(list1);
        method(list2);
        method(list3);
    }
    //弊端:此时它可以接收任意的数据类型
    //使用泛型的通配符可以解决只想要接受某种特定的类型的问题
    //通配符:?表示的是不确定的类型,它可以进行类型的限定
    //? extends E:表示可以传递E或者E所有的子类类型
    //?super E:表示可传递E或者E所有的父类类型
    public static void method(ArrayList<? extends ye> list){
        
    }

案例:

六、Set系列集合

特点:

无序:存取顺序不一致

不重复:可以去除重复

无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

set集合的实现类:

HashSet:无序,不重复,无索引

LinkedHashSet:有序,不重复,无索引

TreeSet:可排序,不重复,无索引

Set接口中的方法上基本与collection的API一致

package set;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Consumer;

public class SetDemo1 {
    /*
        利用set系列的集合,添加字符串,并使用多种方式进行遍历
        迭代器
        增强for
        lambda表达式
     */
    public static void main(String[] args) {
        //1、创建一个set集合的对象
        Set<String> s = new HashSet<>();

        //2、添加元素
        //如果当前元素是第一次添加,那么可以添加成功,返回true
        //如果当前元素不是第一次添加,那么添加失败,返回false
        s.add("张三");
        s.add("张三");
        s.add("李四");
        s.add("王五");

       // System.out.println(s);//[李四, 张三, 王五]由此结果得知色图集合是无序的

        //迭代器遍历
        Iterator<String> it = s.iterator();
        while (it.hasNext()) {
            String next = it.next();
            System.out.println(next);
        }

        System.out.println("------------");

        //增强for循环遍历
        for (String s1 : s) {
            System.out.println(s1);
        }

        System.out.println("------------");

        //lambda表达式遍历
        s.forEach(s1 ->  System.out.println(s1));

    }
}

七、HashSet集合

底层原理:采取哈希表的形式存储数据

哈希表是一种对于增删改查数据性能都较好的结构

哈希表组成:JDK8之前:数组+链表

​ JDK8之后:数组+链表+红黑树

哈希值:对象的整数表现形式;int index = (数组长度-1)&哈希值;

①根据hashcode方法计算出来的int类型的整数

②该方法定义在object类中,所有对象都可以调用,默认使用地址值进行计算

③一般情况下,会重写hashcode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点:

①如果没有重写hashcode方法,不同对象计算出的哈希值是不同的

②如果已经重写hashcode方法,不同的对象只要属性相同,计算出的哈希值就是一样的

③在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能是一样的。(哈希碰撞)

HashSet底层原理:

①创建一个默认长度为16,默认加载因子为0.75的数组,数组名table(该加载因子是为了数组扩容的)

②根据元素的哈希值跟数组的长度计算出应存入的位置

③判断当前位置是否为null,如果是null就直接存入

④如果当前位置不是null,表示有元素,则调用equals方法比较属性值

⑤一样:不存;不一样:存入数组,形成链表

JDK8以前:新元素存入数组,老元素挂在新元素下面

JDK8以后:新元素存入数组,直接挂在老元素下面

当链表长度大于8而且数组长度大于等于64的时候,链表就会自动转为红黑树,提高了查找效率;

如果集合中存储的是自定义对象,必须重写hashcode和equals方法

HashSet三大特性的原理

**HashSet为什么存和取的顺序不一样:**因为取的时候是按从前往后依次遍历每一个链表的顺序,而存储的时候是通过hash计算,位置不确定;

**HashSet为什么没有索引:**因为底层的结构不唯一,结构太多导致确定索引困难,所以舍弃了索引机制;

**HashSet利用什么机制保证去重的:**通过hashcode方法计算出需要存储的位置,再用equals方法比较是否相同,相同的就舍弃

三个特性的代码:

package set;

public class HashSetDemo1 {
    public static void main(String[] args) {
        //1、创建对象
        Student s1 = new Student("zhangsan",23);
        Student s2 = new Student("zhangsan",23);

        //2、如果没有重写,不同对象计算出的哈希值是不同的
        //如果已经重写hashcode方法,不同的对象只要属性相同,计算出的哈希值就是一样的
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

        //3、在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能是一样的。
        System.out.println("abc".hashCode());//96354
        System.out.println("acD".hashCode());//96354
    }
}
package set;

import java.util.Objects;

public class Student {
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    @Override//重写hashcode和equals方法的快捷键alt+insert
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

案例:需求:创建一个存储学生对象的集合,存储多个学生对象
使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们被认为是同一个对象

package set;

import java.util.HashSet;

public class HashSetDemo2 {
    /*
        需求:创建一个存储学生对象的集合,存储多个学生对象
        使用程序实现在控制台遍历该集合
        要求:学生对象的成员变量值相同,我们被认为是同一个对象
     */
    public static void main(String[] args) {
        //1、创建三个学生对象
        Student s1 = new Student("张三",23);
        Student s2 = new Student("李四",24);
        Student s3 = new Student("王五",25);
        Student s4 = new Student("张三",23);

        //2、创建集合用于存储学生对象
        HashSet<Student> students = new HashSet<>();

        //3、添加元素
        System.out.println(students.add(s1));
        System.out.println(students.add(s2));
        System.out.println(students.add(s3));
        System.out.println(students.add(s4));

        //4、打印集合
        System.out.println(students);
    }

}
//学生Student类见上一个代码中

八、LinkedHashSet集合

底层原理:有序,不重复,无索引

这里的有序是指保证存储和取出的元素的顺序是一致的

有序的原理:底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的数据;此时遍历顺序与hashset不一样,是按照双向链表的方式进行遍历

package set;

import java.util.LinkedHashSet;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        //1、创建四个学生对象
        Student s1 = new Student("zhangsan",23);
        Student s2 = new Student("lisi",24);
        Student s3 = new Student("wangwu",25);
        Student s4 = new Student("zhangsan",23);

        //2、创建一个集合对象
        LinkedHashSet<Student> students = new LinkedHashSet<>();

        //3、往集合对象里添加元素
        System.out.println(students.add(s1));
        System.out.println(students.add(s2));
        System.out.println(students.add(s3));
        System.out.println(students.add(s4));
        
        //4、打印集合
        System.out.println(students);
    }
}//学生Student类见上一个代码中

结果为:true
true
true
false
[Student{name = zhangsan, age = 23}, Student{name = lisi, age = 24}, Student{name = wangwu, age = 25}]

如果要数据去重,默认使用hashset,只有要求去重且存取有序的时候,才使用LinkedHashSet。

九、TreeSet集合

特点:不重复,无索引,可排序

可排序:按照元素的默认规则(从小到大)排序。

TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都比较好

package set;

import java.util.Iterator;
import java.util.TreeSet;
import java.util.function.Consumer;

public class TreeSetDemo1 {
    /*
        需求:利用TreeSet存储数据并进行排序
     */
    public static void main(String[] args) {
        //1、创建TreeSet集合
        TreeSet<Integer> ts = new TreeSet<>();

        //2、添加元素
        ts.add(123);
        ts.add(789);
        ts.add(678);

        //3、打印集合
        System.out.println(ts);

        //4、遍历集合
        //迭代器遍历
        Iterator<Integer> its = ts.iterator();
        while (its.hasNext()) {
            Integer a = its.next();
            System.out.println(a);
        }
        System.out.println("-------------------------");

        //增强for遍历
        for (Integer s : ts) {
            System.out.println(s);
        }

        System.out.println("-------------------------");
        //lambda表达式遍历
        ts.forEach(integer-> System.out.println(integer));
    }
}

TreeSet集合默认的规则:

对于数值类型:Intger,Double,默认按照从小到大的顺序进行排序

对于字符或者字符串类型:按照字符在ASCII码表的数字升序进行排序

TreeSet的两种比较方式:默认采取第一种比较方式,当第一种比较方式无法满足需求的时候就采取第二种方式

①默认排序/自然排序:JavaBean类实现Comparable接口指定比较规则

package set;

public class Student1 implements Comparable<Student1>{
    private String name;
    private int age;


    public Student1() {
    }

    public Student1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student1{name = " + name + ", age = " + age + "}";
    }

    @Override
    public int compareTo(Student1 o) {
        //指定排序规则
        //只看年龄,按照升序排列
        return  this.getAge()- o.getAge();

        //this:表示当前要添加的元素
        //o:表示已经再红黑树存在的元素
        //返回值:负数:认为要添加的元素是小的,存左边
        //       正数:认为要添加的元素是大的,存右边
        //        0:认为添加的元素已经存在,舍弃不存
    }
}
package set;

import java.util.TreeSet;

public class TreeSetDemo2 {
    /*
        需求:创建TreeSet集合,并添加三个学生对象
            学生对象属性:
                姓名,年龄
                要求按照学生的年龄进行排序
                同年龄的按照姓名首字母排序(不考虑中文)
                同姓名,同年龄认为是一个人
            排序方法一:student1类实现Cpmparable接口,重写里面的抽象方法,再指定比较规则
     */
    public static void main(String[] args) {
        //1、创建三个学生对象
        Student1 s1 = new Student1("zhangsan",23);
        Student1 s2 = new Student1("lisi",24);
        Student1 s3 = new Student1("wangwu",25);

        //2、创建集合对象
        TreeSet<Student1> ts = new TreeSet<>();

        //3、添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);

        //4、打印集合
        System.out.println(ts);

        //TreeSet 底层是红黑树,不是哈希表,因此不需要重写hashcode和equals方法

    }
}

② 比较器排序:创建TreeSet对象的时候,传递比较器Comparator指定规则

package set;

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDemo3 {
    /*
        需求:请自行选择比较器排序和自然排序两种方式:
        要求:存入四个字符串,“c","ab","df","qwer"
        按照长度排序。如果一样长则按照首字母排序
     */
    public static void main(String[] args) {
        //1、创建集合
        /*o1表示当前要添加的元素
          o2表示已经在红黑树存在的元素
          返回值跟之前一样
          lambda表达式改写如下
         */
        TreeSet<String> ts = new TreeSet<>((o1, o2) ->{
                //按照长度排序
                int i = o1.length() - o2.length();
                //如果按照首字母排序
              i = i == 0 ? o1.compareTo(o2) : i;
                return i;
        });

        //2、添加元素
        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");

        //3、打印集合
        System.out.println(ts);
    }
}

结果为:[c, ab, df, qwer]

练习:student

package set;

public class Student2 implements Comparable<Student2>{
    private String name;
    private int age;
    private int Chinese;
    private int Math;
    private int English;


    public Student2() {
    }

    public Student2(String name, int age, int Chinese, int Math, int English) {
        this.name = name;
        this.age = age;
        this.Chinese = Chinese;
        this.Math = Math;
        this.English = English;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return Chinese
     */
    public int getChinese() {
        return Chinese;
    }

    /**
     * 设置
     * @param Chinese
     */
    public void setChinese(int Chinese) {
        this.Chinese = Chinese;
    }

    /**
     * 获取
     * @return Math
     */
    public int getMath() {
        return Math;
    }

    /**
     * 设置
     * @param Math
     */
    public void setMath(int Math) {
        this.Math = Math;
    }

    /**
     * 获取
     * @return English
     */
    public int getEnglish() {
        return English;
    }

    /**
     * 设置
     * @param English
     */
    public void setEnglish(int English) {
        this.English = English;
    }

    public String toString() {
        return "Student2{name = " + name + ", age = " + age + ", Chinese = " + Chinese + ", Math = " + Math + ", English = " + English + "}";
    }

    @Override
    public int compareTo(Student2 o) {
        int sum1 = this.getChinese()+this.getMath()+this.getEnglish();
        int sum2 = o.getChinese()+o.getMath()+o.getEnglish();

        int i =sum2-sum1;

        i = i == 0 ? this.getChinese()-o.getChinese():i;
        i = i == 0 ? this.getMath()-o.getMath():i;
        i = i == 0 ? this.getEnglish()-o.getEnglish():i;
        i = i == 0 ? this.getAge()-o.getAge():i;
        i = i == 0 ? this.getName().compareTo(o.getName()):i;
        return i;
    }
}

测试类

package set;

import java.util.TreeSet;

public class TreeSetDemo4 {
    /*
        需求:创建5个学生对象
        属性:(姓名,年龄,语文成绩、数学成绩、英语成绩)
        按照总分的高低输出到控制台
        如果总分一样,按照语文成绩排序
        如果语文成绩一样,按照数学成绩排序
        如果数学成绩一样,按照英语成绩排序
        如果英文成绩一样,按照年龄排序
        如果年龄一样按照姓名字母排序
        如果都一样认为这个人不存在

        排序:第一种,默认排序/自然排序
             第二种,比较器排序
     */
    public static void main(String[] args) {
        //1、创建学生对象
        Student2 s1 = new Student2("zhangsan",23,90,99,50);
        Student2 s2 = new Student2("lisi",24,90,98,50);
        Student2 s3 = new Student2("wangwu",25,95,100,30);
        Student2 s4 = new Student2("zhaoliu",26,60,99,70);
        Student2 s5 = new Student2("qianqi",26,70,80,70);

        //2、创建集合对象
        TreeSet<Student2> ts = new TreeSet<>();

        //3、添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);

        //4、遍历集合
        for (Student2 t : ts) {
            System.out.println(t);
            System.out.println("总分"+(t.getChinese()+t.getMath()+t.getEnglish()));
        }
    }

结果为:

Student2{name = zhangsan, age = 23, Chines e = 90, Math = 99, English = 50}
总分239
Student2{name = lisi, age = 24, Chinese = 90, Math = 98, English = 50}
总分238
Student2{name = zhaoliu, age = 26, Chinese = 60, Math = 99, English = 70}
总分229
Student2{name = wangwu, age = 25, Chinese = 95, Math = 100, English = 30}
总分225
Student2{name = qianqi, age = 26, Chinese = 70, Math = 80, English = 70}
总分220

总结

1、如果想要集合中的元素可重复

用ArrayList集合,基于数组的。(地址是连续的查询和修改都很快用的最多)

2、如果想要集合中的元素可重复,且当前的增删操作明显多于查询

用LinkedList集合,基于链表的

3、如果相对集合中的元素去重

用HashSet集合,基于哈希表的。(用的最多)

4、如果相对集合中的元素去重,而且保证存取顺序

用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet

5、如果想对集合中的元素进行排序

用TreeSet集合,基于红黑树。后续也可以用List集合实现排序

第十五章 数据结构

概念:计算机底层存储数据、组织数据的方式,指数据相互之间以什么样的方式排列在一起的。

数据结构是为了更加方便的管理和使用数据结构,需要结合具体的业务场景来进行选择

常见的数据结构:

①栈

②队列

③数组

④链表

⑤二叉树

⑥二叉查找树

⑦平衡二叉树

⑧红黑树

一、栈

栈的特点:后进先出,先进后出

数据进入栈的过程称为:压栈或者进栈

数据离开栈的过程称为:弹栈或者出栈

栈内存:方法运行的时候进栈,执行完毕出栈

堆内存:new出来的新对象都在这里

方法区:字节码文件

二、队列

队列的特点:先进先出,后进后出

数据进入队列的过程称为:入队列

数据离开队列的过程称为:出队列

三、数组

数组的特点:是一种查询快,增删慢的模型

查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)

删除效率低:要将原始数据删除,同时后面每个数据前移

添加效率低:添加位置后的每个数据都要后移,再添加元素

四、链表

链表中的元素叫结点,每一个结点都是独立的对象,都有属于自己的地址,每个链表的第一个结点都是头结点

结点会存储——具体的数据、下一结点的地址

链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址

特点:

链表查询慢,无论查询哪个数据都需要从头开始查找

链表的增删相对快,首尾操作极快

五、二叉树

每个节点会记录以下内容:父节点地址,值,左子节点地址,右子节点地址

度:每一个节点的子节点的数量

二叉树:二叉树中,任意节点的度<=2

树高:树的总层数

根节点:最顶层的节点

1、二叉查找树

特点:每一个节点上最多有两个子节点

​ 任意节点左子树上的值都小于当前节点

​ 任意节点右子树上的值都大于当前节点

添加节点规则:小的存左边;大的存右边,一样的不存

遍历方式:

①前序遍历:从根节点开始,然后按照当前节点,左子节点,右子节点的顺序遍历

②中序遍历:从最左边的子节点开始,然后按照左子节点,当前节点,右子节点的顺序遍历

③后序遍历:从最左边的子节点开始,然后按照左子节点,右子节点,当前节点的顺序遍历

④层序遍历:从根节点开始一层一层地遍历

2、平衡二叉树

规则:任意节点左右子树高度差不超过1

2、1平衡二叉树旋转机制

旋转分为左旋和右旋两种机制,只有当添加了节点后不再是一颗平衡二叉树后才会触发旋转机制

①左旋:

步骤:从添加的节点开始,不断地往父节点找不平衡的节点

1、以不平衡的点作为支点

2、把支点左旋降级,变成左子节点 || 将根节点的右侧往左拉

3、晋升原来的右子节点 || 原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

②右旋:

步骤:从添加的节点开始,不断地往父节点找不平衡的节点

1、以不平衡的点作为支点

2、把支点右旋降级,变成右子节点 || 将根节点的左侧往右拉

3、晋升原来的左子节点 || 原先的左子节点变成新的父节点,并把多余的右子节点出让,给已经降级的根节点当左子节点

需要旋转的四种情况:

①左左:(一次右旋)当根节点的左子树的左子树有节点插入,导致二叉树不平衡

②左右:(先局部左旋,再整体右旋)当根节点的左子树的右子树有节点插入,导致二叉树不平衡

③右右:(一次左旋)当根节点的右子树的右子树有节点插入,导致二叉树不平衡

④右左:(先局部右旋,再整体左旋)当根节点的右子树的左子树有节点插入,导致二叉树不平衡

3、红黑树

是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色。

每一个节点可以是红色或者黑色,红黑树不是高度平衡的,它的平衡是通过”红黑规则“进行实现的。

红黑规则:

①每一个节点或是红色的,或者是黑色的

②根节点必须是黑色的

③如果一个节点没有子节点也没有父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的

④如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)

⑤对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

添加节点的规则:

默认颜色:添加的节点默认是红色的(效率高)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QRrTe6FG-1689128132294)(D:\IDEA\练习\day18\红黑树添加节点的规则.jpg)]

红黑树的增删改查的性能都很好

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值