前言

一,基本计算机理论知识
二、进制转换
三、环境配置
四、包的命名规范,域名.公司名.项目名

一、变量和基本类型

一、变量

1,变量—存储数据
2,由于存放的数据内容大小不一样,导致所需要存储的单元的大小不一样,由此引出—“数据类型”
数据类型图示:

数据类型.jpg
扩展:String不是基本数据类型,它定义的是对象

3,变量名拿来唯一标识一个存储单元,以方便访问该变量

二、单个字节表示的整数范围(*)(面试的基础考点)

1,单个字节表示八位二进制位,其中最高位(最左边)代表符号位
0为非负数,1为负数
2,具体的整数范围:

  • 非负数:0000 0000~0111 1111( 0~127)
  • 负数所能表示的整数范围:1000 0000~1111 1111 (-128~-1) (-27~-20)

    1000 0000=>先减1: 0111 1111
    =>按位取反: 1000 0000
    =>转十进制:128
    =>添加负号: -128
    1111 1111 =>先减1: 1111 1110
    =>按位取反: 0000 0001
    =>转为十进制:1
    =>添加负号:-1
    综上所述:
    单个字节所能表示的整数范围:-128~127
    三、整数类型

    byte、short、int 、long
    1,byte 占内存空间1个字节,8位,表示范围-2^7~2^7-1
    2, short占内存空间2个字节,16位,表示范围-2^15~2^15-1 (3万多之间)
    3, int类型在内存空间中占4个字节,32位,表示范围:-2^31~2^31-1(亿级)
    4,long类型在内存空间中占8个字节,64位,表示范围:-2^63~2^63-1(百亿级别以上)

在java中,默认为int类型
注意:

  • 如果赋的值超过这个类型的范围会出现错误,所以一般也使用int类型,这样可以避免一些错误。
  • 如果赋值给int类型的数字超过int类型的范围,则错误为“整数太大”,这个数本身就出错,因为本身就是int类型,这时需要在数的后面加上字符进行区别,比如加上个大L,这时报的错误就为“不兼容的类型,从long转换到int可能会有损失”
  • 如果出现比long类型还大的数据则使用java.math.BigInteger类型(官方写好的)

四,*整型的常见笔试考点

  1. *1,标识符的命名规则:由数字、字母、下划线、以及$等组成,不能以数字开头,如果我声明了一个数字299999999999L,会分不清是字符串常量还是变量,那就分不清了,数字开头就乱套了,不规范。<br /> 区分大小写、见名知义<br /> 2,请问下面的代码是否有错误?若有请指出并说明原因<br /> int i2=25;<br /> byte b2=i2; //错误,不兼容的类型,从int转换到byte可能会有损失<br /> System.out.println(b2);<br />答:(1)错误,不兼容的类型,从int转换到byte可能会有损失,i2是int类型,大转小会有精度损失<br /> (2)i2是一个变量,赋值给b2不一定就是25,也可能是赋值成其他值,当然从本题中,目前赋值给i2的是25,但是类型还是前面说的int

五,浮点类型
用于描述小数数据的类型:float和double,推荐double类型
1,float 在内存空间占4个字节,单精度浮点数,范围:-3.403E38~3.403E38(E38表示10的38次方)
2,double在内存空间占8个字节,双精度浮点数,可以表示15位有效数字,范围:-1.798E308~1.798E308
double精度高,表示有效位数多,但是消耗空间多,多占4个字节。

浮点类型默认为double类型,所以为float赋值时用f区分,float f=2.3f; float浮点数有点位为7位
如果 float f1=3.1415926f; 打印出来的f1 显示为3.1415925
若为double d2=3.1415926; 打印出来为3.1415926 15位有效数字

六,浮点类型考点

  1. public class floatTest {
  2. public static void main(String[] args) {
  3. System.out.println(0.1+0.2);
  4. }
  5. }

输出:0.30000000000000004
运算时可能会有误差,不能得到精确的结果,所以涉及金融业务时不会使用float和double类型
涉及金钱时使用,若期望实现精确运算,则借助java.math.BigDecimal类型(官方)

七,布尔类型 boolean

boolean描述真假信息,数值只有true和false
内存空间没有规定,可以认为是一个字节

八,字符类型

1,用于描述单个字符的数据类型,char类型,如‘a’’中’
char在内存中占两个字节,没有符号位,表示的范围是:0~65535,由于生活中很少有数据只用单个字符描述的,后面多用字符串,用双引号串起来的。而String类型是属于引用类型中的一种。

注意:
计算机的底层只识别0和1组成的二进制序列,对于字符‘a’这样的图案来说不满足该规则,因此该数据无法直接在计算机中存储,但现实生活中存在这样的图案数据需要计算机存储,为了使得该数据能够存储起来就可以给该数据指定一个编号,然后将编号存储起来即可,该编号就交ASCII( ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符 , https://baike.baidu.com/item/ASCII/309296?fr=aladdin)

2,字符类型的使用
定义一个字符,比如 char c1=’a’;
对其进行打印,打印的结果取决于他的解析方式,如果对直接进行打印,输入a,如果按int类型
System.out.println((int)c1);

进行打印,输出为:97

char c2=98;
System.out.println(“c2=”+c2);
System.out.println(“对应编号是:”+(int)c2);
输出:
c2=b
对应编号是:98

注意:
常用字符和对应编码
ASCII:‘0’- 48 ‘A’- 65 ‘a’ - 97 空格 - 32 换行符 - 10

九,Unicode字符集
java字符类型采用Unicode字符集编码。Unicode是世界通用的定长字符集,所有的字符都是16位。
保证各国的进行对java的运用,ASCII:美国标准信息交换码

//使用Unicode字符集表示自己的名字 宏栋 对应编号 \u5b8f\u680b
char c3=’\u5b8f’;
char c4=’\u680b’;
System.out.println(c3+c4);

使用Unicode编码转换工具(传送门:https://www.zxgj.cn/g/unicode

十,转义字符
\” - “
\’ - ‘
\ - \
\t-制表符 回车(CR) ,将当前位置移到本行开头
\n - 换行
System.out.println(“我想过\’过过过过\”过的\t生活”);
输出:我想过’过过过过”过的 生活

十一、自动类型转换

  1. 主要指从小类型到大类型之间的转换。<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778167346-38a3c3fd-5140-40b0-9e6c-35d63e63b0be.png#id=CQtFp&originHeight=297&originWidth=1455&originalType=binary&ratio=1&status=done&style=none)

小类型转换到大类型不用进行其他的处理

十一、强制类型转换
从大类型转到小类型,需要进行强制转换,且大的转成小的会有精度损失
例如,当程序中需要将 double 型变量的值赋给一个 int 型变量,该如何实现呢?
显然,这种转换是不会自动进行的!因为 int 型的存储范围比 double 型的小。此时就需要通过强制类型转换来实现了。

double d1=78.32;
int i2=(int)d1;
System.out.println(d1);
System.out.println(i2);

输出:78.32
78

二、运算符


一、算术运算符

  • +表示加法运算符
  • -表示减法运算符
  • *表示乘法运算符
  • /表示除法运算符
  • %表示取模/取余运算符

案例:提示用户输入正整数类型的秒数,拆分秒数后输出x小时x分x秒
如:输入7199,输出1小时59分59秒

import java.util.Scanner;

public class Demo01 {
public static void main(String[] args) {7
System.out.println(“请输入一个整数\n:”);
Scanner sc=new Scanner(System.in);
int input= sc.nextInt();
int h=input/3600;
int m=input%3600/60;
int s=input%3600%60;
System.out.println(h+”时”+m+”分”+s+”秒”);
}
}

二、字符串连接符

  • +实现字符串的连接,同时可以实现字符串与其他数据类型的“相连”

三、关系比较运算符

  • 大于

  • <小于
  • = 大于等于

  • <=小于等于
  • ==等于
  • !=不等于

注意:所有以关系运算符作为最终运算的表达式结果一定是boolean类型

四、自增自减运算符

  • ++表示自增运算符,用于使得当前变量自身的数值加1的结果
  • —表示自减运算符,用于使得当前变量自身的数值减1的结果
  • 后++表示先让变量的数值作为整个表达式的最终结果,然后再让变量自身加1
  • 前++表示先让变量自身的数值加1 ,然后再让变量的数值作为整个表达式的结果

注意:只能用于变量,常数不可以
笔试考点*
int ia=12;
int ib=ia++;
System.out.println(“ib=”+ib);//12
System.out.println(“ia=”+ia);//13
int ic=++ia;
System.out.println(“ic=”+ic);//14
System.out.println(“ia=”+ia);//14
System.out.println(ia++ + ++ia);//14 前面14再加1,则15, 后面一个数++ia,则15加1再运算,为16

输出:
ib=12
ia=13
ic=14
ia=14
30

五、逻辑运算符

  • &&表示逻辑与运算符,相当于“并且”,同真为真,一假为假
  • ||表示逻辑或运算符,相当于“或者”,一真为真,同假为假
  • !表示逻辑非运算符,相当于“取反”,真为假,假为真

注意:逻辑运算符的操作数均为boolean表达式
1-14(章节) - 图2

  • 逻辑运算符的特性
    • 对于逻辑与来说,若第一个表达式为假则结果为假,跳过第二个表达式
    • 对于逻辑或来说,若第一个表达式为真则结果为真,跳过第二个表达式

六、条件/三目运算符

  • 条件表达式?表达式1:表达式2
  • 判断条件表达式是否成立,若成立则执行表达式1,否则执行表达式2

案例

  • 提示用户输入两个整数,使用三目运算符找到最大值并打印出来

package ch8;

import java.util.Scanner;

public class DemoMax {
public static void main(String[] args) {
System.out.println(“请依次输入两个整数”);
System.out.println(“请输入第一个数:”);
Scanner sc=new Scanner(System.in);
int input1=sc.nextInt();
System.out.println(“请输入第二个数:”);
int input2= sc.nextInt();
int max ;
max=input1>input2? input1:input2;
System.out.println(max);

  1. }<br />}

七、赋值运算符

  • =表示赋值运算符,用于将=右边的数据赋值给=左边的变量,覆盖变量原来的数值
  • 赋值表达式本身也有值,其本身之值即为所赋之值
  • +=、-=、*=、/=…

笔试考点一:
请问以下的区别?
byte b1=10;
b1=b1+2; //错误:不兼容的类型:从int转换到byte可能会有损失,加上(byte)2还是错误
b1=b1+(byte)2;//错误:不兼容的类型:从int转换到byte可能会有损失 byte+byte还是int类型 编译器优化
b1+=2;

编译器优化:为了避免溢出,因为byte表示的数范围有限,编译器自动将类型提升为int类型

可以进行强制类型转换
b1=(byte)(b1+2);//强制类型转换,将int类型转换为byte

而b1+=2;真正等价于b1=(byte)(b1+2);自带强转!

笔试考点2:
ia==2 ;//表示判断变量ia的数值是否等于2
2==ia;//表示判断2是否等于变量ia的数值,从结果上来说等价,推荐该方式—能规避低级错误

ia=2;//表示将2赋值给变量ia,覆盖变量ia原来的数值
2=ia;//编译报错,错误:意外的类型

八、移位运算符

  • << 左移运算符,用于将数据的二进制位向左移动,右边使用0补充
  • 右移运算符,用于将数据的二进制位向右移动,左边使用符号位补充

  • 表示逻辑右移运算符,用于将数据的二进制位向右移动,左边使用0补充

九、位运算符

  • &位与运算符
  • |位或运算符
  • ~按位取反运算符,1为0,0为1
  • ^按位异或运算符,按照二进制位进行异或运算,同为0,不同为1

十、运算符的优先级

  • ()的优先级极高
  • =的优先级极低
  • 若无法确定优先级,则使用()来确保即可

三、分支语句


一,认识分支结构
1-14(章节) - 图3
二、if分支结构

  • if(条件表达式){

    1. 语句块;<br /> }

1-14(章节) - 图4

• 判断条件表达式是否成立
=> 若成立,则执行语句块;
=> 若不成立,则跳过语句块;
案例:提示用户输入两个整数,使用if分支结构找到最大值并打印出来
package ch8;
import java.util.Scanner;
public class DemoMax {
public static void main(String[] args) {
System.out.println(“请依次输入两个整数”);
System.out.println(“请输入第一个数:”);
Scanner sc=new Scanner(System.in);
int input1=sc.nextInt();
System.out.println(“请输入第二个数:”);
int input2= sc.nextInt();
int max ;
if(input1>=input2){
System.out.println(input1);
}
if (input1 System.out.println(input2);
}
}
}

方式二——思想:也可以假设为一个最大值,然后进行比较 推荐、通用性
int max=ia;
if(ib>ia){
max=ib;
}
System.out.println(max);

三、if else分支结构

• if(条件表达式) {
语句块1;
} else {
语句块2;
}
1-14(章节) - 图5
• 判断条件表达式是否成立
=> 若成立,则执行语句块1;
=> 若不成立,则执行语句块2;
if(input1>=input2){
System.out.println(input1);
}
else {
System.out.println(input2);
}
System.out.println(“世界上最遥远的距离不是生与死,而是你在if,我在else,似乎一直相伴却又永远分离”);

四、if else if else分支结构

  • if(条件表达式1){

    1. 语句块1 <br /> }<br /> else if(条件表达式2){<br /> 语句块2;<br /> }<br /> else{<br /> 语句块n;<br /> }

1-14(章节) - 图6
• 判断条件表达式1是否成立
=> 若成立,则执行语句块1;
=> 若不成立,则判断条件表达式2是否成立
=> 若成立,则执行语句块2;
=> 若不成立,则执行语句块n;
案例1:
编程实现if else if else分支结构的使用,模拟购买火车票的过程

  1. package ch8;
  2. import java.util.Scanner;
  3. public class DemoMax {
  4. public static void main(String[] args) {
  5. System.out.println("请输入你的身份信息");
  6. Scanner sc=new Scanner(System.in);
  7. String str=sc.next();
  8. if("军人".equals(str)){
  9. System.out.println("免费乘车!");
  10. }
  11. else if("学生".equals(str)){
  12. System.out.println("半价乘车");
  13. }
  14. else {
  15. System.out.println("请购买全价票");
  16. }
  17. }
  18. }

案例2:
1-14(章节) - 图7
方式一

  1. import java.util.Scanner;
  2. /*
  3. 使用if else if else计算个人所得税
  4. */
  5. public class Demo02 {
  6. public static void main(String[] args) {
  7. System.out.println("请输入您的工资");
  8. Scanner sc=new Scanner(System.in);
  9. double num=sc.nextDouble();
  10. //局部变量:作用范围是从声明开始一直到方法体结束
  11. double d1=0.0;
  12. if (num<=5000){
  13. System.out.println("无需纳税");
  14. }
  15. else if(num<=8000){
  16. //块变量:作用范围是从声明开始一直到语句块结束
  17. d1=(num-5000)*0.03;
  18. }
  19. else if(num<=17000){
  20. d1=(num-8000)*0.1+(8000-5000)*0.03;
  21. }
  22. else if(num<=30000){
  23. d1=(num-17000)*0.2+(17000-8000)*0.1+(8000-5000)*0.03;
  24. }
  25. else if(num<=40000){
  26. d1=(num-30000)*0.25+(num-17000)*0.2+(17000-8000)*0.1+(8000-5000)*0.03;
  27. }
  28. else{
  29. System.out.println("您的工资真高");
  30. }
  31. //打印最终结果
  32. System.out.println("您需纳税金额是:"+d1);
  33. }
  34. }

方式二
使用个人所得税公式:
本月应缴纳税所得金额*对应的税率-速算扣除数,如:

  1. if (num<=5000){
  2. System.out.println("无需纳税");
  3. }
  4. else if(num<=8000){
  5. //块变量:作用范围是从声明开始一直到语句块结束
  6. //d1=(num-5000)*0.03;
  7. d1=(num-5000)-0;
  8. }
  9. ......

案例3
1-14(章节) - 图8

  1. /*
  2. 编程使用if分支实现出租车计费
  3. */
  4. public class Demo03 {
  5. public static void main(String[] args) {
  6. //1,提示用户输入公里数和等待的秒数并使用变量记录
  7. System.out.println("请输入公里数和等待秒数");
  8. Scanner sc=new Scanner(System.in);
  9. int km= sc.nextInt();
  10. int sec= sc.nextInt();
  11. int keyprice=0;
  12. //2,根据公里数计算对应的里程费并使用变量记录
  13. if(km<=3){
  14. keyprice=13;
  15. }
  16. else if(km<=15){
  17. keyprice=(km-3)*2+13;
  18. }
  19. else {
  20. keyprice=(km-15)*3+(15-3)*2+13;
  21. }
  22. //3,根据等待的秒数来计算对应的等待费并使用变量记录
  23. int secprice=sec/150;
  24. //4,计算总费用并打印
  25. System.out.println("费用是:"+(secprice+keyprice));
  26. }
  27. }

五、switch case分支结构
• switch(变量/表达式) {
case 字面值1: 语句块1; break;
case 字面值2: 语句块2; break;

default:语句块n;
}
1-14(章节) - 图9

• 计算变量/表达式的数值 => 判断是否匹配字面值1
=> 若匹配,则执行语句块1 => 执行break跳出当前结构
=> 若不匹配,则判断是否匹配字面值2
=> 若匹配,则执行语句块2 => 执行break跳出当前结构
=> 若不匹配,则执行语句块n
案例1:
使用switch case 分支结构查询成绩等级

  1. import java.util.Scanner;
  2. public class Demo04 {
  3. public static void main(String[] args) {
  4. System.out.println("请输入您的成绩:");
  5. Scanner sc=new Scanner(System.in);
  6. int score=sc.nextInt();
  7. switch (score/10){
  8. case 10:
  9. System.out.println("A");
  10. break;
  11. case 9:
  12. System.out.println("A");
  13. break;
  14. case 8:
  15. System.out.println("B");
  16. break;
  17. case 7:
  18. System.out.println("c");
  19. break;
  20. case 6:
  21. System.out.println("D");
  22. break;
  23. case 5:
  24. System.out.println("E");
  25. break;
  26. default: System.out.println("世界上最痴情的等待就是当我case你当switch,或许永远都不会选到自己!");
  27. }
  28. }
  29. }

在开发中要减少代码的重复,如10和9的代码就重复了。

  1. switch (score/10){
  2. case 10:
  3. case 9:
  4. System.out.println("A");
  5. break;
  6. case 8:
  7. System.out.println("B");
  8. break;
  9. case 7:
  10. System.out.println("c");
  11. break;
  12. case 6:
  13. System.out.println("D");
  14. break;
  15. case 5:
  16. System.out.println("E");
  17. break;
  18. }

case穿透!
• switch()中支持的数据类型有:byte、short、char以及int类型,从jdk1.5
开始支持枚举类型,从jdk1.7开始支持String类型。

六、循环结构
1-14(章节) - 图10
分支结构也叫选择结构

1,for 循环
• for(初始化表达式; 条件表达式; 修改初始值表达式) {
循环体;
}
1-14(章节) - 图11

• 执行初始化表达式 => 判断条件表达式是否成立
=> 成立则执行循环体 => 修改初始值表达式 => 判断条件是否成立
=> 若不成立,则循环结束
案例1:
模拟玩游戏的过程
public class Demo06 {
public static void main(String[] args) {
for (int i = 1; i <=10; i++) {
System.out.println(“正在进行第”+i+”场游戏”);
try {
Thread.sleep(5000);//表示模拟睡眠5秒的效果
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“游戏”+i+”结束”);
}
}
}

案例2:
编程实现,打印1-100的所有奇数,三种方式
方式一:
public class Demo07 {
public static void main(String[] args) {
for (int i = 1; i <=100 ; i++) {
if(i%2!=0){
System.out.println(i);
}
}
}
}
方式二:
使用等差数列的概念进行打印
public class Demo07 {
public static void main(String[] args) {
for (int i = 1; i <=100 ; i+=2) {
System.out.println(i);
}
}}
方式三:
使用通项的规律进行打印2i-1
public class Demo07 {
public static void main(String[] args) {
for (int i = 1; i <=50 ; i++) {
System.out.println(2
i-1);
}
}}
案例3:
打印1~10000的和
package ch8;
public class Demo07 {
public static void main(String[] args) {
int sum=0;
for (int i = 1; i <=10000 ; i++) {
sum+=i;
}
System.out.println(sum);
}}

案例4:
使用for循环打印三位数中所有水仙花数。

所谓“水仙花数”即一个整数满足其值等于各个数位的立方和。

如:153是一个水仙花数,因为153=1^3+5^3+3^3。
public class Demo07 {
public static void main(String[] args) {
for (int i = 100; i <1000 ; i++) {
int ia=i/100;
int ib=(i%100/10);
int ic=i%100%10;
if(i==(iaiaia+ibibib+icicic)){
System.out.println(i);
}
}
}}

七、continue关键字

  • continue语句使用在循环体中,用于结束本次循环而开始下一次循环。

案例1:
使用for循环打印1 ~ 20之间的所有整数,若遇到5的倍数则跳过不打印。
public class Demo07 {
public static void main(String[] args) {
for (int i = 1; i <=20 ; i++) {
if (0==i%5){
continue;//表示提前结束本次循环,继续下一次循环,也就是奔向了i++
}
System.out.println(“i=”+i);
}
}}

八、break 关键字

  • break用于退出当前语句块,break用在循环体中用于退出循环
  • for(;;)-这种没有循环条件的循环叫做无限循环,俗称“死循环”

案例1:
不断地提示用户输入聊天内容并输出,直到用户输入”bye”结束聊天。
public class Demo07 {
public static void main(String[] args) {
for (;;){
System.out.println(“请输入内容”);
Scanner sc=new Scanner(System.in);
String str=sc.next();
if (str.equals(“bye”)){
System.out.println(“好的,再见!”);
break;
}
else {
System.out.println(“聊天内容:”+str);
}
}
}}
案例2:
猜数字
package ch8;

import java.util.Random;
import java.util.Scanner;

/
编程使用for循环实现猜数字游戏
/
public class Demo09 {
public static void main(String[] args) {
//1,随机产生生成1~100之间的整数并使用变量记录
Random random=new Random();
int num=random.nextInt(100)+1;
System.out.println(num);
for(;;){
//2,提示用户输入1~100之间猜测的整数并使用变量记录
System.out.println(“请输入1~100之间的整数”);
Scanner sc=new Scanner(System.in);
int num2= sc.nextInt();
//3,比较随机数和用户输入的数

  1. if(num2>num){<br /> System.out.println("您输入的数大了");<br /> }<br /> else if(num2<num){<br /> System.out.println("您输入的数小了");<br /> }<br /> else<br /> {<br /> System.out.println("恭喜您!猜对了!");<br /> break;<br /> }<br /> }<br /> }<br />}<br />增加等级评定<br />package ch8;

import java.util.Random;
import java.util.Scanner;

/
编程使用for循环实现猜数字游戏
/
public class Demo09 {
public static void main(String[] args) {
//1,随机产生生成1~100之间的整数并使用变量记录
Random random=new Random();
int num=random.nextInt(100)+1;
System.out.println(num);
//4,声明一个int类型的变量,统计用户猜测的数字
int sum = 0;
for(;;){
//2,提示用户输入1~100之间猜测的整数并使用变量记录
System.out.println(“请输入1~100之间的整数”);
Scanner sc=new Scanner(System.in);
int num2= sc.nextInt();
sum++;
//3,比较随机数和用户输入的数
if(num2>num){
System.out.println(“您输入的数大了”);
}
else if(num2 System.out.println(“您输入的数小了”);
}
else
{
System.out.println(“恭喜您!猜对了!”);
break;
}
}
switch (sum){

  1. case 1:<br /> System.out.println("牛");<br /> }<br /> }

}

九,双重for循环

  • for(初始化表达式1; 条件表达式2; 修改初始值表达式3) {

for(初始化表达式4; 条件表达式5; 修改初始值表达式6) {
循环体;
}
}

  • 编程实现:

双重for循环打印5行5列“厉害了我的哥”
public class Demo10 {
public static void main(String[] args) {
for (int i = 1; i <=5 ; i++) {
for (int j = 1; j <=5 ; j++) {
System.out.print(“厉害了我的哥 “);
}
System.out.println();
}

  1. }<br />}<br />外层循环打印行数<br />里层循环打印列数
  • 双重for循环的执行流程

• 执行表达式1 => 判断表达式2是否成立
=> 若成立,则执行表达式4 => 判断表达式5是否成立
=> 若成立,则执行循环体 => 执行表达式6 => 表达式5是否成立
=> 若不成立,则内层循环结束 => 表达式3 => 表达式2是否成立
=> 若不成立,则外层循环结束
1-14(章节) - 图12

  • 双重for循环的特点
    • 外层循环用于控制打印的行数,内层循环用于控制打印的列数,外层循环改一下,内层循环从头到尾跑一圈。
    • 在以后的开发中若需要打印多行多列时,需要使用双重循环。
    • 多重循环不宜嵌套太多层,否则效率很低。一般到三重循环即可,最常见的就是双重循环。

案例1:
打印各种星星图案
1-14(章节) - 图13
public class Demo11 {
public static void main(String[] args) {
//打印第一种星星图案
for (int i = 1; i <=5 ; i++) {
for (int j = 1; j <=5 ; j++) {
System.out.print(“*”);
}
System.out.println();
}
}
}

public class Demo11 {
public static void main(String[] args) {
//打印第二种星星图案
for (int i = 1; i <=5 ; i++) {
for (int j = 1; j <=i ; j++) {
System.out.print(“*”);
}
System.out.println();
}
}
}

public class Demo11 {
public static void main(String[] args) {
//打印第三种星星图案
for (int i = 1; i <=5 ; i++) {
for (int j = 5; j >=i ; j—) {
System.out.print(“*”);
}
System.out.println();
}
}
}

package ch8;

public class Demo11 {
public static void main(String[] args) {
//打印第四种星星图案
for (int i = 1; i <=5 ; i++) {
//还需要打印空格,控制空格个数,5-i
for (int j = 1; j <=5-i ; j++) {
System.out.print(“ “);
}
//内层循环主要用于控制打印的列数,也就是当前行的列数与当前行的行数为2i-1的关系
for (int j = 1; j <=2
i-1 ; j++) {
System.out.print(“*”);
}
System.out.println();
}
}
}

案例2:
使用双重循环打印乘法表

package ch8;

public class Demo12 {
public static void main(String[] args) {
for (int i = 1; i <=9 ; i++) {
for (int j = 1; j <=i ; j++) {
int num=ij;
System.out.print(j+”
“+i+”=”+num+” “);
}
System.out.println();
}
}
}

1-14(章节) - 图14

break ;//主要用于退出循环,但该关键字只能跳出当前所在的循环

1-14(章节) - 图15

打印到66就跳出循环
public class Demo12 {
public static void main(String[] args) {
outer: for (int i = 1; i <=9 ; i++) {
for (int j = 1; j <=i ; j++) {
int num=i
j;
System.out.print(j+”*”+i+”=”+num+” “);
if(6==j){
break outer;//主要用于退出循环,但该关键字只能跳出当前所在的循环,如果要跳出要使用标记
}
}
System.out.println();
}
}
}

案例3:
打印2~100的素数
package ch8;
public class Demo13 {
public static void main(String[] args)
//1,使用for循环打印2~100之间的数
for (int i = 2; i <=100; i++) {
boolean flag=true;
//2,针对每一个当前的整数都要判断是否为素数,若是素数打印,不是则不打印
//判断一个数是否为素数的方法:若该数不能被2到它本身-1之间的所有整数整除时,则证明该数是素数
//使用内层for循环用于控制2到该数自身-1之间的范围
for (int j = 2; j <=i-1 ; j++) {
//使用当前数除以该循环中的每个数据并判断是否可以整除,只要找到一个不整除的数据,则证明该数不是素数
if (0==i%j){
flag=false;
break;
}
}
//只打印素数
if (flag){
System.out.println(i);
}}
}
}

优化
package ch8;
public class Demo13 {
public static void main(String[] args) {
//1,使用for循环打印2~100之间的数
for (int i = 2; i <=100; i++) {
boolean flag=true;
//2,针对每一个当前的整数都要判断是否为素数,若是素数打印,不是则不打印
//判断一个数是否为素数的方法:若该数不能被2到它本身-1之间的所有整数整除时,则证明该数是素数
//使用内层for循环用于控制2到该数自身-1之间的范围
// for (int j = 2; j <=i-1 ; j++) {
//优化:只需要判断2到该数的平方根即可,因为随着除数的增大商必然减小,会造成重复的判断
for (int j = 2; j <=Math.sqrt(i) ; j++) {
//使用当前数除以该循环中的每个数据并判断是否可以整除,只要找到一个不整除的数据,则证明该数不是素数
if (0==i%j){
flag=false;
break;
}
}
//只打印素数
if (flag){
System.out.println(i);
}}
}
}

十、while循环
•while(条件表达式) {
循环体;
}
1-14(章节) - 图16
编程实现:
public class Demo14 {
public static void main(String[] args) {
int i=1;
while (i<=10){
System.out.println(i);
i++;
}
}
}

与for的区别是代码放的位置不一样,理解了其实用起来一样,一般用for比较多

•判断条件表达式是否成立
=> 若成立,则执行循环体 => 判断条件表达式是否成立
=> 若不成立,则循环结束
案例1:
• 使用while循环计算调和数列的和并打印,即: 1/1 + 1/2 + … + 1/n。
package ch8;

public class Demo15 {
public static void main(String[] args) {
int i=1;
double sum=0.0;
while(i<=100){
sum+=(1.0/i);
i++;
}
System.out.println(sum);
}
}
这里注意数据类型,1.0/i,不然算出来都是0

十一、while 和for循环比较

  • while循环和for循环完全可以互换,当然推荐使用for循环
  • while循环更适合于明确循环条件但不明确循环次数的场合中
  • for循环更适合于明确循环次数或范围的场合中
  • while(true)等价于for(;;)都表示无限循环

案例1:
提示用户输入一个任意位数的正整数然后反向输出
package ch8;
import java.util.Scanner;
public class Demo16 {
public static void main(String[] args) {
System.out.println(“请输入一个整数”);
Scanner sc=new Scanner(System.in);
int num= sc.nextInt();
while(num>0){
System.out.print(num%10);
//丢弃个位数
num/=10;
}
}
}

十二、do while循环,先做再判断

do {
循环体;
} while(条件表达式);

执行循环体 => 判断条件表达式是否成立
=> 若成立,则执行循环体 => 判断条件表达式是否成立
=> 若不成立,则循环结束
1-14(章节) - 图17

编程实现do while循环的使用,打印1~10之间的所有整数

一般主要用于至少执行一次循环体的场合中。

案例1:
模拟任务检查
使用do while循环来模拟学习任务是否合格的检查,如果合格则停止,否则就重新完成学习任务。
package ch8;

import java.util.Scanner;

public class Demo18 {
public static void main(String[] args) {
String input=null;
do{
System.out.println(“疯狂学习中。。。”);

  1. try {<br /> Thread.sleep(5000);<br /> } catch (InterruptedException e) {<br /> e.printStackTrace();<br /> }<br /> System.out.println("是否合格?y/n");<br /> Scanner sc=new Scanner(System.in);<br /> input=sc.next();<br /> }<br /> while ("n".equals(input));<br /> System.out.println("恭喜任务合格!");<br /> }<br />}

十三、循环的笔试考点
典故:十动然拒 笔试考点:有没分号
正确代码
package ch8;

public class Demo19 {
public static void main(String[] args) {
int i=1;
while (i<=10000){
System.out.println(“i love you “);
i++;
}
}
}
错误演示—注意高亮部分,新增分号
package ch8;

public class Demo19 {
public static void main(String[] args) {
int i=1;
while (i<=10000);{
System.out.println(“i love you “);
i++;
}
}
}
错误演示2
package ch8;
public class Demo19 {
public static void main(String[] args) {
int i=1;
while (i<=10000)
{
;//分号是空语句,啥也不干,可以用于延时
}{
System.out.println(“i love you “);
i++;
}
}
}

四、数组


一、一维数组的概念

  • 当在java中只需要记录单个数据时,声明一个变量即可
  • 当需要在Java程序中记录多个类型相同的数据内容时,则声明一个一维数组即可,一维数组本质上就是在内存空间中申请一段连续的存储单元。
  • 数组是相同数据类型的多个元素的容器,元素按线性顺序排列,在Java言中体现为一种引用数据类型。

二、一维数组的声明

  • 数据类型 【】 数组名称=new 数据类型【数组长度】;
  • 调用length可以获取数组长度
  • 通过下标访问数组元素,下标从0开始

从声明变量的角度看“声明数组”
1-14(章节) - 图18
三、一维数组的使用
package ch9;

public class Array {
public static void main(String[] args) {
int [] arr=new int[2];
System.out.println(“数组的长度:”+arr.length);
System.out.println(“数组元素1:”+arr[0]);
System.out.println(“数组元素2:”+arr[1]);
//System.out.println(“数组元素3:”+arr[2]);//java.lang.ArrayIndexOutOfBoundsException—数组越界
//1,给数组赋值
arr[0]=1;
arr[1]=123;
//2,for循环打印数组元素
for (int i = 0; i System.out.println(arr[i]);
}
}
}
三、一维数组的初始化
•基本类型的数组(数据元素为基本类型)创建后,其元素的初始值:byte、
short、char、int、long为0;float和double为0.0;boolean为false。
使用动态初始化时,元素会拥有一个默认值,规则如下
1,如果是整数类型,则默认值为0;
2,如果是浮点数类型,则默认值是0.0;
3,如果是字符型,则默认值为‘\u0000’;
4,如果是布尔类型,则默认值为false;
5,如果是引用类型,则默认值是null。
•可以在数组声明的同时进行初始化,具体如下(静态初始化):
数据类型[] 数组名称 = {初始值1, 初始值2, …};

char [ ] arr={‘a’,b’,’’c’};

四、内存结构分析
1,内存结构之栈区,里面存放程序运性过程中的所有局部变量。一个运行的java程序从开始到结束会有多次变量的声明。
2,内存结构之堆区,jvm会在其内存空间中开辟一个称为“堆”的存储空间,这部分空间用于存储使用new关键字,创建的数组和对象。
1-14(章节) - 图19

定义一个数组arraya,只是开辟了一个内存空间,直接打印数组名显示的是一个地址
1-14(章节) - 图20

五、一维数组的增删查改
1,声明和赋值
•声明一个长度为5元素类型为int类型的一维数组,打印数组中所有元素值;
•使用元素11、22、33、44分别对数组中前四个元素赋值后再次打印;
package ch9;

public class Array2 {
public static void main(String[] args) {
int [] Arrayb=new int[5];
for (int i = 0; i <=Arrayb.length-1 ; i++) {
Arrayb[i]=(i+1)*11;
}
for (int j = 0; j < Arrayb.length; j++) {
System.out.println(Arrayb[j]);
}
}
}
2,插入
•将元素55插入到下标为0的位置,原有元素向后移动,再打印所有元素值;
1-14(章节) - 图21

package ch9;

public class Array2 {
public static void main(String[] args) {
int [] Arrayb=new int[5];
for (int i = 0; i <=Arrayb.length-1 ; i++) {
Arrayb[i]=(i+1)11;
}
/

Arrayb[4]=Arrayb[3];
Arrayb[3]=Arrayb[2];
Arrayb[2]=Arrayb[1];
Arrayb[1]=Arrayb[0];
Arrayb[0]=55;
*/
for (int j = Arrayb.length-1; j >0 ; j—) {
Arrayb[j]=Arrayb[j-1];
}
Arrayb[0]=55;
for (int h = 0; h <=Arrayb.length-1 ; h++) {
System.out.println(Arrayb[h]);
}
}
}
3、删除
将元素55从数组中删除,删除方式为后续元素向前移动,最后位置置为0并打印;

  • 1-14(章节) - 图22

package ch9;

public class Array3 {
public static void main(String[] args) {
int [] arrayA=new int[5];
for (int h = 0; h <=arrayA.length-1 ; h++) {
arrayA[h]=h+1;
}
for (int j = 0; j <= arrayA.length-1 ; j++) {
System.out.println(arrayA[j]);
}
System.out.println(“——————————————————————————-“);
for (int i = 0; i < arrayA.length -1; i++) {
arrayA[i]=arrayA[i+1];
}
arrayA[4]=0;
for (int j = 0; j <= arrayA.length-1 ; j++) {
System.out.println(arrayA[j]);
}
}
}

  1. 4,查找、改查<br /> 查找数组中是否存在元素22,若存在则修改为220后再次打印所有元素;<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778169669-bc3cee6d-e532-4cc7-9c2b-9ebaa69ea945.png#id=Qph5Q&originHeight=461&originWidth=878&originalType=binary&ratio=1&status=done&style=none)

package ch9;

public class Array5 {
public static void main(String[] args) {
//查找数组22
int [] arr1={11,22,33,44,55};
for (int i = 0; i < arr1.length ; i++) {
if(arr1[i]==22){
arr1[i]=220;
break;
}
}
for (int j = 0; j < arr1.length ; j++) {
System.out.println(arr1[j]);
}
}
}

五、一维数组的优缺点
•可以直接通过下标(或索引)的方式访问指定位置的元素,速度很快。
•数组要求所有元素的类型相同。
•数组要求内存空间连续,并且长度一旦确定就不能修改。
•增加和删除元素时可能移动大量元素,效率低。

案例1:
声明一个初始值为11 22 33 44 55的一维数组并打印所有元素

声明一个长度为3元素类型为int类型的一维数组并打印所有元素

实现将第一个数组中间3个元素赋值到第二个数组中

再次打印第二个数组中的所有元素
package ch9;

public class Array6 {
public static void main(String[] args) {
int[] arr1={11,22,33,44,55};
int[] arr2=new int[3];
for (int i = 0; i < 3 ; i++) {
arr2[i]= arr1[i+1];
}
//分别打印for语句省略!
//打印拷贝后的arr2里面的元素
for (int j = 0; j System.out.println(arr2[j]);
}
}
}

优化:
使用api提供的方法,直接使用java提供的拷贝功能
package ch9;

public class Array6 {
public static void main(String[] args) {
int[] arr1={11,22,33,44,55};
int[] arr2=new int[3];
// for (int i = 0; i < 3 ; i++) {
// arr2[i]= arr1[i+1];
// }
//分别打印for语句省略!
//直接使用java官方提供的拷贝功能
//System.arraycopy(拷贝的内容来源数组,开始下标,目的数组,目的数组开始下标,元素个数);
System.arraycopy(arr1,1,arr2,0,3);
//打印拷贝后的arr2里面的元素
for (int j = 0; j System.out.println(arr2[j]);
}

  1. }<br />}

六、笔试考点
数组名的赋值,是让其指向同一块堆区空间,本质上就改变指向而已。
下图中0x30由于没有人使用,java中的垃圾回收机制会将其回收。
1-14(章节) - 图23

1-14(章节) - 图24

案例2:

•编程统计用户输入任意一个正整数中每个数字出现次数的统计并打印。
•如:123123 => 1出现2次,2出现2次,3出现2次
分析原理:
1-14(章节) - 图25

次数为数组中的内容,出现的数字则为数组下标。
package ch9;

import java.util.Scanner;

public class Array7 {
public static void main(String[] args) {
//1,提示一个用户输入一个正整数,并使用变量记录
System.out.println(“请输入一个正整数:”);
Scanner sc=new Scanner(System.in);
int num= sc.nextInt();
//2,准备一个长度为10的int数组
int [] array=new int[10];
//3,拆分正整数的每个数字并记录到数组中
while (num>0){
array[num%10]++;
num/=10;
}
for (int j = 0; j < array.length ; j++) {
System.out.println(array[j]);
}
//4,打印最终的统计结果

  1. }<br />}

案例3:

  • 提示用户输入学生的人数以及每个学生的考试成绩并打印出来。
  • 计算该班级的总分和平均分并打印出来。

package ch9;

import java.util.Scanner;

public class Array8 {
public static void main(String[] args) {
//1,提示用户输入学生人数并记录
System.out.println(“请输入学生人数:”);
Scanner sc=new Scanner(System.in);
int num=sc.nextInt();
//2,根据学生人数来声明数组的长度,负责记录学生的考试成绩
//变长数组:主要指变量可以作为数组的长度,但绝不是数组的长度可以改变
int [] array=new int[num];
//3,提示用户输入学生成绩,并存储到数组当中
for (int i = 0; i System.out.println(“请输入第”+(i+1)+”个学生成绩”);
array[i]= sc.nextInt();
}
//遍历数组,打印数组信息
for (int j= 0; j< num; j++) {
System.out.println(“第”+(j+1)+”个学生的成绩是”+array[j]);
}
}
}

public class Array8 {
public static void main(String[] args) {
……
//5,计算该班级的总分和平均分
int sum=0;
double average=0.0;
for (int h = 0; h sum+=array[h];
average=sum*1.0/num;
}
System.out.println(“总分是:”+sum+”平均分是”+average);
}
}

七、数组工具类的概念
• java.util.Arrays类可以实现对数组中元素的遍历、查找、排序等操作。
1,数组工具类的基本编程实现
1-14(章节) - 图26
2,常用方法
1-14(章节) - 图27

3,使用方法进行数组的填充
使其全部填充为特定的值
1-14(章节) - 图28

4,判断两个数组是否相同
结果是boolean类型
1-14(章节) - 图29

5,排序与查找
排序:从小到大
查找:查找到的结果是元素的下标
1-14(章节) - 图30

八、二维数组
1, 基本概念:*一维数组中的每个元素才是数据内容

1-14(章节) - 图31

  1. 2,一维和二维的区别!

1-14(章节) - 图32

  1. 3,二维数组的声明和使用
  • 数据类型[][] 数组名称 = new 数据类型[行数][列数];

  • 数据类型[][] 数组名称 = {{元素1, 元素2,…}, …};

package ch9;

public class Array9 {
public static void main(String[] args) {
//声明一个两行三列的二维数组
int [][] arr1=new int[2][3];
//外层for循环控制行数
for (int i = 0; i //内层for循环控制列数
for (int j = 0; j System.out.println(arr1[i][j]);
}
}
//给二维数组赋值
int num=1;
for (int i = 0; i < arr1.length ; i++) {
for (int j = 0; j < arr1[i].length; j++) {
arr1[i][j]=num++;
}
}
//外层for循环控制行数
for (int i = 0; i //内层for循环控制列数
for (int j = 0; j System.out.print(arr1[i][j]);
}
}
}
}

  1. 4、*二维数组初始化的考点<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778170848-7e1a3d30-4cff-4a3d-98bb-ba7fda1a5b6a.png#id=asWIC&originHeight=866&originWidth=1805&originalType=binary&ratio=1&status=done&style=none)<br />真正打印出来还是连续的空间,考点就是注意二维数组定义的行数和列数

案例1:
打印杨辉三角
1-14(章节) - 图33
1-14(章节) - 图34

五、面向对象


一、面向对象编程的概念

  • 万物皆对象
  • 面向对象指以属性和行为的观点去分析现实生活中的事物。
  • 面向对象编程指先以面向对象的思想进行分析,然后使用面向对象的编程语言
  • 进行表达的过程。
  • 面向对象编程是软件产业化发展的需求。
  • 理解面向对象的思想精髓(封装、继承、多态),至少掌握一种编程语言。

面向过程:具体怎么做?一步一步安排
面向对象:能做什么?有什么功能,直接使用
二、类和对象及其引用(*)
•对象主要指现实生活中客观存在的实体,在Java语言中对象体现为内存空
间中的一块存储区域。(堆区)
•类简单来说就是“分类”,是对具有相同特征和行为的多个对象共性的抽象描
述,在Java语言中体现为一种引用数据类型,里面包含了描述特征/属性的成员变量
以及描述行为的成员方法。
•类是用于构建对象的模板,对象的数据结构由定义它的类来决定。
三、类的定义
class 类名 {
类体;
}

注意:
通常情况下,当类名由多个单词组成时,要求每个单词首字母都要大写。

成员变量的定义
class 类名 {
数据类型 成员变量名 = 初始值;
}

注意:
当成员变量由多个单词组成时,通常要求从第二个单词起每个单词的首
字母大写 。

对象的创建(类的实例化)
new 类名();
如new person(),创建了空间,但是没有指定变量,称其为匿名对象

注意:
a.当一个类定义完毕后,可以使用new关键字来创建该类的对象,这个
过程叫做类的实例化。
b.创建对象的本质就是在内存空间的堆区申请一块存储区域(堆区), 用于存放
该对象独有特征信息

引用的定义
基本概念
a.使用引用数据类型定义的变量叫做引用型变量,简称为”引用”。
b.引用变量主要用于记录对象在堆区中的内存地址信息,便于下次访问。

语法格式
类名 引用变量名;
引用变量名.成员变量名;

Person p = new Person();
p.name = “张飞”;
System.out.println(p.name);

1-14(章节) - 图35

*person类的执行流程和内存分析

1-14(章节) - 图36
案例1:
• 编程实现Point类的定义,特征有:横纵坐标(整数),要求在main方法中
声明Point类型的引用指向Point对象并打印特征,然后将横纵坐标修改为
3和5后再次打印。

package ch10;

public class Point {
int h;
int z;

  1. public static void main(String[] args) {<br /> Point p=new Point();<br /> p.h=3;<br /> p.z=5;<br /> System.out.println("横坐标:"+p.h+"纵坐标"+p.z);<br /> }<br />}

成员方法的定义

class 类名 {
返回值类型 成员方法名(形参列表) {
成员方法体;
}
}

当成员方法名由多个单词组成时,要求从第二个单词起每个单词的首字母大写。
例如:
class Person {
void show() {
System.out.println(“没事秀一下!”);
}
}
返回值
类型的详解

返回值主要指从方法体内返回到方法体外的数据内容。

返回值类型主要指返回值的数据类型,可以是基本数据类型,也可以是
引用数据类型。

当返回的数据内容是66时,则返回值类型写 int 即可

在方法体中使用return关键字可以返回具体的数据内容并结束当前方法。

当返回的数据内容是66时,则方法体中写 return 66; 即可

当该方法不需要返回任何数据内容时,则返回值类型写void即可。

形参列表的详解

形式参数主要用于将方法体外的数据内容带入到方法体内部。

形式参数列表主要指多个形式参数组成的列表,语法格式如下:
数据类型 形参变量名1, 数据类型 形参变量名2, …

当带入的数据内容是”hello”时,则形参列表写 String s 即可

当带入的数据内容是66和”hello”时,则形参列表写 int i, String s 即可

若该方法不需要带入任何数据内容时,则形参列表位置啥也不写即可。

方法体的详解

成员方法体主要用于编写描述该方法功能的语句块。

成员方法可以实现代码的重用,简化代码。
方法的调用

引用变量名.成员方法名(实参列表);

实际参数列表主要用于对形式参数列表进行初始化操作,因此参数的个
数、类型以及顺序都要完全一致。

实际参数可以传递直接量、变量、表达式、方法的调用等。

案例1:
Ponint类中无参方法的定义和调用
package ch10;

public class Point {
int h;
int z;
public static void main(String[] args) {
Point p=new Point();
p.h=3;
p.z=5;
p.show();//调用方式

  1. }<br /> //自定义成员方法实现所有成员变量的打印<br /> //返回值类型 方法名称(形参列表){方法体;}

//——定义方式———-
void show(){
//成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println(“横坐标:”+h+”纵坐标”+z);
}

}

案例2:
Point类实现有参无返回值方法的定义和调用
package ch10;

public class Point {
int h;
int z;
public static void main(String[] args) {
Point p=new Point();
p.h=3;
p.z=5;
p.show();
p.setH(20);//方法的调用
p.show();
}
//自定义成员方法实现所有成员变量的打印
//返回值类型 方法名称(形参列表){方法体;}
void show(){
//成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println(“横坐标:”+h+”纵坐标”+z);
}

  1. **//point类实现有参无返回值的定义个调用**<br /> void setH(int i){<br /> h=i;<br /> }

}

案例3:
Point类实现多参无返回值方法的定义和调用
package ch10;
public class Point {
int h;
int z;
public static void main(String[] args) {
Point p=new Point();
p.h=3;
p.z=5;
p.show();
p.setH(20);
p.show();
p.setHZ(25,58);//多参无返回值方法的调用

}
//自定义成员方法实现所有成员变量的打印
//返回值类型 方法名称(形参列表){方法体;}
void show(){
//成员变量和成员方法都属于类内部的成员,因此可以直接访问成员变量不需要再加引用.的前缀
System.out.println(“横坐标:”+h+”纵坐标”+z);
}
//多参无返回值方法
void setHZ(int x,int y){
h=x;
z=y;
}
//point类实现有参无返回值的定义个调用
void setH(int i){
h=i;
}
}

可变长参数

返回值类型 方法名(参数的类型… 参数名)

方法参数部分指定类型的参数个数是可以改变的,也就是0~n个 。

一个方法的形参列表中最多只能声明一个可变长形参,并且需要放到参
数列表的末尾。

定义:
//定义可变长参数
void showArgument(String…args){
for (int i = 0; i System.out.println(“第”+(i+1)+”个参数为:”+args[i]);

}
}
调用:
public static void main(String[] args) {
Point p=new Point();
p.h=3;
p.z=5;
p.show();
p.setH(20);
p.show();
p.setHZ(25,58);
p.showArgument(“1”,”2”);
}

案例4:
Point类实现无参有返回值方法的定义和调用
定义:
//无参有返回值方法
int show2(){
return h;
}
调用:
int num=p.show2();//25
System.out.println(num);

四、方法的传参过程
1-14(章节) - 图37
1-14(章节) - 图38

五、参数传递的注意事项:

  • 基本数据类型的变量作为方法的参数传递时,形参变量数值的改变通常

不会影响到实参变量的数值,因为两个变量有各自独立的内存空间;【基本数据类型传参不会改变数值】
1-14(章节) - 图39

  • 引用数据类型的变量作为方法的参数传递时,形参变量指向内容的改变

会影响到实参变量指向内容的数值,因为两个变量指向同一块内存空间【引用数据类型传参会改变数值 】
1-14(章节) - 图40

  • 引用数据类型的变量作为方法的参数传递时,若形参变量改变指向后

、再改变指定的内容,则通常不会影响到实参变量指向内容的改变,因为
两个变量指向不同的内存空间。
1-14(章节) - 图41
1-14(章节) - 图42

1-14(章节) - 图43
1-14(章节) - 图44

六、方法和封装


1-14(章节) - 图45
一、构造方法的基本概念
名字与类名完全相同,并且没有返回值,连void返回值都没有。【重点】
语法格式:

class 类名 {
类名(形参列表) {
构造方法体;
}
}
如:
class Person {
Person() { - Person类中的构造方法
}
}
1-14(章节) - 图46
二、默认构造方法

当一个类中没有定义任何构造方法时,编译器会自动添加一个无参空构
造构造方法,叫做默认/缺省构造方法,如:Person(){}

若类中出现了构造方法,则编译器不再提供任何形式的构造方法。有了就不再送!!!

总之就是若没有自行构造方法,就使用编译器的构造方法,如果自己定义了构造方法,就会使用自己的构造方法。
区别:使用编译器的构造方法输出的是默认值,如果想输入确定的数据就自行定义一个构造方法。
1-14(章节) - 图47
有参构造方法的定义
1-14(章节) - 图48
创建两个对象的内存分析
1-14(章节) - 图49
三、构造方法的作用
• 使用new关键字创建对象时会自动调用构造方法实现成员变量初始化工作。
1-14(章节) - 图50
若定义完有参构造后,却去使用无参构造方法,那么就会出错,因为编译器不再提供无参的构造方法。
若想使用,就必须再定义一个无参的构造函数,但是输出的是默认值,需要给予赋值。
1-14(章节) - 图51

四、重载的概念和体现形式
1,概念:若方法名称相同,参数列表不同,这样的方法之间构成重载关系
(Overload)。
2,体现形式

  • 方法重载的主要形式体现在:参数的个数不同、参数的类型不同、参数

的顺序不同,与返回值类型和形参变量名无关,但建议返回值类型最好
相同。

  • 判断方法能否构成重载的核心:调用方法时能否加以区分。

1-14(章节) - 图52

案例1:

  • 编程实现为Point类添加重载的成员方法:

up() – 实现纵坐标减1的功能。
up(int dy) – 实现纵坐标减去参数指定数值的功能。

  • 测试重载方法的调用规则

package ch9;

public class Point {
int h;
int z;
int up(){
return h—;
}
int up(int dy){
return z-=dy;

  1. }<br /> public static void main(String[] args) {<br /> Point p=new Point();<br /> p.up();<br /> System.out.println(p.h);<br /> p.up(2);<br /> System.out.println(p.z);<br /> }<br />}

五、重载的意义
方法重载的实际意义在于调用者只需要记住一个方法名就可以调用各种
不同的版本,来实现各种不同的功能。
如:java.io.PrintStream类中的println方法。
六、this关键字
1,概念

  • 若在构造方法中出现了this关键字,则代表当前正在构造的对象。构造代表构造!1-14(章节) - 图53
  • 若在成员方法中出现了this关键字,则代表当前正在调用的对象。当前谁调用我就代表谁!1-14(章节) - 图54
  • this关键字本质上就是当前类类型的引用变量。意思就是代表本类对象

    1. **2,原理**
  • 在构造方法中和成员方法中访问成员变量时,编译器会加上this.的前缀,

  • 而this.相当于汉语中”我的”,当不同的对象调用同一个方法时,由于调用

方法的对象不同导致this关键字不同,从而this.方式访问的结果也就随之不同。
1-14(章节) - 图55
3,this的使用方式

  • 当局部变量名与成员变量名相同时,在方法体中会优先使用局部变量(就

近原则),若希望使用成员变量,则需要在成员变量的前面加上this.的前
缀,明确要求该变量是成员变量(重中之重)。

  • 1-14(章节) - 图56

    1. this.name<br /> this.age<br /> 告诉编译器使用的是当前的nameage,即当前类的nameage(成员变量)
  • this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以

作为方法的返回值(重点)。
1-14(章节) - 图57

  • 在构造方法的第一行可以使用this()的方式来调用本类中的其它构造方法

(了解)
七、引用变量的注意事项
•引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不
指向任何对象。
•当某个引用类型变量为null时无法对对象实施访问(因为它没有指向任何
对象)。此时,如果通过引用访问成员变量或调用方法,会产生NullPointerException(空指针) 异常。

八、方法递归调用
1,阶乘实现的方式一(递推)

package ch10;

public class JieChengTest {

  1. //递推<br /> int show(int n){<br /> int num=1;<br /> for (int i = 1; i <=n ; i++) {<br /> num*=i;<br /> }<br /> return num;<br /> }
  2. public static void main(String[] args) {<br /> JieChengTest jieChengTest=new JieChengTest();<br /> int num= jieChengTest.show(2);System.out.println(num);<br /> }<br />}<br /> 2,阶乘实现的方式二(递归)<br /> <br />package ch10;

public class JieChengTest {

// int show(int n){
// int num=1;
// for (int i = 1; i <=n ; i++) {
// num=i;
// }
// return num;
// }
int show(int n){
if(n==1){
return 1;
}
return n
show(n-1);
}
public static void main(String[] args) {
JieChengTest jieChengTest=new JieChengTest();
int num= jieChengTest.show(2);System.out.println(num);
}
}

  1. 3,递归的原理<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778174010-cd3dbc69-e478-424d-8f20-a7b46cdb6273.png#id=WDddc&originHeight=922&originWidth=1860&originalType=binary&ratio=1&status=done&style=none)

算到return1 ,就把show(1)的结果代回去算出结果。

注意事项
•使用递归必须有递归的规律以及退出条件。
•使用递归必须使得问题简单化而不是复杂化。
•若递归影响到程序的执行性能,则使用递推取代之。

案例题目
•编程实现费式数列中第n项的数值并返回。
•费式数列: 1 1 2 3 5 8 13 21 ……
package ch10;

public class FeeTest {
int show(int n){
if(n==1||n==2){
return 1;
}
return show(n-1)+show(n-2);
}

  1. public static void main(String[] args) {<br /> FeeTest feeTest=new FeeTest();<br /> int result=feeTest.show(6);<br /> System.out.println("费氏数列的第六个字段结果是"+result);<br /> }<br />}

1-14(章节) - 图58

虽然递归方便,但是若把n改成55,则会严重影响程序的执行效率(如下是原理图)
1-14(章节) - 图59
此时,要改成递推的方式去解决这个问题!
1-14(章节) - 图60

package ch10;

public class FeeTest {
int show(int n){
int ia=1;
int ib=1;
for (int i = 3; i <=n ; i++) {
int ic=ia+ib;
ia=ib;
ib=ic;
}
return ib;
}

  1. public static void main(String[] args) {<br /> FeeTest feeTest=new FeeTest();<br /> int result=feeTest.show(55);<br /> System.out.println("费氏数列的第六个字段结果是"+result);<br /> }<br />}

注意:
开发中的习惯是把main方法放到测试类中。

九、封装
1,概念

  • 通常情况下可以在测试类给成员变量赋值一些合法但不合理的数值,无论是编译阶段还是运行阶段都不会报错或者给出提示,此时与现实生活不符。
  • 为了避免上述错误的发生,就需要对成员变量进行密封包装处理,来隐藏成员变量的细节以及保证成员变量数值的合理性,该机制就叫做封装。
  1. 2,封装的实现<br /> <br /> 1)私有化成员变量,使用private关键字修饰。<br /> private关键字表示私有的含义,被修饰的变量只能在当前类的内部使用)
  2. 2)提供公有的getset方法,并在方法体中进行合理值的判断。
  3. 3)在构造方法中调用set方法进行合理值的判断。<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778174490-3304adad-24bd-401d-893e-df3e8b488b14.png#id=hQRtr&originHeight=717&originWidth=1113&originalType=binary&ratio=1&status=done&style=none)

为了防止通过有参构造方法实例化又去输入错误的信息,可以在有参构造方法中调用set方法
1-14(章节) - 图61
1-14(章节) - 图62

案例1:

  • 提示用户输入班级的学生人数以及每个学生的信息,学生的信息有:学号、姓名,最后分别打印出来。

    提示:Student[] arr = new Student[num];1-14(章节) - 图63 图中可见打出来的是地址信息,因为如下图new两次地址不一样。
    1-14(章节) - 图64
    解决办法:
    package ch10;

import java.util.Scanner;

public class StudentTest {
public static void main(String[] args) {
//提示输入学生人数
System.out.println(“请输入班级人数”);
Scanner sc=new Scanner(System.in);
int num=sc.nextInt();
//建立对应人数的数组
Student [] arr=new Student[num];
for (int i = 0; i < num; i++) {
System.out.println(“请输入第”+(i+1)+”个学生的信息(学号 姓名)”);
arr[i]=new Student(sc.nextInt(),sc.next());
}
for (int i = 0; i < arr.length ; i++) {
// System.out.println(arr[i]);
arr[i].show();
}
}
}
对应的学生类
package ch10;

public class Student {
int id;
String name;

  1. public Student(int id, String name) {<br /> this.id=id;<br /> this.name=name;<br /> }
  2. public int getId() {<br /> return id;<br /> }
  3. public void setId(int id) {<br /> this.id = id;<br /> }
  4. public String getName() {<br /> return name;<br /> }
  5. public void setName(String name) {<br /> this.name = name;<br /> }
  6. public void show() {<br /> getId();<br /> getName();<br /> System.out.println(getId()+getName());<br /> }<br />}<br />**十、JavaBean的概念(了解)**<br />•<br />JavaBean是一种Java语言写成的可重用组件,其它Java 类可以通过反射机<br />制发现和操作这些JavaBean 的属性。<br />•<br />JavaBean本质上就是符合以下标准的Java类:<br /> 类是公共的<br /> 有一个无参的公共的构造器<br /> 有属性,且有对应的getset方法

七、static和继承


1-14(章节) - 图65
一、static的概念
=》不使用static关键字创建对象的内存指向
1-14(章节) - 图66
=》使用static关键字的内存指向
1-14(章节) - 图67

  • 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层

级提升为类层级,也就是整个类只有一份并被所有对象共享,该成员变
量随着类的加载准备就绪,与是否创建对象无关。

  • static关键字修饰的成员可以使用引用.的方式访问,但推荐类名.的方式。

1-14(章节) - 图68
二、static的使用方式
使用方式

在非静态成员方法中既能访问非静态的成员又能访问静态的成员。
(成员:成员变量 + 成员方法, 静态成员被所有对象共享)

在静态成员方法中只能访问静态成员不能访问非静态成员。
(成员:成员变量 + 成员方法, 因为此时可能还没有创建对象)

在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用
static关键字修饰。(不能滥用static关键字)

实际应用:不用new关键字,通过类名.的方式引用对象或方法
三、构造块和静态代码块

构造块:在类体中直接使用{}括起来的代码块。

每创建一个对象都会执行一次构造块。如下图:
1-14(章节) - 图69

静态代码块:使用static关键字修饰的构造块。

静态代码块随着类加载时执行一次。

案例1:(重点)
package ch10;

public class BlockTest {
{
System.out.println(“代码块”);
}
static{
System.out.println(“静态代码块”);
}
public BlockTest(){
System.out.println(“构造方法”);
}
public static void main(String[] args) {
BlockTest bt=new BlockTest();

}
}
输出:
静态代码块
代码块
构造方法

=》由此可见,static是最先执行输出的,原因是static随着类的加载准备就绪的。
静态代码块的作用:
1-14(章节) - 图70
四、main方法详解
•语法格式:
public static void main(String[] args){}

参数使用的举例。1-14(章节) - 图71

五、单例模式
1,单例设计模式的概念
• 在某些特殊场合中,一个类对外提供且只提供一个对象时,这样的类叫做单例类,而设计单例的流程和思想叫做单例设计模式。—( 单例模式能保证一个类仅有一个实例,并提供一个访问它的全局访问点,同时在类内部创造单一对象,通过设置权限,使类外部无法再创造对象。单例对象能保证在一个JVM中,该对象只有一个实例存在。)
2, 单例设计模式的实现流程

私有化构造方法,使用private关键字修饰。

声明本类类型的引用指向本类类型的对象,并使用private static关键字共
同修饰。

提供公有的get方法负责将对象返回出去,并使用public static关键字共同
修饰
案例题目(重中之重)

编程实现Singleton类的封装。

编程实现SingletonTest类对Singleton类进行测试,要求main方法中能得
到且只能得到该类的一个对象。
1-14(章节) - 图72
1-14(章节) - 图73
内存的执行流程
1-14(章节) - 图74
• 单例设计模式的实现方式有两种:饿汉式 和 懒汉式,在以后的开发中推
荐饿汉式。
饿汉式:定义变量的时候就给初始化;
缺点,消耗空间
懒汉式:后面再判断初始化。
1-14(章节) - 图75

六、继承(重点)
1,继承的由来
1-14(章节) - 图76
2,继承的概念
• 当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成
一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需
要编写自己独有特征和行为的机制,叫做继承。
•在Java语言中使用extends(扩展)关键字来表示继承关系。
•如:
public class Worker extends Person{} - 表示Worker类继承自Person类
其中Person类叫做超类、父类、基类。
其中Worker类叫做派生类、子类、孩子类。

使用继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件。

  1. **3,继承的特点**<br /> <br /> 1)子类不能继承父类的构造方法和私有方法,但私有成员变量可以被继承<br /> 只是不能直接访问。<br /> <br /> 2)无论使用何种方式构造子类的对象时都会自动调用父类的无参构造方法,<br /> 来初始化从父类中继承的成员变量,相当于在构造方法的第一行增加代<br /> super()的效果。<br /> ![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778176594-6a6f7e55-febb-4a0d-9dd2-0e767d92c763.png#height=351&id=tnbdX&originHeight=527&originWidth=1467&originalType=binary&ratio=1&status=done&style=none&width=978)
  2. super关键字<br /> ![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778176694-6203b6f1-ca1b-4e6f-aa59-6d4019d29db6.png#id=xUSHp&originHeight=783&originWidth=945&originalType=binary&ratio=1&status=done&style=none)<br /> <br /> 子类创建的原则:<br /> 3)使用继承必须满足逻辑关系:子类 is a 父类,也就是不能滥用继承。<br /> 如:Bird类不可能继承person类
  3. 4Java语言中只支持单继承不支持多继承,也就是说一个子类只能有一个父<br /> 类,但一个父类可以有多个子类。<br /> ![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778176795-9f82fe13-d0ee-460a-b5ff-a187fc5dec7a.png#height=540&id=kQTvg&originHeight=694&originWidth=1151&originalType=binary&ratio=1&status=done&style=none&width=896)<br /> <br /> ** 七、方法重写**<br /> 1,概念<br /> • 从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写<br />一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式就叫做<br />方法的重写(Override)。<br />person类<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778176897-8f2c2573-6544-4668-b8f7-d4851585a4bb.png#id=ivLzA&originHeight=920&originWidth=1127&originalType=binary&ratio=1&status=done&style=none)<br />worker类<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778177023-82d4cb78-2f35-4ed1-9b0f-2a0b13fe19db.png#id=gODLN&originHeight=942&originWidth=1605&originalType=binary&ratio=1&status=done&style=none)

当子类从写show方法后,子类调用的是重写后的版本

  1. 2,重写的原则<br /> •<br /> 要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许<br /> 返回子类类型。<br /> •<br /> 要求方法的访问权限不能变小,可以相同或者变大。<br /> publicprotectedprivate <br /> •<br /> 要求方法不能抛出更大的异常(异常机制)。
  2. **八、又见构造块和代码块(笔试考点)**<br /> 执行顺序(序号是本类的执行顺序,字母是表示子类调用父类的执行顺序)![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778177146-d0d5f33b-631b-4198-85b4-fd110999e104.png#id=iy5KL&originHeight=749&originWidth=941&originalType=binary&ratio=1&status=done&style=none)

1-14(章节) - 图77

先执行父类的静态代码块,再执行子类的静态代码块。

执行父类的构造块,执行父类的构造方法体。

执行子类的构造块,执行子类的构造方法体。

九、访问控制
1-14(章节) - 图78
注意事项

public修饰的成员可以在任意位置使用。

private修饰的成员只能在本类内部使用。

通常情况下,成员方法都使用public关键字修饰,成员变量都使用private
关键字修饰。

十、package

1,package语句的由来

定义类时需要指定类的名称,但如果仅仅将类名作为类的唯一标识,则
不可避免的出现命名冲突的问题。这会给组件复用以及团队间的合作造
成很大的麻烦!

在Java语言中,用包(package)的概念来解决命名冲突的问题。

2,包的定义

在定义一个类时,除了定义类的名称一般还要指定一个包名,格式如下:
package 包名;
package 包名1.包名2.包名3…包名n;

为了实现项目管理、解决命名冲突以及权限控制的效果。
3,定义包的规范

如果各个公司或开发组织的程序员都随心所欲的命名包名的话,仍然不
能从根本上解决命名冲突的问题。因此,在指定包名的时候应该按照一
定的规范。

org.apache.commons.lang.StringUtil

其中StringUtils是类名而org.apache.commons.lang是多层包名,其含义
如下:org.apache表示公司或组织的信息(是这个公司(或组织)域名的
反写);common 表示项目的名称信息;lang 表示模块的名称信息。
4,包的导入

使用import关键字导入包。

使用import关键字导入静态成员,从Java5.0开始支持。

  1. **十一、final关键字(重点)**<br /> 1,**基本概念**<br /> final本意为"最终的、不可改变的",可以修饰类、成员方法以及成员变量。<br />** 2,使用方式**<br /> •<br /> final关键字修饰类体现在该类不能被继承。<br /> - 主要用于防止滥用继承,如:java.lang.String类等。<br /> •<br /> final关键字修饰成员方法体现在该方法不能被重写但可以被继承。<br /> - 主要用于防止不经意间造成重写,如:java.text.Dateformat类中format方法等。<br /> •<br /> final关键字修饰成员变量体现在该变量必须初始化且不能改变。<br /> - 主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等。<br /> ![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778177487-35798917-389b-4a13-ab1a-fce97fa3c6f5.png#id=QMUdK&originHeight=550&originWidth=791&originalType=binary&ratio=1&status=done&style=none)<br /> 十二、常量<br /> **常量的概念**<br /> •<br /> 在以后的开发中很少单独使用final关键字来修饰成员变量,通常使用<br /> public static final关键字共同修饰成员变量来表达常量的含义,常量的命<br /> 名规范要求是所有字母都要大写,不同的单词之间采用下划线连。<br /> •<br /> public static final double PI = 3.14;

八、多态


1-14(章节) - 图79
一、多态的概念
父类类型引用指向子类

多态主要指同一种事物表现出来的多种形态。

饮料:可乐、雪碧、红牛、脉动、…

宠物:猫、狗、鸟、小强、鱼、…

人:学生、教师、工人、保安、…

图形:矩形、圆形、梯形、三角形、…

二、多态的语法格式
多态的语法格式

父类类型 引用变量名 = new 子类类型();

如:
Shape sr = new Rect();
sr.show();

案例1
案例题目

编程实现Shape类的封装,特征有:横纵坐标,要求提供打印所有特征的
方法。

编程实现Rect类的封装并继承自Shape类,特征有:长度和宽度。

编程实现ShapeRectTest类,在main方法中分别创建Shape和Rect类型对
象并打印特征。
package ch10;
public class shape {
private int x;
private int y;

  1. public shape() {<br /> }
  2. public shape(int x, int y) {<br /> this.x = x;<br /> this.y = y;<br /> }<br /> public int getX() {<br /> return x;<br /> }
  3. public void setX(int x) {<br /> this.x = x;<br /> }
  4. public int getY() {<br /> return y;<br /> }
  5. public void setY(int y) {<br /> this.y = y;<br /> }<br /> public void show(){<br /> System.out.println("横坐标:"+getX()+" "+"纵坐标:"+getY());<br /> }<br />}

package ch10;
public class React extends shape {
private int width;
private int height;

  1. @Override<br /> public void show() {<br /> super.show();<br /> System.out.println("长度:"+getHeight()+" "+"宽度"+width);<br /> }
  2. public React() {<br /> }
  3. public React(int x, int y, int width, int height) {<br /> super(x, y);<br /> this.width = width;<br /> this.height = height;<br /> }
  4. public int getWidth() {<br /> return width;<br /> }
  5. public void setWidth(int width) {<br /> this.width = width;<br /> }
  6. public int getHeight() {<br /> return height;<br /> }
  7. public void setHeight(int height) {<br /> this.height = height;<br /> }<br />}

package ch10;

public class shapeTest {
public static void main(String[] args) {
//声明指向shpae类型引用,指向shape类型的对象
shape s=new shape(12,13);
s.show();//12 13
System.out.println(“—————————————————-“);
//声明指向React类型引用,指向React类型的对象
React r=new React(14 ,15 ,16 ,17);
r.show();//14 15
System.out.println(“—————————————————-“);
//声明指向shpae类型引用,指向React类型的对象
shape s2=new React(16 ,17 ,18 ,19);
//下面的show方法,在编译阶段调用的是shape的show方法,在运行阶段调用的是React的show方法
s2.show();

  1. }<br />}

三、多态的特点

当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调
用父类独有的方法。
//声明指向shpae类型引用,指向React类型的对象
shape s2=new React(16 ,17 ,18 ,19);
//下面的show方法,在编译阶段调用的是shape的show方法,在运行阶段调用的是React的show方法
s2.show();
System.out.println(s2.getX()); //16
说明对于父类类型的引用可以直接调用父类中的方法

当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接
调用子类独有的方法。
1-14(章节) - 图80
说明对于父类类型的引用,无法直接调用子类中独有的方法。

对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段
调用子类重写的版本(动态绑定)。

对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本。
1-14(章节) - 图81

1-14(章节) - 图82

由此可见,对于静态方法来说,不管是编译还是运行阶段,与你创建的对象类型无关,调用的都是父类版本。

四、引用数据类型的转换方式
1,引用数据类型之间的转换

引用数据类型之间的转换方式有两种:自动类型转换 和 强制类型转换。

自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也
叫做向上转型。
如:
//声明指向shpae类型引用,指向React类型的对象
shape s2=new React(16 ,17 ,18 ,19);

强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也
叫做向下转型或显式类型转换。
// 大转小,强制类型转换,由父类型转为子类型
int result=((React) s2).getWidth();

2,注意事项:

引用数据类型之间的转换必须发生在父子类之间,否则编译报错。


若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运
行阶段发生类型转换异常。
1-14(章节) - 图83
说明:s2是指向React类型的对象,无法转换成Circle对象,所以编译时无异常,运行时则产生类型转换异常。

为了避免上述错误的发生,应该在强转之前进行判断,格式如下:
if(引用变量 instanceof 数据类型)【注:如果引用变量所指向的堆区内存里面的对象是intstanceof后面指定的数据类型,则条件成立】
判断引用变量指向的对象是否为后面的数据类型
//先进行判断,判断s2指向的堆区内存里面的对象时Circle这个类型吗?是就返回true,不是就返回false
if(s2 instanceof Circle){
System.out.println(“可以放心转换!”);
Circle c1=(Circle)s2;
}
else
{
System.out.println(“强制转换有风险!操作需谨慎!”);
}

五、Rect类的特征打印~引出多态的实际意义
类作为类型引用
多态使用场合一:通过参数传递形成多态
1-14(章节) - 图84

多态的实际意义
• 多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的
效果。
1-14(章节) - 图85
通过父类的引用,打印想打印的特征。
s.show()编译时调用的是父类,但实际运行过程中,是调用对应的子类对象(多态使用场合二:直接在方法体中使用抽象类类型的引用指向子类类型的对象
通过参数传递形成了多态
即回到中心思想:同一种事物,表现出不同形态

六、抽象方法和抽象类的概念
抽象方法的概念

抽象方法主要指不能具体实现的方法并且使用abstract关键字修饰,也就
是没有方法体。

具体格式如下:
访问权限 abstract 返回值类型 方法名(形参列表);
public abstract void cry();
抽象类的概念
• 抽象类主要指不能具体实例化的类并且使用abstract关键字修饰,也就是
不能创建对象。

抽象类和抽象方法的关系

抽象类中可以有成员变量、构造方法、成员方法;

抽象类中可以没有抽象方法,也可以有抽象方法;

拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是具有
抽象方法并且使用abstract关键字修饰的类。(不能实例化的原因,因为抽象类中可能有抽象方法,而抽象方法没有方法体,实例化引用也没有意义,所以为了防止程序员犯错,抽象类不能被实例化,而由此也可以看出,真正意义上的抽象类不仅仅使用abstract关键字修饰,而且里面要有抽象方法)

抽象类的实际意义

抽象类的实际意义不在于创建对象而在于被继承。

当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也
就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式。

继承抽象类,要么重写方法,要么自己要为抽象类(硬性要求)

七、开发经验分享
1,分析这两种实例化方式的优缺点:

1-14(章节) - 图86

开发经验分享

在以后的开发中推荐使用多态的格式,此时父类类型引用直接调用的所
有方法一定是父类中拥有的方法,若以后更换子类时,只需要将new关键
字后面的子类类型修改而其它地方无需改变就可以立即生效,从而提高
了代码的可维护性和可扩展型。

该方式的缺点就是:父类引用不能直接调用子类独有的方法,若调用则
需要强制类型转换。(多态的特点)

实际应用
抽象类的应用

银行有 定期账户和活期账户。继承自 账户类。账户类中:

public class Account{
private double money;
public double getLixi(){}
}
package ch11;

public abstract class Account {
private double money;

  1. public Account() {<br /> }
  2. public Account(double money) {<br /> this.money = money;<br /> }
  3. public double getMoney() {<br /> return money;<br /> }
  4. public void setMoney(double money) {<br /> this.money = money;<br /> }
  5. public double getlixi(){<br /> return money*0.03*1;<br /> }<br />}

子类:活期账户
package ch11;

public class SubAccount extends Account{
public SubAccount() {
//构造方法
}

  1. public SubAccount(double i) {<br /> super(i);//调用父类的有参构造,这也是抽象方法的实际意义所在<br /> }
  2. @Override<br /> public double getlixi() {<br /> return super.getlixi();<br /> }
  3. public static void main(String[] args) {<br /> //有参调用<br /> Account account=new SubAccount(1000);<br /> double res=account.getlixi();<br /> System.out.println(res);<br /> //无参调用<br /> Account account1=new SubAccount();<br /> account1.setMoney(10000);<br /> double res1=account1.getlixi();<br /> System.out.println(res1);<br /> }<br />}

笔试考点

1-14(章节) - 图87

(1)private 和abstract不能共同修饰,原因:private是私有的,而子类是不能继承父类的私有方法和构造方法的,若抽象类不能被继承,那就失去意义了
(2)final 和 abstract 不能共同修饰,原因:fianal表示最终的,被修饰的方法不能被重写,而子类继承抽象父类就是要通过重写方法,去完成方法的具体实现细节(即方法体的编写),若是无法重写,那也是没意义了
(3)static 和 abstract 不能共同修饰,原因:使用static会把抽象方法提升到类层级,那通过类名.的方式就能调用方法,而抽象方法是不能被直接调用的,所以没有意义

修饰也是这个道理~

八、接口
1,接口的概念
接口的基本概念

接口就是一种比抽象类还抽象的类,体现在所有方法都为抽象方法。

定义类的关键字是class,而定义接口的关键字是interface。

如:
金属接口 货币接口 黄金类

public interface InterfacerTest {
/public static final/ int CNT = 0;//里面只能有常量
/private void show(){}; 新特性,1.9开始允许接口里面有私有方法 /
/public abstract /void show();//前面可以不写,但是建议写上!
}

2,接口的实际作用,多实现
Money接口
public interface Money {
public abstract void buy();
}
Metal接口
public interface Metal {
public abstract void shine();
}

Gold类
package ch11;

public class Gold implements Metal,Money{
@Override
public void shine() {
System.out.println(“是金子总会发光!”);
}

  1. @Override<br /> public void buy() {<br /> System.out.println("有钱才能购物!");<br /> }
  2. public static void main(String[] args) {<br /> Money money=new Gold();<br /> money.buy();<br /> Metal metal=new Gold();<br /> metal.shine();<br /> }<br />}

3,案例1
1-14(章节) - 图88

public interface Runner {
public abstract void run();
}

public interface Hunter extends Runner{
public abstract void hunt();
}
package ch11;

public class Man implements Hunter{
@Override
public void hunt() {
System.out.println(“捕猎”);
}

  1. @Override<br /> public void run() {<br /> System.out.println("奔跑");<br /> }
  2. public static void main(String[] args) {<br /> //使用多态的形式引用<br /> Runner runner=new Man();<br /> runner.run();
  3. Hunter hunter=new Man() ;<br /> hunter.hunt();<br /> }<br />}<br />由上面的例子可以看出类和接口之间的关系

1-14(章节) - 图89

4,抽象类和接口的区别
抽象类和接口的主要区别(笔试题)

定义抽象类的关键字是abstract class,而定义接口的关键字是interface。

继承抽象类的关键字是extends,而实现接口的关键字是implements。

继承抽象类支持单继承,而实现接口支持多实现。

抽象类中可以有构造方法,而接口中不可以有构造方法。

抽象类中可以有成员变量,而接口中只可以有常量。接口中默认private static final修饰
抽象类和接口的主要区别2

抽象类中可以有成员方法,而接口中只可以有抽象方法。

抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需
要重写(Java8以前的版本)。【注:因为8之前接口中的抽象类中都是抽象方法】

从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法,但非
抽象方法需要使用default关键字修饰。
1-14(章节) - 图90

从Java9开始增加新特性,接口中允许出现私有方法。

提供私有方法给本类使用(提高代码的复用):
1-14(章节) - 图91

九、特殊类


一、内部类
1,概念

当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类
(Inner),而这个内部类所在的类叫做外部类(Outer)。

类中的内容:成员变量、成员方法、构造方法、静态成员、构造块和静
态代码块、内部类。
2,实际应用
• 当一个类存在的价值仅仅是为某一个类单独服务时,那么就可以将这个
类定义为所服务类中的内部类,这样可以隐藏该类的实现细节并且可以
方便的访问外部类(所在的这个类)的私有成员而不再需要提供公有的get和set方法。
3,内部类的分类

普通内部类 - 直接将一个类的定义放在另外一个类的类体中。

静态内部类 - 使用static关键字修饰的内部类,隶属于类层级。

局部内部类 - 直接将一个类的定义放在方法体的内部时。

匿名内部类 - 就是指没有名字的内部类。.

内部类的使用时机:
内部类的使用时机

1、实现事件监听器的时候(比方说actionListener 。。。采用内部类很容易实现);

2、编写事件驱动时(内部类的对象可以访问外部类的成员方法和变量,注意包括私有成员);

3、在能实现功能的情况下,为了节省编译后产生的字节码(内部类可以减少字节码文件,即java文件编译后的.class文件);
原文链接:https://blog.csdn.net/yy471101598/article/details/88990500
二、普通内部类(成员内部类)

普通(成员)内部类的格式
• 访问修饰符 class 外部类的类名 {
访问修饰符 class 内部类的类名 {
内部类的类体;
}
}

普通内部类的使用方式

普通内部类和普通类一样可以定义成员变量、成员方法以及构造方法等。

普通内部类和普通类一样可以使用final或者abstract关键字修饰。

普通内部类还可以使用private或protected关键字进行修饰。

普通内部类需要使用外部类对象来创建对象。

如果内部类访问外部类中与本类内部同名的成员变量或方法时,需要使
用this关键字。

package ch12;

/*
内部类的定义 -文档注释
*/
public class NormalOuter {
private int cnt=1;

  1. //定义内部类,隶属于外部类的成员,并且是对象层级<br /> public class Inner{<br /> private int ia=2;<br /> private int cnt=3;<br /> public Inner(){}<br /> public void show(){<br /> System.out.println("外部类的成员变量cnt为:"+cnt);<br /> System.out.println("ia="+ia);<br /> }
  2. public void show2(int cnt){<br /> System.out.println("局部变量的cnt"+cnt); //局部优先原则 -就近原则<br /> System.out.println("内部类变量的cnt"+this.cnt);<br /> System.out.println("外部类变量的cnt"+NormalOuter.this.cnt);<br /> }<br /> }<br />}

package ch12;

public class NormalOuterTest {
public static void main(String[] args) {
//声明NormalOuter类型的引用指向该对象
NormalOuter normalOuter=new NormalOuter();
//声明NormalOuter内部类的引用指向内部类的对象
NormalOuter.Inner inner=normalOuter.new Inner();
inner.show();
System.out.println(“————————————————————-“);
inner.show2(100);
}
}

三、静态内部类
1,静态内部类的格式
• 访问修饰符 class 外部类的类名 {
访问修饰符 static class 内部类的类名 {
内部类的类体;
}
}
package ch12;
/
静态内部类的定义
/
public class StaticOuter {
private int snt=3;

  1. public static class Inner{<br /> public Inner() {<br /> System.out.println("静态内部类构造函数");<br /> }
  2. public void show(){<br /> System.out.println("外部类的cnt="+cnt);//静态不能访问非静态,cnt报错,静态上下文不能访问非静态的变量<br /> // 因为此时可能还没有创建对象<br /> System.out.println("静态内部类成员方法");<br /> }<br /> }<br />}<br />**package ch12;**

public class StaticOuterTest {
public static void main(String[] args) {
StaticOuter.Inner inner=new StaticOuter.Inner();
inner.show();
}
}
2,静态内部类的使用方式

静态内部类不能直接访问外部类的非静态成员。

静态内部类可以直接创建对象。

如果静态内部类访问外部类中与本类内同名的成员变量或方法时,需要
使用类名.的方式访问。
package ch12;

import sun.nio.cs.ext.IBM300;

/
静态内部类的定义
/
public class StaticOuter {
private int ia=2;
private static int snt=3;

  1. public static class Inner{<br /> private static int snt=4;<br /> public Inner() {<br /> System.out.println("静态内部类构造函数");<br /> }
  2. public void show(){<br /> //System.out.println("外部类的cnt="+cnt);//静态不能访问非静态,cnt报错,静态上下文不能访问非静态的变量<br /> // 因为此时可能还没有创建对象<br /> System.out.println("静态内部类成员方法");<br /> }<br /> public void show2(int snt){<br /> System.out.println("snt="+snt);<br /> System.out.println("内部类的snt="+ Inner.snt);<br /> System.out.println("外部类的snt="+StaticOuter.snt);<br /> }<br /> }<br />}

四,局部内部类
1,局部(方法)内部类的格式
• 访问修饰符 class 外部类的类名 {
访问修饰符 返回值类型 成员方法名(形参列表) {
class 内部类的类名 {
内部类的类体;
}
}
}
1-14(章节) - 图92
2,局部内部类的使用方式

局部内部类只能在该方法的内部可以使用。

局部内部类可以在方法体内部直接创建对象。

局部内部类不能使用访问控制符和static关键字修饰符。

局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内
部类和局部变量的声明周期不同所致。
1-14(章节) - 图93

五、回调模式的概念
• 回调模式是指——如果一个方法的参数是接口类型,则在调用该方法时,
需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用
到参数对象中所实现的方法(接口中定义的)。

测试类
1-14(章节) - 图94

实现类
1-14(章节) - 图95

六、匿名内部类的语法格式(重点)
• 接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 };
java8前的写法
1-14(章节) - 图96

java8后提出lamda表达式
1-14(章节) - 图97
开发经验分享

当接口/类类型的引用作为方法的形参时,实参的传递方式有两种:

自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递;

使用上述匿名内部类的语法格式得到接口/类类型的引用即可;

七、枚举
1,枚举的基本概念

一年中的所有季节:春季、夏季、秋季、冬季。

所有的性别:男、女。

键盘上的所有方向按键:向上、向下、向左、向右。

在日常生活中这些事物的取值只有明确的几个固定值,此时描述这些事
物的所有值都可以一一列举出来,而这个列举出来的类型就叫做枚举类
型。

使用private static final修饰一个对象
1-14(章节) - 图98
测试方法
1-14(章节) - 图99
2、枚举的定义

使用public static final表示的常量描述较为繁琐,使用enum关键字来定
义枚举类型取代常量,枚举类型是从Java5开始增加的一种引用数据类型。

枚举值就是当前类的类型,也就是指向本类的对象,默认使用public
static final关键字共同修饰,因此采用枚举类型.的方式调用。

枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默
认也是私有的。
1-14(章节) - 图100
调用:
1-14(章节) - 图101

3,枚举在switch中的引用
1-14(章节) - 图102

4、Enum类的概念和常用方法
static表示类层级
1-14(章节) - 图103

前三个方法的运用
package ch12;

public enum DirectionEnum {
up(“向上”),down(“向下”),left(“向左”),right(“向右”);
private final String desc;
private DirectionEnum(String desc){
this.desc=desc;
}

  1. public String getDesc() {<br /> return desc;<br /> }<br />}<br />package ch12;

public class DirectionEnumTest {
public static void main(String[] args) {
DirectionEnum directionEnum=DirectionEnum.down;
System.out.println(directionEnum.getDesc());
System.out.println(“——————————-“);

  1. //获取DirectionEnum类型中的所有枚举对象<br /> DirectionEnum [] arr=DirectionEnum.values();<br /> //打印DirectionEnum中枚举对象的名称和下标<br /> for (int i = 0; i < arr.length ; i++) {<br /> System.out.println("枚举类型的对象名称是"+arr[i]);<br /> System.out.println("对应的下标是"+arr[i].ordinal());<br /> }<br /> }<br />}<br />后两个的运用<br />注意传参时容易犯的错误,valuesof传的值应该是枚举类型中的对象名称<br />(1)<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778184126-ca744b5c-16ea-4d27-a01a-84d6ce9a5107.png#id=SDzD0&originHeight=151&originWidth=1158&originalType=binary&ratio=1&status=done&style=none)<br />(2)<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778184238-cee6d5e7-f1be-4bc9-a4f2-c5e6d74b09a7.png#id=XgOeX&originHeight=175&originWidth=1209&originalType=binary&ratio=1&status=done&style=none)<br />(3)<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778184369-4263846c-ede2-41e6-8c97-f803312f9645.png#id=xsK49&originHeight=205&originWidth=1301&originalType=binary&ratio=1&status=done&style=none)<br />第四个方法的运用<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778184517-1421148f-c44a-4b3b-b887-71e23cfc70ab.png#id=mdHsQ&originHeight=881&originWidth=1645&originalType=binary&ratio=1&status=done&style=none)<br />比较原则:<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778184767-1e8eed17-8aa6-4aca-b78c-ab52e2b83bc8.png#id=hBs1c&originHeight=255&originWidth=1124&originalType=binary&ratio=1&status=done&style=none)<br />5,**枚举类实现接口的方式**<br />• 枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写<br />一个,或者每个对象都重写。

八、注解(重点)
1,注解的基本概念

注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型。

注解本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、
以及运行时执行指定的处理。
2,注解的语法格式

访问修饰符 @interface 注解名称 {
注解成员;
}

自定义注解自动继承java.lang.annotation.Annotation接口。

通过@注解名称的方式可以修饰包、类、 成员方法、成员变量、构造方
法、参数、局部变量的声明等。

(1)标识注解
1-14(章节) - 图104

(2)定义注解的成员变量
1-14(章节) - 图105
(3)注解成员初始化
1-14(章节) - 图106

3,注解的使用方式

注解体中只有成员变量没有成员方法,而注解的成员变量以“无形参的方
法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该
成员变量的类型。

如果注解只有一个参数成员,建议使用参数名为value,而类型只能是八
种基本数据类型、String类型、Class类型、enum类型及Annotation类型。
package ch12;

public @interface MyAnnoation {
public String value();
//使用default给value默认值
public String value2() default “2”;
}

九、元注解
1,元注解的概念

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但
是它能够应用到其它的注解上面。

元注解主要有 @Retention、@Documented、@Target、@Inherited、
@Repeatable。
2,元注解@Retention

@Retention 应用到一个注解上用于说明该注解的的生命周期,取值如下:

RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时
它将被丢弃忽视。

RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加
载到 JVM 中,默认方式。

RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载
进入到 JVM 中,所以在程序运行时可以获取到它们。

1-14(章节) - 图107
在实际开发中,希望注解什么时候有效,就用哪个值

3,元注解@Documented

使用javadoc工具可以从程序源代码中抽取类、方法、成员等注释形成一
个和源代码配套的API帮助文档,而该工具抽取时默认不包括注解内容。

@Documented用于指定被该注解将被javadoc工具提取成文档。

定义为@Documented的注解必须设置Retention值为RUNTIME。

生成API的工具
1-14(章节) - 图108

作用:使用@Documented表示下面的注解信息可以被javadoc工具提取到API文档中(较少使用)
1-14(章节) - 图109

4,元注解@Target1-14(章节) - 图110

5,元注解@Inherited
• @Inherited并不是说注解本身可以继承,而是说如果一个超类被该注解标
记过的注解进行注解时,如果子类没有被任何注解应用时,则子类就继
承超类的注解。

6,元注解@Repeatable

@Repeatable表示自然可重复的含义,从Java8开始增加的新特性。

从Java8开始对元注解@Target的参数类型ElementType枚举值增加了两个:

其中ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明
语句中,如:泛型。

其中ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。

java8之前处理多个注解的方式
1-14(章节) - 图111
java8后
1-14(章节) - 图112
1-14(章节) - 图113
7,常见预制注解1-14(章节) - 图114
1-14(章节) - 图115

十、常用API和类


1-14(章节) - 图116
一,API的使用
文档,查询对应的包中的类和方法
二、常用的包(熟悉)

  • java.lang包 - 该包是Java语言的核心包,并且该包中的所有内容由Java虚拟机自动导入。
  • 如:System类、String类、…
  • java.util包 - 该包是Java语言的工具包,里面提供了大量工具类以及集合类等。
  • 如:Scanner类、Random类、List集合、…
  • java.io包 - 该包是Java语言中的输入输出包,里面提供了大量读写文件相关的类等。
  • 如:FileInputStream类、FileOutputStream类、…
  • java.net包 - 该包是Java语言中的网络包,里面提供了大量网络编程相关的类等。
  • 如:ServerSocket类、Socket类、…
  • java.sql 包 - 该包是Java语言中的数据包,里面提供了大量操作数据库的类和接口等。
  • 如:DriverManager类、Connection接口、…
  • … …
  • Java程序员在编程时可以使用大量类库,因此Java编程时需要记的很多,对编程能力本身要求不是
  • 特别的高。

三、Object类
1,Object类的基本概念

  • java.lang.Object类是Java语言中类层次结构的根类,也就是说任何一个类都是该类的直接或者间接子类。
  • 如果定义一个Java类时没有使用extends关键字声明其父类,则其父类为 java.lang.Object 类。(万物皆对象)
  • Object类定义了“对象”的基本行为, 被子类默认继承。

2,常用方法
1-14(章节) - 图117

3、equals方法
1,默认使用地址进行比较
Student封装类
package task11;
//student类的封装
public class Student {
private String name;
private int age;

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

public Student() {
}

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;
}
}
package task11;

public class StudentTest {
public static void main(String[] args) {
Student s1=new Student(“zahngfei”,19);
Student s2=new Student(“guangyu”,89);
//调用Object类继承下来的equal方法,默认比较两个对象的地址
boolean b1=s1.equals(s2);
System.out.println(s1==s2);//false
System.out.println(b1); //false
}
}

2,通过使用equals方法比较两个对象的内容是否一致

因为是s1在调用equals方法,所以要在s1中重写equals方法,因为s1是Student类型的,所以在Student封装类中重写。
package task11;
//student类的封装
public class Student {
private String name;
private int age;

  1. public Student(String name, int age) {<br /> this.name = name;<br /> this.age = age;<br /> }
  2. public Student() {<br /> }
  3. //省略get和set代码
  4. /*<br /> * 重写equals方法比较两个对象的内容 -age<br /> * Studnet this=s1;<br /> * Object obj=s2; 多态的体现--引用父类类型指向子类的对象,父类不能直接使用子类独有的方法,此时需要强转(1),<br /> * */<br /> @Override<br /> public boolean equals(Object obj){<br /> //先判断输入的类型是不是指向Studnet的类型,防止出现类型转换异常<br /> if( obj instanceof Student){<br /> //父类不能直接引用子类的独有的方法,需要先进行强转<br /> // return this.getAge()==obj.getAge();<br /> Student ts=(Student)obj;<br /> return this.getAge()==ts.getAge();<br /> }<br /> //类型不一致,没有可比性!,一定不相同,直接返回false<br /> return false;<br /> }<br />}<br />![](javaSe_files/01 equals方法的原理分析.png#id=LEpVv&originalType=binary&ratio=1&status=done&style=none)<br />package task11;

public class StudentTest {
public static void main(String[] args) {
Student s1=new Student(“zahngfei”,19);
Student s2=new Student(“guangyu”,19);
//调用Object的equal方法,默认比较两个对象的地址
//方法重写后,调用的是重写后的版本。
boolean b1=s1.equals(s2);
System.out.println(“b1:”+b1); //此时比较的是age的内容-值

  1. System.out.println(s1==s2); //false 比较的仍然是地址<br /> }<br />}

3、equals方法重写的优化
(1)基本特性

  • equals方法在非null对象引用上实现等价关系:(如果为null,则出现空指针异常)
    • 自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
    • 它是对称的 :对于任何非空引用值x和y , x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
    • 传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true ,然后x.equals(z)应该返回true 。
    • 它是一致的 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,前提是未修改对象上的equals比较中使用的信息。
    • 对于任何非空的参考值x , x.equals(null)应该返回false 。
  • (2)优化

    /
    重写equals方法比较两个对象的内容 -age
    Studnet this=s1;
    Object obj=s2; 多态的体现—引用父类类型指向子类的对象,父类不能直接使用子类独有的方法,此时需要强转(1),
    /
    @Override
    public boolean equals(Object obj){
    //2特殊情况,当调用对象和参数对象指向同一对象时,则内容一定相同
    if(this==obj)return true;

  1. //1特殊情况:根据equals的基本特性,调用的对象不为null,传入的参数为null时,结果一定为false<br /> if(null==obj) return false;
  2. //先判断输入的类型是不是指向Studnet的类型,防止出现类型转换异常<br /> if( obj instanceof Student){<br /> //父类不能直接引用子类的独有的方法,需要先进行强转<br /> // return this.getAge()==obj.getAge();<br /> Student ts=(Student)obj;<br /> return this.getAge()==ts.getAge();<br /> }<br />// 类型不一致,没有可比性!,一定不相同,直接返回false<br /> return false;<br /> }<br />**4,hashcode方法的重写**<br />目的:满足java的常规协定,让hashcode方法的输出值和equals的结果保持一致。<br />package task11;<br />//student类的封装<br />public class Student {<br /> private String name;<br /> private int age;<br /> public Student(String name, int age) {<br /> this.name = name;<br /> this.age = age;<br /> }<br /> public Student() {<br /> }<br /> public String getName() {<br /> return name;<br /> }
  3. public void setName(String name) {<br /> this.name = name;<br /> }
  4. public int getAge() {<br /> return age;<br /> }
  5. public void setAge(int age) {<br /> this.age = age;<br /> }
  6. /*<br /> * 重写equals方法比较两个对象的内容 -age<br /> * Studnet this=s1;<br /> * Object obj=s2; 多态的体现--引用父类类型指向子类的对象,父类不能直接使用子类独有的方法,此时需要强转(1),<br /> * */<br /> @Override<br /> public boolean equals(Object obj){<br /> //2特殊情况,当调用对象和参数对象指向同一对象时,则内容一定相同<br /> if(this==obj)return true;
  7. //1特殊情况:根据equals的基本特性,调用的对象不为null,传入的参数为null时,结果一定为false<br /> if(null==obj) return false;
  8. //先判断输入的类型是不是指向Studnet的类型,防止出现类型转换异常<br /> if( obj instanceof Student){<br /> //父类不能直接引用子类的独有的方法,需要先进行强转<br /> // return this.getAge()==obj.getAge();<br /> Student ts=(Student)obj;<br /> return this.getAge()==ts.getAge();<br /> }<br />// 类型不一致,没有可比性!,一定不相同,直接返回false<br /> return false;<br /> }
  9. //为了使得该方法的结果和equals方法的结果保持一致,为了满足java的常规协定,需要对hashcode方法进行重写<br /> @Override<br /> public int hashCode(){<br /> //因为在上面的equals方法中比较的是age的值,所以返回age就可以判断其比较的值是否一致<br /> //return getAge();//粗糙的写法!!!不是真实的地址值,虽然官方没规定!!!<br /> final int type=12;<br /> return type*31+getAge();//区别判断,暂时这么写~~~<br /> }<br />}

5、toString方法的重写
倘若不重写该方法,打印的是:包名.类名@哈希值的十六进制

//调用从Object类继承下来的toSrtring方法,包名.类名@哈希值的十六进制
String str= s1.toString();
System.out.println(str);
所以为了打印有意义的值,需要重写该方法
Student.java
package task11;
//student类的封装
public class Student {
private String name;
private int age;

  1. public Student(String name, int age) {<br /> this.name = name;<br /> this.age = age;<br /> }
  2. public Student() {<br /> }
  3. public String getName() {<br /> return name;<br /> }
  4. public void setName(String name) {<br /> this.name = name;<br /> }
  5. public int getAge() {<br /> return age;<br /> }
  6. public void setAge(int age) {<br /> this.age = age;<br /> }
  7. /*<br /> * 重写equals方法比较两个对象的内容 -age<br /> * Studnet this=s1;<br /> * Object obj=s2; 多态的体现--引用父类类型指向子类的对象,父类不能直接使用子类独有的方法,此时需要强转(1),<br /> * */<br /> @Override<br /> public boolean equals(Object obj){<br /> //2特殊情况,当调用对象和参数对象指向同一对象时,则内容一定相同<br /> if(this==obj)return true;
  8. //1特殊情况:根据equals的基本特性,调用的对象不为null,传入的参数为null时,结果一定为false<br /> if(null==obj) return false;
  9. //先判断输入的类型是不是指向Studnet的类型,防止出现类型转换异常<br /> if( obj instanceof Student){<br /> //父类不能直接引用子类的独有的方法,需要先进行强转<br /> // return this.getAge()==obj.getAge();<br /> Student ts=(Student)obj;<br /> return this.getAge()==ts.getAge();<br /> }<br />// 类型不一致,没有可比性!,一定不相同,直接返回false<br /> return false;<br /> }
  10. //为了使得该方法的结果和equals方法的结果保持一致,为了满足java的常规协定,需要对hashcode方法进行重写<br /> @Override<br /> public int hashCode(){<br /> //因为在上面的equals方法中比较的是age的值,所以返回age就可以判断其比较的值是否一致<br /> //return getAge();//粗糙的写法!!!不是真实的地址值,虽然官方没规定!!!
  11. final int type=12;<br /> return type*31+getAge();//区别判断,暂时这么写~~~<br /> }
  12. @Override<br /> public String toString(){<br /> return "[age="+getAge()+","+"name="+getName()+"]";<br /> }<br />}<br />StudentTest.java<br />//调用从Object类继承下来的toSrtring方法,包名.类名@哈希值的十六进制<br />String str= s1.toString();<br />System.out.println(str);//当引用一个变量时会自动调用toString方法

对于以上例子,若以Student的name为基准,则有所区别,age是基本数据类型,而name是String 、为引用数据类型,在地址中存放的东西不一样,基本数据类型存放的是具体的数据,引用数据类型存放的是地址。
1-14(章节) - 图118

1-14(章节) - 图119

package task11;
//student类的封装
public class Student {
private String name;
private int age;

  1. public Student(String name, int age) {<br /> this.name = name;<br /> this.age = age;<br /> }
  2. public Student() {<br /> }
  3. public String getName() {<br /> return name;<br /> }
  4. public void setName(String name) {<br /> this.name = name;<br /> }
  5. public int getAge() {<br /> return age;<br /> }
  6. public void setAge(int age) {<br /> this.age = age;<br /> }
  7. /*<br /> * 重写equals方法比较两个对象的内容 -age<br /> * Studnet this=s1;<br /> * Object obj=s2; 多态的体现--引用父类类型指向子类的对象,父类不能直接使用子类独有的方法,此时需要强转(1),<br /> * */<br /> @Override<br /> public boolean equals(Object obj){<br /> //2特殊情况,当调用对象和参数对象指向同一对象时,则内容一定相同<br /> if(this==obj)return true;
  8. //1特殊情况:根据equals的基本特性,调用的对象不为null,传入的参数为null时,结果一定为false<br /> if(null==obj) return false;
  9. //先判断输入的类型是不是指向Studnet的类型,防止出现类型转换异常<br /> if( obj instanceof Student){<br /> //父类不能直接引用子类的独有的方法,需要先进行强转<br /> // return this.getAge()==obj.getAge();<br /> Student ts=(Student)obj;<br /> //return this.getAge()==ts.getAge();
  10. //以姓名为基准,因为toString类中有该方法,所以调用的对象通过调用equanls方法去比较两个字符串<br /> ** //注意这个equals方法是toString里面的方法**<br />** return this.getName().equals(ts.getName());**<br /> }<br />// 类型不一致,没有可比性!,一定不相同,直接返回false<br /> return false;<br /> }
  11. //为了使得该方法的结果和equals方法的结果保持一致,为了满足java的常规协定,需要对hashcode方法进行重写<br /> @Override<br /> public int hashCode(){<br /> //因为在上面的equals方法中比较的是age的值,所以返回age就可以判断其比较的值是否一致<br /> //return getAge();//粗糙的写法!!!不是真实的地址值,虽然官方没规定!!!
  12. final int type=12;<br /> return type*31+getName().hashCode();//toString里面也重写了hashcode的方法<br /> }
  13. @Override<br /> public String toString(){<br /> return "[age="+getAge()+","+"name="+getName()+"]";<br /> }<br />}

后续开发直接用生成的代码即可~

四,包装类的概念和分类
1,包装类的概念
通常情况下基本数据类型的变量不是对象,为了满足万物皆对象的理念就需要对基本数据类型的变量进行打包封装处理变成对象,而负责将这些变量声明为成员变量进行对象化处理的相关类,叫做包装类。
如:
Person p = new Person();
int num = 10;
num 不是对象,将其包装成对象
public class Myint(){
private int num=10;
} 实际上官方已经写好~~~
1-14(章节) - 图120
2,Integer类的概念和构造方式
(1)基本概念
java.lang.Integer类内部包装了一个int类型的变量作为成员变量,主要用于实现对int类型的包装并提供int类型到String类之间的转换等方法。
(2)常用的常量
1-14(章节) - 图121

(3)装箱和拆箱的概念
在Java5发布之前使用包装类对象进行运算时,需要较为繁琐的“拆箱”和“装箱”操作;即运算前先将包装类对象拆分为基本类型数据,运算后再将结果封装成包装类对象。
从Java5开始增加了自动拆箱和自动装箱的功能。
1-14(章节) - 图122

笔试考点:
1-14(章节) - 图123

原题:
原理为自动装箱池
1-14(章节) - 图124
(4)自动装箱池
在Integer类的内部提供了自动装箱池技术,将-128到127之间的整数已经装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,从而提高效率。

五、Integer常用方法
1-14(章节) - 图125
1-14(章节) - 图126

六、Double类的概述
(1)基本概念
java.lang.Double类型内部包装了一个double类型的变量作为成员变量,主要用于实现对double
类型的包装并提供double类型到String类之间的转换等方法。
(2)常用的常量
1-14(章节) - 图127
(3)常用的方法
1-14(章节) - 图128

七、Boolean类的概述
(1)基本概念
java.lang.Boolean类型内部包装了一个boolean类型的变量作为成员变量,主要用于实现对
boolean类型的包装并提供boolean类型到String类之间的转换等方法。
(2)常用的常量
1-14(章节) - 图129
(3)常用方法
1-14(章节) - 图130

拆箱和装箱
java5前
1-14(章节) - 图131

java5后
1-14(章节) - 图132

转换字符串
1-14(章节) - 图133+

八、Character类型的概述
(1)基本概念
java.lang.Character类型内部包装了一个char类型的变量作为成员变量,主要用于实现对char类型
的包装并提供字符类别的判断和转换等方法。
(2)常用的常量
1-14(章节) - 图134
(3)常用的方法
1-14(章节) - 图135

1-14(章节) - 图136
九、包装类的总结(*)
包装类(Wrapper)的使用总结

  1. 基本数据类型转换为对应包装类的方式—-调用包装类的构造方法或静态方法即可
  2. 获取包装类对象中基本数据类型变量数值的方式—-调用包装类中的xxxValue方法即可
  3. 字符串转换为基本数据类型的方式—-调用包装类中的parseXxx方法即可

十、Math类的概念和使用
(1)基本概念
java.lang.Math类主要用于提供执行数学运算的方法,如:对数,平方根。
(2)常用方法
1-14(章节) - 图137
运用:
1-14(章节) - 图138

十一、BigDicmal类的概念和使用
(1)基本概念
由于flfloat类型和double类型在运算时可能会有误差,若希望实现精确运算则借助
java.math.BigDecimal类型加以描述。
(2)常用方法
1-14(章节) - 图139

运用:

package task11;

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

import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalTest {
public static void main(String[] args) {
//两数相加,注意要先转换成字符串,才能进行精确运算
BigDecimal b1=new BigDecimal(Double.toString(0.1));
BigDecimal b2=new BigDecimal(Double.toString(0.2));
System.out.println(b1.add(b2));
//两数相除,注意需要就行舍入模式的设置,一般为四舍五入
BigDecimal b3=new BigDecimal(5);
BigDecimal b4=new BigDecimal(0.3);
System.out.println(b3.divide(b4, RoundingMode.HALF_UP));
}
}
1-14(章节) - 图140
参考:https://blog.csdn.net/shadow_zed/article/details/73522157
十二、BigInteger类的概念和使用
(1)基本概念
若希望表示比long类型范围还大的整数数据,则需要借助java.math.BigInteger类型描述。
(2)常用方法
1-14(章节) - 图141
运用:
1-14(章节) - 图142

十一、字符串


1-14(章节) - 图143
一、String类和常量池的概念(重点)
1、String类的概念

  • java.lang.String类用于描述字符串,Java程序中所有的字符串字面值都可以使用该类的对象加以描述,如:”abc”。
  • 该类由final关键字修饰,表示该类不能被继承。
  • 从jdk1.9开始该类的底层不使用char[]来存储数据,而是改成 byte[]加上编码标记,从而节约了一些空间。
  • 该类描述的字符串内容是个常量不可更改,因此可以被共享使用。

    1. 如:<br /> String str1 = abc”; - 其中"abc"这个字符串是个常量不可改变。<br /> str1 = 123”; - 将“123”字符串的地址赋值给变量str1。<br /> - 改变str1的指向并没有改变指向的内容<br />![](javaSe_files/01 String类型引用的指向改变.png#id=XmbQE&originalType=binary&ratio=1&status=done&style=none)<br />改变的是Str1的地址,str1与abc的联系断开,并被java的垃圾回收机制(GC)回收。<br /> ** 2,常量池的概念**<br /> 由于String类型描述的字符串内容是常量不可改变,因此Java虚拟机将首次出现的字符串放入常量池中,若后续代码中出现了相同字符串内容则直接使用池中已有的字符串对象而无需申请内存及创建对象,从而提高了性能。<br /> (常量池有则不再new对象,常量池没有,new对象并把新出现的字符串放进常量池)<br />![](https://cdn.nlark.com/yuque/0/2021/png/22605889/1631778190850-61811f70-e195-4c25-ba3b-11d9daeda7c7.png#id=v9aPP&originHeight=737&originWidth=1189&originalType=binary&ratio=1&status=done&style=none)<br />一样的字符串,同样的内存空间。

二、String类的常用方法
1-14(章节) - 图144

1,使用byte[ ]构建字符串对象
1-14(章节) - 图145

2,使用char [ ]构建字符串对象
1-14(章节) - 图146

3,使用字符串构建字符串
//使用字符串来构建字符串
String str3=new String(“world”);
System.out.println(str3);

三、String的笔试考点(*)
1-14(章节) - 图147
常量有优化机制,变量没有优化机制!!!
1-14(章节) - 图148
1-14(章节) - 图149
四、String类常用的成员方法

1-14(章节) - 图150

(1)把当前字符串转换为数组(byte数组)并返回
1-14(章节) - 图151
//把字符串转换为数组
String s3=”Hello”;
byte [] arr3=s3.getBytes();
for (int i = 0; i System.out.println(arr3[i]);
}

//转回字符串
String s4=new String(arr3);
System.out.println(“转为字符串为:”+s4);

(2)把当前字符串转换为数组(char数组)并返回

//把字符串转为char数组
char [] arr4=s3.toCharArray();
for (int i = 0; i < arr4.length ; i++) {
System.out.println(arr4[i]);
}

//char数组转回String
String s6=new String(arr4);
System.out.println(“char转回的字符串为:”+s6);

1-14(章节) - 图152
(3)返回字符串长度,根据下标获取数组元素
1-14(章节) - 图153
(4)String类中字符的获取和常见笔试考点
1-14(章节) - 图154
字符‘1’对应数字49,对战ASCII表
(5)案例
判断字符串“上海自来水来自海上”是否为回文并打印,所谓回文是指一个字符序列无论从左向右读
还是从右向左读都是相同的句子

1-14(章节) - 图155

(6)字符串的比较
一个字符一个字符比(根据ASCII码表)
1-14(章节) - 图156
1-14(章节) - 图157

(7)String各种方法的使用
1-14(章节) - 图158

7.1测试是否包含— 转换成大小写
1-14(章节) - 图159

7.2trim()去除空格,比如\t等字符
1-14(章节) - 图160

7.3判断开头以什么开头。

1-14(章节) - 图161

(8)案例—判断是否能登录
===》》》1-14(章节) - 图162

8.1也可以使用忽略大小写的方法:
===》》》1-14(章节) - 图163

8.2自己写的代码!!!哈哈哈~
package task11;

import java.util.Scanner;

public class Login {
public static void main(String[] args) {
for(int i=3;i>0;i—){
System.out.println(“请输入账号名:”);
Scanner scanner=new Scanner(System.in);
String username=scanner.next();
System.out.println(“请输入密码:”);
String password=scanner.next();
if(username.equals(“admin”)&&password.equals(“123456”)){
System.out.println(“登录成功!欢迎您!”);
break;
}
else {
if((i-1)==0){
System.out.println(“账号已冻结!请联系客服!”);
break;
}
else
System.out.println(“您还有”+(i-1)+”次机会”);
}
scanner.close();
}
}
}

(9)字符串正向查找(查找首次出现的位置!)
1-14(章节) - 图164
———————————————————————————————————————————-分隔线————————————————————————————————————————————————————————》
1-14(章节) - 图165

—————————————————————————————————————————————————————————
1-14(章节) - 图166

(10)字符串反向查找(查找最后出现的位置)
1-14(章节) - 图167
———————————————————————————————————————————-分隔线————————————————————————————————————————————————————————》
反向查找的第一个就是正向查找的最后一个找不到就返回-1
1-14(章节) - 图168
———————————————————————————————————————————-分隔线————————————————————————————————————————————————————————》
(11)获取字符串,截取一段字符串~(使用下标作为参数)
1-14(章节) - 图169
———————————————————————————————————————————-分隔线————————————————————————————————————————————————————————》

1-14(章节) - 图170
案例
提示用户从键盘输入一个字符串和一个字符,输出该字符(不含)后面的所有子字符串。
1-14(章节) - 图171
五、正则表达式
1,正则表达式的概念
正则表达式本质就是一个“规则字符串”,可以用于对字符串数据的格式进行验证,以及匹配、查 找、替换等操作。该字符串通常使用^运算符作为开头标志,使用$运算符作为结尾标志,当然也可以省略。
2,正则表达式的规则
1-14(章节) - 图172
1-14(章节) - 图173
3正则表达式的相关方法
1-14(章节) - 图174
4、案例
案例题目
使用正则表达式描述一下银行卡密码的规则:要求是由6位数字组成。
1-14(章节) - 图175
若是用\d需要再加一个\,转义一下
1-14(章节) - 图176
使用正则表达式描述一下QQ号码的规则:要求是由非0开头的5~15位数组成。
使用正则表达式描述一下手机号码的规则:要求是由1开头,第二位数是3、4、5、7、8中的一 位,总共11位
描述身份证号码的规则:总共18位,6位数字代表地区,4位数字代表年,2位数字代表月,2位数 字代表日期, 3位数字代表个人,最后一位可能数字也可能是X
1-14(章节) - 图177
5、方法~~
1-14(章节) - 图178
1-14(章节) - 图179
1-14(章节) - 图180

十二、可变字符串和日期类


1-14(章节) - 图181
需要练熟和背诵!!!
1-14(章节) - 图182

一、构造方法
1-14(章节) - 图183
二、插入字符串
1-14(章节) - 图184

1-14(章节) - 图185

三、超过默认容量时字符串容量的算法(看源码append)
1-14(章节) - 图186

四、字符串的删除
(1)第一个删除的方法
1-14(章节) - 图187
(2)起始终止删除字符串
1-14(章节) - 图188
(3)修改字符串
1-14(章节) - 图189
(4)查找和反转字符串
1-14(章节) - 图190
五、笔试考点
1-14(章节) - 图191

六、System类
(1)距离1970年1月1日已经过去的时间(毫秒)1-14(章节) - 图192七、Date类(JAVA8前)
(1)常用的方法
1-14(章节) - 图193
(2 )SimpleDateFormat类
1-14(章节) - 图194
(3)Calender类(设置指定日期—全球化)
1-14(章节) - 图195
(4)向指定字段设置和添加数值
1-14(章节) - 图196
(5)考点,Calendar类的多态使用
1-14(章节) - 图197

八、java 8的日期类
(1)localDateTime类的方法
一般获取dateTime
1-14(章节) - 图198
(2)使用指定参数来设置日期时间
1-14(章节) - 图199
(3)获取日期具体数值信息
1-14(章节) - 图200
(4)LocalDateTime类型的不可变性
1-14(章节) - 图201
(5)日期具体数值的增删改
1-14(章节) - 图202
注意创建了新的一个对象

九、instant类
(1)获取当前时间,与上面获取当前时间的区别是这个不是默认时区的。
1-14(章节) - 图203
所以要设置偏移量
1-14(章节) - 图204

(2)根据距离基准时间的毫秒数来获取当前时间
1-14(章节) - 图205

(3) DateTimeFormatter类的引用
1-14(章节) - 图206

十三、集合(上)


1-14(章节) - 图207

1-14(章节) - 图208

一、集合框架
Collection 继承Iterator

01 集合框架的两张图.png
因为每一个容器对数据的存储方式不同,所以有了这些容器,这些存储方式称为数据结构!
二、集合collection调用子类,形成多态
1-14(章节) - 图210
(1)添加多个元素
1-14(章节) - 图211
(2)add和addall方法的区别
1-14(章节) - 图212
(3)contains方法(重点)
person类重写方法后重新比较才是true。
1-14(章节) - 图213
(4)retainAll方法(取两个集合的交集),注意该方法的返回值:当前集合发生改变返回true,当前集合未发生改变返回false!
如下图所示,c2和c2取交集还是c2,未改变返回false,当c2与c3取交集,集合c2发生改变,返回true.
1-14(章节) - 图214
(5)删除操作—去交集
1-14(章节) - 图215
(6)其他方法的测试
1-14(章节) - 图216

1-14(章节) - 图217
(7)数组和集合之间的转换
1-14(章节) - 图218
(8)Collection集合迭代器
原理图
迭代器原路.png
Image.png
1-14(章节) - 图221
用while循环(不确定循环次数)
1-14(章节) - 图222
(9)迭代器实现toString的效果
Arraylist已重写toString方法
1-14(章节) - 图223
(10)使用迭代器的remove()删除元素
1-14(章节) - 图224
注意:不能使用集合的remove方法,因为在迭代过程中,不允许其他元素的进进出出
(11)foreach循环
1-14(章节) - 图225
三、List集合
(1)由源码可知,当ArrayList长度不足时,扩容为原始长度的1.5倍
1-14(章节) - 图226

02 ArrayList类的底层原理.png

(2)linkedlist是链表结构
—原理图—

03 LinkedList类的底层原理.png
(3)list方法—获取、查找
1-14(章节) - 图229
(4)list方法—增加

  1. package task12;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. public class LinkedlistTest {
  5. public static void main(String[] args) {
  6. List list=new LinkedList();
  7. System.out.println(list);//啥也没有
  8. list.add(0,"one");//[one]
  9. list.add(1,3);//[one,3]
  10. list.add(1,"two");//[one,two,3]
  11. System.out.println(list);
  12. }
  13. }

(5)list方法—修改和删除
1-14(章节) - 图230

5.1 修改——E返回值表示的是原先下标保存的元素

  1. package task12;
  2. import sun.security.rsa.RSAUtil;
  3. import java.util.LinkedList;
  4. import java.util.List;
  5. public class LinkedlistTest {
  6. public static void main(String[] args) {
  7. List list=new LinkedList();
  8. System.out.println(list);//啥也没有
  9. list.add(0,"one");//[one]
  10. list.add(1,3);//[one,3]
  11. list.add(1,"two");//[one,two,3]
  12. System.out.println(list);
  13. //修改集合的元素
  14. String s1= (String) list.set(0,3);
  15. System.out.println(s1);
  16. System.out.println("修改后集合元素为"+list);
  17. }
  18. }

1-14(章节) - 图231
5.2删除

  1. //删除list中的元素 i在增加,siz在减小,所以直接不用i++
  2. for (int i = 0; i < list.size() ;/* i++*/) {
  3. System.out.println("删除的元素为"+ list.remove(0));//因为每次删除后,list.size()会跟着改变
  4. }
  5. System.out.println(list);//[]

5.3获取部分元素

  1. //获取集合中的部分元素--获取0和1索引位置的元素,不包含2索引所在位置的元素
  2. List l2=list.subList(0,2);
  3. System.out.println("获取到的部分元素为"+l2);

注意:
1-14(章节) - 图232
就是lt1和lt2使用的是同个内存,所以lt2移除元素时,lt1的同个元素也被移除。
6,stack案例,出栈和入栈操作

  1. package task12;
  2. import java.util.Stack;
  3. public class StackTest {
  4. public static void main(String[] args) {
  5. //引用stack类型
  6. Stack stack=new Stack();
  7. Stack stack1=new Stack();
  8. //依次入栈
  9. for (int i = 1; i <=5 ; i++) {
  10. stack.push(i*11);
  11. System.out.println(stack);
  12. }
  13. //获取栈顶元素
  14. Object obj=stack.peek();//55
  15. System.out.println("栈顶元素为"+obj);
  16. //依次出栈
  17. int length=stack.size(); //只获取一次栈的大小,在循环里面获取,出栈时会影响栈的大小
  18. for (int i = 0; i <length ; i++) {
  19. Object pop = stack.pop();
  20. System.out.println("出栈的元素为"+pop);
  21. //出栈后进入stack1
  22. stack1.push(pop);
  23. }
  24. System.out.println("出栈后的栈为"+stack);
  25. System.out.println("--------------------------------");
  26. System.out.println("从stack获取的元素为"+stack1);
  27. //StacK1出栈
  28. for (int i = 0; i <length; i++) {
  29. Object obj1=stack1.pop();
  30. System.out.println("出栈元素为"+obj1);
  31. }
  32. }
  33. }

7,Queue集合的概念和使用
(1)基本概念

  • java.util.Queue集合是Collection集合的子集合,与List集合属于平级关系。
  • 该集合的主要用于描述具有先进先出特征的数据结构,叫做队列(fifirst in fifirst out FIFO)。
  • 该集合的主要实现类是LinkedList类,因为该类在增删方面比较有优势。

(2)常用方法
1-14(章节) - 图233
(3)案例实现
1-14(章节) - 图234

十四、泛型机制和Map集合(下)


1-14(章节) - 图235
一、泛型
使用object类型,即万物皆对象的方式虽然可以存取,但是进行强制转换时,容易发生类型转换异常,所以引用泛型机制。
(1)泛型的概念

  • 通常情况下集合中可以存放不同类型的对象,是因为将所有对象都看做Object类型放入的,因此从集合中取出元素时也是Object类型,为了表达该元素真实的数据类型,则需要强制类型转换,而强制类型转换可能会引发类型转换异常。
  • 为了避免上述错误的发生,从Java5开始增加泛型机制,也就是在集合名称的右侧使用<数据类型>的方式来明确要求该集合中可以存放的元素类型,若放入其它类型的元素则编译报错。(即定义什么类型就为什么类型,由此避免了类型的转换异常)
  • 泛型只在编译时期有效,在运行时期不区分是什么类型。

(2)泛型的编程使用
//基本使用
package task12;

import java.util.LinkedList;
import java.util.List;

public class ListGenericTest {
public static void main(String[] args) {
//引用泛型机制的集合
List list=new LinkedList();
//增加元素,增加的元素必须为Integer类型的,否者会报错
list.add(0,1);
System.out.println(list.get(0));
}

}

如果想要一个String类型的,可以自己再定义一个泛型机制的集合
List list=new LinkedList();

(3)泛型机制的菱形
jvav7开始的新特性,泛型机制的菱形,就是后面的<>可以省略
List list=new LinkedList<>();

(4)*笔试考点
1-14(章节) - 图236
泛型只在编译时期有效,在运行时期不区分是什么类型。

(5)泛型机制的底层原理
1-14(章节) - 图237

(6)自定义泛型类

  • 泛型类和普通类的区别就是类名后面添加了类型参数列表,可以有多个类型参数,如:等。
  • 实例化泛型类时应该指定具体的数据类型,并且是引用数据类型而不是基本数据类型。
  1. package task12;
  2. /**
  3. * 自定义泛型类
  4. *
  5. * */
  6. class Person <T> {
  7. private int age;
  8. private String name;
  9. private T gender;
  10. public Person(int age, String name, T gender) {
  11. this.age = age;
  12. this.name = name;
  13. this.gender = gender;
  14. System.out.println();
  15. }
  16. public Person() {
  17. }
  18. public int getAge() {
  19. return age;
  20. }
  21. public void setAge(int age) {
  22. this.age = age;
  23. }
  24. public String getName() {
  25. return name;
  26. }
  27. public void setName(String name) {
  28. this.name = name;
  29. }
  30. public T getGender() {
  31. return gender;
  32. }
  33. public void setGender(T gender) {
  34. this.gender = gender;
  35. }
  36. @Override
  37. public String toString() {
  38. return "Person{" +
  39. "age=" + age +
  40. ", name='" + name + '\'' +
  41. ", gender=" + gender +
  42. '}';
  43. }
  44. }

自定泛型类的使用
1-14(章节) - 图238
(同上!)
1-14(章节) - 图239

实例化时定义什么类型,就传入什么类型,这也正符合了泛型的底层原理,参数类型传参!

  • 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型。
  • 子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型。

子类继承泛型类的方式
1-14(章节) - 图240
1-14(章节) - 图241
(7)自定义泛型方法
1-14(章节) - 图242
注意:以下例子不是泛型方法
1-14(章节) - 图243

不能加static,因为T是实例化的时候才设定的类型,如果加上Static,直接类名调用,就没有意义了。
(8)泛型在继承上的体现
如果B是A的一个子类或子接口,而G是具有泛型声明的类或接口,则G并不是G的子类型!
比如:String是Object的子类,但是List并不是List的子类。

如下所示:不能进行类型转换,Animal 和 Dog不具备父子类关系1-14(章节) - 图244
(9)通配符的使用
有时候我们希望传入的类型在一个指定的范围内,此时就可以使用泛型通配符了。
如:之前传入的类型要求为Integer类型,但是后来业务需要Integer的父类Number类也可以传
入。
泛型中有三种通配符形式:
<?> 无限制通配符:表示我们可以传入任意类型的参数。
但是不支持添加操作,因为?表示任意类型,但是不知道你会传入什么类型。
1-14(章节) - 图245
<? extends E> 表示类型的上界是E,只能是E或者是E的子类。
意思就是<>里放的是Animal类型或者是Animal类型,进行添加操作时:
如果你添加Animal,但是<>里存放的刚好时候Dog,那你添加的类型就比Dog类型大了,所以不支持添加操作!
如果你添加Dog,但是<>里存放的是Dog的子类类型,那也不符合。
1-14(章节) - 图246
<? super E> 表示类型的下界是E,只能是E或者是E的父类。
支持添加操作,因为<>里存放的类型可能就是添加类型的类型或者父类类型。
1-14(章节) - 图247

二、Set
(1)基本概念
01 集合框架的两张图.png

  • java.util.Set集合是Collection集合的子集合,与List集合平级。
  • 该集合中元素没有先后放入次序,且不允许重复。(*)**
  • 其中TreeSet类的底层是采用红黑树进行数据管理的。
  • 其中LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。

(2)Set常用方法
1-14(章节) - 图249
HashSet 和 使用LinkedHashSet的区别,使用linkedHashSet有顺序:
1-14(章节) - 图250

  • 该集合的主要实现类是:HashSet类 和 TreeSet类以及LinkedHashSet类。
  • 其中HashSet类的底层是采用哈希表进行数据管理的。

1-14(章节) - 图251
(3)笔试考点
*(3.1)元素放入HashSet集合的原理:

  1. 使用元素调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算出该元素在数组中的索引位置。
  2. 若该位置没有元素,则将该元素直接放入即可。
  3. 若该位置有元素,则使用新元素与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。
  4. 若新元素与已有元素的哈希值相同,则使用新元素调用equals方法与已有元素依次比较。
  5. 若相等则添加元素失败,否则将元素直接放入即可。

(3.2)思考:为什么要求重写equals方法后要重写hashCode方法呢?
解析:当两个元素调用equals方法相等时证明这两个元素相同,重写hashCode方法后保证这两个元
素得到的哈希码值相同,由同一个哈希算法生成的索引位置相同,此时只需要与该索引位置已有元素比较即可,
从而提高效率并避免重复元素的出现。

三、TreeSet集合
(1)基本概念

  • 二叉树主要指每个节点最多只有两个子节点的树形结构。
  • 满足以下3个特征的二叉树叫做有序二叉树。

a.左子树中的任意节点元素都小于根节点元素值;
b.右子树中的任意节点元素都大于根节点元素值;
c.左子树和右子树的内部也遵守上述规则;

  • 由于TreeSet集合的底层采用红黑树进行数据的管理,当有新元素插入到TreeSet集合时,需要使
  • 用新元素与集合中已有的元素依次比较来确定新元素的合理位置。
  • 比较元素大小的规则有两种方式:
  • 使用元素的自然排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口;
  • 使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口;
  • 自然排序的规则比较单一,而比较器的规则比较多元化,而且比较器优先于自然排序;

红黑树就是特殊的有序二叉树
1-14(章节) - 图252
(2)TreeSet放人String对象的实现
1-14(章节) - 图253
由此上程序可知,排序的规则为自然排序,那为何是自然排序呢?
我们可以看下String对象的源码:
1-14(章节) - 图254

因为String对象实现了Comparable接口!

(3)TreeSet比较器规则—自然排序

  1. package task12;
  2. public class Student implements Comparable<Student>{
  3. private String name;
  4. private int age;
  5. public Student(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. public int getAge() {
  16. return age;
  17. }
  18. public void setAge(int age) {
  19. this.age = age;
  20. }
  21. @Override
  22. public String toString() {
  23. return "Student{" +
  24. "name='" + name + '\'' +
  25. ", age=" + age +
  26. '}';
  27. }
  28. @Override
  29. public int compareTo(Student o) {
  30. //return 0;//调用对象和参数相等,调用对象就是新增加的对象
  31. //return 1; //调用对象小于参数
  32. //return 1;//调用对象大于参数
  33. //return this.getName().compareTo(o.getName());//按姓名比
  34. //return this.getAge()-o.getAge();//按年龄比
  35. /* int ia=this.getName().compareTo(o.getName());
  36. if (0==ia){
  37. return this.getAge()-o.getAge();
  38. }
  39. return this.getName().compareTo(o.getName());*/
  40. //使用三目运算符
  41. int ia=this.getName().compareTo(o.getName());
  42. return 0==ia? this.getAge()-o.getAge():ia;
  43. }
  44. }

定义的Student类需要实现Comparable接口,并实现compareTo方法,这样才能进行插入操作,而插入操作的排序问题可以进行自定义。

  1. package task12;
  2. import java.util.Set;
  3. import java.util.TreeSet;
  4. public class TreeSetTest {
  5. public static void main(String[] args) {
  6. Set<String> s1=new TreeSet<>();
  7. s1.add("aa");
  8. s1.add("bb");
  9. System.out.println(s1);
  10. s1.add("cc");
  11. System.out.println(s1);
  12. //TreeSet插入引用对象类型
  13. Set<Student> s2=new TreeSet<>();
  14. s2.add(new Student("Zhangfei",19));
  15. s2.add(new Student("liubei",20));
  16. s2.add(new Student("LINMINGLANG",1));
  17. System.out.println(s2);
  18. }
  19. }

通过比较年龄的输出内容。。。
1-14(章节) - 图255

(4)comparator比较器
自然排序的规则比较单一,而比较器的规则比较多元化,而且比较器优先于自然排序;

  1. //构建一个比较器
  2. //匿名内部类 接口/父类类型 变量名=new 接口/父类类型(){}
  3. Comparator <Student> comparator=new Comparator<Student>() {
  4. @Override
  5. public int compare(Student o1, Student o2) {
  6. return o1.getAge()-o2.getAge();
  7. }
  8. } ;
  9. //TreeSet插入引用对象类型
  10. // Set<Student> s2=new TreeSet<>();
  11. Set<Student> s2=new TreeSet<>(comparator);//指定比较器
  12. s2.add(new Student("Zhangfei",19));
  13. s2.add(new Student("liubei",20));
  14. s2.add(new Student("LINMINGLANG",1));
  15. System.out.println(s2);

也可以用lambda表达式
1-14(章节) - 图256

四、Map集合
(1)Map集合的基本概念

  • java.util.Map集合中存取元素的基本单位是:单对元素,其中类型参数如下:
  • K - 此映射所维护的键(Key)的类型,相当于目录。
  • V - 映射值(Value)的类型,相当于内容。
  • 该集合中key是不允许重复的,而且一个key只能对应一个value。
  • 该集合的主要实现类有:HashMap类、TreeMap类、LinkedHashMap类、Hashtable类、
  • Properties类。
  • 其中HashMap类的底层是采用哈希表进行数据管理的。
  • 其中TreeMap类的底层是采用红黑树进行数据管理的。
  • 其中LinkedHashMap类与HashMap类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。
  • 其中Hashtable类是古老的Map实现类,与HashMap类相比属于线程安全的类,且不允许null作为key或者value的数值。
  • 其中Properties类是Hashtable类的子类,该对象用于处理属性文件,key和value都是String类型的。
  • Map集合是面向查询优化的数据结构, 在大数据量情况下有着优良的查询性能。
  • 经常用于根据key检索value的业务场景。

07 Set集合转换为Map集合的原理.png
(2)常用方法
1-14(章节) - 图258

—- v put(K key, V value) 方法
如果不存在插入的数据就返回null,如果存在一样的key值,就修改其对应的value值。
1-14(章节) - 图259
1-14(章节) - 图260
(3)元素放入HashMap集合的原理
使用元素的key调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算在数组中的索引
位置。
若该位置没有元素,则将该键值对直接放入即可。
若该位置有元素,则使用key与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放
入。
若key与已有元素的哈希值相同,则使用key调用equals方法与已有元素依次比较。
若相等则将对应的value修改,否则将键值对直接放入即可。
(4)Map查找的方法
1-14(章节) - 图261
1-14(章节) - 图262
(5)删除操作
1-14(章节) - 图263

1-14(章节) - 图264

(6)Map集合的遍历
1-14(章节) - 图265
1
1-14(章节) - 图266
2,3
1-14(章节) - 图267

五、Collections类【注意与Collection接口的区别】
常用方法
1-14(章节) - 图268
1-14(章节) - 图269
1-14(章节) - 图270
copy操作,目的集合要大于原集合的大小,否则出现下标越界异常!
1-14(章节) - 图271