Java笔记--2025/4/28

基础

注释:

1.单行注释

2.多行注释(不可以嵌套使用,这应该都知道吧)

3.文档注释(java特有):注释内容可以 被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档

 

用javadoc所解析的类,通常前面加public

暂时不知道IDEA怎么用文档注释

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

API是Java提供的基本编程接口

 

java提供的类库称作API

对第一个java程序的总结

  1. java程序编写--编译--运行的过程

编写:我们将编写的java代码保存在以“.java”为后缀的源文件中

编译:使用javac.exe命令编译我们的java源文件。格式:javac 源文件名.java

运行:使用java.exe命令解释运行我们的字节码文件。格式:java 类名

  1. 在一个java源文件中可以声明多个class。但是,最多有一个类声明为public的。

     

    public只能加到与文件名同名的类上

  2. 程序的入口是main方法,格式是固定的

    public static void main(String[] args){
    }
    //虽说是固定的,但其实args可以变,args是arguments(参数)的缩写,可以改为任意变量
    //中括号的位置也可以变,可以放在参数的后面
    //但是习惯上中括号放在前面,后面参数用args
  3. 输出语句:

    System.out.println("Hello,World!");输出之后换行	
    System.out.print("Hello,World!");//输出之后不换行
    System.out.print();//换行
  4. 每一行执行语句都以";"结束

  5. 编译的过程:编译以后,会生成一个或多个字节码文件。字节码文件的文件名与java源文件中声明的类名相同,有几个类,就会有几个字节码文件

java集成开发环境(Intergrated Development Environment)

基本语法

(上)变量和运算符

关键字和保留字

关键字(keyword)的定义与特点

  • 定义:在java中被赋予特殊含义,用作专门用途的字符串

  • 特点:关键字中的所有字母均为小写

保留字(reserved word)

  • 在现有java版本中尚未使用,但以后版本中可能作为关键字使用,自己命名标识符的时候要避免使用这些保留字

  • goto、const

标识符(Identifier)

  • 定义:Java对各种变量、方法和类等要素命名时使用的字符序列称为标识符

  • 技巧:凡是可以自己起名的地方都叫标识符

  • 定义合法的标识符规则:

    1. 由26个英文字母大小写,0-9,_或$组成

    2. 数字不可以开头

    3. 不可以使用关键字和保留字,但能包含关键字和保留字

    4. Java中严格区分大小写,长度无限制

    5. 标识符不能包含空格

Java中的命名规范

包名:多单词组成时所有字母都小写

类名、接口名:多单词组成时,所有单词首字母大写

变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个首字母大写

常量名:所有字母都大写。多单词时,每个单词用下划线连接

 

java使用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用

变量

先声明,后使用

除了字符串类型,其他类型println时不需要加双引号

如果一个变量未赋值就打印,会报错

变量都定义在其作用域内

同一个作用域内,不可以声明同名的变量

作用域:其定义所在的一对{}内

数据类型

image-20250310093734976

整形:

java各整数类型有固定的表数范围和字段长度,不受具体OS的影响,以保证java程序的可移植性

java的整形常量默认为int型,声明long型常量需后加“l”或“L”

java程序中变量通常声明为int型,除非不足以表示较大的数,采用long

image-20250310094351854

浮点型:

与整数类型相似,Java浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响

浮点数常量有两种表示形式:

  • 十进制数形式:如5.12、512.0f、.512(必须有小数点)

  • 科学计数法形式:如5.12e2、512E2、100E-2

float:单精度,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求

double:双精度,精度时float的两倍。通常采用此类型

Java的浮点型常量默认为double型,声明float型常量,需后加“f”或“F”

image-20250310095552268

虽然float只占4字节,但是它的表数范围比long还大

字符类型:char

  • char型数据用来表示通常意义上的”字符“(2字节)

  • Java中的所有字符都用Unicode编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符

  • 字符型变量的三种表现形式:

    • 字符型常量是用单引号(‘’)括起来的单个字符。如char a1 = ‘a’;char a2 = ‘中’;char a3 = ‘9’;//只能写一个

    • Java中还允许使用转义字符’'来将其后的字符变成特殊字符常量。如char a3 = ‘\n';//\n表示换行符

    • 直接使用Unicode值来表示字符型常量:’\uXXXX'。其中,XXXX代表一个十六进制整数。如\u000a表示\n

  • char类型是可以运算的。因为它都对应有Unicode码

//char类型可运算:
System.out.print("hello");
System.out.println("world");
//helloworld

char c1 = '\n';
System.out.print("hello" + c1);
System.out.println("world");
//hello
//world

char c2 = '/t';
System.out.print("hello" + c2);
System.out.println("world");
//hello world

布尔型:boolean

  • 只能取两个值之一:true、false

  • 常在条件判断、循环结构中使用

boolean isMerried = true;
if(isMerried)
System.out.println("你就不能参加单身party了,很遗憾");
/*字符串中的每个字都是一个字符
比如System.out.println("你就不能参加单身party了\n很遗憾")
输出:你就不能参加单身party了
很遗憾
如果就想输出\n,可以再加一个反斜杠\\n
*/
else
System.out.println("你可以多谈谈女朋友");

基本数据类型之间的运算规则

前提:不包含boolean类型的运算

为什么包含char?

char的字符在ASCLL码和Unicode编码中有对应的数字,运算时用该数字进行

自动类型提升

当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型

此时的容量大小指的是,表示数的范围的大和小。比如float的容量要大于long

从大到小:byte、char、short-->int-->long-->float-->double//byte、char、short之间运算得到最少是int

byte b1 = 2;
int i1 = 127;
//如果byte b2 = b1 + i1;编译不通过,超出byte上限
int i2 = b1 + i1;//i2 = 129,自动提升数据类型


short s1 = 12;
double d1 = s1;
//也是可以的
System.out.println(d1);//12.0

char的讨论:

char c1 = 'a';
int i1 = 15;
//char c2 = c1 + i1;//报错
int i2 = c1 + i1;//char与int运算之后是int

char c1 = '15';
short s1 = '12';
//char c2 = c1 + s1;//报错
//short s2 = c1 + s1;//报错
int i1 = c1 + s1;//char与short运算之后是int
//后续测试:byte与short和char运算之后也是int

byte b1 = 123,b2 = 23;
//short s2 = b1 + b2;// 报错
byte、short、char之间直接运算也是得到int

强制类型转换:自动类型转换的逆运算

需要使用强转符()

可能导致精度损失

//精度损失举例1:
double d1 = 12.9;
int i1 = (int)d1;//截断操作,不会四舍五入
System.out.println(i1);//12
//精度损失举例2:
int i1 = 128;
byte b1 = (byte)i1;
System.out.println(b1);//-128

几种编码情况

//1.
long l = 123123;
System.out. println(l);//123123——为什么long类型不加l没有报错?不加l,会把123123当成int类型,自动类型提升

long l = 124123432434212212;
System.out.println(l);//报错

float f = 12.3 ;//报错,这个不加f是不行的,int中没有小数点
//2.
//整形常量,默认为int类型
//浮点型常量,默认为double类型
byte b = 12;
//byte b1 = b + 1;//编译失败,1默认是int型,与byte运算后得到int
//float f1 = b + 12.3;//编译失败,12.3默认是double型,用float降级了

字符串类型:String

  • String不是基本数据类型,属于引用数据类型

  • 使用方式与基本数据类型一致。例如:String str = ”abcd“;

  • 一个字符串可以串接另一个字符串,也可以直接串接其他类型的数据。例如:

    str = str + "xyz";

    int n = 100;

    str = str + n;

/*
String类型变量的使用:
1.String属于引用数据类型,翻译为:字符串
2.声明String类型变量时,使用一对""
3.String可以与八种基本数据类型变量做运算,且运算只能是连接运算:+
String s1 = "Hello,world!";
System.out.println(s1);
String s2 = "c";
String s3 = "";
//char c1 = '';//报错,编译不通过
连接运算:结果一定是String类型
int i = 1001;
String s = "学号:";
String S1 = i + s;//+:连接运算
boolean b1 = true;
String s2 = b1 + s1;
System.out.println(s1);//学号:1001
System.out.println(s2);//学号:1001true
判断+是加号还是连接运算,看它两边是否有String类型,如果有,就是连接运算,否则就是加号
char c = 'a';//a97 A65
int num = 10;
String str = "hello";
System.out.println(c + num + str);107hello
System.out.println(c + str + num);ahello10
System.out.println(c + (num + str));a10hello
System.out.println((c + num) + str);107hello
System.out.println(str + num + c);//hello10a
练习:输出*	*
System.out.println("* *");//T
System.out.println('*' + '\t' + '*');//F
System.out.println('*' + "\t" + '*');//T
System.out.println('*' + '\t' + "*");//F
System.out.println('*' + ('\t' + "*"));//T

 

String a = 123;//编译不通过

String a = 123 + "";//T

进制

二进制:0-1,满二进一,ob或oB开头

八进制:0-7,满八进一,数字0开头

十进制:0-9,满十进一

十六进制:0-9及A-F,满十六进一,0x或0X开头。此处A-F不区分大小写

image-20250310151417590

计算机底层都以补码的方式来存储数据!

image-20250310151919213

十进制转二进制:了解

image-20250310152216246

运算符

  • 算术运算符

  • 赋值运算符

  • 比较运算符(关系运算符)

  • 逻辑运算符

  • 位运算符

  • 三元运算符

算术运算符

image-20250311202836000

赋值运算符

=、+=、-=、*=、/=、%=

三元运算符

(条件表达式)?表达式1:表达式2

条件表达式为ture,运算后结果为表达式1。为false,结果为表达式2

  • 表达式1和表达式2为同种类型

三元运算符与if-else的联系和区别

  1. 三元运算符可以简化if-else语句

  2. 三元运算符要求必须返回一个结果

  3. if后的代码块可有多个语句

程序流程控制:

分支结构

if-else语句

  1. if(条件表达式){
    执行代码块;
    }
  2. if(条件表达式){
    执行代码块1;
    }
    else{
    执行代码块2;
    }
  3. if(条件表达式1){
    执行代码块1;
    }
    else if(条件表达式2){
    执行代码块2;
    }
    ......
    else{
    执行代码块n;
    }

用键盘获取不同类型的变量:需要使用Scanner类

具体实现步骤:

1.导包:

import java.util.Scanner;
//类似#include,写在class声明之上

2.Scanner的实例化

Scanner scan = new Scanner(System.in);//scan是一个变量名

3.调用Scanner类的相关方法,来获取指定类型的变量

double weight = scan.nextDouble();

int age = scan.nextInt();

boolean gender = scan.nextBoolean();

//char类型的获取,Scanner没有提供相关的方法。只能获取一个字符串
System.out.println("请输入你的性别(男/女");
String gender = scan.next();//"男"
//如果非要用char:
char genderChar = gender.charAt(0);//第一个char索引位置在0,它是把String的每个字符分开,作为类似数组的结构

swtich-case结构

switch(表达式){
case 常量1:
语句1;
//break;----break不是必须要有的,根据实际情况
case 常量2:
语句2;
//break;
...
case 常量n:
语句n;
//break;
default:
语句;
//break;

}
//default类似else,如果前面都没有匹配到,则直接用default.default结构也是可选的
/*
说明:
根据swtich表达式的值,依次匹配各个case中的常量,一旦匹配成功,则调用相应case中的执行语句.
调用完之后程序不会立刻终止,而是知道碰到break;或者程序结束
switch结构中的表达式一定是如下6种类型之一:byte\short\char\int\枚举类型(JDK5.0新增)\String类型(JDK7.0新增)
注意:布尔型严格不能放在表达式中
case只能声明常量,不能声明范围
*/

特殊关键字的使用:break,continue

 使用范围循环中的作用相同点
break switch-case,循环结构 结束当前循环 关键字后面不能声明执行语句
continue 循环结构 结束当次循环 关键字后面不能执行声明语句
for(int i = 1;i <= 10;i++){
if(i % 4 == 0){
break;//123
//System.out.prinln("nihaoshijie");//报错
}
System.out.println(i);
}
for(int i = 1;i <= 10;i++){
if(i % 4 == 0){
continue;//123567910
//System.out.println("nihaoshijie");//报错
}
System.out.print(i);
}

在嵌套循环中:

break:默认跳出包裹此关键字最近的一层循环

continue:默认跳出包裹此关键字最近的一层循环的一次

带标签的break,continue关键字:

label:for(int i = 1;i <= 10;i++){
for(int j = 1;j <= 10;j++)
if(j % 4 == 0){
break label;//结束指定标签的一层循环结构
//continue label;//结束指定标签的一层循环结构的当次循环
//按理来说标签可以是任意单词
}
System.out.print(i);
}

附加:特殊流程控制语句3

  • return:并非专门用于结束循环的,它的功能是结束一个方法.当一个方法执行到return语句时,这个方法将被结束

  • 与break和cotinue不同的是,return会直接结束整个算法,不管这个return处于多少层循环之中

数组:

数组存储数据时,可以使用自动转换类型.但还是建议容器和存储的数据类型保持一致

格式:

数据类型 []数组名;
数据类型 数组名[];
//两者一样,习惯上用第一种

初始化:在内存中,为数组容器开辟空间,将数据存入容器的过程.

数组的静态初始化

//完整格式:
数据类型 [] 数组名 = new 数据类型[]{元素1,元素2,元素3...};
//如:
int [] array = new int[][1,2,3...];//注意前后数据类型一定保持一致
//简化格式:
数据类型 [] 数组名 = {元素1,元素2,元素3...}

数组定义之后长度确定,不能发生改变

//定义数组储存5名学生的年龄
int [] arr1 = {15,12,23,23,12};
int [] arr2 = new int[]{15,12,23,23,12};
//定义数组储存3名学生年龄
String [] arr3 = {"zhangsan","lisi","wangwu"};
String [] arr4 = new String[]{"zhangsan","lisi","wangwu"};

数组的地址值

double [] arr = {1.93,1.75,1.73,1.81};
System.out.println(arr);//[D@776ec8df
//解释下地址值的格式含义:
//[:表示当前是一个数组
//D:表示当前数组里面的元素都是double类型的
//@:表示一个间隔符号.(固定格式,没有什么特别含义)
//776ec8df:才是真正的地址值(16进制)
//平时习惯性的会把这个整体叫做数组的地址值.

数组元素的访问

格式:数组名[索引];
//索引也叫下标,或者角标
//索引特点:从零开始,逐个加一增长,连续不间断
//利用索引对数组中的元素进行访问
//1.获取数组中的元素
//格式:数组名[索引]
int [] arr = {1,2,3,4,5};
//获取数组中的第一个元素值
int number = arr[0];
System.out.println(number);//1

System.out.println(arr[0]);//1

2.把数据存储到数组中:
//格式: 数组名[索引] = 具体数据/变量;//原来数据被覆盖

数组遍历

数组遍历:把数组中所有的内容取出来,之后可以(打印,求和,判断...)

注意:遍历指的是取出数据的过程,不要局限的理解为,遍历就是打印

int []arr = {1,2,3,4,5};
for(int i = 0;i < 5;i++){
System.out.println(arr[i]);
}

//在java中.关于数组的一个长度属性,length
//调用方式:数组名.length

System.out.println(arr.length);//5
//现在用for循环遍历数组就不用数数组的长度了
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}

//扩展:
//自动的快速生成数组的遍历方式:
//idea提供
数组名.fori//输入之后idea自动补全循环语句
//练习:遍历数组并求和
//定义一个数组,存储1,2,3,4,5
//遍历数组得到每一个元素,求数组里面所有的数据和
int [] arr = {1,2,3,4,5};
int sum = 0;
for(int i = 0;i < arr.length;i++){
sum += arr[i];
}
System.out.println(sum);
//统计个数:
//定义一个数组,存储1,2,3,4,5,6,7,8,9,10
//遍历数组得到的每一个元素,统计数组里面一共有多少能被3整除的数字
int [] arr = {1,2,3,4,5,6,7,8,9,10};
int count = 0;
for(int i = 0;i < arr.length;i++){
if(arr[i] % 3 == 0){
count++;
}
}
System.out.println(count);
//变化数据:
//定义一个数组,存储1,2,3,4,5,6,7,8,9,10
//遍历数组得到每一个元素.
//要求:1.如果是奇数,则当前数字扩大两倍
//2.如果是偶数,则将当前数字减小二分之一
int [] arr = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0;i < arr.length;i++)[
if(arr[i] % 2 == 0){
arr[i] = arr[i] /2;
}
else{
arr[i] = arr[i] *2;
}
//System.out.println(arr[i]);//我一开始是这样写的,这样写结果一样,但是不建议这样

//一个循环尽量只做一件事,防止思路混淆+
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i])
}

数组的动态初始化

数组的动态初始化:初始化只指定数组长度,由系统为数组分配初始值

//格式:数据类型[] 数组名 = new 数据类型[数组长度];
//创建
int[] arr = new int[3];
//添加
arr[0] = 12;
//获取
System.out.println(arr[0]);//12
System.out.println(arr[1]);//输出默认初始化值----0

 

数组默认初始化值的规律:

整数类型----0

浮点数类型----0.0

字符类型----'/u0000',也就是空格

布尔类型----false

引用数据类型----null.(现在默认除了八种基础数据类型以外的都是引用数据类型)

 

数组的动态初始化和静态初始化的区别

动态初始化:手动指定数组长度,由系统给出默认初始化值

  • 只明确元素个数,不明确具体数值,推荐使用动态初始化

  • 举例:使用数组容器来存储键盘录入的5个整数

静态初始化:手动指定数组元素,系统会根据元素个数,计算出数组的长度.

  • 需求中已经明确了要操作的具体数据,直接静态初始化即可

  • 举例:将全班学生的成绩存入数组中,11,22,33

数组常见问题

当访问了数组中不存在的索引,就会引发索引越界异常

数组例题

//求最大值
//已知数组元素为{33,5,22,44,55},请找出数组中最大值并打印在控制台
int [] arr = {33,5,22,44,55};
int max = arr[0];
for(int i = 0;i < arr.length;i++){
//循环的开始条件如果为0,那么第一次循环的时候是自己跟自己比了一下,对结果没有任何影响,但是效率偏低
if(arr[i] > max){
max = arr[i];
}
}
System.out.println(max);
//犯了一个重大错误:一个for中做了多件事
import java.util.Random;
public class _07 {
public static void main(String[] args) {
//生成10个1~100之间的随机数存入数组
//1.求出所有数据的和
//2.求出所有数据的平均数
//3.统计有多少个数据比平均数小
int[] arr = new int[10];
int sum = 0;
Random r = new Random();
for (int i = 0;i < arr.length; i++) {
int number = r.nextInt(100) + 1;//r.nextInt(100)指的是在1~99随机,所以要加一
arr[i] = number;
sum += arr[i];
}
int average = sum / 10;
int count = 0;
for (
int i = 0;
i < arr.length; i++) {
if (arr[i] < average) {
count++;
}
}
System.out.println("所有数据的和为:" + sum);
System.out.println("所有数据的平均数为:" + average);
System.out.println(count + "个数比平均数小");
}
}
//需求:定义一个数组,存入1,2,3,4,5.按照要求交换索引对应的元素
//交换前:1,2,3,4,5
//交换后:5,2,3,4,1

public class _08 {
public static void main(String[] args){
int [] arr = {1,2,3,4,5};
int temp;
temp = arr[0];
arr[0] = arr[4];
arr[4] = temp;
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
//打乱数组中的数据
//需求:定义一个数组,存入1~5.要求打乱数组中所有数据的顺序

import java.util.Random;
public class _09 {
public static void main(String[] args) {
int [] arr ={1,2,3,4,5};
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
int j = r.nextInt(arr.length);
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

}
}

数组的内存图

回头再看吧,看不下去了

Java内存分配:

Java内存分配 
方法运行时使用的内存,比如main方法的运行,进入方法栈中执行
存储对象或者数组,new来创建的,都存储在堆内存
方法区 存储可以运行的class文件
本地方法栈 JVM在使用操作系统的时候使用,和我们开发无关
寄存器 给CPU使用,和我们开发无关

方法

什么是方法?

main方法是程序的主入口,程序从main方法开始

什么是方法?

方法是程序中最小的执行单元,在同一方法中的代码,要么全部执行,要么全部不执行

实际开发中,什么时候用到方法?

重复的代码,具有独立功能的代码可以抽取到方法中

实际开发中,方法有什么好处?

提高代码的复用性和可维护性

方法的格式

方法定义:把一些代码打包在一起,该过程称为方法定义

方法调用:方法定义并不是直接运行的,需要手动调用才行,该过程称为方法调用

最简单的方法定义和调用:

//定义:
public static void 方法名(){
方法体(就是打包起来的代码);
}
//调用
方法名();

方法必须先定义后调用,否则程序报错

//例子:
public static void main(String[] args){
playGames();
}
public static void playGames(){
System.out.println("选英雄");
System.out.println("失败");
}

带参数的方法定义和调用

//定义:
//单个参数:
public static void method(参数){
...
}
//多个参数:
public static void method(参数1,参数2){//各参数以逗号隔开
...
}

 

方法调用时,参数的数量与类型必须与方法定义中小括号里面的变量一一对应,否则程序将报错.

//调用:
public static void getSum(int num1,int num2){// num1 和 num2 称为形参
int result = num1 + num2;
System.out.println(result);
}
public static void main(String[] args){
getSum(12,23);//35
//12和23称为实参
}

带返回值的方法定义和调用

public static 返回值类型 方法名(参数){
方法体;
return 返回值;
}
//调用:
public static void main(String[] args){
getSum();//直接调用,适用于没有返回值的方法
int sum = getSum(12,23);//赋值调用
System.out.println(getSum(12,23));//输出调用
}
public static int getSum(int num1,int num2){
int result = num1 + num2;
return result;//返回给方法的调用出
}

方法的注意事项:

  • 方法不调用就不执行

  • 方法和方法之间是平级关系,不能互相嵌套定义

  • 方法的编写顺序和执行顺序无关

  • 方法的返回值为void,表示该方法没有返回值

    • 没有返回值的方法可以省略return不写

    • 如果要编写return,后面不能跟具体的数据

  • return语句的下面,不能编写代码,因为永远执行不到,属于无效的代码

return关键字

方法没有返回值:可以省略不写.如果书写,表示结束方法

方法有返回值:必须要写,表示结束方法返回结果

方法的重载

  • 在同一个类中,定义了多个同名的方法,这些同名的方法具有同种的功能

  • 每一个方法具有不同的参数类型参数个数,这些同名的方法,就构成了重载关系

    • 简单记:同一个类中,方法名相同,参数不同的方法.与返回值无关

参数不同:个数不同,类型不同,顺序不同.(顺序不同可以构成重载,但是不建议)

public static void fn(int a){
//方法体
}
public statc int fn(int a){
//方法体
}
//如上两个方法不构成重载

通常把功能相同的方法定义为同名,定义和调用更方便

//需求:使用方法重载的思想,设计比较两个整数是否相同的方法
//要求:兼容全整数类型(byte,short,int,long)
public class _10 {
public static void main(String[] args) {
compare((byte)10,(byte)20);

byte b1 =10;
byte b2 =20;
compare(b1,b2);

}
public static void compare(byte b1 ,byte b2){
System.out.println(b1 == b2);
}
public static void compare(short s1 ,short s2){
System.out.println(s1 == s2);
}
public static void compare(int i1, int i2){
System.out.println(i1 == i2);
}
public static void compare(long n1 ,long n2){
System.out.println(n1 == n2);
}

}

练习

//复制数组:
//需求:定义一个方法copyOfRange(int[] arr,int from,int to)
//功能:将数组arr中从索引from(包含from)开始,到索引to结束(不包含to)的元素复制到新数组中,将新数组返回
public class _11 {

public static void main(String[] args) {
int[] arr ={1,2,3,4,5,6,7,8,9};
int[] copyArr = copyOfRange(arr,3,7);
for (int i = 0; i < copyArr.length; i++) {
System.out.println(copyArr[i]);
}
}

public static int[] copyOfRange(int[] arr,int from,int to){
int[] newArr = new int[to - from];
//伪造索引的思想,没有能表示索引的变量时考虑
int index = 0;
for(int i = from;i <to;i++){
newArr[index] = arr[i];
index++;
}
return newArr;
}
}

方法调用的基本内存原理

没听,黑马---方法--9--10--11

面向对象

面向:拿.找

对象:能干活的东西

面向对象编程:拿东西过来做对应的事情

01设计对象并使用

类和对象

  • 类(设计图):是对对象共同特征的描述

  • 对象:是真实存在的具体东西.

在Java中,必须先设计类,才能获取对象

如何定义类?

public class 类名 {
1.成员变量(代表属性,一般是名词)
2.成员方法(代表行为,一般是动词)
3.构造器(后面学习)
4.代码块(后面学习)
5.内部类(后面学习)
}

如何得到对象?

类名 对象名 = new 类名();

定义类的补充注意事项

  • 用来描述一类事物的类,专业叫做:Javabean类

    在Javabean类中,是不写main方法的

  • 编写main方法的类,叫做测试类

    我们可以在测试类中创建Javabean的对象并进行赋值调用

  • 类名的首字母建议大写,需要见名知意.驼峰模式.

  • 一个Java文件中可以定义多个class类,且只能一个类是public修饰,而且public修饰的类名必须成为代码文件名

 

实际开发中还是建议一个文件夹定义一个class类

  • 成员变量的完整定义格式是: 修饰符 数据类型 变量名称 = 初始化值;,一般无需指定初始化值,存在默认值.

数据类型明细默认值
  byte short int long 0
基本类型 float double 0.0
  boolean false
引用类型 类 接口 数组 String null
//定义女朋友类
public class GirlFriend{//不带main,就是Javabean类
//属性
String name;
int age;
String gender;

//行为
public void sleep(){
System.out.println("女朋友在睡觉");
}
public void eat(){
System.out.priintln("女朋友在吃饭");
}
}
//新建一个测试类文件
public class GirlFriendTest{
public static void main(String[] args){
GirlFriend gf1 = new GirlFriend();
gf1.name = "小诗诗";
gf1.age = 18;
gf1.gender = "萌妹子";

gf1.eat();
gf1.sleep();
}
}

02封装

什么是封装?

封装告诉我们,如何正确设计对象的属性和方法

原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为

封装的好处:

  • 让编程变得很简单,有什么事,找对象,调方法就行.

  • 降低我们的学习成本,可以少学,少记,或者说不用学,不用记对象有哪些方法,有需要时去找就行

private关键字

  • 是一个权限修饰符

  • 可以修饰成员(成员变量和成员方法)

  • private修饰的成员只能在本类中才能访问

  • 针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作(如:set和get方法)

对于每一个私有化的变量,都要提供get和set方法

  • set方法:给成员变量赋值

  • get方法:对外提供成员变量的值

public class GirlFriend{
private String name;
private int age;
private String gender;
public void setname(String n){
name = n;
}
public String getname(){
return name;
}
public void setAge(int age){
if(a >= 18 && a <= 50){
this.age = age;
}
else{
System.out.println("非法参数");
}
public String setGender(String gender){
this.gender = gender;//前面的gender为成员变量.后面的gender为测试类中赋值的"女",是局部变量
}
public String getGender(){
return gender;
}
}
}
public class GirlFriendTest{
public static void main(String[] agrs){
GirlFriend gf1 = new GirlFriend();
gf1.setName("小诗诗");
gf1.setAge(18);
gf1.setGender("萌妹子");

System.out.println(gf1.getName());
System.out.println(gf1.getAge());
System.out.println(gf1.getGender());
}
}

 

this关键字

成员变量和局部变量

public class GirlFriend {
private int age;//此处age是成员变量,未赋值默认0
public void method(){
int age = 10;//此处age是局部变量
System.out.println(age);//遵循就近原则,取局部变量age = 10
System.out.println(this.age);//如果就想在此处用成员变量,可以调用this关键字
//使用this关键字,不会从局部变量中调用,而是直接从成员变量中寻找
}
}

this关键字的作用:

可以区别成员变量和局部变量

 

 

 

构造方法

构造方法概述

构造方法也叫作构造器,构造函数

作用:在创建对象的时候,由虚拟机自动调用,给成员变量进行初始化

//构造方法格式:
public class Student{
修饰符 类名(参数) {
方法体;
}
}

构造方法有几种,各自的作用是什么?

  • 无参数构造方法:初始化对象时,成员变量的数据均采用默认值

  • 有参数构造方法:初始化对象时,同时可以为对象进行赋值

特点:

  1. 方法名和类名相同,大小写也要一致

  2. 没有返回值类型,连void都meiy

  3. 没有具体的数值(不能由return带回结果数据)

执行时机:

  1. 创建对象时由虚拟机调用,不能手动调用构造方法

  2. 每创建一次,就会调用一次构造方法

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

public Student() {
System.out.println("空参构造");
}

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

public String getName() {
return name;
}

public String setName() {
this.name = name;
}

public int getAge() {
return age;
}

public int setAge() {
this.age = age;
}
}
public class StudentTest {
Student s = new Student();
//调用了一个空参构造,但是上面没有写任何构造方法,为什么没有报错呢?
//如果我们没有写任何的构造方法,那么虚拟机会自动给我们加一个空参构造方法
//测试该行代码时还未写"带参构造"代码块

Student s = new Student("zhangsan",23);
//系统会自动根据参数选择有参构造,而略过空参构造
//所以 name 和 age 赋值成功,可以调用 getName 和 getAge 获取相应值
//这样调用有参构造,我们可以不必再自己调用set方法
System.out.println(s.getName);//zhangsan
System.out.println(s.getAge);//23

}

构造方法注意事项:

  1. 构造方法定义:

    • 如果没有定义构造方法,系统将给出一个默认无参数构造方法

    • 如果定义了构造方法,系统将不再提供默认的构造方法

  2. 构造方法的重载

    • 带参构造方法,和无参构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载

  3. 推荐的使用方式:

    • 无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法

标准 JavaBean

  1. 类名需要见名知意,驼峰命名

  2. 成员变量使用private修饰

  3. 提供至少两个构造方法

    • 无参构造方法

    • 带全部参数的构造方法

  4. 成员方法:

    • 提供每一个成员变量对应的setXxx()/getXxx()

    • 如果还有其他行为,也需要写上

//写一个标准JavaBean
public class User {
private String username;

public User(){}

public User(String username){
this.username = username;
}

public String getName(){
return username;
}
}
public class UserTest {
public static void main(String[] args) {
User u = new User("shijiehuizhuan");
System.out.println(u.getName());
}
}

快捷键

  • alt + insert(alt + Fn + insert)-->Constructor

    • -->Select None :创建空参构造

    • -->ctrl + A全选 --> OK :创建有全部参数的构造

  • alt + insert -->Getter and Setter-->Ctrl + A全选-->OK :创建每个私有化成员变量的set和get方法

插件PTG:一秒生成标准JavaBean

ctrl + shift + 逗号:一键生成标准javabean

最后生成toString方法还没学,删掉即可

对象内存图:听不进去

 

补充知识:成员变量,局部变量区别

成员变量:类中方法外的变量,没有上下顺序

局部变量:方法中的变量

区别成员变量局部变量
类中位置不同 类中,方法外 方法内,方法声明上
初始化值不同 有默认初始化值 没有,使用之前需要完成赋值
内存位置不同 堆内存 栈内存
生命周期不同 随着对象的创建而存在,随着对象的消失而消失 随着方法调用而存在,随着方法的运行结束而消失
作用域 整个类中有效 当前方法中有效

面向对象综合案例

文字版格斗游戏

格斗游戏,每个游戏角色的姓名,血量,都不相同,在选定人物的时候(new对象的时候),这些信息就应该被确定下来

角色1:乔峰,血量:100

角色2:鸠摩智,血量:100

乔峰举起拳头打了鸠摩智一下,造成了XX伤害,鸠摩智还剩下XXX点血

鸠摩智举起拳头打了乔峰一下,造成了XX伤害,乔峰还剩下XXX点血

乔峰举起拳头打了鸠摩智一下,造成了XX伤害,鸠摩智还剩下XXX点血

鸠摩智举起拳头打了乔峰一下,造成了XX伤害,乔峰还剩下XXX点血

鸠摩智K.O.了乔峰

import java.util.Random;


public class Role {
private String name;
private int blood;
//空参
public Role() {
}
//全参
public Role(String name, int blood) {
this.name = name;
this.blood = blood;
}
//name的get和set方法
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
//Blood的get和set方法
public int getBlood() {
return blood;
}

public void setBlood(int blood) {
this.blood = blood;
}

//定义一个方法用于攻击别人
//思考:谁攻击谁?
//Role r1 = new Role();
//Role r2 = new Role();
//r1.攻击(r2);
//方法的调用者去攻击参数
public void attack(Role role) {
//计算造成的伤害 1~20
Random r = new Random();
int hurt = r.nextInt(20) + 1;
//剩余血量
int remainBlood = role.getBlood() - hurt;
//对剩余血量进行验证,如果为负数了,就修改为0
remainBlood =remainBlood < 0 ? 0 : remainBlood;
//修改一下挨揍的人的血量
role.setBlood(remainBlood);

System.out.println(this.getName() + "举起拳头,打了" + role.getName() + "一下,造成了" + hurt + "点伤害,"
+ role.getName() +"还剩下" + remainBlood + "点血");
}
public class GameTest {
public static void main(String[] args) {
//1.创建第一个角色
Role r1 = new Role("乔峰",100);
//2.创建第二个角色
Role r2 = new Role("鸠摩智",100);
//3.开始格斗,回合制游戏
while(true){
//r1开始攻击r2
r1.attack(r2);
//判断r2剩余血量
if(r2.getBlood() == 0){
System.out.println(r1.getName() + "K.O了" + r2.getName());
break;
}
//r2开始攻击r1
r2.attack(r1);
//判断r1剩余血量
if(r1.getBlood() == 0){
System.out.println(r2.getName() + "K.O了" + r1.getName());
break;
}
}
}
}
System.out,printf("你好啊%s","张三");//在IDEA中输入souf.
//打印出:你好啊张三
//有两部分参数:
//第一部分参数:要输出的内容%s(占位)
//第二部分参数:填充的数据
//前面有几个占位,后面就要有多少个数据
//没有huan'han

 

对象数组练习

购物车

字符串

String

API和API帮助文档

API (Application Programming Interface):应用程序编辑接口

简单理解:API就是别人已经写好的东西,我们不需要自己写,直接使用即可

Java API : 指的就是JDK中提供的各种功能的Java类

这些类将底层的实现封装起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可

已经学习过的API:
Scanner 键盘录入
Random 随机数

API帮助文档 : 帮助开发人员更好的使用API和查询API的一个工具

字符串学习内容

String StringBuilder StringJonier StringBuffer Pattern Matcher

String概述

java.lang.String类代表字符串 , java程序中的所有字符串文字都为此类对象

创建String对象的两种方式

//01---直接赋值
String name = "尼古拉斯.阿玮";
构造方法 
public String() 创建空白字符串,不含任何内容
public String(String original) 根据传入的字符串,创建字符串对象(脱裤子放屁)
public String(char[] chs) 根据字符数组,创建字符串对象
public String(byte[] chs) 根据字节数组,创建字符串对象
//根据传入的字符串,创建字符串对象
String s1 = new String("abc");
System.out.println(s1);//abc
//根据字符数组,创建字符串对象
//应用场景:需要改变字符串的内容,可以通过改变数组中的内容来改变
char[] chs = {'a','b','c'};//char[] chs = {'A','b','c'};
String s2 = new String(chs);
System.out.println(s2);//abc-->Abc
//根据字节数组,创建字符串对象
//应用场景:以后在网络中传输的数据其实都是字节信息
//我们一般要把字节信息进行转换,转成字符串,此时就要用到这个构造了
byte[] bytes = {97,98,99,100};
String s3 = new String(bytes);
System.out.println(bytes);//abcd

字符串的比较

String s1 = new String("abc");//记录堆里面的地址值
String s2 = "abc";//记录串池中的地址值
System.out.println(s1 == s2);//false
== 号比的到底是什么 
基本数据类型 : 比较的是数据值 引用数据类型 : 比较的是地址值
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
//直接赋值的字符串会存储在串池中
//当使用双引号直接赋值时,系统会检查该字符在串池中是否存在
//不存在:创建新的
//存在:复用

字符串内容比较:

  • boolean equals方法(要比较的字符串)----完全一样结果才是true,否则为false

  • boolean equalsIgnoreCase(要比较的字符串)----忽略大小写的比较

String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1 == s2);//false
boolean result1 = s1.equals(s2);//比较内容是否相等
System.out.println(result1);//true
boolean result2 = s1.equalsIgnoreCase(s2);//比较内容是否相等,忽略大小写
System.out.println(result2);//true
		//1.假设我在键盘中录入一个abc
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");//看源码得知:输入的字符串最终是new出来的
String str1 = sc.next();//如果是字符串类型,next后面不用写数据类型吗
//2.代码中再定义一个字符串abc
String str2 = "abc";
//3,用"=="比较,能一样吗
System.out.println(str1 == str2);//false
//已知正确的用户名和密码,请用程序实现模拟用户登录,总共有三次机会,登录之后,给出相应的提示
public class _13 {
public static void main(String[] args) {

//正确的用户名和密码
String rightUsername = "zhangsan";
String rightPassword = "123456";
Scanner sc = new Scanner(System.in);
//三次机会:

for (int i = 0; i < 3; i++) {
//用户输入用户名和密码
System.out.println("请输入用户名");
String username = sc.next();
System.out.println("请输入密码");
String password = sc.next();

//比较
if (username.equals(rightUsername) && password.equals(rightPassword)) {
System.out.println("成功登录");
break;
} else {
if (i < 2)
System.out.printf("输入错误,请重新输入,您还剩下%d次机会", 2 - i);
System.out.println();
else {
System.out.println("错误次数过多,请稍后再试");
}
}
}
}
}
//为什么用for不用while?
//for:已知循环次数
//while:不知道循环次数,但知道结束循环条件
//遍历字符串
//需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串
//public char charAt(int index):根据索引返回字符
//public int length():返回此字符串的长度
//数组的长度:数组名.length
//字符串长度:字符串对象.length()

import java.util.Scanner;

public class _14 {
public static void main(String[] args) {
//录入一个字符串
System.out.println("请输入一个字符串");
Scanner sc = new Scanner(System.in);
String str = sc.next();
//进行遍历
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
System.out.println(c);

}
}
}
//金额转换
//需求:2135-->零佰零仟零万贰仟壹佰叁拾伍元

import java.util.Scanner;
public class _15 {
public static void main(String[] args) {
//1.键盘录入一个金额
Scanner sc = new Scanner(System.in);
int money;

while (true) {
System.out.println("请输入金额");
money = sc.nextInt();
if(money < 0 || money >9999999){
System.out.println("输入错误,请重新输入");
}
else{
break;
}
}
//用一个变量来表示钱的大写
String moneyStr ="";

//2.得到money里面的每一位数字,再转成中文
while(true){
//从右往左获取数据,因为右侧是数据的个位
int ge = money % 10;
String capitalNumber = getCapitalNumber(ge);
//把转换之后的大写拼接到moneyStr中
moneyStr = capitalNumber + moneyStr;
money =money / 10;
//如果数字上的每一位全部获取到了,那么money记录的就是0,此时循环结束
if(money == 0){
break;
}
}

//在前面补零,补齐七位
int count = 7 - moneyStr.length();
for (int i = 0; i < count; i++) {
moneyStr = "零" + moneyStr;
}

//插入单位
//定义一个数组表示单位
String result = "";
String[] arr = {"佰","拾","万","仟","佰","拾","元"};
//遍历moneyStr,然后把arr的单位插进去
for (int i = 0; i < moneyStr.length(); i++) {
char c = moneyStr.charAt(i);
//把大写数字和单位拼接
result =result + c + arr[i];
}
System.out.println(result);

//定义一个方法把数字变成大写的中文
}
public static String getCapitalNumber(int number) {
//定义数组,让数字跟大写的中文产生一个对应关系
String[] arr = {"零", "壹", "二", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
//返回结果
return arr[number];
}
}
    //案例:手机号屏蔽
//String substring(int beginIndex,int endIndex) 截取
//注意点:包头不包尾,包左不包右
//只有返回值才是截取的小串
//对原本字符串是没有任何影响的,因为字符串是不能被更改的
//String substring(int binginIndex) 截取到末尾
//beginIndex和endIndex都是索引

public static void main(String[] args) {
//获取一个手机号码
String phoneNumber = "13112349468";

//截取手机号码前三位
String start = phoneNumber.substring(0,3);

//截取手机号码后面4位
String end = phoneNumber.substring(7);

//拼接
String result = start + "****" + end;

//输出打印
System.out.println(result);
}
}
//敏感词替换
//String replace(旧值,新值)替换
//注意点:只有返回值才是替换之后的结果

public class _18 {
public static void main(String[] args) {
//获取说到的话
String talk = "你玩的真好,以后不要再玩了,TMD,CNM";

//定义一个敏感词库
String[] arr = {"TMD","CNM","SB","MLGB"};



//将里面的敏感词替换为***
for (int i = 0; i < arr.length; i++) {
talk = talk.replace(arr[i],"***");

}

//打印结果
System.out.println(talk);
}
}

StringBuilder

为什么学习StringBuilder?

String s = "";
for(int i = 0,i < 1000000;i++) {
s = s + "abc";
}
System.out.println(s);
//当数据量特别大的时候,拼接速度非常慢
StringBuilder sb = new StringBuilder("");
for(int i = 0;i < 1000000;i++){
sb.append("abc");
}
System.out.println(sb);
//用StringBuilder拼接就非常的快

StringBuilder概述

StringBuilder可以看成一个容器,创建之后里面的内容是可变

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

String s1 = "aaa";
String s2 = "bbb";
String s3 = "ccc";
String s4 = "ddd";
//如果要将这些字符串拼接,因为字符串是不可改变的,所以在拼接的过程中会产生"aaabbb""aaabbbccc"等无用的字符串,非常影响内存和代码的运行效率

StringBuilder构造方法

方法名说明
public StringBuilder() 创建一个空白可变字符串对象,不含有任何内容
public StringBuilder(String str) 根据字符串的内容,来创建可变字符串对象

StringBuilder常用方法

方法名说明
public StringBuilder append(任意类型) 添加数据,并返回对象本身
public StringBuilder reverse() 反转
public int length() 返回长度(字符出现的个数)
public String toString() 通过toString()就可以把StringBuilder转换为String
//常用方法演示
//1.创建对象
StringBuilder sb = new StringBuilder(abc);
//2.添加元素
/*sb.append(1);
sb.append(2.3);
sb.append(true);*/
sb.reverse();
System.out.println(sb);//abc12.3true

//反转
StringBuilder sb = new StringBuilder(abc);
sb.reverse();
System.out.println(sb);/cba

//获取长度
StringBuilder sb = new StringBuilder(abc);
int length = sb.length();
System.out.println(length);//3
//toString方法演示
StringBuilder sb = new StringBuilder();
sb.append("aaa");
sb.append("bbb");
Sb.append("ccc");
System.out.println(sb)//aaabbbccc,打印结果没问题
//把StringBuilder变回字符串
String str = sb.toString();//可以使用String的一些方法了
System.out.println(str);

 

//打印:
System.out.println(sb);//什么也没有
//普及:
//因为StringBuilder是java已经写好的类
//java在底层对它做了一些特殊处理
//打印对象不是地址值而是属性值
StringBuilder sb = new StringBuilder("投币");
System.out.println(sb);//投币

链式编程

当我们在调用一个方法的时候,不需要接收它的结果,可以继续调用其他方法

public static void main(String[] args){
int len = getString.substring(1).replace("A","Q").length();
Sytem.out.println(len);//2
}
public static String getString(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");//abc
String str = sc.next();
return str;
}
//利用链式编程,就可以简化StringBuilder方法的使用
StringBuilder sb = new StringBuilder();
sb.append("aaa").append("bbb").append("ccc");
System.out.println(sb);//aaabbbccc
//需求:键盘接收一个字符串,程序判断出该字符是否是对称字符串,并在控制台打印是或不是
//对称字符串111.121
//非对称字符串123123

import java.util.Scanner;
public class _19 {
public static void main(String[] args) {
String s = getString();
boolean result = s.equals(new StringBuilder().append(s).reverse().toString());
System.out.println(result);

}
public static String getString(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String sb = sc.next();
return sb;
}
}

StringJoiner

StringJoiner概述

  • StringJoiner和StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的

  • 作用:提高字符串的操作效率,而且代码编写特别简介,但是目前市场上很少有人用

  • JDK8出现的

StringJoiner的构造方法

方法名说明
public StringJoiner(间隔符号) 创建一个StringJoiner的对象,指定拼接时的间隔符号
public StringJoiner(间隔符号,开始符号,结束符号) 创建一个StringJoiner的对象,指定拼接时的间隔符号,开始符号,结束符号
StringJoiner sj = new StringJoiner("---");1---2---3
StringJOiner sj = new StringJoiner(",","[","]");//[1,2,3]

StringJoiner的成员方法

方法名说明
public StringJoiner add(添加内容) 添加数据,并返回对象本身
public int length() 返回长度(字符出现的个数)
public String toString() 返回一个字符串(该字符串就是拼接之后的结果)
//1.创建一个对象
StringJoiner sj = new StringJoiner("---");
//2.添加元素(目前只能添加字符串)
sj.add("aaa").add("bbb").add("ccc");
//3.获取长度
int len = sj.length();
System.out.println(len);//15,算上了间隔符号长度,注意空格也会算长度
//.打印结果
System.out.println(sj);//aaa---bbb---ccc

总结

1.String

表示字符串的类,定义了很多操作字符串的方法

2.StringBuilder

一个可变的操作字符串的容器

可以高效地拼接字符串,还可以将容器里面的内容反转

3.StringJoiner

JDK8出现的一个可变的操作字符串的容器,可以高效,方便地操作字符串

在拼接的时候,可以指定间隔符号,开始符号,结束符号

扩展:字符串原理

扩展底层原理1:字符串存储的内存原理

  • 直接赋值会复用字符串常量池中的

  • new出来不会复用,而是开辟一个新空间

扩展底层原理2:==号比较的到底是什么?

  • 基本数据类型比较数据值

  • 引用数据类型比较地址值

扩展底层原理3:字符串拼接的底层原理

  • 拼接时如果没有变量参与,都是字符串直接相加,编译之后就是拼接之后的结果,会复用串池中的字符串

    String s = "a"+"b"+"c";
    String s = "abc";
    //两种是完全一样的
  • 拼接的时候含有变量

    • JDK8以前:会new一个StringBuilder,使用append进行拼接,拼接完成后调用toString方法再转换为String(一个加号,堆内存中至少两个对象,浪费性能,且速度慢)

    • JDK8及以后:根据变量字符串长度预估一个数组的长度,将这些字符串变量都写到数组里,但是预估字符串也需要时间

结论:如果很多字符串变量拼接,不要直接+.在底层会创建多个对象,浪费时间,浪费性能

推荐使用StringBuilder或者StringJoiner

扩展底层原理4:StringBuilder提高效率原理图

  • 所有要拼接的内容都会往StringBuilder中放,不会创建很多无用的空间,节约内存

扩展底层原理5:StringBuilder源码分析

  • 默认创建一个长度为16的字节数组

  • 添加的内容长度小于16,直接存

  • 添加的内容长度大于16会扩容(原来的容量*2+2)

  • 如果扩容之后长度还不够,以实际长度为准,比如初始16,现在需要存36个,则扩容到36

StringBuilder sb = new StringBuilder();
System.out.println(sb.capacity);//16
System.out.println(sb.length);//0

字符串原理小结

集合

长度可变,自动扩容

数组可以存基本数据类型,也可以存引用数据类型

集合可以存引用数据类型,但不能直接存基本数据类型,但可以把基本数据类型转换成它们对应的包装类之后,再进行储存

定义:
ArrayList list = new ArrayList();

泛型:限定集合中的数据类型
ArrayList<String> list = new ArrayList<>();
sout(list);//[]
此时,我们创建的是ArrayList的对象,而ArrayList是java已经写好的一个类
这个类在底层做了一些处理:打印对象不是地址值,而是集合中存储的数据内容
在展示时,会用[]包裹所有的数据
方法名说明
boolean add(E e) 添加元素,返回值表示是否添加成功(不管添加什么都能添加成功,返回值永远是true)
boolean remove(E e) 删除指定元素,返回值表示是否删除成功
E remove(int index) 删除指定索引的元素,返回被删除的元素
E set(int index,E e) 修改指定索引下的元素,返回原来的元素
E get(int index) 获取指定索引的元素
int size() 集合的长度,也就是集合中元素的个数
ArrayList<String> list = new ArrayList<>();
list.add("aaa");//aaa
list.add("bbb");//aaabbb
list.add("ccc");//aaabbbccc
list.remove("aaa");//bbbccc
list.remove(1);//bbb
list.set(0,"aaa");//aaa
list.get(0);//aaa
sout(list.size());1
遍历:
list.size.fori{
String str = list.get(i);
sout(str);
}
//定义一个集合,添加字符串并进行遍历
//遍历格式参照:[元素1.元素2.元素3]

import java.util.ArrayList;

public class bbb {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.print("[");
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
System.out.print(list.get(i));
} else {
System.out.print(list.get(i) +",");
}
}
System.out.print("]");
}
}
基本数据类型对应的包装类
byte Byte
short Short
char Character
int Integer
long Long
float Float
double Double
boolean Boolean
import java.util.ArrayList;
public class StudentTest {
public static void main(String[] args) {
ArrayList<bbb> list = new ArrayList<>();
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",21);
Student s3 = new Student("wangwu",22);
list.add(s1);
list.add(s2);
list.add(s3);
for (int i = 0; i < list.size(); i++) {
Student stu = list.get(i);
System.out.println(stu.getName() + "," + stu.getAge());
}
}
}
public class Student {
private String name;
private int age;
}//标准JavaBean省略

面向对象进阶

static

static表示静态,是java中的一个修饰符,可以修饰成员方法,成员变量

被static修饰的成员变量,叫静态变量被static修饰的成员方法,叫静态方法
特点:被该类所有对象共享,不属于对象而是属于类,随着类的加载,优先于对象存在 特点:多用于测试类和工具类中,在JavaBean中很少使用
调用方式:类名调用(推荐),对象名调用 调用方式:类名调用(推荐),对象名调用
public class Student(){
private String name;
private static String teacherName;
public void show(){
sout("name" + "teacherName");
}
}//javabean在此省略
public class StudentTest(){
public static void main(){
//方法1:Student.setTeacherName = "wangermazi";
Student stu1 = new Student();
stu1.setName = "zhangsan"
//方法2:stu1.setTeacherName = "wangermazi"
Student stu2 = new Student();
stu2.setName = "lisi";
//方法1和2任意使用一个:
show(stu2);//lisi,wangermazi
}
}

工具类:能帮助我们做一些事情的,但是不描述任何事物的类

JavaBean类:用来描述一类事物的类.比如:Student,Dog,Cat等

测试类:用来检查其他类是否书写正确.带有main方法的类,是程序的入口

 

工具类需要:

类名见名知意

私有化构造方法

方法定义为静态(方便调用)

工具类示例:
public class ArrayUtil(){
private ArrayUtil(){}//私有化构造方法
public static String printArr(int[] arr){
略//用来打印数组
}
}
public class Test(){
public static void main(){
String str = ArrayUtil.printArr(arr1);//调用工具类的方法
sout(str);
}
}

static注意事项

  • 静态方法只能访问静态变量和静态方法

  • 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法

  • 静态方法中没有this关键字

重新认识main方法

public: 被JVM(虚拟机)调用,访问权限要足够大

static: 被JVM调用,不用创建对象,直接类名访问

因为main方法是静态的,所以测试类中其他方法也是静态的

void: 被JVM调用,不需要给JVM返回值

main: 一个通用的名字,虽然不是关键字,但是被JVM识别

String[] args: 以前用于接收键盘数据录入数据的,现在没用

继承

  • java中提供一个关键字extends,用这个关键字.我们可以让一个类与另一个类建立起继承关系

public class Student extends Person {}
  • Student称为子类(派生类),person称为父类(基类或超类)

使用继承的好处:

  • 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性

  • 子类可以在父类的基础上,增加其他的功能,使子类更强大

什么时候用继承?

当类和类之间,存在相同的内容,并满足子类是父类中的一种,就可以考虑继承,来优化代码

继承的特点:

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

  • 单继承:一个子类只能继承一个父类

  • 不支持多继承:子类不能同时继承多个父类

每一个类都直接或者间接的继承于Object

子类只能访问父类中的非私有成员

 

子类到底能继承父类中的哪些内容?

构造方法: 非私有--不能 私有--不能 如果构造方法能被继承的话,子类的构造方法名与类名就不一样了,不符合规则

成员变量:非私有--能 私有--能

成员方法:非私有--能 私有--不能

 

此处数据结构没看

继承中:成员变量的访问特点

就近原则:先在局部位置找,本类成员位置找,父类位置找,逐级往上

出现了同名的成员变量怎么办?

  • name:从局部位置开始往上找

  • this.name:从本类成员位置开始往上找

  • 从父类成员位置开始往上找

public class Fu {
String name = "Fu";
}
public class Zi extends Fu {
String name = "Zi";
public void Zishow(){
String name = "Zishow";
sout(name);Zishow
sout(this.name);Zi
sout(super.name);//Fu----super调用,直接访问父类最多只能调用一次super
}
}

继承中:成员方法的访问特点

与成员变量一样,就近原则

方法的重写

当父类的方法不能满足子类现在的需求时,就需要进行方法重写

书写格式:在继承体系中,子类出现了和父类一模一样的方法声明时,我们就称子类的这个方法是重写的方法

@Override重写注解

  1. @Override是放在重写后的方法上.校验子类重写时语法是否正确

  2. 加上注解后如果有红色波浪线,表示语法错误

  3. 建议重写方法都加@Override注解,代码安全,优雅

@Override//如果重写的出错,会在@Override下面出现红色波浪线
public void eat(){
sout("吃意面");
}

方法重写的注意事项和要求:

  1. 重写方法的名称,形参列表必须与父类中一致

  2. 子类重写父类方法时,访问权限子类必须大于等于父类(暂时了解:空着不写<protected<public)

  3. 子类重写父类方法时,返回值类型子类必须小于等于父类

  4. 建议:重写的方法尽量和父类保持一致

  5. 只有被添加到虚方法表中的方法才能被重写

继承中:构造方法的访问特点

  • 父类中的构造方法不会被子类继承

  • 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己

为什么?

  • 子类在初始化的时候,有可能会使用到父类中的数据

  • 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化

怎么调用父类构造方法的?

  • 子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行

  • 如果想调用父类有参构造,必须手动写super进行调用

总结:

  • 子类不能继承父类的构造方法,但是可以使用super调用

  • 子类构造方法的第一行,有一个默认的super()

  • 默认先访问父类中无参的构造方法,在执行自己

  • 如果想要访问父类有参构造,必须手动书写

this,super使用总结

  • this:理解为一个变量,表示当前方法调用者的地址值

  • super:代表父类存储空间

一些注意:

  • 子类在写带参构造方法时,要父类和子类的参数全写

多态

定义:同类型的对象,表现出的不同形态

表现形式:父类类型 对象名称 = 子类对象;

前提:

  • 有继承/实现关系

  • 有父类引用指向子类对象

    Fu f = new Zi();
  • 有方法重写

多态调用成员的特点

  • 变量调用:编译看左边,运行也看左边

  • 方法调用:编译看左边,运行看右边

class Animal{
String name = "动物";

public void show(){
sout("动物----show");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show(){
sout("狗----show");
}
}
class Cat extends Animal{
String name = "猫";
@Override
public void show(){
sout("猫----show");
}
}

public static void main(String[] args){
//创建对象:多态方式
Animal a = new Dog();
sout(a.name);//动物
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译代码时,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有,编译失败
//运行也看左边,java运行代码的时候,实际获取的就是左边父类中成员变量的值


//调用成员方法:编译看左边,运行看右边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败
//java运行代码的时候,实际上运行的是子类的方法
a.show();//狗----show

//理解:
//Animal a = new Dog();
//现在是用a区调用变量和方法的
//而a是Animal类型的,所以默认都会从Animal这个类去找
//成员变量:在子类的对象中,会把父类的成员变量也继承下来
//成员方法:如果子类中对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的
}

一些问题:

为什么注释@override之后,重写的方法即使不加注释符号也会被注释

多态的优势:

  • 在多态形式下,右边的对象可以实现解耦合,便于扩展和维护

Person p = new Student();//如果要改成老师工作,把Student换成Teacher就行了
p.work();//业务逻辑发生改变时,后续代码无需修改
  • 定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利

多态的弊端:

  • 不能调用子类的特有功能----调用方法先看左边,后看右边,编译的时候先检查父类中是否有这种方法,发现没有,报错

解决方案:把调用者a变回子类类型就行了

Dog d = (Dog)a;//强转类型
//转换的时候不能瞎转,如果转换成其他类的类型,就会报错
//比如用多态以动物类型创建了一条狗,不能转成猫,只能强转成狗

关键字istanceof:判断变量是哪种类型
if(a instance Dog){
Dog d = (Dog)a;
}else if(a instance Cat){
Cat d = (cat a);
}else{
sout("无此类型,无法转换")
}
//JDK14新特性:判断强转写在一起
if(a instance Dog d){}//先判断a是否为Dog类型,如果是,强转成Dog类型,转换之后变量名为d,如果不是,不强转

就是文件夹.用来管理各种不同功能的Java类,方便后期代码维护

  • 包名的规则:公司域名反写 + 包的作用,需要全部英文小写,见名知意.如:com.itheima.domain

    package com.itheima.domain;
    public class Student{

    }//Student的全类名/全限定名:com.itheima.domain.Student

    使用其他类时,需要使用全类名.

    import com.itheima.domain.Student;//导包
    public class Test{
    public static void main(String[] args){
    Student s = new Student();
    }
    }
  • 使用其他类的规则:

    • 使用同一个包中的类时,不需要导包

    • 使用java.lang包中的类时,不需要导包------String就是定义在java.lang包中的,java.lang包是Java的核心包

    • 其他情况都要导包

    • 如果同时使用两个包中的同名类,需要用全类名(此时就可以不用导包了)

final关键字

final:最终的-->不能被重写

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

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

修饰变量:叫做常量,只能被赋值一次

final int a = 10;赋值之后a就是个常数,不能被再次赋值,且此处必须赋值.这种用法类似C语言中的define
sout(a);//10

 

用final关键字修饰方法和修饰类很少用,了解即可

常量:实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性

常量的命名规范:

  • 单个单词:全部大写

  • 多个单词:全部大写,单词与单词之间用下划线隔开

细节:

  • 如果final修饰的变量是基本数据类型:那么变量存储的数据值不能发生改变

  • 如果final修饰的变量是引用数据类型:那么变量存储的地址值不能发生改变,对象内部可以改变

    核心:常量记录的数据是不能发生改变的

权限修饰符

  • 权限修饰符:是用来控制一个成员能够被访问的范围的

  • 可以修饰成员变量,方法,构造方法,内部类

有四种作用范围由小到大(private<空着不写<protected<public)

修饰符同一个类中同一个包中其他类不同包下的子类不同包下的无关类
private yes      
空着不写 yes yes    
protected yes yes yes  
public yes yes yes yse

权限修饰符使用规则:

实际开发中,一般只用private和public

  • 成员变量私有

  • 方法公开

特例:如果方法中的代码是抽取其他方法中的共性代码,这个方法一般也私有

protected:本包中可以使用,其他包的子类可以使用

接口中的成员特点:

  • 成员变量:

    • 只能是常量

    • 默认修饰符:public static final

  • 构造方法:没有

  • 成员方法:

    • 只能是抽象方法

    • 默认修饰符:public abstract

  • JDK以前:接口中只能定义抽象方法

  • JDK8:

  • JDK9:

接口和类之间的关系:

  • 类和类的关系:继承关系,不能多继承,但是可以多层继承

  • 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口(如果这样的话,需要把接口中的所有方法全部重写

  • 接口和接口的关系:

接口的应用

  1. 接口代表规则,是行为的抽象.想要哪让哪个类拥有一个行为,就让这个实现对应的接口就行了

  2. 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态

适配器设计模式

  • 设计模式(Design pattern)是一套被反复使用,多数人知晓的,经过分类 编目的,代码设计经验的总结,使用设计模式是为了可重复用代码,让代码更容易被他人理解,保证代码可靠性,程序的重用性

 

简单理解:设计模式就是各种套路

  • 适配器设计模式:解决接口与接口实现类之间的矛盾问题

写法:用一个实现类作为适配器,对所实现接口的所有默认方法进行空实现(方法体不写),再用InterImpl继承适配器,将需要用的默认方法重写即可

注意:适配器一般会加abstract修饰,因为里面的空实现方法没有意义,所以不会让外界建立适配器的对象

总结:

  1. 当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以使用适配器设计模式

  2. 书写步骤:-

    • 编写中间类XXXAdapter(适配器),实现对应的接口

    • 对接口中的抽象方法进行空实现

    • 让真正的实现类继承中间类,并重写需要用的方法

    • 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰

代码块

代码块就是{}内的东西

局部代码块方法里
构造代码块 方法外,类里
静态代码块 类里

局部代码块的好处是节约内存,但是现在硬件够硬,不再使用局部代码块了

构造代码块:

  1. 写在成员位置的代码块

  2. 作用:可以把多个构造方法中重复代码提取出来

  3. 执行时机:我们在创建本类对象时会先执行构造代码块在执行构造方法

    构造代码块也被逐渐淘汰了,不够灵活

不用构造代码块的话,如果有相同代码怎么办?用this关键字

public Student(){
this(null,0);//给name和age赋初始值null和0
}
public Student(String name,int age){
sout("创建对象");
this.age = age;
this.name = name;
}

或者:把重复代码抽取成方法,如果要用,调用方法即可

静态代码块(常用)

//格式:
static{}

特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次

使用场景:在类加载时,做一些数据初始化时使用

接口

接口就是一种规则,是对行为的抽象

接口的定义和使用

  • 接口用关键字interface定义

    public interface 接口名{}
  • 接口不能实例化

  • 接口和类之间是实现关系,通过implements关键字表示

    public class 类名 implements 接口名{}
  • 接口的子类:实现类(要么重写接口中的所有抽象方法,要么是抽象类)

 

1.接口和类的实现关系,可以是单实现,也可以多实现

public class 类名 implements 接口名1,接口名2{}

2.实现类还可以在继承一个类的同时实现多个接口

public class 类名 extends 父类 implements 接口名1,接口名2{}

抽象方法:没有方法体的方法是抽象方法

public abstract void eat();

抽象方法所在的类是抽象类,用abstract修饰

重写接口中的方法:@Override

@Override
public void swim(){
sout("青蛙在游泳");
}

接口中成员的特点

  • 成员变量:只能是常量.默认修饰符:public static final

  • 构造方法:没有

  • 成员方法:只能是抽象方法,默认修饰符:public abstract

    • JDK7以前:接口中只能定义抽象方法

    • JDK8:接口中可以定义有方法体的方法

    • JDK9:接口中可以定义私有方法

 

  1. 一个类实现了多个接口,就要重写接口中的所有抽象方法

  2. 如果在两个接口中有重名的方法,有一个类同时接入这两个接口时,就只要重写这两个接口中不重复的方法

接口和接口的关系

继承关系:可以单继承,也可以多继承

注意:如果实现类接入了最下面的接口,那么它就要重写接口中的所有抽象方法

一些注意

  • 复习继承条件:

    • 子类是父类中的一种

    • 一个父类中所有的子类是同一种事物

  • 如果两个子类继承的方法功能不一样,可以在父类中把方法写成抽象的

  • 抽象方法所在的类必须是抽象类

  • 子类中的构造方法只要写有参的即可

接口扩展

JDK8之后新增的方法

  • 允许接口中定义默认方法.需要用关键字default修饰

  • 作用:解决接口升级的问题(如此可以使接口新增功能后,实现类不需要立马做出重写,等到以后需要用到某个规则再重写就行了)

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

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

  • 范例:public default void show(){}

接口中默认方法的注意事项:

  • 默认方法不是抽象方法,所以不强制被重写,但是如果需要被重写,重写的时候要去掉default关键字

  • public 可以省略,但是default不能被省略

  • 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写

JDK8以后接口中新增的方法

  • 允许接口中定义静态方法,需要用static修饰

  • 接口中静态方法的定义格式:public static 返回值类型 方法名 (参数列表){}

  • 范例:public static void show(){}

接口中静态方法的注意事项:

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

  • public 可以省略,但是static 不能省略

  • static 静态方法不能被重写

JDK9之后新增的方法:私有

接口中私有方法的定义格式:

//格式1:
private 返回值类型 方法名 (参数列表){}
//范例1:
private void show(){}
//格式2:
private static 返回值类型 方法名(参数列表){}
//范例2:private static void method({})

私有接口的定义和使用可以更好的解决接口中有重复代码的问题:

public interface InterA{
public default show1(){
sout("show1执行");
sout("日志");//改为调用show()
}
}
public interface InterA{
public default show2(){
sout("show2执行");
sout("日志");//改为调用show()
}
}
//JDK9之前的解决方法
public default show(){
sout("日志");
}
//JDK9之后的解决方法:不再可以被外界调用
private void show(){
sout("日志");
}//用于默认方法
private static void show(){
sout("日志");
}//用于静态方法

 

内部类

什么是内部类

类的五大成员: 属性 , 方法 , 构造方法 , 代码块 , 内部类

什么是内部类?

在一个类的里面,再定义另一个类

其中一个类是外部类,另一个类是内部类,其他所有类是外部其他类

public class Car{外部类
String carName;
int carAge;
int carColor;
public void show(){
Engine e = new Engine();
sout(e.engineName);//外部类要访问内部类的成员,必须创建对象
}
class Engine{//内部类
String engineName;
String engineAge;

public void show(){
sout(carName);//内部类可以直接访问外部类的成员
}
}
}

内部类表示的事物是外部类的一部分

内部类单独出现没有任何意义

内部类的访问特点:

  • 内部类可以直接访问外部类的成员,包括私有

  • 外部类要访问内部类的成员,必须创建对象

什么时候用到内部类?B类事物是A类事物的一部分,且B单独存在没有意义,比如:汽车的发动机,ArrayList的迭代器,人的心脏等

内部类的分类

  • 成员内部类

  • 静态内部类

  • 局部内部类

  • 匿名内部类(常用)

成员内部类

成员内部类的书写
public class Car{外部类
String carName;
int carAge;
int carColor;
class Engine{//成员内部类
String engineName;
String engineAge;
}
}
  • 写在成员位置的(类中,方法外),属于外部类的成员

  • 成员内部类可以被一些修饰符所修饰:private , 默认 , protected , public , static等

    • 如果用private修饰成员内部类,其他类就不能直接创建成员内部类的对象,只能在外部类中创建

  • 在成员内部类的里面,JDK16之前不能定义静态变量,JDK16之后才可以定义静态变量

获取成员内部类的对象

方式一:当成员内部类被private修饰时, 在外部类中编写方法,对外提供内部类的对象

public class Outer{
publi Inner getInstance(){//instance:对象
return new Inner();
}
private class Inner{//内部类是私有类,外部其他类不能直接创建它的对象
}
}
//外部其他类有两种创建Inner的方式:
public class a{
//方式一:
Outer o = new Outer();
object i = o.getInstance();
//方式二:
Outer o = new Outer();
sout(o.getInstance());
}

方式二:当成员内部类被非私有修饰时,直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

范例:Outer.Inner oi = new Outer().new Inner();

//面试题:??填什么能输出对应值
public class Outer{
private int a = 10;
class Inner{
int a = 20;
public void show(){
int a = 30;
sout(??);//10
sout(??);//20
sout(??)://30
}
}
}
//答案:a , this.a , Outer.this.a

静态内部类

静态内部类是成员内部类的一种,再成员内部类前加static修饰

静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象

创建静态内部类对象的格式:

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

调用静态内部类中非静态方法的格式:先创建对象,用对象调用

调用静态方法的格式:外部类名.内部类名.方法名();

局部内部类

  1. 定义:将内部类定义在放法里面就叫作局部内部类,类似于方法里面的局部变量

  2. 外界是无法直接使用局部内部类的,需要现在方法内部创建对象猜可以使用

  3. 该类可以直接访问外部类的成员,也可以访问方法内部的局部变量

匿名内部类(重点)

匿名内部类本质上就是隐藏了名字的内部类

//格式:
new 类名或接口名(){
重写方法;
};
//举例:new Inter(){
public void show(){

}
};
其中{
public void show(){

}
}才是没有名字的类,它是Inter的实现类,而对于这个整体,我们可以理解为Inter接口的实现类对象,可以把这个对象赋值给一个Inter类对象
//方法重写:重写接口或者类中的所有抽象类

匿名内部类的应用:

//需求:在测试类中调用下面的method方法
public class test{
public static void method(Animal a){
a.eat();
}
Dog d = new Dog();
method(d);
//Dog类只需要用一次,还需要单独定义一个类,太麻烦了
}
//以前的方式:自己写一个子类继承Animal类,再创建子类的对象,传递给method方法
public class Dog extends Animal{
@Override
public void eat(){
sout("狗吃骨头");
}
}
//现在,用匿名内部类
method(
new Animal(){
@Override
public void eat(){
sout("狗吃骨头");
}
}
);

 

  1. 什么是匿名内部类?

    • 隐藏了名字的内部类(名字不需要用户自己起,Java会自动起名),可以写在成员位置,也可以写在局部位置

  2. 匿名内部类的格式?

    new 类名或者接口名(){

    重写方法;

    }

  3. 格式的细节

    包含了继承或实现,方法重写,创建对象

    整体就是一个类的子类或者接口的实现类对象

  4. 使用场景

    当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象.如果实现类只需要使用一次,就可以用匿名内部类简化代码

阶段综合项目:拼图小游戏puzzlegame(GUI)

主界面分析

JFrame:最外层的窗体

JMenuBar:最上层的菜单

JLabel:管理文字和图片的容器

上述三个都是组件之一

创建主界面1

//需求:
//到IDEA中创建一个宽603像素,高680像素的游戏主界面
//到IDEA中创建一个宽488像素,高430像素的登陆界面
//到IDEA中创建一个宽488像素,高500像素的注册界面

 

创建主界面2

//用继承改写上述的主界面,并思考用继承的好处:把代码可以分类来写
//设置一些

菜单制作

//JMenuBar是Java中对应菜单的类
//JMenuBar是一个菜单长条,里面可以存放多个JMenu对象,而每一个JMenu对象里又有许多JMenuItem
/*
1.先创建JMenuBar
2.再创建JMenu
3.再创建JMenuItem
4.把JMenuItem放到JMenu里面
5.把JMenu放到JMenuBar里面
6.把整个菜单JMenuBar添加到整个JFrame界面中
*/

添加图片

//Imageicon是Java中对应图片的类
//JLable管理区域,可以用来管理图片和文字,在管理图片时,可以对图片的宽高,位置,还有边框进行设置,默认是展示图片在界面的正中央
//初始化图片
//1.创建一个ImageIcon的对象
//2.创建一个JLable的对象(管理容器)
//3.把管理容器添加到界面中
//指定图片在界面中的位置
//坐标:以左上角为原点,横为x轴,纵为y轴
//创建界面时会自动生成三个部分,标题,菜单,窗体
//组件都是放置在窗体中的.在放置时,默认居中.如果想要指定位置,需要用this.setLayout(null);取消默认
//窗体要用getContentPane()调用

打乱图片顺序

//定义一个数组.把每一张图片定义为数组中的一个数,打乱数组中数的顺序,图片的顺序也随之打乱
//因为有行和列的问题,定义二维数组方便管理
//一般来说,长度确定的用数组,长度不确定的用集合
//练习:打乱一维数组中的数据
int[] tempArr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
//要求:打乱一维数组里面的数据,并按照4个一组的方式添加到二位数组中
//解法:遍历数组.得到每一个元素.拿着每一个元素跟随机索引上的数据进行交换
Random r = new Random();
for(int i = 0;i < tempArr.length;i++){
//获取到随机索引
int index = r.nextInt(tempArr.length);
//拿着遍历到的每一个数据,跟随机索引上的数据进行交换
int temp = tempArr[i];
tempArr[i] = tempArr[index];
tempArr[index] = temp;
}
//创建二维数组
int[][] data = new int[4][4];
//给二维数组添加数据
//解法1:遍历一维数组tempArr,得到每一个元素,把每一个元素依次添加到二维数组当中
for(int i = 0;i < tempArr.length;i++){
data[i / 4][i % 4] = tempArr[i];
}
//解法2:遍历二维数组,给里面的每一个数据赋值
int index = 0;
for(int i = 0;i < data.length;i++){
for(int j = 0;j < data[i].length;j++){
data[i][j] = tempArr[index];
index++;
}
}

事件

事件就是可以被组件识别的操作

  • 事件源:按钮,图片,窗体...

  • 事件:某些操作,如鼠标单击,鼠标划入...

  • 绑定监听:当事件源上发生了某个事件,则执行某段代码

    • KeyListener:键盘监听(键盘相关一系列动作)

    • MouseListener:鼠标监听(鼠标相关一系列动作)

    • ActionListener:动作监听(包含鼠标左键点击和空格)

//创建一个按钮对象
JButton jtb = new JButton("点我啊");
//设置位置和宽高
jtb.setBounds(0,0,100,50);
//给按钮添加动作监听
jtb.addActionListener(new MyActionListener());
//把按钮添加到界面中
JFrame.getContentPane().add(jtb);
public classMyActionLIstender implements ActionListener {
@Override
public void actionPerformed(ActionEvent e){
System.out.println("按钮被点击了");
}
}
//如果一个实现类只会被一个按钮使用,可以用匿名内部类,这样就不用再创建实现类了
jtb.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvernt e){
sout("不要点我");
//获取当前被操作的那个按钮对象
e.getSource();
}
});
//第三种添加动作监听的方法:直接在让写界面的类实现ActionListener
//在第一种方法中我们需要new一个MyActionListener实现类,但在这里,我们只需要调用this就行,this调用的是本类中重写的ActionListener的方法
//getSourse()的使用方法
Object sourse = e.getSourse();
if (sourse == jtb1){
jtb1.setSize(200,200);
}

鼠标监听机制-MouseListener

划入,按下,松开,划出

其中按下和松开被归为单击动作

如果我想监听一个按钮的单击事件,有几种方式?

  • 动作监听

  • 鼠标监听中的单击事件

  • 鼠标监听中的松开事件

方法描述
mouseClicked(MouseEvent e) 在组件上单击(按下并释放)鼠标按钮时调用
mouseEntered(MouseEvent e) 当鼠标进入组件时调用
mouseExited(MouseEvent e) 当鼠标退出组件时调用
mousePressed(MouseEvent e) 在组件上按下鼠标时调用
mouseReleased(MouseEvent e) 在组件上释放鼠标按钮时调用

键盘监听机制-KeyLIstener

方法描述
keyPressed(KeyEvent e) 按下键时调用。
keyReleased(KeyEvent e) 当键已被释放时调用。
keyTyped(KeyEvent e) 键入键时调用。(一般不用,有局限性,有些比如ctrl,alt键不能输入)
//一般是给整个窗体添加键盘监听
this.addKeyListener(this);//括号里写KeyListener的实现类对象,可以让本类实现KeyListener接口,在这里直接调用this
//调用者this:本类对象,也就是当前的界面对象,表示要给整个界面添加监听
//addKeyListener:表示要给本界面添加键盘监听
//参数this:当事件触发之后,会执行本类中的对应代码
//细节1:如果按住一个按键没有松开,那么会重复调用keyPeressed()方法
//细节2:键盘中那么多按键,怎么区分呢?每一个按键都有一个编号与之对应
//获取键盘上每一个按键的编号
int code = e.getKeyCode();//虽然ABCD等编号与ASCII码表中大写字母编号一样,但是这个编号与ASCII码表没什么关系

美化界面

//1.将图片移动到中间偏下的地方,通过给图片的位置横纵坐标添加位移,来改变图片位置
//2.给图片添加一个背景图
//添加图片的细节:
//先加载的图片在上方,后加载的图片塞在下面
//3.给每张图片添加边框
setBorder();//Border是一个接口,需要实现
//4.优化路径
//把绝对路径改为相对路径

路径分为两种

  1. 绝对路径:一定是从盘符开始.C: D:

  2. 相对路径:不是从盘符开始的

    • 相对路径是相对当前项目而言的

实际开发中,推荐写相对路径

图片加载不出来,一般是路径写错了,路径写错并不会报错,只是没有图片

上下左右移动

  1. 本类实现KeyListener接口,并重写所有抽象方法

  2. 给整个界面添加键盘监听事件

  3. 统计一下空白方块对应的数字0在二维数组的位置

  4. 在keyReleased方法中实现移动逻辑

  5. Bug修复:

    • 当空白块在边界时,无法继续朝边界移动

查看完整图片

作弊码

判断胜利

其实就是判断二维数组中的数字是否按照顺序进行排列

如果按照顺序进行排列的,那么显示胜利的图片

实现步骤:

  1. 定义一个正确的二维数组win

  2. 在加载图片之前,先判断一下二维数组中的数字跟win数组中是否相同

  3. 如果相同展示胜利图标

  4. 如果不相同不展示胜利图标

游戏打包exe要考虑的因素

1.一定要包含图形化界面

2.代码要打包起来

3.游戏用到的图片也要打包起来

4.JDK也要打包

游戏打包exe的核心步骤

  1. 把所有代码打包成一个压缩包,jar后缀的压缩包

  2. 把jar包转换成exe的安装包

  3. 把第二步的exe,图片,JDK整合在一起,变成最终的exe安装包

 

Objects

Objects是一个工具类,提供了一些方法去完成一些功能

需要重点学习的Objects成员方法

public static String toString(Object o) 					// 获取对象的字符串表现形式
public static boolean equals(Object a, Object b) // 比较两个对象是否相等
public static boolean isNull(Object obj) // 判断对象是否为null
public static boolean nonNull(Object obj) // 判断对象是否不为null

需要了解的Objects成员方法

public static <T> T requireNonNull(T obj)
// 检查对象是否不为null,如果为null直接抛出异常;如果不是null返回该对象;
public static <T> T requireNonNullElse(T obj, T defaultObj)
// 检查对象是否不为null,如果不为null,返回该对象;如果为null返回defaultObj值
public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier)
// 检查对象是否不为null,如果不为null,返回该对象;
// 如果为null,返回由Supplier所提供的值

BigInteger(大整数)

BigInteger构造方法

public BigInteger(int num, Random rnd) 		//获取随机大整数,范围:[0 ~ 2的num次方-1]
public BigInteger(String val) //获取指定的大整数
public BigInteger(String val, int radix) //获取指定进制的大整数

下面这个不是构造,而是一个静态方法获取BigInteger对象
public static BigInteger valueOf(long val) //静态方法获取BigInteger的对象,内部有优化

 

BigInteger对象一旦创建,内部记录的值不能发生改变

    public static void main(String[] args) {

//获得随机大整数
Random r = new Random();
BigInteger bd1 = new BigInteger(4,r);
//或者两行合并:BigInteger bd1 = new BigInteger(4,new Random());
BigInteger bd2 = new BigInteger(4,new Random());
System.out.println(bd1);//13
System.out.println(bd2);//14

//获得指定的大整数
BigInteger bd3 = new BigInteger("1000");
System.out.println(bd3);//1000
//细节:
//字符串中的数字必须是整数

//获得指定进制的大整数
BigInteger bd4 = new BigInteger("10000",2);//意思是前面的10000是二进制数
System.out.println(bd4);//输出的是二进制数的十进制格式:16
//细节:
//1.字符串中的数字必须是整数
//2.字符串中的数字必须与进制吻合,比如二进制中只能存在0和1

//静态方法获取BigInteger的对象,内部有优化
//细节:
//1.能表示的范围较小.在long的取值范围之内,如果超出long的范围就不行了
//2.在内部对常用的数字: -16~16 进行了优化
//提前把 -16~16 先创建好BigInteger的对象,如果多次获取不会创建新的
BigInteger bd5 = BigInteger.valueOf(100);
System.out.println(bd5);//100
BigInterger bd6 = BigInteger.valueOf(16);
BigInteger bd7 = BigInterger.valueof(16);
System.out.println(bd6 == bd7);//true

//对象一旦创建,内部的数据不能发生改变
BigInteger bd8 = BigInteger.valueOf(1);
BigInteger bd9 = BigInteger.valueOf(2);
BigInteger result = bd8.add(bd9);
System.out.println(result);//3
System.out.println(bd8);//1
System.out.println(bd9);//2
}

 

System.out.println("Long.MAX_VALUE"); //可以获取Long能存储的最大长度

BigInteger构造方法小结:

  • 如果BigInteger表示的数字没有超出long的范围,可以用静态方法获取。

  • 如果BigInteger表示的超出long的范围,可以用构造方法获取。

  • 对象一旦创建,BigInteger内部记录的值不能发生改变。

  • 只要进行计算都会产生一个新的BigInteger对象

常见成员方法总结

public BigInteger add(BigInteger val)					//加法
public BigInteger subtract(BigInteger val) //减法
public BigInteger multiply(BigInteger val) //乘法
public BigInteger divide(BigInteger val) //除法
public BigInteger[] divideAndRemainder(BigInteger val) //除法,获取商和余数
public boolean equals(Object x) //比较是否相同
public BigInteger pow(int exponent) //次幂、次方
public BigInteger max/min(BigInteger val) //返回较大值/较小值
public int intValue(BigInteger val) //转为int类型整数,超出范围数据有误

代码演示

BigInteger bd1 = BigInteger.valueOf(10);
BigInteger bd2 = BigInteger.valueOf(9);
//加法
System.out.println(bd1.add(bd2));//19

//减法
System.out.println(bd1.subtract(bd2));//1

//乘法
System.out.println(bd1.multiply(bd2));//90

//除法
System.out.println(bd1.divide(bd2));//1

//除法,获得商和余数
//错误示范
//System.out.println(bd1.divideAndRemainder(bd2));//[Ljava.math.BigInteger;@1b6d3586
//正确示范:
BigInteger[] arr = bd1.divideAndRemainder(bd2);
System.out.println(arr[0]);//1
System.out.println(arr[1]);//1
System.out.println(arr.length);//2

//比较是否相同
System.out.println(bd1.equals(bd2));//false

//次幂、次方
System.out.println(bd1.pow(3));//1000

//返回较大值/较小值
System.out.println(bd1.max(bd2));//10

//转为int类型整数,超出范围数据有误
int i = bd1.intValue();
//同理
double d = bd1.doubleValue();
System.out.println(i);//10
System.out.println(d);//10.0
BigInteger bd3 = BigInteger.valueOf(2147483648L);//int的最大值为2147483647
System.out.println(bd3.intValue());// - 2147483648,超出范围有误

BigInteger底层存储方式

对于计算机而言,其实是没有数据类型的概念的,都是0101010101,数据类型是编程语言自己规定的,所以在实际存储的时候,先把具体的数字变成二进制,每32个bit为一组,存储在数组中。

数组中最多能存储元素个数:21亿多

数组中每一位能表示的数字:42亿多

理论上,BigInteger能表示的最大数字为:42亿的21亿次方。

但是还没到这个数字,电脑的内存就会撑爆,所以一般认为BigInteger是无限的。

总结

  • BigInteger表示一个大整数

  • 如何获取BigInteger对象?(常用)

    • BigInteger b1 = BigInteger.valueOf(0.1);

    • BigInteger b1 = new BigInteger("整数");

  • 常见操作

    • 加:add

    • 减:subtract

    • 乘:multiply

    • 除:divide , divideAndRemainder

    • 比较:equals , max , min

    • 次幂:pow

    • 转成整数:intValue , longValue

BigDecimal

计算机中的小数(引子)

一个浮点数在二进制中由整数部分和小数部分组成

十进制:69.875 -->整数部分二进制:0100 0101

-->小数部分二进制:111

不同小数的二进制的差距:

69.875 : 111

0.9 : 11100110011001100110011001100110011001101,共45位

0..226 : 0011001110110110010001011010000111001010111000000100001,共55位

类型占字节数总bit位数小数部分bit位数
float 4个字节 32个bit位 23个bit位
double 8个字节 64个bit位 52个bit位

小数部分超出的bit位数只能舍弃了

BigDecimal的作用

  • 用于小数的精确运算

  • 用来表示很大的小数

如何创建BigDecimal的对象?

  • 通过传递double类型的小数来创建对象

    • 这种方式可能是不精确的,不建议使用

  • 通过传递字符串表示的小数来创建对象

  • 通过静态方法获取对象

  1. 如果要表示的数字不大,没有超出double的取值范围,建议使用静态方法

  2. 如果要表示的数字比较大,超出课double的取值范围,建议使用构造方法

  3. 如果我们传递的是0 ~ 10之间的整数,包含0,包含10,那么方法会返回已经创建好的对象,不会重新new

		/*构造方法获取BigDecimal的对象
public BigDecimal(Double val)
public BIgDecimal(String val)

静态方法获取BigDecimal的对象
public static BigDecimal valueOf(Double val)
*/
//1,通过传递double类型的小数来创建对象
//细节:这种方式可能是不精确的,不建议使用
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal(0.09);
System.out.println(bd1);//0.01000000000000000020816681711721685132943093776702880859375
System.out.println(bd2);//0.0899999999999999966693309261245303787291049957275390625

//2.通过传递字符串表示的小数来创建对象
BigDecimal bd3 = new BigDecimal("0.01");
BigDecimal bd4 = new BigDecimal("0.09");
System.out.println(bd3);//0.01
System.out.println(bd4);//0.09
System.out.println(bd3.add(bd4));//0.10

//3.通过静态方法获取对象
BigDecimal bd5 = BigDecimal.valueOf(10);
System.out.println(bd5);
//细节:
//1.如果要表示的数字不大,没有超出double的取值范围,建议使用静态方法
//2.如果要表示的数字比较大,超出课double的取值范围,建议使用构造方法
//3.如果我们传递的是0 ~ 10之间的整数,包含0,包含10,那么方法会返回已经创建好的对象,不会重新new

BigDecimal的使用

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 devide(BigDecimal value,精确几位,舍入模式) //除法
其中舍入模式:RoudingMode.HALF_UP 四舍五入,具体的舍入方法在api中查询

 

舍入模式:去尾法等

底层存储方式

把数据看成字符串,遍历得到里面的每一个字符,把这些字符在ASCII码表上的值,都存储到数组中。

image-20250427124421899

总结

  1. BigDecimal的作用是什么?

    • 表示较大的小数和解决小数运算精度失真问题

  2. BigDecimal的对象如何获取?

    • BigDecimal bd1 = new BigDecimal("较大的小数");

    • BIgDecimal bd2 = BigDecimal.valueOf(0.1);

  3. 常见操作

    • 加:add

    • 减:subtract

    • 乘:multiply

    • 除:divide(四舍五入 : RoundingMode.HALF_UP)

正则表达式

正则表达式可以校验字符串是否满足一定的规则,并用来校验数据格式的合法性

 

 

集合进阶

Collection(单列集合)

List系列集合:添加的元素是有序,可重复,有索引的

  • 有序:存和取的顺序一样

  • 可重复:集合中存储的元素是可以重复的

  • 有索引:可以通过索引去获取List系列集合中的每一个元素

Set系列集合:添加的元素是无序,不重复,无索引

  • 无序:存和取的顺序有可能不一样

  • 不重复:集合中不能存储重复的元素(可以根据这一特性完成元素的去重)

  • 无索引:不能通过索引取获取set系列集合中的每一个元素

Collection集合

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的

常用方法

方法名说明
boolean add(E e) 添加元素
boolean remove(Object o) 从集合中移除指定的元素
boolean removeIf(Object o) 根据条件进行移除
void clear() 清空集合中的元素
boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数

代码演示

public class a01collectiondemo {
public static void main(String[] args) {
/*
boolean add(E e) 添加元素
boolean remove(Object o) 从集合中移除指定的元素
boolean removeIf(Object o) 根据条件进行移除
void clear() 清空集合中的元素
boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数
*/

/*
注意点:
Collection是一个接口,我们不能直接创建它的对象
所以,现在我们学习它的方法时,只能创建它的实现类的对象
实现类:ArrayList
*/
//目的:为了学习Collection接口里面的方法
//自己在做一些练习的时候,还是按照之前的方式去创建对象
Collection<String> coll = new ArrayList<>();//左边写接口,右边new它的实现类的对象,可以使用左边接口的方法

//1.添加元素
//细节:关于返回值的boolean
//细节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.删除
coll.remove("aaa");
//细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除,只能通过元素的对象进行删除
//细节2:方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false
//如果要删除的元素不存在,就会删除失败
System.out.println(coll);//

//4.判断元素是否包含
//细节:底层是依赖equals方法进行判断是否存在的
//所以,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法
boolean result = coll.contains("bbb");//字符串在底层也是依赖equals()方法进行判断,只是Java已经重写好了,不需要我们自己再写了
System.out.println(result);//true

//5.判断集合是否为空
boolean result2 = coll.isEmpty();
System.out.println(result2);//false

//6.获取集合的长度
int size = coll.size();
System.out.println(size);//2
}
}

Collection的三种通用的遍历方式

迭代器遍历

  • 迭代器在java中的类是Iterator,迭代器是集合专用的遍历方式

Collection集合获取迭代器

方法名称说明
Iterator<E> iterator() 返回迭代器对象,默认指向当前集合的0索引

Iterator中的常用方法

方法名称说明
boolean hasNext() 判断当前位置是否有元素,有元素返回true,没有元素返回false
E next() 获取当前位置的元素,并将迭代器对象移向下一个位置
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class a03collectiondemo {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();//创建集合对象
coll.add("0");
coll.add("1");
coll.add("2");
coll.add("3");
Iterator<String> it = coll.iterator();//创建指针
while(it.hasNext()){//判断是否有元素
String str = it.next();//获取元素,移动指针
if("1".equals(str)){
//coll.remove("1");//不可行,会报错
it.remove();//用迭代器中的删除方法
}

}
System.out.println(coll);
/*
细节注意点:
1.迭代器已经遍历到集合没有元素的地方,如果继续调用next()方法,会报错NoSuchElementException
2.迭代器遍历完毕,指针不会复位.想要复位,只能重新再获取一个迭代器对象
3.循环中只能用一次next方法,如果想要多次使用当前元素,可以先用一个变量接收该元素,再调用对象就可以了
4.迭代器遍历时,不能用集合的方法进行增加或者删除
如果实在是要删除,可以用迭代器提供的remove方法进行删除
如果是要添加,暂时没有办法
*/
//System.out.println(it.next());//报错NoSuchElementException
}
}

总结

  1. 迭代器在遍历集合的时候是不依赖索引的

  2. 迭代器需要掌握三个方法

    Iterator<String> it = list.iterator();//iterator()创建迭代器对象
    while(it.hasNext()){//hasNext()判断当前指向的位置是否有元素
    String str = it.next();//next()获取当前指向位置的元素,并移动迭代器中的指针
    System.out.println(str);
    }
  3. 迭代器的四个细节:

    1.迭代器已经遍历到集合没有元素的地方,如果继续调用next()方法,会报错NoSuchElementException
    2.迭代器遍历完毕,指针不会复位.想要复位,只能重新再获取一个迭代器对象
    3.循环中只能用一次next方法,如果想要多次使用当前元素,可以先用一个变量接收该元素,再调用对象就可以了
    4.迭代器遍历时,不能用集合的方法进行增加或者删除
    如果实在是要删除,可以用迭代器提供的remove方法进行删除
    如果是要添加,暂时没有办法

增强for遍历

  • 增强for的底层就是迭代器,为了简化迭代器的代码书写的

  • 它是JDK5之后出现的,其内部原理就是一个Iterator迭代器

  • 所有的单列集合数组才能使用增强for进行遍历

格式:

for(元素的数据类型 变量名 : 数组或者集合){

}
//比如
for(String s : list) {
System.out.println(s);
}

代码演示:

import java.util.ArrayList;
import java.util.Collection;

public class a04collectiondemo {
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){
System.out.println(s);
}
/*
注意点:
s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个元素
*/
//IDEA快捷生成增强for方式: coll.for
}
}

增强for的细节

  • 修改增强for中的变量 , 不会改变集合中原本的数据

    for(String s : list){
    s = "q";//没什么卵用
    }

Lambda表达式遍历

  • 得益于JDK8开始的新技术Lambda表达式,提供了一种更简单 , 更直接的遍历集合的方式

    方法名称说明
    default void forEach(Consumer<? super T> action): 结合lambda遍历集合
    import java.util.ArrayList;
    import java.util.Collection;

    public class a04collectiondemo {
    public static void main(String[] args) {
    //1.创建集合并添加元素
    Collection<String> coll = new ArrayList<>();
    coll.add("zhangsan");
    coll.add("lisi");
    coll.add("wangwu");
    //3.Lambda表达式
    //形式: ()->{}
    /*完整版
    coll.forEach((String s)->{
    System.out.println(s);
    }
    );
    */
    coll.forEach((String s)-> System.out.println(s) );
    //元素的数据类型可以省略,如果参数只有一个,小括号可以省略.如果方法体只有一行,大括号可以省略,return可以省略,分号可以省略
    }
    }

     

 

为什么没有普通for遍历?

在Collecton集合的set系列集合中,是没有索引的,而普通for遍历是通过索引遍历,所以不能使用

总结:

  1. Collection是单列集合的顶层接口,所有方法被List和Set系列集合共享

  2. 常见成员方法

    • add , clear , remove , contains , isEmply , size

  3. 三种通用的遍历方式:

    • 迭代器:在遍历的过程中需要删除元素,请使用迭代器

    • 增强for , Lambda : 仅仅只是想遍历 , 那么使用增强for或Lambda表达式就可以了

List集合

List集合的特点

  • 有序:存和取的顺序一样

  • 可重复:集合中存储的元素是可以重复的

  • 有索引:可以通过索引去获取List系列集合中的每一个元素

List集合的特有方法

  • Collection的方法List都继承了

  • LIst集合因为有索引,所以多了很多索引操作的方法

    方法名描述
    void add(int index,E element) 在此集合中的指定位置插入指定的元素
    E remove(int index) 删除指定索引处的元素,返回被删除的元素
    E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
    E get(int index) 返回指定索引处的元素

    方法演示以及注意事项

import java.util.ArrayList;
import java.util.List;

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

//1.创建一个集合
List<String> list = new ArrayList<>();

//2.添加元素
list.add("aaa" );
list.add("bbb" );
list.add("3" );
list.add("ccc");

//在集合中指定位置插入指定元素
//void add(int index E,element)
list.add(1,"QQQ");
//细节:原来索引上的元素会依次往后移动

//删除指定索引的元素,返回被删除的元素
//E remove(int index)
String remove1 = list.remove(0);
System.out.println(remove1);//aaa

String remove2 = list.remove(3);
//请问:此时删除的是3索引的元素,还是3这个元素? ----3索引上的
//为什么?
//因为在调用方法的时候,如果出现了重载现象
//优先调用,实参和形参一致的那个方法
//可以使用手动装箱 : Integer i = Integer.valueOf(3);
//list.remove(i);
//这样删除的就是3这个元素了

System.out.println(remove2);

//修改指定索引的元素,返回被修改的元素
//E set(int index,E element)
String result = list.set(0,"QQQ");
System.out.println(result);

//返回指定索引的元素
//E get(int index)
String s = list.get(0);
System.out.println(s);

//3.打印集合
System.out.println(list);

}
}

List集合的遍历方式

迭代器遍历

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
}
}

列表迭代器遍历

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");

//获取一个列表迭代器对象,里面的指针也是默认指向零的
//与迭代器不同的是:列表迭代器额外添加了一个方法,在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
String str = it.next();
}
}
}

增强for遍历

import java.util.ArrayList;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (String s : list) {
System.out.println(s);
}
}
}

Lambda表达式遍历

import java.util.ArrayList;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.forEach((String s)-> System.out.println(s) );
}
}

普通for循环(因为List集合存在索引)

import java.util.ArrayList;
import java.util.List;
public class a07listdemo {
public static void main(String[] args) {
//1.创建集合并添加元素
List<String> list = new ArrayList<>();//通过多态的方式创建对象
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println(str);
}
}
}

总结:

  • 迭代器遍历 : 在遍历的过程中需要删除元素,请使用迭代器

  • 列表迭代器 : 在遍历的过程中需要添加元素,请使用列表迭代器

  • 增强for和Lambda : 仅仅想要遍历,那么使用增强for或Lambda表达式

  • 普通for : 如果遍历的时候想操作索引,可以使用普通for

数据结构

数据结构:计算机存储 , 组织数据的方式 . 是指数据相互之间是以什么方式排列在一起的 . 数据结构是为了更加方便地管理和使用数据 , 需要结合具体地业务场景来进行选择 . 一般来说 , 精心选择的数据结构可以带来更高的运行或者存储效率

要知道

  1. 每种数据结构长什么样子

  2. 如何添加数据

  3. 如何删除数据

常见的数据结构:

  1. 队列

  2. 数组

  3. 链表

  4. 二叉树

  5. 二叉查找树

  6. 平衡二叉树

  7. 红黑树

数据结构(栈)

栈的特点:后进先出,先进后出

数据进入栈模型的过程称为:压/进栈

数据离开栈模型的过程称为:弹/出栈

类似子弹的弹夹,最先压进去的子弹是最后打出来的

image-20250428101903801

在Java的内存区域有一块叫栈内存,也是这个思想

方法运行的时候进栈,执行完毕出栈

数据结构(队列)

队列特点 : 先进先出,后进后出

数据从后端进入队列模型的过程称为 : 入队列

数据从前端离开队列模型的过程称为 : 出队列

image-20250428104307757

数据结构(数组)

 

数组是一种查询快,增删慢的模型

  • 查询速度快 : 查询数据通过地址值和索引定位 , 查询任意数据耗时相同 . (元素在内存中是连续存储的)

  • 删除效率低 : 要将原始数据删除 , 同时后面每个数据前移

  • 添加效率极低 : 添加位置的每个数据后移 , 再添加元素

    image-20250428105003886

数据结构(链表)

链表中的结点是独立的对象 , 在内存中是不连续的 , 每个结点包含数据值和下一个结点的地址

链表查询速度慢 , 无论查询哪个数据都要从头开始找

链表增删相对快

 

image-20250428110402886

image-20250428110611477

image-20250428111159450

image-20250428111311303

image-20250428111429723

双向链,查询较快,如果要查找的数据靠近前端,就从前面开始找,靠近后端,就从后端开始找

总结:

各种数据结构的特点和作用是什么样的?

  • 栈 : 后进先出,先进后出

  • 队列 : 先进先出 , 后进后出

  • 数组 : 内存连续区域 , 查询快 , 增删慢

  • 链表 : 元素是游离的 , 查询慢 , 首尾操作极快

ArrayList集合

LInkedLIst集合

 
posted @ 2025-04-28 11:21  世界回转  阅读(30)  评论(0)    收藏  举报